aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorTim Jenssen <tim.jenssen@qt.io>2023-06-02 21:23:26 +0200
committerTim Jenssen <tim.jenssen@qt.io>2023-06-02 19:26:05 +0000
commit002d84cb15e314a207346fdfd6cf78055d1b8b92 (patch)
treef3354186e1b3f7e80a2786acfaff6656eb7cdb09 /src/plugins
parentbc6487068892be8c99404998c93dc6125551cf18 (diff)
parente759ce310fe6f30724f29b809bf0bcab1df88e1d (diff)
Merge remote-tracking branch 'origin/11.0' into qds/dev
Conflicts: src/plugins/qmldesigner/designercore/metainfo/metainfo.cpp src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp tests/auto/qml/qmlprojectmanager/fileformat/fileformat.qbs tests/auto/qml/qmlprojectmanager/fileformat/tst_fileformat.cpp Change-Id: I257f1908917bcc58805619b53b6866f2f73ca544
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/CMakeLists.txt4
-rw-r--r--src/plugins/android/android.qbs4
-rw-r--r--src/plugins/android/androidavdmanager.cpp44
-rw-r--r--src/plugins/android/androidavdmanager.h9
-rw-r--r--src/plugins/android/androidbuildapkstep.cpp21
-rw-r--r--src/plugins/android/androidconfigurations.cpp28
-rw-r--r--src/plugins/android/androidcreatekeystorecertificate.cpp7
-rw-r--r--src/plugins/android/androiddebugsupport.cpp2
-rw-r--r--src/plugins/android/androiddeployqtstep.cpp139
-rw-r--r--src/plugins/android/androiddeployqtstep.h83
-rw-r--r--src/plugins/android/androiddevice.cpp24
-rw-r--r--src/plugins/android/androiddevice.h5
-rw-r--r--src/plugins/android/androidmanager.cpp39
-rw-r--r--src/plugins/android/androidmanager.h1
-rw-r--r--src/plugins/android/androidmanifesteditorwidget.cpp4
-rw-r--r--src/plugins/android/androidpackageinstallationstep.cpp10
-rw-r--r--src/plugins/android/androidplugin.cpp19
-rw-r--r--src/plugins/android/androidpotentialkit.cpp24
-rw-r--r--src/plugins/android/androidpotentialkit.h18
-rw-r--r--src/plugins/android/androidqmlpreviewworker.cpp12
-rw-r--r--src/plugins/android/androidrunconfiguration.cpp96
-rw-r--r--src/plugins/android/androidrunconfiguration.h5
-rw-r--r--src/plugins/android/androidruncontrol.h6
-rw-r--r--src/plugins/android/androidrunnerworker.cpp31
-rw-r--r--src/plugins/android/androidsdkmanager.cpp86
-rw-r--r--src/plugins/android/androidsdkmanagerwidget.cpp12
-rw-r--r--src/plugins/android/androidsettingswidget.cpp12
-rw-r--r--src/plugins/android/androidsignaloperation.cpp6
-rw-r--r--src/plugins/android/androidsignaloperation.h2
-rw-r--r--src/plugins/autotest/autotest.qbs4
-rw-r--r--src/plugins/autotest/autotestplugin.cpp25
-rw-r--r--src/plugins/autotest/autotestplugin.h2
-rw-r--r--src/plugins/autotest/autotestunittests.cpp16
-rw-r--r--src/plugins/autotest/boost/boosttestconfiguration.cpp9
-rw-r--r--src/plugins/autotest/boost/boosttestconfiguration.h3
-rw-r--r--src/plugins/autotest/boost/boosttestframework.h9
-rw-r--r--src/plugins/autotest/boost/boosttestoutputreader.cpp10
-rw-r--r--src/plugins/autotest/boost/boosttestoutputreader.h3
-rw-r--r--src/plugins/autotest/boost/boosttestparser.cpp5
-rw-r--r--src/plugins/autotest/boost/boosttestparser.h2
-rw-r--r--src/plugins/autotest/boost/boosttestsettings.cpp65
-rw-r--r--src/plugins/autotest/boost/boosttestsettings.h33
-rw-r--r--src/plugins/autotest/boost/boosttesttreeitem.cpp10
-rw-r--r--src/plugins/autotest/catch/catchconfiguration.cpp11
-rw-r--r--src/plugins/autotest/catch/catchconfiguration.h3
-rw-r--r--src/plugins/autotest/catch/catchframework.h9
-rw-r--r--src/plugins/autotest/catch/catchoutputreader.cpp9
-rw-r--r--src/plugins/autotest/catch/catchoutputreader.h3
-rw-r--r--src/plugins/autotest/catch/catchtestparser.cpp5
-rw-r--r--src/plugins/autotest/catch/catchtestparser.h2
-rw-r--r--src/plugins/autotest/catch/catchtestsettings.cpp80
-rw-r--r--src/plugins/autotest/catch/catchtestsettings.h52
-rw-r--r--src/plugins/autotest/catch/catchtreeitem.cpp16
-rw-r--r--src/plugins/autotest/ctest/ctestconfiguration.cpp5
-rw-r--r--src/plugins/autotest/ctest/ctestconfiguration.h3
-rw-r--r--src/plugins/autotest/ctest/ctestoutputreader.cpp7
-rw-r--r--src/plugins/autotest/ctest/ctestoutputreader.h5
-rw-r--r--src/plugins/autotest/ctest/ctestsettings.cpp88
-rw-r--r--src/plugins/autotest/ctest/ctestsettings.h40
-rw-r--r--src/plugins/autotest/ctest/ctesttool.h9
-rw-r--r--src/plugins/autotest/ctest/ctesttreeitem.cpp8
-rw-r--r--src/plugins/autotest/gtest/gtestconfiguration.cpp10
-rw-r--r--src/plugins/autotest/gtest/gtestconfiguration.h3
-rw-r--r--src/plugins/autotest/gtest/gtestframework.h9
-rw-r--r--src/plugins/autotest/gtest/gtestoutputreader.cpp15
-rw-r--r--src/plugins/autotest/gtest/gtestoutputreader.h3
-rw-r--r--src/plugins/autotest/gtest/gtestparser.cpp5
-rw-r--r--src/plugins/autotest/gtest/gtestparser.h2
-rw-r--r--src/plugins/autotest/gtest/gtestsettings.cpp69
-rw-r--r--src/plugins/autotest/gtest/gtestsettings.h38
-rw-r--r--src/plugins/autotest/gtest/gtesttreeitem.cpp14
-rw-r--r--src/plugins/autotest/itestparser.cpp6
-rw-r--r--src/plugins/autotest/itestparser.h10
-rw-r--r--src/plugins/autotest/qtest/qttest_utils.cpp12
-rw-r--r--src/plugins/autotest/qtest/qttest_utils.h4
-rw-r--r--src/plugins/autotest/qtest/qttestconfiguration.cpp12
-rw-r--r--src/plugins/autotest/qtest/qttestconfiguration.h3
-rw-r--r--src/plugins/autotest/qtest/qttestframework.h9
-rw-r--r--src/plugins/autotest/qtest/qttestoutputreader.cpp18
-rw-r--r--src/plugins/autotest/qtest/qttestoutputreader.h6
-rw-r--r--src/plugins/autotest/qtest/qttestparser.cpp7
-rw-r--r--src/plugins/autotest/qtest/qttestparser.h4
-rw-r--r--src/plugins/autotest/qtest/qttestsettings.cpp71
-rw-r--r--src/plugins/autotest/qtest/qttestsettings.h34
-rw-r--r--src/plugins/autotest/qtest/qttesttreeitem.cpp16
-rw-r--r--src/plugins/autotest/quick/quicktest_utils.cpp3
-rw-r--r--src/plugins/autotest/quick/quicktest_utils.h2
-rw-r--r--src/plugins/autotest/quick/quicktestconfiguration.cpp15
-rw-r--r--src/plugins/autotest/quick/quicktestconfiguration.h3
-rw-r--r--src/plugins/autotest/quick/quicktestparser.cpp40
-rw-r--r--src/plugins/autotest/quick/quicktestparser.h6
-rw-r--r--src/plugins/autotest/quick/quicktesttreeitem.cpp34
-rw-r--r--src/plugins/autotest/quick/quicktesttreeitem.h2
-rw-r--r--src/plugins/autotest/testcodeparser.cpp185
-rw-r--r--src/plugins/autotest/testcodeparser.h31
-rw-r--r--src/plugins/autotest/testconfiguration.cpp16
-rw-r--r--src/plugins/autotest/testconfiguration.h6
-rw-r--r--src/plugins/autotest/testframeworkmanager.cpp3
-rw-r--r--src/plugins/autotest/testframeworkmanager.h2
-rw-r--r--src/plugins/autotest/testnavigationwidget.cpp11
-rw-r--r--src/plugins/autotest/testoutputreader.cpp19
-rw-r--r--src/plugins/autotest/testoutputreader.h8
-rw-r--r--src/plugins/autotest/testprojectsettings.cpp2
-rw-r--r--src/plugins/autotest/testresultdelegate.cpp7
-rw-r--r--src/plugins/autotest/testresultdelegate.h7
-rw-r--r--src/plugins/autotest/testresultmodel.cpp3
-rw-r--r--src/plugins/autotest/testresultspane.cpp10
-rw-r--r--src/plugins/autotest/testrunner.cpp401
-rw-r--r--src/plugins/autotest/testrunner.h28
-rw-r--r--src/plugins/autotest/testsettings.cpp146
-rw-r--r--src/plugins/autotest/testsettings.h61
-rw-r--r--src/plugins/autotest/testsettingspage.cpp240
-rw-r--r--src/plugins/autotest/testsettingspage.h22
-rw-r--r--src/plugins/autotest/testtreeitem.cpp8
-rw-r--r--src/plugins/autotest/testtreeitem.h2
-rw-r--r--src/plugins/autotest/testtreemodel.cpp46
-rw-r--r--src/plugins/autotest/testtreemodel.h3
-rw-r--r--src/plugins/autotoolsprojectmanager/autogenstep.cpp16
-rw-r--r--src/plugins/autotoolsprojectmanager/autotoolsbuildsystem.cpp3
-rw-r--r--src/plugins/autotoolsprojectmanager/makefileparser.cpp6
-rw-r--r--src/plugins/axivion/Axivion.json.in21
-rw-r--r--src/plugins/axivion/CMakeLists.txt15
-rw-r--r--src/plugins/axivion/axivion.qbs32
-rw-r--r--src/plugins/axivion/axivion.qrc6
-rw-r--r--src/plugins/axivion/axivionoutputpane.cpp209
-rw-r--r--src/plugins/axivion/axivionoutputpane.h42
-rw-r--r--src/plugins/axivion/axivionplugin.cpp348
-rw-r--r--src/plugins/axivion/axivionplugin.h41
-rw-r--r--src/plugins/axivion/axivionprojectsettings.cpp184
-rw-r--r--src/plugins/axivion/axivionprojectsettings.h67
-rw-r--r--src/plugins/axivion/axivionquery.cpp97
-rw-r--r--src/plugins/axivion/axivionquery.h40
-rw-r--r--src/plugins/axivion/axivionresultparser.cpp347
-rw-r--r--src/plugins/axivion/axivionresultparser.h101
-rw-r--r--src/plugins/axivion/axivionsettings.cpp131
-rw-r--r--src/plugins/axivion/axivionsettings.h51
-rw-r--r--src/plugins/axivion/axivionsettingspage.cpp215
-rw-r--r--src/plugins/axivion/axivionsettingspage.h21
-rw-r--r--src/plugins/axivion/axiviontr.h15
-rw-r--r--src/plugins/axivion/images/axivion.pngbin0 -> 293 bytes
-rw-r--r--src/plugins/axivion/images/axivion@2x.pngbin0 -> 545 bytes
-rw-r--r--src/plugins/baremetal/baremetaldebugsupport.cpp3
-rw-r--r--src/plugins/baremetal/baremetaldebugsupport.h5
-rw-r--r--src/plugins/baremetal/baremetaldevice.cpp1
-rw-r--r--src/plugins/baremetal/debugserverproviderssettingspage.cpp4
-rw-r--r--src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp2
-rw-r--r--src/plugins/baremetal/debugservers/gdb/gdbserverprovider.h2
-rw-r--r--src/plugins/baremetal/debugservers/gdb/genericgdbserverprovider.h5
-rw-r--r--src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp2
-rw-r--r--src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp4
-rw-r--r--src/plugins/baremetal/debugservers/uvsc/uvproject.cpp2
-rw-r--r--src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp5
-rw-r--r--src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h4
-rw-r--r--src/plugins/baremetal/iarewtoolchain.cpp6
-rw-r--r--src/plugins/baremetal/keiltoolchain.cpp8
-rw-r--r--src/plugins/baremetal/sdcctoolchain.cpp6
-rw-r--r--src/plugins/bazaar/bazaarclient.cpp30
-rw-r--r--src/plugins/bazaar/bazaarclient.h4
-rw-r--r--src/plugins/bazaar/bazaarcommitwidget.cpp7
-rw-r--r--src/plugins/bazaar/bazaarplugin.cpp29
-rw-r--r--src/plugins/bazaar/bazaarsettings.cpp54
-rw-r--r--src/plugins/bazaar/bazaarsettings.h20
-rw-r--r--src/plugins/bazaar/pullorpushdialog.cpp2
-rw-r--r--src/plugins/beautifier/CMakeLists.txt4
-rw-r--r--src/plugins/beautifier/abstractsettings.cpp122
-rw-r--r--src/plugins/beautifier/abstractsettings.h26
-rw-r--r--src/plugins/beautifier/artisticstyle/artisticstyle.cpp2
-rw-r--r--src/plugins/beautifier/artisticstyle/artisticstyle.h1
-rw-r--r--src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.cpp135
-rw-r--r--src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.h18
-rw-r--r--src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp173
-rw-r--r--src/plugins/beautifier/artisticstyle/artisticstylesettings.h32
-rw-r--r--src/plugins/beautifier/beautifier.qbs8
-rw-r--r--src/plugins/beautifier/beautifierplugin.cpp20
-rw-r--r--src/plugins/beautifier/clangformat/clangformat.cpp6
-rw-r--r--src/plugins/beautifier/clangformat/clangformat.h1
-rw-r--r--src/plugins/beautifier/clangformat/clangformatoptionspage.cpp144
-rw-r--r--src/plugins/beautifier/clangformat/clangformatoptionspage.h18
-rw-r--r--src/plugins/beautifier/clangformat/clangformatsettings.cpp202
-rw-r--r--src/plugins/beautifier/clangformat/clangformatsettings.h26
-rw-r--r--src/plugins/beautifier/configurationdialog.cpp2
-rw-r--r--src/plugins/beautifier/configurationpanel.cpp9
-rw-r--r--src/plugins/beautifier/generaloptionspage.cpp105
-rw-r--r--src/plugins/beautifier/generaloptionspage.h16
-rw-r--r--src/plugins/beautifier/generalsettings.cpp141
-rw-r--r--src/plugins/beautifier/generalsettings.h33
-rw-r--r--src/plugins/beautifier/uncrustify/uncrustify.cpp2
-rw-r--r--src/plugins/beautifier/uncrustify/uncrustify.h1
-rw-r--r--src/plugins/beautifier/uncrustify/uncrustifyoptionspage.cpp139
-rw-r--r--src/plugins/beautifier/uncrustify/uncrustifyoptionspage.h18
-rw-r--r--src/plugins/beautifier/uncrustify/uncrustifysettings.cpp195
-rw-r--r--src/plugins/beautifier/uncrustify/uncrustifysettings.h41
-rw-r--r--src/plugins/bookmarks/bookmarkfilter.cpp118
-rw-r--r--src/plugins/bookmarks/bookmarkfilter.h11
-rw-r--r--src/plugins/bookmarks/bookmarkmanager.cpp21
-rw-r--r--src/plugins/bookmarks/bookmarksplugin.cpp10
-rw-r--r--src/plugins/boot2qt/device-detection/qdbwatcher.cpp4
-rw-r--r--src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp2
-rw-r--r--src/plugins/boot2qt/qdbdevice.cpp15
-rw-r--r--src/plugins/boot2qt/qdbdevicedebugsupport.cpp12
-rw-r--r--src/plugins/boot2qt/qdbmakedefaultappstep.cpp68
-rw-r--r--src/plugins/boot2qt/qdbplugin.cpp34
-rw-r--r--src/plugins/boot2qt/qdbstopapplicationstep.cpp67
-rw-r--r--src/plugins/clangcodemodel/CMakeLists.txt1
-rw-r--r--src/plugins/clangcodemodel/clangcodemodel.qbs6
-rw-r--r--src/plugins/clangcodemodel/clangcodemodelplugin.cpp47
-rw-r--r--src/plugins/clangcodemodel/clangcodemodelplugin.h2
-rw-r--r--src/plugins/clangcodemodel/clangdast.cpp14
-rw-r--r--src/plugins/clangcodemodel/clangdast.h1
-rw-r--r--src/plugins/clangcodemodel/clangdclient.cpp49
-rw-r--r--src/plugins/clangcodemodel/clangdclient.h6
-rw-r--r--src/plugins/clangcodemodel/clangdfindreferences.cpp30
-rw-r--r--src/plugins/clangcodemodel/clangdfindreferences.h4
-rw-r--r--src/plugins/clangcodemodel/clangdlocatorfilters.cpp375
-rw-r--r--src/plugins/clangcodemodel/clangdlocatorfilters.h50
-rw-r--r--src/plugins/clangcodemodel/clangdqpropertyhighlighter.cpp4
-rw-r--r--src/plugins/clangcodemodel/clangdsemantichighlighting.cpp37
-rw-r--r--src/plugins/clangcodemodel/clangdsemantichighlighting.h8
-rw-r--r--src/plugins/clangcodemodel/clangmodelmanagersupport.cpp230
-rw-r--r--src/plugins/clangcodemodel/clangmodelmanagersupport.h2
-rw-r--r--src/plugins/clangcodemodel/clangtextmark.cpp9
-rw-r--r--src/plugins/clangcodemodel/test/clangbatchfileprocessor.cpp729
-rw-r--r--src/plugins/clangcodemodel/test/clangbatchfileprocessor.h16
-rw-r--r--src/plugins/clangcodemodel/test/clangdtests.cpp109
-rw-r--r--src/plugins/clangcodemodel/test/clangdtests.h4
-rw-r--r--src/plugins/clangformat/CMakeLists.txt1
-rw-r--r--src/plugins/clangformat/clangformat.qbs4
-rw-r--r--src/plugins/clangformat/clangformatbaseindenter.cpp30
-rw-r--r--src/plugins/clangformat/clangformatchecks.cpp2
-rw-r--r--src/plugins/clangformat/clangformatconfigwidget.cpp9
-rw-r--r--src/plugins/clangformat/clangformatfile.cpp16
-rw-r--r--src/plugins/clangformat/clangformatfile.h3
-rw-r--r--src/plugins/clangformat/clangformatglobalconfigwidget.cpp75
-rw-r--r--src/plugins/clangformat/clangformatglobalconfigwidget.h4
-rw-r--r--src/plugins/clangformat/clangformatindenter.cpp2
-rw-r--r--src/plugins/clangformat/clangformatutils.cpp15
-rw-r--r--src/plugins/clangtools/clangtool.cpp23
-rw-r--r--src/plugins/clangtools/clangtoolruncontrol.cpp13
-rw-r--r--src/plugins/clangtools/clangtoolruncontrol.h4
-rw-r--r--src/plugins/clangtools/clangtoolrunner.cpp65
-rw-r--r--src/plugins/clangtools/clangtoolrunner.h10
-rw-r--r--src/plugins/clangtools/clangtools.qbs4
-rw-r--r--src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp8
-rw-r--r--src/plugins/clangtools/clangtoolsdiagnosticview.cpp5
-rw-r--r--src/plugins/clangtools/clangtoolsplugin.cpp3
-rw-r--r--src/plugins/clangtools/clangtoolspreconfiguredsessiontests.cpp27
-rw-r--r--src/plugins/clangtools/clangtoolsprojectsettings.cpp2
-rw-r--r--src/plugins/clangtools/clangtoolsprojectsettingswidget.cpp7
-rw-r--r--src/plugins/clangtools/clangtoolssettings.cpp17
-rw-r--r--src/plugins/clangtools/clangtoolssettings.h6
-rw-r--r--src/plugins/clangtools/clangtoolsunittests.cpp1
-rw-r--r--src/plugins/clangtools/clangtoolsutils.cpp28
-rw-r--r--src/plugins/clangtools/clangtoolsutils.h3
-rw-r--r--src/plugins/clangtools/diagnosticconfigswidget.cpp75
-rw-r--r--src/plugins/clangtools/diagnosticconfigswidget.h1
-rw-r--r--src/plugins/clangtools/documentclangtoolrunner.cpp25
-rw-r--r--src/plugins/clangtools/documentclangtoolrunner.h4
-rw-r--r--src/plugins/clangtools/executableinfo.cpp4
-rw-r--r--src/plugins/clangtools/filterdialog.cpp2
-rw-r--r--src/plugins/clangtools/runsettingswidget.cpp17
-rw-r--r--src/plugins/clangtools/runsettingswidget.h1
-rw-r--r--src/plugins/clangtools/settingswidget.cpp3
-rw-r--r--src/plugins/clangtools/unit-tests/exported-diagnostics/CMakeLists.txt2
-rw-r--r--src/plugins/classview/classviewmanager.cpp15
-rw-r--r--src/plugins/classview/classviewparsertreeitem.cpp8
-rw-r--r--src/plugins/clearcase/checkoutdialog.cpp4
-rw-r--r--src/plugins/clearcase/clearcaseplugin.cpp54
-rw-r--r--src/plugins/clearcase/clearcasesubmiteditorwidget.cpp2
-rw-r--r--src/plugins/clearcase/clearcasesync.cpp37
-rw-r--r--src/plugins/clearcase/clearcasesync.h9
-rw-r--r--src/plugins/clearcase/settingspage.cpp6
-rw-r--r--src/plugins/clearcase/versionselector.cpp2
-rw-r--r--src/plugins/cmakeprojectmanager/3rdparty/cmake/Copyright.txt136
-rw-r--r--src/plugins/cmakeprojectmanager/3rdparty/cmake/README.md4
-rw-r--r--src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileCache.cxx235
-rw-r--r--src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileCache.h114
-rw-r--r--src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.cxx2831
-rw-r--r--src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.h68
-rw-r--r--src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.in.l543
-rw-r--r--src/plugins/cmakeprojectmanager/3rdparty/cmake/cmStandardLexer.h88
-rw-r--r--src/plugins/cmakeprojectmanager/CMakeLists.txt6
-rw-r--r--src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp91
-rw-r--r--src/plugins/cmakeprojectmanager/cmakebuildstep.cpp124
-rw-r--r--src/plugins/cmakeprojectmanager/cmakebuildstep.h7
-rw-r--r--src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp555
-rw-r--r--src/plugins/cmakeprojectmanager/cmakebuildsystem.h31
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeeditor.cpp11
-rw-r--r--src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp5
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeformatter.cpp193
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeformatter.h31
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeformatteroptionspage.cpp101
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeformatteroptionspage.h17
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeformattersettings.cpp135
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeformattersettings.h58
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeinstallstep.cpp9
-rw-r--r--src/plugins/cmakeprojectmanager/cmakekitinformation.cpp79
-rw-r--r--src/plugins/cmakeprojectmanager/cmakekitinformation.h5
-rw-r--r--src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp193
-rw-r--r--src/plugins/cmakeprojectmanager/cmakelocatorfilter.h35
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprocess.cpp12
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprocess.h4
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeproject.cpp45
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeproject.h6
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectconstants.h1
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp261
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectimporter.h10
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp93
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectmanager.h2
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs18
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp13
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectnodes.h6
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp82
-rw-r--r--src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp84
-rw-r--r--src/plugins/cmakeprojectmanager/cmakespecificsettings.h30
-rw-r--r--src/plugins/cmakeprojectmanager/cmaketool.cpp8
-rw-r--r--src/plugins/cmakeprojectmanager/cmaketool.h6
-rw-r--r--src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp1
-rw-r--r--src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp41
-rw-r--r--src/plugins/cmakeprojectmanager/fileapidataextractor.cpp61
-rw-r--r--src/plugins/cmakeprojectmanager/fileapidataextractor.h2
-rw-r--r--src/plugins/cmakeprojectmanager/fileapiparser.cpp7
-rw-r--r--src/plugins/cmakeprojectmanager/fileapiparser.h8
-rw-r--r--src/plugins/cmakeprojectmanager/fileapireader.cpp26
-rw-r--r--src/plugins/cmakeprojectmanager/fileapireader.h2
-rw-r--r--src/plugins/cmakeprojectmanager/presetsmacros.cpp56
-rw-r--r--src/plugins/cmakeprojectmanager/presetsparser.cpp14
-rw-r--r--src/plugins/cmakeprojectmanager/projecttreehelper.cpp33
-rw-r--r--src/plugins/cmakeprojectmanager/projecttreehelper.h2
-rw-r--r--src/plugins/coco/cocoplugin.cpp9
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp25
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs4
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp4
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp4
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp29
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h8
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp6
-rw-r--r--src/plugins/conan/CMakeLists.txt2
-rw-r--r--src/plugins/conan/conan.qbs1
-rw-r--r--src/plugins/conan/conaninstallstep.cpp42
-rw-r--r--src/plugins/conan/conanplugin.cpp84
-rw-r--r--src/plugins/conan/conanplugin.h34
-rw-r--r--src/plugins/conan/conansettings.cpp12
-rw-r--r--src/plugins/conan/conansettings.h4
-rw-r--r--src/plugins/copilot/CMakeLists.txt19
-rw-r--r--src/plugins/copilot/Copilot.json.in19
-rw-r--r--src/plugins/copilot/authwidget.cpp159
-rw-r--r--src/plugins/copilot/authwidget.h48
-rw-r--r--src/plugins/copilot/copilot.qbs37
-rw-r--r--src/plugins/copilot/copilot.qrc8
-rw-r--r--src/plugins/copilot/copilotclient.cpp252
-rw-r--r--src/plugins/copilot/copilotclient.h64
-rw-r--r--src/plugins/copilot/copilotconstants.h20
-rw-r--r--src/plugins/copilot/copilothoverhandler.cpp159
-rw-r--r--src/plugins/copilot/copilothoverhandler.h32
-rw-r--r--src/plugins/copilot/copiloticons.h12
-rw-r--r--src/plugins/copilot/copilotoptionspage.cpp101
-rw-r--r--src/plugins/copilot/copilotoptionspage.h18
-rw-r--r--src/plugins/copilot/copilotplugin.cpp139
-rw-r--r--src/plugins/copilot/copilotplugin.h31
-rw-r--r--src/plugins/copilot/copilotprojectpanel.cpp59
-rw-r--r--src/plugins/copilot/copilotprojectpanel.h15
-rw-r--r--src/plugins/copilot/copilotsettings.cpp118
-rw-r--r--src/plugins/copilot/copilotsettings.h41
-rw-r--r--src/plugins/copilot/copilotsuggestion.cpp79
-rw-r--r--src/plugins/copilot/copilotsuggestion.h32
-rw-r--r--src/plugins/copilot/copilottr.h15
-rw-r--r--src/plugins/copilot/images/copilot.pngbin0 -> 192 bytes
-rw-r--r--src/plugins/copilot/images/copilot@2x.pngbin0 -> 361 bytes
-rw-r--r--src/plugins/copilot/images/settingscategory_copilot.pngbin0 -> 251 bytes
-rw-r--r--src/plugins/copilot/images/settingscategory_copilot@2x.pngbin0 -> 497 bytes
-rw-r--r--src/plugins/copilot/requests/checkstatus.h54
-rw-r--r--src/plugins/copilot/requests/getcompletions.h119
-rw-r--r--src/plugins/copilot/requests/signinconfirm.h36
-rw-r--r--src/plugins/copilot/requests/signininitiate.h43
-rw-r--r--src/plugins/copilot/requests/signout.h26
-rw-r--r--src/plugins/coreplugin/CMakeLists.txt426
-rw-r--r--src/plugins/coreplugin/Core.json.in8
-rw-r--r--src/plugins/coreplugin/actionmanager/actionmanager.cpp4
-rw-r--r--src/plugins/coreplugin/actionmanager/command.cpp15
-rw-r--r--src/plugins/coreplugin/actionmanager/command.h2
-rw-r--r--src/plugins/coreplugin/actionmanager/commandbutton.cpp6
-rw-r--r--src/plugins/coreplugin/actionmanager/commandmappings.h2
-rw-r--r--src/plugins/coreplugin/actionsfilter.cpp139
-rw-r--r--src/plugins/coreplugin/actionsfilter.h17
-rw-r--r--src/plugins/coreplugin/coreconstants.h5
-rw-r--r--src/plugins/coreplugin/coreplugin.cpp13
-rw-r--r--src/plugins/coreplugin/coreplugin.h6
-rw-r--r--src/plugins/coreplugin/coreplugin.qbs19
-rw-r--r--src/plugins/coreplugin/dialogs/addtovcsdialog.cpp4
-rw-r--r--src/plugins/coreplugin/dialogs/externaltoolconfig.cpp8
-rw-r--r--src/plugins/coreplugin/dialogs/ioptionspage.cpp41
-rw-r--r--src/plugins/coreplugin/dialogs/ioptionspage.h45
-rw-r--r--src/plugins/coreplugin/dialogs/openwithdialog.cpp2
-rw-r--r--src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp8
-rw-r--r--src/plugins/coreplugin/dialogs/saveitemsdialog.cpp2
-rw-r--r--src/plugins/coreplugin/dialogs/settingsdialog.cpp28
-rw-r--r--src/plugins/coreplugin/dialogs/shortcutsettings.cpp57
-rw-r--r--src/plugins/coreplugin/dialogs/shortcutsettings.h13
-rw-r--r--src/plugins/coreplugin/documentmanager.cpp9
-rw-r--r--src/plugins/coreplugin/editormanager/documentmodel.cpp108
-rw-r--r--src/plugins/coreplugin/editormanager/documentmodel_p.h12
-rw-r--r--src/plugins/coreplugin/editormanager/editormanager.cpp44
-rw-r--r--src/plugins/coreplugin/editormanager/editormanager.h22
-rw-r--r--src/plugins/coreplugin/editormanager/iexternaleditor.cpp15
-rw-r--r--src/plugins/coreplugin/editortoolbar.cpp13
-rw-r--r--src/plugins/coreplugin/externaltool.cpp10
-rw-r--r--src/plugins/coreplugin/externaltool.h4
-rw-r--r--src/plugins/coreplugin/fancyactionbar.cpp17
-rw-r--r--src/plugins/coreplugin/featureprovider.cpp12
-rw-r--r--src/plugins/coreplugin/fileutils.cpp20
-rw-r--r--src/plugins/coreplugin/find/findtoolbar.cpp7
-rw-r--r--src/plugins/coreplugin/find/findtoolwindow.cpp3
-rw-r--r--src/plugins/coreplugin/find/itemviewfind.cpp11
-rw-r--r--src/plugins/coreplugin/find/searchresultcolor.h52
-rw-r--r--src/plugins/coreplugin/find/searchresultitem.h132
-rw-r--r--src/plugins/coreplugin/find/searchresulttreeitems.cpp11
-rw-r--r--src/plugins/coreplugin/find/searchresulttreeitems.h13
-rw-r--r--src/plugins/coreplugin/find/searchresulttreemodel.cpp26
-rw-r--r--src/plugins/coreplugin/find/searchresulttreemodel.h6
-rw-r--r--src/plugins/coreplugin/find/searchresulttreeview.cpp7
-rw-r--r--src/plugins/coreplugin/find/searchresulttreeview.h8
-rw-r--r--src/plugins/coreplugin/find/searchresultwidget.cpp8
-rw-r--r--src/plugins/coreplugin/find/searchresultwidget.h15
-rw-r--r--src/plugins/coreplugin/find/searchresultwindow.cpp67
-rw-r--r--src/plugins/coreplugin/find/searchresultwindow.h23
-rw-r--r--src/plugins/coreplugin/foldernavigationwidget.cpp3
-rw-r--r--src/plugins/coreplugin/generalsettings.cpp50
-rw-r--r--src/plugins/coreplugin/generalsettings.h2
-rw-r--r--src/plugins/coreplugin/ioutputpane.h2
-rw-r--r--src/plugins/coreplugin/iwizardfactory.cpp14
-rw-r--r--src/plugins/coreplugin/locator/basefilefilter.cpp276
-rw-r--r--src/plugins/coreplugin/locator/basefilefilter.h65
-rw-r--r--src/plugins/coreplugin/locator/commandlocator.cpp128
-rw-r--r--src/plugins/coreplugin/locator/commandlocator.h21
-rw-r--r--src/plugins/coreplugin/locator/directoryfilter.cpp141
-rw-r--r--src/plugins/coreplugin/locator/directoryfilter.h22
-rw-r--r--src/plugins/coreplugin/locator/executefilter.cpp140
-rw-r--r--src/plugins/coreplugin/locator/executefilter.h25
-rw-r--r--src/plugins/coreplugin/locator/externaltoolsfilter.cpp99
-rw-r--r--src/plugins/coreplugin/locator/externaltoolsfilter.h15
-rw-r--r--src/plugins/coreplugin/locator/filesystemfilter.cpp376
-rw-r--r--src/plugins/coreplugin/locator/filesystemfilter.h32
-rw-r--r--src/plugins/coreplugin/locator/ilocatorfilter.cpp981
-rw-r--r--src/plugins/coreplugin/locator/ilocatorfilter.h173
-rw-r--r--src/plugins/coreplugin/locator/javascriptfilter.cpp486
-rw-r--r--src/plugins/coreplugin/locator/javascriptfilter.h31
-rw-r--r--src/plugins/coreplugin/locator/locator.cpp29
-rw-r--r--src/plugins/coreplugin/locator/locator.h8
-rw-r--r--src/plugins/coreplugin/locator/locator_test.cpp28
-rw-r--r--src/plugins/coreplugin/locator/locatorfiltersfilter.cpp91
-rw-r--r--src/plugins/coreplugin/locator/locatorfiltersfilter.h21
-rw-r--r--src/plugins/coreplugin/locator/locatorfiltertest.cpp43
-rw-r--r--src/plugins/coreplugin/locator/locatorfiltertest.h18
-rw-r--r--src/plugins/coreplugin/locator/locatorsearchutils.cpp35
-rw-r--r--src/plugins/coreplugin/locator/locatorsearchutils.h16
-rw-r--r--src/plugins/coreplugin/locator/locatorsettingspage.cpp124
-rw-r--r--src/plugins/coreplugin/locator/locatorsettingspage.h8
-rw-r--r--src/plugins/coreplugin/locator/locatorwidget.cpp341
-rw-r--r--src/plugins/coreplugin/locator/locatorwidget.h29
-rw-r--r--src/plugins/coreplugin/locator/opendocumentsfilter.cpp129
-rw-r--r--src/plugins/coreplugin/locator/opendocumentsfilter.h37
-rw-r--r--src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp249
-rw-r--r--src/plugins/coreplugin/locator/spotlightlocatorfilter.h18
-rw-r--r--src/plugins/coreplugin/locator/urllocatorfilter.cpp72
-rw-r--r--src/plugins/coreplugin/locator/urllocatorfilter.h22
-rw-r--r--src/plugins/coreplugin/loggingviewer.cpp4
-rw-r--r--src/plugins/coreplugin/mainwindow.cpp64
-rw-r--r--src/plugins/coreplugin/manhattanstyle.cpp198
-rw-r--r--src/plugins/coreplugin/mimetypemagicdialog.cpp2
-rw-r--r--src/plugins/coreplugin/mimetypesettings.cpp55
-rw-r--r--src/plugins/coreplugin/mimetypesettings.h7
-rw-r--r--src/plugins/coreplugin/minisplitter.cpp4
-rw-r--r--src/plugins/coreplugin/navigationsubwidget.cpp3
-rw-r--r--src/plugins/coreplugin/outputpanemanager.cpp35
-rw-r--r--src/plugins/coreplugin/outputpanemanager.h1
-rw-r--r--src/plugins/coreplugin/patchtool.cpp7
-rw-r--r--src/plugins/coreplugin/plugindialog.cpp55
-rw-r--r--src/plugins/coreplugin/plugininstallwizard.cpp69
-rw-r--r--src/plugins/coreplugin/progressmanager/processprogress.cpp25
-rw-r--r--src/plugins/coreplugin/progressmanager/processprogress.h4
-rw-r--r--src/plugins/coreplugin/progressmanager/progressmanager.cpp29
-rw-r--r--src/plugins/coreplugin/progressmanager/progressmanager.h2
-rw-r--r--src/plugins/coreplugin/progressmanager/progressview.cpp42
-rw-r--r--src/plugins/coreplugin/progressmanager/progressview.h1
-rw-r--r--src/plugins/coreplugin/progressmanager/taskprogress.cpp5
-rw-r--r--src/plugins/coreplugin/progressmanager/taskprogress.h4
-rw-r--r--src/plugins/coreplugin/session.cpp745
-rw-r--r--src/plugins/coreplugin/session.h90
-rw-r--r--src/plugins/coreplugin/session_p.h57
-rw-r--r--src/plugins/coreplugin/sessiondialog.cpp (renamed from src/plugins/projectexplorer/sessiondialog.cpp)45
-rw-r--r--src/plugins/coreplugin/sessiondialog.h (renamed from src/plugins/projectexplorer/sessiondialog.h)6
-rw-r--r--src/plugins/coreplugin/sessionmodel.cpp (renamed from src/plugins/projectexplorer/sessionmodel.cpp)84
-rw-r--r--src/plugins/coreplugin/sessionmodel.h (renamed from src/plugins/projectexplorer/sessionmodel.h)19
-rw-r--r--src/plugins/coreplugin/sessionview.cpp (renamed from src/plugins/projectexplorer/sessionview.cpp)6
-rw-r--r--src/plugins/coreplugin/sessionview.h (renamed from src/plugins/projectexplorer/sessionview.h)6
-rw-r--r--src/plugins/coreplugin/statusbarmanager.cpp1
-rw-r--r--src/plugins/coreplugin/systemsettings.cpp24
-rw-r--r--src/plugins/coreplugin/welcomepagehelper.cpp215
-rw-r--r--src/plugins/coreplugin/welcomepagehelper.h52
-rw-r--r--src/plugins/cpaster/cpasterplugin.cpp6
-rw-r--r--src/plugins/cpaster/fileshareprotocol.cpp20
-rw-r--r--src/plugins/cpaster/fileshareprotocol.h5
-rw-r--r--src/plugins/cpaster/fileshareprotocolsettingspage.cpp29
-rw-r--r--src/plugins/cpaster/fileshareprotocolsettingspage.h14
-rw-r--r--src/plugins/cpaster/pasteselectdialog.cpp2
-rw-r--r--src/plugins/cpaster/pasteview.cpp5
-rw-r--r--src/plugins/cpaster/protocol.cpp2
-rw-r--r--src/plugins/cpaster/protocol.h2
-rw-r--r--src/plugins/cpaster/settings.cpp40
-rw-r--r--src/plugins/cpaster/settings.h20
-rw-r--r--src/plugins/cpaster/stickynotespasteprotocol.h1
-rw-r--r--src/plugins/cppcheck/cppcheckconstants.h17
-rw-r--r--src/plugins/cppcheck/cppcheckmanualrundialog.cpp19
-rw-r--r--src/plugins/cppcheck/cppcheckmanualrundialog.h8
-rw-r--r--src/plugins/cppcheck/cppcheckoptions.cpp267
-rw-r--r--src/plugins/cppcheck/cppcheckoptions.h98
-rw-r--r--src/plugins/cppcheck/cppcheckplugin.cpp34
-rw-r--r--src/plugins/cppcheck/cppcheckrunner.cpp6
-rw-r--r--src/plugins/cppcheck/cppcheckrunner.h4
-rw-r--r--src/plugins/cppcheck/cppchecktool.cpp51
-rw-r--r--src/plugins/cppcheck/cppchecktool.h8
-rw-r--r--src/plugins/cppcheck/cppchecktrigger.cpp4
-rw-r--r--src/plugins/cppeditor/CMakeLists.txt2
-rw-r--r--src/plugins/cppeditor/baseeditordocumentparser.cpp9
-rw-r--r--src/plugins/cppeditor/baseeditordocumentparser.h11
-rw-r--r--src/plugins/cppeditor/baseeditordocumentprocessor.cpp19
-rw-r--r--src/plugins/cppeditor/baseeditordocumentprocessor.h7
-rw-r--r--src/plugins/cppeditor/builtincursorinfo.cpp6
-rw-r--r--src/plugins/cppeditor/builtineditordocumentparser.cpp10
-rw-r--r--src/plugins/cppeditor/builtineditordocumentparser.h3
-rw-r--r--src/plugins/cppeditor/builtineditordocumentprocessor.cpp50
-rw-r--r--src/plugins/cppeditor/clangdiagnosticconfig.h4
-rw-r--r--src/plugins/cppeditor/clangdiagnosticconfigswidget.cpp8
-rw-r--r--src/plugins/cppeditor/compileroptionsbuilder.cpp7
-rw-r--r--src/plugins/cppeditor/compileroptionsbuilder.h1
-rw-r--r--src/plugins/cppeditor/compileroptionsbuilder_test.cpp3
-rw-r--r--src/plugins/cppeditor/cppcanonicalsymbol.cpp2
-rw-r--r--src/plugins/cppeditor/cppcodeformatter.cpp2
-rw-r--r--src/plugins/cppeditor/cppcodemodelinspectordialog.cpp21
-rw-r--r--src/plugins/cppeditor/cppcodemodelinspectordumper.cpp8
-rw-r--r--src/plugins/cppeditor/cppcodemodelsettings.cpp44
-rw-r--r--src/plugins/cppeditor/cppcodemodelsettings.h5
-rw-r--r--src/plugins/cppeditor/cppcodemodelsettingspage.cpp43
-rw-r--r--src/plugins/cppeditor/cppcodestylesettingspage.cpp85
-rw-r--r--src/plugins/cppeditor/cppcodestylesettingspage.h8
-rw-r--r--src/plugins/cppeditor/cppcompletion_test.cpp16
-rw-r--r--src/plugins/cppeditor/cppcompletionassist.cpp6
-rw-r--r--src/plugins/cppeditor/cppcurrentdocumentfilter.cpp169
-rw-r--r--src/plugins/cppeditor/cppcurrentdocumentfilter.h49
-rw-r--r--src/plugins/cppeditor/cppeditor.qbs11
-rw-r--r--src/plugins/cppeditor/cppeditorconstants.h8
-rw-r--r--src/plugins/cppeditor/cppeditordocument.cpp9
-rw-r--r--src/plugins/cppeditor/cppeditoroutline.cpp1
-rw-r--r--src/plugins/cppeditor/cppeditorplugin.cpp6
-rw-r--r--src/plugins/cppeditor/cppeditorwidget.cpp21
-rw-r--r--src/plugins/cppeditor/cppelementevaluator.cpp45
-rw-r--r--src/plugins/cppeditor/cppelementevaluator.h10
-rw-r--r--src/plugins/cppeditor/cppfindreferences.cpp95
-rw-r--r--src/plugins/cppeditor/cppfindreferences.h19
-rw-r--r--src/plugins/cppeditor/cppfollowsymbolundercursor.cpp13
-rw-r--r--src/plugins/cppeditor/cppfunctiondecldeflink.cpp4
-rw-r--r--src/plugins/cppeditor/cpphighlighter.cpp89
-rw-r--r--src/plugins/cppeditor/cpphighlighter.h1
-rw-r--r--src/plugins/cppeditor/cppincludehierarchy.cpp2
-rw-r--r--src/plugins/cppeditor/cppincludesfilter.cpp149
-rw-r--r--src/plugins/cppeditor/cppincludesfilter.h14
-rw-r--r--src/plugins/cppeditor/cppindexingsupport.cpp65
-rw-r--r--src/plugins/cppeditor/cppindexingsupport.h7
-rw-r--r--src/plugins/cppeditor/cpplocatordata.cpp16
-rw-r--r--src/plugins/cppeditor/cpplocatordata.h2
-rw-r--r--src/plugins/cppeditor/cpplocatorfilter.cpp395
-rw-r--r--src/plugins/cppeditor/cpplocatorfilter.h53
-rw-r--r--src/plugins/cppeditor/cpplocatorfilter_test.cpp127
-rw-r--r--src/plugins/cppeditor/cppmodelmanager.cpp320
-rw-r--r--src/plugins/cppeditor/cppmodelmanager.h2
-rw-r--r--src/plugins/cppeditor/cppmodelmanager_test.cpp12
-rw-r--r--src/plugins/cppeditor/cppoutline.cpp8
-rw-r--r--src/plugins/cppeditor/cppoutlinemodel.cpp28
-rw-r--r--src/plugins/cppeditor/cppoutlinemodel.h6
-rw-r--r--src/plugins/cppeditor/cpppreprocessordialog.cpp6
-rw-r--r--src/plugins/cppeditor/cppprojectinfogenerator.cpp16
-rw-r--r--src/plugins/cppeditor/cppprojectinfogenerator.h11
-rw-r--r--src/plugins/cppeditor/cppprojectupdater.cpp28
-rw-r--r--src/plugins/cppeditor/cppprojectupdater.h4
-rw-r--r--src/plugins/cppeditor/cppquickfix_test.cpp263
-rw-r--r--src/plugins/cppeditor/cppquickfix_test.h4
-rw-r--r--src/plugins/cppeditor/cppquickfixes.cpp599
-rw-r--r--src/plugins/cppeditor/cppquickfixes.h53
-rw-r--r--src/plugins/cppeditor/cppquickfixprojectsettingswidget.cpp2
-rw-r--r--src/plugins/cppeditor/cppquickfixsettingspage.cpp26
-rw-r--r--src/plugins/cppeditor/cppquickfixsettingspage.h18
-rw-r--r--src/plugins/cppeditor/cppquickfixsettingswidget.cpp21
-rw-r--r--src/plugins/cppeditor/cppquickfixsettingswidget.h7
-rw-r--r--src/plugins/cppeditor/cpprefactoringchanges.cpp4
-rw-r--r--src/plugins/cppeditor/cppsemanticinfoupdater.cpp17
-rw-r--r--src/plugins/cppeditor/cppsourceprocessor.cpp18
-rw-r--r--src/plugins/cppeditor/cpptoolsjsextension.cpp11
-rw-r--r--src/plugins/cppeditor/cpptoolsreuse.cpp14
-rw-r--r--src/plugins/cppeditor/cpptoolssettings.cpp48
-rw-r--r--src/plugins/cppeditor/cpptoolstestcase.cpp10
-rw-r--r--src/plugins/cppeditor/cpptypehierarchy.cpp5
-rw-r--r--src/plugins/cppeditor/cppuseselections_test.cpp2
-rw-r--r--src/plugins/cppeditor/cppworkingcopy.cpp14
-rw-r--r--src/plugins/cppeditor/cppworkingcopy.h11
-rw-r--r--src/plugins/cppeditor/doxygengenerator.cpp12
-rw-r--r--src/plugins/cppeditor/editordocumenthandle.cpp5
-rw-r--r--src/plugins/cppeditor/editordocumenthandle.h4
-rw-r--r--src/plugins/cppeditor/fileandtokenactions_test.cpp2
-rw-r--r--src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp1
-rw-r--r--src/plugins/cppeditor/generatedcodemodelsupport.cpp2
-rw-r--r--src/plugins/cppeditor/generatedcodemodelsupport.h2
-rw-r--r--src/plugins/cppeditor/indexitem.cpp4
-rw-r--r--src/plugins/cppeditor/indexitem.h5
-rw-r--r--src/plugins/cppeditor/insertionpointlocator.h26
-rw-r--r--src/plugins/cppeditor/modelmanagertesthelper.cpp6
-rw-r--r--src/plugins/cppeditor/projectinfo_test.cpp6
-rw-r--r--src/plugins/cppeditor/projectpart.cpp1
-rw-r--r--src/plugins/cppeditor/resourcepreviewhoverhandler.cpp8
-rw-r--r--src/plugins/cppeditor/searchsymbols.cpp3
-rw-r--r--src/plugins/cppeditor/semantichighlighter.cpp31
-rw-r--r--src/plugins/cppeditor/semantichighlighter.h12
-rw-r--r--src/plugins/cppeditor/senddocumenttracker.cpp236
-rw-r--r--src/plugins/cppeditor/senddocumenttracker.h67
-rw-r--r--src/plugins/cppeditor/symbolsearcher_test.cpp12
-rw-r--r--src/plugins/cppeditor/symbolsfindfilter.cpp15
-rw-r--r--src/plugins/cppeditor/symbolsfindfilter.h10
-rw-r--r--src/plugins/cppeditor/testcases/highlightingtestcase.cpp14
-rw-r--r--src/plugins/cppeditor/typehierarchybuilder.cpp28
-rw-r--r--src/plugins/cppeditor/typehierarchybuilder.h12
-rw-r--r--src/plugins/ctfvisualizer/ctfvisualizertool.cpp4
-rw-r--r--src/plugins/cvs/cvsplugin.cpp42
-rw-r--r--src/plugins/cvs/cvssettings.cpp71
-rw-r--r--src/plugins/cvs/cvssettings.h20
-rw-r--r--src/plugins/debugger/CMakeLists.txt1
-rw-r--r--src/plugins/debugger/analyzer/analyzerutils.cpp2
-rw-r--r--src/plugins/debugger/breakhandler.cpp23
-rw-r--r--src/plugins/debugger/breakhandler.h2
-rw-r--r--src/plugins/debugger/breakpoint.cpp7
-rw-r--r--src/plugins/debugger/breakpoint.h2
-rw-r--r--src/plugins/debugger/cdb/cdbengine.cpp127
-rw-r--r--src/plugins/debugger/cdb/cdbengine.h14
-rw-r--r--src/plugins/debugger/commonoptionspage.cpp51
-rw-r--r--src/plugins/debugger/dap/dapengine.cpp799
-rw-r--r--src/plugins/debugger/dap/dapengine.h98
-rw-r--r--src/plugins/debugger/debugger.qbs12
-rw-r--r--src/plugins/debugger/debugger.qrc1
-rw-r--r--src/plugins/debugger/debuggeractions.cpp15
-rw-r--r--src/plugins/debugger/debuggeractions.h5
-rw-r--r--src/plugins/debugger/debuggerconstants.h1
-rw-r--r--src/plugins/debugger/debuggerdialogs.cpp1
-rw-r--r--src/plugins/debugger/debuggerengine.cpp56
-rw-r--r--src/plugins/debugger/debuggerengine.h15
-rw-r--r--src/plugins/debugger/debuggeritem.cpp30
-rw-r--r--src/plugins/debugger/debuggeritem.h5
-rw-r--r--src/plugins/debugger/debuggeritemmanager.cpp136
-rw-r--r--src/plugins/debugger/debuggerkitinformation.cpp100
-rw-r--r--src/plugins/debugger/debuggerkitinformation.h1
-rw-r--r--src/plugins/debugger/debuggermainwindow.cpp14
-rw-r--r--src/plugins/debugger/debuggerplugin.cpp34
-rw-r--r--src/plugins/debugger/debuggerrunconfigurationaspect.cpp223
-rw-r--r--src/plugins/debugger/debuggerrunconfigurationaspect.h6
-rw-r--r--src/plugins/debugger/debuggerruncontrol.cpp70
-rw-r--r--src/plugins/debugger/debuggersourcepathmappingwidget.cpp8
-rw-r--r--src/plugins/debugger/debuggertooltipmanager.cpp43
-rw-r--r--src/plugins/debugger/gdb/gdbengine.cpp269
-rw-r--r--src/plugins/debugger/gdb/gdbengine.h24
-rw-r--r--src/plugins/debugger/gdb/gdboptionspage.cpp4
-rw-r--r--src/plugins/debugger/images/pin.xpm19
-rw-r--r--src/plugins/debugger/lldb/lldbengine.cpp43
-rw-r--r--src/plugins/debugger/lldb/lldbengine.h8
-rw-r--r--src/plugins/debugger/loadcoredialog.cpp18
-rw-r--r--src/plugins/debugger/moduleshandler.cpp32
-rw-r--r--src/plugins/debugger/moduleshandler.h6
-rw-r--r--src/plugins/debugger/pdb/pdbengine.cpp20
-rw-r--r--src/plugins/debugger/pdb/pdbengine.h8
-rw-r--r--src/plugins/debugger/qml/qmlengine.cpp32
-rw-r--r--src/plugins/debugger/qml/qmlengine.h6
-rw-r--r--src/plugins/debugger/qml/qmlengineutils.cpp8
-rw-r--r--src/plugins/debugger/qml/qmlengineutils.h4
-rw-r--r--src/plugins/debugger/qml/qmlinspectoragent.cpp7
-rw-r--r--src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp2
-rw-r--r--src/plugins/debugger/sourcefileshandler.cpp14
-rw-r--r--src/plugins/debugger/sourcefileshandler.h6
-rw-r--r--src/plugins/debugger/stackframe.cpp2
-rw-r--r--src/plugins/debugger/stackhandler.cpp2
-rw-r--r--src/plugins/debugger/terminal.cpp21
-rw-r--r--src/plugins/debugger/terminal.h4
-rw-r--r--src/plugins/debugger/uvsc/uvscengine.cpp2
-rw-r--r--src/plugins/debugger/watchdata.cpp14
-rw-r--r--src/plugins/debugger/watchdata.h2
-rw-r--r--src/plugins/debugger/watchhandler.cpp79
-rw-r--r--src/plugins/debugger/watchhandler.h1
-rw-r--r--src/plugins/designer/CMakeLists.txt2
-rw-r--r--src/plugins/designer/codemodelhelpers.cpp4
-rw-r--r--src/plugins/designer/cpp/newclasswidget.cpp5
-rw-r--r--src/plugins/designer/designer.qbs6
-rw-r--r--src/plugins/designer/designercontext.cpp4
-rw-r--r--src/plugins/designer/editorwidget.cpp4
-rw-r--r--src/plugins/designer/formeditor.cpp (renamed from src/plugins/designer/formeditorw.cpp)54
-rw-r--r--src/plugins/designer/formeditor.h64
-rw-r--r--src/plugins/designer/formeditorfactory.cpp12
-rw-r--r--src/plugins/designer/formeditorplugin.cpp8
-rw-r--r--src/plugins/designer/formeditorstack.cpp4
-rw-r--r--src/plugins/designer/formeditorw.h69
-rw-r--r--src/plugins/designer/formtemplatewizardpage.cpp4
-rw-r--r--src/plugins/designer/gotoslot_test.cpp4
-rw-r--r--src/plugins/designer/qtcreatorintegration.cpp37
-rw-r--r--src/plugins/designer/resourcehandler.cpp13
-rw-r--r--src/plugins/designer/settingspage.cpp56
-rw-r--r--src/plugins/designer/settingspage.h23
-rw-r--r--src/plugins/diffeditor/diffeditor.cpp85
-rw-r--r--src/plugins/diffeditor/diffeditor.h1
-rw-r--r--src/plugins/diffeditor/diffeditorcontroller.cpp1
-rw-r--r--src/plugins/diffeditor/diffeditorcontroller.h8
-rw-r--r--src/plugins/diffeditor/diffeditordocument.cpp6
-rw-r--r--src/plugins/diffeditor/diffeditorplugin.cpp59
-rw-r--r--src/plugins/diffeditor/diffeditorplugin.h4
-rw-r--r--src/plugins/diffeditor/diffutils.cpp108
-rw-r--r--src/plugins/diffeditor/diffutils.h8
-rw-r--r--src/plugins/diffeditor/diffview.cpp27
-rw-r--r--src/plugins/diffeditor/diffview.h9
-rw-r--r--src/plugins/diffeditor/sidebysidediffeditorwidget.cpp70
-rw-r--r--src/plugins/diffeditor/sidebysidediffeditorwidget.h27
-rw-r--r--src/plugins/diffeditor/unifieddiffeditorwidget.cpp80
-rw-r--r--src/plugins/diffeditor/unifieddiffeditorwidget.h28
-rw-r--r--src/plugins/docker/dockerapi.cpp12
-rw-r--r--src/plugins/docker/dockerdevice.cpp371
-rw-r--r--src/plugins/docker/dockerdevice.h6
-rw-r--r--src/plugins/docker/dockerdevicewidget.cpp5
-rw-r--r--src/plugins/docker/dockerplugin.cpp11
-rw-r--r--src/plugins/docker/dockerplugin.h6
-rw-r--r--src/plugins/docker/dockersettings.cpp51
-rw-r--r--src/plugins/docker/dockersettings.h12
-rw-r--r--src/plugins/emacskeys/emacskeysplugin.cpp5
-rw-r--r--src/plugins/emacskeys/emacskeysplugin.h1
-rw-r--r--src/plugins/fakevim/fakevim.qbs4
-rw-r--r--src/plugins/fakevim/fakevimactions.cpp156
-rw-r--r--src/plugins/fakevim/fakevimactions.h15
-rw-r--r--src/plugins/fakevim/fakevimhandler.cpp183
-rw-r--r--src/plugins/fakevim/fakevimhandler.h87
-rw-r--r--src/plugins/fakevim/fakevimplugin.cpp734
-rw-r--r--src/plugins/fossil/configuredialog.cpp2
-rw-r--r--src/plugins/fossil/fossilclient.cpp58
-rw-r--r--src/plugins/fossil/fossilclient.h3
-rw-r--r--src/plugins/fossil/fossilcommitwidget.cpp9
-rw-r--r--src/plugins/fossil/fossilplugin.cpp60
-rw-r--r--src/plugins/fossil/fossilplugin.h3
-rw-r--r--src/plugins/fossil/fossilsettings.cpp130
-rw-r--r--src/plugins/fossil/fossilsettings.h51
-rw-r--r--src/plugins/fossil/pullorpushdialog.cpp14
-rw-r--r--src/plugins/fossil/pullorpushdialog.h12
-rw-r--r--src/plugins/fossil/wizard/fossiljsextension.cpp34
-rw-r--r--src/plugins/fossil/wizard/fossiljsextension.h8
-rw-r--r--src/plugins/fossil/wizard/projects/vcs/wizard.json2
-rw-r--r--src/plugins/genericprojectmanager/genericproject.cpp10
-rw-r--r--src/plugins/git/branchadddialog.cpp2
-rw-r--r--src/plugins/git/branchcheckoutdialog.cpp2
-rw-r--r--src/plugins/git/branchmodel.cpp112
-rw-r--r--src/plugins/git/branchmodel.h3
-rw-r--r--src/plugins/git/branchview.cpp82
-rw-r--r--src/plugins/git/branchview.h4
-rw-r--r--src/plugins/git/changeselectiondialog.cpp10
-rw-r--r--src/plugins/git/changeselectiondialog.h4
-rw-r--r--src/plugins/git/gerrit/gerritmodel.cpp12
-rw-r--r--src/plugins/git/gerrit/gerritoptionspage.cpp167
-rw-r--r--src/plugins/git/gerrit/gerritoptionspage.h49
-rw-r--r--src/plugins/git/gerrit/gerritplugin.cpp37
-rw-r--r--src/plugins/git/gerrit/gerritplugin.h9
-rw-r--r--src/plugins/git/gerrit/gerritpushdialog.cpp4
-rw-r--r--src/plugins/git/gitclient.cpp255
-rw-r--r--src/plugins/git/gitclient.h22
-rw-r--r--src/plugins/git/giteditor.cpp2
-rw-r--r--src/plugins/git/gitgrep.cpp262
-rw-r--r--src/plugins/git/gitgrep.h8
-rw-r--r--src/plugins/git/gitplugin.cpp135
-rw-r--r--src/plugins/git/gitplugin.h3
-rw-r--r--src/plugins/git/gitsettings.cpp123
-rw-r--r--src/plugins/git/gitsettings.h43
-rw-r--r--src/plugins/git/gitsubmiteditor.cpp4
-rw-r--r--src/plugins/git/gitsubmiteditorwidget.cpp5
-rw-r--r--src/plugins/git/logchangedialog.cpp4
-rw-r--r--src/plugins/git/mergetool.cpp4
-rw-r--r--src/plugins/git/mergetool.h4
-rw-r--r--src/plugins/gitlab/gitlabclonedialog.cpp2
-rw-r--r--src/plugins/gitlab/gitlabdialog.cpp4
-rw-r--r--src/plugins/gitlab/gitlaboptionspage.cpp150
-rw-r--r--src/plugins/gitlab/gitlaboptionspage.h75
-rw-r--r--src/plugins/gitlab/gitlabparameters.cpp7
-rw-r--r--src/plugins/gitlab/gitlabparameters.h9
-rw-r--r--src/plugins/gitlab/gitlabplugin.cpp43
-rw-r--r--src/plugins/gitlab/gitlabprojectsettings.cpp2
-rw-r--r--src/plugins/gitlab/queryrunner.cpp2
-rw-r--r--src/plugins/gitlab/queryrunner.h4
-rw-r--r--src/plugins/haskell/CMakeLists.txt3
-rw-r--r--src/plugins/haskell/haskell.qbs2
-rw-r--r--src/plugins/haskell/haskell.qrc3
-rw-r--r--src/plugins/haskell/haskelleditorfactory.cpp14
-rw-r--r--src/plugins/haskell/haskelleditorfactory.h6
-rw-r--r--src/plugins/haskell/haskellmanager.cpp85
-rw-r--r--src/plugins/haskell/haskellmanager.h21
-rw-r--r--src/plugins/haskell/haskellplugin.cpp18
-rw-r--r--src/plugins/haskell/haskellproject.cpp1
-rw-r--r--src/plugins/haskell/haskellrunconfiguration.cpp10
-rw-r--r--src/plugins/haskell/haskellsettings.cpp58
-rw-r--r--src/plugins/haskell/haskellsettings.h20
-rw-r--r--src/plugins/haskell/images/category_haskell.pngbin6264 -> 0 bytes
-rw-r--r--src/plugins/haskell/images/settingscategory_haskell.pngbin0 -> 187 bytes
-rw-r--r--src/plugins/haskell/images/settingscategory_haskell@2x.pngbin0 -> 322 bytes
-rw-r--r--src/plugins/haskell/optionspage.cpp63
-rw-r--r--src/plugins/haskell/optionspage.h31
-rw-r--r--src/plugins/haskell/stackbuildstep.cpp4
-rw-r--r--src/plugins/help/CMakeLists.txt3
-rw-r--r--src/plugins/help/filtersettingspage.cpp74
-rw-r--r--src/plugins/help/filtersettingspage.h29
-rw-r--r--src/plugins/help/generalsettingspage.cpp310
-rw-r--r--src/plugins/help/generalsettingspage.h43
-rw-r--r--src/plugins/help/help.qbs1
-rw-r--r--src/plugins/help/helpindexfilter.cpp123
-rw-r--r--src/plugins/help/helpindexfilter.h34
-rw-r--r--src/plugins/help/helpmanager.cpp19
-rw-r--r--src/plugins/help/helpmanager.h14
-rw-r--r--src/plugins/help/helpmode.cpp24
-rw-r--r--src/plugins/help/helpmode.h21
-rw-r--r--src/plugins/help/helpplugin.cpp70
-rw-r--r--src/plugins/help/helpplugin.h2
-rw-r--r--src/plugins/help/helpwidget.cpp5
-rw-r--r--src/plugins/imageviewer/CMakeLists.txt2
-rw-r--r--src/plugins/imageviewer/imageview.cpp2
-rw-r--r--src/plugins/imageviewer/imageviewer.cpp5
-rw-r--r--src/plugins/incredibuild/CMakeLists.txt3
-rw-r--r--src/plugins/incredibuild/buildconsolebuildstep.cpp9
-rw-r--r--src/plugins/incredibuild/cmakecommandbuilder.cpp2
-rw-r--r--src/plugins/incredibuild/commandbuilderaspect.cpp8
-rw-r--r--src/plugins/incredibuild/commandbuilderaspect.h2
-rw-r--r--src/plugins/incredibuild/incredibuild.qbs4
-rw-r--r--src/plugins/incredibuild/incredibuildplugin.cpp28
-rw-r--r--src/plugins/incredibuild/incredibuildplugin.h25
-rw-r--r--src/plugins/insight/insightmodel.cpp6
-rw-r--r--src/plugins/ios/createsimulatordialog.cpp9
-rw-r--r--src/plugins/ios/iosbuildstep.cpp8
-rw-r--r--src/plugins/ios/iosconfigurations.cpp9
-rw-r--r--src/plugins/ios/iosdevice.cpp6
-rw-r--r--src/plugins/ios/iosdevice.h1
-rw-r--r--src/plugins/ios/iosdsymbuildstep.cpp14
-rw-r--r--src/plugins/ios/iosprobe.cpp4
-rw-r--r--src/plugins/ios/iosrunconfiguration.cpp8
-rw-r--r--src/plugins/ios/iosrunconfiguration.h2
-rw-r--r--src/plugins/ios/iosrunner.cpp2
-rw-r--r--src/plugins/ios/iossettingswidget.cpp23
-rw-r--r--src/plugins/ios/iossimulator.cpp9
-rw-r--r--src/plugins/ios/iossimulator.h1
-rw-r--r--src/plugins/ios/iostoolhandler.cpp52
-rw-r--r--src/plugins/ios/simulatorcontrol.cpp127
-rw-r--r--src/plugins/ios/simulatorcontrol.h6
-rw-r--r--src/plugins/ios/simulatorinfomodel.cpp6
-rw-r--r--src/plugins/ios/simulatoroperationdialog.cpp2
-rw-r--r--src/plugins/languageclient/CMakeLists.txt8
-rw-r--r--src/plugins/languageclient/callhierarchy.cpp48
-rw-r--r--src/plugins/languageclient/callhierarchy.h6
-rw-r--r--src/plugins/languageclient/client.cpp82
-rw-r--r--src/plugins/languageclient/client.h13
-rw-r--r--src/plugins/languageclient/clientrequesttask.cpp39
-rw-r--r--src/plugins/languageclient/clientrequesttask.h80
-rw-r--r--src/plugins/languageclient/currentdocumentsymbolsrequest.cpp83
-rw-r--r--src/plugins/languageclient/currentdocumentsymbolsrequest.h53
-rw-r--r--src/plugins/languageclient/documentsymbolcache.cpp2
-rw-r--r--src/plugins/languageclient/languageclient.qbs4
-rw-r--r--src/plugins/languageclient/languageclient_global.h13
-rw-r--r--src/plugins/languageclient/languageclientcompletionassist.cpp1
-rw-r--r--src/plugins/languageclient/languageclientinterface.cpp12
-rw-r--r--src/plugins/languageclient/languageclientinterface.h11
-rw-r--r--src/plugins/languageclient/languageclientmanager.cpp88
-rw-r--r--src/plugins/languageclient/languageclientmanager.h18
-rw-r--r--src/plugins/languageclient/languageclientoutline.cpp110
-rw-r--r--src/plugins/languageclient/languageclientoutline.h38
-rw-r--r--src/plugins/languageclient/languageclientplugin.cpp4
-rw-r--r--src/plugins/languageclient/languageclientsettings.cpp129
-rw-r--r--src/plugins/languageclient/languageclientsymbolsupport.cpp34
-rw-r--r--src/plugins/languageclient/languageclientsymbolsupport.h13
-rw-r--r--src/plugins/languageclient/languageclientutils.cpp5
-rw-r--r--src/plugins/languageclient/locatorfilter.cpp474
-rw-r--r--src/plugins/languageclient/locatorfilter.h123
-rw-r--r--src/plugins/languageclient/progressmanager.cpp52
-rw-r--r--src/plugins/languageclient/progressmanager.h15
-rw-r--r--src/plugins/macros/CMakeLists.txt1
-rw-r--r--src/plugins/macros/macrolocatorfilter.cpp96
-rw-r--r--src/plugins/macros/macrolocatorfilter.h18
-rw-r--r--src/plugins/macros/macrooptionspage.cpp189
-rw-r--r--src/plugins/macros/macrooptionspage.h6
-rw-r--r--src/plugins/macros/macrooptionswidget.cpp166
-rw-r--r--src/plugins/macros/macrooptionswidget.h55
-rw-r--r--src/plugins/macros/macros.qbs2
-rw-r--r--src/plugins/macros/savedialog.cpp2
-rw-r--r--src/plugins/marketplace/productlistmodel.cpp2
-rw-r--r--src/plugins/mcusupport/CMakeLists.txt1
-rw-r--r--src/plugins/mcusupport/dialogs/mcukitcreationdialog.cpp134
-rw-r--r--src/plugins/mcusupport/dialogs/mcukitcreationdialog.h42
-rw-r--r--src/plugins/mcusupport/mcubuildstep.cpp40
-rw-r--r--src/plugins/mcusupport/mcubuildstep.h22
-rw-r--r--src/plugins/mcusupport/mcukitinformation.cpp2
-rw-r--r--src/plugins/mcusupport/mcukitmanager.cpp71
-rw-r--r--src/plugins/mcusupport/mcuqmlprojectnode.h2
-rw-r--r--src/plugins/mcusupport/mcusupport.qbs7
-rw-r--r--src/plugins/mcusupport/mcusupport.qrc1
-rw-r--r--src/plugins/mcusupport/mcusupport_global.h9
-rw-r--r--src/plugins/mcusupport/mcusupportconstants.h2
-rw-r--r--src/plugins/mcusupport/mcusupportdevice.h1
-rw-r--r--src/plugins/mcusupport/mcusupportoptions.cpp27
-rw-r--r--src/plugins/mcusupport/mcusupportoptions.h3
-rw-r--r--src/plugins/mcusupport/mcusupportoptionspage.cpp9
-rw-r--r--src/plugins/mcusupport/mcusupportplugin.cpp6
-rw-r--r--src/plugins/mcusupport/mcusupportversiondetection.cpp4
-rw-r--r--src/plugins/mcusupport/mcutarget.cpp20
-rw-r--r--src/plugins/mcusupport/mcutarget.h2
-rw-r--r--src/plugins/mcusupport/settingshandler.cpp13
-rw-r--r--src/plugins/mcusupport/settingshandler.h3
-rw-r--r--src/plugins/mcusupport/test/unittest.cpp10
-rw-r--r--src/plugins/mcusupport/wizards/qmlproject/CMakeLists.txt3
-rw-r--r--src/plugins/mcusupport/wizards/qmlproject/Qul.cmake3
-rw-r--r--src/plugins/mcusupport/wizards/qmlproject/wizard.json26
-rw-r--r--src/plugins/mercurial/authenticationdialog.cpp2
-rw-r--r--src/plugins/mercurial/mercurialclient.cpp15
-rw-r--r--src/plugins/mercurial/mercurialclient.h3
-rw-r--r--src/plugins/mercurial/mercurialcommitwidget.cpp7
-rw-r--r--src/plugins/mercurial/mercurialplugin.cpp13
-rw-r--r--src/plugins/mercurial/mercurialsettings.cpp48
-rw-r--r--src/plugins/mercurial/mercurialsettings.h12
-rw-r--r--src/plugins/mercurial/revertdialog.cpp6
-rw-r--r--src/plugins/mesonprojectmanager/CMakeLists.txt2
-rw-r--r--src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp5
-rw-r--r--src/plugins/mesonprojectmanager/mesonbuildconfiguration.h4
-rw-r--r--src/plugins/mesonprojectmanager/mesonbuildsettingswidget.cpp14
-rw-r--r--src/plugins/mesonprojectmanager/mesonbuildsystem.cpp2
-rw-r--r--src/plugins/mesonprojectmanager/mesonpluginconstants.h1
-rw-r--r--src/plugins/mesonprojectmanager/mesonprocess.cpp10
-rw-r--r--src/plugins/mesonprojectmanager/mesonprocess.h4
-rw-r--r--src/plugins/mesonprojectmanager/mesonprojectmanager.qbs2
-rw-r--r--src/plugins/mesonprojectmanager/mesonprojectparser.cpp4
-rw-r--r--src/plugins/mesonprojectmanager/mesonprojectplugin.cpp4
-rw-r--r--src/plugins/mesonprojectmanager/mesonrunconfiguration.cpp93
-rw-r--r--src/plugins/mesonprojectmanager/mesonrunconfiguration.h15
-rw-r--r--src/plugins/mesonprojectmanager/mesonwrapper.h4
-rw-r--r--src/plugins/mesonprojectmanager/ninjabuildstep.cpp18
-rw-r--r--src/plugins/mesonprojectmanager/settings.cpp57
-rw-r--r--src/plugins/mesonprojectmanager/settings.h20
-rw-r--r--src/plugins/mesonprojectmanager/toolitemsettings.cpp3
-rw-r--r--src/plugins/mesonprojectmanager/toolkitaspectwidget.h6
-rw-r--r--src/plugins/mesonprojectmanager/toolssettingsaccessor.cpp8
-rw-r--r--src/plugins/mesonprojectmanager/toolssettingspage.cpp117
-rw-r--r--src/plugins/mesonprojectmanager/toolssettingspage.h6
-rw-r--r--src/plugins/mesonprojectmanager/toolssettingswidget.cpp107
-rw-r--r--src/plugins/mesonprojectmanager/toolssettingswidget.h40
-rw-r--r--src/plugins/mesonprojectmanager/toolwrapper.cpp4
-rw-r--r--src/plugins/modeleditor/componentviewcontroller.cpp16
-rw-r--r--src/plugins/modeleditor/elementtasks.cpp54
-rw-r--r--src/plugins/modeleditor/modelindexer.cpp34
-rw-r--r--src/plugins/nim/editor/nimcompletionassistprovider.cpp6
-rw-r--r--src/plugins/nim/editor/nimtexteditorwidget.cpp2
-rw-r--r--src/plugins/nim/images/settingscategory_nim.pngbin245 -> 245 bytes
-rw-r--r--src/plugins/nim/images/settingscategory_nim@2x.pngbin511 -> 511 bytes
-rw-r--r--src/plugins/nim/nimplugin.cpp1
-rw-r--r--src/plugins/nim/project/nimblebuildsystem.cpp8
-rw-r--r--src/plugins/nim/project/nimblerunconfiguration.cpp5
-rw-r--r--src/plugins/nim/project/nimbletaskstep.cpp36
-rw-r--r--src/plugins/nim/project/nimbuildsystem.cpp6
-rw-r--r--src/plugins/nim/project/nimcompilerbuildstep.cpp4
-rw-r--r--src/plugins/nim/project/nimcompilercleanstep.cpp2
-rw-r--r--src/plugins/nim/project/nimrunconfiguration.cpp5
-rw-r--r--src/plugins/nim/project/nimtoolchain.cpp4
-rw-r--r--src/plugins/nim/project/nimtoolchainfactory.cpp5
-rw-r--r--src/plugins/nim/settings/nimcodestylepreferenceswidget.cpp5
-rw-r--r--src/plugins/nim/settings/nimcodestylesettingspage.cpp4
-rw-r--r--src/plugins/nim/settings/nimsettings.cpp69
-rw-r--r--src/plugins/nim/settings/nimsettings.h15
-rw-r--r--src/plugins/nim/suggest/server.cpp4
-rw-r--r--src/plugins/nim/suggest/server.h4
-rw-r--r--src/plugins/perforce/changenumberdialog.cpp2
-rw-r--r--src/plugins/perforce/pendingchangesdialog.cpp2
-rw-r--r--src/plugins/perforce/perforcechecker.cpp8
-rw-r--r--src/plugins/perforce/perforcechecker.h4
-rw-r--r--src/plugins/perforce/perforceplugin.cpp40
-rw-r--r--src/plugins/perforce/perforcesettings.cpp57
-rw-r--r--src/plugins/perforce/perforcesettings.h21
-rw-r--r--src/plugins/perforce/perforcesubmiteditorwidget.cpp1
-rw-r--r--src/plugins/perfprofiler/perfconfigeventsmodel.cpp5
-rw-r--r--src/plugins/perfprofiler/perfconfigwidget.cpp6
-rw-r--r--src/plugins/perfprofiler/perfconfigwidget.h4
-rw-r--r--src/plugins/perfprofiler/perfdatareader.cpp2
-rw-r--r--src/plugins/perfprofiler/perfloaddialog.cpp4
-rw-r--r--src/plugins/perfprofiler/perfprofiler.qbs4
-rw-r--r--src/plugins/perfprofiler/perfprofilerflamegraphmodel.h5
-rw-r--r--src/plugins/perfprofiler/perfprofilerruncontrol.cpp32
-rw-r--r--src/plugins/perfprofiler/perfprofilertool.cpp20
-rw-r--r--src/plugins/perfprofiler/perfsettings.cpp22
-rw-r--r--src/plugins/perfprofiler/perfsettings.h18
-rw-r--r--src/plugins/perfprofiler/perftracepointdialog.cpp14
-rw-r--r--src/plugins/perfprofiler/perftracepointdialog.h4
-rw-r--r--src/plugins/plugins.qbs8
-rw-r--r--src/plugins/projectexplorer/CMakeLists.txt14
-rw-r--r--src/plugins/projectexplorer/ProjectExplorer.json.in8
-rw-r--r--src/plugins/projectexplorer/abstractprocessstep.cpp21
-rw-r--r--src/plugins/projectexplorer/abstractprocessstep.h9
-rw-r--r--src/plugins/projectexplorer/allprojectsfilter.cpp56
-rw-r--r--src/plugins/projectexplorer/allprojectsfilter.h19
-rw-r--r--src/plugins/projectexplorer/allprojectsfind.cpp6
-rw-r--r--src/plugins/projectexplorer/appoutputpane.cpp3
-rw-r--r--src/plugins/projectexplorer/buildaspects.cpp7
-rw-r--r--src/plugins/projectexplorer/buildaspects.h7
-rw-r--r--src/plugins/projectexplorer/buildconfiguration.cpp43
-rw-r--r--src/plugins/projectexplorer/buildmanager.cpp26
-rw-r--r--src/plugins/projectexplorer/buildmanager.h9
-rw-r--r--src/plugins/projectexplorer/buildpropertiessettings.cpp73
-rw-r--r--src/plugins/projectexplorer/buildpropertiessettings.h26
-rw-r--r--src/plugins/projectexplorer/buildsettingspropertiespage.cpp10
-rw-r--r--src/plugins/projectexplorer/buildstep.cpp58
-rw-r--r--src/plugins/projectexplorer/buildstep.h41
-rw-r--r--src/plugins/projectexplorer/buildsteplist.cpp10
-rw-r--r--src/plugins/projectexplorer/buildstepspage.cpp8
-rw-r--r--src/plugins/projectexplorer/buildsystem.cpp10
-rw-r--r--src/plugins/projectexplorer/buildsystem.h1
-rw-r--r--src/plugins/projectexplorer/codestylesettingspropertiespage.cpp7
-rw-r--r--src/plugins/projectexplorer/compileoutputwindow.cpp118
-rw-r--r--src/plugins/projectexplorer/compileoutputwindow.h24
-rw-r--r--src/plugins/projectexplorer/copystep.cpp114
-rw-r--r--src/plugins/projectexplorer/copystep.h22
-rw-r--r--src/plugins/projectexplorer/currentprojectfilter.cpp51
-rw-r--r--src/plugins/projectexplorer/currentprojectfilter.h23
-rw-r--r--src/plugins/projectexplorer/currentprojectfind.cpp13
-rw-r--r--src/plugins/projectexplorer/customexecutablerunconfiguration.cpp4
-rw-r--r--src/plugins/projectexplorer/customparserconfigdialog.cpp2
-rw-r--r--src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp4
-rw-r--r--src/plugins/projectexplorer/dependenciespanel.cpp24
-rw-r--r--src/plugins/projectexplorer/desktoprunconfiguration.cpp9
-rw-r--r--src/plugins/projectexplorer/devicesupport/desktopdevice.cpp118
-rw-r--r--src/plugins/projectexplorer/devicesupport/desktopdevice.h10
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp88
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.h14
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.cpp2
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicemanager.cpp28
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp63
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicesettingswidget.h1
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp2
-rw-r--r--src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp14
-rw-r--r--src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h15
-rw-r--r--src/plugins/projectexplorer/devicesupport/filetransfer.cpp41
-rw-r--r--src/plugins/projectexplorer/devicesupport/filetransfer.h12
-rw-r--r--src/plugins/projectexplorer/devicesupport/filetransferinterface.h8
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevice.cpp45
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevice.h22
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevicefactory.cpp27
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevicefactory.h3
-rw-r--r--src/plugins/projectexplorer/devicesupport/localprocesslist.cpp59
-rw-r--r--src/plugins/projectexplorer/devicesupport/processlist.cpp61
-rw-r--r--src/plugins/projectexplorer/devicesupport/processlist.h (renamed from src/plugins/projectexplorer/devicesupport/localprocesslist.h)10
-rw-r--r--src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp83
-rw-r--r--src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.h36
-rw-r--r--src/plugins/projectexplorer/devicesupport/sshparameters.cpp33
-rw-r--r--src/plugins/projectexplorer/devicesupport/sshparameters.h34
-rw-r--r--src/plugins/projectexplorer/editorconfiguration.cpp8
-rw-r--r--src/plugins/projectexplorer/editorsettingspropertiespage.cpp5
-rw-r--r--src/plugins/projectexplorer/environmentaspect.cpp27
-rw-r--r--src/plugins/projectexplorer/environmentaspect.h9
-rw-r--r--src/plugins/projectexplorer/environmentaspectwidget.cpp9
-rw-r--r--src/plugins/projectexplorer/extraabi.cpp17
-rw-r--r--src/plugins/projectexplorer/extracompiler.cpp90
-rw-r--r--src/plugins/projectexplorer/extracompiler.h20
-rw-r--r--src/plugins/projectexplorer/fileinsessionfinder.cpp12
-rw-r--r--src/plugins/projectexplorer/filesinallprojectsfind.cpp4
-rw-r--r--src/plugins/projectexplorer/gcctoolchain.cpp23
-rw-r--r--src/plugins/projectexplorer/gcctoolchain.h4
-rw-r--r--src/plugins/projectexplorer/ipotentialkit.h10
-rw-r--r--src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp37
-rw-r--r--src/plugins/projectexplorer/jsonwizard/jsonsummarypage.cpp4
-rw-r--r--src/plugins/projectexplorer/kitchooser.cpp4
-rw-r--r--src/plugins/projectexplorer/kitinformation.cpp98
-rw-r--r--src/plugins/projectexplorer/kitmanager.cpp9
-rw-r--r--src/plugins/projectexplorer/kitmanager.h2
-rw-r--r--src/plugins/projectexplorer/kitmanagerconfigwidget.cpp49
-rw-r--r--src/plugins/projectexplorer/kitmanagerconfigwidget.h13
-rw-r--r--src/plugins/projectexplorer/kitmodel.cpp186
-rw-r--r--src/plugins/projectexplorer/kitoptionspage.cpp55
-rw-r--r--src/plugins/projectexplorer/kitoptionspage.h14
-rw-r--r--src/plugins/projectexplorer/localenvironmentaspect.cpp46
-rw-r--r--src/plugins/projectexplorer/localenvironmentaspect.h18
-rw-r--r--src/plugins/projectexplorer/makestep.cpp35
-rw-r--r--src/plugins/projectexplorer/makestep.h6
-rw-r--r--src/plugins/projectexplorer/miniprojecttargetselector.cpp90
-rw-r--r--src/plugins/projectexplorer/msvcparser.cpp2
-rw-r--r--src/plugins/projectexplorer/msvctoolchain.cpp43
-rw-r--r--src/plugins/projectexplorer/msvctoolchain.h11
-rw-r--r--src/plugins/projectexplorer/processparameters.cpp2
-rw-r--r--src/plugins/projectexplorer/processstep.cpp6
-rw-r--r--src/plugins/projectexplorer/project.cpp160
-rw-r--r--src/plugins/projectexplorer/project.h7
-rw-r--r--src/plugins/projectexplorer/projectconfiguration.cpp13
-rw-r--r--src/plugins/projectexplorer/projectconfiguration.h16
-rw-r--r--src/plugins/projectexplorer/projectexplorer.cpp706
-rw-r--r--src/plugins/projectexplorer/projectexplorer.h4
-rw-r--r--src/plugins/projectexplorer/projectexplorer.qbs18
-rw-r--r--src/plugins/projectexplorer/projectexplorer.qrc8
-rw-r--r--src/plugins/projectexplorer/projectexplorerconstants.h6
-rw-r--r--src/plugins/projectexplorer/projectexplorersettings.cpp (renamed from src/plugins/projectexplorer/projectexplorersettingspage.cpp)57
-rw-r--r--src/plugins/projectexplorer/projectexplorersettings.h10
-rw-r--r--src/plugins/projectexplorer/projectexplorersettingspage.h29
-rw-r--r--src/plugins/projectexplorer/projectfilewizardextension.cpp5
-rw-r--r--src/plugins/projectexplorer/projectmanager.cpp768
-rw-r--r--src/plugins/projectexplorer/projectmanager.h69
-rw-r--r--src/plugins/projectexplorer/projectmodels.cpp24
-rw-r--r--src/plugins/projectexplorer/projectnodes.cpp58
-rw-r--r--src/plugins/projectexplorer/projectnodes.h10
-rw-r--r--src/plugins/projectexplorer/projectnodeshelper.h26
-rw-r--r--src/plugins/projectexplorer/projecttree.cpp22
-rw-r--r--src/plugins/projectexplorer/projecttreewidget.cpp7
-rw-r--r--src/plugins/projectexplorer/projectwelcomepage.cpp11
-rw-r--r--src/plugins/projectexplorer/projectwelcomepage.h6
-rw-r--r--src/plugins/projectexplorer/projectwindow.cpp28
-rw-r--r--src/plugins/projectexplorer/projectwizardpage.cpp14
-rw-r--r--src/plugins/projectexplorer/rawprojectpart.cpp6
-rw-r--r--src/plugins/projectexplorer/rawprojectpart.h2
-rw-r--r--src/plugins/projectexplorer/runconfiguration.cpp34
-rw-r--r--src/plugins/projectexplorer/runconfiguration.h2
-rw-r--r--src/plugins/projectexplorer/runconfigurationaspects.cpp42
-rw-r--r--src/plugins/projectexplorer/runconfigurationaspects.h11
-rw-r--r--src/plugins/projectexplorer/runcontrol.cpp81
-rw-r--r--src/plugins/projectexplorer/runcontrol.h3
-rw-r--r--src/plugins/projectexplorer/runsettingspropertiespage.cpp12
-rw-r--r--src/plugins/projectexplorer/selectablefilesmodel.cpp14
-rw-r--r--src/plugins/projectexplorer/selectablefilesmodel.h7
-rw-r--r--src/plugins/projectexplorer/session.cpp1262
-rw-r--r--src/plugins/projectexplorer/session.h136
-rw-r--r--src/plugins/projectexplorer/target.cpp79
-rw-r--r--src/plugins/projectexplorer/target.h7
-rw-r--r--src/plugins/projectexplorer/targetsettingspanel.cpp14
-rw-r--r--src/plugins/projectexplorer/targetsetuppage.cpp9
-rw-r--r--src/plugins/projectexplorer/targetsetupwidget.cpp6
-rw-r--r--src/plugins/projectexplorer/task.cpp48
-rw-r--r--src/plugins/projectexplorer/task.h6
-rw-r--r--src/plugins/projectexplorer/taskfile.cpp10
-rw-r--r--src/plugins/projectexplorer/taskhub.cpp10
-rw-r--r--src/plugins/projectexplorer/taskmodel.cpp85
-rw-r--r--src/plugins/projectexplorer/taskmodel.h2
-rw-r--r--src/plugins/projectexplorer/taskwindow.cpp660
-rw-r--r--src/plugins/projectexplorer/testdata/multi-target-project/CMakeLists.txt3
-rw-r--r--src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-app.pro4
-rw-r--r--src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.cpp6
-rw-r--r--src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.pro4
-rw-r--r--src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-main.cpp6
-rw-r--r--src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-shared.h3
-rw-r--r--src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.pro4
-rw-r--r--src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.qbs10
-rw-r--r--src/plugins/projectexplorer/toolchain.cpp23
-rw-r--r--src/plugins/projectexplorer/toolchain.h10
-rw-r--r--src/plugins/projectexplorer/toolchainconfigwidget.cpp2
-rw-r--r--src/plugins/projectexplorer/toolchainoptionspage.cpp45
-rw-r--r--src/plugins/projectexplorer/toolchainsettingsaccessor.cpp16
-rw-r--r--src/plugins/projectexplorer/treescanner.cpp16
-rw-r--r--src/plugins/projectexplorer/treescanner.h4
-rw-r--r--src/plugins/projectexplorer/userfileaccessor.cpp18
-rw-r--r--src/plugins/python/CMakeLists.txt1
-rw-r--r--src/plugins/python/Python.json.in8
-rw-r--r--src/plugins/python/pipsupport.cpp57
-rw-r--r--src/plugins/python/pipsupport.h11
-rw-r--r--src/plugins/python/pyside.cpp21
-rw-r--r--src/plugins/python/pyside.h1
-rw-r--r--src/plugins/python/pysidebuildconfiguration.cpp80
-rw-r--r--src/plugins/python/pysidebuildconfiguration.h23
-rw-r--r--src/plugins/python/pysideuicextracompiler.cpp4
-rw-r--r--src/plugins/python/pysideuicextracompiler.h2
-rw-r--r--src/plugins/python/python.qbs2
-rw-r--r--src/plugins/python/pythoneditor.cpp49
-rw-r--r--src/plugins/python/pythonlanguageclient.cpp14
-rw-r--r--src/plugins/python/pythonplugin.cpp15
-rw-r--r--src/plugins/python/pythonplugin.h3
-rw-r--r--src/plugins/python/pythonproject.h3
-rw-r--r--src/plugins/python/pythonrunconfiguration.cpp15
-rw-r--r--src/plugins/python/pythonsettings.cpp153
-rw-r--r--src/plugins/python/pythonsettings.h13
-rw-r--r--src/plugins/python/pythonutils.cpp76
-rw-r--r--src/plugins/python/pythonutils.h4
-rw-r--r--src/plugins/python/pythonwizardpage.cpp219
-rw-r--r--src/plugins/python/pythonwizardpage.h43
-rw-r--r--src/plugins/qbsprojectmanager/customqbspropertiesdialog.cpp2
-rw-r--r--src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp4
-rw-r--r--src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp2
-rw-r--r--src/plugins/qbsprojectmanager/qbsbuildstep.cpp58
-rw-r--r--src/plugins/qbsprojectmanager/qbsinstallstep.cpp27
-rw-r--r--src/plugins/qbsprojectmanager/qbsinstallstep.h2
-rw-r--r--src/plugins/qbsprojectmanager/qbskitinformation.cpp6
-rw-r--r--src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp4
-rw-r--r--src/plugins/qbsprojectmanager/qbsprofilemanager.cpp4
-rw-r--r--src/plugins/qbsprojectmanager/qbsprofilessettingspage.cpp30
-rw-r--r--src/plugins/qbsprojectmanager/qbsprofilessettingspage.h14
-rw-r--r--src/plugins/qbsprojectmanager/qbsproject.cpp15
-rw-r--r--src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp18
-rw-r--r--src/plugins/qbsprojectmanager/qbsprojectparser.cpp8
-rw-r--r--src/plugins/qbsprojectmanager/qbssession.cpp13
-rw-r--r--src/plugins/qbsprojectmanager/qbssettings.cpp36
-rw-r--r--src/plugins/qbsprojectmanager/qbssettings.h23
-rw-r--r--src/plugins/qmakeprojectmanager/addlibrarywizard.cpp1
-rw-r--r--src/plugins/qmakeprojectmanager/customwidgetwizard/classdefinition.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetpluginwizardpage.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwidgetswizardpage.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/customwidgetwizard/plugingenerator.cpp18
-rw-r--r--src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp13
-rw-r--r--src/plugins/qmakeprojectmanager/makefileparse.cpp13
-rw-r--r--src/plugins/qmakeprojectmanager/profileeditor.cpp29
-rw-r--r--src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp13
-rw-r--r--src/plugins/qmakeprojectmanager/qmakekitinformation.cpp4
-rw-r--r--src/plugins/qmakeprojectmanager/qmakemakestep.cpp4
-rw-r--r--src/plugins/qmakeprojectmanager/qmakenodes.cpp4
-rw-r--r--src/plugins/qmakeprojectmanager/qmakenodes.h1
-rw-r--r--src/plugins/qmakeprojectmanager/qmakenodetreebuilder.cpp26
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeparser.cpp7
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp13
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeparsernodes.h2
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeproject.cpp115
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeproject.h1
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp17
-rw-r--r--src/plugins/qmakeprojectmanager/qmakesettings.cpp120
-rw-r--r--src/plugins/qmakeprojectmanager/qmakesettings.h36
-rw-r--r--src/plugins/qmakeprojectmanager/qmakestep.cpp46
-rw-r--r--src/plugins/qmakeprojectmanager/wizards/qtprojectparameters.cpp6
-rw-r--r--src/plugins/qmldesigner/CMakeLists.txt19
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp4
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp24
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp12
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp19
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp2
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h2
-rw-r--r--src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp4
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp6
-rw-r--r--src/plugins/qmldesigner/components/componentcore/zoomaction.cpp4
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp4
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp8
-rw-r--r--src/plugins/qmldesigner/components/eventlist/eventlist.cpp4
-rw-r--r--src/plugins/qmldesigner/components/formeditor/backgroundaction.cpp4
-rw-r--r--src/plugins/qmldesigner/components/formeditor/seekerslider.cpp5
-rw-r--r--src/plugins/qmldesigner/components/formeditor/toolbox.cpp18
-rw-r--r--src/plugins/qmldesigner/components/integration/designdocument.cpp8
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp6
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp4
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp4
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp2
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatorview.cpp6
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/aligndistribute.cpp9
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp4
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp5
-rw-r--r--src/plugins/qmldesigner/components/toolbar/toolbar.cpp2
-rw-r--r--src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp23
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.cpp5
-rw-r--r--src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h3
-rw-r--r--src/plugins/qmldesigner/designercore/include/nodeinstanceview.h4
-rw-r--r--src/plugins/qmldesigner/designercore/include/qmlobjectnode.h1
-rw-r--r--src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp12
-rw-r--r--src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.cpp65
-rw-r--r--src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.h38
-rw-r--r--src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.ui78
-rw-r--r--src/plugins/qmldesigner/designercore/instances/puppetdialog.cpp42
-rw-r--r--src/plugins/qmldesigner/designercore/instances/puppetdialog.h35
-rw-r--r--src/plugins/qmldesigner/designercore/instances/puppetdialog.ui96
-rw-r--r--src/plugins/qmldesigner/documentmanager.cpp20
-rw-r--r--src/plugins/qmldesigner/generateresource.cpp26
-rw-r--r--src/plugins/qmldesigner/openuiqmlfiledialog.cpp2
-rw-r--r--src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp15
-rw-r--r--src/plugins/qmldesigner/qmldesignerplugin.cpp6
-rw-r--r--src/plugins/qmldesigner/qmldesignerprojectmanager.cpp16
-rw-r--r--src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp8
-rw-r--r--src/plugins/qmldesigner/settingspage.cpp2
-rw-r--r--src/plugins/qmldesigner/utils/filedownloader.h1
-rw-r--r--src/plugins/qmldesigner/utils/multifiledownloader.cpp5
-rw-r--r--src/plugins/qmldesigner/utils/multifiledownloader.h3
-rw-r--r--src/plugins/qmldesignerbase/qmldesignerbase.qbs40
-rw-r--r--src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp2
-rw-r--r--src/plugins/qmldesignerbase/qmldesignerbaseplugin.h1
-rw-r--r--src/plugins/qmldesignerbase/studio/studiosettingspage.cpp12
-rw-r--r--src/plugins/qmldesignerbase/studio/studiosettingspage.h2
-rw-r--r--src/plugins/qmldesignerbase/studio/studiostyle.h2
-rw-r--r--src/plugins/qmldesignerbase/utils/designerpaths.cpp4
-rw-r--r--src/plugins/qmljseditor/qmljscomponentnamedialog.cpp2
-rw-r--r--src/plugins/qmljseditor/qmljseditingsettingspage.cpp243
-rw-r--r--src/plugins/qmljseditor/qmljseditingsettingspage.h16
-rw-r--r--src/plugins/qmljseditor/qmljseditorplugin.cpp5
-rw-r--r--src/plugins/qmljseditor/qmljseditorplugin.h1
-rw-r--r--src/plugins/qmljseditor/qmljsfindreferences.cpp78
-rw-r--r--src/plugins/qmljseditor/qmljsfindreferences.h9
-rw-r--r--src/plugins/qmljseditor/qmljsquickfix.h2
-rw-r--r--src/plugins/qmljseditor/qmljssemantichighlighter.cpp27
-rw-r--r--src/plugins/qmljseditor/qmljssemantichighlighter.h2
-rw-r--r--src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp2
-rw-r--r--src/plugins/qmljseditor/qmllssettings.cpp2
-rw-r--r--src/plugins/qmljseditor/qmltaskmanager.cpp15
-rw-r--r--src/plugins/qmljseditor/qmltaskmanager.h2
-rw-r--r--src/plugins/qmljstools/qmljsbundleprovider.cpp30
-rw-r--r--src/plugins/qmljstools/qmljsbundleprovider.h9
-rw-r--r--src/plugins/qmljstools/qmljscodestylesettingspage.cpp83
-rw-r--r--src/plugins/qmljstools/qmljscodestylesettingspage.h16
-rw-r--r--src/plugins/qmljstools/qmljscodestylesettingswidget.cpp11
-rw-r--r--src/plugins/qmljstools/qmljsfunctionfilter.cpp83
-rw-r--r--src/plugins/qmljstools/qmljsfunctionfilter.h20
-rw-r--r--src/plugins/qmljstools/qmljslocatordata.cpp6
-rw-r--r--src/plugins/qmljstools/qmljsmodelmanager.cpp9
-rw-r--r--src/plugins/qmljstools/qmljsrefactoringchanges.h2
-rw-r--r--src/plugins/qmljstools/qmljstools.qbs4
-rw-r--r--src/plugins/qmljstools/qmljstoolsplugin.cpp2
-rw-r--r--src/plugins/qmljstools/qmljstoolssettings.cpp39
-rw-r--r--src/plugins/qmlpreview/qmlpreview.qbs4
-rw-r--r--src/plugins/qmlpreview/qmlpreviewplugin.cpp10
-rw-r--r--src/plugins/qmlpreview/qmlpreviewruncontrol.cpp19
-rw-r--r--src/plugins/qmlprofiler/qmlprofiler.qbs4
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp2
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp2
-rw-r--r--src/plugins/qmlprofiler/qmlprofilersettings.cpp58
-rw-r--r--src/plugins/qmlprofiler/qmlprofilersettings.h17
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertool.cpp9
-rw-r--r--src/plugins/qmlprofiler/tests/qmlprofilerclientmanager_test.cpp40
-rw-r--r--src/plugins/qmlprofiler/tests/qmlprofilerdetailsrewriter_test.cpp26
-rw-r--r--src/plugins/qmlprojectmanager/CMakeLists.txt4
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp6
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp13
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp18
-rw-r--r--src/plugins/qmlprojectmanager/projectfilecontenttools.cpp2
-rw-r--r--src/plugins/qmlprojectmanager/projectfilecontenttools.h2
-rw-r--r--src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp4
-rw-r--r--src/plugins/qmlprojectmanager/qmlmainfileaspect.h2
-rw-r--r--src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp4
-rw-r--r--src/plugins/qmlprojectmanager/qmlproject.cpp35
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectmanager.qbs4
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectplugin.cpp12
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp6
-rw-r--r--src/plugins/qnx/CMakeLists.txt5
-rw-r--r--src/plugins/qnx/qnx.qbs10
-rw-r--r--src/plugins/qnx/qnxanalyzesupport.cpp3
-rw-r--r--src/plugins/qnx/qnxconfiguration.cpp468
-rw-r--r--src/plugins/qnx/qnxconfiguration.h114
-rw-r--r--src/plugins/qnx/qnxconfigurationmanager.cpp124
-rw-r--r--src/plugins/qnx/qnxconfigurationmanager.h41
-rw-r--r--src/plugins/qnx/qnxconstants.h1
-rw-r--r--src/plugins/qnx/qnxdebugsupport.cpp6
-rw-r--r--src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp38
-rw-r--r--src/plugins/qnx/qnxdevice.cpp212
-rw-r--r--src/plugins/qnx/qnxdevice.h34
-rw-r--r--src/plugins/qnx/qnxdeviceprocesslist.cpp59
-rw-r--r--src/plugins/qnx/qnxdeviceprocesslist.h21
-rw-r--r--src/plugins/qnx/qnxdeviceprocesssignaloperation.cpp36
-rw-r--r--src/plugins/qnx/qnxdeviceprocesssignaloperation.h22
-rw-r--r--src/plugins/qnx/qnxdevicetester.cpp83
-rw-r--r--src/plugins/qnx/qnxdevicetester.h17
-rw-r--r--src/plugins/qnx/qnxdevicewizard.cpp48
-rw-r--r--src/plugins/qnx/qnxdevicewizard.h38
-rw-r--r--src/plugins/qnx/qnxplugin.cpp35
-rw-r--r--src/plugins/qnx/qnxsettingspage.cpp682
-rw-r--r--src/plugins/qnx/qnxsettingspage.h6
-rw-r--r--src/plugins/qnx/qnxtoolchain.cpp10
-rw-r--r--src/plugins/qnx/qnxutils.cpp13
-rw-r--r--src/plugins/qnx/qnxutils.h10
-rw-r--r--src/plugins/qnx/slog2inforunner.cpp39
-rw-r--r--src/plugins/qnx/slog2inforunner.h4
-rw-r--r--src/plugins/qtsupport/baseqtversion.cpp15
-rw-r--r--src/plugins/qtsupport/codegensettingspage.cpp4
-rw-r--r--src/plugins/qtsupport/exampleslistmodel.cpp116
-rw-r--r--src/plugins/qtsupport/exampleslistmodel.h2
-rw-r--r--src/plugins/qtsupport/examplesparser.cpp71
-rw-r--r--src/plugins/qtsupport/examplesparser.h9
-rw-r--r--src/plugins/qtsupport/externaleditors.cpp8
-rw-r--r--src/plugins/qtsupport/gettingstartedwelcomepage.cpp4
-rw-r--r--src/plugins/qtsupport/qscxmlcgenerator.cpp2
-rw-r--r--src/plugins/qtsupport/qscxmlcgenerator.h2
-rw-r--r--src/plugins/qtsupport/qtbuildaspects.cpp12
-rw-r--r--src/plugins/qtsupport/qtbuildaspects.h4
-rw-r--r--src/plugins/qtsupport/qtkitinformation.cpp6
-rw-r--r--src/plugins/qtsupport/qtoptionspage.cpp9
-rw-r--r--src/plugins/qtsupport/qtsupport_global.h12
-rw-r--r--src/plugins/qtsupport/qtsupportplugin.cpp12
-rw-r--r--src/plugins/qtsupport/qtversionmanager.cpp5
-rw-r--r--src/plugins/qtsupport/uicgenerator.cpp11
-rw-r--r--src/plugins/qtsupport/uicgenerator.h2
-rw-r--r--src/plugins/remotelinux/CMakeLists.txt2
-rw-r--r--src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp186
-rw-r--r--src/plugins/remotelinux/abstractremotelinuxdeploystep.h93
-rw-r--r--src/plugins/remotelinux/customcommanddeploystep.cpp90
-rw-r--r--src/plugins/remotelinux/filesystemaccess_test.cpp360
-rw-r--r--src/plugins/remotelinux/filesystemaccess_test.h16
-rw-r--r--src/plugins/remotelinux/genericdirectuploadservice.cpp314
-rw-r--r--src/plugins/remotelinux/genericdirectuploadservice.h38
-rw-r--r--src/plugins/remotelinux/genericdirectuploadstep.cpp326
-rw-r--r--src/plugins/remotelinux/genericdirectuploadstep.h19
-rw-r--r--src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp65
-rw-r--r--src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h13
-rw-r--r--src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp8
-rw-r--r--src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp34
-rw-r--r--src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.h3
-rw-r--r--src/plugins/remotelinux/killappstep.cpp50
-rw-r--r--src/plugins/remotelinux/linuxdevice.cpp660
-rw-r--r--src/plugins/remotelinux/linuxdevice.h10
-rw-r--r--src/plugins/remotelinux/linuxdevicetester.cpp98
-rw-r--r--src/plugins/remotelinux/linuxdevicetester.h4
-rw-r--r--src/plugins/remotelinux/linuxprocessinterface.h33
-rw-r--r--src/plugins/remotelinux/makeinstallstep.cpp48
-rw-r--r--src/plugins/remotelinux/makeinstallstep.h10
-rw-r--r--src/plugins/remotelinux/publickeydeploymentdialog.cpp6
-rw-r--r--src/plugins/remotelinux/remotelinux.qbs7
-rw-r--r--src/plugins/remotelinux/remotelinux_constants.h5
-rw-r--r--src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp3
-rw-r--r--src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp43
-rw-r--r--src/plugins/remotelinux/remotelinuxenvironmentaspect.cpp2
-rw-r--r--src/plugins/remotelinux/remotelinuxplugin.cpp18
-rw-r--r--src/plugins/remotelinux/remotelinuxrunconfiguration.cpp18
-rw-r--r--src/plugins/remotelinux/remotelinuxsignaloperation.cpp6
-rw-r--r--src/plugins/remotelinux/remotelinuxsignaloperation.h2
-rw-r--r--src/plugins/remotelinux/rsyncdeploystep.cpp131
-rw-r--r--src/plugins/remotelinux/rsyncdeploystep.h11
-rw-r--r--src/plugins/remotelinux/sshkeycreationdialog.cpp4
-rw-r--r--src/plugins/remotelinux/sshprocessinterface.h46
-rw-r--r--src/plugins/remotelinux/tarpackagecreationstep.cpp27
-rw-r--r--src/plugins/remotelinux/tarpackagedeploystep.cpp118
-rw-r--r--src/plugins/resourceeditor/qrceditor/qrceditor.cpp2
-rw-r--r--src/plugins/resourceeditor/resourcenode.cpp4
-rw-r--r--src/plugins/scxmleditor/CMakeLists.txt1
-rw-r--r--src/plugins/scxmleditor/common/colorpicker.cpp6
-rw-r--r--src/plugins/scxmleditor/common/colorsettings.cpp5
-rw-r--r--src/plugins/scxmleditor/common/colorthemedialog.cpp2
-rw-r--r--src/plugins/scxmleditor/common/navigatorslider.cpp6
-rw-r--r--src/plugins/scxmleditor/common/search.cpp6
-rw-r--r--src/plugins/scxmleditor/common/shapestoolbox.cpp6
-rw-r--r--src/plugins/scxmleditor/common/stateproperties.cpp1
-rw-r--r--src/plugins/scxmleditor/common/stateview.cpp12
-rw-r--r--src/plugins/scxmleditor/common/statistics.cpp7
-rw-r--r--src/plugins/scxmleditor/common/statisticsdialog.cpp2
-rw-r--r--src/plugins/scxmleditor/plugin_interface/baseitem.cpp3
-rw-r--r--src/plugins/scxmleditor/plugin_interface/baseitem.h1
-rw-r--r--src/plugins/scxmleditor/plugin_interface/eventitem.cpp108
-rw-r--r--src/plugins/scxmleditor/plugin_interface/eventitem.h41
-rw-r--r--src/plugins/scxmleditor/plugin_interface/graphicsscene.cpp15
-rw-r--r--src/plugins/scxmleditor/plugin_interface/stateitem.cpp66
-rw-r--r--src/plugins/scxmleditor/plugin_interface/stateitem.h8
-rw-r--r--src/plugins/scxmleditor/scxmleditor.qbs1
-rw-r--r--src/plugins/scxmleditor/scxmleditordocument.cpp2
-rw-r--r--src/plugins/silversearcher/CMakeLists.txt4
-rw-r--r--src/plugins/silversearcher/findinfilessilversearcher.cpp165
-rw-r--r--src/plugins/silversearcher/findinfilessilversearcher.h10
-rw-r--r--src/plugins/silversearcher/outputparser_test.cpp59
-rw-r--r--src/plugins/silversearcher/silversearcher.qbs10
-rw-r--r--src/plugins/silversearcher/silversearcheroutputparser.cpp119
-rw-r--r--src/plugins/silversearcher/silversearcheroutputparser.h38
-rw-r--r--src/plugins/silversearcher/silversearcherparser.cpp157
-rw-r--r--src/plugins/silversearcher/silversearcherparser.h22
-rw-r--r--src/plugins/silversearcher/silversearcherparser_test.cpp69
-rw-r--r--src/plugins/silversearcher/silversearcherparser_test.h (renamed from src/plugins/silversearcher/outputparser_test.h)0
-rw-r--r--src/plugins/silversearcher/silversearcherplugin.cpp4
-rw-r--r--src/plugins/squish/deletesymbolicnamedialog.cpp2
-rw-r--r--src/plugins/squish/images/picker.pngbin0 -> 194 bytes
-rw-r--r--src/plugins/squish/images/picker@2x.pngbin0 -> 362 bytes
-rw-r--r--src/plugins/squish/objectsmapdocument.cpp10
-rw-r--r--src/plugins/squish/objectsmapeditorwidget.cpp2
-rw-r--r--src/plugins/squish/opensquishsuitesdialog.cpp2
-rw-r--r--src/plugins/squish/squish.qrc2
-rw-r--r--src/plugins/squish/squishfilehandler.cpp21
-rw-r--r--src/plugins/squish/squishnavigationwidget.cpp20
-rw-r--r--src/plugins/squish/squishoutputpane.cpp6
-rw-r--r--src/plugins/squish/squishperspective.cpp243
-rw-r--r--src/plugins/squish/squishperspective.h38
-rw-r--r--src/plugins/squish/squishplugin.cpp7
-rw-r--r--src/plugins/squish/squishprocessbase.cpp4
-rw-r--r--src/plugins/squish/squishprocessbase.h4
-rw-r--r--src/plugins/squish/squishrunnerprocess.cpp93
-rw-r--r--src/plugins/squish/squishrunnerprocess.h17
-rw-r--r--src/plugins/squish/squishserverprocess.cpp4
-rw-r--r--src/plugins/squish/squishsettings.cpp94
-rw-r--r--src/plugins/squish/squishsettings.h46
-rw-r--r--src/plugins/squish/squishtesttreeview.cpp1
-rw-r--r--src/plugins/squish/squishtools.cpp100
-rw-r--r--src/plugins/squish/squishtools.h11
-rw-r--r--src/plugins/squish/squishwizardpages.cpp5
-rw-r--r--src/plugins/studiowelcome/qdsnewdialog.cpp4
-rw-r--r--src/plugins/studiowelcome/studiowelcomeplugin.cpp13
-rw-r--r--src/plugins/studiowelcome/stylemodel.cpp4
-rw-r--r--src/plugins/studiowelcome/wizardhandler.cpp2
-rw-r--r--src/plugins/subversion/subversionclient.cpp48
-rw-r--r--src/plugins/subversion/subversionclient.h2
-rw-r--r--src/plugins/subversion/subversionplugin.cpp40
-rw-r--r--src/plugins/subversion/subversionsettings.cpp81
-rw-r--r--src/plugins/subversion/subversionsettings.h23
-rw-r--r--src/plugins/terminal/CMakeLists.txt23
-rw-r--r--src/plugins/terminal/Terminal.json.in18
-rw-r--r--src/plugins/terminal/celliterator.cpp94
-rw-r--r--src/plugins/terminal/celliterator.h97
-rw-r--r--src/plugins/terminal/glyphcache.cpp48
-rw-r--r--src/plugins/terminal/glyphcache.h34
-rw-r--r--src/plugins/terminal/images/settingscategory_terminal.pngbin0 -> 164 bytes
-rw-r--r--src/plugins/terminal/images/settingscategory_terminal@2x.pngbin0 -> 193 bytes
-rw-r--r--src/plugins/terminal/images/terminal.pngbin0 -> 154 bytes
-rw-r--r--src/plugins/terminal/images/terminal@2x.pngbin0 -> 180 bytes
-rw-r--r--src/plugins/terminal/keys.cpp83
-rw-r--r--src/plugins/terminal/keys.h15
-rw-r--r--src/plugins/terminal/scrollback.cpp61
-rw-r--r--src/plugins/terminal/scrollback.h57
-rw-r--r--src/plugins/terminal/shellintegration.cpp164
-rw-r--r--src/plugins/terminal/shellintegration.h34
-rwxr-xr-xsrc/plugins/terminal/shellintegrations/shellintegration-bash.sh252
-rw-r--r--src/plugins/terminal/shellintegrations/shellintegration-clink.lua52
-rw-r--r--src/plugins/terminal/shellintegrations/shellintegration-env.zsh15
-rw-r--r--src/plugins/terminal/shellintegrations/shellintegration-login.zsh7
-rw-r--r--src/plugins/terminal/shellintegrations/shellintegration-profile.zsh15
-rw-r--r--src/plugins/terminal/shellintegrations/shellintegration-rc.zsh160
-rw-r--r--src/plugins/terminal/shellintegrations/shellintegration.fish122
-rw-r--r--src/plugins/terminal/shellintegrations/shellintegration.ps1158
-rw-r--r--src/plugins/terminal/shellmodel.cpp110
-rw-r--r--src/plugins/terminal/shellmodel.h37
-rw-r--r--src/plugins/terminal/terminal.qbs43
-rw-r--r--src/plugins/terminal/terminal.qrc16
-rw-r--r--src/plugins/terminal/terminalconstants.h19
-rw-r--r--src/plugins/terminal/terminalicons.h18
-rw-r--r--src/plugins/terminal/terminalpane.cpp394
-rw-r--r--src/plugins/terminal/terminalpane.h82
-rw-r--r--src/plugins/terminal/terminalplugin.cpp87
-rw-r--r--src/plugins/terminal/terminalprocessimpl.cpp82
-rw-r--r--src/plugins/terminal/terminalprocessimpl.h18
-rw-r--r--src/plugins/terminal/terminalsearch.cpp274
-rw-r--r--src/plugins/terminal/terminalsearch.h82
-rw-r--r--src/plugins/terminal/terminalsettings.cpp591
-rw-r--r--src/plugins/terminal/terminalsettings.h37
-rw-r--r--src/plugins/terminal/terminalsurface.cpp531
-rw-r--r--src/plugins/terminal/terminalsurface.h111
-rw-r--r--src/plugins/terminal/terminaltr.h15
-rw-r--r--src/plugins/terminal/terminalwidget.cpp1568
-rw-r--r--src/plugins/terminal/terminalwidget.h256
-rwxr-xr-xsrc/plugins/terminal/tests/colors112
-rwxr-xr-xsrc/plugins/terminal/tests/cursor/bar3
-rwxr-xr-xsrc/plugins/terminal/tests/cursor/blinkbar3
-rwxr-xr-xsrc/plugins/terminal/tests/cursor/blinkblock3
-rwxr-xr-xsrc/plugins/terminal/tests/cursor/blinkunderline3
-rwxr-xr-xsrc/plugins/terminal/tests/cursor/block3
-rwxr-xr-xsrc/plugins/terminal/tests/cursor/underline3
-rwxr-xr-xsrc/plugins/terminal/tests/decoration9
-rwxr-xr-xsrc/plugins/terminal/tests/filenames22
-rwxr-xr-xsrc/plugins/terminal/tests/integration70
-rw-r--r--src/plugins/texteditor/CMakeLists.txt5
-rw-r--r--src/plugins/texteditor/basefilefind.cpp160
-rw-r--r--src/plugins/texteditor/basefilefind.h36
-rw-r--r--src/plugins/texteditor/basehoverhandler.h3
-rw-r--r--src/plugins/texteditor/behaviorsettingspage.cpp126
-rw-r--r--src/plugins/texteditor/behaviorsettingspage.h22
-rw-r--r--src/plugins/texteditor/behaviorsettingswidget.cpp9
-rw-r--r--src/plugins/texteditor/codeassist/asyncprocessor.cpp4
-rw-r--r--src/plugins/texteditor/codeassist/codeassist_test.cpp152
-rw-r--r--src/plugins/texteditor/codeassist/codeassist_test.h37
-rw-r--r--src/plugins/texteditor/codeassist/codeassistant.cpp40
-rw-r--r--src/plugins/texteditor/codestyleeditor.cpp1
-rw-r--r--src/plugins/texteditor/codestyleselectorwidget.cpp8
-rw-r--r--src/plugins/texteditor/colorschemeedit.cpp66
-rw-r--r--src/plugins/texteditor/colorschemeedit.h8
-rw-r--r--src/plugins/texteditor/completionsettingspage.cpp2
-rw-r--r--src/plugins/texteditor/displaysettingspage.cpp59
-rw-r--r--src/plugins/texteditor/fontsettingspage.cpp3
-rw-r--r--src/plugins/texteditor/formattexteditor.cpp16
-rw-r--r--src/plugins/texteditor/highlighter.cpp25
-rw-r--r--src/plugins/texteditor/highlightersettingspage.cpp178
-rw-r--r--src/plugins/texteditor/highlightersettingspage.h4
-rw-r--r--src/plugins/texteditor/icodestylepreferences.cpp11
-rw-r--r--src/plugins/texteditor/icodestylepreferences.h3
-rw-r--r--src/plugins/texteditor/linenumberfilter.cpp102
-rw-r--r--src/plugins/texteditor/linenumberfilter.h24
-rw-r--r--src/plugins/texteditor/markdowneditor.cpp330
-rw-r--r--src/plugins/texteditor/markdowneditor.h21
-rw-r--r--src/plugins/texteditor/outlinefactory.cpp5
-rw-r--r--src/plugins/texteditor/quickfix.h6
-rw-r--r--src/plugins/texteditor/semantichighlighter.cpp26
-rw-r--r--src/plugins/texteditor/snippets/snippet.cpp4
-rw-r--r--src/plugins/texteditor/snippets/snippetscollection.cpp4
-rw-r--r--src/plugins/texteditor/snippets/snippetssettingspage.cpp202
-rw-r--r--src/plugins/texteditor/snippets/snippetssettingspage.h16
-rw-r--r--src/plugins/texteditor/syntaxhighlighter.cpp16
-rw-r--r--src/plugins/texteditor/syntaxhighlighter.h5
-rw-r--r--src/plugins/texteditor/tabsettingswidget.cpp3
-rw-r--r--src/plugins/texteditor/textdocument.cpp21
-rw-r--r--src/plugins/texteditor/textdocument.h4
-rw-r--r--src/plugins/texteditor/textdocumentlayout.cpp142
-rw-r--r--src/plugins/texteditor/textdocumentlayout.h43
-rw-r--r--src/plugins/texteditor/texteditor.cpp314
-rw-r--r--src/plugins/texteditor/texteditor.h25
-rw-r--r--src/plugins/texteditor/texteditor.qbs8
-rw-r--r--src/plugins/texteditor/texteditor_test.cpp19
-rw-r--r--src/plugins/texteditor/texteditoractionhandler.cpp5
-rw-r--r--src/plugins/texteditor/texteditoractionhandler.h3
-rw-r--r--src/plugins/texteditor/texteditorconstants.h1
-rw-r--r--src/plugins/texteditor/texteditoroverlay.cpp12
-rw-r--r--src/plugins/texteditor/texteditorplugin.cpp10
-rw-r--r--src/plugins/texteditor/textmark.cpp17
-rw-r--r--src/plugins/texteditor/textmark.h6
-rw-r--r--src/plugins/todo/keyworddialog.cpp2
-rw-r--r--src/plugins/todo/optionsdialog.cpp2
-rw-r--r--src/plugins/todo/todoitemsprovider.cpp7
-rw-r--r--src/plugins/todo/todoprojectsettingswidget.cpp2
-rw-r--r--src/plugins/updateinfo/settingspage.cpp2
-rw-r--r--src/plugins/updateinfo/settingspage.h8
-rw-r--r--src/plugins/updateinfo/updateinfoplugin.cpp19
-rw-r--r--src/plugins/valgrind/callgrindengine.cpp46
-rw-r--r--src/plugins/valgrind/callgrindengine.h6
-rw-r--r--src/plugins/valgrind/callgrindtool.cpp8
-rw-r--r--src/plugins/valgrind/memcheckerrorview.cpp2
-rw-r--r--src/plugins/valgrind/memchecktool.cpp75
-rw-r--r--src/plugins/valgrind/suppressiondialog.cpp8
-rw-r--r--src/plugins/valgrind/valgrind.qbs4
-rw-r--r--src/plugins/valgrind/valgrindengine.cpp36
-rw-r--r--src/plugins/valgrind/valgrindengine.h3
-rw-r--r--src/plugins/valgrind/valgrindrunner.cpp14
-rw-r--r--src/plugins/valgrind/valgrindsettings.cpp55
-rw-r--r--src/plugins/valgrind/valgrindsettings.h56
-rw-r--r--src/plugins/vcpkg/CMakeLists.txt17
-rw-r--r--src/plugins/vcpkg/Vcpkg.json.in30
-rw-r--r--src/plugins/vcpkg/vcpkg.qbs32
-rw-r--r--src/plugins/vcpkg/vcpkg.qrc6
-rw-r--r--src/plugins/vcpkg/vcpkg_test.cpp110
-rw-r--r--src/plugins/vcpkg/vcpkg_test.h24
-rw-r--r--src/plugins/vcpkg/vcpkgconstants.h14
-rw-r--r--src/plugins/vcpkg/vcpkgmanifesteditor.cpp72
-rw-r--r--src/plugins/vcpkg/vcpkgmanifesteditor.h16
-rw-r--r--src/plugins/vcpkg/vcpkgplugin.cpp38
-rw-r--r--src/plugins/vcpkg/vcpkgplugin.h26
-rw-r--r--src/plugins/vcpkg/vcpkgsearch.cpp214
-rw-r--r--src/plugins/vcpkg/vcpkgsearch.h29
-rw-r--r--src/plugins/vcpkg/vcpkgsettings.cpp68
-rw-r--r--src/plugins/vcpkg/vcpkgsettings.h20
-rw-r--r--src/plugins/vcpkg/vcpkgtr.h15
-rw-r--r--src/plugins/vcpkg/wizards/manifest/vcpkg.json.tpl6
-rw-r--r--src/plugins/vcpkg/wizards/manifest/wizard.json80
-rw-r--r--src/plugins/vcsbase/cleandialog.cpp29
-rw-r--r--src/plugins/vcsbase/commonvcssettings.cpp111
-rw-r--r--src/plugins/vcsbase/commonvcssettings.h32
-rw-r--r--src/plugins/vcsbase/submiteditorwidget.cpp14
-rw-r--r--src/plugins/vcsbase/vcsbaseclient.cpp18
-rw-r--r--src/plugins/vcsbase/vcsbaseclient.h8
-rw-r--r--src/plugins/vcsbase/vcsbaseclientsettings.cpp16
-rw-r--r--src/plugins/vcsbase/vcsbaseclientsettings.h19
-rw-r--r--src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp42
-rw-r--r--src/plugins/vcsbase/vcsbasediffeditorcontroller.h8
-rw-r--r--src/plugins/vcsbase/vcsbaseeditor.cpp4
-rw-r--r--src/plugins/vcsbase/vcsbaseplugin.cpp34
-rw-r--r--src/plugins/vcsbase/vcsbaseplugin.h3
-rw-r--r--src/plugins/vcsbase/vcsbasesubmiteditor.cpp24
-rw-r--r--src/plugins/vcsbase/vcscommand.cpp28
-rw-r--r--src/plugins/vcsbase/vcscommand.h4
-rw-r--r--src/plugins/vcsbase/vcsenums.h2
-rw-r--r--src/plugins/vcsbase/vcsoutputwindow.cpp4
-rw-r--r--src/plugins/vcsbase/vcsplugin.cpp78
-rw-r--r--src/plugins/vcsbase/vcsplugin.h9
-rw-r--r--src/plugins/vcsbase/wizard/vcscommandpage.cpp1
-rw-r--r--src/plugins/webassembly/CMakeLists.txt6
-rw-r--r--src/plugins/webassembly/webassembly.qbs15
-rw-r--r--src/plugins/webassembly/webassemblyconstants.h3
-rw-r--r--src/plugins/webassembly/webassemblydevice.h1
-rw-r--r--src/plugins/webassembly/webassemblyemsdk.cpp53
-rw-r--r--src/plugins/webassembly/webassemblyemsdk.h2
-rw-r--r--src/plugins/webassembly/webassemblyoptionspage.cpp175
-rw-r--r--src/plugins/webassembly/webassemblyoptionspage.h18
-rw-r--r--src/plugins/webassembly/webassemblyplugin.cpp4
-rw-r--r--src/plugins/webassembly/webassemblyrunconfigurationaspects.cpp8
-rw-r--r--src/plugins/webassembly/webassemblyrunconfigurationaspects.h2
-rw-r--r--src/plugins/webassembly/webassemblysettings.cpp168
-rw-r--r--src/plugins/webassembly/webassemblysettings.h34
-rw-r--r--src/plugins/webassembly/webassemblytoolchain.cpp12
-rw-r--r--src/plugins/welcome/introductionwidget.cpp4
-rw-r--r--src/plugins/welcome/introductionwidget.h3
-rw-r--r--src/plugins/welcome/welcomeplugin.cpp3
1665 files changed, 44204 insertions, 27980 deletions
diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
index 0ec8eb2de7..4fd8b65573 100644
--- a/src/plugins/CMakeLists.txt
+++ b/src/plugins/CMakeLists.txt
@@ -20,6 +20,7 @@ add_subdirectory(projectexplorer)
add_subdirectory(silversearcher)
# Level 3: (only depends on Level 2 and below)
+add_subdirectory(axivion)
add_subdirectory(bookmarks)
add_subdirectory(cppeditor)
add_subdirectory(haskell)
@@ -27,6 +28,7 @@ add_subdirectory(help)
add_subdirectory(resourceeditor)
add_subdirectory(nim)
add_subdirectory(conan)
+add_subdirectory(vcpkg)
# Level 4: (only depends on Level 3 and below)
add_subdirectory(classview)
@@ -108,3 +110,5 @@ add_subdirectory(qnx)
add_subdirectory(webassembly)
add_subdirectory(mcusupport)
add_subdirectory(saferenderer)
+add_subdirectory(copilot)
+add_subdirectory(terminal)
diff --git a/src/plugins/android/android.qbs b/src/plugins/android/android.qbs
index 5c9b6564c7..c6d1611636 100644
--- a/src/plugins/android/android.qbs
+++ b/src/plugins/android/android.qbs
@@ -116,9 +116,7 @@ Project {
"sdkmanageroutputparser.h"
]
- Group {
- name: "Unit tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
"android_tst.qrc",
"androidsdkmanager_test.cpp",
diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp
index 7cf62fadf8..6740370302 100644
--- a/src/plugins/android/androidavdmanager.cpp
+++ b/src/plugins/android/androidavdmanager.cpp
@@ -10,25 +10,21 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
-#include <QApplication>
#include <QLoggingCategory>
#include <QMainWindow>
#include <QMessageBox>
-#include <QSettings>
#include <chrono>
-#include <functional>
using namespace Utils;
+using namespace std;
namespace Android::Internal {
-using namespace std;
-
const int avdCreateTimeoutMs = 30000;
static Q_LOGGING_CATEGORY(avdManagerLog, "qtc.android.avdManager", QtWarningMsg)
@@ -41,7 +37,7 @@ static Q_LOGGING_CATEGORY(avdManagerLog, "qtc.android.avdManager", QtWarningMsg)
bool AndroidAvdManager::avdManagerCommand(const AndroidConfig &config, const QStringList &args, QString *output)
{
CommandLine cmd(config.avdManagerToolPath(), args);
- QtcProcess proc;
+ Process proc;
Environment env = AndroidConfigurations::toolsEnvironment(config);
proc.setEnvironment(env);
qCDebug(avdManagerLog).noquote() << "Running AVD Manager command:" << cmd.toUserOutput();
@@ -89,7 +85,7 @@ static CreateAvdInfo createAvdCommand(const AndroidConfig &config, const CreateA
avdManager.addArg("-f");
qCDebug(avdManagerLog).noquote() << "Running AVD Manager command:" << avdManager.toUserOutput();
- QtcProcess proc;
+ Process proc;
proc.setProcessMode(ProcessMode::Writer);
proc.setEnvironment(AndroidConfigurations::toolsEnvironment(config));
proc.setCommand(avdManager);
@@ -143,14 +139,14 @@ AndroidAvdManager::~AndroidAvdManager() = default;
QFuture<CreateAvdInfo> AndroidAvdManager::createAvd(CreateAvdInfo info) const
{
- return runAsync(&createAvdCommand, m_config, info);
+ return Utils::asyncRun(&createAvdCommand, m_config, info);
}
bool AndroidAvdManager::removeAvd(const QString &name) const
{
const CommandLine command(m_config.avdManagerToolPath(), {"delete", "avd", "-n", name});
qCDebug(avdManagerLog).noquote() << "Running command (removeAvd):" << command.toUserOutput();
- QtcProcess proc;
+ Process proc;
proc.setTimeoutS(5);
proc.setEnvironment(AndroidConfigurations::toolsEnvironment(m_config));
proc.setCommand(command);
@@ -221,14 +217,14 @@ static AndroidDeviceInfoList listVirtualDevices(const AndroidConfig &config)
QFuture<AndroidDeviceInfoList> AndroidAvdManager::avdList() const
{
- return runAsync(listVirtualDevices, m_config);
+ return Utils::asyncRun(listVirtualDevices, m_config);
}
QString AndroidAvdManager::startAvd(const QString &name) const
{
if (!findAvd(name).isEmpty() || startAvdAsync(name))
return waitForAvd(name);
- return QString();
+ return {};
}
static bool is32BitUserSpace()
@@ -236,7 +232,7 @@ static bool is32BitUserSpace()
// Do a similar check as android's emulator is doing:
if (HostOsInfo::isLinuxHost()) {
if (QSysInfo::WordSize == 32) {
- QtcProcess proc;
+ Process proc;
proc.setTimeoutS(3);
proc.setCommand({"getconf", {"LONG_BIT"}});
proc.runBlocking();
@@ -262,13 +258,13 @@ bool AndroidAvdManager::startAvdAsync(const QString &avdName) const
return false;
}
- // TODO: Here we are potentially leaking QtcProcess instance in case when shutdown happens
+ // TODO: Here we are potentially leaking Process instance in case when shutdown happens
// after the avdProcess has started and before it has finished. Giving a parent object here
// should solve the issue. However, AndroidAvdManager is not a QObject, so no clue what parent
// would be the most appropriate. Preferably some object taken form android plugin...
- QtcProcess *avdProcess = new QtcProcess;
+ Process *avdProcess = new Process;
avdProcess->setProcessChannelMode(QProcess::MergedChannels);
- QObject::connect(avdProcess, &QtcProcess::done, avdProcess, [avdProcess] {
+ QObject::connect(avdProcess, &Process::done, avdProcess, [avdProcess] {
if (avdProcess->exitCode()) {
const QString errorOutput = QString::fromLatin1(avdProcess->readAllRawStandardOutput());
QMetaObject::invokeMethod(Core::ICore::mainWindow(), [errorOutput] {
@@ -301,21 +297,21 @@ QString AndroidAvdManager::findAvd(const QString &avdName) const
if (device.avdName == avdName)
return device.serialNumber;
}
- return QString();
+ return {};
}
QString AndroidAvdManager::waitForAvd(const QString &avdName,
- const QFutureInterfaceBase &fi) const
+ const std::optional<QFuture<void>> &future) const
{
// we cannot use adb -e wait-for-device, since that doesn't work if a emulator is already running
// 60 rounds of 2s sleeping, two minutes for the avd to start
QString serialNumber;
for (int i = 0; i < 60; ++i) {
- if (fi.isCanceled())
+ if (future && future->isCanceled())
return {};
serialNumber = findAvd(avdName);
if (!serialNumber.isEmpty())
- return waitForBooted(serialNumber, fi) ? serialNumber : QString();
+ return waitForBooted(serialNumber, future) ? serialNumber : QString();
QThread::sleep(2);
}
return {};
@@ -328,7 +324,7 @@ bool AndroidAvdManager::isAvdBooted(const QString &device) const
const CommandLine command({m_config.adbToolPath(), arguments});
qCDebug(avdManagerLog).noquote() << "Running command (isAvdBooted):" << command.toUserOutput();
- QtcProcess adbProc;
+ Process adbProc;
adbProc.setTimeoutS(10);
adbProc.setCommand(command);
adbProc.runBlocking();
@@ -339,11 +335,11 @@ bool AndroidAvdManager::isAvdBooted(const QString &device) const
}
bool AndroidAvdManager::waitForBooted(const QString &serialNumber,
- const QFutureInterfaceBase &fi) const
+ const std::optional<QFuture<void>> &future) const
{
// found a serial number, now wait until it's done booting...
for (int i = 0; i < 60; ++i) {
- if (fi.isCanceled())
+ if (future && future->isCanceled())
return false;
if (isAvdBooted(serialNumber))
return true;
diff --git a/src/plugins/android/androidavdmanager.h b/src/plugins/android/androidavdmanager.h
index cad3a2efe7..545dedbfe2 100644
--- a/src/plugins/android/androidavdmanager.h
+++ b/src/plugins/android/androidavdmanager.h
@@ -4,8 +4,9 @@
#include "androidconfigurations.h"
-#include <functional>
-#include <memory>
+#include <QFuture>
+
+#include <optional>
namespace Android::Internal {
@@ -22,14 +23,14 @@ public:
QString startAvd(const QString &name) const;
bool startAvdAsync(const QString &avdName) const;
QString findAvd(const QString &avdName) const;
- QString waitForAvd(const QString &avdName, const QFutureInterfaceBase &fi = {}) const;
+ QString waitForAvd(const QString &avdName, const std::optional<QFuture<void>> &future = {}) const;
bool isAvdBooted(const QString &device) const;
static bool avdManagerCommand(const AndroidConfig &config,
const QStringList &args,
QString *output);
private:
- bool waitForBooted(const QString &serialNumber, const QFutureInterfaceBase &fi = {}) const;
+ bool waitForBooted(const QString &serialNumber, const std::optional<QFuture<void>> &future = {}) const;
private:
const AndroidConfig &m_config;
diff --git a/src/plugins/android/androidbuildapkstep.cpp b/src/plugins/android/androidbuildapkstep.cpp
index adf96eb68d..b27730b45b 100644
--- a/src/plugins/android/androidbuildapkstep.cpp
+++ b/src/plugins/android/androidbuildapkstep.cpp
@@ -36,7 +36,7 @@
#include <utils/infolabel.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QCheckBox>
#include <QComboBox>
@@ -137,8 +137,9 @@ AndroidBuildApkWidget::AndroidBuildApkWidget(AndroidBuildApkStep *step)
createSignPackageGroup(),
createApplicationGroup(),
createAdvancedGroup(),
- createAdditionalLibrariesGroup()
- }.attachTo(this, WithoutMargins);
+ createAdditionalLibrariesGroup(),
+ noMargin
+ }.attachTo(this);
connect(m_step->buildConfiguration(), &BuildConfiguration::buildTypeChanged,
this, &AndroidBuildApkWidget::updateSigningWarning);
@@ -711,6 +712,13 @@ void AndroidBuildApkStep::doRun()
return;
}
+ if (AndroidManager::skipInstallationAndPackageSteps(target())) {
+ reportWarningOrError(Tr::tr("Product type is not an application, not building an APK."),
+ Task::Warning);
+ emit finished(true);
+ return;
+ }
+
auto setup = [this] {
const auto androidAbis = AndroidManager::applicationAbis(target());
const QString buildKey = target()->activeBuildKey();
@@ -863,7 +871,7 @@ void AndroidBuildApkStep::updateBuildToolsVersionInJsonFile()
if (!contents)
return;
- QRegularExpression regex(QLatin1String("\"sdkBuildToolsRevision\":.\"[0-9.]+\""));
+ static const QRegularExpression regex(R"("sdkBuildToolsRevision":."[0-9.]+")");
QRegularExpressionMatch match = regex.match(QString::fromUtf8(contents.value()));
const QString version = buildToolsVersion().toString();
if (match.hasMatch() && !version.isEmpty()) {
@@ -925,7 +933,8 @@ void AndroidBuildApkStep::setBuildToolsVersion(const QVersionNumber &version)
void AndroidBuildApkStep::stdError(const QString &output)
{
QString newOutput = output;
- newOutput.remove(QRegularExpression("^(\\n)+"));
+ static const QRegularExpression re("^(\\n)+");
+ newOutput.remove(re);
if (newOutput.isEmpty())
return;
@@ -1041,7 +1050,7 @@ QAbstractItemModel *AndroidBuildApkStep::keystoreCertificates()
const QStringList params = {"-list", "-v", "-keystore", m_keystorePath.toUserOutput(),
"-storepass", m_keystorePasswd, "-J-Duser.language=en"};
- QtcProcess keytoolProc;
+ Process keytoolProc;
keytoolProc.setTimeoutS(30);
keytoolProc.setCommand({AndroidConfigurations::currentConfig().keytoolPath(), params});
keytoolProc.runBlocking(EventLoopMode::On);
diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp
index 5477bf14bf..9c444f6e75 100644
--- a/src/plugins/android/androidconfigurations.cpp
+++ b/src/plugins/android/androidconfigurations.cpp
@@ -19,7 +19,7 @@
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/toolchainmanager.h>
#include <debugger/debuggeritemmanager.h>
@@ -34,8 +34,8 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/persistentsettings.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <QApplication>
@@ -306,7 +306,7 @@ void AndroidConfig::parseDependenciesJson()
auto fillQtVersionsRange = [](const QString &shortVersion) {
QList<QVersionNumber> versions;
- const QRegularExpression re(R"(([0-9]\.[0-9]+\.)\[([0-9]+)\-([0-9]+)\])");
+ static const QRegularExpression re(R"(([0-9]\.[0-9]+\.)\[([0-9]+)\-([0-9]+)\])");
QRegularExpressionMatch match = re.match(shortVersion);
if (match.hasMatch() && match.lastCapturedIndex() == 3)
for (int i = match.captured(2).toInt(); i <= match.captured(3).toInt(); ++i)
@@ -432,8 +432,12 @@ QStringList AndroidConfig::apiLevelNamesFor(const SdkPlatformList &platforms)
QString AndroidConfig::apiLevelNameFor(const SdkPlatform *platform)
{
- return platform && platform->apiLevel() > 0 ?
- QString("android-%1").arg(platform->apiLevel()) : "";
+ if (platform && platform->apiLevel() > 0) {
+ QString sdkStylePath = platform->sdkStylePath();
+ return sdkStylePath.remove("platforms;");
+ }
+
+ return {};
}
FilePath AndroidConfig::adbToolPath() const
@@ -597,7 +601,7 @@ FilePath AndroidConfig::keytoolPath() const
QVector<AndroidDeviceInfo> AndroidConfig::connectedDevices(QString *error) const
{
QVector<AndroidDeviceInfo> devices;
- QtcProcess adbProc;
+ Process adbProc;
adbProc.setTimeoutS(30);
CommandLine cmd{adbToolPath(), {"devices"}};
adbProc.setCommand(cmd);
@@ -666,7 +670,7 @@ QString AndroidConfig::getDeviceProperty(const QString &device, const QString &p
AndroidDeviceInfo::adbSelector(device));
cmd.addArgs({"shell", "getprop", property});
- QtcProcess adbProc;
+ Process adbProc;
adbProc.setTimeoutS(10);
adbProc.setCommand(cmd);
adbProc.runBlocking();
@@ -743,7 +747,7 @@ QStringList AndroidConfig::getAbis(const QString &device)
// First try via ro.product.cpu.abilist
QStringList arguments = AndroidDeviceInfo::adbSelector(device);
arguments << "shell" << "getprop" << "ro.product.cpu.abilist";
- QtcProcess adbProc;
+ Process adbProc;
adbProc.setTimeoutS(10);
adbProc.setCommand({adbTool, arguments});
adbProc.runBlocking();
@@ -766,7 +770,7 @@ QStringList AndroidConfig::getAbis(const QString &device)
else
arguments << QString::fromLatin1("ro.product.cpu.abi%1").arg(i);
- QtcProcess abiProc;
+ Process abiProc;
abiProc.setTimeoutS(10);
abiProc.setCommand({adbTool, arguments});
abiProc.runBlocking();
@@ -892,7 +896,7 @@ QVersionNumber AndroidConfig::ndkVersion(const FilePath &ndkPath)
// r6a
// r10e (64 bit)
QString content = QString::fromUtf8(reader.data());
- QRegularExpression re("(r)(?<major>[0-9]{1,2})(?<minor>[a-z]{1,1})");
+ static const QRegularExpression re("(r)(?<major>[0-9]{1,2})(?<minor>[a-z]{1,1})");
QRegularExpressionMatch match = re.match(content);
if (match.hasMatch()) {
QString major = match.captured("major");
@@ -1169,7 +1173,7 @@ void AndroidConfigurations::removeUnusedDebuggers()
uniqueNdks.append(ndkLocation);
}
- uniqueNdks.append(FileUtils::toFilePathList(currentConfig().getCustomNdkList()).toVector());
+ uniqueNdks.append(FileUtils::toFilePathList(currentConfig().getCustomNdkList()));
const QList<Debugger::DebuggerItem> allDebuggers = Debugger::DebuggerItemManager::debuggers();
for (const Debugger::DebuggerItem &debugger : allDebuggers) {
@@ -1526,7 +1530,7 @@ FilePath AndroidConfig::getJdkPath()
args << "-c"
<< "readlink -f $(which java)";
- QtcProcess findJdkPathProc;
+ Process findJdkPathProc;
findJdkPathProc.setCommand({"sh", args});
findJdkPathProc.start();
findJdkPathProc.waitForFinished();
diff --git a/src/plugins/android/androidcreatekeystorecertificate.cpp b/src/plugins/android/androidcreatekeystorecertificate.cpp
index 003eb7e9b9..67e6c8ff83 100644
--- a/src/plugins/android/androidcreatekeystorecertificate.cpp
+++ b/src/plugins/android/androidcreatekeystorecertificate.cpp
@@ -7,7 +7,7 @@
#include <utils/infolabel.h>
#include <utils/layoutbuilder.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QCheckBox>
#include <QDialog>
@@ -217,7 +217,8 @@ bool AndroidCreateKeystoreCertificate::checkCertificateAlias()
bool AndroidCreateKeystoreCertificate::checkCountryCode()
{
- if (!m_countryLineEdit->text().contains(QRegularExpression("[A-Z]{2}"))) {
+ static const QRegularExpression re("[A-Z]{2}");
+ if (!m_countryLineEdit->text().contains(re)) {
m_infoLabel->show();
m_infoLabel->setText(Tr::tr("Invalid country code."));
return false;
@@ -271,7 +272,7 @@ void AndroidCreateKeystoreCertificate::buttonBoxAccepted()
"-keypass", certificatePassword(),
"-dname", distinguishedNames});
- QtcProcess genKeyCertProc;
+ Process genKeyCertProc;
genKeyCertProc.setTimeoutS(15);
genKeyCertProc.setCommand(command);
genKeyCertProc.runBlocking(EventLoopMode::On);
diff --git a/src/plugins/android/androiddebugsupport.cpp b/src/plugins/android/androiddebugsupport.cpp
index 138740e4a3..bb4fc51b98 100644
--- a/src/plugins/android/androiddebugsupport.cpp
+++ b/src/plugins/android/androiddebugsupport.cpp
@@ -21,7 +21,7 @@
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QHostAddress>
#include <QJsonDocument>
diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp
index c305987a50..a9e9596e77 100644
--- a/src/plugins/android/androiddeployqtstep.cpp
+++ b/src/plugins/android/androiddeployqtstep.cpp
@@ -2,10 +2,11 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "androiddeployqtstep.h"
+
#include "androidavdmanager.h"
#include "androidbuildapkstep.h"
#include "androidconstants.h"
-#include "androiddeployqtstep.h"
#include "androiddevice.h"
#include "androidmanager.h"
#include "androidqtversion.h"
@@ -16,6 +17,7 @@
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
+#include <projectexplorer/abstractprocessstep.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/buildsteplist.h>
@@ -27,13 +29,17 @@
#include <projectexplorer/taskhub.h>
#include <projectexplorer/toolchain.h>
+#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
+#include <utils/commandline.h>
+#include <utils/environment.h>
+#include <utils/futuresynchronizer.h>
#include <utils/layoutbuilder.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <QCheckBox>
#include <QFileDialog>
@@ -59,23 +65,94 @@ const QLatin1String InstallFailedVersionDowngrade("INSTALL_FAILED_VERSION_DOWNGR
// AndroidDeployQtStep
-AndroidDeployQtStep::AndroidDeployQtStep(BuildStepList *parent, Utils::Id id)
+class AndroidDeployQtStep : public BuildStep
+{
+ Q_OBJECT
+
+ enum DeployErrorCode
+ {
+ NoError = 0,
+ InconsistentCertificates = 0x0001,
+ UpdateIncompatible = 0x0002,
+ PermissionModelDowngrade = 0x0004,
+ VersionDowngrade = 0x0008,
+ Failure = 0x0010
+ };
+
+public:
+ AndroidDeployQtStep(BuildStepList *bc, Id id);
+
+signals:
+ void askForUninstall(DeployErrorCode errorCode);
+
+private:
+ void runCommand(const CommandLine &command);
+
+ bool init() override;
+ void doRun() override;
+ void doCancel() override;
+ void gatherFilesToPull();
+ DeployErrorCode runDeploy();
+ void slotAskForUninstall(DeployErrorCode errorCode);
+
+ void runImpl(QPromise<bool> &promise);
+
+ QWidget *createConfigWidget() override;
+
+ void processReadyReadStdOutput(DeployErrorCode &errorCode);
+ void stdOutput(const QString &line);
+ void processReadyReadStdError(DeployErrorCode &errorCode);
+ void stdError(const QString &line);
+ DeployErrorCode parseDeployErrors(const QString &deployOutputLine) const;
+
+ friend void operator|=(DeployErrorCode &e1, const DeployErrorCode &e2) {
+ e1 = static_cast<AndroidDeployQtStep::DeployErrorCode>((int)e1 | (int)e2);
+ }
+
+ friend DeployErrorCode operator|(const DeployErrorCode &e1, const DeployErrorCode &e2) {
+ return static_cast<AndroidDeployQtStep::DeployErrorCode>((int)e1 | (int)e2);
+ }
+
+ void reportWarningOrError(const QString &message, Task::TaskType type);
+
+ FilePath m_manifestName;
+ QString m_serialNumber;
+ QString m_avdName;
+ FilePath m_apkPath;
+ QMap<QString, FilePath> m_filesToPull;
+
+ QStringList m_androidABIs;
+ BoolAspect m_uninstallPreviousPackage{this};
+ bool m_uninstallPreviousPackageRun = false;
+ bool m_useAndroiddeployqt = false;
+ bool m_askForUninstall = false;
+ CommandLine m_androiddeployqtArgs;
+ FilePath m_adbPath;
+ FilePath m_command;
+ FilePath m_workingDirectory;
+ Environment m_environment;
+ AndroidDeviceInfo m_deviceInfo;
+
+ // The synchronizer has cancelOnWait set to true by default.
+ FutureSynchronizer m_synchronizer;
+};
+
+AndroidDeployQtStep::AndroidDeployQtStep(BuildStepList *parent, Id id)
: BuildStep(parent, id)
{
setImmutable(true);
setUserExpanded(true);
- m_uninstallPreviousPackage = addAspect<BoolAspect>();
- m_uninstallPreviousPackage->setSettingsKey(UninstallPreviousPackageKey);
- m_uninstallPreviousPackage->setLabel(Tr::tr("Uninstall the existing app before deployment"),
+ m_uninstallPreviousPackage.setSettingsKey(UninstallPreviousPackageKey);
+ m_uninstallPreviousPackage.setLabel(Tr::tr("Uninstall the existing app before deployment"),
BoolAspect::LabelPlacement::AtCheckBox);
- m_uninstallPreviousPackage->setValue(false);
+ m_uninstallPreviousPackage.setValue(false);
const QtSupport::QtVersion * const qt = QtSupport::QtKitAspect::qtVersion(kit());
const bool forced = qt && qt->qtVersion() < QVersionNumber(5, 4, 0);
if (forced) {
- m_uninstallPreviousPackage->setValue(true);
- m_uninstallPreviousPackage->setEnabled(false);
+ m_uninstallPreviousPackage.setValue(true);
+ m_uninstallPreviousPackage.setEnabled(false);
}
connect(this, &AndroidDeployQtStep::askForUninstall,
@@ -157,8 +234,7 @@ bool AndroidDeployQtStep::init()
return false;
}
- const bool abiListNotEmpty = !selectedAbis.isEmpty() && !dev->supportedAbis().isEmpty();
- if (abiListNotEmpty && !dev->canSupportAbis(selectedAbis)) {
+ if (!dev->canSupportAbis(selectedAbis)) {
const QString error = Tr::tr("The deployment device \"%1\" does not support the "
"architectures used by the kit.\n"
"The kit supports \"%2\", but the device uses \"%3\".")
@@ -197,7 +273,7 @@ bool AndroidDeployQtStep::init()
emit addOutput(Tr::tr("Deploying to %1").arg(m_serialNumber), OutputFormat::NormalMessage);
- m_uninstallPreviousPackageRun = m_uninstallPreviousPackage->value();
+ m_uninstallPreviousPackageRun = m_uninstallPreviousPackage();
if (m_uninstallPreviousPackageRun)
m_manifestName = AndroidManager::manifestPath(target());
@@ -209,9 +285,9 @@ bool AndroidDeployQtStep::init()
reportWarningOrError(Tr::tr("The deployment step's project node is invalid."), Task::Error);
return false;
}
- m_apkPath = Utils::FilePath::fromString(node->data(Constants::AndroidApk).toString());
+ m_apkPath = FilePath::fromString(node->data(Constants::AndroidApk).toString());
if (!m_apkPath.isEmpty()) {
- m_manifestName = Utils::FilePath::fromString(node->data(Constants::AndroidManifest).toString());
+ m_manifestName = FilePath::fromString(node->data(Constants::AndroidManifest).toString());
m_command = AndroidConfigurations::currentConfig().adbToolPath();
AndroidManager::setManifestPath(target(), m_manifestName);
} else {
@@ -254,7 +330,7 @@ bool AndroidDeployQtStep::init()
m_apkPath = AndroidManager::packagePath(target());
m_workingDirectory = bc ? AndroidManager::buildDirectory(target()): FilePath();
}
- m_environment = bc ? bc->environment() : Utils::Environment();
+ m_environment = bc ? bc->environment() : Environment();
m_adbPath = AndroidConfigurations::currentConfig().adbToolPath();
@@ -303,7 +379,7 @@ AndroidDeployQtStep::DeployErrorCode AndroidDeployQtStep::runDeploy()
cmd.addArgs({"install", "-r", m_apkPath.toString()});
}
- QtcProcess process;
+ Process process;
process.setCommand(cmd);
process.setWorkingDirectory(m_workingDirectory);
process.setEnvironment(m_environment);
@@ -401,15 +477,16 @@ void AndroidDeployQtStep::slotAskForUninstall(DeployErrorCode errorCode)
m_askForUninstall = button == QMessageBox::Yes;
}
-void AndroidDeployQtStep::runImpl(QFutureInterface<bool> &fi)
+void AndroidDeployQtStep::runImpl(QPromise<bool> &promise)
{
if (!m_avdName.isEmpty()) {
- QString serialNumber = AndroidAvdManager().waitForAvd(m_avdName, fi);
+ const QString serialNumber = AndroidAvdManager().waitForAvd(m_avdName,
+ QFuture<void>(promise.future()));
qCDebug(deployStepLog) << "Deploying to AVD:" << m_avdName << serialNumber;
if (serialNumber.isEmpty()) {
reportWarningOrError(Tr::tr("The deployment AVD \"%1\" cannot be started.")
.arg(m_avdName), Task::Error);
- fi.reportResult(false);
+ promise.addResult(false);
return;
}
m_serialNumber = serialNumber;
@@ -445,7 +522,7 @@ void AndroidDeployQtStep::runImpl(QFutureInterface<bool> &fi)
reportWarningOrError(error, Task::Error);
}
}
- fi.reportResult(returnValue == NoError);
+ promise.addResult(returnValue == NoError);
}
void AndroidDeployQtStep::gatherFilesToPull()
@@ -485,7 +562,7 @@ void AndroidDeployQtStep::doRun()
emit finished(success);
watcher->deleteLater();
});
- auto future = Utils::runAsync(&AndroidDeployQtStep::runImpl, this);
+ auto future = Utils::asyncRun(&AndroidDeployQtStep::runImpl, this);
watcher->setFuture(future);
m_synchronizer.addFuture(future);
}
@@ -497,7 +574,7 @@ void AndroidDeployQtStep::doCancel()
void AndroidDeployQtStep::runCommand(const CommandLine &command)
{
- QtcProcess buildProc;
+ Process buildProc;
buildProc.setTimeoutS(2 * 60);
emit addOutput(Tr::tr("Package deploy: Running command \"%1\".").arg(command.toUserOutput()),
OutputFormat::NormalMessage);
@@ -524,10 +601,13 @@ QWidget *AndroidDeployQtStep::createConfigWidget()
AndroidManager::installQASIPackage(target(), packagePath);
});
- Layouting::Form builder;
- builder.addRow(m_uninstallPreviousPackage);
- builder.addRow(installCustomApkButton);
- builder.attachTo(widget, Layouting::WithoutMargins);
+ using namespace Layouting;
+
+ Form {
+ m_uninstallPreviousPackage, br,
+ installCustomApkButton,
+ noMargin
+ }.attachTo(widget);
return widget;
}
@@ -542,7 +622,8 @@ void AndroidDeployQtStep::stdError(const QString &line)
emit addOutput(line, BuildStep::OutputFormat::Stderr, BuildStep::DontAppendNewline);
QString newOutput = line;
- newOutput.remove(QRegularExpression("^(\\n)+"));
+ static const QRegularExpression re("^(\\n)+");
+ newOutput.remove(re);
if (newOutput.isEmpty())
return;
@@ -592,3 +673,5 @@ AndroidDeployQtStepFactory::AndroidDeployQtStepFactory()
}
} // Android::Internal
+
+#include "androiddeployqtstep.moc"
diff --git a/src/plugins/android/androiddeployqtstep.h b/src/plugins/android/androiddeployqtstep.h
index 334be8ea22..dda4304b6a 100644
--- a/src/plugins/android/androiddeployqtstep.h
+++ b/src/plugins/android/androiddeployqtstep.h
@@ -4,16 +4,7 @@
#pragma once
-#include "androiddeviceinfo.h"
-
-#include <projectexplorer/abstractprocessstep.h>
-#include <qtsupport/baseqtversion.h>
-
-#include <utils/commandline.h>
-#include <utils/environment.h>
-#include <utils/futuresynchronizer.h>
-
-namespace Utils { class QtcProcess; }
+#include <projectexplorer/buildstep.h>
namespace Android::Internal {
@@ -23,76 +14,4 @@ public:
AndroidDeployQtStepFactory();
};
-class AndroidDeployQtStep : public ProjectExplorer::BuildStep
-{
- Q_OBJECT
-
- enum DeployErrorCode
- {
- NoError = 0,
- InconsistentCertificates = 0x0001,
- UpdateIncompatible = 0x0002,
- PermissionModelDowngrade = 0x0004,
- VersionDowngrade = 0x0008,
- Failure = 0x0010
- };
-
-public:
- AndroidDeployQtStep(ProjectExplorer::BuildStepList *bc, Utils::Id id);
-
-signals:
- void askForUninstall(DeployErrorCode errorCode);
-
-private:
- void runCommand(const Utils::CommandLine &command);
-
- bool init() override;
- void doRun() override;
- void doCancel() override;
- void gatherFilesToPull();
- DeployErrorCode runDeploy();
- void slotAskForUninstall(DeployErrorCode errorCode);
-
- void runImpl(QFutureInterface<bool> &fi);
-
- QWidget *createConfigWidget() override;
-
- void processReadyReadStdOutput(DeployErrorCode &errorCode);
- void stdOutput(const QString &line);
- void processReadyReadStdError(DeployErrorCode &errorCode);
- void stdError(const QString &line);
- DeployErrorCode parseDeployErrors(const QString &deployOutputLine) const;
-
- friend void operator|=(DeployErrorCode &e1, const DeployErrorCode &e2) {
- e1 = static_cast<AndroidDeployQtStep::DeployErrorCode>((int)e1 | (int)e2);
- }
-
- friend DeployErrorCode operator|(const DeployErrorCode &e1, const DeployErrorCode &e2) {
- return static_cast<AndroidDeployQtStep::DeployErrorCode>((int)e1 | (int)e2);
- }
-
- void reportWarningOrError(const QString &message, ProjectExplorer::Task::TaskType type);
-
- Utils::FilePath m_manifestName;
- QString m_serialNumber;
- QString m_avdName;
- Utils::FilePath m_apkPath;
- QMap<QString, Utils::FilePath> m_filesToPull;
-
- QStringList m_androidABIs;
- Utils::BoolAspect *m_uninstallPreviousPackage = nullptr;
- bool m_uninstallPreviousPackageRun = false;
- bool m_useAndroiddeployqt = false;
- bool m_askForUninstall = false;
- static const Utils::Id Id;
- Utils::CommandLine m_androiddeployqtArgs;
- Utils::FilePath m_adbPath;
- Utils::FilePath m_command;
- Utils::FilePath m_workingDirectory;
- Utils::Environment m_environment;
- AndroidDeviceInfo m_deviceInfo;
-
- Utils::FutureSynchronizer m_synchronizer;
-};
-
} // Android::Internal
diff --git a/src/plugins/android/androiddevice.cpp b/src/plugins/android/androiddevice.cpp
index 4c034c30af..ce2f4c11ee 100644
--- a/src/plugins/android/androiddevice.cpp
+++ b/src/plugins/android/androiddevice.cpp
@@ -18,15 +18,14 @@
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/runconfiguration.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
+#include <utils/async.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <utils/url.h>
-#include <QEventLoop>
#include <QFormLayout>
#include <QInputDialog>
#include <QLoggingCategory>
@@ -388,11 +387,6 @@ IDeviceWidget *AndroidDevice::createWidget()
return new AndroidDeviceWidget(sharedFromThis());
}
-bool AndroidDevice::canAutoDetectPorts() const
-{
- return true;
-}
-
DeviceProcessSignalOperation::Ptr AndroidDevice::signalOperation() const
{
return DeviceProcessSignalOperation::Ptr(new AndroidSignalOperation());
@@ -454,7 +448,7 @@ void AndroidDeviceManager::startAvd(const ProjectExplorer::IDevice::Ptr &device,
const AndroidDevice *androidDev = static_cast<const AndroidDevice *>(device.data());
const QString name = androidDev->avdName();
qCDebug(androidDeviceLog, "Starting Android AVD id \"%s\".", qPrintable(name));
- runAsync([this, name, device] {
+ auto future = Utils::asyncRun([this, name, device] {
const QString serialNumber = m_avdManager.startAvd(name);
// Mark the AVD as ReadyToUse once we know it's started
if (!serialNumber.isEmpty()) {
@@ -462,6 +456,7 @@ void AndroidDeviceManager::startAvd(const ProjectExplorer::IDevice::Ptr &device,
devMgr->setDeviceState(device->id(), IDevice::DeviceReadyToUse);
}
});
+ // TODO: use future!
}
void AndroidDeviceManager::eraseAvd(const IDevice::Ptr &device, QWidget *parent)
@@ -479,7 +474,7 @@ void AndroidDeviceManager::eraseAvd(const IDevice::Ptr &device, QWidget *parent)
return;
qCDebug(androidDeviceLog) << QString("Erasing Android AVD \"%1\" from the system.").arg(name);
- m_removeAvdFutureWatcher.setFuture(runAsync([this, name, device] {
+ m_removeAvdFutureWatcher.setFuture(Utils::asyncRun([this, name, device] {
QPair<IDevice::ConstPtr, bool> pair;
pair.first = device;
pair.second = false;
@@ -618,20 +613,20 @@ void AndroidDeviceManager::setupDevicesWatcher()
}
if (!m_adbDeviceWatcherProcess)
- m_adbDeviceWatcherProcess.reset(new QtcProcess(this));
+ m_adbDeviceWatcherProcess.reset(new Process(this));
if (m_adbDeviceWatcherProcess->isRunning()) {
qCDebug(androidDeviceLog) << "ADB device watcher is already running.";
return;
}
- connect(m_adbDeviceWatcherProcess.get(), &QtcProcess::done, this, [this] {
+ connect(m_adbDeviceWatcherProcess.get(), &Process::done, this, [this] {
if (m_adbDeviceWatcherProcess->error() != QProcess::UnknownError) {
qCDebug(androidDeviceLog) << "ADB device watcher encountered an error:"
<< m_adbDeviceWatcherProcess->errorString();
if (!m_adbDeviceWatcherProcess->isRunning()) {
qCDebug(androidDeviceLog) << "Restarting the ADB device watcher now.";
- QTimer::singleShot(0, m_adbDeviceWatcherProcess.get(), &QtcProcess::start);
+ QTimer::singleShot(0, m_adbDeviceWatcherProcess.get(), &Process::start);
}
}
qCDebug(androidDeviceLog) << "ADB device watcher finished.";
@@ -847,7 +842,6 @@ AndroidDeviceFactory::AndroidDeviceFactory()
setDisplayName(Tr::tr("Android Device"));
setCombinedIcon(":/android/images/androiddevicesmall.png",
":/android/images/androiddevice.png");
-
setConstructionFunction(&AndroidDevice::create);
if (m_androidConfig.sdkToolsOk()) {
setCreator([this] {
diff --git a/src/plugins/android/androiddevice.h b/src/plugins/android/androiddevice.h
index 0d17c73ec8..cf8046cfa9 100644
--- a/src/plugins/android/androiddevice.h
+++ b/src/plugins/android/androiddevice.h
@@ -15,7 +15,7 @@
#include <QFileSystemWatcher>
#include <QSettings>
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace Android {
namespace Internal {
@@ -61,7 +61,6 @@ private:
void addActionsIfNotFound();
ProjectExplorer::IDevice::DeviceInfo deviceInformation() const override;
ProjectExplorer::IDeviceWidget *createWidget() override;
- bool canAutoDetectPorts() const override;
ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const override;
QUrl toolControlChannel(const ControlChannelHint &) const override;
@@ -109,7 +108,7 @@ private:
QFutureWatcher<AndroidDeviceInfoList> m_avdsFutureWatcher;
QFutureWatcher<QPair<ProjectExplorer::IDevice::ConstPtr, bool>> m_removeAvdFutureWatcher;
QFileSystemWatcher m_avdFileSystemWatcher;
- std::unique_ptr<Utils::QtcProcess> m_adbDeviceWatcherProcess;
+ std::unique_ptr<Utils::Process> m_adbDeviceWatcherProcess;
AndroidConfig &m_androidConfig;
AndroidAvdManager m_avdManager;
diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp
index a15da30048..78b6202a65 100644
--- a/src/plugins/android/androidmanager.cpp
+++ b/src/plugins/android/androidmanager.cpp
@@ -24,7 +24,7 @@
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/buildsystem.h>
@@ -36,8 +36,8 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <QApplication>
@@ -359,6 +359,29 @@ Abi AndroidManager::androidAbi2Abi(const QString &androidAbi)
}
}
+bool AndroidManager::skipInstallationAndPackageSteps(const Target *target)
+{
+ // For projects using Qt 5.15 and Qt 6, the deployment settings file
+ // is generated by CMake/qmake and not Qt Creator, so if such file doesn't exist
+ // or it's been generated by Qt Creator, we can assume the project is not
+ // an android app.
+ const FilePath inputFile = AndroidQtVersion::androidDeploymentSettings(target);
+ if (!inputFile.exists() || AndroidManager::isQtCreatorGenerated(inputFile))
+ return true;
+
+ const Project *p = target->project();
+
+ const Core::Context cmakeCtx = Core::Context(CMakeProjectManager::Constants::CMAKE_PROJECT_ID);
+ const bool isCmakeProject = p->projectContext() == cmakeCtx;
+ if (isCmakeProject)
+ return false; // CMake reports ProductType::Other for Android Apps
+
+ const ProjectNode *n = p->rootProjectNode()->findProjectNode([] (const ProjectNode *n) {
+ return n->productType() == ProductType::App;
+ });
+ return n == nullptr; // If no Application target found, then skip steps
+}
+
FilePath AndroidManager::manifestSourcePath(const Target *target)
{
if (const ProjectNode *node = currentProjectNode(target)) {
@@ -519,9 +542,9 @@ QString AndroidManager::androidNameForApiLevel(int x)
case 31:
return QLatin1String("Android 12.0 (S)");
case 32:
- return QLatin1String("Android 12L (API 32)");
+ return QLatin1String("Android 12L (Sv2, API 32)");
case 33:
- return QLatin1String("Android Tiramisu");
+ return QLatin1String("Android 13.0 (Tiramisu)");
default:
return Tr::tr("Unknown Android version. API Level: %1").arg(x);
}
@@ -597,7 +620,7 @@ bool AndroidManager::checkKeystorePassword(const FilePath &keystorePath,
const CommandLine cmd(AndroidConfigurations::currentConfig().keytoolPath(),
{"-list", "-keystore", keystorePath.toUserOutput(),
"--storepass", keystorePasswd});
- QtcProcess proc;
+ Process proc;
proc.setTimeoutS(10);
proc.setCommand(cmd);
proc.runBlocking(EventLoopMode::On);
@@ -617,7 +640,7 @@ bool AndroidManager::checkCertificatePassword(const FilePath &keystorePath,
else
arguments << certificatePasswd;
- QtcProcess proc;
+ Process proc;
proc.setTimeoutS(10);
proc.setCommand({AndroidConfigurations::currentConfig().keytoolPath(), arguments});
proc.runBlocking(EventLoopMode::On);
@@ -631,7 +654,7 @@ bool AndroidManager::checkCertificateExists(const FilePath &keystorePath,
QStringList arguments = { "-list", "-keystore", keystorePath.toUserOutput(),
"--storepass", keystorePasswd, "-alias", alias };
- QtcProcess proc;
+ Process proc;
proc.setTimeoutS(10);
proc.setCommand({AndroidConfigurations::currentConfig().keytoolPath(), arguments});
proc.runBlocking(EventLoopMode::On);
@@ -666,7 +689,7 @@ SdkToolResult AndroidManager::runCommand(const CommandLine &command,
const QByteArray &writeData, int timeoutS)
{
Android::SdkToolResult cmdResult;
- QtcProcess cmdProc;
+ Process cmdProc;
cmdProc.setTimeoutS(timeoutS);
cmdProc.setWriteData(writeData);
qCDebug(androidManagerLog) << "Running command (sync):" << command.toUserOutput();
diff --git a/src/plugins/android/androidmanager.h b/src/plugins/android/androidmanager.h
index d468ec3279..ec6aff85bc 100644
--- a/src/plugins/android/androidmanager.h
+++ b/src/plugins/android/androidmanager.h
@@ -82,6 +82,7 @@ public:
static bool matchedAbis(const QStringList &deviceAbis, const QStringList &appAbis);
static QString devicePreferredAbi(const QStringList &deviceAbis, const QStringList &appAbis);
static ProjectExplorer::Abi androidAbi2Abi(const QString &androidAbi);
+ static bool skipInstallationAndPackageSteps(const ProjectExplorer::Target *target);
static QString androidNameForApiLevel(int x);
diff --git a/src/plugins/android/androidmanifesteditorwidget.cpp b/src/plugins/android/androidmanifesteditorwidget.cpp
index 009a021943..9c6a616abe 100644
--- a/src/plugins/android/androidmanifesteditorwidget.cpp
+++ b/src/plugins/android/androidmanifesteditorwidget.cpp
@@ -19,8 +19,8 @@
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectnodes.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectwindow.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/kitinformation.h>
@@ -73,7 +73,7 @@ static bool checkPackageName(const QString &packageName)
static Target *androidTarget(const FilePath &fileName)
{
- for (Project *project : SessionManager::projects()) {
+ for (Project *project : ProjectManager::projects()) {
if (Target *target = project->activeTarget()) {
Kit *kit = target->kit();
if (DeviceTypeKitAspect::deviceTypeId(kit) == Android::Constants::ANDROID_DEVICE_TYPE
diff --git a/src/plugins/android/androidpackageinstallationstep.cpp b/src/plugins/android/androidpackageinstallationstep.cpp
index 3b8a83fa57..e29bac3476 100644
--- a/src/plugins/android/androidpackageinstallationstep.cpp
+++ b/src/plugins/android/androidpackageinstallationstep.cpp
@@ -22,7 +22,7 @@
#include <qtsupport/qtkitinformation.h>
#include <utils/hostosinfo.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QDir>
#include <QLoggingCategory>
@@ -122,6 +122,14 @@ void AndroidPackageInstallationStep::setupOutputFormatter(OutputFormatter *forma
void AndroidPackageInstallationStep::doRun()
{
+ if (AndroidManager::skipInstallationAndPackageSteps(target())) {
+ reportWarningOrError(Tr::tr("Product type is not an application, not running the "
+ "Make install step."),
+ Task::Warning);
+ emit finished(true);
+ return;
+ }
+
QString error;
for (const QString &dir : std::as_const(m_androidDirsToClean)) {
FilePath androidDir = FilePath::fromString(dir);
diff --git a/src/plugins/android/androidplugin.cpp b/src/plugins/android/androidplugin.cpp
index b8ff5dbe0b..916600c1df 100644
--- a/src/plugins/android/androidplugin.cpp
+++ b/src/plugins/android/androidplugin.cpp
@@ -34,7 +34,6 @@
#endif
#include <coreplugin/icore.h>
-#include <utils/checkablemessagebox.h>
#include <utils/infobar.h>
#include <languageclient/languageclientsettings.h>
@@ -45,11 +44,13 @@
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qtsupport/qtversionmanager.h>
+#include <QTimer>
+
using namespace ProjectExplorer;
using namespace ProjectExplorer::Constants;
@@ -69,17 +70,6 @@ public:
}
};
-class AndroidRunConfigurationFactory : public RunConfigurationFactory
-{
-public:
- AndroidRunConfigurationFactory()
- {
- registerRunConfiguration<Android::AndroidRunConfiguration>
- ("Qt4ProjectManager.AndroidRunConfiguration:");
- addSupportedTargetDeviceType(Android::Constants::ANDROID_DEVICE_TYPE);
- }
-};
-
class AndroidPluginPrivate : public QObject
{
public:
@@ -151,8 +141,7 @@ void AndroidPlugin::kitsRestored()
void AndroidPlugin::askUserAboutAndroidSetup()
{
- if (!Utils::CheckableMessageBox::shouldAskAgain(Core::ICore::settings(), kSetupAndroidSetting)
- || !Core::ICore::infoBar()->canInfoBeAdded(kSetupAndroidSetting))
+ if (!Core::ICore::infoBar()->canInfoBeAdded(kSetupAndroidSetting))
return;
Utils::InfoBarEntry
diff --git a/src/plugins/android/androidpotentialkit.cpp b/src/plugins/android/androidpotentialkit.cpp
index 914cbc0acd..4d95f485d0 100644
--- a/src/plugins/android/androidpotentialkit.cpp
+++ b/src/plugins/android/androidpotentialkit.cpp
@@ -8,23 +8,35 @@
#include <app/app_version.h>
-#include <utils/detailswidget.h>
-#include <utils/utilsicons.h>
-
#include <coreplugin/coreicons.h>
#include <coreplugin/icore.h>
+
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/kit.h>
#include <projectexplorer/kitinformation.h>
+
#include <qtsupport/qtversionmanager.h>
#include <qtsupport/baseqtversion.h>
+#include <utils/detailswidget.h>
+#include <utils/utilsicons.h>
+
#include <QGridLayout>
#include <QLabel>
#include <QPushButton>
-using namespace Android;
-using namespace Android::Internal;
+
+namespace Android::Internal {
+
+class AndroidPotentialKitWidget : public Utils::DetailsWidget
+{
+public:
+ AndroidPotentialKitWidget(QWidget *parent);
+
+private:
+ void openOptions();
+ void recheck();
+};
QString AndroidPotentialKit::displayName() const
{
@@ -102,3 +114,5 @@ void AndroidPotentialKitWidget::recheck()
}
}
}
+
+} // Android::Internal
diff --git a/src/plugins/android/androidpotentialkit.h b/src/plugins/android/androidpotentialkit.h
index 9e1ed41742..d111029475 100644
--- a/src/plugins/android/androidpotentialkit.h
+++ b/src/plugins/android/androidpotentialkit.h
@@ -4,14 +4,11 @@
#pragma once
#include <projectexplorer/ipotentialkit.h>
-#include <utils/detailswidget.h>
-namespace Android {
-namespace Internal {
+namespace Android::Internal {
class AndroidPotentialKit : public ProjectExplorer::IPotentialKit
{
- Q_OBJECT
public:
QString displayName() const override;
void executeFromMenu() override;
@@ -19,15 +16,4 @@ public:
bool isEnabled() const override;
};
-class AndroidPotentialKitWidget : public Utils::DetailsWidget
-{
- Q_OBJECT
-public:
- AndroidPotentialKitWidget(QWidget *parent);
-private:
- void openOptions();
- void recheck();
-};
-
-}
-}
+} // Android::Internal
diff --git a/src/plugins/android/androidqmlpreviewworker.cpp b/src/plugins/android/androidqmlpreviewworker.cpp
index ab9fb34de5..d296dfe677 100644
--- a/src/plugins/android/androidqmlpreviewworker.cpp
+++ b/src/plugins/android/androidqmlpreviewworker.cpp
@@ -27,8 +27,8 @@
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
+#include <utils/async.h>
+#include <utils/process.h>
#include <QDateTime>
#include <QDeadlineTimer>
@@ -117,7 +117,7 @@ private:
QStringList m_avdAbis;
int m_viewerPid = -1;
QFutureWatcher<void> m_pidFutureWatcher;
- Utils::QtcProcess m_logcatProcess;
+ Utils::Process m_logcatProcess;
QString m_logcatStartTimeStamp;
UploadInfo m_uploadInfo;
};
@@ -163,7 +163,7 @@ bool AndroidQmlPreviewWorker::isPreviewRunning(int lastKnownPid) const
void AndroidQmlPreviewWorker::startPidWatcher()
{
- m_pidFutureWatcher.setFuture(runAsync([this] {
+ m_pidFutureWatcher.setFuture(Utils::asyncRun([this] {
// wait for started
const int sleepTimeMs = 2000;
QDeadlineTimer deadline(20000);
@@ -226,7 +226,7 @@ AndroidQmlPreviewWorker::AndroidQmlPreviewWorker(RunControl *runControl)
connect(this, &AndroidQmlPreviewWorker::previewPidChanged,
this, &AndroidQmlPreviewWorker::startLogcat);
- connect(this, &RunWorker::stopped, &m_logcatProcess, &QtcProcess::stop);
+ connect(this, &RunWorker::stopped, &m_logcatProcess, &Process::stop);
m_logcatProcess.setStdOutCallback([this](const QString &stdOut) {
filterLogcatAndAppendMessage(stdOut);
});
@@ -376,7 +376,7 @@ FilePath AndroidQmlPreviewWorker::createQmlrcFile(const FilePath &workFolder,
{
const QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(m_rc->kit());
const FilePath rccBinary = qtVersion->rccFilePath();
- QtcProcess rccProcess;
+ Process rccProcess;
FilePath qrcPath = FilePath::fromString(basename + ".qrc4viewer");
const FilePath qmlrcPath = FilePath::fromString(QDir::tempPath()) / (basename + packageSuffix);
diff --git a/src/plugins/android/androidrunconfiguration.cpp b/src/plugins/android/androidrunconfiguration.cpp
index ef469da2b6..45f991ca74 100644
--- a/src/plugins/android/androidrunconfiguration.cpp
+++ b/src/plugins/android/androidrunconfiguration.cpp
@@ -18,8 +18,8 @@
#include <qtsupport/qtkitinformation.h>
#include <utils/detailswidget.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/utilsicons.h>
using namespace ProjectExplorer;
@@ -46,49 +46,59 @@ public:
}
};
-AndroidRunConfiguration::AndroidRunConfiguration(Target *target, Utils::Id id)
- : RunConfiguration(target, id)
+class AndroidRunConfiguration : public RunConfiguration
{
- auto envAspect = addAspect<EnvironmentAspect>();
- envAspect->addSupportedBaseEnvironment(Tr::tr("Clean Environment"), {});
-
- auto extraAppArgsAspect = addAspect<ArgumentsAspect>(macroExpander());
-
- connect(extraAppArgsAspect, &BaseAspect::changed, this, [target, extraAppArgsAspect] {
- if (target->buildConfigurations().first()->buildType() == BuildConfiguration::BuildType::Release) {
- const QString buildKey = target->activeBuildKey();
- target->buildSystem()->setExtraData(buildKey,
- Android::Constants::AndroidApplicationArgs,
- extraAppArgsAspect->arguments());
- }
- });
-
- auto amStartArgsAspect = addAspect<StringAspect>();
- amStartArgsAspect->setId(Constants::ANDROID_AM_START_ARGS);
- amStartArgsAspect->setSettingsKey("Android.AmStartArgsKey");
- amStartArgsAspect->setLabelText(Tr::tr("Activity manager start arguments:"));
- amStartArgsAspect->setDisplayStyle(StringAspect::LineEditDisplay);
- amStartArgsAspect->setHistoryCompleter("Android.AmStartArgs.History");
-
- auto preStartShellCmdAspect = addAspect<BaseStringListAspect>();
- preStartShellCmdAspect->setDisplayStyle(StringAspect::TextEditDisplay);
- preStartShellCmdAspect->setId(Constants::ANDROID_PRESTARTSHELLCMDLIST);
- preStartShellCmdAspect->setSettingsKey("Android.PreStartShellCmdListKey");
- preStartShellCmdAspect->setLabelText(Tr::tr("Pre-launch on-device shell commands:"));
-
- auto postStartShellCmdAspect = addAspect<BaseStringListAspect>();
- postStartShellCmdAspect->setDisplayStyle(StringAspect::TextEditDisplay);
- postStartShellCmdAspect->setId(Constants::ANDROID_POSTFINISHSHELLCMDLIST);
- postStartShellCmdAspect->setSettingsKey("Android.PostStartShellCmdListKey");
- postStartShellCmdAspect->setLabelText(Tr::tr("Post-quit on-device shell commands:"));
-
- setUpdater([this] {
- const BuildTargetInfo bti = buildTargetInfo();
- setDisplayName(bti.displayName);
- setDefaultDisplayName(bti.displayName);
- });
-
- connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update);
+public:
+ AndroidRunConfiguration(Target *target, Id id)
+ : RunConfiguration(target, id)
+ {
+ auto envAspect = addAspect<EnvironmentAspect>();
+ envAspect->addSupportedBaseEnvironment(Tr::tr("Clean Environment"), {});
+
+ auto extraAppArgsAspect = addAspect<ArgumentsAspect>(macroExpander());
+
+ connect(extraAppArgsAspect, &BaseAspect::changed, this, [target, extraAppArgsAspect] {
+ if (target->buildConfigurations().first()->buildType() == BuildConfiguration::BuildType::Release) {
+ const QString buildKey = target->activeBuildKey();
+ target->buildSystem()->setExtraData(buildKey,
+ Android::Constants::AndroidApplicationArgs,
+ extraAppArgsAspect->arguments());
+ }
+ });
+
+ auto amStartArgsAspect = addAspect<StringAspect>();
+ amStartArgsAspect->setId(Constants::ANDROID_AM_START_ARGS);
+ amStartArgsAspect->setSettingsKey("Android.AmStartArgsKey");
+ amStartArgsAspect->setLabelText(Tr::tr("Activity manager start arguments:"));
+ amStartArgsAspect->setDisplayStyle(StringAspect::LineEditDisplay);
+ amStartArgsAspect->setHistoryCompleter("Android.AmStartArgs.History");
+
+ auto preStartShellCmdAspect = addAspect<BaseStringListAspect>();
+ preStartShellCmdAspect->setDisplayStyle(StringAspect::TextEditDisplay);
+ preStartShellCmdAspect->setId(Constants::ANDROID_PRESTARTSHELLCMDLIST);
+ preStartShellCmdAspect->setSettingsKey("Android.PreStartShellCmdListKey");
+ preStartShellCmdAspect->setLabelText(Tr::tr("Pre-launch on-device shell commands:"));
+
+ auto postStartShellCmdAspect = addAspect<BaseStringListAspect>();
+ postStartShellCmdAspect->setDisplayStyle(StringAspect::TextEditDisplay);
+ postStartShellCmdAspect->setId(Constants::ANDROID_POSTFINISHSHELLCMDLIST);
+ postStartShellCmdAspect->setSettingsKey("Android.PostStartShellCmdListKey");
+ postStartShellCmdAspect->setLabelText(Tr::tr("Post-quit on-device shell commands:"));
+
+ setUpdater([this] {
+ const BuildTargetInfo bti = buildTargetInfo();
+ setDisplayName(bti.displayName);
+ setDefaultDisplayName(bti.displayName);
+ });
+
+ connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update);
+ }
+};
+
+AndroidRunConfigurationFactory::AndroidRunConfigurationFactory()
+{
+ registerRunConfiguration<AndroidRunConfiguration>("Qt4ProjectManager.AndroidRunConfiguration:");
+ addSupportedTargetDeviceType(Android::Constants::ANDROID_DEVICE_TYPE);
}
} // namespace Android
diff --git a/src/plugins/android/androidrunconfiguration.h b/src/plugins/android/androidrunconfiguration.h
index 57576c555a..9ad677aeef 100644
--- a/src/plugins/android/androidrunconfiguration.h
+++ b/src/plugins/android/androidrunconfiguration.h
@@ -9,11 +9,10 @@
namespace Android {
-class ANDROID_EXPORT AndroidRunConfiguration : public ProjectExplorer::RunConfiguration
+class AndroidRunConfigurationFactory : public ProjectExplorer::RunConfigurationFactory
{
- Q_OBJECT
public:
- explicit AndroidRunConfiguration(ProjectExplorer::Target *target, Utils::Id id);
+ AndroidRunConfigurationFactory();
};
} // namespace Android
diff --git a/src/plugins/android/androidruncontrol.h b/src/plugins/android/androidruncontrol.h
index a466de7226..f96c19443b 100644
--- a/src/plugins/android/androidruncontrol.h
+++ b/src/plugins/android/androidruncontrol.h
@@ -3,14 +3,10 @@
#pragma once
-#include "androidrunner.h"
-
-#include <projectexplorer/runconfiguration.h>
+#include <projectexplorer/runcontrol.h>
namespace Android::Internal {
-class AndroidRunner;
-
class AndroidRunWorkerFactory final : public ProjectExplorer::RunWorkerFactory
{
public:
diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp
index 2676fa6305..65ef869558 100644
--- a/src/plugins/android/androidrunnerworker.cpp
+++ b/src/plugins/android/androidrunnerworker.cpp
@@ -11,8 +11,8 @@
#include <debugger/debuggerrunconfigurationaspect.h>
#include <projectexplorer/buildconfiguration.h>
-#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/environmentaspect.h>
+#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/runconfigurationaspects.h>
#include <projectexplorer/runcontrol.h>
#include <projectexplorer/target.h>
@@ -20,10 +20,10 @@
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
+#include <utils/async.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
+#include <utils/process.h>
#include <utils/stringutils.h>
#include <utils/temporaryfile.h>
#include <utils/url.h>
@@ -78,8 +78,10 @@ static qint64 extractPID(const QString &output, const QString &packageName)
return pid;
}
-static void findProcessPIDAndUser(QFutureInterface<PidUserPair> &fi, QStringList selector,
- const QString &packageName, bool preNougat)
+static void findProcessPIDAndUser(QPromise<PidUserPair> &promise,
+ QStringList selector,
+ const QString &packageName,
+ bool preNougat)
{
if (packageName.isEmpty())
return;
@@ -96,7 +98,7 @@ static void findProcessPIDAndUser(QFutureInterface<PidUserPair> &fi, QStringList
chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now();
do {
QThread::msleep(200);
- QtcProcess proc;
+ Process proc;
proc.setCommand({adbPath, args});
proc.runBlocking();
const QString out = proc.allOutput();
@@ -106,16 +108,16 @@ static void findProcessPIDAndUser(QFutureInterface<PidUserPair> &fi, QStringList
if (!out.isEmpty())
processPID = out.trimmed().toLongLong();
}
- } while ((processPID == -1 || processPID == 0) && !isTimedOut(start) && !fi.isCanceled());
+ } while ((processPID == -1 || processPID == 0) && !isTimedOut(start) && !promise.isCanceled());
qCDebug(androidRunWorkerLog) << "PID found:" << processPID << ", PreNougat:" << preNougat;
qint64 processUser = 0;
- if (processPID > 0 && !fi.isCanceled()) {
+ if (processPID > 0 && !promise.isCanceled()) {
args = {selector};
args.append({"shell", "ps", "-o", "user", "-p"});
args.append(QString::number(processPID));
- QtcProcess proc;
+ Process proc;
proc.setCommand({adbPath, args});
proc.runBlocking();
const QString out = proc.allOutput();
@@ -133,8 +135,8 @@ static void findProcessPIDAndUser(QFutureInterface<PidUserPair> &fi, QStringList
qCDebug(androidRunWorkerLog) << "USER found:" << processUser;
- if (!fi.isCanceled())
- fi.reportResult(PidUserPair(processPID, processUser));
+ if (!promise.isCanceled())
+ promise.addResult(PidUserPair(processPID, processUser));
}
static void deleter(QProcess *p)
@@ -724,8 +726,11 @@ void AndroidRunnerWorker::asyncStart()
{
asyncStartHelper();
- m_pidFinder = Utils::onResultReady(Utils::runAsync(findProcessPIDAndUser, selector(),
- m_packageName, m_isPreNougat),
+ m_pidFinder = Utils::onResultReady(Utils::asyncRun(findProcessPIDAndUser,
+ selector(),
+ m_packageName,
+ m_isPreNougat),
+ this,
bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1));
}
diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp
index 4494f4837f..1839f7c21c 100644
--- a/src/plugins/android/androidsdkmanager.cpp
+++ b/src/plugins/android/androidsdkmanager.cpp
@@ -8,9 +8,9 @@
#include "sdkmanageroutputparser.h"
#include <utils/algorithm.h>
+#include <utils/async.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <utils/stringutils.h>
#include <QFutureWatcher>
@@ -34,7 +34,7 @@ namespace Internal {
const int sdkManagerCmdTimeoutS = 60;
const int sdkManagerOperationTimeoutS = 600;
-using SdkCmdFutureInterface = QFutureInterface<AndroidSdkManager::OperationOutput>;
+using SdkCmdPromise = QPromise<AndroidSdkManager::OperationOutput>;
static const QRegularExpression &assertionRegExp()
{
@@ -93,7 +93,7 @@ static bool sdkManagerCommand(const AndroidConfig &config, const QStringList &ar
qCDebug(sdkManagerLog).noquote() << "Running SDK Manager command (sync):"
<< CommandLine(config.sdkManagerToolPath(), newArgs)
.toUserOutput();
- QtcProcess proc;
+ Process proc;
proc.setEnvironment(AndroidConfigurations::toolsEnvironment(config));
proc.setTimeoutS(timeout);
proc.setTimeOutMessageBoxEnabled(true);
@@ -111,7 +111,7 @@ static bool sdkManagerCommand(const AndroidConfig &config, const QStringList &ar
after the lapse of \a timeout seconds. The function blocks the calling thread.
*/
static void sdkManagerCommand(const AndroidConfig &config, const QStringList &args,
- AndroidSdkManager &sdkManager, SdkCmdFutureInterface &fi,
+ AndroidSdkManager &sdkManager, SdkCmdPromise &promise,
AndroidSdkManager::OperationOutput &output, double progressQuota,
bool interruptible = true, int timeout = sdkManagerOperationTimeoutS)
{
@@ -120,19 +120,19 @@ static void sdkManagerCommand(const AndroidConfig &config, const QStringList &ar
qCDebug(sdkManagerLog).noquote() << "Running SDK Manager command (async):"
<< CommandLine(config.sdkManagerToolPath(), newArgs)
.toUserOutput();
- int offset = fi.progressValue();
- QtcProcess proc;
+ int offset = promise.future().progressValue();
+ Process proc;
proc.setEnvironment(AndroidConfigurations::toolsEnvironment(config));
bool assertionFound = false;
proc.setTimeoutS(timeout);
- proc.setStdOutCallback([offset, progressQuota, &proc, &assertionFound, &fi](const QString &out) {
+ proc.setStdOutCallback([offset, progressQuota, &proc, &assertionFound, &promise](const QString &out) {
int progressPercent = parseProgress(out, assertionFound);
if (assertionFound) {
proc.stop();
proc.waitForFinished();
}
if (progressPercent != -1)
- fi.setProgressValue(offset + qRound((progressPercent / 100.0) * progressQuota));
+ promise.setProgressValue(offset + qRound((progressPercent / 100.0) * progressQuota));
});
proc.setStdErrCallback([&output](const QString &err) {
output.stdError = err;
@@ -168,12 +168,12 @@ public:
const AndroidSdkPackageList &allPackages(bool forceUpdate = false);
void refreshSdkPackages(bool forceReload = false);
- void parseCommonArguments(QFutureInterface<QString> &fi);
- void updateInstalled(SdkCmdFutureInterface &fi);
- void update(SdkCmdFutureInterface &fi, const QStringList &install,
+ void parseCommonArguments(QPromise<QString> &promise);
+ void updateInstalled(SdkCmdPromise &fi);
+ void update(SdkCmdPromise &fi, const QStringList &install,
const QStringList &uninstall);
- void checkPendingLicense(SdkCmdFutureInterface &fi);
- void getPendingLicense(SdkCmdFutureInterface &fi);
+ void checkPendingLicense(SdkCmdPromise &fi);
+ void getPendingLicense(SdkCmdPromise &fi);
void addWatcher(const QFuture<AndroidSdkManager::OperationOutput> &future);
void setLicenseInput(bool acceptLicense);
@@ -186,7 +186,7 @@ private:
void reloadSdkPackages();
void clearPackages();
bool onLicenseStdOut(const QString &output, bool notify,
- AndroidSdkManager::OperationOutput &result, SdkCmdFutureInterface &fi);
+ AndroidSdkManager::OperationOutput &result, SdkCmdPromise &fi);
AndroidSdkManager &m_sdkManager;
const AndroidConfig &m_config;
@@ -308,7 +308,7 @@ bool AndroidSdkManager::packageListingSuccessful() const
QFuture<QString> AndroidSdkManager::availableArguments() const
{
- return Utils::runAsync(&AndroidSdkManagerPrivate::parseCommonArguments, m_d.get());
+ return Utils::asyncRun(&AndroidSdkManagerPrivate::parseCommonArguments, m_d.get());
}
QFuture<AndroidSdkManager::OperationOutput> AndroidSdkManager::updateAll()
@@ -316,7 +316,7 @@ QFuture<AndroidSdkManager::OperationOutput> AndroidSdkManager::updateAll()
if (isBusy()) {
return QFuture<AndroidSdkManager::OperationOutput>();
}
- auto future = Utils::runAsync(&AndroidSdkManagerPrivate::updateInstalled, m_d.get());
+ auto future = Utils::asyncRun(&AndroidSdkManagerPrivate::updateInstalled, m_d.get());
m_d->addWatcher(future);
return future;
}
@@ -326,7 +326,7 @@ AndroidSdkManager::update(const QStringList &install, const QStringList &uninsta
{
if (isBusy())
return QFuture<AndroidSdkManager::OperationOutput>();
- auto future = Utils::runAsync(&AndroidSdkManagerPrivate::update, m_d.get(), install, uninstall);
+ auto future = Utils::asyncRun(&AndroidSdkManagerPrivate::update, m_d.get(), install, uninstall);
m_d->addWatcher(future);
return future;
}
@@ -335,7 +335,7 @@ QFuture<AndroidSdkManager::OperationOutput> AndroidSdkManager::checkPendingLicen
{
if (isBusy())
return QFuture<AndroidSdkManager::OperationOutput>();
- auto future = Utils::runAsync(&AndroidSdkManagerPrivate::checkPendingLicense, m_d.get());
+ auto future = Utils::asyncRun(&AndroidSdkManagerPrivate::checkPendingLicense, m_d.get());
m_d->addWatcher(future);
return future;
}
@@ -344,7 +344,7 @@ QFuture<AndroidSdkManager::OperationOutput> AndroidSdkManager::runLicenseCommand
{
if (isBusy())
return QFuture<AndroidSdkManager::OperationOutput>();
- auto future = Utils::runAsync(&AndroidSdkManagerPrivate::getPendingLicense, m_d.get());
+ auto future = Utils::asyncRun(&AndroidSdkManagerPrivate::getPendingLicense, m_d.get());
m_d->addWatcher(future);
return future;
}
@@ -422,29 +422,29 @@ void AndroidSdkManagerPrivate::refreshSdkPackages(bool forceReload)
reloadSdkPackages();
}
-void AndroidSdkManagerPrivate::updateInstalled(SdkCmdFutureInterface &fi)
+void AndroidSdkManagerPrivate::updateInstalled(SdkCmdPromise &promise)
{
- fi.setProgressRange(0, 100);
- fi.setProgressValue(0);
+ promise.setProgressRange(0, 100);
+ promise.setProgressValue(0);
AndroidSdkManager::OperationOutput result;
result.type = AndroidSdkManager::UpdateAll;
result.stdOutput = Tr::tr("Updating installed packages.");
- fi.reportResult(result);
+ promise.addResult(result);
QStringList args("--update");
args << m_config.sdkManagerToolArgs();
- if (!fi.isCanceled())
- sdkManagerCommand(m_config, args, m_sdkManager, fi, result, 100);
+ if (!promise.isCanceled())
+ sdkManagerCommand(m_config, args, m_sdkManager, promise, result, 100);
else
qCDebug(sdkManagerLog) << "Update: Operation cancelled before start";
if (result.stdError.isEmpty() && !result.success)
result.stdError = Tr::tr("Failed.");
result.stdOutput = Tr::tr("Done\n\n");
- fi.reportResult(result);
- fi.setProgressValue(100);
+ promise.addResult(result);
+ promise.setProgressValue(100);
}
-void AndroidSdkManagerPrivate::update(SdkCmdFutureInterface &fi, const QStringList &install,
+void AndroidSdkManagerPrivate::update(SdkCmdPromise &fi, const QStringList &install,
const QStringList &uninstall)
{
fi.setProgressRange(0, 100);
@@ -461,7 +461,7 @@ void AndroidSdkManagerPrivate::update(SdkCmdFutureInterface &fi, const QStringLi
result.type = AndroidSdkManager::UpdatePackage;
result.stdOutput = QString("%1 %2").arg(isInstall ? installTag : uninstallTag)
.arg(packagePath);
- fi.reportResult(result);
+ fi.addResult(result);
if (fi.isCanceled())
qCDebug(sdkManagerLog) << args << "Update: Operation cancelled before start";
else
@@ -471,7 +471,7 @@ void AndroidSdkManagerPrivate::update(SdkCmdFutureInterface &fi, const QStringLi
if (result.stdError.isEmpty() && !result.success)
result.stdError = Tr::tr("AndroidSdkManager", "Failed");
result.stdOutput = Tr::tr("AndroidSdkManager", "Done\n\n");
- fi.reportResult(result);
+ fi.addResult(result);
return fi.isCanceled();
};
@@ -495,7 +495,7 @@ void AndroidSdkManagerPrivate::update(SdkCmdFutureInterface &fi, const QStringLi
fi.setProgressValue(100);
}
-void AndroidSdkManagerPrivate::checkPendingLicense(SdkCmdFutureInterface &fi)
+void AndroidSdkManagerPrivate::checkPendingLicense(SdkCmdPromise &fi)
{
fi.setProgressRange(0, 100);
fi.setProgressValue(0);
@@ -509,11 +509,11 @@ void AndroidSdkManagerPrivate::checkPendingLicense(SdkCmdFutureInterface &fi)
qCDebug(sdkManagerLog) << "Update: Operation cancelled before start";
}
- fi.reportResult(result);
+ fi.addResult(result);
fi.setProgressValue(100);
}
-void AndroidSdkManagerPrivate::getPendingLicense(SdkCmdFutureInterface &fi)
+void AndroidSdkManagerPrivate::getPendingLicense(SdkCmdPromise &fi)
{
fi.setProgressRange(0, 100);
fi.setProgressValue(0);
@@ -521,7 +521,7 @@ void AndroidSdkManagerPrivate::getPendingLicense(SdkCmdFutureInterface &fi)
AndroidSdkManager::OperationOutput result;
result.type = AndroidSdkManager::LicenseWorkflow;
- QtcProcess licenseCommand;
+ Process licenseCommand;
licenseCommand.setProcessMode(ProcessMode::Writer);
licenseCommand.setEnvironment(AndroidConfigurations::toolsEnvironment(m_config));
bool reviewingLicenses = false;
@@ -549,7 +549,7 @@ void AndroidSdkManagerPrivate::getPendingLicense(SdkCmdFutureInterface &fi)
} else if (assertionFound) {
// The first assertion is to start reviewing licenses. Always accept.
reviewingLicenses = true;
- QRegularExpression reg("(\\d+\\sof\\s)(?<steps>\\d+)");
+ static const QRegularExpression reg(R"((\d+\sof\s)(?<steps>\d+))");
QRegularExpressionMatch match = reg.match(stdOut);
if (match.hasMatch())
steps = match.captured("steps").toInt();
@@ -571,7 +571,7 @@ void AndroidSdkManagerPrivate::getPendingLicense(SdkCmdFutureInterface &fi)
result.success = licenseCommand.exitStatus() == QProcess::NormalExit;
if (!result.success)
result.stdError = Tr::tr("License command failed.\n\n");
- fi.reportResult(result);
+ fi.addResult(result);
fi.setProgressValue(100);
}
@@ -595,14 +595,14 @@ void AndroidSdkManagerPrivate::clearUserInput()
bool AndroidSdkManagerPrivate::onLicenseStdOut(const QString &output, bool notify,
AndroidSdkManager::OperationOutput &result,
- SdkCmdFutureInterface &fi)
+ SdkCmdPromise &fi)
{
m_licenseTextCache.append(output);
const QRegularExpressionMatch assertionMatch = assertionRegExp().match(m_licenseTextCache);
if (assertionMatch.hasMatch()) {
if (notify) {
result.stdOutput = m_licenseTextCache;
- fi.reportResult(result);
+ fi.addResult(result);
}
// Clear the current contents. The found license text is dispatched. Continue collecting the
// next license text.
@@ -620,7 +620,7 @@ void AndroidSdkManagerPrivate::addWatcher(const QFuture<AndroidSdkManager::Opera
m_activeOperation->setFuture(QFuture<void>(future));
}
-void AndroidSdkManagerPrivate::parseCommonArguments(QFutureInterface<QString> &fi)
+void AndroidSdkManagerPrivate::parseCommonArguments(QPromise<QString> &promise)
{
QString argumentDetails;
QString output;
@@ -628,7 +628,7 @@ void AndroidSdkManagerPrivate::parseCommonArguments(QFutureInterface<QString> &f
bool foundTag = false;
const auto lines = output.split('\n');
for (const QString& line : lines) {
- if (fi.isCanceled())
+ if (promise.isCanceled())
break;
if (foundTag)
argumentDetails.append(line + "\n");
@@ -636,8 +636,8 @@ void AndroidSdkManagerPrivate::parseCommonArguments(QFutureInterface<QString> &f
foundTag = true;
}
- if (!fi.isCanceled())
- fi.reportResult(argumentDetails);
+ if (!promise.isCanceled())
+ promise.addResult(argumentDetails);
}
void AndroidSdkManagerPrivate::clearPackages()
diff --git a/src/plugins/android/androidsdkmanagerwidget.cpp b/src/plugins/android/androidsdkmanagerwidget.cpp
index c2b1ce6d6b..8627e0c64e 100644
--- a/src/plugins/android/androidsdkmanagerwidget.cpp
+++ b/src/plugins/android/androidsdkmanagerwidget.cpp
@@ -9,10 +9,10 @@
#include <app/app_version.h>
+#include <utils/async.h>
#include <utils/layoutbuilder.h>
#include <utils/outputformatter.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <utils/utilsicons.h>
#include <QAbstractButton>
@@ -141,14 +141,16 @@ AndroidSdkManagerWidget::AndroidSdkManagerWidget(AndroidConfig &config,
}
},
optionsButton
- }
- }.attachTo(m_packagesStack, WithoutMargins);
+ },
+ noMargin
+ }.attachTo(m_packagesStack);
Column {
m_outputEdit,
Row { m_sdkLicenseLabel, m_sdkLicenseButtonBox },
m_operationProgress,
- }.attachTo(m_outputStack, WithoutMargins);
+ noMargin
+ }.attachTo(m_outputStack);
Column {
m_viewStack,
@@ -644,7 +646,7 @@ OptionsDialog::OptionsDialog(AndroidSdkManager *sdkManager, const QStringList &a
}
};
m_optionsFuture = sdkManager->availableArguments();
- Utils::onResultReady(m_optionsFuture, populateOptions);
+ Utils::onResultReady(m_optionsFuture, this, populateOptions);
auto dialogButtons = new QDialogButtonBox(this);
dialogButtons->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp
index ae2d28ae1a..dfb14c49cf 100644
--- a/src/plugins/android/androidsettingswidget.cpp
+++ b/src/plugins/android/androidsettingswidget.cpp
@@ -17,8 +17,8 @@
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
#include <utils/progressindicator.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/utilsicons.h>
#include <QCheckBox>
@@ -145,8 +145,6 @@ public:
~AndroidSettingsWidget() final;
private:
- void apply() final { AndroidConfigurations::setConfig(m_androidConfig); }
-
void showEvent(QShowEvent *event) override;
void validateJdk();
@@ -450,6 +448,8 @@ AndroidSettingsWidget::AndroidSettingsWidget()
delete openSslOneShot;
});
});
+
+ setOnApply([this] { AndroidConfigurations::setConfig(m_androidConfig); });
}
AndroidSettingsWidget::~AndroidSettingsWidget()
@@ -659,7 +659,7 @@ void AndroidSettingsWidget::downloadOpenSslRepo(const bool silent)
openSslProgressDialog->setFixedSize(openSslProgressDialog->sizeHint());
const QString openSslRepo("https://github.com/KDAB/android_openssl.git");
- QtcProcess *gitCloner = new QtcProcess(this);
+ Process *gitCloner = new Process(this);
CommandLine gitCloneCommand("git", {"clone", "--depth=1", openSslRepo, openSslPath.toString()});
gitCloner->setCommand(gitCloneCommand);
@@ -684,7 +684,7 @@ void AndroidSettingsWidget::downloadOpenSslRepo(const bool silent)
openButton->deleteLater();
};
- connect(gitCloner, &QtcProcess::done, this, [=] {
+ connect(gitCloner, &Process::done, this, [=] {
openSslProgressDialog->close();
if (gitCloner->error() != QProcess::UnknownError) {
if (gitCloner->error() == QProcess::FailedToStart) {
@@ -718,7 +718,7 @@ void AndroidSettingsWidget::updateUI()
const bool openSslOk = m_openSslSummary->allRowsOk();
const QListWidgetItem *currentItem = m_ndkListWidget->currentItem();
- const FilePath currentNdk = FilePath::fromString(currentItem ? currentItem->text() : "");
+ const FilePath currentNdk = FilePath::fromUserInput(currentItem ? currentItem->text() : "");
const QString infoText = Tr::tr("(SDK Version: %1, NDK Version: %2)")
.arg(m_androidConfig.sdkToolsVersion().toString())
.arg(currentNdk.isEmpty() ? "" : m_androidConfig.ndkVersion(currentNdk).toString());
diff --git a/src/plugins/android/androidsignaloperation.cpp b/src/plugins/android/androidsignaloperation.cpp
index 426b63c144..b6a7699dd4 100644
--- a/src/plugins/android/androidsignaloperation.cpp
+++ b/src/plugins/android/androidsignaloperation.cpp
@@ -4,8 +4,8 @@
#include "androidconfigurations.h"
#include "androidsignaloperation.h"
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
using namespace Utils;
@@ -90,8 +90,8 @@ void AndroidSignalOperation::startAdbProcess(State state, const Utils::CommandLi
{
m_state = state;
m_timeout->start();
- m_adbProcess.reset(new QtcProcess);
- connect(m_adbProcess.get(), &QtcProcess::done, this, handler);
+ m_adbProcess.reset(new Process);
+ connect(m_adbProcess.get(), &Process::done, this, handler);
m_adbProcess->setCommand(commandLine);
m_adbProcess->start();
}
diff --git a/src/plugins/android/androidsignaloperation.h b/src/plugins/android/androidsignaloperation.h
index 06a88b6705..1c3bb48454 100644
--- a/src/plugins/android/androidsignaloperation.h
+++ b/src/plugins/android/androidsignaloperation.h
@@ -42,7 +42,7 @@ private:
void startAdbProcess(State state, const Utils::CommandLine &commandLine, FinishHandler handler);
Utils::FilePath m_adbPath;
- std::unique_ptr<Utils::QtcProcess> m_adbProcess;
+ std::unique_ptr<Utils::Process> m_adbProcess;
QTimer *m_timeout;
State m_state = Idle;
diff --git a/src/plugins/autotest/autotest.qbs b/src/plugins/autotest/autotest.qbs
index f0bccd79c3..90f1728c4a 100644
--- a/src/plugins/autotest/autotest.qbs
+++ b/src/plugins/autotest/autotest.qbs
@@ -125,9 +125,7 @@ QtcPlugin {
]
}
- Group {
- name: "Test sources"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
"autotestunittests.cpp",
"autotestunittests.h",
diff --git a/src/plugins/autotest/autotestplugin.cpp b/src/plugins/autotest/autotestplugin.cpp
index 98b804b16d..d6bfc6f9a8 100644
--- a/src/plugins/autotest/autotestplugin.cpp
+++ b/src/plugins/autotest/autotestplugin.cpp
@@ -32,22 +32,28 @@
#include <coreplugin/icontext.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
+
#include <cplusplus/CppDocument.h>
#include <cplusplus/LookupContext.h>
#include <cplusplus/Overview.h>
+
#include <cppeditor/cppeditorconstants.h>
#include <cppeditor/cppmodelmanager.h>
+
#include <extensionsystem/pluginmanager.h>
+
#include <projectexplorer/buildmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorericons.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectpanelfactory.h>
#include <projectexplorer/runcontrol.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
+
#include <texteditor/textdocument.h>
#include <texteditor/texteditor.h>
+
#include <utils/algorithm.h>
#include <utils/textutils.h>
#include <utils/utilsicons.h>
@@ -90,7 +96,7 @@ public:
void onRunUnderCursorTriggered(TestRunMode mode);
TestSettings m_settings;
- TestSettingsPage m_testSettingPage{&m_settings};
+ TestSettingsPage m_testSettingPage;
TestCodeParser m_testCodeParser;
TestTreeModel m_testTreeModel{&m_testCodeParser};
@@ -148,11 +154,11 @@ AutotestPluginPrivate::AutotestPluginPrivate()
m_testTreeModel.synchronizeTestFrameworks();
m_testTreeModel.synchronizeTestTools();
- auto sessionManager = ProjectExplorer::SessionManager::instance();
- connect(sessionManager, &ProjectExplorer::SessionManager::startupProjectChanged,
+ auto sessionManager = ProjectExplorer::ProjectManager::instance();
+ connect(sessionManager, &ProjectExplorer::ProjectManager::startupProjectChanged,
this, [this] { m_runconfigCache.clear(); });
- connect(sessionManager, &ProjectExplorer::SessionManager::aboutToRemoveProject,
+ connect(sessionManager, &ProjectExplorer::ProjectManager::aboutToRemoveProject,
this, [](ProjectExplorer::Project *project) {
const auto it = s_projectSettings.constFind(project);
if (it != s_projectSettings.constEnd()) {
@@ -172,11 +178,6 @@ AutotestPluginPrivate::~AutotestPluginPrivate()
delete m_resultsPane;
}
-TestSettings *AutotestPlugin::settings()
-{
- return &dd->m_settings;
-}
-
TestProjectSettings *AutotestPlugin::projectSettings(ProjectExplorer::Project *project)
{
auto &settings = s_projectSettings[project];
@@ -471,7 +472,7 @@ void AutotestPluginPrivate::onRunUnderCursorTriggered(TestRunMode mode)
TestFrameworks AutotestPlugin::activeTestFrameworks()
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
TestFrameworks sorted;
if (!project || projectSettings(project)->useGlobalSettings()) {
sorted = Utils::filtered(TestFrameworkManager::registeredFrameworks(),
@@ -489,7 +490,7 @@ TestFrameworks AutotestPlugin::activeTestFrameworks()
void AutotestPlugin::updateMenuItemsEnabledState()
{
- const ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ const ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
const ProjectExplorer::Target *target = project ? project->activeTarget() : nullptr;
const bool canScan = !dd->m_testRunner.isTestRunning()
&& dd->m_testCodeParser.state() == TestCodeParser::Idle;
diff --git a/src/plugins/autotest/autotestplugin.h b/src/plugins/autotest/autotestplugin.h
index 8705a8a538..e6daa0c739 100644
--- a/src/plugins/autotest/autotestplugin.h
+++ b/src/plugins/autotest/autotestplugin.h
@@ -18,7 +18,6 @@ namespace Autotest {
namespace Internal {
class TestProjectSettings;
-struct TestSettings;
struct ChoicePair
{
@@ -43,7 +42,6 @@ public:
void extensionsInitialized() override;
ShutdownFlag aboutToShutdown() override;
- static TestSettings *settings();
static TestProjectSettings *projectSettings(ProjectExplorer::Project *project);
static TestFrameworks activeTestFrameworks();
static void updateMenuItemsEnabledState();
diff --git a/src/plugins/autotest/autotestunittests.cpp b/src/plugins/autotest/autotestunittests.cpp
index e72cc0cbbb..50d0b496c2 100644
--- a/src/plugins/autotest/autotestunittests.cpp
+++ b/src/plugins/autotest/autotestunittests.cpp
@@ -99,8 +99,8 @@ void AutoTestUnitTests::testCodeParser()
CppEditor::Tests::ProjectOpenerAndCloser projectManager;
QVERIFY(projectManager.open(projectFilePath, true, m_kit));
- QSignalSpy parserSpy(m_model->parser(), SIGNAL(parsingFinished()));
- QSignalSpy modelUpdateSpy(m_model, SIGNAL(sweepingDone()));
+ QSignalSpy parserSpy(m_model->parser(), &TestCodeParser::parsingFinished);
+ QSignalSpy modelUpdateSpy(m_model, &TestTreeModel::sweepingDone);
QVERIFY(parserSpy.wait(20000));
QVERIFY(modelUpdateSpy.wait());
@@ -149,8 +149,8 @@ void AutoTestUnitTests::testCodeParserSwitchStartup()
qDebug() << "Opening project" << projectFilePaths.at(i);
QVERIFY(projectManager.open(projectFilePaths.at(i), true, m_kit));
- QSignalSpy parserSpy(m_model->parser(), SIGNAL(parsingFinished()));
- QSignalSpy modelUpdateSpy(m_model, SIGNAL(sweepingDone()));
+ QSignalSpy parserSpy(m_model->parser(), &TestCodeParser::parsingFinished);
+ QSignalSpy modelUpdateSpy(m_model, &TestTreeModel::sweepingDone);
QVERIFY(parserSpy.wait(20000));
QVERIFY(modelUpdateSpy.wait());
@@ -199,8 +199,8 @@ void AutoTestUnitTests::testCodeParserGTest()
CppEditor::Tests::ProjectOpenerAndCloser projectManager;
QVERIFY(projectManager.open(projectFilePath, true, m_kit));
- QSignalSpy parserSpy(m_model->parser(), SIGNAL(parsingFinished()));
- QSignalSpy modelUpdateSpy(m_model, SIGNAL(sweepingDone()));
+ QSignalSpy parserSpy(m_model->parser(), &TestCodeParser::parsingFinished);
+ QSignalSpy modelUpdateSpy(m_model, &TestTreeModel::sweepingDone);
QVERIFY(parserSpy.wait(20000));
QVERIFY(modelUpdateSpy.wait());
@@ -250,8 +250,8 @@ void AutoTestUnitTests::testCodeParserBoostTest()
= projectManager.open(projectFilePath, true, m_kit);
QVERIFY(projectInfo);
- QSignalSpy parserSpy(m_model->parser(), SIGNAL(parsingFinished()));
- QSignalSpy modelUpdateSpy(m_model, SIGNAL(sweepingDone()));
+ QSignalSpy parserSpy(m_model->parser(), &TestCodeParser::parsingFinished);
+ QSignalSpy modelUpdateSpy(m_model, &TestTreeModel::sweepingDone);
QVERIFY(parserSpy.wait(20000));
QVERIFY(modelUpdateSpy.wait());
diff --git a/src/plugins/autotest/boost/boosttestconfiguration.cpp b/src/plugins/autotest/boost/boosttestconfiguration.cpp
index 50d8a863f4..e581cff50e 100644
--- a/src/plugins/autotest/boost/boosttestconfiguration.cpp
+++ b/src/plugins/autotest/boost/boosttestconfiguration.cpp
@@ -6,23 +6,20 @@
#include "boosttestoutputreader.h"
#include "boosttestsettings.h"
-#include "../autotestplugin.h"
#include "../itestframework.h"
#include "../testsettings.h"
#include <utils/algorithm.h>
-#include <utils/stringutils.h>
using namespace Utils;
namespace Autotest {
namespace Internal {
-TestOutputReader *BoostTestConfiguration::createOutputReader(
- const QFutureInterface<TestResult> &fi, QtcProcess *app) const
+TestOutputReader *BoostTestConfiguration::createOutputReader(Process *app) const
{
auto settings = static_cast<BoostTestSettings *>(framework()->testSettings());
- return new BoostTestOutputReader(fi, app, buildDirectory(), projectFile(),
+ return new BoostTestOutputReader(app, buildDirectory(), projectFile(),
LogLevel(settings->logLevel.value()),
ReportLevel(settings->reportLevel.value()));
}
@@ -108,7 +105,7 @@ QStringList BoostTestConfiguration::argumentsForTestRunner(QStringList *omitted)
for (const QString &test : testCases())
arguments << "-t" << test;
- if (AutotestPlugin::settings()->processArgs) {
+ if (TestSettings::instance()->processArgs()) {
arguments << filterInterfering(runnable().command.arguments().split(
' ', Qt::SkipEmptyParts), omitted);
}
diff --git a/src/plugins/autotest/boost/boosttestconfiguration.h b/src/plugins/autotest/boost/boosttestconfiguration.h
index 8afb74049f..10e50004bc 100644
--- a/src/plugins/autotest/boost/boosttestconfiguration.h
+++ b/src/plugins/autotest/boost/boosttestconfiguration.h
@@ -13,8 +13,7 @@ class BoostTestConfiguration : public DebuggableTestConfiguration
public:
explicit BoostTestConfiguration(ITestFramework *framework)
: DebuggableTestConfiguration(framework) {}
- TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
- Utils::QtcProcess *app) const override;
+ TestOutputReader *createOutputReader(Utils::Process *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};
diff --git a/src/plugins/autotest/boost/boosttestframework.h b/src/plugins/autotest/boost/boosttestframework.h
index e8f4c98ed8..da015fbd6f 100644
--- a/src/plugins/autotest/boost/boosttestframework.h
+++ b/src/plugins/autotest/boost/boosttestframework.h
@@ -7,8 +7,7 @@
#include "boosttestsettings.h"
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
class BoostTestFramework : public ITestFramework
{
@@ -23,9 +22,7 @@ private:
ITestParser *createTestParser() override;
ITestTreeItem *createRootNode() override;
- BoostTestSettings m_settings;
- BoostTestSettingsPage m_settingsPage{&m_settings, settingsId()};
+ BoostTestSettings m_settings{settingsId()};
};
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/boost/boosttestoutputreader.cpp b/src/plugins/autotest/boost/boosttestoutputreader.cpp
index 5f549cca32..96b0dffd61 100644
--- a/src/plugins/autotest/boost/boosttestoutputreader.cpp
+++ b/src/plugins/autotest/boost/boosttestoutputreader.cpp
@@ -5,11 +5,12 @@
#include "boosttestsettings.h"
#include "boosttestresult.h"
+
#include "../autotesttr.h"
#include "../testtreeitem.h"
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QLoggingCategory>
#include <QRegularExpression>
@@ -21,12 +22,11 @@ namespace Internal {
static Q_LOGGING_CATEGORY(orLog, "qtc.autotest.boost.outputreader", QtWarningMsg)
-BoostTestOutputReader::BoostTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
- QtcProcess *testApplication,
+BoostTestOutputReader::BoostTestOutputReader(Process *testApplication,
const FilePath &buildDirectory,
const FilePath &projectFile,
LogLevel log, ReportLevel report)
- : TestOutputReader(futureInterface, testApplication, buildDirectory)
+ : TestOutputReader(testApplication, buildDirectory)
, m_projectFile(projectFile)
, m_logLevel(log)
, m_reportLevel(report)
@@ -34,7 +34,7 @@ BoostTestOutputReader::BoostTestOutputReader(const QFutureInterface<TestResult>
if (!testApplication)
return;
- connect(testApplication, &QtcProcess::done, this, [this, testApplication] {
+ connect(testApplication, &Process::done, this, [this, testApplication] {
onDone(testApplication->exitCode());
});
}
diff --git a/src/plugins/autotest/boost/boosttestoutputreader.h b/src/plugins/autotest/boost/boosttestoutputreader.h
index 8449a0708a..648b55547f 100644
--- a/src/plugins/autotest/boost/boosttestoutputreader.h
+++ b/src/plugins/autotest/boost/boosttestoutputreader.h
@@ -15,8 +15,7 @@ class BoostTestOutputReader : public TestOutputReader
{
Q_OBJECT
public:
- BoostTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
- Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
+ BoostTestOutputReader(Utils::Process *testApplication, const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile, LogLevel log, ReportLevel report);
protected:
void processOutputLine(const QByteArray &outputLine) override;
diff --git a/src/plugins/autotest/boost/boosttestparser.cpp b/src/plugins/autotest/boost/boosttestparser.cpp
index bbf6dfd56c..8c701942ad 100644
--- a/src/plugins/autotest/boost/boosttestparser.cpp
+++ b/src/plugins/autotest/boost/boosttestparser.cpp
@@ -9,6 +9,7 @@
#include <cppeditor/cppmodelmanager.h>
#include <QMap>
+#include <QPromise>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
@@ -96,7 +97,7 @@ static BoostTestParseResult *createParseResult(const QString &name, const FilePa
}
-bool BoostTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+bool BoostTestParser::processDocument(QPromise<TestParseResultPtr> &promise,
const FilePath &fileName)
{
CPlusPlus::Document::Ptr doc = document(fileName);
@@ -148,7 +149,7 @@ bool BoostTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futu
locationAndType.m_type,
tmpInfo);
currentSuite->children.append(funcResult);
- futureInterface.reportResult(TestParseResultPtr(topLevelSuite));
+ promise.addResult(TestParseResultPtr(topLevelSuite));
}
}
return true;
diff --git a/src/plugins/autotest/boost/boosttestparser.h b/src/plugins/autotest/boost/boosttestparser.h
index 049f42d0c9..3ea67815c9 100644
--- a/src/plugins/autotest/boost/boosttestparser.h
+++ b/src/plugins/autotest/boost/boosttestparser.h
@@ -22,7 +22,7 @@ class BoostTestParser : public CppParser
{
public:
explicit BoostTestParser(ITestFramework *framework) : CppParser(framework) {}
- bool processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+ bool processDocument(QPromise<TestParseResultPtr> &promise,
const Utils::FilePath &fileName) override;
};
diff --git a/src/plugins/autotest/boost/boosttestsettings.cpp b/src/plugins/autotest/boost/boosttestsettings.cpp
index c995cab9b6..ed0bad6bca 100644
--- a/src/plugins/autotest/boost/boosttestsettings.cpp
+++ b/src/plugins/autotest/boost/boosttestsettings.cpp
@@ -10,17 +10,29 @@
#include <utils/layoutbuilder.h>
+using namespace Layouting;
using namespace Utils;
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
-BoostTestSettings::BoostTestSettings()
+BoostTestSettings::BoostTestSettings(Id settingsId)
{
+ setId(settingsId);
+ setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
+ setDisplayName(Tr::tr(BoostTest::Constants::FRAMEWORK_SETTINGS_CATEGORY));
setSettingsGroups("Autotest", "BoostTest");
- setAutoApply(false);
- registerAspect(&logLevel);
+ setLayouter([this] {
+ return Row { Form {
+ logLevel, br,
+ reportLevel, br,
+ randomize, Row { seed }, br,
+ systemErrors, br,
+ fpExceptions, br,
+ memLeaks,
+ }, st};
+ });
+
logLevel.setSettingsKey("LogLevel");
logLevel.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
logLevel.addOption("All");
@@ -37,7 +49,6 @@ BoostTestSettings::BoostTestSettings()
logLevel.setDefaultValue(int(LogLevel::Warning));
logLevel.setLabelText(Tr::tr("Log format:"));
- registerAspect(&reportLevel);
reportLevel.setSettingsKey("ReportLevel");
reportLevel.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
reportLevel.addOption("Confirm");
@@ -47,64 +58,35 @@ BoostTestSettings::BoostTestSettings()
reportLevel.setDefaultValue(int(ReportLevel::Confirm));
reportLevel.setLabelText(Tr::tr("Report level:"));
- registerAspect(&seed);
seed.setSettingsKey("Seed");
seed.setEnabled(false);
seed.setLabelText(Tr::tr("Seed:"));
seed.setToolTip(Tr::tr("A seed of 0 means no randomization. A value of 1 uses the current "
- "time, any other value is used as random seed generator."));
+ "time, any other value is used as random seed generator."));
seed.setEnabler(&randomize);
- registerAspect(&randomize);
randomize.setSettingsKey("Randomize");
- randomize.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ randomize.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox);
randomize.setLabelText(Tr::tr("Randomize"));
randomize.setToolTip(Tr::tr("Randomize execution order."));
- registerAspect(&systemErrors);
systemErrors.setSettingsKey("SystemErrors");
- systemErrors.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ systemErrors.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox);
systemErrors.setLabelText(Tr::tr("Catch system errors"));
systemErrors.setToolTip(Tr::tr("Catch or ignore system errors."));
- registerAspect(&fpExceptions);
fpExceptions.setSettingsKey("FPExceptions");
- fpExceptions.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ fpExceptions.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox);
fpExceptions.setLabelText(Tr::tr("Floating point exceptions"));
fpExceptions.setToolTip(Tr::tr("Enable floating point exception traps."));
- registerAspect(&memLeaks);
memLeaks.setSettingsKey("MemoryLeaks");
- memLeaks.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ memLeaks.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox);
memLeaks.setDefaultValue(true);
memLeaks.setLabelText(Tr::tr("Detect memory leaks"));
memLeaks.setToolTip(Tr::tr("Enable memory leak detection."));
}
-BoostTestSettingsPage::BoostTestSettingsPage(BoostTestSettings *settings, Id settingsId)
-{
- setId(settingsId);
- setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
- setDisplayName(Tr::tr(BoostTest::Constants::FRAMEWORK_SETTINGS_CATEGORY));
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- BoostTestSettings &s = *settings;
- using namespace Layouting;
-
- Grid grid {
- s.logLevel, br,
- s.reportLevel, br,
- s.randomize, Row { s.seed }, br,
- s.systemErrors, br,
- s.fpExceptions, br,
- s.memLeaks,
- };
-
- Column { Row { Column { grid, st }, st } }.attachTo(widget);
- });
-}
-
QString BoostTestSettings::logLevelToOption(const LogLevel logLevel)
{
switch (logLevel) {
@@ -134,5 +116,4 @@ QString BoostTestSettings::reportLevelToOption(const ReportLevel reportLevel)
return {};
}
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/boost/boosttestsettings.h b/src/plugins/autotest/boost/boosttestsettings.h
index 60a3df5042..e95f98218c 100644
--- a/src/plugins/autotest/boost/boosttestsettings.h
+++ b/src/plugins/autotest/boost/boosttestsettings.h
@@ -5,10 +5,7 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
-
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
enum class LogLevel
{
@@ -33,32 +30,24 @@ enum class ReportLevel
No
};
-class BoostTestSettings : public Utils::AspectContainer
+class BoostTestSettings : public Core::PagedSettings
{
public:
- BoostTestSettings();
+ explicit BoostTestSettings(Utils::Id settingsId);
static QString logLevelToOption(const LogLevel logLevel);
static QString reportLevelToOption(const ReportLevel reportLevel);
- Utils::SelectionAspect logLevel;
- Utils::SelectionAspect reportLevel;
- Utils::IntegerAspect seed;
- Utils::BoolAspect randomize;
- Utils::BoolAspect systemErrors;
- Utils::BoolAspect fpExceptions;
- Utils::BoolAspect memLeaks;
-};
-
-
-class BoostTestSettingsPage final : public Core::IOptionsPage
-{
-public:
- BoostTestSettingsPage(BoostTestSettings *settings, Utils::Id settingsId);
+ Utils::SelectionAspect logLevel{this};
+ Utils::SelectionAspect reportLevel{this};
+ Utils::IntegerAspect seed{this};
+ Utils::BoolAspect randomize{this};
+ Utils::BoolAspect systemErrors{this};
+ Utils::BoolAspect fpExceptions{this};
+ Utils::BoolAspect memLeaks{this};
};
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
Q_DECLARE_METATYPE(Autotest::Internal::LogLevel)
Q_DECLARE_METATYPE(Autotest::Internal::ReportLevel)
diff --git a/src/plugins/autotest/boost/boosttesttreeitem.cpp b/src/plugins/autotest/boost/boosttesttreeitem.cpp
index 4b53d92754..957ff73021 100644
--- a/src/plugins/autotest/boost/boosttesttreeitem.cpp
+++ b/src/plugins/autotest/boost/boosttesttreeitem.cpp
@@ -11,7 +11,9 @@
#include "../itestframework.h"
#include <cppeditor/cppmodelmanager.h>
-#include <projectexplorer/session.h>
+
+#include <projectexplorer/projectmanager.h>
+
#include <utils/qtcassert.h>
#include <QRegularExpression>
@@ -156,7 +158,7 @@ static QString handleSpecialFunctionNames(const QString &name)
QList<ITestConfiguration *> BoostTestTreeItem::getAllTestConfigurations() const
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
@@ -200,7 +202,7 @@ QList<ITestConfiguration *> BoostTestTreeItem::getTestConfigurations(
std::function<bool(BoostTestTreeItem *)> predicate) const
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
@@ -261,7 +263,7 @@ QList<ITestConfiguration *> BoostTestTreeItem::getFailedTestConfigurations() con
ITestConfiguration *BoostTestTreeItem::testConfiguration() const
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(project, return nullptr);
const auto cppMM = CppEditor::CppModelManager::instance();
QTC_ASSERT(cppMM, return nullptr);
diff --git a/src/plugins/autotest/catch/catchconfiguration.cpp b/src/plugins/autotest/catch/catchconfiguration.cpp
index c08f7ac82b..a503639868 100644
--- a/src/plugins/autotest/catch/catchconfiguration.cpp
+++ b/src/plugins/autotest/catch/catchconfiguration.cpp
@@ -2,24 +2,21 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "catchconfiguration.h"
+
#include "catchoutputreader.h"
#include "catchtestsettings.h"
-#include "../autotestplugin.h"
#include "../itestframework.h"
#include "../testsettings.h"
-#include <utils/stringutils.h>
-
using namespace Utils;
namespace Autotest {
namespace Internal {
-TestOutputReader *CatchConfiguration::createOutputReader(const QFutureInterface<TestResult> &fi,
- QtcProcess *app) const
+TestOutputReader *CatchConfiguration::createOutputReader(Process *app) const
{
- return new CatchOutputReader(fi, app, buildDirectory(), projectFile());
+ return new CatchOutputReader(app, buildDirectory(), projectFile());
}
static QStringList filterInterfering(const QStringList &provided, QStringList *omitted)
@@ -82,7 +79,7 @@ QStringList CatchConfiguration::argumentsForTestRunner(QStringList *omitted) con
arguments << "\"" + testCases().join("\", \"") + "\"";
arguments << "--reporter" << "xml";
- if (AutotestPlugin::settings()->processArgs) {
+ if (TestSettings::instance()->processArgs()) {
arguments << filterInterfering(runnable().command.arguments().split(
' ', Qt::SkipEmptyParts), omitted);
}
diff --git a/src/plugins/autotest/catch/catchconfiguration.h b/src/plugins/autotest/catch/catchconfiguration.h
index bfa37f0164..63ac84dba7 100644
--- a/src/plugins/autotest/catch/catchconfiguration.h
+++ b/src/plugins/autotest/catch/catchconfiguration.h
@@ -12,8 +12,7 @@ class CatchConfiguration : public DebuggableTestConfiguration
{
public:
CatchConfiguration(ITestFramework *framework) : DebuggableTestConfiguration(framework) {}
- TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
- Utils::QtcProcess *app) const override;
+ TestOutputReader *createOutputReader(Utils::Process *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};
diff --git a/src/plugins/autotest/catch/catchframework.h b/src/plugins/autotest/catch/catchframework.h
index 0fe7138ce9..6bd20b44f6 100644
--- a/src/plugins/autotest/catch/catchframework.h
+++ b/src/plugins/autotest/catch/catchframework.h
@@ -7,8 +7,7 @@
#include "catchtestsettings.h"
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
class CatchFramework : public ITestFramework
{
@@ -25,9 +24,7 @@ protected:
private:
ITestSettings * testSettings() override { return &m_settings; }
- CatchTestSettings m_settings;
- CatchTestSettingsPage m_settingsPage{&m_settings, settingsId()};
+ CatchTestSettings m_settings{settingsId()};
};
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/catch/catchoutputreader.cpp b/src/plugins/autotest/catch/catchoutputreader.cpp
index 95e437fdc4..7a3aa277d4 100644
--- a/src/plugins/autotest/catch/catchoutputreader.cpp
+++ b/src/plugins/autotest/catch/catchoutputreader.cpp
@@ -2,13 +2,11 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "catchoutputreader.h"
+
#include "catchresult.h"
#include "../autotesttr.h"
-#include <utils/fileutils.h>
-#include <utils/qtcassert.h>
-
using namespace Utils;
namespace Autotest {
@@ -31,11 +29,10 @@ namespace CatchXml {
const char TestCaseResultElement[] = "OverallResult";
}
-CatchOutputReader::CatchOutputReader(const QFutureInterface<TestResult> &futureInterface,
- QtcProcess *testApplication,
+CatchOutputReader::CatchOutputReader(Process *testApplication,
const FilePath &buildDirectory,
const FilePath &projectFile)
- : TestOutputReader (futureInterface, testApplication, buildDirectory)
+ : TestOutputReader(testApplication, buildDirectory)
, m_projectFile(projectFile)
{
}
diff --git a/src/plugins/autotest/catch/catchoutputreader.h b/src/plugins/autotest/catch/catchoutputreader.h
index 51e8c1e338..b46db333f8 100644
--- a/src/plugins/autotest/catch/catchoutputreader.h
+++ b/src/plugins/autotest/catch/catchoutputreader.h
@@ -14,8 +14,7 @@ namespace Internal {
class CatchOutputReader : public TestOutputReader
{
public:
- CatchOutputReader(const QFutureInterface<TestResult> &futureInterface,
- Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
+ CatchOutputReader(Utils::Process *testApplication, const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile);
protected:
diff --git a/src/plugins/autotest/catch/catchtestparser.cpp b/src/plugins/autotest/catch/catchtestparser.cpp
index dd01c01c90..3cc7a4540b 100644
--- a/src/plugins/autotest/catch/catchtestparser.cpp
+++ b/src/plugins/autotest/catch/catchtestparser.cpp
@@ -11,6 +11,7 @@
#include <cppeditor/projectpart.h>
#include <utils/qtcassert.h>
+#include <QPromise>
#include <QRegularExpression>
using namespace Utils;
@@ -91,7 +92,7 @@ static bool hasCatchNames(const CPlusPlus::Document::Ptr &document)
return false;
}
-bool CatchTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+bool CatchTestParser::processDocument(QPromise<TestParseResultPtr> &promise,
const FilePath &fileName)
{
CPlusPlus::Document::Ptr doc = document(fileName);
@@ -144,7 +145,7 @@ bool CatchTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futu
parseResult->children.append(testCase);
}
- futureInterface.reportResult(TestParseResultPtr(parseResult));
+ promise.addResult(TestParseResultPtr(parseResult));
return !foundTests.isEmpty();
}
diff --git a/src/plugins/autotest/catch/catchtestparser.h b/src/plugins/autotest/catch/catchtestparser.h
index 6159ff5813..8b72073204 100644
--- a/src/plugins/autotest/catch/catchtestparser.h
+++ b/src/plugins/autotest/catch/catchtestparser.h
@@ -23,7 +23,7 @@ class CatchTestParser : public CppParser
public:
CatchTestParser(ITestFramework *framework)
: CppParser(framework) {}
- bool processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+ bool processDocument(QPromise<TestParseResultPtr> &promise,
const Utils::FilePath &fileName) override;
};
diff --git a/src/plugins/autotest/catch/catchtestsettings.cpp b/src/plugins/autotest/catch/catchtestsettings.cpp
index 6fd0cde16d..5ee4a6acf9 100644
--- a/src/plugins/autotest/catch/catchtestsettings.cpp
+++ b/src/plugins/autotest/catch/catchtestsettings.cpp
@@ -6,141 +6,105 @@
#include "../autotestconstants.h"
#include "../autotesttr.h"
-#include <coreplugin/icore.h>
-
#include <utils/layoutbuilder.h>
+using namespace Layouting;
using namespace Utils;
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
-CatchTestSettings::CatchTestSettings()
+CatchTestSettings::CatchTestSettings(Id settingsId)
{
+ setId(settingsId);
+ setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
+ setDisplayName(Tr::tr("Catch Test"));
setSettingsGroups("Autotest", "Catch2");
- setAutoApply(false);
- registerAspect(&abortAfter);
+ setLayouter([this] {
+ return Row { Form {
+ showSuccess, br,
+ breakOnFailure, br,
+ noThrow, br,
+ visibleWhitespace, br,
+ abortAfterChecked, abortAfter, br,
+ samplesChecked, benchmarkSamples, br,
+ resamplesChecked, benchmarkResamples, br,
+ confidenceIntervalChecked, confidenceInterval, br,
+ warmupChecked, benchmarkWarmupTime, br,
+ noAnalysis
+ }, st };
+ });
+
abortAfter.setSettingsKey("AbortAfter");
abortAfter.setRange(1, 9999);
abortAfter.setEnabler(&abortAfterChecked);
- registerAspect(&benchmarkSamples);
benchmarkSamples.setSettingsKey("BenchSamples");
benchmarkSamples.setRange(1, 999999);
benchmarkSamples.setDefaultValue(100);
benchmarkSamples.setEnabler(&samplesChecked);
- registerAspect(&benchmarkResamples);
benchmarkResamples.setSettingsKey("BenchResamples");
benchmarkResamples.setRange(1, 9999999);
benchmarkResamples.setDefaultValue(100000);
benchmarkResamples.setToolTip(Tr::tr("Number of resamples for bootstrapping."));
benchmarkResamples.setEnabler(&resamplesChecked);
- registerAspect(&confidenceInterval);
confidenceInterval.setSettingsKey("BenchConfInt");
confidenceInterval.setRange(0., 1.);
confidenceInterval.setSingleStep(0.05);
confidenceInterval.setDefaultValue(0.95);
confidenceInterval.setEnabler(&confidenceIntervalChecked);
- registerAspect(&benchmarkWarmupTime);
benchmarkWarmupTime.setSettingsKey("BenchWarmup");
benchmarkWarmupTime.setSuffix(Tr::tr(" ms"));
benchmarkWarmupTime.setRange(0, 10000);
benchmarkWarmupTime.setEnabler(&warmupChecked);
- registerAspect(&abortAfterChecked);
abortAfterChecked.setSettingsKey("AbortChecked");
abortAfterChecked.setLabelText(Tr::tr("Abort after"));
abortAfterChecked.setToolTip(Tr::tr("Aborts after the specified number of failures."));
- registerAspect(&samplesChecked);
samplesChecked.setSettingsKey("SamplesChecked");
samplesChecked.setLabelText(Tr::tr("Benchmark samples"));
samplesChecked.setToolTip(Tr::tr("Number of samples to collect while running benchmarks."));
- registerAspect(&resamplesChecked);
resamplesChecked.setSettingsKey("ResamplesChecked");
resamplesChecked.setLabelText(Tr::tr("Benchmark resamples"));
resamplesChecked.setToolTip(Tr::tr("Number of resamples used for statistical bootstrapping."));
- registerAspect(&confidenceIntervalChecked);
confidenceIntervalChecked.setSettingsKey("ConfIntChecked");
confidenceIntervalChecked.setToolTip(Tr::tr("Confidence interval used for statistical bootstrapping."));
confidenceIntervalChecked.setLabelText(Tr::tr("Benchmark confidence interval"));
- registerAspect(&warmupChecked);
warmupChecked.setSettingsKey("WarmupChecked");
warmupChecked.setLabelText(Tr::tr("Benchmark warmup time"));
warmupChecked.setToolTip(Tr::tr("Warmup time for each test."));
- registerAspect(&noAnalysis);
noAnalysis.setSettingsKey("NoAnalysis");
noAnalysis.setLabelText(Tr::tr("Disable analysis"));
noAnalysis.setToolTip(Tr::tr("Disables statistical analysis and bootstrapping."));
- registerAspect(&showSuccess);
showSuccess.setSettingsKey("ShowSuccess");
showSuccess.setLabelText(Tr::tr("Show success"));
showSuccess.setToolTip(Tr::tr("Show success for tests."));
- registerAspect(&breakOnFailure);
breakOnFailure.setSettingsKey("BreakOnFailure");
breakOnFailure.setDefaultValue(true);
breakOnFailure.setLabelText(Tr::tr("Break on failure while debugging"));
breakOnFailure.setToolTip(Tr::tr("Turns failures into debugger breakpoints."));
- registerAspect(&noThrow);
noThrow.setSettingsKey("NoThrow");
noThrow.setLabelText(Tr::tr("Skip throwing assertions"));
noThrow.setToolTip(Tr::tr("Skips all assertions that test for thrown exceptions."));
- registerAspect(&visibleWhitespace);
visibleWhitespace.setSettingsKey("VisibleWS");
visibleWhitespace.setLabelText(Tr::tr("Visualize whitespace"));
visibleWhitespace.setToolTip(Tr::tr("Makes whitespace visible."));
- registerAspect(&warnOnEmpty);
warnOnEmpty.setSettingsKey("WarnEmpty");
warnOnEmpty.setLabelText(Tr::tr("Warn on empty tests"));
warnOnEmpty.setToolTip(Tr::tr("Warns if a test section does not check any assertion."));
-
- forEachAspect([](BaseAspect *aspect) {
- // FIXME: Make the positioning part of the LayoutBuilder later
- if (auto boolAspect = dynamic_cast<BoolAspect *>(aspect))
- boolAspect->setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
- });
-}
-
-CatchTestSettingsPage::CatchTestSettingsPage(CatchTestSettings *settings, Id settingsId)
-{
- setId(settingsId);
- setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
- setDisplayName(Tr::tr("Catch Test"));
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- CatchTestSettings &s = *settings;
- using namespace Layouting;
-
- Grid col {
- s.showSuccess, br,
- s.breakOnFailure, br,
- s.noThrow, br,
- s.visibleWhitespace, br,
- s.abortAfterChecked, s.abortAfter, br,
- s.samplesChecked, s.benchmarkSamples, br,
- s.resamplesChecked, s.benchmarkResamples, br,
- s.confidenceIntervalChecked, s.confidenceInterval, br,
- s.warmupChecked, s.benchmarkWarmupTime, br,
- s.noAnalysis
- };
-
- Column { Row { col, st }, st }.attachTo(widget);
- });
}
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/catch/catchtestsettings.h b/src/plugins/autotest/catch/catchtestsettings.h
index 9e1b639fd3..71c1caf583 100644
--- a/src/plugins/autotest/catch/catchtestsettings.h
+++ b/src/plugins/autotest/catch/catchtestsettings.h
@@ -5,39 +5,29 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
+namespace Autotest::Internal {
-namespace Autotest {
-namespace Internal {
-
-class CatchTestSettings : public Utils::AspectContainer
-{
-public:
- CatchTestSettings();
-
- Utils::IntegerAspect abortAfter;
- Utils::IntegerAspect benchmarkSamples;
- Utils::IntegerAspect benchmarkResamples;
- Utils::DoubleAspect confidenceInterval;
- Utils::IntegerAspect benchmarkWarmupTime;
- Utils::BoolAspect abortAfterChecked;
- Utils::BoolAspect samplesChecked;
- Utils::BoolAspect resamplesChecked;
- Utils::BoolAspect confidenceIntervalChecked;
- Utils::BoolAspect warmupChecked;
- Utils::BoolAspect noAnalysis;
- Utils::BoolAspect showSuccess;
- Utils::BoolAspect breakOnFailure;
- Utils::BoolAspect noThrow;
- Utils::BoolAspect visibleWhitespace;
- Utils::BoolAspect warnOnEmpty;
-};
-
-class CatchTestSettingsPage : public Core::IOptionsPage
+class CatchTestSettings : public Core::PagedSettings
{
public:
- CatchTestSettingsPage(CatchTestSettings *settings, Utils::Id settingsId);
+ explicit CatchTestSettings(Utils::Id settingsId);
+
+ Utils::IntegerAspect abortAfter{this};
+ Utils::IntegerAspect benchmarkSamples{this};
+ Utils::IntegerAspect benchmarkResamples{this};
+ Utils::DoubleAspect confidenceInterval{this};
+ Utils::IntegerAspect benchmarkWarmupTime{this};
+ Utils::BoolAspect abortAfterChecked{this};
+ Utils::BoolAspect samplesChecked{this};
+ Utils::BoolAspect resamplesChecked{this};
+ Utils::BoolAspect confidenceIntervalChecked{this};
+ Utils::BoolAspect warmupChecked{this};
+ Utils::BoolAspect noAnalysis{this};
+ Utils::BoolAspect showSuccess{this};
+ Utils::BoolAspect breakOnFailure{this};
+ Utils::BoolAspect noThrow{this};
+ Utils::BoolAspect visibleWhitespace{this};
+ Utils::BoolAspect warnOnEmpty{this};
};
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/catch/catchtreeitem.cpp b/src/plugins/autotest/catch/catchtreeitem.cpp
index fe69fece87..034c7ab1d0 100644
--- a/src/plugins/autotest/catch/catchtreeitem.cpp
+++ b/src/plugins/autotest/catch/catchtreeitem.cpp
@@ -10,8 +10,10 @@
#include "../itestframework.h"
#include <cppeditor/cppmodelmanager.h>
+
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
+
#include <utils/qtcassert.h>
using namespace Utils;
@@ -30,7 +32,7 @@ static QString nonRootDisplayName(const CatchTreeItem *it)
{
if (it->type() != TestTreeItem::TestSuite)
return it->name();
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project)
return it->name();
TestTreeItem *parent = it->parentItem();
@@ -141,7 +143,7 @@ bool CatchTreeItem::canProvideDebugConfiguration() const
ITestConfiguration *CatchTreeItem::testConfiguration() const
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(project, return nullptr);
const auto cppMM = CppEditor::CppModelManager::instance();
QTC_ASSERT(cppMM, return nullptr);
@@ -244,7 +246,7 @@ QList<ITestConfiguration *> CatchTreeItem::getSelectedTestConfigurations() const
QList<ITestConfiguration *> CatchTreeItem::getFailedTestConfigurations() const
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
@@ -271,7 +273,7 @@ QList<ITestConfiguration *> CatchTreeItem::getTestConfigurationsForFile(const Fi
const auto cppMM = CppEditor::CppModelManager::instance();
QTC_ASSERT(cppMM, return result);
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
@@ -293,7 +295,7 @@ QList<ITestConfiguration *> CatchTreeItem::getTestConfigurationsForFile(const Fi
testConfig = new CatchConfiguration(framework());
testConfig->setTestCases(testCases);
testConfig->setProjectFile(item->proFile());
- testConfig->setProject(ProjectExplorer::SessionManager::startupProject());
+ testConfig->setProject(ProjectExplorer::ProjectManager::startupProject());
testConfig->setInternalTargets(cppMM->internalTargets(item->filePath()));
result << testConfig;
}
@@ -314,7 +316,7 @@ QString CatchTreeItem::stateSuffix() const
QList<ITestConfiguration *> CatchTreeItem::getTestConfigurations(bool ignoreCheckState) const
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
diff --git a/src/plugins/autotest/ctest/ctestconfiguration.cpp b/src/plugins/autotest/ctest/ctestconfiguration.cpp
index 9208888033..a2e2edfe0a 100644
--- a/src/plugins/autotest/ctest/ctestconfiguration.cpp
+++ b/src/plugins/autotest/ctest/ctestconfiguration.cpp
@@ -13,10 +13,9 @@ CTestConfiguration::CTestConfiguration(ITestBase *testBase)
setDisplayName("CTest");
}
-TestOutputReader *CTestConfiguration::createOutputReader(const QFutureInterface<TestResult> &fi,
- Utils::QtcProcess *app) const
+TestOutputReader *CTestConfiguration::createOutputReader(Utils::Process *app) const
{
- return new CTestOutputReader(fi, app, workingDirectory());
+ return new CTestOutputReader(app, workingDirectory());
}
} // namespace Internal
diff --git a/src/plugins/autotest/ctest/ctestconfiguration.h b/src/plugins/autotest/ctest/ctestconfiguration.h
index db0efb1717..a298fec86d 100644
--- a/src/plugins/autotest/ctest/ctestconfiguration.h
+++ b/src/plugins/autotest/ctest/ctestconfiguration.h
@@ -13,8 +13,7 @@ class CTestConfiguration final : public Autotest::TestToolConfiguration
public:
explicit CTestConfiguration(ITestBase *testBase);
- TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
- Utils::QtcProcess *app) const final;
+ TestOutputReader *createOutputReader(Utils::Process *app) const final;
};
} // namespace Internal
diff --git a/src/plugins/autotest/ctest/ctestoutputreader.cpp b/src/plugins/autotest/ctest/ctestoutputreader.cpp
index 70ec5f0659..10f48d9d46 100644
--- a/src/plugins/autotest/ctest/ctestoutputreader.cpp
+++ b/src/plugins/autotest/ctest/ctestoutputreader.cpp
@@ -5,13 +5,11 @@
#include "../autotesttr.h"
#include "../testframeworkmanager.h"
-#include "../testresult.h"
#include "../testtreeitem.h"
#include <cmakeprojectmanager/cmakeprojectconstants.h>
#include <utils/qtcassert.h>
-#include <utils/treemodel.h>
#include <QRegularExpression>
@@ -52,10 +50,9 @@ public:
{}
};
-CTestOutputReader::CTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
- QtcProcess *testApplication,
+CTestOutputReader::CTestOutputReader(Process *testApplication,
const FilePath &buildDirectory)
- : TestOutputReader(futureInterface, testApplication, buildDirectory)
+ : TestOutputReader(testApplication, buildDirectory)
{
}
diff --git a/src/plugins/autotest/ctest/ctestoutputreader.h b/src/plugins/autotest/ctest/ctestoutputreader.h
index 8a6e1f124e..85785d44fc 100644
--- a/src/plugins/autotest/ctest/ctestoutputreader.h
+++ b/src/plugins/autotest/ctest/ctestoutputreader.h
@@ -5,7 +5,7 @@
#include "../testoutputreader.h"
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace Autotest {
namespace Internal {
@@ -13,8 +13,7 @@ namespace Internal {
class CTestOutputReader final : public Autotest::TestOutputReader
{
public:
- CTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
- Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory);
+ CTestOutputReader(Utils::Process *testApplication, const Utils::FilePath &buildDirectory);
protected:
void processOutputLine(const QByteArray &outputLineWithNewLine) final;
diff --git a/src/plugins/autotest/ctest/ctestsettings.cpp b/src/plugins/autotest/ctest/ctestsettings.cpp
index 5de02b8480..a2f3e90427 100644
--- a/src/plugins/autotest/ctest/ctestsettings.cpp
+++ b/src/plugins/autotest/ctest/ctestsettings.cpp
@@ -8,70 +8,85 @@
#include <utils/layoutbuilder.h>
-namespace Autotest {
-namespace Internal {
+using namespace Layouting;
+using namespace Utils;
-CTestSettings::CTestSettings()
+namespace Autotest::Internal {
+
+CTestSettings::CTestSettings(Id settingsId)
{
setSettingsGroups("Autotest", "CTest");
- setAutoApply(false);
+ setId(settingsId);
+ setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
+ setDisplayName(Tr::tr("CTest"));
+
+ setLayouter([this] {
+ return Row { Form {
+ outputOnFail, br,
+ scheduleRandom, br,
+ stopOnFailure, br,
+ outputMode, br,
+ Group {
+ title(Tr::tr("Repeat tests")),
+ repeat.groupChecker(),
+ Row { repetitionMode, repetitionCount},
+ }, br,
+ Group {
+ title(Tr::tr("Run in parallel")),
+ parallel.groupChecker(),
+ Column {
+ Row { jobs }, br,
+ Row { testLoad, threshold}
+ }
+ }
+ }, st };
+ });
- registerAspect(&outputOnFail);
outputOnFail.setSettingsKey("OutputOnFail");
outputOnFail.setLabelText(Tr::tr("Output on failure"));
outputOnFail.setDefaultValue(true);
- registerAspect(&outputMode);
outputMode.setSettingsKey("OutputMode");
outputMode.setLabelText(Tr::tr("Output mode"));
- outputMode.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
+ outputMode.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
outputMode.addOption({Tr::tr("Default"), {}, 0});
outputMode.addOption({Tr::tr("Verbose"), {}, 1});
outputMode.addOption({Tr::tr("Very Verbose"), {}, 2});
- registerAspect(&repetitionMode);
repetitionMode.setSettingsKey("RepetitionMode");
repetitionMode.setLabelText(Tr::tr("Repetition mode"));
- repetitionMode.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
+ repetitionMode.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
repetitionMode.addOption({Tr::tr("Until Fail"), {}, 0});
repetitionMode.addOption({Tr::tr("Until Pass"), {}, 1});
repetitionMode.addOption({Tr::tr("After Timeout"), {}, 2});
- registerAspect(&repetitionCount);
repetitionCount.setSettingsKey("RepetitionCount");
repetitionCount.setDefaultValue(1);
repetitionCount.setLabelText(Tr::tr("Count"));
repetitionCount.setToolTip(Tr::tr("Number of re-runs for the test."));
repetitionCount.setRange(1, 10000);
- registerAspect(&repeat);
repeat.setSettingsKey("Repeat");
- registerAspect(&scheduleRandom);
scheduleRandom.setSettingsKey("ScheduleRandom");
scheduleRandom.setLabelText(Tr::tr("Schedule random"));
- registerAspect(&stopOnFailure);
stopOnFailure.setSettingsKey("StopOnFail");
stopOnFailure.setLabelText(Tr::tr("Stop on failure"));
- registerAspect(&parallel);
parallel.setSettingsKey("Parallel");
parallel.setToolTip(Tr::tr("Run tests in parallel mode using given number of jobs."));
- registerAspect(&jobs);
jobs.setSettingsKey("Jobs");
jobs.setLabelText(Tr::tr("Jobs"));
jobs.setDefaultValue(1);
jobs.setRange(1, 128);
- registerAspect(&testLoad);
testLoad.setSettingsKey("TestLoad");
testLoad.setLabelText(Tr::tr("Test load"));
testLoad.setToolTip(Tr::tr("Try not to start tests when they may cause CPU load to pass a "
- "threshold."));
+ "threshold."));
- registerAspect(&threshold);
threshold.setSettingsKey("Threshold");
threshold.setLabelText(Tr::tr("Threshold"));
threshold.setDefaultValue(1);
@@ -115,39 +130,4 @@ QStringList CTestSettings::activeSettingsAsOptions() const
return options;
}
-CTestSettingsPage::CTestSettingsPage(CTestSettings *settings, Utils::Id settingsId)
-{
- setId(settingsId);
- setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
- setDisplayName(Tr::tr("CTest"));
-
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- CTestSettings &s = *settings;
- using namespace Utils::Layouting;
-
- Form form {
- Row {s.outputOnFail}, br,
- Row {s.scheduleRandom}, br,
- Row {s.stopOnFailure}, br,
- Row {s.outputMode}, br,
- Group {
- title(Tr::tr("Repeat tests"), &s.repeat),
- Row {s.repetitionMode, s.repetitionCount},
- }, br,
- Group {
- title(Tr::tr("Run in parallel"), &s.parallel),
- Column {
- Row {s.jobs}, br,
- Row {s.testLoad, s.threshold}
- }
- }
- };
-
- Column { Row { Column { form , st }, st } }.attachTo(widget);
- });
-}
-
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/ctest/ctestsettings.h b/src/plugins/autotest/ctest/ctestsettings.h
index f9e87f5a8e..77de6b0aac 100644
--- a/src/plugins/autotest/ctest/ctestsettings.h
+++ b/src/plugins/autotest/ctest/ctestsettings.h
@@ -5,38 +5,28 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
+namespace Autotest::Internal {
-namespace Autotest {
-namespace Internal {
-
-class CTestSettings : public Utils::AspectContainer
+class CTestSettings : public Core::PagedSettings
{
public:
- CTestSettings();
+ explicit CTestSettings(Utils::Id settingsId);
QStringList activeSettingsAsOptions() const;
- Utils::IntegerAspect repetitionCount;
- Utils::SelectionAspect repetitionMode;
- Utils::SelectionAspect outputMode;
- Utils::BoolAspect outputOnFail;
- Utils::BoolAspect stopOnFailure;
- Utils::BoolAspect scheduleRandom;
- Utils::BoolAspect repeat;
+ Utils::IntegerAspect repetitionCount{this};
+ Utils::SelectionAspect repetitionMode{this};
+ Utils::SelectionAspect outputMode{this};
+ Utils::BoolAspect outputOnFail{this};
+ Utils::BoolAspect stopOnFailure{this};
+ Utils::BoolAspect scheduleRandom{this};
+ Utils::BoolAspect repeat{this};
// FIXME.. this makes the outputreader fail to get all results correctly for visual display
- Utils::BoolAspect parallel;
- Utils::IntegerAspect jobs;
- Utils::BoolAspect testLoad;
- Utils::IntegerAspect threshold;
-};
-
-class CTestSettingsPage final : public Core::IOptionsPage
-{
-public:
- CTestSettingsPage(CTestSettings *settings, Utils::Id settingsId);
+ Utils::BoolAspect parallel{this};
+ Utils::IntegerAspect jobs{this};
+ Utils::BoolAspect testLoad{this};
+ Utils::IntegerAspect threshold{this};
};
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/ctest/ctesttool.h b/src/plugins/autotest/ctest/ctesttool.h
index ad510ac947..e7a7f74218 100644
--- a/src/plugins/autotest/ctest/ctesttool.h
+++ b/src/plugins/autotest/ctest/ctesttool.h
@@ -6,8 +6,7 @@
#include "../itestframework.h"
#include "ctestsettings.h"
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
class CTestTool final : public Autotest::ITestTool
{
@@ -26,9 +25,7 @@ protected:
private:
ITestSettings *testSettings() override { return &m_settings; }
- CTestSettings m_settings;
- CTestSettingsPage m_settingsPage{&m_settings, settingsId()};
+ CTestSettings m_settings{settingsId()};
};
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/ctest/ctesttreeitem.cpp b/src/plugins/autotest/ctest/ctesttreeitem.cpp
index 3e59c2db81..9ffc06b028 100644
--- a/src/plugins/autotest/ctest/ctesttreeitem.cpp
+++ b/src/plugins/autotest/ctest/ctesttreeitem.cpp
@@ -14,11 +14,11 @@
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/environmentaspect.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <utils/link.h>
-#include <utils/qtcassert.h>
+#include <utils/qtcassert.h>
using namespace Utils;
@@ -79,7 +79,7 @@ QVariant CTestTreeItem::data(int column, int role) const
QList<ITestConfiguration *> CTestTreeItem::testConfigurationsFor(const QStringList &selected) const
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project)
return {};
@@ -88,7 +88,7 @@ QList<ITestConfiguration *> CTestTreeItem::testConfigurationsFor(const QStringLi
return {};
const ProjectExplorer::BuildSystem *buildSystem = target->buildSystem();
- QStringList options{"--timeout", QString::number(AutotestPlugin::settings()->timeout / 1000)};
+ QStringList options{"--timeout", QString::number(TestSettings::instance()->timeout() / 1000)};
auto ctestSettings = static_cast<CTestSettings *>(testBase()->testSettings());
options << ctestSettings->activeSettingsAsOptions();
CommandLine command = buildSystem->commandLineForTests(selected, options);
diff --git a/src/plugins/autotest/gtest/gtestconfiguration.cpp b/src/plugins/autotest/gtest/gtestconfiguration.cpp
index d2f869d764..8680da1fca 100644
--- a/src/plugins/autotest/gtest/gtestconfiguration.cpp
+++ b/src/plugins/autotest/gtest/gtestconfiguration.cpp
@@ -5,22 +5,20 @@
#include "gtestoutputreader.h"
#include "gtestsettings.h"
-#include "../autotestplugin.h"
+
#include "../itestframework.h"
#include "../testsettings.h"
#include <utils/algorithm.h>
-#include <utils/stringutils.h>
using namespace Utils;
namespace Autotest {
namespace Internal {
-TestOutputReader *GTestConfiguration::createOutputReader(const QFutureInterface<TestResult> &fi,
- QtcProcess *app) const
+TestOutputReader *GTestConfiguration::createOutputReader(Process *app) const
{
- return new GTestOutputReader(fi, app, buildDirectory(), projectFile());
+ return new GTestOutputReader(app, buildDirectory(), projectFile());
}
QStringList filterInterfering(const QStringList &provided, QStringList *omitted)
@@ -56,7 +54,7 @@ QStringList filterInterfering(const QStringList &provided, QStringList *omitted)
QStringList GTestConfiguration::argumentsForTestRunner(QStringList *omitted) const
{
QStringList arguments;
- if (AutotestPlugin::settings()->processArgs) {
+ if (TestSettings::instance()->processArgs()) {
arguments << filterInterfering(runnable().command.arguments().split(
' ', Qt::SkipEmptyParts), omitted);
}
diff --git a/src/plugins/autotest/gtest/gtestconfiguration.h b/src/plugins/autotest/gtest/gtestconfiguration.h
index 97bcbd654f..0cbfc8b0d8 100644
--- a/src/plugins/autotest/gtest/gtestconfiguration.h
+++ b/src/plugins/autotest/gtest/gtestconfiguration.h
@@ -14,8 +14,7 @@ public:
explicit GTestConfiguration(ITestFramework *framework)
: DebuggableTestConfiguration(framework) {}
- TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
- Utils::QtcProcess *app) const override;
+ TestOutputReader *createOutputReader(Utils::Process *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};
diff --git a/src/plugins/autotest/gtest/gtestframework.h b/src/plugins/autotest/gtest/gtestframework.h
index c9e8cbddc0..03c8a4fc0d 100644
--- a/src/plugins/autotest/gtest/gtestframework.h
+++ b/src/plugins/autotest/gtest/gtestframework.h
@@ -7,8 +7,7 @@
#include "gtestconstants.h"
#include "gtestsettings.h"
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
class GTestFramework : public ITestFramework
{
@@ -28,9 +27,7 @@ private:
ITestParser *createTestParser() override;
ITestTreeItem *createRootNode() override;
- GTestSettings m_settings;
- GTestSettingsPage m_settingsPage{&m_settings, settingsId()};
+ GTestSettings m_settings{settingsId()};
};
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/gtest/gtestoutputreader.cpp b/src/plugins/autotest/gtest/gtestoutputreader.cpp
index a24377bef7..1e980c06d0 100644
--- a/src/plugins/autotest/gtest/gtestoutputreader.cpp
+++ b/src/plugins/autotest/gtest/gtestoutputreader.cpp
@@ -8,7 +8,7 @@
#include "../autotesttr.h"
#include <utils/hostosinfo.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QRegularExpression>
@@ -17,15 +17,14 @@ using namespace Utils;
namespace Autotest {
namespace Internal {
-GTestOutputReader::GTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
- QtcProcess *testApplication,
+GTestOutputReader::GTestOutputReader(Process *testApplication,
const FilePath &buildDirectory,
const FilePath &projectFile)
- : TestOutputReader(futureInterface, testApplication, buildDirectory)
+ : TestOutputReader(testApplication, buildDirectory)
, m_projectFile(projectFile)
{
if (testApplication) {
- connect(testApplication, &QtcProcess::done, this, [this, testApplication] {
+ connect(testApplication, &Process::done, this, [this, testApplication] {
const int exitCode = testApplication->exitCode();
if (exitCode == 1 && !m_description.isEmpty()) {
createAndReportResult(Tr::tr("Running tests failed.\n %1\nExecutable: %2")
@@ -116,7 +115,7 @@ void GTestOutputReader::processOutputLine(const QByteArray &outputLine)
testResult.setResult(ResultType::MessageInternal);
testResult.setDescription(Tr::tr("Execution took %1.").arg(match.captured(2)));
reportResult(testResult);
- m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1);
+ // TODO: bump progress?
} else if (ExactMatch match = testSetFail.match(line)) {
m_testSetStarted = false;
TestResult testResult = createDefaultResult();
@@ -127,7 +126,7 @@ void GTestOutputReader::processOutputLine(const QByteArray &outputLine)
testResult.setResult(ResultType::MessageInternal);
testResult.setDescription(Tr::tr("Execution took %1.").arg(match.captured(2)));
reportResult(testResult);
- m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1);
+ // TODO: bump progress?
} else if (ExactMatch match = testSetSkipped.match(line)) {
if (!m_testSetStarted) // ignore SKIPPED at summary
return;
@@ -210,7 +209,7 @@ void GTestOutputReader::handleDescriptionAndReportResult(const TestResult &testR
}
}
result.setDescription(resultDescription.join('\n'));
- reportResult(testResult);
+ reportResult(result);
resultDescription.clear();
result = createDefaultResult();
diff --git a/src/plugins/autotest/gtest/gtestoutputreader.h b/src/plugins/autotest/gtest/gtestoutputreader.h
index e73ab0e823..a63c566829 100644
--- a/src/plugins/autotest/gtest/gtestoutputreader.h
+++ b/src/plugins/autotest/gtest/gtestoutputreader.h
@@ -11,8 +11,7 @@ namespace Internal {
class GTestOutputReader : public TestOutputReader
{
public:
- GTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
- Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
+ GTestOutputReader(Utils::Process *testApplication, const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile);
protected:
void processOutputLine(const QByteArray &outputLine) override;
diff --git a/src/plugins/autotest/gtest/gtestparser.cpp b/src/plugins/autotest/gtest/gtestparser.cpp
index aa59bb94c2..c8cc92005e 100644
--- a/src/plugins/autotest/gtest/gtestparser.cpp
+++ b/src/plugins/autotest/gtest/gtestparser.cpp
@@ -10,6 +10,7 @@
#include <cppeditor/cppmodelmanager.h>
#include <cppeditor/projectpart.h>
+#include <QPromise>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
@@ -70,7 +71,7 @@ static bool hasGTestNames(const CPlusPlus::Document::Ptr &document)
return false;
}
-bool GTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+bool GTestParser::processDocument(QPromise<TestParseResultPtr> &promise,
const FilePath &fileName)
{
CPlusPlus::Document::Ptr doc = document(fileName);
@@ -124,7 +125,7 @@ bool GTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futureIn
parseResult->children.append(testSet);
}
- futureInterface.reportResult(TestParseResultPtr(parseResult));
+ promise.addResult(TestParseResultPtr(parseResult));
}
return !result.isEmpty();
}
diff --git a/src/plugins/autotest/gtest/gtestparser.h b/src/plugins/autotest/gtest/gtestparser.h
index 665a8a496c..52febe6b89 100644
--- a/src/plugins/autotest/gtest/gtestparser.h
+++ b/src/plugins/autotest/gtest/gtestparser.h
@@ -22,7 +22,7 @@ class GTestParser : public CppParser
{
public:
explicit GTestParser(ITestFramework *framework) : CppParser(framework) {}
- bool processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+ bool processDocument(QPromise<TestParseResultPtr> &futureInterface,
const Utils::FilePath &fileName) override;
};
diff --git a/src/plugins/autotest/gtest/gtestsettings.cpp b/src/plugins/autotest/gtest/gtestsettings.cpp
index babea83f78..ca31c99f12 100644
--- a/src/plugins/autotest/gtest/gtestsettings.cpp
+++ b/src/plugins/autotest/gtest/gtestsettings.cpp
@@ -11,24 +11,35 @@
#include <utils/layoutbuilder.h>
+using namespace Layouting;
using namespace Utils;
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
-GTestSettings::GTestSettings()
+GTestSettings::GTestSettings(Id settingsId)
{
setSettingsGroups("Autotest", "GTest");
- setAutoApply(false);
+ setId(settingsId);
+ setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
+ setDisplayName(Tr::tr(GTest::Constants::FRAMEWORK_SETTINGS_CATEGORY));
+
+ setLayouter([this] {
+ return Row { Form {
+ runDisabled, br,
+ breakOnFailure, br,
+ repeat, iterations, br,
+ shuffle, seed, br,
+ groupMode, br,
+ gtestFilter, br
+ }, st };
+ });
- registerAspect(&iterations);
iterations.setSettingsKey("Iterations");
iterations.setDefaultValue(1);
iterations.setEnabled(false);
iterations.setLabelText(Tr::tr("Iterations:"));
iterations.setEnabler(&repeat);
- registerAspect(&seed);
seed.setSettingsKey("Seed");
seed.setSpecialValueText({});
seed.setEnabled(false);
@@ -36,33 +47,28 @@ GTestSettings::GTestSettings()
seed.setToolTip(Tr::tr("A seed of 0 generates a seed based on the current timestamp."));
seed.setEnabler(&shuffle);
- registerAspect(&runDisabled);
runDisabled.setSettingsKey("RunDisabled");
runDisabled.setLabelText(Tr::tr("Run disabled tests"));
runDisabled.setToolTip(Tr::tr("Executes disabled tests when performing a test run."));
- registerAspect(&shuffle);
shuffle.setSettingsKey("Shuffle");
shuffle.setLabelText(Tr::tr("Shuffle tests"));
shuffle.setToolTip(Tr::tr("Shuffles tests automatically on every iteration by the given seed."));
- registerAspect(&repeat);
repeat.setSettingsKey("Repeat");
repeat.setLabelText(Tr::tr("Repeat tests"));
- repeat.setToolTip(Tr::tr("Repeats a test run (you might be required to increase the timeout to avoid canceling the tests)."));
+ repeat.setToolTip(Tr::tr("Repeats a test run (you might be required to increase the timeout to "
+ "avoid canceling the tests)."));
- registerAspect(&throwOnFailure);
throwOnFailure.setSettingsKey("ThrowOnFailure");
throwOnFailure.setLabelText(Tr::tr("Throw on failure"));
throwOnFailure.setToolTip(Tr::tr("Turns assertion failures into C++ exceptions."));
- registerAspect(&breakOnFailure);
breakOnFailure.setSettingsKey("BreakOnFailure");
breakOnFailure.setDefaultValue(true);
breakOnFailure.setLabelText(Tr::tr("Break on failure while debugging"));
breakOnFailure.setToolTip(Tr::tr("Turns failures into debugger breakpoints."));
- registerAspect(&groupMode);
groupMode.setSettingsKey("GroupMode");
groupMode.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
groupMode.setFromSettingsTransformation([this](const QVariant &savedValue) -> QVariant {
@@ -80,7 +86,6 @@ GTestSettings::GTestSettings()
groupMode.setLabelText(Tr::tr("Group mode:"));
groupMode.setToolTip(Tr::tr("Select on what grouping the tests should be based."));
- registerAspect(&gtestFilter);
gtestFilter.setSettingsKey("GTestFilter");
gtestFilter.setDisplayStyle(StringAspect::LineEditDisplay);
gtestFilter.setDefaultValue(GTest::Constants::DEFAULT_FILTER);
@@ -93,8 +98,8 @@ GTestSettings::GTestSettings()
});
gtestFilter.setEnabled(false);
gtestFilter.setLabelText(Tr::tr("Active filter:"));
- gtestFilter.setToolTip(Tr::tr("Set the GTest filter to be used for grouping.\n"
- "See Google Test documentation for further information on GTest filters."));
+ gtestFilter.setToolTip(Tr::tr("Set the GTest filter to be used for grouping.\nSee Google Test "
+ "documentation for further information on GTest filters."));
gtestFilter.setValidationFunction([](FancyLineEdit *edit, QString * /*error*/) {
return edit && GTestUtils::isValidGTestFilter(edit->text());
@@ -104,39 +109,11 @@ GTestSettings::GTestSettings()
&gtestFilter, [this](int val) {
gtestFilter.setEnabled(groupMode.itemValueForIndex(val) == GTest::Constants::GTestFilter);
});
-}
-
-GTestSettingsPage::GTestSettingsPage(GTestSettings *settings, Id settingsId)
-{
- setId(settingsId);
- setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
- setDisplayName(Tr::tr(GTest::Constants::FRAMEWORK_SETTINGS_CATEGORY));
- setSettings(settings);
- QObject::connect(settings, &AspectContainer::applied, this, [] {
+ QObject::connect(this, &AspectContainer::applied, this, [] {
Id id = Id(Constants::FRAMEWORK_PREFIX).withSuffix(GTest::Constants::FRAMEWORK_NAME);
TestTreeModel::instance()->rebuild({id});
});
-
- setLayouter([settings](QWidget *widget) {
- GTestSettings &s = *settings;
- using namespace Layouting;
-
- Grid grid {
- s.runDisabled, br,
- s.breakOnFailure, br,
- s.repeat, s.iterations, br,
- s.shuffle, s.seed
- };
-
- Form form {
- s.groupMode,
- s.gtestFilter
- };
-
- Column { Row { Column { grid, form, st }, st } }.attachTo(widget);
- });
}
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/gtest/gtestsettings.h b/src/plugins/autotest/gtest/gtestsettings.h
index 11fb144e22..ea0ab565e6 100644
--- a/src/plugins/autotest/gtest/gtestsettings.h
+++ b/src/plugins/autotest/gtest/gtestsettings.h
@@ -5,32 +5,22 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
+namespace Autotest::Internal {
-namespace Autotest {
-namespace Internal {
-
-class GTestSettings : public Utils::AspectContainer
-{
-public:
- GTestSettings();
-
- Utils::IntegerAspect iterations;
- Utils::IntegerAspect seed;
- Utils::BoolAspect runDisabled;
- Utils::BoolAspect shuffle;
- Utils::BoolAspect repeat;
- Utils::BoolAspect throwOnFailure;
- Utils::BoolAspect breakOnFailure;
- Utils::SelectionAspect groupMode;
- Utils::StringAspect gtestFilter;
-};
-
-class GTestSettingsPage final : public Core::IOptionsPage
+class GTestSettings : public Core::PagedSettings
{
public:
- GTestSettingsPage(GTestSettings *settings, Utils::Id settingsId);
+ explicit GTestSettings(Utils::Id settingsId);
+
+ Utils::IntegerAspect iterations{this};
+ Utils::IntegerAspect seed{this};
+ Utils::BoolAspect runDisabled{this};
+ Utils::BoolAspect shuffle{this};
+ Utils::BoolAspect repeat{this};
+ Utils::BoolAspect throwOnFailure{this};
+ Utils::BoolAspect breakOnFailure{this};
+ Utils::SelectionAspect groupMode{this};
+ Utils::StringAspect gtestFilter{this};
};
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/gtest/gtesttreeitem.cpp b/src/plugins/autotest/gtest/gtesttreeitem.cpp
index c79ae93d1b..f91b68f0e3 100644
--- a/src/plugins/autotest/gtest/gtesttreeitem.cpp
+++ b/src/plugins/autotest/gtest/gtesttreeitem.cpp
@@ -10,7 +10,9 @@
#include "../autotesttr.h"
#include <cppeditor/cppmodelmanager.h>
-#include <projectexplorer/session.h>
+
+#include <projectexplorer/projectmanager.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/algorithm.h>
#include <utils/icon.h>
@@ -146,7 +148,7 @@ QVariant GTestTreeItem::data(int column, int role) const
ITestConfiguration *GTestTreeItem::testConfiguration() const
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(project, return nullptr);
GTestConfiguration *config = nullptr;
@@ -252,7 +254,7 @@ static void collectFailedTestInfo(const GTestTreeItem *item,
QList<ITestConfiguration *> GTestTreeItem::getTestConfigurations(bool ignoreCheckState) const
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
@@ -291,7 +293,7 @@ QList<ITestConfiguration *> GTestTreeItem::getSelectedTestConfigurations() const
QList<ITestConfiguration *> GTestTreeItem::getFailedTestConfigurations() const
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
@@ -316,7 +318,7 @@ QList<ITestConfiguration *> GTestTreeItem::getFailedTestConfigurations() const
QList<ITestConfiguration *> GTestTreeItem::getTestConfigurationsForFile(const FilePath &fileName) const
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
@@ -503,7 +505,7 @@ QSet<QString> internalTargets(const TestTreeItem &item)
{
QSet<QString> result;
const auto cppMM = CppEditor::CppModelManager::instance();
- const auto projectInfo = cppMM->projectInfo(ProjectExplorer::SessionManager::startupProject());
+ const auto projectInfo = cppMM->projectInfo(ProjectExplorer::ProjectManager::startupProject());
if (!projectInfo)
return {};
const FilePath filePath = item.filePath();
diff --git a/src/plugins/autotest/itestparser.cpp b/src/plugins/autotest/itestparser.cpp
index d8f1d6953b..79fde2a0bb 100644
--- a/src/plugins/autotest/itestparser.cpp
+++ b/src/plugins/autotest/itestparser.cpp
@@ -24,7 +24,7 @@ CppParser::CppParser(ITestFramework *framework)
{
}
-void CppParser::init(const FilePaths &filesToParse, bool fullParse)
+void CppParser::init(const QSet<FilePath> &filesToParse, bool fullParse)
{
Q_UNUSED(filesToParse)
Q_UNUSED(fullParse)
@@ -43,8 +43,8 @@ bool CppParser::selectedForBuilding(const FilePath &fileName)
QByteArray CppParser::getFileContent(const FilePath &filePath) const
{
QByteArray fileContent;
- if (m_workingCopy.contains(filePath)) {
- fileContent = m_workingCopy.source(filePath);
+ if (const auto source = m_workingCopy.source(filePath)) {
+ fileContent = *source;
} else {
QString error;
const QTextCodec *codec = Core::EditorManager::defaultTextCodec();
diff --git a/src/plugins/autotest/itestparser.h b/src/plugins/autotest/itestparser.h
index 7e16a61b90..93233bd7a9 100644
--- a/src/plugins/autotest/itestparser.h
+++ b/src/plugins/autotest/itestparser.h
@@ -9,9 +9,9 @@
#include <cppeditor/cppworkingcopy.h>
#include <qmljs/qmljsdocument.h>
-#include <QFutureInterface>
-
QT_BEGIN_NAMESPACE
+template <class T>
+class QPromise;
class QRegularExpression;
QT_END_NAMESPACE
@@ -45,8 +45,8 @@ class ITestParser
public:
explicit ITestParser(ITestFramework *framework) : m_framework(framework) {}
virtual ~ITestParser() { }
- virtual void init(const Utils::FilePaths &filesToParse, bool fullParse) = 0;
- virtual bool processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+ virtual void init(const QSet<Utils::FilePath> &filesToParse, bool fullParse) = 0;
+ virtual bool processDocument(QPromise<TestParseResultPtr> &futureInterface,
const Utils::FilePath &fileName) = 0;
virtual QStringList supportedExtensions() const { return {}; }
@@ -63,7 +63,7 @@ class CppParser : public ITestParser
{
public:
explicit CppParser(ITestFramework *framework);
- void init(const Utils::FilePaths &filesToParse, bool fullParse) override;
+ void init(const QSet<Utils::FilePath> &filesToParse, bool fullParse) override;
static bool selectedForBuilding(const Utils::FilePath &fileName);
QByteArray getFileContent(const Utils::FilePath &filePath) const;
void release() override;
diff --git a/src/plugins/autotest/qtest/qttest_utils.cpp b/src/plugins/autotest/qtest/qttest_utils.cpp
index 1069280bee..9bedaa64c3 100644
--- a/src/plugins/autotest/qtest/qttest_utils.cpp
+++ b/src/plugins/autotest/qtest/qttest_utils.cpp
@@ -2,9 +2,9 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qttest_utils.h"
+
#include "qttesttreeitem.h"
-#include "../autotestplugin.h"
-#include "../testframeworkmanager.h"
+#include "../itestframework.h"
#include "../testsettings.h"
#include <utils/algorithm.h>
@@ -27,7 +27,8 @@ bool isQTestMacro(const QByteArray &macro)
return valid.contains(macro);
}
-QHash<FilePath, TestCases> testCaseNamesForFiles(ITestFramework *framework, const FilePaths &files)
+QHash<FilePath, TestCases> testCaseNamesForFiles(ITestFramework *framework,
+ const QSet<FilePath> &files)
{
QHash<FilePath, TestCases> result;
TestTreeItem *rootNode = framework->rootNode();
@@ -49,7 +50,8 @@ QHash<FilePath, TestCases> testCaseNamesForFiles(ITestFramework *framework, cons
return result;
}
-QMultiHash<FilePath, FilePath> alternativeFiles(ITestFramework *framework, const FilePaths &files)
+QMultiHash<FilePath, FilePath> alternativeFiles(ITestFramework *framework,
+ const QSet<FilePath> &files)
{
QMultiHash<FilePath, FilePath> result;
TestTreeItem *rootNode = framework->rootNode();
@@ -135,7 +137,7 @@ Environment prepareBasicEnvironment(const Environment &env)
result.set("QT_FORCE_STDERR_LOGGING", "1");
result.set("QT_LOGGING_TO_CONSOLE", "1");
}
- const int timeout = AutotestPlugin::settings()->timeout;
+ const int timeout = TestSettings::instance()->timeout();
if (timeout > 5 * 60 * 1000) // Qt5.5 introduced hard limit, Qt5.6.1 added env var to raise this
result.set("QTEST_FUNCTION_TIMEOUT", QString::number(timeout));
return result;
diff --git a/src/plugins/autotest/qtest/qttest_utils.h b/src/plugins/autotest/qtest/qttest_utils.h
index 6f5a587df3..8f07734541 100644
--- a/src/plugins/autotest/qtest/qttest_utils.h
+++ b/src/plugins/autotest/qtest/qttest_utils.h
@@ -26,9 +26,9 @@ namespace QTestUtils {
bool isQTestMacro(const QByteArray &macro);
QHash<Utils::FilePath, TestCases> testCaseNamesForFiles(ITestFramework *framework,
- const Utils::FilePaths &files);
+ const QSet<Utils::FilePath> &files);
QMultiHash<Utils::FilePath, Utils::FilePath> alternativeFiles(ITestFramework *framework,
- const Utils::FilePaths &files);
+ const QSet<Utils::FilePath> &files);
QStringList filterInterfering(const QStringList &provided, QStringList *omitted, bool isQuickTest);
Utils::Environment prepareBasicEnvironment(const Utils::Environment &env);
diff --git a/src/plugins/autotest/qtest/qttestconfiguration.cpp b/src/plugins/autotest/qtest/qttestconfiguration.cpp
index 8a77f305e3..5dcf59a48e 100644
--- a/src/plugins/autotest/qtest/qttestconfiguration.cpp
+++ b/src/plugins/autotest/qtest/qttestconfiguration.cpp
@@ -2,16 +2,15 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qttestconfiguration.h"
-#include "qttestconstants.h"
+
#include "qttestoutputreader.h"
#include "qttestsettings.h"
#include "qttest_utils.h"
-#include "../autotestplugin.h"
+
#include "../itestframework.h"
#include "../testsettings.h"
#include <utils/algorithm.h>
-#include <utils/stringutils.h>
using namespace Utils;
@@ -28,20 +27,19 @@ static QStringList quoteIfNeeded(const QStringList &testCases, bool debugMode)
});
}
-TestOutputReader *QtTestConfiguration::createOutputReader(const QFutureInterface<TestResult> &fi,
- QtcProcess *app) const
+TestOutputReader *QtTestConfiguration::createOutputReader(Process *app) const
{
auto qtSettings = static_cast<QtTestSettings *>(framework()->testSettings());
const QtTestOutputReader::OutputMode mode = qtSettings && qtSettings->useXMLOutput.value()
? QtTestOutputReader::XML
: QtTestOutputReader::PlainText;
- return new QtTestOutputReader(fi, app, buildDirectory(), projectFile(), mode, TestType::QtTest);
+ return new QtTestOutputReader(app, buildDirectory(), projectFile(), mode, TestType::QtTest);
}
QStringList QtTestConfiguration::argumentsForTestRunner(QStringList *omitted) const
{
QStringList arguments;
- if (AutotestPlugin::settings()->processArgs) {
+ if (TestSettings::instance()->processArgs()) {
arguments.append(QTestUtils::filterInterfering(
runnable().command.arguments().split(' ', Qt::SkipEmptyParts),
omitted, false));
diff --git a/src/plugins/autotest/qtest/qttestconfiguration.h b/src/plugins/autotest/qtest/qttestconfiguration.h
index a99c93f0b7..d41697090e 100644
--- a/src/plugins/autotest/qtest/qttestconfiguration.h
+++ b/src/plugins/autotest/qtest/qttestconfiguration.h
@@ -13,8 +13,7 @@ class QtTestConfiguration : public DebuggableTestConfiguration
public:
explicit QtTestConfiguration(ITestFramework *framework)
: DebuggableTestConfiguration(framework) {}
- TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
- Utils::QtcProcess *app) const override;
+ TestOutputReader *createOutputReader(Utils::Process *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};
diff --git a/src/plugins/autotest/qtest/qttestframework.h b/src/plugins/autotest/qtest/qttestframework.h
index 4fa765ab19..c90bd3c9d8 100644
--- a/src/plugins/autotest/qtest/qttestframework.h
+++ b/src/plugins/autotest/qtest/qttestframework.h
@@ -7,8 +7,7 @@
#include "qttestsettings.h"
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
class QtTestFramework : public ITestFramework
{
@@ -24,9 +23,7 @@ private:
ITestTreeItem *createRootNode() override;
ITestSettings *testSettings() override { return &m_settings; }
- QtTestSettings m_settings;
- QtTestSettingsPage m_settingsPage{&m_settings, settingsId()};
+ QtTestSettings m_settings{settingsId()};
};
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/qtest/qttestoutputreader.cpp b/src/plugins/autotest/qtest/qttestoutputreader.cpp
index 073ef0e9fe..70df35e663 100644
--- a/src/plugins/autotest/qtest/qttestoutputreader.cpp
+++ b/src/plugins/autotest/qtest/qttestoutputreader.cpp
@@ -12,8 +12,6 @@
#include <QRegularExpression>
-#include <cctype>
-
using namespace Utils;
namespace Autotest {
@@ -99,18 +97,15 @@ static QString constructBenchmarkInformation(const QString &metric, double value
else if (metric == "CPUCycles") // -perf
metricsText = "CPU cycles";
return Tr::tr("%1 %2 per iteration (total: %3, iterations: %4)")
- .arg(formatResult(value))
- .arg(metricsText)
- .arg(formatResult(value * double(iterations)))
+ .arg(formatResult(value), metricsText, formatResult(value * double(iterations)))
.arg(iterations);
}
-QtTestOutputReader::QtTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
- QtcProcess *testApplication,
+QtTestOutputReader::QtTestOutputReader(Process *testApplication,
const FilePath &buildDirectory,
const FilePath &projectFile,
OutputMode mode, TestType type)
- : TestOutputReader(futureInterface, testApplication, buildDirectory)
+ : TestOutputReader(testApplication, buildDirectory)
, m_projectFile(projectFile)
, m_mode(mode)
, m_testType(type)
@@ -177,8 +172,6 @@ void QtTestOutputReader::processXMLOutput(const QByteArray &outputLine)
m_xmlReader.addData("\n");
m_xmlReader.addData(QString::fromUtf8(outputLine));
while (!m_xmlReader.atEnd()) {
- if (m_futureInterface.isCanceled())
- return;
QXmlStreamReader::TokenType token = m_xmlReader.readNext();
switch (token) {
case QXmlStreamReader::StartDocument:
@@ -277,7 +270,7 @@ void QtTestOutputReader::processXMLOutput(const QByteArray &outputLine)
const QStringView currentTag = m_xmlReader.name();
if (currentTag == QStringLiteral("TestFunction")) {
sendFinishMessage(true);
- m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1);
+ // TODO: bump progress?
m_dataTag.clear();
m_formerTestCase = m_testCase;
m_testCase.clear();
@@ -347,9 +340,6 @@ void QtTestOutputReader::processPlainTextOutput(const QByteArray &outputLine)
static const QRegularExpression locationUnix(QT_TEST_FAIL_UNIX_REGEXP);
static const QRegularExpression locationWin(QT_TEST_FAIL_WIN_REGEXP);
- if (m_futureInterface.isCanceled())
- return;
-
const QString line = QString::fromUtf8(outputLine);
QRegularExpressionMatch match;
diff --git a/src/plugins/autotest/qtest/qttestoutputreader.h b/src/plugins/autotest/qtest/qttestoutputreader.h
index 6db9aa8781..0962c8d684 100644
--- a/src/plugins/autotest/qtest/qttestoutputreader.h
+++ b/src/plugins/autotest/qtest/qttestoutputreader.h
@@ -3,9 +3,10 @@
#pragma once
-#include "qttestconstants.h"
#include "../testoutputreader.h"
+#include "qttestconstants.h"
+
#include <QXmlStreamReader>
namespace Autotest {
@@ -22,8 +23,7 @@ public:
PlainText
};
- QtTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
- Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
+ QtTestOutputReader(Utils::Process *testApplication, const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile, OutputMode mode, TestType type);
protected:
void processOutputLine(const QByteArray &outputLine) override;
diff --git a/src/plugins/autotest/qtest/qttestparser.cpp b/src/plugins/autotest/qtest/qttestparser.cpp
index 2b13133f03..f68a87d7bf 100644
--- a/src/plugins/autotest/qtest/qttestparser.cpp
+++ b/src/plugins/autotest/qtest/qttestparser.cpp
@@ -10,6 +10,7 @@
#include <cplusplus/TypeOfExpression.h>
#include <utils/algorithm.h>
+#include <QPromise>
#include <QRegularExpressionMatchIterator>
using namespace Utils;
@@ -292,7 +293,7 @@ static bool isQObject(const CPlusPlus::Document::Ptr &declaringDoc)
|| file.endsWith("QtCore/qobject.h") || file.endsWith("kernel/qobject.h");
}
-bool QtTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+bool QtTestParser::processDocument(QPromise<TestParseResultPtr> &promise,
const FilePath &fileName)
{
CPlusPlus::Document::Ptr doc = document(fileName);
@@ -325,7 +326,7 @@ bool QtTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futureI
data.multipleTestCases = testCase.multipleTestCases;
QtTestParseResult *parseResult
= createParseResult(testCase.name, data, projectParts.first()->projectFile);
- futureInterface.reportResult(TestParseResultPtr(parseResult));
+ promise.addResult(TestParseResultPtr(parseResult));
reported = true;
}
}
@@ -413,7 +414,7 @@ QtTestParseResult *QtTestParser::createParseResult(
return parseResult;
}
-void QtTestParser::init(const FilePaths &filesToParse, bool fullParse)
+void QtTestParser::init(const QSet<FilePath> &filesToParse, bool fullParse)
{
if (!fullParse) { // in a full parse cached information might lead to wrong results
m_testCases = QTestUtils::testCaseNamesForFiles(framework(), filesToParse);
diff --git a/src/plugins/autotest/qtest/qttestparser.h b/src/plugins/autotest/qtest/qttestparser.h
index db677929ec..abd8b2b0aa 100644
--- a/src/plugins/autotest/qtest/qttestparser.h
+++ b/src/plugins/autotest/qtest/qttestparser.h
@@ -34,9 +34,9 @@ class QtTestParser : public CppParser
public:
explicit QtTestParser(ITestFramework *framework) : CppParser(framework) {}
- void init(const Utils::FilePaths &filesToParse, bool fullParse) override;
+ void init(const QSet<Utils::FilePath> &filesToParse, bool fullParse) override;
void release() override;
- bool processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+ bool processDocument(QPromise<TestParseResultPtr> &promise,
const Utils::FilePath &fileName) override;
private:
diff --git a/src/plugins/autotest/qtest/qttestsettings.cpp b/src/plugins/autotest/qtest/qttestsettings.cpp
index 4be93d9937..a8953a0449 100644
--- a/src/plugins/autotest/qtest/qttestsettings.cpp
+++ b/src/plugins/autotest/qtest/qttestsettings.cpp
@@ -10,17 +10,33 @@
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
+using namespace Layouting;
using namespace Utils;
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
-QtTestSettings::QtTestSettings()
+QtTestSettings::QtTestSettings(Id settingsId)
{
setSettingsGroups("Autotest", "QtTest");
- setAutoApply(false);
+ setId(settingsId);
+ setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
+ setDisplayName(Tr::tr(QtTest::Constants::FRAMEWORK_SETTINGS_CATEGORY));
+
+ setLayouter([this] {
+ return Row { Form {
+ noCrashHandler, br,
+ useXMLOutput, br,
+ verboseBench, br,
+ logSignalsSlots, br,
+ limitWarnings, maxWarnings, br,
+ Group {
+ title(Tr::tr("Benchmark Metrics")),
+ Column { metrics }
+ }, br,
+ quickCheckForDerivedTests, br
+ }, st };
+ });
- registerAspect(&metrics);
metrics.setSettingsKey("Metrics");
metrics.setDefaultValue(Walltime);
metrics.addOption(Tr::tr("Walltime"), Tr::tr("Uses walltime metrics for executing benchmarks (default)."));
@@ -37,43 +53,36 @@ QtTestSettings::QtTestSettings()
HostOsInfo::isLinuxHost() // according to docs perf Linux only
});
- registerAspect(&noCrashHandler);
noCrashHandler.setSettingsKey("NoCrashhandlerOnDebug");
noCrashHandler.setDefaultValue(true);
noCrashHandler.setLabelText(Tr::tr("Disable crash handler while debugging"));
noCrashHandler.setToolTip(Tr::tr("Enables interrupting tests on assertions."));
- registerAspect(&useXMLOutput);
useXMLOutput.setSettingsKey("UseXMLOutput");
useXMLOutput.setDefaultValue(true);
useXMLOutput.setLabelText(Tr::tr("Use XML output"));
useXMLOutput.setToolTip(Tr::tr("XML output is recommended, because it avoids parsing issues, "
- "while plain text is more human readable.\n\n"
- "Warning: Plain text misses some information, such as duration."));
+ "while plain text is more human readable.\n\nWarning: "
+ "Plain text misses some information, such as duration."));
- registerAspect(&verboseBench);
verboseBench.setSettingsKey("VerboseBench");
verboseBench.setLabelText(Tr::tr("Verbose benchmarks"));
- registerAspect(&logSignalsSlots);
logSignalsSlots.setSettingsKey("LogSignalsSlots");
logSignalsSlots.setLabelText(Tr::tr("Log signals and slots"));
logSignalsSlots.setToolTip(Tr::tr("Log every signal emission and resulting slot invocations."));
- registerAspect(&limitWarnings);
limitWarnings.setSettingsKey("LimitWarnings");
limitWarnings.setLabelText(Tr::tr("Limit warnings"));
limitWarnings.setToolTip(Tr::tr("Set the maximum number of warnings. 0 means that the number "
"is not limited."));
- registerAspect(&maxWarnings);
maxWarnings.setSettingsKey("MaxWarnings");
maxWarnings.setRange(0, 10000);
maxWarnings.setDefaultValue(2000);
maxWarnings.setSpecialValueText(Tr::tr("Unlimited"));
maxWarnings.setEnabler(&limitWarnings);
- registerAspect(&quickCheckForDerivedTests);
quickCheckForDerivedTests.setSettingsKey("QuickCheckForDerivedTests");
quickCheckForDerivedTests.setDefaultValue(false);
quickCheckForDerivedTests.setLabelText(Tr::tr("Check for derived Qt Quick tests"));
@@ -99,36 +108,4 @@ QString QtTestSettings::metricsTypeToOption(const MetricsType type)
return {};
}
-QtTestSettingsPage::QtTestSettingsPage(QtTestSettings *settings, Id settingsId)
-{
- setId(settingsId);
- setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
- setDisplayName(Tr::tr(QtTest::Constants::FRAMEWORK_SETTINGS_CATEGORY));
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- QtTestSettings &s = *settings;
- using namespace Layouting;
-
- Column col {
- s.noCrashHandler,
- s.useXMLOutput,
- s.verboseBench,
- s.logSignalsSlots,
- Row {
- s.limitWarnings, s.maxWarnings
- },
- Group {
- title(Tr::tr("Benchmark Metrics")),
- Column { s.metrics }
- },
- br,
- s.quickCheckForDerivedTests,
- };
-
- Column { Row { col, st }, st }.attachTo(widget);
- });
-}
-
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/qtest/qttestsettings.h b/src/plugins/autotest/qtest/qttestsettings.h
index 4bbec4332d..31394ee602 100644
--- a/src/plugins/autotest/qtest/qttestsettings.h
+++ b/src/plugins/autotest/qtest/qttestsettings.h
@@ -5,10 +5,7 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
-
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
enum MetricsType
{
@@ -19,28 +16,21 @@ enum MetricsType
Perf
};
-class QtTestSettings : public Utils::AspectContainer
+class QtTestSettings : public Core::PagedSettings
{
public:
- QtTestSettings();
+ explicit QtTestSettings(Utils::Id settingsId);
static QString metricsTypeToOption(const MetricsType type);
- Utils::SelectionAspect metrics;
- Utils::BoolAspect noCrashHandler;
- Utils::BoolAspect useXMLOutput;
- Utils::BoolAspect verboseBench;
- Utils::BoolAspect logSignalsSlots;
- Utils::BoolAspect limitWarnings;
- Utils::IntegerAspect maxWarnings;
- Utils::BoolAspect quickCheckForDerivedTests;
-};
-
-class QtTestSettingsPage final : public Core::IOptionsPage
-{
-public:
- QtTestSettingsPage(QtTestSettings *settings, Utils::Id settingsId);
+ Utils::SelectionAspect metrics{this};
+ Utils::BoolAspect noCrashHandler{this};
+ Utils::BoolAspect useXMLOutput{this};
+ Utils::BoolAspect verboseBench{this};
+ Utils::BoolAspect logSignalsSlots{this};
+ Utils::BoolAspect limitWarnings{this};
+ Utils::IntegerAspect maxWarnings{this};
+ Utils::BoolAspect quickCheckForDerivedTests{this};
};
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/qtest/qttesttreeitem.cpp b/src/plugins/autotest/qtest/qttesttreeitem.cpp
index 2f84637282..81f95c1775 100644
--- a/src/plugins/autotest/qtest/qttesttreeitem.cpp
+++ b/src/plugins/autotest/qtest/qttesttreeitem.cpp
@@ -9,7 +9,9 @@
#include "../itestframework.h"
#include <cppeditor/cppmodelmanager.h>
-#include <projectexplorer/session.h>
+
+#include <projectexplorer/projectmanager.h>
+
#include <utils/qtcassert.h>
using namespace Utils;
@@ -116,7 +118,7 @@ bool QtTestTreeItem::canProvideDebugConfiguration() const
ITestConfiguration *QtTestTreeItem::testConfiguration() const
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(project, return nullptr);
const auto cppMM = CppEditor::CppModelManager::instance();
QTC_ASSERT(cppMM, return nullptr);
@@ -195,7 +197,7 @@ static void fillTestConfigurationsFromCheckState(const TestTreeItem *item,
testConfig = new QtTestConfiguration(item->framework());
testConfig->setTestCases(testCases);
testConfig->setProjectFile(item->proFile());
- testConfig->setProject(ProjectExplorer::SessionManager::startupProject());
+ testConfig->setProject(ProjectExplorer::ProjectManager::startupProject());
testConfig->setInternalTargets(cppMM->internalTargets(item->filePath()));
testConfigurations << testConfig;
}
@@ -229,7 +231,7 @@ static void collectFailedTestInfo(TestTreeItem *item, QList<ITestConfiguration *
QtTestConfiguration *testConfig = new QtTestConfiguration(item->framework());
testConfig->setTestCases(testCases);
testConfig->setProjectFile(item->proFile());
- testConfig->setProject(ProjectExplorer::SessionManager::startupProject());
+ testConfig->setProject(ProjectExplorer::ProjectManager::startupProject());
testConfig->setInternalTargets(cppMM->internalTargets(item->filePath()));
testConfigs << testConfig;
}
@@ -246,7 +248,7 @@ QList<ITestConfiguration *> QtTestTreeItem::getAllTestConfigurations() const
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
@@ -269,7 +271,7 @@ QList<ITestConfiguration *> QtTestTreeItem::getAllTestConfigurations() const
QList<ITestConfiguration *> QtTestTreeItem::getSelectedTestConfigurations() const
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
@@ -292,7 +294,7 @@ QList<ITestConfiguration *> QtTestTreeItem::getTestConfigurationsForFile(const F
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
diff --git a/src/plugins/autotest/quick/quicktest_utils.cpp b/src/plugins/autotest/quick/quicktest_utils.cpp
index 09d2453c96..d5ffc26038 100644
--- a/src/plugins/autotest/quick/quicktest_utils.cpp
+++ b/src/plugins/autotest/quick/quicktest_utils.cpp
@@ -22,7 +22,8 @@ bool isQuickTestMacro(const QByteArray &macro)
return valid.contains(macro);
}
-QHash<FilePath, FilePath> proFilesForQmlFiles(ITestFramework *framework, const FilePaths &files)
+QHash<FilePath, FilePath> proFilesForQmlFiles(ITestFramework *framework,
+ const QSet<FilePath> &files)
{
QHash<FilePath, FilePath> result;
TestTreeItem *rootNode = framework->rootNode();
diff --git a/src/plugins/autotest/quick/quicktest_utils.h b/src/plugins/autotest/quick/quicktest_utils.h
index fb1dbb2675..731f7c6864 100644
--- a/src/plugins/autotest/quick/quicktest_utils.h
+++ b/src/plugins/autotest/quick/quicktest_utils.h
@@ -16,7 +16,7 @@ namespace QuickTestUtils {
bool isQuickTestMacro(const QByteArray &macro);
QHash<Utils::FilePath, Utils::FilePath> proFilesForQmlFiles(ITestFramework *framework,
- const Utils::FilePaths &files);
+ const QSet<Utils::FilePath> &files);
} // namespace QuickTestUtils
} // namespace Internal
diff --git a/src/plugins/autotest/quick/quicktestconfiguration.cpp b/src/plugins/autotest/quick/quicktestconfiguration.cpp
index c7cce31acf..e37b208ac4 100644
--- a/src/plugins/autotest/quick/quicktestconfiguration.cpp
+++ b/src/plugins/autotest/quick/quicktestconfiguration.cpp
@@ -2,16 +2,13 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "quicktestconfiguration.h"
-#include "../qtest/qttestconstants.h"
+
+#include "../itestframework.h"
#include "../qtest/qttestoutputreader.h"
#include "../qtest/qttestsettings.h"
#include "../qtest/qttest_utils.h"
-#include "../autotestplugin.h"
-#include "../itestframework.h"
#include "../testsettings.h"
-#include <utils/stringutils.h>
-
using namespace Utils;
namespace Autotest {
@@ -23,21 +20,19 @@ QuickTestConfiguration::QuickTestConfiguration(ITestFramework *framework)
setMixedDebugging(true);
}
-TestOutputReader *QuickTestConfiguration::createOutputReader(
- const QFutureInterface<TestResult> &fi, QtcProcess *app) const
+TestOutputReader *QuickTestConfiguration::createOutputReader(Process *app) const
{
auto qtSettings = static_cast<QtTestSettings *>(framework()->testSettings());
const QtTestOutputReader::OutputMode mode = qtSettings && qtSettings->useXMLOutput.value()
? QtTestOutputReader::XML
: QtTestOutputReader::PlainText;
- return new QtTestOutputReader(fi, app, buildDirectory(), projectFile(),
- mode, TestType::QuickTest);
+ return new QtTestOutputReader(app, buildDirectory(), projectFile(), mode, TestType::QuickTest);
}
QStringList QuickTestConfiguration::argumentsForTestRunner(QStringList *omitted) const
{
QStringList arguments;
- if (AutotestPlugin::settings()->processArgs) {
+ if (TestSettings::instance()->processArgs()) {
arguments.append(QTestUtils::filterInterfering
(runnable().command.arguments().split(' ', Qt::SkipEmptyParts),
omitted, true));
diff --git a/src/plugins/autotest/quick/quicktestconfiguration.h b/src/plugins/autotest/quick/quicktestconfiguration.h
index 84e374ebe8..33a07b0294 100644
--- a/src/plugins/autotest/quick/quicktestconfiguration.h
+++ b/src/plugins/autotest/quick/quicktestconfiguration.h
@@ -12,8 +12,7 @@ class QuickTestConfiguration : public DebuggableTestConfiguration
{
public:
explicit QuickTestConfiguration(ITestFramework *framework);
- TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
- Utils::QtcProcess *app) const override;
+ TestOutputReader *createOutputReader(Utils::Process *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};
diff --git a/src/plugins/autotest/quick/quicktestparser.cpp b/src/plugins/autotest/quick/quicktestparser.cpp
index 2c72cdf3f1..a4cf0967b6 100644
--- a/src/plugins/autotest/quick/quicktestparser.cpp
+++ b/src/plugins/autotest/quick/quicktestparser.cpp
@@ -12,14 +12,19 @@
#include <cppeditor/cppmodelmanager.h>
#include <cppeditor/projectpart.h>
-#include <projectexplorer/session.h>
+
+#include <projectexplorer/projectmanager.h>
+
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/qmljsdialect.h>
#include <qmljstools/qmljsmodelmanager.h>
+
#include <utils/hostosinfo.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
+#include <QPromise>
+
using namespace QmlJS;
using namespace Utils;
@@ -155,10 +160,9 @@ QList<Document::Ptr> QuickTestParser::scanDirectoryForQuickTestQmlFiles(const Fi
QStringList dirsStr({srcDir.toString()});
ModelManagerInterface *qmlJsMM = QmlJSTools::Internal::ModelManager::instance();
// make sure even files not listed in pro file are available inside the snapshot
- QFutureInterface<void> future;
PathsAndLanguages paths;
paths.maybeInsert(srcDir, Dialect::Qml);
- ModelManagerInterface::importScan(future, ModelManagerInterface::workingCopy(), paths, qmlJsMM,
+ ModelManagerInterface::importScan(ModelManagerInterface::workingCopy(), paths, qmlJsMM,
false /*emitDocumentChanges*/, false /*onlyTheLib*/, true /*forceRescan*/ );
const Snapshot snapshot = QmlJSTools::Internal::ModelManager::instance()->snapshot();
@@ -196,7 +200,7 @@ QList<Document::Ptr> QuickTestParser::scanDirectoryForQuickTestQmlFiles(const Fi
return foundDocs;
}
-static bool checkQmlDocumentForQuickTestCode(QFutureInterface<TestParseResultPtr> &futureInterface,
+static bool checkQmlDocumentForQuickTestCode(QPromise<TestParseResultPtr> &promise,
const Document::Ptr &qmlJSDoc,
ITestFramework *framework,
const FilePath &proFile = {},
@@ -240,12 +244,12 @@ static bool checkQmlDocumentForQuickTestCode(QFutureInterface<TestParseResultPtr
parseResult->children.append(funcResult);
}
- futureInterface.reportResult(TestParseResultPtr(parseResult));
+ promise.addResult(TestParseResultPtr(parseResult));
}
return true;
}
-bool QuickTestParser::handleQtQuickTest(QFutureInterface<TestParseResultPtr> &futureInterface,
+bool QuickTestParser::handleQtQuickTest(QPromise<TestParseResultPtr> &promise,
CPlusPlus::Document::Ptr document,
ITestFramework *framework)
{
@@ -263,17 +267,14 @@ bool QuickTestParser::handleQtQuickTest(QFutureInterface<TestParseResultPtr> &fu
if (srcDir.isEmpty())
return false;
- if (futureInterface.isCanceled())
+ if (promise.isCanceled())
return false;
const QList<Document::Ptr> qmlDocs = scanDirectoryForQuickTestQmlFiles(srcDir);
bool result = false;
for (const Document::Ptr &qmlJSDoc : qmlDocs) {
- if (futureInterface.isCanceled())
+ if (promise.isCanceled())
break;
- result |= checkQmlDocumentForQuickTestCode(futureInterface,
- qmlJSDoc,
- framework,
- proFile,
+ result |= checkQmlDocumentForQuickTestCode(promise, qmlJSDoc, framework, proFile,
m_checkForDerivedTests);
}
return result;
@@ -305,9 +306,8 @@ void QuickTestParser::handleDirectoryChanged(const QString &directory)
m_watchedFiles[directory] = filesAndDates;
PathsAndLanguages paths;
paths.maybeInsert(FilePath::fromString(directory), Dialect::Qml);
- QFutureInterface<void> future;
ModelManagerInterface *qmlJsMM = ModelManagerInterface::instance();
- ModelManagerInterface::importScan(future, ModelManagerInterface::workingCopy(), paths,
+ ModelManagerInterface::importScan(ModelManagerInterface::workingCopy(), paths,
qmlJsMM,
true /*emitDocumentChanges*/,
false /*onlyTheLib*/,
@@ -327,8 +327,8 @@ void QuickTestParser::doUpdateWatchPaths(const QStringList &directories)
QuickTestParser::QuickTestParser(ITestFramework *framework)
: CppParser(framework)
{
- connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::startupProjectChanged, this, [this] {
+ connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged, this, [this] {
const QStringList &dirs = m_directoryWatcher.directories();
if (!dirs.isEmpty())
m_directoryWatcher.removePaths(dirs);
@@ -338,7 +338,7 @@ QuickTestParser::QuickTestParser(ITestFramework *framework)
this, &QuickTestParser::handleDirectoryChanged);
}
-void QuickTestParser::init(const FilePaths &filesToParse, bool fullParse)
+void QuickTestParser::init(const QSet<FilePath> &filesToParse, bool fullParse)
{
m_qmlSnapshot = QmlJSTools::Internal::ModelManager::instance()->snapshot();
if (!fullParse) {
@@ -370,7 +370,7 @@ void QuickTestParser::release()
CppParser::release();
}
-bool QuickTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+bool QuickTestParser::processDocument(QPromise<TestParseResultPtr> &promise,
const FilePath &fileName)
{
if (fileName.endsWith(".qml")) {
@@ -378,7 +378,7 @@ bool QuickTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futu
if (proFile.isEmpty())
return false;
Document::Ptr qmlJSDoc = m_qmlSnapshot.document(fileName);
- return checkQmlDocumentForQuickTestCode(futureInterface,
+ return checkQmlDocumentForQuickTestCode(promise,
qmlJSDoc,
framework(),
proFile,
@@ -389,7 +389,7 @@ bool QuickTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futu
if (cppdoc.isNull() || !includesQtQuickTest(cppdoc, m_cppSnapshot))
return false;
- return handleQtQuickTest(futureInterface, cppdoc, framework());
+ return handleQtQuickTest(promise, cppdoc, framework());
}
FilePath QuickTestParser::projectFileForMainCppFile(const FilePath &fileName) const
diff --git a/src/plugins/autotest/quick/quicktestparser.h b/src/plugins/autotest/quick/quicktestparser.h
index c0fbc5a3e8..dfd71333ea 100644
--- a/src/plugins/autotest/quick/quicktestparser.h
+++ b/src/plugins/autotest/quick/quicktestparser.h
@@ -24,15 +24,15 @@ class QuickTestParser : public QObject, public CppParser
Q_OBJECT
public:
explicit QuickTestParser(ITestFramework *framework);
- void init(const Utils::FilePaths &filesToParse, bool fullParse) override;
+ void init(const QSet<Utils::FilePath> &filesToParse, bool fullParse) override;
void release() override;
- bool processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+ bool processDocument(QPromise<TestParseResultPtr> &promise,
const Utils::FilePath &fileName) override;
Utils::FilePath projectFileForMainCppFile(const Utils::FilePath &fileName) const;
QStringList supportedExtensions() const override { return {"qml"}; };
private:
- bool handleQtQuickTest(QFutureInterface<TestParseResultPtr> &futureInterface,
+ bool handleQtQuickTest(QPromise<TestParseResultPtr> &promise,
CPlusPlus::Document::Ptr document,
ITestFramework *framework);
void handleDirectoryChanged(const QString &directory);
diff --git a/src/plugins/autotest/quick/quicktesttreeitem.cpp b/src/plugins/autotest/quick/quicktesttreeitem.cpp
index f721ce3bc0..88f43e1541 100644
--- a/src/plugins/autotest/quick/quicktesttreeitem.cpp
+++ b/src/plugins/autotest/quick/quicktesttreeitem.cpp
@@ -9,7 +9,9 @@
#include "../itestframework.h"
#include <cppeditor/cppmodelmanager.h>
-#include <projectexplorer/session.h>
+
+#include <projectexplorer/projectmanager.h>
+
#include <utils/qtcassert.h>
using namespace Utils;
@@ -108,7 +110,7 @@ bool QuickTestTreeItem::canProvideDebugConfiguration() const
ITestConfiguration *QuickTestTreeItem::testConfiguration() const
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(project, return nullptr);
QuickTestConfiguration *config = nullptr;
@@ -147,7 +149,7 @@ static QList<ITestConfiguration *> testConfigurationsFor(
const TestTreeItem *rootNode, const std::function<bool(TestTreeItem *)> &predicate)
{
QTC_ASSERT(rootNode, return {});
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || rootNode->type() != TestTreeItem::Root)
return {};
@@ -171,7 +173,7 @@ static QList<ITestConfiguration *> testConfigurationsFor(
if (it == configurationForProFiles.end()) {
auto tc = new QuickTestConfiguration(treeItem->framework());
tc->setProjectFile(treeItem->proFile());
- tc->setProject(ProjectExplorer::SessionManager::startupProject());
+ tc->setProject(ProjectExplorer::ProjectManager::startupProject());
tc->setInternalTargets(internalTargets(treeItem->proFile()));
it = configurationForProFiles.insert(treeItem->proFile(), tc);
}
@@ -206,7 +208,7 @@ QList<ITestConfiguration *> QuickTestTreeItem::getAllTestConfigurations() const
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
@@ -369,7 +371,7 @@ QSet<QString> internalTargets(const FilePath &proFile)
{
QSet<QString> result;
const auto cppMM = CppEditor::CppModelManager::instance();
- const auto projectInfo = cppMM->projectInfo(ProjectExplorer::SessionManager::startupProject());
+ const auto projectInfo = cppMM->projectInfo(ProjectExplorer::ProjectManager::startupProject());
if (!projectInfo)
return {};
for (const CppEditor::ProjectPart::ConstPtr &projectPart : projectInfo->projectParts()) {
@@ -387,17 +389,19 @@ QSet<QString> internalTargets(const FilePath &proFile)
return result;
}
-void QuickTestTreeItem::markForRemovalRecursively(const FilePath &filePath)
+void QuickTestTreeItem::markForRemovalRecursively(const QSet<FilePath> &filePaths)
{
- TestTreeItem::markForRemovalRecursively(filePath);
+ TestTreeItem::markForRemovalRecursively(filePaths);
auto parser = static_cast<QuickTestParser *>(framework()->testParser());
- const FilePath proFile = parser->projectFileForMainCppFile(filePath);
- if (!proFile.isEmpty()) {
- TestTreeItem *root = framework()->rootNode();
- root->forAllChildItems([proFile](TestTreeItem *it) {
- if (it->proFile() == proFile)
- it->markForRemoval(true);
- });
+ for (const FilePath &filePath : filePaths) {
+ const FilePath proFile = parser->projectFileForMainCppFile(filePath);
+ if (!proFile.isEmpty()) {
+ TestTreeItem *root = framework()->rootNode();
+ root->forAllChildItems([proFile](TestTreeItem *it) {
+ if (it->proFile() == proFile)
+ it->markForRemoval(true);
+ });
+ }
}
}
diff --git a/src/plugins/autotest/quick/quicktesttreeitem.h b/src/plugins/autotest/quick/quicktesttreeitem.h
index adbf95fa8b..c09bd97b98 100644
--- a/src/plugins/autotest/quick/quicktesttreeitem.h
+++ b/src/plugins/autotest/quick/quicktesttreeitem.h
@@ -35,7 +35,7 @@ public:
bool removeOnSweepIfEmpty() const override;
TestTreeItem *createParentGroupNode() const override;
bool isGroupable() const override;
- void markForRemovalRecursively(const Utils::FilePath &filePath) override;
+ void markForRemovalRecursively(const QSet<Utils::FilePath> &filePaths) override;
private:
TestTreeItem *findChildByFileNameAndType(const Utils::FilePath &filePath, const QString &name,
Type tType);
diff --git a/src/plugins/autotest/testcodeparser.cpp b/src/plugins/autotest/testcodeparser.cpp
index edf126f8d4..8553702432 100644
--- a/src/plugins/autotest/testcodeparser.cpp
+++ b/src/plugins/autotest/testcodeparser.cpp
@@ -8,24 +8,21 @@
#include "testtreemodel.h"
#include <coreplugin/editormanager/editormanager.h>
-#include <coreplugin/progressmanager/futureprogress.h>
#include <coreplugin/progressmanager/progressmanager.h>
+#include <coreplugin/progressmanager/taskprogress.h>
#include <cppeditor/cppeditorconstants.h>
#include <cppeditor/cppmodelmanager.h>
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
-#include <qmljstools/qmljsmodelmanager.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/algorithm.h>
-#include <utils/mapreduce.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
-#include <QFuture>
-#include <QFutureInterface>
#include <QLoggingCategory>
+using namespace Core;
using namespace Utils;
namespace Autotest {
@@ -37,33 +34,30 @@ using namespace ProjectExplorer;
static bool isProjectParsing()
{
- const BuildSystem *bs = SessionManager::startupBuildSystem();
+ const BuildSystem *bs = ProjectManager::startupBuildSystem();
return bs && bs->isParsing();
}
TestCodeParser::TestCodeParser()
- : m_threadPool(new QThreadPool(this))
{
// connect to ProgressManager to postpone test parsing when CppModelManager is parsing
- auto progressManager = qobject_cast<Core::ProgressManager *>(Core::ProgressManager::instance());
- connect(progressManager, &Core::ProgressManager::taskStarted,
+ ProgressManager *progressManager = ProgressManager::instance();
+ connect(progressManager, &ProgressManager::taskStarted,
this, &TestCodeParser::onTaskStarted);
- connect(progressManager, &Core::ProgressManager::allTasksFinished,
+ connect(progressManager, &ProgressManager::allTasksFinished,
this, &TestCodeParser::onAllTasksFinished);
- connect(&m_futureWatcher, &QFutureWatcher<TestParseResultPtr>::started,
- this, &TestCodeParser::parsingStarted);
- connect(&m_futureWatcher, &QFutureWatcher<TestParseResultPtr>::finished,
- this, &TestCodeParser::onFinished);
- connect(&m_futureWatcher, &QFutureWatcher<TestParseResultPtr>::resultReadyAt,
- this, [this](int index) {
- emit testParseResultReady(m_futureWatcher.resultAt(index));
- });
connect(this, &TestCodeParser::parsingFinished, this, &TestCodeParser::releaseParserInternals);
+ connect(EditorManager::instance(), &EditorManager::documentClosed, this, [this](IDocument *doc){
+ QTC_ASSERT(doc, return);
+ if (FilePath filePath = doc->filePath(); filePath.endsWith(".qml"))
+ m_qmlEditorRev.remove(filePath);
+ });
m_reparseTimer.setSingleShot(true);
connect(&m_reparseTimer, &QTimer::timeout, this, &TestCodeParser::parsePostponedFiles);
- m_threadPool->setMaxThreadCount(std::max(QThread::idealThreadCount()/4, 1));
}
+TestCodeParser::~TestCodeParser() = default;
+
void TestCodeParser::setState(State state)
{
if (m_parserState == Shutdown)
@@ -82,14 +76,14 @@ void TestCodeParser::setState(State state)
}
m_parserState = state;
- if (m_parserState == Idle && SessionManager::startupProject()) {
+ if (m_parserState == Idle && ProjectManager::startupProject()) {
if (m_postponedUpdateType == UpdateType::FullUpdate || m_dirty) {
emitUpdateTestTree();
} else if (m_postponedUpdateType == UpdateType::PartialUpdate) {
m_postponedUpdateType = UpdateType::NoUpdate;
qCDebug(LOG) << "calling scanForTests with postponed files (setState)";
if (!m_reparseTimer.isActive())
- scanForTests(Utils::toList(m_postponedFiles));
+ scanForTests(m_postponedFiles);
}
}
}
@@ -100,7 +94,7 @@ void TestCodeParser::syncTestFrameworks(const QList<ITestParser *> &parsers)
// there's a running parse
m_postponedUpdateType = UpdateType::NoUpdate;
m_postponedFiles.clear();
- Core::ProgressManager::cancelTasks(Constants::TASK_PARSE);
+ ProgressManager::cancelTasks(Constants::TASK_PARSE);
}
qCDebug(LOG) << "Setting" << parsers << "as current parsers";
m_testCodeParsers = parsers;
@@ -139,7 +133,7 @@ void TestCodeParser::updateTestTree(const QSet<ITestParser *> &parsers)
return;
}
- if (!SessionManager::startupProject())
+ if (!ProjectManager::startupProject())
return;
m_postponedUpdateType = UpdateType::NoUpdate;
@@ -158,7 +152,7 @@ void TestCodeParser::onDocumentUpdated(const FilePath &fileName, bool isQmlFile)
if (isProjectParsing() || m_codeModelParsing || m_postponedUpdateType == UpdateType::FullUpdate)
return;
- Project *project = SessionManager::startupProject();
+ Project *project = ProjectManager::startupProject();
if (!project)
return;
// Quick tests: qml files aren't necessarily listed inside project files
@@ -177,15 +171,20 @@ void TestCodeParser::onQmlDocumentUpdated(const QmlJS::Document::Ptr &document)
{
static const QStringList ignoredSuffixes{ "qbs", "ui.qml" };
const FilePath fileName = document->fileName();
- if (!ignoredSuffixes.contains(fileName.suffix()))
- onDocumentUpdated(fileName, true);
+ int editorRevision = document->editorRevision();
+ if (editorRevision != m_qmlEditorRev.value(fileName, 0)) {
+ m_qmlEditorRev.insert(fileName, editorRevision);
+ if (!ignoredSuffixes.contains(fileName.suffix()))
+ onDocumentUpdated(fileName, true);
+ }
}
void TestCodeParser::onStartupProjectChanged(Project *project)
{
+ m_qmlEditorRev.clear();
if (m_parserState == FullParse || m_parserState == PartialParse) {
qCDebug(LOG) << "Canceling scanForTest (startup project changed)";
- Core::ProgressManager::cancelTasks(Constants::TASK_PARSE);
+ ProgressManager::cancelTasks(Constants::TASK_PARSE);
}
emit aboutToPerformFullParse();
if (project)
@@ -194,7 +193,7 @@ void TestCodeParser::onStartupProjectChanged(Project *project)
void TestCodeParser::onProjectPartsUpdated(Project *project)
{
- if (project != SessionManager::startupProject())
+ if (project != ProjectManager::startupProject())
return;
if (isProjectParsing() || m_codeModelParsing)
m_postponedUpdateType = UpdateType::FullUpdate;
@@ -205,35 +204,33 @@ void TestCodeParser::onProjectPartsUpdated(Project *project)
void TestCodeParser::aboutToShutdown()
{
qCDebug(LOG) << "Disabling (immediately) - shutting down";
- State oldState = m_parserState;
m_parserState = Shutdown;
- if (oldState == PartialParse || oldState == FullParse) {
- m_futureWatcher.cancel();
- m_futureWatcher.waitForFinished();
- }
+ m_taskTree.reset();
+ m_futureSynchronizer.waitForFinished();
}
-bool TestCodeParser::postponed(const FilePaths &fileList)
+bool TestCodeParser::postponed(const QSet<FilePath> &filePaths)
{
switch (m_parserState) {
case Idle:
- if (fileList.size() == 1) {
+ if (filePaths.size() == 1) {
if (m_reparseTimerTimedOut)
return false;
+ const FilePath filePath = *filePaths.begin();
switch (m_postponedFiles.size()) {
case 0:
- m_postponedFiles.insert(fileList.first());
+ m_postponedFiles.insert(filePath);
m_reparseTimer.setInterval(1000);
m_reparseTimer.start();
return true;
case 1:
- if (m_postponedFiles.contains(fileList.first())) {
+ if (m_postponedFiles.contains(filePath)) {
m_reparseTimer.start();
return true;
}
Q_FALLTHROUGH();
default:
- m_postponedFiles.insert(fileList.first());
+ m_postponedFiles.insert(filePath);
m_reparseTimer.stop();
m_reparseTimer.setInterval(0);
m_reparseTimerTimedOut = false;
@@ -245,18 +242,17 @@ bool TestCodeParser::postponed(const FilePaths &fileList)
case PartialParse:
case FullParse:
// parse is running, postponing a full parse
- if (fileList.isEmpty()) {
+ if (filePaths.isEmpty()) {
m_postponedFiles.clear();
m_postponedUpdateType = UpdateType::FullUpdate;
qCDebug(LOG) << "Canceling scanForTest (full parse triggered while running a scan)";
- Core::ProgressManager::cancelTasks(Constants::TASK_PARSE);
+ ProgressManager::cancelTasks(Constants::TASK_PARSE);
} else {
// partial parse triggered, but full parse is postponed already, ignoring this
if (m_postponedUpdateType == UpdateType::FullUpdate)
return true;
// partial parse triggered, postpone or add current files to already postponed partial
- for (const FilePath &file : fileList)
- m_postponedFiles.insert(file);
+ m_postponedFiles += filePaths;
m_postponedUpdateType = UpdateType::PartialUpdate;
}
return true;
@@ -266,43 +262,43 @@ bool TestCodeParser::postponed(const FilePaths &fileList)
QTC_ASSERT(false, return false); // should not happen at all
}
-static void parseFileForTests(const QList<ITestParser *> &parsers,
- QFutureInterface<TestParseResultPtr> &futureInterface,
- const FilePath &fileName)
+static void parseFileForTests(QPromise<TestParseResultPtr> &promise,
+ const QList<ITestParser *> &parsers, const FilePath &fileName)
{
for (ITestParser *parser : parsers) {
- if (futureInterface.isCanceled())
+ if (promise.isCanceled())
return;
- if (parser->processDocument(futureInterface, fileName))
+ if (parser->processDocument(promise, fileName))
break;
}
}
-void TestCodeParser::scanForTests(const FilePaths &fileList, const QList<ITestParser *> &parsers)
+void TestCodeParser::scanForTests(const QSet<FilePath> &filePaths,
+ const QList<ITestParser *> &parsers)
{
if (m_parserState == Shutdown || m_testCodeParsers.isEmpty())
return;
- if (postponed(fileList))
+ if (postponed(filePaths))
return;
+ QSet<FilePath> files = filePaths; // avoid getting cleared if m_postponedFiles have been passed
m_reparseTimer.stop();
m_reparseTimerTimedOut = false;
m_postponedFiles.clear();
- bool isFullParse = fileList.isEmpty();
- Project *project = SessionManager::startupProject();
+ const bool isFullParse = files.isEmpty();
+ Project *project = ProjectManager::startupProject();
if (!project)
return;
- FilePaths list;
if (isFullParse) {
- list = project->files(Project::SourceFiles);
- if (list.isEmpty()) {
+ files = Utils::toSet(project->files(Project::SourceFiles));
+ if (files.isEmpty()) {
// at least project file should be there, but might happen if parsing current project
// takes too long, especially when opening sessions holding multiple projects
qCDebug(LOG) << "File list empty (FullParse) - trying again in a sec";
emitUpdateTestTree();
return;
- } else if (list.size() == 1 && list.first() == project->projectFilePath()) {
+ } else if (files.size() == 1 && *files.constBegin() == project->projectFilePath()) {
qCDebug(LOG) << "File list contains only the project file.";
return;
}
@@ -310,7 +306,6 @@ void TestCodeParser::scanForTests(const FilePaths &fileList, const QList<ITestPa
qCDebug(LOG) << "setting state to FullParse (scanForTests)";
m_parserState = FullParse;
} else {
- list << fileList;
qCDebug(LOG) << "setting state to PartialParse (scanForTests)";
m_parserState = PartialParse;
}
@@ -319,43 +314,39 @@ void TestCodeParser::scanForTests(const FilePaths &fileList, const QList<ITestPa
TestTreeModel::instance()->updateCheckStateCache();
if (isFullParse) {
// remove qml files as they will be found automatically by the referencing cpp file
- list = Utils::filtered(list, [](const FilePath &fn) {
- return !fn.endsWith(".qml");
- });
+ files = Utils::filtered(files, [](const FilePath &fn) { return !fn.endsWith(".qml"); });
if (!parsers.isEmpty()) {
- for (ITestParser *parser : parsers) {
+ for (ITestParser *parser : parsers)
parser->framework()->rootNode()->markForRemovalRecursively(true);
- }
} else {
emit requestRemoveAllFrameworkItems();
}
} else if (!parsers.isEmpty()) {
for (ITestParser *parser: parsers) {
- for (const FilePath &filePath : std::as_const(list))
- parser->framework()->rootNode()->markForRemovalRecursively(filePath);
+ parser->framework()->rootNode()->markForRemovalRecursively(files);
}
} else {
- for (const FilePath &filePath : std::as_const(list))
- emit requestRemoval(filePath);
+ emit requestRemoval(files);
}
- QTC_ASSERT(!(isFullParse && list.isEmpty()), onFinished(); return);
+ QTC_ASSERT(!(isFullParse && files.isEmpty()), onFinished(true); return);
// use only a single parser or all current active?
const QList<ITestParser *> codeParsers = parsers.isEmpty() ? m_testCodeParsers : parsers;
qCDebug(LOG) << QDateTime::currentDateTime().toString("hh:mm:ss.zzz") << "StartParsing";
+ m_parsingTimer.restart();
QSet<QString> extensions;
const auto cppSnapshot = CppEditor::CppModelManager::instance()->snapshot();
for (ITestParser *parser : codeParsers) {
- parser->init(list, isFullParse);
+ parser->init(files, isFullParse);
for (const QString &ext : parser->supportedExtensions())
extensions.insert(ext);
}
// We are only interested in files that have been either parsed by the c++ parser,
// or have an extension that one of the parsers is specifically interested in.
- const FilePaths filteredList
- = Utils::filtered(list, [&extensions, &cppSnapshot](const FilePath &fn) {
+ const QSet<FilePath> filteredFiles
+ = Utils::filtered(files, [&extensions, &cppSnapshot](const FilePath &fn) {
const bool isSupportedExtension = Utils::anyOf(extensions, [&fn](const QString &ext) {
return fn.suffix() == ext;
});
@@ -364,21 +355,37 @@ void TestCodeParser::scanForTests(const FilePaths &fileList, const QList<ITestPa
return cppSnapshot.contains(fn);
});
- qCDebug(LOG) << "Starting scan of" << filteredList.size() << "(" << list.size() << ")"
+ qCDebug(LOG) << "Starting scan of" << filteredFiles.size() << "(" << files.size() << ")"
<< "files with" << codeParsers.size() << "parsers";
- QFuture<TestParseResultPtr> future = Utils::map(filteredList,
- [codeParsers](QFutureInterface<TestParseResultPtr> &fi, const FilePath &file) {
- parseFileForTests(codeParsers, fi, file);
- },
- MapReduceOption::Unordered,
- m_threadPool,
- QThread::LowestPriority);
- m_futureWatcher.setFuture(future);
- if (filteredList.size() > 5) {
- Core::ProgressManager::addTask(future, Tr::tr("Scanning for Tests"),
- Autotest::Constants::TASK_PARSE);
+ using namespace Tasking;
+
+ QList<TaskItem> tasks{parallelLimit(std::max(QThread::idealThreadCount() / 4, 1))};
+ for (const FilePath &file : filteredFiles) {
+ const auto setup = [this, codeParsers, file](Async<TestParseResultPtr> &async) {
+ async.setConcurrentCallData(parseFileForTests, codeParsers, file);
+ async.setPriority(QThread::LowestPriority);
+ async.setFutureSynchronizer(&m_futureSynchronizer);
+ };
+ const auto onDone = [this](const Async<TestParseResultPtr> &async) {
+ const QList<TestParseResultPtr> results = async.results();
+ for (const TestParseResultPtr &result : results)
+ emit testParseResultReady(result);
+ };
+ tasks.append(AsyncTask<TestParseResultPtr>(setup, onDone));
+ }
+ m_taskTree.reset(new TaskTree{tasks});
+ const auto onDone = [this] { m_taskTree.release()->deleteLater(); onFinished(true); };
+ const auto onError = [this] { m_taskTree.release()->deleteLater(); onFinished(false); };
+ connect(m_taskTree.get(), &TaskTree::started, this, &TestCodeParser::parsingStarted);
+ connect(m_taskTree.get(), &TaskTree::done, this, onDone);
+ connect(m_taskTree.get(), &TaskTree::errorOccurred, this, onError);
+ if (filteredFiles.size() > 5) {
+ auto progress = new TaskProgress(m_taskTree.get());
+ progress->setDisplayName(Tr::tr("Scanning for Tests"));
+ progress->setId(Constants::TASK_PARSE);
}
+ m_taskTree->start();
}
void TestCodeParser::onTaskStarted(Id type)
@@ -390,7 +397,7 @@ void TestCodeParser::onTaskStarted(Id type)
? UpdateType::FullUpdate : UpdateType::PartialUpdate;
qCDebug(LOG) << "Canceling scan for test (CppModelParsing started)";
m_parsingHasFailed = true;
- Core::ProgressManager::cancelTasks(Constants::TASK_PARSE);
+ ProgressManager::cancelTasks(Constants::TASK_PARSE);
}
}
}
@@ -410,10 +417,9 @@ void TestCodeParser::onAllTasksFinished(Id type)
setState(Idle);
}
-void TestCodeParser::onFinished()
+void TestCodeParser::onFinished(bool success)
{
- if (m_futureWatcher.isCanceled())
- m_parsingHasFailed = true;
+ m_parsingHasFailed = !success;
switch (m_parserState) {
case PartialParse:
qCDebug(LOG) << "setting state to Idle (onFinished, PartialParse)";
@@ -433,6 +439,7 @@ void TestCodeParser::onFinished()
m_updateParsers.clear();
emit parsingFinished();
qCDebug(LOG) << QDateTime::currentDateTime().toString("hh:mm:ss.zzz") << "ParsingFin";
+ qCDebug(LOG) << "Parsing took:" << m_parsingTimer.elapsed() << "ms";
}
m_dirty = false;
break;
@@ -457,7 +464,7 @@ void TestCodeParser::onPartialParsingFinished()
case UpdateType::PartialUpdate:
qCDebug(LOG) << "calling scanForTests with postponed files (onPartialParsingFinished)";
if (!m_reparseTimer.isActive())
- scanForTests(Utils::toList(m_postponedFiles));
+ scanForTests(m_postponedFiles);
break;
case UpdateType::NoUpdate:
m_dirty |= m_codeModelParsing;
@@ -481,7 +488,7 @@ void TestCodeParser::onPartialParsingFinished()
void TestCodeParser::parsePostponedFiles()
{
m_reparseTimerTimedOut = true;
- scanForTests(Utils::toList(m_postponedFiles));
+ scanForTests(m_postponedFiles);
}
void TestCodeParser::releaseParserInternals()
diff --git a/src/plugins/autotest/testcodeparser.h b/src/plugins/autotest/testcodeparser.h
index efd0d602e4..2dd81d93d6 100644
--- a/src/plugins/autotest/testcodeparser.h
+++ b/src/plugins/autotest/testcodeparser.h
@@ -6,10 +6,10 @@
#include "itestparser.h"
#include <qmljs/qmljsdocument.h>
+
+#include <utils/futuresynchronizer.h>
#include <utils/id.h>
-#include <QFutureWatcher>
-#include <QMap>
#include <QObject>
#include <QTimer>
@@ -18,9 +18,9 @@ class QThreadPool;
QT_END_NAMESPACE
namespace ProjectExplorer { class Project; }
+namespace Tasking { class TaskTree; }
namespace Autotest {
-
namespace Internal {
class TestCodeParser : public QObject
@@ -35,6 +35,7 @@ public:
};
TestCodeParser();
+ ~TestCodeParser();
void setState(State state);
State state() const { return m_parserState; }
@@ -48,11 +49,11 @@ public:
signals:
void aboutToPerformFullParse();
- void testParseResultReady(const TestParseResultPtr result);
+ void testParseResultReady(const TestParseResultPtr result); // TODO: pass list of results?
void parsingStarted();
void parsingFinished();
void parsingFailed();
- void requestRemoval(const Utils::FilePath &filePath);
+ void requestRemoval(const QSet<Utils::FilePath> &filePaths);
void requestRemoveAllFrameworkItems();
public:
@@ -65,15 +66,15 @@ public:
void aboutToShutdown();
private:
- bool postponed(const Utils::FilePaths &fileList);
- void scanForTests(const Utils::FilePaths &fileList = Utils::FilePaths(),
+ bool postponed(const QSet<Utils::FilePath> &fileList);
+ void scanForTests(const QSet<Utils::FilePath> &filePaths = {},
const QList<ITestParser *> &parsers = {});
// qml files must be handled slightly different
void onDocumentUpdated(const Utils::FilePath &fileName, bool isQmlFile = false);
void onTaskStarted(Utils::Id type);
void onAllTasksFinished(Utils::Id type);
- void onFinished();
+ void onFinished(bool success);
void onPartialParsingFinished();
void parsePostponedFiles();
void releaseParserInternals();
@@ -83,21 +84,21 @@ private:
bool m_parsingHasFailed = false;
bool m_codeModelParsing = false;
- enum class UpdateType {
- NoUpdate,
- PartialUpdate,
- FullUpdate
- } m_postponedUpdateType = UpdateType::NoUpdate;
+ enum class UpdateType { NoUpdate, PartialUpdate, FullUpdate };
+ UpdateType m_postponedUpdateType = UpdateType::NoUpdate;
bool m_dirty = false;
bool m_singleShotScheduled = false;
bool m_reparseTimerTimedOut = false;
QSet<Utils::FilePath> m_postponedFiles;
State m_parserState = Idle;
- QFutureWatcher<TestParseResultPtr> m_futureWatcher;
QList<ITestParser *> m_testCodeParsers; // ptrs are still owned by TestFrameworkManager
QTimer m_reparseTimer;
QSet<ITestParser *> m_updateParsers;
- QThreadPool *m_threadPool = nullptr;
+ Utils::FutureSynchronizer m_futureSynchronizer;
+ std::unique_ptr<Tasking::TaskTree> m_taskTree;
+ QHash<Utils::FilePath, int> m_qmlEditorRev;
+
+ QElapsedTimer m_parsingTimer;
};
} // namespace Internal
diff --git a/src/plugins/autotest/testconfiguration.cpp b/src/plugins/autotest/testconfiguration.cpp
index 764ede697d..c095e9e0f6 100644
--- a/src/plugins/autotest/testconfiguration.cpp
+++ b/src/plugins/autotest/testconfiguration.cpp
@@ -4,22 +4,19 @@
#include "testconfiguration.h"
#include "itestframework.h"
-#include "testoutputreader.h"
#include "testrunconfiguration.h"
-#include <cppeditor/cppmodelmanager.h>
-#include <cppeditor/projectinfo.h>
-
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/buildtargetinfo.h>
#include <projectexplorer/deploymentdata.h>
-#include <projectexplorer/environmentaspect.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/runconfiguration.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
+#include <utils/algorithm.h>
+
#include <QLoggingCategory>
static Q_LOGGING_CATEGORY(LOG, "qtc.autotest.testconfiguration", QtWarningMsg)
@@ -29,7 +26,6 @@ using namespace Utils;
namespace Autotest {
-
ITestConfiguration::ITestConfiguration(ITestBase *testBase)
: m_testBase(testBase)
{
@@ -94,7 +90,7 @@ static FilePath ensureExeEnding(const FilePath &file)
return file.withExecutableSuffix();
}
-void TestConfiguration::completeTestInformation(ProjectExplorer::RunConfiguration *rc,
+void TestConfiguration::completeTestInformation(RunConfiguration *rc,
TestRunMode runMode)
{
QTC_ASSERT(rc, return);
@@ -104,7 +100,7 @@ void TestConfiguration::completeTestInformation(ProjectExplorer::RunConfiguratio
qCDebug(LOG) << "Executable has been set already - not completing configuration again.";
return;
}
- Project *startupProject = SessionManager::startupProject();
+ Project *startupProject = ProjectManager::startupProject();
if (!startupProject || startupProject != project())
return;
@@ -149,7 +145,7 @@ void TestConfiguration::completeTestInformation(TestRunMode runMode)
}
qCDebug(LOG) << "Failed to complete - using 'normal' way.";
}
- Project *startupProject = SessionManager::startupProject();
+ Project *startupProject = ProjectManager::startupProject();
if (!startupProject || startupProject != project()) {
setProject(nullptr);
return;
diff --git a/src/plugins/autotest/testconfiguration.h b/src/plugins/autotest/testconfiguration.h
index d57416d016..6292ef8204 100644
--- a/src/plugins/autotest/testconfiguration.h
+++ b/src/plugins/autotest/testconfiguration.h
@@ -9,11 +9,10 @@
#include <projectexplorer/runcontrol.h>
#include <utils/environment.h>
-#include <QFutureInterface>
#include <QPointer>
#include <QStringList>
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace Autotest {
namespace Internal {
@@ -40,8 +39,7 @@ public:
Utils::FilePath executableFilePath() const;
virtual Utils::FilePath testExecutable() const { return executableFilePath(); };
- virtual TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
- Utils::QtcProcess *app) const = 0;
+ virtual TestOutputReader *createOutputReader(Utils::Process *app) const = 0;
virtual Utils::Environment filteredEnvironment(const Utils::Environment &original) const;
ITestBase *testBase() const { return m_testBase; }
diff --git a/src/plugins/autotest/testframeworkmanager.cpp b/src/plugins/autotest/testframeworkmanager.cpp
index 91fa313593..3c6aff581d 100644
--- a/src/plugins/autotest/testframeworkmanager.cpp
+++ b/src/plugins/autotest/testframeworkmanager.cpp
@@ -3,7 +3,6 @@
#include "testframeworkmanager.h"
-#include "autotestplugin.h"
#include "testsettings.h"
#include <utils/aspects.h>
@@ -98,7 +97,7 @@ ITestTool *TestFrameworkManager::testToolForBuildSystemId(Id buildSystemId)
void TestFrameworkManager::synchronizeSettings(QSettings *s)
{
- Internal::AutotestPlugin::settings()->fromSettings(s);
+ Internal::TestSettings::instance()->fromSettings(s);
for (ITestFramework *framework : std::as_const(m_registeredFrameworks)) {
if (ITestSettings *fSettings = framework->testSettings())
fSettings->readSettings(s);
diff --git a/src/plugins/autotest/testframeworkmanager.h b/src/plugins/autotest/testframeworkmanager.h
index 3eab62adbc..73dfd96ff9 100644
--- a/src/plugins/autotest/testframeworkmanager.h
+++ b/src/plugins/autotest/testframeworkmanager.h
@@ -11,7 +11,7 @@ QT_END_NAMESPACE
namespace Autotest {
namespace Internal {
-struct TestSettings;
+class TestSettings;
}
class TestFrameworkManager final
diff --git a/src/plugins/autotest/testnavigationwidget.cpp b/src/plugins/autotest/testnavigationwidget.cpp
index 100eed89ed..431f5a3af8 100644
--- a/src/plugins/autotest/testnavigationwidget.cpp
+++ b/src/plugins/autotest/testnavigationwidget.cpp
@@ -18,12 +18,15 @@
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/find/itemviewfind.h>
+
#include <projectexplorer/buildmanager.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
+
#include <utils/algorithm.h>
#include <utils/link.h>
#include <utils/progressindicator.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QAction>
@@ -87,8 +90,8 @@ TestNavigationWidget::TestNavigationWidget(QWidget *parent) :
connect(m_model, &TestTreeModel::updatedActiveFrameworks, this, [this](int numberOfActive) {
m_missingFrameworksWidget->setVisible(numberOfActive == 0);
});
- ProjectExplorer::SessionManager *sm = ProjectExplorer::SessionManager::instance();
- connect(sm, &ProjectExplorer::SessionManager::startupProjectChanged,
+ ProjectExplorer::ProjectManager *sm = ProjectExplorer::ProjectManager::instance();
+ connect(sm, &ProjectExplorer::ProjectManager::startupProjectChanged,
this, [this](ProjectExplorer::Project * /*project*/) {
m_expandedStateCache.clear();
});
@@ -190,7 +193,7 @@ QList<QToolButton *> TestNavigationWidget::createToolButtons()
m_filterButton = new QToolButton(m_view);
m_filterButton->setIcon(Utils::Icons::FILTER.icon());
m_filterButton->setToolTip(Tr::tr("Filter Test Tree"));
- m_filterButton->setProperty("noArrow", true);
+ m_filterButton->setProperty(StyleHelper::C_NO_ARROW, true);
m_filterButton->setPopupMode(QToolButton::InstantPopup);
m_filterMenu = new QMenu(m_filterButton);
initializeFilterMenu();
diff --git a/src/plugins/autotest/testoutputreader.cpp b/src/plugins/autotest/testoutputreader.cpp
index 2f1dc75e48..cef36c90e7 100644
--- a/src/plugins/autotest/testoutputreader.cpp
+++ b/src/plugins/autotest/testoutputreader.cpp
@@ -4,17 +4,12 @@
#include "testoutputreader.h"
#include "autotesttr.h"
-#include "testresult.h"
-#include "testresultspane.h"
#include "testtreeitem.h"
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <QDebug>
-#include <QDir>
-#include <QFileInfo>
-#include <QProcess>
+#include <QRegularExpression>
using namespace Utils;
@@ -26,11 +21,8 @@ FilePath TestOutputReader::constructSourceFilePath(const FilePath &path, const Q
return filePath.isReadableFile() ? filePath : FilePath();
}
-TestOutputReader::TestOutputReader(const QFutureInterface<TestResult> &futureInterface,
- QtcProcess *testApplication, const FilePath &buildDirectory)
- : m_futureInterface(futureInterface)
- , m_buildDir(buildDirectory)
- , m_id(testApplication ? testApplication->commandLine().executable().toUserOutput() : QString())
+TestOutputReader::TestOutputReader(Process *testApplication, const FilePath &buildDirectory)
+ : m_buildDir(buildDirectory)
{
auto chopLineBreak = [](QByteArray line) {
if (line.endsWith('\n'))
@@ -41,6 +33,9 @@ TestOutputReader::TestOutputReader(const QFutureInterface<TestResult> &futureInt
};
if (testApplication) {
+ connect(testApplication, &Process::started, this, [this, testApplication] {
+ m_id = testApplication->commandLine().executable().toUserOutput();
+ });
testApplication->setStdOutLineCallback([this, &chopLineBreak](const QString &line) {
processStdOutput(chopLineBreak(line.toUtf8()));
});
diff --git a/src/plugins/autotest/testoutputreader.h b/src/plugins/autotest/testoutputreader.h
index 55c645d87c..e87c463735 100644
--- a/src/plugins/autotest/testoutputreader.h
+++ b/src/plugins/autotest/testoutputreader.h
@@ -5,11 +5,9 @@
#include "testresult.h"
-#include <QFutureInterface>
#include <QObject>
-#include <QString>
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace Autotest {
@@ -17,8 +15,7 @@ class TestOutputReader : public QObject
{
Q_OBJECT
public:
- TestOutputReader(const QFutureInterface<TestResult> &futureInterface,
- Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory);
+ TestOutputReader(Utils::Process *testApplication, const Utils::FilePath &buildDirectory);
virtual ~TestOutputReader();
void processStdOutput(const QByteArray &outputLine);
virtual void processStdError(const QByteArray &outputLine);
@@ -46,7 +43,6 @@ protected:
void sendAndResetSanitizerResult();
void reportResult(const TestResult &result);
- QFutureInterface<TestResult> m_futureInterface;
Utils::FilePath m_buildDir;
QString m_id;
QHash<ResultType, int> m_summary;
diff --git a/src/plugins/autotest/testprojectsettings.cpp b/src/plugins/autotest/testprojectsettings.cpp
index 862fc8a24f..5f745637db 100644
--- a/src/plugins/autotest/testprojectsettings.cpp
+++ b/src/plugins/autotest/testprojectsettings.cpp
@@ -7,7 +7,7 @@
#include "testframeworkmanager.h"
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/algorithm.h>
#include <QLoggingCategory>
diff --git a/src/plugins/autotest/testresultdelegate.cpp b/src/plugins/autotest/testresultdelegate.cpp
index 9b55623d57..c14b6c1703 100644
--- a/src/plugins/autotest/testresultdelegate.cpp
+++ b/src/plugins/autotest/testresultdelegate.cpp
@@ -3,7 +3,6 @@
#include "testresultdelegate.h"
-#include "autotestplugin.h"
#include "testresultmodel.h"
#include "testsettings.h"
@@ -162,10 +161,10 @@ void TestResultDelegate::clearCache()
void TestResultDelegate::limitTextOutput(QString &output) const
{
- int maxLineCount = Internal::AutotestPlugin::settings()->resultDescriptionMaxSize;
+ int maxLineCount = Internal::TestSettings::instance()->resultDescriptionMaxSize();
bool limited = false;
- if (Internal::AutotestPlugin::settings()->limitResultDescription && maxLineCount > 0) {
+ if (Internal::TestSettings::instance()->limitResultDescription() && maxLineCount > 0) {
int index = -1;
int lastChar = output.size() - 1;
@@ -183,7 +182,7 @@ void TestResultDelegate::limitTextOutput(QString &output) const
}
}
- if (AutotestPlugin::settings()->limitResultOutput && output.length() > outputLimit) {
+ if (TestSettings::instance()->limitResultOutput() && output.length() > outputLimit) {
output = output.left(outputLimit);
limited = true;
}
diff --git a/src/plugins/autotest/testresultdelegate.h b/src/plugins/autotest/testresultdelegate.h
index c5a4919a71..f7d6aa1ab2 100644
--- a/src/plugins/autotest/testresultdelegate.h
+++ b/src/plugins/autotest/testresultdelegate.h
@@ -38,7 +38,6 @@ private:
public:
LayoutPositions(QStyleOptionViewItem &options, const TestResultFilterModel *filterModel)
: m_top(options.rect.top()),
- m_bottom(options.rect.bottom()),
m_left(options.rect.left()),
m_right(options.rect.right())
{
@@ -57,20 +56,15 @@ private:
int top() const { return m_top + ITEM_MARGIN; }
int left() const { return m_left + ITEM_MARGIN; }
int right() const { return m_right - ITEM_MARGIN; }
- int bottom() const { return m_bottom; }
int minimumHeight() const { return ICON_SIZE + 2 * ITEM_MARGIN; }
int iconSize() const { return ICON_SIZE; }
- int fontHeight() const { return m_fontHeight; }
int typeAreaLeft() const { return left() + ICON_SIZE + ITEM_SPACING; }
- int typeAreaWidth() const { return m_typeAreaWidth; }
int textAreaLeft() const { return typeAreaLeft() + m_typeAreaWidth + ITEM_SPACING; }
int textAreaWidth() const { return fileAreaLeft() - ITEM_SPACING - textAreaLeft(); }
int fileAreaLeft() const { return lineAreaLeft() - ITEM_SPACING - m_realFileLength; }
int lineAreaLeft() const { return right() - m_maxLineLength; }
- QRect typeArea() const { return QRect(typeAreaLeft(), top(),
- typeAreaWidth(), m_fontHeight); }
QRect textArea() const { return QRect(textAreaLeft(), top(),
textAreaWidth(), m_fontHeight); }
QRect fileArea() const { return QRect(fileAreaLeft(), top(),
@@ -84,7 +78,6 @@ private:
int m_maxLineLength;
int m_realFileLength;
int m_top;
- int m_bottom;
int m_left;
int m_right;
int m_fontHeight;
diff --git a/src/plugins/autotest/testresultmodel.cpp b/src/plugins/autotest/testresultmodel.cpp
index 434d647410..f686692faa 100644
--- a/src/plugins/autotest/testresultmodel.cpp
+++ b/src/plugins/autotest/testresultmodel.cpp
@@ -4,7 +4,6 @@
#include "testresultmodel.h"
#include "autotesticons.h"
-#include "autotestplugin.h"
#include "testrunner.h"
#include "testsettings.h"
#include "testtreeitem.h"
@@ -274,7 +273,7 @@ void TestResultModel::addTestResult(const TestResult &testResult, bool autoExpan
TestResultItem *newItem = new TestResultItem(testResult);
TestResultItem *root = nullptr;
- if (AutotestPlugin::settings()->displayApplication) {
+ if (TestSettings::instance()->displayApplication()) {
const QString application = testResult.id();
if (!application.isEmpty()) {
root = rootItem()->findFirstLevelChild([&application](TestResultItem *child) {
diff --git a/src/plugins/autotest/testresultspane.cpp b/src/plugins/autotest/testresultspane.cpp
index 87d44b0f2f..3dbad31ea9 100644
--- a/src/plugins/autotest/testresultspane.cpp
+++ b/src/plugins/autotest/testresultspane.cpp
@@ -197,7 +197,7 @@ void TestResultsPane::createToolButtons()
m_filterButton = new QToolButton(m_treeView);
m_filterButton->setIcon(Utils::Icons::FILTER.icon());
m_filterButton->setToolTip(Tr::tr("Filter Test Results"));
- m_filterButton->setProperty("noArrow", true);
+ m_filterButton->setProperty(StyleHelper::C_NO_ARROW, true);
m_filterButton->setPopupMode(QToolButton::InstantPopup);
m_filterMenu = new QMenu(m_filterButton);
initializeFilterMenu();
@@ -291,7 +291,7 @@ void TestResultsPane::clearContents()
setIconBadgeNumber(0);
navigateStateChanged();
m_summaryWidget->setVisible(false);
- m_autoScroll = AutotestPlugin::settings()->autoScroll;
+ m_autoScroll = TestSettings::instance()->autoScroll();
connect(m_treeView->verticalScrollBar(), &QScrollBar::rangeChanged,
this, &TestResultsPane::onScrollBarRangeChanged, Qt::UniqueConnection);
m_textOutput->clear();
@@ -437,7 +437,7 @@ void TestResultsPane::onRunSelectedTriggered()
void TestResultsPane::initializeFilterMenu()
{
- const bool omitIntern = AutotestPlugin::settings()->omitInternalMssg;
+ const bool omitIntern = TestSettings::instance()->omitInternalMsg();
// FilterModel has all messages enabled by default
if (omitIntern)
m_filterModel->toggleTestResultType(ResultType::MessageInternal);
@@ -553,8 +553,8 @@ void TestResultsPane::onTestRunFinished()
m_model->removeCurrentTestMessage();
disconnect(m_treeView->verticalScrollBar(), &QScrollBar::rangeChanged,
this, &TestResultsPane::onScrollBarRangeChanged);
- if (AutotestPlugin::settings()->popupOnFinish
- && (!AutotestPlugin::settings()->popupOnFail || hasFailedTests(m_model))) {
+ if (TestSettings::instance()->popupOnFinish()
+ && (!TestSettings::instance()->popupOnFail() || hasFailedTests(m_model))) {
popup(IOutputPane::NoModeSwitch);
}
createMarks();
diff --git a/src/plugins/autotest/testrunner.cpp b/src/plugins/autotest/testrunner.cpp
index cb01d0d9a2..5c53d742e6 100644
--- a/src/plugins/autotest/testrunner.cpp
+++ b/src/plugins/autotest/testrunner.cpp
@@ -6,18 +6,15 @@
#include "autotestconstants.h"
#include "autotestplugin.h"
#include "autotesttr.h"
-#include "itestframework.h"
#include "testoutputreader.h"
#include "testprojectsettings.h"
#include "testresultspane.h"
#include "testrunconfiguration.h"
-#include "testsettings.h"
#include "testtreeitem.h"
#include "testtreemodel.h"
#include <coreplugin/icore.h>
-#include <coreplugin/progressmanager/futureprogress.h>
-#include <coreplugin/progressmanager/progressmanager.h>
+#include <coreplugin/progressmanager/taskprogress.h>
#include <debugger/debuggerkitinformation.h>
#include <debugger/debuggerruncontrol.h>
@@ -28,30 +25,28 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorersettings.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/runconfiguration.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
#include <utils/outputformat.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QCheckBox>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QFormLayout>
-#include <QFuture>
-#include <QFutureInterface>
#include <QLabel>
#include <QLoggingCategory>
#include <QPointer>
-#include <QProcess>
#include <QPushButton>
-#include <QTimer>
+using namespace Core;
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
namespace Autotest {
@@ -72,14 +67,7 @@ TestRunner::TestRunner()
m_cancelTimer.setSingleShot(true);
connect(&m_cancelTimer, &QTimer::timeout, this, [this] { cancelCurrent(Timeout); });
- connect(&m_futureWatcher, &QFutureWatcher<TestResult>::finished,
- this, &TestRunner::onFinished);
- connect(this, &TestRunner::requestStopTestRun,
- &m_futureWatcher, &QFutureWatcher<TestResult>::cancel);
- connect(&m_futureWatcher, &QFutureWatcher<TestResult>::canceled, this, [this] {
- cancelCurrent(UserCanceled);
- reportResult(ResultType::MessageFatal, Tr::tr("Test run canceled by user."));
- });
+ connect(this, &TestRunner::requestStopTestRun, this, [this] { cancelCurrent(UserCanceled); });
connect(BuildManager::instance(), &BuildManager::buildQueueFinished,
this, &TestRunner::onBuildQueueFinished);
}
@@ -93,14 +81,14 @@ TestRunner::~TestRunner()
void TestRunner::runTest(TestRunMode mode, const ITestTreeItem *item)
{
- QTC_ASSERT(!m_executingTests, return);
+ QTC_ASSERT(!isTestRunning(), return);
ITestConfiguration *configuration = item->asConfiguration(mode);
if (configuration)
runTests(mode, {configuration});
}
-static QString processInformation(const QtcProcess *proc)
+static QString processInformation(const Process *proc)
{
QTC_ASSERT(proc, return {});
const CommandLine command = proc->commandLine();
@@ -144,180 +132,21 @@ static QString constructOmittedVariablesDetailsString(const EnvironmentItems &di
+ '\n' + removedVars.join('\n');
}
-bool TestRunner::currentConfigValid()
-{
- const FilePath commandFilePath = m_currentConfig->testExecutable();
- if (!commandFilePath.isEmpty())
- return true;
-
- reportResult(ResultType::MessageFatal,
- Tr::tr("Executable path is empty. (%1)").arg(m_currentConfig->displayName()));
- delete m_currentConfig;
- m_currentConfig = nullptr;
- if (m_selectedTests.isEmpty()) {
- if (m_fakeFutureInterface)
- m_fakeFutureInterface->reportFinished();
- onFinished();
- } else {
- onProcessDone();
- }
- return false;
-}
-
-void TestRunner::setUpProcessEnv()
-{
- CommandLine command = m_currentProcess->commandLine();
- if (m_currentConfig->testBase()->type() == ITestBase::Framework) {
- TestConfiguration *current = static_cast<TestConfiguration *>(m_currentConfig);
-
- QStringList omitted;
- command.addArgs(current->argumentsForTestRunner(&omitted).join(' '), CommandLine::Raw);
- if (!omitted.isEmpty()) {
- const QString &details = constructOmittedDetailsString(omitted);
- reportResult(ResultType::MessageWarn, details.arg(current->displayName()));
- }
- } else {
- TestToolConfiguration *current = static_cast<TestToolConfiguration *>(m_currentConfig);
- command.setArguments(current->commandLine().arguments());
- }
- m_currentProcess->setCommand(command);
-
- m_currentProcess->setWorkingDirectory(m_currentConfig->workingDirectory());
- const Environment &original = m_currentConfig->environment();
- Environment environment = m_currentConfig->filteredEnvironment(original);
- const EnvironmentItems removedVariables = Utils::filtered(
- original.diff(environment), [](const EnvironmentItem &it) {
- return it.operation == EnvironmentItem::Unset;
- });
- if (!removedVariables.isEmpty()) {
- const QString &details = constructOmittedVariablesDetailsString(removedVariables)
- .arg(m_currentConfig->displayName());
- reportResult(ResultType::MessageWarn, details);
- }
- m_currentProcess->setEnvironment(environment);
-}
-
-void TestRunner::scheduleNext()
-{
- QTC_ASSERT(!m_selectedTests.isEmpty(), onFinished(); return);
- QTC_ASSERT(!m_currentConfig && !m_currentProcess, resetInternalPointers());
- QTC_ASSERT(m_fakeFutureInterface, onFinished(); return);
- QTC_ASSERT(!m_canceled, onFinished(); return);
-
- m_currentConfig = m_selectedTests.takeFirst();
-
- if (!currentConfigValid())
- return;
-
- if (!m_currentConfig->project())
- onProcessDone();
-
- m_currentProcess = new QtcProcess;
- m_currentProcess->setCommand({m_currentConfig->testExecutable(), {}});
-
- QTC_ASSERT(!m_currentOutputReader, delete m_currentOutputReader);
- m_currentOutputReader = m_currentConfig->createOutputReader(*m_fakeFutureInterface, m_currentProcess);
- QTC_ASSERT(m_currentOutputReader, onProcessDone(); return);
- connect(m_currentOutputReader, &TestOutputReader::newResult, this, &TestRunner::testResultReady);
- connect(m_currentOutputReader, &TestOutputReader::newOutputLineAvailable,
- TestResultsPane::instance(), &TestResultsPane::addOutputLine);
-
- setUpProcessEnv();
-
- connect(m_currentProcess, &QtcProcess::done, this, &TestRunner::onProcessDone);
- const int timeout = AutotestPlugin::settings()->timeout;
- m_cancelTimer.setInterval(timeout);
- m_cancelTimer.start();
-
- qCInfo(runnerLog) << "Command:" << m_currentProcess->commandLine().executable();
- qCInfo(runnerLog) << "Arguments:" << m_currentProcess->commandLine().arguments();
- qCInfo(runnerLog) << "Working directory:" << m_currentProcess->workingDirectory();
- qCDebug(runnerLog) << "Environment:" << m_currentProcess->environment().toStringList();
-
- m_currentProcess->start();
-}
-
void TestRunner::cancelCurrent(TestRunner::CancelReason reason)
{
- m_canceled = true;
-
- if (m_fakeFutureInterface)
- m_fakeFutureInterface->reportCanceled();
-
if (reason == KitChanged)
reportResult(ResultType::MessageWarn, Tr::tr("Current kit has changed. Canceling test run."));
else if (reason == Timeout)
reportResult(ResultType::MessageFatal, Tr::tr("Test case canceled due to timeout.\nMaybe raise the timeout?"));
-
- // if user or timeout cancels the current run ensure to kill the running process
- if (m_currentProcess && m_currentProcess->state() != QProcess::NotRunning) {
- m_currentProcess->kill();
- m_currentProcess->waitForFinished();
- }
-}
-
-void TestRunner::onProcessDone()
-{
- if (m_currentProcess->result() == ProcessResult::StartFailed) {
- reportResult(ResultType::MessageFatal,
- Tr::tr("Failed to start test for project \"%1\".").arg(m_currentConfig->displayName())
- + processInformation(m_currentProcess) + rcInfo(m_currentConfig));
- }
-
- if (m_executingTests && m_currentConfig) {
- QTC_CHECK(m_fakeFutureInterface);
- m_fakeFutureInterface->setProgressValue(m_fakeFutureInterface->progressValue()
- + m_currentConfig->testCaseCount());
- if (m_currentProcess && !m_fakeFutureInterface->isCanceled()) {
- if (m_currentProcess->exitStatus() == QProcess::CrashExit) {
- if (m_currentOutputReader)
- m_currentOutputReader->reportCrash();
- reportResult(ResultType::MessageFatal,
- Tr::tr("Test for project \"%1\" crashed.").arg(m_currentConfig->displayName())
- + processInformation(m_currentProcess) + rcInfo(m_currentConfig));
- } else if (m_currentOutputReader && !m_currentOutputReader->hadValidOutput()) {
- reportResult(ResultType::MessageFatal,
- Tr::tr("Test for project \"%1\" did not produce any expected output.")
- .arg(m_currentConfig->displayName()) + processInformation(m_currentProcess)
- + rcInfo(m_currentConfig));
- }
- }
- }
- if (m_currentOutputReader) {
- const int disabled = m_currentOutputReader->disabledTests();
- if (disabled > 0)
- emit hadDisabledTests(disabled);
- if (m_currentOutputReader->hasSummary())
- emit reportSummary(m_currentOutputReader->id(), m_currentOutputReader->summary());
-
- m_currentOutputReader->resetCommandlineColor();
- }
- resetInternalPointers();
-
- if (!m_fakeFutureInterface) {
- QTC_ASSERT(!m_executingTests, m_executingTests = false);
- return;
- }
- if (!m_selectedTests.isEmpty() && !m_fakeFutureInterface->isCanceled())
- scheduleNext();
- else
- m_fakeFutureInterface->reportFinished();
-}
-
-void TestRunner::resetInternalPointers()
-{
- delete m_currentOutputReader;
- if (m_currentProcess)
- m_currentProcess->deleteLater();
- delete m_currentConfig;
- m_currentOutputReader = nullptr;
- m_currentProcess = nullptr;
- m_currentConfig = nullptr;
+ else if (reason == UserCanceled)
+ reportResult(ResultType::MessageFatal, Tr::tr("Test run canceled by user."));
+ m_taskTree.reset();
+ onFinished();
}
void TestRunner::runTests(TestRunMode mode, const QList<ITestConfiguration *> &selectedTests)
{
- QTC_ASSERT(!m_executingTests, return);
+ QTC_ASSERT(!isTestRunning(), return);
qDeleteAll(m_selectedTests);
m_selectedTests = selectedTests;
@@ -332,8 +161,6 @@ void TestRunner::runTests(TestRunMode mode, const QList<ITestConfiguration *> &s
return;
}
- m_executingTests = true;
- m_canceled = false;
emit testRunStarted();
// clear old log and output pane
@@ -385,7 +212,7 @@ static QString firstNonEmptyTestCaseTarget(const TestConfiguration *config)
static RunConfiguration *getRunConfiguration(const QString &buildTargetKey)
{
- const Project *project = SessionManager::startupProject();
+ const Project *project = ProjectManager::startupProject();
if (!project)
return nullptr;
const Target *target = project->activeTarget();
@@ -411,7 +238,7 @@ static RunConfiguration *getRunConfiguration(const QString &buildTargetKey)
if (runConfigurations.size() == 1)
return runConfigurations.first();
- RunConfigurationSelectionDialog dialog(buildTargetKey, Core::ICore::dialogParent());
+ RunConfigurationSelectionDialog dialog(buildTargetKey, ICore::dialogParent());
if (dialog.exec() == QDialog::Accepted) {
const QString dName = dialog.displayName();
if (dName.isEmpty())
@@ -431,7 +258,7 @@ static RunConfiguration *getRunConfiguration(const QString &buildTargetKey)
int TestRunner::precheckTestConfigurations()
{
- const bool omitWarnings = AutotestPlugin::settings()->omitRunConfigWarn;
+ const bool omitWarnings = TestSettings::instance()->omitRunConfigWarn();
int testCaseCount = 0;
for (ITestConfiguration *itc : std::as_const(m_selectedTests)) {
if (itc->testBase()->type() == ITestBase::Tool) {
@@ -467,7 +294,7 @@ int TestRunner::precheckTestConfigurations()
void TestRunner::onBuildSystemUpdated()
{
- Target *target = SessionManager::startupTarget();
+ Target *target = ProjectManager::startupTarget();
if (QTC_GUARD(target))
disconnect(target, &Target::buildSystemUpdated, this, &TestRunner::onBuildSystemUpdated);
if (!m_skipTargetsCheck) {
@@ -482,7 +309,7 @@ void TestRunner::runTestsHelper()
bool projectChanged = false;
for (ITestConfiguration *itc : std::as_const(m_selectedTests)) {
if (itc->testBase()->type() == ITestBase::Tool) {
- if (itc->project() != SessionManager::startupProject()) {
+ if (itc->project() != ProjectManager::startupProject()) {
projectChanged = true;
toBeRemoved.append(itc);
}
@@ -513,19 +340,135 @@ void TestRunner::runTestsHelper()
return;
}
- int testCaseCount = precheckTestConfigurations();
+ const int testCaseCount = precheckTestConfigurations();
+ Q_UNUSED(testCaseCount) // TODO: may be useful for fine-grained progress reporting, when fixed
+
+ struct TestStorage {
+ std::unique_ptr<TestOutputReader> m_outputReader;
+ };
+
+ QList<TaskItem> tasks{finishAllAndDone};
+
+ for (ITestConfiguration *config : m_selectedTests) {
+ QTC_ASSERT(config, continue);
+ const TreeStorage<TestStorage> storage;
+
+ const auto onSetup = [this, config] {
+ if (!config->project())
+ return TaskAction::StopWithDone;
+ if (config->testExecutable().isEmpty()) {
+ reportResult(ResultType::MessageFatal,
+ Tr::tr("Executable path is empty. (%1)").arg(config->displayName()));
+ return TaskAction::StopWithDone;
+ }
+ return TaskAction::Continue;
+ };
+ const auto onProcessSetup = [this, config, storage](Process &process) {
+ TestStorage *testStorage = storage.activeStorage();
+ QTC_ASSERT(testStorage, return);
+ testStorage->m_outputReader.reset(config->createOutputReader(&process));
+ QTC_ASSERT(testStorage->m_outputReader, return);
+ connect(testStorage->m_outputReader.get(), &TestOutputReader::newResult,
+ this, &TestRunner::testResultReady);
+ connect(testStorage->m_outputReader.get(), &TestOutputReader::newOutputLineAvailable,
+ TestResultsPane::instance(), &TestResultsPane::addOutputLine);
+
+ CommandLine command{config->testExecutable(), {}};
+ if (config->testBase()->type() == ITestBase::Framework) {
+ TestConfiguration *current = static_cast<TestConfiguration *>(config);
+ QStringList omitted;
+ command.addArgs(current->argumentsForTestRunner(&omitted).join(' '), CommandLine::Raw);
+ if (!omitted.isEmpty()) {
+ const QString &details = constructOmittedDetailsString(omitted);
+ reportResult(ResultType::MessageWarn, details.arg(current->displayName()));
+ }
+ } else {
+ TestToolConfiguration *current = static_cast<TestToolConfiguration *>(config);
+ command.setArguments(current->commandLine().arguments());
+ }
+ process.setCommand(command);
+
+ process.setWorkingDirectory(config->workingDirectory());
+ const Environment &original = config->environment();
+ Environment environment = config->filteredEnvironment(original);
+ const EnvironmentItems removedVariables = Utils::filtered(
+ original.diff(environment), [](const EnvironmentItem &it) {
+ return it.operation == EnvironmentItem::Unset;
+ });
+ if (!removedVariables.isEmpty()) {
+ const QString &details = constructOmittedVariablesDetailsString(removedVariables)
+ .arg(config->displayName());
+ reportResult(ResultType::MessageWarn, details);
+ }
+ process.setEnvironment(environment);
+
+ m_cancelTimer.setInterval(TestSettings::instance()->timeout());
+ m_cancelTimer.start();
+
+ qCInfo(runnerLog) << "Command:" << process.commandLine().executable();
+ qCInfo(runnerLog) << "Arguments:" << process.commandLine().arguments();
+ qCInfo(runnerLog) << "Working directory:" << process.workingDirectory();
+ qCDebug(runnerLog) << "Environment:" << process.environment().toStringList();
+ };
+ const auto onProcessDone = [this, config, storage](const Process &process) {
+ TestStorage *testStorage = storage.activeStorage();
+ QTC_ASSERT(testStorage, return);
+ if (process.result() == ProcessResult::StartFailed) {
+ reportResult(ResultType::MessageFatal,
+ Tr::tr("Failed to start test for project \"%1\".").arg(config->displayName())
+ + processInformation(&process) + rcInfo(config));
+ }
- // Fake future interface - destruction will be handled by QFuture/QFutureWatcher
- m_fakeFutureInterface = new QFutureInterface<TestResult>(QFutureInterfaceBase::Running);
- QFuture<TestResult> future = m_fakeFutureInterface->future();
- m_fakeFutureInterface->setProgressRange(0, testCaseCount);
- m_fakeFutureInterface->setProgressValue(0);
- m_futureWatcher.setFuture(future);
+ if (process.exitStatus() == QProcess::CrashExit) {
+ if (testStorage->m_outputReader)
+ testStorage->m_outputReader->reportCrash();
+ reportResult(ResultType::MessageFatal,
+ Tr::tr("Test for project \"%1\" crashed.").arg(config->displayName())
+ + processInformation(&process) + rcInfo(config));
+ } else if (testStorage->m_outputReader && !testStorage->m_outputReader->hadValidOutput()) {
+ reportResult(ResultType::MessageFatal,
+ Tr::tr("Test for project \"%1\" did not produce any expected output.")
+ .arg(config->displayName()) + processInformation(&process)
+ + rcInfo(config));
+ }
+ if (testStorage->m_outputReader) {
+ const int disabled = testStorage->m_outputReader->disabledTests();
+ if (disabled > 0)
+ emit hadDisabledTests(disabled);
+ if (testStorage->m_outputReader->hasSummary())
+ emit reportSummary(testStorage->m_outputReader->id(), testStorage->m_outputReader->summary());
+
+ testStorage->m_outputReader->resetCommandlineColor();
+ }
+ };
+ const Group group {
+ finishAllAndDone,
+ Storage(storage),
+ onGroupSetup(onSetup),
+ ProcessTask(onProcessSetup, onProcessDone, onProcessDone)
+ };
+ tasks.append(group);
+ }
+
+ m_taskTree.reset(new TaskTree(tasks));
+ connect(m_taskTree.get(), &TaskTree::done, this, &TestRunner::onFinished);
+ connect(m_taskTree.get(), &TaskTree::errorOccurred, this, &TestRunner::onFinished);
+
+ auto progress = new TaskProgress(m_taskTree.get());
+ progress->setDisplayName(tr("Running Tests"));
+ progress->setAutoStopOnCancel(false);
+ progress->setHalfLifeTimePerTask(10000); // 10 seconds
+ connect(progress, &TaskProgress::canceled, this, [this, progress] {
+ // progress was a child of task tree which is going to be deleted directly. Unwind properly.
+ progress->setParent(nullptr);
+ progress->deleteLater();
+ cancelCurrent(UserCanceled);
+ });
- Core::ProgressManager::addTask(future, Tr::tr("Running Tests"), Autotest::Constants::TASK_INDEX);
- if (AutotestPlugin::settings()->popupOnStart)
+ if (TestSettings::instance()->popupOnStart())
AutotestPlugin::popupResultsPane();
- scheduleNext();
+
+ m_taskTree->start();
}
static void processOutput(TestOutputReader *outputreader, const QString &msg, OutputFormat format)
@@ -626,13 +569,8 @@ void TestRunner::debugTests()
}
}
- // We need a fake QFuture for the results. TODO: replace with QtConcurrent::run
- QFutureInterface<TestResult> *futureInterface
- = new QFutureInterface<TestResult>(QFutureInterfaceBase::Running);
- m_futureWatcher.setFuture(futureInterface->future());
-
if (useOutputProcessor) {
- TestOutputReader *outputreader = config->createOutputReader(*futureInterface, nullptr);
+ TestOutputReader *outputreader = config->createOutputReader(nullptr);
connect(outputreader, &TestOutputReader::newResult, this, &TestRunner::testResultReady);
outputreader->setId(inferior.command.executable().toString());
connect(outputreader, &TestOutputReader::newOutputLineAvailable,
@@ -641,9 +579,7 @@ void TestRunner::debugTests()
this, [outputreader](const QString &msg, OutputFormat format) {
processOutput(outputreader, msg, format);
});
-
- connect(runControl, &RunControl::stopped,
- outputreader, &QObject::deleteLater);
+ connect(runControl, &RunControl::stopped, outputreader, &QObject::deleteLater);
}
m_stopDebugConnect = connect(this, &TestRunner::requestStopTestRun,
@@ -652,13 +588,13 @@ void TestRunner::debugTests()
connect(runControl, &RunControl::stopped, this, &TestRunner::onFinished);
m_finishDebugConnect = connect(runControl, &RunControl::finished, this, &TestRunner::onFinished);
ProjectExplorerPlugin::startRunControl(runControl);
- if (useOutputProcessor && AutotestPlugin::settings()->popupOnStart)
+ if (useOutputProcessor && TestSettings::instance()->popupOnStart())
AutotestPlugin::popupResultsPane();
}
static bool executablesEmpty()
{
- Target *target = SessionManager::startupTarget();
+ Target *target = ProjectManager::startupTarget();
const QList<RunConfiguration *> configs = target->runConfigurations();
QTC_ASSERT(!configs.isEmpty(), return false);
if (auto execAspect = configs.first()->aspect<ExecutableAspect>())
@@ -671,7 +607,7 @@ void TestRunner::runOrDebugTests()
if (!m_skipTargetsCheck) {
if (executablesEmpty()) {
m_skipTargetsCheck = true;
- Target * target = SessionManager::startupTarget();
+ Target *target = ProjectManager::startupTarget();
QTimer::singleShot(5000, this, [this, target = QPointer<Target>(target)] {
if (target) {
disconnect(target, &Target::buildSystemUpdated,
@@ -706,8 +642,7 @@ void TestRunner::buildProject(Project *project)
BuildManager *buildManager = BuildManager::instance();
m_buildConnect = connect(this, &TestRunner::requestStopTestRun,
buildManager, &BuildManager::cancel);
- connect(buildManager, &BuildManager::buildQueueFinished,
- this, &TestRunner::buildFinished);
+ connect(buildManager, &BuildManager::buildQueueFinished, this, &TestRunner::buildFinished);
BuildManager::buildProjectWithDependencies(project);
if (!BuildManager::isBuilding())
buildFinished(false);
@@ -717,37 +652,33 @@ void TestRunner::buildFinished(bool success)
{
disconnect(m_buildConnect);
BuildManager *buildManager = BuildManager::instance();
- disconnect(buildManager, &BuildManager::buildQueueFinished,
- this, &TestRunner::buildFinished);
+ disconnect(buildManager, &BuildManager::buildQueueFinished, this, &TestRunner::buildFinished);
if (success) {
- if (!m_canceled)
- runOrDebugTests();
- else if (m_executingTests)
- onFinished();
- } else {
- reportResult(ResultType::MessageFatal, Tr::tr("Build failed. Canceling test run."));
- onFinished();
+ runOrDebugTests();
+ return;
}
+ reportResult(ResultType::MessageFatal, Tr::tr("Build failed. Canceling test run."));
+ onFinished();
}
static RunAfterBuildMode runAfterBuild()
{
- Project *project = SessionManager::startupProject();
+ Project *project = ProjectManager::startupProject();
if (!project)
return RunAfterBuildMode::None;
if (!project->namedSettings(Constants::SK_USE_GLOBAL).isValid())
- return AutotestPlugin::settings()->runAfterBuild;
+ return TestSettings::instance()->runAfterBuildMode();
TestProjectSettings *projectSettings = AutotestPlugin::projectSettings(project);
- return projectSettings->useGlobalSettings() ? AutotestPlugin::settings()->runAfterBuild
+ return projectSettings->useGlobalSettings() ? TestSettings::instance()->runAfterBuildMode()
: projectSettings->runAfterBuild();
}
void TestRunner::onBuildQueueFinished(bool success)
{
- if (m_executingTests || !m_selectedTests.isEmpty()) // paranoia!
+ if (isTestRunning() || !m_selectedTests.isEmpty()) // paranoia!
return;
if (!success || m_runMode != TestRunMode::None)
@@ -768,17 +699,15 @@ void TestRunner::onBuildQueueFinished(bool success)
void TestRunner::onFinished()
{
- m_cancelTimer.stop();
- // if we've been canceled and we still have test configurations queued just throw them away
- qDeleteAll(m_selectedTests);
- m_selectedTests.clear();
-
+ if (m_taskTree)
+ m_taskTree.release()->deleteLater();
disconnect(m_stopDebugConnect);
disconnect(m_finishDebugConnect);
disconnect(m_targetConnect);
- m_fakeFutureInterface = nullptr;
+ qDeleteAll(m_selectedTests);
+ m_selectedTests.clear();
+ m_cancelTimer.stop();
m_runMode = TestRunMode::None;
- m_executingTests = false;
emit testRunFinished();
}
@@ -855,7 +784,7 @@ void RunConfigurationSelectionDialog::populate()
{
m_rcCombo->addItem({}, QStringList{{}, {}, {}}); // empty default
- if (auto project = SessionManager::startupProject()) {
+ if (auto project = ProjectManager::startupProject()) {
if (auto target = project->activeTarget()) {
for (RunConfiguration *rc : target->runConfigurations()) {
auto runnable = rc->runnable();
diff --git a/src/plugins/autotest/testrunner.h b/src/plugins/autotest/testrunner.h
index c96af56075..25c65f8985 100644
--- a/src/plugins/autotest/testrunner.h
+++ b/src/plugins/autotest/testrunner.h
@@ -4,12 +4,11 @@
#pragma once
#include "autotest_global.h"
-#include "testresult.h"
+
+#include "autotestconstants.h"
#include <QDialog>
-#include <QFutureWatcher>
#include <QList>
-#include <QObject>
#include <QTimer>
QT_BEGIN_NAMESPACE
@@ -20,13 +19,14 @@ class QLabel;
QT_END_NAMESPACE
namespace ProjectExplorer { class Project; }
-namespace Utils { class QtcProcess; }
+namespace Tasking { class TaskTree; }
namespace Autotest {
-enum class TestRunMode;
class ITestConfiguration;
-class TestOutputReader;
+class ITestTreeItem;
+class TestResult;
+enum class ResultType;
namespace Internal {
@@ -44,7 +44,7 @@ public:
void runTests(TestRunMode mode, const QList<ITestConfiguration *> &selectedTests);
void runTest(TestRunMode mode, const ITestTreeItem *item);
- bool isTestRunning() const { return m_executingTests; }
+ bool isTestRunning() const { return m_buildConnect || m_stopDebugConnect || m_taskTree.get(); }
signals:
void testRunStarted();
@@ -61,12 +61,7 @@ private:
void onFinished();
int precheckTestConfigurations();
- bool currentConfigValid();
- void setUpProcessEnv();
- void scheduleNext();
void cancelCurrent(CancelReason reason);
- void onProcessDone();
- void resetInternalPointers();
void runTestsHelper();
void debugTests();
@@ -75,14 +70,9 @@ private:
bool postponeTestRunWithEmptyExecutable(ProjectExplorer::Project *project);
void onBuildSystemUpdated();
- QFutureWatcher<TestResult> m_futureWatcher;
- QFutureInterface<TestResult> *m_fakeFutureInterface = nullptr;
+ std::unique_ptr<Tasking::TaskTree> m_taskTree;
+
QList<ITestConfiguration *> m_selectedTests;
- bool m_executingTests = false;
- bool m_canceled = false;
- ITestConfiguration *m_currentConfig = nullptr;
- Utils::QtcProcess *m_currentProcess = nullptr;
- TestOutputReader *m_currentOutputReader = nullptr;
TestRunMode m_runMode = TestRunMode::None;
// temporarily used if building before running is necessary
diff --git a/src/plugins/autotest/testsettings.cpp b/src/plugins/autotest/testsettings.cpp
index 6de76d815f..b4507a9d9a 100644
--- a/src/plugins/autotest/testsettings.cpp
+++ b/src/plugins/autotest/testsettings.cpp
@@ -4,53 +4,110 @@
#include "testsettings.h"
#include "autotestconstants.h"
+#include "autotesttr.h"
#include "testframeworkmanager.h"
-#include <utils/id.h>
-
#include <QSettings>
-namespace Autotest {
-namespace Internal {
-
-static const char timeoutKey[] = "Timeout";
-static const char omitInternalKey[] = "OmitInternal";
-static const char omitRunConfigWarnKey[] = "OmitRCWarnings";
-static const char limitResultOutputKey[] = "LimitResultOutput";
-static const char limitResultDescriptionKey[] = "LimitResultDescription";
-static const char resultDescriptionMaxSizeKey[] = "ResultDescriptionMaxSize";
-static const char autoScrollKey[] = "AutoScrollResults";
-static const char processArgsKey[] = "ProcessArgs";
-static const char displayApplicationKey[] = "DisplayApp";
-static const char popupOnStartKey[] = "PopupOnStart";
-static const char popupOnFinishKey[] = "PopupOnFinish";
-static const char popupOnFailKey[] = "PopupOnFail";
-static const char runAfterBuildKey[] = "RunAfterBuild";
+namespace Autotest::Internal {
+
static const char groupSuffix[] = ".group";
constexpr int defaultTimeout = 60000;
+static TestSettings *s_instance;
+
+TestSettings *TestSettings::instance()
+{
+ return s_instance;
+}
+
TestSettings::TestSettings()
- : timeout(defaultTimeout)
{
+ s_instance = this;
+
+ setSettingsGroup(Constants::SETTINGSGROUP);
+
+ timeout.setSettingsKey("Timeout");
+ timeout.setDefaultValue(defaultTimeout);
+ timeout.setRange(5000, 36'000'000); // 36 Mio ms = 36'000 s = 10 h
+ timeout.setSuffix(Tr::tr(" s")); // we show seconds, but store milliseconds
+ timeout.setDisplayScaleFactor(1000);
+ timeout.setToolTip(Tr::tr("Timeout used when executing test cases. This will apply "
+ "for each test case on its own, not the whole project."));
+
+ omitInternalMsg.setSettingsKey("OmitInternal");
+ omitInternalMsg.setDefaultValue(true);
+ omitInternalMsg.setLabelText(Tr::tr("Omit internal messages"));
+ omitInternalMsg.setToolTip(Tr::tr("Hides internal messages by default. "
+ "You can still enable them by using the test results filter."));
+
+ omitRunConfigWarn.setSettingsKey("OmitRCWarnings");
+ omitRunConfigWarn.setLabelText(Tr::tr("Omit run configuration warnings"));
+ omitRunConfigWarn.setToolTip(Tr::tr("Hides warnings related to a deduced run configuration."));
+
+ limitResultOutput.setSettingsKey("LimitResultOutput");
+ limitResultOutput.setDefaultValue(true);
+ limitResultOutput.setLabelText(Tr::tr("Limit result output"));
+ limitResultOutput.setToolTip(Tr::tr("Limits result output to 100000 characters."));
+
+ limitResultDescription.setSettingsKey("LimitResultDescription");
+ limitResultDescription.setLabelText(Tr::tr("Limit result description:"));
+ limitResultDescription.setToolTip(
+ Tr::tr("Limit number of lines shown in test result tooltip and description."));
+
+ resultDescriptionMaxSize.setSettingsKey("ResultDescriptionMaxSize");
+ resultDescriptionMaxSize.setDefaultValue(10);
+ resultDescriptionMaxSize.setRange(1, 100000);
+ resultDescriptionMaxSize.setEnabler(&limitResultDescription);
+
+ autoScroll.setSettingsKey("AutoScrollResults");
+ autoScroll.setDefaultValue(true);
+ autoScroll.setLabelText(Tr::tr("Automatically scroll results"));
+ autoScroll.setToolTip(Tr::tr("Automatically scrolls down when new items are added "
+ "and scrollbar is at bottom."));
+
+ processArgs.setSettingsKey("ProcessArgs");
+ processArgs.setLabelText(Tr::tr("Process arguments"));
+ processArgs.setToolTip(
+ Tr::tr("Allow passing arguments specified on the respective run configuration.\n"
+ "Warning: this is an experimental feature and might lead to failing to "
+ "execute the test executable."));
+
+ displayApplication.setSettingsKey("DisplayApp");
+ displayApplication.setLabelText(Tr::tr("Group results by application"));
+
+ popupOnStart.setSettingsKey("PopupOnStart");
+ popupOnStart.setLabelText(Tr::tr("Open results when tests start"));
+ popupOnStart.setToolTip(
+ Tr::tr("Displays test results automatically when tests are started."));
+
+ popupOnFinish.setSettingsKey("PopupOnFinish");
+ popupOnFinish.setDefaultValue(true);
+ popupOnFinish.setLabelText(Tr::tr("Open results when tests finish"));
+ popupOnFinish.setToolTip(
+ Tr::tr("Displays test results automatically when tests are finished."));
+
+ popupOnFail.setSettingsKey("PopupOnFail");
+ popupOnFail.setLabelText(Tr::tr("Only for unsuccessful test runs"));
+ popupOnFail.setEnabler(&popupOnFinish);
+ popupOnFail.setToolTip(Tr::tr("Displays test results only if the test run contains "
+ "failed, fatal or unexpectedly passed tests."));
+
+ runAfterBuild.setSettingsKey("RunAfterBuild");
+ runAfterBuild.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
+ runAfterBuild.setToolTip(Tr::tr("Runs chosen tests automatically if a build succeeded."));
+ runAfterBuild.addOption(Tr::tr("None"));
+ runAfterBuild.addOption(Tr::tr("All"));
+ runAfterBuild.addOption(Tr::tr("Selected"));
}
void TestSettings::toSettings(QSettings *s) const
{
+ AspectContainer::writeSettings(s);
+
s->beginGroup(Constants::SETTINGSGROUP);
- s->setValue(timeoutKey, timeout);
- s->setValue(omitInternalKey, omitInternalMssg);
- s->setValue(omitRunConfigWarnKey, omitRunConfigWarn);
- s->setValue(limitResultOutputKey, limitResultOutput);
- s->setValue(limitResultDescriptionKey, limitResultDescription);
- s->setValue(resultDescriptionMaxSizeKey, resultDescriptionMaxSize);
- s->setValue(autoScrollKey, autoScroll);
- s->setValue(processArgsKey, processArgs);
- s->setValue(displayApplicationKey, displayApplication);
- s->setValue(popupOnStartKey, popupOnStart);
- s->setValue(popupOnFinishKey, popupOnFinish);
- s->setValue(popupOnFailKey, popupOnFail);
- s->setValue(runAfterBuildKey, int(runAfterBuild));
+
// store frameworks and their current active and grouping state
for (auto it = frameworks.cbegin(); it != frameworks.cend(); ++it) {
const Utils::Id &id = it.key();
@@ -65,21 +122,10 @@ void TestSettings::toSettings(QSettings *s) const
void TestSettings::fromSettings(QSettings *s)
{
+ AspectContainer::readSettings(s);
+
s->beginGroup(Constants::SETTINGSGROUP);
- timeout = s->value(timeoutKey, defaultTimeout).toInt();
- omitInternalMssg = s->value(omitInternalKey, true).toBool();
- omitRunConfigWarn = s->value(omitRunConfigWarnKey, false).toBool();
- limitResultOutput = s->value(limitResultOutputKey, true).toBool();
- limitResultDescription = s->value(limitResultDescriptionKey, false).toBool();
- resultDescriptionMaxSize = s->value(resultDescriptionMaxSizeKey, 10).toInt();
- autoScroll = s->value(autoScrollKey, true).toBool();
- processArgs = s->value(processArgsKey, false).toBool();
- displayApplication = s->value(displayApplicationKey, false).toBool();
- popupOnStart = s->value(popupOnStartKey, true).toBool();
- popupOnFinish = s->value(popupOnFinishKey, true).toBool();
- popupOnFail = s->value(popupOnFailKey, false).toBool();
- runAfterBuild = RunAfterBuildMode(s->value(runAfterBuildKey,
- int(RunAfterBuildMode::None)).toInt());
+
// try to get settings for registered frameworks
const TestFrameworks &registered = TestFrameworkManager::registeredFrameworks();
frameworks.clear();
@@ -102,5 +148,9 @@ void TestSettings::fromSettings(QSettings *s)
s->endGroup();
}
-} // namespace Internal
-} // namespace Autotest
+RunAfterBuildMode TestSettings::runAfterBuildMode() const
+{
+ return static_cast<RunAfterBuildMode>(runAfterBuild.value());
+}
+
+} // namespace Autotest::Internal
diff --git a/src/plugins/autotest/testsettings.h b/src/plugins/autotest/testsettings.h
index ecf04fabaa..1e33269bd4 100644
--- a/src/plugins/autotest/testsettings.h
+++ b/src/plugins/autotest/testsettings.h
@@ -3,18 +3,9 @@
#pragma once
-#include <QHash>
+#include <utils/aspects.h>
-namespace Utils {
-class Id;
-}
-
-QT_BEGIN_NAMESPACE
-class QSettings;
-QT_END_NAMESPACE
-
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
enum class RunAfterBuildMode
{
@@ -23,29 +14,39 @@ enum class RunAfterBuildMode
Selected
};
-struct TestSettings
+class NonAspectSettings
{
+public:
+ QHash<Utils::Id, bool> frameworks;
+ QHash<Utils::Id, bool> frameworksGrouping;
+ QHash<Utils::Id, bool> tools;
+};
+
+class TestSettings : public Utils::AspectContainer, public NonAspectSettings
+{
+public:
TestSettings();
+
+ static TestSettings *instance();
+
void toSettings(QSettings *s) const;
void fromSettings(QSettings *s);
- int timeout;
- bool omitInternalMssg = true;
- bool omitRunConfigWarn = false;
- bool limitResultOutput = true;
- bool limitResultDescription = false;
- int resultDescriptionMaxSize = 10;
- bool autoScroll = true;
- bool processArgs = false;
- bool displayApplication = false;
- bool popupOnStart = true;
- bool popupOnFinish = true;
- bool popupOnFail = false;
- RunAfterBuildMode runAfterBuild = RunAfterBuildMode::None;
- QHash<Utils::Id, bool> frameworks;
- QHash<Utils::Id, bool> frameworksGrouping;
- QHash<Utils::Id, bool> tools;
+ Utils::IntegerAspect timeout{this};
+ Utils::BoolAspect omitInternalMsg{this};
+ Utils::BoolAspect omitRunConfigWarn{this};
+ Utils::BoolAspect limitResultOutput{this};
+ Utils::BoolAspect limitResultDescription{this};
+ Utils::IntegerAspect resultDescriptionMaxSize{this};
+ Utils::BoolAspect autoScroll{this};
+ Utils::BoolAspect processArgs{this};
+ Utils::BoolAspect displayApplication{this};
+ Utils::BoolAspect popupOnStart{this};
+ Utils::BoolAspect popupOnFinish{this};
+ Utils::BoolAspect popupOnFail{this};
+ Utils::SelectionAspect runAfterBuild{this};
+
+ RunAfterBuildMode runAfterBuildMode() const;
};
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/testsettingspage.cpp b/src/plugins/autotest/testsettingspage.cpp
index 1cd6e2d1ae..6c9850bc2a 100644
--- a/src/plugins/autotest/testsettingspage.cpp
+++ b/src/plugins/autotest/testsettingspage.cpp
@@ -25,116 +25,33 @@
#include <QLabel>
#include <QPushButton>
#include <QSpacerItem>
-#include <QSpinBox>
#include <QTreeWidget>
-#include <QWidget>
using namespace Utils;
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
-class TestSettingsWidget : public QWidget
+class TestSettingsWidget : public Core::IOptionsPageWidget
{
public:
- explicit TestSettingsWidget(QWidget *parent = nullptr);
-
- void setSettings(const TestSettings &settings);
- TestSettings settings() const;
+ TestSettingsWidget();
private:
void populateFrameworksListWidget(const QHash<Id, bool> &frameworks,
const QHash<Id, bool> &testTools);
- void testSettings(TestSettings &settings) const;
- void testToolsSettings(TestSettings &settings) const;
+ void testSettings(NonAspectSettings &settings) const;
+ void testToolsSettings(NonAspectSettings &settings) const;
void onFrameworkItemChanged();
- QCheckBox *m_omitInternalMsgCB;
- QCheckBox *m_omitRunConfigWarnCB;
- QCheckBox *m_limitResultOutputCB;
- QCheckBox *m_limitResultDescriptionCb;
- QSpinBox *m_limitResultDescriptionSpinBox;
- QCheckBox *m_openResultsOnStartCB;
- QCheckBox *m_openResultsOnFinishCB;
- QCheckBox *m_openResultsOnFailCB;
- QCheckBox *m_autoScrollCB;
- QCheckBox *m_displayAppCB;
- QCheckBox *m_processArgsCB;
- QComboBox *m_runAfterBuildCB;
- QSpinBox *m_timeoutSpin;
QTreeWidget *m_frameworkTreeWidget;
InfoLabel *m_frameworksWarn;
};
-TestSettingsWidget::TestSettingsWidget(QWidget *parent)
- : QWidget(parent)
+TestSettingsWidget::TestSettingsWidget()
{
- resize(586, 469);
-
- m_omitInternalMsgCB = new QCheckBox(Tr::tr("Omit internal messages"));
- m_omitInternalMsgCB->setChecked(true);
- m_omitInternalMsgCB->setToolTip(Tr::tr("Hides internal messages by default. "
- "You can still enable them by using the test results filter."));
-
- m_omitRunConfigWarnCB = new QCheckBox(Tr::tr("Omit run configuration warnings"));
- m_omitRunConfigWarnCB->setToolTip(Tr::tr("Hides warnings related to a deduced run configuration."));
-
- m_limitResultOutputCB = new QCheckBox(Tr::tr("Limit result output"));
- m_limitResultOutputCB->setChecked(true);
- m_limitResultOutputCB->setToolTip(Tr::tr("Limits result output to 100000 characters."));
-
- m_limitResultDescriptionCb = new QCheckBox(Tr::tr("Limit result description:"));
- m_limitResultDescriptionCb->setToolTip(
- Tr::tr("Limit number of lines shown in test result tooltip and description."));
-
- m_limitResultDescriptionSpinBox = new QSpinBox;
- m_limitResultDescriptionSpinBox->setEnabled(false);
- m_limitResultDescriptionSpinBox->setMinimum(1);
- m_limitResultDescriptionSpinBox->setMaximum(1000000);
- m_limitResultDescriptionSpinBox->setValue(10);
-
- m_openResultsOnStartCB = new QCheckBox(Tr::tr("Open results when tests start"));
- m_openResultsOnStartCB->setToolTip(
- Tr::tr("Displays test results automatically when tests are started."));
-
- m_openResultsOnFinishCB = new QCheckBox(Tr::tr("Open results when tests finish"));
- m_openResultsOnFinishCB->setChecked(true);
- m_openResultsOnFinishCB->setToolTip(
- Tr::tr("Displays test results automatically when tests are finished."));
-
- m_openResultsOnFailCB = new QCheckBox(Tr::tr("Only for unsuccessful test runs"));
- m_openResultsOnFailCB->setToolTip(
- Tr::tr("Displays test results only if the test run contains failed, fatal or unexpectedly passed tests."));
-
- m_autoScrollCB = new QCheckBox(Tr::tr("Automatically scroll results"));
- m_autoScrollCB->setChecked(true);
- m_autoScrollCB->setToolTip(Tr::tr("Automatically scrolls down when new items are added and scrollbar is at bottom."));
-
- m_displayAppCB = new QCheckBox(Tr::tr("Group results by application"));
-
- m_processArgsCB = new QCheckBox(Tr::tr("Process arguments"));
- m_processArgsCB->setToolTip(
- Tr::tr("Allow passing arguments specified on the respective run configuration.\n"
- "Warning: this is an experimental feature and might lead to failing to execute the test executable."));
-
- m_runAfterBuildCB = new QComboBox;
- m_runAfterBuildCB->setToolTip(Tr::tr("Runs chosen tests automatically if a build succeeded."));
- m_runAfterBuildCB->addItem(Tr::tr("None"));
- m_runAfterBuildCB->addItem(Tr::tr("All"));
- m_runAfterBuildCB->addItem(Tr::tr("Selected"));
-
auto timeoutLabel = new QLabel(Tr::tr("Timeout:"));
timeoutLabel->setToolTip(Tr::tr("Timeout used when executing each test case."));
- m_timeoutSpin = new QSpinBox;
- m_timeoutSpin->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
- m_timeoutSpin->setRange(5, 36000);
- m_timeoutSpin->setValue(60);
- m_timeoutSpin->setSuffix(Tr::tr(" s"));
- m_timeoutSpin->setToolTip(
- Tr::tr("Timeout used when executing test cases. This will apply "
- "for each test case on its own, not the whole project."));
-
m_frameworkTreeWidget = new QTreeWidget;
m_frameworkTreeWidget->setRootIsDecorated(false);
m_frameworkTreeWidget->setHeaderHidden(false);
@@ -162,21 +79,22 @@ TestSettingsWidget::TestSettingsWidget(QWidget *parent)
onClicked([] { AutotestPlugin::clearChoiceCache(); }, this)
};
+ TestSettings &s = *TestSettings::instance();
Group generalGroup {
title(Tr::tr("General")),
Column {
- m_omitInternalMsgCB,
- m_omitRunConfigWarnCB,
- m_limitResultOutputCB,
- Row { m_limitResultDescriptionCb, m_limitResultDescriptionSpinBox, st },
- m_openResultsOnStartCB,
- m_openResultsOnFinishCB,
- Row { Space(20), m_openResultsOnFailCB },
- m_autoScrollCB,
- m_displayAppCB,
- m_processArgsCB,
- Row { Tr::tr("Automatically run"), m_runAfterBuildCB, st },
- Row { timeoutLabel, m_timeoutSpin, st },
+ s.omitInternalMsg,
+ s.omitRunConfigWarn,
+ s.limitResultOutput,
+ Row { s.limitResultDescription, s.resultDescriptionMaxSize, st },
+ s.popupOnStart,
+ s.popupOnFinish,
+ Row { Space(20), s.popupOnFail },
+ s.autoScroll,
+ s.displayApplication,
+ s.processArgs,
+ Row { Tr::tr("Automatically run"), s.runAfterBuild, st },
+ Row { timeoutLabel, s.timeout, st },
Row { resetChoicesButton, st }
}
};
@@ -199,50 +117,38 @@ TestSettingsWidget::TestSettingsWidget(QWidget *parent)
connect(m_frameworkTreeWidget, &QTreeWidget::itemChanged,
this, &TestSettingsWidget::onFrameworkItemChanged);
- connect(m_openResultsOnFinishCB, &QCheckBox::toggled,
- m_openResultsOnFailCB, &QCheckBox::setEnabled);
- connect(m_limitResultDescriptionCb, &QCheckBox::toggled,
- m_limitResultDescriptionSpinBox, &QSpinBox::setEnabled);
-}
-void TestSettingsWidget::setSettings(const TestSettings &settings)
-{
- m_timeoutSpin->setValue(settings.timeout / 1000); // we store milliseconds
- m_omitInternalMsgCB->setChecked(settings.omitInternalMssg);
- m_omitRunConfigWarnCB->setChecked(settings.omitRunConfigWarn);
- m_limitResultOutputCB->setChecked(settings.limitResultOutput);
- m_limitResultDescriptionCb->setChecked(settings.limitResultDescription);
- m_limitResultDescriptionSpinBox->setEnabled(settings.limitResultDescription);
- m_limitResultDescriptionSpinBox->setValue(settings.resultDescriptionMaxSize);
- m_autoScrollCB->setChecked(settings.autoScroll);
- m_processArgsCB->setChecked(settings.processArgs);
- m_displayAppCB->setChecked(settings.displayApplication);
- m_openResultsOnStartCB->setChecked(settings.popupOnStart);
- m_openResultsOnFinishCB->setChecked(settings.popupOnFinish);
- m_openResultsOnFailCB->setChecked(settings.popupOnFail);
- m_runAfterBuildCB->setCurrentIndex(int(settings.runAfterBuild));
- populateFrameworksListWidget(settings.frameworks, settings.tools);
-}
+ populateFrameworksListWidget(s.frameworks, s.tools);
-TestSettings TestSettingsWidget::settings() const
-{
- TestSettings result;
- result.timeout = m_timeoutSpin->value() * 1000; // we display seconds
- result.omitInternalMssg = m_omitInternalMsgCB->isChecked();
- result.omitRunConfigWarn = m_omitRunConfigWarnCB->isChecked();
- result.limitResultOutput = m_limitResultOutputCB->isChecked();
- result.limitResultDescription = m_limitResultDescriptionCb->isChecked();
- result.resultDescriptionMaxSize = m_limitResultDescriptionSpinBox->value();
- result.autoScroll = m_autoScrollCB->isChecked();
- result.processArgs = m_processArgsCB->isChecked();
- result.displayApplication = m_displayAppCB->isChecked();
- result.popupOnStart = m_openResultsOnStartCB->isChecked();
- result.popupOnFinish = m_openResultsOnFinishCB->isChecked();
- result.popupOnFail = m_openResultsOnFailCB->isChecked();
- result.runAfterBuild = RunAfterBuildMode(m_runAfterBuildCB->currentIndex());
- testSettings(result);
- testToolsSettings(result);
- return result;
+ setOnApply([this] {
+ TestSettings &s = *TestSettings::instance();
+
+ NonAspectSettings tmp;
+ testSettings(tmp);
+ testToolsSettings(tmp);
+
+ const QList<Utils::Id> changedIds = Utils::filtered(tmp.frameworksGrouping.keys(),
+ [&tmp, &s](Utils::Id id) {
+ return tmp.frameworksGrouping[id] != s.frameworksGrouping[id];
+ });
+
+ testSettings(s);
+ testToolsSettings(s);
+ s.toSettings(Core::ICore::settings());
+
+ for (ITestFramework *framework : TestFrameworkManager::registeredFrameworks()) {
+ framework->setActive(s.frameworks.value(framework->id(), false));
+ framework->setGrouping(s.frameworksGrouping.value(framework->id(), false));
+ }
+
+ for (ITestTool *testTool : TestFrameworkManager::registeredTestTools())
+ testTool->setActive(s.tools.value(testTool->id(), false));
+
+ TestTreeModel::instance()->synchronizeTestFrameworks();
+ TestTreeModel::instance()->synchronizeTestTools();
+ if (!changedIds.isEmpty())
+ TestTreeModel::instance()->rebuild(changedIds);
+ });
}
enum TestBaseInfo
@@ -283,7 +189,7 @@ void TestSettingsWidget::populateFrameworksListWidget(const QHash<Id, bool> &fra
}
}
-void TestSettingsWidget::testSettings(TestSettings &settings) const
+void TestSettingsWidget::testSettings(NonAspectSettings &settings) const
{
const QAbstractItemModel *model = m_frameworkTreeWidget->model();
QTC_ASSERT(model, return);
@@ -298,7 +204,7 @@ void TestSettingsWidget::testSettings(TestSettings &settings) const
}
}
-void TestSettingsWidget::testToolsSettings(TestSettings &settings) const
+void TestSettingsWidget::testToolsSettings(NonAspectSettings &settings) const
{
const QAbstractItemModel *model = m_frameworkTreeWidget->model();
QTC_ASSERT(model, return);
@@ -343,50 +249,16 @@ void TestSettingsWidget::onFrameworkItemChanged()
|| (mixed == (ITestBase::Framework | ITestBase::Tool)));
}
-TestSettingsPage::TestSettingsPage(TestSettings *settings)
- : m_settings(settings)
+// TestSettingsPage
+
+TestSettingsPage::TestSettingsPage()
{
setId(Constants::AUTOTEST_SETTINGS_ID);
setDisplayName(Tr::tr("General"));
setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
setDisplayCategory(Tr::tr("Testing"));
setCategoryIconPath(":/autotest/images/settingscategory_autotest.png");
+ setWidgetCreator([] { return new TestSettingsWidget; });
}
-QWidget *TestSettingsPage::widget()
-{
- if (!m_widget) {
- m_widget = new TestSettingsWidget;
- m_widget->setSettings(*m_settings);
- }
- return m_widget;
-}
-
-void TestSettingsPage::apply()
-{
- if (!m_widget) // page was not shown at all
- return;
- const TestSettings newSettings = m_widget->settings();
- const QList<Id> changedIds = Utils::filtered(newSettings.frameworksGrouping.keys(),
- [newSettings, this](const Id &id) {
- return newSettings.frameworksGrouping[id] != m_settings->frameworksGrouping[id];
- });
- *m_settings = newSettings;
- m_settings->toSettings(Core::ICore::settings());
-
- for (ITestFramework *framework : TestFrameworkManager::registeredFrameworks()) {
- framework->setActive(m_settings->frameworks.value(framework->id(), false));
- framework->setGrouping(m_settings->frameworksGrouping.value(framework->id(), false));
- }
-
- for (ITestTool *testTool : TestFrameworkManager::registeredTestTools())
- testTool->setActive(m_settings->tools.value(testTool->id(), false));
-
- TestTreeModel::instance()->synchronizeTestFrameworks();
- TestTreeModel::instance()->synchronizeTestTools();
- if (!changedIds.isEmpty())
- TestTreeModel::instance()->rebuild(changedIds);
-}
-
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/testsettingspage.h b/src/plugins/autotest/testsettingspage.h
index 064a9504c9..03eb214097 100644
--- a/src/plugins/autotest/testsettingspage.h
+++ b/src/plugins/autotest/testsettingspage.h
@@ -5,28 +5,12 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <QPointer>
-
-namespace Autotest {
-namespace Internal {
-
-struct TestSettings;
-class TestSettingsWidget;
+namespace Autotest::Internal {
class TestSettingsPage : public Core::IOptionsPage
{
- Q_OBJECT
public:
- explicit TestSettingsPage(TestSettings *settings);
-
- QWidget *widget() override;
- void apply() override;
- void finish() override { }
-
-private:
- TestSettings *m_settings;
- QPointer<TestSettingsWidget> m_widget;
+ TestSettingsPage();
};
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/testtreeitem.cpp b/src/plugins/autotest/testtreeitem.cpp
index 884544e3b2..f18adb812d 100644
--- a/src/plugins/autotest/testtreeitem.cpp
+++ b/src/plugins/autotest/testtreeitem.cpp
@@ -224,11 +224,11 @@ void TestTreeItem::markForRemovalRecursively(bool mark)
childItem(row)->markForRemovalRecursively(mark);
}
-void TestTreeItem::markForRemovalRecursively(const FilePath &filepath)
+void TestTreeItem::markForRemovalRecursively(const QSet<FilePath> &filePaths)
{
- bool mark = filePath() == filepath;
- forFirstLevelChildItems([&mark, &filepath](TestTreeItem *child) {
- child->markForRemovalRecursively(filepath);
+ bool mark = filePaths.contains(filePath());
+ forFirstLevelChildItems([&mark, &filePaths](TestTreeItem *child) {
+ child->markForRemovalRecursively(filePaths);
mark &= child->markedForRemoval();
});
markForRemoval(mark);
diff --git a/src/plugins/autotest/testtreeitem.h b/src/plugins/autotest/testtreeitem.h
index e51f48b211..3737d2a360 100644
--- a/src/plugins/autotest/testtreeitem.h
+++ b/src/plugins/autotest/testtreeitem.h
@@ -115,7 +115,7 @@ public:
void setProFile(const Utils::FilePath &proFile) { m_proFile = proFile; }
void markForRemoval(bool mark);
void markForRemovalRecursively(bool mark);
- virtual void markForRemovalRecursively(const Utils::FilePath &filepath);
+ virtual void markForRemovalRecursively(const QSet<Utils::FilePath> &filePaths);
virtual bool removeOnSweepIfEmpty() const { return type() == GroupNode; }
bool markedForRemoval() const { return m_status == MarkedForRemoval; }
bool newlyAdded() const { return m_status == NewlyAdded; }
diff --git a/src/plugins/autotest/testtreemodel.cpp b/src/plugins/autotest/testtreemodel.cpp
index 8842734f0c..5e7fcb85a8 100644
--- a/src/plugins/autotest/testtreemodel.cpp
+++ b/src/plugins/autotest/testtreemodel.cpp
@@ -10,12 +10,16 @@
#include "testprojectsettings.h"
#include <cppeditor/cppmodelmanager.h>
+
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
+
#include <qmljs/qmljsmodelmanagerinterface.h>
+
#include <texteditor/texteditor.h>
+
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
@@ -38,7 +42,7 @@ TestTreeModel::TestTreeModel(TestCodeParser *parser) :
connect(m_parser, &TestCodeParser::aboutToPerformFullParse, this,
&TestTreeModel::removeAllTestItems, Qt::QueuedConnection);
connect(m_parser, &TestCodeParser::testParseResultReady,
- this, &TestTreeModel::onParseResultReady, Qt::QueuedConnection);
+ this, &TestTreeModel::onParseResultReady);
connect(m_parser, &TestCodeParser::parsingFinished,
this, &TestTreeModel::sweep, Qt::QueuedConnection);
connect(m_parser, &TestCodeParser::parsingFailed,
@@ -70,8 +74,8 @@ void TestTreeModel::setupParsingConnections()
m_parser->setDirty();
m_parser->setState(TestCodeParser::Idle);
- SessionManager *sm = SessionManager::instance();
- connect(sm, &SessionManager::startupProjectChanged, this, [this, sm](Project *project) {
+ ProjectManager *sm = ProjectManager::instance();
+ connect(sm, &ProjectManager::startupProjectChanged, this, [this, sm](Project *project) {
synchronizeTestFrameworks(); // we might have project settings
m_parser->onStartupProjectChanged(project);
removeAllTestToolItems();
@@ -96,8 +100,8 @@ void TestTreeModel::setupParsingConnections()
m_parser, &TestCodeParser::onCppDocumentUpdated, Qt::QueuedConnection);
connect(cppMM, &CppEditor::CppModelManager::aboutToRemoveFiles,
this, [this](const QStringList &files) {
- const FilePaths filesToRemove = FileUtils::toFilePathList(files);
- removeFiles(filesToRemove);
+ markForRemoval(transform<QSet>(files, &FilePath::fromString));
+ sweep();
}, Qt::QueuedConnection);
connect(cppMM, &CppEditor::CppModelManager::projectPartsUpdated,
m_parser, &TestCodeParser::onProjectPartsUpdated);
@@ -105,11 +109,11 @@ void TestTreeModel::setupParsingConnections()
QmlJS::ModelManagerInterface *qmlJsMM = QmlJS::ModelManagerInterface::instance();
connect(qmlJsMM, &QmlJS::ModelManagerInterface::documentUpdated,
m_parser, &TestCodeParser::onQmlDocumentUpdated, Qt::QueuedConnection);
- connect(qmlJsMM,
- &QmlJS::ModelManagerInterface::aboutToRemoveFiles,
- this,
- &TestTreeModel::removeFiles,
- Qt::QueuedConnection);
+ connect(qmlJsMM, &QmlJS::ModelManagerInterface::aboutToRemoveFiles,
+ this, [this](const FilePaths &filePaths) {
+ markForRemoval(Utils::toSet(filePaths));
+ sweep();
+ }, Qt::QueuedConnection);
connectionsInitialized = true;
}
@@ -226,7 +230,7 @@ static QList<ITestTreeItem *> testItemsByName(TestTreeItem *root, const QString
void TestTreeModel::onTargetChanged(Target *target)
{
if (target && target->buildSystem()) {
- const Target *topLevelTarget = SessionManager::startupProject()->targets().first();
+ const Target *topLevelTarget = ProjectManager::startupProject()->targets().first();
connect(topLevelTarget->buildSystem(), &BuildSystem::testInformationUpdated,
this, &TestTreeModel::onBuildSystemTestsUpdated, Qt::UniqueConnection);
disconnect(target->project(), &Project::activeTargetChanged,
@@ -236,7 +240,7 @@ void TestTreeModel::onTargetChanged(Target *target)
void TestTreeModel::onBuildSystemTestsUpdated()
{
- const BuildSystem *bs = SessionManager::startupBuildSystem();
+ const BuildSystem *bs = ProjectManager::startupBuildSystem();
if (!bs || !bs->project())
return;
@@ -333,7 +337,7 @@ void TestTreeModel::synchronizeTestFrameworks()
void TestTreeModel::synchronizeTestTools()
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
TestTools tools;
if (!project || AutotestPlugin::projectSettings(project)->useGlobalSettings()) {
tools = Utils::filtered(TestFrameworkManager::registeredTestTools(),
@@ -459,13 +463,6 @@ void TestTreeModel::clearFailedMarks()
m_failedStateCache.clear();
}
-void TestTreeModel::removeFiles(const FilePaths &files)
-{
- for (const FilePath &file : files)
- markForRemoval(file);
- sweep();
-}
-
void TestTreeModel::markAllFrameworkItemsForRemoval()
{
for (TestTreeItem *frameworkRoot : frameworkRootNodes()) {
@@ -475,15 +472,12 @@ void TestTreeModel::markAllFrameworkItemsForRemoval()
}
}
-void TestTreeModel::markForRemoval(const FilePath &filePath)
+void TestTreeModel::markForRemoval(const QSet<Utils::FilePath> &filePaths)
{
- if (filePath.isEmpty())
- return;
-
for (TestTreeItem *frameworkRoot : frameworkRootNodes()) {
for (int childRow = frameworkRoot->childCount() - 1; childRow >= 0; --childRow) {
TestTreeItem *child = frameworkRoot->childItem(childRow);
- child->markForRemovalRecursively(filePath);
+ child->markForRemovalRecursively(filePaths);
}
}
}
diff --git a/src/plugins/autotest/testtreemodel.h b/src/plugins/autotest/testtreemodel.h
index b8104e8b43..c48c957d99 100644
--- a/src/plugins/autotest/testtreemodel.h
+++ b/src/plugins/autotest/testtreemodel.h
@@ -66,7 +66,7 @@ public:
#endif
void markAllFrameworkItemsForRemoval();
- void markForRemoval(const Utils::FilePath &filePath);
+ void markForRemoval(const QSet<Utils::FilePath> &filePaths);
void sweep();
signals:
@@ -83,7 +83,6 @@ private:
void handleParseResult(const TestParseResult *result, TestTreeItem *rootNode);
void removeAllTestItems();
void removeAllTestToolItems();
- void removeFiles(const Utils::FilePaths &files);
bool sweepChildren(TestTreeItem *item);
void insertItemInParent(TestTreeItem *item, TestTreeItem *root, bool groupingEnabled);
void revalidateCheckState(ITestTreeItem *item);
diff --git a/src/plugins/autotoolsprojectmanager/autogenstep.cpp b/src/plugins/autotoolsprojectmanager/autogenstep.cpp
index c0a383205d..090389716d 100644
--- a/src/plugins/autotoolsprojectmanager/autogenstep.cpp
+++ b/src/plugins/autotoolsprojectmanager/autogenstep.cpp
@@ -42,23 +42,23 @@ private:
void doRun() final;
bool m_runAutogen = false;
+ StringAspect m_arguments{this};
};
AutogenStep::AutogenStep(BuildStepList *bsl, Id id) : AbstractProcessStep(bsl, id)
{
- auto arguments = addAspect<StringAspect>();
- arguments->setSettingsKey("AutotoolsProjectManager.AutogenStep.AdditionalArguments");
- arguments->setLabelText(Tr::tr("Arguments:"));
- arguments->setDisplayStyle(StringAspect::LineEditDisplay);
- arguments->setHistoryCompleter("AutotoolsPM.History.AutogenStepArgs");
+ m_arguments.setSettingsKey("AutotoolsProjectManager.AutogenStep.AdditionalArguments");
+ m_arguments.setLabelText(Tr::tr("Arguments:"));
+ m_arguments.setDisplayStyle(StringAspect::LineEditDisplay);
+ m_arguments.setHistoryCompleter("AutotoolsPM.History.AutogenStepArgs");
- connect(arguments, &BaseAspect::changed, this, [this] { m_runAutogen = true; });
+ connect(&m_arguments, &BaseAspect::changed, this, [this] { m_runAutogen = true; });
setWorkingDirectoryProvider([this] { return project()->projectDirectory(); });
- setCommandLineProvider([this, arguments] {
+ setCommandLineProvider([this] {
return CommandLine(project()->projectDirectory() / "autogen.sh",
- arguments->value(),
+ m_arguments(),
CommandLine::Raw);
});
diff --git a/src/plugins/autotoolsprojectmanager/autotoolsbuildsystem.cpp b/src/plugins/autotoolsprojectmanager/autotoolsbuildsystem.cpp
index 0375ce9ac6..113212be5f 100644
--- a/src/plugins/autotoolsprojectmanager/autotoolsbuildsystem.cpp
+++ b/src/plugins/autotoolsprojectmanager/autotoolsbuildsystem.cpp
@@ -14,6 +14,7 @@
#include <utils/qtcassert.h>
using namespace ProjectExplorer;
+using namespace Utils;
namespace AutotoolsProjectManager::Internal {
@@ -140,7 +141,7 @@ void AutotoolsBuildSystem::updateCppCodeModel()
if (cxxflags.isEmpty())
cxxflags = cflags;
- const QString includeFileBaseDir = projectDirectory().toString();
+ const FilePath includeFileBaseDir = projectDirectory();
rpp.setFlagsForC({kitInfo.cToolChain, cflags, includeFileBaseDir});
rpp.setFlagsForCxx({kitInfo.cxxToolChain, cxxflags, includeFileBaseDir});
diff --git a/src/plugins/autotoolsprojectmanager/makefileparser.cpp b/src/plugins/autotoolsprojectmanager/makefileparser.cpp
index 09fbd66647..e1cac6b186 100644
--- a/src/plugins/autotoolsprojectmanager/makefileparser.cpp
+++ b/src/plugins/autotoolsprojectmanager/makefileparser.cpp
@@ -5,14 +5,16 @@
#include "autotoolsprojectmanagertr.h"
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <QDir>
#include <QFile>
#include <QFileInfo>
+using namespace Utils;
+
namespace AutotoolsProjectManager::Internal {
MakefileParser::MakefileParser(const QString &makefile) : m_makefile(makefile)
@@ -423,7 +425,7 @@ QStringList MakefileParser::parseTermsAfterAssign(const QString &line)
if (assignPos <= 0 || assignPos >= line.size())
return QStringList();
- const QStringList parts = Utils::ProcessArgs::splitArgs(line.mid(assignPos));
+ const QStringList parts = ProcessArgs::splitArgs(line.mid(assignPos), HostOsInfo::hostOs());
QStringList result;
for (int i = 0; i < parts.count(); ++i) {
const QString cur = parts.at(i);
diff --git a/src/plugins/axivion/Axivion.json.in b/src/plugins/axivion/Axivion.json.in
new file mode 100644
index 0000000000..fac0520565
--- /dev/null
+++ b/src/plugins/axivion/Axivion.json.in
@@ -0,0 +1,21 @@
+{
+ \"Name\" : \"Axivion\",
+ \"Version\" : \"$$QTCREATOR_VERSION\",
+ \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
+ \"Revision\" : \"$$QTC_PLUGIN_REVISION\",
+ \"Experimental\" : true,
+ \"Vendor\" : \"The Qt Company Ltd\",
+ \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\",
+ \"License\" : [ \"Commercial Usage\",
+ \"\",
+ \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\",
+ \"\",
+ \"GNU General Public License Usage\",
+ \"\",
+ \"Alternatively, this plugin 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 plugin. 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.\"
+ ],
+ \"Category\" : \"Code Analyzer\",
+ \"Description\" : \"Integration of the axivion dashboard.\",
+ \"Url\" : \"https://www.qt.io\",
+ $$dependencyList
+}
diff --git a/src/plugins/axivion/CMakeLists.txt b/src/plugins/axivion/CMakeLists.txt
new file mode 100644
index 0000000000..b346cc276d
--- /dev/null
+++ b/src/plugins/axivion/CMakeLists.txt
@@ -0,0 +1,15 @@
+add_qtc_plugin(Axivion
+ PLUGIN_DEPENDS
+ Core ProjectExplorer TextEditor
+ DEPENDS Qt::Network Qt::Widgets ExtensionSystem Utils
+ SOURCES
+ axivion.qrc
+ axivionoutputpane.cpp axivionoutputpane.h
+ axivionplugin.cpp axivionplugin.h
+ axivionprojectsettings.h axivionprojectsettings.cpp
+ axivionquery.h axivionquery.cpp
+ axivionresultparser.h axivionresultparser.cpp
+ axivionsettings.cpp axivionsettings.h
+ axivionsettingspage.cpp axivionsettingspage.h
+ axiviontr.h
+)
diff --git a/src/plugins/axivion/axivion.qbs b/src/plugins/axivion/axivion.qbs
new file mode 100644
index 0000000000..bfe8efe6cf
--- /dev/null
+++ b/src/plugins/axivion/axivion.qbs
@@ -0,0 +1,32 @@
+import qbs
+
+QtcPlugin {
+ name: "Axivion"
+
+ Depends { name: "Core" }
+ Depends { name: "ProjectExplorer" }
+ Depends { name: "TextEditor" }
+ Depends { name: "ExtensionSystem" }
+ Depends { name: "Utils" }
+ Depends { name: "Qt.widgets" }
+ Depends { name: "Qt.network" }
+
+ files: [
+ "axivion.qrc",
+ "axivionoutputpane.cpp",
+ "axivionoutputpane.h",
+ "axivionplugin.cpp",
+ "axivionplugin.h",
+ "axivionprojectsettings.h",
+ "axivionprojectsettings.cpp",
+ "axivionquery.h",
+ "axivionquery.cpp",
+ "axivionresultparser.h",
+ "axivionresultparser.cpp",
+ "axivionsettings.cpp",
+ "axivionsettings.h",
+ "axivionsettingspage.cpp",
+ "axivionsettingspage.h",
+ "axiviontr.h",
+ ]
+}
diff --git a/src/plugins/axivion/axivion.qrc b/src/plugins/axivion/axivion.qrc
new file mode 100644
index 0000000000..13d0b616c9
--- /dev/null
+++ b/src/plugins/axivion/axivion.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/axivion">
+ <file>images/axivion.png</file>
+ <file>images/axivion@2x.png</file>
+ </qresource>
+</RCC>
diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp
new file mode 100644
index 0000000000..ba226f63b5
--- /dev/null
+++ b/src/plugins/axivion/axivionoutputpane.cpp
@@ -0,0 +1,209 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "axivionoutputpane.h"
+
+#include "axivionplugin.h"
+#include "axivionresultparser.h"
+#include "axiviontr.h"
+
+#include <utils/qtcassert.h>
+#include <utils/utilsicons.h>
+
+#include <QFormLayout>
+#include <QLabel>
+#include <QScrollArea>
+#include <QStackedWidget>
+#include <QTextBrowser>
+#include <QToolButton>
+
+namespace Axivion::Internal {
+
+class DashboardWidget : public QScrollArea
+{
+public:
+ explicit DashboardWidget(QWidget *parent = nullptr);
+ void updateUi();
+ bool hasProject() const { return !m_project->text().isEmpty(); }
+private:
+ QLabel *m_project = nullptr;
+ QLabel *m_loc = nullptr;
+ QFormLayout *m_formLayout = nullptr;
+};
+
+DashboardWidget::DashboardWidget(QWidget *parent)
+ : QScrollArea(parent)
+{
+ QWidget *widget = new QWidget(this);
+ QVBoxLayout *layout = new QVBoxLayout(widget);
+ QFormLayout *projectLayout = new QFormLayout;
+ m_project = new QLabel(this);
+ projectLayout->addRow(Tr::tr("Project:"), m_project);
+ m_loc = new QLabel(this);
+ projectLayout->addRow(Tr::tr("Lines of Code:"), m_loc);
+ layout->addLayout(projectLayout);
+ m_formLayout = new QFormLayout;
+ layout->addLayout(m_formLayout);
+ setWidget(widget);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ setWidgetResizable(true);
+}
+
+void DashboardWidget::updateUi()
+{
+ const ProjectInfo &info = AxivionPlugin::projectInfo();
+ m_project->setText(info.name);
+ m_loc->setText({});
+ while (m_formLayout->rowCount())
+ m_formLayout->removeRow(0);
+
+ if (info.versions.isEmpty())
+ return;
+
+ const ResultVersion &last = info.versions.last();
+ m_loc->setText(QString::number(last.linesOfCode));
+
+ const QString tmpl("%1 %2 +%3 / -%4");
+ auto apply = [&tmpl](int t, int a, int r){
+ QChar tr = (a == r ? '=' : (a < r ? '^' : 'v'));
+ return tmpl.arg(t, 10, 10, QLatin1Char(' ')).arg(tr).arg(a, 5, 10, QLatin1Char(' '))
+ .arg(r, 5, 10, QLatin1Char(' '));
+ };
+ const QList<IssueKind> &issueKinds = info.issueKinds;
+ auto toolTip = [issueKinds](const QString &prefix){
+ for (const IssueKind &kind : issueKinds) {
+ if (kind.prefix == prefix)
+ return kind.nicePlural;
+ }
+ return QString();
+ };
+ int allTotal = 0, allAdded = 0, allRemoved = 0;
+ for (auto issueCount : std::as_const(last.issueCounts)) {
+ allTotal += issueCount.total;
+ allAdded += issueCount.added;
+ allRemoved += issueCount.removed;
+ const QString txt = apply(issueCount.total, issueCount.added, issueCount.removed);
+ const QString currentToolTip = toolTip(issueCount.issueKind);
+ QLabel *label = new QLabel(issueCount.issueKind, this);
+ label->setToolTip(currentToolTip);
+ QLabel *values = new QLabel(txt, this);
+ values->setToolTip(currentToolTip);
+ m_formLayout->addRow(label, values);
+ }
+
+ QLabel *label = new QLabel(apply(allTotal, allAdded, allRemoved), this);
+ m_formLayout->addRow(Tr::tr("Total:"), label);
+}
+
+AxivionOutputPane::AxivionOutputPane(QObject *parent)
+ : Core::IOutputPane(parent)
+{
+ m_outputWidget = new QStackedWidget;
+ DashboardWidget *dashboardWidget = new DashboardWidget(m_outputWidget);
+ m_outputWidget->addWidget(dashboardWidget);
+ QTextBrowser *browser = new QTextBrowser(m_outputWidget);
+ m_outputWidget->addWidget(browser);
+}
+
+AxivionOutputPane::~AxivionOutputPane()
+{
+ if (!m_outputWidget->parent())
+ delete m_outputWidget;
+}
+
+QWidget *AxivionOutputPane::outputWidget(QWidget *parent)
+{
+ if (m_outputWidget)
+ m_outputWidget->setParent(parent);
+ else
+ QTC_CHECK(false);
+ return m_outputWidget;
+}
+
+QList<QWidget *> AxivionOutputPane::toolBarWidgets() const
+{
+ QList<QWidget *> buttons;
+ auto showDashboard = new QToolButton(m_outputWidget);
+ showDashboard->setIcon(Utils::Icons::ONLINE_TOOLBAR.icon());
+ showDashboard->setToolTip(Tr::tr("Show dashboard"));
+ connect(showDashboard, &QToolButton::clicked, this, [this]{
+ QTC_ASSERT(m_outputWidget, return);
+ m_outputWidget->setCurrentIndex(0);
+ });
+ buttons.append(showDashboard);
+ return buttons;
+}
+
+QString AxivionOutputPane::displayName() const
+{
+ return Tr::tr("Axivion");
+}
+
+int AxivionOutputPane::priorityInStatusBar() const
+{
+ return -1;
+}
+
+void AxivionOutputPane::clearContents()
+{
+}
+
+void AxivionOutputPane::setFocus()
+{
+}
+
+bool AxivionOutputPane::hasFocus() const
+{
+ return false;
+}
+
+bool AxivionOutputPane::canFocus() const
+{
+ return true;
+}
+
+bool AxivionOutputPane::canNavigate() const
+{
+ return true;
+}
+
+bool AxivionOutputPane::canNext() const
+{
+ return false;
+}
+
+bool AxivionOutputPane::canPrevious() const
+{
+ return false;
+}
+
+void AxivionOutputPane::goToNext()
+{
+}
+
+void AxivionOutputPane::goToPrev()
+{
+}
+
+void AxivionOutputPane::updateDashboard()
+{
+ if (auto dashboard = static_cast<DashboardWidget *>(m_outputWidget->widget(0))) {
+ dashboard->updateUi();
+ m_outputWidget->setCurrentIndex(0);
+ if (dashboard->hasProject())
+ flash();
+ }
+}
+
+void AxivionOutputPane::updateAndShowRule(const QString &ruleHtml)
+{
+ if (auto browser = static_cast<QTextBrowser *>(m_outputWidget->widget(1))) {
+ browser->setText(ruleHtml);
+ if (!ruleHtml.isEmpty()) {
+ m_outputWidget->setCurrentIndex(1);
+ popup(Core::IOutputPane::NoModeSwitch);
+ }
+ }
+}
+
+} // Axivion::Internal
diff --git a/src/plugins/axivion/axivionoutputpane.h b/src/plugins/axivion/axivionoutputpane.h
new file mode 100644
index 0000000000..d11accc140
--- /dev/null
+++ b/src/plugins/axivion/axivionoutputpane.h
@@ -0,0 +1,42 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <coreplugin/ioutputpane.h>
+
+QT_BEGIN_NAMESPACE
+class QStackedWidget;
+QT_END_NAMESPACE
+
+namespace Axivion::Internal {
+
+class AxivionOutputPane : public Core::IOutputPane
+{
+ Q_OBJECT
+public:
+ explicit AxivionOutputPane(QObject *parent = nullptr);
+ ~AxivionOutputPane();
+
+ // IOutputPane interface
+ QWidget *outputWidget(QWidget *parent) override;
+ QList<QWidget *> toolBarWidgets() const override;
+ QString displayName() const override;
+ int priorityInStatusBar() const override;
+ void clearContents() override;
+ void setFocus() override;
+ bool hasFocus() const override;
+ bool canFocus() const override;
+ bool canNavigate() const override;
+ bool canNext() const override;
+ bool canPrevious() const override;
+ void goToNext() override;
+ void goToPrev() override;
+
+ void updateDashboard();
+ void updateAndShowRule(const QString &ruleHtml);
+private:
+ QStackedWidget *m_outputWidget = nullptr;
+};
+
+} // Axivion::Internal
diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp
new file mode 100644
index 0000000000..7648448132
--- /dev/null
+++ b/src/plugins/axivion/axivionplugin.cpp
@@ -0,0 +1,348 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "axivionplugin.h"
+
+#include "axivionoutputpane.h"
+#include "axivionprojectsettings.h"
+#include "axivionquery.h"
+#include "axivionresultparser.h"
+#include "axivionsettings.h"
+#include "axivionsettingspage.h"
+#include "axiviontr.h"
+
+#include <coreplugin/editormanager/documentmodel.h>
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/messagemanager.h>
+
+#include <extensionsystem/pluginmanager.h>
+
+#include <projectexplorer/buildsystem.h>
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
+#include <projectexplorer/projectpanelfactory.h>
+
+#include <texteditor/textdocument.h>
+#include <texteditor/texteditor.h>
+#include <texteditor/textmark.h>
+
+#include <utils/qtcassert.h>
+#include <utils/utilsicons.h>
+
+#include <QAction>
+#include <QMessageBox>
+#include <QTimer>
+
+constexpr char AxivionTextMarkId[] = "AxivionTextMark";
+
+namespace Axivion::Internal {
+
+class AxivionPluginPrivate : public QObject
+{
+public:
+ AxivionProjectSettings *projectSettings(ProjectExplorer::Project *project);
+ void onStartupProjectChanged();
+ void fetchProjectInfo(const QString &projectName);
+ void handleProjectInfo(const ProjectInfo &info);
+ void handleOpenedDocs(ProjectExplorer::Project *project);
+ void onDocumentOpened(Core::IDocument *doc);
+ void onDocumentClosed(Core::IDocument * doc);
+ void clearAllMarks();
+ void handleIssuesForFile(const IssuesList &issues);
+ void fetchRuleInfo(const QString &id);
+
+ AxivionSettings m_axivionSettings;
+ AxivionSettingsPage m_axivionSettingsPage{&m_axivionSettings};
+ AxivionOutputPane m_axivionOutputPane;
+ QHash<ProjectExplorer::Project *, AxivionProjectSettings *> m_axivionProjectSettings;
+ ProjectInfo m_currentProjectInfo;
+ bool m_runningQuery = false;
+};
+
+static AxivionPlugin *s_instance = nullptr;
+static AxivionPluginPrivate *dd = nullptr;
+
+class AxivionTextMark : public TextEditor::TextMark
+{
+public:
+ AxivionTextMark(const Utils::FilePath &filePath, const ShortIssue &issue);
+
+private:
+ QString m_id;
+};
+
+AxivionTextMark::AxivionTextMark(const Utils::FilePath &filePath, const ShortIssue &issue)
+ : TextEditor::TextMark(filePath, issue.lineNumber, {Tr::tr("Axivion"), AxivionTextMarkId})
+ , m_id(issue.id)
+{
+ const QString markText = issue.entity.isEmpty() ? issue.message
+ : issue.entity + ": " + issue.message;
+ setToolTip(issue.errorNumber + " " + markText);
+ setPriority(TextEditor::TextMark::NormalPriority);
+ setLineAnnotation(markText);
+ setActionsProvider([this]{
+ auto action = new QAction;
+ action->setIcon(Utils::Icons::INFO.icon());
+ action->setToolTip(Tr::tr("Show rule details"));
+ QObject::connect(action, &QAction::triggered,
+ dd, [this]{ dd->fetchRuleInfo(m_id); });
+ return QList{action};
+ });
+}
+
+AxivionPlugin::AxivionPlugin()
+{
+ s_instance = this;
+}
+
+AxivionPlugin::~AxivionPlugin()
+{
+ if (dd && !dd->m_axivionProjectSettings.isEmpty()) {
+ qDeleteAll(dd->m_axivionProjectSettings);
+ dd->m_axivionProjectSettings.clear();
+ }
+ delete dd;
+ dd = nullptr;
+}
+
+AxivionPlugin *AxivionPlugin::instance()
+{
+ return s_instance;
+}
+
+bool AxivionPlugin::initialize(const QStringList &arguments, QString *errorMessage)
+{
+ Q_UNUSED(arguments)
+ Q_UNUSED(errorMessage)
+
+ dd = new AxivionPluginPrivate;
+ dd->m_axivionSettings.fromSettings(Core::ICore::settings());
+
+ auto panelFactory = new ProjectExplorer::ProjectPanelFactory;
+ panelFactory->setPriority(250);
+ panelFactory->setDisplayName(Tr::tr("Axivion"));
+ panelFactory->setCreateWidgetFunction([](ProjectExplorer::Project *project){
+ return new AxivionProjectSettingsWidget(project);
+ });
+ ProjectExplorer::ProjectPanelFactory::registerFactory(panelFactory);
+ connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged,
+ dd, &AxivionPluginPrivate::onStartupProjectChanged);
+ connect(Core::EditorManager::instance(), &Core::EditorManager::documentOpened,
+ dd, &AxivionPluginPrivate::onDocumentOpened);
+ connect(Core::EditorManager::instance(), &Core::EditorManager::documentClosed,
+ dd, &AxivionPluginPrivate::onDocumentClosed);
+ return true;
+}
+
+AxivionSettings *AxivionPlugin::settings()
+{
+ QTC_ASSERT(dd, return nullptr);
+ return &dd->m_axivionSettings;
+}
+
+AxivionProjectSettings *AxivionPlugin::projectSettings(ProjectExplorer::Project *project)
+{
+ QTC_ASSERT(project, return nullptr);
+ QTC_ASSERT(dd, return nullptr);
+
+ return dd->projectSettings(project);
+}
+
+bool AxivionPlugin::handleCertificateIssue()
+{
+ QTC_ASSERT(dd, return false);
+
+ const QString serverHost = QUrl(dd->m_axivionSettings.server.dashboard).host();
+ if (QMessageBox::question(Core::ICore::dialogParent(), Tr::tr("Certificate Error"),
+ Tr::tr("Server certificate for %1 cannot be authenticated.\n"
+ "Do you want to disable SSL verification for this server?\n"
+ "Note: This can expose you to man-in-the-middle attack.")
+ .arg(serverHost))
+ != QMessageBox::Yes) {
+ return false;
+ }
+ dd->m_axivionSettings.server.validateCert = false;
+ emit s_instance->settingsChanged();
+ return true;
+}
+
+void AxivionPlugin::fetchProjectInfo(const QString &projectName)
+{
+ QTC_ASSERT(dd, return);
+ dd->fetchProjectInfo(projectName);
+}
+
+ProjectInfo AxivionPlugin::projectInfo()
+{
+ QTC_ASSERT(dd, return {});
+ return dd->m_currentProjectInfo;
+}
+
+AxivionProjectSettings *AxivionPluginPrivate::projectSettings(ProjectExplorer::Project *project)
+{
+ auto &settings = m_axivionProjectSettings[project];
+ if (!settings)
+ settings = new AxivionProjectSettings(project);
+ return settings;
+}
+
+void AxivionPluginPrivate::onStartupProjectChanged()
+{
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
+ if (!project) {
+ clearAllMarks();
+ m_currentProjectInfo = ProjectInfo();
+ m_axivionOutputPane.updateDashboard();
+ return;
+ }
+
+ const AxivionProjectSettings *projSettings = projectSettings(project);
+ fetchProjectInfo(projSettings->dashboardProjectName());
+}
+
+void AxivionPluginPrivate::fetchProjectInfo(const QString &projectName)
+{
+ if (m_runningQuery) { // re-schedule
+ QTimer::singleShot(3000, [this, projectName]{ fetchProjectInfo(projectName); });
+ return;
+ }
+ clearAllMarks();
+ if (projectName.isEmpty()) {
+ m_currentProjectInfo = ProjectInfo();
+ m_axivionOutputPane.updateDashboard();
+ return;
+ }
+ m_runningQuery = true;
+
+ AxivionQuery query(AxivionQuery::ProjectInfo, {projectName});
+ AxivionQueryRunner *runner = new AxivionQueryRunner(query, this);
+ connect(runner, &AxivionQueryRunner::resultRetrieved, this, [this](const QByteArray &result){
+ handleProjectInfo(ResultParser::parseProjectInfo(result));
+ });
+ connect(runner, &AxivionQueryRunner::finished, [runner]{ runner->deleteLater(); });
+ runner->start();
+}
+
+void AxivionPluginPrivate::fetchRuleInfo(const QString &id)
+{
+ if (m_runningQuery) {
+ QTimer::singleShot(3000, [this, id]{ fetchRuleInfo(id); });
+ return;
+ }
+
+ const QStringList args = id.split(':');
+ QTC_ASSERT(args.size() == 2, return);
+ m_runningQuery = true;
+ AxivionQuery query(AxivionQuery::RuleInfo, args);
+ AxivionQueryRunner *runner = new AxivionQueryRunner(query, this);
+ connect(runner, &AxivionQueryRunner::resultRetrieved, this, [this](const QByteArray &result){
+ m_runningQuery = false;
+ m_axivionOutputPane.updateAndShowRule(ResultParser::parseRuleInfo(result));
+ });
+ connect(runner, &AxivionQueryRunner::finished, [runner]{ runner->deleteLater(); });
+ runner->start();
+}
+
+void AxivionPluginPrivate::handleOpenedDocs(ProjectExplorer::Project *project)
+{
+ if (project && ProjectExplorer::ProjectManager::startupProject() != project)
+ return;
+ const QList<Core::IDocument *> openDocuments = Core::DocumentModel::openedDocuments();
+ for (Core::IDocument *doc : openDocuments)
+ onDocumentOpened(doc);
+ if (project)
+ disconnect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::projectFinishedParsing,
+ this, &AxivionPluginPrivate::handleOpenedDocs);
+}
+
+void AxivionPluginPrivate::clearAllMarks()
+{
+ const QList<Core::IDocument *> openDocuments = Core::DocumentModel::openedDocuments();
+ for (Core::IDocument *doc : openDocuments)
+ onDocumentClosed(doc);
+}
+
+void AxivionPluginPrivate::handleProjectInfo(const ProjectInfo &info)
+{
+ m_runningQuery = false;
+ if (!info.error.isEmpty()) {
+ Core::MessageManager::writeFlashing("Axivion: " + info.error);
+ return;
+ }
+
+ m_currentProjectInfo = info;
+ m_axivionOutputPane.updateDashboard();
+
+ if (m_currentProjectInfo.name.isEmpty())
+ return;
+
+ // handle already opened documents
+ if (auto buildSystem = ProjectExplorer::ProjectManager::startupBuildSystem();
+ !buildSystem || !buildSystem->isParsing()) {
+ handleOpenedDocs(nullptr);
+ } else {
+ connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::projectFinishedParsing,
+ this, &AxivionPluginPrivate::handleOpenedDocs);
+ }
+}
+
+void AxivionPluginPrivate::onDocumentOpened(Core::IDocument *doc)
+{
+ if (m_currentProjectInfo.name.isEmpty()) // we do not have a project info (yet)
+ return;
+
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
+ if (!doc || !project->isKnownFile(doc->filePath()))
+ return;
+
+ Utils::FilePath relative = doc->filePath().relativeChildPath(project->projectDirectory());
+ // for now only style violations
+ AxivionQuery query(AxivionQuery::IssuesForFileList, {m_currentProjectInfo.name, "SV",
+ relative.path() } );
+ AxivionQueryRunner *runner = new AxivionQueryRunner(query, this);
+ connect(runner, &AxivionQueryRunner::resultRetrieved, this, [this](const QByteArray &result){
+ handleIssuesForFile(ResultParser::parseIssuesList(result));
+ });
+ connect(runner, &AxivionQueryRunner::finished, [runner]{ runner->deleteLater(); });
+ runner->start();
+}
+
+void AxivionPluginPrivate::onDocumentClosed(Core::IDocument *doc)
+{
+ const auto document = qobject_cast<TextEditor::TextDocument *>(doc);
+ if (!document)
+ return;
+
+ const TextEditor::TextMarks marks = document->marks();
+ for (auto m : marks) {
+ if (m->category().id == AxivionTextMarkId)
+ delete m;
+ }
+}
+
+void AxivionPluginPrivate::handleIssuesForFile(const IssuesList &issues)
+{
+ if (issues.issues.isEmpty())
+ return;
+
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
+ if (!project)
+ return;
+
+ const Utils::FilePath filePath = project->projectDirectory()
+ .pathAppended(issues.issues.first().filePath);
+
+ const Utils::Id axivionId(AxivionTextMarkId);
+ for (const ShortIssue &issue : std::as_const(issues.issues)) {
+ // FIXME the line location can be wrong (even the whole issue could be wrong)
+ // depending on whether this line has been changed since the last axivion run and the
+ // current state of the file - some magic has to happen here
+ new AxivionTextMark(filePath, issue);
+ }
+}
+
+} // Axivion::Internal
diff --git a/src/plugins/axivion/axivionplugin.h b/src/plugins/axivion/axivionplugin.h
new file mode 100644
index 0000000000..992971b6bd
--- /dev/null
+++ b/src/plugins/axivion/axivionplugin.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <extensionsystem/iplugin.h>
+
+namespace ProjectExplorer { class Project; }
+
+namespace Axivion::Internal {
+
+class AxivionSettings;
+class AxivionProjectSettings;
+class ProjectInfo;
+
+class AxivionPlugin final : public ExtensionSystem::IPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Axivion.json")
+
+public:
+ AxivionPlugin();
+ ~AxivionPlugin() final;
+
+ static AxivionPlugin *instance();
+ static AxivionSettings *settings();
+ static AxivionProjectSettings *projectSettings(ProjectExplorer::Project *project);
+
+ static bool handleCertificateIssue();
+ static void fetchProjectInfo(const QString &projectName);
+ static ProjectInfo projectInfo();
+signals:
+ void settingsChanged();
+
+private:
+ bool initialize(const QStringList &arguments, QString *errorMessage) final;
+ void extensionsInitialized() final {}
+};
+
+} // Axivion::Internal
+
diff --git a/src/plugins/axivion/axivionprojectsettings.cpp b/src/plugins/axivion/axivionprojectsettings.cpp
new file mode 100644
index 0000000000..32689ae950
--- /dev/null
+++ b/src/plugins/axivion/axivionprojectsettings.cpp
@@ -0,0 +1,184 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "axivionprojectsettings.h"
+
+#include "axivionplugin.h"
+#include "axivionquery.h"
+#include "axivionresultparser.h"
+#include "axivionsettings.h"
+#include "axiviontr.h"
+
+#include <projectexplorer/project.h>
+#include <utils/infolabel.h>
+#include <utils/qtcassert.h>
+
+#include <QPushButton>
+#include <QTreeWidget>
+#include <QVBoxLayout>
+
+namespace Axivion::Internal {
+
+const char PSK_PROJECTNAME[] = "Axivion.ProjectName";
+
+AxivionProjectSettings::AxivionProjectSettings(ProjectExplorer::Project *project)
+ : m_project{project}
+{
+ load();
+ connect(project, &ProjectExplorer::Project::settingsLoaded,
+ this, &AxivionProjectSettings::load);
+ connect(project, &ProjectExplorer::Project::aboutToSaveSettings,
+ this, &AxivionProjectSettings::save);
+}
+
+void AxivionProjectSettings::load()
+{
+ m_dashboardProjectName = m_project->namedSettings(PSK_PROJECTNAME).toString();
+}
+
+void AxivionProjectSettings::save()
+{
+ m_project->setNamedSettings(PSK_PROJECTNAME, m_dashboardProjectName);
+}
+
+AxivionProjectSettingsWidget::AxivionProjectSettingsWidget(ProjectExplorer::Project *project,
+ QWidget *parent)
+ : ProjectExplorer::ProjectSettingsWidget{parent}
+ , m_projectSettings(AxivionPlugin::projectSettings(project))
+ , m_globalSettings(AxivionPlugin::settings())
+{
+ setUseGlobalSettingsCheckBoxVisible(false);
+ setUseGlobalSettingsLabelVisible(true);
+ setGlobalSettingsId("Axivion.Settings.General"); // FIXME move id to constants
+ // setup ui
+ auto verticalLayout = new QVBoxLayout(this);
+ verticalLayout->setContentsMargins(0, 0, 0, 0);
+
+ m_linkedProject = new QLabel(this);
+ verticalLayout->addWidget(m_linkedProject);
+
+ m_dashboardProjects = new QTreeWidget(this);
+ m_dashboardProjects->setHeaderHidden(true);
+ m_dashboardProjects->setRootIsDecorated(false);
+ verticalLayout->addWidget(new QLabel(Tr::tr("Dashboard projects:")));
+ verticalLayout->addWidget(m_dashboardProjects);
+
+ m_infoLabel = new Utils::InfoLabel(this);
+ m_infoLabel->setVisible(false);
+ verticalLayout->addWidget(m_infoLabel);
+
+ auto horizontalLayout = new QHBoxLayout;
+ horizontalLayout->setContentsMargins(0, 0, 0, 0);
+ m_fetchProjects = new QPushButton(Tr::tr("Fetch Projects"));
+ horizontalLayout->addWidget(m_fetchProjects);
+ m_link = new QPushButton(Tr::tr("Link Project"));
+ m_link->setEnabled(false);
+ horizontalLayout->addWidget(m_link);
+ m_unlink = new QPushButton(Tr::tr("Unlink Project"));
+ m_unlink->setEnabled(false);
+ horizontalLayout->addWidget(m_unlink);
+ verticalLayout->addLayout(horizontalLayout);
+
+ connect(m_dashboardProjects, &QTreeWidget::itemSelectionChanged,
+ this, &AxivionProjectSettingsWidget::updateEnabledStates);
+ connect(m_fetchProjects, &QPushButton::clicked,
+ this, &AxivionProjectSettingsWidget::fetchProjects);
+ connect(m_link, &QPushButton::clicked,
+ this, &AxivionProjectSettingsWidget::linkProject);
+ connect(m_unlink, &QPushButton::clicked,
+ this, &AxivionProjectSettingsWidget::unlinkProject);
+ connect(AxivionPlugin::instance(), &AxivionPlugin::settingsChanged,
+ this, &AxivionProjectSettingsWidget::onSettingsChanged);
+
+ updateUi();
+}
+
+void AxivionProjectSettingsWidget::fetchProjects()
+{
+ m_dashboardProjects->clear();
+ m_fetchProjects->setEnabled(false);
+ m_infoLabel->setVisible(false);
+ // TODO perform query and populate m_dashboardProjects
+ const AxivionQuery query(AxivionQuery::DashboardInfo);
+ AxivionQueryRunner *runner = new AxivionQueryRunner(query, this);
+ connect(runner, &AxivionQueryRunner::resultRetrieved,
+ this, [this](const QByteArray &result){
+ onDashboardInfoReceived(ResultParser::parseDashboardInfo(result));
+ });
+ connect(runner, &AxivionQueryRunner::finished, this, [runner]{ runner->deleteLater(); });
+ runner->start();
+}
+
+void AxivionProjectSettingsWidget::onDashboardInfoReceived(const DashboardInfo &info)
+{
+ if (!info.error.isEmpty()) {
+ m_infoLabel->setText(info.error);
+ m_infoLabel->setType(Utils::InfoLabel::Error);
+ m_infoLabel->setVisible(true);
+ updateEnabledStates();
+ return;
+ }
+
+ for (const Project &project : info.projects)
+ new QTreeWidgetItem(m_dashboardProjects, {project.name});
+ updateEnabledStates();
+}
+
+void AxivionProjectSettingsWidget::onSettingsChanged()
+{
+ m_dashboardProjects->clear();
+ m_infoLabel->setVisible(false);
+ updateUi();
+}
+
+void AxivionProjectSettingsWidget::linkProject()
+{
+ const QList<QTreeWidgetItem *> selected = m_dashboardProjects->selectedItems();
+ QTC_ASSERT(selected.size() == 1, return);
+
+ const QString projectName = selected.first()->text(0);
+ m_projectSettings->setDashboardProjectName(projectName);
+ updateUi();
+ AxivionPlugin::fetchProjectInfo(projectName);
+}
+
+void AxivionProjectSettingsWidget::unlinkProject()
+{
+ QTC_ASSERT(!m_projectSettings->dashboardProjectName().isEmpty(), return);
+
+ m_projectSettings->setDashboardProjectName({});
+ updateUi();
+ AxivionPlugin::fetchProjectInfo({});
+}
+
+void AxivionProjectSettingsWidget::updateUi()
+{
+ const QString projectName = m_projectSettings->dashboardProjectName();
+ if (projectName.isEmpty())
+ m_linkedProject->setText(Tr::tr("This project is not linked to a dashboard project."));
+ else
+ m_linkedProject->setText(Tr::tr("This project is linked to \"%1\".").arg(projectName));
+ updateEnabledStates();
+}
+
+void AxivionProjectSettingsWidget::updateEnabledStates()
+{
+ const bool hasDashboardSettings = m_globalSettings->curl().isExecutableFile()
+ && !m_globalSettings->server.dashboard.isEmpty()
+ && !m_globalSettings->server.token.isEmpty();
+ const bool linked = !m_projectSettings->dashboardProjectName().isEmpty();
+ const bool linkable = m_dashboardProjects->topLevelItemCount()
+ && !m_dashboardProjects->selectedItems().isEmpty();
+
+ m_fetchProjects->setEnabled(hasDashboardSettings);
+ m_link->setEnabled(!linked && linkable);
+ m_unlink->setEnabled(linked);
+
+ if (!hasDashboardSettings) {
+ m_infoLabel->setText(Tr::tr("Incomplete or misconfigured settings."));
+ m_infoLabel->setType(Utils::InfoLabel::NotOk);
+ m_infoLabel->setVisible(true);
+ }
+}
+
+} // Axivion::Internal
diff --git a/src/plugins/axivion/axivionprojectsettings.h b/src/plugins/axivion/axivionprojectsettings.h
new file mode 100644
index 0000000000..d5bc1fd2e6
--- /dev/null
+++ b/src/plugins/axivion/axivionprojectsettings.h
@@ -0,0 +1,67 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "axivionsettings.h"
+
+#include <projectexplorer/projectsettingswidget.h>
+
+#include <QObject>
+
+QT_BEGIN_NAMESPACE
+class QLabel;
+class QPushButton;
+class QTreeWidget;
+QT_END_NAMESPACE
+
+namespace ProjectExplorer { class Project; }
+
+namespace Utils { class InfoLabel; }
+
+namespace Axivion::Internal {
+
+class DashboardInfo;
+
+class AxivionProjectSettings : public QObject
+{
+public:
+ explicit AxivionProjectSettings(ProjectExplorer::Project *project);
+
+ void setDashboardProjectName(const QString &name) { m_dashboardProjectName = name; }
+ QString dashboardProjectName() const { return m_dashboardProjectName; }
+
+private:
+ void load();
+ void save();
+
+ ProjectExplorer::Project *m_project = nullptr;
+ QString m_dashboardProjectName;
+};
+
+class AxivionProjectSettingsWidget : public ProjectExplorer::ProjectSettingsWidget
+{
+public:
+ explicit AxivionProjectSettingsWidget(ProjectExplorer::Project *project,
+ QWidget *parent = nullptr);
+
+private:
+ void fetchProjects();
+ void onDashboardInfoReceived(const DashboardInfo &info);
+ void onSettingsChanged();
+ void linkProject();
+ void unlinkProject();
+ void updateUi();
+ void updateEnabledStates();
+
+ AxivionProjectSettings *m_projectSettings = nullptr;
+ AxivionSettings *m_globalSettings;
+ QLabel *m_linkedProject = nullptr;
+ QTreeWidget *m_dashboardProjects = nullptr;
+ QPushButton *m_fetchProjects = nullptr;
+ QPushButton *m_link = nullptr;
+ QPushButton *m_unlink = nullptr;
+ Utils::InfoLabel *m_infoLabel = nullptr;
+};
+
+} // Axivion::Internal
diff --git a/src/plugins/axivion/axivionquery.cpp b/src/plugins/axivion/axivionquery.cpp
new file mode 100644
index 0000000000..fda0eccf12
--- /dev/null
+++ b/src/plugins/axivion/axivionquery.cpp
@@ -0,0 +1,97 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "axivionquery.h"
+
+#include "axivionplugin.h"
+#include "axivionsettings.h"
+
+#include <utils/processenums.h>
+#include <utils/qtcassert.h>
+
+#include <QUrl>
+
+using namespace Utils;
+
+namespace Axivion::Internal {
+
+AxivionQuery::AxivionQuery(QueryType type, const QStringList &parameters)
+ : m_type(type)
+ , m_parameters(parameters)
+{
+}
+
+QString AxivionQuery::toString() const
+{
+ QString query = "/api"; // common for all except RuleInfo
+ switch (m_type) {
+ case NoQuery:
+ return {};
+ case DashboardInfo:
+ return query;
+ case ProjectInfo:
+ QTC_ASSERT(m_parameters.size() == 1, return {});
+ query += "/projects/" + QUrl::toPercentEncoding(m_parameters.first());
+ return query;
+ case IssuesForFileList:
+ QTC_ASSERT(m_parameters.size() == 3, return {});
+ // FIXME shall we validate the kind? (some kinds do not support path filter)
+ query += "/projects/" + QUrl::toPercentEncoding(m_parameters.first())
+ + "/issues?kind=" + m_parameters.at(1) + "&filter_path="
+ + QUrl::toPercentEncoding(m_parameters.at(2)) + "&format=csv";
+ return query;
+ case RuleInfo:
+ QTC_ASSERT(m_parameters.size() == 2, return {});
+ query = "/projects/" + QUrl::toPercentEncoding(m_parameters.first())
+ + "/issues/" + m_parameters.at(1) + "/rule";
+ return query;
+ }
+
+ return {};
+}
+
+AxivionQueryRunner::AxivionQueryRunner(const AxivionQuery &query, QObject *parent)
+ : QObject(parent)
+{
+ const AxivionSettings *settings = AxivionPlugin::settings();
+ const AxivionServer server = settings->server;
+
+ QStringList args = server.curlArguments();
+ args << "-i";
+ args << "--header" << "Authorization: AxToken " + server.token;
+
+ QString url = server.dashboard;
+ while (url.endsWith('/')) url.chop(1);
+ url += query.toString();
+ args << url;
+
+ m_process.setCommand({settings->curl(), args});
+ connect(&m_process, &Process::done, this, [this]{
+ if (m_process.result() != ProcessResult::FinishedWithSuccess) {
+ const int exitCode = m_process.exitCode();
+ if (m_process.exitStatus() == QProcess::NormalExit
+ && (exitCode == 35 || exitCode == 60)
+ && AxivionPlugin::handleCertificateIssue()) {
+ // prepend -k for re-requesting same query
+ CommandLine cmdline = m_process.commandLine();
+ cmdline.prependArgs({"-k"});
+ m_process.close();
+ m_process.setCommand(cmdline);
+ start();
+ return;
+ }
+ emit resultRetrieved(m_process.readAllRawStandardError());
+ } else {
+ emit resultRetrieved(m_process.readAllRawStandardOutput());
+ }
+ emit finished();
+ });
+}
+
+void AxivionQueryRunner::start()
+{
+ QTC_ASSERT(!m_process.isRunning(), return);
+ m_process.start();
+}
+
+} // Axivion::Internal
diff --git a/src/plugins/axivion/axivionquery.h b/src/plugins/axivion/axivionquery.h
new file mode 100644
index 0000000000..11af5bbd87
--- /dev/null
+++ b/src/plugins/axivion/axivionquery.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <utils/process.h>
+
+#include <QObject>
+
+namespace Axivion::Internal {
+
+class AxivionQuery
+{
+public:
+ enum QueryType {NoQuery, DashboardInfo, ProjectInfo, IssuesForFileList, RuleInfo};
+ explicit AxivionQuery(QueryType type, const QStringList &parameters = {});
+
+ QString toString() const;
+
+private:
+ QueryType m_type = NoQuery;
+ QStringList m_parameters;
+};
+
+class AxivionQueryRunner : public QObject
+{
+ Q_OBJECT
+public:
+ explicit AxivionQueryRunner(const AxivionQuery &query, QObject *parent = nullptr);
+ void start();
+
+signals:
+ void finished();
+ void resultRetrieved(const QByteArray &json);
+
+private:
+ Utils::Process m_process;
+};
+
+} // Axivion::Internal
diff --git a/src/plugins/axivion/axivionresultparser.cpp b/src/plugins/axivion/axivionresultparser.cpp
new file mode 100644
index 0000000000..0d8bb27b7d
--- /dev/null
+++ b/src/plugins/axivion/axivionresultparser.cpp
@@ -0,0 +1,347 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "axivionresultparser.h"
+
+#include <utils/qtcassert.h>
+
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QRegularExpression>
+
+#include <utility>
+
+namespace Axivion::Internal {
+
+static std::pair<QByteArray, QByteArray> splitHeaderAndBody(const QByteArray &input)
+{
+ QByteArray header;
+ QByteArray json;
+ int emptyLine = input.indexOf("\r\n\r\n"); // we always get \r\n as line separator
+ if (emptyLine != -1) {
+ header = input.left(emptyLine);
+ json = input.mid(emptyLine + 4);
+ } else {
+ json = input;
+ }
+ return {header, json};
+}
+
+static int httpStatus(const QByteArray &header)
+{
+ int firstHeaderEnd = header.indexOf("\r\n");
+ if (firstHeaderEnd == -1)
+ return 600; // unexpected header
+ const QString firstLine = QString::fromUtf8(header.first(firstHeaderEnd));
+ static const QRegularExpression regex(R"(^HTTP/\d\.\d (\d{3}) .*$)");
+ const QRegularExpressionMatch match = regex.match(firstLine);
+ return match.hasMatch() ? match.captured(1).toInt() : 601;
+}
+
+static BaseResult prehandleHeader(const QByteArray &header, const QByteArray &body)
+{
+ BaseResult result;
+ if (header.isEmpty()) {
+ result.error = QString::fromUtf8(body); // we likely had a curl problem
+ return result;
+ }
+ int status = httpStatus(header);
+ if ((status > 399) || (status > 299 && body.isEmpty())) { // FIXME handle some explicitly?
+ const QString statusStr = QString::number(status);
+ if (body.isEmpty() || body.startsWith('<')) // likely an html response or redirect
+ result.error = QLatin1String("(%1)").arg(statusStr);
+ else
+ result.error = QLatin1String("%1 (%2)").arg(QString::fromUtf8(body)).arg(statusStr);
+ }
+ return result;
+}
+
+static std::pair<BaseResult, QJsonDocument> prehandleHeaderAndBody(const QByteArray &header,
+ const QByteArray &body)
+{
+ BaseResult result = prehandleHeader(header, body);
+ if (!result.error.isEmpty())
+ return {result, {}};
+
+ QJsonParseError error;
+ const QJsonDocument doc = QJsonDocument::fromJson(body, &error);
+ if (error.error != QJsonParseError::NoError) {
+ result.error = error.errorString();
+ return {result, doc};
+ }
+
+ if (!doc.isObject()) {
+ result.error = "Not an object.";
+ return {result, {}};
+ }
+
+ return {result, doc};
+}
+
+static User::UserType userTypeForString(const QString &type)
+{
+ if (type == "DASHBOARD_USER")
+ return User::Dashboard;
+ if (type == "VIRTUAL_USER")
+ return User::Virtual;
+ return User::Unknown;
+}
+
+static User userFromJson(const QJsonObject &object)
+{
+ User result;
+ if (object.isEmpty()) {
+ result.error = "Not a user object.";
+ return result;
+ }
+ result.name = object.value("name").toString();
+ result.displayName = object.value("displayName").toString();
+ result.type = userTypeForString(object.value("type").toString());
+ return result;
+}
+
+static QList<User> usersFromJson(const QJsonArray &array)
+{
+ QList<User> result;
+ for (const QJsonValue &value : array) {
+ User user = userFromJson(value.toObject());
+ if (!user.error.isEmpty()) // add this error to result.error?
+ continue;
+ result.append(user);
+ }
+ return result;
+}
+
+static IssueCount issueCountFromJson(const QJsonObject &object)
+{
+ IssueCount result;
+ if (object.isEmpty()) {
+ result.error = "Not an issue count object.";
+ return result;
+ }
+ result.added = object.value("Added").toInt();
+ result.removed = object.value("Removed").toInt();
+ result.total = object.value("Total").toInt();
+ return result;
+}
+
+static QList<IssueCount> issueCountsFromJson(const QJsonObject &object)
+{
+ QList<IssueCount> result;
+
+ const QStringList keys = object.keys();
+ for (const QString &k : keys) {
+ IssueCount issue = issueCountFromJson(object.value(k).toObject());
+ if (!issue.error.isEmpty()) // add this error to result.error?
+ continue;
+ issue.issueKind = k;
+ result.append(issue);
+ }
+ return result;
+}
+
+static ResultVersion versionFromJson(const QJsonObject &object)
+{
+ ResultVersion result;
+ if (object.isEmpty()) {
+ result.error = "Not a version object.";
+ return result;
+ }
+ const QJsonValue issuesValue = object.value("issueCounts");
+ if (!issuesValue.isObject()) {
+ result.error = "Not an object (issueCounts).";
+ return result;
+ }
+ result.issueCounts = issueCountsFromJson(issuesValue.toObject());
+ result.timeStamp = object.value("date").toString();
+ result.name = object.value("name").toString();
+ result.linesOfCode = object.value("linesOfCode").toInt();
+ return result;
+}
+
+static QList<ResultVersion> versionsFromJson(const QJsonArray &array)
+{
+ QList<ResultVersion> result;
+ for (const QJsonValue &value : array) {
+ ResultVersion version = versionFromJson(value.toObject());
+ if (!version.error.isEmpty()) // add this error to result.error?
+ continue;
+ result.append(version);
+ }
+ return result;
+}
+
+static IssueKind issueKindFromJson(const QJsonObject &object)
+{
+ IssueKind result;
+ if (object.isEmpty()) {
+ result.error = "Not an issue kind object.";
+ return result;
+ }
+ result.prefix = object.value("prefix").toString();
+ result.niceSingular = object.value("niceSingularName").toString();
+ result.nicePlural = object.value("nicePluralName").toString();
+ return result;
+}
+
+static QList<IssueKind> issueKindsFromJson(const QJsonArray &array)
+{
+ QList<IssueKind> result;
+ for (const QJsonValue &value : array) {
+ IssueKind kind = issueKindFromJson(value.toObject());
+ if (!kind.error.isEmpty()) // add this error to result.error?
+ continue;
+ result.append(kind);
+ }
+ return result;
+}
+
+namespace ResultParser {
+
+DashboardInfo parseDashboardInfo(const QByteArray &input)
+{
+ DashboardInfo result;
+
+ auto [header, body] = splitHeaderAndBody(input);
+ auto [error, doc] = prehandleHeaderAndBody(header, body);
+ if (!error.error.isEmpty()) {
+ result.error = error.error;
+ return result;
+ }
+ const QJsonObject object = doc.object();
+ result.mainUrl = object.value("mainUrl").toString();
+
+ if (!object.contains("projects")) {
+ result.error = "Missing projects information.";
+ return result;
+ }
+ const QJsonValue projects = object.value("projects");
+ if (!projects.isArray()) {
+ result.error = "Projects information not an array.";
+ return result;
+ }
+ const QJsonArray array = projects.toArray();
+ for (const QJsonValue &val : array) {
+ if (!val.isObject())
+ continue;
+ const QJsonObject projectObject = val.toObject();
+ Project project;
+ project.name = projectObject.value("name").toString();
+ project.url = projectObject.value("url").toString();
+ if (project.name.isEmpty() || project.url.isEmpty())
+ continue;
+ result.projects.append(project);
+ }
+ return result;
+}
+
+ProjectInfo parseProjectInfo(const QByteArray &input)
+{
+ ProjectInfo result;
+
+ auto [header, body] = splitHeaderAndBody(input);
+ auto [error, doc] = prehandleHeaderAndBody(header, body);
+ if (!error.error.isEmpty()) {
+ result.error = error.error;
+ return result;
+ }
+
+ const QJsonObject object = doc.object();
+ result.name = object.value("name").toString();
+
+ const QJsonValue usersValue = object.value("users");
+ if (!usersValue.isArray()) {
+ result.error = "Malformed json response (users).";
+ return result;
+ }
+ result.users = usersFromJson(usersValue.toArray());
+
+ const QJsonValue versionsValue = object.value("versions");
+ if (!versionsValue.isArray()) {
+ result.error = "Malformed json response (versions).";
+ return result;
+ }
+ result.versions = versionsFromJson(versionsValue.toArray());
+
+ const QJsonValue issueKindsValue = object.value("issueKinds");
+ if (!issueKindsValue.isArray()) {
+ result.error = "Malformed json response (issueKinds).";
+ return result;
+ }
+ result.issueKinds = issueKindsFromJson(issueKindsValue.toArray());
+ return result;
+}
+
+static QRegularExpression issueCsvLineRegex(const QByteArray &firstCsvLine)
+{
+ QString pattern = "^";
+ for (const QByteArray &part : firstCsvLine.split(',')) {
+ const QString cleaned = QString::fromUtf8(part).remove(' ').chopped(1).mid(1);
+ pattern.append(QString("\"(?<" + cleaned + ">.*)\","));
+ }
+ pattern.chop(1); // remove last comma
+ pattern.append('$');
+ const QRegularExpression regex(pattern);
+ QTC_ASSERT(regex.isValid(), return {});
+ return regex;
+}
+
+static void parseCsvIssue(const QByteArray &csv, QList<ShortIssue> *issues)
+{
+ QTC_ASSERT(issues, return);
+
+ bool first = true;
+ std::optional<QRegularExpression> regex;
+ for (auto &line : csv.split('\n')) {
+ if (first) {
+ regex.emplace(issueCsvLineRegex(line));
+ first = false;
+ if (regex.value().pattern().isEmpty())
+ return;
+ continue;
+ }
+ if (line.isEmpty())
+ continue;
+ const QRegularExpressionMatch match = regex->match(QString::fromUtf8(line));
+ QTC_ASSERT(match.hasMatch(), continue);
+ // FIXME: some of these are not present for all issue kinds! Limited to SV for now
+ ShortIssue issue;
+ issue.id = match.captured("Id");
+ issue.state = match.captured("State");
+ issue.errorNumber = match.captured("ErrorNumber");
+ issue.message = match.captured("Message");
+ issue.entity = match.captured("Entity");
+ issue.filePath = match.captured("Path");
+ issue.severity = match.captured("Severity");
+ issue.lineNumber = match.captured("Line").toInt();
+ issues->append(issue);
+ }
+}
+
+IssuesList parseIssuesList(const QByteArray &input)
+{
+ IssuesList result;
+
+ auto [header, body] = splitHeaderAndBody(input);
+ BaseResult headerResult = prehandleHeader(header, body);
+ if (!headerResult.error.isEmpty()) {
+ result.error = headerResult.error;
+ return result;
+ }
+ parseCsvIssue(body, &result.issues);
+ return result;
+}
+
+QString parseRuleInfo(const QByteArray &input) // html result!
+{
+ auto [header, body] = splitHeaderAndBody(input);
+ BaseResult headerResult = prehandleHeader(header, body);
+ if (!headerResult.error.isEmpty())
+ return QString();
+ return QString::fromLocal8Bit(body);
+}
+
+} // ResultParser
+
+} // Axivion::Internal
diff --git a/src/plugins/axivion/axivionresultparser.h b/src/plugins/axivion/axivionresultparser.h
new file mode 100644
index 0000000000..84c5a6b8ca
--- /dev/null
+++ b/src/plugins/axivion/axivionresultparser.h
@@ -0,0 +1,101 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QList>
+
+namespace Axivion::Internal {
+
+class BaseResult
+{
+public:
+ QString error;
+};
+
+class Project : public BaseResult
+{
+public:
+ QString name;
+ QString url;
+};
+
+class DashboardInfo : public BaseResult
+{
+public:
+ QString mainUrl;
+ QList<Project> projects;
+};
+
+class User : public BaseResult
+{
+public:
+ QString name;
+ QString displayName;
+ enum UserType { Dashboard, Virtual, Unknown } type;
+};
+
+class IssueKind : public BaseResult
+{
+public:
+ QString prefix;
+ QString niceSingular;
+ QString nicePlural;
+};
+
+class IssueCount : public BaseResult
+{
+public:
+ QString issueKind;
+ int total = 0;
+ int added = 0;
+ int removed = 0;
+};
+
+class ResultVersion : public BaseResult
+{
+public:
+ QString name;
+ QString timeStamp;
+ QList<IssueCount> issueCounts;
+ int linesOfCode = 0;
+};
+
+class ProjectInfo : public BaseResult
+{
+public:
+ QString name;
+ QList<User> users;
+ QList<ResultVersion> versions;
+ QList<IssueKind> issueKinds;
+};
+
+class ShortIssue : public BaseResult
+{
+public:
+ QString id;
+ QString state;
+ QString errorNumber;
+ QString message;
+ QString entity;
+ QString filePath;
+ QString severity;
+ int lineNumber = 0;
+};
+
+class IssuesList : public BaseResult
+{
+public:
+ QList<ShortIssue> issues;
+};
+
+namespace ResultParser {
+
+DashboardInfo parseDashboardInfo(const QByteArray &input);
+ProjectInfo parseProjectInfo(const QByteArray &input);
+IssuesList parseIssuesList(const QByteArray &input);
+QString parseRuleInfo(const QByteArray &input);
+
+} // ResultParser
+
+} // Axivion::Internal
diff --git a/src/plugins/axivion/axivionsettings.cpp b/src/plugins/axivion/axivionsettings.cpp
new file mode 100644
index 0000000000..4a18363163
--- /dev/null
+++ b/src/plugins/axivion/axivionsettings.cpp
@@ -0,0 +1,131 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "axivionsettings.h"
+
+#include "axiviontr.h"
+
+#include <utils/filepath.h>
+#include <utils/hostosinfo.h>
+
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QSettings>
+#include <QStandardPaths>
+
+namespace Axivion::Internal {
+
+AxivionServer::AxivionServer(const Utils::Id &id, const QString &dashboard,
+ const QString &description, const QString &token)
+ : id(id)
+ , dashboard(dashboard)
+ , description(description)
+ , token(token)
+{
+}
+
+bool AxivionServer::operator==(const AxivionServer &other) const
+{
+ return id == other.id && dashboard == other.dashboard
+ && description == other.description && token == other.token;
+}
+
+bool AxivionServer::operator!=(const AxivionServer &other) const
+{
+ return !(*this == other);
+}
+
+QJsonObject AxivionServer::toJson() const
+{
+ QJsonObject result;
+ result.insert("id", id.toString());
+ result.insert("dashboard", dashboard);
+ result.insert("description", description);
+ result.insert("token", token);
+ return result;
+}
+
+AxivionServer AxivionServer::fromJson(const QJsonObject &json)
+{
+ const AxivionServer invalidServer;
+ const QJsonValue id = json.value("id");
+ if (id == QJsonValue::Undefined)
+ return invalidServer;
+ const QJsonValue dashboard = json.value("dashboard");
+ if (dashboard == QJsonValue::Undefined)
+ return invalidServer;
+ const QJsonValue description = json.value("description");
+ if (description == QJsonValue::Undefined)
+ return invalidServer;
+ const QJsonValue token = json.value("token");
+ if (token == QJsonValue::Undefined)
+ return invalidServer;
+ return { Utils::Id::fromString(id.toString()), dashboard.toString(),
+ description.toString(), token.toString() };
+}
+
+QStringList AxivionServer::curlArguments() const
+{
+ QStringList args { "-sS" }; // silent, but show error
+ if (dashboard.startsWith("https://") && !validateCert)
+ args << "-k";
+ return args;
+}
+
+AxivionSettings::AxivionSettings()
+{
+ setSettingsGroup("Axivion");
+
+ curl.setSettingsKey("Curl");
+ curl.setLabelText(Tr::tr("curl:"));
+ curl.setExpectedKind(Utils::PathChooser::ExistingCommand);
+}
+
+static Utils::FilePath tokensFilePath(const QSettings *s)
+{
+ return Utils::FilePath::fromString(s->fileName()).parentDir()
+ .pathAppended("qtcreator/axivion.json");
+}
+
+static void writeTokenFile(const Utils::FilePath &filePath, const AxivionServer &server)
+{
+ QJsonDocument doc;
+ doc.setObject(server.toJson());
+ // FIXME error handling?
+ filePath.writeFileContents(doc.toJson());
+ filePath.setPermissions(QFile::ReadUser | QFile::WriteUser);
+}
+
+static AxivionServer readTokenFile(const Utils::FilePath &filePath)
+{
+ if (!filePath.exists())
+ return {};
+ Utils::expected_str<QByteArray> contents = filePath.fileContents();
+ if (!contents)
+ return {};
+ const QJsonDocument doc = QJsonDocument::fromJson(*contents);
+ if (!doc.isObject())
+ return {};
+ return AxivionServer::fromJson(doc.object());
+}
+
+void AxivionSettings::toSettings(QSettings *s) const
+{
+ writeTokenFile(tokensFilePath(s), server);
+ Utils::AspectContainer::writeSettings(s);
+}
+
+void AxivionSettings::fromSettings(QSettings *s)
+{
+ Utils::AspectContainer::readSettings(s);
+ server = readTokenFile(tokensFilePath(s));
+
+ if (curl().isEmpty() || !curl().exists()) {
+ const QString curlPath = QStandardPaths::findExecutable(
+ Utils::HostOsInfo::withExecutableSuffix("curl"));
+ if (!curlPath.isEmpty())
+ curl.setFilePath(Utils::FilePath::fromString(curlPath));
+ }
+}
+
+} // Axivion::Internal
diff --git a/src/plugins/axivion/axivionsettings.h b/src/plugins/axivion/axivionsettings.h
new file mode 100644
index 0000000000..0df1746387
--- /dev/null
+++ b/src/plugins/axivion/axivionsettings.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <utils/aspects.h>
+#include <utils/id.h>
+
+#include <QtGlobal>
+
+QT_BEGIN_NAMESPACE
+class QJsonObject;
+class QSettings;
+QT_END_NAMESPACE
+
+namespace Axivion::Internal {
+
+class AxivionServer
+{
+public:
+ AxivionServer() = default;
+ AxivionServer(const Utils::Id &id, const QString &dashboardUrl,
+ const QString &description, const QString &token);
+
+ bool operator==(const AxivionServer &other) const;
+ bool operator!=(const AxivionServer &other) const;
+
+ QJsonObject toJson() const;
+ static AxivionServer fromJson(const QJsonObject &json);
+ QStringList curlArguments() const;
+
+ Utils::Id id;
+ QString dashboard;
+ QString description;
+ QString token;
+
+ bool validateCert = true;
+};
+
+class AxivionSettings : public Utils::AspectContainer
+{
+public:
+ AxivionSettings();
+ void toSettings(QSettings *s) const;
+ void fromSettings(QSettings *s);
+
+ AxivionServer server; // shall we have more than one?
+ Utils::FilePathAspect curl{this};
+};
+
+} // Axivion::Internal
diff --git a/src/plugins/axivion/axivionsettingspage.cpp b/src/plugins/axivion/axivionsettingspage.cpp
new file mode 100644
index 0000000000..e837d71598
--- /dev/null
+++ b/src/plugins/axivion/axivionsettingspage.cpp
@@ -0,0 +1,215 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "axivionsettingspage.h"
+
+#include "axivionplugin.h"
+#include "axivionsettings.h"
+#include "axiviontr.h"
+
+#include <coreplugin/icore.h>
+#include <utils/aspects.h>
+#include <utils/id.h>
+#include <utils/layoutbuilder.h>
+#include <utils/pathchooser.h>
+
+#include <QDialog>
+#include <QDialogButtonBox>
+#include <QLabel>
+#include <QPushButton>
+#include <QRegularExpression>
+#include <QUuid>
+#include <QVBoxLayout>
+
+using namespace Utils;
+
+namespace Axivion::Internal {
+
+// may allow some invalid, but does some minimal check for legality
+static bool hostValid(const QString &host)
+{
+ static const QRegularExpression ip(R"(^(\d+).(\d+).(\d+).(\d+)$)");
+ static const QRegularExpression dn(R"(^([a-zA-Z0-9][a-zA-Z0-9-]+\.)+[a-zA-Z0-9][a-zA-Z0-9-]+$)");
+ const QRegularExpressionMatch match = ip.match(host);
+ if (match.hasMatch()) {
+ for (int i = 1; i < 5; ++i) {
+ int val = match.captured(i).toInt();
+ if (val < 0 || val > 255)
+ return false;
+ }
+ return true;
+ }
+ return (host == "localhost") || dn.match(host).hasMatch();
+}
+
+static bool isUrlValid(const QString &in)
+{
+ const QUrl url(in);
+ return hostValid(url.host()) && (url.scheme() == "https" || url.scheme() == "http");
+}
+
+class DashboardSettingsWidget : public QWidget
+{
+public:
+ enum Mode { Display, Edit };
+ explicit DashboardSettingsWidget(Mode m, QWidget *parent, QPushButton *ok = nullptr);
+
+ AxivionServer dashboardServer() const;
+ void setDashboardServer(const AxivionServer &server);
+
+ bool isValid() const;
+
+private:
+ Mode m_mode = Display;
+ Id m_id;
+ StringAspect m_dashboardUrl;
+ StringAspect m_description;
+ StringAspect m_token;
+ BoolAspect m_valid;
+};
+
+DashboardSettingsWidget::DashboardSettingsWidget(Mode mode, QWidget *parent, QPushButton *ok)
+ : QWidget(parent)
+ , m_mode(mode)
+{
+ auto labelStyle = mode == Display ? StringAspect::LabelDisplay : StringAspect::LineEditDisplay;
+ m_dashboardUrl.setLabelText(Tr::tr("Dashboard URL:"));
+ m_dashboardUrl.setDisplayStyle(labelStyle);
+ m_dashboardUrl.setValidationFunction([](FancyLineEdit *edit, QString *){
+ return isUrlValid(edit->text());
+ });
+ m_description.setLabelText(Tr::tr("Description:"));
+ m_description.setDisplayStyle(labelStyle);
+ m_description.setPlaceHolderText(Tr::tr("Non-empty description"));
+
+ m_token.setLabelText(Tr::tr("Access token:"));
+ m_token.setDisplayStyle(labelStyle);
+ m_token.setPlaceHolderText(Tr::tr("IDE Access Token"));
+ m_token.setVisible(mode == Edit);
+
+ using namespace Layouting;
+
+ Form {
+ m_dashboardUrl, br,
+ m_description, br,
+ m_token, br,
+ mode == Edit ? normalMargin : noMargin
+ }.attachTo(this);
+
+ if (mode == Edit) {
+ QTC_ASSERT(ok, return);
+ auto checkValidity = [this, ok] {
+ m_valid.setValue(isValid());
+ ok->setEnabled(m_valid());
+ };
+ connect(&m_dashboardUrl, &BaseAspect::changed, this, checkValidity);
+ connect(&m_description, &BaseAspect::changed, this, checkValidity);
+ connect(&m_token, &BaseAspect::changed, this, checkValidity);
+ }
+}
+
+AxivionServer DashboardSettingsWidget::dashboardServer() const
+{
+ AxivionServer result;
+ if (m_id.isValid())
+ result.id = m_id;
+ else
+ result.id = m_mode == Edit ? Utils::Id::fromName(QUuid::createUuid().toByteArray()) : m_id;
+ result.dashboard = m_dashboardUrl();
+ result.description = m_description();
+ result.token = m_token();
+ return result;
+}
+
+void DashboardSettingsWidget::setDashboardServer(const AxivionServer &server)
+{
+ m_id = server.id;
+ m_dashboardUrl.setValue(server.dashboard);
+ m_description.setValue(server.description);
+ m_token.setValue(server.token);
+}
+
+bool DashboardSettingsWidget::isValid() const
+{
+ return !m_token().isEmpty() && !m_description().isEmpty() && isUrlValid(m_dashboardUrl());
+}
+
+class AxivionSettingsWidget : public Core::IOptionsPageWidget
+{
+public:
+ explicit AxivionSettingsWidget(AxivionSettings *settings);
+
+ void apply() override;
+private:
+ void showEditServerDialog();
+
+ AxivionSettings *m_settings;
+
+ DashboardSettingsWidget *m_dashboardDisplay = nullptr;
+ QPushButton *m_edit = nullptr;
+};
+
+AxivionSettingsWidget::AxivionSettingsWidget(AxivionSettings *settings)
+ : m_settings(settings)
+{
+ using namespace Layouting;
+
+ m_dashboardDisplay = new DashboardSettingsWidget(DashboardSettingsWidget::Display, this);
+ m_dashboardDisplay->setDashboardServer(m_settings->server);
+ m_edit = new QPushButton(Tr::tr("Edit..."), this);
+ Row {
+ Form {
+ m_dashboardDisplay, br,
+ m_settings->curl, br,
+ }, Column { m_edit, st }
+ }.attachTo(this);
+
+ connect(m_edit, &QPushButton::clicked, this, &AxivionSettingsWidget::showEditServerDialog);
+}
+
+void AxivionSettingsWidget::apply()
+{
+ m_settings->server = m_dashboardDisplay->dashboardServer();
+ m_settings->toSettings(Core::ICore::settings());
+ emit AxivionPlugin::instance()->settingsChanged();
+}
+
+void AxivionSettingsWidget::showEditServerDialog()
+{
+ const AxivionServer old = m_dashboardDisplay->dashboardServer();
+ QDialog d;
+ d.setWindowTitle(Tr::tr("Edit Dashboard Configuration"));
+ QVBoxLayout *layout = new QVBoxLayout;
+ auto buttons = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok, this);
+ auto ok = buttons->button(QDialogButtonBox::Ok);
+ auto dashboardWidget = new DashboardSettingsWidget(DashboardSettingsWidget::Edit, this, ok);
+ dashboardWidget->setDashboardServer(old);
+ layout->addWidget(dashboardWidget);
+ ok->setEnabled(m_dashboardDisplay->isValid());
+ connect(buttons->button(QDialogButtonBox::Cancel), &QPushButton::clicked, &d, &QDialog::reject);
+ connect(ok, &QPushButton::clicked, &d, &QDialog::accept);
+ layout->addWidget(buttons);
+ d.setLayout(layout);
+ d.resize(500, 200);
+
+ if (d.exec() != QDialog::Accepted)
+ return;
+ if (dashboardWidget->isValid()) {
+ const AxivionServer server = dashboardWidget->dashboardServer();
+ if (server != old)
+ m_dashboardDisplay->setDashboardServer(server);
+ }
+}
+
+AxivionSettingsPage::AxivionSettingsPage(AxivionSettings *settings)
+ : m_settings(settings)
+{
+ setId("Axivion.Settings.General");
+ setDisplayName(Tr::tr("General"));
+ setCategory("XY.Axivion");
+ setDisplayCategory(Tr::tr("Axivion"));
+ setCategoryIconPath(":/axivion/images/axivion.png");
+ setWidgetCreator([this] { return new AxivionSettingsWidget(m_settings); });
+}
+
+} // Axivion::Internal
diff --git a/src/plugins/axivion/axivionsettingspage.h b/src/plugins/axivion/axivionsettingspage.h
new file mode 100644
index 0000000000..744818300d
--- /dev/null
+++ b/src/plugins/axivion/axivionsettingspage.h
@@ -0,0 +1,21 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <coreplugin/dialogs/ioptionspage.h>
+
+namespace Axivion::Internal {
+
+class AxivionSettings;
+
+class AxivionSettingsPage : public Core::IOptionsPage
+{
+public:
+ explicit AxivionSettingsPage(AxivionSettings *settings);
+
+private:
+ AxivionSettings *m_settings;
+};
+
+} // Axivion::Internal
diff --git a/src/plugins/axivion/axiviontr.h b/src/plugins/axivion/axiviontr.h
new file mode 100644
index 0000000000..1f3475cb45
--- /dev/null
+++ b/src/plugins/axivion/axiviontr.h
@@ -0,0 +1,15 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QCoreApplication>
+
+namespace Axivion {
+
+struct Tr
+{
+ Q_DECLARE_TR_FUNCTIONS(Axivion)
+};
+
+} // Axivion
diff --git a/src/plugins/axivion/images/axivion.png b/src/plugins/axivion/images/axivion.png
new file mode 100644
index 0000000000..647f5972ce
--- /dev/null
+++ b/src/plugins/axivion/images/axivion.png
Binary files differ
diff --git a/src/plugins/axivion/images/axivion@2x.png b/src/plugins/axivion/images/axivion@2x.png
new file mode 100644
index 0000000000..15bac95f52
--- /dev/null
+++ b/src/plugins/axivion/images/axivion@2x.png
Binary files differ
diff --git a/src/plugins/baremetal/baremetaldebugsupport.cpp b/src/plugins/baremetal/baremetaldebugsupport.cpp
index db0c376a5a..4475be0b96 100644
--- a/src/plugins/baremetal/baremetaldebugsupport.cpp
+++ b/src/plugins/baremetal/baremetaldebugsupport.cpp
@@ -11,6 +11,7 @@
#include "idebugserverprovider.h"
#include <debugger/debuggerkitinformation.h>
+#include <debugger/debuggerruncontrol.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildsteplist.h>
@@ -23,8 +24,8 @@
#include <projectexplorer/toolchain.h>
#include <utils/portlist.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
using namespace Debugger;
using namespace ProjectExplorer;
diff --git a/src/plugins/baremetal/baremetaldebugsupport.h b/src/plugins/baremetal/baremetaldebugsupport.h
index f0addb4729..ca16c1ab7e 100644
--- a/src/plugins/baremetal/baremetaldebugsupport.h
+++ b/src/plugins/baremetal/baremetaldebugsupport.h
@@ -3,12 +3,11 @@
#pragma once
-#include <debugger/debuggerruncontrol.h>
+#include <projectexplorer/runcontrol.h>
namespace BareMetal::Internal {
-class BareMetalDebugSupportFactory final
- : public ProjectExplorer::RunWorkerFactory
+class BareMetalDebugSupportFactory final : public ProjectExplorer::RunWorkerFactory
{
public:
BareMetalDebugSupportFactory();
diff --git a/src/plugins/baremetal/baremetaldevice.cpp b/src/plugins/baremetal/baremetaldevice.cpp
index 4c8d7ea8c6..d7754ebc9a 100644
--- a/src/plugins/baremetal/baremetaldevice.cpp
+++ b/src/plugins/baremetal/baremetaldevice.cpp
@@ -24,7 +24,6 @@ const char debugServerProviderIdKeyC[] = "IDebugServerProviderId";
BareMetalDevice::BareMetalDevice()
{
setDisplayType(Tr::tr("Bare Metal"));
- setDefaultDisplayName(defaultDisplayName());
setOsType(Utils::OsTypeOther);
}
diff --git a/src/plugins/baremetal/debugserverproviderssettingspage.cpp b/src/plugins/baremetal/debugserverproviderssettingspage.cpp
index 097d08033a..58af3578a9 100644
--- a/src/plugins/baremetal/debugserverproviderssettingspage.cpp
+++ b/src/plugins/baremetal/debugserverproviderssettingspage.cpp
@@ -257,8 +257,6 @@ class DebugServerProvidersSettingsWidget final : public Core::IOptionsPageWidget
public:
DebugServerProvidersSettingsWidget();
- void apply() final { m_model.apply(); }
-
void providerSelectionChanged();
void removeProvider();
void updateState();
@@ -363,6 +361,8 @@ DebugServerProvidersSettingsWidget::DebugServerProvidersSettingsWidget()
this, &DebugServerProvidersSettingsWidget::removeProvider);
updateState();
+
+ setOnApply([this] { m_model.apply(); });
}
void DebugServerProvidersSettingsWidget::providerSelectionChanged()
diff --git a/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp
index 336b606d79..222d22ade9 100644
--- a/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp
+++ b/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp
@@ -8,6 +8,8 @@
#include <baremetal/baremetaltr.h>
#include <baremetal/debugserverprovidermanager.h>
+#include <debugger/debuggerruncontrol.h>
+
#include <projectexplorer/runconfigurationaspects.h>
#include <utils/environment.h>
diff --git a/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.h b/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.h
index eac2ca6e68..1c23d49995 100644
--- a/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.h
+++ b/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.h
@@ -40,7 +40,7 @@ public:
bool aboutToRun(Debugger::DebuggerRunTool *runTool,
QString &errorMessage) const final;
ProjectExplorer::RunWorker *targetRunner(
- ProjectExplorer::RunControl *runControl) const final;
+ ProjectExplorer::RunControl *runControl) const override;
bool isValid() const override;
virtual QSet<StartupMode> supportedStartupModes() const = 0;
diff --git a/src/plugins/baremetal/debugservers/gdb/genericgdbserverprovider.h b/src/plugins/baremetal/debugservers/gdb/genericgdbserverprovider.h
index 2754a3d91a..98ca17451c 100644
--- a/src/plugins/baremetal/debugservers/gdb/genericgdbserverprovider.h
+++ b/src/plugins/baremetal/debugservers/gdb/genericgdbserverprovider.h
@@ -19,6 +19,11 @@ class GenericGdbServerProvider final : public GdbServerProvider
private:
GenericGdbServerProvider();
QSet<StartupMode> supportedStartupModes() const final;
+ ProjectExplorer::RunWorker *targetRunner(ProjectExplorer::RunControl *runControl) const final {
+ Q_UNUSED(runControl)
+ // Generic Runner assumes GDB Server already running
+ return nullptr;
+ }
friend class GenericGdbServerProviderConfigWidget;
friend class GenericGdbServerProviderFactory;
diff --git a/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp
index d631923788..c1ee31b710 100644
--- a/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp
+++ b/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp
@@ -9,8 +9,8 @@
#include <utils/fileutils.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/variablechooser.h>
#include <QComboBox>
diff --git a/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp
index 334f31132c..ca7f25e56f 100644
--- a/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp
+++ b/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp
@@ -9,8 +9,8 @@
#include <utils/fileutils.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/variablechooser.h>
#include <QComboBox>
@@ -64,7 +64,7 @@ QString OpenOcdGdbServerProvider::channelString() const
// otherwise running will be stuck.
CommandLine cmd = command();
QStringList args = {"|", cmd.executable().toString()};
- for (const QString &a : ProcessArgs::splitArgs(cmd.arguments())) {
+ for (const QString &a : ProcessArgs::splitArgs(cmd.arguments(), HostOsInfo::hostOs())) {
if (a.startsWith('\"') && a.endsWith('\"'))
args << a;
else
diff --git a/src/plugins/baremetal/debugservers/uvsc/uvproject.cpp b/src/plugins/baremetal/debugservers/uvsc/uvproject.cpp
index 773ec41066..faa32fea08 100644
--- a/src/plugins/baremetal/debugservers/uvsc/uvproject.cpp
+++ b/src/plugins/baremetal/debugservers/uvsc/uvproject.cpp
@@ -9,7 +9,7 @@
#include <debugger/debuggerkitinformation.h>
#include <debugger/debuggerruncontrol.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <QFileInfo>
diff --git a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp
index b95c333600..a266ed7bfd 100644
--- a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp
+++ b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp
@@ -14,6 +14,7 @@
#include <baremetal/debugserverprovidermanager.h>
#include <debugger/debuggerkitinformation.h>
+#include <debugger/debuggerruncontrol.h>
#include <projectexplorer/project.h>
#include <projectexplorer/runconfigurationaspects.h>
@@ -357,12 +358,12 @@ UvscServerProviderRunner::UvscServerProviderRunner(ProjectExplorer::RunControl *
m_process.setCommand(runnable.command);
- connect(&m_process, &QtcProcess::started, this, [this] {
+ connect(&m_process, &Process::started, this, [this] {
ProcessHandle pid(m_process.processId());
this->runControl()->setApplicationProcessHandle(pid);
reportStarted();
});
- connect(&m_process, &QtcProcess::done, this, [this] {
+ connect(&m_process, &Process::done, this, [this] {
appendMessage(m_process.exitMessage(), NormalMessageFormat);
reportStopped();
});
diff --git a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h
index d7b3bcafba..4c14ee7b7c 100644
--- a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h
+++ b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h
@@ -10,7 +10,7 @@
#include <projectexplorer/runcontrol.h> // for RunWorker
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
namespace Utils { class PathChooser; }
@@ -127,7 +127,7 @@ private:
void start() final;
void stop() final;
- Utils::QtcProcess m_process;
+ Utils::Process m_process;
};
} // namespace Internal
diff --git a/src/plugins/baremetal/iarewtoolchain.cpp b/src/plugins/baremetal/iarewtoolchain.cpp
index 80b5491ddf..c6653fefbb 100644
--- a/src/plugins/baremetal/iarewtoolchain.cpp
+++ b/src/plugins/baremetal/iarewtoolchain.cpp
@@ -15,8 +15,8 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDebug>
#include <QDir>
@@ -78,7 +78,7 @@ static Macros dumpPredefinedMacros(const FilePath &compiler, const QStringList &
const QString outpath = fakeIn.fileName() + ".tmp";
- QtcProcess cpp;
+ Process cpp;
cpp.setEnvironment(env);
cpp.setTimeoutS(10);
@@ -130,7 +130,7 @@ static HeaderPaths dumpHeaderPaths(const FilePath &compiler, const Id languageId
cmd.addArg("--preinclude");
cmd.addArg(".");
- QtcProcess cpp;
+ Process cpp;
cpp.setEnvironment(env);
cpp.setTimeoutS(10);
cpp.setCommand(cmd);
diff --git a/src/plugins/baremetal/keiltoolchain.cpp b/src/plugins/baremetal/keiltoolchain.cpp
index 4c023ce306..3bb2423ab2 100644
--- a/src/plugins/baremetal/keiltoolchain.cpp
+++ b/src/plugins/baremetal/keiltoolchain.cpp
@@ -15,8 +15,8 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDebug>
#include <QDir>
@@ -113,7 +113,7 @@ static Macros dumpMcsPredefinedMacros(const FilePath &compiler, const Environmen
fakeIn.close();
- QtcProcess cpp;
+ Process cpp;
cpp.setEnvironment(env);
cpp.setTimeoutS(10);
cpp.setCommand({compiler, {fakeIn.fileName()}});
@@ -190,7 +190,7 @@ static Macros dumpC166PredefinedMacros(const FilePath &compiler, const Environme
fakeIn.close();
- QtcProcess cpp;
+ Process cpp;
cpp.setEnvironment(env);
cpp.setTimeoutS(10);
@@ -255,7 +255,7 @@ static Macros dumpC166PredefinedMacros(const FilePath &compiler, const Environme
static Macros dumpArmPredefinedMacros(const FilePath &compiler, const QStringList &extraArgs, const Environment &env)
{
- QtcProcess cpp;
+ Process cpp;
cpp.setEnvironment(env);
cpp.setTimeoutS(10);
diff --git a/src/plugins/baremetal/sdcctoolchain.cpp b/src/plugins/baremetal/sdcctoolchain.cpp
index a16372eff9..a35a468d20 100644
--- a/src/plugins/baremetal/sdcctoolchain.cpp
+++ b/src/plugins/baremetal/sdcctoolchain.cpp
@@ -15,8 +15,8 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDebug>
#include <QDir>
@@ -65,7 +65,7 @@ static Macros dumpPredefinedMacros(const FilePath &compiler, const Environment &
return {};
fakeIn.close();
- QtcProcess cpp;
+ Process cpp;
cpp.setEnvironment(env);
cpp.setTimeoutS(10);
cpp.setCommand({compiler, {compilerTargetFlag(abi), "-dM", "-E", fakeIn.fileName()}});
@@ -86,7 +86,7 @@ static HeaderPaths dumpHeaderPaths(const FilePath &compiler, const Environment &
if (!compiler.exists())
return {};
- QtcProcess cpp;
+ Process cpp;
cpp.setEnvironment(env);
cpp.setTimeoutS(10);
cpp.setCommand({compiler, {compilerTargetFlag(abi), "--print-search-dirs"}});
diff --git a/src/plugins/bazaar/bazaarclient.cpp b/src/plugins/bazaar/bazaarclient.cpp
index 0b07bcd9ec..303b7e21c2 100644
--- a/src/plugins/bazaar/bazaarclient.cpp
+++ b/src/plugins/bazaar/bazaarclient.cpp
@@ -29,13 +29,13 @@ namespace Bazaar::Internal {
class BazaarDiffConfig : public VcsBaseEditorConfig
{
public:
- BazaarDiffConfig(BazaarSettings &settings, QToolBar *toolBar) :
- VcsBaseEditorConfig(toolBar)
+ explicit BazaarDiffConfig(QToolBar *toolBar)
+ : VcsBaseEditorConfig(toolBar)
{
mapSetting(addToggleButton("-w", Tr::tr("Ignore Whitespace")),
- &settings.diffIgnoreWhiteSpace);
+ &settings().diffIgnoreWhiteSpace);
mapSetting(addToggleButton("-B", Tr::tr("Ignore Blank Lines")),
- &settings.diffIgnoreBlankLines);
+ &settings().diffIgnoreBlankLines);
}
QStringList arguments() const override
@@ -54,18 +54,18 @@ public:
class BazaarLogConfig : public VcsBaseEditorConfig
{
public:
- BazaarLogConfig(BazaarSettings &settings, QToolBar *toolBar) :
- VcsBaseEditorConfig(toolBar)
+ BazaarLogConfig(QToolBar *toolBar)
+ : VcsBaseEditorConfig(toolBar)
{
mapSetting(addToggleButton("--verbose", Tr::tr("Verbose"),
Tr::tr("Show files changed in each revision.")),
- &settings.logVerbose);
+ &settings().logVerbose);
mapSetting(addToggleButton("--forward", Tr::tr("Forward"),
Tr::tr("Show from oldest to newest.")),
- &settings.logForward);
+ &settings().logForward);
mapSetting(addToggleButton("--include-merges", Tr::tr("Include Merges"),
Tr::tr("Show merged revisions.")),
- &settings.logIncludeMerges);
+ &settings().logIncludeMerges);
const QList<ChoiceItem> logChoices = {
{Tr::tr("Detailed"), "long"},
@@ -74,18 +74,14 @@ public:
{Tr::tr("GNU Change Log"), "gnu-changelog"}
};
mapSetting(addChoices(Tr::tr("Format"), { "--log-format=%1" }, logChoices),
- &settings.logFormat);
+ &settings().logFormat);
}
};
-BazaarClient::BazaarClient(BazaarSettings *settings) : VcsBaseClient(settings)
+BazaarClient::BazaarClient() : VcsBaseClient(&Internal::settings())
{
- setDiffConfigCreator([settings](QToolBar *toolBar) {
- return new BazaarDiffConfig(*settings, toolBar);
- });
- setLogConfigCreator([settings](QToolBar *toolBar) {
- return new BazaarLogConfig(*settings, toolBar);
- });
+ setDiffConfigCreator([](QToolBar *toolBar) { return new BazaarDiffConfig(toolBar); });
+ setLogConfigCreator([](QToolBar *toolBar) { return new BazaarLogConfig(toolBar); });
}
BranchInfo BazaarClient::synchronousBranchQuery(const FilePath &repositoryRoot) const
diff --git a/src/plugins/bazaar/bazaarclient.h b/src/plugins/bazaar/bazaarclient.h
index b84484b687..5b6ce1e6d4 100644
--- a/src/plugins/bazaar/bazaarclient.h
+++ b/src/plugins/bazaar/bazaarclient.h
@@ -9,12 +9,10 @@
namespace Bazaar::Internal {
-class BazaarSettings;
-
class BazaarClient : public VcsBase::VcsBaseClient
{
public:
- explicit BazaarClient(BazaarSettings *settings);
+ BazaarClient();
BranchInfo synchronousBranchQuery(const Utils::FilePath &repositoryRoot) const;
bool synchronousUncommit(const Utils::FilePath &workingDir,
diff --git a/src/plugins/bazaar/bazaarcommitwidget.cpp b/src/plugins/bazaar/bazaarcommitwidget.cpp
index 79d0b56b5a..379fca6661 100644
--- a/src/plugins/bazaar/bazaarcommitwidget.cpp
+++ b/src/plugins/bazaar/bazaarcommitwidget.cpp
@@ -49,7 +49,7 @@ public:
emailLineEdit = new QLineEdit;
fixedBugsLineEdit = new QLineEdit;
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
title(Tr::tr("General Information")),
@@ -65,8 +65,9 @@ public:
Tr::tr("Email:"), emailLineEdit, br,
Tr::tr("Fixed bugs:"), fixedBugsLineEdit
}
- }
- }.attachTo(this, WithoutMargins);
+ },
+ noMargin
+ }.attachTo(this);
}
QLineEdit *branchLineEdit;
diff --git a/src/plugins/bazaar/bazaarplugin.cpp b/src/plugins/bazaar/bazaarplugin.cpp
index a0f980606d..542c2ef706 100644
--- a/src/plugins/bazaar/bazaarplugin.cpp
+++ b/src/plugins/bazaar/bazaarplugin.cpp
@@ -215,9 +215,8 @@ public:
void createRepositoryActions(const Core::Context &context);
// Variables
- BazaarSettings m_settings;
- BazaarClient m_client{&m_settings};
- BazaarSettingsPage m_settingPage{&m_settings};
+ BazaarSettings m_setting;
+ BazaarClient m_client;
VcsSubmitEditorFactory m_submitEditorFactory {
submitEditorParameters,
@@ -286,7 +285,7 @@ public:
dryRunBtn->setToolTip(Tr::tr("Test the outcome of removing the last committed revision, without actually removing anything."));
buttonBox->addButton(dryRunBtn, QDialogButtonBox::ApplyRole);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Form {
keepTagsCheckBox, br,
@@ -372,7 +371,7 @@ BazaarPluginPrivate::BazaarPluginPrivate()
toolsMenu->addMenu(m_bazaarContainer);
m_menuAction = m_bazaarContainer->menu()->menuAction();
- connect(&m_settings, &AspectContainer::applied, this, &IVersionControl::configurationChanged);
+ connect(&settings(), &AspectContainer::applied, this, &IVersionControl::configurationChanged);
}
void BazaarPluginPrivate::createFileActions(const Context &context)
@@ -387,7 +386,8 @@ void BazaarPluginPrivate::createFileActions(const Context &context)
m_diffFile = new ParameterAction(Tr::tr("Diff Current File"), Tr::tr("Diff \"%1\""), ParameterAction::EnabledWithParameter, this);
command = ActionManager::registerAction(m_diffFile, DIFF, context);
command->setAttribute(Command::CA_UpdateText);
- command->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+Z,Meta+D") : Tr::tr("ALT+Z,Alt+D")));
+ command->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+Z,Meta+D")
+ : Tr::tr("Alt+Z,Alt+D")));
connect(m_diffFile, &QAction::triggered, this, &BazaarPluginPrivate::diffCurrentFile);
m_bazaarContainer->addAction(command);
m_commandLocator->appendCommand(command);
@@ -395,7 +395,8 @@ void BazaarPluginPrivate::createFileActions(const Context &context)
m_logFile = new ParameterAction(Tr::tr("Log Current File"), Tr::tr("Log \"%1\""), ParameterAction::EnabledWithParameter, this);
command = ActionManager::registerAction(m_logFile, LOG, context);
command->setAttribute(Command::CA_UpdateText);
- command->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+Z,Meta+L") : Tr::tr("ALT+Z,Alt+L")));
+ command->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+Z,Meta+L")
+ : Tr::tr("Alt+Z,Alt+L")));
connect(m_logFile, &QAction::triggered, this, &BazaarPluginPrivate::logCurrentFile);
m_bazaarContainer->addAction(command);
m_commandLocator->appendCommand(command);
@@ -403,7 +404,8 @@ void BazaarPluginPrivate::createFileActions(const Context &context)
m_statusFile = new ParameterAction(Tr::tr("Status Current File"), Tr::tr("Status \"%1\""), ParameterAction::EnabledWithParameter, this);
command = ActionManager::registerAction(m_statusFile, STATUS, context);
command->setAttribute(Command::CA_UpdateText);
- command->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+Z,Meta+S") : Tr::tr("ALT+Z,Alt+S")));
+ command->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+Z,Meta+S")
+ : Tr::tr("Alt+Z,Alt+S")));
connect(m_statusFile, &QAction::triggered, this, &BazaarPluginPrivate::statusCurrentFile);
m_bazaarContainer->addAction(command);
m_commandLocator->appendCommand(command);
@@ -523,7 +525,7 @@ void BazaarPluginPrivate::logRepository()
const VcsBasePluginState state = currentState();
QTC_ASSERT(state.hasTopLevel(), return);
QStringList extraOptions;
- extraOptions += "--limit=" + QString::number(m_settings.logCount.value());
+ extraOptions += "--limit=" + QString::number(settings().logCount());
m_client.log(state.topLevel(), QStringList(), extraOptions);
}
@@ -571,7 +573,8 @@ void BazaarPluginPrivate::createRepositoryActions(const Context &context)
action = new QAction(Tr::tr("Commit..."), this);
m_repositoryActionList.append(action);
command = ActionManager::registerAction(action, COMMIT, context);
- command->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+Z,Meta+C") : Tr::tr("ALT+Z,Alt+C")));
+ command->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+Z,Meta+C")
+ : Tr::tr("Alt+Z,Alt+C")));
connect(action, &QAction::triggered, this, &BazaarPluginPrivate::commit);
m_bazaarContainer->addAction(command);
m_commandLocator->appendCommand(command);
@@ -705,8 +708,8 @@ void BazaarPluginPrivate::showCommitWidget(const QList<VcsBaseClient::StatusItem
const BranchInfo branch = m_client.synchronousBranchQuery(m_submitRepository);
commitEditor->setFields(m_submitRepository, branch,
- m_settings.userName.value(),
- m_settings.userEmail.value(), status);
+ settings().userName(),
+ settings().userEmail(), status);
}
void BazaarPluginPrivate::diffFromEditorSelected(const QStringList &files)
@@ -872,7 +875,7 @@ bool BazaarPluginPrivate::managesFile(const FilePath &workingDirectory, const QS
bool BazaarPluginPrivate::isConfigured() const
{
- const FilePath binary = m_settings.binaryPath.filePath();
+ const FilePath binary = settings().binaryPath();
return !binary.isEmpty() && binary.isExecutableFile();
}
diff --git a/src/plugins/bazaar/bazaarsettings.cpp b/src/plugins/bazaar/bazaarsettings.cpp
index 7149f93251..dc6ef77dd4 100644
--- a/src/plugins/bazaar/bazaarsettings.cpp
+++ b/src/plugins/bazaar/bazaarsettings.cpp
@@ -16,95 +16,79 @@ using namespace Utils;
namespace Bazaar::Internal {
+static BazaarSettings *theSettings;
+
+BazaarSettings &settings()
+{
+ return *theSettings;
+}
+
BazaarSettings::BazaarSettings()
{
+ theSettings = this;
+
setSettingsGroup(Constants::BAZAAR);
- setAutoApply(false);
+ setId(VcsBase::Constants::VCS_ID_BAZAAR);
+ setDisplayName(Tr::tr("Bazaar"));
+ setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
- registerAspect(&binaryPath);
- binaryPath.setDisplayStyle(StringAspect::PathChooserDisplay);
binaryPath.setExpectedKind(PathChooser::ExistingCommand);
binaryPath.setDefaultValue(Constants::BAZAARDEFAULT);
binaryPath.setDisplayName(Tr::tr("Bazaar Command"));
binaryPath.setHistoryCompleter("Bazaar.Command.History");
binaryPath.setLabelText(Tr::tr("Command:"));
- registerAspect(&diffIgnoreWhiteSpace);
diffIgnoreWhiteSpace.setSettingsKey("diffIgnoreWhiteSpace");
- registerAspect(&diffIgnoreBlankLines);
diffIgnoreBlankLines.setSettingsKey("diffIgnoreBlankLines");
- registerAspect(&logVerbose);
logVerbose.setSettingsKey("logVerbose");
- registerAspect(&logFormat);
logForward.setSettingsKey("logForward");
- registerAspect(&logIncludeMerges);
logIncludeMerges.setSettingsKey("logIncludeMerges");
- registerAspect(&logFormat);
logFormat.setDisplayStyle(StringAspect::LineEditDisplay);
logFormat.setSettingsKey("logFormat");
logFormat.setDefaultValue("long");
- registerAspect(&userName);
userName.setDisplayStyle(StringAspect::LineEditDisplay);
userName.setLabelText(Tr::tr("Default username:"));
userName.setToolTip(Tr::tr("Username to use by default on commit."));
- registerAspect(&userEmail);
userEmail.setDisplayStyle(StringAspect::LineEditDisplay);
userEmail.setLabelText(Tr::tr("Default email:"));
userEmail.setToolTip(Tr::tr("Email to use by default on commit."));
- registerAspect(&logCount);
logCount.setLabelText(Tr::tr("Log count:"));
logCount.setToolTip(Tr::tr("The number of recent commit logs to show. Choose 0 to see all entries."));
- registerAspect(&logCount);
timeout.setLabelText(Tr::tr("Timeout:"));
timeout.setSuffix(Tr::tr("s"));
-}
-
-// BazaarSettingsPage
-
-BazaarSettingsPage::BazaarSettingsPage(BazaarSettings *settings)
-{
- setId(VcsBase::Constants::VCS_ID_BAZAAR);
- setDisplayName(Tr::tr("Bazaar"));
- setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
- setSettings(settings);
- setLayouter([settings](QWidget *widget) {
- BazaarSettings &s = *settings;
+ setLayouter([this] {
using namespace Layouting;
- Column {
+ return Column {
Group {
title(Tr::tr("Configuration")),
- Row { s.binaryPath }
+ Row { binaryPath }
},
Group {
title(Tr::tr("User")),
Form {
- s.userName,
- s.userEmail
+ userName, br,
+ userEmail
}
},
Group {
title(Tr::tr("Miscellaneous")),
- Row {
- s.logCount,
- s.timeout,
- st
- }
+ Row { logCount, timeout, st }
},
st
- }.attachTo(widget);
+ };
});
}
diff --git a/src/plugins/bazaar/bazaarsettings.h b/src/plugins/bazaar/bazaarsettings.h
index a10828b42d..0e6cc0d852 100644
--- a/src/plugins/bazaar/bazaarsettings.h
+++ b/src/plugins/bazaar/bazaarsettings.h
@@ -3,8 +3,6 @@
#pragma once
-#include <coreplugin/dialogs/ioptionspage.h>
-
#include <vcsbase/vcsbaseclientsettings.h>
namespace Bazaar::Internal {
@@ -14,18 +12,14 @@ class BazaarSettings final : public VcsBase::VcsBaseSettings
public:
BazaarSettings();
- Utils::BoolAspect diffIgnoreWhiteSpace;
- Utils::BoolAspect diffIgnoreBlankLines;
- Utils::BoolAspect logVerbose;
- Utils::BoolAspect logForward;
- Utils::BoolAspect logIncludeMerges;
- Utils::StringAspect logFormat;
+ Utils::BoolAspect diffIgnoreWhiteSpace{this};
+ Utils::BoolAspect diffIgnoreBlankLines{this};
+ Utils::BoolAspect logVerbose{this};
+ Utils::BoolAspect logForward{this};
+ Utils::BoolAspect logIncludeMerges{this};
+ Utils::StringAspect logFormat{this};
};
-class BazaarSettingsPage final : public Core::IOptionsPage
-{
-public:
- explicit BazaarSettingsPage(BazaarSettings *settings);
-};
+BazaarSettings &settings();
} // Bazaar::Internal
diff --git a/src/plugins/bazaar/pullorpushdialog.cpp b/src/plugins/bazaar/pullorpushdialog.cpp
index 10a4ce0452..4c2cd99e7c 100644
--- a/src/plugins/bazaar/pullorpushdialog.cpp
+++ b/src/plugins/bazaar/pullorpushdialog.cpp
@@ -72,7 +72,7 @@ PullOrPushDialog::PullOrPushDialog(Mode mode, QWidget *parent)
m_localCheckBox->setVisible(false);
}
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
title(Tr::tr("Branch Location")),
diff --git a/src/plugins/beautifier/CMakeLists.txt b/src/plugins/beautifier/CMakeLists.txt
index d721f1ef20..4e921342b7 100644
--- a/src/plugins/beautifier/CMakeLists.txt
+++ b/src/plugins/beautifier/CMakeLists.txt
@@ -5,7 +5,6 @@ add_qtc_plugin(Beautifier
abstractsettings.cpp abstractsettings.h
artisticstyle/artisticstyle.cpp artisticstyle/artisticstyle.h
artisticstyle/artisticstyleconstants.h
- artisticstyle/artisticstyleoptionspage.cpp artisticstyle/artisticstyleoptionspage.h
artisticstyle/artisticstylesettings.cpp artisticstyle/artisticstylesettings.h
beautifier.qrc
beautifierabstracttool.h
@@ -14,15 +13,12 @@ add_qtc_plugin(Beautifier
beautifiertr.h
clangformat/clangformat.cpp clangformat/clangformat.h
clangformat/clangformatconstants.h
- clangformat/clangformatoptionspage.cpp clangformat/clangformatoptionspage.h
clangformat/clangformatsettings.cpp clangformat/clangformatsettings.h
configurationdialog.cpp configurationdialog.h
configurationeditor.cpp configurationeditor.h
configurationpanel.cpp configurationpanel.h
- generaloptionspage.cpp generaloptionspage.h
generalsettings.cpp generalsettings.h
uncrustify/uncrustify.cpp uncrustify/uncrustify.h
uncrustify/uncrustifyconstants.h
- uncrustify/uncrustifyoptionspage.cpp uncrustify/uncrustifyoptionspage.h
uncrustify/uncrustifysettings.cpp uncrustify/uncrustifysettings.h
)
diff --git a/src/plugins/beautifier/abstractsettings.cpp b/src/plugins/beautifier/abstractsettings.cpp
index 0b96e1579e..1b4ff3aa10 100644
--- a/src/plugins/beautifier/abstractsettings.cpp
+++ b/src/plugins/beautifier/abstractsettings.cpp
@@ -14,7 +14,7 @@
#include <utils/fileutils.h>
#include <utils/genericconstants.h>
#include <utils/mimeutils.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QFile>
#include <QFileInfo>
@@ -26,15 +26,12 @@ using namespace Utils;
namespace Beautifier::Internal {
-const char COMMAND[] = "command";
-const char SUPPORTED_MIME[] = "supportedMime";
-
class VersionUpdater
{
public:
VersionUpdater()
{
- QObject::connect(&m_process, &QtcProcess::done, &m_process, [this] {
+ QObject::connect(&m_process, &Process::done, &m_process, [this] {
if (m_process.result() != ProcessResult::FinishedWithSuccess)
return;
@@ -77,7 +74,7 @@ private:
}
QRegularExpression m_versionRegExp;
- mutable QtcProcess m_process;
+ mutable Process m_process;
QVersionNumber m_versionNumber;
};
@@ -86,9 +83,38 @@ AbstractSettings::AbstractSettings(const QString &name, const QString &ending)
, m_styleDir(Core::ICore::userResourcePath(Beautifier::Constants::SETTINGS_DIRNAME)
.pathAppended(name)
.toString())
- , m_name(name)
, m_versionUpdater(new VersionUpdater)
{
+ setSettingsGroups(Utils::Constants::BEAUTIFIER_SETTINGS_GROUP, name);
+
+ command.setSettingsKey("command");
+ command.setExpectedKind(Utils::PathChooser::ExistingCommand);
+ command.setCommandVersionArguments({"--version"});
+ command.setPromptDialogTitle(BeautifierPlugin::msgCommandPromptDialogTitle("Clang Format"));
+
+ supportedMimeTypes.setDisplayStyle(StringAspect::LineEditDisplay);
+ supportedMimeTypes.setSettingsKey("supportedMime");
+ supportedMimeTypes.setLabelText(Tr::tr("Restrict to MIME types:"));
+ supportedMimeTypes.setDefaultValue("text/x-c++src; text/x-c++hdr; text/x-csrc;text/x-chdr; "
+ "text/x-objcsrc; text/x-objc++src");
+
+ supportedMimeTypes.setValueAcceptor([](const QString &, const QString &value) -> std::optional<QString> {
+ const QStringList stringTypes = value.split(';');
+ QStringList types;
+ for (const QString &type : stringTypes) {
+ const MimeType mime = mimeTypeForName(type.trimmed());
+ if (!mime.isValid())
+ continue;
+ const QString canonicalName = mime.name();
+ if (!types.contains(canonicalName))
+ types << canonicalName;
+ }
+ return types.join("; ");
+ });
+
+ connect(&command, &BaseAspect::changed, this, [this] {
+ m_versionUpdater->update(command());
+ });
}
AbstractSettings::~AbstractSettings() = default;
@@ -157,20 +183,6 @@ QString AbstractSettings::styleFileName(const QString &key) const
return m_styleDir.absoluteFilePath(key + m_ending);
}
-FilePath AbstractSettings::command() const
-{
- return m_command;
-}
-
-void AbstractSettings::setCommand(const FilePath &cmd)
-{
- if (cmd == m_command)
- return;
-
- m_command = cmd;
- m_versionUpdater->update(command());
-}
-
QVersionNumber AbstractSettings::version() const
{
return m_versionUpdater->version();
@@ -181,30 +193,6 @@ void AbstractSettings::setVersionRegExp(const QRegularExpression &versionRegExp)
m_versionUpdater->setVersionRegExp(versionRegExp);
}
-QString AbstractSettings::supportedMimeTypesAsString() const
-{
- return m_supportedMimeTypes.join("; ");
-}
-
-void AbstractSettings::setSupportedMimeTypes(const QString &mimes)
-{
- const QStringList stringTypes = mimes.split(';');
- QStringList types;
- for (const QString &type : stringTypes) {
- const MimeType mime = mimeTypeForName(type.trimmed());
- if (!mime.isValid())
- continue;
- const QString canonicalName = mime.name();
- if (!types.contains(canonicalName))
- types << canonicalName;
- }
-
- if (m_supportedMimeTypes != types) {
- m_supportedMimeTypes = types;
- emit supportedMimeTypesChanged();
- }
-}
-
bool AbstractSettings::isApplicable(const Core::IDocument *document) const
{
if (!document)
@@ -240,17 +228,8 @@ void AbstractSettings::save()
{
// Save settings, except styles
QSettings *s = Core::ICore::settings();
- s->beginGroup(Utils::Constants::BEAUTIFIER_SETTINGS_GROUP);
- s->beginGroup(m_name);
- QMap<QString, QVariant>::const_iterator iSettings = m_settings.constBegin();
- while (iSettings != m_settings.constEnd()) {
- s->setValue(iSettings.key(), iSettings.value());
- ++iSettings;
- }
- s->setValue(COMMAND, m_command.toSettings());
- s->setValue(SUPPORTED_MIME, supportedMimeTypesAsString());
- s->endGroup();
- s->endGroup();
+
+ AspectContainer::writeSettings(s);
// Save styles
if (m_stylesToRemove.isEmpty() && m_styles.isEmpty())
@@ -306,27 +285,9 @@ void AbstractSettings::createDocumentationFile() const
void AbstractSettings::read()
{
- // Set default values
- setSupportedMimeTypes("text/x-c++src;text/x-c++hdr;text/x-csrc;text/x-chdr;text/x-objcsrc;"
- "text/x-objc++src");
-
// Read settings, except styles
QSettings *s = Core::ICore::settings();
- s->beginGroup(Utils::Constants::BEAUTIFIER_SETTINGS_GROUP);
- s->beginGroup(m_name);
- const QStringList keys = s->allKeys();
- for (const QString &key : keys) {
- if (key == COMMAND)
- setCommand(FilePath::fromSettings(s->value(key)));
- else if (key == SUPPORTED_MIME)
- setSupportedMimeTypes(s->value(key).toString());
- else if (m_settings.contains(key))
- m_settings[key] = s->value(key);
- else
- s->remove(key);
- }
- s->endGroup();
- s->endGroup();
+ AspectContainer::readSettings(s);
m_styles.clear();
m_changedStyles.clear();
@@ -336,18 +297,19 @@ void AbstractSettings::read()
void AbstractSettings::readDocumentation()
{
- const QString filename = documentationFilePath();
+ const FilePath filename = documentationFilePath;
if (filename.isEmpty()) {
BeautifierPlugin::showError(Tr::tr("No documentation file specified."));
return;
}
- QFile file(filename);
+ QFile file(filename.toFSPathString());
if (!file.exists())
createDocumentationFile();
if (!file.open(QIODevice::ReadOnly)) {
- BeautifierPlugin::showError(Tr::tr("Cannot open documentation file \"%1\".").arg(filename));
+ BeautifierPlugin::showError(Tr::tr("Cannot open documentation file \"%1\".")
+ .arg(filename.toUserOutput()));
return;
}
@@ -356,7 +318,7 @@ void AbstractSettings::readDocumentation()
return;
if (xml.name() != QLatin1String(Constants::DOCUMENTATION_XMLROOT)) {
BeautifierPlugin::showError(Tr::tr("The file \"%1\" is not a valid documentation file.")
- .arg(filename));
+ .arg(filename.toUserOutput()));
return;
}
@@ -387,7 +349,7 @@ void AbstractSettings::readDocumentation()
if (xml.hasError()) {
BeautifierPlugin::showError(Tr::tr("Cannot read documentation file \"%1\": %2.")
- .arg(filename).arg(xml.errorString()));
+ .arg(filename.toUserOutput()).arg(xml.errorString()));
}
}
diff --git a/src/plugins/beautifier/abstractsettings.h b/src/plugins/beautifier/abstractsettings.h
index d386313e1b..8e9ceea931 100644
--- a/src/plugins/beautifier/abstractsettings.h
+++ b/src/plugins/beautifier/abstractsettings.h
@@ -3,17 +3,17 @@
#pragma once
+#include <coreplugin/dialogs/ioptionspage.h>
+
+#include <utils/aspects.h>
#include <utils/filepath.h>
-#include <QCoreApplication>
#include <QDir>
#include <QHash>
#include <QMap>
-#include <QObject>
#include <QSet>
#include <QString>
#include <QStringList>
-#include <QVector>
#include <memory>
@@ -28,10 +28,8 @@ namespace Beautifier::Internal {
class VersionUpdater;
-class AbstractSettings : public QObject
+class AbstractSettings : public Utils::AspectContainer
{
- Q_OBJECT
-
public:
explicit AbstractSettings(const QString &name, const QString &ending);
~AbstractSettings() override;
@@ -39,7 +37,6 @@ public:
void read();
void save();
- virtual QString documentationFilePath() const = 0;
virtual void createDocumentationFile() const;
virtual QStringList completerWords();
@@ -52,25 +49,22 @@ public:
void replaceStyle(const QString &oldKey, const QString &newKey, const QString &value);
virtual QString styleFileName(const QString &key) const;
- Utils::FilePath command() const;
- void setCommand(const Utils::FilePath &cmd);
+ Utils::FilePathAspect command{this};
+ Utils::StringAspect supportedMimeTypes{this};
+
+ Utils::FilePath documentationFilePath;
+
QVersionNumber version() const;
- QString supportedMimeTypesAsString() const;
- void setSupportedMimeTypes(const QString &mimes);
bool isApplicable(const Core::IDocument *document) const;
QStringList options();
QString documentation(const QString &option) const;
-signals:
- void supportedMimeTypesChanged();
-
protected:
void setVersionRegExp(const QRegularExpression &versionRegExp);
QMap<QString, QString> m_styles;
- QMap<QString, QVariant> m_settings;
QString m_ending;
QDir m_styleDir;
@@ -78,11 +72,9 @@ protected:
virtual void readStyles();
private:
- QString m_name;
std::unique_ptr<VersionUpdater> m_versionUpdater;
QStringList m_stylesToRemove;
QSet<QString> m_changedStyles;
- Utils::FilePath m_command;
QHash<QString, int> m_options;
QStringList m_docu;
QStringList m_supportedMimeTypes;
diff --git a/src/plugins/beautifier/artisticstyle/artisticstyle.cpp b/src/plugins/beautifier/artisticstyle/artisticstyle.cpp
index 610f636d74..1f06382994 100644
--- a/src/plugins/beautifier/artisticstyle/artisticstyle.cpp
+++ b/src/plugins/beautifier/artisticstyle/artisticstyle.cpp
@@ -48,7 +48,7 @@ ArtisticStyle::ArtisticStyle()
Core::ActionManager::actionContainer(Constants::MENU_ID)->addMenu(menu);
- connect(&m_settings, &ArtisticStyleSettings::supportedMimeTypesChanged,
+ connect(&m_settings.supportedMimeTypes, &Utils::BaseAspect::changed,
this, [this] { updateActions(Core::EditorManager::currentEditor()); });
}
diff --git a/src/plugins/beautifier/artisticstyle/artisticstyle.h b/src/plugins/beautifier/artisticstyle/artisticstyle.h
index ef394d068a..3f3f15946a 100644
--- a/src/plugins/beautifier/artisticstyle/artisticstyle.h
+++ b/src/plugins/beautifier/artisticstyle/artisticstyle.h
@@ -5,7 +5,6 @@
#include "../beautifierabstracttool.h"
-#include "artisticstyleoptionspage.h"
#include "artisticstylesettings.h"
namespace Beautifier::Internal {
diff --git a/src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.cpp b/src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.cpp
deleted file mode 100644
index 0c0735be0e..0000000000
--- a/src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (C) 2016 Lorenz Haas
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "artisticstyleoptionspage.h"
-
-#include "artisticstyleconstants.h"
-#include "artisticstylesettings.h"
-
-#include "../beautifierconstants.h"
-#include "../beautifierplugin.h"
-#include "../beautifiertr.h"
-#include "../configurationpanel.h"
-
-#include <utils/layoutbuilder.h>
-#include <utils/pathchooser.h>
-
-#include <QApplication>
-#include <QCheckBox>
-#include <QGroupBox>
-#include <QLabel>
-#include <QLineEdit>
-
-namespace Beautifier::Internal {
-
-class ArtisticStyleOptionsPageWidget : public Core::IOptionsPageWidget
-{
-public:
- explicit ArtisticStyleOptionsPageWidget(ArtisticStyleSettings *settings);
-
- void apply() final;
-
-private:
- ArtisticStyleSettings *m_settings;
-
- Utils::PathChooser *m_command;
- QLineEdit *m_mime;
- QCheckBox *m_useOtherFiles;
- QCheckBox *m_useSpecificConfigFile;
- Utils::PathChooser *m_specificConfigFile;
- QCheckBox *m_useHomeFile;
- QCheckBox *m_useCustomStyle;
- Beautifier::Internal::ConfigurationPanel *m_configurations;
-};
-
-ArtisticStyleOptionsPageWidget::ArtisticStyleOptionsPageWidget(ArtisticStyleSettings *settings)
- : m_settings(settings)
-{
- resize(817, 631);
-
- m_command = new Utils::PathChooser;
-
- m_mime = new QLineEdit(m_settings->supportedMimeTypesAsString());
-
- auto options = new QGroupBox(Tr::tr("Options"));
-
- m_useOtherFiles = new QCheckBox(Tr::tr("Use file *.astylerc defined in project files"));
- m_useOtherFiles->setChecked(m_settings->useOtherFiles());
-
- m_useSpecificConfigFile = new QCheckBox(Tr::tr("Use specific config file:"));
- m_useSpecificConfigFile->setChecked(m_settings->useSpecificConfigFile());
-
- m_specificConfigFile = new Utils::PathChooser;
- m_specificConfigFile->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
- m_specificConfigFile->setExpectedKind(Utils::PathChooser::File);
- m_specificConfigFile->setPromptDialogFilter(Tr::tr("AStyle (*.astylerc)"));
- m_specificConfigFile->setFilePath(m_settings->specificConfigFile());
-
- m_useHomeFile = new QCheckBox(
- Tr::tr("Use file .astylerc or astylerc in HOME").
- replace("HOME", QDir::toNativeSeparators(QDir::home().absolutePath())));
- m_useHomeFile->setChecked(m_settings->useHomeFile());
-
- m_useCustomStyle = new QCheckBox(Tr::tr("Use customized style:"));
- m_useCustomStyle->setChecked(m_settings->useCustomStyle());
-
- m_configurations = new ConfigurationPanel(options);
- m_configurations->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
- m_configurations->setSettings(m_settings);
- m_configurations->setCurrentConfiguration(m_settings->customStyle());
-
- m_command->setExpectedKind(Utils::PathChooser::ExistingCommand);
- m_command->setCommandVersionArguments({"--version"});
- m_command->setPromptDialogTitle(BeautifierPlugin::msgCommandPromptDialogTitle(
- Tr::tr(Constants::ARTISTICSTYLE_DISPLAY_NAME)));
- m_command->setFilePath(m_settings->command());
-
- using namespace Utils::Layouting;
-
- Column {
- m_useOtherFiles,
- Row { m_useSpecificConfigFile, m_specificConfigFile },
- m_useHomeFile,
- Row { m_useCustomStyle, m_configurations }
- }.attachTo(options);
-
- Column {
- Group {
- title(Tr::tr("Configuration")),
- Form {
- Tr::tr("Artistic Style command:"), m_command, br,
- Tr::tr("Restrict to MIME types:"), m_mime
- }
- },
- options,
- st
- }.attachTo(this);
-
- connect(m_command, &Utils::PathChooser::validChanged, options, &QWidget::setEnabled);
-}
-
-void ArtisticStyleOptionsPageWidget::apply()
-{
- m_settings->setCommand(m_command->filePath());
- m_settings->setSupportedMimeTypes(m_mime->text());
- m_settings->setUseOtherFiles(m_useOtherFiles->isChecked());
- m_settings->setUseSpecificConfigFile(m_useSpecificConfigFile->isChecked());
- m_settings->setSpecificConfigFile(m_specificConfigFile->filePath());
- m_settings->setUseHomeFile(m_useHomeFile->isChecked());
- m_settings->setUseCustomStyle(m_useCustomStyle->isChecked());
- m_settings->setCustomStyle(m_configurations->currentConfiguration());
- m_settings->save();
-
- // update since not all MIME types are accepted (invalids or duplicates)
- m_mime->setText(m_settings->supportedMimeTypesAsString());
-}
-
-ArtisticStyleOptionsPage::ArtisticStyleOptionsPage(ArtisticStyleSettings *settings)
-{
- setId("ArtisticStyle");
- setDisplayName(Tr::tr("Artistic Style"));
- setCategory(Constants::OPTION_CATEGORY);
- setWidgetCreator([settings] { return new ArtisticStyleOptionsPageWidget(settings); });
-}
-
-} // Beautifier::Internal
diff --git a/src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.h b/src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.h
deleted file mode 100644
index 161e20f706..0000000000
--- a/src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (C) 2016 Lorenz Haas
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <coreplugin/dialogs/ioptionspage.h>
-
-namespace Beautifier::Internal {
-
-class ArtisticStyleSettings;
-
-class ArtisticStyleOptionsPage final : public Core::IOptionsPage
-{
-public:
- explicit ArtisticStyleOptionsPage(ArtisticStyleSettings *settings);
-};
-
-} // Beautifier::Internal
diff --git a/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp b/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp
index 1cff800ed1..415d4ab20d 100644
--- a/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp
+++ b/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp
@@ -3,16 +3,24 @@
#include "artisticstylesettings.h"
+#include "artisticstyleconstants.h"
#include "../beautifierconstants.h"
+#include "../beautifierplugin.h"
+#include "../beautifiertr.h"
+#include "../configurationpanel.h"
#include <coreplugin/icore.h>
+#include <utils/layoutbuilder.h>
+#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/stringutils.h>
-#include <utils/qtcprocess.h>
+#include <QApplication>
#include <QDateTime>
#include <QFile>
#include <QFileInfo>
+#include <QGroupBox>
#include <QRegularExpression>
#include <QXmlStreamWriter>
@@ -20,106 +28,56 @@ using namespace Utils;
namespace Beautifier::Internal {
-const char USE_OTHER_FILES[] = "useOtherFiles";
-const char USE_SPECIFIC_CONFIG_FILE[] = "useSpecificConfigFile";
-const char SPECIFIC_CONFIG_FILE[] = "specificConfigFile";
-const char USE_HOME_FILE[] = "useHomeFile";
-const char USE_CUSTOM_STYLE[] = "useCustomStyle";
-const char CUSTOM_STYLE[] = "customStyle";
const char SETTINGS_NAME[] = "artisticstyle";
-ArtisticStyleSettings::ArtisticStyleSettings() :
- AbstractSettings(SETTINGS_NAME, ".astyle")
+ArtisticStyleSettings::ArtisticStyleSettings()
+ : AbstractSettings(SETTINGS_NAME, ".astyle")
{
setVersionRegExp(QRegularExpression("([2-9]{1})\\.([0-9]{1,2})(\\.[1-9]{1})?$"));
- setCommand("astyle");
- m_settings.insert(USE_OTHER_FILES, QVariant(true));
- m_settings.insert(USE_SPECIFIC_CONFIG_FILE, QVariant(false));
- m_settings.insert(SPECIFIC_CONFIG_FILE, QVariant());
- m_settings.insert(USE_HOME_FILE, QVariant(false));
- m_settings.insert(USE_CUSTOM_STYLE, QVariant(false));
- m_settings.insert(CUSTOM_STYLE, QVariant());
- read();
-}
-
-bool ArtisticStyleSettings::useOtherFiles() const
-{
- return m_settings.value(USE_OTHER_FILES).toBool();
-}
-
-void ArtisticStyleSettings::setUseOtherFiles(bool useOtherFiles)
-{
- m_settings.insert(USE_OTHER_FILES, QVariant(useOtherFiles));
-}
-
-bool ArtisticStyleSettings::useSpecificConfigFile() const
-{
- return m_settings.value(USE_SPECIFIC_CONFIG_FILE).toBool();
-}
+ command.setLabelText(Tr::tr("Artistic Style command:"));
+ command.setDefaultValue("astyle");
+ command.setPromptDialogTitle(BeautifierPlugin::msgCommandPromptDialogTitle(
+ Tr::tr(Constants::ARTISTICSTYLE_DISPLAY_NAME)));
-void ArtisticStyleSettings::setUseSpecificConfigFile(bool useSpecificConfigFile)
-{
- m_settings.insert(USE_SPECIFIC_CONFIG_FILE, QVariant(useSpecificConfigFile));
-}
+ useOtherFiles.setSettingsKey("useOtherFiles");
+ useOtherFiles.setLabelText(Tr::tr("Use file *.astylerc defined in project files"));
+ useOtherFiles.setDefaultValue(true);
-FilePath ArtisticStyleSettings::specificConfigFile() const
-{
- return FilePath::fromString(m_settings.value(SPECIFIC_CONFIG_FILE).toString());
-}
+ useSpecificConfigFile.setSettingsKey("useSpecificConfigFile");
+ useSpecificConfigFile.setLabelText(Tr::tr("Use specific config file:"));
-void ArtisticStyleSettings::setSpecificConfigFile(const FilePath &specificConfigFile)
-{
- m_settings.insert(SPECIFIC_CONFIG_FILE, QVariant(specificConfigFile.toString()));
-}
-
-bool ArtisticStyleSettings::useHomeFile() const
-{
- return m_settings.value(USE_HOME_FILE).toBool();
-}
-
-void ArtisticStyleSettings::setUseHomeFile(bool useHomeFile)
-{
- m_settings.insert(USE_HOME_FILE, QVariant(useHomeFile));
-}
+ specificConfigFile.setSettingsKey("specificConfigFile");
+ specificConfigFile.setExpectedKind(PathChooser::File);
+ specificConfigFile.setPromptDialogFilter(Tr::tr("AStyle (*.astylerc)"));
-bool ArtisticStyleSettings::useCustomStyle() const
-{
- return m_settings.value(USE_CUSTOM_STYLE).toBool();
-}
+ useHomeFile.setSettingsKey("useHomeFile");
+ useHomeFile.setLabelText(Tr::tr("Use file .astylerc or astylerc in HOME").
+ replace("HOME", QDir::toNativeSeparators(QDir::home().absolutePath())));
-void ArtisticStyleSettings::setUseCustomStyle(bool useCustomStyle)
-{
- m_settings.insert(USE_CUSTOM_STYLE, QVariant(useCustomStyle));
-}
+ useCustomStyle.setSettingsKey("useCustomStyle");
+ useCustomStyle.setLabelText(Tr::tr("Use customized style:"));
-QString ArtisticStyleSettings::customStyle() const
-{
- return m_settings.value(CUSTOM_STYLE).toString();
-}
+ customStyle.setSettingsKey("customStyle");
-void ArtisticStyleSettings::setCustomStyle(const QString &customStyle)
-{
- m_settings.insert(CUSTOM_STYLE, QVariant(customStyle));
-}
+ documentationFilePath =
+ Core::ICore::userResourcePath(Beautifier::Constants::SETTINGS_DIRNAME)
+ .pathAppended(Beautifier::Constants::DOCUMENTATION_DIRNAME)
+ .pathAppended(SETTINGS_NAME)
+ .stringAppended(".xml");
-QString ArtisticStyleSettings::documentationFilePath() const
-{
- return (Core::ICore::userResourcePath(Beautifier::Constants::SETTINGS_DIRNAME)
- / Beautifier::Constants::DOCUMENTATION_DIRNAME / SETTINGS_NAME)
- .stringAppended(".xml")
- .toString();
+ read();
}
void ArtisticStyleSettings::createDocumentationFile() const
{
- QtcProcess process;
+ Process process;
process.setTimeoutS(2);
process.setCommand({command(), {"-h"}});
process.runBlocking();
if (process.result() != ProcessResult::FinishedWithSuccess)
return;
- QFile file(documentationFilePath());
+ QFile file(documentationFilePath.toFSPathString());
const QFileInfo fi(file);
if (!fi.exists())
fi.dir().mkpath(fi.absolutePath());
@@ -183,4 +141,61 @@ void ArtisticStyleSettings::createDocumentationFile() const
}
}
+class ArtisticStyleOptionsPageWidget : public Core::IOptionsPageWidget
+{
+public:
+ explicit ArtisticStyleOptionsPageWidget(ArtisticStyleSettings *settings)
+ {
+ QGroupBox *options = nullptr;
+
+ auto configurations = new ConfigurationPanel(this);
+ configurations->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+ configurations->setSettings(settings);
+ configurations->setCurrentConfiguration(settings->customStyle());
+
+ using namespace Layouting;
+
+ ArtisticStyleSettings &s = *settings;
+
+ Column {
+ Group {
+ title(Tr::tr("Configuration")),
+ Form {
+ s.command, br,
+ s.supportedMimeTypes
+ }
+ },
+ Group {
+ title(Tr::tr("Options")),
+ bindTo(&options),
+ Column {
+ s.useOtherFiles,
+ Row { s.useSpecificConfigFile, s.specificConfigFile },
+ s.useHomeFile,
+ Row { s.useCustomStyle, configurations },
+ }
+ },
+ st
+ }.attachTo(this);
+
+ setOnApply([&s, configurations] {
+ s.customStyle.setValue(configurations->currentConfiguration());
+ s.save();
+ });
+
+ s.read();
+
+ connect(s.command.pathChooser(), &PathChooser::validChanged, options, &QWidget::setEnabled);
+ options->setEnabled(s.command.pathChooser()->isValid());
+ }
+};
+
+ArtisticStyleOptionsPage::ArtisticStyleOptionsPage(ArtisticStyleSettings *settings)
+{
+ setId("ArtisticStyle");
+ setDisplayName(Tr::tr("Artistic Style"));
+ setCategory(Constants::OPTION_CATEGORY);
+ setWidgetCreator([settings] { return new ArtisticStyleOptionsPageWidget(settings); });
+}
+
} // Beautifier::Internal
diff --git a/src/plugins/beautifier/artisticstyle/artisticstylesettings.h b/src/plugins/beautifier/artisticstyle/artisticstylesettings.h
index de5402a8ee..25932cf8db 100644
--- a/src/plugins/beautifier/artisticstyle/artisticstylesettings.h
+++ b/src/plugins/beautifier/artisticstyle/artisticstylesettings.h
@@ -5,8 +5,6 @@
#include "../abstractsettings.h"
-#include <utils/fileutils.h>
-
namespace Beautifier::Internal {
class ArtisticStyleSettings : public AbstractSettings
@@ -14,26 +12,20 @@ class ArtisticStyleSettings : public AbstractSettings
public:
ArtisticStyleSettings();
- bool useOtherFiles() const;
- void setUseOtherFiles(bool useOtherFiles);
-
- bool useSpecificConfigFile() const;
- void setUseSpecificConfigFile(bool useSpecificConfigFile);
-
- Utils::FilePath specificConfigFile() const;
- void setSpecificConfigFile(const Utils::FilePath &specificConfigFile);
-
- bool useHomeFile() const;
- void setUseHomeFile(bool useHomeFile);
-
- bool useCustomStyle() const;
- void setUseCustomStyle(bool useCustomStyle);
+ Utils::BoolAspect useOtherFiles{this};
+ Utils::BoolAspect useSpecificConfigFile{this};
+ Utils::FilePathAspect specificConfigFile{this};
+ Utils::BoolAspect useHomeFile{this};
+ Utils::BoolAspect useCustomStyle{this};
+ Utils::StringAspect customStyle{this};
- QString customStyle() const;
- void setCustomStyle(const QString &customStyle);
-
- QString documentationFilePath() const override;
void createDocumentationFile() const override;
};
+class ArtisticStyleOptionsPage final : public Core::IOptionsPage
+{
+public:
+ explicit ArtisticStyleOptionsPage(ArtisticStyleSettings *settings);
+};
+
} // Beautifier::Internal
diff --git a/src/plugins/beautifier/beautifier.qbs b/src/plugins/beautifier/beautifier.qbs
index d1160ae5f5..24fecd671f 100644
--- a/src/plugins/beautifier/beautifier.qbs
+++ b/src/plugins/beautifier/beautifier.qbs
@@ -25,8 +25,6 @@ QtcPlugin {
"configurationeditor.h",
"configurationpanel.cpp",
"configurationpanel.h",
- "generaloptionspage.cpp",
- "generaloptionspage.h",
"generalsettings.cpp",
"generalsettings.h",
]
@@ -38,8 +36,6 @@ QtcPlugin {
"artisticstyle.cpp",
"artisticstyle.h",
"artisticstyleconstants.h",
- "artisticstyleoptionspage.cpp",
- "artisticstyleoptionspage.h",
"artisticstylesettings.cpp",
"artisticstylesettings.h"
]
@@ -52,8 +48,6 @@ QtcPlugin {
"clangformat.cpp",
"clangformat.h",
"clangformatconstants.h",
- "clangformatoptionspage.cpp",
- "clangformatoptionspage.h",
"clangformatsettings.cpp",
"clangformatsettings.h"
]
@@ -66,8 +60,6 @@ QtcPlugin {
"uncrustify.cpp",
"uncrustify.h",
"uncrustifyconstants.h",
- "uncrustifyoptionspage.cpp",
- "uncrustifyoptionspage.h",
"uncrustifysettings.cpp",
"uncrustifysettings.h"
]
diff --git a/src/plugins/beautifier/beautifierplugin.cpp b/src/plugins/beautifier/beautifierplugin.cpp
index 76db40daea..b849976a05 100644
--- a/src/plugins/beautifier/beautifierplugin.cpp
+++ b/src/plugins/beautifier/beautifierplugin.cpp
@@ -5,7 +5,6 @@
#include "beautifierconstants.h"
#include "beautifiertr.h"
-#include "generaloptionspage.h"
#include "generalsettings.h"
#include "artisticstyle/artisticstyle.h"
@@ -31,8 +30,8 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/mimeutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/temporarydirectory.h>
#include <utils/textutils.h>
@@ -80,12 +79,6 @@ public:
&uncrustifyBeautifier,
&clangFormatBeautifier
};
-
- GeneralOptionsPage optionsPage {{
- artisticStyleBeautifier.id(),
- uncrustifyBeautifier.id(),
- clangFormatBeautifier.id()
- }};
};
static BeautifierPluginPrivate *dd = nullptr;
@@ -112,6 +105,9 @@ ExtensionSystem::IPlugin::ShutdownFlag BeautifierPlugin::aboutToShutdown()
BeautifierPluginPrivate::BeautifierPluginPrivate()
{
+ for (BeautifierAbstractTool *tool : m_tools)
+ generalSettings.autoFormatTools.addOption(tool->id());
+
updateActions();
const Core::EditorManager *editorManager = Core::EditorManager::instance();
@@ -129,14 +125,14 @@ void BeautifierPluginPrivate::updateActions(Core::IEditor *editor)
void BeautifierPluginPrivate::autoFormatOnSave(Core::IDocument *document)
{
- if (!generalSettings.autoFormatOnSave())
+ if (!generalSettings.autoFormatOnSave.value())
return;
- if (!isAutoFormatApplicable(document, generalSettings.autoFormatMime()))
+ if (!isAutoFormatApplicable(document, generalSettings.allowedMimeTypes()))
return;
// Check if file is contained in the current project (if wished)
- if (generalSettings.autoFormatOnlyCurrentProject()) {
+ if (generalSettings.autoFormatOnlyCurrentProject.value()) {
const ProjectExplorer::Project *pro = ProjectExplorer::ProjectTree::currentProject();
if (!pro
|| pro->files([document](const ProjectExplorer::Node *n) {
@@ -149,7 +145,7 @@ void BeautifierPluginPrivate::autoFormatOnSave(Core::IDocument *document)
}
// Find tool to use by id and format file!
- const QString id = generalSettings.autoFormatTool();
+ const QString id = generalSettings.autoFormatTools.stringValue();
auto tool = std::find_if(std::begin(m_tools), std::end(m_tools),
[&id](const BeautifierAbstractTool *t){return t->id() == id;});
if (tool != std::end(m_tools)) {
diff --git a/src/plugins/beautifier/clangformat/clangformat.cpp b/src/plugins/beautifier/clangformat/clangformat.cpp
index c1833a77f1..92c8c7f454 100644
--- a/src/plugins/beautifier/clangformat/clangformat.cpp
+++ b/src/plugins/beautifier/clangformat/clangformat.cpp
@@ -64,7 +64,7 @@ ClangFormat::ClangFormat()
Core::ActionManager::actionContainer(Constants::MENU_ID)->addMenu(menu);
- connect(&m_settings, &ClangFormatSettings::supportedMimeTypesChanged,
+ connect(&m_settings.supportedMimeTypes, &Utils::BaseAspect::changed,
this, [this] { updateActions(Core::EditorManager::currentEditor()); });
}
@@ -191,10 +191,10 @@ Command ClangFormat::command() const
command.setProcessing(Command::PipeProcessing);
if (m_settings.usePredefinedStyle()) {
- const QString predefinedStyle = m_settings.predefinedStyle();
+ const QString predefinedStyle = m_settings.predefinedStyle.stringValue();
command.addOption("-style=" + predefinedStyle);
if (predefinedStyle == "File") {
- const QString fallbackStyle = m_settings.fallbackStyle();
+ const QString fallbackStyle = m_settings.fallbackStyle.stringValue();
if (fallbackStyle != "Default")
command.addOption("-fallback-style=" + fallbackStyle);
}
diff --git a/src/plugins/beautifier/clangformat/clangformat.h b/src/plugins/beautifier/clangformat/clangformat.h
index 8563a782ef..65fd23a43f 100644
--- a/src/plugins/beautifier/clangformat/clangformat.h
+++ b/src/plugins/beautifier/clangformat/clangformat.h
@@ -5,7 +5,6 @@
#include "../beautifierabstracttool.h"
-#include "clangformatoptionspage.h"
#include "clangformatsettings.h"
namespace Beautifier::Internal {
diff --git a/src/plugins/beautifier/clangformat/clangformatoptionspage.cpp b/src/plugins/beautifier/clangformat/clangformatoptionspage.cpp
deleted file mode 100644
index 9423aa54a8..0000000000
--- a/src/plugins/beautifier/clangformat/clangformatoptionspage.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright (C) 2016 Lorenz Haas
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "clangformatoptionspage.h"
-
-#include "clangformatsettings.h"
-
-#include "../beautifierconstants.h"
-#include "../beautifierplugin.h"
-#include "../beautifiertr.h"
-#include "../configurationpanel.h"
-
-#include <utils/layoutbuilder.h>
-#include <utils/pathchooser.h>
-
-#include <QButtonGroup>
-#include <QComboBox>
-#include <QGroupBox>
-#include <QLabel>
-#include <QLineEdit>
-#include <QRadioButton>
-#include <QSpacerItem>
-
-namespace Beautifier::Internal {
-
-class ClangFormatOptionsPageWidget : public Core::IOptionsPageWidget
-{
-public:
- explicit ClangFormatOptionsPageWidget(ClangFormatSettings *settings);
-
- void apply() final;
-
-private:
- ClangFormatSettings *m_settings;
- ConfigurationPanel *m_configurations;
- QRadioButton *m_usePredefinedStyle;
- QComboBox *m_predefinedStyle;
- QComboBox *m_fallbackStyle;
- Utils::PathChooser *m_command;
- QLineEdit *m_mime;
-};
-
-ClangFormatOptionsPageWidget::ClangFormatOptionsPageWidget(ClangFormatSettings *settings)
- : m_settings(settings)
-{
- auto options = new QGroupBox(Tr::tr("Options"));
- options->setEnabled(false);
-
- auto styleButtonGroup = new QButtonGroup(this);
-
- auto useCustomizedStyle = new QRadioButton(Tr::tr("Use customized style:"));
- styleButtonGroup->addButton(useCustomizedStyle);
-
- m_configurations = new ConfigurationPanel;
- m_configurations->setSettings(m_settings);
- m_configurations->setCurrentConfiguration(m_settings->customStyle());
-
- m_usePredefinedStyle = new QRadioButton(Tr::tr("Use predefined style:"));
-
- m_usePredefinedStyle->setChecked(true);
- styleButtonGroup->addButton(m_usePredefinedStyle);
-
- m_predefinedStyle = new QComboBox;
- m_predefinedStyle->addItems(m_settings->predefinedStyles());
- const int predefinedStyleIndex = m_predefinedStyle->findText(m_settings->predefinedStyle());
- if (predefinedStyleIndex != -1)
- m_predefinedStyle->setCurrentIndex(predefinedStyleIndex);
-
- m_fallbackStyle = new QComboBox;
- m_fallbackStyle->addItems(m_settings->fallbackStyles());
- m_fallbackStyle->setEnabled(false);
- const int fallbackStyleIndex = m_fallbackStyle->findText(m_settings->fallbackStyle());
- if (fallbackStyleIndex != -1)
- m_fallbackStyle->setCurrentIndex(fallbackStyleIndex);
-
- m_mime = new QLineEdit(m_settings->supportedMimeTypesAsString());
-
- m_command = new Utils::PathChooser;
- m_command->setExpectedKind(Utils::PathChooser::ExistingCommand);
- m_command->setCommandVersionArguments({"--version"});
- m_command->setPromptDialogTitle(
- BeautifierPlugin::msgCommandPromptDialogTitle("Clang Format"));
-
- if (m_settings->usePredefinedStyle())
- m_usePredefinedStyle->setChecked(true);
- else
- useCustomizedStyle->setChecked(true);
-
- using namespace Utils::Layouting;
-
- Form {
- m_usePredefinedStyle, m_predefinedStyle, br,
- empty, Row { Tr::tr("Fallback style:"), m_fallbackStyle }, br,
- useCustomizedStyle, m_configurations, br,
- }.attachTo(options);
-
- Column {
- Group {
- title(Tr::tr("Configuration")),
- Form {
- Tr::tr("Clang Format command:"), m_command, br,
- Tr::tr("Restrict to MIME types:"), m_mime
- }
- },
- options,
- st
- }.attachTo(this);
-
- connect(m_command, &Utils::PathChooser::validChanged, options, &QWidget::setEnabled);
- connect(m_predefinedStyle, &QComboBox::currentTextChanged, this, [this](const QString &item) {
- m_fallbackStyle->setEnabled(item == "File");
- });
- connect(m_usePredefinedStyle, &QRadioButton::toggled, this, [this](bool checked) {
- m_fallbackStyle->setEnabled(checked && m_predefinedStyle->currentText() == "File");
- m_predefinedStyle->setEnabled(checked);
- });
-
- // might trigger PathChooser::validChanged, so so after the connect above
- m_command->setFilePath(m_settings->command());
-}
-
-void ClangFormatOptionsPageWidget::apply()
-{
- m_settings->setCommand(m_command->filePath());
- m_settings->setSupportedMimeTypes(m_mime->text());
- m_settings->setUsePredefinedStyle(m_usePredefinedStyle->isChecked());
- m_settings->setPredefinedStyle(m_predefinedStyle->currentText());
- m_settings->setFallbackStyle(m_fallbackStyle->currentText());
- m_settings->setCustomStyle(m_configurations->currentConfiguration());
- m_settings->save();
-
- // update since not all MIME types are accepted (invalids or duplicates)
- m_mime->setText(m_settings->supportedMimeTypesAsString());
-}
-
-ClangFormatOptionsPage::ClangFormatOptionsPage(ClangFormatSettings *settings)
-{
- setId("ClangFormat");
- setDisplayName(Tr::tr("Clang Format"));
- setCategory(Constants::OPTION_CATEGORY);
- setWidgetCreator([settings] { return new ClangFormatOptionsPageWidget(settings); });
-}
-
-} // Beautifier::Internal
diff --git a/src/plugins/beautifier/clangformat/clangformatoptionspage.h b/src/plugins/beautifier/clangformat/clangformatoptionspage.h
deleted file mode 100644
index e9a845af26..0000000000
--- a/src/plugins/beautifier/clangformat/clangformatoptionspage.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (C) 2016 Lorenz Haas
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <coreplugin/dialogs/ioptionspage.h>
-
-namespace Beautifier::Internal {
-
-class ClangFormatSettings;
-
-class ClangFormatOptionsPage final : public Core::IOptionsPage
-{
-public:
- explicit ClangFormatOptionsPage(ClangFormatSettings *settings);
-};
-
-} // Beautifier::Internal
diff --git a/src/plugins/beautifier/clangformat/clangformatsettings.cpp b/src/plugins/beautifier/clangformat/clangformatsettings.cpp
index 16a2225cd9..6a31667edb 100644
--- a/src/plugins/beautifier/clangformat/clangformatsettings.cpp
+++ b/src/plugins/beautifier/clangformat/clangformatsettings.cpp
@@ -4,43 +4,75 @@
#include "clangformatsettings.h"
#include "../beautifierconstants.h"
+#include "../beautifierplugin.h"
#include "../beautifiertr.h"
+#include "../configurationpanel.h"
+
+#include <coreplugin/icore.h>
+
+#include <utils/layoutbuilder.h>
+#include <utils/pathchooser.h>
#include <QDateTime>
#include <QXmlStreamWriter>
+#include <QButtonGroup>
+#include <QComboBox>
+#include <QGroupBox>
+#include <QRadioButton>
-#include <coreplugin/icore.h>
+using namespace Utils;
namespace Beautifier::Internal {
-const char USE_PREDEFINED_STYLE[] = "usePredefinedStyle";
-const char PREDEFINED_STYLE[] = "predefinedStyle";
-const char FALLBACK_STYLE[] = "fallbackStyle";
-const char CUSTOM_STYLE[] = "customStyle";
const char SETTINGS_NAME[] = "clangformat";
-ClangFormatSettings::ClangFormatSettings() :
- AbstractSettings(SETTINGS_NAME, ".clang-format")
+ClangFormatSettings::ClangFormatSettings()
+ : AbstractSettings(SETTINGS_NAME, ".clang-format")
{
- setCommand("clang-format");
- m_settings.insert(USE_PREDEFINED_STYLE, QVariant(true));
- m_settings.insert(PREDEFINED_STYLE, "LLVM");
- m_settings.insert(FALLBACK_STYLE, "Default");
- m_settings.insert(CUSTOM_STYLE, QVariant());
- read();
-}
+ command.setDefaultValue("clang-format");
+ command.setPromptDialogTitle(BeautifierPlugin::msgCommandPromptDialogTitle("Clang Format"));
+ command.setLabelText(Tr::tr("Clang Format command:"));
-QString ClangFormatSettings::documentationFilePath() const
-{
- return (Core::ICore::userResourcePath() / Beautifier::Constants::SETTINGS_DIRNAME
- / Beautifier::Constants::DOCUMENTATION_DIRNAME / SETTINGS_NAME)
- .stringAppended(".xml")
- .toString();
+ usePredefinedStyle.setSettingsKey("usePredefinedStyle");
+ usePredefinedStyle.setLabelText(Tr::tr("Use predefined style:"));
+ usePredefinedStyle.setDefaultValue(true);
+
+ predefinedStyle.setSettingsKey("predefinedStyle");
+ predefinedStyle.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
+ predefinedStyle.addOption("LLVM");
+ predefinedStyle.addOption("Google");
+ predefinedStyle.addOption("Chromium");
+ predefinedStyle.addOption("Mozilla");
+ predefinedStyle.addOption("WebKit");
+ predefinedStyle.addOption("File");
+ predefinedStyle.setDefaultValue("LLVM");
+
+ fallbackStyle.setSettingsKey("fallbackStyle");
+ fallbackStyle.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
+ fallbackStyle.addOption("Default");
+ fallbackStyle.addOption("None");
+ fallbackStyle.addOption("LLVM");
+ fallbackStyle.addOption("Google");
+ fallbackStyle.addOption("Chromium");
+ fallbackStyle.addOption("Mozilla");
+ fallbackStyle.addOption("WebKit");
+ fallbackStyle.setDefaultValue("Default");
+
+ predefinedStyle.setSettingsKey("predefinedStyle");
+ predefinedStyle.setDefaultValue("LLVM");
+
+ customStyle.setSettingsKey("customStyle");
+
+ documentationFilePath = Core::ICore::userResourcePath(Constants::SETTINGS_DIRNAME)
+ .pathAppended(Constants::DOCUMENTATION_DIRNAME)
+ .pathAppended(SETTINGS_NAME).stringAppended(".xml");
+
+ read();
}
void ClangFormatSettings::createDocumentationFile() const
{
- QFile file(documentationFilePath());
+ QFile file(documentationFilePath.toFSPathString());
const QFileInfo fi(file);
if (!fi.exists())
fi.dir().mkpath(fi.absolutePath());
@@ -144,73 +176,101 @@ QStringList ClangFormatSettings::completerWords()
};
}
-bool ClangFormatSettings::usePredefinedStyle() const
+QString ClangFormatSettings::styleFileName(const QString &key) const
{
- return m_settings.value(USE_PREDEFINED_STYLE).toBool();
+ return m_styleDir.absolutePath() + '/' + key + '/' + m_ending;
}
-void ClangFormatSettings::setUsePredefinedStyle(bool usePredefinedStyle)
+void ClangFormatSettings::readStyles()
{
- m_settings.insert(USE_PREDEFINED_STYLE, QVariant(usePredefinedStyle));
+ const QStringList dirs = m_styleDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
+ for (const QString &dir : dirs) {
+ QFile file(m_styleDir.absoluteFilePath(dir + '/' + m_ending));
+ if (file.open(QIODevice::ReadOnly))
+ m_styles.insert(dir, QString::fromLocal8Bit(file.readAll()));
+ }
}
-QString ClangFormatSettings::predefinedStyle() const
+class ClangFormatOptionsPageWidget : public Core::IOptionsPageWidget
{
- return m_settings.value(PREDEFINED_STYLE).toString();
-}
+public:
+ explicit ClangFormatOptionsPageWidget(ClangFormatSettings *settings)
+ {
+ ClangFormatSettings &s = *settings;
+ QGroupBox *options = nullptr;
-void ClangFormatSettings::setPredefinedStyle(const QString &predefinedStyle)
-{
- const QStringList test = predefinedStyles();
- if (test.contains(predefinedStyle))
- m_settings.insert(PREDEFINED_STYLE, QVariant(predefinedStyle));
-}
+ auto predefinedStyleButton = new QRadioButton;
+ s.usePredefinedStyle.adoptButton(predefinedStyleButton);
-QString ClangFormatSettings::fallbackStyle() const
-{
- return m_settings.value(FALLBACK_STYLE).toString();
-}
+ auto customizedStyleButton = new QRadioButton(Tr::tr("Use customized style:"));
-void ClangFormatSettings::setFallbackStyle(const QString &fallbackStyle)
-{
- const QStringList test = fallbackStyles();
- if (test.contains(fallbackStyle))
- m_settings.insert(FALLBACK_STYLE, QVariant(fallbackStyle));
-}
+ auto styleButtonGroup = new QButtonGroup;
+ styleButtonGroup->addButton(predefinedStyleButton);
+ styleButtonGroup->addButton(customizedStyleButton);
-QString ClangFormatSettings::customStyle() const
-{
- return m_settings.value(CUSTOM_STYLE).toString();
-}
+ auto configurations = new ConfigurationPanel(this);
+ configurations->setSettings(&s);
+ configurations->setCurrentConfiguration(s.customStyle());
-void ClangFormatSettings::setCustomStyle(const QString &customStyle)
-{
- m_settings.insert(CUSTOM_STYLE, QVariant(customStyle));
-}
+ using namespace Layouting;
-QStringList ClangFormatSettings::predefinedStyles() const
-{
- return {"LLVM", "Google", "Chromium", "Mozilla", "WebKit", "File"};
-}
+ auto fallbackBlob = Row { noMargin, Tr::tr("Fallback style:"), s.fallbackStyle }.emerge();
-QStringList ClangFormatSettings::fallbackStyles() const
-{
- return {"Default", "None", "LLVM", "Google", "Chromium", "Mozilla", "WebKit"};
-}
+ auto predefinedBlob = Column { noMargin, s.predefinedStyle, fallbackBlob }.emerge();
-QString ClangFormatSettings::styleFileName(const QString &key) const
-{
- return m_styleDir.absolutePath() + '/' + key + '/' + m_ending;
-}
+ Column {
+ Group {
+ title(Tr::tr("Configuration")),
+ Form {
+ s.command, br,
+ s.supportedMimeTypes
+ }
+ },
+ Group {
+ title(Tr::tr("Options")),
+ bindTo(&options),
+ Form {
+ s.usePredefinedStyle, predefinedBlob, br,
+ customizedStyleButton, configurations,
+ },
+ },
+ st
+ }.attachTo(this);
-void ClangFormatSettings::readStyles()
-{
- const QStringList dirs = m_styleDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
- for (const QString &dir : dirs) {
- QFile file(m_styleDir.absoluteFilePath(dir + '/' + m_ending));
- if (file.open(QIODevice::ReadOnly))
- m_styles.insert(dir, QString::fromLocal8Bit(file.readAll()));
+ if (s.usePredefinedStyle.value())
+ predefinedStyleButton->click();
+ else
+ customizedStyleButton->click();
+
+ const auto updateEnabled = [&s, styleButtonGroup, predefinedBlob, fallbackBlob,
+ configurations, predefinedStyleButton] {
+ const bool predefSelected = styleButtonGroup->checkedButton() == predefinedStyleButton;
+ predefinedBlob->setEnabled(predefSelected);
+ fallbackBlob->setEnabled(predefSelected && s.predefinedStyle.volatileValue().toInt() == 5); // File
+ configurations->setEnabled(!predefSelected);
+ };
+ updateEnabled();
+ connect(styleButtonGroup, &QButtonGroup::buttonClicked, this, updateEnabled);
+ connect(&s.predefinedStyle, &SelectionAspect::volatileValueChanged, this, updateEnabled);
+
+ setOnApply([settings, configurations] {
+ settings->customStyle.setValue(configurations->currentConfiguration());
+ settings->save();
+ });
+
+ s.read();
+
+ connect(s.command.pathChooser(), &PathChooser::validChanged, options, &QWidget::setEnabled);
+ options->setEnabled(s.command.pathChooser()->isValid());
}
+};
+
+ClangFormatOptionsPage::ClangFormatOptionsPage(ClangFormatSettings *settings)
+{
+ setId("ClangFormat");
+ setDisplayName(Tr::tr("Clang Format"));
+ setCategory(Constants::OPTION_CATEGORY);
+ setWidgetCreator([settings] { return new ClangFormatOptionsPageWidget(settings); });
}
} // Beautifier::Internal
diff --git a/src/plugins/beautifier/clangformat/clangformatsettings.h b/src/plugins/beautifier/clangformat/clangformatsettings.h
index 310eeaf892..64f2b4b9bb 100644
--- a/src/plugins/beautifier/clangformat/clangformatsettings.h
+++ b/src/plugins/beautifier/clangformat/clangformatsettings.h
@@ -12,24 +12,14 @@ class ClangFormatSettings : public AbstractSettings
public:
explicit ClangFormatSettings();
- QString documentationFilePath() const override;
void createDocumentationFile() const override;
- QStringList completerWords() override;
-
- bool usePredefinedStyle() const;
- void setUsePredefinedStyle(bool usePredefinedStyle);
-
- QString predefinedStyle() const;
- void setPredefinedStyle(const QString &predefinedStyle);
- QString fallbackStyle() const;
- void setFallbackStyle(const QString &fallbackStyle);
-
- QString customStyle() const;
- void setCustomStyle(const QString &customStyle);
+ QStringList completerWords() override;
- QStringList predefinedStyles() const;
- QStringList fallbackStyles() const;
+ Utils::BoolAspect usePredefinedStyle{this};
+ Utils::SelectionAspect predefinedStyle{this};
+ Utils::SelectionAspect fallbackStyle{this};
+ Utils::StringAspect customStyle{this};
QString styleFileName(const QString &key) const override;
@@ -37,4 +27,10 @@ private:
void readStyles() override;
};
+class ClangFormatOptionsPage final : public Core::IOptionsPage
+{
+public:
+ explicit ClangFormatOptionsPage(ClangFormatSettings *settings);
+};
+
} // Beautifier::Internal
diff --git a/src/plugins/beautifier/configurationdialog.cpp b/src/plugins/beautifier/configurationdialog.cpp
index 8b2c61e720..09e8c595f3 100644
--- a/src/plugins/beautifier/configurationdialog.cpp
+++ b/src/plugins/beautifier/configurationdialog.cpp
@@ -40,7 +40,7 @@ ConfigurationDialog::ConfigurationDialog(QWidget *parent)
m_buttonBox->setOrientation(Qt::Horizontal);
m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
diff --git a/src/plugins/beautifier/configurationpanel.cpp b/src/plugins/beautifier/configurationpanel.cpp
index 6aa0f5b2d9..fa0fca7139 100644
--- a/src/plugins/beautifier/configurationpanel.cpp
+++ b/src/plugins/beautifier/configurationpanel.cpp
@@ -17,8 +17,6 @@ namespace Beautifier::Internal {
ConfigurationPanel::ConfigurationPanel(QWidget *parent)
: QWidget(parent)
{
- resize(595, 23);
-
m_configurations = new QComboBox;
m_configurations->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
@@ -26,14 +24,15 @@ ConfigurationPanel::ConfigurationPanel(QWidget *parent)
m_remove = new QPushButton(Tr::tr("Remove"));
auto add = new QPushButton(Tr::tr("Add"));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Row {
m_configurations,
m_edit,
m_remove,
- add
- }.attachTo(this, WithoutMargins);
+ add,
+ noMargin,
+ }.attachTo(this);
connect(add, &QPushButton::clicked, this, &ConfigurationPanel::add);
connect(m_edit, &QPushButton::clicked, this, &ConfigurationPanel::edit);
diff --git a/src/plugins/beautifier/generaloptionspage.cpp b/src/plugins/beautifier/generaloptionspage.cpp
deleted file mode 100644
index 859f323e24..0000000000
--- a/src/plugins/beautifier/generaloptionspage.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright (C) 2016 Lorenz Haas
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "generaloptionspage.h"
-
-#include "beautifierconstants.h"
-#include "beautifiertr.h"
-#include "generalsettings.h"
-
-#include <utils/layoutbuilder.h>
-
-#include <QApplication>
-#include <QCheckBox>
-#include <QComboBox>
-#include <QLabel>
-#include <QLineEdit>
-
-namespace Beautifier::Internal {
-
-class GeneralOptionsPageWidget : public Core::IOptionsPageWidget
-{
-public:
- explicit GeneralOptionsPageWidget(const QStringList &toolIds);
-
-private:
- void apply() final;
-
- QCheckBox *m_autoFormat;
- QComboBox *m_autoFormatTool;
- QLineEdit *m_autoFormatMime;
- QCheckBox *m_autoFormatOnlyCurrentProject;
-};
-
-GeneralOptionsPageWidget::GeneralOptionsPageWidget(const QStringList &toolIds)
-{
- resize(817, 631);
-
- auto settings = GeneralSettings::instance();
-
- m_autoFormat = new QCheckBox(Tr::tr("Enable auto format on file save"));
- m_autoFormat->setChecked(settings->autoFormatOnSave());
-
- auto toolLabel = new QLabel(Tr::tr("Tool:"));
- toolLabel->setEnabled(false);
-
- auto mimeLabel = new QLabel(Tr::tr("Restrict to MIME types:"));
- mimeLabel->setEnabled(false);
-
- m_autoFormatMime = new QLineEdit(settings->autoFormatMimeAsString());
- m_autoFormatMime->setEnabled(false);
-
- m_autoFormatOnlyCurrentProject =
- new QCheckBox(Tr::tr("Restrict to files contained in the current project"));
- m_autoFormatOnlyCurrentProject->setEnabled(false);
- m_autoFormatOnlyCurrentProject->setChecked(settings->autoFormatOnlyCurrentProject());
-
- m_autoFormatTool = new QComboBox;
- m_autoFormatTool->setEnabled(false);
- m_autoFormatTool->addItems(toolIds);
- const int index = m_autoFormatTool->findText(settings->autoFormatTool());
- m_autoFormatTool->setCurrentIndex(qMax(index, 0));
-
- using namespace Utils::Layouting;
-
- Column {
- Group {
- title(Tr::tr("Automatic Formatting on File Save")),
- Form {
- Span(2, m_autoFormat), br,
- toolLabel, m_autoFormatTool, br,
- mimeLabel, m_autoFormatMime, br,
- Span(2, m_autoFormatOnlyCurrentProject)
- }
- },
- st
- }.attachTo(this);
-
- connect(m_autoFormat, &QCheckBox::toggled, m_autoFormatTool, &QComboBox::setEnabled);
- connect(m_autoFormat, &QCheckBox::toggled, m_autoFormatMime, &QLineEdit::setEnabled);
- connect(m_autoFormat, &QCheckBox::toggled, toolLabel, &QLabel::setEnabled);
- connect(m_autoFormat, &QCheckBox::toggled, mimeLabel, &QLabel::setEnabled);
- connect(m_autoFormat, &QCheckBox::toggled, m_autoFormatOnlyCurrentProject, &QCheckBox::setEnabled);
-}
-
-void GeneralOptionsPageWidget::apply()
-{
- auto settings = GeneralSettings::instance();
- settings->setAutoFormatOnSave(m_autoFormat->isChecked());
- settings->setAutoFormatTool(m_autoFormatTool->currentText());
- settings->setAutoFormatMime(m_autoFormatMime->text());
- settings->setAutoFormatOnlyCurrentProject(m_autoFormatOnlyCurrentProject->isChecked());
- settings->save();
-}
-
-GeneralOptionsPage::GeneralOptionsPage(const QStringList &toolIds)
-{
- setId(Constants::OPTION_GENERAL_ID);
- setDisplayName(Tr::tr("General"));
- setCategory(Constants::OPTION_CATEGORY);
- setDisplayCategory(Tr::tr("Beautifier"));
- setWidgetCreator([toolIds] { return new GeneralOptionsPageWidget(toolIds); });
- setCategoryIconPath(":/beautifier/images/settingscategory_beautifier.png");
-}
-
-} // Beautifier::Internal
diff --git a/src/plugins/beautifier/generaloptionspage.h b/src/plugins/beautifier/generaloptionspage.h
deleted file mode 100644
index 885a27cb19..0000000000
--- a/src/plugins/beautifier/generaloptionspage.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (C) 2016 Lorenz Haas
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <coreplugin/dialogs/ioptionspage.h>
-
-namespace Beautifier::Internal {
-
-class GeneralOptionsPage final : public Core::IOptionsPage
-{
-public:
- explicit GeneralOptionsPage(const QStringList &toolIds);
-};
-
-} // Beautifier::Internal
diff --git a/src/plugins/beautifier/generalsettings.cpp b/src/plugins/beautifier/generalsettings.cpp
index 218f6963f3..2f7dc4c3a0 100644
--- a/src/plugins/beautifier/generalsettings.cpp
+++ b/src/plugins/beautifier/generalsettings.cpp
@@ -3,114 +3,73 @@
#include "generalsettings.h"
-#include <coreplugin/icore.h>
+#include "beautifierconstants.h"
+#include "beautifiertr.h"
#include <utils/algorithm.h>
#include <utils/genericconstants.h>
-#include <utils/mimeutils.h>
+#include <utils/layoutbuilder.h>
-namespace Beautifier::Internal {
-
-const char AUTO_FORMAT_TOOL[] = "autoFormatTool";
-const char AUTO_FORMAT_MIME[] = "autoFormatMime";
-const char AUTO_FORMAT_ONLY_CURRENT_PROJECT[] = "autoFormatOnlyCurrentProject";
+using namespace Utils;
-static GeneralSettings *m_instance;
+namespace Beautifier::Internal {
GeneralSettings::GeneralSettings()
{
- m_instance = this;
- read();
-}
-
-GeneralSettings *GeneralSettings::instance()
-{
- return m_instance;
-}
-
-void GeneralSettings::read()
-{
- QSettings *s = Core::ICore::settings();
- s->beginGroup(Utils::Constants::BEAUTIFIER_SETTINGS_GROUP);
- s->beginGroup(Utils::Constants::BEAUTIFIER_GENERAL_GROUP);
- m_autoFormatOnSave = s->value(Utils::Constants::BEAUTIFIER_AUTO_FORMAT_ON_SAVE, false).toBool();
- m_autoFormatTool = s->value(AUTO_FORMAT_TOOL, QString()).toString();
- setAutoFormatMime(s->value(AUTO_FORMAT_MIME, "text/x-c++src;text/x-c++hdr").toString());
- m_autoFormatOnlyCurrentProject = s->value(AUTO_FORMAT_ONLY_CURRENT_PROJECT, true).toBool();
- s->endGroup();
- s->endGroup();
-}
-
-void GeneralSettings::save()
-{
- QSettings *s = Core::ICore::settings();
- s->beginGroup(Utils::Constants::BEAUTIFIER_SETTINGS_GROUP);
- s->beginGroup(Utils::Constants::BEAUTIFIER_GENERAL_GROUP);
- s->setValue(Utils::Constants::BEAUTIFIER_AUTO_FORMAT_ON_SAVE, m_autoFormatOnSave);
- s->setValue(AUTO_FORMAT_TOOL, m_autoFormatTool);
- s->setValue(AUTO_FORMAT_MIME, autoFormatMimeAsString());
- s->setValue(AUTO_FORMAT_ONLY_CURRENT_PROJECT, m_autoFormatOnlyCurrentProject);
- s->endGroup();
- s->endGroup();
-}
-
-bool GeneralSettings::autoFormatOnSave() const
-{
- return m_autoFormatOnSave;
-}
-
-void GeneralSettings::setAutoFormatOnSave(bool autoFormatOnSave)
-{
- m_autoFormatOnSave = autoFormatOnSave;
-}
-
-QString GeneralSettings::autoFormatTool() const
-{
- return m_autoFormatTool;
-}
-
-void GeneralSettings::setAutoFormatTool(const QString &autoFormatTool)
-{
- m_autoFormatTool = autoFormatTool;
+ setId(Constants::OPTION_GENERAL_ID);
+ setDisplayName(Tr::tr("General"));
+ setCategory(Constants::OPTION_CATEGORY);
+ setDisplayCategory(Tr::tr("Beautifier"));
+ setCategoryIconPath(":/beautifier/images/settingscategory_beautifier.png");
+ setSettingsGroups("Beautifier", "General");
+
+ autoFormatOnSave.setSettingsKey(Utils::Constants::BEAUTIFIER_AUTO_FORMAT_ON_SAVE);
+ autoFormatOnSave.setDefaultValue(false);
+ autoFormatOnSave.setLabelText(Tr::tr("Enable auto format on file save"));
+
+ autoFormatOnlyCurrentProject.setSettingsKey("autoFormatOnlyCurrentProject");
+ autoFormatOnlyCurrentProject.setDefaultValue(true);
+ autoFormatOnlyCurrentProject.setLabelText(Tr::tr("Restrict to files contained in the current project"));
+
+ autoFormatTools.setSettingsKey("autoFormatTool");
+ autoFormatTools.setLabelText(Tr::tr("Tool:"));
+ autoFormatTools.setDefaultValue(0);
+ autoFormatTools.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
+
+ autoFormatMime.setSettingsKey("autoFormatMime");
+ autoFormatMime.setDefaultValue("text/x-c++src;text/x-c++hdr");
+ autoFormatMime.setLabelText(Tr::tr("Restrict to MIME types:"));
+ autoFormatMime.setDisplayStyle(StringAspect::LineEditDisplay);
+
+ setLayouter([this] {
+ using namespace Layouting;
+ return Column {
+ Group {
+ title(Tr::tr("Automatic Formatting on File Save")),
+ autoFormatOnSave.groupChecker(),
+ Form {
+ autoFormatTools, br,
+ autoFormatMime, br,
+ Span(2, autoFormatOnlyCurrentProject)
+ }
+ },
+ st
+ };
+ });
}
-QList<Utils::MimeType> GeneralSettings::autoFormatMime() const
+QList<MimeType> GeneralSettings::allowedMimeTypes() const
{
- return m_autoFormatMime;
-}
+ const QStringList stringTypes = autoFormatMime.value().split(';');
-QString GeneralSettings::autoFormatMimeAsString() const
-{
- return Utils::transform(m_autoFormatMime, &Utils::MimeType::name).join("; ");
-}
-
-void GeneralSettings::setAutoFormatMime(const QList<Utils::MimeType> &autoFormatMime)
-{
- m_autoFormatMime = autoFormatMime;
-}
-
-void GeneralSettings::setAutoFormatMime(const QString &mimeList)
-{
- const QStringList stringTypes = mimeList.split(';');
- QList<Utils::MimeType> types;
- types.reserve(stringTypes.count());
+ QList<MimeType> types;
for (QString t : stringTypes) {
t = t.trimmed();
- const Utils::MimeType mime = Utils::mimeTypeForName(t);
+ const MimeType mime = Utils::mimeTypeForName(t);
if (mime.isValid())
types << mime;
}
- setAutoFormatMime(types);
-}
-
-bool GeneralSettings::autoFormatOnlyCurrentProject() const
-{
- return m_autoFormatOnlyCurrentProject;
-}
-
-void GeneralSettings::setAutoFormatOnlyCurrentProject(bool autoFormatOnlyCurrentProject)
-{
- m_autoFormatOnlyCurrentProject = autoFormatOnlyCurrentProject;
+ return types;
}
} // Beautifier::Internal
diff --git a/src/plugins/beautifier/generalsettings.h b/src/plugins/beautifier/generalsettings.h
index b381dacb26..0be68b98e3 100644
--- a/src/plugins/beautifier/generalsettings.h
+++ b/src/plugins/beautifier/generalsettings.h
@@ -5,38 +5,21 @@
#include <utils/mimeutils.h>
-#include <QList>
+#include <coreplugin/dialogs/ioptionspage.h>
namespace Beautifier::Internal {
-class GeneralSettings
+class GeneralSettings : public Core::PagedSettings
{
public:
- explicit GeneralSettings();
- static GeneralSettings *instance();
+ GeneralSettings();
- void read();
- void save();
+ QList<Utils::MimeType> allowedMimeTypes() const;
- bool autoFormatOnSave() const;
- void setAutoFormatOnSave(bool autoFormatOnSave);
-
- QString autoFormatTool() const;
- void setAutoFormatTool(const QString &autoFormatTool);
-
- QList<Utils::MimeType> autoFormatMime() const;
- QString autoFormatMimeAsString() const;
- void setAutoFormatMime(const QList<Utils::MimeType> &autoFormatMime);
- void setAutoFormatMime(const QString &mimeList);
-
- bool autoFormatOnlyCurrentProject() const;
- void setAutoFormatOnlyCurrentProject(bool autoFormatOnlyCurrentProject);
-
-private:
- bool m_autoFormatOnSave = false;
- bool m_autoFormatOnlyCurrentProject = true;
- QString m_autoFormatTool;
- QList<Utils::MimeType> m_autoFormatMime;
+ Utils::BoolAspect autoFormatOnSave{this};
+ Utils::BoolAspect autoFormatOnlyCurrentProject{this};
+ Utils::SelectionAspect autoFormatTools{this};
+ Utils::StringAspect autoFormatMime{this};
};
} // Beautifier::Internal
diff --git a/src/plugins/beautifier/uncrustify/uncrustify.cpp b/src/plugins/beautifier/uncrustify/uncrustify.cpp
index 2e6f6d67d5..1439dc315f 100644
--- a/src/plugins/beautifier/uncrustify/uncrustify.cpp
+++ b/src/plugins/beautifier/uncrustify/uncrustify.cpp
@@ -54,7 +54,7 @@ Uncrustify::Uncrustify()
Core::ActionManager::actionContainer(Constants::MENU_ID)->addMenu(menu);
- connect(&m_settings, &UncrustifySettings::supportedMimeTypesChanged,
+ connect(&m_settings.supportedMimeTypes, &Utils::BaseAspect::changed,
this, [this] { updateActions(Core::EditorManager::currentEditor()); });
}
diff --git a/src/plugins/beautifier/uncrustify/uncrustify.h b/src/plugins/beautifier/uncrustify/uncrustify.h
index 1261576274..685a29c25a 100644
--- a/src/plugins/beautifier/uncrustify/uncrustify.h
+++ b/src/plugins/beautifier/uncrustify/uncrustify.h
@@ -5,7 +5,6 @@
#include "../beautifierabstracttool.h"
-#include "uncrustifyoptionspage.h"
#include "uncrustifysettings.h"
namespace Beautifier::Internal {
diff --git a/src/plugins/beautifier/uncrustify/uncrustifyoptionspage.cpp b/src/plugins/beautifier/uncrustify/uncrustifyoptionspage.cpp
deleted file mode 100644
index 2102dd311f..0000000000
--- a/src/plugins/beautifier/uncrustify/uncrustifyoptionspage.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright (C) 2016 Lorenz Haas
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "uncrustifyoptionspage.h"
-
-#include "uncrustifyconstants.h"
-#include "uncrustifysettings.h"
-
-#include "../beautifierconstants.h"
-#include "../beautifierplugin.h"
-#include "../beautifiertr.h"
-#include "../configurationpanel.h"
-
-#include <utils/layoutbuilder.h>
-#include <utils/pathchooser.h>
-
-#include <QCheckBox>
-#include <QGroupBox>
-#include <QLabel>
-#include <QLineEdit>
-
-namespace Beautifier::Internal {
-
-class UncrustifyOptionsPageWidget : public Core::IOptionsPageWidget
-{
-public:
- explicit UncrustifyOptionsPageWidget(UncrustifySettings *settings);
-
- void apply() final;
-
-private:
- UncrustifySettings *m_settings;
-
- Utils::PathChooser *m_command;
- QLineEdit *m_mime;
- QCheckBox *m_useOtherFiles;
- QCheckBox *m_useSpecificFile;
- Utils::PathChooser *m_uncrusifyFilePath;
- QCheckBox *m_useHomeFile;
- QCheckBox *m_useCustomStyle;
- ConfigurationPanel *m_configurations;
- QCheckBox *m_formatEntireFileFallback;
-};
-
-UncrustifyOptionsPageWidget::UncrustifyOptionsPageWidget(UncrustifySettings *settings)
- : m_settings(settings)
-{
- resize(817, 631);
-
- m_command = new Utils::PathChooser;
-
- m_mime = new QLineEdit(m_settings->supportedMimeTypesAsString());
-
- m_useOtherFiles = new QCheckBox(Tr::tr("Use file uncrustify.cfg defined in project files"));
- m_useOtherFiles->setChecked(m_settings->useOtherFiles());
-
- m_useSpecificFile = new QCheckBox(Tr::tr("Use file specific uncrustify.cfg"));
- m_useSpecificFile->setChecked(m_settings->useSpecificConfigFile());
-
- m_uncrusifyFilePath = new Utils::PathChooser;
- m_uncrusifyFilePath->setExpectedKind(Utils::PathChooser::File);
- m_uncrusifyFilePath->setPromptDialogFilter(Tr::tr("Uncrustify file (*.cfg)"));
- m_uncrusifyFilePath->setFilePath(m_settings->specificConfigFile());
-
- m_useHomeFile = new QCheckBox(Tr::tr("Use file uncrustify.cfg in HOME")
- .replace( "HOME", QDir::toNativeSeparators(QDir::home().absolutePath())));
- m_useHomeFile->setChecked(m_settings->useHomeFile());
-
- m_useCustomStyle = new QCheckBox(Tr::tr("Use customized style:"));
- m_useCustomStyle->setChecked(m_settings->useCustomStyle());
-
- m_configurations = new ConfigurationPanel;
- m_configurations->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
- m_configurations->setSettings(m_settings);
- m_configurations->setCurrentConfiguration(m_settings->customStyle());
-
- m_formatEntireFileFallback = new QCheckBox(Tr::tr("Format entire file if no text was selected"));
- m_formatEntireFileFallback->setToolTip(Tr::tr("For action Format Selected Text"));
- m_formatEntireFileFallback->setChecked(m_settings->formatEntireFileFallback());
-
- m_command->setExpectedKind(Utils::PathChooser::ExistingCommand);
- m_command->setCommandVersionArguments({"--version"});
- m_command->setPromptDialogTitle(BeautifierPlugin::msgCommandPromptDialogTitle(
- Tr::tr(Constants::UNCRUSTIFY_DISPLAY_NAME)));
- m_command->setFilePath(m_settings->command());
-
- auto options = new QGroupBox(Tr::tr("Options"));
-
- using namespace Utils::Layouting;
-
- Column {
- m_useOtherFiles,
- Row { m_useSpecificFile, m_uncrusifyFilePath },
- m_useHomeFile,
- Row { m_useCustomStyle, m_configurations },
- m_formatEntireFileFallback
- }.attachTo(options);
-
- Column {
- Group {
- title(Tr::tr("Configuration")),
- Form {
- Tr::tr("Uncrustify command:"), m_command, br,
- Tr::tr("Restrict to MIME types:"), m_mime
- }
- },
- options,
- st
- }.attachTo(this);
-
- connect(m_command, &Utils::PathChooser::validChanged, options, &QWidget::setEnabled);
-}
-
-void UncrustifyOptionsPageWidget::apply()
-{
- m_settings->setCommand(m_command->filePath());
- m_settings->setSupportedMimeTypes(m_mime->text());
- m_settings->setUseOtherFiles(m_useOtherFiles->isChecked());
- m_settings->setUseHomeFile(m_useHomeFile->isChecked());
- m_settings->setUseSpecificConfigFile(m_useSpecificFile->isChecked());
- m_settings->setSpecificConfigFile(m_uncrusifyFilePath->filePath());
- m_settings->setUseCustomStyle(m_useCustomStyle->isChecked());
- m_settings->setCustomStyle(m_configurations->currentConfiguration());
- m_settings->setFormatEntireFileFallback(m_formatEntireFileFallback->isChecked());
- m_settings->save();
-
- // update since not all MIME types are accepted (invalids or duplicates)
- m_mime->setText(m_settings->supportedMimeTypesAsString());
-}
-
-UncrustifyOptionsPage::UncrustifyOptionsPage(UncrustifySettings *settings)
-{
- setId("Uncrustify");
- setDisplayName(Tr::tr("Uncrustify"));
- setCategory(Constants::OPTION_CATEGORY);
- setWidgetCreator([settings] { return new UncrustifyOptionsPageWidget(settings); });
-}
-
-} // Beautifier::Internal
diff --git a/src/plugins/beautifier/uncrustify/uncrustifyoptionspage.h b/src/plugins/beautifier/uncrustify/uncrustifyoptionspage.h
deleted file mode 100644
index a8512d0da6..0000000000
--- a/src/plugins/beautifier/uncrustify/uncrustifyoptionspage.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (C) 2016 Lorenz Haas
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <coreplugin/dialogs/ioptionspage.h>
-
-namespace Beautifier::Internal {
-
-class UncrustifySettings;
-
-class UncrustifyOptionsPage final : public Core::IOptionsPage
-{
-public:
- explicit UncrustifyOptionsPage(UncrustifySettings *settings);
-};
-
-} // Beautifier::Internal
diff --git a/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp b/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp
index e827a8f791..1aa9e8502c 100644
--- a/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp
+++ b/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp
@@ -3,14 +3,25 @@
#include "uncrustifysettings.h"
+#include "uncrustifyconstants.h"
#include "../beautifierconstants.h"
+#include "../beautifierplugin.h"
+#include "../beautifiertr.h"
+#include "../configurationpanel.h"
#include <coreplugin/icore.h>
-#include <utils/qtcprocess.h>
+#include <utils/layoutbuilder.h>
+#include <utils/pathchooser.h>
+#include <utils/process.h>
+
+#include <QCheckBox>
#include <QDateTime>
#include <QFile>
#include <QFileInfo>
+#include <QGroupBox>
+#include <QLabel>
+#include <QLineEdit>
#include <QRegularExpression>
#include <QXmlStreamWriter>
@@ -18,120 +29,60 @@ using namespace Utils;
namespace Beautifier::Internal {
-const char USE_OTHER_FILES[] = "useOtherFiles";
-const char USE_HOME_FILE[] = "useHomeFile";
-const char USE_SPECIFIC_CONFIG_FILE_PATH[] = "useSpecificConfigFile";
-const char SPECIFIC_CONFIG_FILE_PATH[] = "specificConfigFile";
-const char USE_CUSTOM_STYLE[] = "useCustomStyle";
-const char CUSTOM_STYLE[] = "customStyle";
-const char FORMAT_ENTIRE_FILE_FALLBACK[] = "formatEntireFileFallback";
-const char SETTINGS_NAME[] = "uncrustify";
-
-UncrustifySettings::UncrustifySettings() :
- AbstractSettings(SETTINGS_NAME, ".cfg")
+const char SETTINGS_NAME[] = "uncrustify";
+
+UncrustifySettings::UncrustifySettings()
+ : AbstractSettings(SETTINGS_NAME, ".cfg")
{
setVersionRegExp(QRegularExpression("([0-9]{1})\\.([0-9]{2})"));
- setCommand("uncrustify");
- m_settings.insert(USE_OTHER_FILES, QVariant(true));
- m_settings.insert(USE_HOME_FILE, QVariant(false));
- m_settings.insert(USE_CUSTOM_STYLE, QVariant(false));
- m_settings.insert(USE_SPECIFIC_CONFIG_FILE_PATH, QVariant(false));
- m_settings.insert(CUSTOM_STYLE, QVariant());
- m_settings.insert(FORMAT_ENTIRE_FILE_FALLBACK, QVariant(true));
- m_settings.insert(SPECIFIC_CONFIG_FILE_PATH, QVariant());
- read();
-}
-UncrustifySettings::~UncrustifySettings() = default;
+ command.setDefaultValue("uncrustify");
+ command.setLabelText(Tr::tr("Uncrustify command:"));
+ command.setPromptDialogTitle(BeautifierPlugin::msgCommandPromptDialogTitle(
+ Tr::tr(Constants::UNCRUSTIFY_DISPLAY_NAME)));
-bool UncrustifySettings::useOtherFiles() const
-{
- return m_settings.value(USE_OTHER_FILES).toBool();
-}
+ useOtherFiles.setSettingsKey("useOtherFiles");
+ useOtherFiles.setDefaultValue(true);
+ useOtherFiles.setLabelText(Tr::tr("Use file uncrustify.cfg defined in project files"));
-void UncrustifySettings::setUseOtherFiles(bool useOtherFiles)
-{
- m_settings.insert(USE_OTHER_FILES, QVariant(useOtherFiles));
-}
+ useHomeFile.setSettingsKey("useHomeFile");
+ useHomeFile.setLabelText(Tr::tr("Use file uncrustify.cfg in HOME")
+ .replace( "HOME", QDir::toNativeSeparators(QDir::home().absolutePath())));
-bool UncrustifySettings::useHomeFile() const
-{
- return m_settings.value(USE_HOME_FILE).toBool();
-}
+ useCustomStyle.setSettingsKey("useCustomStyle");
+ useCustomStyle.setLabelText(Tr::tr("Use customized style:"));
-void UncrustifySettings::setUseHomeFile(bool useHomeFile)
-{
- m_settings.insert(USE_HOME_FILE, QVariant(useHomeFile));
-}
+ useSpecificConfigFile.setSettingsKey("useSpecificConfigFile");
+ useSpecificConfigFile.setLabelText(Tr::tr("Use file specific uncrustify.cfg"));
-FilePath UncrustifySettings::specificConfigFile() const
-{
- return FilePath::fromString(m_settings.value(SPECIFIC_CONFIG_FILE_PATH).toString());
-}
+ customStyle.setSettingsKey("customStyle");
-void UncrustifySettings::setSpecificConfigFile(const FilePath &filePath)
-{
- m_settings.insert(SPECIFIC_CONFIG_FILE_PATH, QVariant(filePath.toString()));
-}
+ formatEntireFileFallback.setSettingsKey("formatEntireFileFallback");
+ formatEntireFileFallback.setDefaultValue(true);
+ formatEntireFileFallback.setLabelText(Tr::tr("Format entire file if no text was selected"));
+ formatEntireFileFallback.setToolTip(Tr::tr("For action Format Selected Text"));
-bool UncrustifySettings::useSpecificConfigFile() const
-{
- return m_settings.value(USE_SPECIFIC_CONFIG_FILE_PATH).toBool();
-}
+ specificConfigFile.setSettingsKey("specificConfigFile");
+ specificConfigFile.setExpectedKind(Utils::PathChooser::File);
+ specificConfigFile.setPromptDialogFilter(Tr::tr("Uncrustify file (*.cfg)"));
-void UncrustifySettings::setUseSpecificConfigFile(bool useConfigFile)
-{
- m_settings.insert(USE_SPECIFIC_CONFIG_FILE_PATH, QVariant(useConfigFile));
-}
-
-bool UncrustifySettings::useCustomStyle() const
-{
- return m_settings.value(USE_CUSTOM_STYLE).toBool();
-}
+ documentationFilePath = Core::ICore::userResourcePath(Constants::SETTINGS_DIRNAME)
+ .pathAppended(Constants::DOCUMENTATION_DIRNAME)
+ .pathAppended(SETTINGS_NAME).stringAppended(".xml");
-void UncrustifySettings::setUseCustomStyle(bool useCustomStyle)
-{
- m_settings.insert(USE_CUSTOM_STYLE, QVariant(useCustomStyle));
-}
-
-QString UncrustifySettings::customStyle() const
-{
- return m_settings.value(CUSTOM_STYLE).toString();
-}
-
-void UncrustifySettings::setCustomStyle(const QString &customStyle)
-{
- m_settings.insert(CUSTOM_STYLE, QVariant(customStyle));
-}
-
-bool UncrustifySettings::formatEntireFileFallback() const
-{
- return m_settings.value(FORMAT_ENTIRE_FILE_FALLBACK).toBool();
-}
-
-void UncrustifySettings::setFormatEntireFileFallback(bool formatEntireFileFallback)
-{
- m_settings.insert(FORMAT_ENTIRE_FILE_FALLBACK, QVariant(formatEntireFileFallback));
-}
-
-QString UncrustifySettings::documentationFilePath() const
-{
- return (Core::ICore::userResourcePath() / Beautifier::Constants::SETTINGS_DIRNAME
- / Beautifier::Constants::DOCUMENTATION_DIRNAME / SETTINGS_NAME)
- .stringAppended(".xml")
- .toString();
+ read();
}
void UncrustifySettings::createDocumentationFile() const
{
- QtcProcess process;
+ Process process;
process.setTimeoutS(2);
process.setCommand({command(), {"--show-config"}});
process.runBlocking();
if (process.result() != ProcessResult::FinishedWithSuccess)
return;
- QFile file(documentationFilePath());
+ QFile file(documentationFilePath.toFSPathString());
const QFileInfo fi(file);
if (!fi.exists())
fi.dir().mkpath(fi.absolutePath());
@@ -185,4 +136,62 @@ void UncrustifySettings::createDocumentationFile() const
}
}
+class UncrustifyOptionsPageWidget : public Core::IOptionsPageWidget
+{
+public:
+ explicit UncrustifyOptionsPageWidget(UncrustifySettings *settings)
+ {
+ UncrustifySettings &s = *settings;
+
+ auto configurations = new ConfigurationPanel(this);
+ configurations->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+ configurations->setSettings(settings);
+ configurations->setCurrentConfiguration(settings->customStyle());
+
+ QGroupBox *options = nullptr;
+
+ using namespace Layouting;
+
+ Column {
+ Group {
+ title(Tr::tr("Configuration")),
+ Form {
+ s.command, br,
+ s.supportedMimeTypes,
+ }
+ },
+ Group {
+ title(Tr::tr("Options")),
+ bindTo(&options),
+ Column {
+ s.useOtherFiles,
+ Row { s.useSpecificConfigFile, s.specificConfigFile },
+ s.useHomeFile,
+ Row { s.useCustomStyle, configurations },
+ s.formatEntireFileFallback
+ },
+ },
+ st
+ }.attachTo(this);
+
+ s.read();
+
+ connect(s.command.pathChooser(), &PathChooser::validChanged, options, &QWidget::setEnabled);
+ options->setEnabled(s.command.pathChooser()->isValid());
+
+ setOnApply([&s, configurations] {
+ s.customStyle.setValue(configurations->currentConfiguration());
+ s.save();
+ });
+ }
+};
+
+UncrustifyOptionsPage::UncrustifyOptionsPage(UncrustifySettings *settings)
+{
+ setId("Uncrustify");
+ setDisplayName(Tr::tr("Uncrustify"));
+ setCategory(Constants::OPTION_CATEGORY);
+ setWidgetCreator([settings] { return new UncrustifyOptionsPageWidget(settings); });
+}
+
} // Beautifier::Internal
diff --git a/src/plugins/beautifier/uncrustify/uncrustifysettings.h b/src/plugins/beautifier/uncrustify/uncrustifysettings.h
index d57bfe6d39..3911cfa9e9 100644
--- a/src/plugins/beautifier/uncrustify/uncrustifysettings.h
+++ b/src/plugins/beautifier/uncrustify/uncrustifysettings.h
@@ -5,41 +5,30 @@
#include "../abstractsettings.h"
-namespace Beautifier {
-namespace Internal {
+namespace Beautifier::Internal {
class UncrustifySettings : public AbstractSettings
{
- Q_OBJECT
-
public:
UncrustifySettings();
- ~UncrustifySettings() override;
-
- bool useOtherFiles() const;
- void setUseOtherFiles(bool useOtherFiles);
-
- bool useHomeFile() const;
- void setUseHomeFile(bool useHomeFile);
- bool useCustomStyle() const;
- void setUseCustomStyle(bool useCustomStyle);
+ void createDocumentationFile() const override;
- QString customStyle() const;
- void setCustomStyle(const QString &customStyle);
+ Utils::BoolAspect useOtherFiles{this};
+ Utils::BoolAspect useHomeFile{this};
+ Utils::BoolAspect useCustomStyle{this};
- bool formatEntireFileFallback() const;
- void setFormatEntireFileFallback(bool formatEntireFileFallback);
+ Utils::StringAspect customStyle{this};
+ Utils::BoolAspect formatEntireFileFallback{this};
- QString documentationFilePath() const override;
- void createDocumentationFile() const override;
-
- Utils::FilePath specificConfigFile() const;
- void setSpecificConfigFile(const Utils::FilePath &filePath);
+ Utils::FilePathAspect specificConfigFile{this};
+ Utils::BoolAspect useSpecificConfigFile{this};
+};
- bool useSpecificConfigFile() const;
- void setUseSpecificConfigFile(bool useConfigFile);
+class UncrustifyOptionsPage final : public Core::IOptionsPage
+{
+public:
+ explicit UncrustifyOptionsPage(UncrustifySettings *settings);
};
-} // namespace Internal
-} // namespace Beautifier
+} // Beautifier::Internal
diff --git a/src/plugins/bookmarks/bookmarkfilter.cpp b/src/plugins/bookmarks/bookmarkfilter.cpp
index 7cc9bf2a75..1cedbade48 100644
--- a/src/plugins/bookmarks/bookmarkfilter.cpp
+++ b/src/plugins/bookmarks/bookmarkfilter.cpp
@@ -19,101 +19,97 @@ BookmarkFilter::BookmarkFilter(BookmarkManager *manager)
{
setId("Bookmarks");
setDisplayName(Tr::tr("Bookmarks"));
- setDescription(Tr::tr("Matches all bookmarks. Filter by file name, by the text on the line of the "
- "bookmark, or by the bookmark's note text."));
+ setDescription(Tr::tr("Locates bookmarks. Filter by file name, by the text on the line of the "
+ "bookmark, or by the bookmark's note text."));
setPriority(Medium);
setDefaultShortcutString("b");
}
-void BookmarkFilter::prepareSearch(const QString &entry)
+LocatorMatcherTasks BookmarkFilter::matchers()
{
- m_results = {};
- if (m_manager->rowCount() == 0)
- return;
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [=] { storage->reportOutput(match(storage->input())); };
+ return {{Sync(onSetup), storage}};
+}
- auto match = [this](const QString &name, BookmarkManager::Roles role) {
+LocatorFilterEntries BookmarkFilter::match(const QString &input) const
+{
+ if (m_manager->rowCount() == 0)
+ return {};
+ const auto match = [this](const QString &name, BookmarkManager::Roles role) {
return m_manager->match(m_manager->index(0, 0), role, name, -1,
Qt::MatchContains | Qt::MatchWrap);
};
- int colonIndex = entry.lastIndexOf(':');
+ const int colonIndex = input.lastIndexOf(':');
QModelIndexList fileNameLineNumberMatches;
if (colonIndex >= 0) {
// Filter by "fileName:lineNumber" pattern
- const QString fileName = entry.left(colonIndex);
- const QString lineNumber = entry.mid(colonIndex + 1);
+ const QString fileName = input.left(colonIndex);
+ const QString lineNumber = input.mid(colonIndex + 1);
fileNameLineNumberMatches = match(fileName, BookmarkManager::Filename);
- fileNameLineNumberMatches =
- Utils::filtered(fileNameLineNumberMatches, [lineNumber](const QModelIndex &index) {
- return index.data(BookmarkManager::LineNumber).toString().contains(lineNumber);
- });
+ fileNameLineNumberMatches = Utils::filtered(
+ fileNameLineNumberMatches, [lineNumber](const QModelIndex &index) {
+ return index.data(BookmarkManager::LineNumber).toString().contains(lineNumber);
+ });
}
-
const QModelIndexList matches = filteredUnique(fileNameLineNumberMatches
- + match(entry, BookmarkManager::Filename)
- + match(entry, BookmarkManager::LineNumber)
- + match(entry, BookmarkManager::Note)
- + match(entry, BookmarkManager::LineText));
-
+ + match(input, BookmarkManager::Filename)
+ + match(input, BookmarkManager::LineNumber)
+ + match(input, BookmarkManager::Note)
+ + match(input, BookmarkManager::LineText));
+ LocatorFilterEntries entries;
for (const QModelIndex &idx : matches) {
const Bookmark *bookmark = m_manager->bookmarkForIndex(idx);
const QString filename = bookmark->filePath().fileName();
- LocatorFilterEntry filterEntry(this,
- QString("%1:%2").arg(filename).arg(bookmark->lineNumber()),
- QVariant::fromValue(idx));
+ LocatorFilterEntry entry;
+ entry.displayName = QString("%1:%2").arg(filename).arg(bookmark->lineNumber());
+ // TODO: according to QModelIndex docs, we shouldn't store model indexes:
+ // Model indexes should be used immediately and then discarded.
+ // You should not rely on indexes to remain valid after calling model functions
+ // that change the structure of the model or delete items.
+ entry.acceptor = [manager = m_manager, idx] {
+ if (Bookmark *bookmark = manager->bookmarkForIndex(idx))
+ manager->gotoBookmark(bookmark);
+ return AcceptResult();
+ };
if (!bookmark->note().isEmpty())
- filterEntry.extraInfo = bookmark->note();
+ entry.extraInfo = bookmark->note();
else if (!bookmark->lineText().isEmpty())
- filterEntry.extraInfo = bookmark->lineText();
+ entry.extraInfo = bookmark->lineText();
else
- filterEntry.extraInfo = bookmark->filePath().toString();
- int highlightIndex = filterEntry.displayName.indexOf(entry, 0, Qt::CaseInsensitive);
+ entry.extraInfo = bookmark->filePath().toString();
+ int highlightIndex = entry.displayName.indexOf(input, 0, Qt::CaseInsensitive);
if (highlightIndex >= 0) {
- filterEntry.highlightInfo = {highlightIndex, int(entry.length())};
+ entry.highlightInfo = {highlightIndex, int(input.length())};
} else {
- highlightIndex = filterEntry.extraInfo.indexOf(entry, 0, Qt::CaseInsensitive);
+ highlightIndex = entry.extraInfo.indexOf(input, 0, Qt::CaseInsensitive);
if (highlightIndex >= 0) {
- filterEntry.highlightInfo = {highlightIndex, int(entry.length()),
- LocatorFilterEntry::HighlightInfo::ExtraInfo};
+ entry.highlightInfo = {highlightIndex, int(input.length()),
+ LocatorFilterEntry::HighlightInfo::ExtraInfo};
} else if (colonIndex >= 0) {
- const QString fileName = entry.left(colonIndex);
- const QString lineNumber = entry.mid(colonIndex + 1);
- highlightIndex = filterEntry.displayName.indexOf(fileName, 0, Qt::CaseInsensitive);
+ const QString fileName = input.left(colonIndex);
+ const QString lineNumber = input.mid(colonIndex + 1);
+ highlightIndex = entry.displayName.indexOf(fileName, 0,
+ Qt::CaseInsensitive);
if (highlightIndex >= 0) {
- filterEntry.highlightInfo = {highlightIndex, int(fileName.length())};
- highlightIndex = filterEntry.displayName.indexOf(
+ entry.highlightInfo = {highlightIndex, int(fileName.length())};
+ highlightIndex = entry.displayName.indexOf(
lineNumber, highlightIndex, Qt::CaseInsensitive);
if (highlightIndex >= 0) {
- filterEntry.highlightInfo.startsDisplay += highlightIndex;
- filterEntry.highlightInfo.lengthsDisplay += lineNumber.length();
+ entry.highlightInfo.startsDisplay += highlightIndex;
+ entry.highlightInfo.lengthsDisplay += lineNumber.length();
}
}
}
}
-
- filterEntry.displayIcon = bookmark->icon();
- m_results.append(filterEntry);
- }
-}
-
-QList<LocatorFilterEntry> BookmarkFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry)
-{
- Q_UNUSED(future)
- Q_UNUSED(entry)
- return m_results;
-}
-
-void BookmarkFilter::accept(const LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const
-{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- if (const Bookmark *bookmark = m_manager->bookmarkForIndex(
- selection.internalData.toModelIndex())) {
- m_manager->gotoBookmark(bookmark);
+ entry.displayIcon = bookmark->icon();
+ entries.append(entry);
}
+ return entries;
}
} // Bookmarks::Internal
diff --git a/src/plugins/bookmarks/bookmarkfilter.h b/src/plugins/bookmarks/bookmarkfilter.h
index 0c69e2b6d4..f85eff9b7d 100644
--- a/src/plugins/bookmarks/bookmarkfilter.h
+++ b/src/plugins/bookmarks/bookmarkfilter.h
@@ -12,16 +12,13 @@ class BookmarkManager;
class BookmarkFilter : public Core::ILocatorFilter
{
public:
- explicit BookmarkFilter(BookmarkManager *manager);
- void prepareSearch(const QString &entry) override;
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const override;
+ BookmarkFilter(BookmarkManager *manager);
private:
+ Core::LocatorMatcherTasks matchers() final;
+ Core::LocatorFilterEntries match(const QString &input) const;
+
BookmarkManager *m_manager = nullptr; // not owned
- QList<Core::LocatorFilterEntry> m_results;
};
} // Bookmarks::Internal
diff --git a/src/plugins/bookmarks/bookmarkmanager.cpp b/src/plugins/bookmarks/bookmarkmanager.cpp
index e7c5a4e388..83a2650b75 100644
--- a/src/plugins/bookmarks/bookmarkmanager.cpp
+++ b/src/plugins/bookmarks/bookmarkmanager.cpp
@@ -7,13 +7,13 @@
#include "bookmarks_global.h"
#include "bookmarkstr.h"
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/actionmanager/command.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/idocument.h>
-#include <coreplugin/actionmanager/actionmanager.h>
-#include <coreplugin/actionmanager/command.h>
-#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <coreplugin/session.h>
+
#include <texteditor/texteditor.h>
#include <utils/algorithm.h>
#include <utils/icon.h>
@@ -29,7 +29,6 @@
#include <QDialog>
#include <QDialogButtonBox>
#include <QDir>
-#include <QFileInfo>
#include <QFormLayout>
#include <QLineEdit>
#include <QMenu>
@@ -39,7 +38,6 @@
Q_DECLARE_METATYPE(Bookmarks::Internal::Bookmark*)
-using namespace ProjectExplorer;
using namespace Core;
using namespace Utils;
@@ -256,11 +254,12 @@ void BookmarkView::keyPressEvent(QKeyEvent *event)
void BookmarkView::removeAll()
{
- if (CheckableMessageBox::doNotAskAgainQuestion(this,
- Tr::tr("Remove All Bookmarks"),
- Tr::tr("Are you sure you want to remove all bookmarks from all files in the current session?"),
- ICore::settings(),
- QLatin1String("RemoveAllBookmarks")) != QDialogButtonBox::Yes)
+ if (CheckableMessageBox::question(this,
+ Tr::tr("Remove All Bookmarks"),
+ Tr::tr("Are you sure you want to remove all bookmarks from "
+ "all files in the current session?"),
+ QString("RemoveAllBookmarks"))
+ != QMessageBox::Yes)
return;
// The performance of this function could be greatly improved.
diff --git a/src/plugins/bookmarks/bookmarksplugin.cpp b/src/plugins/bookmarks/bookmarksplugin.cpp
index 2f25571b53..7473876ec6 100644
--- a/src/plugins/bookmarks/bookmarksplugin.cpp
+++ b/src/plugins/bookmarks/bookmarksplugin.cpp
@@ -128,14 +128,16 @@ BookmarksPluginPrivate::BookmarksPluginPrivate()
mbm->addAction(cmd);
connect(&m_toggleAction, &QAction::triggered, this, [this] {
- BaseTextEditor *editor = BaseTextEditor::currentTextEditor();
- if (editor && !editor->document()->isTemporary())
+ IEditor *editor = EditorManager::currentEditor();
+ auto widget = TextEditorWidget::fromEditor(editor);
+ if (widget && editor && !editor->document()->isTemporary())
m_bookmarkManager.toggleBookmark(editor->document()->filePath(), editor->currentLine());
});
connect(&m_editAction, &QAction::triggered, this, [this] {
- BaseTextEditor *editor = BaseTextEditor::currentTextEditor();
- if (editor && !editor->document()->isTemporary()) {
+ IEditor *editor = EditorManager::currentEditor();
+ auto widget = TextEditorWidget::fromEditor(editor);
+ if (widget && editor && !editor->document()->isTemporary()) {
const FilePath filePath = editor->document()->filePath();
const int line = editor->currentLine();
if (!m_bookmarkManager.hasBookmarkInPosition(filePath, line))
diff --git a/src/plugins/boot2qt/device-detection/qdbwatcher.cpp b/src/plugins/boot2qt/device-detection/qdbwatcher.cpp
index 3375e10e02..869b2d84fc 100644
--- a/src/plugins/boot2qt/device-detection/qdbwatcher.cpp
+++ b/src/plugins/boot2qt/device-detection/qdbwatcher.cpp
@@ -8,7 +8,7 @@
#include "../qdbutils.h"
#include <utils/filepath.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QFile>
#include <QTimer>
@@ -118,7 +118,7 @@ void QdbWatcher::forkHostServer()
showMessage(message, true);
return;
}
- if (Utils::QtcProcess::startDetached({qdbFilePath, {"server"}}))
+ if (Utils::Process::startDetached({qdbFilePath, {"server"}}))
showMessage(Tr::tr("QDB host server started."), false);
else
showMessage(Tr::tr("Could not start QDB host server in %1").arg(qdbFilePath.toString()), true);
diff --git a/src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp b/src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp
index f115685794..3abb700df1 100644
--- a/src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp
+++ b/src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp
@@ -7,7 +7,7 @@
#include "qdbtr.h"
#include <projectexplorer/deploymentdataview.h>
-#include "projectexplorer/devicesupport/idevice.h"
+#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
#include <projectexplorer/target.h>
diff --git a/src/plugins/boot2qt/qdbdevice.cpp b/src/plugins/boot2qt/qdbdevice.cpp
index 90568f4264..43783279d6 100644
--- a/src/plugins/boot2qt/qdbdevice.cpp
+++ b/src/plugins/boot2qt/qdbdevice.cpp
@@ -15,8 +15,8 @@
#include <remotelinux/linuxprocessinterface.h>
#include <utils/portlist.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/theme/theme.h>
#include <QFormLayout>
@@ -30,11 +30,10 @@ using namespace Utils;
namespace Qdb::Internal {
-class QdbProcessImpl : public LinuxProcessInterface
+class QdbProcessImpl : public SshProcessInterface
{
public:
- QdbProcessImpl(const LinuxDevice *linuxDevice)
- : LinuxProcessInterface(linuxDevice) {}
+ QdbProcessImpl(const IDevice::ConstPtr &device) : SshProcessInterface(device) {}
~QdbProcessImpl() { killIfRunning(); }
private:
@@ -51,7 +50,7 @@ class DeviceApplicationObserver : public QObject
public:
DeviceApplicationObserver(const IDevice::ConstPtr &device, const CommandLine &command)
{
- connect(&m_appRunner, &QtcProcess::done, this, &DeviceApplicationObserver::handleDone);
+ connect(&m_appRunner, &Process::done, this, &DeviceApplicationObserver::handleDone);
QTC_ASSERT(device, return);
m_deviceName = device->displayName();
@@ -95,7 +94,7 @@ private:
deleteLater();
}
- QtcProcess m_appRunner;
+ Process m_appRunner;
QString m_deviceName;
};
@@ -105,6 +104,7 @@ private:
QdbDevice::QdbDevice()
{
setDisplayType(Tr::tr("Boot2Qt Device"));
+ setType(Constants::QdbLinuxOsType);
addDeviceAction({Tr::tr("Reboot Device"), [](const IDevice::Ptr &device, QWidget *) {
(void) new DeviceApplicationObserver(device, {device->filePath("reboot"), {}});
@@ -124,7 +124,7 @@ ProjectExplorer::IDeviceWidget *QdbDevice::createWidget()
ProcessInterface *QdbDevice::createProcessInterface() const
{
- return new QdbProcessImpl(this);
+ return new QdbProcessImpl(sharedFromThis());
}
void QdbDevice::setSerialNumber(const QString &serial)
@@ -248,6 +248,7 @@ QdbLinuxDeviceFactory::QdbLinuxDeviceFactory()
{
setDisplayName(Tr::tr("Boot2Qt Device"));
setCombinedIcon(":/qdb/images/qdbdevicesmall.png", ":/qdb/images/qdbdevice.png");
+ setQuickCreationAllowed(true);
setConstructionFunction(&QdbDevice::create);
setCreator([] {
QdbDeviceWizard wizard(Core::ICore::dialogParent());
diff --git a/src/plugins/boot2qt/qdbdevicedebugsupport.cpp b/src/plugins/boot2qt/qdbdevicedebugsupport.cpp
index 97529dfa9f..d6e198538f 100644
--- a/src/plugins/boot2qt/qdbdevicedebugsupport.cpp
+++ b/src/plugins/boot2qt/qdbdevicedebugsupport.cpp
@@ -13,7 +13,7 @@
#include <debugger/debuggerruncontrol.h>
#include <utils/algorithm.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/url.h>
using namespace Debugger;
@@ -34,13 +34,13 @@ public:
{
setId("QdbDebuggeeRunner");
- connect(&m_launcher, &QtcProcess::started, this, &RunWorker::reportStarted);
- connect(&m_launcher, &QtcProcess::done, this, &RunWorker::reportStopped);
+ connect(&m_launcher, &Process::started, this, &RunWorker::reportStarted);
+ connect(&m_launcher, &Process::done, this, &RunWorker::reportStopped);
- connect(&m_launcher, &QtcProcess::readyReadStandardOutput, this, [this] {
+ connect(&m_launcher, &Process::readyReadStandardOutput, this, [this] {
appendMessage(m_launcher.readAllStandardOutput(), StdOutFormat);
});
- connect(&m_launcher, &QtcProcess::readyReadStandardError, this, [this] {
+ connect(&m_launcher, &Process::readyReadStandardError, this, [this] {
appendMessage(m_launcher.readAllStandardError(), StdErrFormat);
});
@@ -112,7 +112,7 @@ private:
bool m_useGdbServer;
bool m_useQmlServer;
QmlDebug::QmlDebugServicesPreset m_qmlServices;
- QtcProcess m_launcher;
+ Process m_launcher;
};
// QdbDeviceRunSupport
diff --git a/src/plugins/boot2qt/qdbmakedefaultappstep.cpp b/src/plugins/boot2qt/qdbmakedefaultappstep.cpp
index 70c1c40acb..ae5806deb9 100644
--- a/src/plugins/boot2qt/qdbmakedefaultappstep.cpp
+++ b/src/plugins/boot2qt/qdbmakedefaultappstep.cpp
@@ -14,27 +14,35 @@
#include <remotelinux/abstractremotelinuxdeploystep.h>
#include <utils/commandline.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
-using namespace Utils::Tasking;
namespace Qdb::Internal {
-// QdbMakeDefaultAppService
-
-class QdbMakeDefaultAppService : public RemoteLinux::AbstractRemoteLinuxDeployService
+class QdbMakeDefaultAppStep final : public RemoteLinux::AbstractRemoteLinuxDeployStep
{
public:
- void setMakeDefault(bool makeDefault) { m_makeDefault = makeDefault; }
+ QdbMakeDefaultAppStep(BuildStepList *bsl, Id id)
+ : AbstractRemoteLinuxDeployStep(bsl, id)
+ {
+ auto selection = addAspect<SelectionAspect>();
+ selection->setSettingsKey("QdbMakeDefaultDeployStep.MakeDefault");
+ selection->addOption(Tr::tr("Set this application to start by default"));
+ selection->addOption(Tr::tr("Reset default application"));
-private:
- bool isDeploymentNecessary() const final { return true; }
+ setInternalInitializer([this, selection] {
+ m_makeDefault = selection->value() == 0;
+ return isDeploymentPossible();
+ });
+ }
+private:
Group deployRecipe() final
{
- const auto setupHandler = [this](QtcProcess &process) {
+ const auto setupHandler = [this](Process &process) {
QString remoteExe;
if (RunConfiguration *rc = target()->activeRunConfiguration()) {
if (auto exeAspect = rc->aspect<ExecutableAspect>())
@@ -46,50 +54,26 @@ private:
else
cmd.addArg("--remove-default");
process.setCommand(cmd);
- QtcProcess *proc = &process;
- connect(proc, &QtcProcess::readyReadStandardError, this, [this, proc] {
- emit stdErrData(proc->readAllStandardError());
+ Process *proc = &process;
+ connect(proc, &Process::readyReadStandardError, this, [this, proc] {
+ handleStdErrData(proc->readAllStandardError());
});
};
- const auto doneHandler = [this](const QtcProcess &) {
+ const auto doneHandler = [this](const Process &) {
if (m_makeDefault)
- emit progressMessage(Tr::tr("Application set as the default one."));
+ addProgressMessage(Tr::tr("Application set as the default one."));
else
- emit progressMessage(Tr::tr("Reset the default application."));
+ addProgressMessage(Tr::tr("Reset the default application."));
};
- const auto errorHandler = [this](const QtcProcess &process) {
- emit errorMessage(Tr::tr("Remote process failed: %1").arg(process.errorString()));
+ const auto errorHandler = [this](const Process &process) {
+ addErrorMessage(Tr::tr("Remote process failed: %1").arg(process.errorString()));
};
- return Group { Process(setupHandler, doneHandler, errorHandler) };
+ return Group { ProcessTask(setupHandler, doneHandler, errorHandler) };
}
bool m_makeDefault = true;
};
-// QdbMakeDefaultAppStep
-
-class QdbMakeDefaultAppStep final : public RemoteLinux::AbstractRemoteLinuxDeployStep
-{
-public:
- QdbMakeDefaultAppStep(BuildStepList *bsl, Id id)
- : AbstractRemoteLinuxDeployStep(bsl, id)
- {
- auto service = new QdbMakeDefaultAppService;
- setDeployService(service);
-
- auto selection = addAspect<SelectionAspect>();
- selection->setSettingsKey("QdbMakeDefaultDeployStep.MakeDefault");
- selection->addOption(Tr::tr("Set this application to start by default"));
- selection->addOption(Tr::tr("Reset default application"));
-
- setInternalInitializer([service, selection] {
- service->setMakeDefault(selection->value() == 0);
- return service->isDeploymentPossible();
- });
- }
-};
-
-
// QdbMakeDefaultAppStepFactory
QdbMakeDefaultAppStepFactory::QdbMakeDefaultAppStepFactory()
diff --git a/src/plugins/boot2qt/qdbplugin.cpp b/src/plugins/boot2qt/qdbplugin.cpp
index a73646b6c8..8c806cb158 100644
--- a/src/plugins/boot2qt/qdbplugin.cpp
+++ b/src/plugins/boot2qt/qdbplugin.cpp
@@ -27,22 +27,18 @@
#include <qtsupport/qtversionfactory.h>
-#include <remotelinux/genericdirectuploadstep.h>
-#include <remotelinux/makeinstallstep.h>
-#include <remotelinux/rsyncdeploystep.h>
#include <remotelinux/remotelinux_constants.h>
-#include <utils/hostosinfo.h>
#include <utils/fileutils.h>
-#include <utils/qtcprocess.h>
+#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <QAction>
using namespace ProjectExplorer;
using namespace Utils;
-namespace Qdb {
-namespace Internal {
+namespace Qdb::Internal {
static FilePath flashWizardFilePath()
{
@@ -53,9 +49,9 @@ static void startFlashingWizard()
{
const FilePath filePath = flashWizardFilePath();
if (HostOsInfo::isWindowsHost()) {
- if (QtcProcess::startDetached({"explorer.exe", {filePath.toUserOutput()}}))
+ if (Process::startDetached({"explorer.exe", {filePath.toUserOutput()}}))
return;
- } else if (QtcProcess::startDetached({filePath, {}})) {
+ } else if (Process::startDetached({filePath, {}})) {
return;
}
const QString message = Tr::tr("Flash wizard \"%1\" failed to start.");
@@ -100,14 +96,12 @@ void registerFlashAction(QObject *parentForAction)
toolsContainer->addAction(flashCommand, flashActionId);
}
-template <class Step>
-class QdbDeployStepFactory : public ProjectExplorer::BuildStepFactory
+class QdbDeployStepFactory : public BuildStepFactory
{
public:
- explicit QdbDeployStepFactory(Id id)
+ explicit QdbDeployStepFactory(Id existingStepId)
{
- registerStep<Step>(id);
- setDisplayName(Step::displayName());
+ cloneStepCreator(existingStepId);
setSupportedConfiguration(Constants::QdbDeployConfigurationId);
setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY);
}
@@ -125,12 +119,9 @@ public:
QdbStopApplicationStepFactory m_stopApplicationStepFactory;
QdbMakeDefaultAppStepFactory m_makeDefaultAppStepFactory;
- QdbDeployStepFactory<RemoteLinux::GenericDirectUploadStep>
- m_directUploadStepFactory{RemoteLinux::Constants::DirectUploadStepId};
- QdbDeployStepFactory<RemoteLinux::RsyncDeployStep>
- m_rsyncDeployStepFactory{RemoteLinux::Constants::RsyncDeployStepId};
- QdbDeployStepFactory<RemoteLinux::MakeInstallStep>
- m_makeInstallStepFactory{RemoteLinux::Constants::MakeInstallStepId};
+ QdbDeployStepFactory m_directUploadStepFactory{RemoteLinux::Constants::DirectUploadStepId};
+ QdbDeployStepFactory m_rsyncDeployStepFactory{RemoteLinux::Constants::RsyncDeployStepId};
+ QdbDeployStepFactory m_makeInstallStepFactory{RemoteLinux::Constants::MakeInstallStepId};
const QList<Id> supportedRunConfigs {
m_runConfigFactory.runConfigurationId(),
@@ -180,5 +171,4 @@ void QdbPluginPrivate::setupDeviceDetection()
m_deviceDetector.start();
}
-} // Internal
-} // Qdb
+} // Qdb::Internal
diff --git a/src/plugins/boot2qt/qdbstopapplicationstep.cpp b/src/plugins/boot2qt/qdbstopapplicationstep.cpp
index 60d891beaf..5fef60268f 100644
--- a/src/plugins/boot2qt/qdbstopapplicationstep.cpp
+++ b/src/plugins/boot2qt/qdbstopapplicationstep.cpp
@@ -13,78 +13,67 @@
#include <remotelinux/abstractremotelinuxdeploystep.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
-using namespace Utils::Tasking;
namespace Qdb::Internal {
-// QdbStopApplicationService
+// QdbStopApplicationStep
-class QdbStopApplicationService : public RemoteLinux::AbstractRemoteLinuxDeployService
+class QdbStopApplicationStep final : public RemoteLinux::AbstractRemoteLinuxDeployStep
{
-private:
- bool isDeploymentNecessary() const final { return true; }
+public:
+ QdbStopApplicationStep(BuildStepList *bsl, Id id)
+ : AbstractRemoteLinuxDeployStep(bsl, id)
+ {
+ setWidgetExpandedByDefault(false);
+
+ setInternalInitializer([this] { return isDeploymentPossible(); });
+ }
+
Group deployRecipe() final;
};
-Group QdbStopApplicationService::deployRecipe()
+Group QdbStopApplicationStep::deployRecipe()
{
- const auto setupHandler = [this](QtcProcess &process) {
+ const auto setupHandler = [this](Process &process) {
const auto device = DeviceKitAspect::device(target()->kit());
if (!device) {
- emit errorMessage(Tr::tr("No device to stop the application on."));
+ addErrorMessage(Tr::tr("No device to stop the application on."));
return TaskAction::StopWithError;
}
QTC_CHECK(device);
process.setCommand({device->filePath(Constants::AppcontrollerFilepath), {"--stop"}});
process.setWorkingDirectory("/usr/bin");
- QtcProcess *proc = &process;
- connect(proc, &QtcProcess::readyReadStandardOutput, this, [this, proc] {
- emit stdOutData(proc->readAllStandardOutput());
+ Process *proc = &process;
+ connect(proc, &Process::readyReadStandardOutput, this, [this, proc] {
+ handleStdOutData(proc->readAllStandardOutput());
});
return TaskAction::Continue;
};
- const auto doneHandler = [this](const QtcProcess &) {
- emit progressMessage(Tr::tr("Stopped the running application."));
+ const auto doneHandler = [this](const Process &) {
+ addProgressMessage(Tr::tr("Stopped the running application."));
};
- const auto errorHandler = [this](const QtcProcess &process) {
+ const auto errorHandler = [this](const Process &process) {
const QString errorOutput = process.cleanedStdErr();
const QString failureMessage = Tr::tr("Could not check and possibly stop running application.");
if (process.exitStatus() == QProcess::CrashExit) {
- emit errorMessage(failureMessage);
+ addErrorMessage(failureMessage);
} else if (process.result() != ProcessResult::FinishedWithSuccess) {
- emit stdErrData(process.errorString());
+ handleStdErrData(process.errorString());
} else if (errorOutput.contains("Could not connect: Connection refused")) {
- emit progressMessage(Tr::tr("Checked that there is no running application."));
+ addProgressMessage(Tr::tr("Checked that there is no running application."));
} else if (!errorOutput.isEmpty()) {
- emit stdErrData(errorOutput);
- emit errorMessage(failureMessage);
+ handleStdErrData(errorOutput);
+ addErrorMessage(failureMessage);
}
};
- return Group { Process(setupHandler, doneHandler, errorHandler) };
+ return Group { ProcessTask(setupHandler, doneHandler, errorHandler) };
}
-// QdbStopApplicationStep
-
-class QdbStopApplicationStep final : public RemoteLinux::AbstractRemoteLinuxDeployStep
-{
-public:
- QdbStopApplicationStep(BuildStepList *bsl, Id id)
- : AbstractRemoteLinuxDeployStep(bsl, id)
- {
- auto service = new QdbStopApplicationService;
- setDeployService(service);
-
- setWidgetExpandedByDefault(false);
-
- setInternalInitializer([service] { return service->isDeploymentPossible(); });
- }
-};
-
-
// QdbStopApplicationStepFactory
QdbStopApplicationStepFactory::QdbStopApplicationStepFactory()
diff --git a/src/plugins/clangcodemodel/CMakeLists.txt b/src/plugins/clangcodemodel/CMakeLists.txt
index ace3907171..243a330efa 100644
--- a/src/plugins/clangcodemodel/CMakeLists.txt
+++ b/src/plugins/clangcodemodel/CMakeLists.txt
@@ -51,7 +51,6 @@ extend_qtc_plugin(ClangCodeModel
CONDITION WITH_TESTS
SOURCES
test/activationsequenceprocessortest.cpp test/activationsequenceprocessortest.h
- test/clangbatchfileprocessor.cpp test/clangbatchfileprocessor.h
test/clangdtests.cpp test/clangdtests.h
test/clangfixittest.cpp test/clangfixittest.h
test/data/clangtestdata.qrc
diff --git a/src/plugins/clangcodemodel/clangcodemodel.qbs b/src/plugins/clangcodemodel/clangcodemodel.qbs
index 45a6abc347..903a81147a 100644
--- a/src/plugins/clangcodemodel/clangcodemodel.qbs
+++ b/src/plugins/clangcodemodel/clangcodemodel.qbs
@@ -94,15 +94,11 @@ QtcPlugin {
}
}
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
prefix: "test/"
files: [
"activationsequenceprocessortest.cpp",
"activationsequenceprocessortest.h",
- "clangbatchfileprocessor.cpp",
- "clangbatchfileprocessor.h",
"clangdtests.cpp",
"clangdtests.h",
"clangfixittest.cpp",
diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp
index 2a3772afb2..9b5ec42523 100644
--- a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp
+++ b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp
@@ -10,7 +10,6 @@
#ifdef WITH_TESTS
# include "test/activationsequenceprocessortest.h"
-# include "test/clangbatchfileprocessor.h"
# include "test/clangdtests.h"
# include "test/clangfixittest.h"
#endif
@@ -28,15 +27,16 @@
#include <projectexplorer/projectpanelfactory.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
#include <texteditor/textmark.h>
+#include <utils/async.h>
#include <utils/environment.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <utils/temporarydirectory.h>
using namespace Core;
@@ -49,7 +49,7 @@ void ClangCodeModelPlugin::generateCompilationDB()
{
using namespace CppEditor;
- Target *target = SessionManager::startupTarget();
+ Target *target = ProjectManager::startupTarget();
if (!target)
return;
@@ -61,7 +61,7 @@ void ClangCodeModelPlugin::generateCompilationDB()
baseDir = TemporaryDirectory::masterDirectoryFilePath();
QFuture<GenerateCompilationDbResult> task
- = Utils::runAsync(&Internal::generateCompilationDB, ProjectInfoList{projectInfo},
+ = Utils::asyncRun(&Internal::generateCompilationDB, ProjectInfoList{projectInfo},
baseDir, CompilationDbPurpose::Project,
warningsConfigForProject(target->project()),
globalClangOptions(),
@@ -78,15 +78,8 @@ ClangCodeModelPlugin::~ClangCodeModelPlugin()
void ClangCodeModelPlugin::initialize()
{
TaskHub::addCategory(Constants::TASK_CATEGORY_DIAGNOSTICS, Tr::tr("Clang Code Model"));
-
- connect(ProjectExplorerPlugin::instance(),
- &ProjectExplorerPlugin::finishedInitialization,
- this,
- &ClangCodeModelPlugin::maybeHandleBatchFileAndExit);
-
CppEditor::CppModelManager::instance()->activateClangCodeModel(
- std::make_unique<ClangModelManagerSupport>());
-
+ std::make_unique<ClangModelManagerSupport>());
createCompilationDBAction();
#ifdef WITH_TESTS
@@ -109,7 +102,7 @@ void ClangCodeModelPlugin::createCompilationDBAction()
Tr::tr("Generate Compilation Database"),
Tr::tr("Generate Compilation Database for \"%1\""),
ParameterAction::AlwaysEnabled, this);
- Project *startupProject = SessionManager::startupProject();
+ Project *startupProject = ProjectManager::startupProject();
if (startupProject)
m_generateCompilationDBAction->setParameter(startupProject->displayName());
Command *command = ActionManager::registerAction(m_generateCompilationDBAction,
@@ -136,7 +129,7 @@ void ClangCodeModelPlugin::createCompilationDBAction()
"Generator is already running.");
return;
}
- Project * const project = SessionManager::startupProject();
+ Project * const project = ProjectManager::startupProject();
if (!project) {
MessageManager::writeDisrupting("Cannot generate compilation database: "
"No active project.");
@@ -154,21 +147,21 @@ void ClangCodeModelPlugin::createCompilationDBAction()
});
connect(CppEditor::CppModelManager::instance(), &CppEditor::CppModelManager::projectPartsUpdated,
this, [this](Project *project) {
- if (project != SessionManager::startupProject())
+ if (project != ProjectManager::startupProject())
return;
m_generateCompilationDBAction->setParameter(project->displayName());
});
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, [this](Project *project) {
m_generateCompilationDBAction->setParameter(project ? project->displayName() : "");
});
- connect(SessionManager::instance(), &SessionManager::projectDisplayNameChanged,
+ connect(ProjectManager::instance(), &ProjectManager::projectDisplayNameChanged,
this, [this](Project *project) {
- if (project != SessionManager::startupProject())
+ if (project != ProjectManager::startupProject())
return;
m_generateCompilationDBAction->setParameter(project->displayName());
});
- connect(SessionManager::instance(), &SessionManager::projectAdded,
+ connect(ProjectManager::instance(), &ProjectManager::projectAdded,
this, [this](Project *project) {
project->registerGenerator(Constants::GENERATE_COMPILATION_DB,
m_generateCompilationDBAction->text(),
@@ -176,16 +169,4 @@ void ClangCodeModelPlugin::createCompilationDBAction()
});
}
-// For e.g. creation of profile-guided optimization builds.
-void ClangCodeModelPlugin::maybeHandleBatchFileAndExit() const
-{
-#ifdef WITH_TESTS
- const QString batchFilePath = qtcEnvironmentVariable("QTC_CLANG_BATCH");
- if (!batchFilePath.isEmpty() && QTC_GUARD(QFileInfo::exists(batchFilePath))) {
- const bool runSucceeded = runClangBatchFile(batchFilePath);
- QCoreApplication::exit(!runSucceeded);
- }
-#endif
-}
-
-} // ClangCodeModel::Internal
+} // namespace ClangCodeModel::Internal
diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.h b/src/plugins/clangcodemodel/clangcodemodelplugin.h
index 664cf113aa..d0456257f2 100644
--- a/src/plugins/clangcodemodel/clangcodemodelplugin.h
+++ b/src/plugins/clangcodemodel/clangcodemodelplugin.h
@@ -23,8 +23,6 @@ public:
void initialize() override;
private:
- void maybeHandleBatchFileAndExit() const;
-
void generateCompilationDB();
void createCompilationDBAction();
diff --git a/src/plugins/clangcodemodel/clangdast.cpp b/src/plugins/clangcodemodel/clangdast.cpp
index 6b97146dd5..01f12d1177 100644
--- a/src/plugins/clangcodemodel/clangdast.cpp
+++ b/src/plugins/clangcodemodel/clangdast.cpp
@@ -172,6 +172,20 @@ bool ClangdAstNode::hasChildWithRole(const QString &role) const
[&role](const ClangdAstNode &c) { return c.role() == role; });
}
+bool ClangdAstNode::hasChild(const std::function<bool(const ClangdAstNode &)> &predicate,
+ bool recursive) const
+{
+ std::function<bool(const ClangdAstNode &)> fullPredicate = predicate;
+ if (recursive) {
+ fullPredicate = [&predicate](const ClangdAstNode &n) {
+ if (predicate(n))
+ return true;
+ return n.hasChild(predicate, true);
+ };
+ }
+ return Utils::contains(children().value_or(QList<ClangdAstNode>()), fullPredicate);
+}
+
QString ClangdAstNode::operatorString() const
{
if (kind() == "BinaryOperator")
diff --git a/src/plugins/clangcodemodel/clangdast.h b/src/plugins/clangcodemodel/clangdast.h
index 7eb24e9f21..cdb87c7be0 100644
--- a/src/plugins/clangcodemodel/clangdast.h
+++ b/src/plugins/clangcodemodel/clangdast.h
@@ -77,6 +77,7 @@ public:
bool childContainsRange(int index, const LanguageServerProtocol::Range &range) const;
bool hasChildWithRole(const QString &role) const;
+ bool hasChild(const std::function<bool(const ClangdAstNode &child)> &predicate, bool recursive) const;
QString operatorString() const;
enum class FileStatus { Ours, Foreign, Mixed, Unknown };
diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp
index 53a5b967aa..0082ff6cef 100644
--- a/src/plugins/clangcodemodel/clangdclient.cpp
+++ b/src/plugins/clangcodemodel/clangdclient.cpp
@@ -9,12 +9,10 @@
#include "clangdcompletion.h"
#include "clangdfindreferences.h"
#include "clangdfollowsymbol.h"
-#include "clangdlocatorfilters.h"
#include "clangdmemoryusagewidget.h"
#include "clangdquickfixes.h"
#include "clangdsemantichighlighting.h"
#include "clangdswitchdecldef.h"
-#include "clangmodelmanagersupport.h"
#include "clangtextmark.h"
#include "clangutils.h"
#include "tasktimers.h"
@@ -38,6 +36,7 @@
#include <languageclient/languageclienthoverhandler.h>
#include <languageclient/languageclientinterface.h>
#include <languageclient/languageclientmanager.h>
+#include <languageclient/languageclientoutline.h>
#include <languageclient/languageclientsymbolsupport.h>
#include <languageclient/languageclientutils.h>
#include <languageclient/progressmanager.h>
@@ -48,7 +47,7 @@
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
#include <texteditor/codeassist/assistinterface.h>
@@ -57,10 +56,11 @@
#include <texteditor/codeassist/textdocumentmanipulatorinterface.h>
#include <texteditor/texteditor.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/itemviews.h>
-#include <utils/runextensions.h>
+#include <utils/theme/theme.h>
#include <utils/utilsicons.h>
#include <QAction>
@@ -128,6 +128,27 @@ public:
: Request("textDocument/symbolInfo", params) {}
};
+class ClangdOutlineItem : public LanguageClientOutlineItem
+{
+ using LanguageClientOutlineItem::LanguageClientOutlineItem;
+private:
+ QVariant data(int column, int role) const override
+ {
+ switch (role) {
+ case Qt::DisplayRole:
+ return ClangdClient::displayNameFromDocumentSymbol(
+ static_cast<SymbolKind>(type()), name(), detail());
+ case Qt::ForegroundRole:
+ if ((detail().endsWith("class") || detail().endsWith("struct"))
+ && range().end() == selectionRange().end()) {
+ return creatorTheme()->color(Theme::TextColorDisabled);
+ }
+ break;
+ }
+ return LanguageClientOutlineItem::data(column, role);
+ }
+};
+
void setupClangdConfigFile()
{
const Utils::FilePath targetConfigFile = CppEditor::ClangdSettings::clangdUserConfigFilePath();
@@ -192,7 +213,7 @@ static BaseClientInterface *clientInterface(Project *project, const Utils::FileP
if (settings.clangdVersion() >= QVersionNumber(16))
cmd.addArg("--rename-file-limit=0");
if (!jsonDbDir.isEmpty())
- cmd.addArg("--compile-commands-dir=" + jsonDbDir.onDevice(clangdExePath).path());
+ cmd.addArg("--compile-commands-dir=" + clangdExePath.withNewMappedPath(jsonDbDir).path());
if (clangdLogServer().isDebugEnabled())
cmd.addArgs({"--log=verbose", "--pretty"});
cmd.addArg("--use-dirty-headers");
@@ -427,7 +448,6 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir, c
});
setCurrentProject(project);
setDocumentChangeUpdateThreshold(d->settings.documentUpdateThreshold);
- setSymbolStringifier(displayNameFromDocumentSymbol);
setSemanticTokensHandler([this](TextDocument *doc, const QList<ExpandedSemanticToken> &tokens,
int version, bool force) {
d->handleSemanticTokens(doc, tokens, version, force);
@@ -450,12 +470,7 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir, c
}
});
- connect(this, &Client::initialized, this, [this] {
- auto currentDocumentFilter = static_cast<ClangdCurrentDocumentFilter *>(
- CppEditor::CppModelManager::instance()->currentDocumentFilter());
- currentDocumentFilter->updateCurrentClient();
- d->openedExtraFiles.clear();
- });
+ connect(this, &Client::initialized, this, [this] { d->openedExtraFiles.clear(); });
start();
}
@@ -698,6 +713,12 @@ DiagnosticManager *ClangdClient::createDiagnosticManager()
return diagnosticManager;
}
+LanguageClientOutlineItem *ClangdClient::createOutlineItem(
+ const LanguageServerProtocol::DocumentSymbol &symbol)
+{
+ return new ClangdOutlineItem(this, symbol);
+}
+
bool ClangdClient::referencesShadowFile(const TextEditor::TextDocument *doc,
const Utils::FilePath &candidate)
{
@@ -710,7 +731,7 @@ bool ClangdClient::fileBelongsToProject(const Utils::FilePath &filePath) const
{
if (CppEditor::ClangdSettings::instance().granularity()
== CppEditor::ClangdSettings::Granularity::Session) {
- return SessionManager::projectForFile(filePath);
+ return ProjectManager::projectForFile(filePath);
}
return Client::fileBelongsToProject(filePath);
}
@@ -1477,7 +1498,7 @@ void ClangdClient::Private::handleSemanticTokens(TextDocument *doc,
clangdVersion = q->versionNumber(),
this] {
try {
- return Utils::runAsync(doSemanticHighlighting, filePath, tokens, text, ast, doc,
+ return Utils::asyncRun(doSemanticHighlighting, filePath, tokens, text, ast, doc,
rev, clangdVersion, highlightingTimer);
} catch (const std::exception &e) {
qWarning() << "caught" << e.what() << "in main highlighting thread";
diff --git a/src/plugins/clangcodemodel/clangdclient.h b/src/plugins/clangcodemodel/clangdclient.h
index 67416d7379..27ec66a301 100644
--- a/src/plugins/clangcodemodel/clangdclient.h
+++ b/src/plugins/clangcodemodel/clangdclient.h
@@ -3,12 +3,12 @@
#pragma once
-#include <coreplugin/find/searchresultitem.h>
#include <cppeditor/baseeditordocumentparser.h>
#include <cppeditor/cppcodemodelsettings.h>
#include <cppeditor/cursorineditor.h>
#include <languageclient/client.h>
#include <utils/link.h>
+#include <utils/searchresultitem.h>
#include <QVersionNumber>
@@ -119,7 +119,7 @@ public:
signals:
void indexingFinished();
- void foundReferences(const QList<Core::SearchResultItem> &items);
+ void foundReferences(const Utils::SearchResultItems &items);
void findUsagesDone();
void helpItemGathered(const Core::HelpItem &helpItem);
void highlightingResultsReady(const TextEditor::HighlightingResults &results,
@@ -137,6 +137,8 @@ private:
const CustomInspectorTabs createCustomInspectorTabs() override;
TextEditor::RefactoringChangesData *createRefactoringChangesBackend() const override;
LanguageClient::DiagnosticManager *createDiagnosticManager() override;
+ LanguageClient::LanguageClientOutlineItem *createOutlineItem(
+ const LanguageServerProtocol::DocumentSymbol &symbol) override;
bool referencesShadowFile(const TextEditor::TextDocument *doc,
const Utils::FilePath &candidate) override;
bool fileBelongsToProject(const Utils::FilePath &filePath) const override;
diff --git a/src/plugins/clangcodemodel/clangdfindreferences.cpp b/src/plugins/clangcodemodel/clangdfindreferences.cpp
index 8a2d3faaef..b44fd41a82 100644
--- a/src/plugins/clangcodemodel/clangdfindreferences.cpp
+++ b/src/plugins/clangcodemodel/clangdfindreferences.cpp
@@ -21,9 +21,10 @@
#include <languageserverprotocol/lsptypes.h>
#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <texteditor/basefilefind.h>
@@ -71,7 +72,7 @@ public:
const Position linkAsPosition;
const QPointer<SearchResult> search;
const LinkHandler callback;
- QList<SearchResultItem> declDefItems;
+ SearchResultItems declDefItems;
bool openedExtraFileForLink = false;
bool declHasUsedTag = false;
bool recursiveCallDetected = false;
@@ -88,7 +89,7 @@ public:
const SearchResult *search,
const ReplacementData &replacementData,
const QString &newSymbolName,
- const QList<SearchResultItem> &checkedItems,
+ const SearchResultItems &checkedItems,
bool preserveCase);
void handleFindUsagesResult(const QList<Location> &locations);
void finishSearch();
@@ -142,7 +143,7 @@ ClangdFindReferences::ClangdFindReferences(ClangdClient *client, TextDocument *d
d->search->setAdditionalReplaceWidget(renameFilesCheckBox);
const auto renameHandler =
[search = d->search](const QString &newSymbolName,
- const QList<SearchResultItem> &checkedItems,
+ const SearchResultItems &checkedItems,
bool preserveCase) {
const auto replacementData = search->userData().value<ReplacementData>();
Private::handleRenameRequest(search, replacementData, newSymbolName, checkedItems,
@@ -150,7 +151,7 @@ ClangdFindReferences::ClangdFindReferences(ClangdClient *client, TextDocument *d
};
connect(d->search, &SearchResult::replaceButtonClicked, renameHandler);
}
- connect(d->search, &SearchResult::activated, [](const SearchResultItem& item) {
+ connect(d->search, &SearchResult::activated, [](const SearchResultItem &item) {
EditorManager::openEditorAtSearchResult(item);
});
if (d->search->isInteractive())
@@ -242,7 +243,7 @@ void ClangdFindReferences::Private::handleRenameRequest(
const SearchResult *search,
const ReplacementData &replacementData,
const QString &newSymbolName,
- const QList<SearchResultItem> &checkedItems,
+ const SearchResultItems &checkedItems,
bool preserveCase)
{
const Utils::FilePaths filePaths = BaseFileFind::replaceAll(newSymbolName, checkedItems,
@@ -390,7 +391,7 @@ static Usage::Tags getUsageType(const ClangdAstPath &path, const QString &search
void ClangdFindReferences::Private::addSearchResultsForFile(const FilePath &file,
const ReferencesFileData &fileData)
{
- QList<SearchResultItem> items;
+ SearchResultItems items;
qCDebug(clangdLog) << file << "has valid AST:" << fileData.ast.isValid();
const auto expectedDeclTypes = [this]() -> QStringList {
if (checkUnusedData)
@@ -451,7 +452,7 @@ void ClangdFindReferences::Private::addSearchResultsForFile(const FilePath &file
item.setContainingFunctionName(getContainingFunction(astPath, range).detail());
if (search->supportsReplace()) {
- const bool fileInSession = SessionManager::projectForFile(file);
+ const bool fileInSession = ProjectManager::projectForFile(file);
item.setSelectForReplacement(fileInSession);
if (fileInSession && file.baseName().compare(replacementData->oldSymbolName,
Qt::CaseInsensitive) == 0) {
@@ -594,6 +595,19 @@ static Usage::Tags getUsageType(const ClangdAstPath &path, const QString &search
tags |= Usage::Tag::Operator;
}
}
+ if (pathIt->kind() == "CXXMethod") {
+ const ClangdAstNode &classNode = *std::next(pathIt);
+ if (classNode.hasChild([&](const ClangdAstNode &n) {
+ if (n.kind() != "StaticAssert")
+ return false;
+ return n.hasChild([&](const ClangdAstNode &n) {
+ return n.arcanaContains("Q_PROPERTY"); }, true)
+ && n.hasChild([&](const ClangdAstNode &n) {
+ return n.arcanaContains(" " + searchTerm); }, true);
+ }, false)) {
+ tags |= Usage::Tag::MocInvokable;
+ }
+ }
return tags;
}
if (pathIt->kind() == "MemberInitializer")
diff --git a/src/plugins/clangcodemodel/clangdfindreferences.h b/src/plugins/clangcodemodel/clangdfindreferences.h
index d904db31f4..e110b35543 100644
--- a/src/plugins/clangcodemodel/clangdfindreferences.h
+++ b/src/plugins/clangcodemodel/clangdfindreferences.h
@@ -3,9 +3,9 @@
#pragma once
-#include <coreplugin/find/searchresultitem.h>
#include <cppeditor/cursorineditor.h>
#include <utils/link.h>
+#include <utils/searchresultitem.h>
#include <QObject>
@@ -35,7 +35,7 @@ public:
~ClangdFindReferences();
signals:
- void foundReferences(const QList<Core::SearchResultItem> &items);
+ void foundReferences(const Utils::SearchResultItems &items);
void done();
private:
diff --git a/src/plugins/clangcodemodel/clangdlocatorfilters.cpp b/src/plugins/clangcodemodel/clangdlocatorfilters.cpp
index 725d961591..f783a98499 100644
--- a/src/plugins/clangcodemodel/clangdlocatorfilters.cpp
+++ b/src/plugins/clangcodemodel/clangdlocatorfilters.cpp
@@ -9,290 +9,205 @@
#include <cppeditor/cppeditorconstants.h>
#include <cppeditor/cppeditortr.h>
#include <cppeditor/cpplocatorfilter.h>
-#include <cppeditor/cppmodelmanager.h>
-#include <cppeditor/indexitem.h>
-#include <languageclient/languageclientutils.h>
+
+#include <extensionsystem/pluginmanager.h>
+
+#include <languageclient/currentdocumentsymbolsrequest.h>
#include <languageclient/locatorfilter.h>
-#include <projectexplorer/session.h>
-#include <utils/link.h>
-#include <set>
-#include <tuple>
+#include <utils/algorithm.h>
+#include <utils/async.h>
+
+#include <QHash>
+using namespace Core;
using namespace LanguageClient;
using namespace LanguageServerProtocol;
+using namespace ProjectExplorer;
+using namespace TextEditor;
using namespace Utils;
-namespace ClangCodeModel {
-namespace Internal {
+namespace ClangCodeModel::Internal {
const int MaxResultCount = 10000;
-class CppLocatorFilter : public CppEditor::CppLocatorFilter
-{
-public:
- CppLocatorFilter()
- : CppEditor::CppLocatorFilter(CppEditor::CppModelManager::instance()->locatorData())
- {
- setId({});
- setDisplayName({});
- setDefaultShortcutString({});
- setEnabled(false);
- setHidden(true);
- }
-};
-
-class LspWorkspaceFilter : public WorkspaceLocatorFilter
-{
-public:
- LspWorkspaceFilter()
- {
- setId({});
- setDisplayName({});
- setDefaultShortcutString({});
- setEnabled(false);
- setHidden(true);
- setMaxResultCount(MaxResultCount);
- }
-};
-
-
-class CppClassesFilter : public CppEditor::CppClassesFilter
-{
-public:
- CppClassesFilter()
- : CppEditor::CppClassesFilter(CppEditor::CppModelManager::instance()->locatorData())
- {
- setId({});
- setDisplayName({});
- setDefaultShortcutString({});
- setEnabled(false);
- setHidden(true);
- }
-};
-
-class LspClassesFilter : public WorkspaceClassLocatorFilter
-{
-public:
- LspClassesFilter() {
- setId({});
- setDisplayName({});
- setDefaultShortcutString({});
- setEnabled(false);
- setHidden(true);
- setMaxResultCount(MaxResultCount);
- }
-};
-
-class CppFunctionsFilter : public CppEditor::CppFunctionsFilter
-{
-public:
- CppFunctionsFilter()
- : CppEditor::CppFunctionsFilter(CppEditor::CppModelManager::instance()->locatorData())
- {
- setId({});
- setDisplayName({});
- setDefaultShortcutString({});
- setEnabled(false);
- setHidden(true);
- }
-};
-
-class LspFunctionsFilter : public WorkspaceMethodLocatorFilter
-{
-public:
- LspFunctionsFilter()
- {
- setId({});
- setDisplayName({});
- setDefaultShortcutString({});
- setEnabled(false);
- setHidden(true);
- setMaxResultCount(MaxResultCount);
- }
-};
-
-
-ClangGlobalSymbolFilter::ClangGlobalSymbolFilter()
- : ClangGlobalSymbolFilter(new CppLocatorFilter, new LspWorkspaceFilter)
-{
-}
-
-ClangGlobalSymbolFilter::ClangGlobalSymbolFilter(ILocatorFilter *cppFilter,
- WorkspaceLocatorFilter *lspFilter)
- : m_cppFilter(cppFilter), m_lspFilter(lspFilter)
+ClangdAllSymbolsFilter::ClangdAllSymbolsFilter()
{
setId(CppEditor::Constants::LOCATOR_FILTER_ID);
setDisplayName(::CppEditor::Tr::tr(CppEditor::Constants::LOCATOR_FILTER_DISPLAY_NAME));
+ setDescription(::CppEditor::Tr::tr(CppEditor::Constants::LOCATOR_FILTER_DESCRIPTION));
setDefaultShortcutString(":");
- setDefaultIncludedByDefault(false);
}
-ClangGlobalSymbolFilter::~ClangGlobalSymbolFilter()
+LocatorMatcherTasks ClangdAllSymbolsFilter::matchers()
{
- delete m_cppFilter;
- delete m_lspFilter;
+ return CppEditor::cppMatchers(MatcherType::AllSymbols)
+ + LanguageClient::languageClientMatchers(MatcherType::AllSymbols,
+ ClangModelManagerSupport::clientsForOpenProjects(), MaxResultCount);
}
-void ClangGlobalSymbolFilter::prepareSearch(const QString &entry)
+ClangdClassesFilter::ClangdClassesFilter()
{
- m_cppFilter->prepareSearch(entry);
- QList<Client *> clients;
- for (ProjectExplorer::Project * const project : ProjectExplorer::SessionManager::projects()) {
- if (Client * const client = ClangModelManagerSupport::clientForProject(project))
- clients << client;
- }
- if (!clients.isEmpty())
- m_lspFilter->prepareSearch(entry, clients);
+ setId(CppEditor::Constants::CLASSES_FILTER_ID);
+ setDisplayName(::CppEditor::Tr::tr(CppEditor::Constants::CLASSES_FILTER_DISPLAY_NAME));
+ setDescription(::CppEditor::Tr::tr(CppEditor::Constants::CLASSES_FILTER_DESCRIPTION));
+ setDefaultShortcutString("c");
}
-QList<Core::LocatorFilterEntry> ClangGlobalSymbolFilter::matchesFor(
- QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
+LocatorMatcherTasks ClangdClassesFilter::matchers()
{
- QList<Core::LocatorFilterEntry> matches = m_cppFilter->matchesFor(future, entry);
- const QList<Core::LocatorFilterEntry> lspMatches = m_lspFilter->matchesFor(future, entry);
- if (!lspMatches.isEmpty()) {
- std::set<std::tuple<FilePath, int, int>> locations;
- for (const auto &entry : std::as_const(matches)) {
- const CppEditor::IndexItem::Ptr item
- = qvariant_cast<CppEditor::IndexItem::Ptr>(entry.internalData);
- locations.insert(std::make_tuple(item->filePath(), item->line(), item->column()));
- }
- for (const auto &entry : lspMatches) {
- if (!entry.internalData.canConvert<Link>())
- continue;
- const auto link = qvariant_cast<Link>(entry.internalData);
- if (locations.find(std::make_tuple(link.targetFilePath, link.targetLine,
- link.targetColumn)) == locations.cend()) {
- matches << entry; // TODO: Insert sorted?
- }
- }
- }
-
- return matches;
+ return CppEditor::cppMatchers(MatcherType::Classes)
+ + LanguageClient::languageClientMatchers(MatcherType::Classes,
+ ClangModelManagerSupport::clientsForOpenProjects(), MaxResultCount);
}
-void ClangGlobalSymbolFilter::accept(const Core::LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const
+ClangdFunctionsFilter::ClangdFunctionsFilter()
{
- if (qvariant_cast<CppEditor::IndexItem::Ptr>(selection.internalData))
- m_cppFilter->accept(selection, newText, selectionStart, selectionLength);
- else
- m_lspFilter->accept(selection, newText, selectionStart, selectionLength);
+ setId(CppEditor::Constants::FUNCTIONS_FILTER_ID);
+ setDisplayName(::CppEditor::Tr::tr(CppEditor::Constants::FUNCTIONS_FILTER_DISPLAY_NAME));
+ setDescription(::CppEditor::Tr::tr(CppEditor::Constants::FUNCTIONS_FILTER_DESCRIPTION));
+ setDefaultShortcutString("m");
}
-
-ClangClassesFilter::ClangClassesFilter()
- : ClangGlobalSymbolFilter(new CppClassesFilter, new LspClassesFilter)
+LocatorMatcherTasks ClangdFunctionsFilter::matchers()
{
- setId(CppEditor::Constants::CLASSES_FILTER_ID);
- setDisplayName(::CppEditor::Tr::tr(CppEditor::Constants::CLASSES_FILTER_DISPLAY_NAME));
- setDefaultShortcutString("c");
- setDefaultIncludedByDefault(false);
+ return CppEditor::cppMatchers(MatcherType::Functions)
+ + LanguageClient::languageClientMatchers(MatcherType::Functions,
+ ClangModelManagerSupport::clientsForOpenProjects(), MaxResultCount);
}
-ClangFunctionsFilter::ClangFunctionsFilter()
- : ClangGlobalSymbolFilter(new CppFunctionsFilter, new LspFunctionsFilter)
+ClangdCurrentDocumentFilter::ClangdCurrentDocumentFilter()
{
- setId(CppEditor::Constants::FUNCTIONS_FILTER_ID);
- setDisplayName(::CppEditor::Tr::tr(CppEditor::Constants::FUNCTIONS_FILTER_DISPLAY_NAME));
- setDefaultShortcutString("m");
- setDefaultIncludedByDefault(false);
+ setId(CppEditor::Constants::CURRENT_DOCUMENT_FILTER_ID);
+ setDisplayName(::CppEditor::Tr::tr(CppEditor::Constants::CURRENT_DOCUMENT_FILTER_DISPLAY_NAME));
+ setDescription(::CppEditor::Tr::tr(CppEditor::Constants::CURRENT_DOCUMENT_FILTER_DESCRIPTION));
+ setDefaultShortcutString(".");
+ setPriority(High);
+ setEnabled(false);
+ connect(EditorManager::instance(), &EditorManager::currentEditorChanged,
+ this, [this](const IEditor *editor) { setEnabled(editor); });
}
-class LspCurrentDocumentFilter : public DocumentLocatorFilter
+static void filterCurrentResults(QPromise<void> &promise, const LocatorStorage &storage,
+ const CurrentDocumentSymbolsData &currentSymbolsData,
+ const QString &contents)
{
-public:
- LspCurrentDocumentFilter()
- {
- setId({});
- setDisplayName({});
- setDefaultShortcutString({});
- setEnabled(false);
- setHidden(true);
- forceUse();
- }
-
-private:
- Core::LocatorFilterEntry generateLocatorEntry(const DocumentSymbol &info,
- const Core::LocatorFilterEntry &parent) override
+ Q_UNUSED(promise)
+ struct Entry
{
- Core::LocatorFilterEntry entry;
- entry.filter = this;
+ LocatorFilterEntry entry;
+ DocumentSymbol symbol;
+ };
+ QList<Entry> docEntries;
+
+ const auto docSymbolModifier = [&docEntries](LocatorFilterEntry &entry,
+ const DocumentSymbol &info,
+ const LocatorFilterEntry &parent) {
entry.displayName = ClangdClient::displayNameFromDocumentSymbol(
- static_cast<SymbolKind>(info.kind()), info.name(),
- info.detail().value_or(QString()));
- const Position &pos = info.range().start();
- entry.internalData = QVariant::fromValue(LineColumn(pos.line(), pos.character()));
+ static_cast<SymbolKind>(info.kind()), info.name(),
+ info.detail().value_or(QString()));
entry.extraInfo = parent.extraInfo;
if (!entry.extraInfo.isEmpty())
entry.extraInfo.append("::");
entry.extraInfo.append(parent.displayName);
// TODO: Can we extend clangd to send visibility information?
- entry.displayIcon = LanguageClient::symbolIcon(info.kind());
-
+ docEntries.append({entry, info});
return entry;
+ };
+ // TODO: Pass promise into currentSymbols
+ const LocatorFilterEntries allMatches = LanguageClient::currentDocumentSymbols(storage.input(),
+ currentSymbolsData, docSymbolModifier);
+ if (docEntries.isEmpty()) {
+ storage.reportOutput(allMatches);
+ return; // SymbolInformation case
}
-};
-
-class ClangdCurrentDocumentFilter::Private
-{
-public:
- ~Private() { delete cppFilter; }
-
- Core::ILocatorFilter * const cppFilter
- = CppEditor::CppModelManager::createAuxiliaryCurrentDocumentFilter();
- LspCurrentDocumentFilter lspFilter;
- Core::ILocatorFilter *activeFilter = nullptr;
-};
+ QTC_CHECK(docEntries.size() == allMatches.size());
+ QHash<QString, QList<Entry>> possibleDuplicates;
+ for (const Entry &e : std::as_const(docEntries))
+ possibleDuplicates[e.entry.displayName + e.entry.extraInfo] << e;
+ const QTextDocument doc(contents);
+ for (auto it = possibleDuplicates.cbegin(); it != possibleDuplicates.cend(); ++it) {
+ const QList<Entry> &duplicates = it.value();
+ if (duplicates.size() == 1)
+ continue;
+ QList<Entry> declarations;
+ QList<Entry> definitions;
+ for (const Entry &candidate : duplicates) {
+ const DocumentSymbol symbol = candidate.symbol;
+ const SymbolKind kind = static_cast<SymbolKind>(symbol.kind());
+ if (kind != SymbolKind::Class && kind != SymbolKind::Function)
+ break;
+ const Range range = symbol.range();
+ const Range selectionRange = symbol.selectionRange();
+ if (kind == SymbolKind::Class) {
+ if (range.end() == selectionRange.end())
+ declarations << candidate;
+ else
+ definitions << candidate;
+ continue;
+ }
+ const int startPos = selectionRange.end().toPositionInDocument(&doc);
+ const int endPos = range.end().toPositionInDocument(&doc);
+ const QString functionBody = contents.mid(startPos, endPos - startPos);
+
+ // Hacky, but I don't see anything better.
+ if (functionBody.contains('{') && functionBody.contains('}'))
+ definitions << candidate;
+ else
+ declarations << candidate;
+ }
+ if (definitions.size() == 1
+ && declarations.size() + definitions.size() == duplicates.size()) {
+ for (const Entry &decl : std::as_const(declarations)) {
+ Utils::erase(docEntries, [&decl](const Entry &e) {
+ return e.symbol == decl.symbol;
+ });
+ }
+ }
+ }
+ storage.reportOutput(Utils::transform(docEntries,
+ [](const Entry &entry) { return entry.entry; }));
+}
-ClangdCurrentDocumentFilter::ClangdCurrentDocumentFilter() : d(new Private)
+LocatorMatcherTask currentDocumentMatcher()
{
- setId(CppEditor::Constants::CURRENT_DOCUMENT_FILTER_ID);
- setDisplayName(::CppEditor::Tr::tr(CppEditor::Constants::CURRENT_DOCUMENT_FILTER_DISPLAY_NAME));
- setDefaultShortcutString(".");
- setPriority(High);
- setDefaultIncludedByDefault(false);
- setEnabled(false);
- connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
- this, [this](const Core::IEditor *editor) { setEnabled(editor); });
-}
+ using namespace Tasking;
-ClangdCurrentDocumentFilter::~ClangdCurrentDocumentFilter() { delete d; }
+ TreeStorage<LocatorStorage> storage;
+ TreeStorage<CurrentDocumentSymbolsData> resultStorage;
-void ClangdCurrentDocumentFilter::updateCurrentClient()
-{
- d->lspFilter.updateCurrentClient();
-}
+ const auto onQuerySetup = [=](CurrentDocumentSymbolsRequest &request) {
+ Q_UNUSED(request)
+ };
+ const auto onQueryDone = [resultStorage](const CurrentDocumentSymbolsRequest &request) {
+ *resultStorage = request.currentDocumentSymbolsData();
+ };
-void ClangdCurrentDocumentFilter::prepareSearch(const QString &entry)
-{
- const auto doc = TextEditor::TextDocument::currentTextDocument();
- QTC_ASSERT(doc, return);
- if (const ClangdClient * const client = ClangModelManagerSupport::clientForFile
- (doc->filePath()); client && client->reachable()) {
- d->activeFilter = &d->lspFilter;
- } else {
- d->activeFilter = d->cppFilter;
- }
- d->activeFilter->prepareSearch(entry);
-}
+ const auto onFilterSetup = [=](Async<void> &async) {
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(filterCurrentResults, *storage, *resultStorage,
+ TextDocument::currentTextDocument()->plainText());
+ };
-QList<Core::LocatorFilterEntry> ClangdCurrentDocumentFilter::matchesFor(
- QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
-{
- QTC_ASSERT(d->activeFilter, return {});
- return d->activeFilter->matchesFor(future, entry);
+ const Group root {
+ Storage(resultStorage),
+ CurrentDocumentSymbolsRequestTask(onQuerySetup, onQueryDone),
+ AsyncTask<void>(onFilterSetup)
+ };
+ return {root, storage};
}
-void ClangdCurrentDocumentFilter::accept(const Core::LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const
+LocatorMatcherTasks ClangdCurrentDocumentFilter::matchers()
{
- QTC_ASSERT(d->activeFilter, return);
- d->activeFilter->accept(selection, newText, selectionStart, selectionLength);
+ const auto doc = TextDocument::currentTextDocument();
+ QTC_ASSERT(doc, return {});
+ if (const ClangdClient *client = ClangModelManagerSupport::clientForFile(doc->filePath());
+ client && client->reachable()) {
+ return {currentDocumentMatcher()};
+ }
+ return CppEditor::cppMatchers(MatcherType::CurrentDocumentSymbols);
}
-} // namespace Internal
-} // namespace ClangCodeModel
+} // namespace ClangCodeModel::Internal
diff --git a/src/plugins/clangcodemodel/clangdlocatorfilters.h b/src/plugins/clangcodemodel/clangdlocatorfilters.h
index f7deacc760..1aba669417 100644
--- a/src/plugins/clangcodemodel/clangdlocatorfilters.h
+++ b/src/plugins/clangcodemodel/clangdlocatorfilters.h
@@ -5,60 +5,42 @@
#include <coreplugin/locator/ilocatorfilter.h>
-namespace LanguageClient { class WorkspaceLocatorFilter; }
+namespace ClangCodeModel::Internal {
-namespace ClangCodeModel {
-namespace Internal {
-
-class ClangGlobalSymbolFilter : public Core::ILocatorFilter
+class ClangdAllSymbolsFilter : public Core::ILocatorFilter
{
public:
- ClangGlobalSymbolFilter();
- ClangGlobalSymbolFilter(Core::ILocatorFilter *cppFilter,
- LanguageClient::WorkspaceLocatorFilter *lspFilter);
- ~ClangGlobalSymbolFilter() override;
+ ClangdAllSymbolsFilter();
private:
- void prepareSearch(const QString &entry) override;
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const override;
-
- Core::ILocatorFilter * const m_cppFilter;
- LanguageClient::WorkspaceLocatorFilter * const m_lspFilter;
+ Core::LocatorMatcherTasks matchers() final;
};
-class ClangClassesFilter : public ClangGlobalSymbolFilter
+class ClangdClassesFilter : public Core::ILocatorFilter
{
public:
- ClangClassesFilter();
+ ClangdClassesFilter();
+
+private:
+ Core::LocatorMatcherTasks matchers() final;
};
-class ClangFunctionsFilter : public ClangGlobalSymbolFilter
+class ClangdFunctionsFilter : public Core::ILocatorFilter
{
public:
- ClangFunctionsFilter();
+ ClangdFunctionsFilter();
+
+private:
+ Core::LocatorMatcherTasks matchers() final;
};
class ClangdCurrentDocumentFilter : public Core::ILocatorFilter
{
public:
ClangdCurrentDocumentFilter();
- ~ClangdCurrentDocumentFilter() override;
-
- void updateCurrentClient();
private:
- void prepareSearch(const QString &entry) override;
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const override;
-
- class Private;
- Private * const d;
+ Core::LocatorMatcherTasks matchers() final;
};
-} // namespace Internal
-} // namespace ClangCodeModel
+} // namespace ClangCodeModel::Internal
diff --git a/src/plugins/clangcodemodel/clangdqpropertyhighlighter.cpp b/src/plugins/clangcodemodel/clangdqpropertyhighlighter.cpp
index e6e32b0e9e..4a8db524ce 100644
--- a/src/plugins/clangcodemodel/clangdqpropertyhighlighter.cpp
+++ b/src/plugins/clangcodemodel/clangdqpropertyhighlighter.cpp
@@ -394,10 +394,10 @@ void QPropertyHighlighter::Private::addResult(TextStyle textStyle, int symbolOff
const Symbol &s = parser.symbol_lookup(symbolOffset);
int line, column;
Utils::Text::convertPosition(document, position + s.from, &line, &column);
- if (line > 0 && column > 0) {
+ if (line > 0 && column >= 0) {
TextStyles styles;
styles.mainStyle = textStyle;
- results << HighlightingResult(line, column, s.len, styles);
+ results << HighlightingResult(line, column + 1, s.len, styles);
}
}
diff --git a/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp b/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp
index cff8253de6..e1b82ab377 100644
--- a/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp
+++ b/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp
@@ -112,7 +112,7 @@ static QList<BlockRange> cleanupDisabledCode(HighlightingResults &results, const
class ExtraHighlightingResultsCollector
{
public:
- ExtraHighlightingResultsCollector(QFutureInterface<HighlightingResult> &future,
+ ExtraHighlightingResultsCollector(QPromise<HighlightingResult> &promise,
HighlightingResults &results,
const Utils::FilePath &filePath, const ClangdAstNode &ast,
const QTextDocument *doc, const QString &docContent,
@@ -131,7 +131,7 @@ private:
void collectFromNode(const ClangdAstNode &node);
void visitNode(const ClangdAstNode&node);
- QFutureInterface<HighlightingResult> &m_future;
+ QPromise<HighlightingResult> &m_promise;
HighlightingResults &m_results;
const Utils::FilePath m_filePath;
const ClangdAstNode &m_ast;
@@ -142,7 +142,7 @@ private:
};
void doSemanticHighlighting(
- QFutureInterface<HighlightingResult> &future,
+ QPromise<HighlightingResult> &promise,
const Utils::FilePath &filePath,
const QList<ExpandedSemanticToken> &tokens,
const QString &docContents,
@@ -153,10 +153,8 @@ void doSemanticHighlighting(
const TaskTimer &taskTimer)
{
ThreadedSubtaskTimer t("highlighting", taskTimer);
- if (future.isCanceled()) {
- future.reportFinished();
+ if (promise.isCanceled())
return;
- }
const QTextDocument doc(docContents);
const auto tokenRange = [&doc](const ExpandedSemanticToken &token) {
@@ -399,13 +397,13 @@ void doSemanticHighlighting(
};
auto results = QtConcurrent::blockingMapped<HighlightingResults>(tokens, safeToResult);
const QList<BlockRange> ifdefedOutBlocks = cleanupDisabledCode(results, &doc, docContents);
- ExtraHighlightingResultsCollector(future, results, filePath, ast, &doc, docContents,
+ ExtraHighlightingResultsCollector(promise, results, filePath, ast, &doc, docContents,
clangdVersion).collect();
Utils::erase(results, [](const HighlightingResult &res) {
// QTCREATORBUG-28639
return res.textStyles.mainStyle == C_TEXT && res.textStyles.mixinStyles.empty();
});
- if (!future.isCanceled()) {
+ if (!promise.isCanceled()) {
qCInfo(clangdLogHighlight) << "reporting" << results.size() << "highlighting results";
QMetaObject::invokeMethod(textDocument, [textDocument, ifdefedOutBlocks, docRevision] {
if (textDocument && textDocument->document()->revision() == docRevision)
@@ -423,16 +421,20 @@ void doSemanticHighlighting(
if (ClangdClient * const client = ClangModelManagerSupport::clientForFile(filePath))
client->setVirtualRanges(filePath, virtualRanges, docRevision);
}, Qt::QueuedConnection);
- future.reportResults(QVector<HighlightingResult>(results.cbegin(), results.cend()));
+#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
+ promise.addResults(results);
+#else
+ for (const HighlightingResult &r : results)
+ promise.addResult(r);
+#endif
}
- future.reportFinished();
}
ExtraHighlightingResultsCollector::ExtraHighlightingResultsCollector(
- QFutureInterface<HighlightingResult> &future, HighlightingResults &results,
+ QPromise<HighlightingResult> &promise, HighlightingResults &results,
const Utils::FilePath &filePath, const ClangdAstNode &ast, const QTextDocument *doc,
const QString &docContent, const QVersionNumber &clangdVersion)
- : m_future(future), m_results(results), m_filePath(filePath), m_ast(ast), m_doc(doc),
+ : m_promise(promise), m_results(results), m_filePath(filePath), m_ast(ast), m_doc(doc),
m_docContent(docContent), m_clangdVersion(clangdVersion.majorVersion())
{
}
@@ -573,10 +575,12 @@ void ExtraHighlightingResultsCollector::insertAngleBracketInfo(int searchStart1,
result.useTextSyles = true;
result.textStyles.mainStyle = C_PUNCTUATION;
Utils::Text::convertPosition(m_doc, absOpeningAngleBracketPos, &result.line, &result.column);
+ ++result.column;
result.length = 1;
result.kind = CppEditor::SemanticHighlighter::AngleBracketOpen;
insertResult(result);
Utils::Text::convertPosition(m_doc, absClosingAngleBracketPos, &result.line, &result.column);
+ ++result.column;
result.kind = CppEditor::SemanticHighlighter::AngleBracketClose;
insertResult(result);
}
@@ -648,10 +652,12 @@ void ExtraHighlightingResultsCollector::collectFromNode(const ClangdAstNode &nod
result.textStyles.mainStyle = C_PUNCTUATION;
result.textStyles.mixinStyles.push_back(C_OPERATOR);
Utils::Text::convertPosition(m_doc, absQuestionMarkPos, &result.line, &result.column);
+ ++result.column;
result.length = 1;
result.kind = CppEditor::SemanticHighlighter::TernaryIf;
insertResult(result);
Utils::Text::convertPosition(m_doc, absColonPos, &result.line, &result.column);
+ ++result.column;
result.kind = CppEditor::SemanticHighlighter::TernaryElse;
insertResult(result);
return;
@@ -839,10 +845,12 @@ void ExtraHighlightingResultsCollector::collectFromNode(const ClangdAstNode &nod
Utils::Text::convertPosition(m_doc,
nodeStartPos + openingBracketOffset,
&result.line, &result.column);
+ ++result.column;
insertResult(result);
Utils::Text::convertPosition(m_doc,
nodeStartPos + closingBracketOffset,
&result.line, &result.column);
+ ++result.column;
insertResult(result);
}
return;
@@ -887,6 +895,7 @@ void ExtraHighlightingResultsCollector::collectFromNode(const ClangdAstNode &nod
const int opStringOffsetInDoc = nodeStartPos + opStringOffset
+ detail.length() - opStringLen;
Utils::Text::convertPosition(m_doc, opStringOffsetInDoc, &result.line, &result.column);
+ ++result.column;
result.length = opStringLen;
if (isArray || isCall)
result.length = 1;
@@ -908,15 +917,17 @@ void ExtraHighlightingResultsCollector::collectFromNode(const ClangdAstNode &nod
return;
Utils::Text::convertPosition(m_doc, nodeStartPos + openingParenOffset,
&result.line, &result.column);
+ ++result.column;
insertResult(result);
Utils::Text::convertPosition(m_doc, nodeStartPos + closingParenOffset,
&result.line, &result.column);
+ ++result.column;
insertResult(result);
}
void ExtraHighlightingResultsCollector::visitNode(const ClangdAstNode &node)
{
- if (m_future.isCanceled())
+ if (m_promise.isCanceled())
return;
const ClangdAstNode::FileStatus prevFileStatus = m_currentFileStatus;
m_currentFileStatus = node.fileStatus(m_filePath);
diff --git a/src/plugins/clangcodemodel/clangdsemantichighlighting.h b/src/plugins/clangcodemodel/clangdsemantichighlighting.h
index 10bc4b8d09..a7f667d459 100644
--- a/src/plugins/clangcodemodel/clangdsemantichighlighting.h
+++ b/src/plugins/clangcodemodel/clangdsemantichighlighting.h
@@ -3,11 +3,15 @@
#pragma once
-#include <QFutureInterface>
#include <QLoggingCategory>
#include <QPointer>
#include <QVersionNumber>
+QT_BEGIN_NAMESPACE
+template <typename T>
+class QPromise;
+QT_END_NAMESPACE
+
namespace LanguageClient { class ExpandedSemanticToken; }
namespace TextEditor {
class HighlightingResult;
@@ -21,7 +25,7 @@ class TaskTimer;
Q_DECLARE_LOGGING_CATEGORY(clangdLogHighlight);
void doSemanticHighlighting(
- QFutureInterface<TextEditor::HighlightingResult> &future,
+ QPromise<TextEditor::HighlightingResult> &promise,
const Utils::FilePath &filePath,
const QList<LanguageClient::ExpandedSemanticToken> &tokens,
const QString &docContents,
diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
index 248e97349f..42e77c11a3 100644
--- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
+++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
@@ -16,6 +16,7 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
+#include <coreplugin/session.h>
#include <cppeditor/cppcodemodelsettings.h>
#include <cppeditor/cppeditorconstants.h>
@@ -27,22 +28,23 @@
#include <cppeditor/editordocumenthandle.h>
#include <languageclient/languageclientmanager.h>
+#include <languageclient/locatorfilter.h>
#include <texteditor/quickfix.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/infobar.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <QApplication>
#include <QLabel>
@@ -51,22 +53,24 @@
#include <QTimer>
#include <QtDebug>
+using namespace Core;
using namespace CppEditor;
using namespace LanguageClient;
+using namespace ProjectExplorer;
using namespace Utils;
namespace ClangCodeModel::Internal {
-static CppEditor::CppModelManager *cppModelManager()
+static CppModelManager *cppModelManager()
{
- return CppEditor::CppModelManager::instance();
+ return CppModelManager::instance();
}
-static ProjectExplorer::Project *fallbackProject()
+static Project *fallbackProject()
{
- if (ProjectExplorer::Project * const p = ProjectExplorer::ProjectTree::currentProject())
+ if (Project * const p = ProjectTree::currentProject())
return p;
- return ProjectExplorer::SessionManager::startupProject();
+ return ProjectManager::startupProject();
}
static bool sessionModeEnabled()
@@ -76,18 +80,17 @@ static bool sessionModeEnabled()
static const QList<TextEditor::TextDocument *> allCppDocuments()
{
- const auto isCppDocument = Utils::equal(&Core::IDocument::id,
- Utils::Id(CppEditor::Constants::CPPEDITOR_ID));
- const QList<Core::IDocument *> documents
- = Utils::filtered(Core::DocumentModel::openedDocuments(), isCppDocument);
+ const auto isCppDocument = Utils::equal(&IDocument::id, Id(CppEditor::Constants::CPPEDITOR_ID));
+ const QList<IDocument *> documents = Utils::filtered(DocumentModel::openedDocuments(),
+ isCppDocument);
return Utils::qobject_container_cast<TextEditor::TextDocument *>(documents);
}
-static const QList<ProjectExplorer::Project *> projectsForClient(const Client *client)
+static const QList<Project *> projectsForClient(const Client *client)
{
- QList<ProjectExplorer::Project *> projects;
+ QList<Project *> projects;
if (sessionModeEnabled()) {
- for (ProjectExplorer::Project * const p : ProjectExplorer::SessionManager::projects()) {
+ for (Project * const p : ProjectManager::projects()) {
if (ClangdProjectSettings(p).settings().useClangd)
projects << p;
}
@@ -99,7 +102,7 @@ static const QList<ProjectExplorer::Project *> projectsForClient(const Client *c
static bool fileIsProjectBuildArtifact(const Client *client, const FilePath &filePath)
{
- for (const ProjectExplorer::Project * const p : projectsForClient(client)) {
+ for (const Project * const p : projectsForClient(client)) {
if (const auto t = p->activeTarget()) {
if (const auto bc = t->activeBuildConfiguration()) {
if (filePath.isChildOf(bc->buildDirectory()))
@@ -144,15 +147,15 @@ static void checkSystemForClangdSuitability()
"You can enable/disable and fine-tune clangd <a href=\"dummy\">here</a>."));
label->setWordWrap(true);
QObject::connect(label, &QLabel::linkActivated, [] {
- Core::ICore::showOptionsDialog(CppEditor::Constants::CPP_CLANGD_SETTINGS_ID);
+ ICore::showOptionsDialog(CppEditor::Constants::CPP_CLANGD_SETTINGS_ID);
});
return label;
});
info.addCustomButton(Tr::tr("Enable Anyway"), [clangdWarningSetting] {
ClangdSettings::setUseClangdAndSave(true);
- Core::ICore::infoBar()->removeInfo(clangdWarningSetting);
+ ICore::infoBar()->removeInfo(clangdWarningSetting);
});
- Core::ICore::infoBar()->addInfo(info);
+ ICore::infoBar()->addInfo(info);
}
static void updateParserConfig(ClangdClient *client)
@@ -170,8 +173,8 @@ static void updateParserConfig(ClangdClient *client)
static bool projectIsParsing(const ClangdClient *client)
{
- for (const ProjectExplorer::Project * const p : projectsForClient(client)) {
- const ProjectExplorer::BuildSystem * const bs = p && p->activeTarget()
+ for (const Project * const p : projectsForClient(client)) {
+ const BuildSystem * const bs = p && p->activeTarget()
? p->activeTarget()->buildSystem() : nullptr;
if (bs && (bs->isParsing() || bs->isWaitingForParse()))
return true;
@@ -179,7 +182,6 @@ static bool projectIsParsing(const ClangdClient *client)
return false;
}
-
ClangModelManagerSupport::ClangModelManagerSupport()
: m_clientRestartTimer(new QTimer(this))
{
@@ -200,24 +202,37 @@ ClangModelManagerSupport::ClangModelManagerSupport()
setupClangdConfigFile();
checkSystemForClangdSuitability();
cppModelManager()->setCurrentDocumentFilter(std::make_unique<ClangdCurrentDocumentFilter>());
- cppModelManager()->setLocatorFilter(std::make_unique<ClangGlobalSymbolFilter>());
- cppModelManager()->setClassesFilter(std::make_unique<ClangClassesFilter>());
- cppModelManager()->setFunctionsFilter(std::make_unique<ClangFunctionsFilter>());
+ cppModelManager()->setLocatorFilter(std::make_unique<ClangdAllSymbolsFilter>());
+ cppModelManager()->setClassesFilter(std::make_unique<ClangdClassesFilter>());
+ cppModelManager()->setFunctionsFilter(std::make_unique<ClangdFunctionsFilter>());
+ // Setup matchers
+ LocatorMatcher::addMatcherCreator(MatcherType::AllSymbols, [] {
+ return LanguageClient::languageClientMatchers(
+ MatcherType::AllSymbols, clientsForOpenProjects(), 10000);
+ });
+ LocatorMatcher::addMatcherCreator(MatcherType::Classes, [] {
+ return LanguageClient::languageClientMatchers(
+ MatcherType::Classes, clientsForOpenProjects(), 10000);
+ });
+ LocatorMatcher::addMatcherCreator(MatcherType::Functions, [] {
+ return LanguageClient::languageClientMatchers(
+ MatcherType::Functions, clientsForOpenProjects(), 10000);
+ });
- Core::EditorManager *editorManager = Core::EditorManager::instance();
- connect(editorManager, &Core::EditorManager::editorOpened,
+ EditorManager *editorManager = EditorManager::instance();
+ connect(editorManager, &EditorManager::editorOpened,
this, &ClangModelManagerSupport::onEditorOpened);
- connect(editorManager, &Core::EditorManager::currentEditorChanged,
+ connect(editorManager, &EditorManager::currentEditorChanged,
this, &ClangModelManagerSupport::onCurrentEditorChanged);
- CppEditor::CppModelManager *modelManager = cppModelManager();
- connect(modelManager, &CppEditor::CppModelManager::abstractEditorSupportContentsUpdated,
+ CppModelManager *modelManager = cppModelManager();
+ connect(modelManager, &CppModelManager::abstractEditorSupportContentsUpdated,
this, &ClangModelManagerSupport::onAbstractEditorSupportContentsUpdated);
- connect(modelManager, &CppEditor::CppModelManager::abstractEditorSupportRemoved,
+ connect(modelManager, &CppModelManager::abstractEditorSupportRemoved,
this, &ClangModelManagerSupport::onAbstractEditorSupportRemoved);
- connect(modelManager, &CppEditor::CppModelManager::projectPartsUpdated,
+ connect(modelManager, &CppModelManager::projectPartsUpdated,
this, &ClangModelManagerSupport::onProjectPartsUpdated);
- connect(modelManager, &CppEditor::CppModelManager::projectPartsRemoved,
+ connect(modelManager, &CppModelManager::projectPartsRemoved,
this, &ClangModelManagerSupport::onProjectPartsRemoved);
connect(modelManager, &CppModelManager::fallbackProjectPartUpdated, this, [this] {
if (sessionModeEnabled())
@@ -228,26 +243,23 @@ ClangModelManagerSupport::ClangModelManagerSupport()
}
});
- auto *sessionManager = ProjectExplorer::SessionManager::instance();
- connect(sessionManager, &ProjectExplorer::SessionManager::projectRemoved,
- this, [this] {
+ auto projectManager = ProjectManager::instance();
+ connect(projectManager, &ProjectManager::projectRemoved, this, [this] {
if (!sessionModeEnabled())
claimNonProjectSources(clientForProject(fallbackProject()));
});
- connect(sessionManager, &ProjectExplorer::SessionManager::sessionLoaded,
- this, [this] {
+ connect(SessionManager::instance(), &SessionManager::sessionLoaded, this, [this] {
if (sessionModeEnabled())
onClangdSettingsChanged();
});
- CppEditor::ClangdSettings::setDefaultClangdPath(Core::ICore::clangdExecutable(CLANG_BINDIR));
- connect(&CppEditor::ClangdSettings::instance(), &CppEditor::ClangdSettings::changed,
+ ClangdSettings::setDefaultClangdPath(ICore::clangdExecutable(CLANG_BINDIR));
+ connect(&ClangdSettings::instance(), &ClangdSettings::changed,
this, &ClangModelManagerSupport::onClangdSettingsChanged);
- if (CppEditor::ClangdSettings::instance().useClangd())
+ if (ClangdSettings::instance().useClangd())
new ClangdClient(nullptr, {});
- m_generatorSynchronizer.setCancelOnWait(true);
new ClangdQuickFixFactory(); // memory managed by CppEditor::g_cppQuickFixFactories
}
@@ -256,9 +268,9 @@ ClangModelManagerSupport::~ClangModelManagerSupport()
m_generatorSynchronizer.waitForFinished();
}
-void ClangModelManagerSupport::followSymbol(const CppEditor::CursorInEditor &data,
- const LinkHandler &processLinkCallback, bool resolveTarget,
- bool inNextSplit)
+void ClangModelManagerSupport::followSymbol(const CursorInEditor &data,
+ const LinkHandler &processLinkCallback,
+ bool resolveTarget, bool inNextSplit)
{
if (ClangdClient * const client = clientForFile(data.filePath());
client && client->isFullyIndexed()) {
@@ -271,7 +283,7 @@ void ClangModelManagerSupport::followSymbol(const CppEditor::CursorInEditor &dat
CppModelManager::Backend::Builtin);
}
-void ClangModelManagerSupport::followSymbolToType(const CppEditor::CursorInEditor &data,
+void ClangModelManagerSupport::followSymbolToType(const CursorInEditor &data,
const LinkHandler &processLinkCallback,
bool inNextSplit)
{
@@ -284,8 +296,8 @@ void ClangModelManagerSupport::followSymbolToType(const CppEditor::CursorInEdito
CppModelManager::Backend::Builtin);
}
-void ClangModelManagerSupport::switchDeclDef(const CppEditor::CursorInEditor &data,
- const LinkHandler &processLinkCallback)
+void ClangModelManagerSupport::switchDeclDef(const CursorInEditor &data,
+ const LinkHandler &processLinkCallback)
{
if (ClangdClient * const client = clientForFile(data.filePath());
client && client->isFullyIndexed()) {
@@ -297,9 +309,9 @@ void ClangModelManagerSupport::switchDeclDef(const CppEditor::CursorInEditor &da
CppModelManager::switchDeclDef(data, processLinkCallback, CppModelManager::Backend::Builtin);
}
-void ClangModelManagerSupport::startLocalRenaming(const CppEditor::CursorInEditor &data,
- const CppEditor::ProjectPart *projectPart,
- RenameCallback &&renameSymbolsCallback)
+void ClangModelManagerSupport::startLocalRenaming(const CursorInEditor &data,
+ const ProjectPart *projectPart,
+ RenameCallback &&renameSymbolsCallback)
{
if (ClangdClient * const client = clientForFile(data.filePath());
client && client->reachable()) {
@@ -312,7 +324,7 @@ void ClangModelManagerSupport::startLocalRenaming(const CppEditor::CursorInEdito
std::move(renameSymbolsCallback), CppModelManager::Backend::Builtin);
}
-void ClangModelManagerSupport::globalRename(const CppEditor::CursorInEditor &cursor,
+void ClangModelManagerSupport::globalRename(const CursorInEditor &cursor,
const QString &replacement,
const std::function<void()> &callback)
{
@@ -326,7 +338,7 @@ void ClangModelManagerSupport::globalRename(const CppEditor::CursorInEditor &cur
CppModelManager::globalRename(cursor, replacement, callback, CppModelManager::Backend::Builtin);
}
-void ClangModelManagerSupport::findUsages(const CppEditor::CursorInEditor &cursor) const
+void ClangModelManagerSupport::findUsages(const CursorInEditor &cursor) const
{
if (ClangdClient * const client = clientForFile(cursor.filePath());
client && client->isFullyIndexed()) {
@@ -340,17 +352,30 @@ void ClangModelManagerSupport::findUsages(const CppEditor::CursorInEditor &curso
void ClangModelManagerSupport::switchHeaderSource(const FilePath &filePath, bool inNextSplit)
{
- if (ClangdClient * const client = clientForFile(filePath))
- client->switchHeaderSource(filePath, inNextSplit);
- else
- CppModelManager::switchHeaderSource(inNextSplit, CppModelManager::Backend::Builtin);
+ if (ClangdClient * const client = clientForFile(filePath)) {
+ switch (ClangdProjectSettings(client->project()).settings().headerSourceSwitchMode) {
+ case ClangdSettings::HeaderSourceSwitchMode::BuiltinOnly:
+ CppModelManager::switchHeaderSource(inNextSplit, CppModelManager::Backend::Builtin);
+ return;
+ case ClangdSettings::HeaderSourceSwitchMode::ClangdOnly:
+ client->switchHeaderSource(filePath, inNextSplit);
+ return;
+ case ClangdSettings::HeaderSourceSwitchMode::Both:
+ const FilePath otherFile = correspondingHeaderOrSource(filePath);
+ if (!otherFile.isEmpty())
+ openEditor(otherFile, inNextSplit);
+ else
+ client->switchHeaderSource(filePath, inNextSplit);
+ return;
+ }
+ }
+ CppModelManager::switchHeaderSource(inNextSplit, CppModelManager::Backend::Builtin);
}
-void ClangModelManagerSupport::checkUnused(const Link &link, Core::SearchResult *search,
+void ClangModelManagerSupport::checkUnused(const Link &link, SearchResult *search,
const LinkHandler &callback)
{
- if (const ProjectExplorer::Project * const project
- = ProjectExplorer::SessionManager::projectForFile(link.targetFilePath)) {
+ if (const Project * const project = ProjectManager::projectForFile(link.targetFilePath)) {
if (ClangdClient * const client = clientWithProject(project);
client && client->isFullyIndexed()) {
client->checkUnused(link, search, callback);
@@ -367,7 +392,7 @@ bool ClangModelManagerSupport::usesClangd(const TextEditor::TextDocument *docume
return clientForFile(document->filePath());
}
-CppEditor::BaseEditorDocumentProcessor *ClangModelManagerSupport::createEditorDocumentProcessor(
+BaseEditorDocumentProcessor *ClangModelManagerSupport::createEditorDocumentProcessor(
TextEditor::TextDocument *baseTextDocument)
{
const auto processor = new ClangEditorDocumentProcessor(baseTextDocument);
@@ -381,10 +406,10 @@ CppEditor::BaseEditorDocumentProcessor *ClangModelManagerSupport::createEditorDo
return processor;
}
-void ClangModelManagerSupport::onCurrentEditorChanged(Core::IEditor *editor)
+void ClangModelManagerSupport::onCurrentEditorChanged(IEditor *editor)
{
// Update task hub issues for current CppEditorDocument
- ProjectExplorer::TaskHub::clearTasks(Constants::TASK_CATEGORY_DIAGNOSTICS);
+ TaskHub::clearTasks(Constants::TASK_CATEGORY_DIAGNOSTICS);
if (!editor || !editor->document() || !cppModelManager()->isCppEditor(editor))
return;
@@ -407,28 +432,25 @@ void ClangModelManagerSupport::connectToWidgetsMarkContextMenuRequested(QWidget
}
}
-static FilePath getJsonDbDir(const ProjectExplorer::Project *project)
+static FilePath getJsonDbDir(const Project *project)
{
static const QString dirName(".qtc_clangd");
if (!project) {
const QString sessionDirName = FileUtils::fileSystemFriendlyName(
- ProjectExplorer::SessionManager::activeSession());
- return Core::ICore::userResourcePath() / dirName / sessionDirName; // TODO: Make configurable?
+ SessionManager::activeSession());
+ return ICore::userResourcePath() / dirName / sessionDirName; // TODO: Make configurable?
}
- if (const ProjectExplorer::Target * const target = project->activeTarget()) {
- if (const ProjectExplorer::BuildConfiguration * const bc
- = target->activeBuildConfiguration()) {
+ if (const Target * const target = project->activeTarget()) {
+ if (const BuildConfiguration * const bc = target->activeBuildConfiguration())
return bc->buildDirectory() / dirName;
- }
}
return {};
}
-static bool isProjectDataUpToDate(
- ProjectExplorer::Project *project, ProjectInfoList projectInfo,
- const FilePath &jsonDbDir)
+static bool isProjectDataUpToDate(Project *project, ProjectInfoList projectInfo,
+ const FilePath &jsonDbDir)
{
- if (project && !ProjectExplorer::SessionManager::hasProject(project))
+ if (project && !ProjectManager::hasProject(project))
return false;
const ClangdSettings settings(ClangdProjectSettings(project).settings());
if (!settings.useClangd())
@@ -457,7 +479,7 @@ static bool isProjectDataUpToDate(
return true;
}
-void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *project)
+void ClangModelManagerSupport::updateLanguageClient(Project *project)
{
const ClangdSettings settings(ClangdProjectSettings(project).settings());
if (!settings.useClangd())
@@ -483,12 +505,12 @@ void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *pr
return;
const GenerateCompilationDbResult result = generatorWatcher->result();
if (!result.error.isEmpty()) {
- Core::MessageManager::writeDisrupting(
+ MessageManager::writeDisrupting(
Tr::tr("Cannot use clangd: Failed to generate compilation database:\n%1")
.arg(result.error));
return;
}
- Utils::Id previousId;
+ Id previousId;
if (Client * const oldClient = clientForProject(project)) {
previousId = oldClient->id();
LanguageClientManager::shutdownClient(oldClient);
@@ -524,7 +546,7 @@ void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *pr
hasDocuments = true;
continue;
}
- const Project * const docProject = SessionManager::projectForFile(doc->filePath());
+ const Project * const docProject = ProjectManager::projectForFile(doc->filePath());
if (currentClient && currentClient->project()
&& currentClient->project() != project
&& currentClient->project() == docProject) {
@@ -564,8 +586,8 @@ void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *pr
ProjectNode *rootNode = nullptr;
if (project)
rootNode = project->rootProjectNode();
- else if (SessionManager::startupProject())
- rootNode = SessionManager::startupProject()->rootProjectNode();
+ else if (ProjectManager::startupProject())
+ rootNode = ProjectManager::startupProject()->rootProjectNode();
if (!rootNode)
return;
const Node * const cxxNode = rootNode->findNode([](Node *n) {
@@ -583,7 +605,7 @@ void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *pr
});
const FilePath includeDir = settings.clangdIncludePath();
- auto future = Utils::runAsync(&Internal::generateCompilationDB, projectInfo,
+ auto future = Utils::asyncRun(&Internal::generateCompilationDB, projectInfo,
jsonDbDir, CompilationDbPurpose::CodeModel,
warningsConfigForProject(project),
globalClangOptions(), includeDir);
@@ -591,18 +613,28 @@ void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *pr
m_generatorSynchronizer.addFuture(future);
}
-ClangdClient *ClangModelManagerSupport::clientForProject(const ProjectExplorer::Project *project)
+QList<Client *> ClangModelManagerSupport::clientsForOpenProjects()
+{
+ QSet<Client *> clients;
+ const QList<Project *> projects = ProjectManager::projects();
+ for (Project *project : projects) {
+ if (Client *client = ClangModelManagerSupport::clientForProject(project))
+ clients << client;
+ }
+ return clients.values();
+}
+
+ClangdClient *ClangModelManagerSupport::clientForProject(const Project *project)
{
if (sessionModeEnabled())
project = nullptr;
return clientWithProject(project);
}
-ClangdClient *ClangModelManagerSupport::clientWithProject(const ProjectExplorer::Project *project)
+ClangdClient *ClangModelManagerSupport::clientWithProject(const Project *project)
{
const QList<Client *> clients = Utils::filtered(
- LanguageClientManager::clientsForProject(project),
- [](const LanguageClient::Client *c) {
+ LanguageClientManager::clientsForProject(project), [](const Client *c) {
return qobject_cast<const ClangdClient *>(c)
&& c->state() != Client::ShutdownRequested
&& c->state() != Client::Shutdown;
@@ -640,7 +672,7 @@ void ClangModelManagerSupport::claimNonProjectSources(ClangdClient *client)
}
if (!ClangdSettings::instance().sizeIsOkay(doc->filePath()))
continue;
- if (ProjectExplorer::SessionManager::projectForFile(doc->filePath()))
+ if (ProjectManager::projectForFile(doc->filePath()))
continue;
if (client->project() && !ProjectFile::isHeader(doc->filePath()))
continue;
@@ -656,7 +688,7 @@ void ClangModelManagerSupport::claimNonProjectSources(ClangdClient *client)
// workflow, e.g. a git branch switch will hit at least one open file.
void ClangModelManagerSupport::watchForExternalChanges()
{
- connect(Core::DocumentManager::instance(), &Core::DocumentManager::filesChangedExternally,
+ connect(DocumentManager::instance(), &DocumentManager::filesChangedExternally,
this, [this](const QSet<FilePath> &files) {
if (!LanguageClientManager::hasClients<ClangdClient>())
return;
@@ -664,8 +696,7 @@ void ClangModelManagerSupport::watchForExternalChanges()
const ProjectFile::Kind kind = ProjectFile::classify(file.toString());
if (!ProjectFile::isSource(kind) && !ProjectFile::isHeader(kind))
continue;
- ProjectExplorer::Project * const project
- = ProjectExplorer::SessionManager::projectForFile(file);
+ Project * const project = ProjectManager::projectForFile(file);
if (!project)
continue;
@@ -684,14 +715,13 @@ void ClangModelManagerSupport::watchForExternalChanges()
// restart clangd for reliable re-parsing and re-indexing.
void ClangModelManagerSupport::watchForInternalChanges()
{
- connect(Core::DocumentManager::instance(), &Core::DocumentManager::filesChangedInternally,
+ connect(DocumentManager::instance(), &DocumentManager::filesChangedInternally,
this, [this](const FilePaths &filePaths) {
for (const FilePath &fp : filePaths) {
const ProjectFile::Kind kind = ProjectFile::classify(fp.toString());
if (!ProjectFile::isSource(kind) && !ProjectFile::isHeader(kind))
continue;
- ProjectExplorer::Project * const project
- = ProjectExplorer::SessionManager::projectForFile(fp);
+ Project * const project = ProjectManager::projectForFile(fp);
if (!project)
continue;
if (ClangdClient * const client = clientForProject(project);
@@ -717,18 +747,17 @@ void ClangModelManagerSupport::scheduleClientRestart(ClangdClient *client)
m_clientRestartTimer->start();
}
-void ClangModelManagerSupport::onEditorOpened(Core::IEditor *editor)
+void ClangModelManagerSupport::onEditorOpened(IEditor *editor)
{
QTC_ASSERT(editor, return);
- Core::IDocument *document = editor->document();
+ IDocument *document = editor->document();
QTC_ASSERT(document, return);
auto textDocument = qobject_cast<TextEditor::TextDocument *>(document);
if (textDocument && cppModelManager()->isCppEditor(editor)) {
connectToWidgetsMarkContextMenuRequested(editor->widget());
- ProjectExplorer::Project * project
- = ProjectExplorer::SessionManager::projectForFile(document->filePath());
+ Project * project = ProjectManager::projectForFile(document->filePath());
const ClangdSettings settings(ClangdProjectSettings(project).settings());
if (!settings.sizeIsOkay(textDocument->filePath()))
return;
@@ -823,17 +852,17 @@ static ClangEditorDocumentProcessors clangProcessors()
return result;
}
-void ClangModelManagerSupport::onProjectPartsUpdated(ProjectExplorer::Project *project)
+void ClangModelManagerSupport::onProjectPartsUpdated(Project *project)
{
QTC_ASSERT(project, return);
updateLanguageClient(project);
QStringList projectPartIds;
- const CppEditor::ProjectInfo::ConstPtr projectInfo = cppModelManager()->projectInfo(project);
+ const ProjectInfo::ConstPtr projectInfo = cppModelManager()->projectInfo(project);
QTC_ASSERT(projectInfo, return);
- for (const CppEditor::ProjectPart::ConstPtr &projectPart : projectInfo->projectParts())
+ for (const ProjectPart::ConstPtr &projectPart : projectInfo->projectParts())
projectPartIds.append(projectPart->id());
onProjectPartsRemoved(projectPartIds);
}
@@ -848,9 +877,8 @@ void ClangModelManagerSupport::onClangdSettingsChanged()
{
const bool sessionMode = sessionModeEnabled();
- for (ProjectExplorer::Project * const project : ProjectExplorer::SessionManager::projects()) {
- const CppEditor::ClangdSettings settings(
- CppEditor::ClangdProjectSettings(project).settings());
+ for (Project * const project : ProjectManager::projects()) {
+ const ClangdSettings settings(ClangdProjectSettings(project).settings());
ClangdClient * const client = clientWithProject(project);
if (sessionMode) {
if (client && client->project())
diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.h b/src/plugins/clangcodemodel/clangmodelmanagersupport.h
index a901b4407e..140239550d 100644
--- a/src/plugins/clangcodemodel/clangmodelmanagersupport.h
+++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.h
@@ -24,6 +24,7 @@ QT_END_NAMESPACE
namespace Core { class IEditor; }
namespace CppEditor { class RefactoringEngineInterface; }
+namespace LanguageClient { class Client; }
namespace TextEditor { class TextEditorWidget; }
namespace ClangCodeModel {
@@ -45,6 +46,7 @@ public:
TextEditor::TextDocument *baseTextDocument) override;
bool usesClangd(const TextEditor::TextDocument *document) const override;
+ static QList<LanguageClient::Client *> clientsForOpenProjects();
static ClangdClient *clientForProject(const ProjectExplorer::Project *project);
static ClangdClient *clientForFile(const Utils::FilePath &file);
diff --git a/src/plugins/clangcodemodel/clangtextmark.cpp b/src/plugins/clangcodemodel/clangtextmark.cpp
index a38aeb6287..570e6a2e95 100644
--- a/src/plugins/clangcodemodel/clangtextmark.cpp
+++ b/src/plugins/clangcodemodel/clangtextmark.cpp
@@ -98,15 +98,12 @@ ClangDiagnosticConfig diagnosticConfig()
return warningsConfigForProject(project);
}
-bool isDiagnosticConfigChangable(Project *project, const ClangDiagnostic &diagnostic)
+static bool isDiagnosticConfigChangable(Project *project)
{
if (!project)
return false;
- const ClangDiagnosticConfig config = diagnosticConfig();
- if (config.clangTidyMode() == ClangDiagnosticConfig::TidyMode::UseConfigFile
- && diagnosticType(diagnostic) == DiagnosticType::Tidy) {
+ if (diagnosticConfig().useBuildSystemWarnings())
return false;
- }
return true;
}
@@ -308,7 +305,7 @@ ClangdTextMark::ClangdTextMark(const FilePath &filePath,
// Remove diagnostic warning action
Project *project = projectForCurrentEditor();
- if (project && isDiagnosticConfigChangable(project, diag)) {
+ if (project && isDiagnosticConfigChangable(project)) {
action = new QAction();
action->setIcon(Icons::BROKEN.icon());
action->setToolTip(Tr::tr("Disable Diagnostic in Current Project"));
diff --git a/src/plugins/clangcodemodel/test/clangbatchfileprocessor.cpp b/src/plugins/clangcodemodel/test/clangbatchfileprocessor.cpp
deleted file mode 100644
index 1f0219df99..0000000000
--- a/src/plugins/clangcodemodel/test/clangbatchfileprocessor.cpp
+++ /dev/null
@@ -1,729 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "clangbatchfileprocessor.h"
-
-#include <clangcodemodel/clangeditordocumentprocessor.h>
-
-#include <coreplugin/editormanager/editormanager.h>
-#include <coreplugin/editormanager/ieditor.h>
-#include <coreplugin/icore.h>
-#include <cppeditor/cpptoolsreuse.h>
-#include <cppeditor/cpptoolstestcase.h>
-#include <cppeditor/modelmanagertesthelper.h>
-#include <cppeditor/projectinfo.h>
-#include <projectexplorer/projectexplorer.h>
-#include <texteditor/codeassist/assistinterface.h>
-#include <texteditor/codeassist/assistproposalitem.h>
-#include <texteditor/codeassist/completionassistprovider.h>
-#include <texteditor/codeassist/genericproposalmodel.h>
-#include <texteditor/codeassist/iassistprocessor.h>
-#include <texteditor/codeassist/iassistproposal.h>
-#include <texteditor/textdocument.h>
-#include <texteditor/texteditor.h>
-
-#include <utils/environment.h>
-#include <utils/executeondestruction.h>
-#include <utils/qtcassert.h>
-
-#include <QDebug>
-#include <QElapsedTimer>
-#include <QFileInfo>
-#include <QLoggingCategory>
-#include <QSharedPointer>
-#include <QString>
-#include <QThread>
-
-using namespace ProjectExplorer;
-
-namespace ClangCodeModel {
-namespace Internal {
-
-static Q_LOGGING_CATEGORY(debug, "qtc.clangcodemodel.batch", QtWarningMsg);
-
-static int timeOutFromEnvironmentVariable()
-{
- bool isConversionOk = false;
- const int intervalAsInt = Utils::qtcEnvironmentVariableIntValue("QTC_CLANG_BATCH_TIMEOUT",
- &isConversionOk);
- if (!isConversionOk) {
- qCDebug(debug, "Environment variable QTC_CLANG_BATCH_TIMEOUT is not set, assuming 30000.");
- return 30000;
- }
-
- return intervalAsInt;
-}
-
-int timeOutInMs()
-{
- static int timeOut = timeOutFromEnvironmentVariable();
- return timeOut;
-}
-
-namespace {
-
-class BatchFileLineTokenizer
-{
-public:
- BatchFileLineTokenizer(const QString &line);
-
- QString nextToken();
-
-private:
- const QChar *advanceToTokenBegin();
- const QChar *advanceToTokenEnd();
-
- bool atEnd() const;
- bool atWhiteSpace() const;
- bool atQuotationMark() const;
-
-private:
- bool m_isWithinQuotation = false;
- QString m_line;
- const QChar *m_currentChar;
-};
-
-BatchFileLineTokenizer::BatchFileLineTokenizer(const QString &line)
- : m_line(line)
- , m_currentChar(m_line.unicode())
-{
-}
-
-QString BatchFileLineTokenizer::nextToken()
-{
- if (const QChar *tokenBegin = advanceToTokenBegin()) {
- if (const QChar *tokenEnd = advanceToTokenEnd()) {
- const int length = tokenEnd - tokenBegin;
- return QString(tokenBegin, length);
- }
- }
-
- return QString();
-}
-
-const QChar *BatchFileLineTokenizer::advanceToTokenBegin()
-{
- m_isWithinQuotation = false;
-
- forever {
- if (atEnd())
- return nullptr;
-
- if (atQuotationMark()) {
- m_isWithinQuotation = true;
- ++m_currentChar;
- return m_currentChar;
- }
-
- if (!atWhiteSpace())
- return m_currentChar;
-
- ++m_currentChar;
- }
-}
-
-const QChar *BatchFileLineTokenizer::advanceToTokenEnd()
-{
- forever {
- if (m_isWithinQuotation) {
- if (atEnd()) {
- qWarning("ClangBatchFileProcessor: error: unfinished quotation.");
- return nullptr;
- }
-
- if (atQuotationMark())
- return m_currentChar++;
-
- } else if (atWhiteSpace() || atEnd()) {
- return m_currentChar;
- }
-
- ++m_currentChar;
- }
-}
-
-bool BatchFileLineTokenizer::atEnd() const
-{
- return *m_currentChar == QLatin1Char('\0');
-}
-
-bool BatchFileLineTokenizer::atWhiteSpace() const
-{
- return *m_currentChar == ' '
- || *m_currentChar == '\t'
- || *m_currentChar == '\n';
-}
-
-bool BatchFileLineTokenizer::atQuotationMark() const
-{
- return *m_currentChar == '"';
-}
-
-struct CommandContext {
- QString filePath;
- int lineNumber = -1;
-};
-
-class Command
-{
-public:
- using Ptr = QSharedPointer<Command>;
-
-public:
- Command(const CommandContext &context) : m_commandContext(context) {}
- virtual ~Command() = default;
-
- const CommandContext &context() const { return m_commandContext; }
- virtual bool run() { return true; }
-
-private:
- const CommandContext m_commandContext;
-};
-
-class OpenProjectCommand : public Command
-{
-public:
- OpenProjectCommand(const CommandContext &context,
- const QString &projectFilePath);
-
- bool run() override;
-
- static Command::Ptr parse(BatchFileLineTokenizer &arguments,
- const CommandContext &context);
-
-private:
- QString m_projectFilePath;
-};
-
-OpenProjectCommand::OpenProjectCommand(const CommandContext &context,
- const QString &projectFilePath)
- : Command(context)
- , m_projectFilePath(projectFilePath)
-{
-}
-
-bool OpenProjectCommand::run()
-{
- qCDebug(debug) << "line" << context().lineNumber << "OpenProjectCommand" << m_projectFilePath;
-
- const ProjectExplorerPlugin::OpenProjectResult openProjectSucceeded
- = ProjectExplorerPlugin::openProject(Utils::FilePath::fromString(m_projectFilePath));
- QTC_ASSERT(openProjectSucceeded, return false);
-
- Project *project = openProjectSucceeded.project();
- project->configureAsExampleProject(nullptr);
-
- return CppEditor::Tests::TestCase::waitUntilProjectIsFullyOpened(project, timeOutInMs());
-}
-
-Command::Ptr OpenProjectCommand::parse(BatchFileLineTokenizer &arguments,
- const CommandContext &context)
-{
- const QString projectFilePath = arguments.nextToken();
- if (projectFilePath.isEmpty()) {
- qWarning("%s:%d: error: No project file path given.",
- qPrintable(context.filePath),
- context.lineNumber);
- return Command::Ptr();
- }
-
- const QString absoluteProjectFilePath = QFileInfo(projectFilePath).absoluteFilePath();
-
- return Command::Ptr(new OpenProjectCommand(context, absoluteProjectFilePath));
-}
-
-class OpenDocumentCommand : public Command
-{
-public:
- OpenDocumentCommand(const CommandContext &context,
- const QString &documentFilePath);
-
- bool run() override;
-
- static Command::Ptr parse(BatchFileLineTokenizer &arguments, const CommandContext &context);
-
-private:
- Utils::FilePath m_documentFilePath;
-};
-
-OpenDocumentCommand::OpenDocumentCommand(const CommandContext &context,
- const QString &documentFilePath)
- : Command(context)
- , m_documentFilePath(Utils::FilePath::fromString(documentFilePath))
-{
-}
-
-class WaitForUpdatedCodeWarnings : public QObject
-{
-public:
- WaitForUpdatedCodeWarnings(ClangEditorDocumentProcessor *processor);
-
- bool wait(int timeOutInMs) const;
-
-private:
- void onCodeWarningsUpdated() { m_gotResults = true; }
-
-private:
-
- bool m_gotResults = false;
-};
-
-WaitForUpdatedCodeWarnings::WaitForUpdatedCodeWarnings(ClangEditorDocumentProcessor *processor)
-{
- connect(processor,
- &ClangEditorDocumentProcessor::codeWarningsUpdated,
- this, &WaitForUpdatedCodeWarnings::onCodeWarningsUpdated);
-}
-
-bool WaitForUpdatedCodeWarnings::wait(int timeOutInMs) const
-{
- QElapsedTimer time;
- time.start();
-
- forever {
- if (time.elapsed() > timeOutInMs) {
- qWarning("WaitForUpdatedCodeWarnings: timeout of %d ms reached.", timeOutInMs);
- return false;
- }
-
- if (m_gotResults)
- return true;
-
- QCoreApplication::processEvents();
- QThread::msleep(20);
- }
-}
-
-bool OpenDocumentCommand::run()
-{
- qCDebug(debug) << "line" << context().lineNumber << "OpenDocumentCommand" << m_documentFilePath;
-
- const bool openEditorSucceeded = Core::EditorManager::openEditor(m_documentFilePath);
- QTC_ASSERT(openEditorSucceeded, return false);
-
- auto *processor = ClangEditorDocumentProcessor::get(m_documentFilePath);
- QTC_ASSERT(processor, return false);
-
- WaitForUpdatedCodeWarnings waiter(processor);
- return waiter.wait(timeOutInMs());
-}
-
-Command::Ptr OpenDocumentCommand::parse(BatchFileLineTokenizer &arguments,
- const CommandContext &context)
-{
- const QString documentFilePath = arguments.nextToken();
- if (documentFilePath.isEmpty()) {
- qWarning("%s:%d: error: No document file path given.",
- qPrintable(context.filePath),
- context.lineNumber);
- return Command::Ptr();
- }
-
- const QString absoluteDocumentFilePath = QFileInfo(documentFilePath).absoluteFilePath();
-
- return Command::Ptr(new OpenDocumentCommand(context, absoluteDocumentFilePath));
-}
-
-class CloseAllDocuments : public Command
-{
-public:
- CloseAllDocuments(const CommandContext &context);
-
- bool run() override;
-
- static Command::Ptr parse(BatchFileLineTokenizer &arguments, const CommandContext &context);
-};
-
-CloseAllDocuments::CloseAllDocuments(const CommandContext &context)
- : Command(context)
-{
-}
-
-bool CloseAllDocuments::run()
-{
- qCDebug(debug) << "line" << context().lineNumber << "CloseAllDocuments";
-
- return Core::EditorManager::closeAllEditors(/*askAboutModifiedEditors=*/ false);
-}
-
-Command::Ptr CloseAllDocuments::parse(BatchFileLineTokenizer &arguments,
- const CommandContext &context)
-{
- const QString argument = arguments.nextToken();
- if (!argument.isEmpty()) {
- qWarning("%s:%d: error: Unexpected argument.",
- qPrintable(context.filePath),
- context.lineNumber);
- return Command::Ptr();
- }
-
- return Command::Ptr(new CloseAllDocuments(context));
-}
-
-class InsertTextCommand : public Command
-{
-public:
- // line and column are 1-based
- InsertTextCommand(const CommandContext &context, const QString &text);
-
- bool run() override;
-
- static Command::Ptr parse(BatchFileLineTokenizer &arguments,
- const CommandContext &context);
-
-private:
- const QString m_textToInsert;
-};
-
-InsertTextCommand::InsertTextCommand(const CommandContext &context, const QString &text)
- : Command(context)
- , m_textToInsert(text)
-{
-}
-
-TextEditor::BaseTextEditor *currentTextEditor()
-{
- return qobject_cast<TextEditor::BaseTextEditor*>(Core::EditorManager::currentEditor());
-}
-
-bool InsertTextCommand::run()
-{
- qCDebug(debug) << "line" << context().lineNumber << "InsertTextCommand" << m_textToInsert;
-
- TextEditor::BaseTextEditor *editor = currentTextEditor();
- QTC_ASSERT(editor, return false);
- const Utils::FilePath documentFilePath = editor->document()->filePath();
- auto processor = ClangEditorDocumentProcessor::get(documentFilePath);
- QTC_ASSERT(processor, return false);
-
- editor->insert(m_textToInsert);
-
- WaitForUpdatedCodeWarnings waiter(processor);
- return waiter.wait(timeOutInMs());
-}
-
-Command::Ptr InsertTextCommand::parse(BatchFileLineTokenizer &arguments,
- const CommandContext &context)
-{
- const QString textToInsert = arguments.nextToken();
- if (textToInsert.isEmpty()) {
- qWarning("%s:%d: error: No text to insert given.",
- qPrintable(context.filePath),
- context.lineNumber);
- return Command::Ptr();
- }
-
- return Command::Ptr(new InsertTextCommand(context, textToInsert));
-}
-
-class SetCursorCommand : public Command
-{
-public:
- // line and column are 1-based
- SetCursorCommand(const CommandContext &context, int line, int column);
-
- bool run() override;
-
- static Command::Ptr parse(BatchFileLineTokenizer &arguments,
- const CommandContext &context);
-
-private:
- int m_line;
- int m_column;
-};
-
-SetCursorCommand::SetCursorCommand(const CommandContext &context, int line, int column)
- : Command(context)
- , m_line(line)
- , m_column(column)
-{
-}
-
-bool SetCursorCommand::run()
-{
- qCDebug(debug) << "line" << context().lineNumber << "SetCursorCommand" << m_line << m_column;
-
- TextEditor::BaseTextEditor *editor = currentTextEditor();
- QTC_ASSERT(editor, return false);
-
- editor->gotoLine(m_line, m_column - 1);
-
- return true;
-}
-
-Command::Ptr SetCursorCommand::parse(BatchFileLineTokenizer &arguments,
- const CommandContext &context)
-{
- // Process line
- const QString line = arguments.nextToken();
- if (line.isEmpty()) {
- qWarning("%s:%d: error: No line number given.",
- qPrintable(context.filePath),
- context.lineNumber);
- return Command::Ptr();
- }
- bool converted = false;
- const int lineNumber = line.toInt(&converted);
- if (!converted) {
- qWarning("%s:%d: error: Invalid line number.",
- qPrintable(context.filePath),
- context.lineNumber);
- return Command::Ptr();
- }
-
- // Process column
- const QString column = arguments.nextToken();
- if (column.isEmpty()) {
- qWarning("%s:%d: error: No column number given.",
- qPrintable(context.filePath),
- context.lineNumber);
- return Command::Ptr();
- }
- converted = false;
- const int columnNumber = column.toInt(&converted);
- if (!converted) {
- qWarning("%s:%d: error: Invalid column number.",
- qPrintable(context.filePath),
- context.lineNumber);
- return Command::Ptr();
- }
-
- return Command::Ptr(new SetCursorCommand(context, lineNumber, columnNumber));
-}
-
-class ProcessEventsCommand : public Command
-{
-public:
- ProcessEventsCommand(const CommandContext &context, int durationInMs);
-
- bool run() override;
-
- static Command::Ptr parse(BatchFileLineTokenizer &arguments,
- const CommandContext &context);
-
-private:
- int m_durationInMs;
-};
-
-ProcessEventsCommand::ProcessEventsCommand(const CommandContext &context,
- int durationInMs)
- : Command(context)
- , m_durationInMs(durationInMs)
-{
-}
-
-bool ProcessEventsCommand::run()
-{
- qCDebug(debug) << "line" << context().lineNumber << "ProcessEventsCommand" << m_durationInMs;
-
- QElapsedTimer time;
- time.start();
-
- forever {
- if (time.elapsed() > m_durationInMs)
- return true;
-
- QCoreApplication::processEvents();
- QThread::msleep(20);
- }
-}
-
-Command::Ptr ProcessEventsCommand::parse(BatchFileLineTokenizer &arguments,
- const CommandContext &context)
-{
- const QString durationInMsText = arguments.nextToken();
- if (durationInMsText.isEmpty()) {
- qWarning("%s:%d: error: No duration given.",
- qPrintable(context.filePath),
- context.lineNumber);
- return Command::Ptr();
- }
-
- bool converted = false;
- const int durationInMs = durationInMsText.toInt(&converted);
- if (!converted) {
- qWarning("%s:%d: error: Invalid duration given.",
- qPrintable(context.filePath),
- context.lineNumber);
- return Command::Ptr();
- }
-
- return Command::Ptr(new ProcessEventsCommand(context, durationInMs));
-}
-
-class BatchFileReader
-{
-public:
- BatchFileReader(const QString &filePath);
-
- bool isFilePathValid() const;
-
- QString read() const;
-
-private:
- const QString m_batchFilePath;
-};
-
-BatchFileReader::BatchFileReader(const QString &filePath)
- : m_batchFilePath(filePath)
-{
-}
-
-bool BatchFileReader::isFilePathValid() const
-{
- QFileInfo fileInfo(m_batchFilePath);
-
- return !m_batchFilePath.isEmpty()
- && fileInfo.isFile()
- && fileInfo.isReadable();
-}
-
-QString BatchFileReader::read() const
-{
- QFile file(m_batchFilePath);
- QTC_CHECK(file.open(QFile::ReadOnly | QFile::Text));
-
- return QString::fromLocal8Bit(file.readAll());
-}
-
-class BatchFileParser
-{
-public:
- BatchFileParser(const QString &filePath,
- const QString &commands);
-
- bool parse();
- QVector<Command::Ptr> commands() const;
-
-private:
- bool advanceLine();
- QString currentLine() const;
- bool parseLine(const QString &line);
-
-private:
- using ParseFunction = Command::Ptr (*)(BatchFileLineTokenizer &, const CommandContext &);
- using CommandToParseFunction = QHash<QString, ParseFunction>;
- CommandToParseFunction m_commandParsers;
-
- int m_currentLineIndex = -1;
- CommandContext m_context;
- QStringList m_lines;
- QVector<Command::Ptr> m_commands;
-};
-
-BatchFileParser::BatchFileParser(const QString &filePath,
- const QString &commands)
- : m_lines(commands.split('\n'))
-{
- m_context.filePath = filePath;
-
- m_commandParsers.insert("openProject", &OpenProjectCommand::parse);
- m_commandParsers.insert("openDocument", &OpenDocumentCommand::parse);
- m_commandParsers.insert("closeAllDocuments", &CloseAllDocuments::parse);
- m_commandParsers.insert("setCursor", &SetCursorCommand::parse);
- m_commandParsers.insert("insertText", &InsertTextCommand::parse);
- m_commandParsers.insert("processEvents", &ProcessEventsCommand::parse);
-}
-
-bool BatchFileParser::parse()
-{
- while (advanceLine()) {
- const QString line = currentLine().trimmed();
- if (line.isEmpty() || line.startsWith('#'))
- continue;
-
- if (!parseLine(line))
- return false;
- }
-
- return true;
-}
-
-QVector<Command::Ptr> BatchFileParser::commands() const
-{
- return m_commands;
-}
-
-bool BatchFileParser::advanceLine()
-{
- ++m_currentLineIndex;
- m_context.lineNumber = m_currentLineIndex + 1;
- return m_currentLineIndex < m_lines.size();
-}
-
-QString BatchFileParser::currentLine() const
-{
- return m_lines[m_currentLineIndex];
-}
-
-bool BatchFileParser::parseLine(const QString &line)
-{
- BatchFileLineTokenizer tokenizer(line);
- QString command = tokenizer.nextToken();
- QTC_CHECK(!command.isEmpty());
-
- if (const ParseFunction parseFunction = m_commandParsers.value(command)) {
- if (Command::Ptr cmd = parseFunction(tokenizer, m_context)) {
- m_commands.append(cmd);
- return true;
- }
-
- return false;
- }
-
- qWarning("%s:%d: error: Unknown command \"%s\".",
- qPrintable(m_context.filePath),
- m_context.lineNumber,
- qPrintable(command));
-
- return false;
-}
-
-} // anonymous namespace
-
-static QString applySubstitutions(const QString &filePath, const QString &text)
-{
- const QString dirPath = QFileInfo(filePath).absolutePath();
-
- QString result = text;
- result.replace("${PWD}", dirPath);
-
- return result;
-}
-
-bool runClangBatchFile(const QString &filePath)
-{
- qWarning("ClangBatchFileProcessor: Running \"%s\".", qPrintable(filePath));
-
- BatchFileReader reader(filePath);
- QTC_ASSERT(reader.isFilePathValid(), return false);
- const QString fileContent = reader.read();
- const QString fileContentWithSubstitutionsApplied = applySubstitutions(filePath, fileContent);
-
- BatchFileParser parser(filePath, fileContentWithSubstitutionsApplied);
- QTC_ASSERT(parser.parse(), return false);
- const QVector<Command::Ptr> commands = parser.commands();
-
- Utils::ExecuteOnDestruction closeAllEditors([] {
- qWarning("ClangBatchFileProcessor: Finished, closing all documents.");
- QTC_CHECK(Core::EditorManager::closeAllEditors(/*askAboutModifiedEditors=*/ false));
- });
-
- for (const Command::Ptr &command : commands) {
- const bool runSucceeded = command->run();
- QCoreApplication::processEvents(); // Update GUI
-
- if (!runSucceeded) {
- const CommandContext context = command->context();
- qWarning("%s:%d: Failed to run.",
- qPrintable(context.filePath),
- context.lineNumber);
- return false;
- }
- }
-
- return true;
-}
-
-} // namespace Internal
-} // namespace ClangCodeModel
diff --git a/src/plugins/clangcodemodel/test/clangbatchfileprocessor.h b/src/plugins/clangcodemodel/test/clangbatchfileprocessor.h
deleted file mode 100644
index 942269b364..0000000000
--- a/src/plugins/clangcodemodel/test/clangbatchfileprocessor.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <QString>
-
-namespace ClangCodeModel {
-namespace Internal {
-
-int timeOutInMs();
-
-bool runClangBatchFile(const QString &filePath);
-
-} // namespace Internal
-} // namespace ClangCodeModel
diff --git a/src/plugins/clangcodemodel/test/clangdtests.cpp b/src/plugins/clangcodemodel/test/clangdtests.cpp
index c60a45f1df..a90126c00e 100644
--- a/src/plugins/clangcodemodel/test/clangdtests.cpp
+++ b/src/plugins/clangcodemodel/test/clangdtests.cpp
@@ -3,7 +3,6 @@
#include "clangdtests.h"
-#include "clangbatchfileprocessor.h"
#include "../clangdclient.h"
#include "../clangmodelmanagersupport.h"
@@ -44,6 +43,7 @@ using namespace CppEditor::Tests;
using namespace LanguageClient;
using namespace ProjectExplorer;
using namespace TextEditor;
+using namespace Utils;
namespace ClangCodeModel {
namespace Internal {
@@ -69,6 +69,27 @@ static QString qrcPath(const QString &relativeFilePath)
return ":/unittests/ClangCodeModel/" + relativeFilePath;
}
+static Q_LOGGING_CATEGORY(debug, "qtc.clangcodemodel.batch", QtWarningMsg);
+
+static int timeOutFromEnvironmentVariable()
+{
+ bool isConversionOk = false;
+ const int intervalAsInt = Utils::qtcEnvironmentVariableIntValue("QTC_CLANG_BATCH_TIMEOUT",
+ &isConversionOk);
+ if (!isConversionOk) {
+ qCDebug(debug, "Environment variable QTC_CLANG_BATCH_TIMEOUT is not set, assuming 30000.");
+ return 30000;
+ }
+
+ return intervalAsInt;
+}
+
+int timeOutInMs()
+{
+ static int timeOut = timeOutFromEnvironmentVariable();
+ return timeOut;
+}
+
ClangdTest::~ClangdTest()
{
EditorManager::closeAllEditors(false);
@@ -77,7 +98,7 @@ ClangdTest::~ClangdTest()
delete m_projectDir;
}
-Utils::FilePath ClangdTest::filePath(const QString &fileName) const
+FilePath ClangdTest::filePath(const QString &fileName) const
{
return m_projectDir->absolutePath(fileName);
}
@@ -119,7 +140,7 @@ void ClangdTest::initTestCase()
{
const QString clangdFromEnv = Utils::qtcEnvironmentVariable("QTC_CLANGD");
if (!clangdFromEnv.isEmpty())
- CppEditor::ClangdSettings::setClangdFilePath(Utils::FilePath::fromString(clangdFromEnv));
+ CppEditor::ClangdSettings::setClangdFilePath(FilePath::fromString(clangdFromEnv));
const auto clangd = CppEditor::ClangdSettings::instance().clangdFilePath();
if (clangd.isEmpty() || !clangd.exists())
QSKIP("clangd binary not found");
@@ -170,7 +191,7 @@ void ClangdTestFindReferences::initTestCase()
ClangdTest::initTestCase();
CppEditor::codeModelSettings()->setCategorizeFindReferences(true);
connect(client(), &ClangdClient::foundReferences, this,
- [this](const QList<SearchResultItem> &results) {
+ [this](const SearchResultItems &results) {
if (results.isEmpty())
return;
if (results.first().path().first().endsWith("defs.h"))
@@ -184,8 +205,7 @@ void ClangdTestFindReferences::test_data()
{
QTest::addColumn<QString>("fileName");
QTest::addColumn<int>("pos");
- using ItemList = QList<SearchResultItem>;
- QTest::addColumn<ItemList>("expectedResults");
+ QTest::addColumn<SearchResultItems>("expectedResults");
static const auto makeItem = [](int line, int column, Usage::Tags tags) {
SearchResultItem item;
@@ -194,7 +214,7 @@ void ClangdTestFindReferences::test_data()
return item;
};
- QTest::newRow("struct member") << "defs.h" << 55 << ItemList{
+ QTest::newRow("struct member") << "defs.h" << 55 << SearchResultItems{
makeItem(2, 17, Usage::Tag::Read), makeItem(3, 15, Usage::Tag::Declaration),
makeItem(6, 17, Usage::Tag::WritableRef), makeItem(8, 11, Usage::Tag::WritableRef),
makeItem(9, 13, Usage::Tag::WritableRef), makeItem(10, 12, Usage::Tag::WritableRef),
@@ -211,23 +231,23 @@ void ClangdTestFindReferences::test_data()
makeItem(56, 7, Usage::Tag::Write), makeItem(56, 25, Usage::Tags()),
makeItem(58, 13, Usage::Tag::Read), makeItem(58, 25, Usage::Tag::Read),
makeItem(59, 7, Usage::Tag::Write), makeItem(59, 24, Usage::Tag::Read)};
- QTest::newRow("constructor member initialization") << "defs.h" << 68 << ItemList{
+ QTest::newRow("constructor member initialization") << "defs.h" << 68 << SearchResultItems{
makeItem(2, 10, Usage::Tag::Write), makeItem(4, 8, Usage::Tag::Declaration)};
- QTest::newRow("direct member initialization") << "defs.h" << 101 << ItemList{
+ QTest::newRow("direct member initialization") << "defs.h" << 101 << SearchResultItems{
makeItem(5, 21, Initialization), makeItem(45, 16, Usage::Tag::Read)};
- ItemList pureVirtualRefs{makeItem(17, 17, Usage::Tag::Declaration),
+ SearchResultItems pureVirtualRefs{makeItem(17, 17, Usage::Tag::Declaration),
makeItem(21, 9, {Usage::Tag::Declaration, Usage::Tag::Override})};
QTest::newRow("pure virtual declaration") << "defs.h" << 420 << pureVirtualRefs;
- QTest::newRow("pointer variable") << "main.cpp" << 52 << ItemList{
+ QTest::newRow("pointer variable") << "main.cpp" << 52 << SearchResultItems{
makeItem(6, 10, Initialization), makeItem(8, 4, Usage::Tag::Write),
makeItem(10, 4, Usage::Tag::Write), makeItem(24, 5, Usage::Tag::Write),
makeItem(25, 11, Usage::Tag::WritableRef), makeItem(26, 11, Usage::Tag::Read),
makeItem(27, 10, Usage::Tag::WritableRef), makeItem(28, 10, Usage::Tag::Read),
makeItem(29, 11, Usage::Tag::Read), makeItem(30, 15, Usage::Tag::WritableRef),
makeItem(31, 22, Usage::Tag::Read)};
- QTest::newRow("struct variable") << "main.cpp" << 39 << ItemList{
+ QTest::newRow("struct variable") << "main.cpp" << 39 << SearchResultItems{
makeItem(5, 7, Usage::Tag::Declaration), makeItem(6, 15, Usage::Tag::WritableRef),
makeItem(8, 9, Usage::Tag::WritableRef), makeItem(9, 11, Usage::Tag::WritableRef),
makeItem(11, 4, Usage::Tag::Write), makeItem(11, 11, Usage::Tag::WritableRef),
@@ -248,7 +268,7 @@ void ClangdTestFindReferences::test_data()
// Some of these are conceptually questionable, as S is a type and thus we cannot "read from"
// or "write to" it. But it probably matches the intuitive user expectation.
- QTest::newRow("struct type") << "defs.h" << 7 << ItemList{
+ QTest::newRow("struct type") << "defs.h" << 7 << SearchResultItems{
makeItem(1, 7, Usage::Tag::Declaration),
makeItem(2, 4, (Usage::Tags{Usage::Tag::Declaration, Usage::Tag::ConstructorDestructor})),
makeItem(20, 19, Usage::Tags()), makeItem(10, 9, Usage::Tag::WritableRef),
@@ -260,24 +280,24 @@ void ClangdTestFindReferences::test_data()
makeItem(58, 10, Usage::Tag::Read), makeItem(58, 22, Usage::Tag::Read),
makeItem(59, 4, Usage::Tag::Write), makeItem(59, 21, Usage::Tag::Read)};
- QTest::newRow("struct type 2") << "defs.h" << 450 << ItemList{
+ QTest::newRow("struct type 2") << "defs.h" << 450 << SearchResultItems{
makeItem(20, 7, Usage::Tag::Declaration), makeItem(5, 4, Usage::Tags()),
makeItem(13, 21, Usage::Tags()), makeItem(32, 8, Usage::Tags())};
- QTest::newRow("constructor") << "defs.h" << 627 << ItemList{
+ QTest::newRow("constructor") << "defs.h" << 627 << SearchResultItems{
makeItem(31, 4, (Usage::Tags{Usage::Tag::Declaration, Usage::Tag::ConstructorDestructor})),
makeItem(36, 7, Usage::Tag::ConstructorDestructor)};
- QTest::newRow("subclass") << "defs.h" << 450 << ItemList{
+ QTest::newRow("subclass") << "defs.h" << 450 << SearchResultItems{
makeItem(20, 7, Usage::Tag::Declaration), makeItem(5, 4, Usage::Tags()),
makeItem(13, 21, Usage::Tags()), makeItem(32, 8, Usage::Tags())};
- QTest::newRow("array variable") << "main.cpp" << 1134 << ItemList{
+ QTest::newRow("array variable") << "main.cpp" << 1134 << SearchResultItems{
makeItem(57, 8, Usage::Tag::Declaration), makeItem(58, 4, Usage::Tag::Write),
makeItem(59, 15, Usage::Tag::Read)};
- QTest::newRow("free function") << "defs.h" << 510 << ItemList{
+ QTest::newRow("free function") << "defs.h" << 510 << SearchResultItems{
makeItem(24, 5, Usage::Tag::Declaration), makeItem(19, 4, Usage::Tags()),
makeItem(25, 4, Usage::Tags()), makeItem(60, 26, Usage::Tag::Read)};
- QTest::newRow("member function") << "defs.h" << 192 << ItemList{
+ QTest::newRow("member function") << "defs.h" << 192 << SearchResultItems{
makeItem(9, 12, Usage::Tag::Declaration), makeItem(40, 8, Usage::Tags())};
}
@@ -288,7 +308,7 @@ void ClangdTestFindReferences::test()
{
QFETCH(QString, fileName);
QFETCH(int, pos);
- QFETCH(QList<SearchResultItem>, expectedResults);
+ QFETCH(SearchResultItems, expectedResults);
TextEditor::TextDocument * const doc = document(fileName);
QVERIFY(doc);
@@ -388,13 +408,13 @@ void ClangdTestFollowSymbol::test()
timer.setSingleShot(true);
QEventLoop loop;
QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
- Utils::Link actualLink;
- const auto handler = [&actualLink, &loop](const Utils::Link &l) {
+ Link actualLink;
+ const auto handler = [&actualLink, &loop](const Link &l) {
actualLink = l;
loop.quit();
};
QTextCursor cursor(doc->document());
- const int pos = Utils::Text::positionInText(doc->document(), sourceLine, sourceColumn);
+ const int pos = Text::positionInText(doc->document(), sourceLine, sourceColumn);
cursor.setPosition(pos);
client()->followSymbol(doc, cursor, nullptr, handler, true,
goToType ? FollowTo::SymbolType : FollowTo::SymbolDef, false);
@@ -500,15 +520,14 @@ void ClangdTestLocalReferences::test()
QEventLoop loop;
QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
QList<Range> actualRanges;
- const auto handler = [&actualRanges, &loop](const QString &symbol,
- const Utils::Links &links, int) {
- for (const Utils::Link &link : links)
+ const auto handler = [&actualRanges, &loop](const QString &symbol, const Links &links, int) {
+ for (const Link &link : links)
actualRanges << Range(link.targetLine, link.targetColumn, symbol.length());
loop.quit();
};
QTextCursor cursor(doc->document());
- const int pos = Utils::Text::positionInText(doc->document(), sourceLine, sourceColumn);
+ const int pos = Text::positionInText(doc->document(), sourceLine, sourceColumn);
cursor.setPosition(pos);
client()->findLocalUsages(doc, cursor, std::move(handler));
timer.start(10000);
@@ -639,7 +658,7 @@ void ClangdTestTooltips::test()
connect(client(), &ClangdClient::helpItemGathered, &loop, handler);
QTextCursor cursor(doc->document());
- const int pos = Utils::Text::positionInText(doc->document(), line, column);
+ const int pos = Text::positionInText(doc->document(), line, column);
cursor.setPosition(pos);
editor->editorWidget()->processTooltipRequest(cursor);
@@ -1296,11 +1315,11 @@ void ClangdTestHighlighting::test()
const TextEditor::TextDocument * const doc = document("highlighting.cpp");
QVERIFY(doc);
- const int startPos = Utils::Text::positionInText(doc->document(), firstLine, startColumn);
- const int endPos = Utils::Text::positionInText(doc->document(), lastLine, endColumn);
+ const int startPos = Text::positionInText(doc->document(), firstLine, startColumn);
+ const int endPos = Text::positionInText(doc->document(), lastLine, endColumn);
const auto lessThan = [=](const TextEditor::HighlightingResult &r, int) {
- return Utils::Text::positionInText(doc->document(), r.line, r.column) < startPos;
+ return Text::positionInText(doc->document(), r.line, r.column) < startPos;
};
const auto findResults = [=] {
TextEditor::HighlightingResults results;
@@ -1308,7 +1327,7 @@ void ClangdTestHighlighting::test()
if (it == m_results.cend())
return results;
while (it != m_results.cend()) {
- const int resultEndPos = Utils::Text::positionInText(doc->document(), it->line,
+ const int resultEndPos = Text::positionInText(doc->document(), it->line,
it->column) + it->length;
if (resultEndPos > endPos)
break;
@@ -1419,7 +1438,7 @@ public:
{
const int pos = currentPosition();
QPair<int, int> lineAndColumn;
- Utils::Text::convertPosition(m_doc, pos, &lineAndColumn.first, &lineAndColumn.second);
+ Text::convertPosition(m_doc, pos, &lineAndColumn.first, &lineAndColumn.second);
return lineAndColumn;
}
@@ -1520,7 +1539,7 @@ void ClangdTestCompletion::testCompleteGlobals()
Manipulator manipulator;
item->apply(manipulator, cursorPos);
QCOMPARE(manipulator.getLine(7), " globalFunction() /* COMPLETE HERE */");
- QCOMPARE(manipulator.cursorPos(), qMakePair(7, 20));
+ QCOMPARE(manipulator.cursorPos(), qMakePair(7, 19));
QCOMPARE(manipulator.skipPos(), -1);
}
@@ -1540,7 +1559,7 @@ void ClangdTestCompletion::testCompleteMembers()
Manipulator manipulator;
item->apply(manipulator, cursorPos);
QCOMPARE(manipulator.getLine(7), " s.member /* COMPLETE HERE */");
- QCOMPARE(manipulator.cursorPos(), qMakePair(7, 13));
+ QCOMPARE(manipulator.cursorPos(), qMakePair(7, 12));
QCOMPARE(manipulator.skipPos(), -1);
}
@@ -1558,7 +1577,7 @@ void ClangdTestCompletion::testCompleteMembersFromInside()
Manipulator manipulator;
item->apply(manipulator, cursorPos);
QCOMPARE(manipulator.getLine(4), " privateFunc() /* COMPLETE HERE */");
- QCOMPARE(manipulator.cursorPos(), qMakePair(4, 22));
+ QCOMPARE(manipulator.cursorPos(), qMakePair(4, 21));
QCOMPARE(manipulator.skipPos(), -1);
}
@@ -1576,7 +1595,7 @@ void ClangdTestCompletion::testCompleteMembersFromOutside()
Manipulator manipulator;
item->apply(manipulator, cursorPos);
QCOMPARE(manipulator.getLine(13), " c.publicFunc() /* COMPLETE HERE */");
- QCOMPARE(manipulator.cursorPos(), qMakePair(13, 19));
+ QCOMPARE(manipulator.cursorPos(), qMakePair(13, 18));
QCOMPARE(manipulator.skipPos(), -1);
}
@@ -1594,7 +1613,7 @@ void ClangdTestCompletion::testCompleteMembersFromFriend()
Manipulator manipulator;
item->apply(manipulator, cursorPos);
QCOMPARE(manipulator.getLine(14), " C().privateFunc() /* COMPLETE HERE */");
- QCOMPARE(manipulator.cursorPos(), qMakePair(14, 22));
+ QCOMPARE(manipulator.cursorPos(), qMakePair(14, 21));
QCOMPARE(manipulator.skipPos(), -1);
}
@@ -1611,7 +1630,7 @@ void ClangdTestCompletion::testFunctionAddress()
Manipulator manipulator;
item->apply(manipulator, cursorPos);
QCOMPARE(manipulator.getLine(7), " const auto p = &S::memberFunc /* COMPLETE HERE */;");
- QCOMPARE(manipulator.cursorPos(), qMakePair(7, 34));
+ QCOMPARE(manipulator.cursorPos(), qMakePair(7, 33));
QCOMPARE(manipulator.skipPos(), -1);
}
@@ -1677,7 +1696,7 @@ void ClangdTestCompletion::testCompleteClassAndConstructor()
Manipulator manipulator;
item->apply(manipulator, cursorPos);
QCOMPARE(manipulator.getLine(7), " Foo( /* COMPLETE HERE */");
- QCOMPARE(manipulator.cursorPos(), qMakePair(7, 9));
+ QCOMPARE(manipulator.cursorPos(), qMakePair(7, 8));
QCOMPARE(manipulator.skipPos(), -1);
}
@@ -1704,7 +1723,7 @@ void ClangdTestCompletion::testCompleteWithDotToArrowCorrection()
Manipulator manipulator;
item->apply(manipulator, cursorPos);
QCOMPARE(manipulator.getLine(4), " bar->member /* COMPLETE HERE */");
- QCOMPARE(manipulator.cursorPos(), qMakePair(4, 16));
+ QCOMPARE(manipulator.cursorPos(), qMakePair(4, 15));
QCOMPARE(manipulator.skipPos(), -1);
}
@@ -1735,7 +1754,7 @@ void ClangdTestCompletion::testCompleteCodeInGeneratedUiFile()
Manipulator manipulator;
item->apply(manipulator, cursorPos);
QCOMPARE(manipulator.getLine(34), " ui->setupUi( /* COMPLETE HERE */");
- QCOMPARE(manipulator.cursorPos(), qMakePair(34, 17));
+ QCOMPARE(manipulator.cursorPos(), qMakePair(34, 16));
QCOMPARE(manipulator.skipPos(), -1);
}
@@ -1844,7 +1863,7 @@ void ClangdTestCompletion::startCollectingHighlightingInfo()
{
m_documentsWithHighlighting.clear();
connect(client(), &ClangdClient::highlightingResultsReady, this,
- [this](const HighlightingResults &, const Utils::FilePath &file) {
+ [this](const HighlightingResults &, const FilePath &file) {
m_documentsWithHighlighting.insert(file);
});
}
@@ -1861,9 +1880,9 @@ void ClangdTestCompletion::getProposal(const QString &fileName,
if (cursorPos)
*cursorPos = pos;
int line, column;
- Utils::Text::convertPosition(doc->document(), pos, &line, &column);
+ Text::convertPosition(doc->document(), pos, &line, &column);
const auto editor = qobject_cast<BaseTextEditor *>(
- EditorManager::openEditorAt({doc->filePath(), line, column - 1}));
+ EditorManager::openEditorAt({doc->filePath(), line, column}));
QVERIFY(editor);
QCOMPARE(EditorManager::currentEditor(), editor);
QCOMPARE(editor->textDocument(), doc);
diff --git a/src/plugins/clangcodemodel/test/clangdtests.h b/src/plugins/clangcodemodel/test/clangdtests.h
index 2a8f33579c..90f4eca45a 100644
--- a/src/plugins/clangcodemodel/test/clangdtests.h
+++ b/src/plugins/clangcodemodel/test/clangdtests.h
@@ -4,11 +4,11 @@
#pragma once
#include <cppeditor/cpptoolstestcase.h>
-#include <coreplugin/find/searchresultitem.h>
#include <texteditor/blockrange.h>
#include <texteditor/codeassist/genericproposal.h>
#include <texteditor/semantichighlighter.h>
#include <utils/fileutils.h>
+#include <utils/searchresultitem.h>
#include <QHash>
#include <QObject>
@@ -74,7 +74,7 @@ private slots:
void test();
private:
- QList<Core::SearchResultItem> m_actualResults;
+ Utils::SearchResultItems m_actualResults;
};
class ClangdTestFollowSymbol : public ClangdTest
diff --git a/src/plugins/clangformat/CMakeLists.txt b/src/plugins/clangformat/CMakeLists.txt
index 79767a2ffe..7c49e9ad47 100644
--- a/src/plugins/clangformat/CMakeLists.txt
+++ b/src/plugins/clangformat/CMakeLists.txt
@@ -39,4 +39,5 @@ extend_qtc_plugin(ClangFormat
tests/clangformat-test.h
DEFINES
TESTDATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/tests/data"
+ EXPLICIT_MOC tests/clangformat-test.h
)
diff --git a/src/plugins/clangformat/clangformat.qbs b/src/plugins/clangformat/clangformat.qbs
index bd1b967763..057035cd1a 100644
--- a/src/plugins/clangformat/clangformat.qbs
+++ b/src/plugins/clangformat/clangformat.qbs
@@ -54,10 +54,8 @@ QtcPlugin {
"clangformatutils.cpp",
]
- Group {
- name: "Tests"
+ QtcTestFiles {
prefix: "tests/"
- condition: qtc.testsEnabled
cpp.defines: outer.concat('TESTDATA_DIR="' + sourceDirectory + "/tests/data" + '"')
files: [
"clangformat-test.cpp",
diff --git a/src/plugins/clangformat/clangformatbaseindenter.cpp b/src/plugins/clangformat/clangformatbaseindenter.cpp
index affabfec67..5138e03510 100644
--- a/src/plugins/clangformat/clangformatbaseindenter.cpp
+++ b/src/plugins/clangformat/clangformatbaseindenter.cpp
@@ -9,7 +9,7 @@
#include <projectexplorer/editorconfiguration.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <texteditor/icodestylepreferences.h>
#include <texteditor/texteditorsettings.h>
@@ -56,9 +56,6 @@ void adjustFormatStyleForLineBreak(clang::format::FormatStyle &style,
return;
style.ColumnLimit = 0;
-#ifdef KEEP_LINE_BREAKS_FOR_NON_EMPTY_LINES_BACKPORTED
- style.KeepLineBreaksForNonEmptyLines = true;
-#endif
}
llvm::StringRef clearExtraNewline(llvm::StringRef text)
@@ -349,6 +346,17 @@ bool isInsideDummyTextInLine(const QString &originalLine, const QString &modifie
|| !modifiedLine.startsWith(originalLine));
}
+static Utils::Text::Position utf16LineColumn(const QByteArray &utf8Buffer, int utf8Offset)
+{
+ Utils::Text::Position position;
+ position.line = static_cast<int>(std::count(utf8Buffer.begin(),
+ utf8Buffer.begin() + utf8Offset, '\n')) + 1;
+ const int startOfLineOffset = utf8Offset ? (utf8Buffer.lastIndexOf('\n', utf8Offset - 1) + 1)
+ : 0;
+ position.column = QString::fromUtf8(utf8Buffer.mid(startOfLineOffset,
+ utf8Offset - startOfLineOffset)).length();
+ return position;
+}
Utils::Text::Replacements utf16Replacements(const QTextDocument *doc,
const QByteArray &utf8Buffer,
const clang::tooling::Replacements &replacements)
@@ -357,9 +365,8 @@ Utils::Text::Replacements utf16Replacements(const QTextDocument *doc,
convertedReplacements.reserve(replacements.size());
for (const clang::tooling::Replacement &replacement : replacements) {
- Utils::LineColumn lineColUtf16 = Utils::Text::utf16LineColumn(utf8Buffer,
- static_cast<int>(
- replacement.getOffset()));
+ Utils::Text::Position lineColUtf16 = utf16LineColumn(
+ utf8Buffer, static_cast<int>(replacement.getOffset()));
if (!lineColUtf16.isValid())
continue;
@@ -367,14 +374,13 @@ Utils::Text::Replacements utf16Replacements(const QTextDocument *doc,
const QString bufferLineText
= Utils::Text::utf16LineTextInUtf8Buffer(utf8Buffer,
static_cast<int>(replacement.getOffset()));
- if (isInsideDummyTextInLine(lineText, bufferLineText, lineColUtf16.column))
+ if (isInsideDummyTextInLine(lineText, bufferLineText, lineColUtf16.column + 1))
continue;
- lineColUtf16.column = std::min(lineColUtf16.column, int(lineText.length()) + 1);
-
+ lineColUtf16.column = std::min(lineColUtf16.column, int(lineText.length()));
const int utf16Offset = Utils::Text::positionInText(doc,
lineColUtf16.line,
- lineColUtf16.column);
+ lineColUtf16.column + 1);
const int utf16Length = QString::fromUtf8(
utf8Buffer.mid(static_cast<int>(replacement.getOffset()),
static_cast<int>(replacement.getLength())))
@@ -744,7 +750,7 @@ void ClangFormatBaseIndenter::autoIndent(const QTextCursor &cursor,
clang::format::FormatStyle overrideStyle(const Utils::FilePath &fileName)
{
const ProjectExplorer::Project *projectForFile
- = ProjectExplorer::SessionManager::projectForFile(fileName);
+ = ProjectExplorer::ProjectManager::projectForFile(fileName);
const TextEditor::ICodeStylePreferences *preferences
= projectForFile
diff --git a/src/plugins/clangformat/clangformatchecks.cpp b/src/plugins/clangformat/clangformatchecks.cpp
index 1ec64e7a37..6027e600d7 100644
--- a/src/plugins/clangformat/clangformatchecks.cpp
+++ b/src/plugins/clangformat/clangformatchecks.cpp
@@ -15,8 +15,6 @@
#include <QPushButton>
#include <QWidget>
-using namespace Utils;
-
using namespace ClangFormat;
ClangFormatChecks::ClangFormatChecks(QWidget *parent)
diff --git a/src/plugins/clangformat/clangformatconfigwidget.cpp b/src/plugins/clangformat/clangformatconfigwidget.cpp
index 12e02b4fa7..e2f9272e14 100644
--- a/src/plugins/clangformat/clangformatconfigwidget.cpp
+++ b/src/plugins/clangformat/clangformatconfigwidget.cpp
@@ -82,15 +82,14 @@ ClangFormatConfigWidget::ClangFormatConfigWidget(TextEditor::ICodeStylePreferenc
d->project = project;
d->config = std::make_unique<ClangFormatFile>(filePathToCurrentSettings(codeStyle->currentPreferences()));
- resize(489, 305);
d->fallbackConfig = new QLabel(Tr::tr("Clang-Format Style"));
d->checksScrollArea = new QScrollArea();
d->checksWidget = new ClangFormatChecks();
d->checksScrollArea->setWidget(d->checksWidget);
d->checksScrollArea->setWidgetResizable(true);
- d->checksWidget->setEnabled(!codeStyle->isReadOnly()
- && !codeStyle->isTemporarilyReadOnly());
+ d->checksWidget->setEnabled(!codeStyle->isReadOnly() && !codeStyle->isTemporarilyReadOnly()
+ && !codeStyle->isAdditionalTabDisabled());
FilePath fileName;
if (d->project)
@@ -141,8 +140,8 @@ void ClangFormatConfigWidget::slotCodeStyleChanged(
d->config->setIsReadOnly(codeStyle->isReadOnly());
d->style = d->config->style();
- d->checksWidget->setEnabled(!codeStyle->isReadOnly()
- && !codeStyle->isTemporarilyReadOnly());
+ d->checksWidget->setEnabled(!codeStyle->isReadOnly() && !codeStyle->isTemporarilyReadOnly()
+ && !codeStyle->isAdditionalTabDisabled());
fillTable();
updatePreview();
diff --git a/src/plugins/clangformat/clangformatfile.cpp b/src/plugins/clangformat/clangformatfile.cpp
index 7023645cc8..f0c4809913 100644
--- a/src/plugins/clangformat/clangformatfile.cpp
+++ b/src/plugins/clangformat/clangformatfile.cpp
@@ -169,6 +169,12 @@ CppEditor::CppCodeStyleSettings ClangFormatFile::toCppCodeStyleSettings(
settings.bindStarToRightSpecifier = style.PointerAlignment == FormatStyle::PAS_Right;
}
+ settings.extraPaddingForConditionsIfConfusingAlign = style.BreakBeforeBinaryOperators
+ == FormatStyle::BOS_All;
+ settings.alignAssignments = style.BreakBeforeBinaryOperators == FormatStyle::BOS_All
+ || style.BreakBeforeBinaryOperators
+ == FormatStyle::BOS_NonAssignment;
+
return settings;
}
@@ -188,6 +194,9 @@ void ClangFormatFile::fromCppCodeStyleSettings(const CppEditor::CppCodeStyleSett
if (settings.indentClassBraces || settings.indentEnumBraces || settings.indentBlockBraces
|| settings.indentFunctionBraces)
m_style.BreakBeforeBraces = FormatStyle::BS_Whitesmiths;
+ else
+ m_style.BreakBeforeBraces = FormatStyle::BS_Custom;
+
m_style.IndentCaseLabels = settings.indentSwitchLabels;
#if LLVM_VERSION_MAJOR >= 11
@@ -196,11 +205,12 @@ void ClangFormatFile::fromCppCodeStyleSettings(const CppEditor::CppCodeStyleSett
|| settings.indentControlFlowRelativeToSwitchLabels;
#endif
- if (settings.alignAssignments)
- m_style.BreakBeforeBinaryOperators = FormatStyle::BOS_NonAssignment;
-
if (settings.extraPaddingForConditionsIfConfusingAlign)
m_style.BreakBeforeBinaryOperators = FormatStyle::BOS_All;
+ else if (settings.alignAssignments)
+ m_style.BreakBeforeBinaryOperators = FormatStyle::BOS_NonAssignment;
+ else
+ m_style.BreakBeforeBinaryOperators = FormatStyle::BOS_None;
m_style.DerivePointerAlignment = settings.bindStarToIdentifier || settings.bindStarToTypeName
|| settings.bindStarToLeftSpecifier
diff --git a/src/plugins/clangformat/clangformatfile.h b/src/plugins/clangformat/clangformatfile.h
index 6e2267befc..dcd0e0d6c1 100644
--- a/src/plugins/clangformat/clangformatfile.h
+++ b/src/plugins/clangformat/clangformatfile.h
@@ -3,7 +3,8 @@
#pragma once
-#include "utils/filepath.h"
+#include <utils/filepath.h>
+
#include <clang/Format/Format.h>
namespace CppEditor { class CppCodeStyleSettings; }
diff --git a/src/plugins/clangformat/clangformatglobalconfigwidget.cpp b/src/plugins/clangformat/clangformatglobalconfigwidget.cpp
index a0344e14ed..af8a0846b1 100644
--- a/src/plugins/clangformat/clangformatglobalconfigwidget.cpp
+++ b/src/plugins/clangformat/clangformatglobalconfigwidget.cpp
@@ -31,34 +31,37 @@ ClangFormatGlobalConfigWidget::ClangFormatGlobalConfigWidget(
, m_project(project)
, m_codeStyle(codeStyle)
{
- resize(489, 305);
-
m_projectHasClangFormat = new QLabel(this);
m_formattingModeLabel = new QLabel(Tr::tr("Formatting mode:"));
m_indentingOrFormatting = new QComboBox(this);
m_formatWhileTyping = new QCheckBox(Tr::tr("Format while typing"));
m_formatOnSave = new QCheckBox(Tr::tr("Format edited code on file save"));
- m_overrideDefault = new QCheckBox(Tr::tr("Override Clang Format configuration file"));
+ m_overrideDefault = new QCheckBox(Tr::tr("Override .clang-format file"));
m_useGlobalSettings = new QCheckBox(Tr::tr("Use global settings"));
m_useGlobalSettings->hide();
+ m_overrideDefaultFile = ClangFormatSettings::instance().overrideDefaultFile();
using namespace Layouting;
+ QWidget *globalSettingsGroupBoxWidget = nullptr;
+
Group globalSettingsGroupBox {
+ bindTo(&globalSettingsGroupBoxWidget),
title(Tr::tr("ClangFormat settings:")),
- Column {
- m_useGlobalSettings,
- Row { m_formattingModeLabel, m_indentingOrFormatting, st },
- m_formatWhileTyping,
- m_formatOnSave,
- m_projectHasClangFormat,
- m_overrideDefault
+ Column {
+ m_useGlobalSettings,
+ Row { m_formattingModeLabel, m_indentingOrFormatting, st },
+ m_formatWhileTyping,
+ m_formatOnSave,
+ m_projectHasClangFormat,
+ m_overrideDefault
}
};
Column {
- globalSettingsGroupBox
- }.attachTo(this, Layouting::WithoutMargins);
+ globalSettingsGroupBox,
+ noMargin
+ }.attachTo(this);
initCheckBoxes();
initIndentationOrFormattingCombobox();
@@ -73,7 +76,8 @@ ClangFormatGlobalConfigWidget::ClangFormatGlobalConfigWidget(
m_useGlobalSettings->show();
return;
}
- globalSettingsGroupBox.widget->show();
+
+ globalSettingsGroupBoxWidget->show();
}
ClangFormatGlobalConfigWidget::~ClangFormatGlobalConfigWidget() = default;
@@ -153,33 +157,52 @@ void ClangFormatGlobalConfigWidget::initOverrideCheckBox()
"can be overridden by the settings below."));
}
- auto setEnableOverrideCheckBox = [this](int index) {
+ auto setTemporarilyReadOnly = [this]() {
+ if (m_ignoreChanges.isLocked())
+ return;
+ Utils::GuardLocker locker(m_ignoreChanges);
+ m_codeStyle->currentPreferences()->setTemporarilyReadOnly(!m_overrideDefault->isChecked());
+ m_codeStyle->currentPreferences()->setIsAdditionalTabDisabled(!m_overrideDefault->isEnabled());
+ ClangFormatSettings::instance().write();
+ emit m_codeStyle->currentPreferencesChanged(m_codeStyle->currentPreferences());
+ };
+
+ auto setEnableOverrideCheckBox = [this, setTemporarilyReadOnly](int index) {
bool isDisable = index == static_cast<int>(ClangFormatSettings::Mode::Disable);
m_overrideDefault->setDisabled(isDisable);
+ setTemporarilyReadOnly();
};
setEnableOverrideCheckBox(m_indentingOrFormatting->currentIndex());
connect(m_indentingOrFormatting, &QComboBox::currentIndexChanged,
this, setEnableOverrideCheckBox);
- m_overrideDefault->setToolTip(
- Tr::tr("Override Clang Format configuration file with the chosen configuration."));
+ m_overrideDefault->setToolTip(Tr::tr(
+ "When this option is enabled, ClangFormat will use a\n"
+ "user-specified configuration from the widget below,\n"
+ "instead of the project .clang-format file. You can\n"
+ "customize the formatting options for your code by\n"
+ "adjusting the settings in the widget. Note that any\n"
+ "changes made there will only affect the current\n"
+ "configuration, and will not modify the project\n"
+ ".clang-format file."));
m_overrideDefault->setChecked(getProjectOverriddenSettings(m_project));
- m_codeStyle->currentPreferences()->setTemporarilyReadOnly(!m_overrideDefault->isChecked());
+ setTemporarilyReadOnly();
- connect(m_overrideDefault, &QCheckBox::toggled, this, [this](bool checked) {
- if (m_project)
+ connect(m_overrideDefault, &QCheckBox::toggled, this, [this, setTemporarilyReadOnly](bool checked) {
+ if (m_project) {
m_project->setNamedSettings(Constants::OVERRIDE_FILE_ID, checked);
- else {
- m_codeStyle->currentPreferences()->setTemporarilyReadOnly(!checked);
- emit m_codeStyle->currentPreferencesChanged(m_codeStyle->currentPreferences());
+ } else {
+ ClangFormatSettings::instance().setOverrideDefaultFile(checked);
+ setTemporarilyReadOnly();
}
});
- connect(m_codeStyle, &TextEditor::ICodeStylePreferences::currentPreferencesChanged, this, [this] {
- m_codeStyle->currentPreferences()->setTemporarilyReadOnly(!m_overrideDefault->isChecked());
- });
+ connect(m_codeStyle,
+ &TextEditor::ICodeStylePreferences::currentPreferencesChanged,
+ this,
+ setTemporarilyReadOnly);
}
@@ -192,12 +215,14 @@ void ClangFormatGlobalConfigWidget::apply()
settings.setMode(
static_cast<ClangFormatSettings::Mode>(m_indentingOrFormatting->currentIndex()));
settings.setOverrideDefaultFile(m_overrideDefault->isChecked());
+ m_overrideDefaultFile = m_overrideDefault->isChecked();
}
settings.write();
}
void ClangFormatGlobalConfigWidget::finish()
{
+ ClangFormatSettings::instance().setOverrideDefaultFile(m_overrideDefaultFile);
m_codeStyle->currentPreferences()->setTemporarilyReadOnly(
!ClangFormatSettings::instance().overrideDefaultFile());
}
diff --git a/src/plugins/clangformat/clangformatglobalconfigwidget.h b/src/plugins/clangformat/clangformatglobalconfigwidget.h
index 063c82852a..7b2d6fe7c9 100644
--- a/src/plugins/clangformat/clangformatglobalconfigwidget.h
+++ b/src/plugins/clangformat/clangformatglobalconfigwidget.h
@@ -5,6 +5,8 @@
#include <cppeditor/cppcodestylesettingspage.h>
+#include <utils/guard.h>
+
#include <memory>
QT_BEGIN_NAMESPACE
@@ -40,6 +42,8 @@ private:
ProjectExplorer::Project *m_project;
TextEditor::ICodeStylePreferences *m_codeStyle;
+ Utils::Guard m_ignoreChanges;
+ bool m_overrideDefaultFile;
QLabel *m_projectHasClangFormat;
QLabel *m_formattingModeLabel;
diff --git a/src/plugins/clangformat/clangformatindenter.cpp b/src/plugins/clangformat/clangformatindenter.cpp
index 94e0fafd8e..67d5171b01 100644
--- a/src/plugins/clangformat/clangformatindenter.cpp
+++ b/src/plugins/clangformat/clangformatindenter.cpp
@@ -15,7 +15,7 @@
#include <utils/genericconstants.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <texteditor/tabsettings.h>
#include <texteditor/textdocumentlayout.h>
diff --git a/src/plugins/clangformat/clangformatutils.cpp b/src/plugins/clangformat/clangformatutils.cpp
index 120e27fde7..9304599d99 100644
--- a/src/plugins/clangformat/clangformatutils.cpp
+++ b/src/plugins/clangformat/clangformatutils.cpp
@@ -15,7 +15,7 @@
#include <projectexplorer/editorconfiguration.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/qtcassert.h>
@@ -210,13 +210,12 @@ bool getProjectOverriddenSettings(const ProjectExplorer::Project *project)
bool getCurrentOverriddenSettings(const Utils::FilePath &filePath)
{
- const ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(
+ const ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::projectForFile(
filePath);
- return getProjectUseGlobalSettings(project) ? !TextEditor::TextEditorSettings::codeStyle("Cpp")
- ->currentPreferences()
- ->isTemporarilyReadOnly()
- : getProjectOverriddenSettings(project);
+ return getProjectUseGlobalSettings(project)
+ ? ClangFormatSettings::instance().overrideDefaultFile()
+ : getProjectOverriddenSettings(project);
}
ClangFormatSettings::Mode getProjectIndentationOrFormattingSettings(
@@ -233,7 +232,7 @@ ClangFormatSettings::Mode getProjectIndentationOrFormattingSettings(
ClangFormatSettings::Mode getCurrentIndentationOrFormattingSettings(const Utils::FilePath &filePath)
{
- const ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(
+ const ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::projectForFile(
filePath);
return getProjectUseGlobalSettings(project)
@@ -264,7 +263,7 @@ Utils::FilePath configForFile(const Utils::FilePath &fileName)
return findConfig(fileName);
const ProjectExplorer::Project *projectForFile
- = ProjectExplorer::SessionManager::projectForFile(fileName);
+ = ProjectExplorer::ProjectManager::projectForFile(fileName);
const TextEditor::ICodeStylePreferences *preferences
= projectForFile
diff --git a/src/plugins/clangtools/clangtool.cpp b/src/plugins/clangtools/clangtool.cpp
index 109f23875c..00da4bb880 100644
--- a/src/plugins/clangtools/clangtool.cpp
+++ b/src/plugins/clangtools/clangtool.cpp
@@ -34,7 +34,7 @@
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectexplorericons.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
@@ -342,7 +342,7 @@ static FileInfos sortedFileInfos(const QVector<CppEditor::ProjectPart::ConstPtr>
static RunSettings runSettings()
{
- if (Project *project = SessionManager::startupProject()) {
+ if (Project *project = ProjectManager::startupProject()) {
const auto projectSettings = ClangToolsProjectSettings::getSettings(project);
if (!projectSettings->useGlobalSettings())
return projectSettings->runSettings();
@@ -599,19 +599,18 @@ static bool continueDespiteReleaseBuild(const QString &toolName)
"<p>%2</p>"
"</body></html>")
.arg(problem, question);
- return CheckableMessageBox::doNotAskAgainQuestion(ICore::dialogParent(),
- title,
- message,
- ICore::settings(),
- "ClangToolsCorrectModeWarning")
- == QDialogButtonBox::Yes;
+ return CheckableMessageBox::question(ICore::dialogParent(),
+ title,
+ message,
+ QString("ClangToolsCorrectModeWarning"))
+ == QMessageBox::Yes;
}
void ClangTool::startTool(ClangTool::FileSelection fileSelection,
const RunSettings &runSettings,
const CppEditor::ClangDiagnosticConfig &diagnosticConfig)
{
- Project *project = SessionManager::startupProject();
+ Project *project = ProjectManager::startupProject();
QTC_ASSERT(project, return);
QTC_ASSERT(project->activeTarget(), return);
@@ -858,19 +857,19 @@ static CheckResult canAnalyze()
{
const ClangDiagnosticConfig config = diagnosticConfig(runSettings().diagnosticConfigId());
- if (config.isEnabled(ClangToolType::Tidy)
+ if (toolEnabled(ClangToolType::Tidy, config, runSettings())
&& !toolExecutable(ClangToolType::Tidy).isExecutableFile()) {
return {CheckResult::InvalidTidyExecutable,
Tr::tr("Set a valid Clang-Tidy executable.")};
}
- if (config.isEnabled(ClangToolType::Clazy)
+ if (toolEnabled(ClangToolType::Clazy, config, runSettings())
&& !toolExecutable(ClangToolType::Clazy).isExecutableFile()) {
return {CheckResult::InvalidClazyExecutable,
Tr::tr("Set a valid Clazy-Standalone executable.")};
}
- if (Project *project = SessionManager::startupProject()) {
+ if (Project *project = ProjectManager::startupProject()) {
if (!canAnalyzeProject(project)) {
return {CheckResult::ProjectNotSuitable,
Tr::tr("Project \"%1\" is not a C/C++ project.")
diff --git a/src/plugins/clangtools/clangtoolruncontrol.cpp b/src/plugins/clangtools/clangtoolruncontrol.cpp
index e583a6269d..11353b04ae 100644
--- a/src/plugins/clangtools/clangtoolruncontrol.cpp
+++ b/src/plugins/clangtools/clangtoolruncontrol.cpp
@@ -21,14 +21,14 @@
#include <projectexplorer/toolchain.h>
#include <utils/algorithm.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/stringutils.h>
-#include <utils/tasktree.h>
#include <QLoggingCategory>
using namespace CppEditor;
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.runcontrol", QtWarningMsg)
@@ -183,10 +183,13 @@ void ClangToolRunWorker::start()
m_filesAnalyzed.clear();
m_filesNotAnalyzed.clear();
- using namespace Tasking;
- QList<TaskItem> tasks{ParallelLimit(qMax(1, m_runSettings.parallelJobs()))};
+ QList<TaskItem> tasks{parallelLimit(qMax(1, m_runSettings.parallelJobs()))};
for (const AnalyzeUnit &unit : std::as_const(unitsToProcess)) {
- const AnalyzeInputData input{tool, m_diagnosticConfig, m_temporaryDir.path(),
+ if (!m_diagnosticConfig.isEnabled(tool)
+ && !m_runSettings.hasConfigFileForSourceFile(unit.file)) {
+ continue;
+ }
+ const AnalyzeInputData input{tool, m_runSettings, m_diagnosticConfig, m_temporaryDir.path(),
m_environment, unit};
const auto setupHandler = [this, unit, tool] {
const QString filePath = unit.file.toUserOutput();
diff --git a/src/plugins/clangtools/clangtoolruncontrol.h b/src/plugins/clangtools/clangtoolruncontrol.h
index 19ad9189af..e6204da4ff 100644
--- a/src/plugins/clangtools/clangtoolruncontrol.h
+++ b/src/plugins/clangtools/clangtoolruncontrol.h
@@ -15,7 +15,7 @@
#include <QElapsedTimer>
#include <QSet>
-namespace Utils { class TaskTree; }
+namespace Tasking { class TaskTree; }
namespace ClangTools {
namespace Internal {
@@ -67,7 +67,7 @@ private:
QString m_targetTriple;
Utils::Id m_toolChainType;
- std::unique_ptr<Utils::TaskTree> m_taskTree;
+ std::unique_ptr<Tasking::TaskTree> m_taskTree;
QSet<Utils::FilePath> m_projectFiles;
QSet<Utils::FilePath> m_filesAnalyzed;
QSet<Utils::FilePath> m_filesNotAnalyzed;
diff --git a/src/plugins/clangtools/clangtoolrunner.cpp b/src/plugins/clangtools/clangtoolrunner.cpp
index f5c955c50e..3e2c7a6c11 100644
--- a/src/plugins/clangtools/clangtoolrunner.cpp
+++ b/src/plugins/clangtools/clangtoolrunner.cpp
@@ -11,8 +11,8 @@
#include <cppeditor/clangdiagnosticconfigsmodel.h>
#include <cppeditor/cpptoolsreuse.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/temporaryfile.h>
#include <QDebug>
@@ -52,21 +52,22 @@ static bool isClMode(const QStringList &options)
return options.contains("--driver-mode=cl");
}
-static QStringList checksArguments(ClangToolType tool,
- const ClangDiagnosticConfig &diagnosticConfig)
+static QStringList checksArguments(const AnalyzeInputData &input)
{
- if (tool == ClangToolType::Tidy) {
- const ClangDiagnosticConfig::TidyMode tidyMode = diagnosticConfig.clangTidyMode();
- // The argument "-config={}" stops stating/evaluating the .clang-tidy file.
- if (tidyMode == ClangDiagnosticConfig::TidyMode::UseDefaultChecks)
+ if (input.tool == ClangToolType::Tidy) {
+ if (input.runSettings.hasConfigFileForSourceFile(input.unit.file))
+ return {"--warnings-as-errors=-*", "-checks=-clang-diagnostic-*"};
+ switch (input.config.clangTidyMode()) {
+ case ClangDiagnosticConfig::TidyMode::UseDefaultChecks:
+ // The argument "-config={}" stops stating/evaluating the .clang-tidy file.
return {"-config={}", "-checks=-clang-diagnostic-*"};
- if (tidyMode == ClangDiagnosticConfig::TidyMode::UseCustomChecks)
- return {"-config=" + diagnosticConfig.clangTidyChecksAsJson()};
- return {"--warnings-as-errors=-*", "-checks=-clang-diagnostic-*"};
+ case ClangDiagnosticConfig::TidyMode::UseCustomChecks:
+ return {"-config=" + input.config.clangTidyChecksAsJson()};
+ }
}
- const QString clazyChecks = diagnosticConfig.checks(ClangToolType::Clazy);
+ const QString clazyChecks = input.config.checks(ClangToolType::Clazy);
if (!clazyChecks.isEmpty())
- return {"-checks=" + diagnosticConfig.checks(ClangToolType::Clazy)};
+ return {"-checks=" + input.config.checks(ClangToolType::Clazy)};
return {};
}
@@ -111,17 +112,16 @@ TaskItem clangToolTask(const AnalyzeInputData &input,
};
const TreeStorage<ClangToolStorage> storage;
- const auto mainToolArguments = [=](const ClangToolStorage *data)
- {
+ const auto mainToolArguments = [=](const ClangToolStorage &data) {
QStringList result;
- result << "-export-fixes=" + data->outputFilePath.nativePath();
- if (!input.overlayFilePath.isEmpty() && isVFSOverlaySupported(data->executable))
+ result << "-export-fixes=" + data.outputFilePath.nativePath();
+ if (!input.overlayFilePath.isEmpty() && isVFSOverlaySupported(data.executable))
result << "--vfsoverlay=" + input.overlayFilePath;
result << input.unit.file.nativePath();
return result;
};
- const auto onGroupSetup = [=] {
+ const auto onSetup = [=] {
if (setupHandler && !setupHandler())
return TaskAction::StopWithError;
@@ -141,54 +141,53 @@ TaskItem clangToolTask(const AnalyzeInputData &input,
return TaskAction::Continue;
};
- const auto onProcessSetup = [=](QtcProcess &process) {
+ const auto onProcessSetup = [=](Process &process) {
process.setEnvironment(input.environment);
process.setUseCtrlCStub(true);
process.setLowPriority();
process.setWorkingDirectory(input.outputDirPath); // Current clang-cl puts log file into working dir.
- const ClangToolStorage *data = storage.activeStorage();
+ const ClangToolStorage &data = *storage;
- const QStringList args = checksArguments(input.tool, input.config)
+ const QStringList args = checksArguments(input)
+ mainToolArguments(data)
+ QStringList{"--"}
+ clangArguments(input.config, input.unit.arguments);
- const CommandLine commandLine = {data->executable, args};
+ const CommandLine commandLine = {data.executable, args};
qCDebug(LOG).noquote() << "Starting" << commandLine.toUserOutput();
process.setCommand(commandLine);
};
- const auto onProcessDone = [=](const QtcProcess &process) {
+ const auto onProcessDone = [=](const Process &process) {
qCDebug(LOG).noquote() << "Output:\n" << process.cleanedStdOut();
if (!outputHandler)
return;
- const ClangToolStorage *data = storage.activeStorage();
- outputHandler({true, input.unit.file, data->outputFilePath, input.tool});
+ outputHandler({true, input.unit.file, storage->outputFilePath, input.tool});
};
- const auto onProcessError = [=](const QtcProcess &process) {
+ const auto onProcessError = [=](const Process &process) {
if (!outputHandler)
return;
const QString details = Tr::tr("Command line: %1\nProcess Error: %2\nOutput:\n%3")
.arg(process.commandLine().toUserOutput())
.arg(process.error())
.arg(process.cleanedStdOut());
- const ClangToolStorage *data = storage.activeStorage();
+ const ClangToolStorage &data = *storage;
QString message;
if (process.result() == ProcessResult::StartFailed)
- message = Tr::tr("An error occurred with the %1 process.").arg(data->name);
+ message = Tr::tr("An error occurred with the %1 process.").arg(data.name);
else if (process.result() == ProcessResult::FinishedWithError)
- message = Tr::tr("%1 finished with exit code: %2.").arg(data->name).arg(process.exitCode());
+ message = Tr::tr("%1 finished with exit code: %2.").arg(data.name).arg(process.exitCode());
else
- message = Tr::tr("%1 crashed.").arg(data->name);
- outputHandler({false, input.unit.file, data->outputFilePath, input.tool, message, details});
+ message = Tr::tr("%1 crashed.").arg(data.name);
+ outputHandler({false, input.unit.file, data.outputFilePath, input.tool, message, details});
};
const Group group {
Storage(storage),
- OnGroupSetup(onGroupSetup),
+ onGroupSetup(onSetup),
Group {
- optional,
- Process(onProcessSetup, onProcessDone, onProcessError)
+ finishAllAndDone,
+ ProcessTask(onProcessSetup, onProcessDone, onProcessError)
}
};
return group;
diff --git a/src/plugins/clangtools/clangtoolrunner.h b/src/plugins/clangtools/clangtoolrunner.h
index 104e3ab90b..3a0f59f341 100644
--- a/src/plugins/clangtools/clangtoolrunner.h
+++ b/src/plugins/clangtools/clangtoolrunner.h
@@ -4,12 +4,13 @@
#pragma once
#include "clangfileinfo.h"
+#include "clangtoolssettings.h"
#include <cppeditor/clangdiagnosticconfig.h>
#include <utils/environment.h>
-namespace Utils::Tasking { class TaskItem; }
+namespace Tasking { class TaskItem; }
namespace ClangTools {
namespace Internal {
@@ -28,6 +29,7 @@ using AnalyzeUnits = QList<AnalyzeUnit>;
struct AnalyzeInputData
{
CppEditor::ClangToolType tool = CppEditor::ClangToolType::Tidy;
+ RunSettings runSettings;
CppEditor::ClangDiagnosticConfig config;
Utils::FilePath outputDirPath;
Utils::Environment environment;
@@ -48,9 +50,9 @@ struct AnalyzeOutputData
using AnalyzeSetupHandler = std::function<bool()>;
using AnalyzeOutputHandler = std::function<void(const AnalyzeOutputData &)>;
-Utils::Tasking::TaskItem clangToolTask(const AnalyzeInputData &input,
- const AnalyzeSetupHandler &setupHandler,
- const AnalyzeOutputHandler &outputHandler);
+Tasking::TaskItem clangToolTask(const AnalyzeInputData &input,
+ const AnalyzeSetupHandler &setupHandler,
+ const AnalyzeOutputHandler &outputHandler);
} // namespace Internal
} // namespace ClangTools
diff --git a/src/plugins/clangtools/clangtools.qbs b/src/plugins/clangtools/clangtools.qbs
index 9c86d4bb11..ad8543bf61 100644
--- a/src/plugins/clangtools/clangtools.qbs
+++ b/src/plugins/clangtools/clangtools.qbs
@@ -74,9 +74,7 @@ QtcPlugin {
"virtualfilesystemoverlay.h",
]
- Group {
- name: "Unit tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
"clangtoolspreconfiguredsessiontests.cpp",
"clangtoolspreconfiguredsessiontests.h",
diff --git a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp
index 39329c79f1..e0b797a1e0 100644
--- a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp
+++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp
@@ -10,14 +10,14 @@
#include "diagnosticmark.h"
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
+
#include <texteditor/textmark.h>
#include <utils/fsengine/fileiconprovider.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
-#include <QFileInfo>
#include <QLoggingCategory>
#include <tuple>
@@ -484,8 +484,8 @@ DiagnosticFilterModel::DiagnosticFilterModel(QObject *parent)
{
// So that when a user closes and re-opens a project and *then* clicks "Suppress",
// we enter that information into the project settings.
- connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::projectAdded, this,
+ connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::projectAdded, this,
[this](ProjectExplorer::Project *project) {
if (!m_project && project->projectDirectory() == m_lastProjectDirectory)
setProject(project);
diff --git a/src/plugins/clangtools/clangtoolsdiagnosticview.cpp b/src/plugins/clangtools/clangtoolsdiagnosticview.cpp
index a516ef3066..ed264b603d 100644
--- a/src/plugins/clangtools/clangtoolsdiagnosticview.cpp
+++ b/src/plugins/clangtools/clangtoolsdiagnosticview.cpp
@@ -313,9 +313,8 @@ bool DiagnosticView::disableChecksEnabled() const
if (!activeConfig.id().isValid())
return false;
- // If all selected diagnostics come from clang-tidy and the active config is controlled
- // by a .clang-tidy file, then we do not offer the action.
- if (activeConfig.clangTidyMode() != ClangDiagnosticConfig::TidyMode::UseConfigFile)
+ // If all selected diagnostics come from clang-tidy and we prefer a .clang-tidy file, then we do not offer the action.
+ if (!settings->runSettings().preferConfigFile())
return true;
return Utils::anyOf(indexes, [this](const QModelIndex &index) {
return model()->data(index).toString().startsWith("clazy-");
diff --git a/src/plugins/clangtools/clangtoolsplugin.cpp b/src/plugins/clangtools/clangtoolsplugin.cpp
index 6bd68d8710..35086be7aa 100644
--- a/src/plugins/clangtools/clangtoolsplugin.cpp
+++ b/src/plugins/clangtools/clangtoolsplugin.cpp
@@ -19,6 +19,7 @@
#include <utils/mimeutils.h>
#include <utils/qtcassert.h>
+#include <utils/stylehelper.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
@@ -171,7 +172,7 @@ void ClangToolsPlugin::registerAnalyzeActions()
button->setPopupMode(QToolButton::InstantPopup);
button->setIcon(icon);
button->setToolTip(Tr::tr("Analyze File..."));
- button->setProperty("noArrow", true);
+ button->setProperty(Utils::StyleHelper::C_NO_ARROW, true);
widget->toolBar()->addWidget(button);
const auto toolsMenu = new QMenu(widget);
button->setMenu(toolsMenu);
diff --git a/src/plugins/clangtools/clangtoolspreconfiguredsessiontests.cpp b/src/plugins/clangtools/clangtoolspreconfiguredsessiontests.cpp
index 5041dc4dac..4c858451fa 100644
--- a/src/plugins/clangtools/clangtoolspreconfiguredsessiontests.cpp
+++ b/src/plugins/clangtools/clangtoolspreconfiguredsessiontests.cpp
@@ -5,16 +5,18 @@
#include "clangtool.h"
#include "clangtoolsdiagnostic.h"
-#include "clangtoolsutils.h"
#include <coreplugin/icore.h>
+#include <coreplugin/session.h>
+
#include <cppeditor/compileroptionsbuilder.h>
#include <cppeditor/projectinfo.h>
+
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/toolchain.h>
@@ -29,6 +31,7 @@
#include <functional>
+using namespace Core;
using namespace CppEditor;
using namespace ProjectExplorer;
using namespace Utils;
@@ -55,8 +58,8 @@ public:
WaitForParsedProjects(const FilePaths &projects)
: m_projectsToWaitFor(projects)
{
- connect(SessionManager::instance(),
- &ProjectExplorer::SessionManager::projectFinishedParsing,
+ connect(ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::projectFinishedParsing,
this, &WaitForParsedProjects::onProjectFinishedParsing);
}
@@ -87,7 +90,7 @@ void PreconfiguredSessionTests::initTestCase()
QSKIP("Session must not be already active.");
// Load session
- const FilePaths projects = SessionManager::projectsForSessionName(preconfiguredSessionName);
+ const FilePaths projects = ProjectManager::projectsForSessionName(preconfiguredSessionName);
WaitForParsedProjects waitForParsedProjects(projects);
QVERIFY(SessionManager::loadSession(preconfiguredSessionName));
QVERIFY(waitForParsedProjects.wait());
@@ -102,7 +105,7 @@ void PreconfiguredSessionTests::testPreconfiguredSession()
for (ClangTool * const tool : {ClangTidyTool::instance(), ClazyTool::instance()}) {
tool->startTool(ClangTool::FileSelectionType::AllFiles);
- QSignalSpy waitUntilAnalyzerFinished(tool, SIGNAL(finished(bool)));
+ QSignalSpy waitUntilAnalyzerFinished(tool, &ClangTool::finished);
QVERIFY(waitUntilAnalyzerFinished.wait(30000));
const QList<QVariant> arguments = waitUntilAnalyzerFinished.takeFirst();
const bool analyzerFinishedSuccessfully = arguments.first().toBool();
@@ -175,7 +178,7 @@ void PreconfiguredSessionTests::testPreconfiguredSession_data()
bool hasAddedTestData = false;
- for (Project *project : validProjects(SessionManager::projects())) {
+ for (Project *project : validProjects(ProjectManager::projects())) {
for (Target *target : validTargets(project)) {
hasAddedTestData = true;
QTest::newRow(dataTagName(project, target)) << project << target;
@@ -189,17 +192,17 @@ void PreconfiguredSessionTests::testPreconfiguredSession_data()
bool PreconfiguredSessionTests::switchToProjectAndTarget(Project *project,
Target *target)
{
- Project * const activeProject = SessionManager::startupProject();
+ Project * const activeProject = ProjectManager::startupProject();
if (project == activeProject && target == activeProject->activeTarget())
return true; // OK, desired project/target already active.
if (project != activeProject)
- SessionManager::setStartupProject(project);
+ ProjectManager::setStartupProject(project);
if (target != project->activeTarget()) {
- QSignalSpy spyFinishedParsing(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::projectFinishedParsing);
- SessionManager::setActiveTarget(project, target, ProjectExplorer::SetActive::NoCascade);
+ QSignalSpy spyFinishedParsing(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::projectFinishedParsing);
+ project->setActiveTarget(target, ProjectExplorer::SetActive::NoCascade);
QTC_ASSERT(spyFinishedParsing.wait(30000), return false);
const QVariant projectArgument = spyFinishedParsing.takeFirst().constFirst();
diff --git a/src/plugins/clangtools/clangtoolsprojectsettings.cpp b/src/plugins/clangtools/clangtoolsprojectsettings.cpp
index cde1731acb..581a8ca57b 100644
--- a/src/plugins/clangtools/clangtoolsprojectsettings.cpp
+++ b/src/plugins/clangtools/clangtoolsprojectsettings.cpp
@@ -4,7 +4,7 @@
#include "clangtoolsprojectsettings.h"
#include "clangtoolsdiagnostic.h"
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
diff --git a/src/plugins/clangtools/clangtoolsprojectsettingswidget.cpp b/src/plugins/clangtools/clangtoolsprojectsettingswidget.cpp
index 5fe1487259..8e1ac057f9 100644
--- a/src/plugins/clangtools/clangtoolsprojectsettingswidget.cpp
+++ b/src/plugins/clangtools/clangtoolsprojectsettingswidget.cpp
@@ -68,7 +68,7 @@ ClangToolsProjectSettingsWidget::ClangToolsProjectSettingsWidget(ProjectExplorer
m_removeSelectedButton = new QPushButton(Tr::tr("Remove Selected"), this);
m_removeAllButton = new QPushButton(Tr::tr("Remove All"));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row { m_restoreGlobal, st, gotoClangTidyModeLabel, gotoClazyModeLabel },
@@ -84,8 +84,9 @@ ClangToolsProjectSettingsWidget::ClangToolsProjectSettingsWidget(ProjectExplorer
st
}
}
- }
- }.attachTo(this, WithoutMargins);
+ },
+ noMargin
+ }.attachTo(this);
setUseGlobalSettings(m_projectSettings->useGlobalSettings());
onGlobalCustomChanged(useGlobalSettings());
diff --git a/src/plugins/clangtools/clangtoolssettings.cpp b/src/plugins/clangtools/clangtoolssettings.cpp
index eeb24d1457..286b076f95 100644
--- a/src/plugins/clangtools/clangtoolssettings.cpp
+++ b/src/plugins/clangtools/clangtoolssettings.cpp
@@ -27,6 +27,7 @@ const char clangTidyExecutableKey[] = "ClangTidyExecutable";
const char clazyStandaloneExecutableKey[] = "ClazyStandaloneExecutable";
const char parallelJobsKey[] = "ParallelJobs";
+const char preferConfigFileKey[] = "PreferConfigFile";
const char buildBeforeAnalysisKey[] = "BuildBeforeAnalysis";
const char analyzeOpenFilesKey[] = "AnalyzeOpenFiles";
const char oldDiagnosticConfigIdKey[] = "diagnosticConfigId";
@@ -46,6 +47,7 @@ void RunSettings::fromMap(const QVariantMap &map, const QString &prefix)
{
m_diagnosticConfigId = Id::fromSetting(map.value(prefix + diagnosticConfigIdKey));
m_parallelJobs = map.value(prefix + parallelJobsKey).toInt();
+ m_preferConfigFile = map.value(prefix + preferConfigFileKey).toBool();
m_buildBeforeAnalysis = map.value(prefix + buildBeforeAnalysisKey).toBool();
m_analyzeOpenFiles = map.value(prefix + analyzeOpenFilesKey).toBool();
}
@@ -54,6 +56,7 @@ void RunSettings::toMap(QVariantMap &map, const QString &prefix) const
{
map.insert(prefix + diagnosticConfigIdKey, m_diagnosticConfigId.toSetting());
map.insert(prefix + parallelJobsKey, m_parallelJobs);
+ map.insert(prefix + preferConfigFileKey, m_preferConfigFile);
map.insert(prefix + buildBeforeAnalysisKey, m_buildBeforeAnalysis);
map.insert(prefix + analyzeOpenFilesKey, m_analyzeOpenFiles);
}
@@ -69,10 +72,23 @@ bool RunSettings::operator==(const RunSettings &other) const
{
return m_diagnosticConfigId == other.m_diagnosticConfigId
&& m_parallelJobs == other.m_parallelJobs
+ && m_preferConfigFile == other.m_preferConfigFile
&& m_buildBeforeAnalysis == other.m_buildBeforeAnalysis
&& m_analyzeOpenFiles == other.m_analyzeOpenFiles;
}
+bool RunSettings::hasConfigFileForSourceFile(const Utils::FilePath &sourceFile) const
+{
+ if (!preferConfigFile())
+ return false;
+ for (FilePath parentDir = sourceFile.parentDir(); !parentDir.isEmpty();
+ parentDir = parentDir.parentDir()) {
+ if (parentDir.resolvePath(QLatin1String(".clang-tidy")).isReadableFile())
+ return true;
+ }
+ return false;
+}
+
ClangToolsSettings::ClangToolsSettings()
{
readSettings();
@@ -139,6 +155,7 @@ void ClangToolsSettings::readSettings()
QVariantMap defaults;
defaults.insert(diagnosticConfigIdKey, defaultDiagnosticId().toSetting());
defaults.insert(parallelJobsKey, m_runSettings.parallelJobs());
+ defaults.insert(preferConfigFileKey, m_runSettings.preferConfigFile());
defaults.insert(buildBeforeAnalysisKey, m_runSettings.buildBeforeAnalysis());
defaults.insert(analyzeOpenFilesKey, m_runSettings.analyzeOpenFiles());
map = defaults;
diff --git a/src/plugins/clangtools/clangtoolssettings.h b/src/plugins/clangtools/clangtoolssettings.h
index 3ecbfcc30a..45a2cb032e 100644
--- a/src/plugins/clangtools/clangtoolssettings.h
+++ b/src/plugins/clangtools/clangtoolssettings.h
@@ -31,6 +31,9 @@ public:
Utils::Id diagnosticConfigId() const;
void setDiagnosticConfigId(const Utils::Id &id) { m_diagnosticConfigId = id; }
+ bool preferConfigFile() const { return m_preferConfigFile; }
+ void setPreferConfigFile(bool yesno) { m_preferConfigFile = yesno; }
+
bool buildBeforeAnalysis() const { return m_buildBeforeAnalysis; }
void setBuildBeforeAnalysis(bool yesno) { m_buildBeforeAnalysis = yesno; }
@@ -42,9 +45,12 @@ public:
bool operator==(const RunSettings &other) const;
+ bool hasConfigFileForSourceFile(const Utils::FilePath &sourceFile) const;
+
private:
Utils::Id m_diagnosticConfigId;
int m_parallelJobs = -1;
+ bool m_preferConfigFile = true;
bool m_buildBeforeAnalysis = true;
bool m_analyzeOpenFiles = true;
};
diff --git a/src/plugins/clangtools/clangtoolsunittests.cpp b/src/plugins/clangtools/clangtoolsunittests.cpp
index c8650c0b77..0672d43054 100644
--- a/src/plugins/clangtools/clangtoolsunittests.cpp
+++ b/src/plugins/clangtools/clangtoolsunittests.cpp
@@ -26,7 +26,6 @@
#include <utils/executeondestruction.h>
#include <utils/fileutils.h>
-#include <QEventLoop>
#include <QSignalSpy>
#include <QTimer>
#include <QtTest>
diff --git a/src/plugins/clangtools/clangtoolsutils.cpp b/src/plugins/clangtools/clangtoolsutils.cpp
index 01dcb614b5..de3ac2b9cb 100644
--- a/src/plugins/clangtools/clangtoolsutils.cpp
+++ b/src/plugins/clangtools/clangtoolsutils.cpp
@@ -18,8 +18,8 @@
#include <utils/environment.h>
#include <utils/filepath.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <cppeditor/clangdiagnosticconfigsmodel.h>
@@ -138,12 +138,10 @@ QString hintAboutBuildBeforeAnalysis()
void showHintAboutBuildBeforeAnalysis()
{
- Utils::CheckableMessageBox::doNotShowAgainInformation(
- Core::ICore::dialogParent(),
- Tr::tr("Info About Build the Project Before Analysis"),
- hintAboutBuildBeforeAnalysis(),
- Core::ICore::settings(),
- "ClangToolsDisablingBuildBeforeAnalysisHint");
+ Utils::CheckableMessageBox::information(Core::ICore::dialogParent(),
+ Tr::tr("Info About Build the Project Before Analysis"),
+ hintAboutBuildBeforeAnalysis(),
+ QString("ClangToolsDisablingBuildBeforeAnalysisHint"));
}
FilePath fullPath(const FilePath &executable)
@@ -211,7 +209,7 @@ bool isVFSOverlaySupported(const FilePath &executable)
static QMap<FilePath, bool> vfsCapabilities;
auto it = vfsCapabilities.find(executable);
if (it == vfsCapabilities.end()) {
- QtcProcess p;
+ Process p;
p.setCommand({executable, {"--help"}});
p.runBlocking();
it = vfsCapabilities.insert(executable, p.allOutput().contains("vfsoverlay"));
@@ -284,7 +282,7 @@ static QStringList extraOptions(const QString &envVar)
if (!qtcEnvironmentVariableIsSet(envVar))
return QStringList();
QString arguments = qtcEnvironmentVariable(envVar);
- return Utils::ProcessArgs::splitArgs(arguments);
+ return ProcessArgs::splitArgs(arguments, HostOsInfo::hostOs());
}
QStringList extraClangToolsPrependOptions()
@@ -294,7 +292,7 @@ QStringList extraClangToolsPrependOptions()
static const QStringList options = extraOptions(csaPrependOptions)
+ extraOptions(toolsPrependOptions);
if (!options.isEmpty())
- qWarning() << "ClangTools options are prepended with " << options.toVector();
+ qWarning() << "ClangTools options are prepended with " << options;
return options;
}
@@ -305,7 +303,7 @@ QStringList extraClangToolsAppendOptions()
static const QStringList options = extraOptions(csaAppendOptions)
+ extraOptions(toolsAppendOptions);
if (!options.isEmpty())
- qWarning() << "ClangTools options are appended with " << options.toVector();
+ qWarning() << "ClangTools options are appended with " << options;
return options;
}
@@ -343,5 +341,13 @@ QString clazyDocUrl(const QString &check)
return QString::fromLatin1(urlTemplate).arg(versionString, check);
}
+bool toolEnabled(CppEditor::ClangToolType type, const ClangDiagnosticConfig &config,
+ const RunSettings &runSettings)
+{
+ if (type == ClangToolType::Tidy && runSettings.preferConfigFile())
+ return true;
+ return config.isEnabled(type);
+}
+
} // namespace Internal
} // namespace ClangTools
diff --git a/src/plugins/clangtools/clangtoolsutils.h b/src/plugins/clangtools/clangtoolsutils.h
index 6f4f2871a0..36bcc75ff2 100644
--- a/src/plugins/clangtools/clangtoolsutils.h
+++ b/src/plugins/clangtools/clangtoolsutils.h
@@ -17,6 +17,7 @@ namespace Utils { class FilePath; }
namespace ClangTools {
namespace Internal {
+class RunSettings;
QString clangTidyDocUrl(const QString &check);
QString clazyDocUrl(const QString &check);
@@ -47,6 +48,8 @@ Utils::FilePath toolShippedExecutable(CppEditor::ClangToolType tool);
Utils::FilePath toolExecutable(CppEditor::ClangToolType tool);
Utils::FilePath toolFallbackExecutable(CppEditor::ClangToolType tool);
QString clangToolName(CppEditor::ClangToolType tool);
+bool toolEnabled(CppEditor::ClangToolType type, const CppEditor::ClangDiagnosticConfig &config,
+ const RunSettings &runSettings);
bool isVFSOverlaySupported(const Utils::FilePath &executable);
diff --git a/src/plugins/clangtools/diagnosticconfigswidget.cpp b/src/plugins/clangtools/diagnosticconfigswidget.cpp
index 1d160befa2..57480bf8cd 100644
--- a/src/plugins/clangtools/diagnosticconfigswidget.cpp
+++ b/src/plugins/clangtools/diagnosticconfigswidget.cpp
@@ -14,8 +14,8 @@
#include <cppeditor/cppeditorconstants.h>
#include <cppeditor/cpptoolsreuse.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/selectablefilesmodel.h>
-#include <projectexplorer/session.h>
#include <utils/algorithm.h>
#include <utils/fancylineedit.h>
@@ -56,7 +56,6 @@ QString removeClazyCheck(const QString &checks, const QString &check);
class TidyChecksWidget : public QWidget
{
public:
- QComboBox *tidyMode;
QPushButton *plainTextEditButton;
FancyLineEdit *filterLineEdit;
QTreeView *checksPrefixesTree;
@@ -65,10 +64,6 @@ public:
TidyChecksWidget()
{
- tidyMode = new QComboBox;
- tidyMode->addItem(Tr::tr("Select Checks"));
- tidyMode->addItem(Tr::tr("Use .clang-tidy config file"));
-
plainTextEditButton = new QPushButton(Tr::tr("Edit Checks as String..."));
filterLineEdit = new FancyLineEdit;
@@ -95,16 +90,18 @@ public:
using namespace Layouting;
Column {
- checksPrefixesTree
- }.attachTo(checksPage, WithoutMargins);
+ checksPrefixesTree,
+ noMargin
+ }.attachTo(checksPage);
Column {
invalidExecutableLabel,
st,
- }.attachTo(invalidExecutablePage, WithoutMargins);
+ noMargin
+ }.attachTo(invalidExecutablePage);
Column {
- Row { tidyMode, plainTextEditButton, filterLineEdit },
+ Row { plainTextEditButton, filterLineEdit },
stackedWidget,
}.attachTo(this);
}
@@ -174,13 +171,15 @@ public:
Column {
label,
- Row { groupBox, checksGroupBox }
- }.attachTo(checksPage, WithoutMargins);
+ Row { groupBox, checksGroupBox },
+ noMargin
+ }.attachTo(checksPage);
Column {
invalidExecutableLabel,
- st
- }.attachTo(invalidExecutablePage, WithoutMargins);
+ st,
+ noMargin
+ }.attachTo(invalidExecutablePage);
Column {
enableLowerLevelsCheckBox,
@@ -1107,32 +1106,18 @@ void DiagnosticConfigsWidget::syncClangTidyWidgets(const ClangDiagnosticConfig &
disconnectClangTidyItemChanged();
- const ClangDiagnosticConfig::TidyMode tidyMode = config.clangTidyMode();
- switch (tidyMode) {
- case ClangDiagnosticConfig::TidyMode::UseConfigFile:
- m_tidyChecks->tidyMode->setCurrentIndex(1);
+ if (m_tidyInfo.supportedChecks.isEmpty()) {
m_tidyChecks->plainTextEditButton->setVisible(false);
m_tidyChecks->filterLineEdit->setVisible(false);
- m_tidyChecks->stackedWidget->setCurrentIndex(TidyPages::EmptyPage);
- break;
- case ClangDiagnosticConfig::TidyMode::UseCustomChecks:
- case ClangDiagnosticConfig::TidyMode::UseDefaultChecks:
- m_tidyChecks->tidyMode->setCurrentIndex(0);
- if (m_tidyInfo.supportedChecks.isEmpty()) {
- m_tidyChecks->plainTextEditButton->setVisible(false);
- m_tidyChecks->filterLineEdit->setVisible(false);
- m_tidyChecks->stackedWidget->setCurrentIndex(TidyPages::InvalidExecutablePage);
- } else {
- m_tidyChecks->plainTextEditButton->setVisible(true);
- m_tidyChecks->filterLineEdit->setVisible(true);
- m_tidyChecks->stackedWidget->setCurrentIndex(TidyPages::ChecksPage);
- syncTidyChecksToTree(config);
- }
- break;
+ m_tidyChecks->stackedWidget->setCurrentIndex(TidyPages::InvalidExecutablePage);
+ } else {
+ m_tidyChecks->plainTextEditButton->setVisible(true);
+ m_tidyChecks->filterLineEdit->setVisible(true);
+ m_tidyChecks->stackedWidget->setCurrentIndex(TidyPages::ChecksPage);
+ syncTidyChecksToTree(config);
}
const bool enabled = !config.isReadOnly();
- m_tidyChecks->tidyMode->setEnabled(enabled);
m_tidyChecks->plainTextEditButton->setText(enabled ? Tr::tr("Edit Checks as String...")
: Tr::tr("View Checks as String..."));
m_tidyTreeModel->setEnabled(enabled);
@@ -1189,16 +1174,12 @@ void DiagnosticConfigsWidget::syncExtraWidgets(const ClangDiagnosticConfig &conf
void DiagnosticConfigsWidget::connectClangTidyItemChanged()
{
- connect(m_tidyChecks->tidyMode, &QComboBox::currentIndexChanged,
- this, &DiagnosticConfigsWidget::onClangTidyModeChanged);
connect(m_tidyTreeModel.get(), &TidyChecksTreeModel::dataChanged,
this, &DiagnosticConfigsWidget::onClangTidyTreeChanged);
}
void DiagnosticConfigsWidget::disconnectClangTidyItemChanged()
{
- disconnect(m_tidyChecks->tidyMode, &QComboBox::currentIndexChanged,
- this, &DiagnosticConfigsWidget::onClangTidyModeChanged);
disconnect(m_tidyTreeModel.get(), &TidyChecksTreeModel::dataChanged,
this, &DiagnosticConfigsWidget::onClangTidyTreeChanged);
}
@@ -1215,18 +1196,6 @@ void DiagnosticConfigsWidget::disconnectClazyItemChanged()
this, &DiagnosticConfigsWidget::onClazyTreeChanged);
}
-void DiagnosticConfigsWidget::onClangTidyModeChanged(int index)
-{
- const ClangDiagnosticConfig::TidyMode tidyMode
- = index == 0 ? ClangDiagnosticConfig::TidyMode::UseCustomChecks
- : ClangDiagnosticConfig::TidyMode::UseConfigFile;
-
- ClangDiagnosticConfig config = currentConfig();
- config.setClangTidyMode(tidyMode);
- updateConfig(config);
- syncClangTidyWidgets(config);
-}
-
void DiagnosticConfigsWidget::onClangTidyTreeChanged()
{
ClangDiagnosticConfig config = currentConfig();
@@ -1294,7 +1263,7 @@ void disableChecks(const QList<Diagnostic> &diagnostics)
Utils::Id activeConfigId = settings->runSettings().diagnosticConfigId();
ClangToolsProjectSettings::ClangToolsProjectSettingsPtr projectSettings;
- if (ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(
+ if (ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::projectForFile(
diagnostics.first().location.filePath)) {
projectSettings = ClangToolsProjectSettings::getSettings(project);
if (!projectSettings->useGlobalSettings())
@@ -1330,7 +1299,7 @@ void disableChecks(const QList<Diagnostic> &diagnostics)
}
config.setChecks(ClangToolType::Clazy,
removeClazyCheck(config.checks(ClangToolType::Clazy), diag.name));
- } else if (config.clangTidyMode() != ClangDiagnosticConfig::TidyMode::UseConfigFile) {
+ } else if (!settings->runSettings().preferConfigFile()){
if (config.clangTidyMode() == ClangDiagnosticConfig::TidyMode::UseDefaultChecks) {
config.setClangTidyMode(ClangDiagnosticConfig::TidyMode::UseCustomChecks);
const ClangTidyInfo tidyInfo(toolExecutable(ClangToolType::Tidy));
diff --git a/src/plugins/clangtools/diagnosticconfigswidget.h b/src/plugins/clangtools/diagnosticconfigswidget.h
index 7a3b892760..04af842f0e 100644
--- a/src/plugins/clangtools/diagnosticconfigswidget.h
+++ b/src/plugins/clangtools/diagnosticconfigswidget.h
@@ -41,7 +41,6 @@ private:
void syncClazyWidgets(const CppEditor::ClangDiagnosticConfig &config);
void syncClazyChecksGroupBox();
- void onClangTidyModeChanged(int index);
void onClangTidyTreeChanged();
void onClazyTreeChanged();
diff --git a/src/plugins/clangtools/documentclangtoolrunner.cpp b/src/plugins/clangtools/documentclangtoolrunner.cpp
index 81a7e8f544..3de2c6d55c 100644
--- a/src/plugins/clangtools/documentclangtoolrunner.cpp
+++ b/src/plugins/clangtools/documentclangtoolrunner.cpp
@@ -18,14 +18,15 @@
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildtargettype.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
+#include <solutions/tasking/tasktree.h>
+
#include <texteditor/textdocument.h>
#include <texteditor/texteditor.h>
#include <utils/qtcassert.h>
-#include <utils/tasktree.h>
#include <QLoggingCategory>
#include <QScopeGuard>
@@ -35,6 +36,7 @@ static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.cftr", QtWarningMsg)
using namespace Core;
using namespace CppEditor;
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
namespace ClangTools {
@@ -97,8 +99,8 @@ void DocumentClangToolRunner::scheduleRun()
static Project *findProject(const FilePath &file)
{
- Project *project = SessionManager::projectForFile(file);
- return project ? project : SessionManager::startupProject();
+ Project *project = ProjectManager::projectForFile(file);
+ return project ? project : ProjectManager::startupProject();
}
static VirtualFileSystemOverlay &vfso()
@@ -188,23 +190,26 @@ void DocumentClangToolRunner::run()
vfso().update();
const ClangDiagnosticConfig config = diagnosticConfig(runSettings.diagnosticConfigId());
const Environment env = projectBuildEnvironment(project);
- using namespace Tasking;
QList<TaskItem> tasks{parallel};
- const auto addClangTool = [this, &config, &env, &tasks](ClangToolType tool) {
- if (!config.isEnabled(tool))
+ const auto addClangTool = [this, &runSettings, &config, &env, &tasks](ClangToolType tool) {
+ if (!toolEnabled(tool, config, runSettings))
+ return;
+ if (!config.isEnabled(tool) && !runSettings.hasConfigFileForSourceFile(m_fileInfo.file))
return;
const FilePath executable = toolExecutable(tool);
+ if (executable.isEmpty() || !executable.isExecutableFile())
+ return;
const auto [includeDir, clangVersion] = getClangIncludeDirAndVersion(executable);
- if (!executable.isExecutableFile() || includeDir.isEmpty() || clangVersion.isEmpty())
+ if (includeDir.isEmpty() || clangVersion.isEmpty())
return;
const AnalyzeUnit unit(m_fileInfo, includeDir, clangVersion);
- const AnalyzeInputData input{tool, config, m_temporaryDir.path(), env, unit,
+ const AnalyzeInputData input{tool, runSettings, config, m_temporaryDir.path(), env, unit,
vfso().overlayFilePath().toString()};
const auto setupHandler = [this, executable] {
return !m_document->isModified() || isVFSOverlaySupported(executable);
};
const auto outputHandler = [this](const AnalyzeOutputData &output) { onDone(output); };
- tasks.append(Group{optional, clangToolTask(input, setupHandler, outputHandler)});
+ tasks.append(Group{finishAllAndDone, clangToolTask(input, setupHandler, outputHandler)});
};
addClangTool(ClangToolType::Tidy);
addClangTool(ClangToolType::Clazy);
diff --git a/src/plugins/clangtools/documentclangtoolrunner.h b/src/plugins/clangtools/documentclangtoolrunner.h
index 94111abb37..e80a9632dc 100644
--- a/src/plugins/clangtools/documentclangtoolrunner.h
+++ b/src/plugins/clangtools/documentclangtoolrunner.h
@@ -14,8 +14,8 @@
#include <QTimer>
namespace Core { class IDocument; }
+namespace Tasking { class TaskTree; }
namespace TextEditor { class TextEditorWidget; }
-namespace Utils { class TaskTree; }
namespace ClangTools {
namespace Internal {
@@ -51,7 +51,7 @@ private:
QList<QPointer<TextEditor::TextEditorWidget>> m_editorsWithMarkers;
SuppressedDiagnosticsList m_suppressed;
Utils::FilePath m_lastProjectDirectory;
- std::unique_ptr<Utils::TaskTree> m_taskTree;
+ std::unique_ptr<Tasking::TaskTree> m_taskTree;
};
} // namespace Internal
diff --git a/src/plugins/clangtools/executableinfo.cpp b/src/plugins/clangtools/executableinfo.cpp
index 1e280a6889..22898b5734 100644
--- a/src/plugins/clangtools/executableinfo.cpp
+++ b/src/plugins/clangtools/executableinfo.cpp
@@ -7,7 +7,7 @@
#include <coreplugin/messagemanager.h>
#include <utils/environment.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QDir>
#include <QFileInfo>
@@ -26,7 +26,7 @@ static QString runExecutable(const Utils::CommandLine &commandLine, QueryFailMod
if (commandLine.executable().isEmpty() || !commandLine.executable().toFileInfo().isExecutable())
return {};
- QtcProcess cpp;
+ Process cpp;
Environment env = Environment::systemEnvironment();
env.setupEnglishOutput();
cpp.setEnvironment(env);
diff --git a/src/plugins/clangtools/filterdialog.cpp b/src/plugins/clangtools/filterdialog.cpp
index 4c59b44b55..bcb81346d0 100644
--- a/src/plugins/clangtools/filterdialog.cpp
+++ b/src/plugins/clangtools/filterdialog.cpp
@@ -86,7 +86,7 @@ FilterDialog::FilterDialog(const Checks &checks, QWidget *parent)
m_view->setSelectionBehavior(QAbstractItemView::SelectRows);
m_view->setIndentation(0);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Tr::tr("Select the diagnostics to display."),
diff --git a/src/plugins/clangtools/runsettingswidget.cpp b/src/plugins/clangtools/runsettingswidget.cpp
index 0e7a34959f..cd0dc6a1ee 100644
--- a/src/plugins/clangtools/runsettingswidget.cpp
+++ b/src/plugins/clangtools/runsettingswidget.cpp
@@ -28,14 +28,10 @@ namespace ClangTools::Internal {
RunSettingsWidget::RunSettingsWidget(QWidget *parent)
: QWidget(parent)
{
- resize(383, 125);
-
m_diagnosticWidget = new ClangDiagnosticConfigsSelectionWidget;
-
+ m_preferConfigFile = new QCheckBox(Tr::tr("Prefer .clang-tidy file, if present"));
m_buildBeforeAnalysis = new QCheckBox(Tr::tr("Build the project before analysis"));
-
m_analyzeOpenFiles = new QCheckBox(Tr::tr("Analyze open files"));
-
m_parallelJobsSpinBox = new QSpinBox;
m_parallelJobsSpinBox->setRange(1, 32);
@@ -47,12 +43,14 @@ RunSettingsWidget::RunSettingsWidget(QWidget *parent)
title(Tr::tr("Run Options")),
Column {
m_diagnosticWidget,
+ m_preferConfigFile,
m_buildBeforeAnalysis,
m_analyzeOpenFiles,
Row { Tr::tr("Parallel jobs:"), m_parallelJobsSpinBox, st },
}
- }
- }.attachTo(this, WithoutMargins);
+ },
+ noMargin
+ }.attachTo(this);
}
RunSettingsWidget::~RunSettingsWidget() = default;
@@ -99,6 +97,9 @@ void RunSettingsWidget::fromSettings(const RunSettings &s)
connect(m_diagnosticWidget, &ClangDiagnosticConfigsSelectionWidget::changed,
this, &RunSettingsWidget::changed);
+ m_preferConfigFile->setChecked(s.preferConfigFile());
+ connect(m_preferConfigFile, &QCheckBox::toggled, this, &RunSettingsWidget::changed);
+
disconnect(m_buildBeforeAnalysis, 0, 0, 0);
m_buildBeforeAnalysis->setToolTip(hintAboutBuildBeforeAnalysis());
m_buildBeforeAnalysis->setCheckState(s.buildBeforeAnalysis() ? Qt::Checked : Qt::Unchecked);
@@ -115,13 +116,13 @@ void RunSettingsWidget::fromSettings(const RunSettings &s)
connect(m_parallelJobsSpinBox, &QSpinBox::valueChanged, this, &RunSettingsWidget::changed);
m_analyzeOpenFiles->setChecked(s.analyzeOpenFiles());
connect(m_analyzeOpenFiles, &QCheckBox::toggled, this, &RunSettingsWidget::changed);
-
}
RunSettings RunSettingsWidget::toSettings() const
{
RunSettings s;
s.setDiagnosticConfigId(m_diagnosticWidget->currentConfigId());
+ s.setPreferConfigFile(m_preferConfigFile->isChecked());
s.setBuildBeforeAnalysis(m_buildBeforeAnalysis->checkState() == Qt::CheckState::Checked);
s.setParallelJobs(m_parallelJobsSpinBox->value());
s.setAnalyzeOpenFiles(m_analyzeOpenFiles->checkState() == Qt::CheckState::Checked);
diff --git a/src/plugins/clangtools/runsettingswidget.h b/src/plugins/clangtools/runsettingswidget.h
index 532bbd4ff8..0d2e4b645a 100644
--- a/src/plugins/clangtools/runsettingswidget.h
+++ b/src/plugins/clangtools/runsettingswidget.h
@@ -37,6 +37,7 @@ signals:
private:
CppEditor::ClangDiagnosticConfigsSelectionWidget *m_diagnosticWidget;
+ QCheckBox *m_preferConfigFile;
QCheckBox *m_buildBeforeAnalysis;
QCheckBox *m_analyzeOpenFiles;
QSpinBox *m_parallelJobsSpinBox;
diff --git a/src/plugins/clangtools/settingswidget.cpp b/src/plugins/clangtools/settingswidget.cpp
index 76f915dd99..80aad561b0 100644
--- a/src/plugins/clangtools/settingswidget.cpp
+++ b/src/plugins/clangtools/settingswidget.cpp
@@ -34,8 +34,6 @@ SettingsWidget::SettingsWidget()
{
m_instance = this;
- resize(400, 300);
-
auto createPathChooser = [this](ClangToolType tool)
{
const QString placeHolderText = toolShippedExecutable(tool).toUserOutput();
@@ -53,6 +51,7 @@ SettingsWidget::SettingsWidget()
pathChooser->setHistoryCompleter(tool == ClangToolType::Tidy
? QString("ClangTools.ClangTidyExecutable.History")
: QString("ClangTools.ClazyStandaloneExecutable.History"));
+ pathChooser->setCommandVersionArguments({"--version"});
return pathChooser;
};
m_clangTidyPathChooser = createPathChooser(ClangToolType::Tidy);
diff --git a/src/plugins/clangtools/unit-tests/exported-diagnostics/CMakeLists.txt b/src/plugins/clangtools/unit-tests/exported-diagnostics/CMakeLists.txt
index 30efc99691..98f9833492 100644
--- a/src/plugins/clangtools/unit-tests/exported-diagnostics/CMakeLists.txt
+++ b/src/plugins/clangtools/unit-tests/exported-diagnostics/CMakeLists.txt
@@ -11,7 +11,7 @@ set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
-find_package(Qt5 COMPONENTS Widgets REQUIRED)
+find_package(Qt6 COMPONENTS Widgets REQUIRED)
add_executable(clangtools
main.cpp
diff --git a/src/plugins/classview/classviewmanager.cpp b/src/plugins/classview/classviewmanager.cpp
index a98cec7044..90c834a621 100644
--- a/src/plugins/classview/classviewmanager.cpp
+++ b/src/plugins/classview/classviewmanager.cpp
@@ -8,8 +8,11 @@
#include <cppeditor/cppeditorconstants.h>
#include <cppeditor/cppmodelmanager.h>
+
#include <coreplugin/progressmanager/progressmanager.h>
-#include <projectexplorer/session.h>
+
+#include <projectexplorer/projectmanager.h>
+
#include <texteditor/texteditor.h>
#include <QThread>
@@ -90,7 +93,7 @@ void ManagerPrivate::resetParser()
cancelScheduledUpdate();
QHash<FilePath, QPair<QString, FilePaths>> projectData;
- for (const Project *project : SessionManager::projects()) {
+ for (const Project *project : ProjectManager::projects()) {
projectData.insert(project->projectFilePath(),
{project->displayName(), project->files(Project::SourceFiles)});
}
@@ -201,8 +204,8 @@ void Manager::initialize()
d->m_timer.setSingleShot(true);
// connections to enable/disable navi widget factory
- SessionManager *sessionManager = SessionManager::instance();
- connect(sessionManager, &SessionManager::projectAdded,
+ ProjectManager *sessionManager = ProjectManager::instance();
+ connect(sessionManager, &ProjectManager::projectAdded,
this, [this](Project *project) {
const FilePath projectPath = project->projectFilePath();
const QString projectName = project->displayName();
@@ -211,7 +214,7 @@ void Manager::initialize()
d->m_parser->addProject(projectPath, projectName, projectFiles);
}, Qt::QueuedConnection);
});
- connect(sessionManager, &SessionManager::projectRemoved,
+ connect(sessionManager, &ProjectManager::projectRemoved,
this, [this](Project *project) {
const FilePath projectPath = project->projectFilePath();
QMetaObject::invokeMethod(d->m_parser, [this, projectPath]() {
@@ -375,7 +378,7 @@ void Manager::gotoLocations(const QList<QVariant> &list)
int line;
int column;
textEditor->convertPosition(textEditor->position(), &line, &column);
- const SymbolLocation current(filePath, line, column);
+ const SymbolLocation current(filePath, line, column + 1);
if (auto it = locations.constFind(current), end = locations.constEnd(); it != end) {
// we already are at the symbol, cycle to next location
++it;
diff --git a/src/plugins/classview/classviewparsertreeitem.cpp b/src/plugins/classview/classviewparsertreeitem.cpp
index 1d97d0c013..787b63b3c8 100644
--- a/src/plugins/classview/classviewparsertreeitem.cpp
+++ b/src/plugins/classview/classviewparsertreeitem.cpp
@@ -7,9 +7,11 @@
#include <cplusplus/Icons.h>
#include <cplusplus/Overview.h>
+
#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <QDebug>
#include <QHash>
@@ -261,7 +263,7 @@ bool ParserTreeItem::canFetchMore(QStandardItem *item) const
*/
void ParserTreeItem::fetchMore(QStandardItem *item) const
{
- using ProjectExplorer::SessionManager;
+ using ProjectExplorer::ProjectManager;
if (!item)
return;
@@ -283,7 +285,7 @@ void ParserTreeItem::fetchMore(QStandardItem *item) const
// icon
const Utils::FilePath &filePath = ptr->projectFilePath();
if (!filePath.isEmpty()) {
- ProjectExplorer::Project *project = SessionManager::projectForFile(filePath);
+ ProjectExplorer::Project *project = ProjectManager::projectForFile(filePath);
if (project)
add->setIcon(project->containerNode()->icon());
}
diff --git a/src/plugins/clearcase/checkoutdialog.cpp b/src/plugins/clearcase/checkoutdialog.cpp
index 8c3a5b8204..df8c43a4e1 100644
--- a/src/plugins/clearcase/checkoutdialog.cpp
+++ b/src/plugins/clearcase/checkoutdialog.cpp
@@ -48,7 +48,7 @@ CheckOutDialog::CheckOutDialog(const QString &fileName, bool isUcm, bool showCom
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
lblFileName,
@@ -67,7 +67,7 @@ CheckOutDialog::CheckOutDialog(const QString &fileName, bool isUcm, bool showCom
m_actSelector = new ActivitySelector(this);
m_verticalLayout->insertWidget(0, m_actSelector);
- m_verticalLayout->insertWidget(1, Utils::Layouting::createHr());
+ m_verticalLayout->insertWidget(1, Layouting::createHr());
}
if (!showComment)
diff --git a/src/plugins/clearcase/clearcaseplugin.cpp b/src/plugins/clearcase/clearcaseplugin.cpp
index de884452be..943ffa194a 100644
--- a/src/plugins/clearcase/clearcaseplugin.cpp
+++ b/src/plugins/clearcase/clearcaseplugin.cpp
@@ -27,16 +27,16 @@
#include <texteditor/textdocument.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/hostosinfo.h>
#include <utils/infobar.h>
#include <utils/layoutbuilder.h>
#include <utils/parameteraction.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <utils/temporarydirectory.h>
#include <vcsbase/basevcseditorfactory.h>
@@ -57,7 +57,6 @@
#include <QDir>
#include <QFileInfo>
#include <QFuture>
-#include <QFutureInterface>
#include <QInputDialog>
#include <QLabel>
#include <QList>
@@ -228,6 +227,10 @@ private:
Q_INVOKABLE void updateStatusActions();
QString commitDisplayName() const final;
+ QString commitAbortTitle() const final;
+ QString commitAbortMessage() const final;
+ QString commitErrorMessage(const QString &error) const final;
+
void checkOutCurrentFile();
void addCurrentFile();
void undoCheckOutCurrent();
@@ -259,7 +262,7 @@ private:
CommandResult runCleartool(const FilePath &workingDir, const QStringList &arguments,
VcsBase::RunFlags flags = VcsBase::RunFlags::None,
QTextCodec *codec = nullptr, int timeoutMultiplier = 1) const;
- static void sync(QFutureInterface<void> &future, QStringList files);
+ static void sync(QPromise<void> &promise, QStringList files);
void history(const FilePath &workingDir,
const QStringList &file = {},
@@ -488,7 +491,7 @@ FileStatus::Status ClearCasePluginPrivate::getFileStatus(const QString &fileName
/// "cleartool pwv" returns the values for "set view" and "working directory view", also for
/// snapshot views.
///
-/// \returns The ClearCase topLevel/VOB directory for this directory
+/// Returns the ClearCase topLevel/VOB directory for this directory.
QString ClearCasePluginPrivate::ccManagesDirectory(const FilePath &directory) const
{
const CommandResult result = runCleartoolProc(directory, {"pwv"});
@@ -595,7 +598,7 @@ ClearCasePluginPrivate::ClearCasePluginPrivate()
m_settings.fromSettings(ICore::settings());
// update view name when changing active project
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, &ClearCasePluginPrivate::projectChanged);
const QString description = QLatin1String("ClearCase");
@@ -949,9 +952,27 @@ void ClearCasePluginPrivate::updateActions(VcsBasePluginPrivate::ActionState as)
QString ClearCasePluginPrivate::commitDisplayName() const
{
+ //: Name of the "commit" action of the VCS
return Tr::tr("Check In");
}
+QString ClearCasePluginPrivate::commitAbortTitle() const
+{
+ return Tr::tr("Close Check In Editor");
+}
+
+QString ClearCasePluginPrivate::commitAbortMessage() const
+{
+ return Tr::tr("Closing this editor will abort the check in.");
+}
+
+QString ClearCasePluginPrivate::commitErrorMessage(const QString &error) const
+{
+ if (error.isEmpty())
+ return Tr::tr("Cannot check in.");
+ return Tr::tr("Cannot check in: %1.").arg(error);
+}
+
void ClearCasePluginPrivate::checkOutCurrentFile()
{
const VcsBasePluginState state = currentState();
@@ -1575,7 +1596,7 @@ CommandResult ClearCasePluginPrivate::runCleartoolProc(const FilePath &workingDi
if (m_settings.ccBinaryPath.isEmpty())
return CommandResult(ProcessResult::StartFailed, Tr::tr("No ClearCase executable specified."));
- QtcProcess process;
+ Process process;
Environment env = Environment::systemEnvironment();
VcsBase::setProcessEnvironment(&env);
process.setEnvironment(env);
@@ -1657,7 +1678,7 @@ bool ClearCasePluginPrivate::vcsOpen(const FilePath &workingDir, const QString &
if (!m_settings.disableIndexer &&
(fi.isWritable() || vcsStatus(absPath).status == FileStatus::Unknown))
- runAsync(sync, QStringList(absPath)).waitForFinished();
+ Utils::asyncRun(sync, QStringList(absPath)).waitForFinished();
if (vcsStatus(absPath).status == FileStatus::CheckedOut) {
QMessageBox::information(ICore::dialogParent(), Tr::tr("ClearCase Checkout"),
Tr::tr("File is already checked out."));
@@ -2124,12 +2145,13 @@ void ClearCasePluginPrivate::updateIndex()
{
QTC_ASSERT(currentState().hasTopLevel(), return);
ProgressManager::cancelTasks(ClearCase::Constants::TASK_INDEX);
- Project *project = SessionManager::startupProject();
+ Project *project = ProjectManager::startupProject();
if (!project)
return;
m_checkInAllAction->setEnabled(false);
m_statusMap->clear();
- QFuture<void> result = runAsync(sync, transform(project->files(Project::SourceFiles), &FilePath::toString));
+ QFuture<void> result = Utils::asyncRun(sync, transform(project->files(Project::SourceFiles),
+ &FilePath::toString));
if (!m_settings.disableIndexer)
ProgressManager::addTask(result, Tr::tr("Updating ClearCase Index"), ClearCase::Constants::TASK_INDEX);
}
@@ -2232,7 +2254,7 @@ void ClearCasePluginPrivate::diffGraphical(const QString &file1, const QString &
args << file1;
if (!pred)
args << file2;
- QtcProcess::startDetached({m_settings.ccBinaryPath, args}, m_topLevel);
+ Process::startDetached({m_settings.ccBinaryPath, args}, m_topLevel);
}
QString ClearCasePluginPrivate::runExtDiff(const FilePath &workingDir, const QStringList &arguments,
@@ -2242,7 +2264,7 @@ QString ClearCasePluginPrivate::runExtDiff(const FilePath &workingDir, const QSt
diff.addArgs(m_settings.diffArgs.split(' ', Qt::SkipEmptyParts));
diff.addArgs(arguments);
- QtcProcess process;
+ Process process;
process.setTimeoutS(timeOutS);
process.setWorkingDirectory(workingDir);
process.setCodec(outputCodec ? outputCodec : QTextCodec::codecForName("UTF-8"));
@@ -2261,7 +2283,7 @@ void ClearCasePluginPrivate::syncSlot()
FilePath topLevel = state.topLevel();
if (topLevel != state.currentProjectTopLevel())
return;
- runAsync(sync, QStringList());
+ Utils::asyncRun(sync, QStringList()); // TODO: make use of returned QFuture
}
void ClearCasePluginPrivate::closing()
@@ -2271,12 +2293,12 @@ void ClearCasePluginPrivate::closing()
disconnect(qApp, &QApplication::applicationStateChanged, nullptr, nullptr);
}
-void ClearCasePluginPrivate::sync(QFutureInterface<void> &future, QStringList files)
+void ClearCasePluginPrivate::sync(QPromise<void> &promise, QStringList files)
{
ClearCasePluginPrivate *plugin = ClearCasePluginPrivate::instance();
ClearCaseSync ccSync(plugin->m_statusMap);
connect(&ccSync, &ClearCaseSync::updateStreamAndView, plugin, &ClearCasePluginPrivate::updateStreamAndView);
- ccSync.run(future, files);
+ ccSync.run(promise, files);
}
QString ClearCasePluginPrivate::displayName() const
diff --git a/src/plugins/clearcase/clearcasesubmiteditorwidget.cpp b/src/plugins/clearcase/clearcasesubmiteditorwidget.cpp
index 44c3fd1b44..8708e566f3 100644
--- a/src/plugins/clearcase/clearcasesubmiteditorwidget.cpp
+++ b/src/plugins/clearcase/clearcasesubmiteditorwidget.cpp
@@ -69,7 +69,7 @@ void ClearCaseSubmitEditorWidget::addActivitySelector(bool isUcm)
m_actSelector = new ActivitySelector;
m_verticalLayout->insertWidget(0, m_actSelector);
- m_verticalLayout->insertWidget(1, Utils::Layouting::createHr());
+ m_verticalLayout->insertWidget(1, Layouting::createHr());
}
QString ClearCaseSubmitEditorWidget::commitName() const
diff --git a/src/plugins/clearcase/clearcasesync.cpp b/src/plugins/clearcase/clearcasesync.cpp
index 388ce1a8c1..8476d1f04a 100644
--- a/src/plugins/clearcase/clearcasesync.cpp
+++ b/src/plugins/clearcase/clearcasesync.cpp
@@ -6,12 +6,13 @@
#include "clearcasesettings.h"
#include <QDir>
-#include <QFutureInterface>
#include <QRegularExpression>
#include <QStringList>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
+
+#include <QPromise>
#ifdef WITH_TESTS
#include <QTest>
@@ -22,13 +23,12 @@ using namespace Utils;
namespace ClearCase::Internal {
-static void runProcess(QFutureInterface<void> &future,
- const ClearCaseSettings &settings,
+static void runProcess(QPromise<void> &promise, const ClearCaseSettings &settings,
const QStringList &args,
std::function<void(const QString &buffer, int processed)> processLine)
{
const QString viewRoot = ClearCasePlugin::viewData().root;
- QtcProcess process;
+ Process process;
process.setWorkingDirectory(FilePath::fromString(viewRoot));
process.setCommand({settings.ccBinaryPath, args});
process.start();
@@ -37,7 +37,7 @@ static void runProcess(QFutureInterface<void> &future,
int processed = 0;
QString buffer;
- while (process.waitForReadyRead() && !future.isCanceled()) {
+ while (process.waitForReadyRead() && !promise.isCanceled()) {
buffer += QString::fromLocal8Bit(process.readAllRawStandardOutput());
int index = buffer.indexOf('\n');
while (index != -1) {
@@ -135,7 +135,7 @@ void ClearCaseSync::updateStatusForNotManagedFiles(const QStringList &files)
}
}
-void ClearCaseSync::syncSnapshotView(QFutureInterface<void> &future, QStringList &files,
+void ClearCaseSync::syncSnapshotView(QPromise<void> &promise, QStringList &files,
const ClearCaseSettings &settings)
{
const QString view = ClearCasePlugin::viewData().name;
@@ -167,18 +167,18 @@ void ClearCaseSync::syncSnapshotView(QFutureInterface<void> &future, QStringList
// adding 1 for initial sync in which total is not accurate, to prevent finishing
// (we don't want it to become green)
- future.setProgressRange(0, totalFileCount + 1);
+ promise.setProgressRange(0, totalFileCount + 1);
int totalProcessed = 0;
- runProcess(future, settings, args, [&](const QString &buffer, int processed) {
+ runProcess(promise, settings, args, [&](const QString &buffer, int processed) {
processCleartoolLsLine(viewRootDir, buffer);
- future.setProgressValue(qMin(totalFileCount, processed));
+ promise.setProgressValue(qMin(totalFileCount, processed));
totalProcessed = processed;
});
- if (!future.isCanceled()) {
+ if (!promise.isCanceled()) {
updateStatusForNotManagedFiles(files);
- future.setProgressValue(totalFileCount + 1);
+ promise.setProgressValue(totalFileCount + 1);
if (!hot)
updateTotalFilesCount(view, settings, totalProcessed);
}
@@ -193,21 +193,20 @@ void ClearCaseSync::processCleartoolLscheckoutLine(const QString &buffer)
///
/// Update the file status for dynamic views.
///
-void ClearCaseSync::syncDynamicView(QFutureInterface<void> &future,
- const ClearCaseSettings& settings)
+void ClearCaseSync::syncDynamicView(QPromise<void> &promise, const ClearCaseSettings& settings)
{
// Always invalidate status for all files
invalidateStatusAllFiles();
const QStringList args({"lscheckout", "-avobs", "-me", "-cview", "-s"});
- runProcess(future, settings, args, [&](const QString &buffer, int processed) {
+ runProcess(promise, settings, args, [&](const QString &buffer, int processed) {
processCleartoolLscheckoutLine(buffer);
- future.setProgressValue(processed);
+ promise.setProgressValue(processed);
});
}
-void ClearCaseSync::run(QFutureInterface<void> &future, QStringList &files)
+void ClearCaseSync::run(QPromise<void> &promise, QStringList &files)
{
ClearCaseSettings settings = ClearCasePlugin::settings();
if (settings.disableIndexer)
@@ -225,9 +224,9 @@ void ClearCaseSync::run(QFutureInterface<void> &future, QStringList &files)
emit updateStreamAndView();
if (ClearCasePlugin::viewData().isDynamic)
- syncDynamicView(future, settings);
+ syncDynamicView(promise, settings);
else
- syncSnapshotView(future, files, settings);
+ syncSnapshotView(promise, files, settings);
}
#ifdef WITH_TESTS
diff --git a/src/plugins/clearcase/clearcasesync.h b/src/plugins/clearcase/clearcasesync.h
index a008dabb60..4b69985b0c 100644
--- a/src/plugins/clearcase/clearcasesync.h
+++ b/src/plugins/clearcase/clearcasesync.h
@@ -8,7 +8,7 @@
QT_BEGIN_NAMESPACE
class QDir;
template <typename T>
-class QFutureInterface;
+class QPromise;
QT_END_NAMESPACE
namespace ClearCase::Internal {
@@ -18,7 +18,7 @@ class ClearCaseSync : public QObject
Q_OBJECT
public:
explicit ClearCaseSync(QSharedPointer<StatusMap> statusMap);
- void run(QFutureInterface<void> &future, QStringList &files);
+ void run(QPromise<void> &promise, QStringList &files);
QStringList updateStatusHotFiles(const QString &viewRoot, int &total);
void invalidateStatus(const QDir &viewRootDir, const QStringList &files);
@@ -28,9 +28,8 @@ public:
const int processed);
void updateStatusForNotManagedFiles(const QStringList &files);
- void syncDynamicView(QFutureInterface<void> &future,
- const ClearCaseSettings &settings);
- void syncSnapshotView(QFutureInterface<void> &future, QStringList &files,
+ void syncDynamicView(QPromise<void> &promise, const ClearCaseSettings &settings);
+ void syncSnapshotView(QPromise<void> &promise, QStringList &files,
const ClearCaseSettings &settings);
void processCleartoolLscheckoutLine(const QString &buffer);
diff --git a/src/plugins/clearcase/settingspage.cpp b/src/plugins/clearcase/settingspage.cpp
index bcbab9f16a..1ff8e48264 100644
--- a/src/plugins/clearcase/settingspage.cpp
+++ b/src/plugins/clearcase/settingspage.cpp
@@ -50,8 +50,6 @@ private:
SettingsPageWidget::SettingsPageWidget()
{
- resize(512, 589);
-
commandPathChooser = new PathChooser;
commandPathChooser->setPromptDialogTitle(Tr::tr("ClearCase Command"));
commandPathChooser->setExpectedKind(PathChooser::ExistingCommand);
@@ -109,8 +107,8 @@ SettingsPageWidget::SettingsPageWidget()
using namespace Layouting;
Form {
- Tr::tr("Arg&uments:"), diffArgsEdit
- }.attachTo(diffWidget, WithoutMargins);
+ Tr::tr("Arg&uments:"), diffArgsEdit, noMargin
+ }.attachTo(diffWidget);
Column {
Group {
diff --git a/src/plugins/clearcase/versionselector.cpp b/src/plugins/clearcase/versionselector.cpp
index 2c0d46a6a3..4d3dd16059 100644
--- a/src/plugins/clearcase/versionselector.cpp
+++ b/src/plugins/clearcase/versionselector.cpp
@@ -51,7 +51,7 @@ VersionSelector::VersionSelector(const QString &fileName, const QString &message
"the changes (not supported by the plugin)")
+ "</b></p></body></html>");
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
headerLabel,
diff --git a/src/plugins/cmakeprojectmanager/3rdparty/cmake/Copyright.txt b/src/plugins/cmakeprojectmanager/3rdparty/cmake/Copyright.txt
new file mode 100644
index 0000000000..515e403a2d
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/3rdparty/cmake/Copyright.txt
@@ -0,0 +1,136 @@
+CMake - Cross Platform Makefile Generator
+Copyright 2000-2023 Kitware, Inc. and Contributors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Kitware, Inc. nor the names of Contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+------------------------------------------------------------------------------
+
+The following individuals and institutions are among the Contributors:
+
+* Aaron C. Meadows <cmake@shadowguarddev.com>
+* Adriaan de Groot <groot@kde.org>
+* Aleksey Avdeev <solo@altlinux.ru>
+* Alexander Neundorf <neundorf@kde.org>
+* Alexander Smorkalov <alexander.smorkalov@itseez.com>
+* Alexey Sokolov <sokolov@google.com>
+* Alex Merry <alex.merry@kde.org>
+* Alex Turbov <i.zaufi@gmail.com>
+* Andreas Pakulat <apaku@gmx.de>
+* Andreas Schneider <asn@cryptomilk.org>
+* André Rigland Brodtkorb <Andre.Brodtkorb@ifi.uio.no>
+* Axel Huebl, Helmholtz-Zentrum Dresden - Rossendorf
+* Benjamin Eikel
+* Bjoern Ricks <bjoern.ricks@gmail.com>
+* Brad Hards <bradh@kde.org>
+* Christopher Harvey
+* Christoph Grüninger <foss@grueninger.de>
+* Clement Creusot <creusot@cs.york.ac.uk>
+* Daniel Blezek <blezek@gmail.com>
+* Daniel Pfeifer <daniel@pfeifer-mail.de>
+* Dawid Wróbel <me@dawidwrobel.com>
+* Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+* Eran Ifrah <eran.ifrah@gmail.com>
+* Esben Mose Hansen, Ange Optimization ApS
+* Geoffrey Viola <geoffrey.viola@asirobots.com>
+* Google Inc
+* Gregor Jasny
+* Helio Chissini de Castro <helio@kde.org>
+* Ilya Lavrenov <ilya.lavrenov@itseez.com>
+* Insight Software Consortium <insightsoftwareconsortium.org>
+* Intel Corporation <www.intel.com>
+* Jan Woetzel
+* Jordan Williams <jordan@jwillikers.com>
+* Julien Schueller
+* Kelly Thompson <kgt@lanl.gov>
+* Konstantin Podsvirov <konstantin@podsvirov.pro>
+* Laurent Montel <montel@kde.org>
+* Mario Bensi <mbensi@ipsquad.net>
+* Martin Gräßlin <mgraesslin@kde.org>
+* Mathieu Malaterre <mathieu.malaterre@gmail.com>
+* Matthaeus G. Chajdas
+* Matthias Kretz <kretz@kde.org>
+* Matthias Maennich <matthias@maennich.net>
+* Michael Hirsch, Ph.D. <www.scivision.co>
+* Michael Stürmer
+* Miguel A. Figueroa-Villanueva
+* Mike Durso <rbprogrammer@gmail.com>
+* Mike Jackson
+* Mike McQuaid <mike@mikemcquaid.com>
+* Nicolas Bock <nicolasbock@gmail.com>
+* Nicolas Despres <nicolas.despres@gmail.com>
+* Nikita Krupen'ko <krnekit@gmail.com>
+* NVIDIA Corporation <www.nvidia.com>
+* OpenGamma Ltd. <opengamma.com>
+* Patrick Stotko <stotko@cs.uni-bonn.de>
+* Per Øyvind Karlsen <peroyvind@mandriva.org>
+* Peter Collingbourne <peter@pcc.me.uk>
+* Petr Gotthard <gotthard@honeywell.com>
+* Philip Lowman <philip@yhbt.com>
+* Philippe Proulx <pproulx@efficios.com>
+* Raffi Enficiaud, Max Planck Society
+* Raumfeld <raumfeld.com>
+* Roger Leigh <rleigh@codelibre.net>
+* Rolf Eike Beer <eike@sf-mail.de>
+* Roman Donchenko <roman.donchenko@itseez.com>
+* Roman Kharitonov <roman.kharitonov@itseez.com>
+* Ruslan Baratov
+* Sebastian Holtermann <sebholt@xwmw.org>
+* Stephen Kelly <steveire@gmail.com>
+* Sylvain Joubert <joubert.sy@gmail.com>
+* The Qt Company Ltd.
+* Thomas Sondergaard <ts@medical-insight.com>
+* Tobias Hunger <tobias.hunger@qt.io>
+* Todd Gamblin <tgamblin@llnl.gov>
+* Tristan Carel
+* University of Dundee
+* Vadim Zhukov
+* Will Dicharry <wdicharry@stellarscience.com>
+
+See version control history for details of individual contributions.
+
+The above copyright and license notice applies to distributions of
+CMake in source and binary form. Third-party software packages supplied
+with CMake under compatible licenses provide their own copyright notices
+documented in corresponding subdirectories or source files.
+
+------------------------------------------------------------------------------
+
+CMake was initially developed by Kitware with the following sponsorship:
+
+ * National Library of Medicine at the National Institutes of Health
+ as part of the Insight Segmentation and Registration Toolkit (ITK).
+
+ * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel
+ Visualization Initiative.
+
+ * National Alliance for Medical Image Computing (NAMIC) is funded by the
+ National Institutes of Health through the NIH Roadmap for Medical Research,
+ Grant U54 EB005149.
+
+ * Kitware, Inc.
diff --git a/src/plugins/cmakeprojectmanager/3rdparty/cmake/README.md b/src/plugins/cmakeprojectmanager/3rdparty/cmake/README.md
new file mode 100644
index 0000000000..92a4d869a6
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/3rdparty/cmake/README.md
@@ -0,0 +1,4 @@
+Files taken from the CMake repository https://gitlab.kitware.com/cmake/cmake.git
+
+624461526f4707a2406ebbd40245a605b6bd41fa (tag: v3.26.3)
+
diff --git a/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileCache.cxx b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileCache.cxx
new file mode 100644
index 0000000000..13fd40ca64
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileCache.cxx
@@ -0,0 +1,235 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#define cmListFileCache_cxx
+#include "cmListFileCache.h"
+
+#include <memory>
+#include <sstream>
+#include <utility>
+
+#include "cmListFileLexer.h"
+
+struct cmListFileParser
+{
+ cmListFileParser(cmListFile* lf, std::string &error);
+ ~cmListFileParser();
+ cmListFileParser(const cmListFileParser&) = delete;
+ cmListFileParser& operator=(const cmListFileParser&) = delete;
+ void IssueError(std::string const& text) const;
+ bool ParseFile(const char* filename);
+ bool ParseString(const std::string &str, const std::string &virtual_filename);
+ bool Parse();
+ bool ParseFunction(const char* name, long line);
+ bool AddArgument(cmListFileLexer_Token* token,
+ cmListFileArgument::Delimiter delim);
+ cmListFile* ListFile;
+ cmListFileLexer* Lexer;
+ std::string FunctionName;
+ long FunctionLine;
+ long FunctionLineEnd;
+ std::vector<cmListFileArgument> FunctionArguments;
+ std::string &Error;
+ enum
+ {
+ SeparationOkay,
+ SeparationWarning,
+ SeparationError
+ } Separation;
+};
+
+cmListFileParser::cmListFileParser(cmListFile *lf, std::string &error)
+ : ListFile(lf)
+ , Lexer(cmListFileLexer_New())
+ , Error(error)
+{
+}
+
+cmListFileParser::~cmListFileParser()
+{
+ cmListFileLexer_Delete(this->Lexer);
+}
+
+void cmListFileParser::IssueError(const std::string& text) const
+{
+ Error += text;
+ Error += "\n";
+}
+
+bool cmListFileParser::ParseString(const std::string &str,
+ const std::string &/*virtual_filename*/)
+{
+ if (!cmListFileLexer_SetString(this->Lexer, str.c_str(), (int)str.size())) {
+ this->IssueError("cmListFileCache: cannot allocate buffer.");
+ return false;
+ }
+
+ return this->Parse();
+}
+
+bool cmListFileParser::Parse()
+{
+ // Use a simple recursive-descent parser to process the token
+ // stream.
+ bool haveNewline = true;
+ while (cmListFileLexer_Token* token = cmListFileLexer_Scan(this->Lexer)) {
+ if (token->type == cmListFileLexer_Token_Space) {
+ } else if (token->type == cmListFileLexer_Token_Newline) {
+ haveNewline = true;
+ } else if (token->type == cmListFileLexer_Token_CommentBracket) {
+ haveNewline = false;
+ } else if (token->type == cmListFileLexer_Token_Identifier) {
+ if (haveNewline) {
+ haveNewline = false;
+ if (this->ParseFunction(token->text, token->line)) {
+ this->ListFile->Functions.emplace_back(
+ std::move(this->FunctionName), this->FunctionLine,
+ this->FunctionLineEnd, std::move(this->FunctionArguments));
+ } else {
+ return false;
+ }
+ } else {
+ std::ostringstream error;
+ error << "Parse error. Expected a newline, got "
+ << cmListFileLexer_GetTypeAsString(this->Lexer, token->type)
+ << " with text \"" << token->text << "\".";
+ this->IssueError(error.str());
+ return false;
+ }
+ } else {
+ std::ostringstream error;
+ error << "Parse error. Expected a command name, got "
+ << cmListFileLexer_GetTypeAsString(this->Lexer, token->type)
+ << " with text \"" << token->text << "\".";
+ this->IssueError(error.str());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool cmListFile::ParseString(const std::string &str, const std::string& virtual_filename, std::string &error)
+{
+ bool parseError = false;
+
+ {
+ cmListFileParser parser(this, error);
+ parseError = !parser.ParseString(str, virtual_filename);
+ }
+
+ return !parseError;
+}
+
+bool cmListFileParser::ParseFunction(const char* name, long line)
+{
+ // Ininitialize a new function call.
+ this->FunctionName = name;
+ this->FunctionLine = line;
+
+ // Command name has already been parsed. Read the left paren.
+ cmListFileLexer_Token* token;
+ while ((token = cmListFileLexer_Scan(this->Lexer)) &&
+ token->type == cmListFileLexer_Token_Space) {
+ }
+ if (!token) {
+ std::ostringstream error;
+ /* clang-format off */
+ error << "Unexpected end of file.\n"
+ << "Parse error. Function missing opening \"(\".";
+ /* clang-format on */
+ this->IssueError(error.str());
+ return false;
+ }
+ if (token->type != cmListFileLexer_Token_ParenLeft) {
+ std::ostringstream error;
+ error << "Parse error. Expected \"(\", got "
+ << cmListFileLexer_GetTypeAsString(this->Lexer, token->type)
+ << " with text \"" << token->text << "\".";
+ this->IssueError(error.str());
+ return false;
+ }
+
+ // Arguments.
+ unsigned long parenDepth = 0;
+ this->Separation = SeparationOkay;
+ while ((token = cmListFileLexer_Scan(this->Lexer))) {
+ if (token->type == cmListFileLexer_Token_Space ||
+ token->type == cmListFileLexer_Token_Newline) {
+ this->Separation = SeparationOkay;
+ continue;
+ }
+ if (token->type == cmListFileLexer_Token_ParenLeft) {
+ parenDepth++;
+ this->Separation = SeparationOkay;
+ if (!this->AddArgument(token, cmListFileArgument::Unquoted)) {
+ return false;
+ }
+ } else if (token->type == cmListFileLexer_Token_ParenRight) {
+ if (parenDepth == 0) {
+ this->FunctionLineEnd = token->line;
+ return true;
+ }
+ parenDepth--;
+ this->Separation = SeparationOkay;
+ if (!this->AddArgument(token, cmListFileArgument::Unquoted)) {
+ return false;
+ }
+ this->Separation = SeparationWarning;
+ } else if (token->type == cmListFileLexer_Token_Identifier ||
+ token->type == cmListFileLexer_Token_ArgumentUnquoted) {
+ if (!this->AddArgument(token, cmListFileArgument::Unquoted)) {
+ return false;
+ }
+ this->Separation = SeparationWarning;
+ } else if (token->type == cmListFileLexer_Token_ArgumentQuoted) {
+ if (!this->AddArgument(token, cmListFileArgument::Quoted)) {
+ return false;
+ }
+ this->Separation = SeparationWarning;
+ } else if (token->type == cmListFileLexer_Token_ArgumentBracket) {
+ if (!this->AddArgument(token, cmListFileArgument::Bracket)) {
+ return false;
+ }
+ this->Separation = SeparationError;
+ } else if (token->type == cmListFileLexer_Token_CommentBracket) {
+ this->Separation = SeparationError;
+ } else {
+ // Error.
+ std::ostringstream error;
+ error << "Parse error. Function missing ending \")\". "
+ << "Instead found "
+ << cmListFileLexer_GetTypeAsString(this->Lexer, token->type)
+ << " with text \"" << token->text << "\".";
+ this->IssueError(error.str());
+ return false;
+ }
+ }
+
+ std::ostringstream error;
+ error << "Parse error. Function missing ending \")\". "
+ << "End of file reached.";
+ IssueError(error.str());
+ return false;
+}
+
+bool cmListFileParser::AddArgument(cmListFileLexer_Token* token,
+ cmListFileArgument::Delimiter delim)
+{
+ this->FunctionArguments.emplace_back(token->text, delim, token->line, token->column);
+ if (this->Separation == SeparationOkay) {
+ return true;
+ }
+ bool isError = (this->Separation == SeparationError ||
+ delim == cmListFileArgument::Bracket);
+ std::ostringstream m;
+
+ m << "Syntax " << (isError ? "Error" : "Warning") << " in cmake code at "
+ << "column " << token->column << "\n"
+ << "Argument not separated from preceding token by whitespace.";
+ /* clang-format on */
+ if (isError) {
+ IssueError(m.str());
+ return false;
+ }
+ return true;
+}
diff --git a/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileCache.h b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileCache.h
new file mode 100644
index 0000000000..4b419e7a5a
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileCache.h
@@ -0,0 +1,114 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include <algorithm>
+#include <iosfwd>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+/** \class cmListFileCache
+ * \brief A class to cache list file contents.
+ *
+ * cmListFileCache is a class used to cache the contents of parsed
+ * cmake list files.
+ */
+
+struct cmListFileArgument
+{
+ enum Delimiter
+ {
+ Unquoted,
+ Quoted,
+ Bracket
+ };
+ cmListFileArgument() = default;
+ cmListFileArgument(std::string v, Delimiter d, long line, long column)
+ : Value(std::move(v))
+ , Delim(d)
+ , Line(line)
+ , Column(column)
+ {
+ }
+ bool operator==(const cmListFileArgument& r) const
+ {
+ return (this->Value == r.Value) && (this->Delim == r.Delim);
+ }
+ bool operator!=(const cmListFileArgument& r) const { return !(*this == r); }
+ std::string Value;
+ Delimiter Delim = Unquoted;
+ long Line = 0;
+ long Column = 0;
+};
+
+class cmListFileFunction
+{
+public:
+ cmListFileFunction(std::string name, long line, long lineEnd,
+ std::vector<cmListFileArgument> args)
+ : Impl{ std::make_shared<Implementation>(std::move(name), line, lineEnd,
+ std::move(args)) }
+ {
+ }
+
+ std::string const& OriginalName() const noexcept
+ {
+ return this->Impl->OriginalName;
+ }
+
+ std::string const& LowerCaseName() const noexcept
+ {
+ return this->Impl->LowerCaseName;
+ }
+
+ long Line() const noexcept { return this->Impl->Line; }
+ long LineEnd() const noexcept { return this->Impl->LineEnd; }
+
+ std::vector<cmListFileArgument> const& Arguments() const noexcept
+ {
+ return this->Impl->Arguments;
+ }
+
+private:
+ struct Implementation
+ {
+ Implementation(std::string name, long line, long lineEnd,
+ std::vector<cmListFileArgument> args)
+ : OriginalName{ std::move(name) }
+ , LowerCaseName{ tolower(this->OriginalName) }
+ , Line{ line }
+ , LineEnd{ lineEnd }
+ , Arguments{ std::move(args) }
+ {
+ }
+
+ // taken from yaml-cpp
+ static bool IsLower(char ch) { return 'a' <= ch && ch <= 'z'; }
+ static bool IsUpper(char ch) { return 'A' <= ch && ch <= 'Z'; }
+ static char ToLower(char ch) { return IsUpper(ch) ? ch + 'a' - 'A' : ch; }
+
+ std::string tolower(const std::string& str)
+ {
+ std::string s(str);
+ std::transform(s.begin(), s.end(), s.begin(), ToLower);
+ return s;
+ }
+
+ std::string OriginalName;
+ std::string LowerCaseName;
+ long Line = 0;
+ long LineEnd = 0;
+ std::vector<cmListFileArgument> Arguments;
+ };
+
+ std::shared_ptr<Implementation const> Impl;
+};
+
+struct cmListFile
+{
+ bool ParseString(const std::string &str, const std::string &virtual_filename, std::string &error);
+
+ std::vector<cmListFileFunction> Functions;
+};
diff --git a/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.cxx b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.cxx
new file mode 100644
index 0000000000..e093275a8d
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.cxx
@@ -0,0 +1,2831 @@
+#include "cmStandardLexer.h"
+
+#define FLEXINT_H 1
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner created by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 6
+#define YY_FLEX_SUBMINOR_VERSION 4
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+#ifdef yy_create_buffer
+#define cmListFileLexer_yy_create_buffer_ALREADY_DEFINED
+#else
+#define yy_create_buffer cmListFileLexer_yy_create_buffer
+#endif
+
+#ifdef yy_delete_buffer
+#define cmListFileLexer_yy_delete_buffer_ALREADY_DEFINED
+#else
+#define yy_delete_buffer cmListFileLexer_yy_delete_buffer
+#endif
+
+#ifdef yy_scan_buffer
+#define cmListFileLexer_yy_scan_buffer_ALREADY_DEFINED
+#else
+#define yy_scan_buffer cmListFileLexer_yy_scan_buffer
+#endif
+
+#ifdef yy_scan_string
+#define cmListFileLexer_yy_scan_string_ALREADY_DEFINED
+#else
+#define yy_scan_string cmListFileLexer_yy_scan_string
+#endif
+
+#ifdef yy_scan_bytes
+#define cmListFileLexer_yy_scan_bytes_ALREADY_DEFINED
+#else
+#define yy_scan_bytes cmListFileLexer_yy_scan_bytes
+#endif
+
+#ifdef yy_init_buffer
+#define cmListFileLexer_yy_init_buffer_ALREADY_DEFINED
+#else
+#define yy_init_buffer cmListFileLexer_yy_init_buffer
+#endif
+
+#ifdef yy_flush_buffer
+#define cmListFileLexer_yy_flush_buffer_ALREADY_DEFINED
+#else
+#define yy_flush_buffer cmListFileLexer_yy_flush_buffer
+#endif
+
+#ifdef yy_load_buffer_state
+#define cmListFileLexer_yy_load_buffer_state_ALREADY_DEFINED
+#else
+#define yy_load_buffer_state cmListFileLexer_yy_load_buffer_state
+#endif
+
+#ifdef yy_switch_to_buffer
+#define cmListFileLexer_yy_switch_to_buffer_ALREADY_DEFINED
+#else
+#define yy_switch_to_buffer cmListFileLexer_yy_switch_to_buffer
+#endif
+
+#ifdef yypush_buffer_state
+#define cmListFileLexer_yypush_buffer_state_ALREADY_DEFINED
+#else
+#define yypush_buffer_state cmListFileLexer_yypush_buffer_state
+#endif
+
+#ifdef yypop_buffer_state
+#define cmListFileLexer_yypop_buffer_state_ALREADY_DEFINED
+#else
+#define yypop_buffer_state cmListFileLexer_yypop_buffer_state
+#endif
+
+#ifdef yyensure_buffer_stack
+#define cmListFileLexer_yyensure_buffer_stack_ALREADY_DEFINED
+#else
+#define yyensure_buffer_stack cmListFileLexer_yyensure_buffer_stack
+#endif
+
+#ifdef yylex
+#define cmListFileLexer_yylex_ALREADY_DEFINED
+#else
+#define yylex cmListFileLexer_yylex
+#endif
+
+#ifdef yyrestart
+#define cmListFileLexer_yyrestart_ALREADY_DEFINED
+#else
+#define yyrestart cmListFileLexer_yyrestart
+#endif
+
+#ifdef yylex_init
+#define cmListFileLexer_yylex_init_ALREADY_DEFINED
+#else
+#define yylex_init cmListFileLexer_yylex_init
+#endif
+
+#ifdef yylex_init_extra
+#define cmListFileLexer_yylex_init_extra_ALREADY_DEFINED
+#else
+#define yylex_init_extra cmListFileLexer_yylex_init_extra
+#endif
+
+#ifdef yylex_destroy
+#define cmListFileLexer_yylex_destroy_ALREADY_DEFINED
+#else
+#define yylex_destroy cmListFileLexer_yylex_destroy
+#endif
+
+#ifdef yyget_debug
+#define cmListFileLexer_yyget_debug_ALREADY_DEFINED
+#else
+#define yyget_debug cmListFileLexer_yyget_debug
+#endif
+
+#ifdef yyset_debug
+#define cmListFileLexer_yyset_debug_ALREADY_DEFINED
+#else
+#define yyset_debug cmListFileLexer_yyset_debug
+#endif
+
+#ifdef yyget_extra
+#define cmListFileLexer_yyget_extra_ALREADY_DEFINED
+#else
+#define yyget_extra cmListFileLexer_yyget_extra
+#endif
+
+#ifdef yyset_extra
+#define cmListFileLexer_yyset_extra_ALREADY_DEFINED
+#else
+#define yyset_extra cmListFileLexer_yyset_extra
+#endif
+
+#ifdef yyget_in
+#define cmListFileLexer_yyget_in_ALREADY_DEFINED
+#else
+#define yyget_in cmListFileLexer_yyget_in
+#endif
+
+#ifdef yyset_in
+#define cmListFileLexer_yyset_in_ALREADY_DEFINED
+#else
+#define yyset_in cmListFileLexer_yyset_in
+#endif
+
+#ifdef yyget_out
+#define cmListFileLexer_yyget_out_ALREADY_DEFINED
+#else
+#define yyget_out cmListFileLexer_yyget_out
+#endif
+
+#ifdef yyset_out
+#define cmListFileLexer_yyset_out_ALREADY_DEFINED
+#else
+#define yyset_out cmListFileLexer_yyset_out
+#endif
+
+#ifdef yyget_leng
+#define cmListFileLexer_yyget_leng_ALREADY_DEFINED
+#else
+#define yyget_leng cmListFileLexer_yyget_leng
+#endif
+
+#ifdef yyget_text
+#define cmListFileLexer_yyget_text_ALREADY_DEFINED
+#else
+#define yyget_text cmListFileLexer_yyget_text
+#endif
+
+#ifdef yyget_lineno
+#define cmListFileLexer_yyget_lineno_ALREADY_DEFINED
+#else
+#define yyget_lineno cmListFileLexer_yyget_lineno
+#endif
+
+#ifdef yyset_lineno
+#define cmListFileLexer_yyset_lineno_ALREADY_DEFINED
+#else
+#define yyset_lineno cmListFileLexer_yyset_lineno
+#endif
+
+#ifdef yyget_column
+#define cmListFileLexer_yyget_column_ALREADY_DEFINED
+#else
+#define yyget_column cmListFileLexer_yyget_column
+#endif
+
+#ifdef yyset_column
+#define cmListFileLexer_yyset_column_ALREADY_DEFINED
+#else
+#define yyset_column cmListFileLexer_yyset_column
+#endif
+
+#ifdef yywrap
+#define cmListFileLexer_yywrap_ALREADY_DEFINED
+#else
+#define yywrap cmListFileLexer_yywrap
+#endif
+
+#ifdef yyalloc
+#define cmListFileLexer_yyalloc_ALREADY_DEFINED
+#else
+#define yyalloc cmListFileLexer_yyalloc
+#endif
+
+#ifdef yyrealloc
+#define cmListFileLexer_yyrealloc_ALREADY_DEFINED
+#else
+#define yyrealloc cmListFileLexer_yyrealloc
+#endif
+
+#ifdef yyfree
+#define cmListFileLexer_yyfree_ALREADY_DEFINED
+#else
+#define yyfree cmListFileLexer_yyfree
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#ifndef SIZE_MAX
+#define SIZE_MAX (~(size_t)0)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+/* begin standard C++ headers. */
+
+/* TODO: this is always defined, so inline it */
+#define yyconst const
+
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define yynoreturn __attribute__((__noreturn__))
+#else
+#define yynoreturn
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an
+ * integer in range [0..255] for use as an array index.
+ */
+#define YY_SC_TO_UI(c) ((YY_CHAR) (c))
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+ are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin , yyscanner )
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires
+ * access to the local variable yy_act. Since yyless() is a macro, it would break
+ * existing scanners that call yyless() from OUTSIDE yylex.
+ * One obvious solution it to make yy_act a global. I tried that, and saw
+ * a 5% performance hit in a non-yylineno scanner, because yy_act is
+ * normally declared as a register variable-- so it is not worth it.
+ */
+ #define YY_LESS_LINENO(n) \
+ do { \
+ int yyl;\
+ for ( yyl = n; yyl < yyleng; ++yyl )\
+ if ( yytext[yyl] == '\n' )\
+ --yylineno;\
+ }while(0)
+ #define YY_LINENO_REWIND_TO(dst) \
+ do {\
+ const char *p;\
+ for ( p = yy_cp-1; p >= (dst); --p)\
+ if ( *p == '\n' )\
+ --yylineno;\
+ }while(0)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = yyg->yy_hold_char; \
+ YY_RESTORE_YY_MORE_OFFSET \
+ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ int yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+ : NULL)
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void yyrestart ( FILE *input_file , yyscan_t yyscanner );
+void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner );
+void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
+void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
+void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
+void yypop_buffer_state ( yyscan_t yyscanner );
+
+static void yyensure_buffer_stack ( yyscan_t yyscanner );
+static void yy_load_buffer_state ( yyscan_t yyscanner );
+static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner );
+#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner)
+
+YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner );
+
+void *yyalloc ( yy_size_t , yyscan_t yyscanner );
+void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner );
+void yyfree ( void * , yyscan_t yyscanner );
+
+#define yy_new_buffer yy_create_buffer
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define cmListFileLexer_yywrap(yyscanner) (/*CONSTCOND*/1)
+#define YY_SKIP_YYWRAP
+typedef flex_uint8_t YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state ( yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner);
+static int yy_get_next_buffer ( yyscan_t yyscanner );
+static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yyg->yytext_ptr = yy_bp; \
+ yyleng = (int) (yy_cp - yy_bp); \
+ yyg->yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yyg->yy_c_buf_p = yy_cp;
+#define YY_NUM_RULES 24
+#define YY_END_OF_BUFFER 25
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static const flex_int16_t yy_accept[79] =
+ { 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 4, 4,
+ 25, 13, 22, 1, 16, 3, 13, 5, 6, 7,
+ 15, 23, 23, 17, 19, 20, 21, 24, 10, 11,
+ 8, 12, 9, 4, 13, 0, 13, 0, 22, 0,
+ 0, 7, 13, 0, 13, 0, 2, 0, 13, 17,
+ 0, 18, 10, 8, 4, 0, 14, 0, 0, 0,
+ 0, 14, 0, 0, 14, 0, 0, 0, 2, 14,
+ 0, 0, 0, 0, 0, 0, 0, 0
+ } ;
+
+static const YY_CHAR yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 4, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 5, 6, 7, 1, 1, 1, 8,
+ 9, 1, 1, 1, 1, 1, 1, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 1, 1, 1,
+ 11, 1, 1, 1, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 13, 14, 15, 1, 12, 1, 12, 12, 12, 12,
+
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static const YY_CHAR yy_meta[17] =
+ { 0,
+ 1, 1, 2, 3, 4, 3, 1, 3, 5, 6,
+ 1, 6, 1, 1, 7, 2
+ } ;
+
+static const flex_int16_t yy_base[97] =
+ { 0,
+ 0, 0, 14, 28, 42, 56, 70, 84, 18, 19,
+ 68, 100, 16, 298, 298, 54, 58, 298, 298, 13,
+ 115, 0, 298, 51, 298, 298, 21, 298, 0, 298,
+ 53, 298, 298, 0, 0, 126, 55, 0, 25, 25,
+ 53, 0, 0, 136, 53, 0, 57, 0, 0, 42,
+ 50, 298, 0, 43, 0, 146, 160, 45, 172, 43,
+ 26, 0, 42, 177, 0, 42, 188, 40, 298, 40,
+ 0, 38, 37, 34, 32, 31, 23, 298, 197, 204,
+ 211, 218, 225, 232, 239, 245, 252, 259, 262, 268,
+ 275, 278, 280, 286, 289, 291
+
+ } ;
+
+static const flex_int16_t yy_def[97] =
+ { 0,
+ 78, 1, 79, 79, 80, 80, 81, 81, 82, 82,
+ 78, 78, 78, 78, 78, 78, 12, 78, 78, 12,
+ 78, 83, 78, 84, 78, 78, 84, 78, 85, 78,
+ 78, 78, 78, 86, 12, 87, 12, 88, 78, 78,
+ 89, 20, 12, 90, 12, 21, 78, 91, 12, 84,
+ 84, 78, 85, 78, 86, 87, 78, 56, 87, 92,
+ 78, 57, 89, 90, 57, 64, 90, 93, 78, 57,
+ 94, 95, 92, 96, 93, 95, 96, 0, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78
+
+ } ;
+
+static const flex_int16_t yy_nxt[315] =
+ { 0,
+ 12, 13, 14, 13, 15, 16, 17, 18, 19, 12,
+ 12, 20, 21, 22, 12, 23, 25, 39, 26, 39,
+ 14, 14, 42, 52, 42, 50, 39, 27, 39, 28,
+ 25, 64, 26, 28, 28, 61, 61, 47, 47, 56,
+ 65, 27, 64, 28, 30, 57, 56, 60, 65, 74,
+ 62, 57, 72, 54, 50, 51, 31, 28, 30, 69,
+ 68, 62, 60, 54, 51, 41, 40, 78, 78, 78,
+ 31, 28, 30, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 33, 28, 30, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78, 33, 28,
+
+ 35, 78, 78, 78, 36, 78, 37, 78, 78, 35,
+ 35, 35, 35, 38, 35, 43, 78, 78, 78, 44,
+ 78, 45, 78, 78, 43, 46, 43, 47, 48, 43,
+ 57, 78, 58, 78, 78, 78, 78, 78, 78, 59,
+ 65, 78, 66, 78, 78, 78, 78, 78, 78, 67,
+ 57, 78, 58, 78, 78, 78, 78, 78, 78, 59,
+ 57, 78, 78, 78, 36, 78, 70, 78, 78, 57,
+ 57, 57, 57, 71, 57, 56, 78, 56, 78, 56,
+ 56, 65, 78, 66, 78, 78, 78, 78, 78, 78,
+ 67, 64, 78, 64, 78, 64, 64, 24, 24, 24,
+
+ 24, 24, 24, 24, 29, 29, 29, 29, 29, 29,
+ 29, 32, 32, 32, 32, 32, 32, 32, 34, 34,
+ 34, 34, 34, 34, 34, 49, 78, 49, 49, 49,
+ 49, 49, 50, 78, 50, 78, 50, 50, 50, 53,
+ 78, 53, 53, 53, 53, 55, 78, 55, 55, 55,
+ 55, 55, 56, 78, 78, 56, 78, 56, 56, 35,
+ 78, 35, 35, 35, 35, 35, 63, 63, 64, 78,
+ 78, 64, 78, 64, 64, 43, 78, 43, 43, 43,
+ 43, 43, 73, 73, 75, 75, 57, 78, 57, 57,
+ 57, 57, 57, 76, 76, 77, 77, 11, 78, 78,
+
+ 78, 78, 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78
+ } ;
+
+static const flex_int16_t yy_chk[315] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 3, 13, 3, 13,
+ 9, 10, 20, 27, 20, 27, 39, 3, 39, 3,
+ 4, 77, 4, 9, 10, 40, 61, 40, 61, 76,
+ 75, 4, 74, 4, 5, 73, 72, 70, 68, 66,
+ 63, 60, 58, 54, 51, 50, 5, 5, 6, 47,
+ 45, 41, 37, 31, 24, 17, 16, 11, 0, 0,
+ 6, 6, 7, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 7, 8, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 8, 8,
+
+ 12, 0, 0, 0, 12, 0, 12, 0, 0, 12,
+ 12, 12, 12, 12, 12, 21, 0, 0, 0, 21,
+ 0, 21, 0, 0, 21, 21, 21, 21, 21, 21,
+ 36, 0, 36, 0, 0, 0, 0, 0, 0, 36,
+ 44, 0, 44, 0, 0, 0, 0, 0, 0, 44,
+ 56, 0, 56, 0, 0, 0, 0, 0, 0, 56,
+ 57, 0, 0, 0, 57, 0, 57, 0, 0, 57,
+ 57, 57, 57, 57, 57, 59, 0, 59, 0, 59,
+ 59, 64, 0, 64, 0, 0, 0, 0, 0, 0,
+ 64, 67, 0, 67, 0, 67, 67, 79, 79, 79,
+
+ 79, 79, 79, 79, 80, 80, 80, 80, 80, 80,
+ 80, 81, 81, 81, 81, 81, 81, 81, 82, 82,
+ 82, 82, 82, 82, 82, 83, 0, 83, 83, 83,
+ 83, 83, 84, 0, 84, 0, 84, 84, 84, 85,
+ 0, 85, 85, 85, 85, 86, 0, 86, 86, 86,
+ 86, 86, 87, 0, 0, 87, 0, 87, 87, 88,
+ 0, 88, 88, 88, 88, 88, 89, 89, 90, 0,
+ 0, 90, 0, 90, 90, 91, 0, 91, 91, 91,
+ 91, 91, 92, 92, 93, 93, 94, 0, 94, 94,
+ 94, 94, 94, 95, 95, 96, 96, 78, 78, 78,
+
+ 78, 78, 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78
+ } ;
+
+/* Table of booleans, true if rule could match eol. */
+static const flex_int32_t yy_rule_can_match_eol[25] =
+ { 0,
+1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, };
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+/*
+
+This file must be translated to C and modified to build everywhere.
+
+Run flex >= 2.6 like this:
+
+ flex --nounistd -DFLEXINT_H --noline -ocmListFileLexer.cxx cmListFileLexer.in.l
+
+Modify cmListFileLexer.c:
+ - remove trailing whitespace: sed -i 's/\s*$//' cmListFileLexer.c
+ - remove blank lines at end of file: sed -i '${/^$/d;}' cmListFileLexer.c
+ - #include "cmStandardLexer.h" at the top: sed -i '1i#include "cmStandardLexer.h"' cmListFileLexer.c
+
+*/
+
+/* IWYU pragma: no_forward_declare yyguts_t */
+
+/* Setup the proper cmListFileLexer_yylex declaration. */
+#define YY_EXTRA_TYPE cmListFileLexer*
+#define YY_DECL int cmListFileLexer_yylex (yyscan_t yyscanner, cmListFileLexer* lexer)
+
+#include "cmListFileLexer.h"
+
+/*--------------------------------------------------------------------------*/
+struct cmListFileLexer_s
+{
+ cmListFileLexer_Token token;
+ int bracket;
+ int comment;
+ int line;
+ int column;
+ int size;
+ FILE* file;
+ size_t cr;
+ char* string_buffer;
+ char* string_position;
+ int string_left;
+ yyscan_t scanner;
+};
+
+static void cmListFileLexerSetToken(cmListFileLexer* lexer, const char* text,
+ int length);
+static void cmListFileLexerAppend(cmListFileLexer* lexer, const char* text,
+ int length);
+static int cmListFileLexerInput(cmListFileLexer* lexer, char* buffer,
+ size_t bufferSize);
+static void cmListFileLexerInit(cmListFileLexer* lexer);
+static void cmListFileLexerDestroy(cmListFileLexer* lexer);
+
+/* Replace the lexer input function. */
+#undef YY_INPUT
+#define YY_INPUT(buf, result, max_size) \
+ do { result = cmListFileLexerInput(cmListFileLexer_yyget_extra(yyscanner), buf, max_size); } while (0)
+
+/*--------------------------------------------------------------------------*/
+
+#define INITIAL 0
+#define STRING 1
+#define BRACKET 2
+#define BRACKETEND 3
+#define COMMENT 4
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+ {
+
+ /* User-defined. Not touched by flex. */
+ YY_EXTRA_TYPE yyextra_r;
+
+ /* The rest are the same as the globals declared in the non-reentrant scanner. */
+ FILE *yyin_r, *yyout_r;
+ size_t yy_buffer_stack_top; /**< index of top of stack. */
+ size_t yy_buffer_stack_max; /**< capacity of stack. */
+ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+ char yy_hold_char;
+ int yy_n_chars;
+ int yyleng_r;
+ char *yy_c_buf_p;
+ int yy_init;
+ int yy_start;
+ int yy_did_buffer_switch_on_eof;
+ int yy_start_stack_ptr;
+ int yy_start_stack_depth;
+ int *yy_start_stack;
+ yy_state_type yy_last_accepting_state;
+ char* yy_last_accepting_cpos;
+
+ int yylineno_r;
+ int yy_flex_debug_r;
+
+ char *yytext_r;
+ int yy_more_flag;
+ int yy_more_len;
+
+ }; /* end struct yyguts_t */
+
+static int yy_init_globals ( yyscan_t yyscanner );
+
+int yylex_init (yyscan_t* scanner);
+
+int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner);
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy ( yyscan_t yyscanner );
+
+int yyget_debug ( yyscan_t yyscanner );
+
+void yyset_debug ( int debug_flag , yyscan_t yyscanner );
+
+YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner );
+
+void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner );
+
+FILE *yyget_in ( yyscan_t yyscanner );
+
+void yyset_in ( FILE * _in_str , yyscan_t yyscanner );
+
+FILE *yyget_out ( yyscan_t yyscanner );
+
+void yyset_out ( FILE * _out_str , yyscan_t yyscanner );
+
+ int yyget_leng ( yyscan_t yyscanner );
+
+char *yyget_text ( yyscan_t yyscanner );
+
+int yyget_lineno ( yyscan_t yyscanner );
+
+void yyset_lineno ( int _line_number , yyscan_t yyscanner );
+
+int yyget_column ( yyscan_t yyscanner );
+
+void yyset_column ( int _column_no , yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap ( yyscan_t yyscanner );
+#else
+extern int yywrap ( yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef YY_NO_UNPUT
+
+ static void yyunput ( int c, char *buf_ptr , yyscan_t yyscanner);
+
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen ( const char * , yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+static int yyinput ( yyscan_t yyscanner );
+#else
+static int input ( yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ int n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (yyscan_t yyscanner);
+
+#define YY_DECL int yylex (yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK /*LINTED*/break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ yy_state_type yy_current_state;
+ char *yy_cp, *yy_bp;
+ int yy_act;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( !yyg->yy_init )
+ {
+ yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! yyg->yy_start )
+ yyg->yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner);
+ }
+
+ yy_load_buffer_state( yyscanner );
+ }
+
+ {
+
+ while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yyg->yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yyg->yy_start;
+yy_match:
+ do
+ {
+ YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 79 )
+ yy_c = yy_meta[yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 298 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+ if ( yy_act == 0 )
+ { /* have to back up */
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ yy_act = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+ if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] )
+ {
+ int yyl;
+ for ( yyl = 0; yyl < yyleng; ++yyl )
+ if ( yytext[yyl] == '\n' )
+
+ do{ yylineno++;
+ yycolumn=0;
+ }while(0)
+;
+ }
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = yyg->yy_hold_char;
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ goto yy_find_action;
+
+case 1:
+/* rule 1 can match eol */
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_Newline;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ ++lexer->line;
+ lexer->column = 1;
+ BEGIN(INITIAL);
+ return 1;
+}
+ YY_BREAK
+case 2:
+/* rule 2 can match eol */
+YY_RULE_SETUP
+{
+ const char* bracket = yytext;
+ lexer->comment = yytext[0] == '#';
+ if (lexer->comment) {
+ lexer->token.type = cmListFileLexer_Token_CommentBracket;
+ bracket += 1;
+ } else {
+ lexer->token.type = cmListFileLexer_Token_ArgumentBracket;
+ }
+ cmListFileLexerSetToken(lexer, "", 0);
+ lexer->bracket = strchr(bracket+1, '[') - bracket;
+ if (yytext[yyleng-1] == '\n') {
+ ++lexer->line;
+ lexer->column = 1;
+ } else {
+ lexer->column += yyleng;
+ }
+ BEGIN(BRACKET);
+}
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+{
+ lexer->column += yyleng;
+ BEGIN(COMMENT);
+}
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+{
+ lexer->column += yyleng;
+}
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_ParenLeft;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_ParenRight;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_Identifier;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+{
+ /* Handle ]]====]=======]*/
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ if (yyleng == lexer->bracket) {
+ BEGIN(BRACKETEND);
+ }
+}
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+{
+ lexer->column += yyleng;
+ /* Erase the partial bracket from the token. */
+ lexer->token.length -= lexer->bracket;
+ lexer->token.text[lexer->token.length] = 0;
+ BEGIN(INITIAL);
+ return 1;
+}
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+{
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+}
+ YY_BREAK
+case 11:
+/* rule 11 can match eol */
+YY_RULE_SETUP
+{
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ ++lexer->line;
+ lexer->column = 1;
+ BEGIN(BRACKET);
+}
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+{
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ BEGIN(BRACKET);
+}
+ YY_BREAK
+case YY_STATE_EOF(BRACKET):
+case YY_STATE_EOF(BRACKETEND):
+{
+ lexer->token.type = cmListFileLexer_Token_BadBracket;
+ BEGIN(INITIAL);
+ return 1;
+}
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_ArgumentUnquoted;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_ArgumentUnquoted;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_ArgumentUnquoted;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_ArgumentQuoted;
+ cmListFileLexerSetToken(lexer, "", 0);
+ lexer->column += yyleng;
+ BEGIN(STRING);
+}
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+{
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+}
+ YY_BREAK
+case 18:
+/* rule 18 can match eol */
+YY_RULE_SETUP
+{
+ /* Continuation: text is not part of string */
+ ++lexer->line;
+ lexer->column = 1;
+}
+ YY_BREAK
+case 19:
+/* rule 19 can match eol */
+YY_RULE_SETUP
+{
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ ++lexer->line;
+ lexer->column = 1;
+}
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+{
+ lexer->column += yyleng;
+ BEGIN(INITIAL);
+ return 1;
+}
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+{
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+}
+ YY_BREAK
+case YY_STATE_EOF(STRING):
+{
+ lexer->token.type = cmListFileLexer_Token_BadString;
+ BEGIN(INITIAL);
+ return 1;
+}
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_Space;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_BadCharacter;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+ YY_BREAK
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(COMMENT):
+{
+ lexer->token.type = cmListFileLexer_Token_None;
+ cmListFileLexerSetToken(lexer, 0, 0);
+ return 0;
+}
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+ECHO;
+ YY_BREAK
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yyg->yy_hold_char;
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yyg->yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = yyg->yy_c_buf_p;
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yyg->yy_did_buffer_switch_on_eof = 0;
+
+ if ( yywrap( yyscanner ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p =
+ yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yyg->yy_c_buf_p =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+ } /* end of user's declarations */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ char *source = yyg->yytext_ptr;
+ int number_to_move, i;
+ int ret_val;
+
+ if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1);
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
+
+ int yy_c_buf_p_offset =
+ (int) (yyg->yy_c_buf_p - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ yyrealloc( (void *) b->yy_ch_buf,
+ (yy_size_t) (b->yy_buf_size + 2) , yyscanner );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = NULL;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ yyg->yy_n_chars, num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ if ( yyg->yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart( yyin , yyscanner);
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc(
+ (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ /* "- 2" to take care of EOB's */
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2);
+ }
+
+ yyg->yy_n_chars += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+ yy_state_type yy_current_state;
+ char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_current_state = yyg->yy_start;
+
+ for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+ {
+ YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 16);
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 79 )
+ yy_c = yy_meta[yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+ int yy_is_jam;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+ char *yy_cp = yyg->yy_c_buf_p;
+
+ YY_CHAR yy_c = 16;
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 79 )
+ yy_c = yy_meta[yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+ yy_is_jam = (yy_current_state == 78);
+
+ (void)yyg;
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_UNPUT
+
+ static void yyunput (int c, char * yy_bp , yyscan_t yyscanner)
+{
+ char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yyg->yy_hold_char;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ int number_to_move = yyg->yy_n_chars + 2;
+ char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ yyg->yy_n_chars = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ if ( c == '\n' ){
+ --yylineno;
+ }
+
+ yyg->yytext_ptr = yy_bp;
+ yyg->yy_hold_char = *yy_cp;
+ yyg->yy_c_buf_p = yy_cp;
+}
+
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (yyscan_t yyscanner)
+#else
+ static int input (yyscan_t yyscanner)
+#endif
+
+{
+ int c;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+ if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ /* This was really a NUL. */
+ *yyg->yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr);
+ ++yyg->yy_c_buf_p;
+
+ switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart( yyin , yyscanner);
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap( yyscanner ) )
+ return 0;
+
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput(yyscanner);
+#else
+ return input(yyscanner);
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */
+ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */
+ yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+ if ( c == '\n' )
+
+ do{ yylineno++;
+ yycolumn=0;
+ }while(0)
+;
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void yyrestart (FILE * input_file , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! YY_CURRENT_BUFFER ){
+ yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner);
+ }
+
+ yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner);
+ yy_load_buffer_state( yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * yypop_buffer_state();
+ * yypush_buffer_state(new_buffer);
+ */
+ yyensure_buffer_stack (yyscanner);
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ yy_load_buffer_state( yyscanner );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void yy_load_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer( b, file , yyscanner);
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+ void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yyfree( (void *) b->yy_ch_buf , yyscanner );
+
+ yyfree( (void *) b , yyscanner );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner)
+
+{
+ int oerrno = errno;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_flush_buffer( b , yyscanner);
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then yy_init_buffer was _probably_
+ * called from yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+ void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ yy_load_buffer_state( yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ * @param yyscanner The scanner object.
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (new_buffer == NULL)
+ return;
+
+ yyensure_buffer_stack(yyscanner);
+
+ /* This block is copied from yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ yyg->yy_buffer_stack_top++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from yy_switch_to_buffer. */
+ yy_load_buffer_state( yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ * @param yyscanner The scanner object.
+ */
+void yypop_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner);
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if (yyg->yy_buffer_stack_top > 0)
+ --yyg->yy_buffer_stack_top;
+
+ if (YY_CURRENT_BUFFER) {
+ yy_load_buffer_state( yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (yyscan_t yyscanner)
+{
+ yy_size_t num_to_alloc;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (!yyg->yy_buffer_stack) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ yyg->yy_buffer_stack_top = 0;
+ return;
+ }
+
+ if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ yy_size_t grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc
+ (yyg->yy_buffer_stack,
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return NULL;
+
+ b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = NULL;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer( b , yyscanner );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner)
+{
+
+ return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = (yy_size_t) (_yybytes_len + 2);
+ buf = (char *) yyalloc( n , yyscanner );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer( buf, n , yyscanner);
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ (void)yyg;
+ fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = yyg->yy_hold_char; \
+ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+ yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+ *yyg->yy_c_buf_p = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int yyget_lineno (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int yyget_column (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *yyget_in (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *yyget_out (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+int yyget_leng (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *yyget_text (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param _line_number line number
+ * @param yyscanner The scanner object.
+ */
+void yyset_lineno (int _line_number , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* lineno is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ YY_FATAL_ERROR( "yyset_lineno called with no buffer" );
+
+ yylineno = _line_number;
+}
+
+/** Set the current column.
+ * @param _column_no column number
+ * @param yyscanner The scanner object.
+ */
+void yyset_column (int _column_no , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* column is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ YY_FATAL_ERROR( "yyset_column called with no buffer" );
+
+ yycolumn = _column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param _in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE * _in_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyin = _in_str ;
+}
+
+void yyset_out (FILE * _out_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyout = _out_str ;
+}
+
+int yyget_debug (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yy_flex_debug;
+}
+
+void yyset_debug (int _bdebug , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yy_flex_debug = _bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+/* User-visible API */
+
+/* yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+int yylex_init(yyscan_t* ptr_yy_globals)
+{
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+/* yylex_init_extra has the same functionality as yylex_init, but follows the
+ * convention of taking the scanner as the last argument. Note however, that
+ * this is a *pointer* to a scanner, as it will be allocated by this call (and
+ * is the reason, too, why this function also must handle its own declaration).
+ * The user defined value in the first argument will be available to yyalloc in
+ * the yyextra field.
+ */
+int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals )
+{
+ struct yyguts_t dummy_yyguts;
+
+ yyset_extra (yy_user_defined, &dummy_yyguts);
+
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in
+ yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ yyset_extra (yy_user_defined, *ptr_yy_globals);
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from yylex_destroy(), so don't allocate here.
+ */
+
+ yyg->yy_buffer_stack = NULL;
+ yyg->yy_buffer_stack_top = 0;
+ yyg->yy_buffer_stack_max = 0;
+ yyg->yy_c_buf_p = NULL;
+ yyg->yy_init = 0;
+ yyg->yy_start = 0;
+
+ yyg->yy_start_stack_ptr = 0;
+ yyg->yy_start_stack_depth = 0;
+ yyg->yy_start_stack = NULL;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = NULL;
+ yyout = NULL;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * yylex_init()
+ */
+ return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ yypop_buffer_state(yyscanner);
+ }
+
+ /* Destroy the stack itself. */
+ yyfree(yyg->yy_buffer_stack , yyscanner);
+ yyg->yy_buffer_stack = NULL;
+
+ /* Destroy the start condition stack. */
+ yyfree( yyg->yy_start_stack , yyscanner );
+ yyg->yy_start_stack = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * yylex() is called, initialization will occur. */
+ yy_init_globals( yyscanner);
+
+ /* Destroy the main struct (reentrant only). */
+ yyfree ( yyscanner , yyscanner );
+ yyscanner = NULL;
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ (void)yyg;
+
+ int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (const char * s , yyscan_t yyscanner)
+{
+ int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *yyalloc (yy_size_t size , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ (void)yyg;
+ return malloc(size);
+}
+
+void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ (void)yyg;
+
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return realloc(ptr, size);
+}
+
+void yyfree (void * ptr , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ (void)yyg;
+ free( (char *) ptr ); /* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+/*--------------------------------------------------------------------------*/
+static void cmListFileLexerSetToken(cmListFileLexer* lexer, const char* text,
+ int length)
+{
+ /* Set the token line and column number. */
+ lexer->token.line = lexer->line;
+ lexer->token.column = lexer->column;
+
+ /* Use the same buffer if possible. */
+ if (lexer->token.text) {
+ if (text && length < lexer->size) {
+ strcpy(lexer->token.text, text);
+ lexer->token.length = length;
+ return;
+ }
+ free(lexer->token.text);
+ lexer->token.text = 0;
+ lexer->size = 0;
+ }
+
+ /* Need to extend the buffer. */
+ if (text) {
+ lexer->token.text = strdup(text);
+ lexer->token.length = length;
+ lexer->size = length + 1;
+ } else {
+ lexer->token.length = 0;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+static void cmListFileLexerAppend(cmListFileLexer* lexer, const char* text,
+ int length)
+{
+ char* temp;
+ int newSize;
+
+ /* If the appended text will fit in the buffer, do not reallocate. */
+ newSize = lexer->token.length + length + 1;
+ if (lexer->token.text && newSize <= lexer->size) {
+ strcpy(lexer->token.text + lexer->token.length, text);
+ lexer->token.length += length;
+ return;
+ }
+
+ /* We need to extend the buffer. */
+ temp = (char *)malloc(newSize);
+ if (lexer->token.text) {
+ memcpy(temp, lexer->token.text, lexer->token.length);
+ free(lexer->token.text);
+ }
+ memcpy(temp + lexer->token.length, text, length);
+ temp[lexer->token.length + length] = 0;
+ lexer->token.text = temp;
+ lexer->token.length += length;
+ lexer->size = newSize;
+}
+
+/*--------------------------------------------------------------------------*/
+static int cmListFileLexerInput(cmListFileLexer* lexer, char* buffer,
+ size_t bufferSize)
+{
+ if (lexer) {
+ if (lexer->file) {
+ /* Convert CRLF -> LF explicitly. The C FILE "t"ext mode
+ does not convert newlines on all platforms. Move any
+ trailing CR to the start of the buffer for the next read. */
+ size_t cr = lexer->cr;
+ size_t n;
+ buffer[0] = '\r';
+ n = fread(buffer + cr, 1, bufferSize - cr, lexer->file);
+ if (n) {
+ char* o = buffer;
+ const char* i = buffer;
+ const char* e;
+ n += cr;
+ cr = (buffer[n - 1] == '\r') ? 1 : 0;
+ e = buffer + n - cr;
+ while (i != e) {
+ if (i[0] == '\r' && i[1] == '\n') {
+ ++i;
+ }
+ *o++ = *i++;
+ }
+ n = o - buffer;
+ } else {
+ n = cr;
+ cr = 0;
+ }
+ lexer->cr = cr;
+ return n;
+ } else if (lexer->string_left) {
+ int length = lexer->string_left;
+ if ((int)bufferSize < length) {
+ length = (int)bufferSize;
+ }
+ memcpy(buffer, lexer->string_position, length);
+ lexer->string_position += length;
+ lexer->string_left -= length;
+ return length;
+ }
+ }
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+static void cmListFileLexerInit(cmListFileLexer* lexer)
+{
+ if (lexer->file || lexer->string_buffer) {
+ cmListFileLexer_yylex_init(&lexer->scanner);
+ cmListFileLexer_yyset_extra(lexer, lexer->scanner);
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+static void cmListFileLexerDestroy(cmListFileLexer* lexer)
+{
+ cmListFileLexerSetToken(lexer, 0, 0);
+ if (lexer->file || lexer->string_buffer) {
+ cmListFileLexer_yylex_destroy(lexer->scanner);
+ if (lexer->file) {
+ fclose(lexer->file);
+ lexer->file = 0;
+ }
+ if (lexer->string_buffer) {
+ lexer->string_buffer = 0;
+ lexer->string_left = 0;
+ lexer->string_position = 0;
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+cmListFileLexer* cmListFileLexer_New(void)
+{
+ cmListFileLexer* lexer = (cmListFileLexer*)malloc(sizeof(cmListFileLexer));
+ if (!lexer) {
+ return 0;
+ }
+ memset(lexer, 0, sizeof(*lexer));
+ lexer->line = 1;
+ lexer->column = 1;
+ return lexer;
+}
+
+/*--------------------------------------------------------------------------*/
+void cmListFileLexer_Delete(cmListFileLexer* lexer)
+{
+ cmListFileLexer_SetFileName(lexer, 0, 0);
+ free(lexer);
+}
+
+/*--------------------------------------------------------------------------*/
+static cmListFileLexer_BOM cmListFileLexer_ReadBOM(FILE* f)
+{
+ unsigned char b[2];
+ if (fread(b, 1, 2, f) == 2) {
+ if (b[0] == 0xEF && b[1] == 0xBB) {
+ if (fread(b, 1, 1, f) == 1 && b[0] == 0xBF) {
+ return cmListFileLexer_BOM_UTF8;
+ }
+ } else if (b[0] == 0xFE && b[1] == 0xFF) {
+ /* UTF-16 BE */
+ return cmListFileLexer_BOM_UTF16BE;
+ } else if (b[0] == 0 && b[1] == 0) {
+ if (fread(b, 1, 2, f) == 2 && b[0] == 0xFE && b[1] == 0xFF) {
+ return cmListFileLexer_BOM_UTF32BE;
+ }
+ } else if (b[0] == 0xFF && b[1] == 0xFE) {
+ fpos_t p;
+ fgetpos(f, &p);
+ if (fread(b, 1, 2, f) == 2 && b[0] == 0 && b[1] == 0) {
+ return cmListFileLexer_BOM_UTF32LE;
+ }
+ if (fsetpos(f, &p) != 0) {
+ return cmListFileLexer_BOM_Broken;
+ }
+ return cmListFileLexer_BOM_UTF16LE;
+ }
+ }
+ if (fseek(f, 0, SEEK_SET) != 0) {
+ return cmListFileLexer_BOM_Broken;
+ }
+ return cmListFileLexer_BOM_None;
+}
+
+/*--------------------------------------------------------------------------*/
+int cmListFileLexer_SetFileName(cmListFileLexer* lexer, const char* name,
+ cmListFileLexer_BOM* bom)
+{
+ int result = 1;
+ cmListFileLexerDestroy(lexer);
+ if (name) {
+ lexer->file = fopen(name, "rb");
+ if (lexer->file) {
+ if (bom) {
+ *bom = cmListFileLexer_ReadBOM(lexer->file);
+ }
+ } else {
+ result = 0;
+ }
+ }
+ cmListFileLexerInit(lexer);
+ return result;
+}
+
+/*--------------------------------------------------------------------------*/
+int cmListFileLexer_SetString(cmListFileLexer* lexer, const char* text, int length)
+{
+ int result = 1;
+ cmListFileLexerDestroy(lexer);
+ if (text) {
+ lexer->string_buffer = (char *) text;
+ lexer->string_position = lexer->string_buffer;
+ lexer->string_left = length;
+ }
+ cmListFileLexerInit(lexer);
+ return result;
+}
+
+/*--------------------------------------------------------------------------*/
+cmListFileLexer_Token* cmListFileLexer_Scan(cmListFileLexer* lexer)
+{
+ if (!lexer->file && !lexer->string_buffer) {
+ return 0;
+ }
+ if (cmListFileLexer_yylex(lexer->scanner, lexer)) {
+ return &lexer->token;
+ } else {
+ cmListFileLexer_SetFileName(lexer, 0, 0);
+ return 0;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+long cmListFileLexer_GetCurrentLine(cmListFileLexer* lexer)
+{
+ return lexer->line;
+}
+
+/*--------------------------------------------------------------------------*/
+long cmListFileLexer_GetCurrentColumn(cmListFileLexer* lexer)
+{
+ return lexer->column;
+}
+
+/*--------------------------------------------------------------------------*/
+const char* cmListFileLexer_GetTypeAsString(cmListFileLexer* lexer,
+ cmListFileLexer_Type type)
+{
+ (void)lexer;
+ switch (type) {
+ case cmListFileLexer_Token_None:
+ return "nothing";
+ case cmListFileLexer_Token_Space:
+ return "space";
+ case cmListFileLexer_Token_Newline:
+ return "newline";
+ case cmListFileLexer_Token_Identifier:
+ return "identifier";
+ case cmListFileLexer_Token_ParenLeft:
+ return "left paren";
+ case cmListFileLexer_Token_ParenRight:
+ return "right paren";
+ case cmListFileLexer_Token_ArgumentUnquoted:
+ return "unquoted argument";
+ case cmListFileLexer_Token_ArgumentQuoted:
+ return "quoted argument";
+ case cmListFileLexer_Token_ArgumentBracket:
+ return "bracket argument";
+ case cmListFileLexer_Token_CommentBracket:
+ return "bracket comment";
+ case cmListFileLexer_Token_BadCharacter:
+ return "bad character";
+ case cmListFileLexer_Token_BadBracket:
+ return "unterminated bracket";
+ case cmListFileLexer_Token_BadString:
+ return "unterminated string";
+ }
+ return "unknown token";
+}
+
diff --git a/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.h b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.h
new file mode 100644
index 0000000000..14ee2c1ba1
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.h
@@ -0,0 +1,68 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* NOLINTNEXTLINE(modernize-use-using) */
+typedef enum cmListFileLexer_Type_e
+{
+ cmListFileLexer_Token_None,
+ cmListFileLexer_Token_Space,
+ cmListFileLexer_Token_Newline,
+ cmListFileLexer_Token_Identifier,
+ cmListFileLexer_Token_ParenLeft,
+ cmListFileLexer_Token_ParenRight,
+ cmListFileLexer_Token_ArgumentUnquoted,
+ cmListFileLexer_Token_ArgumentQuoted,
+ cmListFileLexer_Token_ArgumentBracket,
+ cmListFileLexer_Token_CommentBracket,
+ cmListFileLexer_Token_BadCharacter,
+ cmListFileLexer_Token_BadBracket,
+ cmListFileLexer_Token_BadString
+} cmListFileLexer_Type;
+
+/* NOLINTNEXTLINE(modernize-use-using) */
+typedef struct cmListFileLexer_Token_s cmListFileLexer_Token;
+struct cmListFileLexer_Token_s
+{
+ cmListFileLexer_Type type;
+ char* text;
+ int length;
+ int line;
+ int column;
+};
+
+enum cmListFileLexer_BOM_e
+{
+ cmListFileLexer_BOM_None,
+ cmListFileLexer_BOM_Broken,
+ cmListFileLexer_BOM_UTF8,
+ cmListFileLexer_BOM_UTF16BE,
+ cmListFileLexer_BOM_UTF16LE,
+ cmListFileLexer_BOM_UTF32BE,
+ cmListFileLexer_BOM_UTF32LE
+};
+
+/* NOLINTNEXTLINE(modernize-use-using) */
+typedef enum cmListFileLexer_BOM_e cmListFileLexer_BOM;
+
+/* NOLINTNEXTLINE(modernize-use-using) */
+typedef struct cmListFileLexer_s cmListFileLexer;
+
+cmListFileLexer* cmListFileLexer_New(void);
+int cmListFileLexer_SetFileName(cmListFileLexer*, const char*,
+ cmListFileLexer_BOM* bom);
+int cmListFileLexer_SetString(cmListFileLexer*, const char*, int length);
+cmListFileLexer_Token* cmListFileLexer_Scan(cmListFileLexer*);
+long cmListFileLexer_GetCurrentLine(cmListFileLexer*);
+long cmListFileLexer_GetCurrentColumn(cmListFileLexer*);
+const char* cmListFileLexer_GetTypeAsString(cmListFileLexer*,
+ cmListFileLexer_Type);
+void cmListFileLexer_Delete(cmListFileLexer*);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
diff --git a/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.in.l b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.in.l
new file mode 100644
index 0000000000..b7a5c46687
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.in.l
@@ -0,0 +1,543 @@
+%{
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+/*
+
+This file must be translated to C and modified to build everywhere.
+
+Run flex >= 2.6 like this:
+
+ flex --nounistd -DFLEXINT_H --noline -ocmListFileLexer.cxx cmListFileLexer.in.l
+
+Modify cmListFileLexer.c:
+ - remove trailing whitespace: sed -i 's/\s*$//' cmListFileLexer.c
+ - remove blank lines at end of file: sed -i '${/^$/d;}' cmListFileLexer.c
+ - #include "cmStandardLexer.h" at the top: sed -i '1i#include "cmStandardLexer.h"' cmListFileLexer.c
+
+*/
+
+/* IWYU pragma: no_forward_declare yyguts_t */
+
+/* Setup the proper cmListFileLexer_yylex declaration. */
+#define YY_EXTRA_TYPE cmListFileLexer*
+#define YY_DECL int cmListFileLexer_yylex (yyscan_t yyscanner, cmListFileLexer* lexer)
+
+#include "cmListFileLexer.h"
+
+/*--------------------------------------------------------------------------*/
+struct cmListFileLexer_s
+{
+ cmListFileLexer_Token token;
+ int bracket;
+ int comment;
+ int line;
+ int column;
+ int size;
+ FILE* file;
+ size_t cr;
+ char* string_buffer;
+ char* string_position;
+ int string_left;
+ yyscan_t scanner;
+};
+
+static void cmListFileLexerSetToken(cmListFileLexer* lexer, const char* text,
+ int length);
+static void cmListFileLexerAppend(cmListFileLexer* lexer, const char* text,
+ int length);
+static int cmListFileLexerInput(cmListFileLexer* lexer, char* buffer,
+ size_t bufferSize);
+static void cmListFileLexerInit(cmListFileLexer* lexer);
+static void cmListFileLexerDestroy(cmListFileLexer* lexer);
+
+/* Replace the lexer input function. */
+#undef YY_INPUT
+#define YY_INPUT(buf, result, max_size) \
+ do { result = cmListFileLexerInput(cmListFileLexer_yyget_extra(yyscanner), buf, max_size); } while (0)
+
+/*--------------------------------------------------------------------------*/
+%}
+
+%option prefix="cmListFileLexer_yy"
+
+%option reentrant
+%option yylineno
+%option noyywrap
+%pointer
+%x STRING
+%x BRACKET
+%x BRACKETEND
+%x COMMENT
+
+MAKEVAR \$\([A-Za-z0-9_]*\)
+UNQUOTED ([^ \0\t\r\n\(\)#\\\"[=]|\\[^\0\n])
+LEGACY {MAKEVAR}|{UNQUOTED}|\"({MAKEVAR}|{UNQUOTED}|[ \t[=])*\"
+
+%%
+
+<INITIAL,COMMENT>\n {
+ lexer->token.type = cmListFileLexer_Token_Newline;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ ++lexer->line;
+ lexer->column = 1;
+ BEGIN(INITIAL);
+ return 1;
+}
+
+#?\[=*\[\n? {
+ const char* bracket = yytext;
+ lexer->comment = yytext[0] == '#';
+ if (lexer->comment) {
+ lexer->token.type = cmListFileLexer_Token_CommentBracket;
+ bracket += 1;
+ } else {
+ lexer->token.type = cmListFileLexer_Token_ArgumentBracket;
+ }
+ cmListFileLexerSetToken(lexer, "", 0);
+ lexer->bracket = strchr(bracket+1, '[') - bracket;
+ if (yytext[yyleng-1] == '\n') {
+ ++lexer->line;
+ lexer->column = 1;
+ } else {
+ lexer->column += yyleng;
+ }
+ BEGIN(BRACKET);
+}
+
+# {
+ lexer->column += yyleng;
+ BEGIN(COMMENT);
+}
+
+<COMMENT>[^\0\n]* {
+ lexer->column += yyleng;
+}
+
+\( {
+ lexer->token.type = cmListFileLexer_Token_ParenLeft;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+
+\) {
+ lexer->token.type = cmListFileLexer_Token_ParenRight;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+
+[A-Za-z_][A-Za-z0-9_]* {
+ lexer->token.type = cmListFileLexer_Token_Identifier;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+
+<BRACKET>\]=* {
+ /* Handle ]]====]=======]*/
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ if (yyleng == lexer->bracket) {
+ BEGIN(BRACKETEND);
+ }
+}
+
+<BRACKETEND>\] {
+ lexer->column += yyleng;
+ /* Erase the partial bracket from the token. */
+ lexer->token.length -= lexer->bracket;
+ lexer->token.text[lexer->token.length] = 0;
+ BEGIN(INITIAL);
+ return 1;
+}
+
+<BRACKET>([^]\0\n])+ {
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+}
+
+<BRACKET,BRACKETEND>\n {
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ ++lexer->line;
+ lexer->column = 1;
+ BEGIN(BRACKET);
+}
+
+<BRACKET,BRACKETEND>[^\0\n] {
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ BEGIN(BRACKET);
+}
+
+<BRACKET,BRACKETEND><<EOF>> {
+ lexer->token.type = cmListFileLexer_Token_BadBracket;
+ BEGIN(INITIAL);
+ return 1;
+}
+
+({UNQUOTED}|=|\[=*{UNQUOTED})({UNQUOTED}|[[=])* {
+ lexer->token.type = cmListFileLexer_Token_ArgumentUnquoted;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+
+({MAKEVAR}|{UNQUOTED}|=|\[=*{LEGACY})({LEGACY}|[[=])* {
+ lexer->token.type = cmListFileLexer_Token_ArgumentUnquoted;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+
+\[ {
+ lexer->token.type = cmListFileLexer_Token_ArgumentUnquoted;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+
+\" {
+ lexer->token.type = cmListFileLexer_Token_ArgumentQuoted;
+ cmListFileLexerSetToken(lexer, "", 0);
+ lexer->column += yyleng;
+ BEGIN(STRING);
+}
+
+<STRING>([^\\\0\n\"]|\\[^\0\n])+ {
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+}
+
+<STRING>\\\n {
+ /* Continuation: text is not part of string */
+ ++lexer->line;
+ lexer->column = 1;
+}
+
+<STRING>\n {
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ ++lexer->line;
+ lexer->column = 1;
+}
+
+<STRING>\" {
+ lexer->column += yyleng;
+ BEGIN(INITIAL);
+ return 1;
+}
+
+<STRING>[^\0\n] {
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+}
+
+<STRING><<EOF>> {
+ lexer->token.type = cmListFileLexer_Token_BadString;
+ BEGIN(INITIAL);
+ return 1;
+}
+
+[ \t\r]+ {
+ lexer->token.type = cmListFileLexer_Token_Space;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+
+. {
+ lexer->token.type = cmListFileLexer_Token_BadCharacter;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+
+<<EOF>> {
+ lexer->token.type = cmListFileLexer_Token_None;
+ cmListFileLexerSetToken(lexer, 0, 0);
+ return 0;
+}
+
+%%
+
+/*--------------------------------------------------------------------------*/
+static void cmListFileLexerSetToken(cmListFileLexer* lexer, const char* text,
+ int length)
+{
+ /* Set the token line and column number. */
+ lexer->token.line = lexer->line;
+ lexer->token.column = lexer->column;
+
+ /* Use the same buffer if possible. */
+ if (lexer->token.text) {
+ if (text && length < lexer->size) {
+ strcpy(lexer->token.text, text);
+ lexer->token.length = length;
+ return;
+ }
+ free(lexer->token.text);
+ lexer->token.text = 0;
+ lexer->size = 0;
+ }
+
+ /* Need to extend the buffer. */
+ if (text) {
+ lexer->token.text = strdup(text);
+ lexer->token.length = length;
+ lexer->size = length + 1;
+ } else {
+ lexer->token.length = 0;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+static void cmListFileLexerAppend(cmListFileLexer* lexer, const char* text,
+ int length)
+{
+ char* temp;
+ int newSize;
+
+ /* If the appended text will fit in the buffer, do not reallocate. */
+ newSize = lexer->token.length + length + 1;
+ if (lexer->token.text && newSize <= lexer->size) {
+ strcpy(lexer->token.text + lexer->token.length, text);
+ lexer->token.length += length;
+ return;
+ }
+
+ /* We need to extend the buffer. */
+ temp = (char *)malloc(newSize);
+ if (lexer->token.text) {
+ memcpy(temp, lexer->token.text, lexer->token.length);
+ free(lexer->token.text);
+ }
+ memcpy(temp + lexer->token.length, text, length);
+ temp[lexer->token.length + length] = 0;
+ lexer->token.text = temp;
+ lexer->token.length += length;
+ lexer->size = newSize;
+}
+
+/*--------------------------------------------------------------------------*/
+static int cmListFileLexerInput(cmListFileLexer* lexer, char* buffer,
+ size_t bufferSize)
+{
+ if (lexer) {
+ if (lexer->file) {
+ /* Convert CRLF -> LF explicitly. The C FILE "t"ext mode
+ does not convert newlines on all platforms. Move any
+ trailing CR to the start of the buffer for the next read. */
+ size_t cr = lexer->cr;
+ size_t n;
+ buffer[0] = '\r';
+ n = fread(buffer + cr, 1, bufferSize - cr, lexer->file);
+ if (n) {
+ char* o = buffer;
+ const char* i = buffer;
+ const char* e;
+ n += cr;
+ cr = (buffer[n - 1] == '\r') ? 1 : 0;
+ e = buffer + n - cr;
+ while (i != e) {
+ if (i[0] == '\r' && i[1] == '\n') {
+ ++i;
+ }
+ *o++ = *i++;
+ }
+ n = o - buffer;
+ } else {
+ n = cr;
+ cr = 0;
+ }
+ lexer->cr = cr;
+ return n;
+ } else if (lexer->string_left) {
+ int length = lexer->string_left;
+ if ((int)bufferSize < length) {
+ length = (int)bufferSize;
+ }
+ memcpy(buffer, lexer->string_position, length);
+ lexer->string_position += length;
+ lexer->string_left -= length;
+ return length;
+ }
+ }
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+static void cmListFileLexerInit(cmListFileLexer* lexer)
+{
+ if (lexer->file || lexer->string_buffer) {
+ cmListFileLexer_yylex_init(&lexer->scanner);
+ cmListFileLexer_yyset_extra(lexer, lexer->scanner);
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+static void cmListFileLexerDestroy(cmListFileLexer* lexer)
+{
+ cmListFileLexerSetToken(lexer, 0, 0);
+ if (lexer->file || lexer->string_buffer) {
+ cmListFileLexer_yylex_destroy(lexer->scanner);
+ if (lexer->file) {
+ fclose(lexer->file);
+ lexer->file = 0;
+ }
+ if (lexer->string_buffer) {
+ lexer->string_buffer = 0;
+ lexer->string_left = 0;
+ lexer->string_position = 0;
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+cmListFileLexer* cmListFileLexer_New(void)
+{
+ cmListFileLexer* lexer = (cmListFileLexer*)malloc(sizeof(cmListFileLexer));
+ if (!lexer) {
+ return 0;
+ }
+ memset(lexer, 0, sizeof(*lexer));
+ lexer->line = 1;
+ lexer->column = 1;
+ return lexer;
+}
+
+/*--------------------------------------------------------------------------*/
+void cmListFileLexer_Delete(cmListFileLexer* lexer)
+{
+ cmListFileLexer_SetFileName(lexer, 0, 0);
+ free(lexer);
+}
+
+/*--------------------------------------------------------------------------*/
+static cmListFileLexer_BOM cmListFileLexer_ReadBOM(FILE* f)
+{
+ unsigned char b[2];
+ if (fread(b, 1, 2, f) == 2) {
+ if (b[0] == 0xEF && b[1] == 0xBB) {
+ if (fread(b, 1, 1, f) == 1 && b[0] == 0xBF) {
+ return cmListFileLexer_BOM_UTF8;
+ }
+ } else if (b[0] == 0xFE && b[1] == 0xFF) {
+ /* UTF-16 BE */
+ return cmListFileLexer_BOM_UTF16BE;
+ } else if (b[0] == 0 && b[1] == 0) {
+ if (fread(b, 1, 2, f) == 2 && b[0] == 0xFE && b[1] == 0xFF) {
+ return cmListFileLexer_BOM_UTF32BE;
+ }
+ } else if (b[0] == 0xFF && b[1] == 0xFE) {
+ fpos_t p;
+ fgetpos(f, &p);
+ if (fread(b, 1, 2, f) == 2 && b[0] == 0 && b[1] == 0) {
+ return cmListFileLexer_BOM_UTF32LE;
+ }
+ if (fsetpos(f, &p) != 0) {
+ return cmListFileLexer_BOM_Broken;
+ }
+ return cmListFileLexer_BOM_UTF16LE;
+ }
+ }
+ if (fseek(f, 0, SEEK_SET) != 0) {
+ return cmListFileLexer_BOM_Broken;
+ }
+ return cmListFileLexer_BOM_None;
+}
+
+/*--------------------------------------------------------------------------*/
+int cmListFileLexer_SetFileName(cmListFileLexer* lexer, const char* name,
+ cmListFileLexer_BOM* bom)
+{
+ int result = 1;
+ cmListFileLexerDestroy(lexer);
+ if (name) {
+ lexer->file = fopen(name, "rb");
+ if (lexer->file) {
+ if (bom) {
+ *bom = cmListFileLexer_ReadBOM(lexer->file);
+ }
+ } else {
+ result = 0;
+ }
+ }
+ cmListFileLexerInit(lexer);
+ return result;
+}
+
+/*--------------------------------------------------------------------------*/
+int cmListFileLexer_SetString(cmListFileLexer* lexer, const char* text, int length)
+{
+ int result = 1;
+ cmListFileLexerDestroy(lexer);
+ if (text) {
+ lexer->string_buffer = (char *) text;
+ lexer->string_position = lexer->string_buffer;
+ lexer->string_left = length;
+ }
+ cmListFileLexerInit(lexer);
+ return result;
+}
+
+/*--------------------------------------------------------------------------*/
+cmListFileLexer_Token* cmListFileLexer_Scan(cmListFileLexer* lexer)
+{
+ if (!lexer->file && !lexer->string_buffer) {
+ return 0;
+ }
+ if (cmListFileLexer_yylex(lexer->scanner, lexer)) {
+ return &lexer->token;
+ } else {
+ cmListFileLexer_SetFileName(lexer, 0, 0);
+ return 0;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+long cmListFileLexer_GetCurrentLine(cmListFileLexer* lexer)
+{
+ return lexer->line;
+}
+
+/*--------------------------------------------------------------------------*/
+long cmListFileLexer_GetCurrentColumn(cmListFileLexer* lexer)
+{
+ return lexer->column;
+}
+
+/*--------------------------------------------------------------------------*/
+const char* cmListFileLexer_GetTypeAsString(cmListFileLexer* lexer,
+ cmListFileLexer_Type type)
+{
+ (void)lexer;
+ switch (type) {
+ case cmListFileLexer_Token_None:
+ return "nothing";
+ case cmListFileLexer_Token_Space:
+ return "space";
+ case cmListFileLexer_Token_Newline:
+ return "newline";
+ case cmListFileLexer_Token_Identifier:
+ return "identifier";
+ case cmListFileLexer_Token_ParenLeft:
+ return "left paren";
+ case cmListFileLexer_Token_ParenRight:
+ return "right paren";
+ case cmListFileLexer_Token_ArgumentUnquoted:
+ return "unquoted argument";
+ case cmListFileLexer_Token_ArgumentQuoted:
+ return "quoted argument";
+ case cmListFileLexer_Token_ArgumentBracket:
+ return "bracket argument";
+ case cmListFileLexer_Token_CommentBracket:
+ return "bracket comment";
+ case cmListFileLexer_Token_BadCharacter:
+ return "bad character";
+ case cmListFileLexer_Token_BadBracket:
+ return "unterminated bracket";
+ case cmListFileLexer_Token_BadString:
+ return "unterminated string";
+ }
+ return "unknown token";
+}
diff --git a/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmStandardLexer.h b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmStandardLexer.h
new file mode 100644
index 0000000000..0808861342
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmStandardLexer.h
@@ -0,0 +1,88 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#if defined(__linux)
+/* Needed for glibc < 2.12 */
+// NOLINTNEXTLINE(bugprone-reserved-identifier)
+# define _XOPEN_SOURCE 600
+#endif
+#if !defined(_POSIX_C_SOURCE) && !defined(_WIN32) && !defined(__sun) && \
+ !defined(__OpenBSD__)
+/* POSIX APIs are needed */
+// NOLINTNEXTLINE(bugprone-reserved-identifier)
+# define _POSIX_C_SOURCE 200809L
+#endif
+#if defined(__sun) && defined(__GNUC__) && !defined(__cplusplus)
+/* C sources: for fileno and strdup */
+// NOLINTNEXTLINE(bugprone-reserved-identifier)
+# define _XOPEN_SOURCE 600
+#endif
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+/* For isascii */
+// NOLINTNEXTLINE(bugprone-reserved-identifier)
+# define _XOPEN_SOURCE 700
+#endif
+
+/* Disable some warnings. */
+#if defined(_MSC_VER)
+# pragma warning(disable : 4018)
+# pragma warning(disable : 4127)
+# pragma warning(disable : 4131)
+# pragma warning(disable : 4244)
+# pragma warning(disable : 4251)
+# pragma warning(disable : 4267)
+# pragma warning(disable : 4305)
+# pragma warning(disable : 4309)
+# pragma warning(disable : 4706)
+# pragma warning(disable : 4786)
+# pragma warning(disable : 4996)
+#endif
+
+#if defined(__GNUC__) && !defined(__INTEL_COMPILER)
+# if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 402
+# pragma GCC diagnostic ignored "-Wconversion"
+# pragma GCC diagnostic ignored "-Wsign-compare"
+# endif
+# if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 403
+# pragma GCC diagnostic ignored "-Wsign-conversion"
+# endif
+#endif
+
+#if defined(__LCC__)
+# pragma diag_suppress 1873 /* comparison between signed and unsigned */
+#endif
+
+#if defined(__NVCOMPILER)
+# pragma diag_suppress 111 /* statement is unreachable */
+# pragma diag_suppress 550 /* variable set but never used */
+#endif
+
+/* Make sure isatty is available. */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+# include <io.h>
+# if defined(_MSC_VER)
+# define isatty _isatty
+# endif
+#else
+# include <unistd.h> // IWYU pragma: export
+#endif
+
+/* Make sure malloc and free are available on QNX. */
+#ifdef __QNX__
+# include <malloc.h>
+#endif
+
+/* Disable features we do not need. */
+#define YY_NEVER_INTERACTIVE 1
+#define YY_NO_INPUT 1
+#define YY_NO_UNPUT 1
+#define ECHO
+
+#include <stdint.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
diff --git a/src/plugins/cmakeprojectmanager/CMakeLists.txt b/src/plugins/cmakeprojectmanager/CMakeLists.txt
index 683a2a407a..057b413945 100644
--- a/src/plugins/cmakeprojectmanager/CMakeLists.txt
+++ b/src/plugins/cmakeprojectmanager/CMakeLists.txt
@@ -2,6 +2,7 @@ add_qtc_plugin(CMakeProjectManager
PLUGIN_CLASS CMakeProjectPlugin
DEPENDS QmlJS
PLUGIN_DEPENDS Core CppEditor ProjectExplorer TextEditor QtSupport
+ INCLUDES 3dparty/cmake
SOURCES
builddirparameters.cpp builddirparameters.h
cmake_global.h
@@ -15,8 +16,6 @@ add_qtc_plugin(CMakeProjectManager
cmakeeditor.cpp cmakeeditor.h
cmakefilecompletionassist.cpp cmakefilecompletionassist.h
cmakeformatter.cpp cmakeformatter.h
- cmakeformatteroptionspage.cpp cmakeformatteroptionspage.h
- cmakeformattersettings.cpp cmakeformattersettings.h
cmakeindenter.cpp cmakeindenter.h
cmakeinstallstep.cpp cmakeinstallstep.h
cmakekitinformation.cpp cmakekitinformation.h
@@ -44,4 +43,7 @@ add_qtc_plugin(CMakeProjectManager
presetsparser.cpp presetsparser.h
presetsmacros.cpp presetsmacros.h
projecttreehelper.cpp projecttreehelper.h
+ 3rdparty/cmake/cmListFileCache.cxx
+ 3rdparty/cmake/cmListFileLexer.cxx
+ 3rdparty/cmake/cmListFileCache.h
)
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp
index 494d7ee67d..5ddc68c1c2 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp
@@ -41,7 +41,7 @@
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectexplorertr.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
@@ -54,7 +54,6 @@
#include <utils/checkablemessagebox.h>
#include <utils/commandline.h>
#include <utils/detailswidget.h>
-#include <utils/headerviewstretcher.h>
#include <utils/infolabel.h>
#include <utils/itemviews.h>
#include <utils/layoutbuilder.h>
@@ -68,6 +67,7 @@
#include <QDialogButtonBox>
#include <QDir>
#include <QGridLayout>
+#include <QHeaderView>
#include <QLoggingCategory>
#include <QMenu>
#include <QMessageBox>
@@ -250,7 +250,7 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) :
m_configView->setUniformRowHeights(true);
m_configView->setSortingEnabled(true);
m_configView->sortByColumn(0, Qt::AscendingOrder);
- (void) new HeaderViewStretcher(m_configView->header(), 0);
+ m_configView->header()->setSectionResizeMode(QHeaderView::Stretch);
m_configView->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_configView->setSelectionBehavior(QAbstractItemView::SelectItems);
m_configView->setAlternatingRowColors(true);
@@ -335,8 +335,8 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) :
Column {
Form {
- buildDirAspect,
- bc->aspect<BuildTypeAspect>(),
+ buildDirAspect, br,
+ bc->aspect<BuildTypeAspect>(), br,
qmlDebugAspect
},
m_warningMessageLabel,
@@ -347,19 +347,21 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) :
Column {
cmakeConfiguration,
Row {
- bc->aspect<InitialCMakeArgumentsAspect>(),
+ bc->aspect<InitialCMakeArgumentsAspect>(), br,
bc->aspect<AdditionalCMakeOptionsAspect>()
},
m_reconfigureButton,
}
},
configureEnvironmentAspectWidget
- }
- }.attachTo(details, WithoutMargins);
+ },
+ noMargin
+ }.attachTo(details);
Column {
m_configureDetailsWidget,
- }.attachTo(this, WithoutMargins);
+ noMargin
+ }.attachTo(this);
updateAdvancedCheckBox();
setError(m_buildSystem->error());
@@ -588,23 +590,18 @@ void CMakeBuildSettingsWidget::batchEditConfiguration()
void CMakeBuildSettingsWidget::reconfigureWithInitialParameters()
{
auto settings = CMakeSpecificSettings::instance();
- bool doNotAsk = !settings->askBeforeReConfigureInitialParams.value();
- if (!doNotAsk) {
- QDialogButtonBox::StandardButton reply = CheckableMessageBox::question(
- Core::ICore::dialogParent(),
- Tr::tr("Re-configure with Initial Parameters"),
- Tr::tr("Clear CMake configuration and configure with initial parameters?"),
- Tr::tr("Do not ask again"),
- &doNotAsk,
- QDialogButtonBox::Yes | QDialogButtonBox::No,
- QDialogButtonBox::Yes);
-
- settings->askBeforeReConfigureInitialParams.setValue(!doNotAsk);
- settings->writeSettings(Core::ICore::settings());
-
- if (reply != QDialogButtonBox::Yes) {
- return;
- }
+ QMessageBox::StandardButton reply = CheckableMessageBox::question(
+ Core::ICore::dialogParent(),
+ Tr::tr("Re-configure with Initial Parameters"),
+ Tr::tr("Clear CMake configuration and configure with initial parameters?"),
+ settings->askBeforeReConfigureInitialParams.checkableDecider(),
+ QMessageBox::Yes | QMessageBox::No,
+ QMessageBox::Yes);
+
+ settings->writeSettings(Core::ICore::settings());
+
+ if (reply != QMessageBox::Yes) {
+ return;
}
m_buildSystem->clearCMakeCache();
@@ -643,7 +640,7 @@ void CMakeBuildSettingsWidget::updateInitialCMakeArguments()
// As the user would expect to have e.g. "--preset" from "Initial Configuration"
// to "Current Configuration" as additional parameters
m_buildSystem->setAdditionalCMakeArguments(ProcessArgs::splitArgs(
- bc->aspect<InitialCMakeArgumentsAspect>()->value()));
+ bc->aspect<InitialCMakeArgumentsAspect>()->value(), HostOsInfo::hostOs()));
}
void CMakeBuildSettingsWidget::kitCMakeConfiguration()
@@ -663,17 +660,19 @@ void CMakeBuildSettingsWidget::kitCMakeConfiguration()
CMakeGeneratorKitAspect generatorAspect;
CMakeConfigurationKitAspect configurationKitAspect;
- auto layout = new QGridLayout(dialog);
-
+ Layouting::Grid grid;
KitAspectWidget *widget = kitAspect.createConfigWidget(m_buildSystem->kit());
widget->setParent(dialog);
- widget->addToLayoutWithLabel(layout->parentWidget());
+ widget->addToLayoutWithLabel(grid, dialog);
widget = generatorAspect.createConfigWidget(m_buildSystem->kit());
widget->setParent(dialog);
- widget->addToLayoutWithLabel(layout->parentWidget());
+ widget->addToLayoutWithLabel(grid, dialog);
widget = configurationKitAspect.createConfigWidget(m_buildSystem->kit());
widget->setParent(dialog);
- widget->addToLayoutWithLabel(layout->parentWidget());
+ widget->addToLayoutWithLabel(grid, dialog);
+ grid.attachTo(dialog);
+
+ auto layout = qobject_cast<QGridLayout *>(dialog->layout());
layout->setColumnStretch(1, 1);
@@ -701,7 +700,7 @@ void CMakeBuildSettingsWidget::updateConfigureDetailsWidgetsSummary(
const FilePath buildDirectory = bc ? bc->buildDirectory() : ".";
cmd.addArgs({"-S", m_buildSystem->projectDirectory().path()});
- cmd.addArgs({"-B", buildDirectory.onDevice(cmd.executable()).path()});
+ cmd.addArgs({"-B", buildDirectory.path()});
cmd.addArgs(configurationArguments);
params.setCommandLine(cmd);
@@ -826,9 +825,10 @@ void CMakeBuildSettingsWidget::updateFromKit()
// Then the additional parameters
const QStringList additionalKitCMake = ProcessArgs::splitArgs(
- CMakeConfigurationKitAspect::additionalConfiguration(k));
+ CMakeConfigurationKitAspect::additionalConfiguration(k), HostOsInfo::hostOs());
const QStringList additionalInitialCMake = ProcessArgs::splitArgs(
- m_buildSystem->buildConfiguration()->aspect<InitialCMakeArgumentsAspect>()->value());
+ m_buildSystem->buildConfiguration()->aspect<InitialCMakeArgumentsAspect>()->value(),
+ HostOsInfo::hostOs());
QStringList mergedArgumentList;
std::set_union(additionalInitialCMake.begin(),
@@ -1137,7 +1137,7 @@ static CommandLine defaultInitialCMakeCommand(const Kit *k, const QString buildT
// Package manager auto setup
if (Internal::CMakeSpecificSettings::instance()->packageManagerAutoSetup.value()) {
cmd.addArg(QString("-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH="
- "%{buildDir}/%1/auto-setup.cmake")
+ "%{BuildConfig:BuildDirectory:NativeFilePath}/%1/auto-setup.cmake")
.arg(Constants::PACKAGE_MANAGER_DIR));
}
@@ -1177,12 +1177,6 @@ static void addCMakeConfigurePresetToInitialArguments(QStringList &initialArgume
initialArguments.removeIf(
[presetArgument](const QString &item) { return item == presetArgument; });
- // Remove the -DQTC_KIT_DEFAULT_CONFIG_HASH argument
- const QString presetHashArgument
- = CMakeConfigurationKitAspect::kitDefaultConfigHashItem(k).toArgument();
- initialArguments.removeIf(
- [presetHashArgument](const QString &item) { return item == presetHashArgument; });
-
PresetsDetails::ConfigurePreset configurePreset
= Utils::findOrDefault(project->presetsData().configurePresets,
[presetName](const PresetsDetails::ConfigurePreset &preset) {
@@ -1743,7 +1737,8 @@ void CMakeBuildSystem::setInitialCMakeArguments(const QStringList &args)
QStringList CMakeBuildSystem::additionalCMakeArguments() const
{
- return ProcessArgs::splitArgs(buildConfiguration()->aspect<AdditionalCMakeOptionsAspect>()->value());
+ return ProcessArgs::splitArgs(buildConfiguration()->aspect<AdditionalCMakeOptionsAspect>()->value(),
+ HostOsInfo::hostOs());
}
void CMakeBuildSystem::setAdditionalCMakeArguments(const QStringList &args)
@@ -1766,7 +1761,8 @@ void CMakeBuildSystem::filterConfigArgumentsFromAdditionalCMakeArguments()
// which is already part of the CMake variables and should not be also
// in the addtional CMake options
const QStringList arguments = ProcessArgs::splitArgs(
- buildConfiguration()->aspect<AdditionalCMakeOptionsAspect>()->value());
+ buildConfiguration()->aspect<AdditionalCMakeOptionsAspect>()->value(),
+ HostOsInfo::hostOs());
QStringList unknownOptions;
const CMakeConfig config = CMakeConfig::fromArguments(arguments, unknownOptions);
@@ -2083,8 +2079,8 @@ void CMakeBuildConfiguration::addToEnvironment(Utils::Environment &env) const
return;
auto settings = CMakeSpecificSettings::instance();
- if (!settings->ninjaPath.filePath().isEmpty()) {
- const Utils::FilePath ninja = settings->ninjaPath.filePath();
+ if (!settings->ninjaPath().isEmpty()) {
+ const Utils::FilePath ninja = settings->ninjaPath();
env.appendOrSetPath(ninja.isFile() ? ninja.parentDir() : ninja);
}
}
@@ -2162,7 +2158,7 @@ const QStringList InitialCMakeArgumentsAspect::allValues() const
return ci.toArgument(nullptr);
});
- initialCMakeArguments.append(ProcessArgs::splitArgs(value()));
+ initialCMakeArguments.append(ProcessArgs::splitArgs(value(), HostOsInfo::hostOs()));
return initialCMakeArguments;
}
@@ -2281,6 +2277,7 @@ public:
ConfigureEnvironmentAspect::ConfigureEnvironmentAspect(ProjectExplorer::Target *target)
{
setIsLocal(true);
+ setAllowPrintOnRun(false);
setConfigWidgetCreator(
[this, target] { return new ConfigureEnvironmentAspectWidget(this, target); });
addSupportedBaseEnvironment(Tr::tr("Clean Environment"), {});
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp
index ccb21ccfb3..2d2501ab03 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp
@@ -31,6 +31,7 @@
#include <utils/layoutbuilder.h>
#include <QListWidget>
+#include <QRandomGenerator>
#include <QRegularExpression>
#include <QTreeView>
#include <QCheckBox>
@@ -44,6 +45,8 @@ namespace CMakeProjectManager::Internal {
const char BUILD_TARGETS_KEY[] = "CMakeProjectManager.MakeStep.BuildTargets";
const char CMAKE_ARGUMENTS_KEY[] = "CMakeProjectManager.MakeStep.CMakeArguments";
const char TOOL_ARGUMENTS_KEY[] = "CMakeProjectManager.MakeStep.AdditionalArguments";
+const char USE_STAGING_KEY[] = "CMakeProjectManager.MakeStep.UseStaging";
+const char STAGING_DIR_KEY[] = "CMakeProjectManager.MakeStep.StagingDir";
const char IOS_AUTOMATIC_PROVISIONG_UPDATES_ARGUMENTS_KEY[] =
"CMakeProjectManager.MakeStep.iOSAutomaticProvisioningUpdates";
const char CLEAR_SYSTEM_ENVIRONMENT_KEY[] = "CMakeProjectManager.MakeStep.ClearSystemEnvironment";
@@ -156,7 +159,27 @@ Qt::ItemFlags CMakeTargetItem::flags(int) const
// CMakeBuildStep
-CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Utils::Id id) :
+static QString initialStagingDir()
+{
+ // Avoid actual file accesses.
+ auto rg = QRandomGenerator::global();
+ const qulonglong rand = rg->generate64();
+ char buf[sizeof(rand)];
+ memcpy(&buf, &rand, sizeof(rand));
+ const QByteArray ba = QByteArray(buf, sizeof(buf)).toHex();
+ return QString::fromUtf8("/tmp/Qt-Creator-staging-" + ba);
+}
+
+static bool buildAndRunOnSameDevice(Kit *kit)
+{
+ IDeviceConstPtr runDevice = DeviceKitAspect::device(kit);
+ IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(kit);
+ QTC_ASSERT(runDevice, return false);
+ QTC_ASSERT(buildDevice, return false);
+ return runDevice->id() == buildDevice->id();
+}
+
+CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Id id) :
CMakeAbstractProcessStep(bsl, id)
{
m_cmakeArguments = addAspect<StringAspect>();
@@ -169,6 +192,16 @@ CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Utils::Id id) :
m_toolArguments->setLabelText(Tr::tr("Tool arguments:"));
m_toolArguments->setDisplayStyle(StringAspect::LineEditDisplay);
+ m_useStaging = addAspect<BoolAspect>();
+ m_useStaging->setSettingsKey(USE_STAGING_KEY);
+ m_useStaging->setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox);
+ m_useStaging->setDefaultValue(!buildAndRunOnSameDevice(kit()));
+
+ m_stagingDir = addAspect<FilePathAspect>();
+ m_stagingDir->setSettingsKey(STAGING_DIR_KEY);
+ m_stagingDir->setLabelText(Tr::tr("Staging directory:"));
+ m_stagingDir->setDefaultValue(initialStagingDir());
+
Kit *kit = buildConfiguration()->kit();
if (CMakeBuildConfiguration::isIos(kit)) {
m_useiOSAutomaticProvisioningUpdates = addAspect<BoolAspect>();
@@ -199,6 +232,9 @@ CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Utils::Id id) :
if (!env.expandedValueForKey("NINJA_STATUS").startsWith(ninjaProgressString))
env.set("NINJA_STATUS", ninjaProgressString + "%o/sec] ");
env.modify(m_userEnvironmentChanges);
+
+ if (m_useStaging)
+ env.set("DESTDIR", currentStagingDir());
});
connect(target(), &Target::parsingFinished, this, [this](bool success) {
@@ -210,7 +246,6 @@ CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Utils::Id id) :
this, &CMakeBuildStep::updateBuildTargetsModel);
}
-
QVariantMap CMakeBuildStep::toMap() const
{
QVariantMap map(CMakeAbstractProcessStep::toMap());
@@ -375,15 +410,13 @@ void CMakeBuildStep::setBuildTargets(const QStringList &buildTargets)
CommandLine CMakeBuildStep::cmakeCommand() const
{
- CommandLine cmd;
- if (CMakeTool *tool = CMakeKitAspect::cmakeTool(kit()))
- cmd.setExecutable(tool->cmakeExecutable());
+ CommandLine cmd{cmakeExecutable()};
FilePath buildDirectory = ".";
if (buildConfiguration())
buildDirectory = buildConfiguration()->buildDirectory();
- cmd.addArgs({"--build", buildDirectory.onDevice(cmd.executable()).path()});
+ cmd.addArgs({"--build", buildDirectory.path()});
cmd.addArg("--target");
cmd.addArgs(Utils::transform(m_buildTargets, [this](const QString &s) {
@@ -406,6 +439,9 @@ CommandLine CMakeBuildStep::cmakeCommand() const
if (!m_cmakeArguments->value().isEmpty())
cmd.addArgs(m_cmakeArguments->value(), CommandLine::Raw);
+ if (m_useStaging->value())
+ cmd.addArg("install");
+
bool toolArgumentsSpecified = false;
if (!m_toolArguments->value().isEmpty()) {
cmd.addArg("--");
@@ -466,6 +502,12 @@ QWidget *CMakeBuildStep::createConfigWidget()
QString summaryText = param.summary(displayName());
+ m_stagingDir->setEnabled(m_useStaging->value());
+ if (m_useStaging->value()) {
+ summaryText.append(" " + Tr::tr("and stage at %2 for %3")
+ .arg(currentStagingDir(), currentInstallPrefix()));
+ }
+
if (!m_buildPreset.isEmpty()) {
const CMakeProject *cp = static_cast<const CMakeProject *>(project());
@@ -519,28 +561,32 @@ QWidget *CMakeBuildStep::createConfigWidget()
envWidget->setBaseEnvironmentText(baseEnvironmentText());
});
- builder.addRow(clearBox);
- builder.addRow(envWidget);
+ builder.addRow({clearBox});
+ builder.addRow({envWidget});
};
Layouting::Form builder;
- builder.addRow(m_cmakeArguments);
- builder.addRow(m_toolArguments);
+ builder.addRow({m_cmakeArguments});
+ builder.addRow({m_toolArguments});
+ builder.addRow({Tr::tr("Stage for installation:"), Layouting::Row{m_useStaging, m_stagingDir}});
if (m_useiOSAutomaticProvisioningUpdates)
- builder.addRow(m_useiOSAutomaticProvisioningUpdates);
+ builder.addRow({m_useiOSAutomaticProvisioningUpdates});
builder.addRow({new QLabel(Tr::tr("Targets:")), frame});
if (!isCleanStep() && !m_buildPreset.isEmpty())
createAndAddEnvironmentWidgets(builder);
- auto widget = builder.emerge(Layouting::WithoutMargins);
+ builder.addItem(Layouting::noMargin);
+ auto widget = builder.emerge();
updateDetails();
connect(m_cmakeArguments, &StringAspect::changed, this, updateDetails);
connect(m_toolArguments, &StringAspect::changed, this, updateDetails);
+ connect(m_useStaging, &BoolAspect::changed, this, updateDetails);
+ connect(m_stagingDir, &StringAspect::changed, this, updateDetails);
if (m_useiOSAutomaticProvisioningUpdates)
connect(m_useiOSAutomaticProvisioningUpdates, &BoolAspect::changed, this, updateDetails);
@@ -683,8 +729,62 @@ QString CMakeBuildStep::baseEnvironmentText() const
return Tr::tr("System Environment");
}
+QString CMakeBuildStep::currentInstallPrefix() const
+{
+ auto bs = qobject_cast<CMakeBuildSystem *>(buildSystem());
+ QTC_ASSERT(bs, return {});
+ const CMakeConfig config = bs->configurationFromCMake();
+ return QString::fromUtf8(config.valueOf("CMAKE_INSTALL_PREFIX"));
+}
+
+QString CMakeBuildStep::currentStagingDir() const
+{
+ return m_stagingDir->filePath().path();
+}
+
+FilePath CMakeBuildStep::cmakeExecutable() const
+{
+ CMakeTool *tool = CMakeKitAspect::cmakeTool(kit());
+ return tool ? tool->cmakeExecutable() : FilePath();
+}
+
+void CMakeBuildStep::updateDeploymentData()
+{
+ if (!m_useStaging->value())
+ return;
+
+ QString install = currentInstallPrefix();
+ QString stagingDir = currentStagingDir();
+ FilePath rootDir = cmakeExecutable().withNewPath(stagingDir);
+ Q_UNUSED(install);
+
+ DeploymentData deploymentData;
+ deploymentData.setLocalInstallRoot(rootDir);
+
+ const int startPos = rootDir.path().length();
+
+ const auto appFileNames = transform<QSet<QString>>(buildSystem()->applicationTargets(),
+ [](const BuildTargetInfo &appTarget) { return appTarget.targetFilePath.fileName(); });
+
+ auto handleFile = [&appFileNames, startPos, &deploymentData](const FilePath &filePath) {
+ const DeployableFile::Type type = appFileNames.contains(filePath.fileName())
+ ? DeployableFile::TypeExecutable
+ : DeployableFile::TypeNormal;
+ const QString targetDir = filePath.parentDir().path().mid(startPos);
+ deploymentData.addFile(filePath, targetDir, type);
+ return IterationPolicy::Continue;
+ };
+
+ rootDir.iterateDirectory(handleFile,
+ {{}, QDir::Files | QDir::Hidden, QDirIterator::Subdirectories});
+
+ buildSystem()->setDeploymentData(deploymentData);
+}
+
void CMakeBuildStep::finish(ProcessResult result)
{
+ updateDeploymentData();
+
emit progress(100, {});
AbstractProcessStep::finish(result);
}
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.h b/src/plugins/cmakeprojectmanager/cmakebuildstep.h
index 96d53919e1..85ee46d953 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildstep.h
+++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.h
@@ -85,6 +85,10 @@ private:
void doRun() override;
QWidget *createConfigWidget() override;
+ Utils::FilePath cmakeExecutable() const;
+ QString currentInstallPrefix() const;
+ QString currentStagingDir() const;
+
QString defaultBuildTarget() const;
bool isCleanStep() const;
@@ -94,6 +98,7 @@ private:
void handleBuildTargetsChanges(bool success);
void recreateBuildTargetsModel();
void updateBuildTargetsModel();
+ void updateDeploymentData();
QMetaObject::Connection m_runTrigger;
@@ -102,6 +107,8 @@ private:
Utils::StringAspect *m_cmakeArguments = nullptr;
Utils::StringAspect *m_toolArguments = nullptr;
Utils::BoolAspect *m_useiOSAutomaticProvisioningUpdates = nullptr;
+ Utils::BoolAspect *m_useStaging = nullptr;
+ Utils::FilePathAspect *m_stagingDir = nullptr;
QString m_allTarget = "all";
QString m_installTarget = "install";
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
index 726414dafc..1bd19dffeb 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
@@ -17,6 +17,8 @@
#include <android/androidconstants.h>
#include <coreplugin/icore.h>
+#include <coreplugin/documentmanager.h>
+#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/messagemanager.h>
#include <coreplugin/progressmanager/progressmanager.h>
@@ -30,7 +32,11 @@
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
+#include <texteditor/texteditor.h>
+#include <texteditor/textdocument.h>
+
#include <qmljs/qmljsmodelmanagerinterface.h>
+#include <qmljstools/qmljstoolsconstants.h>
#include <qtsupport/qtcppkitinfo.h>
#include <qtsupport/qtkitinformation.h>
@@ -40,8 +46,8 @@
#include <utils/checkablemessagebox.h>
#include <utils/fileutils.h>
#include <utils/macroexpander.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QClipboard>
#include <QGuiApplication>
@@ -51,74 +57,11 @@
#include <QLoggingCategory>
using namespace ProjectExplorer;
+using namespace TextEditor;
using namespace Utils;
namespace CMakeProjectManager::Internal {
-static void copySourcePathsToClipboard(const FilePaths &srcPaths, const ProjectNode *node)
-{
- QClipboard *clip = QGuiApplication::clipboard();
-
- QString data = Utils::transform(srcPaths, [projDir = node->filePath()](const FilePath &path) {
- return path.relativePathFrom(projDir).cleanPath().toString();
- }).join(" ");
- clip->setText(data);
-}
-
-static void noAutoAdditionNotify(const FilePaths &filePaths, const ProjectNode *node)
-{
- const FilePaths srcPaths = Utils::filtered(filePaths, [](const FilePath &file) {
- const auto mimeType = Utils::mimeTypeForFile(file).name();
- return mimeType == CppEditor::Constants::C_SOURCE_MIMETYPE ||
- mimeType == CppEditor::Constants::C_HEADER_MIMETYPE ||
- mimeType == CppEditor::Constants::CPP_SOURCE_MIMETYPE ||
- mimeType == CppEditor::Constants::CPP_HEADER_MIMETYPE ||
- mimeType == ProjectExplorer::Constants::FORM_MIMETYPE ||
- mimeType == ProjectExplorer::Constants::RESOURCE_MIMETYPE ||
- mimeType == ProjectExplorer::Constants::SCXML_MIMETYPE;
- });
-
- if (!srcPaths.empty()) {
- auto settings = CMakeSpecificSettings::instance();
- switch (settings->afterAddFileSetting.value()) {
- case AskUser: {
- bool checkValue{false};
- QDialogButtonBox::StandardButton reply = CheckableMessageBox::question(
- Core::ICore::dialogParent(),
- Tr::tr("Copy to Clipboard?"),
- Tr::tr("Files are not automatically added to the "
- "CMakeLists.txt file of the CMake project."
- "\nCopy the path to the source files to the clipboard?"),
- "Remember My Choice",
- &checkValue,
- QDialogButtonBox::Yes | QDialogButtonBox::No,
- QDialogButtonBox::Yes);
- if (checkValue) {
- if (QDialogButtonBox::Yes == reply)
- settings->afterAddFileSetting.setValue(CopyFilePath);
- else if (QDialogButtonBox::No == reply)
- settings->afterAddFileSetting.setValue(NeverCopyFilePath);
-
- settings->writeSettings(Core::ICore::settings());
- }
-
- if (QDialogButtonBox::Yes == reply)
- copySourcePathsToClipboard(srcPaths, node);
-
- break;
- }
-
- case CopyFilePath: {
- copySourcePathsToClipboard(srcPaths, node);
- break;
- }
-
- case NeverCopyFilePath:
- break;
- }
- }
-}
-
static Q_LOGGING_CATEGORY(cmakeBuildSystemLog, "qtc.cmake.buildsystem", QtWarningMsg);
// --------------------------------------------------------------------
@@ -258,29 +201,443 @@ void CMakeBuildSystem::triggerParsing()
bool CMakeBuildSystem::supportsAction(Node *context, ProjectAction action, const Node *node) const
{
if (dynamic_cast<CMakeTargetNode *>(context))
- return action == ProjectAction::AddNewFile;
-
- if (dynamic_cast<CMakeListsNode *>(context))
- return action == ProjectAction::AddNewFile;
+ return action == ProjectAction::AddNewFile || action == ProjectAction::AddExistingFile
+ || action == ProjectAction::AddExistingDirectory || action == ProjectAction::Rename
+ || action == ProjectAction::RemoveFile;
return BuildSystem::supportsAction(context, action, node);
}
-bool CMakeBuildSystem::addFiles(Node *context, const FilePaths &filePaths, FilePaths *notAdded)
+static QString newFilesForFunction(const std::string &cmakeFunction,
+ const FilePaths &filePaths,
+ const FilePath &projDir)
{
- if (auto n = dynamic_cast<CMakeProjectNode *>(context)) {
- noAutoAdditionNotify(filePaths, n);
- return true; // Return always true as autoadd is not supported!
+ auto relativeFilePaths = [projDir](const FilePaths &filePaths) {
+ return Utils::transform(filePaths, [projDir](const FilePath &path) {
+ return path.canonicalPath().relativePathFrom(projDir).cleanPath().toString();
+ });
+ };
+
+ if (cmakeFunction == "qt_add_qml_module" || cmakeFunction == "qt6_add_qml_module") {
+ FilePaths sourceFiles;
+ FilePaths resourceFiles;
+ FilePaths qmlFiles;
+
+ for (const auto &file : filePaths) {
+ const auto mimeType = Utils::mimeTypeForFile(file);
+ if (mimeType.matchesName(CppEditor::Constants::CPP_SOURCE_MIMETYPE)
+ || mimeType.matchesName(CppEditor::Constants::CPP_HEADER_MIMETYPE)
+ || mimeType.matchesName(CppEditor::Constants::OBJECTIVE_C_SOURCE_MIMETYPE)
+ || mimeType.matchesName(CppEditor::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE)) {
+ sourceFiles << file;
+ } else if (mimeType.matchesName(QmlJSTools::Constants::QML_MIMETYPE)
+ || mimeType.matchesName(QmlJSTools::Constants::QMLUI_MIMETYPE)
+ || mimeType.matchesName(QmlJSTools::Constants::QMLPROJECT_MIMETYPE)
+ || mimeType.matchesName(QmlJSTools::Constants::JS_MIMETYPE)
+ || mimeType.matchesName(QmlJSTools::Constants::JSON_MIMETYPE)) {
+ qmlFiles << file;
+ } else {
+ resourceFiles << file;
+ }
+ }
+
+ QStringList result;
+ if (!sourceFiles.isEmpty())
+ result << QString("SOURCES %1").arg(relativeFilePaths(sourceFiles).join(" "));
+ if (!resourceFiles.isEmpty())
+ result << QString("RESOURCES %1").arg(relativeFilePaths(resourceFiles).join(" "));
+ if (!qmlFiles.isEmpty())
+ result << QString("QML_FILES %1").arg(relativeFilePaths(qmlFiles).join(" "));
+
+ return result.join("\n");
}
+ return relativeFilePaths(filePaths).join(" ");
+}
+
+bool CMakeBuildSystem::addFiles(Node *context, const FilePaths &filePaths, FilePaths *notAdded)
+{
if (auto n = dynamic_cast<CMakeTargetNode *>(context)) {
- noAutoAdditionNotify(filePaths, n);
- return true; // Return always true as autoadd is not supported!
+ const QString targetName = n->buildKey();
+ auto target = Utils::findOrDefault(buildTargets(),
+ [targetName](const CMakeBuildTarget &target) {
+ return target.title == targetName;
+ });
+
+ if (target.backtrace.isEmpty()) {
+ *notAdded = filePaths;
+ return false;
+ }
+ const FilePath targetCMakeFile = target.backtrace.last().path;
+ const int targetDefinitionLine = target.backtrace.last().line;
+
+ // Have a fresh look at the CMake file, not relying on a cached value
+ expected_str<QByteArray> fileContent = targetCMakeFile.fileContents();
+ cmListFile cmakeListFile;
+ std::string errorString;
+ if (fileContent) {
+ fileContent = fileContent->replace("\r\n", "\n");
+ if (!cmakeListFile.ParseString(fileContent->toStdString(),
+ targetCMakeFile.fileName().toStdString(),
+ errorString)) {
+ *notAdded = filePaths;
+ return false;
+ }
+ }
+
+ auto function = std::find_if(cmakeListFile.Functions.begin(),
+ cmakeListFile.Functions.end(),
+ [targetDefinitionLine](const auto &func) {
+ return func.Line() == targetDefinitionLine;
+ });
+
+ if (function == cmakeListFile.Functions.end()) {
+ *notAdded = filePaths;
+ return false;
+ }
+
+ // Special case: when qt_add_executable and qt_add_qml_module use the same target name
+ // then qt_add_qml_module function should be used
+ const std::string target_name = targetName.toStdString();
+ auto add_qml_module_func
+ = std::find_if(cmakeListFile.Functions.begin(),
+ cmakeListFile.Functions.end(),
+ [target_name](const auto &func) {
+ return (func.LowerCaseName() == "qt_add_qml_module"
+ || func.LowerCaseName() == "qt6_add_qml_module")
+ && func.Arguments().front().Value == target_name;
+ });
+ if (add_qml_module_func != cmakeListFile.Functions.end())
+ function = add_qml_module_func;
+
+ const QString newSourceFiles = newFilesForFunction(function->LowerCaseName(),
+ filePaths,
+ n->filePath().canonicalPath());
+
+ static QSet<std::string> knownFunctions{"add_executable",
+ "add_library",
+ "qt_add_executable",
+ "qt_add_library",
+ "qt6_add_executable",
+ "qt6_add_library",
+ "qt_add_qml_module",
+ "qt6_add_qml_module"};
+
+ int line = 0;
+ int column = 0;
+ int extraChars = 0;
+ QString snippet;
+
+ auto afterFunctionLastArgument =
+ [&line, &column, &snippet, &extraChars, newSourceFiles](const auto &f) {
+ auto lastArgument = f->Arguments().back();
+
+ line = lastArgument.Line;
+ column = lastArgument.Column + static_cast<int>(lastArgument.Value.size()) - 1;
+ snippet = QString("\n%1").arg(newSourceFiles);
+
+ // Take into consideration the quotes
+ if (lastArgument.Delim == cmListFileArgument::Quoted)
+ extraChars = 2;
+ };
+
+ if (knownFunctions.contains(function->LowerCaseName())) {
+ afterFunctionLastArgument(function);
+ } else {
+ auto targetSourcesFunc = std::find_if(cmakeListFile.Functions.begin(),
+ cmakeListFile.Functions.end(),
+ [target_name](const auto &func) {
+ return func.LowerCaseName()
+ == "target_sources"
+ && func.Arguments().front().Value
+ == target_name;
+ });
+
+ if (targetSourcesFunc == cmakeListFile.Functions.end()) {
+ line = function->LineEnd() + 1;
+ column = 0;
+ snippet = QString("\ntarget_sources(%1\n PRIVATE\n %2\n)\n")
+ .arg(targetName)
+ .arg(newSourceFiles);
+ } else {
+ afterFunctionLastArgument(targetSourcesFunc);
+ }
+ }
+
+ BaseTextEditor *editor = qobject_cast<BaseTextEditor *>(
+ Core::EditorManager::openEditorAt({targetCMakeFile, line, column + extraChars},
+ Constants::CMAKE_EDITOR_ID,
+ Core::EditorManager::DoNotMakeVisible));
+ if (!editor) {
+ *notAdded = filePaths;
+ return false;
+ }
+
+ editor->insert(snippet);
+ editor->editorWidget()->autoIndent();
+ if (!Core::DocumentManager::saveDocument(editor->document()))
+ return false;
+
+ return true;
}
return BuildSystem::addFiles(context, filePaths, notAdded);
}
+std::optional<CMakeBuildSystem::ProjectFileArgumentPosition>
+CMakeBuildSystem::projectFileArgumentPosition(const QString &targetName, const QString &fileName)
+{
+ auto target = Utils::findOrDefault(buildTargets(), [targetName](const CMakeBuildTarget &target) {
+ return target.title == targetName;
+ });
+
+ if (target.backtrace.isEmpty())
+ return std::nullopt;
+
+ const FilePath targetCMakeFile = target.backtrace.last().path;
+
+ // Have a fresh look at the CMake file, not relying on a cached value
+ expected_str<QByteArray> fileContent = targetCMakeFile.fileContents();
+ cmListFile cmakeListFile;
+ std::string errorString;
+ if (fileContent) {
+ fileContent = fileContent->replace("\r\n", "\n");
+ if (!cmakeListFile.ParseString(fileContent->toStdString(),
+ targetCMakeFile.fileName().toStdString(),
+ errorString))
+ return std::nullopt;
+ }
+
+ const int targetDefinitionLine = target.backtrace.last().line;
+
+ auto function = std::find_if(cmakeListFile.Functions.begin(),
+ cmakeListFile.Functions.end(),
+ [targetDefinitionLine](const auto &func) {
+ return func.Line() == targetDefinitionLine;
+ });
+
+ const std::string target_name = targetName.toStdString();
+ auto targetSourcesFunc = std::find_if(cmakeListFile.Functions.begin(),
+ cmakeListFile.Functions.end(),
+ [target_name](const auto &func) {
+ return func.LowerCaseName() == "target_sources"
+ && func.Arguments().size() > 1
+ && func.Arguments().front().Value
+ == target_name;
+ });
+ auto addQmlModuleFunc = std::find_if(cmakeListFile.Functions.begin(),
+ cmakeListFile.Functions.end(),
+ [target_name](const auto &func) {
+ return (func.LowerCaseName() == "qt_add_qml_module"
+ || func.LowerCaseName() == "qt6_add_qml_module")
+ && func.Arguments().size() > 1
+ && func.Arguments().front().Value
+ == target_name;
+ });
+
+ for (const auto &func : {function, targetSourcesFunc, addQmlModuleFunc}) {
+ if (func == cmakeListFile.Functions.end())
+ continue;
+ auto filePathArgument = Utils::findOrDefault(func->Arguments(),
+ [file_name = fileName.toStdString()](
+ const auto &arg) {
+ return arg.Value == file_name;
+ });
+
+ if (!filePathArgument.Value.empty()) {
+ return ProjectFileArgumentPosition{filePathArgument, targetCMakeFile, fileName};
+ } else {
+ // Check if the filename is part of globbing variable result
+ const auto globFunctions = std::get<0>(
+ Utils::partition(cmakeListFile.Functions, [](const auto &f) {
+ return f.LowerCaseName() == "file" && f.Arguments().size() > 2
+ && (f.Arguments().front().Value == "GLOB"
+ || f.Arguments().front().Value == "GLOB_RECURSE");
+ }));
+
+ const auto globVariables = Utils::transform<QSet>(globFunctions, [](const auto &func) {
+ return std::string("${") + func.Arguments()[1].Value + "}";
+ });
+
+ const auto haveGlobbing = Utils::anyOf(func->Arguments(),
+ [globVariables](const auto &arg) {
+ return globVariables.contains(arg.Value);
+ });
+
+ if (haveGlobbing) {
+ return ProjectFileArgumentPosition{filePathArgument,
+ targetCMakeFile,
+ fileName,
+ true};
+ }
+
+ // Check if the filename is part of a variable set by the user
+ const auto setFunctions = std::get<0>(
+ Utils::partition(cmakeListFile.Functions, [](const auto &f) {
+ return f.LowerCaseName() == "set" && f.Arguments().size() > 1;
+ }));
+
+ for (const auto &arg : func->Arguments()) {
+ auto matchedFunctions = Utils::filtered(setFunctions, [arg](const auto &f) {
+ return arg.Value == std::string("${") + f.Arguments()[0].Value + "}";
+ });
+
+ for (const auto &f : matchedFunctions) {
+ filePathArgument = Utils::findOrDefault(f.Arguments(),
+ [file_name = fileName.toStdString()](
+ const auto &arg) {
+ return arg.Value == file_name;
+ });
+
+ if (!filePathArgument.Value.empty()) {
+ return ProjectFileArgumentPosition{filePathArgument,
+ targetCMakeFile,
+ fileName};
+ }
+ }
+ }
+ }
+ }
+
+ return std::nullopt;
+}
+
+RemovedFilesFromProject CMakeBuildSystem::removeFiles(Node *context,
+ const FilePaths &filePaths,
+ FilePaths *notRemoved)
+{
+ FilePaths badFiles;
+ if (auto n = dynamic_cast<CMakeTargetNode *>(context)) {
+ const FilePath projDir = n->filePath().canonicalPath();
+ const QString targetName = n->buildKey();
+
+ for (const auto &file : filePaths) {
+ const QString fileName
+ = file.canonicalPath().relativePathFrom(projDir).cleanPath().toString();
+
+ auto filePos = projectFileArgumentPosition(targetName, fileName);
+ if (filePos) {
+ if (!filePos.value().cmakeFile.exists()) {
+ badFiles << file;
+ continue;
+ }
+
+ BaseTextEditor *editor = qobject_cast<BaseTextEditor *>(
+ Core::EditorManager::openEditorAt({filePos.value().cmakeFile,
+ static_cast<int>(filePos.value().argumentPosition.Line),
+ static_cast<int>(filePos.value().argumentPosition.Column
+ - 1)},
+ Constants::CMAKE_EDITOR_ID,
+ Core::EditorManager::DoNotMakeVisible));
+ if (!editor) {
+ badFiles << file;
+ continue;
+ }
+
+ // If quotes were used for the source file, remove the quotes too
+ int extraChars = 0;
+ if (filePos->argumentPosition.Delim == cmListFileArgument::Quoted)
+ extraChars = 2;
+
+ if (!filePos.value().fromGlobbing)
+ editor->replace(filePos.value().relativeFileName.length() + extraChars, "");
+
+ editor->editorWidget()->autoIndent();
+ if (!Core::DocumentManager::saveDocument(editor->document())) {
+ badFiles << file;
+ continue;
+ }
+ } else {
+ badFiles << file;
+ }
+ }
+
+ if (notRemoved && !badFiles.isEmpty())
+ *notRemoved = badFiles;
+
+ return badFiles.isEmpty() ? RemovedFilesFromProject::Ok : RemovedFilesFromProject::Error;
+ }
+
+ return RemovedFilesFromProject::Error;
+}
+
+bool CMakeBuildSystem::canRenameFile(Node *context,
+ const FilePath &oldFilePath,
+ const FilePath &newFilePath)
+{
+ // "canRenameFile" will cause an actual rename after the function call.
+ // This will make the a sequence like
+ // canonicalPath().relativePathFrom(projDir).cleanPath().toString()
+ // to fail if the file doesn't exist on disk
+ // therefore cache the results for the subsequent "renameFile" call
+ // where oldFilePath has already been renamed as newFilePath.
+
+ if (auto n = dynamic_cast<CMakeTargetNode *>(context)) {
+ const FilePath projDir = n->filePath().canonicalPath();
+ const QString oldRelPathName
+ = oldFilePath.canonicalPath().relativePathFrom(projDir).cleanPath().toString();
+
+ const QString targetName = n->buildKey();
+
+ const QString key
+ = QStringList{projDir.path(), targetName, oldFilePath.path(), newFilePath.path()}
+ .join(";");
+
+ auto filePos = projectFileArgumentPosition(targetName, oldRelPathName);
+ if (!filePos)
+ return false;
+
+ m_filesToBeRenamed.insert(key, filePos.value());
+ return true;
+ }
+ return false;
+}
+
+bool CMakeBuildSystem::renameFile(Node *context,
+ const FilePath &oldFilePath,
+ const FilePath &newFilePath)
+{
+ if (auto n = dynamic_cast<CMakeTargetNode *>(context)) {
+ const FilePath projDir = n->filePath().canonicalPath();
+ const QString newRelPathName
+ = newFilePath.canonicalPath().relativePathFrom(projDir).cleanPath().toString();
+
+ const QString targetName = n->buildKey();
+ const QString key
+ = QStringList{projDir.path(), targetName, oldFilePath.path(), newFilePath.path()}.join(
+ ";");
+
+ auto fileToRename = m_filesToBeRenamed.take(key);
+ if (!fileToRename.cmakeFile.exists())
+ return false;
+
+ BaseTextEditor *editor = qobject_cast<BaseTextEditor *>(
+ Core::EditorManager::openEditorAt({fileToRename.cmakeFile,
+ static_cast<int>(fileToRename.argumentPosition.Line),
+ static_cast<int>(fileToRename.argumentPosition.Column
+ - 1)},
+ Constants::CMAKE_EDITOR_ID,
+ Core::EditorManager::DoNotMakeVisible));
+ if (!editor)
+ return false;
+
+ // If quotes were used for the source file, skip the starting quote
+ if (fileToRename.argumentPosition.Delim == cmListFileArgument::Quoted)
+ editor->setCursorPosition(editor->position() + 1);
+
+ if (!fileToRename.fromGlobbing)
+ editor->replace(fileToRename.relativeFileName.length(), newRelPathName);
+
+ editor->editorWidget()->autoIndent();
+ if (!Core::DocumentManager::saveDocument(editor->document()))
+ return false;
+
+ return true;
+ }
+
+ return false;
+}
+
FilePaths CMakeBuildSystem::filesGeneratedFrom(const FilePath &sourceFile) const
{
FilePath project = projectDirectory();
@@ -522,13 +879,22 @@ void CMakeBuildSystem::checkAndReportError(QString &errorMessage)
}
}
+static QSet<FilePath> projectFilesToWatch(const QSet<CMakeFileInfo> &cmakeFiles)
+{
+ return Utils::transform(Utils::filtered(cmakeFiles,
+ [](const CMakeFileInfo &info) {
+ return !info.isGenerated;
+ }),
+ [](const CMakeFileInfo &info) { return info.path; });
+}
+
void CMakeBuildSystem::updateProjectData()
{
qCDebug(cmakeBuildSystemLog) << "Updating CMake project data";
QTC_ASSERT(m_treeScanner.isFinished() && !m_reader.isParsing(), return );
- buildConfiguration()->project()->setExtraProjectFiles(m_reader.projectFilesToWatch());
+ buildConfiguration()->project()->setExtraProjectFiles(projectFilesToWatch(m_cmakeFiles));
CMakeConfig patchedConfig = configurationFromCMake();
{
@@ -601,7 +967,7 @@ void CMakeBuildSystem::updateProjectData()
for (RawProjectPart &rpp : rpps) {
rpp.setQtVersion(
kitInfo.projectPartQtVersion); // TODO: Check if project actually uses Qt.
- const QString includeFileBaseDir = buildConfiguration()->buildDirectory().toString();
+ const FilePath includeFileBaseDir = buildConfiguration()->buildDirectory();
QStringList cxxFlags = rpp.flagsForCxx.commandLineFlags;
QStringList cFlags = rpp.flagsForC.commandLineFlags;
addTargetFlagForIos(cxxFlags, cFlags, this, [this] {
@@ -735,6 +1101,8 @@ void CMakeBuildSystem::handleParsingSucceeded(bool restoredFromBackup)
return result;
});
m_buildTargets += m_reader.takeBuildTargets(errorMessage);
+ m_cmakeFiles = m_reader.takeCMakeFileInfos(errorMessage);
+
checkAndReportError(errorMessage);
}
@@ -747,7 +1115,11 @@ void CMakeBuildSystem::handleParsingSucceeded(bool restoredFromBackup)
m_ctestPath = tool->cmakeExecutable().withNewPath(m_reader.ctestPath());
setApplicationTargets(appTargets());
- setDeploymentData(deploymentData());
+
+ // Note: This is practically always wrong and resulting in an empty view.
+ // Setting the real data is triggered from a successful run of a
+ // MakeInstallStep.
+ setDeploymentData(deploymentDataFromFile());
QTC_ASSERT(m_waitingForParse, return );
m_waitingForParse = false;
@@ -901,11 +1273,11 @@ void CMakeBuildSystem::runCTest()
QTC_ASSERT(parameters.isValid(), return);
ensureBuildDirectory(parameters);
- m_ctestProcess.reset(new QtcProcess);
+ m_ctestProcess.reset(new Process);
m_ctestProcess->setEnvironment(buildConfiguration()->environment());
m_ctestProcess->setWorkingDirectory(parameters.buildDirectory);
m_ctestProcess->setCommand({m_ctestPath, { "-N", "--show-only=json-v1"}});
- connect(m_ctestProcess.get(), &QtcProcess::done, this, [this] {
+ connect(m_ctestProcess.get(), &Process::done, this, [this] {
if (m_ctestProcess->result() == ProcessResult::FinishedWithSuccess) {
const QJsonDocument json
= QJsonDocument::fromJson(m_ctestProcess->readAllRawStandardOutput());
@@ -1064,7 +1436,7 @@ CommandLine CMakeBuildSystem::commandLineForTests(const QList<QString> &tests,
return {m_ctestPath, args};
}
-DeploymentData CMakeBuildSystem::deploymentData() const
+DeploymentData CMakeBuildSystem::deploymentDataFromFile() const
{
DeploymentData result;
@@ -1226,7 +1598,6 @@ void CMakeBuildSystem::updateInitialCMakeExpandableVars()
"CMAKE_CXX_COMPILER",
"QT_QMAKE_EXECUTABLE",
"QT_HOST_PATH",
- "CMAKE_PROJECT_INCLUDE_BEFORE",
"CMAKE_TOOLCHAIN_FILE"
};
for (const auto &var : singlePathList) {
@@ -1299,7 +1670,7 @@ MakeInstallCommand CMakeBuildSystem::makeInstallCommand(const FilePath &installR
buildDirectory = bc->buildDirectory();
cmd.command.addArg("--build");
- cmd.command.addArg(buildDirectory.onDevice(cmd.command.executable()).path());
+ cmd.command.addArg(buildDirectory.path());
cmd.command.addArg("--target");
cmd.command.addArg(installTarget);
@@ -1341,20 +1712,20 @@ void CMakeBuildSystem::runGenerator(Id id)
const CMakeTool * const cmakeTool
= CMakeKitAspect::cmakeTool(buildConfiguration()->target()->kit());
if (!cmakeTool) {
- showError(Tr::tr("Kit does not have a cmake binary set"));
+ showError(Tr::tr("Kit does not have a cmake binary set."));
return;
}
const QString generator = id.toSetting().toString();
const FilePath outDir = buildConfiguration()->buildDirectory()
/ ("qtc_" + FileUtils::fileSystemFriendlyName(generator));
if (!outDir.ensureWritableDir()) {
- showError(Tr::tr("Cannot create output directory \"%1\"").arg(outDir.toString()));
+ showError(Tr::tr("Cannot create output directory \"%1\".").arg(outDir.toString()));
return;
}
CommandLine cmdLine(cmakeTool->cmakeExecutable(), {"-S", buildConfiguration()->target()
->project()->projectDirectory().toUserOutput(), "-G", generator});
if (!cmdLine.executable().isExecutableFile()) {
- showError(Tr::tr("No valid cmake executable"));
+ showError(Tr::tr("No valid cmake executable."));
return;
}
const auto itemFilter = [](const CMakeConfigItem &item) {
@@ -1380,19 +1751,19 @@ void CMakeBuildSystem::runGenerator(Id id)
optionsAspect && !optionsAspect->value().isEmpty()) {
cmdLine.addArgs(optionsAspect->value(), CommandLine::Raw);
}
- const auto proc = new QtcProcess(this);
- connect(proc, &QtcProcess::done, proc, &QtcProcess::deleteLater);
- connect(proc, &QtcProcess::readyReadStandardOutput, this, [proc] {
+ const auto proc = new Process(this);
+ connect(proc, &Process::done, proc, &Process::deleteLater);
+ connect(proc, &Process::readyReadStandardOutput, this, [proc] {
Core::MessageManager::writeFlashing(QString::fromLocal8Bit(proc->readAllRawStandardOutput()));
});
- connect(proc, &QtcProcess::readyReadStandardError, this, [proc] {
+ connect(proc, &Process::readyReadStandardError, this, [proc] {
Core::MessageManager::writeDisrupting(QString::fromLocal8Bit(proc->readAllRawStandardError()));
});
proc->setWorkingDirectory(outDir);
proc->setEnvironment(buildConfiguration()->environment());
proc->setCommand(cmdLine);
- Core::MessageManager::writeFlashing(Tr::tr("Running in %1: %2")
- .arg(outDir.toUserOutput(), cmdLine.toUserOutput()));
+ Core::MessageManager::writeFlashing(
+ Tr::tr("Running in %1: %2.").arg(outDir.toUserOutput(), cmdLine.toUserOutput()));
proc->start();
}
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h
index 802921f04b..4770b4d84b 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h
+++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h
@@ -18,7 +18,7 @@ namespace ProjectExplorer {
class ExtraCompiler;
class FolderNode;
}
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace CMakeProjectManager {
@@ -48,6 +48,18 @@ public:
bool addFiles(ProjectExplorer::Node *context,
const Utils::FilePaths &filePaths, Utils::FilePaths *) final;
+ ProjectExplorer::RemovedFilesFromProject removeFiles(ProjectExplorer::Node *context,
+ const Utils::FilePaths &filePaths,
+ Utils::FilePaths *notRemoved
+ = nullptr) final;
+
+ bool canRenameFile(ProjectExplorer::Node *context,
+ const Utils::FilePath &oldFilePath,
+ const Utils::FilePath &newFilePath) final;
+ bool renameFile(ProjectExplorer::Node *context,
+ const Utils::FilePath &oldFilePath,
+ const Utils::FilePath &newFilePath) final;
+
Utils::FilePaths filesGeneratedFrom(const Utils::FilePath &sourceFile) const final;
QString name() const final { return QLatin1String("cmake"); }
@@ -67,7 +79,7 @@ public:
const QList<ProjectExplorer::BuildTargetInfo> appTargets() const;
QStringList buildTargetTitles() const;
const QList<CMakeBuildTarget> &buildTargets() const;
- ProjectExplorer::DeploymentData deploymentData() const;
+ ProjectExplorer::DeploymentData deploymentDataFromFile() const;
CMakeBuildConfiguration *cmakeBuildConfiguration() const;
@@ -185,6 +197,16 @@ private:
void runCTest();
+ struct ProjectFileArgumentPosition
+ {
+ cmListFileArgument argumentPosition;
+ Utils::FilePath cmakeFile;
+ QString relativeFileName;
+ bool fromGlobbing = false;
+ };
+ std::optional<ProjectFileArgumentPosition> projectFileArgumentPosition(
+ const QString &targetName, const QString &fileName);
+
ProjectExplorer::TreeScanner m_treeScanner;
std::shared_ptr<ProjectExplorer::FolderNode> m_allFiles;
QHash<QString, bool> m_mimeBinaryCache;
@@ -199,6 +221,9 @@ private:
CppEditor::CppProjectUpdater *m_cppCodeModelUpdater = nullptr;
QList<ProjectExplorer::ExtraCompiler *> m_extraCompilers;
QList<CMakeBuildTarget> m_buildTargets;
+ QSet<CMakeFileInfo> m_cmakeFiles;
+
+ QHash<QString, ProjectFileArgumentPosition> m_filesToBeRenamed;
// Parsing state:
BuildDirParameters m_parameters;
@@ -208,7 +233,7 @@ private:
// CTest integration
Utils::FilePath m_ctestPath;
- std::unique_ptr<Utils::QtcProcess> m_ctestProcess;
+ std::unique_ptr<Utils::Process> m_ctestProcess;
QList<ProjectExplorer::TestCaseInfo> m_testNames;
CMakeConfig m_configurationFromCMake;
diff --git a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp
index 9f507e1480..5193661e57 100644
--- a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp
@@ -147,18 +147,17 @@ void CMakeEditorWidget::findLinkAt(const QTextCursor &cursor,
int line = 0;
int column = 0;
convertPosition(cursor.position(), &line, &column);
- const int positionInBlock = column - 1;
const QString block = cursor.block().text();
// check if the current position is commented out
const int hashPos = block.indexOf(QLatin1Char('#'));
- if (hashPos >= 0 && hashPos < positionInBlock)
+ if (hashPos >= 0 && hashPos < column)
return processLinkCallback(link);
// find the beginning of a filename
QString buffer;
- int beginPos = positionInBlock - 1;
+ int beginPos = column;
while (beginPos >= 0) {
if (isValidFileNameChar(block, beginPos)) {
buffer.prepend(block.at(beginPos));
@@ -169,7 +168,7 @@ void CMakeEditorWidget::findLinkAt(const QTextCursor &cursor,
}
// find the end of a filename
- int endPos = positionInBlock;
+ int endPos = column;
while (endPos < block.count()) {
if (isValidFileNameChar(block, endPos)) {
buffer.append(block.at(endPos));
@@ -199,8 +198,8 @@ void CMakeEditorWidget::findLinkAt(const QTextCursor &cursor,
return processLinkCallback(link);
}
link.targetFilePath = Utils::FilePath::fromString(fileName);
- link.linkTextStart = cursor.position() - positionInBlock + beginPos + 1;
- link.linkTextEnd = cursor.position() - positionInBlock + endPos;
+ link.linkTextStart = cursor.position() - column + beginPos + 1;
+ link.linkTextEnd = cursor.position() - column + endPos;
}
processLinkCallback(link);
}
diff --git a/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp b/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp
index 325a6012a5..01fbeaa232 100644
--- a/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp
@@ -8,8 +8,9 @@
#include "cmaketool.h"
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
+
#include <texteditor/codeassist/assistinterface.h>
#include <QFileInfo>
@@ -39,7 +40,7 @@ IAssistProposal *CMakeFileCompletionAssist::performAsync()
Keywords kw;
const Utils::FilePath &filePath = interface()->filePath();
if (!filePath.isEmpty() && filePath.toFileInfo().isFile()) {
- Project *p = SessionManager::projectForFile(filePath);
+ Project *p = ProjectManager::projectForFile(filePath);
if (p && p->activeTarget()) {
CMakeTool *cmake = CMakeKitAspect::cmakeTool(p->activeTarget()->kit());
if (cmake && cmake->isValid())
diff --git a/src/plugins/cmakeprojectmanager/cmakeformatter.cpp b/src/plugins/cmakeprojectmanager/cmakeformatter.cpp
index d9aa8b2c84..dca6d47e20 100644
--- a/src/plugins/cmakeprojectmanager/cmakeformatter.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeformatter.cpp
@@ -4,7 +4,6 @@
#include "cmakeformatter.h"
-#include "cmakeformattersettings.h"
#include "cmakeprojectconstants.h"
#include "cmakeprojectmanagertr.h"
@@ -12,6 +11,7 @@
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/coreconstants.h>
+#include <coreplugin/dialogs/ioptionspage.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/idocument.h>
@@ -20,54 +20,183 @@
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
+#include <texteditor/command.h>
#include <texteditor/formattexteditor.h>
#include <texteditor/texteditor.h>
-#include <QAction>
-#include <QVersionNumber>
+#include <utils/algorithm.h>
+#include <utils/genericconstants.h>
+#include <utils/layoutbuilder.h>
+#include <utils/mimeutils.h>
+#include <QMenu>
+
+using namespace Core;
using namespace TextEditor;
+using namespace Utils;
-namespace CMakeProjectManager {
-namespace Internal {
+namespace CMakeProjectManager::Internal {
-void CMakeFormatter::updateActions(Core::IEditor *editor)
+class CMakeFormatterPrivate : public PagedSettings
{
- const bool enabled = editor && CMakeFormatterSettings::instance()->isApplicable(editor->document());
- m_formatFile->setEnabled(enabled);
-}
-
-void CMakeFormatter::formatFile()
+public:
+ CMakeFormatterPrivate()
+ {
+ setSettingsGroups(Constants::CMAKEFORMATTER_SETTINGS_GROUP,
+ Constants::CMAKEFORMATTER_GENERAL_GROUP);
+
+ setId(Constants::Settings::FORMATTER_ID);
+ setDisplayName(Tr::tr("Formatter"));
+ setDisplayCategory("CMake");
+ setCategory(Constants::Settings::CATEGORY);
+
+ command.setSettingsKey("autoFormatCommand");
+ command.setDefaultValue("cmake-format");
+ command.setExpectedKind(PathChooser::ExistingCommand);
+
+ autoFormatOnSave.setSettingsKey("autoFormatOnSave");
+ autoFormatOnSave.setLabelText(Tr::tr("Enable auto format on file save"));
+
+ autoFormatOnlyCurrentProject.setSettingsKey("autoFormatOnlyCurrentProject");
+ autoFormatOnlyCurrentProject.setDefaultValue(true);
+ autoFormatOnlyCurrentProject.setLabelText(Tr::tr("Restrict to files contained in the current project"));
+
+ autoFormatMime.setSettingsKey("autoFormatMime");
+ autoFormatMime.setDefaultValue("text/x-cmake");
+ autoFormatMime.setLabelText(Tr::tr("Restrict to MIME types:"));
+
+ setLayouter([this] {
+ using namespace Layouting;
+ return Column {
+ Row { Tr::tr("CMakeFormat command:"), command },
+ Space(10),
+ Group {
+ title(Tr::tr("Automatic Formatting on File Save")),
+ autoFormatOnSave.groupChecker(),
+ Form {
+ autoFormatMime, br,
+ Span(2, autoFormatOnlyCurrentProject)
+ }
+ },
+ st
+ };
+ });
+
+ ActionContainer *menu = ActionManager::createMenu(Constants::CMAKEFORMATTER_MENU_ID);
+ menu->menu()->setTitle(Tr::tr("CMakeFormatter"));
+ menu->setOnAllDisabledBehavior(ActionContainer::Show);
+ ActionManager::actionContainer(Core::Constants::M_TOOLS)->addMenu(menu);
+
+ Core::Command *cmd = ActionManager::registerAction(&formatFile, Constants::CMAKEFORMATTER_ACTION_ID);
+ connect(&formatFile, &QAction::triggered, this, [this] {
+ TextEditor::formatCurrentFile(formatCommand());
+ });
+
+ ActionManager::actionContainer(Constants::CMAKEFORMATTER_MENU_ID)->addAction(cmd);
+
+ auto updateActions = [this] {
+ auto editor = EditorManager::currentEditor();
+ formatFile.setEnabled(editor && isApplicable(editor->document()));
+ };
+
+ connect(&autoFormatMime, &Utils::StringAspect::changed,
+ this, updateActions);
+ connect(EditorManager::instance(), &EditorManager::currentEditorChanged,
+ this, updateActions);
+ connect(EditorManager::instance(), &EditorManager::aboutToSave,
+ this, &CMakeFormatterPrivate::applyIfNecessary);
+
+ readSettings();
+ }
+
+ bool isApplicable(const IDocument *document) const;
+
+ void applyIfNecessary(IDocument *document) const;
+
+ TextEditor::Command formatCommand() const
+ {
+ TextEditor::Command cmd;
+ cmd.setExecutable(command());
+ cmd.setProcessing(TextEditor::Command::FileProcessing);
+ cmd.addOption("--in-place");
+ cmd.addOption("%file");
+ return cmd;
+ }
+
+ FilePathAspect command{this};
+ BoolAspect autoFormatOnSave{this};
+ BoolAspect autoFormatOnlyCurrentProject{this};
+ StringAspect autoFormatMime{this};
+
+ QAction formatFile{Tr::tr("Format &Current File")};
+};
+
+bool CMakeFormatterPrivate::isApplicable(const IDocument *document) const
{
- formatCurrentFile(command());
+ if (!document)
+ return false;
+
+ if (autoFormatMime.value().isEmpty())
+ return true;
+
+ const QStringList allowedMimeTypes = autoFormatMime.value().split(';');
+ const MimeType documentMimeType = Utils::mimeTypeForName(document->mimeType());
+
+ return anyOf(allowedMimeTypes, [&documentMimeType](const QString &mime) {
+ return documentMimeType.inherits(mime);
+ });
}
-Command CMakeFormatter::command() const
+void CMakeFormatterPrivate::applyIfNecessary(IDocument *document) const
{
- Command command;
- command.setExecutable(CMakeFormatterSettings::instance()->command());
- command.setProcessing(Command::FileProcessing);
- command.addOption("--in-place");
- command.addOption("%file");
- return command;
+ if (!autoFormatOnSave.value())
+ return;
+
+ if (!document)
+ return;
+
+ if (!isApplicable(document))
+ return;
+
+ // Check if file is contained in the current project (if wished)
+ if (autoFormatOnlyCurrentProject.value()) {
+ const ProjectExplorer::Project *pro = ProjectExplorer::ProjectTree::currentProject();
+ if (!pro || pro->files([document](const ProjectExplorer::Node *n) {
+ return ProjectExplorer::Project::SourceFiles(n)
+ && n->filePath() == document->filePath();
+ }).isEmpty()) {
+ return;
+ }
+ }
+
+ TextEditor::Command command = formatCommand();
+ if (!command.isValid())
+ return;
+
+ const QList<IEditor *> editors = DocumentModel::editorsForDocument(document);
+ if (editors.isEmpty())
+ return;
+
+ IEditor *currentEditor = EditorManager::currentEditor();
+ IEditor *editor = editors.contains(currentEditor) ? currentEditor : editors.first();
+ if (auto widget = TextEditorWidget::fromEditor(editor))
+ TextEditor::formatEditor(widget, command);
}
-bool CMakeFormatter::isApplicable(const Core::IDocument *document) const
+// CMakeFormatter
+
+CMakeFormatter::CMakeFormatter()
+ : d(new CMakeFormatterPrivate)
+{}
+
+CMakeFormatter::~CMakeFormatter()
{
- return CMakeFormatterSettings::instance()->isApplicable(document);
+ delete d;
}
-void CMakeFormatter::initialize()
+void CMakeFormatter::applyIfNecessary(IDocument *document) const
{
- m_formatFile = new QAction(Tr::tr("Format &Current File"), this);
- Core::Command *cmd = Core::ActionManager::registerAction(m_formatFile, Constants::CMAKEFORMATTER_ACTION_ID);
- connect(m_formatFile, &QAction::triggered, this, &CMakeFormatter::formatFile);
-
- Core::ActionManager::actionContainer(Constants::CMAKEFORMATTER_MENU_ID)->addAction(cmd);
-
- connect(CMakeFormatterSettings::instance(), &CMakeFormatterSettings::supportedMimeTypesChanged,
- [this] { updateActions(Core::EditorManager::currentEditor()); });
+ d->applyIfNecessary(document);
}
-} // namespace Internal
-} // namespace CMakeProjectManager
+} // CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmakeformatter.h b/src/plugins/cmakeprojectmanager/cmakeformatter.h
index f3fc3a28c5..9727c245a5 100644
--- a/src/plugins/cmakeprojectmanager/cmakeformatter.h
+++ b/src/plugins/cmakeprojectmanager/cmakeformatter.h
@@ -4,35 +4,20 @@
#pragma once
-#include <texteditor/command.h>
+namespace Core { class IDocument; }
-#include "cmakeformatteroptionspage.h"
+namespace CMakeProjectManager::Internal {
-namespace Core {
-class IDocument;
-class IEditor;
-}
-
-namespace CMakeProjectManager {
-namespace Internal {
-
-class CMakeFormatter : public QObject
+class CMakeFormatter
{
- Q_OBJECT
-
public:
- void updateActions(Core::IEditor *editor);
- TextEditor::Command command() const;
- bool isApplicable(const Core::IDocument *document) const;
+ CMakeFormatter();
+ ~CMakeFormatter();
- void initialize();
+ void applyIfNecessary(Core::IDocument *document) const;
private:
- void formatFile();
-
- QAction *m_formatFile = nullptr;
- CMakeFormatterOptionsPage m_page;
+ class CMakeFormatterPrivate *d = nullptr;
};
-} // namespace Internal
-} // namespace CMakeProjectManager
+} // CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmakeformatteroptionspage.cpp b/src/plugins/cmakeprojectmanager/cmakeformatteroptionspage.cpp
deleted file mode 100644
index a82e7be07a..0000000000
--- a/src/plugins/cmakeprojectmanager/cmakeformatteroptionspage.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright (C) 2016 Lorenz Haas
-// Copyright (C) 2022 Xavier BESSON
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "cmakeformatteroptionspage.h"
-
-#include "cmakeprojectconstants.h"
-#include "cmakeprojectmanagertr.h"
-#include "cmakeformattersettings.h"
-
-#include <utils/layoutbuilder.h>
-#include <utils/pathchooser.h>
-
-#include <QApplication>
-#include <QCheckBox>
-#include <QComboBox>
-#include <QLabel>
-#include <QLineEdit>
-
-namespace CMakeProjectManager::Internal {
-
-class CMakeFormatterOptionsPageWidget : public Core::IOptionsPageWidget
-{
-public:
- explicit CMakeFormatterOptionsPageWidget();
-
-private:
- void apply() final;
-
- Utils::PathChooser *m_command;
- QCheckBox *m_autoFormat;
- QLineEdit *m_autoFormatMime;
- QCheckBox *m_autoFormatOnlyCurrentProject;
-};
-
-CMakeFormatterOptionsPageWidget::CMakeFormatterOptionsPageWidget()
-{
- resize(817, 631);
-
- auto settings = CMakeFormatterSettings::instance();
-
- m_autoFormat = new QCheckBox(Tr::tr("Enable auto format on file save"));
- m_autoFormat->setChecked(settings->autoFormatOnSave());
-
- auto mimeLabel = new QLabel(Tr::tr("Restrict to MIME types:"));
- mimeLabel->setEnabled(false);
-
- m_autoFormatMime = new QLineEdit(settings->autoFormatMimeAsString());
- m_autoFormatMime->setEnabled(m_autoFormat->isChecked());
-
- m_autoFormatOnlyCurrentProject =
- new QCheckBox(Tr::tr("Restrict to files contained in the current project"));
- m_autoFormatOnlyCurrentProject->setEnabled(m_autoFormat->isChecked());
- m_autoFormatOnlyCurrentProject->setChecked(settings->autoFormatOnlyCurrentProject());
-
- m_command = new Utils::PathChooser;
- m_command->setExpectedKind(Utils::PathChooser::ExistingCommand);
- m_command->setCommandVersionArguments({"--version"});
- m_command->setPromptDialogTitle(Tr::tr("%1 Command").arg(Tr::tr("Formatter")));
- m_command->setFilePath(settings->command());
-
- using namespace Utils::Layouting;
-
- Column {
- Group {
- title(Tr::tr("Automatic Formatting on File Save")),
- Form {
- Tr::tr("CMakeFormat command:"), m_command, br,
- Span(2, m_autoFormat), br,
- mimeLabel, m_autoFormatMime, br,
- Span(2, m_autoFormatOnlyCurrentProject)
- }
- },
- st
- }.attachTo(this);
-
- connect(m_autoFormat, &QCheckBox::toggled, m_autoFormatMime, &QLineEdit::setEnabled);
- connect(m_autoFormat, &QCheckBox::toggled, mimeLabel, &QLabel::setEnabled);
- connect(m_autoFormat, &QCheckBox::toggled, m_autoFormatOnlyCurrentProject, &QCheckBox::setEnabled);
-}
-
-void CMakeFormatterOptionsPageWidget::apply()
-{
- auto settings = CMakeFormatterSettings::instance();
- settings->setCommand(m_command->filePath().toString());
- settings->setAutoFormatOnSave(m_autoFormat->isChecked());
- settings->setAutoFormatMime(m_autoFormatMime->text());
- settings->setAutoFormatOnlyCurrentProject(m_autoFormatOnlyCurrentProject->isChecked());
- settings->save();
-}
-
-CMakeFormatterOptionsPage::CMakeFormatterOptionsPage()
-{
- setId(Constants::Settings::FORMATTER_ID);
- setDisplayName(Tr::tr("Formatter"));
- setDisplayCategory("CMake");
- setCategory(Constants::Settings::CATEGORY);
- setWidgetCreator([] { return new CMakeFormatterOptionsPageWidget; });
-}
-
-} // CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmakeformatteroptionspage.h b/src/plugins/cmakeprojectmanager/cmakeformatteroptionspage.h
deleted file mode 100644
index 08cfac3590..0000000000
--- a/src/plugins/cmakeprojectmanager/cmakeformatteroptionspage.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (C) 2016 Lorenz Haas
-// Copyright (C) 2022 Xavier BESSON
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <coreplugin/dialogs/ioptionspage.h>
-
-namespace CMakeProjectManager::Internal {
-
-class CMakeFormatterOptionsPage final : public Core::IOptionsPage
-{
-public:
- explicit CMakeFormatterOptionsPage();
-};
-
-} // CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmakeformattersettings.cpp b/src/plugins/cmakeprojectmanager/cmakeformattersettings.cpp
deleted file mode 100644
index 4bb2218340..0000000000
--- a/src/plugins/cmakeprojectmanager/cmakeformattersettings.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (C) 2016 Lorenz Haas
-// Copyright (C) 2022 Xavier BESSON
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "cmakeformattersettings.h"
-#include "cmakeprojectconstants.h"
-
-#include <coreplugin/icore.h>
-#include <coreplugin/idocument.h>
-#include <utils/algorithm.h>
-#include <utils/genericconstants.h>
-#include <utils/mimeutils.h>
-#include <utils/qtcprocess.h>
-
-namespace CMakeProjectManager {
-namespace Internal {
-
-namespace {
-const char AUTO_FORMAT_COMMAND[] = "autoFormatCommand";
-const char AUTO_FORMAT_MIME[] = "autoFormatMime";
-const char AUTO_FORMAT_ONLY_CURRENT_PROJECT[] = "autoFormatOnlyCurrentProject";
-const char AUTO_FORMAT_ON_SAVE[] = "autoFormatOnSave";
-}
-
-CMakeFormatterSettings::CMakeFormatterSettings(QObject* parent)
- : QObject(parent)
-{
- read();
-}
-
-CMakeFormatterSettings *CMakeFormatterSettings::instance()
-{
- static CMakeFormatterSettings m_instance;
- return &m_instance;
-}
-
-void CMakeFormatterSettings::read()
-{
- QSettings *s = Core::ICore::settings();
- s->beginGroup(Constants::CMAKEFORMATTER_SETTINGS_GROUP);
- s->beginGroup(Constants::CMAKEFORMATTER_GENERAL_GROUP);
- setCommand(s->value(AUTO_FORMAT_COMMAND, QString("cmake-format")).toString());
- m_autoFormatOnSave = s->value(AUTO_FORMAT_ON_SAVE, false).toBool();
- setAutoFormatMime(s->value(AUTO_FORMAT_MIME, QString("text/x-cmake")).toString());
- m_autoFormatOnlyCurrentProject = s->value(AUTO_FORMAT_ONLY_CURRENT_PROJECT, true).toBool();
- s->endGroup();
- s->endGroup();
-}
-
-void CMakeFormatterSettings::save()
-{
- QSettings *s = Core::ICore::settings();
- s->beginGroup(Constants::CMAKEFORMATTER_SETTINGS_GROUP);
- s->beginGroup(Constants::CMAKEFORMATTER_GENERAL_GROUP);
- Utils::QtcSettings::setValueWithDefault(s, AUTO_FORMAT_COMMAND, m_command, QString("cmake-format"));
- Utils::QtcSettings::setValueWithDefault(s, AUTO_FORMAT_ON_SAVE, m_autoFormatOnSave, false);
- Utils::QtcSettings::setValueWithDefault(s, AUTO_FORMAT_MIME, autoFormatMimeAsString(), QString("text/x-cmake"));
- Utils::QtcSettings::setValueWithDefault(s, AUTO_FORMAT_ONLY_CURRENT_PROJECT, m_autoFormatOnlyCurrentProject, true);
- s->endGroup();
- s->endGroup();
-}
-
-Utils::FilePath CMakeFormatterSettings::command() const
-{
- return Utils::FilePath::fromString(m_command);
-}
-
-void CMakeFormatterSettings::setCommand(const QString &cmd)
-{
- if (cmd == m_command)
- return;
-
- m_command = cmd;
-}
-
-bool CMakeFormatterSettings::autoFormatOnSave() const
-{
- return m_autoFormatOnSave;
-}
-
-void CMakeFormatterSettings::setAutoFormatOnSave(bool autoFormatOnSave)
-{
- m_autoFormatOnSave = autoFormatOnSave;
-}
-
-QStringList CMakeFormatterSettings::autoFormatMime() const
-{
- return m_autoFormatMime;
-}
-
-QString CMakeFormatterSettings::autoFormatMimeAsString() const
-{
- return m_autoFormatMime.join("; ");
-}
-
-void CMakeFormatterSettings::setAutoFormatMime(const QStringList &autoFormatMime)
-{
- if (m_autoFormatMime == autoFormatMime)
- return;
-
- m_autoFormatMime = autoFormatMime;
- emit supportedMimeTypesChanged();
-}
-
-void CMakeFormatterSettings::setAutoFormatMime(const QString &mimeList)
-{
- setAutoFormatMime(mimeList.split(';'));
-}
-
-bool CMakeFormatterSettings::autoFormatOnlyCurrentProject() const
-{
- return m_autoFormatOnlyCurrentProject;
-}
-
-void CMakeFormatterSettings::setAutoFormatOnlyCurrentProject(bool autoFormatOnlyCurrentProject)
-{
- m_autoFormatOnlyCurrentProject = autoFormatOnlyCurrentProject;
-}
-
-bool CMakeFormatterSettings::isApplicable(const Core::IDocument *document) const
-{
- if (!document)
- return false;
-
- if (m_autoFormatMime.isEmpty())
- return true;
-
- const Utils::MimeType documentMimeType = Utils::mimeTypeForName(document->mimeType());
- return Utils::anyOf(m_autoFormatMime, [&documentMimeType](const QString &mime) {
- return documentMimeType.inherits(mime);
- });
-}
-
-} // namespace Internal
-} // namespace CMakeProjectManager
diff --git a/src/plugins/cmakeprojectmanager/cmakeformattersettings.h b/src/plugins/cmakeprojectmanager/cmakeformattersettings.h
deleted file mode 100644
index ea8a3ba162..0000000000
--- a/src/plugins/cmakeprojectmanager/cmakeformattersettings.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (C) 2016 Lorenz Haas
-// Copyright (C) 2022 Xavier BESSON
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <QList>
-#include <QObject>
-#include <QVersionNumber>
-
-namespace Core { class IDocument; }
-namespace Utils { class FilePath; }
-
-namespace CMakeProjectManager {
-namespace Internal {
-
-class VersionUpdater;
-
-class CMakeFormatterSettings : public QObject
-{
- Q_OBJECT
-public:
- explicit CMakeFormatterSettings(QObject* parent = nullptr);
- static CMakeFormatterSettings *instance();
-
- void read();
- void save();
-
- Utils::FilePath command() const;
- void setCommand(const QString &cmd);
-
- bool autoFormatOnSave() const;
- void setAutoFormatOnSave(bool autoFormatOnSave);
-
- QStringList autoFormatMime() const;
- QString autoFormatMimeAsString() const;
- void setAutoFormatMime(const QStringList &autoFormatMime);
- void setAutoFormatMime(const QString &mimeList);
-
- bool autoFormatOnlyCurrentProject() const;
- void setAutoFormatOnlyCurrentProject(bool autoFormatOnlyCurrentProject);
-
- bool isApplicable(const Core::IDocument *document) const;
-
-signals:
- void supportedMimeTypesChanged();
-
-private:
- QString m_command;
-
- bool m_autoFormatOnSave = false;
- bool m_autoFormatOnlyCurrentProject = true;
- QString m_autoFormatTool;
- QStringList m_autoFormatMime;
-};
-
-} // namespace Internal
-} // namespace CMakeProjectManager
diff --git a/src/plugins/cmakeprojectmanager/cmakeinstallstep.cpp b/src/plugins/cmakeprojectmanager/cmakeinstallstep.cpp
index 6e6a49523f..07d3c6fd47 100644
--- a/src/plugins/cmakeprojectmanager/cmakeinstallstep.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeinstallstep.cpp
@@ -74,7 +74,7 @@ CommandLine CMakeInstallStep::cmakeCommand() const
if (buildConfiguration())
buildDirectory = buildConfiguration()->buildDirectory();
- cmd.addArgs({"--install", buildDirectory.onDevice(cmd.executable()).path()});
+ cmd.addArgs({"--install", buildDirectory.path()});
auto bs = qobject_cast<CMakeBuildSystem *>(buildSystem());
if (bs && bs->isMultiConfigReader()) {
@@ -93,6 +93,7 @@ void CMakeInstallStep::finish(ProcessResult result)
emit progress(100, {});
AbstractProcessStep::finish(result);
}
+
QWidget *CMakeInstallStep::createConfigWidget()
{
auto updateDetails = [this] {
@@ -105,10 +106,8 @@ QWidget *CMakeInstallStep::createConfigWidget()
setDisplayName(Tr::tr("Install", "ConfigWidget display name."));
- Layouting::Form builder;
- builder.addRow(m_cmakeArguments);
-
- auto widget = builder.emerge(Layouting::WithoutMargins);
+ using namespace Layouting;
+ auto widget = Form { m_cmakeArguments, noMargin }.emerge();
updateDetails();
diff --git a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp
index 347c8e794c..def549f580 100644
--- a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp
@@ -38,7 +38,6 @@
#include <utils/variablechooser.h>
#include <QComboBox>
-#include <QCryptographicHash>
#include <QDialog>
#include <QDialogButtonBox>
#include <QGridLayout>
@@ -49,7 +48,6 @@
using namespace ProjectExplorer;
using namespace Utils;
-using namespace Utils::Layouting;
namespace CMakeProjectManager {
@@ -105,7 +103,7 @@ private:
// KitAspectWidget interface
void makeReadOnly() override { m_comboBox->setEnabled(false); }
- void addToLayout(LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &builder) override
{
addMutableAction(m_comboBox);
builder.addItem(m_comboBox);
@@ -350,11 +348,11 @@ private:
// KitAspectWidget interface
void makeReadOnly() override { m_changeButton->setEnabled(false); }
- void addToLayout(LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &parent) override
{
addMutableAction(m_label);
- builder.addItem(m_label);
- builder.addItem(m_changeButton);
+ parent.addItem(m_label);
+ parent.addItem(m_changeButton);
}
void refresh() override
@@ -680,7 +678,7 @@ QVariant CMakeGeneratorKitAspect::defaultValue(const Kit *k) const
if (it != known.constEnd()) {
const bool hasNinja = [k, tool] {
auto settings = Internal::CMakeSpecificSettings::instance();
- if (settings->ninjaPath.filePath().isEmpty()) {
+ if (settings->ninjaPath().isEmpty()) {
auto findNinja = [](const Environment &env) -> bool {
return !env.searchInPath("ninja").isEmpty();
};
@@ -874,7 +872,6 @@ const char CMAKE_CXX_TOOLCHAIN_KEY[] = "CMAKE_CXX_COMPILER";
const char CMAKE_QMAKE_KEY[] = "QT_QMAKE_EXECUTABLE";
const char CMAKE_PREFIX_PATH_KEY[] = "CMAKE_PREFIX_PATH";
const char QTC_CMAKE_PRESET_KEY[] = "QTC_CMAKE_PRESET";
-const char QTC_KIT_DEFAULT_CONFIG_HASH[] = "QTC_KIT_DEFAULT_CONFIG_HASH";
class CMakeConfigurationKitAspectWidget final : public KitAspectWidget
{
@@ -892,11 +889,11 @@ public:
private:
// KitAspectWidget interface
- void addToLayout(LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &parent) override
{
addMutableAction(m_summaryLabel);
- builder.addItem(m_summaryLabel);
- builder.addItem(m_manageButton);
+ parent.addItem(m_summaryLabel);
+ parent.addItem(m_manageButton);
}
void makeReadOnly() override
@@ -1135,51 +1132,6 @@ CMakeConfigItem CMakeConfigurationKitAspect::cmakePresetConfigItem(const Project
});
}
-void CMakeConfigurationKitAspect::setKitDefaultConfigHash(ProjectExplorer::Kit *k)
-{
- const CMakeConfig defaultConfigExpanded
- = Utils::transform(defaultConfiguration(k).toList(), [k](const CMakeConfigItem &item) {
- CMakeConfigItem expanded(item);
- expanded.value = item.expandedValue(k).toUtf8();
- return expanded;
- });
- const CMakeTool *const tool = CMakeKitAspect::cmakeTool(k);
- const QByteArray kitHash = computeDefaultConfigHash(defaultConfigExpanded,
- tool ? tool->cmakeExecutable()
- : FilePath());
-
- CMakeConfig config = configuration(k);
- config.append(CMakeConfigItem(QTC_KIT_DEFAULT_CONFIG_HASH, CMakeConfigItem::INTERNAL, kitHash));
-
- setConfiguration(k, config);
-}
-
-CMakeConfigItem CMakeConfigurationKitAspect::kitDefaultConfigHashItem(const ProjectExplorer::Kit *k)
-{
- const CMakeConfig config = configuration(k);
- return Utils::findOrDefault(config, [](const CMakeConfigItem &item) {
- return item.key == QTC_KIT_DEFAULT_CONFIG_HASH;
- });
-}
-
-QByteArray CMakeConfigurationKitAspect::computeDefaultConfigHash(const CMakeConfig &config,
- const FilePath &cmakeBinary)
-{
- const CMakeConfig defaultConfig = defaultConfiguration(nullptr);
- const QByteArray configValues = std::accumulate(defaultConfig.begin(),
- defaultConfig.end(),
- QByteArray(),
- [config](QByteArray &sum,
- const CMakeConfigItem &item) {
- return sum += config.valueOf(item.key);
- });
- return QCryptographicHash::hash(cmakeBinary.caseSensitivity() == Qt::CaseInsensitive
- ? configValues.toLower()
- : configValues,
- QCryptographicHash::Md5)
- .toHex();
-}
-
QVariant CMakeConfigurationKitAspect::defaultValue(const Kit *k) const
{
// FIXME: Convert preload scripts
@@ -1209,16 +1161,15 @@ Tasks CMakeConfigurationKitAspect::validate(const Kit *k) const
FilePath tcCxxPath;
for (const CMakeConfigItem &i : config) {
// Do not use expand(QByteArray) as we cannot be sure the input is latin1
- const FilePath expandedValue
- = FilePath::fromString(k->macroExpander()->expand(QString::fromUtf8(i.value)));
+ const QString expandedValue = k->macroExpander()->expand(QString::fromUtf8(i.value));
if (i.key == CMAKE_QMAKE_KEY)
- qmakePath = expandedValue.onDevice(cmake->cmakeExecutable());
+ qmakePath = cmake->cmakeExecutable().withNewPath(expandedValue);
else if (i.key == CMAKE_C_TOOLCHAIN_KEY)
- tcCPath = expandedValue.onDevice(cmake->cmakeExecutable());
+ tcCPath = cmake->cmakeExecutable().withNewPath(expandedValue);
else if (i.key == CMAKE_CXX_TOOLCHAIN_KEY)
- tcCxxPath = expandedValue.onDevice(cmake->cmakeExecutable());
+ tcCxxPath = cmake->cmakeExecutable().withNewPath(expandedValue);
else if (i.key == CMAKE_PREFIX_PATH_KEY)
- qtInstallDirs = CMakeConfigItem::cmakeSplitValue(expandedValue.path());
+ qtInstallDirs = CMakeConfigItem::cmakeSplitValue(expandedValue);
}
Tasks result;
@@ -1259,7 +1210,7 @@ Tasks CMakeConfigurationKitAspect::validate(const Kit *k) const
if (!tcC || !tcC->isValid()) {
addWarning(Tr::tr("CMake configuration has a path to a C compiler set, "
"even though the kit has no valid tool chain."));
- } else if (tcCPath != tcC->compilerCommand() && tcCPath != tcC->compilerCommand().onDevice(tcCPath)) {
+ } else if (tcCPath != tcC->compilerCommand() && tcCPath != tcCPath.withNewMappedPath(tcC->compilerCommand())) {
addWarning(Tr::tr("CMake configuration has a path to a C compiler set "
"that does not match the compiler path "
"configured in the tool chain of the kit."));
@@ -1275,7 +1226,7 @@ Tasks CMakeConfigurationKitAspect::validate(const Kit *k) const
if (!tcCxx || !tcCxx->isValid()) {
addWarning(Tr::tr("CMake configuration has a path to a C++ compiler set, "
"even though the kit has no valid tool chain."));
- } else if (tcCxxPath != tcCxx->compilerCommand() && tcCxxPath != tcCxx->compilerCommand().onDevice(tcCxxPath)) {
+ } else if (tcCxxPath != tcCxx->compilerCommand() && tcCxxPath != tcCxxPath.withNewMappedPath(tcCxx->compilerCommand())) {
addWarning(Tr::tr("CMake configuration has a path to a C++ compiler set "
"that does not match the compiler path "
"configured in the tool chain of the kit."));
diff --git a/src/plugins/cmakeprojectmanager/cmakekitinformation.h b/src/plugins/cmakeprojectmanager/cmakekitinformation.h
index bf056f99e2..3b85235d91 100644
--- a/src/plugins/cmakeprojectmanager/cmakekitinformation.h
+++ b/src/plugins/cmakeprojectmanager/cmakekitinformation.h
@@ -91,11 +91,6 @@ public:
static void setCMakePreset(ProjectExplorer::Kit *k, const QString &presetName);
static CMakeConfigItem cmakePresetConfigItem(const ProjectExplorer::Kit *k);
- static void setKitDefaultConfigHash(ProjectExplorer::Kit *k);
- static CMakeConfigItem kitDefaultConfigHashItem(const ProjectExplorer::Kit *k);
- static QByteArray computeDefaultConfigHash(const CMakeConfig &config,
- const Utils::FilePath &cmakeBinary);
-
// KitAspect interface
ProjectExplorer::Tasks validate(const ProjectExplorer::Kit *k) const final;
void setup(ProjectExplorer::Kit *k) final;
diff --git a/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp b/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp
index ab646d1fa6..8d63ee0fa9 100644
--- a/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp
@@ -8,168 +8,151 @@
#include "cmakeproject.h"
#include "cmakeprojectmanagertr.h"
-#include <coreplugin/editormanager/editormanager.h>
-
#include <projectexplorer/buildmanager.h>
#include <projectexplorer/buildsteplist.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <utils/algorithm.h>
+using namespace Core;
using namespace ProjectExplorer;
using namespace Utils;
namespace CMakeProjectManager::Internal {
-// --------------------------------------------------------------------
-// CMakeTargetLocatorFilter:
-// --------------------------------------------------------------------
+using BuildAcceptor = std::function<void(const FilePath &, const QString &)>;
-CMakeTargetLocatorFilter::CMakeTargetLocatorFilter()
+static LocatorMatcherTasks cmakeMatchers(const BuildAcceptor &acceptor)
{
- connect(SessionManager::instance(), &SessionManager::projectAdded,
- this, &CMakeTargetLocatorFilter::projectListUpdated);
- connect(SessionManager::instance(), &SessionManager::projectRemoved,
- this, &CMakeTargetLocatorFilter::projectListUpdated);
+ using namespace Tasking;
- // Initialize the filter
- projectListUpdated();
-}
+ TreeStorage<LocatorStorage> storage;
-void CMakeTargetLocatorFilter::prepareSearch(const QString &entry)
-{
- m_result.clear();
- const QList<Project *> projects = SessionManager::projects();
- for (Project *p : projects) {
- auto cmakeProject = qobject_cast<const CMakeProject *>(p);
- if (!cmakeProject || !cmakeProject->activeTarget())
- continue;
- auto bs = qobject_cast<CMakeBuildSystem *>(cmakeProject->activeTarget()->buildSystem());
- if (!bs)
- continue;
-
- const QList<CMakeBuildTarget> buildTargets = bs->buildTargets();
- for (const CMakeBuildTarget &target : buildTargets) {
- if (CMakeBuildSystem::filteredOutTarget(target))
+ const auto onSetup = [storage, acceptor] {
+ const QString input = storage->input();
+ const QList<Project *> projects = ProjectManager::projects();
+ LocatorFilterEntries entries;
+ for (Project *project : projects) {
+ const auto cmakeProject = qobject_cast<const CMakeProject *>(project);
+ if (!cmakeProject || !cmakeProject->activeTarget())
+ continue;
+ const auto bs = qobject_cast<CMakeBuildSystem *>(
+ cmakeProject->activeTarget()->buildSystem());
+ if (!bs)
continue;
- const int index = target.title.indexOf(entry, 0, Qt::CaseInsensitive);
- if (index >= 0) {
- const FilePath path = target.backtrace.isEmpty() ? cmakeProject->projectFilePath()
- : target.backtrace.last().path;
- const int line = target.backtrace.isEmpty() ? -1 : target.backtrace.last().line;
-
- QVariantMap extraData;
- extraData.insert("project", cmakeProject->projectFilePath().toString());
- extraData.insert("line", line);
- extraData.insert("file", path.toString());
-
- Core::LocatorFilterEntry filterEntry(this, target.title, extraData);
- filterEntry.extraInfo = path.shortNativePath();
- filterEntry.highlightInfo = {index, int(entry.length())};
- filterEntry.filePath = path;
-
- m_result.append(filterEntry);
+
+ const QList<CMakeBuildTarget> buildTargets = bs->buildTargets();
+ for (const CMakeBuildTarget &target : buildTargets) {
+ if (CMakeBuildSystem::filteredOutTarget(target))
+ continue;
+ const int index = target.title.indexOf(input, 0, Qt::CaseInsensitive);
+ if (index >= 0) {
+ const FilePath path = target.backtrace.isEmpty()
+ ? cmakeProject->projectFilePath()
+ : target.backtrace.last().path;
+ const int line = target.backtrace.isEmpty() ? 0 : target.backtrace.last().line;
+ const FilePath projectPath = cmakeProject->projectFilePath();
+ const QString displayName = target.title;
+ LocatorFilterEntry entry;
+ entry.displayName = displayName;
+ if (acceptor) {
+ entry.acceptor = [projectPath, displayName, acceptor] {
+ acceptor(projectPath, displayName);
+ return AcceptResult();
+ };
+ }
+ entry.linkForEditor = {path, line};
+ entry.extraInfo = path.shortNativePath();
+ entry.highlightInfo = {index, int(input.length())};
+ entry.filePath = cmakeProject->projectFilePath();
+ entries.append(entry);
+ }
}
}
- }
+ storage->reportOutput(entries);
+ };
+ return {{Sync(onSetup), storage}};
}
-QList<Core::LocatorFilterEntry> CMakeTargetLocatorFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
+void setupFilter(ILocatorFilter *filter)
{
- Q_UNUSED(future)
- Q_UNUSED(entry)
- return m_result;
-}
-
-void CMakeTargetLocatorFilter::projectListUpdated()
-{
- // Enable the filter if there's at least one CMake project
- setEnabled(Utils::contains(SessionManager::projects(), [](Project *p) { return qobject_cast<CMakeProject *>(p); }));
+ const auto projectListUpdated = [filter] {
+ filter->setEnabled(Utils::contains(ProjectManager::projects(),
+ [](Project *p) { return qobject_cast<CMakeProject *>(p); }));
+ };
+ QObject::connect(ProjectManager::instance(), &ProjectManager::projectAdded,
+ filter, projectListUpdated);
+ QObject::connect(ProjectManager::instance(), &ProjectManager::projectRemoved,
+ filter, projectListUpdated);
}
// --------------------------------------------------------------------
// BuildCMakeTargetLocatorFilter:
// --------------------------------------------------------------------
-BuildCMakeTargetLocatorFilter::BuildCMakeTargetLocatorFilter()
-{
- setId("Build CMake target");
- setDisplayName(Tr::tr("Build CMake target"));
- setDescription(Tr::tr("Builds a target of any open CMake project."));
- setDefaultShortcutString("cm");
- setPriority(High);
-}
-
-void BuildCMakeTargetLocatorFilter::accept(const Core::LocatorFilterEntry &selection,
- QString *newText,
- int *selectionStart,
- int *selectionLength) const
+static void buildAcceptor(const FilePath &projectPath, const QString &displayName)
{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
-
- const QVariantMap extraData = selection.internalData.toMap();
- const FilePath projectPath = FilePath::fromString(extraData.value("project").toString());
-
// Get the project containing the target selected
const auto cmakeProject = qobject_cast<CMakeProject *>(
- Utils::findOrDefault(SessionManager::projects(), [projectPath](Project *p) {
+ Utils::findOrDefault(ProjectManager::projects(), [projectPath](Project *p) {
return p->projectFilePath() == projectPath;
}));
if (!cmakeProject || !cmakeProject->activeTarget()
|| !cmakeProject->activeTarget()->activeBuildConfiguration())
return;
+ if (BuildManager::isBuilding(cmakeProject))
+ BuildManager::cancel();
+
// Find the make step
- BuildStepList *buildStepList =
- cmakeProject->activeTarget()->activeBuildConfiguration()->buildSteps();
- auto buildStep = buildStepList->firstOfType<CMakeBuildStep>();
+ const BuildStepList *buildStepList =
+ cmakeProject->activeTarget()->activeBuildConfiguration()->buildSteps();
+ const auto buildStep = buildStepList->firstOfType<CMakeBuildStep>();
if (!buildStep)
return;
// Change the make step to build only the given target
- QStringList oldTargets = buildStep->buildTargets();
- buildStep->setBuildTargets({selection.displayName});
+ const QStringList oldTargets = buildStep->buildTargets();
+ buildStep->setBuildTargets({displayName});
// Build
BuildManager::buildProjectWithDependencies(cmakeProject);
buildStep->setBuildTargets(oldTargets);
}
+CMakeBuildTargetFilter::CMakeBuildTargetFilter()
+{
+ setId("Build CMake target");
+ setDisplayName(Tr::tr("Build CMake Target"));
+ setDescription(Tr::tr("Builds a target of any open CMake project."));
+ setDefaultShortcutString("cm");
+ setPriority(High);
+ setupFilter(this);
+}
+
+Core::LocatorMatcherTasks CMakeBuildTargetFilter::matchers()
+{
+ return cmakeMatchers(&buildAcceptor);
+}
+
// --------------------------------------------------------------------
// OpenCMakeTargetLocatorFilter:
// --------------------------------------------------------------------
-OpenCMakeTargetLocatorFilter::OpenCMakeTargetLocatorFilter()
+CMakeOpenTargetFilter::CMakeOpenTargetFilter()
{
setId("Open CMake target definition");
- setDisplayName(Tr::tr("Open CMake target"));
- setDescription(Tr::tr("Jumps to the definition of a target of any open CMake project."));
+ setDisplayName(Tr::tr("Open CMake Target"));
+ setDescription(Tr::tr("Locates the definition of a target of any open CMake project."));
setDefaultShortcutString("cmo");
setPriority(Medium);
+ setupFilter(this);
}
-void OpenCMakeTargetLocatorFilter::accept(const Core::LocatorFilterEntry &selection,
- QString *newText,
- int *selectionStart,
- int *selectionLength) const
+Core::LocatorMatcherTasks CMakeOpenTargetFilter::matchers()
{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
-
- const QVariantMap extraData = selection.internalData.toMap();
- const int line = extraData.value("line").toInt();
- const auto file = FilePath::fromVariant(extraData.value("file"));
-
- if (line >= 0)
- Core::EditorManager::openEditorAt({file, line},
- {},
- Core::EditorManager::AllowExternalEditor);
- else
- Core::EditorManager::openEditor(file, {}, Core::EditorManager::AllowExternalEditor);
+ return cmakeMatchers({});
}
-} // CMakeProjectManager::Internal
+} // namespace CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmakelocatorfilter.h b/src/plugins/cmakeprojectmanager/cmakelocatorfilter.h
index 0a74eeafeb..73e0a9fe4a 100644
--- a/src/plugins/cmakeprojectmanager/cmakelocatorfilter.h
+++ b/src/plugins/cmakeprojectmanager/cmakelocatorfilter.h
@@ -7,41 +7,22 @@
namespace CMakeProjectManager::Internal {
-class CMakeTargetLocatorFilter : public Core::ILocatorFilter
+class CMakeBuildTargetFilter : Core::ILocatorFilter
{
public:
- CMakeTargetLocatorFilter();
-
- void prepareSearch(const QString &entry) override;
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) final;
+ CMakeBuildTargetFilter();
private:
- void projectListUpdated();
-
- QList<Core::LocatorFilterEntry> m_result;
-};
-
-class BuildCMakeTargetLocatorFilter : CMakeTargetLocatorFilter
-{
-public:
- BuildCMakeTargetLocatorFilter();
-
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText,
- int *selectionStart,
- int *selectionLength) const final;
+ Core::LocatorMatcherTasks matchers() final;
};
-class OpenCMakeTargetLocatorFilter : CMakeTargetLocatorFilter
+class CMakeOpenTargetFilter : Core::ILocatorFilter
{
public:
- OpenCMakeTargetLocatorFilter();
+ CMakeOpenTargetFilter();
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText,
- int *selectionStart,
- int *selectionLength) const final;
+private:
+ Core::LocatorMatcherTasks matchers() final;
};
-} // CMakeProjectManager::Internal
+} // namespace CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmakeprocess.cpp b/src/plugins/cmakeprojectmanager/cmakeprocess.cpp
index 7a14143a4b..1455cbf88b 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprocess.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeprocess.cpp
@@ -14,8 +14,8 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/taskhub.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
using namespace Core;
@@ -38,7 +38,7 @@ CMakeProcess::~CMakeProcess()
m_parser.flush();
}
-static const int failedToStartExitCode = 0xFF; // See QtcProcessPrivate::handleDone() impl
+static const int failedToStartExitCode = 0xFF; // See ProcessPrivate::handleDone() impl
void CMakeProcess::run(const BuildDirParameters &parameters, const QStringList &arguments)
{
@@ -67,8 +67,8 @@ void CMakeProcess::run(const BuildDirParameters &parameters, const QStringList &
return;
}
- const FilePath sourceDirectory = parameters.sourceDirectory.onDevice(cmakeExecutable);
- const FilePath buildDirectory = parameters.buildDirectory.onDevice(cmakeExecutable);
+ const FilePath sourceDirectory = cmakeExecutable.withNewMappedPath(parameters.sourceDirectory);
+ const FilePath buildDirectory = parameters.buildDirectory;
if (!buildDirectory.exists()) {
const QString msg = ::CMakeProjectManager::Tr::tr(
@@ -106,7 +106,7 @@ void CMakeProcess::run(const BuildDirParameters &parameters, const QStringList &
// Always use the sourceDir: If we are triggered because the build directory is getting deleted
// then we are racing against CMakeCache.txt also getting deleted.
- m_process.reset(new QtcProcess);
+ m_process.reset(new Process);
m_process->setWorkingDirectory(buildDirectory);
m_process->setEnvironment(parameters.environment);
@@ -120,7 +120,7 @@ void CMakeProcess::run(const BuildDirParameters &parameters, const QStringList &
BuildSystem::appendBuildSystemOutput(stripTrailingNewline(s));
});
- connect(m_process.get(), &QtcProcess::done, this, [this] {
+ connect(m_process.get(), &Process::done, this, [this] {
handleProcessDone(m_process->resultData());
});
diff --git a/src/plugins/cmakeprojectmanager/cmakeprocess.h b/src/plugins/cmakeprojectmanager/cmakeprocess.h
index d13b0efabe..bb365d337c 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprocess.h
+++ b/src/plugins/cmakeprojectmanager/cmakeprocess.h
@@ -13,7 +13,7 @@
namespace Utils {
class ProcessResultData;
-class QtcProcess;
+class Process;
}
namespace CMakeProjectManager::Internal {
@@ -37,7 +37,7 @@ signals:
private:
void handleProcessDone(const Utils::ProcessResultData &resultData);
- std::unique_ptr<Utils::QtcProcess> m_process;
+ std::unique_ptr<Utils::Process> m_process;
Utils::OutputFormatter m_parser;
QElapsedTimer m_elapsed;
};
diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp
index ae6e26f9e3..78b6449a7a 100644
--- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp
@@ -7,16 +7,17 @@
#include "cmakeprojectconstants.h"
#include "cmakeprojectimporter.h"
#include "cmakeprojectmanagertr.h"
-#include "cmaketool.h"
#include <coreplugin/icontext.h>
#include <projectexplorer/buildconfiguration.h>
+#include <projectexplorer/buildinfo.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
+#include <qtsupport/qtkitinformation.h>
using namespace ProjectExplorer;
using namespace Utils;
@@ -34,7 +35,11 @@ CMakeProject::CMakeProject(const FilePath &fileName)
setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID));
setDisplayName(projectDirectory().fileName());
setCanBuildProducts();
- setHasMakeInstallEquivalent(true);
+
+ // This only influences whether 'Install into temporary host directory'
+ // will show up by default enabled in some remote deploy configurations.
+ // We rely on staging via the actual cmake build step.
+ setHasMakeInstallEquivalent(false);
readPresets();
}
@@ -62,7 +67,7 @@ Tasks CMakeProject::projectIssues(const Kit *k) const
ProjectImporter *CMakeProject::projectImporter() const
{
if (!m_projectImporter)
- m_projectImporter = new CMakeProjectImporter(projectFilePath(), m_presetsData);
+ m_projectImporter = new CMakeProjectImporter(projectFilePath(), this);
return m_projectImporter;
}
@@ -88,6 +93,14 @@ Internal::PresetsData CMakeProject::combinePresets(Internal::PresetsData &cmakeP
result.version = cmakePresetsData.version;
result.cmakeMinimimRequired = cmakePresetsData.cmakeMinimimRequired;
+ result.include = cmakePresetsData.include;
+ if (result.include) {
+ if (cmakeUserPresetsData.include)
+ result.include->append(cmakeUserPresetsData.include.value());
+ } else {
+ result.include = cmakeUserPresetsData.include;
+ }
+
auto combinePresetsInternal = [](auto &presetsHash,
auto &presets,
auto &userPresets,
@@ -225,7 +238,7 @@ void CMakeProject::readPresets()
if (includeStack.contains(includePath)) {
TaskHub::addTask(BuildSystemTask(
Task::TaskType::Warning,
- Tr::tr("Attempt to include %1 which was already parsed.")
+ Tr::tr("Attempt to include \"%1\" which was already parsed.")
.arg(includePath.path()),
Utils::FilePath(),
-1));
@@ -280,4 +293,28 @@ ProjectExplorer::DeploymentKnowledge CMakeProject::deploymentKnowledge() const
: DeploymentKnowledge::Bad;
}
+void CMakeProject::configureAsExampleProject(ProjectExplorer::Kit *kit)
+{
+ QList<BuildInfo> infoList;
+ const QList<Kit *> kits(kit != nullptr ? QList<Kit *>({kit}) : KitManager::kits());
+ for (Kit *k : kits) {
+ if (QtSupport::QtKitAspect::qtVersion(k) != nullptr) {
+ if (auto factory = BuildConfigurationFactory::find(k, projectFilePath()))
+ infoList << factory->allAvailableSetups(k, projectFilePath());
+ }
+ }
+ setup(infoList);
+}
+
+void CMakeProjectManager::CMakeProject::setOldPresetKits(
+ const QList<ProjectExplorer::Kit *> &presetKits) const
+{
+ m_oldPresetKits = presetKits;
+}
+
+QList<Kit *> CMakeProject::oldPresetKits() const
+{
+ return m_oldPresetKits;
+}
+
} // namespace CMakeProjectManager
diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.h b/src/plugins/cmakeprojectmanager/cmakeproject.h
index 2885b9eb4b..6540dd4964 100644
--- a/src/plugins/cmakeprojectmanager/cmakeproject.h
+++ b/src/plugins/cmakeprojectmanager/cmakeproject.h
@@ -31,16 +31,22 @@ public:
Internal::PresetsData presetsData() const;
void readPresets();
+ void setOldPresetKits(const QList<ProjectExplorer::Kit *> &presetKits) const;
+ QList<ProjectExplorer::Kit *> oldPresetKits() const;
+
protected:
bool setupTarget(ProjectExplorer::Target *t) final;
private:
ProjectExplorer::DeploymentKnowledge deploymentKnowledge() const override;
+ void configureAsExampleProject(ProjectExplorer::Kit *kit) override;
+
Internal::PresetsData combinePresets(Internal::PresetsData &cmakePresetsData,
Internal::PresetsData &cmakeUserPresetsData);
void setupBuildPresets(Internal::PresetsData &presetsData);
mutable Internal::CMakeProjectImporter *m_projectImporter = nullptr;
+ mutable QList<ProjectExplorer::Kit*> m_oldPresetKits;
ProjectExplorer::Tasks m_issues;
Internal::PresetsData m_presetsData;
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h b/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h
index cbaa648726..183e950136 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h
@@ -17,6 +17,7 @@ const char BUILD_FILE_CONTEXT_MENU[] = "CMakeProject.BuildFileContextMenu";
const char BUILD_FILE[] = "CMakeProject.BuildFile";
const char CMAKE_HOME_DIR[] = "CMakeProject.HomeDirectory";
const char QML_DEBUG_SETTING[] = "CMakeProject.EnableQmlDebugging";
+const char RELOAD_CMAKE_PRESETS[] = "CMakeProject.ReloadCMakePresets";
const char CMAKEFORMATTER_SETTINGS_GROUP[] = "CMakeFormatter";
const char CMAKEFORMATTER_GENERAL_GROUP[] = "General";
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp
index dc752f0a9f..3a7a7e6c53 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp
@@ -5,6 +5,7 @@
#include "cmakebuildconfiguration.h"
#include "cmakekitinformation.h"
+#include "cmakeproject.h"
#include "cmakeprojectconstants.h"
#include "cmakeprojectmanagertr.h"
#include "cmaketoolmanager.h"
@@ -15,13 +16,14 @@
#include <projectexplorer/buildinfo.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/target.h>
#include <projectexplorer/toolchainmanager.h>
#include <qtsupport/qtkitinformation.h>
#include <utils/algorithm.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/temporarydirectory.h>
@@ -46,7 +48,6 @@ struct DirectoryData
QString cmakePresetDisplayname;
QString cmakePreset;
- QByteArray cmakePresetDefaultConfigHash;
// Kit Stuff
FilePath cmakeBinary;
@@ -93,9 +94,9 @@ static QString uniqueCMakeToolDisplayName(CMakeTool &tool)
// CMakeProjectImporter
-CMakeProjectImporter::CMakeProjectImporter(const FilePath &path, const PresetsData &presetsData)
+CMakeProjectImporter::CMakeProjectImporter(const FilePath &path, const CMakeProject *project)
: QtProjectImporter(path)
- , m_presetsData(presetsData)
+ , m_project(project)
, m_presetsTempDir("qtc-cmake-presets-XXXXXXXX")
{
useTemporaryKitAspect(CMakeKitAspect::id(),
@@ -120,7 +121,7 @@ FilePaths CMakeProjectImporter::importCandidates()
candidates << scanDirectory(shadowBuildDirectory.absolutePath(), QString());
}
- for (const auto &configPreset : m_presetsData.configurePresets) {
+ for (const auto &configPreset : m_project->presetsData().configurePresets) {
if (configPreset.hidden.value())
continue;
@@ -153,6 +154,21 @@ FilePaths CMakeProjectImporter::importCandidates()
return finalists;
}
+Target *CMakeProjectImporter::preferredTarget(const QList<Target *> &possibleTargets)
+{
+ for (Kit *kit : m_project->oldPresetKits()) {
+ const bool haveKit = Utils::contains(possibleTargets, [kit](const auto &target) {
+ return target->kit() == kit;
+ });
+
+ if (!haveKit)
+ KitManager::deregisterKit(kit);
+ }
+ m_project->setOldPresetKits({});
+
+ return ProjectImporter::preferredTarget(possibleTargets);
+}
+
static CMakeConfig configurationFromPresetProbe(
const FilePath &importPath,
const FilePath &sourceDirectory,
@@ -164,7 +180,7 @@ static CMakeConfig configurationFromPresetProbe(
"project(preset-probe)\n"
"\n"));
- QtcProcess cmake;
+ Process cmake;
cmake.setTimeoutS(30);
cmake.setDisableUnixTerminal();
@@ -247,81 +263,107 @@ static CMakeConfig configurationFromPresetProbe(
return config;
}
-static FilePath qmakeFromCMakeCache(const CMakeConfig &config)
+struct QMakeAndCMakePrefixPath
+{
+ FilePath qmakePath;
+ QString cmakePrefixPath; // can be a semicolon-separated list
+};
+
+static QMakeAndCMakePrefixPath qtInfoFromCMakeCache(const CMakeConfig &config,
+ const Environment &env)
{
// Qt4 way to define things (more convenient for us, so try this first;-)
const FilePath qmake = config.filePathValueOf("QT_QMAKE_EXECUTABLE");
qCDebug(cmInputLog) << "QT_QMAKE_EXECUTABLE=" << qmake.toUserOutput();
- if (!qmake.isEmpty())
- return qmake;
// Check Qt5 settings: oh, the horror!
- const FilePath qtCMakeDir = [config] {
- FilePath tmp = config.filePathValueOf("Qt5Core_DIR");
- if (tmp.isEmpty())
- tmp = config.filePathValueOf("Qt6Core_DIR");
+ const FilePath qtCMakeDir = [config, env] {
+ FilePath tmp;
+ // Check the CMake "<package-name>_DIR" variable
+ for (const auto &var : {"Qt6", "Qt6Core", "Qt5", "Qt5Core"}) {
+ tmp = config.filePathValueOf(QByteArray(var) + "_DIR");
+ if (!tmp.isEmpty())
+ break;
+ }
return tmp;
}();
qCDebug(cmInputLog) << "QtXCore_DIR=" << qtCMakeDir.toUserOutput();
const FilePath canQtCMakeDir = FilePath::fromString(qtCMakeDir.toFileInfo().canonicalFilePath());
qCInfo(cmInputLog) << "QtXCore_DIR (canonical)=" << canQtCMakeDir.toUserOutput();
- QString prefixPath;
- if (!qtCMakeDir.isEmpty()) {
- prefixPath = canQtCMakeDir.parentDir().parentDir().parentDir().toString(); // Up 3 levels...
- } else {
- prefixPath = config.stringValueOf("CMAKE_PREFIX_PATH");
- }
+
+ const QString prefixPath = [qtCMakeDir, canQtCMakeDir, config, env] {
+ QString result;
+ if (!qtCMakeDir.isEmpty()) {
+ result = canQtCMakeDir.parentDir().parentDir().parentDir().path(); // Up 3 levels...
+ } else {
+ // Check the CMAKE_PREFIX_PATH and "<package-name>_ROOT" CMake or environment variables
+ // This can be a single value or a semicolon-separated list
+ for (const auto &var : {"CMAKE_PREFIX_PATH", "Qt6_ROOT", "Qt5_ROOT"}) {
+ result = config.stringValueOf(var);
+ if (result.isEmpty())
+ result = env.value(QString::fromUtf8(var));
+ if (!result.isEmpty())
+ break;
+ }
+ }
+ return result;
+ }();
qCDebug(cmInputLog) << "PrefixPath:" << prefixPath;
+ if (!qmake.isEmpty() && !prefixPath.isEmpty())
+ return {qmake, prefixPath};
+
FilePath toolchainFile = config.filePathValueOf(QByteArray("CMAKE_TOOLCHAIN_FILE"));
if (prefixPath.isEmpty() && toolchainFile.isEmpty())
- return FilePath();
+ return {qmake, QString()};
// Run a CMake project that would do qmake probing
TemporaryDirectory qtcQMakeProbeDir("qtc-cmake-qmake-probe-XXXXXXXX");
- QFile cmakeListTxt(qtcQMakeProbeDir.filePath("CMakeLists.txt").toString());
- if (!cmakeListTxt.open(QIODevice::WriteOnly)) {
- return FilePath();
- }
- // FIXME replace by raw string when gcc 8+ is minimum
- cmakeListTxt.write(QByteArray(
-"cmake_minimum_required(VERSION 3.15)\n"
-"\n"
-"project(qmake-probe LANGUAGES NONE)\n"
-"\n"
-"# Bypass Qt6's usage of find_dependency, which would require compiler\n"
-"# and source code probing, which slows things unnecessarily\n"
-"file(WRITE \"${CMAKE_SOURCE_DIR}/CMakeFindDependencyMacro.cmake\"\n"
-"[=["
-" macro(find_dependency dep)\n"
-" endmacro()\n"
-"]=])\n"
-"set(CMAKE_MODULE_PATH \"${CMAKE_SOURCE_DIR}\")\n"
-"\n"
-"find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)\n"
-"find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED)\n"
-"\n"
-"if (CMAKE_CROSSCOMPILING)\n"
-" find_program(qmake_binary\n"
-" NAMES qmake qmake.bat\n"
-" PATHS \"${Qt${QT_VERSION_MAJOR}_DIR}/../../../bin\"\n"
-" NO_DEFAULT_PATH)\n"
-" file(WRITE \"${CMAKE_SOURCE_DIR}/qmake-location.txt\" \"${qmake_binary}\")\n"
-"else()\n"
-" file(GENERATE\n"
-" OUTPUT \"${CMAKE_SOURCE_DIR}/qmake-location.txt\"\n"
-" CONTENT \"$<TARGET_PROPERTY:Qt${QT_VERSION_MAJOR}::qmake,IMPORTED_LOCATION>\")\n"
-"endif()\n"
-));
- cmakeListTxt.close();
-
- QtcProcess cmake;
+ FilePath cmakeListTxt(qtcQMakeProbeDir.filePath("CMakeLists.txt"));
+
+ cmakeListTxt.writeFileContents(QByteArray(R"(
+ cmake_minimum_required(VERSION 3.15)
+
+ project(qmake-probe LANGUAGES NONE)
+
+ # Bypass Qt6's usage of find_dependency, which would require compiler
+ # and source code probing, which slows things unnecessarily
+ file(WRITE "${CMAKE_SOURCE_DIR}/CMakeFindDependencyMacro.cmake"
+ [=[
+ macro(find_dependency dep)
+ endmacro()
+ ]=])
+ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}")
+
+ find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
+ find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED)
+
+ if (CMAKE_CROSSCOMPILING)
+ find_program(qmake_binary
+ NAMES qmake qmake.bat
+ PATHS "${Qt${QT_VERSION_MAJOR}_DIR}/../../../bin"
+ NO_DEFAULT_PATH)
+ file(WRITE "${CMAKE_SOURCE_DIR}/qmake-location.txt" "${qmake_binary}")
+ else()
+ file(GENERATE
+ OUTPUT "${CMAKE_SOURCE_DIR}/qmake-location.txt"
+ CONTENT "$<TARGET_PROPERTY:Qt${QT_VERSION_MAJOR}::qmake,IMPORTED_LOCATION>")
+ endif()
+
+ # Remove a Qt CMake hack that adds lib/cmake at the end of every path in CMAKE_PREFIX_PATH
+ list(REMOVE_DUPLICATES CMAKE_PREFIX_PATH)
+ list(TRANSFORM CMAKE_PREFIX_PATH REPLACE "/lib/cmake$" "")
+ file(WRITE "${CMAKE_SOURCE_DIR}/cmake-prefix-path.txt" "${CMAKE_PREFIX_PATH}")
+ )"));
+
+ Process cmake;
cmake.setTimeoutS(5);
cmake.setDisableUnixTerminal();
- Environment env = Environment::systemEnvironment();
- env.setupEnglishOutput();
- cmake.setEnvironment(env);
+
+ Environment cmakeEnv(env);
+ cmakeEnv.setupEnglishOutput();
+ cmake.setEnvironment(cmakeEnv);
cmake.setTimeOutMessageBoxEnabled(false);
QString cmakeGenerator = config.stringValueOf(QByteArray("CMAKE_GENERATOR"));
@@ -330,6 +372,7 @@ static FilePath qmakeFromCMakeCache(const CMakeConfig &config)
FilePath cmakeExecutable = config.filePathValueOf(QByteArray("CMAKE_COMMAND"));
FilePath cmakeMakeProgram = config.filePathValueOf(QByteArray("CMAKE_MAKE_PROGRAM"));
FilePath hostPath = config.filePathValueOf(QByteArray("QT_HOST_PATH"));
+ const QString findRootPath = config.stringValueOf("CMAKE_FIND_ROOT_PATH");
QStringList args;
args.push_back("-S");
@@ -350,13 +393,16 @@ static FilePath qmakeFromCMakeCache(const CMakeConfig &config)
if (!cmakeMakeProgram.isEmpty()) {
args.push_back(QStringLiteral("-DCMAKE_MAKE_PROGRAM=%1").arg(cmakeMakeProgram.toString()));
}
- if (toolchainFile.isEmpty()) {
- args.push_back(QStringLiteral("-DCMAKE_PREFIX_PATH=%1").arg(prefixPath));
- } else {
- if (!prefixPath.isEmpty())
- args.push_back(QStringLiteral("-DCMAKE_FIND_ROOT_PATH=%1").arg(prefixPath));
+
+ if (!toolchainFile.isEmpty()) {
args.push_back(QStringLiteral("-DCMAKE_TOOLCHAIN_FILE=%1").arg(toolchainFile.toString()));
}
+ if (!prefixPath.isEmpty()) {
+ args.push_back(QStringLiteral("-DCMAKE_PREFIX_PATH=%1").arg(prefixPath));
+ }
+ if (!findRootPath.isEmpty()) {
+ args.push_back(QStringLiteral("-DCMAKE_FIND_ROOT_PATH=%1").arg(findRootPath));
+ }
if (!hostPath.isEmpty()) {
args.push_back(QStringLiteral("-DQT_HOST_PATH=%1").arg(hostPath.toString()));
}
@@ -365,14 +411,17 @@ static FilePath qmakeFromCMakeCache(const CMakeConfig &config)
cmake.setCommand({cmakeExecutable, args});
cmake.runBlocking();
- QFile qmakeLocationTxt(qtcQMakeProbeDir.filePath("qmake-location.txt").path());
- if (!qmakeLocationTxt.open(QIODevice::ReadOnly)) {
- return FilePath();
- }
- FilePath qmakeLocation = FilePath::fromUtf8(qmakeLocationTxt.readLine().constData());
+ const FilePath qmakeLocationTxt = qtcQMakeProbeDir.filePath("qmake-location.txt");
+ const FilePath qmakeLocation = FilePath::fromUtf8(
+ qmakeLocationTxt.fileContents().value_or(QByteArray()));
qCDebug(cmInputLog) << "qmake location: " << qmakeLocation.toUserOutput();
- return qmakeLocation;
+ const FilePath prefixPathTxt = qtcQMakeProbeDir.filePath("cmake-prefix-path.txt");
+ const QString resultedPrefixPath = QString::fromUtf8(
+ prefixPathTxt.fileContents().value_or(QByteArray()));
+ qCDebug(cmInputLog) << "PrefixPath [after qmake probe]: " << resultedPrefixPath;
+
+ return {qmakeLocation, resultedPrefixPath};
}
static QVector<ToolChainDescription> extractToolChainsFromCache(const CMakeConfig &config)
@@ -590,7 +639,7 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
const QString presetName = importPath.fileName();
PresetsDetails::ConfigurePreset configurePreset
- = Utils::findOrDefault(m_presetsData.configurePresets,
+ = Utils::findOrDefault(m_project->presetsData().configurePresets,
[presetName](const PresetsDetails::ConfigurePreset &preset) {
return preset.name == presetName;
});
@@ -687,19 +736,21 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
configurePreset.generator.value().toUtf8());
}
- const FilePath qmake = qmakeFromCMakeCache(config);
+ const auto [qmake, cmakePrefixPath] = qtInfoFromCMakeCache(config, env);
if (!qmake.isEmpty())
data->qt = findOrCreateQtVersion(qmake);
+ if (!cmakePrefixPath.isEmpty() && config.valueOf("CMAKE_PREFIX_PATH").isEmpty())
+ config << CMakeConfigItem("CMAKE_PREFIX_PATH",
+ CMakeConfigItem::PATH,
+ cmakePrefixPath.toUtf8());
+
// ToolChains:
data->toolChains = extractToolChainsFromCache(config);
// Update QT_QMAKE_EXECUTABLE and CMAKE_C|XX_COMPILER config values
updateConfigWithDirectoryData(config, data);
- data->cmakePresetDefaultConfigHash
- = CMakeConfigurationKitAspect::computeDefaultConfigHash(config, data->cmakeBinary);
-
QByteArrayList buildConfigurationTypes = {cache.valueOf("CMAKE_BUILD_TYPE")};
if (buildConfigurationTypes.front().isEmpty()) {
buildConfigurationTypes.clear();
@@ -747,6 +798,8 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
buildConfigurationTypes = buildConfigurationTypesString.split(';');
}
+ const Environment env = projectDirectory().deviceEnvironment();
+
for (auto const &buildType: std::as_const(buildConfigurationTypes)) {
auto data = std::make_unique<DirectoryData>();
@@ -778,7 +831,7 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
data->sysroot = config.filePathValueOf("CMAKE_SYSROOT");
// Qt:
- const FilePath qmake = qmakeFromCMakeCache(config);
+ const auto [qmake, cmakePrefixPath] = qtInfoFromCMakeCache(config, env);
if (!qmake.isEmpty())
data->qt = findOrCreateQtVersion(qmake);
@@ -826,36 +879,50 @@ bool CMakeProjectImporter::matchKit(void *directoryData, const Kit *k) const
if (data->qt.qt && QtSupport::QtKitAspect::qtVersionId(k) != data->qt.qt->uniqueId())
return false;
+ const bool compilersMatch = [k, data] {
+ const QList<Id> allLanguages = ToolChainManager::allLanguages();
+ for (const ToolChainDescription &tcd : data->toolChains) {
+ if (!Utils::contains(allLanguages,
+ [&tcd](const Id &language) { return language == tcd.language; }))
+ continue;
+ ToolChain *tc = ToolChainKitAspect::toolChain(k, tcd.language);
+ if ((!tc || !tc->matchesCompilerCommand(tcd.compilerPath))) {
+ return false;
+ }
+ }
+ return true;
+ }();
+ const bool noCompilers = [k, data] {
+ const QList<Id> allLanguages = ToolChainManager::allLanguages();
+ for (const ToolChainDescription &tcd : data->toolChains) {
+ if (!Utils::contains(allLanguages,
+ [&tcd](const Id &language) { return language == tcd.language; }))
+ continue;
+ ToolChain *tc = ToolChainKitAspect::toolChain(k, tcd.language);
+ if (tc && tc->matchesCompilerCommand(tcd.compilerPath)) {
+ return false;
+ }
+ }
+ return true;
+ }();
+
bool haveCMakePreset = false;
if (!data->cmakePreset.isEmpty()) {
const auto presetConfigItem = CMakeConfigurationKitAspect::cmakePresetConfigItem(k);
- const auto kitConfigHashItem = CMakeConfigurationKitAspect::kitDefaultConfigHashItem(k);
const QString presetName = presetConfigItem.expandedValue(k);
- const bool haveSameKitConfigHash = kitConfigHashItem.isNull()
- ? true
- : data->cmakePresetDefaultConfigHash
- == kitConfigHashItem.value;
-
- if (data->cmakePreset != presetName || !haveSameKitConfigHash)
+ if (data->cmakePreset != presetName)
return false;
ensureBuildDirectory(*data, k);
haveCMakePreset = true;
}
- const QList<Id> allLanguages = ToolChainManager::allLanguages();
- for (const ToolChainDescription &tcd : data->toolChains) {
- if (!Utils::contains(allLanguages, [&tcd](const Id& language) {return language == tcd.language;}))
- continue;
- ToolChain *tc = ToolChainKitAspect::toolChain(k, tcd.language);
- if ((!tc || !tc->matchesCompilerCommand(tcd.compilerPath)) && !haveCMakePreset) {
- return false;
- }
- }
+ if (!compilersMatch && !(haveCMakePreset && noCompilers))
+ return false;
qCDebug(cmInputLog) << k->displayName()
- << "matches directoryData for" << data->buildDirectory.toUserOutput();
+ << "matches directoryData for" << data->buildDirectory.toUserOutput();
return true;
}
@@ -894,7 +961,6 @@ Kit *CMakeProjectImporter::createKit(void *directoryData) const
QString("%1 (CMake preset)").arg(data->cmakePresetDisplayname));
CMakeConfigurationKitAspect::setCMakePreset(k, data->cmakePreset);
- CMakeConfigurationKitAspect::setKitDefaultConfigHash(k);
}
if (!data->cmakePreset.isEmpty())
ensureBuildDirectory(*data, k);
@@ -1022,8 +1088,9 @@ void CMakeProjectPlugin::testCMakeProjectImporterQt()
config.append(CMakeConfigItem(key.toUtf8(), value.toUtf8()));
}
- FilePath realQmake = qmakeFromCMakeCache(config);
- QCOMPARE(realQmake.toString(), expectedQmake);
+ auto [realQmake, cmakePrefixPath] = qtInfoFromCMakeCache(config,
+ Environment::systemEnvironment());
+ QCOMPARE(realQmake.path(), expectedQmake);
}
void CMakeProjectPlugin::testCMakeProjectImporterToolChain_data()
{
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.h b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.h
index 7c7716dc8b..350b0b77d6 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.h
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.h
@@ -4,12 +4,14 @@
#pragma once
#include "presetsparser.h"
-#include "utils/temporarydirectory.h"
#include <qtsupport/qtprojectimporter.h>
+#include <utils/temporarydirectory.h>
+
namespace CMakeProjectManager {
+class CMakeProject;
class CMakeTool;
namespace Internal {
@@ -19,9 +21,11 @@ struct DirectoryData;
class CMakeProjectImporter : public QtSupport::QtProjectImporter
{
public:
- CMakeProjectImporter(const Utils::FilePath &path, const Internal::PresetsData &presetsData);
+ CMakeProjectImporter(const Utils::FilePath &path,
+ const CMakeProjectManager::CMakeProject *project);
Utils::FilePaths importCandidates() final;
+ ProjectExplorer::Target *preferredTarget(const QList<ProjectExplorer::Target *> &possibleTargets) final;
private:
QList<void *> examineDirectory(const Utils::FilePath &importPath,
@@ -43,7 +47,7 @@ private:
void ensureBuildDirectory(DirectoryData &data, const ProjectExplorer::Kit *k) const;
- Internal::PresetsData m_presetsData;
+ const CMakeProject *m_project;
Utils::TemporaryDirectory m_presetsTempDir;
};
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp
index e05e155cf6..c43c7978a6 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp
@@ -9,6 +9,7 @@
#include "cmakeprojectconstants.h"
#include "cmakeprojectmanagertr.h"
#include "cmakeprojectnodes.h"
+#include "cmakespecificsettings.h"
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
@@ -16,14 +17,19 @@
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
+#include <coreplugin/modemanager.h>
+
#include <cppeditor/cpptoolsreuse.h>
+
#include <projectexplorer/buildmanager.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
+#include <utils/checkablemessagebox.h>
+#include <utils/utilsicons.h>
#include <utils/parameteraction.h>
#include <QAction>
@@ -40,6 +46,8 @@ CMakeManager::CMakeManager()
, m_clearCMakeCacheAction(new QAction(QIcon(), Tr::tr("Clear CMake Configuration"), this))
, m_runCMakeActionContextMenu(new QAction(QIcon(), Tr::tr("Run CMake"), this))
, m_rescanProjectAction(new QAction(QIcon(), Tr::tr("Rescan Project"), this))
+ , m_reloadCMakePresetsAction(
+ new QAction(Utils::Icons::RELOAD_TOOLBAR.icon(), Tr::tr("Reload CMake Presets"), this))
{
Core::ActionContainer *mbuild =
Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT);
@@ -59,7 +67,7 @@ CMakeManager::CMakeManager()
command->setAttribute(Core::Command::CA_Hide);
mbuild->addAction(command, ProjectExplorer::Constants::G_BUILD_BUILD);
connect(m_runCMakeAction, &QAction::triggered, this, [this] {
- runCMake(SessionManager::startupBuildSystem());
+ runCMake(ProjectManager::startupBuildSystem());
});
command = Core::ActionManager::registerAction(m_clearCMakeCacheAction,
@@ -68,7 +76,7 @@ CMakeManager::CMakeManager()
command->setAttribute(Core::Command::CA_Hide);
mbuild->addAction(command, ProjectExplorer::Constants::G_BUILD_BUILD);
connect(m_clearCMakeCacheAction, &QAction::triggered, this, [this] {
- clearCMakeCache(SessionManager::startupBuildSystem());
+ clearCMakeCache(ProjectManager::startupBuildSystem());
});
command = Core::ActionManager::registerAction(m_runCMakeActionContextMenu,
@@ -99,6 +107,15 @@ CMakeManager::CMakeManager()
rescanProject(ProjectTree::currentBuildSystem());
});
+ command = Core::ActionManager::registerAction(m_reloadCMakePresetsAction,
+ Constants::RELOAD_CMAKE_PRESETS,
+ globalContext);
+ command->setAttribute(Core::Command::CA_Hide);
+ mbuild->addAction(command, ProjectExplorer::Constants::G_BUILD_BUILD);
+ connect(m_reloadCMakePresetsAction, &QAction::triggered, this, [this] {
+ reloadCMakePresets();
+ });
+
m_buildFileAction = new Utils::ParameterAction(Tr::tr("Build File"),
Tr::tr("Build File \"%1\""),
Utils::ParameterAction::AlwaysEnabled,
@@ -111,7 +128,7 @@ CMakeManager::CMakeManager()
mbuild->addAction(command, ProjectExplorer::Constants::G_BUILD_BUILD);
connect(m_buildFileAction, &QAction::triggered, this, [this] { buildFile(); });
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged, this, [this] {
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged, this, [this] {
updateCmakeActions(ProjectTree::currentNode());
});
connect(BuildManager::instance(), &BuildManager::buildStateChanged, this, [this] {
@@ -127,12 +144,22 @@ CMakeManager::CMakeManager()
void CMakeManager::updateCmakeActions(Node *node)
{
- auto project = qobject_cast<CMakeProject *>(SessionManager::startupProject());
+ auto project = qobject_cast<CMakeProject *>(ProjectManager::startupProject());
const bool visible = project && !BuildManager::isBuilding(project);
m_runCMakeAction->setVisible(visible);
m_runCMakeActionContextMenu->setEnabled(visible);
m_clearCMakeCacheAction->setVisible(visible);
m_rescanProjectAction->setVisible(visible);
+
+ const bool reloadPresetsVisible = [project] {
+ if (!project)
+ return false;
+ const FilePath presetsPath = project->projectFilePath().parentDir().pathAppended(
+ "CMakePresets.json");
+ return presetsPath.exists();
+ }();
+ m_reloadCMakePresetsAction->setVisible(reloadPresetsVisible);
+
enableBuildFileMenus(node);
}
@@ -202,6 +229,62 @@ void CMakeManager::enableBuildFileMenus(Node *node)
}
}
+void CMakeManager::reloadCMakePresets()
+{
+ auto settings = CMakeSpecificSettings::instance();
+
+ QMessageBox::StandardButton clickedButton
+ = CheckableMessageBox::question(Core::ICore::dialogParent(),
+ Tr::tr("Reload CMake Presets"),
+ Tr::tr("Re-generates the CMake presets kits. The manual "
+ "CMake project modifications will be lost."),
+ settings->askBeforePresetsReload.checkableDecider(),
+ QMessageBox::Yes | QMessageBox::Cancel,
+ QMessageBox::Yes,
+ QMessageBox::Yes,
+ {
+ {QMessageBox::Yes, Tr::tr("Reload")},
+ });
+
+ if (clickedButton == QMessageBox::Cancel)
+ return;
+
+ CMakeProject *project = static_cast<CMakeProject *>(ProjectTree::currentProject());
+ if (!project)
+ return;
+
+ const QSet<QString> oldPresets = Utils::transform<QSet>(project->presetsData().configurePresets,
+ [](const auto &preset) {
+ return preset.name;
+ });
+ project->readPresets();
+
+ QList<Kit*> oldKits;
+ for (const auto &target : project->targets()) {
+ const CMakeConfigItem presetItem = CMakeConfigurationKitAspect::cmakePresetConfigItem(
+ target->kit());
+
+ if (BuildManager::isBuilding(target))
+ BuildManager::cancel();
+
+ // Only clear the CMake configuration for preset kits. Any manual kit configuration
+ // will get the chance to get imported afterwards in the Kit selection wizard
+ CMakeBuildSystem *bs = static_cast<CMakeBuildSystem *>(target->buildSystem());
+ if (!presetItem.isNull() && bs)
+ bs->clearCMakeCache();
+
+ if (!presetItem.isNull() && oldPresets.contains(QString::fromUtf8(presetItem.value)))
+ oldKits << target->kit();
+
+ project->removeTarget(target);
+ }
+
+ project->setOldPresetKits(oldKits);
+
+ Core::ModeManager::activateMode(ProjectExplorer::Constants::MODE_SESSION);
+ Core::ModeManager::setFocusToCurrentMode();
+}
+
void CMakeManager::buildFile(Node *node)
{
if (!node) {
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h
index 3a52709291..c47d724c9b 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h
@@ -30,12 +30,14 @@ private:
void buildFile(ProjectExplorer::Node *node = nullptr);
void updateBuildFileAction();
void enableBuildFileMenus(ProjectExplorer::Node *node);
+ void reloadCMakePresets();
QAction *m_runCMakeAction;
QAction *m_clearCMakeCacheAction;
QAction *m_runCMakeActionContextMenu;
QAction *m_rescanProjectAction;
QAction *m_buildFileContextMenu;
+ QAction *m_reloadCMakePresetsAction;
Utils::ParameterAction *m_buildFileAction;
};
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs
index 89e449ae40..3597a94871 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs
@@ -35,10 +35,6 @@ QtcPlugin {
"cmakefilecompletionassist.h",
"cmakeformatter.cpp",
"cmakeformatter.h",
- "cmakeformatteroptionspage.cpp",
- "cmakeformatteroptionspage.h",
- "cmakeformattersettings.cpp",
- "cmakeformattersettings.h",
"cmakeinstallstep.cpp",
"cmakeinstallstep.h",
"cmakekitinformation.h",
@@ -93,4 +89,18 @@ QtcPlugin {
"projecttreehelper.cpp",
"projecttreehelper.h"
]
+
+ Group {
+ name: "3rdparty"
+ cpp.includePaths: base.concat("3rdparty/cmake")
+
+ prefix: "3rdparty/cmake/"
+ files: [
+ "cmListFileCache.cxx",
+ "cmListFileCache.h",
+ "cmListFileLexer.cxx",
+ "cmListFileLexer.h",
+ "cmStandardLexer.h",
+ ]
+ }
}
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp
index 9c0d687ca6..b26f9a70b5 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp
@@ -30,6 +30,15 @@ CMakeInputsNode::CMakeInputsNode(const FilePath &cmakeLists) :
setListInProject(false);
}
+CMakePresetsNode::CMakePresetsNode(const FilePath &projectPath) :
+ ProjectExplorer::ProjectNode(projectPath)
+{
+ setPriority(Node::DefaultPriority - 9);
+ setDisplayName(Tr::tr("CMake Presets"));
+ setIcon(DirectoryIcon(ProjectExplorer::Constants::FILEOVERLAY_PRODUCT));
+ setListInProject(false);
+}
+
CMakeListsNode::CMakeListsNode(const FilePath &cmakeListPath) :
ProjectExplorer::ProjectNode(cmakeListPath)
{
@@ -198,6 +207,10 @@ void CMakeTargetNode::setTargetInformation(const QList<FilePath> &artifacts, con
m_tooltip += Tr::tr("Build artifacts:") + "<br>" + tmp.join("<br>");
m_artifact = artifacts.first();
}
+ if (type == "EXECUTABLE")
+ setProductType(ProductType::App);
+ else if (type == "SHARED_LIBRARY" || type == "STATIC_LIBRARY")
+ setProductType(ProductType::Lib);
}
} // CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h
index 5d248020ab..94cde6dfef 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h
@@ -15,6 +15,12 @@ public:
CMakeInputsNode(const Utils::FilePath &cmakeLists);
};
+class CMakePresetsNode : public ProjectExplorer::ProjectNode
+{
+public:
+ CMakePresetsNode(const Utils::FilePath &projectPath);
+};
+
class CMakeListsNode : public ProjectExplorer::ProjectNode
{
public:
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp
index bb0270d16e..39e3ec2926 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp
@@ -8,7 +8,6 @@
#include "cmakebuildsystem.h"
#include "cmakeeditor.h"
#include "cmakeformatter.h"
-#include "cmakeformattersettings.h"
#include "cmakeinstallstep.h"
#include "cmakekitinformation.h"
#include "cmakelocatorfilter.h"
@@ -23,7 +22,6 @@
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
-#include <coreplugin/icore.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectmanager.h>
@@ -44,27 +42,9 @@ using namespace Utils;
namespace CMakeProjectManager::Internal {
-bool isAutoFormatApplicable(const Core::IDocument *document, const QStringList &allowedMimeTypes)
-{
- if (!document)
- return false;
-
- if (allowedMimeTypes.isEmpty())
- return true;
-
- const Utils::MimeType documentMimeType = Utils::mimeTypeForName(document->mimeType());
- return Utils::anyOf(allowedMimeTypes, [&documentMimeType](const QString &mime) {
- return documentMimeType.inherits(mime);
- });
-}
-
class CMakeProjectPluginPrivate : public QObject
{
public:
- CMakeProjectPluginPrivate();
- void updateActions(Core::IEditor *editor = nullptr);
- void autoFormatOnSave(Core::IDocument *document);
-
CMakeToolManager cmakeToolManager; // have that before the first CMakeKitAspect
ParameterAction buildTargetContextAction{
@@ -74,15 +54,15 @@ public:
};
CMakeSettingsPage settingsPage;
- CMakeSpecificSettingsPage specificSettings;
+ CMakeSpecificSettings specificSettings;
CMakeManager manager;
CMakeBuildStepFactory buildStepFactory;
CMakeBuildConfigurationFactory buildConfigFactory;
CMakeEditorFactory editorFactor;
CMakeInstallStepFactory installStepFactory;
- BuildCMakeTargetLocatorFilter buildCMakeTargetLocatorFilter;
- OpenCMakeTargetLocatorFilter openCMakeTargetLocationFilter;
+ CMakeBuildTargetFilter cMakeBuildTargetFilter;
+ CMakeOpenTargetFilter cMakeOpenTargetFilter;
CMakeKitAspect cmakeKitAspect;
CMakeGeneratorKitAspect cmakeGeneratorKitAspect;
@@ -91,53 +71,6 @@ public:
CMakeFormatter cmakeFormatter;
};
-CMakeProjectPluginPrivate::CMakeProjectPluginPrivate()
-{
- const Core::EditorManager *editorManager = Core::EditorManager::instance();
- QObject::connect(editorManager, &Core::EditorManager::currentEditorChanged,
- this, &CMakeProjectPluginPrivate::updateActions);
- QObject::connect(editorManager, &Core::EditorManager::aboutToSave,
- this, &CMakeProjectPluginPrivate::autoFormatOnSave);
-}
-
-void CMakeProjectPluginPrivate::updateActions(Core::IEditor *editor)
-{
- cmakeFormatter.updateActions(editor);
-}
-
-void CMakeProjectPluginPrivate::autoFormatOnSave(Core::IDocument *document)
-{
- if (!CMakeFormatterSettings::instance()->autoFormatOnSave())
- return;
-
- if (!isAutoFormatApplicable(document, CMakeFormatterSettings::instance()->autoFormatMime()))
- return;
-
- // Check if file is contained in the current project (if wished)
- if (CMakeFormatterSettings::instance()->autoFormatOnlyCurrentProject()) {
- const ProjectExplorer::Project *pro = ProjectExplorer::ProjectTree::currentProject();
- if (!pro || pro->files([document](const ProjectExplorer::Node *n) {
- return ProjectExplorer::Project::SourceFiles(n)
- && n->filePath() == document->filePath();
- }).isEmpty()) {
- return;
- }
- }
-
- if (!cmakeFormatter.isApplicable(document))
- return;
- const TextEditor::Command command = cmakeFormatter.command();
- if (!command.isValid())
- return;
- const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForDocument(document);
- if (editors.isEmpty())
- return;
- IEditor *currentEditor = EditorManager::currentEditor();
- IEditor *editor = editors.contains(currentEditor) ? currentEditor : editors.first();
- if (auto widget = TextEditor::TextEditorWidget::fromEditor(editor))
- TextEditor::formatEditor(widget, command);
-}
-
CMakeProjectPlugin::~CMakeProjectPlugin()
{
delete d;
@@ -146,7 +79,6 @@ CMakeProjectPlugin::~CMakeProjectPlugin()
void CMakeProjectPlugin::initialize()
{
d = new CMakeProjectPluginPrivate;
- CMakeSpecificSettings::instance()->readSettings(ICore::settings());
const Context projectContext{CMakeProjectManager::Constants::CMAKE_PROJECT_ID};
@@ -179,14 +111,6 @@ void CMakeProjectPlugin::initialize()
bs->buildCMakeTarget(targetNode ? targetNode->displayName() : QString());
}
});
-
- Core::ActionContainer *menu = Core::ActionManager::createMenu(Constants::CMAKEFORMATTER_MENU_ID);
- menu->menu()->setTitle(Tr::tr("CMakeFormatter"));
- menu->setOnAllDisabledBehavior(Core::ActionContainer::Show);
- Core::ActionManager::actionContainer(Core::Constants::M_TOOLS)->addMenu(menu);
-
- d->cmakeFormatter.initialize();
- d->updateActions();
}
void CMakeProjectPlugin::extensionsInitialized()
diff --git a/src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp b/src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp
index b29351d175..1fe948cc72 100644
--- a/src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp
@@ -15,38 +15,53 @@ using namespace Utils;
namespace CMakeProjectManager::Internal {
+static CMakeSpecificSettings *theSettings;
+
+CMakeSpecificSettings *CMakeSpecificSettings::instance()
+{
+ return theSettings;
+}
+
CMakeSpecificSettings::CMakeSpecificSettings()
{
+ theSettings = this;
+
+ setId(Constants::Settings::GENERAL_ID);
+ setDisplayName(::CMakeProjectManager::Tr::tr("General"));
+ setDisplayCategory("CMake");
+ setCategory(Constants::Settings::CATEGORY);
+ setCategoryIconPath(Constants::Icons::SETTINGS_CATEGORY);
+
+ setLayouter([this] {
+ using namespace Layouting;
+ return Column {
+ autorunCMake,
+ packageManagerAutoSetup,
+ askBeforeReConfigureInitialParams,
+ askBeforePresetsReload,
+ showSourceSubFolders,
+ showAdvancedOptionsByDefault,
+ st
+ };
+ });
+
// TODO: fixup of QTCREATORBUG-26289 , remove in Qt Creator 7 or so
Core::ICore::settings()->remove("CMakeSpecificSettings/NinjaPath");
setSettingsGroup("CMakeSpecificSettings");
setAutoApply(false);
- registerAspect(&autorunCMake);
autorunCMake.setSettingsKey("AutorunCMake");
autorunCMake.setDefaultValue(true);
autorunCMake.setLabelText(::CMakeProjectManager::Tr::tr("Autorun CMake"));
autorunCMake.setToolTip(::CMakeProjectManager::Tr::tr(
"Automatically run CMake after changes to CMake project files."));
- registerAspect(&afterAddFileSetting);
- afterAddFileSetting.setSettingsKey("ProjectPopupSetting");
- afterAddFileSetting.setDefaultValue(AfterAddFileAction::AskUser);
- afterAddFileSetting.addOption(::CMakeProjectManager::Tr::tr("Ask about copying file paths"));
- afterAddFileSetting.addOption(::CMakeProjectManager::Tr::tr("Do not copy file paths"));
- afterAddFileSetting.addOption(::CMakeProjectManager::Tr::tr("Copy file paths"));
- afterAddFileSetting.setToolTip(::CMakeProjectManager::Tr::tr("Determines whether file paths are copied "
- "to the clipboard for pasting to the CMakeLists.txt file when you "
- "add new files to CMake projects."));
-
- registerAspect(&ninjaPath);
ninjaPath.setSettingsKey("NinjaPath");
// never save this to the settings:
ninjaPath.setToSettingsTransformation(
[](const QVariant &) { return QVariant::fromValue(QString()); });
- registerAspect(&packageManagerAutoSetup);
packageManagerAutoSetup.setSettingsKey("PackageManagerAutoSetup");
packageManagerAutoSetup.setDefaultValue(true);
packageManagerAutoSetup.setLabelText(::CMakeProjectManager::Tr::tr("Package manager auto setup"));
@@ -54,59 +69,26 @@ CMakeSpecificSettings::CMakeSpecificSettings()
"pointing to a CMake script that will install dependencies from the conanfile.txt, "
"conanfile.py, or vcpkg.json file from the project source directory."));
- registerAspect(&askBeforeReConfigureInitialParams);
askBeforeReConfigureInitialParams.setSettingsKey("AskReConfigureInitialParams");
askBeforeReConfigureInitialParams.setDefaultValue(true);
askBeforeReConfigureInitialParams.setLabelText(::CMakeProjectManager::Tr::tr("Ask before re-configuring with "
"initial parameters"));
- registerAspect(&showSourceSubFolders);
+ askBeforePresetsReload.setSettingsKey("AskBeforePresetsReload");
+ askBeforePresetsReload.setDefaultValue(true);
+ askBeforePresetsReload.setLabelText(::CMakeProjectManager::Tr::tr("Ask before reloading CMake Presets"));
+
showSourceSubFolders.setSettingsKey("ShowSourceSubFolders");
showSourceSubFolders.setDefaultValue(true);
showSourceSubFolders.setLabelText(
::CMakeProjectManager::Tr::tr("Show subfolders inside source group folders"));
- registerAspect(&showAdvancedOptionsByDefault);
showAdvancedOptionsByDefault.setSettingsKey("ShowAdvancedOptionsByDefault");
showAdvancedOptionsByDefault.setDefaultValue(false);
showAdvancedOptionsByDefault.setLabelText(
::CMakeProjectManager::Tr::tr("Show advanced options by default"));
-}
-
-CMakeSpecificSettings *CMakeSpecificSettings::instance()
-{
- static CMakeSpecificSettings theSettings;
- return &theSettings;
-}
-
-// CMakeSpecificSettingsPage
-
-CMakeSpecificSettingsPage::CMakeSpecificSettingsPage()
-{
- CMakeSpecificSettings *settings = CMakeSpecificSettings::instance();
- setId(Constants::Settings::GENERAL_ID);
- setDisplayName(::CMakeProjectManager::Tr::tr("General"));
- setDisplayCategory("CMake");
- setCategory(Constants::Settings::CATEGORY);
- setCategoryIconPath(Constants::Icons::SETTINGS_CATEGORY);
- setSettings(settings);
- setLayouter([settings](QWidget *widget) {
- CMakeSpecificSettings &s = *settings;
- using namespace Layouting;
- Column {
- Group {
- title(::CMakeProjectManager::Tr::tr("Adding Files")),
- Column { s.afterAddFileSetting }
- },
- s.autorunCMake,
- s.packageManagerAutoSetup,
- s.askBeforeReConfigureInitialParams,
- s.showSourceSubFolders,
- s.showAdvancedOptionsByDefault,
- st
- }.attachTo(widget);
- });
+ readSettings();
}
} // CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmakespecificsettings.h b/src/plugins/cmakeprojectmanager/cmakespecificsettings.h
index d0b19bee42..a9c2758e19 100644
--- a/src/plugins/cmakeprojectmanager/cmakespecificsettings.h
+++ b/src/plugins/cmakeprojectmanager/cmakespecificsettings.h
@@ -5,36 +5,22 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
-
namespace CMakeProjectManager::Internal {
-enum AfterAddFileAction : int {
- AskUser,
- CopyFilePath,
- NeverCopyFilePath
-};
-
-class CMakeSpecificSettings final : public Utils::AspectContainer
+class CMakeSpecificSettings final : public Core::PagedSettings
{
public:
CMakeSpecificSettings();
static CMakeSpecificSettings *instance();
- Utils::BoolAspect autorunCMake;
- Utils::SelectionAspect afterAddFileSetting;
- Utils::StringAspect ninjaPath;
- Utils::BoolAspect packageManagerAutoSetup;
- Utils::BoolAspect askBeforeReConfigureInitialParams;
- Utils::BoolAspect showSourceSubFolders;
- Utils::BoolAspect showAdvancedOptionsByDefault;
-};
-
-class CMakeSpecificSettingsPage final : public Core::IOptionsPage
-{
-public:
- CMakeSpecificSettingsPage();
+ Utils::BoolAspect autorunCMake{this};
+ Utils::FilePathAspect ninjaPath{this};
+ Utils::BoolAspect packageManagerAutoSetup{this};
+ Utils::BoolAspect askBeforeReConfigureInitialParams{this};
+ Utils::BoolAspect askBeforePresetsReload{this};
+ Utils::BoolAspect showSourceSubFolders{this};
+ Utils::BoolAspect showAdvancedOptionsByDefault{this};
};
} // CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmaketool.cpp b/src/plugins/cmakeprojectmanager/cmaketool.cpp
index 184811884d..9239ea5417 100644
--- a/src/plugins/cmakeprojectmanager/cmaketool.cpp
+++ b/src/plugins/cmakeprojectmanager/cmaketool.cpp
@@ -10,8 +10,8 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDir>
#include <QJsonDocument>
@@ -162,7 +162,7 @@ bool CMakeTool::isValid() const
return m_introspection->m_didRun && !m_introspection->m_fileApis.isEmpty();
}
-void CMakeTool::runCMake(QtcProcess &cmake, const QStringList &args, int timeoutS) const
+void CMakeTool::runCMake(Process &cmake, const QStringList &args, int timeoutS) const
{
const FilePath executable = cmakeExecutable();
cmake.setTimeoutS(timeoutS);
@@ -248,7 +248,7 @@ TextEditor::Keywords CMakeTool::keywords()
return {};
if (m_introspection->m_functions.isEmpty() && m_introspection->m_didRun) {
- QtcProcess proc;
+ Process proc;
runCMake(proc, {"--help-command-list"}, 5);
if (proc.result() == ProcessResult::FinishedWithSuccess)
m_introspection->m_functions = proc.cleanedStdOut().split('\n');
@@ -492,7 +492,7 @@ QStringList CMakeTool::parseVariableOutput(const QString &output)
void CMakeTool::fetchFromCapabilities() const
{
- QtcProcess cmake;
+ Process cmake;
runCMake(cmake, {"-E", "capabilities"});
if (cmake.result() == ProcessResult::FinishedWithSuccess) {
diff --git a/src/plugins/cmakeprojectmanager/cmaketool.h b/src/plugins/cmakeprojectmanager/cmaketool.h
index 42fa7b8f62..31b9e01627 100644
--- a/src/plugins/cmakeprojectmanager/cmaketool.h
+++ b/src/plugins/cmakeprojectmanager/cmaketool.h
@@ -12,7 +12,7 @@
#include <optional>
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace CMakeProjectManager {
@@ -61,6 +61,8 @@ public:
Utils::Id id() const { return m_id; }
QVariantMap toMap () const;
+ void setAutorun(bool autoRun) { m_isAutoRun = autoRun; }
+
void setFilePath(const Utils::FilePath &executable);
Utils::FilePath filePath() const;
Utils::FilePath cmakeExecutable() const;
@@ -95,7 +97,7 @@ public:
private:
void readInformation() const;
- void runCMake(Utils::QtcProcess &proc, const QStringList &args, int timeoutS = 1) const;
+ void runCMake(Utils::Process &proc, const QStringList &args, int timeoutS = 1) const;
void parseFunctionDetailsOutput(const QString &output);
QStringList parseVariableOutput(const QString &output);
diff --git a/src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp b/src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp
index 8bc396ced1..7e4c777317 100644
--- a/src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp
+++ b/src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp
@@ -141,6 +141,7 @@ void CMakeToolManager::restoreCMakeTools()
emit m_instance->cmakeToolsLoaded();
// Store the default CMake tool "Autorun CMake" value globally
+ // TODO: Remove in Qt Creator 13
auto settings = Internal::CMakeSpecificSettings::instance();
if (settings->autorunCMake.value() == settings->autorunCMake.defaultValue()) {
CMakeTool *cmake = defaultCMakeTool();
diff --git a/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp b/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp
index 43f1915709..05969b492e 100644
--- a/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp
+++ b/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp
@@ -4,6 +4,7 @@
#include "cmaketoolsettingsaccessor.h"
#include "cmakeprojectmanagertr.h"
+#include "cmakespecificsettings.h"
#include "cmaketool.h"
#include <coreplugin/icore.h>
@@ -44,36 +45,26 @@ const char CMAKE_TOOL_FILENAME[] = "cmaketools.xml";
static std::vector<std::unique_ptr<CMakeTool>> autoDetectCMakeTools()
{
- Environment env = Environment::systemEnvironment();
-
- FilePaths path = env.path();
- path = Utils::filteredUnique(path);
+ FilePaths extraDirs;
if (HostOsInfo::isWindowsHost()) {
for (const auto &envVar : QStringList{"ProgramFiles", "ProgramFiles(x86)", "ProgramW6432"}) {
if (qtcEnvironmentVariableIsSet(envVar)) {
const QString progFiles = qtcEnvironmentVariable(envVar);
- path.append(FilePath::fromUserInput(progFiles + "/CMake"));
- path.append(FilePath::fromUserInput(progFiles + "/CMake/bin"));
+ extraDirs.append(FilePath::fromUserInput(progFiles + "/CMake"));
+ extraDirs.append(FilePath::fromUserInput(progFiles + "/CMake/bin"));
}
}
}
if (HostOsInfo::isMacHost()) {
- path.append("/Applications/CMake.app/Contents/bin");
- path.append("/usr/local/bin"); // homebrew intel
- path.append("/opt/homebrew/bin"); // homebrew arm
- path.append("/opt/local/bin"); // macports
+ extraDirs.append("/Applications/CMake.app/Contents/bin");
+ extraDirs.append("/usr/local/bin"); // homebrew intel
+ extraDirs.append("/opt/homebrew/bin"); // homebrew arm
+ extraDirs.append("/opt/local/bin"); // macports
}
- FilePaths suspects;
- for (const FilePath &base : std::as_const(path)) {
- if (base.isEmpty())
- continue;
- const FilePath suspect = base / "cmake";
- if (std::optional<FilePath> foundExe = suspect.refersToExecutableFile(FilePath::WithAnySuffix))
- suspects << *foundExe;
- }
+ const FilePaths suspects = FilePath("cmake").searchAllInPath(extraDirs);
std::vector<std::unique_ptr<CMakeTool>> found;
for (const FilePath &command : std::as_const(suspects)) {
@@ -138,11 +129,10 @@ mergeTools(std::vector<std::unique_ptr<CMakeTool>> &sdkTools,
// CMakeToolSettingsAccessor:
// --------------------------------------------------------------------
-CMakeToolSettingsAccessor::CMakeToolSettingsAccessor() :
- UpgradingSettingsAccessor("QtCreatorCMakeTools",
- Tr::tr("CMake"),
- Core::Constants::IDE_DISPLAY_NAME)
+CMakeToolSettingsAccessor::CMakeToolSettingsAccessor()
{
+ setDocType("QtCreatorCMakeTools");
+ setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME);
setBaseFilePath(Core::ICore::userResourcePath(CMAKE_TOOL_FILENAME));
addVersionUpgrader(std::make_unique<CMakeToolSettingsUpgraderV0>());
@@ -185,9 +175,14 @@ void CMakeToolSettingsAccessor::saveCMakeTools(const QList<CMakeTool *> &cmakeTo
data.insert(QLatin1String(CMAKE_TOOL_DEFAULT_KEY), defaultId.toSetting());
int count = 0;
- for (const CMakeTool *item : cmakeTools) {
+ for (CMakeTool *item : cmakeTools) {
Utils::FilePath fi = item->cmakeExecutable();
+ // Gobal Autorun value will be set for all tools
+ // TODO: Remove in Qt Creator 13
+ const auto settings = CMakeSpecificSettings::instance();
+ item->setAutorun(settings->autorunCMake.value());
+
if (fi.needsDevice() || fi.isExecutableFile()) { // be graceful for device related stuff
QVariantMap tmp = item->toMap();
if (tmp.isEmpty())
diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
index 13a1510ebd..1ef7172168 100644
--- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
+++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
@@ -3,6 +3,7 @@
#include "fileapidataextractor.h"
+#include "cmakeprojectconstants.h"
#include "cmakeprojectmanagertr.h"
#include "cmakeprojectplugin.h"
#include "cmakespecificsettings.h"
@@ -13,8 +14,8 @@
#include <utils/algorithm.h>
#include <utils/mimeutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/utilsicons.h>
#include <projectexplorer/projecttree.h>
@@ -27,6 +28,8 @@ using namespace CMakeProjectManager::Internal::FileApiDetails;
namespace CMakeProjectManager::Internal {
+static Q_LOGGING_CATEGORY(cmakeLogger, "qtc.cmake.fileApiExtractor", QtWarningMsg);
+
// --------------------------------------------------------------------
// Helpers:
// --------------------------------------------------------------------
@@ -53,7 +56,24 @@ CMakeFileResult extractCMakeFilesData(const std::vector<CMakeFileInfo> &cmakefil
const int oldCount = result.cmakeFiles.count();
CMakeFileInfo absolute(info);
absolute.path = sfn;
+
+ const auto mimeType = Utils::mimeTypeForFile(info.path);
+ if (mimeType.matchesName(Constants::CMAKE_MIMETYPE)
+ || mimeType.matchesName(Constants::CMAKE_PROJECT_MIMETYPE)) {
+ expected_str<QByteArray> fileContent = sfn.fileContents();
+ std::string errorString;
+ if (fileContent) {
+ fileContent = fileContent->replace("\r\n", "\n");
+ if (!absolute.cmakeListFile.ParseString(fileContent->toStdString(),
+ sfn.fileName().toStdString(),
+ errorString))
+ qCWarning(cmakeLogger)
+ << "Failed to parse:" << sfn.path() << QString::fromLatin1(errorString);
+ }
+ }
+
result.cmakeFiles.insert(absolute);
+
if (oldCount < result.cmakeFiles.count()) {
if (info.isCMake && !info.isCMakeListsDotTxt) {
// Skip files that cmake considers to be part of the installation -- but include
@@ -245,7 +265,7 @@ QList<CMakeBuildTarget> generateBuildTargets(const PreprocessedData &input,
continue;
// CMake sometimes mixes several shell-escaped pieces into one fragment. Disentangle that again:
- const QStringList parts = ProcessArgs::splitArgs(f.fragment);
+ const QStringList parts = ProcessArgs::splitArgs(f.fragment, HostOsInfo::hostOs());
for (QString part : parts) {
// Library search paths that are added with target_link_directories are added as
// -LIBPATH:... (Windows/MSVC), or
@@ -264,7 +284,7 @@ QList<CMakeBuildTarget> generateBuildTargets(const PreprocessedData &input,
continue;
const FilePath buildDir = haveLibrariesRelativeToBuildDirectory ? buildDirectory : currentBuildDir;
- FilePath tmp = buildDir.resolvePath(FilePath::fromUserInput(part).onDevice(buildDir));
+ FilePath tmp = buildDir.resolvePath(part);
if (f.role == "libraries")
tmp = tmp.parentDir();
@@ -306,7 +326,7 @@ static QStringList splitFragments(const QStringList &fragments)
{
QStringList result;
for (const QString &f : fragments) {
- result += ProcessArgs::splitArgs(f);
+ result += ProcessArgs::splitArgs(f, HostOsInfo::hostOs());
}
return result;
}
@@ -497,11 +517,8 @@ FolderNode *createSourceGroupNode(const QString &sourceGroupName,
const QStringList parts = sourceGroupName.split("\\");
for (const QString &p : parts) {
- FolderNode *existingNode = Utils::findOrDefault(currentNode->folderNodes(),
- [&p](const FolderNode *fn) {
- return fn->displayName() == p;
- });
-
+ FolderNode *existingNode = currentNode->findChildFolderNode(
+ [&p](const FolderNode *fn) { return fn->displayName() == p; });
if (!existingNode) {
auto node = createCMakeVFolder(sourceDirectory, Node::DefaultFolderPriority + 5, p);
node->setListInProject(false);
@@ -605,6 +622,28 @@ void addCompileGroups(ProjectNode *targetRoot,
std::move(otherFileNodes));
}
+static void addGeneratedFilesNode(ProjectNode *targetRoot, const FilePath &topLevelBuildDir,
+ const TargetDetails &td)
+{
+ if (td.artifacts.isEmpty())
+ return;
+ FileType type = FileType::Unknown;
+ if (td.type == "EXECUTABLE")
+ type = FileType::App;
+ else if (td.type == "SHARED_LIBRARY" || td.type == "STATIC_LIBRARY")
+ type = FileType::Lib;
+ if (type == FileType::Unknown)
+ return;
+ std::vector<std::unique_ptr<FileNode>> nodes;
+ const FilePath buildDir = topLevelBuildDir.resolvePath(td.buildDir);
+ for (const FilePath &artifact : td.artifacts) {
+ nodes.emplace_back(new FileNode(buildDir.resolvePath(artifact), type));
+ type = FileType::Unknown;
+ nodes.back()->setIsGenerated(true);
+ }
+ addCMakeVFolder(targetRoot, buildDir, 10, Tr::tr("<Generated Files>"), std::move(nodes));
+}
+
void addTargets(const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cmakeListsNodes,
const Configuration &config,
const std::vector<TargetDetails> &targetDetails,
@@ -635,6 +674,7 @@ void addTargets(const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cm
tNode->setBuildDirectory(directoryBuildDir(config, buildDir, t.directory));
addCompileGroups(tNode, sourceDir, dir, tNode->buildDirectory(), td);
+ addGeneratedFilesNode(tNode, buildDir, td);
}
}
@@ -671,6 +711,9 @@ std::unique_ptr<CMakeProjectNode> generateRootProjectNode(
std::move(data.cmakeNodesBuild),
std::move(data.cmakeNodesOther));
+
+ addCMakePresets(result.get(), sourceDirectory);
+
data.cmakeNodesSource.clear(); // Remove all the nullptr in the vector...
data.cmakeNodesBuild.clear(); // Remove all the nullptr in the vector...
data.cmakeNodesOther.clear(); // Remove all the nullptr in the vector...
diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.h b/src/plugins/cmakeprojectmanager/fileapidataextractor.h
index 04f4ecea43..e4ed3aa778 100644
--- a/src/plugins/cmakeprojectmanager/fileapidataextractor.h
+++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.h
@@ -5,6 +5,7 @@
#include "cmakebuildtarget.h"
#include "cmakeprojectnodes.h"
+#include "3rdparty/cmake/cmListFileCache.h"
#include <projectexplorer/rawprojectpart.h>
@@ -32,6 +33,7 @@ public:
bool isCMakeListsDotTxt = false;
bool isExternal = false;
bool isGenerated = false;
+ cmListFile cmakeListFile;
};
class FileApiQtcData
diff --git a/src/plugins/cmakeprojectmanager/fileapiparser.cpp b/src/plugins/cmakeprojectmanager/fileapiparser.cpp
index 0f25f27580..e4dca5c9b2 100644
--- a/src/plugins/cmakeprojectmanager/fileapiparser.cpp
+++ b/src/plugins/cmakeprojectmanager/fileapiparser.cpp
@@ -16,6 +16,7 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QLoggingCategory>
+#include <QPromise>
using namespace Utils;
@@ -825,7 +826,7 @@ static QStringList uniqueTargetFiles(const Configuration &config)
return files;
}
-FileApiData FileApiParser::parseData(QFutureInterface<std::shared_ptr<FileApiQtcData>> &fi,
+FileApiData FileApiParser::parseData(QPromise<std::shared_ptr<FileApiQtcData>> &promise,
const FilePath &replyFilePath,
const QString &cmakeBuildType,
QString &errorMessage)
@@ -836,8 +837,8 @@ FileApiData FileApiParser::parseData(QFutureInterface<std::shared_ptr<FileApiQtc
FileApiData result;
- const auto cancelCheck = [&fi, &errorMessage] {
- if (fi.isCanceled()) {
+ const auto cancelCheck = [&promise, &errorMessage] {
+ if (promise.isCanceled()) {
errorMessage = Tr::tr("CMake parsing was canceled.");
return true;
}
diff --git a/src/plugins/cmakeprojectmanager/fileapiparser.h b/src/plugins/cmakeprojectmanager/fileapiparser.h
index 14ac55873b..1d7c5d5ab3 100644
--- a/src/plugins/cmakeprojectmanager/fileapiparser.h
+++ b/src/plugins/cmakeprojectmanager/fileapiparser.h
@@ -14,13 +14,17 @@
#include <utils/fileutils.h>
#include <QDir>
-#include <QFutureInterface>
#include <QString>
#include <QVector>
#include <QVersionNumber>
#include <vector>
+QT_BEGIN_NAMESPACE
+template <typename Ret>
+class QPromise;
+QT_END_NAMESPACE
+
namespace CMakeProjectManager::Internal {
namespace FileApiDetails {
@@ -218,7 +222,7 @@ public:
class FileApiParser
{
public:
- static FileApiData parseData(QFutureInterface<std::shared_ptr<FileApiQtcData>> &fi,
+ static FileApiData parseData(QPromise<std::shared_ptr<FileApiQtcData>> &promise,
const Utils::FilePath &replyFilePath,
const QString &cmakeBuildType,
QString &errorMessage);
diff --git a/src/plugins/cmakeprojectmanager/fileapireader.cpp b/src/plugins/cmakeprojectmanager/fileapireader.cpp
index c6ee73d6ab..fd507f7bf7 100644
--- a/src/plugins/cmakeprojectmanager/fileapireader.cpp
+++ b/src/plugins/cmakeprojectmanager/fileapireader.cpp
@@ -5,7 +5,6 @@
#include "cmakeprocess.h"
#include "cmakeprojectmanagertr.h"
-#include "cmakeprojectplugin.h"
#include "cmakespecificsettings.h"
#include "fileapidataextractor.h"
#include "fileapiparser.h"
@@ -15,8 +14,8 @@
#include <projectexplorer/projectexplorer.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <QLoggingCategory>
@@ -166,20 +165,19 @@ bool FileApiReader::isParsing() const
return m_isParsing;
}
-QSet<FilePath> FileApiReader::projectFilesToWatch() const
-{
- return Utils::transform(
- Utils::filtered(m_cmakeFiles,
- [](const CMakeFileInfo &info) { return !info.isGenerated; }),
- [](const CMakeFileInfo &info) { return info.path;});
-}
-
QList<CMakeBuildTarget> FileApiReader::takeBuildTargets(QString &errorMessage){
Q_UNUSED(errorMessage)
return std::exchange(m_buildTargets, {});
}
+QSet<CMakeFileInfo> FileApiReader::takeCMakeFileInfos(QString &errorMessage)
+{
+ Q_UNUSED(errorMessage)
+
+ return std::exchange(m_cmakeFiles, {});
+}
+
CMakeConfig FileApiReader::takeParsedConfiguration(QString &errorMessage)
{
if (m_lastCMakeExitCode != 0)
@@ -235,11 +233,11 @@ void FileApiReader::endState(const FilePath &replyFilePath, bool restoredFromBac
m_lastReplyTimestamp = replyFilePath.lastModified();
- m_future = runAsync(ProjectExplorerPlugin::sharedThreadPool(),
+ m_future = Utils::asyncRun(ProjectExplorerPlugin::sharedThreadPool(),
[replyFilePath, sourceDirectory, buildDirectory, cmakeBuildType](
- QFutureInterface<std::shared_ptr<FileApiQtcData>> &fi) {
+ QPromise<std::shared_ptr<FileApiQtcData>> &promise) {
auto result = std::make_shared<FileApiQtcData>();
- FileApiData data = FileApiParser::parseData(fi,
+ FileApiData data = FileApiParser::parseData(promise,
replyFilePath,
cmakeBuildType,
result->errorMessage);
@@ -248,7 +246,7 @@ void FileApiReader::endState(const FilePath &replyFilePath, bool restoredFromBac
else
qWarning() << result->errorMessage;
- fi.reportResult(result);
+ promise.addResult(result);
});
onResultReady(m_future.value(),
this,
diff --git a/src/plugins/cmakeprojectmanager/fileapireader.h b/src/plugins/cmakeprojectmanager/fileapireader.h
index c854ce2040..115d22ea71 100644
--- a/src/plugins/cmakeprojectmanager/fileapireader.h
+++ b/src/plugins/cmakeprojectmanager/fileapireader.h
@@ -44,8 +44,8 @@ public:
bool isParsing() const;
- QSet<Utils::FilePath> projectFilesToWatch() const;
QList<CMakeBuildTarget> takeBuildTargets(QString &errorMessage);
+ QSet<CMakeFileInfo> takeCMakeFileInfos(QString &errorMessage);
CMakeConfig takeParsedConfiguration(QString &errorMessage);
QString ctestPath() const;
ProjectExplorer::RawProjectParts createRawProjectParts(QString &errorMessage);
diff --git a/src/plugins/cmakeprojectmanager/presetsmacros.cpp b/src/plugins/cmakeprojectmanager/presetsmacros.cpp
index af5524216f..220d6d6575 100644
--- a/src/plugins/cmakeprojectmanager/presetsmacros.cpp
+++ b/src/plugins/cmakeprojectmanager/presetsmacros.cpp
@@ -10,6 +10,8 @@
#include <utils/hostosinfo.h>
#include <utils/osspecificaspects.h>
+using namespace Utils;
+
namespace CMakeProjectManager::Internal::CMakePresets::Macros {
static QString getHostSystemName(Utils::OsType osType)
@@ -61,6 +63,7 @@ static void expandAllButEnv(const PresetsDetails::BuildPreset &preset,
value.replace("${sourceDirName}", sourceDirectory.fileName());
value.replace("${presetName}", preset.name);
+ value.replace("${hostSystemName}", getHostSystemName(sourceDirectory.osType()));
value.replace("${pathListSep}",
Utils::OsSpecificAspects::pathListSeparator(sourceDirectory.osType()));
}
@@ -107,32 +110,26 @@ static QString expandMacroEnv(const QString &macroPrefix,
return result;
}
-static Utils::Environment getEnvCombined(const std::optional<Utils::Environment> &optPresetEnv,
- const Utils::Environment &env)
+static Environment getEnvCombined(const std::optional<Environment> &optPresetEnv,
+ const Environment &env)
{
- Utils::Environment result = env;
+ Environment result = env;
- if (!optPresetEnv)
- return result;
-
- Utils::Environment presetEnv = optPresetEnv.value();
- for (auto it = presetEnv.constBegin(); it != presetEnv.constEnd(); ++it) {
- result.set(it.key().name, it.value().first);
+ if (optPresetEnv) {
+ optPresetEnv->forEachEntry([&result](const QString &key, const QString &value, bool) {
+ result.set(key, value);
+ });
}
return result;
}
template<class PresetType>
-void expand(const PresetType &preset,
- Utils::Environment &env,
- const Utils::FilePath &sourceDirectory)
+void expand(const PresetType &preset, Environment &env, const FilePath &sourceDirectory)
{
- const Utils::Environment presetEnv = getEnvCombined(preset.environment, env);
- for (auto it = presetEnv.constBegin(); it != presetEnv.constEnd(); ++it) {
- const QString key = it.key().name;
- QString value = it.value().first;
-
+ const Environment presetEnv = getEnvCombined(preset.environment, env);
+ presetEnv.forEachEntry([&](const QString &key, const QString &value_, bool) {
+ QString value = value_;
expandAllButEnv(preset, sourceDirectory, value);
value = expandMacroEnv("env", value, [presetEnv](const QString &macroName) {
return presetEnv.value(macroName);
@@ -141,7 +138,7 @@ void expand(const PresetType &preset,
QString sep;
bool append = true;
if (key.compare("PATH", Qt::CaseInsensitive) == 0) {
- sep = Utils::OsSpecificAspects::pathListSeparator(env.osType());
+ sep = OsSpecificAspects::pathListSeparator(env.osType());
const int index = value.indexOf("$penv{PATH}", 0, Qt::CaseInsensitive);
if (index != 0)
append = false;
@@ -159,20 +156,15 @@ void expand(const PresetType &preset,
env.appendOrSet(key, value, sep);
else
env.prependOrSet(key, value, sep);
- }
+ });
}
template<class PresetType>
-void expand(const PresetType &preset,
- Utils::EnvironmentItems &envItems,
- const Utils::FilePath &sourceDirectory)
+void expand(const PresetType &preset, EnvironmentItems &envItems, const FilePath &sourceDirectory)
{
- const Utils::Environment presetEnv = preset.environment ? preset.environment.value()
- : Utils::Environment();
- for (auto it = presetEnv.constBegin(); it != presetEnv.constEnd(); ++it) {
- const QString key = it.key().name;
- QString value = it.value().first;
-
+ const Environment presetEnv = preset.environment ? *preset.environment : Environment();
+ presetEnv.forEachEntry([&](const QString &key, const QString &value_, bool) {
+ QString value = value_;
expandAllButEnv(preset, sourceDirectory, value);
value = expandMacroEnv("env", value, [presetEnv](const QString &macroName) {
if (presetEnv.hasKey(macroName))
@@ -180,12 +172,12 @@ void expand(const PresetType &preset,
return QString("${%1}").arg(macroName);
});
- auto operation = Utils::EnvironmentItem::Operation::SetEnabled;
+ auto operation = EnvironmentItem::Operation::SetEnabled;
if (key.compare("PATH", Qt::CaseInsensitive) == 0) {
- operation = Utils::EnvironmentItem::Operation::Append;
+ operation = EnvironmentItem::Operation::Append;
const int index = value.indexOf("$penv{PATH}", 0, Qt::CaseInsensitive);
if (index != 0)
- operation = Utils::EnvironmentItem::Operation::Prepend;
+ operation = EnvironmentItem::Operation::Prepend;
value.replace("$penv{PATH}", "", Qt::CaseInsensitive);
}
@@ -197,7 +189,7 @@ void expand(const PresetType &preset,
expandAllButEnv(preset, sourceDirectory, value);
envItems.emplace_back(Utils::EnvironmentItem(key, value, operation));
- }
+ });
}
template<class PresetType>
diff --git a/src/plugins/cmakeprojectmanager/presetsparser.cpp b/src/plugins/cmakeprojectmanager/presetsparser.cpp
index 5ebd17fd63..f3be87f7ea 100644
--- a/src/plugins/cmakeprojectmanager/presetsparser.cpp
+++ b/src/plugins/cmakeprojectmanager/presetsparser.cpp
@@ -494,16 +494,6 @@ static QHash<QString, QString> merge(const QHash<QString, QString> &first,
return result;
}
-static Utils::Environment merge(const Utils::Environment &first, const Utils::Environment &second)
-{
- Utils::Environment result = first;
- for (auto it = second.constBegin(); it != second.constEnd(); ++it) {
- result.set(it.key().name, it.value().first);
- }
-
- return result;
-}
-
static CMakeConfig merge(const CMakeConfig &first, const CMakeConfig &second)
{
return Utils::setUnionMerge<CMakeConfig>(
@@ -561,7 +551,7 @@ void PresetsDetails::ConfigurePreset::inheritFrom(const ConfigurePreset &other)
if (!environment && other.environment)
environment = other.environment;
else if (environment && other.environment)
- environment = merge(other.environment.value(), environment.value());
+ environment = environment.value().appliedToEnvironment(other.environment.value());
if (!warnings && other.warnings)
warnings = other.warnings;
@@ -587,7 +577,7 @@ void PresetsDetails::BuildPreset::inheritFrom(const BuildPreset &other)
if (!environment && other.environment)
environment = other.environment;
else if (environment && other.environment)
- environment = merge(other.environment.value(), environment.value());
+ environment = environment.value().appliedToEnvironment(other.environment.value());
if (!configurePreset && other.configurePreset)
configurePreset = other.configurePreset;
diff --git a/src/plugins/cmakeprojectmanager/projecttreehelper.cpp b/src/plugins/cmakeprojectmanager/projecttreehelper.cpp
index 3fb89d9fec..7b9efb07b9 100644
--- a/src/plugins/cmakeprojectmanager/projecttreehelper.cpp
+++ b/src/plugins/cmakeprojectmanager/projecttreehelper.cpp
@@ -3,9 +3,11 @@
#include "projecttreehelper.h"
+#include "cmakeproject.h"
#include "cmakeprojectmanagertr.h"
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/algorithm.h>
#include <utils/fsengine/fileiconprovider.h>
@@ -42,8 +44,7 @@ void addCMakeVFolder(FolderNode *base,
base->addNode(std::move(newFolder));
}
folder->addNestedNodes(std::move(files));
- for (FolderNode *fn : folder->folderNodes())
- fn->compress();
+ folder->forEachFolderNode([] (FolderNode *fn) { fn->compress(); });
}
std::vector<std::unique_ptr<FileNode>> &&removeKnownNodes(
@@ -89,6 +90,34 @@ void addCMakeInputs(FolderNode *root,
root->addNode(std::move(cmakeVFolder));
}
+void addCMakePresets(FolderNode *root, const Utils::FilePath &sourceDir)
+{
+ QStringList presetFileNames;
+ presetFileNames << "CMakePresets.json";
+ presetFileNames << "CMakeUserPresets.json";
+
+ const CMakeProject *cp = static_cast<const CMakeProject *>(
+ ProjectManager::projectForFile(sourceDir.pathAppended("CMakeLists.txt")));
+
+ if (cp && cp->presetsData().include)
+ presetFileNames.append(cp->presetsData().include.value());
+
+ std::vector<std::unique_ptr<FileNode>> presets;
+ for (const auto &fileName : presetFileNames) {
+ Utils::FilePath file = sourceDir.pathAppended(fileName);
+ if (file.exists())
+ presets.push_back(std::make_unique<FileNode>(file, Node::fileTypeForFileName(file)));
+ }
+
+ if (presets.empty())
+ return;
+
+ std::unique_ptr<ProjectNode> cmakeVFolder = std::make_unique<CMakePresetsNode>(root->filePath());
+ addCMakeVFolder(cmakeVFolder.get(), sourceDir, 1000, QString(), std::move(presets));
+
+ root->addNode(std::move(cmakeVFolder));
+}
+
QHash<Utils::FilePath, ProjectNode *> addCMakeLists(
CMakeProjectNode *root, std::vector<std::unique_ptr<FileNode>> &&cmakeLists)
{
diff --git a/src/plugins/cmakeprojectmanager/projecttreehelper.h b/src/plugins/cmakeprojectmanager/projecttreehelper.h
index 66cde4aea9..34a8723759 100644
--- a/src/plugins/cmakeprojectmanager/projecttreehelper.h
+++ b/src/plugins/cmakeprojectmanager/projecttreehelper.h
@@ -30,6 +30,8 @@ void addCMakeInputs(ProjectExplorer::FolderNode *root,
std::vector<std::unique_ptr<ProjectExplorer::FileNode>> &&buildInputs,
std::vector<std::unique_ptr<ProjectExplorer::FileNode>> &&rootInputs);
+void addCMakePresets(ProjectExplorer::FolderNode *root, const Utils::FilePath &sourceDir);
+
QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> addCMakeLists(
CMakeProjectNode *root, std::vector<std::unique_ptr<ProjectExplorer::FileNode>> &&cmakeLists);
diff --git a/src/plugins/coco/cocoplugin.cpp b/src/plugins/coco/cocoplugin.cpp
index abea861af1..f192b70a62 100644
--- a/src/plugins/coco/cocoplugin.cpp
+++ b/src/plugins/coco/cocoplugin.cpp
@@ -40,12 +40,13 @@ void CocoPluginPrivate::startCoco()
auto layout = new QFormLayout();
const Environment env = Environment::systemEnvironment();
- FilePath squishCocoPath = FilePath::fromString(env.value("SQUISHCOCO"));
- const FilePaths candidates = env.findAllInPath("coveragebrowser", {squishCocoPath});
+ const FilePath squishCocoPath = FilePath::fromUserInput(env.value("SQUISHCOCO"));
+ const FilePath candidate = FilePath("coveragebrowser").searchInPath({squishCocoPath},
+ FilePath::PrependToPath);
PathChooser cocoChooser;
- if (!candidates.isEmpty())
- cocoChooser.setFilePath(candidates.first());
+ if (!candidate.isEmpty())
+ cocoChooser.setFilePath(candidate);
cocoChooser.setExpectedKind(PathChooser::Command);
cocoChooser.setPromptDialogTitle(Tr::tr("Select a Squish Coco CoverageBrowser Executable"));
diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp
index 051552d16c..ad614e7e23 100644
--- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp
+++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp
@@ -148,8 +148,8 @@ void addDriverModeFlagIfNeeded(const ToolChain *toolchain,
RawProjectPart makeRawProjectPart(const Utils::FilePath &projectFile,
Kit *kit,
ProjectExplorer::KitInfo &kitInfo,
- const QString &workingDir,
- const Utils::FilePath &fileName,
+ const FilePath &workingDir,
+ const FilePath &filePath,
QStringList flags)
{
HeaderPaths headerPaths;
@@ -157,7 +157,7 @@ RawProjectPart makeRawProjectPart(const Utils::FilePath &projectFile,
CppEditor::ProjectFile::Kind fileKind = CppEditor::ProjectFile::Unclassified;
const QStringList originalFlags = flags;
- filteredFlags(fileName.fileName(),
+ filteredFlags(filePath,
workingDir,
flags,
headerPaths,
@@ -166,10 +166,12 @@ RawProjectPart makeRawProjectPart(const Utils::FilePath &projectFile,
kitInfo.sysRootPath);
RawProjectPart rpp;
+
rpp.setProjectFileLocation(projectFile.toString());
- rpp.setBuildSystemTarget(workingDir);
- rpp.setDisplayName(fileName.fileName());
- rpp.setFiles({fileName.toString()});
+ rpp.setBuildSystemTarget(workingDir.path());
+ rpp.setDisplayName(filePath.fileName());
+ rpp.setFiles({filePath.toFSPathString()});
+
rpp.setHeaderPaths(headerPaths);
rpp.setMacros(macros);
@@ -221,13 +223,10 @@ FolderNode *addChildFolderNode(FolderNode *parent, const QString &childName)
FolderNode *addOrGetChildFolderNode(FolderNode *parent, const QString &childName)
{
- for (FolderNode *folder : parent->folderNodes()) {
- if (folder->filePath().fileName() == childName) {
- return folder;
- }
- }
-
- return addChildFolderNode(parent, childName);
+ FolderNode *fn = parent->findChildFolderNode([&](FolderNode *folder) {
+ return folder->filePath().fileName() == childName;
+ });
+ return fn ? fn : addChildFolderNode(parent, childName);
}
// Return the node for folderPath.
diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs
index 9c12b05a3a..ce7e4e35ac 100644
--- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs
+++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs
@@ -21,9 +21,7 @@ QtcPlugin {
"compilationdbparser.h",
]
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
"compilationdatabasetests.cpp",
"compilationdatabasetests.h",
diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp
index fc1da70ca7..13f03c0ed3 100644
--- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp
+++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp
@@ -15,7 +15,7 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/fsengine/fileiconprovider.h>
#include <utils/parameteraction.h>
@@ -72,7 +72,7 @@ void CompilationDatabaseProjectManagerPlugin::initialize()
d->changeRootAction.setEnabled(currentProject);
};
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, onProjectChanged);
connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged,
diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp
index 6fff494764..f8b2574771 100644
--- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp
+++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp
@@ -92,7 +92,9 @@ public:
QStringList getFilteredFlags()
{
- filteredFlags(fileName, workingDir, flags, headerPaths, macros, fileKind, sysRoot);
+ filteredFlags(FilePath::fromString(fileName),
+ FilePath::fromString(workingDir),
+ flags, headerPaths, macros, fileKind, sysRoot);
return flags;
}
diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp
index df8e08a68f..be0ab87e54 100644
--- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp
+++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp
@@ -22,15 +22,6 @@ using namespace Utils;
namespace CompilationDatabaseProjectManager {
namespace Internal {
-static QString updatedPathFlag(const QString &pathStr, const QString &workingDir)
-{
- QString result = pathStr;
- if (QDir(pathStr).isRelative())
- result = workingDir + "/" + pathStr;
-
- return result;
-}
-
static CppEditor::ProjectFile::Kind fileKindFromString(QString flag)
{
using namespace CppEditor;
@@ -86,13 +77,13 @@ QStringList filterFromFileName(const QStringList &flags, QString fileName)
return result;
}
-void filteredFlags(const QString &fileName,
- const QString &workingDir,
+void filteredFlags(const FilePath &filePath,
+ const FilePath &workingDir,
QStringList &flags,
HeaderPaths &headerPaths,
Macros &macros,
CppEditor::ProjectFile::Kind &fileKind,
- Utils::FilePath &sysRoot)
+ FilePath &sysRoot)
{
if (flags.empty())
return;
@@ -113,7 +104,7 @@ void filteredFlags(const QString &fileName,
}
if (includePathType) {
- const QString pathStr = updatedPathFlag(flag, workingDir);
+ const QString pathStr = workingDir.resolvePath(flag).toString();
headerPaths.append({pathStr, includePathType.value()});
includePathType.reset();
continue;
@@ -145,14 +136,14 @@ void filteredFlags(const QString &fileName,
continue;
}
- const QStringList userIncludeFlags{"-I", "/I"};
- const QStringList systemIncludeFlags{"-isystem", "-imsvc", "/imsvc"};
+ const QStringList userIncludeFlags{"-I", "-iquote", "/I"};
+ const QStringList systemIncludeFlags{"-isystem", "-idirafter", "-imsvc", "/imsvc"};
const QStringList allIncludeFlags = QStringList(userIncludeFlags) << systemIncludeFlags;
const QString includeOpt = Utils::findOrDefault(allIncludeFlags, [flag](const QString &opt) {
return flag.startsWith(opt) && flag != opt;
});
if (!includeOpt.isEmpty()) {
- const QString pathStr = updatedPathFlag(flag.mid(includeOpt.length()), workingDir);
+ const QString pathStr = workingDir.resolvePath(flag.mid(includeOpt.length())).toString();
headerPaths.append({pathStr, userIncludeFlags.contains(includeOpt)
? HeaderPathType::User : HeaderPathType::System});
continue;
@@ -182,14 +173,14 @@ void filteredFlags(const QString &fileName,
if (flag.startsWith("--sysroot=")) {
if (sysRoot.isEmpty())
- sysRoot = FilePath::fromUserInput(updatedPathFlag(flag.mid(10), workingDir));
+ sysRoot = workingDir.resolvePath(flag.mid(10));
continue;
}
if ((flag.startsWith("-std=") || flag.startsWith("/std:"))
&& fileKind == CppEditor::ProjectFile::Unclassified) {
const bool cpp = (flag.contains("c++") || flag.contains("gnu++"));
- if (CppEditor::ProjectFile::isHeader(CppEditor::ProjectFile::classify(fileName)))
+ if (CppEditor::ProjectFile::isHeader(CppEditor::ProjectFile::classify(filePath.path())))
fileKind = cpp ? CppEditor::ProjectFile::CXXHeader : CppEditor::ProjectFile::CHeader;
else
fileKind = cpp ? CppEditor::ProjectFile::CXXSource : CppEditor::ProjectFile::CSource;
@@ -203,7 +194,7 @@ void filteredFlags(const QString &fileName,
}
if (fileKind == CppEditor::ProjectFile::Unclassified)
- fileKind = CppEditor::ProjectFile::classify(fileName);
+ fileKind = CppEditor::ProjectFile::classify(filePath.path());
flags = filtered;
}
diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h
index 79130799b4..ec024443d8 100644
--- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h
+++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h
@@ -4,10 +4,8 @@
#pragma once
#include <cppeditor/cppprojectfile.h>
-#include <utils/filepath.h>
#include <QHash>
-#include <QStringList>
namespace ProjectExplorer {
class HeaderPath;
@@ -21,7 +19,7 @@ class DbEntry {
public:
QStringList flags;
Utils::FilePath fileName;
- QString workingDir;
+ Utils::FilePath workingDir;
};
class DbContents {
@@ -35,8 +33,8 @@ using MimeBinaryCache = QHash<QString, bool>;
QStringList filterFromFileName(const QStringList &flags, QString baseName);
-void filteredFlags(const QString &fileName,
- const QString &workingDir,
+void filteredFlags(const Utils::FilePath &filePath,
+ const Utils::FilePath &workingDir,
QStringList &flags,
QVector<ProjectExplorer::HeaderPath> &headerPaths,
QVector<ProjectExplorer::Macro> &macros,
diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp
index 251e9685c4..ccbead4c4c 100644
--- a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp
+++ b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp
@@ -8,8 +8,8 @@
#include <coreplugin/progressmanager/progressmanager.h>
#include <projectexplorer/treescanner.h>
+#include <utils/async.h>
#include <utils/mimeutils.h>
-#include <utils/runextensions.h>
#include <QCryptographicHash>
#include <QDir>
@@ -95,7 +95,7 @@ void CompilationDbParser::start()
}
// Thread 2: Parse the project file.
- const QFuture<DbContents> future = runAsync(&CompilationDbParser::parseProject, this);
+ const QFuture<DbContents> future = Utils::asyncRun(&CompilationDbParser::parseProject, this);
Core::ProgressManager::addTask(future,
Tr::tr("Parse \"%1\" project").arg(m_projectName),
"CompilationDatabase.Parse");
@@ -182,7 +182,7 @@ std::vector<DbEntry> CompilationDbParser::readJsonObjects() const
const Utils::FilePath filePath = jsonObjectFilePath(object);
const QStringList flags = filterFromFileName(jsonObjectFlags(object, flagsCache),
filePath.fileName());
- result.push_back({flags, filePath, object["directory"].toString()});
+ result.push_back({flags, filePath, FilePath::fromUserInput(object["directory"].toString())});
objectStart = m_projectFileContents.indexOf('{', objectEnd + 1);
objectEnd = m_projectFileContents.indexOf('}', objectStart + 1);
diff --git a/src/plugins/conan/CMakeLists.txt b/src/plugins/conan/CMakeLists.txt
index c9ad746a84..80da66de4d 100644
--- a/src/plugins/conan/CMakeLists.txt
+++ b/src/plugins/conan/CMakeLists.txt
@@ -3,7 +3,7 @@ add_qtc_plugin(Conan
SOURCES
conanconstants.h
conaninstallstep.cpp conaninstallstep.h
- conanplugin.cpp conanplugin.h
+ conanplugin.cpp
conansettings.cpp conansettings.h
conantr.h
)
diff --git a/src/plugins/conan/conan.qbs b/src/plugins/conan/conan.qbs
index 486df35f49..b5beeb7d35 100644
--- a/src/plugins/conan/conan.qbs
+++ b/src/plugins/conan/conan.qbs
@@ -13,7 +13,6 @@ QtcPlugin {
"conanconstants.h",
"conaninstallstep.h",
"conaninstallstep.cpp",
- "conanplugin.h",
"conanplugin.cpp",
"conansettings.h",
"conansettings.cpp",
diff --git a/src/plugins/conan/conaninstallstep.cpp b/src/plugins/conan/conaninstallstep.cpp
index d0c2ef5bac..2a63419036 100644
--- a/src/plugins/conan/conaninstallstep.cpp
+++ b/src/plugins/conan/conaninstallstep.cpp
@@ -4,7 +4,6 @@
#include "conaninstallstep.h"
#include "conanconstants.h"
-#include "conanplugin.h"
#include "conansettings.h"
#include "conantr.h"
@@ -19,12 +18,40 @@
#include <projectexplorer/target.h>
#include <projectexplorer/task.h>
#include <projectexplorer/toolchain.h>
+#include <projectexplorer/projectmanager.h>
using namespace ProjectExplorer;
using namespace Utils;
namespace Conan::Internal {
+static FilePath conanFilePath(Project *project, const FilePath &defaultFilePath = {})
+{
+ const FilePath projectDirectory = project->projectDirectory();
+ // conanfile.py takes precedence over conanfile.txt when "conan install dir" is invoked
+ const FilePath conanPy = projectDirectory / "conanfile.py";
+ if (conanPy.exists())
+ return conanPy;
+ const FilePath conanTxt = projectDirectory / "conanfile.txt";
+ if (conanTxt.exists())
+ return conanTxt;
+ return defaultFilePath;
+}
+
+static void connectTarget(Project *project, Target *target)
+{
+ if (!conanFilePath(project).isEmpty()) {
+ const QList<BuildConfiguration *> buildConfigurations = target->buildConfigurations();
+ for (BuildConfiguration *buildConfiguration : buildConfigurations)
+ buildConfiguration->buildSteps()->insertStep(0, Constants::INSTALL_STEP);
+ }
+ QObject::connect(target, &Target::addedBuildConfiguration,
+ target, [project] (BuildConfiguration *buildConfiguration) {
+ if (!conanFilePath(project).isEmpty())
+ buildConfiguration->buildSteps()->insertStep(0, Constants::INSTALL_STEP);
+ });
+}
+
// ConanInstallStep
class ConanInstallStep final : public AbstractProcessStep
@@ -43,13 +70,12 @@ ConanInstallStep::ConanInstallStep(BuildStepList *bsl, Id id)
setUseEnglishOutput();
setDisplayName(Tr::tr("Conan install"));
- auto conanFile = addAspect<StringAspect>();
+ auto conanFile = addAspect<FilePathAspect>();
conanFile->setSettingsKey("ConanPackageManager.InstallStep.ConanFile");
- conanFile->setFilePath(ConanPlugin::conanFilePath(project(),
+ conanFile->setFilePath(conanFilePath(project(),
project()->projectDirectory() / "conanfile.txt"));
conanFile->setLabelText(Tr::tr("Conan file:"));
conanFile->setToolTip(Tr::tr("Enter location of conanfile.txt or conanfile.py."));
- conanFile->setDisplayStyle(StringAspect::PathChooserDisplay);
conanFile->setExpectedKind(PathChooser::File);
auto additionalArguments = addAspect<StringAspect>();
@@ -68,7 +94,7 @@ ConanInstallStep::ConanInstallStep(BuildStepList *bsl, Id id)
const QString buildType = bt == BuildConfiguration::Release ? QString("Release")
: QString("Debug");
- CommandLine cmd(ConanPlugin::conanSettings()->conanFilePath.filePath());
+ CommandLine cmd(settings().conanFilePath());
cmd.addArgs({"install", "-s", "build_type=" + buildType});
if (buildMissing->value())
cmd.addArg("--build=missing");
@@ -85,6 +111,12 @@ ConanInstallStep::ConanInstallStep(BuildStepList *bsl, Id id)
setupProcessParameters(&param);
return param.summary(displayName());
});
+
+ connect(ProjectManager::instance(), &ProjectManager::projectAdded, this, [](Project * project) {
+ connect(project, &Project::addedTarget, project, [project] (Target *target) {
+ connectTarget(project, target);
+ });
+ });
}
bool ConanInstallStep::init()
diff --git a/src/plugins/conan/conanplugin.cpp b/src/plugins/conan/conanplugin.cpp
index a1a78acca3..13655b93b6 100644
--- a/src/plugins/conan/conanplugin.cpp
+++ b/src/plugins/conan/conanplugin.cpp
@@ -1,86 +1,26 @@
// Copyright (C) 2018 Jochen Seemann
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "conanplugin.h"
-
-#include "conanconstants.h"
#include "conaninstallstep.h"
#include "conansettings.h"
-#include <coreplugin/icore.h>
-
-#include <projectexplorer/buildconfiguration.h>
-#include <projectexplorer/buildmanager.h>
-#include <projectexplorer/buildsteplist.h>
-#include <projectexplorer/project.h>
-#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
-#include <projectexplorer/target.h>
-
-using namespace Core;
-using namespace ProjectExplorer;
-using namespace Utils;
+#include <extensionsystem/iplugin.h>
namespace Conan::Internal {
-class ConanPluginPrivate
-{
-public:
- ConanInstallStepFactory installStepFactory;
-};
-
-ConanPlugin::~ConanPlugin()
+class ConanPlugin final : public ExtensionSystem::IPlugin
{
- delete d;
-}
-
-void ConanPlugin::initialize()
-{
- d = new ConanPluginPrivate;
- conanSettings()->readSettings(ICore::settings());
-
- connect(SessionManager::instance(), &SessionManager::projectAdded,
- this, &ConanPlugin::projectAdded);
-}
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Conan.json")
-static void connectTarget(Project *project, Target *target)
-{
- if (!ConanPlugin::conanFilePath(project).isEmpty()) {
- const QList<BuildConfiguration *> buildConfigurations = target->buildConfigurations();
- for (BuildConfiguration *buildConfiguration : buildConfigurations)
- buildConfiguration->buildSteps()->appendStep(Constants::INSTALL_STEP);
+public:
+ ConanPlugin()
+ {
+ addManaged<ConanSettings>();
+ addManaged<ConanInstallStepFactory>();
}
- QObject::connect(target, &Target::addedBuildConfiguration,
- target, [project] (BuildConfiguration *buildConfiguration) {
- if (!ConanPlugin::conanFilePath(project).isEmpty())
- buildConfiguration->buildSteps()->appendStep(Constants::INSTALL_STEP);
- });
-}
-
-void ConanPlugin::projectAdded(Project *project)
-{
- connect(project, &Project::addedTarget, project, [project] (Target *target) {
- connectTarget(project, target);
- });
-}
-
-ConanSettings *ConanPlugin::conanSettings()
-{
- static ConanSettings theSettings;
- return &theSettings;
-}
-
-FilePath ConanPlugin::conanFilePath(Project *project, const FilePath &defaultFilePath)
-{
- const FilePath projectDirectory = project->projectDirectory();
- // conanfile.py takes precedence over conanfile.txt when "conan install dir" is invoked
- const FilePath conanPy = projectDirectory / "conanfile.py";
- if (conanPy.exists())
- return conanPy;
- const FilePath conanTxt = projectDirectory / "conanfile.txt";
- if (conanTxt.exists())
- return conanTxt;
- return defaultFilePath;
-}
+};
} // Conan::Internal
+
+#include "conanplugin.moc"
diff --git a/src/plugins/conan/conanplugin.h b/src/plugins/conan/conanplugin.h
deleted file mode 100644
index 59fbe976d2..0000000000
--- a/src/plugins/conan/conanplugin.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2018 Jochen Seemann
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <extensionsystem/iplugin.h>
-#include <utils/filepath.h>
-
-namespace ProjectExplorer { class Project; }
-
-namespace Conan::Internal {
-
-class ConanSettings;
-
-class ConanPlugin final : public ExtensionSystem::IPlugin
-{
- Q_OBJECT
- Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Conan.json")
-
-public:
- static ConanSettings *conanSettings();
- static Utils::FilePath conanFilePath(ProjectExplorer::Project *project,
- const Utils::FilePath &defaultFilePath = {});
-
-private:
- ~ConanPlugin() final;
- void projectAdded(ProjectExplorer::Project *project);
-
- void initialize() final;
-
- class ConanPluginPrivate *d = nullptr;
-};
-
-} // Conan::Internal
diff --git a/src/plugins/conan/conansettings.cpp b/src/plugins/conan/conansettings.cpp
index 6a608b99f8..50373c764b 100644
--- a/src/plugins/conan/conansettings.cpp
+++ b/src/plugins/conan/conansettings.cpp
@@ -3,22 +3,30 @@
#include "conansettings.h"
+#include <coreplugin/icore.h>
+
#include <utils/hostosinfo.h>
using namespace Utils;
namespace Conan::Internal {
+static ConanSettings *theSettings;
+
+ConanSettings &settings() { return *theSettings; }
+
ConanSettings::ConanSettings()
{
+ theSettings = this;
+
setSettingsGroup("ConanSettings");
setAutoApply(false);
- registerAspect(&conanFilePath);
conanFilePath.setSettingsKey("ConanFilePath");
- conanFilePath.setDisplayStyle(StringAspect::PathChooserDisplay);
conanFilePath.setExpectedKind(PathChooser::ExistingCommand);
conanFilePath.setDefaultValue(HostOsInfo::withExecutableSuffix("conan"));
+
+ readSettings(Core::ICore::settings());
}
} // Conan::Internal
diff --git a/src/plugins/conan/conansettings.h b/src/plugins/conan/conansettings.h
index 93e6190839..6748503728 100644
--- a/src/plugins/conan/conansettings.h
+++ b/src/plugins/conan/conansettings.h
@@ -12,7 +12,9 @@ class ConanSettings : public Utils::AspectContainer
public:
ConanSettings();
- Utils::StringAspect conanFilePath;
+ Utils::FilePathAspect conanFilePath{this};
};
+ConanSettings &settings();
+
} // Conan::Internal
diff --git a/src/plugins/copilot/CMakeLists.txt b/src/plugins/copilot/CMakeLists.txt
new file mode 100644
index 0000000000..01129674fc
--- /dev/null
+++ b/src/plugins/copilot/CMakeLists.txt
@@ -0,0 +1,19 @@
+add_qtc_plugin(Copilot
+ PLUGIN_DEPENDS Core LanguageClient
+ SOURCES
+ authwidget.cpp authwidget.h
+ copilot.qrc
+ copilotclient.cpp copilotclient.h
+ copilotconstants.h
+ copilothoverhandler.cpp copilothoverhandler.h
+ copilotoptionspage.cpp copilotoptionspage.h
+ copilotplugin.cpp copilotplugin.h
+ copilotprojectpanel.cpp copilotprojectpanel.h
+ copilotsettings.cpp copilotsettings.h
+ copilotsuggestion.cpp copilotsuggestion.h
+ requests/checkstatus.h
+ requests/getcompletions.h
+ requests/signinconfirm.h
+ requests/signininitiate.h
+ requests/signout.h
+)
diff --git a/src/plugins/copilot/Copilot.json.in b/src/plugins/copilot/Copilot.json.in
new file mode 100644
index 0000000000..55ff6f6bf4
--- /dev/null
+++ b/src/plugins/copilot/Copilot.json.in
@@ -0,0 +1,19 @@
+{
+ \"Name\" : \"Copilot\",
+ \"Version\" : \"$$QTCREATOR_VERSION\",
+ \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
+ \"Experimental\" : true,
+ \"Vendor\" : \"The Qt Company Ltd\",
+ \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\",
+ \"License\" : [ \"Commercial Usage\",
+ \"\",
+ \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\",
+ \"\",
+ \"GNU General Public License Usage\",
+ \"\",
+ \"Alternatively, this plugin 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 plugin. 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.\"
+ ],
+ \"Description\" : \"Copilot support\",
+ \"Url\" : \"http://www.qt.io\",
+ $$dependencyList
+}
diff --git a/src/plugins/copilot/authwidget.cpp b/src/plugins/copilot/authwidget.cpp
new file mode 100644
index 0000000000..f29e3d3a15
--- /dev/null
+++ b/src/plugins/copilot/authwidget.cpp
@@ -0,0 +1,159 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "authwidget.h"
+
+#include "copilotclient.h"
+#include "copilottr.h"
+
+#include <utils/layoutbuilder.h>
+#include <utils/stringutils.h>
+
+#include <languageclient/languageclientmanager.h>
+
+#include <QDesktopServices>
+#include <QMessageBox>
+
+using namespace LanguageClient;
+using namespace Copilot::Internal;
+
+namespace Copilot {
+
+AuthWidget::AuthWidget(QWidget *parent)
+ : QWidget(parent)
+{
+ using namespace Layouting;
+
+ m_button = new QPushButton(Tr::tr("Sign in"));
+ m_button->setEnabled(false);
+ m_progressIndicator = new Utils::ProgressIndicator(Utils::ProgressIndicatorSize::Small);
+ m_progressIndicator->setVisible(false);
+ m_statusLabel = new QLabel();
+ m_statusLabel->setVisible(false);
+
+ // clang-format off
+ Column {
+ Row {
+ m_button, m_progressIndicator, st
+ },
+ m_statusLabel
+ }.attachTo(this);
+ // clang-format on
+
+ connect(m_button, &QPushButton::clicked, this, [this]() {
+ if (m_status == Status::SignedIn)
+ signOut();
+ else if (m_status == Status::SignedOut)
+ signIn();
+ });
+}
+
+AuthWidget::~AuthWidget()
+{
+ if (m_client)
+ LanguageClientManager::shutdownClient(m_client);
+}
+
+void AuthWidget::setState(const QString &buttonText, bool working)
+{
+ m_button->setText(buttonText);
+ m_button->setVisible(true);
+ m_progressIndicator->setVisible(working);
+ m_statusLabel->setVisible(!m_statusLabel->text().isEmpty());
+ m_button->setEnabled(!working);
+}
+
+void AuthWidget::checkStatus()
+{
+ QTC_ASSERT(m_client && m_client->reachable(), return);
+
+ setState("Checking status ...", true);
+
+ m_client->requestCheckStatus(false, [this](const CheckStatusRequest::Response &response) {
+ if (response.error()) {
+ setState("failed: " + response.error()->message(), false);
+ return;
+ }
+ const CheckStatusResponse result = *response.result();
+
+ if (result.user().isEmpty()) {
+ setState("Sign in", false);
+ m_status = Status::SignedOut;
+ return;
+ }
+
+ setState("Sign out " + result.user(), false);
+ m_status = Status::SignedIn;
+ });
+}
+
+void AuthWidget::updateClient(const Utils::FilePath &nodeJs, const Utils::FilePath &agent)
+{
+ LanguageClientManager::shutdownClient(m_client);
+ m_client = nullptr;
+ setState(Tr::tr("Sign in"), false);
+ m_button->setEnabled(false);
+ if (!nodeJs.isExecutableFile() || !agent.exists()) {
+ return;
+ }
+
+ setState(Tr::tr("Sign in"), true);
+
+ m_client = new CopilotClient(nodeJs, agent);
+ connect(m_client, &Client::initialized, this, &AuthWidget::checkStatus);
+}
+
+void AuthWidget::signIn()
+{
+ qCritical() << "Not implemented";
+ QTC_ASSERT(m_client && m_client->reachable(), return);
+
+ setState("Signing in ...", true);
+
+ m_client->requestSignInInitiate([this](const SignInInitiateRequest::Response &response) {
+ QTC_ASSERT(!response.error(), return);
+
+ Utils::setClipboardAndSelection(response.result()->userCode());
+
+ QDesktopServices::openUrl(QUrl(response.result()->verificationUri()));
+
+ m_statusLabel->setText(Tr::tr("A browser window will open, enter the code %1 when "
+ "asked.\nThe code has been copied to your clipboard.")
+ .arg(response.result()->userCode()));
+ m_statusLabel->setVisible(true);
+
+ m_client
+ ->requestSignInConfirm(response.result()->userCode(),
+ [this](const SignInConfirmRequest::Response &response) {
+ m_statusLabel->setText("");
+
+ if (response.error()) {
+ QMessageBox::critical(this,
+ Tr::tr("Login failed"),
+ Tr::tr(
+ "The login request failed: ")
+ + response.error()->message());
+ setState("Sign in", false);
+ return;
+ }
+
+ setState("Sign Out " + response.result()->user(), false);
+ });
+ });
+}
+
+void AuthWidget::signOut()
+{
+ QTC_ASSERT(m_client && m_client->reachable(), return);
+
+ setState("Signing out ...", true);
+
+ m_client->requestSignOut([this](const SignOutRequest::Response &response) {
+ QTC_ASSERT(!response.error(), return);
+ QTC_ASSERT(response.result()->status() == "NotSignedIn", return);
+
+ checkStatus();
+ });
+}
+
+} // namespace Copilot
diff --git a/src/plugins/copilot/authwidget.h b/src/plugins/copilot/authwidget.h
new file mode 100644
index 0000000000..acb18810fe
--- /dev/null
+++ b/src/plugins/copilot/authwidget.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "copilotclient.h"
+
+#include <utils/progressindicator.h>
+
+#include <QLabel>
+#include <QPushButton>
+#include <QWidget>
+
+namespace LanguageClient {
+class Client;
+}
+
+namespace Copilot {
+
+class AuthWidget : public QWidget
+{
+ Q_OBJECT
+
+ enum class Status { SignedIn, SignedOut, Unknown };
+
+public:
+ explicit AuthWidget(QWidget *parent = nullptr);
+ ~AuthWidget() override;
+
+ void updateClient(const Utils::FilePath &nodeJs, const Utils::FilePath &agent);
+
+private:
+ void setState(const QString &buttonText, bool working);
+ void checkStatus();
+
+
+ void signIn();
+ void signOut();
+
+private:
+ Status m_status = Status::Unknown;
+ QPushButton *m_button = nullptr;
+ QLabel *m_statusLabel = nullptr;
+ Utils::ProgressIndicator *m_progressIndicator = nullptr;
+ Internal::CopilotClient *m_client = nullptr;
+};
+
+} // namespace Copilot
diff --git a/src/plugins/copilot/copilot.qbs b/src/plugins/copilot/copilot.qbs
new file mode 100644
index 0000000000..714c45543d
--- /dev/null
+++ b/src/plugins/copilot/copilot.qbs
@@ -0,0 +1,37 @@
+import qbs 1.0
+
+QtcPlugin {
+ name: "Copilot"
+
+ Depends { name: "Core" }
+ Depends { name: "LanguageClient" }
+ Depends { name: "ProjectExplorer" }
+ Depends { name: "TextEditor" }
+ Depends { name: "Qt"; submodules: ["widgets", "xml", "network"] }
+
+ files: [
+ "authwidget.cpp",
+ "authwidget.h",
+ "copilot.qrc",
+ "copilotclient.cpp",
+ "copilotclient.h",
+ "copilotconstants.h",
+ "copilothoverhandler.cpp",
+ "copilothoverhandler.h",
+ "copilotoptionspage.cpp",
+ "copilotoptionspage.h",
+ "copilotplugin.cpp",
+ "copilotplugin.h",
+ "copilotprojectpanel.cpp",
+ "copilotprojectpanel.h",
+ "copilotsettings.cpp",
+ "copilotsettings.h",
+ "copilotsuggestion.cpp",
+ "copilotsuggestion.h",
+ "requests/checkstatus.h",
+ "requests/getcompletions.h",
+ "requests/signinconfirm.h",
+ "requests/signininitiate.h",
+ "requests/signout.h",
+ ]
+}
diff --git a/src/plugins/copilot/copilot.qrc b/src/plugins/copilot/copilot.qrc
new file mode 100644
index 0000000000..65d481b92a
--- /dev/null
+++ b/src/plugins/copilot/copilot.qrc
@@ -0,0 +1,8 @@
+<RCC>
+ <qresource prefix="/copilot">
+ <file>images/settingscategory_copilot.png</file>
+ <file>images/settingscategory_copilot@2x.png</file>
+ <file>images/copilot.png</file>
+ <file>images/copilot@2x.png</file>
+ </qresource>
+</RCC>
diff --git a/src/plugins/copilot/copilotclient.cpp b/src/plugins/copilot/copilotclient.cpp
new file mode 100644
index 0000000000..f935210b53
--- /dev/null
+++ b/src/plugins/copilot/copilotclient.cpp
@@ -0,0 +1,252 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "copilotclient.h"
+#include "copilotconstants.h"
+#include "copilotsettings.h"
+#include "copilotsuggestion.h"
+
+#include <languageclient/languageclientinterface.h>
+#include <languageclient/languageclientmanager.h>
+#include <languageclient/languageclientsettings.h>
+
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/editormanager/editormanager.h>
+
+#include <projectexplorer/projectmanager.h>
+
+#include <utils/filepath.h>
+
+#include <texteditor/textdocumentlayout.h>
+#include <texteditor/texteditor.h>
+
+#include <languageserverprotocol/lsptypes.h>
+
+#include <QTimer>
+#include <QToolButton>
+
+using namespace LanguageServerProtocol;
+using namespace TextEditor;
+using namespace Utils;
+using namespace ProjectExplorer;
+using namespace Core;
+
+namespace Copilot::Internal {
+
+static LanguageClient::BaseClientInterface *clientInterface(const FilePath &nodePath,
+ const FilePath &distPath)
+{
+ CommandLine cmd{nodePath, {distPath.toFSPathString()}};
+
+ const auto interface = new LanguageClient::StdIOClientInterface;
+ interface->setCommandLine(cmd);
+ return interface;
+}
+
+CopilotClient::CopilotClient(const FilePath &nodePath, const FilePath &distPath)
+ : LanguageClient::Client(clientInterface(nodePath, distPath))
+{
+ setName("Copilot");
+ LanguageClient::LanguageFilter langFilter;
+
+ langFilter.filePattern = {"*"};
+
+ setSupportedLanguage(langFilter);
+ start();
+
+ auto openDoc = [this](IDocument *document) {
+ if (auto *textDocument = qobject_cast<TextDocument *>(document))
+ openDocument(textDocument);
+ };
+
+ connect(EditorManager::instance(), &EditorManager::documentOpened, this, openDoc);
+ connect(EditorManager::instance(),
+ &EditorManager::documentClosed,
+ this,
+ [this](IDocument *document) {
+ if (auto textDocument = qobject_cast<TextDocument *>(document))
+ closeDocument(textDocument);
+ });
+
+ for (IDocument *doc : DocumentModel::openedDocuments())
+ openDoc(doc);
+}
+
+CopilotClient::~CopilotClient()
+{
+ for (IEditor *editor : DocumentModel::editorsForOpenedDocuments()) {
+ if (auto textEditor = qobject_cast<BaseTextEditor *>(editor))
+ textEditor->editorWidget()->removeHoverHandler(&m_hoverHandler);
+ }
+}
+
+void CopilotClient::openDocument(TextDocument *document)
+{
+ auto project = ProjectManager::projectForFile(document->filePath());
+ if (!isEnabled(project))
+ return;
+
+ Client::openDocument(document);
+ connect(document,
+ &TextDocument::contentsChangedWithPosition,
+ this,
+ [this, document](int position, int charsRemoved, int charsAdded) {
+ Q_UNUSED(charsRemoved)
+ if (!CopilotSettings::instance().autoComplete())
+ return;
+
+ auto project = ProjectManager::projectForFile(document->filePath());
+ if (!isEnabled(project))
+ return;
+
+ auto textEditor = BaseTextEditor::currentTextEditor();
+ if (!textEditor || textEditor->document() != document)
+ return;
+ TextEditorWidget *widget = textEditor->editorWidget();
+ if (widget->multiTextCursor().hasMultipleCursors())
+ return;
+ const int cursorPosition = widget->textCursor().position();
+ if (cursorPosition < position || cursorPosition > position + charsAdded)
+ return;
+ scheduleRequest(widget);
+ });
+}
+
+void CopilotClient::scheduleRequest(TextEditorWidget *editor)
+{
+ cancelRunningRequest(editor);
+
+ if (!m_scheduledRequests.contains(editor)) {
+ auto timer = new QTimer(this);
+ timer->setSingleShot(true);
+ connect(timer, &QTimer::timeout, this, [this, editor]() {
+ if (m_scheduledRequests[editor].cursorPosition == editor->textCursor().position())
+ requestCompletions(editor);
+ });
+ connect(editor, &TextEditorWidget::destroyed, this, [this, editor]() {
+ delete m_scheduledRequests.take(editor).timer;
+ cancelRunningRequest(editor);
+ });
+ connect(editor, &TextEditorWidget::cursorPositionChanged, this, [this, editor] {
+ cancelRunningRequest(editor);
+ });
+ m_scheduledRequests.insert(editor, {editor->textCursor().position(), timer});
+ } else {
+ m_scheduledRequests[editor].cursorPosition = editor->textCursor().position();
+ }
+ m_scheduledRequests[editor].timer->start(500);
+}
+
+void CopilotClient::requestCompletions(TextEditorWidget *editor)
+{
+ auto project = ProjectManager::projectForFile(editor->textDocument()->filePath());
+
+ if (!isEnabled(project))
+ return;
+
+ Utils::MultiTextCursor cursor = editor->multiTextCursor();
+ if (cursor.hasMultipleCursors() || cursor.hasSelection() || editor->suggestionVisible())
+ return;
+
+ const Utils::FilePath filePath = editor->textDocument()->filePath();
+ GetCompletionRequest request{
+ {TextDocumentIdentifier(hostPathToServerUri(filePath)),
+ documentVersion(filePath),
+ Position(cursor.mainCursor())}};
+ request.setResponseCallback([this, editor = QPointer<TextEditorWidget>(editor)](
+ const GetCompletionRequest::Response &response) {
+ QTC_ASSERT(editor, return);
+ handleCompletions(response, editor);
+ });
+ m_runningRequests[editor] = request;
+ sendMessage(request);
+}
+
+void CopilotClient::handleCompletions(const GetCompletionRequest::Response &response,
+ TextEditorWidget *editor)
+{
+ if (response.error())
+ log(*response.error());
+
+ int requestPosition = -1;
+ if (const auto requestParams = m_runningRequests.take(editor).params())
+ requestPosition = requestParams->position().toPositionInDocument(editor->document());
+
+ const Utils::MultiTextCursor cursors = editor->multiTextCursor();
+ if (cursors.hasMultipleCursors())
+ return;
+
+ if (cursors.hasSelection() || cursors.mainCursor().position() != requestPosition)
+ return;
+
+ if (const std::optional<GetCompletionResponse> result = response.result()) {
+ QList<Completion> completions = result->completions().toListOrEmpty();
+ if (completions.isEmpty())
+ return;
+ editor->insertSuggestion(
+ std::make_unique<CopilotSuggestion>(completions, editor->document()));
+ editor->addHoverHandler(&m_hoverHandler);
+ }
+}
+
+void CopilotClient::cancelRunningRequest(TextEditor::TextEditorWidget *editor)
+{
+ auto it = m_runningRequests.find(editor);
+ if (it == m_runningRequests.end())
+ return;
+ cancelRequest(it->id());
+ m_runningRequests.erase(it);
+}
+
+void CopilotClient::requestCheckStatus(
+ bool localChecksOnly, std::function<void(const CheckStatusRequest::Response &response)> callback)
+{
+ CheckStatusRequest request{localChecksOnly};
+ request.setResponseCallback(callback);
+
+ sendMessage(request);
+}
+
+void CopilotClient::requestSignOut(
+ std::function<void(const SignOutRequest::Response &response)> callback)
+{
+ SignOutRequest request;
+ request.setResponseCallback(callback);
+
+ sendMessage(request);
+}
+
+void CopilotClient::requestSignInInitiate(
+ std::function<void(const SignInInitiateRequest::Response &response)> callback)
+{
+ SignInInitiateRequest request;
+ request.setResponseCallback(callback);
+
+ sendMessage(request);
+}
+
+void CopilotClient::requestSignInConfirm(
+ const QString &userCode,
+ std::function<void(const SignInConfirmRequest::Response &response)> callback)
+{
+ SignInConfirmRequest request(userCode);
+ request.setResponseCallback(callback);
+
+ sendMessage(request);
+}
+
+bool CopilotClient::canOpenProject(Project *project)
+{
+ return isEnabled(project);
+}
+
+bool CopilotClient::isEnabled(Project *project)
+{
+ if (!project)
+ return CopilotSettings::instance().enableCopilot();
+
+ CopilotProjectSettings settings(project);
+ return settings.isEnabled();
+}
+
+} // namespace Copilot::Internal
diff --git a/src/plugins/copilot/copilotclient.h b/src/plugins/copilot/copilotclient.h
new file mode 100644
index 0000000000..47208236b5
--- /dev/null
+++ b/src/plugins/copilot/copilotclient.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "copilothoverhandler.h"
+#include "requests/checkstatus.h"
+#include "requests/getcompletions.h"
+#include "requests/signinconfirm.h"
+#include "requests/signininitiate.h"
+#include "requests/signout.h"
+
+#include <languageclient/client.h>
+
+#include <utils/filepath.h>
+
+#include <QHash>
+#include <QTemporaryDir>
+
+namespace Copilot::Internal {
+
+class CopilotClient : public LanguageClient::Client
+{
+public:
+ CopilotClient(const Utils::FilePath &nodePath, const Utils::FilePath &distPath);
+ ~CopilotClient() override;
+
+ void openDocument(TextEditor::TextDocument *document) override;
+
+ void scheduleRequest(TextEditor::TextEditorWidget *editor);
+ void requestCompletions(TextEditor::TextEditorWidget *editor);
+ void handleCompletions(const GetCompletionRequest::Response &response,
+ TextEditor::TextEditorWidget *editor);
+ void cancelRunningRequest(TextEditor::TextEditorWidget *editor);
+
+ void requestCheckStatus(
+ bool localChecksOnly,
+ std::function<void(const CheckStatusRequest::Response &response)> callback);
+
+ void requestSignOut(std::function<void(const SignOutRequest::Response &response)> callback);
+
+ void requestSignInInitiate(
+ std::function<void(const SignInInitiateRequest::Response &response)> callback);
+
+ void requestSignInConfirm(
+ const QString &userCode,
+ std::function<void(const SignInConfirmRequest::Response &response)> callback);
+
+ bool canOpenProject(ProjectExplorer::Project *project) override;
+
+ bool isEnabled(ProjectExplorer::Project *project);
+
+private:
+ QMap<TextEditor::TextEditorWidget *, GetCompletionRequest> m_runningRequests;
+ struct ScheduleData
+ {
+ int cursorPosition = -1;
+ QTimer *timer = nullptr;
+ };
+ QMap<TextEditor::TextEditorWidget *, ScheduleData> m_scheduledRequests;
+ CopilotHoverHandler m_hoverHandler;
+};
+
+} // namespace Copilot::Internal
diff --git a/src/plugins/copilot/copilotconstants.h b/src/plugins/copilot/copilotconstants.h
new file mode 100644
index 0000000000..6ace5f2f6f
--- /dev/null
+++ b/src/plugins/copilot/copilotconstants.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace Copilot::Constants {
+const char COPILOT_PROJECT_SETTINGS_ID[] = "Copilot.Project.Settings";
+const char ENABLE_COPILOT[] = "Copilot.EnableCopilot";
+const char COPILOT_USE_GLOBAL_SETTINGS[] = "Copilot.UseGlobalSettings";
+
+const char COPILOT_TOGGLE[] = "Copilot.Toggle";
+const char COPILOT_ENABLE[] = "Copilot.Enable";
+const char COPILOT_DISABLE[] = "Copilot.Disable";
+const char COPILOT_REQUEST_SUGGESTION[] = "Copilot.RequestSuggestion";
+
+const char COPILOT_GENERAL_OPTIONS_ID[] = "Copilot.General";
+const char COPILOT_GENERAL_OPTIONS_CATEGORY[] = "ZY.Copilot";
+const char COPILOT_GENERAL_OPTIONS_DISPLAY_CATEGORY[] = "Copilot";
+
+} // namespace Copilot::Constants
diff --git a/src/plugins/copilot/copilothoverhandler.cpp b/src/plugins/copilot/copilothoverhandler.cpp
new file mode 100644
index 0000000000..135cbd8390
--- /dev/null
+++ b/src/plugins/copilot/copilothoverhandler.cpp
@@ -0,0 +1,159 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "copilothoverhandler.h"
+
+#include "copilotclient.h"
+#include "copilotsuggestion.h"
+#include "copilottr.h"
+
+#include <texteditor/textdocument.h>
+#include <texteditor/textdocumentlayout.h>
+#include <texteditor/texteditor.h>
+
+#include <utils/tooltip/tooltip.h>
+#include <utils/utilsicons.h>
+
+#include <QPushButton>
+#include <QToolBar>
+#include <QToolButton>
+
+using namespace TextEditor;
+using namespace LanguageServerProtocol;
+using namespace Utils;
+
+namespace Copilot::Internal {
+
+class CopilotCompletionToolTip : public QToolBar
+{
+public:
+ CopilotCompletionToolTip(QList<Completion> completions,
+ int currentCompletion,
+ TextEditorWidget *editor)
+ : m_numberLabel(new QLabel)
+ , m_completions(completions)
+ , m_currentCompletion(std::max(0, std::min<int>(currentCompletion, completions.size() - 1)))
+ , m_editor(editor)
+ {
+ auto prev = addAction(Utils::Icons::PREV_TOOLBAR.icon(),
+ Tr::tr("Select Previous Copilot Suggestion"));
+ prev->setEnabled(m_completions.size() > 1);
+ addWidget(m_numberLabel);
+ auto next = addAction(Utils::Icons::NEXT_TOOLBAR.icon(),
+ Tr::tr("Select Next Copilot Suggestion"));
+ next->setEnabled(m_completions.size() > 1);
+
+ auto apply = addAction(Tr::tr("Apply (%1)").arg(QKeySequence(Qt::Key_Tab).toString()));
+ auto applyWord = addAction(
+ Tr::tr("Apply Word (%1)").arg(QKeySequence(QKeySequence::MoveToNextWord).toString()));
+
+ connect(prev, &QAction::triggered, this, &CopilotCompletionToolTip::selectPrevious);
+ connect(next, &QAction::triggered, this, &CopilotCompletionToolTip::selectNext);
+ connect(apply, &QAction::triggered, this, &CopilotCompletionToolTip::apply);
+ connect(applyWord, &QAction::triggered, this, &CopilotCompletionToolTip::applyWord);
+
+ updateLabels();
+ }
+
+private:
+ void updateLabels()
+ {
+ m_numberLabel->setText(Tr::tr("%1 of %2")
+ .arg(m_currentCompletion + 1)
+ .arg(m_completions.count()));
+ }
+
+ void selectPrevious()
+ {
+ --m_currentCompletion;
+ if (m_currentCompletion < 0)
+ m_currentCompletion = m_completions.size() - 1;
+ setCurrentCompletion();
+ }
+
+ void selectNext()
+ {
+ ++m_currentCompletion;
+ if (m_currentCompletion >= m_completions.size())
+ m_currentCompletion = 0;
+ setCurrentCompletion();
+ }
+
+ void setCurrentCompletion()
+ {
+ updateLabels();
+ if (TextSuggestion *suggestion = m_editor->currentSuggestion())
+ suggestion->reset();
+ m_editor->insertSuggestion(std::make_unique<CopilotSuggestion>(m_completions,
+ m_editor->document(),
+ m_currentCompletion));
+ }
+
+ void apply()
+ {
+ if (TextSuggestion *suggestion = m_editor->currentSuggestion()) {
+ if (!suggestion->apply())
+ return;
+ }
+ ToolTip::hide();
+ }
+
+ void applyWord()
+ {
+ if (TextSuggestion *suggestion = m_editor->currentSuggestion()) {
+ if (!suggestion->applyWord(m_editor))
+ return;
+ }
+ ToolTip::hide();
+ }
+
+ QLabel *m_numberLabel;
+ QList<Completion> m_completions;
+ int m_currentCompletion = 0;
+ TextEditorWidget *m_editor;
+};
+
+void CopilotHoverHandler::identifyMatch(TextEditorWidget *editorWidget,
+ int pos,
+ ReportPriority report)
+{
+ auto reportNone = qScopeGuard([&] { report(Priority_None); });
+ if (!editorWidget->suggestionVisible())
+ return;
+
+ QTextCursor cursor(editorWidget->document());
+ cursor.setPosition(pos);
+ m_block = cursor.block();
+ auto *suggestion = dynamic_cast<CopilotSuggestion *>(TextDocumentLayout::suggestion(m_block));
+
+ if (!suggestion)
+ return;
+
+ const QList<Completion> completions = suggestion->completions();
+ if (completions.isEmpty())
+ return;
+
+ reportNone.dismiss();
+ report(Priority_Suggestion);
+}
+
+void CopilotHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const QPoint &point)
+{
+ Q_UNUSED(point)
+ auto *suggestion = dynamic_cast<CopilotSuggestion *>(TextDocumentLayout::suggestion(m_block));
+
+ if (!suggestion)
+ return;
+
+ auto tooltipWidget = new CopilotCompletionToolTip(suggestion->completions(),
+ suggestion->currentCompletion(),
+ editorWidget);
+
+ const QRect cursorRect = editorWidget->cursorRect(editorWidget->textCursor());
+ QPoint pos = editorWidget->viewport()->mapToGlobal(cursorRect.topLeft())
+ - Utils::ToolTip::offsetFromPosition();
+ pos.ry() -= tooltipWidget->sizeHint().height();
+ ToolTip::show(pos, tooltipWidget, editorWidget);
+}
+
+} // namespace Copilot::Internal
diff --git a/src/plugins/copilot/copilothoverhandler.h b/src/plugins/copilot/copilothoverhandler.h
new file mode 100644
index 0000000000..1c48e75d5b
--- /dev/null
+++ b/src/plugins/copilot/copilothoverhandler.h
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "requests/getcompletions.h"
+
+#include <texteditor/basehoverhandler.h>
+
+#include <QTextBlock>
+
+#pragma once
+
+namespace TextEditor { class TextSuggestion; }
+namespace Copilot::Internal {
+
+class CopilotClient;
+
+class CopilotHoverHandler final : public TextEditor::BaseHoverHandler
+{
+public:
+ CopilotHoverHandler() = default;
+
+protected:
+ void identifyMatch(TextEditor::TextEditorWidget *editorWidget,
+ int pos,
+ ReportPriority report) final;
+ void operateTooltip(TextEditor::TextEditorWidget *editorWidget, const QPoint &point) final;
+
+private:
+ QTextBlock m_block;
+};
+
+} // namespace Copilot::Internal
diff --git a/src/plugins/copilot/copiloticons.h b/src/plugins/copilot/copiloticons.h
new file mode 100644
index 0000000000..b2b54077d3
--- /dev/null
+++ b/src/plugins/copilot/copiloticons.h
@@ -0,0 +1,12 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <utils/icon.h>
+
+namespace Copilot {
+static const Utils::Icon COPILOT_ICON({{":/copilot/images/copilot.png",
+ Utils::Theme::IconsBaseColor}});
+
+}
diff --git a/src/plugins/copilot/copilotoptionspage.cpp b/src/plugins/copilot/copilotoptionspage.cpp
new file mode 100644
index 0000000000..001db87834
--- /dev/null
+++ b/src/plugins/copilot/copilotoptionspage.cpp
@@ -0,0 +1,101 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "copilotoptionspage.h"
+
+#include "authwidget.h"
+#include "copilotconstants.h"
+#include "copilotsettings.h"
+#include "copilottr.h"
+
+#include <coreplugin/icore.h>
+
+#include <utils/layoutbuilder.h>
+#include <utils/pathchooser.h>
+
+using namespace Utils;
+using namespace LanguageClient;
+
+namespace Copilot {
+
+class CopilotOptionsPageWidget : public Core::IOptionsPageWidget
+{
+public:
+ CopilotOptionsPageWidget()
+ {
+ using namespace Layouting;
+
+ auto authWidget = new AuthWidget();
+
+ QLabel *helpLabel = new QLabel();
+ helpLabel->setTextFormat(Qt::MarkdownText);
+ helpLabel->setWordWrap(true);
+ helpLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse
+ | Qt::LinksAccessibleByKeyboard);
+ helpLabel->setOpenExternalLinks(true);
+
+ // clang-format off
+ helpLabel->setText(Tr::tr(R"(
+The Copilot plugin requires node.js and the Copilot neovim plugin.
+If you install the neovim plugin as described in the
+[README.md](https://github.com/github/copilot.vim),
+the plugin will find the agent.js file automatically.
+
+Otherwise you need to specify the path to the
+[agent.js](https://github.com/github/copilot.vim/tree/release/copilot/dist)
+file from the Copilot neovim plugin.
+ )", "Markdown text for the copilot instruction label"));
+
+ Column {
+ authWidget, br,
+ CopilotSettings::instance().enableCopilot, br,
+ CopilotSettings::instance().nodeJsPath, br,
+ CopilotSettings::instance().distPath, br,
+ CopilotSettings::instance().autoComplete, br,
+ helpLabel, br,
+ st
+ }.attachTo(this);
+ // clang-format on
+
+ auto updateAuthWidget = [authWidget]() {
+ authWidget->updateClient(
+ FilePath::fromUserInput(
+ CopilotSettings::instance().nodeJsPath.volatileValue().toString()),
+ FilePath::fromUserInput(
+ CopilotSettings::instance().distPath.volatileValue().toString()));
+ };
+
+ connect(CopilotSettings::instance().nodeJsPath.pathChooser(),
+ &PathChooser::textChanged,
+ authWidget,
+ updateAuthWidget);
+ connect(CopilotSettings::instance().distPath.pathChooser(),
+ &PathChooser::textChanged,
+ authWidget,
+ updateAuthWidget);
+ updateAuthWidget();
+
+ setOnApply([] {
+ CopilotSettings::instance().apply();
+ CopilotSettings::instance().writeSettings(Core::ICore::settings());
+ });
+ }
+};
+
+CopilotOptionsPage::CopilotOptionsPage()
+{
+ setId(Constants::COPILOT_GENERAL_OPTIONS_ID);
+ setDisplayName("Copilot");
+ setCategory(Constants::COPILOT_GENERAL_OPTIONS_CATEGORY);
+ setDisplayCategory(Constants::COPILOT_GENERAL_OPTIONS_DISPLAY_CATEGORY);
+ setCategoryIconPath(":/copilot/images/settingscategory_copilot.png");
+ setWidgetCreator([] { return new CopilotOptionsPageWidget; });
+}
+
+CopilotOptionsPage &CopilotOptionsPage::instance()
+{
+ static CopilotOptionsPage settingsPage;
+ return settingsPage;
+}
+
+} // namespace Copilot
diff --git a/src/plugins/copilot/copilotoptionspage.h b/src/plugins/copilot/copilotoptionspage.h
new file mode 100644
index 0000000000..103e975b63
--- /dev/null
+++ b/src/plugins/copilot/copilotoptionspage.h
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <coreplugin/dialogs/ioptionspage.h>
+
+namespace Copilot {
+
+class CopilotOptionsPage : public Core::IOptionsPage
+{
+public:
+ CopilotOptionsPage();
+
+ static CopilotOptionsPage &instance();
+};
+
+} // Copilot
diff --git a/src/plugins/copilot/copilotplugin.cpp b/src/plugins/copilot/copilotplugin.cpp
new file mode 100644
index 0000000000..526a6061cb
--- /dev/null
+++ b/src/plugins/copilot/copilotplugin.cpp
@@ -0,0 +1,139 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "copilotplugin.h"
+
+#include "copilotclient.h"
+#include "copilotconstants.h"
+#include "copiloticons.h"
+#include "copilotoptionspage.h"
+#include "copilotprojectpanel.h"
+#include "copilotsettings.h"
+#include "copilottr.h"
+
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/statusbarmanager.h>
+
+#include <languageclient/languageclientmanager.h>
+
+#include <projectexplorer/projectpanelfactory.h>
+
+#include <texteditor/texteditor.h>
+
+#include <QAction>
+#include <QToolButton>
+
+using namespace Utils;
+using namespace Core;
+using namespace ProjectExplorer;
+
+namespace Copilot {
+namespace Internal {
+
+void CopilotPlugin::initialize()
+{
+ CopilotSettings::instance().readSettings(ICore::settings());
+
+ restartClient();
+
+ connect(&CopilotSettings::instance(),
+ &CopilotSettings::applied,
+ this,
+ &CopilotPlugin::restartClient);
+
+ QAction *requestAction = new QAction(this);
+ requestAction->setText(Tr::tr("Request Copilot Suggestion"));
+ requestAction->setToolTip(
+ Tr::tr("Request Copilot suggestion at the current editor's cursor position."));
+
+ connect(requestAction, &QAction::triggered, this, [this] {
+ if (auto editor = TextEditor::TextEditorWidget::currentTextEditorWidget()) {
+ if (m_client->reachable())
+ m_client->requestCompletions(editor);
+ }
+ });
+
+ ActionManager::registerAction(requestAction, Constants::COPILOT_REQUEST_SUGGESTION);
+
+ QAction *disableAction = new QAction(this);
+ disableAction->setText(Tr::tr("Disable Copilot"));
+ disableAction->setToolTip(Tr::tr("Disable Copilot."));
+ connect(disableAction, &QAction::triggered, this, [] {
+ CopilotSettings::instance().enableCopilot.setValue(true);
+ CopilotSettings::instance().apply();
+ });
+ ActionManager::registerAction(disableAction, Constants::COPILOT_DISABLE);
+
+ QAction *enableAction = new QAction(this);
+ enableAction->setText(Tr::tr("Enable Copilot"));
+ enableAction->setToolTip(Tr::tr("Enable Copilot."));
+ connect(enableAction, &QAction::triggered, this, [] {
+ CopilotSettings::instance().enableCopilot.setValue(false);
+ CopilotSettings::instance().apply();
+ });
+ ActionManager::registerAction(enableAction, Constants::COPILOT_ENABLE);
+
+ QAction *toggleAction = new QAction(this);
+ toggleAction->setText(Tr::tr("Toggle Copilot"));
+ toggleAction->setCheckable(true);
+ toggleAction->setChecked(CopilotSettings::instance().enableCopilot.value());
+ toggleAction->setIcon(COPILOT_ICON.icon());
+ connect(toggleAction, &QAction::toggled, this, [](bool checked) {
+ CopilotSettings::instance().enableCopilot.setValue(checked);
+ CopilotSettings::instance().apply();
+ });
+
+ ActionManager::registerAction(toggleAction, Constants::COPILOT_TOGGLE);
+
+ auto updateActions = [toggleAction, requestAction] {
+ const bool enabled = CopilotSettings::instance().enableCopilot.value();
+ toggleAction->setToolTip(enabled ? Tr::tr("Disable Copilot.") : Tr::tr("Enable Copilot."));
+ toggleAction->setChecked(enabled);
+ requestAction->setEnabled(enabled);
+ };
+
+ connect(&CopilotSettings::instance().enableCopilot,
+ &BoolAspect::valueChanged,
+ this,
+ updateActions);
+
+ updateActions();
+
+ auto toggleButton = new QToolButton;
+ toggleButton->setDefaultAction(toggleAction);
+ StatusBarManager::addStatusBarWidget(toggleButton, StatusBarManager::RightCorner);
+
+ auto panelFactory = new ProjectPanelFactory;
+ panelFactory->setPriority(1000);
+ panelFactory->setDisplayName(Tr::tr("Copilot"));
+ panelFactory->setCreateWidgetFunction(&Internal::createCopilotProjectPanel);
+ ProjectPanelFactory::registerFactory(panelFactory);
+}
+
+void CopilotPlugin::extensionsInitialized()
+{
+ (void)CopilotOptionsPage::instance();
+}
+
+void CopilotPlugin::restartClient()
+{
+ LanguageClient::LanguageClientManager::shutdownClient(m_client);
+
+ if (!CopilotSettings::instance().nodeJsPath().isExecutableFile())
+ return;
+ m_client = new CopilotClient(CopilotSettings::instance().nodeJsPath(),
+ CopilotSettings::instance().distPath());
+}
+
+ExtensionSystem::IPlugin::ShutdownFlag CopilotPlugin::aboutToShutdown()
+{
+ if (!m_client)
+ return SynchronousShutdown;
+ connect(m_client, &QObject::destroyed, this, &IPlugin::asynchronousShutdownFinished);
+ return AsynchronousShutdown;
+}
+
+} // namespace Internal
+} // namespace Copilot
diff --git a/src/plugins/copilot/copilotplugin.h b/src/plugins/copilot/copilotplugin.h
new file mode 100644
index 0000000000..8c1a176a73
--- /dev/null
+++ b/src/plugins/copilot/copilotplugin.h
@@ -0,0 +1,31 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "copilotclient.h"
+
+#include <extensionsystem/iplugin.h>
+
+#include <QPointer>
+
+namespace Copilot {
+namespace Internal {
+
+class CopilotPlugin : public ExtensionSystem::IPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Copilot.json")
+
+public:
+ void initialize() override;
+ void extensionsInitialized() override;
+ void restartClient();
+ ShutdownFlag aboutToShutdown() override;
+
+private:
+ QPointer<CopilotClient> m_client;
+};
+
+} // namespace Internal
+} // namespace Copilot
diff --git a/src/plugins/copilot/copilotprojectpanel.cpp b/src/plugins/copilot/copilotprojectpanel.cpp
new file mode 100644
index 0000000000..f1eb5d119e
--- /dev/null
+++ b/src/plugins/copilot/copilotprojectpanel.cpp
@@ -0,0 +1,59 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "copilotprojectpanel.h"
+#include "copilotconstants.h"
+#include "copilotsettings.h"
+
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectsettingswidget.h>
+
+#include <utils/layoutbuilder.h>
+
+#include <QWidget>
+
+using namespace ProjectExplorer;
+
+namespace Copilot::Internal {
+
+class CopilotProjectSettingsWidget : public ProjectExplorer::ProjectSettingsWidget
+{
+public:
+ CopilotProjectSettingsWidget()
+ {
+ setGlobalSettingsId(Constants::COPILOT_GENERAL_OPTIONS_ID);
+ setUseGlobalSettingsCheckBoxVisible(true);
+ }
+};
+
+ProjectSettingsWidget *createCopilotProjectPanel(Project *project)
+{
+ using namespace Layouting;
+ using namespace ProjectExplorer;
+
+ auto widget = new CopilotProjectSettingsWidget;
+ auto settings = new CopilotProjectSettings(project, widget);
+
+ QObject::connect(widget,
+ &ProjectSettingsWidget::useGlobalSettingsChanged,
+ settings,
+ &CopilotProjectSettings::setUseGlobalSettings);
+
+ widget->setUseGlobalSettings(settings->useGlobalSettings());
+ widget->setEnabled(!settings->useGlobalSettings());
+
+ QObject::connect(widget,
+ &ProjectSettingsWidget::useGlobalSettingsChanged,
+ widget,
+ [widget](bool useGlobal) { widget->setEnabled(!useGlobal); });
+
+ // clang-format off
+ Column {
+ settings->enableCopilot,
+ }.attachTo(widget);
+ // clang-format on
+
+ return widget;
+}
+
+} // namespace Copilot::Internal
diff --git a/src/plugins/copilot/copilotprojectpanel.h b/src/plugins/copilot/copilotprojectpanel.h
new file mode 100644
index 0000000000..2f4be6d6f9
--- /dev/null
+++ b/src/plugins/copilot/copilotprojectpanel.h
@@ -0,0 +1,15 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace ProjectExplorer {
+class ProjectSettingsWidget;
+class Project;
+} // namespace ProjectExplorer
+
+namespace Copilot::Internal {
+
+ProjectExplorer::ProjectSettingsWidget *createCopilotProjectPanel(ProjectExplorer::Project *project);
+
+} // namespace Copilot::Internal
diff --git a/src/plugins/copilot/copilotsettings.cpp b/src/plugins/copilot/copilotsettings.cpp
new file mode 100644
index 0000000000..b8567f2feb
--- /dev/null
+++ b/src/plugins/copilot/copilotsettings.cpp
@@ -0,0 +1,118 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "copilotsettings.h"
+#include "copilotconstants.h"
+#include "copilottr.h"
+
+#include <projectexplorer/project.h>
+
+#include <utils/algorithm.h>
+#include <utils/environment.h>
+
+using namespace Utils;
+
+namespace Copilot {
+
+static void initEnableAspect(BoolAspect &enableCopilot)
+{
+ enableCopilot.setSettingsKey(Constants::ENABLE_COPILOT);
+ enableCopilot.setDisplayName(Tr::tr("Enable Copilot"));
+ enableCopilot.setLabelText(Tr::tr("Enable Copilot"));
+ enableCopilot.setToolTip(Tr::tr("Enables the Copilot integration."));
+ enableCopilot.setDefaultValue(true);
+}
+
+CopilotSettings &CopilotSettings::instance()
+{
+ static CopilotSettings settings;
+ return settings;
+}
+
+CopilotSettings::CopilotSettings()
+{
+ setAutoApply(false);
+
+ const FilePath nodeFromPath = FilePath("node").searchInPath();
+
+ const FilePaths searchDirs
+ = {FilePath::fromUserInput("~/.vim/pack/github/start/copilot.vim/copilot/dist/agent.js"),
+ FilePath::fromUserInput(
+ "~/.config/nvim/pack/github/start/copilot.vim/copilot/dist/agent.js"),
+ FilePath::fromUserInput(
+ "~/vimfiles/pack/github/start/copilot.vim/copilot/dist/agent.js"),
+ FilePath::fromUserInput(
+ "~/AppData/Local/nvim/pack/github/start/copilot.vim/copilot/dist/agent.js")};
+
+ const FilePath distFromVim = findOrDefault(searchDirs, &FilePath::exists);
+
+ nodeJsPath.setExpectedKind(PathChooser::ExistingCommand);
+ nodeJsPath.setDefaultFilePath(nodeFromPath);
+ nodeJsPath.setSettingsKey("Copilot.NodeJsPath");
+ nodeJsPath.setLabelText(Tr::tr("Node.js path:"));
+ nodeJsPath.setHistoryCompleter("Copilot.NodePath.History");
+ nodeJsPath.setDisplayName(Tr::tr("Node.js Path"));
+ nodeJsPath.setToolTip(
+ Tr::tr("Select path to node.js executable. See https://nodejs.org/en/download/"
+ "for installation instructions."));
+
+ distPath.setExpectedKind(PathChooser::File);
+ distPath.setDefaultFilePath(distFromVim);
+ distPath.setSettingsKey("Copilot.DistPath");
+ distPath.setLabelText(Tr::tr("Path to agent.js:"));
+ distPath.setHistoryCompleter("Copilot.DistPath.History");
+ distPath.setDisplayName(Tr::tr("Agent.js path"));
+ distPath.setToolTip(Tr::tr(
+ "Select path to agent.js in Copilot Neovim plugin. See "
+ "https://github.com/github/copilot.vim#getting-started for installation instructions."));
+
+ autoComplete.setDisplayName(Tr::tr("Auto Complete"));
+ autoComplete.setSettingsKey("Copilot.Autocomplete");
+ autoComplete.setLabelText(Tr::tr("Request completions automatically"));
+ autoComplete.setDefaultValue(true);
+ autoComplete.setToolTip(Tr::tr("Automatically request suggestions for the current text cursor "
+ "position after changes to the document."));
+
+ initEnableAspect(enableCopilot);
+}
+
+CopilotProjectSettings::CopilotProjectSettings(ProjectExplorer::Project *project, QObject *parent)
+ : AspectContainer(parent)
+{
+ setAutoApply(true);
+
+ useGlobalSettings.setSettingsKey(Constants::COPILOT_USE_GLOBAL_SETTINGS);
+ useGlobalSettings.setDefaultValue(true);
+
+ initEnableAspect(enableCopilot);
+
+ QVariantMap map = project->namedSettings(Constants::COPILOT_PROJECT_SETTINGS_ID).toMap();
+ fromMap(map);
+
+ connect(&enableCopilot, &BoolAspect::valueChanged, this, [this, project] { save(project); });
+ connect(&useGlobalSettings, &BoolAspect::valueChanged, this, [this, project] { save(project); });
+}
+
+void CopilotProjectSettings::setUseGlobalSettings(bool useGlobal)
+{
+ useGlobalSettings.setValue(useGlobal);
+}
+
+bool CopilotProjectSettings::isEnabled() const
+{
+ if (useGlobalSettings())
+ return CopilotSettings::instance().enableCopilot();
+ return enableCopilot();
+}
+
+void CopilotProjectSettings::save(ProjectExplorer::Project *project)
+{
+ QVariantMap map;
+ toMap(map);
+ project->setNamedSettings(Constants::COPILOT_PROJECT_SETTINGS_ID, map);
+
+ // This triggers a restart of the Copilot language server.
+ CopilotSettings::instance().apply();
+}
+
+} // namespace Copilot
diff --git a/src/plugins/copilot/copilotsettings.h b/src/plugins/copilot/copilotsettings.h
new file mode 100644
index 0000000000..cec44c43fe
--- /dev/null
+++ b/src/plugins/copilot/copilotsettings.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <utils/aspects.h>
+
+namespace ProjectExplorer {
+class Project;
+}
+
+namespace Copilot {
+
+class CopilotSettings : public Utils::AspectContainer
+{
+public:
+ CopilotSettings();
+
+ static CopilotSettings &instance();
+
+ Utils::FilePathAspect nodeJsPath{this};
+ Utils::FilePathAspect distPath{this};
+ Utils::BoolAspect autoComplete{this};
+ Utils::BoolAspect enableCopilot{this};
+};
+
+class CopilotProjectSettings : public Utils::AspectContainer
+{
+public:
+ CopilotProjectSettings(ProjectExplorer::Project *project, QObject *parent = nullptr);
+
+ void save(ProjectExplorer::Project *project);
+ void setUseGlobalSettings(bool useGlobalSettings);
+
+ bool isEnabled() const;
+
+ Utils::BoolAspect enableCopilot{this};
+ Utils::BoolAspect useGlobalSettings{this};
+};
+
+} // namespace Copilot
diff --git a/src/plugins/copilot/copilotsuggestion.cpp b/src/plugins/copilot/copilotsuggestion.cpp
new file mode 100644
index 0000000000..28da286f1f
--- /dev/null
+++ b/src/plugins/copilot/copilotsuggestion.cpp
@@ -0,0 +1,79 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "copilotsuggestion.h"
+
+#include <texteditor/texteditor.h>
+
+#include <utils/stringutils.h>
+
+using namespace Utils;
+using namespace TextEditor;
+using namespace LanguageServerProtocol;
+
+namespace Copilot::Internal {
+
+CopilotSuggestion::CopilotSuggestion(const QList<Completion> &completions,
+ QTextDocument *origin,
+ int currentCompletion)
+ : m_completions(completions)
+ , m_currentCompletion(currentCompletion)
+{
+ const Completion completion = completions.value(currentCompletion);
+ const Position start = completion.range().start();
+ const Position end = completion.range().end();
+ QString text = start.toTextCursor(origin).block().text();
+ int length = text.length() - start.character();
+ if (start.line() == end.line())
+ length = end.character() - start.character();
+ text.replace(start.character(), length, completion.text());
+ document()->setPlainText(text);
+ m_start = completion.position().toTextCursor(origin);
+ m_start.setKeepPositionOnInsert(true);
+ setCurrentPosition(m_start.position());
+}
+
+bool CopilotSuggestion::apply()
+{
+ reset();
+ const Completion completion = m_completions.value(m_currentCompletion);
+ QTextCursor cursor = completion.range().toSelection(m_start.document());
+ cursor.insertText(completion.text());
+ return true;
+}
+
+bool CopilotSuggestion::applyWord(TextEditorWidget *widget)
+{
+ const Completion completion = m_completions.value(m_currentCompletion);
+ const QTextCursor cursor = completion.range().toSelection(m_start.document());
+ QTextCursor currentCursor = widget->textCursor();
+ const QString text = completion.text();
+ const int startPos = currentCursor.positionInBlock() - cursor.positionInBlock()
+ + (cursor.selectionEnd() - cursor.selectionStart());
+ const int next = endOfNextWord(text, startPos);
+
+ if (next == -1)
+ return apply();
+
+ // TODO: Allow adding more than one line
+ QString subText = text.mid(startPos, next - startPos);
+ subText = subText.left(subText.indexOf('\n'));
+ if (subText.isEmpty())
+ return false;
+
+ currentCursor.insertText(subText);
+ return false;
+}
+
+void CopilotSuggestion::reset()
+{
+ m_start.removeSelectedText();
+}
+
+int CopilotSuggestion::position()
+{
+ return m_start.position();
+}
+
+} // namespace Copilot::Internal
+
diff --git a/src/plugins/copilot/copilotsuggestion.h b/src/plugins/copilot/copilotsuggestion.h
new file mode 100644
index 0000000000..719016236a
--- /dev/null
+++ b/src/plugins/copilot/copilotsuggestion.h
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#pragma once
+
+#include "requests/getcompletions.h"
+
+#include <texteditor/textdocumentlayout.h>
+#include <texteditor/texteditor.h>
+
+namespace Copilot::Internal {
+
+class CopilotSuggestion final : public TextEditor::TextSuggestion
+{
+public:
+ CopilotSuggestion(const QList<Completion> &completions,
+ QTextDocument *origin,
+ int currentCompletion = 0);
+
+ bool apply() final;
+ bool applyWord(TextEditor::TextEditorWidget *widget) final;
+ void reset() final;
+ int position() final;
+
+ const QList<Completion> &completions() const { return m_completions; }
+ int currentCompletion() const { return m_currentCompletion; }
+
+private:
+ QList<Completion> m_completions;
+ int m_currentCompletion = 0;
+ QTextCursor m_start;
+};
+} // namespace Copilot::Internal
diff --git a/src/plugins/copilot/copilottr.h b/src/plugins/copilot/copilottr.h
new file mode 100644
index 0000000000..42fef818db
--- /dev/null
+++ b/src/plugins/copilot/copilottr.h
@@ -0,0 +1,15 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QCoreApplication>
+
+namespace Copilot {
+
+struct Tr
+{
+ Q_DECLARE_TR_FUNCTIONS(QtC::Copilot)
+};
+
+} // namespace Copilot
diff --git a/src/plugins/copilot/images/copilot.png b/src/plugins/copilot/images/copilot.png
new file mode 100644
index 0000000000..00d63f6560
--- /dev/null
+++ b/src/plugins/copilot/images/copilot.png
Binary files differ
diff --git a/src/plugins/copilot/images/copilot@2x.png b/src/plugins/copilot/images/copilot@2x.png
new file mode 100644
index 0000000000..add84de171
--- /dev/null
+++ b/src/plugins/copilot/images/copilot@2x.png
Binary files differ
diff --git a/src/plugins/copilot/images/settingscategory_copilot.png b/src/plugins/copilot/images/settingscategory_copilot.png
new file mode 100644
index 0000000000..9a47fb2413
--- /dev/null
+++ b/src/plugins/copilot/images/settingscategory_copilot.png
Binary files differ
diff --git a/src/plugins/copilot/images/settingscategory_copilot@2x.png b/src/plugins/copilot/images/settingscategory_copilot@2x.png
new file mode 100644
index 0000000000..0437465aff
--- /dev/null
+++ b/src/plugins/copilot/images/settingscategory_copilot@2x.png
Binary files differ
diff --git a/src/plugins/copilot/requests/checkstatus.h b/src/plugins/copilot/requests/checkstatus.h
new file mode 100644
index 0000000000..20e77eb145
--- /dev/null
+++ b/src/plugins/copilot/requests/checkstatus.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <languageserverprotocol/jsonrpcmessages.h>
+#include <languageserverprotocol/lsptypes.h>
+
+namespace Copilot {
+
+class CheckStatusParams : public LanguageServerProtocol::JsonObject
+{
+ static constexpr char16_t optionsKey[] = u"options";
+ static constexpr char16_t localChecksOnlyKey[] = u"options";
+
+public:
+ using JsonObject::JsonObject;
+
+ CheckStatusParams(bool localChecksOnly = false) { setLocalChecksOnly(localChecksOnly); }
+
+ void setLocalChecksOnly(bool localChecksOnly)
+ {
+ QJsonObject options;
+ options.insert(localChecksOnlyKey, localChecksOnly);
+ setOptions(options);
+ }
+
+ void setOptions(QJsonObject options) { insert(optionsKey, options); }
+};
+
+class CheckStatusResponse : public LanguageServerProtocol::JsonObject
+{
+ static constexpr char16_t userKey[] = u"user";
+ static constexpr char16_t statusKey[] = u"status";
+
+public:
+ using JsonObject::JsonObject;
+
+ QString status() const { return typedValue<QString>(statusKey); }
+ QString user() const { return typedValue<QString>(userKey); }
+};
+
+class CheckStatusRequest
+ : public LanguageServerProtocol::Request<CheckStatusResponse, std::nullptr_t, CheckStatusParams>
+{
+public:
+ explicit CheckStatusRequest(const CheckStatusParams &params)
+ : Request(methodName, params)
+ {}
+ using Request::Request;
+ constexpr static const char methodName[] = "checkStatus";
+};
+
+} // namespace Copilot
diff --git a/src/plugins/copilot/requests/getcompletions.h b/src/plugins/copilot/requests/getcompletions.h
new file mode 100644
index 0000000000..d602fea97d
--- /dev/null
+++ b/src/plugins/copilot/requests/getcompletions.h
@@ -0,0 +1,119 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <languageserverprotocol/jsonkeys.h>
+#include <languageserverprotocol/jsonrpcmessages.h>
+#include <languageserverprotocol/lsptypes.h>
+
+namespace Copilot {
+
+class Completion : public LanguageServerProtocol::JsonObject
+{
+ static constexpr char16_t displayTextKey[] = u"displayText";
+ static constexpr char16_t uuidKey[] = u"uuid";
+
+public:
+ using JsonObject::JsonObject;
+
+ QString displayText() const { return typedValue<QString>(displayTextKey); }
+ LanguageServerProtocol::Position position() const
+ {
+ return typedValue<LanguageServerProtocol::Position>(LanguageServerProtocol::positionKey);
+ }
+ LanguageServerProtocol::Range range() const
+ {
+ return typedValue<LanguageServerProtocol::Range>(LanguageServerProtocol::rangeKey);
+ }
+ QString text() const { return typedValue<QString>(LanguageServerProtocol::textKey); }
+ QString uuid() const { return typedValue<QString>(uuidKey); }
+
+ bool isValid() const override
+ {
+ return contains(LanguageServerProtocol::textKey)
+ && contains(LanguageServerProtocol::rangeKey)
+ && contains(LanguageServerProtocol::positionKey);
+ }
+};
+
+class GetCompletionParams : public LanguageServerProtocol::JsonObject
+{
+public:
+ static constexpr char16_t docKey[] = u"doc";
+
+ GetCompletionParams(const LanguageServerProtocol::TextDocumentIdentifier &document,
+ int version,
+ const LanguageServerProtocol::Position &position)
+ {
+ setTextDocument(document);
+ setVersion(version);
+ setPosition(position);
+ }
+ using JsonObject::JsonObject;
+
+ // The text document.
+ LanguageServerProtocol::TextDocumentIdentifier textDocument() const
+ {
+ return typedValue<LanguageServerProtocol::TextDocumentIdentifier>(docKey);
+ }
+ void setTextDocument(const LanguageServerProtocol::TextDocumentIdentifier &id)
+ {
+ insert(docKey, id);
+ }
+
+ // The position inside the text document.
+ LanguageServerProtocol::Position position() const
+ {
+ return LanguageServerProtocol::fromJsonValue<LanguageServerProtocol::Position>(
+ value(docKey).toObject().value(LanguageServerProtocol::positionKey));
+ }
+ void setPosition(const LanguageServerProtocol::Position &position)
+ {
+ QJsonObject result = value(docKey).toObject();
+ result[LanguageServerProtocol::positionKey] = (QJsonObject) position;
+ insert(docKey, result);
+ }
+
+ // The version
+ int version() const { return typedValue<int>(LanguageServerProtocol::versionKey); }
+ void setVersion(int version)
+ {
+ QJsonObject result = value(docKey).toObject();
+ result[LanguageServerProtocol::versionKey] = version;
+ insert(docKey, result);
+ }
+
+ bool isValid() const override
+ {
+ return contains(docKey)
+ && value(docKey).toObject().contains(LanguageServerProtocol::positionKey)
+ && value(docKey).toObject().contains(LanguageServerProtocol::versionKey);
+ }
+};
+
+class GetCompletionResponse : public LanguageServerProtocol::JsonObject
+{
+ static constexpr char16_t completionKey[] = u"completions";
+
+public:
+ using JsonObject::JsonObject;
+
+ LanguageServerProtocol::LanguageClientArray<Completion> completions() const
+ {
+ return clientArray<Completion>(completionKey);
+ }
+};
+
+class GetCompletionRequest
+ : public LanguageServerProtocol::Request<GetCompletionResponse, std::nullptr_t, GetCompletionParams>
+{
+public:
+ explicit GetCompletionRequest(const GetCompletionParams &params = {})
+ : Request(methodName, params)
+ {}
+ using Request::Request;
+ constexpr static const char methodName[] = "getCompletionsCycling";
+};
+
+} // namespace Copilot
diff --git a/src/plugins/copilot/requests/signinconfirm.h b/src/plugins/copilot/requests/signinconfirm.h
new file mode 100644
index 0000000000..64f4ce7d53
--- /dev/null
+++ b/src/plugins/copilot/requests/signinconfirm.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "checkstatus.h"
+
+#include <languageserverprotocol/jsonrpcmessages.h>
+#include <languageserverprotocol/lsptypes.h>
+
+namespace Copilot {
+
+class SignInConfirmParams : public LanguageServerProtocol::JsonObject
+{
+ static constexpr char16_t userCodeKey[] = u"userCode";
+
+public:
+ using JsonObject::JsonObject;
+
+ SignInConfirmParams(const QString &userCode) { setUserCode(userCode); }
+
+ void setUserCode(const QString &userCode) { insert(userCodeKey, userCode); }
+};
+
+class SignInConfirmRequest
+ : public LanguageServerProtocol::Request<CheckStatusResponse, std::nullptr_t, SignInConfirmParams>
+{
+public:
+ explicit SignInConfirmRequest(const QString &userCode)
+ : Request(methodName, {userCode})
+ {}
+ using Request::Request;
+ constexpr static const char methodName[] = "signInConfirm";
+};
+
+} // namespace Copilot
diff --git a/src/plugins/copilot/requests/signininitiate.h b/src/plugins/copilot/requests/signininitiate.h
new file mode 100644
index 0000000000..005205e6e0
--- /dev/null
+++ b/src/plugins/copilot/requests/signininitiate.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <languageserverprotocol/jsonrpcmessages.h>
+#include <languageserverprotocol/lsptypes.h>
+
+namespace Copilot {
+
+using SignInInitiateParams = LanguageServerProtocol::JsonObject;
+
+class SignInInitiateResponse : public LanguageServerProtocol::JsonObject
+{
+ static constexpr char16_t verificationUriKey[] = u"verificationUri";
+ static constexpr char16_t userCodeKey[] = u"userCode";
+
+public:
+ using JsonObject::JsonObject;
+
+public:
+ QString verificationUri() const { return typedValue<QString>(verificationUriKey); }
+ QString userCode() const { return typedValue<QString>(userCodeKey); }
+};
+
+class SignInInitiateRequest : public LanguageServerProtocol::Request<SignInInitiateResponse,
+ std::nullptr_t,
+ SignInInitiateParams>
+{
+public:
+ explicit SignInInitiateRequest()
+ : Request(methodName, {})
+ {}
+ using Request::Request;
+ constexpr static const char methodName[] = "signInInitiate";
+};
+
+} // namespace Copilot
diff --git a/src/plugins/copilot/requests/signout.h b/src/plugins/copilot/requests/signout.h
new file mode 100644
index 0000000000..944c10d414
--- /dev/null
+++ b/src/plugins/copilot/requests/signout.h
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "checkstatus.h"
+
+#include <languageserverprotocol/jsonrpcmessages.h>
+#include <languageserverprotocol/lsptypes.h>
+
+namespace Copilot {
+
+using SignOutParams = LanguageServerProtocol::JsonObject;
+
+class SignOutRequest
+ : public LanguageServerProtocol::Request<CheckStatusResponse, std::nullptr_t, SignOutParams>
+{
+public:
+ explicit SignOutRequest()
+ : Request(methodName, {})
+ {}
+ using Request::Request;
+ constexpr static const char methodName[] = "signOut";
+};
+
+} // namespace Copilot
diff --git a/src/plugins/coreplugin/CMakeLists.txt b/src/plugins/coreplugin/CMakeLists.txt
index 17d1af01e3..b262d0d768 100644
--- a/src/plugins/coreplugin/CMakeLists.txt
+++ b/src/plugins/coreplugin/CMakeLists.txt
@@ -7,156 +7,302 @@ add_qtc_plugin(Core
DEPENDS Qt::PrintSupport Qt::Qml Qt::Sql Qt::Gui Qt::GuiPrivate
PUBLIC_DEPENDS Aggregation ExtensionSystem Utils app_version
SOURCES
- actionmanager/actioncontainer.cpp actionmanager/actioncontainer.h actionmanager/actioncontainer_p.h
- actionmanager/actionmanager.cpp actionmanager/actionmanager.h actionmanager/actionmanager_p.h
- actionmanager/command.cpp actionmanager/command.h actionmanager/command_p.h
- actionmanager/commandbutton.cpp actionmanager/commandbutton.h
- actionmanager/commandmappings.cpp actionmanager/commandmappings.h
- actionmanager/commandsfile.cpp actionmanager/commandsfile.h
- actionsfilter.cpp actionsfilter.h
- basefilewizard.cpp basefilewizard.h
- basefilewizardfactory.cpp basefilewizardfactory.h
${CMAKE_CURRENT_BINARY_DIR}/core_logo_cmake.qrc
+ actionmanager/actioncontainer.cpp
+ actionmanager/actioncontainer.h
+ actionmanager/actioncontainer_p.h
+ actionmanager/actionmanager.cpp
+ actionmanager/actionmanager.h
+ actionmanager/actionmanager_p.h
+ actionmanager/command.cpp
+ actionmanager/command.h
+ actionmanager/command_p.h
+ actionmanager/commandbutton.cpp
+ actionmanager/commandbutton.h
+ actionmanager/commandmappings.cpp
+ actionmanager/commandmappings.h
+ actionmanager/commandsfile.cpp
+ actionmanager/commandsfile.h
+ actionsfilter.cpp
+ actionsfilter.h
+ basefilewizard.cpp
+ basefilewizard.h
+ basefilewizardfactory.cpp
+ basefilewizardfactory.h
core.qrc
core_global.h
coreconstants.h
- coreicons.cpp coreicons.h
- corejsextensions.cpp corejsextensions.h
- coreplugin.cpp coreplugin.h
+ coreicons.cpp
+ coreicons.h
+ corejsextensions.cpp
+ corejsextensions.h
+ coreplugin.cpp
+ coreplugin.h
coreplugintr.h
- designmode.cpp designmode.h
- dialogs/addtovcsdialog.cpp dialogs/addtovcsdialog.h
- dialogs/codecselector.cpp dialogs/codecselector.h
- dialogs/externaltoolconfig.cpp dialogs/externaltoolconfig.h
- dialogs/filepropertiesdialog.cpp dialogs/filepropertiesdialog.h
- dialogs/ioptionspage.cpp dialogs/ioptionspage.h
- dialogs/newdialog.cpp dialogs/newdialog.h
- dialogs/newdialogwidget.cpp dialogs/newdialogwidget.h
- dialogs/openwithdialog.cpp dialogs/openwithdialog.h
- dialogs/promptoverwritedialog.cpp dialogs/promptoverwritedialog.h
- dialogs/readonlyfilesdialog.cpp dialogs/readonlyfilesdialog.h
- dialogs/restartdialog.cpp dialogs/restartdialog.h
- dialogs/saveitemsdialog.cpp dialogs/saveitemsdialog.h
- dialogs/settingsdialog.cpp dialogs/settingsdialog.h
- dialogs/shortcutsettings.cpp dialogs/shortcutsettings.h
- diffservice.cpp diffservice.h
- documentmanager.cpp documentmanager.h
- editmode.cpp editmode.h
- editormanager/documentmodel.cpp editormanager/documentmodel.h editormanager/documentmodel_p.h
- editormanager/editorarea.cpp editormanager/editorarea.h
- editormanager/editormanager.cpp editormanager/editormanager.h editormanager/editormanager_p.h
- editormanager/editorview.cpp editormanager/editorview.h
- editormanager/editorwindow.cpp editormanager/editorwindow.h
- editormanager/ieditor.cpp editormanager/ieditor.h
- editormanager/ieditorfactory.cpp editormanager/ieditorfactory.h editormanager/ieditorfactory_p.h
- editormanager/iexternaleditor.cpp editormanager/iexternaleditor.h
- editormanager/openeditorsview.cpp editormanager/openeditorsview.h
- editormanager/openeditorswindow.cpp editormanager/openeditorswindow.h
- editormanager/systemeditor.cpp editormanager/systemeditor.h
- editortoolbar.cpp editortoolbar.h
- externaltool.cpp externaltool.h
- externaltoolmanager.cpp externaltoolmanager.h
- fancyactionbar.cpp fancyactionbar.h
+ designmode.cpp
+ designmode.h
+ dialogs/addtovcsdialog.cpp
+ dialogs/addtovcsdialog.h
+ dialogs/codecselector.cpp
+ dialogs/codecselector.h
+ dialogs/externaltoolconfig.cpp
+ dialogs/externaltoolconfig.h
+ dialogs/filepropertiesdialog.cpp
+ dialogs/filepropertiesdialog.h
+ dialogs/ioptionspage.cpp
+ dialogs/ioptionspage.h
+ dialogs/newdialog.cpp
+ dialogs/newdialog.h
+ dialogs/newdialogwidget.cpp
+ dialogs/newdialogwidget.h
+ dialogs/openwithdialog.cpp
+ dialogs/openwithdialog.h
+ dialogs/promptoverwritedialog.cpp
+ dialogs/promptoverwritedialog.h
+ dialogs/readonlyfilesdialog.cpp
+ dialogs/readonlyfilesdialog.h
+ dialogs/restartdialog.cpp
+ dialogs/restartdialog.h
+ dialogs/saveitemsdialog.cpp
+ dialogs/saveitemsdialog.h
+ dialogs/settingsdialog.cpp
+ dialogs/settingsdialog.h
+ dialogs/shortcutsettings.cpp
+ dialogs/shortcutsettings.h
+ diffservice.cpp
+ diffservice.h
+ documentmanager.cpp
+ documentmanager.h
+ editmode.cpp
+ editmode.h
+ editormanager/documentmodel.cpp
+ editormanager/documentmodel.h
+ editormanager/documentmodel_p.h
+ editormanager/editorarea.cpp
+ editormanager/editorarea.h
+ editormanager/editormanager.cpp
+ editormanager/editormanager.h
+ editormanager/editormanager_p.h
+ editormanager/editorview.cpp
+ editormanager/editorview.h
+ editormanager/editorwindow.cpp
+ editormanager/editorwindow.h
+ editormanager/ieditor.cpp
+ editormanager/ieditor.h
+ editormanager/ieditorfactory.cpp
+ editormanager/ieditorfactory.h
+ editormanager/ieditorfactory_p.h
+ editormanager/iexternaleditor.cpp
+ editormanager/iexternaleditor.h
+ editormanager/openeditorsview.cpp
+ editormanager/openeditorsview.h
+ editormanager/openeditorswindow.cpp
+ editormanager/openeditorswindow.h
+ editormanager/systemeditor.cpp
+ editormanager/systemeditor.h
+ editortoolbar.cpp
+ editortoolbar.h
+ externaltool.cpp
+ externaltool.h
+ externaltoolmanager.cpp
+ externaltoolmanager.h
+ fancyactionbar.cpp
+ fancyactionbar.h
fancyactionbar.qrc
- fancytabwidget.cpp fancytabwidget.h
- featureprovider.cpp featureprovider.h
- fileutils.cpp fileutils.h
- find/basetextfind.cpp find/basetextfind.h
- find/currentdocumentfind.cpp find/currentdocumentfind.h
+ fancytabwidget.cpp
+ fancytabwidget.h
+ featureprovider.cpp
+ featureprovider.h
+ fileutils.cpp
+ fileutils.h
+ find/basetextfind.cpp
+ find/basetextfind.h
+ find/currentdocumentfind.cpp
+ find/currentdocumentfind.h
find/find.qrc
- find/findplugin.cpp find/findplugin.h
- find/findtoolbar.cpp find/findtoolbar.h
- find/findtoolwindow.cpp find/findtoolwindow.h
- find/highlightscrollbarcontroller.cpp find/highlightscrollbarcontroller.h
- find/ifindfilter.cpp find/ifindfilter.h
- find/ifindsupport.cpp find/ifindsupport.h
- find/itemviewfind.cpp find/itemviewfind.h
- find/optionspopup.cpp find/optionspopup.h
- find/searchresultcolor.h
- find/searchresultitem.h
- find/searchresulttreeitemdelegate.cpp find/searchresulttreeitemdelegate.h
+ find/findplugin.cpp
+ find/findplugin.h
+ find/findtoolbar.cpp
+ find/findtoolbar.h
+ find/findtoolwindow.cpp
+ find/findtoolwindow.h
+ find/highlightscrollbarcontroller.cpp
+ find/highlightscrollbarcontroller.h
+ find/ifindfilter.cpp
+ find/ifindfilter.h
+ find/ifindsupport.cpp
+ find/ifindsupport.h
+ find/itemviewfind.cpp
+ find/itemviewfind.h
+ find/optionspopup.cpp
+ find/optionspopup.h
+ find/searchresulttreeitemdelegate.cpp
+ find/searchresulttreeitemdelegate.h
find/searchresulttreeitemroles.h
- find/searchresulttreeitems.cpp find/searchresulttreeitems.h
- find/searchresulttreemodel.cpp find/searchresulttreemodel.h
- find/searchresulttreeview.cpp find/searchresulttreeview.h
- find/searchresultwidget.cpp find/searchresultwidget.h
- find/searchresultwindow.cpp find/searchresultwindow.h
+ find/searchresulttreeitems.cpp
+ find/searchresulttreeitems.h
+ find/searchresulttreemodel.cpp
+ find/searchresulttreemodel.h
+ find/searchresulttreeview.cpp
+ find/searchresulttreeview.h
+ find/searchresultwidget.cpp
+ find/searchresultwidget.h
+ find/searchresultwindow.cpp
+ find/searchresultwindow.h
find/textfindconstants.h
- findplaceholder.cpp findplaceholder.h
+ findplaceholder.cpp
+ findplaceholder.h
foldernavigationwidget.cpp
foldernavigationwidget.h
- generalsettings.cpp generalsettings.h
- generatedfile.cpp generatedfile.h
- helpitem.cpp helpitem.h
- helpmanager.cpp helpmanager.h helpmanager_implementation.h
- icontext.cpp icontext.h
- icore.cpp icore.h
- idocument.cpp idocument.h
- idocumentfactory.cpp idocumentfactory.h
+ generalsettings.cpp
+ generalsettings.h
+ generatedfile.cpp
+ generatedfile.h
+ helpitem.cpp
+ helpitem.h
+ helpmanager.cpp
+ helpmanager.h
+ helpmanager_implementation.h
+ icontext.cpp
+ icontext.h
+ icore.cpp
+ icore.h
+ idocument.cpp
+ idocument.h
+ idocumentfactory.cpp
+ idocumentfactory.h
ifilewizardextension.h
- imode.cpp imode.h
- inavigationwidgetfactory.cpp inavigationwidgetfactory.h
- ioutputpane.cpp ioutputpane.h
- iversioncontrol.cpp iversioncontrol.h
- iwelcomepage.cpp iwelcomepage.h
- iwizardfactory.cpp iwizardfactory.h
- jsexpander.cpp jsexpander.h
- locator/basefilefilter.cpp locator/basefilefilter.h
- locator/commandlocator.cpp locator/commandlocator.h
- locator/directoryfilter.cpp locator/directoryfilter.h
- locator/executefilter.cpp locator/executefilter.h
- locator/externaltoolsfilter.cpp locator/externaltoolsfilter.h
- locator/filesystemfilter.cpp locator/filesystemfilter.h
- locator/ilocatorfilter.cpp locator/ilocatorfilter.h
- locator/javascriptfilter.cpp locator/javascriptfilter.h
- locator/locator.cpp locator/locator.h
+ imode.cpp
+ imode.h
+ inavigationwidgetfactory.cpp
+ inavigationwidgetfactory.h
+ ioutputpane.cpp
+ ioutputpane.h
+ iversioncontrol.cpp
+ iversioncontrol.h
+ iwelcomepage.cpp
+ iwelcomepage.h
+ iwizardfactory.cpp
+ iwizardfactory.h
+ jsexpander.cpp
+ jsexpander.h
+ locator/commandlocator.cpp
+ locator/commandlocator.h
+ locator/directoryfilter.cpp
+ locator/directoryfilter.h
+ locator/executefilter.cpp
+ locator/executefilter.h
+ locator/externaltoolsfilter.cpp
+ locator/externaltoolsfilter.h
+ locator/filesystemfilter.cpp
+ locator/filesystemfilter.h
+ locator/ilocatorfilter.cpp
+ locator/ilocatorfilter.h
+ locator/javascriptfilter.cpp
+ locator/javascriptfilter.h
+ locator/locator.cpp
+ locator/locator.h
locator/locatorconstants.h
- locator/locatorfiltersfilter.cpp locator/locatorfiltersfilter.h
- locator/locatormanager.cpp locator/locatormanager.h
- locator/locatorsearchutils.cpp locator/locatorsearchutils.h
- locator/locatorsettingspage.cpp locator/locatorsettingspage.h
- locator/locatorwidget.cpp locator/locatorwidget.h
- locator/opendocumentsfilter.cpp locator/opendocumentsfilter.h
- locator/spotlightlocatorfilter.h locator/spotlightlocatorfilter.cpp
- locator/urllocatorfilter.cpp locator/urllocatorfilter.h
- loggingviewer.cpp loggingviewer.h
- loggingmanager.cpp loggingmanager.h
- mainwindow.cpp mainwindow.h
- manhattanstyle.cpp manhattanstyle.h
- messagebox.cpp messagebox.h
- messagemanager.cpp messagemanager.h
- messageoutputwindow.cpp messageoutputwindow.h
- mimetypemagicdialog.cpp mimetypemagicdialog.h
- mimetypesettings.cpp mimetypesettings.h
- minisplitter.cpp minisplitter.h
- modemanager.cpp modemanager.h
- navigationsubwidget.cpp navigationsubwidget.h
- navigationwidget.cpp navigationwidget.h
- opendocumentstreeview.cpp opendocumentstreeview.h
- outputpane.cpp outputpane.h
- outputpanemanager.cpp outputpanemanager.h
- outputwindow.cpp outputwindow.h
- patchtool.cpp patchtool.h
- plugindialog.cpp plugindialog.h
- plugininstallwizard.cpp plugininstallwizard.h
- progressmanager/futureprogress.cpp progressmanager/futureprogress.h
- progressmanager/processprogress.cpp progressmanager/processprogress.h
- progressmanager/progressbar.cpp progressmanager/progressbar.h
- progressmanager/progressmanager.cpp progressmanager/progressmanager.h progressmanager/progressmanager_p.h
- progressmanager/progressview.cpp progressmanager/progressview.h
- progressmanager/taskprogress.cpp progressmanager/taskprogress.h
- rightpane.cpp rightpane.h
- settingsdatabase.cpp settingsdatabase.h
- sidebar.cpp sidebar.h
- sidebarwidget.cpp sidebarwidget.h
- statusbarmanager.cpp statusbarmanager.h
- systemsettings.cpp systemsettings.h
- textdocument.cpp textdocument.h
- themechooser.cpp themechooser.h
- vcsmanager.cpp vcsmanager.h
- versiondialog.cpp versiondialog.h
- welcomepagehelper.cpp welcomepagehelper.h
- windowsupport.cpp windowsupport.h
+ locator/locatorfiltersfilter.cpp
+ locator/locatorfiltersfilter.h
+ locator/locatormanager.cpp
+ locator/locatormanager.h
+ locator/locatorsettingspage.cpp
+ locator/locatorsettingspage.h
+ locator/locatorwidget.cpp
+ locator/locatorwidget.h
+ locator/opendocumentsfilter.cpp
+ locator/opendocumentsfilter.h
+ locator/spotlightlocatorfilter.cpp
+ locator/spotlightlocatorfilter.h
+ locator/urllocatorfilter.cpp
+ locator/urllocatorfilter.h
+ loggingmanager.cpp
+ loggingmanager.h
+ loggingviewer.cpp
+ loggingviewer.h
+ mainwindow.cpp
+ mainwindow.h
+ manhattanstyle.cpp
+ manhattanstyle.h
+ messagebox.cpp
+ messagebox.h
+ messagemanager.cpp
+ messagemanager.h
+ messageoutputwindow.cpp
+ messageoutputwindow.h
+ mimetypemagicdialog.cpp
+ mimetypemagicdialog.h
+ mimetypesettings.cpp
+ mimetypesettings.h
+ minisplitter.cpp
+ minisplitter.h
+ modemanager.cpp
+ modemanager.h
+ navigationsubwidget.cpp
+ navigationsubwidget.h
+ navigationwidget.cpp
+ navigationwidget.h
+ opendocumentstreeview.cpp
+ opendocumentstreeview.h
+ outputpane.cpp
+ outputpane.h
+ outputpanemanager.cpp
+ outputpanemanager.h
+ outputwindow.cpp
+ outputwindow.h
+ patchtool.cpp
+ patchtool.h
+ plugindialog.cpp
+ plugindialog.h
+ plugininstallwizard.cpp
+ plugininstallwizard.h
+ progressmanager/futureprogress.cpp
+ progressmanager/futureprogress.h
+ progressmanager/processprogress.cpp
+ progressmanager/processprogress.h
+ progressmanager/progressbar.cpp
+ progressmanager/progressbar.h
+ progressmanager/progressmanager.cpp
+ progressmanager/progressmanager.h
+ progressmanager/progressmanager_p.h
+ progressmanager/progressview.cpp
+ progressmanager/progressview.h
+ progressmanager/taskprogress.cpp
+ progressmanager/taskprogress.h
+ rightpane.cpp
+ rightpane.h
+ session.cpp
+ session.h
+ session_p.h
+ sessiondialog.cpp
+ sessiondialog.h
+ sessionmodel.cpp
+ sessionmodel.h
+ sessionview.cpp
+ sessionview.h
+ settingsdatabase.cpp
+ settingsdatabase.h
+ sidebar.cpp
+ sidebar.h
+ sidebarwidget.cpp
+ sidebarwidget.h
+ statusbarmanager.cpp
+ statusbarmanager.h
+ systemsettings.cpp
+ systemsettings.h
+ textdocument.cpp
+ textdocument.h
+ themechooser.cpp
+ themechooser.h
+ vcsmanager.cpp
+ vcsmanager.h
+ versiondialog.cpp
+ versiondialog.h
+ welcomepagehelper.cpp
+ welcomepagehelper.h
+ windowsupport.cpp
+ windowsupport.h
EXPLICIT_MOC dialogs/filepropertiesdialog.h
)
@@ -169,8 +315,10 @@ extend_qtc_plugin(Core
CONDITION WITH_TESTS
SOURCES
locator/locator_test.cpp
- locator/locatorfiltertest.cpp locator/locatorfiltertest.h
- testdatadir.cpp testdatadir.h
+ locator/locatorfiltertest.cpp
+ locator/locatorfiltertest.h
+ testdatadir.cpp
+ testdatadir.h
)
extend_qtc_plugin(Core
diff --git a/src/plugins/coreplugin/Core.json.in b/src/plugins/coreplugin/Core.json.in
index b3f36025bf..606085c91d 100644
--- a/src/plugins/coreplugin/Core.json.in
+++ b/src/plugins/coreplugin/Core.json.in
@@ -30,6 +30,14 @@
{
\"Name\" : \"-presentationMode\",
\"Description\" : \"Enable presentation mode with pop-ups for key combos\"
+ },
+ {
+ \"Name\" : \"-lastsession\",
+ \"Description\" : \"Restore the last session\"
+ },
+ {
+ \"Name\" : \"<session>\",
+ \"Description\" : \"Restore a saved session\"
}
],
$$dependencyList
diff --git a/src/plugins/coreplugin/actionmanager/actionmanager.cpp b/src/plugins/coreplugin/actionmanager/actionmanager.cpp
index 3d0a200283..54e58dc217 100644
--- a/src/plugins/coreplugin/actionmanager/actionmanager.cpp
+++ b/src/plugins/coreplugin/actionmanager/actionmanager.cpp
@@ -34,8 +34,6 @@ namespace Core::Internal {
class PresentationModeHandler : public QObject
{
- Q_OBJECT
-
public:
void connectCommand(Command *command);
@@ -520,5 +518,3 @@ void ActionManagerPrivate::saveSettings()
saveSettings(j.value());
}
}
-
-#include "actionmanager.moc"
diff --git a/src/plugins/coreplugin/actionmanager/command.cpp b/src/plugins/coreplugin/actionmanager/command.cpp
index 592ba08faa..23b398791f 100644
--- a/src/plugins/coreplugin/actionmanager/command.cpp
+++ b/src/plugins/coreplugin/actionmanager/command.cpp
@@ -289,6 +289,15 @@ QAction *Command::action() const
return d->m_action;
}
+QAction *Command::actionForContext(const Utils::Id &contextId) const
+{
+ auto it = d->m_contextActionMap.find(contextId);
+ if (it == d->m_contextActionMap.end())
+ return nullptr;
+
+ return *it;
+}
+
QString Command::stringWithAppendedShortcut(const QString &str) const
{
return Utils::ProxyAction::stringWithAppendedShortcut(str, keySequence());
@@ -338,8 +347,10 @@ void Internal::CommandPrivate::setCurrentContext(const Context &context)
m_context = context;
QAction *currentAction = nullptr;
- for (int i = 0; i < m_context.size(); ++i) {
- if (QAction *a = m_contextActionMap.value(m_context.at(i), nullptr)) {
+ for (const Id &id : std::as_const(m_context)) {
+ if (id == Constants::C_GLOBAL_CUTOFF)
+ break;
+ if (QAction *a = m_contextActionMap.value(id, nullptr)) {
currentAction = a;
break;
}
diff --git a/src/plugins/coreplugin/actionmanager/command.h b/src/plugins/coreplugin/actionmanager/command.h
index b900a50f55..73dd92a5eb 100644
--- a/src/plugins/coreplugin/actionmanager/command.h
+++ b/src/plugins/coreplugin/actionmanager/command.h
@@ -57,6 +57,8 @@ public:
Utils::Id id() const;
QAction *action() const;
+ QAction *actionForContext(const Utils::Id &contextId) const;
+
Context context() const;
void setAttribute(CommandAttribute attr);
diff --git a/src/plugins/coreplugin/actionmanager/commandbutton.cpp b/src/plugins/coreplugin/actionmanager/commandbutton.cpp
index ed724e42d0..a8dab39136 100644
--- a/src/plugins/coreplugin/actionmanager/commandbutton.cpp
+++ b/src/plugins/coreplugin/actionmanager/commandbutton.cpp
@@ -92,7 +92,8 @@ QString CommandAction::toolTipBase() const
}
/*!
- Sets the base tool tip that is extended with the command's shortcut.
+ Sets the base tool tip that is extended with the command's shortcut to
+ \a toolTipBase.
\sa toolTipBase()
*/
@@ -155,7 +156,8 @@ QString CommandButton::toolTipBase() const
}
/*!
- Sets the base tool tip that is extended with the command's shortcut.
+ Sets the base tool tip that is extended with the command's shortcut to
+ \a toolTipBase.
\sa toolTipBase()
*/
diff --git a/src/plugins/coreplugin/actionmanager/commandmappings.h b/src/plugins/coreplugin/actionmanager/commandmappings.h
index 67c864a0bf..a672526550 100644
--- a/src/plugins/coreplugin/actionmanager/commandmappings.h
+++ b/src/plugins/coreplugin/actionmanager/commandmappings.h
@@ -12,8 +12,6 @@ class QTreeWidget;
class QTreeWidgetItem;
QT_END_NAMESPACE
-namespace Utils { class FancyLineEdit; }
-
namespace Core {
namespace Internal { class CommandMappingsPrivate; }
diff --git a/src/plugins/coreplugin/actionsfilter.cpp b/src/plugins/coreplugin/actionsfilter.cpp
index 927e7da633..01772ac252 100644
--- a/src/plugins/coreplugin/actionsfilter.cpp
+++ b/src/plugins/coreplugin/actionsfilter.cpp
@@ -10,9 +10,11 @@
#include "icore.h"
#include "locator/locatormanager.h"
+#include <extensionsystem/pluginmanager.h>
+
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/fuzzymatcher.h>
-#include <utils/mapreduce.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
@@ -21,8 +23,11 @@
#include <QMenuBar>
#include <QPointer>
#include <QRegularExpression>
+#include <QtConcurrent>
#include <QTextDocument>
+using namespace Utils;
+
static const char lastTriggeredC[] = "LastTriggeredActions";
QT_BEGIN_NAMESPACE
@@ -34,6 +39,13 @@ QT_END_NAMESPACE
namespace Core::Internal {
+static const QList<QAction *> menuBarActions()
+{
+ QMenuBar *menuBar = Core::ActionManager::actionContainer(Constants::MENU_BAR)->menuBar();
+ QTC_ASSERT(menuBar, return {});
+ return menuBar->actions();
+}
+
ActionsFilter::ActionsFilter()
{
setId("Actions from the menu");
@@ -50,29 +62,21 @@ ActionsFilter::ActionsFilter()
});
}
-static const QList<QAction *> menuBarActions()
-{
- QMenuBar *menuBar = Core::ActionManager::actionContainer(Constants::MENU_BAR)->menuBar();
- QTC_ASSERT(menuBar, return {});
- return menuBar->actions();
-}
-
-QList<LocatorFilterEntry> ActionsFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry)
+static void matches(QPromise<void> &promise, const LocatorStorage &storage,
+ const LocatorFilterEntries &entries)
{
using Highlight = LocatorFilterEntry::HighlightInfo;
- if (entry.simplified().isEmpty())
- return m_entries;
-
- const QRegularExpression regExp = createRegExp(entry, Qt::CaseInsensitive, true);
-
+ using MatchLevel = ILocatorFilter::MatchLevel;
+ const QString input = storage.input();
+ const QRegularExpression regExp = ILocatorFilter::createRegExp(input, Qt::CaseInsensitive,
+ true);
using FilterResult = std::pair<MatchLevel, LocatorFilterEntry>;
const auto filter = [&](const LocatorFilterEntry &filterEntry) -> std::optional<FilterResult> {
- if (future.isCanceled())
+ if (promise.isCanceled())
return {};
Highlight highlight;
- const auto withHighlight = [&](LocatorFilterEntry result) {
+ const auto withHighlight = [&highlight](LocatorFilterEntry result) {
result.highlightInfo = highlight;
return result;
};
@@ -89,17 +93,18 @@ QList<LocatorFilterEntry> ActionsFilter::matchesFor(QFutureInterface<LocatorFilt
if (first == Highlight::DisplayName) {
const QRegularExpressionMatch displayMatch = regExp.match(filterEntry.displayName);
if (displayMatch.hasMatch())
- highlight = highlightInfo(displayMatch);
+ highlight = ILocatorFilter::highlightInfo(displayMatch);
const QRegularExpressionMatch extraMatch = regExp.match(filterEntry.extraInfo);
if (extraMatch.hasMatch()) {
- Highlight extraHighlight = highlightInfo(extraMatch, Highlight::ExtraInfo);
+ Highlight extraHighlight = ILocatorFilter::highlightInfo(extraMatch,
+ Highlight::ExtraInfo);
highlight.startsExtraInfo = extraHighlight.startsExtraInfo;
highlight.lengthsExtraInfo = extraHighlight.lengthsExtraInfo;
}
- if (filterEntry.displayName.startsWith(entry, Qt::CaseInsensitive))
+ if (filterEntry.displayName.startsWith(input, Qt::CaseInsensitive))
return FilterResult{MatchLevel::Best, withHighlight(filterEntry)};
- if (filterEntry.displayName.contains(entry, Qt::CaseInsensitive))
+ if (filterEntry.displayName.contains(input, Qt::CaseInsensitive))
return FilterResult{MatchLevel::Better, withHighlight(filterEntry)};
if (displayMatch.hasMatch())
return FilterResult{MatchLevel::Good, withHighlight(filterEntry)};
@@ -154,29 +159,53 @@ QList<LocatorFilterEntry> ActionsFilter::matchesFor(QFutureInterface<LocatorFilt
return {};
};
- QMap<MatchLevel, QList<LocatorFilterEntry>> filtered;
- const QList<std::optional<FilterResult>> filterResults = Utils::map(std::as_const(m_entries), filter)
- .results();
+ QMap<MatchLevel, LocatorFilterEntries> filtered;
+ const QList<std::optional<FilterResult>> filterResults
+ = QtConcurrent::blockingMapped(entries, filter);
+ if (promise.isCanceled())
+ return;
for (const std::optional<FilterResult> &filterResult : filterResults) {
+ if (promise.isCanceled())
+ return;
if (filterResult)
filtered[filterResult->first] << filterResult->second;
}
+ storage.reportOutput(std::accumulate(std::begin(filtered), std::end(filtered),
+ LocatorFilterEntries()));
+}
- QList<LocatorFilterEntry> result;
- for (const QList<LocatorFilterEntry> &sublist : std::as_const(filtered))
- result << sublist;
- return result;
+LocatorMatcherTasks ActionsFilter::matchers()
+{
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [this, storage](Async<void> &async) {
+ m_entries.clear();
+ m_indexes.clear();
+ QList<const QMenu *> processedMenus;
+ collectEntriesForLastTriggered();
+ for (QAction* action : menuBarActions())
+ collectEntriesForAction(action, {}, processedMenus);
+ collectEntriesForCommands();
+ if (storage->input().simplified().isEmpty()) {
+ storage->reportOutput(m_entries);
+ return TaskAction::StopWithDone;
+ }
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(matches, *storage, m_entries);
+ return TaskAction::Continue;
+ };
+
+ return {{AsyncTask<void>(onSetup), storage}};
}
-void ActionsFilter::accept(const LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const
+LocatorFilterEntry::Acceptor ActionsFilter::acceptor(const ActionFilterEntryData &data) const
{
static const int maxHistorySize = 30;
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- auto data = selection.internalData.value<ActionFilterEntryData>();
- if (data.action) {
+ return [this, data] {
+ if (!data.action)
+ return AcceptResult();
m_lastTriggered.removeAll(data);
m_lastTriggered.prepend(data);
QMetaObject::invokeMethod(data.action, [action = data.action] {
@@ -185,7 +214,8 @@ void ActionsFilter::accept(const LocatorFilterEntry &selection, QString *newText
}, Qt::QueuedConnection);
if (m_lastTriggered.size() > maxHistorySize)
m_lastTriggered.resize(maxHistorySize);
- }
+ return AcceptResult();
+ };
}
static QString actionText(QAction *action)
@@ -214,8 +244,10 @@ void ActionsFilter::collectEntriesForAction(QAction *action,
collectEntriesForAction(menuAction, menuPath, processedMenus);
}
} else if (!text.isEmpty()) {
- const ActionFilterEntryData data{action, {}};
- LocatorFilterEntry filterEntry(this, text, QVariant::fromValue(data), action->icon());
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = text;
+ filterEntry.acceptor = acceptor({action, {}});
+ filterEntry.displayIcon = action->icon();
filterEntry.extraInfo = path.join(" > ");
updateEntry(action, filterEntry);
}
@@ -241,8 +273,10 @@ void ActionsFilter::collectEntriesForCommands()
const QString identifier = command->id().toString();
const QStringList path = identifier.split(QLatin1Char('.'));
- const ActionFilterEntryData data{action, command->id()};
- LocatorFilterEntry filterEntry(this, text, QVariant::fromValue(data), action->icon());
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = text;
+ filterEntry.acceptor = acceptor({action, command->id()});
+ filterEntry.displayIcon = action->icon();
filterEntry.displayExtra = command->keySequence().toString(QKeySequence::NativeText);
if (path.size() >= 2)
filterEntry.extraInfo = path.mid(0, path.size() - 1).join(" > ");
@@ -259,15 +293,17 @@ void ActionsFilter::collectEntriesForLastTriggered()
}
if (!data.action || !m_enabledActions.contains(data.action))
continue;
- const QString text = actionText(data.action);
- LocatorFilterEntry filterEntry(this, text, QVariant::fromValue(data), data.action->icon());
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = actionText(data.action);
+ filterEntry.acceptor = acceptor(data);
+ filterEntry.displayIcon = data.action->icon();
updateEntry(data.action, filterEntry);
}
}
void ActionsFilter::updateEntry(const QPointer<QAction> action, const LocatorFilterEntry &entry)
{
- auto index = m_indexes.value(action, -1);
+ const int index = m_indexes.value(action, -1);
if (index < 0) {
m_indexes[action] = m_entries.size();
m_entries << entry;
@@ -311,18 +347,6 @@ void ActionsFilter::updateEnabledActionCache()
}
}
-void Core::Internal::ActionsFilter::prepareSearch(const QString &entry)
-{
- Q_UNUSED(entry)
- m_entries.clear();
- m_indexes.clear();
- QList<const QMenu *> processedMenus;
- collectEntriesForLastTriggered();
- for (QAction* action : menuBarActions())
- collectEntriesForAction(action, QStringList(), processedMenus);
- collectEntriesForCommands();
-}
-
void ActionsFilter::saveState(QJsonObject &object) const
{
QJsonArray commands;
@@ -336,9 +360,10 @@ void ActionsFilter::saveState(QJsonObject &object) const
void ActionsFilter::restoreState(const QJsonObject &object)
{
m_lastTriggered.clear();
- for (const QJsonValue &command : object.value(lastTriggeredC).toArray()) {
+ const QJsonArray commands = object.value(lastTriggeredC).toArray();
+ for (const QJsonValue &command : commands) {
if (command.isString())
- m_lastTriggered.append({nullptr, Utils::Id::fromString(command.toString())});
+ m_lastTriggered.append({nullptr, Id::fromString(command.toString())});
}
}
diff --git a/src/plugins/coreplugin/actionsfilter.h b/src/plugins/coreplugin/actionsfilter.h
index 50a993ad01..692dea95d2 100644
--- a/src/plugins/coreplugin/actionsfilter.h
+++ b/src/plugins/coreplugin/actionsfilter.h
@@ -14,8 +14,7 @@ class QAction;
class QMenu;
QT_END_NAMESPACE
-namespace Core {
-namespace Internal {
+namespace Core::Internal {
class ActionFilterEntryData
{
@@ -30,19 +29,14 @@ public:
class ActionsFilter : public ILocatorFilter
{
- Q_OBJECT
public:
ActionsFilter();
- QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const override;
- void prepareSearch(const QString &entry) override;
-
private:
+ LocatorMatcherTasks matchers() final;
void saveState(QJsonObject &object) const override;
void restoreState(const QJsonObject &object) override;
+ LocatorFilterEntry::Acceptor acceptor(const ActionFilterEntryData &data) const;
void collectEntriesForAction(QAction *action,
const QStringList &path,
QList<const QMenu *> &processedMenus);
@@ -51,11 +45,10 @@ private:
void updateEntry(const QPointer<QAction> action, const LocatorFilterEntry &entry);
void updateEnabledActionCache();
- QList<LocatorFilterEntry> m_entries;
+ LocatorFilterEntries m_entries;
QMap<QPointer<QAction>, int> m_indexes;
QSet<QPointer<QAction>> m_enabledActions;
mutable QList<ActionFilterEntryData> m_lastTriggered;
};
-} // namespace Internal
-} // namespace Core
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/coreconstants.h b/src/plugins/coreplugin/coreconstants.h
index 0b4831e51b..513d02eb6e 100644
--- a/src/plugins/coreplugin/coreconstants.h
+++ b/src/plugins/coreplugin/coreconstants.h
@@ -46,6 +46,11 @@ const char C_EDITORMANAGER[] = "Core.EditorManager";
const char C_NAVIGATION_PANE[] = "Core.NavigationPane";
const char C_PROBLEM_PANE[] = "Core.ProblemPane";
const char C_GENERAL_OUTPUT_PANE[] = "Core.GeneralOutputPane";
+// Special context that leads to all "more specific" contexts to be ignored.
+// If you use Context(mycontextId, C_GLOBAL_CUTOFF) for a widget that has focus,
+// mycontextId will be enabled but the contexts for all parent widgets, the manually added
+// "additional" contexts, and the global context will be turned off.
+const char C_GLOBAL_CUTOFF[] = "Global Cutoff";
// Default editor kind
const char K_DEFAULT_TEXT_EDITOR_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::Core", "Plain Text Editor");
diff --git a/src/plugins/coreplugin/coreplugin.cpp b/src/plugins/coreplugin/coreplugin.cpp
index 043bdba74d..2d825dd3ae 100644
--- a/src/plugins/coreplugin/coreplugin.cpp
+++ b/src/plugins/coreplugin/coreplugin.cpp
@@ -6,12 +6,12 @@
#include "designmode.h"
#include "editmode.h"
#include "foldernavigationwidget.h"
-#include "helpmanager.h"
#include "icore.h"
#include "idocument.h"
#include "iwizardfactory.h"
#include "mainwindow.h"
#include "modemanager.h"
+#include "session.h"
#include "themechooser.h"
#include <coreplugin/actionmanager/actionmanager.h>
@@ -36,6 +36,7 @@
#include <utils/pathchooser.h>
#include <utils/savefile.h>
#include <utils/stringutils.h>
+#include <utils/textutils.h>
#include <utils/theme/theme.h>
#include <utils/theme/theme_p.h>
@@ -71,7 +72,7 @@ void CorePlugin::setupSystemEnvironment()
CorePlugin::CorePlugin()
{
qRegisterMetaType<Id>();
- qRegisterMetaType<Core::Search::TextPosition>();
+ qRegisterMetaType<Utils::Text::Position>();
qRegisterMetaType<Utils::CommandLine>();
qRegisterMetaType<Utils::FilePath>();
qRegisterMetaType<Utils::Environment>();
@@ -148,6 +149,7 @@ bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage)
Theme::setInitialPalette(theme); // Initialize palette before setting it
setCreatorTheme(theme);
InfoBar::initialize(ICore::settings());
+ CheckableMessageBox::initialize(ICore::settings());
new ActionManager(this);
ActionManager::setPresentationModeEnabled(args.presentationMode);
m_mainWindow = new MainWindow;
@@ -158,8 +160,8 @@ bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage)
m_mainWindow->init();
m_editMode = new EditMode;
ModeManager::activateMode(m_editMode->id());
-
m_folderNavigationWidgetFactory = new FolderNavigationWidgetFactory;
+ m_sessionManager.reset(new SessionManager);
IWizardFactory::initialize();
@@ -471,8 +473,7 @@ QString CorePlugin::msgCrashpadInformation()
ExtensionSystem::IPlugin::ShutdownFlag CorePlugin::aboutToShutdown()
{
Find::aboutToShutdown();
- ExtensionSystem::IPlugin::ShutdownFlag shutdownFlag = m_locator->aboutToShutdown(
- [this] { emit asynchronousShutdownFinished(); });
+ m_locator->aboutToShutdown();
m_mainWindow->aboutToShutdown();
- return shutdownFlag;
+ return SynchronousShutdown;
}
diff --git a/src/plugins/coreplugin/coreplugin.h b/src/plugins/coreplugin/coreplugin.h
index 7153c5415b..fa73f951c1 100644
--- a/src/plugins/coreplugin/coreplugin.h
+++ b/src/plugins/coreplugin/coreplugin.h
@@ -9,6 +9,8 @@
#include <extensionsystem/iplugin.h>
#include <utils/environment.h>
+#include <memory>
+
QT_BEGIN_NAMESPACE
class QMenu;
QT_END_NAMESPACE
@@ -20,6 +22,7 @@ class PathChooser;
namespace Core {
class FolderNavigationWidgetFactory;
+class SessionManager;
namespace Internal {
@@ -52,7 +55,7 @@ public:
static QString msgCrashpadInformation();
public slots:
- void fileOpenRequest(const QString&);
+ void fileOpenRequest(const QString &);
#if defined(WITH_TESTS)
private slots:
@@ -74,6 +77,7 @@ private:
MainWindow *m_mainWindow = nullptr;
EditMode *m_editMode = nullptr;
Locator *m_locator = nullptr;
+ std::unique_ptr<SessionManager> m_sessionManager;
FolderNavigationWidgetFactory *m_folderNavigationWidgetFactory = nullptr;
Utils::Environment m_startupSystemEnvironment;
Utils::EnvironmentItems m_environmentChanges;
diff --git a/src/plugins/coreplugin/coreplugin.qbs b/src/plugins/coreplugin/coreplugin.qbs
index 36edf7f5ac..096076877f 100644
--- a/src/plugins/coreplugin/coreplugin.qbs
+++ b/src/plugins/coreplugin/coreplugin.qbs
@@ -149,6 +149,15 @@ Project {
"plugininstallwizard.h",
"rightpane.cpp",
"rightpane.h",
+ "session.cpp",
+ "session.h",
+ "session_p.h",
+ "sessiondialog.cpp",
+ "sessiondialog.h",
+ "sessionmodel.cpp",
+ "sessionmodel.h",
+ "sessionview.cpp",
+ "sessionview.h",
"settingsdatabase.cpp",
"settingsdatabase.h",
"sidebar.cpp",
@@ -269,9 +278,7 @@ Project {
]
}
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
"testdatadir.cpp",
"testdatadir.h",
@@ -308,8 +315,6 @@ Project {
"itemviewfind.h",
"optionspopup.cpp",
"optionspopup.h",
- "searchresultcolor.h",
- "searchresultitem.h",
"searchresulttreeitemdelegate.cpp",
"searchresulttreeitemdelegate.h",
"searchresulttreeitemroles.h",
@@ -331,8 +336,6 @@ Project {
name: "Locator"
prefix: "locator/"
files: [
- "basefilefilter.cpp",
- "basefilefilter.h",
"commandlocator.cpp",
"commandlocator.h",
"directoryfilter.cpp",
@@ -354,8 +357,6 @@ Project {
"locatormanager.h",
"locator.cpp",
"locator.h",
- "locatorsearchutils.cpp",
- "locatorsearchutils.h",
"locatorsettingspage.cpp",
"locatorsettingspage.h",
"locatorwidget.cpp",
diff --git a/src/plugins/coreplugin/dialogs/addtovcsdialog.cpp b/src/plugins/coreplugin/dialogs/addtovcsdialog.cpp
index 7750cfcc17..09a4bf4592 100644
--- a/src/plugins/coreplugin/dialogs/addtovcsdialog.cpp
+++ b/src/plugins/coreplugin/dialogs/addtovcsdialog.cpp
@@ -22,7 +22,7 @@ AddToVcsDialog::AddToVcsDialog(QWidget *parent,
const QString &vcsDisplayName)
: QDialog(parent)
{
- using namespace Utils::Layouting;
+ using namespace Layouting;
resize(363, 375);
setMinimumSize({200, 200});
@@ -33,7 +33,7 @@ AddToVcsDialog::AddToVcsDialog(QWidget *parent,
filesListWidget->setSelectionMode(QAbstractItemView::NoSelection);
filesListWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
- QWidget *scrollAreaWidgetContents = Column{filesListWidget}.emerge(WithoutMargins);
+ QWidget *scrollAreaWidgetContents = Column{filesListWidget, noMargin}.emerge();
scrollAreaWidgetContents->setGeometry({0, 0, 341, 300});
auto scrollArea = new QScrollArea;
diff --git a/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp b/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp
index 270ed9f681..59237aad79 100644
--- a/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp
+++ b/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp
@@ -18,8 +18,8 @@
#include <utils/layoutbuilder.h>
#include <utils/macroexpander.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/variablechooser.h>
#include <QCheckBox>
@@ -564,11 +564,11 @@ ExternalToolConfig::ExternalToolConfig()
Tr::tr("Environment:"), m_environmentLabel, environmentButton, br,
empty, m_modifiesDocumentCheckbox, br,
inputLabel, m_inputText
- }.attachTo(m_infoWidget, WithMargins);
+ }.attachTo(m_infoWidget);
Column {
- m_infoWidget
- }.attachTo(scrollAreaWidgetContents, WithoutMargins);
+ m_infoWidget, noMargin
+ }.attachTo(scrollAreaWidgetContents);
Row {
Column {
diff --git a/src/plugins/coreplugin/dialogs/ioptionspage.cpp b/src/plugins/coreplugin/dialogs/ioptionspage.cpp
index 451859c527..4def6095a1 100644
--- a/src/plugins/coreplugin/dialogs/ioptionspage.cpp
+++ b/src/plugins/coreplugin/dialogs/ioptionspage.cpp
@@ -9,6 +9,7 @@
#include <coreplugin/icore.h>
#include <utils/aspects.h>
+#include <utils/layoutbuilder.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
@@ -135,9 +136,6 @@ QWidget *IOptionsPage::widget()
if (!m_widget) {
if (m_widgetCreator) {
m_widget = m_widgetCreator();
- } else if (m_layouter) {
- m_widget = new QWidget;
- m_layouter(m_widget);
} else {
QTC_CHECK(false);
}
@@ -156,9 +154,10 @@ QWidget *IOptionsPage::widget()
void IOptionsPage::apply()
{
- if (auto widget = qobject_cast<IOptionsPageWidget *>(m_widget)) {
+ if (auto widget = qobject_cast<IOptionsPageWidget *>(m_widget))
widget->apply();
- } else if (m_settings) {
+
+ if (m_settings) {
if (m_settings->isDirty()) {
m_settings->apply();
m_settings->writeSettings(ICore::settings());
@@ -179,7 +178,8 @@ void IOptionsPage::finish()
{
if (auto widget = qobject_cast<IOptionsPageWidget *>(m_widget))
widget->finish();
- else if (m_settings)
+
+ if (m_settings)
m_settings->finish();
delete m_widget;
@@ -199,9 +199,13 @@ void IOptionsPage::setSettings(AspectContainer *settings)
m_settings = settings;
}
-void IOptionsPage::setLayouter(const std::function<void(QWidget *w)> &layouter)
+void IOptionsPage::setLayouter(const std::function<Layouting::LayoutItem ()> &layouter)
{
- m_layouter = layouter;
+ m_widgetCreator = [layouter] {
+ auto widget = new IOptionsPageWidget;
+ layouter().attachTo(widget);
+ return widget;
+ };
}
/*!
@@ -237,11 +241,10 @@ void IOptionsPage::setLayouter(const std::function<void(QWidget *w)> &layouter)
static QList<IOptionsPage *> g_optionsPages;
/*!
- Constructs an options page with the given \a parent and registers it
+ Constructs an options page and registers it
at the global options page pool if \a registerGlobally is \c true.
*/
-IOptionsPage::IOptionsPage(QObject *parent, bool registerGlobally)
- : QObject(parent)
+IOptionsPage::IOptionsPage(bool registerGlobally)
{
if (registerGlobally)
g_optionsPages.append(this);
@@ -278,8 +281,7 @@ bool IOptionsPage::matches(const QRegularExpression &regexp) const
static QList<IOptionsPageProvider *> g_optionsPagesProviders;
-IOptionsPageProvider::IOptionsPageProvider(QObject *parent)
- : QObject(parent)
+IOptionsPageProvider::IOptionsPageProvider()
{
g_optionsPagesProviders.append(this);
}
@@ -299,4 +301,17 @@ QIcon IOptionsPageProvider::categoryIcon() const
return m_categoryIcon.icon();
}
+// PagedSettings
+
+PagedSettings::PagedSettings()
+{
+ setSettings(this);
+ setAutoApply(false);
+}
+
+void PagedSettings::readSettings()
+{
+ return AspectContainer::readSettings(Core::ICore::settings());
+}
+
} // Core
diff --git a/src/plugins/coreplugin/dialogs/ioptionspage.h b/src/plugins/coreplugin/dialogs/ioptionspage.h
index 300ecda4a0..f4801b7c73 100644
--- a/src/plugins/coreplugin/dialogs/ioptionspage.h
+++ b/src/plugins/coreplugin/dialogs/ioptionspage.h
@@ -5,6 +5,7 @@
#include <coreplugin/core_global.h>
+#include <utils/aspects.h>
#include <utils/icon.h>
#include <utils/id.h>
@@ -15,6 +16,8 @@
#include <functional>
+namespace Layouting { class LayoutItem; };
+
namespace Utils { class AspectContainer; };
namespace Core {
@@ -22,18 +25,28 @@ namespace Core {
class CORE_EXPORT IOptionsPageWidget : public QWidget
{
Q_OBJECT
+
public:
- virtual void apply() = 0;
- virtual void finish() {}
+ void setOnApply(const std::function<void ()> &func) { m_onApply = func; }
+ void setOnFinish(const std::function<void ()> &func) { m_onFinish = func; }
+
+protected:
+ friend class IOptionsPage;
+ virtual void apply() { if (m_onApply) m_onApply(); }
+ virtual void finish() { if (m_onFinish) m_onFinish(); }
+
+private:
+ std::function<void()> m_onApply;
+ std::function<void()> m_onFinish;
};
-class CORE_EXPORT IOptionsPage : public QObject
+class CORE_EXPORT IOptionsPage
{
- Q_OBJECT
+ Q_DISABLE_COPY_MOVE(IOptionsPage)
public:
- IOptionsPage(QObject *parent = nullptr, bool registerGlobally = true);
- ~IOptionsPage() override;
+ explicit IOptionsPage(bool registerGlobally = true);
+ virtual ~IOptionsPage();
static const QList<IOptionsPage *> allOptionsPages();
@@ -61,7 +74,7 @@ protected:
void setCategoryIcon(const Utils::Icon &categoryIcon) { m_categoryIcon = categoryIcon; }
void setCategoryIconPath(const Utils::FilePath &categoryIconPath);
void setSettings(Utils::AspectContainer *settings);
- void setLayouter(const std::function<void(QWidget *w)> &layouter);
+ void setLayouter(const std::function<Layouting::LayoutItem()> &layouter);
// Used in FontSettingsPage. FIXME?
QPointer<QWidget> m_widget; // Used in conjunction with m_widgetCreator
@@ -78,7 +91,6 @@ private:
mutable QStringList m_keywords;
Utils::AspectContainer *m_settings = nullptr;
- std::function<void(QWidget *w)> m_layouter;
};
/*
@@ -89,13 +101,13 @@ private:
before the options pages get available.)
*/
-class CORE_EXPORT IOptionsPageProvider : public QObject
+class CORE_EXPORT IOptionsPageProvider
{
- Q_OBJECT
+ Q_DISABLE_COPY_MOVE(IOptionsPageProvider);
public:
- IOptionsPageProvider(QObject *parent = nullptr);
- ~IOptionsPageProvider() override;
+ IOptionsPageProvider();
+ virtual ~IOptionsPageProvider();
static const QList<IOptionsPageProvider *> allOptionsPagesProviders();
@@ -116,4 +128,13 @@ protected:
Utils::Icon m_categoryIcon;
};
+class CORE_EXPORT PagedSettings : public Utils::AspectContainer, public IOptionsPage
+{
+public:
+ PagedSettings();
+
+ using AspectContainer::readSettings; // FIXME: Remove.
+ void readSettings(); // Intentionally hides AspectContainer::readSettings()
+};
+
} // namespace Core
diff --git a/src/plugins/coreplugin/dialogs/openwithdialog.cpp b/src/plugins/coreplugin/dialogs/openwithdialog.cpp
index ddec97928b..ff8db72ddb 100644
--- a/src/plugins/coreplugin/dialogs/openwithdialog.cpp
+++ b/src/plugins/coreplugin/dialogs/openwithdialog.cpp
@@ -25,7 +25,7 @@ OpenWithDialog::OpenWithDialog(const Utils::FilePath &filePath, QWidget *parent)
buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
- using namespace Utils::Layouting;
+ using namespace Layouting;
// clang-format off
Column {
Tr::tr("Open file \"%1\" with:").arg(filePath.fileName()),
diff --git a/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp b/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp
index 97d35aca82..88431454fe 100644
--- a/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp
+++ b/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp
@@ -422,8 +422,12 @@ void ReadOnlyFilesDialogPrivate::initDialog(const FilePaths &filePaths)
using namespace Layouting;
- QWidget *setAllWidget = Row{Tr::tr("Select all, if possible: "), m_setAll, st}.emerge(
- WithoutMargins);
+ QWidget *setAllWidget = Row {
+ Tr::tr("Select all, if possible: "),
+ m_setAll,
+ st,
+ noMargin
+ }.emerge();
// clang-format off
Column {
diff --git a/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp b/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp
index 87953b3ab9..3b9034ddd7 100644
--- a/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp
+++ b/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp
@@ -59,7 +59,7 @@ SaveItemsDialog::SaveItemsDialog(QWidget *parent, const QList<IDocument *> &item
m_saveBeforeBuildCheckBox->setVisible(false);
- using namespace Utils::Layouting;
+ using namespace Layouting;
// clang-format off
Column {
m_msgLabel,
diff --git a/src/plugins/coreplugin/dialogs/settingsdialog.cpp b/src/plugins/coreplugin/dialogs/settingsdialog.cpp
index 017b8dc884..f429994edd 100644
--- a/src/plugins/coreplugin/dialogs/settingsdialog.cpp
+++ b/src/plugins/coreplugin/dialogs/settingsdialog.cpp
@@ -331,14 +331,26 @@ public:
class SmartScrollArea : public QScrollArea
{
public:
- explicit SmartScrollArea(QWidget *parent)
- : QScrollArea(parent)
+ explicit SmartScrollArea(QWidget *parent, IOptionsPage *page)
+ : QScrollArea(parent), m_page(page)
{
setFrameStyle(QFrame::NoFrame | QFrame::Plain);
viewport()->setAutoFillBackground(false);
setWidgetResizable(true);
}
+
private:
+ void showEvent(QShowEvent *event) final
+ {
+ if (!widget()) {
+ QWidget *inner = m_page->widget();
+ setWidget(inner);
+ inner->setAutoFillBackground(false);
+ }
+
+ QScrollArea::showEvent(event);
+ }
+
void resizeEvent(QResizeEvent *event) final
{
QWidget *inner = widget();
@@ -387,6 +399,8 @@ private:
return 0;
return list.first()->sizeHint().width();
}
+
+ IOptionsPage *m_page = nullptr;
};
// ----------- SettingsDialog
@@ -604,14 +618,8 @@ void SettingsDialog::ensureCategoryWidget(Category *category)
m_model.ensurePages(category);
auto tabWidget = new QTabWidget;
tabWidget->tabBar()->setObjectName("qc_settings_main_tabbar"); // easier lookup in Squish
- for (IOptionsPage *page : std::as_const(category->pages)) {
- QWidget *widget = page->widget();
- ICore::setupScreenShooter(page->displayName(), widget);
- auto ssa = new SmartScrollArea(this);
- ssa->setWidget(widget);
- widget->setAutoFillBackground(false);
- tabWidget->addTab(ssa, page->displayName());
- }
+ for (IOptionsPage *page : std::as_const(category->pages))
+ tabWidget->addTab(new SmartScrollArea(this, page), page->displayName());
connect(tabWidget, &QTabWidget::currentChanged,
this, &SettingsDialog::currentTabChanged);
diff --git a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp
index 598cf92d99..c846fb9dae 100644
--- a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp
+++ b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp
@@ -268,10 +268,8 @@ ShortcutSettingsWidget::ShortcutSettingsWidget()
this, &ShortcutSettingsWidget::initialize);
connect(this, &ShortcutSettingsWidget::currentCommandChanged,
this, &ShortcutSettingsWidget::handleCurrentCommandChanged);
- connect(this,
- &ShortcutSettingsWidget::resetRequested,
- this,
- &ShortcutSettingsWidget::resetToDefault);
+ connect(this, &ShortcutSettingsWidget::resetRequested,
+ this, &ShortcutSettingsWidget::resetToDefault);
m_shortcutBox = new QGroupBox(Tr::tr("Shortcut"), this);
m_shortcutBox->setEnabled(false);
@@ -287,37 +285,12 @@ ShortcutSettingsWidget::~ShortcutSettingsWidget()
qDeleteAll(m_scitems);
}
-ShortcutSettings::ShortcutSettings()
-{
- setId(Constants::SETTINGS_ID_SHORTCUTS);
- setDisplayName(Tr::tr("Keyboard"));
- setCategory(Constants::SETTINGS_CATEGORY_CORE);
-}
-
-QWidget *ShortcutSettings::widget()
-{
- if (!m_widget)
- m_widget = new ShortcutSettingsWidget();
- return m_widget;
-}
-
void ShortcutSettingsWidget::apply()
{
for (const ShortcutItem *item : std::as_const(m_scitems))
item->m_cmd->setKeySequences(item->m_keys);
}
-void ShortcutSettings::apply()
-{
- QTC_ASSERT(m_widget, return);
- m_widget->apply();
-}
-
-void ShortcutSettings::finish()
-{
- delete m_widget;
-}
-
ShortcutItem *shortcutItem(QTreeWidgetItem *treeItem)
{
if (!treeItem)
@@ -706,5 +679,31 @@ void ShortcutInput::setConflictChecker(const ShortcutInput::ConflictChecker &fun
m_conflictChecker = fun;
}
+// ShortcutSettingsPageWidget
+
+class ShortcutSettingsPageWidget : public IOptionsPageWidget
+{
+public:
+ ShortcutSettingsPageWidget()
+ {
+ auto inner = new ShortcutSettingsWidget;
+ auto vbox = new QVBoxLayout(this);
+ vbox->addWidget(inner);
+ vbox->setContentsMargins(0, 0, 0, 0);
+
+ setOnApply([inner] { inner->apply(); });
+ }
+};
+
+// ShortcutSettings
+
+ShortcutSettings::ShortcutSettings()
+{
+ setId(Constants::SETTINGS_ID_SHORTCUTS);
+ setDisplayName(Tr::tr("Keyboard"));
+ setCategory(Constants::SETTINGS_CATEGORY_CORE);
+ setWidgetCreator([] { return new ShortcutSettingsPageWidget; });
+}
+
} // namespace Internal
} // namespace Core
diff --git a/src/plugins/coreplugin/dialogs/shortcutsettings.h b/src/plugins/coreplugin/dialogs/shortcutsettings.h
index d7edfcad29..49297b3d50 100644
--- a/src/plugins/coreplugin/dialogs/shortcutsettings.h
+++ b/src/plugins/coreplugin/dialogs/shortcutsettings.h
@@ -14,19 +14,17 @@
#include <array>
QT_BEGIN_NAMESPACE
-class QGroupBox;
class QLabel;
QT_END_NAMESPACE
+namespace Utils { class FancyLineEdit; }
+
namespace Core {
class Command;
namespace Internal {
-class ActionManagerPrivate;
-class ShortcutSettingsWidget;
-
struct ShortcutItem
{
Command *m_cmd;
@@ -90,13 +88,6 @@ class ShortcutSettings final : public IOptionsPage
{
public:
ShortcutSettings();
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-private:
- QPointer<ShortcutSettingsWidget> m_widget;
};
} // namespace Internal
diff --git a/src/plugins/coreplugin/documentmanager.cpp b/src/plugins/coreplugin/documentmanager.cpp
index 226564fa0b..b7b7ccfbe0 100644
--- a/src/plugins/coreplugin/documentmanager.cpp
+++ b/src/plugins/coreplugin/documentmanager.cpp
@@ -856,7 +856,7 @@ FilePath DocumentManager::getSaveFileNameWithExtension(const QString &title, con
FilePath DocumentManager::getSaveAsFileName(const IDocument *document)
{
QTC_ASSERT(document, return {});
- const QString filter = allDocumentFactoryFiltersString();
+ QString filter = allDocumentFactoryFiltersString();
const FilePath filePath = document->filePath();
QString selectedFilter;
FilePath fileDialogPath = filePath;
@@ -876,6 +876,9 @@ FilePath DocumentManager::getSaveAsFileName(const IDocument *document)
if (selectedFilter.isEmpty())
selectedFilter = Utils::mimeTypeForName(document->mimeType()).filterString();
+ if (!filter.contains(selectedFilter))
+ filter.prepend(selectedFilter + QLatin1String(";;"));
+
return getSaveFileName(Tr::tr("Save File As"),
fileDialogPath,
filter,
@@ -1027,6 +1030,10 @@ void DocumentManager::showFilePropertiesDialog(const FilePath &filePath)
and \a selectedFilter arguments are interpreted like in
QFileDialog::getOpenFileNames(). \a pathIn specifies a path to open the
dialog in if that is not overridden by the user's policy.
+
+ The \a options argument holds various options about how to run the dialog.
+ See the QFileDialog::Option enum for more information about the flags you
+ can pass.
*/
FilePaths DocumentManager::getOpenFileNames(const QString &filters,
diff --git a/src/plugins/coreplugin/editormanager/documentmodel.cpp b/src/plugins/coreplugin/editormanager/documentmodel.cpp
index 04a877ec49..876b22b128 100644
--- a/src/plugins/coreplugin/editormanager/documentmodel.cpp
+++ b/src/plugins/coreplugin/editormanager/documentmodel.cpp
@@ -11,6 +11,8 @@
#include <utils/algorithm.h>
#include <utils/dropsupport.h>
+#include <utils/filepath.h>
+#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
@@ -121,63 +123,55 @@ DocumentModel::Entry *DocumentModelPrivate::addEntry(DocumentModel::Entry *entry
bool DocumentModelPrivate::disambiguateDisplayNames(DocumentModel::Entry *entry)
{
const QString displayName = entry->plainDisplayName();
- int minIdx = -1, maxIdx = -1;
- QList<DynamicEntry> dups;
+ QList<DocumentModel::Entry *> dups;
+ FilePaths paths;
+ int minIdx = m_entries.count();
+ int maxIdx = 0;
- for (int i = 0, total = m_entries.count(); i < total; ++i) {
+ for (int i = 0; i < m_entries.count(); ++i) {
DocumentModel::Entry *e = m_entries.at(i);
if (e == entry || e->plainDisplayName() == displayName) {
- e->document->setUniqueDisplayName(QString());
- dups += DynamicEntry(e);
- maxIdx = i;
- if (minIdx < 0)
+ if (minIdx > i)
minIdx = i;
+ if (maxIdx < i)
+ maxIdx = i;
+ dups += e;
+ if (!e->filePath().isEmpty())
+ paths += e->filePath();
}
}
- const int dupsCount = dups.count();
- if (dupsCount == 0)
+ const auto triggerDataChanged = [this](int minIdx, int maxIdx) {
+ const QModelIndex idxMin = index(minIdx + 1 /*<no document>*/, 0);
+ const QModelIndex idxMax = index(maxIdx + 1 /*<no document>*/, 0);
+ if (idxMin.isValid() && idxMax.isValid())
+ emit dataChanged(idxMin, idxMax);
+ };
+
+ if (dups.count() == 1) {
+ dups.at(0)->document->setUniqueDisplayName({});
+ triggerDataChanged(minIdx, maxIdx);
return false;
+ }
- if (dupsCount > 1) {
- int serial = 0;
- int count = 0;
- // increase uniqueness unless no dups are left
- forever {
- bool seenDups = false;
- for (int i = 0; i < dupsCount - 1; ++i) {
- DynamicEntry &e = dups[i];
- const Utils::FilePath myFileName = e->document->filePath();
- if (e->document->isTemporary() || myFileName.isEmpty() || count > 10) {
- // path-less entry, append number
- e.setNumberedName(++serial);
- continue;
- }
- for (int j = i + 1; j < dupsCount; ++j) {
- DynamicEntry &e2 = dups[j];
- if (e->displayName().compare(e2->displayName(), Utils::HostOsInfo::fileNameCaseSensitivity()) == 0) {
- const Utils::FilePath otherFileName = e2->document->filePath();
- if (otherFileName.isEmpty())
- continue;
- seenDups = true;
- e2.disambiguate();
- if (j > maxIdx)
- maxIdx = j;
- }
- }
- if (seenDups) {
- e.disambiguate();
- ++count;
- break;
- }
- }
- if (!seenDups)
- break;
+ const FilePath commonAncestor = FileUtils::commonPath(paths);
+
+ int countWithoutFilePath = 0;
+ for (DocumentModel::Entry *e : std::as_const(dups)) {
+ const FilePath path = e->filePath();
+ if (path.isEmpty()) {
+ e->document->setUniqueDisplayName(QStringLiteral("%1 (%2)")
+ .arg(e->document->displayName())
+ .arg(++countWithoutFilePath));
+ continue;
+ }
+ const QString uniqueDisplayName = path.relativeChildPath(commonAncestor).toString();
+ if (uniqueDisplayName != "" && e->document->uniqueDisplayName() != uniqueDisplayName) {
+ e->document->setUniqueDisplayName(uniqueDisplayName);
}
}
-
- emit dataChanged(index(minIdx + 1, 0), index(maxIdx + 1, 0));
+ triggerDataChanged(minIdx, maxIdx);
return true;
}
@@ -483,30 +477,6 @@ void DocumentModelPrivate::removeAllSuspendedEntries(PinnedFileRemovalPolicy pin
}
}
-DocumentModelPrivate::DynamicEntry::DynamicEntry(DocumentModel::Entry *e) :
- entry(e),
- pathComponents(0)
-{
-}
-
-DocumentModel::Entry *DocumentModelPrivate::DynamicEntry::operator->() const
-{
- return entry;
-}
-
-void DocumentModelPrivate::DynamicEntry::disambiguate()
-{
- const QString display = entry->filePath().fileNameWithPathComponents(++pathComponents);
- entry->document->setUniqueDisplayName(display);
-}
-
-void DocumentModelPrivate::DynamicEntry::setNumberedName(int number)
-{
- entry->document->setUniqueDisplayName(QStringLiteral("%1 (%2)")
- .arg(entry->document->displayName())
- .arg(number));
-}
-
} // Internal
DocumentModel::Entry::Entry() :
diff --git a/src/plugins/coreplugin/editormanager/documentmodel_p.h b/src/plugins/coreplugin/editormanager/documentmodel_p.h
index 8e3efa98b0..6b99d3fe43 100644
--- a/src/plugins/coreplugin/editormanager/documentmodel_p.h
+++ b/src/plugins/coreplugin/editormanager/documentmodel_p.h
@@ -61,18 +61,6 @@ public:
void itemChanged(IDocument *document);
- class DynamicEntry
- {
- public:
- DocumentModel::Entry *entry;
- int pathComponents;
-
- DynamicEntry(DocumentModel::Entry *e);
- DocumentModel::Entry *operator->() const;
- void disambiguate();
- void setNumberedName(int number);
- };
-
QList<DocumentModel::Entry *> m_entries;
QMap<IDocument *, QList<IEditor *> > m_editors;
QHash<Utils::FilePath, DocumentModel::Entry *> m_entryByFixedPath;
diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp
index 2953a833b7..e7147e94ce 100644
--- a/src/plugins/coreplugin/editormanager/editormanager.cpp
+++ b/src/plugins/coreplugin/editormanager/editormanager.cpp
@@ -24,10 +24,10 @@
#include "../editormanager/ieditorfactory_p.h"
#include "../editormanager/iexternaleditor.h"
#include "../fileutils.h"
-#include "../find/searchresultitem.h"
#include "../findplaceholder.h"
#include "../icore.h"
#include "../iversioncontrol.h"
+#include "../locator/ilocatorfilter.h"
#include "../modemanager.h"
#include "../outputpane.h"
#include "../outputpanemanager.h"
@@ -51,6 +51,7 @@
#include <utils/mimeutils.h>
#include <utils/overridecursor.h>
#include <utils/qtcassert.h>
+#include <utils/searchresultitem.h>
#include <utils/stringutils.h>
#include <utils/utilsicons.h>
@@ -238,6 +239,10 @@ void EditorManagerPlaceHolder::showEvent(QShowEvent *)
\value SwitchSplitIfAlreadyVisible
Switches to another split if the document is already
visible there.
+ \value DoNotRaise
+ Prevents raising the \QC window to the foreground.
+ \value AllowExternalEditor
+ Allows opening the file in an external editor.
*/
/*!
@@ -751,17 +756,13 @@ bool EditorManagerPrivate::skipOpeningBigTextFile(const FilePath &filePath)
.arg(filePath.fileName())
.arg(fileSizeInMB, 0, 'f', 2);
- CheckableMessageBox messageBox(ICore::dialogParent());
- messageBox.setWindowTitle(title);
- messageBox.setText(text);
- messageBox.setStandardButtons(QDialogButtonBox::Yes|QDialogButtonBox::No);
- messageBox.setDefaultButton(QDialogButtonBox::No);
- messageBox.setIcon(QMessageBox::Question);
- messageBox.setCheckBoxVisible(true);
- messageBox.setCheckBoxText(CheckableMessageBox::msgDoNotAskAgain());
- messageBox.exec();
- setWarnBeforeOpeningBigFilesEnabled(!messageBox.isChecked());
- return messageBox.clickedStandardButton() != QDialogButtonBox::Yes;
+ bool askAgain = true;
+ CheckableDecider decider(&askAgain);
+
+ QMessageBox::StandardButton clickedButton
+ = CheckableMessageBox::question(ICore::dialogParent(), title, text, decider);
+ setWarnBeforeOpeningBigFilesEnabled(askAgain);
+ return clickedButton != QMessageBox::Yes;
}
return false;
@@ -3157,6 +3158,17 @@ IEditor *EditorManager::openEditorAt(const Link &link,
newEditor);
}
+IEditor *EditorManager::openEditor(const LocatorFilterEntry &entry)
+{
+ const OpenEditorFlags defaultFlags = EditorManager::AllowExternalEditor;
+ if (entry.linkForEditor)
+ return EditorManager::openEditorAt(*entry.linkForEditor, {}, defaultFlags);
+ else if (!entry.filePath.isEmpty())
+ return EditorManager::openEditor(entry.filePath, {}, defaultFlags);
+ return nullptr;
+}
+
+
/*!
Opens the document at the position of the search result \a item using the
editor type \a editorId and the specified \a flags.
@@ -3180,7 +3192,7 @@ void EditorManager::openEditorAtSearchResult(const SearchResultItem &item,
openEditor(FilePath::fromUserInput(item.lineText()), editorId, flags, newEditor);
return;
}
- const Search::TextPosition position = item.mainRange().begin;
+ const Text::Position position = item.mainRange().begin;
openEditorAt({FilePath::fromUserInput(path.first()), position.line, position.column},
editorId, flags, newEditor);
}
@@ -3238,7 +3250,11 @@ void EditorManager::addCloseEditorListener(const std::function<bool (IEditor *)>
/*!
Asks the user for a list of files to open and returns the choice.
- \sa DocumentManager::getOpenFileNames()
+ The \a options argument holds various options about how to run the dialog.
+ See the QFileDialog::Options enum for more information about the flags you
+ can pass.
+
+ \sa DocumentManager::getOpenFileNames(), QFileDialog::Options
*/
FilePaths EditorManager::getOpenFilePaths(QFileDialog::Options options)
{
diff --git a/src/plugins/coreplugin/editormanager/editormanager.h b/src/plugins/coreplugin/editormanager/editormanager.h
index c1c4c4644c..09218f42c3 100644
--- a/src/plugins/coreplugin/editormanager/editormanager.h
+++ b/src/plugins/coreplugin/editormanager/editormanager.h
@@ -9,8 +9,8 @@
#include "documentmodel.h"
#include "ieditor.h"
-#include "utils/link.h"
-#include "utils/textfileformat.h"
+#include <utils/link.h>
+#include <utils/textfileformat.h>
#include <QFileDialog>
#include <QList>
@@ -18,21 +18,18 @@
#include <functional>
-QT_FORWARD_DECLARE_CLASS(QMenu)
+QT_BEGIN_NAMESPACE
+class QMenu;
+QT_END_NAMESPACE
-namespace Utils {
-class MimeType;
-}
+namespace Utils { class SearchResultItem; }
namespace Core {
class IDocument;
-class SearchResultItem;
+class LocatorFilterEntry;
-namespace Internal {
-class EditorManagerPrivate;
-class MainWindow;
-} // namespace Internal
+namespace Internal { class MainWindow; }
class CORE_EXPORT EditorManagerPlaceHolder final : public QWidget
{
@@ -76,8 +73,9 @@ public:
Utils::Id editorId = {},
OpenEditorFlags flags = NoFlags,
bool *newEditor = nullptr);
+ static IEditor *openEditor(const LocatorFilterEntry &entry);
- static void openEditorAtSearchResult(const SearchResultItem &item,
+ static void openEditorAtSearchResult(const Utils::SearchResultItem &item,
Utils::Id editorId = {},
OpenEditorFlags flags = NoFlags,
bool *newEditor = nullptr);
diff --git a/src/plugins/coreplugin/editormanager/iexternaleditor.cpp b/src/plugins/coreplugin/editormanager/iexternaleditor.cpp
index 44a2db8483..8176bf4d3e 100644
--- a/src/plugins/coreplugin/editormanager/iexternaleditor.cpp
+++ b/src/plugins/coreplugin/editormanager/iexternaleditor.cpp
@@ -18,21 +18,6 @@ namespace Core {
*/
/*!
- \fn QString Core::IExternalEditor::displayName() const
- Returns a user-visible description of the editor type.
-*/
-
-/*!
- \fn Utils::Id Core::IExternalEditor::id() const
- Returns the ID of the factory or editor type.
-*/
-
-/*!
- \fn QStringList Core::IExternalEditor::mimeTypes() const
- Returns a list of MIME types that the editor supports
-*/
-
-/*!
\fn bool Core::IExternalEditor::startEditor(const Utils::FilePath &fileName, QString *errorMessage)
Opens the editor with \a fileName. Returns \c true on success or \c false
diff --git a/src/plugins/coreplugin/editortoolbar.cpp b/src/plugins/coreplugin/editortoolbar.cpp
index fadfe4cae6..ee4d912d85 100644
--- a/src/plugins/coreplugin/editortoolbar.cpp
+++ b/src/plugins/coreplugin/editortoolbar.cpp
@@ -16,6 +16,7 @@
#include <utils/fsengine/fileiconprovider.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QApplication>
@@ -109,7 +110,7 @@ EditorToolBar::EditorToolBar(QWidget *parent) :
d->m_lockButton->setEnabled(false);
- d->m_dragHandle->setProperty("noArrow", true);
+ d->m_dragHandle->setProperty(Utils::StyleHelper::C_NO_ARROW, true);
d->m_dragHandle->setToolTip(Tr::tr("Drag to drag documents between splits"));
d->m_dragHandle->installEventFilter(this);
d->m_dragHandleMenu = new QMenu(d->m_dragHandle);
@@ -118,9 +119,9 @@ EditorToolBar::EditorToolBar(QWidget *parent) :
connect(d->m_goBackAction, &QAction::triggered, this, &EditorToolBar::goBackClicked);
connect(d->m_goForwardAction, &QAction::triggered, this, &EditorToolBar::goForwardClicked);
- d->m_editorList->setProperty("hideicon", true);
- d->m_editorList->setProperty("notelideasterisk", true);
- d->m_editorList->setProperty("elidemode", Qt::ElideMiddle);
+ d->m_editorList->setProperty(Utils::StyleHelper::C_HIDE_ICON, true);
+ d->m_editorList->setProperty(Utils::StyleHelper::C_NOT_ELIDE_ASTERISK, true);
+ d->m_editorList->setProperty(Utils::StyleHelper::C_ELIDE_MODE, Qt::ElideMiddle);
d->m_editorList->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
d->m_editorList->setMinimumContentsLength(20);
d->m_editorList->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
@@ -130,7 +131,7 @@ EditorToolBar::EditorToolBar(QWidget *parent) :
d->m_closeEditorButton->setIcon(Utils::Icons::CLOSE_TOOLBAR.icon());
d->m_closeEditorButton->setEnabled(false);
- d->m_closeEditorButton->setProperty("showborder", true);
+ d->m_closeEditorButton->setProperty(Utils::StyleHelper::C_SHOW_BORDER, true);
d->m_toolBarPlaceholder->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
@@ -141,7 +142,7 @@ EditorToolBar::EditorToolBar(QWidget *parent) :
d->m_splitButton->setIcon(Utils::Icons::SPLIT_HORIZONTAL_TOOLBAR.icon());
d->m_splitButton->setToolTip(Tr::tr("Split"));
d->m_splitButton->setPopupMode(QToolButton::InstantPopup);
- d->m_splitButton->setProperty("noArrow", true);
+ d->m_splitButton->setProperty(Utils::StyleHelper::C_NO_ARROW, true);
auto splitMenu = new QMenu(d->m_splitButton);
splitMenu->addAction(d->m_horizontalSplitAction);
splitMenu->addAction(d->m_verticalSplitAction);
diff --git a/src/plugins/coreplugin/externaltool.cpp b/src/plugins/coreplugin/externaltool.cpp
index 0c0b1d8926..6cafa6ff88 100644
--- a/src/plugins/coreplugin/externaltool.cpp
+++ b/src/plugins/coreplugin/externaltool.cpp
@@ -16,8 +16,8 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/macroexpander.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QCoreApplication>
#include <QDateTime>
@@ -623,11 +623,11 @@ void ExternalToolRunner::run()
DocumentManager::expectFileChange(m_expectedFilePath);
}
}
- m_process = new QtcProcess(this);
- connect(m_process, &QtcProcess::done, this, &ExternalToolRunner::done);
- connect(m_process, &QtcProcess::readyReadStandardOutput,
+ m_process = new Process(this);
+ connect(m_process, &Process::done, this, &ExternalToolRunner::done);
+ connect(m_process, &Process::readyReadStandardOutput,
this, &ExternalToolRunner::readStandardOutput);
- connect(m_process, &QtcProcess::readyReadStandardError,
+ connect(m_process, &Process::readyReadStandardError,
this, &ExternalToolRunner::readStandardError);
if (!m_resolvedWorkingDirectory.isEmpty())
m_process->setWorkingDirectory(m_resolvedWorkingDirectory);
diff --git a/src/plugins/coreplugin/externaltool.h b/src/plugins/coreplugin/externaltool.h
index 8a32d24165..d92d6099bc 100644
--- a/src/plugins/coreplugin/externaltool.h
+++ b/src/plugins/coreplugin/externaltool.h
@@ -14,7 +14,7 @@
#include <QTextCodec>
#include <QMetaType>
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace Core {
@@ -127,7 +127,7 @@ private:
QString m_resolvedInput;
Utils::FilePath m_resolvedWorkingDirectory;
Utils::Environment m_resolvedEnvironment;
- Utils::QtcProcess *m_process;
+ Utils::Process *m_process;
QTextCodec *m_outputCodec;
QTextCodec::ConverterState m_outputCodecState;
QTextCodec::ConverterState m_errorCodecState;
diff --git a/src/plugins/coreplugin/fancyactionbar.cpp b/src/plugins/coreplugin/fancyactionbar.cpp
index 55904bf149..a07dd71897 100644
--- a/src/plugins/coreplugin/fancyactionbar.cpp
+++ b/src/plugins/coreplugin/fancyactionbar.cpp
@@ -192,10 +192,7 @@ void FancyToolButton::paintEvent(QPaintEvent *event)
const int textFlags = Qt::AlignVCenter | Qt::AlignHCenter;
const QString projectName = defaultAction()->property("heading").toString();
- if (!projectName.isNull())
- centerRect.adjust(0, lineHeight + 4, 0, 0);
-
- centerRect.adjust(0, 0, 0, -lineHeight * 2 - 4);
+ centerRect.adjust(0, lineHeight + 4, 0, -lineHeight * 2 - 4);
iconRect.moveCenter(centerRect.center());
StyleHelper::drawIconWithShadow(icon(), iconRect, &painter, iconMode);
@@ -294,12 +291,12 @@ QSize FancyToolButton::sizeHint() const
boldFont.setBold(true);
const QFontMetrics fm(boldFont);
const qreal lineHeight = fm.height();
- const QString projectName = defaultAction()->property("heading").toString();
- buttonSize += QSizeF(0, 10);
- if (!projectName.isEmpty())
- buttonSize += QSizeF(0, lineHeight + 2);
-
- buttonSize += QSizeF(0, lineHeight * 2 + 2);
+ const int extraHeight = 10 // Spacing between top and projectName
+ + lineHeight // projectName height
+ + 2 // Spacing between projectName and icon
+ + lineHeight * 2 // configurationName height (2 lines)
+ + 2; // Spacing between configurationName and bottom
+ buttonSize.rheight() += extraHeight;
}
return buttonSize.toSize();
}
diff --git a/src/plugins/coreplugin/featureprovider.cpp b/src/plugins/coreplugin/featureprovider.cpp
index efe792bfb2..739bdb7067 100644
--- a/src/plugins/coreplugin/featureprovider.cpp
+++ b/src/plugins/coreplugin/featureprovider.cpp
@@ -13,18 +13,22 @@
for wizards.
The features provided by an object in the object pool implementing IFeatureProvider
- will be respected by wizards implementing IWizard.
+ will be respected by wizards implementing IWizardFactory.
This feature set, provided by all instances of IFeatureProvider in the
- object pool, is checked against \c IWizard::requiredFeatures()
+ object pool, is checked against \c IWizardFactory::requiredFeatures()
and only if all required features are available, the wizard is displayed
when creating a new file or project.
+ If you created JSON-based wizards, the feature set is checked against the
+ \c featuresRequired setting that is described in
+ \l{https://doc.qt.io/qtcreator/creator-project-wizards.html}
+ {Adding New Custom Wizards} in the \QC Manual.
+
The QtSupport plugin creates an instance of IFeatureProvider and provides Qt specific
features for the available versions of Qt.
- \sa Core::IWizard
- \sa QtSupport::QtVersionManager
+ \sa Core::IWizardFactory
*/
/*!
diff --git a/src/plugins/coreplugin/fileutils.cpp b/src/plugins/coreplugin/fileutils.cpp
index 6c78348897..3df3309c31 100644
--- a/src/plugins/coreplugin/fileutils.cpp
+++ b/src/plugins/coreplugin/fileutils.cpp
@@ -17,8 +17,9 @@
#include <utils/commandline.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/terminalcommand.h>
+#include <utils/terminalhooks.h>
#include <utils/textfileformat.h>
#include <utils/unixutils.h>
@@ -56,7 +57,7 @@ void FileUtils::showInGraphicalShell(QWidget *parent, const FilePath &pathIn)
const QFileInfo fileInfo = pathIn.toFileInfo();
// Mac, Windows support folder or file.
if (HostOsInfo::isWindowsHost()) {
- const FilePath explorer = Environment::systemEnvironment().searchInPath(QLatin1String("explorer.exe"));
+ const FilePath explorer = FilePath("explorer.exe").searchInPath();
if (explorer.isEmpty()) {
QMessageBox::warning(parent,
Tr::tr("Launching Windows Explorer Failed"),
@@ -67,15 +68,16 @@ void FileUtils::showInGraphicalShell(QWidget *parent, const FilePath &pathIn)
if (!pathIn.isDir())
param += QLatin1String("/select,");
param += QDir::toNativeSeparators(fileInfo.canonicalFilePath());
- QtcProcess::startDetached({explorer, param});
+ Process::startDetached({explorer, param});
} else if (HostOsInfo::isMacHost()) {
- QtcProcess::startDetached({"/usr/bin/open", {"-R", fileInfo.canonicalFilePath()}});
+ Process::startDetached({"/usr/bin/open", {"-R", fileInfo.canonicalFilePath()}});
} else {
// we cannot select a file here, because no file browser really supports it...
const QString folder = fileInfo.isDir() ? fileInfo.absoluteFilePath() : fileInfo.filePath();
const QString app = UnixUtils::fileBrowser(ICore::settings());
QStringList browserArgs = ProcessArgs::splitArgs(
- UnixUtils::substituteFileBrowserParameters(app, folder));
+ UnixUtils::substituteFileBrowserParameters(app, folder),
+ HostOsInfo::hostOs());
QString error;
if (browserArgs.isEmpty()) {
error = Tr::tr("The command for file browser is not set.");
@@ -104,8 +106,7 @@ void FileUtils::showInFileSystemView(const FilePath &path)
void FileUtils::openTerminal(const FilePath &path, const Environment &env)
{
- QTC_ASSERT(DeviceFileHooks::instance().openTerminal, return);
- DeviceFileHooks::instance().openTerminal(path, env);
+ Terminal::Hooks::instance().openTerminal({std::nullopt, path, env});
}
QString FileUtils::msgFindInDirectory()
@@ -169,8 +170,11 @@ bool FileUtils::renameFile(const FilePath &orgFilePath, const FilePath &newFileP
if (orgFilePath == newFilePath)
return false;
- FilePath dir = orgFilePath.absolutePath();
+ const FilePath dir = orgFilePath.absolutePath();
IVersionControl *vc = VcsManager::findVersionControlForDirectory(dir);
+ const FilePath newDir = newFilePath.absolutePath();
+ if (newDir != dir && !newDir.ensureWritableDir())
+ return false;
bool result = false;
if (vc && vc->supportsOperation(IVersionControl::MoveOperation))
diff --git a/src/plugins/coreplugin/find/findtoolbar.cpp b/src/plugins/coreplugin/find/findtoolbar.cpp
index 11c51fe26e..96a99f6eba 100644
--- a/src/plugins/coreplugin/find/findtoolbar.cpp
+++ b/src/plugins/coreplugin/find/findtoolbar.cpp
@@ -133,7 +133,6 @@ FindToolBar::FindToolBar(CurrentDocumentFind *currentDocumentFind)
auto verticalLayout_3 = new QVBoxLayout();
verticalLayout_3->setSpacing(0);
verticalLayout_3->addWidget(m_advancedButton);
- verticalLayout_3->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding));
auto gridLayout = new QGridLayout();
gridLayout->setHorizontalSpacing(3);
@@ -147,8 +146,8 @@ FindToolBar::FindToolBar(CurrentDocumentFind *currentDocumentFind)
m_findEdit,
findButtonsWidget,
br,
- Column { m_replaceLabel, st }.setSpacing(0),
- Column { m_replaceEdit, st }.setSpacing(0),
+ Column { spacing(0), m_replaceLabel, st },
+ Column { spacing(0), m_replaceEdit, st },
gridLayout,
}.attachTo(this);
@@ -159,7 +158,7 @@ FindToolBar::FindToolBar(CurrentDocumentFind *currentDocumentFind)
mainLayout->setColumnStretch(1, 10);
setFocusProxy(m_findEdit);
- setProperty("topBorder", true);
+ setProperty(StyleHelper::C_TOP_BORDER, true);
setSingleRow(false);
QWidget::setTabOrder(m_findEdit, m_replaceEdit);
diff --git a/src/plugins/coreplugin/find/findtoolwindow.cpp b/src/plugins/coreplugin/find/findtoolwindow.cpp
index 1ab13cdc7c..0125671d34 100644
--- a/src/plugins/coreplugin/find/findtoolwindow.cpp
+++ b/src/plugins/coreplugin/find/findtoolwindow.cpp
@@ -109,7 +109,8 @@ FindToolWindow::FindToolWindow(QWidget *parent)
m_wholeWords,
m_regExp,
st,
- }.attachTo(m_optionsWidget, WithoutMargins);
+ noMargin
+ }.attachTo(m_optionsWidget);
Grid {
label, m_filterList, br,
diff --git a/src/plugins/coreplugin/find/itemviewfind.cpp b/src/plugins/coreplugin/find/itemviewfind.cpp
index c1c6e84f93..8bba18ae69 100644
--- a/src/plugins/coreplugin/find/itemviewfind.cpp
+++ b/src/plugins/coreplugin/find/itemviewfind.cpp
@@ -203,11 +203,18 @@ IFindSupport::Result ItemViewFind::find(const QString &searchTxt,
index, d->m_role).toString();
if (d->m_view->model()->flags(index) & Qt::ItemIsSelectable
&& (index.row() != currentRow || index.parent() != currentIndex.parent())
- && text.indexOf(searchExpr) != -1)
+ && text.indexOf(searchExpr) != -1) {
resultIndex = index;
+ break;
+ }
}
index = followingIndex(index, backward, &stepWrapped);
- } while (!resultIndex.isValid() && index.isValid() && index != currentIndex);
+ if (index == currentIndex) { // we're back where we started
+ if (d->m_view->model()->data(index, d->m_role).toString().indexOf(searchExpr) != -1)
+ resultIndex = index;
+ break;
+ }
+ } while (index.isValid());
if (resultIndex.isValid()) {
d->m_view->setCurrentIndex(resultIndex);
diff --git a/src/plugins/coreplugin/find/searchresultcolor.h b/src/plugins/coreplugin/find/searchresultcolor.h
deleted file mode 100644
index a6bc46b4e1..0000000000
--- a/src/plugins/coreplugin/find/searchresultcolor.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "../core_global.h"
-
-#include <QColor>
-#include <QHash>
-
-namespace Core {
-
-class CORE_EXPORT SearchResultColor
-{
-public:
- enum class Style { Default, Alt1, Alt2 };
-
- SearchResultColor() = default;
- SearchResultColor(const QColor &textBg, const QColor &textFg,
- const QColor &highlightBg, const QColor &highlightFg,
- const QColor &functionBg, const QColor &functionFg
- )
- : textBackground(textBg), textForeground(textFg),
- highlightBackground(highlightBg), highlightForeground(highlightFg),
- containingFunctionBackground(functionBg),containingFunctionForeground(functionFg)
- {
- if (!highlightBackground.isValid())
- highlightBackground = textBackground;
- if (!highlightForeground.isValid())
- highlightForeground = textForeground;
- if (!containingFunctionBackground.isValid())
- containingFunctionBackground = textBackground;
- if (!containingFunctionForeground.isValid())
- containingFunctionForeground = textForeground;
- }
-
- friend auto qHash(SearchResultColor::Style style)
- {
- return QT_PREPEND_NAMESPACE(qHash(int(style)));
- }
-
- QColor textBackground;
- QColor textForeground;
- QColor highlightBackground;
- QColor highlightForeground;
- QColor containingFunctionBackground;
- QColor containingFunctionForeground;
-};
-
-using SearchResultColors = QHash<SearchResultColor::Style, SearchResultColor>;
-
-} // namespace Core
diff --git a/src/plugins/coreplugin/find/searchresultitem.h b/src/plugins/coreplugin/find/searchresultitem.h
deleted file mode 100644
index d33df65f0a..0000000000
--- a/src/plugins/coreplugin/find/searchresultitem.h
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "searchresultcolor.h"
-
-#include <utils/filepath.h>
-#include <utils/hostosinfo.h>
-
-#include <QIcon>
-#include <QStringList>
-#include <QVariant>
-
-#include <optional>
-
-namespace Core {
-
-namespace Search {
-
-class TextPosition
-{
-public:
- TextPosition() = default;
- TextPosition(int line, int column) : line(line), column(column) {}
-
- int line = -1; // (0 or -1 for no line number)
- int column = -1; // 0-based starting position for a mark (-1 for no mark)
-
- bool operator<(const TextPosition &other)
- { return line < other.line || (line == other.line && column < other.column); }
-};
-
-class TextRange
-{
-public:
- TextRange() = default;
- TextRange(TextPosition begin, TextPosition end) : begin(begin), end(end) {}
-
- QString mid(const QString &text) const { return text.mid(begin.column, length(text)); }
-
- int length(const QString &text) const
- {
- if (begin.line == end.line)
- return end.column - begin.column;
-
- const int lineCount = end.line - begin.line;
- int index = text.indexOf(QChar::LineFeed);
- int currentLine = 1;
- while (index > 0 && currentLine < lineCount) {
- ++index;
- index = text.indexOf(QChar::LineFeed, index);
- ++currentLine;
- }
-
- if (index < 0)
- return 0;
-
- return index - begin.column + end.column;
- }
-
- TextPosition begin;
- TextPosition end;
-
- bool operator<(const TextRange &other)
- { return begin < other.begin; }
-};
-
-} // namespace Search
-
-class CORE_EXPORT SearchResultItem
-{
-public:
- QStringList path() const { return m_path; }
- void setPath(const QStringList &path) { m_path = path; }
- void setFilePath(const Utils::FilePath &filePath)
- {
- m_path = QStringList{filePath.toUserOutput()};
- }
-
- QString lineText() const { return m_lineText; }
- void setLineText(const QString &text) { m_lineText = text; }
-
- QIcon icon() const { return m_icon; }
- void setIcon(const QIcon &icon) { m_icon = icon; }
-
- QVariant userData() const { return m_userData; }
- void setUserData(const QVariant &userData) { m_userData = userData; }
-
- Search::TextRange mainRange() const { return m_mainRange; }
- void setMainRange(const Search::TextRange &mainRange) { m_mainRange = mainRange; }
- void setMainRange(int line, int column, int length)
- {
- m_mainRange = {};
- m_mainRange.begin.line = line;
- m_mainRange.begin.column = column;
- m_mainRange.end.line = m_mainRange.begin.line;
- m_mainRange.end.column = m_mainRange.begin.column + length;
- }
-
- bool useTextEditorFont() const { return m_useTextEditorFont; }
- void setUseTextEditorFont(bool useTextEditorFont) { m_useTextEditorFont = useTextEditorFont; }
-
- SearchResultColor::Style style() const { return m_style; }
- void setStyle(SearchResultColor::Style style) { m_style = style; }
-
- bool selectForReplacement() const { return m_selectForReplacement; }
- void setSelectForReplacement(bool select) { m_selectForReplacement = select; }
-
- std::optional<QString> containingFunctionName() const { return m_containingFunctionName; }
-
- void setContainingFunctionName(std::optional<QString> containingFunctionName)
- {
- m_containingFunctionName = std::move(containingFunctionName);
- }
-
-private:
- QStringList m_path; // hierarchy to the parent item of this item
- QString m_lineText; // text to show for the item itself
- QIcon m_icon; // icon to show in front of the item (by be null icon to hide)
- QVariant m_userData; // user data for identification of the item
- Search::TextRange m_mainRange;
- bool m_useTextEditorFont = false;
- bool m_selectForReplacement = true;
- SearchResultColor::Style m_style = SearchResultColor::Style::Default;
- std::optional<QString> m_containingFunctionName;
-};
-
-} // namespace Core
-
-Q_DECLARE_METATYPE(Core::SearchResultItem)
-Q_DECLARE_METATYPE(Core::Search::TextPosition)
diff --git a/src/plugins/coreplugin/find/searchresulttreeitems.cpp b/src/plugins/coreplugin/find/searchresulttreeitems.cpp
index 79ccc2429d..9aa8a765db 100644
--- a/src/plugins/coreplugin/find/searchresulttreeitems.cpp
+++ b/src/plugins/coreplugin/find/searchresulttreeitems.cpp
@@ -3,10 +3,12 @@
#include "searchresulttreeitems.h"
+#include <utils/searchresultitem.h>
+
namespace Core {
namespace Internal {
-SearchResultTreeItem::SearchResultTreeItem(const SearchResultItem &item,
+SearchResultTreeItem::SearchResultTreeItem(const Utils::SearchResultItem &item,
SearchResultTreeItem *parent)
: item(item),
m_parent(parent),
@@ -79,7 +81,8 @@ int SearchResultTreeItem::insertionIndex(const QString &text, SearchResultTreeIt
return insertionPosition - m_children.begin();
}
-int SearchResultTreeItem::insertionIndex(const SearchResultItem &item, SearchResultTreeItem **existingItem) const
+int SearchResultTreeItem::insertionIndex(const Utils::SearchResultItem &item,
+ SearchResultTreeItem **existingItem) const
{
return insertionIndex(item.lineText(), existingItem);
}
@@ -89,13 +92,13 @@ void SearchResultTreeItem::insertChild(int index, SearchResultTreeItem *child)
m_children.insert(index, child);
}
-void SearchResultTreeItem::insertChild(int index, const SearchResultItem &item)
+void SearchResultTreeItem::insertChild(int index, const Utils::SearchResultItem &item)
{
auto child = new SearchResultTreeItem(item, this);
insertChild(index, child);
}
-void SearchResultTreeItem::appendChild(const SearchResultItem &item)
+void SearchResultTreeItem::appendChild(const Utils::SearchResultItem &item)
{
insertChild(m_children.count(), item);
}
diff --git a/src/plugins/coreplugin/find/searchresulttreeitems.h b/src/plugins/coreplugin/find/searchresulttreeitems.h
index 4d4b353039..dbbc27d886 100644
--- a/src/plugins/coreplugin/find/searchresulttreeitems.h
+++ b/src/plugins/coreplugin/find/searchresulttreeitems.h
@@ -5,8 +5,7 @@
#include "searchresultwindow.h"
-#include <QString>
-#include <QList>
+#include <utils/searchresultitem.h>
namespace Core {
namespace Internal {
@@ -14,7 +13,7 @@ namespace Internal {
class SearchResultTreeItem
{
public:
- explicit SearchResultTreeItem(const SearchResultItem &item = SearchResultItem(),
+ explicit SearchResultTreeItem(const Utils::SearchResultItem &item = {},
SearchResultTreeItem *parent = nullptr);
virtual ~SearchResultTreeItem();
@@ -22,10 +21,10 @@ public:
SearchResultTreeItem *parent() const;
SearchResultTreeItem *childAt(int index) const;
int insertionIndex(const QString &text, SearchResultTreeItem **existingItem) const;
- int insertionIndex(const SearchResultItem &item, SearchResultTreeItem **existingItem) const;
+ int insertionIndex(const Utils::SearchResultItem &item, SearchResultTreeItem **existingItem) const;
void insertChild(int index, SearchResultTreeItem *child);
- void insertChild(int index, const SearchResultItem &item);
- void appendChild(const SearchResultItem &item);
+ void insertChild(int index, const Utils::SearchResultItem &item);
+ void appendChild(const Utils::SearchResultItem &item);
int childrenCount() const;
int rowOfItem() const;
void clearChildren();
@@ -36,7 +35,7 @@ public:
bool isGenerated() const { return m_isGenerated; }
void setGenerated(bool value) { m_isGenerated = value; }
- SearchResultItem item;
+ Utils::SearchResultItem item;
private:
SearchResultTreeItem *m_parent;
diff --git a/src/plugins/coreplugin/find/searchresulttreemodel.cpp b/src/plugins/coreplugin/find/searchresulttreemodel.cpp
index cf00cf755a..b6d620b80b 100644
--- a/src/plugins/coreplugin/find/searchresulttreemodel.cpp
+++ b/src/plugins/coreplugin/find/searchresulttreemodel.cpp
@@ -6,12 +6,15 @@
#include "searchresulttreeitemroles.h"
#include <utils/algorithm.h>
+#include <utils/searchresultitem.h>
#include <QApplication>
#include <QFont>
#include <QFontMetrics>
#include <QDebug>
+using namespace Utils;
+
namespace Core {
namespace Internal {
@@ -38,7 +41,7 @@ public:
QModelIndex next(const QModelIndex &idx, bool includeGenerated = false, bool *wrapped = nullptr) const;
QModelIndex prev(const QModelIndex &idx, bool includeGenerated = false, bool *wrapped = nullptr) const;
- QList<QModelIndex> addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
+ QList<QModelIndex> addResults(const SearchResultItems &items, SearchResult::AddMode mode);
static SearchResultTreeItem *treeItemAtIndex(const QModelIndex &idx);
@@ -51,7 +54,7 @@ public slots:
private:
QModelIndex index(SearchResultTreeItem *item) const;
- void addResultsToCurrentParent(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
+ void addResultsToCurrentParent(const SearchResultItems &items, SearchResult::AddMode mode);
QSet<SearchResultTreeItem *> addPath(const QStringList &path);
QVariant data(const SearchResultTreeItem *row, int role) const;
bool setCheckState(const QModelIndex &idx, Qt::CheckState checkState, bool firstCall = true);
@@ -319,9 +322,13 @@ QVariant SearchResultTreeModel::data(const SearchResultTreeItem *row, int role)
case ItemDataRoles::ResultBeginColumnNumberRole:
result = row->item.mainRange().begin.column;
break;
- case ItemDataRoles::SearchTermLengthRole:
- result = row->item.mainRange().length(row->item.lineText());
+ case ItemDataRoles::SearchTermLengthRole:{
+ Text::Range range = row->item.mainRange();
+ range.end.line -= range.begin.line - 1;
+ range.begin.line = 1;
+ result = range.length(row->item.lineText());
break;
+ }
case ItemDataRoles::ContainingFunctionNameRole:
result = row->item.containingFunctionName().value_or(QString{});
break;
@@ -382,7 +389,8 @@ QSet<SearchResultTreeItem *> SearchResultTreeModel::addPath(const QStringList &p
return pathNodes;
}
-void SearchResultTreeModel::addResultsToCurrentParent(const QList<SearchResultItem> &items, SearchResult::AddMode mode)
+void SearchResultTreeModel::addResultsToCurrentParent(const SearchResultItems &items,
+ SearchResult::AddMode mode)
{
if (!m_currentParent)
return;
@@ -433,12 +441,12 @@ static bool lessThanByPath(const SearchResultItem &a, const SearchResultItem &b)
* Adds the search result to the list of results, creating nodes for the path when
* necessary.
*/
-QList<QModelIndex> SearchResultTreeModel::addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode)
+QList<QModelIndex> SearchResultTreeModel::addResults(const SearchResultItems &items, SearchResult::AddMode mode)
{
QSet<SearchResultTreeItem *> pathNodes;
- QList<SearchResultItem> sortedItems = items;
+ SearchResultItems sortedItems = items;
std::stable_sort(sortedItems.begin(), sortedItems.end(), lessThanByPath);
- QList<SearchResultItem> itemSet;
+ SearchResultItems itemSet;
for (const SearchResultItem &item : sortedItems) {
m_editorFontIsUsed |= item.useTextEditorFont();
if (!m_currentParent || (m_currentPath != item.path())) {
@@ -577,7 +585,7 @@ void SearchResultFilterModel::setTextEditorFont(const QFont &font, const SearchR
sourceModel()->setTextEditorFont(font, colors);
}
-QList<QModelIndex> SearchResultFilterModel::addResults(const QList<SearchResultItem> &items,
+QList<QModelIndex> SearchResultFilterModel::addResults(const SearchResultItems &items,
SearchResult::AddMode mode)
{
QList<QModelIndex> sourceIndexes = sourceModel()->addResults(items, mode);
diff --git a/src/plugins/coreplugin/find/searchresulttreemodel.h b/src/plugins/coreplugin/find/searchresulttreemodel.h
index 6727fc56b4..716bcccca3 100644
--- a/src/plugins/coreplugin/find/searchresulttreemodel.h
+++ b/src/plugins/coreplugin/find/searchresulttreemodel.h
@@ -4,7 +4,6 @@
#pragma once
#include "searchresultwindow.h"
-#include "searchresultcolor.h"
#include <QFont>
#include <QSortFilterProxyModel>
@@ -25,8 +24,9 @@ public:
void setFilter(SearchResultFilter *filter);
void setShowReplaceUI(bool show);
- void setTextEditorFont(const QFont &font, const SearchResultColors &colors);
- QList<QModelIndex> addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
+ void setTextEditorFont(const QFont &font, const Utils::SearchResultColors &colors);
+ QList<QModelIndex> addResults(const Utils::SearchResultItems &items,
+ SearchResult::AddMode mode);
void clear();
QModelIndex next(const QModelIndex &idx, bool includeGenerated = false,
bool *wrapped = nullptr) const;
diff --git a/src/plugins/coreplugin/find/searchresulttreeview.cpp b/src/plugins/coreplugin/find/searchresulttreeview.cpp
index e5ac5b4719..d8026ccd84 100644
--- a/src/plugins/coreplugin/find/searchresulttreeview.cpp
+++ b/src/plugins/coreplugin/find/searchresulttreeview.cpp
@@ -7,11 +7,14 @@
#include "searchresulttreeitemdelegate.h"
#include <utils/qtcassert.h>
+#include <utils/searchresultitem.h>
#include <QHeaderView>
#include <QKeyEvent>
#include <QVBoxLayout>
+using namespace Utils;
+
namespace Core {
namespace Internal {
@@ -31,7 +34,7 @@ public:
};
SearchResultTreeView::SearchResultTreeView(QWidget *parent)
- : Utils::TreeView(parent)
+ : TreeView(parent)
, m_model(new SearchResultFilterModel(this))
, m_autoExpandResults(false)
{
@@ -70,7 +73,7 @@ void SearchResultTreeView::clear()
m_model->clear();
}
-void SearchResultTreeView::addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode)
+void SearchResultTreeView::addResults(const SearchResultItems &items, SearchResult::AddMode mode)
{
const QList<QModelIndex> addedParents = m_model->addResults(items, mode);
if (m_autoExpandResults && !addedParents.isEmpty()) {
diff --git a/src/plugins/coreplugin/find/searchresulttreeview.h b/src/plugins/coreplugin/find/searchresulttreeview.h
index 9ddbb06cd1..06996fefe0 100644
--- a/src/plugins/coreplugin/find/searchresulttreeview.h
+++ b/src/plugins/coreplugin/find/searchresulttreeview.h
@@ -6,9 +6,9 @@
#include "searchresultwindow.h"
#include <utils/itemviews.h>
+#include <utils/searchresultitem.h>
namespace Core {
-class SearchResultColor;
namespace Internal {
@@ -22,11 +22,11 @@ public:
explicit SearchResultTreeView(QWidget *parent = nullptr);
void setAutoExpandResults(bool expand);
- void setTextEditorFont(const QFont &font, const SearchResultColors &colors);
+ void setTextEditorFont(const QFont &font, const Utils::SearchResultColors &colors);
void setTabWidth(int tabWidth);
SearchResultFilterModel *model() const;
- void addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
+ void addResults(const Utils::SearchResultItems &items, SearchResult::AddMode mode);
void setFilter(SearchResultFilter *filter);
bool hasFilter() const;
void showFilterWidget(QWidget *parent);
@@ -35,7 +35,7 @@ public:
bool event(QEvent *e) override;
signals:
- void jumpToSearchResult(const SearchResultItem &item);
+ void jumpToSearchResult(const Utils::SearchResultItem &item);
void filterInvalidated();
void filterChanged();
diff --git a/src/plugins/coreplugin/find/searchresultwidget.cpp b/src/plugins/coreplugin/find/searchresultwidget.cpp
index 36344ed804..ec2ab2c0f6 100644
--- a/src/plugins/coreplugin/find/searchresultwidget.cpp
+++ b/src/plugins/coreplugin/find/searchresultwidget.cpp
@@ -93,7 +93,7 @@ SearchResultWidget::SearchResultWidget(QWidget *parent) :
topLayout->addWidget(m_topReplaceWidget);
m_messageWidget = new QFrame;
- pal.setColor(QPalette::WindowText, creatorTheme()->color(Theme::CanceledSearchTextColor));
+ pal.setColor(QPalette::WindowText, creatorTheme()->color(Theme::TextColorError));
m_messageWidget->setPalette(pal);
if (creatorTheme()->flag(Theme::DrawSearchResultWidgetFrame)) {
m_messageWidget->setFrameStyle(QFrame::Panel | QFrame::Raised);
@@ -226,7 +226,7 @@ void SearchResultWidget::setAdditionalReplaceWidget(QWidget *widget)
m_additionalReplaceWidget = widget;
}
-void SearchResultWidget::addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode)
+void SearchResultWidget::addResults(const SearchResultItems &items, SearchResult::AddMode mode)
{
bool firstItems = (m_count == 0);
m_count += items.size();
@@ -496,9 +496,9 @@ void SearchResultWidget::searchAgain()
emit searchAgainRequested();
}
-QList<SearchResultItem> SearchResultWidget::checkedItems() const
+SearchResultItems SearchResultWidget::checkedItems() const
{
- QList<SearchResultItem> result;
+ SearchResultItems result;
SearchResultFilterModel *model = m_searchResultTreeView->model();
const int fileCount = model->rowCount();
for (int i = 0; i < fileCount; ++i) {
diff --git a/src/plugins/coreplugin/find/searchresultwidget.h b/src/plugins/coreplugin/find/searchresultwidget.h
index f10bd5dd67..6722af579e 100644
--- a/src/plugins/coreplugin/find/searchresultwidget.h
+++ b/src/plugins/coreplugin/find/searchresultwidget.h
@@ -6,6 +6,7 @@
#include "searchresultwindow.h"
#include <utils/infobar.h>
+#include <utils/searchresultitem.h>
#include <QWidget>
@@ -33,9 +34,10 @@ public:
QWidget *additionalReplaceWidget() const;
void setAdditionalReplaceWidget(QWidget *widget);
- void addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
+ void addResults(const Utils::SearchResultItems &items, SearchResult::AddMode mode);
int count() const;
+ bool isSearching() const { return m_searching; }
void setSupportsReplace(bool replaceSupported, const QString &group);
bool supportsReplace() const;
@@ -51,7 +53,7 @@ public:
void notifyVisibilityChanged(bool visible);
- void setTextEditorFont(const QFont &font, const SearchResultColors &colors);
+ void setTextEditorFont(const QFont &font, const Utils::SearchResultColors &colors);
void setTabWidth(int tabWidth);
void setAutoExpandResults(bool expand);
@@ -75,8 +77,9 @@ public slots:
void sendRequestPopup();
signals:
- void activated(const Core::SearchResultItem &item);
- void replaceButtonClicked(const QString &replaceText, const QList<Core::SearchResultItem> &checkedItems, bool preserveCase);
+ void activated(const Utils::SearchResultItem &item);
+ void replaceButtonClicked(const QString &replaceText,
+ const Utils::SearchResultItems &checkedItems, bool preserveCase);
void replaceTextChanged(const QString &replaceText);
void searchAgainRequested();
void canceled();
@@ -90,7 +93,7 @@ signals:
void navigateStateChanged();
private:
- void handleJumpToSearchResult(const SearchResultItem &item);
+ void handleJumpToSearchResult(const Utils::SearchResultItem &item);
void handleReplaceButton();
void doReplace();
void cancel();
@@ -100,7 +103,7 @@ private:
void continueAfterSizeWarning();
void cancelAfterSizeWarning();
- QList<SearchResultItem> checkedItems() const;
+ Utils::SearchResultItems checkedItems() const;
void updateMatchesFoundLabel();
SearchResultTreeView *m_searchResultTreeView = nullptr;
diff --git a/src/plugins/coreplugin/find/searchresultwindow.cpp b/src/plugins/coreplugin/find/searchresultwindow.cpp
index 3b2ea6e8ae..e2a83a71a0 100644
--- a/src/plugins/coreplugin/find/searchresultwindow.cpp
+++ b/src/plugins/coreplugin/find/searchresultwindow.cpp
@@ -8,11 +8,11 @@
#include "../actionmanager/actionmanager.h"
#include "../actionmanager/command.h"
#include "../coreplugintr.h"
-#include "../icontext.h"
#include "../icore.h"
#include <utils/qtcassert.h>
#include <utils/styledbar.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QAction>
@@ -28,6 +28,9 @@
static const char SETTINGSKEYSECTIONNAME[] = "SearchResults";
static const char SETTINGSKEYEXPANDRESULTS[] = "ExpandResults";
+
+// Note that this is a soft limit: If all searches are still running, none of them will be
+// removed when a new one is started.
static const int MAX_SEARCH_HISTORY = 12;
namespace Core {
@@ -38,24 +41,6 @@ namespace Core {
\internal
*/
-/*!
- \class Core::Search::TextPosition
- \inmodule QtCreator
- \internal
-*/
-
-/*!
- \class Core::Search::TextRange
- \inmodule QtCreator
- \internal
-*/
-
-/*!
- \class Core::SearchResultItem
- \inmodule QtCreator
- \internal
-*/
-
namespace Internal {
class InternalScrollArea : public QScrollArea
@@ -90,6 +75,7 @@ namespace Internal {
void popupRequested(SearchResultWidget *widget, bool focus);
void handleExpandCollapseToolButton(bool checked);
void updateFilterButton();
+ int indexOfSearchToEvict() const;
QList<QWidget *> toolBarWidgets();
SearchResultWindow *q;
@@ -107,7 +93,7 @@ namespace Internal {
QList<SearchResult *> m_searchResults;
int m_currentIndex;
QFont m_font;
- SearchResultColors m_colors;
+ Utils::SearchResultColors m_colors;
int m_tabWidth;
};
@@ -257,14 +243,14 @@ using namespace Core::Internal;
*/
/*!
- \fn void Core::SearchResult::activated(const Core::SearchResultItem &item)
+ \fn void Core::SearchResult::activated(const Utils::SearchResultItem &item)
Indicates that the user activated the search result \a item by
double-clicking it, for example.
*/
/*!
\fn void Core::SearchResult::replaceButtonClicked(const QString &replaceText,
- const QList<Core::SearchResultItem> &checkedItems,
+ const Utils::SearchResultItems &checkedItems,
bool preserveCase)
Indicates that the user initiated a text replace by selecting
@@ -290,7 +276,7 @@ using namespace Core::Internal;
*/
/*!
- \fn void Core::SearchResult::cancelled()
+ \fn void Core::SearchResult::canceled()
This signal is emitted if the user cancels the search.
*/
@@ -473,11 +459,15 @@ SearchResult *SearchResultWindow::startNewSearch(const QString &label,
// temporarily set the index to the last but one existing
d->m_currentIndex = d->m_recentSearchesBox->count() - 2;
}
- d->m_searchResultWidgets.last()->notifyVisibilityChanged(false);
- // widget first, because that might send interesting signals to SearchResult
- delete d->m_searchResultWidgets.takeLast();
- delete d->m_searchResults.takeLast();
- d->m_recentSearchesBox->removeItem(d->m_recentSearchesBox->count() - 1);
+ if (const int toRemoveIndex = d->indexOfSearchToEvict(); toRemoveIndex != -1) {
+ SearchResultWidget * const widgetToRemove
+ = d->m_searchResultWidgets.takeAt(toRemoveIndex);
+ widgetToRemove->notifyVisibilityChanged(false);
+ // widget first, because that might send interesting signals to SearchResult
+ delete widgetToRemove;
+ delete d->m_searchResults.takeAt(toRemoveIndex);
+ d->m_recentSearchesBox->removeItem(toRemoveIndex + 1);
+ }
}
d->m_recentSearchesBox->insertItem(1, Tr::tr("%1 %2").arg(label, searchTerm));
}
@@ -570,7 +560,8 @@ void SearchResultWindow::setFocus()
/*!
\internal
*/
-void SearchResultWindow::setTextEditorFont(const QFont &font, const SearchResultColors &colors)
+void SearchResultWindow::setTextEditorFont(const QFont &font,
+ const Utils::SearchResultColors &colors)
{
d->m_font = font;
d->m_colors = colors;
@@ -616,13 +607,22 @@ void SearchResultWindowPrivate::updateFilterButton()
&& m_searchResultWidgets.at(visibleSearchIndex())->hasFilter());
}
+int SearchResultWindowPrivate::indexOfSearchToEvict() const
+{
+ for (int i = m_searchResultWidgets.size() - 1; i >= 0; --i) {
+ if (!m_searchResultWidgets.at(i)->isSearching())
+ return i;
+ }
+ return -1;
+}
+
QList<QWidget *> SearchResultWindowPrivate::toolBarWidgets()
{
if (!m_historyLabel)
m_historyLabel = new QLabel(Tr::tr("History:"));
if (!m_recentSearchesBox) {
m_recentSearchesBox = new QComboBox;
- m_recentSearchesBox->setProperty("drawleftborder", true);
+ m_recentSearchesBox->setProperty(Utils::StyleHelper::C_DRAW_LEFT_BORDER, true);
m_recentSearchesBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
m_recentSearchesBox->addItem(Tr::tr("New Search"));
connect(m_recentSearchesBox, &QComboBox::activated,
@@ -821,7 +821,7 @@ void SearchResult::setAdditionalReplaceWidget(QWidget *widget)
\sa addResults()
*/
-void SearchResult::addResult(const SearchResultItem &item)
+void SearchResult::addResult(const Utils::SearchResultItem &item)
{
m_widget->addResults({item}, AddOrdered);
}
@@ -832,7 +832,7 @@ void SearchResult::addResult(const SearchResultItem &item)
\sa addResult()
*/
-void SearchResult::addResults(const QList<SearchResultItem> &items, AddMode mode)
+void SearchResult::addResults(const Utils::SearchResultItems &items, AddMode mode)
{
m_widget->addResults(items, mode);
emit countChanged(m_widget->count());
@@ -845,7 +845,8 @@ void SearchResult::setFilter(SearchResultFilter *filter)
/*!
Notifies the \uicontrol {Search Results} output pane that the current search
- has been \a canceled, and the UI should reflect that.
+ has been \a canceled for the specified \a reason, and the UI should reflect
+ that.
*/
void SearchResult::finishSearch(bool canceled, const QString &reason)
{
diff --git a/src/plugins/coreplugin/find/searchresultwindow.h b/src/plugins/coreplugin/find/searchresultwindow.h
index da1bcf67e4..ae61192681 100644
--- a/src/plugins/coreplugin/find/searchresultwindow.h
+++ b/src/plugins/coreplugin/find/searchresultwindow.h
@@ -3,11 +3,10 @@
#pragma once
-#include "searchresultcolor.h"
-#include "searchresultitem.h"
-
#include <coreplugin/ioutputpane.h>
+#include <utils/searchresultitem.h>
+
#include <QVariant>
#include <QStringList>
#include <QIcon>
@@ -19,9 +18,10 @@ class QFont;
QT_END_NAMESPACE
namespace Core {
+
namespace Internal {
- class SearchResultWindowPrivate;
- class SearchResultWidget;
+class SearchResultWindowPrivate;
+class SearchResultWidget;
}
class SearchResultWindow;
@@ -31,7 +31,7 @@ class CORE_EXPORT SearchResultFilter : public QObject
public:
virtual QWidget *createWidget() = 0;
- virtual bool matches(const SearchResultItem &item) const = 0;
+ virtual bool matches(const Utils::SearchResultItem &item) const = 0;
signals:
void filterChanged();
@@ -59,8 +59,8 @@ public:
bool isInteractive() const { return !m_finishedHandler; }
public slots:
- void addResult(const SearchResultItem &item);
- void addResults(const QList<SearchResultItem> &items, AddMode mode);
+ void addResult(const Utils::SearchResultItem &item);
+ void addResults(const Utils::SearchResultItems &items, AddMode mode);
void setFilter(SearchResultFilter *filter); // Takes ownership
void finishSearch(bool canceled, const QString &reason = {});
void setTextToReplace(const QString &textToReplace);
@@ -70,8 +70,9 @@ public slots:
void popup();
signals:
- void activated(const Core::SearchResultItem &item);
- void replaceButtonClicked(const QString &replaceText, const QList<Core::SearchResultItem> &checkedItems, bool preserveCase);
+ void activated(const Utils::SearchResultItem &item);
+ void replaceButtonClicked(const QString &replaceText,
+ const Utils::SearchResultItems &checkedItems, bool preserveCase);
void replaceTextChanged(const QString &replaceText);
void canceled();
void paused(bool paused);
@@ -125,7 +126,7 @@ public:
void goToPrev() override;
bool canNavigate() const override;
- void setTextEditorFont(const QFont &font, const SearchResultColors &colors);
+ void setTextEditorFont(const QFont &font, const Utils::SearchResultColors &colors);
void setTabWidth(int width);
void openNewSearchPanel();
diff --git a/src/plugins/coreplugin/foldernavigationwidget.cpp b/src/plugins/coreplugin/foldernavigationwidget.cpp
index fc7b86ab17..7460f427b5 100644
--- a/src/plugins/coreplugin/foldernavigationwidget.cpp
+++ b/src/plugins/coreplugin/foldernavigationwidget.cpp
@@ -32,6 +32,7 @@
#include <utils/removefiledialog.h>
#include <utils/stringutils.h>
#include <utils/styledbar.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QAction>
@@ -818,7 +819,7 @@ Core::NavigationView FolderNavigationWidgetFactory::createWidget()
filter->setIcon(Utils::Icons::FILTER.icon());
filter->setToolTip(Tr::tr("Options"));
filter->setPopupMode(QToolButton::InstantPopup);
- filter->setProperty("noArrow", true);
+ filter->setProperty(StyleHelper::C_NO_ARROW, true);
auto filterMenu = new QMenu(filter);
filterMenu->addAction(fnw->m_filterHiddenFilesAction);
filterMenu->addAction(fnw->m_showBreadCrumbsAction);
diff --git a/src/plugins/coreplugin/generalsettings.cpp b/src/plugins/coreplugin/generalsettings.cpp
index ae5139b95c..19b66e6449 100644
--- a/src/plugins/coreplugin/generalsettings.cpp
+++ b/src/plugins/coreplugin/generalsettings.cpp
@@ -9,6 +9,8 @@
#include <coreplugin/dialogs/restartdialog.h>
+#include <extensionsystem/pluginmanager.h>
+
#include <utils/algorithm.h>
#include <utils/checkablemessagebox.h>
#include <utils/hostosinfo.h>
@@ -17,6 +19,7 @@
#include <utils/qtcolorbutton.h>
#include <utils/stylehelper.h>
+#include <QApplication>
#include <QCheckBox>
#include <QComboBox>
#include <QCoreApplication>
@@ -38,6 +41,7 @@ namespace Internal {
const char settingsKeyDPI[] = "Core/EnableHighDpiScaling";
const char settingsKeyShortcutsInContextMenu[] = "General/ShowShortcutsInContextMenu";
const char settingsKeyCodecForLocale[] = "General/OverrideCodecForLocale";
+const char settingsKeyToolbarStyle[] = "General/ToolbarStyle";
class GeneralSettingsWidget final : public IOptionsPageWidget
{
@@ -57,6 +61,7 @@ public:
void fillCodecBox() const;
static QByteArray codecForLocale();
static void setCodecForLocale(const QByteArray&);
+ void fillToolbarSyleBox() const;
GeneralSettings *q;
QComboBox *m_languageBox;
@@ -65,6 +70,7 @@ public:
QtColorButton *m_colorButton;
ThemeChooser *m_themeChooser;
QPushButton *m_resetWarningsButton;
+ QComboBox *m_toolbarStyleBox;
};
GeneralSettingsWidget::GeneralSettingsWidget(GeneralSettings *q)
@@ -75,6 +81,7 @@ GeneralSettingsWidget::GeneralSettingsWidget(GeneralSettings *q)
, m_colorButton(new QtColorButton)
, m_themeChooser(new ThemeChooser)
, m_resetWarningsButton(new QPushButton)
+ , m_toolbarStyleBox(new QComboBox)
{
m_languageBox->setObjectName("languageBox");
m_languageBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
@@ -92,6 +99,8 @@ GeneralSettingsWidget::GeneralSettingsWidget(GeneralSettings *q)
"Show Again\" (for example, missing highlighter).",
nullptr));
+ m_toolbarStyleBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+
auto resetColorButton = new QPushButton(Tr::tr("Reset"));
resetColorButton->setToolTip(Tr::tr("Reset to default.", "Color"));
@@ -114,12 +123,14 @@ GeneralSettingsWidget::GeneralSettingsWidget(GeneralSettings *q)
}
form.addRow({empty, m_showShortcutsInContextMenus});
- form.addRow(Row{m_resetWarningsButton, st});
+ form.addRow({Row{m_resetWarningsButton, st}});
form.addRow({Tr::tr("Text codec for tools:"), m_codecBox, st});
+ form.addRow({Tr::tr("Toolbar Style:"), m_toolbarStyleBox, st});
Column{Group{title(Tr::tr("User Interface")), form}}.attachTo(this);
fillLanguageBox();
fillCodecBox();
+ fillToolbarSyleBox();
m_colorButton->setColor(StyleHelper::requestedBaseColor());
m_resetWarningsButton->setEnabled(canResetWarnings());
@@ -188,6 +199,15 @@ void GeneralSettingsWidget::apply()
// Apply the new base color if accepted
StyleHelper::setBaseColor(m_colorButton->color());
m_themeChooser->apply();
+ if (const auto newStyle = m_toolbarStyleBox->currentData().value<StyleHelper::ToolbarStyle>();
+ newStyle != StyleHelper::toolbarStyle()) {
+ ICore::settings()->setValueWithDefault(settingsKeyToolbarStyle, int(newStyle),
+ int(StyleHelper::defaultToolbarStyle));
+ StyleHelper::setToolbarStyle(newStyle);
+ QStyle *applicationStyle = QApplication::style();
+ for (QWidget *widget : QApplication::allWidgets())
+ applicationStyle->polish(widget);
+ }
}
bool GeneralSettings::showShortcutsInContextMenu()
@@ -206,14 +226,13 @@ void GeneralSettingsWidget::resetInterfaceColor()
void GeneralSettingsWidget::resetWarnings()
{
InfoBar::clearGloballySuppressed();
- CheckableMessageBox::resetAllDoNotAskAgainQuestions(ICore::settings());
+ CheckableMessageBox::resetAllDoNotAskAgainQuestions();
m_resetWarningsButton->setEnabled(false);
}
bool GeneralSettingsWidget::canResetWarnings()
{
- return InfoBar::anyGloballySuppressed()
- || CheckableMessageBox::hasSuppressedQuestions(ICore::settings());
+ return InfoBar::anyGloballySuppressed() || CheckableMessageBox::hasSuppressedQuestions();
}
void GeneralSettingsWidget::resetLanguage()
@@ -268,6 +287,24 @@ void GeneralSettingsWidget::setCodecForLocale(const QByteArray &codec)
QTextCodec::setCodecForLocale(QTextCodec::codecForName(codec));
}
+StyleHelper::ToolbarStyle toolbarStylefromSettings()
+{
+ if (!ExtensionSystem::PluginManager::instance()) // May happen in tests
+ return StyleHelper::defaultToolbarStyle;
+
+ return StyleHelper::ToolbarStyle(
+ ICore::settings()->value(settingsKeyToolbarStyle,
+ StyleHelper::defaultToolbarStyle).toInt());
+}
+
+void GeneralSettingsWidget::fillToolbarSyleBox() const
+{
+ m_toolbarStyleBox->addItem(Tr::tr("Compact"), StyleHelper::ToolbarStyleCompact);
+ m_toolbarStyleBox->addItem(Tr::tr("Relaxed"), StyleHelper::ToolbarStyleRelaxed);
+ const int curId = m_toolbarStyleBox->findData(toolbarStylefromSettings());
+ m_toolbarStyleBox->setCurrentIndex(curId);
+}
+
void GeneralSettings::setShowShortcutsInContextMenu(bool show)
{
ICore::settings()->setValueWithDefault(settingsKeyShortcutsInContextMenu,
@@ -276,6 +313,11 @@ void GeneralSettings::setShowShortcutsInContextMenu(bool show)
QCoreApplication::setAttribute(Qt::AA_DontShowShortcutsInContextMenus, !show);
}
+void GeneralSettings::applyToolbarStyleFromSettings()
+{
+ StyleHelper::setToolbarStyle(toolbarStylefromSettings());
+}
+
GeneralSettings::GeneralSettings()
{
setId(Constants::SETTINGS_ID_INTERFACE);
diff --git a/src/plugins/coreplugin/generalsettings.h b/src/plugins/coreplugin/generalsettings.h
index 78d1dab270..4587443eac 100644
--- a/src/plugins/coreplugin/generalsettings.h
+++ b/src/plugins/coreplugin/generalsettings.h
@@ -16,6 +16,8 @@ public:
static bool showShortcutsInContextMenu();
void setShowShortcutsInContextMenu(bool show);
+ static void applyToolbarStyleFromSettings();
+
private:
friend class GeneralSettingsWidget;
bool m_defaultShowShortcutsInContextMenu;
diff --git a/src/plugins/coreplugin/ioutputpane.h b/src/plugins/coreplugin/ioutputpane.h
index 8222ca19bc..f3ae7fa0b8 100644
--- a/src/plugins/coreplugin/ioutputpane.h
+++ b/src/plugins/coreplugin/ioutputpane.h
@@ -4,6 +4,7 @@
#pragma once
#include "core_global.h"
+#include "icontext.h"
#include <utils/fancylineedit.h>
#include <utils/id.h>
@@ -88,6 +89,7 @@ protected:
void setFilteringEnabled(bool enable);
QWidget *filterWidget() const { return m_filterOutputLineEdit; }
void setupContext(const char *context, QWidget *widget);
+ void setupContext(const Context &context, QWidget *widget);
void setZoomButtonsEnabled(bool enabled);
private:
diff --git a/src/plugins/coreplugin/iwizardfactory.cpp b/src/plugins/coreplugin/iwizardfactory.cpp
index 3571353105..caa664d7e4 100644
--- a/src/plugins/coreplugin/iwizardfactory.cpp
+++ b/src/plugins/coreplugin/iwizardfactory.cpp
@@ -67,6 +67,17 @@
*/
/*!
+ \enum Core::IWizardFactory::WizardFlag
+
+ Holds information about the created projects and files.
+
+ \value PlatformIndependent
+ The wizard creates projects that run on all platforms.
+ \value ForceCapitalLetterForFileName
+ The wizard uses an initial capital letter for the names of new files.
+*/
+
+/*!
\fn Core::IWizardFactory::WizardKind Core::IWizardFactory::kind() const
Returns what kind of objects are created by the wizard.
*/
@@ -237,6 +248,9 @@ FilePath IWizardFactory::runPath(const FilePath &defaultPath) const
The \a path argument is a suggestion for the location where files should be
created. The wizard should fill this in its path selection elements as a
default path.
+
+ When \a showWizard is \c false, the wizard instance is created and set up
+ but not actually shown.
*/
Wizard *IWizardFactory::runWizard(const FilePath &path, QWidget *parent, Id platform,
const QVariantMap &variables,
diff --git a/src/plugins/coreplugin/locator/basefilefilter.cpp b/src/plugins/coreplugin/locator/basefilefilter.cpp
deleted file mode 100644
index 9ce27cc052..0000000000
--- a/src/plugins/coreplugin/locator/basefilefilter.cpp
+++ /dev/null
@@ -1,276 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "basefilefilter.h"
-
-#include <coreplugin/editormanager/editormanager.h>
-#include <utils/algorithm.h>
-#include <utils/filepath.h>
-#include <utils/linecolumn.h>
-#include <utils/link.h>
-#include <utils/qtcassert.h>
-
-#include <QDir>
-#include <QRegularExpression>
-
-using namespace Utils;
-
-namespace Core {
-namespace Internal {
-
-class Data
-{
-public:
- void clear()
- {
- iterator.clear();
- previousResultPaths.clear();
- previousEntry.clear();
- }
-
- QSharedPointer<BaseFileFilter::Iterator> iterator;
- FilePaths previousResultPaths;
- bool forceNewSearchList;
- QString previousEntry;
-};
-
-class BaseFileFilterPrivate
-{
-public:
- Data m_data;
- Data m_current;
-};
-
-} // Internal
-
-/*!
- \class Core::BaseFileFilter
- \inheaderfile coreplugin/locator/basefilefilter.h
- \inmodule QtCreator
-
- \brief The BaseFileFilter class is a base class for locator filter classes.
-*/
-
-/*!
- \class Core::BaseFileFilter::Iterator
- \inmodule QtCreator
- \internal
-*/
-
-/*!
- \class Core::BaseFileFilter::ListIterator
- \inmodule QtCreator
- \internal
-*/
-
-BaseFileFilter::Iterator::~Iterator() = default;
-
-/*!
- \internal
-*/
-BaseFileFilter::BaseFileFilter()
- : d(new Internal::BaseFileFilterPrivate)
-{
- d->m_data.forceNewSearchList = true;
- setFileIterator(new ListIterator({}));
-}
-
-/*!
- \internal
-*/
-BaseFileFilter::~BaseFileFilter()
-{
- delete d;
-}
-
-/*!
- \reimp
-*/
-void BaseFileFilter::prepareSearch(const QString &entry)
-{
- Q_UNUSED(entry)
- d->m_current = d->m_data;
- d->m_data.forceNewSearchList = false;
-}
-
-ILocatorFilter::MatchLevel BaseFileFilter::matchLevelFor(const QRegularExpressionMatch &match,
- const QString &matchText)
-{
- const int consecutivePos = match.capturedStart(1);
- if (consecutivePos == 0)
- return MatchLevel::Best;
- if (consecutivePos > 0) {
- const QChar prevChar = matchText.at(consecutivePos - 1);
- if (prevChar == '_' || prevChar == '.')
- return MatchLevel::Better;
- }
- if (match.capturedStart() == 0)
- return MatchLevel::Good;
- return MatchLevel::Normal;
-}
-
-/*!
- \reimp
-*/
-QList<LocatorFilterEntry> BaseFileFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future, const QString &origEntry)
-{
- QList<LocatorFilterEntry> entries[int(MatchLevel::Count)];
- // If search string contains spaces, treat them as wildcard '*' and search in full path
- const QString entry = QDir::fromNativeSeparators(origEntry).replace(' ', '*');
- const Link link = Link::fromString(entry, true);
-
- const QRegularExpression regexp = createRegExp(link.targetFilePath.toString());
- if (!regexp.isValid()) {
- d->m_current.clear(); // free memory
- return {};
- }
- auto containsPathSeparator = [](const QString &candidate) {
- return candidate.contains('/') || candidate.contains('*');
- };
-
- const bool hasPathSeparator = containsPathSeparator(link.targetFilePath.toString());
- const bool containsPreviousEntry = !d->m_current.previousEntry.isEmpty()
- && link.targetFilePath.toString().contains(d->m_current.previousEntry);
- const bool pathSeparatorAdded = !containsPathSeparator(d->m_current.previousEntry)
- && hasPathSeparator;
- const bool searchInPreviousResults = !d->m_current.forceNewSearchList && containsPreviousEntry
- && !pathSeparatorAdded;
- if (searchInPreviousResults)
- d->m_current.iterator.reset(new ListIterator(d->m_current.previousResultPaths));
-
- QTC_ASSERT(d->m_current.iterator.data(), return QList<LocatorFilterEntry>());
- d->m_current.previousResultPaths.clear();
- d->m_current.previousEntry = link.targetFilePath.toString();
- d->m_current.iterator->toFront();
- bool canceled = false;
- while (d->m_current.iterator->hasNext()) {
- if (future.isCanceled()) {
- canceled = true;
- break;
- }
-
- d->m_current.iterator->next();
- FilePath path = d->m_current.iterator->filePath();
- QString matchText = hasPathSeparator ? path.toString() : path.fileName();
- QRegularExpressionMatch match = regexp.match(matchText);
-
- if (match.hasMatch()) {
- LocatorFilterEntry filterEntry(this, path.fileName());
- filterEntry.filePath = path;
- filterEntry.extraInfo = path.shortNativePath();
- filterEntry.linkForEditor = Link(path, link.targetLine, link.targetColumn);
- const MatchLevel matchLevel = matchLevelFor(match, matchText);
- if (hasPathSeparator) {
- match = regexp.match(filterEntry.extraInfo);
- filterEntry.highlightInfo =
- highlightInfo(match, LocatorFilterEntry::HighlightInfo::ExtraInfo);
- } else {
- filterEntry.highlightInfo = highlightInfo(match);
- }
-
- entries[int(matchLevel)].append(filterEntry);
- d->m_current.previousResultPaths.append(path);
- }
- }
-
- if (canceled) {
- // we keep the old list of previous search results if this search was canceled
- // so a later search without forceNewSearchList will use that previous list instead of an
- // incomplete list of a canceled search
- d->m_current.clear(); // free memory
- } else {
- d->m_current.iterator.clear();
- QMetaObject::invokeMethod(this, &BaseFileFilter::updatePreviousResultData,
- Qt::QueuedConnection);
- }
-
- for (auto &entry : entries) {
- if (entry.size() < 1000)
- Utils::sort(entry, LocatorFilterEntry::compareLexigraphically);
- }
-
- return std::accumulate(std::begin(entries), std::end(entries), QList<LocatorFilterEntry>());
-}
-
-/*!
- \reimp
-*/
-void BaseFileFilter::accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const
-{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- openEditorAt(selection);
-}
-
-void BaseFileFilter::openEditorAt(const LocatorFilterEntry &entry)
-{
- if (entry.linkForEditor) {
- EditorManager::openEditorAt(*entry.linkForEditor, {}, EditorManager::AllowExternalEditor);
- return;
- }
- EditorManager::openEditor(entry.filePath, {}, EditorManager::AllowExternalEditor);
-}
-
-/*!
- Takes ownership of the \a iterator. The previously set iterator might not be deleted until
- a currently running search is finished.
-*/
-
-void BaseFileFilter::setFileIterator(BaseFileFilter::Iterator *iterator)
-{
- d->m_data.clear();
- d->m_data.forceNewSearchList = true;
- d->m_data.iterator.reset(iterator);
-}
-
-/*!
- Returns the file iterator.
-*/
-QSharedPointer<BaseFileFilter::Iterator> BaseFileFilter::fileIterator()
-{
- return d->m_data.iterator;
-}
-
-void BaseFileFilter::updatePreviousResultData()
-{
- if (d->m_data.forceNewSearchList) // in the meantime the iterator was reset / cache invalidated
- return; // do not update with the new result list etc
- d->m_data.previousEntry = d->m_current.previousEntry;
- d->m_data.previousResultPaths = d->m_current.previousResultPaths;
- // forceNewSearchList was already reset in prepareSearch
-}
-
-BaseFileFilter::ListIterator::ListIterator(const FilePaths &filePaths)
-{
- m_filePaths = filePaths;
- toFront();
-}
-
-void BaseFileFilter::ListIterator::toFront()
-{
- m_pathPosition = m_filePaths.constBegin() - 1;
-}
-
-bool BaseFileFilter::ListIterator::hasNext() const
-{
- QTC_ASSERT(m_pathPosition != m_filePaths.constEnd(), return false);
- return m_pathPosition + 1 != m_filePaths.constEnd();
-}
-
-FilePath BaseFileFilter::ListIterator::next()
-{
- QTC_ASSERT(m_pathPosition != m_filePaths.constEnd(), return {});
- ++m_pathPosition;
- QTC_ASSERT(m_pathPosition != m_filePaths.constEnd(), return {});
- return *m_pathPosition;
-}
-
-FilePath BaseFileFilter::ListIterator::filePath() const
-{
- QTC_ASSERT(m_pathPosition != m_filePaths.constEnd(), return {});
- return *m_pathPosition;
-}
-
-} // Core
diff --git a/src/plugins/coreplugin/locator/basefilefilter.h b/src/plugins/coreplugin/locator/basefilefilter.h
deleted file mode 100644
index 710cd21edf..0000000000
--- a/src/plugins/coreplugin/locator/basefilefilter.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "ilocatorfilter.h"
-
-#include <utils/filepath.h>
-
-#include <QSharedPointer>
-
-namespace Core {
-
-namespace Internal { class BaseFileFilterPrivate; }
-
-class CORE_EXPORT BaseFileFilter : public ILocatorFilter
-{
- Q_OBJECT
-
-public:
- class CORE_EXPORT Iterator {
- public:
- virtual ~Iterator();
- virtual void toFront() = 0;
- virtual bool hasNext() const = 0;
- virtual Utils::FilePath next() = 0;
- virtual Utils::FilePath filePath() const = 0;
- };
-
- class CORE_EXPORT ListIterator final : public Iterator {
- public:
- ListIterator(const Utils::FilePaths &filePaths);
-
- void toFront() override;
- bool hasNext() const override;
- Utils::FilePath next() override;
- Utils::FilePath filePath() const override;
-
- private:
- Utils::FilePaths m_filePaths;
- Utils::FilePaths::const_iterator m_pathPosition;
- };
-
- BaseFileFilter();
- ~BaseFileFilter() override;
- void prepareSearch(const QString &entry) override;
- QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
- static void openEditorAt(const LocatorFilterEntry &entry);
-
-protected:
- void setFileIterator(Iterator *iterator);
- QSharedPointer<Iterator> fileIterator();
-
-private:
- static MatchLevel matchLevelFor(const QRegularExpressionMatch &match,
- const QString &matchText);
- void updatePreviousResultData();
-
- Internal::BaseFileFilterPrivate *d = nullptr;
-};
-
-} // namespace Core
diff --git a/src/plugins/coreplugin/locator/commandlocator.cpp b/src/plugins/coreplugin/locator/commandlocator.cpp
index de2989bc17..e960b4df85 100644
--- a/src/plugins/coreplugin/locator/commandlocator.cpp
+++ b/src/plugins/coreplugin/locator/commandlocator.cpp
@@ -5,7 +5,6 @@
#include <coreplugin/actionmanager/command.h>
-#include <utils/qtcassert.h>
#include <utils/stringutils.h>
#include <QAction>
@@ -14,97 +13,58 @@ using namespace Utils;
namespace Core {
-struct CommandLocatorPrivate
-{
- QList<Command *> commands;
- QList<QPair<int, QString>> commandsData;
-};
-
-/*!
- \class Core::CommandLocator
- \inmodule QtCreator
- \internal
-*/
-
-CommandLocator::CommandLocator(Id id,
- const QString &displayName,
- const QString &shortCutString,
- QObject *parent) :
- ILocatorFilter(parent),
- d(new CommandLocatorPrivate)
+CommandLocator::CommandLocator(Id id, const QString &displayName, const QString &shortCutString,
+ QObject *parent)
+ : ILocatorFilter(parent)
{
setId(id);
setDisplayName(displayName);
setDefaultShortcutString(shortCutString);
}
-CommandLocator::~CommandLocator()
-{
- delete d;
-}
-
-void CommandLocator::appendCommand(Command *cmd)
-{
- d->commands.push_back(cmd);
-}
-
-void CommandLocator::prepareSearch(const QString &entry)
+LocatorMatcherTasks CommandLocator::matchers()
{
- Q_UNUSED(entry)
- d->commandsData = {};
- const int count = d->commands.size();
- // Get active, enabled actions matching text, store in list.
- // Reference via index in extraInfo.
- for (int i = 0; i < count; ++i) {
- Command *command = d->commands.at(i);
- if (!command->isActive())
- continue;
- QAction *action = command->action();
- if (action && action->isEnabled())
- d->commandsData.append({i, action->text()});
- }
-}
-
-QList<LocatorFilterEntry> CommandLocator::matchesFor(QFutureInterface<LocatorFilterEntry> &future, const QString &entry)
-{
- QList<LocatorFilterEntry> goodEntries;
- QList<LocatorFilterEntry> betterEntries;
- const Qt::CaseSensitivity entryCaseSensitivity = caseSensitivity(entry);
- for (const auto &pair : std::as_const(d->commandsData)) {
- if (future.isCanceled())
- break;
-
- const QString text = Utils::stripAccelerator(pair.second);
- const int index = text.indexOf(entry, 0, entryCaseSensitivity);
- if (index >= 0) {
- LocatorFilterEntry filterEntry(this, text, QVariant(pair.first));
- filterEntry.highlightInfo = {index, int(entry.length())};
-
- if (index == 0)
- betterEntries.append(filterEntry);
- else
- goodEntries.append(filterEntry);
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [storage, commands = m_commands] {
+ const QString input = storage->input();
+ const Qt::CaseSensitivity inputCaseSensitivity = caseSensitivity(input);
+ LocatorFilterEntries goodEntries;
+ LocatorFilterEntries betterEntries;
+ for (Command *command : commands) {
+ if (!command->isActive())
+ continue;
+
+ QAction *action = command->action();
+ if (!action || !action->isEnabled())
+ continue;
+
+ const QString text = Utils::stripAccelerator(action->text());
+ const int index = text.indexOf(input, 0, inputCaseSensitivity);
+ if (index >= 0) {
+ LocatorFilterEntry entry;
+ entry.displayName = text;
+ entry.acceptor = [actionPointer = QPointer(action)] {
+ if (actionPointer) {
+ QMetaObject::invokeMethod(actionPointer, [actionPointer] {
+ if (actionPointer && actionPointer->isEnabled())
+ actionPointer->trigger();
+ }, Qt::QueuedConnection);
+ }
+ return AcceptResult();
+ };
+ entry.highlightInfo = {index, int(input.length())};
+ if (index == 0)
+ betterEntries.append(entry);
+ else
+ goodEntries.append(entry);
+ }
}
- }
- betterEntries.append(goodEntries);
- return betterEntries;
-}
-
-void CommandLocator::accept(const LocatorFilterEntry &entry,
- QString *newText, int *selectionStart, int *selectionLength) const
-{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- // Retrieve action via index.
- const int index = entry.internalData.toInt();
- QTC_ASSERT(index >= 0 && index < d->commands.size(), return);
- QAction *action = d->commands.at(index)->action();
- // avoid nested stack trace and blocking locator by delayed triggering
- QMetaObject::invokeMethod(action, [action] {
- if (action->isEnabled())
- action->trigger();
- }, Qt::QueuedConnection);
+ storage->reportOutput(betterEntries + goodEntries);
+ };
+ return {{Sync(onSetup), storage}};
}
} // namespace Core
diff --git a/src/plugins/coreplugin/locator/commandlocator.h b/src/plugins/coreplugin/locator/commandlocator.h
index 8199661bed..8e7d1ead03 100644
--- a/src/plugins/coreplugin/locator/commandlocator.h
+++ b/src/plugins/coreplugin/locator/commandlocator.h
@@ -10,27 +10,18 @@ namespace Core {
/* Command locators: Provides completion for a set of
* Core::Command's by sub-string of their action's text. */
class Command;
-struct CommandLocatorPrivate;
class CORE_EXPORT CommandLocator : public ILocatorFilter
{
- Q_OBJECT
-
public:
- CommandLocator(Utils::Id id, const QString &displayName,
- const QString &shortCutString, QObject *parent = nullptr);
- ~CommandLocator() override;
-
- void appendCommand(Command *cmd);
-
- void prepareSearch(const QString &entry) override;
- QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
+ CommandLocator(Utils::Id id, const QString &displayName, const QString &shortCutString,
+ QObject *parent = nullptr);
+ void appendCommand(Command *cmd) { m_commands.push_back(cmd); }
private:
- CommandLocatorPrivate *d = nullptr;
+ LocatorMatcherTasks matchers() final;
+
+ QList<Command *> m_commands;
};
} // namespace Core
diff --git a/src/plugins/coreplugin/locator/directoryfilter.cpp b/src/plugins/coreplugin/locator/directoryfilter.cpp
index 19a9f098e6..42cf51addf 100644
--- a/src/plugins/coreplugin/locator/directoryfilter.cpp
+++ b/src/plugins/coreplugin/locator/directoryfilter.cpp
@@ -7,12 +7,12 @@
#include "../coreplugintr.h"
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/fileutils.h>
#include <utils/filesearch.h>
#include <utils/layoutbuilder.h>
#include <QCheckBox>
-#include <QCoreApplication>
#include <QDialog>
#include <QDialogButtonBox>
#include <QJsonArray>
@@ -46,6 +46,28 @@ static QString defaultDisplayName()
return Tr::tr("Generic Directory Filter");
}
+static void refresh(QPromise<FilePaths> &promise, const FilePaths &directories,
+ const QStringList &filters, const QStringList &exclusionFilters,
+ const QString &displayName)
+{
+ SubDirFileIterator subDirIterator(directories, filters, exclusionFilters);
+ promise.setProgressRange(0, subDirIterator.maxProgress());
+ FilePaths files;
+ const auto end = subDirIterator.end();
+ for (auto it = subDirIterator.begin(); it != end; ++it) {
+ if (promise.isCanceled()) {
+ promise.setProgressValueAndText(subDirIterator.currentProgress(),
+ Tr::tr("%1 filter update: canceled").arg(displayName));
+ return;
+ }
+ files << (*it).filePath;
+ promise.setProgressValueAndText(subDirIterator.currentProgress(),
+ Tr::tr("%1 filter update: %n files", nullptr, files.size()).arg(displayName));
+ }
+ promise.setProgressValue(subDirIterator.maxProgress());
+ promise.addResult(files);
+}
+
DirectoryFilter::DirectoryFilter(Id id)
: m_filters(kFiltersDefault)
, m_exclusionFilters(kExclusionFiltersDefault)
@@ -53,15 +75,34 @@ DirectoryFilter::DirectoryFilter(Id id)
setId(id);
setDefaultIncludedByDefault(true);
setDisplayName(defaultDisplayName());
- setDescription(Tr::tr("Matches all files from a custom set of directories. Append \"+<number>\" "
+ setDescription(Tr::tr("Locates files from a custom set of directories. Append \"+<number>\" "
"or \":<number>\" to jump to the given line number. Append another "
"\"+<number>\" or \":<number>\" to jump to the column number as well."));
+
+ using namespace Tasking;
+ const auto groupSetup = [this] {
+ if (!m_directories.isEmpty())
+ return TaskAction::Continue; // Async task will run
+ m_cache.setFilePaths({});
+ return TaskAction::StopWithDone; // Group stops, skips async task
+ };
+ const auto asyncSetup = [this](Async<FilePaths> &async) {
+ async.setConcurrentCallData(&refresh, m_directories, m_filters, m_exclusionFilters,
+ displayName());
+ };
+ const auto asyncDone = [this](const Async<FilePaths> &async) {
+ if (async.isResultAvailable())
+ m_cache.setFilePaths(async.result());
+ };
+ const Group root {
+ onGroupSetup(groupSetup),
+ AsyncTask<FilePaths>(asyncSetup, asyncDone)
+ };
+ setRefreshRecipe(root);
}
void DirectoryFilter::saveState(QJsonObject &object) const
{
- QMutexLocker locker(&m_lock); // m_files is modified in other thread
-
if (displayName() != defaultDisplayName())
object.insert(kDisplayNameKey, displayName());
if (!m_directories.isEmpty()) {
@@ -71,10 +112,11 @@ void DirectoryFilter::saveState(QJsonObject &object) const
}
if (m_filters != kFiltersDefault)
object.insert(kFiltersKey, QJsonArray::fromStringList(m_filters));
- if (!m_files.isEmpty())
- object.insert(kFilesKey,
- QJsonArray::fromStringList(
- Utils::transform(m_files, &Utils::FilePath::toString)));
+ const std::optional<FilePaths> files = m_cache.filePaths();
+ if (files) {
+ object.insert(kFilesKey, QJsonArray::fromStringList(
+ Utils::transform(*files, &FilePath::toString)));
+ }
if (m_exclusionFilters != kExclusionFiltersDefault)
object.insert(kExclusionFiltersKey, QJsonArray::fromStringList(m_exclusionFilters));
}
@@ -92,12 +134,14 @@ static FilePaths toFilePaths(const QJsonArray &array)
void DirectoryFilter::restoreState(const QJsonObject &object)
{
- QMutexLocker locker(&m_lock);
setDisplayName(object.value(kDisplayNameKey).toString(defaultDisplayName()));
m_directories = toFilePaths(object.value(kDirectoriesKey).toArray());
m_filters = toStringList(
object.value(kFiltersKey).toArray(QJsonArray::fromStringList(kFiltersDefault)));
- m_files = FileUtils::toFilePathList(toStringList(object.value(kFilesKey).toArray()));
+ if (object.contains(kFilesKey)) {
+ m_cache.setFilePaths(FileUtils::toFilePathList(
+ toStringList(object.value(kFilesKey).toArray())));
+ }
m_exclusionFilters = toStringList(
object.value(kExclusionFiltersKey)
.toArray(QJsonArray::fromStringList(kExclusionFiltersDefault)));
@@ -107,8 +151,6 @@ void DirectoryFilter::restoreState(const QByteArray &state)
{
if (isOldSetting(state)) {
// TODO read old settings, remove some time after Qt Creator 4.15
- QMutexLocker locker(&m_lock);
-
QString name;
QStringList directories;
QString shortcut;
@@ -122,7 +164,7 @@ void DirectoryFilter::restoreState(const QByteArray &state)
in >> shortcut;
in >> defaultFilter;
in >> files;
- m_files = FileUtils::toFilePathList(files);
+ m_cache.setFilePaths(FileUtils::toFilePathList(files));
if (!in.atEnd()) // Qt Creator 4.3 and later
in >> m_exclusionFilters;
else
@@ -136,12 +178,9 @@ void DirectoryFilter::restoreState(const QByteArray &state)
setDisplayName(name);
setShortcutString(shortcut);
setIncludedByDefault(defaultFilter);
-
- locker.unlock();
} else {
ILocatorFilter::restoreState(state);
}
- updateFileIterator();
}
class DirectoryFilterOptions : public QDialog
@@ -263,8 +302,6 @@ bool DirectoryFilter::openConfigDialog(QWidget *parent, bool &needsRefresh)
&DirectoryFilter::updateOptionButtons,
Qt::DirectConnection);
m_dialog->directoryList->clear();
- // Note: assuming we only change m_directories in the Gui thread,
- // we don't need to protect it here with mutex
m_dialog->directoryList->addItems(Utils::transform(m_directories, &FilePath::toString));
m_dialog->nameLabel->setVisible(m_isCustomFilter);
m_dialog->nameEdit->setVisible(m_isCustomFilter);
@@ -276,14 +313,10 @@ bool DirectoryFilter::openConfigDialog(QWidget *parent, bool &needsRefresh)
m_dialog->filePatternLabel->setText(Utils::msgFilePatternLabel());
m_dialog->filePatternLabel->setBuddy(m_dialog->filePattern);
m_dialog->filePattern->setToolTip(Utils::msgFilePatternToolTip());
- // Note: assuming we only change m_filters in the Gui thread,
- // we don't need to protect it here with mutex
m_dialog->filePattern->setText(Utils::transform(m_filters, &QDir::toNativeSeparators).join(','));
m_dialog->exclusionPatternLabel->setText(Utils::msgExclusionPatternLabel());
m_dialog->exclusionPatternLabel->setBuddy(m_dialog->exclusionPattern);
- m_dialog->exclusionPattern->setToolTip(Utils::msgFilePatternToolTip());
- // Note: assuming we only change m_exclusionFilters in the Gui thread,
- // we don't need to protect it here with mutex
+ m_dialog->exclusionPattern->setToolTip(Utils::msgFilePatternToolTip(InclusionType::Excluded));
m_dialog->exclusionPattern->setText(
Utils::transform(m_exclusionFilters, &QDir::toNativeSeparators).join(','));
m_dialog->shortcutEdit->setText(shortcutString());
@@ -291,7 +324,6 @@ bool DirectoryFilter::openConfigDialog(QWidget *parent, bool &needsRefresh)
updateOptionButtons();
dialog.adjustSize();
if (dialog.exec() == QDialog::Accepted) {
- QMutexLocker locker(&m_lock);
bool directoriesChanged = false;
const FilePaths oldDirectories = m_directories;
const QStringList oldFilters = m_filters;
@@ -351,55 +383,6 @@ void DirectoryFilter::updateOptionButtons()
m_dialog->removeButton->setEnabled(haveSelectedItem);
}
-void DirectoryFilter::updateFileIterator()
-{
- QMutexLocker locker(&m_lock);
- setFileIterator(new BaseFileFilter::ListIterator(m_files));
-}
-
-void DirectoryFilter::refresh(QFutureInterface<void> &future)
-{
- FilePaths directories;
- QStringList filters, exclusionFilters;
- {
- QMutexLocker locker(&m_lock);
- if (m_directories.isEmpty()) {
- m_files.clear();
- QMetaObject::invokeMethod(this, &DirectoryFilter::updateFileIterator,
- Qt::QueuedConnection);
- future.setProgressRange(0, 1);
- future.setProgressValueAndText(1, Tr::tr("%1 filter update: 0 files").arg(displayName()));
- return;
- }
- directories = m_directories;
- filters = m_filters;
- exclusionFilters = m_exclusionFilters;
- }
- Utils::SubDirFileIterator subDirIterator(directories, filters, exclusionFilters);
- future.setProgressRange(0, subDirIterator.maxProgress());
- Utils::FilePaths filesFound;
- auto end = subDirIterator.end();
- for (auto it = subDirIterator.begin(); it != end; ++it) {
- if (future.isCanceled())
- break;
- filesFound << (*it).filePath;
- if (future.isProgressUpdateNeeded()
- || future.progressValue() == 0 /*workaround for regression in Qt*/) {
- future.setProgressValueAndText(subDirIterator.currentProgress(),
- Tr::tr("%1 filter update: %n files", nullptr, filesFound.size()).arg(displayName()));
- }
- }
-
- if (!future.isCanceled()) {
- QMutexLocker locker(&m_lock);
- m_files = filesFound;
- QMetaObject::invokeMethod(this, &DirectoryFilter::updateFileIterator, Qt::QueuedConnection);
- future.setProgressValue(subDirIterator.maxProgress());
- } else {
- future.setProgressValueAndText(subDirIterator.currentProgress(), Tr::tr("%1 filter update: canceled").arg(displayName()));
- }
-}
-
void DirectoryFilter::setIsCustomFilter(bool value)
{
m_isCustomFilter = value;
@@ -409,10 +392,7 @@ void DirectoryFilter::setDirectories(const FilePaths &directories)
{
if (directories == m_directories)
return;
- {
- QMutexLocker locker(&m_lock);
- m_directories = directories;
- }
+ m_directories = directories;
Internal::Locator::instance()->refresh({this});
}
@@ -428,20 +408,13 @@ void DirectoryFilter::removeDirectory(const FilePath &directory)
setDirectories(directories);
}
-FilePaths DirectoryFilter::directories() const
-{
- return m_directories;
-}
-
void DirectoryFilter::setFilters(const QStringList &filters)
{
- QMutexLocker locker(&m_lock);
m_filters = filters;
}
void DirectoryFilter::setExclusionFilters(const QStringList &exclusionFilters)
{
- QMutexLocker locker(&m_lock);
m_exclusionFilters = exclusionFilters;
}
diff --git a/src/plugins/coreplugin/locator/directoryfilter.h b/src/plugins/coreplugin/locator/directoryfilter.h
index 7fb88ce112..485377d2a1 100644
--- a/src/plugins/coreplugin/locator/directoryfilter.h
+++ b/src/plugins/coreplugin/locator/directoryfilter.h
@@ -3,45 +3,36 @@
#pragma once
-#include "basefilefilter.h"
+#include "ilocatorfilter.h"
#include <coreplugin/core_global.h>
-#include <QString>
-#include <QByteArray>
-#include <QFutureInterface>
-#include <QMutex>
-
namespace Core {
-class CORE_EXPORT DirectoryFilter : public BaseFileFilter
+class CORE_EXPORT DirectoryFilter : public ILocatorFilter
{
- Q_OBJECT
-
public:
DirectoryFilter(Utils::Id id);
void restoreState(const QByteArray &state) override;
bool openConfigDialog(QWidget *parent, bool &needsRefresh) override;
- void refresh(QFutureInterface<void> &future) override;
+protected:
void setIsCustomFilter(bool value);
- void setDirectories(const Utils::FilePaths &directories);
void addDirectory(const Utils::FilePath &directory);
void removeDirectory(const Utils::FilePath &directory);
- Utils::FilePaths directories() const;
void setFilters(const QStringList &filters);
void setExclusionFilters(const QStringList &exclusionFilters);
-protected:
void saveState(QJsonObject &object) const override;
void restoreState(const QJsonObject &object) override;
private:
+ LocatorMatcherTasks matchers() final { return {m_cache.matcher()}; }
+ void setDirectories(const Utils::FilePaths &directories);
void handleAddDirectory();
void handleEditDirectory();
void handleRemoveDirectory();
void updateOptionButtons();
- void updateFileIterator();
Utils::FilePaths m_directories;
QStringList m_filters;
@@ -49,9 +40,8 @@ private:
// Our config dialog, uses in addDirectory and editDirectory
// to give their dialogs the right parent
class DirectoryFilterOptions *m_dialog = nullptr;
- mutable QMutex m_lock;
- Utils::FilePaths m_files;
bool m_isCustomFilter = true;
+ LocatorFileCache m_cache;
};
} // namespace Core
diff --git a/src/plugins/coreplugin/locator/executefilter.cpp b/src/plugins/coreplugin/locator/executefilter.cpp
index 6be23fff3f..2a405c369e 100644
--- a/src/plugins/coreplugin/locator/executefilter.cpp
+++ b/src/plugins/coreplugin/locator/executefilter.cpp
@@ -11,8 +11,8 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/macroexpander.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QJsonArray>
#include <QJsonObject>
@@ -31,7 +31,6 @@ ExecuteFilter::ExecuteFilter()
"environment variable if needed. Note that the command is run directly, not in a shell."));
setDefaultShortcutString("!");
setPriority(High);
- setDefaultIncludedByDefault(false);
}
ExecuteFilter::~ExecuteFilter()
@@ -39,77 +38,78 @@ ExecuteFilter::~ExecuteFilter()
removeProcess();
}
-QList<LocatorFilterEntry> ExecuteFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry)
+LocatorMatcherTasks ExecuteFilter::matchers()
{
- QList<LocatorFilterEntry> value;
- if (!entry.isEmpty()) // avoid empty entry
- value.append(LocatorFilterEntry(this, entry));
- QList<LocatorFilterEntry> others;
- const Qt::CaseSensitivity entryCaseSensitivity = caseSensitivity(entry);
- for (const QString &cmd : std::as_const(m_commandHistory)) {
- if (future.isCanceled())
- break;
- if (cmd == entry) // avoid repeated entry
- continue;
- LocatorFilterEntry filterEntry(this, cmd);
- const int index = cmd.indexOf(entry, 0, entryCaseSensitivity);
- if (index >= 0) {
- filterEntry.highlightInfo = {index, int(entry.length())};
- value.append(filterEntry);
- } else {
- others.append(filterEntry);
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [=] {
+ const QString input = storage->input();
+ LocatorFilterEntries entries;
+ if (!input.isEmpty()) { // avoid empty entry
+ LocatorFilterEntry entry;
+ entry.displayName = input;
+ entry.acceptor = [this, input] { acceptCommand(input); return AcceptResult(); };
+ entries.append(entry);
}
- }
- value.append(others);
- return value;
+ LocatorFilterEntries others;
+ const Qt::CaseSensitivity entryCaseSensitivity = caseSensitivity(input);
+ for (const QString &cmd : std::as_const(m_commandHistory)) {
+ if (cmd == input) // avoid repeated entry
+ continue;
+ LocatorFilterEntry entry;
+ entry.displayName = cmd;
+ entry.acceptor = [this, cmd] { acceptCommand(cmd); return AcceptResult(); };
+ const int index = cmd.indexOf(input, 0, entryCaseSensitivity);
+ if (index >= 0) {
+ entry.highlightInfo = {index, int(input.length())};
+ entries.append(entry);
+ } else {
+ others.append(entry);
+ }
+ }
+ storage->reportOutput(entries + others);
+ };
+ return {{Sync(onSetup), storage}};
}
-void ExecuteFilter::accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const
+void ExecuteFilter::acceptCommand(const QString &cmd)
{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- auto p = const_cast<ExecuteFilter *>(this);
-
- const QString value = selection.displayName.trimmed();
-
- const int index = m_commandHistory.indexOf(value);
+ const QString displayName = cmd.trimmed();
+ const int index = m_commandHistory.indexOf(displayName);
if (index != -1 && index != 0)
- p->m_commandHistory.removeAt(index);
+ m_commandHistory.removeAt(index);
if (index != 0)
- p->m_commandHistory.prepend(value);
+ m_commandHistory.prepend(displayName);
static const int maxHistory = 100;
- while (p->m_commandHistory.size() > maxHistory)
- p->m_commandHistory.removeLast();
+ while (m_commandHistory.size() > maxHistory)
+ m_commandHistory.removeLast();
bool found;
- QString workingDirectory = Utils::globalMacroExpander()->value("CurrentDocument:Path", &found);
+ QString workingDirectory = globalMacroExpander()->value("CurrentDocument:Path", &found);
if (!found || workingDirectory.isEmpty())
- workingDirectory = Utils::globalMacroExpander()->value("CurrentDocument:Project:Path", &found);
-
- ExecuteData d;
- d.command = CommandLine::fromUserInput(value, Utils::globalMacroExpander());
- d.workingDirectory = FilePath::fromString(workingDirectory);
+ workingDirectory = globalMacroExpander()->value("CurrentDocument:Project:Path", &found);
+ const ExecuteData data{CommandLine::fromUserInput(displayName, globalMacroExpander()),
+ FilePath::fromString(workingDirectory)};
if (m_process) {
- const QString info(Tr::tr("Previous command is still running (\"%1\").\nDo you want to kill it?")
- .arg(p->headCommand()));
- int r = QMessageBox::question(ICore::dialogParent(), Tr::tr("Kill Previous Process?"), info,
- QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
- QMessageBox::Yes);
- if (r == QMessageBox::Cancel)
+ const QString info(Tr::tr("Previous command is still running (\"%1\").\n"
+ "Do you want to kill it?").arg(headCommand()));
+ const auto result = QMessageBox::question(ICore::dialogParent(),
+ Tr::tr("Kill Previous Process?"), info,
+ QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
+ QMessageBox::Yes);
+ if (result == QMessageBox::Cancel)
return;
- if (r == QMessageBox::No) {
- p->m_taskQueue.enqueue(d);
+ if (result == QMessageBox::No) {
+ m_taskQueue.append(data);
return;
}
- p->removeProcess();
+ removeProcess();
}
-
- p->m_taskQueue.enqueue(d);
- p->runHeadCommand();
+ m_taskQueue.append(data);
+ runHeadCommand();
}
void ExecuteFilter::done()
@@ -122,7 +122,7 @@ void ExecuteFilter::done()
runHeadCommand();
}
-void ExecuteFilter::readStandardOutput()
+void ExecuteFilter::readStdOutput()
{
QTC_ASSERT(m_process, return);
const QByteArray data = m_process->readAllRawStandardOutput();
@@ -130,7 +130,7 @@ void ExecuteFilter::readStandardOutput()
QTextCodec::codecForLocale()->toUnicode(data.constData(), data.size(), &m_stdoutState));
}
-void ExecuteFilter::readStandardError()
+void ExecuteFilter::readStdError()
{
QTC_ASSERT(m_process, return);
const QByteArray data = m_process->readAllRawStandardError();
@@ -141,11 +141,11 @@ void ExecuteFilter::readStandardError()
void ExecuteFilter::runHeadCommand()
{
if (!m_taskQueue.isEmpty()) {
- const ExecuteData &d = m_taskQueue.head();
+ const ExecuteData &d = m_taskQueue.first();
if (d.command.executable().isEmpty()) {
- MessageManager::writeDisrupting(
- Tr::tr("Could not find executable for \"%1\".").arg(d.command.executable().toUserOutput()));
- m_taskQueue.dequeue();
+ MessageManager::writeDisrupting(Tr::tr("Could not find executable for \"%1\".")
+ .arg(d.command.executable().toUserOutput()));
+ m_taskQueue.removeFirst();
runHeadCommand();
return;
}
@@ -163,11 +163,11 @@ void ExecuteFilter::createProcess()
if (m_process)
return;
- m_process = new Utils::QtcProcess;
- m_process->setEnvironment(Utils::Environment::systemEnvironment());
- connect(m_process, &QtcProcess::done, this, &ExecuteFilter::done);
- connect(m_process, &QtcProcess::readyReadStandardOutput, this, &ExecuteFilter::readStandardOutput);
- connect(m_process, &QtcProcess::readyReadStandardError, this, &ExecuteFilter::readStandardError);
+ m_process = new Process;
+ m_process->setEnvironment(Environment::systemEnvironment());
+ connect(m_process, &Process::done, this, &ExecuteFilter::done);
+ connect(m_process, &Process::readyReadStandardOutput, this, &ExecuteFilter::readStdOutput);
+ connect(m_process, &Process::readyReadStandardError, this, &ExecuteFilter::readStdError);
}
void ExecuteFilter::removeProcess()
@@ -175,7 +175,7 @@ void ExecuteFilter::removeProcess()
if (!m_process)
return;
- m_taskQueue.dequeue();
+ m_taskQueue.removeFirst();
m_process->deleteLater();
m_process = nullptr;
}
@@ -198,8 +198,8 @@ QString ExecuteFilter::headCommand() const
{
if (m_taskQueue.isEmpty())
return QString();
- const ExecuteData &data = m_taskQueue.head();
+ const ExecuteData &data = m_taskQueue.first();
return data.command.toUserOutput();
}
-} // Core::Internal
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/executefilter.h b/src/plugins/coreplugin/locator/executefilter.h
index 0d499e8860..2e51d320d3 100644
--- a/src/plugins/coreplugin/locator/executefilter.h
+++ b/src/plugins/coreplugin/locator/executefilter.h
@@ -7,19 +7,15 @@
#include <utils/commandline.h>
-#include <QQueue>
#include <QStringList>
#include <QTextCodec>
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
-namespace Core {
-namespace Internal {
+namespace Core::Internal {
class ExecuteFilter : public Core::ILocatorFilter
{
- Q_OBJECT
-
struct ExecuteData
{
Utils::CommandLine command;
@@ -29,15 +25,13 @@ class ExecuteFilter : public Core::ILocatorFilter
public:
ExecuteFilter();
~ExecuteFilter() override;
- QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
private:
+ LocatorMatcherTasks matchers() final;
+ void acceptCommand(const QString &cmd);
void done();
- void readStandardOutput();
- void readStandardError();
+ void readStdOutput();
+ void readStdError();
void runHeadCommand();
void createProcess();
@@ -48,12 +42,11 @@ private:
QString headCommand() const;
- QQueue<ExecuteData> m_taskQueue;
+ QList<ExecuteData> m_taskQueue;
QStringList m_commandHistory;
- Utils::QtcProcess *m_process = nullptr;
+ Utils::Process *m_process = nullptr;
QTextCodec::ConverterState m_stdoutState;
QTextCodec::ConverterState m_stderrState;
};
-} // namespace Internal
-} // namespace Core
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/externaltoolsfilter.cpp b/src/plugins/coreplugin/locator/externaltoolsfilter.cpp
index 96ce58e20b..36fc78cba9 100644
--- a/src/plugins/coreplugin/locator/externaltoolsfilter.cpp
+++ b/src/plugins/coreplugin/locator/externaltoolsfilter.cpp
@@ -4,6 +4,7 @@
#include "externaltoolsfilter.h"
#include "../coreconstants.h"
+#include "../coreplugin.h"
#include "../coreplugintr.h"
#include "../externaltool.h"
#include "../externaltoolmanager.h"
@@ -24,64 +25,62 @@ ExternalToolsFilter::ExternalToolsFilter()
setPriority(Medium);
}
-QList<LocatorFilterEntry> ExternalToolsFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &,
- const QString &)
+LocatorMatcherTasks ExternalToolsFilter::matchers()
{
- return m_results;
-}
+ using namespace Tasking;
-void ExternalToolsFilter::accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const
-{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
+ TreeStorage<LocatorStorage> storage;
- if (!selection.internalData.isValid()) {
- ICore::showOptionsDialog(Constants::SETTINGS_ID_TOOLS);
- return;
- }
+ const auto onSetup = [storage] {
+ const QString input = storage->input();
- auto tool = selection.internalData.value<ExternalTool *>();
- QTC_ASSERT(tool, return);
+ LocatorFilterEntries bestEntries;
+ LocatorFilterEntries betterEntries;
+ LocatorFilterEntries goodEntries;
+ const Qt::CaseSensitivity entryCaseSensitivity = caseSensitivity(input);
+ const QMap<QString, ExternalTool *> externalToolsById = ExternalToolManager::toolsById();
+ for (ExternalTool *tool : externalToolsById) {
+ int index = tool->displayName().indexOf(input, 0, entryCaseSensitivity);
+ LocatorFilterEntry::HighlightInfo::DataType hDataType = LocatorFilterEntry::HighlightInfo::DisplayName;
+ if (index < 0) {
+ index = tool->description().indexOf(input, 0, entryCaseSensitivity);
+ hDataType = LocatorFilterEntry::HighlightInfo::ExtraInfo;
+ }
- auto runner = new ExternalToolRunner(tool);
- if (runner->hasError())
- MessageManager::writeFlashing(runner->errorString());
-}
+ if (index >= 0) {
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = tool->displayName();
+ filterEntry.acceptor = [tool] {
+ auto runner = new ExternalToolRunner(tool);
+ if (runner->hasError())
+ MessageManager::writeFlashing(runner->errorString());
+ return AcceptResult();
+ };
+ filterEntry.extraInfo = tool->description();
+ filterEntry.highlightInfo = LocatorFilterEntry::HighlightInfo(index, input.length(), hDataType);
-void ExternalToolsFilter::prepareSearch(const QString &entry)
-{
- QList<LocatorFilterEntry> bestEntries;
- QList<LocatorFilterEntry> betterEntries;
- QList<LocatorFilterEntry> goodEntries;
- const Qt::CaseSensitivity entryCaseSensitivity = caseSensitivity(entry);
- const QMap<QString, ExternalTool *> externalToolsById = ExternalToolManager::toolsById();
- for (ExternalTool *tool : externalToolsById) {
- int index = tool->displayName().indexOf(entry, 0, entryCaseSensitivity);
- LocatorFilterEntry::HighlightInfo::DataType hDataType = LocatorFilterEntry::HighlightInfo::DisplayName;
- if (index < 0) {
- index = tool->description().indexOf(entry, 0, entryCaseSensitivity);
- hDataType = LocatorFilterEntry::HighlightInfo::ExtraInfo;
+ if (filterEntry.displayName.startsWith(input, entryCaseSensitivity))
+ bestEntries.append(filterEntry);
+ else if (filterEntry.displayName.contains(input, entryCaseSensitivity))
+ betterEntries.append(filterEntry);
+ else
+ goodEntries.append(filterEntry);
+ }
}
+ LocatorFilterEntry configEntry;
+ configEntry.displayName = "Configure External Tool...";
+ configEntry.extraInfo = "Opens External Tool settings";
+ configEntry.acceptor = [] {
+ QMetaObject::invokeMethod(CorePlugin::instance(), [] {
+ ICore::showOptionsDialog(Constants::SETTINGS_ID_TOOLS);
+ }, Qt::QueuedConnection);
+ return AcceptResult();
+ };
- if (index >= 0) {
- LocatorFilterEntry filterEntry(this, tool->displayName(), QVariant::fromValue(tool));
- filterEntry.extraInfo = tool->description();
- filterEntry.highlightInfo = LocatorFilterEntry::HighlightInfo(index, entry.length(), hDataType);
-
- if (filterEntry.displayName.startsWith(entry, entryCaseSensitivity))
- bestEntries.append(filterEntry);
- else if (filterEntry.displayName.contains(entry, entryCaseSensitivity))
- betterEntries.append(filterEntry);
- else
- goodEntries.append(filterEntry);
- }
- }
- LocatorFilterEntry configEntry(this, "Configure External Tool...", {});
- configEntry.extraInfo = "Opens External Tool settings";
- m_results = {};
- m_results << bestEntries << betterEntries << goodEntries << configEntry;
+ storage->reportOutput(bestEntries + betterEntries + goodEntries
+ + LocatorFilterEntries{configEntry});
+ };
+ return {{Sync(onSetup), storage}};
}
} // Core::Internal
diff --git a/src/plugins/coreplugin/locator/externaltoolsfilter.h b/src/plugins/coreplugin/locator/externaltoolsfilter.h
index 74d5214aa3..bc8fca6e70 100644
--- a/src/plugins/coreplugin/locator/externaltoolsfilter.h
+++ b/src/plugins/coreplugin/locator/externaltoolsfilter.h
@@ -5,24 +5,15 @@
#include "ilocatorfilter.h"
-namespace Core {
-namespace Internal {
+namespace Core::Internal {
class ExternalToolsFilter : public ILocatorFilter
{
- Q_OBJECT
public:
ExternalToolsFilter();
- QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
- void prepareSearch(const QString &entry) override;
-
private:
- QList<LocatorFilterEntry> m_results;
+ LocatorMatcherTasks matchers() final;
};
-} // namespace Internal
-} // namespace Core
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/filesystemfilter.cpp b/src/plugins/coreplugin/locator/filesystemfilter.cpp
index 17c54c748d..7c6ac13abd 100644
--- a/src/plugins/coreplugin/locator/filesystemfilter.cpp
+++ b/src/plugins/coreplugin/locator/filesystemfilter.cpp
@@ -3,19 +3,25 @@
#include "filesystemfilter.h"
-#include "basefilefilter.h"
#include "../coreplugintr.h"
#include "../documentmanager.h"
#include "../editormanager/editormanager.h"
#include "../icore.h"
#include "../vcsmanager.h"
+#include "locatormanager.h"
+#include <extensionsystem/pluginmanager.h>
+
+#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/checkablemessagebox.h>
#include <utils/environment.h>
#include <utils/filepath.h>
+#include <utils/fsengine/fileiconprovider.h>
#include <utils/layoutbuilder.h>
#include <utils/link.h>
+#include <QApplication>
#include <QCheckBox>
#include <QDialog>
#include <QDialogButtonBox>
@@ -25,26 +31,84 @@
#include <QLineEdit>
#include <QPushButton>
#include <QRegularExpression>
+#include <QStyle>
using namespace Utils;
-namespace Core {
-namespace Internal {
+namespace Core::Internal {
+
+Q_GLOBAL_STATIC(QIcon, sDeviceRootIcon);
+
+static const char kAlwaysCreate[] = "Locator/FileSystemFilter/AlwaysCreate";
-ILocatorFilter::MatchLevel FileSystemFilter::matchLevelFor(const QRegularExpressionMatch &match,
- const QString &matchText)
+static ILocatorFilter::MatchLevel matchLevelFor(const QRegularExpressionMatch &match,
+ const QString &matchText)
{
const int consecutivePos = match.capturedStart(1);
if (consecutivePos == 0)
- return MatchLevel::Best;
+ return ILocatorFilter::MatchLevel::Best;
if (consecutivePos > 0) {
const QChar prevChar = matchText.at(consecutivePos - 1);
if (prevChar == '_' || prevChar == '.')
- return MatchLevel::Better;
+ return ILocatorFilter::MatchLevel::Better;
}
if (match.capturedStart() == 0)
- return MatchLevel::Good;
- return MatchLevel::Normal;
+ return ILocatorFilter::MatchLevel::Good;
+ return ILocatorFilter::MatchLevel::Normal;
+}
+
+static bool askForCreating(const QString &title, const FilePath &filePath)
+{
+ QMessageBox::StandardButton selected
+ = CheckableMessageBox::question(ICore::dialogParent(),
+ title,
+ Tr::tr("Create \"%1\"?").arg(filePath.shortNativePath()),
+ QString(kAlwaysCreate),
+ QMessageBox::Yes | QMessageBox::Cancel,
+ QMessageBox::Cancel,
+ QMessageBox::Yes,
+ {{QMessageBox::Yes, Tr::tr("Create")}},
+ Tr::tr("Always create"));
+ return selected == QMessageBox::Yes;
+}
+
+static void createAndOpenFile(const FilePath &filePath)
+{
+ if (!filePath.exists()) {
+ if (askForCreating(Tr::tr("Create File"), filePath)) {
+ QFile file(filePath.toFSPathString());
+ file.open(QFile::WriteOnly);
+ file.close();
+ VcsManager::promptToAdd(filePath.absolutePath(), {filePath});
+ }
+ }
+ if (filePath.exists())
+ EditorManager::openEditor(filePath);
+}
+
+static bool createDirectory(const FilePath &filePath)
+{
+ if (!filePath.exists()) {
+ if (askForCreating(Tr::tr("Create Directory"), filePath))
+ filePath.createDir();
+ }
+ if (filePath.exists())
+ return true;
+ return false;
+}
+
+static FilePaths deviceRoots()
+{
+ const QString rootPath = FilePath::specialRootPath();
+ const QStringList roots = QDir(rootPath).entryList();
+ FilePaths devices;
+ for (const QString &root : roots) {
+ const QString prefix = rootPath + '/' + root;
+ devices += Utils::transform(QDir(prefix).entryList(), [prefix](const QString &s) {
+ return FilePath::fromString(prefix + '/' + s);
+ });
+ }
+ return devices;
}
FileSystemFilter::FileSystemFilter()
@@ -55,146 +119,199 @@ FileSystemFilter::FileSystemFilter()
"path. \"~\" refers to your home directory. You have the option to create a "
"file if it does not exist yet."));
setDefaultShortcutString("f");
- setDefaultIncludedByDefault(false);
+ *sDeviceRootIcon = qApp->style()->standardIcon(QStyle::SP_DriveHDIcon);
}
-void FileSystemFilter::prepareSearch(const QString &entry)
+static void matches(QPromise<void> &promise, const LocatorStorage &storage,
+ const QString &shortcutString, const FilePath &currentDocumentDir,
+ bool includeHidden)
{
- Q_UNUSED(entry)
- m_currentDocumentDirectory = DocumentManager::fileDialogInitialDirectory().toString();
- m_currentIncludeHidden = m_includeHidden;
-}
-
-QList<LocatorFilterEntry> FileSystemFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry)
-{
- QList<LocatorFilterEntry> entries[int(MatchLevel::Count)];
-
- Environment env = Environment::systemEnvironment();
- const QString expandedEntry = env.expandVariables(entry);
-
- const QFileInfo entryInfo(expandedEntry);
- const QString entryFileName = entryInfo.fileName();
- QString directory = entryInfo.path();
- if (entryInfo.isRelative()) {
- if (entryInfo.filePath().startsWith("~/"))
- directory.replace(0, 1, QDir::homePath());
- else if (!m_currentDocumentDirectory.isEmpty())
- directory.prepend(m_currentDocumentDirectory + "/");
- }
- const QDir dirInfo(directory);
- QDir::Filters dirFilter = QDir::Dirs|QDir::Drives|QDir::NoDot|QDir::NoDotDot;
+ const QString input = storage.input();
+ LocatorFilterEntries entries[int(ILocatorFilter::MatchLevel::Count)];
+
+ const Environment env = Environment::systemEnvironment();
+ const QString expandedEntry = env.expandVariables(input);
+ const auto expandedEntryPath = FilePath::fromUserInput(expandedEntry);
+ const FilePath absoluteEntryPath = currentDocumentDir.isEmpty()
+ ? expandedEntryPath
+ : currentDocumentDir.resolvePath(expandedEntryPath);
+ // The case of e.g. "ssh://", "ssh://*p", etc
+ const bool isPartOfDeviceRoot = expandedEntryPath.needsDevice()
+ && expandedEntryPath.path().isEmpty();
+
+ // Consider the entered path a directory if it ends with slash/backslash.
+ // If it is a dir but doesn't end with a backslash, we want to still show all (other) matching
+ // items from the same parent directory.
+ // Unfortunately fromUserInput removes slash/backslash at the end, so manually check the original.
+ const bool isDir = expandedEntry.isEmpty() || expandedEntry.endsWith('/')
+ || expandedEntry.endsWith('\\');
+ const FilePath directory = isDir ? absoluteEntryPath : absoluteEntryPath.parentDir();
+ const QString entryFileName = isDir ? QString() : absoluteEntryPath.fileName();
+
+ QDir::Filters dirFilter = QDir::Dirs | QDir::Drives | QDir::NoDot | QDir::NoDotDot;
QDir::Filters fileFilter = QDir::Files;
- if (m_currentIncludeHidden) {
+ if (includeHidden) {
dirFilter |= QDir::Hidden;
fileFilter |= QDir::Hidden;
}
// use only 'name' for case sensitivity decision, because we need to make the path
// match the case on the file system for case-sensitive file systems
- const Qt::CaseSensitivity caseSensitivity_ = caseSensitivity(entryFileName);
- const QStringList dirs = QStringList("..")
- + dirInfo.entryList(dirFilter, QDir::Name|QDir::IgnoreCase|QDir::LocaleAware);
- const QStringList files = dirInfo.entryList(fileFilter,
- QDir::Name|QDir::IgnoreCase|QDir::LocaleAware);
-
- QRegularExpression regExp = createRegExp(entryFileName, caseSensitivity_);
- if (!regExp.isValid())
- return {};
-
- for (const QString &dir : dirs) {
- if (future.isCanceled())
- break;
-
- const QRegularExpressionMatch match = regExp.match(dir);
- if (match.hasMatch()) {
- const MatchLevel level = matchLevelFor(match, dir);
- const QString fullPath = dirInfo.filePath(dir);
- LocatorFilterEntry filterEntry(this, dir);
- filterEntry.filePath = FilePath::fromString(fullPath);
- filterEntry.highlightInfo = highlightInfo(match);
-
- entries[int(level)].append(filterEntry);
+ const Qt::CaseSensitivity caseSensitivity = ILocatorFilter::caseSensitivity(entryFileName);
+ const FilePaths dirs = isPartOfDeviceRoot
+ ? FilePaths()
+ : FilePaths({directory / ".."})
+ + directory.dirEntries({{}, dirFilter},
+ QDir::Name | QDir::IgnoreCase
+ | QDir::LocaleAware);
+ const FilePaths files = isPartOfDeviceRoot ? FilePaths()
+ : directory.dirEntries({{}, fileFilter},
+ QDir::Name | QDir::IgnoreCase
+ | QDir::LocaleAware);
+
+ // directories
+ QRegularExpression regExp = ILocatorFilter::createRegExp(entryFileName, caseSensitivity);
+ if (regExp.isValid()) {
+ for (const FilePath &dir : dirs) {
+ if (promise.isCanceled())
+ return;
+
+ const QString dirString = dir.relativeChildPath(directory).nativePath();
+ const QRegularExpressionMatch match = regExp.match(dirString);
+ if (match.hasMatch()) {
+ const ILocatorFilter::MatchLevel level = matchLevelFor(match, dirString);
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = dirString;
+ filterEntry.acceptor = [shortcutString, dir] {
+ const QString value
+ = shortcutString + ' '
+ + dir.absoluteFilePath().cleanPath().pathAppended("/").toUserOutput();
+ return AcceptResult{value, int(value.length())};
+ };
+ filterEntry.filePath = dir;
+ filterEntry.highlightInfo = ILocatorFilter::highlightInfo(match);
+
+ entries[int(level)].append(filterEntry);
+ }
}
}
// file names can match with +linenumber or :linenumber
const Link link = Link::fromString(entryFileName, true);
- regExp = createRegExp(link.targetFilePath.toString(), caseSensitivity_);
- if (!regExp.isValid())
- return {};
- for (const QString &file : files) {
- if (future.isCanceled())
- break;
-
- const QRegularExpressionMatch match = regExp.match(file);
- if (match.hasMatch()) {
- const MatchLevel level = matchLevelFor(match, file);
- const QString fullPath = dirInfo.filePath(file);
- LocatorFilterEntry filterEntry(this, file);
- filterEntry.filePath = FilePath::fromString(fullPath);
- filterEntry.highlightInfo = highlightInfo(match);
- filterEntry.linkForEditor = Link(filterEntry.filePath, link.targetLine,
- link.targetColumn);
- entries[int(level)].append(filterEntry);
+ regExp = ILocatorFilter::createRegExp(link.targetFilePath.toString(), caseSensitivity);
+ if (regExp.isValid()) {
+ for (const FilePath &file : files) {
+ if (promise.isCanceled())
+ return;
+
+ const QString fileString = file.relativeChildPath(directory).nativePath();
+ const QRegularExpressionMatch match = regExp.match(fileString);
+ if (match.hasMatch()) {
+ const ILocatorFilter::MatchLevel level = matchLevelFor(match, fileString);
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = fileString;
+ filterEntry.filePath = file;
+ filterEntry.highlightInfo = ILocatorFilter::highlightInfo(match);
+ filterEntry.linkForEditor = Link(filterEntry.filePath,
+ link.targetLine,
+ link.targetColumn);
+ entries[int(level)].append(filterEntry);
+ }
+ }
+ }
+ // device roots
+ // check against full search text
+ regExp = ILocatorFilter::createRegExp(expandedEntryPath.toUserOutput(), caseSensitivity);
+ if (regExp.isValid()) {
+ const FilePaths roots = deviceRoots();
+ for (const FilePath &root : roots) {
+ if (promise.isCanceled())
+ return;
+
+ const QString displayString = root.toUserOutput();
+ const QRegularExpressionMatch match = regExp.match(displayString);
+ if (match.hasMatch()) {
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = displayString;
+ filterEntry.acceptor = [shortcutString, root] {
+ const QString value
+ = shortcutString + ' '
+ + root.absoluteFilePath().cleanPath().pathAppended("/").toUserOutput();
+ return AcceptResult{value, int(value.length())};
+ };
+ filterEntry.filePath = root;
+ filterEntry.displayIcon = *sDeviceRootIcon;
+ filterEntry.highlightInfo = ILocatorFilter::highlightInfo(match);
+
+ entries[int(ILocatorFilter::MatchLevel::Normal)].append(filterEntry);
+ }
}
}
// "create and open" functionality
- const QString fullFilePath = dirInfo.filePath(entryFileName);
+ const FilePath fullFilePath = directory / entryFileName;
const bool containsWildcard = expandedEntry.contains('?') || expandedEntry.contains('*');
- if (!containsWildcard && !QFileInfo::exists(fullFilePath) && dirInfo.exists()) {
- LocatorFilterEntry createAndOpen(this, Tr::tr("Create and Open \"%1\"").arg(expandedEntry));
- createAndOpen.filePath = FilePath::fromString(fullFilePath);
- createAndOpen.extraInfo = FilePath::fromString(dirInfo.absolutePath()).shortNativePath();
- entries[int(MatchLevel::Normal)].append(createAndOpen);
+ if (!containsWildcard && !fullFilePath.exists() && directory.exists()) {
+ // create and open file
+ {
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = Tr::tr("Create and Open File \"%1\"").arg(expandedEntry);
+ filterEntry.displayIcon = Utils::FileIconProvider::icon(QFileIconProvider::File);
+ filterEntry.acceptor = [fullFilePath] {
+ QMetaObject::invokeMethod(
+ EditorManager::instance(),
+ [fullFilePath] { createAndOpenFile(fullFilePath); },
+ Qt::QueuedConnection);
+ return AcceptResult();
+ };
+ filterEntry.filePath = fullFilePath;
+ filterEntry.extraInfo = directory.absoluteFilePath().shortNativePath();
+ entries[int(ILocatorFilter::MatchLevel::Normal)].append(filterEntry);
+ }
+
+ // create directory
+ {
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = Tr::tr("Create Directory \"%1\"").arg(expandedEntry);
+ filterEntry.displayIcon = Utils::FileIconProvider::icon(QFileIconProvider::Folder);
+ filterEntry.acceptor = [fullFilePath, shortcutString] {
+ QMetaObject::invokeMethod(
+ EditorManager::instance(),
+ [fullFilePath, shortcutString] {
+ if (createDirectory(fullFilePath)) {
+ const QString value = shortcutString + ' '
+ + fullFilePath.absoluteFilePath()
+ .cleanPath()
+ .pathAppended("/")
+ .toUserOutput();
+ LocatorManager::show(value, value.length());
+ }
+ },
+ Qt::QueuedConnection);
+ return AcceptResult();
+ };
+ filterEntry.filePath = fullFilePath;
+ filterEntry.extraInfo = directory.absoluteFilePath().shortNativePath();
+ entries[int(ILocatorFilter::MatchLevel::Normal)].append(filterEntry);
+ }
}
- return std::accumulate(std::begin(entries), std::end(entries), QList<LocatorFilterEntry>());
+ storage.reportOutput(std::accumulate(std::begin(entries), std::end(entries),
+ LocatorFilterEntries()));
}
-const char kAlwaysCreate[] = "Locator/FileSystemFilter/AlwaysCreate";
-
-void FileSystemFilter::accept(const LocatorFilterEntry &selection,
- QString *newText,
- int *selectionStart,
- int *selectionLength) const
+LocatorMatcherTasks FileSystemFilter::matchers()
{
- Q_UNUSED(selectionLength)
- if (selection.filePath.isDir()) {
- const QString value
- = shortcutString() + ' '
- + selection.filePath.absoluteFilePath().cleanPath().pathAppended("/").toUserOutput();
- *newText = value;
- *selectionStart = value.length();
- } else {
- // Don't block locator filter execution with dialog
- QMetaObject::invokeMethod(EditorManager::instance(), [selection] {
- if (!selection.filePath.exists()) {
- if (CheckableMessageBox::shouldAskAgain(ICore::settings(), kAlwaysCreate)) {
- CheckableMessageBox messageBox(ICore::dialogParent());
- messageBox.setWindowTitle(Tr::tr("Create File"));
- messageBox.setIcon(QMessageBox::Question);
- messageBox.setText(Tr::tr("Create \"%1\"?").arg(selection.filePath.shortNativePath()));
- messageBox.setCheckBoxVisible(true);
- messageBox.setCheckBoxText(Tr::tr("Always create"));
- messageBox.setChecked(false);
- messageBox.setStandardButtons(QDialogButtonBox::Cancel);
- QPushButton *createButton = messageBox.addButton(Tr::tr("Create"),
- QDialogButtonBox::AcceptRole);
- messageBox.setDefaultButton(QDialogButtonBox::Cancel);
- messageBox.exec();
- if (messageBox.clickedButton() != createButton)
- return;
- if (messageBox.isChecked())
- CheckableMessageBox::doNotAskAgain(ICore::settings(), kAlwaysCreate);
- }
- QFile file(selection.filePath.toString());
- file.open(QFile::WriteOnly);
- file.close();
- VcsManager::promptToAdd(selection.filePath.absolutePath(), {selection.filePath});
- }
- BaseFileFilter::openEditorAt(selection);
- }, Qt::QueuedConnection);
- }
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [storage, includeHidden = m_includeHidden, shortcut = shortcutString()]
+ (Async<void> &async) {
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(matches, *storage, shortcut,
+ DocumentManager::fileDialogInitialDirectory(), includeHidden);
+ };
+
+ return {{AsyncTask<void>(onSetup), storage}};
}
class FileSystemFilterOptions : public QDialog
@@ -260,13 +377,13 @@ const char kIncludeHiddenKey[] = "includeHidden";
void FileSystemFilter::saveState(QJsonObject &object) const
{
- if (m_includeHidden != kIncludeHiddenDefault)
+ if (m_includeHidden != s_includeHiddenDefault)
object.insert(kIncludeHiddenKey, m_includeHidden);
}
void FileSystemFilter::restoreState(const QJsonObject &object)
{
- m_currentIncludeHidden = object.value(kIncludeHiddenKey).toBool(kIncludeHiddenDefault);
+ m_includeHidden = object.value(kIncludeHiddenKey).toBool(s_includeHiddenDefault);
}
void FileSystemFilter::restoreState(const QByteArray &state)
@@ -290,5 +407,4 @@ void FileSystemFilter::restoreState(const QByteArray &state)
}
}
-} // Internal
-} // Core
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/filesystemfilter.h b/src/plugins/coreplugin/locator/filesystemfilter.h
index 33338d626e..3dfac11b3a 100644
--- a/src/plugins/coreplugin/locator/filesystemfilter.h
+++ b/src/plugins/coreplugin/locator/filesystemfilter.h
@@ -5,40 +5,24 @@
#include "ilocatorfilter.h"
-#include <QByteArray>
-#include <QFutureInterface>
-#include <QList>
-#include <QString>
-
-namespace Core {
-namespace Internal {
+namespace Core::Internal {
class FileSystemFilter : public ILocatorFilter
{
- Q_OBJECT
-
public:
- explicit FileSystemFilter();
- void prepareSearch(const QString &entry) override;
- QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
- void restoreState(const QByteArray &state) override;
- bool openConfigDialog(QWidget *parent, bool &needsRefresh) override;
+ FileSystemFilter();
+ void restoreState(const QByteArray &state) final;
+ bool openConfigDialog(QWidget *parent, bool &needsRefresh) final;
protected:
void saveState(QJsonObject &object) const final;
void restoreState(const QJsonObject &object) final;
private:
- static MatchLevel matchLevelFor(const QRegularExpressionMatch &match, const QString &matchText);
+ LocatorMatcherTasks matchers() final;
- static const bool kIncludeHiddenDefault = true;
- bool m_includeHidden = kIncludeHiddenDefault;
- bool m_currentIncludeHidden = kIncludeHiddenDefault;
- QString m_currentDocumentDirectory;
+ static const bool s_includeHiddenDefault = true;
+ bool m_includeHidden = s_includeHiddenDefault;
};
-} // namespace Internal
-} // namespace Core
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/ilocatorfilter.cpp b/src/plugins/coreplugin/locator/ilocatorfilter.cpp
index 4523c719fb..3a84a0b548 100644
--- a/src/plugins/coreplugin/locator/ilocatorfilter.cpp
+++ b/src/plugins/coreplugin/locator/ilocatorfilter.cpp
@@ -5,19 +5,27 @@
#include "../coreplugintr.h"
+#include <extensionsystem/pluginmanager.h>
+
+#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/fuzzymatcher.h>
#include <QBoxLayout>
#include <QCheckBox>
-#include <QCoreApplication>
#include <QDialog>
#include <QDialogButtonBox>
+#include <QFutureWatcher>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLabel>
#include <QLineEdit>
#include <QRegularExpression>
+#include <QWaitCondition>
+
+#include <unordered_set>
+using namespace Tasking;
using namespace Utils;
/*!
@@ -44,6 +52,509 @@ using namespace Utils;
namespace Core {
+// ResultsDeduplicator squashes upcoming results from various filters and removes
+// duplicated entries. It also reports intermediate results (to be displayed in LocatorWidget).
+//
+// Assuming the results from filters come in this order (numbers are indices to filter results):
+// 2, 4, 3, 1 - the various strategies are possible. The current strategy looks like this:
+// - When 2nd result came - the result is stored
+// - When 4th result came - the result is stored
+// - When 3rd result came - it's being squased to the 2nd result and afterwards the 4th result
+// result is being squashed into the common result list.
+// - When 1st result came - the stored common list is squashed into the 1st result
+// and intermediate results are reported (for 1-4 results).
+// If the filterCount is 4, the deduplicator finishes now.
+// If the filterCount is greater than 4, it waits for the remaining
+// results.
+//
+// TODO: The other possible startegy would be to just store the newly reported data
+// and do the actual deduplication only when new results are reachable from 1st index
+// (i.e. skip the intermediate deduplication).
+class ResultsDeduplicator
+{
+ enum class State {
+ Awaiting, // Waiting in a separate thread for new data, or fetched the last new data and
+ // is currently deduplicating.
+ // This happens when all previous data were squashed in the separate thread but
+ // still some data needs to come (reportOutput wasn't called for all filters,
+ // yet). The expected number of calls to reportOutput equals m_filterCount.
+ NewData, // The new data came and the separate thread is being awaken in order to squash
+ // it. After the separate thread is awaken it transitions to Awaiting state.
+ Canceled // The Deduplicator task has been canceled.
+ };
+
+ // A separate item for keeping squashed entries. Call mergeWith() to squash a consecutive
+ // results into this results.
+ struct WorkingData {
+ WorkingData() = default;
+ WorkingData(const LocatorFilterEntries &entries, std::atomic<State> &state) {
+ mergeWith(entries, state);
+ }
+ LocatorFilterEntries mergeWith(const LocatorFilterEntries &entries,
+ std::atomic<State> &state) {
+ LocatorFilterEntries results;
+ results.reserve(entries.size());
+ for (const LocatorFilterEntry &entry : entries) {
+ if (state == State::Canceled)
+ return {};
+ const auto &link = entry.linkForEditor;
+ if (!link || m_cache.emplace(*link).second)
+ results.append(entry);
+ }
+ if (state == State::Canceled)
+ return {};
+
+ m_data += results;
+ return results;
+ }
+ LocatorFilterEntries entries() const { return m_data; }
+ private:
+ LocatorFilterEntries m_data;
+ std::unordered_set<Link> m_cache;
+ };
+
+public:
+ // filterCount is the expected numbers of running filters. The separate thread executing run()
+ // will stop after reportOutput was called filterCount times, for all different indices
+ // in range [0, filterCount).
+ ResultsDeduplicator(int filterCount)
+ : m_filterCount(filterCount)
+ , m_outputData(filterCount, {})
+ {}
+
+ void reportOutput(int index, const LocatorFilterEntries &outputData)
+ // Called directly by running filters. The calls may come from main thread in case of
+ // e.g. Sync task or directly from other threads when AsyncTask was used.
+ {
+ QTC_ASSERT(index >= 0, return);
+
+ QMutexLocker locker(&m_mutex);
+ // It may happen that the task tree was canceled, while tasks are still running in other
+ // threads and are about to be canceled. In this case we just ignore the call.
+ if (m_state == State::Canceled)
+ return;
+ QTC_ASSERT(index < m_filterCount, return);
+ QTC_ASSERT(!m_outputData.at(index).has_value(), return);
+
+ m_outputData[index] = outputData;
+ m_state = State::NewData;
+ m_waitCondition.wakeOne();
+ }
+
+ // Called when the LocatorMatcher was canceled. It wakes the separate thread in order to
+ // finish it, soon.
+ void cancel()
+ {
+ QMutexLocker locker(&m_mutex);
+ m_state = State::Canceled;
+ m_waitCondition.wakeOne();
+ }
+
+ // Called from the separate thread (ResultsCollector's thread)
+ void run(QPromise<LocatorFilterEntries> &promise)
+ {
+ QList<std::optional<LocatorFilterEntries>> data;
+ QList<std::optional<WorkingData>> workingList(m_filterCount, {});
+ while (waitForData(&data)) {
+ // Emit new results only when new data is reachable from the beginning (i.e. no gaps)
+ int currentIndex = 0;
+ int mergeToIndex = 0;
+ bool hasGap = false;
+ while (currentIndex < m_filterCount) {
+ if (m_state == State::Canceled)
+ return;
+ const auto &outputData = data.at(currentIndex);
+ if (!outputData.has_value()) {
+ ++currentIndex;
+ mergeToIndex = currentIndex;
+ hasGap = true;
+ continue;
+ }
+ const auto &workingData = workingList.at(currentIndex);
+ if (!workingData.has_value()) {
+ const bool mergeToCurrent = currentIndex == mergeToIndex;
+ const LocatorFilterEntries dataForIndex = mergeToCurrent ? *outputData
+ : LocatorFilterEntries();
+ workingList[currentIndex] = std::make_optional(WorkingData(dataForIndex,
+ m_state));
+ if (m_state == State::Canceled)
+ return;
+ const LocatorFilterEntries newData = mergeToCurrent
+ ? workingList[currentIndex]->entries()
+ : workingList[mergeToIndex]->mergeWith(*outputData, m_state);
+ if (m_state == State::Canceled)
+ return;
+ if (!hasGap && !newData.isEmpty())
+ promise.addResult(newData);
+ } else if (currentIndex != mergeToIndex) {
+ const LocatorFilterEntries newData
+ = workingList[mergeToIndex]->mergeWith(workingData->entries(), m_state);
+ workingList[currentIndex] = std::make_optional<WorkingData>({});
+ if (m_state == State::Canceled)
+ return;
+ if (!hasGap && !newData.isEmpty())
+ promise.addResult(newData);
+ }
+ ++currentIndex;
+ }
+ // All data arrived (no gap), so finish here
+ if (!hasGap)
+ return;
+ }
+ }
+
+private:
+ // Called from the separate thread, exclusively by run(). Checks if the new data already
+ // came before sleeping with wait condition. If so, it doesn't sleep with wait condition,
+ // but returns the data collected in meantime. Otherwise, it calls wait() on wait condition.
+ bool waitForData(QList<std::optional<LocatorFilterEntries>> *data)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_state == State::Canceled)
+ return false;
+ if (m_state == State::NewData) {
+ m_state = State::Awaiting; // Mark the state as awaiting to detect new calls to
+ // setOutputData while the separate thread deduplicates the
+ // new data.
+ *data = m_outputData;
+ return true;
+ }
+ m_waitCondition.wait(&m_mutex);
+ QTC_ASSERT(m_state != State::Awaiting, return false);
+ if (m_state == State::Canceled)
+ return false;
+ m_state = State::Awaiting; // Mark the state as awaiting to detect new calls to
+ // setOutputData while the separate thread deduplicates the
+ // new data.
+ *data = m_outputData;
+ return true;
+ }
+
+ QMutex m_mutex;
+ QWaitCondition m_waitCondition;
+ const int m_filterCount = 0;
+ std::atomic<State> m_state = State::Awaiting;
+ QList<std::optional<LocatorFilterEntries>> m_outputData;
+};
+
+// This instance of this object is created by LocatorMatcher tree.
+// It starts a separate thread which collects and deduplicates the results reported
+// by LocatorStorage instances. The ResultsCollector is started as a first task in
+// LocatorMatcher and runs in parallel to all the filters started by LocatorMatcher.
+// When all the results are reported (the expected number of reports is set with setFilterCount()),
+// the ResultsCollector finishes. The intermediate results are reported with
+// serialOutputDataReady() signal.
+// The object of ResultsCollector is registered in Tasking namespace under the
+// ResultsCollectorTask name.
+class ResultsCollector : public QObject
+{
+ Q_OBJECT
+
+public:
+ ~ResultsCollector();
+ void setFilterCount(int count);
+ void start();
+
+ bool isRunning() const { return m_watcher.get(); }
+
+ std::shared_ptr<ResultsDeduplicator> deduplicator() const { return m_deduplicator; }
+
+signals:
+ void serialOutputDataReady(const LocatorFilterEntries &serialOutputData);
+ void done();
+
+private:
+ int m_filterCount = 0;
+ std::unique_ptr<QFutureWatcher<LocatorFilterEntries>> m_watcher;
+ std::shared_ptr<ResultsDeduplicator> m_deduplicator;
+};
+
+ResultsCollector::~ResultsCollector()
+{
+ if (!isRunning())
+ return;
+
+ m_deduplicator->cancel();
+ if (ExtensionSystem::PluginManager::futureSynchronizer()) {
+ ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(m_watcher->future());
+ return;
+ }
+ m_watcher->future().waitForFinished();
+}
+
+void ResultsCollector::setFilterCount(int count)
+{
+ QTC_ASSERT(!isRunning(), return);
+ QTC_ASSERT(count >= 0, return);
+
+ m_filterCount = count;
+}
+
+void ResultsCollector::start()
+{
+ QTC_ASSERT(!m_watcher, return);
+ QTC_ASSERT(!isRunning(), return);
+ if (m_filterCount == 0) {
+ emit done();
+ return;
+ }
+
+ m_deduplicator.reset(new ResultsDeduplicator(m_filterCount));
+ m_watcher.reset(new QFutureWatcher<LocatorFilterEntries>);
+ connect(m_watcher.get(), &QFutureWatcherBase::resultReadyAt, this, [this](int index) {
+ emit serialOutputDataReady(m_watcher->resultAt(index));
+ });
+ connect(m_watcher.get(), &QFutureWatcherBase::finished, this, [this] {
+ emit done();
+ m_watcher.release()->deleteLater();
+ m_deduplicator.reset();
+ });
+
+ // TODO: When filterCount == 1, deliver results directly and finish?
+ auto deduplicate = [](QPromise<LocatorFilterEntries> &promise,
+ const std::shared_ptr<ResultsDeduplicator> &deduplicator) {
+ deduplicator->run(promise);
+ };
+ m_watcher->setFuture(Utils::asyncRun(deduplicate, m_deduplicator));
+}
+
+class ResultsCollectorTaskAdapter : public TaskAdapter<ResultsCollector>
+{
+public:
+ ResultsCollectorTaskAdapter() {
+ connect(task(), &ResultsCollector::done, this, [this] { emit done(true); });
+ }
+ void start() final { task()->start(); }
+};
+
+} // namespace Core
+
+TASKING_DECLARE_TASK(ResultsCollectorTask, Core::ResultsCollectorTaskAdapter);
+
+namespace Core {
+
+class LocatorStoragePrivate
+{
+public:
+ LocatorStoragePrivate(const QString &input, int index,
+ const std::shared_ptr<ResultsDeduplicator> &deduplicator)
+ : m_input(input)
+ , m_index(index)
+ , m_deduplicator(deduplicator)
+ {}
+
+ QString input() const { return m_input; }
+
+ void reportOutput(const LocatorFilterEntries &outputData)
+ {
+ QMutexLocker locker(&m_mutex);
+ QTC_ASSERT(m_deduplicator, return);
+ reportOutputImpl(outputData);
+ }
+
+ void finalize()
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_deduplicator)
+ reportOutputImpl({});
+ }
+
+private:
+ // Call me with mutex locked
+ void reportOutputImpl(const LocatorFilterEntries &outputData)
+ {
+ QTC_ASSERT(m_index >= 0, return);
+ m_deduplicator->reportOutput(m_index, outputData);
+ // Deliver results only once for all copies of the storage, drop ref afterwards
+ m_deduplicator.reset();
+ }
+
+ const QString m_input;
+ const int m_index = -1;
+ std::shared_ptr<ResultsDeduplicator> m_deduplicator;
+ QMutex m_mutex = {};
+};
+
+QString LocatorStorage::input() const
+{
+ QTC_ASSERT(d, return {});
+ return d->input();
+}
+
+void LocatorStorage::reportOutput(const LocatorFilterEntries &outputData) const
+{
+ QTC_ASSERT(d, return);
+ d->reportOutput(outputData);
+}
+
+void LocatorStorage::finalize() const
+{
+ QTC_ASSERT(d, return);
+ d->finalize();
+}
+
+class LocatorMatcherPrivate
+{
+public:
+ LocatorMatcherTasks m_tasks;
+ QString m_input;
+ LocatorFilterEntries m_output;
+ int m_parallelLimit = 0;
+ std::unique_ptr<TaskTree> m_taskTree;
+};
+
+LocatorMatcher::LocatorMatcher()
+ : d(new LocatorMatcherPrivate) {}
+
+LocatorMatcher::~LocatorMatcher() = default;
+
+void LocatorMatcher::setTasks(const LocatorMatcherTasks &tasks)
+{
+ d->m_tasks = tasks;
+}
+
+void LocatorMatcher::setInputData(const QString &inputData)
+{
+ d->m_input = inputData;
+}
+
+void LocatorMatcher::setParallelLimit(int limit)
+{
+ d->m_parallelLimit = limit;
+}
+
+void LocatorMatcher::start()
+{
+ QTC_ASSERT(!isRunning(), return);
+ d->m_output = {};
+ d->m_taskTree.reset(new TaskTree);
+
+ struct CollectorStorage
+ {
+ ResultsCollector *m_collector = nullptr;
+ };
+ TreeStorage<CollectorStorage> collectorStorage;
+
+ const int filterCount = d->m_tasks.size();
+ const auto onCollectorSetup = [this, filterCount, collectorStorage](ResultsCollector &collector) {
+ collectorStorage->m_collector = &collector;
+ collector.setFilterCount(filterCount);
+ connect(&collector, &ResultsCollector::serialOutputDataReady,
+ this, [this](const LocatorFilterEntries &serialOutputData) {
+ d->m_output += serialOutputData;
+ emit serialOutputDataReady(serialOutputData);
+ });
+ };
+ const auto onCollectorDone = [collectorStorage](const ResultsCollector &collector) {
+ Q_UNUSED(collector)
+ collectorStorage->m_collector = nullptr;
+ };
+
+ QList<TaskItem> parallelTasks {parallelLimit(d->m_parallelLimit)};
+
+ const auto onSetup = [this, collectorStorage](const TreeStorage<LocatorStorage> &storage,
+ int index) {
+ return [this, collectorStorage, storage, index] {
+ ResultsCollector *collector = collectorStorage->m_collector;
+ QTC_ASSERT(collector, return);
+ *storage = std::make_shared<LocatorStoragePrivate>(d->m_input, index,
+ collector->deduplicator());
+ };
+ };
+
+ const auto onDone = [](const TreeStorage<LocatorStorage> &storage) {
+ return [storage] { storage->finalize(); };
+ };
+
+ int index = 0;
+ for (const LocatorMatcherTask &task : std::as_const(d->m_tasks)) {
+ const auto storage = task.storage;
+ const Group group {
+ finishAllAndDone,
+ Storage(storage),
+ onGroupSetup(onSetup(storage, index)),
+ onGroupDone(onDone(storage)),
+ onGroupError(onDone(storage)),
+ task.task
+ };
+ parallelTasks << group;
+ ++index;
+ }
+
+ const Group root {
+ parallel,
+ Storage(collectorStorage),
+ ResultsCollectorTask(onCollectorSetup, onCollectorDone, onCollectorDone),
+ Group {
+ parallelTasks
+ }
+ };
+
+ d->m_taskTree->setupRoot(root);
+
+ const auto onFinish = [this](bool success) {
+ return [this, success] {
+ emit done(success);
+ d->m_taskTree.release()->deleteLater();
+ };
+ };
+ connect(d->m_taskTree.get(), &TaskTree::done, this, onFinish(true));
+ connect(d->m_taskTree.get(), &TaskTree::errorOccurred, this, onFinish(false));
+ d->m_taskTree->start();
+}
+
+void LocatorMatcher::stop()
+{
+ if (!isRunning())
+ return;
+
+ d->m_taskTree->stop();
+ d->m_taskTree.reset();
+}
+
+bool LocatorMatcher::isRunning() const
+{
+ return d->m_taskTree.get() && d->m_taskTree->isRunning();
+}
+
+LocatorFilterEntries LocatorMatcher::outputData() const
+{
+ return d->m_output;
+}
+
+LocatorFilterEntries LocatorMatcher::runBlocking(const LocatorMatcherTasks &tasks,
+ const QString &input, int parallelLimit)
+{
+ LocatorMatcher tree;
+ tree.setTasks(tasks);
+ tree.setInputData(input);
+ tree.setParallelLimit(parallelLimit);
+
+ QEventLoop loop;
+ connect(&tree, &LocatorMatcher::done, &loop, [&loop] { loop.quit(); });
+ tree.start();
+ if (tree.isRunning())
+ loop.exec(QEventLoop::ExcludeUserInputEvents);
+ return tree.outputData();
+}
+
+static QHash<MatcherType, QList<LocatorMatcherTaskCreator>> s_matcherCreators = {};
+
+void LocatorMatcher::addMatcherCreator(MatcherType type, const LocatorMatcherTaskCreator &creator)
+{
+ QTC_ASSERT(creator, return);
+ s_matcherCreators[type].append(creator);
+}
+
+LocatorMatcherTasks LocatorMatcher::matchers(MatcherType type)
+{
+ const QList<LocatorMatcherTaskCreator> creators = s_matcherCreators.value(type);
+ LocatorMatcherTasks result;
+ for (const LocatorMatcherTaskCreator &creator : creators)
+ result << creator();
+ return result;
+}
+
static QList<ILocatorFilter *> g_locatorFilters;
/*!
@@ -83,19 +594,21 @@ QString ILocatorFilter::shortcutString() const
}
/*!
- Performs actions that need to be done in the main thread before actually
- running the search for \a entry.
-
- Called on the main thread before matchesFor() is called in a separate
- thread.
-
- The default implementation does nothing.
+ \internal
+ Sets the refresh recipe for refreshing cached data.
+*/
+void ILocatorFilter::setRefreshRecipe(const std::optional<TaskItem> &recipe)
+{
+ m_refreshRecipe = recipe;
+}
- \sa matchesFor()
+/*!
+ Returns the refresh recipe for refreshing cached data. By default, the locator filter has
+ no recipe set, so that it won't be refreshed.
*/
-void ILocatorFilter::prepareSearch(const QString &entry)
+std::optional<TaskItem> ILocatorFilter::refreshRecipe() const
{
- Q_UNUSED(entry)
+ return m_refreshRecipe;
}
/*!
@@ -201,13 +714,13 @@ void ILocatorFilter::restoreState(const QByteArray &state)
various aspects of the filter. Called when the user requests to configure
the filter.
- Set \a needsRefresh to \c true, if a refresh() should be done after
+ Set \a needsRefresh to \c true, if a refresh should be done after
closing the dialog. Return \c false if the user canceled the dialog.
The default implementation allows changing the shortcut and whether the
filter is included by default.
- \sa refresh()
+ \sa refreshRecipe()
*/
bool ILocatorFilter::openConfigDialog(QWidget *parent, bool &needsRefresh)
{
@@ -425,7 +938,10 @@ ILocatorFilter::Priority ILocatorFilter::priority() const
*/
void ILocatorFilter::setEnabled(bool enabled)
{
+ if (enabled == m_enabled)
+ return;
m_enabled = enabled;
+ emit enabledChanged(m_enabled);
}
/*!
@@ -582,39 +1098,6 @@ bool ILocatorFilter::isOldSetting(const QByteArray &state)
}
/*!
- \fn QList<Core::LocatorFilterEntry> Core::ILocatorFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
-
- Returns the list of results of this filter for the search term \a entry.
- This is run in a separate thread, but is guaranteed to only run in a single
- thread at any given time. Quickly running preparations can be done in the
- GUI thread in prepareSearch().
-
- Implementations should do a case sensitive or case insensitive search
- depending on caseSensitivity(). If \a future is \c canceled, the search
- should be aborted.
-
- \sa prepareSearch()
- \sa caseSensitivity()
-*/
-
-/*!
- \fn void Core::ILocatorFilter::accept(Core::const LocatorFilterEntry &selection, QString *newText, int *selectionStart, int *selectionLength) const
-
- Called with the entry specified by \a selection when the user activates it
- in the result list.
- Implementations can return a new search term \a newText, which has \a selectionLength characters
- starting from \a selectionStart preselected, and the cursor set to the end of the selection.
-*/
-
-/*!
- \fn void Core::ILocatorFilter::refresh(QFutureInterface<void> &future)
-
- Refreshes cached data asynchronously.
-
- If \a future is \c canceled, the refresh should be aborted.
-*/
-
-/*!
\enum Core::ILocatorFilter::Priority
This enum value holds the priority that is used for ordering the results
@@ -647,4 +1130,410 @@ bool ILocatorFilter::isOldSetting(const QByteArray &state)
expression.
*/
+std::atomic_int s_executeId = 0;
+
+
+class LocatorFileCachePrivate
+{
+public:
+ bool isValid() const { return bool(m_generator); }
+ void invalidate();
+ bool ensureValidated();
+ void bumpExecutionId() { m_executionId = s_executeId.fetch_add(1) + 1; }
+ void update(const LocatorFileCachePrivate &newCache);
+ void setGeneratorProvider(const LocatorFileCache::GeneratorProvider &provider)
+ { m_provider = provider; }
+ void setGenerator(const LocatorFileCache::FilePathsGenerator &generator);
+ LocatorFilterEntries generate(const QFuture<void> &future, const QString &input);
+
+ // Is persistent, does not reset on invalidate
+ LocatorFileCache::GeneratorProvider m_provider;
+ LocatorFileCache::FilePathsGenerator m_generator;
+ int m_executionId = 0;
+
+ std::optional<FilePaths> m_filePaths;
+
+ QString m_lastInput;
+ std::optional<FilePaths> m_cache;
+};
+
+// Clears all but provider
+void LocatorFileCachePrivate::invalidate()
+{
+ LocatorFileCachePrivate that;
+ that.m_provider = m_provider;
+ *this = that;
+}
+
+/*!
+ \internal
+
+ Returns true if the cache is valid. Otherwise, tries to validate the cache and returns whether
+ the validation succeeded.
+
+ When the cache is valid, it does nothing and returns true.
+ Otherwise, when the GeneratorProvider is not set, it does nothing and returns false.
+ Otherwise, the GeneratorProvider is used for recreating the FilePathsGenerator.
+ If the recreated FilePathsGenerator is not empty, it return true.
+ Otherwise, it returns false;
+*/
+bool LocatorFileCachePrivate::ensureValidated()
+{
+ if (isValid())
+ return true;
+
+ if (!m_provider)
+ return false;
+
+ invalidate();
+ m_generator = m_provider();
+ return isValid();
+}
+
+void LocatorFileCachePrivate::update(const LocatorFileCachePrivate &newCache)
+{
+ if (m_executionId != newCache.m_executionId)
+ return; // The mismatching executionId was detected, ignoring the update...
+ auto provider = m_provider;
+ *this = newCache;
+ m_provider = provider;
+}
+
+void LocatorFileCachePrivate::setGenerator(const LocatorFileCache::FilePathsGenerator &generator)
+{
+ invalidate();
+ m_generator = generator;
+}
+
+static bool containsPathSeparator(const QString &candidate)
+{
+ return candidate.contains('/') || candidate.contains('*');
+};
+
+/*!
+ \internal
+
+ Uses the generator to update the cache if needed and returns entries for the input.
+ Uses the cached data when no need for re-generation. Updates the cache accordingly.
+*/
+LocatorFilterEntries LocatorFileCachePrivate::generate(const QFuture<void> &future,
+ const QString &input)
+{
+ QTC_ASSERT(isValid(), return {});
+
+ // If search string contains spaces, treat them as wildcard '*' and search in full path
+ const QString wildcardInput = QDir::fromNativeSeparators(input).replace(' ', '*');
+ const Link inputLink = Link::fromString(wildcardInput, true);
+ const QString newInput = inputLink.targetFilePath.toString();
+ const QRegularExpression regExp = ILocatorFilter::createRegExp(newInput);
+ if (!regExp.isValid())
+ return {}; // Don't clear the cache - still remember the cache for the last valid input.
+
+ if (future.isCanceled())
+ return {};
+
+ const bool hasPathSeparator = containsPathSeparator(newInput);
+ const bool containsLastInput = !m_lastInput.isEmpty() && newInput.contains(m_lastInput);
+ const bool pathSeparatorAdded = !containsPathSeparator(m_lastInput) && hasPathSeparator;
+ const bool searchInCache = m_filePaths && m_cache && containsLastInput && !pathSeparatorAdded;
+
+ std::optional<FilePaths> newPaths = m_filePaths;
+ if (!searchInCache && !newPaths) {
+ newPaths = m_generator(future);
+ if (future.isCanceled()) // Ensure we got not canceled results from generator.
+ return {};
+ }
+
+ const FilePaths &sourcePaths = searchInCache ? *m_cache : *newPaths;
+ LocatorFileCache::MatchedEntries entries = {};
+ const FilePaths newCache = LocatorFileCache::processFilePaths(
+ future, sourcePaths, hasPathSeparator, regExp, inputLink, &entries);
+ for (auto &entry : entries) {
+ if (future.isCanceled())
+ return {};
+
+ if (entry.size() < 1000)
+ Utils::sort(entry, LocatorFilterEntry::compareLexigraphically);
+ }
+
+ if (future.isCanceled())
+ return {};
+
+ // Update all the cache data in one go
+ m_filePaths = newPaths;
+ m_lastInput = newInput;
+ m_cache = newCache;
+
+ return std::accumulate(std::begin(entries), std::end(entries), LocatorFilterEntries());
+}
+
+/*!
+ \class Core::LocatorFileCache
+ \inmodule QtCreator
+
+ \brief The LocatorFileCache class encapsulates all the responsibilities needed for
+ implementing a cache for file filters.
+
+ LocatorFileCache serves as a replacement for the old BaseFileFilter interface.
+*/
+
+/*!
+ Constructs an invalid cache.
+
+ The cache is considered to be in an invalid state after a call to invalidate(),
+ of after a call to setFilePathsGenerator() when passed functions was empty.
+
+ It it possible to setup the automatic validator for the cache through the
+ setGeneratorProvider().
+
+ \sa invalidate, setGeneratorProvider, setFilePathsGenerator, setFilePaths
+*/
+
+LocatorFileCache::LocatorFileCache()
+ : d(new LocatorFileCachePrivate) {}
+
+/*!
+ Invalidates the cache.
+
+ In order to validate it, use either setFilePathsGenerator() or setFilePaths().
+ The cache may be automatically validated if the GeneratorProvider was set
+ through the setGeneratorProvider().
+
+ \note This function invalidates the cache permanently, clearing all the cached data,
+ and removing the stored generator. The stored generator provider is preserved.
+*/
+void LocatorFileCache::invalidate()
+{
+ d->invalidate();
+}
+
+/*!
+ Sets the file path generator provider.
+
+ The \a provider serves for an automatic validation of the invalid cache by recreating
+ the FilePathsGenerator. The automatic validation happens when the LocatorMatcherTask returned
+ by matcher() is being started, and the cache is not valid at that moment. In this case
+ the stored \a provider is being called.
+
+ The passed \a provider function is always called from the main thread. If needed, it is
+ called prior to starting an asynchronous task that collects the locator filter results.
+
+ When this function is called, the cache isn't invalidated.
+ Whenever cache's invalidation happens, e.g. when invalidate(), setFilePathsGenerator() or
+ setFilePaths() is called, the stored GeneratorProvider is being preserved.
+ In order to clear the stored GeneratorProvider, call this method with an empty
+ function {}.
+*/
+void LocatorFileCache::setGeneratorProvider(const GeneratorProvider &provider)
+{
+ d->setGeneratorProvider(provider);
+}
+
+std::optional<FilePaths> LocatorFileCache::filePaths() const
+{
+ return d->m_filePaths;
+}
+
+/*!
+ Sets the file path generator.
+
+ The \a generator serves for returning the full input list of file paths when the
+ associated LocatorMatherTask is being run in a separate thread. When the computation of the
+ full list of file paths takes a considerable amount of time, this computation may
+ be potentially moved to the separate thread, provided that all the dependent data may be safely
+ passed to the \a generator function when this function is being set in the main thread.
+
+ The passed \a generator is always called exclusively from the non-main thread when running
+ LocatorMatcherTask returned by matcher(). It is called when the cached data is
+ empty or when it needs to be regenerated due to a new search which can't reuse
+ the cache from the previous search.
+
+ Generating a new file path list may be a time consuming task. In order to finish the task early
+ when being canceled, the \e future argument of the FilePathsGenerator may be used.
+ The FilePathsGenerator returns the full list of file paths used for file filter's processing.
+
+ Whenever it is possible to postpone the creation of a file path list so that it may be done
+ safely later from the non-main thread, based on some other reentrant/thread-safe data,
+ this method should be used. The other dependent data should be passed by lambda capture.
+ The body of the passed \a generator should take extra care for ensuring that the passed
+ other data via lambda captures are reentrant and the lambda body is thread safe.
+ See the example usage of the generator inside CppIncludesFilter implementation.
+
+ Otherwise, when postponing the creation of file paths list isn't safe, use setFilePaths()
+ with ready made list, prepared in main thread.
+
+ \note This function invalidates the cache, clearing all the cached data,
+ and if the passed generator is non-empty, the cache is set to a valid state.
+ The stored generator provider is preserved.
+
+ \sa setGeneratorProvider, setFilePaths
+*/
+void LocatorFileCache::setFilePathsGenerator(const FilePathsGenerator &generator)
+{
+ d->setGenerator(generator);
+}
+
+/*!
+ Wraps the passed \a filePaths into a trivial FilePathsGenerator and sets it
+ as a cache's generator.
+
+ \note This function invalidates the cache temporarily, clearing all the cached data,
+ and sets it to a valid state with the new generator for the passed \a filePaths.
+ The stored generator provider is preserved.
+
+ \sa setGeneratorProvider
+*/
+void LocatorFileCache::setFilePaths(const FilePaths &filePaths)
+{
+ setFilePathsGenerator(filePathsGenerator(filePaths));
+ d->m_filePaths = filePaths;
+}
+
+/*!
+ Adapts the \a filePaths list into a LocatorFileCacheGenerator.
+ Useful when implementing GeneratorProvider in case a creation of file paths
+ can't be invoked from the non-main thread.
+*/
+LocatorFileCache::FilePathsGenerator LocatorFileCache::filePathsGenerator(
+ const FilePaths &filePaths)
+{
+ return [filePaths](const QFuture<void> &) { return filePaths; };
+}
+
+static ILocatorFilter::MatchLevel matchLevelFor(const QRegularExpressionMatch &match,
+ const QString &matchText)
+{
+ const int consecutivePos = match.capturedStart(1);
+ if (consecutivePos == 0)
+ return ILocatorFilter::MatchLevel::Best;
+ if (consecutivePos > 0) {
+ const QChar prevChar = matchText.at(consecutivePos - 1);
+ if (prevChar == '_' || prevChar == '.')
+ return ILocatorFilter::MatchLevel::Better;
+ }
+ if (match.capturedStart() == 0)
+ return ILocatorFilter::MatchLevel::Good;
+ return ILocatorFilter::MatchLevel::Normal;
+}
+
+/*!
+ Helper used internally and by SpotlightLocatorFilter.
+
+ To be called from non-main thread. The cancellation is controlled by the passed \a future.
+ This function periodically checks for the cancellation state of the \a future and returns
+ early when cancellation was detected.
+ Creates lists of matching LocatorFilterEntries categorized by MatcherType. These lists
+ are returned through the \a entries argument.
+
+ Returns a list of all matching files.
+
+ This function checks for each file in \a filePaths if it matches the passed \a regExp.
+ If so, a new entry is created using \a hasPathSeparator and \a inputLink and
+ it's being added into the \a entries argument and the results list.
+*/
+FilePaths LocatorFileCache::processFilePaths(const QFuture<void> &future,
+ const FilePaths &filePaths,
+ bool hasPathSeparator,
+ const QRegularExpression &regExp,
+ const Link &inputLink,
+ LocatorFileCache::MatchedEntries *entries)
+{
+ FilePaths cache;
+ for (const FilePath &path : filePaths) {
+ if (future.isCanceled())
+ return {};
+
+ const QString matchText = hasPathSeparator ? path.toString() : path.fileName();
+ const QRegularExpressionMatch match = regExp.match(matchText);
+
+ if (match.hasMatch()) {
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = path.fileName();
+ filterEntry.filePath = path;
+ filterEntry.extraInfo = path.shortNativePath();
+ filterEntry.linkForEditor = Link(path, inputLink.targetLine, inputLink.targetColumn);
+ filterEntry.highlightInfo = hasPathSeparator
+ ? ILocatorFilter::highlightInfo(regExp.match(filterEntry.extraInfo),
+ LocatorFilterEntry::HighlightInfo::ExtraInfo)
+ : ILocatorFilter::highlightInfo(match);
+ const ILocatorFilter::MatchLevel matchLevel = matchLevelFor(match, matchText);
+ (*entries)[int(matchLevel)].append(filterEntry);
+ cache << path;
+ }
+ }
+ return cache;
+}
+
+static void filter(QPromise<LocatorFileCachePrivate> &promise, const LocatorStorage &storage,
+ const LocatorFileCachePrivate &cache)
+{
+ QTC_ASSERT(cache.isValid(), return);
+ auto newCache = cache;
+ const LocatorFilterEntries output = newCache.generate(QFuture<void>(promise.future()),
+ storage.input());
+ if (promise.isCanceled())
+ return;
+ storage.reportOutput(output);
+ promise.addResult(newCache);
+}
+
+/*!
+ Returns the locator matcher task for the cache. The task, when successfully finished,
+ updates this LocatorFileCache instance if needed.
+
+ This method is to be used directly by the FilePaths filters. The FilePaths filter should
+ keep an instance of a LocatorFileCache internally. Ensure the LocatorFileCache instance
+ outlives the running matcher, otherwise the cache won't be updated after the task finished.
+
+ When returned LocatorMatcherTask is being run it checks if this cache is valid.
+ When the cache is invalid, it uses GeneratorProvider to update the
+ cache's FilePathsGenerator and validates the cache. If that failed, the task
+ is not started. When the cache is valid, the running task will reuse cached data for
+ calculating the LocatorMatcherTask's results.
+
+ After a successful run of the task, this cache is updated according to the last search.
+ When this cache started a new search in meantime, the cache was invalidated or even deleted,
+ the update of the cache after a successful run of the task is ignored.
+*/
+LocatorMatcherTask LocatorFileCache::matcher() const
+{
+ TreeStorage<LocatorStorage> storage;
+ std::weak_ptr<LocatorFileCachePrivate> weak = d;
+
+ const auto onSetup = [storage, weak](Async<LocatorFileCachePrivate> &async) {
+ auto that = weak.lock();
+ if (!that) // LocatorMatcher is running after *this LocatorFileCache was destructed.
+ return TaskAction::StopWithDone;
+
+ if (!that->ensureValidated())
+ return TaskAction::StopWithDone; // The cache is invalid and
+ // no provider is set or it returned empty generator
+ that->bumpExecutionId();
+
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(&filter, *storage, *that);
+ return TaskAction::Continue;
+ };
+ const auto onDone = [weak](const Async<LocatorFileCachePrivate> &async) {
+ auto that = weak.lock();
+ if (!that)
+ return; // LocatorMatcherTask finished after *this LocatorFileCache was destructed.
+
+ if (!that->isValid())
+ return; // The cache has been invalidated in meantime.
+
+ if (that->m_executionId == 0)
+ return; // The cache has been invalidated and not started.
+
+ if (!async.isResultAvailable())
+ return; // The async task didn't report updated cache.
+
+ that->update(async.result());
+ };
+
+ return {AsyncTask<LocatorFileCachePrivate>(onSetup, onDone), storage};
+}
+
} // Core
+
+#include "ilocatorfilter.moc"
diff --git a/src/plugins/coreplugin/locator/ilocatorfilter.h b/src/plugins/coreplugin/locator/ilocatorfilter.h
index e6b49ab5e9..b008c12bb5 100644
--- a/src/plugins/coreplugin/locator/ilocatorfilter.h
+++ b/src/plugins/coreplugin/locator/ilocatorfilter.h
@@ -5,24 +5,44 @@
#include <coreplugin/core_global.h>
+#include <solutions/tasking/tasktree.h>
+
#include <utils/filepath.h>
#include <utils/id.h>
#include <utils/link.h>
-#include <QFutureInterface>
#include <QIcon>
-#include <QMetaType>
-#include <QVariant>
#include <QKeySequence>
#include <optional>
+QT_BEGIN_NAMESPACE
+template <typename T>
+class QFuture;
+QT_END_NAMESPACE
+
namespace Core {
+namespace Internal {
+class Locator;
+class LocatorWidget;
+}
+
class ILocatorFilter;
+class LocatorStoragePrivate;
+class LocatorFileCachePrivate;
+
+class AcceptResult
+{
+public:
+ QString newText;
+ int selectionStart = -1;
+ int selectionLength = 0;
+};
-struct LocatorFilterEntry
+class LocatorFilterEntry
{
+public:
struct HighlightInfo {
enum DataType {
DisplayName,
@@ -66,18 +86,7 @@ struct LocatorFilterEntry
LocatorFilterEntry() = default;
- LocatorFilterEntry(ILocatorFilter *fromFilter,
- const QString &name,
- const QVariant &data = {},
- std::optional<QIcon> icon = std::nullopt)
- : filter(fromFilter)
- , displayName(name)
- , internalData(data)
- , displayIcon(icon)
- {}
-
- /* backpointer to creating filter */
- ILocatorFilter *filter = nullptr;
+ using Acceptor = std::function<AcceptResult()>;
/* displayed string */
QString displayName;
/* extra information displayed in parentheses and light-gray next to display name (optional)*/
@@ -86,8 +95,9 @@ struct LocatorFilterEntry
QString extraInfo;
/* additional tooltip */
QString toolTip;
- /* can be used by the filter to save more information about the entry */
- QVariant internalData;
+ /* called by locator widget on accept. By default, when acceptor is empty,
+ EditorManager::openEditor(LocatorFilterEntry) will be used instead. */
+ Acceptor acceptor;
/* icon to display along with the entry */
std::optional<QIcon> displayIcon;
/* file path, if the entry is related to a file, is used e.g. for resolving a file icon */
@@ -108,6 +118,76 @@ struct LocatorFilterEntry
}
};
+using LocatorFilterEntries = QList<LocatorFilterEntry>;
+
+class CORE_EXPORT LocatorStorage final
+{
+public:
+ LocatorStorage() = default;
+ QString input() const;
+ void reportOutput(const LocatorFilterEntries &outputData) const;
+
+private:
+ friend class LocatorMatcher;
+ LocatorStorage(const std::shared_ptr<LocatorStoragePrivate> &priv) { d = priv; }
+ void finalize() const;
+ std::shared_ptr<LocatorStoragePrivate> d;
+};
+
+class CORE_EXPORT LocatorMatcherTask final
+{
+public:
+ // The main task. Initial data (searchTerm) should be taken from storage.input().
+ // Results reporting is done via the storage.reportOutput().
+ Tasking::TaskItem task = Tasking::Group{};
+
+ // When constructing the task, don't place the storage inside the task above.
+ Tasking::TreeStorage<LocatorStorage> storage;
+};
+
+using LocatorMatcherTasks = QList<LocatorMatcherTask>;
+using LocatorMatcherTaskCreator = std::function<LocatorMatcherTasks()>;
+class LocatorMatcherPrivate;
+
+enum class MatcherType {
+ AllSymbols,
+ Classes,
+ Functions,
+ CurrentDocumentSymbols
+};
+
+class CORE_EXPORT LocatorMatcher final : public QObject
+{
+ Q_OBJECT
+
+public:
+ LocatorMatcher();
+ ~LocatorMatcher();
+ void setTasks(const LocatorMatcherTasks &tasks);
+ void setInputData(const QString &inputData);
+ void setParallelLimit(int limit); // by default 0 = parallel
+ void start();
+ void stop();
+
+ bool isRunning() const;
+ // Total data collected so far, even when running.
+ LocatorFilterEntries outputData() const;
+
+ // Note: Starts internal event loop.
+ static LocatorFilterEntries runBlocking(const LocatorMatcherTasks &tasks,
+ const QString &input, int parallelLimit = 0);
+
+ static void addMatcherCreator(MatcherType type, const LocatorMatcherTaskCreator &creator);
+ static LocatorMatcherTasks matchers(MatcherType type);
+
+signals:
+ void serialOutputDataReady(const LocatorFilterEntries &serialOutputData);
+ void done(bool success);
+
+private:
+ std::unique_ptr<LocatorMatcherPrivate> d;
+};
+
class CORE_EXPORT ILocatorFilter : public QObject
{
Q_OBJECT
@@ -126,8 +206,6 @@ public:
ILocatorFilter(QObject *parent = nullptr);
~ILocatorFilter() override;
- static const QList<ILocatorFilter *> allLocatorFilters();
-
Utils::Id id() const;
Utils::Id actionId() const;
@@ -149,15 +227,6 @@ public:
std::optional<QString> defaultSearchText() const;
void setDefaultSearchText(const QString &defaultSearchText);
- virtual void prepareSearch(const QString &entry);
-
- virtual QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future, const QString &entry) = 0;
-
- virtual void accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const = 0;
-
- virtual void refresh(QFutureInterface<void> &future) { Q_UNUSED(future) };
-
virtual QByteArray saveState() const;
virtual void restoreState(const QByteArray &state);
@@ -188,6 +257,9 @@ public:
public slots:
void setEnabled(bool enabled);
+signals:
+ void enabledChanged(bool enabled);
+
protected:
void setHidden(bool hidden);
void setId(Utils::Id id);
@@ -198,9 +270,18 @@ protected:
virtual void saveState(QJsonObject &object) const;
virtual void restoreState(const QJsonObject &object);
+ void setRefreshRecipe(const std::optional<Tasking::TaskItem> &recipe);
+ std::optional<Tasking::TaskItem> refreshRecipe() const;
+
static bool isOldSetting(const QByteArray &state);
private:
+ virtual LocatorMatcherTasks matchers() = 0;
+
+ friend class Internal::Locator;
+ friend class Internal::LocatorWidget;
+ static const QList<ILocatorFilter *> allLocatorFilters();
+
Utils::Id m_id;
QString m_shortcut;
Priority m_priority = Medium;
@@ -208,6 +289,7 @@ private:
QString m_description;
QString m_defaultShortcut;
std::optional<QString> m_defaultSearchText;
+ std::optional<Tasking::TaskItem> m_refreshRecipe;
QKeySequence m_defaultKeySequence;
bool m_defaultIncludedByDefault = false;
bool m_includedByDefault = m_defaultIncludedByDefault;
@@ -216,4 +298,37 @@ private:
bool m_isConfigurable = true;
};
+class CORE_EXPORT LocatorFileCache final
+{
+ Q_DISABLE_COPY_MOVE(LocatorFileCache)
+
+public:
+ // Always called from non-main thread.
+ using FilePathsGenerator = std::function<Utils::FilePaths(const QFuture<void> &)>;
+ // Always called from main thread.
+ using GeneratorProvider = std::function<FilePathsGenerator()>;
+
+ LocatorFileCache();
+
+ void invalidate();
+ void setFilePathsGenerator(const FilePathsGenerator &generator);
+ void setFilePaths(const Utils::FilePaths &filePaths);
+ void setGeneratorProvider(const GeneratorProvider &provider);
+
+ std::optional<Utils::FilePaths> filePaths() const;
+
+ static FilePathsGenerator filePathsGenerator(const Utils::FilePaths &filePaths);
+ LocatorMatcherTask matcher() const;
+
+ using MatchedEntries = std::array<LocatorFilterEntries, int(ILocatorFilter::MatchLevel::Count)>;
+ static Utils::FilePaths processFilePaths(const QFuture<void> &future,
+ const Utils::FilePaths &filePaths,
+ bool hasPathSeparator,
+ const QRegularExpression &regExp,
+ const Utils::Link &inputLink,
+ LocatorFileCache::MatchedEntries *entries);
+private:
+ std::shared_ptr<LocatorFileCachePrivate> d;
+};
+
} // namespace Core
diff --git a/src/plugins/coreplugin/locator/javascriptfilter.cpp b/src/plugins/coreplugin/locator/javascriptfilter.cpp
index b97b054051..3656d67320 100644
--- a/src/plugins/coreplugin/locator/javascriptfilter.cpp
+++ b/src/plugins/coreplugin/locator/javascriptfilter.cpp
@@ -5,122 +5,434 @@
#include "../coreplugintr.h"
+#include <extensionsystem/pluginmanager.h>
+
+#include <utils/algorithm.h>
+#include <utils/async.h>
+
#include <QClipboard>
#include <QGuiApplication>
#include <QJSEngine>
+#include <QPair>
+#include <QPointer>
+#include <QScopeGuard>
-namespace Core {
-namespace Internal {
+#include <chrono>
-enum class EngineAction { Reset = 1, Abort };
+using namespace Core;
+using namespace Core::Internal;
+using namespace Tasking;
+using namespace Utils;
-JavaScriptFilter::JavaScriptFilter()
+using namespace std::chrono_literals;
+
+static const char s_initData[] = R"(
+ function abs(x) { return Math.abs(x); }
+ function acos(x) { return Math.acos(x); }
+ function asin(x) { return Math.asin(x); }
+ function atan(x) { return Math.atan(x); }
+ function atan2(x, y) { return Math.atan2(x, y); }
+ function bin(x) { return '0b' + x.toString(2); }
+ function ceil(x) { return Math.ceil(x); }
+ function cos(x) { return Math.cos(x); }
+ function exp(x) { return Math.exp(x); }
+ function e() { return Math.E; }
+ function floor(x) { return Math.floor(x); }
+ function hex(x) { return '0x' + x.toString(16); }
+ function log(x) { return Math.log(x); }
+ function max() { return Math.max.apply(null, arguments); }
+ function min() { return Math.min.apply(null, arguments); }
+ function oct(x) { return '0' + x.toString(8); }
+ function pi() { return Math.PI; }
+ function pow(x, y) { return Math.pow(x, y); }
+ function random() { return Math.random(); }
+ function round(x) { return Math.round(x); }
+ function sin(x) { return Math.sin(x); }
+ function sqrt(x) { return Math.sqrt(x); }
+ function tan(x) { return Math.tan(x); }
+)";
+
+enum class JavaScriptResult {
+ FinishedWithSuccess,
+ FinishedWithError,
+ TimedOut,
+ Canceled
+};
+
+class JavaScriptOutput
{
- setId("JavaScriptFilter");
- setDisplayName(Tr::tr("Evaluate JavaScript"));
- setDescription(Tr::tr("Evaluates arbitrary JavaScript expressions and copies the result."));
- setDefaultIncludedByDefault(false);
- setDefaultShortcutString("=");
- m_abortTimer.setSingleShot(true);
- m_abortTimer.setInterval(1000);
- connect(&m_abortTimer, &QTimer::timeout, this, [this] {
- m_aborted = true;
- if (m_engine)
- m_engine->setInterrupted(true);
- });
-}
+public:
+ QString m_output;
+ JavaScriptResult m_result = JavaScriptResult::Canceled;
+};
+
+using JavaScriptCallback = std::function<void(const JavaScriptOutput &)>;
-JavaScriptFilter::~JavaScriptFilter()
+class JavaScriptInput
{
-}
+public:
+ bool m_reset = false; // Recreates the QJSEngine, re-inits it and continues the request queue
+ QString m_input;
+ JavaScriptCallback m_callback = {};
+};
-void JavaScriptFilter::prepareSearch(const QString &entry)
+class JavaScriptThread : public QObject
{
- Q_UNUSED(entry)
+ Q_OBJECT
- if (!m_engine)
- setupEngine();
- m_engine->setInterrupted(false);
- m_aborted = false;
- m_abortTimer.start();
-}
+public:
+ // Called from the other thread, scheduled from the main thread through the queued
+ // invocation.
+ void run();
+
+ // Called from main thread exclusively
+ void cancel();
+ // Called from main thread exclusively
+ int addRequest(const JavaScriptInput &input);
+ // Called from main thread exclusively
+ void removeRequest(int id);
+
+ // Called from the main thread exclusively, scheduled from the other thread through the queued
+ // invocation when the new result is ready.
+ void flush();
+
+signals:
+ void newOutput();
+
+private:
+ struct QueueItem {
+ int m_id = 0;
+ JavaScriptInput m_input;
+ std::optional<JavaScriptOutput> m_output = {};
+ };
+
+ // Called from the main thread exclusively
+ QList<QueueItem> takeOutputQueue() {
+ QMutexLocker locker(&m_mutex);
+ return std::exchange(m_outputQueue, {});
+ }
+
+ int m_maxId = 0;
+ std::unique_ptr<QJSEngine> m_engine;
-QList<LocatorFilterEntry> JavaScriptFilter::matchesFor(
- QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
+ mutable QMutex m_mutex;
+ QWaitCondition m_waitCondition;
+ bool m_canceled = false;
+ QList<QueueItem> m_inputQueue;
+ std::optional<QueueItem> m_currentItem;
+ QList<QueueItem> m_outputQueue;
+};
+
+void JavaScriptThread::run()
{
- Q_UNUSED(future)
-
- QList<LocatorFilterEntry> entries;
- if (entry.trimmed().isEmpty()) {
- entries.append({this, Tr::tr("Reset Engine"), QVariant::fromValue(EngineAction::Reset)});
- } else {
- const QString result = m_engine->evaluate(entry).toString();
- if (m_aborted) {
- const QString message = entry + " = " + Tr::tr("Engine aborted after timeout.");
- entries.append({this, message, QVariant::fromValue(EngineAction::Abort)});
- } else {
- const QString expression = entry + " = " + result;
- entries.append({this, expression});
- entries.append({this, Tr::tr("Copy to clipboard: %1").arg(result), result});
- entries.append({this, Tr::tr("Copy to clipboard: %1").arg(expression), expression});
+ const auto evaluate = [this](const QString &input) {
+ const QJSValue result = m_engine->evaluate(input);
+ if (m_engine->isInterrupted()) {
+ return JavaScriptOutput{Tr::tr("The evaluation was interrupted."),
+ JavaScriptResult::Canceled};
}
+ return JavaScriptOutput{result.toString(),
+ result.isError() ? JavaScriptResult::FinishedWithError
+ : JavaScriptResult::FinishedWithSuccess};
+ };
+ const auto reset = [evaluate] {
+ JavaScriptOutput output = evaluate(s_initData);
+ output.m_output = output.m_result == JavaScriptResult::FinishedWithSuccess
+ ? Tr::tr("Engine reinitialized properly.")
+ : Tr::tr("Engine did not reinitialize properly.");
+ return output;
+ };
+
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_canceled)
+ return;
+ m_engine.reset(new QJSEngine);
}
- return entries;
+ // TODO: consider placing a reset request as the first input instead
+ const JavaScriptOutput output = reset();
+ QTC_ASSERT(output.m_result == JavaScriptResult::FinishedWithSuccess,
+ qWarning() << output.m_output);
+
+ QueueItem currentItem;
+ while (true) {
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_canceled)
+ return;
+ if (m_currentItem) {
+ QTC_CHECK(m_currentItem->m_id == currentItem.m_id);
+ m_outputQueue.append(currentItem);
+ m_currentItem = {};
+ emit newOutput();
+ }
+ while (m_inputQueue.isEmpty()) {
+ m_waitCondition.wait(&m_mutex);
+ if (m_canceled)
+ return;
+ }
+ m_currentItem = currentItem = m_inputQueue.takeFirst();
+ if (currentItem.m_input.m_reset)
+ m_engine.reset(new QJSEngine);
+ m_engine->setInterrupted(false);
+ }
+ const JavaScriptInput &input = currentItem.m_input;
+ if (input.m_reset) {
+ currentItem.m_output = reset();
+ QTC_ASSERT(currentItem.m_output->m_result == JavaScriptResult::FinishedWithSuccess,
+ qWarning() << currentItem.m_output->m_output);
+ continue;
+ }
+ currentItem.m_output = evaluate(input.m_input);
+ }
}
-void JavaScriptFilter::accept(const LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const
+void JavaScriptThread::cancel()
{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
+ QMutexLocker locker(&m_mutex);
+ m_canceled = true;
+ if (m_engine) // we may be canceling before the run() started
+ m_engine->setInterrupted(true);
+ m_waitCondition.wakeOne();
+}
- if (selection.internalData.isNull())
- return;
+int JavaScriptThread::addRequest(const JavaScriptInput &input)
+{
+ QMutexLocker locker(&m_mutex);
+ if (input.m_reset) {
+ if (m_currentItem) {
+ m_outputQueue += *m_currentItem;
+ m_engine->setInterrupted(true);
+ }
+ m_outputQueue += m_inputQueue;
+ m_currentItem = {};
+ m_inputQueue.clear();
+ for (int i = 0; i < m_outputQueue.size(); ++i)
+ m_outputQueue[i].m_output = {{}, JavaScriptResult::Canceled};
+ QMetaObject::invokeMethod(this, &JavaScriptThread::newOutput, Qt::QueuedConnection);
+ }
+ m_inputQueue.append({++m_maxId, input});
+ m_waitCondition.wakeOne();
+ return m_maxId;
+}
- const EngineAction action = selection.internalData.value<EngineAction>();
- if (action == EngineAction::Reset) {
- m_engine.reset();
+void JavaScriptThread::removeRequest(int id)
+{
+ QMutexLocker locker(&m_mutex);
+ if (m_currentItem && m_currentItem->m_id == id) {
+ m_currentItem = {};
+ m_engine->setInterrupted(true);
+ m_waitCondition.wakeOne();
return;
}
- if (action == EngineAction::Abort)
+ const auto predicate = [id](const QueueItem &item) { return item.m_id == id; };
+ if (Utils::eraseOne(m_inputQueue, predicate))
return;
+ Utils::eraseOne(m_outputQueue, predicate);
+}
+
+void JavaScriptThread::flush()
+{
+ const QList<QueueItem> outputQueue = takeOutputQueue();
+ for (const QueueItem &item : outputQueue) {
+ if (item.m_input.m_callback)
+ item.m_input.m_callback(*item.m_output);
+ }
+}
+
+class JavaScriptEngine : public QObject
+{
+ Q_OBJECT
- QClipboard *clipboard = QGuiApplication::clipboard();
- clipboard->setText(selection.internalData.toString());
+public:
+ JavaScriptEngine() : m_javaScriptThread(new JavaScriptThread) {
+ connect(m_javaScriptThread, &JavaScriptThread::newOutput, this, [this] {
+ m_javaScriptThread->flush();
+ });
+ m_javaScriptThread->moveToThread(&m_thread);
+ QObject::connect(&m_thread, &QThread::finished, m_javaScriptThread, &QObject::deleteLater);
+ m_thread.start();
+ QMetaObject::invokeMethod(m_javaScriptThread, &JavaScriptThread::run);
+ }
+ ~JavaScriptEngine() {
+ m_javaScriptThread->cancel();
+ m_thread.quit();
+ m_thread.wait();
+ }
+
+ int addRequest(const JavaScriptInput &input) { return m_javaScriptThread->addRequest(input); }
+ void removeRequest(int id) { m_javaScriptThread->removeRequest(id); }
+
+private:
+ QThread m_thread;
+ JavaScriptThread *m_javaScriptThread = nullptr;
+};
+
+class JavaScriptRequest : public QObject
+{
+ Q_OBJECT
+
+public:
+ virtual ~JavaScriptRequest()
+ {
+ if (m_engine && m_id) // In order to not to invoke a response callback anymore
+ m_engine->removeRequest(*m_id);
+ }
+
+ void setEngine(JavaScriptEngine *engine) {
+ QTC_ASSERT(!isRunning(), return);
+ m_engine = engine;
+ }
+ void setReset(bool reset) {
+ QTC_ASSERT(!isRunning(), return);
+ m_input.m_reset = reset; // Reply: "Engine has been reset"?
+ }
+ void setEvaluateData(const QString &input) {
+ QTC_ASSERT(!isRunning(), return);
+ m_input.m_input = input;
+ }
+ void setTimeout(std::chrono::milliseconds timeout) {
+ QTC_ASSERT(!isRunning(), return);
+ m_timeout = timeout;
+ }
+
+ void start() {
+ QTC_ASSERT(!isRunning(), return);
+ QTC_ASSERT(m_engine, return);
+
+ JavaScriptInput input = m_input;
+ input.m_callback = [this](const JavaScriptOutput &output) {
+ m_timer.reset();
+ m_output = output;
+ m_id = {};
+ emit done(output.m_result == JavaScriptResult::FinishedWithSuccess);
+ };
+ m_id = m_engine->addRequest(input);
+ if (m_timeout > 0ms) {
+ m_timer.reset(new QTimer);
+ m_timer->setSingleShot(true);
+ m_timer->setInterval(m_timeout);
+ connect(m_timer.get(), &QTimer::timeout, this, [this] {
+ if (m_engine && m_id)
+ m_engine->removeRequest(*m_id);
+ m_timer.release()->deleteLater();
+ m_id = {};
+ m_output = {Tr::tr("Engine aborted after timeout."), JavaScriptResult::Canceled};
+ emit done(false);
+ });
+ m_timer->start();
+ }
+ }
+
+ bool isRunning() const { return m_id.has_value(); }
+ JavaScriptOutput output() const { return m_output; }
+
+signals:
+ void done(bool success);
+
+private:
+ QPointer<JavaScriptEngine> m_engine;
+ JavaScriptInput m_input;
+ std::chrono::milliseconds m_timeout = 1000ms;
+
+ std::unique_ptr<QTimer> m_timer;
+
+ std::optional<int> m_id;
+ JavaScriptOutput m_output;
+};
+
+class JavaScriptRequestAdapter : public TaskAdapter<JavaScriptRequest>
+{
+public:
+ JavaScriptRequestAdapter() { connect(task(), &JavaScriptRequest::done,
+ this, &TaskInterface::done); }
+ void start() final { task()->start(); }
+};
+
+TASKING_DECLARE_TASK(JavaScriptRequestTask, JavaScriptRequestAdapter);
+
+namespace Core::Internal {
+
+JavaScriptFilter::JavaScriptFilter()
+{
+ setId("JavaScriptFilter");
+ setDisplayName(Tr::tr("Evaluate JavaScript"));
+ setDescription(Tr::tr("Evaluates arbitrary JavaScript expressions and copies the result."));
+ setDefaultShortcutString("=");
}
-void JavaScriptFilter::setupEngine()
+JavaScriptFilter::~JavaScriptFilter() = default;
+
+LocatorMatcherTasks JavaScriptFilter::matchers()
{
- m_engine.reset(new QJSEngine);
- m_engine->evaluate(
- "function abs(x) { return Math.abs(x); }\n"
- "function acos(x) { return Math.acos(x); }\n"
- "function asin(x) { return Math.asin(x); }\n"
- "function atan(x) { return Math.atan(x); }\n"
- "function atan2(x, y) { return Math.atan2(x, y); }\n"
- "function bin(x) { return '0b' + x.toString(2); }\n"
- "function ceil(x) { return Math.ceil(x); }\n"
- "function cos(x) { return Math.cos(x); }\n"
- "function exp(x) { return Math.exp(x); }\n"
- "function e() { return Math.E; }\n"
- "function floor(x) { return Math.floor(x); }\n"
- "function hex(x) { return '0x' + x.toString(16); }\n"
- "function log(x) { return Math.log(x); }\n"
- "function max() { return Math.max.apply(null, arguments); }\n"
- "function min() { return Math.min.apply(null, arguments); }\n"
- "function oct(x) { return '0' + x.toString(8); }\n"
- "function pi() { return Math.PI; }\n"
- "function pow(x, y) { return Math.pow(x, y); }\n"
- "function random() { return Math.random(); }\n"
- "function round(x) { return Math.round(x); }\n"
- "function sin(x) { return Math.sin(x); }\n"
- "function sqrt(x) { return Math.sqrt(x); }\n"
- "function tan(x) { return Math.tan(x); }\n");
+ TreeStorage<LocatorStorage> storage;
+ if (!m_javaScriptEngine)
+ m_javaScriptEngine.reset(new JavaScriptEngine);
+ QPointer<JavaScriptEngine> engine = m_javaScriptEngine.get();
+
+ const auto onSetup = [storage, engine] {
+ if (!engine)
+ return TaskAction::StopWithError;
+ if (storage->input().trimmed().isEmpty()) {
+ LocatorFilterEntry entry;
+ entry.displayName = Tr::tr("Reset Engine");
+ entry.acceptor = [engine] {
+ if (engine) {
+ JavaScriptInput request;
+ request.m_reset = true;
+ engine->addRequest(request); // TODO: timeout not handled
+ }
+ return AcceptResult();
+ };
+ storage->reportOutput({entry});
+ return TaskAction::StopWithDone;
+ }
+ return TaskAction::Continue;
+ };
+
+ const auto onJavaScriptSetup = [storage, engine](JavaScriptRequest &request) {
+ request.setEngine(engine);
+ request.setEvaluateData(storage->input());
+ };
+ const auto onJavaScriptDone = [storage](const JavaScriptRequest &request) {
+ const auto acceptor = [](const QString &clipboardContents) {
+ return [clipboardContents] {
+ QGuiApplication::clipboard()->setText(clipboardContents);
+ return AcceptResult();
+ };
+ };
+ const QString input = storage->input();
+ const QString result = request.output().m_output;
+ const QString expression = input + " = " + result;
+
+ LocatorFilterEntry entry;
+ entry.displayName = expression;
+
+ LocatorFilterEntry copyResultEntry;
+ copyResultEntry.displayName = Tr::tr("Copy to clipboard: %1").arg(result);
+ copyResultEntry.acceptor = acceptor(result);
+
+ LocatorFilterEntry copyExpressionEntry;
+ copyExpressionEntry.displayName = Tr::tr("Copy to clipboard: %1").arg(expression);
+ copyExpressionEntry.acceptor = acceptor(expression);
+
+ storage->reportOutput({entry, copyResultEntry, copyExpressionEntry});
+ };
+ const auto onJavaScriptError = [storage](const JavaScriptRequest &request) {
+ LocatorFilterEntry entry;
+ entry.displayName = request.output().m_output;
+ storage->reportOutput({entry});
+ };
+
+ const Group root {
+ onGroupSetup(onSetup),
+ JavaScriptRequestTask(onJavaScriptSetup, onJavaScriptDone, onJavaScriptError)
+ };
+
+ return {{root, storage}};
}
-} // namespace Internal
-} // namespace Core
+} // namespace Core::Internal
-Q_DECLARE_METATYPE(Core::Internal::EngineAction)
+#include "javascriptfilter.moc"
diff --git a/src/plugins/coreplugin/locator/javascriptfilter.h b/src/plugins/coreplugin/locator/javascriptfilter.h
index 5153e0b7b5..8b7112fd27 100644
--- a/src/plugins/coreplugin/locator/javascriptfilter.h
+++ b/src/plugins/coreplugin/locator/javascriptfilter.h
@@ -5,38 +5,19 @@
#include <coreplugin/locator/ilocatorfilter.h>
-#include <QTimer>
+class JavaScriptEngine;
-#include <atomic>
-#include <memory>
-
-QT_BEGIN_NAMESPACE
-class QJSEngine;
-QT_END_NAMESPACE
-
-namespace Core {
-namespace Internal {
+namespace Core::Internal {
class JavaScriptFilter : public Core::ILocatorFilter
{
- Q_OBJECT
public:
JavaScriptFilter();
- ~JavaScriptFilter() override;
-
- void prepareSearch(const QString &entry) override;
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const override;
+ ~JavaScriptFilter();
private:
- void setupEngine();
-
- mutable std::unique_ptr<QJSEngine> m_engine;
- QTimer m_abortTimer;
- std::atomic_bool m_aborted = false;
+ LocatorMatcherTasks matchers() final;
+ std::unique_ptr<JavaScriptEngine> m_javaScriptEngine;
};
-} // namespace Internal
-} // namespace Core
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/locator.cpp b/src/plugins/coreplugin/locator/locator.cpp
index afc060385b..2deaef9fe0 100644
--- a/src/plugins/coreplugin/locator/locator.cpp
+++ b/src/plugins/coreplugin/locator/locator.cpp
@@ -26,8 +26,10 @@
#include "../settingsdatabase.h"
#include "../statusbarmanager.h"
+#include <extensionsystem/pluginmanager.h>
+
#include <utils/algorithm.h>
-#include <utils/asynctask.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
@@ -66,6 +68,7 @@ public:
LocatorData::LocatorData()
{
+ m_urlFilter.setDescription(Tr::tr("Triggers a web search with the selected search engine."));
m_urlFilter.setDefaultShortcutString("r");
m_urlFilter.addDefaultUrl("https://www.bing.com/search?q=%1");
m_urlFilter.addDefaultUrl("https://www.google.com/search?q=%1");
@@ -75,6 +78,7 @@ LocatorData::LocatorData()
"http://en.cppreference.com/mwiki/index.php?title=Special%3ASearch&search=%1");
m_urlFilter.addDefaultUrl("https://en.wikipedia.org/w/index.php?search=%1");
+ m_bugFilter.setDescription(Tr::tr("Triggers a search in the Qt bug tracker."));
m_bugFilter.setDefaultShortcutString("bug");
m_bugFilter.addDefaultUrl("https://bugreports.qt.io/secure/QuickSearch.jspa?searchString=%1");
}
@@ -143,13 +147,10 @@ bool Locator::delayedInitialize()
return true;
}
-ExtensionSystem::IPlugin::ShutdownFlag Locator::aboutToShutdown(
- const std::function<void()> &emitAsynchronousShutdownFinished)
+void Locator::aboutToShutdown()
{
- m_shuttingDown = true;
m_refreshTimer.stop();
m_taskTree.reset();
- return LocatorWidget::aboutToShutdown(emitAsynchronousShutdownFinished);
}
void Locator::loadSettings()
@@ -373,7 +374,7 @@ void Locator::setUseCenteredPopupForShortcut(bool center)
void Locator::refresh(const QList<ILocatorFilter *> &filters)
{
- if (m_shuttingDown)
+ if (ExtensionSystem::PluginManager::isShuttingDown())
return;
m_taskTree.reset(); // Superfluous, just for clarity. The next reset() below is enough.
@@ -382,14 +383,16 @@ void Locator::refresh(const QList<ILocatorFilter *> &filters)
using namespace Tasking;
QList<TaskItem> tasks{parallel};
for (ILocatorFilter *filter : std::as_const(m_refreshingFilters)) {
- const auto setupRefresh = [filter](AsyncTask<void> &async) {
- async.setAsyncCallData(&ILocatorFilter::refresh, filter);
- };
- const auto onRefreshDone = [this, filter](const AsyncTask<void> &async) {
- Q_UNUSED(async)
- m_refreshingFilters.removeOne(filter);
+ const auto task = filter->refreshRecipe();
+ if (!task.has_value())
+ continue;
+
+ const Group group {
+ finishAllAndDone,
+ *task,
+ onGroupDone([this, filter] { m_refreshingFilters.removeOne(filter); })
};
- tasks.append(Async<void>(setupRefresh, onRefreshDone));
+ tasks.append(group);
}
m_taskTree.reset(new TaskTree{tasks});
diff --git a/src/plugins/coreplugin/locator/locator.h b/src/plugins/coreplugin/locator/locator.h
index 59da8767d8..45d4b65050 100644
--- a/src/plugins/coreplugin/locator/locator.h
+++ b/src/plugins/coreplugin/locator/locator.h
@@ -13,7 +13,7 @@
#include <functional>
-namespace Utils { class TaskTree; }
+namespace Tasking { class TaskTree; }
namespace Core {
namespace Internal {
@@ -30,8 +30,7 @@ public:
~Locator() override;
static Locator *instance();
- ExtensionSystem::IPlugin::ShutdownFlag aboutToShutdown(
- const std::function<void()> &emitAsynchronousShutdownFinished);
+ void aboutToShutdown();
void initialize();
void extensionsInitialized();
@@ -68,14 +67,13 @@ private:
bool useCenteredPopup = false;
};
- bool m_shuttingDown = false;
bool m_settingsInitialized = false;
Settings m_settings;
QList<ILocatorFilter *> m_filters;
QList<ILocatorFilter *> m_customFilters;
QMap<Utils::Id, QAction *> m_filterActionMap;
QTimer m_refreshTimer;
- std::unique_ptr<Utils::TaskTree> m_taskTree;
+ std::unique_ptr<Tasking::TaskTree> m_taskTree;
QList<ILocatorFilter *> m_refreshingFilters;
};
diff --git a/src/plugins/coreplugin/locator/locator_test.cpp b/src/plugins/coreplugin/locator/locator_test.cpp
index eca70f2eaa..c83a2d8779 100644
--- a/src/plugins/coreplugin/locator/locator_test.cpp
+++ b/src/plugins/coreplugin/locator/locator_test.cpp
@@ -3,35 +3,22 @@
#include "../coreplugin.h"
-#include "basefilefilter.h"
#include "locatorfiltertest.h"
#include <coreplugin/testdatadir.h>
#include <utils/algorithm.h>
-#include <utils/filepath.h>
#include <utils/fileutils.h>
#include <QDir>
-#include <QTextStream>
#include <QtTest>
using namespace Core::Tests;
+using namespace Utils;
namespace {
QTC_DECLARE_MYTESTDATADIR("../../../tests/locators/")
-class MyBaseFileFilter : public Core::BaseFileFilter
-{
-public:
- MyBaseFileFilter(const Utils::FilePaths &theFiles)
- {
- setFileIterator(new BaseFileFilter::ListIterator(theFiles));
- }
-
- void refresh(QFutureInterface<void> &) override {}
-};
-
class ReferenceData
{
public:
@@ -53,14 +40,13 @@ void Core::Internal::CorePlugin::test_basefilefilter()
QFETCH(QStringList, testFiles);
QFETCH(QList<ReferenceData>, referenceDataList);
- MyBaseFileFilter filter(Utils::FileUtils::toFilePathList(testFiles));
- BasicLocatorFilterTest test(&filter);
-
+ LocatorFileCache cache;
+ cache.setFilePaths(FileUtils::toFilePathList(testFiles));
+ const LocatorMatcherTasks tasks = {cache.matcher()};
for (const ReferenceData &reference : std::as_const(referenceDataList)) {
- const QList<LocatorFilterEntry> filterEntries = test.matchesFor(reference.searchText);
+ const LocatorFilterEntries filterEntries = LocatorMatcher::runBlocking(
+ tasks, reference.searchText);
const ResultDataList results = ResultData::fromFilterEntryList(filterEntries);
-// QTextStream(stdout) << "----" << endl;
-// ResultData::printFilterEntries(results);
QCOMPARE(results, reference.results);
}
}
@@ -68,7 +54,7 @@ void Core::Internal::CorePlugin::test_basefilefilter()
void Core::Internal::CorePlugin::test_basefilefilter_data()
{
auto shortNativePath = [](const QString &file) {
- return Utils::FilePath::fromString(file).shortNativePath();
+ return FilePath::fromString(file).shortNativePath();
};
QTest::addColumn<QStringList>("testFiles");
diff --git a/src/plugins/coreplugin/locator/locatorfiltersfilter.cpp b/src/plugins/coreplugin/locator/locatorfiltersfilter.cpp
index 800c9171db..782cb3f557 100644
--- a/src/plugins/coreplugin/locator/locatorfiltersfilter.cpp
+++ b/src/plugins/coreplugin/locator/locatorfiltersfilter.cpp
@@ -7,10 +7,9 @@
#include "../actionmanager/actionmanager.h"
#include "../coreplugintr.h"
-#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
-Q_DECLARE_METATYPE(Core::ILocatorFilter*)
+using namespace Utils;
namespace Core::Internal {
@@ -25,65 +24,45 @@ LocatorFiltersFilter::LocatorFiltersFilter():
setConfigurable(false);
}
-void LocatorFiltersFilter::prepareSearch(const QString &entry)
+LocatorMatcherTasks LocatorFiltersFilter::matchers()
{
- m_filterShortcutStrings.clear();
- m_filterDisplayNames.clear();
- m_filterDescriptions.clear();
- if (!entry.isEmpty())
- return;
+ using namespace Tasking;
- QMap<QString, ILocatorFilter *> uniqueFilters;
- const QList<ILocatorFilter *> allFilters = Locator::filters();
- for (ILocatorFilter *filter : allFilters) {
- const QString filterId = filter->shortcutString() + ',' + filter->displayName();
- uniqueFilters.insert(filterId, filter);
- }
+ TreeStorage<LocatorStorage> storage;
- for (ILocatorFilter *filter : std::as_const(uniqueFilters)) {
- if (!filter->shortcutString().isEmpty() && !filter->isHidden() && filter->isEnabled()) {
- m_filterShortcutStrings.append(filter->shortcutString());
- m_filterDisplayNames.append(filter->displayName());
- m_filterDescriptions.append(filter->description());
- QString keyboardShortcut;
- if (auto command = ActionManager::command(filter->actionId()))
- keyboardShortcut = command->keySequence().toString(QKeySequence::NativeText);
- m_filterKeyboardShortcuts.append(keyboardShortcut);
- }
- }
-}
+ const auto onSetup = [storage, icon = m_icon] {
+ if (!storage->input().isEmpty())
+ return;
-QList<LocatorFilterEntry> LocatorFiltersFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future, const QString &entry)
-{
- Q_UNUSED(entry) // search is already done in the GUI thread in prepareSearch
- QList<LocatorFilterEntry> entries;
- for (int i = 0; i < m_filterShortcutStrings.size(); ++i) {
- if (future.isCanceled())
- break;
- LocatorFilterEntry filterEntry(this,
- m_filterShortcutStrings.at(i),
- i,
- m_icon);
- filterEntry.extraInfo = m_filterDisplayNames.at(i);
- filterEntry.toolTip = m_filterDescriptions.at(i);
- filterEntry.displayExtra = m_filterKeyboardShortcuts.at(i);
- entries.append(filterEntry);
- }
- return entries;
-}
+ QMap<QString, ILocatorFilter *> uniqueFilters;
+ const QList<ILocatorFilter *> allFilters = Locator::filters();
+ for (ILocatorFilter *filter : allFilters) {
+ const QString filterId = filter->shortcutString() + ',' + filter->displayName();
+ uniqueFilters.insert(filterId, filter);
+ }
-void LocatorFiltersFilter::accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const
-{
- Q_UNUSED(selectionLength)
- bool ok;
- int index = selection.internalData.toInt(&ok);
- QTC_ASSERT(ok && index >= 0 && index < m_filterShortcutStrings.size(), return);
- const QString shortcutString = m_filterShortcutStrings.at(index);
- if (!shortcutString.isEmpty()) {
- *newText = shortcutString + ' ';
- *selectionStart = shortcutString.length() + 1;
- }
+ LocatorFilterEntries entries;
+ for (ILocatorFilter *filter : std::as_const(uniqueFilters)) {
+ const QString shortcutString = filter->shortcutString();
+ if (!shortcutString.isEmpty() && !filter->isHidden() && filter->isEnabled()) {
+ LocatorFilterEntry entry;
+ entry.displayName = shortcutString;
+ entry.acceptor = [shortcutString] {
+ return AcceptResult{shortcutString + ' ', int(shortcutString.size() + 1)};
+ };
+ entry.displayIcon = icon;
+ entry.extraInfo = filter->displayName();
+ entry.toolTip = filter->description();
+ QString keyboardShortcut;
+ if (auto command = ActionManager::command(filter->actionId()))
+ keyboardShortcut = command->keySequence().toString(QKeySequence::NativeText);
+ entry.displayExtra = keyboardShortcut;
+ entries.append(entry);
+ }
+ }
+ storage->reportOutput(entries);
+ };
+ return {{Sync(onSetup), storage}};
}
} // Core::Internal
diff --git a/src/plugins/coreplugin/locator/locatorfiltersfilter.h b/src/plugins/coreplugin/locator/locatorfiltersfilter.h
index f4679ec9c1..f6dea092eb 100644
--- a/src/plugins/coreplugin/locator/locatorfiltersfilter.h
+++ b/src/plugins/coreplugin/locator/locatorfiltersfilter.h
@@ -5,12 +5,7 @@
#include "ilocatorfilter.h"
-#include <QIcon>
-
-namespace Core {
-namespace Internal {
-
-class Locator;
+namespace Core::Internal {
/*!
This filter provides the user with the list of available Locator filters.
@@ -18,19 +13,12 @@ class Locator;
*/
class LocatorFiltersFilter : public ILocatorFilter
{
- Q_OBJECT
-
public:
LocatorFiltersFilter();
- // ILocatorFilter
- void prepareSearch(const QString &entry) override;
- QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
-
private:
+ LocatorMatcherTasks matchers() final;
+
QStringList m_filterShortcutStrings;
QStringList m_filterDisplayNames;
QStringList m_filterDescriptions;
@@ -38,5 +26,4 @@ private:
QIcon m_icon;
};
-} // namespace Internal
-} // namespace Core
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/locatorfiltertest.cpp b/src/plugins/coreplugin/locator/locatorfiltertest.cpp
index c134db1669..152ac11733 100644
--- a/src/plugins/coreplugin/locator/locatorfiltertest.cpp
+++ b/src/plugins/coreplugin/locator/locatorfiltertest.cpp
@@ -3,53 +3,12 @@
#include "locatorfiltertest.h"
-#include "locatorsearchutils.h"
-
-#include <utils/runextensions.h>
-
-#include <QFuture>
-#include <QList>
-#include <QString>
#include <QTextStream>
using namespace Core;
using namespace Core::Tests;
/*!
- \class Core::Tests::BasicLocatorFilterTest
- \inmodule QtCreator
- \internal
-*/
-
-/*!
- \class Core::Tests::TestDataDir
- \inmodule QtCreator
- \internal
-*/
-
-/*!
- \namespace Core::Tests
- \inmodule QtCreator
- \internal
-*/
-BasicLocatorFilterTest::BasicLocatorFilterTest(ILocatorFilter *filter) : m_filter(filter)
-{
-}
-
-BasicLocatorFilterTest::~BasicLocatorFilterTest() = default;
-
-QList<LocatorFilterEntry> BasicLocatorFilterTest::matchesFor(const QString &searchText)
-{
- doBeforeLocatorRun();
- m_filter->prepareSearch(searchText);
- QFuture<LocatorFilterEntry> locatorSearch = Utils::runAsync(
- &Internal::runSearch, QList<ILocatorFilter *>({m_filter}), searchText);
- locatorSearch.waitForFinished();
- doAfterLocatorRun();
- return locatorSearch.results();
-}
-
-/*!
\class Core::Tests::ResultData
\inmodule QtCreator
\internal
@@ -71,7 +30,7 @@ bool ResultData::operator==(const ResultData &other) const
return textColumn1 == other.textColumn1 && textColumn2 == other.textColumn2 && highlightEqual;
}
-ResultData::ResultDataList ResultData::fromFilterEntryList(const QList<LocatorFilterEntry> &entries)
+ResultData::ResultDataList ResultData::fromFilterEntryList(const LocatorFilterEntries &entries)
{
ResultDataList result;
for (const LocatorFilterEntry &entry : entries) {
diff --git a/src/plugins/coreplugin/locator/locatorfiltertest.h b/src/plugins/coreplugin/locator/locatorfiltertest.h
index f6d5d59501..2ad18ac236 100644
--- a/src/plugins/coreplugin/locator/locatorfiltertest.h
+++ b/src/plugins/coreplugin/locator/locatorfiltertest.h
@@ -10,22 +10,6 @@
namespace Core {
namespace Tests {
-/// Runs a locator filter for a search text and returns the results.
-class CORE_EXPORT BasicLocatorFilterTest
-{
-public:
- BasicLocatorFilterTest(ILocatorFilter *filter);
- virtual ~BasicLocatorFilterTest();
-
- QList<LocatorFilterEntry> matchesFor(const QString &searchText = QString());
-
-private:
- virtual void doBeforeLocatorRun() {}
- virtual void doAfterLocatorRun() {}
-
- ILocatorFilter *m_filter = nullptr;
-};
-
class CORE_EXPORT ResultData
{
public:
@@ -37,7 +21,7 @@ public:
bool operator==(const ResultData &other) const;
- static ResultDataList fromFilterEntryList(const QList<LocatorFilterEntry> &entries);
+ static ResultDataList fromFilterEntryList(const LocatorFilterEntries &entries);
/// For debugging and creating reference data
static void printFilterEntries(const ResultDataList &entries, const QString &msg = QString());
diff --git a/src/plugins/coreplugin/locator/locatorsearchutils.cpp b/src/plugins/coreplugin/locator/locatorsearchutils.cpp
deleted file mode 100644
index 77e460ad4f..0000000000
--- a/src/plugins/coreplugin/locator/locatorsearchutils.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "locatorsearchutils.h"
-
-#include <utils/link.h>
-
-#include <unordered_set>
-
-void Core::Internal::runSearch(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QList<ILocatorFilter *> &filters, const QString &searchText)
-{
- std::unordered_set<Utils::FilePath> addedCache;
- const bool checkDuplicates = (filters.size() > 1);
- const auto duplicatesRemoved = [&](const QList<LocatorFilterEntry> &entries) {
- if (!checkDuplicates)
- return entries;
- QList<LocatorFilterEntry> results;
- results.reserve(entries.size());
- for (const LocatorFilterEntry &entry : entries) {
- const auto &link = entry.linkForEditor;
- if (!link || addedCache.emplace(link->targetFilePath).second)
- results.append(entry);
- }
- return results;
- };
-
- for (ILocatorFilter *filter : filters) {
- if (future.isCanceled())
- break;
- const auto results = duplicatesRemoved(filter->matchesFor(future, searchText));
- if (!results.isEmpty())
- future.reportResults(results);
- }
-}
diff --git a/src/plugins/coreplugin/locator/locatorsearchutils.h b/src/plugins/coreplugin/locator/locatorsearchutils.h
deleted file mode 100644
index d863b580a6..0000000000
--- a/src/plugins/coreplugin/locator/locatorsearchutils.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "ilocatorfilter.h"
-
-namespace Core {
-namespace Internal {
-
-void CORE_EXPORT runSearch(QFutureInterface<LocatorFilterEntry> &future,
- const QList<ILocatorFilter *> &filters,
- const QString &searchText);
-
-} // namespace Internal
-} // namespace Core
diff --git a/src/plugins/coreplugin/locator/locatorsettingspage.cpp b/src/plugins/coreplugin/locator/locatorsettingspage.cpp
index 0193a791c3..74c3b45692 100644
--- a/src/plugins/coreplugin/locator/locatorsettingspage.cpp
+++ b/src/plugins/coreplugin/locator/locatorsettingspage.cpp
@@ -20,12 +20,15 @@
#include <utils/qtcassert.h>
#include <utils/treemodel.h>
+#include <QAbstractTextDocumentLayout>
#include <QHash>
#include <QHeaderView>
#include <QLabel>
#include <QMenu>
+#include <QPainter>
#include <QPushButton>
#include <QSpinBox>
+#include <QStyledItemDelegate>
using namespace Utils;
@@ -77,8 +80,14 @@ QVariant FilterItem::data(int column, int role) const
{
switch (column) {
case FilterName:
- if (role == Qt::DisplayRole || role == SortRole)
+ if (role == SortRole)
return m_filter->displayName();
+ if (role == Qt::DisplayRole) {
+ if (m_filter->description().isEmpty())
+ return m_filter->displayName();
+ return QString("<html>%1<br/><span style=\"font-weight: 70\">%2</span>")
+ .arg(m_filter->displayName(), m_filter->description().toHtmlEscaped());
+ }
break;
case FilterPrefix:
if (role == Qt::DisplayRole || role == SortRole || role == Qt::EditRole)
@@ -92,8 +101,10 @@ QVariant FilterItem::data(int column, int role) const
break;
}
- if (role == Qt::ToolTipRole)
- return m_filter->description();
+ if (role == Qt::ToolTipRole) {
+ const QString description = m_filter->description();
+ return description.isEmpty() ? QString() : ("<html>" + description.toHtmlEscaped());
+ }
return QVariant();
}
@@ -146,6 +157,97 @@ QVariant CategoryItem::data(int column, int role) const
return QVariant();
}
+class RichTextDelegate : public QStyledItemDelegate
+{
+public:
+ RichTextDelegate(QObject *parent);
+ ~RichTextDelegate();
+
+ QTextDocument &doc() { return m_doc; }
+
+ void setMaxWidth(int width);
+ int maxWidth() const;
+
+private:
+ void paint(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override;
+ QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
+
+ int m_maxWidth = -1;
+ mutable QTextDocument m_doc;
+};
+
+RichTextDelegate::RichTextDelegate(QObject *parent)
+ : QStyledItemDelegate(parent)
+{}
+
+void RichTextDelegate::setMaxWidth(int width)
+{
+ m_maxWidth = width;
+ emit sizeHintChanged({});
+}
+
+int RichTextDelegate::maxWidth() const
+{
+ return m_maxWidth;
+}
+
+RichTextDelegate::~RichTextDelegate() = default;
+
+void RichTextDelegate::paint(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ QStyleOptionViewItem options = option;
+ initStyleOption(&options, index);
+
+ painter->save();
+ QTextOption textOption;
+ if (m_maxWidth > 0) {
+ textOption.setWrapMode(QTextOption::WordWrap);
+ m_doc.setDefaultTextOption(textOption);
+ if (options.rect.width() > m_maxWidth)
+ options.rect.setWidth(m_maxWidth);
+ }
+ m_doc.setHtml(options.text);
+ m_doc.setTextWidth(options.rect.width());
+ options.text = "";
+ options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter);
+ painter->translate(options.rect.left(), options.rect.top());
+ QRect clip(0, 0, options.rect.width(), options.rect.height());
+ QAbstractTextDocumentLayout::PaintContext paintContext;
+ paintContext.palette = options.palette;
+ painter->setClipRect(clip);
+ paintContext.clip = clip;
+ if (qobject_cast<const QAbstractItemView *>(options.widget)->selectionModel()->isSelected(index)) {
+ QAbstractTextDocumentLayout::Selection selection;
+ selection.cursor = QTextCursor(&m_doc);
+ selection.cursor.select(QTextCursor::Document);
+ selection.format.setBackground(options.palette.brush(QPalette::Highlight));
+ selection.format.setForeground(options.palette.brush(QPalette::HighlightedText));
+ paintContext.selections << selection;
+ }
+ m_doc.documentLayout()->draw(painter, paintContext);
+ painter->restore();
+}
+
+QSize RichTextDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ QStyleOptionViewItem options = option;
+ initStyleOption(&options, index);
+ QTextOption textOption;
+ if (m_maxWidth > 0) {
+ textOption.setWrapMode(QTextOption::WordWrap);
+ m_doc.setDefaultTextOption(textOption);
+ if (!options.rect.isValid() || options.rect.width() > m_maxWidth)
+ options.rect.setWidth(m_maxWidth);
+ }
+ m_doc.setHtml(options.text);
+ m_doc.setTextWidth(options.rect.width());
+ return QSize(m_doc.idealWidth(), m_doc.size().height());
+}
+
class LocatorSettingsWidget : public IOptionsPageWidget
{
public:
@@ -178,8 +280,17 @@ public:
m_filterList->setSelectionMode(QAbstractItemView::SingleSelection);
m_filterList->setSelectionBehavior(QAbstractItemView::SelectRows);
m_filterList->setSortingEnabled(true);
- m_filterList->setUniformRowHeights(true);
m_filterList->setActivationMode(Utils::DoubleClickActivation);
+ m_filterList->setAlternatingRowColors(true);
+ auto nameDelegate = new RichTextDelegate(m_filterList);
+ connect(m_filterList->header(),
+ &QHeaderView::sectionResized,
+ nameDelegate,
+ [nameDelegate](int col, [[maybe_unused]] int old, int updated) {
+ if (col == 0)
+ nameDelegate->setMaxWidth(updated);
+ });
+ m_filterList->setItemDelegateForColumn(0, nameDelegate);
m_model = new TreeModel<>(m_filterList);
initializeModel();
@@ -230,12 +341,13 @@ public:
auto addMenu = new QMenu(addButton);
addMenu->addAction(Tr::tr("Files in Directories"), this, [this] {
- addCustomFilter(new DirectoryFilter(Id(Constants::CUSTOM_DIRECTORY_FILTER_BASEID)
+ addCustomFilter(new DirectoryFilter(Utils::Id(Constants::CUSTOM_DIRECTORY_FILTER_BASEID)
.withSuffix(m_customFilters.size() + 1)));
});
addMenu->addAction(Tr::tr("URL Template"), this, [this] {
auto filter = new UrlLocatorFilter(
- Id(Constants::CUSTOM_URL_FILTER_BASEID).withSuffix(m_customFilters.size() + 1));
+ Utils::Id(Constants::CUSTOM_URL_FILTER_BASEID)
+ .withSuffix(m_customFilters.size() + 1));
filter->setIsCustomFilter(true);
addCustomFilter(filter);
});
diff --git a/src/plugins/coreplugin/locator/locatorsettingspage.h b/src/plugins/coreplugin/locator/locatorsettingspage.h
index 4818fc2e23..408fe7b91f 100644
--- a/src/plugins/coreplugin/locator/locatorsettingspage.h
+++ b/src/plugins/coreplugin/locator/locatorsettingspage.h
@@ -5,16 +5,12 @@
#include <coreplugin/dialogs/ioptionspage.h>
-namespace Core {
-namespace Internal {
+namespace Core::Internal {
class LocatorSettingsPage : public IOptionsPage
{
- Q_OBJECT
-
public:
LocatorSettingsPage();
};
-} // namespace Internal
-} // namespace Core
+} // Core::Internal
diff --git a/src/plugins/coreplugin/locator/locatorwidget.cpp b/src/plugins/coreplugin/locator/locatorwidget.cpp
index ba717ea0f9..8af3f4b753 100644
--- a/src/plugins/coreplugin/locator/locatorwidget.cpp
+++ b/src/plugins/coreplugin/locator/locatorwidget.cpp
@@ -4,17 +4,15 @@
#include "locatorwidget.h"
#include "ilocatorfilter.h"
-#include "locator.h"
#include "locatorconstants.h"
#include "locatormanager.h"
-#include "locatorsearchutils.h"
#include "../actionmanager/actionmanager.h"
#include "../coreplugintr.h"
+#include "../editormanager/editormanager.h"
#include "../icore.h"
#include "../modemanager.h"
#include <utils/algorithm.h>
-#include <utils/appmainwindow.h>
#include <utils/fancylineedit.h>
#include <utils/fsengine/fileiconprovider.h>
#include <utils/highlightingitemdelegate.h>
@@ -22,24 +20,20 @@
#include <utils/itemviews.h>
#include <utils/progressindicator.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
-#include <utils/stylehelper.h>
+#include <utils/tooltip/tooltip.h>
#include <utils/utilsicons.h>
#include <QAction>
#include <QApplication>
#include <QColor>
#include <QEvent>
-#include <QFileInfo>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QKeyEvent>
+#include <QMainWindow>
#include <QMenu>
-#include <QScreen>
#include <QScrollBar>
-#include <QTimer>
#include <QToolTip>
-#include <QTreeView>
Q_DECLARE_METATYPE(Core::LocatorFilterEntry)
@@ -50,10 +44,6 @@ const int LocatorEntryRole = int(HighlightingItemRole::User);
namespace Core {
namespace Internal {
-bool LocatorWidget::m_shuttingDown = false;
-QFuture<void> LocatorWidget::m_sharedFuture;
-LocatorWidget *LocatorWidget::m_sharedFutureOrigin = nullptr;
-
/* A model to represent the Locator results. */
class LocatorModel : public QAbstractListModel
{
@@ -67,8 +57,8 @@ public:
LocatorModel(QObject *parent = nullptr)
: QAbstractListModel(parent)
- , mBackgroundColor(Utils::creatorTheme()->color(Utils::Theme::TextColorHighlightBackground))
- , mForegroundColor(Utils::creatorTheme()->color(Utils::Theme::TextColorNormal))
+ , m_backgroundColor(Utils::creatorTheme()->color(Theme::TextColorHighlightBackground))
+ , m_foregroundColor(Utils::creatorTheme()->color(Theme::TextColorNormal))
{}
void clear();
@@ -76,13 +66,13 @@ public:
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
- void addEntries(const QList<LocatorFilterEntry> &entries);
+ void addEntries(const LocatorFilterEntries &entries);
private:
- mutable QList<LocatorFilterEntry> mEntries;
- bool hasExtraInfo = false;
- QColor mBackgroundColor;
- QColor mForegroundColor;
+ mutable LocatorFilterEntries m_entries;
+ bool m_hasExtraInfo = false;
+ QColor m_backgroundColor;
+ QColor m_foregroundColor;
};
class CompletionDelegate : public HighlightingItemDelegate
@@ -93,7 +83,7 @@ public:
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};
-class CompletionList : public Utils::TreeView
+class CompletionList : public TreeView
{
public:
CompletionList(QWidget *parent = nullptr);
@@ -144,8 +134,8 @@ protected:
void LocatorModel::clear()
{
beginResetModel();
- mEntries.clear();
- hasExtraInfo = false;
+ m_entries.clear();
+ m_hasExtraInfo = false;
endResetModel();
}
@@ -153,30 +143,30 @@ int LocatorModel::rowCount(const QModelIndex & parent) const
{
if (parent.isValid())
return 0;
- return mEntries.size();
+ return m_entries.size();
}
int LocatorModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
- return hasExtraInfo ? ColumnCount : 1;
+ return m_hasExtraInfo ? ColumnCount : 1;
}
QVariant LocatorModel::data(const QModelIndex &index, int role) const
{
- if (!index.isValid() || index.row() >= mEntries.size())
+ if (!index.isValid() || index.row() >= m_entries.size())
return QVariant();
switch (role) {
case Qt::DisplayRole:
if (index.column() == DisplayNameColumn)
- return mEntries.at(index.row()).displayName;
+ return m_entries.at(index.row()).displayName;
else if (index.column() == ExtraInfoColumn)
- return mEntries.at(index.row()).extraInfo;
+ return m_entries.at(index.row()).extraInfo;
break;
case Qt::ToolTipRole: {
- const LocatorFilterEntry &entry = mEntries.at(index.row());
+ const LocatorFilterEntry &entry = m_entries.at(index.row());
QString toolTip = entry.displayName;
if (!entry.extraInfo.isEmpty())
toolTip += "\n\n" + entry.extraInfo;
@@ -186,7 +176,7 @@ QVariant LocatorModel::data(const QModelIndex &index, int role) const
}
case Qt::DecorationRole:
if (index.column() == DisplayNameColumn) {
- LocatorFilterEntry &entry = mEntries[index.row()];
+ LocatorFilterEntry &entry = m_entries[index.row()];
if (!entry.displayIcon && !entry.filePath.isEmpty())
entry.displayIcon = FileIconProvider::icon(entry.filePath);
return entry.displayIcon ? entry.displayIcon.value() : QIcon();
@@ -197,10 +187,10 @@ QVariant LocatorModel::data(const QModelIndex &index, int role) const
return QColor(Qt::darkGray);
break;
case LocatorEntryRole:
- return QVariant::fromValue(mEntries.at(index.row()));
+ return QVariant::fromValue(m_entries.at(index.row()));
case int(HighlightingItemRole::StartColumn):
case int(HighlightingItemRole::Length): {
- const LocatorFilterEntry &entry = mEntries[index.row()];
+ const LocatorFilterEntry &entry = m_entries[index.row()];
auto highlights = [&](LocatorFilterEntry::HighlightInfo::DataType type){
const bool startIndexRole = role == int(HighlightingItemRole::StartColumn);
return startIndexRole ? QVariant::fromValue(entry.highlightInfo.starts(type))
@@ -214,7 +204,7 @@ QVariant LocatorModel::data(const QModelIndex &index, int role) const
}
case int(HighlightingItemRole::DisplayExtra): {
if (index.column() == LocatorFilterEntry::HighlightInfo::DisplayName) {
- LocatorFilterEntry &entry = mEntries[index.row()];
+ LocatorFilterEntry &entry = m_entries[index.row()];
if (!entry.displayExtra.isEmpty())
return QString(" (" + entry.displayExtra + ')');
}
@@ -223,9 +213,9 @@ QVariant LocatorModel::data(const QModelIndex &index, int role) const
case int(HighlightingItemRole::DisplayExtraForeground):
return QColor(Qt::darkGray);
case int(HighlightingItemRole::Background):
- return mBackgroundColor;
+ return m_backgroundColor;
case int(HighlightingItemRole::Foreground):
- return mForegroundColor;
+ return m_foregroundColor;
}
return QVariant();
@@ -233,14 +223,14 @@ QVariant LocatorModel::data(const QModelIndex &index, int role) const
void LocatorModel::addEntries(const QList<LocatorFilterEntry> &entries)
{
- beginInsertRows(QModelIndex(), mEntries.size(), mEntries.size() + entries.size() - 1);
- mEntries.append(entries);
+ beginInsertRows(QModelIndex(), m_entries.size(), m_entries.size() + entries.size() - 1);
+ m_entries.append(entries);
endInsertRows();
- if (hasExtraInfo)
+ if (m_hasExtraInfo)
return;
if (Utils::anyOf(entries, [](const LocatorFilterEntry &e) { return !e.extraInfo.isEmpty();})) {
beginInsertColumns(QModelIndex(), 1, 1);
- hasExtraInfo = true;
+ m_hasExtraInfo = true;
endInsertColumns();
}
}
@@ -248,7 +238,7 @@ void LocatorModel::addEntries(const QList<LocatorFilterEntry> &entries)
// =========== CompletionList ===========
CompletionList::CompletionList(QWidget *parent)
- : Utils::TreeView(parent)
+ : TreeView(parent)
{
// on macOS and Windows the popup doesn't really get focus, so fake the selection color
// which would then just be a very light gray, but should look as if it had focus
@@ -265,7 +255,7 @@ CompletionList::CompletionList(QWidget *parent)
header()->setStretchLastSection(true);
// This is too slow when done on all results
//header()->setSectionResizeMode(QHeaderView::ResizeToContents);
- if (Utils::HostOsInfo::isMacHost()) {
+ if (HostOsInfo::isMacHost()) {
if (horizontalScrollBar())
horizontalScrollBar()->setAttribute(Qt::WA_MacMiniSize);
if (verticalScrollBar())
@@ -416,7 +406,7 @@ LocatorPopup::LocatorPopup(LocatorWidget *locatorWidget, QWidget *parent)
m_tree(new CompletionList(this)),
m_inputWidget(locatorWidget)
{
- if (Utils::HostOsInfo::isMacHost())
+ if (HostOsInfo::isMacHost())
m_tree->setFrameStyle(QFrame::NoFrame); // tool tip already includes a frame
m_tree->setModel(locatorWidget->model());
m_tree->setTextElideMode(Qt::ElideMiddle);
@@ -444,9 +434,10 @@ LocatorPopup::LocatorPopup(LocatorWidget *locatorWidget, QWidget *parent)
}, Qt::DirectConnection); // must be handled directly before event is deleted
connect(m_tree, &QAbstractItemView::activated, locatorWidget,
[this, locatorWidget](const QModelIndex &index) {
- if (isVisible())
- locatorWidget->scheduleAcceptEntry(index);
- });
+ if (!index.isValid() || !isVisible())
+ return;
+ locatorWidget->acceptEntry(index.row());
+ });
}
CompletionList *LocatorPopup::completionList() const
@@ -459,29 +450,28 @@ LocatorWidget *LocatorPopup::inputWidget() const
return m_inputWidget;
}
-void LocatorPopup::focusOutEvent(QFocusEvent *event) {
+void LocatorPopup::focusOutEvent(QFocusEvent *event)
+{
if (event->reason() == Qt::ActiveWindowFocusReason)
hide();
QWidget::focusOutEvent(event);
}
-void CompletionList::next() {
+void CompletionList::next()
+{
int index = currentIndex().row();
++index;
- if (index >= model()->rowCount(QModelIndex())) {
- // wrap
- index = 0;
- }
+ if (index >= model()->rowCount(QModelIndex()))
+ index = 0; // wrap
setCurrentIndex(model()->index(index, 0));
}
-void CompletionList::previous() {
+void CompletionList::previous()
+{
int index = currentIndex().row();
--index;
- if (index < 0) {
- // wrap
- index = model()->rowCount(QModelIndex()) - 1;
- }
+ if (index < 0)
+ index = model()->rowCount(QModelIndex()) - 1; // wrap
setCurrentIndex(model()->index(index, 0));
}
@@ -510,7 +500,7 @@ void CompletionList::keyPressEvent(QKeyEvent *event)
return;
case Qt::Key_P:
case Qt::Key_N:
- if (event->modifiers() == Qt::KeyboardModifiers(Utils::HostOsInfo::controlModifier())) {
+ if (event->modifiers() == Qt::KeyboardModifiers(HostOsInfo::controlModifier())) {
if (event->key() == Qt::Key_P)
previous();
else
@@ -529,7 +519,7 @@ void CompletionList::keyPressEvent(QKeyEvent *event)
}
break;
}
- Utils::TreeView::keyPressEvent(event);
+ TreeView::keyPressEvent(event);
}
bool CompletionList::eventFilter(QObject *watched, QEvent *event)
@@ -545,14 +535,14 @@ bool CompletionList::eventFilter(QObject *watched, QEvent *event)
break;
case Qt::Key_P:
case Qt::Key_N:
- if (ke->modifiers() == Qt::KeyboardModifiers(Utils::HostOsInfo::controlModifier())) {
+ if (ke->modifiers() == Qt::KeyboardModifiers(HostOsInfo::controlModifier())) {
event->accept();
return true;
}
break;
}
}
- return Utils::TreeView::eventFilter(watched, event);
+ return TreeView::eventFilter(watched, event);
}
// =========== LocatorWidget ===========
@@ -563,7 +553,7 @@ LocatorWidget::LocatorWidget(Locator *locator)
, m_centeredPopupAction(new QAction(Tr::tr("Open as Centered Popup"), this))
, m_refreshAction(new QAction(Tr::tr("Refresh"), this))
, m_configureAction(new QAction(ICore::msgShowOptionsDialog(), this))
- , m_fileLineEdit(new Utils::FancyLineEdit)
+ , m_fileLineEdit(new FancyLineEdit)
{
setAttribute(Qt::WA_Hover);
setFocusProxy(m_fileLineEdit);
@@ -581,12 +571,12 @@ LocatorWidget::LocatorWidget(Locator *locator)
const QIcon icon = Utils::Icons::MAGNIFIER.icon();
m_fileLineEdit->setFiltering(true);
- m_fileLineEdit->setButtonIcon(Utils::FancyLineEdit::Left, icon);
- m_fileLineEdit->setButtonToolTip(Utils::FancyLineEdit::Left, Tr::tr("Options"));
+ m_fileLineEdit->setButtonIcon(FancyLineEdit::Left, icon);
+ m_fileLineEdit->setButtonToolTip(FancyLineEdit::Left, Tr::tr("Options"));
m_fileLineEdit->setFocusPolicy(Qt::ClickFocus);
- m_fileLineEdit->setButtonVisible(Utils::FancyLineEdit::Left, true);
+ m_fileLineEdit->setButtonVisible(FancyLineEdit::Left, true);
// We set click focus since otherwise you will always get two popups
- m_fileLineEdit->setButtonFocusPolicy(Utils::FancyLineEdit::Left, Qt::ClickFocus);
+ m_fileLineEdit->setButtonFocusPolicy(FancyLineEdit::Left, Qt::ClickFocus);
m_fileLineEdit->setAttribute(Qt::WA_MacShowFocusRect, false);
m_fileLineEdit->installEventFilter(this);
@@ -597,6 +587,10 @@ LocatorWidget::LocatorWidget(Locator *locator)
connect(m_filterMenu, &QMenu::aboutToShow, this, [this] {
m_centeredPopupAction->setChecked(Locator::useCenteredPopupForShortcut());
});
+ connect(m_filterMenu, &QMenu::hovered, this, [this](QAction *action) {
+ ToolTip::show(m_filterMenu->mapToGlobal(m_filterMenu->actionGeometry(action).topRight()),
+ action->toolTip());
+ });
connect(m_centeredPopupAction, &QAction::toggled, locator, [locator](bool toggled) {
if (toggled != Locator::useCenteredPopupForShortcut()) {
Locator::setUseCenteredPopupForShortcut(toggled);
@@ -608,27 +602,15 @@ LocatorWidget::LocatorWidget(Locator *locator)
m_filterMenu->addAction(m_refreshAction);
m_filterMenu->addAction(m_configureAction);
- m_fileLineEdit->setButtonMenu(Utils::FancyLineEdit::Left, m_filterMenu);
+ m_fileLineEdit->setButtonMenu(FancyLineEdit::Left, m_filterMenu);
connect(m_refreshAction, &QAction::triggered, locator, [locator] {
locator->refresh(Locator::filters());
});
connect(m_configureAction, &QAction::triggered, this, &LocatorWidget::showConfigureDialog);
- connect(m_fileLineEdit, &QLineEdit::textChanged,
- this, &LocatorWidget::showPopupDelayed);
-
- m_entriesWatcher = new QFutureWatcher<LocatorFilterEntry>(this);
- connect(m_entriesWatcher, &QFutureWatcher<LocatorFilterEntry>::resultsReadyAt,
- this, &LocatorWidget::addSearchResults);
- connect(m_entriesWatcher, &QFutureWatcher<LocatorFilterEntry>::finished,
- this, &LocatorWidget::handleSearchFinished);
-
- m_showPopupTimer.setInterval(100);
- m_showPopupTimer.setSingleShot(true);
- connect(&m_showPopupTimer, &QTimer::timeout, this, &LocatorWidget::showPopupNow);
+ connect(m_fileLineEdit, &QLineEdit::textChanged, this, &LocatorWidget::showPopupNow);
- m_progressIndicator = new Utils::ProgressIndicator(Utils::ProgressIndicatorSize::Small,
- m_fileLineEdit);
+ m_progressIndicator = new ProgressIndicator(ProgressIndicatorSize::Small, m_fileLineEdit);
m_progressIndicator->raise();
m_progressIndicator->hide();
m_showProgressTimer.setSingleShot(true);
@@ -638,7 +620,7 @@ LocatorWidget::LocatorWidget(Locator *locator)
Command *locateCmd = ActionManager::command(Constants::LOCATE);
if (QTC_GUARD(locateCmd)) {
- connect(locateCmd, &Command::keySequenceChanged, this, [this,locateCmd] {
+ connect(locateCmd, &Command::keySequenceChanged, this, [this, locateCmd] {
updatePlaceholderText(locateCmd);
});
updatePlaceholderText(locateCmd);
@@ -650,12 +632,7 @@ LocatorWidget::LocatorWidget(Locator *locator)
updateFilterList();
}
-LocatorWidget::~LocatorWidget()
-{
- // no need to completely finish a running search, cancel it
- if (m_entriesWatcher->future().isRunning())
- m_entriesWatcher->future().cancel();
-}
+LocatorWidget::~LocatorWidget() = default;
void LocatorWidget::updatePlaceholderText(Command *command)
{
@@ -670,12 +647,19 @@ void LocatorWidget::updatePlaceholderText(Command *command)
void LocatorWidget::updateFilterList()
{
m_filterMenu->clear();
- const QList<ILocatorFilter *> filters = Locator::filters();
+ const QList<ILocatorFilter *> filters = Utils::sorted(
+ Locator::filters(), [](ILocatorFilter *a, ILocatorFilter *b) {
+ return a->displayName() < b->displayName();
+ });
for (ILocatorFilter *filter : filters) {
if (filter->shortcutString().isEmpty() || filter->isHidden())
continue;
QAction *action = m_filterMenu->addAction(filter->displayName());
- action->setToolTip(filter->description());
+ action->setEnabled(filter->isEnabled());
+ const QString description = filter->description();
+ action->setToolTip(description.isEmpty() ? QString()
+ : ("<html>" + description.toHtmlEscaped()));
+ connect(filter, &ILocatorFilter::enabledChanged, action, &QAction::setEnabled);
connect(action, &QAction::triggered, this, [this, filter] {
Locator::showFilter(filter, this);
});
@@ -715,7 +699,7 @@ bool LocatorWidget::eventFilter(QObject *obj, QEvent *event)
switch (keyEvent->key()) {
case Qt::Key_P:
case Qt::Key_N:
- if (keyEvent->modifiers() == Qt::KeyboardModifiers(Utils::HostOsInfo::controlModifier())) {
+ if (keyEvent->modifiers() == Qt::KeyboardModifiers(HostOsInfo::controlModifier())) {
event->accept();
return true;
}
@@ -772,7 +756,7 @@ bool LocatorWidget::eventFilter(QObject *obj, QEvent *event)
return true;
case Qt::Key_Home:
case Qt::Key_End:
- if (Utils::HostOsInfo::isMacHost()
+ if (HostOsInfo::isMacHost()
!= (keyEvent->modifiers() == Qt::KeyboardModifiers(Qt::ControlModifier))) {
emit showPopup();
emit handleKey(keyEvent);
@@ -794,7 +778,7 @@ bool LocatorWidget::eventFilter(QObject *obj, QEvent *event)
break;
case Qt::Key_P:
case Qt::Key_N:
- if (keyEvent->modifiers() == Qt::KeyboardModifiers(Utils::HostOsInfo::controlModifier())) {
+ if (keyEvent->modifiers() == Qt::KeyboardModifiers(HostOsInfo::controlModifier())) {
emit showPopup();
emit handleKey(keyEvent);
return true;
@@ -847,16 +831,9 @@ bool LocatorWidget::eventFilter(QObject *obj, QEvent *event)
return QWidget::eventFilter(obj, event);
}
-void LocatorWidget::showPopupDelayed()
-{
- m_updateRequested = true;
- m_showPopupTimer.start();
-}
-
void LocatorWidget::showPopupNow()
{
- m_showPopupTimer.stop();
- updateCompletionList(m_fileLineEdit->text());
+ runMatcher(m_fileLineEdit->text());
emit showPopup();
}
@@ -894,7 +871,7 @@ void LocatorWidget::setProgressIndicatorVisible(bool visible)
return;
}
const QSize iconSize = m_progressIndicator->sizeHint();
- m_progressIndicator->setGeometry(m_fileLineEdit->button(Utils::FancyLineEdit::Right)->geometry().x()
+ m_progressIndicator->setGeometry(m_fileLineEdit->button(FancyLineEdit::Right)->geometry().x()
- iconSize.width(),
(m_fileLineEdit->height() - iconSize.height()) / 2 /*center*/,
iconSize.width(),
@@ -902,118 +879,78 @@ void LocatorWidget::setProgressIndicatorVisible(bool visible)
m_progressIndicator->show();
}
-void LocatorWidget::updateCompletionList(const QString &text)
+void LocatorWidget::runMatcher(const QString &text)
{
- if (m_shuttingDown)
- return;
-
- m_updateRequested = true;
- if (m_sharedFuture.isRunning()) {
- // Cancel the old future. We may not just block the UI thread to wait for the search to
- // actually cancel.
- m_requestedCompletionText = text;
- if (m_sharedFutureOrigin == this) {
- // This locator widget is currently running. Make handleSearchFinished trigger another
- // update.
- m_rerunAfterFinished = true;
- } else {
- // Another locator widget is running. Trigger another update when that is finished.
- Utils::onFinished(m_sharedFuture, this, [this](const QFuture<void> &) {
- const QString text = m_requestedCompletionText;
- m_requestedCompletionText.clear();
- updateCompletionList(text);
- });
- }
- m_sharedFuture.cancel();
- return;
- }
-
- m_showProgressTimer.start();
- m_needsClearResult = true;
QString searchText;
const QList<ILocatorFilter *> filters = filtersFor(text, searchText);
+ LocatorMatcherTasks tasks;
for (ILocatorFilter *filter : filters)
- filter->prepareSearch(searchText);
- QFuture<LocatorFilterEntry> future = Utils::runAsync(&runSearch, filters, searchText);
- m_sharedFuture = QFuture<void>(future);
- m_sharedFutureOrigin = this;
- m_entriesWatcher->setFuture(future);
-}
-
-void LocatorWidget::handleSearchFinished()
-{
- m_showProgressTimer.stop();
- setProgressIndicatorVisible(false);
- m_updateRequested = false;
- if (m_rowRequestedForAccept) {
- acceptEntry(m_rowRequestedForAccept.value());
- m_rowRequestedForAccept.reset();
- return;
- }
- if (m_rerunAfterFinished) {
- m_rerunAfterFinished = false;
- const QString text = m_requestedCompletionText;
- m_requestedCompletionText.clear();
- updateCompletionList(text);
- return;
- }
-
- if (m_needsClearResult) {
- m_locatorModel->clear();
- m_needsClearResult = false;
- }
-}
-
-void LocatorWidget::scheduleAcceptEntry(const QModelIndex &index)
-{
- if (m_updateRequested) {
- // don't just accept the selected entry, since the list is not up to date
- // accept will be called after the update finished
- m_rowRequestedForAccept = index.row();
- // do not wait for the rest of the search to finish
- m_entriesWatcher->future().cancel();
- } else {
- acceptEntry(index.row());
- }
-}
+ tasks += filter->matchers();
+
+ m_locatorMatcher.reset(new LocatorMatcher);
+ m_locatorMatcher->setTasks(tasks);
+ m_locatorMatcher->setInputData(searchText);
+ m_rowRequestedForAccept.reset();
+
+ std::shared_ptr<std::atomic_bool> needsClearResult = std::make_shared<std::atomic_bool>(true);
+ connect(m_locatorMatcher.get(), &LocatorMatcher::done, this, [this, needsClearResult] {
+ m_showProgressTimer.stop();
+ setProgressIndicatorVisible(false);
+ m_locatorMatcher.release()->deleteLater();
+ if (m_rowRequestedForAccept) {
+ acceptEntry(m_rowRequestedForAccept.value());
+ m_rowRequestedForAccept.reset();
+ return;
+ }
+ if (needsClearResult->exchange(false))
+ m_locatorModel->clear();
+ });
+ connect(m_locatorMatcher.get(), &LocatorMatcher::serialOutputDataReady,
+ this, [this, needsClearResult](const LocatorFilterEntries &serialOutputData) {
+ if (needsClearResult->exchange(false))
+ m_locatorModel->clear();
+ const bool selectFirst = m_locatorModel->rowCount() == 0;
+ m_locatorModel->addEntries(serialOutputData);
+ if (selectFirst) {
+ emit selectRow(0);
+ if (m_rowRequestedForAccept)
+ m_rowRequestedForAccept = 0;
+ }
+ });
-ExtensionSystem::IPlugin::ShutdownFlag LocatorWidget::aboutToShutdown(
- const std::function<void()> &emitAsynchronousShutdownFinished)
-{
- m_shuttingDown = true;
- if (m_sharedFuture.isRunning()) {
- Utils::onFinished(m_sharedFuture,
- Locator::instance(),
- [emitAsynchronousShutdownFinished](const QFuture<void> &) {
- emitAsynchronousShutdownFinished();
- });
- m_sharedFuture.cancel();
- return ExtensionSystem::IPlugin::AsynchronousShutdown;
- }
- return ExtensionSystem::IPlugin::SynchronousShutdown;
+ m_showProgressTimer.start();
+ m_locatorMatcher->start();
}
void LocatorWidget::acceptEntry(int row)
{
+ if (m_locatorMatcher) {
+ m_rowRequestedForAccept = row;
+ return;
+ }
if (row < 0 || row >= m_locatorModel->rowCount())
return;
const QModelIndex index = m_locatorModel->index(row, 0);
if (!index.isValid())
return;
- const LocatorFilterEntry entry = m_locatorModel->data(index, LocatorEntryRole).value<LocatorFilterEntry>();
- Q_ASSERT(entry.filter != nullptr);
- QString newText;
- int selectionStart = -1;
- int selectionLength = 0;
+ const LocatorFilterEntry entry
+ = m_locatorModel->data(index, LocatorEntryRole).value<LocatorFilterEntry>();
+
+ if (!entry.acceptor) {
+ // Opening editors can open dialogs (e.g. the ssh prompt, or showing erros), so delay until
+ // we have hidden the popup with emit hidePopup below and Qt actually processed that
+ QMetaObject::invokeMethod(EditorManager::instance(),
+ [entry] { EditorManager::openEditor(entry); }, Qt::QueuedConnection);
+ }
QWidget *focusBeforeAccept = QApplication::focusWidget();
- entry.filter->accept(entry, &newText, &selectionStart, &selectionLength);
- if (newText.isEmpty()) {
+ const AcceptResult result = entry.acceptor ? entry.acceptor() : AcceptResult();
+ if (result.newText.isEmpty()) {
emit hidePopup();
if (QApplication::focusWidget() == focusBeforeAccept)
resetFocus(m_previousFocusWidget, isInMainWindow());
} else {
- showText(newText, selectionStart, selectionLength);
+ showText(result.newText, result.selectionStart, result.selectionLength);
}
}
@@ -1049,24 +986,6 @@ void LocatorWidget::showConfigureDialog()
ICore::showOptionsDialog(Constants::FILTER_OPTIONS_PAGE);
}
-void LocatorWidget::addSearchResults(int firstIndex, int endIndex)
-{
- if (m_needsClearResult) {
- m_locatorModel->clear();
- m_needsClearResult = false;
- }
- const bool selectFirst = m_locatorModel->rowCount() == 0;
- QList<LocatorFilterEntry> entries;
- for (int i = firstIndex; i < endIndex; ++i)
- entries.append(m_entriesWatcher->resultAt(i));
- m_locatorModel->addEntries(entries);
- if (selectFirst) {
- emit selectRow(0);
- if (m_rowRequestedForAccept)
- m_rowRequestedForAccept = 0;
- }
-}
-
LocatorWidget *createStaticLocatorWidget(Locator *locator)
{
auto widget = new LocatorWidget(locator);
diff --git a/src/plugins/coreplugin/locator/locatorwidget.h b/src/plugins/coreplugin/locator/locatorwidget.h
index eef5a14414..c766637c3d 100644
--- a/src/plugins/coreplugin/locator/locatorwidget.h
+++ b/src/plugins/coreplugin/locator/locatorwidget.h
@@ -7,8 +7,8 @@
#include <extensionsystem/iplugin.h>
-#include <QFutureWatcher>
#include <QPointer>
+#include <QTimer>
#include <QWidget>
#include <functional>
@@ -28,8 +28,7 @@ namespace Internal {
class LocatorModel;
class CompletionList;
-class LocatorWidget
- : public QWidget
+class LocatorWidget : public QWidget
{
Q_OBJECT
@@ -42,11 +41,7 @@ public:
QAbstractItemModel *model() const;
void updatePlaceholderText(Command *command);
-
- void scheduleAcceptEntry(const QModelIndex &index);
-
- static ExtensionSystem::IPlugin::ShutdownFlag aboutToShutdown(
- const std::function<void()> &emitAsynchronousShutdownFinished);
+ void acceptEntry(int row);
signals:
void showCurrentItemToolTip();
@@ -58,44 +53,30 @@ signals:
void showPopup();
private:
- void showPopupDelayed();
void showPopupNow();
- void acceptEntry(int row);
static void showConfigureDialog();
- void addSearchResults(int firstIndex, int endIndex);
- void handleSearchFinished();
void updateFilterList();
bool isInMainWindow() const;
void updatePreviousFocusWidget(QWidget *previous, QWidget *current);
bool eventFilter(QObject *obj, QEvent *event) override;
- void updateCompletionList(const QString &text);
+ void runMatcher(const QString &text);
static QList<ILocatorFilter*> filtersFor(const QString &text, QString &searchText);
void setProgressIndicatorVisible(bool visible);
LocatorModel *m_locatorModel = nullptr;
-
- static bool m_shuttingDown;
- static QFuture<void> m_sharedFuture;
- static LocatorWidget *m_sharedFutureOrigin;
-
QMenu *m_filterMenu = nullptr;
QAction *m_centeredPopupAction = nullptr;
QAction *m_refreshAction = nullptr;
QAction *m_configureAction = nullptr;
Utils::FancyLineEdit *m_fileLineEdit = nullptr;
- QTimer m_showPopupTimer;
- QFutureWatcher<LocatorFilterEntry> *m_entriesWatcher = nullptr;
- QString m_requestedCompletionText;
- bool m_needsClearResult = true;
- bool m_updateRequested = false;
- bool m_rerunAfterFinished = false;
bool m_possibleToolTipRequest = false;
QWidget *m_progressIndicator = nullptr;
QTimer m_showProgressTimer;
std::optional<int> m_rowRequestedForAccept;
QPointer<QWidget> m_previousFocusWidget;
+ std::unique_ptr<LocatorMatcher> m_locatorMatcher;
};
class LocatorPopup : public QWidget
diff --git a/src/plugins/coreplugin/locator/opendocumentsfilter.cpp b/src/plugins/coreplugin/locator/opendocumentsfilter.cpp
index f8febc69f3..95e076ec2b 100644
--- a/src/plugins/coreplugin/locator/opendocumentsfilter.cpp
+++ b/src/plugins/coreplugin/locator/opendocumentsfilter.cpp
@@ -3,102 +3,63 @@
#include "opendocumentsfilter.h"
-#include "basefilefilter.h"
#include "../coreplugintr.h"
-#include <utils/filepath.h>
+#include <coreplugin/editormanager/documentmodel.h>
+
+#include <extensionsystem/pluginmanager.h>
+
+#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/link.h>
-#include <utils/linecolumn.h>
-#include <QAbstractItemModel>
-#include <QMutexLocker>
#include <QRegularExpression>
+using namespace Tasking;
using namespace Utils;
namespace Core::Internal {
+class Entry
+{
+public:
+ Utils::FilePath fileName;
+ QString displayName;
+};
+
OpenDocumentsFilter::OpenDocumentsFilter()
{
setId("Open documents");
setDisplayName(Tr::tr("Open Documents"));
+ setDescription(Tr::tr("Switches to an open document."));
setDefaultShortcutString("o");
setPriority(High);
setDefaultIncludedByDefault(true);
-
- connect(DocumentModel::model(), &QAbstractItemModel::dataChanged,
- this, &OpenDocumentsFilter::slotDataChanged);
- connect(DocumentModel::model(), &QAbstractItemModel::rowsInserted,
- this, &OpenDocumentsFilter::slotRowsInserted);
- connect(DocumentModel::model(), &QAbstractItemModel::rowsRemoved,
- this, &OpenDocumentsFilter::slotRowsRemoved);
-}
-
-void OpenDocumentsFilter::slotDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
- const QVector<int> &roles)
-{
- Q_UNUSED(roles)
-
- const int topIndex = std::max(0, topLeft.row() - 1 /*<no document>*/);
- const int bottomIndex = bottomRight.row() - 1 /*<no document>*/;
-
- QMutexLocker lock(&m_mutex);
-
- const QList<DocumentModel::Entry *> documentEntries = DocumentModel::entries();
- for (int i = topIndex; i <= bottomIndex; ++i) {
- QTC_ASSERT(i < m_editors.size(), break);
- DocumentModel::Entry *e = documentEntries.at(i);
- m_editors[i] = {e->filePath(), e->displayName()};
- }
-}
-
-void OpenDocumentsFilter::slotRowsInserted(const QModelIndex &, int first, int last)
-{
- const int firstIndex = std::max(0, first - 1 /*<no document>*/);
-
- QMutexLocker lock(&m_mutex);
-
- const QList<DocumentModel::Entry *> documentEntries = DocumentModel::entries();
- for (int i = firstIndex; i < last; ++i) {
- DocumentModel::Entry *e = documentEntries.at(i);
- m_editors.insert(i, {e->filePath(), e->displayName()});
- }
}
-void OpenDocumentsFilter::slotRowsRemoved(const QModelIndex &, int first, int last)
+static void matchEditors(QPromise<void> &promise, const LocatorStorage &storage,
+ const QList<Entry> &editorsData)
{
- QMutexLocker lock(&m_mutex);
-
- const int firstIndex = std::max(0, first - 1 /*<no document>*/);
- for (int i = firstIndex; i < last; ++i)
- m_editors.removeAt(i);
-}
+ const Link link = Link::fromString(storage.input(), true);
+ const QRegularExpression regexp = ILocatorFilter::createRegExp(link.targetFilePath.toString());
+ if (!regexp.isValid())
+ return;
-QList<LocatorFilterEntry> OpenDocumentsFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry)
-{
- QList<LocatorFilterEntry> goodEntries;
- QList<LocatorFilterEntry> betterEntries;
- const Link link = Link::fromString(entry, true);
+ LocatorFilterEntries goodEntries;
+ LocatorFilterEntries betterEntries;
- const QRegularExpression regexp = createRegExp(link.targetFilePath.toString());
- if (!regexp.isValid())
- return goodEntries;
-
- const QList<Entry> editorEntries = editors();
- for (const Entry &editorEntry : editorEntries) {
- if (future.isCanceled())
- break;
- QString fileName = editorEntry.fileName.toString();
- if (fileName.isEmpty())
+ for (const Entry &editorData : editorsData) {
+ if (promise.isCanceled())
+ return;
+ if (editorData.fileName.isEmpty())
continue;
- QString displayName = editorEntry.displayName;
- const QRegularExpressionMatch match = regexp.match(displayName);
+ const QRegularExpressionMatch match = regexp.match(editorData.displayName);
if (match.hasMatch()) {
- LocatorFilterEntry filterEntry(this, displayName);
- filterEntry.filePath = FilePath::fromString(fileName);
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = editorData.displayName;
+ filterEntry.filePath = editorData.fileName;
filterEntry.extraInfo = filterEntry.filePath.shortNativePath();
- filterEntry.highlightInfo = highlightInfo(match);
+ filterEntry.highlightInfo = ILocatorFilter::highlightInfo(match);
filterEntry.linkForEditor = Link(filterEntry.filePath, link.targetLine,
link.targetColumn);
if (match.capturedStart() == 0)
@@ -107,23 +68,21 @@ QList<LocatorFilterEntry> OpenDocumentsFilter::matchesFor(QFutureInterface<Locat
goodEntries.append(filterEntry);
}
}
- betterEntries.append(goodEntries);
- return betterEntries;
+ storage.reportOutput(betterEntries + goodEntries);
}
-QList<OpenDocumentsFilter::Entry> OpenDocumentsFilter::editors() const
+LocatorMatcherTasks OpenDocumentsFilter::matchers()
{
- QMutexLocker lock(&m_mutex);
- return m_editors;
-}
+ TreeStorage<LocatorStorage> storage;
-void OpenDocumentsFilter::accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const
-{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- BaseFileFilter::openEditorAt(selection);
+ const auto onSetup = [storage](Async<void> &async) {
+ const QList<Entry> editorsData = Utils::transform(DocumentModel::entries(),
+ [](const DocumentModel::Entry *e) { return Entry{e->filePath(), e->displayName()}; });
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(matchEditors, *storage, editorsData);
+ };
+
+ return {{AsyncTask<void>(onSetup), storage}};
}
-} // Core::Internal
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/opendocumentsfilter.h b/src/plugins/coreplugin/locator/opendocumentsfilter.h
index 537bdd45a5..94306d0515 100644
--- a/src/plugins/coreplugin/locator/opendocumentsfilter.h
+++ b/src/plugins/coreplugin/locator/opendocumentsfilter.h
@@ -5,46 +5,15 @@
#include "ilocatorfilter.h"
-#include <coreplugin/editormanager/documentmodel.h>
-
-#include <QFutureInterface>
-#include <QList>
-#include <QMutex>
-#include <QString>
-
-namespace Core {
-namespace Internal {
+namespace Core::Internal {
class OpenDocumentsFilter : public ILocatorFilter
{
- Q_OBJECT
-
public:
OpenDocumentsFilter();
- QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
-
-public slots:
- void slotDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
- const QVector<int> &roles);
- void slotRowsInserted(const QModelIndex &, int first, int last);
- void slotRowsRemoved(const QModelIndex &, int first, int last);
private:
- class Entry
- {
- public:
- Utils::FilePath fileName;
- QString displayName;
- };
-
- QList<Entry> editors() const;
-
- mutable QMutex m_mutex;
- QList<Entry> m_editors;
+ LocatorMatcherTasks matchers() final;
};
-} // namespace Internal
-} // namespace Core
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp b/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp
index cc62ec91c0..64ea71998b 100644
--- a/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp
+++ b/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp
@@ -6,149 +6,28 @@
#include "../coreplugintr.h"
#include "../messagemanager.h"
+#include <extensionsystem/pluginmanager.h>
+
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/commandline.h>
#include <utils/environment.h>
#include <utils/fancylineedit.h>
#include <utils/link.h>
#include <utils/macroexpander.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/variablechooser.h>
#include <QFormLayout>
-#include <QJsonDocument>
#include <QJsonObject>
-#include <QMutex>
-#include <QMutexLocker>
#include <QRegularExpression>
-#include <QWaitCondition>
using namespace Utils;
-namespace Core {
-namespace Internal {
-
-// #pragma mark -- SpotlightIterator
-
-class SpotlightIterator : public BaseFileFilter::Iterator
-{
-public:
- SpotlightIterator(const CommandLine &command);
- ~SpotlightIterator() override;
-
- void toFront() override;
- bool hasNext() const override;
- Utils::FilePath next() override;
- Utils::FilePath filePath() const override;
-
- void scheduleKillProcess();
- void killProcess();
-
-private:
- void ensureNext();
-
- std::unique_ptr<QtcProcess> m_process;
- QMutex m_mutex;
- QWaitCondition m_waitForItems;
- FilePaths m_queue;
- FilePaths m_filePaths;
- int m_index;
- bool m_finished;
-};
-
-SpotlightIterator::SpotlightIterator(const CommandLine &command)
- : m_index(-1)
- , m_finished(false)
-{
- QTC_ASSERT(!command.isEmpty(), return );
- m_process.reset(new QtcProcess);
- m_process->setCommand(command);
- m_process->setEnvironment(Utils::Environment::systemEnvironment());
- QObject::connect(m_process.get(), &QtcProcess::done,
- m_process.get(), [this, exe = command.executable().toUserOutput()] {
- if (m_process->result() != ProcessResult::FinishedWithSuccess) {
- MessageManager::writeFlashing(Tr::tr(
- "Locator: Error occurred when running \"%1\".").arg(exe));
- }
- scheduleKillProcess();
- });
- QObject::connect(m_process.get(), &QtcProcess::readyReadStandardOutput,
- m_process.get(), [this] {
- QString output = m_process->readAllStandardOutput();
- output.replace("\r\n", "\n");
- const QStringList items = output.split('\n');
- QMutexLocker lock(&m_mutex);
- m_queue.append(Utils::transform(items, &FilePath::fromUserInput));
- if (m_filePaths.size() + m_queue.size() > 10000) // limit the amount of data
- scheduleKillProcess();
- m_waitForItems.wakeAll();
- });
- m_process->start();
-}
-
-SpotlightIterator::~SpotlightIterator()
-{
- killProcess();
-}
-
-void SpotlightIterator::toFront()
-{
- m_index = -1;
-}
-
-bool SpotlightIterator::hasNext() const
-{
- auto that = const_cast<SpotlightIterator *>(this);
- that->ensureNext();
- return (m_index + 1 < m_filePaths.size());
-}
-
-Utils::FilePath SpotlightIterator::next()
-{
- ensureNext();
- ++m_index;
- QTC_ASSERT(m_index < m_filePaths.size(), return FilePath());
- return m_filePaths.at(m_index);
-}
-
-Utils::FilePath SpotlightIterator::filePath() const
-{
- QTC_ASSERT(m_index < m_filePaths.size(), return FilePath());
- return m_filePaths.at(m_index);
-}
-
-void SpotlightIterator::scheduleKillProcess()
-{
- QMetaObject::invokeMethod(m_process.get(), [this] { killProcess(); }, Qt::QueuedConnection);
-}
-
-void SpotlightIterator::killProcess()
-{
- if (!m_process)
- return;
- m_process->disconnect();
- QMutexLocker lock(&m_mutex);
- m_finished = true;
- m_waitForItems.wakeAll();
- m_process.reset();
-}
-
-void SpotlightIterator::ensureNext()
-{
- if (m_index + 1 < m_filePaths.size()) // nothing to do
- return;
- // check if there are items in the queue, otherwise wait for some
- QMutexLocker lock(&m_mutex);
- if (m_queue.isEmpty() && !m_finished)
- m_waitForItems.wait(&m_mutex);
- m_filePaths.append(m_queue);
- m_queue.clear();
-}
-
-// #pragma mark -- SpotlightLocatorFilter
+namespace Core::Internal {
static QString defaultCommand()
{
@@ -223,33 +102,97 @@ SpotlightLocatorFilter::SpotlightLocatorFilter()
{
setId("SpotlightFileNamesLocatorFilter");
setDefaultShortcutString("md");
- setDefaultIncludedByDefault(false);
setDisplayName(Tr::tr("File Name Index"));
- setDescription(
- Tr::tr("Matches files from a global file system index (Spotlight, Locate, Everything). Append "
- "\"+<number>\" or \":<number>\" to jump to the given line number. Append another "
- "\"+<number>\" or \":<number>\" to jump to the column number as well."));
- setConfigurable(true);
- reset();
+ setDescription(Tr::tr(
+ "Locates files from a global file system index (Spotlight, Locate, Everything). Append "
+ "\"+<number>\" or \":<number>\" to jump to the given line number. Append another "
+ "\"+<number>\" or \":<number>\" to jump to the column number as well."));
+ m_command = defaultCommand();
+ m_arguments = defaultArguments();
+ m_caseSensitiveArguments = defaultArguments(Qt::CaseSensitive);
}
-void SpotlightLocatorFilter::prepareSearch(const QString &entry)
+static void matches(QPromise<void> &promise, const LocatorStorage &storage,
+ const CommandLine &command)
{
- Link link = Utils::Link::fromString(entry, true);
- if (link.targetFilePath.isEmpty()) {
- setFileIterator(new BaseFileFilter::ListIterator(Utils::FilePaths()));
- } else {
- // only pass the file name part to allow searches like "somepath/*foo"
+ // If search string contains spaces, treat them as wildcard '*' and search in full path
+ const QString wildcardInput = QDir::fromNativeSeparators(storage.input()).replace(' ', '*');
+ const Link inputLink = Link::fromString(wildcardInput, true);
+ const QString newInput = inputLink.targetFilePath.toString();
+ const QRegularExpression regExp = ILocatorFilter::createRegExp(newInput);
+ if (!regExp.isValid())
+ return;
+
+ const bool hasPathSeparator = newInput.contains('/') || newInput.contains('*');
+ LocatorFileCache::MatchedEntries entries = {};
+ QEventLoop loop;
+ Process process;
+ process.setCommand(command);
+ process.setEnvironment(Environment::systemEnvironment()); // TODO: Is it needed?
- std::unique_ptr<MacroExpander> expander(createMacroExpander(link.targetFilePath.fileName()));
- const QString argumentString = expander->expand(
- caseSensitivity(link.targetFilePath.toString()) == Qt::CaseInsensitive
- ? m_arguments
- : m_caseSensitiveArguments);
- const CommandLine cmd(FilePath::fromString(m_command), argumentString, CommandLine::Raw);
- setFileIterator(new SpotlightIterator(cmd));
+ QObject::connect(&process, &Process::readyReadStandardOutput, &process,
+ [&, entriesPtr = &entries] {
+ QString output = process.readAllStandardOutput();
+ output.replace("\r\n", "\n");
+ const QStringList items = output.split('\n');
+ const FilePaths filePaths = Utils::transform(items, &FilePath::fromUserInput);
+ LocatorFileCache::processFilePaths(promise.future(), filePaths, hasPathSeparator, regExp,
+ inputLink, entriesPtr);
+ if (promise.isCanceled())
+ loop.exit();
+ });
+ QObject::connect(&process, &Process::done, &process, [&] {
+ if (process.result() != ProcessResult::FinishedWithSuccess) {
+ MessageManager::writeFlashing(Tr::tr("Locator: Error occurred when running \"%1\".")
+ .arg(command.executable().toUserOutput()));
+ }
+ loop.exit();
+ });
+ QFutureWatcher<void> watcher;
+ watcher.setFuture(promise.future());
+ QObject::connect(&watcher, &QFutureWatcherBase::canceled, &watcher, [&loop] { loop.exit(); });
+ if (promise.isCanceled())
+ return;
+ process.start();
+ loop.exec();
+
+ for (auto &entry : entries) {
+ if (promise.isCanceled())
+ return;
+ if (entry.size() < 1000)
+ Utils::sort(entry, LocatorFilterEntry::compareLexigraphically);
}
- BaseFileFilter::prepareSearch(entry);
+ if (promise.isCanceled())
+ return;
+ storage.reportOutput(std::accumulate(std::begin(entries), std::end(entries),
+ LocatorFilterEntries()));
+}
+
+LocatorMatcherTasks SpotlightLocatorFilter::matchers()
+{
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [storage, command = m_command, insensArgs = m_arguments,
+ sensArgs = m_caseSensitiveArguments](Async<void> &async) {
+ const Link link = Link::fromString(storage->input(), true);
+ const FilePath input = link.targetFilePath;
+ if (input.isEmpty())
+ return TaskAction::StopWithDone;
+
+ // only pass the file name part to allow searches like "somepath/*foo"
+ const std::unique_ptr<MacroExpander> expander(createMacroExpander(input.fileName()));
+ const QString args = caseSensitivity(input.toString()) == Qt::CaseInsensitive
+ ? insensArgs : sensArgs;
+ const CommandLine cmd(FilePath::fromString(command), expander->expand(args),
+ CommandLine::Raw);
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(matches, *storage, cmd);
+ return TaskAction::Continue;
+ };
+
+ return {{AsyncTask<void>(onSetup), storage}};
}
bool SpotlightLocatorFilter::openConfigDialog(QWidget *parent, bool &needsRefresh)
@@ -275,7 +218,7 @@ bool SpotlightLocatorFilter::openConfigDialog(QWidget *parent, bool &needsRefres
chooser->addMacroExpanderProvider([expander = expander.get()] { return expander; });
chooser->addSupportedWidget(argumentsEdit);
chooser->addSupportedWidget(caseSensitiveArgumentsEdit);
- const bool accepted = openConfigDialog(parent, &configWidget);
+ const bool accepted = ILocatorFilter::openConfigDialog(parent, &configWidget);
if (accepted) {
m_command = commandEdit->rawFilePath().toString();
m_arguments = argumentsEdit->text();
@@ -301,12 +244,4 @@ void SpotlightLocatorFilter::restoreState(const QJsonObject &obj)
m_caseSensitiveArguments = obj.value(kCaseSensitiveKey).toString(defaultArguments(Qt::CaseSensitive));
}
-void SpotlightLocatorFilter::reset()
-{
- m_command = defaultCommand();
- m_arguments = defaultArguments();
- m_caseSensitiveArguments = defaultArguments(Qt::CaseSensitive);
-}
-
-} // Internal
-} // Core
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/spotlightlocatorfilter.h b/src/plugins/coreplugin/locator/spotlightlocatorfilter.h
index 174319d434..06114d4e26 100644
--- a/src/plugins/coreplugin/locator/spotlightlocatorfilter.h
+++ b/src/plugins/coreplugin/locator/spotlightlocatorfilter.h
@@ -3,22 +3,15 @@
#pragma once
-#include "basefilefilter.h"
+#include "ilocatorfilter.h"
-#include <functional>
+namespace Core::Internal {
-namespace Core {
-namespace Internal {
-
-class SpotlightLocatorFilter : public BaseFileFilter
+class SpotlightLocatorFilter : public ILocatorFilter
{
- Q_OBJECT
public:
SpotlightLocatorFilter();
- void prepareSearch(const QString &entry) override;
-
- using ILocatorFilter::openConfigDialog;
bool openConfigDialog(QWidget *parent, bool &needsRefresh) final;
protected:
@@ -26,12 +19,11 @@ protected:
void restoreState(const QJsonObject &obj) final;
private:
- void reset();
+ LocatorMatcherTasks matchers() final;
QString m_command;
QString m_arguments;
QString m_caseSensitiveArguments;
};
-} // Internal
-} // Core
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/urllocatorfilter.cpp b/src/plugins/coreplugin/locator/urllocatorfilter.cpp
index 70651e74ee..c9025b2237 100644
--- a/src/plugins/coreplugin/locator/urllocatorfilter.cpp
+++ b/src/plugins/coreplugin/locator/urllocatorfilter.cpp
@@ -7,7 +7,6 @@
#include <utils/algorithm.h>
#include <utils/layoutbuilder.h>
-#include <utils/stringutils.h>
#include <QCheckBox>
#include <QDesktopServices>
@@ -17,9 +16,7 @@
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
-#include <QMutexLocker>
#include <QPushButton>
-#include <QUrl>
using namespace Utils;
@@ -162,38 +159,32 @@ UrlLocatorFilter::UrlLocatorFilter(const QString &displayName, Id id)
setId(id);
m_defaultDisplayName = displayName;
setDisplayName(displayName);
- setDefaultIncludedByDefault(false);
}
-UrlLocatorFilter::~UrlLocatorFilter() = default;
-
-QList<Core::LocatorFilterEntry> UrlLocatorFilter::matchesFor(
- QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
-{
- QList<Core::LocatorFilterEntry> entries;
- const QStringList urls = remoteUrls();
- for (const QString &url : urls) {
- if (future.isCanceled())
- break;
- const QString name = url.arg(entry);
- Core::LocatorFilterEntry filterEntry(this, name);
- filterEntry.highlightInfo = {int(name.lastIndexOf(entry)), int(entry.length())};
- entries.append(filterEntry);
- }
- return entries;
-}
-
-void UrlLocatorFilter::accept(const Core::LocatorFilterEntry &selection,
- QString *newText,
- int *selectionStart,
- int *selectionLength) const
+LocatorMatcherTasks UrlLocatorFilter::matchers()
{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- const QString &url = selection.displayName;
- if (!url.isEmpty())
- QDesktopServices::openUrl(url);
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [storage, urls = remoteUrls()] {
+ const QString input = storage->input();
+ LocatorFilterEntries entries;
+ for (const QString &url : urls) {
+ const QString name = url.arg(input);
+ LocatorFilterEntry entry;
+ entry.displayName = name;
+ entry.acceptor = [name] {
+ if (!name.isEmpty())
+ QDesktopServices::openUrl(name);
+ return AcceptResult();
+ };
+ entry.highlightInfo = {int(name.lastIndexOf(input)), int(input.length())};
+ entries.append(entry);
+ }
+ storage->reportOutput(entries);
+ };
+ return {{Sync(onSetup), storage}};
}
const char kDisplayNameKey[] = "displayName";
@@ -249,7 +240,6 @@ bool UrlLocatorFilter::openConfigDialog(QWidget *parent, bool &needsRefresh)
Q_UNUSED(needsRefresh)
Internal::UrlFilterOptions optionsDialog(this, parent);
if (optionsDialog.exec() == QDialog::Accepted) {
- QMutexLocker lock(&m_mutex);
m_remoteUrls.clear();
setIncludedByDefault(optionsDialog.includeByDefault->isChecked());
setShortcutString(optionsDialog.shortcutEdit->text().trimmed());
@@ -268,20 +258,4 @@ void UrlLocatorFilter::addDefaultUrl(const QString &urlTemplate)
m_defaultUrls.append(urlTemplate);
}
-QStringList UrlLocatorFilter::remoteUrls() const
-{
- QMutexLocker lock(&m_mutex);
- return m_remoteUrls;
-}
-
-void UrlLocatorFilter::setIsCustomFilter(bool value)
-{
- m_isCustomFilter = value;
-}
-
-bool UrlLocatorFilter::isCustomFilter() const
-{
- return m_isCustomFilter;
-}
-
} // namespace Core
diff --git a/src/plugins/coreplugin/locator/urllocatorfilter.h b/src/plugins/coreplugin/locator/urllocatorfilter.h
index befe162d60..57f652aeca 100644
--- a/src/plugins/coreplugin/locator/urllocatorfilter.h
+++ b/src/plugins/coreplugin/locator/urllocatorfilter.h
@@ -8,7 +8,6 @@
#include <coreplugin/core_global.h>
#include <QDialog>
-#include <QMutex>
QT_BEGIN_NAMESPACE
class QCheckBox;
@@ -21,36 +20,30 @@ namespace Core {
class CORE_EXPORT UrlLocatorFilter final : public Core::ILocatorFilter
{
- Q_OBJECT
public:
UrlLocatorFilter(Utils::Id id);
UrlLocatorFilter(const QString &displayName, Utils::Id id);
- ~UrlLocatorFilter() final;
- // ILocatorFilter
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
- void restoreState(const QByteArray &state) override;
- bool openConfigDialog(QWidget *parent, bool &needsRefresh) override;
+ void restoreState(const QByteArray &state) final;
+ bool openConfigDialog(QWidget *parent, bool &needsRefresh) final;
void addDefaultUrl(const QString &urlTemplate);
- QStringList remoteUrls() const;
+ QStringList remoteUrls() const { return m_remoteUrls; }
- void setIsCustomFilter(bool value);
- bool isCustomFilter() const;
+ void setIsCustomFilter(bool value) { m_isCustomFilter = value; }
+ bool isCustomFilter() const { return m_isCustomFilter; }
protected:
void saveState(QJsonObject &object) const final;
void restoreState(const QJsonObject &object) final;
private:
+ LocatorMatcherTasks matchers() final;
+
QString m_defaultDisplayName;
QStringList m_defaultUrls;
QStringList m_remoteUrls;
bool m_isCustomFilter = false;
- mutable QMutex m_mutex;
};
namespace Internal {
@@ -81,5 +74,4 @@ private:
};
} // namespace Internal
-
} // namespace Core
diff --git a/src/plugins/coreplugin/loggingviewer.cpp b/src/plugins/coreplugin/loggingviewer.cpp
index 4392ef6344..a81ed5936d 100644
--- a/src/plugins/coreplugin/loggingviewer.cpp
+++ b/src/plugins/coreplugin/loggingviewer.cpp
@@ -622,7 +622,7 @@ void LoggingViewManagerWidget::saveLoggingsToFile() const
void LoggingViewManagerWidget::saveEnabledCategoryPreset() const
{
Utils::FilePath fp = Utils::FileUtils::getSaveFilePath(ICore::dialogParent(),
- Tr::tr("Save Enabled Categories As"));
+ Tr::tr("Save Enabled Categories As..."));
if (fp.isEmpty())
return;
const QList<LoggingCategoryItem> enabled = m_categoryModel->enabledCategories();
@@ -656,7 +656,7 @@ void LoggingViewManagerWidget::loadAndUpdateFromPreset()
if (!contents) {
QMessageBox::critical(ICore::dialogParent(),
Tr::tr("Error"),
- Tr::tr("Failed to open preset file \"%1\" for reading")
+ Tr::tr("Failed to open preset file \"%1\" for reading.")
.arg(fp.toUserOutput()));
return;
}
diff --git a/src/plugins/coreplugin/mainwindow.cpp b/src/plugins/coreplugin/mainwindow.cpp
index 168fff9220..2d7ec70c8e 100644
--- a/src/plugins/coreplugin/mainwindow.cpp
+++ b/src/plugins/coreplugin/mainwindow.cpp
@@ -1384,67 +1384,6 @@ public:
}
};
-class MarkdownHighlighter : public QSyntaxHighlighter
-{
- QBrush h2Brush;
-public:
- MarkdownHighlighter(QTextDocument *parent)
- : QSyntaxHighlighter(parent)
- , h2Brush(Qt::NoBrush)
- {
- parent->setIndentWidth(30); // default value is 40
- }
-
- void highlightBlock(const QString &text)
- {
- if (text.isEmpty())
- return;
-
- QTextBlockFormat fmt = currentBlock().blockFormat();
- QTextCursor cur(currentBlock());
- if (fmt.hasProperty(QTextFormat::HeadingLevel)) {
- fmt.setTopMargin(10);
- fmt.setBottomMargin(10);
-
- // Draw an underline for Heading 2, by creating a texture brush
- // with the last pixel visible
- if (fmt.property(QTextFormat::HeadingLevel) == 2) {
- QTextCharFormat charFmt = currentBlock().charFormat();
- charFmt.setBaselineOffset(15);
- setFormat(0, text.length(), charFmt);
-
- if (h2Brush.style() == Qt::NoBrush) {
- const int height = QFontMetrics(charFmt.font()).height();
- QImage image(1, height, QImage::Format_ARGB32);
-
- image.fill(QColor(0, 0, 0, 0).rgba());
- image.setPixel(0,
- height - 1,
- Utils::creatorTheme()->color(Theme::TextColorDisabled).rgba());
-
- h2Brush = QBrush(image);
- }
- fmt.setBackground(h2Brush);
- }
- cur.setBlockFormat(fmt);
- } else if (fmt.hasProperty(QTextFormat::BlockCodeLanguage) && fmt.indent() == 0) {
- // set identation for code blocks
- fmt.setIndent(1);
- cur.setBlockFormat(fmt);
- }
-
- // Show the bulet points as filled circles
- QTextList *list = cur.currentList();
- if (list) {
- QTextListFormat listFmt = list->format();
- if (listFmt.indent() == 1 && listFmt.style() == QTextListFormat::ListCircle) {
- listFmt.setStyle(QTextListFormat::ListDisc);
- list->setFormat(listFmt);
- }
- }
- }
-};
-
void MainWindow::changeLog()
{
static QPointer<LogDialog> dialog;
@@ -1484,8 +1423,7 @@ void MainWindow::changeLog()
aggregate->add(textEdit);
aggregate->add(new Core::BaseTextFind(textEdit));
- auto highlighter = new MarkdownHighlighter(textEdit->document());
- (void)highlighter;
+ new MarkdownHighlighter(textEdit->document());
auto textEditWidget = new QFrame;
textEditWidget->setFrameStyle(QFrame::NoFrame);
diff --git a/src/plugins/coreplugin/manhattanstyle.cpp b/src/plugins/coreplugin/manhattanstyle.cpp
index 3d51642e44..0f16100e11 100644
--- a/src/plugins/coreplugin/manhattanstyle.cpp
+++ b/src/plugins/coreplugin/manhattanstyle.cpp
@@ -3,9 +3,12 @@
#include "manhattanstyle.h"
+#include "generalsettings.h"
+
#include <utils/algorithm.h>
#include <utils/fancymainwindow.h>
#include <utils/hostosinfo.h>
+#include <utils/itemviews.h>
#include <utils/qtcassert.h>
#include <utils/styleanimator.h>
#include <utils/stylehelper.h>
@@ -24,6 +27,8 @@
#include <QPainter>
#include <QPainterPath>
#include <QPixmap>
+#include <QPixmapCache>
+#include <QScrollArea>
#include <QSpinBox>
#include <QStatusBar>
#include <QStyleFactory>
@@ -53,7 +58,7 @@ static bool isInUnstyledDialogOrPopup(const QWidget *widget)
{
// Do not style contents of dialogs or popups without "panelwidget" property
const QWidget *window = widget->window();
- if (window->property("panelwidget").toBool())
+ if (window->property(StyleHelper::C_PANEL_WIDGET).toBool())
return false;
const Qt::WindowType windowType = window->windowType();
return (windowType == Qt::Dialog || windowType == Qt::Popup);
@@ -74,12 +79,15 @@ bool panelWidget(const QWidget *widget)
if (qobject_cast<const QTabBar *>(widget))
return styleEnabled(widget);
+ if (qobject_cast<const QScrollArea *>(widget)) // See DebuggerMainWindowPrivate
+ return widget->property(StyleHelper::C_PANEL_WIDGET_SINGLE_ROW).toBool();
+
const QWidget *p = widget;
while (p) {
if (qobject_cast<const QToolBar *>(p) ||
qobject_cast<const QStatusBar *>(p) ||
qobject_cast<const QMenuBar *>(p) ||
- p->property("panelwidget").toBool())
+ p->property(StyleHelper::C_PANEL_WIDGET).toBool())
return styleEnabled(widget);
p = p->parentWidget();
}
@@ -97,7 +105,7 @@ bool lightColored(const QWidget *widget)
const QWidget *p = widget;
while (p) {
- if (p->property("lightColored").toBool())
+ if (p->property(StyleHelper::C_LIGHT_COLORED).toBool())
return true;
p = p->parentWidget();
}
@@ -131,6 +139,7 @@ ManhattanStyle::ManhattanStyle(const QString &baseStyleName)
: QProxyStyle(QStyleFactory::create(baseStyleName))
, d(new ManhattanStylePrivate())
{
+ Core::Internal::GeneralSettings::applyToolbarStyleFromSettings();
}
ManhattanStyle::~ManhattanStyle()
@@ -154,7 +163,7 @@ QSize ManhattanStyle::sizeFromContents(ContentsType type, const QStyleOption *op
switch (type) {
case CT_Splitter:
- if (widget && widget->property("minisplitter").toBool())
+ if (widget && widget->property(StyleHelper::C_MINI_SPLITTER).toBool())
newSize = QSize(1, 1);
break;
case CT_ComboBox:
@@ -203,8 +212,14 @@ int ManhattanStyle::pixelMetric(PixelMetric metric, const QStyleOption *option,
int retval = QProxyStyle::pixelMetric(metric, option, widget);
switch (metric) {
+#ifdef Q_OS_MACOS
+ case PM_MenuButtonIndicator:
+ if (widget && option->type == QStyleOption::SO_ToolButton)
+ return 12;
+ break;
+#endif
case PM_SplitterWidth:
- if (widget && widget->property("minisplitter").toBool())
+ if (widget && widget->property(StyleHelper::C_MINI_SPLITTER).toBool())
retval = 1;
break;
case PM_ToolBarIconSize:
@@ -295,16 +310,26 @@ void ManhattanStyle::polish(QWidget *widget)
widget->setAttribute(Qt::WA_LayoutUsesWidgetRect, true);
// So that text isn't cutoff in line-edits, comboboxes... etc.
const int height = qMax(StyleHelper::navigationWidgetHeight(), QApplication::fontMetrics().height());
- if (qobject_cast<QToolButton*>(widget) || qobject_cast<QLineEdit*>(widget)) {
+ if (qobject_cast<QToolButton*>(widget)) {
+ widget->setMinimumWidth(
+ StyleHelper::toolbarStyle() == StyleHelper::ToolbarStyleCompact ? 24 : 28);
widget->setAttribute(Qt::WA_Hover);
widget->setMaximumHeight(height - 2);
+ } else if (qobject_cast<QLineEdit*>(widget)) {
+ widget->setAttribute(Qt::WA_Hover);
+ widget->setFixedHeight(height - (StyleHelper::toolbarStyle()
+ == StyleHelper::ToolbarStyleCompact ? 1 : 3));
} else if (qobject_cast<QLabel*>(widget) || qobject_cast<QSpinBox*>(widget)
|| qobject_cast<QCheckBox*>(widget)) {
widget->setPalette(panelPalette(widget->palette(), lightColored(widget)));
- } else if (widget->property("panelwidget_singlerow").toBool()) {
+ } else if ((qobject_cast<QToolBar*>(widget) && !StyleHelper::isQDSTheme())
+ || widget->property(StyleHelper::C_PANEL_WIDGET_SINGLE_ROW).toBool()) {
widget->setFixedHeight(height);
} else if (qobject_cast<QStatusBar*>(widget)) {
- widget->setFixedHeight(height + 2);
+ const bool flatAndNotCompact =
+ StyleHelper::toolbarStyle() != StyleHelper::ToolbarStyleCompact
+ && creatorTheme()->flag(Theme::FlatToolBars);
+ widget->setFixedHeight(height + (flatAndNotCompact ? 3 : 2));
} else if (qobject_cast<QComboBox*>(widget)) {
const bool isLightColored = lightColored(widget);
QPalette palette = panelPalette(widget->palette(), isLightColored);
@@ -314,6 +339,9 @@ void ManhattanStyle::polish(QWidget *widget)
widget->setPalette(palette);
widget->setMaximumHeight(height - 2);
widget->setAttribute(Qt::WA_Hover);
+ } else if (qobject_cast<QScrollArea*>(widget)
+ && widget->property(StyleHelper::C_PANEL_WIDGET_SINGLE_ROW).toBool()) {
+ widget->setFixedHeight(height);
}
}
}
@@ -393,7 +421,7 @@ int ManhattanStyle::styleHint(StyleHint hint, const QStyleOption *option, const
case QStyle::SH_ItemView_ActivateItemOnSingleClick:
// default depends on the style
if (widget) {
- QVariant activationMode = widget->property("ActivationMode");
+ QVariant activationMode = widget->property(activationModeC);
if (activationMode.isValid())
ret = activationMode.toBool();
}
@@ -633,76 +661,76 @@ void ManhattanStyle::drawPrimitiveForPanelWidget(PrimitiveElement element,
const bool animating = (state & State_Animating);
if (widget && !animating) {
- auto w = const_cast<QWidget *> (widget);
- int oldState = w->property("_q_stylestate").toInt();
- QRect oldRect = w->property("_q_stylerect").toRect();
- QRect newRect = w->rect();
- w->setProperty("_q_stylestate", (int)option->state);
- w->setProperty("_q_stylerect", w->rect());
-
- // Determine the animated transition
- bool doTransition = ((state & State_On) != (oldState & State_On) ||
- (state & State_MouseOver) != (oldState & State_MouseOver));
- if (oldRect != newRect) {
- doTransition = false;
- d->animator.stopAnimation(widget);
- }
-
- if (doTransition) {
- QImage startImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied);
- QImage endImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied);
- Animation *anim = d->animator.widgetAnimation(widget);
- QStyleOption opt = *option;
- opt.state = (QStyle::State)oldState;
- opt.state |= State_Animating;
- startImage.fill(0);
- auto t = new Transition;
- t->setWidget(w);
- QPainter startPainter(&startImage);
- if (!anim) {
- drawPrimitive(element, &opt, &startPainter, widget);
- } else {
- anim->paint(&startPainter, &opt);
+ auto w = const_cast<QWidget *> (widget);
+ int oldState = w->property("_q_stylestate").toInt();
+ QRect oldRect = w->property("_q_stylerect").toRect();
+ QRect newRect = w->rect();
+ w->setProperty("_q_stylestate", (int)option->state);
+ w->setProperty("_q_stylerect", w->rect());
+
+ // Determine the animated transition
+ bool doTransition = ((state & State_On) != (oldState & State_On) ||
+ (state & State_MouseOver) != (oldState & State_MouseOver));
+ if (oldRect != newRect) {
+ doTransition = false;
d->animator.stopAnimation(widget);
}
- QStyleOption endOpt = *option;
- endOpt.state |= State_Animating;
- t->setStartImage(startImage);
- d->animator.startAnimation(t);
- endImage.fill(0);
- QPainter endPainter(&endImage);
- drawPrimitive(element, &endOpt, &endPainter, widget);
- t->setEndImage(endImage);
- if (oldState & State_MouseOver)
- t->setDuration(150);
- else
- t->setDuration(75);
- t->setStartTime(QTime::currentTime());
- }
+
+ if (doTransition) {
+ QImage startImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied);
+ QImage endImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied);
+ Animation *anim = d->animator.widgetAnimation(widget);
+ QStyleOption opt = *option;
+ opt.state = (QStyle::State)oldState;
+ opt.state |= State_Animating;
+ startImage.fill(0);
+ auto t = new Transition;
+ t->setWidget(w);
+ QPainter startPainter(&startImage);
+ if (!anim) {
+ drawPrimitive(element, &opt, &startPainter, widget);
+ } else {
+ anim->paint(&startPainter, &opt);
+ d->animator.stopAnimation(widget);
+ }
+ QStyleOption endOpt = *option;
+ endOpt.state |= State_Animating;
+ t->setStartImage(startImage);
+ d->animator.startAnimation(t);
+ endImage.fill(0);
+ QPainter endPainter(&endImage);
+ drawPrimitive(element, &endOpt, &endPainter, widget);
+ t->setEndImage(endImage);
+ if (oldState & State_MouseOver)
+ t->setDuration(150);
+ else
+ t->setDuration(75);
+ t->setStartTime(QTime::currentTime());
+ }
}
Animation *anim = d->animator.widgetAnimation(widget);
if (!animating && anim) {
- anim->paint(painter, option);
+ anim->paint(painter, option);
} else {
- bool pressed = option->state & State_Sunken || option->state & State_On;
- painter->setPen(StyleHelper::sidebarShadow());
- if (pressed) {
- const QColor shade = creatorTheme()->color(Theme::FancyToolButtonSelectedColor);
- painter->fillRect(rect, shade);
- if (!creatorTheme()->flag(Theme::FlatToolBars)) {
- const QRectF borderRect = QRectF(rect).adjusted(0.5, 0.5, -0.5, -0.5);
- painter->drawLine(borderRect.topLeft() + QPointF(1, 0), borderRect.topRight() - QPointF(1, 0));
- painter->drawLine(borderRect.topLeft(), borderRect.bottomLeft());
- painter->drawLine(borderRect.topRight(), borderRect.bottomRight());
+ const bool pressed = option->state & State_Sunken || option->state & State_On
+ || (widget && widget->property(StyleHelper::C_HIGHLIGHT_WIDGET).toBool());
+ painter->setPen(StyleHelper::sidebarShadow());
+ if (pressed) {
+ StyleHelper::drawPanelBgRect(
+ painter, rect, creatorTheme()->color(Theme::FancyToolButtonSelectedColor));
+ if (StyleHelper::toolbarStyle() == StyleHelper::ToolbarStyleCompact
+ && !creatorTheme()->flag(Theme::FlatToolBars)) {
+ const QRectF borderRect = QRectF(rect).adjusted(0.5, 0.5, -0.5, -0.5);
+ painter->drawLine(borderRect.topLeft() + QPointF(1, 0), borderRect.topRight() - QPointF(1, 0));
+ painter->drawLine(borderRect.topLeft(), borderRect.bottomLeft());
+ painter->drawLine(borderRect.topRight(), borderRect.bottomRight());
+ }
+ } else if (option->state & State_Enabled && option->state & State_MouseOver) {
+ StyleHelper::drawPanelBgRect(
+ painter, rect, creatorTheme()->color(Theme::FancyToolButtonHoverColor));
}
- } else if (option->state & State_Enabled && option->state & State_MouseOver) {
- painter->fillRect(rect, creatorTheme()->color(Theme::FancyToolButtonHoverColor));
- } else if (widget && widget->property("highlightWidget").toBool()) {
- QColor shade(0, 0, 0, 128);
- painter->fillRect(rect, shade);
- }
if (option->state & State_HasFocus && (option->state & State_KeyboardFocusChange)) {
QColor highlight = option->palette.highlight().color();
highlight.setAlphaF(0.4f);
@@ -884,7 +912,8 @@ void ManhattanStyle::drawControl(
painter->save();
QRect editRect = subControlRect(CC_ComboBox, cb, SC_ComboBoxEditField, widget);
QPalette customPal = cb->palette;
- bool drawIcon = !(widget && widget->property("hideicon").toBool());
+ const bool drawIcon =
+ !(widget && widget->property(StyleHelper::C_HIDE_ICON).toBool());
if (!cb->currentIcon.isNull() && drawIcon) {
QIcon::Mode mode = cb->state & State_Enabled ? QIcon::Normal
@@ -909,14 +938,15 @@ void ManhattanStyle::drawControl(
}
Qt::TextElideMode elideMode = Qt::ElideRight;
- if (widget && widget->dynamicPropertyNames().contains("elidemode"))
- elideMode = widget->property("elidemode").value<Qt::TextElideMode>();
+ if (widget && widget->dynamicPropertyNames().contains(StyleHelper::C_ELIDE_MODE))
+ elideMode = widget->property(StyleHelper::C_ELIDE_MODE)
+ .value<Qt::TextElideMode>();
QLatin1Char asterisk('*');
int elideWidth = editRect.width();
bool notElideAsterisk = elideMode == Qt::ElideRight && widget
- && widget->property("notelideasterisk").toBool()
+ && widget->property(StyleHelper::C_NOT_ELIDE_ASTERISK).toBool()
&& cb->currentText.endsWith(asterisk)
&& option->fontMetrics.horizontalAdvance(cb->currentText)
> elideWidth;
@@ -1031,7 +1061,7 @@ void ManhattanStyle::drawControl(
: StyleHelper::sidebarHighlight();
const QColor borderColor = drawLightColored
? QColor(255, 255, 255, 180) : hightLight;
- if (widget && widget->property("topBorder").toBool()) {
+ if (widget && widget->property(StyleHelper::C_TOP_BORDER).toBool()) {
painter->drawLine(borderRect.topLeft(), borderRect.topRight());
painter->setPen(borderColor);
painter->drawLine(borderRect.topLeft() + QPointF(0, 1), borderRect.topRight() + QPointF(0, 1));
@@ -1047,7 +1077,7 @@ void ManhattanStyle::drawControl(
}
if (creatorTheme()->flag(Theme::DrawToolBarBorders)) {
painter->setPen(StyleHelper::toolBarBorderColor());
- if (widget && widget->property("topBorder").toBool())
+ if (widget && widget->property(StyleHelper::C_TOP_BORDER).toBool())
painter->drawLine(borderRect.topLeft(), borderRect.topRight());
else
painter->drawLine(borderRect.bottomLeft(), borderRect.bottomRight());
@@ -1076,7 +1106,8 @@ void ManhattanStyle::drawComplexControl(ComplexControl control, const QStyleOpti
case CC_ToolButton:
if (const auto toolbutton = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
bool reverse = option->direction == Qt::RightToLeft;
- bool drawborder = (widget && widget->property("showborder").toBool());
+ const bool drawborder =
+ (widget && widget->property(StyleHelper::C_SHOW_BORDER).toBool());
if (drawborder)
drawButtonSeparator(painter, rect, reverse);
@@ -1110,7 +1141,7 @@ void ManhattanStyle::drawComplexControl(ComplexControl control, const QStyleOpti
QStyleOptionToolButton label = *toolbutton;
label.palette = panelPalette(option->palette, lightColored(widget));
- if (widget && widget->property("highlightWidget").toBool()) {
+ if (widget && widget->property(StyleHelper::C_HIGHLIGHT_WIDGET).toBool()) {
label.palette.setColor(QPalette::ButtonText,
creatorTheme()->color(Theme::IconsWarningToolBarColor));
}
@@ -1137,7 +1168,7 @@ void ManhattanStyle::drawComplexControl(ComplexControl control, const QStyleOpti
tool.rect = tool.rect.adjusted(2, 2, -2, -2);
drawPrimitive(PE_IndicatorArrowDown, &tool, painter, widget);
} else if (toolbutton->features & QStyleOptionToolButton::HasMenu
- && widget && !widget->property("noArrow").toBool()) {
+ && widget && !widget->property(StyleHelper::C_NO_ARROW).toBool()) {
int arrowSize = 6;
QRect ir = toolbutton->rect.adjusted(1, 1, -1, -1);
QStyleOptionToolButton newBtn = *toolbutton;
@@ -1154,9 +1185,12 @@ void ManhattanStyle::drawComplexControl(ComplexControl control, const QStyleOpti
painter->save();
bool isEmpty = cb->currentText.isEmpty() && cb->currentIcon.isNull();
bool reverse = option->direction == Qt::RightToLeft;
- bool drawborder = !(widget && widget->property("hideborder").toBool());
- bool drawleftborder = (widget && widget->property("drawleftborder").toBool());
- bool alignarrow = !(widget && widget->property("alignarrow").toBool());
+ const bool drawborder =
+ !(widget && widget->property(StyleHelper::C_HIDE_BORDER).toBool());
+ const bool drawleftborder =
+ (widget && widget->property(StyleHelper::C_DRAW_LEFT_BORDER).toBool());
+ const bool alignarrow =
+ !(widget && widget->property(StyleHelper::C_ALIGN_ARROW).toBool());
if (drawborder) {
drawButtonSeparator(painter, rect, reverse);
diff --git a/src/plugins/coreplugin/mimetypemagicdialog.cpp b/src/plugins/coreplugin/mimetypemagicdialog.cpp
index 475cc57dd3..2a55539bf7 100644
--- a/src/plugins/coreplugin/mimetypemagicdialog.cpp
+++ b/src/plugins/coreplugin/mimetypemagicdialog.cpp
@@ -78,7 +78,7 @@ MimeTypeMagicDialog::MimeTypeMagicDialog(QWidget *parent) :
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Form {
diff --git a/src/plugins/coreplugin/mimetypesettings.cpp b/src/plugins/coreplugin/mimetypesettings.cpp
index a4efe1c30e..116fcf4a06 100644
--- a/src/plugins/coreplugin/mimetypesettings.cpp
+++ b/src/plugins/coreplugin/mimetypesettings.cpp
@@ -82,8 +82,6 @@ public:
// MimeTypeSettingsModel
class MimeTypeSettingsModel : public QAbstractTableModel
{
- Q_OBJECT
-
public:
enum class Role {
DefaultHandler = Qt::UserRole
@@ -227,8 +225,6 @@ void MimeTypeSettingsModel::resetUserDefaults()
// MimeTypeSettingsPrivate
class MimeTypeSettingsPrivate : public QObject
{
- Q_OBJECT
-
public:
MimeTypeSettingsPrivate();
~MimeTypeSettingsPrivate() override;
@@ -291,6 +287,31 @@ MimeTypeSettingsPrivate::MimeTypeSettingsPrivate()
MimeTypeSettingsPrivate::~MimeTypeSettingsPrivate() = default;
+class MimeTypeSettingsWidget : public IOptionsPageWidget
+{
+public:
+ MimeTypeSettingsWidget(MimeTypeSettingsPrivate *d)
+ : d(d)
+ {
+ d->configureUi(this);
+ }
+
+ void apply() final
+ {
+ MimeTypeSettingsPrivate::applyUserModifiedMimeTypes(d->m_pendingModifiedMimeTypes);
+ Core::Internal::setUserPreferredEditorTypes(d->m_model->m_userDefault);
+ d->m_pendingModifiedMimeTypes.clear();
+ d->m_model->load();
+ }
+
+ void finish() final
+ {
+ d->m_pendingModifiedMimeTypes.clear();
+ }
+
+ MimeTypeSettingsPrivate *d;
+};
+
void MimeTypeSettingsPrivate::configureUi(QWidget *w)
{
auto filterLineEdit = new FancyLineEdit;
@@ -713,6 +734,7 @@ MimeTypeSettings::MimeTypeSettings()
setId(Constants::SETTINGS_ID_MIMETYPES);
setDisplayName(Tr::tr("MIME Types"));
setCategory(Constants::SETTINGS_CATEGORY_CORE);
+ setWidgetCreator([this] { return new MimeTypeSettingsWidget(d); });
}
MimeTypeSettings::~MimeTypeSettings()
@@ -720,29 +742,6 @@ MimeTypeSettings::~MimeTypeSettings()
delete d;
}
-QWidget *MimeTypeSettings::widget()
-{
- if (!d->m_widget) {
- d->m_widget = new QWidget;
- d->configureUi(d->m_widget);
- }
- return d->m_widget;
-}
-
-void MimeTypeSettings::apply()
-{
- MimeTypeSettingsPrivate::applyUserModifiedMimeTypes(d->m_pendingModifiedMimeTypes);
- Core::Internal::setUserPreferredEditorTypes(d->m_model->m_userDefault);
- d->m_pendingModifiedMimeTypes.clear();
- d->m_model->load();
-}
-
-void MimeTypeSettings::finish()
-{
- d->m_pendingModifiedMimeTypes.clear();
- delete d->m_widget;
-}
-
void MimeTypeSettings::restoreSettings()
{
MimeTypeSettingsPrivate::UserMimeTypeHash mimetypes
@@ -784,5 +783,3 @@ void MimeEditorDelegate::setModelData(QWidget *editor,
}
} // Core::Internal
-
-#include "mimetypesettings.moc"
diff --git a/src/plugins/coreplugin/mimetypesettings.h b/src/plugins/coreplugin/mimetypesettings.h
index f676de53e6..0127e22768 100644
--- a/src/plugins/coreplugin/mimetypesettings.h
+++ b/src/plugins/coreplugin/mimetypesettings.h
@@ -11,17 +11,12 @@ class MimeTypeSettingsPrivate;
class MimeTypeSettings : public IOptionsPage
{
- Q_OBJECT
-
public:
MimeTypeSettings();
~MimeTypeSettings() override;
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
static void restoreSettings();
+
private:
MimeTypeSettingsPrivate *d;
};
diff --git a/src/plugins/coreplugin/minisplitter.cpp b/src/plugins/coreplugin/minisplitter.cpp
index d589a7dfc6..e4e9db129b 100644
--- a/src/plugins/coreplugin/minisplitter.cpp
+++ b/src/plugins/coreplugin/minisplitter.cpp
@@ -84,7 +84,7 @@ MiniSplitter::MiniSplitter(QWidget *parent, SplitterStyle style)
{
setHandleWidth(1);
setChildrenCollapsible(false);
- setProperty("minisplitter", true);
+ setProperty(Utils::StyleHelper::C_MINI_SPLITTER, true);
}
MiniSplitter::MiniSplitter(Qt::Orientation orientation, QWidget *parent, SplitterStyle style)
@@ -93,7 +93,7 @@ MiniSplitter::MiniSplitter(Qt::Orientation orientation, QWidget *parent, Splitte
{
setHandleWidth(1);
setChildrenCollapsible(false);
- setProperty("minisplitter", true);
+ setProperty(Utils::StyleHelper::C_MINI_SPLITTER, true);
}
/*!
diff --git a/src/plugins/coreplugin/navigationsubwidget.cpp b/src/plugins/coreplugin/navigationsubwidget.cpp
index 1d5b0e4bb6..838635c7b2 100644
--- a/src/plugins/coreplugin/navigationsubwidget.cpp
+++ b/src/plugins/coreplugin/navigationsubwidget.cpp
@@ -11,6 +11,7 @@
#include "navigationwidget.h"
#include <utils/styledbar.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QHBoxLayout>
@@ -53,7 +54,7 @@ NavigationSubWidget::NavigationSubWidget(NavigationWidget *parentWidget, int pos
splitAction->setIcon(Utils::Icons::SPLIT_HORIZONTAL_TOOLBAR.icon());
splitAction->setToolTip(Tr::tr("Split"));
splitAction->setPopupMode(QToolButton::InstantPopup);
- splitAction->setProperty("noArrow", true);
+ splitAction->setProperty(StyleHelper::C_NO_ARROW, true);
m_splitMenu = new QMenu(splitAction);
splitAction->setMenu(m_splitMenu);
connect(m_splitMenu, &QMenu::aboutToShow, this, &NavigationSubWidget::populateSplitMenu);
diff --git a/src/plugins/coreplugin/outputpanemanager.cpp b/src/plugins/coreplugin/outputpanemanager.cpp
index 97b4177694..037779f0bf 100644
--- a/src/plugins/coreplugin/outputpanemanager.cpp
+++ b/src/plugins/coreplugin/outputpanemanager.cpp
@@ -167,9 +167,14 @@ void IOutputPane::setFilteringEnabled(bool enable)
void IOutputPane::setupContext(const char *context, QWidget *widget)
{
+ return setupContext(Context(context), widget);
+}
+
+void IOutputPane::setupContext(const Context &context, QWidget *widget)
+{
QTC_ASSERT(!m_context, return);
m_context = new IContext(this);
- m_context->setContext(Context(context));
+ m_context->setContext(context);
m_context->setWidget(widget);
ICore::addContextObject(m_context);
@@ -813,6 +818,13 @@ QSize OutputPaneToggleButton::sizeHint() const
return s;
}
+static QRect bgRect(const QRect &widgetRect)
+{
+ // Removes/compensates the left and right margins of StyleHelper::drawPanelBgRect
+ return StyleHelper::toolbarStyle() == StyleHelper::ToolbarStyleCompact
+ ? widgetRect : widgetRect.adjusted(-2, 0, 2, 0);
+}
+
void OutputPaneToggleButton::paintEvent(QPaintEvent*)
{
const QFontMetrics fm = fontMetrics();
@@ -834,7 +846,7 @@ void OutputPaneToggleButton::paintEvent(QPaintEvent*)
c = Theme::BackgroundColorSelected;
if (c != Theme::BackgroundColorDark)
- p.fillRect(rect(), creatorTheme()->color(c));
+ StyleHelper::drawPanelBgRect(&p, bgRect(rect()), creatorTheme()->color(c));
} else {
const QImage *image = nullptr;
if (isDown()) {
@@ -870,9 +882,10 @@ void OutputPaneToggleButton::paintEvent(QPaintEvent*)
{
QColor c = creatorTheme()->color(Theme::OutputPaneButtonFlashColor);
c.setAlpha (m_flashTimer->currentFrame());
- QRect r = creatorTheme()->flag(Theme::FlatToolBars)
- ? rect() : rect().adjusted(numberAreaWidth(), 1, -1, -1);
- p.fillRect(r, c);
+ if (creatorTheme()->flag(Theme::FlatToolBars))
+ StyleHelper::drawPanelBgRect(&p, bgRect(rect()), c);
+ else
+ p.fillRect(rect().adjusted(numberAreaWidth(), 1, -1, -1), c);
}
p.setFont(font());
@@ -932,13 +945,7 @@ OutputPaneManageButton::OutputPaneManageButton()
{
setFocusPolicy(Qt::NoFocus);
setCheckable(true);
- setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
-}
-
-QSize OutputPaneManageButton::sizeHint() const
-{
- ensurePolished();
- return QSize(numberAreaWidth(), 16);
+ setFixedWidth(StyleHelper::toolbarStyle() == Utils::StyleHelper::ToolbarStyleCompact ? 17 : 21);
}
void OutputPaneManageButton::paintEvent(QPaintEvent*)
@@ -951,7 +958,9 @@ void OutputPaneManageButton::paintEvent(QPaintEvent*)
QStyle *s = style();
QStyleOption arrowOpt;
arrowOpt.initFrom(this);
- arrowOpt.rect = QRect(6, rect().center().y() - 3, 8, 8);
+ constexpr int arrowSize = 8;
+ arrowOpt.rect = QRect(0, 0, arrowSize, arrowSize);
+ arrowOpt.rect.moveCenter(rect().center());
arrowOpt.rect.translate(0, -3);
s->drawPrimitive(QStyle::PE_IndicatorArrowUp, &arrowOpt, &p, this);
arrowOpt.rect.translate(0, 6);
diff --git a/src/plugins/coreplugin/outputpanemanager.h b/src/plugins/coreplugin/outputpanemanager.h
index aa3a550a28..7dc7085d53 100644
--- a/src/plugins/coreplugin/outputpanemanager.h
+++ b/src/plugins/coreplugin/outputpanemanager.h
@@ -134,7 +134,6 @@ class OutputPaneManageButton : public QToolButton
Q_OBJECT
public:
OutputPaneManageButton();
- QSize sizeHint() const override;
void paintEvent(QPaintEvent*) override;
};
diff --git a/src/plugins/coreplugin/patchtool.cpp b/src/plugins/coreplugin/patchtool.cpp
index 9af0835380..744282728a 100644
--- a/src/plugins/coreplugin/patchtool.cpp
+++ b/src/plugins/coreplugin/patchtool.cpp
@@ -7,7 +7,7 @@
#include "patchtool.h"
#include <utils/environment.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QMessageBox>
@@ -76,7 +76,7 @@ static bool runPatchHelper(const QByteArray &input, const FilePath &workingDirec
return false;
}
- QtcProcess patchProcess;
+ Process patchProcess;
if (!workingDirectory.isEmpty())
patchProcess.setWorkingDirectory(workingDirectory);
Environment env = Environment::systemEnvironment();
@@ -93,7 +93,8 @@ static bool runPatchHelper(const QByteArray &input, const FilePath &workingDirec
if (patchAction == PatchAction::Revert)
args << "-R";
args << "--binary";
- MessageManager::writeDisrupting(Tr::tr("Running in %1: %2 %3")
+ MessageManager::writeDisrupting(
+ Tr::tr("Running in \"%1\": %2 %3.")
.arg(workingDirectory.toUserOutput(), patch.toUserOutput(), args.join(' ')));
patchProcess.setCommand({patch, args});
patchProcess.setWriteData(input);
diff --git a/src/plugins/coreplugin/plugindialog.cpp b/src/plugins/coreplugin/plugindialog.cpp
index 1298238722..24d8c8dff1 100644
--- a/src/plugins/coreplugin/plugindialog.cpp
+++ b/src/plugins/coreplugin/plugindialog.cpp
@@ -17,14 +17,11 @@
#include <extensionsystem/pluginview.h>
#include <utils/fancylineedit.h>
+#include <utils/layoutbuilder.h>
-#include <QCheckBox>
#include <QDialog>
#include <QDialogButtonBox>
-#include <QHBoxLayout>
-#include <QLabel>
#include <QPushButton>
-#include <QVBoxLayout>
using namespace Utils;
@@ -35,29 +32,27 @@ PluginDialog::PluginDialog(QWidget *parent)
: QDialog(parent),
m_view(new ExtensionSystem::PluginView(this))
{
- auto vl = new QVBoxLayout(this);
-
- auto filterLayout = new QHBoxLayout;
- vl->addLayout(filterLayout);
auto filterEdit = new Utils::FancyLineEdit(this);
+ filterEdit->setFocus();
filterEdit->setFiltering(true);
connect(filterEdit, &Utils::FancyLineEdit::filterChanged,
m_view, &ExtensionSystem::PluginView::setFilter);
- filterLayout->addWidget(filterEdit);
-
- vl->addWidget(m_view);
- m_detailsButton = new QPushButton(Tr::tr("Details"), this);
- m_errorDetailsButton = new QPushButton(Tr::tr("Error Details"), this);
- m_installButton = new QPushButton(Tr::tr("Install Plugin..."), this);
+ auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+ m_detailsButton = buttonBox->addButton(Tr::tr("Details"), QDialogButtonBox::ActionRole);
m_detailsButton->setEnabled(false);
+ m_errorDetailsButton = buttonBox->addButton(Tr::tr("Error Details"),
+ QDialogButtonBox::ActionRole);
m_errorDetailsButton->setEnabled(false);
+ m_installButton = buttonBox->addButton(Tr::tr("Install Plugin..."),
+ QDialogButtonBox::ActionRole);
- auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
- buttonBox->addButton(m_detailsButton, QDialogButtonBox::ActionRole);
- buttonBox->addButton(m_errorDetailsButton, QDialogButtonBox::ActionRole);
- buttonBox->addButton(m_installButton, QDialogButtonBox::ActionRole);
- vl->addWidget(buttonBox);
+ using namespace Layouting;
+ Column {
+ filterEdit,
+ m_view,
+ buttonBox,
+ }.attachTo(this);
resize(650, 400);
setWindowTitle(Tr::tr("Installed Plugins"));
@@ -115,13 +110,16 @@ void PluginDialog::openDetails(ExtensionSystem::PluginSpec *spec)
return;
QDialog dialog(this);
dialog.setWindowTitle(Tr::tr("Plugin Details of %1").arg(spec->name()));
- auto layout = new QVBoxLayout;
- dialog.setLayout(layout);
auto details = new ExtensionSystem::PluginDetailsView(&dialog);
- layout->addWidget(details);
details->update(spec);
QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, &dialog);
- layout->addWidget(buttons);
+
+ using namespace Layouting;
+ Column {
+ details,
+ buttons,
+ }.attachTo(&dialog);
+
connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
dialog.resize(400, 500);
@@ -135,13 +133,16 @@ void PluginDialog::openErrorDetails()
return;
QDialog dialog(this);
dialog.setWindowTitle(Tr::tr("Plugin Errors of %1").arg(spec->name()));
- auto layout = new QVBoxLayout;
- dialog.setLayout(layout);
auto errors = new ExtensionSystem::PluginErrorView(&dialog);
- layout->addWidget(errors);
errors->update(spec);
QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, &dialog);
- layout->addWidget(buttons);
+
+ using namespace Layouting;
+ Column {
+ errors,
+ buttons,
+ }.attachTo(&dialog);
+
connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
dialog.resize(500, 300);
diff --git a/src/plugins/coreplugin/plugininstallwizard.cpp b/src/plugins/coreplugin/plugininstallwizard.cpp
index 6547c78835..ac2dfd6c24 100644
--- a/src/plugins/coreplugin/plugininstallwizard.cpp
+++ b/src/plugins/coreplugin/plugininstallwizard.cpp
@@ -11,13 +11,14 @@
#include <extensionsystem/pluginspec.h>
#include <utils/archive.h>
+#include <utils/async.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <utils/infolabel.h>
+#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <utils/temporarydirectory.h>
#include <utils/wizard.h>
#include <utils/wizardpage.h>
@@ -33,7 +34,6 @@
#include <QPushButton>
#include <QRadioButton>
#include <QTextEdit>
-#include <QVBoxLayout>
#include <memory>
@@ -80,19 +80,15 @@ public:
, m_data(data)
{
setTitle(Tr::tr("Source"));
- auto vlayout = new QVBoxLayout;
- setLayout(vlayout);
auto label = new QLabel(
"<p>"
+ Tr::tr("Choose source location. This can be a plugin library file or a zip file.")
+ "</p>");
label->setWordWrap(true);
- vlayout->addWidget(label);
auto chooser = new PathChooser;
chooser->setExpectedKind(PathChooser::Any);
- vlayout->addWidget(chooser);
connect(chooser, &PathChooser::textChanged, this, [this, chooser] {
m_data->sourcePath = chooser->filePath();
updateWarnings();
@@ -101,7 +97,8 @@ public:
m_info = new InfoLabel;
m_info->setType(InfoLabel::Error);
m_info->setVisible(false);
- vlayout->addWidget(m_info);
+
+ Layouting::Column { label, chooser, m_info }.attachTo(this);
}
void updateWarnings()
@@ -153,8 +150,6 @@ public:
, m_data(data)
{
setTitle(Tr::tr("Check Archive"));
- auto vlayout = new QVBoxLayout;
- setLayout(vlayout);
m_label = new InfoLabel;
m_label->setElideMode(Qt::ElideNone);
@@ -163,13 +158,11 @@ public:
m_output = new QTextEdit;
m_output->setReadOnly(true);
- auto hlayout = new QHBoxLayout;
- hlayout->addWidget(m_label, 1);
- hlayout->addStretch();
- hlayout->addWidget(m_cancelButton);
-
- vlayout->addLayout(hlayout);
- vlayout->addWidget(m_output);
+ using namespace Layouting;
+ Column {
+ Row { m_label, st, m_cancelButton },
+ m_output,
+ }.attachTo(this);
}
void initializePage() final
@@ -221,8 +214,8 @@ public:
m_label->setText(Tr::tr("There was an error while unarchiving."));
}
} else { // unarchiving was successful, run a check
- m_archiveCheck = Utils::runAsync(
- [this](QFutureInterface<ArchiveIssue> &fi) { return checkContents(fi); });
+ m_archiveCheck = Utils::asyncRun([this](QPromise<ArchiveIssue> &promise)
+ { return checkContents(promise); });
Utils::onFinished(m_archiveCheck, this, [this](const QFuture<ArchiveIssue> &f) {
m_cancelButton->setVisible(false);
m_cancelButton->disconnect();
@@ -248,7 +241,7 @@ public:
}
// Async. Result is set if any issue was found.
- void checkContents(QFutureInterface<ArchiveIssue> &fi)
+ void checkContents(QPromise<ArchiveIssue> &promise)
{
QTC_ASSERT(m_tempDir.get(), return );
@@ -260,7 +253,7 @@ public:
QDir::Files | QDir::NoSymLinks,
QDirIterator::Subdirectories);
while (it.hasNext()) {
- if (fi.isCanceled())
+ if (promise.isCanceled())
return;
it.next();
PluginSpec *spec = PluginSpec::read(it.filePath());
@@ -275,18 +268,18 @@ public:
});
if (found != dependencies.constEnd()) {
if (!coreplugin->provides(found->name, found->version)) {
- fi.reportResult({Tr::tr("Plugin requires an incompatible version of %1 (%2).")
- .arg(Constants::IDE_DISPLAY_NAME)
- .arg(found->version),
- InfoLabel::Error});
+ promise.addResult(ArchiveIssue{
+ Tr::tr("Plugin requires an incompatible version of %1 (%2).")
+ .arg(Constants::IDE_DISPLAY_NAME).arg(found->version),
+ InfoLabel::Error});
return;
}
}
return; // successful / no error
}
}
- fi.reportResult({Tr::tr("Did not find %1 plugin.").arg(Constants::IDE_DISPLAY_NAME),
- InfoLabel::Error});
+ promise.addResult(ArchiveIssue{Tr::tr("Did not find %1 plugin.")
+ .arg(Constants::IDE_DISPLAY_NAME), InfoLabel::Error});
}
void cleanupPage() final
@@ -322,13 +315,9 @@ public:
, m_data(data)
{
setTitle(Tr::tr("Install Location"));
- auto vlayout = new QVBoxLayout;
- setLayout(vlayout);
auto label = new QLabel("<p>" + Tr::tr("Choose install location.") + "</p>");
label->setWordWrap(true);
- vlayout->addWidget(label);
- vlayout->addSpacing(10);
auto localInstall = new QRadioButton(Tr::tr("User plugins"));
localInstall->setChecked(!m_data->installIntoApplication);
@@ -338,10 +327,6 @@ public:
localLabel->setWordWrap(true);
localLabel->setAttribute(Qt::WA_MacSmallSize, true);
- vlayout->addWidget(localInstall);
- vlayout->addWidget(localLabel);
- vlayout->addSpacing(10);
-
auto appInstall = new QRadioButton(
Tr::tr("%1 installation").arg(Constants::IDE_DISPLAY_NAME));
appInstall->setChecked(m_data->installIntoApplication);
@@ -351,8 +336,11 @@ public:
.arg(Constants::IDE_DISPLAY_NAME));
appLabel->setWordWrap(true);
appLabel->setAttribute(Qt::WA_MacSmallSize, true);
- vlayout->addWidget(appInstall);
- vlayout->addWidget(appLabel);
+
+ using namespace Layouting;
+ Column {
+ label, Space(10), localInstall, localLabel, Space(10), appInstall, appLabel,
+ }.attachTo(this);
auto group = new QButtonGroup(this);
group->addButton(localInstall);
@@ -375,12 +363,9 @@ public:
{
setTitle(Tr::tr("Summary"));
- auto vlayout = new QVBoxLayout;
- setLayout(vlayout);
-
m_summaryLabel = new QLabel(this);
m_summaryLabel->setWordWrap(true);
- vlayout->addWidget(m_summaryLabel);
+ Layouting::Column { m_summaryLabel }.attachTo(this);
}
void initializePage() final
@@ -403,7 +388,7 @@ static std::function<void(FilePath)> postCopyOperation()
return;
// On macOS, downloaded files get a quarantine flag, remove it, otherwise it is a hassle
// to get it loaded as a plugin in Qt Creator.
- QtcProcess xattr;
+ Process xattr;
xattr.setTimeoutS(1);
xattr.setCommand({"/usr/bin/xattr", {"-d", "com.apple.quarantine", filePath.absoluteFilePath().toString()}});
xattr.runBlocking();
diff --git a/src/plugins/coreplugin/progressmanager/processprogress.cpp b/src/plugins/coreplugin/progressmanager/processprogress.cpp
index 9a545561a7..9aeebf4ecf 100644
--- a/src/plugins/coreplugin/progressmanager/processprogress.cpp
+++ b/src/plugins/coreplugin/progressmanager/processprogress.cpp
@@ -6,8 +6,8 @@
#include "progressmanager.h"
#include "../coreplugintr.h"
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QFutureWatcher>
@@ -18,13 +18,13 @@ namespace Core {
class ProcessProgressPrivate : public QObject
{
public:
- explicit ProcessProgressPrivate(ProcessProgress *progress, QtcProcess *process);
+ explicit ProcessProgressPrivate(ProcessProgress *progress, Process *process);
~ProcessProgressPrivate();
QString displayName() const;
void parseProgress(const QString &inputText);
- QtcProcess *m_process = nullptr;
+ Process *m_process = nullptr;
ProgressParser m_parser = {};
QFutureWatcher<void> m_watcher;
QFutureInterface<void> m_futureInterface;
@@ -33,7 +33,7 @@ public:
FutureProgress::KeepOnFinishType m_keep = FutureProgress::HideOnFinish;
};
-ProcessProgressPrivate::ProcessProgressPrivate(ProcessProgress *progress, QtcProcess *process)
+ProcessProgressPrivate::ProcessProgressPrivate(ProcessProgress *progress, Process *process)
: QObject(progress)
, m_process(process)
{
@@ -70,21 +70,22 @@ void ProcessProgressPrivate::parseProgress(const QString &inputText)
/*!
\class Core::ProcessProgress
+ \inmodule QtCreator
\brief The ProcessProgress class is responsible for showing progress of the running process.
It's possible to cancel the running process automatically after pressing a small 'x'
- indicator on progress panel. In this case QtcProcess::stop() method is being called.
+ indicator on progress panel. In this case Process::stop() method is being called.
*/
-ProcessProgress::ProcessProgress(QtcProcess *process)
+ProcessProgress::ProcessProgress(Process *process)
: QObject(process)
, d(new ProcessProgressPrivate(this, process))
{
connect(&d->m_watcher, &QFutureWatcher<void>::canceled, this, [this] {
d->m_process->stop(); // TODO: See TaskProgress::setAutoStopOnCancel
});
- connect(d->m_process, &QtcProcess::starting, this, [this] {
+ connect(d->m_process, &Process::starting, this, [this] {
d->m_futureInterface = QFutureInterface<void>();
d->m_futureInterface.setProgressRange(0, 1);
d->m_watcher.setFuture(d->m_futureInterface.future());
@@ -100,7 +101,7 @@ ProcessProgress::ProcessProgress(QtcProcess *process)
}
d->m_futureProgress->setKeepOnFinish(d->m_keep);
});
- connect(d->m_process, &QtcProcess::done, this, [this] {
+ connect(d->m_process, &Process::done, this, [this] {
if (d->m_process->result() != ProcessResult::FinishedWithSuccess)
d->m_futureInterface.reportCanceled();
d->m_futureInterface.reportFinished();
@@ -124,9 +125,9 @@ void ProcessProgress::setKeepOnFinish(FutureProgress::KeepOnFinishType keepType)
void ProcessProgress::setProgressParser(const ProgressParser &parser)
{
if (d->m_parser) {
- disconnect(d->m_process, &QtcProcess::textOnStandardOutput,
+ disconnect(d->m_process, &Process::textOnStandardOutput,
d.get(), &ProcessProgressPrivate::parseProgress);
- disconnect(d->m_process, &QtcProcess::textOnStandardError,
+ disconnect(d->m_process, &Process::textOnStandardError,
d.get(), &ProcessProgressPrivate::parseProgress);
}
d->m_parser = parser;
@@ -137,9 +138,9 @@ void ProcessProgress::setProgressParser(const ProgressParser &parser)
qWarning() << "Setting progress parser on a process without changing process' "
"text channel mode is no-op.");
- connect(d->m_process, &QtcProcess::textOnStandardOutput,
+ connect(d->m_process, &Process::textOnStandardOutput,
d.get(), &ProcessProgressPrivate::parseProgress);
- connect(d->m_process, &QtcProcess::textOnStandardError,
+ connect(d->m_process, &Process::textOnStandardError,
d.get(), &ProcessProgressPrivate::parseProgress);
}
diff --git a/src/plugins/coreplugin/progressmanager/processprogress.h b/src/plugins/coreplugin/progressmanager/processprogress.h
index fd2f64caf0..e91c0b7b3c 100644
--- a/src/plugins/coreplugin/progressmanager/processprogress.h
+++ b/src/plugins/coreplugin/progressmanager/processprogress.h
@@ -9,7 +9,7 @@
#include <QObject>
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace Core {
@@ -20,7 +20,7 @@ class ProcessProgressPrivate;
class CORE_EXPORT ProcessProgress : public QObject
{
public:
- ProcessProgress(Utils::QtcProcess *process); // Makes ProcessProgress a child of process
+ ProcessProgress(Utils::Process *process); // Makes ProcessProgress a child of process
~ProcessProgress() override;
void setDisplayName(const QString &name);
diff --git a/src/plugins/coreplugin/progressmanager/progressmanager.cpp b/src/plugins/coreplugin/progressmanager/progressmanager.cpp
index efc9731221..5504f8fe0c 100644
--- a/src/plugins/coreplugin/progressmanager/progressmanager.cpp
+++ b/src/plugins/coreplugin/progressmanager/progressmanager.cpp
@@ -109,7 +109,7 @@ namespace Core {
start a task concurrently in a different thread.
QtConcurrent has several different functions to run e.g.
a class function in a different thread. Qt Creator itself
- adds a few more in \c{src/libs/qtconcurrent/runextensions.h}.
+ adds a few more in \c{src/libs/utils/async.h}.
The QtConcurrent functions to run a concurrent task return a
\c QFuture object. This is what you want to give the
ProgressManager in the addTask() function.
@@ -740,6 +740,33 @@ FutureProgress *ProgressManager::addTimedTask(const QFutureInterface<void> &futu
return fp;
}
+FutureProgress *ProgressManager::addTimedTask(const QFuture<void> &future, const QString &title,
+ Id type, int expectedSeconds, ProgressFlags flags)
+{
+ QFutureInterface<void> dummyFutureInterface;
+ QFuture<void> dummyFuture = dummyFutureInterface.future();
+ FutureProgress *fp = m_instance->doAddTask(dummyFuture, title, type, flags);
+ (void) new ProgressTimer(dummyFutureInterface, expectedSeconds, fp);
+
+ QFutureWatcher<void> *dummyWatcher = new QFutureWatcher<void>(fp);
+ connect(dummyWatcher, &QFutureWatcher<void>::canceled, dummyWatcher, [future] {
+ QFuture<void> mutableFuture = future;
+ mutableFuture.cancel();
+ });
+ dummyWatcher->setFuture(dummyFuture);
+
+ QFutureWatcher<void> *origWatcher = new QFutureWatcher<void>(fp);
+ connect(origWatcher, &QFutureWatcher<void>::finished, origWatcher, [future, dummyFutureInterface] {
+ QFutureInterface<void> mutableDummyFutureInterface = dummyFutureInterface;
+ if (future.isCanceled())
+ mutableDummyFutureInterface.reportCanceled();
+ mutableDummyFutureInterface.reportFinished();
+ });
+ origWatcher->setFuture(future);
+
+ return fp;
+}
+
/*!
Shows the given \a text in a platform dependent way in the application
icon in the system's task bar or dock. This is used to show the number
diff --git a/src/plugins/coreplugin/progressmanager/progressmanager.h b/src/plugins/coreplugin/progressmanager/progressmanager.h
index 78c1b771ae..8c51bf4ccd 100644
--- a/src/plugins/coreplugin/progressmanager/progressmanager.h
+++ b/src/plugins/coreplugin/progressmanager/progressmanager.h
@@ -40,6 +40,8 @@ public:
Utils::Id type, ProgressFlags flags = {});
static FutureProgress *addTimedTask(const QFutureInterface<void> &fi, const QString &title,
Utils::Id type, int expectedSeconds, ProgressFlags flags = {});
+ static FutureProgress *addTimedTask(const QFuture<void> &future, const QString &title,
+ Utils::Id type, int expectedSeconds, ProgressFlags flags = {});
static void setApplicationLabel(const QString &text);
public slots:
diff --git a/src/plugins/coreplugin/progressmanager/progressview.cpp b/src/plugins/coreplugin/progressmanager/progressview.cpp
index 498ec8ae7b..45b10286dc 100644
--- a/src/plugins/coreplugin/progressmanager/progressview.cpp
+++ b/src/plugins/coreplugin/progressmanager/progressview.cpp
@@ -5,11 +5,19 @@
#include "../coreplugintr.h"
+#include <utils/icon.h>
+#include <utils/overlaywidget.h>
+
#include <QApplication>
#include <QEvent>
#include <QMouseEvent>
+#include <QPainter>
#include <QVBoxLayout>
+using namespace Utils;
+
+const int PIN_SIZE = 12;
+
namespace Core::Internal {
ProgressView::ProgressView(QWidget *parent)
@@ -21,15 +29,29 @@ ProgressView::ProgressView(QWidget *parent)
m_layout->setSpacing(0);
m_layout->setSizeConstraint(QLayout::SetFixedSize);
setWindowTitle(Tr::tr("Processes"));
+
+ auto pinButton = new OverlayWidget(this);
+ pinButton->attachToWidget(this);
+ pinButton->setAttribute(Qt::WA_TransparentForMouseEvents, false); // override OverlayWidget
+ pinButton->setPaintFunction([](QWidget *that, QPainter &p, QPaintEvent *) {
+ static const QIcon icon = Icon({{":/utils/images/pinned_small.png", Theme::IconsBaseColor}},
+ Icon::Tint)
+ .icon();
+ QRect iconRect(0, 0, PIN_SIZE, PIN_SIZE);
+ iconRect.moveTopRight(that->rect().topRight());
+ icon.paint(&p, iconRect);
+ });
+ pinButton->setVisible(false);
+ pinButton->installEventFilter(this);
+ m_pinButton = pinButton;
}
ProgressView::~ProgressView() = default;
void ProgressView::addProgressWidget(QWidget *widget)
{
- if (m_layout->count() == 0)
- m_anchorBottomRight = {}; // reset temporarily user-moved progress details
m_layout->insertWidget(0, widget);
+ m_pinButton->raise();
}
void ProgressView::removeProgressWidget(QWidget *widget)
@@ -63,9 +85,12 @@ bool ProgressView::event(QEvent *event)
reposition();
} else if (event->type() == QEvent::Enter) {
m_hovered = true;
+ if (m_anchorBottomRight != QPoint())
+ m_pinButton->setVisible(true);
emit hoveredChanged(m_hovered);
} else if (event->type() == QEvent::Leave) {
m_hovered = false;
+ m_pinButton->setVisible(false);
emit hoveredChanged(m_hovered);
} else if (event->type() == QEvent::Show) {
m_anchorBottomRight = {}; // reset temporarily user-moved progress details
@@ -78,6 +103,16 @@ bool ProgressView::eventFilter(QObject *obj, QEvent *event)
{
if ((obj == parentWidget() || obj == m_referenceWidget) && event->type() == QEvent::Resize)
reposition();
+ if (obj == m_pinButton && event->type() == QEvent::MouseButtonRelease) {
+ auto me = static_cast<QMouseEvent *>(event);
+ if (me->button() == Qt::LeftButton
+ && QRectF(m_pinButton->width() - PIN_SIZE, 0, PIN_SIZE, PIN_SIZE)
+ .contains(me->position())) {
+ me->accept();
+ m_anchorBottomRight = {};
+ reposition();
+ }
+ }
return false;
}
@@ -133,6 +168,9 @@ void ProgressView::reposition()
{
if (!parentWidget() || !m_referenceWidget)
return;
+
+ m_pinButton->setVisible(m_anchorBottomRight != QPoint() && m_hovered);
+
move(boundedInParent(this, topRightReferenceInParent() + m_anchorBottomRight, parentWidget())
- rect().bottomRight());
}
diff --git a/src/plugins/coreplugin/progressmanager/progressview.h b/src/plugins/coreplugin/progressmanager/progressview.h
index 37cfa24826..e0b42c8547 100644
--- a/src/plugins/coreplugin/progressmanager/progressview.h
+++ b/src/plugins/coreplugin/progressmanager/progressview.h
@@ -44,6 +44,7 @@ private:
QVBoxLayout *m_layout;
QWidget *m_referenceWidget = nullptr;
+ QWidget *m_pinButton = nullptr;
// dragging
std::optional<QPointF> m_clickPosition;
diff --git a/src/plugins/coreplugin/progressmanager/taskprogress.cpp b/src/plugins/coreplugin/progressmanager/taskprogress.cpp
index 6c7ee0bbb7..5ee5feb24a 100644
--- a/src/plugins/coreplugin/progressmanager/taskprogress.cpp
+++ b/src/plugins/coreplugin/progressmanager/taskprogress.cpp
@@ -6,14 +6,16 @@
#include "futureprogress.h"
#include "progressmanager.h"
+#include <solutions/tasking/tasktree.h>
+
#include <utils/mathutils.h>
#include <utils/qtcassert.h>
-#include <utils/tasktree.h>
#include <QFutureWatcher>
#include <QTimer>
using namespace Utils;
+using namespace Tasking;
namespace Core {
@@ -89,6 +91,7 @@ void TaskProgressPrivate::updateProgress()
/*!
\class Core::TaskProgress
+ \inmodule QtCreator
\brief The TaskProgress class is responsible for showing progress of the running task tree.
diff --git a/src/plugins/coreplugin/progressmanager/taskprogress.h b/src/plugins/coreplugin/progressmanager/taskprogress.h
index e15967fa2b..d82fc433d5 100644
--- a/src/plugins/coreplugin/progressmanager/taskprogress.h
+++ b/src/plugins/coreplugin/progressmanager/taskprogress.h
@@ -9,7 +9,7 @@
#include <QObject>
-namespace Utils { class TaskTree; }
+namespace Tasking { class TaskTree; }
namespace Core {
@@ -20,7 +20,7 @@ class CORE_EXPORT TaskProgress : public QObject
Q_OBJECT
public:
- TaskProgress(Utils::TaskTree *taskTree); // Makes TaskProgress a child of task tree
+ TaskProgress(Tasking::TaskTree *taskTree); // Makes TaskProgress a child of task tree
~TaskProgress() override;
void setId(Utils::Id id);
diff --git a/src/plugins/coreplugin/session.cpp b/src/plugins/coreplugin/session.cpp
new file mode 100644
index 0000000000..7238a09a2e
--- /dev/null
+++ b/src/plugins/coreplugin/session.cpp
@@ -0,0 +1,745 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "session.h"
+#include "session_p.h"
+
+#include "sessiondialog.h"
+
+#include "actionmanager/actioncontainer.h"
+#include "actionmanager/actionmanager.h"
+#include "coreconstants.h"
+#include "coreplugin.h"
+#include "editormanager/editormanager.h"
+#include "icore.h"
+#include "modemanager.h"
+#include "progressmanager/progressmanager.h"
+
+#include <extensionsystem/pluginmanager.h>
+#include <extensionsystem/pluginspec.h>
+
+#include <texteditor/texteditor.h>
+
+#include <utils/algorithm.h>
+#include <utils/filepath.h>
+#include <utils/macroexpander.h>
+#include <utils/qtcassert.h>
+#include <utils/stringutils.h>
+#include <utils/stylehelper.h>
+
+#include <QAction>
+#include <QActionGroup>
+#include <QDebug>
+#include <QMenu>
+#include <QMessageBox>
+#include <QPushButton>
+#include <QTimer>
+
+using namespace ExtensionSystem;
+using namespace Utils;
+
+namespace Core {
+
+namespace PE {
+struct Tr
+{
+ Q_DECLARE_TR_FUNCTIONS(QtC::ProjectExplorer)
+};
+} // namespace PE
+
+const char DEFAULT_SESSION[] = "default";
+const char LAST_ACTIVE_TIMES_KEY[] = "LastActiveTimes";
+const char STARTUPSESSION_KEY[] = "ProjectExplorer/SessionToRestore";
+const char LASTSESSION_KEY[] = "ProjectExplorer/StartupSession";
+const char AUTO_RESTORE_SESSION_SETTINGS_KEY[] = "ProjectExplorer/Settings/AutoRestoreLastSession";
+static bool kIsAutoRestoreLastSessionDefault = false;
+const char M_SESSION[] = "ProjectExplorer.Menu.Session";
+
+/*!
+ \class Core::SessionManager
+ \inmodule QtCreator
+
+ \brief The SessionManager class manages sessions.
+
+ TODO the interface of this class is not really great.
+ The implementation suffers from that all the functions from the
+ public interface just wrap around functions which do the actual work.
+ This could be improved.
+*/
+
+static SessionManager *m_instance = nullptr;
+SessionManagerPrivate *sb_d = nullptr;
+
+SessionManager::SessionManager()
+{
+ m_instance = this;
+ sb_d = new SessionManagerPrivate;
+
+ connect(ICore::instance(), &ICore::coreOpened, this, [] { sb_d->restoreStartupSession(); });
+
+ connect(ModeManager::instance(), &ModeManager::currentModeChanged,
+ this, &SessionManager::saveActiveMode);
+
+ connect(ICore::instance(), &ICore::saveSettingsRequested, this, [] {
+ if (!SessionManager::isLoadingSession())
+ SessionManager::saveSession();
+ sb_d->saveSettings();
+ });
+
+ connect(EditorManager::instance(), &EditorManager::editorOpened,
+ this, &SessionManager::markSessionFileDirty);
+ connect(EditorManager::instance(), &EditorManager::editorsClosed,
+ this, &SessionManager::markSessionFileDirty);
+ connect(EditorManager::instance(), &EditorManager::autoSaved, this, [] {
+ if (!PluginManager::isShuttingDown() && !SessionManager::isLoadingSession())
+ SessionManager::saveSession();
+ });
+
+ // session menu
+ ActionContainer *mfile = ActionManager::actionContainer(Core::Constants::M_FILE);
+ ActionContainer *msession = ActionManager::createMenu(M_SESSION);
+ msession->menu()->setTitle(PE::Tr::tr("S&essions"));
+ msession->setOnAllDisabledBehavior(ActionContainer::Show);
+ mfile->addMenu(msession, Core::Constants::G_FILE_OPEN);
+ sb_d->m_sessionMenu = msession->menu();
+ connect(mfile->menu(), &QMenu::aboutToShow, this, [] { sb_d->updateSessionMenu(); });
+
+ // session manager action
+ sb_d->m_sessionManagerAction = new QAction(PE::Tr::tr("&Manage..."), this);
+ sb_d->m_sessionMenu->addAction(sb_d->m_sessionManagerAction);
+ sb_d->m_sessionMenu->addSeparator();
+ Command *cmd = ActionManager::registerAction(sb_d->m_sessionManagerAction,
+ "ProjectExplorer.ManageSessions");
+ cmd->setDefaultKeySequence(QKeySequence());
+ connect(sb_d->m_sessionManagerAction,
+ &QAction::triggered,
+ SessionManager::instance(),
+ &SessionManager::showSessionManager);
+
+ MacroExpander *expander = Utils::globalMacroExpander();
+ expander->registerFileVariables("Session",
+ PE::Tr::tr("File where current session is saved."),
+ [] {
+ return SessionManager::sessionNameToFileName(
+ SessionManager::activeSession());
+ });
+ expander->registerVariable("Session:Name", PE::Tr::tr("Name of current session."), [] {
+ return SessionManager::activeSession();
+ });
+
+ sb_d->restoreSettings();
+}
+
+SessionManager::~SessionManager()
+{
+ emit m_instance->aboutToUnloadSession(sb_d->m_sessionName);
+ delete sb_d->m_writer;
+ delete sb_d;
+ sb_d = nullptr;
+}
+
+SessionManager *SessionManager::instance()
+{
+ return m_instance;
+}
+
+bool SessionManager::isDefaultVirgin()
+{
+ return isDefaultSession(sb_d->m_sessionName) && sb_d->m_virginSession;
+}
+
+bool SessionManager::isDefaultSession(const QString &session)
+{
+ return session == QLatin1String(DEFAULT_SESSION);
+}
+
+void SessionManager::saveActiveMode(Id mode)
+{
+ if (mode != Core::Constants::MODE_WELCOME)
+ setValue(QLatin1String("ActiveMode"), mode.toString());
+}
+
+bool SessionManager::isLoadingSession()
+{
+ return sb_d->m_loadingSession;
+}
+
+/*!
+ Lets other plugins store persistent values specified by \a name and \a value
+ within the session file.
+*/
+
+void SessionManager::setValue(const QString &name, const QVariant &value)
+{
+ if (sb_d->m_values.value(name) == value)
+ return;
+ sb_d->m_values.insert(name, value);
+}
+
+QVariant SessionManager::value(const QString &name)
+{
+ auto it = sb_d->m_values.constFind(name);
+ return (it == sb_d->m_values.constEnd()) ? QVariant() : *it;
+}
+
+void SessionManager::setSessionValue(const QString &name, const QVariant &value)
+{
+ sb_d->m_sessionValues.insert(name, value);
+}
+
+QVariant SessionManager::sessionValue(const QString &name, const QVariant &defaultValue)
+{
+ auto it = sb_d->m_sessionValues.constFind(name);
+ return (it == sb_d->m_sessionValues.constEnd()) ? defaultValue : *it;
+}
+
+QString SessionManager::activeSession()
+{
+ return sb_d->m_sessionName;
+}
+
+QStringList SessionManager::sessions()
+{
+ if (sb_d->m_sessions.isEmpty()) {
+ // We are not initialized yet, so do that now
+ const FilePaths sessionFiles =
+ ICore::userResourcePath().dirEntries({{"*qws"}}, QDir::Time | QDir::Reversed);
+ const QVariantMap lastActiveTimes = ICore::settings()->value(LAST_ACTIVE_TIMES_KEY).toMap();
+ for (const FilePath &file : sessionFiles) {
+ const QString &name = file.completeBaseName();
+ sb_d->m_sessionDateTimes.insert(name, file.lastModified());
+ const auto lastActiveTime = lastActiveTimes.find(name);
+ sb_d->m_lastActiveTimes.insert(name, lastActiveTime != lastActiveTimes.end()
+ ? lastActiveTime->toDateTime()
+ : file.lastModified());
+ if (name != QLatin1String(DEFAULT_SESSION))
+ sb_d->m_sessions << name;
+ }
+ sb_d->m_sessions.prepend(QLatin1String(DEFAULT_SESSION));
+ }
+ return sb_d->m_sessions;
+}
+
+QDateTime SessionManager::sessionDateTime(const QString &session)
+{
+ return sb_d->m_sessionDateTimes.value(session);
+}
+
+QDateTime SessionManager::lastActiveTime(const QString &session)
+{
+ return sb_d->m_lastActiveTimes.value(session);
+}
+
+FilePath SessionManager::sessionNameToFileName(const QString &session)
+{
+ return ICore::userResourcePath(session + ".qws");
+}
+
+/*!
+ Creates \a session, but does not actually create the file.
+
+ Returns whether the creation was successful.
+
+*/
+
+bool SessionManager::createSession(const QString &session)
+{
+ if (sessions().contains(session))
+ return false;
+ Q_ASSERT(sb_d->m_sessions.size() > 0);
+ sb_d->m_sessions.insert(1, session);
+ sb_d->m_lastActiveTimes.insert(session, QDateTime::currentDateTime());
+ emit instance()->sessionCreated(session);
+ return true;
+}
+
+bool SessionManager::renameSession(const QString &original, const QString &newName)
+{
+ if (!cloneSession(original, newName))
+ return false;
+ if (original == activeSession())
+ loadSession(newName);
+ emit instance()->sessionRenamed(original, newName);
+ return deleteSession(original);
+}
+
+void SessionManager::showSessionManager()
+{
+ saveSession();
+ Internal::SessionDialog sessionDialog(ICore::dialogParent());
+ sessionDialog.setAutoLoadSession(sb_d->isAutoRestoreLastSession());
+ sessionDialog.exec();
+ sb_d->setAutoRestoreLastSession(sessionDialog.autoLoadSession());
+}
+
+/*!
+ Shows a dialog asking the user to confirm the deletion of the specified
+ \a sessions.
+
+ Returns whether the user confirmed the deletion.
+*/
+bool SessionManager::confirmSessionDelete(const QStringList &sessions)
+{
+ const QString title = sessions.size() == 1 ? PE::Tr::tr("Delete Session")
+ : PE::Tr::tr("Delete Sessions");
+ const QString question
+ = sessions.size() == 1
+ ? PE::Tr::tr("Delete session %1?").arg(sessions.first())
+ : PE::Tr::tr("Delete these sessions?\n %1").arg(sessions.join("\n "));
+ return QMessageBox::question(ICore::dialogParent(),
+ title,
+ question,
+ QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes;
+}
+
+/*!
+ Deletes \a session name from session list and the file from disk.
+
+ Returns whether the deletion was successful.
+*/
+bool SessionManager::deleteSession(const QString &session)
+{
+ if (!sb_d->m_sessions.contains(session))
+ return false;
+ sb_d->m_sessions.removeOne(session);
+ sb_d->m_lastActiveTimes.remove(session);
+ emit instance()->sessionRemoved(session);
+ FilePath sessionFile = sessionNameToFileName(session);
+ if (sessionFile.exists())
+ return sessionFile.removeFile();
+ return false;
+}
+
+void SessionManager::deleteSessions(const QStringList &sessions)
+{
+ for (const QString &session : sessions)
+ deleteSession(session);
+}
+
+bool SessionManager::cloneSession(const QString &original, const QString &clone)
+{
+ if (!sb_d->m_sessions.contains(original))
+ return false;
+
+ FilePath sessionFile = sessionNameToFileName(original);
+ // If the file does not exist, we can still clone
+ if (!sessionFile.exists() || sessionFile.copyFile(sessionNameToFileName(clone))) {
+ sb_d->m_sessions.insert(1, clone);
+ sb_d->m_sessionDateTimes.insert(clone, sessionNameToFileName(clone).lastModified());
+ emit instance()->sessionCreated(clone);
+
+ return true;
+ }
+ return false;
+}
+
+static QString determineSessionToRestoreAtStartup()
+{
+ // TODO (session) move argument to core
+ // Process command line arguments first:
+ const bool lastSessionArg = PluginManager::specForPlugin(Internal::CorePlugin::instance())
+ ->arguments()
+ .contains("-lastsession");
+ if (lastSessionArg && !SessionManager::startupSession().isEmpty())
+ return SessionManager::startupSession();
+ const QStringList arguments = PluginManager::arguments();
+ QStringList sessions = SessionManager::sessions();
+ // We have command line arguments, try to find a session in them
+ // Default to no session loading
+ for (const QString &arg : arguments) {
+ if (sessions.contains(arg)) {
+ // Session argument
+ return arg;
+ }
+ }
+ // Handle settings only after command line arguments:
+ if (sb_d->m_isAutoRestoreLastSession)
+ return SessionManager::startupSession();
+ return {};
+}
+
+void SessionManagerPrivate::restoreStartupSession()
+{
+ m_isStartupSessionRestored = true;
+ QString sessionToRestoreAtStartup = determineSessionToRestoreAtStartup();
+ if (!sessionToRestoreAtStartup.isEmpty())
+ ModeManager::activateMode(Core::Constants::MODE_EDIT);
+
+ // We have command line arguments, try to find a session in them
+ QStringList arguments = PluginManager::arguments();
+ if (!sessionToRestoreAtStartup.isEmpty() && !arguments.isEmpty())
+ arguments.removeOne(sessionToRestoreAtStartup);
+
+ // Massage the argument list.
+ // Be smart about directories: If there is a session of that name, load it.
+ // Other than that, look for project files in it. The idea is to achieve
+ // 'Do what I mean' functionality when starting Creator in a directory with
+ // the single command line argument '.' and avoid editor warnings about not
+ // being able to open directories.
+ // In addition, convert "filename" "+45" or "filename" ":23" into
+ // "filename+45" and "filename:23".
+ if (!arguments.isEmpty()) {
+ const QStringList sessions = SessionManager::sessions();
+ for (int a = 0; a < arguments.size();) {
+ const QString &arg = arguments.at(a);
+ const QFileInfo fi(arg);
+ if (fi.isDir()) {
+ const QDir dir(fi.absoluteFilePath());
+ // Does the directory name match a session?
+ if (sessionToRestoreAtStartup.isEmpty() && sessions.contains(dir.dirName())) {
+ sessionToRestoreAtStartup = dir.dirName();
+ arguments.removeAt(a);
+ continue;
+ }
+ } // Done directories.
+ // Converts "filename" "+45" or "filename" ":23" into "filename+45" and "filename:23"
+ if (a && (arg.startsWith(QLatin1Char('+')) || arg.startsWith(QLatin1Char(':')))) {
+ arguments[a - 1].append(arguments.takeAt(a));
+ continue;
+ }
+ ++a;
+ } // for arguments
+ } // !arguments.isEmpty()
+
+ // Restore latest session or what was passed on the command line
+ SessionManager::loadSession(!sessionToRestoreAtStartup.isEmpty() ? sessionToRestoreAtStartup
+ : QString(),
+ true);
+
+ // delay opening projects from the command line even more
+ QTimer::singleShot(0, m_instance, [arguments] {
+ ICore::openFiles(Utils::transform(arguments, &FilePath::fromUserInput),
+ ICore::OpenFilesFlags(ICore::CanContainLineAndColumnNumbers
+ | ICore::SwitchMode));
+ emit m_instance->startupSessionRestored();
+ });
+}
+
+void SessionManagerPrivate::saveSettings()
+{
+ QtcSettings *s = ICore::settings();
+ QVariantMap times;
+ for (auto it = sb_d->m_lastActiveTimes.cbegin(); it != sb_d->m_lastActiveTimes.cend(); ++it)
+ times.insert(it.key(), it.value());
+ s->setValue(LAST_ACTIVE_TIMES_KEY, times);
+ if (SessionManager::isDefaultVirgin()) {
+ s->remove(STARTUPSESSION_KEY);
+ } else {
+ s->setValue(STARTUPSESSION_KEY, SessionManager::activeSession());
+ s->setValue(LASTSESSION_KEY, SessionManager::activeSession());
+ }
+ s->setValueWithDefault(AUTO_RESTORE_SESSION_SETTINGS_KEY,
+ sb_d->m_isAutoRestoreLastSession,
+ kIsAutoRestoreLastSessionDefault);
+}
+
+void SessionManagerPrivate::restoreSettings()
+{
+ sb_d->m_isAutoRestoreLastSession = ICore::settings()
+ ->value(AUTO_RESTORE_SESSION_SETTINGS_KEY,
+ kIsAutoRestoreLastSessionDefault)
+ .toBool();
+}
+
+bool SessionManagerPrivate::isAutoRestoreLastSession()
+{
+ return sb_d->m_isAutoRestoreLastSession;
+}
+
+void SessionManagerPrivate::setAutoRestoreLastSession(bool restore)
+{
+ sb_d->m_isAutoRestoreLastSession = restore;
+}
+
+void SessionManagerPrivate::updateSessionMenu()
+{
+ // Delete group of previous actions (the actions are owned by the group and are deleted with it)
+ auto oldGroup = m_sessionMenu->findChild<QActionGroup *>();
+ if (oldGroup)
+ delete oldGroup;
+ m_sessionMenu->clear();
+
+ m_sessionMenu->addAction(m_sessionManagerAction);
+ m_sessionMenu->addSeparator();
+ auto *ag = new QActionGroup(m_sessionMenu);
+ const QString activeSession = SessionManager::activeSession();
+ const bool isDefaultVirgin = SessionManager::isDefaultVirgin();
+
+ QStringList sessions = SessionManager::sessions();
+ std::sort(std::next(sessions.begin()), sessions.end(), [](const QString &s1, const QString &s2) {
+ return SessionManager::lastActiveTime(s1) > SessionManager::lastActiveTime(s2);
+ });
+ for (int i = 0; i < sessions.size(); ++i) {
+ const QString &session = sessions[i];
+
+ const QString actionText
+ = ActionManager::withNumberAccelerator(Utils::quoteAmpersands(session), i + 1);
+ QAction *act = ag->addAction(actionText);
+ act->setCheckable(true);
+ if (session == activeSession && !isDefaultVirgin)
+ act->setChecked(true);
+ QObject::connect(act, &QAction::triggered, SessionManager::instance(), [session] {
+ SessionManager::loadSession(session);
+ });
+ }
+ m_sessionMenu->addActions(ag->actions());
+ m_sessionMenu->setEnabled(true);
+}
+
+void SessionManagerPrivate::restoreValues(const PersistentSettingsReader &reader)
+{
+ const QStringList keys = reader.restoreValue(QLatin1String("valueKeys")).toStringList();
+ for (const QString &key : keys) {
+ QVariant value = reader.restoreValue(QLatin1String("value-") + key);
+ m_values.insert(key, value);
+ }
+}
+
+void SessionManagerPrivate::restoreSessionValues(const PersistentSettingsReader &reader)
+{
+ const QVariantMap values = reader.restoreValues();
+ // restore toplevel items that are not restored by restoreValues
+ const auto end = values.constEnd();
+ for (auto it = values.constBegin(); it != end; ++it) {
+ if (it.key() == "valueKeys" || it.key().startsWith("value-"))
+ continue;
+ m_sessionValues.insert(it.key(), it.value());
+ }
+}
+
+void SessionManagerPrivate::restoreEditors()
+{
+ const QVariant editorsettings = m_sessionValues.value("EditorSettings");
+ if (editorsettings.isValid()) {
+ EditorManager::restoreState(QByteArray::fromBase64(editorsettings.toByteArray()));
+ SessionManager::sessionLoadingProgress();
+ }
+}
+
+/*!
+ Returns the last session that was opened by the user.
+*/
+QString SessionManager::lastSession()
+{
+ return ICore::settings()->value(LASTSESSION_KEY).toString();
+}
+
+/*!
+ Returns the session that was active when \QC was last closed, if any.
+*/
+QString SessionManager::startupSession()
+{
+ return ICore::settings()->value(STARTUPSESSION_KEY).toString();
+}
+
+void SessionManager::markSessionFileDirty()
+{
+ sb_d->m_virginSession = false;
+}
+
+void SessionManager::sessionLoadingProgress()
+{
+ sb_d->m_future.setProgressValue(sb_d->m_future.progressValue() + 1);
+ QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+}
+
+void SessionManager::addSessionLoadingSteps(int steps)
+{
+ sb_d->m_future.setProgressRange(0, sb_d->m_future.progressMaximum() + steps);
+}
+
+/*
+ * ========== Notes on storing and loading the default session ==========
+ * The default session comes in two flavors: implicit and explicit. The implicit one,
+ * also referred to as "default virgin" in the code base, is the one that is active
+ * at start-up, if no session has been explicitly loaded due to command-line arguments
+ * or the "restore last session" setting in the session manager.
+ * The implicit default session silently turns into the explicit default session
+ * by loading a project or a file or changing settings in the Dependencies panel. The explicit
+ * default session can also be loaded by the user via the Welcome Screen.
+ * This mechanism somewhat complicates the handling of session-specific settings such as
+ * the ones in the task pane: Users expect that changes they make there become persistent, even
+ * when they are in the implicit default session. However, we can't just blindly store
+ * the implicit default session, because then we'd overwrite the project list of the explicit
+ * default session. Therefore, we use the following logic:
+ * - Upon start-up, if no session is to be explicitly loaded, we restore the parts of the
+ * explicit default session that are not related to projects, editors etc; the
+ * "general settings" of the session, so to speak.
+ * - When storing the implicit default session, we overwrite only these "general settings"
+ * of the explicit default session and keep the others as they are.
+ * - When switching from the implicit to the explicit default session, we keep the
+ * "general settings" and load everything else from the session file.
+ * This guarantees that user changes are properly transferred and nothing gets lost from
+ * either the implicit or the explicit default session.
+ *
+ */
+bool SessionManager::loadSession(const QString &session, bool initial)
+{
+ const bool loadImplicitDefault = session.isEmpty();
+ const bool switchFromImplicitToExplicitDefault = session == DEFAULT_SESSION
+ && sb_d->m_sessionName == DEFAULT_SESSION
+ && !initial;
+
+ // Do nothing if we have that session already loaded,
+ // exception if the session is the default virgin session
+ // we still want to be able to load the default session
+ if (session == sb_d->m_sessionName && !SessionManager::isDefaultVirgin())
+ return true;
+
+ if (!loadImplicitDefault && !SessionManager::sessions().contains(session))
+ return false;
+
+ // Try loading the file
+ FilePath fileName = SessionManager::sessionNameToFileName(loadImplicitDefault ? DEFAULT_SESSION
+ : session);
+ PersistentSettingsReader reader;
+ if (fileName.exists()) {
+ if (!reader.load(fileName)) {
+ QMessageBox::warning(ICore::dialogParent(),
+ PE::Tr::tr("Error while restoring session"),
+ PE::Tr::tr("Could not restore session %1")
+ .arg(fileName.toUserOutput()));
+
+ return false;
+ }
+
+ if (loadImplicitDefault) {
+ sb_d->restoreValues(reader);
+ emit SessionManager::instance()->sessionLoaded(DEFAULT_SESSION);
+ return true;
+ }
+ } else if (loadImplicitDefault) {
+ return true;
+ }
+
+ sb_d->m_loadingSession = true;
+
+ // Allow everyone to set something in the session and before saving
+ emit SessionManager::instance()->aboutToUnloadSession(sb_d->m_sessionName);
+
+ if (!saveSession()) {
+ sb_d->m_loadingSession = false;
+ return false;
+ }
+
+ // Clean up
+ if (!EditorManager::closeAllEditors()) {
+ sb_d->m_loadingSession = false;
+ return false;
+ }
+
+ if (!switchFromImplicitToExplicitDefault)
+ sb_d->m_values.clear();
+ sb_d->m_sessionValues.clear();
+
+ sb_d->m_sessionName = session;
+ delete sb_d->m_writer;
+ sb_d->m_writer = nullptr;
+ EditorManager::updateWindowTitles();
+
+ sb_d->m_virginSession = false;
+
+ ProgressManager::addTask(sb_d->m_future.future(),
+ PE::Tr::tr("Loading Session"),
+ "ProjectExplorer.SessionFile.Load");
+
+ sb_d->m_future.setProgressRange(0, 1 /*initialization*/ + 1 /*editors*/);
+ sb_d->m_future.setProgressValue(0);
+
+ if (fileName.exists()) {
+ if (!switchFromImplicitToExplicitDefault)
+ sb_d->restoreValues(reader);
+ sb_d->restoreSessionValues(reader);
+ }
+
+ QColor c = QColor(SessionManager::sessionValue("Color").toString());
+ if (c.isValid())
+ StyleHelper::setBaseColor(c);
+
+ SessionManager::sessionLoadingProgress();
+
+ sb_d->restoreEditors();
+
+ // let other code restore the session
+ emit SessionManager::instance()->aboutToLoadSession(session);
+
+ sb_d->m_future.reportFinished();
+ sb_d->m_future = QFutureInterface<void>();
+
+ sb_d->m_lastActiveTimes.insert(session, QDateTime::currentDateTime());
+
+ emit SessionManager::instance()->sessionLoaded(session);
+
+ sb_d->m_loadingSession = false;
+ return true;
+}
+
+bool SessionManager::saveSession()
+{
+ emit SessionManager::instance()->aboutToSaveSession();
+
+ const FilePath filePath = SessionManager::sessionNameToFileName(sb_d->m_sessionName);
+ QVariantMap data;
+
+ // See the explanation at loadSession() for how we handle the implicit default session.
+ if (SessionManager::isDefaultVirgin()) {
+ if (filePath.exists()) {
+ PersistentSettingsReader reader;
+ if (!reader.load(filePath)) {
+ QMessageBox::warning(ICore::dialogParent(),
+ PE::Tr::tr("Error while saving session"),
+ PE::Tr::tr("Could not save session %1")
+ .arg(filePath.toUserOutput()));
+ return false;
+ }
+ data = reader.restoreValues();
+ }
+ } else {
+ const QColor c = StyleHelper::requestedBaseColor();
+ if (c.isValid()) {
+ QString tmp = QString::fromLatin1("#%1%2%3")
+ .arg(c.red(), 2, 16, QLatin1Char('0'))
+ .arg(c.green(), 2, 16, QLatin1Char('0'))
+ .arg(c.blue(), 2, 16, QLatin1Char('0'));
+ setSessionValue("Color", tmp);
+ }
+ setSessionValue("EditorSettings", EditorManager::saveState().toBase64());
+
+ const auto end = sb_d->m_sessionValues.constEnd();
+ for (auto it = sb_d->m_sessionValues.constBegin(); it != end; ++it)
+ data.insert(it.key(), it.value());
+ }
+
+ const auto end = sb_d->m_values.constEnd();
+ QStringList keys;
+ for (auto it = sb_d->m_values.constBegin(); it != end; ++it) {
+ data.insert("value-" + it.key(), it.value());
+ keys << it.key();
+ }
+ data.insert("valueKeys", keys);
+
+ if (!sb_d->m_writer || sb_d->m_writer->fileName() != filePath) {
+ delete sb_d->m_writer;
+ sb_d->m_writer = new PersistentSettingsWriter(filePath, "QtCreatorSession");
+ }
+ const bool result = sb_d->m_writer->save(data, ICore::dialogParent());
+ if (result) {
+ if (!SessionManager::isDefaultVirgin())
+ sb_d->m_sessionDateTimes.insert(SessionManager::activeSession(),
+ QDateTime::currentDateTime());
+ } else {
+ QMessageBox::warning(ICore::dialogParent(),
+ PE::Tr::tr("Error while saving session"),
+ PE::Tr::tr("Could not save session to file \"%1\"")
+ .arg(sb_d->m_writer->fileName().toUserOutput()));
+ }
+
+ return result;
+}
+
+bool SessionManager::isStartupSessionRestored()
+{
+ return sb_d->m_isStartupSessionRestored;
+}
+
+} // namespace Core
diff --git a/src/plugins/coreplugin/session.h b/src/plugins/coreplugin/session.h
new file mode 100644
index 0000000000..30dc70edf4
--- /dev/null
+++ b/src/plugins/coreplugin/session.h
@@ -0,0 +1,90 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "core_global.h"
+
+#include <utils/id.h>
+#include <utils/persistentsettings.h>
+
+#include <QDateTime>
+#include <QString>
+#include <QStringList>
+
+namespace Core {
+
+class CORE_EXPORT SessionManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ SessionManager();
+ ~SessionManager() override;
+
+ static SessionManager *instance();
+
+ // higher level session management
+ static QString activeSession();
+ static QString lastSession();
+ static QString startupSession();
+ static QStringList sessions();
+ static QDateTime sessionDateTime(const QString &session);
+ static QDateTime lastActiveTime(const QString &session);
+
+ static bool createSession(const QString &session);
+
+ static bool confirmSessionDelete(const QStringList &sessions);
+ static bool deleteSession(const QString &session);
+ static void deleteSessions(const QStringList &sessions);
+
+ static bool cloneSession(const QString &original, const QString &clone);
+ static bool renameSession(const QString &original, const QString &newName);
+ static void showSessionManager();
+
+ static Utils::FilePath sessionNameToFileName(const QString &session);
+
+ static bool isDefaultVirgin();
+ static bool isDefaultSession(const QString &session);
+
+ // Let other plugins store persistent values within the session file
+ // These are settings that are also saved and loaded at startup, and are taken over
+ // to the default session when switching from implicit to explicit default session
+ static void setValue(const QString &name, const QVariant &value);
+ static QVariant value(const QString &name);
+
+ // These are settings that are specific to a session and are not loaded
+ // at startup and also not taken over to the default session when switching from implicit
+ static void setSessionValue(const QString &name, const QVariant &value);
+ static QVariant sessionValue(const QString &name, const QVariant &defaultValue = {});
+
+ static bool isLoadingSession();
+ static void markSessionFileDirty();
+
+ static void sessionLoadingProgress();
+ static void addSessionLoadingSteps(int steps);
+
+ static bool loadSession(const QString &session, bool initial = false);
+ static bool saveSession();
+
+signals:
+ void startupSessionRestored();
+ void aboutToUnloadSession(QString sessionName);
+ // Sent during session loading, after the values of the session are available via value() and
+ // sessionValue. Use to restore values from the new session
+ void aboutToLoadSession(QString sessionName);
+ void sessionLoaded(QString sessionName);
+ void aboutToSaveSession();
+
+ void sessionCreated(const QString &name);
+ void sessionRenamed(const QString &oldName, const QString &newName);
+ void sessionRemoved(const QString &name);
+
+public: // internal
+ static bool isStartupSessionRestored();
+
+private:
+ static void saveActiveMode(Utils::Id mode);
+};
+
+} // namespace Core
diff --git a/src/plugins/coreplugin/session_p.h b/src/plugins/coreplugin/session_p.h
new file mode 100644
index 0000000000..20ceecbcbc
--- /dev/null
+++ b/src/plugins/coreplugin/session_p.h
@@ -0,0 +1,57 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <utils/persistentsettings.h>
+
+#include <QFutureInterface>
+
+QT_BEGIN_NAMESPACE
+class QAction;
+class QMenu;
+QT_END_NAMESPACE
+
+using namespace Utils;
+
+namespace Core {
+
+class SessionManagerPrivate
+{
+public:
+ void restoreStartupSession();
+
+ void restoreValues(const PersistentSettingsReader &reader);
+ void restoreSessionValues(const PersistentSettingsReader &reader);
+ void restoreEditors();
+
+ void saveSettings();
+ void restoreSettings();
+ bool isAutoRestoreLastSession();
+ void setAutoRestoreLastSession(bool restore);
+
+ void updateSessionMenu();
+
+ static QString windowTitleAddition(const FilePath &filePath);
+ static QString sessionTitle(const FilePath &filePath);
+
+ QString m_sessionName = "default";
+ bool m_isStartupSessionRestored = false;
+ bool m_isAutoRestoreLastSession = false;
+ bool m_virginSession = true;
+ bool m_loadingSession = false;
+
+ mutable QStringList m_sessions;
+ mutable QHash<QString, QDateTime> m_sessionDateTimes;
+ QHash<QString, QDateTime> m_lastActiveTimes;
+
+ QMap<QString, QVariant> m_values;
+ QMap<QString, QVariant> m_sessionValues;
+ QFutureInterface<void> m_future;
+ PersistentSettingsWriter *m_writer = nullptr;
+
+ QMenu *m_sessionMenu;
+ QAction *m_sessionManagerAction;
+};
+
+extern SessionManagerPrivate *sb_d;
+
+} // namespace Core
diff --git a/src/plugins/projectexplorer/sessiondialog.cpp b/src/plugins/coreplugin/sessiondialog.cpp
index 745a876230..dd81f43fd0 100644
--- a/src/plugins/projectexplorer/sessiondialog.cpp
+++ b/src/plugins/coreplugin/sessiondialog.cpp
@@ -1,9 +1,8 @@
-// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "sessiondialog.h"
-#include "projectexplorertr.h"
#include "session.h"
#include "sessionview.h"
@@ -11,13 +10,21 @@
#include <utils/layoutbuilder.h>
#include <QCheckBox>
+#include <QCoreApplication>
#include <QDialogButtonBox>
#include <QInputDialog>
#include <QLabel>
#include <QPushButton>
#include <QValidator>
-namespace ProjectExplorer::Internal {
+namespace Core::Internal {
+
+namespace PE {
+struct Tr
+{
+ Q_DECLARE_TR_FUNCTIONS(QtC::ProjectExplorer)
+};
+} // namespace PE
class SessionValidator : public QValidator
{
@@ -77,12 +84,14 @@ SessionNameInputDialog::SessionNameInputDialog(QWidget *parent)
m_usedSwitchTo = true;
});
- using namespace Utils::Layouting;
+ // clang-format off
+ using namespace Layouting;
Column {
- Tr::tr("Enter the name of the session:"),
+ PE::Tr::tr("Enter the name of the session:"),
m_newSessionLineEdit,
buttons,
}.attachTo(this);
+ // clang-format on
connect(m_newSessionLineEdit, &QLineEdit::textChanged, [this](const QString &text) {
m_okButton->setEnabled(!text.isEmpty());
@@ -120,37 +129,35 @@ SessionDialog::SessionDialog(QWidget *parent) : QDialog(parent)
{
setObjectName("ProjectExplorer.SessionDialog");
resize(550, 400);
- setWindowTitle(Tr::tr("Session Manager"));
-
+ setWindowTitle(PE::Tr::tr("Session Manager"));
auto sessionView = new SessionView(this);
sessionView->setObjectName("sessionView");
sessionView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
sessionView->setActivationMode(Utils::DoubleClickActivation);
- auto createNewButton = new QPushButton(Tr::tr("&New"));
+ auto createNewButton = new QPushButton(PE::Tr::tr("&New"));
createNewButton->setObjectName("btCreateNew");
- m_openButton = new QPushButton(Tr::tr("&Open"));
+ m_openButton = new QPushButton(PE::Tr::tr("&Open"));
m_openButton->setObjectName("btOpen");
- m_renameButton = new QPushButton(Tr::tr("&Rename"));
- m_cloneButton = new QPushButton(Tr::tr("C&lone"));
- m_deleteButton = new QPushButton(Tr::tr("&Delete"));
+ m_renameButton = new QPushButton(PE::Tr::tr("&Rename"));
+ m_cloneButton = new QPushButton(PE::Tr::tr("C&lone"));
+ m_deleteButton = new QPushButton(PE::Tr::tr("&Delete"));
- m_autoLoadCheckBox = new QCheckBox(Tr::tr("Restore last session on startup"));
+ m_autoLoadCheckBox = new QCheckBox(PE::Tr::tr("Restore last session on startup"));
auto buttonBox = new QDialogButtonBox(this);
buttonBox->setStandardButtons(QDialogButtonBox::Close);
m_openButton->setDefault(true);
- // FIXME: Simplify translator's work.
- auto whatsASessionLabel = new QLabel(
- Tr::tr("<a href=\"qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html\">"
- "What is a Session?</a>"));
+ auto whatsASessionLabel = new QLabel(QString("<a href=\"qthelp://org.qt-project.qtcreator/doc/"
+ "creator-project-managing-sessions.html\">%1</a>")
+ .arg(PE::Tr::tr("What is a Session?")));
whatsASessionLabel->setOpenExternalLinks(true);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row {
@@ -220,4 +227,4 @@ void SessionDialog::updateActions(const QStringList &sessions)
m_deleteButton->setEnabled(!defaultIsSelected && !activeIsSelected);
}
-} // ProjectExplorer::Internal
+} // namespace Core::Internal
diff --git a/src/plugins/projectexplorer/sessiondialog.h b/src/plugins/coreplugin/sessiondialog.h
index 56cab430ea..b6512feab6 100644
--- a/src/plugins/projectexplorer/sessiondialog.h
+++ b/src/plugins/coreplugin/sessiondialog.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
@@ -12,7 +12,7 @@ class QLineEdit;
class QPushButton;
QT_END_NAMESPACE
-namespace ProjectExplorer::Internal {
+namespace Core::Internal {
class SessionDialog : public QDialog
{
@@ -53,4 +53,4 @@ private:
bool m_usedSwitchTo = false;
};
-} // ProjectExplorer::Internal
+} // namespace Core::Internal
diff --git a/src/plugins/projectexplorer/sessionmodel.cpp b/src/plugins/coreplugin/sessionmodel.cpp
index d8c9aa935e..fa0d8ee087 100644
--- a/src/plugins/projectexplorer/sessionmodel.cpp
+++ b/src/plugins/coreplugin/sessionmodel.cpp
@@ -1,9 +1,8 @@
-// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "sessionmodel.h"
-#include "projectexplorertr.h"
#include "session.h"
#include "sessiondialog.h"
@@ -16,11 +15,17 @@
#include <QFileInfo>
#include <QDir>
-using namespace Core;
using namespace Utils;
+using namespace Core::Internal;
-namespace ProjectExplorer {
-namespace Internal {
+namespace Core {
+
+namespace PE {
+struct Tr
+{
+ Q_DECLARE_TR_FUNCTIONS(QtC::ProjectExplorer)
+};
+} // namespace PE
SessionModel::SessionModel(QObject *parent)
: QAbstractTableModel(parent)
@@ -47,9 +52,11 @@ QVariant SessionModel::headerData(int section, Qt::Orientation orientation, int
switch (role) {
case Qt::DisplayRole:
switch (section) {
- case 0: result = Tr::tr("Session");
+ case 0:
+ result = PE::Tr::tr("Session");
break;
- case 1: result = Tr::tr("Last Modified");
+ case 1:
+ result = PE::Tr::tr("Last Modified");
break;
} // switch (section)
break;
@@ -87,17 +94,16 @@ QStringList pathsWithTildeHomePath(const FilePaths &paths)
QVariant SessionModel::data(const QModelIndex &index, int role) const
{
- QVariant result;
if (index.isValid()) {
QString sessionName = m_sortedSessions.at(index.row());
switch (role) {
case Qt::DisplayRole:
switch (index.column()) {
- case 0: result = sessionName;
- break;
- case 1: result = SessionManager::sessionDateTime(sessionName);
- break;
+ case 0:
+ return sessionName;
+ case 1:
+ return SessionManager::sessionDateTime(sessionName);
} // switch (section)
break;
case Qt::FontRole: {
@@ -110,44 +116,30 @@ QVariant SessionModel::data(const QModelIndex &index, int role) const
font.setBold(true);
else
font.setBold(false);
- result = font;
- } break;
+ return font;
+ }
case DefaultSessionRole:
- result = SessionManager::isDefaultSession(sessionName);
- break;
+ return SessionManager::isDefaultSession(sessionName);
case LastSessionRole:
- result = SessionManager::lastSession() == sessionName;
- break;
+ return SessionManager::lastSession() == sessionName;
case ActiveSessionRole:
- result = SessionManager::activeSession() == sessionName;
- break;
- case ProjectsPathRole:
- result = pathsWithTildeHomePath(SessionManager::projectsForSessionName(sessionName));
- break;
- case ProjectsDisplayRole:
- result = pathsToBaseNames(SessionManager::projectsForSessionName(sessionName));
- break;
+ return SessionManager::activeSession() == sessionName;
case ShortcutRole: {
const Id sessionBase = SESSION_BASE_ID;
if (Command *cmd = ActionManager::command(sessionBase.withSuffix(index.row() + 1)))
- result = cmd->keySequence().toString(QKeySequence::NativeText);
+ return cmd->keySequence().toString(QKeySequence::NativeText);
} break;
} // switch (role)
}
-
- return result;
+ return {};
}
QHash<int, QByteArray> SessionModel::roleNames() const
{
- static const QHash<int, QByteArray> extraRoles{
- {Qt::DisplayRole, "sessionName"},
- {DefaultSessionRole, "defaultSession"},
- {ActiveSessionRole, "activeSession"},
- {LastSessionRole, "lastSession"},
- {ProjectsPathRole, "projectsPath"},
- {ProjectsDisplayRole, "projectsName"}
- };
+ static const QHash<int, QByteArray> extraRoles{{Qt::DisplayRole, "sessionName"},
+ {DefaultSessionRole, "defaultSession"},
+ {ActiveSessionRole, "activeSession"},
+ {LastSessionRole, "lastSession"}};
QHash<int, QByteArray> roles = QAbstractTableModel::roleNames();
Utils::addToHash(&roles, extraRoles);
return roles;
@@ -195,8 +187,8 @@ void SessionModel::resetSessions()
void SessionModel::newSession(QWidget *parent)
{
SessionNameInputDialog sessionInputDialog(parent);
- sessionInputDialog.setWindowTitle(Tr::tr("New Session Name"));
- sessionInputDialog.setActionText(Tr::tr("&Create"), Tr::tr("Create and &Open"));
+ sessionInputDialog.setWindowTitle(PE::Tr::tr("New Session Name"));
+ sessionInputDialog.setActionText(PE::Tr::tr("&Create"), PE::Tr::tr("Create and &Open"));
runSessionNameInputDialog(&sessionInputDialog, [](const QString &newName) {
SessionManager::createSession(newName);
@@ -206,8 +198,8 @@ void SessionModel::newSession(QWidget *parent)
void SessionModel::cloneSession(QWidget *parent, const QString &session)
{
SessionNameInputDialog sessionInputDialog(parent);
- sessionInputDialog.setWindowTitle(Tr::tr("New Session Name"));
- sessionInputDialog.setActionText(Tr::tr("&Clone"), Tr::tr("Clone and &Open"));
+ sessionInputDialog.setWindowTitle(PE::Tr::tr("New Session Name"));
+ sessionInputDialog.setActionText(PE::Tr::tr("&Clone"), PE::Tr::tr("Clone and &Open"));
sessionInputDialog.setValue(session + " (2)");
runSessionNameInputDialog(&sessionInputDialog, [session](const QString &newName) {
@@ -229,8 +221,8 @@ void SessionModel::deleteSessions(const QStringList &sessions)
void SessionModel::renameSession(QWidget *parent, const QString &session)
{
SessionNameInputDialog sessionInputDialog(parent);
- sessionInputDialog.setWindowTitle(Tr::tr("Rename Session"));
- sessionInputDialog.setActionText(Tr::tr("&Rename"), Tr::tr("Rename and &Open"));
+ sessionInputDialog.setWindowTitle(PE::Tr::tr("Rename Session"));
+ sessionInputDialog.setActionText(PE::Tr::tr("&Rename"), PE::Tr::tr("Rename and &Open"));
sessionInputDialog.setValue(session);
runSessionNameInputDialog(&sessionInputDialog, [session](const QString &newName) {
@@ -244,7 +236,8 @@ void SessionModel::switchToSession(const QString &session)
emit sessionSwitched();
}
-void SessionModel::runSessionNameInputDialog(SessionNameInputDialog *sessionInputDialog, std::function<void(const QString &)> createSession)
+void SessionModel::runSessionNameInputDialog(SessionNameInputDialog *sessionInputDialog,
+ std::function<void(const QString &)> createSession)
{
if (sessionInputDialog->exec() == QDialog::Accepted) {
QString newSession = sessionInputDialog->value();
@@ -262,5 +255,4 @@ void SessionModel::runSessionNameInputDialog(SessionNameInputDialog *sessionInpu
}
}
-} // namespace Internal
-} // namespace ProjectExplorer
+} // namespace Core
diff --git a/src/plugins/projectexplorer/sessionmodel.h b/src/plugins/coreplugin/sessionmodel.h
index 82f1994210..79abd7ae3f 100644
--- a/src/plugins/projectexplorer/sessionmodel.h
+++ b/src/plugins/coreplugin/sessionmodel.h
@@ -1,20 +1,21 @@
-// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
+#include "core_global.h"
+
#include <QAbstractTableModel>
#include <functional>
-namespace ProjectExplorer {
-namespace Internal {
+namespace Core {
const char SESSION_BASE_ID[] = "Welcome.OpenSession";
-class SessionNameInputDialog;
+namespace Internal { class SessionNameInputDialog; }
-class SessionModel : public QAbstractTableModel
+class CORE_EXPORT SessionModel : public QAbstractTableModel
{
Q_OBJECT
@@ -23,8 +24,6 @@ public:
DefaultSessionRole = Qt::UserRole+1,
LastSessionRole,
ActiveSessionRole,
- ProjectsPathRole,
- ProjectsDisplayRole,
ShortcutRole
};
@@ -56,12 +55,12 @@ public slots:
void switchToSession(const QString &session);
private:
- void runSessionNameInputDialog(ProjectExplorer::Internal::SessionNameInputDialog *sessionInputDialog, std::function<void(const QString &)> createSession);
+ void runSessionNameInputDialog(Internal::SessionNameInputDialog *sessionInputDialog,
+ std::function<void(const QString &)> createSession);
QStringList m_sortedSessions;
int m_currentSortColumn = 0;
Qt::SortOrder m_currentSortOrder = Qt::AscendingOrder;
};
-} // namespace Internal
-} // namespace ProjectExplorer
+} // namespace Core
diff --git a/src/plugins/projectexplorer/sessionview.cpp b/src/plugins/coreplugin/sessionview.cpp
index e4b3b1a5d0..c3d7ed25b3 100644
--- a/src/plugins/projectexplorer/sessionview.cpp
+++ b/src/plugins/coreplugin/sessionview.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "sessionview.h"
@@ -12,7 +12,7 @@
#include <QStringList>
#include <QStyledItemDelegate>
-namespace ProjectExplorer {
+namespace Core {
namespace Internal {
// custom item delegate class
@@ -149,4 +149,4 @@ QStringList SessionView::selectedSessions() const
}
} // namespace Internal
-} // namespace ProjectExplorer
+} // namespace Core
diff --git a/src/plugins/projectexplorer/sessionview.h b/src/plugins/coreplugin/sessionview.h
index 656da080ea..2581a8b171 100644
--- a/src/plugins/projectexplorer/sessionview.h
+++ b/src/plugins/coreplugin/sessionview.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
@@ -9,7 +9,7 @@
#include <QAbstractTableModel>
-namespace ProjectExplorer::Internal {
+namespace Core::Internal {
class SessionView : public Utils::TreeView
{
@@ -44,4 +44,4 @@ private:
SessionModel m_sessionModel;
};
-} // ProjectExplorer::Internal
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/statusbarmanager.cpp b/src/plugins/coreplugin/statusbarmanager.cpp
index 4841337975..83d910867b 100644
--- a/src/plugins/coreplugin/statusbarmanager.cpp
+++ b/src/plugins/coreplugin/statusbarmanager.cpp
@@ -59,7 +59,6 @@ static void createStatusBarManager()
m_statusBarWidgets.append(w);
QWidget *w2 = createWidget(m_splitter);
- w2->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
m_splitter->addWidget(w2);
// second
w = createWidget(w2);
diff --git a/src/plugins/coreplugin/systemsettings.cpp b/src/plugins/coreplugin/systemsettings.cpp
index f34e7fb5b7..f6641bad08 100644
--- a/src/plugins/coreplugin/systemsettings.cpp
+++ b/src/plugins/coreplugin/systemsettings.cpp
@@ -51,8 +51,7 @@ const char showCrashButtonKey[] = "ShowCrashButton";
// TODO: move to somewhere in Utils
static QString formatSize(qint64 size)
{
- QStringList units {Tr::tr("Bytes"), Tr::tr("KB"), Tr::tr("MB"),
- Tr::tr("GB"), Tr::tr("TB")};
+ QStringList units{Tr::tr("Bytes"), Tr::tr("KiB"), Tr::tr("MiB"), Tr::tr("GiB"), Tr::tr("TiB")};
double outputSize = size;
int i;
for (i = 0; i < units.size() - 1; ++i) {
@@ -170,19 +169,18 @@ public:
{Tr::tr("When files are externally modified:"), Span(2, Row{m_reloadBehavior, st})});
form.addRow(
{m_autoSaveCheckBox, Span(2, Row{Tr::tr("Interval:"), m_autoSaveInterval, st})});
- form.addRow(Span(3, m_autoSaveRefactoringCheckBox));
+ form.addRow({Span(3, m_autoSaveRefactoringCheckBox)});
form.addRow({m_autoSuspendCheckBox,
Span(2, Row{autoSuspendLabel, m_autoSuspendMinDocumentCount, st})});
- form.addRow(Span(3, Row{m_warnBeforeOpeningBigFiles, m_bigFilesLimitSpinBox, st}));
- form.addRow(Span(3,
+ form.addRow({Span(3, Row{m_warnBeforeOpeningBigFiles, m_bigFilesLimitSpinBox, st})});
+ form.addRow({Span(3,
Row{Tr::tr("Maximum number of entries in \"Recent Files\":"),
m_maxRecentFilesSpinBox,
- st}));
- form.addRow(m_askBeforeExitCheckBox);
+ st})});
+ form.addRow({m_askBeforeExitCheckBox});
#ifdef ENABLE_CRASHPAD
- form.addRow(
- Span(3, Row{m_enableCrashReportingCheckBox, helpCrashReportingButton, st}));
- form.addRow(Span(3, Row{m_clearCrashReportsButton, m_crashReportsSizeText, st}));
+ form.addRow({Span(3, Row{m_enableCrashReportingCheckBox, helpCrashReportingButton, st})});
+ form.addRow({Span(3, Row{m_clearCrashReportsButton, m_crashReportsSizeText, st})});
#endif
Column {
@@ -438,9 +436,9 @@ void SystemSettingsWidget::resetFileBrowser()
void SystemSettingsWidget::updatePath()
{
- EnvironmentChange change;
- change.addAppendToPath(VcsManager::additionalToolsPath());
- m_patchChooser->setEnvironmentChange(change);
+ Environment env;
+ env.appendToPath(VcsManager::additionalToolsPath());
+ m_patchChooser->setEnvironment(env);
}
void SystemSettingsWidget::updateEnvironmentChangesLabel()
diff --git a/src/plugins/coreplugin/welcomepagehelper.cpp b/src/plugins/coreplugin/welcomepagehelper.cpp
index 0c46ea98f3..d0b1c9f3f9 100644
--- a/src/plugins/coreplugin/welcomepagehelper.cpp
+++ b/src/plugins/coreplugin/welcomepagehelper.cpp
@@ -7,6 +7,7 @@
#include <utils/algorithm.h>
#include <utils/fancylineedit.h>
+#include <utils/layoutbuilder.h>
#include <utils/qtcassert.h>
#include <utils/stylehelper.h>
#include <utils/theme/theme.h>
@@ -25,9 +26,11 @@
#include <qdrawutil.h>
+using namespace Utils;
+
namespace Core {
-using namespace Utils;
+using namespace WelcomePageHelpers;
static QColor themeColor(Theme::Color role)
{
@@ -123,6 +126,17 @@ SectionGridView::SectionGridView(QWidget *parent)
: GridView(parent)
{}
+void SectionGridView::setMaxRows(std::optional<int> max)
+{
+ m_maxRows = max;
+ updateGeometry();
+}
+
+std::optional<int> SectionGridView::maxRows() const
+{
+ return m_maxRows;
+}
+
bool SectionGridView::hasHeightForWidth() const
{
return true;
@@ -130,12 +144,39 @@ bool SectionGridView::hasHeightForWidth() const
int SectionGridView::heightForWidth(int width) const
{
- const int columnCount = width / Core::ListItemDelegate::GridItemWidth;
+ const int columnCount = qMax(1, width / Core::WelcomePageHelpers::GridItemWidth);
const int rowCount = (model()->rowCount() + columnCount - 1) / columnCount;
- return rowCount * Core::ListItemDelegate::GridItemHeight;
+ const int maxRowCount = m_maxRows ? std::min(*m_maxRows, rowCount) : rowCount;
+ return maxRowCount * Core::WelcomePageHelpers::GridItemHeight;
+}
+
+void SectionGridView::wheelEvent(QWheelEvent *e)
+{
+ if (m_maxRows) // circumvent scrolling of the list view
+ QWidget::wheelEvent(e);
+ else
+ GridView::wheelEvent(e);
}
-const QSize ListModel::defaultImageSize(214, 160);
+bool SectionGridView::event(QEvent *e)
+{
+ if (e->type() == QEvent::Resize) {
+ const auto itemsFit = [this](const QSize &size) {
+ const int maxColumns = std::max(size.width() / WelcomePageHelpers::GridItemWidth, 1);
+ const int maxRows = std::max(size.height() / WelcomePageHelpers::GridItemHeight, 1);
+ const int maxItems = maxColumns * maxRows;
+ const int items = model()->rowCount();
+ return maxItems >= items;
+ };
+ auto resizeEvent = static_cast<QResizeEvent *>(e);
+ const bool itemsCurrentyFit = itemsFit(size());
+ if (!resizeEvent->oldSize().isValid()
+ || itemsFit(resizeEvent->oldSize()) != itemsCurrentyFit) {
+ emit itemsFitChanged(itemsCurrentyFit);
+ }
+ }
+ return GridView::event(e);
+}
ListModel::ListModel(QObject *parent)
: QAbstractListModel(parent)
@@ -403,6 +444,7 @@ ListItemDelegate::ListItemDelegate()
: backgroundPrimaryColor(themeColor(Theme::Welcome_BackgroundPrimaryColor))
, backgroundSecondaryColor(themeColor(Theme::Welcome_BackgroundSecondaryColor))
, foregroundPrimaryColor(themeColor(Theme::Welcome_ForegroundPrimaryColor))
+ , foregroundSecondaryColor(themeColor(Theme::Welcome_ForegroundSecondaryColor))
, hoverColor(themeColor(Theme::Welcome_HoverColor))
, textColor(themeColor(Theme::Welcome_TextColor))
{
@@ -415,13 +457,14 @@ void ListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
const QRect rc = option.rect;
const QRect tileRect(0, 0, rc.width() - GridItemGap, rc.height() - GridItemGap);
- const QSize thumbnailBgSize = ListModel::defaultImageSize.grownBy(QMargins(1, 1, 1, 1));
+ const QSize thumbnailBgSize = GridItemImageSize.grownBy(QMargins(1, 1, 1, 1));
const QRect thumbnailBgRect((tileRect.width() - thumbnailBgSize.width()) / 2, GridItemGap,
thumbnailBgSize.width(), thumbnailBgSize.height());
const QRect textArea = tileRect.adjusted(GridItemGap, GridItemGap, -GridItemGap, -GridItemGap);
const bool hovered = option.state & QStyle::State_MouseOver;
+ constexpr int TagsSeparatorY = GridItemHeight - GridItemGap - 52;
constexpr int tagsBase = TagsSeparatorY + 17;
constexpr int shiftY = TagsSeparatorY - 16;
constexpr int nameY = TagsSeparatorY - 20;
@@ -524,7 +567,7 @@ void ListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
// The separator line below the example title.
const int ll = nameRect.height() + 3;
const QLine line = QLine(0, ll, textArea.width(), ll).translated(shiftedTextRect.topLeft());
- painter->setPen(foregroundPrimaryColor);
+ painter->setPen(foregroundSecondaryColor);
painter->setOpacity(animationProgress); // "fade in" separator line and description
painter->drawLine(line);
@@ -543,7 +586,7 @@ void ListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
}
// Separator line between text and 'Tags:' section
- painter->setPen(foregroundPrimaryColor);
+ painter->setPen(foregroundSecondaryColor);
painter->drawLine(QLineF(textArea.topLeft(), textArea.topRight())
.translated(0, TagsSeparatorY));
@@ -633,13 +676,9 @@ void ListItemDelegate::goon()
SectionedGridView::SectionedGridView(QWidget *parent)
: QStackedWidget(parent)
- , m_allItemsView(new Core::GridView(this))
{
- auto allItemsModel = new ListModel(this);
- allItemsModel->setPixmapFunction(m_pixmapFunction);
- // it just "borrows" the items from the section models:
- allItemsModel->setOwnsItems(false);
- m_filteredAllItemsModel = new Core::ListModelFilter(allItemsModel, this);
+ m_allItemsModel.reset(new ListModel);
+ m_allItemsModel->setPixmapFunction(m_pixmapFunction);
auto area = new QScrollArea(this);
area->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
@@ -650,21 +689,23 @@ SectionedGridView::SectionedGridView(QWidget *parent)
auto sectionedView = new QWidget;
auto layout = new QVBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
- layout->addStretch();
+ layout->addStretch(1);
sectionedView->setLayout(layout);
area->setWidget(sectionedView);
addWidget(area);
-
- m_allItemsView->setModel(m_filteredAllItemsModel);
- addWidget(m_allItemsView);
}
-SectionedGridView::~SectionedGridView() = default;
+SectionedGridView::~SectionedGridView()
+{
+ clear();
+}
void SectionedGridView::setItemDelegate(QAbstractItemDelegate *delegate)
{
- m_allItemsView->setItemDelegate(delegate);
+ m_itemDelegate = delegate;
+ if (m_allItemsView)
+ m_allItemsView->setItemDelegate(delegate);
for (GridView *view : std::as_const(m_gridViews))
view->setItemDelegate(delegate);
}
@@ -672,38 +713,97 @@ void SectionedGridView::setItemDelegate(QAbstractItemDelegate *delegate)
void SectionedGridView::setPixmapFunction(const Core::ListModel::PixmapFunction &pixmapFunction)
{
m_pixmapFunction = pixmapFunction;
- auto allProducts = static_cast<ListModel *>(m_filteredAllItemsModel->sourceModel());
- allProducts->setPixmapFunction(pixmapFunction);
+ m_allItemsModel->setPixmapFunction(pixmapFunction);
for (ListModel *model : std::as_const(m_sectionModels))
model->setPixmapFunction(pixmapFunction);
}
void SectionedGridView::setSearchString(const QString &searchString)
{
- int view = searchString.isEmpty() ? 0 // sectioned view
- : 1; // search view
- setCurrentIndex(view);
- m_filteredAllItemsModel->setSearchString(searchString);
+ if (searchString.isEmpty()) {
+ // back to previous view
+ m_allItemsView.reset();
+ if (m_zoomedInWidget)
+ setCurrentWidget(m_zoomedInWidget);
+ else
+ setCurrentIndex(0);
+ return;
+ }
+ if (!m_allItemsView) {
+ // We don't have a grid set for searching yet.
+ // Create all items view for filtering.
+ m_allItemsView.reset(new GridView);
+ m_allItemsView->setModel(new ListModelFilter(m_allItemsModel.get(), m_allItemsView.get()));
+ if (m_itemDelegate)
+ m_allItemsView->setItemDelegate(m_itemDelegate);
+ addWidget(m_allItemsView.get());
+ }
+ setCurrentWidget(m_allItemsView.get());
+ auto filterModel = static_cast<ListModelFilter *>(m_allItemsView.get()->model());
+ filterModel->setSearchString(searchString);
+}
+
+static QWidget *createSeparator(QWidget *parent)
+{
+ QWidget *line = Layouting::createHr(parent);
+ QSizePolicy linePolicy(QSizePolicy::Expanding, QSizePolicy::Ignored);
+ linePolicy.setHorizontalStretch(2);
+ line->setSizePolicy(linePolicy);
+ QPalette pal = line->palette();
+ pal.setColor(QPalette::Dark, Qt::transparent);
+ pal.setColor(QPalette::Light, themeColor(Theme::Welcome_ForegroundSecondaryColor));
+ line->setPalette(pal);
+ return line;
+}
+
+static QLabel *createLinkLabel(const QString &text, QWidget *parent)
+{
+ const QString linkColor = themeColor(Theme::Welcome_LinkColor).name();
+ auto link = new QLabel("<a href=\"link\" style=\"color: " + linkColor + ";\">"
+ + text + "</a>", parent);
+ return link;
}
ListModel *SectionedGridView::addSection(const Section &section, const QList<ListItem *> &items)
{
auto model = new ListModel(this);
model->setPixmapFunction(m_pixmapFunction);
+ // the sections only keep a weak reference to the items,
+ // they are owned by the allProducts model, since multiple sections can contain duplicates
+ // of the same item
+ model->setOwnsItems(false);
model->appendItems(items);
auto gridView = new SectionGridView(this);
- gridView->setItemDelegate(m_allItemsView->itemDelegate());
+ gridView->setItemDelegate(m_itemDelegate);
gridView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
gridView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
gridView->setModel(model);
+ gridView->setMaxRows(section.maxRows);
m_sectionModels.insert(section, model);
const auto it = m_gridViews.insert(section, gridView);
- auto sectionLabel = new QLabel(section.name);
+ QLabel *seeAllLink = createLinkLabel(Tr::tr("Show All") + " &gt;", this);
+ if (gridView->maxRows().has_value()) {
+ seeAllLink->setVisible(true);
+ connect(gridView, &SectionGridView::itemsFitChanged, seeAllLink, [seeAllLink](bool fits) {
+ seeAllLink->setVisible(!fits);
+ });
+ } else {
+ seeAllLink->setVisible(false);
+ }
+ connect(seeAllLink, &QLabel::linkActivated, this, [this, section] { zoomInSection(section); });
+ using namespace Layouting;
+ QWidget *sectionLabel = Row {
+ section.name,
+ createSeparator(this),
+ seeAllLink,
+ Space(HSpacing),
+ noMargin
+ }.emerge();
m_sectionLabels.append(sectionLabel);
- sectionLabel->setContentsMargins(0, Core::WelcomePageHelpers::ItemGap, 0, 0);
+ sectionLabel->setContentsMargins(0, ItemGap, 0, 0);
sectionLabel->setFont(Core::WelcomePageHelpers::brandFont());
auto scrollArea = qobject_cast<QScrollArea *>(widget(0));
auto vbox = qobject_cast<QVBoxLayout *>(scrollArea->widget()->layout());
@@ -715,8 +815,11 @@ ListModel *SectionedGridView::addSection(const Section &section, const QList<Lis
vbox->insertWidget(position + 1, gridView);
// add the items also to the all products model to be able to search correctly
- auto allProducts = static_cast<ListModel *>(m_filteredAllItemsModel->sourceModel());
- allProducts->appendItems(items);
+ const QSet<ListItem *> allItems = toSet(m_allItemsModel->items());
+ const QList<ListItem *> newItems = filtered(items, [&allItems](ListItem *item) {
+ return !allItems.contains(item);
+ });
+ m_allItemsModel->appendItems(newItems);
// only show section label(s) if there is more than one section
m_sectionLabels.at(0)->setVisible(m_sectionLabels.size() > 1);
@@ -726,14 +829,62 @@ ListModel *SectionedGridView::addSection(const Section &section, const QList<Lis
void SectionedGridView::clear()
{
- auto allProducts = static_cast<ListModel *>(m_filteredAllItemsModel->sourceModel());
- allProducts->clear();
+ m_allItemsModel->clear();
qDeleteAll(m_sectionModels);
qDeleteAll(m_sectionLabels);
qDeleteAll(m_gridViews);
m_sectionModels.clear();
m_sectionLabels.clear();
m_gridViews.clear();
+ m_allItemsView.reset();
+}
+
+void SectionedGridView::zoomInSection(const Section &section)
+{
+ auto zoomedInWidget = new QWidget(this);
+ auto layout = new QVBoxLayout;
+ layout->setContentsMargins(0, 0, 0, 0);
+ zoomedInWidget->setLayout(layout);
+
+ QLabel *backLink = createLinkLabel("&lt; " + Tr::tr("Back"), this);
+ connect(backLink, &QLabel::linkActivated, this, [this, zoomedInWidget] {
+ removeWidget(zoomedInWidget);
+ delete zoomedInWidget;
+ setCurrentIndex(0);
+ });
+ using namespace Layouting;
+ QWidget *sectionLabel = Row {
+ section.name,
+ createSeparator(this),
+ backLink,
+ Space(HSpacing),
+ noMargin
+ }.emerge();
+ sectionLabel->setContentsMargins(0, ItemGap, 0, 0);
+ sectionLabel->setFont(Core::WelcomePageHelpers::brandFont());
+
+ auto gridView = new GridView(zoomedInWidget);
+ gridView->setItemDelegate(m_itemDelegate);
+ gridView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ gridView->setModel(m_sectionModels.value(section));
+
+ layout->addWidget(sectionLabel);
+ layout->addWidget(gridView);
+
+ m_zoomedInWidget = zoomedInWidget;
+ addWidget(zoomedInWidget);
+ setCurrentWidget(zoomedInWidget);
}
+Section::Section(const QString &name, int priority)
+ : name(name)
+ , priority(priority)
+{}
+
+Section::Section(const QString &name, int priority, std::optional<int> maxRows)
+ : name(name)
+ , priority(priority)
+ , maxRows(maxRows)
+{}
+
} // namespace Core
diff --git a/src/plugins/coreplugin/welcomepagehelper.h b/src/plugins/coreplugin/welcomepagehelper.h
index 77760000e9..8599c227b9 100644
--- a/src/plugins/coreplugin/welcomepagehelper.h
+++ b/src/plugins/coreplugin/welcomepagehelper.h
@@ -24,6 +24,16 @@ namespace WelcomePageHelpers {
constexpr int HSpacing = 20;
constexpr int ItemGap = 4;
+
+constexpr int GridItemGap = 3 * ItemGap;
+constexpr int GridItemWidth = 240 + GridItemGap; // Extra GridItemGap as "spacing"
+constexpr int GridItemHeight = GridItemWidth;
+constexpr QSize GridItemImageSize(GridItemWidth - GridItemGap
+ - 2 * (GridItemGap + 1), // Horizontal margins + 1 pixel
+ GridItemHeight - GridItemGap
+ - GridItemGap - 1 // Upper margin + 1 pixel
+ - 67); // Bottom margin (for title + tags)
+
CORE_EXPORT QFont brandFont();
CORE_EXPORT QWidget *panelBar(QWidget *parent = nullptr);
@@ -40,7 +50,7 @@ public:
class CORE_EXPORT GridView : public QListView
{
public:
- explicit GridView(QWidget *parent);
+ explicit GridView(QWidget *parent = nullptr);
protected:
void leaveEvent(QEvent *) final;
@@ -48,11 +58,25 @@ protected:
class CORE_EXPORT SectionGridView : public GridView
{
+ Q_OBJECT
+
public:
explicit SectionGridView(QWidget *parent);
- bool hasHeightForWidth() const;
- int heightForWidth(int width) const;
+ void setMaxRows(std::optional<int> max);
+ std::optional<int> maxRows() const;
+
+ bool hasHeightForWidth() const override;
+ int heightForWidth(int width) const override;
+
+ void wheelEvent(QWheelEvent *e) override;
+ bool event(QEvent *e) override;
+
+signals:
+ void itemsFitChanged(bool fit);
+
+private:
+ std::optional<int> m_maxRows;
};
using OptModelIndex = std::optional<QModelIndex>;
@@ -74,7 +98,7 @@ public:
using PixmapFunction = std::function<QPixmap(QString)>;
- explicit ListModel(QObject *parent);
+ explicit ListModel(QObject *parent = nullptr);
~ListModel() override;
void appendItems(const QList<ListItem *> &items);
@@ -85,8 +109,6 @@ public:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
void setPixmapFunction(const PixmapFunction &fetchPixmapAndUpdatePixmapCache);
- static const QSize defaultImageSize;
-
void setOwnsItems(bool owns);
private:
@@ -128,11 +150,6 @@ public:
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
- static constexpr int GridItemGap = 3 * WelcomePageHelpers::ItemGap;
- static constexpr int GridItemWidth = 240 + GridItemGap;
- static constexpr int GridItemHeight = GridItemWidth;
- static constexpr int TagsSeparatorY = GridItemHeight - GridItemGap - 52;
-
signals:
void tagClicked(const QString &tag);
@@ -151,6 +168,7 @@ protected:
const QColor backgroundPrimaryColor;
const QColor backgroundSecondaryColor;
const QColor foregroundPrimaryColor;
+ const QColor foregroundSecondaryColor;
const QColor hoverColor;
const QColor textColor;
@@ -165,6 +183,9 @@ private:
class CORE_EXPORT Section
{
public:
+ Section(const QString &name, int priority);
+ Section(const QString &name, int priority, std::optional<int> maxRows);
+
friend bool operator<(const Section &lhs, const Section &rhs)
{
if (lhs.priority < rhs.priority)
@@ -179,6 +200,7 @@ public:
QString name;
int priority;
+ std::optional<int> maxRows;
};
class CORE_EXPORT SectionedGridView : public QStackedWidget
@@ -196,12 +218,16 @@ public:
void clear();
private:
+ void zoomInSection(const Section &section);
+
QMap<Section, Core::ListModel *> m_sectionModels;
QList<QWidget *> m_sectionLabels;
QMap<Section, Core::GridView *> m_gridViews;
- Core::GridView *m_allItemsView = nullptr;
- Core::ListModelFilter *m_filteredAllItemsModel = nullptr;
+ std::unique_ptr<Core::ListModel> m_allItemsModel;
+ std::unique_ptr<Core::GridView> m_allItemsView;
+ QPointer<QWidget> m_zoomedInWidget;
Core::ListModel::PixmapFunction m_pixmapFunction;
+ QAbstractItemDelegate *m_itemDelegate = nullptr;
};
} // namespace Core
diff --git a/src/plugins/cpaster/cpasterplugin.cpp b/src/plugins/cpaster/cpasterplugin.cpp
index 65b3d25179..a18b454c86 100644
--- a/src/plugins/cpaster/cpasterplugin.cpp
+++ b/src/plugins/cpaster/cpasterplugin.cpp
@@ -75,8 +75,6 @@ public:
&dpasteProto
};
- SettingsPage m_settingsPage{&m_settings};
-
QStringList m_fetchedSnippets;
UrlOpenProtocol m_urlOpen;
@@ -247,8 +245,8 @@ void CodePasterPluginPrivate::post(QString data, const QString &mimeType)
const FileDataList diffChunks = splitDiffToFiles(data);
const int dialogResult = diffChunks.isEmpty() ?
- view.show(username, {}, {}, m_settings.expiryDays.value(), data) :
- view.show(username, {}, {}, m_settings.expiryDays.value(), diffChunks);
+ view.show(username, {}, {}, m_settings.expiryDays(), data) :
+ view.show(username, {}, {}, m_settings.expiryDays(), diffChunks);
// Save new protocol in case user changed it.
if (dialogResult == QDialog::Accepted && m_settings.protocols.value() != view.protocol()) {
diff --git a/src/plugins/cpaster/fileshareprotocol.cpp b/src/plugins/cpaster/fileshareprotocol.cpp
index 2ca4bfa677..e9b85ee1af 100644
--- a/src/plugins/cpaster/fileshareprotocol.cpp
+++ b/src/plugins/cpaster/fileshareprotocol.cpp
@@ -6,7 +6,6 @@
#include "cpastertr.h"
#include "fileshareprotocolsettingspage.h"
-#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
#include <utils/fileutils.h>
@@ -29,20 +28,13 @@ const char textElementC[] = "text";
namespace CodePaster {
-FileShareProtocol::FileShareProtocol() :
- m_settingsPage(new FileShareProtocolSettingsPage(&m_settings))
-{
- m_settings.readSettings(Core::ICore::settings());
-}
+FileShareProtocol::FileShareProtocol() = default;
-FileShareProtocol::~FileShareProtocol()
-{
- delete m_settingsPage;
-}
+FileShareProtocol::~FileShareProtocol() = default;
QString FileShareProtocol::name() const
{
- return m_settingsPage->displayName();
+ return m_settings.displayName();
}
unsigned FileShareProtocol::capabilities() const
@@ -55,9 +47,9 @@ bool FileShareProtocol::hasSettings() const
return true;
}
-Core::IOptionsPage *FileShareProtocol::settingsPage() const
+const Core::IOptionsPage *FileShareProtocol::settingsPage() const
{
- return m_settingsPage;
+ return &m_settings;
}
static bool parse(const QString &fileName,
@@ -141,7 +133,7 @@ void FileShareProtocol::list()
QString errorMessage;
const QChar blank = QLatin1Char(' ');
const QFileInfoList entryInfoList = dir.entryInfoList();
- const int count = qMin(int(m_settings.displayCount.value()), entryInfoList.size());
+ const int count = qMin(int(m_settings.displayCount()), entryInfoList.size());
for (int i = 0; i < count; i++) {
const QFileInfo& entryFi = entryInfoList.at(i);
if (parse(entryFi.absoluteFilePath(), &errorMessage, &user, &description)) {
diff --git a/src/plugins/cpaster/fileshareprotocol.h b/src/plugins/cpaster/fileshareprotocol.h
index 100c0aece0..db03bb11bf 100644
--- a/src/plugins/cpaster/fileshareprotocol.h
+++ b/src/plugins/cpaster/fileshareprotocol.h
@@ -8,8 +8,6 @@
namespace CodePaster {
-class FileShareProtocolSettingsPage;
-
/* FileShareProtocol: Allows for pasting via a shared network
* drive by writing XML files. */
@@ -22,7 +20,7 @@ public:
QString name() const override;
unsigned capabilities() const override;
bool hasSettings() const override;
- Core::IOptionsPage *settingsPage() const override;
+ const Core::IOptionsPage *settingsPage() const override;
bool checkConfiguration(QString *errorMessage = nullptr) override;
void fetch(const QString &id) override;
@@ -35,7 +33,6 @@ public:
private:
FileShareProtocolSettings m_settings;
- FileShareProtocolSettingsPage *m_settingsPage;
};
} // CodePaster
diff --git a/src/plugins/cpaster/fileshareprotocolsettingspage.cpp b/src/plugins/cpaster/fileshareprotocolsettingspage.cpp
index 775c0438ab..99599a3cd8 100644
--- a/src/plugins/cpaster/fileshareprotocolsettingspage.cpp
+++ b/src/plugins/cpaster/fileshareprotocolsettingspage.cpp
@@ -15,33 +15,22 @@ namespace CodePaster {
FileShareProtocolSettings::FileShareProtocolSettings()
{
+ setId("X.CodePaster.FileSharePaster");
+ setDisplayName(Tr::tr("Fileshare"));
+ setCategory(Constants::CPASTER_SETTINGS_CATEGORY);
setSettingsGroup("FileSharePasterSettings");
- setAutoApply(false);
- registerAspect(&path);
path.setSettingsKey("Path");
- path.setDisplayStyle(StringAspect::PathChooserDisplay);
path.setExpectedKind(PathChooser::ExistingDirectory);
path.setDefaultValue(TemporaryDirectory::masterDirectoryPath());
path.setLabelText(Tr::tr("&Path:"));
- registerAspect(&displayCount);
displayCount.setSettingsKey("DisplayCount");
displayCount.setDefaultValue(10);
displayCount.setSuffix(' ' + Tr::tr("entries"));
displayCount.setLabelText(Tr::tr("&Display:"));
-}
-
-// Settings page
-FileShareProtocolSettingsPage::FileShareProtocolSettingsPage(FileShareProtocolSettings *settings)
-{
- setId("X.CodePaster.FileSharePaster");
- setDisplayName(Tr::tr("Fileshare"));
- setCategory(Constants::CPASTER_SETTINGS_CATEGORY);
- setSettings(settings);
-
- setLayouter([&s = *settings](QWidget *widget) {
+ setLayouter([this] {
using namespace Layouting;
auto label = new QLabel(Tr::tr(
@@ -49,15 +38,17 @@ FileShareProtocolSettingsPage::FileShareProtocolSettingsPage(FileShareProtocolSe
"simple files on a shared network drive. Files are never deleted."));
label->setWordWrap(true);
- Column {
+ return Column {
Form {
label, br,
- s.path,
- s.displayCount
+ path, br,
+ displayCount
},
st
- }.attachTo(widget);
+ };
});
+
+ readSettings();
}
} // namespace CodePaster
diff --git a/src/plugins/cpaster/fileshareprotocolsettingspage.h b/src/plugins/cpaster/fileshareprotocolsettingspage.h
index 3b92c3596d..8775fe1609 100644
--- a/src/plugins/cpaster/fileshareprotocolsettingspage.h
+++ b/src/plugins/cpaster/fileshareprotocolsettingspage.h
@@ -5,23 +5,15 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
-
namespace CodePaster {
-class FileShareProtocolSettings : public Utils::AspectContainer
+class FileShareProtocolSettings : public Core::PagedSettings
{
public:
FileShareProtocolSettings();
- Utils::StringAspect path;
- Utils::IntegerAspect displayCount;
-};
-
-class FileShareProtocolSettingsPage final : public Core::IOptionsPage
-{
-public:
- explicit FileShareProtocolSettingsPage(FileShareProtocolSettings *settings);
+ Utils::FilePathAspect path{this};
+ Utils::IntegerAspect displayCount{this};
};
} // CodePaster
diff --git a/src/plugins/cpaster/pasteselectdialog.cpp b/src/plugins/cpaster/pasteselectdialog.cpp
index 5b4c164c3b..2df87e7137 100644
--- a/src/plugins/cpaster/pasteselectdialog.cpp
+++ b/src/plugins/cpaster/pasteselectdialog.cpp
@@ -54,7 +54,7 @@ PasteSelectDialog::PasteSelectDialog(const QList<Protocol*> &protocols, QWidget
listFont.setStyleHint(QFont::TypeWriter);
m_listWidget->setFont(listFont);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Form {
Tr::tr("Protocol:"), m_protocolBox, br,
diff --git a/src/plugins/cpaster/pasteview.cpp b/src/plugins/cpaster/pasteview.cpp
index 02ae0f6530..b1f4864b6e 100644
--- a/src/plugins/cpaster/pasteview.cpp
+++ b/src/plugins/cpaster/pasteview.cpp
@@ -100,7 +100,7 @@ PasteView::PasteView(const QList<Protocol *> &protocols,
m_uiPatchList->setSortingEnabled(false);
m_uiPatchList->setSortingEnabled(__sortingEnabled);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_uiPatchList,
@@ -108,6 +108,7 @@ PasteView::PasteView(const QList<Protocol *> &protocols,
}.attachTo(groupBox);
Column {
+ spacing(2),
Form {
Tr::tr("Protocol:"), m_protocolBox, br,
Tr::tr("&Expires after:"), m_expirySpinBox, br,
@@ -117,7 +118,7 @@ PasteView::PasteView(const QList<Protocol *> &protocols,
m_uiComment,
m_stackedWidget,
buttonBox
- }.setSpacing(2).attachTo(this);
+ }.attachTo(this);
connect(m_uiPatchList, &QListWidget::itemChanged, this, &PasteView::contentChanged);
diff --git a/src/plugins/cpaster/protocol.cpp b/src/plugins/cpaster/protocol.cpp
index d26396205a..c2630d09c1 100644
--- a/src/plugins/cpaster/protocol.cpp
+++ b/src/plugins/cpaster/protocol.cpp
@@ -47,7 +47,7 @@ bool Protocol::checkConfiguration(QString *)
return true;
}
-Core::IOptionsPage *Protocol::settingsPage() const
+const Core::IOptionsPage *Protocol::settingsPage() const
{
return nullptr;
}
diff --git a/src/plugins/cpaster/protocol.h b/src/plugins/cpaster/protocol.h
index a8d233619b..90464768c2 100644
--- a/src/plugins/cpaster/protocol.h
+++ b/src/plugins/cpaster/protocol.h
@@ -38,7 +38,7 @@ public:
virtual unsigned capabilities() const = 0;
virtual bool hasSettings() const;
- virtual Core::IOptionsPage *settingsPage() const;
+ virtual const Core::IOptionsPage *settingsPage() const;
virtual bool checkConfiguration(QString *errorMessage = nullptr);
virtual void fetch(const QString &id) = 0;
diff --git a/src/plugins/cpaster/settings.cpp b/src/plugins/cpaster/settings.cpp
index 7459ae1098..1c3d9e5e4d 100644
--- a/src/plugins/cpaster/settings.cpp
+++ b/src/plugins/cpaster/settings.cpp
@@ -16,13 +16,16 @@ Settings::Settings()
{
setSettingsGroup("CodePaster");
setAutoApply(false);
+ setId("A.CodePaster.General");
+ setDisplayName(Tr::tr("General"));
+ setCategory(Constants::CPASTER_SETTINGS_CATEGORY);
+ setDisplayCategory(Tr::tr("Code Pasting"));
+ setCategoryIconPath(":/cpaster/images/settingscategory_cpaster.png");
- registerAspect(&username);
username.setDisplayStyle(StringAspect::LineEditDisplay);
username.setSettingsKey("UserName");
username.setLabelText(Tr::tr("Username:"));
- registerAspect(&protocols);
protocols.setSettingsKey("DefaultProtocol");
protocols.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
protocols.setLabelText(Tr::tr("Default protocol:"));
@@ -33,48 +36,31 @@ Settings::Settings()
return protocols.indexForDisplay(val.toString());
});
- registerAspect(&expiryDays);
expiryDays.setSettingsKey("ExpiryDays");
expiryDays.setDefaultValue(1);
expiryDays.setSuffix(Tr::tr(" Days"));
expiryDays.setLabelText(Tr::tr("&Expires after:"));
- registerAspect(&copyToClipboard);
copyToClipboard.setSettingsKey("CopyToClipboard");
copyToClipboard.setDefaultValue(true);
copyToClipboard.setLabelText(Tr::tr("Copy-paste URL to clipboard"));
- registerAspect(&displayOutput);
displayOutput.setSettingsKey("DisplayOutput");
displayOutput.setDefaultValue(true);
displayOutput.setLabelText(Tr::tr("Display General Messages after sending a post"));
-}
-// SettingsPage
-
-SettingsPage::SettingsPage(Settings *settings)
-{
- setId("A.CodePaster.General");
- setDisplayName(Tr::tr("General"));
- setCategory(Constants::CPASTER_SETTINGS_CATEGORY);
- setDisplayCategory(Tr::tr("Code Pasting"));
- setCategoryIconPath(":/cpaster/images/settingscategory_cpaster.png");
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- Settings &s = *settings;
+ setLayouter([this] {
using namespace Layouting;
-
- Column {
+ return Column {
Form {
- s.protocols,
- s.username,
- s.expiryDays
+ protocols, br,
+ username, br,
+ expiryDays
},
- s.copyToClipboard,
- s.displayOutput,
+ copyToClipboard,
+ displayOutput,
st
- }.attachTo(widget);
+ };
});
}
diff --git a/src/plugins/cpaster/settings.h b/src/plugins/cpaster/settings.h
index d54cc393bc..1e7d03be76 100644
--- a/src/plugins/cpaster/settings.h
+++ b/src/plugins/cpaster/settings.h
@@ -5,26 +5,18 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
-
namespace CodePaster {
-class Settings : public Utils::AspectContainer
+class Settings : public Core::PagedSettings
{
public:
Settings();
- Utils::StringAspect username;
- Utils::SelectionAspect protocols;
- Utils::IntegerAspect expiryDays;
- Utils::BoolAspect copyToClipboard;
- Utils::BoolAspect displayOutput;
-};
-
-class SettingsPage final : public Core::IOptionsPage
-{
-public:
- SettingsPage(Settings *settings);
+ Utils::StringAspect username{this};
+ Utils::SelectionAspect protocols{this};
+ Utils::IntegerAspect expiryDays{this};
+ Utils::BoolAspect copyToClipboard{this};
+ Utils::BoolAspect displayOutput{this};
};
} // CodePaster
diff --git a/src/plugins/cpaster/stickynotespasteprotocol.h b/src/plugins/cpaster/stickynotespasteprotocol.h
index e17508e084..f212c883ef 100644
--- a/src/plugins/cpaster/stickynotespasteprotocol.h
+++ b/src/plugins/cpaster/stickynotespasteprotocol.h
@@ -38,7 +38,6 @@ private:
QNetworkReply *m_listReply = nullptr;
QString m_fetchId;
- int m_postId = -1;
bool m_hostChecked = false;
};
diff --git a/src/plugins/cppcheck/cppcheckconstants.h b/src/plugins/cppcheck/cppcheckconstants.h
index 6efbb2f615..427bdfc10f 100644
--- a/src/plugins/cppcheck/cppcheckconstants.h
+++ b/src/plugins/cppcheck/cppcheckconstants.h
@@ -9,23 +9,6 @@ const char TEXTMARK_CATEGORY_ID[] = "Cppcheck";
const char OPTIONS_PAGE_ID[] = "Analyzer.Cppcheck.Settings";
-const char SETTINGS_ID[] = "Cppcheck";
-const char SETTINGS_BINARY[] = "binary";
-const char SETTINGS_WARNING[] = "warning";
-const char SETTINGS_STYLE[] = "style";
-const char SETTINGS_PERFORMANCE[] = "performance";
-const char SETTINGS_PORTABILITY[] = "portability";
-const char SETTINGS_INFORMATION[] = "information";
-const char SETTINGS_UNUSED_FUNCTION[] = "unusedFunction";
-const char SETTINGS_MISSING_INCLUDE[] = "missingInclude";
-const char SETTINGS_INCONCLUSIVE[] = "inconclusive";
-const char SETTINGS_FORCE_DEFINES[] = "forceDefines";
-const char SETTINGS_CUSTOM_ARGUMENTS[] = "customArguments";
-const char SETTINGS_IGNORE_PATTERNS[] = "ignorePatterns";
-const char SETTINGS_SHOW_OUTPUT[] = "showOutput";
-const char SETTINGS_ADD_INCLUDE_PATHS[] = "addIncludePaths";
-const char SETTINGS_GUESS_ARGUMENTS[] = "guessArguments";
-
const char CHECK_PROGRESS_ID[] = "Cppcheck.CheckingTask";
const char MANUAL_CHECK_PROGRESS_ID[] = "Cppcheck.ManualCheckingTask";
diff --git a/src/plugins/cppcheck/cppcheckmanualrundialog.cpp b/src/plugins/cppcheck/cppcheckmanualrundialog.cpp
index 1f53e6835b..060f888cc2 100644
--- a/src/plugins/cppcheck/cppcheckmanualrundialog.cpp
+++ b/src/plugins/cppcheck/cppcheckmanualrundialog.cpp
@@ -18,11 +18,9 @@
namespace Cppcheck::Internal {
-ManualRunDialog::ManualRunDialog(const CppcheckOptions &options,
+ManualRunDialog::ManualRunDialog(QWidget *optionsWidget,
const ProjectExplorer::Project *project)
- : QDialog(),
- m_options(new OptionsWidget(this)),
- m_model(new ProjectExplorer::SelectableFilesFromDirModel(this))
+ : m_model(new ProjectExplorer::SelectableFilesFromDirModel(this))
{
QTC_ASSERT(project, return );
@@ -55,21 +53,12 @@ ManualRunDialog::ManualRunDialog(const CppcheckOptions &options,
});
auto layout = new QVBoxLayout(this);
- layout->addWidget(m_options);
+ layout->addWidget(optionsWidget);
layout->addWidget(view);
layout->addWidget(buttons);
- if (auto layout = m_options->layout())
+ if (auto layout = optionsWidget->layout())
layout->setContentsMargins(0, 0, 0, 0);
-
- m_options->load(options);
-}
-
-CppcheckOptions ManualRunDialog::options() const
-{
- CppcheckOptions result;
- m_options->save(result);
- return result;
}
Utils::FilePaths ManualRunDialog::filePaths() const
diff --git a/src/plugins/cppcheck/cppcheckmanualrundialog.h b/src/plugins/cppcheck/cppcheckmanualrundialog.h
index 8ffadcc48a..460a85e75c 100644
--- a/src/plugins/cppcheck/cppcheckmanualrundialog.h
+++ b/src/plugins/cppcheck/cppcheckmanualrundialog.h
@@ -17,21 +17,15 @@ class SelectableFilesFromDirModel;
namespace Cppcheck::Internal {
-class OptionsWidget;
-class CppcheckOptions;
-
class ManualRunDialog : public QDialog
{
public:
- ManualRunDialog(const CppcheckOptions &options,
- const ProjectExplorer::Project *project);
+ ManualRunDialog(QWidget *optionsWidget, const ProjectExplorer::Project *project);
- CppcheckOptions options() const;
Utils::FilePaths filePaths() const;
QSize sizeHint() const override;
private:
- OptionsWidget *m_options;
ProjectExplorer::SelectableFilesFromDirModel *m_model;
};
diff --git a/src/plugins/cppcheck/cppcheckoptions.cpp b/src/plugins/cppcheck/cppcheckoptions.cpp
index 11563430cc..1289ba0e0e 100644
--- a/src/plugins/cppcheck/cppcheckoptions.cpp
+++ b/src/plugins/cppcheck/cppcheckoptions.cpp
@@ -12,6 +12,7 @@
#include <utils/flowlayout.h>
#include <utils/hostosinfo.h>
#include <utils/pathchooser.h>
+#include <utils/layoutbuilder.h>
#include <utils/qtcassert.h>
#include <utils/variablechooser.h>
@@ -20,211 +21,119 @@
#include <debugger/analyzer/analyzericons.h>
#include <debugger/debuggertr.h>
-#include <QCheckBox>
-#include <QFormLayout>
-
using namespace Utils;
namespace Cppcheck::Internal {
-OptionsWidget::OptionsWidget(QWidget *parent)
- : QWidget(parent),
- m_binary(new Utils::PathChooser(this)),
- m_customArguments(new QLineEdit(this)),
- m_ignorePatterns(new QLineEdit(this)),
- m_warning(new QCheckBox(Tr::tr("Warnings"), this)),
- m_style(new QCheckBox(Tr::tr("Style"), this)),
- m_performance(new QCheckBox(Tr::tr("Performance"), this)),
- m_portability(new QCheckBox(Tr::tr("Portability"), this)),
- m_information(new QCheckBox(Tr::tr("Information"), this)),
- m_unusedFunction(new QCheckBox(Tr::tr("Unused functions"), this)),
- m_missingInclude(new QCheckBox(Tr::tr("Missing includes"), this)),
- m_inconclusive(new QCheckBox(Tr::tr("Inconclusive errors"), this)),
- m_forceDefines(new QCheckBox(Tr::tr("Check all define combinations"), this)),
- m_showOutput(new QCheckBox(Tr::tr("Show raw output"), this)),
- m_addIncludePaths(new QCheckBox(Tr::tr("Add include paths"), this)),
- m_guessArguments(new QCheckBox(Tr::tr("Calculate additional arguments"), this))
-{
- m_binary->setExpectedKind(Utils::PathChooser::ExistingCommand);
- m_binary->setCommandVersionArguments({"--version"});
-
- auto variableChooser = new Utils::VariableChooser(this);
- variableChooser->addSupportedWidget (m_customArguments);
-
- m_unusedFunction->setToolTip(Tr::tr("Disables multithreaded check."));
- m_ignorePatterns->setToolTip(Tr::tr("Comma-separated wildcards of full file paths. "
- "Files still can be checked if others include them."));
- m_addIncludePaths->setToolTip(Tr::tr("Can find missing includes but makes "
- "checking slower. Use only when needed."));
- m_guessArguments->setToolTip(Tr::tr("Like C++ standard and language."));
-
- auto layout = new QFormLayout(this);
- layout->addRow(Tr::tr("Binary:"), m_binary);
-
- auto checks = new Utils::FlowLayout;
- layout->addRow(Tr::tr("Checks:"), checks);
- checks->addWidget(m_warning);
- checks->addWidget(m_style);
- checks->addWidget(m_performance);
- checks->addWidget(m_portability);
- checks->addWidget(m_information);
- checks->addWidget(m_unusedFunction);
- checks->addWidget(m_missingInclude);
-
- layout->addRow(Tr::tr("Custom arguments:"), m_customArguments);
- layout->addRow(Tr::tr("Ignored file patterns:"), m_ignorePatterns);
- auto flags = new Utils::FlowLayout;
- layout->addRow(flags);
- flags->addWidget(m_inconclusive);
- flags->addWidget(m_forceDefines);
- flags->addWidget(m_showOutput);
- flags->addWidget(m_addIncludePaths);
- flags->addWidget(m_guessArguments);
-}
-
-void OptionsWidget::load(const CppcheckOptions &options)
-{
- m_binary->setFilePath(options.binary);
- m_customArguments->setText(options.customArguments);
- m_ignorePatterns->setText(options.ignoredPatterns);
- m_warning->setChecked(options.warning);
- m_style->setChecked(options.style);
- m_performance->setChecked(options.performance);
- m_portability->setChecked(options.portability);
- m_information->setChecked(options.information);
- m_unusedFunction->setChecked(options.unusedFunction);
- m_missingInclude->setChecked(options.missingInclude);
- m_inconclusive->setChecked(options.inconclusive);
- m_forceDefines->setChecked(options.forceDefines);
- m_showOutput->setChecked(options.showOutput);
- m_addIncludePaths->setChecked(options.addIncludePaths);
- m_guessArguments->setChecked(options.guessArguments);
-}
-
-void OptionsWidget::save(CppcheckOptions &options) const
-{
- options.binary = m_binary->filePath();
- options.customArguments = m_customArguments->text();
- options.ignoredPatterns = m_ignorePatterns->text();
- options.warning = m_warning->isChecked();
- options.style = m_style->isChecked();
- options.performance = m_performance->isChecked();
- options.portability = m_portability->isChecked();
- options.information = m_information->isChecked();
- options.unusedFunction = m_unusedFunction->isChecked();
- options.missingInclude = m_missingInclude->isChecked();
- options.inconclusive = m_inconclusive->isChecked();
- options.forceDefines = m_forceDefines->isChecked();
- options.showOutput = m_showOutput->isChecked();
- options.addIncludePaths = m_addIncludePaths->isChecked();
- options.guessArguments = m_guessArguments->isChecked();
-}
-
-CppcheckOptionsPage::CppcheckOptionsPage(CppcheckTool &tool, CppcheckTrigger &trigger):
- m_tool(tool),
- m_trigger(trigger)
+CppcheckOptions::CppcheckOptions()
{
setId(Constants::OPTIONS_PAGE_ID);
setDisplayName(Tr::tr("Cppcheck"));
setCategory("T.Analyzer");
setDisplayCategory(::Debugger::Tr::tr("Analyzer"));
setCategoryIconPath(Analyzer::Icons::SETTINGSCATEGORY_ANALYZER);
+ setSettingsGroup("Cppcheck");
- CppcheckOptions options;
+ binary.setSettingsKey("binary");
+ binary.setExpectedKind(PathChooser::ExistingCommand);
+ binary.setCommandVersionArguments({"--version"});
+ binary.setLabelText(Tr::tr("Binary:"));
if (HostOsInfo::isAnyUnixHost()) {
- options.binary = "cppcheck";
+ binary.setDefaultValue("cppcheck");
} else {
FilePath programFiles = FilePath::fromUserInput(qtcEnvironmentVariable("PROGRAMFILES"));
if (programFiles.isEmpty())
programFiles = "C:/Program Files";
- options.binary = programFiles / "Cppcheck/cppcheck.exe";
+ binary.setDefaultValue(programFiles.pathAppended("Cppcheck/cppcheck.exe").toString());
}
- load(options);
+ warning.setSettingsKey("warning");
+ warning.setDefaultValue(true);
+ warning.setLabelText(Tr::tr("Warnings"));
- m_tool.updateOptions(options);
-}
+ style.setSettingsKey("style");
+ style.setDefaultValue(true);
+ style.setLabelText(Tr::tr("Style"));
-QWidget *CppcheckOptionsPage::widget()
-{
- if (!m_widget)
- m_widget = new OptionsWidget;
- m_widget->load(m_tool.options());
- return m_widget.data();
-}
+ performance.setSettingsKey("performance");
+ performance.setDefaultValue(true);
+ performance.setLabelText(Tr::tr("Performance"));
-void CppcheckOptionsPage::apply()
-{
- CppcheckOptions options;
- m_widget->save(options);
- save(options);
- m_tool.updateOptions(options);
- m_trigger.recheck();
-}
+ portability.setSettingsKey("portability");
+ portability.setDefaultValue(true);
+ portability.setLabelText(Tr::tr("Portability"));
-void CppcheckOptionsPage::finish()
-{
-}
+ information.setSettingsKey("information");
+ information.setDefaultValue(true);
+ information.setLabelText(Tr::tr("Information"));
-void CppcheckOptionsPage::save(const CppcheckOptions &options) const
-{
- QSettings *s = Core::ICore::settings();
- QTC_ASSERT(s, return);
- s->beginGroup(Constants::SETTINGS_ID);
- s->setValue(Constants::SETTINGS_BINARY, options.binary.toString());
- s->setValue(Constants::SETTINGS_CUSTOM_ARGUMENTS, options.customArguments);
- s->setValue(Constants::SETTINGS_IGNORE_PATTERNS, options.ignoredPatterns);
- s->setValue(Constants::SETTINGS_WARNING, options.warning);
- s->setValue(Constants::SETTINGS_STYLE, options.style);
- s->setValue(Constants::SETTINGS_PERFORMANCE, options.performance);
- s->setValue(Constants::SETTINGS_PORTABILITY, options.portability);
- s->setValue(Constants::SETTINGS_INFORMATION, options.information);
- s->setValue(Constants::SETTINGS_UNUSED_FUNCTION, options.unusedFunction);
- s->setValue(Constants::SETTINGS_MISSING_INCLUDE, options.missingInclude);
- s->setValue(Constants::SETTINGS_INCONCLUSIVE, options.inconclusive);
- s->setValue(Constants::SETTINGS_FORCE_DEFINES, options.forceDefines);
- s->setValue(Constants::SETTINGS_SHOW_OUTPUT, options.showOutput);
- s->setValue(Constants::SETTINGS_ADD_INCLUDE_PATHS, options.addIncludePaths);
- s->setValue(Constants::SETTINGS_GUESS_ARGUMENTS, options.guessArguments);
- s->endGroup();
+ unusedFunction.setSettingsKey("unusedFunction");
+ unusedFunction.setLabelText(Tr::tr("Unused functions"));
+ unusedFunction.setToolTip(Tr::tr("Disables multithreaded check."));
+
+ missingInclude.setSettingsKey("missingInclude");
+ missingInclude.setLabelText(Tr::tr("Missing includes"));
+
+ inconclusive.setSettingsKey("inconclusive");
+ inconclusive.setLabelText(Tr::tr("Inconclusive errors"));
+
+ forceDefines.setSettingsKey("forceDefines");
+ forceDefines.setLabelText(Tr::tr("Check all define combinations"));
+
+ customArguments.setSettingsKey("customArguments");
+ customArguments.setDisplayStyle(StringAspect::LineEditDisplay);
+ customArguments.setLabelText(Tr::tr("Custom arguments:"));
+
+ ignoredPatterns.setSettingsKey("ignoredPatterns");
+ ignoredPatterns.setDisplayStyle(StringAspect::LineEditDisplay);
+ ignoredPatterns.setLabelText(Tr::tr("Ignored file patterns:"));
+ ignoredPatterns.setToolTip(Tr::tr("Comma-separated wildcards of full file paths. "
+ "Files still can be checked if others include them."));
+
+ showOutput.setSettingsKey("showOutput");
+ showOutput.setLabelText(Tr::tr("Show raw output"));
+
+ addIncludePaths.setSettingsKey("addIncludePaths");
+ addIncludePaths.setLabelText(Tr::tr("Add include paths"));
+ addIncludePaths.setToolTip(Tr::tr("Can find missing includes but makes "
+ "checking slower. Use only when needed."));
+
+ guessArguments.setSettingsKey("guessArguments");
+ guessArguments.setDefaultValue(true);
+ guessArguments.setLabelText(Tr::tr("Calculate additional arguments"));
+ guessArguments.setToolTip(Tr::tr("Like C++ standard and language."));
+
+ setLayouter(layouter());
+
+ readSettings();
}
-void CppcheckOptionsPage::load(CppcheckOptions &options) const
+std::function<Layouting::LayoutItem()> CppcheckOptions::layouter()
{
- QSettings *s = Core::ICore::settings();
- QTC_ASSERT(s, return);
- s->beginGroup(Constants::SETTINGS_ID);
- options.binary = FilePath::fromString(s->value(Constants::SETTINGS_BINARY,
- options.binary.toString()).toString());
- options.customArguments = s->value(Constants::SETTINGS_CUSTOM_ARGUMENTS,
- options.customArguments).toString();
- options.ignoredPatterns = s->value(Constants::SETTINGS_IGNORE_PATTERNS,
- options.ignoredPatterns).toString();
- options.warning = s->value(Constants::SETTINGS_WARNING,
- options.warning).toBool();
- options.style = s->value(Constants::SETTINGS_STYLE,
- options.style).toBool();
- options.performance = s->value(Constants::SETTINGS_PERFORMANCE,
- options.performance).toBool();
- options.portability = s->value(Constants::SETTINGS_PORTABILITY,
- options.portability).toBool();
- options.information = s->value(Constants::SETTINGS_INFORMATION,
- options.information).toBool();
- options.unusedFunction = s->value(Constants::SETTINGS_UNUSED_FUNCTION,
- options.unusedFunction).toBool();
- options.missingInclude = s->value(Constants::SETTINGS_MISSING_INCLUDE,
- options.missingInclude).toBool();
- options.inconclusive = s->value(Constants::SETTINGS_INCONCLUSIVE,
- options.inconclusive).toBool();
- options.forceDefines = s->value(Constants::SETTINGS_FORCE_DEFINES,
- options.forceDefines).toBool();
- options.showOutput = s->value(Constants::SETTINGS_SHOW_OUTPUT,
- options.showOutput).toBool();
- options.addIncludePaths = s->value(Constants::SETTINGS_ADD_INCLUDE_PATHS,
- options.addIncludePaths).toBool();
- options.guessArguments = s->value(Constants::SETTINGS_GUESS_ARGUMENTS,
- options.guessArguments).toBool();
- s->endGroup();
+ return [this] {
+ using namespace Layouting;
+ return Form {
+ binary, br,
+ Tr::tr("Checks:"), Flow {
+ warning,
+ style,
+ performance,
+ portability,
+ information,
+ unusedFunction,
+ missingInclude
+ }, br,
+ customArguments, br,
+ ignoredPatterns, br,
+ Flow {
+ inconclusive,
+ forceDefines,
+ showOutput,
+ addIncludePaths,
+ guessArguments
+ }
+ };
+ };
}
} // Cppcheck::Internal
diff --git a/src/plugins/cppcheck/cppcheckoptions.h b/src/plugins/cppcheck/cppcheckoptions.h
index 7c23d69f60..eb3e1c7ddb 100644
--- a/src/plugins/cppcheck/cppcheckoptions.h
+++ b/src/plugins/cppcheck/cppcheckoptions.h
@@ -4,88 +4,32 @@
#pragma once
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/filepath.h>
-
-#include <QCoreApplication>
-#include <QPointer>
-#include <QWidget>
-
-QT_BEGIN_NAMESPACE
-class QLineEdit;
-class QCheckBox;
-QT_END_NAMESPACE
-
-namespace Utils { class PathChooser; }
namespace Cppcheck::Internal {
-class CppcheckTool;
-class CppcheckTrigger;
-class OptionsWidget;
-
-class CppcheckOptions final
-{
-public:
- Utils::FilePath binary;
-
- bool warning = true;
- bool style = true;
- bool performance = true;
- bool portability = true;
- bool information = true;
- bool unusedFunction = false;
- bool missingInclude = false;
- bool inconclusive = false;
- bool forceDefines = false;
-
- QString customArguments;
- QString ignoredPatterns;
- bool showOutput = false;
- bool addIncludePaths = false;
- bool guessArguments = true;
-};
-
-class OptionsWidget final : public QWidget
+class CppcheckOptions final : public Core::PagedSettings
{
public:
- explicit OptionsWidget(QWidget *parent = nullptr);
- void load(const CppcheckOptions &options);
- void save(CppcheckOptions &options) const;
-
-private:
- Utils::PathChooser *m_binary = nullptr;
- QLineEdit *m_customArguments = nullptr;
- QLineEdit *m_ignorePatterns = nullptr;
- QCheckBox *m_warning = nullptr;
- QCheckBox *m_style = nullptr;
- QCheckBox *m_performance = nullptr;
- QCheckBox *m_portability = nullptr;
- QCheckBox *m_information = nullptr;
- QCheckBox *m_unusedFunction = nullptr;
- QCheckBox *m_missingInclude = nullptr;
- QCheckBox *m_inconclusive = nullptr;
- QCheckBox *m_forceDefines = nullptr;
- QCheckBox *m_showOutput = nullptr;
- QCheckBox *m_addIncludePaths = nullptr;
- QCheckBox *m_guessArguments = nullptr;
-};
-
-class CppcheckOptionsPage final : public Core::IOptionsPage
-{
-public:
- explicit CppcheckOptionsPage(CppcheckTool &tool, CppcheckTrigger &trigger);
-
- QWidget *widget() final;
- void apply() final;
- void finish() final;
-
-private:
- void save(const CppcheckOptions &options) const;
- void load(CppcheckOptions &options) const;
-
- CppcheckTool &m_tool;
- CppcheckTrigger &m_trigger;
- QPointer<OptionsWidget> m_widget;
+ CppcheckOptions();
+
+ std::function<Layouting::LayoutItem()> layouter();
+
+ Utils::FilePathAspect binary{this};
+ Utils::BoolAspect warning{this};
+ Utils::BoolAspect style{this};
+ Utils::BoolAspect performance{this};
+ Utils::BoolAspect portability{this};
+ Utils::BoolAspect information{this};
+ Utils::BoolAspect unusedFunction{this};
+ Utils::BoolAspect missingInclude{this};
+ Utils::BoolAspect inconclusive{this};
+ Utils::BoolAspect forceDefines{this};
+
+ Utils::StringAspect customArguments{this};
+ Utils::StringAspect ignoredPatterns{this};
+ Utils::BoolAspect showOutput{this};
+ Utils::BoolAspect addIncludePaths{this};
+ Utils::BoolAspect guessArguments{this};
};
} // Cppcheck::Internal
diff --git a/src/plugins/cppcheck/cppcheckplugin.cpp b/src/plugins/cppcheck/cppcheckplugin.cpp
index 5dd09a1820..b025762c8c 100644
--- a/src/plugins/cppcheck/cppcheckplugin.cpp
+++ b/src/plugins/cppcheck/cppcheckplugin.cpp
@@ -16,7 +16,7 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <coreplugin/actionmanager/actioncontainer.h>
@@ -25,9 +25,12 @@
#include <debugger/analyzer/analyzerconstants.h>
#include <debugger/debuggermainwindow.h>
+#include <utils/layoutbuilder.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
+using namespace Utils;
+
namespace Cppcheck::Internal {
class CppcheckPluginPrivate final : public QObject
@@ -36,11 +39,11 @@ public:
explicit CppcheckPluginPrivate();
CppcheckTextMarkManager marks;
- CppcheckTool tool{marks, Constants::CHECK_PROGRESS_ID};
+ CppcheckOptions options;
+ CppcheckTool tool{options, marks, Constants::CHECK_PROGRESS_ID};
CppcheckTrigger trigger{marks, tool};
- CppcheckOptionsPage options{tool, trigger};
DiagnosticsModel manualRunModel;
- CppcheckTool manualRunTool{manualRunModel, Constants::MANUAL_CHECK_PROGRESS_ID};
+ CppcheckTool manualRunTool{options, manualRunModel, Constants::MANUAL_CHECK_PROGRESS_ID};
Utils::Perspective perspective{Constants::PERSPECTIVE_ID, ::Cppcheck::Tr::tr("Cppcheck")};
QAction *manualRunAction;
@@ -51,7 +54,11 @@ public:
CppcheckPluginPrivate::CppcheckPluginPrivate()
{
- manualRunTool.updateOptions(tool.options());
+ tool.updateOptions();
+ connect(&options, &AspectContainer::changed, [this] {
+ tool.updateOptions();
+ trigger.recheck();
+ });
auto manualRunView = new DiagnosticView;
manualRunView->setModel(&manualRunModel);
@@ -97,12 +104,17 @@ CppcheckPluginPrivate::CppcheckPluginPrivate()
}
}
-void CppcheckPluginPrivate::startManualRun() {
- auto project = ProjectExplorer::SessionManager::startupProject();
+void CppcheckPluginPrivate::startManualRun()
+{
+ auto project = ProjectExplorer::ProjectManager::startupProject();
if (!project)
return;
- ManualRunDialog dialog(manualRunTool.options(), project);
+ manualRunTool.updateOptions();
+
+ auto optionsWidget = options.layouter()().emerge();
+
+ ManualRunDialog dialog(optionsWidget, project);
if (dialog.exec() == ManualRunDialog::Rejected)
return;
@@ -113,7 +125,7 @@ void CppcheckPluginPrivate::startManualRun() {
return;
manualRunTool.setProject(project);
- manualRunTool.updateOptions(dialog.options());
+ manualRunTool.updateOptions();
manualRunTool.check(files);
perspective.select();
}
@@ -121,8 +133,8 @@ void CppcheckPluginPrivate::startManualRun() {
void CppcheckPluginPrivate::updateManualRunAction()
{
using namespace ProjectExplorer;
- const Project *project = SessionManager::startupProject();
- const Target *target = SessionManager::startupTarget();
+ const Project *project = ProjectManager::startupProject();
+ const Target *target = ProjectManager::startupTarget();
const Utils::Id cxx = ProjectExplorer::Constants::CXX_LANGUAGE_ID;
const bool canRun = target && project->projectLanguages().contains(cxx)
&& ToolChainKitAspect::cxxToolChain(target->kit());
diff --git a/src/plugins/cppcheck/cppcheckrunner.cpp b/src/plugins/cppcheck/cppcheckrunner.cpp
index 8595eac28b..436c8ab392 100644
--- a/src/plugins/cppcheck/cppcheckrunner.cpp
+++ b/src/plugins/cppcheck/cppcheckrunner.cpp
@@ -16,7 +16,7 @@ namespace Cppcheck::Internal {
CppcheckRunner::CppcheckRunner(CppcheckTool &tool) : m_tool(tool)
{
if (HostOsInfo::hostOs() == OsTypeLinux) {
- QtcProcess getConf;
+ Process getConf;
getConf.setCommand({"getconf", {"ARG_MAX"}});
getConf.start();
getConf.waitForFinished(2000);
@@ -31,8 +31,8 @@ CppcheckRunner::CppcheckRunner(CppcheckTool &tool) : m_tool(tool)
m_tool.parseErrorLine(line);
});
- connect(&m_process, &QtcProcess::started, &m_tool, &CppcheckTool::startParsing);
- connect(&m_process, &QtcProcess::done, this, &CppcheckRunner::handleDone);
+ connect(&m_process, &Process::started, &m_tool, &CppcheckTool::startParsing);
+ connect(&m_process, &Process::done, this, &CppcheckRunner::handleDone);
m_queueTimer.setSingleShot(true);
const int checkDelayInMs = 200;
diff --git a/src/plugins/cppcheck/cppcheckrunner.h b/src/plugins/cppcheck/cppcheckrunner.h
index 23689b2b05..686a6c9eb9 100644
--- a/src/plugins/cppcheck/cppcheckrunner.h
+++ b/src/plugins/cppcheck/cppcheckrunner.h
@@ -4,7 +4,7 @@
#pragma once
#include <utils/filepath.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QHash>
#include <QTimer>
@@ -35,7 +35,7 @@ private:
void handleDone();
CppcheckTool &m_tool;
- Utils::QtcProcess m_process;
+ Utils::Process m_process;
Utils::FilePath m_binary;
QString m_arguments;
QHash<QString, Utils::FilePaths> m_queue;
diff --git a/src/plugins/cppcheck/cppchecktool.cpp b/src/plugins/cppcheck/cppchecktool.cpp
index a5c3bc6afb..cd824b3db4 100644
--- a/src/plugins/cppcheck/cppchecktool.cpp
+++ b/src/plugins/cppcheck/cppchecktool.cpp
@@ -26,7 +26,8 @@ using namespace Utils;
namespace Cppcheck::Internal {
-CppcheckTool::CppcheckTool(CppcheckDiagnosticManager &manager, const Id &progressId) :
+CppcheckTool::CppcheckTool(CppcheckOptions &options, CppcheckDiagnosticManager &manager, const Id &progressId) :
+ m_options(options),
m_manager(manager),
m_progressRegexp("^.* checked (\\d+)% done$"),
m_messageRegexp("^(.+),(\\d+),(\\w+),(\\w+),(.*)$"),
@@ -39,11 +40,10 @@ CppcheckTool::CppcheckTool(CppcheckDiagnosticManager &manager, const Id &progres
CppcheckTool::~CppcheckTool() = default;
-void CppcheckTool::updateOptions(const CppcheckOptions &options)
+void CppcheckTool::updateOptions()
{
- m_options = options;
m_filters.clear();
- for (const QString &pattern : m_options.ignoredPatterns.split(',')) {
+ for (const QString &pattern : m_options.ignoredPatterns().split(',')) {
const QString trimmedPattern = pattern.trimmed();
if (trimmedPattern.isEmpty())
continue;
@@ -70,44 +70,44 @@ void CppcheckTool::updateArguments()
m_cachedAdditionalArguments.clear();
QStringList arguments;
- if (!m_options.customArguments.isEmpty()) {
+ if (!m_options.customArguments().isEmpty()) {
Utils::MacroExpander *expander = Utils::globalMacroExpander();
- const QString expanded = expander->expand(m_options.customArguments);
+ const QString expanded = expander->expand(m_options.customArguments());
arguments.push_back(expanded);
}
- if (m_options.warning)
+ if (m_options.warning())
arguments.push_back("--enable=warning");
- if (m_options.style)
+ if (m_options.style())
arguments.push_back("--enable=style");
- if (m_options.performance)
+ if (m_options.performance())
arguments.push_back("--enable=performance");
- if (m_options.portability)
+ if (m_options.portability())
arguments.push_back("--enable=portability");
- if (m_options.information)
+ if (m_options.information())
arguments.push_back("--enable=information");
- if (m_options.unusedFunction)
+ if (m_options.unusedFunction())
arguments.push_back("--enable=unusedFunction");
- if (m_options.missingInclude)
+ if (m_options.missingInclude())
arguments.push_back("--enable=missingInclude");
- if (m_options.inconclusive)
+ if (m_options.inconclusive())
arguments.push_back("--inconclusive");
- if (m_options.forceDefines)
+ if (m_options.forceDefines())
arguments.push_back("--force");
- if (!m_options.unusedFunction && !m_options.customArguments.contains("-j "))
+ if (!m_options.unusedFunction() && !m_options.customArguments().contains("-j "))
arguments.push_back("-j " + QString::number(QThread::idealThreadCount()));
arguments.push_back("--template=\"{file},{line},{severity},{id},{message}\"");
- m_runner->reconfigure(m_options.binary, arguments.join(' '));
+ m_runner->reconfigure(m_options.binary(), arguments.join(' '));
}
QStringList CppcheckTool::additionalArguments(const CppEditor::ProjectPart &part) const
{
QStringList result;
- if (m_options.addIncludePaths) {
+ if (m_options.addIncludePaths()) {
for (const ProjectExplorer::HeaderPath &path : part.headerPaths) {
const QString projectDir = m_project->projectDirectory().toString();
if (path.type == ProjectExplorer::HeaderPathType::User
@@ -116,7 +116,7 @@ QStringList CppcheckTool::additionalArguments(const CppEditor::ProjectPart &part
}
}
- if (!m_options.guessArguments)
+ if (!m_options.guessArguments())
return result;
using Version = Utils::LanguageVersion;
@@ -158,11 +158,6 @@ QStringList CppcheckTool::additionalArguments(const CppEditor::ProjectPart &part
return result;
}
-const CppcheckOptions &CppcheckTool::options() const
-{
- return m_options;
-}
-
void CppcheckTool::check(const Utils::FilePaths &files)
{
QTC_ASSERT(m_project, return);
@@ -226,7 +221,7 @@ void CppcheckTool::stop(const Utils::FilePaths &files)
void CppcheckTool::startParsing()
{
- if (m_options.showOutput) {
+ if (m_options.showOutput()) {
const QString message = Tr::tr("Cppcheck started: \"%1\".").arg(m_runner->currentCommand());
Core::MessageManager::writeSilently(message);
}
@@ -245,7 +240,7 @@ void CppcheckTool::parseOutputLine(const QString &line)
if (line.isEmpty())
return;
- if (m_options.showOutput)
+ if (m_options.showOutput())
Core::MessageManager::writeSilently(line);
enum Matches { Percentage = 1 };
@@ -276,7 +271,7 @@ void CppcheckTool::parseErrorLine(const QString &line)
if (line.isEmpty())
return;
- if (m_options.showOutput)
+ if (m_options.showOutput())
Core::MessageManager::writeSilently(line);
enum Matches { File = 1, Line, Severity, Id, Message };
@@ -301,7 +296,7 @@ void CppcheckTool::parseErrorLine(const QString &line)
void CppcheckTool::finishParsing()
{
- if (m_options.showOutput)
+ if (m_options.showOutput())
Core::MessageManager::writeSilently(Tr::tr("Cppcheck finished."));
QTC_ASSERT(m_progress, return);
diff --git a/src/plugins/cppcheck/cppchecktool.h b/src/plugins/cppcheck/cppchecktool.h
index ccb8e19d0f..4dc6699e33 100644
--- a/src/plugins/cppcheck/cppchecktool.h
+++ b/src/plugins/cppcheck/cppchecktool.h
@@ -31,10 +31,10 @@ class CppcheckTool final : public QObject
Q_OBJECT
public:
- CppcheckTool(CppcheckDiagnosticManager &manager, const Utils::Id &progressId);
+ CppcheckTool(CppcheckOptions &options, CppcheckDiagnosticManager &manager, const Utils::Id &progressId);
~CppcheckTool() override;
- void updateOptions(const CppcheckOptions &options);
+ void updateOptions();
void setProject(ProjectExplorer::Project *project);
void check(const Utils::FilePaths &files);
void stop(const Utils::FilePaths &files);
@@ -44,15 +44,13 @@ public:
void parseErrorLine(const QString &line);
void finishParsing();
- const CppcheckOptions &options() const;
-
private:
void updateArguments();
void addToQueue(const Utils::FilePaths &files, const CppEditor::ProjectPart &part);
QStringList additionalArguments(const CppEditor::ProjectPart &part) const;
+ CppcheckOptions &m_options;
CppcheckDiagnosticManager &m_manager;
- CppcheckOptions m_options;
QPointer<ProjectExplorer::Project> m_project;
std::unique_ptr<CppcheckRunner> m_runner;
std::unique_ptr<QFutureInterface<void>> m_progress;
diff --git a/src/plugins/cppcheck/cppchecktrigger.cpp b/src/plugins/cppcheck/cppchecktrigger.cpp
index 55b1cf7ac0..8be098df55 100644
--- a/src/plugins/cppcheck/cppchecktrigger.cpp
+++ b/src/plugins/cppcheck/cppchecktrigger.cpp
@@ -13,7 +13,7 @@
#include <coreplugin/editormanager/ieditor.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
using namespace Core;
using namespace ProjectExplorer;
@@ -34,7 +34,7 @@ CppcheckTrigger::CppcheckTrigger(CppcheckTextMarkManager &marks, CppcheckTool &t
connect(EditorManager::instance(), &EditorManager::aboutToSave,
this, &CppcheckTrigger::checkChangedDocument);
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, &CppcheckTrigger::changeCurrentProject);
connect(CppModelManager::instance(), &CppModelManager::projectPartsUpdated,
diff --git a/src/plugins/cppeditor/CMakeLists.txt b/src/plugins/cppeditor/CMakeLists.txt
index b3db8c4d1d..021bf0b763 100644
--- a/src/plugins/cppeditor/CMakeLists.txt
+++ b/src/plugins/cppeditor/CMakeLists.txt
@@ -32,7 +32,6 @@ add_qtc_plugin(CppEditor
cppcompletionassist.cpp cppcompletionassist.h
cppcompletionassistprocessor.cpp cppcompletionassistprocessor.h
cppcompletionassistprovider.cpp cppcompletionassistprovider.h
- cppcurrentdocumentfilter.cpp cppcurrentdocumentfilter.h
cppcursorinfo.h
cppdoxygen.cpp cppdoxygen.h
cppeditor.qrc
@@ -107,7 +106,6 @@ add_qtc_plugin(CppEditor
resourcepreviewhoverhandler.cpp resourcepreviewhoverhandler.h
searchsymbols.cpp searchsymbols.h
semantichighlighter.cpp semantichighlighter.h
- senddocumenttracker.cpp senddocumenttracker.h
symbolfinder.cpp symbolfinder.h
symbolsfindfilter.cpp symbolsfindfilter.h
typehierarchybuilder.cpp typehierarchybuilder.h
diff --git a/src/plugins/cppeditor/baseeditordocumentparser.cpp b/src/plugins/cppeditor/baseeditordocumentparser.cpp
index 8a8d1bc9af..cbfa6f9685 100644
--- a/src/plugins/cppeditor/baseeditordocumentparser.cpp
+++ b/src/plugins/cppeditor/baseeditordocumentparser.cpp
@@ -8,6 +8,8 @@
#include "cppprojectpartchooser.h"
#include "editordocumenthandle.h"
+#include <QPromise>
+
using namespace Utils;
namespace CppEditor {
@@ -59,15 +61,16 @@ void BaseEditorDocumentParser::setConfiguration(const Configuration &configurati
void BaseEditorDocumentParser::update(const UpdateParams &updateParams)
{
- QFutureInterface<void> dummy;
+ QPromise<void> dummy;
+ dummy.start();
update(dummy, updateParams);
}
-void BaseEditorDocumentParser::update(const QFutureInterface<void> &future,
+void BaseEditorDocumentParser::update(const QPromise<void> &promise,
const UpdateParams &updateParams)
{
QMutexLocker locker(&m_updateIsRunning);
- updateImpl(future, updateParams);
+ updateImpl(promise, updateParams);
}
BaseEditorDocumentParser::State BaseEditorDocumentParser::state() const
diff --git a/src/plugins/cppeditor/baseeditordocumentparser.h b/src/plugins/cppeditor/baseeditordocumentparser.h
index fb7f79d101..45f6b95399 100644
--- a/src/plugins/cppeditor/baseeditordocumentparser.h
+++ b/src/plugins/cppeditor/baseeditordocumentparser.h
@@ -6,14 +6,17 @@
#include "cppeditor_global.h"
#include "cpptoolsreuse.h"
#include "cppworkingcopy.h"
-#include "projectpart.h"
#include <projectexplorer/project.h>
-#include <QFutureInterface>
#include <QObject>
#include <QMutex>
+QT_BEGIN_NAMESPACE
+template <typename T>
+class QPromise;
+QT_END_NAMESPACE
+
namespace ProjectExplorer { class Project; }
namespace CppEditor {
@@ -66,7 +69,7 @@ public:
void setConfiguration(const Configuration &configuration);
void update(const UpdateParams &updateParams);
- void update(const QFutureInterface<void> &future, const UpdateParams &updateParams);
+ void update(const QPromise<void> &promise, const UpdateParams &updateParams);
ProjectPartInfo projectPartInfo() const;
@@ -91,7 +94,7 @@ protected:
mutable QMutex m_stateAndConfigurationMutex;
private:
- virtual void updateImpl(const QFutureInterface<void> &future,
+ virtual void updateImpl(const QPromise<void> &promise,
const UpdateParams &updateParams) = 0;
const Utils::FilePath m_filePath;
diff --git a/src/plugins/cppeditor/baseeditordocumentprocessor.cpp b/src/plugins/cppeditor/baseeditordocumentprocessor.cpp
index ca344aa6c7..c6c51675eb 100644
--- a/src/plugins/cppeditor/baseeditordocumentprocessor.cpp
+++ b/src/plugins/cppeditor/baseeditordocumentprocessor.cpp
@@ -8,9 +8,12 @@
#include "cpptoolsreuse.h"
#include "editordocumenthandle.h"
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
+
#include <texteditor/quickfix.h>
+#include <QPromise>
+
namespace CppEditor {
/*!
@@ -37,7 +40,7 @@ void BaseEditorDocumentProcessor::run(bool projectsUpdated)
: Utils::Language::Cxx;
runImpl({CppModelManager::instance()->workingCopy(),
- ProjectExplorer::SessionManager::startupProject(),
+ ProjectExplorer::ProjectManager::startupProject(),
languagePreference,
projectsUpdated});
}
@@ -58,20 +61,20 @@ void BaseEditorDocumentProcessor::setParserConfig(
parser()->setConfiguration(config);
}
-void BaseEditorDocumentProcessor::runParser(QFutureInterface<void> &future,
+void BaseEditorDocumentProcessor::runParser(QPromise<void> &promise,
BaseEditorDocumentParser::Ptr parser,
BaseEditorDocumentParser::UpdateParams updateParams)
{
- future.setProgressRange(0, 1);
- if (future.isCanceled()) {
- future.setProgressValue(1);
+ promise.setProgressRange(0, 1);
+ if (promise.isCanceled()) {
+ promise.setProgressValue(1);
return;
}
- parser->update(future, updateParams);
+ parser->update(promise, updateParams);
CppModelManager::instance()->finishedRefreshingSourceFiles({parser->filePath().toString()});
- future.setProgressValue(1);
+ promise.setProgressValue(1);
}
} // namespace CppEditor
diff --git a/src/plugins/cppeditor/baseeditordocumentprocessor.h b/src/plugins/cppeditor/baseeditordocumentprocessor.h
index afb74f46e6..78c0f55aab 100644
--- a/src/plugins/cppeditor/baseeditordocumentprocessor.h
+++ b/src/plugins/cppeditor/baseeditordocumentprocessor.h
@@ -23,6 +23,11 @@
#include <functional>
+QT_BEGIN_NAMESPACE
+template <typename T>
+class QPromise;
+QT_END_NAMESPACE
+
namespace TextEditor { class TextDocument; }
namespace CppEditor {
@@ -83,7 +88,7 @@ signals:
void semanticInfoUpdated(const SemanticInfo semanticInfo); // TODO: Remove me
protected:
- static void runParser(QFutureInterface<void> &future,
+ static void runParser(QPromise<void> &promise,
BaseEditorDocumentParser::Ptr parser,
BaseEditorDocumentParser::UpdateParams updateParams);
diff --git a/src/plugins/cppeditor/builtincursorinfo.cpp b/src/plugins/cppeditor/builtincursorinfo.cpp
index 14d99ce4db..4545ebe748 100644
--- a/src/plugins/cppeditor/builtincursorinfo.cpp
+++ b/src/plugins/cppeditor/builtincursorinfo.cpp
@@ -14,9 +14,9 @@
#include <cplusplus/Macro.h>
#include <cplusplus/TranslationUnit.h>
-#include <utils/textutils.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
+#include <utils/textutils.h>
#include <QTextBlock>
@@ -322,7 +322,7 @@ QFuture<CursorInfo> BuiltinCursorInfo::run(const CursorInfoParams &cursorInfoPar
QString expression;
Scope *scope = canonicalSymbol.getScopeAndExpression(textCursor, &expression);
- return Utils::runAsync(&FindUses::find, document, snapshot, line, column, scope, expression);
+ return Utils::asyncRun(&FindUses::find, document, snapshot, line, column + 1, scope, expression);
}
SemanticInfo::LocalUseMap
diff --git a/src/plugins/cppeditor/builtineditordocumentparser.cpp b/src/plugins/cppeditor/builtineditordocumentparser.cpp
index 60c768b3a1..f6d7d6faf2 100644
--- a/src/plugins/cppeditor/builtineditordocumentparser.cpp
+++ b/src/plugins/cppeditor/builtineditordocumentparser.cpp
@@ -11,6 +11,8 @@
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
+#include <QPromise>
+
using namespace CPlusPlus;
using namespace Utils;
@@ -42,7 +44,7 @@ BuiltinEditorDocumentParser::BuiltinEditorDocumentParser(const FilePath &filePat
qRegisterMetaType<CPlusPlus::Snapshot>("CPlusPlus::Snapshot");
}
-void BuiltinEditorDocumentParser::updateImpl(const QFutureInterface<void> &future,
+void BuiltinEditorDocumentParser::updateImpl(const QPromise<void> &promise,
const UpdateParams &updateParams)
{
if (filePath().isEmpty())
@@ -142,8 +144,8 @@ void BuiltinEditorDocumentParser::updateImpl(const QFutureInterface<void> &futur
QSet<Utils::FilePath> toRemove;
for (const Document::Ptr &doc : std::as_const(state.snapshot)) {
const Utils::FilePath filePath = doc->filePath();
- if (workingCopy.contains(filePath)) {
- if (workingCopy.get(filePath).second != doc->editorRevision())
+ if (const auto entry = workingCopy.get(filePath)) {
+ if (entry->second != doc->editorRevision())
addFileAndDependencies(&state.snapshot, &toRemove, filePath);
continue;
}
@@ -180,7 +182,7 @@ void BuiltinEditorDocumentParser::updateImpl(const QFutureInterface<void> &futur
doc->releaseSourceAndAST();
});
sourceProcessor.setFileSizeLimitInMb(m_fileSizeLimitInMb);
- sourceProcessor.setCancelChecker([future]() { return future.isCanceled(); });
+ sourceProcessor.setCancelChecker([&promise] { return promise.isCanceled(); });
Snapshot globalSnapshot = modelManager->snapshot();
globalSnapshot.remove(filePath());
diff --git a/src/plugins/cppeditor/builtineditordocumentparser.h b/src/plugins/cppeditor/builtineditordocumentparser.h
index b1a400a7c4..31ac126c69 100644
--- a/src/plugins/cppeditor/builtineditordocumentparser.h
+++ b/src/plugins/cppeditor/builtineditordocumentparser.h
@@ -34,8 +34,7 @@ public:
static Ptr get(const Utils::FilePath &filePath);
private:
- void updateImpl(const QFutureInterface<void> &future,
- const UpdateParams &updateParams) override;
+ void updateImpl(const QPromise<void> &promise, const UpdateParams &updateParams) override;
void addFileAndDependencies(CPlusPlus::Snapshot *snapshot,
QSet<Utils::FilePath> *toRemove,
const Utils::FilePath &fileName) const;
diff --git a/src/plugins/cppeditor/builtineditordocumentprocessor.cpp b/src/plugins/cppeditor/builtineditordocumentprocessor.cpp
index c94a639eef..8e551fd15b 100644
--- a/src/plugins/cppeditor/builtineditordocumentprocessor.cpp
+++ b/src/plugins/cppeditor/builtineditordocumentprocessor.cpp
@@ -6,11 +6,14 @@
#include "builtincursorinfo.h"
#include "cppchecksymbols.h"
#include "cppcodemodelsettings.h"
+#include "cppeditordocument.h"
#include "cppeditorplugin.h"
#include "cppmodelmanager.h"
#include "cpptoolsreuse.h"
#include "cppworkingcopy.h"
+#include <coreplugin/editormanager/documentmodel.h>
+
#include <texteditor/fontsettings.h>
#include <texteditor/refactoroverlay.h>
#include <texteditor/texteditorsettings.h>
@@ -18,9 +21,9 @@
#include <cplusplus/CppDocument.h>
#include <cplusplus/SimpleLexer.h>
-#include <utils/textutils.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
+#include <utils/textutils.h>
#include <QLoggingCategory>
#include <QTextBlock>
@@ -89,10 +92,10 @@ CheckSymbols *createHighlighter(const CPlusPlus::Document::Ptr &doc,
for (const CPlusPlus::Macro &macro : doc->definedMacros()) {
int line, column;
convertPosition(textDocument, macro.utf16CharOffset(), &line, &column);
- QTC_ASSERT(line >= 0 && column >= 0, qDebug() << doc->filePath() << macro.toString();
+ QTC_ASSERT(line > 0 && column >= 0, qDebug() << doc->filePath() << macro.toString();
continue);
- Result use(line, column, macro.nameToQString().size(), SemanticHighlighter::MacroUse);
+ Result use(line, column + 1, macro.nameToQString().size(), SemanticHighlighter::MacroUse);
macroUses.append(use);
}
@@ -116,10 +119,10 @@ CheckSymbols *createHighlighter(const CPlusPlus::Document::Ptr &doc,
int line, column;
convertPosition(textDocument, macro.utf16charsBegin(), &line, &column);
- QTC_ASSERT(line >= 0 && column >= 0, qDebug() << doc->filePath()
+ QTC_ASSERT(line > 0 && column >= 0, qDebug() << doc->filePath()
<< macro.macro().toString(); continue);
- Result use(line, column, name.size(), SemanticHighlighter::MacroUse);
+ Result use(line, column + 1, name.size(), SemanticHighlighter::MacroUse);
macroUses.append(use);
}
@@ -180,10 +183,8 @@ BuiltinEditorDocumentProcessor::~BuiltinEditorDocumentProcessor()
void BuiltinEditorDocumentProcessor::runImpl(
const BaseEditorDocumentParser::UpdateParams &updateParams)
{
- m_parserFuture = Utils::runAsync(CppModelManager::instance()->sharedThreadPool(),
- runParser,
- parser(),
- updateParams);
+ m_parserFuture = Utils::asyncRun(CppModelManager::instance()->sharedThreadPool(),
+ runParser, parser(), updateParams);
}
BaseEditorDocumentParser::Ptr BuiltinEditorDocumentProcessor::parser()
@@ -266,6 +267,22 @@ void BuiltinEditorDocumentProcessor::onParserFinished(CPlusPlus::Document::Ptr d
const auto source = createSemanticInfoSource(false);
QTC_CHECK(source.snapshot.contains(document->filePath()));
m_semanticInfoUpdater.updateDetached(source);
+
+ const QList<Core::IDocument *> openDocuments = Core::DocumentModel::openedDocuments();
+ for (Core::IDocument * const openDocument : openDocuments) {
+ const auto cppEditorDoc = qobject_cast<Internal::CppEditorDocument *>(openDocument);
+ if (!cppEditorDoc)
+ continue;
+ if (cppEditorDoc->filePath() == document->filePath())
+ continue;
+ CPlusPlus::Document::Ptr cppDoc = CppModelManager::instance()->document(
+ cppEditorDoc->filePath());
+ if (!cppDoc)
+ continue;
+ if (!cppDoc->includedFiles().contains(document->filePath()))
+ continue;
+ cppEditorDoc->scheduleProcessDocument();
+ }
}
void BuiltinEditorDocumentProcessor::onSemanticInfoUpdated(const SemanticInfo semanticInfo)
@@ -304,12 +321,13 @@ void BuiltinEditorDocumentProcessor::onCodeWarningsUpdated(
SemanticInfo::Source BuiltinEditorDocumentProcessor::createSemanticInfoSource(bool force) const
{
- const WorkingCopy workingCopy = CppModelManager::instance()->workingCopy();
- return SemanticInfo::Source(filePath().toString(),
- workingCopy.source(filePath()),
- workingCopy.revision(filePath()),
- m_documentSnapshot,
- force);
+ QByteArray source;
+ int revision = 0;
+ if (const auto entry = CppModelManager::instance()->workingCopy().get(filePath())) {
+ source = entry->first;
+ revision = entry->second;
+ }
+ return SemanticInfo::Source(filePath().toString(), source, revision, m_documentSnapshot, force);
}
} // namespace CppEditor
diff --git a/src/plugins/cppeditor/clangdiagnosticconfig.h b/src/plugins/cppeditor/clangdiagnosticconfig.h
index c1d21cf7d7..3442c95db5 100644
--- a/src/plugins/cppeditor/clangdiagnosticconfig.h
+++ b/src/plugins/cppeditor/clangdiagnosticconfig.h
@@ -42,10 +42,8 @@ public:
// Clang-Tidy
enum class TidyMode
{
- // Disabled, // Used by Qt Creator 4.10 and below.
UseCustomChecks = 1,
- UseConfigFile,
- UseDefaultChecks,
+ UseDefaultChecks = 3,
};
TidyMode clangTidyMode() const;
void setClangTidyMode(TidyMode mode);
diff --git a/src/plugins/cppeditor/clangdiagnosticconfigswidget.cpp b/src/plugins/cppeditor/clangdiagnosticconfigswidget.cpp
index f5fc8cfe42..8e7e1b8f84 100644
--- a/src/plugins/cppeditor/clangdiagnosticconfigswidget.cpp
+++ b/src/plugins/cppeditor/clangdiagnosticconfigswidget.cpp
@@ -120,9 +120,11 @@ class ClangBaseChecksWidget : public QWidget
public:
ClangBaseChecksWidget()
{
- auto label = new QLabel(Tr::tr("For appropriate options, consult the GCC or Clang manual "
- "pages or the %1 GCC online documentation</a>.")
- .arg("<a href=\"https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html\">"));
+ auto label = new QLabel;
+ label->setTextFormat(Qt::MarkdownText);
+ label->setText(Tr::tr("For appropriate options, consult the GCC or Clang manual "
+ "pages or the [GCC online documentation](%1).")
+ .arg("https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html"));
label->setOpenExternalLinks(true);
useFlagsFromBuildSystemCheckBox = new QCheckBox(Tr::tr("Use diagnostic flags from build system"));
diff --git a/src/plugins/cppeditor/compileroptionsbuilder.cpp b/src/plugins/cppeditor/compileroptionsbuilder.cpp
index d2c5e16325..5f09212121 100644
--- a/src/plugins/cppeditor/compileroptionsbuilder.cpp
+++ b/src/plugins/cppeditor/compileroptionsbuilder.cpp
@@ -130,6 +130,7 @@ QStringList CompilerOptionsBuilder::build(ProjectFile::Kind fileKind,
undefineCppLanguageFeatureMacrosForMsvc2015();
addDefineFunctionMacrosMsvc();
addDefineFunctionMacrosQnx();
+ addQtMacros();
addHeaderPathOptions();
@@ -786,6 +787,12 @@ void CompilerOptionsBuilder::addDefineFunctionMacrosQnx()
addMacros({{"_LIBCPP_HAS_NO_BUILTIN_OPERATOR_NEW_DELETE"}});
}
+void CompilerOptionsBuilder::addQtMacros()
+{
+ if (m_projectPart.qtVersion != QtMajorVersion::None)
+ addMacros({{"QT_ANNOTATE_FUNCTION(x)", "__attribute__((annotate(#x)))"}});
+}
+
void CompilerOptionsBuilder::reset()
{
m_options.clear();
diff --git a/src/plugins/cppeditor/compileroptionsbuilder.h b/src/plugins/cppeditor/compileroptionsbuilder.h
index 0e8c1a5fad..d47a7e87e3 100644
--- a/src/plugins/cppeditor/compileroptionsbuilder.h
+++ b/src/plugins/cppeditor/compileroptionsbuilder.h
@@ -62,6 +62,7 @@ public:
void undefineClangVersionMacrosForMsvc();
void addDefineFunctionMacrosQnx();
+ void addQtMacros();
// Add custom options
void add(const QString &arg, bool gccOnlyOption = false);
diff --git a/src/plugins/cppeditor/compileroptionsbuilder_test.cpp b/src/plugins/cppeditor/compileroptionsbuilder_test.cpp
index 3a19c16a66..b09a8061fc 100644
--- a/src/plugins/cppeditor/compileroptionsbuilder_test.cpp
+++ b/src/plugins/cppeditor/compileroptionsbuilder_test.cpp
@@ -593,6 +593,7 @@ void CompilerOptionsBuilderTest::testBuildAllOptions()
(QStringList{"-nostdinc", "-nostdinc++", "-arch", "x86_64", "-fsyntax-only", "-m64",
"--target=x86_64-apple-darwin10", "-x", "c++", "-std=c++17",
"-DprojectFoo=projectBar",
+ "-DQT_ANNOTATE_FUNCTION(x)=__attribute__((annotate(#x)))",
wrappedQtHeadersPath, // contains -I already
wrappedQtCoreHeadersPath, // contains -I already
"-I" + t.toNative("/tmp/path"),
@@ -621,6 +622,7 @@ void CompilerOptionsBuilderTest::testBuildAllOptionsMsvc()
"-D__FUNCSIG__=\"void __cdecl someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580(void)\"",
"-D__FUNCTION__=\"someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580\"",
"-D__FUNCDNAME__=\"?someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580@@YAXXZ\"",
+ "-DQT_ANNOTATE_FUNCTION(x)=__attribute__((annotate(#x)))",
wrappedQtHeadersPath, // contains -I already
wrappedQtCoreHeadersPath, // contains -I already
"-I" + t.toNative("/tmp/path"),
@@ -651,6 +653,7 @@ void CompilerOptionsBuilderTest::testBuildAllOptionsMsvcWithExceptions()
"-D__FUNCSIG__=\"void __cdecl someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580(void)\"",
"-D__FUNCTION__=\"someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580\"",
"-D__FUNCDNAME__=\"?someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580@@YAXXZ\"",
+ "-DQT_ANNOTATE_FUNCTION(x)=__attribute__((annotate(#x)))",
wrappedQtHeadersPath, // contains -I already
wrappedQtCoreHeadersPath, // contains -I already
"-I" + t.toNative("/tmp/path"),
diff --git a/src/plugins/cppeditor/cppcanonicalsymbol.cpp b/src/plugins/cppeditor/cppcanonicalsymbol.cpp
index 1df0c71cf9..102a5d0781 100644
--- a/src/plugins/cppeditor/cppcanonicalsymbol.cpp
+++ b/src/plugins/cppeditor/cppcanonicalsymbol.cpp
@@ -51,7 +51,7 @@ Scope *CanonicalSymbol::getScopeAndExpression(const QTextCursor &cursor, QString
ExpressionUnderCursor expressionUnderCursor(m_document->languageFeatures());
*code = expressionUnderCursor(tc);
- return m_document->scopeAt(line, column - 1);
+ return m_document->scopeAt(line, column);
}
Symbol *CanonicalSymbol::operator()(const QTextCursor &cursor)
diff --git a/src/plugins/cppeditor/cppcodeformatter.cpp b/src/plugins/cppeditor/cppcodeformatter.cpp
index ce250f672b..6b30e78fa6 100644
--- a/src/plugins/cppeditor/cppcodeformatter.cpp
+++ b/src/plugins/cppeditor/cppcodeformatter.cpp
@@ -924,6 +924,7 @@ bool CodeFormatter::tryStatement()
return true;
switch (kind) {
case T_RETURN:
+ case T_CO_RETURN:
enter(return_statement);
enter(expression);
return true;
@@ -1651,6 +1652,7 @@ void QtStyleCodeFormatter::adjustIndent(const Tokens &tokens, int lexerState, in
case T_BREAK:
case T_CONTINUE:
case T_RETURN:
+ case T_CO_RETURN:
if (topState.type == case_cont) {
*indentDepth = topState.savedIndentDepth;
if (m_styleSettings.indentControlFlowRelativeToSwitchLabels)
diff --git a/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp b/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp
index 8b9cba0d63..48ee4c80f1 100644
--- a/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp
+++ b/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp
@@ -1429,7 +1429,9 @@ CppCodeModelInspectorDialog::CppCodeModelInspectorDialog(QWidget *parent)
m_workingCopyView->setModel(m_proxyWorkingCopyModel);
using namespace Layouting;
- m_projectPartTab = qobject_cast<QTabWidget*>(TabWidget{
+
+ TabWidget projectPart {
+ bindTo(&m_projectPartTab),
Tab("&General",
Row {
m_partGeneralView,
@@ -1454,10 +1456,10 @@ CppCodeModelInspectorDialog::CppCodeModelInspectorDialog(QWidget *parent)
),
Tab("&Header Paths", Column{ projectHeaderPathsView }),
Tab("Pre&compiled Headers", Column{ m_partPrecompiledHeadersEdit }),
- }.widget);
- QTC_CHECK(m_projectPartTab);
+ };
- m_docTab = qobject_cast<QTabWidget*>(TabWidget{
+ TabWidget docTab {
+ bindTo(&m_docTab),
Tab("&General", Column { m_docGeneralView }),
Tab("&Includes", Column { m_docIncludesView }),
Tab("&Diagnostic Messages", Column { m_docDiagnosticMessagesView }),
@@ -1465,8 +1467,7 @@ CppCodeModelInspectorDialog::CppCodeModelInspectorDialog(QWidget *parent)
Tab("P&reprocessed Source", Column { m_docPreprocessedSourceEdit }),
Tab("&Symbols", Column { m_docSymbolsView }),
Tab("&Tokens", Column { m_docTokensView }),
- }.widget);
- QTC_CHECK(m_docTab);
+ };
Column {
TabWidget {
@@ -1474,7 +1475,7 @@ CppCodeModelInspectorDialog::CppCodeModelInspectorDialog(QWidget *parent)
Column {
Splitter {
m_projectPartsView,
- m_projectPartTab,
+ projectPart,
},
}
),
@@ -1484,8 +1485,9 @@ CppCodeModelInspectorDialog::CppCodeModelInspectorDialog(QWidget *parent)
Column {
Form { QString("Sn&apshot:"), m_snapshotSelector },
m_snapshotView,
- }.emerge(Utils::Layouting::WithoutMargins),
- m_docTab,
+ noMargin,
+ }.emerge(),
+ docTab,
},
}
),
@@ -1506,6 +1508,7 @@ CppCodeModelInspectorDialog::CppCodeModelInspectorDialog(QWidget *parent)
}
}.attachTo(this);
+ QTC_CHECK(m_projectPartTab);
m_projectPartTab->setCurrentIndex(3);
connect(m_snapshotView->selectionModel(),
diff --git a/src/plugins/cppeditor/cppcodemodelinspectordumper.cpp b/src/plugins/cppeditor/cppcodemodelinspectordumper.cpp
index 45de8f3053..2a6dfb6e1e 100644
--- a/src/plugins/cppeditor/cppcodemodelinspectordumper.cpp
+++ b/src/plugins/cppeditor/cppcodemodelinspectordumper.cpp
@@ -254,12 +254,19 @@ QString Utils::toString(CPlusPlus::Kind kind)
TOKEN(T_CASE);
TOKEN(T_CATCH);
TOKEN(T_CHAR);
+ TOKEN(T_CHAR8_T);
TOKEN(T_CHAR16_T);
TOKEN(T_CHAR32_T);
TOKEN(T_CLASS);
+ TOKEN(T_CO_AWAIT);
+ TOKEN(T_CO_RETURN);
+ TOKEN(T_CO_YIELD);
+ TOKEN(T_CONCEPT);
TOKEN_AND_ALIASES(T_CONST, T___CONST/T___CONST__);
TOKEN(T_CONST_CAST);
TOKEN(T_CONSTEXPR);
+ TOKEN(T_CONSTEVAL);
+ TOKEN(T_CONSTINIT);
TOKEN(T_CONTINUE);
TOKEN_AND_ALIASES(T_DECLTYPE, T___DECLTYPE);
TOKEN(T_DEFAULT);
@@ -292,6 +299,7 @@ QString Utils::toString(CPlusPlus::Kind kind)
TOKEN(T_PUBLIC);
TOKEN(T_REGISTER);
TOKEN(T_REINTERPRET_CAST);
+ TOKEN(T_REQUIRES);
TOKEN(T_RETURN);
TOKEN(T_SHORT);
TOKEN(T_SIGNED);
diff --git a/src/plugins/cppeditor/cppcodemodelsettings.cpp b/src/plugins/cppeditor/cppcodemodelsettings.cpp
index e0873427f7..d0976b8128 100644
--- a/src/plugins/cppeditor/cppcodemodelsettings.cpp
+++ b/src/plugins/cppeditor/cppcodemodelsettings.cpp
@@ -9,13 +9,14 @@
#include "cpptoolsreuse.h"
#include <coreplugin/icore.h>
+#include <coreplugin/session.h>
+
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/settingsutils.h>
#include <QDateTime>
@@ -63,6 +64,9 @@ static QString useClangdKey() { return QLatin1String("UseClangdV7"); }
static QString clangdPathKey() { return QLatin1String("ClangdPath"); }
static QString clangdIndexingKey() { return QLatin1String("ClangdIndexing"); }
static QString clangdIndexingPriorityKey() { return QLatin1String("ClangdIndexingPriority"); }
+static QString clangdHeaderSourceSwitchModeKey() {
+ return QLatin1String("ClangdHeaderSourceSwitchMode");
+}
static QString clangdHeaderInsertionKey() { return QLatin1String("ClangdHeaderInsertion"); }
static QString clangdThreadLimitKey() { return QLatin1String("ClangdThreadLimit"); }
static QString clangdDocumentThresholdKey() { return QLatin1String("ClangdDocumentThreshold"); }
@@ -228,6 +232,16 @@ QString ClangdSettings::priorityToDisplayString(const IndexingPriority &priority
return {};
}
+QString ClangdSettings::headerSourceSwitchModeToDisplayString(HeaderSourceSwitchMode mode)
+{
+ switch (mode) {
+ case HeaderSourceSwitchMode::BuiltinOnly: return Tr::tr("Use Built-in Only");
+ case HeaderSourceSwitchMode::ClangdOnly: return Tr::tr("Use Clangd Only");
+ case HeaderSourceSwitchMode::Both: return Tr::tr("Try Both");
+ }
+ return {};
+}
+
ClangdSettings &ClangdSettings::instance()
{
static ClangdSettings settings;
@@ -237,15 +251,18 @@ ClangdSettings &ClangdSettings::instance()
ClangdSettings::ClangdSettings()
{
loadSettings();
- const auto sessionMgr = ProjectExplorer::SessionManager::instance();
- connect(sessionMgr, &ProjectExplorer::SessionManager::sessionRemoved,
- this, [this](const QString &name) { m_data.sessionsWithOneClangd.removeOne(name); });
- connect(sessionMgr, &ProjectExplorer::SessionManager::sessionRenamed,
- this, [this](const QString &oldName, const QString &newName) {
- const auto index = m_data.sessionsWithOneClangd.indexOf(oldName);
- if (index != -1)
- m_data.sessionsWithOneClangd[index] = newName;
+ const auto sessionMgr = Core::SessionManager::instance();
+ connect(sessionMgr, &Core::SessionManager::sessionRemoved, this, [this](const QString &name) {
+ m_data.sessionsWithOneClangd.removeOne(name);
});
+ connect(sessionMgr,
+ &Core::SessionManager::sessionRenamed,
+ this,
+ [this](const QString &oldName, const QString &newName) {
+ const auto index = m_data.sessionsWithOneClangd.indexOf(oldName);
+ if (index != -1)
+ m_data.sessionsWithOneClangd[index] = newName;
+ });
}
bool ClangdSettings::useClangd() const
@@ -319,7 +336,7 @@ ClangDiagnosticConfig ClangdSettings::diagnosticConfig() const
ClangdSettings::Granularity ClangdSettings::granularity() const
{
- if (m_data.sessionsWithOneClangd.contains(ProjectExplorer::SessionManager::activeSession()))
+ if (m_data.sessionsWithOneClangd.contains(Core::SessionManager::activeSession()))
return Granularity::Session;
return Granularity::Project;
}
@@ -339,7 +356,7 @@ static FilePath getClangHeadersPathFromClang(const FilePath &clangdFilePath)
.withExecutableSuffix();
if (!clangFilePath.exists())
return {};
- QtcProcess clang;
+ Process clang;
clang.setCommand({clangFilePath, {"-print-resource-dir"}});
clang.start();
if (!clang.waitForFinished())
@@ -527,6 +544,7 @@ QVariantMap ClangdSettings::Data::toMap() const
: QString());
map.insert(clangdIndexingKey(), indexingPriority != IndexingPriority::Off);
map.insert(clangdIndexingPriorityKey(), int(indexingPriority));
+ map.insert(clangdHeaderSourceSwitchModeKey(), int(headerSourceSwitchMode));
map.insert(clangdHeaderInsertionKey(), autoIncludeHeaders);
map.insert(clangdThreadLimitKey(), workerThreadLimit);
map.insert(clangdDocumentThresholdKey(), documentUpdateThreshold);
@@ -548,6 +566,8 @@ void ClangdSettings::Data::fromMap(const QVariantMap &map)
const auto it = map.find(clangdIndexingKey());
if (it != map.end() && !it->toBool())
indexingPriority = IndexingPriority::Off;
+ headerSourceSwitchMode = HeaderSourceSwitchMode(map.value(clangdHeaderSourceSwitchModeKey(),
+ int(headerSourceSwitchMode)).toInt());
autoIncludeHeaders = map.value(clangdHeaderInsertionKey(), false).toBool();
workerThreadLimit = map.value(clangdThreadLimitKey(), 0).toInt();
documentUpdateThreshold = map.value(clangdDocumentThresholdKey(), 500).toInt();
diff --git a/src/plugins/cppeditor/cppcodemodelsettings.h b/src/plugins/cppeditor/cppcodemodelsettings.h
index 7bfdc2e85e..c22fb88faa 100644
--- a/src/plugins/cppeditor/cppcodemodelsettings.h
+++ b/src/plugins/cppeditor/cppcodemodelsettings.h
@@ -84,9 +84,11 @@ class CPPEDITOR_EXPORT ClangdSettings : public QObject
Q_OBJECT
public:
enum class IndexingPriority { Off, Background, Normal, Low, };
+ enum class HeaderSourceSwitchMode { BuiltinOnly, ClangdOnly, Both };
static QString priorityToString(const IndexingPriority &priority);
static QString priorityToDisplayString(const IndexingPriority &priority);
+ static QString headerSourceSwitchModeToDisplayString(HeaderSourceSwitchMode mode);
class CPPEDITOR_EXPORT Data
{
@@ -103,6 +105,7 @@ public:
&& s1.diagnosticConfigId == s2.diagnosticConfigId
&& s1.workerThreadLimit == s2.workerThreadLimit
&& s1.indexingPriority == s2.indexingPriority
+ && s1.headerSourceSwitchMode == s2.headerSourceSwitchMode
&& s1.autoIncludeHeaders == s2.autoIncludeHeaders
&& s1.documentUpdateThreshold == s2.documentUpdateThreshold
&& s1.sizeThresholdEnabled == s2.sizeThresholdEnabled
@@ -123,6 +126,7 @@ public:
qint64 sizeThresholdInKb = 1024;
bool useClangd = true;
IndexingPriority indexingPriority = IndexingPriority::Low;
+ HeaderSourceSwitchMode headerSourceSwitchMode = HeaderSourceSwitchMode::Both;
bool autoIncludeHeaders = false;
bool sizeThresholdEnabled = false;
bool haveCheckedHardwareReqirements = false;
@@ -143,6 +147,7 @@ public:
static void setCustomDiagnosticConfigs(const ClangDiagnosticConfigs &configs);
Utils::FilePath clangdFilePath() const;
IndexingPriority indexingPriority() const { return m_data.indexingPriority; }
+ HeaderSourceSwitchMode headerSourceSwitchMode() const { return m_data.headerSourceSwitchMode; }
bool autoIncludeHeaders() const { return m_data.autoIncludeHeaders; }
int workerThreadLimit() const { return m_data.workerThreadLimit; }
int documentUpdateThreshold() const { return m_data.documentUpdateThreshold; }
diff --git a/src/plugins/cppeditor/cppcodemodelsettingspage.cpp b/src/plugins/cppeditor/cppcodemodelsettingspage.cpp
index 46870ba252..289cd3dc86 100644
--- a/src/plugins/cppeditor/cppcodemodelsettingspage.cpp
+++ b/src/plugins/cppeditor/cppcodemodelsettingspage.cpp
@@ -10,8 +10,7 @@
#include "cpptoolsreuse.h"
#include <coreplugin/icore.h>
-
-#include <projectexplorer/session.h>
+#include <coreplugin/session.h>
#include <utils/algorithm.h>
#include <utils/infolabel.h>
@@ -73,8 +72,10 @@ CppCodeModelSettingsWidget::CppCodeModelSettingsWidget(CppCodeModelSettings *s)
m_bigFilesLimitSpinBox->setValue(m_settings->indexerFileSizeLimitInMb());
m_ignoreFilesCheckBox = new QCheckBox(Tr::tr("Ignore files"));
- m_ignoreFilesCheckBox->setToolTip(Tr::tr(
- "<html><head/><body><p>Ignore files that match these wildcard patterns, one wildcard per line.</p></body></html>"));
+ m_ignoreFilesCheckBox->setToolTip(
+ "<html><head/><body><p>"
+ + Tr::tr("Ignore files that match these wildcard patterns, one wildcard per line.")
+ + "</p></body></html>");
m_ignoreFilesCheckBox->setChecked(m_settings->ignoreFiles());
m_ignorePatternTextEdit = new QPlainTextEdit(m_settings->ignorePattern());
@@ -103,7 +104,7 @@ CppCodeModelSettingsWidget::CppCodeModelSettingsWidget(CppCodeModelSettings *s)
m_ignorePchCheckBox->setChecked(m_settings->pchUsage() == CppCodeModelSettings::PchUse_None);
m_useBuiltinPreprocessorCheckBox->setChecked(m_settings->useBuiltinPreprocessor());
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
@@ -192,6 +193,7 @@ class ClangdSettingsWidget::Private
public:
QCheckBox useClangdCheckBox;
QComboBox indexingComboBox;
+ QComboBox headerSourceSwitchComboBox;
QCheckBox autoIncludeHeadersCheckBox;
QCheckBox sizeThresholdCheckBox;
QSpinBox threadLimitSpinBox;
@@ -220,6 +222,12 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD
"cores unused.</p>"
"<p>Normal Priority: Reduced priority compared to interactive work.</p>"
"Low Priority: Same priority as other clangd work.");
+ const QString headerSourceSwitchToolTip = Tr::tr(
+ "<p>Which C/C++ backend to use when switching between header and source file."
+ "<p>The clangd implementation has more capabilities, but also has some bugs not present "
+ "in the built-in variant."
+ "<p>When \"Try Both\" is selected, clangd will be employed only if the built-in variant "
+ "does not find anything.");
const QString workerThreadsToolTip = Tr::tr(
"Number of worker threads used by clangd. Background indexing also uses this many "
"worker threads.");
@@ -241,6 +249,7 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD
d->clangdChooser.setFilePath(settings.clangdFilePath());
d->clangdChooser.setAllowPathFromDevice(true);
d->clangdChooser.setEnabled(d->useClangdCheckBox.isChecked());
+ d->clangdChooser.setCommandVersionArguments({"--version"});
using Priority = ClangdSettings::IndexingPriority;
for (Priority prio : {Priority::Off, Priority::Background, Priority::Low, Priority::Normal}) {
d->indexingComboBox.addItem(ClangdSettings::priorityToDisplayString(prio), int(prio));
@@ -248,6 +257,15 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD
d->indexingComboBox.setCurrentIndex(d->indexingComboBox.count() - 1);
}
d->indexingComboBox.setToolTip(indexingToolTip);
+ using SwitchMode = ClangdSettings::HeaderSourceSwitchMode;
+ for (SwitchMode mode : {SwitchMode::BuiltinOnly, SwitchMode::ClangdOnly, SwitchMode::Both}) {
+ d->headerSourceSwitchComboBox.addItem(
+ ClangdSettings::headerSourceSwitchModeToDisplayString(mode), int(mode));
+ if (mode == settings.headerSourceSwitchMode())
+ d->headerSourceSwitchComboBox.setCurrentIndex(
+ d->headerSourceSwitchComboBox.count() - 1);
+ }
+ d->headerSourceSwitchComboBox.setToolTip(headerSourceSwitchToolTip);
d->autoIncludeHeadersCheckBox.setText(Tr::tr("Insert header files on completion"));
d->autoIncludeHeadersCheckBox.setChecked(settings.autoIncludeHeaders());
d->autoIncludeHeadersCheckBox.setToolTip(autoIncludeToolTip);
@@ -293,6 +311,13 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD
indexingPriorityLabel->setToolTip(indexingToolTip);
formLayout->addRow(indexingPriorityLabel, indexingPriorityLayout);
+ const auto headerSourceSwitchLayout = new QHBoxLayout;
+ headerSourceSwitchLayout->addWidget(&d->headerSourceSwitchComboBox);
+ headerSourceSwitchLayout->addStretch(1);
+ const auto headerSourceSwitchLabel = new QLabel(Tr::tr("Header/source switch mode:"));
+ headerSourceSwitchLabel->setToolTip(headerSourceSwitchToolTip);
+ formLayout->addRow(headerSourceSwitchLabel, headerSourceSwitchLayout);
+
const auto threadLimitLayout = new QHBoxLayout;
threadLimitLayout->addWidget(&d->threadLimitSpinBox);
threadLimitLayout->addStretch(1);
@@ -368,7 +393,7 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD
connect(addButton, &QPushButton::clicked, this, [this, sessionsView] {
QInputDialog dlg(sessionsView);
- QStringList sessions = ProjectExplorer::SessionManager::sessions();
+ QStringList sessions = Core::SessionManager::sessions();
QStringList currentSessions = d->sessionsModel.stringList();
for (const QString &s : std::as_const(currentSessions))
sessions.removeOne(s);
@@ -400,7 +425,7 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD
else
Core::EditorManager::openEditor(Utils::FilePath::fromString(link));
});
- layout->addWidget(Utils::Layouting::createHr());
+ layout->addWidget(Layouting::createHr());
layout->addWidget(configFilesHelpLabel);
layout->addStretch(1);
@@ -448,6 +473,8 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD
this, &ClangdSettingsWidget::settingsDataChanged);
connect(&d->indexingComboBox, &QComboBox::currentIndexChanged,
this, &ClangdSettingsWidget::settingsDataChanged);
+ connect(&d->headerSourceSwitchComboBox, &QComboBox::currentIndexChanged,
+ this, &ClangdSettingsWidget::settingsDataChanged);
connect(&d->autoIncludeHeadersCheckBox, &QCheckBox::toggled,
this, &ClangdSettingsWidget::settingsDataChanged);
connect(&d->threadLimitSpinBox, &QSpinBox::valueChanged,
@@ -478,6 +505,8 @@ ClangdSettings::Data ClangdSettingsWidget::settingsData() const
data.executableFilePath = d->clangdChooser.filePath();
data.indexingPriority = ClangdSettings::IndexingPriority(
d->indexingComboBox.currentData().toInt());
+ data.headerSourceSwitchMode = ClangdSettings::HeaderSourceSwitchMode(
+ d->headerSourceSwitchComboBox.currentData().toInt());
data.autoIncludeHeaders = d->autoIncludeHeadersCheckBox.isChecked();
data.workerThreadLimit = d->threadLimitSpinBox.value();
data.documentUpdateThreshold = d->documentUpdateThreshold.value();
diff --git a/src/plugins/cppeditor/cppcodestylesettingspage.cpp b/src/plugins/cppeditor/cppcodestylesettingspage.cpp
index ebb4a37485..84573bdeed 100644
--- a/src/plugins/cppeditor/cppcodestylesettingspage.cpp
+++ b/src/plugins/cppeditor/cppcodestylesettingspage.cpp
@@ -34,6 +34,7 @@
#include <QCheckBox>
#include <QTabWidget>
#include <QTextBlock>
+#include <QVBoxLayout>
using namespace TextEditor;
using namespace Utils;
@@ -161,11 +162,8 @@ public:
, m_bindStarToLeftSpecifier(createCheckBox(Tr::tr("Left const/volatile")))
, m_bindStarToRightSpecifier(createCheckBox(Tr::tr("Right const/volatile"),
Tr::tr("This does not apply to references.")))
- , m_categoryTab(new QTabWidget)
, m_tabSettingsWidget(new TabSettingsWidget)
{
- m_categoryTab->setProperty("_q_custom_style_disabled", true);
-
QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
@@ -175,10 +173,17 @@ public:
QObject::connect(m_tabSettingsWidget, &TabSettingsWidget::settingsChanged,
q, &CppCodeStylePreferencesWidget::slotTabSettingsChanged);
- using namespace Utils::Layouting;
+ using namespace Layouting;
+
+ QWidget *contentGroupWidget = nullptr;
+ QWidget *bracesGroupWidget = nullptr;
+ QWidget *switchGroupWidget = nullptr;
+ QWidget *alignmentGroupWidget = nullptr;
+ QWidget *typesGroupWidget = nullptr;
const Group contentGroup {
title(Tr::tr("Indent")),
+ bindTo(&contentGroupWidget),
Column {
m_indentAccessSpecifiers,
m_indentDeclarationsRelativeToAccessSpecifiers,
@@ -191,6 +196,7 @@ public:
const Group bracesGroup {
title(Tr::tr("Indent Braces")),
+ bindTo(&bracesGroupWidget),
Column {
m_indentClassBraces,
m_indentNamespaceBraces,
@@ -203,6 +209,7 @@ public:
const Group switchGroup {
title(Tr::tr("Indent within \"switch\"")),
+ bindTo(&switchGroupWidget),
Column {
m_indentSwitchLabels,
m_indentCaseStatements,
@@ -214,6 +221,7 @@ public:
const Group alignmentGroup {
title(Tr::tr("Align")),
+ bindTo(&alignmentGroupWidget),
Column {
m_alignAssignments,
m_extraPaddingConditions,
@@ -223,6 +231,7 @@ public:
const Group typesGroup {
title(Tr::tr("Bind '*' and '&&' in types/declarations to")),
+ bindTo(&typesGroupWidget),
Column {
m_bindStarToIdentifier,
m_bindStarToTypeName,
@@ -233,7 +242,8 @@ public:
};
Row {
- TabWidget { m_categoryTab, {
+ TabWidget {
+ bindTo(&m_categoryTab),
Tab { Tr::tr("General"),
Row { Column { m_tabSettingsWidget, st }, createPreview(0) }
},
@@ -242,15 +252,17 @@ public:
Tab { Tr::tr("\"switch\""), Row { switchGroup, createPreview(3) } },
Tab { Tr::tr("Alignment"), Row { alignmentGroup, createPreview(4) } },
Tab { Tr::tr("Pointers and References"), Row { typesGroup, createPreview(5) } }
- } }
+ }
}.attachTo(q);
+ m_categoryTab->setProperty("_q_custom_style_disabled", true);
+
m_controllers.append(m_tabSettingsWidget);
- m_controllers.append(contentGroup.widget);
- m_controllers.append(bracesGroup.widget);
- m_controllers.append(switchGroup.widget);
- m_controllers.append(alignmentGroup.widget);
- m_controllers.append(typesGroup.widget);
+ m_controllers.append(contentGroupWidget);
+ m_controllers.append(bracesGroupWidget);
+ m_controllers.append(switchGroupWidget);
+ m_controllers.append(alignmentGroupWidget);
+ m_controllers.append(typesGroupWidget);
}
QCheckBox *createCheckBox(const QString &text, const QString &toolTip = {})
@@ -414,7 +426,8 @@ void CppCodeStylePreferencesWidget::setCodeStyleSettings(const CppCodeStyleSetti
void CppCodeStylePreferencesWidget::slotCurrentPreferencesChanged(ICodeStylePreferences *preferences, bool preview)
{
- const bool enable = !preferences->isReadOnly() && !preferences->isTemporarilyReadOnly();
+ const bool enable = !preferences->isReadOnly() && (!preferences->isTemporarilyReadOnly()
+ || preferences->isAdditionalTabDisabled());
for (QWidget *widget : d->m_controllers)
widget->setEnabled(enable);
@@ -548,18 +561,13 @@ void CppCodeStylePreferencesWidget::finish()
emit finishEmitted();
}
-// ------------------ CppCodeStyleSettingsPage
-
-CppCodeStyleSettingsPage::CppCodeStyleSettingsPage()
-{
- setId(Constants::CPP_CODE_STYLE_SETTINGS_ID);
- setDisplayName(Tr::tr("Code Style"));
- setCategory(Constants::CPP_SETTINGS_CATEGORY);
-}
+// CppCodeStyleSettingsPageWidget
-QWidget *CppCodeStyleSettingsPage::widget()
+class CppCodeStyleSettingsPageWidget : public Core::IOptionsPageWidget
{
- if (!m_widget) {
+public:
+ CppCodeStyleSettingsPageWidget()
+ {
CppCodeStylePreferences *originalCodeStylePreferences = CppToolsSettings::instance()
->cppCodeStyle();
m_pageCppCodeStylePreferences = new CppCodeStylePreferences();
@@ -571,15 +579,16 @@ QWidget *CppCodeStyleSettingsPage::widget()
originalCodeStylePreferences->currentDelegate());
// we set id so that it won't be possible to set delegate to the original prefs
m_pageCppCodeStylePreferences->setId(originalCodeStylePreferences->id());
- m_widget = TextEditorSettings::codeStyleFactory(CppEditor::Constants::CPP_SETTINGS_ID)
- ->createCodeStyleEditor(m_pageCppCodeStylePreferences);
+
+ m_codeStyleEditor = TextEditorSettings::codeStyleFactory(CppEditor::Constants::CPP_SETTINGS_ID)
+ ->createCodeStyleEditor(m_pageCppCodeStylePreferences);
+
+ auto hbox = new QVBoxLayout(this);
+ hbox->addWidget(m_codeStyleEditor);
}
- return m_widget;
-}
-void CppCodeStyleSettingsPage::apply()
-{
- if (m_widget) {
+ void apply() final
+ {
QSettings *s = Core::ICore::settings();
CppCodeStylePreferences *originalCppCodeStylePreferences = CppToolsSettings::instance()->cppCodeStyle();
@@ -596,15 +605,21 @@ void CppCodeStyleSettingsPage::apply()
originalCppCodeStylePreferences->toSettings(QLatin1String(CppEditor::Constants::CPP_SETTINGS_ID), s);
}
- m_widget->apply();
+ m_codeStyleEditor->apply();
}
-}
-void CppCodeStyleSettingsPage::finish()
+ CppCodeStylePreferences *m_pageCppCodeStylePreferences = nullptr;
+ CodeStyleEditorWidget *m_codeStyleEditor;
+};
+
+// CppCodeStyleSettingsPage
+
+CppCodeStyleSettingsPage::CppCodeStyleSettingsPage()
{
- if (m_widget)
- m_widget->finish();
- delete m_widget;
+ setId(Constants::CPP_CODE_STYLE_SETTINGS_ID);
+ setDisplayName(Tr::tr("Code Style"));
+ setCategory(Constants::CPP_SETTINGS_CATEGORY);
+ setWidgetCreator([] { return new CppCodeStyleSettingsPageWidget; });
}
} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/cppcodestylesettingspage.h b/src/plugins/cppeditor/cppcodestylesettingspage.h
index da15c38948..666b3ab4a2 100644
--- a/src/plugins/cppeditor/cppcodestylesettingspage.h
+++ b/src/plugins/cppeditor/cppcodestylesettingspage.h
@@ -86,14 +86,6 @@ class CppCodeStyleSettingsPage : public Core::IOptionsPage
{
public:
CppCodeStyleSettingsPage();
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-private:
- CppCodeStylePreferences *m_pageCppCodeStylePreferences = nullptr;
- QPointer<TextEditor::CodeStyleEditorWidget> m_widget;
};
} // namespace Internal
diff --git a/src/plugins/cppeditor/cppcompletion_test.cpp b/src/plugins/cppeditor/cppcompletion_test.cpp
index 78407d5959..1e212dde60 100644
--- a/src/plugins/cppeditor/cppcompletion_test.cpp
+++ b/src/plugins/cppeditor/cppcompletion_test.cpp
@@ -70,6 +70,10 @@ public:
// Get Document
const Document::Ptr document = waitForFileInGlobalSnapshot(filePath);
QVERIFY(document);
+ if (!document->diagnosticMessages().isEmpty()) {
+ for (const Document::DiagnosticMessage &m : document->diagnosticMessages())
+ qDebug().noquote() << m.text();
+ }
QVERIFY(document->diagnosticMessages().isEmpty());
m_snapshot.insert(document);
@@ -409,16 +413,16 @@ static void enumTestCase(const QByteArray &tag, const QByteArray &source,
const QByteArray &prefix = QByteArray())
{
QByteArray fullSource = source;
- fullSource.replace('$', "enum E { val1, val2, val3 };");
- QTest::newRow(tag) << fullSource << (prefix + "val")
- << QStringList({"val1", "val2", "val3"});
+ fullSource.replace('$', "enum E { value1, value2, value3 };");
+ QTest::newRow(tag) << fullSource << (prefix + "value")
+ << QStringList({"value1", "value2", "value3"});
QTest::newRow(QByteArray{tag + "_cxx11"}) << fullSource << QByteArray{prefix + "E::"}
- << QStringList({"E", "val1", "val2", "val3"});
+ << QStringList({"E", "value1", "value2", "value3"});
fullSource.replace("enum E ", "enum ");
- QTest::newRow(QByteArray{tag + "_anon"}) << fullSource << QByteArray{prefix + "val"}
- << QStringList({"val1", "val2", "val3"});
+ QTest::newRow(QByteArray{tag + "_anon"}) << fullSource << QByteArray{prefix + "value"}
+ << QStringList({"value1", "value2", "value3"});
}
void CompletionTest::testCompletion_data()
diff --git a/src/plugins/cppeditor/cppcompletionassist.cpp b/src/plugins/cppeditor/cppcompletionassist.cpp
index e47bee37c4..5466f675ea 100644
--- a/src/plugins/cppeditor/cppcompletionassist.cpp
+++ b/src/plugins/cppeditor/cppcompletionassist.cpp
@@ -1041,7 +1041,7 @@ int InternalCppCompletionAssistProcessor::startCompletionHelper()
int line = 0, column = 0;
Utils::Text::convertPosition(interface()->textDocument(), startOfExpression, &line, &column);
return startCompletionInternal(interface()->filePath(),
- line, column - 1, expression, endOfExpression);
+ line, column, expression, endOfExpression);
}
bool InternalCppCompletionAssistProcessor::tryObjCCompletion()
@@ -1074,7 +1074,7 @@ bool InternalCppCompletionAssistProcessor::tryObjCCompletion()
int line = 0, column = 0;
Utils::Text::convertPosition(interface()->textDocument(), interface()->position(), &line,
&column);
- Scope *scope = thisDocument->scopeAt(line, column - 1);
+ Scope *scope = thisDocument->scopeAt(line, column);
if (!scope)
return false;
@@ -1989,7 +1989,7 @@ bool InternalCppCompletionAssistProcessor::completeConstructorOrFunction(const Q
int lineSigned = 0, columnSigned = 0;
Utils::Text::convertPosition(interface()->textDocument(), interface()->position(),
&lineSigned, &columnSigned);
- unsigned line = lineSigned, column = columnSigned - 1;
+ unsigned line = lineSigned, column = columnSigned;
// find a scope that encloses the current location, starting from the lastVisibileSymbol
// and moving outwards
diff --git a/src/plugins/cppeditor/cppcurrentdocumentfilter.cpp b/src/plugins/cppeditor/cppcurrentdocumentfilter.cpp
deleted file mode 100644
index 0285fd71fd..0000000000
--- a/src/plugins/cppeditor/cppcurrentdocumentfilter.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "cppcurrentdocumentfilter.h"
-
-#include "cppeditorconstants.h"
-#include "cppeditortr.h"
-#include "cppmodelmanager.h"
-
-#include <coreplugin/editormanager/editormanager.h>
-#include <coreplugin/editormanager/ieditor.h>
-#include <coreplugin/idocument.h>
-
-#include <QRegularExpression>
-
-using namespace CPlusPlus;
-
-namespace CppEditor::Internal {
-
-CppCurrentDocumentFilter::CppCurrentDocumentFilter(CppModelManager *manager)
- : m_modelManager(manager)
-{
- setId(Constants::CURRENT_DOCUMENT_FILTER_ID);
- setDisplayName(Tr::tr(Constants::CURRENT_DOCUMENT_FILTER_DISPLAY_NAME));
- setDefaultShortcutString(".");
- setPriority(High);
- setDefaultIncludedByDefault(false);
-
- search.setSymbolsToSearchFor(SymbolSearcher::Declarations |
- SymbolSearcher::Enums |
- SymbolSearcher::Functions |
- SymbolSearcher::Classes);
-
- connect(manager, &CppModelManager::documentUpdated,
- this, &CppCurrentDocumentFilter::onDocumentUpdated);
- connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
- this, &CppCurrentDocumentFilter::onCurrentEditorChanged);
- connect(Core::EditorManager::instance(), &Core::EditorManager::editorAboutToClose,
- this, &CppCurrentDocumentFilter::onEditorAboutToClose);
-}
-
-void CppCurrentDocumentFilter::makeAuxiliary()
-{
- setId({});
- setDisplayName({});
- setDefaultShortcutString({});
- setEnabled(false);
- setHidden(true);
-}
-
-QList<Core::LocatorFilterEntry> CppCurrentDocumentFilter::matchesFor(
- QFutureInterface<Core::LocatorFilterEntry> &future, const QString & entry)
-{
- QList<Core::LocatorFilterEntry> goodEntries;
- QList<Core::LocatorFilterEntry> betterEntries;
-
- const QRegularExpression regexp = createRegExp(entry);
- if (!regexp.isValid())
- return goodEntries;
-
- const QList<IndexItem::Ptr> items = itemsOfCurrentDocument();
- for (const IndexItem::Ptr &info : items) {
- if (future.isCanceled())
- break;
-
- QString matchString = info->symbolName();
- if (info->type() == IndexItem::Declaration)
- matchString = info->representDeclaration();
- else if (info->type() == IndexItem::Function)
- matchString += info->symbolType();
-
- QRegularExpressionMatch match = regexp.match(matchString);
- if (match.hasMatch()) {
- const bool betterMatch = match.capturedStart() == 0;
- QVariant id = QVariant::fromValue(info);
- QString name = matchString;
- QString extraInfo = info->symbolScope();
- if (info->type() == IndexItem::Function) {
- if (info->unqualifiedNameAndScope(matchString, &name, &extraInfo)) {
- name += info->symbolType();
- match = regexp.match(name);
- }
- }
-
- Core::LocatorFilterEntry filterEntry(this, name, id, info->icon());
- filterEntry.extraInfo = extraInfo;
- if (match.hasMatch()) {
- filterEntry.highlightInfo = highlightInfo(match);
- } else {
- match = regexp.match(extraInfo);
- filterEntry.highlightInfo =
- highlightInfo(match, Core::LocatorFilterEntry::HighlightInfo::ExtraInfo);
- }
-
- if (betterMatch)
- betterEntries.append(filterEntry);
- else
- goodEntries.append(filterEntry);
- }
- }
-
- // entries are unsorted by design!
-
- betterEntries += goodEntries;
- return betterEntries;
-}
-
-void CppCurrentDocumentFilter::accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart,
- int *selectionLength) const
-{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- IndexItem::Ptr info = qvariant_cast<IndexItem::Ptr>(selection.internalData);
- Core::EditorManager::openEditorAt({info->filePath(), info->line(), info->column()});
-}
-
-void CppCurrentDocumentFilter::onDocumentUpdated(Document::Ptr doc)
-{
- QMutexLocker locker(&m_mutex);
- if (m_currentFileName == doc->filePath())
- m_itemsOfCurrentDoc.clear();
-}
-
-void CppCurrentDocumentFilter::onCurrentEditorChanged(Core::IEditor *currentEditor)
-{
- QMutexLocker locker(&m_mutex);
- if (currentEditor)
- m_currentFileName = currentEditor->document()->filePath();
- else
- m_currentFileName.clear();
- m_itemsOfCurrentDoc.clear();
-}
-
-void CppCurrentDocumentFilter::onEditorAboutToClose(Core::IEditor *editorAboutToClose)
-{
- if (!editorAboutToClose)
- return;
-
- QMutexLocker locker(&m_mutex);
- if (m_currentFileName == editorAboutToClose->document()->filePath()) {
- m_currentFileName.clear();
- m_itemsOfCurrentDoc.clear();
- }
-}
-
-QList<IndexItem::Ptr> CppCurrentDocumentFilter::itemsOfCurrentDocument()
-{
- QMutexLocker locker(&m_mutex);
-
- if (m_currentFileName.isEmpty())
- return QList<IndexItem::Ptr>();
-
- if (m_itemsOfCurrentDoc.isEmpty()) {
- const Snapshot snapshot = m_modelManager->snapshot();
- if (const Document::Ptr thisDocument = snapshot.document(m_currentFileName)) {
- IndexItem::Ptr rootNode = search(thisDocument);
- rootNode->visitAllChildren([&](const IndexItem::Ptr &info) -> IndexItem::VisitorResult {
- m_itemsOfCurrentDoc.append(info);
- return IndexItem::Recurse;
- });
- }
- }
-
- return m_itemsOfCurrentDoc;
-}
-
-} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/cppcurrentdocumentfilter.h b/src/plugins/cppeditor/cppcurrentdocumentfilter.h
deleted file mode 100644
index 484812a0cb..0000000000
--- a/src/plugins/cppeditor/cppcurrentdocumentfilter.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "searchsymbols.h"
-
-#include <coreplugin/locator/ilocatorfilter.h>
-
-namespace Core { class IEditor; }
-
-namespace CppEditor {
-
-class CppModelManager;
-
-namespace Internal {
-
-class CppCurrentDocumentFilter : public Core::ILocatorFilter
-{
- Q_OBJECT
-
-public:
- explicit CppCurrentDocumentFilter(CppModelManager *manager);
- ~CppCurrentDocumentFilter() override = default;
-
- void makeAuxiliary();
-
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
-
-private:
- void onDocumentUpdated(CPlusPlus::Document::Ptr doc);
- void onCurrentEditorChanged(Core::IEditor *currentEditor);
- void onEditorAboutToClose(Core::IEditor *currentEditor);
-
- QList<IndexItem::Ptr> itemsOfCurrentDocument();
-
- CppModelManager * m_modelManager;
- SearchSymbols search;
-
- mutable QMutex m_mutex;
- Utils::FilePath m_currentFileName;
- QList<IndexItem::Ptr> m_itemsOfCurrentDoc;
-};
-
-} // namespace Internal
-} // namespace CppEditor
diff --git a/src/plugins/cppeditor/cppeditor.qbs b/src/plugins/cppeditor/cppeditor.qbs
index cb7b145078..b7d2aea031 100644
--- a/src/plugins/cppeditor/cppeditor.qbs
+++ b/src/plugins/cppeditor/cppeditor.qbs
@@ -82,8 +82,6 @@ QtcPlugin {
"cppcompletionassistprocessor.h",
"cppcompletionassistprovider.cpp",
"cppcompletionassistprovider.h",
- "cppcurrentdocumentfilter.cpp",
- "cppcurrentdocumentfilter.h",
"cppcursorinfo.h",
"cppdoxygen.cpp",
"cppdoxygen.h",
@@ -91,7 +89,8 @@ QtcPlugin {
"cppeditorwidget.cpp",
"cppeditorwidget.h",
"cppeditor.qrc",
- "cppeditor_global.h", "cppeditortr.h",
+ "cppeditor_global.h",
+ "cppeditortr.h",
"cppeditorconstants.h",
"cppeditordocument.cpp",
"cppeditordocument.h",
@@ -224,8 +223,6 @@ QtcPlugin {
"searchsymbols.h",
"semantichighlighter.cpp",
"semantichighlighter.h",
- "senddocumenttracker.cpp",
- "senddocumenttracker.h",
"symbolfinder.cpp",
"symbolfinder.h",
"symbolsfindfilter.cpp",
@@ -245,9 +242,7 @@ QtcPlugin {
]
}
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
cpp.defines: outer.concat(['SRCDIR="' + FileInfo.path(filePath) + '"'])
files: [
"compileroptionsbuilder_test.cpp",
diff --git a/src/plugins/cppeditor/cppeditorconstants.h b/src/plugins/cppeditor/cppeditorconstants.h
index 3c87aa6d63..179c3b3b9b 100644
--- a/src/plugins/cppeditor/cppeditorconstants.h
+++ b/src/plugins/cppeditor/cppeditorconstants.h
@@ -110,12 +110,18 @@ const char CPP_SETTINGS_NAME[] = QT_TRANSLATE_NOOP("QtC::CppEditor", "C++");
const char CURRENT_DOCUMENT_FILTER_ID[] = "Methods in current Document";
const char CURRENT_DOCUMENT_FILTER_DISPLAY_NAME[]
= QT_TRANSLATE_NOOP("QtC::CppEditor", "C++ Symbols in Current Document");
+const char CURRENT_DOCUMENT_FILTER_DESCRIPTION[]
+ = QT_TRANSLATE_NOOP("QtC::CppEditor", "Locates C++ symbols in the current document.");
const char CLASSES_FILTER_ID[] = "Classes";
const char CLASSES_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::CppEditor", "C++ Classes");
+const char CLASSES_FILTER_DESCRIPTION[]
+ = QT_TRANSLATE_NOOP("QtC::CppEditor", "Locates C++ classes in any open project.");
const char FUNCTIONS_FILTER_ID[] = "Methods";
const char FUNCTIONS_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::CppEditor", "C++ Functions");
+const char FUNCTIONS_FILTER_DESCRIPTION[]
+ = QT_TRANSLATE_NOOP("QtC::CppEditor", "Locates C++ functions in any open project.");
const char INCLUDES_FILTER_ID[] = "All Included C/C++ Files";
const char INCLUDES_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::CppEditor",
@@ -124,6 +130,8 @@ const char INCLUDES_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::CppEditor",
const char LOCATOR_FILTER_ID[] = "Classes and Methods";
const char LOCATOR_FILTER_DISPLAY_NAME[]
= QT_TRANSLATE_NOOP("QtC::CppEditor", "C++ Classes, Enums, Functions and Type Aliases");
+const char LOCATOR_FILTER_DESCRIPTION[] = QT_TRANSLATE_NOOP(
+ "QtC::CppEditor", "Locates C++ classes, enums, functions and type aliases in any open project.");
const char SYMBOLS_FIND_FILTER_ID[] = "Symbols";
const char SYMBOLS_FIND_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::CppEditor", "C++ Symbols");
diff --git a/src/plugins/cppeditor/cppeditordocument.cpp b/src/plugins/cppeditor/cppeditordocument.cpp
index 7db63ec7af..328b2c2346 100644
--- a/src/plugins/cppeditor/cppeditordocument.cpp
+++ b/src/plugins/cppeditor/cppeditordocument.cpp
@@ -16,8 +16,7 @@
#include "cppquickfixassistant.h"
#include <coreplugin/editormanager/editormanager.h>
-
-#include <projectexplorer/session.h>
+#include <coreplugin/session.h>
#include <texteditor/icodestylepreferencesfactory.h>
#include <texteditor/storagesettings.h>
@@ -217,7 +216,7 @@ void CppEditorDocument::reparseWithPreferredParseContext(const QString &parseCon
// Remember the setting
const QString key = Constants::PREFERRED_PARSE_CONTEXT + filePath().toString();
- ProjectExplorer::SessionManager::setValue(key, parseContextId);
+ Core::SessionManager::setValue(key, parseContextId);
// Reprocess
scheduleProcessDocument();
@@ -284,7 +283,7 @@ void CppEditorDocument::applyPreferredParseContextFromSettings()
return;
const QString key = Constants::PREFERRED_PARSE_CONTEXT + filePath().toString();
- const QString parseContextId = ProjectExplorer::SessionManager::value(key).toString();
+ const QString parseContextId = Core::SessionManager::value(key).toString();
setPreferredParseContext(parseContextId);
}
@@ -295,7 +294,7 @@ void CppEditorDocument::applyExtraPreprocessorDirectivesFromSettings()
return;
const QString key = Constants::EXTRA_PREPROCESSOR_DIRECTIVES + filePath().toString();
- const QByteArray directives = ProjectExplorer::SessionManager::value(key).toString().toUtf8();
+ const QByteArray directives = Core::SessionManager::value(key).toString().toUtf8();
setExtraPreprocessorDirectives(directives);
}
diff --git a/src/plugins/cppeditor/cppeditoroutline.cpp b/src/plugins/cppeditor/cppeditoroutline.cpp
index b0d14ca119..a34d254361 100644
--- a/src/plugins/cppeditor/cppeditoroutline.cpp
+++ b/src/plugins/cppeditor/cppeditoroutline.cpp
@@ -14,7 +14,6 @@
#include <texteditor/textdocument.h>
#include <coreplugin/editormanager/editormanager.h>
-#include <utils/linecolumn.h>
#include <utils/treeviewcombobox.h>
#include <QAction>
diff --git a/src/plugins/cppeditor/cppeditorplugin.cpp b/src/plugins/cppeditor/cppeditorplugin.cpp
index f1fc63b49a..5791a5cb62 100644
--- a/src/plugins/cppeditor/cppeditorplugin.cpp
+++ b/src/plugins/cppeditor/cppeditorplugin.cpp
@@ -49,7 +49,6 @@
#include "functionutils.h"
#include "includeutils.h"
#include "projectinfo_test.h"
-#include "senddocumenttracker.h"
#include "symbolsearcher_test.h"
#include "typehierarchybuilder_test.h"
#endif
@@ -431,6 +430,10 @@ void CppEditorPlugin::initialize()
contextMenu->addAction(cmd, Constants::G_CONTEXT_FIRST);
cppToolsMenu->addAction(cmd);
+ cmd = ActionManager::command(TextEditor::Constants::OPEN_CALL_HIERARCHY);
+ contextMenu->addAction(cmd, Constants::G_CONTEXT_FIRST);
+ cppToolsMenu->addAction(cmd);
+
// Refactoring sub-menu
Command *sep = contextMenu->addSeparator();
sep->action()->setObjectName(QLatin1String(Constants::M_REFACTORING_MENU_INSERTION_POINT));
@@ -482,7 +485,6 @@ void CppEditorPlugin::initialize()
addTest<ProjectFileCategorizerTest>();
addTest<ProjectInfoGeneratorTest>();
addTest<ProjectPartChooserTest>();
- addTest<DocumentTrackerTest>();
addTest<SourceProcessorTest>();
addTest<SymbolSearcherTest>();
addTest<TypeHierarchyBuilderTest>();
diff --git a/src/plugins/cppeditor/cppeditorwidget.cpp b/src/plugins/cppeditor/cppeditorwidget.cpp
index 0430f210a0..edc65f7652 100644
--- a/src/plugins/cppeditor/cppeditorwidget.cpp
+++ b/src/plugins/cppeditor/cppeditorwidget.cpp
@@ -30,9 +30,9 @@
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/extracompiler.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <texteditor/basefilefind.h>
@@ -56,6 +56,7 @@
#include <utils/infobar.h>
#include <utils/progressindicator.h>
#include <utils/qtcassert.h>
+#include <utils/stylehelper.h>
#include <utils/textutils.h>
#include <utils/utilsicons.h>
@@ -684,13 +685,13 @@ void CppEditorWidget::updateWidgetHighlighting(QWidget *widget, bool highlight)
if (!widget)
return;
- widget->setProperty("highlightWidget", highlight);
+ widget->setProperty(StyleHelper::C_HIGHLIGHT_WIDGET, highlight);
widget->update();
}
bool CppEditorWidget::isWidgetHighlighted(QWidget *widget)
{
- return widget ? widget->property("highlightWidget").toBool() : false;
+ return widget ? widget->property(StyleHelper::C_HIGHLIGHT_WIDGET).toBool() : false;
}
namespace {
@@ -755,7 +756,7 @@ void CppEditorWidget::showRenameWarningIfFileIsGenerated(const Utils::FilePath &
{
if (filePath.isEmpty())
return;
- for (const Project * const project : SessionManager::projects()) {
+ for (const Project * const project : ProjectManager::projects()) {
const Node * const node = project->nodeForFilePath(filePath);
if (!node)
continue;
@@ -779,11 +780,11 @@ void CppEditorWidget::showRenameWarningIfFileIsGenerated(const Utils::FilePath &
static const Id infoId("cppeditor.renameWarning");
InfoBarEntry info(infoId, warning);
if (ec) {
- info.addCustomButton(CppEditor::Tr::tr("Open %1").arg(ec->source().fileName()),
- [source = ec->source()] {
- EditorManager::openEditor(source);
- ICore::infoBar()->removeInfo(infoId);
- });
+ info.addCustomButton(CppEditor::Tr::tr("Open \"%1\"").arg(ec->source().fileName()),
+ [source = ec->source()] {
+ EditorManager::openEditor(source);
+ ICore::infoBar()->removeInfo(infoId);
+ });
}
ICore::infoBar()->addInfo(info);
return;
@@ -989,7 +990,7 @@ void CppEditorWidget::findLinkAt(const QTextCursor &cursor,
const QString fileName = filePath.fileName();
if (fileName.startsWith("ui_") && fileName.endsWith(".h")) {
const QString uiFileName = fileName.mid(3, fileName.length() - 4) + "ui";
- for (const Project * const project : SessionManager::projects()) {
+ for (const Project * const project : ProjectManager::projects()) {
const auto nodeMatcher = [uiFileName](Node *n) {
return n->filePath().fileName() == uiFileName;
};
diff --git a/src/plugins/cppeditor/cppelementevaluator.cpp b/src/plugins/cppeditor/cppelementevaluator.cpp
index dabb8cffc0..27b481cd0c 100644
--- a/src/plugins/cppeditor/cppelementevaluator.cpp
+++ b/src/plugins/cppeditor/cppelementevaluator.cpp
@@ -14,7 +14,7 @@
#include <cplusplus/Icons.h>
#include <cplusplus/TypeOfExpression.h>
-#include <utils/runextensions.h>
+#include <utils/async.h>
#include <QDir>
#include <QQueue>
@@ -140,20 +140,20 @@ CppClass *CppClass::toCppClass()
return this;
}
-void CppClass::lookupBases(QFutureInterfaceBase &futureInterface,
- Symbol *declaration, const LookupContext &context)
+void CppClass::lookupBases(const QFuture<void> &future, Symbol *declaration,
+ const LookupContext &context)
{
ClassOrNamespace *hierarchy = context.lookupType(declaration);
if (!hierarchy)
return;
QSet<ClassOrNamespace *> visited;
- addBaseHierarchy(futureInterface, context, hierarchy, &visited);
+ addBaseHierarchy(future, context, hierarchy, &visited);
}
-void CppClass::addBaseHierarchy(QFutureInterfaceBase &futureInterface, const LookupContext &context,
+void CppClass::addBaseHierarchy(const QFuture<void> &future, const LookupContext &context,
ClassOrNamespace *hierarchy, QSet<ClassOrNamespace *> *visited)
{
- if (futureInterface.isCanceled())
+ if (future.isCanceled())
return;
visited->insert(hierarchy);
const QList<ClassOrNamespace *> &baseClasses = hierarchy->usings();
@@ -165,21 +165,21 @@ void CppClass::addBaseHierarchy(QFutureInterfaceBase &futureInterface, const Loo
ClassOrNamespace *baseHierarchy = context.lookupType(symbol);
if (baseHierarchy && !visited->contains(baseHierarchy)) {
CppClass classSymbol(symbol);
- classSymbol.addBaseHierarchy(futureInterface, context, baseHierarchy, visited);
+ classSymbol.addBaseHierarchy(future, context, baseHierarchy, visited);
bases.append(classSymbol);
}
}
}
}
-void CppClass::lookupDerived(QFutureInterfaceBase &futureInterface,
- Symbol *declaration, const Snapshot &snapshot)
+void CppClass::lookupDerived(const QFuture<void> &future, Symbol *declaration,
+ const Snapshot &snapshot)
{
- snapshot.updateDependencyTable(futureInterface);
- if (futureInterface.isCanceled())
+ snapshot.updateDependencyTable(future);
+ if (future.isCanceled())
return;
addDerivedHierarchy(TypeHierarchyBuilder::buildDerivedTypeHierarchy(
- futureInterface, declaration, snapshot));
+ declaration, snapshot, future));
}
void CppClass::addDerivedHierarchy(const TypeHierarchy &hierarchy)
@@ -340,13 +340,13 @@ static Symbol *followTemplateAsClass(Symbol *symbol)
return symbol;
}
-static void createTypeHierarchy(QFutureInterface<QSharedPointer<CppElement>> &futureInterface,
+static void createTypeHierarchy(QPromise<QSharedPointer<CppElement>> &promise,
const Snapshot &snapshot,
const LookupItem &lookupItem,
const LookupContext &context,
SymbolFinder symbolFinder)
{
- if (futureInterface.isCanceled())
+ if (promise.isCanceled())
return;
Symbol *declaration = lookupItem.declaration();
@@ -360,16 +360,17 @@ static void createTypeHierarchy(QFutureInterface<QSharedPointer<CppElement>> &fu
declaration = followClassDeclaration(declaration, snapshot, symbolFinder, &contextToUse);
declaration = followTemplateAsClass(declaration);
- if (futureInterface.isCanceled())
+ if (promise.isCanceled())
return;
QSharedPointer<CppClass> cppClass(new CppClass(declaration));
- cppClass->lookupBases(futureInterface, declaration, contextToUse);
- if (futureInterface.isCanceled())
+ const QFuture<void> future = QFuture<void>(promise.future());
+ cppClass->lookupBases(future, declaration, contextToUse);
+ if (promise.isCanceled())
return;
- cppClass->lookupDerived(futureInterface, declaration, snapshot);
- if (futureInterface.isCanceled())
+ cppClass->lookupDerived(future, declaration, snapshot);
+ if (promise.isCanceled())
return;
- futureInterface.reportResult(cppClass);
+ promise.addResult(cppClass);
}
static QSharedPointer<CppElement> handleLookupItemMatch(const Snapshot &snapshot,
@@ -495,7 +496,7 @@ static QFuture<QSharedPointer<CppElement>> asyncExec(
const CPlusPlus::Snapshot &snapshot, const CPlusPlus::LookupItem &lookupItem,
const CPlusPlus::LookupContext &lookupContext)
{
- return Utils::runAsync(&createTypeHierarchy, snapshot, lookupItem, lookupContext,
+ return Utils::asyncRun(&createTypeHierarchy, snapshot, lookupItem, lookupContext,
*CppModelManager::instance()->symbolFinder());
}
@@ -561,7 +562,7 @@ public:
expression = expressionUnderCursor(m_tc);
// Fetch the expression's code
- *scope = doc->scopeAt(line, column - 1);
+ *scope = doc->scopeAt(line, column);
return true;
}
QFuture<QSharedPointer<CppElement>> syncExec(const CPlusPlus::Snapshot &,
diff --git a/src/plugins/cppeditor/cppelementevaluator.h b/src/plugins/cppeditor/cppelementevaluator.h
index 7d03df6712..47ddcbeae1 100644
--- a/src/plugins/cppeditor/cppelementevaluator.h
+++ b/src/plugins/cppeditor/cppelementevaluator.h
@@ -92,16 +92,16 @@ public:
CppClass *toCppClass() final;
- void lookupBases(QFutureInterfaceBase &futureInterface,
- CPlusPlus::Symbol *declaration, const CPlusPlus::LookupContext &context);
- void lookupDerived(QFutureInterfaceBase &futureInterface,
- CPlusPlus::Symbol *declaration, const CPlusPlus::Snapshot &snapshot);
+ void lookupBases(const QFuture<void> &future, CPlusPlus::Symbol *declaration,
+ const CPlusPlus::LookupContext &context);
+ void lookupDerived(const QFuture<void> &future, CPlusPlus::Symbol *declaration,
+ const CPlusPlus::Snapshot &snapshot);
QList<CppClass> bases;
QList<CppClass> derived;
private:
- void addBaseHierarchy(QFutureInterfaceBase &futureInterface,
+ void addBaseHierarchy(const QFuture<void> &future,
const CPlusPlus::LookupContext &context,
CPlusPlus::ClassOrNamespace *hierarchy,
QSet<CPlusPlus::ClassOrNamespace *> *visited);
diff --git a/src/plugins/cppeditor/cppfindreferences.cpp b/src/plugins/cppeditor/cppfindreferences.cpp
index 940eb358e9..5da2285438 100644
--- a/src/plugins/cppeditor/cppfindreferences.cpp
+++ b/src/plugins/cppeditor/cppfindreferences.cpp
@@ -10,25 +10,27 @@
#include "cpptoolsreuse.h"
#include "cppworkingcopy.h"
+#include <cplusplus/Overview.h>
+
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/futureprogress.h>
#include <coreplugin/progressmanager/progressmanager.h>
+
#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
+
#include <texteditor/basefilefind.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <utils/textfileformat.h>
-#include <cplusplus/Overview.h>
#include <QtConcurrentMap>
#include <QCheckBox>
-#include <QDir>
#include <QFutureWatcher>
#include <QVBoxLayout>
@@ -103,8 +105,8 @@ namespace Internal {
static QByteArray getSource(const Utils::FilePath &fileName,
const WorkingCopy &workingCopy)
{
- if (workingCopy.contains(fileName)) {
- return workingCopy.source(fileName);
+ if (const auto source = workingCopy.source(fileName)) {
+ return *source;
} else {
QString fileContents;
Utils::TextFileFormat format;
@@ -218,7 +220,7 @@ class ProcessFile
const CPlusPlus::Snapshot snapshot;
CPlusPlus::Document::Ptr symbolDocument;
CPlusPlus::Symbol *symbol;
- QFutureInterface<CPlusPlus::Usage> *future;
+ QPromise<CPlusPlus::Usage> *m_promise;
const bool categorize;
public:
@@ -230,22 +232,21 @@ public:
const CPlusPlus::Snapshot snapshot,
CPlusPlus::Document::Ptr symbolDocument,
CPlusPlus::Symbol *symbol,
- QFutureInterface<CPlusPlus::Usage> *future,
+ QPromise<CPlusPlus::Usage> *promise,
bool categorize)
: workingCopy(workingCopy),
snapshot(snapshot),
symbolDocument(symbolDocument),
symbol(symbol),
- future(future),
+ m_promise(promise),
categorize(categorize)
{ }
QList<CPlusPlus::Usage> operator()(const Utils::FilePath &filePath)
{
QList<CPlusPlus::Usage> usages;
- if (future->isPaused())
- future->waitForResume();
- if (future->isCanceled())
+ m_promise->suspendIfRequested();
+ if (m_promise->isCanceled())
return usages;
const CPlusPlus::Identifier *symbolId = symbol->identifier();
@@ -275,25 +276,24 @@ public:
usages = process.usages();
}
- if (future->isPaused())
- future->waitForResume();
+ m_promise->suspendIfRequested();
return usages;
}
};
class UpdateUI
{
- QFutureInterface<CPlusPlus::Usage> *future;
+ QPromise<CPlusPlus::Usage> *m_promise;
public:
- explicit UpdateUI(QFutureInterface<CPlusPlus::Usage> *future): future(future) {}
+ explicit UpdateUI(QPromise<CPlusPlus::Usage> *promise): m_promise(promise) {}
void operator()(QList<CPlusPlus::Usage> &, const QList<CPlusPlus::Usage> &usages)
{
for (const CPlusPlus::Usage &u : usages)
- future->reportResult(u);
+ m_promise->addResult(u);
- future->setProgressValue(future->progressValue() + 1);
+ m_promise->setProgressValue(m_promise->future().progressValue() + 1);
}
};
@@ -319,7 +319,7 @@ QList<int> CppFindReferences::references(CPlusPlus::Symbol *symbol,
return references;
}
-static void find_helper(QFutureInterface<CPlusPlus::Usage> &future,
+static void find_helper(QPromise<CPlusPlus::Usage> &promise,
const WorkingCopy workingCopy,
const CPlusPlus::LookupContext &context,
CPlusPlus::Symbol *symbol,
@@ -353,16 +353,16 @@ static void find_helper(QFutureInterface<CPlusPlus::Usage> &future,
}
files = Utils::filteredUnique(files);
- future.setProgressRange(0, files.size());
+ promise.setProgressRange(0, files.size());
- ProcessFile process(workingCopy, snapshot, context.thisDocument(), symbol, &future, categorize);
- UpdateUI reduce(&future);
+ ProcessFile process(workingCopy, snapshot, context.thisDocument(), symbol, &promise, categorize);
+ UpdateUI reduce(&promise);
// This thread waits for blockingMappedReduced to finish, so reduce the pool's used thread count
// so the blockingMappedReduced can use one more thread, and increase it again afterwards.
QThreadPool::globalInstance()->releaseThread();
QtConcurrent::blockingMappedReduced<QList<CPlusPlus::Usage> > (files, process, reduce);
QThreadPool::globalInstance()->reserveThread();
- future.setProgressValue(files.size());
+ promise.setProgressValue(files.size());
}
void CppFindReferences::findUsages(CPlusPlus::Symbol *symbol,
@@ -437,7 +437,7 @@ void CppFindReferences::findAll_helper(SearchResult *search, CPlusPlus::Symbol *
SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus);
const WorkingCopy workingCopy = m_modelManager->workingCopy();
QFuture<CPlusPlus::Usage> result;
- result = Utils::runAsync(m_modelManager->sharedThreadPool(), find_helper,
+ result = Utils::asyncRun(m_modelManager->sharedThreadPool(), find_helper,
workingCopy, context, symbol, categorize);
createWatcher(result, search);
@@ -456,10 +456,8 @@ void CppFindReferences::setupSearch(Core::SearchResult *search)
std::bind(&CppFindReferences::onReplaceButtonClicked, this, search, _1, _2, _3));
}
-void CppFindReferences::onReplaceButtonClicked(Core::SearchResult *search,
- const QString &text,
- const QList<SearchResultItem> &items,
- bool preserveCase)
+void CppFindReferences::onReplaceButtonClicked(Core::SearchResult *search, const QString &text,
+ const SearchResultItems &items, bool preserveCase)
{
const Utils::FilePaths filePaths = TextEditor::BaseFileFind::replaceAll(text, items, preserveCase);
if (!filePaths.isEmpty()) {
@@ -577,7 +575,7 @@ static void displayResults(SearchResult *search,
item.setStyle(colorStyleForUsageType(result.tags));
item.setUseTextEditorFont(true);
if (search->supportsReplace())
- item.setSelectForReplacement(SessionManager::projectForFile(result.path));
+ item.setSelectForReplacement(ProjectManager::projectForFile(result.path));
search->addResult(item);
if (parameters.prettySymbolName.isEmpty())
@@ -586,7 +584,7 @@ static void displayResults(SearchResult *search,
if (parameters.filesToRename.contains(result.path))
continue;
- if (!SessionManager::projectForFile(result.path))
+ if (!ProjectManager::projectForFile(result.path))
continue;
if (result.path.baseName().compare(parameters.prettySymbolName, Qt::CaseInsensitive) == 0)
@@ -623,7 +621,7 @@ class FindMacroUsesInFile
const WorkingCopy workingCopy;
const CPlusPlus::Snapshot snapshot;
const CPlusPlus::Macro &macro;
- QFutureInterface<CPlusPlus::Usage> *future;
+ QPromise<CPlusPlus::Usage> *m_promise;
public:
// needed by QtConcurrent
@@ -633,8 +631,8 @@ public:
FindMacroUsesInFile(const WorkingCopy &workingCopy,
const CPlusPlus::Snapshot snapshot,
const CPlusPlus::Macro &macro,
- QFutureInterface<CPlusPlus::Usage> *future)
- : workingCopy(workingCopy), snapshot(snapshot), macro(macro), future(future)
+ QPromise<CPlusPlus::Usage> *promise)
+ : workingCopy(workingCopy), snapshot(snapshot), macro(macro), m_promise(promise)
{ }
QList<CPlusPlus::Usage> operator()(const Utils::FilePath &fileName)
@@ -644,9 +642,8 @@ public:
QByteArray source;
restart_search:
- if (future->isPaused())
- future->waitForResume();
- if (future->isCanceled())
+ m_promise->suspendIfRequested();
+ if (m_promise->isCanceled())
return usages;
usages.clear();
@@ -674,8 +671,7 @@ restart_search:
}
}
- if (future->isPaused())
- future->waitForResume();
+ m_promise->suspendIfRequested();
return usages;
}
@@ -704,7 +700,7 @@ restart_search:
} // end of anonymous namespace
-static void findMacroUses_helper(QFutureInterface<CPlusPlus::Usage> &future,
+static void findMacroUses_helper(QPromise<CPlusPlus::Usage> &promise,
const WorkingCopy workingCopy,
const CPlusPlus::Snapshot snapshot,
const CPlusPlus::Macro macro)
@@ -713,15 +709,15 @@ static void findMacroUses_helper(QFutureInterface<CPlusPlus::Usage> &future,
FilePaths files{sourceFile};
files = Utils::filteredUnique(files + snapshot.filesDependingOn(sourceFile));
- future.setProgressRange(0, files.size());
- FindMacroUsesInFile process(workingCopy, snapshot, macro, &future);
- UpdateUI reduce(&future);
+ promise.setProgressRange(0, files.size());
+ FindMacroUsesInFile process(workingCopy, snapshot, macro, &promise);
+ UpdateUI reduce(&promise);
// This thread waits for blockingMappedReduced to finish, so reduce the pool's used thread count
// so the blockingMappedReduced can use one more thread, and increase it again afterwards.
QThreadPool::globalInstance()->releaseThread();
QtConcurrent::blockingMappedReduced<QList<CPlusPlus::Usage> > (files, process, reduce);
QThreadPool::globalInstance()->reserveThread();
- future.setProgressValue(files.size());
+ promise.setProgressValue(files.size());
}
void CppFindReferences::findMacroUses(const CPlusPlus::Macro &macro)
@@ -745,10 +741,9 @@ void CppFindReferences::findMacroUses(const CPlusPlus::Macro &macro, const QStri
setupSearch(search);
SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus);
- connect(search, &SearchResult::activated,
- [](const Core::SearchResultItem& item) {
- Core::EditorManager::openEditorAtSearchResult(item);
- });
+ connect(search, &SearchResult::activated, search, [](const SearchResultItem &item) {
+ Core::EditorManager::openEditorAtSearchResult(item);
+ });
const CPlusPlus::Snapshot snapshot = m_modelManager->snapshot();
const WorkingCopy workingCopy = m_modelManager->workingCopy();
@@ -766,12 +761,12 @@ void CppFindReferences::findMacroUses(const CPlusPlus::Macro &macro, const QStri
item.setMainRange(macro.line(), column, macro.nameToQString().length());
item.setUseTextEditorFont(true);
if (search->supportsReplace())
- item.setSelectForReplacement(SessionManager::projectForFile(filePath));
+ item.setSelectForReplacement(ProjectManager::projectForFile(filePath));
search->addResult(item);
}
QFuture<CPlusPlus::Usage> result;
- result = Utils::runAsync(m_modelManager->sharedThreadPool(), findMacroUses_helper,
+ result = Utils::asyncRun(m_modelManager->sharedThreadPool(), findMacroUses_helper,
workingCopy, snapshot, macro);
createWatcher(result, search);
@@ -832,7 +827,7 @@ void CppFindReferences::checkUnused(Core::SearchResult *search, const Link &link
});
connect(search, &SearchResult::canceled, watcher, [watcher] { watcher->cancel(); });
connect(search, &SearchResult::destroyed, watcher, [watcher] { watcher->cancel(); });
- watcher->setFuture(Utils::runAsync(m_modelManager->sharedThreadPool(), find_helper,
+ watcher->setFuture(Utils::asyncRun(m_modelManager->sharedThreadPool(), find_helper,
m_modelManager->workingCopy(), context, symbol, true));
}
diff --git a/src/plugins/cppeditor/cppfindreferences.h b/src/plugins/cppeditor/cppfindreferences.h
index 54f40bf4cf..b05ce723c4 100644
--- a/src/plugins/cppeditor/cppfindreferences.h
+++ b/src/plugins/cppeditor/cppfindreferences.h
@@ -9,30 +9,29 @@
#include <cplusplus/FindUsages.h>
#include <utils/filepath.h>
#include <utils/link.h>
+#include <utils/searchresultitem.h>
-#include <QObject>
-#include <QPointer>
#include <QFuture>
+#include <QPointer>
#include <functional>
-QT_FORWARD_DECLARE_CLASS(QTimer)
+QT_BEGIN_NAMESPACE
+class QTimer;
+QT_END_NAMESPACE
-namespace Core {
-class SearchResultItem;
-class SearchResult;
-} // namespace Core
+namespace Core { class SearchResult; }
namespace CppEditor {
class CppModelManager;
-Core::SearchResultColor::Style CPPEDITOR_EXPORT
+Utils::SearchResultColor::Style CPPEDITOR_EXPORT
colorStyleForUsageType(CPlusPlus::Usage::Tags tags);
class CPPEDITOR_EXPORT CppSearchResultFilter : public Core::SearchResultFilter
{
QWidget *createWidget() override;
- bool matches(const Core::SearchResultItem &item) const override;
+ bool matches(const Utils::SearchResultItem &item) const override;
void setValue(bool &member, bool value);
@@ -79,7 +78,7 @@ public:
private:
void setupSearch(Core::SearchResult *search);
void onReplaceButtonClicked(Core::SearchResult *search, const QString &text,
- const QList<Core::SearchResultItem> &items, bool preserveCase);
+ const Utils::SearchResultItems &items, bool preserveCase);
void searchAgain(Core::SearchResult *search);
void findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context,
diff --git a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp
index d9ac0da200..0353e16c30 100644
--- a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp
+++ b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp
@@ -485,7 +485,6 @@ void FollowSymbolUnderCursor::findLink(
int line = 0;
int column = 0;
Utils::Text::convertPosition(document, cursor.position(), &line, &column);
- const int positionInBlock = column - 1;
Snapshot snapshot = theSnapshot;
@@ -530,7 +529,7 @@ void FollowSymbolUnderCursor::findLink(
for (int i = 0; i < tokens.size(); ++i) {
const Token &tk = tokens.at(i);
- if (positionInBlock >= tk.utf16charsBegin() && positionInBlock < tk.utf16charsEnd()) {
+ if (column >= tk.utf16charsBegin() && column < tk.utf16charsEnd()) {
int closingParenthesisPos = tokens.size();
if (i >= 2 && tokens.at(i).is(T_IDENTIFIER) && tokens.at(i - 1).is(T_LPAREN)
&& (tokens.at(i - 2).is(T_SIGNAL) || tokens.at(i - 2).is(T_SLOT))) {
@@ -572,7 +571,7 @@ void FollowSymbolUnderCursor::findLink(
// In this case we want to look at one token before the current position to recognize
// an operator if the cursor is inside the actual operator: operator[$]
- if (positionInBlock >= tk.utf16charsBegin() && positionInBlock <= tk.utf16charsEnd()) {
+ if (column >= tk.utf16charsBegin() && column <= tk.utf16charsEnd()) {
cursorRegionReached = true;
if (tk.is(T_OPERATOR)) {
link = attemptDeclDef(cursor, theSnapshot,
@@ -582,7 +581,7 @@ void FollowSymbolUnderCursor::findLink(
} else if (tk.isPunctuationOrOperator() && i > 0 && tokens.at(i - 1).is(T_OPERATOR)) {
QTextCursor c = cursor;
c.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor,
- positionInBlock - tokens.at(i - 1).utf16charsBegin());
+ column - tokens.at(i - 1).utf16charsBegin());
link = attemptDeclDef(c, theSnapshot, documentFromSemanticInfo, symbolFinder);
if (link.hasValidLinkText())
return processLinkCallback(link);
@@ -662,7 +661,7 @@ void FollowSymbolUnderCursor::findLink(
}
// Find the last symbol up to the cursor position
- Scope *scope = doc->scopeAt(line, positionInBlock);
+ Scope *scope = doc->scopeAt(line, column);
if (!scope)
return processLinkCallback(link);
@@ -684,7 +683,7 @@ void FollowSymbolUnderCursor::findLink(
if (Symbol *d = r.declaration()) {
if (d->asDeclaration() || d->asFunction()) {
if (data.filePath() == d->filePath()) {
- if (line == d->line() && positionInBlock >= d->column()) {
+ if (line == d->line() && column >= d->column()) {
// TODO: check the end
result = r; // take the symbol under cursor.
break;
@@ -697,7 +696,7 @@ void FollowSymbolUnderCursor::findLink(
&tokenBeginColumnNumber);
if (tokenBeginLineNumber > d->line()
|| (tokenBeginLineNumber == d->line()
- && tokenBeginColumnNumber >= d->column())) {
+ && tokenBeginColumnNumber + 1 >= d->column())) {
result = r; // take the symbol under cursor.
break;
}
diff --git a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp
index fa271108cf..b43e87758e 100644
--- a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp
+++ b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp
@@ -21,9 +21,9 @@
#include <cplusplus/Overview.h>
#include <cplusplus/TypeOfExpression.h>
+#include <utils/async.h>
#include <utils/proxyaction.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <utils/tooltip/tooltip.h>
#include <QRegularExpression>
@@ -232,7 +232,7 @@ void FunctionDeclDefLinkFinder::startFindLinkAt(
// handle the rest in a thread
m_watcher.reset(new QFutureWatcher<QSharedPointer<FunctionDeclDefLink> >());
connect(m_watcher.data(), &QFutureWatcherBase::finished, this, &FunctionDeclDefLinkFinder::onFutureDone);
- m_watcher->setFuture(Utils::runAsync(findLinkHelper, result, refactoringChanges));
+ m_watcher->setFuture(Utils::asyncRun(findLinkHelper, result, refactoringChanges));
}
bool FunctionDeclDefLink::isValid() const
diff --git a/src/plugins/cppeditor/cpphighlighter.cpp b/src/plugins/cppeditor/cpphighlighter.cpp
index 1f83b317ad..f5ef94efde 100644
--- a/src/plugins/cppeditor/cpphighlighter.cpp
+++ b/src/plugins/cppeditor/cpphighlighter.cpp
@@ -76,6 +76,7 @@ void CppHighlighter::highlightBlock(const QString &text)
setFormat(0, text.length(), formatForCategory(C_VISUAL_WHITESPACE));
}
TextDocumentLayout::setFoldingIndent(currentBlock(), foldingIndent);
+ TextDocumentLayout::setExpectedRawStringSuffix(currentBlock(), inheritedRawStringSuffix);
return;
}
@@ -161,10 +162,8 @@ void CppHighlighter::highlightBlock(const QString &text)
} else if (tk.is(T_NUMERIC_LITERAL)) {
setFormat(tk.utf16charsBegin(), tk.utf16chars(), formatForCategory(C_NUMBER));
} else if (tk.isStringLiteral() || tk.isCharLiteral()) {
- if (!highlightRawStringLiteral(text, tk, QString::fromUtf8(inheritedRawStringSuffix))) {
- setFormatWithSpaces(text, tk.utf16charsBegin(), tk.utf16chars(),
- formatForCategory(C_STRING));
- }
+ if (!highlightRawStringLiteral(text, tk, QString::fromUtf8(inheritedRawStringSuffix)))
+ highlightStringLiteral(text, tk);
} else if (tk.isComment()) {
const int startPosition = initialLexerState ? previousTokenEnd : tk.utf16charsBegin();
if (tk.is(T_COMMENT) || tk.is(T_CPP_COMMENT)) {
@@ -413,8 +412,18 @@ bool CppHighlighter::highlightRawStringLiteral(QStringView text, const Token &tk
stringOffset = delimiterOffset + delimiter.length() + 1;
stringLength -= delimiter.length() + 1;
}();
- if (text.mid(tk.utf16charsBegin(), tk.utf16chars()).endsWith(expectedSuffix)) {
- endDelimiterOffset = tk.utf16charsBegin() + tk.utf16chars() - expectedSuffix.size();
+ int operatorOffset = tk.utf16charsBegin() + tk.utf16chars();
+ int operatorLength = 0;
+ if (tk.f.userDefinedLiteral) {
+ const int closingQuoteOffset = text.lastIndexOf('"', operatorOffset);
+ QTC_ASSERT(closingQuoteOffset >= tk.utf16charsBegin(), return false);
+ operatorOffset = closingQuoteOffset + 1;
+ operatorLength = tk.utf16charsBegin() + tk.utf16chars() - operatorOffset;
+ stringLength -= operatorLength;
+ }
+ if (text.mid(tk.utf16charsBegin(), operatorOffset - tk.utf16charsBegin())
+ .endsWith(expectedSuffix)) {
+ endDelimiterOffset = operatorOffset - expectedSuffix.size();
stringLength -= expectedSuffix.size();
}
@@ -422,13 +431,52 @@ bool CppHighlighter::highlightRawStringLiteral(QStringView text, const Token &tk
// a string, and the rest (including the delimiter) as a keyword.
const QTextCharFormat delimiterFormat = formatForCategory(C_KEYWORD);
if (delimiterOffset != -1)
- setFormat(tk.utf16charsBegin(), stringOffset, delimiterFormat);
+ setFormat(tk.utf16charsBegin(), stringOffset - tk.utf16charsBegin(), delimiterFormat);
setFormatWithSpaces(text.toString(), stringOffset, stringLength, formatForCategory(C_STRING));
if (endDelimiterOffset != -1)
setFormat(endDelimiterOffset, expectedSuffix.size(), delimiterFormat);
+ if (operatorLength > 0)
+ setFormat(operatorOffset, operatorLength, formatForCategory(C_OPERATOR));
return true;
}
+void CppHighlighter::highlightStringLiteral(QStringView text, const CPlusPlus::Token &tk)
+{
+ switch (tk.kind()) {
+ case T_WIDE_STRING_LITERAL:
+ case T_UTF8_STRING_LITERAL:
+ case T_UTF16_STRING_LITERAL:
+ case T_UTF32_STRING_LITERAL:
+ break;
+ default:
+ if (!tk.userDefinedLiteral()) { // Simple case: No prefix, no suffix.
+ setFormatWithSpaces(text.toString(), tk.utf16charsBegin(), tk.utf16chars(),
+ formatForCategory(C_STRING));
+ return;
+ }
+ }
+
+ int stringOffset = 0;
+ if (!tk.f.joined) {
+ stringOffset = text.indexOf('"', tk.utf16charsBegin());
+ QTC_ASSERT(stringOffset > 0, return);
+ setFormat(tk.utf16charsBegin(), stringOffset - tk.utf16charsBegin(),
+ formatForCategory(C_KEYWORD));
+ }
+ int operatorOffset = tk.utf16charsBegin() + tk.utf16chars();
+ if (tk.userDefinedLiteral()) {
+ const int closingQuoteOffset = text.lastIndexOf('"', operatorOffset);
+ QTC_ASSERT(closingQuoteOffset >= tk.utf16charsBegin(), return);
+ operatorOffset = closingQuoteOffset + 1;
+ }
+ setFormatWithSpaces(text.toString(), stringOffset, operatorOffset - tk.utf16charsBegin(),
+ formatForCategory(C_STRING));
+ if (const int operatorLength = tk.utf16charsBegin() + tk.utf16chars() - operatorOffset;
+ operatorLength > 0) {
+ setFormat(operatorOffset, operatorLength, formatForCategory(C_OPERATOR));
+ }
+}
+
void CppHighlighter::highlightDoxygenComment(const QString &text, int position, int)
{
int initial = position;
@@ -513,6 +561,33 @@ void CppHighlighterTest::test_data()
QTest::newRow("struct keyword") << 25 << 1 << 25 << 6 << C_KEYWORD;
QTest::newRow("operator keyword") << 26 << 5 << 26 << 12 << C_KEYWORD;
QTest::newRow("type in conversion operator") << 26 << 14 << 26 << 16 << C_PRIMITIVE_TYPE;
+ QTest::newRow("concept keyword") << 29 << 22 << 29 << 28 << C_KEYWORD;
+ QTest::newRow("user-defined UTF-16 string literal (prefix)")
+ << 32 << 16 << 32 << 16 << C_KEYWORD;
+ QTest::newRow("user-defined UTF-16 string literal (content)")
+ << 32 << 17 << 32 << 21 << C_STRING;
+ QTest::newRow("user-defined UTF-16 string literal (suffix)")
+ << 32 << 22 << 32 << 23 << C_OPERATOR;
+ QTest::newRow("wide string literal (prefix)") << 33 << 17 << 33 << 17 << C_KEYWORD;
+ QTest::newRow("wide string literal (content)") << 33 << 18 << 33 << 24 << C_STRING;
+ QTest::newRow("UTF-8 string literal (prefix)") << 34 << 17 << 34 << 18 << C_KEYWORD;
+ QTest::newRow("UTF-8 string literal (content)") << 34 << 19 << 34 << 24 << C_STRING;
+ QTest::newRow("UTF-32 string literal (prefix)") << 35 << 17 << 35 << 17 << C_KEYWORD;
+ QTest::newRow("UTF-8 string literal (content)") << 35 << 18 << 35 << 23 << C_STRING;
+ QTest::newRow("user-defined UTF-16 raw string literal (prefix)")
+ << 36 << 17 << 36 << 20 << C_KEYWORD;
+ QTest::newRow("user-defined UTF-16 raw string literal (content)")
+ << 36 << 38 << 37 << 8 << C_STRING;
+ QTest::newRow("user-defined UTF-16 raw string literal (suffix 1)")
+ << 37 << 9 << 37 << 10 << C_KEYWORD;
+ QTest::newRow("user-defined UTF-16 raw string literal (suffix 2)")
+ << 37 << 11 << 37 << 12 << C_OPERATOR;
+ QTest::newRow("multi-line user-defined UTF-16 string literal (prefix)")
+ << 38 << 17 << 38 << 17 << C_KEYWORD;
+ QTest::newRow("multi-line user-defined UTF-16 string literal (content)")
+ << 38 << 18 << 39 << 3 << C_STRING;
+ QTest::newRow("multi-line user-defined UTF-16 string literal (suffix)")
+ << 39 << 4 << 39 << 5 << C_OPERATOR;
}
void CppHighlighterTest::test()
diff --git a/src/plugins/cppeditor/cpphighlighter.h b/src/plugins/cppeditor/cpphighlighter.h
index 358fcb2760..1728ffeb77 100644
--- a/src/plugins/cppeditor/cpphighlighter.h
+++ b/src/plugins/cppeditor/cpphighlighter.h
@@ -29,6 +29,7 @@ private:
void highlightWord(QStringView word, int position, int length);
bool highlightRawStringLiteral(QStringView text, const CPlusPlus::Token &tk,
const QString &inheritedSuffix);
+ void highlightStringLiteral(QStringView text, const CPlusPlus::Token &tk);
void highlightDoxygenComment(const QString &text, int position,
int length);
diff --git a/src/plugins/cppeditor/cppincludehierarchy.cpp b/src/plugins/cppeditor/cppincludehierarchy.cpp
index c2a9ead6b7..caad2e0937 100644
--- a/src/plugins/cppeditor/cppincludehierarchy.cpp
+++ b/src/plugins/cppeditor/cppincludehierarchy.cpp
@@ -27,6 +27,7 @@
#include <utils/navigationtreeview.h>
#include <utils/qtcassert.h>
#include <utils/qtcsettings.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QCoreApplication>
@@ -382,6 +383,7 @@ CppIncludeHierarchyWidget::CppIncludeHierarchyWidget()
this, &CppIncludeHierarchyWidget::perform);
m_toggleSync = new QToolButton(this);
+ StyleHelper::setPanelWidget(m_toggleSync);
m_toggleSync->setIcon(Utils::Icons::LINK_TOOLBAR.icon());
m_toggleSync->setCheckable(true);
m_toggleSync->setToolTip(Tr::tr("Synchronize with Editor"));
diff --git a/src/plugins/cppeditor/cppincludesfilter.cpp b/src/plugins/cppeditor/cppincludesfilter.cpp
index 9f637f6582..9711b6f52d 100644
--- a/src/plugins/cppeditor/cppincludesfilter.cpp
+++ b/src/plugins/cppeditor/cppincludesfilter.cpp
@@ -7,11 +7,11 @@
#include "cppeditortr.h"
#include "cppmodelmanager.h"
-#include <cplusplus/CppDocument.h>
#include <coreplugin/editormanager/documentmodel.h>
+
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
using namespace Core;
using namespace ProjectExplorer;
@@ -19,79 +19,33 @@ using namespace Utils;
namespace CppEditor::Internal {
-class CppIncludesIterator final : public BaseFileFilter::Iterator
-{
-public:
- CppIncludesIterator(CPlusPlus::Snapshot snapshot, const QSet<FilePath> &seedPaths)
- : m_snapshot(snapshot),
- m_paths(seedPaths)
- {
- toFront();
- }
-
- void toFront() override;
- bool hasNext() const override;
- Utils::FilePath next() override;
- Utils::FilePath filePath() const override;
-
-private:
- void fetchMore();
-
- CPlusPlus::Snapshot m_snapshot;
- QSet<FilePath> m_paths;
- QSet<FilePath> m_queuedPaths;
- QSet<FilePath> m_allResultPaths;
- FilePaths m_resultQueue;
- FilePath m_currentPath;
-};
-
-
-
-void CppIncludesIterator::toFront()
-{
- m_queuedPaths = m_paths;
- m_allResultPaths.clear();
- m_resultQueue.clear();
- fetchMore();
-}
-
-bool CppIncludesIterator::hasNext() const
-{
- return !m_resultQueue.isEmpty();
-}
-
-FilePath CppIncludesIterator::next()
+static FilePaths generateFilePaths(const QFuture<void> &future,
+ const CPlusPlus::Snapshot &snapshot,
+ const std::unordered_set<FilePath> &inputFilePaths)
{
- if (m_resultQueue.isEmpty())
- return {};
- m_currentPath = m_resultQueue.takeFirst();
- if (m_resultQueue.isEmpty())
- fetchMore();
- return m_currentPath;
-}
-
-FilePath CppIncludesIterator::filePath() const
-{
- return m_currentPath;
-}
-
-void CppIncludesIterator::fetchMore()
-{
- while (!m_queuedPaths.isEmpty() && m_resultQueue.isEmpty()) {
- const FilePath filePath = *m_queuedPaths.begin();
- m_queuedPaths.remove(filePath);
- CPlusPlus::Document::Ptr doc = m_snapshot.document(filePath);
+ FilePaths results;
+ std::unordered_set<FilePath> resultsCache;
+ std::unordered_set<FilePath> queuedPaths = inputFilePaths;
+
+ while (!queuedPaths.empty()) {
+ if (future.isCanceled())
+ return {};
+
+ const auto iterator = queuedPaths.cbegin();
+ const FilePath filePath = *iterator;
+ queuedPaths.erase(iterator);
+ const CPlusPlus::Document::Ptr doc = snapshot.document(filePath);
if (!doc)
continue;
const FilePaths includedFiles = doc->includedFiles();
- for (const FilePath &includedPath : includedFiles ) {
- if (!m_allResultPaths.contains(includedPath)) {
- m_allResultPaths.insert(includedPath);
- m_queuedPaths.insert(includedPath);
- m_resultQueue.append(includedPath);
+ for (const FilePath &includedFile : includedFiles) {
+ if (resultsCache.emplace(includedFile).second) {
+ queuedPaths.emplace(includedFile);
+ results.append(includedFile);
}
}
}
+ return results;
}
CppIncludesFilter::CppIncludesFilter()
@@ -99,61 +53,50 @@ CppIncludesFilter::CppIncludesFilter()
setId(Constants::INCLUDES_FILTER_ID);
setDisplayName(Tr::tr(Constants::INCLUDES_FILTER_DISPLAY_NAME));
setDescription(
- Tr::tr("Matches all files that are included by all C++ files in all projects. Append "
+ Tr::tr("Locates files that are included by C++ files of any open project. Append "
"\"+<number>\" or \":<number>\" to jump to the given line number. Append another "
"\"+<number>\" or \":<number>\" to jump to the column number as well."));
setDefaultShortcutString("ai");
setDefaultIncludedByDefault(true);
+ const auto invalidate = [this] { m_cache.invalidate(); };
+ setRefreshRecipe(Tasking::Sync([invalidate] { invalidate(); return true; }));
setPriority(ILocatorFilter::Low);
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::fileListChanged,
- this, &CppIncludesFilter::markOutdated);
+ this, invalidate);
connect(CppModelManager::instance(), &CppModelManager::documentUpdated,
- this, &CppIncludesFilter::markOutdated);
+ this, invalidate);
connect(CppModelManager::instance(), &CppModelManager::aboutToRemoveFiles,
- this, &CppIncludesFilter::markOutdated);
+ this, invalidate);
connect(DocumentModel::model(), &QAbstractItemModel::rowsInserted,
- this, &CppIncludesFilter::markOutdated);
+ this, invalidate);
connect(DocumentModel::model(), &QAbstractItemModel::rowsRemoved,
- this, &CppIncludesFilter::markOutdated);
+ this, invalidate);
connect(DocumentModel::model(), &QAbstractItemModel::dataChanged,
- this, &CppIncludesFilter::markOutdated);
+ this, invalidate);
connect(DocumentModel::model(), &QAbstractItemModel::modelReset,
- this, &CppIncludesFilter::markOutdated);
-}
+ this, invalidate);
-void CppIncludesFilter::prepareSearch(const QString &entry)
-{
- Q_UNUSED(entry)
- if (m_needsUpdate) {
- m_needsUpdate = false;
- QSet<FilePath> seedPaths;
- for (Project *project : SessionManager::projects()) {
+ const auto generatorProvider = [] {
+ // This body runs in main thread
+ std::unordered_set<FilePath> inputFilePaths;
+ for (Project *project : ProjectManager::projects()) {
const FilePaths allFiles = project->files(Project::SourceFiles);
- for (const FilePath &filePath : allFiles )
- seedPaths.insert(filePath);
+ for (const FilePath &filePath : allFiles)
+ inputFilePaths.insert(filePath);
}
const QList<DocumentModel::Entry *> entries = DocumentModel::entries();
for (DocumentModel::Entry *entry : entries) {
if (entry)
- seedPaths.insert(entry->filePath());
+ inputFilePaths.insert(entry->filePath());
}
- CPlusPlus::Snapshot snapshot = CppModelManager::instance()->snapshot();
- setFileIterator(new CppIncludesIterator(snapshot, seedPaths));
- }
- BaseFileFilter::prepareSearch(entry);
-}
-
-void CppIncludesFilter::refresh(QFutureInterface<void> &future)
-{
- Q_UNUSED(future)
- QMetaObject::invokeMethod(this, &CppIncludesFilter::markOutdated, Qt::QueuedConnection);
-}
-
-void CppIncludesFilter::markOutdated()
-{
- m_needsUpdate = true;
- setFileIterator(nullptr); // clean up
+ const CPlusPlus::Snapshot snapshot = CppModelManager::instance()->snapshot();
+ return [snapshot, inputFilePaths](const QFuture<void> &future) {
+ // This body runs in non-main thread
+ return generateFilePaths(future, snapshot, inputFilePaths);
+ };
+ };
+ m_cache.setGeneratorProvider(generatorProvider);
}
} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/cppincludesfilter.h b/src/plugins/cppeditor/cppincludesfilter.h
index 6ae5dbb91c..4ab5ffba97 100644
--- a/src/plugins/cppeditor/cppincludesfilter.h
+++ b/src/plugins/cppeditor/cppincludesfilter.h
@@ -3,24 +3,18 @@
#pragma once
-#include <coreplugin/locator/basefilefilter.h>
+#include <coreplugin/locator/ilocatorfilter.h>
namespace CppEditor::Internal {
-class CppIncludesFilter : public Core::BaseFileFilter
+class CppIncludesFilter : public Core::ILocatorFilter
{
public:
CppIncludesFilter();
- // ILocatorFilter interface
-public:
- void prepareSearch(const QString &entry) override;
- void refresh(QFutureInterface<void> &future) override;
-
private:
- void markOutdated();
-
- bool m_needsUpdate = true;
+ Core::LocatorMatcherTasks matchers() final { return {m_cache.matcher()}; }
+ Core::LocatorFileCache m_cache;
};
} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/cppindexingsupport.cpp b/src/plugins/cppeditor/cppindexingsupport.cpp
index 2d0e2b5519..22ff93cdaa 100644
--- a/src/plugins/cppeditor/cppindexingsupport.cpp
+++ b/src/plugins/cppeditor/cppindexingsupport.cpp
@@ -10,13 +10,13 @@
#include "cppsourceprocessor.h"
#include "searchsymbols.h"
-#include <coreplugin/find/searchresultitem.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <cplusplus/LookupContext.h>
+#include <utils/async.h>
#include <utils/filepath.h>
-#include <utils/runextensions.h>
+#include <utils/searchresultitem.h>
#include <utils/stringutils.h>
#include <utils/temporarydirectory.h>
@@ -54,7 +54,7 @@ class WriteTaskFileForDiagnostics
public:
WriteTaskFileForDiagnostics()
{
- const QString fileName = Utils::TemporaryDirectory::masterDirectoryPath()
+ const QString fileName = TemporaryDirectory::masterDirectoryPath()
+ "/qtc_findErrorsIndexing.diagnostics."
+ QDateTime::currentDateTime().toString("yyMMdd_HHmm") + ".tasks";
@@ -116,8 +116,7 @@ void classifyFiles(const QSet<QString> &files, QStringList *headers, QStringList
}
}
-void indexFindErrors(QFutureInterface<void> &indexingFuture,
- const ParseParams params)
+void indexFindErrors(QPromise<void> &promise, const ParseParams params)
{
QStringList sources, headers;
classifyFiles(params.sourceFiles, &headers, &sources);
@@ -130,7 +129,7 @@ void indexFindErrors(QFutureInterface<void> &indexingFuture,
timer.start();
for (int i = 0, end = files.size(); i < end ; ++i) {
- if (indexingFuture.isCanceled())
+ if (promise.isCanceled())
break;
const QString file = files.at(i);
@@ -140,7 +139,7 @@ void indexFindErrors(QFutureInterface<void> &indexingFuture,
BuiltinEditorDocumentParser parser(FilePath::fromString(file));
parser.setReleaseSourceAndAST(false);
parser.update({CppModelManager::instance()->workingCopy(), nullptr,
- Utils::Language::Cxx, false});
+ Language::Cxx, false});
CPlusPlus::Document::Ptr document = parser.document();
QTC_ASSERT(document, return);
@@ -153,15 +152,14 @@ void indexFindErrors(QFutureInterface<void> &indexingFuture,
document->releaseSourceAndAST();
- indexingFuture.setProgressValue(i + 1);
+ promise.setProgressValue(i + 1);
}
const QString elapsedTime = Utils::formatElapsedTime(timer.elapsed());
qDebug("FindErrorsIndexing: %s", qPrintable(elapsedTime));
}
-void index(QFutureInterface<void> &indexingFuture,
- const ParseParams params)
+void index(QPromise<void> &promise, const ParseParams params)
{
QScopedPointer<Internal::CppSourceProcessor> sourceProcessor(CppModelManager::createSourceProcessor());
sourceProcessor->setFileSizeLimitInMb(params.indexerFileSizeLimitInMb);
@@ -190,7 +188,7 @@ void index(QFutureInterface<void> &indexingFuture,
qCDebug(indexerLog) << "About to index" << files.size() << "files.";
for (int i = 0; i < files.size(); ++i) {
- if (indexingFuture.isCanceled())
+ if (promise.isCanceled())
break;
const QString fileName = files.at(i);
@@ -216,7 +214,7 @@ void index(QFutureInterface<void> &indexingFuture,
sourceProcessor->setHeaderPaths(headerPaths);
sourceProcessor->run(FilePath::fromString(fileName));
- indexingFuture.setProgressValue(files.size() - sourceProcessor->todo().size());
+ promise.setProgressValue(files.size() - sourceProcessor->todo().size());
if (isSourceFile)
sourceProcessor->resetEnvironment();
@@ -224,29 +222,29 @@ void index(QFutureInterface<void> &indexingFuture,
qCDebug(indexerLog) << "Indexing finished.";
}
-void parse(QFutureInterface<void> &indexingFuture, const ParseParams params)
+void parse(QPromise<void> &promise, const ParseParams params)
{
const QSet<QString> &files = params.sourceFiles;
if (files.isEmpty())
return;
- indexingFuture.setProgressRange(0, files.size());
+ promise.setProgressRange(0, files.size());
if (CppIndexingSupport::isFindErrorsIndexingActive())
- indexFindErrors(indexingFuture, params);
+ indexFindErrors(promise, params);
else
- index(indexingFuture, params);
+ index(promise, params);
- indexingFuture.setProgressValue(files.size());
+ promise.setProgressValue(files.size());
CppModelManager::instance()->finishedRefreshingSourceFiles(files);
}
} // anonymous namespace
-void SymbolSearcher::runSearch(QFutureInterface<Core::SearchResultItem> &future)
+void SymbolSearcher::runSearch(QPromise<SearchResultItem> &promise)
{
- future.setProgressRange(0, m_snapshot.size());
- future.setProgressValue(0);
+ promise.setProgressRange(0, m_snapshot.size());
+ promise.setProgressValue(0);
int progress = 0;
SearchSymbols search;
@@ -262,12 +260,11 @@ void SymbolSearcher::runSearch(QFutureInterface<Core::SearchResultItem> &future)
: QRegularExpression::CaseInsensitiveOption));
matcher.optimize();
while (it != m_snapshot.end()) {
- if (future.isPaused())
- future.waitForResume();
- if (future.isCanceled())
+ promise.suspendIfRequested();
+ if (promise.isCanceled())
break;
if (m_fileNames.isEmpty() || m_fileNames.contains(it.value()->filePath().path())) {
- QVector<Core::SearchResultItem> resultItems;
+ SearchResultItems resultItems;
auto filter = [&](const IndexItem::Ptr &info) -> IndexItem::VisitorResult {
if (matcher.match(info->symbolName()).hasMatch()) {
QString text = info->symbolName();
@@ -280,7 +277,7 @@ void SymbolSearcher::runSearch(QFutureInterface<Core::SearchResultItem> &future)
text = info->representDeclaration();
}
- Core::SearchResultItem item;
+ SearchResultItem item;
item.setPath(scope.split(QLatin1String("::"), Qt::SkipEmptyParts));
item.setLineText(text);
item.setIcon(info->icon());
@@ -291,24 +288,16 @@ void SymbolSearcher::runSearch(QFutureInterface<Core::SearchResultItem> &future)
return IndexItem::Recurse;
};
search(it.value())->visitAllChildren(filter);
- if (!resultItems.isEmpty())
- future.reportResults(resultItems);
+ for (const SearchResultItem &item : std::as_const(resultItems))
+ promise.addResult(item);
}
++it;
++progress;
- future.setProgressValue(progress);
+ promise.setProgressValue(progress);
}
- if (future.isPaused())
- future.waitForResume();
+ promise.suspendIfRequested();
}
-CppIndexingSupport::CppIndexingSupport()
-{
- m_synchronizer.setCancelOnWait(true);
-}
-
-CppIndexingSupport::~CppIndexingSupport() = default;
-
bool CppIndexingSupport::isFindErrorsIndexingActive()
{
return Utils::qtcEnvironmentVariable("QTC_FIND_ERRORS_INDEXING") == "1";
@@ -325,7 +314,7 @@ QFuture<void> CppIndexingSupport::refreshSourceFiles(const QSet<QString> &source
params.workingCopy = mgr->workingCopy();
params.sourceFiles = sourceFiles;
- QFuture<void> result = Utils::runAsync(mgr->sharedThreadPool(), parse, params);
+ QFuture<void> result = Utils::asyncRun(mgr->sharedThreadPool(), parse, params);
m_synchronizer.addFuture(result);
if (mode == CppModelManager::ForcedProgressNotification || sourceFiles.count() > 1) {
diff --git a/src/plugins/cppeditor/cppindexingsupport.h b/src/plugins/cppeditor/cppindexingsupport.h
index 584632a4f0..b5aa68585d 100644
--- a/src/plugins/cppeditor/cppindexingsupport.h
+++ b/src/plugins/cppeditor/cppindexingsupport.h
@@ -11,7 +11,7 @@
#include <QFuture>
-namespace Core { class SearchResultItem; }
+namespace Utils { class SearchResultItem; }
namespace CppEditor {
@@ -44,7 +44,7 @@ public:
};
SymbolSearcher(const SymbolSearcher::Parameters &parameters, const QSet<QString> &fileNames);
- void runSearch(QFutureInterface<Core::SearchResultItem> &future);
+ void runSearch(QPromise<Utils::SearchResultItem> &promise);
private:
const CPlusPlus::Snapshot m_snapshot;
@@ -55,9 +55,6 @@ private:
class CPPEDITOR_EXPORT CppIndexingSupport
{
public:
- CppIndexingSupport();
- ~CppIndexingSupport();
-
static bool isFindErrorsIndexingActive();
QFuture<void> refreshSourceFiles(const QSet<QString> &sourceFiles,
diff --git a/src/plugins/cppeditor/cpplocatordata.cpp b/src/plugins/cppeditor/cpplocatordata.cpp
index 9b260ccc2e..a0899b0ddd 100644
--- a/src/plugins/cppeditor/cpplocatordata.cpp
+++ b/src/plugins/cppeditor/cpplocatordata.cpp
@@ -20,6 +20,22 @@ CppLocatorData::CppLocatorData()
m_pendingDocuments.reserve(MaxPendingDocuments);
}
+QList<IndexItem::Ptr> CppLocatorData::findSymbols(IndexItem::ItemType type,
+ const QString &symbolName) const
+{
+ QList<IndexItem::Ptr> matches;
+ filterAllFiles([&](const IndexItem::Ptr &info) {
+ if (info->type() & type) {
+ if (info->symbolName() == symbolName || info->scopedSymbolName() == symbolName)
+ matches << info;
+ }
+ if (info->type() & IndexItem::Enum)
+ return IndexItem::Continue;
+ return IndexItem::Recurse;
+ });
+ return matches;
+}
+
void CppLocatorData::onDocumentUpdated(const CPlusPlus::Document::Ptr &document)
{
QMutexLocker locker(&m_pendingDocumentsMutex);
diff --git a/src/plugins/cppeditor/cpplocatordata.h b/src/plugins/cppeditor/cpplocatordata.h
index 2170deb75d..d981eb7c04 100644
--- a/src/plugins/cppeditor/cpplocatordata.h
+++ b/src/plugins/cppeditor/cpplocatordata.h
@@ -33,6 +33,8 @@ public:
return;
}
+ QList<IndexItem::Ptr> findSymbols(IndexItem::ItemType type, const QString &symbolName) const;
+
public slots:
void onDocumentUpdated(const CPlusPlus::Document::Ptr &document);
void onAboutToRemoveFiles(const QStringList &files);
diff --git a/src/plugins/cppeditor/cpplocatorfilter.cpp b/src/plugins/cppeditor/cpplocatorfilter.cpp
index 083c5df1b2..cd1add7786 100644
--- a/src/plugins/cppeditor/cpplocatorfilter.cpp
+++ b/src/plugins/cppeditor/cpplocatorfilter.cpp
@@ -4,60 +4,49 @@
#include "cpplocatorfilter.h"
#include "cppeditorconstants.h"
+#include "cppeditorplugin.h"
#include "cppeditortr.h"
+#include "cpplocatordata.h"
+#include "cppmodelmanager.h"
#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/editormanager/ieditor.h>
+
+#include <extensionsystem/pluginmanager.h>
+
#include <utils/algorithm.h>
+#include <utils/async.h>
+#include <utils/fuzzymatcher.h>
#include <QRegularExpression>
-#include <algorithm>
-#include <numeric>
+using namespace Core;
+using namespace CPlusPlus;
+using namespace Utils;
namespace CppEditor {
-CppLocatorFilter::CppLocatorFilter(CppLocatorData *locatorData)
- : m_data(locatorData)
-{
- setId(Constants::LOCATOR_FILTER_ID);
- setDisplayName(Tr::tr(Constants::LOCATOR_FILTER_DISPLAY_NAME));
- setDefaultShortcutString(":");
- setDefaultIncludedByDefault(false);
-}
+using EntryFromIndex = std::function<LocatorFilterEntry(const IndexItem::Ptr &)>;
-CppLocatorFilter::~CppLocatorFilter() = default;
-
-Core::LocatorFilterEntry CppLocatorFilter::filterEntryFromIndexItem(IndexItem::Ptr info)
-{
- const QVariant id = QVariant::fromValue(info);
- Core::LocatorFilterEntry filterEntry(this, info->scopedSymbolName(), id, info->icon());
- if (info->type() == IndexItem::Class || info->type() == IndexItem::Enum)
- filterEntry.extraInfo = info->shortNativeFilePath();
- else
- filterEntry.extraInfo = info->symbolType();
-
- return filterEntry;
-}
-
-QList<Core::LocatorFilterEntry> CppLocatorFilter::matchesFor(
- QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
+void matchesFor(QPromise<void> &promise, const LocatorStorage &storage,
+ IndexItem::ItemType wantedType, const EntryFromIndex &converter)
{
- QList<Core::LocatorFilterEntry> entries[int(MatchLevel::Count)];
- const Qt::CaseSensitivity caseSensitivityForPrefix = caseSensitivity(entry);
- const IndexItem::ItemType wanted = matchTypes();
-
- const QRegularExpression regexp = createRegExp(entry);
+ const QString input = storage.input();
+ LocatorFilterEntries entries[int(ILocatorFilter::MatchLevel::Count)];
+ const Qt::CaseSensitivity caseSensitivityForPrefix = ILocatorFilter::caseSensitivity(input);
+ const QRegularExpression regexp = ILocatorFilter::createRegExp(input);
if (!regexp.isValid())
- return {};
- const bool hasColonColon = entry.contains("::");
- const QRegularExpression shortRegexp =
- hasColonColon ? createRegExp(entry.mid(entry.lastIndexOf("::") + 2)) : regexp;
+ return;
- m_data->filterAllFiles([&](const IndexItem::Ptr &info) -> IndexItem::VisitorResult {
- if (future.isCanceled())
+ const bool hasColonColon = input.contains("::");
+ const QRegularExpression shortRegexp = hasColonColon
+ ? ILocatorFilter::createRegExp(input.mid(input.lastIndexOf("::") + 2)) : regexp;
+ CppLocatorData *locatorData = CppModelManager::instance()->locatorData();
+ locatorData->filterAllFiles([&](const IndexItem::Ptr &info) {
+ if (promise.isCanceled())
return IndexItem::Break;
const IndexItem::ItemType type = info->type();
- if (type & wanted) {
+ if (type & wantedType) {
const QString symbolName = info->symbolName();
QString matchString = hasColonColon ? info->scopedSymbolName() : symbolName;
int matchOffset = hasColonColon ? matchString.size() - symbolName.size() : 0;
@@ -70,7 +59,7 @@ QList<Core::LocatorFilterEntry> CppLocatorFilter::matchesFor(
}
if (match.hasMatch()) {
- Core::LocatorFilterEntry filterEntry = filterEntryFromIndexItem(info);
+ LocatorFilterEntry filterEntry = converter(info);
// Highlight the matched characters, therefore it may be necessary
// to update the match if the displayName is different from matchString
@@ -78,103 +67,323 @@ QList<Core::LocatorFilterEntry> CppLocatorFilter::matchesFor(
match = shortRegexp.match(filterEntry.displayName);
matchOffset = 0;
}
- filterEntry.highlightInfo = highlightInfo(match);
+ filterEntry.highlightInfo = ILocatorFilter::highlightInfo(match);
if (matchInParameterList && filterEntry.highlightInfo.startsDisplay.isEmpty()) {
match = regexp.match(filterEntry.extraInfo);
- filterEntry.highlightInfo
- = highlightInfo(match, Core::LocatorFilterEntry::HighlightInfo::ExtraInfo);
+ filterEntry.highlightInfo = ILocatorFilter::highlightInfo(
+ match, LocatorFilterEntry::HighlightInfo::ExtraInfo);
} else if (matchOffset > 0) {
for (int &start : filterEntry.highlightInfo.startsDisplay)
start -= matchOffset;
}
if (matchInParameterList)
- entries[int(MatchLevel::Normal)].append(filterEntry);
- else if (filterEntry.displayName.startsWith(entry, caseSensitivityForPrefix))
- entries[int(MatchLevel::Best)].append(filterEntry);
- else if (filterEntry.displayName.contains(entry, caseSensitivityForPrefix))
- entries[int(MatchLevel::Better)].append(filterEntry);
+ entries[int(ILocatorFilter::MatchLevel::Normal)].append(filterEntry);
+ else if (filterEntry.displayName.startsWith(input, caseSensitivityForPrefix))
+ entries[int(ILocatorFilter::MatchLevel::Best)].append(filterEntry);
+ else if (filterEntry.displayName.contains(input, caseSensitivityForPrefix))
+ entries[int(ILocatorFilter::MatchLevel::Better)].append(filterEntry);
else
- entries[int(MatchLevel::Good)].append(filterEntry);
+ entries[int(ILocatorFilter::MatchLevel::Good)].append(filterEntry);
}
}
if (info->type() & IndexItem::Enum)
return IndexItem::Continue;
- else
- return IndexItem::Recurse;
+ return IndexItem::Recurse;
});
for (auto &entry : entries) {
if (entry.size() < 1000)
- Utils::sort(entry, Core::LocatorFilterEntry::compareLexigraphically);
+ Utils::sort(entry, LocatorFilterEntry::compareLexigraphically);
}
- return std::accumulate(std::begin(entries), std::end(entries), QList<Core::LocatorFilterEntry>());
+ storage.reportOutput(std::accumulate(std::begin(entries), std::end(entries),
+ LocatorFilterEntries()));
+}
+
+LocatorMatcherTask locatorMatcher(IndexItem::ItemType type, const EntryFromIndex &converter)
+{
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [=](Async<void> &async) {
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(matchesFor, *storage, type, converter);
+ };
+ return {AsyncTask<void>(onSetup), storage};
+}
+
+LocatorMatcherTask allSymbolsMatcher()
+{
+ const auto converter = [](const IndexItem::Ptr &info) {
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = info->scopedSymbolName();
+ filterEntry.displayIcon = info->icon();
+ filterEntry.linkForEditor = {info->filePath(), info->line(), info->column()};
+ if (info->type() == IndexItem::Class || info->type() == IndexItem::Enum)
+ filterEntry.extraInfo = info->shortNativeFilePath();
+ else
+ filterEntry.extraInfo = info->symbolType();
+ return filterEntry;
+ };
+ return locatorMatcher(IndexItem::All, converter);
}
-void CppLocatorFilter::accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const
+LocatorMatcherTask classMatcher()
{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- IndexItem::Ptr info = qvariant_cast<IndexItem::Ptr>(selection.internalData);
- Core::EditorManager::openEditorAt({info->filePath(), info->line(), info->column()},
- {},
- Core::EditorManager::AllowExternalEditor);
+ const auto converter = [](const IndexItem::Ptr &info) {
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = info->symbolName();
+ filterEntry.displayIcon = info->icon();
+ filterEntry.linkForEditor = {info->filePath(), info->line(), info->column()};
+ filterEntry.extraInfo = info->symbolScope().isEmpty()
+ ? info->shortNativeFilePath()
+ : info->symbolScope();
+ filterEntry.filePath = info->filePath();
+ return filterEntry;
+ };
+ return locatorMatcher(IndexItem::Class, converter);
}
-CppClassesFilter::CppClassesFilter(CppLocatorData *locatorData)
- : CppLocatorFilter(locatorData)
+LocatorMatcherTask functionMatcher()
+{
+ const auto converter = [](const IndexItem::Ptr &info) {
+ QString name = info->symbolName();
+ QString extraInfo = info->symbolScope();
+ info->unqualifiedNameAndScope(name, &name, &extraInfo);
+ if (extraInfo.isEmpty())
+ extraInfo = info->shortNativeFilePath();
+ else
+ extraInfo.append(" (" + info->filePath().fileName() + ')');
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = name + info->symbolType();
+ filterEntry.displayIcon = info->icon();
+ filterEntry.linkForEditor = {info->filePath(), info->line(), info->column()};
+ filterEntry.extraInfo = extraInfo;
+
+ return filterEntry;
+ };
+ return locatorMatcher(IndexItem::Function, converter);
+}
+
+QList<IndexItem::Ptr> itemsOfCurrentDocument(const FilePath &currentFileName)
+{
+ if (currentFileName.isEmpty())
+ return {};
+
+ QList<IndexItem::Ptr> results;
+ const Snapshot snapshot = CppModelManager::instance()->snapshot();
+ if (const Document::Ptr thisDocument = snapshot.document(currentFileName)) {
+ SearchSymbols search;
+ search.setSymbolsToSearchFor(SymbolSearcher::Declarations |
+ SymbolSearcher::Enums |
+ SymbolSearcher::Functions |
+ SymbolSearcher::Classes);
+ IndexItem::Ptr rootNode = search(thisDocument);
+ rootNode->visitAllChildren([&](const IndexItem::Ptr &info) {
+ results.append(info);
+ return IndexItem::Recurse;
+ });
+ }
+ return results;
+}
+
+LocatorFilterEntry::HighlightInfo highlightInfo(const QRegularExpressionMatch &match,
+ LocatorFilterEntry::HighlightInfo::DataType dataType)
+{
+ const FuzzyMatcher::HighlightingPositions positions =
+ FuzzyMatcher::highlightingPositions(match);
+
+ return LocatorFilterEntry::HighlightInfo(positions.starts, positions.lengths, dataType);
+}
+
+void matchesForCurrentDocument(QPromise<void> &promise, const LocatorStorage &storage,
+ const FilePath &currentFileName)
+{
+ const QString input = storage.input();
+ const QRegularExpression regexp = ILocatorFilter::createRegExp(input);
+ if (!regexp.isValid())
+ return;
+
+ struct Entry
+ {
+ LocatorFilterEntry entry;
+ IndexItem::Ptr info;
+ };
+ QList<Entry> goodEntries;
+ QList<Entry> betterEntries;
+ const QList<IndexItem::Ptr> items = itemsOfCurrentDocument(currentFileName);
+ for (const IndexItem::Ptr &info : items) {
+ if (promise.isCanceled())
+ break;
+
+ QString matchString = info->symbolName();
+ if (info->type() == IndexItem::Declaration)
+ matchString = info->representDeclaration();
+ else if (info->type() == IndexItem::Function)
+ matchString += info->symbolType();
+
+ QRegularExpressionMatch match = regexp.match(matchString);
+ if (match.hasMatch()) {
+ const bool betterMatch = match.capturedStart() == 0;
+ QString name = matchString;
+ QString extraInfo = info->symbolScope();
+ if (info->type() == IndexItem::Function) {
+ if (info->unqualifiedNameAndScope(matchString, &name, &extraInfo)) {
+ name += info->symbolType();
+ match = regexp.match(name);
+ }
+ }
+
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = name;
+ filterEntry.displayIcon = info->icon();
+ filterEntry.linkForEditor = {info->filePath(), info->line(), info->column()};
+ filterEntry.extraInfo = extraInfo;
+ if (match.hasMatch()) {
+ filterEntry.highlightInfo = highlightInfo(match,
+ LocatorFilterEntry::HighlightInfo::DisplayName);
+ } else {
+ match = regexp.match(extraInfo);
+ filterEntry.highlightInfo =
+ highlightInfo(match, LocatorFilterEntry::HighlightInfo::ExtraInfo);
+ }
+
+ if (betterMatch)
+ betterEntries.append({filterEntry, info});
+ else
+ goodEntries.append({filterEntry, info});
+ }
+ }
+
+ // entries are unsorted by design!
+ betterEntries += goodEntries;
+
+ QHash<QString, QList<Entry>> possibleDuplicates;
+ for (const Entry &e : std::as_const(betterEntries))
+ possibleDuplicates[e.info->scopedSymbolName() + e.info->symbolType()] << e;
+ for (auto it = possibleDuplicates.cbegin(); it != possibleDuplicates.cend(); ++it) {
+ const QList<Entry> &duplicates = it.value();
+ if (duplicates.size() == 1)
+ continue;
+ QList<Entry> declarations;
+ QList<Entry> definitions;
+ for (const Entry &candidate : duplicates) {
+ const IndexItem::Ptr info = candidate.info;
+ if (info->type() != IndexItem::Function)
+ break;
+ if (info->isFunctionDefinition())
+ definitions << candidate;
+ else
+ declarations << candidate;
+ }
+ if (definitions.size() == 1
+ && declarations.size() + definitions.size() == duplicates.size()) {
+ for (const Entry &decl : std::as_const(declarations)) {
+ Utils::erase(betterEntries, [&decl](const Entry &e) {
+ return e.info == decl.info;
+ });
+ }
+ }
+ }
+ storage.reportOutput(Utils::transform(betterEntries,
+ [](const Entry &entry) { return entry.entry; }));
+}
+
+FilePath currentFileName()
+{
+ IEditor *currentEditor = EditorManager::currentEditor();
+ return currentEditor ? currentEditor->document()->filePath() : FilePath();
+}
+
+LocatorMatcherTask currentDocumentMatcher()
+{
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [=](Async<void> &async) {
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(matchesForCurrentDocument, *storage, currentFileName());
+ };
+ return {AsyncTask<void>(onSetup), storage};
+}
+
+using MatcherCreator = std::function<Core::LocatorMatcherTask()>;
+
+static MatcherCreator creatorForType(MatcherType type)
+{
+ switch (type) {
+ case MatcherType::AllSymbols: return &allSymbolsMatcher;
+ case MatcherType::Classes: return &classMatcher;
+ case MatcherType::Functions: return &functionMatcher;
+ case MatcherType::CurrentDocumentSymbols: return &currentDocumentMatcher;
+ }
+ return {};
+}
+
+LocatorMatcherTasks cppMatchers(MatcherType type)
+{
+ const MatcherCreator creator = creatorForType(type);
+ if (!creator)
+ return {};
+ return {creator()};
+}
+
+CppAllSymbolsFilter::CppAllSymbolsFilter()
+{
+ setId(Constants::LOCATOR_FILTER_ID);
+ setDisplayName(Tr::tr(Constants::LOCATOR_FILTER_DISPLAY_NAME));
+ setDescription(Tr::tr(Constants::LOCATOR_FILTER_DESCRIPTION));
+ setDefaultShortcutString(":");
+}
+
+LocatorMatcherTasks CppAllSymbolsFilter::matchers()
+{
+ return {allSymbolsMatcher()};
+}
+
+
+CppClassesFilter::CppClassesFilter()
{
setId(Constants::CLASSES_FILTER_ID);
setDisplayName(Tr::tr(Constants::CLASSES_FILTER_DISPLAY_NAME));
+ setDescription(Tr::tr(Constants::CLASSES_FILTER_DESCRIPTION));
setDefaultShortcutString("c");
- setDefaultIncludedByDefault(false);
}
-CppClassesFilter::~CppClassesFilter() = default;
-
-Core::LocatorFilterEntry CppClassesFilter::filterEntryFromIndexItem(IndexItem::Ptr info)
+LocatorMatcherTasks CppClassesFilter::matchers()
{
- const QVariant id = QVariant::fromValue(info);
- Core::LocatorFilterEntry filterEntry(this, info->symbolName(), id, info->icon());
- filterEntry.extraInfo = info->symbolScope().isEmpty()
- ? info->shortNativeFilePath()
- : info->symbolScope();
- filterEntry.filePath = info->filePath();
- return filterEntry;
+ return {classMatcher()};
}
-CppFunctionsFilter::CppFunctionsFilter(CppLocatorData *locatorData)
- : CppLocatorFilter(locatorData)
+CppFunctionsFilter::CppFunctionsFilter()
{
setId(Constants::FUNCTIONS_FILTER_ID);
setDisplayName(Tr::tr(Constants::FUNCTIONS_FILTER_DISPLAY_NAME));
+ setDescription(Tr::tr(Constants::FUNCTIONS_FILTER_DESCRIPTION));
setDefaultShortcutString("m");
- setDefaultIncludedByDefault(false);
}
-CppFunctionsFilter::~CppFunctionsFilter() = default;
-
-Core::LocatorFilterEntry CppFunctionsFilter::filterEntryFromIndexItem(IndexItem::Ptr info)
+LocatorMatcherTasks CppFunctionsFilter::matchers()
{
- const QVariant id = QVariant::fromValue(info);
-
- QString name = info->symbolName();
- QString extraInfo = info->symbolScope();
- info->unqualifiedNameAndScope(name, &name, &extraInfo);
- if (extraInfo.isEmpty()) {
- extraInfo = info->shortNativeFilePath();
- } else {
- extraInfo.append(" (" + info->filePath().fileName() + ')');
- }
+ return {functionMatcher()};
+}
- Core::LocatorFilterEntry filterEntry(this, name + info->symbolType(), id, info->icon());
- filterEntry.extraInfo = extraInfo;
+CppCurrentDocumentFilter::CppCurrentDocumentFilter()
+{
+ setId(Constants::CURRENT_DOCUMENT_FILTER_ID);
+ setDisplayName(Tr::tr(Constants::CURRENT_DOCUMENT_FILTER_DISPLAY_NAME));
+ setDescription(Tr::tr(Constants::CURRENT_DOCUMENT_FILTER_DESCRIPTION));
+ setDefaultShortcutString(".");
+ setPriority(High);
+}
- return filterEntry;
+LocatorMatcherTasks CppCurrentDocumentFilter::matchers()
+{
+ return {currentDocumentMatcher()};
}
} // namespace CppEditor
diff --git a/src/plugins/cppeditor/cpplocatorfilter.h b/src/plugins/cppeditor/cpplocatorfilter.h
index 9770afe21b..0b012fac6b 100644
--- a/src/plugins/cppeditor/cpplocatorfilter.h
+++ b/src/plugins/cppeditor/cpplocatorfilter.h
@@ -4,58 +4,47 @@
#pragma once
#include "cppeditor_global.h"
-#include "cpplocatordata.h"
-#include "searchsymbols.h"
#include <coreplugin/locator/ilocatorfilter.h>
namespace CppEditor {
-class CPPEDITOR_EXPORT CppLocatorFilter : public Core::ILocatorFilter
-{
- Q_OBJECT
+Core::LocatorMatcherTasks CPPEDITOR_EXPORT cppMatchers(Core::MatcherType type);
+class CppAllSymbolsFilter : public Core::ILocatorFilter
+{
public:
- explicit CppLocatorFilter(CppLocatorData *locatorData);
- ~CppLocatorFilter() override;
+ CppAllSymbolsFilter();
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
+private:
+ Core::LocatorMatcherTasks matchers() final;
+};
-protected:
- virtual IndexItem::ItemType matchTypes() const { return IndexItem::All; }
- virtual Core::LocatorFilterEntry filterEntryFromIndexItem(IndexItem::Ptr info);
+class CppClassesFilter : public Core::ILocatorFilter
+{
+public:
+ CppClassesFilter();
-protected:
- CppLocatorData *m_data = nullptr;
+private:
+ Core::LocatorMatcherTasks matchers() final;
};
-class CPPEDITOR_EXPORT CppClassesFilter : public CppLocatorFilter
+class CppFunctionsFilter : public Core::ILocatorFilter
{
- Q_OBJECT
-
public:
- explicit CppClassesFilter(CppLocatorData *locatorData);
- ~CppClassesFilter() override;
+ CppFunctionsFilter();
-protected:
- IndexItem::ItemType matchTypes() const override { return IndexItem::Class; }
- Core::LocatorFilterEntry filterEntryFromIndexItem(IndexItem::Ptr info) override;
+private:
+ Core::LocatorMatcherTasks matchers() final;
};
-class CPPEDITOR_EXPORT CppFunctionsFilter : public CppLocatorFilter
+class CppCurrentDocumentFilter : public Core::ILocatorFilter
{
- Q_OBJECT
-
public:
- explicit CppFunctionsFilter(CppLocatorData *locatorData);
- ~CppFunctionsFilter() override;
+ CppCurrentDocumentFilter();
-protected:
- IndexItem::ItemType matchTypes() const override { return IndexItem::Function; }
- Core::LocatorFilterEntry filterEntryFromIndexItem(IndexItem::Ptr info) override;
+private:
+ Core::LocatorMatcherTasks matchers() final;
};
} // namespace CppEditor
diff --git a/src/plugins/cppeditor/cpplocatorfilter_test.cpp b/src/plugins/cppeditor/cpplocatorfilter_test.cpp
index 598a6e8564..18e9b933cb 100644
--- a/src/plugins/cppeditor/cpplocatorfilter_test.cpp
+++ b/src/plugins/cppeditor/cpplocatorfilter_test.cpp
@@ -3,17 +3,12 @@
#include "cpplocatorfilter_test.h"
-#include "cppcurrentdocumentfilter.h"
-#include "cpplocatorfilter.h"
-#include "cppmodelmanager.h"
#include "cpptoolstestcase.h"
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/locator/locatorfiltertest.h>
#include <coreplugin/testdatadir.h>
-#include <extensionsystem/pluginmanager.h>
#include <utils/environment.h>
-#include <utils/fileutils.h>
#include <QDebug>
#include <QFileInfo>
@@ -21,7 +16,6 @@
using namespace Core;
using namespace Core::Tests;
-using namespace ExtensionSystem;
using namespace Utils;
namespace CppEditor::Internal {
@@ -31,23 +25,22 @@ const bool debug = qtcEnvironmentVariable("QTC_DEBUG_CPPLOCATORFILTERTESTCASE")
QTC_DECLARE_MYTESTDATADIR("../../../tests/cpplocators/")
-class CppLocatorFilterTestCase
- : public BasicLocatorFilterTest
- , public CppEditor::Tests::TestCase
+class CppLocatorFilterTestCase : public CppEditor::Tests::TestCase
{
public:
- CppLocatorFilterTestCase(ILocatorFilter *filter,
+ CppLocatorFilterTestCase(const QList<LocatorMatcherTask> &matchers,
const QString &fileName,
const QString &searchText,
const ResultDataList &expectedResults)
- : BasicLocatorFilterTest(filter)
- , m_fileName(fileName)
{
QVERIFY(succeededSoFar());
- QVERIFY(!m_fileName.isEmpty());
+ QVERIFY(!fileName.isEmpty());
QVERIFY(garbageCollectGlobalSnapshot());
- ResultDataList results = ResultData::fromFilterEntryList(matchesFor(searchText));
+ QVERIFY(parseFiles(fileName));
+ const LocatorFilterEntries entries = LocatorMatcher::runBlocking(matchers, searchText);
+ QVERIFY(garbageCollectGlobalSnapshot());
+ const ResultDataList results = ResultData::fromFilterEntryList(entries);
if (debug) {
ResultData::printFilterEntries(expectedResults, "Expected:");
ResultData::printFilterEntries(results, "Results:");
@@ -55,61 +48,39 @@ public:
QVERIFY(!results.isEmpty());
QCOMPARE(results, expectedResults);
}
-
-private:
- void doBeforeLocatorRun() override { QVERIFY(parseFiles(m_fileName)); }
- void doAfterLocatorRun() override { QVERIFY(garbageCollectGlobalSnapshot()); }
-
-private:
- const QString m_fileName;
};
-class CppCurrentDocumentFilterTestCase
- : public BasicLocatorFilterTest
- , public CppEditor::Tests::TestCase
+class CppCurrentDocumentFilterTestCase : public CppEditor::Tests::TestCase
{
public:
CppCurrentDocumentFilterTestCase(const FilePath &filePath,
+ const QList<LocatorMatcherTask> &matchers,
const ResultDataList &expectedResults,
const QString &searchText = QString())
- : BasicLocatorFilterTest(CppModelManager::instance()->currentDocumentFilter())
- , m_filePath(filePath)
{
QVERIFY(succeededSoFar());
- QVERIFY(!m_filePath.isEmpty());
+ QVERIFY(!filePath.isEmpty());
- ResultDataList results = ResultData::fromFilterEntryList(matchesFor(searchText));
- if (debug) {
- ResultData::printFilterEntries(expectedResults, "Expected:");
- ResultData::printFilterEntries(results, "Results:");
- }
- QVERIFY(!results.isEmpty());
- QCOMPARE(results, expectedResults);
- }
-
-private:
- void doBeforeLocatorRun() override
- {
QVERIFY(DocumentModel::openedDocuments().isEmpty());
QVERIFY(garbageCollectGlobalSnapshot());
- m_editor = EditorManager::openEditor(m_filePath);
- QVERIFY(m_editor);
-
- QVERIFY(waitForFileInGlobalSnapshot(m_filePath));
- }
+ const auto editor = EditorManager::openEditor(filePath);
+ QVERIFY(editor);
- void doAfterLocatorRun() override
- {
- QVERIFY(closeEditorWithoutGarbageCollectorInvocation(m_editor));
+ QVERIFY(waitForFileInGlobalSnapshot(filePath));
+ const LocatorFilterEntries entries = LocatorMatcher::runBlocking(matchers, searchText);
+ QVERIFY(closeEditorWithoutGarbageCollectorInvocation(editor));
QCoreApplication::processEvents();
QVERIFY(DocumentModel::openedDocuments().isEmpty());
QVERIFY(garbageCollectGlobalSnapshot());
+ const ResultDataList results = ResultData::fromFilterEntryList(entries);
+ if (debug) {
+ ResultData::printFilterEntries(expectedResults, "Expected:");
+ ResultData::printFilterEntries(results, "Results:");
+ }
+ QVERIFY(!results.isEmpty());
+ QCOMPARE(results, expectedResults);
}
-
-private:
- IEditor *m_editor = nullptr;
- const FilePath m_filePath;
};
} // anonymous namespace
@@ -117,28 +88,22 @@ private:
void LocatorFilterTest::testLocatorFilter()
{
QFETCH(QString, testFile);
- QFETCH(ILocatorFilter *, filter);
+ QFETCH(MatcherType, matcherType);
QFETCH(QString, searchText);
QFETCH(ResultDataList, expectedResults);
Tests::VerifyCleanCppModelManager verify;
-
- CppLocatorFilterTestCase(filter, testFile, searchText, expectedResults);
+ CppLocatorFilterTestCase(LocatorMatcher::matchers(matcherType), testFile, searchText,
+ expectedResults);
}
void LocatorFilterTest::testLocatorFilter_data()
{
QTest::addColumn<QString>("testFile");
- QTest::addColumn<ILocatorFilter *>("filter");
+ QTest::addColumn<MatcherType>("matcherType");
QTest::addColumn<QString>("searchText");
QTest::addColumn<ResultDataList>("expectedResults");
- CppModelManager *cppModelManager = CppModelManager::instance();
-
- ILocatorFilter *cppFunctionsFilter = cppModelManager->functionsFilter();
- ILocatorFilter *cppClassesFilter = cppModelManager->classesFilter();
- ILocatorFilter *cppLocatorFilter = cppModelManager->locatorFilter();
-
MyTestDataDir testDirectory("testdata_basic");
QString testFile = testDirectory.file("file1.cpp");
testFile[0] = testFile[0].toLower(); // Ensure Windows path sorts after scope names.
@@ -148,7 +113,7 @@ void LocatorFilterTest::testLocatorFilter_data()
QTest::newRow("CppFunctionsFilter")
<< testFile
- << cppFunctionsFilter
+ << MatcherType::Functions
<< "function"
<< ResultDataList{
ResultData("functionDefinedInClass(bool, int)",
@@ -170,7 +135,7 @@ void LocatorFilterTest::testLocatorFilter_data()
QTest::newRow("CppFunctionsFilter-Sorting")
<< testFile
- << cppFunctionsFilter
+ << MatcherType::Functions
<< "pos"
<< ResultDataList{
ResultData("positiveNumber()", testFileShort),
@@ -181,7 +146,7 @@ void LocatorFilterTest::testLocatorFilter_data()
QTest::newRow("CppFunctionsFilter-arguments")
<< testFile
- << cppFunctionsFilter
+ << MatcherType::Functions
<< "function*bool"
<< ResultDataList{
ResultData("functionDefinedInClass(bool, int)",
@@ -197,7 +162,7 @@ void LocatorFilterTest::testLocatorFilter_data()
QTest::newRow("CppFunctionsFilter-WithNamespacePrefix")
<< testFile
- << cppFunctionsFilter
+ << MatcherType::Functions
<< "mynamespace::"
<< ResultDataList{
ResultData("MyClass()", "MyNamespace::MyClass (file1.cpp)"),
@@ -212,7 +177,7 @@ void LocatorFilterTest::testLocatorFilter_data()
QTest::newRow("CppFunctionsFilter-WithClassPrefix")
<< testFile
- << cppFunctionsFilter
+ << MatcherType::Functions
<< "MyClass::func"
<< ResultDataList{
ResultData("functionDefinedInClass(bool, int)",
@@ -233,7 +198,7 @@ void LocatorFilterTest::testLocatorFilter_data()
QTest::newRow("CppClassesFilter")
<< testFile
- << cppClassesFilter
+ << MatcherType::Classes
<< "myclass"
<< ResultDataList{
ResultData("MyClass", "<anonymous namespace>"),
@@ -243,7 +208,7 @@ void LocatorFilterTest::testLocatorFilter_data()
QTest::newRow("CppClassesFilter-WithNamespacePrefix")
<< testFile
- << cppClassesFilter
+ << MatcherType::Classes
<< "mynamespace::"
<< ResultDataList{
ResultData("MyClass", "MyNamespace")
@@ -252,7 +217,7 @@ void LocatorFilterTest::testLocatorFilter_data()
// all symbols in the left column are expected to be fully qualified.
QTest::newRow("CppLocatorFilter-filtered")
<< testFile
- << cppLocatorFilter
+ << MatcherType::AllSymbols
<< "my"
<< ResultDataList{
ResultData("MyClass", testFileShort),
@@ -278,7 +243,7 @@ void LocatorFilterTest::testLocatorFilter_data()
QTest::newRow("CppClassesFilter-ObjC")
<< objTestFile
- << cppClassesFilter
+ << MatcherType::Classes
<< "M"
<< ResultDataList{
ResultData("MyClass", objTestFileShort),
@@ -289,7 +254,7 @@ void LocatorFilterTest::testLocatorFilter_data()
QTest::newRow("CppFunctionsFilter-ObjC")
<< objTestFile
- << cppFunctionsFilter
+ << MatcherType::Functions
<< "M"
<< ResultDataList{
ResultData("anotherMethod", "MyClass (file1.mm)"),
@@ -319,7 +284,6 @@ void LocatorFilterTest::testCurrentDocumentFilter()
ResultData("functionDeclaredOnly()", "MyClass"),
ResultData("functionDefinedInClass(bool, int)", "MyClass"),
ResultData("functionDefinedOutSideClass(char)", "MyClass"),
- ResultData("functionDefinedOutSideClass(char)", "MyClass"),
ResultData("int myVariable", "MyNamespace"),
ResultData("myFunction(bool, int)", "MyNamespace"),
ResultData("MyEnum", "MyNamespace"),
@@ -332,9 +296,6 @@ void LocatorFilterTest::testCurrentDocumentFilter()
ResultData("functionDefinedOutSideClass(char)", "MyNamespace::MyClass"),
ResultData("functionDefinedOutSideClassAndNamespace(float)",
"MyNamespace::MyClass"),
- ResultData("functionDefinedOutSideClass(char)", "MyNamespace::MyClass"),
- ResultData("functionDefinedOutSideClassAndNamespace(float)",
- "MyNamespace::MyClass"),
ResultData("int myVariable", "<anonymous namespace>"),
ResultData("myFunction(bool, int)", "<anonymous namespace>"),
ResultData("MyEnum", "<anonymous namespace>"),
@@ -345,11 +306,12 @@ void LocatorFilterTest::testCurrentDocumentFilter()
ResultData("functionDeclaredOnly()", "<anonymous namespace>::MyClass"),
ResultData("functionDefinedInClass(bool, int)", "<anonymous namespace>::MyClass"),
ResultData("functionDefinedOutSideClass(char)", "<anonymous namespace>::MyClass"),
- ResultData("functionDefinedOutSideClass(char)", "<anonymous namespace>::MyClass"),
ResultData("main()", ""),
};
- CppCurrentDocumentFilterTestCase(testFile, expectedResults);
+ Tests::VerifyCleanCppModelManager verify;
+ CppCurrentDocumentFilterTestCase(
+ testFile, LocatorMatcher::matchers(MatcherType::CurrentDocumentSymbols), expectedResults);
}
void LocatorFilterTest::testCurrentDocumentHighlighting()
@@ -372,8 +334,8 @@ void LocatorFilterTest::testCurrentDocumentHighlighting()
};
Tests::VerifyCleanCppModelManager verify;
-
- CppCurrentDocumentFilterTestCase(testFile, expectedResults, searchText);
+ CppCurrentDocumentFilterTestCase(testFile,
+ LocatorMatcher::matchers(MatcherType::CurrentDocumentSymbols), expectedResults, searchText);
}
void LocatorFilterTest::testFunctionsFilterHighlighting()
@@ -394,12 +356,9 @@ void LocatorFilterTest::testFunctionsFilterHighlighting()
" ~~~ ")
};
- CppModelManager *cppModelManager = CppModelManager::instance();
- ILocatorFilter *filter = cppModelManager->functionsFilter();
-
Tests::VerifyCleanCppModelManager verify;
-
- CppLocatorFilterTestCase(filter, testFile, searchText, expectedResults);
+ CppLocatorFilterTestCase(LocatorMatcher::matchers(MatcherType::Functions), testFile,
+ searchText, expectedResults);
}
} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/cppmodelmanager.cpp b/src/plugins/cppeditor/cppmodelmanager.cpp
index 8fc44622ab..2b5d4e3fb8 100644
--- a/src/plugins/cppeditor/cppmodelmanager.cpp
+++ b/src/plugins/cppeditor/cppmodelmanager.cpp
@@ -6,10 +6,10 @@
#include "abstracteditorsupport.h"
#include "baseeditordocumentprocessor.h"
#include "compileroptionsbuilder.h"
+#include "cppbuiltinmodelmanagersupport.h"
#include "cppcanonicalsymbol.h"
#include "cppcodemodelinspectordumper.h"
#include "cppcodemodelsettings.h"
-#include "cppcurrentdocumentfilter.h"
#include "cppeditorconstants.h"
#include "cppeditortr.h"
#include "cppfindreferences.h"
@@ -17,7 +17,6 @@
#include "cppindexingsupport.h"
#include "cpplocatordata.h"
#include "cpplocatorfilter.h"
-#include "cppbuiltinmodelmanagersupport.h"
#include "cppprojectfile.h"
#include "cppsourceprocessor.h"
#include "cpptoolsjsextension.h"
@@ -37,11 +36,15 @@
#include <coreplugin/progressmanager/futureprogress.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/vcsmanager.h>
+
#include <cplusplus/ASTPath.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/TypeOfExpression.h>
+
#include <extensionsystem/pluginmanager.h>
+#include <coreplugin/session.h>
+
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/gcctoolchain.h>
#include <projectexplorer/kitinformation.h>
@@ -49,10 +52,10 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectmacro.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <texteditor/textdocument.h>
@@ -60,9 +63,8 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <utils/savefile.h>
#include <utils/temporarydirectory.h>
@@ -90,6 +92,7 @@
#include <sstream>
#endif
+using namespace Core;
using namespace CPlusPlus;
using namespace ProjectExplorer;
using namespace Utils;
@@ -155,7 +158,7 @@ public:
class CppModelManagerPrivate
{
public:
- void setupWatcher(const QFuture<void> &future, ProjectExplorer::Project *project,
+ void setupWatcher(const QFuture<void> &future, Project *project,
ProjectData *projectData, CppModelManager *q);
// Snapshot
@@ -164,15 +167,15 @@ public:
// Project integration
QReadWriteLock m_projectLock;
- QHash<ProjectExplorer::Project *, ProjectData> m_projectData;
- QMap<Utils::FilePath, QList<ProjectPart::ConstPtr> > m_fileToProjectParts;
+ QHash<Project *, ProjectData> m_projectData;
+ QMap<FilePath, QList<ProjectPart::ConstPtr> > m_fileToProjectParts;
QMap<QString, ProjectPart::ConstPtr> m_projectPartIdToProjectProjectPart;
// The members below are cached/(re)calculated from the projects and/or their project parts
bool m_dirty;
- Utils::FilePaths m_projectFiles;
- ProjectExplorer::HeaderPaths m_headerPaths;
- ProjectExplorer::Macros m_definedMacros;
+ FilePaths m_projectFiles;
+ HeaderPaths m_headerPaths;
+ Macros m_definedMacros;
// Editor integration
mutable QMutex m_cppEditorDocumentsMutex;
@@ -201,12 +204,12 @@ public:
QTimer m_fallbackProjectPartTimer;
CppLocatorData m_locatorData;
- std::unique_ptr<Core::ILocatorFilter> m_locatorFilter;
- std::unique_ptr<Core::ILocatorFilter> m_classesFilter;
- std::unique_ptr<Core::ILocatorFilter> m_includesFilter;
- std::unique_ptr<Core::ILocatorFilter> m_functionsFilter;
- std::unique_ptr<Core::IFindFilter> m_symbolsFindFilter;
- std::unique_ptr<Core::ILocatorFilter> m_currentDocumentFilter;
+ std::unique_ptr<ILocatorFilter> m_locatorFilter;
+ std::unique_ptr<ILocatorFilter> m_classesFilter;
+ std::unique_ptr<ILocatorFilter> m_includesFilter;
+ std::unique_ptr<ILocatorFilter> m_functionsFilter;
+ std::unique_ptr<IFindFilter> m_symbolsFindFilter;
+ std::unique_ptr<ILocatorFilter> m_currentDocumentFilter;
QList<Document::DiagnosticMessage> m_diagnosticMessages;
};
@@ -338,7 +341,7 @@ void CppModelManager::findUsages(const CursorInEditor &data, Backend backend)
void CppModelManager::switchHeaderSource(bool inNextSplit, Backend backend)
{
- const Core::IDocument *currentDocument = Core::EditorManager::currentDocument();
+ const IDocument *currentDocument = EditorManager::currentDocument();
QTC_ASSERT(currentDocument, return);
instance()->modelManagerSupport(backend)->switchHeaderSource(currentDocument->filePath(),
inNextSplit);
@@ -346,16 +349,16 @@ void CppModelManager::switchHeaderSource(bool inNextSplit, Backend backend)
void CppModelManager::showPreprocessedFile(bool inNextSplit)
{
- const Core::IDocument *doc = Core::EditorManager::currentDocument();
+ const IDocument *doc = EditorManager::currentDocument();
QTC_ASSERT(doc, return);
static const auto showError = [](const QString &reason) {
- Core::MessageManager::writeFlashing(Tr::tr("Cannot show preprocessed file: %1")
- .arg(reason));
+ MessageManager::writeFlashing(Tr::tr("Cannot show preprocessed file: %1")
+ .arg(reason));
};
static const auto showFallbackWarning = [](const QString &reason) {
- Core::MessageManager::writeSilently(
- Tr::tr("Falling back to built-in preprocessor: %1").arg(reason));
+ MessageManager::writeSilently(Tr::tr("Falling back to built-in preprocessor: %1")
+ .arg(reason));
};
static const auto saveAndOpen = [](const FilePath &filePath, const QByteArray &contents,
bool inNextSplit) {
@@ -446,10 +449,10 @@ void CppModelManager::showPreprocessedFile(bool inNextSplit)
compilerArgs.append("/E");
compilerArgs.append(filePath.toUserOutput());
const CommandLine compilerCommandLine(tc->compilerCommand(), compilerArgs);
- const auto compiler = new QtcProcess(instance());
+ const auto compiler = new Process(instance());
compiler->setCommand(compilerCommandLine);
compiler->setEnvironment(project->activeTarget()->activeBuildConfiguration()->environment());
- connect(compiler, &QtcProcess::done, instance(), [compiler, outFilePath, inNextSplit,
+ connect(compiler, &Process::done, instance(), [compiler, outFilePath, inNextSplit,
useBuiltinPreprocessor, isMsvc] {
compiler->deleteLater();
if (compiler->result() != ProcessResult::FinishedWithSuccess) {
@@ -469,24 +472,24 @@ class FindUnusedActionsEnabledSwitcher
{
public:
FindUnusedActionsEnabledSwitcher()
- : actions{Core::ActionManager::command("CppTools.FindUnusedFunctions"),
- Core::ActionManager::command("CppTools.FindUnusedFunctionsInSubProject")}
+ : actions{ActionManager::command("CppTools.FindUnusedFunctions"),
+ ActionManager::command("CppTools.FindUnusedFunctionsInSubProject")}
{
- for (Core::Command * const action : actions)
+ for (Command * const action : actions)
action->action()->setEnabled(false);
}
~FindUnusedActionsEnabledSwitcher()
{
- for (Core::Command * const action : actions)
+ for (Command * const action : actions)
action->action()->setEnabled(true);
}
private:
- const QList<Core::Command *> actions;
+ const QList<Command *> actions;
};
using FindUnusedActionsEnabledSwitcherPtr = std::shared_ptr<FindUnusedActionsEnabledSwitcher>;
static void checkNextFunctionForUnused(
- const QPointer<Core::SearchResult> &search,
+ const QPointer<SearchResult> &search,
const std::shared_ptr<QFutureInterface<bool>> &findRefsFuture,
const FindUnusedActionsEnabledSwitcherPtr &actionsSwitcher)
{
@@ -531,54 +534,43 @@ void CppModelManager::findUnusedFunctions(const FilePath &folder)
const auto actionsSwitcher = std::make_shared<FindUnusedActionsEnabledSwitcher>();
// Step 1: Employ locator to find all functions
- Core::ILocatorFilter *const functionsFilter
- = Utils::findOrDefault(Core::ILocatorFilter::allLocatorFilters(),
- Utils::equal(&Core::ILocatorFilter::id,
- Id(Constants::FUNCTIONS_FILTER_ID)));
- QTC_ASSERT(functionsFilter, return);
- const QPointer<Core::SearchResult> search
- = Core::SearchResultWindow::instance()
- ->startNewSearch(Tr::tr("Find Unused Functions"),
+ LocatorMatcher *matcher = new LocatorMatcher;
+ matcher->setTasks(LocatorMatcher::matchers(MatcherType::Functions));
+ const QPointer<SearchResult> search
+ = SearchResultWindow::instance()->startNewSearch(Tr::tr("Find Unused Functions"),
{},
{},
- Core::SearchResultWindow::SearchOnly,
- Core::SearchResultWindow::PreserveCaseDisabled,
+ SearchResultWindow::SearchOnly,
+ SearchResultWindow::PreserveCaseDisabled,
"CppEditor");
- connect(search, &Core::SearchResult::activated, [](const Core::SearchResultItem &item) {
- Core::EditorManager::openEditorAtSearchResult(item);
- });
- Core::SearchResultWindow::instance()->popup(Core::IOutputPane::ModeSwitch
- | Core::IOutputPane::WithFocus);
- const auto locatorWatcher = new QFutureWatcher<Core::LocatorFilterEntry>(search);
- functionsFilter->prepareSearch({});
- connect(search, &Core::SearchResult::canceled, locatorWatcher, [locatorWatcher] {
- locatorWatcher->cancel();
+ matcher->setParent(search);
+ connect(search, &SearchResult::activated, [](const SearchResultItem &item) {
+ EditorManager::openEditorAtSearchResult(item);
});
- connect(locatorWatcher, &QFutureWatcher<Core::LocatorFilterEntry>::finished, search,
- [locatorWatcher, search, folder, actionsSwitcher] {
- locatorWatcher->deleteLater();
- if (locatorWatcher->isCanceled()) {
+ SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus);
+ connect(search, &SearchResult::canceled, matcher, [matcher] { delete matcher; });
+ connect(matcher, &LocatorMatcher::done, search,
+ [matcher, search, folder, actionsSwitcher](bool success) {
+ matcher->deleteLater();
+ if (!success) {
search->finishSearch(true);
return;
}
Links links;
- for (int i = 0; i < locatorWatcher->future().resultCount(); ++i) {
- const Core::LocatorFilterEntry &entry = locatorWatcher->resultAt(i);
+ const LocatorFilterEntries entries = matcher->outputData();
+ for (const LocatorFilterEntry &entry : entries) {
static const QStringList prefixBlacklist{"main(", "~", "qHash(", "begin()", "end()",
"cbegin()", "cend()", "constBegin()", "constEnd()"};
if (Utils::anyOf(prefixBlacklist, [&entry](const QString &prefix) {
return entry.displayName.startsWith(prefix); })) {
continue;
}
- Link link;
- if (entry.internalData.canConvert<Link>())
- link = qvariant_cast<Link>(entry.internalData);
- else if (const auto item = qvariant_cast<IndexItem::Ptr>(entry.internalData))
- link = Link(item->filePath(), item->line(), item->column());
-
+ if (!entry.linkForEditor)
+ continue;
+ const Link link = *entry.linkForEditor;
if (link.hasValidTarget() && link.targetFilePath.isReadableFile()
&& (folder.isEmpty() || link.targetFilePath.isChildOf(folder))
- && SessionManager::projectForFile(link.targetFilePath)) {
+ && ProjectManager::projectForFile(link.targetFilePath)) {
links << link;
}
}
@@ -593,12 +585,11 @@ void CppModelManager::findUnusedFunctions(const FilePath &folder)
}));
search->setUserData(remainingAndActiveLinks);
const auto findRefsFuture = std::make_shared<QFutureInterface<bool>>();
- Core::FutureProgress *const progress
- = Core::ProgressManager::addTask(findRefsFuture->future(),
- Tr::tr("Finding Unused Functions"),
- "CppEditor.FindUnusedFunctions");
+ FutureProgress *const progress = ProgressManager::addTask(findRefsFuture->future(),
+ Tr::tr("Finding Unused Functions"),
+ "CppEditor.FindUnusedFunctions");
connect(progress,
- &Core::FutureProgress::canceled,
+ &FutureProgress::canceled,
search,
[search, future = std::weak_ptr<QFutureInterface<bool>>(findRefsFuture)] {
search->finishSearch(true);
@@ -608,7 +599,7 @@ void CppModelManager::findUnusedFunctions(const FilePath &folder)
}
});
findRefsFuture->setProgressRange(0, links.size());
- connect(search, &Core::SearchResult::canceled, [findRefsFuture] {
+ connect(search, &SearchResult::canceled, [findRefsFuture] {
findRefsFuture->cancel();
findRefsFuture->reportFinished();
});
@@ -619,13 +610,10 @@ void CppModelManager::findUnusedFunctions(const FilePath &folder)
for (int i = 0; i < inFlightCount; ++i)
checkNextFunctionForUnused(search, findRefsFuture, actionsSwitcher);
});
- locatorWatcher->setFuture(
- Utils::runAsync([functionsFilter](QFutureInterface<Core::LocatorFilterEntry> &future) {
- future.reportResults(functionsFilter->matchesFor(future, {}));
- }));
+ matcher->start();
}
-void CppModelManager::checkForUnusedSymbol(Core::SearchResult *search,
+void CppModelManager::checkForUnusedSymbol(SearchResult *search,
const Link &link,
CPlusPlus::Symbol *symbol,
const CPlusPlus::LookupContext &context,
@@ -785,62 +773,62 @@ static void setFilter(std::unique_ptr<FilterClass> &filter,
filter = std::move(newFilter);
}
-void CppModelManager::setLocatorFilter(std::unique_ptr<Core::ILocatorFilter> &&filter)
+void CppModelManager::setLocatorFilter(std::unique_ptr<ILocatorFilter> &&filter)
{
setFilter(d->m_locatorFilter, std::move(filter));
}
-void CppModelManager::setClassesFilter(std::unique_ptr<Core::ILocatorFilter> &&filter)
+void CppModelManager::setClassesFilter(std::unique_ptr<ILocatorFilter> &&filter)
{
setFilter(d->m_classesFilter, std::move(filter));
}
-void CppModelManager::setIncludesFilter(std::unique_ptr<Core::ILocatorFilter> &&filter)
+void CppModelManager::setIncludesFilter(std::unique_ptr<ILocatorFilter> &&filter)
{
setFilter(d->m_includesFilter, std::move(filter));
}
-void CppModelManager::setFunctionsFilter(std::unique_ptr<Core::ILocatorFilter> &&filter)
+void CppModelManager::setFunctionsFilter(std::unique_ptr<ILocatorFilter> &&filter)
{
setFilter(d->m_functionsFilter, std::move(filter));
}
-void CppModelManager::setSymbolsFindFilter(std::unique_ptr<Core::IFindFilter> &&filter)
+void CppModelManager::setSymbolsFindFilter(std::unique_ptr<IFindFilter> &&filter)
{
setFilter(d->m_symbolsFindFilter, std::move(filter));
}
-void CppModelManager::setCurrentDocumentFilter(std::unique_ptr<Core::ILocatorFilter> &&filter)
+void CppModelManager::setCurrentDocumentFilter(std::unique_ptr<ILocatorFilter> &&filter)
{
setFilter(d->m_currentDocumentFilter, std::move(filter));
}
-Core::ILocatorFilter *CppModelManager::locatorFilter() const
+ILocatorFilter *CppModelManager::locatorFilter() const
{
return d->m_locatorFilter.get();
}
-Core::ILocatorFilter *CppModelManager::classesFilter() const
+ILocatorFilter *CppModelManager::classesFilter() const
{
return d->m_classesFilter.get();
}
-Core::ILocatorFilter *CppModelManager::includesFilter() const
+ILocatorFilter *CppModelManager::includesFilter() const
{
return d->m_includesFilter.get();
}
-Core::ILocatorFilter *CppModelManager::functionsFilter() const
+ILocatorFilter *CppModelManager::functionsFilter() const
{
return d->m_functionsFilter.get();
}
-Core::IFindFilter *CppModelManager::symbolsFindFilter() const
+IFindFilter *CppModelManager::symbolsFindFilter() const
{
return d->m_symbolsFindFilter.get();
}
-Core::ILocatorFilter *CppModelManager::currentDocumentFilter() const
+ILocatorFilter *CppModelManager::currentDocumentFilter() const
{
return d->m_currentDocumentFilter.get();
}
@@ -880,7 +868,7 @@ CppModelManager *CppModelManager::instance()
void CppModelManager::registerJsExtension()
{
- Core::JsExpander::registerGlobalObject("Cpp", [this] {
+ JsExpander::registerGlobalObject("Cpp", [this] {
return new CppToolsJsExtension(&d->m_locatorData);
});
}
@@ -888,10 +876,10 @@ void CppModelManager::registerJsExtension()
void CppModelManager::initCppTools()
{
// Objects
- connect(Core::VcsManager::instance(), &Core::VcsManager::repositoryChanged,
+ connect(VcsManager::instance(), &VcsManager::repositoryChanged,
this, &CppModelManager::updateModifiedSourceFiles);
- connect(Core::DocumentManager::instance(), &Core::DocumentManager::filesChangedInternally,
- [this](const FilePaths &filePaths) {
+ connect(DocumentManager::instance(), &DocumentManager::filesChangedInternally,
+ this, [this](const FilePaths &filePaths) {
updateSourceFiles(toSet(filePaths));
});
@@ -902,13 +890,25 @@ void CppModelManager::initCppTools()
&d->m_locatorData, &CppLocatorData::onAboutToRemoveFiles);
// Set up builtin filters
- setLocatorFilter(std::make_unique<CppLocatorFilter>(&d->m_locatorData));
- setClassesFilter(std::make_unique<CppClassesFilter>(&d->m_locatorData));
+ setLocatorFilter(std::make_unique<CppAllSymbolsFilter>());
+ setClassesFilter(std::make_unique<CppClassesFilter>());
setIncludesFilter(std::make_unique<CppIncludesFilter>());
- setFunctionsFilter(std::make_unique<CppFunctionsFilter>(&d->m_locatorData));
+ setFunctionsFilter(std::make_unique<CppFunctionsFilter>());
setSymbolsFindFilter(std::make_unique<SymbolsFindFilter>(this));
- setCurrentDocumentFilter(
- std::make_unique<Internal::CppCurrentDocumentFilter>(this));
+ setCurrentDocumentFilter(std::make_unique<CppCurrentDocumentFilter>());
+ // Setup matchers
+ LocatorMatcher::addMatcherCreator(MatcherType::AllSymbols, [] {
+ return cppMatchers(MatcherType::AllSymbols);
+ });
+ LocatorMatcher::addMatcherCreator(MatcherType::Classes, [] {
+ return cppMatchers(MatcherType::Classes);
+ });
+ LocatorMatcher::addMatcherCreator(MatcherType::Functions, [] {
+ return cppMatchers(MatcherType::Functions);
+ });
+ LocatorMatcher::addMatcherCreator(MatcherType::CurrentDocumentSymbols, [] {
+ return cppMatchers(MatcherType::CurrentDocumentSymbols);
+ });
}
CppModelManager::CppModelManager()
@@ -924,7 +924,7 @@ CppModelManager::CppModelManager()
d->m_enableGC = true;
// Visual C++ has 1MiB, macOSX has 512KiB
- if (Utils::HostOsInfo::isWindowsHost() || Utils::HostOsInfo::isMacHost())
+ if (HostOsInfo::isWindowsHost() || HostOsInfo::isMacHost())
d->m_threadPool.setStackSize(2 * 1024 * 1024);
qRegisterMetaType<QSet<QString> >();
@@ -940,23 +940,23 @@ CppModelManager::CppModelManager()
d->m_delayedGcTimer.setSingleShot(true);
connect(&d->m_delayedGcTimer, &QTimer::timeout, this, &CppModelManager::GC);
- auto sessionManager = ProjectExplorer::SessionManager::instance();
- connect(sessionManager, &ProjectExplorer::SessionManager::projectAdded,
+ auto projectManager = ProjectManager::instance();
+ connect(projectManager, &ProjectManager::projectAdded,
this, &CppModelManager::onProjectAdded);
- connect(sessionManager, &ProjectExplorer::SessionManager::aboutToRemoveProject,
+ connect(projectManager, &ProjectManager::aboutToRemoveProject,
this, &CppModelManager::onAboutToRemoveProject);
- connect(sessionManager, &ProjectExplorer::SessionManager::aboutToLoadSession,
+ connect(SessionManager::instance(), &SessionManager::aboutToLoadSession,
this, &CppModelManager::onAboutToLoadSession);
- connect(sessionManager, &ProjectExplorer::SessionManager::startupProjectChanged,
+ connect(projectManager, &ProjectManager::startupProjectChanged,
this, &CppModelManager::onActiveProjectChanged);
- connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
+ connect(EditorManager::instance(), &EditorManager::currentEditorChanged,
this, &CppModelManager::onCurrentEditorChanged);
- connect(Core::DocumentManager::instance(), &Core::DocumentManager::allDocumentsRenamed,
+ connect(DocumentManager::instance(), &DocumentManager::allDocumentsRenamed,
this, &CppModelManager::renameIncludes);
- connect(Core::ICore::instance(), &Core::ICore::coreAboutToClose,
+ connect(ICore::instance(), &ICore::coreAboutToClose,
this, &CppModelManager::onCoreAboutToClose);
d->m_fallbackProjectPartTimer.setSingleShot(true);
@@ -1002,7 +1002,7 @@ Document::Ptr CppModelManager::document(const FilePath &filePath) const
/// Replace the document in the snapshot.
///
-/// \returns true if successful, false if the new document is out-dated.
+/// Returns true if successful, false if the new document is out-dated.
bool CppModelManager::replaceDocument(Document::Ptr newDoc)
{
QMutexLocker locker(&d->m_snapshotMutex);
@@ -1041,13 +1041,13 @@ FilePaths CppModelManager::internalProjectFiles() const
return files;
}
-ProjectExplorer::HeaderPaths CppModelManager::internalHeaderPaths() const
+HeaderPaths CppModelManager::internalHeaderPaths() const
{
- ProjectExplorer::HeaderPaths headerPaths;
+ HeaderPaths headerPaths;
for (const ProjectData &projectData: std::as_const(d->m_projectData)) {
for (const ProjectPart::ConstPtr &part : projectData.projectInfo->projectParts()) {
- for (const ProjectExplorer::HeaderPath &path : part->headerPaths) {
- ProjectExplorer::HeaderPath hp(QDir::cleanPath(path.path), path.type);
+ for (const HeaderPath &path : part->headerPaths) {
+ HeaderPath hp(QDir::cleanPath(path.path), path.type);
if (!headerPaths.contains(hp))
headerPaths.push_back(std::move(hp));
}
@@ -1056,8 +1056,7 @@ ProjectExplorer::HeaderPaths CppModelManager::internalHeaderPaths() const
return headerPaths;
}
-static void addUnique(const ProjectExplorer::Macros &newMacros,
- ProjectExplorer::Macros &macros,
+static void addUnique(const Macros &newMacros, Macros &macros,
QSet<ProjectExplorer::Macro> &alreadyIn)
{
for (const ProjectExplorer::Macro &macro : newMacros) {
@@ -1068,9 +1067,9 @@ static void addUnique(const ProjectExplorer::Macros &newMacros,
}
}
-ProjectExplorer::Macros CppModelManager::internalDefinedMacros() const
+Macros CppModelManager::internalDefinedMacros() const
{
- ProjectExplorer::Macros macros;
+ Macros macros;
QSet<ProjectExplorer::Macro> alreadyIn;
for (const ProjectData &projectData : std::as_const(d->m_projectData)) {
for (const ProjectPart::ConstPtr &part : projectData.projectInfo->projectParts()) {
@@ -1093,7 +1092,7 @@ void CppModelManager::dumpModelManagerConfiguration(const QString &logFileId)
dumper.dumpSnapshot(globalSnapshot, globalSnapshotTitle, /*isGlobalSnapshot=*/ true);
dumper.dumpWorkingCopy(workingCopy());
dumper.dumpMergedEntities(headerPaths(),
- ProjectExplorer:: Macro::toByteArray(definedMacros()));
+ ProjectExplorer::Macro::toByteArray(definedMacros()));
}
QSet<AbstractEditorSupport *> CppModelManager::abstractEditorSupports() const
@@ -1268,8 +1267,8 @@ static QSet<QString> filteredFilesRemoved(const QSet<QString> &files, int fileSi
const QString msg = Tr::tr("C++ Indexer: Skipping file \"%1\" "
"because its path matches the ignore pattern.")
.arg(filePath.displayName());
- QMetaObject::invokeMethod(Core::MessageManager::instance(),
- [msg]() { Core::MessageManager::writeSilently(msg); });
+ QMetaObject::invokeMethod(MessageManager::instance(),
+ [msg] { MessageManager::writeSilently(msg); });
skip = true;
break;
}
@@ -1304,7 +1303,7 @@ ProjectInfoList CppModelManager::projectInfos() const
[](const ProjectData &d) { return d.projectInfo; });
}
-ProjectInfo::ConstPtr CppModelManager::projectInfo(ProjectExplorer::Project *project) const
+ProjectInfo::ConstPtr CppModelManager::projectInfo(Project *project) const
{
QReadLocker locker(&d->m_projectLock);
return d->m_projectData.value(project).projectInfo;
@@ -1424,8 +1423,7 @@ void CppModelManager::recalculateProjectPartMappings()
d->m_symbolFinder.clearCache();
}
-void CppModelManagerPrivate::setupWatcher(const QFuture<void> &future,
- ProjectExplorer::Project *project,
+void CppModelManagerPrivate::setupWatcher(const QFuture<void> &future, Project *project,
ProjectData *projectData, CppModelManager *q)
{
projectData->indexer = new QFutureWatcher<void>(q);
@@ -1446,10 +1444,10 @@ void CppModelManagerPrivate::setupWatcher(const QFuture<void> &future,
void CppModelManager::updateCppEditorDocuments(bool projectsUpdated) const
{
// Refresh visible documents
- QSet<Core::IDocument *> visibleCppEditorDocuments;
- const QList<Core::IEditor *> editors = Core::EditorManager::visibleEditors();
- for (Core::IEditor *editor: editors) {
- if (Core::IDocument *document = editor->document()) {
+ QSet<IDocument *> visibleCppEditorDocuments;
+ const QList<IEditor *> editors = EditorManager::visibleEditors();
+ for (IEditor *editor: editors) {
+ if (IDocument *document = editor->document()) {
const FilePath filePath = document->filePath();
if (CppEditorDocumentHandle *theCppEditorDocument = cppEditorDocument(filePath)) {
visibleCppEditorDocuments.insert(document);
@@ -1459,10 +1457,10 @@ void CppModelManager::updateCppEditorDocuments(bool projectsUpdated) const
}
// Mark invisible documents dirty
- QSet<Core::IDocument *> invisibleCppEditorDocuments
- = Utils::toSet(Core::DocumentModel::openedDocuments());
+ QSet<IDocument *> invisibleCppEditorDocuments
+ = Utils::toSet(DocumentModel::openedDocuments());
invisibleCppEditorDocuments.subtract(visibleCppEditorDocuments);
- for (Core::IDocument *document : std::as_const(invisibleCppEditorDocuments)) {
+ for (IDocument *document : std::as_const(invisibleCppEditorDocuments)) {
const FilePath filePath = document->filePath();
if (CppEditorDocumentHandle *theCppEditorDocument = cppEditorDocument(filePath)) {
const CppEditorDocumentHandle::RefreshReason refreshReason = projectsUpdated
@@ -1483,7 +1481,7 @@ QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo::ConstPtr &ne
QStringList removedProjectParts;
bool filesRemoved = false;
- ProjectExplorer::Project * const project = projectForProjectInfo(*newProjectInfo);
+ Project * const project = projectForProjectInfo(*newProjectInfo);
if (!project)
return {};
@@ -1589,20 +1587,20 @@ ProjectPart::ConstPtr CppModelManager::projectPartForId(const QString &projectPa
return d->m_projectPartIdToProjectProjectPart.value(projectPartId);
}
-QList<ProjectPart::ConstPtr> CppModelManager::projectPart(const Utils::FilePath &fileName) const
+QList<ProjectPart::ConstPtr> CppModelManager::projectPart(const FilePath &fileName) const
{
QReadLocker locker(&d->m_projectLock);
return d->m_fileToProjectParts.value(fileName.canonicalPath());
}
QList<ProjectPart::ConstPtr> CppModelManager::projectPartFromDependencies(
- const Utils::FilePath &fileName) const
+ const FilePath &fileName) const
{
QSet<ProjectPart::ConstPtr> parts;
- const Utils::FilePaths deps = snapshot().filesDependingOn(fileName);
+ const FilePaths deps = snapshot().filesDependingOn(fileName);
QReadLocker locker(&d->m_projectLock);
- for (const Utils::FilePath &dep : deps)
+ for (const FilePath &dep : deps)
parts.unite(Utils::toSet(d->m_fileToProjectParts.value(dep.canonicalPath())));
return parts.values();
@@ -1614,7 +1612,7 @@ ProjectPart::ConstPtr CppModelManager::fallbackProjectPart()
return d->m_fallbackProjectPart;
}
-bool CppModelManager::isCppEditor(Core::IEditor *editor)
+bool CppModelManager::isCppEditor(IEditor *editor)
{
return editor->context().contains(ProjectExplorer::Constants::CXX_LANGUAGE_ID);
}
@@ -1647,7 +1645,7 @@ void CppModelManager::emitAbstractEditorSupportRemoved(const QString &filePath)
emit abstractEditorSupportRemoved(filePath);
}
-void CppModelManager::onProjectAdded(ProjectExplorer::Project *)
+void CppModelManager::onProjectAdded(Project *)
{
QWriteLocker locker(&d->m_projectLock);
d->m_dirty = true;
@@ -1667,7 +1665,7 @@ static QStringList removedProjectParts(const QStringList &before, const QStringL
return Utils::toList(b);
}
-void CppModelManager::onAboutToRemoveProject(ProjectExplorer::Project *project)
+void CppModelManager::onAboutToRemoveProject(Project *project)
{
QStringList idsOfRemovedProjectParts;
@@ -1689,7 +1687,7 @@ void CppModelManager::onAboutToRemoveProject(ProjectExplorer::Project *project)
delayedGC();
}
-void CppModelManager::onActiveProjectChanged(ProjectExplorer::Project *project)
+void CppModelManager::onActiveProjectChanged(Project *project)
{
if (!project)
return; // Last project closed.
@@ -1711,7 +1709,7 @@ void CppModelManager::onSourceFilesRefreshed() const
}
}
-void CppModelManager::onCurrentEditorChanged(Core::IEditor *editor)
+void CppModelManager::onCurrentEditorChanged(IEditor *editor)
{
if (!editor || !editor->document())
return;
@@ -1752,7 +1750,7 @@ QSet<QString> CppModelManager::dependingInternalTargets(const FilePath &file) co
return result;
}
-QSet<QString> CppModelManager::internalTargets(const Utils::FilePath &filePath) const
+QSet<QString> CppModelManager::internalTargets(const FilePath &filePath) const
{
const QList<ProjectPart::ConstPtr> projectParts = projectPart(filePath);
// if we have no project parts it's most likely a header with declarations only and CMake based
@@ -1761,14 +1759,13 @@ QSet<QString> CppModelManager::internalTargets(const Utils::FilePath &filePath)
QSet<QString> targets;
for (const ProjectPart::ConstPtr &part : projectParts) {
targets.insert(part->buildSystemTarget);
- if (part->buildTargetType != ProjectExplorer::BuildTargetType::Executable)
+ if (part->buildTargetType != BuildTargetType::Executable)
targets.unite(dependingInternalTargets(filePath));
}
return targets;
}
-void CppModelManager::renameIncludes(const Utils::FilePath &oldFilePath,
- const Utils::FilePath &newFilePath)
+void CppModelManager::renameIncludes(const FilePath &oldFilePath, const FilePath &newFilePath)
{
if (oldFilePath.isEmpty() || newFilePath.isEmpty())
return;
@@ -1815,7 +1812,7 @@ void CppModelManager::renameIncludes(const Utils::FilePath &oldFilePath,
const QTextBlock &block = file->document()->findBlockByNumber(loc.second - 1);
const int replaceStart = block.text().indexOf(oldFileName);
if (replaceStart > -1) {
- Utils::ChangeSet changeSet;
+ ChangeSet changeSet;
changeSet.replace(block.position() + replaceStart,
block.position() + replaceStart + oldFileName.length(),
newFileName);
@@ -1843,13 +1840,13 @@ static const char *belongingClassName(const Function *function)
return nullptr;
}
-QSet<QString> CppModelManager::symbolsInFiles(const QSet<Utils::FilePath> &files) const
+QSet<QString> CppModelManager::symbolsInFiles(const QSet<FilePath> &files) const
{
QSet<QString> uniqueSymbols;
const Snapshot cppSnapShot = snapshot();
// Iterate over the files and get interesting symbols
- for (const Utils::FilePath &file : files) {
+ for (const FilePath &file : files) {
// Add symbols from the C++ code model
const CPlusPlus::Document::Ptr doc = cppSnapShot.document(file);
if (!doc.isNull() && doc->control()) {
@@ -1881,7 +1878,7 @@ QSet<QString> CppModelManager::symbolsInFiles(const QSet<Utils::FilePath> &files
void CppModelManager::onCoreAboutToClose()
{
- Core::ProgressManager::cancelTasks(Constants::TASK_INDEX);
+ ProgressManager::cancelTasks(Constants::TASK_INDEX);
d->m_enableGC = false;
}
@@ -1891,27 +1888,27 @@ void CppModelManager::setupFallbackProjectPart()
RawProjectPart rpp;
rpp.setMacros(definedMacros());
rpp.setHeaderPaths(headerPaths());
- rpp.setQtVersion(Utils::QtMajorVersion::Qt5);
+ rpp.setQtVersion(QtMajorVersion::Qt5);
// Do not activate ObjectiveCExtensions since this will lead to the
// "objective-c++" language option for a project-less *.cpp file.
- Utils::LanguageExtensions langExtensions = Utils::LanguageExtension::All;
- langExtensions &= ~Utils::LanguageExtensions(Utils::LanguageExtension::ObjectiveC);
+ LanguageExtensions langExtensions = LanguageExtension::All;
+ langExtensions &= ~LanguageExtensions(LanguageExtension::ObjectiveC);
// TODO: Use different fallback toolchain for different kinds of files?
const Kit * const defaultKit = KitManager::isLoaded() ? KitManager::defaultKit() : nullptr;
const ToolChain * const defaultTc = defaultKit
? ToolChainKitAspect::cxxToolChain(defaultKit) : nullptr;
if (defaultKit && defaultTc) {
- Utils::FilePath sysroot = SysRootKitAspect::sysRoot(defaultKit);
+ FilePath sysroot = SysRootKitAspect::sysRoot(defaultKit);
if (sysroot.isEmpty())
- sysroot = Utils::FilePath::fromString(defaultTc->sysRoot());
+ sysroot = FilePath::fromString(defaultTc->sysRoot());
Utils::Environment env = defaultKit->buildEnvironment();
tcInfo = ToolChainInfo(defaultTc, sysroot, env);
const auto macroInspectionWrapper = [runner = tcInfo.macroInspectionRunner](
const QStringList &flags) {
ToolChain::MacroInspectionReport report = runner(flags);
- report.languageVersion = Utils::LanguageVersion::LatestCxx;
+ report.languageVersion = LanguageVersion::LatestCxx;
return report;
};
tcInfo.macroInspectionRunner = macroInspectionWrapper;
@@ -1941,7 +1938,7 @@ void CppModelManager::GC()
filesInEditorSupports << abstractEditorSupport->filePath();
Snapshot currentSnapshot = snapshot();
- QSet<Utils::FilePath> reachableFiles;
+ QSet<FilePath> reachableFiles;
// The configuration file is part of the project files, which is just fine.
// If single files are open, without any project, then there is no need to
// keep the configuration file around.
@@ -1964,7 +1961,7 @@ void CppModelManager::GC()
QStringList notReachableFiles;
Snapshot newSnapshot;
for (Snapshot::const_iterator it = currentSnapshot.begin(); it != currentSnapshot.end(); ++it) {
- const Utils::FilePath &fileName = it.key();
+ const FilePath &fileName = it.key();
if (reachableFiles.contains(fileName))
newSnapshot.insert(it.value());
@@ -2001,7 +1998,7 @@ TextEditor::BaseHoverHandler *CppModelManager::createHoverHandler() const
}
void CppModelManager::followSymbol(const CursorInEditor &data,
- const Utils::LinkHandler &processLinkCallback,
+ const LinkHandler &processLinkCallback,
bool resolveTarget, bool inNextSplit, Backend backend)
{
instance()->modelManagerSupport(backend)->followSymbol(data, processLinkCallback,
@@ -2009,7 +2006,7 @@ void CppModelManager::followSymbol(const CursorInEditor &data,
}
void CppModelManager::followSymbolToType(const CursorInEditor &data,
- const Utils::LinkHandler &processLinkCallback,
+ const LinkHandler &processLinkCallback,
bool inNextSplit, Backend backend)
{
instance()->modelManagerSupport(backend)->followSymbolToType(data, processLinkCallback,
@@ -2017,19 +2014,12 @@ void CppModelManager::followSymbolToType(const CursorInEditor &data,
}
void CppModelManager::switchDeclDef(const CursorInEditor &data,
- const Utils::LinkHandler &processLinkCallback,
+ const LinkHandler &processLinkCallback,
Backend backend)
{
instance()->modelManagerSupport(backend)->switchDeclDef(data, processLinkCallback);
}
-Core::ILocatorFilter *CppModelManager::createAuxiliaryCurrentDocumentFilter()
-{
- const auto filter = new Internal::CppCurrentDocumentFilter(instance());
- filter->makeAuxiliary();
- return filter;
-}
-
BaseEditorDocumentProcessor *CppModelManager::createEditorDocumentProcessor(
TextEditor::TextDocument *baseTextDocument) const
{
@@ -2049,7 +2039,7 @@ FilePaths CppModelManager::projectFiles()
return d->m_projectFiles;
}
-ProjectExplorer::HeaderPaths CppModelManager::headerPaths()
+HeaderPaths CppModelManager::headerPaths()
{
QWriteLocker locker(&d->m_projectLock);
ensureUpdated();
@@ -2057,13 +2047,13 @@ ProjectExplorer::HeaderPaths CppModelManager::headerPaths()
return d->m_headerPaths;
}
-void CppModelManager::setHeaderPaths(const ProjectExplorer::HeaderPaths &headerPaths)
+void CppModelManager::setHeaderPaths(const HeaderPaths &headerPaths)
{
QWriteLocker locker(&d->m_projectLock);
d->m_headerPaths = headerPaths;
}
-ProjectExplorer::Macros CppModelManager::definedMacros()
+Macros CppModelManager::definedMacros()
{
QWriteLocker locker(&d->m_projectLock);
ensureUpdated();
diff --git a/src/plugins/cppeditor/cppmodelmanager.h b/src/plugins/cppeditor/cppmodelmanager.h
index 2929d615e6..3ab9ee2689 100644
--- a/src/plugins/cppeditor/cppmodelmanager.h
+++ b/src/plugins/cppeditor/cppmodelmanager.h
@@ -196,8 +196,6 @@ public:
const CPlusPlus::LookupContext &context,
const Utils::LinkHandler &callback);
- static Core::ILocatorFilter *createAuxiliaryCurrentDocumentFilter();
-
CppIndexingSupport *indexingSupport();
Utils::FilePaths projectFiles();
diff --git a/src/plugins/cppeditor/cppmodelmanager_test.cpp b/src/plugins/cppeditor/cppmodelmanager_test.cpp
index 97d13d0704..c3fb341646 100644
--- a/src/plugins/cppeditor/cppmodelmanager_test.cpp
+++ b/src/plugins/cppeditor/cppmodelmanager_test.cpp
@@ -18,7 +18,7 @@
#include <cplusplus/LookupContext.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/executeondestruction.h>
#include <utils/hostosinfo.h>
@@ -649,7 +649,7 @@ void ModelManagerTest::testGcIfLastCppeditorClosed()
QVERIFY(editor);
QCOMPARE(Core::DocumentModel::openedDocuments().size(), 1);
QVERIFY(mm->isCppEditor(editor));
- QVERIFY(mm->workingCopy().contains(file));
+ QVERIFY(mm->workingCopy().get(file));
// Wait until the file is refreshed
helper.waitForRefreshedSourceFiles();
@@ -659,7 +659,7 @@ void ModelManagerTest::testGcIfLastCppeditorClosed()
helper.waitForFinishedGc();
// Check: File is removed from the snapshpt
- QVERIFY(!mm->workingCopy().contains(file));
+ QVERIFY(!mm->workingCopy().get(file));
QVERIFY(!mm->snapshot().contains(file));
}
@@ -684,13 +684,13 @@ void ModelManagerTest::testDontGcOpenedFiles()
// Wait until the file is refreshed and check whether it is in the working copy
helper.waitForRefreshedSourceFiles();
- QVERIFY(mm->workingCopy().contains(file));
+ QVERIFY(mm->workingCopy().get(file));
// Run the garbage collector
mm->GC();
// Check: File is still there
- QVERIFY(mm->workingCopy().contains(file));
+ QVERIFY(mm->workingCopy().get(file));
QVERIFY(mm->snapshot().contains(file));
// Close editor
@@ -1090,7 +1090,7 @@ void ModelManagerTest::testRenameIncludesInEditor()
});
QCOMPARE(Core::DocumentModel::openedDocuments().size(), 1);
QVERIFY(modelManager->isCppEditor(editor));
- QVERIFY(modelManager->workingCopy().contains(mainFile));
+ QVERIFY(modelManager->workingCopy().get(mainFile));
// Test the renaming of a header file where a pragma once guard is present
QVERIFY(Core::FileUtils::renameFile(headerWithPragmaOnce,
diff --git a/src/plugins/cppeditor/cppoutline.cpp b/src/plugins/cppeditor/cppoutline.cpp
index 8c28c7e30a..0d8883f631 100644
--- a/src/plugins/cppeditor/cppoutline.cpp
+++ b/src/plugins/cppeditor/cppoutline.cpp
@@ -13,7 +13,6 @@
#include <coreplugin/editormanager/editormanager.h>
#include <texteditor/textdocument.h>
#include <texteditor/texteditor.h>
-#include <utils/linecolumn.h>
#include <utils/qtcassert.h>
#include <QDebug>
@@ -184,8 +183,8 @@ void CppOutlineWidget::updateIndexNow()
void CppOutlineWidget::updateTextCursor(const QModelIndex &proxyIndex)
{
QModelIndex index = m_proxyModel->mapToSource(proxyIndex);
- Utils::LineColumn lineColumn
- = m_editor->cppEditorDocument()->outlineModel().lineColumnFromIndex(index);
+ Utils::Text::Position lineColumn
+ = m_editor->cppEditorDocument()->outlineModel().positionFromIndex(index);
if (!lineColumn.isValid())
return;
@@ -194,8 +193,7 @@ void CppOutlineWidget::updateTextCursor(const QModelIndex &proxyIndex)
Core::EditorManager::cutForwardNavigationHistory();
Core::EditorManager::addCurrentPositionToNavigationHistory();
- // line has to be 1 based, column 0 based!
- m_editor->gotoLine(lineColumn.line, lineColumn.column - 1, true, true);
+ m_editor->gotoLine(lineColumn.line, lineColumn.column, true, true);
m_blockCursorSync = false;
}
diff --git a/src/plugins/cppeditor/cppoutlinemodel.cpp b/src/plugins/cppeditor/cppoutlinemodel.cpp
index d1875b6bb3..068dc310b1 100644
--- a/src/plugins/cppeditor/cppoutlinemodel.cpp
+++ b/src/plugins/cppeditor/cppoutlinemodel.cpp
@@ -9,8 +9,8 @@
#include <cplusplus/Scope.h>
#include <cplusplus/Symbols.h>
-#include <utils/linecolumn.h>
#include <utils/link.h>
+#include <utils/theme/theme.h>
#include <QTimer>
@@ -103,6 +103,24 @@ public:
return name;
}
+ case Qt::ForegroundRole: {
+ const auto isFwdDecl = [&] {
+ const FullySpecifiedType type = symbol->type();
+ if (type->asForwardClassDeclarationType())
+ return true;
+ if (const Template * const tmpl = type->asTemplateType())
+ return tmpl->declaration() && tmpl->declaration()->asForwardClassDeclaration();
+ if (type->asObjCForwardClassDeclarationType())
+ return true;
+ if (type->asObjCForwardProtocolDeclarationType())
+ return true;
+ return false;
+ };
+ if (isFwdDecl())
+ return Utils::creatorTheme()->color(Utils::Theme::TextColorDisabled);
+ return TreeItem::data(column, role);
+ }
+
case Qt::DecorationRole:
return Icons::iconForSymbol(symbol);
@@ -220,20 +238,20 @@ Utils::Link OutlineModel::linkFromIndex(const QModelIndex &sourceIndex) const
return symbol->toLink();
}
-Utils::LineColumn OutlineModel::lineColumnFromIndex(const QModelIndex &sourceIndex) const
+Utils::Text::Position OutlineModel::positionFromIndex(const QModelIndex &sourceIndex) const
{
- Utils::LineColumn lineColumn;
+ Utils::Text::Position lineColumn;
CPlusPlus::Symbol *symbol = symbolFromIndex(sourceIndex);
if (!symbol)
return lineColumn;
lineColumn.line = symbol->line();
- lineColumn.column = symbol->column();
+ lineColumn.column = symbol->column() - 1;
return lineColumn;
}
OutlineModel::Range OutlineModel::rangeFromIndex(const QModelIndex &sourceIndex) const
{
- Utils::LineColumn lineColumn = lineColumnFromIndex(sourceIndex);
+ Utils::Text::Position lineColumn = positionFromIndex(sourceIndex);
return {lineColumn, lineColumn};
}
diff --git a/src/plugins/cppeditor/cppoutlinemodel.h b/src/plugins/cppeditor/cppoutlinemodel.h
index 08b7adb442..ce499c91a0 100644
--- a/src/plugins/cppeditor/cppoutlinemodel.h
+++ b/src/plugins/cppeditor/cppoutlinemodel.h
@@ -4,6 +4,7 @@
#pragma once
#include <utils/dropsupport.h>
+#include <utils/textutils.h>
#include <utils/treemodel.h>
#include <cplusplus/CppDocument.h>
@@ -44,10 +45,11 @@ public:
bool isGenerated(const QModelIndex &sourceIndex) const;
Utils::Link linkFromIndex(const QModelIndex &sourceIndex) const;
- Utils::LineColumn lineColumnFromIndex(const QModelIndex &sourceIndex) const;
- using Range = std::pair<Utils::LineColumn, Utils::LineColumn>;
+ Utils::Text::Position positionFromIndex(const QModelIndex &sourceIndex) const;
+ using Range = std::pair<Utils::Text::Position, Utils::Text::Position>;
Range rangeFromIndex(const QModelIndex &sourceIndex) const;
+ // line is 1-based and column is 0-based
QModelIndex indexForPosition(int line, int column, const QModelIndex &rootIndex = {}) const;
private:
diff --git a/src/plugins/cppeditor/cpppreprocessordialog.cpp b/src/plugins/cppeditor/cpppreprocessordialog.cpp
index b6b1d9d2a6..87afd9761a 100644
--- a/src/plugins/cppeditor/cpppreprocessordialog.cpp
+++ b/src/plugins/cppeditor/cpppreprocessordialog.cpp
@@ -7,7 +7,7 @@
#include "cppeditortr.h"
#include "cpptoolsreuse.h"
-#include <projectexplorer/session.h>
+#include <coreplugin/session.h>
#include <texteditor/snippets/snippeteditor.h>
@@ -27,7 +27,7 @@ CppPreProcessorDialog::CppPreProcessorDialog(const FilePath &filePath, QWidget *
setWindowTitle(Tr::tr("Additional C++ Preprocessor Directives"));
const QString key = Constants::EXTRA_PREPROCESSOR_DIRECTIVES + m_filePath.toString();
- const QString directives = ProjectExplorer::SessionManager::value(key).toString();
+ const QString directives = Core::SessionManager::value(key).toString();
m_editWidget = new TextEditor::SnippetEditorWidget;
m_editWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
@@ -56,7 +56,7 @@ int CppPreProcessorDialog::exec()
return Rejected;
const QString key = Constants::EXTRA_PREPROCESSOR_DIRECTIVES + m_filePath.toString();
- ProjectExplorer::SessionManager::setValue(key, extraPreprocessorDirectives());
+ Core::SessionManager::setValue(key, extraPreprocessorDirectives());
return Accepted;
}
diff --git a/src/plugins/cppeditor/cppprojectinfogenerator.cpp b/src/plugins/cppeditor/cppprojectinfogenerator.cpp
index fe077bd377..5c4c03de4c 100644
--- a/src/plugins/cppeditor/cppprojectinfogenerator.cpp
+++ b/src/plugins/cppeditor/cppprojectinfogenerator.cpp
@@ -10,30 +10,24 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/taskhub.h>
-#include <utils/qtcassert.h>
-
+#include <QPromise>
#include <QTimer>
-#include <set>
-
using namespace ProjectExplorer;
using namespace Utils;
namespace CppEditor::Internal {
-ProjectInfoGenerator::ProjectInfoGenerator(
- const QFutureInterface<ProjectInfo::ConstPtr> &futureInterface,
- const ProjectUpdateInfo &projectUpdateInfo)
- : m_futureInterface(futureInterface)
- , m_projectUpdateInfo(projectUpdateInfo)
+ProjectInfoGenerator::ProjectInfoGenerator(const ProjectUpdateInfo &projectUpdateInfo)
+ : m_projectUpdateInfo(projectUpdateInfo)
{
}
-ProjectInfo::ConstPtr ProjectInfoGenerator::generate()
+ProjectInfo::ConstPtr ProjectInfoGenerator::generate(const QPromise<ProjectInfo::ConstPtr> &promise)
{
QVector<ProjectPart::ConstPtr> projectParts;
for (const RawProjectPart &rpp : m_projectUpdateInfo.rawProjectParts) {
- if (m_futureInterface.isCanceled())
+ if (promise.isCanceled())
return {};
for (const ProjectPart::ConstPtr &part : createProjectParts(
rpp, m_projectUpdateInfo.projectFilePath)) {
diff --git a/src/plugins/cppeditor/cppprojectinfogenerator.h b/src/plugins/cppeditor/cppprojectinfogenerator.h
index c090974c04..8720d3e1cd 100644
--- a/src/plugins/cppeditor/cppprojectinfogenerator.h
+++ b/src/plugins/cppeditor/cppprojectinfogenerator.h
@@ -5,17 +5,19 @@
#include "projectinfo.h"
-#include <QFutureInterface>
+QT_BEGIN_NAMESPACE
+template <class T>
+class QPromise;
+QT_END_NAMESPACE
namespace CppEditor::Internal {
class ProjectInfoGenerator
{
public:
- ProjectInfoGenerator(const QFutureInterface<ProjectInfo::ConstPtr> &futureInterface,
- const ProjectExplorer::ProjectUpdateInfo &projectUpdateInfo);
+ ProjectInfoGenerator(const ProjectExplorer::ProjectUpdateInfo &projectUpdateInfo);
- ProjectInfo::ConstPtr generate();
+ ProjectInfo::ConstPtr generate(const QPromise<ProjectInfo::ConstPtr> &promise);
private:
const QVector<ProjectPart::ConstPtr> createProjectParts(
@@ -29,7 +31,6 @@ private:
Utils::LanguageExtensions languageExtensions);
private:
- const QFutureInterface<ProjectInfo::ConstPtr> m_futureInterface;
const ProjectExplorer::ProjectUpdateInfo &m_projectUpdateInfo;
bool m_cToolchainMissing = false;
bool m_cxxToolchainMissing = false;
diff --git a/src/plugins/cppeditor/cppprojectupdater.cpp b/src/plugins/cppeditor/cppprojectupdater.cpp
index a784d3bb79..c92d091396 100644
--- a/src/plugins/cppeditor/cppprojectupdater.cpp
+++ b/src/plugins/cppeditor/cppprojectupdater.cpp
@@ -13,21 +13,15 @@
#include <projectexplorer/extracompiler.h>
#include <utils/algorithm.h>
-#include <utils/asynctask.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
-#include <QFutureInterface>
-
using namespace ProjectExplorer;
using namespace Utils;
namespace CppEditor {
-CppProjectUpdater::CppProjectUpdater()
-{
- m_futureSynchronizer.setCancelOnWait(true);
-}
-
+CppProjectUpdater::CppProjectUpdater() = default;
CppProjectUpdater::~CppProjectUpdater() = default;
void CppProjectUpdater::update(const ProjectUpdateInfo &projectUpdateInfo)
@@ -49,12 +43,12 @@ void CppProjectUpdater::update(const ProjectUpdateInfo &projectUpdateInfo,
using namespace ProjectExplorer;
// Run the project info generator in a worker thread and continue if that one is finished.
- const auto infoGenerator = [=](QFutureInterface<ProjectInfo::ConstPtr> &futureInterface) {
+ const auto infoGenerator = [=](QPromise<ProjectInfo::ConstPtr> &promise) {
ProjectUpdateInfo fullProjectUpdateInfo = projectUpdateInfo;
if (fullProjectUpdateInfo.rppGenerator)
fullProjectUpdateInfo.rawProjectParts = fullProjectUpdateInfo.rppGenerator();
- Internal::ProjectInfoGenerator generator(futureInterface, fullProjectUpdateInfo);
- futureInterface.reportResult(generator.generate());
+ Internal::ProjectInfoGenerator generator(fullProjectUpdateInfo);
+ promise.addResult(generator.generate(promise));
};
using namespace Tasking;
@@ -62,16 +56,16 @@ void CppProjectUpdater::update(const ProjectUpdateInfo &projectUpdateInfo,
ProjectInfo::ConstPtr projectInfo = nullptr;
};
const TreeStorage<UpdateStorage> storage;
- const auto setupInfoGenerator = [=](AsyncTask<ProjectInfo::ConstPtr> &async) {
- async.setAsyncCallData(infoGenerator);
+ const auto setupInfoGenerator = [=](Async<ProjectInfo::ConstPtr> &async) {
+ async.setConcurrentCallData(infoGenerator);
async.setFutureSynchronizer(&m_futureSynchronizer);
};
- const auto onInfoGeneratorDone = [=](const AsyncTask<ProjectInfo::ConstPtr> &async) {
+ const auto onInfoGeneratorDone = [=](const Async<ProjectInfo::ConstPtr> &async) {
if (async.isResultAvailable())
storage->projectInfo = async.result();
};
QList<TaskItem> tasks{parallel};
- tasks.append(Async<ProjectInfo::ConstPtr>(setupInfoGenerator, onInfoGeneratorDone));
+ tasks.append(AsyncTask<ProjectInfo::ConstPtr>(setupInfoGenerator, onInfoGeneratorDone));
for (QPointer<ExtraCompiler> compiler : compilers) {
if (compiler && compiler->isDirty())
tasks.append(compiler->compileFileItem());
@@ -99,8 +93,8 @@ void CppProjectUpdater::update(const ProjectUpdateInfo &projectUpdateInfo,
const Group root {
Storage(storage),
Group(tasks),
- OnGroupDone(onDone),
- OnGroupError(onError)
+ onGroupDone(onDone),
+ onGroupError(onError)
};
m_taskTree.reset(new TaskTree(root));
auto progress = new Core::TaskProgress(m_taskTree.get());
diff --git a/src/plugins/cppeditor/cppprojectupdater.h b/src/plugins/cppeditor/cppprojectupdater.h
index 50910e8320..a8cebc5b1e 100644
--- a/src/plugins/cppeditor/cppprojectupdater.h
+++ b/src/plugins/cppeditor/cppprojectupdater.h
@@ -10,7 +10,7 @@
#include <utils/futuresynchronizer.h>
namespace ProjectExplorer { class ExtraCompiler; }
-namespace Utils { class TaskTree; }
+namespace Tasking { class TaskTree; }
namespace CppEditor {
@@ -44,7 +44,7 @@ public:
private:
Utils::FutureSynchronizer m_futureSynchronizer;
- std::unique_ptr<Utils::TaskTree> m_taskTree;
+ std::unique_ptr<Tasking::TaskTree> m_taskTree;
};
} // namespace CppEditor
diff --git a/src/plugins/cppeditor/cppquickfix_test.cpp b/src/plugins/cppeditor/cppquickfix_test.cpp
index 560a27fe24..f57670c161 100644
--- a/src/plugins/cppeditor/cppquickfix_test.cpp
+++ b/src/plugins/cppeditor/cppquickfix_test.cpp
@@ -1725,7 +1725,7 @@ void QuickfixTest::testGeneric_data()
<< _(R"(const char *str = @"\xc3\xa0""f23\xd0\xb1g\xd0\xb1""1";)")
<< _(R"(const char *str = "àf23бgб1";)");
QTest::newRow("AddLocalDeclaration_QTCREATORBUG-26004")
- << CppQuickFixFactoryPtr(new AddLocalDeclaration)
+ << CppQuickFixFactoryPtr(new AddDeclarationForUndeclaredIdentifier)
<< _("void func() {\n"
" QStringList list;\n"
" @it = list.cbegin();\n"
@@ -2315,7 +2315,7 @@ signals:
void newFooBarTestValue();
private:
- Q_PROPERTY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue)
+ Q_PROPERTY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue FINAL)
};
)-";
QTest::addRow("create right names") << QByteArrayList{originalSource, expectedSource} << 4;
@@ -2346,7 +2346,7 @@ signals:
void newFooBarTestValue();
private:
- Q_PROPERTY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue)
+ Q_PROPERTY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue FINAL)
};
)-";
expectedSource = "";
@@ -2355,7 +2355,7 @@ private:
// create from Q_PROPERTY with custom names
originalSource = R"-(
class Test {
- Q_PROPER@TY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue)
+ Q_PROPER@TY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue FINAL)
public:
int give_me_foo_bar_test() const
@@ -2380,7 +2380,7 @@ signals:
)-";
expectedSource = R"-(
class Test {
- Q_PROPERTY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue)
+ Q_PROPERTY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue FINAL)
public:
int give_me_foo_bar_test() const
@@ -2411,14 +2411,14 @@ private:
// create from Q_PROPERTY with custom names
originalSource = R"-(
class Test {
- Q_PROPE@RTY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue)
+ Q_PROPE@RTY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue FINAL)
int mem_fooBar_test;
public:
};
)-";
expectedSource = R"-(
class Test {
- Q_PROPERTY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue)
+ Q_PROPERTY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue FINAL)
int mem_fooBar_test;
public:
int give_me_foo_bar_test() const
@@ -2766,7 +2766,7 @@ public:
signals:
void barChanged(N2::test *bar);
private:
- Q_PROPERTY(N2::test *bar READ getBar NOTIFY barChanged)
+ Q_PROPERTY(N2::test *bar READ getBar NOTIFY barChanged FINAL)
};
})--";
testDocuments << CppTestDocument::create("file.h", original, expected);
@@ -3303,7 +3303,7 @@ void QuickfixTest::testGenerateGetterSetterAnonymousClass()
void fooChanged();
private:
- Q_PROPERTY(int foo READ foo WRITE setFoo RESET resetFoo NOTIFY fooChanged)
+ Q_PROPERTY(int foo READ foo WRITE setFoo RESET resetFoo NOTIFY fooChanged FINAL)
} bar;
)";
testDocuments << CppTestDocument::create("file.h", original, expected);
@@ -3334,7 +3334,7 @@ public:
signals:
void barChanged();
private:
- Q_PROPERTY(int bar READ getBar WRITE setBar RESET resetBar NOTIFY barChanged)
+ Q_PROPERTY(int bar READ getBar WRITE setBar RESET resetBar NOTIFY barChanged FINAL)
};
inline int Foo::getBar() const
@@ -3560,8 +3560,8 @@ private:
int m_bar;
int bar2_;
QString bar3;
- Q_PROPERTY(int bar2 READ getBar2 WRITE setBar2 RESET resetBar2 NOTIFY bar2Changed)
- Q_PROPERTY(QString bar3 READ getBar3 WRITE setBar3 RESET resetBar3 NOTIFY bar3Changed)
+ Q_PROPERTY(int bar2 READ getBar2 WRITE setBar2 RESET resetBar2 NOTIFY bar2Changed FINAL)
+ Q_PROPERTY(QString bar3 READ getBar3 WRITE setBar3 RESET resetBar3 NOTIFY bar3Changed FINAL)
};
inline void Foo::resetBar()
{
@@ -3780,7 +3780,7 @@ void QuickfixTest::testInsertQtPropertyMembers()
QuickFixOperationTest({CppTestDocument::create("file.cpp", original, expected)}, &factory);
}
-void QuickfixTest::testInsertMemberFromInitialization_data()
+void QuickfixTest::testInsertMemberFromUse_data()
{
QTest::addColumn<QByteArray>("original");
QTest::addColumn<QByteArray>("expected");
@@ -3852,9 +3852,241 @@ void QuickfixTest::testInsertMemberFromInitialization_data()
" int m_x;\n"
"};\n";
QTest::addRow("initialization via function call") << original << expected;
+
+ original =
+ "struct S {\n\n};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { m_s.@value = v; }\n"
+ "private:\n"
+ " S m_s;\n"
+ "};\n";
+ expected =
+ "struct S {\n\n"
+ " int value;\n"
+ "};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { m_s.value = v; }\n"
+ "private:\n"
+ " S m_s;\n"
+ "};\n";
+ QTest::addRow("add member to other struct") << original << expected;
+
+ original =
+ "struct S {\n\n};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { S::@value = v; }\n"
+ "};\n";
+ expected =
+ "struct S {\n\n"
+ " static int value;\n"
+ "};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { S::value = v; }\n"
+ "};\n";
+ QTest::addRow("add static member to other struct (explicit)") << original << expected;
+
+ original =
+ "struct S {\n\n};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { m_s.@value = v; }\n"
+ "private:\n"
+ " static S m_s;\n"
+ "};\n";
+ expected =
+ "struct S {\n\n"
+ " static int value;\n"
+ "};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { m_s.value = v; }\n"
+ "private:\n"
+ " static S m_s;\n"
+ "};\n";
+ QTest::addRow("add static member to other struct (implicit)") << original << expected;
+
+ original =
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v);\n"
+ "};\n"
+ "void C::setValue(int v) { this->@m_value = v; }\n";
+ expected =
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v);\n"
+ "private:\n"
+ " int m_value;\n"
+ "};\n"
+ "void C::setValue(int v) { this->@m_value = v; }\n";
+ QTest::addRow("add member to this (explicit)") << original << expected;
+
+ original =
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { @m_value = v; }\n"
+ "};\n";
+ expected =
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { m_value = v; }\n"
+ "private:\n"
+ " int m_value;\n"
+ "};\n";
+ QTest::addRow("add member to this (implicit)") << original << expected;
+
+ original =
+ "class C {\n"
+ "public:\n"
+ " static void setValue(int v) { @m_value = v; }\n"
+ "};\n";
+ expected =
+ "class C {\n"
+ "public:\n"
+ " static void setValue(int v) { m_value = v; }\n"
+ "private:\n"
+ " static int m_value;\n"
+ "};\n";
+ QTest::addRow("add static member to this (inline)") << original << expected;
+
+ original =
+ "class C {\n"
+ "public:\n"
+ " static void setValue(int v);\n"
+ "};\n"
+ "void C::setValue(int v) { @m_value = v; }\n";
+ expected =
+ "class C {\n"
+ "public:\n"
+ " static void setValue(int v);\n"
+ "private:\n"
+ " static int m_value;\n"
+ "};\n"
+ "void C::setValue(int v) { @m_value = v; }\n";
+ QTest::addRow("add static member to this (non-inline)") << original << expected;
+
+ original =
+ "struct S {\n\n};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { m_s.@setValue(v); }\n"
+ "private:\n"
+ " S m_s;\n"
+ "};\n";
+ expected =
+ "struct S {\n\n"
+ " void setValue(int);\n"
+ "};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { m_s.setValue(v); }\n"
+ "private:\n"
+ " S m_s;\n"
+ "};\n";
+ QTest::addRow("add member function to other struct") << original << expected;
+
+ original =
+ "struct S {\n\n};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { S::@setValue(v); }\n"
+ "};\n";
+ expected =
+ "struct S {\n\n"
+ " static void setValue(int);\n"
+ "};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { S::setValue(v); }\n"
+ "};\n";
+ QTest::addRow("add static member function to other struct (explicit)") << original << expected;
+
+ original =
+ "struct S {\n\n};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { m_s.@setValue(v); }\n"
+ "private:\n"
+ " static S m_s;\n"
+ "};\n";
+ expected =
+ "struct S {\n\n"
+ " static void setValue(int);\n"
+ "};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { m_s.setValue(v); }\n"
+ "private:\n"
+ " static S m_s;\n"
+ "};\n";
+ QTest::addRow("add static member function to other struct (implicit)") << original << expected;
+
+ original =
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v);\n"
+ "};\n"
+ "void C::setValue(int v) { this->@setValueInternal(v); }\n";
+ expected =
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v);\n"
+ "private:\n"
+ " void setValueInternal(int);\n"
+ "};\n"
+ "void C::setValue(int v) { this->setValueInternal(v); }\n";
+ QTest::addRow("add member function to this (explicit)") << original << expected;
+
+ original =
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { @setValueInternal(v); }\n"
+ "};\n";
+ expected =
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { setValueInternal(v); }\n"
+ "private:\n"
+ " void setValueInternal(int);\n"
+ "};\n";
+ QTest::addRow("add member function to this (implicit)") << original << expected;
+
+ original =
+ "class C {\n"
+ "public:\n"
+ " static int value() { int i = @valueInternal(); return i; }\n"
+ "};\n";
+ expected =
+ "class C {\n"
+ "public:\n"
+ " static int value() { int i = @valueInternal(); return i; }\n"
+ "private:\n"
+ " static int valueInternal();\n"
+ "};\n";
+ QTest::addRow("add static member function to this (inline)") << original << expected;
+
+ original =
+ "class C {\n"
+ "public:\n"
+ " static int value();\n"
+ "};\n"
+ "int C::value() { return @valueInternal(); }\n";
+ expected =
+ "class C {\n"
+ "public:\n"
+ " static int value();\n"
+ "private:\n"
+ " static int valueInternal();\n"
+ "};\n"
+ "int C::value() { return valueInternal(); }\n";
+ QTest::addRow("add static member function to this (non-inline)") << original << expected;
}
-void QuickfixTest::testInsertMemberFromInitialization()
+void QuickfixTest::testInsertMemberFromUse()
{
QFETCH(QByteArray, original);
QFETCH(QByteArray, expected);
@@ -3863,7 +4095,8 @@ void QuickfixTest::testInsertMemberFromInitialization()
CppTestDocument::create("file.h", original, expected)
});
- InsertMemberFromInitialization factory;
+ AddDeclarationForUndeclaredIdentifier factory;
+ factory.setMembersOnly();
QuickFixOperationTest(testDocuments, &factory);
}
diff --git a/src/plugins/cppeditor/cppquickfix_test.h b/src/plugins/cppeditor/cppquickfix_test.h
index 56cacb367c..2d0dd0ebc9 100644
--- a/src/plugins/cppeditor/cppquickfix_test.h
+++ b/src/plugins/cppeditor/cppquickfix_test.h
@@ -101,8 +101,8 @@ private slots:
void testInsertQtPropertyMembers_data();
void testInsertQtPropertyMembers();
- void testInsertMemberFromInitialization_data();
- void testInsertMemberFromInitialization();
+ void testInsertMemberFromUse_data();
+ void testInsertMemberFromUse();
void testConvertQt4ConnectConnectOutOfClass();
void testConvertQt4ConnectConnectWithinClass_data();
diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp
index 3dae225cea..85489b877e 100644
--- a/src/plugins/cppeditor/cppquickfixes.cpp
+++ b/src/plugins/cppeditor/cppquickfixes.cpp
@@ -10,6 +10,7 @@
#include "cppeditorwidget.h"
#include "cppfunctiondecldeflink.h"
#include "cppinsertvirtualmethods.h"
+#include "cpplocatordata.h"
#include "cpppointerdeclarationformatter.h"
#include "cppquickfixassistant.h"
#include "cppquickfixprojectsettings.h"
@@ -26,6 +27,7 @@
#include <cplusplus/ASTPath.h>
#include <cplusplus/CPlusPlusForwardDeclarations.h>
#include <cplusplus/CppRewriter.h>
+#include <cplusplus/NamePrettyPrinter.h>
#include <cplusplus/TypeOfExpression.h>
#include <cplusplus/TypePrettyPrinter.h>
@@ -33,7 +35,7 @@
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/algorithm.h>
#include <utils/basetreeview.h>
@@ -301,6 +303,64 @@ ClassSpecifierAST *astForClassOperations(const CppQuickFixInterface &interface)
return nullptr;
}
+QString nameString(const NameAST *name)
+{
+ return CppCodeStyleSettings::currentProjectCodeStyleOverview().prettyName(name->name);
+}
+
+// FIXME: Needs to consider the scope at the insertion site.
+QString declFromExpr(const TypeOrExpr &typeOrExpr, const CallAST *call, const NameAST *varName,
+ const Snapshot &snapshot, const LookupContext &context,
+ const CppRefactoringFilePtr &file)
+{
+ const auto getTypeFromUser = [varName, call]() -> QString {
+ if (call)
+ return {};
+ const QString typeFromUser = QInputDialog::getText(Core::ICore::dialogParent(),
+ Tr::tr("Provide the type"),
+ Tr::tr("Data type:"), QLineEdit::Normal);
+ if (!typeFromUser.isEmpty())
+ return typeFromUser + ' ' + nameString(varName);
+ return {};
+ };
+ const auto getTypeOfExpr = [&](const ExpressionAST *expr) -> FullySpecifiedType {
+ TypeOfExpression typeOfExpression;
+ typeOfExpression.init(file->cppDocument(), snapshot, context.bindings());
+ Scope *scope = file->scopeAt(expr->firstToken());
+ const QList<LookupItem> result = typeOfExpression(
+ file->textOf(expr).toUtf8(), scope, TypeOfExpression::Preprocess);
+ if (result.isEmpty())
+ return {};
+
+ SubstitutionEnvironment env;
+ env.setContext(context);
+ env.switchScope(result.first().scope());
+ ClassOrNamespace *con = typeOfExpression.context().lookupType(scope);
+ if (!con)
+ con = typeOfExpression.context().globalNamespace();
+ UseMinimalNames q(con);
+ env.enter(&q);
+
+ Control *control = context.bindings()->control().data();
+ return rewriteType(result.first().type(), &env, control);
+ };
+
+ const Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ const FullySpecifiedType type = std::holds_alternative<FullySpecifiedType>(typeOrExpr)
+ ? std::get<FullySpecifiedType>(typeOrExpr)
+ : getTypeOfExpr(std::get<const ExpressionAST *>(typeOrExpr));
+ if (!call)
+ return type.isValid() ? oo.prettyType(type, varName->name) : getTypeFromUser();
+
+ Function func(file->cppDocument()->translationUnit(), 0, varName->name);
+ for (ExpressionListAST *it = call->expression_list; it; it = it->next) {
+ Argument * const arg = new Argument(nullptr, 0, nullptr);
+ arg->setType(getTypeOfExpr(it->value));
+ func.addMember(arg);
+ }
+ return oo.prettyType(type) + ' ' + oo.prettyType(func.type(), varName->name);
+}
+
} // anonymous namespace
namespace {
@@ -1610,33 +1670,8 @@ private:
if (currentFile->cppDocument()->languageFeatures().cxx11Enabled && settings->useAuto)
return "auto " + oo.prettyName(simpleNameAST->name);
-
- TypeOfExpression typeOfExpression;
- typeOfExpression.init(semanticInfo().doc, snapshot(), context().bindings());
- Scope *scope = currentFile->scopeAt(binaryAST->firstToken());
- const QList<LookupItem> result =
- typeOfExpression(currentFile->textOf(binaryAST->right_expression).toUtf8(),
- scope,
- TypeOfExpression::Preprocess);
-
- if (!result.isEmpty()) {
- SubstitutionEnvironment env;
- env.setContext(context());
- env.switchScope(result.first().scope());
- ClassOrNamespace *con = typeOfExpression.context().lookupType(scope);
- if (!con)
- con = typeOfExpression.context().globalNamespace();
- UseMinimalNames q(con);
- env.enter(&q);
-
- Control *control = context().bindings()->control().data();
- FullySpecifiedType tn = rewriteType(result.first().type(), &env, control);
-
- QString declaration = oo.prettyType(tn, simpleNameAST->name);
- return declaration;
- }
-
- return {};
+ return declFromExpr(binaryAST->right_expression, nullptr, simpleNameAST, snapshot(),
+ context(), currentFile);
}
const BinaryExpressionAST *binaryAST;
@@ -1645,42 +1680,6 @@ private:
} // anonymous namespace
-void AddLocalDeclaration::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- const QList<AST *> &path = interface.path();
- CppRefactoringFilePtr file = interface.currentFile();
-
- for (int index = path.size() - 1; index != -1; --index) {
- if (BinaryExpressionAST *binary = path.at(index)->asBinaryExpression()) {
- if (binary->left_expression && binary->right_expression
- && file->tokenAt(binary->binary_op_token).is(T_EQUAL)) {
- IdExpressionAST *idExpr = binary->left_expression->asIdExpression();
- if (interface.isCursorOn(binary->left_expression) && idExpr
- && idExpr->name->asSimpleName() != nullptr) {
- SimpleNameAST *nameAST = idExpr->name->asSimpleName();
- const QList<LookupItem> results = interface.context().lookup(nameAST->name, file->scopeAt(nameAST->firstToken()));
- Declaration *decl = nullptr;
- for (const LookupItem &r : results) {
- if (!r.declaration())
- continue;
- if (Declaration *d = r.declaration()->asDeclaration()) {
- if (!d->type()->asFunctionType()) {
- decl = d;
- break;
- }
- }
- }
-
- if (!decl) {
- result << new AddLocalDeclarationOp(interface, index, binary, nameAST);
- return;
- }
- }
- }
- }
- }
-}
-
namespace {
class ConvertToCamelCaseOp: public CppQuickFixOperation
@@ -1990,45 +1989,45 @@ Snapshot forwardingHeaders(const CppQuickFixInterface &interface)
return result;
}
-bool matchName(const Name *name, QList<Core::LocatorFilterEntry> *matches, QString *className) {
+QList<IndexItem::Ptr> matchName(const Name *name, QString *className)
+{
if (!name)
- return false;
+ return {};
QString simpleName;
- if (Core::ILocatorFilter *classesFilter = CppModelManager::instance()->classesFilter()) {
- QFutureInterface<Core::LocatorFilterEntry> dummy;
-
- const Overview oo;
- if (const QualifiedNameId *qualifiedName = name->asQualifiedNameId()) {
- const Name *name = qualifiedName->name();
- if (const TemplateNameId *templateName = name->asTemplateNameId()) {
- *className = templateNameAsString(templateName);
- } else {
- simpleName = oo.prettyName(name);
- *className = simpleName;
- *matches = classesFilter->matchesFor(dummy, *className);
- if (matches->empty()) {
- if (const Name *name = qualifiedName->base()) {
- if (const TemplateNameId *templateName = name->asTemplateNameId())
- *className = templateNameAsString(templateName);
- else
- *className = oo.prettyName(name);
- }
- }
- }
- } else if (const TemplateNameId *templateName = name->asTemplateNameId()) {
+ QList<IndexItem::Ptr> matches;
+ CppLocatorData *locatorData = CppModelManager::instance()->locatorData();
+ const Overview oo;
+ if (const QualifiedNameId *qualifiedName = name->asQualifiedNameId()) {
+ const Name *name = qualifiedName->name();
+ if (const TemplateNameId *templateName = name->asTemplateNameId()) {
*className = templateNameAsString(templateName);
} else {
- *className = oo.prettyName(name);
- }
-
- if (matches->empty())
- *matches = classesFilter->matchesFor(dummy, *className);
- if (matches->empty() && !simpleName.isEmpty())
+ simpleName = oo.prettyName(name);
*className = simpleName;
+ matches = locatorData->findSymbols(IndexItem::Class, *className);
+ if (matches.isEmpty()) {
+ if (const Name *name = qualifiedName->base()) {
+ if (const TemplateNameId *templateName = name->asTemplateNameId())
+ *className = templateNameAsString(templateName);
+ else
+ *className = oo.prettyName(name);
+ }
+ }
+ }
+ } else if (const TemplateNameId *templateName = name->asTemplateNameId()) {
+ *className = templateNameAsString(templateName);
+ } else {
+ *className = oo.prettyName(name);
}
- return !matches->empty();
+ if (matches.isEmpty())
+ matches = locatorData->findSymbols(IndexItem::Class, *className);
+
+ if (matches.isEmpty() && !simpleName.isEmpty())
+ *className = simpleName;
+
+ return matches;
}
} // anonymous namespace
@@ -2045,17 +2044,16 @@ void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interfa
return;
QString className;
- QList<Core::LocatorFilterEntry> matches;
const QString currentDocumentFilePath = interface.semanticInfo().doc->filePath().toString();
const ProjectExplorer::HeaderPaths headerPaths = relevantHeaderPaths(currentDocumentFilePath);
FilePaths headers;
+ const QList<IndexItem::Ptr> matches = matchName(nameAst->name, &className);
// Find an include file through the locator
- if (matchName(nameAst->name, &matches, &className)) {
+ if (!matches.isEmpty()) {
QList<IndexItem::Ptr> indexItems;
const Snapshot forwardHeaders = forwardingHeaders(interface);
- for (const Core::LocatorFilterEntry &entry : std::as_const(matches)) {
- IndexItem::Ptr info = entry.internalData.value<IndexItem::Ptr>();
+ for (const IndexItem::Ptr &info : matches) {
if (!info || info->symbolName() != className)
continue;
indexItems << info;
@@ -2935,70 +2933,239 @@ class InsertMemberFromInitializationOp : public CppQuickFixOperation
{
public:
InsertMemberFromInitializationOp(
- const CppQuickFixInterface &interface,
- const Class *theClass,
- const QString &member,
- const QString &type)
- : CppQuickFixOperation(interface), m_class(theClass), m_member(member), m_type(type)
- {
- setDescription(Tr::tr("Add Class Member \"%1\"").arg(m_member));
+ const CppQuickFixInterface &interface,
+ const Class *theClass,
+ const NameAST *memberName,
+ const TypeOrExpr &typeOrExpr,
+ const CallAST *call,
+ InsertionPointLocator::AccessSpec accessSpec,
+ bool makeStatic)
+ : CppQuickFixOperation(interface),
+ m_class(theClass), m_memberName(memberName), m_typeOrExpr(typeOrExpr), m_call(call),
+ m_accessSpec(accessSpec), m_makeStatic(makeStatic)
+ {
+ if (call)
+ setDescription(Tr::tr("Add Member Function \"%1\"").arg(nameString(memberName)));
+ else
+ setDescription(Tr::tr("Add Class Member \"%1\"").arg(nameString(memberName)));
}
private:
void perform() override
{
- QString type = m_type;
- if (type.isEmpty()) {
- type = QInputDialog::getText(
- Core::ICore::dialogParent(),
- Tr::tr("Provide the type"),
- Tr::tr("Data type:"),
- QLineEdit::Normal);
- }
- if (type.isEmpty())
+ QString decl = declFromExpr(m_typeOrExpr, m_call, m_memberName, snapshot(), context(),
+ currentFile());
+ if (decl.isEmpty())
return;
+ if (m_makeStatic)
+ decl.prepend("static ");
const CppRefactoringChanges refactoring(snapshot());
const InsertionPointLocator locator(refactoring);
const FilePath filePath = FilePath::fromUtf8(m_class->fileName());
const InsertionLocation loc = locator.methodDeclarationInClass(
- filePath, m_class, InsertionPointLocator::Private);
+ filePath, m_class, m_accessSpec);
QTC_ASSERT(loc.isValid(), return);
CppRefactoringFilePtr targetFile = refactoring.file(filePath);
const int targetPosition1 = targetFile->position(loc.line(), loc.column());
const int targetPosition2 = qMax(0, targetFile->position(loc.line(), 1) - 1);
ChangeSet target;
- target.insert(targetPosition1, loc.prefix() + type + ' ' + m_member + ";\n");
+ target.insert(targetPosition1, loc.prefix() + decl + ";\n");
targetFile->setChangeSet(target);
targetFile->appendIndentRange(ChangeSet::Range(targetPosition2, targetPosition1));
targetFile->apply();
}
const Class * const m_class;
- const QString m_member;
- const QString m_type;
+ const NameAST * const m_memberName;
+ const TypeOrExpr m_typeOrExpr;
+ const CallAST * m_call;
+ const InsertionPointLocator::AccessSpec m_accessSpec;
+ const bool m_makeStatic;
};
-void InsertMemberFromInitialization::match(const CppQuickFixInterface &interface,
- QuickFixOperations &result)
+void AddDeclarationForUndeclaredIdentifier::match(const CppQuickFixInterface &interface,
+ QuickFixOperations &result)
{
- // First check whether we are on a member initialization.
- const QList<AST *> path = interface.path();
- const int size = path.size();
- if (size < 4)
+ // Are we on a name?
+ const QList<AST *> &path = interface.path();
+ if (path.isEmpty())
return;
- const SimpleNameAST * const name = path.at(size - 1)->asSimpleName();
- if (!name)
+ if (!path.last()->asSimpleName())
+ return;
+
+ // Special case: Member initializer.
+ if (!checkForMemberInitializer(interface, result))
+ return;
+
+ // Are we inside a function?
+ const FunctionDefinitionAST *func = nullptr;
+ for (auto it = path.rbegin(); !func && it != path.rend(); ++it)
+ func = (*it)->asFunctionDefinition();
+ if (!func)
return;
+
+ // Is this name declared somewhere already?
+ const CursorInEditor cursorInEditor(interface.cursor(), interface.filePath(),
+ interface.editor(), interface.editor()->textDocument());
+ const auto followSymbolFallback = [&](const Link &link) {
+ if (!link.hasValidTarget())
+ collectOperations(interface, result);
+ };
+ CppModelManager::followSymbol(cursorInEditor, followSymbolFallback, false, false,
+ CppModelManager::Backend::Builtin);
+}
+
+void AddDeclarationForUndeclaredIdentifier::collectOperations(
+ const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result)
+{
+ const QList<AST *> &path = interface.path();
+ const CppRefactoringFilePtr &file = interface.currentFile();
+ for (int index = path.size() - 1; index != -1; --index) {
+ if (const auto call = path.at(index)->asCall())
+ return handleCall(call, interface, result);
+
+ // We only trigger if the identifier appears on the left-hand side of an
+ // assignment expression.
+ const auto binExpr = path.at(index)->asBinaryExpression();
+ if (!binExpr)
+ continue;
+ if (!binExpr->left_expression || !binExpr->right_expression
+ || file->tokenAt(binExpr->binary_op_token).kind() != T_EQUAL
+ || !interface.isCursorOn(binExpr->left_expression)) {
+ return;
+ }
+
+ // In the case of "a.|b = c", find out the type of a, locate the class declaration
+ // and add a member b there.
+ if (const auto memberAccess = binExpr->left_expression->asMemberAccess()) {
+ if (interface.isCursorOn(memberAccess->member_name)
+ && memberAccess->member_name == path.last()) {
+ maybeAddMember(interface, file->scopeAt(memberAccess->firstToken()),
+ file->textOf(memberAccess->base_expression).toUtf8(),
+ binExpr->right_expression, nullptr, result);
+ }
+ return;
+ }
+
+ const auto idExpr = binExpr->left_expression->asIdExpression();
+ if (!idExpr || !idExpr->name)
+ return;
+
+ // In the case of "A::|b = c", add a static member b to A.
+ if (const auto qualName = idExpr->name->asQualifiedName()) {
+ return maybeAddStaticMember(interface, qualName, binExpr->right_expression, nullptr,
+ result);
+ }
+
+ // For an unqualified access, offer a local declaration and, if we are
+ // in a member function, a member declaration.
+ if (const auto simpleName = idExpr->name->asSimpleName()) {
+ if (!m_membersOnly)
+ result << new AddLocalDeclarationOp(interface, index, binExpr, simpleName);
+ maybeAddMember(interface, file->scopeAt(idExpr->firstToken()), "this",
+ binExpr->right_expression, nullptr, result);
+ return;
+ }
+ }
+}
+
+void AddDeclarationForUndeclaredIdentifier::handleCall(
+ const CallAST *call, const CppQuickFixInterface &interface,
+ TextEditor::QuickFixOperations &result)
+{
+ if (!call->base_expression)
+ return;
+
+ // In order to find out the return type, we need to check the context of the call.
+ // If it is a statement expression, the type is void, if it's a binary expression,
+ // we assume the type of the other side of the expression, if it's a return statement,
+ // we use the return type of the surrounding function, and if it's a declaration,
+ // we use the type of the variable. Other cases are not supported.
+ const QList<AST *> &path = interface.path();
+ const CppRefactoringFilePtr &file = interface.currentFile();
+ TypeOrExpr returnTypeOrExpr;
+ for (auto it = path.rbegin(); it != path.rend(); ++it) {
+ if ((*it)->asCompoundStatement())
+ return;
+ if ((*it)->asExpressionStatement()) {
+ returnTypeOrExpr = FullySpecifiedType(new VoidType);
+ break;
+ }
+ if (const auto binExpr = (*it)->asBinaryExpression()) {
+ returnTypeOrExpr = interface.isCursorOn(binExpr->left_expression)
+ ? binExpr->right_expression : binExpr->left_expression;
+ break;
+ }
+ if (const auto returnExpr = (*it)->asReturnStatement()) {
+ for (auto it2 = std::next(it); it2 != path.rend(); ++it2) {
+ if (const auto func = (*it2)->asFunctionDefinition()) {
+ if (!func->symbol)
+ return;
+ returnTypeOrExpr = func->symbol->returnType();
+ break;
+ }
+ }
+ break;
+ }
+ if (const auto declarator = (*it)->asDeclarator()) {
+ if (!interface.isCursorOn(declarator->initializer))
+ return;
+ const auto decl = (*std::next(it))->asSimpleDeclaration();
+ if (!decl || !decl->symbols)
+ return;
+ if (!decl->symbols->value->type().isValid())
+ return;
+ returnTypeOrExpr = decl->symbols->value->type();
+ break;
+ }
+ }
+
+ if (std::holds_alternative<const ExpressionAST *>(returnTypeOrExpr)
+ && !std::get<const ExpressionAST *>(returnTypeOrExpr)) {
+ return;
+ }
+
+ // a.f()
+ if (const auto memberAccess = call->base_expression->asMemberAccess()) {
+ if (!interface.isCursorOn(memberAccess->member_name))
+ return;
+ maybeAddMember(
+ interface, file->scopeAt(call->firstToken()),
+ file->textOf(memberAccess->base_expression).toUtf8(), returnTypeOrExpr, call, result);
+ }
+
+ const auto idExpr = call->base_expression->asIdExpression();
+ if (!idExpr || !idExpr->name)
+ return;
+
+ // A::f()
+ if (const auto qualName = idExpr->name->asQualifiedName())
+ return maybeAddStaticMember(interface, qualName, returnTypeOrExpr, call, result);
+
+ // f()
+ if (const auto simpleName = idExpr->name->asSimpleName()) {
+ maybeAddMember(interface, file->scopeAt(idExpr->firstToken()), "this",
+ returnTypeOrExpr, call, result);
+ }
+}
+
+bool AddDeclarationForUndeclaredIdentifier::checkForMemberInitializer(
+ const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result)
+{
+ const QList<AST *> &path = interface.path();
+ const int size = path.size();
+ if (size < 4)
+ return true;
const MemInitializerAST * const memInitializer = path.at(size - 2)->asMemInitializer();
if (!memInitializer)
- return;
+ return true;
if (!path.at(size - 3)->asCtorInitializer())
- return;
+ return true;
const FunctionDefinitionAST * ctor = path.at(size - 4)->asFunctionDefinition();
if (!ctor)
- return;
+ return false;
// Now find the class.
const Class *theClass = nullptr;
@@ -3011,65 +3178,141 @@ void InsertMemberFromInitialization::match(const CppQuickFixInterface &interface
// Out-of-line constructor. We need to find the class.
SymbolFinder finder;
const QList<Declaration *> matches = finder.findMatchingDeclaration(
- LookupContext(interface.currentFile()->cppDocument(), interface.snapshot()),
- ctor->symbol);
+ LookupContext(interface.currentFile()->cppDocument(), interface.snapshot()),
+ ctor->symbol);
if (!matches.isEmpty())
theClass = matches.first()->enclosingClass();
}
if (!theClass)
- return;
+ return false;
+
+ const SimpleNameAST * const name = path.at(size - 1)->asSimpleName();
+ QTC_ASSERT(name, return false);
// Check whether the member exists already.
if (theClass->find(interface.currentFile()->cppDocument()->translationUnit()->identifier(
- name->identifier_token))) {
- return;
+ name->identifier_token))) {
+ return false;
}
- const QString type = getType(interface, memInitializer, ctor);
- const Identifier * const memberId = interface.currentFile()->cppDocument()
- ->translationUnit()->identifier(name->identifier_token);
- const QString member = QString::fromUtf8(memberId->chars(), memberId->size());
+ result << new InsertMemberFromInitializationOp(
+ interface, theClass, memInitializer->name->asSimpleName(), memInitializer->expression,
+ nullptr, InsertionPointLocator::Private, false);
+ return false;
+}
+
+void AddDeclarationForUndeclaredIdentifier::maybeAddMember(
+ const CppQuickFixInterface &interface, Scope *scope, const QByteArray &classTypeExpr,
+ const TypeOrExpr &typeOrExpr, const CallAST *call, TextEditor::QuickFixOperations &result)
+{
+ const QList<AST *> &path = interface.path();
+
+ TypeOfExpression typeOfExpression;
+ typeOfExpression.init(interface.semanticInfo().doc, interface.snapshot(),
+ interface.context().bindings());
+ const QList<LookupItem> lhsTypes = typeOfExpression(
+ classTypeExpr, scope,
+ TypeOfExpression::Preprocess);
+ if (lhsTypes.isEmpty())
+ return;
- result << new InsertMemberFromInitializationOp(interface, theClass, member, type);
+ const Type *type = lhsTypes.first().type().type();
+ if (!type)
+ return;
+ if (type->asPointerType()) {
+ type = type->asPointerType()->elementType().type();
+ if (!type)
+ return;
+ }
+ const auto namedType = type->asNamedType();
+ if (!namedType)
+ return;
+ const ClassOrNamespace * const classOrNamespace
+ = interface.context().lookupType(namedType->name(), scope);
+ if (!classOrNamespace || !classOrNamespace->rootClass())
+ return;
+
+ const Class * const theClass = classOrNamespace->rootClass();
+ bool needsStatic = lhsTypes.first().type().isStatic();
+
+ // If the base expression refers to the same class that the member function is in,
+ // then we want to insert a private member, otherwise a public one.
+ const FunctionDefinitionAST *func = nullptr;
+ for (auto it = path.rbegin(); !func && it != path.rend(); ++it)
+ func = (*it)->asFunctionDefinition();
+ QTC_ASSERT(func, return);
+ InsertionPointLocator::AccessSpec accessSpec = InsertionPointLocator::Public;
+ for (int i = 0; i < theClass->memberCount(); ++i) {
+ if (theClass->memberAt(i) == func->symbol) {
+ accessSpec = InsertionPointLocator::Private;
+ needsStatic = func->symbol->isStatic();
+ break;
+ }
+ }
+ if (accessSpec == InsertionPointLocator::Public) {
+ QList<Declaration *> decls;
+ QList<Declaration *> dummy;
+ SymbolFinder().findMatchingDeclaration(interface.context(), func->symbol, &decls,
+ &dummy, &dummy);
+ for (const Declaration * const decl : std::as_const(decls)) {
+ for (int i = 0; i < theClass->memberCount(); ++i) {
+ if (theClass->memberAt(i) == decl) {
+ accessSpec = InsertionPointLocator::Private;
+ needsStatic = decl->isStatic();
+ break;
+ }
+ }
+ if (accessSpec == InsertionPointLocator::Private)
+ break;
+ }
+ }
+ result << new InsertMemberFromInitializationOp(interface, theClass, path.last()->asName(),
+ typeOrExpr, call, accessSpec, needsStatic);
}
-QString InsertMemberFromInitialization::getType(
- const CppQuickFixInterface &interface,
- const MemInitializerAST *memInitializer,
- const FunctionDefinitionAST *ctor) const
+void AddDeclarationForUndeclaredIdentifier::maybeAddStaticMember(
+ const CppQuickFixInterface &interface, const QualifiedNameAST *qualName,
+ const TypeOrExpr &typeOrExpr, const CallAST *call, TextEditor::QuickFixOperations &result)
{
- // Try to deduce the type: If the initialization expression is just a name
- // (e.g. a constructor argument) or a function call, we don't bother the user.
- if (!memInitializer->expression)
- return {};
- const ExpressionListParenAST * const lParenAst
- = memInitializer->expression->asExpressionListParen();
- if (!lParenAst || !lParenAst->expression_list || !lParenAst->expression_list->value)
- return {};
- const IdExpressionAST *idExpr = lParenAst->expression_list->value->asIdExpression();
- if (!idExpr) { // Not a variable, so check for function call.
- const CallAST * const call = lParenAst->expression_list->value->asCall();
- if (!call || !call->base_expression)
- return {};
- idExpr = call->base_expression->asIdExpression();
+ const QList<AST *> &path = interface.path();
+
+ if (!interface.isCursorOn(qualName->unqualified_name))
+ return;
+ if (qualName->unqualified_name != path.last())
+ return;
+ if (!qualName->nested_name_specifier_list)
+ return;
+
+ const NameAST * const topLevelName
+ = qualName->nested_name_specifier_list->value->class_or_namespace_name;
+ if (!topLevelName)
+ return;
+ ClassOrNamespace * const classOrNamespace = interface.context().lookupType(
+ topLevelName->name, interface.currentFile()->scopeAt(qualName->firstToken()));
+ if (!classOrNamespace)
+ return;
+ QList<const Name *> otherNames;
+ for (auto it = qualName->nested_name_specifier_list->next; it; it = it->next) {
+ if (!it->value || !it->value->class_or_namespace_name)
+ return;
+ otherNames << it->value->class_or_namespace_name->name;
}
- if (!idExpr || !idExpr->name)
- return {};
- LookupContext context(interface.currentFile()->cppDocument(), interface.snapshot());
- const QList<LookupItem> matches = context.lookup(idExpr->name->name, ctor->symbol);
- if (matches.isEmpty())
- return {};
- Overview o = CppCodeStyleSettings::currentProjectCodeStyleOverview();
- TypePrettyPrinter tpp(&o);
- FullySpecifiedType type = matches.first().type();
- if (!type.type())
- return {};
- const Function * const funcType = type.type()->asFunctionType();
- if (funcType)
- type = funcType->returnType();
- return tpp(type);
+ const Class *theClass = nullptr;
+ if (!otherNames.isEmpty()) {
+ const Symbol * const symbol = classOrNamespace->lookupInScope(otherNames);
+ if (!symbol)
+ return;
+ theClass = symbol->asClass();
+ } else {
+ theClass = classOrNamespace->rootClass();
+ }
+ if (theClass) {
+ result << new InsertMemberFromInitializationOp(
+ interface, theClass, path.last()->asName(), typeOrExpr, call,
+ InsertionPointLocator::Public, true);
+ }
}
class MemberFunctionImplSetting
@@ -3697,6 +3940,7 @@ public:
GenerateProperty = 1 << 5,
GenerateConstantProperty = 1 << 6,
HaveExistingQProperty = 1 << 7,
+ Invalid = -1,
};
GenerateGetterSetterOp(const CppQuickFixInterface &interface,
@@ -4182,7 +4426,7 @@ void GetterSetterRefactoringHelper::performGeneration(ExistingGetterSetterData d
propertyDeclaration.append(QLatin1String(" NOTIFY ")).append(data.signalName);
}
- propertyDeclaration.append(QLatin1String(")\n"));
+ propertyDeclaration.append(QLatin1String(" FINAL)\n"));
addHeaderCode(InsertionPointLocator::Private, propertyDeclaration);
}
}
@@ -4394,7 +4638,7 @@ public:
};
using Flag = GenerateGetterSetterOp::GenerateFlag;
constexpr static Flag ColumnFlag[] = {
- static_cast<Flag>(-1),
+ Flag::Invalid,
Flag::GenerateGetter,
Flag::GenerateSetter,
Flag::GenerateSignal,
@@ -7986,7 +8230,7 @@ private:
}
};
- if (const Project *project = SessionManager::projectForFile(filePath())) {
+ if (const Project *project = ProjectManager::projectForFile(filePath())) {
const FilePaths files = project->files(ProjectExplorer::Project::SourceFiles);
QSet<FilePath> projectFiles(files.begin(), files.end());
for (const auto &file : files) {
@@ -9072,7 +9316,6 @@ void createCppQuickFixes()
new SplitIfStatement;
new SplitSimpleDeclaration;
- new AddLocalDeclaration;
new AddBracesToIf;
new RearrangeParamDeclarationList;
new ReformatPointerDeclaration;
@@ -9089,7 +9332,7 @@ void createCppQuickFixes()
new GenerateGettersSettersForClass;
new InsertDeclFromDef;
new InsertDefFromDecl;
- new InsertMemberFromInitialization;
+ new AddDeclarationForUndeclaredIdentifier;
new InsertDefsFromDecls;
new MoveFuncDefOutside;
diff --git a/src/plugins/cppeditor/cppquickfixes.h b/src/plugins/cppeditor/cppquickfixes.h
index 376bfa99bf..61db3ebf53 100644
--- a/src/plugins/cppeditor/cppquickfixes.h
+++ b/src/plugins/cppeditor/cppquickfixes.h
@@ -3,9 +3,10 @@
#pragma once
-#include "cppeditor_global.h"
#include "cppquickfix.h"
+#include <variant>
+
///
/// Adding New Quick Fixes
///
@@ -16,6 +17,7 @@
namespace CppEditor {
namespace Internal {
+using TypeOrExpr = std::variant<const CPlusPlus::ExpressionAST *, CPlusPlus::FullySpecifiedType>;
void createCppQuickFixes();
void destroyCppQuickFixes();
@@ -285,23 +287,6 @@ public:
};
/*!
- Rewrites
- a = foo();
-
- As
- Type a = foo();
-
- Where Type is the return type of foo()
-
- Activates on: the assignee, if the type of the right-hand side of the assignment is known.
-*/
-class AddLocalDeclaration: public CppQuickFixFactory
-{
-public:
- void match(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
-};
-
-/*!
Add curly braces to a if statement that doesn't already contain a
compound statement. I.e.
@@ -374,20 +359,36 @@ public:
void match(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
};
-/*!
- Adds a class member from an initialization in the constructor.
- */
-class InsertMemberFromInitialization : public CppQuickFixFactory
+class AddDeclarationForUndeclaredIdentifier : public CppQuickFixFactory
{
public:
void match(const CppQuickFixInterface &interface,
TextEditor::QuickFixOperations &result) override;
+#ifdef WITH_TESTS
+ void setMembersOnly() { m_membersOnly = true; }
+#endif
+
private:
- QString getType(
- const CppQuickFixInterface &interface,
- const CPlusPlus::MemInitializerAST *memInitializer,
- const CPlusPlus::FunctionDefinitionAST *ctor) const;
+ void collectOperations(const CppQuickFixInterface &interface,
+ TextEditor::QuickFixOperations &result);
+ void handleCall(const CPlusPlus::CallAST *call, const CppQuickFixInterface &interface,
+ TextEditor::QuickFixOperations &result);
+
+ // Returns whether to still do other checks.
+ bool checkForMemberInitializer(const CppQuickFixInterface &interface,
+ TextEditor::QuickFixOperations &result);
+
+ void maybeAddMember(const CppQuickFixInterface &interface, CPlusPlus::Scope *scope,
+ const QByteArray &classTypeExpr, const TypeOrExpr &typeOrExpr,
+ const CPlusPlus::CallAST *call, TextEditor::QuickFixOperations &result);
+
+ void maybeAddStaticMember(
+ const CppQuickFixInterface &interface, const CPlusPlus::QualifiedNameAST *qualName,
+ const TypeOrExpr &typeOrExpr, const CPlusPlus::CallAST *call,
+ TextEditor::QuickFixOperations &result);
+
+ bool m_membersOnly = false;
};
/*!
diff --git a/src/plugins/cppeditor/cppquickfixprojectsettingswidget.cpp b/src/plugins/cppeditor/cppquickfixprojectsettingswidget.cpp
index c0fb864665..c82985043f 100644
--- a/src/plugins/cppeditor/cppquickfixprojectsettingswidget.cpp
+++ b/src/plugins/cppeditor/cppquickfixprojectsettingswidget.cpp
@@ -28,7 +28,7 @@ CppQuickFixProjectSettingsWidget::CppQuickFixProjectSettingsWidget(ProjectExplor
auto layout = new QVBoxLayout();
gridLayout->addLayout(layout, 2, 0, 1, 2);
- m_settingsWidget = new CppQuickFixSettingsWidget(this);
+ m_settingsWidget = new CppQuickFixSettingsWidget;
m_settingsWidget->loadSettings(m_projectSettings->getSettings());
if (QLayout *layout = m_settingsWidget->layout())
diff --git a/src/plugins/cppeditor/cppquickfixsettingspage.cpp b/src/plugins/cppeditor/cppquickfixsettingspage.cpp
index 05aa4c586a..735dbea3b4 100644
--- a/src/plugins/cppeditor/cppquickfixsettingspage.cpp
+++ b/src/plugins/cppeditor/cppquickfixsettingspage.cpp
@@ -5,12 +5,8 @@
#include "cppeditorconstants.h"
#include "cppeditortr.h"
-#include "cppquickfixsettings.h"
#include "cppquickfixsettingswidget.h"
-#include <QCoreApplication>
-#include <QtDebug>
-
namespace CppEditor::Internal {
CppQuickFixSettingsPage::CppQuickFixSettingsPage()
@@ -18,27 +14,7 @@ CppQuickFixSettingsPage::CppQuickFixSettingsPage()
setId(Constants::QUICK_FIX_SETTINGS_ID);
setDisplayName(Tr::tr(Constants::QUICK_FIX_SETTINGS_DISPLAY_NAME));
setCategory(Constants::CPP_SETTINGS_CATEGORY);
-}
-
-QWidget *CppQuickFixSettingsPage::widget()
-{
- if (!m_widget) {
- m_widget = new CppQuickFixSettingsWidget;
- m_widget->loadSettings(CppQuickFixSettings::instance());
- }
- return m_widget;
-}
-
-void CppQuickFixSettingsPage::apply()
-{
- const auto s = CppQuickFixSettings::instance();
- m_widget->saveSettings(s);
- s->saveAsGlobalSettings();
-}
-
-void CppQuickFixSettingsPage::finish()
-{
- delete m_widget;
+ setWidgetCreator([] { return new CppQuickFixSettingsWidget; });
}
} // CppEditor::Internal
diff --git a/src/plugins/cppeditor/cppquickfixsettingspage.h b/src/plugins/cppeditor/cppquickfixsettingspage.h
index 3e14fde864..1965aae956 100644
--- a/src/plugins/cppeditor/cppquickfixsettingspage.h
+++ b/src/plugins/cppeditor/cppquickfixsettingspage.h
@@ -2,25 +2,15 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
-#include "coreplugin/dialogs/ioptionspage.h"
-#include <QPointer>
-namespace CppEditor {
-namespace Internal {
-class CppQuickFixSettingsWidget;
+#include <coreplugin/dialogs/ioptionspage.h>
+
+namespace CppEditor::Internal {
class CppQuickFixSettingsPage : public Core::IOptionsPage
{
public:
CppQuickFixSettingsPage();
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-private:
- QPointer<CppQuickFixSettingsWidget> m_widget;
};
-} // namespace Internal
-} // namespace CppEditor
+} // CppEditor::Internal
diff --git a/src/plugins/cppeditor/cppquickfixsettingswidget.cpp b/src/plugins/cppeditor/cppquickfixsettingswidget.cpp
index 689a519ce3..679d140967 100644
--- a/src/plugins/cppeditor/cppquickfixsettingswidget.cpp
+++ b/src/plugins/cppeditor/cppquickfixsettingswidget.cpp
@@ -20,8 +20,6 @@
#include <QSpinBox>
#include <QtDebug>
-using namespace Utils;
-
namespace CppEditor::Internal {
class LineCountSpinBox : public QWidget
@@ -56,7 +54,7 @@ LineCountSpinBox::LineCountSpinBox(QWidget *parent)
m_unitLabel = new QLabel(Tr::tr("lines"));
using namespace Layouting;
- Row { m_checkBox, m_opLabel, m_spinBox, m_unitLabel, }.attachTo(this, WithoutMargins);
+ Row { m_checkBox, m_opLabel, m_spinBox, m_unitLabel, noMargin }.attachTo(this);
auto handleChange = [this] {
updateFields();
@@ -88,9 +86,8 @@ void LineCountSpinBox::updateFields()
m_unitLabel->setEnabled(enabled);
}
-CppQuickFixSettingsWidget::CppQuickFixSettingsWidget(QWidget *parent)
- : QWidget(parent)
- , m_typeSplitter("\\s*,\\s*")
+CppQuickFixSettingsWidget::CppQuickFixSettingsWidget()
+ : m_typeSplitter("\\s*,\\s*")
{
m_lines_getterOutsideClass = new LineCountSpinBox;
m_lines_getterInCppFile = new LineCountSpinBox;
@@ -223,7 +220,8 @@ e.g. name = "m_test_foo_":
Tr::tr("Inside class:"), Tr::tr("Default"), Tr::tr("Default"), br,
Tr::tr("Outside class:"), m_lines_setterOutsideClass, m_lines_getterOutsideClass, br,
Tr::tr("In .cpp file:"), m_lines_setterInCppFile, m_lines_getterInCppFile, br,
- }.attachTo(functionLocationsGrid, WithoutMargins);
+ noMargin,
+ }.attachTo(functionLocationsGrid);
if (QGridLayout *gl = qobject_cast<QGridLayout*>(functionLocationsGrid->layout()))
gl->setHorizontalSpacing(48);
@@ -319,6 +317,8 @@ e.g. name = "m_test_foo_":
connect(m_radioButton_addUsingnamespace, &QRadioButton::clicked, then);
connect(m_radioButton_generateMissingNamespace, &QRadioButton::clicked, then);
connect(m_radioButton_rewriteTypes, &QRadioButton::clicked, then);
+
+ loadSettings(CppQuickFixSettings::instance());
}
void CppQuickFixSettingsWidget::loadSettings(CppQuickFixSettings *settings)
@@ -426,6 +426,13 @@ void CppQuickFixSettingsWidget::saveSettings(CppQuickFixSettings *settings)
}
}
+void CppQuickFixSettingsWidget::apply()
+{
+ const auto s = CppQuickFixSettings::instance();
+ saveSettings(s);
+ s->saveAsGlobalSettings();
+}
+
void CppQuickFixSettingsWidget::currentCustomItemChanged(QListWidgetItem *newItem,
QListWidgetItem *oldItem)
{
diff --git a/src/plugins/cppeditor/cppquickfixsettingswidget.h b/src/plugins/cppeditor/cppquickfixsettingswidget.h
index f921afbff0..e11d81a9f2 100644
--- a/src/plugins/cppeditor/cppquickfixsettingswidget.h
+++ b/src/plugins/cppeditor/cppquickfixsettingswidget.h
@@ -3,6 +3,8 @@
#pragma once
+#include <coreplugin/dialogs/ioptionspage.h>
+
#include <QApplication>
#include <QRegularExpression>
#include <QWidget>
@@ -23,7 +25,7 @@ namespace CppEditor::Internal {
class LineCountSpinBox;
-class CppQuickFixSettingsWidget : public QWidget
+class CppQuickFixSettingsWidget : public Core::IOptionsPageWidget
{
Q_OBJECT
@@ -36,7 +38,7 @@ class CppQuickFixSettingsWidget : public QWidget
};
public:
- explicit CppQuickFixSettingsWidget(QWidget *parent = nullptr);
+ CppQuickFixSettingsWidget();
void loadSettings(CppQuickFixSettings *settings);
void saveSettings(CppQuickFixSettings *settings);
@@ -45,6 +47,7 @@ signals:
void settingsChanged();
private:
+ void apply() final;
void currentCustomItemChanged(QListWidgetItem *newItem, QListWidgetItem *oldItem);
bool m_isLoadingSettings = false;
diff --git a/src/plugins/cppeditor/cpprefactoringchanges.cpp b/src/plugins/cppeditor/cpprefactoringchanges.cpp
index 1cce747fc6..bb34174d06 100644
--- a/src/plugins/cppeditor/cpprefactoringchanges.cpp
+++ b/src/plugins/cppeditor/cpprefactoringchanges.cpp
@@ -57,8 +57,8 @@ CppRefactoringFilePtr CppRefactoringChanges::file(const FilePath &filePath) cons
CppRefactoringFileConstPtr CppRefactoringChanges::fileNoEditor(const FilePath &filePath) const
{
QTextDocument *document = nullptr;
- if (data()->m_workingCopy.contains(filePath))
- document = new QTextDocument(QString::fromUtf8(data()->m_workingCopy.source(filePath)));
+ if (const auto source = data()->m_workingCopy.source(filePath))
+ document = new QTextDocument(QString::fromUtf8(*source));
CppRefactoringFilePtr result(new CppRefactoringFile(document, filePath));
result->m_data = m_data;
diff --git a/src/plugins/cppeditor/cppsemanticinfoupdater.cpp b/src/plugins/cppeditor/cppsemanticinfoupdater.cpp
index 3e2438dbf8..4fc9dd7c66 100644
--- a/src/plugins/cppeditor/cppsemanticinfoupdater.cpp
+++ b/src/plugins/cppeditor/cppsemanticinfoupdater.cpp
@@ -3,11 +3,10 @@
#include "cppsemanticinfoupdater.h"
-#include "cpplocalsymbols.h"
#include "cppmodelmanager.h"
+#include <utils/async.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <cplusplus/Control.h>
#include <cplusplus/CppDocument.h>
@@ -29,11 +28,11 @@ public:
class FuturizedTopLevelDeclarationProcessor: public TopLevelDeclarationProcessor
{
public:
- explicit FuturizedTopLevelDeclarationProcessor(QFutureInterface<void> &future): m_future(future) {}
+ explicit FuturizedTopLevelDeclarationProcessor(QPromise<void> &promise): m_promise(promise) {}
bool processDeclaration(DeclarationAST *) override { return !isCanceled(); }
- bool isCanceled() { return m_future.isCanceled(); }
+ bool isCanceled() { return m_promise.isCanceled(); }
private:
- QFutureInterface<void> m_future;
+ QPromise<void> &m_promise;
};
public:
@@ -49,7 +48,7 @@ public:
bool reuseCurrentSemanticInfo(const SemanticInfo::Source &source, bool emitSignalWhenFinished);
- void update_helper(QFutureInterface<void> &future, const SemanticInfo::Source &source);
+ void update_helper(QPromise<void> &promise, const SemanticInfo::Source &source);
public:
SemanticInfoUpdater *q;
@@ -136,10 +135,10 @@ bool SemanticInfoUpdaterPrivate::reuseCurrentSemanticInfo(const SemanticInfo::So
return false;
}
-void SemanticInfoUpdaterPrivate::update_helper(QFutureInterface<void> &future,
+void SemanticInfoUpdaterPrivate::update_helper(QPromise<void> &promise,
const SemanticInfo::Source &source)
{
- FuturizedTopLevelDeclarationProcessor processor(future);
+ FuturizedTopLevelDeclarationProcessor processor(promise);
update(source, true, &processor);
}
@@ -179,7 +178,7 @@ void SemanticInfoUpdater::updateDetached(const SemanticInfo::Source &source)
return;
}
- d->m_future = Utils::runAsync(CppModelManager::instance()->sharedThreadPool(),
+ d->m_future = Utils::asyncRun(CppModelManager::instance()->sharedThreadPool(),
&SemanticInfoUpdaterPrivate::update_helper, d.data(), source);
}
diff --git a/src/plugins/cppeditor/cppsourceprocessor.cpp b/src/plugins/cppeditor/cppsourceprocessor.cpp
index 66cca58a0f..55dc1f10e0 100644
--- a/src/plugins/cppeditor/cppsourceprocessor.cpp
+++ b/src/plugins/cppeditor/cppsourceprocessor.cpp
@@ -81,7 +81,8 @@ inline const CPlusPlus::Macro revision(const WorkingCopy &workingCopy,
const CPlusPlus::Macro &macro)
{
CPlusPlus::Macro newMacro(macro);
- newMacro.setFileRevision(workingCopy.get(macro.filePath()).second);
+ if (const auto entry = workingCopy.get(macro.filePath()))
+ newMacro.setFileRevision(entry->second);
return newMacro;
}
@@ -189,10 +190,9 @@ bool CppSourceProcessor::getFileContents(const FilePath &absoluteFilePath,
return false;
// Get from working copy
- if (m_workingCopy.contains(absoluteFilePath)) {
- const QPair<QByteArray, unsigned> entry = m_workingCopy.get(absoluteFilePath);
- *contents = entry.first;
- *revision = entry.second;
+ if (const auto entry = m_workingCopy.get(absoluteFilePath)) {
+ *contents = entry->first;
+ *revision = entry->second;
return true;
}
@@ -216,7 +216,7 @@ bool CppSourceProcessor::checkFile(const FilePath &absoluteFilePath) const
{
if (absoluteFilePath.isEmpty()
|| m_included.contains(absoluteFilePath)
- || m_workingCopy.contains(absoluteFilePath)) {
+ || m_workingCopy.get(absoluteFilePath)) {
return true;
}
@@ -281,7 +281,7 @@ FilePath CppSourceProcessor::resolveFile_helper(const FilePath &filePath,
} else {
path = FilePath::fromString(headerPathsIt->path) / fileName;
}
- if (m_workingCopy.contains(path) || checkFile(path))
+ if (m_workingCopy.get(path) || checkFile(path))
return path;
}
}
@@ -468,8 +468,8 @@ void CppSourceProcessor::sourceNeeded(int line, const FilePath &filePath, Includ
document->setUtf8Source(preprocessedCode);
document->keepSourceAndAST();
document->tokenize();
- document->check(m_workingCopy.contains(document->filePath()) ? Document::FullCheck
- : Document::FastCheck);
+ document->check(m_workingCopy.get(document->filePath()) ? Document::FullCheck
+ : Document::FastCheck);
m_documentFinished(document);
diff --git a/src/plugins/cppeditor/cpptoolsjsextension.cpp b/src/plugins/cppeditor/cpptoolsjsextension.cpp
index a3cbfe1204..73a846593c 100644
--- a/src/plugins/cppeditor/cpptoolsjsextension.cpp
+++ b/src/plugins/cppeditor/cpptoolsjsextension.cpp
@@ -10,12 +10,13 @@
#include <coreplugin/icore.h>
#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
-#include <projectexplorer/session.h>
#include <cplusplus/AST.h>
#include <cplusplus/ASTPath.h>
#include <cplusplus/Overview.h>
+
#include <utils/codegeneration.h>
#include <utils/fileutils.h>
@@ -134,13 +135,13 @@ bool CppToolsJsExtension::hasQObjectParent(const QString &klassName) const
// Find class in AST.
const CPlusPlus::Snapshot snapshot = CppModelManager::instance()->snapshot();
const WorkingCopy workingCopy = CppModelManager::instance()->workingCopy();
- QByteArray source = workingCopy.source(item->filePath());
- if (source.isEmpty()) {
+ std::optional<QByteArray> source = workingCopy.source(item->filePath());
+ if (!source) {
const Utils::expected_str<QByteArray> contents = item->filePath().fileContents();
QTC_ASSERT_EXPECTED(contents, return false);
source = *contents;
}
- const auto doc = snapshot.preprocessedDocument(source, item->filePath());
+ const auto doc = snapshot.preprocessedDocument(*source, item->filePath());
if (!doc)
return false;
doc->check();
@@ -234,7 +235,7 @@ QString CppToolsJsExtension::includeStatement(
}
return false;
};
- for (const Project * const p : SessionManager::projects()) {
+ for (const Project * const p : ProjectManager::projects()) {
const Node *theNode = p->rootProjectNode()->findNode(nodeMatchesFileName);
if (theNode) {
const bool sameDir = pathOfIncludingFile == theNode->filePath().toFileInfo().path();
diff --git a/src/plugins/cppeditor/cpptoolsreuse.cpp b/src/plugins/cppeditor/cpptoolsreuse.cpp
index 7d17e30e4b..8f68ed3ec9 100644
--- a/src/plugins/cppeditor/cpptoolsreuse.cpp
+++ b/src/plugins/cppeditor/cpptoolsreuse.cpp
@@ -21,7 +21,9 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/idocument.h>
#include <coreplugin/messagemanager.h>
-#include <projectexplorer/session.h>
+
+#include <projectexplorer/projectmanager.h>
+
#include <texteditor/codeassist/assistinterface.h>
#include <texteditor/textdocument.h>
@@ -29,6 +31,7 @@
#include <cplusplus/LookupContext.h>
#include <cplusplus/Overview.h>
#include <cplusplus/SimpleLexer.h>
+
#include <utils/algorithm.h>
#include <utils/textutils.h>
#include <utils/qtcassert.h>
@@ -263,10 +266,7 @@ const Macro *findCanonicalMacro(const QTextCursor &cursor, Document::Ptr documen
{
QTC_ASSERT(document, return nullptr);
- int line, column;
- Utils::Text::convertPosition(cursor.document(), cursor.position(), &line, &column);
-
- if (const Macro *macro = document->findMacroDefinitionAt(line)) {
+ if (const Macro *macro = document->findMacroDefinitionAt(cursor.blockNumber() + 1)) {
QTextCursor macroCursor = cursor;
const QByteArray name = identifierUnderCursor(&macroCursor).toUtf8();
if (macro->name() == name)
@@ -596,12 +596,12 @@ NamespaceAST *NSCheckerVisitor::currentNamespace()
ProjectExplorer::Project *projectForProjectPart(const ProjectPart &part)
{
- return ProjectExplorer::SessionManager::projectWithProjectFilePath(part.topLevelProject);
+ return ProjectExplorer::ProjectManager::projectWithProjectFilePath(part.topLevelProject);
}
ProjectExplorer::Project *projectForProjectInfo(const ProjectInfo &info)
{
- return ProjectExplorer::SessionManager::projectWithProjectFilePath(info.projectFilePath());
+ return ProjectExplorer::ProjectManager::projectWithProjectFilePath(info.projectFilePath());
}
void openEditor(const Utils::FilePath &filePath, bool inNextSplit, Utils::Id editorId)
diff --git a/src/plugins/cppeditor/cpptoolssettings.cpp b/src/plugins/cppeditor/cpptoolssettings.cpp
index 3d5b4658b8..0b5536fdd7 100644
--- a/src/plugins/cppeditor/cpptoolssettings.cpp
+++ b/src/plugins/cppeditor/cpptoolssettings.cpp
@@ -134,54 +134,6 @@ CppToolsSettings::CppToolsSettings()
// load global settings (after built-in settings are added to the pool)
d->m_globalCodeStyle->fromSettings(QLatin1String(Constants::CPP_SETTINGS_ID), s);
- // legacy handling start (Qt Creator Version < 2.4)
- const bool legacyTransformed =
- s->value(QLatin1String("CppCodeStyleSettings/LegacyTransformed"), false).toBool();
-
- if (!legacyTransformed) {
- // creator 2.4 didn't mark yet the transformation (first run of creator 2.4)
-
- // we need to transform the settings only if at least one from
- // below settings was already written - otherwise we use
- // defaults like it would be the first run of creator 2.4 without stored settings
- const QStringList groups = s->childGroups();
- const bool needTransform = groups.contains(QLatin1String("textTabPreferences")) ||
- groups.contains(QLatin1String("CppTabPreferences")) ||
- groups.contains(QLatin1String("CppCodeStyleSettings"));
- if (needTransform) {
- CppCodeStyleSettings legacyCodeStyleSettings;
- if (groups.contains(QLatin1String("CppCodeStyleSettings"))) {
- Utils::fromSettings(QLatin1String("CppCodeStyleSettings"),
- QString(), s, &legacyCodeStyleSettings);
- }
-
- const QString currentFallback = s->value(QLatin1String("CppTabPreferences/CurrentFallback")).toString();
- TabSettings legacyTabSettings;
- if (currentFallback == QLatin1String("CppGlobal")) {
- // no delegate, global overwritten
- Utils::fromSettings(QLatin1String("CppTabPreferences"),
- QString(), s, &legacyTabSettings);
- } else {
- // delegating to global
- legacyTabSettings = TextEditorSettings::codeStyle()->currentTabSettings();
- }
-
- // create custom code style out of old settings
- QVariant v;
- v.setValue(legacyCodeStyleSettings);
- ICodeStylePreferences *oldCreator = pool->createCodeStyle(
- "legacy", legacyTabSettings, v, Tr::tr("Old Creator"));
-
- // change the current delegate and save
- d->m_globalCodeStyle->setCurrentDelegate(oldCreator);
- d->m_globalCodeStyle->toSettings(QLatin1String(Constants::CPP_SETTINGS_ID), s);
- }
- // mark old settings as transformed
- s->setValue(QLatin1String("CppCodeStyleSettings/LegacyTransformed"), true);
- // legacy handling stop
- }
-
-
// mimetypes to be handled
TextEditorSettings::registerMimeTypeForLanguageId(Constants::C_SOURCE_MIMETYPE, Constants::CPP_SETTINGS_ID);
TextEditorSettings::registerMimeTypeForLanguageId(Constants::C_HEADER_MIMETYPE, Constants::CPP_SETTINGS_ID);
diff --git a/src/plugins/cppeditor/cpptoolstestcase.cpp b/src/plugins/cppeditor/cpptoolstestcase.cpp
index b7124b01ca..706423c051 100644
--- a/src/plugins/cppeditor/cpptoolstestcase.cpp
+++ b/src/plugins/cppeditor/cpptoolstestcase.cpp
@@ -18,7 +18,7 @@
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <texteditor/texteditor.h>
#include <texteditor/codeassist/iassistproposal.h>
@@ -158,7 +158,7 @@ bool VerifyCleanCppModelManager::isClean(bool testOnlyForCleanedProjects)
if (!testOnlyForCleanedProjects) {
RETURN_FALSE_IF_NOT(mm->snapshot().isEmpty());
RETURN_FALSE_IF_NOT(mm->workingCopy().size() == 1);
- RETURN_FALSE_IF_NOT(mm->workingCopy().contains(mm->configurationFileName()));
+ RETURN_FALSE_IF_NOT(mm->workingCopy().get(mm->configurationFileName()));
}
return true;
}
@@ -348,8 +348,8 @@ bool TestCase::waitUntilProjectIsFullyOpened(Project *project, int timeOutInMs)
return QTest::qWaitFor(
[project]() {
- return SessionManager::startupBuildSystem()
- && !SessionManager::startupBuildSystem()->isParsing()
+ return ProjectManager::startupBuildSystem()
+ && !ProjectManager::startupBuildSystem()->isParsing()
&& CppModelManager::instance()->projectInfo(project);
},
timeOutInMs);
@@ -367,7 +367,7 @@ bool TestCase::writeFile(const FilePath &filePath, const QByteArray &contents)
ProjectOpenerAndCloser::ProjectOpenerAndCloser()
{
- QVERIFY(!SessionManager::hasProjects());
+ QVERIFY(!ProjectManager::hasProjects());
}
ProjectOpenerAndCloser::~ProjectOpenerAndCloser()
diff --git a/src/plugins/cppeditor/cpptypehierarchy.cpp b/src/plugins/cppeditor/cpptypehierarchy.cpp
index c7a9e472ca..837375b6f3 100644
--- a/src/plugins/cppeditor/cpptypehierarchy.cpp
+++ b/src/plugins/cppeditor/cpptypehierarchy.cpp
@@ -154,8 +154,6 @@ CppTypeHierarchyWidget::CppTypeHierarchyWidget()
this, &CppTypeHierarchyWidget::perform);
connect(&m_futureWatcher, &QFutureWatcher<void>::finished,
this, &CppTypeHierarchyWidget::displayHierarchy);
-
- m_synchronizer.setCancelOnWait(true);
}
void CppTypeHierarchyWidget::perform()
@@ -183,7 +181,8 @@ void CppTypeHierarchyWidget::perform()
m_futureWatcher.setFuture(QFuture<void>(m_future));
m_synchronizer.addFuture(m_future);
- Core::ProgressManager::addTask(m_future, Tr::tr("Evaluating Type Hierarchy"), "TypeHierarchy");
+ Core::ProgressManager::addTimedTask(m_futureWatcher.future(),
+ Tr::tr("Evaluating Type Hierarchy"), "TypeHierarchy", 2);
}
void CppTypeHierarchyWidget::performFromExpression(const QString &expression, const FilePath &filePath)
diff --git a/src/plugins/cppeditor/cppuseselections_test.cpp b/src/plugins/cppeditor/cppuseselections_test.cpp
index f2f13ea55f..c9c5675284 100644
--- a/src/plugins/cppeditor/cppuseselections_test.cpp
+++ b/src/plugins/cppeditor/cppuseselections_test.cpp
@@ -104,7 +104,7 @@ SelectionList UseSelectionsTestCase::toSelectionList(
int line, column;
const int position = qMin(selection.cursor.position(), selection.cursor.anchor());
m_editorWidget->convertPosition(position, &line, &column);
- result << Selection(line, column - 1, selection.cursor.selectedText().length());
+ result << Selection(line, column, selection.cursor.selectedText().length());
}
return result;
}
diff --git a/src/plugins/cppeditor/cppworkingcopy.cpp b/src/plugins/cppeditor/cppworkingcopy.cpp
index b8922b7076..828388c96c 100644
--- a/src/plugins/cppeditor/cppworkingcopy.cpp
+++ b/src/plugins/cppeditor/cppworkingcopy.cpp
@@ -20,4 +20,18 @@ namespace CppEditor {
WorkingCopy::WorkingCopy() = default;
+std::optional<QByteArray> WorkingCopy::source(const Utils::FilePath &fileName) const
+{
+ if (const auto value = get(fileName))
+ return value->first;
+ return {};
+}
+
+std::optional<QPair<QByteArray, unsigned>> WorkingCopy::get(const Utils::FilePath &fileName) const
+{
+ const auto it = _elements.constFind(fileName);
+ if (it == _elements.constEnd())
+ return {};
+ return it.value();
+}
} // namespace CppEditor
diff --git a/src/plugins/cppeditor/cppworkingcopy.h b/src/plugins/cppeditor/cppworkingcopy.h
index ccc8258745..87a613bc7a 100644
--- a/src/plugins/cppeditor/cppworkingcopy.h
+++ b/src/plugins/cppeditor/cppworkingcopy.h
@@ -11,6 +11,8 @@
#include <QString>
#include <QPair>
+#include <optional>
+
namespace CppEditor {
class CPPEDITOR_EXPORT WorkingCopy
@@ -21,17 +23,12 @@ public:
void insert(const Utils::FilePath &fileName, const QByteArray &source, unsigned revision = 0)
{ _elements.insert(fileName, {source, revision}); }
- bool contains(const Utils::FilePath &fileName) const
- { return _elements.contains(fileName); }
-
- QByteArray source(const Utils::FilePath &fileName) const
- { return _elements.value(fileName).first; }
+ std::optional<QByteArray> source(const Utils::FilePath &fileName) const;
unsigned revision(const Utils::FilePath &fileName) const
{ return _elements.value(fileName).second; }
- QPair<QByteArray, unsigned> get(const Utils::FilePath &fileName) const
- { return _elements.value(fileName); }
+ std::optional<QPair<QByteArray, unsigned>> get(const Utils::FilePath &fileName) const;
using Table = QHash<Utils::FilePath, QPair<QByteArray, unsigned> >;
const Table &elements() const
diff --git a/src/plugins/cppeditor/doxygengenerator.cpp b/src/plugins/cppeditor/doxygengenerator.cpp
index 24cfba4abf..3d88ddd0f5 100644
--- a/src/plugins/cppeditor/doxygengenerator.cpp
+++ b/src/plugins/cppeditor/doxygengenerator.cpp
@@ -44,16 +44,6 @@ void DoxygenGenerator::setAddLeadingAsterisks(bool add)
m_addLeadingAsterisks = add;
}
-static int lineBeforeCursor(const QTextCursor &cursor)
-{
- int line, column;
- const bool converted = Utils::Text::convertPosition(cursor.document(), cursor.position(), &line,
- &column);
- QTC_ASSERT(converted, return std::numeric_limits<int>::max());
-
- return line - 1;
-}
-
QString DoxygenGenerator::generate(QTextCursor cursor,
const CPlusPlus::Snapshot &snapshot,
const Utils::FilePath &documentFilePath)
@@ -104,7 +94,7 @@ QString DoxygenGenerator::generate(QTextCursor cursor,
Document::Ptr doc = snapshot.preprocessedDocument(declCandidate.toUtf8(),
documentFilePath,
- lineBeforeCursor(initialCursor));
+ cursor.blockNumber());
doc->parse(Document::ParseDeclaration);
doc->check(Document::FastCheck);
diff --git a/src/plugins/cppeditor/editordocumenthandle.cpp b/src/plugins/cppeditor/editordocumenthandle.cpp
index 89b20f2ef3..fe1638f85a 100644
--- a/src/plugins/cppeditor/editordocumenthandle.cpp
+++ b/src/plugins/cppeditor/editordocumenthandle.cpp
@@ -14,11 +14,6 @@ namespace CppEditor {
CppEditorDocumentHandle::~CppEditorDocumentHandle() = default;
-SendDocumentTracker &CppEditorDocumentHandle::sendTracker()
-{
- return m_sendTracker;
-}
-
CppEditorDocumentHandle::RefreshReason CppEditorDocumentHandle::refreshReason() const
{
return m_refreshReason;
diff --git a/src/plugins/cppeditor/editordocumenthandle.h b/src/plugins/cppeditor/editordocumenthandle.h
index 28c60ddae9..ef9b8403b5 100644
--- a/src/plugins/cppeditor/editordocumenthandle.h
+++ b/src/plugins/cppeditor/editordocumenthandle.h
@@ -4,7 +4,6 @@
#pragma once
#include "cppeditor_global.h"
-#include "senddocumenttracker.h"
namespace Utils { class FilePath; }
@@ -35,10 +34,7 @@ public:
virtual void resetProcessor() = 0;
- SendDocumentTracker &sendTracker();
-
private:
- SendDocumentTracker m_sendTracker;
RefreshReason m_refreshReason = None;
};
diff --git a/src/plugins/cppeditor/fileandtokenactions_test.cpp b/src/plugins/cppeditor/fileandtokenactions_test.cpp
index b4b60d6828..2ad6b6b4ef 100644
--- a/src/plugins/cppeditor/fileandtokenactions_test.cpp
+++ b/src/plugins/cppeditor/fileandtokenactions_test.cpp
@@ -163,7 +163,7 @@ TestActionsTestCase::TestActionsTestCase(const Actions &tokenActions, const Acti
QCOMPARE(DocumentModel::openedDocuments().size(), 1);
QVERIFY(m_modelManager->isCppEditor(editor));
- QVERIFY(m_modelManager->workingCopy().contains(filePath));
+ QVERIFY(m_modelManager->workingCopy().get(filePath));
// Rehighlight
waitForRehighlightedSemanticDocument(editorWidget);
diff --git a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp
index 90ddfbdf90..31d69f7363 100644
--- a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp
+++ b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp
@@ -414,6 +414,7 @@ F2TestCase::F2TestCase(CppEditorAction action,
} else {
currentTextEditor->convertPosition(targetTestFile->m_targetCursorPosition,
&expectedLine, &expectedColumn);
+ ++expectedColumn;
if (useClangd && (tag == "classDestructor" || tag == "fromDestructorDefinitionSymbol"
|| tag == "fromDestructorBody")) {
--expectedColumn; // clangd goes before the ~, built-in code model after
diff --git a/src/plugins/cppeditor/generatedcodemodelsupport.cpp b/src/plugins/cppeditor/generatedcodemodelsupport.cpp
index 400e77fb37..22a9b2735e 100644
--- a/src/plugins/cppeditor/generatedcodemodelsupport.cpp
+++ b/src/plugins/cppeditor/generatedcodemodelsupport.cpp
@@ -13,7 +13,7 @@
#include <projectexplorer/extracompiler.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <utils/algorithm.h>
diff --git a/src/plugins/cppeditor/generatedcodemodelsupport.h b/src/plugins/cppeditor/generatedcodemodelsupport.h
index cd7591a3b0..d37177d991 100644
--- a/src/plugins/cppeditor/generatedcodemodelsupport.h
+++ b/src/plugins/cppeditor/generatedcodemodelsupport.h
@@ -20,7 +20,7 @@ public:
const Utils::FilePath &generatedFile);
~GeneratedCodeModelSupport() override;
- /// \returns the contents encoded in UTF-8.
+ /// Returns the contents encoded in UTF-8.
QByteArray contents() const override;
Utils::FilePath filePath() const override; // The generated file
Utils::FilePath sourceFilePath() const override;
diff --git a/src/plugins/cppeditor/indexitem.cpp b/src/plugins/cppeditor/indexitem.cpp
index 3094c9d494..879625263e 100644
--- a/src/plugins/cppeditor/indexitem.cpp
+++ b/src/plugins/cppeditor/indexitem.cpp
@@ -9,7 +9,8 @@ namespace CppEditor {
IndexItem::Ptr IndexItem::create(const QString &symbolName, const QString &symbolType,
const QString &symbolScope, IndexItem::ItemType type,
- const QString &fileName, int line, int column, const QIcon &icon)
+ const QString &fileName, int line, int column, const QIcon &icon,
+ bool isFunctionDefinition)
{
Ptr ptr(new IndexItem);
@@ -21,6 +22,7 @@ IndexItem::Ptr IndexItem::create(const QString &symbolName, const QString &symbo
ptr->m_line = line;
ptr->m_column = column;
ptr->m_icon = icon;
+ ptr->m_isFuncDef = isFunctionDefinition;
return ptr;
}
diff --git a/src/plugins/cppeditor/indexitem.h b/src/plugins/cppeditor/indexitem.h
index eda023394d..135bd60adc 100644
--- a/src/plugins/cppeditor/indexitem.h
+++ b/src/plugins/cppeditor/indexitem.h
@@ -40,7 +40,8 @@ public:
const QString &fileName,
int line,
int column,
- const QIcon &icon);
+ const QIcon &icon,
+ bool isFunctionDefinition);
static Ptr create(const QString &fileName, int sizeHint);
QString scopedSymbolName() const
@@ -64,6 +65,7 @@ public:
ItemType type() const { return m_type; }
int line() const { return m_line; }
int column() const { return m_column; }
+ bool isFunctionDefinition() const { return m_isFuncDef; }
void addChild(IndexItem::Ptr childItem) { m_children.append(childItem); }
void squeeze();
@@ -106,6 +108,7 @@ private:
ItemType m_type = All;
int m_line = 0;
int m_column = 0;
+ bool m_isFuncDef = false;
QVector<IndexItem::Ptr> m_children;
};
diff --git a/src/plugins/cppeditor/insertionpointlocator.h b/src/plugins/cppeditor/insertionpointlocator.h
index c4091d5f9d..d6fd5d735c 100644
--- a/src/plugins/cppeditor/insertionpointlocator.h
+++ b/src/plugins/cppeditor/insertionpointlocator.h
@@ -23,27 +23,21 @@ public:
InsertionLocation(const Utils::FilePath &filePath, const QString &prefix,
const QString &suffix, int line, int column);
- const Utils::FilePath &filePath() const
- { return m_filePath; }
+ const Utils::FilePath &filePath() const { return m_filePath; }
- /// \returns The prefix to insert before any other text.
- QString prefix() const
- { return m_prefix; }
+ /// Returns the prefix to insert before any other text.
+ QString prefix() const { return m_prefix; }
- /// \returns The suffix to insert after the other inserted text.
- QString suffix() const
- { return m_suffix; }
+ /// Returns the suffix to insert after the other inserted text.
+ QString suffix() const { return m_suffix; }
- /// \returns The line where to insert. The line number is 1-based.
- int line() const
- { return m_line; }
+ /// Returns the line where to insert. The line number is 1-based.
+ int line() const { return m_line; }
- /// \returns The column where to insert. The column number is 1-based.
- int column() const
- { return m_column; }
+ /// Returns the column where to insert. The column number is 1-based.
+ int column() const { return m_column; }
- bool isValid() const
- { return !m_filePath.isEmpty() && m_line > 0 && m_column > 0; }
+ bool isValid() const { return !m_filePath.isEmpty() && m_line > 0 && m_column > 0; }
private:
Utils::FilePath m_filePath;
diff --git a/src/plugins/cppeditor/modelmanagertesthelper.cpp b/src/plugins/cppeditor/modelmanagertesthelper.cpp
index f4114c33b2..a133ac7fef 100644
--- a/src/plugins/cppeditor/modelmanagertesthelper.cpp
+++ b/src/plugins/cppeditor/modelmanagertesthelper.cpp
@@ -6,7 +6,7 @@
#include "cpptoolstestcase.h"
#include "projectinfo.h"
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/algorithm.h>
@@ -59,7 +59,7 @@ void ModelManagerTestHelper::cleanup()
CppModelManager *mm = CppModelManager::instance();
QList<ProjectInfo::ConstPtr> pies = mm->projectInfos();
for (Project * const p : std::as_const(m_projects)) {
- ProjectExplorer::SessionManager::removeProject(p);
+ ProjectExplorer::ProjectManager::removeProject(p);
emit aboutToRemoveProject(p);
}
@@ -72,7 +72,7 @@ ModelManagerTestHelper::Project *ModelManagerTestHelper::createProject(
{
auto tp = new TestProject(name, this, filePath);
m_projects.push_back(tp);
- ProjectExplorer::SessionManager::addProject(tp);
+ ProjectExplorer::ProjectManager::addProject(tp);
emit projectAdded(tp);
return tp;
}
diff --git a/src/plugins/cppeditor/projectinfo_test.cpp b/src/plugins/cppeditor/projectinfo_test.cpp
index 73254647f5..88115e2ea4 100644
--- a/src/plugins/cppeditor/projectinfo_test.cpp
+++ b/src/plugins/cppeditor/projectinfo_test.cpp
@@ -362,12 +362,12 @@ public:
ProjectInfo::ConstPtr generate()
{
- QFutureInterface<ProjectInfo::ConstPtr> fi;
+ QPromise<ProjectInfo::ConstPtr> promise;
projectUpdateInfo.rawProjectParts += rawProjectPart;
- ProjectInfoGenerator generator(fi, projectUpdateInfo);
+ ProjectInfoGenerator generator(projectUpdateInfo);
- return generator.generate();
+ return generator.generate(promise);
}
ProjectUpdateInfo projectUpdateInfo;
diff --git a/src/plugins/cppeditor/projectpart.cpp b/src/plugins/cppeditor/projectpart.cpp
index 9cab9a6df2..bea37f91ab 100644
--- a/src/plugins/cppeditor/projectpart.cpp
+++ b/src/plugins/cppeditor/projectpart.cpp
@@ -163,6 +163,7 @@ CPlusPlus::LanguageFeatures ProjectPart::deriveLanguageFeatures() const
CPlusPlus::LanguageFeatures features;
features.cxx11Enabled = languageVersion >= Utils::LanguageVersion::CXX11;
features.cxx14Enabled = languageVersion >= Utils::LanguageVersion::CXX14;
+ features.cxx17Enabled = languageVersion >= Utils::LanguageVersion::CXX17;
features.cxx20Enabled = languageVersion >= Utils::LanguageVersion::CXX20;
features.cxxEnabled = hasCxx;
features.c99Enabled = languageVersion >= Utils::LanguageVersion::C99;
diff --git a/src/plugins/cppeditor/resourcepreviewhoverhandler.cpp b/src/plugins/cppeditor/resourcepreviewhoverhandler.cpp
index 0937df32ab..6c282b879c 100644
--- a/src/plugins/cppeditor/resourcepreviewhoverhandler.cpp
+++ b/src/plugins/cppeditor/resourcepreviewhoverhandler.cpp
@@ -166,7 +166,7 @@ void ResourcePreviewHoverHandler::operateTooltip(TextEditorWidget *editorWidget,
{
const QString tt = makeTooltip();
if (!tt.isEmpty())
- Utils::ToolTip::show(point, tt, editorWidget);
+ Utils::ToolTip::show(point, tt, Qt::MarkdownText, editorWidget);
else
Utils::ToolTip::hide();
}
@@ -180,10 +180,8 @@ QString ResourcePreviewHoverHandler::makeTooltip() const
const Utils::MimeType mimeType = Utils::mimeTypeForFile(m_resPath);
if (mimeType.name().startsWith("image", Qt::CaseInsensitive))
- ret += QString("<img src=\"file:///%1\" /><br/>").arg(m_resPath);
-
- ret += QString("<a href=\"file:///%1\">%2</a>")
- .arg(m_resPath, QDir::toNativeSeparators(m_resPath));
+ ret += QString("![image](%1) \n").arg(m_resPath);
+ ret += QString("[%1](%2)").arg(QDir::toNativeSeparators(m_resPath), m_resPath);
return ret;
}
diff --git a/src/plugins/cppeditor/searchsymbols.cpp b/src/plugins/cppeditor/searchsymbols.cpp
index 22e0c28197..2aea7f8e1b 100644
--- a/src/plugins/cppeditor/searchsymbols.cpp
+++ b/src/plugins/cppeditor/searchsymbols.cpp
@@ -285,7 +285,8 @@ IndexItem::Ptr SearchSymbols::addChildItem(const QString &symbolName, const QStr
StringTable::insert(path),
symbol->line(),
symbol->column() - 1, // 1-based vs 0-based column
- icon);
+ icon,
+ symbol->asFunction());
_parent->addChild(newItem);
return newItem;
}
diff --git a/src/plugins/cppeditor/semantichighlighter.cpp b/src/plugins/cppeditor/semantichighlighter.cpp
index 473991787d..156a1d4052 100644
--- a/src/plugins/cppeditor/semantichighlighter.cpp
+++ b/src/plugins/cppeditor/semantichighlighter.cpp
@@ -63,15 +63,21 @@ void SemanticHighlighter::run()
connectWatcher();
m_revision = documentRevision();
+ m_seenBlocks.clear();
+ m_nextResultToHandle = m_resultCount = 0;
qCDebug(log) << "starting runner for document revision" << m_revision;
m_watcher->setFuture(m_highlightingRunner());
}
-static Parentheses getClearedParentheses(const QTextBlock &block)
+Parentheses SemanticHighlighter::getClearedParentheses(const QTextBlock &block)
{
- return Utils::filtered(TextDocumentLayout::parentheses(block), [](const Parenthesis &p) {
- return p.source != parenSource();
- });
+ Parentheses parens = TextDocumentLayout::parentheses(block);
+ if (m_seenBlocks.insert(block.blockNumber()).second) {
+ parens = Utils::filtered(parens, [](const Parenthesis &p) {
+ return p.source != parenSource();
+ });
+ }
+ return parens;
}
void SemanticHighlighter::onHighlighterResultAvailable(int from, int to)
@@ -87,6 +93,21 @@ void SemanticHighlighter::onHighlighterResultAvailable(int from, int to)
return;
}
+ QTC_CHECK(from == m_resultCount);
+ m_resultCount = to;
+ if (to - m_nextResultToHandle >= 100) {
+ handleHighlighterResults();
+ m_nextResultToHandle = to;
+ }
+}
+
+void SemanticHighlighter::handleHighlighterResults()
+{
+ int from = m_nextResultToHandle;
+ const int to = m_resultCount;
+ if (from >= to)
+ return;
+
QElapsedTimer t;
t.start();
@@ -172,6 +193,8 @@ void SemanticHighlighter::onHighlighterFinished()
{
QTC_ASSERT(m_watcher, return);
+ handleHighlighterResults();
+
QElapsedTimer t;
t.start();
diff --git a/src/plugins/cppeditor/semantichighlighter.h b/src/plugins/cppeditor/semantichighlighter.h
index b34c49ce87..ea49f289a0 100644
--- a/src/plugins/cppeditor/semantichighlighter.h
+++ b/src/plugins/cppeditor/semantichighlighter.h
@@ -8,14 +8,21 @@
#include <QFutureWatcher>
#include <QScopedPointer>
#include <QTextCharFormat>
+#include <QVector>
#include <functional>
+#include <set>
namespace TextEditor {
class HighlightingResult;
+class Parenthesis;
class TextDocument;
}
+QT_BEGIN_NAMESPACE
+class QTextBlock;
+QT_END_NAMESPACE
+
namespace CppEditor {
class CPPEDITOR_EXPORT SemanticHighlighter : public QObject
@@ -60,12 +67,14 @@ public:
private:
void onHighlighterResultAvailable(int from, int to);
+ void handleHighlighterResults();
void onHighlighterFinished();
void connectWatcher();
void disconnectWatcher();
unsigned documentRevision() const;
+ QVector<TextEditor::Parenthesis> getClearedParentheses(const QTextBlock &block);
private:
TextEditor::TextDocument *m_baseTextDocument;
@@ -73,6 +82,9 @@ private:
unsigned m_revision = 0;
QScopedPointer<QFutureWatcher<TextEditor::HighlightingResult>> m_watcher;
QHash<int, QTextCharFormat> m_formatMap;
+ std::set<int> m_seenBlocks;
+ int m_nextResultToHandle = 0;
+ int m_resultCount = 0;
HighlightingRunner m_highlightingRunner;
};
diff --git a/src/plugins/cppeditor/senddocumenttracker.cpp b/src/plugins/cppeditor/senddocumenttracker.cpp
deleted file mode 100644
index f7044a65a2..0000000000
--- a/src/plugins/cppeditor/senddocumenttracker.cpp
+++ /dev/null
@@ -1,236 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "senddocumenttracker.h"
-
-#include <algorithm>
-
-#ifdef WITH_TESTS
-#include <QtTest>
-#endif
-
-namespace CppEditor {
-
-void SendDocumentTracker::setLastSentRevision(int revision)
-{
- m_lastSentRevision = revision;
- m_contentChangeStartPosition = std::numeric_limits<int>::max();
-}
-
-int SendDocumentTracker::lastSentRevision() const
-{
- return m_lastSentRevision;
-}
-
-void SendDocumentTracker::setLastCompletionPosition(int lastCompletionPosition)
-{
- m_lastCompletionPosition = lastCompletionPosition;
-}
-
-int SendDocumentTracker::lastCompletionPosition() const
-{
- return m_lastCompletionPosition;
-}
-
-void SendDocumentTracker::applyContentChange(int startPosition)
-{
- if (startPosition < m_lastCompletionPosition)
- m_lastCompletionPosition = -1;
-
- m_contentChangeStartPosition = std::min(startPosition, m_contentChangeStartPosition);
-}
-
-bool SendDocumentTracker::shouldSendCompletion(int newCompletionPosition) const
-{
- return m_lastCompletionPosition != newCompletionPosition;
-}
-
-bool SendDocumentTracker::shouldSendRevision(uint newRevision) const
-{
- return m_lastSentRevision != int(newRevision);
-}
-
-bool SendDocumentTracker::shouldSendRevisionWithCompletionPosition(int newRevision, int newCompletionPosition) const
-{
- if (shouldSendRevision(newRevision))
- return changedBeforeCompletionPosition(newCompletionPosition);
-
- return false;
-}
-
-bool SendDocumentTracker::changedBeforeCompletionPosition(int newCompletionPosition) const
-{
- return m_contentChangeStartPosition < newCompletionPosition;
-}
-
-#ifdef WITH_TESTS
-namespace Internal {
-
-void DocumentTrackerTest::testDefaultLastSentRevision()
-{
- SendDocumentTracker tracker;
-
- QCOMPARE(tracker.lastSentRevision(), -1);
- QCOMPARE(tracker.lastCompletionPosition(), -1);
-}
-
-void DocumentTrackerTest::testSetRevision()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
-
- QCOMPARE(tracker.lastSentRevision(), 46);
- QCOMPARE(tracker.lastCompletionPosition(), -1);
-}
-
-void DocumentTrackerTest::testSetLastCompletionPosition()
-{
- SendDocumentTracker tracker;
- tracker.setLastCompletionPosition(33);
-
- QCOMPARE(tracker.lastSentRevision(), -1);
- QCOMPARE(tracker.lastCompletionPosition(), 33);
-}
-
-void DocumentTrackerTest::testApplyContentChange()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(33);
- tracker.applyContentChange(10);
-
- QCOMPARE(tracker.lastSentRevision(), 46);
- QCOMPARE(tracker.lastCompletionPosition(), -1);
-}
-
-void DocumentTrackerTest::testDontSendCompletionIfPositionIsEqual()
-{
- SendDocumentTracker tracker;
- tracker.setLastCompletionPosition(33);
-
- QVERIFY(!tracker.shouldSendCompletion(33));
-}
-
-void DocumentTrackerTest::testSendCompletionIfPositionIsDifferent()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(33);
-
- QVERIFY(tracker.shouldSendCompletion(22));
-}
-
-void DocumentTrackerTest::testSendCompletionIfChangeIsBeforeCompletionPositionAndPositionIsEqual()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(33);
- tracker.applyContentChange(10);
-
- QVERIFY(tracker.shouldSendCompletion(33));
-}
-
-void DocumentTrackerTest::testDontSendCompletionIfChangeIsAfterCompletionPositionAndPositionIsEqual()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(33);
- tracker.applyContentChange(40);
-
- QVERIFY(!tracker.shouldSendCompletion(33));
-}
-
-void DocumentTrackerTest::testDontSendRevisionIfRevisionIsEqual()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
-
- QVERIFY(!tracker.shouldSendRevision(46));
-}
-
-void DocumentTrackerTest::testSendRevisionIfRevisionIsDifferent()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
-
- QVERIFY(tracker.shouldSendRevision(21));
-}
-
-void DocumentTrackerTest::testDontSendRevisionWithDefaults()
-{
- SendDocumentTracker tracker;
- QVERIFY(!tracker.shouldSendRevisionWithCompletionPosition(21, 33));
-}
-
-void DocumentTrackerTest::testDontSendIfRevisionIsDifferentAndCompletionPositionIsEqualAndNoContentChange()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(33);
-
- QVERIFY(!tracker.shouldSendRevisionWithCompletionPosition(21, 33));
-}
-
-void DocumentTrackerTest::testDontSendIfRevisionIsDifferentAndCompletionPositionIsDifferentAndNoContentChange()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(33);
-
- QVERIFY(!tracker.shouldSendRevisionWithCompletionPosition(21, 44));
-}
-
-void DocumentTrackerTest::testDontSendIfRevisionIsEqualAndCompletionPositionIsDifferentAndNoContentChange()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(33);
-
- QVERIFY(!tracker.shouldSendRevisionWithCompletionPosition(46,44));
-}
-
-void DocumentTrackerTest::testSendIfChangeIsBeforeCompletionAndPositionIsEqualAndRevisionIsDifferent()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(33);
- tracker.applyContentChange(10);
-
- QVERIFY(tracker.shouldSendRevisionWithCompletionPosition(45, 33));
-}
-
-void DocumentTrackerTest::testDontSendIfChangeIsAfterCompletionPositionAndRevisionIsDifferent()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(50);
- tracker.applyContentChange(40);
-
- QVERIFY(!tracker.shouldSendRevisionWithCompletionPosition(45, 36));
-}
-
-void DocumentTrackerTest::testSendIfChangeIsBeforeCompletionPositionAndRevisionIsDifferent()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(50);
- tracker.applyContentChange(30);
-
- QVERIFY(tracker.shouldSendRevisionWithCompletionPosition(45, 36));
-}
-
-void DocumentTrackerTest::testResetChangedContentStartPositionIfLastRevisionIsSet()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(50);
- tracker.applyContentChange(30);
- tracker.setLastSentRevision(47);
-
- QVERIFY(!tracker.shouldSendRevisionWithCompletionPosition(45, 36));
-}
-
-} // namespace Internal
-#endif
-
-} // namespace CppEditor
diff --git a/src/plugins/cppeditor/senddocumenttracker.h b/src/plugins/cppeditor/senddocumenttracker.h
deleted file mode 100644
index f2736efdd9..0000000000
--- a/src/plugins/cppeditor/senddocumenttracker.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "cppeditor_global.h"
-
-#include <QObject>
-
-#include <limits>
-
-namespace CppEditor {
-
-class CPPEDITOR_EXPORT SendDocumentTracker
-{
-public:
- void setLastSentRevision(int lastSentRevision);
- int lastSentRevision() const;
-
- void setLastCompletionPosition(int lastCompletionPosition);
- int lastCompletionPosition() const;
-
- void applyContentChange(int startPosition);
-
- bool shouldSendCompletion(int newCompletionPosition) const;
- bool shouldSendRevision(uint newRevision) const;
- bool shouldSendRevisionWithCompletionPosition(int newRevision, int newCompletionPosition) const;
-
-private:
- bool changedBeforeCompletionPosition(int newCompletionPosition) const;
-
-private:
- int m_lastSentRevision = -1;
- int m_lastCompletionPosition = -1;
- int m_contentChangeStartPosition = std::numeric_limits<int>::max();
-};
-
-#ifdef WITH_TESTS
-namespace Internal {
-class DocumentTrackerTest : public QObject
-{
- Q_OBJECT
-
-private slots:
- void testDefaultLastSentRevision();
- void testSetRevision();
- void testSetLastCompletionPosition();
- void testApplyContentChange();
- void testDontSendCompletionIfPositionIsEqual();
- void testSendCompletionIfPositionIsDifferent();
- void testSendCompletionIfChangeIsBeforeCompletionPositionAndPositionIsEqual();
- void testDontSendCompletionIfChangeIsAfterCompletionPositionAndPositionIsEqual();
- void testDontSendRevisionIfRevisionIsEqual();
- void testSendRevisionIfRevisionIsDifferent();
- void testDontSendRevisionWithDefaults();
- void testDontSendIfRevisionIsDifferentAndCompletionPositionIsEqualAndNoContentChange();
- void testDontSendIfRevisionIsDifferentAndCompletionPositionIsDifferentAndNoContentChange();
- void testDontSendIfRevisionIsEqualAndCompletionPositionIsDifferentAndNoContentChange();
- void testSendIfChangeIsBeforeCompletionAndPositionIsEqualAndRevisionIsDifferent();
- void testDontSendIfChangeIsAfterCompletionPositionAndRevisionIsDifferent();
- void testSendIfChangeIsBeforeCompletionPositionAndRevisionIsDifferent();
- void testResetChangedContentStartPositionIfLastRevisionIsSet();
-};
-} // namespace Internal
-#endif // WITH_TESTS
-
-} // namespace CppEditor
diff --git a/src/plugins/cppeditor/symbolsearcher_test.cpp b/src/plugins/cppeditor/symbolsearcher_test.cpp
index 9f700d9972..688df3d924 100644
--- a/src/plugins/cppeditor/symbolsearcher_test.cpp
+++ b/src/plugins/cppeditor/symbolsearcher_test.cpp
@@ -4,13 +4,13 @@
#include "symbolsearcher_test.h"
#include "cppindexingsupport.h"
-#include "cppmodelmanager.h"
#include "cpptoolstestcase.h"
#include "searchsymbols.h"
#include <coreplugin/testdatadir.h>
#include <coreplugin/find/searchresultwindow.h>
-#include <utils/runextensions.h>
+
+#include <utils/async.h>
#include <QtTest>
@@ -35,10 +35,10 @@ public:
return m_symbolName == other.m_symbolName && m_scope == other.m_scope;
}
- static ResultDataList fromSearchResultList(const QList<Core::SearchResultItem> &entries)
+ static ResultDataList fromSearchResultList(const Utils::SearchResultItems &entries)
{
ResultDataList result;
- for (const Core::SearchResultItem &entry : entries)
+ for (const Utils::SearchResultItem &entry : entries)
result << ResultData(entry.lineText(), entry.path().join(QLatin1String("::")));
return result;
}
@@ -77,8 +77,8 @@ public:
const QScopedPointer<SymbolSearcher> symbolSearcher(
new SymbolSearcher(searchParameters, QSet<QString>{testFile}));
- QFuture<Core::SearchResultItem> search
- = Utils::runAsync(&SymbolSearcher::runSearch, symbolSearcher.data());
+ QFuture<Utils::SearchResultItem> search
+ = Utils::asyncRun(&SymbolSearcher::runSearch, symbolSearcher.data());
search.waitForFinished();
ResultDataList results = ResultData::fromSearchResultList(search.results());
QCOMPARE(results, expectedResults);
diff --git a/src/plugins/cppeditor/symbolsfindfilter.cpp b/src/plugins/cppeditor/symbolsfindfilter.cpp
index 3b901715b2..eb6a4c81da 100644
--- a/src/plugins/cppeditor/symbolsfindfilter.cpp
+++ b/src/plugins/cppeditor/symbolsfindfilter.cpp
@@ -12,18 +12,19 @@
#include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/find/searchresultwindow.h>
+
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/algorithm.h>
-#include <utils/runextensions.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
-#include <QSet>
+#include <QButtonGroup>
#include <QGridLayout>
#include <QLabel>
-#include <QButtonGroup>
+#include <QSet>
using namespace Core;
using namespace Utils;
@@ -108,7 +109,7 @@ void SymbolsFindFilter::startSearch(SearchResult *search)
SymbolSearcher::Parameters parameters = search->userData().value<SymbolSearcher::Parameters>();
QSet<QString> projectFileNames;
if (parameters.scope == SymbolSearcher::SearchProjectsOnly) {
- for (ProjectExplorer::Project *project : ProjectExplorer::SessionManager::projects())
+ for (ProjectExplorer::Project *project : ProjectExplorer::ProjectManager::projects())
projectFileNames += Utils::transform<QSet>(project->files(ProjectExplorer::Project::AllFiles), &Utils::FilePath::toString);
}
@@ -120,7 +121,7 @@ void SymbolsFindFilter::startSearch(SearchResult *search)
SymbolSearcher *symbolSearcher = new SymbolSearcher(parameters, projectFileNames);
connect(watcher, &QFutureWatcherBase::finished,
symbolSearcher, &QObject::deleteLater);
- watcher->setFuture(Utils::runAsync(m_manager->sharedThreadPool(),
+ watcher->setFuture(Utils::asyncRun(m_manager->sharedThreadPool(),
&SymbolSearcher::runSearch, symbolSearcher));
FutureProgress *progress = ProgressManager::addTask(watcher->future(), Tr::tr("Searching for Symbol"),
Core::Constants::TASK_SEARCH);
@@ -135,7 +136,7 @@ void SymbolsFindFilter::addResults(QFutureWatcher<SearchResultItem> *watcher, in
watcher->cancel();
return;
}
- QList<SearchResultItem> items;
+ SearchResultItems items;
for (int i = begin; i < end; ++i)
items << watcher->resultAt(i);
search->addResults(items, SearchResult::AddSorted);
diff --git a/src/plugins/cppeditor/symbolsfindfilter.h b/src/plugins/cppeditor/symbolsfindfilter.h
index 61866aae4d..a77f587360 100644
--- a/src/plugins/cppeditor/symbolsfindfilter.h
+++ b/src/plugins/cppeditor/symbolsfindfilter.h
@@ -6,7 +6,6 @@
#include "searchsymbols.h"
#include <coreplugin/find/ifindfilter.h>
-#include <coreplugin/find/searchresultitem.h>
#include <coreplugin/find/searchresultwindow.h>
#include <QFutureWatcher>
@@ -16,6 +15,7 @@
#include <QRadioButton>
namespace Core { class SearchResult; }
+namespace Utils { class SearchResultItem; }
namespace CppEditor {
class CppModelManager;
@@ -52,10 +52,10 @@ signals:
void symbolsToSearchChanged();
private:
- void openEditor(const Core::SearchResultItem &item);
+ void openEditor(const Utils::SearchResultItem &item);
- void addResults(QFutureWatcher<Core::SearchResultItem> *watcher, int begin, int end);
- void finish(QFutureWatcher<Core::SearchResultItem> *watcher);
+ void addResults(QFutureWatcher<Utils::SearchResultItem> *watcher, int begin, int end);
+ void finish(QFutureWatcher<Utils::SearchResultItem> *watcher);
void cancel(Core::SearchResult *search);
void setPaused(Core::SearchResult *search, bool paused);
void onTaskStarted(Utils::Id type);
@@ -67,7 +67,7 @@ private:
CppModelManager *m_manager;
bool m_enabled;
- QMap<QFutureWatcher<Core::SearchResultItem> *, QPointer<Core::SearchResult> > m_watchers;
+ QMap<QFutureWatcher<Utils::SearchResultItem> *, QPointer<Core::SearchResult> > m_watchers;
QPointer<Core::SearchResult> m_currentSearch;
SearchSymbols::SymbolTypes m_symbolsToSearch;
SearchScope m_scope;
diff --git a/src/plugins/cppeditor/testcases/highlightingtestcase.cpp b/src/plugins/cppeditor/testcases/highlightingtestcase.cpp
index 20fa252646..53ffcc1b55 100644
--- a/src/plugins/cppeditor/testcases/highlightingtestcase.cpp
+++ b/src/plugins/cppeditor/testcases/highlightingtestcase.cpp
@@ -1,7 +1,7 @@
auto func()
{
return R"(foo
- foobar
+
R"notaprefix!(
barfoobar)" R"(second)" /* comment */ R"(third)";
}
@@ -25,3 +25,15 @@ template<int n = 5> class C;
struct ConversionFunction {
operator int();
};
+
+template<typename T> concept NoConstraint = true;
+
+const char16_t *operator ""_w(const char16_t *s, size_t) { return s; }
+const auto s = u"one"_w;
+const auto s2 = L"hello";
+const auto s3 = u8"hello";
+const auto s4 = U"hello";
+const auto s5 = uR"("o
+ ne")"_w;
+const auto s6 = u"o\
+ne"_w;
diff --git a/src/plugins/cppeditor/typehierarchybuilder.cpp b/src/plugins/cppeditor/typehierarchybuilder.cpp
index 5f3aae00e7..889d0eacfc 100644
--- a/src/plugins/cppeditor/typehierarchybuilder.cpp
+++ b/src/plugins/cppeditor/typehierarchybuilder.cpp
@@ -108,20 +108,12 @@ const QList<TypeHierarchy> &TypeHierarchy::hierarchy() const
}
TypeHierarchy TypeHierarchyBuilder::buildDerivedTypeHierarchy(Symbol *symbol,
- const Snapshot &snapshot)
-{
- QFutureInterfaceBase dummy;
- return TypeHierarchyBuilder::buildDerivedTypeHierarchy(dummy, symbol, snapshot);
-}
-
-TypeHierarchy TypeHierarchyBuilder::buildDerivedTypeHierarchy(QFutureInterfaceBase &futureInterface,
- Symbol *symbol,
- const Snapshot &snapshot)
+ const Snapshot &snapshot, const std::optional<QFuture<void>> &future)
{
TypeHierarchy hierarchy(symbol);
TypeHierarchyBuilder builder;
QHash<QString, QHash<QString, QString>> cache;
- builder.buildDerived(futureInterface, &hierarchy, snapshot, cache);
+ builder.buildDerived(future, &hierarchy, snapshot, cache);
return hierarchy;
}
@@ -172,11 +164,10 @@ static FilePaths filesDependingOn(const Snapshot &snapshot, Symbol *symbol)
return FilePaths{file} + snapshot.filesDependingOn(file);
}
-void TypeHierarchyBuilder::buildDerived(QFutureInterfaceBase &futureInterface,
+void TypeHierarchyBuilder::buildDerived(const std::optional<QFuture<void>> &future,
TypeHierarchy *typeHierarchy,
const Snapshot &snapshot,
- QHash<QString, QHash<QString, QString>> &cache,
- int depth)
+ QHash<QString, QHash<QString, QString>> &cache)
{
Symbol *symbol = typeHierarchy->_symbol;
if (_visited.contains(symbol))
@@ -188,15 +179,10 @@ void TypeHierarchyBuilder::buildDerived(QFutureInterfaceBase &futureInterface,
DerivedHierarchyVisitor visitor(symbolName, cache);
const FilePaths dependingFiles = filesDependingOn(snapshot, symbol);
- if (depth == 0)
- futureInterface.setProgressRange(0, dependingFiles.size());
- int i = -1;
for (const FilePath &fileName : dependingFiles) {
- if (futureInterface.isCanceled())
+ if (future && future->isCanceled())
return;
- if (depth == 0)
- futureInterface.setProgressValue(++i);
Document::Ptr doc = snapshot.document(fileName);
if ((_candidates.contains(fileName) && !_candidates.value(fileName).contains(symbolName))
|| !doc->control()->findIdentifier(symbol->identifier()->chars(),
@@ -210,8 +196,8 @@ void TypeHierarchyBuilder::buildDerived(QFutureInterfaceBase &futureInterface,
const QList<Symbol *> &derived = visitor.derived();
for (Symbol *s : derived) {
TypeHierarchy derivedHierarchy(s);
- buildDerived(futureInterface, &derivedHierarchy, snapshot, cache, depth + 1);
- if (futureInterface.isCanceled())
+ buildDerived(future, &derivedHierarchy, snapshot, cache);
+ if (future && future->isCanceled())
return;
typeHierarchy->_hierarchy.append(derivedHierarchy);
}
diff --git a/src/plugins/cppeditor/typehierarchybuilder.h b/src/plugins/cppeditor/typehierarchybuilder.h
index 07556126dd..c6ee8a56bf 100644
--- a/src/plugins/cppeditor/typehierarchybuilder.h
+++ b/src/plugins/cppeditor/typehierarchybuilder.h
@@ -6,7 +6,7 @@
#include <cplusplus/CppDocument.h>
#include <cplusplus/Overview.h>
-#include <QFutureInterface>
+#include <QFuture>
#include <QList>
#include <QSet>
@@ -44,19 +44,17 @@ class TypeHierarchyBuilder
{
public:
static TypeHierarchy buildDerivedTypeHierarchy(CPlusPlus::Symbol *symbol,
- const CPlusPlus::Snapshot &snapshot);
- static TypeHierarchy buildDerivedTypeHierarchy(QFutureInterfaceBase &futureInterface,
- CPlusPlus::Symbol *symbol,
- const CPlusPlus::Snapshot &snapshot);
+ const CPlusPlus::Snapshot &snapshot,
+ const std::optional<QFuture<void>> &future = {});
static CPlusPlus::LookupItem followTypedef(const CPlusPlus::LookupContext &context,
const CPlusPlus::Name *symbolName,
CPlusPlus::Scope *enclosingScope,
std::set<const CPlusPlus::Symbol *> typedefs = {});
private:
TypeHierarchyBuilder() = default;
- void buildDerived(QFutureInterfaceBase &futureInterface, TypeHierarchy *typeHierarchy,
+ void buildDerived(const std::optional<QFuture<void>> &future, TypeHierarchy *typeHierarchy,
const CPlusPlus::Snapshot &snapshot,
- QHash<QString, QHash<QString, QString> > &cache, int depth = 0);
+ QHash<QString, QHash<QString, QString> > &cache);
QSet<CPlusPlus::Symbol *> _visited;
QHash<Utils::FilePath, QSet<QString> > _candidates;
diff --git a/src/plugins/ctfvisualizer/ctfvisualizertool.cpp b/src/plugins/ctfvisualizer/ctfvisualizertool.cpp
index 0fbc5cac65..05cf55d729 100644
--- a/src/plugins/ctfvisualizer/ctfvisualizertool.cpp
+++ b/src/plugins/ctfvisualizer/ctfvisualizertool.cpp
@@ -15,6 +15,8 @@
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <debugger/analyzer/analyzerconstants.h>
+
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QAction>
@@ -64,7 +66,7 @@ CtfVisualizerTool::CtfVisualizerTool()
m_restrictToThreadsButton->setIcon(Utils::Icons::FILTER.icon());
m_restrictToThreadsButton->setToolTip(Tr::tr("Restrict to Threads"));
m_restrictToThreadsButton->setPopupMode(QToolButton::InstantPopup);
- m_restrictToThreadsButton->setProperty("noArrow", true);
+ m_restrictToThreadsButton->setProperty(Utils::StyleHelper::C_NO_ARROW, true);
m_restrictToThreadsButton->setMenu(m_restrictToThreadsMenu);
connect(m_restrictToThreadsMenu, &QMenu::triggered,
this, &CtfVisualizerTool::toggleThreadRestriction);
diff --git a/src/plugins/cvs/cvsplugin.cpp b/src/plugins/cvs/cvsplugin.cpp
index 85e98c1e4c..d9980328fc 100644
--- a/src/plugins/cvs/cvsplugin.cpp
+++ b/src/plugins/cvs/cvsplugin.cpp
@@ -137,34 +137,28 @@ static inline bool messageBoxQuestion(const QString &title, const QString &quest
class CvsDiffConfig : public VcsBaseEditorConfig
{
public:
- CvsDiffConfig(CvsSettings &settings, QToolBar *toolBar) :
- VcsBaseEditorConfig(toolBar),
- m_settings(settings)
+ CvsDiffConfig(QToolBar *toolBar)
+ : VcsBaseEditorConfig(toolBar)
{
mapSetting(addToggleButton("-w", Tr::tr("Ignore Whitespace")),
- &settings.diffIgnoreWhiteSpace);
+ &settings().diffIgnoreWhiteSpace);
mapSetting(addToggleButton("-B", Tr::tr("Ignore Blank Lines")),
- &settings.diffIgnoreBlankLines);
+ &settings().diffIgnoreBlankLines);
}
QStringList arguments() const override
{
- return m_settings.diffOptions.value().split(' ', Qt::SkipEmptyParts)
+ return settings().diffOptions.value().split(' ', Qt::SkipEmptyParts)
+ VcsBaseEditorConfig::arguments();
}
-
-private:
- CvsSettings &m_settings;
};
class CvsClient : public VcsBaseClient
{
public:
- explicit CvsClient(CvsSettings *settings) : VcsBaseClient(settings)
+ explicit CvsClient() : VcsBaseClient(&Internal::settings())
{
- setDiffConfigCreator([settings](QToolBar *toolBar) {
- return new CvsDiffConfig(*settings, toolBar);
- });
+ setDiffConfigCreator([](QToolBar *toolBar) { return new CvsDiffConfig(toolBar); });
}
ExitCodeInterpreter exitCodeInterpreter(VcsCommandTag cmd) const override
@@ -294,7 +288,7 @@ private:
bool commit(const QString &messageFile, const QStringList &subVersionFileList);
void cleanCommitMessageFile();
- CvsSettings m_settings;
+ CvsSettings m_setting;
CvsClient *m_client = nullptr;
QString m_commitMessageFileName;
@@ -327,8 +321,6 @@ private:
QAction *m_menuAction = nullptr;
- CvsSettingsPage m_settingsPage{&m_settings};
-
public:
VcsSubmitEditorFactory submitEditorFactory {
submitParameters,
@@ -374,7 +366,7 @@ bool CvsPluginPrivate::isVcsFileOrDirectory(const Utils::FilePath &filePath) con
bool CvsPluginPrivate::isConfigured() const
{
- const Utils::FilePath binary = m_settings.binaryPath.filePath();
+ const FilePath binary = settings().binaryPath();
if (binary.isEmpty())
return false;
QFileInfo fi = binary.toFileInfo();
@@ -447,7 +439,7 @@ VcsCommand *CvsPluginPrivate::createInitialCheckoutCommand(const QString &url,
auto command = VcsBaseClient::createVcsCommand(baseDirectory, Environment::systemEnvironment());
command->setDisplayName(Tr::tr("CVS Checkout"));
- command->addJob({m_settings.binaryPath.filePath(), m_settings.addOptions(args)}, -1);
+ command->addJob({settings().binaryPath(), settings().addOptions(args)}, -1);
return command;
}
@@ -497,7 +489,7 @@ CvsPluginPrivate::CvsPluginPrivate()
dd = this;
Context context(CVS_CONTEXT);
- m_client = new CvsClient(&m_settings);
+ m_client = new CvsClient;
const QString prefix = QLatin1String("cvs");
m_commandLocator = new CommandLocator("CVS", prefix, prefix, this);
@@ -692,7 +684,7 @@ CvsPluginPrivate::CvsPluginPrivate()
cvsMenu->addAction(command);
m_commandLocator->appendCommand(command);
- connect(&m_settings, &AspectContainer::applied, this, &IVersionControl::configurationChanged);
+ connect(&settings(), &AspectContainer::applied, this, &IVersionControl::configurationChanged);
}
void CvsPluginPrivate::vcsDescribe(const FilePath &source, const QString &changeNr)
@@ -1230,7 +1222,7 @@ bool CvsPluginPrivate::describe(const FilePath &toplevel, const QString &file,
*errorMessage = Tr::tr("Parsing of the log output failed.");
return false;
}
- if (m_settings.describeByCommitId.value()) {
+ if (settings().describeByCommitId()) {
// Run a log command over the repo, filtering by the commit date
// and commit id, collecting all files touched by the commit.
const QString commitId = fileLog.front().revisions.front().commitId;
@@ -1286,7 +1278,7 @@ bool CvsPluginPrivate::describe(const FilePath &repositoryPath,
for (QList<CvsLogEntry>::iterator it = entries.begin(); it != lend; ++it) {
const QString &revision = it->revisions.front().revision;
if (!isFirstRevision(revision)) {
- const QStringList args{"diff", m_settings.diffOptions.value(),
+ const QStringList args{"diff", settings().diffOptions(),
"-r", previousRevision(revision),
"-r", it->revisions.front().revision, it->file};
const auto diffResponse = runCvs(repositoryPath, args, RunFlags::None, codec);
@@ -1329,13 +1321,13 @@ CommandResult CvsPluginPrivate::runCvs(const FilePath &workingDirectory,
const QStringList &arguments, RunFlags flags,
QTextCodec *outputCodec, int timeoutMultiplier) const
{
- const FilePath executable = m_settings.binaryPath.filePath();
+ const FilePath executable = settings().binaryPath();
if (executable.isEmpty())
return CommandResult(ProcessResult::StartFailed, Tr::tr("No CVS executable specified."));
- const int timeoutS = m_settings.timeout.value() * timeoutMultiplier;
+ const int timeoutS = settings().timeout() * timeoutMultiplier;
return m_client->vcsSynchronousExec(workingDirectory,
- {executable, m_settings.addOptions(arguments)},
+ {executable, settings().addOptions(arguments)},
flags, timeoutS, outputCodec);
}
diff --git a/src/plugins/cvs/cvssettings.cpp b/src/plugins/cvs/cvssettings.cpp
index 917db71921..9e1523486f 100644
--- a/src/plugins/cvs/cvssettings.cpp
+++ b/src/plugins/cvs/cvssettings.cpp
@@ -17,32 +17,37 @@ using namespace Utils;
namespace Cvs::Internal {
-// CvsSettings
+static CvsSettings *theSettings;
+
+CvsSettings &settings()
+{
+ return *theSettings;
+}
CvsSettings::CvsSettings()
{
+ theSettings = this;
setSettingsGroup("CVS");
- registerAspect(&binaryPath);
+ setId(VcsBase::Constants::VCS_ID_CVS);
+ setDisplayName(Tr::tr("CVS"));
+ setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
+
binaryPath.setDefaultValue("cvs" QTC_HOST_EXE_SUFFIX);
- binaryPath.setDisplayStyle(StringAspect::PathChooserDisplay);
binaryPath.setExpectedKind(PathChooser::ExistingCommand);
binaryPath.setHistoryCompleter(QLatin1String("Cvs.Command.History"));
binaryPath.setDisplayName(Tr::tr("CVS Command"));
binaryPath.setLabelText(Tr::tr("CVS command:"));
- registerAspect(&cvsRoot);
cvsRoot.setDisplayStyle(StringAspect::LineEditDisplay);
cvsRoot.setSettingsKey("Root");
cvsRoot.setLabelText(Tr::tr("CVS root:"));
- registerAspect(&diffOptions);
diffOptions.setDisplayStyle(StringAspect::LineEditDisplay);
diffOptions.setSettingsKey("DiffOptions");
diffOptions.setDefaultValue("-du");
diffOptions.setLabelText("Diff options:");
- registerAspect(&describeByCommitId);
describeByCommitId.setSettingsKey("DescribeByCommitId");
describeByCommitId.setDefaultValue(true);
describeByCommitId.setLabelText(Tr::tr("Describe all files matching commit id"));
@@ -50,58 +55,46 @@ CvsSettings::CvsSettings()
"displayed when clicking on a revision number in the annotation view "
"(retrieved via commit ID). Otherwise, only the respective file will be displayed."));
- registerAspect(&diffIgnoreWhiteSpace);
diffIgnoreWhiteSpace.setSettingsKey("DiffIgnoreWhiteSpace");
- registerAspect(&diffIgnoreBlankLines);
diffIgnoreBlankLines.setSettingsKey("DiffIgnoreBlankLines");
-}
-
-QStringList CvsSettings::addOptions(const QStringList &args) const
-{
- const QString cvsRoot = this->cvsRoot.value();
- if (cvsRoot.isEmpty())
- return args;
-
- QStringList rc;
- rc.push_back(QLatin1String("-d"));
- rc.push_back(cvsRoot);
- rc.append(args);
- return rc;
-}
-
-CvsSettingsPage::CvsSettingsPage(CvsSettings *settings)
-{
- setId(VcsBase::Constants::VCS_ID_CVS);
- setDisplayName(Tr::tr("CVS"));
- setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
- setSettings(settings);
- setLayouter([settings](QWidget *widget) {
- CvsSettings &s = *settings;
+ setLayouter([this] {
using namespace Layouting;
-
- Column {
+ return Column {
Group {
title(Tr::tr("Configuration")),
Form {
- s.binaryPath,
- s.cvsRoot
+ binaryPath, br,
+ cvsRoot
}
},
Group {
title(Tr::tr("Miscellaneous")),
Column {
Form {
- s.timeout,
- s.diffOptions,
+ timeout, br,
+ diffOptions,
},
- s.describeByCommitId,
+ describeByCommitId,
}
},
st
- }.attachTo(widget);
+ };
});
}
+QStringList CvsSettings::addOptions(const QStringList &args) const
+{
+ const QString cvsRoot = this->cvsRoot.value();
+ if (cvsRoot.isEmpty())
+ return args;
+
+ QStringList rc;
+ rc.push_back(QLatin1String("-d"));
+ rc.push_back(cvsRoot);
+ rc.append(args);
+ return rc;
+}
+
} // Cvs::Internal
diff --git a/src/plugins/cvs/cvssettings.h b/src/plugins/cvs/cvssettings.h
index ac52ce9402..ade9920b2a 100644
--- a/src/plugins/cvs/cvssettings.h
+++ b/src/plugins/cvs/cvssettings.h
@@ -3,8 +3,6 @@
#pragma once
-#include <coreplugin/dialogs/ioptionspage.h>
-
#include <vcsbase/vcsbaseclientsettings.h>
namespace Cvs::Internal {
@@ -12,21 +10,17 @@ namespace Cvs::Internal {
class CvsSettings : public VcsBase::VcsBaseSettings
{
public:
- Utils::StringAspect cvsRoot;
- Utils::StringAspect diffOptions;
- Utils::BoolAspect diffIgnoreWhiteSpace;
- Utils::BoolAspect diffIgnoreBlankLines;
- Utils::BoolAspect describeByCommitId;
-
CvsSettings();
+ Utils::StringAspect cvsRoot{this};
+ Utils::StringAspect diffOptions{this};
+ Utils::BoolAspect diffIgnoreWhiteSpace{this};
+ Utils::BoolAspect diffIgnoreBlankLines{this};
+ Utils::BoolAspect describeByCommitId{this};
+
QStringList addOptions(const QStringList &args) const;
};
-class CvsSettingsPage final : public Core::IOptionsPage
-{
-public:
- explicit CvsSettingsPage(CvsSettings *settings);
-};
+CvsSettings &settings();
} // Cvs::Internal
diff --git a/src/plugins/debugger/CMakeLists.txt b/src/plugins/debugger/CMakeLists.txt
index f5f45deb4a..d06e82a27e 100644
--- a/src/plugins/debugger/CMakeLists.txt
+++ b/src/plugins/debugger/CMakeLists.txt
@@ -27,6 +27,7 @@ add_qtc_plugin(Debugger
console/consoleitemmodel.cpp console/consoleitemmodel.h
console/consoleproxymodel.cpp console/consoleproxymodel.h
console/consoleview.cpp console/consoleview.h
+ dap/dapengine.cpp dap/dapengine.h
debugger.qrc
debugger_global.h
debuggeractions.cpp debuggeractions.h
diff --git a/src/plugins/debugger/analyzer/analyzerutils.cpp b/src/plugins/debugger/analyzer/analyzerutils.cpp
index dcfda572cb..dc87b2ad61 100644
--- a/src/plugins/debugger/analyzer/analyzerutils.cpp
+++ b/src/plugins/debugger/analyzer/analyzerutils.cpp
@@ -52,7 +52,7 @@ CPlusPlus::Symbol *AnalyzerUtils::findSymbolUnderCursor()
CPlusPlus::ExpressionUnderCursor expressionUnderCursor(doc->languageFeatures());
moveCursorToEndOfName(&tc);
const QString &expression = expressionUnderCursor(tc);
- CPlusPlus::Scope *scope = doc->scopeAt(line, column - 1);
+ CPlusPlus::Scope *scope = doc->scopeAt(line, column);
CPlusPlus::TypeOfExpression typeOfExpression;
typeOfExpression.init(doc, snapshot);
diff --git a/src/plugins/debugger/breakhandler.cpp b/src/plugins/debugger/breakhandler.cpp
index bcc50a4b0d..35915f9755 100644
--- a/src/plugins/debugger/breakhandler.cpp
+++ b/src/plugins/debugger/breakhandler.cpp
@@ -18,8 +18,7 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/idocument.h>
-
-#include <projectexplorer/session.h>
+#include <coreplugin/session.h>
#include <texteditor/textmark.h>
#include <texteditor/texteditor.h>
@@ -41,6 +40,7 @@
#include <QCheckBox>
#include <QComboBox>
#include <QDebug>
+#include <QDialogButtonBox>
#include <QDir>
#include <QFormLayout>
#include <QGroupBox>
@@ -1126,9 +1126,9 @@ void BreakpointItem::addToCommand(DebuggerCommand *cmd) const
cmd->arg("expression", requested.expression);
}
-void BreakpointItem::updateFromGdbOutput(const GdbMi &bkpt)
+void BreakpointItem::updateFromGdbOutput(const GdbMi &bkpt, const FilePath &fileRoot)
{
- m_parameters.updateFromGdbOutput(bkpt);
+ m_parameters.updateFromGdbOutput(bkpt, fileRoot);
adjustMarker();
}
@@ -2691,14 +2691,13 @@ void BreakpointManager::gotoLocation(const GlobalBreakpoint &gbp) const
void BreakpointManager::executeDeleteAllBreakpointsDialog()
{
- QDialogButtonBox::StandardButton pressed =
- CheckableMessageBox::doNotAskAgainQuestion(ICore::dialogParent(),
- Tr::tr("Remove All Breakpoints"),
- Tr::tr("Are you sure you want to remove all breakpoints "
- "from all files in the current session?"),
- ICore::settings(),
- "RemoveAllBreakpoints");
- if (pressed != QDialogButtonBox::Yes)
+ QMessageBox::StandardButton pressed
+ = CheckableMessageBox::question(ICore::dialogParent(),
+ Tr::tr("Remove All Breakpoints"),
+ Tr::tr("Are you sure you want to remove all breakpoints "
+ "from all files in the current session?"),
+ QString("RemoveAllBreakpoints"));
+ if (pressed != QMessageBox::Yes)
return;
for (GlobalBreakpoint gbp : globalBreakpoints())
diff --git a/src/plugins/debugger/breakhandler.h b/src/plugins/debugger/breakhandler.h
index 1024783db3..0b939a011f 100644
--- a/src/plugins/debugger/breakhandler.h
+++ b/src/plugins/debugger/breakhandler.h
@@ -107,7 +107,7 @@ public:
const BreakpointParameters &requestedParameters() const;
void addToCommand(DebuggerCommand *cmd) const;
- void updateFromGdbOutput(const GdbMi &bkpt);
+ void updateFromGdbOutput(const GdbMi &bkpt, const Utils::FilePath &fileRoot);
int modelId() const;
QString responseId() const { return m_responseId; }
diff --git a/src/plugins/debugger/breakpoint.cpp b/src/plugins/debugger/breakpoint.cpp
index 8d456ef67f..79e2badf7b 100644
--- a/src/plugins/debugger/breakpoint.cpp
+++ b/src/plugins/debugger/breakpoint.cpp
@@ -265,7 +265,8 @@ static QString cleanupFullName(const QString &fileName)
return cleanFilePath;
}
-void BreakpointParameters::updateFromGdbOutput(const GdbMi &bkpt)
+
+void BreakpointParameters::updateFromGdbOutput(const GdbMi &bkpt, const Utils::FilePath &fileRoot)
{
QTC_ASSERT(bkpt.isValid(), return);
@@ -358,7 +359,7 @@ void BreakpointParameters::updateFromGdbOutput(const GdbMi &bkpt)
QString name;
if (!fullName.isEmpty()) {
name = cleanupFullName(fullName);
- fileName = Utils::FilePath::fromString(name);
+ fileName = fileRoot.withNewPath(name);
//if (data->markerFileName().isEmpty())
// data->setMarkerFileName(name);
} else {
@@ -367,7 +368,7 @@ void BreakpointParameters::updateFromGdbOutput(const GdbMi &bkpt)
// gdb's own. No point in assigning markerFileName for now.
}
if (!name.isEmpty())
- fileName = Utils::FilePath::fromString(name);
+ fileName = fileRoot.withNewPath(name);
if (fileName.isEmpty())
updateLocation(originalLocation);
diff --git a/src/plugins/debugger/breakpoint.h b/src/plugins/debugger/breakpoint.h
index 52a150f15e..b5ea0628da 100644
--- a/src/plugins/debugger/breakpoint.h
+++ b/src/plugins/debugger/breakpoint.h
@@ -128,7 +128,7 @@ public:
bool isQmlFileAndLineBreakpoint() const;
QString toString() const;
void updateLocation(const QString &location); // file.cpp:42
- void updateFromGdbOutput(const GdbMi &bkpt);
+ void updateFromGdbOutput(const GdbMi &bkpt, const Utils::FilePath &fileRoot);
bool operator==(const BreakpointParameters &p) const { return equals(p); }
bool operator!=(const BreakpointParameters &p) const { return !equals(p); }
diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp
index b95d1c93d5..7c70a3c31b 100644
--- a/src/plugins/debugger/cdb/cdbengine.cpp
+++ b/src/plugins/debugger/cdb/cdbengine.cpp
@@ -15,6 +15,7 @@
#include <debugger/debuggerinternalconstants.h>
#include <debugger/debuggermainwindow.h>
#include <debugger/debuggerprotocol.h>
+#include <debugger/debuggersourcepathmappingwidget.h>
#include <debugger/debuggertooltipmanager.h>
#include <debugger/debuggertr.h>
#include <debugger/disassembleragent.h>
@@ -44,9 +45,9 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/winutils.h>
@@ -181,8 +182,8 @@ CdbEngine::CdbEngine() :
DebuggerSettings *s = debuggerSettings();
connect(s->createFullBacktrace.action(), &QAction::triggered,
this, &CdbEngine::createFullBacktrace);
- connect(&m_process, &QtcProcess::started, this, &CdbEngine::processStarted);
- connect(&m_process, &QtcProcess::done, this, &CdbEngine::processDone);
+ connect(&m_process, &Process::started, this, &CdbEngine::processStarted);
+ connect(&m_process, &Process::done, this, &CdbEngine::processDone);
m_process.setStdOutLineCallback([this](const QString &line) { parseOutputLine(line); });
m_process.setStdErrLineCallback([this](const QString &line) { parseOutputLine(line); });
connect(&s->useDebuggingHelpers, &BaseAspect::changed,
@@ -224,7 +225,8 @@ void CdbEngine::init()
}
}
- const SourcePathMap &sourcePathMap = debuggerSettings()->sourcePathMap.value();
+ const SourcePathMap &sourcePathMap
+ = mergePlatformQtPath(runParameters(), debuggerSettings()->sourcePathMap.value());
if (!sourcePathMap.isEmpty()) {
for (auto it = sourcePathMap.constBegin(), cend = sourcePathMap.constEnd(); it != cend; ++it) {
m_sourcePathMappings.push_back({QDir::toNativeSeparators(it.key()),
@@ -419,10 +421,11 @@ void CdbEngine::setupEngine()
inferiorEnvironment.set(qtLoggingToConsoleKey, "0");
static const char cdbExtensionPathVariableC[] = "_NT_DEBUGGER_EXTENSION_PATH";
- inferiorEnvironment.prependOrSet(cdbExtensionPathVariableC, extensionFi.absolutePath(), {";"});
+ const QString pSep = OsSpecificAspects::pathListSeparator(Utils::OsTypeWindows);
+ inferiorEnvironment.prependOrSet(cdbExtensionPathVariableC, extensionFi.absolutePath(), pSep);
const QString oldCdbExtensionPath = qtcEnvironmentVariable(cdbExtensionPathVariableC);
if (!oldCdbExtensionPath.isEmpty())
- inferiorEnvironment.appendOrSet(cdbExtensionPathVariableC, oldCdbExtensionPath, {";"});
+ inferiorEnvironment.appendOrSet(cdbExtensionPathVariableC, oldCdbExtensionPath, pSep);
m_process.setEnvironment(inferiorEnvironment);
if (!sp.inferior.workingDirectory.isEmpty())
@@ -485,10 +488,10 @@ void CdbEngine::handleInitialSessionIdle()
runCommand({"sxn ibp", NoFlags}); // Do not break on initial breakpoints.
runCommand({".asm source_line", NoFlags}); // Source line in assembly
runCommand({m_extensionCommandPrefix
- + "setparameter maxStringLength=" + QString::number(s.maximalStringLength.value())
- + " maxStackDepth=" + QString::number(s.maximalStackDepth.value())
- + " firstChance=" + (s.firstChanceExceptionTaskEntry.value() ? "1" : "0")
- + " secondChance=" + (s.secondChanceExceptionTaskEntry.value() ? "1" : "0")
+ + "setparameter maxStringLength=" + QString::number(s.maximalStringLength())
+ + " maxStackDepth=" + QString::number(s.maximalStackDepth())
+ + " firstChance=" + (s.firstChanceExceptionTaskEntry() ? "1" : "0")
+ + " secondChance=" + (s.secondChanceExceptionTaskEntry() ? "1" : "0")
, NoFlags});
if (s.cdbUsePythonDumper.value())
@@ -954,9 +957,11 @@ void CdbEngine::runCommand(const DebuggerCommand &dbgCmd)
return;
}
- if (dbgCmd.flags == ScriptCommand) {
+ if (dbgCmd.flags & ScriptCommand) {
// repack script command into an extension command
DebuggerCommand newCmd("script", ExtensionCommand, dbgCmd.callback);
+ if (dbgCmd.flags & DebuggerCommand::Silent)
+ newCmd.flags |= DebuggerCommand::Silent;
if (!dbgCmd.args.isNull())
newCmd.args = QString{dbgCmd.function + '(' + dbgCmd.argsToPython() + ')'};
else
@@ -975,7 +980,7 @@ void CdbEngine::runCommand(const DebuggerCommand &dbgCmd)
} else {
const int token = ++m_nextCommandToken;
StringInputStream str(fullCmd);
- if (dbgCmd.flags == BuiltinCommand) {
+ if (dbgCmd.flags & BuiltinCommand) {
// Post a built-in-command producing free-format output with a callback.
// In order to catch the output, it is enclosed in 'echo' commands
// printing a specially formatted token to be identifiable in the output.
@@ -986,7 +991,7 @@ void CdbEngine::runCommand(const DebuggerCommand &dbgCmd)
showMessage("Command is longer than 4096 characters execution will likely fail.",
LogWarning);
}
- } else if (dbgCmd.flags == ExtensionCommand) {
+ } else if (dbgCmd.flags & ExtensionCommand) {
// Post an extension command producing one-line output with a callback,
// pass along token for identification in hash.
@@ -1020,7 +1025,8 @@ void CdbEngine::runCommand(const DebuggerCommand &dbgCmd)
if (debug) {
qDebug("CdbEngine::postCommand: resulting command '%s'\n", qPrintable(fullCmd));
}
- showMessage(cmd, LogInput);
+ if (!(dbgCmd.flags & DebuggerCommand::Silent))
+ showMessage(cmd, LogInput);
m_process.write(fullCmd);
}
@@ -1441,15 +1447,16 @@ void CdbEngine::reloadModules()
runCommand({"modules", ExtensionCommand, CB(handleModules)});
}
-void CdbEngine::loadSymbols(const QString & /* moduleName */)
+void CdbEngine::loadSymbols(const FilePath &moduleName)
{
+ Q_UNUSED(moduleName)
}
void CdbEngine::loadAllSymbols()
{
}
-void CdbEngine::requestModuleSymbols(const QString &moduleName)
+void CdbEngine::requestModuleSymbols(const FilePath &moduleName)
{
Q_UNUSED(moduleName)
}
@@ -1487,12 +1494,13 @@ void CdbEngine::handleModules(const DebuggerResponse &response)
{
if (response.resultClass == ResultDone) {
if (response.data.type() == GdbMi::List) {
+ const FilePath inferior = runParameters().inferior.command.executable();
ModulesHandler *handler = modulesHandler();
handler->beginUpdateAll();
for (const GdbMi &gdbmiModule : response.data) {
Module module;
module.moduleName = gdbmiModule["name"].data();
- module.modulePath = gdbmiModule["image"].data();
+ module.modulePath = inferior.withNewPath(gdbmiModule["image"].data());
module.startAddress = gdbmiModule["start"].data().toULongLong(nullptr, 0);
module.endAddress = gdbmiModule["end"].data().toULongLong(nullptr, 0);
if (gdbmiModule["deferred"].type() == GdbMi::Invalid)
@@ -2254,12 +2262,10 @@ void CdbEngine::checkQtSdkPdbFiles(const QString &module)
"symbols for the debugger.")
.arg(qtName);
- CheckableMessageBox::doNotShowAgainInformation(
- Core::ICore::dialogParent(),
- Tr::tr("Missing Qt Debug Information"),
- message,
- Core::ICore::settings(),
- "CdbQtSdkPdbHint");
+ CheckableMessageBox::information(Core::ICore::dialogParent(),
+ Tr::tr("Missing Qt Debug Information"),
+ message,
+ QString("CdbQtSdkPdbHint"));
showMessage("Missing Qt Debug Information Files package for " + qtName, LogMisc);
};
@@ -2280,15 +2286,14 @@ void CdbEngine::parseOutputLine(QString line)
// An extension notification (potentially consisting of several chunks)
if (!m_initialSessionIdleHandled && line.startsWith("SECURE: File not allowed to be loaded")
&& line.endsWith("qtcreatorcdbext.dll")) {
- CheckableMessageBox::doNotShowAgainInformation(
+ CheckableMessageBox::information(
Core::ICore::dialogParent(),
Tr::tr("Debugger Start Failed"),
Tr::tr("The system prevents loading of %1, which is required for debugging. "
"Make sure that your antivirus solution is up to date and if that does not work "
"consider adding an exception for %1.")
.arg(m_extensionFileName),
- Core::ICore::settings(),
- "SecureInfoCdbextCannotBeLoaded");
+ QString("SecureInfoCdbextCannotBeLoaded"));
notifyEngineSetupFailed();
}
static const QString creatorExtPrefix = "<qtcreatorcdbext>|";
@@ -2459,8 +2464,8 @@ static CPlusPlus::Document::Ptr getParsedDocument(const FilePath &filePath,
const CPlusPlus::Snapshot &snapshot)
{
QByteArray src;
- if (workingCopy.contains(filePath))
- src = workingCopy.source(filePath);
+ if (const auto source = workingCopy.source(filePath))
+ src = *source;
else
src = QString::fromLocal8Bit(filePath.fileContents().value_or(QByteArray())).toUtf8();
@@ -2764,17 +2769,65 @@ void CdbEngine::setupScripting(const DebuggerResponse &response)
return;
}
- QString dumperPath = runParameters().dumperPath.toUserOutput();
- dumperPath.replace('\\', "\\\\");
- runCommand({"sys.path.insert(1, '" + dumperPath + "')", ScriptCommand});
- runCommand({"from cdbbridge import Dumper", ScriptCommand});
- runCommand({"print(dir())", ScriptCommand});
- runCommand({"theDumper = Dumper()", ScriptCommand});
- const QString path = debuggerSettings()->extraDumperFile.value();
- if (!path.isEmpty() && QFileInfo(path).isReadable()) {
+ if (runParameters().startMode == AttachToRemoteServer) {
+ FilePath dumperPath = Core::ICore::resourcePath("debugger");
+ const FilePath loadOrderFile = dumperPath / "loadorder.txt";
+ const expected_str<QByteArray> toLoad = loadOrderFile.fileContents();
+ if (!toLoad) {
+ Core::AsynchronousMessageBox::critical(
+ Tr::tr("Cannot Find Debugger Initialization Script"),
+ Tr::tr("Cannot read %1: %2").arg(loadOrderFile.toUserOutput(), toLoad.error()));
+ notifyEngineSetupFailed();
+ return;
+ }
+
+ runCommand({"import sys, types", ScriptCommand});
+ QStringList moduleList;
+ for (const QByteArray &rawModuleName : toLoad->split('\n')) {
+ QString module = QString::fromUtf8(rawModuleName).trimmed();
+ if (module.startsWith('#') || module.isEmpty())
+ continue;
+ if (module == "***bridge***")
+ module = "cdbbridge";
+
+ const FilePath codeFile = dumperPath / (module + ".py");
+ const expected_str<QByteArray> code = codeFile.fileContents();
+ if (!code) {
+ qDebug() << Tr::tr("Cannot read %1: %2").arg(codeFile.toUserOutput(), code.error());
+ continue;
+ }
+
+ showMessage("Reading " + codeFile.toUserOutput(), LogInput);
+ runCommand({QString("module = types.ModuleType('%1')").arg(module), ScriptCommand});
+ runCommand({QString("code = bytes.fromhex('%1').decode('utf-8')")
+ .arg(QString::fromUtf8(code->toHex())), ScriptCommand | DebuggerCommand::Silent});
+ runCommand({QString("exec(code, module.__dict__)"), ScriptCommand});
+ runCommand({QString("sys.modules['%1'] = module").arg(module), ScriptCommand});
+ runCommand({QString("import %1").arg(module), ScriptCommand});
+
+ if (module.endsWith("types"))
+ moduleList.append('"' + module + '"');
+ }
+
+ runCommand({"from cdbbridge import Dumper", ScriptCommand});
+ runCommand({"print(dir())", ScriptCommand});
+ runCommand({"theDumper = Dumper()", ScriptCommand});
+ runCommand({QString("theDumper.dumpermodules = [%1]").arg(moduleList.join(',')), ScriptCommand});
+
+ } else {
+ QString dumperPath = Core::ICore::resourcePath("debugger").toUserOutput();
+ dumperPath.replace('\\', "\\\\");
+ runCommand({"sys.path.insert(1, '" + dumperPath + "')", ScriptCommand});
+ runCommand({"from cdbbridge import Dumper", ScriptCommand});
+ runCommand({"print(dir())", ScriptCommand});
+ runCommand({"theDumper = Dumper()", ScriptCommand});
+ }
+
+ const FilePath path = debuggerSettings()->extraDumperFile();
+ if (!path.isEmpty() && path.isReadableFile()) {
DebuggerCommand cmd("theDumper.addDumperModule", ScriptCommand);
- cmd.arg("path", path);
+ cmd.arg("path", path.path());
runCommand(cmd);
}
const QString commands = debuggerSettings()->extraDumperCommands.value();
diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h
index 3bea692d5b..84a0dded1a 100644
--- a/src/plugins/debugger/cdb/cdbengine.h
+++ b/src/plugins/debugger/cdb/cdbengine.h
@@ -10,7 +10,7 @@
#include <projectexplorer/devicesupport/idevice.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QElapsedTimer>
@@ -65,9 +65,9 @@ public:
void changeMemory(MemoryAgent *, quint64 addr, const QByteArray &data) override;
void reloadModules() override;
- void loadSymbols(const QString &moduleName) override;
+ void loadSymbols(const Utils::FilePath &moduleName) override;
void loadAllSymbols() override;
- void requestModuleSymbols(const QString &moduleName) override;
+ void requestModuleSymbols(const Utils::FilePath &moduleName) override;
void reloadRegisters() override;
void reloadSourceFiles() override;
@@ -110,9 +110,9 @@ private:
};
enum CommandFlags {
NoFlags = 0,
- BuiltinCommand,
- ExtensionCommand,
- ScriptCommand
+ BuiltinCommand = DebuggerCommand::Silent << 1,
+ ExtensionCommand = DebuggerCommand::Silent << 2,
+ ScriptCommand = DebuggerCommand::Silent << 3
};
void init();
@@ -174,7 +174,7 @@ private:
const QString m_tokenPrefix;
void handleSetupFailure(const QString &errorMessage);
- Utils::QtcProcess m_process;
+ Utils::Process m_process;
DebuggerStartMode m_effectiveStartMode = NoStartMode;
//! Debugger accessible (expecting commands)
bool m_accessible = false;
diff --git a/src/plugins/debugger/commonoptionspage.cpp b/src/plugins/debugger/commonoptionspage.cpp
index dc47fb79e0..97e9442266 100644
--- a/src/plugins/debugger/commonoptionspage.cpp
+++ b/src/plugins/debugger/commonoptionspage.cpp
@@ -15,8 +15,7 @@ using namespace Core;
using namespace Debugger::Constants;
using namespace Utils;
-namespace Debugger {
-namespace Internal {
+namespace Debugger::Internal {
///////////////////////////////////////////////////////////////////////
//
@@ -30,6 +29,19 @@ public:
explicit CommonOptionsPageWidget()
{
DebuggerSettings &s = *debuggerSettings();
+
+ setOnApply([&s] {
+ const bool originalPostMortem = s.registerForPostMortem->value();
+ const bool currentPostMortem = s.registerForPostMortem->volatileValue().toBool();
+ // explicitly trigger setValue() to override the setValueSilently() and trigger the registration
+ if (originalPostMortem != currentPostMortem)
+ s.registerForPostMortem->setValue(currentPostMortem);
+ s.page1.apply();
+ s.page1.writeSettings(ICore::settings());
+ });
+
+ setOnFinish([&s] { s.page1.finish(); });
+
using namespace Layouting;
Column col1 {
@@ -60,27 +72,8 @@ public:
st
}.attachTo(this);
}
-
- void apply() final;
- void finish() final { m_group.finish(); }
-
-private:
- AspectContainer &m_group = debuggerSettings()->page1;
};
-void CommonOptionsPageWidget::apply()
-{
- const DebuggerSettings *s = debuggerSettings();
- const bool originalPostMortem = s->registerForPostMortem->value();
- const bool currentPostMortem = s->registerForPostMortem->volatileValue().toBool();
- // explicitly trigger setValue() to override the setValueSilently() and trigger the registration
- if (originalPostMortem != currentPostMortem)
- s->registerForPostMortem->setValue(currentPostMortem);
-
- m_group.apply();
- m_group.writeSettings(ICore::settings());
-}
-
CommonOptionsPage::CommonOptionsPage()
{
setId(DEBUGGER_COMMON_SETTINGS_ID);
@@ -122,8 +115,9 @@ class LocalsAndExpressionsOptionsPageWidget : public IOptionsPageWidget
public:
LocalsAndExpressionsOptionsPageWidget()
{
- using namespace Layouting;
DebuggerSettings &s = *debuggerSettings();
+ setOnApply([&s] { s.page4.apply(); s.page4.writeSettings(ICore::settings()); });
+ setOnFinish([&s] { s.page4.finish(); });
auto label = new QLabel; //(useHelperGroup);
label->setTextFormat(Qt::AutoText);
@@ -134,6 +128,7 @@ public:
"std::map in the &quot;Locals&quot; and &quot;Expressions&quot; views.")
+ "</p></body></html>");
+ using namespace Layouting;
Column left {
label,
s.useCodeModel,
@@ -153,7 +148,8 @@ public:
Grid limits {
s.maximalStringLength, br,
- s.displayStringLimit
+ s.displayStringLimit, br,
+ s.defaultArraySize
};
Column {
@@ -168,12 +164,6 @@ public:
st
}.attachTo(this);
}
-
- void apply() final { m_group.apply(); m_group.writeSettings(ICore::settings()); }
- void finish() final { m_group.finish(); }
-
-private:
- AspectContainer &m_group = debuggerSettings()->page4;
};
LocalsAndExpressionsOptionsPage::LocalsAndExpressionsOptionsPage()
@@ -185,5 +175,4 @@ LocalsAndExpressionsOptionsPage::LocalsAndExpressionsOptionsPage()
setWidgetCreator([] { return new LocalsAndExpressionsOptionsPageWidget; });
}
-} // namespace Internal
-} // namespace Debugger
+} // Debugger::Internal
diff --git a/src/plugins/debugger/dap/dapengine.cpp b/src/plugins/debugger/dap/dapengine.cpp
new file mode 100644
index 0000000000..9db7c0f7e5
--- /dev/null
+++ b/src/plugins/debugger/dap/dapengine.cpp
@@ -0,0 +1,799 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "dapengine.h"
+
+#include <debugger/breakhandler.h>
+#include <debugger/debuggeractions.h>
+#include <debugger/debuggercore.h>
+#include <debugger/debuggerdialogs.h>
+#include <debugger/debuggerplugin.h>
+#include <debugger/debuggerprotocol.h>
+#include <debugger/debuggertooltipmanager.h>
+#include <debugger/debuggertr.h>
+#include <debugger/moduleshandler.h>
+#include <debugger/procinterrupt.h>
+#include <debugger/registerhandler.h>
+#include <debugger/sourceutils.h>
+#include <debugger/stackhandler.h>
+#include <debugger/threaddata.h>
+#include <debugger/watchhandler.h>
+#include <debugger/watchutils.h>
+
+#include <utils/algorithm.h>
+#include <utils/environment.h>
+#include <utils/process.h>
+#include <utils/qtcassert.h>
+
+#include <coreplugin/idocument.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/messagebox.h>
+
+#include <QDateTime>
+#include <QDebug>
+#include <QDir>
+#include <QFileInfo>
+#include <QTimer>
+#include <QVariant>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QThread>
+
+using namespace Core;
+using namespace Utils;
+
+namespace Debugger::Internal {
+
+DapEngine::DapEngine()
+{
+ setObjectName("DapEngine");
+ setDebuggerName("DAP");
+}
+
+void DapEngine::executeDebuggerCommand(const QString &/*command*/)
+{
+ QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
+// if (state() == DebuggerNotReady) {
+// showMessage("DAP PROCESS NOT RUNNING, PLAIN CMD IGNORED: " + command);
+// return;
+// }
+// QTC_ASSERT(m_proc.isRunning(), notifyEngineIll());
+// postDirectCommand(command);
+}
+
+void DapEngine::postDirectCommand(const QJsonObject &ob)
+{
+ static int seq = 1;
+ QJsonObject obseq = ob;
+ obseq.insert("seq", seq++);
+
+ const QByteArray data = QJsonDocument(obseq).toJson(QJsonDocument::Compact);
+ const QByteArray msg = "Content-Length: " + QByteArray::number(data.size()) + "\r\n\r\n" + data;
+ qDebug() << msg;
+
+ m_proc.writeRaw(msg);
+
+ showMessage(QString::fromUtf8(msg), LogInput);
+}
+
+void DapEngine::runCommand(const DebuggerCommand &cmd)
+{
+ if (state() == EngineSetupRequested) { // cmd has been triggered too early
+ showMessage("IGNORED COMMAND: " + cmd.function);
+ return;
+ }
+ QTC_ASSERT(m_proc.isRunning(), notifyEngineIll());
+// postDirectCommand(cmd.args.toObject());
+// const QByteArray data = QJsonDocument(cmd.args.toObject()).toJson(QJsonDocument::Compact);
+// m_proc.writeRaw("Content-Length: " + QByteArray::number(data.size()) + "\r\n" + data + "\r\n");
+
+// showMessage(QString::fromUtf8(data), LogInput);
+}
+
+void DapEngine::shutdownInferior()
+{
+ QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state());
+ postDirectCommand({{"command", "terminate"},
+ {"type", "request"}});
+
+ qDebug() << "DapEngine::shutdownInferior()";
+ notifyInferiorShutdownFinished();
+}
+
+void DapEngine::shutdownEngine()
+{
+ QTC_ASSERT(state() == EngineShutdownRequested, qDebug() << state());
+
+ qDebug() << "DapEngine::shutdownEngine()";
+ m_proc.kill();
+}
+
+void DapEngine::setupEngine()
+{
+ QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
+
+ connect(&m_proc, &Process::started, this, &DapEngine::handleDabStarted);
+ connect(&m_proc, &Process::done, this, &DapEngine::handleDapDone);
+ connect(&m_proc, &Process::readyReadStandardOutput, this, &DapEngine::readDapStandardOutput);
+ connect(&m_proc, &Process::readyReadStandardError, this, &DapEngine::readDapStandardError);
+
+ const DebuggerRunParameters &rp = runParameters();
+ const CommandLine cmd{rp.debugger.command.executable(), {"-i", "dap"}};
+ showMessage("STARTING " + cmd.toUserOutput());
+ m_proc.setProcessMode(ProcessMode::Writer);
+ m_proc.setEnvironment(rp.debugger.environment);
+ m_proc.setCommand(cmd);
+ m_proc.start();
+ notifyEngineRunAndInferiorRunOk();
+}
+
+// From the docs:
+// The sequence of events/requests is as follows:
+// * adapters sends initialized event (after the initialize request has returned)
+// * client sends zero or more setBreakpoints requests
+// * client sends one setFunctionBreakpoints request
+// (if corresponding capability supportsFunctionBreakpoints is true)
+// * client sends a setExceptionBreakpoints request if one or more exceptionBreakpointFilters
+// have been defined (or if supportsConfigurationDoneRequest is not true)
+// * client sends other future configuration requests
+// * client sends one configurationDone request to indicate the end of the configuration.
+
+void DapEngine::handleDabStarted()
+{
+ notifyEngineSetupOk();
+ QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
+
+// CHECK_STATE(EngineRunRequested);
+
+ postDirectCommand({
+ {"command", "initialize"},
+ {"type", "request"},
+ {"arguments", QJsonObject {
+ {"clientID", "QtCreator"}, // The ID of the client using this adapter.
+ {"clientName", "QtCreator"} // The human-readable name of the client using this adapter.
+ }}
+ });
+
+ qDebug() << "handleDabStarted";
+}
+
+void DapEngine::handleDabConfigurationDone()
+{
+ QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
+
+ // CHECK_STATE(EngineRunRequested);
+
+ postDirectCommand({{"command", "configurationDone"}, {"type", "request"}});
+
+ qDebug() << "handleDabConfigurationDone";
+}
+
+
+void DapEngine::handleDabLaunch()
+{
+ QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
+
+ // CHECK_STATE(EngineRunRequested);
+
+ postDirectCommand(
+ {{"command", "launch"},
+ {"type", "request"},
+// {"program", runParameters().inferior.command.executable().path()},
+ {"arguments",
+ QJsonObject{
+ {"noDebug", false},
+ {"program", runParameters().inferior.command.executable().path()},
+ {"__restart", ""}
+ }}});
+ qDebug() << "handleDabLaunch";
+}
+
+void DapEngine::interruptInferior()
+{
+ postDirectCommand({{"command", "pause"},
+ {"type", "request"}});
+}
+
+void DapEngine::executeStepIn(bool)
+{
+ notifyInferiorRunRequested();
+
+// postDirectCommand({{"command", "stepIn"},
+// {"type", "request"},
+// {"arguments",
+// QJsonObject{
+// {"threadId", 1}, // The ID of the client using this adapter.
+// }}});
+
+ notifyInferiorRunOk();
+}
+
+void DapEngine::executeStepOut()
+{
+ notifyInferiorRunRequested();
+ notifyInferiorRunOk();
+// postDirectCommand("return");
+}
+
+void DapEngine::executeStepOver(bool)
+{
+ notifyInferiorRunRequested();
+ notifyInferiorRunOk();
+// postDirectCommand("next");
+}
+
+void DapEngine::continueInferior()
+{
+ notifyInferiorRunRequested();
+ postDirectCommand({{"command", "continue"},
+ {"type", "request"},
+ {"arguments",
+ QJsonObject{
+ {"threadId", 1}, // The ID of the client using this adapter.
+ }}});
+}
+
+void DapEngine::executeRunToLine(const ContextData &data)
+{
+ Q_UNUSED(data)
+ QTC_CHECK("FIXME: DapEngine::runToLineExec()" && false);
+}
+
+void DapEngine::executeRunToFunction(const QString &functionName)
+{
+ Q_UNUSED(functionName)
+ QTC_CHECK("FIXME: DapEngine::runToFunctionExec()" && false);
+}
+
+void DapEngine::executeJumpToLine(const ContextData &data)
+{
+ Q_UNUSED(data)
+ QTC_CHECK("FIXME: DapEngine::jumpToLineExec()" && false);
+}
+
+void DapEngine::activateFrame(int frameIndex)
+{
+ if (state() != InferiorStopOk && state() != InferiorUnrunnable)
+ return;
+
+ StackHandler *handler = stackHandler();
+ QTC_ASSERT(frameIndex < handler->stackSize(), return);
+ handler->setCurrentIndex(frameIndex);
+ gotoLocation(handler->currentFrame());
+ updateLocals();
+}
+
+void DapEngine::selectThread(const Thread &thread)
+{
+ Q_UNUSED(thread)
+}
+
+bool DapEngine::acceptsBreakpoint(const BreakpointParameters &) const
+{
+ return true; // FIXME: Too bold.
+}
+
+static QJsonObject createBreakpoint(const Breakpoint &breakpoint)
+{
+ const BreakpointParameters &params = breakpoint->requestedParameters();
+
+ if (params.fileName.isEmpty())
+ return QJsonObject();
+
+ QJsonObject bp;
+ bp["line"] = params.lineNumber;
+ bp["source"] = QJsonObject{{"name", params.fileName.fileName()},
+ {"path", params.fileName.path()}};
+ return bp;
+}
+
+
+void DapEngine::insertBreakpoint(const Breakpoint &bp)
+{
+ QTC_ASSERT(bp, return);
+ QTC_CHECK(bp->state() == BreakpointInsertionRequested);
+ notifyBreakpointInsertProceeding(bp);
+
+ const BreakpointParameters &params = bp->requestedParameters();
+ bp->setResponseId(QString::number(m_nextBreakpointId++));
+
+ QJsonArray breakpoints;
+ for (const auto &breakpoint : breakHandler()->breakpoints()) {
+ QJsonObject jsonBp = createBreakpoint(breakpoint);
+ if (!jsonBp.isEmpty()
+ && params.fileName.path() == jsonBp["source"].toObject()["path"].toString()) {
+ breakpoints.append(jsonBp);
+ }
+ }
+
+ postDirectCommand(
+ {{"command", "setBreakpoints"},
+ {"type", "request"},
+ {"arguments",
+ QJsonObject{{"source", QJsonObject{{"path", params.fileName.path()}}},
+ {"breakpoints", breakpoints}
+
+ }}});
+
+ qDebug() << "insertBreakpoint" << bp->modelId() << bp->responseId();
+}
+
+void DapEngine::updateBreakpoint(const Breakpoint &bp)
+{
+ notifyBreakpointChangeProceeding(bp);
+// QTC_ASSERT(bp, return);
+// const BreakpointState state = bp->state();
+// if (QTC_GUARD(state == BreakpointUpdateRequested))
+// if (bp->responseId().isEmpty()) // FIXME postpone update somehow (QTimer::singleShot?)
+// return;
+
+// // FIXME figure out what needs to be changed (there might be more than enabled state)
+// const BreakpointParameters &requested = bp->requestedParameters();
+// if (requested.enabled != bp->isEnabled()) {
+// if (bp->isEnabled())
+// postDirectCommand("disable " + bp->responseId());
+// else
+// postDirectCommand("enable " + bp->responseId());
+// bp->setEnabled(!bp->isEnabled());
+// }
+// // Pretend it succeeds without waiting for response.
+ notifyBreakpointChangeOk(bp);
+}
+
+void DapEngine::removeBreakpoint(const Breakpoint &bp)
+{
+ QTC_ASSERT(bp, return);
+ QTC_CHECK(bp->state() == BreakpointRemoveRequested);
+ notifyBreakpointRemoveProceeding(bp);
+
+ const BreakpointParameters &params = bp->requestedParameters();
+
+ QJsonArray breakpoints;
+ for (const auto &breakpoint : breakHandler()->breakpoints())
+ if (breakpoint->responseId() != bp->responseId()
+ && params.fileName == breakpoint->requestedParameters().fileName) {
+ QJsonObject jsonBp = createBreakpoint(breakpoint);
+ breakpoints.append(jsonBp);
+ }
+
+ postDirectCommand({{"command", "setBreakpoints"},
+ {"type", "request"},
+ {"arguments",
+ QJsonObject{{"source", QJsonObject{{"path", params.fileName.path()}}},
+ {"breakpoints", breakpoints}}}});
+
+ qDebug() << "removeBreakpoint" << bp->modelId() << bp->responseId();
+}
+
+void DapEngine::loadSymbols(const Utils::FilePath &/*moduleName*/)
+{
+}
+
+void DapEngine::loadAllSymbols()
+{
+}
+
+void DapEngine::reloadModules()
+{
+ runCommand({"listModules"});
+}
+
+void DapEngine::refreshModules(const GdbMi &modules)
+{
+ ModulesHandler *handler = modulesHandler();
+ handler->beginUpdateAll();
+ for (const GdbMi &item : modules) {
+ Module module;
+ module.moduleName = item["name"].data();
+ QString path = item["value"].data();
+ int pos = path.indexOf("' from '");
+ if (pos != -1) {
+ path = path.mid(pos + 8);
+ if (path.size() >= 2)
+ path.chop(2);
+ } else if (path.startsWith("<module '")
+ && path.endsWith("' (built-in)>")) {
+ path = "(builtin)";
+ }
+ module.modulePath = FilePath::fromString(path);
+ handler->updateModule(module);
+ }
+ handler->endUpdateAll();
+}
+
+void DapEngine::requestModuleSymbols(const Utils::FilePath &/*moduleName*/)
+{
+// DebuggerCommand cmd("listSymbols");
+// cmd.arg("module", moduleName);
+// runCommand(cmd);
+}
+
+void DapEngine::refreshState(const GdbMi &reportedState)
+{
+ QString newState = reportedState.data();
+ if (newState == "stopped") {
+ notifyInferiorSpontaneousStop();
+ updateAll();
+ } else if (newState == "inferiorexited") {
+ notifyInferiorExited();
+ }
+}
+
+void DapEngine::refreshLocation(const GdbMi &reportedLocation)
+{
+ StackFrame frame;
+ frame.file = FilePath::fromString(reportedLocation["file"].data());
+ frame.line = reportedLocation["line"].toInt();
+ frame.usable = frame.file.isReadableFile();
+ if (state() == InferiorRunOk) {
+ showMessage(QString("STOPPED AT: %1:%2").arg(frame.file.toUserOutput()).arg(frame.line));
+ gotoLocation(frame);
+ notifyInferiorSpontaneousStop();
+ updateAll();
+ }
+}
+
+void DapEngine::refreshSymbols(const GdbMi &symbols)
+{
+ QString moduleName = symbols["module"].data();
+ Symbols syms;
+ for (const GdbMi &item : symbols["symbols"]) {
+ Symbol symbol;
+ symbol.name = item["name"].data();
+ syms.append(symbol);
+ }
+ showModuleSymbols(FilePath::fromString(moduleName), syms);
+}
+
+bool DapEngine::canHandleToolTip(const DebuggerToolTipContext &) const
+{
+ return state() == InferiorStopOk;
+}
+
+void DapEngine::assignValueInDebugger(WatchItem *, const QString &/*expression*/, const QVariant &/*value*/)
+{
+ //DebuggerCommand cmd("assignValue");
+ //cmd.arg("expression", expression);
+ //cmd.arg("value", value.toString());
+ //runCommand(cmd);
+// postDirectCommand("global " + expression + ';' + expression + "=" + value.toString());
+ updateLocals();
+}
+
+void DapEngine::updateItem(const QString &iname)
+{
+ Q_UNUSED(iname)
+ updateAll();
+}
+
+QString DapEngine::errorMessage(QProcess::ProcessError error) const
+{
+ switch (error) {
+ case QProcess::FailedToStart:
+ return Tr::tr("The DAP process failed to start. Either the "
+ "invoked program \"%1\" is missing, or you may have insufficient "
+ "permissions to invoke the program.")
+ .arg(m_proc.commandLine().executable().toUserOutput());
+ case QProcess::Crashed:
+ return Tr::tr("The DAP process crashed some time after starting "
+ "successfully.");
+ case QProcess::Timedout:
+ return Tr::tr("The last waitFor...() function timed out. "
+ "The state of QProcess is unchanged, and you can try calling "
+ "waitFor...() again.");
+ case QProcess::WriteError:
+ return Tr::tr("An error occurred when attempting to write "
+ "to the DAP process. For example, the process may not be running, "
+ "or it may have closed its input channel.");
+ case QProcess::ReadError:
+ return Tr::tr("An error occurred when attempting to read from "
+ "the DAP process. For example, the process may not be running.");
+ default:
+ return Tr::tr("An unknown error in the DAP process occurred.") + ' ';
+ }
+}
+
+void DapEngine::handleDapDone()
+{
+ if (m_proc.result() == ProcessResult::StartFailed) {
+ notifyEngineSetupFailed();
+ showMessage("ADAPTER START FAILED");
+ ICore::showWarningWithOptions(Tr::tr("Adapter start failed"), m_proc.exitMessage());
+ return;
+ }
+
+ const QProcess::ProcessError error = m_proc.error();
+ if (error != QProcess::UnknownError) {
+ showMessage("HANDLE DAP ERROR");
+ if (error != QProcess::Crashed)
+ AsynchronousMessageBox::critical(Tr::tr("DAP I/O Error"), errorMessage(error));
+ if (error == QProcess::FailedToStart)
+ return;
+ }
+ showMessage(QString("DAP PROCESS FINISHED, status %1, code %2")
+ .arg(m_proc.exitStatus()).arg(m_proc.exitCode()));
+ notifyEngineSpontaneousShutdown();
+}
+
+void DapEngine::readDapStandardError()
+{
+ QString err = m_proc.readAllStandardError();
+ //qWarning() << "Unexpected DAP stderr:" << err;
+ showMessage("Unexpected DAP stderr: " + err);
+ //handleOutput(err);
+}
+
+void DapEngine::readDapStandardOutput()
+{
+ m_inbuffer.append(m_proc.readAllStandardOutput().toUtf8());
+// qDebug() << m_inbuffer;
+
+ while (true) {
+ // Something like
+ // Content-Length: 128\r\n
+ // {"type": "event", "event": "output", "body": {"category": "stdout", "output": "...\n"}, "seq": 1}\r\n
+ // FIXME: There coud be more than one header line.
+ int pos1 = m_inbuffer.indexOf("Content-Length:");
+ if (pos1 == -1)
+ break;
+ pos1 += 15;
+
+ int pos2 = m_inbuffer.indexOf('\n', pos1);
+ if (pos2 == -1)
+ break;
+
+ const int len = m_inbuffer.mid(pos1, pos2 - pos1).trimmed().toInt();
+ if (len < 4)
+ break;
+
+ pos2 += 3; // Skip \r\n\r
+
+ if (pos2 + len > m_inbuffer.size())
+ break;
+
+ QJsonParseError error;
+ const auto doc = QJsonDocument::fromJson(m_inbuffer.mid(pos2, len), &error);
+
+ m_inbuffer = m_inbuffer.mid(pos2 + len);
+
+ handleOutput(doc);
+ }
+}
+
+void DapEngine::handleOutput(const QJsonDocument &data)
+{
+ QJsonObject ob = data.object();
+
+ const QJsonValue t = ob.value("type");
+ const QString type = t.toString();
+ qDebug() << "response" << ob;
+
+ if (type == "response") {
+ const QString command = ob.value("command").toString();
+ if (command == "configurationDone") {
+ showMessage("configurationDone", LogDebug);
+ qDebug() << "configurationDone success";
+ notifyInferiorRunOk();
+ return;
+ }
+
+ if (command == "continue") {
+ showMessage("continue", LogDebug);
+ qDebug() << "continue success";
+ notifyInferiorRunOk();
+ return;
+ }
+
+ }
+
+ if (type == "event") {
+ const QString event = ob.value("event").toString();
+ const QJsonObject body = ob.value("body").toObject();
+
+ if (event == "output") {
+ const QString category = body.value("category").toString();
+ const QString output = body.value("output").toString();
+ if (category == "stdout")
+ showMessage(output, AppOutput);
+ else if (category == "stderr")
+ showMessage(output, AppError);
+ else
+ showMessage(output, LogDebug);
+ return;
+ }
+ qDebug() << data;
+
+ if (event == "initialized") {
+ showMessage(event, LogDebug);
+ qDebug() << "initialize success";
+ handleDabLaunch();
+ handleDabConfigurationDone();
+ return;
+ }
+
+ if (event == "initialized") {
+ showMessage(event, LogDebug);
+ return;
+ }
+
+ if (event == "stopped") {
+ showMessage(event, LogDebug);
+ if (body.value("reason").toString() == "breakpoint") {
+ QString id = QString::number(body.value("hitBreakpointIds").toArray().first().toInteger());
+ const BreakpointParameters &params
+ = breakHandler()->findBreakpointByResponseId(id)->requestedParameters();
+ gotoLocation(Location(params.fileName, params.lineNumber));
+ }
+
+ if (state() == InferiorStopRequested)
+ notifyInferiorStopOk();
+ else
+ notifyInferiorSpontaneousStop();
+ return;
+ }
+
+ if (event == "thread") {
+ showMessage(event, LogDebug);
+ if (body.value("reason").toString() == "started" && body.value("threadId").toInt() == 1)
+ claimInitialBreakpoints();
+ return;
+ }
+
+ if (event == "breakpoint") {
+ showMessage(event, LogDebug);
+ QJsonObject breakpoint = body.value("breakpoint").toObject();
+ Breakpoint bp = breakHandler()->findBreakpointByResponseId(
+ QString::number(breakpoint.value("id").toInt()));
+ qDebug() << "breakpoint id :" << breakpoint.value("id").toInt();
+
+ if (body.value("reason").toString() == "new") {
+ if (breakpoint.value("verified").toBool()) {
+// bp->setPending(false);
+ notifyBreakpointInsertOk(bp);
+ qDebug() << "breakpoint inserted";
+ } else {
+ notifyBreakpointInsertFailed(bp);
+ qDebug() << "breakpoint insertion failed";
+ }
+ return;
+ }
+
+ if (body.value("reason").toString() == "removed") {
+ if (breakpoint.value("verified").toBool()) {
+ notifyBreakpointRemoveOk(bp);
+ qDebug() << "breakpoint removed";
+ } else {
+ notifyBreakpointRemoveFailed(bp);
+ qDebug() << "breakpoint remove failed";
+ }
+ return;
+ }
+ return;
+ }
+
+
+ showMessage("UNKNOWN EVENT:" + event);
+ return;
+ }
+
+ showMessage("UNKNOWN TYPE:" + type);
+}
+
+void DapEngine::refreshLocals(const GdbMi &vars)
+{
+ WatchHandler *handler = watchHandler();
+ handler->resetValueCache();
+ handler->insertItems(vars);
+ handler->notifyUpdateFinished();
+
+ updateToolTips();
+}
+
+void DapEngine::refreshStack(const GdbMi &stack)
+{
+ StackHandler *handler = stackHandler();
+ StackFrames frames;
+ for (const GdbMi &item : stack["frames"]) {
+ StackFrame frame;
+ frame.level = item["level"].data();
+ frame.file = FilePath::fromString(item["file"].data());
+ frame.function = item["function"].data();
+ frame.module = item["function"].data();
+ frame.line = item["line"].toInt();
+ frame.address = item["address"].toAddress();
+ GdbMi usable = item["usable"];
+ if (usable.isValid())
+ frame.usable = usable.data().toInt();
+ else
+ frame.usable = frame.file.isReadableFile();
+ frames.append(frame);
+ }
+ bool canExpand = stack["hasmore"].toInt();
+ //action(ExpandStack)->setEnabled(canExpand);
+ handler->setFrames(frames, canExpand);
+
+ int index = stackHandler()->firstUsableIndex();
+ handler->setCurrentIndex(index);
+ if (index >= 0 && index < handler->stackSize())
+ gotoLocation(handler->frameAt(index));
+}
+
+void DapEngine::updateAll()
+{
+ runCommand({"stackListFrames"});
+ updateLocals();
+}
+
+void DapEngine::updateLocals()
+{
+// DebuggerCommand cmd("updateData");
+// cmd.arg("nativeMixed", isNativeMixedActive());
+// watchHandler()->appendFormatRequests(&cmd);
+// watchHandler()->appendWatchersAndTooltipRequests(&cmd);
+
+// const bool alwaysVerbose = qtcEnvironmentVariableIsSet("QTC_DEBUGGER_PYTHON_VERBOSE");
+// cmd.arg("passexceptions", alwaysVerbose);
+// cmd.arg("fancy", debuggerSettings()->useDebuggingHelpers.value());
+
+// //cmd.arg("resultvarname", m_resultVarName);
+// //m_lastDebuggableCommand = cmd;
+// //m_lastDebuggableCommand.args.replace("\"passexceptions\":0", "\"passexceptions\":1");
+// cmd.arg("frame", stackHandler()->currentIndex());
+
+// watchHandler()->notifyUpdateStarted();
+// runCommand(cmd);
+}
+
+bool DapEngine::hasCapability(unsigned cap) const
+{
+ return cap & (ReloadModuleCapability
+ | BreakConditionCapability
+ | ShowModuleSymbolsCapability);
+}
+
+void DapEngine::claimInitialBreakpoints()
+{
+ BreakpointManager::claimBreakpointsForEngine(this);
+ qDebug() << "claimInitialBreakpoints";
+// const Breakpoints bps = breakHandler()->breakpoints();
+// for (const Breakpoint &bp : bps)
+// qDebug() << "breakpoit: " << bp->fileName() << bp->lineNumber();
+// qDebug() << "claimInitialBreakpoints end";
+
+// const DebuggerRunParameters &rp = runParameters();
+// if (rp.startMode != AttachToCore) {
+// showStatusMessage(Tr::tr("Setting breakpoints..."));
+// showMessage(Tr::tr("Setting breakpoints..."));
+// BreakpointManager::claimBreakpointsForEngine(this);
+
+// const DebuggerSettings &s = *debuggerSettings();
+// const bool onAbort = s.breakOnAbort.value();
+// const bool onWarning = s.breakOnWarning.value();
+// const bool onFatal = s.breakOnFatal.value();
+// if (onAbort || onWarning || onFatal) {
+// DebuggerCommand cmd("createSpecialBreakpoints");
+// cmd.arg("breakonabort", onAbort);
+// cmd.arg("breakonwarning", onWarning);
+// cmd.arg("breakonfatal", onFatal);
+// runCommand(cmd);
+// }
+// }
+
+// // It is ok to cut corners here and not wait for createSpecialBreakpoints()'s
+// // response, as the command is synchronous from Creator's point of view,
+// // and even if it fails (e.g. due to stripped binaries), continuing with
+// // the start up is the best we can do.
+
+// if (!rp.commandsAfterConnect.isEmpty()) {
+// const QString commands = expand(rp.commandsAfterConnect);
+// for (const QString &command : commands.split('\n'))
+// runCommand({command, NativeCommand});
+// }
+}
+
+DebuggerEngine *createDapEngine()
+{
+ return new DapEngine;
+}
+
+} // Debugger::Internal
diff --git a/src/plugins/debugger/dap/dapengine.h b/src/plugins/debugger/dap/dapengine.h
new file mode 100644
index 0000000000..2fa3019cad
--- /dev/null
+++ b/src/plugins/debugger/dap/dapengine.h
@@ -0,0 +1,98 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <debugger/debuggerengine.h>
+#include <utils/process.h>
+
+#include <QVariant>
+
+namespace Debugger::Internal {
+
+class DebuggerCommand;
+class GdbMi;
+
+/*
+ * A debugger engine for the debugger adapter protocol.
+ */
+
+class DapEngine : public DebuggerEngine
+{
+public:
+ DapEngine();
+
+private:
+ void executeStepIn(bool) override;
+ void executeStepOut() override;
+ void executeStepOver(bool) override;
+
+ void setupEngine() override;
+ void shutdownInferior() override;
+ void shutdownEngine() override;
+
+ bool canHandleToolTip(const DebuggerToolTipContext &) const override;
+
+ void continueInferior() override;
+ void interruptInferior() override;
+
+ void executeRunToLine(const ContextData &data) override;
+ void executeRunToFunction(const QString &functionName) override;
+ void executeJumpToLine(const ContextData &data) override;
+
+ void activateFrame(int index) override;
+ void selectThread(const Thread &thread) override;
+
+ bool acceptsBreakpoint(const BreakpointParameters &bp) const override;
+ void insertBreakpoint(const Breakpoint &bp) override;
+ void updateBreakpoint(const Breakpoint &bp) override;
+ void removeBreakpoint(const Breakpoint &bp) override;
+
+ void assignValueInDebugger(WatchItem *item,
+ const QString &expr, const QVariant &value) override;
+ void executeDebuggerCommand(const QString &command) override;
+
+ void loadSymbols(const Utils::FilePath &moduleName) override;
+ void loadAllSymbols() override;
+ void requestModuleSymbols(const Utils::FilePath &moduleName) override;
+ void reloadModules() override;
+ void reloadRegisters() override {}
+ void reloadSourceFiles() override {}
+ void reloadFullStack() override {}
+
+ bool supportsThreads() const { return true; }
+ void updateItem(const QString &iname) override;
+
+ void runCommand(const DebuggerCommand &cmd) override;
+ void postDirectCommand(const QJsonObject &ob);
+
+ void refreshLocation(const GdbMi &reportedLocation);
+ void refreshStack(const GdbMi &stack);
+ void refreshLocals(const GdbMi &vars);
+ void refreshModules(const GdbMi &modules);
+ void refreshState(const GdbMi &reportedState);
+ void refreshSymbols(const GdbMi &symbols);
+
+ QString errorMessage(QProcess::ProcessError error) const;
+ bool hasCapability(unsigned cap) const override;
+
+ void claimInitialBreakpoints();
+
+ void handleDabStarted();
+ void handleDabLaunch();
+ void handleDabConfigurationDone();
+
+ void handleDapDone();
+ void readDapStandardOutput();
+ void readDapStandardError();
+ void handleOutput(const QJsonDocument &data);
+ void handleResponse(const QString &ba);
+ void updateAll() override;
+ void updateLocals() override;
+
+ QByteArray m_inbuffer;
+ Utils::Process m_proc;
+ int m_nextBreakpointId = 1;
+};
+
+} // Debugger::Internal
diff --git a/src/plugins/debugger/debugger.qbs b/src/plugins/debugger/debugger.qbs
index 50f1eba24f..be2adfc04d 100644
--- a/src/plugins/debugger/debugger.qbs
+++ b/src/plugins/debugger/debugger.qbs
@@ -123,6 +123,12 @@ Project {
}
Group {
+ name: "dap"
+ prefix: "dap/"
+ files: ["dapengine.cpp", "dapengine.h"]
+ }
+
+ Group {
name: "uvsc"
prefix: "uvsc/"
files: [
@@ -175,7 +181,7 @@ Project {
Group {
name: "Images"
prefix: "images/"
- files: ["*.png", "*.xpm"]
+ files: ["*.png"]
}
Group {
@@ -239,9 +245,7 @@ Project {
]
}
- Group {
- name: "Unit tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
"debuggerunittests.qrc",
]
diff --git a/src/plugins/debugger/debugger.qrc b/src/plugins/debugger/debugger.qrc
index 86a05a73ff..6fc31bb4fb 100644
--- a/src/plugins/debugger/debugger.qrc
+++ b/src/plugins/debugger/debugger.qrc
@@ -42,7 +42,6 @@
<file>images/mode_debug@2x.png</file>
<file>images/mode_debug_mask.png</file>
<file>images/mode_debug_mask@2x.png</file>
- <file>images/pin.xpm</file>
<file>images/debugger_restart_small.png</file>
<file>images/debugger_restart_small@2x.png</file>
<file>images/recordfill.png</file>
diff --git a/src/plugins/debugger/debuggeractions.cpp b/src/plugins/debugger/debuggeractions.cpp
index a3a6187a41..907e626f4d 100644
--- a/src/plugins/debugger/debuggeractions.cpp
+++ b/src/plugins/debugger/debuggeractions.cpp
@@ -242,7 +242,7 @@ DebuggerSettings::DebuggerSettings()
adjustBreakpointLocations.setDisplayName(Tr::tr("Adjust Breakpoint Locations"));
adjustBreakpointLocations.setToolTip(
"<p>"
- + Tr::tr("<p>Not all source code lines generate "
+ + Tr::tr("Not all source code lines generate "
"executable code. Putting a breakpoint on such a line acts as "
"if the breakpoint was set on the next line that generated code. "
"Selecting 'Adjust Breakpoint Locations' shifts the red "
@@ -364,7 +364,6 @@ DebuggerSettings::DebuggerSettings()
+ "</p></body></html>");
extraDumperFile.setSettingsKey(debugModeGroup, "ExtraDumperFile");
- extraDumperFile.setDisplayStyle(StringAspect::PathChooserDisplay);
extraDumperFile.setDisplayName(Tr::tr("Extra Debugging Helpers"));
// Label text is intentional empty in the GUI.
extraDumperFile.setToolTip(Tr::tr("Path to a Python file containing additional data dumpers."));
@@ -531,6 +530,15 @@ DebuggerSettings::DebuggerSettings()
+ Tr::tr("The maximum length for strings in separated windows. "
"Longer strings are cut off and displayed with an ellipsis attached."));
+ defaultArraySize.setSettingsKey(debugModeGroup, "DefaultArraySize");
+ defaultArraySize.setDefaultValue(100);
+ defaultArraySize.setRange(10, 1000000000);
+ defaultArraySize.setSingleStep(100);
+ defaultArraySize.setLabelText(Tr::tr("Default array size:"));
+ defaultArraySize.setToolTip("<p>"
+ + Tr::tr("The number of array elements requested when expanding "
+ "entries in the Locals and Expressions views."));
+
expandStack.setLabelText(Tr::tr("Reload Full Stack"));
createFullBacktrace.setLabelText(Tr::tr("Create Full Backtrace"));
@@ -610,6 +618,7 @@ DebuggerSettings::DebuggerSettings()
page4.registerAspect(&showQObjectNames);
page4.registerAspect(&displayStringLimit);
page4.registerAspect(&maximalStringLength);
+ page4.registerAspect(&defaultArraySize);
// Page 5
page5.registerAspect(&cdbAdditionalArguments);
@@ -650,7 +659,7 @@ DebuggerSettings::DebuggerSettings()
aspect->setAutoApply(false);
// FIXME: Make the positioning part of the LayoutBuilder later
if (auto boolAspect = dynamic_cast<BoolAspect *>(aspect))
- boolAspect->setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ boolAspect->setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox);
});
}
diff --git a/src/plugins/debugger/debuggeractions.h b/src/plugins/debugger/debuggeractions.h
index 69cc76a1f1..a6fb497815 100644
--- a/src/plugins/debugger/debuggeractions.h
+++ b/src/plugins/debugger/debuggeractions.h
@@ -30,7 +30,7 @@ public:
void fromMap(const QVariantMap &map) override;
void toMap(QVariantMap &map) const override;
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
QVariant volatileValue() const override;
void setVolatileValue(const QVariant &val) override;
@@ -106,7 +106,7 @@ public:
Utils::BoolAspect useDebuggingHelpers;
Utils::BoolAspect useCodeModel;
Utils::BoolAspect showThreadNames;
- Utils::StringAspect extraDumperFile; // For loading a file. Recommended.
+ Utils::FilePathAspect extraDumperFile; // For loading a file. Recommended.
Utils::StringAspect extraDumperCommands; // To modify an existing setup.
Utils::BoolAspect showStdNamespace;
@@ -146,6 +146,7 @@ public:
Utils::BoolAspect autoDerefPointers;
Utils::IntegerAspect maximalStringLength;
Utils::IntegerAspect displayStringLimit;
+ Utils::IntegerAspect defaultArraySize;
Utils::BoolAspect sortStructMembers;
Utils::BoolAspect useToolTipsInLocalsView;
diff --git a/src/plugins/debugger/debuggerconstants.h b/src/plugins/debugger/debuggerconstants.h
index b071713a84..06ccb94f1b 100644
--- a/src/plugins/debugger/debuggerconstants.h
+++ b/src/plugins/debugger/debuggerconstants.h
@@ -99,6 +99,7 @@ enum DebuggerEngineType
CdbEngineType = 0x004,
PdbEngineType = 0x008,
LldbEngineType = 0x100,
+ DapEngineType = 0x200,
UvscEngineType = 0x1000
};
diff --git a/src/plugins/debugger/debuggerdialogs.cpp b/src/plugins/debugger/debuggerdialogs.cpp
index ece2c22669..c382e91c17 100644
--- a/src/plugins/debugger/debuggerdialogs.cpp
+++ b/src/plugins/debugger/debuggerdialogs.cpp
@@ -212,6 +212,7 @@ StartApplicationDialog::StartApplicationDialog(QWidget *parent)
d->localExecutablePathChooser->setHistoryCompleter("LocalExecutable");
d->arguments = new FancyLineEdit(this);
+ d->arguments->setClearButtonEnabled(true);
d->arguments->setHistoryCompleter("CommandlineArguments");
d->workingDirectory = new PathChooser(this);
diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp
index 14cab12750..27c1373c99 100644
--- a/src/plugins/debugger/debuggerengine.cpp
+++ b/src/plugins/debugger/debuggerengine.cpp
@@ -13,15 +13,16 @@
#include "debuggertr.h"
#include "breakhandler.h"
+#include "debuggermainwindow.h"
#include "disassembleragent.h"
+#include "enginemanager.h"
#include "localsandexpressionswindow.h"
#include "logwindow.h"
-#include "debuggermainwindow.h"
-#include "enginemanager.h"
#include "memoryagent.h"
#include "moduleshandler.h"
#include "registerhandler.h"
#include "peripheralregisterhandler.h"
+#include "shared/peutils.h"
#include "sourcefileshandler.h"
#include "sourceutils.h"
#include "stackhandler.h"
@@ -31,7 +32,6 @@
#include "watchhandler.h"
#include "watchutils.h"
#include "watchwindow.h"
-#include "debugger/shared/peutils.h"
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/editormanager/editormanager.h>
@@ -54,10 +54,10 @@
#include <utils/basetreeview.h>
#include <utils/checkablemessagebox.h>
#include <utils/macroexpander.h>
+#include <utils/process.h>
#include <utils/processhandle.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/styledbar.h>
#include <utils/utilsicons.h>
@@ -471,7 +471,7 @@ public:
QString m_qtNamespace;
// Safety net to avoid infinite lookups.
- QSet<QString> m_lookupRequests; // FIXME: Integrate properly.
+ QHash<QString, int> m_lookupRequests; // FIXME: Integrate properly.
QPointer<QWidget> m_alertBox;
QPointer<BaseTreeView> m_breakView;
@@ -1036,11 +1036,6 @@ void DebuggerEngine::setRunTool(DebuggerRunTool *runTool)
{
d->m_device = runTool->device();
- IDevice::ConstPtr debuggerDevice =
- DeviceManager::deviceForPath(d->m_runParameters.debugger.command.executable());
- if (QTC_GUARD(debuggerDevice))
- d->m_runParameters.dumperPath = debuggerDevice->debugDumperPath();
-
d->m_terminalRunner = runTool->terminalRunner();
validateRunParameters(d->m_runParameters);
@@ -2083,7 +2078,7 @@ void DebuggerEngine::examineModules()
{
}
-void DebuggerEngine::loadSymbols(const QString &)
+void DebuggerEngine::loadSymbols(const FilePath &)
{
}
@@ -2095,11 +2090,11 @@ void DebuggerEngine::loadSymbolsForStack()
{
}
-void DebuggerEngine::requestModuleSymbols(const QString &)
+void DebuggerEngine::requestModuleSymbols(const FilePath &)
{
}
-void DebuggerEngine::requestModuleSections(const QString &)
+void DebuggerEngine::requestModuleSections(const FilePath &)
{
}
@@ -2360,9 +2355,10 @@ bool DebuggerEngine::canHandleToolTip(const DebuggerToolTipContext &context) con
void DebuggerEngine::updateItem(const QString &iname)
{
- if (d->m_lookupRequests.contains(iname)) {
+ WatchHandler *handler = watchHandler();
+ const int maxArrayCount = handler->maxArrayCount(iname);
+ if (d->m_lookupRequests.value(iname, -1) == maxArrayCount) {
showMessage(QString("IGNORING REPEATED REQUEST TO EXPAND " + iname));
- WatchHandler *handler = watchHandler();
WatchItem *item = handler->findItem(iname);
QTC_CHECK(item);
WatchModelBase *model = handler->model();
@@ -2382,7 +2378,7 @@ void DebuggerEngine::updateItem(const QString &iname)
}
// We could legitimately end up here after expanding + closing + re-expaning an item.
}
- d->m_lookupRequests.insert(iname);
+ d->m_lookupRequests[iname] = maxArrayCount;
UpdateParameters params;
params.partialVariable = iname;
@@ -2591,6 +2587,7 @@ bool DebuggerRunParameters::isCppDebugging() const
return cppEngineType == GdbEngineType
|| cppEngineType == LldbEngineType
|| cppEngineType == CdbEngineType
+ || cppEngineType == DapEngineType
|| cppEngineType == UvscEngineType;
}
@@ -2651,7 +2648,7 @@ static void createNewDock(QWidget *widget)
dockWidget->show();
}
-void DebuggerEngine::showModuleSymbols(const QString &moduleName, const Symbols &symbols)
+void DebuggerEngine::showModuleSymbols(const FilePath &moduleName, const Symbols &symbols)
{
auto w = new QTreeWidget;
w->setUniformRowHeights(true);
@@ -2659,7 +2656,7 @@ void DebuggerEngine::showModuleSymbols(const QString &moduleName, const Symbols
w->setRootIsDecorated(false);
w->setAlternatingRowColors(true);
w->setSortingEnabled(true);
- w->setObjectName("Symbols." + moduleName);
+ w->setObjectName("Symbols." + moduleName.toFSPathString());
QStringList header;
header.append(Tr::tr("Symbol"));
header.append(Tr::tr("Address"));
@@ -2667,7 +2664,7 @@ void DebuggerEngine::showModuleSymbols(const QString &moduleName, const Symbols
header.append(Tr::tr("Section"));
header.append(Tr::tr("Name"));
w->setHeaderLabels(header);
- w->setWindowTitle(Tr::tr("Symbols in \"%1\"").arg(moduleName));
+ w->setWindowTitle(Tr::tr("Symbols in \"%1\"").arg(moduleName.toUserOutput()));
for (const Symbol &s : symbols) {
auto it = new QTreeWidgetItem;
it->setData(0, Qt::DisplayRole, s.name);
@@ -2680,7 +2677,7 @@ void DebuggerEngine::showModuleSymbols(const QString &moduleName, const Symbols
createNewDock(w);
}
-void DebuggerEngine::showModuleSections(const QString &moduleName, const Sections &sections)
+void DebuggerEngine::showModuleSections(const FilePath &moduleName, const Sections &sections)
{
auto w = new QTreeWidget;
w->setUniformRowHeights(true);
@@ -2688,7 +2685,7 @@ void DebuggerEngine::showModuleSections(const QString &moduleName, const Section
w->setRootIsDecorated(false);
w->setAlternatingRowColors(true);
w->setSortingEnabled(true);
- w->setObjectName("Sections." + moduleName);
+ w->setObjectName("Sections." + moduleName.toFSPathString());
QStringList header;
header.append(Tr::tr("Name"));
header.append(Tr::tr("From"));
@@ -2696,7 +2693,7 @@ void DebuggerEngine::showModuleSections(const QString &moduleName, const Section
header.append(Tr::tr("Address"));
header.append(Tr::tr("Flags"));
w->setHeaderLabels(header);
- w->setWindowTitle(Tr::tr("Sections in \"%1\"").arg(moduleName));
+ w->setWindowTitle(Tr::tr("Sections in \"%1\"").arg(moduleName.toUserOutput()));
for (const Section &s : sections) {
auto it = new QTreeWidgetItem;
it->setData(0, Qt::DisplayRole, s.name);
@@ -2719,7 +2716,6 @@ Context CppDebuggerEngine::languageContext() const
void CppDebuggerEngine::validateRunParameters(DebuggerRunParameters &rp)
{
static const QString warnOnInappropriateDebuggerKey = "DebuggerWarnOnInappropriateDebugger";
- QtcSettings *coreSettings = Core::ICore::settings();
const bool warnOnRelease = debuggerSettings()->warnOnReleaseBuilds.value()
&& rp.toolChainAbi.osFlavor() != Abi::AndroidLinuxFlavor;
@@ -2727,7 +2723,7 @@ void CppDebuggerEngine::validateRunParameters(DebuggerRunParameters &rp)
QString detailedWarning;
switch (rp.toolChainAbi.binaryFormat()) {
case Abi::PEFormat: {
- if (CheckableMessageBox::shouldAskAgain(coreSettings, warnOnInappropriateDebuggerKey)) {
+ if (CheckableDecider(warnOnInappropriateDebuggerKey).shouldAskAgain()) {
QString preferredDebugger;
if (rp.toolChainAbi.osFlavor() == Abi::WindowsMSysFlavor) {
if (rp.cppEngineType == CdbEngineType)
@@ -2767,7 +2763,7 @@ void CppDebuggerEngine::validateRunParameters(DebuggerRunParameters &rp)
break;
}
case Abi::ElfFormat: {
- if (CheckableMessageBox::shouldAskAgain(coreSettings, warnOnInappropriateDebuggerKey)) {
+ if (CheckableDecider(warnOnInappropriateDebuggerKey).shouldAskAgain()) {
if (rp.cppEngineType == CdbEngineType) {
warnOnInappropriateDebugger = true;
detailedWarning = Tr::tr(
@@ -2873,15 +2869,13 @@ void CppDebuggerEngine::validateRunParameters(DebuggerRunParameters &rp)
return;
}
if (warnOnInappropriateDebugger) {
- CheckableMessageBox::doNotShowAgainInformation(
+ CheckableMessageBox::information(
Core::ICore::dialogParent(),
Tr::tr("Warning"),
- Tr::tr(
- "The selected debugger may be inappropriate for the inferior.\n"
- "Examining symbols and setting breakpoints by file name and line number "
- "may fail.\n")
+ Tr::tr("The selected debugger may be inappropriate for the inferior.\n"
+ "Examining symbols and setting breakpoints by file name and line number "
+ "may fail.\n")
+ '\n' + detailedWarning,
- Core::ICore::settings(),
warnOnInappropriateDebuggerKey);
} else if (warnOnRelease) {
AsynchronousMessageBox::information(Tr::tr("Warning"),
diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h
index 422d4eb788..ac91ecb644 100644
--- a/src/plugins/debugger/debuggerengine.h
+++ b/src/plugins/debugger/debuggerengine.h
@@ -7,12 +7,14 @@
#include "debuggerconstants.h"
#include "debuggerprotocol.h"
#include "breakhandler.h"
-#include "projectexplorer/abi.h"
#include "threadshandler.h"
#include <coreplugin/icontext.h>
+
+#include <projectexplorer/abi.h>
#include <projectexplorer/devicesupport/idevicefwd.h>
#include <projectexplorer/runcontrol.h>
+
#include <texteditor/textmark.h>
#include <utils/filepath.h>
@@ -182,7 +184,6 @@ public:
QStringList validationErrors;
- Utils::FilePath dumperPath;
int fallbackQtVersion = 0x50200;
// Common debugger constants.
@@ -303,11 +304,11 @@ public:
virtual void reloadModules();
virtual void examineModules();
- virtual void loadSymbols(const QString &moduleName);
+ virtual void loadSymbols(const Utils::FilePath &moduleName);
virtual void loadSymbolsForStack();
virtual void loadAllSymbols();
- virtual void requestModuleSymbols(const QString &moduleName);
- virtual void requestModuleSections(const QString &moduleName);
+ virtual void requestModuleSymbols(const Utils::FilePath &moduleName);
+ virtual void requestModuleSections(const Utils::FilePath &moduleName);
virtual void reloadRegisters();
virtual void reloadPeripheralRegisters();
@@ -452,8 +453,8 @@ public:
void openMemoryEditor();
- static void showModuleSymbols(const QString &moduleName, const QVector<Symbol> &symbols);
- static void showModuleSections(const QString &moduleName, const QVector<Section> &sections);
+ static void showModuleSymbols(const Utils::FilePath &moduleName, const QVector<Symbol> &symbols);
+ static void showModuleSections(const Utils::FilePath &moduleName, const QVector<Section> &sections);
void handleExecDetach();
void handleExecContinue();
diff --git a/src/plugins/debugger/debuggeritem.cpp b/src/plugins/debugger/debuggeritem.cpp
index a7205d34a1..241ac32ecd 100644
--- a/src/plugins/debugger/debuggeritem.cpp
+++ b/src/plugins/debugger/debuggeritem.cpp
@@ -12,8 +12,8 @@
#include <utils/filepath.h>
#include <utils/hostosinfo.h>
#include <utils/macroexpander.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/utilsicons.h>
#include <utils/winutils.h>
@@ -43,7 +43,7 @@ const char DEBUGGER_INFORMATION_WORKINGDIRECTORY[] = "WorkingDirectory";
static QString getGdbConfiguration(const FilePath &command, const Environment &sysEnv)
{
// run gdb with the --configuration opion
- QtcProcess proc;
+ Process proc;
proc.setEnvironment(sysEnv);
proc.setCommand({command, {"--configuration"}});
proc.runBlocking();
@@ -116,6 +116,9 @@ void DebuggerItem::createId()
void DebuggerItem::reinitializeFromFile(QString *error, Utils::Environment *customEnv)
{
+ if (isGeneric())
+ return;
+
// CDB only understands the single-dash -version, whereas GDB and LLDB are
// happy with both -version and --version. So use the "working" -version
// except for the experimental LLDB-MI which insists on --version.
@@ -159,7 +162,7 @@ void DebuggerItem::reinitializeFromFile(QString *error, Utils::Environment *cust
// hack below tricks it into giving us the information we want.
env.set("QNX_TARGET", QString());
- QtcProcess proc;
+ Process proc;
proc.setEnvironment(env);
proc.setCommand({m_command, {version}});
proc.runBlocking();
@@ -171,8 +174,12 @@ void DebuggerItem::reinitializeFromFile(QString *error, Utils::Environment *cust
return;
}
m_abis.clear();
+
if (output.contains("gdb")) {
m_engineType = GdbEngineType;
+ // FIXME: HACK while introducing DAP support
+ if (m_command.fileName().endsWith("-dap"))
+ m_engineType = DapEngineType;
// Version
bool isMacGdb, isQnxGdb;
@@ -208,6 +215,7 @@ void DebuggerItem::reinitializeFromFile(QString *error, Utils::Environment *cust
//! \note If unable to determine the GDB ABI, no ABI is appended to m_abis here.
return;
}
+
if (output.contains("lldb") || output.startsWith("LLDB")) {
m_engineType = LldbEngineType;
m_abis = Abi::abisOfBinary(m_command);
@@ -275,6 +283,8 @@ QString DebuggerItem::engineTypeName() const
return QLatin1String("CDB");
case LldbEngineType:
return QLatin1String("LLDB");
+ case DapEngineType:
+ return QLatin1String("DAP");
case UvscEngineType:
return QLatin1String("UVSC");
default:
@@ -282,6 +292,16 @@ QString DebuggerItem::engineTypeName() const
}
}
+void DebuggerItem::setGeneric(bool on)
+{
+ m_detectionSource = on ? QLatin1String("Generic") : QLatin1String();
+}
+
+bool DebuggerItem::isGeneric() const
+{
+ return m_detectionSource == "Generic";
+}
+
QStringList DebuggerItem::abiNames() const
{
QStringList list;
@@ -297,13 +317,15 @@ QDateTime DebuggerItem::lastModified() const
QIcon DebuggerItem::decoration() const
{
+ if (isGeneric())
+ return {};
if (m_engineType == NoEngineType)
return Icons::CRITICAL.icon();
if (!m_command.isExecutableFile())
return Icons::WARNING.icon();
if (!m_workingDirectory.isEmpty() && !m_workingDirectory.isDir())
return Icons::WARNING.icon();
- return QIcon();
+ return {};
}
QString DebuggerItem::validityMessage() const
diff --git a/src/plugins/debugger/debuggeritem.h b/src/plugins/debugger/debuggeritem.h
index 1edaf283ca..95993665b6 100644
--- a/src/plugins/debugger/debuggeritem.h
+++ b/src/plugins/debugger/debuggeritem.h
@@ -8,7 +8,7 @@
#include <projectexplorer/abi.h>
-#include <utils/fileutils.h>
+#include <utils/filepath.h>
#include <utils/environment.h>
#include <QDateTime>
@@ -84,6 +84,9 @@ public:
QString detectionSource() const { return m_detectionSource; }
void setDetectionSource(const QString &source) { m_detectionSource = source; }
+ bool isGeneric() const;
+ void setGeneric(bool on);
+
static bool addAndroidLldbPythonEnv(const Utils::FilePath &lldbCmd, Utils::Environment &env);
private:
diff --git a/src/plugins/debugger/debuggeritemmanager.cpp b/src/plugins/debugger/debuggeritemmanager.cpp
index b379255416..aca5a97635 100644
--- a/src/plugins/debugger/debuggeritemmanager.cpp
+++ b/src/plugins/debugger/debuggeritemmanager.cpp
@@ -9,8 +9,6 @@
#include <coreplugin/dialogs/ioptionspage.h>
#include <coreplugin/icore.h>
-#include <extensionsystem/pluginmanager.h>
-
#include <projectexplorer/devicesupport/devicemanager.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectexplorericons.h>
@@ -22,8 +20,8 @@
#include <utils/hostosinfo.h>
#include <utils/pathchooser.h>
#include <utils/persistentsettings.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/treemodel.h>
#include <utils/winutils.h>
@@ -92,7 +90,8 @@ static DebuggerItemManagerPrivate *d = nullptr;
class DebuggerItemConfigWidget : public QWidget
{
public:
- explicit DebuggerItemConfigWidget();
+ DebuggerItemConfigWidget();
+
void load(const DebuggerItem *item);
void store() const;
@@ -104,13 +103,18 @@ private:
QLineEdit *m_displayNameLineEdit;
QLineEdit *m_typeLineEdit;
QLabel *m_cdbLabel;
- QLineEdit *m_versionLabel;
PathChooser *m_binaryChooser;
- PathChooser *m_workingDirectoryChooser;
- QLineEdit *m_abis;
bool m_autodetected = false;
+ bool m_generic = false;
DebuggerEngineType m_engineType = NoEngineType;
QVariant m_id;
+
+ QLabel *m_abisLabel;
+ QLineEdit *m_abis;
+ QLabel *m_versionLabel;
+ QLineEdit *m_version;
+ QLabel *m_workingDirectoryLabel;
+ PathChooser *m_workingDirectoryChooser;
};
// --------------------------------------------------------------------------
@@ -170,10 +174,11 @@ class DebuggerItemModel : public TreeModel<TreeItem, StaticTreeItem, DebuggerTre
{
public:
DebuggerItemModel();
+ enum { Generic, AutoDetected, Manual };
QModelIndex lastIndex() const;
void setCurrentIndex(const QModelIndex &index);
- void addDebugger(const DebuggerItem &item, bool changed = false);
+ DebuggerTreeItem *addDebugger(const DebuggerItem &item, bool changed = false);
void updateDebugger(const DebuggerItem &item);
void apply();
void cancel();
@@ -202,17 +207,40 @@ const DebuggerItem *findDebugger(const Predicate &pred)
DebuggerItemModel::DebuggerItemModel()
{
setHeader({Tr::tr("Name"), Tr::tr("Path"), Tr::tr("Type")});
- rootItem()->appendChild(
- new StaticTreeItem({ProjectExplorer::Constants::msgAutoDetected()},
- {ProjectExplorer::Constants::msgAutoDetectedToolTip()}));
+
+ auto generic = new StaticTreeItem(Tr::tr("Generic"));
+ auto autoDetected = new StaticTreeItem({ProjectExplorer::Constants::msgAutoDetected()},
+ {ProjectExplorer::Constants::msgAutoDetectedToolTip()});
+ rootItem()->appendChild(generic);
+ rootItem()->appendChild(autoDetected);
rootItem()->appendChild(new StaticTreeItem(ProjectExplorer::Constants::msgManual()));
+
+ DebuggerItem genericGdb(QVariant("gdb"));
+ genericGdb.setAutoDetected(true);
+ genericGdb.setGeneric(true);
+ genericGdb.setEngineType(GdbEngineType);
+ genericGdb.setAbi(Abi());
+ genericGdb.setCommand("gdb");
+ genericGdb.setUnexpandedDisplayName(Tr::tr("%1 from PATH on Build Device").arg("GDB"));
+ generic->appendChild(new DebuggerTreeItem(genericGdb, false));
+
+ DebuggerItem genericLldb(QVariant("lldb"));
+ genericLldb.setAutoDetected(true);
+ genericLldb.setEngineType(LldbEngineType);
+ genericLldb.setGeneric(true);
+ genericLldb.setAbi(Abi());
+ genericLldb.setCommand("lldb");
+ genericLldb.setUnexpandedDisplayName(Tr::tr("%1 from PATH on Build Device").arg("LLDB"));
+ generic->appendChild(new DebuggerTreeItem(genericLldb, false));
}
-void DebuggerItemModel::addDebugger(const DebuggerItem &item, bool changed)
+DebuggerTreeItem *DebuggerItemModel::addDebugger(const DebuggerItem &item, bool changed)
{
- QTC_ASSERT(item.id().isValid(), return);
- int group = item.isAutoDetected() ? 0 : 1;
- rootItem()->childAt(group)->appendChild(new DebuggerTreeItem(item, changed));
+ QTC_ASSERT(item.id().isValid(), return {});
+ int group = item.isGeneric() ? Generic : (item.isAutoDetected() ? AutoDetected : Manual);
+ auto treeItem = new DebuggerTreeItem(item, changed);
+ rootItem()->childAt(group)->appendChild(treeItem);
+ return treeItem;
}
void DebuggerItemModel::updateDebugger(const DebuggerItem &item)
@@ -301,6 +329,7 @@ DebuggerItemConfigWidget::DebuggerItemConfigWidget()
});
m_binaryChooser->setAllowPathFromDevice(true);
+ m_workingDirectoryLabel = new QLabel(Tr::tr("ABIs:"));
m_workingDirectoryChooser = new PathChooser(this);
m_workingDirectoryChooser->setExpectedKind(PathChooser::Directory);
m_workingDirectoryChooser->setMinimumWidth(400);
@@ -310,10 +339,12 @@ DebuggerItemConfigWidget::DebuggerItemConfigWidget()
m_cdbLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
m_cdbLabel->setOpenExternalLinks(true);
- m_versionLabel = new QLineEdit(this);
- m_versionLabel->setPlaceholderText(Tr::tr("Unknown"));
- m_versionLabel->setEnabled(false);
+ m_versionLabel = new QLabel(Tr::tr("Version:"));
+ m_version = new QLineEdit(this);
+ m_version->setPlaceholderText(Tr::tr("Unknown"));
+ m_version->setEnabled(false);
+ m_abisLabel = new QLabel(Tr::tr("Working directory:"));
m_abis = new QLineEdit(this);
m_abis->setEnabled(false);
@@ -323,9 +354,9 @@ DebuggerItemConfigWidget::DebuggerItemConfigWidget()
formLayout->addRow(m_cdbLabel);
formLayout->addRow(new QLabel(Tr::tr("Path:")), m_binaryChooser);
formLayout->addRow(new QLabel(Tr::tr("Type:")), m_typeLineEdit);
- formLayout->addRow(new QLabel(Tr::tr("ABIs:")), m_abis);
- formLayout->addRow(new QLabel(Tr::tr("Version:")), m_versionLabel);
- formLayout->addRow(new QLabel(Tr::tr("Working directory:")), m_workingDirectoryChooser);
+ formLayout->addRow(m_abisLabel, m_abis);
+ formLayout->addRow(m_versionLabel, m_version);
+ formLayout->addRow(m_workingDirectoryLabel, m_workingDirectoryChooser);
connect(m_binaryChooser, &PathChooser::textChanged,
this, &DebuggerItemConfigWidget::binaryPathHasChanged);
@@ -337,21 +368,24 @@ DebuggerItemConfigWidget::DebuggerItemConfigWidget()
DebuggerItem DebuggerItemConfigWidget::item() const
{
+ static const QRegularExpression noAbi("[^A-Za-z0-9-_]+");
+
DebuggerItem item(m_id);
item.setUnexpandedDisplayName(m_displayNameLineEdit->text());
item.setCommand(m_binaryChooser->filePath());
item.setWorkingDirectory(m_workingDirectoryChooser->filePath());
item.setAutoDetected(m_autodetected);
Abis abiList;
- const QStringList abis = m_abis->text().split(QRegularExpression("[^A-Za-z0-9-_]+"));
+ const QStringList abis = m_abis->text().split(noAbi);
for (const QString &a : abis) {
if (a.isNull())
continue;
abiList << Abi::fromString(a);
}
item.setAbis(abiList);
- item.setVersion(m_versionLabel->text());
+ item.setVersion(m_version->text());
item.setEngineType(m_engineType);
+ item.setGeneric(m_generic);
return item;
}
@@ -373,6 +407,7 @@ void DebuggerItemConfigWidget::load(const DebuggerItem *item)
return;
// Set values:
+ m_generic = item->isGeneric();
m_autodetected = item->isAutoDetected();
m_displayNameLineEdit->setEnabled(!item->isAutoDetected());
@@ -382,6 +417,15 @@ void DebuggerItemConfigWidget::load(const DebuggerItem *item)
m_binaryChooser->setReadOnly(item->isAutoDetected());
m_binaryChooser->setFilePath(item->command());
+ m_binaryChooser->setExpectedKind(m_generic ? PathChooser::Any : PathChooser::ExistingCommand);
+
+ m_abisLabel->setVisible(!m_generic);
+ m_abis->setVisible(!m_generic);
+ m_versionLabel->setVisible(!m_generic);
+ m_version->setVisible(!m_generic);
+ m_workingDirectoryLabel->setVisible(!m_generic);
+ m_workingDirectoryChooser->setVisible(!m_generic);
+
m_workingDirectoryChooser->setReadOnly(item->isAutoDetected());
m_workingDirectoryChooser->setFilePath(item->workingDirectory());
@@ -405,7 +449,7 @@ void DebuggerItemConfigWidget::load(const DebuggerItem *item)
m_cdbLabel->setText(text);
m_cdbLabel->setVisible(!text.isEmpty());
m_binaryChooser->setCommandVersionArguments(QStringList(versionCommand));
- m_versionLabel->setText(item->version());
+ m_version->setText(item->version());
setAbis(item->abiNames());
m_engineType = item->engineType();
m_id = item->id();
@@ -417,16 +461,18 @@ void DebuggerItemConfigWidget::binaryPathHasChanged()
if (!m_id.isValid())
return;
- DebuggerItem tmp;
- if (m_binaryChooser->filePath().isExecutableFile()) {
- tmp = item();
- tmp.reinitializeFromFile();
- }
+ if (!m_generic) {
+ DebuggerItem tmp;
+ if (m_binaryChooser->filePath().isExecutableFile()) {
+ tmp = item();
+ tmp.reinitializeFromFile();
+ }
- setAbis(tmp.abiNames());
- m_versionLabel->setText(tmp.version());
- m_engineType = tmp.engineType();
- m_typeLineEdit->setText(tmp.engineTypeName());
+ setAbis(tmp.abiNames());
+ m_version->setText(tmp.version());
+ m_engineType = tmp.engineType();
+ m_typeLineEdit->setText(tmp.engineTypeName());
+ }
store();
}
@@ -534,8 +580,10 @@ void DebuggerConfigWidget::cloneDebugger()
newItem.setUnexpandedDisplayName(d->uniqueDisplayName(Tr::tr("Clone of %1").arg(item->displayName())));
newItem.reinitializeFromFile();
newItem.setAutoDetected(false);
- d->m_model->addDebugger(newItem, true);
- m_debuggerView->setCurrentIndex(d->m_model->lastIndex());
+ newItem.setGeneric(item->isGeneric());
+ newItem.setEngineType(item->engineType());
+ auto addedItem = d->m_model->addDebugger(newItem, true);
+ m_debuggerView->setCurrentIndex(d->m_model->indexForItem(addedItem));
}
void DebuggerConfigWidget::addDebugger()
@@ -545,8 +593,8 @@ void DebuggerConfigWidget::addDebugger()
item.setEngineType(NoEngineType);
item.setUnexpandedDisplayName(d->uniqueDisplayName(Tr::tr("New Debugger")));
item.setAutoDetected(false);
- d->m_model->addDebugger(item, true);
- m_debuggerView->setCurrentIndex(d->m_model->lastIndex());
+ auto addedItem = d->m_model->addDebugger(item, true);
+ m_debuggerView->setCurrentIndex(d->m_model->indexForItem(addedItem));
}
void DebuggerConfigWidget::removeDebugger()
@@ -712,7 +760,7 @@ void DebuggerItemManagerPrivate::autoDetectGdbOrLldbDebuggers(const FilePaths &s
FilePaths suspects;
if (searchPaths.front().osType() == OsTypeMac) {
- QtcProcess proc;
+ Process proc;
proc.setTimeoutS(2);
proc.setCommand({"xcrun", {"--find", "lldb"}});
proc.runBlocking();
@@ -763,6 +811,14 @@ void DebuggerItemManagerPrivate::autoDetectGdbOrLldbDebuggers(const FilePaths &s
item.setUnexpandedDisplayName(name.arg(item.engineTypeName()).arg(command.toUserOutput()));
m_model->addDebugger(item);
logMessages.append(Tr::tr("Found: \"%1\"").arg(command.toUserOutput()));
+
+ if (item.engineType() == GdbEngineType) {
+ if (item.version().startsWith("GNU gdb (GDB) 14.0.50.2023")) {
+ // FIXME: Use something more robust
+ item.setEngineType(DapEngineType);
+ m_model->addDebugger(item);
+ }
+ }
}
if (logMessage)
*logMessage = logMessages.join('\n');
@@ -820,7 +876,6 @@ DebuggerItemManagerPrivate::DebuggerItemManagerPrivate()
d = this;
m_model = new DebuggerItemModel;
m_optionsPage = new DebuggerOptionsPage;
- ExtensionSystem::PluginManager::addObject(m_optionsPage);
}
void DebuggerItemManagerPrivate::extensionsInitialized()
@@ -830,7 +885,6 @@ void DebuggerItemManagerPrivate::extensionsInitialized()
DebuggerItemManagerPrivate::~DebuggerItemManagerPrivate()
{
- ExtensionSystem::PluginManager::removeObject(m_optionsPage);
delete m_optionsPage;
delete m_model;
}
@@ -932,6 +986,8 @@ void DebuggerItemManagerPrivate::saveDebuggers()
int count = 0;
forAllDebuggers([&count, &data](DebuggerItem &item) {
+ if (item.isGeneric()) // do not store generic debuggers, these get added automatically
+ return;
if (item.isValid() && item.engineType() != NoEngineType) {
QVariantMap tmp = item.toMap();
if (!tmp.isEmpty()) {
diff --git a/src/plugins/debugger/debuggerkitinformation.cpp b/src/plugins/debugger/debuggerkitinformation.cpp
index cac42d5a42..b1ba6bf1d5 100644
--- a/src/plugins/debugger/debuggerkitinformation.cpp
+++ b/src/plugins/debugger/debuggerkitinformation.cpp
@@ -60,11 +60,11 @@ public:
}
private:
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &parent) override
{
addMutableAction(m_comboBox);
- builder.addItem(m_comboBox);
- builder.addItem(m_manageButton);
+ parent.addItem(m_comboBox);
+ parent.addItem(m_manageButton);
}
void makeReadOnly() override
@@ -228,66 +228,6 @@ void DebuggerKitAspect::setup(Kit *k)
k->setValue(DebuggerKitAspect::id(), bestLevel != DebuggerItem::DoesNotMatch ? bestItem.id() : QVariant());
}
-
-// This handles the upgrade path from 2.8 to 3.0
-void DebuggerKitAspect::fix(Kit *k)
-{
- QTC_ASSERT(k, return);
-
- // This can be Id, binary path, but not "auto" anymore.
- const QVariant rawId = k->value(DebuggerKitAspect::id());
-
- if (rawId.toString().isEmpty()) // No debugger set, that is fine.
- return;
-
- if (rawId.type() == QVariant::String) {
- const DebuggerItem * const item = DebuggerItemManager::findById(rawId);
- if (!item) {
- qWarning("Unknown debugger id %s in kit %s",
- qPrintable(rawId.toString()), qPrintable(k->displayName()));
- k->setValue(DebuggerKitAspect::id(), QVariant());
- setup(k);
- return;
- }
-
- Abi kitAbi;
- if (ToolChainKitAspect::toolChains(k).isEmpty()) {
- if (DeviceTypeKitAspect::deviceTypeId(k)
- != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
- return;
- }
- kitAbi = Abi(Abi::UnknownArchitecture, Abi::hostAbi().os());
- } else {
- kitAbi = ToolChainKitAspect::targetAbi(k);
- }
- if (item->matchTarget(kitAbi) != DebuggerItem::DoesNotMatch)
- return;
- k->setValue(DebuggerKitAspect::id(), QVariant());
- setup(k);
- return; // All fine (now).
- }
-
- QMap<QString, QVariant> map = rawId.toMap();
- QString binary = map.value("Binary").toString();
- if (binary == "auto") {
- // This should not happen as "auto" is handled by setup() already.
- QTC_CHECK(false);
- k->setValue(DebuggerKitAspect::id(), QVariant());
- return;
- }
-
- FilePath fileName = FilePath::fromUserInput(binary);
- const DebuggerItem *item = DebuggerItemManager::findByCommand(fileName);
- if (!item) {
- qWarning("Debugger command %s invalid in kit %s",
- qPrintable(binary), qPrintable(k->displayName()));
- k->setValue(DebuggerKitAspect::id(), QVariant());
- return;
- }
-
- k->setValue(DebuggerKitAspect::id(), item->id());
-}
-
// Check the configuration errors and return a flag mask. Provide a quick check and
// a verbose one with a list of errors.
@@ -299,15 +239,15 @@ DebuggerKitAspect::ConfigurationErrors DebuggerKitAspect::configurationErrors(co
if (!item)
return NoDebugger;
- if (item->command().isEmpty())
+ const FilePath debugger = item->command();
+ if (debugger.isEmpty())
return NoDebugger;
+ if (debugger.isRelativePath())
+ return NoConfigurationError;
+
ConfigurationErrors result = NoConfigurationError;
- const FilePath debugger = item->command();
- const bool found = debugger.exists() && !debugger.isDir();
- if (!found)
- result |= DebuggerNotFound;
- else if (!debugger.isExecutableFile())
+ if (!debugger.isExecutableFile())
result |= DebuggerNotExecutable;
const Abi tcAbi = ToolChainKitAspect::targetAbi(k);
@@ -318,16 +258,15 @@ DebuggerKitAspect::ConfigurationErrors DebuggerKitAspect::configurationErrors(co
result |= DebuggerDoesNotMatch;
}
- if (!found) {
- if (item->engineType() == NoEngineType)
- return NoDebugger;
+ if (item->engineType() == NoEngineType)
+ return NoDebugger;
- // We need an absolute path to be able to locate Python on Windows.
- if (item->engineType() == GdbEngineType) {
- if (tcAbi.os() == Abi::WindowsOS && !debugger.isAbsolutePath())
- result |= DebuggerNeedsAbsolutePath;
- }
+ // We need an absolute path to be able to locate Python on Windows.
+ if (item->engineType() == GdbEngineType) {
+ if (tcAbi.os() == Abi::WindowsOS && !debugger.isAbsolutePath())
+ result |= DebuggerNeedsAbsolutePath;
}
+
return result;
}
@@ -342,7 +281,12 @@ Runnable DebuggerKitAspect::runnable(const Kit *kit)
{
Runnable runnable;
if (const DebuggerItem *item = debugger(kit)) {
- runnable.command = CommandLine{item->command()};
+ FilePath cmd = item->command();
+ if (cmd.isRelativePath()) {
+ if (const IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(kit))
+ cmd = buildDevice->searchExecutableInPath(cmd.path());
+ }
+ runnable.command.setExecutable(cmd);
runnable.workingDirectory = item->workingDirectory();
runnable.environment = kit->runEnvironment();
runnable.environment.set("LC_NUMERIC", "C");
diff --git a/src/plugins/debugger/debuggerkitinformation.h b/src/plugins/debugger/debuggerkitinformation.h
index eb6ad1d58a..548f76793b 100644
--- a/src/plugins/debugger/debuggerkitinformation.h
+++ b/src/plugins/debugger/debuggerkitinformation.h
@@ -21,7 +21,6 @@ public:
{ return DebuggerKitAspect::validateDebugger(k); }
void setup(ProjectExplorer::Kit *k) override;
- void fix(ProjectExplorer::Kit *k) override;
static const DebuggerItem *debugger(const ProjectExplorer::Kit *kit);
static ProjectExplorer::Runnable runnable(const ProjectExplorer::Kit *kit);
diff --git a/src/plugins/debugger/debuggermainwindow.cpp b/src/plugins/debugger/debuggermainwindow.cpp
index 047fc3ec89..2a5ec866ed 100644
--- a/src/plugins/debugger/debuggermainwindow.cpp
+++ b/src/plugins/debugger/debuggermainwindow.cpp
@@ -162,13 +162,13 @@ DebuggerMainWindowPrivate::DebuggerMainWindowPrivate(DebuggerMainWindow *parent)
{
m_centralWidgetStack = new QStackedWidget;
m_statusLabel = new Utils::StatusLabel;
- m_statusLabel->setProperty("panelwidget", true);
+ StyleHelper::setPanelWidget(m_statusLabel);
m_statusLabel->setIndent(2 * QFontMetrics(q->font()).horizontalAdvance(QChar('x')));
m_editorPlaceHolder = new EditorManagerPlaceHolder;
m_perspectiveChooser = new QComboBox;
m_perspectiveChooser->setObjectName("PerspectiveChooser");
- m_perspectiveChooser->setProperty("panelwidget", true);
+ StyleHelper::setPanelWidget(m_perspectiveChooser);
m_perspectiveChooser->setSizeAdjustPolicy(QComboBox::AdjustToContents);
connect(m_perspectiveChooser, &QComboBox::activated, this, [this](int item) {
Perspective *perspective = Perspective::findPerspective(m_perspectiveChooser->itemData(item).toString());
@@ -201,7 +201,7 @@ DebuggerMainWindowPrivate::DebuggerMainWindowPrivate(DebuggerMainWindow *parent)
closeButton->setToolTip(Tr::tr("Leave Debug Mode"));
auto toolbar = new Utils::StyledBar;
- toolbar->setProperty("topBorder", true);
+ toolbar->setProperty(StyleHelper::C_TOP_BORDER, true);
// "Engine switcher" style comboboxes
auto subPerspectiveSwitcher = new QWidget;
@@ -233,8 +233,8 @@ DebuggerMainWindowPrivate::DebuggerMainWindowPrivate(DebuggerMainWindow *parent)
scrolledToolbar->setFrameStyle(QFrame::NoFrame);
scrolledToolbar->setWidgetResizable(true);
scrolledToolbar->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- scrolledToolbar->setFixedHeight(toolbar->height());
scrolledToolbar->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ StyleHelper::setPanelWidgetSingleRow(scrolledToolbar);
auto dock = new QDockWidget(Tr::tr("Toolbar"), q);
dock->setObjectName("Toolbar");
@@ -507,7 +507,7 @@ QWidget *DebuggerMainWindow::centralWidgetStack()
void DebuggerMainWindow::addSubPerspectiveSwitcher(QWidget *widget)
{
widget->setVisible(false);
- widget->setProperty("panelwidget", true);
+ StyleHelper::setPanelWidget(widget);
d->m_subPerspectiveSwitcherLayout->addWidget(widget);
}
@@ -810,7 +810,7 @@ QToolButton *PerspectivePrivate::setupToolButton(QAction *action)
{
QTC_ASSERT(action, return nullptr);
auto toolButton = new QToolButton(m_innerToolBar);
- toolButton->setProperty("panelwidget", true);
+ StyleHelper::setPanelWidget(toolButton);
toolButton->setDefaultAction(action);
toolButton->setToolTip(action->toolTip());
m_innerToolBarLayout->addWidget(toolButton);
@@ -833,7 +833,7 @@ void Perspective::addToolBarWidget(QWidget *widget)
{
QTC_ASSERT(widget, return);
// QStyle::polish is called before it is added to the toolbar, explicitly make it a panel widget
- widget->setProperty("panelwidget", true);
+ StyleHelper::setPanelWidget(widget);
widget->setParent(d->m_innerToolBar);
d->m_innerToolBarLayout->addWidget(widget);
}
diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp
index 8ab67c0bc5..7ccc808b08 100644
--- a/src/plugins/debugger/debuggerplugin.cpp
+++ b/src/plugins/debugger/debuggerplugin.cpp
@@ -67,9 +67,10 @@
#include <projectexplorer/projectexplorericons.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectexplorersettings.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
#include <projectexplorer/runconfiguration.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
#include <projectexplorer/toolchain.h>
@@ -685,7 +686,6 @@ public:
EngineManager m_engineManager;
QTimer m_shutdownTimer;
- bool m_shuttingDown = false;
Console m_console; // ensure Debugger Console is created before settings are taken into account
DebuggerSettings m_debuggerSettings;
@@ -1187,7 +1187,7 @@ DebuggerPluginPrivate::DebuggerPluginPrivate(const QStringList &arguments)
setInitialState();
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, &DebuggerPluginPrivate::onStartupProjectChanged);
connect(EngineManager::instance(), &EngineManager::engineStateChanged,
this, &DebuggerPluginPrivate::updatePresetState);
@@ -1391,11 +1391,11 @@ static QVariant configValue(const QString &name)
void DebuggerPluginPrivate::updatePresetState()
{
- if (m_shuttingDown)
+ if (PluginManager::isShuttingDown())
return;
- Project *startupProject = SessionManager::startupProject();
- RunConfiguration *startupRunConfig = SessionManager::startupRunConfiguration();
+ Project *startupProject = ProjectManager::startupProject();
+ RunConfiguration *startupRunConfig = ProjectManager::startupRunConfiguration();
DebuggerEngine *currentEngine = EngineManager::currentEngine();
QString whyNot;
@@ -1995,9 +1995,7 @@ void DebuggerPluginPrivate::dumpLog()
void DebuggerPluginPrivate::aboutToShutdown()
{
- m_shuttingDown = true;
-
- disconnect(SessionManager::instance(), &SessionManager::startupProjectChanged, this, nullptr);
+ disconnect(ProjectManager::instance(), &ProjectManager::startupProjectChanged, this, nullptr);
m_shutdownTimer.setInterval(0);
m_shutdownTimer.setSingleShot(true);
@@ -2080,7 +2078,7 @@ QWidget *addSearch(BaseTreeView *treeView)
void openTextEditor(const QString &titlePattern0, const QString &contents)
{
- if (dd->m_shuttingDown)
+ if (PluginManager::isShuttingDown())
return;
QString titlePattern = titlePattern0;
IEditor *editor = EditorManager::openEditorWithContents(
@@ -2165,7 +2163,7 @@ static bool buildTypeAccepted(QFlags<ToolMode> toolMode, BuildConfiguration::Bui
static BuildConfiguration::BuildType startupBuildType()
{
BuildConfiguration::BuildType buildType = BuildConfiguration::Unknown;
- if (RunConfiguration *runConfig = SessionManager::startupRunConfiguration()) {
+ if (RunConfiguration *runConfig = ProjectManager::startupRunConfiguration()) {
if (const BuildConfiguration *buildConfig = runConfig->target()->activeBuildConfiguration())
buildType = buildConfig->buildType();
}
@@ -2239,10 +2237,12 @@ bool wantRunTool(ToolMode toolMode, const QString &toolName)
"or otherwise insufficient output.</p><p>"
"Do you want to continue and run the tool in %2 mode?</p></body></html>")
.arg(toolName).arg(currentMode).arg(toolModeString);
- if (Utils::CheckableMessageBox::doNotAskAgainQuestion(ICore::dialogParent(),
- title, message, ICore::settings(), "AnalyzerCorrectModeWarning")
- != QDialogButtonBox::Yes)
- return false;
+ if (Utils::CheckableMessageBox::question(ICore::dialogParent(),
+ title,
+ message,
+ QString("AnalyzerCorrectModeWarning"))
+ != QMessageBox::Yes)
+ return false;
}
return true;
@@ -2335,12 +2335,12 @@ void DebuggerUnitTests::testStateMachine()
QEventLoop loop;
connect(BuildManager::instance(), &BuildManager::buildQueueFinished,
&loop, &QEventLoop::quit);
- BuildManager::buildProjectWithDependencies(SessionManager::startupProject());
+ BuildManager::buildProjectWithDependencies(ProjectManager::startupProject());
loop.exec();
ExecuteOnDestruction guard([] { EditorManager::closeAllEditors(false); });
- RunConfiguration *rc = SessionManager::startupRunConfiguration();
+ RunConfiguration *rc = ProjectManager::startupRunConfiguration();
QVERIFY(rc);
auto runControl = new RunControl(ProjectExplorer::Constants::DEBUG_RUN_MODE);
diff --git a/src/plugins/debugger/debuggerrunconfigurationaspect.cpp b/src/plugins/debugger/debuggerrunconfigurationaspect.cpp
index ef80d58a33..8c118f553b 100644
--- a/src/plugins/debugger/debuggerrunconfigurationaspect.cpp
+++ b/src/plugins/debugger/debuggerrunconfigurationaspect.cpp
@@ -5,6 +5,8 @@
#include "debuggertr.h"
+#include <cppeditor/cppmodelmanager.h>
+
#include <coreplugin/helpmanager.h>
#include <coreplugin/icontext.h>
#include <coreplugin/icore.h>
@@ -20,6 +22,7 @@
#include <qtsupport/qtbuildaspects.h>
+#include <utils/detailswidget.h>
#include <utils/environment.h>
#include <utils/layoutbuilder.h>
@@ -28,117 +31,10 @@
#include <QLabel>
#include <QTextEdit>
-using namespace Debugger::Internal;
using namespace ProjectExplorer;
using namespace Utils;
namespace Debugger {
-namespace Internal {
-
-enum DebuggerLanguageStatus {
- DisabledLanguage = 0,
- EnabledLanguage,
- AutoEnabledLanguage
-};
-
-class DebuggerLanguageAspect : public BaseAspect
-{
-public:
- DebuggerLanguageAspect() = default;
-
- void addToLayout(Layouting::LayoutBuilder &builder) override;
-
- bool value() const;
- void setValue(bool val);
-
- void setAutoSettingsKey(const QString &settingsKey);
- void setLabel(const QString &label);
- void setInfoLabelText(const QString &text) { m_infoLabelText = text; }
-
- void setClickCallBack(const std::function<void (bool)> &clickCallBack)
- {
- m_clickCallBack = clickCallBack;
- }
-
- void fromMap(const QVariantMap &map) override;
- void toMap(QVariantMap &map) const override;
-
-public:
- DebuggerLanguageStatus m_value = AutoEnabledLanguage;
- bool m_defaultValue = false;
- QString m_label;
- QString m_infoLabelText;
- QPointer<QCheckBox> m_checkBox; // Owned by configuration widget
- QPointer<QLabel> m_infoLabel; // Owned by configuration widget
- QString m_autoSettingsKey;
-
- std::function<void(bool)> m_clickCallBack;
-};
-
-void DebuggerLanguageAspect::addToLayout(Layouting::LayoutBuilder &builder)
-{
- QTC_CHECK(!m_checkBox);
- m_checkBox = new QCheckBox(m_label);
- m_checkBox->setChecked(m_value);
- m_checkBox->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
-
- QTC_CHECK(m_clickCallBack);
- connect(m_checkBox, &QAbstractButton::clicked, this, m_clickCallBack, Qt::QueuedConnection);
-
- connect(m_checkBox.data(), &QAbstractButton::clicked, this, [this] {
- m_value = m_checkBox->isChecked() ? EnabledLanguage : DisabledLanguage;
- emit changed();
- });
- builder.addItem(QString());
- builder.addItem(m_checkBox.data());
-
- if (!m_infoLabelText.isEmpty()) {
- QTC_CHECK(!m_infoLabel);
- m_infoLabel = new QLabel(m_infoLabelText);
- connect(m_infoLabel, &QLabel::linkActivated, [](const QString &link) {
- Core::HelpManager::showHelpUrl(link);
- });
- builder.addItem(m_infoLabel.data());
- }
-}
-
-void DebuggerLanguageAspect::setAutoSettingsKey(const QString &settingsKey)
-{
- m_autoSettingsKey = settingsKey;
-}
-
-void DebuggerLanguageAspect::fromMap(const QVariantMap &map)
-{
- const bool val = map.value(settingsKey(), false).toBool();
- const bool autoVal = map.value(m_autoSettingsKey, false).toBool();
- m_value = autoVal ? AutoEnabledLanguage : val ? EnabledLanguage : DisabledLanguage;
-}
-
-void DebuggerLanguageAspect::toMap(QVariantMap &data) const
-{
- data.insert(settingsKey(), m_value == EnabledLanguage);
- data.insert(m_autoSettingsKey, m_value == AutoEnabledLanguage);
-}
-
-
-bool DebuggerLanguageAspect::value() const
-{
- return m_value;
-}
-
-void DebuggerLanguageAspect::setValue(bool value)
-{
- m_value = value ? EnabledLanguage : DisabledLanguage;
- if (m_checkBox)
- m_checkBox->setChecked(m_value);
-}
-
-void DebuggerLanguageAspect::setLabel(const QString &label)
-{
- m_label = label;
-}
-
-} // Internal
/*!
\class Debugger::DebuggerRunConfigurationAspect
@@ -151,15 +47,53 @@ DebuggerRunConfigurationAspect::DebuggerRunConfigurationAspect(Target *target)
setDisplayName(Tr::tr("Debugger settings"));
setConfigWidgetCreator([this] {
- Layouting::Form builder;
- builder.addRow(m_cppAspect);
- builder.addRow(m_qmlAspect);
- builder.addRow(m_overrideStartupAspect);
+ Layouting::Grid builder;
+ builder.addRow({m_cppAspect});
+ auto info = new QLabel(
+ Tr::tr("<a href=\""
+ "qthelp://org.qt-project.qtcreator/doc/creator-debugging-qml.html"
+ "\">What are the prerequisites?</a>"));
+ builder.addRow({m_qmlAspect, info});
+ connect(info, &QLabel::linkActivated, [](const QString &link) {
+ Core::HelpManager::showHelpUrl(link);
+ });
+ builder.addRow({m_overrideStartupAspect});
static const QString env = qtcEnvironmentVariable("QTC_DEBUGGER_MULTIPROCESS");
if (env.toInt())
- builder.addRow(m_multiProcessAspect);
- return builder.emerge(Layouting::WithoutMargins);
+ builder.addRow({m_multiProcessAspect});
+
+ auto details = new DetailsWidget;
+ details->setState(DetailsWidget::Expanded);
+ auto innerPane = new QWidget;
+ details->setWidget(innerPane);
+ builder.addItem(Layouting::noMargin);
+ builder.attachTo(innerPane);
+
+ const auto setSummaryText = [this, details] {
+ QStringList items;
+ if (m_cppAspect->value() == TriState::Enabled)
+ items.append(Tr::tr("Enable C++ debugger"));
+ else if (m_cppAspect->value() == TriState::Default)
+ items.append(Tr::tr("Try to determine need for C++ debugger"));
+
+ if (m_qmlAspect->value() == TriState::Enabled)
+ items.append(Tr::tr("Enable QML debugger"));
+ else if (m_qmlAspect->value() == TriState::Default)
+ items.append(Tr::tr("Try to determine need for QML debugger"));
+
+ items.append(m_overrideStartupAspect->value().isEmpty()
+ ? Tr::tr("Without additional startup commands")
+ : Tr::tr("With additional startup commands"));
+ details->setSummaryText(items.join(". "));
+ };
+ setSummaryText();
+
+ connect(m_cppAspect, &BaseAspect::changed, this, setSummaryText);
+ connect(m_qmlAspect, &BaseAspect::changed, this, setSummaryText);
+ connect(m_overrideStartupAspect, &BaseAspect::changed, this, setSummaryText);
+
+ return details;
});
addDataExtractor(this, &DebuggerRunConfigurationAspect::useCppDebugger, &Data::useCppDebugger);
@@ -167,29 +101,25 @@ DebuggerRunConfigurationAspect::DebuggerRunConfigurationAspect(Target *target)
addDataExtractor(this, &DebuggerRunConfigurationAspect::useMultiProcess, &Data::useMultiProcess);
addDataExtractor(this, &DebuggerRunConfigurationAspect::overrideStartup, &Data::overrideStartup);
- m_cppAspect = new DebuggerLanguageAspect;
- m_cppAspect->setLabel(Tr::tr("Enable C++"));
+ m_cppAspect = new TriStateAspect(nullptr, Tr::tr("Enabled"), Tr::tr("Disabled"), Tr::tr("Automatic"));
+ m_cppAspect->setLabelText(Tr::tr("C++ debugger:"));
m_cppAspect->setSettingsKey("RunConfiguration.UseCppDebugger");
- m_cppAspect->setAutoSettingsKey("RunConfiguration.UseCppDebuggerAuto");
- m_qmlAspect = new DebuggerLanguageAspect;
- m_qmlAspect->setLabel(Tr::tr("Enable QML"));
+ m_qmlAspect = new TriStateAspect(nullptr, Tr::tr("Enabled"), Tr::tr("Disabled"), Tr::tr("Automatic"));
+ m_qmlAspect->setLabelText(Tr::tr("QML debugger:"));
m_qmlAspect->setSettingsKey("RunConfiguration.UseQmlDebugger");
- m_qmlAspect->setAutoSettingsKey("RunConfiguration.UseQmlDebuggerAuto");
- m_qmlAspect->setInfoLabelText(Tr::tr("<a href=\""
- "qthelp://org.qt-project.qtcreator/doc/creator-debugging-qml.html"
- "\">What are the prerequisites?</a>"));
// Make sure at least one of the debuggers is set to be active.
- m_cppAspect->setClickCallBack([this](bool on) {
- if (!on && !m_qmlAspect->value())
- m_qmlAspect->setValue(true);
+ connect(m_cppAspect, &TriStateAspect::changed, this, [this]{
+ if (m_cppAspect->value() == TriState::Disabled && m_qmlAspect->value() == TriState::Disabled)
+ m_qmlAspect->setValue(TriState::Default);
});
- m_qmlAspect->setClickCallBack([this](bool on) {
- if (!on && !m_cppAspect->value())
- m_cppAspect->setValue(true);
+ connect(m_qmlAspect, &TriStateAspect::changed, this, [this]{
+ if (m_qmlAspect->value() == TriState::Disabled && m_cppAspect->value() == TriState::Disabled)
+ m_cppAspect->setValue(TriState::Default);
});
+
m_multiProcessAspect = new BoolAspect;
m_multiProcessAspect->setSettingsKey("RunConfiguration.UseMultiProcess");
m_multiProcessAspect->setLabel(Tr::tr("Enable Debugging of Subprocesses"),
@@ -211,23 +141,37 @@ DebuggerRunConfigurationAspect::~DebuggerRunConfigurationAspect()
void DebuggerRunConfigurationAspect::setUseQmlDebugger(bool value)
{
- m_qmlAspect->setValue(value);
+ m_qmlAspect->setValue(value ? TriState::Enabled : TriState::Disabled);
}
bool DebuggerRunConfigurationAspect::useCppDebugger() const
{
- if (m_cppAspect->m_value == AutoEnabledLanguage)
+ if (m_cppAspect->value() == TriState::Default)
return m_target->project()->projectLanguages().contains(
ProjectExplorer::Constants::CXX_LANGUAGE_ID);
- return m_cppAspect->m_value == EnabledLanguage;
+ return m_cppAspect->value() == TriState::Enabled;
+}
+
+static bool projectHasQmlDefines(ProjectExplorer::Project *project)
+{
+ auto projectInfo = CppEditor::CppModelManager::instance()->projectInfo(project);
+ QTC_ASSERT(projectInfo, return false);
+ return Utils::anyOf(projectInfo->projectParts(),
+ [](const CppEditor::ProjectPart::ConstPtr &part){
+ return Utils::anyOf(part->projectMacros, [](const Macro &macro){
+ return macro.key == "QT_DECLARATIVE_LIB"
+ || macro.key == "QT_QUICK_LIB"
+ || macro.key == "QT_QML_LIB";
+ });
+ });
}
bool DebuggerRunConfigurationAspect::useQmlDebugger() const
{
- if (m_qmlAspect->m_value == AutoEnabledLanguage) {
+ if (m_qmlAspect->value() == TriState::Default) {
const Core::Context languages = m_target->project()->projectLanguages();
if (!languages.contains(ProjectExplorer::Constants::QMLJS_LANGUAGE_ID))
- return false;
+ return projectHasQmlDefines(m_target->project());
//
// Try to find a build configuration to check whether qml debugging is enabled there
@@ -238,7 +182,7 @@ bool DebuggerRunConfigurationAspect::useQmlDebugger() const
return !languages.contains(ProjectExplorer::Constants::CXX_LANGUAGE_ID);
}
- return m_qmlAspect->m_value == EnabledLanguage;
+ return m_qmlAspect->value() == TriState::Enabled;
}
bool DebuggerRunConfigurationAspect::useMultiProcess() const
@@ -272,12 +216,23 @@ void DebuggerRunConfigurationAspect::toMap(QVariantMap &map) const
m_qmlAspect->toMap(map);
m_multiProcessAspect->toMap(map);
m_overrideStartupAspect->toMap(map);
+
+ // compatibility to old settings
+ map.insert("RunConfiguration.UseCppDebuggerAuto", m_cppAspect->value() == TriState::Default);
+ map.insert("RunConfiguration.UseQmlDebuggerAuto", m_qmlAspect->value() == TriState::Default);
}
void DebuggerRunConfigurationAspect::fromMap(const QVariantMap &map)
{
m_cppAspect->fromMap(map);
m_qmlAspect->fromMap(map);
+
+ // respect old project settings
+ if (map.value("RunConfiguration.UseCppDebuggerAuto", false).toBool())
+ m_cppAspect->setValue(TriState::Default);
+ if (map.value("RunConfiguration.UseQmlDebuggerAuto", false).toBool())
+ m_qmlAspect->setValue(TriState::Default);
+
m_multiProcessAspect->fromMap(map);
m_overrideStartupAspect->fromMap(map);
}
diff --git a/src/plugins/debugger/debuggerrunconfigurationaspect.h b/src/plugins/debugger/debuggerrunconfigurationaspect.h
index 2534d4f81c..53b9c1f54d 100644
--- a/src/plugins/debugger/debuggerrunconfigurationaspect.h
+++ b/src/plugins/debugger/debuggerrunconfigurationaspect.h
@@ -10,8 +10,6 @@
namespace Debugger {
-namespace Internal { class DebuggerLanguageAspect; }
-
class DEBUGGER_EXPORT DebuggerRunConfigurationAspect
: public ProjectExplorer::GlobalOrProjectAspect
{
@@ -40,8 +38,8 @@ public:
};
private:
- Internal::DebuggerLanguageAspect *m_cppAspect;
- Internal::DebuggerLanguageAspect *m_qmlAspect;
+ Utils::TriStateAspect *m_cppAspect;
+ Utils::TriStateAspect *m_qmlAspect;
Utils::BoolAspect *m_multiProcessAspect;
Utils::StringAspect *m_overrideStartupAspect;
ProjectExplorer::Target *m_target;
diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp
index 8b6ac29c99..e3feb04e39 100644
--- a/src/plugins/debugger/debuggerruncontrol.cpp
+++ b/src/plugins/debugger/debuggerruncontrol.cpp
@@ -23,8 +23,9 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorericons.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/runconfigurationaspects.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
#include <projectexplorer/toolchain.h>
@@ -34,8 +35,8 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/portlist.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/temporarydirectory.h>
#include <utils/temporaryfile.h>
#include <utils/url.h>
@@ -69,6 +70,7 @@ DebuggerEngine *createPdbEngine();
DebuggerEngine *createQmlEngine();
DebuggerEngine *createLldbEngine();
DebuggerEngine *createUvscEngine();
+DebuggerEngine *createDapEngine();
static QString noEngineMessage()
{
@@ -107,7 +109,7 @@ private:
}
m_coreUnpackProcess.setWorkingDirectory(TemporaryDirectory::masterDirectoryFilePath());
- connect(&m_coreUnpackProcess, &QtcProcess::done, this, [this] {
+ connect(&m_coreUnpackProcess, &Process::done, this, [this] {
if (m_coreUnpackProcess.error() == QProcess::UnknownError) {
reportStopped();
return;
@@ -130,7 +132,7 @@ private:
appendMessage(msg.arg(m_tempCoreFilePath.toUserOutput()), LogMessageFormat);
m_tempCoreFile.setFileName(m_tempCoreFilePath.path());
m_tempCoreFile.open(QFile::WriteOnly);
- connect(&m_coreUnpackProcess, &QtcProcess::readyReadStandardOutput, this, [this] {
+ connect(&m_coreUnpackProcess, &Process::readyReadStandardOutput, this, [this] {
m_tempCoreFile.write(m_coreUnpackProcess.readAllRawStandardOutput());
});
m_coreUnpackProcess.setCommand({"gzip", {"-c", "-d", m_coreFilePath.path()}});
@@ -146,7 +148,7 @@ private:
QFile m_tempCoreFile;
FilePath m_coreFilePath;
FilePath m_tempCoreFilePath;
- QtcProcess m_coreUnpackProcess;
+ Process m_coreUnpackProcess;
};
class DebuggerRunToolPrivate
@@ -182,8 +184,8 @@ void DebuggerRunTool::setStartMode(DebuggerStartMode startMode)
// FIXME: This is horribly wrong.
// get files from all the projects in the session
- QList<Project *> projects = SessionManager::projects();
- if (Project *startupProject = SessionManager::startupProject()) {
+ QList<Project *> projects = ProjectManager::projects();
+ if (Project *startupProject = ProjectManager::startupProject()) {
// startup project first
projects.removeOne(startupProject);
projects.insert(0, startupProject);
@@ -508,6 +510,9 @@ void DebuggerRunTool::start()
case UvscEngineType:
m_engine = createUvscEngine();
break;
+ case DapEngineType:
+ m_engine = createDapEngine();
+ break;
default:
if (!m_runParameters.isQmlDebugging) {
reportFailure(noEngineMessage() + '\n' +
@@ -611,14 +616,12 @@ void DebuggerRunTool::start()
showMessage(warningMessage, LogWarning);
- static bool checked = true;
- if (checked)
- CheckableMessageBox::information(Core::ICore::dialogParent(),
- Tr::tr("Debugger"),
- warningMessage,
- Tr::tr("&Show this message again."),
- &checked,
- QDialogButtonBox::Ok);
+ static bool doNotShowAgain = false;
+ CheckableMessageBox::information(Core::ICore::dialogParent(),
+ Tr::tr("Debugger"),
+ warningMessage,
+ &doNotShowAgain,
+ QMessageBox::Ok);
}
}
@@ -885,8 +888,8 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerm
Runnable inferior = runControl->runnable();
const FilePath &debuggerExecutable = m_runParameters.debugger.command.executable();
- inferior.command.setExecutable(inferior.command.executable().onDevice(debuggerExecutable));
- inferior.workingDirectory = inferior.workingDirectory.onDevice(debuggerExecutable);
+ inferior.command.setExecutable(debuggerExecutable.withNewMappedPath(inferior.command.executable()));
+ inferior.workingDirectory = debuggerExecutable.withNewMappedPath(inferior.workingDirectory);
// Normalize to work around QTBUG-17529 (QtDeclarative fails with 'File name case mismatch'...)
inferior.workingDirectory = inferior.workingDirectory.normalizedPathName();
m_runParameters.inferior = inferior;
@@ -900,6 +903,9 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerm
if (Project *project = runControl->project()) {
m_runParameters.projectSourceDirectory = project->projectDirectory();
m_runParameters.projectSourceFiles = project->files(Project::SourceFiles);
+ } else {
+ m_runParameters.projectSourceDirectory = m_runParameters.debugger.command.executable().parentDir();
+ m_runParameters.projectSourceFiles.clear();
}
m_runParameters.toolChainAbi = ToolChainKitAspect::targetAbi(kit);
@@ -924,7 +930,6 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerm
}
}
- m_runParameters.dumperPath = Core::ICore::resourcePath("debugger/");
if (QtSupport::QtVersion *baseQtVersion = QtSupport::QtKitAspect::qtVersion(kit)) {
const QVersionNumber qtVersion = baseQtVersion->qtVersion();
m_runParameters.fallbackQtVersion = 0x10000 * qtVersion.majorVersion()
@@ -1036,14 +1041,37 @@ DebugServerRunner::DebugServerRunner(RunControl *runControl, DebugServerPortsGat
cmd.setExecutable(commandLine().executable()); // FIXME: Case should not happen?
} else {
cmd.setExecutable(runControl->device()->debugServerPath());
- if (cmd.isEmpty())
- cmd.setExecutable(runControl->device()->filePath("gdbserver"));
+
+ if (cmd.isEmpty()) {
+ if (runControl->device()->osType() == Utils::OsTypeMac) {
+ const FilePath debugServerLocation = runControl->device()->filePath(
+ "/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/"
+ "Resources/debugserver");
+
+ if (debugServerLocation.isExecutableFile()) {
+ cmd.setExecutable(debugServerLocation);
+ } else {
+ // TODO: In the future it is expected that the debugserver will be
+ // replaced by lldb-server. Remove the check for debug server at that point.
+ const FilePath lldbserver
+ = runControl->device()->filePath("lldb-server").searchInPath();
+ if (lldbserver.isExecutableFile())
+ cmd.setExecutable(lldbserver);
+ }
+ } else {
+ cmd.setExecutable(runControl->device()->filePath("gdbserver"));
+ }
+ }
args.clear();
- if (cmd.executable().toString().contains("lldb-server")) {
+ if (cmd.executable().baseName().contains("lldb-server")) {
args.append("platform");
args.append("--listen");
args.append(QString("*:%1").arg(portsGatherer->gdbServer().port()));
args.append("--server");
+ } else if (cmd.executable().baseName() == "debugserver") {
+ args.append(QString("*:%1").arg(portsGatherer->gdbServer().port()));
+ args.append("--attach");
+ args.append(QString::number(m_pid.pid()));
} else {
// Something resembling gdbserver
if (m_useMulti)
diff --git a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp
index 33417b55a0..9d3af533bc 100644
--- a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp
+++ b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp
@@ -12,8 +12,8 @@
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/variablechooser.h>
#include <QFileDialog>
@@ -420,7 +420,7 @@ static QString findQtInstallPath(const FilePath &qmakePath)
{
if (qmakePath.isEmpty())
return QString();
- QtcProcess proc;
+ Process proc;
proc.setCommand({qmakePath, {"-query", "QT_INSTALL_HEADERS"}});
proc.start();
if (!proc.waitForFinished()) {
@@ -491,12 +491,12 @@ void SourcePathMapAspect::toMap(QVariantMap &) const
QTC_CHECK(false);
}
-void SourcePathMapAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void SourcePathMapAspect::addToLayout(Layouting::LayoutItem &parent)
{
QTC_CHECK(!d->m_widget);
d->m_widget = createSubWidget<DebuggerSourcePathMappingWidget>();
d->m_widget->setSourcePathMap(value());
- builder.addRow(d->m_widget.data());
+ parent.addItem(d->m_widget.data());
}
QVariant SourcePathMapAspect::volatileValue() const
diff --git a/src/plugins/debugger/debuggertooltipmanager.cpp b/src/plugins/debugger/debuggertooltipmanager.cpp
index f5e9933fbe..8efea0c487 100644
--- a/src/plugins/debugger/debuggertooltipmanager.cpp
+++ b/src/plugins/debugger/debuggertooltipmanager.cpp
@@ -13,16 +13,15 @@
#include "stackhandler.h"
#include "watchhandler.h"
-#include <coreplugin/icore.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/documentmodel.h>
#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/icore.h>
#include <coreplugin/modemanager.h>
+#include <coreplugin/session.h>
#include <cppeditor/cppprojectfile.h>
-#include <projectexplorer/session.h>
-
#include <texteditor/texteditor.h>
#include <texteditor/textdocument.h>
@@ -266,9 +265,20 @@ public:
void expandNode(const QModelIndex &idx)
{
+ if (!m_engine)
+ return;
+
m_expandedINames.insert(idx.data(LocalsINameRole).toString());
- if (canFetchMore(idx))
- fetchMore(idx);
+ if (canFetchMore(idx)) {
+ if (!idx.isValid())
+ return;
+
+ if (auto item = dynamic_cast<ToolTipWatchItem *>(itemForIndex(idx))) {
+ WatchItem *it = m_engine->watchHandler()->findItem(item->iname);
+ if (QTC_GUARD(it))
+ it->model()->fetchMore(it->index());
+ }
+ }
}
void collapseNode(const QModelIndex &idx)
@@ -276,22 +286,6 @@ public:
m_expandedINames.remove(idx.data(LocalsINameRole).toString());
}
- void fetchMore(const QModelIndex &idx) override
- {
- if (!idx.isValid())
- return;
- auto item = dynamic_cast<ToolTipWatchItem *>(itemForIndex(idx));
- if (!item)
- return;
- QString iname = item->iname;
- if (!m_engine)
- return;
-
- WatchItem *it = m_engine->watchHandler()->findItem(iname);
- QTC_ASSERT(it, return);
- it->model()->fetchMore(it->index());
- }
-
void restoreTreeModel(QXmlStreamReader &r);
QPointer<DebuggerEngine> m_engine;
@@ -519,7 +513,7 @@ DebuggerToolTipWidget::DebuggerToolTipWidget()
setAttribute(Qt::WA_DeleteOnClose);
isPinned = false;
- const QIcon pinIcon(":/debugger/images/pin.xpm");
+ const QIcon pinIcon = Utils::Icons::PINNED_SMALL.icon();
pinButton = new QToolButton;
pinButton->setIcon(pinIcon);
@@ -534,9 +528,7 @@ DebuggerToolTipWidget::DebuggerToolTipWidget()
auto toolBar = new QToolBar(this);
toolBar->setProperty("_q_custom_style_disabled", QVariant(true));
- const QList<QSize> pinIconSizes = pinIcon.availableSizes();
- if (!pinIconSizes.isEmpty())
- toolBar->setIconSize(pinIconSizes.front());
+ toolBar->setIconSize({12, 12});
toolBar->addWidget(pinButton);
toolBar->addWidget(copyButton);
toolBar->addWidget(titleLabel);
@@ -1171,6 +1163,7 @@ void DebuggerToolTipManagerPrivate::slotTooltipOverrideRequested
context.fileName = document->filePath();
context.position = pos;
editorWidget->convertPosition(pos, &context.line, &context.column);
+ ++context.column;
QString raw = cppExpressionAt(editorWidget, context.position, &context.line, &context.column,
&context.function, &context.scopeFromLine, &context.scopeToLine);
context.expression = fixCppExpression(raw);
diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp
index 9f8dbac661..f69c9f72d7 100644
--- a/src/plugins/debugger/gdb/gdbengine.cpp
+++ b/src/plugins/debugger/gdb/gdbengine.cpp
@@ -38,9 +38,9 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/temporaryfile.h>
@@ -146,13 +146,13 @@ GdbEngine::GdbEngine()
connect(&s.useDynamicType, &BaseAspect::changed,
this, &GdbEngine::reloadLocals);
- connect(&m_gdbProc, &QtcProcess::started,
+ connect(&m_gdbProc, &Process::started,
this, &GdbEngine::handleGdbStarted);
- connect(&m_gdbProc, &QtcProcess::done,
+ connect(&m_gdbProc, &Process::done,
this, &GdbEngine::handleGdbDone);
- connect(&m_gdbProc, &QtcProcess::readyReadStandardOutput,
+ connect(&m_gdbProc, &Process::readyReadStandardOutput,
this, &GdbEngine::readGdbStandardOutput);
- connect(&m_gdbProc, &QtcProcess::readyReadStandardError,
+ connect(&m_gdbProc, &Process::readyReadStandardError,
this, &GdbEngine::readGdbStandardError);
// Output
@@ -470,7 +470,8 @@ void GdbEngine::handleAsyncOutput(const QStringView asyncClass, const GdbMi &res
module.startAddress = 0;
module.endAddress = 0;
module.hostPath = result["host-name"].data();
- module.modulePath = result["target-name"].data();
+ const QString target = result["target-name"].data();
+ module.modulePath = runParameters().inferior.command.executable().withNewPath(target);
module.moduleName = QFileInfo(module.hostPath).baseName();
modulesHandler()->updateModule(module);
} else if (asyncClass == u"library-unloaded") {
@@ -478,7 +479,8 @@ void GdbEngine::handleAsyncOutput(const QStringView asyncClass, const GdbMi &res
// target-name="/usr/lib/libdrm.so.2",
// host-name="/usr/lib/libdrm.so.2"
QString id = result["id"].data();
- modulesHandler()->removeModule(result["target-name"].data());
+ const QString target = result["target-name"].data();
+ modulesHandler()->removeModule(runParameters().inferior.command.executable().withNewPath(target));
progressPing();
showStatusMessage(Tr::tr("Library %1 unloaded.").arg(id), 1000);
} else if (asyncClass == u"thread-group-added") {
@@ -537,6 +539,7 @@ void GdbEngine::handleAsyncOutput(const QStringView asyncClass, const GdbMi &res
ba.remove(pos1, pos3 - pos1 + 1);
GdbMi res;
res.fromString(ba);
+ const FilePath &fileRoot = runParameters().projectSourceDirectory;
BreakHandler *handler = breakHandler();
Breakpoint bp;
for (const GdbMi &bkpt : res) {
@@ -545,13 +548,13 @@ void GdbEngine::handleAsyncOutput(const QStringView asyncClass, const GdbMi &res
// A sub-breakpoint.
QTC_ASSERT(bp, continue);
SubBreakpoint loc = bp->findOrCreateSubBreakpoint(nr);
- loc->params.updateFromGdbOutput(bkpt);
+ loc->params.updateFromGdbOutput(bkpt, fileRoot);
loc->params.type = bp->type();
} else {
// A primary breakpoint.
bp = handler->findBreakpointByResponseId(nr);
if (bp)
- bp->updateFromGdbOutput(bkpt);
+ bp->updateFromGdbOutput(bkpt, fileRoot);
}
}
if (bp)
@@ -567,7 +570,7 @@ void GdbEngine::handleAsyncOutput(const QStringView asyncClass, const GdbMi &res
const QString nr = bkpt["number"].data();
BreakpointParameters br;
br.type = BreakpointByFileAndLine;
- br.updateFromGdbOutput(bkpt);
+ br.updateFromGdbOutput(bkpt, runParameters().projectSourceDirectory);
handler->handleAlienBreakpoint(nr, br);
}
} else if (asyncClass == u"breakpoint-deleted") {
@@ -762,7 +765,8 @@ void GdbEngine::runCommand(const DebuggerCommand &command)
if (cmd.flags & ConsoleCommand)
cmd.function = "-interpreter-exec console \"" + cmd.function + '"';
cmd.function = QString::number(token) + cmd.function;
- showMessage(cmd.function, LogInput);
+
+ showMessage(cmd.function.left(100), LogInput);
if (m_scheduledTestResponses.contains(token)) {
// Fake response for test cases.
@@ -797,7 +801,7 @@ void GdbEngine::runCommand(const DebuggerCommand &command)
int GdbEngine::commandTimeoutTime() const
{
- const int time = debuggerSettings()->gdbWatchdogTimeout.value();
+ const int time = debuggerSettings()->gdbWatchdogTimeout();
return 1000 * qMax(20, time);
}
@@ -1012,7 +1016,7 @@ void GdbEngine::updateAll()
{
//PENDING_DEBUG("UPDATING ALL\n");
QTC_CHECK(state() == InferiorUnrunnable || state() == InferiorStopOk);
- DebuggerCommand cmd(stackCommand(debuggerSettings()->maximalStackDepth.value()));
+ DebuggerCommand cmd(stackCommand(debuggerSettings()->maximalStackDepth()));
cmd.callback = [this](const DebuggerResponse &r) { handleStackListFrames(r, false); };
runCommand(cmd);
stackHandler()->setCurrentIndex(0);
@@ -1026,7 +1030,7 @@ void GdbEngine::handleQuerySources(const DebuggerResponse &response)
{
m_sourcesListUpdating = false;
if (response.resultClass == ResultDone) {
- QMap<QString, QString> oldShortToFull = m_shortToFullName;
+ QMap<QString, FilePath> oldShortToFull = m_shortToFullName;
m_shortToFullName.clear();
m_fullToShortName.clear();
// "^done,files=[{file="../../../../bin/dumper/dumper.cpp",
@@ -1037,7 +1041,7 @@ void GdbEngine::handleQuerySources(const DebuggerResponse &response)
continue;
GdbMi fullName = item["fullname"];
QString file = fileName.data();
- QString full;
+ FilePath full;
if (fullName.isValid()) {
full = cleanupFullName(fullName.data());
m_fullToShortName[full] = file;
@@ -1102,9 +1106,9 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
// Ignore trap on Windows terminals, which results in
// spurious "* stopped" message.
if (m_expectTerminalTrap) {
- m_expectTerminalTrap = false;
if ((!data.isValid() || !data["reason"].isValid())
&& Abi::hostAbi().os() == Abi::WindowsOS) {
+ m_expectTerminalTrap = false;
showMessage("IGNORING TERMINAL SIGTRAP", LogMisc);
return;
}
@@ -1181,7 +1185,7 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
const QString nr = data["bkptno"].data();
int lineNumber = 0;
- QString fullName;
+ FilePath fullName;
QString function;
QString language;
if (frame.isValid()) {
@@ -1194,15 +1198,13 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
lineNumber = lineNumberG.toInt();
fullName = cleanupFullName(frame["fullname"].data());
if (fullName.isEmpty())
- fullName = frame["file"].data();
+ fullName = runParameters().projectSourceDirectory.withNewPath(frame["file"].data());
} // found line number
} else {
showMessage("INVALID STOPPED REASON", LogWarning);
}
- const FilePath onDevicePath = FilePath::fromString(fullName).onDevice(
- runParameters().debugger.command.executable());
- const FilePath fileName = onDevicePath.localSource().value_or(onDevicePath);
+ const FilePath fileName = fullName.localSource().value_or(fullName);
if (!nr.isEmpty() && frame.isValid()) {
// Use opportunity to update the breakpoint marker position.
@@ -1420,14 +1422,19 @@ void GdbEngine::handleStop2(const GdbMi &data)
} else if (m_isQnxGdb && name == "0" && meaning == "Signal 0") {
showMessage("SIGNAL 0 CONSIDERED BOGUS.");
} else {
- showMessage("HANDLING SIGNAL " + name);
- if (debuggerSettings()->useMessageBoxForSignals.value() && !isStopperThread)
- if (!showStoppedBySignalMessageBox(meaning, name)) {
- showMessage("SIGNAL RECEIVED WHILE SHOWING SIGNAL MESSAGE");
- return;
- }
- if (!name.isEmpty() && !meaning.isEmpty())
- reasontr = msgStoppedBySignal(meaning, name);
+ if (terminal() && name == "SIGCONT" && m_expectTerminalTrap) {
+ continueInferior();
+ m_expectTerminalTrap = false;
+ } else {
+ showMessage("HANDLING SIGNAL " + name);
+ if (debuggerSettings()->useMessageBoxForSignals.value() && !isStopperThread)
+ if (!showStoppedBySignalMessageBox(meaning, name)) {
+ showMessage("SIGNAL RECEIVED WHILE SHOWING SIGNAL MESSAGE");
+ return;
+ }
+ if (!name.isEmpty() && !meaning.isEmpty())
+ reasontr = msgStoppedBySignal(meaning, name);
+ }
}
}
if (reason.isEmpty())
@@ -1558,50 +1565,47 @@ void GdbEngine::handleExecuteContinue(const DebuggerResponse &response)
}
}
-QString GdbEngine::fullName(const QString &fileName)
+FilePath GdbEngine::fullName(const QString &fileName)
{
if (fileName.isEmpty())
- return QString();
- QTC_ASSERT(!m_sourcesListUpdating, /* */);
- return m_shortToFullName.value(fileName, QString());
+ return {};
+ QTC_CHECK(!m_sourcesListUpdating);
+ return m_shortToFullName.value(fileName, {});
}
-QString GdbEngine::cleanupFullName(const QString &fileName)
+FilePath GdbEngine::cleanupFullName(const QString &fileName)
{
- QString cleanFilePath = fileName;
+ FilePath cleanFilePath =
+ runParameters().projectSourceDirectory.withNewPath(fileName).cleanPath();
// Gdb running on windows often delivers "fullnames" which
// (a) have no drive letter and (b) are not normalized.
if (Abi::hostAbi().os() == Abi::WindowsOS) {
if (fileName.isEmpty())
- return QString();
- QFileInfo fi(fileName);
- if (fi.isReadable())
- cleanFilePath = QDir::cleanPath(fi.absoluteFilePath());
+ return {};
}
if (!debuggerSettings()->autoEnrichParameters.value())
return cleanFilePath;
- const QString sysroot = runParameters().sysRoot.toString();
- if (QFileInfo(cleanFilePath).isReadable())
+ if (cleanFilePath.isReadableFile())
return cleanFilePath;
+
+ const FilePath sysroot = runParameters().sysRoot;
if (!sysroot.isEmpty() && fileName.startsWith('/')) {
- cleanFilePath = sysroot + fileName;
- if (QFileInfo(cleanFilePath).isReadable())
+ cleanFilePath = sysroot.pathAppended(fileName.mid(1));
+ if (cleanFilePath.isReadableFile())
return cleanFilePath;
}
if (m_baseNameToFullName.isEmpty()) {
- FilePath filePath = FilePath::fromString(sysroot + "/usr/src/debug");
+ FilePath filePath = sysroot.pathAppended("/usr/src/debug");
if (filePath.isDir()) {
filePath.iterateDirectory(
[this](const FilePath &filePath) {
QString name = filePath.fileName();
- if (!name.startsWith('.')) {
- QString path = filePath.path();
- m_baseNameToFullName.insert(name, path);
- }
+ if (!name.startsWith('.'))
+ m_baseNameToFullName.insert(name, filePath);
return IterationPolicy::Continue;
},
{{"*"}, QDir::NoFilter, QDirIterator::Subdirectories});
@@ -1611,7 +1615,7 @@ QString GdbEngine::cleanupFullName(const QString &fileName)
cleanFilePath.clear();
const QString base = FilePath::fromUserInput(fileName).fileName();
- QMultiMap<QString, QString>::const_iterator jt = m_baseNameToFullName.constFind(base);
+ auto jt = m_baseNameToFullName.constFind(base);
while (jt != m_baseNameToFullName.constEnd() && jt.key() == base) {
// FIXME: Use some heuristics to find the "best" match.
return jt.value();
@@ -1948,7 +1952,7 @@ void GdbEngine::executeRunToLine(const ContextData &data)
if (data.address)
loc = addressSpec(data.address);
else
- loc = '"' + breakLocation(data.fileName.toString()) + '"' + ':' + QString::number(data.lineNumber);
+ loc = '"' + breakLocation(data.fileName) + '"' + ':' + QString::number(data.lineNumber);
runCommand({"tbreak " + loc});
runCommand({"continue", NativeCommand|RunRequest, CB(handleExecuteRunToLine)});
@@ -1976,7 +1980,7 @@ void GdbEngine::executeJumpToLine(const ContextData &data)
if (data.address)
loc = addressSpec(data.address);
else
- loc = '"' + breakLocation(data.fileName.toString()) + '"' + ':' + QString::number(data.lineNumber);
+ loc = '"' + breakLocation(data.fileName) + '"' + ':' + QString::number(data.lineNumber);
runCommand({"tbreak " + loc});
notifyInferiorRunRequested();
@@ -2050,11 +2054,11 @@ void GdbEngine::setTokenBarrier()
//
//////////////////////////////////////////////////////////////////////
-QString GdbEngine::breakLocation(const QString &file) const
+QString GdbEngine::breakLocation(const FilePath &file) const
{
QString where = m_fullToShortName.value(file);
if (where.isEmpty())
- return FilePath::fromString(file).fileName();
+ return file.fileName();
return where;
}
@@ -2078,7 +2082,7 @@ QString GdbEngine::breakpointLocation(const BreakpointParameters &data)
usage = BreakpointUseShortPath;
const QString fileName = usage == BreakpointUseFullPath
- ? data.fileName.toString() : breakLocation(data.fileName.toString());
+ ? data.fileName.path() : breakLocation(data.fileName);
// The argument is simply a C-quoted version of the argument to the
// non-MI "break" command, including the "original" quoting it wants.
return "\"\\\"" + GdbMi::escapeCString(fileName) + "\\\":"
@@ -2092,7 +2096,7 @@ QString GdbEngine::breakpointLocation2(const BreakpointParameters &data)
usage = BreakpointUseShortPath;
const QString fileName = usage == BreakpointUseFullPath
- ? data.fileName.toString() : breakLocation(data.fileName.toString());
+ ? data.fileName.path() : breakLocation(data.fileName);
return GdbMi::escapeCString(fileName) + ':' + QString::number(data.lineNumber);
}
@@ -2105,7 +2109,7 @@ void GdbEngine::handleInsertInterpreterBreakpoint(const DebuggerResponse &respon
notifyBreakpointInsertOk(bp);
} else {
bp->setResponseId(response.data["number"].data());
- bp->updateFromGdbOutput(response.data);
+ bp->updateFromGdbOutput(response.data, runParameters().projectSourceDirectory);
notifyBreakpointInsertOk(bp);
}
}
@@ -2115,7 +2119,7 @@ void GdbEngine::handleInterpreterBreakpointModified(const GdbMi &data)
int modelId = data["modelid"].toInt();
Breakpoint bp = breakHandler()->findBreakpointByModelId(modelId);
QTC_ASSERT(bp, return);
- bp->updateFromGdbOutput(data);
+ bp->updateFromGdbOutput(data, runParameters().projectSourceDirectory);
}
void GdbEngine::handleWatchInsert(const DebuggerResponse &response, const Breakpoint &bp)
@@ -2165,7 +2169,7 @@ void GdbEngine::handleBkpt(const GdbMi &bkpt, const Breakpoint &bp)
// A sub-breakpoint.
SubBreakpoint sub = bp->findOrCreateSubBreakpoint(nr);
QTC_ASSERT(sub, return);
- sub->params.updateFromGdbOutput(bkpt);
+ sub->params.updateFromGdbOutput(bkpt, runParameters().projectSourceDirectory);
sub->params.type = bp->type();
if (usePseudoTracepoints && bp->isTracepoint()) {
sub->params.tracepoint = true;
@@ -2183,7 +2187,7 @@ void GdbEngine::handleBkpt(const GdbMi &bkpt, const Breakpoint &bp)
const QString subnr = location["number"].data();
SubBreakpoint sub = bp->findOrCreateSubBreakpoint(subnr);
QTC_ASSERT(sub, return);
- sub->params.updateFromGdbOutput(location);
+ sub->params.updateFromGdbOutput(location, runParameters().projectSourceDirectory);
sub->params.type = bp->type();
if (usePseudoTracepoints && bp->isTracepoint()) {
sub->params.tracepoint = true;
@@ -2194,7 +2198,7 @@ void GdbEngine::handleBkpt(const GdbMi &bkpt, const Breakpoint &bp)
// A (the?) primary breakpoint.
bp->setResponseId(nr);
- bp->updateFromGdbOutput(bkpt);
+ bp->updateFromGdbOutput(bkpt, runParameters().projectSourceDirectory);
if (usePseudoTracepoints && bp->isTracepoint())
bp->setMessage(bp->requestedParameters().message);
}
@@ -2501,7 +2505,7 @@ void GdbEngine::handleTracepointModified(const GdbMi &data)
// A sub-breakpoint.
QTC_ASSERT(bp, continue);
SubBreakpoint loc = bp->findOrCreateSubBreakpoint(nr);
- loc->params.updateFromGdbOutput(bkpt);
+ loc->params.updateFromGdbOutput(bkpt, runParameters().projectSourceDirectory);
loc->params.type = bp->type();
if (bp->isTracepoint()) {
loc->params.tracepoint = true;
@@ -2511,7 +2515,7 @@ void GdbEngine::handleTracepointModified(const GdbMi &data)
// A primary breakpoint.
bp = handler->findBreakpointByResponseId(nr);
if (bp)
- bp->updateFromGdbOutput(bkpt);
+ bp->updateFromGdbOutput(bkpt, runParameters().projectSourceDirectory);
}
}
QTC_ASSERT(bp, return);
@@ -2612,13 +2616,13 @@ void GdbEngine::insertBreakpoint(const Breakpoint &bp)
"QTC_DEBUGGER_PYTHON_VERBOSE");
const DebuggerSettings &s = *debuggerSettings();
cmd.arg("passexceptions", alwaysVerbose);
- cmd.arg("fancy", s.useDebuggingHelpers.value());
- cmd.arg("autoderef", s.autoDerefPointers.value());
- cmd.arg("dyntype", s.useDynamicType.value());
- cmd.arg("qobjectnames", s.showQObjectNames.value());
+ cmd.arg("fancy", s.useDebuggingHelpers());
+ cmd.arg("autoderef", s.autoDerefPointers());
+ cmd.arg("dyntype", s.useDynamicType());
+ cmd.arg("qobjectnames", s.showQObjectNames());
cmd.arg("nativemixed", isNativeMixedActive());
- cmd.arg("stringcutoff", s.maximalStringLength.value());
- cmd.arg("displaystringlimit", s.displayStringLimit.value());
+ cmd.arg("stringcutoff", s.maximalStringLength());
+ cmd.arg("displaystringlimit", s.displayStringLimit());
cmd.arg("spec", breakpointLocation2(requested));
cmd.callback = [this, bp](const DebuggerResponse &r) { handleTracepointInsert(r, bp); };
@@ -2783,10 +2787,10 @@ static QString dotEscape(QString str)
return str;
}
-void GdbEngine::loadSymbols(const QString &modulePath)
+void GdbEngine::loadSymbols(const FilePath &modulePath)
{
// FIXME: gdb does not understand quoted names here (tested with 6.8)
- runCommand({"sharedlibrary " + dotEscape(modulePath)});
+ runCommand({"sharedlibrary " + dotEscape(modulePath.path())});
reloadModulesInternal();
reloadStack();
updateLocals();
@@ -2811,7 +2815,7 @@ void GdbEngine::loadSymbolsForStack()
for (const Module &module : modules) {
if (module.startAddress <= frame.address
&& frame.address < module.endAddress) {
- runCommand({"sharedlibrary " + dotEscape(module.modulePath)});
+ runCommand({"sharedlibrary " + dotEscape(module.modulePath.path())});
needUpdate = true;
}
}
@@ -2824,7 +2828,7 @@ void GdbEngine::loadSymbolsForStack()
}
static void handleShowModuleSymbols(const DebuggerResponse &response,
- const QString &modulePath, const QString &fileName)
+ const FilePath &modulePath, const QString &fileName)
{
if (response.resultClass == ResultDone) {
Symbols symbols;
@@ -2883,21 +2887,21 @@ static void handleShowModuleSymbols(const DebuggerResponse &response,
}
}
-void GdbEngine::requestModuleSymbols(const QString &modulePath)
+void GdbEngine::requestModuleSymbols(const FilePath &modulePath)
{
- Utils::TemporaryFile tf("gdbsymbols");
+ TemporaryFile tf("gdbsymbols");
if (!tf.open())
return;
QString fileName = tf.fileName();
tf.close();
- DebuggerCommand cmd("maint print msymbols \"" + fileName + "\" " + modulePath, NeedsTemporaryStop);
+ DebuggerCommand cmd("maint print msymbols \"" + fileName + "\" " + modulePath.path(), NeedsTemporaryStop);
cmd.callback = [modulePath, fileName](const DebuggerResponse &r) {
handleShowModuleSymbols(r, modulePath, fileName);
};
runCommand(cmd);
}
-void GdbEngine::requestModuleSections(const QString &moduleName)
+void GdbEngine::requestModuleSections(const FilePath &moduleName)
{
// There seems to be no way to get the symbols from a single .so.
DebuggerCommand cmd("maint info section ALLOBJ", NeedsTemporaryStop);
@@ -2908,14 +2912,14 @@ void GdbEngine::requestModuleSections(const QString &moduleName)
}
void GdbEngine::handleShowModuleSections(const DebuggerResponse &response,
- const QString &moduleName)
+ const FilePath &moduleName)
{
// ~" Object file: /usr/lib/i386-linux-gnu/libffi.so.6\n"
// ~" 0xb44a6114->0xb44a6138 at 0x00000114: .note.gnu.build-id ALLOC LOAD READONLY DATA HAS_CONTENTS\n"
if (response.resultClass == ResultDone) {
const QStringList lines = response.consoleStreamOutput.split('\n');
const QString prefix = " Object file: ";
- const QString needle = prefix + moduleName;
+ const QString needle = prefix + moduleName.path();
Sections sections;
bool active = false;
for (const QString &line : std::as_const(lines)) {
@@ -2956,11 +2960,6 @@ void GdbEngine::reloadModulesInternal()
runCommand({"info shared", NeedsTemporaryStop, CB(handleModulesList)});
}
-static QString nameFromPath(const QString &path)
-{
- return QFileInfo(path).baseName();
-}
-
void GdbEngine::handleModulesList(const DebuggerResponse &response)
{
if (response.resultClass == ResultDone) {
@@ -2971,14 +2970,15 @@ void GdbEngine::handleModulesList(const DebuggerResponse &response)
QString data = response.consoleStreamOutput;
QTextStream ts(&data, QIODevice::ReadOnly);
bool found = false;
+ const FilePath inferior = runParameters().inferior.command.executable();
while (!ts.atEnd()) {
QString line = ts.readLine();
QString symbolsRead;
QTextStream ts(&line, QIODevice::ReadOnly);
if (line.startsWith("0x")) {
ts >> module.startAddress >> module.endAddress >> symbolsRead;
- module.modulePath = ts.readLine().trimmed();
- module.moduleName = nameFromPath(module.modulePath);
+ module.modulePath = inferior.withNewPath(ts.readLine().trimmed());
+ module.moduleName = module.modulePath.baseName();
module.symbolsRead =
(symbolsRead == "Yes" ? Module::ReadOk : Module::ReadFailed);
handler->updateModule(module);
@@ -2989,8 +2989,8 @@ void GdbEngine::handleModulesList(const DebuggerResponse &response)
QTC_ASSERT(symbolsRead == "No", continue);
module.startAddress = 0;
module.endAddress = 0;
- module.modulePath = ts.readLine().trimmed();
- module.moduleName = nameFromPath(module.modulePath);
+ module.modulePath = inferior.withNewPath(ts.readLine().trimmed());
+ module.moduleName = module.modulePath.baseName();
handler->updateModule(module);
found = true;
}
@@ -3002,8 +3002,8 @@ void GdbEngine::handleModulesList(const DebuggerResponse &response)
// loaded_addr="0x8fe00000",slide="0x0",prefix="__dyld_"},
// shlib-info={...}...
for (const GdbMi &item : response.data) {
- module.modulePath = item["path"].data();
- module.moduleName = nameFromPath(module.modulePath);
+ module.modulePath = inferior.withNewPath(item["path"].data());
+ module.moduleName = module.modulePath.baseName();
module.symbolsRead = (item["state"].data() == "Y")
? Module::ReadOk : Module::ReadFailed;
module.startAddress =
@@ -3038,7 +3038,7 @@ void GdbEngine::reloadSourceFiles()
cmd.callback = [this](const DebuggerResponse &response) {
m_sourcesListUpdating = false;
if (response.resultClass == ResultDone) {
- QMap<QString, QString> oldShortToFull = m_shortToFullName;
+ QMap<QString, FilePath> oldShortToFull = m_shortToFullName;
m_shortToFullName.clear();
m_fullToShortName.clear();
// "^done,files=[{file="../../../../bin/dumper/dumper.cpp",
@@ -3049,7 +3049,7 @@ void GdbEngine::reloadSourceFiles()
continue;
GdbMi fullName = item["fullname"];
QString file = fileName.data();
- QString full;
+ FilePath full;
if (fullName.isValid()) {
full = cleanupFullName(fullName.data());
m_fullToShortName[full] = file;
@@ -3922,7 +3922,7 @@ void GdbEngine::handleGdbStarted()
Module module;
module.startAddress = 0;
module.endAddress = 0;
- module.modulePath = rp.inferior.command.executable().toString();
+ module.modulePath = rp.inferior.command.executable();
module.moduleName = "<executable>";
modulesHandler()->updateModule(module);
@@ -3970,17 +3970,66 @@ void GdbEngine::handleGdbStarted()
//if (terminal()->isUsable())
// runCommand({"set inferior-tty " + QString::fromUtf8(terminal()->slaveDevice())});
- const QString uninstalledData = rp.debugger.command.executable().parentDir()
- .pathAppended("data-directory/python").path();
+ const FilePath dumperPath = ICore::resourcePath("debugger");
+ if (rp.debugger.command.executable().needsDevice()) {
+ // Gdb itself running remotely.
+ const FilePath loadOrderFile = dumperPath / "loadorder.txt";
+ const expected_str<QByteArray> toLoad = loadOrderFile.fileContents();
+ if (!toLoad) {
+ AsynchronousMessageBox::critical(
+ Tr::tr("Cannot Find Debugger Initialization Script"),
+ Tr::tr("Cannot read %1: %2").arg(loadOrderFile.toUserOutput(), toLoad.error()));
+ notifyEngineSetupFailed();
+ return;
+ }
+
+ runCommand({"python import sys, types"});
+ QStringList moduleList;
+ for (const QByteArray &rawModuleName : toLoad->split('\n')) {
+ QString module = QString::fromUtf8(rawModuleName).trimmed();
+ if (module.startsWith('#') || module.isEmpty())
+ continue;
+ if (module == "***bridge***")
+ module = "gdbbridge";
+
+ const FilePath codeFile = dumperPath / (module + ".py");
+ const expected_str<QByteArray> code = codeFile.fileContents();
+ if (!code) {
+ qDebug() << Tr::tr("Cannot read %1: %2").arg(codeFile.toUserOutput(), code.error());
+ continue;
+ }
- runCommand({"python sys.path.insert(1, '" + rp.dumperPath.path() + "')"});
- runCommand({"python sys.path.append('" + uninstalledData + "')"});
- runCommand({"python from gdbbridge import *"});
+ showMessage("Reading " + codeFile.toUserOutput(), LogInput);
+ runCommand({QString("python module = types.ModuleType('%1')").arg(module)});
+ runCommand({QString("python code = bytes.fromhex('%1').decode('utf-8')")
+ .arg(QString::fromUtf8(code->toHex()))});
+ runCommand({QString("python exec(code, module.__dict__)")});
+ runCommand({QString("python sys.modules['%1'] = module").arg(module)});
+ runCommand({QString("python import %1").arg(module)});
- const QString path = debuggerSettings()->extraDumperFile.value();
- if (!path.isEmpty() && QFileInfo(path).isReadable()) {
+ if (module.endsWith("types"))
+ moduleList.append('"' + module + '"');
+ }
+
+ runCommand({"python from gdbbridge import *"});
+ runCommand(QString("python theDumper.dumpermodules = [%1]").arg(moduleList.join(',')));
+
+ } else {
+ // Gdb on local host
+ // This is useful (only) in custom gdb builds that did not run 'make install'
+ const FilePath uninstalledData = rp.debugger.command.executable().parentDir()
+ / "data-directory/python";
+ if (uninstalledData.exists())
+ runCommand({"python sys.path.append('" + uninstalledData.path() + "')"});
+
+ runCommand({"python sys.path.insert(1, '" + dumperPath.path() + "')"});
+ runCommand({"python from gdbbridge import *"});
+ }
+
+ const FilePath path = debuggerSettings()->extraDumperFile();
+ if (!path.isEmpty() && path.isReadableFile()) {
DebuggerCommand cmd("addDumperModule");
- cmd.arg("path", path);
+ cmd.arg("path", path.path());
runCommand(cmd);
}
@@ -4256,7 +4305,7 @@ void GdbEngine::interruptLocalInferior(qint64 pid)
if (runParameters().runAsRoot) {
Environment env = Environment::systemEnvironment();
RunControl::provideAskPassEntry(env);
- QtcProcess proc;
+ Process proc;
proc.setCommand(CommandLine{"sudo", {"-A", "kill", "-s", "SIGINT", QString::number(pid)}});
proc.setEnvironment(env);
proc.start();
@@ -4369,7 +4418,7 @@ void GdbEngine::setupInferior()
setLinuxOsAbi();
QString symbolFile;
if (!rp.symbolFile.isEmpty())
- symbolFile = rp.symbolFile.toFileInfo().absoluteFilePath();
+ symbolFile = rp.symbolFile.absoluteFilePath().path();
//const QByteArray sysroot = sp.sysroot.toLocal8Bit();
//const QByteArray remoteArch = sp.remoteArchitecture.toLatin1();
@@ -4558,7 +4607,13 @@ void GdbEngine::handleLocalAttach(const DebuggerResponse &response)
switch (response.resultClass) {
case ResultDone:
case ResultRunning:
+ {
showMessage("INFERIOR ATTACHED");
+
+ QString commands = expand(debuggerSettings()->gdbPostAttachCommands.value());
+ if (!commands.isEmpty())
+ runCommand({commands, NativeCommand});
+
if (state() == EngineRunRequested) {
// Happens e.g. for "Attach to unstarted application"
// We will get a '*stopped' later that we'll interpret as 'spontaneous'
@@ -4578,6 +4633,7 @@ void GdbEngine::handleLocalAttach(const DebuggerResponse &response)
updateAll();
}
break;
+ }
case ResultError:
if (response.data["msg"].data() == "ptrace: Operation not permitted.") {
QString msg = msgPtraceError(runParameters().startMode);
@@ -4732,6 +4788,13 @@ void GdbEngine::handleExecRun(const DebuggerResponse &response)
CHECK_STATE(EngineRunRequested);
if (response.resultClass == ResultRunning) {
+
+ if (isLocalRunEngine()) {
+ QString commands = expand(debuggerSettings()->gdbPostAttachCommands.value());
+ if (!commands.isEmpty())
+ runCommand({commands, NativeCommand});
+ }
+
notifyEngineRunAndInferiorRunOk();
showMessage("INFERIOR STARTED");
showMessage(msgInferiorSetupOk(), StatusBar);
@@ -4983,7 +5046,7 @@ CoreInfo CoreInfo::readExecutableNameFromCore(const Runnable &debugger, const Fi
args += {"-ex", "set osabi GNU/Linux"};
args += {"-ex", "core " + coreFile.toUserOutput()};
- QtcProcess proc;
+ Process proc;
Environment envLang(Environment::systemEnvironment());
envLang.setupEnglishOutput();
proc.setEnvironment(envLang);
diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h
index cd3a21ffbd..64a2eddd4e 100644
--- a/src/plugins/debugger/gdb/gdbengine.h
+++ b/src/plugins/debugger/gdb/gdbengine.h
@@ -15,7 +15,7 @@
#include <debugger/outputcollector.h>
#include <utils/id.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QProcess>
#include <QTextCodec>
@@ -210,23 +210,23 @@ private: ////////// General Interface //////////
void handleBkpt(const GdbMi &bkpt, const Breakpoint &bp);
QString breakpointLocation(const BreakpointParameters &data); // For gdb/MI.
QString breakpointLocation2(const BreakpointParameters &data); // For gdb/CLI fallback.
- QString breakLocation(const QString &file) const;
+ QString breakLocation(const Utils::FilePath &file) const;
void updateTracepointCaptures(const Breakpoint &bp);
//
// Modules specific stuff
//
- void loadSymbols(const QString &moduleName) final;
+ void loadSymbols(const Utils::FilePath &moduleName) final;
void loadAllSymbols() final;
void loadSymbolsForStack() final;
- void requestModuleSymbols(const QString &moduleName) final;
- void requestModuleSections(const QString &moduleName) final;
+ void requestModuleSymbols(const Utils::FilePath &moduleName) final;
+ void requestModuleSections(const Utils::FilePath &moduleName) final;
void reloadModules() final;
void examineModules() final;
void reloadModulesInternal();
void handleModulesList(const DebuggerResponse &response);
- void handleShowModuleSections(const DebuggerResponse &response, const QString &moduleName);
+ void handleShowModuleSections(const DebuggerResponse &response, const Utils::FilePath &moduleName);
//
// Snapshot specific stuff
@@ -265,13 +265,13 @@ private: ////////// General Interface //////////
void reloadSourceFilesInternal();
void handleQuerySources(const DebuggerResponse &response);
- QString fullName(const QString &fileName);
- QString cleanupFullName(const QString &fileName);
+ Utils::FilePath fullName(const QString &fileName);
+ Utils::FilePath cleanupFullName(const QString &fileName);
// awful hack to keep track of used files
- QMap<QString, QString> m_shortToFullName;
- QMap<QString, QString> m_fullToShortName;
- QMultiMap<QString, QString> m_baseNameToFullName;
+ QMap<QString, Utils::FilePath> m_shortToFullName;
+ QMap<Utils::FilePath, QString> m_fullToShortName;
+ QMultiMap<QString, Utils::FilePath> m_baseNameToFullName;
bool m_sourcesListUpdating = false;
@@ -405,7 +405,7 @@ private: ////////// General Interface //////////
bool usesOutputCollector() const;
- Utils::QtcProcess m_gdbProc;
+ Utils::Process m_gdbProc;
OutputCollector m_outputCollector;
QString m_errorString;
};
diff --git a/src/plugins/debugger/gdb/gdboptionspage.cpp b/src/plugins/debugger/gdb/gdboptionspage.cpp
index 3d24dd6c9c..37008cabcd 100644
--- a/src/plugins/debugger/gdb/gdboptionspage.cpp
+++ b/src/plugins/debugger/gdb/gdboptionspage.cpp
@@ -32,7 +32,7 @@ public:
setCategory(Constants::DEBUGGER_SETTINGS_CATEGORY);
setSettings(&debuggerSettings()->page2);
- setLayouter([](QWidget *w) {
+ setLayouter([] {
using namespace Layouting;
DebuggerSettings &s = *debuggerSettings();
@@ -84,7 +84,7 @@ public:
Column { s.gdbPostAttachCommands },
};
- Grid { general, extended, br, startup, attach }.attachTo(w);
+ return Grid { general, extended, br, startup, attach };
});
}
};
diff --git a/src/plugins/debugger/images/pin.xpm b/src/plugins/debugger/images/pin.xpm
deleted file mode 100644
index 0759becb67..0000000000
--- a/src/plugins/debugger/images/pin.xpm
+++ /dev/null
@@ -1,19 +0,0 @@
-/* XPM */
-static const char * pin_xpm[] = {
-"12 9 7 1",
-" c None",
-". c #000000",
-"+ c #515151",
-"@ c #A8A8A8",
-"# c #A9A9A9",
-"$ c #999999",
-"% c #696969",
-" . ",
-" ......+",
-" .@@@@@.",
-" .#####.",
-"+.....$$$$$.",
-" .%%%%%.",
-" .......",
-" ......+",
-" . "};
diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp
index 3283d5ff7d..09b5d6eaec 100644
--- a/src/plugins/debugger/lldb/lldbengine.cpp
+++ b/src/plugins/debugger/lldb/lldbengine.cpp
@@ -29,9 +29,9 @@
#include <coreplugin/icore.h>
#include <utils/environment.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QApplication>
#include <QDateTime>
@@ -77,11 +77,11 @@ LldbEngine::LldbEngine()
connect(&ds.useDynamicType, &BaseAspect::changed, this, &LldbEngine::updateLocals);
connect(&ds.intelFlavor, &BaseAspect::changed, this, &LldbEngine::updateAll);
- connect(&m_lldbProc, &QtcProcess::started, this, &LldbEngine::handleLldbStarted);
- connect(&m_lldbProc, &QtcProcess::done, this, &LldbEngine::handleLldbDone);
- connect(&m_lldbProc, &QtcProcess::readyReadStandardOutput,
+ connect(&m_lldbProc, &Process::started, this, &LldbEngine::handleLldbStarted);
+ connect(&m_lldbProc, &Process::done, this, &LldbEngine::handleLldbDone);
+ connect(&m_lldbProc, &Process::readyReadStandardOutput,
this, &LldbEngine::readLldbStandardOutput);
- connect(&m_lldbProc, &QtcProcess::readyReadStandardError,
+ connect(&m_lldbProc, &Process::readyReadStandardError,
this, &LldbEngine::readLldbStandardError);
connect(this, &LldbEngine::outputReady,
@@ -187,7 +187,7 @@ void LldbEngine::setupEngine()
// LLDB 14 installation on Ubuntu 22.04 is broken:
// https://bugs.launchpad.net/ubuntu/+source/llvm-defaults/+bug/1972855
// Brush over it:
- QtcProcess lldbPythonPathFinder;
+ Process lldbPythonPathFinder;
lldbPythonPathFinder.setCommand({lldbCmd, {"-P"}});
lldbPythonPathFinder.start();
lldbPythonPathFinder.waitForFinished();
@@ -219,7 +219,8 @@ void LldbEngine::handleLldbStarted()
const DebuggerRunParameters &rp = runParameters();
- executeCommand("script sys.path.insert(1, '" + rp.dumperPath.path() + "')");
+ QString dumperPath = ICore::resourcePath("debugger").path();
+ executeCommand("script sys.path.insert(1, '" + dumperPath + "')");
// This triggers reportState("enginesetupok") or "enginesetupfailed":
executeCommand("script from lldbbridge import *");
@@ -227,10 +228,10 @@ void LldbEngine::handleLldbStarted()
if (!commands.isEmpty())
executeCommand(commands);
- const QString path = debuggerSettings()->extraDumperFile.value();
- if (!path.isEmpty() && QFileInfo(path).isReadable()) {
+ const FilePath path = debuggerSettings()->extraDumperFile();
+ if (!path.isEmpty() && path.isReadableFile()) {
DebuggerCommand cmd("addDumperModule");
- cmd.arg("path", path);
+ cmd.arg("path", path.path());
runCommand(cmd);
}
@@ -267,7 +268,8 @@ void LldbEngine::handleLldbStarted()
cmd2.arg("nativemixed", isNativeMixedActive());
cmd2.arg("workingdirectory", rp.inferior.workingDirectory.path());
cmd2.arg("environment", rp.inferior.environment.toStringList());
- cmd2.arg("processargs", toHex(ProcessArgs::splitArgs(rp.inferior.command.arguments()).join(QChar(0))));
+ cmd2.arg("processargs", toHex(ProcessArgs::splitArgs(rp.inferior.command.arguments(),
+ HostOsInfo::hostOs()).join(QChar(0))));
cmd2.arg("platform", rp.platform);
cmd2.arg("symbolfile", rp.symbolFile.path());
@@ -278,8 +280,8 @@ void LldbEngine::handleLldbStarted()
? QString::fromLatin1("Attaching to %1 (%2)").arg(attachedPID).arg(attachedMainThreadID)
: QString::fromLatin1("Attaching to %1").arg(attachedPID);
showMessage(msg, LogMisc);
+ cmd2.arg("startmode", DebuggerStartMode::AttachToLocalProcess);
cmd2.arg("attachpid", attachedPID);
-
} else {
cmd2.arg("startmode", rp.startMode);
// it is better not to check the start mode on the python sid (as we would have to duplicate the
@@ -467,7 +469,7 @@ void LldbEngine::selectThread(const Thread &thread)
DebuggerCommand cmd("selectThread");
cmd.arg("id", thread->id());
cmd.callback = [this](const DebuggerResponse &) {
- fetchStack(debuggerSettings()->maximalStackDepth.value());
+ fetchStack(debuggerSettings()->maximalStackDepth());
};
runCommand(cmd);
}
@@ -629,7 +631,7 @@ void LldbEngine::handleInterpreterBreakpointModified(const GdbMi &bpItem)
updateBreakpointData(bp, bpItem, false);
}
-void LldbEngine::loadSymbols(const QString &moduleName)
+void LldbEngine::loadSymbols(const FilePath &moduleName)
{
Q_UNUSED(moduleName)
}
@@ -642,12 +644,13 @@ void LldbEngine::reloadModules()
{
DebuggerCommand cmd("fetchModules");
cmd.callback = [this](const DebuggerResponse &response) {
+ const FilePath inferior = runParameters().inferior.command.executable();
const GdbMi &modules = response.data["modules"];
ModulesHandler *handler = modulesHandler();
handler->beginUpdateAll();
for (const GdbMi &item : modules) {
Module module;
- module.modulePath = item["file"].data();
+ module.modulePath = inferior.withNewPath(item["file"].data());
module.moduleName = item["name"].data();
module.symbolsRead = Module::UnknownReadState;
module.startAddress = item["loaded_addr"].toAddress();
@@ -659,13 +662,13 @@ void LldbEngine::reloadModules()
runCommand(cmd);
}
-void LldbEngine::requestModuleSymbols(const QString &moduleName)
+void LldbEngine::requestModuleSymbols(const FilePath &moduleName)
{
DebuggerCommand cmd("fetchSymbols");
- cmd.arg("module", moduleName);
+ cmd.arg("module", moduleName.path());
cmd.callback = [moduleName](const DebuggerResponse &response) {
const GdbMi &symbols = response.data["symbols"];
- QString moduleName = response.data["module"].data();
+ const QString module = response.data["module"].data();
Symbols syms;
for (const GdbMi &item : symbols) {
Symbol symbol;
@@ -676,7 +679,7 @@ void LldbEngine::requestModuleSymbols(const QString &moduleName)
symbol.demangled = item["demangled"].data();
syms.append(symbol);
}
- showModuleSymbols(moduleName, syms);
+ showModuleSymbols(moduleName.withNewPath(module), syms);
};
runCommand(cmd);
}
@@ -698,7 +701,7 @@ void LldbEngine::updateAll()
DebuggerCommand cmd("fetchThreads");
cmd.callback = [this](const DebuggerResponse &response) {
threadsHandler()->setThreads(response.data);
- fetchStack(debuggerSettings()->maximalStackDepth.value());
+ fetchStack(debuggerSettings()->maximalStackDepth());
reloadRegisters();
};
runCommand(cmd);
diff --git a/src/plugins/debugger/lldb/lldbengine.h b/src/plugins/debugger/lldb/lldbengine.h
index 0a0d9adfb5..db11af9c10 100644
--- a/src/plugins/debugger/lldb/lldbengine.h
+++ b/src/plugins/debugger/lldb/lldbengine.h
@@ -11,7 +11,7 @@
#include <debugger/debuggertooltipmanager.h>
#include <debugger/debuggerprotocol.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QPointer>
#include <QProcess>
@@ -68,9 +68,9 @@ private:
void assignValueInDebugger(WatchItem *item, const QString &expr, const QVariant &value) override;
void executeDebuggerCommand(const QString &command) override;
- void loadSymbols(const QString &moduleName) override;
+ void loadSymbols(const Utils::FilePath &moduleName) override;
void loadAllSymbols() override;
- void requestModuleSymbols(const QString &moduleName) override;
+ void requestModuleSymbols(const Utils::FilePath &moduleName) override;
void reloadModules() override;
void reloadRegisters() override;
void reloadSourceFiles() override {}
@@ -113,7 +113,7 @@ private:
QString m_inbuffer;
QString m_scriptFileName;
- Utils::QtcProcess m_lldbProc;
+ Utils::Process m_lldbProc;
// FIXME: Make generic.
int m_lastAgentId = 0;
diff --git a/src/plugins/debugger/loadcoredialog.cpp b/src/plugins/debugger/loadcoredialog.cpp
index 86c9c738dc..4a0fea3f16 100644
--- a/src/plugins/debugger/loadcoredialog.cpp
+++ b/src/plugins/debugger/loadcoredialog.cpp
@@ -11,13 +11,12 @@
#include <projectexplorer/kitchooser.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <utils/asynctask.h>
+#include <utils/async.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
#include <utils/processinterface.h>
#include <utils/progressindicator.h>
#include <utils/qtcassert.h>
-#include <utils/tasktree.h>
#include <utils/temporaryfile.h>
#include <QCheckBox>
@@ -34,6 +33,7 @@
using namespace Core;
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
namespace Debugger::Internal {
@@ -217,8 +217,6 @@ void AttachCoreDialog::accepted()
const DebuggerItem *debuggerItem = Debugger::DebuggerKitAspect::debugger(kit());
const FilePath debuggerCommand = debuggerItem->command();
- using namespace Tasking;
-
const auto copyFile = [debuggerCommand](const FilePath &srcPath) -> expected_str<FilePath> {
if (!srcPath.isSameDevice(debuggerCommand)) {
const expected_str<FilePath> tmpPath = debuggerCommand.tmpDir();
@@ -243,18 +241,18 @@ void AttachCoreDialog::accepted()
using ResultType = expected_str<FilePath>;
- const auto copyFileAsync = [=](QFutureInterface<ResultType> &fi, const FilePath &srcPath) {
- fi.reportResult(copyFile(srcPath));
+ const auto copyFileAsync = [=](QPromise<ResultType> &promise, const FilePath &srcPath) {
+ promise.addResult(copyFile(srcPath));
};
const Group root = {
parallel,
- Async<ResultType>{[=](auto &task) {
- task.setAsyncCallData(copyFileAsync, this->coreFile());
+ AsyncTask<ResultType>{[=](auto &task) {
+ task.setConcurrentCallData(copyFileAsync, this->coreFile());
},
[=](const auto &task) { d->coreFileResult = task.result(); }},
- Async<ResultType>{[=](auto &task) {
- task.setAsyncCallData(copyFileAsync, this->symbolFile());
+ AsyncTask<ResultType>{[=](auto &task) {
+ task.setConcurrentCallData(copyFileAsync, this->symbolFile());
},
[=](const auto &task) { d->symbolFileResult = task.result(); }},
};
diff --git a/src/plugins/debugger/moduleshandler.cpp b/src/plugins/debugger/moduleshandler.cpp
index 3451b9b572..4f063b5728 100644
--- a/src/plugins/debugger/moduleshandler.cpp
+++ b/src/plugins/debugger/moduleshandler.cpp
@@ -11,8 +11,8 @@
#include <utils/basetreeview.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/treemodel.h>
#include <QCoreApplication>
@@ -48,7 +48,7 @@ QVariant ModuleItem::data(int column, int role) const
break;
case 1:
if (role == Qt::DisplayRole)
- return module.modulePath;
+ return module.modulePath.toUserOutput();
if (role == Qt::ToolTipRole) {
QString msg;
if (!module.elfData.buildId.isEmpty())
@@ -140,6 +140,12 @@ public:
DebuggerEngine *engine;
};
+static bool dependsCanBeFound()
+{
+ static bool dependsInPath = Environment::systemEnvironment().searchInPath("depends").isEmpty();
+ return dependsInPath;
+}
+
bool ModulesModel::contextMenuEvent(const ItemViewEvent &ev)
{
ModuleItem *item = itemForIndexAtLevel<1>(ev.sourceModelIndex());
@@ -150,7 +156,7 @@ bool ModulesModel::contextMenuEvent(const ItemViewEvent &ev)
const bool canShowSymbols = engine->hasCapability(ShowModuleSymbolsCapability);
const bool moduleNameValid = item && !item->module.moduleName.isEmpty();
const QString moduleName = item ? item->module.moduleName : QString();
- const QString modulePath = item ? item->module.modulePath : QString();
+ const FilePath modulePath = item ? item->module.modulePath : FilePath();
auto menu = new QMenu;
@@ -163,11 +169,13 @@ bool ModulesModel::contextMenuEvent(const ItemViewEvent &ev)
moduleNameValid && enabled && canReload,
[this, modulePath] { engine->loadSymbols(modulePath); });
- // FIXME: Dependencies only available on Windows, when "depends" is installed.
addAction(this, menu, Tr::tr("Show Dependencies of \"%1\"").arg(moduleName),
Tr::tr("Show Dependencies"),
- moduleNameValid && !moduleName.isEmpty() && HostOsInfo::isWindowsHost(),
- [modulePath] { QtcProcess::startDetached({{"depends"}, {modulePath}}); });
+ moduleNameValid && !modulePath.needsDevice() && modulePath.exists()
+ && dependsCanBeFound(),
+ [modulePath] {
+ Process::startDetached({{"depends"}, {modulePath.toString()}});
+ });
addAction(this, menu, Tr::tr("Load Symbols for All Modules"),
enabled && canLoadSymbols,
@@ -185,7 +193,7 @@ bool ModulesModel::contextMenuEvent(const ItemViewEvent &ev)
addAction(this, menu, Tr::tr("Edit File \"%1\"").arg(moduleName),
Tr::tr("Edit File"),
moduleNameValid,
- [this, modulePath] { engine->gotoLocation(FilePath::fromString(modulePath)); });
+ [this, modulePath] { engine->gotoLocation(modulePath); });
addAction(this, menu, Tr::tr("Show Symbols in File \"%1\"").arg(moduleName),
Tr::tr("Show Symbols"),
@@ -239,7 +247,7 @@ QAbstractItemModel *ModulesHandler::model() const
return m_proxyModel;
}
-ModuleItem *ModulesHandler::moduleFromPath(const QString &modulePath) const
+ModuleItem *ModulesHandler::moduleFromPath(const FilePath &modulePath) const
{
// Recent modules are more likely to be unloaded first.
return m_model->findItemAtLevel<1>([modulePath](ModuleItem *item) {
@@ -259,7 +267,7 @@ const Modules ModulesHandler::modules() const
return mods;
}
-void ModulesHandler::removeModule(const QString &modulePath)
+void ModulesHandler::removeModule(const FilePath &modulePath)
{
if (ModuleItem *item = moduleFromPath(modulePath))
m_model->destroyItem(item);
@@ -267,7 +275,7 @@ void ModulesHandler::removeModule(const QString &modulePath)
void ModulesHandler::updateModule(const Module &module)
{
- const QString path = module.modulePath;
+ const FilePath path = module.modulePath;
if (path.isEmpty())
return;
@@ -281,12 +289,12 @@ void ModulesHandler::updateModule(const Module &module)
}
try { // MinGW occasionallly throws std::bad_alloc.
- ElfReader reader(FilePath::fromUserInput(path));
+ ElfReader reader(path);
item->module.elfData = reader.readHeaders();
item->update();
} catch(...) {
qWarning("%s: An exception occurred while reading module '%s'",
- Q_FUNC_INFO, qPrintable(module.modulePath));
+ Q_FUNC_INFO, qPrintable(module.modulePath.toUserOutput()));
}
item->updated = true;
}
diff --git a/src/plugins/debugger/moduleshandler.h b/src/plugins/debugger/moduleshandler.h
index 082a66f2ca..03434edab2 100644
--- a/src/plugins/debugger/moduleshandler.h
+++ b/src/plugins/debugger/moduleshandler.h
@@ -70,7 +70,7 @@ public:
ReadOk // Dwarf index available.
};
QString moduleName;
- QString modulePath;
+ Utils::FilePath modulePath;
QString hostPath;
SymbolReadState symbolsRead = UnknownReadState;
quint64 startAddress = 0;
@@ -99,7 +99,7 @@ public:
QAbstractItemModel *model() const;
- void removeModule(const QString &modulePath);
+ void removeModule(const Utils::FilePath &modulePath);
void updateModule(const Module &module);
void beginUpdateAll();
@@ -109,7 +109,7 @@ public:
const Modules modules() const;
private:
- ModuleItem *moduleFromPath(const QString &modulePath) const;
+ ModuleItem *moduleFromPath(const Utils::FilePath &modulePath) const;
ModulesModel *m_model;
QSortFilterProxyModel *m_proxyModel;
diff --git a/src/plugins/debugger/pdb/pdbengine.cpp b/src/plugins/debugger/pdb/pdbengine.cpp
index fd749a2d0c..accf32d0e6 100644
--- a/src/plugins/debugger/pdb/pdbengine.cpp
+++ b/src/plugins/debugger/pdb/pdbengine.cpp
@@ -22,8 +22,8 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <coreplugin/idocument.h>
#include <coreplugin/icore.h>
@@ -99,10 +99,10 @@ void PdbEngine::setupEngine()
m_interpreter = runParameters().interpreter;
QString bridge = ICore::resourcePath("debugger/pdbbridge.py").toString();
- connect(&m_proc, &QtcProcess::started, this, &PdbEngine::handlePdbStarted);
- connect(&m_proc, &QtcProcess::done, this, &PdbEngine::handlePdbDone);
- connect(&m_proc, &QtcProcess::readyReadStandardOutput, this, &PdbEngine::readPdbStandardOutput);
- connect(&m_proc, &QtcProcess::readyReadStandardError, this, &PdbEngine::readPdbStandardError);
+ connect(&m_proc, &Process::started, this, &PdbEngine::handlePdbStarted);
+ connect(&m_proc, &Process::done, this, &PdbEngine::handlePdbDone);
+ connect(&m_proc, &Process::readyReadStandardOutput, this, &PdbEngine::readPdbStandardOutput);
+ connect(&m_proc, &Process::readyReadStandardError, this, &PdbEngine::readPdbStandardError);
const FilePath scriptFile = runParameters().mainScript;
if (!scriptFile.isReadableFile()) {
@@ -263,7 +263,7 @@ void PdbEngine::removeBreakpoint(const Breakpoint &bp)
notifyBreakpointRemoveOk(bp);
}
-void PdbEngine::loadSymbols(const QString &moduleName)
+void PdbEngine::loadSymbols(const FilePath &moduleName)
{
Q_UNUSED(moduleName)
}
@@ -294,16 +294,16 @@ void PdbEngine::refreshModules(const GdbMi &modules)
&& path.endsWith("' (built-in)>")) {
path = "(builtin)";
}
- module.modulePath = path;
+ module.modulePath = FilePath::fromString(path);
handler->updateModule(module);
}
handler->endUpdateAll();
}
-void PdbEngine::requestModuleSymbols(const QString &moduleName)
+void PdbEngine::requestModuleSymbols(const FilePath &moduleName)
{
DebuggerCommand cmd("listSymbols");
- cmd.arg("module", moduleName);
+ cmd.arg("module", moduleName.path());
runCommand(cmd);
}
@@ -341,7 +341,7 @@ void PdbEngine::refreshSymbols(const GdbMi &symbols)
symbol.name = item["name"].data();
syms.append(symbol);
}
- showModuleSymbols(moduleName, syms);
+ showModuleSymbols(runParameters().inferior.command.executable().withNewPath(moduleName), syms);
}
bool PdbEngine::canHandleToolTip(const DebuggerToolTipContext &) const
diff --git a/src/plugins/debugger/pdb/pdbengine.h b/src/plugins/debugger/pdb/pdbengine.h
index d65f0e33b1..1e365b36a7 100644
--- a/src/plugins/debugger/pdb/pdbengine.h
+++ b/src/plugins/debugger/pdb/pdbengine.h
@@ -4,7 +4,7 @@
#pragma once
#include <debugger/debuggerengine.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QVariant>
@@ -52,9 +52,9 @@ private:
const QString &expr, const QVariant &value) override;
void executeDebuggerCommand(const QString &command) override;
- void loadSymbols(const QString &moduleName) override;
+ void loadSymbols(const Utils::FilePath &moduleName) override;
void loadAllSymbols() override;
- void requestModuleSymbols(const QString &moduleName) override;
+ void requestModuleSymbols(const Utils::FilePath &moduleName) override;
void reloadModules() override;
void reloadRegisters() override {}
void reloadSourceFiles() override {}
@@ -87,7 +87,7 @@ private:
void updateLocals() override;
QString m_inbuffer;
- Utils::QtcProcess m_proc;
+ Utils::Process m_proc;
Utils::FilePath m_interpreter;
};
diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp
index 204d6c4437..033069c116 100644
--- a/src/plugins/debugger/qml/qmlengine.cpp
+++ b/src/plugins/debugger/qml/qmlengine.cpp
@@ -40,8 +40,8 @@
#include <utils/basetreeview.h>
#include <utils/fileinprojectfinder.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/treemodel.h>
#include <QDebug>
@@ -200,7 +200,7 @@ public:
QHash<QString, QTextDocument*> sourceDocuments;
InteractiveInterpreter interpreter;
- QtcProcess process;
+ Process process;
QmlInspectorAgent inspectorAgent;
QList<quint32> queryIds;
@@ -249,17 +249,17 @@ QmlEngine::QmlEngine()
connect(stackHandler(), &StackHandler::currentIndexChanged,
this, &QmlEngine::updateCurrentContext);
- connect(&d->process, &QtcProcess::readyReadStandardOutput, this, [this] {
+ connect(&d->process, &Process::readyReadStandardOutput, this, [this] {
// FIXME: Redirect to RunControl
showMessage(d->process.readAllStandardOutput(), AppOutput);
});
- connect(&d->process, &QtcProcess::readyReadStandardError, this, [this] {
+ connect(&d->process, &Process::readyReadStandardError, this, [this] {
// FIXME: Redirect to RunControl
showMessage(d->process.readAllStandardError(), AppOutput);
});
- connect(&d->process, &QtcProcess::done, this, &QmlEngine::disconnected);
- connect(&d->process, &QtcProcess::started, this, &QmlEngine::handleLauncherStarted);
+ connect(&d->process, &Process::done, this, &QmlEngine::disconnected);
+ connect(&d->process, &Process::started, this, &QmlEngine::handleLauncherStarted);
debuggerConsole()->populateFileFinder();
debuggerConsole()->setScriptEvaluator([this](const QString &expr) {
@@ -736,7 +736,7 @@ bool QmlEngine::acceptsBreakpoint(const BreakpointParameters &bp) const
return bp.isQmlFileAndLineBreakpoint();
}
-void QmlEngine::loadSymbols(const QString &moduleName)
+void QmlEngine::loadSymbols(const FilePath &moduleName)
{
Q_UNUSED(moduleName)
}
@@ -759,7 +759,7 @@ void QmlEngine::updateAll()
d->updateLocals();
}
-void QmlEngine::requestModuleSymbols(const QString &moduleName)
+void QmlEngine::requestModuleSymbols(const FilePath &moduleName)
{
Q_UNUSED(moduleName)
}
@@ -1802,10 +1802,10 @@ void QmlEnginePrivate::messageReceived(const QByteArray &data)
updateScriptSource(name, lineOffset, columnOffset, source);
}
- QMap<QString,QString> files;
+ QMap<QString, FilePath> files;
for (const QString &file : std::as_const(sourceFiles)) {
QString shortName = file;
- QString fullName = engine->toFileInProject(file);
+ FilePath fullName = engine->toFileInProject(file);
files.insert(shortName, fullName);
}
@@ -1915,7 +1915,7 @@ void QmlEnginePrivate::messageReceived(const QByteArray &data)
const QVariantMap script = body.value("script").toMap();
QUrl fileUrl(script.value(NAME).toString());
- QString filePath = engine->toFileInProject(fileUrl);
+ FilePath filePath = engine->toFileInProject(fileUrl);
const QVariantMap exception = body.value("exception").toMap();
QString errorMessage = exception.value("text").toString();
@@ -2045,8 +2045,7 @@ StackFrame QmlEnginePrivate::extractStackFrame(const QVariant &bodyVal)
stackFrame.function = extractString(body.value("func"));
if (stackFrame.function.isEmpty())
stackFrame.function = Tr::tr("Anonymous Function");
- stackFrame.file = FilePath::fromString(
- engine->toFileInProject(extractString(body.value("script"))));
+ stackFrame.file = engine->toFileInProject(extractString(body.value("script")));
stackFrame.usable = stackFrame.file.isReadableFile();
stackFrame.receiver = extractString(body.value("receiver"));
stackFrame.line = body.value("line").toInt() + 1;
@@ -2321,7 +2320,6 @@ void QmlEnginePrivate::insertSubItems(WatchItem *parent, const QVariantList &pro
QTC_ASSERT(parent, return);
LookupItems itemsToLookup;
- const QSet<QString> expandedINames = engine->watchHandler()->expandedINames();
for (const QVariant &property : properties) {
QmlV8ObjectData propertyData = extractData(property);
std::unique_ptr<WatchItem> item(new WatchItem);
@@ -2343,7 +2341,7 @@ void QmlEnginePrivate::insertSubItems(WatchItem *parent, const QVariantList &pro
item->id = propertyData.handle;
item->type = propertyData.type;
item->value = propertyData.value.toString();
- if (item->type.isEmpty() || expandedINames.contains(item->iname))
+ if (item->type.isEmpty() || engine->watchHandler()->isExpandedIName(item->iname))
itemsToLookup.insert(propertyData.handle, {item->iname, item->name, item->exp});
setWatchItemHasChildren(item.get(), propertyData.hasChildren());
parent->appendChild(item.release());
@@ -2445,7 +2443,7 @@ void QmlEnginePrivate::flushSendBuffer()
sendBuffer.clear();
}
-QString QmlEngine::toFileInProject(const QUrl &fileUrl)
+FilePath QmlEngine::toFileInProject(const QUrl &fileUrl)
{
// make sure file finder is properly initialized
const DebuggerRunParameters &rp = runParameters();
@@ -2454,7 +2452,7 @@ QString QmlEngine::toFileInProject(const QUrl &fileUrl)
d->fileFinder.setAdditionalSearchDirectories(rp.additionalSearchDirectories);
d->fileFinder.setSysroot(rp.sysRoot);
- return d->fileFinder.findFile(fileUrl).constFirst().toString();
+ return d->fileFinder.findFile(fileUrl).constFirst();
}
DebuggerEngine *createQmlEngine()
diff --git a/src/plugins/debugger/qml/qmlengine.h b/src/plugins/debugger/qml/qmlengine.h
index 331176d426..2006ce081f 100644
--- a/src/plugins/debugger/qml/qmlengine.h
+++ b/src/plugins/debugger/qml/qmlengine.h
@@ -25,7 +25,7 @@ public:
void logServiceActivity(const QString &service, const QString &logMessage);
void expressionEvaluated(quint32 queryId, const QVariant &result);
- QString toFileInProject(const QUrl &fileUrl);
+ Utils::FilePath toFileInProject(const QUrl &fileUrl);
private:
void disconnected();
@@ -75,9 +75,9 @@ private:
void assignValueInDebugger(WatchItem *item,
const QString &expr, const QVariant &value) override;
- void loadSymbols(const QString &moduleName) override;
+ void loadSymbols(const Utils::FilePath &moduleName) override;
void loadAllSymbols() override;
- void requestModuleSymbols(const QString &moduleName) override;
+ void requestModuleSymbols(const Utils::FilePath &moduleName) override;
void reloadModules() override;
void reloadRegisters() override {}
void reloadSourceFiles() override;
diff --git a/src/plugins/debugger/qml/qmlengineutils.cpp b/src/plugins/debugger/qml/qmlengineutils.cpp
index 7e304d3c92..80e0ac98f1 100644
--- a/src/plugins/debugger/qml/qmlengineutils.cpp
+++ b/src/plugins/debugger/qml/qmlengineutils.cpp
@@ -20,6 +20,7 @@ using namespace QmlDebug;
using namespace QmlJS;
using namespace QmlJS::AST;
using namespace TextEditor;
+using namespace Utils;
namespace Debugger::Internal {
@@ -218,11 +219,10 @@ void clearExceptionSelection()
}
}
-QStringList highlightExceptionCode(int lineNumber, const QString &filePath, const QString &errorMessage)
+QStringList highlightExceptionCode(int lineNumber, const FilePath &filePath, const QString &errorMessage)
{
QStringList messages;
- const QList<IEditor *> editors = DocumentModel::editorsForFilePath(
- Utils::FilePath::fromString(filePath));
+ const QList<IEditor *> editors = DocumentModel::editorsForFilePath(filePath);
const TextEditor::FontSettings &fontSettings = TextEditor::TextEditorSettings::fontSettings();
QTextCharFormat errorFormat = fontSettings.toTextCharFormat(TextEditor::C_ERROR);
@@ -251,7 +251,7 @@ QStringList highlightExceptionCode(int lineNumber, const QString &filePath, cons
selections.append(sel);
ed->setExtraSelections(TextEditorWidget::DebuggerExceptionSelection, selections);
- messages.append(QString::fromLatin1("%1: %2: %3").arg(filePath).arg(lineNumber).arg(errorMessage));
+ messages.append(QString::fromLatin1("%1: %2: %3").arg(filePath.toUserOutput()).arg(lineNumber).arg(errorMessage));
}
return messages;
}
diff --git a/src/plugins/debugger/qml/qmlengineutils.h b/src/plugins/debugger/qml/qmlengineutils.h
index 7cf55067b6..325c8f28e9 100644
--- a/src/plugins/debugger/qml/qmlengineutils.h
+++ b/src/plugins/debugger/qml/qmlengineutils.h
@@ -6,11 +6,13 @@
#include <qmldebug/qdebugmessageclient.h>
#include <qmldebug/qmloutputparser.h>
+namespace Utils { class FilePath; }
+
namespace Debugger::Internal {
void appendDebugOutput(QtMsgType type, const QString &message, const QmlDebug::QDebugContextInfo &info);
void clearExceptionSelection();
-QStringList highlightExceptionCode(int lineNumber, const QString &filePath, const QString &errorMessage);
+QStringList highlightExceptionCode(int lineNumber, const Utils::FilePath &filePath, const QString &errorMessage);
} // Debugger::Internal
diff --git a/src/plugins/debugger/qml/qmlinspectoragent.cpp b/src/plugins/debugger/qml/qmlinspectoragent.cpp
index 983e38adc0..4b632c0f0d 100644
--- a/src/plugins/debugger/qml/qmlinspectoragent.cpp
+++ b/src/plugins/debugger/qml/qmlinspectoragent.cpp
@@ -32,6 +32,7 @@
using namespace QmlDebug;
using namespace QmlDebug::Constants;
+using namespace Utils;
namespace Debugger::Internal {
@@ -541,8 +542,8 @@ void QmlInspectorAgent::buildDebugIdHashRecursive(const ObjectReference &ref)
lineNum += match.captured(3).toInt() - 1;
}
- const QString filePath = m_qmlEngine->toFileInProject(fileUrl);
- m_debugIdLocations.insert(ref.debugId(), FileReference(filePath, lineNum, colNum));
+ const FilePath filePath = m_qmlEngine->toFileInProject(fileUrl);
+ m_debugIdLocations.insert(ref.debugId(), FileReference(filePath.toFSPathString(), lineNum, colNum));
const auto children = ref.children();
for (const ObjectReference &it : children)
@@ -735,7 +736,7 @@ void QmlInspectorAgent::onShowAppOnTopChanged(bool checked)
void QmlInspectorAgent::jumpToObjectDefinitionInEditor(const FileReference &objSource)
{
- const auto filePath = Utils::FilePath::fromString(m_qmlEngine->toFileInProject(objSource.url()));
+ const FilePath filePath = m_qmlEngine->toFileInProject(objSource.url());
Core::EditorManager::openEditorAt({filePath, objSource.lineNumber()});
}
diff --git a/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp b/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp
index eb17cba3de..9f3824ffce 100644
--- a/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp
+++ b/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp
@@ -9,13 +9,13 @@
#include <coreplugin/icore.h>
#include <coreplugin/messagebox.h>
-#include <utils/checkablemessagebox.h>
#include <utils/pathchooser.h>
#include <utils/temporarydirectory.h>
#include <QAction>
#include <QCheckBox>
#include <QDebug>
+#include <QDialogButtonBox>
#include <QFormLayout>
#include <QLabel>
#include <QPushButton>
diff --git a/src/plugins/debugger/sourcefileshandler.cpp b/src/plugins/debugger/sourcefileshandler.cpp
index 412f550482..bec0305417 100644
--- a/src/plugins/debugger/sourcefileshandler.cpp
+++ b/src/plugins/debugger/sourcefileshandler.cpp
@@ -55,8 +55,8 @@ Qt::ItemFlags SourceFilesHandler::flags(const QModelIndex &index) const
{
if (index.row() >= m_fullNames.size())
return {};
- QFileInfo fi(m_fullNames.at(index.row()));
- return fi.isReadable() ? QAbstractItemModel::flags(index) : Qt::ItemFlags({});
+ FilePath filePath = m_fullNames.at(index.row());
+ return filePath.isReadableFile() ? QAbstractItemModel::flags(index) : Qt::ItemFlags({});
}
QVariant SourceFilesHandler::data(const QModelIndex &index, int role) const
@@ -75,7 +75,7 @@ QVariant SourceFilesHandler::data(const QModelIndex &index, int role) const
break;
case 1:
if (role == Qt::DisplayRole)
- return m_fullNames.at(row);
+ return m_fullNames.at(row).toUserOutput();
//if (role == Qt::DecorationRole)
// return module.symbolsRead ? icon2 : icon;
break;
@@ -123,13 +123,13 @@ bool SourceFilesHandler::setData(const QModelIndex &idx, const QVariant &data, i
return false;
}
-void SourceFilesHandler::setSourceFiles(const QMap<QString, QString> &sourceFiles)
+void SourceFilesHandler::setSourceFiles(const QMap<QString, FilePath> &sourceFiles)
{
beginResetModel();
m_shortNames.clear();
m_fullNames.clear();
- QMap<QString, QString>::ConstIterator it = sourceFiles.begin();
- QMap<QString, QString>::ConstIterator et = sourceFiles.end();
+ auto it = sourceFiles.begin();
+ const auto et = sourceFiles.end();
for (; it != et; ++it) {
m_shortNames.append(it.key());
m_fullNames.append(it.value());
@@ -139,7 +139,7 @@ void SourceFilesHandler::setSourceFiles(const QMap<QString, QString> &sourceFile
void SourceFilesHandler::removeAll()
{
- setSourceFiles(QMap<QString, QString>());
+ setSourceFiles({});
//header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
}
diff --git a/src/plugins/debugger/sourcefileshandler.h b/src/plugins/debugger/sourcefileshandler.h
index dabbdbdad0..d714eda2e3 100644
--- a/src/plugins/debugger/sourcefileshandler.h
+++ b/src/plugins/debugger/sourcefileshandler.h
@@ -3,6 +3,8 @@
#pragma once
+#include <utils/filepath.h>
+
#include <QAbstractItemModel>
#include <QStringList>
@@ -29,7 +31,7 @@ public:
void clearModel();
- void setSourceFiles(const QMap<QString, QString> &sourceFiles);
+ void setSourceFiles(const QMap<QString, Utils::FilePath> &sourceFiles);
void removeAll();
QAbstractItemModel *model() { return m_proxyModel; }
@@ -37,7 +39,7 @@ public:
private:
DebuggerEngine *m_engine;
QStringList m_shortNames;
- QStringList m_fullNames;
+ Utils::FilePaths m_fullNames;
QAbstractItemModel *m_proxyModel;
};
diff --git a/src/plugins/debugger/stackframe.cpp b/src/plugins/debugger/stackframe.cpp
index 7008900aab..7ba504d5ca 100644
--- a/src/plugins/debugger/stackframe.cpp
+++ b/src/plugins/debugger/stackframe.cpp
@@ -74,7 +74,7 @@ StackFrame StackFrame::parseFrame(const GdbMi &frameMi, const DebuggerRunParamet
frame.function = frameMi["function"].data();
frame.module = frameMi["module"].data();
const FilePath debugger = rp.debugger.command.executable();
- const FilePath onDevicePath = FilePath::fromUserInput(frameMi["file"].data()).onDevice(debugger);
+ const FilePath onDevicePath = debugger.withNewPath(frameMi["file"].data()).cleanPath();
frame.file = onDevicePath.localSource().value_or(onDevicePath);
frame.line = frameMi["line"].toInt();
frame.address = frameMi["address"].toAddress();
diff --git a/src/plugins/debugger/stackhandler.cpp b/src/plugins/debugger/stackhandler.cpp
index 6a2390fcad..1fc6f04582 100644
--- a/src/plugins/debugger/stackhandler.cpp
+++ b/src/plugins/debugger/stackhandler.cpp
@@ -234,7 +234,7 @@ void StackHandler::setFramesAndCurrentIndex(const GdbMi &frames, bool isFull)
targetFrame = i;
}
- bool canExpand = !isFull && (n >= debuggerSettings()->maximalStackDepth.value());
+ bool canExpand = !isFull && (n >= debuggerSettings()->maximalStackDepth());
debuggerSettings()->expandStack.setEnabled(canExpand);
setFrames(stackFrames, canExpand);
diff --git a/src/plugins/debugger/terminal.cpp b/src/plugins/debugger/terminal.cpp
index 6707137e85..ac8a043df8 100644
--- a/src/plugins/debugger/terminal.cpp
+++ b/src/plugins/debugger/terminal.cpp
@@ -11,8 +11,8 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDebug>
#include <QIODevice>
@@ -38,12 +38,6 @@ using namespace Utils;
namespace Debugger::Internal {
-static QString currentError()
-{
- int err = errno;
- return QString::fromLatin1(strerror(err));
-}
-
Terminal::Terminal(QObject *parent)
: QObject(parent)
{
@@ -52,6 +46,10 @@ Terminal::Terminal(QObject *parent)
void Terminal::setup()
{
#ifdef DEBUGGER_USE_TERMINAL
+ const auto currentError = [] {
+ int err = errno;
+ return QString::fromLatin1(strerror(err));
+ };
if (!qtcEnvironmentVariableIsSet("QTC_USE_PTY"))
return;
@@ -174,13 +172,12 @@ void TerminalRunner::start()
QTC_ASSERT(!m_stubProc, reportFailure({}); return);
Runnable stub = m_stubRunnable();
- m_stubProc = new QtcProcess(this);
- m_stubProc->setTerminalMode(HostOsInfo::isWindowsHost()
- ? TerminalMode::Suspend : TerminalMode::Debug);
+ m_stubProc = new Process(this);
+ m_stubProc->setTerminalMode(TerminalMode::Debug);
- connect(m_stubProc, &QtcProcess::started,
+ connect(m_stubProc, &Process::started,
this, &TerminalRunner::stubStarted);
- connect(m_stubProc, &QtcProcess::done,
+ connect(m_stubProc, &Process::done,
this, &TerminalRunner::stubDone);
m_stubProc->setEnvironment(stub.environment);
diff --git a/src/plugins/debugger/terminal.h b/src/plugins/debugger/terminal.h
index b4fa1317c5..cd57e9eea2 100644
--- a/src/plugins/debugger/terminal.h
+++ b/src/plugins/debugger/terminal.h
@@ -8,7 +8,7 @@
#include <projectexplorer/runcontrol.h>
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace Debugger {
@@ -65,7 +65,7 @@ private:
void stubStarted();
void stubDone();
- Utils::QtcProcess *m_stubProc = nullptr;
+ Utils::Process *m_stubProc = nullptr;
std::function<ProjectExplorer::Runnable()> m_stubRunnable;
qint64 m_applicationPid = 0;
qint64 m_applicationMainThreadId = 0;
diff --git a/src/plugins/debugger/uvsc/uvscengine.cpp b/src/plugins/debugger/uvsc/uvscengine.cpp
index 2bb0bef8e7..06450651de 100644
--- a/src/plugins/debugger/uvsc/uvscengine.cpp
+++ b/src/plugins/debugger/uvsc/uvscengine.cpp
@@ -600,7 +600,7 @@ void UvscEngine::handleProjectClosed()
Module module;
module.startAddress = 0;
module.endAddress = 0;
- module.modulePath = rp.inferior.command.executable().toString();
+ module.modulePath = rp.inferior.command.executable();
module.moduleName = "<executable>";
modulesHandler()->updateModule(module);
diff --git a/src/plugins/debugger/watchdata.cpp b/src/plugins/debugger/watchdata.cpp
index 329e58b88b..8d0af0e4d3 100644
--- a/src/plugins/debugger/watchdata.cpp
+++ b/src/plugins/debugger/watchdata.cpp
@@ -216,6 +216,13 @@ public:
child->valueEditable = true;
item->appendChild(child);
}
+ if (childrenElided) {
+ auto child = new WatchItem;
+ child->name = WatchItem::loadMoreName;
+ child->iname = item->iname + "." + WatchItem::loadMoreName;
+ child->wantsChildren = true;
+ item->appendChild(child);
+ }
}
void decode()
@@ -264,6 +271,7 @@ public:
QString rawData;
QString childType;
DebuggerEncoding encoding;
+ int childrenElided;
quint64 addrbase;
quint64 addrstep;
Endian endian;
@@ -379,6 +387,7 @@ void WatchItem::parseHelper(const GdbMi &input, bool maySort)
decoder.item = this;
decoder.rawData = mi.data();
decoder.childType = input["childtype"].data();
+ decoder.childrenElided = input["childrenelided"].toInt();
decoder.addrbase = input["addrbase"].toAddress();
decoder.addrstep = input["addrstep"].toAddress();
decoder.endian = input["endian"].data() == ">" ? Endian::Big : Endian::Little;
@@ -504,6 +513,11 @@ QString WatchItem::toToolTip() const
return res;
}
+bool WatchItem::isLoadMore() const
+{
+ return name == loadMoreName;
+}
+
bool WatchItem::isLocal() const
{
if (arrayIndex >= 0)
diff --git a/src/plugins/debugger/watchdata.h b/src/plugins/debugger/watchdata.h
index 4ef57eacf1..42d046f2a1 100644
--- a/src/plugins/debugger/watchdata.h
+++ b/src/plugins/debugger/watchdata.h
@@ -36,8 +36,10 @@ public:
int editType() const;
static const qint64 InvalidId = -1;
+ constexpr static char loadMoreName[] = "<load more>";
void setHasChildren(bool c) { wantsChildren = c; }
+ bool isLoadMore() const;
bool isValid() const { return !iname.isEmpty(); }
bool isVTablePointer() const;
diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp
index 17ce07bee0..5324c7a8dc 100644
--- a/src/plugins/debugger/watchhandler.cpp
+++ b/src/plugins/debugger/watchhandler.cpp
@@ -21,11 +21,10 @@
#include "watchdelegatewidgets.h"
#include "watchutils.h"
-#include <coreplugin/icore.h>
#include <coreplugin/helpmanager.h>
+#include <coreplugin/icore.h>
#include <coreplugin/messagebox.h>
-
-#include <projectexplorer/session.h>
+#include <coreplugin/session.h>
#include <texteditor/syntaxhighlighter.h>
@@ -40,6 +39,7 @@
#include <QApplication>
#include <QDebug>
+#include <QDialogButtonBox>
#include <QFile>
#include <QFloat16>
#include <QItemDelegate>
@@ -52,8 +52,8 @@
#include <QPainter>
#include <QSet>
#include <QStringDecoder>
-#include <QTableWidget>
#include <QTabWidget>
+#include <QTableWidget>
#include <QTextEdit>
#include <QTimer>
#include <QToolTip>
@@ -410,6 +410,7 @@ public:
bool hasChildren(const QModelIndex &idx) const override;
bool canFetchMore(const QModelIndex &idx) const override;
void fetchMore(const QModelIndex &idx) override;
+ void expand(WatchItem *item, bool requestEngineUpdate);
QString displayForAutoTest(const QByteArray &iname) const;
void reinitialize(bool includeInspectData = false);
@@ -468,6 +469,7 @@ public:
SeparatedView *m_separatedView; // Not owned.
QSet<QString> m_expandedINames;
+ QHash<QString, int> m_maxArrayCount;
QTimer m_requestUpdateTimer;
QTimer m_localsWindowsTimer;
@@ -1226,7 +1228,8 @@ bool WatchModel::setData(const QModelIndex &idx, const QVariant &value, int role
if (value.toBool()) {
// Should already have been triggered by fetchMore()
//QTC_CHECK(m_expandedINames.contains(item->iname));
- m_expandedINames.insert(item->iname);
+ if (!item->isLoadMore())
+ m_expandedINames.insert(item->iname);
} else {
m_expandedINames.remove(item->iname);
}
@@ -1336,13 +1339,23 @@ bool WatchModel::canFetchMore(const QModelIndex &idx) const
void WatchModel::fetchMore(const QModelIndex &idx)
{
- if (!idx.isValid())
- return;
+ if (idx.isValid())
+ expand(nonRootItemForIndex(idx), true);
+}
- WatchItem *item = nonRootItemForIndex(idx);
- if (item) {
+void WatchModel::expand(WatchItem *item, bool requestEngineUpdate)
+{
+ if (!item)
+ return;
+ if (item->isLoadMore()) {
+ item = item->parent();
+ m_maxArrayCount[item->iname]
+ = m_maxArrayCount.value(item->iname, debuggerSettings()->defaultArraySize.value()) * 10;
+ if (requestEngineUpdate)
+ m_engine->updateItem(item->iname);
+ } else {
m_expandedINames.insert(item->iname);
- if (item->childCount() == 0)
+ if (requestEngineUpdate && item->childCount() == 0)
m_engine->expandItem(item->iname);
}
}
@@ -1765,10 +1778,14 @@ bool WatchModel::contextMenuEvent(const ItemViewEvent &ev)
menu->addSeparator();
addAction(this, menu, Tr::tr("Expand All Children"), item, [this, name = item ? item->iname : QString()] {
- m_expandedINames.insert(name);
- if (auto item = findItem(name)) {
- item->forFirstLevelChildren(
- [this](WatchItem *child) { m_expandedINames.insert(child->iname); });
+ if (name.isEmpty())
+ return;
+ if (WatchItem *item = findItem(name)) {
+ expand(item, false);
+ item->forFirstLevelChildren([this](WatchItem *child) {
+ if (!child->isLoadMore())
+ expand(child, false);
+ });
m_engine->updateLocals();
}
});
@@ -2226,7 +2243,7 @@ bool WatchHandler::insertItem(WatchItem *item)
void WatchModel::reexpandItems()
{
- for (const QString &iname : std::as_const(m_expandedINames)) {
+ for (const QString &iname: m_expandedINames) {
if (WatchItem *item = findItem(iname)) {
emit itemIsExpanded(indexForItem(item));
emit inameIsExpanded(iname);
@@ -2308,7 +2325,8 @@ void WatchHandler::notifyUpdateFinished()
m_model->destroyItem(item);
m_model->forAllItems([this](WatchItem *item) {
- if (item->wantsChildren && isExpandedIName(item->iname)) {
+ if (item->wantsChildren && isExpandedIName(item->iname)
+ && item->name != WatchItem::loadMoreName) {
// m_model->m_engine->showMessage(QString("ADJUSTING CHILD EXPECTATION FOR " + item->iname));
item->wantsChildren = false;
}
@@ -2553,11 +2571,12 @@ void WatchModel::clearWatches()
if (theWatcherNames.isEmpty())
return;
- const QDialogButtonBox::StandardButton ret = CheckableMessageBox::doNotAskAgainQuestion(
- ICore::dialogParent(), Tr::tr("Remove All Expression Evaluators"),
- Tr::tr("Are you sure you want to remove all expression evaluators?"),
- ICore::settings(), "RemoveAllWatchers");
- if (ret != QDialogButtonBox::Yes)
+ const QMessageBox::StandardButton ret = CheckableMessageBox::question(
+ ICore::dialogParent(),
+ Tr::tr("Remove All Expression Evaluators"),
+ Tr::tr("Are you sure you want to remove all expression evaluators?"),
+ QString("RemoveAllWatchers"));
+ if (ret != QMessageBox::Yes)
return;
m_watchRoot->removeChildren();
@@ -2607,11 +2626,8 @@ const WatchItem *WatchHandler::watchItem(const QModelIndex &idx) const
void WatchHandler::fetchMore(const QString &iname) const
{
- if (WatchItem *item = m_model->findItem(iname)) {
- m_model->m_expandedINames.insert(iname);
- if (item->childCount() == 0)
- m_model->m_engine->expandItem(iname);
- }
+ if (WatchItem *item = m_model->findItem(iname))
+ m_model->expand(item, true);
}
WatchItem *WatchHandler::findItem(const QString &iname) const
@@ -2725,9 +2741,9 @@ QString WatchHandler::individualFormatRequests() const
void WatchHandler::appendFormatRequests(DebuggerCommand *cmd) const
{
- QJsonArray expanded;
- for (const QString &name : std::as_const(m_model->m_expandedINames))
- expanded.append(name);
+ QJsonObject expanded;
+ for (const QString &iname : std::as_const(m_model->m_expandedINames))
+ expanded.insert(iname, maxArrayCount(iname));
cmd->arg("expanded", expanded);
@@ -2831,6 +2847,11 @@ QSet<QString> WatchHandler::expandedINames() const
return m_model->m_expandedINames;
}
+int WatchHandler::maxArrayCount(const QString &iname) const
+{
+ return m_model->m_maxArrayCount.value(iname, debuggerSettings()->defaultArraySize());
+}
+
void WatchHandler::recordTypeInfo(const GdbMi &typeInfo)
{
if (typeInfo.type() == GdbMi::List) {
diff --git a/src/plugins/debugger/watchhandler.h b/src/plugins/debugger/watchhandler.h
index 0683d73ce9..58a64cea4a 100644
--- a/src/plugins/debugger/watchhandler.h
+++ b/src/plugins/debugger/watchhandler.h
@@ -60,6 +60,7 @@ public:
bool isExpandedIName(const QString &iname) const;
QSet<QString> expandedINames() const;
+ int maxArrayCount(const QString &iname) const;
static QStringList watchedExpressions();
static QMap<QString, int> watcherNames();
diff --git a/src/plugins/designer/CMakeLists.txt b/src/plugins/designer/CMakeLists.txt
index f6304e4a67..18b1cf9769 100644
--- a/src/plugins/designer/CMakeLists.txt
+++ b/src/plugins/designer/CMakeLists.txt
@@ -22,7 +22,7 @@ add_qtc_plugin(Designer
formeditorfactory.cpp formeditorfactory.h
formeditorplugin.cpp formeditorplugin.h
formeditorstack.cpp formeditorstack.h
- formeditorw.cpp formeditorw.h
+ formeditor.cpp formeditor.h
formtemplatewizardpage.cpp formtemplatewizardpage.h
formwindoweditor.cpp formwindoweditor.h
formwindowfile.cpp formwindowfile.h
diff --git a/src/plugins/designer/codemodelhelpers.cpp b/src/plugins/designer/codemodelhelpers.cpp
index beddb3714e..8428fd9817 100644
--- a/src/plugins/designer/codemodelhelpers.cpp
+++ b/src/plugins/designer/codemodelhelpers.cpp
@@ -9,7 +9,7 @@
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <QCoreApplication>
@@ -30,7 +30,7 @@ static const char setupUiC[] = "setupUi";
// Find the generated "ui_form.h" header of the form via project.
static FilePath generatedHeaderOf(const FilePath &uiFileName)
{
- if (const Project *uiProject = SessionManager::projectForFile(uiFileName)) {
+ if (const Project *uiProject = ProjectManager::projectForFile(uiFileName)) {
if (Target *t = uiProject->activeTarget()) {
if (BuildSystem *bs = t->buildSystem()) {
FilePaths files = bs->filesGeneratedFrom(uiFileName);
diff --git a/src/plugins/designer/cpp/newclasswidget.cpp b/src/plugins/designer/cpp/newclasswidget.cpp
index b659854777..924700b59c 100644
--- a/src/plugins/designer/cpp/newclasswidget.cpp
+++ b/src/plugins/designer/cpp/newclasswidget.cpp
@@ -66,14 +66,15 @@ NewClassWidget::NewClassWidget(QWidget *parent) :
setNamesDelimiter(QLatin1String("::"));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Form {
Tr::tr("&Class name:"), d->m_classLineEdit, br,
Tr::tr("&Header file:"), d->m_headerFileLineEdit, br,
Tr::tr("&Source file:"), d->m_sourceFileLineEdit, br,
Tr::tr("&Form file:"), d->m_formFileLineEdit, br,
Tr::tr("&Path:"), d->m_pathChooser, br,
- }.attachTo(this, WithoutMargins);
+ noMargin
+ }.attachTo(this);
connect(d->m_classLineEdit, &ClassNameValidatingLineEdit::updateFileName,
this, &NewClassWidget::slotUpdateFileNames);
diff --git a/src/plugins/designer/designer.qbs b/src/plugins/designer/designer.qbs
index 411be43283..8b97a494a7 100644
--- a/src/plugins/designer/designer.qbs
+++ b/src/plugins/designer/designer.qbs
@@ -41,7 +41,7 @@ QtcPlugin {
"formeditorfactory.cpp", "formeditorfactory.h",
"formeditorplugin.cpp", "formeditorplugin.h",
"formeditorstack.cpp", "formeditorstack.h",
- "formeditorw.cpp", "formeditorw.h",
+ "formeditor.cpp", "formeditor.h",
"formtemplatewizardpage.cpp", "formtemplatewizardpage.h",
"formwindoweditor.cpp", "formwindoweditor.h",
"formwindowfile.cpp", "formwindowfile.h",
@@ -77,9 +77,7 @@ QtcPlugin {
]
}
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [ "gotoslot_test.cpp" ]
cpp.defines: outer.concat(['SRCDIR="' + FileInfo.path(filePath) + '"'])
diff --git a/src/plugins/designer/designercontext.cpp b/src/plugins/designer/designercontext.cpp
index de65df8a20..266a347bbb 100644
--- a/src/plugins/designer/designercontext.cpp
+++ b/src/plugins/designer/designercontext.cpp
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "designercontext.h"
-#include "formeditorw.h"
+#include "formeditor.h"
#include <QDesignerFormEditorInterface>
#include <QDesignerIntegration>
@@ -25,7 +25,7 @@ DesignerContext::DesignerContext(const Core::Context &context,
void DesignerContext::contextHelp(const HelpCallback &callback) const
{
- const QDesignerFormEditorInterface *core = FormEditorW::designerEditor();
+ const QDesignerFormEditorInterface *core = designerEditor();
callback(core->integration()->contextHelpId());
}
diff --git a/src/plugins/designer/editorwidget.cpp b/src/plugins/designer/editorwidget.cpp
index f1b1772fc2..664e953b3b 100644
--- a/src/plugins/designer/editorwidget.cpp
+++ b/src/plugins/designer/editorwidget.cpp
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "editorwidget.h"
-#include "formeditorw.h"
+#include "formeditor.h"
#include "formeditorstack.h"
#include <coreplugin/editormanager/ieditor.h>
@@ -28,7 +28,7 @@ EditorWidget::EditorWidget(QWidget *parent) :
setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
- QWidget * const*subs = FormEditorW::designerSubWindows();
+ QWidget * const * subs = designerSubWindows();
for (int i = 0; i < DesignerSubWindowCount; i++) {
QWidget *subWindow = subs[i];
subWindow->setWindowTitle(subs[i]->windowTitle());
diff --git a/src/plugins/designer/formeditorw.cpp b/src/plugins/designer/formeditor.cpp
index a55b93d71f..96e6579650 100644
--- a/src/plugins/designer/formeditorw.cpp
+++ b/src/plugins/designer/formeditor.cpp
@@ -5,7 +5,7 @@
#include "designertr.h"
#include "editordata.h"
#include "editorwidget.h"
-#include "formeditorw.h"
+#include "formeditor.h"
#include "formwindoweditor.h"
#include "formwindowfile.h"
#include "qtcreatorintegration.h"
@@ -123,9 +123,9 @@ public:
}
};
-// --------- FormEditorW
+// FormEditorData
-class FormEditorData
+class FormEditorData : public QObject
{
public:
FormEditorData();
@@ -173,7 +173,7 @@ public:
QDesignerFormEditorInterface *m_formeditor = nullptr;
QtCreatorIntegration *m_integration = nullptr;
QDesignerFormWindowManagerInterface *m_fwm = nullptr;
- FormEditorW::InitializationStage m_initStage = FormEditorW::RegisterPlugins;
+ InitializationStage m_initStage = RegisterPlugins;
QWidget *m_designerSubWindows[DesignerSubWindowCount];
@@ -202,7 +202,6 @@ public:
};
static FormEditorData *d = nullptr;
-static FormEditorW *m_instance = nullptr;
FormEditorData::FormEditorData() :
m_formeditor(QDesignerComponents::createFormEditor(nullptr))
@@ -238,7 +237,7 @@ FormEditorData::FormEditorData() :
if (editor && editor->document()->id() == Constants::K_DESIGNER_XML_EDITOR_ID) {
FormWindowEditor *xmlEditor = qobject_cast<FormWindowEditor *>(editor);
QTC_ASSERT(xmlEditor, return);
- FormEditorW::ensureInitStage(FormEditorW::FullyInitialized);
+ ensureInitStage(FullyInitialized);
SharedTools::WidgetHost *fw = m_editorWidget->formWindowEditorForXmlEditor(xmlEditor);
QTC_ASSERT(fw, return);
m_editorWidget->setVisibleEditor(xmlEditor);
@@ -251,7 +250,7 @@ FormEditorData::FormEditorData() :
FormEditorData::~FormEditorData()
{
- if (m_initStage == FormEditorW::FullyInitialized) {
+ if (m_initStage == FullyInitialized) {
QSettings *s = ICore::settings();
s->beginGroup(settingsGroupC);
m_editorWidget->saveSettings(s);
@@ -324,18 +323,18 @@ void FormEditorData::setupViewActions()
void FormEditorData::fullInit()
{
- QTC_ASSERT(m_initStage == FormEditorW::RegisterPlugins, return);
+ QTC_ASSERT(m_initStage == RegisterPlugins, return);
QElapsedTimer *initTime = nullptr;
if (Designer::Constants::Internal::debug) {
initTime = new QElapsedTimer;
initTime->start();
}
- QDesignerComponents::createTaskMenu(m_formeditor, m_instance);
+ QDesignerComponents::createTaskMenu(m_formeditor, this);
QDesignerComponents::initializePlugins(m_formeditor);
QDesignerComponents::initializeResources();
initDesignerSubWindows();
- m_integration = new QtCreatorIntegration(m_formeditor, m_instance);
+ m_integration = new QtCreatorIntegration(m_formeditor, this);
m_formeditor->setIntegration(m_integration);
// Connect Qt Designer help request to HelpManager.
QObject::connect(m_integration, &QtCreatorIntegration::creatorHelpRequested,
@@ -397,13 +396,13 @@ void FormEditorData::fullInit()
Context designerContexts = m_contexts;
designerContexts.add(Core::Constants::C_EDITORMANAGER);
- ICore::addContextObject(new DesignerContext(designerContexts, m_modeWidget, m_instance));
+ ICore::addContextObject(new DesignerContext(designerContexts, m_modeWidget, this));
DesignMode::registerDesignWidget(m_modeWidget, QStringList(FORM_MIMETYPE), m_contexts);
setupViewActions();
- m_initStage = FormEditorW::FullyInitialized;
+ m_initStage = FullyInitialized;
}
void FormEditorData::initDesignerSubWindows()
@@ -438,22 +437,21 @@ void FormEditorData::initDesignerSubWindows()
ae->setObjectName("ActionEditor");
m_formeditor->setActionEditor(ae);
m_designerSubWindows[ActionEditorSubWindow] = ae;
- m_initStage = FormEditorW::SubwindowsInitialized;
+ m_initStage = SubwindowsInitialized;
}
-QList<IOptionsPage *> FormEditorW::optionsPages()
+QList<IOptionsPage *> optionsPages()
{
return d->m_settingsPages;
}
-void FormEditorW::ensureInitStage(InitializationStage s)
+void ensureInitStage(InitializationStage s)
{
if (Designer::Constants::Internal::debug)
qDebug() << Q_FUNC_INFO << s;
- if (!d) {
- m_instance = new FormEditorW;
+ if (!d)
d = new FormEditorData;
- }
+
if (d->m_initStage >= s)
return;
QApplication::setOverrideCursor(Qt::WaitCursor);
@@ -461,15 +459,13 @@ void FormEditorW::ensureInitStage(InitializationStage s)
QApplication::restoreOverrideCursor();
}
-void FormEditorW::deleteInstance()
+void deleteInstance()
{
delete d;
d = nullptr;
- delete m_instance;
- m_instance = nullptr;
}
-IEditor *FormEditorW::createEditor()
+IEditor *createEditor()
{
ensureInitStage(FullyInitialized);
return d->createEditor();
@@ -489,7 +485,7 @@ void FormEditorData::setupActions()
bindShortcut(ActionManager::registerAction(m_fwm->actionPaste(), Core::Constants::PASTE, m_contexts), m_fwm->actionPaste());
bindShortcut(ActionManager::registerAction(m_fwm->actionSelectAll(), Core::Constants::SELECTALL, m_contexts), m_fwm->actionSelectAll());
- m_actionPrint = new QAction(m_instance);
+ m_actionPrint = new QAction(this);
bindShortcut(ActionManager::registerAction(m_actionPrint, Core::Constants::PRINT, m_contexts), m_actionPrint);
QObject::connect(m_actionPrint, &QAction::triggered, [this]() { print(); });
@@ -502,7 +498,7 @@ void FormEditorData::setupActions()
command->setAttribute(Command::CA_Hide);
medit->addAction(command, Core::Constants::G_EDIT_COPYPASTE);
- m_actionGroupEditMode = new QActionGroup(m_instance);
+ m_actionGroupEditMode = new QActionGroup(this);
m_actionGroupEditMode->setExclusive(true);
QObject::connect(m_actionGroupEditMode, &QActionGroup::triggered,
[this](QAction *a) { activateEditMode(a->data().toInt()); });
@@ -601,7 +597,7 @@ void FormEditorData::setupActions()
QString(), Core::Constants::G_DEFAULT_THREE);
mformtools->addSeparator(m_contexts, Core::Constants::G_DEFAULT_THREE);
- m_actionAboutPlugins = new QAction(Tr::tr("About Qt Designer Plugins..."), m_instance);
+ m_actionAboutPlugins = new QAction(Tr::tr("About Qt Designer Plugins..."), d);
addToolAction(m_actionAboutPlugins, m_contexts, "FormEditor.AboutPlugins", mformtools,
QString(), Core::Constants::G_DEFAULT_THREE);
QObject::connect(m_actionAboutPlugins, &QAction::triggered,
@@ -760,19 +756,19 @@ IEditor *FormEditorData::createEditor()
return formWindowEditor;
}
-QDesignerFormEditorInterface *FormEditorW::designerEditor()
+QDesignerFormEditorInterface *designerEditor()
{
ensureInitStage(FullyInitialized);
return d->m_formeditor;
}
-QWidget * const *FormEditorW::designerSubWindows()
+QWidget * const *designerSubWindows()
{
ensureInitStage(SubwindowsInitialized);
return d->m_designerSubWindows;
}
-SharedTools::WidgetHost *FormEditorW::activeWidgetHost()
+SharedTools::WidgetHost *activeWidgetHost()
{
ensureInitStage(FullyInitialized);
if (d->m_editorWidget)
@@ -780,7 +776,7 @@ SharedTools::WidgetHost *FormEditorW::activeWidgetHost()
return nullptr;
}
-FormWindowEditor *FormEditorW::activeEditor()
+FormWindowEditor *activeEditor()
{
ensureInitStage(FullyInitialized);
if (d->m_editorWidget)
diff --git a/src/plugins/designer/formeditor.h b/src/plugins/designer/formeditor.h
new file mode 100644
index 0000000000..0d069e2322
--- /dev/null
+++ b/src/plugins/designer/formeditor.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QtGlobal>
+
+QT_BEGIN_NAMESPACE
+class QDesignerFormEditorInterface;
+class QWidget;
+QT_END_NAMESPACE
+
+namespace Core {
+class IEditor;
+class IOptionsPage;
+}
+
+namespace SharedTools { class WidgetHost; }
+
+namespace Designer {
+
+class FormWindowEditor;
+
+namespace Internal {
+
+/** This is an interface to the Designer CoreInterface to
+ * performs centralized operations.
+ * Since fully initializing Designer at startup is expensive, the
+ * setup has an internal partial initialization stage "RegisterPlugins"
+ * which is there to register the Creator plugin objects
+ * that must be present at startup (settings pages, actions).
+ * The plugin uses this stage at first by calling ensureInitStage().
+ * Requesting an editor via instance() will fully initialize the class.
+ * This is based on the assumption that the Designer settings work with
+ * no plugins loaded.
+ *
+ * The form editor shows a read-only XML editor in edit mode and Qt Designer
+ * in Design mode. */
+
+enum InitializationStage {
+ // Register Creator plugins (settings pages, actions)
+ RegisterPlugins,
+ // Subwindows of the designer are initialized
+ SubwindowsInitialized,
+ // Fully initialized for handling editor requests
+ FullyInitialized
+};
+
+// Create an instance and initialize up to stage s
+void ensureInitStage(InitializationStage s);
+// Deletes an existing instance if there is one.
+void deleteInstance();
+
+Core::IEditor *createEditor();
+
+QDesignerFormEditorInterface *designerEditor();
+QWidget * const *designerSubWindows();
+
+SharedTools::WidgetHost *activeWidgetHost();
+FormWindowEditor *activeEditor();
+QList<Core::IOptionsPage *> optionsPages();
+
+} // namespace Internal
+} // namespace Designer
diff --git a/src/plugins/designer/formeditorfactory.cpp b/src/plugins/designer/formeditorfactory.cpp
index beace0702e..278cb065f4 100644
--- a/src/plugins/designer/formeditorfactory.cpp
+++ b/src/plugins/designer/formeditorfactory.cpp
@@ -1,18 +1,16 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "designertr.h"
#include "formeditorfactory.h"
-#include "formeditorw.h"
-#include "formwindoweditor.h"
+
+#include "designerconstants.h"
+#include "designertr.h"
+#include "formeditor.h"
#include <coreplugin/coreconstants.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <utils/fsengine/fileiconprovider.h>
-#include <QCoreApplication>
-#include <QDebug>
-
using namespace Core;
using namespace Designer::Constants;
using namespace Utils;
@@ -25,7 +23,7 @@ FormEditorFactory::FormEditorFactory()
setId(K_DESIGNER_XML_EDITOR_ID);
setDisplayName(Tr::tr(C_DESIGNER_XML_DISPLAY_NAME));
addMimeType(FORM_MIMETYPE);
- setEditorCreator([] { return FormEditorW::createEditor(); });
+ setEditorCreator([] { return Designer::Internal::createEditor(); });
FileIconProvider::registerIconOverlayForSuffix(ProjectExplorer::Constants::FILEOVERLAY_UI, "ui");
}
diff --git a/src/plugins/designer/formeditorplugin.cpp b/src/plugins/designer/formeditorplugin.cpp
index 8d26f27f0f..881e6a30eb 100644
--- a/src/plugins/designer/formeditorplugin.cpp
+++ b/src/plugins/designer/formeditorplugin.cpp
@@ -1,10 +1,12 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "formeditorplugin.h"
+
+#include "designerconstants.h"
#include "designertr.h"
#include "formeditorfactory.h"
-#include "formeditorplugin.h"
-#include "formeditorw.h"
+#include "formeditor.h"
#include "formtemplatewizardpage.h"
#ifdef CPP_ENABLED
@@ -54,7 +56,7 @@ public:
FormEditorPlugin::~FormEditorPlugin()
{
- FormEditorW::deleteInstance();
+ deleteInstance();
delete d;
}
diff --git a/src/plugins/designer/formeditorstack.cpp b/src/plugins/designer/formeditorstack.cpp
index 9325827f87..1bccd34178 100644
--- a/src/plugins/designer/formeditorstack.cpp
+++ b/src/plugins/designer/formeditorstack.cpp
@@ -2,8 +2,10 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "formeditorstack.h"
+
+#include "designerconstants.h"
#include "formwindoweditor.h"
-#include "formeditorw.h"
+#include "formeditor.h"
#include "formwindowfile.h"
#include <widgethost.h>
diff --git a/src/plugins/designer/formeditorw.h b/src/plugins/designer/formeditorw.h
deleted file mode 100644
index 50462c2aa1..0000000000
--- a/src/plugins/designer/formeditorw.h
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "designerconstants.h"
-
-#include <QObject>
-
-QT_BEGIN_NAMESPACE
-class QDesignerFormEditorInterface;
-QT_END_NAMESPACE
-
-namespace Core {
-class IEditor;
-class IOptionsPage;
-}
-
-namespace SharedTools { class WidgetHost; }
-
-namespace Designer {
-
-class FormWindowEditor;
-
-namespace Internal {
-
-/** FormEditorW is a singleton that stores the Designer CoreInterface and
- * performs centralized operations. The instance() function will return an
- * instance. However, it must be manually deleted when unloading the
- * plugin. Since fully initializing Designer at startup is expensive, the
- * class has an internal partial initialisation stage "RegisterPlugins"
- * which is there to register the Creator plugin objects
- * that must be present at startup (settings pages, actions).
- * The plugin uses this stage at first by calling ensureInitStage().
- * Requesting an editor via instance() will fully initialize the class.
- * This is based on the assumption that the Designer settings work with
- * no plugins loaded.
- *
- * The form editor shows a read-only XML editor in edit mode and Qt Designer
- * in Design mode. */
-class FormEditorW : public QObject
-{
-public:
- enum InitializationStage {
- // Register Creator plugins (settings pages, actions)
- RegisterPlugins,
- // Subwindows of the designer are initialized
- SubwindowsInitialized,
- // Fully initialized for handling editor requests
- FullyInitialized
- };
-
- // Create an instance and initialize up to stage s
- static void ensureInitStage(InitializationStage s);
- // Deletes an existing instance if there is one.
- static void deleteInstance();
-
- static Core::IEditor *createEditor();
-
- static QDesignerFormEditorInterface *designerEditor();
- static QWidget * const *designerSubWindows();
-
- static SharedTools::WidgetHost *activeWidgetHost();
- static FormWindowEditor *activeEditor();
- static QList<Core::IOptionsPage *> optionsPages();
-};
-
-} // namespace Internal
-} // namespace Designer
diff --git a/src/plugins/designer/formtemplatewizardpage.cpp b/src/plugins/designer/formtemplatewizardpage.cpp
index c6417522f4..68e3fb1711 100644
--- a/src/plugins/designer/formtemplatewizardpage.cpp
+++ b/src/plugins/designer/formtemplatewizardpage.cpp
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "designertr.h"
-#include "formeditorw.h"
+#include "formeditor.h"
#include "formtemplatewizardpage.h"
#include <projectexplorer/jsonwizard/jsonwizardpagefactory.h>
@@ -58,7 +58,7 @@ bool FormPageFactory::validateData(Utils::Id typeId, const QVariant &data, QStri
FormTemplateWizardPage::FormTemplateWizardPage(QWidget * parent) :
Utils::WizardPage(parent),
- m_newFormWidget(QDesignerNewFormWidgetInterface::createNewFormWidget(FormEditorW::designerEditor())),
+ m_newFormWidget(QDesignerNewFormWidgetInterface::createNewFormWidget(designerEditor())),
m_templateSelected(m_newFormWidget->hasCurrentTemplate())
{
setTitle(Tr::tr("Choose a Form Template"));
diff --git a/src/plugins/designer/gotoslot_test.cpp b/src/plugins/designer/gotoslot_test.cpp
index f4562a41e7..f804c2259e 100644
--- a/src/plugins/designer/gotoslot_test.cpp
+++ b/src/plugins/designer/gotoslot_test.cpp
@@ -3,7 +3,7 @@
#include "formeditorplugin.h"
-#include "formeditorw.h"
+#include "formeditor.h"
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/testdatadir.h>
@@ -147,7 +147,7 @@ public:
waitForFilesInGlobalSnapshot({cppFile, hFile});
// Execute "Go To Slot"
- QDesignerIntegrationInterface *integration = FormEditorW::designerEditor()->integration();
+ QDesignerIntegrationInterface *integration = designerEditor()->integration();
QVERIFY(integration);
integration->emitNavigateToSlot("pushButton", "clicked()", QStringList());
diff --git a/src/plugins/designer/qtcreatorintegration.cpp b/src/plugins/designer/qtcreatorintegration.cpp
index db96523c36..010ab85a82 100644
--- a/src/plugins/designer/qtcreatorintegration.cpp
+++ b/src/plugins/designer/qtcreatorintegration.cpp
@@ -1,10 +1,13 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "qtcreatorintegration.h"
+
+#include "designerconstants.h"
#include "designertr.h"
-#include "formeditorw.h"
+#include "formeditor.h"
#include "formwindoweditor.h"
-#include "qtcreatorintegration.h"
+
#include <widgethost.h>
#include <designer/cpp/formclasswizardpage.h>
@@ -16,20 +19,24 @@
#include <cppeditor/cppworkingcopy.h>
#include <cppeditor/insertionpointlocator.h>
#include <cppeditor/symbolfinder.h>
+
#include <cplusplus/LookupContext.h>
#include <cplusplus/Overview.h>
+
#include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/messagemanager.h>
-#include <texteditor/texteditor.h>
-#include <texteditor/textdocument.h>
+
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/extracompiler.h>
#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
+#include <texteditor/texteditor.h>
+#include <texteditor/textdocument.h>
+
#include <utils/algorithm.h>
#include <utils/mimeutils.h>
#include <utils/qtcassert.h>
@@ -152,14 +159,14 @@ void QtCreatorIntegration::slotDesignerHelpRequested(const QString &manual, cons
void QtCreatorIntegration::updateSelection()
{
- if (SharedTools::WidgetHost *host = FormEditorW::activeWidgetHost())
+ if (SharedTools::WidgetHost *host = activeWidgetHost())
host->updateFormWindowSelectionHandles(true);
QDesignerIntegration::updateSelection();
}
QWidget *QtCreatorIntegration::containerWindow(QWidget * /*widget*/) const
{
- if (SharedTools::WidgetHost *host = FormEditorW::activeWidgetHost())
+ if (SharedTools::WidgetHost *host = activeWidgetHost())
return host->integrationContainer();
return nullptr;
}
@@ -431,7 +438,7 @@ void QtCreatorIntegration::slotNavigateToSlot(const QString &objectName, const Q
{
QString errorMessage;
if (!navigateToSlot(objectName, signalSignature, parameterNames, &errorMessage) && !errorMessage.isEmpty())
- QMessageBox::warning(FormEditorW::designerEditor()->topLevel(), Tr::tr("Error finding/adding a slot."), errorMessage);
+ QMessageBox::warning(designerEditor()->topLevel(), Tr::tr("Error finding/adding a slot."), errorMessage);
}
// Build name of the class as generated by uic, insert Ui namespace
@@ -452,8 +459,8 @@ static Document::Ptr getParsedDocument(const FilePath &filePath,
Snapshot &snapshot)
{
QByteArray src;
- if (workingCopy.contains(filePath)) {
- src = workingCopy.source(filePath);
+ if (const auto source = workingCopy.source(filePath)) {
+ src = *source;
} else {
Utils::FileReader reader;
if (reader.fetch(filePath)) // ### FIXME error reporting
@@ -476,7 +483,7 @@ bool QtCreatorIntegration::navigateToSlot(const QString &objectName,
{
using DocumentMap = QMap<int, Document::Ptr>;
- const Utils::FilePath currentUiFile = FormEditorW::activeEditor()->document()->filePath();
+ const Utils::FilePath currentUiFile = activeEditor()->document()->filePath();
#if 0
return Designer::Internal::navigateToSlot(currentUiFile.toString(), objectName,
signalSignature, parameterNames, errorMessage);
@@ -493,10 +500,10 @@ bool QtCreatorIntegration::navigateToSlot(const QString &objectName,
// Retrieve code model snapshot restricted to project of ui file or the working copy.
Snapshot docTable = CppEditor::CppModelManager::instance()->snapshot();
Snapshot newDocTable;
- const Project *uiProject = SessionManager::projectForFile(currentUiFile);
+ const Project *uiProject = ProjectManager::projectForFile(currentUiFile);
if (uiProject) {
for (Snapshot::const_iterator i = docTable.begin(), ei = docTable.end(); i != ei; ++i) {
- const Project *project = SessionManager::projectForFile(i.key());
+ const Project *project = ProjectManager::projectForFile(i.key());
if (project == uiProject)
newDocTable.insert(i.value());
}
@@ -529,7 +536,7 @@ bool QtCreatorIntegration::navigateToSlot(const QString &objectName,
return false;
}
- QDesignerFormWindowInterface *fwi = FormEditorW::activeWidgetHost()->formWindow();
+ QDesignerFormWindowInterface *fwi = activeWidgetHost()->formWindow();
QString uiClass;
const Class *cl = nullptr;
@@ -639,7 +646,7 @@ void QtCreatorIntegration::handleSymbolRenameStage1(
return;
// Get ExtraCompiler.
- const Project * const project = SessionManager::projectForFile(uiFile);
+ const Project * const project = ProjectManager::projectForFile(uiFile);
if (!project) {
return reportRenamingError(oldName, Designer::Tr::tr("File \"%1\" not found in project.")
.arg(uiFile.toUserOutput()));
diff --git a/src/plugins/designer/resourcehandler.cpp b/src/plugins/designer/resourcehandler.cpp
index 29838283d5..fe2cdd28c6 100644
--- a/src/plugins/designer/resourcehandler.cpp
+++ b/src/plugins/designer/resourcehandler.cpp
@@ -2,13 +2,16 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "resourcehandler.h"
+
#include "designerconstants.h"
+#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
-#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+
#include <resourceeditor/resourcenode.h>
+
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
@@ -42,10 +45,10 @@ void ResourceHandler::ensureInitialized()
Qt::QueuedConnection);
};
- for (Project *p : SessionManager::projects())
+ for (Project *p : ProjectManager::projects())
connector(p);
- connect(SessionManager::instance(), &SessionManager::projectAdded, this, connector);
+ connect(ProjectManager::instance(), &ProjectManager::projectAdded, this, connector);
m_originalUiQrcPaths = m_form->activeResourceFilePaths();
if (Designer::Constants::Internal::debug)
@@ -68,7 +71,7 @@ void ResourceHandler::updateResourcesHelper(bool updateProjectResources)
qDebug() << "ResourceHandler::updateResources()" << fileName;
// Filename could change in the meantime.
- Project *project = SessionManager::projectForFile(Utils::FilePath::fromUserInput(fileName));
+ Project *project = ProjectManager::projectForFile(Utils::FilePath::fromUserInput(fileName));
const bool dirty = m_form->property("_q_resourcepathchanged").toBool();
if (dirty)
m_form->setDirty(true);
diff --git a/src/plugins/designer/settingspage.cpp b/src/plugins/designer/settingspage.cpp
index a3e3ce3808..e1998ffd5e 100644
--- a/src/plugins/designer/settingspage.cpp
+++ b/src/plugins/designer/settingspage.cpp
@@ -1,48 +1,46 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "designertr.h"
-#include "formeditorw.h"
#include "settingspage.h"
+#include "designerconstants.h"
+#include "designertr.h"
+#include "formeditor.h"
+
#include <coreplugin/icontext.h>
#include <utils/stringutils.h>
#include <QDesignerOptionsPageInterface>
#include <QCoreApplication>
+#include <QVBoxLayout>
-using namespace Designer::Internal;
+namespace Designer::Internal {
-SettingsPage::SettingsPage(QDesignerOptionsPageInterface *designerPage) :
- Core::IOptionsPage(nullptr, false),
- m_designerPage(designerPage)
+class SettingsPageWidget : public Core::IOptionsPageWidget
{
- setId(Utils::Id::fromString(m_designerPage->name()));
- setDisplayName(m_designerPage->name());
- setCategory(Designer::Constants::SETTINGS_CATEGORY);
-}
+public:
+ explicit SettingsPageWidget(QDesignerOptionsPageInterface *designerPage)
+ : m_designerPage(designerPage)
+ {
+ auto vbox = new QVBoxLayout(this);
+ vbox->addWidget(m_designerPage->createPage(nullptr));
+ }
-QWidget *SettingsPage::widget()
-{
- m_initialized = true;
- if (!m_widget)
- m_widget = m_designerPage->createPage(nullptr);
- return m_widget;
+ void apply() { m_designerPage->apply(); }
+ void finish() { m_designerPage->finish(); }
-}
+ QDesignerOptionsPageInterface *m_designerPage;
+};
-void SettingsPage::apply()
-{
- if (m_initialized)
- m_designerPage->apply();
-}
-void SettingsPage::finish()
+SettingsPage::SettingsPage(QDesignerOptionsPageInterface *designerPage)
+ : Core::IOptionsPage(false)
{
- if (m_initialized)
- m_designerPage->finish();
- delete m_widget;
+ setId(Utils::Id::fromString(designerPage->name()));
+ setDisplayName(designerPage->name());
+ setCategory(Designer::Constants::SETTINGS_CATEGORY);
+ setWidgetCreator([designerPage] { return new SettingsPageWidget(designerPage); });
}
SettingsPageProvider::SettingsPageProvider()
@@ -58,9 +56,9 @@ QList<Core::IOptionsPage *> SettingsPageProvider::pages() const
if (!m_initialized) {
// get options pages from designer
m_initialized = true;
- FormEditorW::ensureInitStage(FormEditorW::RegisterPlugins);
+ ensureInitStage(RegisterPlugins);
}
- return FormEditorW::optionsPages();
+ return optionsPages();
}
bool SettingsPageProvider::matches(const QRegularExpression &regex) const
@@ -102,3 +100,5 @@ bool SettingsPageProvider::matches(const QRegularExpression &regex) const
}
return false;
}
+
+} // Designer::Internal
diff --git a/src/plugins/designer/settingspage.h b/src/plugins/designer/settingspage.h
index cdfb32c003..03206109db 100644
--- a/src/plugins/designer/settingspage.h
+++ b/src/plugins/designer/settingspage.h
@@ -5,38 +5,20 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <QPointer>
-
QT_BEGIN_NAMESPACE
class QDesignerOptionsPageInterface;
QT_END_NAMESPACE
-namespace Designer {
-namespace Internal {
-
-class SettingsPageWidget;
+namespace Designer::Internal {
class SettingsPage : public Core::IOptionsPage
{
- Q_OBJECT
-
public:
explicit SettingsPage(QDesignerOptionsPageInterface *designerPage);
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-private:
- QDesignerOptionsPageInterface *m_designerPage;
- bool m_initialized = false;
- QPointer<QWidget> m_widget;
};
class SettingsPageProvider : public Core::IOptionsPageProvider
{
- Q_OBJECT
-
public:
SettingsPageProvider();
@@ -48,5 +30,4 @@ private:
mutable QStringList m_keywords;
};
-} // namespace Internal
-} // namespace Designer
+} // Designer::Internal
diff --git a/src/plugins/diffeditor/diffeditor.cpp b/src/plugins/diffeditor/diffeditor.cpp
index 5274214f92..929c30c339 100644
--- a/src/plugins/diffeditor/diffeditor.cpp
+++ b/src/plugins/diffeditor/diffeditor.cpp
@@ -185,7 +185,7 @@ DiffEditor::DiffEditor()
policy.setHorizontalPolicy(QSizePolicy::Expanding);
m_entriesComboBox->setSizePolicy(policy);
connect(m_entriesComboBox, &QComboBox::currentIndexChanged,
- this, &DiffEditor::setCurrentDiffFileIndex);
+ this, &DiffEditor::currentIndexChanged);
m_toolBar->addWidget(m_entriesComboBox);
QLabel *contextLabel = new QLabel(m_toolBar);
@@ -309,7 +309,6 @@ TextEditorWidget *DiffEditor::sideEditorWidget(DiffSide side) const
return m_sideBySideView->sideEditorWidget(side);
}
-
void DiffEditor::documentHasChanged()
{
GuardLocker guard(m_ignoreChanges);
@@ -319,7 +318,10 @@ void DiffEditor::documentHasChanged()
currentView()->setDiff(diffFileList);
m_entriesComboBox->clear();
- for (const FileData &diffFile : diffFileList) {
+ const QString startupFile = m_document->startupFile();
+ int startupFileIndex = -1;
+ for (int i = 0, total = diffFileList.count(); i < total; ++i) {
+ const FileData &diffFile = diffFileList.at(i);
const DiffFileInfo &leftEntry = diffFile.fileInfo[LeftSide];
const DiffFileInfo &rightEntry = diffFile.fileInfo[RightSide];
const QString leftShortFileName = FilePath::fromString(leftEntry.fileName).fileName();
@@ -332,30 +334,20 @@ void DiffEditor::documentHasChanged()
if (leftEntry.typeInfo.isEmpty() && rightEntry.typeInfo.isEmpty()) {
itemToolTip = leftEntry.fileName;
} else {
- itemToolTip = Tr::tr("[%1] vs. [%2] %3")
- .arg(leftEntry.typeInfo,
- rightEntry.typeInfo,
- leftEntry.fileName);
+ itemToolTip = Tr::tr("[%1] vs. [%2] %3").arg(
+ leftEntry.typeInfo, rightEntry.typeInfo, leftEntry.fileName);
}
} else {
- if (leftShortFileName == rightShortFileName) {
+ if (leftShortFileName == rightShortFileName)
itemText = leftShortFileName;
- } else {
- itemText = Tr::tr("%1 vs. %2")
- .arg(leftShortFileName,
- rightShortFileName);
- }
+ else
+ itemText = Tr::tr("%1 vs. %2").arg(leftShortFileName, rightShortFileName);
if (leftEntry.typeInfo.isEmpty() && rightEntry.typeInfo.isEmpty()) {
- itemToolTip = Tr::tr("%1 vs. %2")
- .arg(leftEntry.fileName,
- rightEntry.fileName);
+ itemToolTip = Tr::tr("%1 vs. %2").arg(leftEntry.fileName, rightEntry.fileName);
} else {
- itemToolTip = Tr::tr("[%1] %2 vs. [%3] %4")
- .arg(leftEntry.typeInfo,
- leftEntry.fileName,
- rightEntry.typeInfo,
- rightEntry.fileName);
+ itemToolTip = Tr::tr("[%1] %2 vs. [%3] %4").arg(leftEntry.typeInfo,
+ leftEntry.fileName, rightEntry.typeInfo, rightEntry.fileName);
}
}
m_entriesComboBox->addItem(itemText);
@@ -365,7 +357,21 @@ void DiffEditor::documentHasChanged()
rightEntry.fileName, Qt::UserRole + 1);
m_entriesComboBox->setItemData(m_entriesComboBox->count() - 1,
itemToolTip, Qt::ToolTipRole);
+ if (startupFileIndex < 0) {
+ const bool isStartup = m_currentFileChunk.first.isEmpty()
+ && m_currentFileChunk.second.isEmpty()
+ && startupFile.endsWith(rightEntry.fileName);
+ const bool isSame = m_currentFileChunk.first == leftEntry.fileName
+ && m_currentFileChunk.second == rightEntry.fileName;
+ if (isStartup || isSame)
+ startupFileIndex = i;
+ }
}
+
+ currentView()->endOperation();
+ m_currentFileChunk = {};
+ if (startupFileIndex >= 0)
+ setCurrentDiffFileIndex(startupFileIndex);
}
void DiffEditor::toggleDescription()
@@ -439,6 +445,7 @@ void DiffEditor::prepareForReload()
m_whitespaceButtonAction->setChecked(m_document->ignoreWhitespace());
}
currentView()->beginOperation();
+ currentView()->setMessage(Tr::tr("Waiting for data..."));
}
void DiffEditor::reloadHasFinished(bool success)
@@ -446,29 +453,8 @@ void DiffEditor::reloadHasFinished(bool success)
if (!currentView())
return;
- currentView()->endOperation(success);
-
- int index = -1;
- const QString startupFile = m_document->startupFile();
- const QList<FileData> &diffFileList = m_document->diffFiles();
- const int count = diffFileList.count();
- for (int i = 0; i < count; i++) {
- const FileData &diffFile = diffFileList.at(i);
- const DiffFileInfo &leftEntry = diffFile.fileInfo[LeftSide];
- const DiffFileInfo &rightEntry = diffFile.fileInfo[RightSide];
- if ((m_currentFileChunk.first.isEmpty()
- && m_currentFileChunk.second.isEmpty()
- && startupFile.endsWith(rightEntry.fileName))
- || (m_currentFileChunk.first == leftEntry.fileName
- && m_currentFileChunk.second == rightEntry.fileName)) {
- index = i;
- break;
- }
- }
-
- m_currentFileChunk = {};
- if (index >= 0)
- setCurrentDiffFileIndex(index);
+ if (!success)
+ currentView()->setMessage(Tr::tr("Retrieving data failed."));
}
void DiffEditor::updateEntryToolTip()
@@ -478,14 +464,19 @@ void DiffEditor::updateEntryToolTip()
m_entriesComboBox->setToolTip(toolTip);
}
-void DiffEditor::setCurrentDiffFileIndex(int index)
+void DiffEditor::currentIndexChanged(int index)
{
if (m_ignoreChanges.isLocked())
return;
+ GuardLocker guard(m_ignoreChanges);
+ setCurrentDiffFileIndex(index);
+}
+
+void DiffEditor::setCurrentDiffFileIndex(int index)
+{
QTC_ASSERT((index < 0) != (m_entriesComboBox->count() > 0), return);
- GuardLocker guard(m_ignoreChanges);
m_currentDiffFileIndex = index;
currentView()->setCurrentDiffFileIndex(index);
@@ -563,7 +554,7 @@ void DiffEditor::addView(IDiffView *view)
if (m_views.count() == 1)
setCurrentView(view);
- connect(view, &IDiffView::currentDiffFileIndexChanged, this, &DiffEditor::setCurrentDiffFileIndex);
+ connect(view, &IDiffView::currentDiffFileIndexChanged, this, &DiffEditor::currentIndexChanged);
}
IDiffView *DiffEditor::currentView() const
diff --git a/src/plugins/diffeditor/diffeditor.h b/src/plugins/diffeditor/diffeditor.h
index a801ee953b..49922d323e 100644
--- a/src/plugins/diffeditor/diffeditor.h
+++ b/src/plugins/diffeditor/diffeditor.h
@@ -53,6 +53,7 @@ private:
void ignoreWhitespaceHasChanged();
void prepareForReload();
void reloadHasFinished(bool success);
+ void currentIndexChanged(int index);
void setCurrentDiffFileIndex(int index);
void documentStateChanged();
diff --git a/src/plugins/diffeditor/diffeditorcontroller.cpp b/src/plugins/diffeditor/diffeditorcontroller.cpp
index dcce09f0b2..1ab7bfccb2 100644
--- a/src/plugins/diffeditor/diffeditorcontroller.cpp
+++ b/src/plugins/diffeditor/diffeditorcontroller.cpp
@@ -12,6 +12,7 @@
#include <utils/qtcassert.h>
using namespace Core;
+using namespace Tasking;
using namespace Utils;
namespace DiffEditor {
diff --git a/src/plugins/diffeditor/diffeditorcontroller.h b/src/plugins/diffeditor/diffeditorcontroller.h
index f52f2af1a0..1f791b2dcb 100644
--- a/src/plugins/diffeditor/diffeditorcontroller.h
+++ b/src/plugins/diffeditor/diffeditorcontroller.h
@@ -6,7 +6,7 @@
#include "diffeditor_global.h"
#include "diffutils.h"
-#include <utils/tasktree.h>
+#include <solutions/tasking/tasktree.h>
#include <QObject>
@@ -61,7 +61,7 @@ signals:
protected:
// Core functions:
- void setReloadRecipe(const Utils::Tasking::Group &recipe) { m_reloadRecipe = recipe; }
+ void setReloadRecipe(const Tasking::Group &recipe) { m_reloadRecipe = recipe; }
void setDiffFiles(const QList<FileData> &diffFileList);
// Optional:
void setDisplayName(const QString &name) { m_displayName = name; }
@@ -74,8 +74,8 @@ private:
Internal::DiffEditorDocument *const m_document;
QString m_displayName;
- std::unique_ptr<Utils::TaskTree> m_taskTree;
- Utils::Tasking::Group m_reloadRecipe;
+ std::unique_ptr<Tasking::TaskTree> m_taskTree;
+ Tasking::Group m_reloadRecipe;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(DiffEditorController::PatchOptions)
diff --git a/src/plugins/diffeditor/diffeditordocument.cpp b/src/plugins/diffeditor/diffeditordocument.cpp
index 05b1684c8a..1d43064d60 100644
--- a/src/plugins/diffeditor/diffeditordocument.cpp
+++ b/src/plugins/diffeditor/diffeditordocument.cpp
@@ -291,8 +291,8 @@ Core::IDocument::OpenResult DiffEditorDocument::open(QString *errorString, const
return OpenResult::ReadError;
}
- bool ok = false;
- QList<FileData> fileDataList = DiffUtils::readPatch(patch, &ok);
+ const std::optional<QList<FileData>> fileDataList = DiffUtils::readPatch(patch);
+ bool ok = fileDataList.has_value();
if (!ok) {
*errorString = Tr::tr("Could not parse patch file \"%1\". "
"The content is not of unified diff format.")
@@ -302,7 +302,7 @@ Core::IDocument::OpenResult DiffEditorDocument::open(QString *errorString, const
emit temporaryStateChanged();
setFilePath(filePath.absoluteFilePath());
setWorkingDirectory(filePath.absoluteFilePath());
- setDiffFiles(fileDataList);
+ setDiffFiles(*fileDataList);
}
endReload(ok);
if (!ok && readResult == TextFileFormat::ReadEncodingError)
diff --git a/src/plugins/diffeditor/diffeditorplugin.cpp b/src/plugins/diffeditor/diffeditorplugin.cpp
index 2a81320665..99ceef2f0a 100644
--- a/src/plugins/diffeditor/diffeditorplugin.cpp
+++ b/src/plugins/diffeditor/diffeditorplugin.cpp
@@ -13,12 +13,13 @@
#include <coreplugin/editormanager/documentmodel.h>
#include <coreplugin/editormanager/editormanager.h>
+#include <extensionsystem/pluginmanager.h>
+
#include <texteditor/textdocument.h>
-#include <utils/asynctask.h>
+#include <utils/async.h>
#include <utils/differ.h>
#include <utils/fileutils.h>
-#include <utils/futuresynchronizer.h>
#include <utils/qtcassert.h>
#include <QAction>
@@ -47,13 +48,12 @@ public:
m_ignoreWhitespace(ignoreWhitespace)
{}
- void operator()(QFutureInterface<FileData> &futureInterface,
- const ReloadInput &reloadInput) const
+ void operator()(QPromise<FileData> &promise, const ReloadInput &reloadInput) const
{
if (reloadInput.text[LeftSide] == reloadInput.text[RightSide])
return; // We show "No difference" in this case, regardless if it's binary or not
- Differ differ(&futureInterface);
+ Differ differ(QFuture<void>(promise.future()));
FileData fileData;
if (!reloadInput.binaryFiles) {
@@ -85,7 +85,7 @@ public:
fileData.fileInfo = reloadInput.fileInfo;
fileData.fileOperation = reloadInput.fileOperation;
fileData.binaryFiles = reloadInput.binaryFiles;
- futureInterface.reportResult(fileData);
+ promise.addResult(fileData);
}
private:
@@ -114,11 +114,12 @@ DiffFilesController::DiffFilesController(IDocument *document)
const auto setupTree = [this, storage](TaskTree &taskTree) {
QList<std::optional<FileData>> *outputList = storage.activeStorage();
- const auto setupDiff = [this](AsyncTask<FileData> &async, const ReloadInput &reloadInput) {
- async.setAsyncCallData(DiffFile(ignoreWhitespace(), contextLineCount()), reloadInput);
- async.setFutureSynchronizer(Internal::DiffEditorPlugin::futureSynchronizer());
+ const auto setupDiff = [this](Async<FileData> &async, const ReloadInput &reloadInput) {
+ async.setConcurrentCallData(
+ DiffFile(ignoreWhitespace(), contextLineCount()), reloadInput);
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
};
- const auto onDiffDone = [outputList](const AsyncTask<FileData> &async, int i) {
+ const auto onDiffDone = [outputList](const Async<FileData> &async, int i) {
if (async.isResultAvailable())
(*outputList)[i] = async.result();
};
@@ -127,15 +128,15 @@ DiffFilesController::DiffFilesController(IDocument *document)
outputList->resize(inputList.size());
using namespace std::placeholders;
- QList<TaskItem> tasks {parallel, optional};
+ QList<TaskItem> tasks {parallel, finishAllAndDone};
for (int i = 0; i < inputList.size(); ++i) {
- tasks.append(Async<FileData>(std::bind(setupDiff, _1, inputList.at(i)),
+ tasks.append(AsyncTask<FileData>(std::bind(setupDiff, _1, inputList.at(i)),
std::bind(onDiffDone, _1, i)));
}
taskTree.setupRoot(tasks);
};
const auto onTreeDone = [this, storage] {
- const QList<std::optional<FileData>> &results = *storage.activeStorage();
+ const QList<std::optional<FileData>> &results = *storage;
QList<FileData> finalList;
for (const std::optional<FileData> &result : results) {
if (result.has_value())
@@ -149,9 +150,9 @@ DiffFilesController::DiffFilesController(IDocument *document)
const Group root = {
Storage(storage),
- Tree(setupTree),
- OnGroupDone(onTreeDone),
- OnGroupError(onTreeError)
+ TaskTreeTask(setupTree),
+ onGroupDone(onTreeDone),
+ onGroupError(onTreeError)
};
setReloadRecipe(root);
}
@@ -416,12 +417,10 @@ public:
DiffEditorFactory m_editorFactory;
DiffEditorServiceImpl m_service;
- FutureSynchronizer m_futureSynchronizer;
};
DiffEditorPluginPrivate::DiffEditorPluginPrivate()
{
- m_futureSynchronizer.setCancelOnWait(true);
//register actions
ActionContainer *toolsContainer = ActionManager::actionContainer(Core::Constants::M_TOOLS);
toolsContainer->insertGroup(Core::Constants::G_TOOLS_DEBUG, Constants::G_TOOLS_DIFF);
@@ -536,12 +535,6 @@ void DiffEditorPlugin::initialize()
d = new DiffEditorPluginPrivate;
}
-FutureSynchronizer *DiffEditorPlugin::futureSynchronizer()
-{
- QTC_ASSERT(s_instance, return nullptr);
- return &s_instance->d->m_futureSynchronizer;
-}
-
} // namespace Internal
} // namespace DiffEditor
@@ -771,13 +764,13 @@ void DiffEditor::Internal::DiffEditorPlugin::testMakePatch()
QCOMPARE(result, patchText);
- bool ok;
- QList<FileData> resultList = DiffUtils::readPatch(result, &ok);
+ const std::optional<QList<FileData>> resultList = DiffUtils::readPatch(result);
+ const bool ok = resultList.has_value();
QVERIFY(ok);
- QCOMPARE(resultList.count(), 1);
- for (int i = 0; i < resultList.count(); i++) {
- const FileData &resultFileData = resultList.at(i);
+ QCOMPARE(resultList->count(), 1);
+ for (int i = 0; i < resultList->count(); i++) {
+ const FileData &resultFileData = resultList->at(i);
QCOMPARE(resultFileData.fileInfo[LeftSide].fileName, fileName);
QCOMPARE(resultFileData.fileInfo[RightSide].fileName, fileName);
QCOMPARE(resultFileData.chunks.count(), 1);
@@ -1335,14 +1328,14 @@ void DiffEditor::Internal::DiffEditorPlugin::testReadPatch()
QFETCH(QString, sourcePatch);
QFETCH(QList<FileData>, fileDataList);
- bool ok;
- const QList<FileData> &result = DiffUtils::readPatch(sourcePatch, &ok);
+ const std::optional<QList<FileData>> result = DiffUtils::readPatch(sourcePatch);
+ const bool ok = result.has_value();
QVERIFY(ok);
- QCOMPARE(result.count(), fileDataList.count());
+ QCOMPARE(result->count(), fileDataList.count());
for (int i = 0; i < fileDataList.count(); i++) {
const FileData &origFileData = fileDataList.at(i);
- const FileData &resultFileData = result.at(i);
+ const FileData &resultFileData = result->at(i);
QCOMPARE(resultFileData.fileInfo[LeftSide].fileName, origFileData.fileInfo[LeftSide].fileName);
QCOMPARE(resultFileData.fileInfo[LeftSide].typeInfo, origFileData.fileInfo[LeftSide].typeInfo);
QCOMPARE(resultFileData.fileInfo[RightSide].fileName, origFileData.fileInfo[RightSide].fileName);
diff --git a/src/plugins/diffeditor/diffeditorplugin.h b/src/plugins/diffeditor/diffeditorplugin.h
index 66849a7387..17b6a2d430 100644
--- a/src/plugins/diffeditor/diffeditorplugin.h
+++ b/src/plugins/diffeditor/diffeditorplugin.h
@@ -6,8 +6,6 @@
#include <coreplugin/diffservice.h>
#include <extensionsystem/iplugin.h>
-namespace Utils { class FutureSynchronizer; }
-
namespace DiffEditor {
namespace Internal {
@@ -34,8 +32,6 @@ public:
void initialize() final;
- static Utils::FutureSynchronizer *futureSynchronizer();
-
private:
class DiffEditorPluginPrivate *d = nullptr;
diff --git a/src/plugins/diffeditor/diffutils.cpp b/src/plugins/diffeditor/diffutils.cpp
index f9d7d6b264..afb7520303 100644
--- a/src/plugins/diffeditor/diffutils.cpp
+++ b/src/plugins/diffeditor/diffutils.cpp
@@ -6,7 +6,8 @@
#include <utils/algorithm.h>
#include <utils/differ.h>
-#include <QFutureInterfaceBase>
+#include <QFuture>
+#include <QPromise>
#include <QRegularExpression>
#include <QStringList>
#include <QTextStream>
@@ -556,7 +557,7 @@ static QList<RowData> readLines(QStringView patch, bool lastChunk, bool *lastChu
int noNewLineInDelete = -1;
int noNewLineInInsert = -1;
- const QVector<QStringView> lines = patch.split(newLine);
+ const QList<QStringView> lines = patch.split(newLine);
int i;
for (i = 0; i < lines.size(); i++) {
QStringView line = lines.at(i);
@@ -794,7 +795,7 @@ static QList<ChunkData> readChunks(QStringView patch, bool *lastChunkAtTheEndOfF
QList<ChunkData> chunkDataList;
int position = -1;
- QVector<int> startingPositions; // store starting positions of @@
+ QList<int> startingPositions; // store starting positions of @@
if (patch.startsWith(QStringLiteral("@@ -")))
startingPositions.append(position + 1);
@@ -892,7 +893,7 @@ static FileData readDiffHeaderAndChunks(QStringView headerAndChunks, bool *ok)
}
-static QList<FileData> readDiffPatch(QStringView patch, bool *ok, QFutureInterfaceBase *jobController)
+static void readDiffPatch(QPromise<QList<FileData>> &promise, QStringView patch)
{
const QRegularExpression diffRegExp("(?:\\n|^)" // new line of the beginning of a patch
"(" // either
@@ -911,23 +912,20 @@ static QList<FileData> readDiffPatch(QStringView patch, bool *ok, QFutureInterfa
")"); // end of or
bool readOk = false;
-
QList<FileData> fileDataList;
-
QRegularExpressionMatch diffMatch = diffRegExp.match(patch);
if (diffMatch.hasMatch()) {
readOk = true;
int lastPos = -1;
do {
- if (jobController && jobController->isCanceled())
- return {};
+ if (promise.isCanceled())
+ return;
int pos = diffMatch.capturedStart();
if (lastPos >= 0) {
QStringView headerAndChunks = patch.mid(lastPos, pos - lastPos);
- const FileData fileData = readDiffHeaderAndChunks(headerAndChunks,
- &readOk);
+ const FileData fileData = readDiffHeaderAndChunks(headerAndChunks, &readOk);
if (!readOk)
break;
@@ -942,21 +940,15 @@ static QList<FileData> readDiffPatch(QStringView patch, bool *ok, QFutureInterfa
if (readOk) {
QStringView headerAndChunks = patch.mid(lastPos, patch.size() - lastPos - 1);
- const FileData fileData = readDiffHeaderAndChunks(headerAndChunks,
- &readOk);
+ const FileData fileData = readDiffHeaderAndChunks(headerAndChunks, &readOk);
if (readOk)
fileDataList.append(fileData);
}
}
-
- if (ok)
- *ok = readOk;
-
if (!readOk)
- return {};
-
- return fileDataList;
+ return;
+ promise.addResult(fileDataList);
}
// The git diff patch format (ChangeFile, NewFile, DeleteFile)
@@ -1203,11 +1195,11 @@ static bool detectFileData(QStringView patch, FileData *fileData, QStringView *r
return detectIndexAndBinary(*remainingPatch, fileData, remainingPatch);
}
-static QList<FileData> readGitPatch(QStringView patch, bool *ok, QFutureInterfaceBase *jobController)
+static void readGitPatch(QPromise<QList<FileData>> &promise, QStringView patch)
{
int position = -1;
- QVector<int> startingPositions; // store starting positions of git headers
+ QList<int> startingPositions; // store starting positions of git headers
if (patch.startsWith(QStringLiteral("diff --git ")))
startingPositions.append(position + 1);
@@ -1221,13 +1213,12 @@ static QList<FileData> readGitPatch(QStringView patch, bool *ok, QFutureInterfac
};
const QChar newLine('\n');
- bool readOk = true;
- QVector<PatchInfo> patches;
+ QList<PatchInfo> patches;
const int count = startingPositions.size();
for (int i = 0; i < count; i++) {
- if (jobController && jobController->isCanceled())
- return {};
+ if (promise.isCanceled())
+ return;
const int diffStart = startingPositions.at(i);
const int diffEnd = (i < count - 1)
@@ -1242,65 +1233,53 @@ static QList<FileData> readGitPatch(QStringView patch, bool *ok, QFutureInterfac
FileData fileData;
QStringView remainingFileDiff;
- readOk = detectFileData(fileDiff, &fileData, &remainingFileDiff);
-
- if (!readOk)
- break;
+ if (!detectFileData(fileDiff, &fileData, &remainingFileDiff))
+ return;
patches.append(PatchInfo { remainingFileDiff, fileData });
}
- if (!readOk) {
- if (ok)
- *ok = readOk;
- return {};
- }
+ if (patches.isEmpty())
+ return;
- if (jobController)
- jobController->setProgressRange(0, patches.size());
+ promise.setProgressRange(0, patches.size());
QList<FileData> fileDataList;
- readOk = false;
int i = 0;
for (const auto &patchInfo : std::as_const(patches)) {
- if (jobController) {
- if (jobController->isCanceled())
- return {};
- jobController->setProgressValue(i++);
- }
+ if (promise.isCanceled())
+ return;
+ promise.setProgressValue(i++);
FileData fileData = patchInfo.fileData;
+ bool readOk = false;
if (!patchInfo.patch.isEmpty() || fileData.fileOperation == FileData::ChangeFile)
fileData.chunks = readChunks(patchInfo.patch, &fileData.lastChunkAtTheEndOfFile, &readOk);
else
readOk = true;
if (!readOk)
- break;
+ return;
fileDataList.append(fileData);
}
+ promise.addResult(fileDataList);
+}
- if (ok)
- *ok = readOk;
-
- if (!readOk)
+std::optional<QList<FileData>> DiffUtils::readPatch(const QString &patch)
+{
+ QPromise<QList<FileData>> promise;
+ promise.start();
+ readPatchWithPromise(promise, patch);
+ if (promise.future().resultCount() == 0)
return {};
-
- return fileDataList;
+ return promise.future().result();
}
-QList<FileData> DiffUtils::readPatch(const QString &patch, bool *ok,
- QFutureInterfaceBase *jobController)
+void DiffUtils::readPatchWithPromise(QPromise<QList<FileData>> &promise, const QString &patch)
{
- bool readOk = false;
-
- QList<FileData> fileDataList;
-
- if (jobController) {
- jobController->setProgressRange(0, 1);
- jobController->setProgressValue(0);
- }
+ promise.setProgressRange(0, 1);
+ promise.setProgressValue(0);
QStringView croppedPatch = QStringView(patch);
// Crop e.g. "-- \n2.10.2.windows.1\n\n" at end of file
const QRegularExpression formatPatchEndingRegExp("(\\n-- \\n\\S*\\n\\n$)");
@@ -1308,14 +1287,9 @@ QList<FileData> DiffUtils::readPatch(const QString &patch, bool *ok,
if (match.hasMatch())
croppedPatch = croppedPatch.left(match.capturedStart() + 1);
- fileDataList = readGitPatch(croppedPatch, &readOk, jobController);
- if (!readOk)
- fileDataList = readDiffPatch(croppedPatch, &readOk, jobController);
-
- if (ok)
- *ok = readOk;
-
- return fileDataList;
+ readGitPatch(promise, croppedPatch);
+ if (promise.future().resultCount() == 0)
+ readDiffPatch(promise, croppedPatch);
}
} // namespace DiffEditor
diff --git a/src/plugins/diffeditor/diffutils.h b/src/plugins/diffeditor/diffutils.h
index 65fcda4678..e128f01012 100644
--- a/src/plugins/diffeditor/diffutils.h
+++ b/src/plugins/diffeditor/diffutils.h
@@ -14,7 +14,8 @@
#include <array>
QT_BEGIN_NAMESPACE
-class QFutureInterfaceBase;
+template <class T>
+class QPromise;
QT_END_NAMESPACE
namespace Utils { class Diff; }
@@ -142,9 +143,8 @@ public:
const QString &rightFileName,
bool lastChunk = false);
static QString makePatch(const QList<FileData> &fileDataList);
- static QList<FileData> readPatch(const QString &patch,
- bool *ok = nullptr,
- QFutureInterfaceBase *jobController = nullptr);
+ static std::optional<QList<FileData>> readPatch(const QString &patch);
+ static void readPatchWithPromise(QPromise<QList<FileData>> &promise, const QString &patch);
};
} // namespace DiffEditor
diff --git a/src/plugins/diffeditor/diffview.cpp b/src/plugins/diffeditor/diffview.cpp
index 8ff444066a..686f9798b2 100644
--- a/src/plugins/diffeditor/diffview.cpp
+++ b/src/plugins/diffeditor/diffview.cpp
@@ -117,7 +117,6 @@ void UnifiedView::beginOperation()
DiffEditorDocument *document = m_widget->diffDocument();
if (document && document->state() == DiffEditorDocument::LoadOK)
m_widget->saveState();
- m_widget->clear(Tr::tr("Waiting for data..."));
}
void UnifiedView::setDiff(const QList<FileData> &diffFileList)
@@ -126,13 +125,15 @@ void UnifiedView::setDiff(const QList<FileData> &diffFileList)
m_widget->setDiff(diffFileList);
}
-void UnifiedView::endOperation(bool success)
+void UnifiedView::setMessage(const QString &message)
+{
+ m_widget->clear(message);
+}
+
+void UnifiedView::endOperation()
{
QTC_ASSERT(m_widget, return);
- if (success)
- m_widget->restoreState();
- else
- m_widget->clear(Tr::tr("Retrieving data failed."));
+ m_widget->restoreState();
}
void UnifiedView::setCurrentDiffFileIndex(int index)
@@ -197,7 +198,6 @@ void SideBySideView::beginOperation()
DiffEditorDocument *document = m_widget->diffDocument();
if (document && document->state() == DiffEditorDocument::LoadOK)
m_widget->saveState();
- m_widget->clear(Tr::tr("Waiting for data..."));
}
void SideBySideView::setCurrentDiffFileIndex(int index)
@@ -212,13 +212,16 @@ void SideBySideView::setDiff(const QList<FileData> &diffFileList)
m_widget->setDiff(diffFileList);
}
-void SideBySideView::endOperation(bool success)
+void SideBySideView::setMessage(const QString &message)
{
QTC_ASSERT(m_widget, return);
- if (success)
- m_widget->restoreState();
- else
- m_widget->clear(Tr::tr("Retrieving data failed."));
+ m_widget->clear(message);
+}
+
+void SideBySideView::endOperation()
+{
+ QTC_ASSERT(m_widget, return);
+ m_widget->restoreState();
}
void SideBySideView::setSync(bool sync)
diff --git a/src/plugins/diffeditor/diffview.h b/src/plugins/diffeditor/diffview.h
index b1e4f21227..9028f83c63 100644
--- a/src/plugins/diffeditor/diffview.h
+++ b/src/plugins/diffeditor/diffview.h
@@ -44,7 +44,8 @@ public:
virtual void beginOperation() = 0;
virtual void setCurrentDiffFileIndex(int index) = 0;
virtual void setDiff(const QList<FileData> &diffFileList) = 0;
- virtual void endOperation(bool success) = 0;
+ virtual void setMessage(const QString &message) = 0;
+ virtual void endOperation() = 0;
virtual void setSync(bool) = 0;
@@ -81,7 +82,8 @@ public:
void beginOperation() override;
void setCurrentDiffFileIndex(int index) override;
void setDiff(const QList<FileData> &diffFileList) override;
- void endOperation(bool success) override;
+ void setMessage(const QString &message) override;
+ void endOperation() override;
void setSync(bool sync) override;
@@ -104,7 +106,8 @@ public:
void beginOperation() override;
void setCurrentDiffFileIndex(int index) override;
void setDiff(const QList<FileData> &diffFileList) override;
- void endOperation(bool success) override;
+ void setMessage(const QString &message) override;
+ void endOperation() override;
void setSync(bool sync) override;
diff --git a/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp b/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp
index da69e60784..d2f58f83ac 100644
--- a/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp
+++ b/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp
@@ -5,30 +5,30 @@
#include "diffeditorconstants.h"
#include "diffeditordocument.h"
-#include "diffeditorplugin.h"
#include "diffeditortr.h"
-#include <QMenu>
-#include <QPainter>
-#include <QScrollBar>
-#include <QTextBlock>
-#include <QVBoxLayout>
-
-#include <coreplugin/icore.h>
#include <coreplugin/find/highlightscrollbarcontroller.h>
+#include <coreplugin/icore.h>
#include <coreplugin/minisplitter.h>
#include <coreplugin/progressmanager/progressmanager.h>
+#include <extensionsystem/pluginmanager.h>
+
#include <texteditor/displaysettings.h>
#include <texteditor/fontsettings.h>
#include <texteditor/textdocument.h>
#include <texteditor/textdocumentlayout.h>
#include <texteditor/texteditorsettings.h>
-#include <utils/asynctask.h>
+#include <utils/async.h>
#include <utils/mathutils.h>
#include <utils/tooltip/tooltip.h>
+#include <QMenu>
+#include <QPainter>
+#include <QScrollBar>
+#include <QVBoxLayout>
+
using namespace Core;
using namespace TextEditor;
using namespace Utils;
@@ -245,8 +245,8 @@ QString SideDiffEditorWidget::plainTextFromSelection(const QTextCursor &cursor)
return TextDocument::convertToPlainText(text);
}
-SideBySideDiffOutput SideDiffData::diffOutput(QFutureInterface<void> &fi, int progressMin,
- int progressMax, const DiffEditorInput &input)
+static SideBySideDiffOutput diffOutput(QPromise<SideBySideShowResults> &promise, int progressMin,
+ int progressMax, const DiffEditorInput &input)
{
SideBySideDiffOutput output;
@@ -366,8 +366,8 @@ SideBySideDiffOutput SideDiffData::diffOutput(QFutureInterface<void> &fi, int pr
diffText[RightSide].replace('\r', ' ');
output.side[LeftSide].diffText += diffText[LeftSide];
output.side[RightSide].diffText += diffText[RightSide];
- fi.setProgressValue(MathUtils::interpolateLinear(++i, 0, count, progressMin, progressMax));
- if (fi.isCanceled())
+ promise.setProgressValue(MathUtils::interpolateLinear(++i, 0, count, progressMin, progressMax));
+ if (promise.isCanceled())
return {};
}
output.side[LeftSide].selections = SelectableTextEditorWidget::polishedSelections(
@@ -869,16 +869,16 @@ void SideBySideDiffEditorWidget::restoreState()
void SideBySideDiffEditorWidget::showDiff()
{
- m_asyncTask.reset(new AsyncTask<ShowResults>());
- m_asyncTask->setFutureSynchronizer(DiffEditorPlugin::futureSynchronizer());
+ m_asyncTask.reset(new Async<SideBySideShowResults>());
+ m_asyncTask->setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
m_controller.setBusyShowing(true);
- connect(m_asyncTask.get(), &AsyncTaskBase::done, this, [this] {
+ connect(m_asyncTask.get(), &AsyncBase::done, this, [this] {
if (m_asyncTask->isCanceled() || !m_asyncTask->isResultAvailable()) {
for (SideDiffEditorWidget *editor : m_editor)
editor->clearAll(Tr::tr("Retrieving data failed."));
} else {
- const ShowResults results = m_asyncTask->result();
+ const SideBySideShowResults results = m_asyncTask->result();
m_editor[LeftSide]->setDiffData(results[LeftSide].diffData);
m_editor[RightSide]->setDiffData(results[RightSide].diffData);
TextDocumentPtr leftDoc(results[LeftSide].textDocument);
@@ -914,28 +914,23 @@ void SideBySideDiffEditorWidget::showDiff()
const DiffEditorInput input(&m_controller);
- auto getDocument = [input](QFutureInterface<ShowResults> &futureInterface) {
- auto cleanup = qScopeGuard([&futureInterface] {
- if (futureInterface.isCanceled())
- futureInterface.reportCanceled();
- });
+ auto getDocument = [input](QPromise<SideBySideShowResults> &promise) {
const int firstPartMax = 20; // diffOutput is about 4 times quicker than filling document
const int leftPartMax = 60;
const int rightPartMax = 100;
- futureInterface.setProgressRange(0, rightPartMax);
- futureInterface.setProgressValue(0);
- QFutureInterface<void> fi = futureInterface;
- const SideBySideDiffOutput output = SideDiffData::diffOutput(fi, 0, firstPartMax, input);
- if (futureInterface.isCanceled())
+ promise.setProgressRange(0, rightPartMax);
+ promise.setProgressValue(0);
+ const SideBySideDiffOutput output = diffOutput(promise, 0, firstPartMax, input);
+ if (promise.isCanceled())
return;
- const ShowResult leftResult{TextDocumentPtr(new TextDocument("DiffEditor.SideDiffEditor")),
+ const SideBySideShowResult leftResult{TextDocumentPtr(new TextDocument("DiffEditor.SideDiffEditor")),
output.side[LeftSide].diffData, output.side[LeftSide].selections};
- const ShowResult rightResult{TextDocumentPtr(new TextDocument("DiffEditor.SideDiffEditor")),
+ const SideBySideShowResult rightResult{TextDocumentPtr(new TextDocument("DiffEditor.SideDiffEditor")),
output.side[RightSide].diffData, output.side[RightSide].selections};
- const ShowResults result{leftResult, rightResult};
+ const SideBySideShowResults result{leftResult, rightResult};
- auto propagateDocument = [&output, &fi](DiffSide side, const ShowResult &result,
+ auto propagateDocument = [&output, &promise](DiffSide side, const SideBySideShowResult &result,
int progressMin, int progressMax) {
// No need to store the change history
result.textDocument->document()->setUndoRedoEnabled(false);
@@ -952,8 +947,9 @@ void SideBySideDiffEditorWidget::showDiff()
const QString package = output.side[side].diffText.mid(currentPos, packageSize);
cursor.insertText(package);
currentPos += package.size();
- fi.setProgressValue(MathUtils::interpolateLinear(currentPos, 0, diffSize, progressMin, progressMax));
- if (fi.isCanceled())
+ promise.setProgressValue(MathUtils::interpolateLinear(currentPos, 0, diffSize,
+ progressMin, progressMax));
+ if (promise.isCanceled())
return;
}
@@ -968,16 +964,16 @@ void SideBySideDiffEditorWidget::showDiff()
};
propagateDocument(LeftSide, leftResult, firstPartMax, leftPartMax);
- if (fi.isCanceled())
+ if (promise.isCanceled())
return;
propagateDocument(RightSide, rightResult, leftPartMax, rightPartMax);
- if (fi.isCanceled())
+ if (promise.isCanceled())
return;
- futureInterface.reportResult(result);
+ promise.addResult(result);
};
- m_asyncTask->setAsyncCallData(getDocument);
+ m_asyncTask->setConcurrentCallData(getDocument);
m_asyncTask->start();
ProgressManager::addTask(m_asyncTask->future(), Tr::tr("Rendering diff"), "DiffEditor");
}
diff --git a/src/plugins/diffeditor/sidebysidediffeditorwidget.h b/src/plugins/diffeditor/sidebysidediffeditorwidget.h
index 21225fa558..b56fea7da0 100644
--- a/src/plugins/diffeditor/sidebysidediffeditorwidget.h
+++ b/src/plugins/diffeditor/sidebysidediffeditorwidget.h
@@ -7,7 +7,6 @@
#include "diffeditorwidgetcontroller.h"
#include "selectabletexteditorwidget.h" // TODO: we need DiffSelections here only
-#include <QFutureInterface>
#include <QWidget>
namespace Core { class IContext; }
@@ -19,7 +18,7 @@ class TextEditorWidget;
namespace Utils {
template <typename R>
-class AsyncTask;
+class Async;
}
QT_BEGIN_NAMESPACE
@@ -40,9 +39,6 @@ class SideBySideDiffOutput;
class SideDiffData
{
public:
- static SideBySideDiffOutput diffOutput(QFutureInterface<void> &fi, int progressMin,
- int progressMax, const DiffEditorInput &input);
-
DiffChunkInfo m_chunkInfo;
// block number, fileInfo. Set for file lines only.
QMap<int, DiffFileInfo> m_fileInfo;
@@ -60,7 +56,6 @@ public:
int blockNumberForFileIndex(int fileIndex) const;
int fileIndexForBlockNumber(int blockNumber) const;
-private:
void setLineNumber(int blockNumber, int lineNumber);
void setFileInfo(int blockNumber, const DiffFileInfo &fileInfo);
void setSkippedLines(int blockNumber, int skippedLines, const QString &contextInfo = {}) {
@@ -88,6 +83,16 @@ public:
QHash<int, int> foldingIndent;
};
+class SideBySideShowResult
+{
+public:
+ QSharedPointer<TextEditor::TextDocument> textDocument{};
+ SideDiffData diffData;
+ DiffSelections selections;
+};
+
+using SideBySideShowResults = std::array<SideBySideShowResult, SideCount>;
+
class SideBySideDiffEditorWidget : public QWidget
{
Q_OBJECT
@@ -135,15 +140,7 @@ private:
bool m_horizontalSync = false;
- struct ShowResult
- {
- QSharedPointer<TextEditor::TextDocument> textDocument{};
- SideDiffData diffData;
- DiffSelections selections;
- };
- using ShowResults = std::array<ShowResult, SideCount>;
-
- std::unique_ptr<Utils::AsyncTask<ShowResults>> m_asyncTask;
+ std::unique_ptr<Utils::Async<SideBySideShowResults>> m_asyncTask;
};
} // namespace Internal
diff --git a/src/plugins/diffeditor/unifieddiffeditorwidget.cpp b/src/plugins/diffeditor/unifieddiffeditorwidget.cpp
index b937b919f7..bb64e61d5b 100644
--- a/src/plugins/diffeditor/unifieddiffeditorwidget.cpp
+++ b/src/plugins/diffeditor/unifieddiffeditorwidget.cpp
@@ -5,26 +5,24 @@
#include "diffeditorconstants.h"
#include "diffeditordocument.h"
-#include "diffeditorplugin.h"
#include "diffeditortr.h"
-#include <QMenu>
-#include <QPainter>
-#include <QScrollBar>
-#include <QTextBlock>
-
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
+#include <extensionsystem/pluginmanager.h>
+
#include <texteditor/fontsettings.h>
#include <texteditor/textdocument.h>
-#include <texteditor/textdocumentlayout.h>
#include <texteditor/texteditorsettings.h>
-#include <utils/asynctask.h>
+#include <utils/async.h>
#include <utils/mathutils.h>
#include <utils/qtcassert.h>
-#include <utils/tooltip/tooltip.h>
+
+#include <QMenu>
+#include <QScrollBar>
+#include <QTextBlock>
using namespace Core;
using namespace TextEditor;
@@ -48,10 +46,10 @@ UnifiedDiffEditorWidget::UnifiedDiffEditorWidget(QWidget *parent)
connect(this, &QPlainTextEdit::cursorPositionChanged,
this, &UnifiedDiffEditorWidget::slotCursorPositionChangedInEditor);
- auto context = new Core::IContext(this);
+ auto context = new IContext(this);
context->setWidget(this);
- context->setContext(Core::Context(Constants::UNIFIED_VIEW_ID));
- Core::ICore::addContextObject(context);
+ context->setContext(Context(Constants::UNIFIED_VIEW_ID));
+ ICore::addContextObject(context);
}
UnifiedDiffEditorWidget::~UnifiedDiffEditorWidget() = default;
@@ -68,6 +66,14 @@ DiffEditorDocument *UnifiedDiffEditorWidget::diffDocument() const
return m_controller.document();
}
+void UnifiedDiffEditorWidget::setDiff(const QList<FileData> &diffFileList)
+{
+ const GuardLocker locker(m_controller.m_ignoreChanges);
+ clear(Tr::tr("Waiting for data..."));
+ m_controller.m_contextFileData = diffFileList;
+ showDiff();
+}
+
void UnifiedDiffEditorWidget::saveState()
{
if (!m_state.isNull())
@@ -260,14 +266,6 @@ void UnifiedDiffData::setLineNumber(DiffSide side, int blockNumber, int lineNumb
m_lineNumberDigits[side] = qMax(m_lineNumberDigits[side], lineNumberString.count());
}
-void UnifiedDiffEditorWidget::setDiff(const QList<FileData> &diffFileList)
-{
- const GuardLocker locker(m_controller.m_ignoreChanges);
- clear(Tr::tr("Waiting for data..."));
- m_controller.m_contextFileData = diffFileList;
- showDiff();
-}
-
QString UnifiedDiffData::setChunk(const DiffEditorInput &input, const ChunkData &chunkData,
bool lastChunk, int *blockNumber, DiffSelections *selections)
{
@@ -391,8 +389,8 @@ QString UnifiedDiffData::setChunk(const DiffEditorInput &input, const ChunkData
return diffText;
}
-UnifiedDiffOutput UnifiedDiffData::diffOutput(QFutureInterface<void> &fi, int progressMin,
- int progressMax, const DiffEditorInput &input)
+static UnifiedDiffOutput diffOutput(QPromise<UnifiedShowResult> &promise, int progressMin,
+ int progressMax, const DiffEditorInput &input)
{
UnifiedDiffOutput output;
@@ -437,8 +435,8 @@ UnifiedDiffOutput UnifiedDiffData::diffOutput(QFutureInterface<void> &fi, int pr
output.diffData.m_chunkInfo.setChunkIndex(oldBlock, blockNumber - oldBlock, j);
}
}
- fi.setProgressValue(MathUtils::interpolateLinear(++i, 0, count, progressMin, progressMax));
- if (fi.isCanceled())
+ promise.setProgressValue(MathUtils::interpolateLinear(++i, 0, count, progressMin, progressMax));
+ if (promise.isCanceled())
return {};
}
@@ -454,14 +452,14 @@ void UnifiedDiffEditorWidget::showDiff()
return;
}
- m_asyncTask.reset(new AsyncTask<ShowResult>());
- m_asyncTask->setFutureSynchronizer(DiffEditorPlugin::futureSynchronizer());
+ m_asyncTask.reset(new Async<UnifiedShowResult>());
+ m_asyncTask->setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
m_controller.setBusyShowing(true);
- connect(m_asyncTask.get(), &AsyncTaskBase::done, this, [this] {
+ connect(m_asyncTask.get(), &AsyncBase::done, this, [this] {
if (m_asyncTask->isCanceled() || !m_asyncTask->isResultAvailable()) {
setPlainText(Tr::tr("Retrieving data failed."));
} else {
- const ShowResult result = m_asyncTask->result();
+ const UnifiedShowResult result = m_asyncTask->result();
m_data = result.diffData;
TextDocumentPtr doc(result.textDocument);
{
@@ -481,21 +479,16 @@ void UnifiedDiffEditorWidget::showDiff()
const DiffEditorInput input(&m_controller);
- auto getDocument = [input](QFutureInterface<ShowResult> &futureInterface) {
- auto cleanup = qScopeGuard([&futureInterface] {
- if (futureInterface.isCanceled())
- futureInterface.reportCanceled();
- });
+ auto getDocument = [input](QPromise<UnifiedShowResult> &promise) {
const int progressMax = 100;
const int firstPartMax = 20; // diffOutput is about 4 times quicker than filling document
- futureInterface.setProgressRange(0, progressMax);
- futureInterface.setProgressValue(0);
- QFutureInterface<void> fi = futureInterface;
- const UnifiedDiffOutput output = UnifiedDiffData::diffOutput(fi, 0, firstPartMax, input);
- if (futureInterface.isCanceled())
+ promise.setProgressRange(0, progressMax);
+ promise.setProgressValue(0);
+ const UnifiedDiffOutput output = diffOutput(promise, 0, firstPartMax, input);
+ if (promise.isCanceled())
return;
- const ShowResult result = {TextDocumentPtr(new TextDocument("DiffEditor.UnifiedDiffEditor")),
+ const UnifiedShowResult result = {TextDocumentPtr(new TextDocument("DiffEditor.UnifiedDiffEditor")),
output.diffData, output.selections};
// No need to store the change history
result.textDocument->document()->setUndoRedoEnabled(false);
@@ -512,8 +505,9 @@ void UnifiedDiffEditorWidget::showDiff()
const QString package = output.diffText.mid(currentPos, packageSize);
cursor.insertText(package);
currentPos += package.size();
- fi.setProgressValue(MathUtils::interpolateLinear(currentPos, 0, diffSize, firstPartMax, progressMax));
- if (futureInterface.isCanceled())
+ promise.setProgressValue(MathUtils::interpolateLinear(currentPos, 0, diffSize,
+ firstPartMax, progressMax));
+ if (promise.isCanceled())
return;
}
@@ -525,10 +519,10 @@ void UnifiedDiffEditorWidget::showDiff()
// to caller's thread. We push it to no thread (make object to have no thread affinity),
// and later, in the caller's thread, we pull it back to the caller's thread.
result.textDocument->moveToThread(nullptr);
- futureInterface.reportResult(result);
+ promise.addResult(result);
};
- m_asyncTask->setAsyncCallData(getDocument);
+ m_asyncTask->setConcurrentCallData(getDocument);
m_asyncTask->start();
ProgressManager::addTask(m_asyncTask->future(), Tr::tr("Rendering diff"), "DiffEditor");
}
diff --git a/src/plugins/diffeditor/unifieddiffeditorwidget.h b/src/plugins/diffeditor/unifieddiffeditorwidget.h
index 842eec9ea3..e303a75717 100644
--- a/src/plugins/diffeditor/unifieddiffeditorwidget.h
+++ b/src/plugins/diffeditor/unifieddiffeditorwidget.h
@@ -6,15 +6,13 @@
#include "diffeditorwidgetcontroller.h"
#include "selectabletexteditorwidget.h"
-#include <QFutureInterface>
-
namespace Core { class IContext; }
namespace TextEditor { class FontSettings; }
namespace Utils {
template <typename R>
-class AsyncTask;
+class Async;
}
namespace DiffEditor {
@@ -31,9 +29,6 @@ class UnifiedDiffOutput;
class UnifiedDiffData
{
public:
- static UnifiedDiffOutput diffOutput(QFutureInterface<void> &fi, int progressMin, int progressMax,
- const DiffEditorInput &input);
-
DiffChunkInfo m_chunkInfo;
// block number, visual line number.
QMap<int, DiffFileInfoArray> m_fileInfo;
@@ -45,10 +40,10 @@ public:
int blockNumberForFileIndex(int fileIndex) const;
int fileIndexForBlockNumber(int blockNumber) const;
-private:
- void setLineNumber(DiffSide side, int blockNumber, int lineNumber, int rowNumberInChunk);
QString setChunk(const DiffEditorInput &input, const ChunkData &chunkData,
bool lastChunk, int *blockNumber, DiffSelections *selections);
+private:
+ void setLineNumber(DiffSide side, int blockNumber, int lineNumber, int rowNumberInChunk);
};
class UnifiedDiffOutput
@@ -63,6 +58,14 @@ public:
DiffSelections selections;
};
+class UnifiedShowResult
+{
+public:
+ QSharedPointer<TextEditor::TextDocument> textDocument;
+ UnifiedDiffData diffData;
+ DiffSelections selections;
+};
+
class UnifiedDiffEditorWidget final : public SelectableTextEditorWidget
{
Q_OBJECT
@@ -105,14 +108,7 @@ private:
DiffEditorWidgetController m_controller;
QByteArray m_state;
- struct ShowResult
- {
- QSharedPointer<TextEditor::TextDocument> textDocument;
- UnifiedDiffData diffData;
- DiffSelections selections;
- };
-
- std::unique_ptr<Utils::AsyncTask<ShowResult>> m_asyncTask;
+ std::unique_ptr<Utils::Async<UnifiedShowResult>> m_asyncTask;
};
} // namespace Internal
diff --git a/src/plugins/docker/dockerapi.cpp b/src/plugins/docker/dockerapi.cpp
index 75256f9473..baa9ed6be9 100644
--- a/src/plugins/docker/dockerapi.cpp
+++ b/src/plugins/docker/dockerapi.cpp
@@ -6,9 +6,9 @@
#include "dockertr.h"
#include <coreplugin/progressmanager/progressmanager.h>
+#include <utils/async.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <QLoggingCategory>
@@ -35,7 +35,7 @@ DockerApi *DockerApi::instance()
bool DockerApi::canConnect()
{
- QtcProcess process;
+ Process process;
FilePath dockerExe = dockerClient();
if (dockerExe.isEmpty() || !dockerExe.isExecutableFile())
return false;
@@ -43,7 +43,7 @@ bool DockerApi::canConnect()
bool result = false;
process.setCommand(CommandLine(dockerExe, QStringList{"info"}));
- connect(&process, &QtcProcess::done, [&process, &result] {
+ connect(&process, &Process::done, [&process, &result] {
qCInfo(dockerApiLog) << "'docker info' result:\n" << qPrintable(process.allOutput());
if (process.result() == ProcessResult::FinishedWithSuccess)
result = true;
@@ -65,7 +65,7 @@ void DockerApi::checkCanConnect(bool async)
m_dockerDaemonAvailable = std::nullopt;
emit dockerDaemonAvailableChanged();
- auto future = Utils::runAsync([lk = std::move(lk), this] {
+ auto future = Utils::asyncRun([lk = std::move(lk), this] {
m_dockerDaemonAvailable = canConnect();
emit dockerDaemonAvailableChanged();
});
@@ -103,7 +103,7 @@ std::optional<bool> DockerApi::isDockerDaemonAvailable(bool async)
FilePath DockerApi::dockerClient()
{
- return FilePath::fromString(m_settings->dockerBinaryPath.value());
+ return m_settings->dockerBinaryPath();
}
} // Docker::Internal
diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp
index 2a955f131f..2dd2a589f7 100644
--- a/src/plugins/docker/dockerdevice.cpp
+++ b/src/plugins/docker/dockerdevice.cpp
@@ -17,6 +17,7 @@
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/devicesupport/devicemanager.h>
#include <projectexplorer/devicesupport/idevicewidget.h>
+#include <projectexplorer/devicesupport/processlist.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/project.h>
@@ -43,11 +44,12 @@
#include <utils/overridecursor.h>
#include <utils/pathlisteditor.h>
#include <utils/port.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/sortfiltermodel.h>
#include <utils/temporaryfile.h>
+#include <utils/terminalhooks.h>
#include <utils/treemodel.h>
#include <utils/utilsicons.h>
@@ -95,16 +97,16 @@ public:
{}
private:
- void setupShellProcess(QtcProcess *shellProcess) final
+ void setupShellProcess(Process *shellProcess) final
{
- shellProcess->setCommand({m_settings->dockerBinaryPath.filePath(),
+ shellProcess->setCommand({m_settings->dockerBinaryPath(),
{"container", "start", "-i", "-a", m_containerId}});
}
CommandLine createFallbackCommand(const CommandLine &cmdLine)
{
CommandLine result = cmdLine;
- result.setExecutable(cmdLine.executable().onDevice(m_devicePath));
+ result.setExecutable(m_devicePath.withNewPath(cmdLine.executable().path()));
return result;
}
@@ -124,7 +126,6 @@ public:
RunResult runInShell(const CommandLine &cmdLine,
const QByteArray &stdInData) const override;
QString mapToDevicePath(const QString &hostPath) const override;
- OsType osType(const FilePath &filePath) const override;
DockerDevicePrivate *m_dev = nullptr;
};
@@ -142,7 +143,7 @@ public:
RunResult runInShell(const CommandLine &cmd, const QByteArray &stdInData = {});
- void updateContainerAccess();
+ bool updateContainerAccess();
void changeMounts(QStringList newMounts);
bool ensureReachable(const FilePath &other);
void shutdown();
@@ -160,15 +161,17 @@ public:
Environment environment();
CommandLine withDockerExecCmd(const CommandLine &cmd,
- Environment *env = nullptr,
- FilePath *workDir = nullptr,
- bool interactive = false);
+ const std::optional<Environment> &env = std::nullopt,
+ const std::optional<FilePath> &workDir = std::nullopt,
+ bool interactive = false,
+ bool withPty = false,
+ bool withMarker = true);
bool prepareForBuild(const Target *target);
Tasks validateMounts() const;
bool createContainer();
- void startContainer();
+ bool startContainer();
void stopCurrentContainer();
void fetchSystemEnviroment();
@@ -183,6 +186,8 @@ public:
QStringList createMountArgs() const;
+ bool isImageAvailable() const;
+
DockerDevice *const q;
DockerDeviceData m_data;
DockerSettings *m_settings;
@@ -221,7 +226,7 @@ private:
// as this object is alive.
IDevice::ConstPtr m_device;
- QtcProcess m_process;
+ Process m_process;
qint64 m_remotePID = 0;
bool m_hasReceivedFirstOutput = false;
};
@@ -231,43 +236,66 @@ DockerProcessImpl::DockerProcessImpl(IDevice::ConstPtr device, DockerDevicePriva
, m_device(std::move(device))
, m_process(this)
{
- connect(&m_process, &QtcProcess::started, this, [this] {
+ connect(&m_process, &Process::started, this, [this] {
qCDebug(dockerDeviceLog) << "Process started:" << m_process.commandLine();
- });
- connect(&m_process, &QtcProcess::readyReadStandardOutput, this, [this] {
- if (!m_hasReceivedFirstOutput) {
- QByteArray output = m_process.readAllRawStandardOutput();
- qsizetype idx = output.indexOf('\n');
- QByteArray firstLine = output.left(idx);
- QByteArray rest = output.mid(idx + 1);
- qCDebug(dockerDeviceLog)
- << "Process first line received:" << m_process.commandLine() << firstLine;
- if (firstLine.startsWith("__qtc")) {
- bool ok = false;
- m_remotePID = firstLine.mid(5, firstLine.size() - 5 - 5).toLongLong(&ok);
-
- if (ok)
- emit started(m_remotePID);
-
- if (rest.size() > 0)
- emit readyRead(rest, {});
-
- m_hasReceivedFirstOutput = true;
- return;
- }
+ if (m_setup.m_ptyData.has_value()) {
+ m_hasReceivedFirstOutput = true;
+ emit started(m_process.processId(), m_process.applicationMainThreadId());
}
- emit readyRead(m_process.readAllRawStandardOutput(), {});
});
- connect(&m_process, &QtcProcess::readyReadStandardError, this, [this] {
- emit readyRead({}, m_process.readAllRawStandardError());
+ connect(&m_process, &Process::readyReadStandardOutput, this, [this] {
+ if (m_hasReceivedFirstOutput)
+ emit readyRead(m_process.readAllRawStandardOutput(), {});
+
+ QByteArray output = m_process.readAllRawStandardOutput();
+ qsizetype idx = output.indexOf('\n');
+ QByteArray firstLine = output.left(idx).trimmed();
+ QByteArray rest = output.mid(idx + 1);
+ qCDebug(dockerDeviceLog) << "Process first line received:" << m_process.commandLine()
+ << firstLine;
+
+ if (!firstLine.startsWith("__qtc"))
+ return;
+
+ bool ok = false;
+ m_remotePID = firstLine.mid(5, firstLine.size() - 5 - 5).toLongLong(&ok);
+
+ if (ok)
+ emit started(m_remotePID);
+
+ // In case we already received some error output, send it now.
+ const QByteArray stdErr = m_process.readAllRawStandardError();
+ if (rest.size() > 0 || stdErr.size() > 0)
+ emit readyRead(rest, stdErr);
+
+ m_hasReceivedFirstOutput = true;
});
- connect(&m_process, &QtcProcess::done, this, [this] {
+ connect(&m_process, &Process::readyReadStandardError, this, [this] {
+ if (m_remotePID)
+ emit readyRead({}, m_process.readAllRawStandardError());
+ });
+
+ connect(&m_process, &Process::done, this, [this] {
qCDebug(dockerDeviceLog) << "Process exited:" << m_process.commandLine()
<< "with code:" << m_process.resultData().m_exitCode;
- emit done(m_process.resultData());
+
+ Utils::ProcessResultData resultData = m_process.resultData();
+
+ if (m_remotePID == 0 && !m_hasReceivedFirstOutput) {
+ resultData.m_error = QProcess::FailedToStart;
+ qCWarning(dockerDeviceLog) << "Process failed to start:" << m_process.commandLine();
+ QByteArray stdOut = m_process.readAllRawStandardOutput();
+ QByteArray stdErr = m_process.readAllRawStandardError();
+ if (!stdOut.isEmpty())
+ qCWarning(dockerDeviceLog) << "stdout:" << stdOut;
+ if (!stdErr.isEmpty())
+ qCWarning(dockerDeviceLog) << "stderr:" << stdErr;
+ }
+
+ emit done(resultData);
});
}
@@ -282,23 +310,30 @@ void DockerProcessImpl::start()
m_process.setProcessImpl(m_setup.m_processImpl);
m_process.setProcessMode(m_setup.m_processMode);
m_process.setTerminalMode(m_setup.m_terminalMode);
+ m_process.setPtyData(m_setup.m_ptyData);
m_process.setReaperTimeout(m_setup.m_reaperTimeout);
m_process.setWriteData(m_setup.m_writeData);
m_process.setProcessChannelMode(m_setup.m_processChannelMode);
m_process.setExtraData(m_setup.m_extraData);
m_process.setStandardInputFile(m_setup.m_standardInputFile);
m_process.setAbortOnMetaChars(m_setup.m_abortOnMetaChars);
+ m_process.setCreateConsoleOnWindows(m_setup.m_createConsoleOnWindows);
if (m_setup.m_lowPriority)
m_process.setLowPriority();
+ const bool inTerminal = m_setup.m_terminalMode != TerminalMode::Off
+ || m_setup.m_ptyData.has_value();
+
const bool interactive = m_setup.m_processMode == ProcessMode::Writer
- || !m_setup.m_writeData.isEmpty();
+ || !m_setup.m_writeData.isEmpty() || inTerminal;
- const CommandLine fullCommandLine = m_devicePrivate
- ->withDockerExecCmd(m_setup.m_commandLine,
- &m_setup.m_environment,
- &m_setup.m_workingDirectory,
- interactive);
+ const CommandLine fullCommandLine
+ = m_devicePrivate->withDockerExecCmd(m_setup.m_commandLine,
+ m_setup.m_environment,
+ m_setup.m_workingDirectory,
+ interactive,
+ inTerminal,
+ !m_process.ptyData().has_value());
m_process.setCommand(fullCommandLine);
m_process.start();
@@ -311,14 +346,26 @@ qint64 DockerProcessImpl::write(const QByteArray &data)
void DockerProcessImpl::sendControlSignal(ControlSignal controlSignal)
{
- QTC_ASSERT(m_remotePID, return);
- if (controlSignal == ControlSignal::CloseWriteChannel) {
- m_process.closeWriteChannel();
- return;
+ if (!m_setup.m_ptyData.has_value()) {
+ QTC_ASSERT(m_remotePID, return);
+ if (controlSignal == ControlSignal::CloseWriteChannel) {
+ m_process.closeWriteChannel();
+ return;
+ }
+ const int signal = controlSignalToInt(controlSignal);
+ m_devicePrivate->runInShell(
+ {"kill", {QString("-%1").arg(signal), QString("%2").arg(m_remotePID)}});
+ } else {
+ // clang-format off
+ switch (controlSignal) {
+ case ControlSignal::Terminate: m_process.terminate(); break;
+ case ControlSignal::Kill: m_process.kill(); break;
+ case ControlSignal::Interrupt: m_process.interrupt(); break;
+ case ControlSignal::KickOff: m_process.kickoffProcess(); break;
+ case ControlSignal::CloseWriteChannel: break;
+ }
+ // clang-format on
}
- const int signal = controlSignalToInt(controlSignal);
- m_devicePrivate->runInShell(
- {"kill", {QString("-%1").arg(signal), QString("%2").arg(m_remotePID)}});
}
IDeviceWidget *DockerDevice::createWidget()
@@ -367,47 +414,40 @@ QString DockerDeviceFileAccess::mapToDevicePath(const QString &hostPath) const
return newPath;
}
-OsType DockerDeviceFileAccess::osType(const FilePath &filePath) const
-{
- QTC_ASSERT(m_dev, return UnixDeviceFileAccess::osType(filePath));
- return m_dev->q->osType();
-}
-
DockerDevice::DockerDevice(DockerSettings *settings, const DockerDeviceData &data)
: d(new DockerDevicePrivate(this, settings, data))
{
setFileAccess(&d->m_fileAccess);
setDisplayType(Tr::tr("Docker"));
- setOsType(OsTypeOtherUnix);
+ setOsType(OsTypeLinux);
setDefaultDisplayName(Tr::tr("Docker Image"));
-
+ setupId(IDevice::ManuallyAdded);
+ setType(Constants::DOCKER_DEVICE_TYPE);
+ setMachineType(IDevice::Hardware);
setDisplayName(Tr::tr("Docker Image \"%1\" (%2)").arg(data.repoAndTag()).arg(data.imageId));
setAllowEmptyCommand(true);
- setOpenTerminal([this, settings](const Environment &env, const FilePath &workingDir) {
+ setOpenTerminal([this](const Environment &env, const FilePath &workingDir) {
Q_UNUSED(env); // TODO: That's the runnable's environment in general. Use it via -e below.
- updateContainerAccess();
+ if (!updateContainerAccess())
+ return;
+
if (d->containerId().isEmpty()) {
MessageManager::writeDisrupting(Tr::tr("Error starting remote shell. No container."));
return;
}
- QtcProcess *proc = new QtcProcess(d);
- proc->setTerminalMode(TerminalMode::On);
+ Process proc;
+ proc.setTerminalMode(TerminalMode::Detached);
+ proc.setEnvironment(env);
+ proc.setWorkingDirectory(workingDir);
+ proc.setCommand({Terminal::defaultShellForDevice(rootPath()), {}});
+ proc.start();
- QObject::connect(proc, &QtcProcess::done, [proc] {
- if (proc->error() != QProcess::UnknownError && MessageManager::instance()) {
- MessageManager::writeDisrupting(
- Tr::tr("Error starting remote shell: %1").arg(proc->errorString()));
- }
- proc->deleteLater();
- });
-
- const QString wd = workingDir.isEmpty() ? "/" : workingDir.path();
- proc->setCommand({settings->dockerBinaryPath.filePath(),
- {"exec", "-it", "-w", wd, d->containerId(), "/bin/sh"}});
- proc->setEnvironment(Environment::systemEnvironment()); // The host system env. Intentional.
- proc->start();
+ if (proc.error() != QProcess::UnknownError && MessageManager::instance()) {
+ MessageManager::writeDisrupting(
+ Tr::tr("Error starting remote shell: %1").arg(proc.errorString()));
+ }
});
addDeviceAction({Tr::tr("Open Shell in Container"), [](const IDevice::Ptr &device, QWidget *) {
@@ -440,47 +480,58 @@ void DockerDevice::setData(const DockerDeviceData &data)
d->setData(data);
}
-void DockerDevice::updateContainerAccess() const
+bool DockerDevice::updateContainerAccess() const
{
- d->updateContainerAccess();
+ return d->updateContainerAccess();
}
CommandLine DockerDevicePrivate::withDockerExecCmd(const CommandLine &cmd,
- Environment *env,
- FilePath *workDir,
- bool interactive)
+ const std::optional<Environment> &env,
+ const std::optional<FilePath> &workDir,
+ bool interactive,
+ bool withPty,
+ bool withMarker)
{
if (!m_settings)
return {};
- updateContainerAccess();
+ if (!updateContainerAccess())
+ return {};
- CommandLine dockerCmd{m_settings->dockerBinaryPath.filePath(), {"exec"}};
+ CommandLine dockerCmd{m_settings->dockerBinaryPath(), {"exec"}};
if (interactive)
dockerCmd.addArg("-i");
+ if (withPty)
+ dockerCmd.addArg("-t");
+
if (env) {
- for (auto it = env->constBegin(); it != env->constEnd(); ++it) {
+ env->forEachEntry([&](const QString &key, const QString &value, bool) {
dockerCmd.addArg("-e");
- dockerCmd.addArg(env->key(it) + "=" + env->expandedValueForKey(env->key(it)));
- }
+ dockerCmd.addArg(key + "=" + env->expandVariables(value));
+ });
}
if (workDir && !workDir->isEmpty())
- dockerCmd.addArgs({"-w", workDir->path()});
+ dockerCmd.addArgs({"-w", q->rootPath().withNewMappedPath(*workDir).nativePath()});
dockerCmd.addArg(m_container);
+
dockerCmd.addArgs({"/bin/sh", "-c"});
CommandLine exec("exec");
- exec.addCommandLineAsArgs(cmd);
+ exec.addCommandLineAsArgs(cmd, CommandLine::Raw);
- CommandLine echo("echo");
- echo.addArgs("__qtc$$qtc__", CommandLine::Raw);
- echo.addCommandLineWithAnd(exec);
+ if (withMarker) {
+ CommandLine echo("echo");
+ echo.addArgs("__qtc$$qtc__", CommandLine::Raw);
+ echo.addCommandLineWithAnd(exec);
- dockerCmd.addCommandLineAsSingleArg(echo);
+ dockerCmd.addCommandLineAsSingleArg(echo);
+ } else {
+ dockerCmd.addCommandLineAsSingleArg(exec);
+ }
return dockerCmd;
}
@@ -494,10 +545,17 @@ void DockerDevicePrivate::stopCurrentContainer()
if (!DockerApi::isDockerDaemonAvailable(false).value_or(false))
return;
- m_shell.reset();
+ if (m_shell) {
+ // We have to disconnect the shell from the device, otherwise it will try to
+ // tell us about the container being stopped. Since that signal is emitted in a different
+ // thread, it would be delayed received by us when we might already have started
+ // a new shell.
+ m_shell->disconnect(this);
+ m_shell.reset();
+ }
- QtcProcess proc;
- proc.setCommand({m_settings->dockerBinaryPath.filePath(), {"container", "stop", m_container}});
+ Process proc;
+ proc.setCommand({m_settings->dockerBinaryPath(), {"container", "stop", m_container}});
m_container.clear();
@@ -598,14 +656,33 @@ QStringList DockerDevicePrivate::createMountArgs() const
return cmds;
}
+bool DockerDevicePrivate::isImageAvailable() const
+{
+ Process proc;
+ proc.setCommand(
+ {m_settings->dockerBinaryPath(),
+ {"image", "list", m_data.repoAndTag(), "--format", "{{.Repository}}:{{.Tag}}"}});
+ proc.runBlocking();
+ if (proc.result() != ProcessResult::FinishedWithSuccess)
+ return false;
+
+ if (proc.stdOut().trimmed() == m_data.repoAndTag())
+ return true;
+
+ return false;
+}
+
bool DockerDevicePrivate::createContainer()
{
if (!m_settings)
return false;
+ if (!isImageAvailable())
+ return false;
+
const QString display = HostOsInfo::isLinuxHost() ? QString(":0")
: QString("host.docker.internal:0");
- CommandLine dockerCreate{m_settings->dockerBinaryPath.filePath(),
+ CommandLine dockerCreate{m_settings->dockerBinaryPath(),
{"create",
"-i",
"--rm",
@@ -621,9 +698,6 @@ bool DockerDevicePrivate::createContainer()
if (m_data.useLocalUidGid)
dockerCreate.addArgs({"-u", QString("%1:%2").arg(getuid()).arg(getgid())});
#endif
- FilePath dumperPath = FilePath::fromString("/tmp/qtcreator/debugger");
- addTemporaryMount(Core::ICore::resourcePath("debugger/"), dumperPath);
- q->setDebugDumperPath(dumperPath);
dockerCreate.addArgs(createMountArgs());
@@ -636,7 +710,7 @@ bool DockerDevicePrivate::createContainer()
dockerCreate.addArg(m_data.repoAndTag());
qCDebug(dockerDeviceLog).noquote() << "RUNNING: " << dockerCreate.toUserOutput();
- QtcProcess createProcess;
+ Process createProcess;
createProcess.setCommand(dockerCreate);
createProcess.runBlocking();
@@ -655,20 +729,22 @@ bool DockerDevicePrivate::createContainer()
return true;
}
-void DockerDevicePrivate::startContainer()
+bool DockerDevicePrivate::startContainer()
{
if (!createContainer())
- return;
+ return false;
m_shell = std::make_unique<ContainerShell>(m_settings, m_container, q->rootPath());
connect(m_shell.get(), &DeviceShell::done, this, [this](const ProcessResultData &resultData) {
+ if (m_shell)
+ m_shell.release()->deleteLater();
+
if (resultData.m_error != QProcess::UnknownError
|| resultData.m_exitStatus == QProcess::NormalExit)
return;
qCWarning(dockerDeviceLog) << "Container shell encountered error:" << resultData.m_error;
- m_shell.release()->deleteLater();
DockerApi::recheckDockerDaemon();
MessageManager::writeFlashing(Tr::tr("Docker daemon appears to be not running. "
@@ -677,23 +753,25 @@ void DockerDevicePrivate::startContainer()
"or restart Qt Creator."));
});
- if (!m_shell->start()) {
- qCWarning(dockerDeviceLog) << "Container shell failed to start";
- }
+ if (m_shell->start())
+ return true;
+
+ qCWarning(dockerDeviceLog) << "Container shell failed to start";
+ return false;
}
-void DockerDevicePrivate::updateContainerAccess()
+bool DockerDevicePrivate::updateContainerAccess()
{
if (m_isShutdown)
- return;
+ return false;
if (DockerApi::isDockerDaemonAvailable(false).value_or(false) == false)
- return;
+ return false;
if (m_shell)
- return;
+ return true;
- startContainer();
+ return startContainer();
}
void DockerDevice::setMounts(const QStringList &mounts) const
@@ -750,36 +828,9 @@ ProcessInterface *DockerDevice::createProcessInterface() const
return new DockerProcessImpl(this->sharedFromThis(), d);
}
-bool DockerDevice::canAutoDetectPorts() const
+DeviceProcessList *DockerDevice::createProcessListModel(QObject *parent) const
{
- return true;
-}
-
-PortsGatheringMethod DockerDevice::portsGatheringMethod() const
-{
- return {[this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine {
- // We might encounter the situation that protocol is given IPv6
- // but the consumer of the free port information decides to open
- // an IPv4(only) port. As a result the next IPv6 scan will
- // report the port again as open (in IPv6 namespace), while the
- // same port in IPv4 namespace might still be blocked, and
- // re-use of this port fails.
- // GDBserver behaves exactly like this.
-
- Q_UNUSED(protocol)
-
- // /proc/net/tcp* covers /proc/net/tcp and /proc/net/tcp6
- return {filePath("sed"),
- "-e 's/.*: [[:xdigit:]]*:\\([[:xdigit:]]\\{4\\}\\).*/\\1/g' /proc/net/tcp*",
- CommandLine::Raw};
- },
-
- &Port::parseFromSedOutput};
-};
-
-DeviceProcessList *DockerDevice::createProcessListModel(QObject *) const
-{
- return nullptr;
+ return new ProcessList(sharedFromThis(), parent);
}
DeviceTester *DockerDevice::createDeviceTester() const
@@ -865,7 +916,8 @@ void DockerDevice::aboutToBeRemoved() const
void DockerDevicePrivate::fetchSystemEnviroment()
{
- updateContainerAccess();
+ if (!updateContainerAccess())
+ return;
if (m_shell && m_shell->state() == DeviceShell::State::Succeeded) {
const RunResult result = runInShell({"env", {}});
@@ -874,7 +926,7 @@ void DockerDevicePrivate::fetchSystemEnviroment()
return;
}
- QtcProcess proc;
+ Process proc;
proc.setCommand(withDockerExecCmd({"env", {}}));
proc.start();
proc.waitForFinished();
@@ -889,7 +941,8 @@ void DockerDevicePrivate::fetchSystemEnviroment()
RunResult DockerDevicePrivate::runInShell(const CommandLine &cmd, const QByteArray &stdInData)
{
- updateContainerAccess();
+ if (!updateContainerAccess())
+ return {};
QTC_ASSERT(m_shell, return {});
return m_shell->runInShell(cmd, stdInData);
}
@@ -985,6 +1038,7 @@ public:
using namespace Layouting;
+ // clang-format off
Column {
Stack {
statusLabel,
@@ -993,21 +1047,20 @@ public:
m_log,
errorLabel,
Row{showUnnamedContainers, m_buttons},
- }
- .attachTo(this);
-
+ }.attachTo(this);
+ // clang-format on
connect(m_buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(m_buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
m_buttons->button(QDialogButtonBox::Ok)->setEnabled(false);
- CommandLine cmd{m_settings->dockerBinaryPath.filePath(),
+ CommandLine cmd{m_settings->dockerBinaryPath(),
{"images", "--format", "{{.ID}}\\t{{.Repository}}\\t{{.Tag}}\\t{{.Size}}"}};
m_log->append(Tr::tr("Running \"%1\"\n").arg(cmd.toUserOutput()));
- m_process = new QtcProcess(this);
+ m_process = new Process(this);
m_process->setCommand(cmd);
- connect(m_process, &QtcProcess::readyReadStandardOutput, this, [this] {
+ connect(m_process, &Process::readyReadStandardOutput, this, [this] {
const QString out = m_process->readAllStandardOutput().trimmed();
m_log->append(out);
for (const QString &line : out.split('\n')) {
@@ -1026,12 +1079,12 @@ public:
m_log->append(Tr::tr("Done."));
});
- connect(m_process, &Utils::QtcProcess::readyReadStandardError, this, [this] {
+ connect(m_process, &Utils::Process::readyReadStandardError, this, [this] {
const QString out = Tr::tr("Error: %1").arg(m_process->cleanedStdErr());
m_log->append(Tr::tr("Error: %1").arg(out));
});
- connect(m_process, &QtcProcess::done, errorLabel, [errorLabel, this, statusLabel] {
+ connect(m_process, &Process::done, errorLabel, [errorLabel, this, statusLabel] {
delete statusLabel;
if (m_process->result() == ProcessResult::FinishedWithSuccess)
m_view->setEnabled(true);
@@ -1057,9 +1110,6 @@ public:
QTC_ASSERT(item, return {});
auto device = DockerDevice::create(m_settings, *item);
- device->setupId(IDevice::ManuallyAdded);
- device->setType(Constants::DOCKER_DEVICE_TYPE);
- device->setMachineType(IDevice::Hardware);
return device;
}
@@ -1072,7 +1122,7 @@ public:
QDialogButtonBox *m_buttons;
DockerSettings *m_settings;
- QtcProcess *m_process = nullptr;
+ Process *m_process = nullptr;
QString m_selectedId;
};
@@ -1186,16 +1236,25 @@ bool DockerDevicePrivate::ensureReachable(const FilePath &other)
const FilePath fMount = FilePath::fromString(mount);
if (other.isChildOf(fMount))
return true;
+
+ if (fMount == other)
+ return true;
}
for (const auto &[path, containerPath] : m_temporaryMounts) {
if (path.path() != containerPath.path())
continue;
+ if (path == other)
+ return true;
+
if (other.isChildOf(path))
return true;
}
+ if (q->filePath(other.path()).exists())
+ return false;
+
addTemporaryMount(other, other);
return true;
}
diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h
index 3ecc0118d4..bd0ce29d9d 100644
--- a/src/plugins/docker/dockerdevice.h
+++ b/src/plugins/docker/dockerdevice.h
@@ -73,9 +73,7 @@ public:
Utils::ProcessInterface *createProcessInterface() const override;
- bool canAutoDetectPorts() const override;
- ProjectExplorer::PortsGatheringMethod portsGatheringMethod() const override;
- bool canCreateProcessModel() const override { return false; }
+ bool canCreateProcessModel() const override { return true; }
ProjectExplorer::DeviceProcessList *createProcessListModel(QObject *parent) const override;
bool hasDeviceTester() const override { return false; }
ProjectExplorer::DeviceTester *createDeviceTester() const override;
@@ -95,7 +93,7 @@ public:
void setData(const DockerDeviceData &data);
- void updateContainerAccess() const;
+ bool updateContainerAccess() const;
void setMounts(const QStringList &mounts) const;
bool prepareForBuild(const ProjectExplorer::Target *target) override;
diff --git a/src/plugins/docker/dockerdevicewidget.cpp b/src/plugins/docker/dockerdevicewidget.cpp
index bc9c76f5b2..cd32fead6f 100644
--- a/src/plugins/docker/dockerdevicewidget.cpp
+++ b/src/plugins/docker/dockerdevicewidget.cpp
@@ -198,6 +198,8 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
logView->append(Tr::tr("Docker daemon appears to be not running."));
else
logView->append(Tr::tr("Docker daemon appears to be running."));
+
+ logView->append(Tr::tr("Detection complete."));
updateDaemonStateTexts();
});
@@ -245,7 +247,8 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
m_pathsListEdit,
}, br,
(dockerDevice->isAutoDetected() ? Column {} : std::move(detectionControls)),
- }.attachTo(this, WithoutMargins);
+ noMargin,
+ }.attachTo(this);
// clang-format on
searchDirsLineEdit->setVisible(false);
diff --git a/src/plugins/docker/dockerplugin.cpp b/src/plugins/docker/dockerplugin.cpp
index d7addc894d..7e47abd7b1 100644
--- a/src/plugins/docker/dockerplugin.cpp
+++ b/src/plugins/docker/dockerplugin.cpp
@@ -28,28 +28,17 @@ public:
DockerSettings m_settings;
DockerDeviceFactory m_deviceFactory{&m_settings};
- DockerSettingsPage m_settingPage{&m_settings};
DockerApi m_dockerApi{&m_settings};
};
-static DockerPlugin *s_instance = nullptr;
-
DockerPlugin::DockerPlugin()
{
- s_instance = this;
FSEngine::registerDeviceScheme(Constants::DOCKER_DEVICE_SCHEME);
}
-DockerApi *DockerPlugin::dockerApi()
-{
- QTC_ASSERT(s_instance, return nullptr);
- return &s_instance->d->m_dockerApi;
-}
-
DockerPlugin::~DockerPlugin()
{
FSEngine::unregisterDeviceScheme(Constants::DOCKER_DEVICE_SCHEME);
- s_instance = nullptr;
delete d;
}
diff --git a/src/plugins/docker/dockerplugin.h b/src/plugins/docker/dockerplugin.h
index c4ee04ac5a..267244709d 100644
--- a/src/plugins/docker/dockerplugin.h
+++ b/src/plugins/docker/dockerplugin.h
@@ -3,12 +3,8 @@
#pragma once
-#include "dockerapi.h"
-
#include <extensionsystem/iplugin.h>
-#include <optional>
-
namespace Docker::Internal {
class DockerPlugin final : public ExtensionSystem::IPlugin
@@ -19,8 +15,6 @@ class DockerPlugin final : public ExtensionSystem::IPlugin
public:
DockerPlugin();
- static DockerApi *dockerApi();
-
private:
~DockerPlugin() final;
diff --git a/src/plugins/docker/dockersettings.cpp b/src/plugins/docker/dockersettings.cpp
index bfec44f243..0643cb3a31 100644
--- a/src/plugins/docker/dockersettings.cpp
+++ b/src/plugins/docker/dockersettings.cpp
@@ -5,13 +5,10 @@
#include "dockerconstants.h"
#include "dockertr.h"
-#include "utils/hostosinfo.h"
-
-#include <coreplugin/icore.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <utils/filepath.h>
+#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
using namespace Utils;
@@ -21,7 +18,22 @@ namespace Docker::Internal {
DockerSettings::DockerSettings()
{
setSettingsGroup(Constants::DOCKER);
- setAutoApply(false);
+ setId(Docker::Constants::DOCKER_SETTINGS_ID);
+ setDisplayName(Tr::tr("Docker"));
+ setCategory(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY);
+
+ setLayouter([this] {
+ using namespace Layouting;
+ // clang-format off
+ return Column {
+ Group {
+ title(Tr::tr("Configuration")),
+ Row { dockerBinaryPath }
+ },
+ st
+ };
+ // clang-format on
+ });
FilePaths additionalPaths;
if (HostOsInfo::isWindowsHost())
@@ -29,8 +41,6 @@ DockerSettings::DockerSettings()
else
additionalPaths.append("/usr/local/bin");
- registerAspect(&dockerBinaryPath);
- dockerBinaryPath.setDisplayStyle(StringAspect::PathChooserDisplay);
dockerBinaryPath.setExpectedKind(PathChooser::ExistingCommand);
dockerBinaryPath.setDefaultFilePath(
FilePath::fromString("docker").searchInPath(additionalPaths));
@@ -39,32 +49,7 @@ DockerSettings::DockerSettings()
dockerBinaryPath.setLabelText(Tr::tr("Command:"));
dockerBinaryPath.setSettingsKey("cli");
- readSettings(Core::ICore::settings());
-}
-
-// DockerSettingsPage
-
-DockerSettingsPage::DockerSettingsPage(DockerSettings *settings)
-{
- setId(Docker::Constants::DOCKER_SETTINGS_ID);
- setDisplayName(Tr::tr("Docker"));
- setCategory(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY);
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- DockerSettings &s = *settings;
- using namespace Layouting;
-
- // clang-format off
- Column {
- Group {
- title(Tr::tr("Configuration")),
- Row { s.dockerBinaryPath }
- },
- st
- }.attachTo(widget);
- // clang-format on
- });
+ readSettings();
}
} // Docker::Internal
diff --git a/src/plugins/docker/dockersettings.h b/src/plugins/docker/dockersettings.h
index 4980a0a6b0..076acf6fa8 100644
--- a/src/plugins/docker/dockersettings.h
+++ b/src/plugins/docker/dockersettings.h
@@ -5,22 +5,14 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
-
namespace Docker::Internal {
-class DockerSettings final : public Utils::AspectContainer
+class DockerSettings final : public Core::PagedSettings
{
public:
DockerSettings();
- Utils::StringAspect dockerBinaryPath;
-};
-
-class DockerSettingsPage final : public Core::IOptionsPage
-{
-public:
- explicit DockerSettingsPage(DockerSettings *settings);
+ Utils::FilePathAspect dockerBinaryPath{this};
};
} // Docker::Internal
diff --git a/src/plugins/emacskeys/emacskeysplugin.cpp b/src/plugins/emacskeys/emacskeysplugin.cpp
index acb2be5b89..4f1fa8c2c8 100644
--- a/src/plugins/emacskeys/emacskeysplugin.cpp
+++ b/src/plugins/emacskeys/emacskeysplugin.cpp
@@ -135,11 +135,6 @@ void EmacsKeysPlugin::extensionsInitialized()
{
}
-ExtensionSystem::IPlugin::ShutdownFlag EmacsKeysPlugin::aboutToShutdown()
-{
- return SynchronousShutdown;
-}
-
void EmacsKeysPlugin::editorAboutToClose(IEditor *editor)
{
auto w = qobject_cast<QPlainTextEdit*>(editor->widget());
diff --git a/src/plugins/emacskeys/emacskeysplugin.h b/src/plugins/emacskeys/emacskeysplugin.h
index b0acb078cc..ab124b47ce 100644
--- a/src/plugins/emacskeys/emacskeysplugin.h
+++ b/src/plugins/emacskeys/emacskeysplugin.h
@@ -57,7 +57,6 @@ public:
void initialize() override;
void extensionsInitialized() override;
- ShutdownFlag aboutToShutdown() override;
private:
void editorAboutToClose(Core::IEditor *editor);
diff --git a/src/plugins/fakevim/fakevim.qbs b/src/plugins/fakevim/fakevim.qbs
index cdfb9e24dd..a4dad38428 100644
--- a/src/plugins/fakevim/fakevim.qbs
+++ b/src/plugins/fakevim/fakevim.qbs
@@ -25,9 +25,7 @@ QtcPlugin {
"fakevimtr.h",
]
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: ["fakevim_test.cpp"]
}
}
diff --git a/src/plugins/fakevim/fakevimactions.cpp b/src/plugins/fakevim/fakevimactions.cpp
index 7f7c44baf4..916fea1609 100644
--- a/src/plugins/fakevim/fakevimactions.cpp
+++ b/src/plugins/fakevim/fakevimactions.cpp
@@ -10,6 +10,13 @@
// Qt Creator. The idea is to keep this file here in a "clean" state that
// allows easy reuse with any QTextEdit or QPlainTextEdit derived class.
+#ifndef FAKEVIM_STANDALONE
+#include <texteditor/icodestylepreferences.h>
+#include <texteditor/tabsettings.h>
+#include <texteditor/texteditorsettings.h>
+#include <texteditor/typingsettings.h>
+#endif
+
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
#include <utils/qtcassert.h>
@@ -18,8 +25,7 @@
using namespace Utils;
-namespace FakeVim {
-namespace Internal {
+namespace FakeVim::Internal {
#ifdef FAKEVIM_STANDALONE
FvBaseAspect::FvBaseAspect()
@@ -62,13 +68,31 @@ QString FvBaseAspect::settingsKey() const
void setAutoApply(bool ) {}
#endif
+
+static FakeVimSettings *s_settings;
+
+FakeVimSettings &settings()
+{
+ return *s_settings;
+}
+
FakeVimSettings::FakeVimSettings()
{
- setAutoApply(false);
+ s_settings = this;
#ifndef FAKEVIM_STANDALONE
+ const char SETTINGS_CATEGORY[] = "D.FakeVim";
+ const char SETTINGS_ID[] = "A.FakeVim.General";
+
+ setId(SETTINGS_ID);
+ setDisplayName(Tr::tr("General"));
+ setCategory(SETTINGS_CATEGORY);
+ setDisplayCategory(Tr::tr("FakeVim"));
+ setCategoryIconPath(":/fakevim/images/settingscategory_fakevim.png");
+
setup(&useFakeVim, false, "UseFakeVim", {}, Tr::tr("Use FakeVim"));
#endif
+
// Specific FakeVim settings
setup(&readVimRc, false, "ReadVimRc", {}, Tr::tr("Read .vimrc from location:"));
setup(&vimRcPath, QString(), "VimRcPath", {}, {}); // Tr::tr("Path to .vimrc")
@@ -135,6 +159,121 @@ FakeVimSettings::FakeVimSettings()
"%USERPROFILE%\\_vimrc on Windows, ~/.vimrc otherwise."));
vimRcPath.setPlaceHolderText(Tr::tr("Default: %1").arg(vimrcDefault));
vimRcPath.setDisplayStyle(FvStringAspect::PathChooserDisplay);
+
+ setLayouter([this] {
+ using namespace Layouting;
+ using namespace TextEditor;
+
+ Row bools {
+ Column {
+ autoIndent,
+ smartIndent,
+ expandTab,
+ smartTab,
+ hlSearch,
+ showCmd,
+ startOfLine,
+ passKeys,
+ blinkingCursor
+ },
+ Column {
+ incSearch,
+ useCoreSearch,
+ ignoreCase,
+ smartCase,
+ wrapScan,
+ showMarks,
+ passControlKey,
+ relativeNumber,
+ tildeOp
+ }
+ };
+
+ Row ints { shiftWidth, tabStop, scrollOff, st };
+
+ vimRcPath.setEnabler(&readVimRc);
+
+ Column strings {
+ backspace,
+ isKeyword,
+ Row {readVimRc, vimRcPath}
+ };
+
+ return Column {
+ useFakeVim,
+
+ Group {
+ title(Tr::tr("Vim Behavior")),
+ Column {
+ bools,
+ ints,
+ strings
+ }
+ },
+
+ Group {
+ title(Tr::tr("Plugin Emulation")),
+ Column {
+ emulateVimCommentary,
+ emulateReplaceWithRegister,
+ emulateArgTextObj,
+ emulateExchange,
+ emulateSurround
+ }
+ },
+
+ Row {
+ PushButton {
+ text(Tr::tr("Copy Text Editor Settings")),
+ onClicked([this] {
+ TabSettings ts = TextEditorSettings::codeStyle()->tabSettings();
+ TypingSettings tps = TextEditorSettings::typingSettings();
+ expandTab.setValue(ts.m_tabPolicy != TabSettings::TabsOnlyTabPolicy);
+ tabStop.setValue(ts.m_tabSize);
+ shiftWidth.setValue(ts.m_indentSize);
+ smartTab.setValue(tps.m_smartBackspaceBehavior
+ == TypingSettings::BackspaceFollowsPreviousIndents);
+ autoIndent.setValue(true);
+ smartIndent.setValue(tps.m_autoIndent);
+ incSearch.setValue(true);
+ }),
+ },
+ PushButton {
+ text(Tr::tr("Set Qt Style")),
+ onClicked([this] {
+ expandTab.setVolatileValue(true);
+ tabStop.setVolatileValue(4);
+ shiftWidth.setVolatileValue(4);
+ smartTab.setVolatileValue(true);
+ autoIndent.setVolatileValue(true);
+ smartIndent.setVolatileValue(true);
+ incSearch.setVolatileValue(true);
+ backspace.setVolatileValue(QString("indent,eol,start"));
+ passKeys.setVolatileValue(true);
+ }),
+ },
+ PushButton {
+ text(Tr::tr("Set Plain Style")),
+ onClicked([this] {
+ expandTab.setVolatileValue(false);
+ tabStop.setVolatileValue(8);
+ shiftWidth.setVolatileValue(8);
+ smartTab.setVolatileValue(false);
+ autoIndent.setVolatileValue(false);
+ smartIndent.setVolatileValue(false);
+ incSearch.setVolatileValue(false);
+ backspace.setVolatileValue(QString());
+ passKeys.setVolatileValue(false);
+ }),
+ },
+ st
+ },
+ st
+ };
+ });
+
+ readSettings();
+
#endif
}
@@ -173,7 +312,7 @@ void FakeVimSettings::setup(FvBaseAspect *aspect,
registerAspect(aspect);
if (auto boolAspect = dynamic_cast<FvBoolAspect *>(aspect))
- boolAspect->setLabelPlacement(FvBoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ boolAspect->setLabelPlacement(FvBoolAspect::LabelPlacement::AtCheckBox);
#else
Q_UNUSED(labelText)
#endif
@@ -187,11 +326,4 @@ void FakeVimSettings::setup(FvBaseAspect *aspect,
m_nameToAspect[shortName] = aspect;
}
-FakeVimSettings *fakeVimSettings()
-{
- static FakeVimSettings s;
- return &s;
-}
-
-} // namespace Internal
-} // namespace FakeVim
+} // FakeVim::Internal
diff --git a/src/plugins/fakevim/fakevimactions.h b/src/plugins/fakevim/fakevimactions.h
index 6f7e303f0f..e3b14b0d47 100644
--- a/src/plugins/fakevim/fakevimactions.h
+++ b/src/plugins/fakevim/fakevimactions.h
@@ -4,7 +4,7 @@
#pragma once
#ifndef FAKEVIM_STANDALONE
-# include <utils/aspects.h>
+# include <coreplugin/dialogs/ioptionspage.h>
#endif
#include <QCoreApplication>
@@ -13,8 +13,7 @@
#include <QString>
#include <QVariant>
-namespace FakeVim {
-namespace Internal {
+namespace FakeVim::Internal {
#ifdef FAKEVIM_STANDALONE
class FvBaseAspect
@@ -44,18 +43,21 @@ class FvBoolAspect : public FvBaseAspect
{
public:
bool value() const { return FvBaseAspect::value().toBool(); }
+ bool operator()() const { return value(); }
};
class FvIntegerAspect : public FvBaseAspect
{
public:
qint64 value() const { return FvBaseAspect::value().toLongLong(); }
+ qint64 operator()() const { return value(); }
};
class FvStringAspect : public FvBaseAspect
{
public:
QString value() const { return FvBaseAspect::value().toString(); }
+ QString operator()() const { return value(); }
};
class FvAspectContainer : public FvBaseAspect
@@ -65,7 +67,7 @@ public:
#else
-using FvAspectContainer = Utils::AspectContainer;
+using FvAspectContainer = Core::PagedSettings;
using FvBaseAspect = Utils::BaseAspect;
using FvBoolAspect = Utils::BoolAspect;
using FvIntegerAspect = Utils::IntegerAspect;
@@ -142,7 +144,6 @@ private:
QHash<FvBaseAspect *, QString> m_aspectToName;
};
-FakeVimSettings *fakeVimSettings();
+FakeVimSettings &settings();
-} // namespace Internal
-} // namespace FakeVim
+} // FakeVim::Internal
diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp
index 6823965065..edfa315af9 100644
--- a/src/plugins/fakevim/fakevimhandler.cpp
+++ b/src/plugins/fakevim/fakevimhandler.cpp
@@ -36,8 +36,6 @@
#include "fakevimactions.h"
#include "fakevimtr.h"
-#include <utils/qtcprocess.h>
-
#include <QDebug>
#include <QFile>
#include <QObject>
@@ -410,9 +408,8 @@ static QRegularExpression vimPatternToQtPattern(const QString &needle)
*/
// FIXME: Option smartcase should be used only if search was typed by user.
- const bool ignoreCaseOption = fakeVimSettings()->ignoreCase.value();
- const bool smartCaseOption = fakeVimSettings()->smartCase.value();
- const bool initialIgnoreCase = ignoreCaseOption
+ const bool smartCaseOption = settings().smartCase();
+ const bool initialIgnoreCase = settings().ignoreCase()
&& !(smartCaseOption && needle.contains(QRegularExpression("[A-Z]")));
bool ignorecase = initialIgnoreCase;
@@ -833,29 +830,6 @@ static void setClipboardData(const QString &content, RangeMode mode,
clipboard->setMimeData(data, clipboardMode);
}
-static QByteArray toLocalEncoding(const QString &text)
-{
-#if defined(Q_OS_WIN)
- return QString(text).replace("\n", "\r\n").toLocal8Bit();
-#else
- return text.toLocal8Bit();
-#endif
-}
-
-static QString getProcessOutput(const QString &command, const QString &input)
-{
- Utils::QtcProcess proc;
- proc.setCommand(Utils::CommandLine::fromUserInput(command));
- proc.setWriteData(toLocalEncoding(input));
- proc.start();
-
- // FIXME: Process should be interruptable by user.
- // Solution is to create a QObject for each process and emit finished state.
- proc.waitForFinished();
-
- return proc.cleanedStdOut();
-}
-
static const QMap<QString, int> &vimKeyNames()
{
static const QMap<QString, int> k = {
@@ -1042,14 +1016,6 @@ inline QString msgMarkNotSet(const QString &text)
return Tr::tr("Mark \"%1\" not set.").arg(text);
}
-static void initSingleShotTimer(QTimer *timer, int interval, FakeVimHandler::Private *receiver,
- void (FakeVimHandler::Private::*slot)())
-{
- timer->setSingleShot(true);
- timer->setInterval(interval);
- QObject::connect(timer, &QTimer::timeout, receiver, slot);
-}
-
class Input
{
public:
@@ -2406,9 +2372,19 @@ public:
QString surroundFunction; // Used for storing the function name provided to ys{motion}f
} g;
- FakeVimSettings &s = *fakeVimSettings();
+ FakeVimSettings &s = settings();
};
+static void initSingleShotTimer(QTimer *timer,
+ int interval,
+ FakeVimHandler::Private *receiver,
+ void (FakeVimHandler::Private::*slot)())
+{
+ timer->setSingleShot(true);
+ timer->setInterval(interval);
+ QObject::connect(timer, &QTimer::timeout, receiver, slot);
+}
+
FakeVimHandler::Private::GlobalData FakeVimHandler::Private::g;
FakeVimHandler::Private::Private(FakeVimHandler *parent, QWidget *widget)
@@ -2550,7 +2526,7 @@ void FakeVimHandler::Private::leaveFakeVim(bool needUpdate)
// The command might have destroyed the editor.
if (m_textedit || m_plaintextedit) {
- if (s.showMarks.value())
+ if (s.showMarks())
updateSelection();
updateMiniBuffer();
@@ -2599,7 +2575,7 @@ bool FakeVimHandler::Private::wantsOverride(QKeyEvent *ev)
// We are interested in overriding most Ctrl key combinations.
if (isOnlyControlModifier(mods)
- && !s.passControlKey.value()
+ && !s.passControlKey()
&& ((key >= Key_A && key <= Key_Z && key != Key_K)
|| key == Key_BracketLeft || key == Key_BracketRight)) {
// Ctrl-K is special as it is the Core's default notion of Locator
@@ -2809,7 +2785,7 @@ void FakeVimHandler::Private::ensureCursorVisible()
void FakeVimHandler::Private::updateEditor()
{
- setTabSize(s.tabStop.value());
+ setTabSize(s.tabStop());
setupCharClass();
}
@@ -3082,7 +3058,7 @@ void FakeVimHandler::Private::stopIncrementalFind()
void FakeVimHandler::Private::updateFind(bool isComplete)
{
- if (!isComplete && !s.incSearch.value())
+ if (!isComplete && !s.incSearch())
return;
g.currentMessage.clear();
@@ -3184,7 +3160,7 @@ void FakeVimHandler::Private::pushUndoState(bool overwrite)
pos = firstPositionInLine(lineForPosition(pos));
else if (isVisualBlockMode())
pos = blockAt(pos).position() + qMin(columnAt(anchor()), columnAt(position()));
- } else if (g.movetype == MoveLineWise && s.startOfLine.value()) {
+ } else if (g.movetype == MoveLineWise && s.startOfLine()) {
QTextCursor tc = m_cursor;
if (g.submode == ShiftLeftSubMode || g.submode == ShiftRightSubMode
|| g.submode == IndentSubMode) {
@@ -3644,7 +3620,7 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement)
return;
} else if (g.submode == ExchangeSubMode) {
exchangeRange(currentRange());
- } else if (g.submode == ReplaceWithRegisterSubMode && s.emulateReplaceWithRegister.value()) {
+ } else if (g.submode == ReplaceWithRegisterSubMode && s.emulateReplaceWithRegister()) {
pushUndoState(false);
beginEditBlock();
replaceWithRegister(currentRange());
@@ -3757,7 +3733,7 @@ void FakeVimHandler::Private::clearCurrentMode()
void FakeVimHandler::Private::updateSelection()
{
QList<QTextEdit::ExtraSelection> selections = m_extraSelections;
- if (s.showMarks.value()) {
+ if (s.showMarks()) {
for (auto it = m_buffer->marks.cbegin(), end = m_buffer->marks.cend(); it != end; ++it) {
QTextEdit::ExtraSelection sel;
sel.cursor = m_cursor;
@@ -3776,7 +3752,7 @@ void FakeVimHandler::Private::updateSelection()
void FakeVimHandler::Private::updateHighlights()
{
- if (s.useCoreSearch.value() || !s.hlSearch.value() || g.highlightsCleared) {
+ if (s.useCoreSearch() || !s.hlSearch() || g.highlightsCleared) {
if (m_highlighted.isEmpty())
return;
m_highlighted.clear();
@@ -3823,7 +3799,7 @@ void FakeVimHandler::Private::updateMiniBuffer()
} else if (!g.mapStates.isEmpty() && !g.mapStates.last().silent) {
// Do not reset previous message when after running a mapped command.
return;
- } else if (g.mode == CommandMode && !g.currentCommand.isEmpty() && s.showCmd.value()) {
+ } else if (g.mode == CommandMode && !g.currentCommand.isEmpty() && s.showCmd()) {
msg = g.currentCommand;
messageLevel = MessageShowCmd;
} else if (g.mode == CommandMode && isVisualMode()) {
@@ -3930,7 +3906,7 @@ bool FakeVimHandler::Private::handleCommandSubSubMode(const Input &input)
handled = selectBlockTextObject(g.subsubdata.is('i'), '{', '}');
else if (input.is('"') || input.is('\'') || input.is('`'))
handled = selectQuotedStringTextObject(g.subsubdata.is('i'), input.asChar());
- else if (input.is('a') && s.emulateArgTextObj.value())
+ else if (input.is('a') && s.emulateArgTextObj())
handled = selectArgumentTextObject(g.subsubdata.is('i'));
else
handled = false;
@@ -4073,7 +4049,7 @@ bool FakeVimHandler::Private::handleMovement(const Input &input)
g.subsubmode = NoSubSubMode;
} else if (input.is('/') || input.is('?')) {
g.lastSearchForward = input.is('/');
- if (s.useCoreSearch.value()) {
+ if (s.useCoreSearch()) {
// re-use the core dialog.
g.findPending = true;
m_findStartPosition = position();
@@ -4248,7 +4224,7 @@ bool FakeVimHandler::Private::handleMovement(const Input &input)
m_cursor = EDITOR(cursorForPosition(QPoint(0, EDITOR(height()) / 2)));
handleStartOfLine();
} else if (input.is('n') || input.is('N')) {
- if (s.useCoreSearch.value()) {
+ if (s.useCoreSearch()) {
bool forward = (input.is('n')) ? g.lastSearchForward : !g.lastSearchForward;
int pos = position();
q->findNextRequested(!forward);
@@ -4337,7 +4313,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
handled = handleNoSubMode(input);
} else if (g.submode == ExchangeSubMode) {
handled = handleExchangeSubMode(input);
- } else if (g.submode == ChangeSubMode && input.is('x') && s.emulateExchange.value()) {
+ } else if (g.submode == ChangeSubMode && input.is('x') && s.emulateExchange()) {
// Exchange submode is "cx", so we need to switch over from ChangeSubMode here
g.submode = ExchangeSubMode;
handled = true;
@@ -4347,15 +4323,15 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
} else if (g.submode == AddSurroundingSubMode) {
handled = handleAddSurroundingSubMode(input);
} else if (g.submode == ChangeSubMode && (input.is('s') || input.is('S'))
- && s.emulateSurround.value()) {
+ && s.emulateSurround()) {
g.submode = ChangeSurroundingSubMode;
g.surroundUpperCaseS = input.is('S');
handled = true;
- } else if (g.submode == DeleteSubMode && input.is('s') && s.emulateSurround.value()) {
+ } else if (g.submode == DeleteSubMode && input.is('s') && s.emulateSurround()) {
g.submode = DeleteSurroundingSubMode;
handled = true;
} else if (g.submode == YankSubMode && (input.is('s') || input.is('S'))
- && s.emulateSurround.value()) {
+ && s.emulateSurround()) {
g.submode = AddSurroundingSubMode;
g.movetype = MoveInclusive;
g.surroundUpperCaseS = input.is('S');
@@ -4364,10 +4340,9 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
|| g.submode == DeleteSubMode
|| g.submode == YankSubMode) {
handled = handleChangeDeleteYankSubModes(input);
- } else if (g.submode == CommentSubMode && s.emulateVimCommentary.value()) {
+ } else if (g.submode == CommentSubMode && s.emulateVimCommentary()) {
handled = handleCommentSubMode(input);
- } else if (g.submode == ReplaceWithRegisterSubMode
- && s.emulateReplaceWithRegister.value()) {
+ } else if (g.submode == ReplaceWithRegisterSubMode && s.emulateReplaceWithRegister()) {
handled = handleReplaceWithRegisterSubMode(input);
} else if (g.submode == ReplaceSubMode) {
handled = handleReplaceSubMode(input);
@@ -4510,7 +4485,7 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input)
setTargetColumn();
} else if (input.isControl('a')) {
changeNumberTextObject(count());
- } else if (g.gflag && input.is('c') && s.emulateVimCommentary.value()) {
+ } else if (g.gflag && input.is('c') && s.emulateVimCommentary()) {
if (isVisualMode()) {
pushUndoState();
@@ -4535,7 +4510,7 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input)
pushUndoState();
setAnchor();
}
- } else if (g.gflag && input.is('r') && s.emulateReplaceWithRegister.value()) {
+ } else if (g.gflag && input.is('r') && s.emulateReplaceWithRegister()) {
g.submode = ReplaceWithRegisterSubMode;
if (isVisualMode()) {
dotCommand = visualDotCommand() + QString::number(count()) + "gr";
@@ -4694,7 +4669,7 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input)
int repeat = count();
while (--repeat >= 0)
redo();
- } else if (input.is('S') && isVisualMode() && s.emulateSurround.value()) {
+ } else if (input.is('S') && isVisualMode() && s.emulateSurround()) {
g.submode = AddSurroundingSubMode;
g.subsubmode = SurroundSubSubMode;
} else if (input.is('s')) {
@@ -4779,7 +4754,7 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input)
if (isVisualMode()) {
leaveVisualMode();
finishMovement();
- } else if (g.gflag || (g.submode == InvertCaseSubMode && s.tildeOp.value())) {
+ } else if (g.gflag || (g.submode == InvertCaseSubMode && s.tildeOp())) {
if (atEndOfLine())
moveLeft();
setAnchor();
@@ -5197,6 +5172,7 @@ void FakeVimHandler::Private::handleReplaceMode(const Input &input)
moveDown();
} else if (input.isKey(Key_Insert)) {
g.mode = InsertMode;
+ q->modeChanged(isInsertMode());
} else if (input.isControl('o')) {
enterCommandMode(ReplaceMode);
} else {
@@ -5394,6 +5370,7 @@ void FakeVimHandler::Private::handleInsertMode(const Input &input)
removeText(range);
} else if (input.isKey(Key_Insert)) {
g.mode = ReplaceMode;
+ q->modeChanged(isInsertMode());
} else if (input.isKey(Key_Left)) {
moveLeft();
} else if (input.isShift(Key_Left) || input.isControl(Key_Left)) {
@@ -5425,15 +5402,15 @@ void FakeVimHandler::Private::handleInsertMode(const Input &input)
if (!handleInsertInEditor(Input(Qt::Key_Backspace, Qt::NoModifier))) {
joinPreviousEditBlock();
if (!m_buffer->lastInsertion.isEmpty()
- || s.backspace.value().contains("start")
- || s.backspace.value().contains("2")) {
+ || s.backspace().contains("start")
+ || s.backspace().contains("2")) {
const int line = cursorLine() + 1;
const Column col = cursorColumn();
QString data = lineContents(line);
const Column ind = indentation(data);
if (col.logical <= ind.logical && col.logical
&& startsWithWhitespace(data, col.physical)) {
- const int ts = s.tabStop.value();
+ const int ts = s.tabStop();
const int newl = col.logical - 1 - (col.logical - 1) % ts;
const QString prefix = tabExpand(newl);
setLineContents(line, prefix + data.mid(col.physical));
@@ -5457,20 +5434,22 @@ void FakeVimHandler::Private::handleInsertMode(const Input &input)
} else if (input.isKey(Key_PageUp) || input.isControl('b')) {
movePageUp();
} else if (input.isKey(Key_Tab)) {
- m_buffer->insertState.insertingSpaces = true;
- if (s.expandTab.value()) {
- const int ts = s.tabStop.value();
- const int col = logicalCursorColumn();
- QString str = QString(ts - col % ts, ' ');
- insertText(str);
- } else {
- insertInInsertMode(input.raw());
+ if (q->tabPressedInInsertMode()) {
+ m_buffer->insertState.insertingSpaces = true;
+ if (s.expandTab()) {
+ const int ts = s.tabStop();
+ const int col = logicalCursorColumn();
+ QString str = QString(ts - col % ts, ' ');
+ insertText(str);
+ } else {
+ insertInInsertMode(input.raw());
+ }
+ m_buffer->insertState.insertingSpaces = false;
}
- m_buffer->insertState.insertingSpaces = false;
} else if (input.isControl('d')) {
// remove one level of indentation from the current line
- const int shift = s.shiftWidth.value();
- const int tab = s.tabStop.value();
+ const int shift = s.shiftWidth();
+ const int tab = s.tabStop();
int line = cursorLine() + 1;
int pos = firstPositionInLine(line);
QString text = lineContents(line);
@@ -5513,7 +5492,7 @@ void FakeVimHandler::Private::insertInInsertMode(const QString &text)
{
joinPreviousEditBlock();
insertText(text);
- if (s.smartIndent.value() && isElectricCharacter(text.at(0))) {
+ if (s.smartIndent() && isElectricCharacter(text.at(0))) {
const QString leftText = block().text()
.left(position() - 1 - block().position());
if (leftText.simplified().isEmpty()) {
@@ -6284,7 +6263,7 @@ bool FakeVimHandler::Private::handleExMoveCommand(const ExCommand &cmd)
if (!insertAtEnd)
moveUp(1);
- if (s.startOfLine.value())
+ if (s.startOfLine())
moveToFirstNonBlankOnLine();
if (lastAnchor.line >= startLine && lastAnchor.line <= endLine)
@@ -6428,7 +6407,8 @@ bool FakeVimHandler::Private::handleExBangCommand(const ExCommand &cmd) // :!
const QString command = QString(cmd.cmd.mid(1) + ' ' + cmd.args).trimmed();
const QString input = replaceText ? selectText(cmd.range) : QString();
- const QString result = getProcessOutput(command, input);
+ QString result;
+ q->processOutput(command, input, &result);
if (replaceText) {
setCurrentRange(cmd.range);
@@ -6812,7 +6792,7 @@ QTextCursor FakeVimHandler::Private::search(const SearchData &sd, int startPos,
}
if (tc.isNull()) {
- if (s.wrapScan.value()) {
+ if (s.wrapScan()) {
tc = QTextCursor(document());
tc.movePosition(sd.forward ? StartOfDocument : EndOfDocument);
if (sd.forward)
@@ -6972,10 +6952,10 @@ void FakeVimHandler::Private::shiftRegionRight(int repeat)
std::swap(beginLine, endLine);
targetPos = position();
}
- if (s.startOfLine.value())
+ if (s.startOfLine())
targetPos = firstPositionInLine(beginLine);
- const int sw = s.shiftWidth.value();
+ const int sw = s.shiftWidth();
g.movetype = MoveLineWise;
beginEditBlock();
QTextBlock block = document()->findBlockByLineNumber(beginLine - 1);
@@ -7124,7 +7104,7 @@ void FakeVimHandler::Private::setupCharClass()
const QChar c = QLatin1Char(i);
m_charClass[i] = c.isSpace() ? 0 : 1;
}
- const QString conf = s.isKeyword.value();
+ const QString conf = s.isKeyword();
for (const QString &part : conf.split(',')) {
if (part.contains('-')) {
const int from = someInt(part.section('-', 0, 0));
@@ -7313,7 +7293,7 @@ int FakeVimHandler::Private::physicalCursorColumn() const
int FakeVimHandler::Private::physicalToLogicalColumn
(const int physical, const QString &line) const
{
- const int ts = s.tabStop.value();
+ const int ts = s.tabStop();
int p = 0;
int logical = 0;
while (p < physical) {
@@ -7334,7 +7314,7 @@ int FakeVimHandler::Private::physicalToLogicalColumn
int FakeVimHandler::Private::logicalToPhysicalColumn
(const int logical, const QString &line) const
{
- const int ts = s.tabStop.value();
+ const int ts = s.tabStop();
int physical = 0;
for (int l = 0; l < logical && physical < line.size(); ++physical) {
QChar c = line.at(physical);
@@ -7348,7 +7328,7 @@ int FakeVimHandler::Private::logicalToPhysicalColumn
int FakeVimHandler::Private::windowScrollOffset() const
{
- return qMin(static_cast<int>(s.scrollOff.value()), linesOnScreen() / 2);
+ return qMin(static_cast<int>(s.scrollOff()), linesOnScreen() / 2);
}
int FakeVimHandler::Private::logicalCursorColumn() const
@@ -7612,7 +7592,7 @@ void FakeVimHandler::Private::transformText(const Range &range, const Transforma
void FakeVimHandler::Private::insertText(QTextCursor &tc, const QString &text)
{
- if (s.passKeys.value()) {
+ if (s.passKeys()) {
if (tc.hasSelection() && text.isEmpty()) {
QKeyEvent event(QEvent::KeyPress, Qt::Key_Delete, Qt::NoModifier, QString());
passEventToEditor(event, tc);
@@ -7955,7 +7935,7 @@ void FakeVimHandler::Private::joinLines(int count, bool preserveSpace)
moveRight();
// If the line we started from is a comment, remove the comment string from the next line
- if (startingLineIsComment && s.formatOptions.value().contains('f')) {
+ if (startingLineIsComment && s.formatOptions().contains('f')) {
if (characterAtCursor() == '/' && characterAt(position() + 1) == '/')
moveRight(2);
else if (characterAtCursor() == '*' || characterAtCursor() == '#')
@@ -7973,7 +7953,7 @@ void FakeVimHandler::Private::joinLines(int count, bool preserveSpace)
void FakeVimHandler::Private::insertNewLine()
{
- if (m_buffer->editBlockLevel <= 1 && s.passKeys.value()) {
+ if (m_buffer->editBlockLevel <= 1 && s.passKeys()) {
QKeyEvent event(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, "\n");
if (passEventToEditor(event, m_cursor))
return;
@@ -7985,7 +7965,7 @@ void FakeVimHandler::Private::insertNewLine()
bool FakeVimHandler::Private::handleInsertInEditor(const Input &input)
{
- if (m_buffer->editBlockLevel > 0 || !s.passKeys.value())
+ if (m_buffer->editBlockLevel > 0 || !s.passKeys())
return false;
joinPreviousEditBlock();
@@ -8575,6 +8555,8 @@ void FakeVimHandler::Private::enterInsertOrReplaceMode(Mode mode)
g.returnToMode = mode;
clearLastInsertion();
}
+
+ q->modeChanged(isInsertMode());
}
void FakeVimHandler::Private::enterVisualInsertMode(QChar command)
@@ -8650,6 +8632,8 @@ void FakeVimHandler::Private::enterCommandMode(Mode returnToMode)
g.returnToMode = returnToMode;
m_positionPastEnd = false;
m_anchorPastEnd = false;
+
+ q->modeChanged(isInsertMode());
}
void FakeVimHandler::Private::enterExMode(const QString &contents)
@@ -8664,6 +8648,8 @@ void FakeVimHandler::Private::enterExMode(const QString &contents)
g.submode = NoSubMode;
g.subsubmode = NoSubSubMode;
unfocus();
+
+ q->modeChanged(isInsertMode());
}
void FakeVimHandler::Private::recordJump(int position)
@@ -8696,7 +8682,7 @@ void FakeVimHandler::Private::jump(int distance)
Column FakeVimHandler::Private::indentation(const QString &line) const
{
- int ts = s.tabStop.value();
+ int ts = s.tabStop();
int physical = 0;
int logical = 0;
int n = line.size();
@@ -8715,8 +8701,8 @@ Column FakeVimHandler::Private::indentation(const QString &line) const
QString FakeVimHandler::Private::tabExpand(int n) const
{
- int ts = s.tabStop.value();
- if (s.expandTab.value() || ts < 1)
+ int ts = s.tabStop();
+ if (s.expandTab() || ts < 1)
return QString(n, ' ');
return QString(n / ts, '\t')
+ QString(n % ts, ' ');
@@ -8724,10 +8710,10 @@ QString FakeVimHandler::Private::tabExpand(int n) const
void FakeVimHandler::Private::insertAutomaticIndentation(bool goingDown, bool forceAutoIndent)
{
- if (!forceAutoIndent && !s.autoIndent.value() && !s.smartIndent.value())
+ if (!forceAutoIndent && !s.autoIndent() && !s.smartIndent())
return;
- if (s.smartIndent.value()) {
+ if (s.smartIndent()) {
QTextBlock bl = block();
Range range(bl.position(), bl.position());
indentText(range, '\n');
@@ -8746,7 +8732,7 @@ void FakeVimHandler::Private::insertAutomaticIndentation(bool goingDown, bool fo
void FakeVimHandler::Private::handleStartOfLine()
{
- if (s.startOfLine.value())
+ if (s.startOfLine())
moveToFirstNonBlankOnLine();
}
@@ -9343,7 +9329,7 @@ void FakeVimHandler::Private::getRegisterType(int *reg, bool *isClipboard, bool
*reg = c.toLower().unicode();
if (c == '"') {
- QStringList list = s.clipboard.value().split(',');
+ QStringList list = s.clipboard().split(',');
clipboard = list.contains("unnamedplus");
selection = list.contains("unnamed");
} else if (c == '+') {
@@ -9397,7 +9383,7 @@ void FakeVimHandler::updateGlobalMarksFilenames(const QString &oldFileName, cons
bool FakeVimHandler::eventFilter(QObject *ob, QEvent *ev)
{
#ifndef FAKEVIM_STANDALONE
- if (!fakeVimSettings()->useFakeVim.value())
+ if (!settings().useFakeVim())
return QObject::eventFilter(ob, ev);
#endif
@@ -9558,6 +9544,11 @@ bool FakeVimHandler::jumpToLocalMark(QChar mark, bool backTickMode)
return d->jumpToMark(mark, backTickMode);
}
+bool FakeVimHandler::inFakeVimMode()
+{
+ return d->m_inFakeVim;
+}
+
} // namespace Internal
} // namespace FakeVim
diff --git a/src/plugins/fakevim/fakevimhandler.h b/src/plugins/fakevim/fakevimhandler.h
index 57955cd2cb..0b615e1657 100644
--- a/src/plugins/fakevim/fakevimhandler.h
+++ b/src/plugins/fakevim/fakevimhandler.h
@@ -61,23 +61,30 @@ enum MessageLevel
MessageShowCmd // partial command
};
-template <typename Type>
-class Signal
+template<typename>
+class Callback;
+
+template<typename R, typename... Params>
+class Callback<R(Params...)>
{
public:
- using Callable = std::function<Type>;
-
- void connect(const Callable &callable) { m_callables.push_back(callable); }
+ static constexpr auto IsVoidReturnType = std::is_same_v<R, void>;
+ using Function = std::function<R(Params...)>;
+ void set(const Function &callable) { m_callable = callable; }
- template <typename ...Args>
- void operator()(Args ...args) const
+ R operator()(Params... params)
{
- for (const Callable &callable : m_callables)
- callable(args...);
- }
+ if (!m_callable)
+ return R();
+
+ if constexpr (IsVoidReturnType)
+ m_callable(std::forward<Params>(params)...);
+ else
+ return m_callable(std::forward<Params>(params)...);
+ }
private:
- std::vector<Callable> m_callables;
+ Function m_callable;
};
class FakeVimHandler : public QObject
@@ -129,34 +136,40 @@ public:
bool jumpToLocalMark(QChar mark, bool backTickMode);
+ bool inFakeVimMode();
+
bool eventFilter(QObject *ob, QEvent *ev) override;
- Signal<void(const QString &msg, int cursorPos, int anchorPos, int messageLevel)> commandBufferChanged;
- Signal<void(const QString &msg)> statusDataChanged;
- Signal<void(const QString &msg)> extraInformationChanged;
- Signal<void(const QList<QTextEdit::ExtraSelection> &selection)> selectionChanged;
- Signal<void(const QString &needle)> highlightMatches;
- Signal<void(bool *moved, bool *forward, QTextCursor *cursor)> moveToMatchingParenthesis;
- Signal<void(bool *result, QChar c)> checkForElectricCharacter;
- Signal<void(int beginLine, int endLine, QChar typedChar)> indentRegion;
- Signal<void(const QString &needle, bool forward)> simpleCompletionRequested;
- Signal<void(const QString &key, int count)> windowCommandRequested;
- Signal<void(bool reverse)> findRequested;
- Signal<void(bool reverse)> findNextRequested;
- Signal<void(bool *handled, const ExCommand &cmd)> handleExCommandRequested;
- Signal<void()> requestDisableBlockSelection;
- Signal<void(const QTextCursor &cursor)> requestSetBlockSelection;
- Signal<void(QTextCursor *cursor)> requestBlockSelection;
- Signal<void(bool *on)> requestHasBlockSelection;
- Signal<void(int depth)> foldToggle;
- Signal<void(bool fold)> foldAll;
- Signal<void(int depth, bool dofold)> fold;
- Signal<void(int count, bool current)> foldGoTo;
- Signal<void(QChar mark, bool backTickMode, const QString &fileName)> requestJumpToLocalMark;
- Signal<void(QChar mark, bool backTickMode, const QString &fileName)> requestJumpToGlobalMark;
- Signal<void()> completionRequested;
- Signal<void()> tabPreviousRequested;
- Signal<void()> tabNextRequested;
+ Callback<void(const QString &msg, int cursorPos, int anchorPos, int messageLevel)>
+ commandBufferChanged;
+ Callback<void(const QString &msg)> statusDataChanged;
+ Callback<void(const QString &msg)> extraInformationChanged;
+ Callback<void(const QList<QTextEdit::ExtraSelection> &selection)> selectionChanged;
+ Callback<void(const QString &needle)> highlightMatches;
+ Callback<void(bool *moved, bool *forward, QTextCursor *cursor)> moveToMatchingParenthesis;
+ Callback<void(bool *result, QChar c)> checkForElectricCharacter;
+ Callback<void(int beginLine, int endLine, QChar typedChar)> indentRegion;
+ Callback<void(const QString &needle, bool forward)> simpleCompletionRequested;
+ Callback<void(const QString &key, int count)> windowCommandRequested;
+ Callback<void(bool reverse)> findRequested;
+ Callback<void(bool reverse)> findNextRequested;
+ Callback<void(bool *handled, const ExCommand &cmd)> handleExCommandRequested;
+ Callback<void()> requestDisableBlockSelection;
+ Callback<void(const QTextCursor &cursor)> requestSetBlockSelection;
+ Callback<void(QTextCursor *cursor)> requestBlockSelection;
+ Callback<void(bool *on)> requestHasBlockSelection;
+ Callback<void(int depth)> foldToggle;
+ Callback<void(bool fold)> foldAll;
+ Callback<void(int depth, bool dofold)> fold;
+ Callback<void(int count, bool current)> foldGoTo;
+ Callback<void(QChar mark, bool backTickMode, const QString &fileName)> requestJumpToLocalMark;
+ Callback<void(QChar mark, bool backTickMode, const QString &fileName)> requestJumpToGlobalMark;
+ Callback<void()> completionRequested;
+ Callback<void()> tabPreviousRequested;
+ Callback<void()> tabNextRequested;
+ Callback<void(bool insertMode)> modeChanged;
+ Callback<bool()> tabPressedInInsertMode;
+ Callback<void(const QString &, const QString &, QString *)> processOutput;
public:
class Private;
diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp
index 02207de872..54ae4ca93f 100644
--- a/src/plugins/fakevim/fakevimplugin.cpp
+++ b/src/plugins/fakevim/fakevimplugin.cpp
@@ -50,7 +50,7 @@
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/qtcassert.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
#include <utils/stylehelper.h>
@@ -340,149 +340,6 @@ private:
using ExCommandMap = QMap<QString, QRegularExpression>;
using UserCommandMap = QMap<int, QString>;
-class FakeVimOptionPage : public IOptionsPage
-{
-public:
- FakeVimOptionPage()
- {
- setId(SETTINGS_ID);
- setDisplayName(Tr::tr("General"));
- setCategory(SETTINGS_CATEGORY);
- setDisplayCategory(Tr::tr("FakeVim"));
- setCategoryIconPath(":/fakevim/images/settingscategory_fakevim.png");
- setLayouter([this](QWidget *widget) { return layoutPage(widget); });
- setSettings(fakeVimSettings());
- }
-
-private:
- void layoutPage(QWidget *);
- void copyTextEditorSettings();
- void setQtStyle();
- void setPlainStyle();
-};
-
-void FakeVimOptionPage::layoutPage(QWidget *widget)
-{
- auto copyTextEditorSettings = new QPushButton(Tr::tr("Copy Text Editor Settings"));
- auto setQtStyle = new QPushButton(Tr::tr("Set Qt Style"));
- auto setPlainStyle = new QPushButton(Tr::tr("Set Plain Style"));
-
- using namespace Layouting;
- FakeVimSettings &s = *fakeVimSettings();
-
- Row bools {
- Column {
- s.autoIndent,
- s.smartIndent,
- s.expandTab,
- s.smartTab,
- s.hlSearch,
- s.showCmd,
- s.startOfLine,
- s.passKeys,
- s.blinkingCursor
- },
- Column {
- s.incSearch,
- s.useCoreSearch,
- s.ignoreCase,
- s.smartCase,
- s.wrapScan,
- s.showMarks,
- s.passControlKey,
- s.relativeNumber,
- s.tildeOp
- }
- };
-
- Row ints { s.shiftWidth, s.tabStop, s.scrollOff, st };
-
- Column strings {
- s.backspace,
- s.isKeyword,
- Row {s.readVimRc, s.vimRcPath}
- };
-
- Column {
- s.useFakeVim,
-
- Group {
- title(Tr::tr("Vim Behavior")),
- Column {
- bools,
- ints,
- strings
- }
- },
-
- Group {
- title(Tr::tr("Plugin Emulation")),
- Column {
- s.emulateVimCommentary,
- s.emulateReplaceWithRegister,
- s.emulateArgTextObj,
- s.emulateExchange,
- s.emulateSurround
- }
- },
-
- Row { copyTextEditorSettings, setQtStyle, setPlainStyle, st },
- st
-
- }.attachTo(widget);
-
- s.vimRcPath.setEnabler(&s.readVimRc);
-
- connect(copyTextEditorSettings, &QAbstractButton::clicked,
- this, &FakeVimOptionPage::copyTextEditorSettings);
- connect(setQtStyle, &QAbstractButton::clicked,
- this, &FakeVimOptionPage::setQtStyle);
- connect(setPlainStyle, &QAbstractButton::clicked,
- this, &FakeVimOptionPage::setPlainStyle);
-}
-
-void FakeVimOptionPage::copyTextEditorSettings()
-{
- FakeVimSettings &s = *fakeVimSettings();
- TabSettings ts = TextEditorSettings::codeStyle()->tabSettings();
- TypingSettings tps = TextEditorSettings::typingSettings();
- s.expandTab.setValue(ts.m_tabPolicy != TabSettings::TabsOnlyTabPolicy);
- s.tabStop.setValue(ts.m_tabSize);
- s.shiftWidth.setValue(ts.m_indentSize);
- s.smartTab.setValue(tps.m_smartBackspaceBehavior
- == TypingSettings::BackspaceFollowsPreviousIndents);
- s.autoIndent.setValue(true);
- s.smartIndent.setValue(tps.m_autoIndent);
- s.incSearch.setValue(true);
-}
-
-void FakeVimOptionPage::setQtStyle()
-{
- FakeVimSettings &s = *fakeVimSettings();
- s.expandTab.setVolatileValue(true);
- s.tabStop.setVolatileValue(4);
- s.shiftWidth.setVolatileValue(4);
- s.smartTab.setVolatileValue(true);
- s.autoIndent.setVolatileValue(true);
- s.smartIndent.setVolatileValue(true);
- s.incSearch.setVolatileValue(true);
- s.backspace.setVolatileValue(QString("indent,eol,start"));
- s.passKeys.setVolatileValue(true);
-}
-
-void FakeVimOptionPage::setPlainStyle()
-{
- FakeVimSettings &s = *fakeVimSettings();
- s.expandTab.setVolatileValue(false);
- s.tabStop.setVolatileValue(8);
- s.shiftWidth.setVolatileValue(8);
- s.smartTab.setVolatileValue(false);
- s.autoIndent.setVolatileValue(false);
- s.smartIndent.setVolatileValue(false);
- s.incSearch.setVolatileValue(false);
- s.backspace.setVolatileValue(QString());
- s.passKeys.setVolatileValue(false);
-}
///////////////////////////////////////////////////////////////////////
//
@@ -522,13 +379,14 @@ public:
int cursorPos, int anchorPos, int messageLevel);
void handleExCommand(FakeVimHandler *handler, bool *handled, const ExCommand &cmd);
- void writeSettings();
void readSettings();
void handleDelayedQuitAll(bool forced);
void handleDelayedQuit(bool forced, Core::IEditor *editor);
void userActionTriggered(int key);
+ void updateAllHightLights();
+
void switchToFile(int n);
int currentFile() const;
@@ -539,7 +397,22 @@ signals:
void delayedQuitAllRequested(bool forced);
public:
- QHash<IEditor *, FakeVimHandler *> m_editorToHandler;
+ struct HandlerAndData
+ {
+#ifdef Q_OS_WIN
+ // We need to declare a constructor here, otherwise the MSVC 17.5.x compiler fails to parse
+ // the "{nullptr}" initializer in the definition below.
+ // This seems to be a compiler bug,
+ // see: https://developercommunity.visualstudio.com/t/10351118
+ HandlerAndData()
+ : handler(nullptr)
+ {}
+#endif
+ FakeVimHandler *handler{nullptr};
+ TextEditorWidget::SuggestionBlocker suggestionBlocker;
+ };
+
+ QHash<IEditor *, HandlerAndData> m_editorToHandler;
void setActionChecked(Id id, bool check);
@@ -557,6 +430,8 @@ public:
MiniBuffer *m_miniBuffer = nullptr;
FakeVimPluginRunData *runData = nullptr;
+ QString m_lastHighlight;
+
int m_savedCursorFlashTime = 0;
};
@@ -568,12 +443,21 @@ public:
enum { CommandRole = Qt::UserRole };
-class FakeVimExCommandsWidget : public CommandMappings
+const char exCommandMapGroup[] = "FakeVimExCommand";
+const char userCommandMapGroup[] = "FakeVimUserCommand";
+const char reKey[] = "RegEx";
+const char cmdKey[] = "Cmd";
+const char idKey[] = "Command";
+
+class FakeVimExCommandsMappings : public CommandMappings
{
public:
- FakeVimExCommandsWidget();
+ FakeVimExCommandsMappings();
+ void apply();
protected:
+ ExCommandMap exCommandMapFromWidget();
+
void commandChanged();
void resetToDefault();
void defaultAction() override;
@@ -581,113 +465,48 @@ protected:
void handleCurrentCommandChanged(QTreeWidgetItem *current);
private:
- void initialize();
-
- ExCommandMap exCommandMapFromWidget();
-
QGroupBox *m_commandBox;
FancyLineEdit *m_commandEdit;
-
- friend class FakeVimExCommandsPage; // allow the page accessing the ExCommandMaps
};
-FakeVimExCommandsWidget::FakeVimExCommandsWidget()
+FakeVimExCommandsMappings::FakeVimExCommandsMappings()
{
setPageTitle(Tr::tr("Ex Command Mapping"));
setTargetHeader(Tr::tr("Ex Trigger Expression"));
setImportExportEnabled(false);
- connect(this, &FakeVimExCommandsWidget::currentCommandChanged,
- this, &FakeVimExCommandsWidget::handleCurrentCommandChanged);
+ connect(this, &FakeVimExCommandsMappings::currentCommandChanged,
+ this, &FakeVimExCommandsMappings::handleCurrentCommandChanged);
m_commandBox = new QGroupBox(Tr::tr("Ex Command"), this);
m_commandBox->setEnabled(false);
- auto boxLayout = new QHBoxLayout(m_commandBox);
+ auto commandBoxLayout = new QVBoxLayout(m_commandBox);
+ auto boxLayout = new QHBoxLayout;
+ commandBoxLayout->addLayout(boxLayout);
m_commandEdit = new FancyLineEdit(m_commandBox);
m_commandEdit->setFiltering(true);
m_commandEdit->setPlaceholderText(QString());
connect(m_commandEdit, &FancyLineEdit::textChanged,
- this, &FakeVimExCommandsWidget::commandChanged);
+ this, &FakeVimExCommandsMappings::commandChanged);
+ m_commandEdit->setValidationFunction([](FancyLineEdit *e, QString *){
+ return QRegularExpression(e->text()).isValid();
+ });
auto resetButton = new QPushButton(Tr::tr("Reset"), m_commandBox);
resetButton->setToolTip(Tr::tr("Reset to default."));
connect(resetButton, &QPushButton::clicked,
- this, &FakeVimExCommandsWidget::resetToDefault);
+ this, &FakeVimExCommandsMappings::resetToDefault);
boxLayout->addWidget(new QLabel(Tr::tr("Regular expression:")));
boxLayout->addWidget(m_commandEdit);
boxLayout->addWidget(resetButton);
+ auto infoLabel = new InfoLabel(Tr::tr("Invalid regular expression."), InfoLabel::Error);
+ infoLabel->setVisible(false);
+ connect(m_commandEdit, &FancyLineEdit::validChanged, [infoLabel](bool valid){
+ infoLabel->setVisible(!valid);
+ });
+ commandBoxLayout->addWidget(infoLabel);
layout()->addWidget(m_commandBox);
- initialize();
-}
-
-class FakeVimExCommandsPage : public IOptionsPage
-{
-public:
- FakeVimExCommandsPage()
- {
- setId(SETTINGS_EX_CMDS_ID);
- setDisplayName(Tr::tr("Ex Command Mapping"));
- setCategory(SETTINGS_CATEGORY);
- }
-
- QWidget *widget() override
- {
- if (!m_widget)
- m_widget = new FakeVimExCommandsWidget;
- return m_widget;
- }
-
- void apply() override;
- void finish() override {}
-
-private:
- QPointer<FakeVimExCommandsWidget> m_widget;
-};
-
-
-const char exCommandMapGroup[] = "FakeVimExCommand";
-const char userCommandMapGroup[] = "FakeVimUserCommand";
-const char reKey[] = "RegEx";
-const char cmdKey[] = "Cmd";
-const char idKey[] = "Command";
-
-void FakeVimExCommandsPage::apply()
-{
- if (!m_widget) // page has not been shown at all
- return;
- // now save the mappings if necessary
- const ExCommandMap &newMapping = m_widget->exCommandMapFromWidget();
- ExCommandMap &globalCommandMapping = dd->m_exCommandMap;
-
- if (newMapping != globalCommandMapping) {
- const ExCommandMap &defaultMap = dd->m_defaultExCommandMap;
- QSettings *settings = ICore::settings();
- settings->beginWriteArray(exCommandMapGroup);
- int count = 0;
- using Iterator = ExCommandMap::const_iterator;
- const Iterator end = newMapping.constEnd();
- for (Iterator it = newMapping.constBegin(); it != end; ++it) {
- const QString id = it.key();
- const QRegularExpression re = it.value();
-
- if ((defaultMap.contains(id) && defaultMap[id] != re)
- || (!defaultMap.contains(id) && !re.pattern().isEmpty())) {
- settings->setArrayIndex(count);
- settings->setValue(idKey, id);
- settings->setValue(reKey, re.pattern());
- ++count;
- }
- }
- settings->endArray();
- globalCommandMapping.clear();
- globalCommandMapping.insert(defaultMap);
- globalCommandMapping.insert(newMapping);
- }
-}
-
-void FakeVimExCommandsWidget::initialize()
-{
QMap<QString, QTreeWidgetItem *> sections;
const QList<Command *> commands = ActionManager::commands();
@@ -727,7 +546,30 @@ void FakeVimExCommandsWidget::initialize()
handleCurrentCommandChanged(nullptr);
}
-void FakeVimExCommandsWidget::handleCurrentCommandChanged(QTreeWidgetItem *current)
+ExCommandMap FakeVimExCommandsMappings::exCommandMapFromWidget()
+{
+ ExCommandMap map;
+ int n = commandList()->topLevelItemCount();
+ for (int i = 0; i != n; ++i) {
+ QTreeWidgetItem *section = commandList()->topLevelItem(i);
+ int m = section->childCount();
+ for (int j = 0; j != m; ++j) {
+ QTreeWidgetItem *item = section->child(j);
+ const QString name = item->data(0, CommandRole).toString();
+ const QString regex = item->data(2, Qt::DisplayRole).toString();
+ const QString pattern = dd->m_defaultExCommandMap.value(name).pattern();
+ if ((regex.isEmpty() && pattern.isEmpty())
+ || (!regex.isEmpty() && pattern == regex))
+ continue;
+ const QRegularExpression expression(regex);
+ if (expression.isValid())
+ map[name] = expression;
+ }
+ }
+ return map;
+}
+
+void FakeVimExCommandsMappings::handleCurrentCommandChanged(QTreeWidgetItem *current)
{
if (current) {
m_commandEdit->setText(current->text(2));
@@ -738,7 +580,7 @@ void FakeVimExCommandsWidget::handleCurrentCommandChanged(QTreeWidgetItem *curre
}
}
-void FakeVimExCommandsWidget::commandChanged()
+void FakeVimExCommandsMappings::commandChanged()
{
QTreeWidgetItem *current = commandList()->currentItem();
if (!current)
@@ -753,7 +595,7 @@ void FakeVimExCommandsWidget::commandChanged()
setModified(current, regex != dd->m_defaultExCommandMap[name].pattern());
}
-void FakeVimExCommandsWidget::resetToDefault()
+void FakeVimExCommandsMappings::resetToDefault()
{
QTreeWidgetItem *current = commandList()->currentItem();
if (!current)
@@ -765,12 +607,12 @@ void FakeVimExCommandsWidget::resetToDefault()
m_commandEdit->setText(regex);
}
-void FakeVimExCommandsWidget::defaultAction()
+void FakeVimExCommandsMappings::defaultAction()
{
- int n = commandList()->topLevelItemCount();
+ const int n = commandList()->topLevelItemCount();
for (int i = 0; i != n; ++i) {
QTreeWidgetItem *section = commandList()->topLevelItem(i);
- int m = section->childCount();
+ const int m = section->childCount();
for (int j = 0; j != m; ++j) {
QTreeWidgetItem *item = section->child(j);
const QString name = item->data(0, CommandRole).toString();
@@ -785,6 +627,61 @@ void FakeVimExCommandsWidget::defaultAction()
}
}
+void FakeVimExCommandsMappings::apply()
+{
+ // now save the mappings if necessary
+ const ExCommandMap &newMapping = exCommandMapFromWidget();
+ ExCommandMap &globalCommandMapping = dd->m_exCommandMap;
+
+ if (newMapping != globalCommandMapping) {
+ const ExCommandMap &defaultMap = dd->m_defaultExCommandMap;
+ QSettings *settings = ICore::settings();
+ settings->beginWriteArray(exCommandMapGroup);
+ int count = 0;
+ using Iterator = ExCommandMap::const_iterator;
+ const Iterator end = newMapping.constEnd();
+ for (Iterator it = newMapping.constBegin(); it != end; ++it) {
+ const QString id = it.key();
+ const QRegularExpression re = it.value();
+
+ if ((defaultMap.contains(id) && defaultMap[id] != re)
+ || (!defaultMap.contains(id) && !re.pattern().isEmpty())) {
+ settings->setArrayIndex(count);
+ settings->setValue(idKey, id);
+ settings->setValue(reKey, re.pattern());
+ ++count;
+ }
+ }
+ settings->endArray();
+ globalCommandMapping.clear();
+ globalCommandMapping.insert(defaultMap);
+ globalCommandMapping.insert(newMapping);
+ }
+}
+
+class FakeVimExCommandsPageWidget : public IOptionsPageWidget
+{
+public:
+ FakeVimExCommandsPageWidget()
+ {
+ auto exCommands = new FakeVimExCommandsMappings;
+ setOnApply([exCommands] { exCommands->apply(); });
+ Layouting::Column { exCommands, Layouting::noMargin }.attachTo(this);
+ }
+};
+
+class FakeVimExCommandsPage : public IOptionsPage
+{
+public:
+ FakeVimExCommandsPage()
+ {
+ setId(SETTINGS_EX_CMDS_ID);
+ setDisplayName(Tr::tr("Ex Command Mapping"));
+ setCategory(SETTINGS_CATEGORY);
+ setWidgetCreator([] { return new FakeVimExCommandsPageWidget; });
+ }
+};
+
///////////////////////////////////////////////////////////////////////
//
// FakeVimUserCommandsPage
@@ -797,28 +694,17 @@ public:
FakeVimUserCommandsModel() { m_commandMap = dd->m_userCommandMap; }
UserCommandMap commandMap() const { return m_commandMap; }
- int rowCount(const QModelIndex &parent) const override;
- int columnCount(const QModelIndex &parent) const override;
- QVariant data(const QModelIndex &index, int role) const override;
- bool setData(const QModelIndex &index, const QVariant &data, int role) override;
- QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
- Qt::ItemFlags flags(const QModelIndex &index) const override;
+ int rowCount(const QModelIndex &parent) const final { return parent.isValid() ? 0 : 9; }
+ int columnCount(const QModelIndex &parent) const final { return parent.isValid() ? 0 : 2; }
+ QVariant data(const QModelIndex &index, int role) const final;
+ bool setData(const QModelIndex &index, const QVariant &data, int role) final;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const final;
+ Qt::ItemFlags flags(const QModelIndex &index) const final;
private:
UserCommandMap m_commandMap;
};
-int FakeVimUserCommandsModel::rowCount(const QModelIndex &parent) const
-{
- return parent.isValid() ? 0 : 9;
-}
-
-int FakeVimUserCommandsModel::columnCount(const QModelIndex &parent) const
-{
- return parent.isValid() ? 0 : 2;
-}
-
-
QVariant FakeVimUserCommandsModel::headerData(int section,
Qt::Orientation orient, int role) const
{
@@ -862,83 +748,74 @@ public:
}
};
-class FakeVimUserCommandsPage : public IOptionsPage
+class FakeVimUserCommandsPageWidget : public IOptionsPageWidget
{
public:
- FakeVimUserCommandsPage()
+ FakeVimUserCommandsPageWidget(FakeVimUserCommandsModel *model)
+ : m_model(model)
{
- setId(SETTINGS_USER_CMDS_ID);
- setDisplayName(Tr::tr("User Command Mapping"));
- setCategory(SETTINGS_CATEGORY);
- }
-
- void apply() override;
- void finish() override {}
-
- QWidget *widget() override;
- void initialize() {}
- UserCommandMap currentCommandMap() { return m_model->commandMap(); }
-
-private:
- QPointer<QWidget> m_widget;
- FakeVimUserCommandsModel *m_model = nullptr;
-};
-
-QWidget *FakeVimUserCommandsPage::widget()
-{
- if (!m_widget) {
- m_widget = new QWidget;
-
- m_model = new FakeVimUserCommandsModel;
auto widget = new QTreeView;
- m_model->setParent(widget);
widget->setModel(m_model);
widget->resizeColumnToContents(0);
auto delegate = new FakeVimUserCommandsDelegate(widget);
widget->setItemDelegateForColumn(1, delegate);
- auto layout = new QGridLayout(m_widget);
+ auto layout = new QGridLayout(this);
layout->addWidget(widget, 0, 0);
- m_widget->setLayout(layout);
+ setLayout(layout);
}
- return m_widget;
-}
-void FakeVimUserCommandsPage::apply()
-{
- if (!m_widget) // page has not been shown at all
- return;
-
- // now save the mappings if necessary
- const UserCommandMap &current = currentCommandMap();
- UserCommandMap &userMap = dd->m_userCommandMap;
-
- if (current != userMap) {
- QSettings *settings = ICore::settings();
- settings->beginWriteArray(userCommandMapGroup);
- int count = 0;
- using Iterator = UserCommandMap::const_iterator;
- const Iterator end = current.constEnd();
- for (Iterator it = current.constBegin(); it != end; ++it) {
- const int key = it.key();
- const QString cmd = it.value();
-
- if ((dd->m_defaultUserCommandMap.contains(key)
- && dd->m_defaultUserCommandMap[key] != cmd)
- || (!dd->m_defaultUserCommandMap.contains(key) && !cmd.isEmpty())) {
- settings->setArrayIndex(count);
- settings->setValue(idKey, key);
- settings->setValue(cmdKey, cmd);
- ++count;
+private:
+ void apply() final
+ {
+ // now save the mappings if necessary
+ const UserCommandMap &current = m_model->commandMap();
+ UserCommandMap &userMap = dd->m_userCommandMap;
+
+ if (current != userMap) {
+ QSettings *settings = ICore::settings();
+ settings->beginWriteArray(userCommandMapGroup);
+ int count = 0;
+ using Iterator = UserCommandMap::const_iterator;
+ const Iterator end = current.constEnd();
+ for (Iterator it = current.constBegin(); it != end; ++it) {
+ const int key = it.key();
+ const QString cmd = it.value();
+
+ if ((dd->m_defaultUserCommandMap.contains(key)
+ && dd->m_defaultUserCommandMap[key] != cmd)
+ || (!dd->m_defaultUserCommandMap.contains(key) && !cmd.isEmpty())) {
+ settings->setArrayIndex(count);
+ settings->setValue(idKey, key);
+ settings->setValue(cmdKey, cmd);
+ ++count;
+ }
}
+ settings->endArray();
+ userMap.clear();
+ userMap.insert(dd->m_defaultUserCommandMap);
+ userMap.insert(current);
}
- settings->endArray();
- userMap.clear();
- userMap.insert(dd->m_defaultUserCommandMap);
- userMap.insert(current);
}
-}
+
+ FakeVimUserCommandsModel *m_model;
+};
+
+class FakeVimUserCommandsPage : public IOptionsPage
+{
+public:
+ FakeVimUserCommandsPage()
+ {
+ setId(SETTINGS_USER_CMDS_ID);
+ setDisplayName(Tr::tr("User Command Mapping"));
+ setCategory(SETTINGS_CATEGORY);
+ setWidgetCreator([this] { return new FakeVimUserCommandsPageWidget(&m_model); });
+ }
+
+private:
+ FakeVimUserCommandsModel m_model;
+};
///////////////////////////////////////////////////////////////////////
@@ -1101,7 +978,7 @@ IAssistProcessor *FakeVimCompletionAssistProvider::createProcessor(const AssistI
class FakeVimPluginRunData
{
public:
- FakeVimOptionPage optionsPage;
+ FakeVimSettings settings;
FakeVimExCommandsPage exCommandsPage;
FakeVimUserCommandsPage userCommandsPage;
@@ -1173,7 +1050,7 @@ void FakeVimPluginPrivate::initialize()
Command *cmd = nullptr;
- cmd = ActionManager::registerAction(fakeVimSettings()->useFakeVim.action(),
+ cmd = ActionManager::registerAction(settings().useFakeVim.action(),
INSTALL_HANDLER, Context(Core::Constants::C_GLOBAL), true);
cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+Shift+Y,Meta+Shift+Y")
: Tr::tr("Alt+Y,Alt+Y")));
@@ -1211,7 +1088,7 @@ void FakeVimPluginPrivate::initialize()
connect(DocumentManager::instance(), &DocumentManager::documentRenamed,
this, &FakeVimPluginPrivate::documentRenamed);
- FakeVimSettings &s = *fakeVimSettings();
+ FakeVimSettings &s = settings();
connect(&s.useFakeVim, &FvBoolAspect::valueChanged,
this, &FakeVimPluginPrivate::setUseFakeVim);
connect(&s.readVimRc, &FvBaseAspect::changed,
@@ -1229,16 +1106,16 @@ void FakeVimPluginPrivate::initialize()
connect(this, &FakeVimPluginPrivate::delayedQuitAllRequested,
this, &FakeVimPluginPrivate::handleDelayedQuitAll, Qt::QueuedConnection);
- setCursorBlinking(s.blinkingCursor.value());
+ setCursorBlinking(s.blinkingCursor());
}
void FakeVimPluginPrivate::userActionTriggered(int key)
{
IEditor *editor = EditorManager::currentEditor();
- FakeVimHandler *handler = m_editorToHandler[editor];
+ FakeVimHandler *handler = m_editorToHandler[editor].handler;
if (handler) {
// If disabled, enable FakeVim mode just for single user command.
- bool enableFakeVim = !fakeVimSettings()->useFakeVim.value();
+ bool enableFakeVim = !settings().useFakeVim();
if (enableFakeVim)
setUseFakeVimInternal(true);
@@ -1250,37 +1127,41 @@ void FakeVimPluginPrivate::userActionTriggered(int key)
}
}
+void FakeVimPluginPrivate::updateAllHightLights()
+{
+ const QList<IEditor *> editors = EditorManager::visibleEditors();
+ for (IEditor *editor : editors) {
+ QWidget *w = editor->widget();
+ if (auto find = Aggregation::query<IFindSupport>(w))
+ find->highlightAll(m_lastHighlight, FindRegularExpression | FindCaseSensitively);
+ }
+}
+
void FakeVimPluginPrivate::createRelativeNumberWidget(IEditor *editor)
{
if (auto textEditor = TextEditorWidget::fromEditor(editor)) {
auto relativeNumbers = new RelativeNumbersColumn(textEditor);
- connect(&fakeVimSettings()->relativeNumber, &FvBaseAspect::changed,
+ connect(&settings().relativeNumber, &FvBaseAspect::changed,
relativeNumbers, &QObject::deleteLater);
- connect(&fakeVimSettings()->useFakeVim, &FvBaseAspect::changed,
+ connect(&settings().useFakeVim, &FvBaseAspect::changed,
relativeNumbers, &QObject::deleteLater);
relativeNumbers->show();
}
}
-void FakeVimPluginPrivate::writeSettings()
-{
- QSettings *settings = ICore::settings();
- fakeVimSettings()->writeSettings(settings);
-}
-
void FakeVimPluginPrivate::readSettings()
{
QSettings *settings = ICore::settings();
- fakeVimSettings()->readSettings(settings);
-
m_exCommandMap = m_defaultExCommandMap;
int size = settings->beginReadArray(exCommandMapGroup);
for (int i = 0; i < size; ++i) {
settings->setArrayIndex(i);
const QString id = settings->value(idKey).toString();
const QString re = settings->value(reKey).toString();
- m_exCommandMap[id] = QRegularExpression(re);
+ const QRegularExpression regEx(re);
+ if (regEx.isValid())
+ m_exCommandMap[id] = regEx;
}
settings->endArray();
@@ -1300,9 +1181,9 @@ void FakeVimPluginPrivate::maybeReadVimRc()
//qDebug() << theFakeVimSetting(ConfigReadVimRc)
// << theFakeVimSetting(ConfigReadVimRc)->value();
//qDebug() << theFakeVimSetting(ConfigShiftWidth)->value();
- if (!fakeVimSettings()->readVimRc.value())
+ if (!settings().readVimRc())
return;
- QString fileName = fakeVimSettings()->vimRcPath.value();
+ QString fileName = settings().vimRcPath();
if (fileName.isEmpty()) {
fileName = QStandardPaths::writableLocation(QStandardPaths::HomeLocation)
+ QLatin1String(HostOsInfo::isWindowsHost() ? "/_vimrc" : "/.vimrc");
@@ -1312,7 +1193,6 @@ void FakeVimPluginPrivate::maybeReadVimRc()
QPlainTextEdit editor;
FakeVimHandler handler(&editor);
handler.handleCommand("source " + fileName);
- //writeSettings();
//qDebug() << theFakeVimSetting(ConfigShiftWidth)->value();
}
@@ -1549,29 +1429,53 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
// the handler might have triggered the deletion of the editor:
// make sure that it can return before being deleted itself
new DeferredDeleter(widget, handler);
- m_editorToHandler[editor] = handler;
+ m_editorToHandler[editor].handler = handler;
- handler->extraInformationChanged.connect([this](const QString &text) {
+ handler->extraInformationChanged.set([this](const QString &text) {
EditorManager::splitSideBySide();
QString title = "stdout.txt";
IEditor *iedit = EditorManager::openEditorWithContents(Id(), &title, text.toUtf8());
EditorManager::activateEditor(iedit);
- FakeVimHandler *handler = m_editorToHandler.value(iedit, nullptr);
+ FakeVimHandler *handler = m_editorToHandler.value(iedit, {}).handler;
QTC_ASSERT(handler, return);
handler->handleCommand("0");
});
- handler->commandBufferChanged
- .connect([this, handler](const QString &contents, int cursorPos, int anchorPos, int messageLevel) {
- showCommandBuffer(handler, contents, cursorPos, anchorPos, messageLevel);
- });
+ handler->commandBufferChanged.set(
+ [this, handler](const QString &contents, int cursorPos, int anchorPos, int messageLevel) {
+ showCommandBuffer(handler, contents, cursorPos, anchorPos, messageLevel);
+ });
- handler->selectionChanged.connect([tew](const QList<QTextEdit::ExtraSelection> &selection) {
+ handler->selectionChanged.set([tew](const QList<QTextEdit::ExtraSelection> &selection) {
if (tew)
tew->setExtraSelections(TextEditorWidget::FakeVimSelection, selection);
});
- handler->highlightMatches.connect([](const QString &needle) {
+ handler->tabPressedInInsertMode.set([tew]() {
+ auto suggestion = tew->currentSuggestion();
+ if (suggestion) {
+ suggestion->apply();
+ return false;
+ }
+
+ return true;
+ });
+
+ handler->modeChanged.set([tew, this, editor](bool insertMode) {
+ HandlerAndData &handlerAndData = m_editorToHandler[editor];
+ if (!handlerAndData.handler->inFakeVimMode())
+ return;
+
+ // We don't want to show suggestions unless we are in insert mode.
+ if (insertMode != (handlerAndData.suggestionBlocker == nullptr))
+ handlerAndData.suggestionBlocker = insertMode ? nullptr : tew->blockSuggestions();
+
+ if (tew)
+ tew->clearSuggestion();
+ });
+
+ handler->highlightMatches.set([this](const QString &needle) {
+ m_lastHighlight = needle;
for (IEditor *editor : EditorManager::visibleEditors()) {
QWidget *w = editor->widget();
if (auto find = Aggregation::query<IFindSupport>(w))
@@ -1579,7 +1483,7 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
}
});
- handler->moveToMatchingParenthesis.connect([](bool *moved, bool *forward, QTextCursor *cursor) {
+ handler->moveToMatchingParenthesis.set([](bool *moved, bool *forward, QTextCursor *cursor) {
*moved = false;
bool undoFakeEOL = false;
@@ -1612,14 +1516,14 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
}
});
- handler->indentRegion.connect([tew](int beginBlock, int endBlock, QChar typedChar) {
+ handler->indentRegion.set([tew](int beginBlock, int endBlock, QChar typedChar) {
if (!tew)
return;
TabSettings tabSettings;
- tabSettings.m_indentSize = fakeVimSettings()->shiftWidth.value();
- tabSettings.m_tabSize = fakeVimSettings()->tabStop.value();
- tabSettings.m_tabPolicy = fakeVimSettings()->expandTab.value()
+ tabSettings.m_indentSize = settings().shiftWidth();
+ tabSettings.m_tabSize = settings().tabStop();
+ tabSettings.m_tabPolicy = settings().expandTab()
? TabSettings::SpacesOnlyTabPolicy : TabSettings::TabsOnlyTabPolicy;
tabSettings.m_continuationAlignBehavior =
tew->textDocument()->tabSettings().m_continuationAlignBehavior;
@@ -1645,17 +1549,17 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
}
});
- handler->checkForElectricCharacter.connect([tew](bool *result, QChar c) {
+ handler->checkForElectricCharacter.set([tew](bool *result, QChar c) {
if (tew)
*result = tew->textDocument()->indenter()->isElectricCharacter(c);
});
- handler->requestDisableBlockSelection.connect([tew] {
+ handler->requestDisableBlockSelection.set([tew] {
if (tew)
tew->setTextCursor(tew->textCursor());
});
- handler->requestSetBlockSelection.connect([tew](const QTextCursor &cursor) {
+ handler->requestSetBlockSelection.set([tew](const QTextCursor &cursor) {
if (tew) {
const TabSettings &tabs = tew->textDocument()->tabSettings();
MultiTextCursor mtc;
@@ -1679,7 +1583,7 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
}
});
- handler->requestBlockSelection.connect([tew](QTextCursor *cursor) {
+ handler->requestBlockSelection.set([tew](QTextCursor *cursor) {
if (tew && cursor) {
MultiTextCursor mtc = tew->multiTextCursor();
*cursor = mtc.cursors().first();
@@ -1687,16 +1591,16 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
}
});
- handler->requestHasBlockSelection.connect([tew](bool *on) {
+ handler->requestHasBlockSelection.set([tew](bool *on) {
if (tew && on)
*on = tew->multiTextCursor().hasMultipleCursors();
});
- handler->simpleCompletionRequested.connect([this, handler](const QString &needle, bool forward) {
+ handler->simpleCompletionRequested.set([this, handler](const QString &needle, bool forward) {
runData->wordProvider.setActive(needle, forward, handler);
});
- handler->windowCommandRequested.connect([this, handler](const QString &map, int count) {
+ handler->windowCommandRequested.set([this, handler](const QString &map, int count) {
// normalize mapping
const QString key = map.toUpper();
@@ -1726,22 +1630,22 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
qDebug() << "UNKNOWN WINDOW COMMAND: <C-W>" << map;
});
- handler->findRequested.connect([](bool reverse) {
+ handler->findRequested.set([](bool reverse) {
Find::setUseFakeVim(true);
Find::openFindToolBar(reverse ? Find::FindBackwardDirection
: Find::FindForwardDirection);
});
- handler->findNextRequested.connect([](bool reverse) {
+ handler->findNextRequested.set([](bool reverse) {
triggerAction(reverse ? Core::Constants::FIND_PREVIOUS : Core::Constants::FIND_NEXT);
});
- handler->foldToggle.connect([this, handler](int depth) {
+ handler->foldToggle.set([this, handler](int depth) {
QTextBlock block = handler->textCursor().block();
fold(handler, depth, !TextDocumentLayout::isFolded(block));
});
- handler->foldAll.connect([handler](bool fold) {
+ handler->foldAll.set([handler](bool fold) {
QTextDocument *document = handler->textCursor().document();
auto documentLayout = qobject_cast<TextDocumentLayout*>(document->documentLayout());
QTC_ASSERT(documentLayout, return);
@@ -1756,11 +1660,9 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
documentLayout->emitDocumentSizeChanged();
});
- handler->fold.connect([this, handler](int depth, bool dofold) {
- fold(handler, depth, dofold);
- });
+ handler->fold.set([this, handler](int depth, bool dofold) { fold(handler, depth, dofold); });
- handler->foldGoTo.connect([handler](int count, bool current) {
+ handler->foldGoTo.set([handler](int count, bool current) {
QTextCursor tc = handler->textCursor();
QTextBlock block = tc.block();
@@ -1812,44 +1714,48 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
}
});
- handler->requestJumpToGlobalMark.connect(
+ handler->requestJumpToGlobalMark.set(
[this](QChar mark, bool backTickMode, const QString &fileName) {
if (IEditor *iedit = EditorManager::openEditor(FilePath::fromString(fileName))) {
- if (FakeVimHandler *handler = m_editorToHandler.value(iedit, nullptr))
+ if (FakeVimHandler *handler = m_editorToHandler.value(iedit, {}).handler)
handler->jumpToLocalMark(mark, backTickMode);
}
});
- handler->handleExCommandRequested.connect([this, handler](bool *handled, const ExCommand &cmd) {
+ handler->handleExCommandRequested.set([this, handler](bool *handled, const ExCommand &cmd) {
handleExCommand(handler, handled, cmd);
});
- handler->tabNextRequested.connect([] {
- triggerAction(Core::Constants::GOTONEXTINHISTORY);
- });
+ handler->tabNextRequested.set([] { triggerAction(Core::Constants::GOTONEXTINHISTORY); });
- handler->tabPreviousRequested.connect([] {
- triggerAction(Core::Constants::GOTOPREVINHISTORY);
- });
+ handler->tabPreviousRequested.set([] { triggerAction(Core::Constants::GOTOPREVINHISTORY); });
- handler->completionRequested.connect([this, tew] {
+ handler->completionRequested.set([this, tew] {
if (tew)
tew->invokeAssist(Completion, &runData->wordProvider);
});
- connect(ICore::instance(), &ICore::saveSettingsRequested,
- this, &FakeVimPluginPrivate::writeSettings);
+ handler->processOutput.set([](const QString &command, const QString &input, QString *output) {
+ Process proc;
+ proc.setCommand(Utils::CommandLine::fromUserInput(command));
+ proc.setWriteData(input.toLocal8Bit());
+ proc.start();
+ // FIXME: Process should be interruptable by user.
+ // Solution is to create a QObject for each process and emit finished state.
+ proc.waitForFinished();
+ *output = proc.cleanedStdOut();
+ });
handler->setCurrentFileName(editor->document()->filePath().toString());
handler->installEventFilter();
// pop up the bar
- if (fakeVimSettings()->useFakeVim.value()) {
+ if (settings().useFakeVim()) {
resetCommandBuffer();
handler->setupWidget();
- if (fakeVimSettings()->relativeNumber.value())
+ if (settings().relativeNumber())
createRelativeNumberWidget(editor);
}
}
@@ -1862,7 +1768,7 @@ void FakeVimPluginPrivate::editorAboutToClose(IEditor *editor)
void FakeVimPluginPrivate::currentEditorAboutToChange(IEditor *editor)
{
- if (FakeVimHandler *handler = m_editorToHandler.value(editor, 0))
+ if (FakeVimHandler *handler = m_editorToHandler.value(editor, {}).handler)
handler->enterCommandMode();
}
@@ -1880,9 +1786,9 @@ void FakeVimPluginPrivate::documentRenamed(
void FakeVimPluginPrivate::renameFileNameInEditors(const FilePath &oldPath, const FilePath &newPath)
{
- for (FakeVimHandler *handler : m_editorToHandler) {
- if (handler->currentFileName() == oldPath.toString())
- handler->setCurrentFileName(newPath.toString());
+ for (const HandlerAndData &handlerAndData : m_editorToHandler) {
+ if (handlerAndData.handler->currentFileName() == oldPath.toString())
+ handlerAndData.handler->setCurrentFileName(newPath.toString());
}
}
@@ -1891,8 +1797,8 @@ void FakeVimPluginPrivate::setUseFakeVim(bool on)
//qDebug() << "SET USE FAKEVIM" << on;
Find::setUseFakeVim(on);
setUseFakeVimInternal(on);
- setShowRelativeLineNumbers(fakeVimSettings()->relativeNumber.value());
- setCursorBlinking(fakeVimSettings()->blinkingCursor.value());
+ setShowRelativeLineNumbers(settings().relativeNumber());
+ setCursorBlinking(settings().blinkingCursor());
}
void FakeVimPluginPrivate::setUseFakeVimInternal(bool on)
@@ -1901,23 +1807,26 @@ void FakeVimPluginPrivate::setUseFakeVimInternal(bool on)
//ICore *core = ICore::instance();
//core->updateAdditionalContexts(Context(FAKEVIM_CONTEXT),
// Context());
- for (FakeVimHandler *handler : m_editorToHandler)
- handler->setupWidget();
+ for (const HandlerAndData &handlerAndData : m_editorToHandler)
+ handlerAndData.handler->setupWidget();
} else {
//ICore *core = ICore::instance();
//core->updateAdditionalContexts(Context(),
// Context(FAKEVIM_CONTEXT));
resetCommandBuffer();
- for (auto it = m_editorToHandler.constBegin(); it != m_editorToHandler.constEnd(); ++it) {
- if (auto textDocument = qobject_cast<const TextDocument *>(it.key()->document()))
- it.value()->restoreWidget(textDocument->tabSettings().m_tabSize);
+ for (auto it = m_editorToHandler.begin(); it != m_editorToHandler.end(); ++it) {
+ if (auto textDocument = qobject_cast<const TextDocument *>(it.key()->document())) {
+ HandlerAndData &handlerAndData = it.value();
+ handlerAndData.handler->restoreWidget(textDocument->tabSettings().m_tabSize);
+ handlerAndData.suggestionBlocker.reset();
+ }
}
}
}
void FakeVimPluginPrivate::setShowRelativeLineNumbers(bool on)
{
- if (on && fakeVimSettings()->useFakeVim.value()) {
+ if (on && settings().useFakeVim()) {
for (auto it = m_editorToHandler.constBegin(); it != m_editorToHandler.constEnd(); ++it)
createRelativeNumberWidget(it.key());
}
@@ -1928,7 +1837,7 @@ void FakeVimPluginPrivate::setCursorBlinking(bool on)
if (m_savedCursorFlashTime == 0)
m_savedCursorFlashTime = QGuiApplication::styleHints()->cursorFlashTime();
- const bool blink = on || !fakeVimSettings()->useFakeVim.value();
+ const bool blink = on || !settings().useFakeVim();
QGuiApplication::styleHints()->setCursorFlashTime(blink ? m_savedCursorFlashTime : 0);
}
@@ -1945,11 +1854,22 @@ void FakeVimPluginPrivate::handleExCommand(FakeVimHandler *handler, bool *handle
if (editor)
editor->setFocus();
+ auto editorFromHandler = [this, handler]() -> Core::IEditor * {
+ auto itEditor = std::find_if(m_editorToHandler.cbegin(),
+ m_editorToHandler.cend(),
+ [handler](const HandlerAndData &handlerAndData) {
+ return handlerAndData.handler == handler;
+ });
+ if (itEditor != m_editorToHandler.cend())
+ return itEditor.key();
+ return nullptr;
+ };
+
*handled = true;
if ((cmd.matches("w", "write") || cmd.cmd == "wq") && cmd.args.isEmpty()) {
// :w[rite]
bool saved = false;
- IEditor *editor = m_editorToHandler.key(handler);
+ IEditor *editor = editorFromHandler();
const QString fileName = handler->currentFileName();
if (editor && editor->document()->filePath().toString() == fileName) {
triggerAction(Core::Constants::SAVE);
@@ -1961,7 +1881,7 @@ void FakeVimPluginPrivate::handleExCommand(FakeVimHandler *handler, bool *handle
handler->showMessage(MessageInfo, Tr::tr("\"%1\" %2 %3L, %4C written")
.arg(fileName).arg(' ').arg(ba.count('\n')).arg(ba.size()));
if (cmd.cmd == "wq")
- emit delayedQuitRequested(cmd.hasBang, m_editorToHandler.key(handler));
+ emit delayedQuitRequested(cmd.hasBang, editor);
}
}
}
@@ -1980,16 +1900,18 @@ void FakeVimPluginPrivate::handleExCommand(FakeVimHandler *handler, bool *handle
emit delayedQuitAllRequested(cmd.hasBang);
} else if (cmd.matches("q", "quit")) {
// :q[uit]
- emit delayedQuitRequested(cmd.hasBang, m_editorToHandler.key(handler));
+ emit delayedQuitRequested(cmd.hasBang, editorFromHandler());
} else if (cmd.matches("qa", "qall")) {
// :qa[ll]
emit delayedQuitAllRequested(cmd.hasBang);
} else if (cmd.matches("sp", "split")) {
// :sp[lit]
triggerAction(Core::Constants::SPLIT);
+ updateAllHightLights();
} else if (cmd.matches("vs", "vsplit")) {
// :vs[plit]
triggerAction(Core::Constants::SPLIT_SIDE_BY_SIDE);
+ updateAllHightLights();
} else if (cmd.matches("mak", "make")) {
// :mak[e][!] [arguments]
triggerAction(ProjectExplorer::Constants::BUILD);
@@ -2059,7 +1981,7 @@ void FakeVimPluginPrivate::handleDelayedQuitAll(bool forced)
void FakeVimPluginPrivate::quitFakeVim()
{
- fakeVimSettings()->useFakeVim.setValue(false);
+ settings().useFakeVim.setValue(false);
}
void FakeVimPluginPrivate::resetCommandBuffer()
@@ -2096,28 +2018,6 @@ void FakeVimPluginPrivate::switchToFile(int n)
EditorManager::activateEditorForEntry(DocumentModel::entries().at(n));
}
-ExCommandMap FakeVimExCommandsWidget::exCommandMapFromWidget()
-{
- ExCommandMap map;
- int n = commandList()->topLevelItemCount();
- for (int i = 0; i != n; ++i) {
- QTreeWidgetItem *section = commandList()->topLevelItem(i);
- int m = section->childCount();
- for (int j = 0; j != m; ++j) {
- QTreeWidgetItem *item = section->child(j);
- const QString name = item->data(0, CommandRole).toString();
- const QString regex = item->data(2, Qt::DisplayRole).toString();
- const QString pattern = dd->m_defaultExCommandMap.value(name).pattern();
- if ((regex.isEmpty() && pattern.isEmpty())
- || (!regex.isEmpty() && pattern == regex))
- continue;
- map[name] = QRegularExpression(regex);
- }
- }
- return map;
-}
-
-
///////////////////////////////////////////////////////////////////////
//
@@ -2164,7 +2064,7 @@ void FakeVimPlugin::setupTest(QString *title, FakeVimHandler **handler, QWidget
IEditor *iedit = EditorManager::openEditorWithContents(Id(), title);
EditorManager::activateEditor(iedit);
*edit = iedit->widget();
- *handler = dd->m_editorToHandler.value(iedit, 0);
+ *handler = dd->m_editorToHandler.value(iedit, {}).handler;
(*handler)->setupWidget();
(*handler)->handleCommand("set startofline");
diff --git a/src/plugins/fossil/configuredialog.cpp b/src/plugins/fossil/configuredialog.cpp
index 69c74ad8c8..e5b6c8f3e7 100644
--- a/src/plugins/fossil/configuredialog.cpp
+++ b/src/plugins/fossil/configuredialog.cpp
@@ -67,7 +67,7 @@ ConfigureDialog::ConfigureDialog(QWidget *parent) : QDialog(parent),
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
title(Tr::tr("Repository User")),
diff --git a/src/plugins/fossil/fossilclient.cpp b/src/plugins/fossil/fossilclient.cpp
index 16038150cb..f7bb29f2ae 100644
--- a/src/plugins/fossil/fossilclient.cpp
+++ b/src/plugins/fossil/fossilclient.cpp
@@ -16,9 +16,9 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/processenums.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/utilsicons.h>
#include <QSyntaxHighlighter>
@@ -55,9 +55,9 @@ public:
addReloadButton();
if (features.testFlag(FossilClient::DiffIgnoreWhiteSpaceFeature)) {
mapSetting(addToggleButton("-w", Tr::tr("Ignore All Whitespace")),
- &client->settings().diffIgnoreAllWhiteSpace);
+ &settings().diffIgnoreAllWhiteSpace);
mapSetting(addToggleButton("--strip-trailing-cr", Tr::tr("Strip Trailing CR")),
- &client->settings().diffStripTrailingCR);
+ &settings().diffStripTrailingCR);
}
}
};
@@ -73,20 +73,19 @@ public:
{
QTC_ASSERT(client, return);
- FossilSettings &settings = client->settings();
FossilClient::SupportedFeatures features = client->supportedFeatures();
if (features.testFlag(FossilClient::AnnotateBlameFeature)) {
mapSetting(addToggleButton("|BLAME|", Tr::tr("Show Committers")),
- &settings.annotateShowCommitters);
+ &settings().annotateShowCommitters);
}
// Force listVersions setting to false by default.
// This way the annotated line number would not get offset by the version list.
- settings.annotateListVersions.setValue(false);
+ settings().annotateListVersions.setValue(false);
mapSetting(addToggleButton("--log", Tr::tr("List Versions")),
- &settings.annotateListVersions);
+ &settings().annotateListVersions);
}
};
@@ -108,12 +107,9 @@ class FossilLogConfig : public VcsBaseEditorConfig
Q_OBJECT
public:
- FossilLogConfig(FossilClient *client, QToolBar *toolBar) :
- VcsBaseEditorConfig(toolBar),
- m_client(client)
+ FossilLogConfig(QToolBar *toolBar)
+ : VcsBaseEditorConfig(toolBar)
{
- QTC_ASSERT(client, return);
-
addReloadButton();
addLineageComboBox();
addVerboseToggleButton();
@@ -122,8 +118,6 @@ public:
void addLineageComboBox()
{
- FossilSettings &settings = m_client->settings();
-
// ancestors/descendants filter
// This is a positional argument not an option.
// Normally it takes the checkin/branch/tag as an additional parameter
@@ -137,23 +131,19 @@ public:
ChoiceItem(Tr::tr("Unfiltered"), "")
};
mapSetting(addChoices(Tr::tr("Lineage"), QStringList("|LINEAGE|%1|current"), lineageFilterChoices),
- &settings.timelineLineageFilter);
+ &settings().timelineLineageFilter);
}
void addVerboseToggleButton()
{
- FossilSettings &settings = m_client->settings();
-
// show files
mapSetting(addToggleButton("-showfiles", Tr::tr("Verbose"),
Tr::tr("Show files changed in each revision")),
- &settings.timelineVerbose);
+ &settings().timelineVerbose);
}
void addItemTypeComboBox()
{
- FossilSettings &settings = m_client->settings();
-
// option: -t <val>
const QList<ChoiceItem> itemTypeChoices = {
ChoiceItem(Tr::tr("All Items"), "all"),
@@ -169,7 +159,7 @@ public:
// Fossil expects separate arguments for option and value ( i.e. "-t" "all")
// so we need to handle the splitting explicitly in arguments().
mapSetting(addChoices(Tr::tr("Item Types"), QStringList("-t %1"), itemTypeChoices),
- &settings.timelineItemType);
+ &settings().timelineItemType);
}
QStringList arguments() const final
@@ -199,9 +189,6 @@ public:
}
return args;
}
-
-private:
- FossilClient *m_client;
};
unsigned FossilClient::makeVersionNumber(int major, int minor, int patch)
@@ -224,22 +211,22 @@ QString FossilClient::makeVersionString(unsigned version)
.arg(versionPart(version));
}
-FossilClient::FossilClient(FossilSettings *settings)
- : VcsBaseClient(settings), m_settings(settings)
+FossilSettings &FossilClient::settings() const
{
- setDiffConfigCreator([this](QToolBar *toolBar) {
- return new FossilDiffConfig(this, toolBar);
- });
+ return Internal::settings();
}
-FossilSettings &FossilClient::settings() const
+FossilClient::FossilClient()
+ : VcsBaseClient(&Internal::settings())
{
- return *m_settings;
+ setDiffConfigCreator([this](QToolBar *toolBar) {
+ return new FossilDiffConfig(this, toolBar);
+ });
}
unsigned int FossilClient::synchronousBinaryVersion() const
{
- if (settings().binaryPath.value().isEmpty())
+ if (settings().binaryPath().isEmpty())
return 0;
const CommandResult result = vcsSynchronousExec({}, QStringList{"version"});
@@ -599,7 +586,7 @@ bool FossilClient::synchronousCreateRepository(const FilePath &workingDirectory,
// use the configured default user for admin
const QString repoName = workingDirectory.fileName().simplified();
- const QString repoPath = settings().defaultRepoPath.value();
+ const FilePath repoPath = settings().defaultRepoPath();
const QString adminUser = settings().userName.value();
if (repoName.isEmpty() || repoPath.isEmpty())
@@ -609,8 +596,7 @@ bool FossilClient::synchronousCreateRepository(const FilePath &workingDirectory,
// @TODO: what about --template options?
const FilePath fullRepoName = FilePath::fromStringWithExtension(repoName, Constants::FOSSIL_FILE_SUFFIX);
- const FilePath repoFilePath = FilePath::fromString(repoPath)
- .pathAppended(fullRepoName.toString());
+ const FilePath repoFilePath = repoPath.pathAppended(fullRepoName.toString());
QStringList args(vcsCommandString(CreateRepositoryCommand));
if (!adminUser.isEmpty())
args << "--admin-user" << adminUser;
@@ -1177,7 +1163,7 @@ VcsBaseEditorConfig *FossilClient::createLogCurrentFileEditor(VcsBaseEditorWidge
VcsBaseEditorConfig *FossilClient::createLogEditor(VcsBaseEditorWidget *editor)
{
- return new FossilLogConfig(this, editor->toolBar());
+ return new FossilLogConfig(editor->toolBar());
}
} // namespace Internal
diff --git a/src/plugins/fossil/fossilclient.h b/src/plugins/fossil/fossilclient.h
index 21dfeaae04..b301637846 100644
--- a/src/plugins/fossil/fossilclient.h
+++ b/src/plugins/fossil/fossilclient.h
@@ -41,7 +41,7 @@ public:
static unsigned makeVersionNumber(int major, int minor, int patch);
static QString makeVersionString(unsigned version);
- explicit FossilClient(FossilSettings *settings);
+ FossilClient();
FossilSettings &settings() const;
unsigned int synchronousBinaryVersion() const;
@@ -107,7 +107,6 @@ private:
VcsBase::VcsBaseEditorConfig *createLogEditor(VcsBase::VcsBaseEditorWidget *editor);
friend class FossilPluginPrivate;
- FossilSettings *m_settings = nullptr;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(FossilClient::SupportedFeatures)
diff --git a/src/plugins/fossil/fossilcommitwidget.cpp b/src/plugins/fossil/fossilcommitwidget.cpp
index 5658ed9f76..72fba5ef86 100644
--- a/src/plugins/fossil/fossilcommitwidget.cpp
+++ b/src/plugins/fossil/fossilcommitwidget.cpp
@@ -93,8 +93,8 @@ FossilCommitWidget::FossilCommitWidget() : m_commitPanel(new QWidget)
m_invalidBranchLabel->setType(InfoLabel::Error);
m_isPrivateCheckBox = new QCheckBox(Tr::tr("Private"));
- m_isPrivateCheckBox->setToolTip(Tr::tr("Create a private check-in that is never synced.\n"
- "Children of private check-ins are automatically private.\n"
+ m_isPrivateCheckBox->setToolTip("<html>" + Tr::tr("Create a private check-in that is never synced. "
+ "Children of private check-ins are automatically private. "
"Private check-ins are not pushed to the remote repository by default."));
m_tagsLineEdit = new QLineEdit;
@@ -120,8 +120,9 @@ FossilCommitWidget::FossilCommitWidget() : m_commitPanel(new QWidget)
Tr::tr("Tags:"), m_tagsLineEdit, br,
Tr::tr("Author:"), m_authorLineEdit, st,
}
- }
- }.attachTo(m_commitPanel, WithoutMargins);
+ },
+ noMargin
+ }.attachTo(m_commitPanel);
insertTopWidget(m_commitPanel);
new FossilSubmitHighlighter(descriptionEdit());
diff --git a/src/plugins/fossil/fossilplugin.cpp b/src/plugins/fossil/fossilplugin.cpp
index 94f800e1ea..1f6f7d9306 100644
--- a/src/plugins/fossil/fossilplugin.cpp
+++ b/src/plugins/fossil/fossilplugin.cpp
@@ -187,10 +187,8 @@ public:
bool pullOrPush(SyncMode mode);
// Variables
- FossilSettings m_fossilSettings;
- FossilClient m_client{&m_fossilSettings};
-
- OptionsPage optionPage{[this] { configurationChanged(); }, &m_fossilSettings};
+ FossilSettings m_settings;
+ FossilClient m_client;
VcsSubmitEditorFactory submitEditorFactory {
submitEditorParameters,
@@ -274,11 +272,6 @@ void FossilPlugin::extensionsInitialized()
dd->extensionsInitialized();
}
-const FossilSettings &FossilPlugin::settings()
-{
- return dd->m_fossilSettings;
-}
-
FossilClient *FossilPlugin::client()
{
return &dd->m_client;
@@ -293,11 +286,13 @@ FossilPluginPrivate::FossilPluginPrivate()
connect(&m_client, &VcsBase::VcsBaseClient::changed, this, &FossilPluginPrivate::changed);
m_commandLocator = new Core::CommandLocator("Fossil", "fossil", "fossil", this);
+ m_commandLocator->setDescription(Tr::tr("Triggers a Fossil version control operation."));
ProjectExplorer::JsonWizardFactory::addWizardPath(Utils::FilePath::fromString(Constants::WIZARD_PATH));
- Core::JsExpander::registerGlobalObject("Fossil", [this] {
- return new FossilJsExtension(&m_fossilSettings);
- });
+ Core::JsExpander::registerGlobalObject("Fossil", [] { return new FossilJsExtension; });
+
+ connect(&settings(), &AspectContainer::changed,
+ this, &IVersionControl::configurationChanged);
createMenu(context);
}
@@ -337,7 +332,7 @@ void FossilPluginPrivate::createFileActions(const Core::Context &context)
command = Core::ActionManager::registerAction(m_diffFile, Constants::DIFF, context);
command->setAttribute(Core::Command::CA_UpdateText);
command->setDefaultKeySequence(QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+I,Meta+D")
- : Tr::tr("ALT+I,Alt+D")));
+ : Tr::tr("Alt+I,Alt+D")));
connect(m_diffFile, &QAction::triggered, this, &FossilPluginPrivate::diffCurrentFile);
m_fossilContainer->addAction(command);
m_commandLocator->appendCommand(command);
@@ -346,7 +341,7 @@ void FossilPluginPrivate::createFileActions(const Core::Context &context)
command = Core::ActionManager::registerAction(m_logFile, Constants::LOG, context);
command->setAttribute(Core::Command::CA_UpdateText);
command->setDefaultKeySequence(QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+I,Meta+L")
- : Tr::tr("ALT+I,Alt+L")));
+ : Tr::tr("Alt+I,Alt+L")));
connect(m_logFile, &QAction::triggered, this, &FossilPluginPrivate::logCurrentFile);
m_fossilContainer->addAction(command);
m_commandLocator->appendCommand(command);
@@ -355,7 +350,7 @@ void FossilPluginPrivate::createFileActions(const Core::Context &context)
command = Core::ActionManager::registerAction(m_statusFile, Constants::STATUS, context);
command->setAttribute(Core::Command::CA_UpdateText);
command->setDefaultKeySequence(QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+I,Meta+S")
- : Tr::tr("ALT+I,Alt+S")));
+ : Tr::tr("Alt+I,Alt+S")));
connect(m_statusFile, &QAction::triggered, this, &FossilPluginPrivate::statusCurrentFile);
m_fossilContainer->addAction(command);
m_commandLocator->appendCommand(command);
@@ -417,10 +412,10 @@ void FossilPluginPrivate::logCurrentFile()
QTC_ASSERT(state.hasFile(), return);
FossilClient::SupportedFeatures features = m_client.supportedFeatures();
QStringList extraOptions;
- extraOptions << "-n" << QString::number(m_client.settings().logCount.value());
+ extraOptions << "-n" << QString::number(m_client.settings().logCount());
if (features.testFlag(FossilClient::TimelineWidthFeature))
- extraOptions << "-W" << QString::number(m_client.settings().timelineWidth.value());
+ extraOptions << "-W" << QString::number(m_client.settings().timelineWidth());
// disable annotate context menu for older client versions, used to be supported for current revision only
bool enableAnnotationContextMenu = features.testFlag(FossilClient::AnnotateRevisionFeature);
@@ -468,7 +463,7 @@ void FossilPluginPrivate::createDirectoryActions(const Core::Context &context)
m_repositoryActionList.append(action);
command = Core::ActionManager::registerAction(action, Constants::LOGMULTI, context);
command->setDefaultKeySequence(QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+I,Meta+T")
- : Tr::tr("ALT+I,Alt+T")));
+ : Tr::tr("Alt+I,Alt+T")));
connect(action, &QAction::triggered, this, &FossilPluginPrivate::logRepository);
m_fossilContainer->addAction(command);
m_commandLocator->appendCommand(command);
@@ -502,10 +497,10 @@ void FossilPluginPrivate::logRepository()
QTC_ASSERT(state.hasTopLevel(), return);
FossilClient::SupportedFeatures features = m_client.supportedFeatures();
QStringList extraOptions;
- extraOptions << "-n" << QString::number(m_client.settings().logCount.value());
+ extraOptions << "-n" << QString::number(m_client.settings().logCount());
if (features.testFlag(FossilClient::TimelineWidthFeature))
- extraOptions << "-W" << QString::number(m_client.settings().timelineWidth.value());
+ extraOptions << "-W" << QString::number(m_client.settings().timelineWidth());
m_client.log(state.topLevel(), {}, extraOptions);
}
@@ -550,7 +545,7 @@ void FossilPluginPrivate::createRepositoryActions(const Core::Context &context)
m_repositoryActionList.append(action);
command = Core::ActionManager::registerAction(action, Constants::UPDATE, context);
command->setDefaultKeySequence(QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+I,Meta+U")
- : Tr::tr("ALT+I,Alt+U")));
+ : Tr::tr("Alt+I,Alt+U")));
connect(action, &QAction::triggered, this, &FossilPluginPrivate::update);
m_fossilContainer->addAction(command);
m_commandLocator->appendCommand(command);
@@ -559,12 +554,12 @@ void FossilPluginPrivate::createRepositoryActions(const Core::Context &context)
m_repositoryActionList.append(action);
command = Core::ActionManager::registerAction(action, Constants::COMMIT, context);
command->setDefaultKeySequence(QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+I,Meta+C")
- : Tr::tr("ALT+I,Alt+C")));
+ : Tr::tr("Alt+I,Alt+C")));
connect(action, &QAction::triggered, this, &FossilPluginPrivate::commit);
m_fossilContainer->addAction(command);
m_commandLocator->appendCommand(command);
- action = new QAction(Tr::tr("Settings ..."), this);
+ action = new QAction(Tr::tr("Settings..."), this);
m_repositoryActionList.append(action);
command = Core::ActionManager::registerAction(action, Constants::CONFIGURE_REPOSITORY, context);
connect(action, &QAction::triggered, this, &FossilPluginPrivate::configureRepository);
@@ -597,7 +592,7 @@ bool FossilPluginPrivate::pullOrPush(FossilPluginPrivate::SyncMode mode)
QTC_ASSERT(state.hasTopLevel(), return false);
PullOrPushDialog dialog(pullOrPushMode, Core::ICore::dialogParent());
- dialog.setLocalBaseDirectory(m_client.settings().defaultRepoPath.value());
+ dialog.setLocalBaseDirectory(m_client.settings().defaultRepoPath());
const QString defaultURL(m_client.synchronousGetRepositoryURL(state.topLevel()));
dialog.setDefaultRemoteLocation(defaultURL);
if (dialog.exec() != QDialog::Accepted)
@@ -839,7 +834,7 @@ void FossilPluginPrivate::updateActions(VcsBase::VcsBasePluginPrivate::ActionSta
m_revertFile->setParameter(filename);
m_statusFile->setParameter(filename);
- for (QAction *repoAction : qAsConst(m_repositoryActionList))
+ for (QAction *repoAction : std::as_const(m_repositoryActionList))
repoAction->setEnabled(repoEnabled);
}
@@ -873,24 +868,19 @@ bool FossilPluginPrivate::managesFile(const FilePath &workingDirectory, const QS
bool FossilPluginPrivate::isConfigured() const
{
- const Utils::FilePath binary = m_client.vcsBinary();
+ const FilePath binary = m_client.vcsBinary();
if (binary.isEmpty())
return false;
- const QFileInfo fi = binary.toFileInfo();
- if ( !(fi.exists() && fi.isFile() && fi.isExecutable()) )
+ if (!binary.isExecutableFile())
return false;
// Local repositories default path must be set and exist
- const QString repoPath = m_client.settings().defaultRepoPath.value();
+ const FilePath repoPath = m_client.settings().defaultRepoPath();
if (repoPath.isEmpty())
return false;
- const QDir dir(repoPath);
- if (!dir.exists())
- return false;
-
- return true;
+ return repoPath.isReadableDir();
}
bool FossilPluginPrivate::supportsOperation(Operation operation) const
@@ -1093,7 +1083,7 @@ RevertDialog::RevertDialog(const QString &title, QWidget *parent)
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Form {
Tr::tr("Revision"), m_revisionLineEdit, br,
}.attachTo(groupBox);
diff --git a/src/plugins/fossil/fossilplugin.h b/src/plugins/fossil/fossilplugin.h
index b3a45f62db..7297cd05ae 100644
--- a/src/plugins/fossil/fossilplugin.h
+++ b/src/plugins/fossil/fossilplugin.h
@@ -3,8 +3,6 @@
#pragma once
-#include "fossilsettings.h"
-
#include <vcsbase/vcsbaseclient.h>
#include <vcsbase/vcsbaseplugin.h>
#include <coreplugin/icontext.h>
@@ -25,7 +23,6 @@ class FossilPlugin final : public ExtensionSystem::IPlugin
void extensionsInitialized() final;
public:
- static const FossilSettings &settings();
static FossilClient *client();
#ifdef WITH_TESTS
diff --git a/src/plugins/fossil/fossilsettings.cpp b/src/plugins/fossil/fossilsettings.cpp
index 130ff5c2c5..0662445b13 100644
--- a/src/plugins/fossil/fossilsettings.cpp
+++ b/src/plugins/fossil/fossilsettings.cpp
@@ -15,160 +15,110 @@
using namespace Utils;
-namespace Fossil {
-namespace Internal {
+namespace Fossil::Internal {
+
+static FossilSettings *theSettings;
+
+FossilSettings &settings()
+{
+ return *theSettings;
+}
FossilSettings::FossilSettings()
{
+ theSettings = this;
+
setSettingsGroup(Constants::FOSSIL);
- setAutoApply(false);
+ setId(Constants::VCS_ID_FOSSIL);
+ setDisplayName(Tr::tr("Fossil"));
+ setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
- registerAspect(&binaryPath);
- binaryPath.setDisplayStyle(StringAspect::PathChooserDisplay);
binaryPath.setExpectedKind(PathChooser::ExistingCommand);
binaryPath.setDefaultValue(Constants::FOSSILDEFAULT);
binaryPath.setDisplayName(Tr::tr("Fossil Command"));
binaryPath.setHistoryCompleter("Fossil.Command.History");
binaryPath.setLabelText(Tr::tr("Command:"));
- registerAspect(&defaultRepoPath);
defaultRepoPath.setSettingsKey("defaultRepoPath");
- defaultRepoPath.setDisplayStyle(StringAspect::PathChooserDisplay);
defaultRepoPath.setExpectedKind(PathChooser::Directory);
defaultRepoPath.setDisplayName(Tr::tr("Fossil Repositories"));
defaultRepoPath.setLabelText(Tr::tr("Default path:"));
defaultRepoPath.setToolTip(Tr::tr("Directory to store local repositories by default."));
- registerAspect(&userName);
userName.setDisplayStyle(StringAspect::LineEditDisplay);
userName.setLabelText(Tr::tr("Default user:"));
userName.setToolTip(Tr::tr("Existing user to become an author of changes made to the repository."));
- registerAspect(&sslIdentityFile);
sslIdentityFile.setSettingsKey("sslIdentityFile");
- sslIdentityFile.setDisplayStyle(StringAspect::PathChooserDisplay);
sslIdentityFile.setExpectedKind(PathChooser::File);
sslIdentityFile.setDisplayName(Tr::tr("SSL/TLS Identity Key"));
sslIdentityFile.setLabelText(Tr::tr("SSL/TLS identity:"));
sslIdentityFile.setToolTip(Tr::tr("SSL/TLS client identity key to use if requested by the server."));
- registerAspect(&diffIgnoreAllWhiteSpace);
diffIgnoreAllWhiteSpace.setSettingsKey("diffIgnoreAllWhiteSpace");
- registerAspect(&diffStripTrailingCR);
diffStripTrailingCR.setSettingsKey("diffStripTrailingCR");
- registerAspect(&annotateShowCommitters);
annotateShowCommitters.setSettingsKey("annotateShowCommitters");
- registerAspect(&annotateListVersions);
annotateListVersions.setSettingsKey("annotateListVersions");
- registerAspect(&timelineWidth);
timelineWidth.setSettingsKey("timelineWidth");
timelineWidth.setLabelText(Tr::tr("Log width:"));
timelineWidth.setToolTip(Tr::tr("The width of log entry line (>20). "
"Choose 0 to see a single line per entry."));
- registerAspect(&timelineLineageFilter);
timelineLineageFilter.setSettingsKey("timelineLineageFilter");
- registerAspect(&timelineVerbose);
timelineVerbose.setSettingsKey("timelineVerbose");
- registerAspect(&timelineItemType);
timelineItemType.setDefaultValue("all");
timelineItemType.setSettingsKey("timelineItemType");
- registerAspect(&disableAutosync);
disableAutosync.setSettingsKey("disableAutosync");
disableAutosync.setDefaultValue(true);
disableAutosync.setLabelText(Tr::tr("Disable auto-sync"));
disableAutosync.setToolTip(Tr::tr("Disable automatic pull prior to commit or update and "
"automatic push after commit or tag or branch creation."));
- registerAspect(&timeout);
timeout.setLabelText(Tr::tr("Timeout:"));
timeout.setSuffix(Tr::tr("s"));
- registerAspect(&logCount);
logCount.setLabelText(Tr::tr("Log count:"));
logCount.setToolTip(Tr::tr("The number of recent commit log entries to show. "
"Choose 0 to see all entries."));
-};
-
-// OptionsPage
-class OptionsPageWidget final : public Core::IOptionsPageWidget
-{
-public:
- OptionsPageWidget(const std::function<void()> &onApply, FossilSettings *settings);
- void apply() final;
-
-private:
- const std::function<void()> m_onApply;
- FossilSettings *m_settings;
-};
+ setLayouter([this] {
+ using namespace Layouting;
+ return Column {
+ Group {
+ title(Tr::tr("Configuration")),
+ Row { binaryPath }
+ },
-void OptionsPageWidget::apply()
-{
- if (!m_settings->isDirty())
- return;
+ Group {
+ title(Tr::tr("Local Repositories")),
+ Row { defaultRepoPath }
+ },
- m_settings->apply();
- m_onApply();
-}
+ Group {
+ title(Tr::tr("User")),
+ Form {
+ userName, br,
+ sslIdentityFile
+ }
+ },
-OptionsPageWidget::OptionsPageWidget(const std::function<void()> &onApply, FossilSettings *settings) :
- m_onApply(onApply),
- m_settings(settings)
-{
- FossilSettings &s = *m_settings;
-
- using namespace Layouting;
-
- Column {
- Group {
- title(Tr::tr("Configuration")),
- Row { s.binaryPath }
- },
-
- Group {
- title(Tr::tr("Local Repositories")),
- Row { s.defaultRepoPath }
- },
- Group {
- title(Tr::tr("User")),
- Form {
- s.userName, br,
- s.sslIdentityFile
- }
- },
-
- Group {
- title(Tr::tr("Miscellaneous")),
- Column {
- Row {
- s.logCount,
- s.timelineWidth,
- s.timeout,
- st
+ Group {
+ title(Tr::tr("Miscellaneous")),
+ Column {
+ Row { logCount, timelineWidth, timeout, st },
+ disableAutosync
},
- s.disableAutosync
},
- },
- st
-
- }.attachTo(this);
-}
-
-OptionsPage::OptionsPage(const std::function<void()> &onApply, FossilSettings *settings)
-{
- setId(Constants::VCS_ID_FOSSIL);
- setDisplayName(Tr::tr("Fossil"));
- setWidgetCreator([onApply, settings]() { return new OptionsPageWidget(onApply, settings); });
- setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
+ st
+ };
+ });
}
-} // Internal
-} // Fossil
+} // Fossil::Internal
diff --git a/src/plugins/fossil/fossilsettings.h b/src/plugins/fossil/fossilsettings.h
index e4133393f6..8e56b1985c 100644
--- a/src/plugins/fossil/fossilsettings.h
+++ b/src/plugins/fossil/fossilsettings.h
@@ -3,30 +3,30 @@
#pragma once
-#include <coreplugin/dialogs/ioptionspage.h>
#include <vcsbase/vcsbaseclientsettings.h>
-namespace Fossil {
-namespace Internal {
+namespace Fossil::Internal {
class FossilSettings : public VcsBase::VcsBaseSettings
{
public:
- Utils::StringAspect defaultRepoPath;
- Utils::StringAspect sslIdentityFile;
- Utils::BoolAspect diffIgnoreAllWhiteSpace;
- Utils::BoolAspect diffStripTrailingCR;
- Utils::BoolAspect annotateShowCommitters;
- Utils::BoolAspect annotateListVersions;
- Utils::IntegerAspect timelineWidth;
- Utils::StringAspect timelineLineageFilter;
- Utils::BoolAspect timelineVerbose;
- Utils::StringAspect timelineItemType;
- Utils::BoolAspect disableAutosync;
-
FossilSettings();
+
+ Utils::FilePathAspect defaultRepoPath{this};
+ Utils::FilePathAspect sslIdentityFile{this};
+ Utils::BoolAspect diffIgnoreAllWhiteSpace{this};
+ Utils::BoolAspect diffStripTrailingCR{this};
+ Utils::BoolAspect annotateShowCommitters{this};
+ Utils::BoolAspect annotateListVersions{this};
+ Utils::IntegerAspect timelineWidth{this};
+ Utils::StringAspect timelineLineageFilter{this};
+ Utils::BoolAspect timelineVerbose{this};
+ Utils::StringAspect timelineItemType{this};
+ Utils::BoolAspect disableAutosync{this};
};
+FossilSettings &settings();
+
struct RepositorySettings
{
enum AutosyncMode {AutosyncOff, AutosyncOn, AutosyncPullOnly};
@@ -34,20 +34,13 @@ struct RepositorySettings
QString user;
QString sslIdentityFile;
AutosyncMode autosync = AutosyncOn;
-};
-inline bool operator==(const RepositorySettings &lh, const RepositorySettings &rh)
-{
- return (lh.user == rh.user &&
- lh.sslIdentityFile == rh.sslIdentityFile &&
- lh.autosync == rh.autosync);
-}
-
-class OptionsPage : public Core::IOptionsPage
-{
-public:
- OptionsPage(const std::function<void()> &onApply, FossilSettings *settings);
+ friend bool operator==(const RepositorySettings &lh, const RepositorySettings &rh)
+ {
+ return lh.user == rh.user
+ && lh.sslIdentityFile == rh.sslIdentityFile
+ && lh.autosync == rh.autosync;
+ }
};
-} // namespace Internal
-} // namespace Fossil
+} // Fossil::Internal
diff --git a/src/plugins/fossil/pullorpushdialog.cpp b/src/plugins/fossil/pullorpushdialog.cpp
index 05bdf136f1..b6473a68b4 100644
--- a/src/plugins/fossil/pullorpushdialog.cpp
+++ b/src/plugins/fossil/pullorpushdialog.cpp
@@ -15,8 +15,7 @@
#include <QLineEdit>
#include <QRadioButton>
-namespace Fossil {
-namespace Internal {
+namespace Fossil::Internal {
PullOrPushDialog::PullOrPushDialog(Mode mode, QWidget *parent)
: QDialog(parent)
@@ -35,7 +34,7 @@ PullOrPushDialog::PullOrPushDialog(Mode mode, QWidget *parent)
m_localPathChooser->setPromptDialogFilter(Tr::tr(Constants::FOSSIL_FILE_FILTER));
m_urlButton = new QRadioButton(Tr::tr("Specify URL:"));
- m_urlButton->setToolTip(Tr::tr("For example: https://[user[:pass]@]host[:port]/[path]"));
+ m_urlButton->setToolTip(Tr::tr("For example: \"https://[user[:pass]@]host[:port]/[path]\"."));
m_urlLineEdit = new QLineEdit;
m_urlLineEdit->setEnabled(false);
@@ -52,7 +51,7 @@ PullOrPushDialog::PullOrPushDialog(Mode mode, QWidget *parent)
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
title(Tr::tr("Remote Location")),
@@ -106,10 +105,9 @@ void PullOrPushDialog::setDefaultRemoteLocation(const QString &url)
m_urlLineEdit->setText(url);
}
-void PullOrPushDialog::setLocalBaseDirectory(const QString &dir)
+void PullOrPushDialog::setLocalBaseDirectory(const Utils::FilePath &dir)
{
- m_localPathChooser->setBaseDirectory(Utils::FilePath::fromString(dir));
+ m_localPathChooser->setBaseDirectory(dir);
}
-} // namespace Internal
-} // namespace Fossil
+} // Fossil::Internal
diff --git a/src/plugins/fossil/pullorpushdialog.h b/src/plugins/fossil/pullorpushdialog.h
index 1d921733b6..5594947cb0 100644
--- a/src/plugins/fossil/pullorpushdialog.h
+++ b/src/plugins/fossil/pullorpushdialog.h
@@ -3,6 +3,8 @@
#pragma once
+#include <utils/filepath.h>
+
#include <QDialog>
QT_BEGIN_NAMESPACE
@@ -13,13 +15,10 @@ QT_END_NAMESPACE
namespace Utils { class PathChooser; }
-namespace Fossil {
-namespace Internal {
+namespace Fossil::Internal {
class PullOrPushDialog : public QDialog
{
- Q_OBJECT
-
public:
enum Mode {
PullMode,
@@ -33,7 +32,7 @@ public:
bool isRememberOptionEnabled() const;
bool isPrivateOptionEnabled() const;
void setDefaultRemoteLocation(const QString &url);
- void setLocalBaseDirectory(const QString &dir);
+ void setLocalBaseDirectory(const Utils::FilePath &dir);
// Pull-specific options
// Push-specific options
@@ -47,5 +46,4 @@ private:
QCheckBox *m_privateCheckBox;
};
-} // namespace Internal
-} // namespace Fossil
+} // Fossil::Internal
diff --git a/src/plugins/fossil/wizard/fossiljsextension.cpp b/src/plugins/fossil/wizard/fossiljsextension.cpp
index 66747db567..4c013b503a 100644
--- a/src/plugins/fossil/wizard/fossiljsextension.cpp
+++ b/src/plugins/fossil/wizard/fossiljsextension.cpp
@@ -17,19 +17,6 @@ using namespace Core;
namespace Fossil {
namespace Internal {
-class FossilJsExtensionPrivate {
-public:
- FossilJsExtensionPrivate(FossilSettings *settings) :
- m_vscId(Constants::VCS_ID_FOSSIL),
- m_settings(settings)
- {
- }
-
- Utils::Id m_vscId;
- FossilSettings *m_settings;
-};
-
-
QMap<QString, QString> FossilJsExtension::parseArgOptions(const QStringList &args)
{
QMap<QString, QString> options;
@@ -42,24 +29,19 @@ QMap<QString, QString> FossilJsExtension::parseArgOptions(const QStringList &arg
return options;
}
-FossilJsExtension::FossilJsExtension(FossilSettings *settings) :
- d(new FossilJsExtensionPrivate(settings))
-{ }
+FossilJsExtension::FossilJsExtension() = default;
-FossilJsExtension::~FossilJsExtension()
-{
- delete d;
-}
+FossilJsExtension::~FossilJsExtension() = default;
bool FossilJsExtension::isConfigured() const
{
- IVersionControl *vc = VcsManager::versionControl(d->m_vscId);
+ IVersionControl *vc = VcsManager::versionControl(Constants::VCS_ID_FOSSIL);
return vc && vc->isConfigured();
}
QString FossilJsExtension::displayName() const
{
- IVersionControl *vc = VcsManager::versionControl(d->m_vscId);
+ IVersionControl *vc = VcsManager::versionControl(Constants::VCS_ID_FOSSIL);
return vc ? vc->displayName() : QString();
}
@@ -68,7 +50,7 @@ QString FossilJsExtension::defaultAdminUser() const
if (!isConfigured())
return QString();
- return d->m_settings->userName.value();
+ return settings().userName.value();
}
QString FossilJsExtension::defaultSslIdentityFile() const
@@ -76,7 +58,7 @@ QString FossilJsExtension::defaultSslIdentityFile() const
if (!isConfigured())
return QString();
- return d->m_settings->sslIdentityFile.value();
+ return settings().sslIdentityFile.value();
}
QString FossilJsExtension::defaultLocalRepoPath() const
@@ -84,7 +66,7 @@ QString FossilJsExtension::defaultLocalRepoPath() const
if (!isConfigured())
return QString();
- return d->m_settings->defaultRepoPath.value();
+ return settings().defaultRepoPath.value();
}
bool FossilJsExtension::defaultDisableAutosync() const
@@ -92,7 +74,7 @@ bool FossilJsExtension::defaultDisableAutosync() const
if (!isConfigured())
return false;
- return d->m_settings->disableAutosync.value();
+ return settings().disableAutosync.value();
}
} // namespace Internal
diff --git a/src/plugins/fossil/wizard/fossiljsextension.h b/src/plugins/fossil/wizard/fossiljsextension.h
index 3a37fc5280..0947693d58 100644
--- a/src/plugins/fossil/wizard/fossiljsextension.h
+++ b/src/plugins/fossil/wizard/fossiljsextension.h
@@ -12,9 +12,6 @@
namespace Fossil {
namespace Internal {
-class FossilJsExtensionPrivate;
-class FossilSettings;
-
class FossilJsExtension : public QObject
{
Q_OBJECT
@@ -22,7 +19,7 @@ class FossilJsExtension : public QObject
public:
static QMap<QString, QString> parseArgOptions(const QStringList &args);
- FossilJsExtension(FossilSettings *settings);
+ FossilJsExtension();
~FossilJsExtension();
Q_INVOKABLE bool isConfigured() const;
@@ -31,9 +28,6 @@ public:
Q_INVOKABLE QString defaultSslIdentityFile() const;
Q_INVOKABLE QString defaultLocalRepoPath() const;
Q_INVOKABLE bool defaultDisableAutosync() const;
-
-private:
- FossilJsExtensionPrivate *d = nullptr;
};
} // namespace Internal
diff --git a/src/plugins/fossil/wizard/projects/vcs/wizard.json b/src/plugins/fossil/wizard/projects/vcs/wizard.json
index 56e08b6377..7f5fb17452 100644
--- a/src/plugins/fossil/wizard/projects/vcs/wizard.json
+++ b/src/plugins/fossil/wizard/projects/vcs/wizard.json
@@ -86,7 +86,7 @@
{
"name": "Repo",
"trDisplayName": "Remote repository:",
- "trToolTip": "For example: https://[user[:pass]@]host[:port]/[path]",
+ "trToolTip": "For example: \"https://[user[:pass]@]host[:port]/[path]\".",
"type": "LineEdit",
"enabled": "%{isCloneRepo}",
"mandatory": false
diff --git a/src/plugins/genericprojectmanager/genericproject.cpp b/src/plugins/genericprojectmanager/genericproject.cpp
index fbe77e74a9..ef83f906a9 100644
--- a/src/plugins/genericprojectmanager/genericproject.cpp
+++ b/src/plugins/genericprojectmanager/genericproject.cpp
@@ -38,8 +38,8 @@
#include <utils/algorithm.h>
#include <utils/filesystemwatcher.h>
#include <utils/fileutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDir>
#include <QFileInfo>
@@ -96,7 +96,7 @@ private:
////////////////////////////////////////////////////////////////////////////////////
//
-// GenericProjectNode
+// GenericBuildSystem
//
////////////////////////////////////////////////////////////////////////////////////
@@ -401,7 +401,7 @@ static QStringList readFlags(const QString &filePath)
return QStringList();
QStringList flags;
for (const auto &line : lines)
- flags.append(ProcessArgs::splitArgs(line));
+ flags.append(ProcessArgs::splitArgs(line, HostOsInfo::hostOs()));
return flags;
}
@@ -570,8 +570,8 @@ void GenericBuildSystem::refreshCppCodeModel()
rpp.setQtVersion(kitInfo.projectPartQtVersion);
rpp.setHeaderPaths(m_projectIncludePaths);
rpp.setConfigFileName(m_configFileName);
- rpp.setFlagsForCxx({nullptr, m_cxxflags, projectDirectory().toString()});
- rpp.setFlagsForC({nullptr, m_cflags, projectDirectory().toString()});
+ rpp.setFlagsForCxx({nullptr, m_cxxflags, projectDirectory()});
+ rpp.setFlagsForC({nullptr, m_cflags, projectDirectory()});
static const auto sourceFilesToStringList = [](const SourceFiles &sourceFiles) {
return Utils::transform(sourceFiles, [](const SourceFile &f) {
diff --git a/src/plugins/git/branchadddialog.cpp b/src/plugins/git/branchadddialog.cpp
index 3dfd7ad946..011a01ea1e 100644
--- a/src/plugins/git/branchadddialog.cpp
+++ b/src/plugins/git/branchadddialog.cpp
@@ -125,7 +125,7 @@ BranchAddDialog::BranchAddDialog(const QStringList &localBranches, Type type, QW
break;
}
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row { branchNameLabel, m_branchNameEdit },
diff --git a/src/plugins/git/branchcheckoutdialog.cpp b/src/plugins/git/branchcheckoutdialog.cpp
index 9fa2e83a0b..9615c28ff6 100644
--- a/src/plugins/git/branchcheckoutdialog.cpp
+++ b/src/plugins/git/branchcheckoutdialog.cpp
@@ -45,7 +45,7 @@ BranchCheckoutDialog::BranchCheckoutDialog(QWidget *parent,
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_makeStashRadioButton,
diff --git a/src/plugins/git/branchmodel.cpp b/src/plugins/git/branchmodel.cpp
index 3c4971fa73..3bf65cf5b3 100644
--- a/src/plugins/git/branchmodel.cpp
+++ b/src/plugins/git/branchmodel.cpp
@@ -11,8 +11,8 @@
#include <vcsbase/vcsoutputwindow.h>
#include <utils/environment.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <QDateTime>
@@ -20,6 +20,7 @@
#include <set>
+using namespace Tasking;
using namespace Utils;
using namespace VcsBase;
@@ -229,6 +230,7 @@ public:
QString currentSha;
QDateTime currentDateTime;
QStringList obsoleteLocalBranches;
+ std::unique_ptr<TaskTree> refreshTask;
bool oldBranchesIncluded = false;
struct OldEntry
@@ -399,50 +401,82 @@ void BranchModel::clear()
d->obsoleteLocalBranches.clear();
}
-bool BranchModel::refresh(const FilePath &workingDirectory, QString *errorMessage)
+void BranchModel::refresh(const FilePath &workingDirectory, ShowError showError)
{
+ if (d->refreshTask) {
+ endResetModel(); // for the running task tree.
+ d->refreshTask.reset(); // old running tree is reset, no handlers are being called
+ }
beginResetModel();
clear();
if (workingDirectory.isEmpty()) {
endResetModel();
- return true;
+ return;
}
- d->currentSha = d->client->synchronousTopRevision(workingDirectory, &d->currentDateTime);
- QStringList args = {"--format=%(objectname)\t%(refname)\t%(upstream:short)\t"
- "%(*objectname)\t%(committerdate:raw)\t%(*committerdate:raw)",
- "refs/heads/**",
- "refs/remotes/**"};
- if (d->client->settings().showTags.value())
- args << "refs/tags/**";
- QString output;
- if (!d->client->synchronousForEachRefCmd(workingDirectory, args, &output, errorMessage)) {
- endResetModel();
- return false;
- }
+ const ProcessTask topRevisionProc =
+ d->client->topRevision(workingDirectory,
+ [=](const QString &ref, const QDateTime &dateTime) {
+ d->currentSha = ref;
+ d->currentDateTime = dateTime;
+ });
+
+ const auto setupForEachRef = [=](Process &process) {
+ d->workingDirectory = workingDirectory;
+ QStringList args = {"for-each-ref",
+ "--format=%(objectname)\t%(refname)\t%(upstream:short)\t"
+ "%(*objectname)\t%(committerdate:raw)\t%(*committerdate:raw)",
+ "refs/heads/**",
+ "refs/remotes/**"};
+ if (settings().showTags())
+ args << "refs/tags/**";
+ d->client->setupCommand(process, workingDirectory, args);
+ };
- d->workingDirectory = workingDirectory;
- const QStringList lines = output.split('\n');
- for (const QString &l : lines)
- d->parseOutputLine(l);
- d->flushOldEntries();
+ const auto forEachRefDone = [=](const Process &process) {
+ const QString output = process.stdOut();
+ const QStringList lines = output.split('\n');
+ for (const QString &l : lines)
+ d->parseOutputLine(l);
+ d->flushOldEntries();
+
+ d->updateAllUpstreamStatus(d->rootNode->children.at(LocalBranches));
+ if (d->currentBranch) {
+ if (d->currentBranch->isLocal())
+ d->currentBranch = nullptr;
+ setCurrentBranch();
+ }
+ if (!d->currentBranch) {
+ BranchNode *local = d->rootNode->children.at(LocalBranches);
+ d->currentBranch = d->headNode = new BranchNode(
+ Tr::tr("Detached HEAD"), "HEAD", {}, d->currentDateTime);
+ local->prepend(d->headNode);
+ }
+ };
- d->updateAllUpstreamStatus(d->rootNode->children.at(LocalBranches));
- if (d->currentBranch) {
- if (d->currentBranch->isLocal())
- d->currentBranch = nullptr;
- setCurrentBranch();
- }
- if (!d->currentBranch) {
- BranchNode *local = d->rootNode->children.at(LocalBranches);
- d->currentBranch = d->headNode = new BranchNode(Tr::tr("Detached HEAD"), "HEAD", QString(),
- d->currentDateTime);
- local->prepend(d->headNode);
- }
+ const auto forEachRefError = [=](const Process &process) {
+ if (showError == ShowError::No)
+ return;
+ const QString message = Tr::tr("Cannot run \"%1\" in \"%2\": %3")
+ .arg("git for-each-ref")
+ .arg(workingDirectory.toUserOutput())
+ .arg(process.cleanedStdErr());
+ VcsBase::VcsOutputWindow::appendError(message);
+ };
- endResetModel();
+ const auto finalize = [this] {
+ endResetModel();
+ d->refreshTask.release()->deleteLater();
+ };
- return true;
+ const Group root {
+ topRevisionProc,
+ ProcessTask(setupForEachRef, forEachRefDone, forEachRefError),
+ onGroupDone(finalize),
+ onGroupError(finalize)
+ };
+ d->refreshTask.reset(new TaskTree(root));
+ d->refreshTask->start();
}
void BranchModel::setCurrentBranch()
@@ -469,7 +503,7 @@ void BranchModel::renameBranch(const QString &oldName, const QString &newName)
&output, &errorMessage))
VcsOutputWindow::appendError(errorMessage);
else
- refresh(d->workingDirectory, &errorMessage);
+ refresh(d->workingDirectory);
}
void BranchModel::renameTag(const QString &oldName, const QString &newName)
@@ -482,7 +516,7 @@ void BranchModel::renameTag(const QString &oldName, const QString &newName)
&output, &errorMessage)) {
VcsOutputWindow::appendError(errorMessage);
} else {
- refresh(d->workingDirectory, &errorMessage);
+ refresh(d->workingDirectory);
}
}
@@ -771,7 +805,7 @@ void BranchModel::Private::parseOutputLine(const QString &line, bool force)
const qint64 age = dateTime.daysTo(QDateTime::currentDateTime());
isOld = age > Constants::OBSOLETE_COMMIT_AGE_IN_DAYS;
}
- const bool showTags = client->settings().showTags.value();
+ const bool showTags = settings().showTags();
// insert node into tree:
QStringList nameParts = fullName.split('/');
@@ -885,12 +919,12 @@ void BranchModel::updateUpstreamStatus(BranchNode *node)
if (node->tracking.isEmpty())
return;
- QtcProcess *process = new QtcProcess(node);
+ Process *process = new Process(node);
process->setEnvironment(d->client->processEnvironment());
process->setCommand({d->client->vcsBinary(), {"rev-list", "--no-color", "--left-right",
"--count", node->fullRef() + "..." + node->tracking}});
process->setWorkingDirectory(d->workingDirectory);
- connect(process, &QtcProcess::done, this, [this, process, node] {
+ connect(process, &Process::done, this, [this, process, node] {
process->deleteLater();
if (process->result() != ProcessResult::FinishedWithSuccess)
return;
diff --git a/src/plugins/git/branchmodel.h b/src/plugins/git/branchmodel.h
index 580af61298..de126e9a5e 100644
--- a/src/plugins/git/branchmodel.h
+++ b/src/plugins/git/branchmodel.h
@@ -34,7 +34,8 @@ public:
Qt::ItemFlags flags(const QModelIndex &index) const override;
void clear();
- bool refresh(const Utils::FilePath &workingDirectory, QString *errorMessage);
+ enum class ShowError { No, Yes };
+ void refresh(const Utils::FilePath &workingDirectory, ShowError showError = ShowError::No);
void renameBranch(const QString &oldName, const QString &newName);
void renameTag(const QString &oldName, const QString &newName);
diff --git a/src/plugins/git/branchview.cpp b/src/plugins/git/branchview.cpp
index 528ec288f9..c3daf9d5a8 100644
--- a/src/plugins/git/branchview.cpp
+++ b/src/plugins/git/branchview.cpp
@@ -20,6 +20,7 @@
#include <utils/fancylineedit.h>
#include <utils/navigationtreeview.h>
#include <utils/qtcassert.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <vcsbase/vcscommand.h>
@@ -36,6 +37,7 @@
#include <QVBoxLayout>
using namespace Core;
+using namespace Tasking;
using namespace Utils;
using namespace VcsBase;
@@ -113,7 +115,7 @@ BranchView::BranchView()
connect(m_includeOldEntriesAction, &QAction::toggled,
this, &BranchView::setIncludeOldEntries);
m_includeTagsAction->setCheckable(true);
- m_includeTagsAction->setChecked(GitClient::settings().showTags.value());
+ m_includeTagsAction->setChecked(settings().showTags.value());
connect(m_includeTagsAction, &QAction::toggled,
this, &BranchView::setIncludeTags);
@@ -160,9 +162,7 @@ void BranchView::refresh(const FilePath &repository, bool force)
if (!isVisible())
return;
- QString errorMessage;
- if (!m_model->refresh(m_repository, &errorMessage))
- VcsBase::VcsOutputWindow::appendError(errorMessage);
+ m_model->refresh(m_repository, BranchModel::ShowError::Yes);
}
void BranchView::refreshCurrentBranch()
@@ -181,7 +181,7 @@ QList<QToolButton *> BranchView::createToolButtons()
filter->setIcon(Utils::Icons::FILTER.icon());
filter->setToolTip(Tr::tr("Filter"));
filter->setPopupMode(QToolButton::InstantPopup);
- filter->setProperty("noArrow", true);
+ filter->setProperty(StyleHelper::C_NO_ARROW, true);
auto filterMenu = new QMenu(filter);
filterMenu->addAction(m_includeOldEntriesAction);
@@ -190,11 +190,11 @@ QList<QToolButton *> BranchView::createToolButtons()
auto addButton = new QToolButton;
addButton->setDefaultAction(m_addAction);
- addButton->setProperty("noArrow", true);
+ addButton->setProperty(StyleHelper::C_NO_ARROW, true);
auto refreshButton = new QToolButton;
refreshButton->setDefaultAction(m_refreshAction);
- refreshButton->setProperty("noArrow", true);
+ refreshButton->setProperty(StyleHelper::C_NO_ARROW, true);
return {filter, addButton, refreshButton};
}
@@ -225,6 +225,8 @@ void BranchView::slotCustomContextMenu(const QPoint &point)
const bool isTag = m_model->isTag(index);
const bool hasActions = m_model->isLeaf(index);
const bool currentLocal = m_model->isLocal(currentBranch);
+ std::unique_ptr<TaskTree> taskTree;
+ QAction *mergeAction = nullptr;
QMenu contextMenu;
contextMenu.addAction(Tr::tr("&Add..."), this, &BranchView::add);
@@ -268,19 +270,20 @@ void BranchView::slotCustomContextMenu(const QPoint &point)
resetMenu->addAction(Tr::tr("&Mixed"), this, [this] { reset("mixed"); });
resetMenu->addAction(Tr::tr("&Soft"), this, [this] { reset("soft"); });
contextMenu.addMenu(resetMenu);
- QString mergeTitle;
- if (isFastForwardMerge()) {
- contextMenu.addAction(Tr::tr("&Merge \"%1\" into \"%2\" (Fast-Forward)")
- .arg(indexName, currentName),
- this, [this] { merge(true); });
- mergeTitle = Tr::tr("Merge \"%1\" into \"%2\" (No &Fast-Forward)")
- .arg(indexName, currentName);
- } else {
- mergeTitle = Tr::tr("&Merge \"%1\" into \"%2\"")
- .arg(indexName, currentName);
- }
+ mergeAction = contextMenu.addAction(Tr::tr("&Merge \"%1\" into \"%2\"")
+ .arg(indexName, currentName),
+ this,
+ [this] { merge(false); });
+ taskTree.reset(onFastForwardMerge([&] {
+ auto ffMerge = new QAction(
+ Tr::tr("&Merge \"%1\" into \"%2\" (Fast-Forward)").arg(indexName, currentName));
+ connect(ffMerge, &QAction::triggered, this, [this] { merge(true); });
+ contextMenu.insertAction(mergeAction, ffMerge);
+ mergeAction->setText(Tr::tr("Merge \"%1\" into \"%2\" (No &Fast-Forward)")
+ .arg(indexName, currentName));
+ }));
+ connect(mergeAction, &QObject::destroyed, taskTree.get(), &TaskTree::stop);
- contextMenu.addAction(mergeTitle, this, [this] { merge(false); });
contextMenu.addAction(Tr::tr("&Rebase \"%1\" on \"%2\"")
.arg(currentName, indexName),
this, &BranchView::rebase);
@@ -316,7 +319,7 @@ void BranchView::setIncludeOldEntries(bool filter)
void BranchView::setIncludeTags(bool includeTags)
{
- GitClient::settings().showTags.setValue(includeTags);
+ settings().showTags.setValue(includeTags);
refreshCurrentRepository();
}
@@ -523,13 +526,48 @@ bool BranchView::reset(const QByteArray &resetType)
return false;
}
-bool BranchView::isFastForwardMerge()
+TaskTree *BranchView::onFastForwardMerge(const std::function<void()> &callback)
{
const QModelIndex selected = selectedIndex();
QTC_CHECK(selected != m_model->currentBranch());
const QString branch = m_model->fullName(selected, true);
- return GitClient::instance()->isFastForwardMerge(m_repository, branch);
+
+ struct FastForwardStorage
+ {
+ QString mergeBase;
+ QString topRevision;
+ };
+
+ const TreeStorage<FastForwardStorage> storage;
+
+ GitClient *client = GitClient::instance();
+ const auto setupMergeBase = [=](Process &process) {
+ client->setupCommand(process, m_repository, {"merge-base", "HEAD", branch});
+ };
+ const auto onMergeBaseDone = [storage](const Process &process) {
+ storage->mergeBase = process.cleanedStdOut().trimmed();
+ };
+
+ const ProcessTask topRevisionProc = client->topRevision(
+ m_repository,
+ [storage](const QString &revision, const QDateTime &) {
+ storage->topRevision = revision;
+ });
+
+ const Group root {
+ Storage(storage),
+ parallel,
+ ProcessTask(setupMergeBase, onMergeBaseDone),
+ topRevisionProc,
+ onGroupDone([storage, callback] {
+ if (storage->mergeBase == storage->topRevision)
+ callback();
+ })
+ };
+ auto taskTree = new TaskTree(root);
+ taskTree->start();
+ return taskTree;
}
bool BranchView::merge(bool allowFastForward)
diff --git a/src/plugins/git/branchview.h b/src/plugins/git/branchview.h
index 9a1a3c1e45..d272a5e219 100644
--- a/src/plugins/git/branchview.h
+++ b/src/plugins/git/branchview.h
@@ -17,6 +17,8 @@ class QToolButton;
class QTreeView;
QT_END_NAMESPACE;
+namespace Tasking { class TaskTree; }
+
namespace Utils {
class ElidingLabel;
class NavigationTreeView;
@@ -54,7 +56,7 @@ private:
bool remove();
bool rename();
bool reset(const QByteArray &resetType);
- bool isFastForwardMerge();
+ Tasking::TaskTree *onFastForwardMerge(const std::function<void()> &callback);
bool merge(bool allowFastForward);
void rebase();
bool cherryPick();
diff --git a/src/plugins/git/changeselectiondialog.cpp b/src/plugins/git/changeselectiondialog.cpp
index aba7be1ad2..8e4a6bdc99 100644
--- a/src/plugins/git/changeselectiondialog.cpp
+++ b/src/plugins/git/changeselectiondialog.cpp
@@ -12,7 +12,7 @@
#include <utils/completinglineedit.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/theme/theme.h>
#include <QCompleter>
@@ -209,12 +209,12 @@ void ChangeSelectionDialog::recalculateCompletion()
return;
GitClient *client = GitClient::instance();
- QtcProcess *process = new QtcProcess(this);
+ Process *process = new Process(this);
process->setEnvironment(client->processEnvironment());
process->setCommand({client->vcsBinary(), {"for-each-ref", "--format=%(refname:short)"}});
process->setWorkingDirectory(workingDir);
process->setUseCtrlCStub(true);
- connect(process, &QtcProcess::done, this, [this, process] {
+ connect(process, &Process::done, this, [this, process] {
if (process->result() == ProcessResult::FinishedWithSuccess)
m_changeModel->setStringList(process->cleanedStdOut().split('\n'));
process->deleteLater();
@@ -238,8 +238,8 @@ void ChangeSelectionDialog::recalculateDetails()
return;
}
- m_process.reset(new QtcProcess);
- connect(m_process.get(), &QtcProcess::done, this, &ChangeSelectionDialog::setDetails);
+ m_process.reset(new Process);
+ connect(m_process.get(), &Process::done, this, &ChangeSelectionDialog::setDetails);
m_process->setWorkingDirectory(workingDir);
m_process->setEnvironment(m_gitEnvironment);
m_process->setCommand({m_gitExecutable, {"show", "--decorate", "--stat=80", ref}});
diff --git a/src/plugins/git/changeselectiondialog.h b/src/plugins/git/changeselectiondialog.h
index 69da3b20d4..24bc3155dc 100644
--- a/src/plugins/git/changeselectiondialog.h
+++ b/src/plugins/git/changeselectiondialog.h
@@ -18,7 +18,7 @@ QT_END_NAMESPACE
namespace Utils {
class CompletingLineEdit;
class PathChooser;
-class QtcProcess;
+class Process;
} // Utils
namespace Git::Internal {
@@ -53,7 +53,7 @@ private:
void enableButtons(bool b);
- std::unique_ptr<Utils::QtcProcess> m_process;
+ std::unique_ptr<Utils::Process> m_process;
Utils::FilePath m_gitExecutable;
Utils::Environment m_gitEnvironment;
ChangeCommand m_command = NoCommand;
diff --git a/src/plugins/git/gerrit/gerritmodel.cpp b/src/plugins/git/gerrit/gerritmodel.cpp
index 8273fe0f09..6457117b32 100644
--- a/src/plugins/git/gerrit/gerritmodel.cpp
+++ b/src/plugins/git/gerrit/gerritmodel.cpp
@@ -10,8 +10,8 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
-#include <utils/qtcprocess.h>
#include <QApplication>
#include <QDebug>
@@ -227,7 +227,7 @@ private:
void errorTermination(const QString &msg);
- QtcProcess m_process;
+ Process m_process;
QTimer m_timer;
FilePath m_binary;
QByteArray m_output;
@@ -259,15 +259,15 @@ QueryContext::QueryContext(const QString &query,
+ "&o=CURRENT_REVISION&o=DETAILED_LABELS&o=DETAILED_ACCOUNTS";
m_arguments = server.curlArguments() << url;
}
- connect(&m_process, &QtcProcess::readyReadStandardError, this, [this] {
+ connect(&m_process, &Process::readyReadStandardError, this, [this] {
const QString text = QString::fromLocal8Bit(m_process.readAllRawStandardError());
VcsOutputWindow::appendError(text);
m_error.append(text);
});
- connect(&m_process, &QtcProcess::readyReadStandardOutput, this, [this] {
+ connect(&m_process, &Process::readyReadStandardOutput, this, [this] {
m_output.append(m_process.readAllRawStandardOutput());
});
- connect(&m_process, &QtcProcess::done, this, &QueryContext::processDone);
+ connect(&m_process, &Process::done, this, &QueryContext::processDone);
m_process.setEnvironment(Git::Internal::GitClient::instance()->processEnvironment());
m_timer.setInterval(timeOutMS);
@@ -340,7 +340,7 @@ void QueryContext::timeout()
arg(timeOutMS / 1000), QMessageBox::NoButton, parent);
QPushButton *terminateButton = box.addButton(Git::Tr::tr("Terminate"), QMessageBox::YesRole);
box.addButton(Git::Tr::tr("Keep Running"), QMessageBox::NoRole);
- connect(&m_process, &QtcProcess::done, &box, &QDialog::reject);
+ connect(&m_process, &Process::done, &box, &QDialog::reject);
box.exec();
if (m_process.state() != QProcess::Running)
return;
diff --git a/src/plugins/git/gerrit/gerritoptionspage.cpp b/src/plugins/git/gerrit/gerritoptionspage.cpp
index 61aa7047b7..3d77c26aa4 100644
--- a/src/plugins/git/gerrit/gerritoptionspage.cpp
+++ b/src/plugins/git/gerrit/gerritoptionspage.cpp
@@ -7,6 +7,8 @@
#include "../gittr.h"
#include <coreplugin/icore.h>
+
+#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
#include <vcsbase/vcsbaseconstants.h>
@@ -16,108 +18,87 @@
#include <QCheckBox>
#include <QFormLayout>
-namespace Gerrit {
-namespace Internal {
+namespace Gerrit::Internal {
-GerritOptionsPage::GerritOptionsPage(const QSharedPointer<GerritParameters> &p,
- QObject *parent)
- : Core::IOptionsPage(parent)
- , m_parameters(p)
+class GerritOptionsWidget : public Core::IOptionsPageWidget
{
- setId("Gerrit");
- setDisplayName(Git::Tr::tr("Gerrit"));
- setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
-}
+public:
+ GerritOptionsWidget(const QSharedPointer<GerritParameters> &p,
+ const std::function<void()> &onChanged)
+ : m_parameters(p)
+ {
+ auto hostLineEdit = new QLineEdit(p->server.host);
-GerritOptionsPage::~GerritOptionsPage()
-{
- delete m_widget;
-}
+ auto userLineEdit = new QLineEdit(p->server.user.userName);
-QWidget *GerritOptionsPage::widget()
-{
- if (!m_widget) {
- m_widget = new GerritOptionsWidget;
- m_widget->setParameters(*m_parameters);
- }
- return m_widget;
-}
+ auto sshChooser = new Utils::PathChooser;
+ sshChooser->setFilePath(p->ssh);
+ sshChooser->setExpectedKind(Utils::PathChooser::ExistingCommand);
+ sshChooser->setCommandVersionArguments({"-V"});
+ sshChooser->setHistoryCompleter("Git.SshCommand.History");
-void GerritOptionsPage::apply()
-{
- if (GerritOptionsWidget *w = m_widget.data()) {
- GerritParameters newParameters = w->parameters();
- if (newParameters != *m_parameters) {
- if (m_parameters->ssh == newParameters.ssh)
- newParameters.portFlag = m_parameters->portFlag;
- else
- newParameters.setPortFlagBySshType();
- *m_parameters = newParameters;
- m_parameters->toSettings(Core::ICore::settings());
- emit settingsChanged();
- }
- }
-}
+ auto curlChooser = new Utils::PathChooser;
+ curlChooser->setFilePath(p->curl);
+ curlChooser->setExpectedKind(Utils::PathChooser::ExistingCommand);
+ curlChooser->setCommandVersionArguments({"-V"});
-void GerritOptionsPage::finish()
-{
- delete m_widget;
-}
+ auto portSpinBox = new QSpinBox(this);
+ portSpinBox->setRange(1, 65535);
+ portSpinBox->setValue(p->server.port);
-GerritOptionsWidget::GerritOptionsWidget(QWidget *parent)
- : QWidget(parent)
- , m_hostLineEdit(new QLineEdit(this))
- , m_userLineEdit(new QLineEdit(this))
- , m_sshChooser(new Utils::PathChooser)
- , m_curlChooser(new Utils::PathChooser)
- , m_portSpinBox(new QSpinBox(this))
- , m_httpsCheckBox(new QCheckBox(Git::Tr::tr("HTTPS")))
-{
- auto formLayout = new QFormLayout(this);
- formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
- formLayout->addRow(Git::Tr::tr("&Host:"), m_hostLineEdit);
- formLayout->addRow(Git::Tr::tr("&User:"), m_userLineEdit);
- m_sshChooser->setExpectedKind(Utils::PathChooser::ExistingCommand);
- m_sshChooser->setCommandVersionArguments({"-V"});
- m_sshChooser->setHistoryCompleter("Git.SshCommand.History");
- formLayout->addRow(Git::Tr::tr("&ssh:"), m_sshChooser);
- m_curlChooser->setExpectedKind(Utils::PathChooser::ExistingCommand);
- m_curlChooser->setCommandVersionArguments({"-V"});
- formLayout->addRow(Git::Tr::tr("cur&l:"), m_curlChooser);
- m_portSpinBox->setMinimum(1);
- m_portSpinBox->setMaximum(65535);
- formLayout->addRow(Git::Tr::tr("SSH &Port:"), m_portSpinBox);
- formLayout->addRow(Git::Tr::tr("P&rotocol:"), m_httpsCheckBox);
- m_httpsCheckBox->setToolTip(Git::Tr::tr(
- "Determines the protocol used to form a URL in case\n"
- "\"canonicalWebUrl\" is not configured in the file\n"
- "\"gerrit.config\"."));
- setTabOrder(m_sshChooser, m_curlChooser);
- setTabOrder(m_curlChooser, m_portSpinBox);
-}
+ auto httpsCheckBox = new QCheckBox(Git::Tr::tr("HTTPS"));
+ httpsCheckBox->setChecked(p->https);
+ httpsCheckBox->setToolTip(Git::Tr::tr(
+ "Determines the protocol used to form a URL in case\n"
+ "\"canonicalWebUrl\" is not configured in the file\n"
+ "\"gerrit.config\"."));
-GerritParameters GerritOptionsWidget::parameters() const
-{
- GerritParameters result;
- result.server = GerritServer(m_hostLineEdit->text().trimmed(),
- static_cast<unsigned short>(m_portSpinBox->value()),
- m_userLineEdit->text().trimmed(),
- GerritServer::Ssh);
- result.ssh = m_sshChooser->filePath();
- result.curl = m_curlChooser->filePath();
- result.https = m_httpsCheckBox->isChecked();
- return result;
-}
+ using namespace Layouting;
+ Form {
+ Git::Tr::tr("&Host:"), hostLineEdit, br,
+ Git::Tr::tr("&User:"), userLineEdit, br,
+ Git::Tr::tr("&ssh:"), sshChooser, br,
+ Git::Tr::tr("cur&l:"), curlChooser, br,
+ Git::Tr::tr("SSH &Port:"), portSpinBox, br,
+ Git::Tr::tr("P&rotocol:"), httpsCheckBox
+ }.attachTo(this);
+
+ setOnApply([this, hostLineEdit, userLineEdit, sshChooser,
+ curlChooser, portSpinBox, httpsCheckBox, onChanged] {
+ GerritParameters newParameters;
+ newParameters.server = GerritServer(hostLineEdit->text().trimmed(),
+ static_cast<unsigned short>(portSpinBox->value()),
+ userLineEdit->text().trimmed(),
+ GerritServer::Ssh);
+ newParameters.ssh = sshChooser->filePath();
+ newParameters.curl = curlChooser->filePath();
+ newParameters.https = httpsCheckBox->isChecked();
+
+ if (newParameters != *m_parameters) {
+ if (m_parameters->ssh == newParameters.ssh)
+ newParameters.portFlag = m_parameters->portFlag;
+ else
+ newParameters.setPortFlagBySshType();
+ *m_parameters = newParameters;
+ m_parameters->toSettings(Core::ICore::settings());
+ emit onChanged();
+ }
+ });
+ }
+
+private:
+ const QSharedPointer<GerritParameters> &m_parameters;
+};
-void GerritOptionsWidget::setParameters(const GerritParameters &p)
+// GerritOptionsPage
+
+GerritOptionsPage::GerritOptionsPage(const QSharedPointer<GerritParameters> &p,
+ const std::function<void()> &onChanged)
{
- m_hostLineEdit->setText(p.server.host);
- m_userLineEdit->setText(p.server.user.userName);
- m_sshChooser->setFilePath(p.ssh);
- m_curlChooser->setFilePath(p.curl);
- m_portSpinBox->setValue(p.server.port);
- m_httpsCheckBox->setChecked(p.https);
+ setId("Gerrit");
+ setDisplayName(Git::Tr::tr("Gerrit"));
+ setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
+ setWidgetCreator([p, onChanged] { return new GerritOptionsWidget(p, onChanged); });
}
-} // namespace Internal
-} // namespace Gerrit
+} // Gerrit::Internal
diff --git a/src/plugins/git/gerrit/gerritoptionspage.h b/src/plugins/git/gerrit/gerritoptionspage.h
index d5b220eb33..96caab0609 100644
--- a/src/plugins/git/gerrit/gerritoptionspage.h
+++ b/src/plugins/git/gerrit/gerritoptionspage.h
@@ -6,57 +6,16 @@
#include <coreplugin/dialogs/ioptionspage.h>
#include <QSharedPointer>
-#include <QPointer>
-QT_BEGIN_NAMESPACE
-class QLineEdit;
-class QSpinBox;
-class QCheckBox;
-QT_END_NAMESPACE
-
-namespace Utils { class PathChooser; }
-namespace Gerrit {
-namespace Internal {
+namespace Gerrit::Internal {
class GerritParameters;
-class GerritOptionsWidget : public QWidget
-{
- Q_OBJECT
-public:
- explicit GerritOptionsWidget(QWidget *parent = nullptr);
-
- GerritParameters parameters() const;
- void setParameters(const GerritParameters &);
-
-private:
- QLineEdit *m_hostLineEdit;
- QLineEdit *m_userLineEdit;
- Utils::PathChooser *m_sshChooser;
- Utils::PathChooser *m_curlChooser;
- QSpinBox *m_portSpinBox;
- QCheckBox *m_httpsCheckBox;
-};
-
class GerritOptionsPage : public Core::IOptionsPage
{
- Q_OBJECT
-
public:
- GerritOptionsPage(const QSharedPointer<GerritParameters> &p, QObject *parent = nullptr);
- ~GerritOptionsPage() override;
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-signals:
- void settingsChanged();
-
-private:
- const QSharedPointer<GerritParameters> &m_parameters;
- QPointer<GerritOptionsWidget> m_widget;
+ GerritOptionsPage(const QSharedPointer<GerritParameters> &p,
+ const std::function<void()> &onChanged);
};
-} // namespace Internal
-} // namespace Gerrit
+} // Gerrit::Internal
diff --git a/src/plugins/git/gerrit/gerritplugin.cpp b/src/plugins/git/gerrit/gerritplugin.cpp
index 41fa83731f..87ce5bfdac 100644
--- a/src/plugins/git/gerrit/gerritplugin.cpp
+++ b/src/plugins/git/gerrit/gerritplugin.cpp
@@ -22,8 +22,8 @@
#include <coreplugin/vcsmanager.h>
#include <utils/environment.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
-#include <utils/qtcprocess.h>
#include <vcsbase/vcsoutputwindow.h>
@@ -78,7 +78,7 @@ private:
const FetchMode m_fetchMode;
const Utils::FilePath m_git;
const GerritServer m_server;
- QtcProcess m_process;
+ Process m_process;
};
FetchContext::FetchContext(const QSharedPointer<GerritChange> &change,
@@ -93,11 +93,11 @@ FetchContext::FetchContext(const QSharedPointer<GerritChange> &change,
, m_server(server)
{
m_process.setUseCtrlCStub(true);
- connect(&m_process, &QtcProcess::done, this, &FetchContext::processDone);
- connect(&m_process, &QtcProcess::readyReadStandardError, this, [this] {
+ connect(&m_process, &Process::done, this, &FetchContext::processDone);
+ connect(&m_process, &Process::readyReadStandardError, this, [this] {
VcsBase::VcsOutputWindow::append(QString::fromLocal8Bit(m_process.readAllRawStandardError()));
});
- connect(&m_process, &QtcProcess::readyReadStandardOutput, this, [this] {
+ connect(&m_process, &Process::readyReadStandardOutput, this, [this] {
VcsBase::VcsOutputWindow::append(QString::fromLocal8Bit(m_process.readAllRawStandardOutput()));
});
m_process.setWorkingDirectory(repository);
@@ -152,18 +152,26 @@ void FetchContext::checkout()
GitClient::instance()->checkout(m_repository, "FETCH_HEAD");
}
-GerritPlugin::GerritPlugin(QObject *parent)
- : QObject(parent)
- , m_parameters(new GerritParameters)
+GerritPlugin::GerritPlugin()
+ : m_parameters(new GerritParameters)
, m_server(new GerritServer)
{
+ m_parameters->fromSettings(ICore::settings());
+
+ m_gerritOptionsPage = new GerritOptionsPage(m_parameters,
+ [this] {
+ if (m_dialog)
+ m_dialog->scheduleUpdateRemotes();
+ });
}
-GerritPlugin::~GerritPlugin() = default;
+GerritPlugin::~GerritPlugin()
+{
+ delete m_gerritOptionsPage;
+}
-void GerritPlugin::initialize(ActionContainer *ac)
+void GerritPlugin::addToMenu(ActionContainer *ac)
{
- m_parameters->fromSettings(ICore::settings());
QAction *openViewAction = new QAction(Git::Tr::tr("Gerrit..."), this);
@@ -178,13 +186,6 @@ void GerritPlugin::initialize(ActionContainer *ac)
ActionManager::registerAction(pushAction, Constants::GERRIT_PUSH);
connect(pushAction, &QAction::triggered, this, [this] { push(); });
ac->addAction(m_pushToGerritCommand);
-
- auto options = new GerritOptionsPage(m_parameters, this);
- connect(options, &GerritOptionsPage::settingsChanged,
- this, [this] {
- if (m_dialog)
- m_dialog->scheduleUpdateRemotes();
- });
}
void GerritPlugin::updateActions(const VcsBase::VcsBasePluginState &state)
diff --git a/src/plugins/git/gerrit/gerritplugin.h b/src/plugins/git/gerrit/gerritplugin.h
index 77d96d9f74..b7e0de29d8 100644
--- a/src/plugins/git/gerrit/gerritplugin.h
+++ b/src/plugins/git/gerrit/gerritplugin.h
@@ -3,7 +3,7 @@
#pragma once
-#include <utils/fileutils.h>
+#include <utils/filepath.h>
#include <QObject>
#include <QPointer>
@@ -25,15 +25,17 @@ class GerritChange;
class GerritDialog;
class GerritParameters;
class GerritServer;
+class GerritOptionsPage;
class GerritPlugin : public QObject
{
Q_OBJECT
+
public:
- explicit GerritPlugin(QObject *parent = nullptr);
+ GerritPlugin();
~GerritPlugin() override;
- void initialize(Core::ActionContainer *ac);
+ void addToMenu(Core::ActionContainer *ac);
static Utils::FilePath gitBinDirectory();
static QString branch(const Utils::FilePath &repository);
@@ -59,6 +61,7 @@ private:
Core::Command *m_gerritCommand = nullptr;
Core::Command *m_pushToGerritCommand = nullptr;
QString m_reviewers;
+ GerritOptionsPage *m_gerritOptionsPage = nullptr;
};
} // namespace Internal
diff --git a/src/plugins/git/gerrit/gerritpushdialog.cpp b/src/plugins/git/gerrit/gerritpushdialog.cpp
index 057d4cd644..87961c5945 100644
--- a/src/plugins/git/gerrit/gerritpushdialog.cpp
+++ b/src/plugins/git/gerrit/gerritpushdialog.cpp
@@ -125,7 +125,7 @@ GerritPushDialog::GerritPushDialog(const Utils::FilePath &workingDir, const QStr
"Unchecked - Remove mark.\n"
"Partially checked - Do not change current state."));
m_commitView->setToolTip(::Git::Tr::tr(
- "Pushes the selected commit and all dependent commits."));
+ "Pushes the selected commit and all commits it depends on."));
m_reviewersLineEdit->setToolTip(::Git::Tr::tr("Comma-separated list of reviewers.\n"
"\n"
"Reviewers can be specified by nickname or email address. Spaces not allowed.\n"
@@ -136,7 +136,7 @@ GerritPushDialog::GerritPushDialog(const Utils::FilePath &workingDir, const QStr
connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Grid {
::Git::Tr::tr("Push:"), workingDir.toUserOutput(), m_localBranchComboBox, br,
diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp
index e419449fe0..935b56291f 100644
--- a/src/plugins/git/gitclient.cpp
+++ b/src/plugins/git/gitclient.cpp
@@ -19,7 +19,7 @@
#include <coreplugin/iversioncontrol.h>
#include <coreplugin/vcsmanager.h>
-#include <utils/asynctask.h>
+#include <utils/async.h>
#include <utils/algorithm.h>
#include <utils/checkablemessagebox.h>
#include <utils/commandline.h>
@@ -27,8 +27,8 @@
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <utils/mimeutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/temporaryfile.h>
#include <utils/theme/theme.h>
@@ -60,7 +60,7 @@
const char GIT_DIRECTORY[] = ".git";
const char HEAD[] = "HEAD";
const char CHERRY_PICK_HEAD[] = "CHERRY_PICK_HEAD";
-const char BRANCHES_PREFIX[] = "Branches: ";
+[[maybe_unused]] const char BRANCHES_PREFIX[] = "Branches: ";
const char stashNamePrefix[] = "stash@{";
const char noColorOption[] = "--no-color";
const char colorOption[] = "--color=always";
@@ -76,6 +76,7 @@ const char showFormatC[] =
using namespace Core;
using namespace DiffEditor;
+using namespace Tasking;
using namespace Utils;
using namespace VcsBase;
@@ -92,7 +93,7 @@ static QString branchesDisplay(const QString &prefix, QStringList *branches, boo
if (*first)
*first = false;
else
- output += QString(sizeof(BRANCHES_PREFIX) - 1, ' '); // Align
+ output += QString(sizeof(BRANCHES_PREFIX) - 1 /* the \0 */, ' '); // Align
output += prefix + ": ";
// If there are more than 'limit' branches, list limit/2 (first limit/4 and last limit/4)
if (count > limit) {
@@ -165,18 +166,18 @@ GitDiffEditorController::GitDiffEditorController(IDocument *document,
const TreeStorage<QString> diffInputStorage = inputStorage();
- const auto setupDiff = [=](QtcProcess &process) {
+ const auto setupDiff = [=](Process &process) {
process.setCodec(VcsBaseEditor::getCodec(workingDirectory(), {}));
setupCommand(process, {addConfigurationArguments(diffArgs(leftCommit, rightCommit, extraArgs))});
VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine());
};
- const auto onDiffDone = [diffInputStorage](const QtcProcess &process) {
- *diffInputStorage.activeStorage() = process.cleanedStdOut();
+ const auto onDiffDone = [diffInputStorage](const Process &process) {
+ *diffInputStorage = process.cleanedStdOut();
};
const Group root {
Storage(diffInputStorage),
- Process(setupDiff, onDiffDone),
+ ProcessTask(setupDiff, onDiffDone),
postProcessTask()
};
setReloadRecipe(root);
@@ -231,7 +232,7 @@ FileListDiffController::FileListDiffController(IDocument *document, const QStrin
const TreeStorage<DiffStorage> storage;
const TreeStorage<QString> diffInputStorage = inputStorage();
- const auto setupStaged = [this, stagedFiles](QtcProcess &process) {
+ const auto setupStaged = [this, stagedFiles](Process &process) {
if (stagedFiles.isEmpty())
return TaskAction::StopWithError;
process.setCodec(VcsBaseEditor::getCodec(workingDirectory(), stagedFiles));
@@ -240,11 +241,11 @@ FileListDiffController::FileListDiffController(IDocument *document, const QStrin
VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine());
return TaskAction::Continue;
};
- const auto onStagedDone = [storage](const QtcProcess &process) {
+ const auto onStagedDone = [storage](const Process &process) {
storage->m_stagedOutput = process.cleanedStdOut();
};
- const auto setupUnstaged = [this, unstagedFiles](QtcProcess &process) {
+ const auto setupUnstaged = [this, unstagedFiles](Process &process) {
if (unstagedFiles.isEmpty())
return TaskAction::StopWithError;
process.setCodec(VcsBaseEditor::getCodec(workingDirectory(), unstagedFiles));
@@ -253,12 +254,12 @@ FileListDiffController::FileListDiffController(IDocument *document, const QStrin
VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine());
return TaskAction::Continue;
};
- const auto onUnstagedDone = [storage](const QtcProcess &process) {
+ const auto onUnstagedDone = [storage](const Process &process) {
storage->m_unstagedOutput = process.cleanedStdOut();
};
const auto onStagingDone = [storage, diffInputStorage] {
- *diffInputStorage.activeStorage() = storage->m_stagedOutput + storage->m_unstagedOutput;
+ *diffInputStorage = storage->m_stagedOutput + storage->m_unstagedOutput;
};
const Group root {
@@ -267,9 +268,9 @@ FileListDiffController::FileListDiffController(IDocument *document, const QStrin
Group {
parallel,
continueOnDone,
- Process(setupStaged, onStagedDone),
- Process(setupUnstaged, onUnstagedDone),
- OnGroupDone(onStagingDone)
+ ProcessTask(setupStaged, onStagedDone),
+ ProcessTask(setupUnstaged, onUnstagedDone),
+ onGroupDone(onStagingDone)
},
postProcessTask()
};
@@ -321,13 +322,13 @@ ShowController::ShowController(IDocument *document, const QString &id)
setDescription(desc);
};
- const auto setupDescription = [this, id](QtcProcess &process) {
+ const auto setupDescription = [this, id](Process &process) {
process.setCodec(m_instance->encoding(GitClient::EncodingCommit, workingDirectory()));
setupCommand(process, {"show", "-s", noColorOption, showFormatC, id});
VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine());
setDescription(Tr::tr("Waiting for data..."));
};
- const auto onDescriptionDone = [this, storage, updateDescription](const QtcProcess &process) {
+ const auto onDescriptionDone = [this, storage, updateDescription](const Process &process) {
ReloadStorage *data = storage.activeStorage();
const QString output = process.cleanedStdOut();
data->m_postProcessDescription = output.startsWith("commit ");
@@ -348,12 +349,12 @@ ShowController::ShowController(IDocument *document, const QString &id)
return TaskAction::Continue;
};
- const auto setupBranches = [this, storage](QtcProcess &process) {
+ const auto setupBranches = [this, storage](Process &process) {
storage->m_branches = busyMessage;
setupCommand(process, {"branch", noColorOption, "-a", "--contains", storage->m_commit});
VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine());
};
- const auto onBranchesDone = [storage, updateDescription](const QtcProcess &process) {
+ const auto onBranchesDone = [storage, updateDescription](const Process &process) {
ReloadStorage *data = storage.activeStorage();
data->m_branches.clear();
const QString remotePrefix = "remotes/";
@@ -391,17 +392,17 @@ ShowController::ShowController(IDocument *document, const QString &id)
data->m_branches = data->m_branches.trimmed();
updateDescription(*data);
};
- const auto onBranchesError = [storage, updateDescription](const QtcProcess &) {
+ const auto onBranchesError = [storage, updateDescription](const Process &) {
ReloadStorage *data = storage.activeStorage();
data->m_branches.clear();
updateDescription(*data);
};
- const auto setupPrecedes = [this, storage](QtcProcess &process) {
+ const auto setupPrecedes = [this, storage](Process &process) {
storage->m_precedes = busyMessage;
setupCommand(process, {"describe", "--contains", storage->m_commit});
};
- const auto onPrecedesDone = [storage, updateDescription](const QtcProcess &process) {
+ const auto onPrecedesDone = [storage, updateDescription](const Process &process) {
ReloadStorage *data = storage.activeStorage();
data->m_precedes = process.cleanedStdOut().trimmed();
const int tilde = data->m_precedes.indexOf('~');
@@ -411,7 +412,7 @@ ShowController::ShowController(IDocument *document, const QString &id)
data->m_precedes.chop(2);
updateDescription(*data);
};
- const auto onPrecedesError = [storage, updateDescription](const QtcProcess &) {
+ const auto onPrecedesError = [storage, updateDescription](const Process &) {
ReloadStorage *data = storage.activeStorage();
data->m_precedes.clear();
updateDescription(*data);
@@ -427,10 +428,10 @@ ShowController::ShowController(IDocument *document, const QString &id)
data->m_follows = {busyMessage};
data->m_follows.resize(parents.size());
- const auto setupFollow = [this](QtcProcess &process, const QString &parent) {
+ const auto setupFollow = [this](Process &process, const QString &parent) {
setupCommand(process, {"describe", "--tags", "--abbrev=0", parent});
};
- const auto onFollowDone = [data, updateDescription](const QtcProcess &process, int index) {
+ const auto onFollowDone = [data, updateDescription](const Process &process, int index) {
data->m_follows[index] = process.cleanedStdOut().trimmed();
updateDescription(*data);
};
@@ -440,43 +441,43 @@ ShowController::ShowController(IDocument *document, const QString &id)
};
using namespace std::placeholders;
- QList<TaskItem> tasks {parallel, continueOnDone, OnGroupError(onFollowsError)};
+ QList<TaskItem> tasks {parallel, continueOnDone, onGroupError(onFollowsError)};
for (int i = 0, total = parents.size(); i < total; ++i) {
- tasks.append(Process(std::bind(setupFollow, _1, parents.at(i)),
+ tasks.append(ProcessTask(std::bind(setupFollow, _1, parents.at(i)),
std::bind(onFollowDone, _1, i)));
}
taskTree.setupRoot(tasks);
};
- const auto setupDiff = [this, id](QtcProcess &process) {
+ const auto setupDiff = [this, id](Process &process) {
setupCommand(process, addConfigurationArguments(
{"show", "--format=format:", // omit header, already generated
noColorOption, decorateOption, id}));
VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine());
};
- const auto onDiffDone = [diffInputStorage](const QtcProcess &process) {
- *diffInputStorage.activeStorage() = process.cleanedStdOut();
+ const auto onDiffDone = [diffInputStorage](const Process &process) {
+ *diffInputStorage = process.cleanedStdOut();
};
const Group root {
Storage(storage),
Storage(diffInputStorage),
parallel,
- OnGroupSetup([this] { setStartupFile(VcsBase::source(this->document()).toString()); }),
+ onGroupSetup([this] { setStartupFile(VcsBase::source(this->document()).toString()); }),
Group {
- optional,
- Process(setupDescription, onDescriptionDone),
+ finishAllAndDone,
+ ProcessTask(setupDescription, onDescriptionDone),
Group {
parallel,
- optional,
- OnGroupSetup(desciptionDetailsSetup),
- Process(setupBranches, onBranchesDone, onBranchesError),
- Process(setupPrecedes, onPrecedesDone, onPrecedesError),
- Tree(setupFollows)
+ finishAllAndDone,
+ onGroupSetup(desciptionDetailsSetup),
+ ProcessTask(setupBranches, onBranchesDone, onBranchesError),
+ ProcessTask(setupPrecedes, onPrecedesDone, onPrecedesError),
+ TaskTreeTask(setupFollows)
}
},
Group {
- Process(setupDiff, onDiffDone),
+ ProcessTask(setupDiff, onDiffDone),
postProcessTask()
}
};
@@ -490,16 +491,16 @@ class BaseGitDiffArgumentsWidget : public VcsBaseEditorConfig
Q_OBJECT
public:
- BaseGitDiffArgumentsWidget(GitSettings &settings, QToolBar *toolBar) :
- VcsBaseEditorConfig(toolBar)
+ explicit BaseGitDiffArgumentsWidget(QToolBar *toolBar)
+ : VcsBaseEditorConfig(toolBar)
{
m_patienceButton
= addToggleButton("--patience", Tr::tr("Patience"),
Tr::tr("Use the patience algorithm for calculating the differences."));
- mapSetting(m_patienceButton, &settings.diffPatience);
+ mapSetting(m_patienceButton, &settings().diffPatience);
m_ignoreWSButton = addToggleButton("--ignore-space-change", Tr::tr("Ignore Whitespace"),
Tr::tr("Ignore whitespace only changes."));
- mapSetting(m_ignoreWSButton, &settings.ignoreSpaceChangesInDiff);
+ mapSetting(m_ignoreWSButton, &settings().ignoreSpaceChangesInDiff);
}
protected:
@@ -512,15 +513,15 @@ class GitBlameArgumentsWidget : public VcsBaseEditorConfig
Q_OBJECT
public:
- GitBlameArgumentsWidget(GitSettings &settings, QToolBar *toolBar) :
- VcsBaseEditorConfig(toolBar)
+ explicit GitBlameArgumentsWidget(QToolBar *toolBar)
+ : VcsBaseEditorConfig(toolBar)
{
mapSetting(addToggleButton(QString(), Tr::tr("Omit Date"),
Tr::tr("Hide the date of a change from the output.")),
- &settings.omitAnnotationDate);
+ &settings().omitAnnotationDate);
mapSetting(addToggleButton("-w", Tr::tr("Ignore Whitespace"),
Tr::tr("Ignore whitespace only changes.")),
- &settings.ignoreSpaceChangesInBlame);
+ &settings().ignoreSpaceChangesInBlame);
const QList<ChoiceItem> logChoices = {
ChoiceItem(Tr::tr("No Move Detection"), ""),
@@ -529,7 +530,7 @@ public:
ChoiceItem(Tr::tr("Detect Moves and Copies Between Files"), "-M -C -C")
};
mapSetting(addChoices(Tr::tr("Move detection"), {}, logChoices),
- &settings.blameMoveDetection);
+ &settings().blameMoveDetection);
addReloadButton();
}
@@ -540,13 +541,13 @@ class BaseGitLogArgumentsWidget : public BaseGitDiffArgumentsWidget
Q_OBJECT
public:
- BaseGitLogArgumentsWidget(GitSettings &settings, GitEditorWidget *editor) :
- BaseGitDiffArgumentsWidget(settings, editor->toolBar())
+ BaseGitLogArgumentsWidget(GitEditorWidget *editor)
+ : BaseGitDiffArgumentsWidget(editor->toolBar())
{
QToolBar *toolBar = editor->toolBar();
QAction *diffButton = addToggleButton(patchOption, Tr::tr("Diff"),
Tr::tr("Show difference."));
- mapSetting(diffButton, &settings.logDiff);
+ mapSetting(diffButton, &settings().logDiff);
connect(diffButton, &QAction::toggled, m_patienceButton, &QAction::setVisible);
connect(diffButton, &QAction::toggled, m_ignoreWSButton, &QAction::setVisible);
m_patienceButton->setVisible(diffButton->isChecked());
@@ -581,27 +582,27 @@ class GitLogArgumentsWidget : public BaseGitLogArgumentsWidget
Q_OBJECT
public:
- GitLogArgumentsWidget(GitSettings &settings, bool fileRelated, GitEditorWidget *editor) :
- BaseGitLogArgumentsWidget(settings, editor)
+ GitLogArgumentsWidget(bool fileRelated, GitEditorWidget *editor)
+ : BaseGitLogArgumentsWidget(editor)
{
QAction *firstParentButton =
addToggleButton({"-m", "--first-parent"},
Tr::tr("First Parent"),
Tr::tr("Follow only the first parent on merge commits."));
- mapSetting(firstParentButton, &settings.firstParent);
+ mapSetting(firstParentButton, &settings().firstParent);
QAction *graphButton = addToggleButton(graphArguments(), Tr::tr("Graph"),
Tr::tr("Show textual graph log."));
- mapSetting(graphButton, &settings.graphLog);
+ mapSetting(graphButton, &settings().graphLog);
QAction *colorButton = addToggleButton(QStringList{colorOption},
Tr::tr("Color"), Tr::tr("Use colors in log."));
- mapSetting(colorButton, &settings.colorLog);
+ mapSetting(colorButton, &settings().colorLog);
if (fileRelated) {
QAction *followButton = addToggleButton(
"--follow", Tr::tr("Follow"),
Tr::tr("Show log also for previous names of the file."));
- mapSetting(followButton, &settings.followRenames);
+ mapSetting(followButton, &settings().followRenames);
}
addReloadButton();
@@ -640,14 +641,14 @@ class GitRefLogArgumentsWidget : public BaseGitLogArgumentsWidget
Q_OBJECT
public:
- GitRefLogArgumentsWidget(GitSettings &settings, GitEditorWidget *editor) :
- BaseGitLogArgumentsWidget(settings, editor)
+ explicit GitRefLogArgumentsWidget(GitEditorWidget *editor)
+ : BaseGitLogArgumentsWidget(editor)
{
QAction *showDateButton =
addToggleButton("--date=iso",
Tr::tr("Show Date"),
Tr::tr("Show date instead of sequence."));
- mapSetting(showDateButton, &settings.refLogShowDate);
+ mapSetting(showDateButton, &settings().refLogShowDate);
addReloadButton();
}
@@ -735,8 +736,8 @@ static inline void msgCannotRun(const QStringList &args, const FilePath &working
// ---------------- GitClient
-GitClient::GitClient(GitSettings *settings)
- : VcsBase::VcsBaseClientImpl(settings)
+GitClient::GitClient()
+ : VcsBase::VcsBaseClientImpl(&Internal::settings())
{
m_instance = this;
m_gitQtcEditor = QString::fromLatin1("\"%1\" -client -block -pid %2")
@@ -751,7 +752,7 @@ GitClient *GitClient::instance()
GitSettings &GitClient::settings()
{
- return static_cast<GitSettings &>(m_instance->VcsBaseClientImpl::settings());
+ return Internal::settings();
}
FilePath GitClient::findRepositoryForDirectory(const FilePath &directory) const
@@ -821,14 +822,19 @@ FilePaths GitClient::unmanagedFiles(const FilePaths &filePaths) const
return res;
}
+QTextCodec *GitClient::defaultCommitEncoding() const
+{
+ // Set default commit encoding to 'UTF-8', when it's not set,
+ // to solve displaying error of commit log with non-latin characters.
+ return QTextCodec::codecForName("UTF-8");
+}
+
QTextCodec *GitClient::encoding(GitClient::EncodingType encodingType, const FilePath &source) const
{
auto codec = [this](const FilePath &workingDirectory, const QString &configVar) {
const QString codecName = readConfigValue(workingDirectory, configVar).trimmed();
- // Set default commit encoding to 'UTF-8', when it's not set,
- // to solve displaying error of commit log with non-latin characters.
if (codecName.isEmpty())
- return QTextCodec::codecForName("UTF-8");
+ return defaultCommitEncoding();
return QTextCodec::codecForName(codecName.toUtf8());
};
@@ -1074,7 +1080,7 @@ void GitClient::log(const FilePath &workingDirectory, const QString &fileName,
encoding(EncodingLogOutput), "logTitle", msgArg));
VcsBaseEditorConfig *argWidget = editor->editorConfig();
if (!argWidget) {
- argWidget = new GitLogArgumentsWidget(settings(), !fileName.isEmpty(), editor);
+ argWidget = new GitLogArgumentsWidget(!fileName.isEmpty(), editor);
argWidget->setBaseArguments(args);
connect(argWidget, &VcsBaseEditorConfig::commandExecutionRequested, this,
[=] { this->log(workingDir, fileName, enableAnnotationContextMenu, args); });
@@ -1084,7 +1090,7 @@ void GitClient::log(const FilePath &workingDirectory, const QString &fileName,
editor->setWorkingDirectory(workingDir);
QStringList arguments = {"log", decorateOption};
- int logCount = settings().logCount.value();
+ int logCount = settings().logCount();
if (logCount > 0)
arguments << "-n" << QString::number(logCount);
@@ -1130,7 +1136,7 @@ void GitClient::reflog(const FilePath &workingDirectory, const QString &ref)
"reflogRepository", workingDir.toString()));
VcsBaseEditorConfig *argWidget = editor->editorConfig();
if (!argWidget) {
- argWidget = new GitRefLogArgumentsWidget(settings(), editor);
+ argWidget = new GitRefLogArgumentsWidget(editor);
if (!ref.isEmpty())
argWidget->setBaseArguments({ref});
connect(argWidget, &VcsBaseEditorConfig::commandExecutionRequested, this,
@@ -1141,7 +1147,7 @@ void GitClient::reflog(const FilePath &workingDirectory, const QString &ref)
QStringList arguments = {"reflog", noColorOption, decorateOption};
arguments << argWidget->arguments();
- int logCount = settings().logCount.value();
+ int logCount = settings().logCount();
if (logCount > 0)
arguments << "-n" << QString::number(logCount);
@@ -1242,7 +1248,7 @@ void GitClient::annotate(const Utils::FilePath &workingDir, const QString &file,
encoding(EncodingSource, sourceFile), "blameFileName", id);
VcsBaseEditorConfig *argWidget = editor->editorConfig();
if (!argWidget) {
- argWidget = new GitBlameArgumentsWidget(settings(), editor->toolBar());
+ argWidget = new GitBlameArgumentsWidget(editor->toolBar());
argWidget->setBaseArguments(extraOptions);
connect(argWidget, &VcsBaseEditorConfig::commandExecutionRequested, this, [=] {
const int line = VcsBaseEditor::lineNumberOfCurrentEditor();
@@ -1292,14 +1298,15 @@ QStringList GitClient::setupCheckoutArguments(const FilePath &workingDirectory,
if (localBranches.contains(ref))
return arguments;
- if (Utils::CheckableMessageBox::doNotAskAgainQuestion(
- ICore::dialogParent() /*parent*/,
- Tr::tr("Create Local Branch") /*title*/,
- Tr::tr("Would you like to create a local branch?") /*message*/,
- ICore::settings(), "Git.CreateLocalBranchOnCheckout" /*setting*/,
- QDialogButtonBox::Yes | QDialogButtonBox::No /*buttons*/,
- QDialogButtonBox::No /*default button*/,
- QDialogButtonBox::No /*button to save*/) != QDialogButtonBox::Yes) {
+ if (Utils::CheckableMessageBox::question(
+ ICore::dialogParent() /*parent*/,
+ Tr::tr("Create Local Branch") /*title*/,
+ Tr::tr("Would you like to create a local branch?") /*message*/,
+ QString("Git.CreateLocalBranchOnCheckout"), /* decider */
+ QMessageBox::Yes | QMessageBox::No /*buttons*/,
+ QMessageBox::No /*default button*/,
+ QMessageBox::No /*button to save*/)
+ != QMessageBox::Yes) {
return arguments;
}
@@ -1730,19 +1737,25 @@ bool GitClient::synchronousRevParseCmd(const FilePath &workingDirectory, const Q
}
// Retrieve head revision
-QString GitClient::synchronousTopRevision(const FilePath &workingDirectory, QDateTime *dateTime)
+ProcessTask GitClient::topRevision(const FilePath &workingDirectory,
+ const std::function<void(const QString &, const QDateTime &)> &callback)
{
- const QStringList arguments = {"show", "-s", "--pretty=format:%H:%ct", HEAD};
- const CommandResult result = vcsSynchronousExec(workingDirectory, arguments, RunFlags::NoOutput);
- if (result.result() != ProcessResult::FinishedWithSuccess)
- return QString();
- const QStringList output = result.cleanedStdOut().trimmed().split(':');
- if (dateTime && output.size() > 1) {
- bool ok = false;
- const qint64 timeT = output.at(1).toLongLong(&ok);
- *dateTime = ok ? QDateTime::fromSecsSinceEpoch(timeT) : QDateTime();
- }
- return output.first();
+ const auto setupProcess = [=](Process &process) {
+ setupCommand(process, workingDirectory, {"show", "-s", "--pretty=format:%H:%ct", HEAD});
+ };
+ const auto onProcessDone = [=](const Process &process) {
+ const QStringList output = process.cleanedStdOut().trimmed().split(':');
+ QDateTime dateTime;
+ if (output.size() > 1) {
+ bool ok = false;
+ const qint64 timeT = output.at(1).toLongLong(&ok);
+ if (ok)
+ dateTime = QDateTime::fromSecsSinceEpoch(timeT);
+ }
+ callback(output.first(), dateTime);
+ };
+
+ return ProcessTask(setupProcess, onProcessDone);
}
bool GitClient::isRemoteCommit(const FilePath &workingDirectory, const QString &commit)
@@ -1752,13 +1765,6 @@ bool GitClient::isRemoteCommit(const FilePath &workingDirectory, const QString &
return !result.rawStdOut().isEmpty();
}
-bool GitClient::isFastForwardMerge(const FilePath &workingDirectory, const QString &branch)
-{
- const CommandResult result = vcsSynchronousExec(workingDirectory,
- {"merge-base", HEAD, branch}, RunFlags::NoOutput);
- return result.cleanedStdOut().trimmed() == synchronousTopRevision(workingDirectory);
-}
-
// Format an entry in a one-liner for selection list using git log.
QString GitClient::synchronousShortDescription(const FilePath &workingDirectory, const QString &revision,
const QString &format) const
@@ -2420,9 +2426,9 @@ void GitClient::launchGitK(const FilePath &workingDirectory, const QString &file
void GitClient::launchRepositoryBrowser(const FilePath &workingDirectory) const
{
- const FilePath repBrowserBinary = settings().repositoryBrowserCmd.filePath();
+ const FilePath repBrowserBinary = settings().repositoryBrowserCmd();
if (!repBrowserBinary.isEmpty())
- QtcProcess::startDetached({repBrowserBinary, {workingDirectory.toString()}}, workingDirectory);
+ Process::startDetached({repBrowserBinary, {workingDirectory.toString()}}, workingDirectory);
}
static FilePath gitBinDir(const GitClient::GitKLaunchTrial trial, const FilePath &parentDir)
@@ -2466,21 +2472,21 @@ void GitClient::tryLaunchingGitK(const Environment &env,
arguments << "--" << fileName;
VcsOutputWindow::appendCommand(workingDirectory, {binary, arguments});
- // This should always use QtcProcess::startDetached (as not to kill
+ // This should always use Process::startDetached (as not to kill
// the child), but that does not have an environment parameter.
if (!settings().path.value().isEmpty()) {
- auto process = new QtcProcess(const_cast<GitClient*>(this));
+ auto process = new Process(const_cast<GitClient*>(this));
process->setWorkingDirectory(workingDirectory);
process->setEnvironment(env);
process->setCommand({binary, arguments});
- connect(process, &QtcProcess::done, this, [=] {
+ connect(process, &Process::done, this, [=] {
if (process->result() == ProcessResult::StartFailed)
handleGitKFailedToStart(env, workingDirectory, fileName, trial, gitBinDirectory);
process->deleteLater();
});
process->start();
} else {
- if (!QtcProcess::startDetached({binary, arguments}, workingDirectory))
+ if (!Process::startDetached({binary, arguments}, workingDirectory))
handleGitKFailedToStart(env, workingDirectory, fileName, trial, gitBinDirectory);
}
}
@@ -2517,7 +2523,7 @@ bool GitClient::launchGitGui(const FilePath &workingDirectory) {
if (gitBinary.isEmpty()) {
success = false;
} else {
- success = QtcProcess::startDetached({gitBinary, {"gui"}}, workingDirectory);
+ success = Process::startDetached({gitBinary, {"gui"}}, workingDirectory);
}
if (!success)
@@ -2562,7 +2568,7 @@ bool GitClient::launchGitBash(const FilePath &workingDirectory)
success = false;
} else {
const FilePath gitBash = git.absolutePath().parentDir() / "git-bash.exe";
- success = QtcProcess::startDetached({gitBash, {}}, workingDirectory);
+ success = Process::startDetached({gitBash, {}}, workingDirectory);
}
if (!success)
@@ -2574,7 +2580,7 @@ bool GitClient::launchGitBash(const FilePath &workingDirectory)
FilePath GitClient::vcsBinary() const
{
bool ok;
- Utils::FilePath binary = static_cast<GitSettings &>(settings()).gitExecutable(&ok);
+ Utils::FilePath binary = settings().gitExecutable(&ok);
if (!ok)
return Utils::FilePath();
return binary;
@@ -2617,11 +2623,10 @@ bool GitClient::readDataFromCommit(const FilePath &repoDirectory, const QString
return true;
}
-Author GitClient::getAuthor(const Utils::FilePath &workingDirectory)
+Author GitClient::parseAuthor(const QString &authorInfo)
{
// The format is:
// Joe Developer <joedev@example.com> unixtimestamp +HHMM
- const QString authorInfo = readGitVar(workingDirectory, "GIT_AUTHOR_IDENT");
int lt = authorInfo.lastIndexOf('<');
int gt = authorInfo.lastIndexOf('>');
if (gt == -1 || uint(lt) > uint(gt)) {
@@ -2633,6 +2638,12 @@ Author GitClient::getAuthor(const Utils::FilePath &workingDirectory)
return result;
}
+Author GitClient::getAuthor(const Utils::FilePath &workingDirectory)
+{
+ const QString authorInfo = readGitVar(workingDirectory, "GIT_AUTHOR_IDENT");
+ return parseAuthor(authorInfo);
+}
+
bool GitClient::getCommitData(const FilePath &workingDirectory,
QString *commitTemplate,
CommitData &commitData,
@@ -2862,7 +2873,7 @@ bool GitClient::addAndCommit(const FilePath &repositoryDirectory,
GitPlugin::updateCurrentBranch();
return true;
}
- VcsOutputWindow::appendError(Tr::tr("Cannot commit %n files", nullptr, commitCount) + "\n");
+ VcsOutputWindow::appendError(Tr::tr("Cannot commit %n file(s)", nullptr, commitCount) + "\n");
return false;
}
@@ -3120,7 +3131,7 @@ void GitClient::synchronousSubversionFetch(const FilePath &workingDirectory) con
void GitClient::subversionLog(const FilePath &workingDirectory) const
{
QStringList arguments = {"svn", "log"};
- int logCount = settings().logCount.value();
+ int logCount = settings().logCount();
if (logCount > 0)
arguments << ("--limit=" + QString::number(logCount));
@@ -3424,21 +3435,33 @@ QString GitClient::readGitVar(const FilePath &workingDirectory, const QString &c
return readOneLine(workingDirectory, {"var", configVar});
}
-QString GitClient::readOneLine(const FilePath &workingDirectory, const QStringList &arguments) const
+static QTextCodec *configFileCodec()
{
// Git for Windows always uses UTF-8 for configuration:
// https://github.com/msysgit/msysgit/wiki/Git-for-Windows-Unicode-Support#convert-config-files
static QTextCodec *codec = HostOsInfo::isWindowsHost()
? QTextCodec::codecForName("UTF-8")
: QTextCodec::codecForLocale();
+ return codec;
+}
+QString GitClient::readOneLine(const FilePath &workingDirectory, const QStringList &arguments) const
+{
const CommandResult result = vcsSynchronousExec(workingDirectory, arguments,
- RunFlags::NoOutput, vcsTimeoutS(), codec);
+ RunFlags::NoOutput, vcsTimeoutS(),
+ configFileCodec());
if (result.result() == ProcessResult::FinishedWithSuccess)
return result.cleanedStdOut().trimmed();
return {};
}
+void GitClient::readConfigAsync(const FilePath &workingDirectory, const QStringList &arguments,
+ const CommandHandler &handler) const
+{
+ vcsExecWithHandler(workingDirectory, arguments, this, handler, RunFlags::NoOutput,
+ configFileCodec());
+}
+
static unsigned parseGitVersion(const QString &output)
{
// cut 'git version 1.6.5.1.sha'
@@ -3464,8 +3487,8 @@ QFuture<unsigned> GitClient::gitVersion() const
const FilePath newGitBinary = vcsBinary();
const bool needToRunGit = m_gitVersionForBinary != newGitBinary && !newGitBinary.isEmpty();
if (needToRunGit) {
- auto proc = new QtcProcess(const_cast<GitClient *>(this));
- connect(proc, &QtcProcess::done, this, [this, proc, fi, newGitBinary]() mutable {
+ auto proc = new Process(const_cast<GitClient *>(this));
+ connect(proc, &Process::done, this, [this, proc, fi, newGitBinary]() mutable {
if (proc->result() == ProcessResult::FinishedWithSuccess) {
m_cachedGitVersion = parseGitVersion(proc->cleanedStdOut());
m_gitVersionForBinary = newGitBinary;
diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h
index 2dfd6ddaaa..398b1c1cc8 100644
--- a/src/plugins/git/gitclient.h
+++ b/src/plugins/git/gitclient.h
@@ -13,6 +13,7 @@
#include <utils/fileutils.h>
#include <utils/futuresynchronizer.h>
+#include <utils/process.h>
#include <QObject>
#include <QString>
@@ -78,6 +79,12 @@ public:
};
struct Author {
+ bool operator==(const Author &other) const {
+ return name == other.name && email == other.email;
+ }
+ bool operator!=(const Author &other) const {
+ return !operator==(other);
+ }
QString name;
QString email;
};
@@ -114,9 +121,8 @@ public:
PushAction m_pushAction = NoPush;
};
- explicit GitClient(GitSettings *settings);
+ GitClient();
static GitClient *instance();
- static GitSettings &settings();
Utils::FilePath vcsBinary() const override;
QFuture<unsigned> gitVersion() const;
@@ -241,9 +247,9 @@ public:
QString synchronousTopic(const Utils::FilePath &workingDirectory) const;
bool synchronousRevParseCmd(const Utils::FilePath &workingDirectory, const QString &ref,
QString *output, QString *errorMessage = nullptr) const;
- QString synchronousTopRevision(const Utils::FilePath &workingDirectory, QDateTime *dateTime = nullptr);
+ Tasking::ProcessTask topRevision(const Utils::FilePath &workingDirectory,
+ const std::function<void(const QString &, const QDateTime &)> &callback);
bool isRemoteCommit(const Utils::FilePath &workingDirectory, const QString &commit);
- bool isFastForwardMerge(const Utils::FilePath &workingDirectory, const QString &branch);
void fetch(const Utils::FilePath &workingDirectory, const QString &remote);
void pull(const Utils::FilePath &workingDirectory, bool rebase);
@@ -343,12 +349,19 @@ public:
Core::IEditor *openShowEditor(const Utils::FilePath &workingDirectory, const QString &ref,
const Utils::FilePath &path, ShowEditor showSetting = ShowEditor::Always);
+ Author parseAuthor(const QString &authorInfo);
Author getAuthor(const Utils::FilePath &workingDirectory);
+ QTextCodec *defaultCommitEncoding() const;
enum EncodingType { EncodingSource, EncodingLogOutput, EncodingCommit, EncodingDefault };
QTextCodec *encoding(EncodingType encodingType, const Utils::FilePath &source = {}) const;
+ void readConfigAsync(const Utils::FilePath &workingDirectory, const QStringList &arguments,
+ const VcsBase::CommandHandler &handler) const;
+
private:
+ static GitSettings &settings();
+
void finishSubmoduleUpdate();
void chunkActionsRequested(DiffEditor::DiffEditorController *controller,
QMenu *menu, int fileIndex, int chunkIndex,
@@ -400,6 +413,7 @@ private:
QString m_diffCommit;
Utils::FilePaths m_updatedSubmodules;
bool m_disableEditor = false;
+ // The synchronizer has cancelOnWait set to true by default.
Utils::FutureSynchronizer m_synchronizer; // for commit updates
};
diff --git a/src/plugins/git/giteditor.cpp b/src/plugins/git/giteditor.cpp
index 7f73842cb0..c7078dc2ba 100644
--- a/src/plugins/git/giteditor.cpp
+++ b/src/plugins/git/giteditor.cpp
@@ -132,7 +132,7 @@ static QString sanitizeBlameOutput(const QString &b)
if (b.isEmpty())
return b;
- const bool omitDate = GitClient::instance()->settings().omitAnnotationDate.value();
+ const bool omitDate = settings().omitAnnotationDate.value();
const QChar space(' ');
const int parenPos = b.indexOf(')');
if (parenPos == -1)
diff --git a/src/plugins/git/gitgrep.cpp b/src/plugins/git/gitgrep.cpp
index 6baa635080..f6324f152d 100644
--- a/src/plugins/git/gitgrep.cpp
+++ b/src/plugins/git/gitgrep.cpp
@@ -13,21 +13,20 @@
#include <vcsbase/vcsbaseconstants.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/environment.h>
#include <utils/fancylineedit.h>
#include <utils/filesearch.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <QCheckBox>
-#include <QFuture>
#include <QHBoxLayout>
#include <QRegularExpressionValidator>
#include <QSettings>
-#include <QTextStream>
using namespace Core;
+using namespace TextEditor;
using namespace Utils;
using namespace VcsBase;
@@ -43,92 +42,106 @@ public:
QString id() const { return recurseSubmodules ? ref + ".Rec" : ref; }
};
-class GitGrepRunner
+static QStringView nextLine(QStringView *remainingInput)
{
- using FutureInterfaceType = QFutureInterface<FileSearchResultList>;
+ const int newLinePos = remainingInput->indexOf('\n');
+ if (newLinePos < 0) {
+ QStringView ret = *remainingInput;
+ *remainingInput = QStringView();
+ return ret;
+ }
+ QStringView ret = remainingInput->left(newLinePos);
+ *remainingInput = remainingInput->mid(newLinePos + 1);
+ return ret;
+}
-public:
- GitGrepRunner(const TextEditor::FileFindParameters &parameters)
- : m_parameters(parameters)
- {
- m_directory = FilePath::fromString(parameters.additionalParameters.toString());
- m_vcsBinary = GitClient::instance()->vcsBinary();
- m_environment = GitClient::instance()->processEnvironment();
+struct Match
+{
+ Match() = default;
+ Match(int start, int length) :
+ matchStart(start), matchLength(length) {}
+
+ int matchStart = 0;
+ int matchLength = 0;
+ QStringList regexpCapturedTexts;
+};
+
+static void processLine(QStringView line, SearchResultItems *resultList,
+ const std::optional<QRegularExpression> &regExp, const QString &ref,
+ const FilePath &directory)
+{
+ if (line.isEmpty())
+ return;
+ static const QLatin1String boldRed("\x1b[1;31m");
+ static const QLatin1String resetColor("\x1b[m");
+ SearchResultItem result;
+ const int lineSeparator = line.indexOf(QChar::Null);
+ QStringView filePath = line.left(lineSeparator);
+ if (!ref.isEmpty() && filePath.startsWith(ref))
+ filePath = filePath.mid(ref.length());
+ result.setFilePath(directory.pathAppended(filePath.toString()));
+ const int textSeparator = line.indexOf(QChar::Null, lineSeparator + 1);
+ const int lineNumber = line.mid(lineSeparator + 1, textSeparator - lineSeparator - 1).toInt();
+ QString text = line.mid(textSeparator + 1).toString();
+ QList<Match> matches;
+ while (true) {
+ const int matchStart = text.indexOf(boldRed);
+ if (matchStart == -1)
+ break;
+ const int matchTextStart = matchStart + boldRed.size();
+ const int matchEnd = text.indexOf(resetColor, matchTextStart);
+ QTC_ASSERT(matchEnd != -1, break);
+ const int matchLength = matchEnd - matchTextStart;
+ Match match(matchStart, matchLength);
+ const QString matchText = text.mid(matchTextStart, matchLength);
+ if (regExp)
+ match.regexpCapturedTexts = regExp->match(matchText).capturedTexts();
+ matches.append(match);
+ text = text.left(matchStart) + matchText + text.mid(matchEnd + resetColor.size());
}
+ result.setDisplayText(text);
- struct Match
- {
- Match() = default;
- Match(int start, int length) :
- matchStart(start), matchLength(length) {}
+ for (const auto &match : std::as_const(matches)) {
+ result.setMainRange(lineNumber, match.matchStart, match.matchLength);
+ result.setUserData(match.regexpCapturedTexts);
+ result.setUseTextEditorFont(true);
+ resultList->append(result);
+ }
+}
- int matchStart = 0;
- int matchLength = 0;
- QStringList regexpCapturedTexts;
- };
+static SearchResultItems parse(const QFuture<void> &future, const QString &input,
+ const std::optional<QRegularExpression> &regExp, const QString &ref,
+ const FilePath &directory)
+{
+ SearchResultItems items;
+ QStringView remainingInput(input);
+ while (true) {
+ if (future.isCanceled())
+ return {};
- void processLine(const QString &line, FileSearchResultList *resultList) const
- {
+ if (remainingInput.isEmpty())
+ break;
+
+ const QStringView line = nextLine(&remainingInput);
if (line.isEmpty())
- return;
- static const QLatin1String boldRed("\x1b[1;31m");
- static const QLatin1String resetColor("\x1b[m");
- FileSearchResult single;
- const int lineSeparator = line.indexOf(QChar::Null);
- QString filePath = line.left(lineSeparator);
- if (!m_ref.isEmpty() && filePath.startsWith(m_ref))
- filePath.remove(0, m_ref.length());
- single.fileName = m_directory.pathAppended(filePath);
- const int textSeparator = line.indexOf(QChar::Null, lineSeparator + 1);
- single.lineNumber = line.mid(lineSeparator + 1, textSeparator - lineSeparator - 1).toInt();
- QString text = line.mid(textSeparator + 1);
- QRegularExpression regexp;
- QVector<Match> matches;
- if (m_parameters.flags & FindRegularExpression) {
- const QRegularExpression::PatternOptions patternOptions =
- (m_parameters.flags & FindCaseSensitively)
- ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption;
- regexp.setPattern(m_parameters.text);
- regexp.setPatternOptions(patternOptions);
- }
- for (;;) {
- const int matchStart = text.indexOf(boldRed);
- if (matchStart == -1)
- break;
- const int matchTextStart = matchStart + boldRed.size();
- const int matchEnd = text.indexOf(resetColor, matchTextStart);
- QTC_ASSERT(matchEnd != -1, break);
- const int matchLength = matchEnd - matchTextStart;
- Match match(matchStart, matchLength);
- const QString matchText = text.mid(matchTextStart, matchLength);
- if (m_parameters.flags & FindRegularExpression)
- match.regexpCapturedTexts = regexp.match(matchText).capturedTexts();
- matches.append(match);
- text = text.left(matchStart) + matchText + text.mid(matchEnd + resetColor.size());
- }
- single.matchingLine = text;
+ continue;
- for (const auto &match : std::as_const(matches)) {
- single.matchStart = match.matchStart;
- single.matchLength = match.matchLength;
- single.regexpCapturedTexts = match.regexpCapturedTexts;
- resultList->append(single);
- }
+ processLine(line, &items, regExp, ref, directory);
}
+ return items;
+}
- void read(FutureInterfaceType &fi, const QString &text)
- {
- FileSearchResultList resultList;
- QString t = text;
- QTextStream stream(&t);
- while (!stream.atEnd() && !fi.isCanceled())
- processLine(stream.readLine(), &resultList);
- if (!resultList.isEmpty() && !fi.isCanceled())
- fi.reportResult(resultList);
- }
+static void runGitGrep(QPromise<SearchResultItems> &promise, const FileFindParameters &parameters)
+{
+ const FilePath directory = FilePath::fromString(parameters.additionalParameters.toString());
+ const GitGrepParameters gitParameters
+ = parameters.searchEngineParameters.value<GitGrepParameters>();
+ const QString ref = gitParameters.ref.isEmpty() ? QString() : gitParameters.ref + ':';
+
+ const auto setupProcess = [&](Process &process) {
+ const FilePath vcsBinary = GitClient::instance()->vcsBinary();
+ const Environment environment = GitClient::instance()->processEnvironment();
- void operator()(FutureInterfaceType &fi)
- {
QStringList arguments = {
"-c", "color.grep.match=bold red",
"-c", "color.grep=always",
@@ -136,60 +149,41 @@ public:
"-c", "color.grep.lineNumber=",
"grep", "-zn", "--no-full-name"
};
- if (!(m_parameters.flags & FindCaseSensitively))
+ if (!(parameters.flags & FindCaseSensitively))
arguments << "-i";
- if (m_parameters.flags & FindWholeWords)
+ if (parameters.flags & FindWholeWords)
arguments << "-w";
- if (m_parameters.flags & FindRegularExpression)
+ if (parameters.flags & FindRegularExpression)
arguments << "-P";
else
arguments << "-F";
- arguments << "-e" << m_parameters.text;
- GitGrepParameters params = m_parameters.searchEngineParameters.value<GitGrepParameters>();
- if (params.recurseSubmodules)
+ arguments << "-e" << parameters.text;
+ if (gitParameters.recurseSubmodules)
arguments << "--recurse-submodules";
- if (!params.ref.isEmpty()) {
- arguments << params.ref;
- m_ref = params.ref + ':';
+ if (!gitParameters.ref.isEmpty()) {
+ arguments << gitParameters.ref;
}
const QStringList filterArgs =
- m_parameters.nameFilters.isEmpty() ? QStringList("*") // needed for exclusion filters
- : m_parameters.nameFilters;
+ parameters.nameFilters.isEmpty() ? QStringList("*") // needed for exclusion filters
+ : parameters.nameFilters;
const QStringList exclusionArgs =
- Utils::transform(m_parameters.exclusionFilters, [](const QString &filter) {
- return QString(":!" + filter);
- });
+ Utils::transform(parameters.exclusionFilters, [](const QString &filter) {
+ return QString(":!" + filter);
+ });
arguments << "--" << filterArgs << exclusionArgs;
- QtcProcess process;
- process.setEnvironment(m_environment);
- process.setCommand({m_vcsBinary, arguments});
- process.setWorkingDirectory(m_directory);
- process.setStdOutCallback([this, &fi](const QString &text) { read(fi, text); });
- process.start();
- process.waitForFinished();
-
- switch (process.result()) {
- case ProcessResult::TerminatedAbnormally:
- case ProcessResult::StartFailed:
- case ProcessResult::Hang:
- fi.reportCanceled();
- break;
- case ProcessResult::FinishedWithSuccess:
- case ProcessResult::FinishedWithError:
- // When no results are found, git-grep exits with non-zero status.
- // Do not consider this as an error.
- break;
- }
- }
+ process.setEnvironment(environment);
+ process.setCommand({vcsBinary, arguments});
+ process.setWorkingDirectory(directory);
+ };
-private:
- FilePath m_vcsBinary;
- FilePath m_directory;
- QString m_ref;
- TextEditor::FileFindParameters m_parameters;
- Environment m_environment;
-};
+ const auto outputParser = [&ref, &directory](const QFuture<void> &future, const QString &input,
+ const std::optional<QRegularExpression> &regExp) {
+ return parse(future, input, regExp, ref, directory);
+ };
+
+ TextEditor::searchInProcessOutput(promise, parameters, setupProcess, outputParser);
+}
static bool isGitDirectory(const FilePath &path)
{
@@ -212,18 +206,16 @@ GitGrep::GitGrep(GitClient *client)
m_treeLineEdit->setValidator(new QRegularExpressionValidator(refExpression, this));
layout->addWidget(m_treeLineEdit);
// asynchronously check git version, add "recurse submodules" option if available
- Utils::onResultReady(client->gitVersion(),
- this,
+ Utils::onResultReady(client->gitVersion(), this,
[this, pLayout = QPointer<QHBoxLayout>(layout)](unsigned version) {
- if (version >= 0x021300 && pLayout) {
- m_recurseSubmodules = new QCheckBox(Tr::tr("Recurse submodules"));
- pLayout->addWidget(m_recurseSubmodules);
- }
- });
- TextEditor::FindInFiles *findInFiles = TextEditor::FindInFiles::instance();
+ if (version >= 0x021300 && pLayout) {
+ m_recurseSubmodules = new QCheckBox(Tr::tr("Recurse submodules"));
+ pLayout->addWidget(m_recurseSubmodules);
+ }
+ });
+ FindInFiles *findInFiles = FindInFiles::instance();
QTC_ASSERT(findInFiles, return);
- connect(findInFiles, &TextEditor::FindInFiles::pathChanged,
- m_widget, [this](const FilePath &path) {
+ connect(findInFiles, &FindInFiles::pathChanged, m_widget, [this](const FilePath &path) {
setEnabled(isGitDirectory(path));
});
connect(this, &SearchEngine::enabledChanged, m_widget, &QWidget::setEnabled);
@@ -272,14 +264,14 @@ void GitGrep::writeSettings(QSettings *settings) const
settings->setValue(GitGrepRef, m_treeLineEdit->text());
}
-QFuture<FileSearchResultList> GitGrep::executeSearch(const TextEditor::FileFindParameters &parameters,
- TextEditor::BaseFileFind * /*baseFileFind*/)
+QFuture<SearchResultItems> GitGrep::executeSearch(const FileFindParameters &parameters,
+ BaseFileFind *)
{
- return Utils::runAsync(GitGrepRunner(parameters));
+ return Utils::asyncRun(runGitGrep, parameters);
}
IEditor *GitGrep::openEditor(const SearchResultItem &item,
- const TextEditor::FileFindParameters &parameters)
+ const FileFindParameters &parameters)
{
const GitGrepParameters params = parameters.searchEngineParameters.value<GitGrepParameters>();
const QStringList &itemPath = item.path();
diff --git a/src/plugins/git/gitgrep.h b/src/plugins/git/gitgrep.h
index 119751102c..fda6fe56eb 100644
--- a/src/plugins/git/gitgrep.h
+++ b/src/plugins/git/gitgrep.h
@@ -5,7 +5,9 @@
#include <texteditor/basefilefind.h>
-QT_FORWARD_DECLARE_CLASS(QCheckBox);
+QT_BEGIN_NAMESPACE
+class QCheckBox;
+QT_END_NAMESPACE
namespace Utils { class FancyLineEdit; }
@@ -25,10 +27,10 @@ public:
QVariant parameters() const override;
void readSettings(QSettings *settings) override;
void writeSettings(QSettings *settings) const override;
- QFuture<Utils::FileSearchResultList> executeSearch(
+ QFuture<Utils::SearchResultItems> executeSearch(
const TextEditor::FileFindParameters &parameters,
TextEditor::BaseFileFind *baseFileFind) override;
- Core::IEditor *openEditor(const Core::SearchResultItem &item,
+ Core::IEditor *openEditor(const Utils::SearchResultItem &item,
const TextEditor::FileFindParameters &parameters) override;
private:
diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp
index 9729dbe424..c5f30fa036 100644
--- a/src/plugins/git/gitplugin.cpp
+++ b/src/plugins/git/gitplugin.cpp
@@ -42,12 +42,12 @@
#include <texteditor/textmark.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/commandline.h>
#include <utils/infobar.h>
#include <utils/parameteraction.h>
#include <utils/pathchooser.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <utils/stringutils.h>
#include <utils/utilsicons.h>
@@ -392,11 +392,14 @@ public:
void setupInstantBlame();
void instantBlameOnce();
+ void forceInstantBlame();
void instantBlame();
void stopInstantBlame();
+ bool refreshWorkingDirectory(const FilePath &workingDirectory);
void onApplySettings();
+ GitSettings setting;
CommandLocator *m_commandLocator = nullptr;
QAction *m_menuAction = nullptr;
@@ -418,24 +421,23 @@ public:
QVector<ParameterAction *> m_projectActions;
QVector<QAction *> m_repositoryActions;
ParameterAction *m_applyCurrentFilePatchAction = nullptr;
- Gerrit::Internal::GerritPlugin *m_gerritPlugin = nullptr;
+ Gerrit::Internal::GerritPlugin m_gerritPlugin;
- GitSettings m_settings;
- GitClient m_gitClient{&m_settings};
+ GitClient m_gitClient;
QPointer<StashDialog> m_stashDialog;
BranchViewFactory m_branchViewFactory;
QPointer<RemoteDialog> m_remoteDialog;
FilePath m_submitRepository;
QString m_commitMessageFileName;
+ FilePath m_workingDirectory;
+ QTextCodec *m_codec = nullptr;
Author m_author;
int m_lastVisitedEditorLine = -1;
QTimer *m_cursorPositionChangedTimer = nullptr;
std::unique_ptr<BlameMark> m_blameMark;
QMetaObject::Connection m_blameCursorPosConn;
- GitSettingsPage settingPage{&m_settings};
-
GitGrep gitGrep{&m_gitClient};
VcsEditorFactory svnLogEditorFactory {
@@ -524,7 +526,7 @@ void GitPluginPrivate::onApplySettings()
updateRepositoryBrowserAction();
bool gitFoundOk;
QString errorMessage;
- m_settings.gitExecutable(&gitFoundOk, &errorMessage);
+ settings().gitExecutable(&gitFoundOk, &errorMessage);
if (!gitFoundOk) {
QTimer::singleShot(0, this, [errorMessage] {
AsynchronousMessageBox::warning(Tr::tr("Git Settings"), errorMessage);
@@ -555,11 +557,6 @@ IVersionControl *GitPlugin::versionControl()
return dd;
}
-const GitSettings &GitPlugin::settings()
-{
- return dd->m_settings;
-}
-
const VcsBasePluginState &GitPlugin::currentState()
{
return dd->currentState();
@@ -709,6 +706,7 @@ GitPluginPrivate::GitPluginPrivate()
m_fileActions.reserve(10);
m_projectActions.reserve(10);
m_repositoryActions.reserve(50);
+ m_codec = GitClient::instance()->defaultCommitEncoding();
Context context(Constants::GIT_CONTEXT);
@@ -776,13 +774,11 @@ GitPluginPrivate::GitPluginPrivate()
createProjectAction(currentProjectMenu, Tr::tr("Diff Current Project", "Avoid translating \"Diff\""),
Tr::tr("Diff Project \"%1\"", "Avoid translating \"Diff\""),
- "Git.DiffProject", context, true, &GitPluginPrivate::diffCurrentProject,
- QKeySequence(useMacShortcuts ? Tr::tr("Meta+G,Meta+Shift+D") : Tr::tr("Alt+G,Alt+Shift+D")));
+ "Git.DiffProject", context, true, &GitPluginPrivate::diffCurrentProject);
createProjectAction(currentProjectMenu, Tr::tr("Log Project", "Avoid translating \"Log\""),
Tr::tr("Log Project \"%1\"", "Avoid translating \"Log\""),
- "Git.LogProject", context, true, &GitPluginPrivate::logProject,
- QKeySequence(useMacShortcuts ? Tr::tr("Meta+G,Meta+K") : Tr::tr("Alt+G,Alt+K")));
+ "Git.LogProject", context, true, &GitPluginPrivate::logProject);
createProjectAction(currentProjectMenu, Tr::tr("Clean Project...", "Avoid translating \"Clean\""),
Tr::tr("Clean Project \"%1\"...", "Avoid translating \"Clean\""),
@@ -795,10 +791,12 @@ GitPluginPrivate::GitPluginPrivate()
gitContainer->addMenu(localRepositoryMenu);
createRepositoryAction(localRepositoryMenu, "Diff", "Git.DiffRepository",
- context, true, &GitClient::diffRepository);
+ context, true, &GitClient::diffRepository,
+ QKeySequence(useMacShortcuts ? Tr::tr("Meta+G,Meta+Shift+D") : Tr::tr("Alt+G,Alt+Shift+D")));
createRepositoryAction(localRepositoryMenu, "Log", "Git.LogRepository",
- context, true, std::bind(&GitPluginPrivate::logRepository, this));
+ context, true, std::bind(&GitPluginPrivate::logRepository, this),
+ QKeySequence(useMacShortcuts ? Tr::tr("Meta+G,Meta+K") : Tr::tr("Alt+G,Alt+K")));
createRepositoryAction(localRepositoryMenu, "Reflog", "Git.ReflogRepository",
context, true, std::bind(&GitPluginPrivate::reflogRepository, this));
@@ -1065,12 +1063,11 @@ GitPluginPrivate::GitPluginPrivate()
this, &GitPluginPrivate::updateBranches, Qt::QueuedConnection);
/* "Gerrit" */
- m_gerritPlugin = new Gerrit::Internal::GerritPlugin(this);
- m_gerritPlugin->initialize(remoteRepositoryMenu);
- m_gerritPlugin->updateActions(currentState());
- m_gerritPlugin->addToLocator(m_commandLocator);
+ m_gerritPlugin.addToMenu(remoteRepositoryMenu);
+ m_gerritPlugin.updateActions(currentState());
+ m_gerritPlugin.addToLocator(m_commandLocator);
- connect(&m_settings, &AspectContainer::applied, this, &GitPluginPrivate::onApplySettings);
+ connect(&settings(), &AspectContainer::applied, this, &GitPluginPrivate::onApplySettings);
setupInstantBlame();
}
@@ -1440,17 +1437,12 @@ void GitPluginPrivate::setupInstantBlame()
return;
}
- if (!GitClient::instance()->settings().instantBlame.value()) {
+ if (!settings().instantBlame.value()) {
m_lastVisitedEditorLine = -1;
stopInstantBlame();
return;
}
- const Utils::FilePath workingDirectory = GitPlugin::currentState().currentFileTopLevel();
- if (workingDirectory.isEmpty())
- return;
- m_author = GitClient::instance()->getAuthor(workingDirectory);
-
const TextEditorWidget *widget = TextEditorWidget::fromEditor(editor);
if (!widget)
return;
@@ -1458,21 +1450,24 @@ void GitPluginPrivate::setupInstantBlame()
if (qobject_cast<const VcsBaseEditorWidget *>(widget))
return; // Skip in VCS editors like log or blame
+ const Utils::FilePath workingDirectory = GitPlugin::currentState().currentFileTopLevel();
+ if (!refreshWorkingDirectory(workingDirectory))
+ return;
+
m_blameCursorPosConn = connect(widget, &QPlainTextEdit::cursorPositionChanged, this,
[this] {
- if (!GitClient::instance()->settings().instantBlame.value()) {
+ if (!settings().instantBlame.value()) {
disconnect(m_blameCursorPosConn);
return;
}
m_cursorPositionChangedTimer->start(500);
});
- m_lastVisitedEditorLine = -1;
- instantBlame();
+ forceInstantBlame();
};
- connect(&GitClient::instance()->settings().instantBlame,
- &BoolAspect::valueChanged, this, [this, setupBlameForEditor](bool enabled) {
+ connect(&settings().instantBlame, &BoolAspect::valueChanged, this,
+ [this, setupBlameForEditor](bool enabled) {
if (enabled)
setupBlameForEditor(EditorManager::currentEditor());
else
@@ -1521,7 +1516,7 @@ CommitInfo parseBlameOutput(const QStringList &blame, const Utils::FilePath &fil
void GitPluginPrivate::instantBlameOnce()
{
- if (!GitClient::instance()->settings().instantBlame.value()) {
+ if (!settings().instantBlame.value()) {
const TextEditorWidget *widget = TextEditorWidget::currentTextEditorWidget();
if (!widget)
return;
@@ -1532,11 +1527,15 @@ void GitPluginPrivate::instantBlameOnce()
this, [this] { m_blameMark.reset(); }, Qt::SingleShotConnection);
const Utils::FilePath workingDirectory = GitPlugin::currentState().topLevel();
- if (workingDirectory.isEmpty())
+ if (!refreshWorkingDirectory(workingDirectory))
return;
- m_author = GitClient::instance()->getAuthor(workingDirectory);
}
+ forceInstantBlame();
+}
+
+void GitPluginPrivate::forceInstantBlame()
+{
m_lastVisitedEditorLine = -1;
instantBlame();
}
@@ -1556,7 +1555,7 @@ void GitPluginPrivate::instantBlame()
const QTextCursor cursor = widget->textCursor();
const QTextBlock block = cursor.block();
const int line = block.blockNumber() + 1;
- const int lines = widget->document()->lineCount();
+ const int lines = widget->document()->blockCount();
if (line >= lines) {
m_blameMark.reset();
@@ -1582,10 +1581,9 @@ void GitPluginPrivate::instantBlame()
const CommitInfo info = parseBlameOutput(output.split('\n'), filePath, m_author);
m_blameMark.reset(new BlameMark(filePath, line, info));
};
- QTextCodec *codec = GitClient::instance()->encoding(GitClient::EncodingCommit, workingDirectory);
GitClient::instance()->vcsExecWithHandler(workingDirectory,
{"blame", "-p", "-L", lineString, "--", filePath.toString()},
- this, commandHandler, RunFlags::NoOutput, codec);
+ this, commandHandler, RunFlags::NoOutput, m_codec);
}
void GitPluginPrivate::stopInstantBlame()
@@ -1595,6 +1593,51 @@ void GitPluginPrivate::stopInstantBlame()
disconnect(m_blameCursorPosConn);
}
+bool GitPluginPrivate::refreshWorkingDirectory(const FilePath &workingDirectory)
+{
+ if (workingDirectory.isEmpty())
+ return false;
+
+ if (m_workingDirectory == workingDirectory)
+ return true;
+
+ m_workingDirectory = workingDirectory;
+
+ const auto commitCodecHandler = [this, workingDirectory](const CommandResult &result) {
+ QTextCodec *codec = nullptr;
+
+ if (result.result() == ProcessResult::FinishedWithSuccess) {
+ const QString codecName = result.cleanedStdOut().trimmed();
+ codec = QTextCodec::codecForName(codecName.toUtf8());
+ } else {
+ codec = GitClient::instance()->defaultCommitEncoding();
+ }
+
+ if (m_codec != codec) {
+ m_codec = codec;
+ forceInstantBlame();
+ }
+ };
+ GitClient::instance()->readConfigAsync(workingDirectory, {"config", "i18n.commitEncoding"},
+ commitCodecHandler);
+
+ const auto authorHandler = [this, workingDirectory](const CommandResult &result) {
+ if (result.result() == ProcessResult::FinishedWithSuccess) {
+ const QString authorInfo = result.cleanedStdOut().trimmed();
+ const Author author = GitClient::instance()->parseAuthor(authorInfo);
+
+ if (m_author != author) {
+ m_author = author;
+ forceInstantBlame();
+ }
+ }
+ };
+ GitClient::instance()->readConfigAsync(workingDirectory, {"var", "GIT_AUTHOR_IDENT"},
+ authorHandler);
+
+ return true;
+}
+
IEditor *GitPluginPrivate::openSubmitEditor(const QString &fileName, const CommitData &cd)
{
IEditor *editor = EditorManager::openEditor(FilePath::fromString(fileName),
@@ -1683,7 +1726,7 @@ void GitPluginPrivate::pull()
const VcsBasePluginState state = currentState();
QTC_ASSERT(state.hasTopLevel(), return);
FilePath topLevel = state.topLevel();
- bool rebase = m_settings.pullRebase.value();
+ bool rebase = settings().pullRebase.value();
if (!rebase) {
QString currentBranch = m_gitClient.synchronousCurrentLocalBranch(topLevel);
@@ -1927,7 +1970,7 @@ void GitPluginPrivate::updateActions(VcsBasePluginPrivate::ActionState as)
updateContinueAndAbortCommands();
updateRepositoryBrowserAction();
- m_gerritPlugin->updateActions(state);
+ m_gerritPlugin.updateActions(state);
}
void GitPluginPrivate::updateContinueAndAbortCommands()
@@ -1965,7 +2008,7 @@ void GitPluginPrivate::updateContinueAndAbortCommands()
void GitPluginPrivate::delayedPushToGerrit()
{
- m_gerritPlugin->push(m_submitRepository);
+ m_gerritPlugin.push(m_submitRepository);
}
void GitPluginPrivate::updateBranches(const FilePath &repository)
@@ -1994,7 +2037,7 @@ QObject *GitPlugin::remoteCommand(const QStringList &options, const QString &wor
void GitPluginPrivate::updateRepositoryBrowserAction()
{
const bool repositoryEnabled = currentState().hasTopLevel();
- const bool hasRepositoryBrowserCmd = !m_settings.repositoryBrowserCmd.value().isEmpty();
+ const bool hasRepositoryBrowserCmd = !settings().repositoryBrowserCmd().isEmpty();
m_repositoryBrowserAction->setEnabled(repositoryEnabled && hasRepositoryBrowserCmd);
}
@@ -2100,7 +2143,7 @@ GitPluginPrivate::RepoUrl GitPluginPrivate::getRepoUrl(const QString &location)
FilePaths GitPluginPrivate::additionalToolsPath() const
{
- FilePaths res = m_gitClient.settings().searchPathList();
+ FilePaths res = settings().searchPathList();
const FilePath binaryPath = m_gitClient.gitBinDirectory();
if (!binaryPath.isEmpty() && !res.contains(binaryPath))
res << binaryPath;
@@ -2172,7 +2215,7 @@ void GitPlugin::updateBranches(const FilePath &repository)
void GitPlugin::gerritPush(const FilePath &topLevel)
{
- dd->m_gerritPlugin->push(topLevel);
+ dd->m_gerritPlugin.push(topLevel);
}
bool GitPlugin::isCommitEditorOpen()
diff --git a/src/plugins/git/gitplugin.h b/src/plugins/git/gitplugin.h
index 0b0e751801..59d9a87fde 100644
--- a/src/plugins/git/gitplugin.h
+++ b/src/plugins/git/gitplugin.h
@@ -3,7 +3,6 @@
#pragma once
-#include "gitsettings.h"
#include "git_global.h"
#include <coreplugin/iversioncontrol.h>
@@ -36,7 +35,6 @@ public:
static GitClient *client();
static Core::IVersionControl *versionControl();
- static const GitSettings &settings();
static const VcsBase::VcsBasePluginState &currentState();
static QString msgRepositoryLabel(const Utils::FilePath &repository);
@@ -63,7 +61,6 @@ private slots:
void testGitRemote_data();
void testGitRemote();
#endif
-
};
} // Git::Internal
diff --git a/src/plugins/git/gitsettings.cpp b/src/plugins/git/gitsettings.cpp
index 25a8f77988..4ebf7debee 100644
--- a/src/plugins/git/gitsettings.cpp
+++ b/src/plugins/git/gitsettings.cpp
@@ -17,43 +17,46 @@ using namespace VcsBase;
namespace Git::Internal {
+static GitSettings *theSettings;
+
+GitSettings &settings()
+{
+ return *theSettings;
+}
+
GitSettings::GitSettings()
{
+ theSettings = this;
+
+ setId(VcsBase::Constants::VCS_ID_GIT);
+ setDisplayName(Tr::tr("Git"));
+ setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
setSettingsGroup("Git");
path.setDisplayStyle(StringAspect::LineEditDisplay);
path.setLabelText(Tr::tr("Prepend to PATH:"));
- registerAspect(&binaryPath);
binaryPath.setDefaultValue("git");
- registerAspect(&pullRebase);
pullRebase.setSettingsKey("PullRebase");
pullRebase.setLabelText(Tr::tr("Pull with rebase"));
- registerAspect(&showTags);
showTags.setSettingsKey("ShowTags");
- registerAspect(&omitAnnotationDate);
omitAnnotationDate.setSettingsKey("OmitAnnotationDate");
- registerAspect(&ignoreSpaceChangesInDiff);
ignoreSpaceChangesInDiff.setSettingsKey("SpaceIgnorantDiff");
ignoreSpaceChangesInDiff.setDefaultValue(true);
- registerAspect(&ignoreSpaceChangesInBlame);
ignoreSpaceChangesInBlame.setSettingsKey("SpaceIgnorantBlame");
ignoreSpaceChangesInBlame.setDefaultValue(true);
- registerAspect(&blameMoveDetection);
blameMoveDetection.setSettingsKey("BlameDetectMove");
blameMoveDetection.setDefaultValue(0);
- registerAspect(&diffPatience);
diffPatience.setSettingsKey("DiffPatience");
diffPatience.setDefaultValue(true);
- registerAspect(&winSetHomeEnvironment);
winSetHomeEnvironment.setSettingsKey("WinSetHomeEnvironment");
winSetHomeEnvironment.setDefaultValue(true);
winSetHomeEnvironment.setLabelText(Tr::tr("Set \"HOME\" environment variable"));
@@ -71,129 +74,105 @@ GitSettings::GitSettings()
winSetHomeEnvironment.setVisible(false);
}
- registerAspect(&gitkOptions);
gitkOptions.setDisplayStyle(StringAspect::LineEditDisplay);
gitkOptions.setSettingsKey("GitKOptions");
gitkOptions.setLabelText(Tr::tr("Arguments:"));
- registerAspect(&logDiff);
logDiff.setSettingsKey("LogDiff");
logDiff.setToolTip(Tr::tr("Note that huge amount of commits might take some time."));
- registerAspect(&repositoryBrowserCmd);
- repositoryBrowserCmd.setDisplayStyle(StringAspect::PathChooserDisplay);
repositoryBrowserCmd.setSettingsKey("RepositoryBrowserCmd");
repositoryBrowserCmd.setExpectedKind(PathChooser::ExistingCommand);
repositoryBrowserCmd.setHistoryCompleter("Git.RepoCommand.History");
repositoryBrowserCmd.setDisplayName(Tr::tr("Git Repository Browser Command"));
repositoryBrowserCmd.setLabelText(Tr::tr("Command:"));
- registerAspect(&instantBlame);
instantBlame.setSettingsKey("Git Instant");
instantBlame.setDefaultValue(true);
instantBlame.setLabelText(Tr::tr("Add instant blame annotations to editor"));
- instantBlame.setToolTip(Tr::tr("Directly annotate each line in the editor "
- "when scrolling through the document."));
+ instantBlame.setToolTip(
+ Tr::tr("Annotate the current line in the editor with Git \"blame\" output."));
- registerAspect(&graphLog);
graphLog.setSettingsKey("GraphLog");
- registerAspect(&colorLog);
colorLog.setSettingsKey("ColorLog");
colorLog.setDefaultValue(true);
- registerAspect(&firstParent);
firstParent.setSettingsKey("FirstParent");
- registerAspect(&followRenames);
followRenames.setSettingsKey("FollowRenames");
followRenames.setDefaultValue(true);
- registerAspect(&lastResetIndex);
lastResetIndex.setSettingsKey("LastResetIndex");
- registerAspect(&refLogShowDate);
refLogShowDate.setSettingsKey("RefLogShowDate");
timeout.setDefaultValue(Utils::HostOsInfo::isWindowsHost() ? 60 : 30);
- connect(&binaryPath, &StringAspect::valueChanged, this, [this] { tryResolve = true; });
- connect(&path, &StringAspect::valueChanged, this, [this] { tryResolve = true; });
-}
-
-FilePath GitSettings::gitExecutable(bool *ok, QString *errorMessage) const
-{
- // Locate binary in path if one is specified, otherwise default to pathless binary.
- if (ok)
- *ok = true;
- if (errorMessage)
- errorMessage->clear();
-
- if (tryResolve) {
- resolvedBinPath = binaryPath.filePath();
- if (!resolvedBinPath.isAbsolutePath())
- resolvedBinPath = resolvedBinPath.searchInPath({path.filePath()}, FilePath::PrependToPath);
- tryResolve = false;
- }
-
- if (resolvedBinPath.isEmpty()) {
- if (ok)
- *ok = false;
- if (errorMessage)
- *errorMessage = Tr::tr("The binary \"%1\" could not be located in the path \"%2\"")
- .arg(binaryPath.value(), path.value());
- }
- return resolvedBinPath;
-}
-
-// GitSettingsPage
-
-GitSettingsPage::GitSettingsPage(GitSettings *settings)
-{
- setId(VcsBase::Constants::VCS_ID_GIT);
- setDisplayName(Tr::tr("Git"));
- setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- GitSettings &s = *settings;
+ setLayouter([this] {
using namespace Layouting;
-
- Column {
+ return Column {
Group {
title(Tr::tr("Configuration")),
Column {
- Row { s.path },
- s.winSetHomeEnvironment,
+ Row { path },
+ winSetHomeEnvironment,
}
},
Group {
title(Tr::tr("Miscellaneous")),
Column {
- Row { s.logCount, s.timeout, st },
- s.pullRebase
+ Row { logCount, timeout, st },
+ pullRebase
}
},
Group {
title(Tr::tr("Gitk")),
- Row { s.gitkOptions }
+ Row { gitkOptions }
},
Group {
title(Tr::tr("Repository Browser")),
- Row { s.repositoryBrowserCmd }
+ Row { repositoryBrowserCmd }
},
Group {
title(Tr::tr("Instant Blame")),
- Row { s.instantBlame }
+ Row { instantBlame }
},
st
- }.attachTo(widget);
+ };
});
+ connect(&binaryPath, &StringAspect::valueChanged, this, [this] { tryResolve = true; });
+ connect(&path, &StringAspect::valueChanged, this, [this] { tryResolve = true; });
+}
+
+FilePath GitSettings::gitExecutable(bool *ok, QString *errorMessage) const
+{
+ // Locate binary in path if one is specified, otherwise default to pathless binary.
+ if (ok)
+ *ok = true;
+ if (errorMessage)
+ errorMessage->clear();
+
+ if (tryResolve) {
+ resolvedBinPath = binaryPath();
+ if (!resolvedBinPath.isAbsolutePath())
+ resolvedBinPath = resolvedBinPath.searchInPath({path.filePath()}, FilePath::PrependToPath);
+ tryResolve = false;
+ }
+
+ if (resolvedBinPath.isEmpty()) {
+ if (ok)
+ *ok = false;
+ if (errorMessage)
+ *errorMessage = Tr::tr("The binary \"%1\" could not be located in the path \"%2\"")
+ .arg(binaryPath.value(), path.value());
+ }
+ return resolvedBinPath;
}
} // Git::Internal
diff --git a/src/plugins/git/gitsettings.h b/src/plugins/git/gitsettings.h
index c03deadeb9..df6cf56e4e 100644
--- a/src/plugins/git/gitsettings.h
+++ b/src/plugins/git/gitsettings.h
@@ -3,7 +3,6 @@
#pragma once
-#include <coreplugin/dialogs/ioptionspage.h>
#include <vcsbase/vcsbaseclientsettings.h>
namespace Git::Internal {
@@ -21,24 +20,24 @@ class GitSettings : public VcsBase::VcsBaseSettings
public:
GitSettings();
- Utils::BoolAspect pullRebase;
- Utils::BoolAspect showTags;
- Utils::BoolAspect omitAnnotationDate;
- Utils::BoolAspect ignoreSpaceChangesInDiff;
- Utils::BoolAspect ignoreSpaceChangesInBlame;
- Utils::IntegerAspect blameMoveDetection;
- Utils::BoolAspect diffPatience;
- Utils::BoolAspect winSetHomeEnvironment;
- Utils::StringAspect gitkOptions;
- Utils::BoolAspect logDiff;
- Utils::StringAspect repositoryBrowserCmd;
- Utils::BoolAspect graphLog;
- Utils::BoolAspect colorLog;
- Utils::BoolAspect firstParent;
- Utils::BoolAspect followRenames;
- Utils::IntegerAspect lastResetIndex;
- Utils::BoolAspect refLogShowDate;
- Utils::BoolAspect instantBlame;
+ Utils::BoolAspect pullRebase{this};
+ Utils::BoolAspect showTags{this};
+ Utils::BoolAspect omitAnnotationDate{this};
+ Utils::BoolAspect ignoreSpaceChangesInDiff{this};
+ Utils::BoolAspect ignoreSpaceChangesInBlame{this};
+ Utils::IntegerAspect blameMoveDetection{this};
+ Utils::BoolAspect diffPatience{this};
+ Utils::BoolAspect winSetHomeEnvironment{this};
+ Utils::StringAspect gitkOptions{this};
+ Utils::BoolAspect logDiff{this};
+ Utils::FilePathAspect repositoryBrowserCmd{this};
+ Utils::BoolAspect graphLog{this};
+ Utils::BoolAspect colorLog{this};
+ Utils::BoolAspect firstParent{this};
+ Utils::BoolAspect followRenames{this};
+ Utils::IntegerAspect lastResetIndex{this};
+ Utils::BoolAspect refLogShowDate{this};
+ Utils::BoolAspect instantBlame{this};
mutable Utils::FilePath resolvedBinPath;
mutable bool tryResolve = true;
@@ -46,10 +45,6 @@ public:
Utils::FilePath gitExecutable(bool *ok = nullptr, QString *errorMessage = nullptr) const;
};
-class GitSettingsPage final : public Core::IOptionsPage
-{
-public:
- explicit GitSettingsPage(GitSettings *settings);
-};
+GitSettings &settings();
} // Git::Internal
diff --git a/src/plugins/git/gitsubmiteditor.cpp b/src/plugins/git/gitsubmiteditor.cpp
index 23755d9875..9ff83c8c12 100644
--- a/src/plugins/git/gitsubmiteditor.cpp
+++ b/src/plugins/git/gitsubmiteditor.cpp
@@ -11,8 +11,8 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/iversioncontrol.h>
#include <coreplugin/progressmanager/progressmanager.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <vcsbase/submitfilemodel.h>
#include <vcsbase/vcsoutputwindow.h>
@@ -204,7 +204,7 @@ void GitSubmitEditor::updateFileModel()
return;
w->setUpdateInProgress(true);
// TODO: Check if fetch works OK from separate thread, refactor otherwise
- m_fetchWatcher.setFuture(Utils::runAsync(&CommitDataFetchResult::fetch,
+ m_fetchWatcher.setFuture(Utils::asyncRun(&CommitDataFetchResult::fetch,
m_commitType, m_workingDirectory));
Core::ProgressManager::addTask(m_fetchWatcher.future(), Tr::tr("Refreshing Commit Data"),
TASK_UPDATE_COMMIT);
diff --git a/src/plugins/git/gitsubmiteditorwidget.cpp b/src/plugins/git/gitsubmiteditorwidget.cpp
index 65bc5beab4..ec09437e26 100644
--- a/src/plugins/git/gitsubmiteditorwidget.cpp
+++ b/src/plugins/git/gitsubmiteditorwidget.cpp
@@ -36,8 +36,6 @@ class GitSubmitPanel : public QWidget
public:
GitSubmitPanel()
{
- resize(364, 269);
-
repositoryLabel = new QLabel(Tr::tr("repository"));
branchLabel = new QLabel(Tr::tr("branch")); // FIXME: Isn't this overwritten soon?
showHeadLabel = new QLabel("<a href=\"head\">" + Tr::tr("Show HEAD") + "</a>");
@@ -81,7 +79,8 @@ public:
}
},
editGroup,
- }.attachTo(this, WithoutMargins);
+ noMargin,
+ }.attachTo(this);
}
QLabel *repositoryLabel;
diff --git a/src/plugins/git/logchangedialog.cpp b/src/plugins/git/logchangedialog.cpp
index 55055cbf76..75660c1189 100644
--- a/src/plugins/git/logchangedialog.cpp
+++ b/src/plugins/git/logchangedialog.cpp
@@ -224,7 +224,7 @@ LogChangeDialog::LogChangeDialog(bool isReset, QWidget *parent) :
m_resetTypeComboBox->addItem(Tr::tr("Hard"), "--hard");
m_resetTypeComboBox->addItem(Tr::tr("Mixed"), "--mixed");
m_resetTypeComboBox->addItem(Tr::tr("Soft"), "--soft");
- m_resetTypeComboBox->setCurrentIndex(GitClient::settings().lastResetIndex.value());
+ m_resetTypeComboBox->setCurrentIndex(settings().lastResetIndex());
popUpLayout->addWidget(m_resetTypeComboBox);
popUpLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored));
}
@@ -250,7 +250,7 @@ bool LogChangeDialog::runDialog(const FilePath &repository,
if (QDialog::exec() == QDialog::Accepted) {
if (m_resetTypeComboBox)
- GitClient::settings().lastResetIndex.setValue(m_resetTypeComboBox->currentIndex());
+ settings().lastResetIndex.setValue(m_resetTypeComboBox->currentIndex());
return true;
}
return false;
diff --git a/src/plugins/git/mergetool.cpp b/src/plugins/git/mergetool.cpp
index f696bef124..6fbc9d5ea8 100644
--- a/src/plugins/git/mergetool.cpp
+++ b/src/plugins/git/mergetool.cpp
@@ -23,8 +23,8 @@ namespace Git::Internal {
MergeTool::MergeTool(QObject *parent) : QObject(parent)
{
- connect(&m_process, &QtcProcess::done, this, &MergeTool::done);
- connect(&m_process, &QtcProcess::readyReadStandardOutput, this, &MergeTool::readData);
+ connect(&m_process, &Process::done, this, &MergeTool::done);
+ connect(&m_process, &Process::readyReadStandardOutput, this, &MergeTool::readData);
Environment env = Environment::systemEnvironment();
env.set("LANG", "C");
env.set("LANGUAGE", "C");
diff --git a/src/plugins/git/mergetool.h b/src/plugins/git/mergetool.h
index 290b50f10c..727f9a85df 100644
--- a/src/plugins/git/mergetool.h
+++ b/src/plugins/git/mergetool.h
@@ -3,7 +3,7 @@
#pragma once
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QObject>
#include <QStringList>
@@ -49,7 +49,7 @@ private:
void chooseAction();
void addButton(QMessageBox *msgBox, const QString &text, char key);
- Utils::QtcProcess m_process;
+ Utils::Process m_process;
MergeType m_mergeType = NormalMerge;
QString m_fileName;
FileState m_localState = UnknownState;
diff --git a/src/plugins/gitlab/gitlabclonedialog.cpp b/src/plugins/gitlab/gitlabclonedialog.cpp
index 4d54105e44..3a061b9cc5 100644
--- a/src/plugins/gitlab/gitlabclonedialog.cpp
+++ b/src/plugins/gitlab/gitlabclonedialog.cpp
@@ -23,8 +23,8 @@
#include <utils/infolabel.h>
#include <utils/mimeutils.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <vcsbase/vcsbaseplugin.h>
#include <vcsbase/vcscommand.h>
diff --git a/src/plugins/gitlab/gitlabdialog.cpp b/src/plugins/gitlab/gitlabdialog.cpp
index 4a10888c94..9375c057f5 100644
--- a/src/plugins/gitlab/gitlabdialog.cpp
+++ b/src/plugins/gitlab/gitlabdialog.cpp
@@ -9,7 +9,7 @@
#include "gitlabprojectsettings.h"
#include "gitlabtr.h"
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <texteditor/fontsettings.h>
#include <texteditor/texteditorsettings.h>
@@ -188,7 +188,7 @@ void GitLabDialog::requestMainViewUpdate()
bool linked = false;
m_currentServerId = Id();
- if (auto project = ProjectExplorer::SessionManager::startupProject()) {
+ if (auto project = ProjectExplorer::ProjectManager::startupProject()) {
GitLabProjectSettings *projSettings = GitLabPlugin::projectSettings(project);
if (projSettings->isLinked()) {
m_currentServerId = projSettings->currentServer();
diff --git a/src/plugins/gitlab/gitlaboptionspage.cpp b/src/plugins/gitlab/gitlaboptionspage.cpp
index 4f519e7788..4660cc92bb 100644
--- a/src/plugins/gitlab/gitlaboptionspage.cpp
+++ b/src/plugins/gitlab/gitlaboptionspage.cpp
@@ -9,6 +9,7 @@
#include <coreplugin/icore.h>
#include <utils/algorithm.h>
+#include <utils/aspects.h>
#include <utils/layoutbuilder.h>
#include <utils/qtcassert.h>
@@ -44,6 +45,25 @@ static bool hostValid(const QString &host)
return (host == "localhost") || dn.match(host).hasMatch();
}
+class GitLabServerWidget : public QWidget
+{
+public:
+ enum Mode { Display, Edit };
+ explicit GitLabServerWidget(Mode m, QWidget *parent = nullptr);
+
+ GitLabServer gitLabServer() const;
+ void setGitLabServer(const GitLabServer &server);
+
+private:
+ Mode m_mode = Display;
+ Id m_id;
+ StringAspect m_host;
+ StringAspect m_description;
+ StringAspect m_token;
+ IntegerAspect m_port;
+ BoolAspect m_secure;
+};
+
GitLabServerWidget::GitLabServerWidget(Mode m, QWidget *parent)
: QWidget(parent)
, m_mode(m)
@@ -77,13 +97,14 @@ GitLabServerWidget::GitLabServerWidget(Mode m, QWidget *parent)
Row {
Form {
- m_host,
- m_description,
- m_token,
- m_port,
- m_secure
+ m_host, br,
+ m_description, br,
+ m_token, br,
+ m_port, br,
+ m_secure,
+ m == Edit ? normalMargin : noMargin
},
- }.attachTo(this, m == Edit ? WithMargins : WithoutMargins);
+ }.attachTo(this);
}
GitLabServer GitLabServerWidget::gitLabServer() const
@@ -108,14 +129,35 @@ void GitLabServerWidget::setGitLabServer(const GitLabServer &server)
m_secure.setValue(server.secure);
}
-GitLabOptionsWidget::GitLabOptionsWidget(QWidget *parent)
- : QWidget(parent)
+class GitLabOptionsWidget : public Core::IOptionsPageWidget
+{
+public:
+ explicit GitLabOptionsWidget(GitLabParameters *parameters);
+
+private:
+ void showEditServerDialog();
+ void showAddServerDialog();
+ void removeCurrentTriggered();
+ void addServer(const GitLabServer &newServer);
+ void modifyCurrentServer(const GitLabServer &newServer);
+ void updateButtonsState();
+
+ GitLabParameters *m_parameters = nullptr;
+ GitLabServerWidget *m_gitLabServerWidget = nullptr;
+ QPushButton *m_edit = nullptr;
+ QPushButton *m_remove = nullptr;
+ QPushButton *m_add = nullptr;
+ QComboBox *m_defaultGitLabServer = nullptr;
+ FilePathAspect m_curl;
+};
+
+GitLabOptionsWidget::GitLabOptionsWidget(GitLabParameters *params)
+ : m_parameters(params)
{
auto defaultLabel = new QLabel(Tr::tr("Default:"), this);
m_defaultGitLabServer = new QComboBox(this);
- m_curl.setDisplayStyle(Utils::StringAspect::DisplayStyle::PathChooserDisplay);
m_curl.setLabelText(Tr::tr("curl:"));
- m_curl.setExpectedKind(Utils::PathChooser::ExistingCommand);
+ m_curl.setExpectedKind(PathChooser::ExistingCommand);
m_gitLabServerWidget = new GitLabServerWidget(GitLabServerWidget::Display, this);
@@ -126,7 +168,7 @@ GitLabOptionsWidget::GitLabOptionsWidget(QWidget *parent)
m_add = new QPushButton(Tr::tr("Add..."), this);
m_add->setToolTip(Tr::tr("Add new GitLab server configuration."));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Grid {
Form {
@@ -136,42 +178,43 @@ GitLabOptionsWidget::GitLabOptionsWidget(QWidget *parent)
}, Column { m_add, m_edit, m_remove, st },
}.attachTo(this);
- connect(m_edit, &QPushButton::clicked, this, &GitLabOptionsWidget::showEditServerDialog);
- connect(m_remove, &QPushButton::clicked, this, &GitLabOptionsWidget::removeCurrentTriggered);
- connect(m_add, &QPushButton::clicked, this, &GitLabOptionsWidget::showAddServerDialog);
- connect(m_defaultGitLabServer, &QComboBox::currentIndexChanged, this, [this] {
- m_gitLabServerWidget->setGitLabServer(
- m_defaultGitLabServer->currentData().value<GitLabServer>());
- });
-}
-
-GitLabParameters GitLabOptionsWidget::parameters() const
-{
- GitLabParameters result;
- // get all configured gitlabservers
- for (int i = 0, end = m_defaultGitLabServer->count(); i < end; ++i)
- result.gitLabServers.append(m_defaultGitLabServer->itemData(i).value<GitLabServer>());
- if (m_defaultGitLabServer->count())
- result.defaultGitLabServer = m_defaultGitLabServer->currentData().value<GitLabServer>().id;
- result.curl = m_curl.filePath();
- return result;
-}
+ m_curl.setFilePath(params->curl);
-void GitLabOptionsWidget::setParameters(const GitLabParameters &params)
-{
- m_curl.setFilePath(params.curl);
-
- for (const auto &gitLabServer : params.gitLabServers) {
+ for (const auto &gitLabServer : params->gitLabServers) {
m_defaultGitLabServer->addItem(gitLabServer.displayString(),
QVariant::fromValue(gitLabServer));
}
- const GitLabServer found = params.currentDefaultServer();
+ const GitLabServer found = params->currentDefaultServer();
if (found.id.isValid()) {
m_defaultGitLabServer->setCurrentIndex(m_defaultGitLabServer->findData(
QVariant::fromValue(found)));
}
updateButtonsState();
+
+ connect(m_edit, &QPushButton::clicked, this, &GitLabOptionsWidget::showEditServerDialog);
+ connect(m_remove, &QPushButton::clicked, this, &GitLabOptionsWidget::removeCurrentTriggered);
+ connect(m_add, &QPushButton::clicked, this, &GitLabOptionsWidget::showAddServerDialog);
+ connect(m_defaultGitLabServer, &QComboBox::currentIndexChanged, this, [this] {
+ m_gitLabServerWidget->setGitLabServer(
+ m_defaultGitLabServer->currentData().value<GitLabServer>());
+ });
+
+ setOnApply([this] {
+ GitLabParameters result;
+ // get all configured gitlabservers
+ for (int i = 0, end = m_defaultGitLabServer->count(); i < end; ++i)
+ result.gitLabServers.append(m_defaultGitLabServer->itemData(i).value<GitLabServer>());
+ if (m_defaultGitLabServer->count())
+ result.defaultGitLabServer = m_defaultGitLabServer->currentData().value<GitLabServer>().id;
+ result.curl = m_curl();
+
+ if (result != *m_parameters) {
+ m_parameters->assign(result);
+ m_parameters->toSettings(Core::ICore::settings());
+ emit m_parameters->changed();
+ }
+ });
}
void GitLabOptionsWidget::showEditServerDialog()
@@ -253,39 +296,14 @@ void GitLabOptionsWidget::updateButtonsState()
m_remove->setEnabled(hasItems);
}
-GitLabOptionsPage::GitLabOptionsPage(GitLabParameters *p, QObject *parent)
- : Core::IOptionsPage{parent}
- , m_parameters(p)
+// GitLabOptionsPage
+
+GitLabOptionsPage::GitLabOptionsPage(GitLabParameters *p)
{
setId(Constants::GITLAB_SETTINGS);
setDisplayName(Tr::tr("GitLab"));
setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
-}
-
-QWidget *GitLabOptionsPage::widget()
-{
- if (!m_widget) {
- m_widget = new GitLabOptionsWidget;
- m_widget->setParameters(*m_parameters);
- }
- return m_widget;
-}
-
-void GitLabOptionsPage::apply()
-{
- if (GitLabOptionsWidget *w = m_widget.data()) {
- GitLabParameters newParameters = w->parameters();
- if (newParameters != *m_parameters) {
- *m_parameters = newParameters;
- m_parameters->toSettings(Core::ICore::settings());
- emit settingsChanged();
- }
- }
-}
-
-void GitLabOptionsPage::finish()
-{
- delete m_widget;
+ setWidgetCreator([p] { return new GitLabOptionsWidget(p); });
}
} // namespace GitLab
diff --git a/src/plugins/gitlab/gitlaboptionspage.h b/src/plugins/gitlab/gitlaboptionspage.h
index 327cc1128a..664f4a26fc 100644
--- a/src/plugins/gitlab/gitlaboptionspage.h
+++ b/src/plugins/gitlab/gitlaboptionspage.h
@@ -6,84 +6,15 @@
#include "gitlabparameters.h"
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
-
-#include <QPointer>
-
-QT_BEGIN_NAMESPACE
-class QComboBox;
-class QPushButton;
-QT_END_NAMESPACE
namespace GitLab {
-namespace Constants {
-const char GITLAB_SETTINGS[] = "GitLab";
-} // namespace Constants
-
-class GitLabServerWidget : public QWidget
-{
-public:
- enum Mode { Display, Edit };
- explicit GitLabServerWidget(Mode m, QWidget *parent = nullptr);
-
- GitLabServer gitLabServer() const;
- void setGitLabServer(const GitLabServer &server);
-
- bool isValid() const;
-private:
- Mode m_mode = Display;
- Utils::Id m_id;
- Utils::StringAspect m_host;
- Utils::StringAspect m_description;
- Utils::StringAspect m_token;
- Utils::IntegerAspect m_port;
- Utils::BoolAspect m_secure;
-};
-
-class GitLabOptionsWidget : public QWidget
-{
- Q_OBJECT
-public:
- explicit GitLabOptionsWidget(QWidget *parent = nullptr);
-
- GitLabParameters parameters() const;
- void setParameters(const GitLabParameters &params);
-
-private:
- void showEditServerDialog();
- void showAddServerDialog();
- void removeCurrentTriggered();
- void addServer(const GitLabServer &newServer);
- void modifyCurrentServer(const GitLabServer &newServer);
- void updateButtonsState();
-
- GitLabServerWidget *m_gitLabServerWidget = nullptr;
- QPushButton *m_edit = nullptr;
- QPushButton *m_remove = nullptr;
- QPushButton *m_add = nullptr;
- QComboBox *m_defaultGitLabServer = nullptr;
- Utils::StringAspect m_curl;
-};
+namespace Constants { const char GITLAB_SETTINGS[] = "GitLab"; }
class GitLabOptionsPage : public Core::IOptionsPage
{
- Q_OBJECT
public:
- explicit GitLabOptionsPage(GitLabParameters *p, QObject *parent = nullptr);
-
- QWidget *widget() final;
- void apply() final;
- void finish() final;
-
-signals:
- void settingsChanged();
-
-private:
- void addServer();
-
- GitLabParameters *m_parameters;
- QPointer<GitLabOptionsWidget> m_widget;
+ explicit GitLabOptionsPage(GitLabParameters *p);
};
-} // namespace GitLab
+} // GitLab
diff --git a/src/plugins/gitlab/gitlabparameters.cpp b/src/plugins/gitlab/gitlabparameters.cpp
index 1a3f970edb..79a6ce7da6 100644
--- a/src/plugins/gitlab/gitlabparameters.cpp
+++ b/src/plugins/gitlab/gitlabparameters.cpp
@@ -102,6 +102,13 @@ GitLabParameters::GitLabParameters()
{
}
+void GitLabParameters::assign(const GitLabParameters &other)
+{
+ curl = other.curl;
+ defaultGitLabServer = other.defaultGitLabServer;
+ gitLabServers = other.gitLabServers;
+}
+
bool GitLabParameters::equals(const GitLabParameters &other) const
{
return curl == other.curl && defaultGitLabServer == other.defaultGitLabServer
diff --git a/src/plugins/gitlab/gitlabparameters.h b/src/plugins/gitlab/gitlabparameters.h
index 57dda3667d..7a21976dc2 100644
--- a/src/plugins/gitlab/gitlabparameters.h
+++ b/src/plugins/gitlab/gitlabparameters.h
@@ -38,11 +38,14 @@ public:
bool validateCert = true;
};
-class GitLabParameters
+class GitLabParameters : public QObject
{
+ Q_OBJECT
+
public:
GitLabParameters();
+ void assign(const GitLabParameters &other);
bool equals(const GitLabParameters &other) const;
bool isValid() const;
@@ -52,6 +55,10 @@ public:
GitLabServer currentDefaultServer() const;
GitLabServer serverForId(const Utils::Id &id) const;
+signals:
+ void changed();
+
+public:
friend bool operator==(const GitLabParameters &p1, const GitLabParameters &p2)
{
return p1.equals(p2);
diff --git a/src/plugins/gitlab/gitlabplugin.cpp b/src/plugins/gitlab/gitlabplugin.cpp
index 2059ae4d84..5ae4df682b 100644
--- a/src/plugins/gitlab/gitlabplugin.cpp
+++ b/src/plugins/gitlab/gitlabplugin.cpp
@@ -14,11 +14,15 @@
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/icore.h>
+
#include <git/gitplugin.h>
+
#include <projectexplorer/project.h>
#include <projectexplorer/projectpanelfactory.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
+
#include <utils/qtcassert.h>
+
#include <vcsbase/vcsoutputwindow.h>
#include <QAction>
@@ -34,6 +38,18 @@ const char GITLAB_OPEN_VIEW[] = "GitLab.OpenView";
class GitLabPluginPrivate : public QObject
{
public:
+ void setupNotificationTimer();
+ void fetchEvents();
+ void fetchUser();
+ void createAndSendEventsRequest(const QDateTime timeStamp, int page = -1);
+ void handleUser(const User &user);
+ void handleEvents(const Events &events, const QDateTime &timeStamp);
+
+ void onSettingsChanged() {
+ if (dialog)
+ dialog->updateRemotes();
+ }
+
GitLabParameters parameters;
GitLabOptionsPage optionsPage{&parameters};
QHash<ProjectExplorer::Project *, GitLabProjectSettings *> projectSettings;
@@ -43,13 +59,6 @@ public:
QString projectName;
Utils::Id serverId;
bool runningQuery = false;
-
- void setupNotificationTimer();
- void fetchEvents();
- void fetchUser();
- void createAndSendEventsRequest(const QDateTime timeStamp, int page = -1);
- void handleUser(const User &user);
- void handleEvents(const Events &events, const QDateTime &timeStamp);
};
static GitLabPluginPrivate *dd = nullptr;
@@ -85,12 +94,8 @@ void GitLabPlugin::initialize()
connect(openViewAction, &QAction::triggered, this, &GitLabPlugin::openView);
Core::ActionContainer *ac = Core::ActionManager::actionContainer(Core::Constants::M_TOOLS);
ac->addAction(gitlabCommand);
- connect(&dd->optionsPage, &GitLabOptionsPage::settingsChanged, this, [] {
- if (dd->dialog)
- dd->dialog->updateRemotes();
- });
- connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::startupProjectChanged,
+ connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged,
this, &GitLabPlugin::onStartupProjectChanged);
}
@@ -121,7 +126,7 @@ void GitLabPlugin::onStartupProjectChanged()
{
QTC_ASSERT(dd, return);
disconnect(&dd->notificationTimer);
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project) {
dd->notificationTimer.stop();
return;
@@ -147,7 +152,7 @@ void GitLabPluginPrivate::setupNotificationTimer()
void GitLabPluginPrivate::fetchEvents()
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(project, return);
if (runningQuery)
@@ -218,7 +223,7 @@ void GitLabPluginPrivate::handleEvents(const Events &events, const QDateTime &ti
{
runningQuery = false;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(project, return);
GitLabProjectSettings *projSettings = GitLabPlugin::projectSettings(project);
@@ -301,7 +306,7 @@ bool GitLabPlugin::handleCertificateIssue(const Utils::Id &serverId)
int index = dd->parameters.gitLabServers.indexOf(server);
server.validateCert = false;
dd->parameters.gitLabServers.replace(index, server);
- emit dd->optionsPage.settingsChanged();
+ dd->onSettingsChanged();
return true;
}
return false;
@@ -311,7 +316,7 @@ void GitLabPlugin::linkedStateChanged(bool enabled)
{
QTC_ASSERT(dd, return);
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (project) {
const GitLabProjectSettings *pSettings = projectSettings(project);
dd->serverId = pSettings->currentServer();
diff --git a/src/plugins/gitlab/gitlabprojectsettings.cpp b/src/plugins/gitlab/gitlabprojectsettings.cpp
index 73532e492f..240f5292e4 100644
--- a/src/plugins/gitlab/gitlabprojectsettings.cpp
+++ b/src/plugins/gitlab/gitlabprojectsettings.cpp
@@ -157,7 +157,7 @@ GitLabProjectSettingsWidget::GitLabProjectSettingsWidget(ProjectExplorer::Projec
connect(m_hostCB, &QComboBox::currentIndexChanged, this, [this] {
m_infoLabel->setVisible(false);
});
- connect(GitLabPlugin::optionsPage(), &GitLabOptionsPage::settingsChanged,
+ connect(GitLabPlugin::globalParameters(), &GitLabParameters::changed,
this, &GitLabProjectSettingsWidget::updateUi);
updateUi();
}
diff --git a/src/plugins/gitlab/queryrunner.cpp b/src/plugins/gitlab/queryrunner.cpp
index 2ddc86cd09..fd56c68c29 100644
--- a/src/plugins/gitlab/queryrunner.cpp
+++ b/src/plugins/gitlab/queryrunner.cpp
@@ -96,7 +96,7 @@ QueryRunner::QueryRunner(const Query &query, const Id &id, QObject *parent)
url += query.toString();
args << url;
m_process.setCommand({p->curl, args});
- connect(&m_process, &QtcProcess::done, this, [this, id] {
+ connect(&m_process, &Process::done, this, [this, id] {
if (m_process.result() != ProcessResult::FinishedWithSuccess) {
const int exitCode = m_process.exitCode();
if (m_process.exitStatus() == QProcess::NormalExit
diff --git a/src/plugins/gitlab/queryrunner.h b/src/plugins/gitlab/queryrunner.h
index ee698e384f..08afc56e1b 100644
--- a/src/plugins/gitlab/queryrunner.h
+++ b/src/plugins/gitlab/queryrunner.h
@@ -4,7 +4,7 @@
#pragma once
#include <utils/id.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QObject>
@@ -47,7 +47,7 @@ signals:
void resultRetrieved(const QByteArray &json);
private:
- Utils::QtcProcess m_process;
+ Utils::Process m_process;
};
} // namespace GitLab
diff --git a/src/plugins/haskell/CMakeLists.txt b/src/plugins/haskell/CMakeLists.txt
index 0a62921168..d45326695c 100644
--- a/src/plugins/haskell/CMakeLists.txt
+++ b/src/plugins/haskell/CMakeLists.txt
@@ -1,7 +1,6 @@
add_qtc_plugin(Haskell
PLUGIN_DEPENDS
QtCreator::Core QtCreator::TextEditor QtCreator::ProjectExplorer
- DEPENDS Qt5::Widgets
SOURCES
haskell.qrc
haskell_global.h
@@ -13,9 +12,9 @@ add_qtc_plugin(Haskell
haskellplugin.cpp haskellplugin.h
haskellproject.cpp haskellproject.h
haskellrunconfiguration.cpp haskellrunconfiguration.h
+ haskellsettings.cpp haskellsettings.h
haskelltokenizer.cpp haskelltokenizer.h
haskelltr.h
- optionspage.cpp optionspage.h
stackbuildstep.cpp stackbuildstep.h
)
diff --git a/src/plugins/haskell/haskell.qbs b/src/plugins/haskell/haskell.qbs
index 30eb8328f1..5c1be11538 100644
--- a/src/plugins/haskell/haskell.qbs
+++ b/src/plugins/haskell/haskell.qbs
@@ -21,8 +21,8 @@ QtcPlugin {
"haskellplugin.cpp", "haskellplugin.h",
"haskellproject.cpp", "haskellproject.h",
"haskellrunconfiguration.cpp", "haskellrunconfiguration.h",
+ "haskellsettings.cpp", "haskellsettings.h",
"haskelltokenizer.cpp", "haskelltokenizer.h",
- "optionspage.cpp", "optionspage.h",
"stackbuildstep.cpp", "stackbuildstep.h"
]
}
diff --git a/src/plugins/haskell/haskell.qrc b/src/plugins/haskell/haskell.qrc
index 654f45818c..3ddf22f76d 100644
--- a/src/plugins/haskell/haskell.qrc
+++ b/src/plugins/haskell/haskell.qrc
@@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/haskell">
- <file>images/category_haskell.png</file>
+ <file>images/settingscategory_haskell.png</file>
+ <file>images/settingscategory_haskell@2x.png</file>
</qresource>
</RCC>
diff --git a/src/plugins/haskell/haskelleditorfactory.cpp b/src/plugins/haskell/haskelleditorfactory.cpp
index 4053efeba2..ba87f88ef7 100644
--- a/src/plugins/haskell/haskelleditorfactory.cpp
+++ b/src/plugins/haskell/haskelleditorfactory.cpp
@@ -14,17 +14,14 @@
#include <texteditor/texteditoractionhandler.h>
#include <texteditor/textindenter.h>
-#include <QCoreApplication>
+namespace Haskell::Internal {
-namespace Haskell {
-namespace Internal {
-
-static QWidget *createEditorWidget()
+static QWidget *createEditorWidget(QObject *guard)
{
auto widget = new TextEditor::TextEditorWidget;
auto ghciButton = new Core::CommandButton(Constants::A_RUN_GHCI, widget);
ghciButton->setText(Tr::tr("GHCi"));
- QObject::connect(ghciButton, &QToolButton::clicked, HaskellManager::instance(), [widget] {
+ QObject::connect(ghciButton, &QToolButton::clicked, guard, [widget] {
HaskellManager::openGhci(widget->textDocument()->filePath());
});
widget->insertExtraToolBarWidget(TextEditor::TextEditorWidget::Left, ghciButton);
@@ -40,12 +37,11 @@ HaskellEditorFactory::HaskellEditorFactory()
| TextEditor::TextEditorActionHandler::FollowSymbolUnderCursor);
setDocumentCreator([] { return new TextEditor::TextDocument(Constants::C_HASKELLEDITOR_ID); });
setIndenterCreator([](QTextDocument *doc) { return new TextEditor::TextIndenter(doc); });
- setEditorWidgetCreator(createEditorWidget);
+ setEditorWidgetCreator([this] { return createEditorWidget(this); });
setCommentDefinition(Utils::CommentDefinition("--", "{-", "-}"));
setParenthesesMatchingEnabled(true);
setMarksVisible(true);
setSyntaxHighlighterCreator([] { return new HaskellHighlighter(); });
}
-} // Internal
-} // Haskell
+} // Haskell::Internal
diff --git a/src/plugins/haskell/haskelleditorfactory.h b/src/plugins/haskell/haskelleditorfactory.h
index c91f875df2..090a5b1278 100644
--- a/src/plugins/haskell/haskelleditorfactory.h
+++ b/src/plugins/haskell/haskelleditorfactory.h
@@ -5,8 +5,7 @@
#include <texteditor/texteditor.h>
-namespace Haskell {
-namespace Internal {
+namespace Haskell::Internal {
class HaskellEditorFactory : public TextEditor::TextEditorFactory
{
@@ -14,5 +13,4 @@ public:
HaskellEditorFactory();
};
-} // Internal
-} // Haskell
+} // Haskell::Internal
diff --git a/src/plugins/haskell/haskellmanager.cpp b/src/plugins/haskell/haskellmanager.cpp
index ac3564f23e..cc91ae7f81 100644
--- a/src/plugins/haskell/haskellmanager.cpp
+++ b/src/plugins/haskell/haskellmanager.cpp
@@ -3,43 +3,22 @@
#include "haskellmanager.h"
+#include "haskellsettings.h"
#include "haskelltr.h"
-#include <coreplugin/messagemanager.h>
#include <utils/algorithm.h>
#include <utils/commandline.h>
#include <utils/hostosinfo.h>
#include <utils/mimeutils.h>
+#include <utils/process.h>
#include <utils/processenums.h>
-#include <utils/qtcprocess.h>
-#include <QCoreApplication>
#include <QDir>
#include <QFileInfo>
-#include <QSettings>
-
-#include <unordered_map>
-
-static const char kStackExecutableKey[] = "Haskell/StackExecutable";
using namespace Utils;
-namespace Haskell {
-namespace Internal {
-
-class HaskellManagerPrivate
-{
-public:
- FilePath stackExecutable;
-};
-
-Q_GLOBAL_STATIC(HaskellManagerPrivate, m_d)
-Q_GLOBAL_STATIC(HaskellManager, m_instance)
-
-HaskellManager *HaskellManager::instance()
-{
- return m_instance;
-}
+namespace Haskell::Internal {
FilePath HaskellManager::findProjectDirectory(const FilePath &filePath)
{
@@ -57,28 +36,6 @@ FilePath HaskellManager::findProjectDirectory(const FilePath &filePath)
return {};
}
-FilePath defaultStackExecutable()
-{
- // stack from brew or the installer script from https://docs.haskellstack.org
- // install to /usr/local/bin.
- if (HostOsInfo::isAnyUnixHost())
- return FilePath::fromString("/usr/local/bin/stack");
- return FilePath::fromString("stack");
-}
-
-FilePath HaskellManager::stackExecutable()
-{
- return m_d->stackExecutable;
-}
-
-void HaskellManager::setStackExecutable(const FilePath &filePath)
-{
- if (filePath == m_d->stackExecutable)
- return;
- m_d->stackExecutable = filePath;
- emit m_instance->stackExecutableChanged(m_d->stackExecutable);
-}
-
void HaskellManager::openGhci(const FilePath &haskellFile)
{
const QList<MimeType> mimeTypes = mimeTypesForFileName(haskellFile.toString());
@@ -87,35 +44,11 @@ void HaskellManager::openGhci(const FilePath &haskellFile)
});
const auto args = QStringList{"ghci"}
+ (isHaskell ? QStringList{haskellFile.fileName()} : QStringList());
- auto p = new QtcProcess(m_instance);
- p->setTerminalMode(TerminalMode::On);
- p->setCommand({stackExecutable(), args});
- p->setWorkingDirectory(haskellFile.absolutePath());
- connect(p, &QtcProcess::done, p, [p] {
- if (p->result() != ProcessResult::FinishedWithSuccess) {
- Core::MessageManager::writeDisrupting(
- Tr::tr("Failed to run GHCi: \"%1\".").arg(p->errorString()));
- }
- p->deleteLater();
- });
- p->start();
-}
-
-void HaskellManager::readSettings(QSettings *settings)
-{
- m_d->stackExecutable = FilePath::fromString(
- settings->value(kStackExecutableKey,
- defaultStackExecutable().toString()).toString());
- emit m_instance->stackExecutableChanged(m_d->stackExecutable);
-}
-
-void HaskellManager::writeSettings(QSettings *settings)
-{
- if (m_d->stackExecutable == defaultStackExecutable())
- settings->remove(kStackExecutableKey);
- else
- settings->setValue(kStackExecutableKey, m_d->stackExecutable.toString());
+ Process p;
+ p.setTerminalMode(TerminalMode::Detached);
+ p.setCommand({settings().stackPath(), args});
+ p.setWorkingDirectory(haskellFile.absolutePath());
+ p.start();
}
-} // namespace Internal
-} // namespace Haskell
+} // Haskell::Internal
diff --git a/src/plugins/haskell/haskellmanager.h b/src/plugins/haskell/haskellmanager.h
index da4018930d..862f46460f 100644
--- a/src/plugins/haskell/haskellmanager.h
+++ b/src/plugins/haskell/haskellmanager.h
@@ -7,30 +7,15 @@
#include <utils/fileutils.h>
-QT_BEGIN_NAMESPACE
-class QSettings;
-QT_END_NAMESPACE
+namespace Haskell::Internal {
-namespace Haskell {
-namespace Internal {
-
-class HaskellManager : public QObject
+class HaskellManager
{
- Q_OBJECT
-
public:
static HaskellManager *instance();
static Utils::FilePath findProjectDirectory(const Utils::FilePath &filePath);
- static Utils::FilePath stackExecutable();
- static void setStackExecutable(const Utils::FilePath &filePath);
static void openGhci(const Utils::FilePath &haskellFile);
- static void readSettings(QSettings *settings);
- static void writeSettings(QSettings *settings);
-
-signals:
- void stackExecutableChanged(const Utils::FilePath &filePath);
};
-} // namespace Internal
-} // namespace Haskell
+} // Haskell::Internal
diff --git a/src/plugins/haskell/haskellplugin.cpp b/src/plugins/haskell/haskellplugin.cpp
index 334c104911..84a86f7db4 100644
--- a/src/plugins/haskell/haskellplugin.cpp
+++ b/src/plugins/haskell/haskellplugin.cpp
@@ -9,8 +9,8 @@
#include "haskellmanager.h"
#include "haskellproject.h"
#include "haskellrunconfiguration.h"
+#include "haskellsettings.h"
#include "haskelltr.h"
-#include "optionspage.h"
#include "stackbuildstep.h"
#include <coreplugin/actionmanager/actionmanager.h>
@@ -28,8 +28,8 @@ namespace Internal {
class HaskellPluginPrivate
{
public:
+ HaskellSettings settings;
HaskellEditorFactory editorFactory;
- OptionsPage optionsPage;
HaskellBuildConfigurationFactory buildConfigFactory;
StackBuildStepFactory stackBuildStepFactory;
HaskellRunConfigurationFactory runConfigFactory;
@@ -41,11 +41,11 @@ HaskellPlugin::~HaskellPlugin()
delete d;
}
-static void registerGhciAction()
+static void registerGhciAction(QObject *guard)
{
- QAction *action = new QAction(Tr::tr("Run GHCi"), HaskellManager::instance());
+ QAction *action = new QAction(Tr::tr("Run GHCi"), guard);
Core::ActionManager::registerAction(action, Constants::A_RUN_GHCI);
- QObject::connect(action, &QAction::triggered, HaskellManager::instance(), [] {
+ QObject::connect(action, &QAction::triggered, guard, [] {
if (Core::IDocument *doc = Core::EditorManager::currentDocument())
HaskellManager::openGhci(doc->filePath());
});
@@ -63,13 +63,7 @@ bool HaskellPlugin::initialize(const QStringList &arguments, QString *errorStrin
TextEditor::SnippetProvider::registerGroup(Constants::C_HASKELLSNIPPETSGROUP_ID,
Tr::tr("Haskell", "SnippetProvider"));
- connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested, this, [] {
- HaskellManager::writeSettings(Core::ICore::settings());
- });
-
- registerGhciAction();
-
- HaskellManager::readSettings(Core::ICore::settings());
+ registerGhciAction(this);
ProjectExplorer::JsonWizardFactory::addWizardPath(":/haskell/share/wizards/");
return true;
diff --git a/src/plugins/haskell/haskellproject.cpp b/src/plugins/haskell/haskellproject.cpp
index 916632c5dc..f19840a67f 100644
--- a/src/plugins/haskell/haskellproject.cpp
+++ b/src/plugins/haskell/haskellproject.cpp
@@ -14,7 +14,6 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <QFile>
#include <QTextStream>
diff --git a/src/plugins/haskell/haskellrunconfiguration.cpp b/src/plugins/haskell/haskellrunconfiguration.cpp
index 45b6e684bc..76d25e557c 100644
--- a/src/plugins/haskell/haskellrunconfiguration.cpp
+++ b/src/plugins/haskell/haskellrunconfiguration.cpp
@@ -4,12 +4,11 @@
#include "haskellrunconfiguration.h"
#include "haskellconstants.h"
-#include "haskellmanager.h"
#include "haskellproject.h"
#include "haskelltr.h"
+#include "haskellsettings.h"
#include <projectexplorer/buildconfiguration.h>
-#include <projectexplorer/localenvironmentaspect.h>
#include <projectexplorer/runconfigurationaspects.h>
#include <projectexplorer/runcontrol.h>
#include <projectexplorer/target.h>
@@ -35,7 +34,8 @@ HaskellExecutableAspect::HaskellExecutableAspect()
HaskellRunConfiguration::HaskellRunConfiguration(Target *target, Utils::Id id)
: RunConfiguration(target, id)
{
- auto envAspect = addAspect<LocalEnvironmentAspect>(target);
+ auto envAspect = addAspect<EnvironmentAspect>();
+ envAspect->setSupportForBuildEnvironment(target);
addAspect<HaskellExecutableAspect>();
addAspect<ArgumentsAspect>(macroExpander());
@@ -67,8 +67,8 @@ Runnable HaskellRunConfiguration::runnable() const
args << "--" << arguments;
r.workingDirectory = projectDirectory;
- r.environment = aspect<LocalEnvironmentAspect>()->environment();
- r.command = {r.environment.searchInPath(HaskellManager::stackExecutable().toString()), args};
+ r.environment = aspect<EnvironmentAspect>()->environment();
+ r.command = {r.environment.searchInPath(settings().stackPath().path()), args};
return r;
}
diff --git a/src/plugins/haskell/haskellsettings.cpp b/src/plugins/haskell/haskellsettings.cpp
new file mode 100644
index 0000000000..7b2bbc7fd3
--- /dev/null
+++ b/src/plugins/haskell/haskellsettings.cpp
@@ -0,0 +1,58 @@
+// Copyright (c) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "haskellsettings.h"
+
+#include "haskellconstants.h"
+#include "haskelltr.h"
+
+#include <utils/hostosinfo.h>
+#include <utils/layoutbuilder.h>
+
+using namespace Utils;
+
+namespace Haskell::Internal {
+
+static HaskellSettings *theSettings;
+
+HaskellSettings &settings()
+{
+ return *theSettings;
+}
+
+HaskellSettings::HaskellSettings()
+{
+ theSettings = this;
+
+ setId(Constants::OPTIONS_GENERAL);
+ setDisplayName(Tr::tr("General"));
+ setCategory("J.Z.Haskell");
+ setDisplayCategory(Tr::tr("Haskell"));
+ setCategoryIconPath(":/haskell/images/settingscategory_haskell.png");
+
+ stackPath.setSettingsKey("Haskell/StackExecutable");
+ stackPath.setExpectedKind(PathChooser::ExistingCommand);
+ stackPath.setPromptDialogTitle(Tr::tr("Choose Stack Executable"));
+ stackPath.setCommandVersionArguments({"--version"});
+
+ // stack from brew or the installer script from https://docs.haskellstack.org
+ // install to /usr/local/bin.
+ stackPath.setDefaultFilePath(HostOsInfo::isAnyUnixHost()
+ ? FilePath::fromString("/usr/local/bin/stack")
+ : FilePath::fromString("stack"));
+
+ setLayouter([this] {
+ using namespace Layouting;
+ return Column {
+ Group {
+ title(Tr::tr("General")),
+ Row { Tr::tr("Stack executable:"), stackPath }
+ },
+ st,
+ };
+ });
+
+ readSettings();
+}
+
+} // Haskell::Internal
diff --git a/src/plugins/haskell/haskellsettings.h b/src/plugins/haskell/haskellsettings.h
new file mode 100644
index 0000000000..1331e5aa89
--- /dev/null
+++ b/src/plugins/haskell/haskellsettings.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <coreplugin/dialogs/ioptionspage.h>
+
+namespace Haskell::Internal {
+
+class HaskellSettings : public Core::PagedSettings
+{
+public:
+ HaskellSettings();
+
+ Utils::FilePathAspect stackPath{this};
+};
+
+HaskellSettings &settings();
+
+} // Haskell::Internal
diff --git a/src/plugins/haskell/images/category_haskell.png b/src/plugins/haskell/images/category_haskell.png
deleted file mode 100644
index 947f948bd9..0000000000
--- a/src/plugins/haskell/images/category_haskell.png
+++ /dev/null
Binary files differ
diff --git a/src/plugins/haskell/images/settingscategory_haskell.png b/src/plugins/haskell/images/settingscategory_haskell.png
new file mode 100644
index 0000000000..5bd3f69fa8
--- /dev/null
+++ b/src/plugins/haskell/images/settingscategory_haskell.png
Binary files differ
diff --git a/src/plugins/haskell/images/settingscategory_haskell@2x.png b/src/plugins/haskell/images/settingscategory_haskell@2x.png
new file mode 100644
index 0000000000..1eb20959d5
--- /dev/null
+++ b/src/plugins/haskell/images/settingscategory_haskell@2x.png
Binary files differ
diff --git a/src/plugins/haskell/optionspage.cpp b/src/plugins/haskell/optionspage.cpp
deleted file mode 100644
index e64a601234..0000000000
--- a/src/plugins/haskell/optionspage.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (c) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "optionspage.h"
-
-#include "haskellconstants.h"
-#include "haskellmanager.h"
-#include "haskelltr.h"
-
-#include <QGroupBox>
-#include <QHBoxLayout>
-#include <QLabel>
-#include <QVBoxLayout>
-#include <QWidget>
-
-namespace Haskell {
-namespace Internal {
-
-OptionsPage::OptionsPage()
-{
- setId(Constants::OPTIONS_GENERAL);
- setDisplayName(Tr::tr("General"));
- setCategory("J.Z.Haskell");
- setDisplayCategory(Tr::tr("Haskell"));
- setCategoryIcon(Utils::Icon(":/haskell/images/category_haskell.png"));
-}
-
-QWidget *OptionsPage::widget()
-{
- using namespace Utils;
- if (!m_widget) {
- m_widget = new QWidget;
- auto topLayout = new QVBoxLayout;
- m_widget->setLayout(topLayout);
- auto generalBox = new QGroupBox(Tr::tr("General"));
- topLayout->addWidget(generalBox);
- topLayout->addStretch(10);
- auto boxLayout = new QHBoxLayout;
- generalBox->setLayout(boxLayout);
- boxLayout->addWidget(new QLabel(Tr::tr("Stack executable:")));
- m_stackPath = new PathChooser();
- m_stackPath->setExpectedKind(PathChooser::ExistingCommand);
- m_stackPath->setPromptDialogTitle(Tr::tr("Choose Stack Executable"));
- m_stackPath->setFilePath(HaskellManager::stackExecutable());
- m_stackPath->setCommandVersionArguments({"--version"});
- boxLayout->addWidget(m_stackPath);
- }
- return m_widget;
-}
-
-void OptionsPage::apply()
-{
- if (!m_widget)
- return;
- HaskellManager::setStackExecutable(m_stackPath->rawFilePath());
-}
-
-void OptionsPage::finish()
-{
-}
-
-} // namespace Internal
-} // namespace Haskell
diff --git a/src/plugins/haskell/optionspage.h b/src/plugins/haskell/optionspage.h
deleted file mode 100644
index a477d72dfc..0000000000
--- a/src/plugins/haskell/optionspage.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (c) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/pathchooser.h>
-
-#include <QPointer>
-
-namespace Haskell {
-namespace Internal {
-
-class OptionsPage : public Core::IOptionsPage
-{
- Q_OBJECT
-
-public:
- OptionsPage();
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-private:
- QPointer<QWidget> m_widget;
- QPointer<Utils::PathChooser> m_stackPath;
-};
-
-} // namespace Internal
-} // namespace Haskell
diff --git a/src/plugins/haskell/stackbuildstep.cpp b/src/plugins/haskell/stackbuildstep.cpp
index 084ba8af4a..4006ae9daa 100644
--- a/src/plugins/haskell/stackbuildstep.cpp
+++ b/src/plugins/haskell/stackbuildstep.cpp
@@ -4,7 +4,7 @@
#include "stackbuildstep.h"
#include "haskellconstants.h"
-#include "haskellmanager.h"
+#include "haskellsettings.h"
#include "haskelltr.h"
#include <projectexplorer/buildconfiguration.h>
@@ -38,7 +38,7 @@ bool StackBuildStep::init()
if (AbstractProcessStep::init()) {
const auto projectDir = QDir(project()->projectDirectory().toString());
processParameters()->setCommandLine(
- {HaskellManager::stackExecutable(),
+ {settings().stackPath(),
{"build", "--work-dir", projectDir.relativeFilePath(buildDirectory().toString())}});
processParameters()->setEnvironment(buildEnvironment());
}
diff --git a/src/plugins/help/CMakeLists.txt b/src/plugins/help/CMakeLists.txt
index c37b5b0c31..3034781739 100644
--- a/src/plugins/help/CMakeLists.txt
+++ b/src/plugins/help/CMakeLists.txt
@@ -12,7 +12,6 @@ add_qtc_plugin(Help
helpfindsupport.cpp helpfindsupport.h
helpindexfilter.cpp helpindexfilter.h
helpmanager.cpp helpmanager.h
- helpmode.cpp helpmode.h
helpplugin.cpp helpplugin.h
helptr.h
helpviewer.cpp helpviewer.h
@@ -47,7 +46,7 @@ extend_qtc_plugin(Help
)
option(BUILD_HELPVIEWERBACKEND_QTWEBENGINE "Build QtWebEngine based help viewer backend." YES)
-find_package(Qt5 COMPONENTS WebEngineWidgets QUIET)
+find_package(Qt6 COMPONENTS WebEngineWidgets QUIET)
extend_qtc_plugin(Help
CONDITION BUILD_HELPVIEWERBACKEND_QTWEBENGINE AND TARGET Qt::WebEngineWidgets
FEATURE_INFO "QtWebEngine help viewer"
diff --git a/src/plugins/help/filtersettingspage.cpp b/src/plugins/help/filtersettingspage.cpp
index 771c39fc68..e227b025ef 100644
--- a/src/plugins/help/filtersettingspage.cpp
+++ b/src/plugins/help/filtersettingspage.cpp
@@ -7,56 +7,56 @@
#include "helptr.h"
#include "localhelpmanager.h"
+#include <utils/layoutbuilder.h>
+
#include <QHelpFilterEngine>
#include <QHelpFilterSettingsWidget>
#include <QVersionNumber>
-using namespace Help::Internal;
-
-FilterSettingsPage::FilterSettingsPage()
-{
- setId("D.Filters");
- setDisplayName(Tr::tr("Filters"));
- setCategory(Help::Constants::HELP_CATEGORY);
-}
+namespace Help::Internal {
-QWidget *FilterSettingsPage::widget()
+class FilterSettingsPageWidget : public Core::IOptionsPageWidget
{
- if (!m_widget) {
+public:
+ FilterSettingsPageWidget(const std::function<void()> &onChanged)
+ {
LocalHelpManager::setupGuiHelpEngine();
- m_widget = new QHelpFilterSettingsWidget(nullptr);
- m_widget->readSettings(LocalHelpManager::filterEngine());
- connect(Core::HelpManager::Signals::instance(),
- &Core::HelpManager::Signals::documentationChanged,
- this,
- &FilterSettingsPage::updateFilterPage);
+ auto widget = new QHelpFilterSettingsWidget;
+ widget->readSettings(LocalHelpManager::filterEngine());
- updateFilterPage();
- }
- return m_widget;
-}
+ Layouting::Column {
+ Layouting::noMargin,
+ widget
+ }.attachTo(this);
-void FilterSettingsPage::apply()
-{
- if (m_widget->applySettings(LocalHelpManager::filterEngine()))
- emit filtersChanged();
+ auto updateFilterPage = [widget] {
+ widget->setAvailableComponents(LocalHelpManager::filterEngine()->availableComponents());
+ widget->setAvailableVersions(LocalHelpManager::filterEngine()->availableVersions());
+ };
- m_widget->readSettings(LocalHelpManager::filterEngine());
-}
+ auto connection = connect(Core::HelpManager::Signals::instance(),
+ &Core::HelpManager::Signals::documentationChanged,
+ this,
+ updateFilterPage);
+ updateFilterPage();
-void FilterSettingsPage::finish()
-{
- disconnect(Core::HelpManager::Signals::instance(),
- &Core::HelpManager::Signals::documentationChanged,
- this,
- &FilterSettingsPage::updateFilterPage);
- delete m_widget;
-}
+ setOnApply([widget, onChanged] {
+ if (widget->applySettings(LocalHelpManager::filterEngine()))
+ onChanged();
+ widget->readSettings(LocalHelpManager::filterEngine());
+ });
-void FilterSettingsPage::updateFilterPage()
+ setOnFinish([connection] { disconnect(connection); });
+ }
+};
+
+FilterSettingsPage::FilterSettingsPage(const std::function<void ()> &onChanged)
{
- m_widget->setAvailableComponents(LocalHelpManager::filterEngine()->availableComponents());
- m_widget->setAvailableVersions(LocalHelpManager::filterEngine()->availableVersions());
+ setId("D.Filters");
+ setDisplayName(Tr::tr("Filters"));
+ setCategory(Help::Constants::HELP_CATEGORY);
+ setWidgetCreator([onChanged] { return new FilterSettingsPageWidget(onChanged); });
}
+} // Help::Internal
diff --git a/src/plugins/help/filtersettingspage.h b/src/plugins/help/filtersettingspage.h
index 308a489f26..54256b1d7f 100644
--- a/src/plugins/help/filtersettingspage.h
+++ b/src/plugins/help/filtersettingspage.h
@@ -5,35 +5,12 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <QPointer>
-
-QT_BEGIN_NAMESPACE
-class QHelpFilterSettingsWidget;
-QT_END_NAMESPACE
-
-namespace Help {
-namespace Internal {
+namespace Help::Internal {
class FilterSettingsPage : public Core::IOptionsPage
{
- Q_OBJECT
-
public:
- FilterSettingsPage();
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-signals:
- void filtersChanged();
-
-private:
-
- void updateFilterPage();
- QPointer<QHelpFilterSettingsWidget> m_widget;
-
+ explicit FilterSettingsPage(const std::function<void()> &onChanged);
};
-} // namespace Help
-} // namespace Internal
+} // Help::Internal
diff --git a/src/plugins/help/generalsettingspage.cpp b/src/plugins/help/generalsettingspage.cpp
index a05a0db3ee..d5f5756ed6 100644
--- a/src/plugins/help/generalsettingspage.cpp
+++ b/src/plugins/help/generalsettingspage.cpp
@@ -23,6 +23,7 @@
#include <QComboBox>
#include <QCoreApplication>
#include <QFontComboBox>
+#include <QFontDatabase>
#include <QFormLayout>
#include <QGridLayout>
#include <QGroupBox>
@@ -35,19 +36,42 @@
#include <QTextStream>
#include <QVBoxLayout>
-#include <QApplication>
-
using namespace Core;
using namespace Utils;
-namespace Help {
-namespace Internal {
+namespace Help::Internal {
-class GeneralSettingsPageWidget : public QWidget
+class GeneralSettingsPageWidget : public IOptionsPageWidget
{
public:
GeneralSettingsPageWidget();
+private:
+ void apply() final;
+
+ void setCurrentPage();
+ void setBlankPage();
+ void setDefaultPage();
+ void importBookmarks();
+ void exportBookmarks();
+
+ void updateFontSizeSelector();
+ void updateFontStyleSelector();
+ void updateFontFamilySelector();
+ void updateFont();
+ int closestPointSizeIndex(int desiredPointSize) const;
+
+ QFont m_font;
+ int m_fontZoom = 100;
+ QFontDatabase m_fontDatabase;
+
+ QString m_homePage;
+ int m_contextOption;
+
+ int m_startOption;
+ bool m_returnOnClose;
+ bool m_scrollWheelZoomingEnabled;
+
QSpinBox *zoomSpinBox;
QFontComboBox *familyComboBox;
QComboBox *styleComboBox;
@@ -62,7 +86,7 @@ public:
QPushButton *importButton;
QPushButton *exportButton;
QCheckBox *scrollWheelZooming;
- QCheckBox *m_returnOnClose;
+ QCheckBox *m_returnOnCloseCheckBox;
QComboBox *viewerBackend;
};
@@ -152,8 +176,8 @@ GeneralSettingsPageWidget::GeneralSettingsPageWidget()
auto behaviourGroupBox = new QGroupBox(Tr::tr("Behaviour"));
scrollWheelZooming = new QCheckBox(Tr::tr("Enable scroll wheel zooming"));
- m_returnOnClose = new QCheckBox(Tr::tr("Return to editor on closing the last page"));
- m_returnOnClose->setToolTip(
+ m_returnOnCloseCheckBox = new QCheckBox(Tr::tr("Return to editor on closing the last page"));
+ m_returnOnCloseCheckBox->setToolTip(
Tr::tr("Switches to editor context after last help page is closed."));
auto viewerBackendLabel = new QLabel(Tr::tr("Viewer backend:"));
@@ -172,7 +196,7 @@ GeneralSettingsPageWidget::GeneralSettingsPageWidget()
auto behaviourGroupBoxLayout = new QVBoxLayout;
behaviourGroupBox->setLayout(behaviourGroupBoxLayout);
behaviourGroupBoxLayout->addWidget(scrollWheelZooming);
- behaviourGroupBoxLayout->addWidget(m_returnOnClose);
+ behaviourGroupBoxLayout->addWidget(m_returnOnCloseCheckBox);
behaviourGroupBoxLayout->addLayout(viewerBackendLayout);
// bookmarks
@@ -203,167 +227,145 @@ GeneralSettingsPageWidget::GeneralSettingsPageWidget()
mainLayout->addWidget(behaviourGroupBox);
mainLayout->addLayout(bookmarksLayout);
mainLayout->addStretch(1);
-}
-GeneralSettingsPage::GeneralSettingsPage()
-{
- setId("A.General settings");
- setDisplayName(Tr::tr("General"));
- setCategory(Help::Constants::HELP_CATEGORY);
- setDisplayCategory(Tr::tr("Help"));
- setCategoryIconPath(":/help/images/settingscategory_help.png");
-}
+ m_font = LocalHelpManager::fallbackFont();
+ m_fontZoom = LocalHelpManager::fontZoom();
+ zoomSpinBox->setValue(m_fontZoom);
-QWidget *GeneralSettingsPage::widget()
-{
- if (!m_widget) {
- m_widget = new GeneralSettingsPageWidget;
+ updateFontSizeSelector();
+ updateFontStyleSelector();
+ updateFontFamilySelector();
- m_font = LocalHelpManager::fallbackFont();
- m_fontZoom = LocalHelpManager::fontZoom();
- m_widget->zoomSpinBox->setValue(m_fontZoom);
+ connect(familyComboBox, &QFontComboBox::currentFontChanged, this, [this] {
+ updateFont();
+ updateFontStyleSelector();
+ updateFontSizeSelector();
+ updateFont(); // changes that might have happened when updating the selectors
+ });
+ connect(styleComboBox, &QComboBox::currentIndexChanged, this, [this] {
+ updateFont();
updateFontSizeSelector();
- updateFontStyleSelector();
- updateFontFamilySelector();
-
- connect(m_widget->familyComboBox, &QFontComboBox::currentFontChanged, this, [this] {
- updateFont();
- updateFontStyleSelector();
- updateFontSizeSelector();
- updateFont(); // changes that might have happened when updating the selectors
- });
-
- connect(m_widget->styleComboBox, &QComboBox::currentIndexChanged, this, [this] {
- updateFont();
- updateFontSizeSelector();
- updateFont(); // changes that might have happened when updating the selectors
- });
-
- connect(m_widget->sizeComboBox, &QComboBox::currentIndexChanged,
- this, &GeneralSettingsPage::updateFont);
-
- connect(m_widget->zoomSpinBox, &QSpinBox::valueChanged,
- this, [this](int value) { m_fontZoom = value; });
-
- m_homePage = LocalHelpManager::homePage();
- m_widget->homePageLineEdit->setText(m_homePage);
-
- m_startOption = LocalHelpManager::startOption();
- m_widget->helpStartComboBox->setCurrentIndex(m_startOption);
-
- m_contextOption = LocalHelpManager::contextHelpOption();
- m_widget->contextHelpComboBox->setCurrentIndex(m_contextOption);
-
- connect(m_widget->currentPageButton, &QPushButton::clicked,
- this, &GeneralSettingsPage::setCurrentPage);
- connect(m_widget->blankPageButton, &QPushButton::clicked,
- this, &GeneralSettingsPage::setBlankPage);
- connect(m_widget->defaultPageButton,
- &QPushButton::clicked,
- this,
- &GeneralSettingsPage::setDefaultPage);
-
- HelpViewer *viewer = HelpPlugin::modeHelpWidget()->currentViewer();
- if (!viewer)
- m_widget->currentPageButton->setEnabled(false);
-
- m_widget->errorLabel->setVisible(false);
- connect(m_widget->importButton, &QPushButton::clicked,
- this, &GeneralSettingsPage::importBookmarks);
- connect(m_widget->exportButton, &QPushButton::clicked,
- this, &GeneralSettingsPage::exportBookmarks);
-
- m_returnOnClose = LocalHelpManager::returnOnClose();
- m_widget->m_returnOnClose->setChecked(m_returnOnClose);
-
- m_scrollWheelZoomingEnabled = LocalHelpManager::isScrollWheelZoomingEnabled();
- m_widget->scrollWheelZooming->setChecked(m_scrollWheelZoomingEnabled);
-
- m_widget->viewerBackend->addItem(
- Tr::tr("Default (%1)", "Default viewer backend")
- .arg(LocalHelpManager::defaultViewerBackend().displayName));
- const QByteArray currentBackend = LocalHelpManager::viewerBackendId();
- const QVector<HelpViewerFactory> backends = LocalHelpManager::viewerBackends();
- for (const HelpViewerFactory &f : backends) {
- m_widget->viewerBackend->addItem(f.displayName, f.id);
- if (f.id == currentBackend)
- m_widget->viewerBackend->setCurrentIndex(m_widget->viewerBackend->count() - 1);
- }
- if (backends.size() == 1)
- m_widget->viewerBackend->setEnabled(false);
+ updateFont(); // changes that might have happened when updating the selectors
+ });
+
+ connect(sizeComboBox, &QComboBox::currentIndexChanged,
+ this, &GeneralSettingsPageWidget::updateFont);
+
+ connect(zoomSpinBox, &QSpinBox::valueChanged,
+ this, [this](int value) { m_fontZoom = value; });
+
+ m_homePage = LocalHelpManager::homePage();
+ homePageLineEdit->setText(m_homePage);
+
+ m_startOption = LocalHelpManager::startOption();
+ helpStartComboBox->setCurrentIndex(m_startOption);
+
+ m_contextOption = LocalHelpManager::contextHelpOption();
+ contextHelpComboBox->setCurrentIndex(m_contextOption);
+
+ connect(currentPageButton, &QPushButton::clicked,
+ this, &GeneralSettingsPageWidget::setCurrentPage);
+ connect(blankPageButton, &QPushButton::clicked,
+ this, &GeneralSettingsPageWidget::setBlankPage);
+ connect(defaultPageButton, &QPushButton::clicked,
+ this, &GeneralSettingsPageWidget::setDefaultPage);
+
+ HelpViewer *viewer = HelpPlugin::modeHelpWidget()->currentViewer();
+ if (!viewer)
+ currentPageButton->setEnabled(false);
+
+ errorLabel->setVisible(false);
+ connect(importButton, &QPushButton::clicked,
+ this, &GeneralSettingsPageWidget::importBookmarks);
+ connect(exportButton, &QPushButton::clicked,
+ this, &GeneralSettingsPageWidget::exportBookmarks);
+
+ m_returnOnClose = LocalHelpManager::returnOnClose();
+ m_returnOnCloseCheckBox->setChecked(m_returnOnClose);
+
+ m_scrollWheelZoomingEnabled = LocalHelpManager::isScrollWheelZoomingEnabled();
+ scrollWheelZooming->setChecked(m_scrollWheelZoomingEnabled);
+
+ viewerBackend->addItem(
+ Tr::tr("Default (%1)", "Default viewer backend")
+ .arg(LocalHelpManager::defaultViewerBackend().displayName));
+ const QByteArray currentBackend = LocalHelpManager::viewerBackendId();
+ const QVector<HelpViewerFactory> backends = LocalHelpManager::viewerBackends();
+ for (const HelpViewerFactory &f : backends) {
+ viewerBackend->addItem(f.displayName, f.id);
+ if (f.id == currentBackend)
+ viewerBackend->setCurrentIndex(viewerBackend->count() - 1);
}
- return m_widget;
+ if (backends.size() == 1)
+ viewerBackend->setEnabled(false);
}
-void GeneralSettingsPage::apply()
+void GeneralSettingsPageWidget::apply()
{
- if (!m_widget) // page was never shown
- return;
-
if (m_font != LocalHelpManager::fallbackFont())
LocalHelpManager::setFallbackFont(m_font);
if (m_fontZoom != LocalHelpManager::fontZoom())
LocalHelpManager::setFontZoom(m_fontZoom);
- QString homePage = QUrl::fromUserInput(m_widget->homePageLineEdit->text()).toString();
+ QString homePage = QUrl::fromUserInput(homePageLineEdit->text()).toString();
if (homePage.isEmpty())
homePage = Help::Constants::AboutBlank;
- m_widget->homePageLineEdit->setText(homePage);
+ homePageLineEdit->setText(homePage);
if (m_homePage != homePage) {
m_homePage = homePage;
LocalHelpManager::setHomePage(homePage);
}
- const int startOption = m_widget->helpStartComboBox->currentIndex();
+ const int startOption = helpStartComboBox->currentIndex();
if (m_startOption != startOption) {
m_startOption = startOption;
LocalHelpManager::setStartOption(LocalHelpManager::StartOption(m_startOption));
}
- const int helpOption = m_widget->contextHelpComboBox->currentIndex();
+ const int helpOption = contextHelpComboBox->currentIndex();
if (m_contextOption != helpOption) {
m_contextOption = helpOption;
LocalHelpManager::setContextHelpOption(HelpManager::HelpViewerLocation(m_contextOption));
}
- const bool close = m_widget->m_returnOnClose->isChecked();
+ const bool close = m_returnOnCloseCheckBox->isChecked();
if (m_returnOnClose != close) {
m_returnOnClose = close;
LocalHelpManager::setReturnOnClose(m_returnOnClose);
}
- const bool zoom = m_widget->scrollWheelZooming->isChecked();
+ const bool zoom = scrollWheelZooming->isChecked();
if (m_scrollWheelZoomingEnabled != zoom) {
m_scrollWheelZoomingEnabled = zoom;
LocalHelpManager::setScrollWheelZoomingEnabled(m_scrollWheelZoomingEnabled);
}
- const QByteArray viewerBackendId = m_widget->viewerBackend->currentData().toByteArray();
+ const QByteArray viewerBackendId = viewerBackend->currentData().toByteArray();
LocalHelpManager::setViewerBackendId(viewerBackendId);
}
-void GeneralSettingsPage::setCurrentPage()
+void GeneralSettingsPageWidget::setCurrentPage()
{
HelpViewer *viewer = HelpPlugin::modeHelpWidget()->currentViewer();
if (viewer)
- m_widget->homePageLineEdit->setText(viewer->source().toString());
+ homePageLineEdit->setText(viewer->source().toString());
}
-void GeneralSettingsPage::setBlankPage()
+void GeneralSettingsPageWidget::setBlankPage()
{
- m_widget->homePageLineEdit->setText(Help::Constants::AboutBlank);
+ homePageLineEdit->setText(Help::Constants::AboutBlank);
}
-void GeneralSettingsPage::setDefaultPage()
+void GeneralSettingsPageWidget::setDefaultPage()
{
- m_widget->homePageLineEdit->setText(LocalHelpManager::defaultHomePage());
+ homePageLineEdit->setText(LocalHelpManager::defaultHomePage());
}
-void GeneralSettingsPage::importBookmarks()
+void GeneralSettingsPageWidget::importBookmarks()
{
- m_widget->errorLabel->setVisible(false);
+ errorLabel->setVisible(false);
FilePath filePath = FileUtils::getOpenFilePath(nullptr,
Tr::tr("Import Bookmarks"),
@@ -381,13 +383,13 @@ void GeneralSettingsPage::importBookmarks()
return;
}
- m_widget->errorLabel->setVisible(true);
- m_widget->errorLabel->setText(Tr::tr("Cannot import bookmarks."));
+ errorLabel->setVisible(true);
+ errorLabel->setText(Tr::tr("Cannot import bookmarks."));
}
-void GeneralSettingsPage::exportBookmarks()
+void GeneralSettingsPageWidget::exportBookmarks()
{
- m_widget->errorLabel->setVisible(false);
+ errorLabel->setVisible(false);
FilePath filePath = FileUtils::getSaveFilePath(nullptr,
Tr::tr("Save File"),
@@ -405,12 +407,12 @@ void GeneralSettingsPage::exportBookmarks()
saver.setResult(&writer);
}
if (!saver.finalize()) {
- m_widget->errorLabel->setVisible(true);
- m_widget->errorLabel->setText(saver.errorString());
+ errorLabel->setVisible(true);
+ errorLabel->setText(saver.errorString());
}
}
-void GeneralSettingsPage::updateFontSizeSelector()
+void GeneralSettingsPageWidget::updateFontSizeSelector()
{
const QString &family = m_font.family();
const QString &fontStyle = m_fontDatabase.styleString(m_font);
@@ -419,81 +421,81 @@ void GeneralSettingsPage::updateFontSizeSelector()
if (pointSizes.empty())
pointSizes = QFontDatabase::standardSizes();
- QSignalBlocker blocker(m_widget->sizeComboBox);
- m_widget->sizeComboBox->clear();
- m_widget->sizeComboBox->setCurrentIndex(-1);
- m_widget->sizeComboBox->setEnabled(!pointSizes.empty());
+ QSignalBlocker blocker(sizeComboBox);
+ sizeComboBox->clear();
+ sizeComboBox->setCurrentIndex(-1);
+ sizeComboBox->setEnabled(!pointSizes.empty());
// try to maintain selection or select closest.
if (!pointSizes.empty()) {
QString n;
for (int pointSize : std::as_const(pointSizes))
- m_widget->sizeComboBox->addItem(n.setNum(pointSize), QVariant(pointSize));
+ sizeComboBox->addItem(n.setNum(pointSize), QVariant(pointSize));
const int closestIndex = closestPointSizeIndex(m_font.pointSize());
if (closestIndex != -1)
- m_widget->sizeComboBox->setCurrentIndex(closestIndex);
+ sizeComboBox->setCurrentIndex(closestIndex);
}
}
-void GeneralSettingsPage::updateFontStyleSelector()
+void GeneralSettingsPageWidget::updateFontStyleSelector()
{
const QString &fontStyle = m_fontDatabase.styleString(m_font);
const QStringList &styles = m_fontDatabase.styles(m_font.family());
- QSignalBlocker blocker(m_widget->styleComboBox);
- m_widget->styleComboBox->clear();
- m_widget->styleComboBox->setCurrentIndex(-1);
- m_widget->styleComboBox->setEnabled(!styles.empty());
+ QSignalBlocker blocker(styleComboBox);
+ styleComboBox->clear();
+ styleComboBox->setCurrentIndex(-1);
+ styleComboBox->setEnabled(!styles.empty());
if (!styles.empty()) {
int normalIndex = -1;
const QString normalStyle = "Normal";
for (const QString &style : styles) {
// try to maintain selection or select 'normal' preferably
- const int newIndex = m_widget->styleComboBox->count();
- m_widget->styleComboBox->addItem(style);
+ const int newIndex = styleComboBox->count();
+ styleComboBox->addItem(style);
if (fontStyle == style) {
- m_widget->styleComboBox->setCurrentIndex(newIndex);
+ styleComboBox->setCurrentIndex(newIndex);
} else {
if (fontStyle == normalStyle)
normalIndex = newIndex;
}
}
- if (m_widget->styleComboBox->currentIndex() == -1 && normalIndex != -1)
- m_widget->styleComboBox->setCurrentIndex(normalIndex);
+ if (styleComboBox->currentIndex() == -1 && normalIndex != -1)
+ styleComboBox->setCurrentIndex(normalIndex);
}
}
-void GeneralSettingsPage::updateFontFamilySelector()
+void GeneralSettingsPageWidget::updateFontFamilySelector()
{
- m_widget->familyComboBox->setCurrentFont(m_font);
+ familyComboBox->setCurrentFont(m_font);
}
-void GeneralSettingsPage::updateFont()
+void GeneralSettingsPageWidget::updateFont()
{
- const QString &family = m_widget->familyComboBox->currentFont().family();
+ const QString &family = familyComboBox->currentFont().family();
m_font.setFamily(family);
int fontSize = 14;
- int currentIndex = m_widget->sizeComboBox->currentIndex();
+ int currentIndex = sizeComboBox->currentIndex();
if (currentIndex != -1)
- fontSize = m_widget->sizeComboBox->itemData(currentIndex).toInt();
+ fontSize = sizeComboBox->itemData(currentIndex).toInt();
m_font.setPointSize(fontSize);
- currentIndex = m_widget->styleComboBox->currentIndex();
+ currentIndex = styleComboBox->currentIndex();
if (currentIndex != -1)
- m_font.setStyleName(m_widget->styleComboBox->itemText(currentIndex));
+ m_font.setStyleName(styleComboBox->itemText(currentIndex));
}
-int GeneralSettingsPage::closestPointSizeIndex(int desiredPointSize) const
+int GeneralSettingsPageWidget::closestPointSizeIndex(int desiredPointSize) const
{
// try to maintain selection or select closest.
int closestIndex = -1;
int closestAbsError = 0xFFFF;
- const int pointSizeCount = m_widget->sizeComboBox->count();
+ const int pointSizeCount = sizeComboBox->count();
for (int i = 0; i < pointSizeCount; i++) {
- const int itemPointSize = m_widget->sizeComboBox->itemData(i).toInt();
+ const int itemPointSize = sizeComboBox->itemData(i).toInt();
const int absError = qAbs(desiredPointSize - itemPointSize);
if (absError < closestAbsError) {
closestIndex = i;
@@ -508,10 +510,16 @@ int GeneralSettingsPage::closestPointSizeIndex(int desiredPointSize) const
return closestIndex;
}
-void GeneralSettingsPage::finish()
+// GeneralSettingPage
+
+GeneralSettingsPage::GeneralSettingsPage()
{
- delete m_widget;
+ setId("A.General settings");
+ setDisplayName(Tr::tr("General"));
+ setCategory(Help::Constants::HELP_CATEGORY);
+ setDisplayCategory(Tr::tr("Help"));
+ setCategoryIconPath(":/help/images/settingscategory_help.png");
+ setWidgetCreator([] { return new GeneralSettingsPageWidget; });
}
-} // Internal
-} // Help
+} // Help::Interal
diff --git a/src/plugins/help/generalsettingspage.h b/src/plugins/help/generalsettingspage.h
index 4965cac9ea..ff5f6313ac 100644
--- a/src/plugins/help/generalsettingspage.h
+++ b/src/plugins/help/generalsettingspage.h
@@ -5,51 +5,12 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <QFontDatabase>
-#include <QPointer>
-
-namespace Help {
-namespace Internal {
-
-class GeneralSettingsPageWidget;
+namespace Help::Internal {
class GeneralSettingsPage : public Core::IOptionsPage
{
- Q_OBJECT
-
public:
GeneralSettingsPage();
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-private:
- void setCurrentPage();
- void setBlankPage();
- void setDefaultPage();
- void importBookmarks();
- void exportBookmarks();
-
- void updateFontSizeSelector();
- void updateFontStyleSelector();
- void updateFontFamilySelector();
- void updateFont();
- int closestPointSizeIndex(int desiredPointSize) const;
-
- QFont m_font;
- int m_fontZoom = 100;
- QFontDatabase m_fontDatabase;
-
- QString m_homePage;
- int m_contextOption;
-
- int m_startOption;
- bool m_returnOnClose;
- bool m_scrollWheelZoomingEnabled;
-
- QPointer<GeneralSettingsPageWidget> m_widget;
};
- } // Internal
-} // Help
+} // Help::Internal
diff --git a/src/plugins/help/help.qbs b/src/plugins/help/help.qbs
index 25d85bdc37..d96cfa154e 100644
--- a/src/plugins/help/help.qbs
+++ b/src/plugins/help/help.qbs
@@ -43,7 +43,6 @@ Project {
"helpfindsupport.cpp", "helpfindsupport.h",
"helpindexfilter.cpp", "helpindexfilter.h",
"helpmanager.cpp", "helpmanager.h",
- "helpmode.cpp", "helpmode.h",
"helpplugin.cpp", "helpplugin.h",
"helpviewer.cpp", "helpviewer.h",
"helpwidget.cpp", "helpwidget.h",
diff --git a/src/plugins/help/helpindexfilter.cpp b/src/plugins/help/helpindexfilter.cpp
index fe1c34a48e..9da1921480 100644
--- a/src/plugins/help/helpindexfilter.cpp
+++ b/src/plugins/help/helpindexfilter.cpp
@@ -3,30 +3,34 @@
#include "helpindexfilter.h"
+#include "localhelpmanager.h"
#include "helpmanager.h"
+#include "helpplugin.h"
#include "helptr.h"
-#include "localhelpmanager.h"
-#include <coreplugin/icore.h>
#include <coreplugin/helpmanager.h>
+#include <coreplugin/icore.h>
#include <extensionsystem/pluginmanager.h>
+#include <utils/async.h>
#include <utils/utilsicons.h>
#include <QHelpEngine>
#include <QHelpFilterEngine>
#include <QHelpLink>
-#include <QIcon>
using namespace Core;
using namespace Help;
using namespace Help::Internal;
+using namespace Tasking;
+using namespace Utils;
HelpIndexFilter::HelpIndexFilter()
{
setId("HelpIndexFilter");
setDisplayName(Tr::tr("Help Index"));
- setDefaultIncludedByDefault(false);
+ setDescription(Tr::tr("Locates help topics, for example in the Qt documentation."));
setDefaultShortcutString("?");
+ setRefreshRecipe(Sync([this] { invalidateCache(); }));
m_icon = Utils::Icons::BOOKMARK.icon();
connect(Core::HelpManager::Signals::instance(), &Core::HelpManager::Signals::setupFinished,
@@ -39,82 +43,69 @@ HelpIndexFilter::HelpIndexFilter()
this, &HelpIndexFilter::invalidateCache);
}
-HelpIndexFilter::~HelpIndexFilter() = default;
-
-bool HelpIndexFilter::updateCache(QFutureInterface<LocatorFilterEntry> &future,
- const QStringList &cache, const QString &entry)
+static void matches(QPromise<QStringList> &promise, const LocatorStorage &storage,
+ const QStringList &cache, const QIcon &icon)
{
- const Qt::CaseSensitivity cs = caseSensitivity(entry);
+ const QString input = storage.input();
+ const Qt::CaseSensitivity cs = ILocatorFilter::caseSensitivity(input);
QStringList bestKeywords;
QStringList worseKeywords;
bestKeywords.reserve(cache.size());
worseKeywords.reserve(cache.size());
for (const QString &keyword : cache) {
- if (future.isCanceled())
- return false;
- if (keyword.startsWith(entry, cs))
+ if (promise.isCanceled())
+ return;
+ if (keyword.startsWith(input, cs))
bestKeywords.append(keyword);
- else if (keyword.contains(entry, cs))
+ else if (keyword.contains(input, cs))
worseKeywords.append(keyword);
}
- bestKeywords << worseKeywords;
- m_lastIndicesCache = bestKeywords;
- m_lastEntry = entry;
-
- return true;
-}
-
-QList<LocatorFilterEntry> HelpIndexFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future, const QString &entry)
-{
- if (m_needsUpdate.exchange(false)) {
- QStringList indices;
- QMetaObject::invokeMethod(this, [this] { return allIndices(); },
- Qt::BlockingQueuedConnection, &indices);
- m_allIndicesCache = indices;
- // force updating the cache taking the m_allIndicesCache
- m_lastIndicesCache = QStringList();
- m_lastEntry = QString();
- }
-
- const QStringList cacheBase = m_lastEntry.isEmpty() || !entry.contains(m_lastEntry)
- ? m_allIndicesCache : m_lastIndicesCache;
-
- if (!updateCache(future, cacheBase, entry))
- return QList<LocatorFilterEntry>();
-
- const Qt::CaseSensitivity cs = caseSensitivity(entry);
- QList<LocatorFilterEntry> entries;
- for (const QString &keyword : std::as_const(m_lastIndicesCache)) {
- const int index = keyword.indexOf(entry, 0, cs);
- LocatorFilterEntry filterEntry(this, keyword, {}, m_icon);
- filterEntry.highlightInfo = {index, int(entry.length())};
+ const QStringList lastIndicesCache = bestKeywords + worseKeywords;
+
+ LocatorFilterEntries entries;
+ for (const QString &key : lastIndicesCache) {
+ if (promise.isCanceled())
+ return;
+ const int index = key.indexOf(input, 0, cs);
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = key;
+ filterEntry.acceptor = [key] {
+ HelpPlugin::showLinksInCurrentViewer(LocalHelpManager::linksForKeyword(key), key);
+ return AcceptResult();
+ };
+ filterEntry.displayIcon = icon;
+ filterEntry.highlightInfo = {index, int(input.length())};
entries.append(filterEntry);
}
-
- return entries;
-}
-
-void HelpIndexFilter::accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const
-{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- const QString &key = selection.displayName;
- const QMultiMap<QString, QUrl> links = LocalHelpManager::linksForKeyword(key);
- emit linksActivated(links, key);
-}
-
-void HelpIndexFilter::refresh(QFutureInterface<void> &future)
-{
- Q_UNUSED(future)
- invalidateCache();
+ storage.reportOutput(entries);
+ promise.addResult(lastIndicesCache);
}
-QStringList HelpIndexFilter::allIndices() const
+LocatorMatcherTasks HelpIndexFilter::matchers()
{
- LocalHelpManager::setupGuiHelpEngine();
- return LocalHelpManager::filterEngine()->indices(QString());
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [this, storage](Async<QStringList> &async) {
+ if (m_needsUpdate) {
+ m_needsUpdate = false;
+ LocalHelpManager::setupGuiHelpEngine();
+ m_allIndicesCache = LocalHelpManager::filterEngine()->indices({});
+ m_lastIndicesCache.clear();
+ m_lastEntry.clear();
+ }
+ const QStringList cache = m_lastEntry.isEmpty() || !storage->input().contains(m_lastEntry)
+ ? m_allIndicesCache : m_lastIndicesCache;
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(matches, *storage, cache, m_icon);
+ };
+ const auto onDone = [this, storage](const Async<QStringList> &async) {
+ if (async.isResultAvailable()) {
+ m_lastIndicesCache = async.result();
+ m_lastEntry = storage->input();
+ }
+ };
+
+ return {{AsyncTask<QStringList>(onSetup, onDone), storage}};
}
void HelpIndexFilter::invalidateCache()
diff --git a/src/plugins/help/helpindexfilter.h b/src/plugins/help/helpindexfilter.h
index 1dfc6f5995..e980f49ab0 100644
--- a/src/plugins/help/helpindexfilter.h
+++ b/src/plugins/help/helpindexfilter.h
@@ -5,48 +5,22 @@
#include <coreplugin/locator/ilocatorfilter.h>
-#include <QIcon>
-#include <QMultiMap>
-#include <QSet>
-#include <QUrl>
-
-#include <atomic>
-
-namespace Help {
-namespace Internal {
+namespace Help::Internal {
class HelpIndexFilter final : public Core::ILocatorFilter
{
- Q_OBJECT
-
public:
HelpIndexFilter();
- ~HelpIndexFilter() final;
-
- // ILocatorFilter
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
- void refresh(QFutureInterface<void> &future) override;
-
- QStringList allIndices() const;
-
-signals:
- void linksActivated(const QMultiMap<QString, QUrl> &links, const QString &key) const;
private:
+ Core::LocatorMatcherTasks matchers() final;
void invalidateCache();
- bool updateCache(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QStringList &cache, const QString &entry);
-
QStringList m_allIndicesCache;
QStringList m_lastIndicesCache;
QString m_lastEntry;
- std::atomic_bool m_needsUpdate = true;
+ bool m_needsUpdate = true;
QIcon m_icon;
};
-} // namespace Internal
-} // namespace Help
+} // namespace Help::Internal
diff --git a/src/plugins/help/helpmanager.cpp b/src/plugins/help/helpmanager.cpp
index c3e0bd3151..3c053662d5 100644
--- a/src/plugins/help/helpmanager.cpp
+++ b/src/plugins/help/helpmanager.cpp
@@ -7,16 +7,18 @@
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
+
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/filesystemwatcher.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <QDateTime>
#include <QDebug>
#include <QDesktopServices>
#include <QDir>
#include <QFileInfo>
+#include <QPromise>
#include <QStringList>
#include <QUrl>
@@ -99,7 +101,7 @@ void HelpManager::registerDocumentation(const QStringList &files)
return;
}
- QFuture<bool> future = Utils::runAsync(&HelpManager::registerDocumentationNow, files);
+ QFuture<bool> future = Utils::asyncRun(&HelpManager::registerDocumentationNow, files);
Utils::onResultReady(future, this, [](bool docsChanged){
if (docsChanged) {
d->m_helpEngine->setupData();
@@ -122,13 +124,12 @@ void HelpManager::unregisterDocumentation(const QStringList &fileNames)
unregisterNamespaces(getNamespaces(fileNames));
}
-void HelpManager::registerDocumentationNow(QFutureInterface<bool> &futureInterface,
- const QStringList &files)
+void HelpManager::registerDocumentationNow(QPromise<bool> &promise, const QStringList &files)
{
QMutexLocker locker(&d->m_helpengineMutex);
- futureInterface.setProgressRange(0, files.count());
- futureInterface.setProgressValue(0);
+ promise.setProgressRange(0, files.count());
+ promise.setProgressValue(0);
QHelpEngineCore helpEngine(collectionFilePath());
helpEngine.setReadOnly(false);
@@ -136,9 +137,9 @@ void HelpManager::registerDocumentationNow(QFutureInterface<bool> &futureInterfa
bool docsChanged = false;
QStringList nameSpaces = helpEngine.registeredDocumentations();
for (const QString &file : files) {
- if (futureInterface.isCanceled())
+ if (promise.isCanceled())
break;
- futureInterface.setProgressValue(futureInterface.progressValue() + 1);
+ promise.setProgressValue(promise.future().progressValue() + 1);
const QString &nameSpace = QHelpEngineCore::namespaceName(file);
if (nameSpace.isEmpty())
continue;
@@ -152,7 +153,7 @@ void HelpManager::registerDocumentationNow(QFutureInterface<bool> &futureInterfa
}
}
}
- futureInterface.reportResult(docsChanged);
+ promise.addResult(docsChanged);
}
void HelpManager::unregisterNamespaces(const QStringList &nameSpaces)
diff --git a/src/plugins/help/helpmanager.h b/src/plugins/help/helpmanager.h
index d868e96256..739d5a6876 100644
--- a/src/plugins/help/helpmanager.h
+++ b/src/plugins/help/helpmanager.h
@@ -5,12 +5,15 @@
#include <coreplugin/helpmanager_implementation.h>
-QT_FORWARD_DECLARE_CLASS(QUrl)
-
-#include <QFutureInterface>
#include <QHelpEngineCore>
#include <QVariant>
+QT_BEGIN_NAMESPACE
+template <typename T>
+class QPromise;
+class QUrl;
+QT_END_NAMESPACE
+
namespace Help {
namespace Internal {
@@ -55,10 +58,9 @@ public:
const QUrl &url,
Core::HelpManager::HelpViewerLocation location = Core::HelpManager::HelpModeAlways) override;
-
static void setupHelpManager();
- static void registerDocumentationNow(QFutureInterface<bool> &futureInterface,
- const QStringList &fileNames);
+ static void registerDocumentationNow(QPromise<bool> &promise, const QStringList &fileNames);
+
signals:
void collectionFileChanged();
void helpRequested(const QUrl &url, Core::HelpManager::HelpViewerLocation location);
diff --git a/src/plugins/help/helpmode.cpp b/src/plugins/help/helpmode.cpp
deleted file mode 100644
index 2e2ee46893..0000000000
--- a/src/plugins/help/helpmode.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "helpmode.h"
-#include "helpconstants.h"
-#include "helpicons.h"
-#include "helptr.h"
-
-#include <QCoreApplication>
-
-using namespace Help;
-using namespace Help::Internal;
-
-HelpMode::HelpMode(QObject *parent)
- : Core::IMode(parent)
-{
- setObjectName("HelpMode");
- setContext(Core::Context(Constants::C_MODE_HELP));
- setIcon(Utils::Icon::modeIcon(Icons::MODE_HELP_CLASSIC,
- Icons::MODE_HELP_FLAT, Icons::MODE_HELP_FLAT_ACTIVE));
- setDisplayName(Tr::tr("Help"));
- setPriority(Constants::P_MODE_HELP);
- setId(Constants::ID_MODE_HELP);
-}
diff --git a/src/plugins/help/helpmode.h b/src/plugins/help/helpmode.h
deleted file mode 100644
index fbe69f08bc..0000000000
--- a/src/plugins/help/helpmode.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <coreplugin/imode.h>
-
-#include <QString>
-#include <QIcon>
-
-namespace Help {
-namespace Internal {
-
-class HelpMode : public Core::IMode
-{
-public:
- explicit HelpMode(QObject *parent = nullptr);
-};
-
-} // namespace Internal
-} // namespace Help
diff --git a/src/plugins/help/helpplugin.cpp b/src/plugins/help/helpplugin.cpp
index 5612637247..7329b3dad6 100644
--- a/src/plugins/help/helpplugin.cpp
+++ b/src/plugins/help/helpplugin.cpp
@@ -4,7 +4,6 @@
#include "helpplugin.h"
#include "bookmarkmanager.h"
-#include "contentwindow.h"
#include "docsettingspage.h"
#include "filtersettingspage.h"
#include "generalsettingspage.h"
@@ -13,11 +12,9 @@
#include "helpicons.h"
#include "helpindexfilter.h"
#include "helpmanager.h"
-#include "helpmode.h"
#include "helptr.h"
#include "helpviewer.h"
#include "helpwidget.h"
-#include "indexwindow.h"
#include "localhelpmanager.h"
#include "openpagesmanager.h"
#include "searchtaskhandler.h"
@@ -35,6 +32,7 @@
#include <coreplugin/findplaceholder.h>
#include <coreplugin/helpitem.h>
#include <coreplugin/icore.h>
+#include <coreplugin/imode.h>
#include <coreplugin/minisplitter.h>
#include <coreplugin/modemanager.h>
#include <coreplugin/rightpane.h>
@@ -52,45 +50,54 @@
#include <utils/theme/theme.h>
#include <utils/tooltip/tooltip.h>
+#include <QAction>
#include <QApplication>
+#include <QComboBox>
+#include <QDesktopServices>
#include <QDialog>
#include <QDialogButtonBox>
-#include <QDir>
-#include <QFileInfo>
+#include <QHelpEngine>
#include <QLabel>
#include <QLibraryInfo>
+#include <QMenu>
#include <QPlainTextEdit>
-#include <QTimer>
-#include <QTranslator>
-#include <qplugin.h>
#include <QRegularExpression>
-
-#include <QAction>
-#include <QComboBox>
-#include <QDesktopServices>
-#include <QMenu>
-#include <QStackedLayout>
#include <QSplitter>
-
-#include <QHelpEngine>
+#include <QStackedLayout>
+#include <QTimer>
+#include <QTranslator>
#include <functional>
-static const char kExternalWindowStateKey[] = "Help/ExternalWindowState";
-static const char kToolTipHelpContext[] = "Help.ToolTip";
-
using namespace Core;
using namespace Utils;
-namespace Help {
-namespace Internal {
+namespace Help::Internal {
+
+const char kExternalWindowStateKey[] = "Help/ExternalWindowState";
+const char kToolTipHelpContext[] = "Help.ToolTip";
+
+class HelpMode : public IMode
+{
+public:
+ HelpMode()
+ {
+ setObjectName("HelpMode");
+ setContext(Core::Context(Constants::C_MODE_HELP));
+ setIcon(Icon::modeIcon(Icons::MODE_HELP_CLASSIC,
+ Icons::MODE_HELP_FLAT, Icons::MODE_HELP_FLAT_ACTIVE));
+ setDisplayName(Tr::tr("Help"));
+ setPriority(Constants::P_MODE_HELP);
+ setId(Constants::ID_MODE_HELP);
+ }
+};
class HelpPluginPrivate : public QObject
{
public:
HelpPluginPrivate();
- void modeChanged(Utils::Id mode, Utils::Id old);
+ void modeChanged(Id mode, Id old);
void requestContextHelp();
void showContextHelp(const HelpItem &contextHelp);
@@ -127,7 +134,7 @@ public:
QRect m_externalWindowState;
DocSettingsPage m_docSettingsPage;
- FilterSettingsPage m_filterSettingsPage;
+ FilterSettingsPage m_filterSettingsPage{[this] {setupHelpEngineIfNeeded(); }};
SearchTaskHandler m_searchTaskHandler;
GeneralSettingsPage m_generalSettingsPage;
@@ -158,6 +165,11 @@ void HelpPlugin::showHelpUrl(const QUrl &url, Core::HelpManager::HelpViewerLocat
dd->showHelpUrl(url, location);
}
+void HelpPlugin::showLinksInCurrentViewer(const QMultiMap<QString, QUrl> &links, const QString &key)
+{
+ dd->showLinksInCurrentViewer(links, key);
+}
+
void HelpPlugin::initialize()
{
dd = new HelpPluginPrivate;
@@ -185,8 +197,6 @@ HelpPluginPrivate::HelpPluginPrivate()
connect(&m_searchTaskHandler, &SearchTaskHandler::search,
this, &QDesktopServices::openUrl);
- connect(&m_filterSettingsPage, &FilterSettingsPage::filtersChanged,
- this, &HelpPluginPrivate::setupHelpEngineIfNeeded);
connect(Core::HelpManager::Signals::instance(),
&Core::HelpManager::Signals::documentationChanged,
this,
@@ -258,9 +268,6 @@ HelpPluginPrivate::HelpPluginPrivate()
ActionManager::actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_SUPPORT);
connect(action, &QAction::triggered, this, &HelpPluginPrivate::slotSystemInformation);
- connect(&helpIndexFilter, &HelpIndexFilter::linksActivated,
- this, &HelpPluginPrivate::showLinksInCurrentViewer);
-
connect(ModeManager::instance(), &ModeManager::currentModeChanged,
this, &HelpPluginPrivate::modeChanged);
@@ -394,7 +401,7 @@ void HelpPluginPrivate::showLinksInCurrentViewer(const QMultiMap<QString, QUrl>
widget->showLinks(links, key);
}
-void HelpPluginPrivate::modeChanged(Utils::Id mode, Utils::Id old)
+void HelpPluginPrivate::modeChanged(Id mode, Id old)
{
Q_UNUSED(old)
if (mode == m_mode.id()) {
@@ -494,7 +501,7 @@ HelpViewer *HelpPluginPrivate::viewerForContextHelp()
void HelpPluginPrivate::requestContextHelp()
{
// Find out what to show
- const QVariant tipHelpValue = Utils::ToolTip::contextHelp();
+ const QVariant tipHelpValue = ToolTip::contextHelp();
const HelpItem tipHelp = tipHelpValue.canConvert<HelpItem>()
? tipHelpValue.value<HelpItem>()
: HelpItem(tipHelpValue.toString());
@@ -641,5 +648,4 @@ void HelpPluginPrivate::doSetupIfNeeded()
}
}
-} // Internal
-} // Help
+} // Help::Internal
diff --git a/src/plugins/help/helpplugin.h b/src/plugins/help/helpplugin.h
index d3a3e0b980..96d14a152b 100644
--- a/src/plugins/help/helpplugin.h
+++ b/src/plugins/help/helpplugin.h
@@ -26,6 +26,8 @@ public:
~HelpPlugin() final;
static void showHelpUrl(const QUrl &url, Core::HelpManager::HelpViewerLocation location);
+ static void showLinksInCurrentViewer(const QMultiMap<QString, QUrl> &links,
+ const QString &key);
static HelpViewer *createHelpViewer();
static HelpWidget *modeHelpWidget();
diff --git a/src/plugins/help/helpwidget.cpp b/src/plugins/help/helpwidget.cpp
index 571964d0b3..67b42232c9 100644
--- a/src/plugins/help/helpwidget.cpp
+++ b/src/plugins/help/helpwidget.cpp
@@ -30,6 +30,7 @@
#include <texteditor/texteditorconstants.h>
#include <utils/qtcassert.h>
#include <utils/styledbar.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QComboBox>
@@ -319,7 +320,7 @@ HelpWidget::HelpWidget(const Core::Context &context, WidgetStyle style, QWidget
cmd = Core::ActionManager::registerAction(helpTargetAction, "Help.OpenContextHelpHere", context);
QToolButton *helpTargetButton = Core::Command::toolButtonWithAppendedShortcut(helpTargetAction,
cmd);
- helpTargetButton->setProperty("noArrow", true);
+ helpTargetButton->setProperty(Utils::StyleHelper::C_NO_ARROW, true);
helpTargetButton->setPopupMode(QToolButton::DelayedPopup);
helpTargetButton->setMenu(createHelpTargetMenu(helpTargetButton));
connect(LocalHelpManager::instance(),
@@ -416,7 +417,7 @@ HelpWidget::HelpWidget(const Core::Context &context, WidgetStyle style, QWidget
auto openButton = new QToolButton;
openButton->setIcon(Utils::Icons::SPLIT_HORIZONTAL_TOOLBAR.icon());
openButton->setPopupMode(QToolButton::InstantPopup);
- openButton->setProperty("noArrow", true);
+ openButton->setProperty(Utils::StyleHelper::C_NO_ARROW, true);
layout->addWidget(openButton);
auto openMenu = new QMenu(openButton);
openButton->setMenu(openMenu);
diff --git a/src/plugins/imageviewer/CMakeLists.txt b/src/plugins/imageviewer/CMakeLists.txt
index b55ef9931e..38659e8417 100644
--- a/src/plugins/imageviewer/CMakeLists.txt
+++ b/src/plugins/imageviewer/CMakeLists.txt
@@ -1,4 +1,4 @@
-find_package(Qt5 COMPONENTS SvgWidgets QUIET)
+find_package(Qt6 COMPONENTS SvgWidgets QUIET)
if (TARGET Qt::SvgWidgets)
set(SVG_WIDGETS Qt::SvgWidgets)
endif()
diff --git a/src/plugins/imageviewer/imageview.cpp b/src/plugins/imageviewer/imageview.cpp
index 34b74d4908..9a2af284f4 100644
--- a/src/plugins/imageviewer/imageview.cpp
+++ b/src/plugins/imageviewer/imageview.cpp
@@ -8,11 +8,11 @@
#include "imageviewerfile.h"
#include "imageviewertr.h"
#include "multiexportdialog.h"
-#include "utils/mimeutils.h"
#include <coreplugin/messagemanager.h>
#include <utils/fileutils.h>
+#include <utils/mimeutils.h>
#include <utils/qtcassert.h>
#include <utils/qtcsettings.h>
diff --git a/src/plugins/imageviewer/imageviewer.cpp b/src/plugins/imageviewer/imageviewer.cpp
index 9c19b0ffbf..5d4a068e71 100644
--- a/src/plugins/imageviewer/imageviewer.cpp
+++ b/src/plugins/imageviewer/imageviewer.cpp
@@ -17,8 +17,9 @@
#include <utils/filepath.h>
#include <utils/qtcassert.h>
-#include <utils/utilsicons.h>
#include <utils/styledbar.h>
+#include <utils/stylehelper.h>
+#include <utils/utilsicons.h>
#include <QAction>
#include <QDebug>
@@ -112,7 +113,7 @@ void ImageViewer::ctor()
d->shareButton->setToolTip(Tr::tr("Export"));
d->shareButton->setPopupMode(QToolButton::InstantPopup);
d->shareButton->setIcon(Icons::EXPORTFILE_TOOLBAR.icon());
- d->shareButton->setProperty("noArrow", true);
+ d->shareButton->setProperty(StyleHelper::C_NO_ARROW, true);
auto shareMenu = new QMenu(d->shareButton);
shareMenu->addAction(d->actionExportImage);
shareMenu->addAction(d->actionMultiExportImages);
diff --git a/src/plugins/incredibuild/CMakeLists.txt b/src/plugins/incredibuild/CMakeLists.txt
index 81e0273b95..f212046a15 100644
--- a/src/plugins/incredibuild/CMakeLists.txt
+++ b/src/plugins/incredibuild/CMakeLists.txt
@@ -13,10 +13,9 @@ add_qtc_plugin(IncrediBuild
ibconsolebuildstep.cpp
ibconsolebuildstep.h
incredibuild_global.h
- incredibuildtr.h
incredibuildconstants.h
incredibuildplugin.cpp
- incredibuildplugin.h
+ incredibuildtr.h
makecommandbuilder.cpp
makecommandbuilder.h
)
diff --git a/src/plugins/incredibuild/buildconsolebuildstep.cpp b/src/plugins/incredibuild/buildconsolebuildstep.cpp
index 1238b2c243..c28c1cd676 100644
--- a/src/plugins/incredibuild/buildconsolebuildstep.cpp
+++ b/src/plugins/incredibuild/buildconsolebuildstep.cpp
@@ -76,10 +76,9 @@ BuildConsoleBuildStep::BuildConsoleBuildStep(BuildStepList *buildStepList, Id id
addAspect<TextDisplay>("<b>" + Tr::tr("IncrediBuild Distribution Control"));
- auto profileXml = addAspect<StringAspect>();
+ auto profileXml = addAspect<FilePathAspect>();
profileXml->setSettingsKey("IncrediBuild.BuildConsole.ProfileXml");
profileXml->setLabelText(Tr::tr("Profile.xml:"));
- profileXml->setDisplayStyle(StringAspect::PathChooserDisplay);
profileXml->setExpectedKind(PathChooser::Kind::File);
profileXml->setBaseFileName(PathChooser::homePath());
profileXml->setHistoryCompleter("IncrediBuild.BuildConsole.ProfileXml.History");
@@ -138,10 +137,9 @@ BuildConsoleBuildStep::BuildConsoleBuildStep(BuildStepList *buildStepList, Id id
"beginning of the build output text. This title will also be used "
"for the Build History and Build Monitor displays."));
- auto monFile = addAspect<StringAspect>();
+ auto monFile = addAspect<FilePathAspect>();
monFile->setSettingsKey("IncrediBuild.BuildConsole.MonFile");
monFile->setLabelText(Tr::tr("Save IncrediBuild monitor file:"));
- monFile->setDisplayStyle(StringAspect::PathChooserDisplay);
monFile->setExpectedKind(PathChooser::Kind::Any);
monFile->setBaseFileName(PathChooser::homePath());
monFile->setHistoryCompleter(QLatin1String("IncrediBuild.BuildConsole.MonFile.History"));
@@ -155,10 +153,9 @@ BuildConsoleBuildStep::BuildConsoleBuildStep(BuildStepList *buildStepList, Id id
suppressStdOut->setLabel(Tr::tr("Suppress STDOUT:"));
suppressStdOut->setToolTip(Tr::tr("Does not write anything to the standard output."));
- auto logFile = addAspect<StringAspect>();
+ auto logFile = addAspect<FilePathAspect>();
logFile->setSettingsKey("IncrediBuild.BuildConsole.LogFile");
logFile->setLabelText(Tr::tr("Output Log file:"));
- logFile->setDisplayStyle(StringAspect::PathChooserDisplay);
logFile->setExpectedKind(PathChooser::Kind::SaveFile);
logFile->setBaseFileName(PathChooser::homePath());
logFile->setHistoryCompleter(QLatin1String("IncrediBuild.BuildConsole.LogFile.History"));
diff --git a/src/plugins/incredibuild/cmakecommandbuilder.cpp b/src/plugins/incredibuild/cmakecommandbuilder.cpp
index 8732601cbc..2f0dd751ae 100644
--- a/src/plugins/incredibuild/cmakecommandbuilder.cpp
+++ b/src/plugins/incredibuild/cmakecommandbuilder.cpp
@@ -8,7 +8,7 @@
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildsteplist.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <cmakeprojectmanager/cmakeprojectconstants.h> // Compile-time only
diff --git a/src/plugins/incredibuild/commandbuilderaspect.cpp b/src/plugins/incredibuild/commandbuilderaspect.cpp
index 6a16cabc6d..5cfbac953b 100644
--- a/src/plugins/incredibuild/commandbuilderaspect.cpp
+++ b/src/plugins/incredibuild/commandbuilderaspect.cpp
@@ -110,7 +110,7 @@ void CommandBuilderAspectPrivate::tryToMigrate()
}
}
-void CommandBuilderAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void CommandBuilderAspect::addToLayout(Layouting::LayoutItem &parent)
{
if (!d->commandBuilder) {
d->commandBuilder = new QComboBox;
@@ -151,9 +151,9 @@ void CommandBuilderAspect::addToLayout(Layouting::LayoutBuilder &builder)
if (!d->m_loadedFromMap)
d->tryToMigrate();
- builder.addRow({d->label.data(), d->commandBuilder.data()});
- builder.addRow({Tr::tr("Make command:"), d->makePathChooser.data()});
- builder.addRow({Tr::tr("Make arguments:"), d->makeArgumentsLineEdit.data()});
+ parent.addRow({d->label.data(), d->commandBuilder.data()});
+ parent.addRow({Tr::tr("Make command:"), d->makePathChooser.data()});
+ parent.addRow({Tr::tr("Make arguments:"), d->makeArgumentsLineEdit.data()});
updateGui();
}
diff --git a/src/plugins/incredibuild/commandbuilderaspect.h b/src/plugins/incredibuild/commandbuilderaspect.h
index 7c6d04c93b..6033e9036e 100644
--- a/src/plugins/incredibuild/commandbuilderaspect.h
+++ b/src/plugins/incredibuild/commandbuilderaspect.h
@@ -23,7 +23,7 @@ public:
QString fullCommandFlag(bool keepJobNum) const;
private:
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) final;
+ void addToLayout(Layouting::LayoutItem &parent) final;
void fromMap(const QVariantMap &map) final;
void toMap(QVariantMap &map) const final;
diff --git a/src/plugins/incredibuild/incredibuild.qbs b/src/plugins/incredibuild/incredibuild.qbs
index e08d548691..9c315ae8c0 100644
--- a/src/plugins/incredibuild/incredibuild.qbs
+++ b/src/plugins/incredibuild/incredibuild.qbs
@@ -19,10 +19,10 @@ QtcPlugin {
"commandbuilderaspect.h",
"ibconsolebuildstep.cpp",
"ibconsolebuildstep.h",
- "incredibuild_global.h", "incredibuildtr.h",
+ "incredibuild_global.h",
"incredibuildconstants.h",
"incredibuildplugin.cpp",
- "incredibuildplugin.h",
+ "incredibuildtr.h",
"makecommandbuilder.cpp",
"makecommandbuilder.h",
]
diff --git a/src/plugins/incredibuild/incredibuildplugin.cpp b/src/plugins/incredibuild/incredibuildplugin.cpp
index 6e7ea8a4bc..4e8f572812 100644
--- a/src/plugins/incredibuild/incredibuildplugin.cpp
+++ b/src/plugins/incredibuild/incredibuildplugin.cpp
@@ -1,28 +1,26 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "incredibuildplugin.h"
-
#include "buildconsolebuildstep.h"
#include "ibconsolebuildstep.h"
+#include <extensionsystem/iplugin.h>
+
namespace IncrediBuild::Internal {
-class IncrediBuildPluginPrivate
+class IncrediBuildPlugin final : public ExtensionSystem::IPlugin
{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "IncrediBuild.json")
+
public:
- BuildConsoleStepFactory buildConsoleStepFactory;
- IBConsoleStepFactory iBConsoleStepFactory;
+ IncrediBuildPlugin()
+ {
+ addManaged<BuildConsoleStepFactory>();
+ addManaged<IBConsoleStepFactory>();
+ }
};
-IncrediBuildPlugin::~IncrediBuildPlugin()
-{
- delete d;
-}
-
-void IncrediBuildPlugin::initialize()
-{
- d = new IncrediBuildPluginPrivate;
-}
-
} // IncrediBuild::Internal
+
+#include "incredibuildplugin.moc"
diff --git a/src/plugins/incredibuild/incredibuildplugin.h b/src/plugins/incredibuild/incredibuildplugin.h
deleted file mode 100644
index 994d65bb77..0000000000
--- a/src/plugins/incredibuild/incredibuildplugin.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <extensionsystem/iplugin.h>
-
-namespace IncrediBuild::Internal {
-
-class IncrediBuildPlugin final : public ExtensionSystem::IPlugin
-{
- Q_OBJECT
- Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "IncrediBuild.json")
-
-public:
- IncrediBuildPlugin() = default;
- ~IncrediBuildPlugin() final;
-
- void initialize() final;
-
-private:
- class IncrediBuildPluginPrivate *d = nullptr;
-};
-
-} // IncrediBuild::Internal
diff --git a/src/plugins/insight/insightmodel.cpp b/src/plugins/insight/insightmodel.cpp
index 4fe987a597..b80249d212 100644
--- a/src/plugins/insight/insightmodel.cpp
+++ b/src/plugins/insight/insightmodel.cpp
@@ -12,8 +12,8 @@
#include <qmldesignerplugin.h>
#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <qtsupport/qtkitinformation.h>
@@ -211,8 +211,8 @@ InsightModel::InsightModel(InsightView *view, ExternalDependenciesInterface &ext
, m_externalDependencies(externalDependencies)
, m_fileSystemWatcher(new Utils::FileSystemWatcher(this))
{
- QObject::connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::startupProjectChanged,
+ QObject::connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged,
this,
[&](ProjectExplorer::Project *project) {
if (project)
diff --git a/src/plugins/ios/createsimulatordialog.cpp b/src/plugins/ios/createsimulatordialog.cpp
index 784c33742f..aff8a673df 100644
--- a/src/plugins/ios/createsimulatordialog.cpp
+++ b/src/plugins/ios/createsimulatordialog.cpp
@@ -7,8 +7,8 @@
#include "simulatorcontrol.h"
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/layoutbuilder.h>
-#include <utils/runextensions.h>
#include <QApplication>
#include <QComboBox>
@@ -32,7 +32,7 @@ CreateSimulatorDialog::CreateSimulatorDialog(QWidget *parent)
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Form {
@@ -60,11 +60,10 @@ CreateSimulatorDialog::CreateSimulatorDialog(QWidget *parent)
enableOk();
});
- m_futureSync.setCancelOnWait(true);
- m_futureSync.addFuture(Utils::onResultReady(SimulatorControl::updateDeviceTypes(), this,
+ m_futureSync.addFuture(Utils::onResultReady(SimulatorControl::updateDeviceTypes(this), this,
&CreateSimulatorDialog::populateDeviceTypes));
- QFuture<QList<RuntimeInfo>> runtimesfuture = SimulatorControl::updateRuntimes();
+ QFuture<QList<RuntimeInfo>> runtimesfuture = SimulatorControl::updateRuntimes(this);
Utils::onResultReady(runtimesfuture, this, [this](const QList<RuntimeInfo> &runtimes) {
m_runtimes = runtimes;
});
diff --git a/src/plugins/ios/iosbuildstep.cpp b/src/plugins/ios/iosbuildstep.cpp
index eb6664c155..7831d84e23 100644
--- a/src/plugins/ios/iosbuildstep.cpp
+++ b/src/plugins/ios/iosbuildstep.cpp
@@ -20,8 +20,8 @@
#include <projectexplorer/toolchain.h>
#include <utils/filepath.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QGridLayout>
#include <QLabel>
@@ -101,7 +101,8 @@ QWidget *IosBuildStep::createConfigWidget()
updateDetails();
connect(buildArgumentsTextEdit, &QPlainTextEdit::textChanged, this, [=] {
- setBaseArguments(ProcessArgs::splitArgs(buildArgumentsTextEdit->toPlainText()));
+ setBaseArguments(ProcessArgs::splitArgs(buildArgumentsTextEdit->toPlainText(),
+ HostOsInfo::hostOs()));
resetDefaultsButton->setEnabled(!m_useDefaultArguments);
updateDetails();
});
@@ -113,7 +114,8 @@ QWidget *IosBuildStep::createConfigWidget()
});
connect(extraArgumentsLineEdit, &QLineEdit::editingFinished, this, [=] {
- setExtraArguments(ProcessArgs::splitArgs(extraArgumentsLineEdit->text()));
+ setExtraArguments(ProcessArgs::splitArgs(extraArgumentsLineEdit->text(),
+ HostOsInfo::hostOs()));
});
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::settingsChanged,
diff --git a/src/plugins/ios/iosconfigurations.cpp b/src/plugins/ios/iosconfigurations.cpp
index 54674de8ec..04e4363a2a 100644
--- a/src/plugins/ios/iosconfigurations.cpp
+++ b/src/plugins/ios/iosconfigurations.cpp
@@ -15,6 +15,7 @@
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/devicesupport/devicemanager.h>
+#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/toolchainmanager.h>
#include <projectexplorer/toolchain.h>
#include <projectexplorer/gcctoolchain.h>
@@ -30,8 +31,8 @@
#include <qtsupport/qtversionfactory.h>
#include <utils/algorithm.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDir>
#include <QDomDocument>
@@ -211,7 +212,7 @@ static QByteArray decodeProvisioningProfile(const QString &path)
{
QTC_ASSERT(!path.isEmpty(), return QByteArray());
- QtcProcess p;
+ Process p;
p.setTimeoutS(3);
// path is assumed to be valid file path to .mobileprovision
p.setCommand({"openssl", {"smime", "-inform", "der", "-verify", "-in", path}});
@@ -404,7 +405,7 @@ void IosConfigurations::updateSimulators()
dev = IDevice::ConstPtr(new IosSimulator(devId));
devManager->addDevice(dev);
}
- SimulatorControl::updateAvailableSimulators();
+ SimulatorControl::updateAvailableSimulators(this);
}
void IosConfigurations::setDeveloperPath(const FilePath &devPath)
@@ -573,7 +574,7 @@ IosToolChainFactory::IosToolChainFactory()
Toolchains IosToolChainFactory::autoDetect(const ToolchainDetector &detector) const
{
- if (detector.device)
+ if (detector.device->type() != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE)
return {};
QList<ClangToolChain *> existingClangToolChains = clangToolChains(detector.alreadyKnown);
diff --git a/src/plugins/ios/iosdevice.cpp b/src/plugins/ios/iosdevice.cpp
index 49a07a2b10..326092fccc 100644
--- a/src/plugins/ios/iosdevice.cpp
+++ b/src/plugins/ios/iosdevice.cpp
@@ -185,12 +185,6 @@ Utils::Port IosDevice::nextPort() const
return Utils::Port(m_lastPort);
}
-bool IosDevice::canAutoDetectPorts() const
-{
- return true;
-}
-
-
// IosDeviceManager
IosDeviceManager::TranslationMap IosDeviceManager::translationMap()
diff --git a/src/plugins/ios/iosdevice.h b/src/plugins/ios/iosdevice.h
index 9f4e66c502..ada952037d 100644
--- a/src/plugins/ios/iosdevice.h
+++ b/src/plugins/ios/iosdevice.h
@@ -36,7 +36,6 @@ public:
QString osVersion() const;
QString cpuArchitecture() const;
Utils::Port nextPort() const;
- bool canAutoDetectPorts() const override;
static QString name();
diff --git a/src/plugins/ios/iosdsymbuildstep.cpp b/src/plugins/ios/iosdsymbuildstep.cpp
index 54481ded9b..2d0e2b841f 100644
--- a/src/plugins/ios/iosdsymbuildstep.cpp
+++ b/src/plugins/ios/iosdsymbuildstep.cpp
@@ -9,21 +9,22 @@
#include "iostr.h"
#include <extensionsystem/pluginmanager.h>
-#include <projectexplorer/target.h>
-#include <projectexplorer/project.h>
+
+#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/processparameters.h>
+#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/target.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtparser.h>
-#include <utils/stringutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
+#include <utils/stringutils.h>
#include <QGridLayout>
#include <QLabel>
@@ -242,7 +243,8 @@ QWidget *IosDsymBuildStep::createConfigWidget()
connect(argumentsTextEdit, &QPlainTextEdit::textChanged, this,
[this, argumentsTextEdit, resetDefaultsButton, updateDetails] {
- setArguments(Utils::ProcessArgs::splitArgs(argumentsTextEdit->toPlainText()));
+ setArguments(ProcessArgs::splitArgs(argumentsTextEdit->toPlainText(),
+ HostOsInfo::hostOs()));
resetDefaultsButton->setEnabled(!isDefault());
updateDetails();
});
diff --git a/src/plugins/ios/iosprobe.cpp b/src/plugins/ios/iosprobe.cpp
index c835e48390..e6fca977b2 100644
--- a/src/plugins/ios/iosprobe.cpp
+++ b/src/plugins/ios/iosprobe.cpp
@@ -4,7 +4,7 @@
#include "iosprobe.h"
#include <utils/algorithm.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QFileInfo>
#include <QLoggingCategory>
@@ -40,7 +40,7 @@ void XcodeProbe::addDeveloperPath(const QString &path)
void XcodeProbe::detectDeveloperPaths()
{
- Utils::QtcProcess selectedXcode;
+ Utils::Process selectedXcode;
selectedXcode.setTimeoutS(5);
selectedXcode.setCommand({"/usr/bin/xcode-select", {"--print-path"}});
selectedXcode.runBlocking();
diff --git a/src/plugins/ios/iosrunconfiguration.cpp b/src/plugins/ios/iosrunconfiguration.cpp
index 9a9acedb29..8a0a3e62dc 100644
--- a/src/plugins/ios/iosrunconfiguration.cpp
+++ b/src/plugins/ios/iosrunconfiguration.cpp
@@ -21,9 +21,9 @@
#include <utils/algorithm.h>
#include <utils/filepath.h>
-#include <utils/qtcprocess.h>
-#include <utils/qtcassert.h>
#include <utils/layoutbuilder.h>
+#include <utils/process.h>
+#include <utils/qtcassert.h>
#include <QAction>
#include <QApplication>
@@ -324,14 +324,14 @@ IosDeviceTypeAspect::IosDeviceTypeAspect(IosRunConfiguration *runConfiguration)
this, &IosDeviceTypeAspect::deviceChanges);
}
-void IosDeviceTypeAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void IosDeviceTypeAspect::addToLayout(Layouting::LayoutItem &parent)
{
m_deviceTypeComboBox = new QComboBox;
m_deviceTypeComboBox->setModel(&m_deviceTypeModel);
m_deviceTypeLabel = new QLabel(Tr::tr("Device type:"));
- builder.addItems({m_deviceTypeLabel, m_deviceTypeComboBox});
+ parent.addItems({m_deviceTypeLabel, m_deviceTypeComboBox});
updateValues();
diff --git a/src/plugins/ios/iosrunconfiguration.h b/src/plugins/ios/iosrunconfiguration.h
index 161b0703c5..1ebe79efa5 100644
--- a/src/plugins/ios/iosrunconfiguration.h
+++ b/src/plugins/ios/iosrunconfiguration.h
@@ -27,7 +27,7 @@ public:
void fromMap(const QVariantMap &map) override;
void toMap(QVariantMap &map) const override;
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
IosDeviceType deviceType() const;
void setDeviceType(const IosDeviceType &deviceType);
diff --git a/src/plugins/ios/iosrunner.cpp b/src/plugins/ios/iosrunner.cpp
index c01c3c859d..828bb65c00 100644
--- a/src/plugins/ios/iosrunner.cpp
+++ b/src/plugins/ios/iosrunner.cpp
@@ -27,7 +27,7 @@
#include <qmldebug/qmloutputparser.h>
#include <utils/fileutils.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/url.h>
#include <utils/utilsicons.h>
diff --git a/src/plugins/ios/iossettingswidget.cpp b/src/plugins/ios/iossettingswidget.cpp
index 8c4149a91b..bb0bc36d61 100644
--- a/src/plugins/ios/iossettingswidget.cpp
+++ b/src/plugins/ios/iossettingswidget.cpp
@@ -12,9 +12,9 @@
#include "simulatoroperationdialog.h"
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/runextensions.h>
#include <QCheckBox>
#include <QDateTime>
@@ -51,7 +51,6 @@ static void onSimOperation(const SimulatorInfo &simInfo, SimulatorOperationDialo
IosSettingsWidget::IosSettingsWidget()
{
- resize(622, 456);
setWindowTitle(Tr::tr("iOS Configuration"));
m_deviceAskCheckBox = new QCheckBox(Tr::tr("Ask about devices not in developer mode"));
@@ -94,7 +93,7 @@ IosSettingsWidget::IosSettingsWidget()
m_pathWidget->addButton(Tr::tr("Screenshot"), this,
std::bind(&IosSettingsWidget::onScreenshot, this));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
title(Tr::tr("Devices")),
@@ -175,7 +174,7 @@ void IosSettingsWidget::onStart()
Utils::StdErrFormat);
} else {
futureList << QFuture<void>(Utils::onResultReady(
- SimulatorControl::startSimulator(info.identifier),
+ SimulatorControl::startSimulator(info.identifier), this,
std::bind(onSimOperation, info, statusDialog, Tr::tr("simulator start"), _1)));
}
}
@@ -207,11 +206,9 @@ void IosSettingsWidget::onCreate()
CreateSimulatorDialog createDialog(this);
if (createDialog.exec() == QDialog::Accepted) {
- QFuture<void> f = QFuture<void>(
- Utils::onResultReady(SimulatorControl::createSimulator(createDialog.name(),
- createDialog.deviceType(),
- createDialog.runtime()),
- std::bind(onSimulatorCreate, createDialog.name(), _1)));
+ QFuture<void> f = QFuture<void>(Utils::onResultReady(SimulatorControl::createSimulator(
+ createDialog.name(), createDialog.deviceType(), createDialog.runtime()),
+ this, std::bind(onSimulatorCreate, createDialog.name(), _1)));
statusDialog->addFutures({ f });
statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled.
}
@@ -242,7 +239,7 @@ void IosSettingsWidget::onReset()
QList<QFuture<void>> futureList;
for (const SimulatorInfo &info : simulatorInfoList) {
futureList << QFuture<void>(Utils::onResultReady(
- SimulatorControl::resetSimulator(info.identifier),
+ SimulatorControl::resetSimulator(info.identifier), this,
std::bind(onSimOperation, info, statusDialog, Tr::tr("simulator reset"), _1)));
}
@@ -270,7 +267,7 @@ void IosSettingsWidget::onRename()
statusDialog->setAttribute(Qt::WA_DeleteOnClose);
statusDialog->addMessage(Tr::tr("Renaming simulator device..."), Utils::NormalMessageFormat);
QFuture<void> f = QFuture<void>(Utils::onResultReady(
- SimulatorControl::renameSimulator(simInfo.identifier, newName),
+ SimulatorControl::renameSimulator(simInfo.identifier, newName), this,
std::bind(onSimOperation, simInfo, statusDialog, Tr::tr("simulator rename"), _1)));
statusDialog->addFutures({f});
statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled.
@@ -300,7 +297,7 @@ void IosSettingsWidget::onDelete()
QList<QFuture<void>> futureList;
for (const SimulatorInfo &info : simulatorInfoList) {
futureList << QFuture<void>(Utils::onResultReady(
- SimulatorControl::deleteSimulator(info.identifier),
+ SimulatorControl::deleteSimulator(info.identifier), this,
std::bind(onSimOperation, info, statusDialog, Tr::tr("simulator delete"), _1)));
}
@@ -331,7 +328,7 @@ void IosSettingsWidget::onScreenshot()
QList<QFuture<void>> futureList;
for (const SimulatorInfo &info : simulatorInfoList) {
futureList << QFuture<void>(Utils::onResultReady(
- SimulatorControl::takeSceenshot(info.identifier, generatePath(info)),
+ SimulatorControl::takeSceenshot(info.identifier, generatePath(info)), this,
std::bind(onSimOperation, info, statusDialog, Tr::tr("simulator screenshot"), _1)));
}
diff --git a/src/plugins/ios/iossimulator.cpp b/src/plugins/ios/iossimulator.cpp
index b58e451400..fa68c4031e 100644
--- a/src/plugins/ios/iossimulator.cpp
+++ b/src/plugins/ios/iossimulator.cpp
@@ -8,7 +8,7 @@
#include <projectexplorer/kitinformation.h>
#include <utils/port.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QMapIterator>
@@ -52,7 +52,7 @@ Utils::Port IosSimulator::nextPort() const
// use qrand instead?
if (++m_lastPort >= Constants::IOS_SIMULATOR_PORT_END)
m_lastPort = Constants::IOS_SIMULATOR_PORT_START;
- Utils::QtcProcess portVerifier;
+ Utils::Process portVerifier;
// this is a bit too broad (it does not check just listening sockets, but also connections
// to that port from this computer)
portVerifier.setCommand({"lsof", {"-n", "-P", "-i", QString(":%1").arg(m_lastPort)}});
@@ -66,11 +66,6 @@ Utils::Port IosSimulator::nextPort() const
return Utils::Port(m_lastPort);
}
-bool IosSimulator::canAutoDetectPorts() const
-{
- return true;
-}
-
// IosDeviceType
IosDeviceType::IosDeviceType(IosDeviceType::Type type, const QString &identifier, const QString &displayName) :
diff --git a/src/plugins/ios/iossimulator.h b/src/plugins/ios/iossimulator.h
index d2d69f1d0e..988776e508 100644
--- a/src/plugins/ios/iossimulator.h
+++ b/src/plugins/ios/iossimulator.h
@@ -48,7 +48,6 @@ public:
ProjectExplorer::IDeviceWidget *createWidget() override;
Utils::Port nextPort() const;
- bool canAutoDetectPorts() const override;
protected:
friend class IosSimulatorFactory;
diff --git a/src/plugins/ios/iostoolhandler.cpp b/src/plugins/ios/iostoolhandler.cpp
index 0331ba2cca..0e4545bf1d 100644
--- a/src/plugins/ios/iostoolhandler.cpp
+++ b/src/plugins/ios/iostoolhandler.cpp
@@ -12,11 +12,11 @@
#include <debugger/debuggerconstants.h>
+#include <utils/async.h>
#include <utils/filepath.h>
#include <utils/futuresynchronizer.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <utils/temporarydirectory.h>
#include <QDir>
@@ -63,22 +63,22 @@ class LogTailFiles : public QObject
Q_OBJECT
public:
- void exec(QFutureInterface<void> &fi, std::shared_ptr<QTemporaryFile> stdoutFile,
- std::shared_ptr<QTemporaryFile> stderrFile)
+ void exec(QPromise<void> &promise, std::shared_ptr<QTemporaryFile> stdoutFile,
+ std::shared_ptr<QTemporaryFile> stderrFile)
{
- if (fi.isCanceled())
+ if (promise.isCanceled())
return;
// The future is canceled when app on simulator is stoped.
QEventLoop loop;
QFutureWatcher<void> watcher;
connect(&watcher, &QFutureWatcher<void>::canceled, &loop, [&] { loop.quit(); });
- watcher.setFuture(fi.future());
+ watcher.setFuture(promise.future());
// Process to print the console output while app is running.
auto logProcess = [&](QProcess *tailProcess, std::shared_ptr<QTemporaryFile> file) {
- QObject::connect(tailProcess, &QProcess::readyReadStandardOutput, &loop, [=] {
- if (!fi.isCanceled())
+ QObject::connect(tailProcess, &QProcess::readyReadStandardOutput, &loop, [&, tailProcess] {
+ if (!promise.isCanceled())
emit logMessage(QString::fromLocal8Bit(tailProcess->readAll()));
});
tailProcess->start(QStringLiteral("tail"), {"-f", file->fileName()});
@@ -787,7 +787,6 @@ IosSimulatorToolHandlerPrivate::IosSimulatorToolHandlerPrivate(const IosDeviceTy
{
QObject::connect(&outputLogger, &LogTailFiles::logMessage,
std::bind(&IosToolHandlerPrivate::appOutput, this, _1));
- futureSynchronizer.setCancelOnWait(true);
}
void IosSimulatorToolHandlerPrivate::requestTransferApp(const QString &appBundlePath,
@@ -814,8 +813,8 @@ void IosSimulatorToolHandlerPrivate::requestTransferApp(const QString &appBundle
if (SimulatorControl::isSimulatorRunning(m_deviceId))
installAppOnSimulator();
else
- futureSynchronizer.addFuture(
- Utils::onResultReady(SimulatorControl::startSimulator(m_deviceId), onSimulatorStart));
+ futureSynchronizer.addFuture(Utils::onResultReady(
+ SimulatorControl::startSimulator(m_deviceId), q, onSimulatorStart));
}
void IosSimulatorToolHandlerPrivate::requestRunApp(const QString &appBundlePath,
@@ -851,8 +850,8 @@ void IosSimulatorToolHandlerPrivate::requestRunApp(const QString &appBundlePath,
if (SimulatorControl::isSimulatorRunning(m_deviceId))
launchAppOnSimulator(extraArgs);
else
- futureSynchronizer.addFuture(
- Utils::onResultReady(SimulatorControl::startSimulator(m_deviceId), onSimulatorStart));
+ futureSynchronizer.addFuture(Utils::onResultReady(
+ SimulatorControl::startSimulator(m_deviceId), q, onSimulatorStart));
}
void IosSimulatorToolHandlerPrivate::requestDeviceInfo(const QString &deviceId, int timeout)
@@ -904,7 +903,7 @@ void IosSimulatorToolHandlerPrivate::installAppOnSimulator()
isTransferringApp(m_bundlePath, m_deviceId, 20, 100, "");
auto installFuture = SimulatorControl::installApp(m_deviceId,
Utils::FilePath::fromString(m_bundlePath));
- futureSynchronizer.addFuture(Utils::onResultReady(installFuture, onResponseAppInstall));
+ futureSynchronizer.addFuture(Utils::onResultReady(installFuture, q, onResponseAppInstall));
}
void IosSimulatorToolHandlerPrivate::launchAppOnSimulator(const QStringList &extraArgs)
@@ -931,17 +930,17 @@ void IosSimulatorToolHandlerPrivate::launchAppOnSimulator(const QStringList &ext
"Install Xcode 8 or later.").arg(bundleId));
}
- auto monitorPid = [this](QFutureInterface<void> &fi, qint64 pid) {
+ auto monitorPid = [this](QPromise<void> &promise, qint64 pid) {
#ifdef Q_OS_UNIX
do {
// Poll every 1 sec to check whether the app is running.
QThread::msleep(1000);
- } while (!fi.isCanceled() && kill(pid, 0) == 0);
+ } while (!promise.isCanceled() && kill(pid, 0) == 0);
#else
Q_UNUSED(pid)
#endif
// Future is cancelled if the app is stopped from the qt creator.
- if (!fi.isCanceled())
+ if (!promise.isCanceled())
stop(0);
};
@@ -953,9 +952,9 @@ void IosSimulatorToolHandlerPrivate::launchAppOnSimulator(const QStringList &ext
gotInferiorPid(m_bundlePath, m_deviceId, response.pID);
didStartApp(m_bundlePath, m_deviceId, Ios::IosToolHandler::Success);
// Start monitoring app's life signs.
- futureSynchronizer.addFuture(Utils::runAsync(monitorPid, response.pID));
+ futureSynchronizer.addFuture(Utils::asyncRun(monitorPid, response.pID));
if (captureConsole)
- futureSynchronizer.addFuture(Utils::runAsync(&LogTailFiles::exec, &outputLogger,
+ futureSynchronizer.addFuture(Utils::asyncRun(&LogTailFiles::exec, &outputLogger,
stdoutFile, stderrFile));
} else {
m_pid = -1;
@@ -967,16 +966,11 @@ void IosSimulatorToolHandlerPrivate::launchAppOnSimulator(const QStringList &ext
}
};
- futureSynchronizer.addFuture(
- Utils::onResultReady(SimulatorControl::launchApp(m_deviceId,
- bundleId,
- debugRun,
- extraArgs,
- captureConsole ? stdoutFile->fileName()
- : QString(),
- captureConsole ? stderrFile->fileName()
- : QString()),
- onResponseAppLaunch));
+ futureSynchronizer.addFuture(Utils::onResultReady(SimulatorControl::launchApp(
+ m_deviceId, bundleId, debugRun, extraArgs,
+ captureConsole ? stdoutFile->fileName() : QString(),
+ captureConsole ? stderrFile->fileName() : QString()),
+ q, onResponseAppLaunch));
}
bool IosSimulatorToolHandlerPrivate::isResponseValid(const SimulatorControl::ResponseData &responseData)
diff --git a/src/plugins/ios/simulatorcontrol.cpp b/src/plugins/ios/simulatorcontrol.cpp
index 6e090b6188..0832a5faf3 100644
--- a/src/plugins/ios/simulatorcontrol.cpp
+++ b/src/plugins/ios/simulatorcontrol.cpp
@@ -5,9 +5,9 @@
#include "iosconfigurations.h"
#include <utils/algorithm.h>
-#include <utils/runextensions.h>
+#include <utils/async.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#ifdef Q_OS_MAC
#include <CoreFoundation/CoreFoundation.h>
@@ -57,7 +57,7 @@ static bool checkForTimeout(const chrono::high_resolution_clock::time_point &sta
static bool runCommand(const CommandLine &command, QString *stdOutput, QString *allOutput = nullptr)
{
- QtcProcess p;
+ Process p;
p.setTimeoutS(-1);
p.setCommand(command);
p.runBlocking();
@@ -98,7 +98,7 @@ static bool launchSimulator(const QString &simUdid) {
}
}
- return QtcProcess::startDetached({simulatorAppPath, {"--args", "-CurrentDeviceUDID", simUdid}});
+ return Process::startDetached({simulatorAppPath, {"--args", "-CurrentDeviceUDID", simUdid}});
}
static bool isAvailable(const QJsonObject &object)
@@ -162,30 +162,30 @@ static SimulatorInfo deviceInfo(const QString &simUdid);
static QString bundleIdentifier(const Utils::FilePath &bundlePath);
static QString bundleExecutable(const Utils::FilePath &bundlePath);
-static void startSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
+static void startSimulator(QPromise<SimulatorControl::ResponseData> &promise,
const QString &simUdid);
-static void installApp(QFutureInterface<SimulatorControl::ResponseData> &fi,
+static void installApp(QPromise<SimulatorControl::ResponseData> &promise,
const QString &simUdid,
const Utils::FilePath &bundlePath);
-static void launchApp(QFutureInterface<SimulatorControl::ResponseData> &fi,
+static void launchApp(QPromise<SimulatorControl::ResponseData> &promise,
const QString &simUdid,
const QString &bundleIdentifier,
bool waitForDebugger,
const QStringList &extraArgs,
const QString &stdoutPath,
const QString &stderrPath);
-static void deleteSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
+static void deleteSimulator(QPromise<SimulatorControl::ResponseData> &promise,
const QString &simUdid);
-static void resetSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
+static void resetSimulator(QPromise<SimulatorControl::ResponseData> &promise,
const QString &simUdid);
-static void renameSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
+static void renameSimulator(QPromise<SimulatorControl::ResponseData> &promise,
const QString &simUdid,
const QString &newName);
-static void createSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
+static void createSimulator(QPromise<SimulatorControl::ResponseData> &promise,
const QString &name,
const DeviceTypeInfo &deviceType,
const RuntimeInfo &runtime);
-static void takeSceenshot(QFutureInterface<SimulatorControl::ResponseData> &fi,
+static void takeSceenshot(QPromise<SimulatorControl::ResponseData> &promise,
const QString &simUdid,
const QString &filePath);
@@ -234,10 +234,10 @@ static QList<SimulatorInfo> getAvailableSimulators()
return availableDevices;
}
-QFuture<QList<DeviceTypeInfo> > SimulatorControl::updateDeviceTypes()
+QFuture<QList<DeviceTypeInfo>> SimulatorControl::updateDeviceTypes(QObject *context)
{
- QFuture< QList<DeviceTypeInfo> > future = Utils::runAsync(getAvailableDeviceTypes);
- Utils::onResultReady(future, [](const QList<DeviceTypeInfo> &deviceTypes) {
+ QFuture<QList<DeviceTypeInfo>> future = Utils::asyncRun(getAvailableDeviceTypes);
+ Utils::onResultReady(future, context, [](const QList<DeviceTypeInfo> &deviceTypes) {
s_availableDeviceTypes = deviceTypes;
});
return future;
@@ -248,20 +248,21 @@ QList<RuntimeInfo> SimulatorControl::availableRuntimes()
return s_availableRuntimes;
}
-QFuture<QList<RuntimeInfo> > SimulatorControl::updateRuntimes()
+QFuture<QList<RuntimeInfo>> SimulatorControl::updateRuntimes(QObject *context)
{
- QFuture< QList<RuntimeInfo> > future = Utils::runAsync(getAvailableRuntimes);
- Utils::onResultReady(future, [](const QList<RuntimeInfo> &runtimes) {
+ QFuture<QList<RuntimeInfo>> future = Utils::asyncRun(getAvailableRuntimes);
+ Utils::onResultReady(future, context, [](const QList<RuntimeInfo> &runtimes) {
s_availableRuntimes = runtimes;
});
return future;
}
-QFuture< QList<SimulatorInfo> > SimulatorControl::updateAvailableSimulators()
+QFuture<QList<SimulatorInfo>> SimulatorControl::updateAvailableSimulators(QObject *context)
{
- QFuture< QList<SimulatorInfo> > future = Utils::runAsync(getAvailableSimulators);
- Utils::onResultReady(future,
- [](const QList<SimulatorInfo> &devices) { s_availableDevices = devices; });
+ QFuture<QList<SimulatorInfo>> future = Utils::asyncRun(getAvailableSimulators);
+ Utils::onResultReady(future, context, [](const QList<SimulatorInfo> &devices) {
+ s_availableDevices = devices;
+ });
return future;
}
@@ -284,13 +285,13 @@ QString SimulatorControl::bundleExecutable(const Utils::FilePath &bundlePath)
QFuture<SimulatorControl::ResponseData> SimulatorControl::startSimulator(const QString &simUdid)
{
- return Utils::runAsync(Internal::startSimulator, simUdid);
+ return Utils::asyncRun(Internal::startSimulator, simUdid);
}
QFuture<SimulatorControl::ResponseData> SimulatorControl::installApp(
const QString &simUdid, const Utils::FilePath &bundlePath)
{
- return Utils::runAsync(Internal::installApp, simUdid, bundlePath);
+ return Utils::asyncRun(Internal::installApp, simUdid, bundlePath);
}
QFuture<SimulatorControl::ResponseData> SimulatorControl::launchApp(const QString &simUdid,
@@ -300,7 +301,7 @@ QFuture<SimulatorControl::ResponseData> SimulatorControl::launchApp(const QStrin
const QString &stdoutPath,
const QString &stderrPath)
{
- return Utils::runAsync(Internal::launchApp,
+ return Utils::asyncRun(Internal::launchApp,
simUdid,
bundleIdentifier,
waitForDebugger,
@@ -311,18 +312,18 @@ QFuture<SimulatorControl::ResponseData> SimulatorControl::launchApp(const QStrin
QFuture<SimulatorControl::ResponseData> SimulatorControl::deleteSimulator(const QString &simUdid)
{
- return Utils::runAsync(Internal::deleteSimulator, simUdid);
+ return Utils::asyncRun(Internal::deleteSimulator, simUdid);
}
QFuture<SimulatorControl::ResponseData> SimulatorControl::resetSimulator(const QString &simUdid)
{
- return Utils::runAsync(Internal::resetSimulator, simUdid);
+ return Utils::asyncRun(Internal::resetSimulator, simUdid);
}
QFuture<SimulatorControl::ResponseData> SimulatorControl::renameSimulator(const QString &simUdid,
const QString &newName)
{
- return Utils::runAsync(Internal::renameSimulator, simUdid, newName);
+ return Utils::asyncRun(Internal::renameSimulator, simUdid, newName);
}
QFuture<SimulatorControl::ResponseData>
@@ -330,13 +331,13 @@ SimulatorControl::createSimulator(const QString &name,
const DeviceTypeInfo &deviceType,
const RuntimeInfo &runtime)
{
- return Utils::runAsync(Internal::createSimulator, name, deviceType, runtime);
+ return Utils::asyncRun(Internal::createSimulator, name, deviceType, runtime);
}
QFuture<SimulatorControl::ResponseData> SimulatorControl::takeSceenshot(const QString &simUdid,
const QString &filePath)
{
- return Utils::runAsync(Internal::takeSceenshot, simUdid, filePath);
+ return Utils::asyncRun(Internal::takeSceenshot, simUdid, filePath);
}
// Static members
@@ -392,7 +393,7 @@ QString bundleExecutable(const Utils::FilePath &bundlePath)
return executable;
}
-void startSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi, const QString &simUdid)
+void startSimulator(QPromise<SimulatorControl::ResponseData> &promise, const QString &simUdid)
{
SimulatorControl::ResponseData response(simUdid);
SimulatorInfo simInfo = deviceInfo(simUdid);
@@ -420,7 +421,7 @@ void startSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi, const
if (simInfo.isShutdown()) {
if (launchSimulator(simUdid)) {
- if (fi.isCanceled())
+ if (promise.isCanceled())
return;
// At this point the sim device exists, available and was not running.
// So the simulator is started and we'll wait for it to reach to a state
@@ -429,13 +430,11 @@ void startSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi, const
SimulatorInfo info;
do {
info = deviceInfo(simUdid);
- if (fi.isCanceled())
+ if (promise.isCanceled())
return;
- } while (!info.isBooted()
- && !checkForTimeout(start, simulatorStartTimeout));
- if (info.isBooted()) {
+ } while (!info.isBooted() && !checkForTimeout(start, simulatorStartTimeout));
+ if (info.isBooted())
response.success = true;
- }
} else {
qCDebug(simulatorLog) << "Error starting simulator.";
}
@@ -444,14 +443,12 @@ void startSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi, const
<< simInfo;
}
- if (!fi.isCanceled()) {
- fi.reportResult(response);
- }
+ if (!promise.isCanceled())
+ promise.addResult(response);
}
-void installApp(QFutureInterface<SimulatorControl::ResponseData> &fi,
- const QString &simUdid,
- const Utils::FilePath &bundlePath)
+void installApp(QPromise<SimulatorControl::ResponseData> &promise,
+ const QString &simUdid, const Utils::FilePath &bundlePath)
{
QTC_CHECK(bundlePath.exists());
@@ -459,11 +456,11 @@ void installApp(QFutureInterface<SimulatorControl::ResponseData> &fi,
response.success = runSimCtlCommand({"install", simUdid, bundlePath.toString()},
nullptr,
&response.commandOutput);
- if (!fi.isCanceled())
- fi.reportResult(response);
+ if (!promise.isCanceled())
+ promise.addResult(response);
}
-void launchApp(QFutureInterface<SimulatorControl::ResponseData> &fi,
+void launchApp(QPromise<SimulatorControl::ResponseData> &promise,
const QString &simUdid,
const QString &bundleIdentifier,
bool waitForDebugger,
@@ -472,7 +469,7 @@ void launchApp(QFutureInterface<SimulatorControl::ResponseData> &fi,
const QString &stderrPath)
{
SimulatorControl::ResponseData response(simUdid);
- if (!bundleIdentifier.isEmpty() && !fi.isCanceled()) {
+ if (!bundleIdentifier.isEmpty() && !promise.isCanceled()) {
QStringList args({"launch", simUdid, bundleIdentifier});
// simctl usage documentation : Note: Log output is often directed to stderr, not stdout.
@@ -499,30 +496,29 @@ void launchApp(QFutureInterface<SimulatorControl::ResponseData> &fi,
}
}
- if (!fi.isCanceled()) {
- fi.reportResult(response);
- }
+ if (!promise.isCanceled())
+ promise.addResult(response);
}
-void deleteSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi, const QString &simUdid)
+void deleteSimulator(QPromise<SimulatorControl::ResponseData> &promise, const QString &simUdid)
{
SimulatorControl::ResponseData response(simUdid);
response.success = runSimCtlCommand({"delete", simUdid}, nullptr, &response.commandOutput);
- if (!fi.isCanceled())
- fi.reportResult(response);
+ if (!promise.isCanceled())
+ promise.addResult(response);
}
-void resetSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi, const QString &simUdid)
+void resetSimulator(QPromise<SimulatorControl::ResponseData> &promise, const QString &simUdid)
{
SimulatorControl::ResponseData response(simUdid);
response.success = runSimCtlCommand({"erase", simUdid}, nullptr, &response.commandOutput);
- if (!fi.isCanceled())
- fi.reportResult(response);
+ if (!promise.isCanceled())
+ promise.addResult(response);
}
-void renameSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
+void renameSimulator(QPromise<SimulatorControl::ResponseData> &promise,
const QString &simUdid,
const QString &newName)
{
@@ -530,12 +526,11 @@ void renameSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
response.success = runSimCtlCommand({"rename", simUdid, newName},
nullptr,
&response.commandOutput);
-
- if (!fi.isCanceled())
- fi.reportResult(response);
+ if (!promise.isCanceled())
+ promise.addResult(response);
}
-void createSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
+void createSimulator(QPromise<SimulatorControl::ResponseData> &promise,
const QString &name,
const DeviceTypeInfo &deviceType,
const RuntimeInfo &runtime)
@@ -550,11 +545,11 @@ void createSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
response.simUdid = response.success ? stdOutput.trimmed() : QString();
}
- if (!fi.isCanceled())
- fi.reportResult(response);
+ if (!promise.isCanceled())
+ promise.addResult(response);
}
-void takeSceenshot(QFutureInterface<SimulatorControl::ResponseData> &fi,
+void takeSceenshot(QPromise<SimulatorControl::ResponseData> &promise,
const QString &simUdid,
const QString &filePath)
{
@@ -562,8 +557,8 @@ void takeSceenshot(QFutureInterface<SimulatorControl::ResponseData> &fi,
response.success = runSimCtlCommand({"io", simUdid, "screenshot", filePath},
nullptr,
&response.commandOutput);
- if (!fi.isCanceled())
- fi.reportResult(response);
+ if (!promise.isCanceled())
+ promise.addResult(response);
}
QDebug &operator<<(QDebug &stream, const SimulatorInfo &info)
diff --git a/src/plugins/ios/simulatorcontrol.h b/src/plugins/ios/simulatorcontrol.h
index 15fea7b27b..402100fbea 100644
--- a/src/plugins/ios/simulatorcontrol.h
+++ b/src/plugins/ios/simulatorcontrol.h
@@ -65,11 +65,11 @@ public:
public:
static QList<DeviceTypeInfo> availableDeviceTypes();
- static QFuture<QList<DeviceTypeInfo> > updateDeviceTypes();
+ static QFuture<QList<DeviceTypeInfo>> updateDeviceTypes(QObject *context);
static QList<RuntimeInfo> availableRuntimes();
- static QFuture<QList<RuntimeInfo> > updateRuntimes();
+ static QFuture<QList<RuntimeInfo>> updateRuntimes(QObject *context);
static QList<SimulatorInfo> availableSimulators();
- static QFuture<QList<SimulatorInfo> > updateAvailableSimulators();
+ static QFuture<QList<SimulatorInfo>> updateAvailableSimulators(QObject *context);
static bool isSimulatorRunning(const QString &simUdid);
static QString bundleIdentifier(const Utils::FilePath &bundlePath);
static QString bundleExecutable(const Utils::FilePath &bundlePath);
diff --git a/src/plugins/ios/simulatorinfomodel.cpp b/src/plugins/ios/simulatorinfomodel.cpp
index df58dc98cb..dce5646713 100644
--- a/src/plugins/ios/simulatorinfomodel.cpp
+++ b/src/plugins/ios/simulatorinfomodel.cpp
@@ -6,7 +6,7 @@
#include "iostr.h"
#include <utils/algorithm.h>
-#include <utils/runextensions.h>
+#include <utils/async.h>
#include <QTimer>
@@ -23,8 +23,6 @@ const int deviceUpdateInterval = 1000; // Update simulator state every 1 sec.
SimulatorInfoModel::SimulatorInfoModel(QObject *parent) :
QAbstractItemModel(parent)
{
- m_fetchFuture.setCancelOnWait(true);
-
requestSimulatorInfo();
auto updateTimer = new QTimer(this);
@@ -109,7 +107,7 @@ void SimulatorInfoModel::requestSimulatorInfo()
if (!m_fetchFuture.isEmpty())
return; // Ignore the request if the last request is still pending.
- m_fetchFuture.addFuture(Utils::onResultReady(SimulatorControl::updateAvailableSimulators(),
+ m_fetchFuture.addFuture(Utils::onResultReady(SimulatorControl::updateAvailableSimulators(this),
this, &SimulatorInfoModel::populateSimulators));
}
diff --git a/src/plugins/ios/simulatoroperationdialog.cpp b/src/plugins/ios/simulatoroperationdialog.cpp
index 56e2d3b69f..180b28d76e 100644
--- a/src/plugins/ios/simulatoroperationdialog.cpp
+++ b/src/plugins/ios/simulatoroperationdialog.cpp
@@ -40,7 +40,7 @@ SimulatorOperationDialog::SimulatorOperationDialog(QWidget *parent) :
m_formatter = new Utils::OutputFormatter;
m_formatter->setPlainTextEdit(messageEdit);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
messageEdit,
diff --git a/src/plugins/languageclient/CMakeLists.txt b/src/plugins/languageclient/CMakeLists.txt
index 8e1748a04a..5af221f7f8 100644
--- a/src/plugins/languageclient/CMakeLists.txt
+++ b/src/plugins/languageclient/CMakeLists.txt
@@ -1,9 +1,17 @@
+if (MSVC)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
+elseif (MINGW)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj")
+endif()
+
add_qtc_plugin(LanguageClient
PUBLIC_DEPENDS LanguageServerProtocol Qt::Core app_version
PLUGIN_DEPENDS ProjectExplorer Core TextEditor
SOURCES
callhierarchy.cpp callhierarchy.h
client.cpp client.h
+ clientrequesttask.cpp clientrequesttask.h
+ currentdocumentsymbolsrequest.cpp currentdocumentsymbolsrequest.h
diagnosticmanager.cpp diagnosticmanager.h
documentsymbolcache.cpp documentsymbolcache.h
dynamiccapabilities.cpp dynamiccapabilities.h
diff --git a/src/plugins/languageclient/callhierarchy.cpp b/src/plugins/languageclient/callhierarchy.cpp
index 3e0c6589aa..a05922cbe5 100644
--- a/src/plugins/languageclient/callhierarchy.cpp
+++ b/src/plugins/languageclient/callhierarchy.cpp
@@ -23,8 +23,6 @@ using namespace LanguageServerProtocol;
namespace LanguageClient {
-const char CALL_HIERARCHY_FACTORY_ID[] = "LanguageClient.CallHierarchy";
-
namespace {
enum Direction { Incoming, Outgoing };
@@ -186,6 +184,9 @@ public:
layout()->setSpacing(0);
connect(m_view, &NavigationTreeView::activated, this, &CallHierarchy::onItemActivated);
+
+ connect(LanguageClientManager::instance(), &LanguageClientManager::openCallHierarchy,
+ this, &CallHierarchy::updateHierarchyAtCursorPosition);
}
void onItemActivated(const QModelIndex &index)
@@ -211,26 +212,14 @@ void CallHierarchy::updateHierarchyAtCursorPosition()
BaseTextEditor *editor = BaseTextEditor::currentTextEditor();
if (!editor)
return;
- Client *client = LanguageClientManager::clientForFilePath(editor->document()->filePath());
+
+ Core::IDocument *document = editor->document();
+
+ Client *client = LanguageClientManager::clientForFilePath(document->filePath());
if (!client)
return;
- const QString methodName = PrepareCallHierarchyRequest::methodName;
- std::optional<bool> registered = client->dynamicCapabilities().isRegistered(methodName);
- bool supported = registered.value_or(false);
- const Core::IDocument *document = editor->document();
- if (registered) {
- if (supported) {
- const QJsonValue &options = client->dynamicCapabilities().option(methodName);
- const TextDocumentRegistrationOptions docOptions(options);
- supported = docOptions.filterApplies(document->filePath(),
- Utils::mimeTypeForName(document->mimeType()));
- }
- } else {
- supported = client->capabilities().callHierarchyProvider().has_value();
- }
-
- if (!supported)
+ if (!CallHierarchyFactory::supportsCallHierarchy(client, document))
return;
TextDocumentPositionParams params;
@@ -273,7 +262,25 @@ CallHierarchyFactory::CallHierarchyFactory()
{
setDisplayName(Tr::tr("Call Hierarchy"));
setPriority(650);
- setId(CALL_HIERARCHY_FACTORY_ID);
+ setId(Constants::CALL_HIERARCHY_FACTORY_ID);
+}
+
+bool CallHierarchyFactory::supportsCallHierarchy(Client *client, const Core::IDocument *document)
+{
+ const QString methodName = PrepareCallHierarchyRequest::methodName;
+ std::optional<bool> registered = client->dynamicCapabilities().isRegistered(methodName);
+ bool supported = registered.value_or(false);
+ if (registered) {
+ if (supported) {
+ const QJsonValue &options = client->dynamicCapabilities().option(methodName);
+ const TextDocumentRegistrationOptions docOptions(options);
+ supported = docOptions.filterApplies(document->filePath(),
+ Utils::mimeTypeForName(document->mimeType()));
+ }
+ } else {
+ supported = client->capabilities().callHierarchyProvider().has_value();
+ }
+ return supported;
}
Core::NavigationView CallHierarchyFactory::createWidget()
@@ -284,6 +291,7 @@ Core::NavigationView CallHierarchyFactory::createWidget()
Icons::RELOAD_TOOLBAR.icon();
auto button = new QToolButton;
button->setIcon(Icons::RELOAD_TOOLBAR.icon());
+ button->setToolTip(Tr::tr("Reloads the call hierarchy for the symbol under cursor position."));
connect(button, &QToolButton::clicked, [h](){
h->updateHierarchyAtCursorPosition();
});
diff --git a/src/plugins/languageclient/callhierarchy.h b/src/plugins/languageclient/callhierarchy.h
index f707c4fbcb..bbc15b0971 100644
--- a/src/plugins/languageclient/callhierarchy.h
+++ b/src/plugins/languageclient/callhierarchy.h
@@ -5,8 +5,12 @@
#pragma once
+namespace Core { class IDocument; }
+
namespace LanguageClient {
+class Client;
+
class CallHierarchyFactory : public Core::INavigationWidgetFactory
{
Q_OBJECT
@@ -14,6 +18,8 @@ class CallHierarchyFactory : public Core::INavigationWidgetFactory
public:
CallHierarchyFactory();
+ static bool supportsCallHierarchy(Client *client, const Core::IDocument *document);
+
Core::NavigationView createWidget() override;
};
diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp
index 0efaacd350..130961ebd1 100644
--- a/src/plugins/languageclient/client.cpp
+++ b/src/plugins/languageclient/client.cpp
@@ -3,6 +3,7 @@
#include "client.h"
+#include "callhierarchy.h"
#include "diagnosticmanager.h"
#include "documentsymbolcache.h"
#include "languageclientcompletionassist.h"
@@ -11,6 +12,7 @@
#include "languageclienthoverhandler.h"
#include "languageclientinterface.h"
#include "languageclientmanager.h"
+#include "languageclientoutline.h"
#include "languageclientquickfix.h"
#include "languageclientsymbolsupport.h"
#include "languageclientutils.h"
@@ -26,6 +28,8 @@
#include <coreplugin/messagemanager.h>
#include <coreplugin/progressmanager/progressmanager.h>
+#include <extensionsystem/pluginmanager.h>
+
#include <languageserverprotocol/completion.h>
#include <languageserverprotocol/diagnostics.h>
#include <languageserverprotocol/initializemessages.h>
@@ -38,7 +42,7 @@
#include <languageserverprotocol/workspace.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <texteditor/codeassist/documentcontentcompletion.h>
#include <texteditor/codeassist/iassistprocessor.h>
@@ -51,7 +55,7 @@
#include <texteditor/texteditorsettings.h>
#include <utils/mimeutils.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QDebug>
#include <QJsonDocument>
@@ -155,7 +159,7 @@ public:
m_documentUpdateTimer.setInterval(500);
connect(&m_documentUpdateTimer, &QTimer::timeout, this,
[this] { sendPostponedDocumentUpdates(Schedule::Now); });
- connect(SessionManager::instance(), &SessionManager::projectRemoved,
+ connect(ProjectManager::instance(), &ProjectManager::projectRemoved,
q, &Client::projectClosed);
QTC_ASSERT(clientInterface, return);
@@ -192,7 +196,7 @@ public:
// temporary container needed since m_resetAssistProvider is changed in resetAssistProviders
for (TextDocument *document : m_resetAssistProvider.keys())
resetAssistProviders(document);
- if (!LanguageClientManager::isShuttingDown()) {
+ if (!ExtensionSystem::PluginManager::isShuttingDown()) {
// prevent accessing deleted editors on Creator shutdown
const QList<Core::IEditor *> &editors = Core::DocumentModel::editorsForOpenedDocuments();
for (Core::IEditor *editor : editors) {
@@ -325,7 +329,6 @@ public:
SemanticTokenSupport m_tokenSupport;
QString m_serverName;
QString m_serverVersion;
- LanguageServerProtocol::SymbolStringifier m_symbolStringifier;
Client::LogTarget m_logTarget = Client::LogTarget::Ui;
bool m_locatorsEnabled = true;
bool m_autoRequestCodeActions = true;
@@ -353,6 +356,7 @@ void Client::setName(const QString &name)
QString Client::name() const
{
if (d->m_project && !d->m_project->displayName().isEmpty())
+ //: <language client> for <project>
return Tr::tr("%1 for %2").arg(d->m_displayName, d->m_project->displayName());
return d->m_displayName;
}
@@ -508,11 +512,13 @@ void Client::initialize()
if (d->m_project)
params.setRootUri(hostPathToServerUri(d->m_project->projectDirectory()));
+ auto projectFilter = [this](Project *project) { return canOpenProject(project); };
+ auto toWorkSpaceFolder = [this](Project *pro) {
+ return WorkSpaceFolder(hostPathToServerUri(pro->projectDirectory()), pro->displayName());
+ };
const QList<WorkSpaceFolder> workspaces
- = Utils::transform(SessionManager::projects(), [this](Project *pro) {
- return WorkSpaceFolder(hostPathToServerUri(pro->projectDirectory()),
- pro->displayName());
- });
+ = Utils::transform(Utils::filtered(ProjectManager::projects(), projectFilter),
+ toWorkSpaceFolder);
if (workspaces.isEmpty())
params.setWorkSpaceFolders(nullptr);
else
@@ -550,11 +556,17 @@ Client::State Client::state() const
QString Client::stateString() const
{
switch (d->m_state){
+ //: language client state
case Uninitialized: return Tr::tr("uninitialized");
+ //: language client state
case InitializeRequested: return Tr::tr("initialize requested");
+ //: language client state
case Initialized: return Tr::tr("initialized");
+ //: language client state
case ShutdownRequested: return Tr::tr("shutdown requested");
- case Shutdown: return Tr::tr("shutdown");
+ //: language client state
+ case Shutdown: return Tr::tr("shut down");
+ //: language client state
case Error: return Tr::tr("error");
}
return {};
@@ -617,7 +629,7 @@ void Client::openDocument(TextEditor::TextDocument *document)
}
}
- d->m_openedDocument[document].document = document->document()->clone(this);
+ d->m_openedDocument[document].document = new QTextDocument(document->document()->toPlainText());
d->m_openedDocument[document].contentsChangedConnection
= connect(document,
&TextDocument::contentsChangedWithPosition,
@@ -879,6 +891,8 @@ void Client::activateEditor(Core::IEditor *editor)
optionalActions |= TextEditor::TextEditorActionHandler::FindUsage;
if (symbolSupport().supportsRename(widget->textDocument()))
optionalActions |= TextEditor::TextEditorActionHandler::RenameSymbol;
+ if (CallHierarchyFactory::supportsCallHierarchy(this, textEditor->document()))
+ optionalActions |= TextEditor::TextEditorActionHandler::CallHierarchy;
widget->setOptionalActions(optionalActions);
}
}
@@ -1348,6 +1362,7 @@ ProjectExplorer::Project *Client::project() const
void Client::setCurrentProject(ProjectExplorer::Project *project)
{
+ QTC_ASSERT(canOpenProject(project), return);
if (d->m_project == project)
return;
if (d->m_project)
@@ -1364,7 +1379,7 @@ void Client::setCurrentProject(ProjectExplorer::Project *project)
void Client::projectOpened(ProjectExplorer::Project *project)
{
- if (!d->sendWorkspceFolderChanges())
+ if (!d->sendWorkspceFolderChanges() || !canOpenProject(project))
return;
WorkspaceFoldersChangeEvent event;
event.setAdded({WorkSpaceFolder(hostPathToServerUri(project->projectDirectory()),
@@ -1377,7 +1392,7 @@ void Client::projectOpened(ProjectExplorer::Project *project)
void Client::projectClosed(ProjectExplorer::Project *project)
{
- if (d->sendWorkspceFolderChanges()) {
+ if (d->sendWorkspceFolderChanges() && canOpenProject(project)) {
WorkspaceFoldersChangeEvent event;
event.setRemoved({WorkSpaceFolder(hostPathToServerUri(project->projectDirectory()),
project->displayName())});
@@ -1397,6 +1412,12 @@ void Client::projectClosed(ProjectExplorer::Project *project)
}
}
+bool Client::canOpenProject(ProjectExplorer::Project *project)
+{
+ Q_UNUSED(project);
+ return true;
+}
+
void Client::updateConfiguration(const QJsonValue &configuration)
{
d->m_configuration = configuration;
@@ -1484,16 +1505,6 @@ void Client::setSemanticTokensHandler(const SemanticTokensHandler &handler)
d->m_tokenSupport.setTokensHandler(handler);
}
-void Client::setSymbolStringifier(const LanguageServerProtocol::SymbolStringifier &stringifier)
-{
- d->m_symbolStringifier = stringifier;
-}
-
-SymbolStringifier Client::symbolStringifier() const
-{
- return d->m_symbolStringifier;
-}
-
void Client::setSnippetsGroup(const QString &group)
{
if (const auto provider = qobject_cast<LanguageClientCompletionAssistProvider *>(
@@ -1679,10 +1690,15 @@ LanguageClientValue<MessageActionItem> ClientPrivate::showMessageBox(
}
QHash<QAbstractButton *, MessageActionItem> itemForButton;
if (const std::optional<QList<MessageActionItem>> actions = message.actions()) {
- for (const MessageActionItem &action : *actions)
- itemForButton.insert(box->addButton(action.title(), QMessageBox::InvalidRole), action);
+ auto button = box->addButton(QMessageBox::Close);
+ connect(button, &QPushButton::clicked, box, &QMessageBox::reject);
+ for (const MessageActionItem &action : *actions) {
+ connect(button, &QPushButton::clicked, box, &QMessageBox::accept);
+ itemForButton.insert(button, action);
+ }
}
- box->exec();
+ if (box->exec() == QDialog::Rejected)
+ return {};
const MessageActionItem &item = itemForButton.value(box->clickedButton());
return item.isValid() ? LanguageClientValue<MessageActionItem>(item)
: LanguageClientValue<MessageActionItem>();
@@ -1872,7 +1888,7 @@ void ClientPrivate::handleMethod(const QString &method, const MessageId &id, con
} else if (method == WorkSpaceFolderRequest::methodName) {
WorkSpaceFolderRequest::Response response(id);
const QList<ProjectExplorer::Project *> projects
- = ProjectExplorer::SessionManager::projects();
+ = ProjectExplorer::ProjectManager::projects();
if (projects.isEmpty()) {
response.setResult(nullptr);
} else {
@@ -1961,7 +1977,7 @@ void ClientPrivate::initializeCallback(const InitializeRequest::Response &initRe
if (std::optional<ResponseError<InitializeError>> error = initResponse.error()) {
if (std::optional<InitializeError> data = error->data()) {
if (data->retry()) {
- const QString title(Tr::tr("Language Server \"%1\" Initialize Error").arg(m_displayName));
+ const QString title(Tr::tr("Language Server \"%1\" Initialization Error").arg(m_displayName));
auto result = QMessageBox::warning(Core::ICore::dialogParent(),
title,
error->message(),
@@ -1974,7 +1990,7 @@ void ClientPrivate::initializeCallback(const InitializeRequest::Response &initRe
}
}
}
- q->setError(Tr::tr("Initialize error: ") + error->message());
+ q->setError(Tr::tr("Initialization error: %1.").arg(error->message()));
emit q->finished();
return;
}
@@ -2089,6 +2105,12 @@ bool Client::fileBelongsToProject(const Utils::FilePath &filePath) const
return project() && project()->isKnownFile(filePath);
}
+LanguageClientOutlineItem *Client::createOutlineItem(
+ const LanguageServerProtocol::DocumentSymbol &symbol)
+{
+ return new LanguageClientOutlineItem(this, symbol);
+}
+
FilePath toHostPath(const FilePath serverDeviceTemplate, const FilePath localClientPath)
{
const FilePath onDevice = serverDeviceTemplate.withNewPath(localClientPath.path());
@@ -2110,7 +2132,7 @@ FilePath Client::serverUriToHostPath(const LanguageServerProtocol::DocumentUri &
DocumentUri Client::hostPathToServerUri(const Utils::FilePath &path) const
{
return DocumentUri::fromFilePath(path, [&](const Utils::FilePath &clientPath) {
- return clientPath.onDevice(d->m_serverDeviceTemplate);
+ return d->m_serverDeviceTemplate.withNewPath(clientPath.path());
});
}
diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h
index 525739d7a6..2ddd155a9f 100644
--- a/src/plugins/languageclient/client.h
+++ b/src/plugins/languageclient/client.h
@@ -31,7 +31,6 @@ class Unregistration;
} // namespace LanguageServerProtocol
namespace LanguageClient {
-
class BaseClientInterface;
class ClientPrivate;
class DiagnosticManager;
@@ -40,6 +39,7 @@ class DynamicCapabilities;
class HoverHandler;
class InterfaceController;
class LanguageClientCompletionAssistProvider;
+class LanguageClientOutlineItem;
class LanguageClientQuickFixProvider;
class LanguageFilter;
class ProgressManager;
@@ -48,16 +48,12 @@ class SymbolSupport;
class LANGUAGECLIENT_EXPORT Client : public QObject
{
Q_OBJECT
+ Q_DISABLE_COPY_MOVE(Client)
public:
explicit Client(BaseClientInterface *clientInterface, const Utils::Id &id = {}); // takes ownership
~Client() override;
- Client(const Client &) = delete;
- Client(Client &&) = delete;
- Client &operator=(const Client &) = delete;
- Client &operator=(Client &&) = delete;
-
// basic properties
Utils::Id id() const;
void setName(const QString &name);
@@ -136,6 +132,7 @@ public:
ProjectExplorer::Project *project() const;
virtual void projectOpened(ProjectExplorer::Project *project);
virtual void projectClosed(ProjectExplorer::Project *project);
+ virtual bool canOpenProject(ProjectExplorer::Project *project);
void updateConfiguration(const QJsonValue &configuration);
// commands
@@ -160,13 +157,13 @@ public:
const LanguageServerProtocol::Diagnostic &diag) const;
bool hasDiagnostics(const TextEditor::TextDocument *document) const;
void setSemanticTokensHandler(const SemanticTokensHandler &handler);
- void setSymbolStringifier(const LanguageServerProtocol::SymbolStringifier &stringifier);
- LanguageServerProtocol::SymbolStringifier symbolStringifier() const;
void setSnippetsGroup(const QString &group);
void setCompletionAssistProvider(LanguageClientCompletionAssistProvider *provider);
void setQuickFixAssistProvider(LanguageClientQuickFixProvider *provider);
virtual bool supportsDocumentSymbols(const TextEditor::TextDocument *doc) const;
virtual bool fileBelongsToProject(const Utils::FilePath &filePath) const;
+ virtual LanguageClientOutlineItem *createOutlineItem(
+ const LanguageServerProtocol::DocumentSymbol &symbol);
LanguageServerProtocol::DocumentUri::PathMapper hostPathMapper() const;
Utils::FilePath serverUriToHostPath(const LanguageServerProtocol::DocumentUri &uri) const;
diff --git a/src/plugins/languageclient/clientrequesttask.cpp b/src/plugins/languageclient/clientrequesttask.cpp
new file mode 100644
index 0000000000..ed3ddaff2c
--- /dev/null
+++ b/src/plugins/languageclient/clientrequesttask.cpp
@@ -0,0 +1,39 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "clientrequesttask.h"
+
+#include <QScopeGuard>
+
+using namespace LanguageServerProtocol;
+
+namespace LanguageClient {
+
+WorkspaceSymbolRequestTaskAdapter::WorkspaceSymbolRequestTaskAdapter()
+{
+ task()->setResponseCallback([this](const WorkspaceSymbolRequest::Response &response){
+ emit done(response.result().has_value());
+ });
+}
+
+void WorkspaceSymbolRequestTaskAdapter::start()
+{
+ task()->start();
+}
+
+bool WorkspaceSymbolRequestTask::preStartCheck()
+{
+ if (!ClientRequestTask::preStartCheck())
+ return false;
+
+ const std::optional<std::variant<bool, WorkDoneProgressOptions>> capability
+ = client()->capabilities().workspaceSymbolProvider();
+ if (!capability.has_value())
+ return false;
+ if (std::holds_alternative<bool>(*capability) && !std::get<bool>(*capability))
+ return false;
+
+ return true;
+}
+
+} // namespace LanguageClient
diff --git a/src/plugins/languageclient/clientrequesttask.h b/src/plugins/languageclient/clientrequesttask.h
new file mode 100644
index 0000000000..3e08e1a287
--- /dev/null
+++ b/src/plugins/languageclient/clientrequesttask.h
@@ -0,0 +1,80 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "languageclient_global.h"
+
+#include "client.h"
+
+#include <languageserverprotocol/lsptypes.h>
+#include <languageserverprotocol/lsputils.h>
+#include <languageserverprotocol/workspace.h>
+
+#include <solutions/tasking/tasktree.h>
+
+namespace LanguageClient {
+
+template <typename Request>
+class LANGUAGECLIENT_EXPORT ClientRequestTask
+{
+public:
+ virtual ~ClientRequestTask()
+ {
+ if (m_id)
+ m_client->cancelRequest(*m_id); // In order to not to invoke a response callback anymore
+ }
+
+ void setClient(Client *client) { m_client = client; }
+ Client *client() const { return m_client; }
+ void setParams(const typename Request::Parameters &params) { m_params = params; }
+
+ void start()
+ {
+ QTC_ASSERT(!isRunning(), return);
+ if (!preStartCheck()) {
+ m_callback({});
+ return;
+ }
+ Request request(m_params);
+ request.setResponseCallback([this](const typename Request::Response &response) {
+ m_response = response;
+ m_id = {};
+ m_callback(response);
+ });
+ m_id = request.id();
+ m_client->sendMessage(request);
+ }
+
+ bool isRunning() const { return m_id.has_value(); }
+ virtual bool preStartCheck() { return m_client && m_client->reachable() && m_params.isValid(); }
+
+ typename Request::Response response() const { return m_response; }
+ void setResponseCallback(typename Request::ResponseCallback callback) { m_callback = callback; }
+
+private:
+ Client *m_client = nullptr;
+ typename Request::Parameters m_params;
+ typename Request::ResponseCallback m_callback;
+ std::optional<LanguageServerProtocol::MessageId> m_id;
+ typename Request::Response m_response;
+};
+
+class LANGUAGECLIENT_EXPORT WorkspaceSymbolRequestTask
+ : public ClientRequestTask<LanguageServerProtocol::WorkspaceSymbolRequest>
+{
+public:
+ bool preStartCheck() override;
+};
+
+class LANGUAGECLIENT_EXPORT WorkspaceSymbolRequestTaskAdapter
+ : public Tasking::TaskAdapter<WorkspaceSymbolRequestTask>
+{
+public:
+ WorkspaceSymbolRequestTaskAdapter();
+ void start() final;
+};
+
+} // namespace LanguageClient
+
+TASKING_DECLARE_TASK(SymbolRequest, LanguageClient::WorkspaceSymbolRequestTaskAdapter);
diff --git a/src/plugins/languageclient/currentdocumentsymbolsrequest.cpp b/src/plugins/languageclient/currentdocumentsymbolsrequest.cpp
new file mode 100644
index 0000000000..2d272a7216
--- /dev/null
+++ b/src/plugins/languageclient/currentdocumentsymbolsrequest.cpp
@@ -0,0 +1,83 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "currentdocumentsymbolsrequest.h"
+
+#include "documentsymbolcache.h"
+#include "languageclientmanager.h"
+
+#include <coreplugin/editormanager/editormanager.h>
+
+using namespace Core;
+using namespace LanguageServerProtocol;
+using namespace TextEditor;
+using namespace Utils;
+
+namespace LanguageClient {
+
+void CurrentDocumentSymbolsRequest::start()
+{
+ QTC_ASSERT(!isRunning(), return);
+
+ m_currentDocumentSymbolsData = {};
+
+ TextDocument *document = TextDocument::currentTextDocument();
+ Client *client = LanguageClientManager::clientForDocument(document);
+ if (!client) {
+ emit done(false);
+ return;
+ }
+
+ DocumentSymbolCache *symbolCache = client->documentSymbolCache();
+ DocumentUri currentUri = client->hostPathToServerUri(document->filePath());
+ DocumentUri::PathMapper pathMapper = client->hostPathMapper();
+
+ const auto reportFailure = [this] {
+ clearConnections();
+ emit done(false);
+ };
+
+ const auto updateSymbols = [this, currentUri, pathMapper](const DocumentUri &uri,
+ const DocumentSymbolsResult &symbols)
+ {
+ if (uri != currentUri) // We might get updates for not current editor
+ return;
+
+ const FilePath filePath = pathMapper ? currentUri.toFilePath(pathMapper) : FilePath();
+ m_currentDocumentSymbolsData = {filePath, pathMapper, symbols};
+ clearConnections();
+ emit done(true);
+ };
+
+ m_connections.append(connect(EditorManager::instance(), &EditorManager::currentEditorChanged,
+ this, reportFailure));
+ m_connections.append(connect(client, &Client::finished, this, reportFailure));
+ m_connections.append(connect(document, &IDocument::contentsChanged, this, reportFailure));
+ m_connections.append(connect(symbolCache, &DocumentSymbolCache::gotSymbols,
+ this, updateSymbols));
+ symbolCache->requestSymbols(currentUri, Schedule::Now);
+}
+
+bool CurrentDocumentSymbolsRequest::isRunning() const
+{
+ return !m_connections.isEmpty();
+}
+
+void CurrentDocumentSymbolsRequest::clearConnections()
+{
+ for (const QMetaObject::Connection &connection : std::as_const(m_connections))
+ disconnect(connection);
+ m_connections.clear();
+}
+
+CurrentDocumentSymbolsRequestTaskAdapter::CurrentDocumentSymbolsRequestTaskAdapter()
+{
+ connect(task(), &CurrentDocumentSymbolsRequest::done, this, &TaskInterface::done);
+}
+
+void CurrentDocumentSymbolsRequestTaskAdapter::start()
+{
+ task()->start();
+}
+
+} // namespace LanguageClient
diff --git a/src/plugins/languageclient/currentdocumentsymbolsrequest.h b/src/plugins/languageclient/currentdocumentsymbolsrequest.h
new file mode 100644
index 0000000000..0b9c11a221
--- /dev/null
+++ b/src/plugins/languageclient/currentdocumentsymbolsrequest.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "languageclient_global.h"
+
+#include <languageserverprotocol/languagefeatures.h>
+#include <languageserverprotocol/lsptypes.h>
+
+#include <solutions/tasking/tasktree.h>
+
+namespace LanguageClient {
+
+class LANGUAGECLIENT_EXPORT CurrentDocumentSymbolsData
+{
+public:
+ Utils::FilePath m_filePath;
+ LanguageServerProtocol::DocumentUri::PathMapper m_pathMapper;
+ LanguageServerProtocol::DocumentSymbolsResult m_symbols;
+};
+
+class LANGUAGECLIENT_EXPORT CurrentDocumentSymbolsRequest : public QObject
+{
+ Q_OBJECT
+
+public:
+ void start();
+ bool isRunning() const;
+ CurrentDocumentSymbolsData currentDocumentSymbolsData() const { return m_currentDocumentSymbolsData; }
+
+signals:
+ void done(bool success);
+
+private:
+ void clearConnections();
+
+ CurrentDocumentSymbolsData m_currentDocumentSymbolsData;
+ QList<QMetaObject::Connection> m_connections;
+};
+
+class LANGUAGECLIENT_EXPORT CurrentDocumentSymbolsRequestTaskAdapter
+ : public Tasking::TaskAdapter<CurrentDocumentSymbolsRequest>
+{
+public:
+ CurrentDocumentSymbolsRequestTaskAdapter();
+ void start() final;
+};
+
+} // namespace LanguageClient
+
+TASKING_DECLARE_TASK(CurrentDocumentSymbolsRequestTask,
+ LanguageClient::CurrentDocumentSymbolsRequestTaskAdapter);
diff --git a/src/plugins/languageclient/documentsymbolcache.cpp b/src/plugins/languageclient/documentsymbolcache.cpp
index 113b22695a..b08e70d291 100644
--- a/src/plugins/languageclient/documentsymbolcache.cpp
+++ b/src/plugins/languageclient/documentsymbolcache.cpp
@@ -40,6 +40,8 @@ DocumentSymbolCache::DocumentSymbolCache(Client *client)
void DocumentSymbolCache::requestSymbols(const DocumentUri &uri, Schedule schedule)
{
+ if (m_runningRequests.contains(uri))
+ return;
m_compressedUris.insert(uri);
switch (schedule) {
case Schedule::Now:
diff --git a/src/plugins/languageclient/languageclient.qbs b/src/plugins/languageclient/languageclient.qbs
index 9a48caf9e3..286f319a3c 100644
--- a/src/plugins/languageclient/languageclient.qbs
+++ b/src/plugins/languageclient/languageclient.qbs
@@ -24,6 +24,10 @@ QtcPlugin {
"callhierarchy.h",
"client.cpp",
"client.h",
+ "clientrequesttask.cpp",
+ "clientrequesttask.h",
+ "currentdocumentsymbolsrequest.cpp",
+ "currentdocumentsymbolsrequest.h",
"diagnosticmanager.cpp",
"diagnosticmanager.h",
"documentsymbolcache.cpp",
diff --git a/src/plugins/languageclient/languageclient_global.h b/src/plugins/languageclient/languageclient_global.h
index 0dca3e6bbf..2abb395d86 100644
--- a/src/plugins/languageclient/languageclient_global.h
+++ b/src/plugins/languageclient/languageclient_global.h
@@ -22,12 +22,25 @@ const char LANGUAGECLIENT_STDIO_SETTINGS_ID[] = "LanguageClient::StdIOSettingsID
const char LANGUAGECLIENT_SETTINGS_TR[] = QT_TRANSLATE_NOOP("QtC::LanguageClient", "Language Client");
const char LANGUAGECLIENT_DOCUMENT_FILTER_ID[] = "Current Document Symbols";
const char LANGUAGECLIENT_DOCUMENT_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::LanguageClient", "Symbols in Current Document");
+const char LANGUAGECLIENT_DOCUMENT_FILTER_DESCRIPTION[]
+ = QT_TRANSLATE_NOOP("QtC::LanguageClient",
+ "Locates symbols in the current document, based on a language server.");
const char LANGUAGECLIENT_WORKSPACE_FILTER_ID[] = "Workspace Symbols";
const char LANGUAGECLIENT_WORKSPACE_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::LanguageClient", "Symbols in Workspace");
+const char LANGUAGECLIENT_WORKSPACE_FILTER_DESCRIPTION[]
+ = QT_TRANSLATE_NOOP("QtC::LanguageClient", "Locates symbols in the language server workspace.");
const char LANGUAGECLIENT_WORKSPACE_CLASS_FILTER_ID[] = "Workspace Classes and Structs";
const char LANGUAGECLIENT_WORKSPACE_CLASS_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::LanguageClient", "Classes and Structs in Workspace");
+const char LANGUAGECLIENT_WORKSPACE_CLASS_FILTER_DESCRIPTION[]
+ = QT_TRANSLATE_NOOP("QtC::LanguageClient",
+ "Locates classes and structs in the language server workspace.");
const char LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_ID[] = "Workspace Functions and Methods";
const char LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::LanguageClient", "Functions and Methods in Workspace");
+const char LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_DESCRIPTION[]
+ = QT_TRANSLATE_NOOP("QtC::LanguageClient",
+ "Locates functions and methods in the language server workspace.");
+
+const char CALL_HIERARCHY_FACTORY_ID[] = "LanguageClient.CallHierarchy";
} // namespace Constants
} // namespace LanguageClient
diff --git a/src/plugins/languageclient/languageclientcompletionassist.cpp b/src/plugins/languageclient/languageclientcompletionassist.cpp
index f0b66facc1..c5c86122fc 100644
--- a/src/plugins/languageclient/languageclientcompletionassist.cpp
+++ b/src/plugins/languageclient/languageclientcompletionassist.cpp
@@ -447,7 +447,6 @@ IAssistProposal *LanguageClientCompletionAssistProcessor::perform()
if (!Utils::Text::convertPosition(interface()->textDocument(), m_pos, &line, &column))
return nullptr;
--line; // line is 0 based in the protocol
- --column; // column is 0 based in the protocol
params.setPosition({line, column});
params.setContext(context);
params.setTextDocument(
diff --git a/src/plugins/languageclient/languageclientinterface.cpp b/src/plugins/languageclient/languageclientinterface.cpp
index 541d1c3ab0..d42e74c9b3 100644
--- a/src/plugins/languageclient/languageclientinterface.cpp
+++ b/src/plugins/languageclient/languageclientinterface.cpp
@@ -71,7 +71,7 @@ void BaseClientInterface::parseCurrentMessage()
if (m_currentMessage.mimeType == JsonRpcMessage::jsonRpcMimeType()) {
emit messageReceived(JsonRpcMessage(m_currentMessage));
} else {
- emit error(Tr::tr("Cannot handle MIME type of message %1")
+ emit error(Tr::tr("Cannot handle MIME type \"%1\" of message.")
.arg(QString::fromUtf8(m_currentMessage.mimeType)));
}
m_currentMessage = BaseMessage();
@@ -95,14 +95,14 @@ void StdIOClientInterface::startImpl()
QTC_CHECK(!m_process->isRunning());
delete m_process;
}
- m_process = new QtcProcess;
+ m_process = new Process;
m_process->setProcessMode(ProcessMode::Writer);
- connect(m_process, &QtcProcess::readyReadStandardError,
+ connect(m_process, &Process::readyReadStandardError,
this, &StdIOClientInterface::readError);
- connect(m_process, &QtcProcess::readyReadStandardOutput,
+ connect(m_process, &Process::readyReadStandardOutput,
this, &StdIOClientInterface::readOutput);
- connect(m_process, &QtcProcess::started, this, &StdIOClientInterface::started);
- connect(m_process, &QtcProcess::done, this, [this] {
+ connect(m_process, &Process::started, this, &StdIOClientInterface::started);
+ connect(m_process, &Process::done, this, [this] {
m_logFile.flush();
if (m_process->result() != ProcessResult::FinishedWithSuccess)
emit error(QString("%1 (see logs in \"%2\")")
diff --git a/src/plugins/languageclient/languageclientinterface.h b/src/plugins/languageclient/languageclientinterface.h
index 79401f889a..7577f13691 100644
--- a/src/plugins/languageclient/languageclientinterface.h
+++ b/src/plugins/languageclient/languageclientinterface.h
@@ -8,7 +8,7 @@
#include <languageserverprotocol/jsonrpcmessages.h>
#include <utils/environment.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/temporaryfile.h>
#include <QBuffer>
@@ -52,15 +52,12 @@ private:
class LANGUAGECLIENT_EXPORT StdIOClientInterface : public BaseClientInterface
{
Q_OBJECT
+ Q_DISABLE_COPY_MOVE(StdIOClientInterface)
+
public:
StdIOClientInterface();
~StdIOClientInterface() override;
- StdIOClientInterface(const StdIOClientInterface &) = delete;
- StdIOClientInterface(StdIOClientInterface &&) = delete;
- StdIOClientInterface &operator=(const StdIOClientInterface &) = delete;
- StdIOClientInterface &operator=(StdIOClientInterface &&) = delete;
-
void startImpl() override;
// These functions only have an effect if they are called before start
@@ -74,7 +71,7 @@ protected:
void sendData(const QByteArray &data) final;
Utils::CommandLine m_cmd;
Utils::FilePath m_workingDirectory;
- Utils::QtcProcess *m_process = nullptr;
+ Utils::Process *m_process = nullptr;
Utils::Environment m_env;
private:
diff --git a/src/plugins/languageclient/languageclientmanager.cpp b/src/plugins/languageclient/languageclientmanager.cpp
index a41c4f01d6..50a3807189 100644
--- a/src/plugins/languageclient/languageclientmanager.cpp
+++ b/src/plugins/languageclient/languageclientmanager.cpp
@@ -6,18 +6,22 @@
#include "languageclientplugin.h"
#include "languageclientsymbolsupport.h"
#include "languageclienttr.h"
+#include "locatorfilter.h"
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/find/searchresultwindow.h>
#include <coreplugin/icore.h>
+#include <coreplugin/navigationwidget.h>
+
+#include <extensionsystem/pluginmanager.h>
#include <languageserverprotocol/messages.h>
#include <languageserverprotocol/progresssupport.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <texteditor/textdocument.h>
#include <texteditor/texteditor.h>
@@ -28,9 +32,9 @@
#include <utils/theme/theme.h>
#include <utils/utilsicons.h>
-#include <QTextBlock>
#include <QTimer>
+using namespace ExtensionSystem;
using namespace LanguageServerProtocol;
namespace LanguageClient {
@@ -38,11 +42,20 @@ namespace LanguageClient {
static Q_LOGGING_CATEGORY(Log, "qtc.languageclient.manager", QtWarningMsg)
static LanguageClientManager *managerInstance = nullptr;
-static bool g_shuttingDown = false;
+
+class LanguageClientManagerPrivate
+{
+ LanguageCurrentDocumentFilter m_currentDocumentFilter;
+ LanguageAllSymbolsFilter m_allSymbolsFilter;
+ LanguageClassesFilter m_classFilter;
+ LanguageFunctionsFilter m_functionFilter;
+};
LanguageClientManager::LanguageClientManager(QObject *parent)
- : QObject (parent)
+ : QObject(parent)
{
+ managerInstance = this;
+ d.reset(new LanguageClientManagerPrivate);
using namespace Core;
using namespace ProjectExplorer;
connect(EditorManager::instance(), &EditorManager::editorOpened,
@@ -55,9 +68,9 @@ LanguageClientManager::LanguageClientManager(QObject *parent)
this, &LanguageClientManager::documentContentsSaved);
connect(EditorManager::instance(), &EditorManager::aboutToSave,
this, &LanguageClientManager::documentWillSave);
- connect(SessionManager::instance(), &SessionManager::projectAdded,
+ connect(ProjectManager::instance(), &ProjectManager::projectAdded,
this, &LanguageClientManager::projectAdded);
- connect(SessionManager::instance(), &SessionManager::projectRemoved,
+ connect(ProjectManager::instance(), &ProjectManager::projectRemoved,
this, [&](Project *project) { project->disconnect(this); });
}
@@ -73,7 +86,7 @@ void LanguageClientManager::init()
if (managerInstance)
return;
QTC_ASSERT(LanguageClientPlugin::instance(), return);
- managerInstance = new LanguageClientManager(LanguageClientPlugin::instance());
+ new LanguageClientManager(LanguageClientPlugin::instance());
}
void LanguageClient::LanguageClientManager::addClient(Client *client)
@@ -91,7 +104,7 @@ void LanguageClient::LanguageClientManager::addClient(Client *client)
&Client::initialized,
managerInstance,
[client](const LanguageServerProtocol::ServerCapabilities &capabilities) {
- managerInstance->m_currentDocumentLocatorFilter.updateCurrentClient();
+ emit managerInstance->clientInitialized(client);
managerInstance->m_inspector.clientInitialized(client->name(), capabilities);
});
connect(client,
@@ -128,7 +141,7 @@ void LanguageClientManager::clientStarted(Client *client)
QTC_ASSERT(client, return);
if (client->state() != Client::Uninitialized) // do not proceed if we already received an error
return;
- if (g_shuttingDown) {
+ if (PluginManager::isShuttingDown()) {
clientFinished(client);
return;
}
@@ -154,7 +167,7 @@ void LanguageClientManager::clientFinished(Client *client)
&& client->state() != Client::ShutdownRequested;
if (unexpectedFinish) {
- if (!g_shuttingDown) {
+ if (!PluginManager::isShuttingDown()) {
const QList<TextEditor::TextDocument *> &clientDocs
= managerInstance->m_clientForDocument.keys(client);
if (client->reset()) {
@@ -176,7 +189,7 @@ void LanguageClientManager::clientFinished(Client *client)
}
}
deleteClient(client);
- if (g_shuttingDown && managerInstance->m_clients.isEmpty())
+ if (isShutdownFinished())
emit managerInstance->shutdownFinished();
}
@@ -224,18 +237,22 @@ void LanguageClientManager::deleteClient(Client *client)
managerInstance->m_clients.removeAll(client);
for (QList<Client *> &clients : managerInstance->m_clientsForSetting)
clients.removeAll(client);
- client->deleteLater();
- if (!g_shuttingDown)
+
+ // a deleteLater is not sufficient here as it pastes the delete later event at the end
+ // of the main event loop and when the plugins are shutdown we spawn an additional eventloop
+ // that will not handle the delete later event. Use invokeMethod with Qt::QueuedConnection
+ // instead.
+ QMetaObject::invokeMethod(client, [client] {delete client;}, Qt::QueuedConnection);
+ managerInstance->trackClientDeletion(client);
+
+ if (!PluginManager::isShuttingDown())
emit instance()->clientRemoved(client);
}
void LanguageClientManager::shutdown()
{
QTC_ASSERT(managerInstance, return);
- if (g_shuttingDown)
- return;
qCDebug(Log) << "shutdown manager";
- g_shuttingDown = true;
const auto clients = managerInstance->clients();
for (Client *client : clients)
shutdownClient(client);
@@ -247,11 +264,6 @@ void LanguageClientManager::shutdown()
});
}
-bool LanguageClientManager::isShuttingDown()
-{
- return g_shuttingDown;
-}
-
LanguageClientManager *LanguageClientManager::instance()
{
return managerInstance;
@@ -317,7 +329,7 @@ void LanguageClientManager::applySettings()
continue;
const Utils::FilePath filePath = textDocument->filePath();
for (ProjectExplorer::Project *project :
- ProjectExplorer::SessionManager::projects()) {
+ ProjectExplorer::ProjectManager::projects()) {
if (project->isKnownFile(filePath)) {
Client *client = clientForProject[project];
if (!client) {
@@ -448,6 +460,8 @@ QList<Client *> LanguageClientManager::reachableClients()
void LanguageClientManager::editorOpened(Core::IEditor *editor)
{
using namespace TextEditor;
+ using namespace Core;
+
if (auto *textEditor = qobject_cast<BaseTextEditor *>(editor)) {
if (TextEditorWidget *widget = textEditor->editorWidget()) {
connect(widget, &TextEditorWidget::requestLinkAt, this,
@@ -466,6 +480,14 @@ void LanguageClientManager::editorOpened(Core::IEditor *editor)
if (auto client = clientForDocument(document))
client->symbolSupport().renameSymbol(document, cursor);
});
+ connect(widget, &TextEditorWidget::requestCallHierarchy, this,
+ [this, document = textEditor->textDocument()]() {
+ if (clientForDocument(document)) {
+ emit openCallHierarchy();
+ NavigationWidget::activateSubWidget(Constants::CALL_HIERARCHY_FACTORY_ID,
+ Side::Left);
+ }
+ });
connect(widget, &TextEditorWidget::cursorPositionChanged, this, [widget]() {
if (Client *client = clientForDocument(widget->textDocument()))
if (client->reachable())
@@ -494,7 +516,7 @@ void LanguageClientManager::documentOpened(Core::IDocument *document)
if (setting->m_startBehavior == BaseSettings::RequiresProject) {
const Utils::FilePath &filePath = document->filePath();
for (ProjectExplorer::Project *project :
- ProjectExplorer::SessionManager::projects()) {
+ ProjectExplorer::ProjectManager::projects()) {
// check whether file is part of this project
if (!project->isKnownFile(filePath))
continue;
@@ -587,4 +609,24 @@ void LanguageClientManager::projectAdded(ProjectExplorer::Project *project)
client->projectOpened(project);
}
+void LanguageClientManager::trackClientDeletion(Client *client)
+{
+ QTC_ASSERT(!m_scheduledForDeletion.contains(client->id()), return);
+ m_scheduledForDeletion.insert(client->id());
+ connect(client, &QObject::destroyed, [this, id = client->id()](){
+ m_scheduledForDeletion.remove(id);
+ if (isShutdownFinished())
+ emit shutdownFinished();
+ });
+}
+
+bool LanguageClientManager::isShutdownFinished()
+{
+ if (!PluginManager::isShuttingDown())
+ return false;
+ QTC_ASSERT(managerInstance, return true);
+ return managerInstance->m_clients.isEmpty()
+ && managerInstance->m_scheduledForDeletion.isEmpty();
+}
+
} // namespace LanguageClient
diff --git a/src/plugins/languageclient/languageclientmanager.h b/src/plugins/languageclient/languageclientmanager.h
index 6ceca307a6..0a38b3d387 100644
--- a/src/plugins/languageclient/languageclientmanager.h
+++ b/src/plugins/languageclient/languageclientmanager.h
@@ -6,7 +6,6 @@
#include "client.h"
#include "languageclient_global.h"
#include "languageclientsettings.h"
-#include "locatorfilter.h"
#include "lspinspector.h"
#include <utils/algorithm.h>
@@ -25,14 +24,15 @@ namespace ProjectExplorer { class Project; }
namespace LanguageClient {
+class LanguageClientManagerPrivate;
class LanguageClientMark;
class LANGUAGECLIENT_EXPORT LanguageClientManager : public QObject
{
Q_OBJECT
+ Q_DISABLE_COPY_MOVE(LanguageClientManager)
+
public:
- LanguageClientManager(const LanguageClientManager &other) = delete;
- LanguageClientManager(LanguageClientManager &&other) = delete;
~LanguageClientManager() override;
static void init();
@@ -48,7 +48,7 @@ public:
static void deleteClient(Client *client);
static void shutdown();
- static bool isShuttingDown();
+ static bool isShutdownFinished();
static LanguageClientManager *instance();
@@ -80,8 +80,10 @@ public:
signals:
void clientAdded(Client *client);
+ void clientInitialized(Client *client);
void clientRemoved(Client *client);
void shutdownFinished();
+ void openCallHierarchy();
private:
LanguageClientManager(QObject *parent);
@@ -95,6 +97,8 @@ private:
void updateProject(ProjectExplorer::Project *project);
void projectAdded(ProjectExplorer::Project *project);
+ void trackClientDeletion(Client *client);
+
QList<Client *> reachableClients();
QList<Client *> m_clients;
@@ -102,11 +106,9 @@ private:
QList<BaseSettings *> m_currentSettings; // owned
QMap<QString, QList<Client *>> m_clientsForSetting;
QHash<TextEditor::TextDocument *, QPointer<Client>> m_clientForDocument;
- DocumentLocatorFilter m_currentDocumentLocatorFilter;
- WorkspaceLocatorFilter m_workspaceLocatorFilter;
- WorkspaceClassLocatorFilter m_workspaceClassLocatorFilter;
- WorkspaceMethodLocatorFilter m_workspaceMethodLocatorFilter;
+ std::unique_ptr<LanguageClientManagerPrivate> d;
LspInspector m_inspector;
+ QSet<Utils::Id> m_scheduledForDeletion;
};
template<typename T> bool LanguageClientManager::hasClients()
diff --git a/src/plugins/languageclient/languageclientoutline.cpp b/src/plugins/languageclient/languageclientoutline.cpp
index 0d7ae8c371..b065754ec0 100644
--- a/src/plugins/languageclient/languageclientoutline.cpp
+++ b/src/plugins/languageclient/languageclientoutline.cpp
@@ -42,67 +42,10 @@ const QList<DocumentSymbol> sortedSymbols(const QList<DocumentSymbol> &symbols)
});
}
-class LanguageClientOutlineItem : public Utils::TypedTreeItem<LanguageClientOutlineItem>
-{
-public:
- LanguageClientOutlineItem() = default;
- LanguageClientOutlineItem(const SymbolInformation &info)
- : m_name(info.name())
- , m_range(info.location().range())
- , m_type(info.kind())
- { }
-
- LanguageClientOutlineItem(const DocumentSymbol &info, const SymbolStringifier &stringifier)
- : m_name(info.name())
- , m_detail(info.detail().value_or(QString()))
- , m_range(info.range())
- , m_symbolStringifier(stringifier)
- , m_type(info.kind())
- {
- const QList<LanguageServerProtocol::DocumentSymbol> children = sortedSymbols(
- info.children().value_or(QList<DocumentSymbol>()));
- for (const DocumentSymbol &child : children)
- appendChild(new LanguageClientOutlineItem(child, stringifier));
- }
-
- // TreeItem interface
- QVariant data(int column, int role) const override
- {
- switch (role) {
- case Qt::DecorationRole:
- return symbolIcon(m_type);
- case Qt::DisplayRole:
- return m_symbolStringifier
- ? m_symbolStringifier(static_cast<SymbolKind>(m_type), m_name, m_detail)
- : m_name;
- default:
- return Utils::TreeItem::data(column, role);
- }
- }
-
- Qt::ItemFlags flags(int column) const override
- {
- Q_UNUSED(column)
- return Utils::TypedTreeItem<LanguageClientOutlineItem>::flags(column)
- | Qt::ItemIsDragEnabled;
- }
-
- Range range() const { return m_range; }
- Position pos() const { return m_range.start(); }
- bool contains(const Position &pos) const { return m_range.contains(pos); }
-
-private:
- QString m_name;
- QString m_detail;
- Range m_range;
- SymbolStringifier m_symbolStringifier;
- int m_type = -1;
-};
-
class LanguageClientOutlineModel : public Utils::TreeModel<LanguageClientOutlineItem>
{
public:
- using Utils::TreeModel<LanguageClientOutlineItem>::TreeModel;
+ LanguageClientOutlineModel(Client *client) : m_client(client) {}
void setFilePath(const Utils::FilePath &filePath) { m_filePath = filePath; }
void setInfo(const QList<SymbolInformation> &info)
@@ -115,12 +58,7 @@ public:
{
clear();
for (const DocumentSymbol &symbol : sortedSymbols(info))
- rootItem()->appendChild(new LanguageClientOutlineItem(symbol, m_symbolStringifier));
- }
-
- void setSymbolStringifier(const SymbolStringifier &stringifier)
- {
- m_symbolStringifier = stringifier;
+ rootItem()->appendChild(m_client->createOutlineItem(symbol));
}
Qt::DropActions supportedDragActions() const override
@@ -146,7 +84,7 @@ public:
}
private:
- SymbolStringifier m_symbolStringifier;
+ Client * const m_client;
Utils::FilePath m_filePath;
};
@@ -195,6 +133,7 @@ LanguageClientOutlineWidget::LanguageClientOutlineWidget(Client *client,
TextEditor::BaseTextEditor *editor)
: m_client(client)
, m_editor(editor)
+ , m_model(client)
, m_view(this)
, m_uri(m_client->hostPathToServerUri(editor->textDocument()->filePath()))
{
@@ -214,7 +153,6 @@ LanguageClientOutlineWidget::LanguageClientOutlineWidget(Client *client,
layout->setSpacing(0);
layout->addWidget(Core::ItemViewFind::createSearchableWrapper(&m_view));
setLayout(layout);
- m_model.setSymbolStringifier(m_client->symbolStringifier());
m_model.setFilePath(editor->textDocument()->filePath());
m_proxyModel.setSourceModel(&m_model);
m_view.setModel(&m_proxyModel);
@@ -373,11 +311,11 @@ Utils::TreeViewComboBox *LanguageClientOutlineWidgetFactory::createComboBox(
}
OutlineComboBox::OutlineComboBox(Client *client, TextEditor::BaseTextEditor *editor)
- : m_client(client)
+ : m_model(client)
+ , m_client(client)
, m_editorWidget(editor->editorWidget())
, m_uri(m_client->hostPathToServerUri(editor->document()->filePath()))
{
- m_model.setSymbolStringifier(client->symbolStringifier());
m_proxyModel.setSourceModel(&m_model);
const bool sorted = LanguageClientSettings::outlineComboBoxIsSorted();
m_proxyModel.sort(sorted ? 0 : -1);
@@ -455,4 +393,40 @@ void OutlineComboBox::setSorted(bool sorted)
m_proxyModel.sort(sorted ? 0 : -1);
}
+LanguageClientOutlineItem::LanguageClientOutlineItem(const SymbolInformation &info)
+ : m_name(info.name())
+ , m_range(info.location().range())
+ , m_type(info.kind())
+{ }
+
+LanguageClientOutlineItem::LanguageClientOutlineItem(Client *client, const DocumentSymbol &info)
+ : m_client(client)
+ , m_name(info.name())
+ , m_detail(info.detail().value_or(QString()))
+ , m_range(info.range())
+ , m_selectionRange(info.selectionRange())
+ , m_type(info.kind())
+{
+ const QList<LanguageServerProtocol::DocumentSymbol> children = sortedSymbols(
+ info.children().value_or(QList<DocumentSymbol>()));
+ for (const DocumentSymbol &child : children)
+ appendChild(m_client->createOutlineItem(child));
+}
+
+QVariant LanguageClientOutlineItem::data(int column, int role) const
+{
+ switch (role) {
+ case Qt::DecorationRole:
+ return symbolIcon(m_type);
+ case Qt::DisplayRole:
+ return m_name;
+ default:
+ return Utils::TreeItem::data(column, role);
+ }
+}
+Qt::ItemFlags LanguageClientOutlineItem::flags(int column) const
+{
+ Q_UNUSED(column)
+ return Utils::TypedTreeItem<LanguageClientOutlineItem>::flags(column) | Qt::ItemIsDragEnabled;
+}
} // namespace LanguageClient
diff --git a/src/plugins/languageclient/languageclientoutline.h b/src/plugins/languageclient/languageclientoutline.h
index e333563dc2..4ddc9c79c0 100644
--- a/src/plugins/languageclient/languageclientoutline.h
+++ b/src/plugins/languageclient/languageclientoutline.h
@@ -3,7 +3,11 @@
#pragma once
+#include "languageclient_global.h"
+
+#include <languageserverprotocol/lsptypes.h>
#include <texteditor/ioutlinewidget.h>
+#include <utils/treemodel.h>
namespace TextEditor {
class TextDocument;
@@ -12,6 +16,40 @@ class BaseTextEditor;
namespace Utils { class TreeViewComboBox; }
namespace LanguageClient {
+class Client;
+
+class LANGUAGECLIENT_EXPORT LanguageClientOutlineItem
+ : public Utils::TypedTreeItem<LanguageClientOutlineItem>
+{
+public:
+ LanguageClientOutlineItem() = default;
+ LanguageClientOutlineItem(const LanguageServerProtocol::SymbolInformation &info);
+ LanguageClientOutlineItem(Client *client, const LanguageServerProtocol::DocumentSymbol &info);
+
+ LanguageServerProtocol::Range range() const { return m_range; }
+ LanguageServerProtocol::Range selectionRange() const { return m_selectionRange; }
+ LanguageServerProtocol::Position pos() const { return m_range.start(); }
+ bool contains(const LanguageServerProtocol::Position &pos) const {
+ return m_range.contains(pos);
+ }
+
+protected:
+ // TreeItem interface
+ QVariant data(int column, int role) const override;
+ Qt::ItemFlags flags(int column) const override;
+
+ QString name() const { return m_name; }
+ QString detail() const { return m_detail; }
+ int type() const { return m_type; }
+
+private:
+ Client * const m_client = nullptr;
+ QString m_name;
+ QString m_detail;
+ LanguageServerProtocol::Range m_range;
+ LanguageServerProtocol::Range m_selectionRange;
+ int m_type = -1;
+};
class Client;
diff --git a/src/plugins/languageclient/languageclientplugin.cpp b/src/plugins/languageclient/languageclientplugin.cpp
index 7332f03e2b..b6b411f8cb 100644
--- a/src/plugins/languageclient/languageclientplugin.cpp
+++ b/src/plugins/languageclient/languageclientplugin.cpp
@@ -60,12 +60,12 @@ void LanguageClientPlugin::extensionsInitialized()
ExtensionSystem::IPlugin::ShutdownFlag LanguageClientPlugin::aboutToShutdown()
{
LanguageClientManager::shutdown();
- if (LanguageClientManager::clients().isEmpty())
+ if (LanguageClientManager::isShutdownFinished())
return ExtensionSystem::IPlugin::SynchronousShutdown;
QTC_ASSERT(LanguageClientManager::instance(),
return ExtensionSystem::IPlugin::SynchronousShutdown);
connect(LanguageClientManager::instance(), &LanguageClientManager::shutdownFinished,
- this, &ExtensionSystem::IPlugin::asynchronousShutdownFinished, Qt::QueuedConnection);
+ this, &ExtensionSystem::IPlugin::asynchronousShutdownFinished);
return ExtensionSystem::IPlugin::AsynchronousShutdown;
}
diff --git a/src/plugins/languageclient/languageclientsettings.cpp b/src/plugins/languageclient/languageclientsettings.cpp
index 6f2cf7dafe..650b450a5d 100644
--- a/src/plugins/languageclient/languageclientsettings.cpp
+++ b/src/plugins/languageclient/languageclientsettings.cpp
@@ -14,7 +14,7 @@
#include <coreplugin/idocument.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <texteditor/plaintexteditorfactory.h>
#include <texteditor/textmark.h>
@@ -104,17 +104,39 @@ private:
QList<BaseSettings *> m_removed;
};
-class LanguageClientSettingsPageWidget : public QWidget
+class LanguageClientSettingsPageWidget : public Core::IOptionsPageWidget
{
public:
- LanguageClientSettingsPageWidget(LanguageClientSettingsModel &settings);
+ LanguageClientSettingsPageWidget(LanguageClientSettingsModel &settings,
+ QSet<QString> &changedSettings);
+
void currentChanged(const QModelIndex &index);
int currentRow() const;
void resetCurrentSettings(int row);
void applyCurrentSettings();
+ void apply() final
+ {
+ applyCurrentSettings();
+ LanguageClientManager::applySettings();
+
+ for (BaseSettings *setting : m_model.removed()) {
+ for (Client *client : LanguageClientManager::clientsForSetting(setting))
+ LanguageClientManager::shutdownClient(client);
+ }
+
+ int row = currentRow();
+ m_model.reset(LanguageClientManager::currentSettings());
+ resetCurrentSettings(row);
+ }
+
+ void finish()
+ {
+ m_settings.reset(LanguageClientManager::currentSettings());
+ m_changedSettings.clear();
+ }
+
private:
- LanguageClientSettingsModel &m_settings;
QTreeView *m_view = nullptr;
struct CurrentSettings {
BaseSettings *setting = nullptr;
@@ -123,30 +145,10 @@ private:
void addItem(const Utils::Id &clientTypeId);
void deleteItem();
-};
-class LanguageClientSettingsPage : public Core::IOptionsPage
-{
-public:
- LanguageClientSettingsPage();
- ~LanguageClientSettingsPage() override;
-
- void init();
-
- // IOptionsPage interface
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
- QList<BaseSettings *> settings() const;
- QList<BaseSettings *> changedSettings() const;
- void addSettings(BaseSettings *settings);
- void enableSettings(const QString &id, bool enable = true);
-
-private:
+ LanguageClientSettingsModel &m_settings;
+ QSet<QString> &m_changedSettings;
LanguageClientSettingsModel m_model;
- QSet<QString> m_changedSettings;
- QPointer<LanguageClientSettingsPageWidget> m_widget;
};
QMap<Utils::Id, ClientType> &clientTypes()
@@ -155,9 +157,11 @@ QMap<Utils::Id, ClientType> &clientTypes()
return types;
}
-LanguageClientSettingsPageWidget::LanguageClientSettingsPageWidget(LanguageClientSettingsModel &settings)
- : m_settings(settings)
- , m_view(new QTreeView())
+LanguageClientSettingsPageWidget::LanguageClientSettingsPageWidget(LanguageClientSettingsModel &settings,
+ QSet<QString> &changedSettings)
+ : m_view(new QTreeView())
+ , m_settings(settings)
+ , m_changedSettings(changedSettings)
{
auto mainLayout = new QVBoxLayout();
auto layout = new QHBoxLayout();
@@ -264,6 +268,23 @@ void LanguageClientSettingsPageWidget::deleteItem()
m_settings.removeRows(index.row());
}
+class LanguageClientSettingsPage : public Core::IOptionsPage
+{
+public:
+ LanguageClientSettingsPage();
+
+ void init();
+
+ QList<BaseSettings *> settings() const;
+ QList<BaseSettings *> changedSettings() const;
+ void addSettings(BaseSettings *settings);
+ void enableSettings(const QString &id, bool enable = true);
+
+private:
+ LanguageClientSettingsModel m_model;
+ QSet<QString> m_changedSettings;
+};
+
LanguageClientSettingsPage::LanguageClientSettingsPage()
{
setId(Constants::LANGUAGECLIENT_SETTINGS_PAGE);
@@ -271,18 +292,13 @@ LanguageClientSettingsPage::LanguageClientSettingsPage()
setCategory(Constants::LANGUAGECLIENT_SETTINGS_CATEGORY);
setDisplayCategory(Tr::tr(Constants::LANGUAGECLIENT_SETTINGS_TR));
setCategoryIconPath(":/languageclient/images/settingscategory_languageclient.png");
- connect(&m_model, &LanguageClientSettingsModel::dataChanged, [this](const QModelIndex &index) {
+ setWidgetCreator([this] { return new LanguageClientSettingsPageWidget(m_model, m_changedSettings); });
+ QObject::connect(&m_model, &LanguageClientSettingsModel::dataChanged, [this](const QModelIndex &index) {
if (BaseSettings *setting = m_model.settingForIndex(index))
m_changedSettings << setting->m_id;
});
}
-LanguageClientSettingsPage::~LanguageClientSettingsPage()
-{
- if (m_widget)
- delete m_widget;
-}
-
void LanguageClientSettingsPage::init()
{
m_model.reset(LanguageClientSettings::fromSettings(Core::ICore::settings()));
@@ -290,39 +306,6 @@ void LanguageClientSettingsPage::init()
finish();
}
-QWidget *LanguageClientSettingsPage::widget()
-{
- if (!m_widget)
- m_widget = new LanguageClientSettingsPageWidget(m_model);
- return m_widget;
-}
-
-void LanguageClientSettingsPage::apply()
-{
- if (m_widget)
- m_widget->applyCurrentSettings();
- LanguageClientManager::applySettings();
-
- for (BaseSettings *setting : m_model.removed()) {
- for (Client *client : LanguageClientManager::clientsForSetting(setting))
- LanguageClientManager::shutdownClient(client);
- }
-
- if (m_widget) {
- int row = m_widget->currentRow();
- m_model.reset(LanguageClientManager::currentSettings());
- m_widget->resetCurrentSettings(row);
- } else {
- m_model.reset(LanguageClientManager::currentSettings());
- }
-}
-
-void LanguageClientSettingsPage::finish()
-{
- m_model.reset(LanguageClientManager::currentSettings());
- m_changedSettings.clear();
-}
-
QList<BaseSettings *> LanguageClientSettingsPage::settings() const
{
return m_model.settings();
@@ -1047,6 +1030,7 @@ bool LanguageFilter::operator!=(const LanguageFilter &other) const
TextEditor::BaseTextEditor *jsonEditor()
{
using namespace TextEditor;
+ using namespace Utils::Text;
BaseTextEditor *editor = PlainTextEditorFactory::createPlainTextEditor();
TextDocument *document = editor->textDocument();
TextEditorWidget *widget = editor->editorWidget();
@@ -1069,12 +1053,11 @@ TextEditor::BaseTextEditor *jsonEditor()
QJsonDocument::fromJson(content.toUtf8(), &error);
if (error.error == QJsonParseError::NoError)
return;
- const Utils::OptionalLineColumn lineColumn
- = Utils::Text::convertPosition(document->document(), error.offset);
- if (!lineColumn.has_value())
+ const Position pos = Position::fromPositionInDocument(document->document(), error.offset);
+ if (!pos.isValid())
return;
auto mark = new TextMark(Utils::FilePath(),
- lineColumn->line,
+ pos.line,
{::LanguageClient::Tr::tr("JSON Error"), jsonMarkId});
mark->setLineAnnotation(error.errorString());
mark->setColor(Utils::Theme::CodeModel_Error_TextMarkColor);
diff --git a/src/plugins/languageclient/languageclientsymbolsupport.cpp b/src/plugins/languageclient/languageclientsymbolsupport.cpp
index b5f301d443..7069360b02 100644
--- a/src/plugins/languageclient/languageclientsymbolsupport.cpp
+++ b/src/plugins/languageclient/languageclientsymbolsupport.cpp
@@ -13,7 +13,7 @@
#include <coreplugin/find/searchresultwindow.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/algorithm.h>
#include <utils/mimeutils.h>
@@ -193,7 +193,7 @@ bool SymbolSupport::supportsFindUsages(TextEditor::TextDocument *document) const
struct ItemData
{
- Core::Search::TextRange range;
+ Utils::Text::Range range;
QVariant userData;
};
@@ -216,12 +216,12 @@ QStringList SymbolSupport::getFileContents(const Utils::FilePath &filePath)
return fileContent.split("\n");
}
-QList<Core::SearchResultItem> generateSearchResultItems(
+Utils::SearchResultItems generateSearchResultItems(
const QMap<Utils::FilePath, QList<ItemData>> &rangesInDocument,
Core::SearchResult *search = nullptr,
bool limitToProjects = false)
{
- QList<Core::SearchResultItem> result;
+ Utils::SearchResultItems result;
const bool renaming = search && search->supportsReplace();
QString oldSymbolName;
QVariantList userData;
@@ -233,11 +233,11 @@ QList<Core::SearchResultItem> generateSearchResultItems(
for (auto it = rangesInDocument.begin(); it != rangesInDocument.end(); ++it) {
const Utils::FilePath &filePath = it.key();
- Core::SearchResultItem item;
+ Utils::SearchResultItem item;
item.setFilePath(filePath);
item.setUseTextEditorFont(true);
if (renaming && limitToProjects) {
- const bool fileBelongsToProject = ProjectExplorer::SessionManager::projectForFile(
+ const bool fileBelongsToProject = ProjectExplorer::ProjectManager::projectForFile(
filePath);
item.setSelectForReplacement(fileBelongsToProject);
if (fileBelongsToProject
@@ -264,7 +264,7 @@ QList<Core::SearchResultItem> generateSearchResultItems(
return result;
}
-QList<Core::SearchResultItem> generateSearchResultItems(
+Utils::SearchResultItems generateSearchResultItems(
const LanguageClientArray<Location> &locations, const DocumentUri::PathMapper &pathMapper)
{
if (locations.isNull())
@@ -291,7 +291,7 @@ void SymbolSupport::handleFindReferencesResponse(const FindReferencesRequest::Re
Tr::tr("Find References with %1 for:").arg(m_client->name()), "", wordUnderCursor);
search->addResults(generateSearchResultItems(*result, m_client->hostPathMapper()),
Core::SearchResult::AddOrdered);
- connect(search, &Core::SearchResult::activated, [](const Core::SearchResultItem &item) {
+ connect(search, &Core::SearchResult::activated, [](const Utils::SearchResultItem &item) {
Core::EditorManager::openEditorAtSearchResult(item);
});
search->finishSearch(false);
@@ -463,7 +463,7 @@ void SymbolSupport::requestRename(const TextDocumentPositionParams &positionPara
search->popup();
}
-QList<Core::SearchResultItem> generateReplaceItems(const WorkspaceEdit &edits,
+Utils::SearchResultItems generateReplaceItems(const WorkspaceEdit &edits,
Core::SearchResult *search,
bool limitToProjects,
const DocumentUri::PathMapper &pathMapper)
@@ -506,7 +506,7 @@ Core::SearchResult *SymbolSupport::createSearch(const TextDocumentPositionParams
if (callback)
search->makeNonInteractive(callback);
- connect(search, &Core::SearchResult::activated, [](const Core::SearchResultItem &item) {
+ connect(search, &Core::SearchResult::activated, [](const Utils::SearchResultItem &item) {
Core::EditorManager::openEditorAtSearchResult(item);
});
connect(search, &Core::SearchResult::replaceTextChanged, this, [this, search, positionParams]() {
@@ -524,7 +524,7 @@ Core::SearchResult *SymbolSupport::createSearch(const TextDocumentPositionParams
connect(search, &Core::SearchResult::replaceButtonClicked, this,
[this, search, resetConnection](const QString & /*replaceText*/,
- const QList<Core::SearchResultItem> &checkedItems) {
+ const Utils::SearchResultItems &checkedItems) {
applyRename(checkedItems, search);
disconnect(resetConnection);
});
@@ -571,12 +571,12 @@ void SymbolSupport::handleRenameResponse(Core::SearchResult *search,
}
}
-void SymbolSupport::applyRename(const QList<Core::SearchResultItem> &checkedItems,
+void SymbolSupport::applyRename(const Utils::SearchResultItems &checkedItems,
Core::SearchResult *search)
{
QSet<Utils::FilePath> affectedNonOpenFilePaths;
QMap<Utils::FilePath, QList<TextEdit>> editsForDocuments;
- for (const Core::SearchResultItem &item : checkedItems) {
+ for (const Utils::SearchResultItem &item : checkedItems) {
const auto filePath = Utils::FilePath::fromString(item.path().value(0));
if (!m_client->documentForFilePath(filePath))
affectedNonOpenFilePaths << filePath;
@@ -616,12 +616,12 @@ QString SymbolSupport::derivePlaceholder(const QString &oldSymbol, const QString
return m_defaultSymbolMapper ? m_defaultSymbolMapper(oldSymbol) : oldSymbol;
}
-Core::Search::TextRange SymbolSupport::convertRange(const Range &range)
+Utils::Text::Range SymbolSupport::convertRange(const Range &range)
{
- auto convertPosition = [](const Position &pos) {
- return Core::Search::TextPosition(pos.line() + 1, pos.character());
+ const auto convertPosition = [](const Position &pos) {
+ return Utils::Text::Position{pos.line() + 1, pos.character()};
};
- return Core::Search::TextRange(convertPosition(range.start()), convertPosition(range.end()));
+ return {convertPosition(range.start()), convertPosition(range.end())};
}
void SymbolSupport::setDefaultRenamingSymbolMapper(const SymbolMapper &mapper)
diff --git a/src/plugins/languageclient/languageclientsymbolsupport.h b/src/plugins/languageclient/languageclientsymbolsupport.h
index a4b910a9db..3dcc7b0ddc 100644
--- a/src/plugins/languageclient/languageclientsymbolsupport.h
+++ b/src/plugins/languageclient/languageclientsymbolsupport.h
@@ -5,18 +5,15 @@
#include "languageclient_global.h"
-#include <coreplugin/find/searchresultitem.h>
#include <texteditor/textdocument.h>
#include <languageserverprotocol/languagefeatures.h>
-#include <functional>
+#include <utils/searchresultitem.h>
-namespace Core {
-class SearchResult;
-class SearchResultItem;
-}
+#include <functional>
+namespace Core { class SearchResult; }
namespace LanguageServerProtocol { class MessageId; }
namespace LanguageClient {
@@ -46,7 +43,7 @@ public:
const std::function<void()> &callback = {},
bool preferLowerCaseFileNames = true);
- static Core::Search::TextRange convertRange(const LanguageServerProtocol::Range &range);
+ static Utils::Text::Range convertRange(const LanguageServerProtocol::Range &range);
static QStringList getFileContents(const Utils::FilePath &filePath);
using SymbolMapper = std::function<QString(const QString &)>;
@@ -76,7 +73,7 @@ private:
const std::function<void()> &callback, bool preferLowerCaseFileNames);
void handleRenameResponse(Core::SearchResult *search,
const LanguageServerProtocol::RenameRequest::Response &response);
- void applyRename(const QList<Core::SearchResultItem> &checkedItems, Core::SearchResult *search);
+ void applyRename(const Utils::SearchResultItems &checkedItems, Core::SearchResult *search);
QString derivePlaceholder(const QString &oldSymbol, const QString &newSymbol);
Client *m_client = nullptr;
diff --git a/src/plugins/languageclient/languageclientutils.cpp b/src/plugins/languageclient/languageclientutils.cpp
index 44168d1dd2..c421b2a937 100644
--- a/src/plugins/languageclient/languageclientutils.cpp
+++ b/src/plugins/languageclient/languageclientutils.cpp
@@ -97,11 +97,10 @@ void applyTextEdit(TextDocumentManipulatorInterface &manipulator,
const TextEdit &edit,
bool newTextIsSnippet)
{
- using namespace Utils::Text;
const Range range = edit.range();
const QTextDocument *doc = manipulator.textCursorAt(manipulator.currentPosition()).document();
- const int start = positionInText(doc, range.start().line() + 1, range.start().character() + 1);
- const int end = positionInText(doc, range.end().line() + 1, range.end().character() + 1);
+ const int start = Text::positionInText(doc, range.start().line() + 1, range.start().character() + 1);
+ const int end = Text::positionInText(doc, range.end().line() + 1, range.end().character() + 1);
if (newTextIsSnippet) {
manipulator.replace(start, end - start, {});
manipulator.insertCodeSnippet(start, edit.newText(), &parseSnippet);
diff --git a/src/plugins/languageclient/locatorfilter.cpp b/src/plugins/languageclient/locatorfilter.cpp
index ddcfc6b43b..da03b2e7c5 100644
--- a/src/plugins/languageclient/locatorfilter.cpp
+++ b/src/plugins/languageclient/locatorfilter.cpp
@@ -3,358 +3,290 @@
#include "locatorfilter.h"
-#include "documentsymbolcache.h"
-#include "languageclient_global.h"
+#include "clientrequesttask.h"
+#include "currentdocumentsymbolsrequest.h"
#include "languageclientmanager.h"
#include "languageclienttr.h"
-#include "languageclientutils.h"
-#include <coreplugin/editormanager/editormanager.h>
-
-#include <languageserverprotocol/lsptypes.h>
-#include <languageserverprotocol/servercapabilities.h>
-
-#include <texteditor/textdocument.h>
-#include <texteditor/texteditor.h>
+#include <extensionsystem/pluginmanager.h>
+#include <utils/async.h>
#include <utils/fuzzymatcher.h>
-#include <utils/linecolumn.h>
-#include <QFutureWatcher>
#include <QRegularExpression>
+using namespace Core;
using namespace LanguageServerProtocol;
+using namespace Utils;
namespace LanguageClient {
-DocumentLocatorFilter::DocumentLocatorFilter()
+void filterResults(QPromise<void> &promise, const LocatorStorage &storage, Client *client,
+ const QList<SymbolInformation> &results, const QList<SymbolKind> &filter)
{
- setId(Constants::LANGUAGECLIENT_DOCUMENT_FILTER_ID);
- setDisplayName(Tr::tr(Constants::LANGUAGECLIENT_DOCUMENT_FILTER_DISPLAY_NAME));
- setDescription(
- Tr::tr("Matches all symbols from the current document, based on a language server."));
- setDefaultShortcutString(".");
- setDefaultIncludedByDefault(false);
- setPriority(ILocatorFilter::Low);
- connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
- this, &DocumentLocatorFilter::updateCurrentClient);
-}
-
-void DocumentLocatorFilter::updateCurrentClient()
-{
- resetSymbols();
- disconnect(m_resetSymbolsConnection);
-
- TextEditor::TextDocument *document = TextEditor::TextDocument::currentTextDocument();
- if (Client *client = LanguageClientManager::clientForDocument(document);
- client && (client->locatorsEnabled() || m_forced)) {
-
- setEnabled(!m_forced);
- if (m_symbolCache != client->documentSymbolCache()) {
- disconnect(m_updateSymbolsConnection);
- m_symbolCache = client->documentSymbolCache();
- m_updateSymbolsConnection = connect(m_symbolCache, &DocumentSymbolCache::gotSymbols,
- this, &DocumentLocatorFilter::updateSymbols);
- }
- m_resetSymbolsConnection = connect(document, &Core::IDocument::contentsChanged,
- this, &DocumentLocatorFilter::resetSymbols);
- m_currentUri = client->hostPathToServerUri(document->filePath());
- m_pathMapper = client->hostPathMapper();
- } else {
- disconnect(m_updateSymbolsConnection);
- m_symbolCache.clear();
- m_currentUri.clear();
- setEnabled(false);
- m_pathMapper = DocumentUri::PathMapper();
- }
-}
-
-void DocumentLocatorFilter::updateSymbols(const DocumentUri &uri,
- const DocumentSymbolsResult &symbols)
-{
- if (uri != m_currentUri)
+ const auto doFilter = [&](const SymbolInformation &info) {
+ return filter.contains(SymbolKind(info.kind()));
+ };
+ if (promise.isCanceled())
return;
- QMutexLocker locker(&m_mutex);
- m_currentSymbols = symbols;
- emit symbolsUpToDate(QPrivateSignal());
+ const QList<SymbolInformation> filteredResults = filter.isEmpty() ? results
+ : Utils::filtered(results, doFilter);
+ const auto generateEntry = [client](const SymbolInformation &info) {
+ LocatorFilterEntry entry;
+ entry.displayName = info.name();
+ if (std::optional<QString> container = info.containerName())
+ entry.extraInfo = container.value_or(QString());
+ entry.displayIcon = symbolIcon(info.kind());
+ entry.linkForEditor = info.location().toLink(client->hostPathMapper());
+ return entry;
+ };
+ storage.reportOutput(Utils::transform(filteredResults, generateEntry));
}
-void DocumentLocatorFilter::resetSymbols()
+LocatorMatcherTask locatorMatcher(Client *client, int maxResultCount,
+ const QList<SymbolKind> &filter)
{
- QMutexLocker locker(&m_mutex);
- m_currentSymbols.reset();
-}
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+ TreeStorage<QList<SymbolInformation>> resultStorage;
+
+ const auto onQuerySetup = [storage, client, maxResultCount](WorkspaceSymbolRequestTask &request) {
+ request.setClient(client);
+ WorkspaceSymbolParams params;
+ params.setQuery(storage->input());
+ if (maxResultCount > 0)
+ params.setLimit(maxResultCount);
+ request.setParams(params);
+ };
+ const auto onQueryDone = [resultStorage](const WorkspaceSymbolRequestTask &request) {
+ const std::optional<LanguageClientArray<SymbolInformation>> result
+ = request.response().result();
+ if (result.has_value())
+ *resultStorage = result->toList();
+ };
-static Core::LocatorFilterEntry generateLocatorEntry(const SymbolInformation &info,
- Core::ILocatorFilter *filter,
- DocumentUri::PathMapper pathMapper)
-{
- Core::LocatorFilterEntry entry;
- entry.filter = filter;
- entry.displayName = info.name();
- if (std::optional<QString> container = info.containerName())
- entry.extraInfo = container.value_or(QString());
- entry.displayIcon = symbolIcon(info.kind());
- entry.internalData = QVariant::fromValue(info.location().toLink(pathMapper));
- return entry;
-}
+ const auto onFilterSetup = [storage, resultStorage, client, filter](Async<void> &async) {
+ const QList<SymbolInformation> results = *resultStorage;
+ if (results.isEmpty())
+ return TaskAction::StopWithDone;
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(filterResults, *storage, client, results, filter);
+ return TaskAction::Continue;
+ };
-Core::LocatorFilterEntry DocumentLocatorFilter::generateLocatorEntry(const SymbolInformation &info)
-{
- QTC_ASSERT(m_pathMapper, return {});
- return LanguageClient::generateLocatorEntry(info, this, m_pathMapper);
+ const Group root {
+ Storage(resultStorage),
+ SymbolRequest(onQuerySetup, onQueryDone),
+ AsyncTask<void>(onFilterSetup)
+ };
+ return {root, storage};
}
-QList<Core::LocatorFilterEntry> DocumentLocatorFilter::generateLocatorEntries(
- const SymbolInformation &info, const QRegularExpression &regexp,
- const Core::LocatorFilterEntry &parent)
+LocatorMatcherTask allSymbolsMatcher(Client *client, int maxResultCount)
{
- Q_UNUSED(parent)
- if (regexp.match(info.name()).hasMatch())
- return {generateLocatorEntry(info)};
- return {};
+ return locatorMatcher(client, maxResultCount, {});
}
-Core::LocatorFilterEntry DocumentLocatorFilter::generateLocatorEntry(
- const DocumentSymbol &info,
- const Core::LocatorFilterEntry &parent)
+LocatorMatcherTask classMatcher(Client *client, int maxResultCount)
{
- Q_UNUSED(parent)
- Core::LocatorFilterEntry entry;
- entry.filter = this;
- entry.displayName = info.name();
- if (std::optional<QString> detail = info.detail())
- entry.extraInfo = detail.value_or(QString());
- entry.displayIcon = symbolIcon(info.kind());
- const Position &pos = info.range().start();
- entry.internalData = QVariant::fromValue(Utils::LineColumn(pos.line(), pos.character()));
- return entry;
+ return locatorMatcher(client, maxResultCount, {SymbolKind::Class, SymbolKind::Struct});
}
-QList<Core::LocatorFilterEntry> DocumentLocatorFilter::generateLocatorEntries(
- const DocumentSymbol &info, const QRegularExpression &regexp,
- const Core::LocatorFilterEntry &parent)
+LocatorMatcherTask functionMatcher(Client *client, int maxResultCount)
{
- QList<Core::LocatorFilterEntry> entries;
- const QList<DocumentSymbol> children = info.children().value_or(QList<DocumentSymbol>());
- const bool hasMatch = regexp.match(info.name()).hasMatch();
- Core::LocatorFilterEntry entry;
- if (hasMatch || !children.isEmpty())
- entry = generateLocatorEntry(info, parent);
- if (hasMatch)
- entries << entry;
- for (const DocumentSymbol &child : children)
- entries << generateLocatorEntries(child, regexp, entry);
- return entries;
+ return locatorMatcher(client, maxResultCount,
+ {SymbolKind::Method, SymbolKind::Function, SymbolKind::Constructor});
}
-template<class T>
-QList<Core::LocatorFilterEntry> DocumentLocatorFilter::generateEntries(const QList<T> &list,
- const QString &filter)
+static void filterCurrentResults(QPromise<void> &promise, const LocatorStorage &storage,
+ const CurrentDocumentSymbolsData &currentSymbolsData)
{
- QList<Core::LocatorFilterEntry> entries;
- FuzzyMatcher::CaseSensitivity caseSensitivity
- = ILocatorFilter::caseSensitivity(filter) == Qt::CaseSensitive
- ? FuzzyMatcher::CaseSensitivity::CaseSensitive
- : FuzzyMatcher::CaseSensitivity::CaseInsensitive;
- const QRegularExpression regexp = FuzzyMatcher::createRegExp(filter, caseSensitivity);
- if (!regexp.isValid())
- return entries;
-
- for (const T &item : list)
- entries << generateLocatorEntries(item, regexp, {});
- return entries;
+ Q_UNUSED(promise)
+ const auto docSymbolModifier = [](LocatorFilterEntry &entry, const DocumentSymbol &info,
+ const LocatorFilterEntry &parent) {
+ Q_UNUSED(parent)
+ entry.displayName = info.name();
+ if (std::optional<QString> detail = info.detail())
+ entry.extraInfo = *detail;
+ };
+ // TODO: Pass promise into currentSymbols
+ storage.reportOutput(LanguageClient::currentDocumentSymbols(storage.input(), currentSymbolsData,
+ docSymbolModifier));
}
-void DocumentLocatorFilter::prepareSearch(const QString &/*entry*/)
+LocatorMatcherTask currentDocumentMatcher()
{
- QMutexLocker locker(&m_mutex);
- if (m_symbolCache && !m_currentSymbols.has_value()) {
- locker.unlock();
- m_symbolCache->requestSymbols(m_currentUri, Schedule::Now);
- }
-}
+ using namespace Tasking;
-QList<Core::LocatorFilterEntry> DocumentLocatorFilter::matchesFor(
- QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
-{
- QMutexLocker locker(&m_mutex);
- if (!m_symbolCache)
- return {};
- if (!m_currentSymbols.has_value()) {
- QEventLoop loop;
- connect(this, &DocumentLocatorFilter::symbolsUpToDate, &loop, [&]() { loop.exit(1); });
- QFutureWatcher<Core::LocatorFilterEntry> watcher;
- connect(&watcher,
- &QFutureWatcher<Core::LocatorFilterEntry>::canceled,
- &loop,
- &QEventLoop::quit);
- watcher.setFuture(future.future());
- locker.unlock();
- if (!loop.exec())
- return {};
- locker.relock();
- }
+ TreeStorage<LocatorStorage> storage;
+ TreeStorage<CurrentDocumentSymbolsData> resultStorage;
- QTC_ASSERT(m_currentSymbols.has_value(), return {});
+ const auto onQuerySetup = [](CurrentDocumentSymbolsRequest &request) {
+ Q_UNUSED(request)
+ };
+ const auto onQueryDone = [resultStorage](const CurrentDocumentSymbolsRequest &request) {
+ *resultStorage = request.currentDocumentSymbolsData();
+ };
- if (auto list = std::get_if<QList<DocumentSymbol>>(&*m_currentSymbols))
- return generateEntries(*list, entry);
- else if (auto list = std::get_if<QList<SymbolInformation>>(&*m_currentSymbols))
- return generateEntries(*list, entry);
+ const auto onFilterSetup = [storage, resultStorage](Async<void> &async) {
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(filterCurrentResults, *storage, *resultStorage);
+ };
- return {};
+ const Group root {
+ Storage(resultStorage),
+ CurrentDocumentSymbolsRequestTask(onQuerySetup, onQueryDone),
+ AsyncTask<void>(onFilterSetup)
+ };
+ return {root, storage};
}
-void DocumentLocatorFilter::accept(const Core::LocatorFilterEntry &selection,
- QString * /*newText*/,
- int * /*selectionStart*/,
- int * /*selectionLength*/) const
+using MatcherCreator = std::function<Core::LocatorMatcherTask(Client *, int)>;
+
+static MatcherCreator creatorForType(MatcherType type)
{
- if (selection.internalData.canConvert<Utils::LineColumn>()) {
- QTC_ASSERT(m_pathMapper, return);
- auto lineColumn = qvariant_cast<Utils::LineColumn>(selection.internalData);
- const Utils::Link link(m_currentUri.toFilePath(m_pathMapper),
- lineColumn.line + 1,
- lineColumn.column);
- Core::EditorManager::openEditorAt(link, {}, Core::EditorManager::AllowExternalEditor);
- } else if (selection.internalData.canConvert<Utils::Link>()) {
- Core::EditorManager::openEditorAt(qvariant_cast<Utils::Link>(selection.internalData),
- {},
- Core::EditorManager::AllowExternalEditor);
+ switch (type) {
+ case MatcherType::AllSymbols: return &allSymbolsMatcher;
+ case MatcherType::Classes: return &classMatcher;
+ case MatcherType::Functions: return &functionMatcher;
+ case MatcherType::CurrentDocumentSymbols: QTC_CHECK(false); return {};
}
+ return {};
}
-WorkspaceLocatorFilter::WorkspaceLocatorFilter()
- : WorkspaceLocatorFilter(QVector<SymbolKind>())
-{}
+LocatorMatcherTasks languageClientMatchers(MatcherType type, const QList<Client *> &clients,
+ int maxResultCount)
+{
+ if (type == MatcherType::CurrentDocumentSymbols)
+ return {currentDocumentMatcher()};
+ const MatcherCreator creator = creatorForType(type);
+ if (!creator)
+ return {};
+ LocatorMatcherTasks matchers;
+ for (Client *client : clients)
+ matchers << creator(client, maxResultCount);
+ return matchers;
+}
-WorkspaceLocatorFilter::WorkspaceLocatorFilter(const QVector<SymbolKind> &filter)
- : m_filterKinds(filter)
+LanguageCurrentDocumentFilter::LanguageCurrentDocumentFilter()
{
- setId(Constants::LANGUAGECLIENT_WORKSPACE_FILTER_ID);
- setDisplayName(Tr::tr(Constants::LANGUAGECLIENT_WORKSPACE_FILTER_DISPLAY_NAME));
- setDefaultShortcutString(":");
- setDefaultIncludedByDefault(false);
+ setId(Constants::LANGUAGECLIENT_DOCUMENT_FILTER_ID);
+ setDisplayName(Tr::tr(Constants::LANGUAGECLIENT_DOCUMENT_FILTER_DISPLAY_NAME));
+ setDescription(Tr::tr(Constants::LANGUAGECLIENT_DOCUMENT_FILTER_DESCRIPTION));
+ setDefaultShortcutString(".");
setPriority(ILocatorFilter::Low);
}
-void WorkspaceLocatorFilter::prepareSearch(const QString &entry)
+LocatorMatcherTasks LanguageCurrentDocumentFilter::matchers()
{
- prepareSearch(entry, LanguageClientManager::clients(), false);
+ return {currentDocumentMatcher()};
}
-void WorkspaceLocatorFilter::prepareSearch(const QString &entry, const QList<Client *> &clients)
+static LocatorFilterEntry entryForSymbolInfo(const SymbolInformation &info,
+ const DocumentUri::PathMapper &pathMapper)
{
- prepareSearch(entry, clients, true);
+ LocatorFilterEntry entry;
+ entry.displayName = info.name();
+ if (std::optional<QString> container = info.containerName())
+ entry.extraInfo = container.value_or(QString());
+ entry.displayIcon = symbolIcon(info.kind());
+ entry.linkForEditor = info.location().toLink(pathMapper);
+ return entry;
}
-void WorkspaceLocatorFilter::prepareSearch(const QString &entry,
- const QList<Client *> &clients,
- bool force)
+LocatorFilterEntries entriesForSymbolsInfo(const QList<SymbolInformation> &infoList,
+ const QRegularExpression &regexp, const DocumentUri::PathMapper &pathMapper)
{
- m_pendingRequests.clear();
- m_results.clear();
-
- WorkspaceSymbolParams params;
- params.setQuery(entry);
- if (m_maxResultCount > 0)
- params.setLimit(m_maxResultCount);
-
- QMutexLocker locker(&m_mutex);
- for (auto client : std::as_const(clients)) {
- if (!client->reachable())
- continue;
- if (!(force || client->locatorsEnabled()))
- continue;
- std::optional<std::variant<bool, WorkDoneProgressOptions>> capability
- = client->capabilities().workspaceSymbolProvider();
- if (!capability.has_value())
- continue;
- if (std::holds_alternative<bool>(*capability) && !std::get<bool>(*capability))
- continue;
- WorkspaceSymbolRequest request(params);
- request.setResponseCallback(
- [this, client](const WorkspaceSymbolRequest::Response &response) {
- handleResponse(client, response);
- });
- m_pendingRequests[client] = request.id();
- client->sendMessage(request);
+ QTC_ASSERT(pathMapper, return {});
+ LocatorFilterEntries entries;
+ for (const SymbolInformation &info : infoList) {
+ if (regexp.match(info.name()).hasMatch())
+ entries << LanguageClient::entryForSymbolInfo(info, pathMapper);
}
+ return entries;
}
-QList<Core::LocatorFilterEntry> WorkspaceLocatorFilter::matchesFor(
- QFutureInterface<Core::LocatorFilterEntry> &future, const QString & /*entry*/)
+LocatorFilterEntries entriesForDocSymbols(const QList<DocumentSymbol> &infoList,
+ const QRegularExpression &regexp, const FilePath &filePath,
+ const DocSymbolModifier &docSymbolModifier, const LocatorFilterEntry &parent = {})
{
- QMutexLocker locker(&m_mutex);
- if (!m_pendingRequests.isEmpty()) {
- QEventLoop loop;
- connect(this, &WorkspaceLocatorFilter::allRequestsFinished, &loop, [&]() { loop.exit(1); });
- QFutureWatcher<Core::LocatorFilterEntry> watcher;
- connect(&watcher,
- &QFutureWatcher<Core::LocatorFilterEntry>::canceled,
- &loop,
- &QEventLoop::quit);
- watcher.setFuture(future.future());
- locker.unlock();
- if (!loop.exec())
- return {};
-
- locker.relock();
+ LocatorFilterEntries entries;
+ for (const DocumentSymbol &info : infoList) {
+ const QList<DocumentSymbol> children = info.children().value_or(QList<DocumentSymbol>());
+ const bool hasMatch = regexp.match(info.name()).hasMatch();
+ LocatorFilterEntry entry;
+ if (hasMatch) {
+ entry.displayIcon = LanguageClient::symbolIcon(info.kind());
+ const Position &pos = info.range().start();
+ entry.linkForEditor = {filePath, pos.line() + 1, pos.character()};
+ docSymbolModifier(entry, info, parent);
+ entries << entry;
+ } else {
+ entry = parent;
+ }
+ entries << entriesForDocSymbols(children, regexp, filePath, docSymbolModifier, entry);
}
+ return entries;
+}
+Core::LocatorFilterEntries currentDocumentSymbols(const QString &input,
+ const CurrentDocumentSymbolsData &currentSymbolsData,
+ const DocSymbolModifier &docSymbolModifier)
+{
+ const Qt::CaseSensitivity caseSensitivity = ILocatorFilter::caseSensitivity(input);
+ const QRegularExpression regExp = ILocatorFilter::createRegExp(input, caseSensitivity);
+ if (!regExp.isValid())
+ return {};
- if (!m_filterKinds.isEmpty()) {
- m_results = Utils::filtered(m_results, [&](const SymbolInfoWithPathMapper &info) {
- return m_filterKinds.contains(SymbolKind(info.symbol.kind()));
- });
- }
- auto generateEntry = [this](const SymbolInfoWithPathMapper &info) {
- return generateLocatorEntry(info.symbol, this, info.mapper);
- };
- return Utils::transform(m_results, generateEntry).toList();
+ if (auto list = std::get_if<QList<DocumentSymbol>>(&currentSymbolsData.m_symbols))
+ return entriesForDocSymbols(*list, regExp, currentSymbolsData.m_filePath, docSymbolModifier);
+ else if (auto list = std::get_if<QList<SymbolInformation>>(&currentSymbolsData.m_symbols))
+ return entriesForSymbolsInfo(*list, regExp, currentSymbolsData.m_pathMapper);
+ return {};
}
-void WorkspaceLocatorFilter::accept(const Core::LocatorFilterEntry &selection,
- QString * /*newText*/,
- int * /*selectionStart*/,
- int * /*selectionLength*/) const
+LanguageAllSymbolsFilter::LanguageAllSymbolsFilter()
{
- if (selection.internalData.canConvert<Utils::Link>())
- Core::EditorManager::openEditorAt(qvariant_cast<Utils::Link>(selection.internalData),
- {},
- Core::EditorManager::AllowExternalEditor);
+ setId(Constants::LANGUAGECLIENT_WORKSPACE_FILTER_ID);
+ setDisplayName(Tr::tr(Constants::LANGUAGECLIENT_WORKSPACE_FILTER_DISPLAY_NAME));
+ setDescription(Tr::tr(Constants::LANGUAGECLIENT_WORKSPACE_FILTER_DESCRIPTION));
+ setDefaultShortcutString(":");
+ setPriority(ILocatorFilter::Low);
}
-void WorkspaceLocatorFilter::handleResponse(Client *client,
- const WorkspaceSymbolRequest::Response &response)
+LocatorMatcherTasks LanguageAllSymbolsFilter::matchers()
{
- QMutexLocker locker(&m_mutex);
- m_pendingRequests.remove(client);
- auto result = response.result().value_or(LanguageClientArray<SymbolInformation>());
- if (!result.isNull())
- m_results.append(
- Utils::transform(result.toList().toVector(), [client](const SymbolInformation &info) {
- return SymbolInfoWithPathMapper{info, client->hostPathMapper()};
- }));
- if (m_pendingRequests.isEmpty())
- emit allRequestsFinished(QPrivateSignal());
+ return languageClientMatchers(MatcherType::AllSymbols,
+ Utils::filtered(LanguageClientManager::clients(), &Client::locatorsEnabled));
}
-WorkspaceClassLocatorFilter::WorkspaceClassLocatorFilter()
- : WorkspaceLocatorFilter({SymbolKind::Class, SymbolKind::Struct})
+LanguageClassesFilter::LanguageClassesFilter()
{
setId(Constants::LANGUAGECLIENT_WORKSPACE_CLASS_FILTER_ID);
setDisplayName(Tr::tr(Constants::LANGUAGECLIENT_WORKSPACE_CLASS_FILTER_DISPLAY_NAME));
+ setDescription(Tr::tr(Constants::LANGUAGECLIENT_WORKSPACE_CLASS_FILTER_DESCRIPTION));
setDefaultShortcutString("c");
}
-WorkspaceMethodLocatorFilter::WorkspaceMethodLocatorFilter()
- : WorkspaceLocatorFilter({SymbolKind::Method, SymbolKind::Function, SymbolKind::Constructor})
+LocatorMatcherTasks LanguageClassesFilter::matchers()
+{
+ return languageClientMatchers(MatcherType::Classes,
+ Utils::filtered(LanguageClientManager::clients(), &Client::locatorsEnabled));
+}
+
+LanguageFunctionsFilter::LanguageFunctionsFilter()
{
setId(Constants::LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_ID);
setDisplayName(Tr::tr(Constants::LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_DISPLAY_NAME));
+ setDescription(Tr::tr(Constants::LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_DESCRIPTION));
setDefaultShortcutString("m");
}
+LocatorMatcherTasks LanguageFunctionsFilter::matchers()
+{
+ return languageClientMatchers(MatcherType::Functions,
+ Utils::filtered(LanguageClientManager::clients(), &Client::locatorsEnabled));
+}
+
} // namespace LanguageClient
diff --git a/src/plugins/languageclient/locatorfilter.h b/src/plugins/languageclient/locatorfilter.h
index 3eebd907a2..39d135723c 100644
--- a/src/plugins/languageclient/locatorfilter.h
+++ b/src/plugins/languageclient/locatorfilter.h
@@ -3,129 +3,60 @@
#pragma once
-#include "client.h"
#include "languageclient_global.h"
#include <coreplugin/locator/ilocatorfilter.h>
-#include <languageserverprotocol/languagefeatures.h>
-#include <languageserverprotocol/lsptypes.h>
-#include <languageserverprotocol/workspace.h>
-
-#include <QPointer>
-#include <QVector>
-
-namespace Core { class IEditor; }
+namespace LanguageServerProtocol { class DocumentSymbol; };
namespace LanguageClient {
-class LANGUAGECLIENT_EXPORT DocumentLocatorFilter : public Core::ILocatorFilter
-{
- Q_OBJECT
-public:
- DocumentLocatorFilter();
+class Client;
+class CurrentDocumentSymbolsData;
- void updateCurrentClient();
- void prepareSearch(const QString &entry) override;
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText,
- int *selectionStart,
- int *selectionLength) const override;
+using DocSymbolModifier = std::function<void(Core::LocatorFilterEntry &,
+ const LanguageServerProtocol::DocumentSymbol &, const Core::LocatorFilterEntry &)>;
-signals:
- void symbolsUpToDate(QPrivateSignal);
+Core::LocatorFilterEntries LANGUAGECLIENT_EXPORT currentDocumentSymbols(const QString &input,
+ const CurrentDocumentSymbolsData &currentSymbolsData, const DocSymbolModifier &docSymbolModifier);
-protected:
- void forceUse() { m_forced = true; }
+Core::LocatorMatcherTasks LANGUAGECLIENT_EXPORT languageClientMatchers(
+ Core::MatcherType type, const QList<Client *> &clients = {}, int maxResultCount = 0);
- QPointer<DocumentSymbolCache> m_symbolCache;
- LanguageServerProtocol::DocumentUri m_currentUri;
+class LanguageAllSymbolsFilter : public Core::ILocatorFilter
+{
+public:
+ LanguageAllSymbolsFilter();
private:
- void updateSymbols(const LanguageServerProtocol::DocumentUri &uri,
- const LanguageServerProtocol::DocumentSymbolsResult &symbols);
- void resetSymbols();
-
- template<class T>
- QList<Core::LocatorFilterEntry> generateEntries(const QList<T> &list, const QString &filter);
- QList<Core::LocatorFilterEntry> generateLocatorEntries(
- const LanguageServerProtocol::SymbolInformation &info,
- const QRegularExpression &regexp,
- const Core::LocatorFilterEntry &parent);
- QList<Core::LocatorFilterEntry> generateLocatorEntries(
- const LanguageServerProtocol::DocumentSymbol &info,
- const QRegularExpression &regexp,
- const Core::LocatorFilterEntry &parent);
- virtual Core::LocatorFilterEntry generateLocatorEntry(
- const LanguageServerProtocol::DocumentSymbol &info,
- const Core::LocatorFilterEntry &parent);
- virtual Core::LocatorFilterEntry generateLocatorEntry(
- const LanguageServerProtocol::SymbolInformation &info);
-
- QMutex m_mutex;
- QMetaObject::Connection m_updateSymbolsConnection;
- QMetaObject::Connection m_resetSymbolsConnection;
- std::optional<LanguageServerProtocol::DocumentSymbolsResult> m_currentSymbols;
- LanguageServerProtocol::DocumentUri::PathMapper m_pathMapper;
- bool m_forced = false;
+ Core::LocatorMatcherTasks matchers() final;
};
-class LANGUAGECLIENT_EXPORT WorkspaceLocatorFilter : public Core::ILocatorFilter
+class LanguageClassesFilter : public Core::ILocatorFilter
{
- Q_OBJECT
public:
- WorkspaceLocatorFilter();
-
- /// request workspace symbols for all clients with enabled locator
- void prepareSearch(const QString &entry) override;
- /// force request workspace symbols for all given clients
- void prepareSearch(const QString &entry, const QList<Client *> &clients);
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText,
- int *selectionStart,
- int *selectionLength) const override;
-
-signals:
- void allRequestsFinished(QPrivateSignal);
-
-protected:
- explicit WorkspaceLocatorFilter(const QVector<LanguageServerProtocol::SymbolKind> &filter);
-
- void setMaxResultCount(qint64 limit) { m_maxResultCount = limit; }
+ LanguageClassesFilter();
private:
- void prepareSearch(const QString &entry, const QList<Client *> &clients, bool force);
- void handleResponse(Client *client,
- const LanguageServerProtocol::WorkspaceSymbolRequest::Response &response);
-
- QMutex m_mutex;
-
- struct SymbolInfoWithPathMapper
- {
- LanguageServerProtocol::SymbolInformation symbol;
- LanguageServerProtocol::DocumentUri::PathMapper mapper;
- };
-
- QMap<Client *, LanguageServerProtocol::MessageId> m_pendingRequests;
- QVector<SymbolInfoWithPathMapper> m_results;
- QVector<LanguageServerProtocol::SymbolKind> m_filterKinds;
- qint64 m_maxResultCount = 0;
+ Core::LocatorMatcherTasks matchers() final;
};
-class LANGUAGECLIENT_EXPORT WorkspaceClassLocatorFilter : public WorkspaceLocatorFilter
+class LanguageFunctionsFilter : public Core::ILocatorFilter
{
public:
- WorkspaceClassLocatorFilter();
+ LanguageFunctionsFilter();
+
+private:
+ Core::LocatorMatcherTasks matchers() final;
};
-class LANGUAGECLIENT_EXPORT WorkspaceMethodLocatorFilter : public WorkspaceLocatorFilter
+class LanguageCurrentDocumentFilter : public Core::ILocatorFilter
{
public:
- WorkspaceMethodLocatorFilter();
+ LanguageCurrentDocumentFilter();
+
+private:
+ Core::LocatorMatcherTasks matchers() final;
};
} // namespace LanguageClient
diff --git a/src/plugins/languageclient/progressmanager.cpp b/src/plugins/languageclient/progressmanager.cpp
index 285080e498..6b82d289df 100644
--- a/src/plugins/languageclient/progressmanager.cpp
+++ b/src/plugins/languageclient/progressmanager.cpp
@@ -8,6 +8,7 @@
#include <languageserverprotocol/progresssupport.h>
#include <QTime>
+#include <QTimer>
using namespace LanguageServerProtocol;
@@ -81,9 +82,28 @@ void ProgressManager::beginProgress(const ProgressToken &token, const WorkDonePr
auto interface = new QFutureInterface<void>();
interface->reportStarted();
interface->setProgressRange(0, 100); // LSP always reports percentage of the task
- const QString title = m_titles.value(token, begin.title());
- Core::FutureProgress *progress = Core::ProgressManager::addTask(
- interface->future(), title, languageClientProgressId(token));
+ ProgressItem progressItem;
+ progressItem.futureInterface = interface;
+ progressItem.title = m_titles.value(token, begin.title());
+ if (LOGPROGRESS().isDebugEnabled())
+ progressItem.timer.start();
+ progressItem.showBarTimer = new QTimer();
+ progressItem.showBarTimer->setSingleShot(true);
+ progressItem.showBarTimer->setInterval(750);
+ progressItem.showBarTimer->callOnTimeout([this, token]() { spawnProgressBar(token); });
+ progressItem.showBarTimer->start();
+ m_progress[token] = progressItem;
+ reportProgress(token, begin);
+}
+
+void ProgressManager::spawnProgressBar(const LanguageServerProtocol::ProgressToken &token)
+{
+ ProgressItem &progressItem = m_progress[token];
+ QTC_ASSERT(progressItem.futureInterface, return);
+ Core::FutureProgress *progress
+ = Core::ProgressManager::addTask(progressItem.futureInterface->future(),
+ progressItem.title,
+ languageClientProgressId(token));
const std::function<void()> clickHandler = m_clickHandlers.value(token);
if (clickHandler)
QObject::connect(progress, &Core::FutureProgress::clicked, clickHandler);
@@ -92,23 +112,26 @@ void ProgressManager::beginProgress(const ProgressToken &token, const WorkDonePr
QObject::connect(progress, &Core::FutureProgress::canceled, cancelHandler);
else
progress->setCancelEnabled(false);
- m_progress[token] = {progress, interface};
- if (LOGPROGRESS().isDebugEnabled())
- m_timer[token].start();
- reportProgress(token, begin);
+ if (!progressItem.message.isEmpty()) {
+ progress->setSubtitle(progressItem.message);
+ progress->setSubtitleVisibleInStatusBar(true);
+ }
+ progressItem.progressInterface = progress;
}
void ProgressManager::reportProgress(const ProgressToken &token,
const WorkDoneProgressReport &report)
{
- const LanguageClientProgress &progress = m_progress.value(token);
+ ProgressItem &progress = m_progress[token];
+ const std::optional<QString> &message = report.message();
if (progress.progressInterface) {
- const std::optional<QString> &message = report.message();
if (message.has_value()) {
progress.progressInterface->setSubtitle(*message);
const bool showSubtitle = !message->isEmpty();
progress.progressInterface->setSubtitleVisibleInStatusBar(showSubtitle);
}
+ } else if (message.has_value()) {
+ progress.message = *message;
}
if (progress.futureInterface) {
if (const std::optional<double> &percentage = report.percentage(); percentage.has_value())
@@ -118,7 +141,7 @@ void ProgressManager::reportProgress(const ProgressToken &token,
void ProgressManager::endProgress(const ProgressToken &token, const WorkDoneProgressEnd &end)
{
- const LanguageClientProgress &progress = m_progress.value(token);
+ const ProgressItem &progress = m_progress.value(token);
const QString &message = end.message().value_or(QString());
if (progress.progressInterface) {
if (!message.isEmpty()) {
@@ -127,11 +150,11 @@ void ProgressManager::endProgress(const ProgressToken &token, const WorkDoneProg
}
progress.progressInterface->setSubtitle(message);
progress.progressInterface->setSubtitleVisibleInStatusBar(!message.isEmpty());
- auto timer = m_timer.take(token);
- if (timer.isValid()) {
+ if (progress.timer.isValid()) {
qCDebug(LOGPROGRESS) << QString("%1 took %2")
.arg(progress.progressInterface->title())
- .arg(QTime::fromMSecsSinceStartOfDay(timer.elapsed())
+ .arg(QTime::fromMSecsSinceStartOfDay(
+ progress.timer.elapsed())
.toString(Qt::ISODateWithMs));
}
}
@@ -140,7 +163,8 @@ void ProgressManager::endProgress(const ProgressToken &token, const WorkDoneProg
void ProgressManager::endProgressReport(const ProgressToken &token)
{
- const LanguageClientProgress &progress = m_progress.take(token);
+ ProgressItem progress = m_progress.take(token);
+ delete progress.showBarTimer;
if (progress.futureInterface)
progress.futureInterface->reportFinished();
delete progress.futureInterface;
diff --git a/src/plugins/languageclient/progressmanager.h b/src/plugins/languageclient/progressmanager.h
index 1e0ad78180..05b9640745 100644
--- a/src/plugins/languageclient/progressmanager.h
+++ b/src/plugins/languageclient/progressmanager.h
@@ -11,6 +11,10 @@
#include <QFutureInterface>
#include <QPointer>
+QT_BEGIN_NAMESPACE
+class QTimer;
+QT_END_NAMESPACE
+
namespace LanguageServerProtocol {
class ProgressParams;
class ProgressToken;
@@ -46,15 +50,20 @@ private:
const LanguageServerProtocol::WorkDoneProgressReport &report);
void endProgress(const LanguageServerProtocol::ProgressToken &token,
const LanguageServerProtocol::WorkDoneProgressEnd &end);
+ void spawnProgressBar(const LanguageServerProtocol::ProgressToken &token);
- struct LanguageClientProgress {
+ struct ProgressItem
+ {
QPointer<Core::FutureProgress> progressInterface = nullptr;
QFutureInterface<void> *futureInterface = nullptr;
+ QElapsedTimer timer;
+ QTimer *showBarTimer = nullptr;
+ QString message;
+ QString title;
};
- QMap<LanguageServerProtocol::ProgressToken, LanguageClientProgress> m_progress;
+ QMap<LanguageServerProtocol::ProgressToken, ProgressItem> m_progress;
QMap<LanguageServerProtocol::ProgressToken, QString> m_titles;
- QMap<LanguageServerProtocol::ProgressToken, QElapsedTimer> m_timer;
QMap<LanguageServerProtocol::ProgressToken, std::function<void()>> m_clickHandlers;
QMap<LanguageServerProtocol::ProgressToken, std::function<void()>> m_cancelHandlers;
};
diff --git a/src/plugins/macros/CMakeLists.txt b/src/plugins/macros/CMakeLists.txt
index aef9a332dd..588a9a330f 100644
--- a/src/plugins/macros/CMakeLists.txt
+++ b/src/plugins/macros/CMakeLists.txt
@@ -9,7 +9,6 @@ add_qtc_plugin(Macros
macrolocatorfilter.cpp macrolocatorfilter.h
macromanager.cpp macromanager.h
macrooptionspage.cpp macrooptionspage.h
- macrooptionswidget.cpp macrooptionswidget.h
macros.qrc
macrosconstants.h
macrosplugin.cpp macrosplugin.h
diff --git a/src/plugins/macros/macrolocatorfilter.cpp b/src/plugins/macros/macrolocatorfilter.cpp
index 43a9a48945..f43758bd03 100644
--- a/src/plugins/macros/macrolocatorfilter.cpp
+++ b/src/plugins/macros/macrolocatorfilter.cpp
@@ -8,13 +8,13 @@
#include "macrostr.h"
#include <coreplugin/editormanager/editormanager.h>
-#include <coreplugin/editormanager/ieditor.h>
-#include <coreplugin/icore.h>
#include <QPixmap>
+using namespace Core;
using namespace Macros;
using namespace Macros::Internal;
+using namespace Utils;
MacroLocatorFilter::MacroLocatorFilter()
: m_icon(QPixmap(":/macros/images/macro.png"))
@@ -26,54 +26,50 @@ MacroLocatorFilter::MacroLocatorFilter()
setDefaultShortcutString("rm");
}
-MacroLocatorFilter::~MacroLocatorFilter() = default;
-
-QList<Core::LocatorFilterEntry> MacroLocatorFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
+LocatorMatcherTasks MacroLocatorFilter::matchers()
{
- Q_UNUSED(future)
- QList<Core::LocatorFilterEntry> goodEntries;
- QList<Core::LocatorFilterEntry> betterEntries;
-
- const Qt::CaseSensitivity entryCaseSensitivity = caseSensitivity(entry);
-
- const QMap<QString, Macro*> &macros = MacroManager::macros();
-
- for (auto it = macros.cbegin(), end = macros.cend(); it != end; ++it) {
- const QString displayName = it.key();
- const QString description = it.value()->description();
-
- int index = displayName.indexOf(entry, 0, entryCaseSensitivity);
- Core::LocatorFilterEntry::HighlightInfo::DataType hDataType = Core::LocatorFilterEntry::HighlightInfo::DisplayName;
- if (index < 0) {
- index = description.indexOf(entry, 0, entryCaseSensitivity);
- hDataType = Core::LocatorFilterEntry::HighlightInfo::ExtraInfo;
- }
-
- if (index >= 0) {
- Core::LocatorFilterEntry filterEntry(this, displayName, {}, m_icon);
- filterEntry.extraInfo = description;
- filterEntry.highlightInfo = Core::LocatorFilterEntry::HighlightInfo(index, entry.length(), hDataType);
-
- if (index == 0)
- betterEntries.append(filterEntry);
- else
- goodEntries.append(filterEntry);
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [storage, icon = m_icon] {
+ const QString input = storage->input();
+ const Qt::CaseSensitivity entryCaseSensitivity = caseSensitivity(input);
+ const QMap<QString, Macro *> &macros = MacroManager::macros();
+ LocatorFilterEntries goodEntries;
+ LocatorFilterEntries betterEntries;
+ for (auto it = macros.cbegin(); it != macros.cend(); ++it) {
+ const QString displayName = it.key();
+ const QString description = it.value()->description();
+ int index = displayName.indexOf(input, 0, entryCaseSensitivity);
+ LocatorFilterEntry::HighlightInfo::DataType hDataType
+ = LocatorFilterEntry::HighlightInfo::DisplayName;
+ if (index < 0) {
+ index = description.indexOf(input, 0, entryCaseSensitivity);
+ hDataType = LocatorFilterEntry::HighlightInfo::ExtraInfo;
+ }
+
+ if (index >= 0) {
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = displayName;
+ filterEntry.acceptor = [displayName] {
+ IEditor *editor = EditorManager::currentEditor();
+ if (editor)
+ editor->widget()->setFocus(Qt::OtherFocusReason);
+ MacroManager::instance()->executeMacro(displayName);
+ return AcceptResult();
+ };
+ filterEntry.displayIcon = icon;
+ filterEntry.extraInfo = description;
+ filterEntry.highlightInfo = LocatorFilterEntry::HighlightInfo(index, input.length(),
+ hDataType);
+ if (index == 0)
+ betterEntries.append(filterEntry);
+ else
+ goodEntries.append(filterEntry);
+ }
}
- }
- betterEntries.append(goodEntries);
- return betterEntries;
-}
-
-void MacroLocatorFilter::accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const
-{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- // Give the focus back to the editor
- Core::IEditor *editor = Core::EditorManager::currentEditor();
- if (editor)
- editor->widget()->setFocus(Qt::OtherFocusReason);
-
- MacroManager::instance()->executeMacro(selection.displayName);
+ storage->reportOutput(betterEntries + goodEntries);
+ };
+ return {{Sync(onSetup), storage}};
}
diff --git a/src/plugins/macros/macrolocatorfilter.h b/src/plugins/macros/macrolocatorfilter.h
index 61fa7a34db..b6acc6d2c7 100644
--- a/src/plugins/macros/macrolocatorfilter.h
+++ b/src/plugins/macros/macrolocatorfilter.h
@@ -5,27 +5,17 @@
#include <coreplugin/locator/ilocatorfilter.h>
-#include <QIcon>
-
-namespace Macros {
-namespace Internal {
+namespace Macros::Internal {
class MacroLocatorFilter : public Core::ILocatorFilter
{
- Q_OBJECT
-
public:
MacroLocatorFilter();
- ~MacroLocatorFilter() override;
-
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
private:
+ Core::LocatorMatcherTasks matchers() final;
+
const QIcon m_icon;
};
-} // namespace Internal
-} // namespace Macros
+} // namespace Macros::Internal
diff --git a/src/plugins/macros/macrooptionspage.cpp b/src/plugins/macros/macrooptionspage.cpp
index 6295ea94e7..616547283e 100644
--- a/src/plugins/macros/macrooptionspage.cpp
+++ b/src/plugins/macros/macrooptionspage.cpp
@@ -3,15 +3,195 @@
#include "macrooptionspage.h"
+#include "macro.h"
#include "macromanager.h"
-#include "macrooptionswidget.h"
#include "macrosconstants.h"
#include "macrostr.h"
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/actionmanager/command.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/icore.h>
+
#include <texteditor/texteditorconstants.h>
-namespace Macros {
-namespace Internal {
+#include <utils/layoutbuilder.h>
+
+#include <QAction>
+#include <QDir>
+#include <QFileInfo>
+#include <QGroupBox>
+#include <QHeaderView>
+#include <QLabel>
+#include <QLineEdit>
+#include <QMap>
+#include <QPushButton>
+#include <QStringList>
+#include <QTreeWidget>
+#include <QTreeWidgetItem>
+
+namespace Macros::Internal {
+
+const int NAME_ROLE = Qt::UserRole;
+const int WRITE_ROLE = Qt::UserRole + 1;
+
+class MacroOptionsWidget final : public Core::IOptionsPageWidget
+{
+public:
+ MacroOptionsWidget();
+
+ void initialize();
+
+ void apply() final;
+
+private:
+ void remove();
+ void changeCurrentItem(QTreeWidgetItem *current);
+
+ void createTable();
+
+ void changeDescription(const QString &description);
+
+ QStringList m_macroToRemove;
+ bool m_changingCurrent = false;
+
+ QMap<QString, QString> m_macroToChange;
+
+ QTreeWidget *m_treeWidget;
+ QPushButton *m_removeButton;
+ QGroupBox *m_macroGroup;
+ QLineEdit *m_description;
+};
+
+MacroOptionsWidget::MacroOptionsWidget()
+{
+ m_treeWidget = new QTreeWidget;
+ m_treeWidget->setTextElideMode(Qt::ElideLeft);
+ m_treeWidget->setUniformRowHeights(true);
+ m_treeWidget->setSortingEnabled(true);
+ m_treeWidget->setColumnCount(3);
+ m_treeWidget->header()->setSortIndicatorShown(true);
+ m_treeWidget->header()->setStretchLastSection(true);
+ m_treeWidget->header()->setSortIndicator(0, Qt::AscendingOrder);
+ m_treeWidget->setHeaderLabels({Tr::tr("Name"), Tr::tr("Description"), Tr::tr("Shortcut")});
+
+ m_description = new QLineEdit;
+
+ m_removeButton = new QPushButton(Tr::tr("Remove"));
+
+ m_macroGroup = new QGroupBox(Tr::tr("Macro"), this);
+
+ using namespace Layouting;
+
+ Row {
+ Tr::tr("Description:"), m_description
+ }.attachTo(m_macroGroup);
+
+ Column {
+ Group {
+ title(Tr::tr("Preferences")),
+ Row {
+ m_treeWidget,
+ Column { m_removeButton, st },
+ }
+ },
+ m_macroGroup
+ }.attachTo(this);
+
+ connect(m_treeWidget, &QTreeWidget::currentItemChanged,
+ this, &MacroOptionsWidget::changeCurrentItem);
+ connect(m_removeButton, &QPushButton::clicked,
+ this, &MacroOptionsWidget::remove);
+ connect(m_description, &QLineEdit::textChanged,
+ this, &MacroOptionsWidget::changeDescription);
+
+ initialize();
+}
+
+void MacroOptionsWidget::initialize()
+{
+ m_macroToRemove.clear();
+ m_macroToChange.clear();
+ m_treeWidget->clear();
+ changeCurrentItem(nullptr);
+
+ // Create the treeview
+ createTable();
+}
+
+void MacroOptionsWidget::createTable()
+{
+ QDir dir(MacroManager::macrosDirectory());
+ const Utils::Id base = Utils::Id(Constants::PREFIX_MACRO);
+ for (Macro *macro : MacroManager::macros()) {
+ QFileInfo fileInfo(macro->fileName());
+ if (fileInfo.absoluteDir() == dir.absolutePath()) {
+ auto macroItem = new QTreeWidgetItem(m_treeWidget);
+ macroItem->setText(0, macro->displayName());
+ macroItem->setText(1, macro->description());
+ macroItem->setData(0, NAME_ROLE, macro->displayName());
+ macroItem->setData(0, WRITE_ROLE, macro->isWritable());
+
+ Core::Command *command =
+ Core::ActionManager::command(base.withSuffix(macro->displayName()));
+ if (command && command->action()) {
+ macroItem->setText(2,
+ command->action()->shortcut().toString(QKeySequence::NativeText));
+ }
+ }
+ }
+}
+
+void MacroOptionsWidget::changeCurrentItem(QTreeWidgetItem *current)
+{
+ m_changingCurrent = true;
+ m_removeButton->setEnabled(current);
+ m_macroGroup->setEnabled(current);
+ if (!current) {
+ m_description->clear();
+ } else {
+ m_description->setText(current->text(1));
+ m_description->setEnabled(current->data(0, WRITE_ROLE).toBool());
+ }
+ m_changingCurrent = false;
+}
+
+void MacroOptionsWidget::remove()
+{
+ QTreeWidgetItem *current = m_treeWidget->currentItem();
+ m_macroToRemove.append(current->data(0, NAME_ROLE).toString());
+ delete current;
+}
+
+void MacroOptionsWidget::apply()
+{
+ // Remove macro
+ for (const QString &name : std::as_const(m_macroToRemove)) {
+ MacroManager::instance()->deleteMacro(name);
+ m_macroToChange.remove(name);
+ }
+
+ // Change macro
+ for (auto it = m_macroToChange.cbegin(), end = m_macroToChange.cend(); it != end; ++it)
+ MacroManager::instance()->changeMacro(it.key(), it.value());
+
+ // Reinitialize the page
+ initialize();
+}
+
+void MacroOptionsWidget::changeDescription(const QString &description)
+{
+ QTreeWidgetItem *current = m_treeWidget->currentItem();
+ if (m_changingCurrent || !current)
+ return;
+
+ QString macroName = current->data(0, NAME_ROLE).toString();
+ m_macroToChange[macroName] = description;
+ current->setText(1, description);
+ QFont font = current->font(1);
+ font.setItalic(true);
+ current->setFont(1, font);
+}
MacroOptionsPage::MacroOptionsPage()
{
@@ -21,5 +201,4 @@ MacroOptionsPage::MacroOptionsPage()
setWidgetCreator([] { return new MacroOptionsWidget; });
}
-} // Internal
-} // Macros
+} // Macros::Internal
diff --git a/src/plugins/macros/macrooptionspage.h b/src/plugins/macros/macrooptionspage.h
index e9948cb167..511b8b2b87 100644
--- a/src/plugins/macros/macrooptionspage.h
+++ b/src/plugins/macros/macrooptionspage.h
@@ -5,8 +5,7 @@
#include <coreplugin/dialogs/ioptionspage.h>
-namespace Macros {
-namespace Internal {
+namespace Macros::Internal {
class MacroOptionsPage final : public Core::IOptionsPage
{
@@ -14,5 +13,4 @@ public:
MacroOptionsPage();
};
-} // namespace Internal
-} // namespace Macros
+} // Macros::Internal
diff --git a/src/plugins/macros/macrooptionswidget.cpp b/src/plugins/macros/macrooptionswidget.cpp
deleted file mode 100644
index c6a94d4498..0000000000
--- a/src/plugins/macros/macrooptionswidget.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright (C) 2016 Nicolas Arnaud-Cormos
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "macrooptionswidget.h"
-
-#include "macro.h"
-#include "macromanager.h"
-#include "macrosconstants.h"
-#include "macrostr.h"
-
-#include <coreplugin/icore.h>
-#include <coreplugin/coreconstants.h>
-#include <coreplugin/actionmanager/actionmanager.h>
-#include <coreplugin/actionmanager/command.h>
-
-#include <utils/layoutbuilder.h>
-
-#include <QAction>
-#include <QDir>
-#include <QFileInfo>
-#include <QGroupBox>
-#include <QHeaderView>
-#include <QLabel>
-#include <QLineEdit>
-#include <QPushButton>
-#include <QTreeWidget>
-#include <QTreeWidgetItem>
-
-namespace Macros::Internal {
-
-const int NAME_ROLE = Qt::UserRole;
-const int WRITE_ROLE = Qt::UserRole + 1;
-
-MacroOptionsWidget::MacroOptionsWidget()
-{
- m_treeWidget = new QTreeWidget;
- m_treeWidget->setTextElideMode(Qt::ElideLeft);
- m_treeWidget->setUniformRowHeights(true);
- m_treeWidget->setSortingEnabled(true);
- m_treeWidget->setColumnCount(3);
- m_treeWidget->header()->setSortIndicatorShown(true);
- m_treeWidget->header()->setStretchLastSection(true);
- m_treeWidget->header()->setSortIndicator(0, Qt::AscendingOrder);
- m_treeWidget->setHeaderLabels({Tr::tr("Name"), Tr::tr("Description"), Tr::tr("Shortcut")});
-
- m_description = new QLineEdit;
-
- m_removeButton = new QPushButton(Tr::tr("Remove"));
-
- m_macroGroup = new QGroupBox(Tr::tr("Macro"), this);
-
- using namespace Utils::Layouting;
-
- Row {
- Tr::tr("Description:"), m_description
- }.attachTo(m_macroGroup);
-
- Column {
- Group {
- title(Tr::tr("Preferences")),
- Row {
- m_treeWidget,
- Column { m_removeButton, st },
- }
- },
- m_macroGroup
- }.attachTo(this);
-
- connect(m_treeWidget, &QTreeWidget::currentItemChanged,
- this, &MacroOptionsWidget::changeCurrentItem);
- connect(m_removeButton, &QPushButton::clicked,
- this, &MacroOptionsWidget::remove);
- connect(m_description, &QLineEdit::textChanged,
- this, &MacroOptionsWidget::changeDescription);
-
- initialize();
-}
-
-MacroOptionsWidget::~MacroOptionsWidget() = default;
-
-void MacroOptionsWidget::initialize()
-{
- m_macroToRemove.clear();
- m_macroToChange.clear();
- m_treeWidget->clear();
- changeCurrentItem(nullptr);
-
- // Create the treeview
- createTable();
-}
-
-void MacroOptionsWidget::createTable()
-{
- QDir dir(MacroManager::macrosDirectory());
- const Utils::Id base = Utils::Id(Constants::PREFIX_MACRO);
- for (Macro *macro : MacroManager::macros()) {
- QFileInfo fileInfo(macro->fileName());
- if (fileInfo.absoluteDir() == dir.absolutePath()) {
- auto macroItem = new QTreeWidgetItem(m_treeWidget);
- macroItem->setText(0, macro->displayName());
- macroItem->setText(1, macro->description());
- macroItem->setData(0, NAME_ROLE, macro->displayName());
- macroItem->setData(0, WRITE_ROLE, macro->isWritable());
-
- Core::Command *command =
- Core::ActionManager::command(base.withSuffix(macro->displayName()));
- if (command && command->action()) {
- macroItem->setText(2,
- command->action()->shortcut().toString(QKeySequence::NativeText));
- }
- }
- }
-}
-
-void MacroOptionsWidget::changeCurrentItem(QTreeWidgetItem *current)
-{
- m_changingCurrent = true;
- m_removeButton->setEnabled(current);
- m_macroGroup->setEnabled(current);
- if (!current) {
- m_description->clear();
- } else {
- m_description->setText(current->text(1));
- m_description->setEnabled(current->data(0, WRITE_ROLE).toBool());
- }
- m_changingCurrent = false;
-}
-
-void MacroOptionsWidget::remove()
-{
- QTreeWidgetItem *current = m_treeWidget->currentItem();
- m_macroToRemove.append(current->data(0, NAME_ROLE).toString());
- delete current;
-}
-
-void MacroOptionsWidget::apply()
-{
- // Remove macro
- for (const QString &name : std::as_const(m_macroToRemove)) {
- MacroManager::instance()->deleteMacro(name);
- m_macroToChange.remove(name);
- }
-
- // Change macro
- for (auto it = m_macroToChange.cbegin(), end = m_macroToChange.cend(); it != end; ++it)
- MacroManager::instance()->changeMacro(it.key(), it.value());
-
- // Reinitialize the page
- initialize();
-}
-
-void MacroOptionsWidget::changeDescription(const QString &description)
-{
- QTreeWidgetItem *current = m_treeWidget->currentItem();
- if (m_changingCurrent || !current)
- return;
-
- QString macroName = current->data(0, NAME_ROLE).toString();
- m_macroToChange[macroName] = description;
- current->setText(1, description);
- QFont font = current->font(1);
- font.setItalic(true);
- current->setFont(1, font);
-}
-
-} // Macros::Internal
diff --git a/src/plugins/macros/macrooptionswidget.h b/src/plugins/macros/macrooptionswidget.h
deleted file mode 100644
index 9810da6dd1..0000000000
--- a/src/plugins/macros/macrooptionswidget.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (C) 2016 Nicolas Arnaud-Cormos
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <coreplugin/dialogs/ioptionspage.h>
-
-#include <QStringList>
-#include <QMap>
-
-QT_BEGIN_NAMESPACE
-class QGroupBox;
-class QLineEdit;
-class QPushButton;
-class QTreeWidget;
-class QTreeWidgetItem;
-QT_END_NAMESPACE
-
-namespace Macros {
-namespace Internal {
-
-class MacroOptionsWidget final : public Core::IOptionsPageWidget
-{
- Q_OBJECT
-
-public:
- MacroOptionsWidget();
- ~MacroOptionsWidget() final;
-
- void initialize();
-
- void apply() final;
-
-private:
- void remove();
- void changeCurrentItem(QTreeWidgetItem *current);
-
- void createTable();
-
- void changeDescription(const QString &description);
-
-private:
- QStringList m_macroToRemove;
- bool m_changingCurrent = false;
-
- QMap<QString, QString> m_macroToChange;
-
- QTreeWidget *m_treeWidget;
- QPushButton *m_removeButton;
- QGroupBox *m_macroGroup;
- QLineEdit *m_description;
-};
-
-} // namespace Internal
-} // namespace Macros
diff --git a/src/plugins/macros/macros.qbs b/src/plugins/macros/macros.qbs
index f6579c7394..c975f756d7 100644
--- a/src/plugins/macros/macros.qbs
+++ b/src/plugins/macros/macros.qbs
@@ -29,8 +29,6 @@ QtcPlugin {
"macromanager.h",
"macrooptionspage.cpp",
"macrooptionspage.h",
- "macrooptionswidget.cpp",
- "macrooptionswidget.h",
"macros.qrc",
"macrosconstants.h",
"macrosplugin.cpp",
diff --git a/src/plugins/macros/savedialog.cpp b/src/plugins/macros/savedialog.cpp
index 3d8f7d6ada..cb45f96da0 100644
--- a/src/plugins/macros/savedialog.cpp
+++ b/src/plugins/macros/savedialog.cpp
@@ -12,8 +12,6 @@
#include <QLineEdit>
#include <QRegularExpressionValidator>
-using namespace Utils;
-
namespace Macros::Internal {
SaveDialog::SaveDialog(QWidget *parent) :
diff --git a/src/plugins/marketplace/productlistmodel.cpp b/src/plugins/marketplace/productlistmodel.cpp
index cdfb8d79e5..36b047abc2 100644
--- a/src/plugins/marketplace/productlistmodel.cpp
+++ b/src/plugins/marketplace/productlistmodel.cpp
@@ -269,7 +269,7 @@ void SectionedProducts::onImageDownloadFinished(QNetworkReply *reply)
if (pixmap.loadFromData(data, imageFormat.toLatin1())) {
const QString url = imageUrl.toString();
const int dpr = qApp->devicePixelRatio();
- pixmap = pixmap.scaled(ListModel::defaultImageSize * dpr,
+ pixmap = pixmap.scaled(WelcomePageHelpers::GridItemImageSize * dpr,
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
pixmap.setDevicePixelRatio(dpr);
diff --git a/src/plugins/mcusupport/CMakeLists.txt b/src/plugins/mcusupport/CMakeLists.txt
index 81fadd4ddf..e2d81ae5a7 100644
--- a/src/plugins/mcusupport/CMakeLists.txt
+++ b/src/plugins/mcusupport/CMakeLists.txt
@@ -24,6 +24,7 @@ add_qtc_plugin(McuSupport
settingshandler.cpp settingshandler.h
mcuqmlprojectnode.cpp mcuqmlprojectnode.h
mcubuildstep.cpp mcubuildstep.h
+ dialogs/mcukitcreationdialog.cpp dialogs/mcukitcreationdialog.h
)
add_subdirectory(test)
diff --git a/src/plugins/mcusupport/dialogs/mcukitcreationdialog.cpp b/src/plugins/mcusupport/dialogs/mcukitcreationdialog.cpp
new file mode 100644
index 0000000000..9702fb77bf
--- /dev/null
+++ b/src/plugins/mcusupport/dialogs/mcukitcreationdialog.cpp
@@ -0,0 +1,134 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "mcukitcreationdialog.h"
+
+#include "../mcuabstractpackage.h"
+#include "../mcusupportconstants.h"
+#include "../mcusupporttr.h"
+
+#include <utils/filepath.h>
+#include <utils/icon.h>
+#include <utils/layoutbuilder.h>
+
+#include <coreplugin/icore.h>
+
+#include <QApplication>
+#include <QDesktopServices>
+#include <QDialogButtonBox>
+#include <QLabel>
+#include <QPushButton>
+#include <QStyle>
+
+namespace McuSupport::Internal {
+
+McuKitCreationDialog::McuKitCreationDialog(const MessagesList &messages,
+ const SettingsHandler::Ptr &settingsHandler,
+ McuPackagePtr qtMCUPackage,
+ QWidget *parent)
+ : QDialog(parent)
+ , m_messages(messages)
+{
+ resize(500, 300);
+ setWindowTitle(Tr::tr("Qt for MCUs Kit Creation"));
+
+ m_iconLabel = new QLabel;
+ m_iconLabel->setAlignment(Qt::AlignTop);
+
+ m_textLabel = new QLabel;
+
+ m_informationLabel = new QLabel;
+ m_informationLabel->setWordWrap(true);
+ m_informationLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+ m_informationLabel->setAlignment(Qt::AlignTop);
+
+ m_qtMCUsPathLabel = new QLabel;
+
+ auto line = new QFrame;
+ line->setFrameShape(QFrame::VLine);
+ line->setFrameShadow(QFrame::Sunken);
+
+ auto buttonBox = new QDialogButtonBox(Qt::Vertical);
+ buttonBox->setStandardButtons(QDialogButtonBox::Ignore);
+
+ m_messageCountLabel = new QLabel;
+ m_messageCountLabel->setAlignment(Qt::AlignCenter);
+
+ using namespace Layouting;
+ Row {
+ Column {
+ Row {
+ m_iconLabel,
+ Column {
+ m_textLabel,
+ m_informationLabel,
+ },
+ },
+ m_qtMCUsPathLabel,
+ },
+ line,
+ Column {
+ buttonBox,
+ m_messageCountLabel,
+ },
+ }.attachTo(this);
+
+ m_previousButton = buttonBox->addButton("<", QDialogButtonBox::ActionRole);
+ m_nextButton = buttonBox->addButton(">", QDialogButtonBox::ActionRole);
+ QPushButton *fixButton = buttonBox->addButton(Tr::tr("Fix"), QDialogButtonBox::ActionRole);
+ QPushButton *helpButton = buttonBox->addButton(Tr::tr("Help"), QDialogButtonBox::HelpRole);
+
+ if (messages.size() == 1) {
+ m_nextButton->setVisible(false);
+ m_previousButton->setVisible(false);
+ }
+ //display first message
+ if (messages.size() > 1)
+ updateMessage(1);
+
+ if (qtMCUPackage->isValidStatus())
+ m_qtMCUsPathLabel->setText(
+ Tr::tr("Qt for MCUs path %1").arg(qtMCUPackage->path().toUserOutput()));
+ connect(m_nextButton, &QPushButton::clicked, [=] { updateMessage(1); });
+ connect(m_previousButton, &QPushButton::clicked, [=] { updateMessage(-1); });
+ connect(fixButton, &QPushButton::clicked, [=] {
+ // Open the MCU Options widget on the current platform
+ settingsHandler->setInitialPlatformName(m_messages[m_currentIndex].platform);
+ Core::ICore::showOptionsDialog(Constants::SETTINGS_ID);
+ // reset the initial platform name
+ settingsHandler->setInitialPlatformName("");
+ });
+ connect(helpButton, &QPushButton::clicked, [] {
+ QDesktopServices::openUrl(QUrl("https://doc.qt.io/QtForMCUs/qtul-prerequisites.html"));
+ });
+ connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+ connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+}
+
+void McuKitCreationDialog::updateMessage(const int inc)
+{
+ m_currentIndex += inc;
+ m_nextButton->setEnabled(m_currentIndex < (m_messages.size() - 1));
+ m_previousButton->setEnabled(m_currentIndex > 0);
+ m_textLabel->setText(QString("<b>%1 %2</b> : %3")
+ .arg(Tr::tr("Target"),
+ (m_messages[m_currentIndex].status == McuSupportMessage::Warning
+ ? Tr::tr("Warning")
+ : Tr::tr("Error")),
+ m_messages[m_currentIndex].platform));
+ m_iconLabel->setPixmap(
+ QApplication::style()
+ ->standardIcon(m_messages[m_currentIndex].status == McuSupportMessage::Warning
+ ? QStyle::SP_MessageBoxWarning
+ : QStyle::SP_MessageBoxCritical)
+ .pixmap(64, 64));
+ m_informationLabel->setText(QString("<b>%1</b>: %2<br><br><b>%3</b>: %4")
+ .arg(Tr::tr("Package"),
+ m_messages[m_currentIndex].packageName,
+ Tr::tr("Status"),
+ m_messages.at(m_currentIndex).message));
+ m_messageCountLabel->setText(QString("%1 / %2").arg(QString::number(m_currentIndex + 1),
+ QString::number(m_messages.size())));
+}
+
+} // namespace McuSupport::Internal
diff --git a/src/plugins/mcusupport/dialogs/mcukitcreationdialog.h b/src/plugins/mcusupport/dialogs/mcukitcreationdialog.h
new file mode 100644
index 0000000000..6caae59678
--- /dev/null
+++ b/src/plugins/mcusupport/dialogs/mcukitcreationdialog.h
@@ -0,0 +1,42 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "../mcusupport_global.h"
+#include "../settingshandler.h"
+
+#include <QDialog>
+
+QT_BEGIN_NAMESPACE
+class QLabel;
+class QPushButton;
+QT_END_NAMESPACE
+
+namespace McuSupport::Internal {
+
+class McuKitCreationDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit McuKitCreationDialog(const MessagesList &messages,
+ const SettingsHandler::Ptr &settingsHandler,
+ McuPackagePtr qtMCUPackage,
+ QWidget *parent = nullptr);
+
+private slots:
+ void updateMessage(const int inc);
+
+private:
+ int m_currentIndex = -1;
+ QLabel *m_iconLabel;
+ QLabel *m_textLabel;
+ QLabel *m_informationLabel;
+ QLabel *m_qtMCUsPathLabel;
+ QLabel *m_messageCountLabel;
+ QPushButton *m_previousButton;
+ QPushButton *m_nextButton;
+ const MessagesList &m_messages;
+};
+} // namespace McuSupport::Internal
diff --git a/src/plugins/mcusupport/mcubuildstep.cpp b/src/plugins/mcusupport/mcubuildstep.cpp
index a26e8ed0d1..3b0c639df5 100644
--- a/src/plugins/mcusupport/mcubuildstep.cpp
+++ b/src/plugins/mcusupport/mcubuildstep.cpp
@@ -2,18 +2,21 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "mcubuildstep.h"
+
#include "mcukitmanager.h"
#include "mculegacyconstants.h"
#include "mcusupportconstants.h"
#include <cmakeprojectmanager/cmakekitinformation.h>
-#include <projectexplorer/buildstep.h>
+#include <projectexplorer/abstractprocessstep.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/deployconfiguration.h>
#include <projectexplorer/kit.h>
+#include <projectexplorer/kit.h>
#include <projectexplorer/kitmanager.h>
+#include <projectexplorer/project.h>
#include <projectexplorer/target.h>
#include <projectexplorer/task.h>
#include <projectexplorer/taskhub.h>
@@ -23,12 +26,25 @@
#include <qtsupport/qtsupportconstants.h>
#include <utils/aspects.h>
-#include <utils/filepath.h>
+#include <QTemporaryDir>
#include <QVersionNumber>
namespace McuSupport::Internal {
+class DeployMcuProcessStep : public ProjectExplorer::AbstractProcessStep
+{
+public:
+ static const Utils::Id id;
+ static void showError(const QString &text);
+
+ DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, Utils::Id id);
+
+private:
+ QString findKitInformation(ProjectExplorer::Kit *kit, const QString &key);
+ QTemporaryDir m_tmpDir;
+};
+
const Utils::Id DeployMcuProcessStep::id = "QmlProject.Mcu.DeployStep";
void DeployMcuProcessStep::showError(const QString &text)
@@ -58,9 +74,8 @@ DeployMcuProcessStep::DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, U
QString root = findKitInformation(kit, Internal::Legacy::Constants::QUL_CMAKE_VAR);
auto rootPath = Utils::FilePath::fromString(root);
- auto cmd = addAspect<Utils::StringAspect>();
+ auto cmd = addAspect<Utils::FilePathAspect>();
cmd->setSettingsKey("QmlProject.Mcu.ProcessStep.Command");
- cmd->setDisplayStyle(Utils::StringAspect::PathChooserDisplay);
cmd->setExpectedKind(Utils::PathChooser::Command);
cmd->setLabelText(QmlProjectManager::Tr::tr("Command:"));
cmd->setFilePath(rootPath.pathAppended("/bin/qmlprojectexporter"));
@@ -87,9 +102,8 @@ DeployMcuProcessStep::DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, U
args->setLabelText(QmlProjectManager::Tr::tr("Arguments:"));
args->setValue(Utils::ProcessArgs::joinArgs(arguments));
- auto outDir = addAspect<Utils::StringAspect>();
+ auto outDir = addAspect<Utils::FilePathAspect>();
outDir->setSettingsKey("QmlProject.Mcu.ProcessStep.BuildDirectory");
- outDir->setDisplayStyle(Utils::StringAspect::PathChooserDisplay);
outDir->setExpectedKind(Utils::PathChooser::Directory);
outDir->setLabelText(QmlProjectManager::Tr::tr("Build directory:"));
outDir->setPlaceHolderText(m_tmpDir.path());
@@ -119,13 +133,6 @@ QString DeployMcuProcessStep::findKitInformation(ProjectExplorer::Kit *kit, cons
return {};
}
-MCUBuildStepFactory::MCUBuildStepFactory()
- : BuildStepFactory()
-{
- setDisplayName(QmlProjectManager::Tr::tr("Qt for MCUs Deploy Step"));
- registerStep<DeployMcuProcessStep>(DeployMcuProcessStep::id);
-}
-
ProjectExplorer::Kit *MCUBuildStepFactory::findMostRecentQulKit()
{
ProjectExplorer::Kit *mcuKit = nullptr;
@@ -168,4 +175,11 @@ void MCUBuildStepFactory::updateDeployStep(ProjectExplorer::Target *target, bool
}
}
+
+MCUBuildStepFactory::MCUBuildStepFactory()
+{
+ setDisplayName(QmlProjectManager::Tr::tr("Qt for MCUs Deploy Step"));
+ registerStep<DeployMcuProcessStep>(DeployMcuProcessStep::id);
+}
+
} // namespace McuSupport::Internal
diff --git a/src/plugins/mcusupport/mcubuildstep.h b/src/plugins/mcusupport/mcubuildstep.h
index aebca09eba..5a202336d7 100644
--- a/src/plugins/mcusupport/mcubuildstep.h
+++ b/src/plugins/mcusupport/mcubuildstep.h
@@ -1,35 +1,17 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
#pragma once
-#include <projectexplorer/abstractprocessstep.h>
#include <projectexplorer/buildstep.h>
-#include <projectexplorer/kit.h>
-#include <projectexplorer/project.h>
-
-#include <utils/id.h>
-
-#include <QTemporaryDir>
namespace McuSupport::Internal {
-class DeployMcuProcessStep : public ProjectExplorer::AbstractProcessStep
-{
-public:
- static const Utils::Id id;
- static void showError(const QString &text);
-
- DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, Utils::Id id);
-
-private:
- QString findKitInformation(ProjectExplorer::Kit *kit, const QString &key);
- QTemporaryDir m_tmpDir;
-};
-
class MCUBuildStepFactory : public ProjectExplorer::BuildStepFactory
{
public:
MCUBuildStepFactory();
+
static ProjectExplorer::Kit *findMostRecentQulKit();
static void updateDeployStep(ProjectExplorer::Target *target, bool enabled);
};
diff --git a/src/plugins/mcusupport/mcukitinformation.cpp b/src/plugins/mcusupport/mcukitinformation.cpp
index 801b7cdecc..2fd6f846e1 100644
--- a/src/plugins/mcusupport/mcukitinformation.cpp
+++ b/src/plugins/mcusupport/mcukitinformation.cpp
@@ -22,7 +22,7 @@ public:
void makeReadOnly() override {}
void refresh() override {}
- void addToLayout(Utils::Layouting::LayoutBuilder &) override {}
+ void addToLayout(Layouting::LayoutItem &) override {}
};
} // anonymous namespace
diff --git a/src/plugins/mcusupport/mcukitmanager.cpp b/src/plugins/mcusupport/mcukitmanager.cpp
index 61f268f021..00709b126f 100644
--- a/src/plugins/mcusupport/mcukitmanager.cpp
+++ b/src/plugins/mcusupport/mcukitmanager.cpp
@@ -3,6 +3,7 @@
#include "mcukitmanager.h"
#include "mculegacyconstants.h"
+#include "mcusupport_global.h"
#include "mcusupportoptions.h"
#include "mcukitinformation.h"
@@ -289,8 +290,7 @@ public:
cMakeToolchainFile.toString().toUtf8());
if (!cMakeToolchainFile.exists()) {
printMessage(
- Tr::tr(
- "Warning for target %1: missing CMake toolchain file expected at %2.")
+ Tr::tr("Warning for target %1: missing CMake toolchain file expected at %2.")
.arg(generateKitNameFromTarget(mcuTarget),
cMakeToolchainFile.toUserOutput()),
false);
@@ -301,8 +301,7 @@ public:
"/lib/cmake/Qul/QulGenerators.cmake");
configMap.insert("QUL_GENERATORS", generatorsPath.toString().toUtf8());
if (!generatorsPath.exists()) {
- printMessage(Tr::tr(
- "Warning for target %1: missing QulGenerators expected at %2.")
+ printMessage(Tr::tr("Warning for target %1: missing QulGenerators expected at %2.")
.arg(generateKitNameFromTarget(mcuTarget),
generatorsPath.toUserOutput()),
false);
@@ -488,32 +487,37 @@ void createAutomaticKits(const SettingsHandler::Ptr &settingsHandler)
{
McuPackagePtr qtForMCUsPackage{createQtForMCUsPackage(settingsHandler)};
- const auto createKits = [qtForMCUsPackage, settingsHandler]() {
+ // add a list of package, board, errormessage,
+ MessagesList autoGenerationMessages;
+
+ const auto createKits = [&autoGenerationMessages, qtForMCUsPackage, settingsHandler] {
if (settingsHandler->isAutomaticKitCreationEnabled()) {
qtForMCUsPackage->updateStatus();
if (!qtForMCUsPackage->isValidStatus()) {
switch (qtForMCUsPackage->status()) {
case McuAbstractPackage::Status::ValidPathInvalidPackage: {
- printMessage(Tr::tr("Path %1 exists, but does not contain %2.")
- .arg(qtForMCUsPackage->path().toUserOutput(),
- qtForMCUsPackage->detectionPath().toUserOutput()),
- true);
+ const QString message
+ = Tr::tr("Path %1 exists, but does not contain %2.")
+ .arg(qtForMCUsPackage->path().toUserOutput(),
+ qtForMCUsPackage->detectionPath().toUserOutput());
+ autoGenerationMessages.push_back({qtForMCUsPackage->label(), "", message});
+ printMessage(message, true);
break;
}
case McuAbstractPackage::Status::InvalidPath: {
- printMessage(Tr::tr(
- "Path %1 does not exist. Add the path in Edit > Preferences > "
- "Devices > MCU.")
- .arg(qtForMCUsPackage->path().toUserOutput()),
- true);
+ const QString message
+ = Tr::tr("Path %1 does not exist. Add the path in Edit > Preferences > "
+ "Devices > MCU.")
+ .arg(qtForMCUsPackage->path().toUserOutput());
+ autoGenerationMessages.push_back({qtForMCUsPackage->label(), "", message});
+ printMessage(message, true);
break;
}
case McuAbstractPackage::Status::EmptyPath: {
- printMessage(
- Tr::tr(
- "Missing %1. Add the path in Edit > Preferences > Devices > MCU.")
- .arg(qtForMCUsPackage->detectionPath().toUserOutput()),
- true);
+ const QString message = Tr::tr("Missing %1. Add the path in Edit > Preferences > Devices > MCU.")
+ .arg(qtForMCUsPackage->detectionPath().toUserOutput());
+ autoGenerationMessages.push_back({qtForMCUsPackage->label(), "", message});
+ printMessage(message, true);
return;
}
default:
@@ -523,11 +527,10 @@ void createAutomaticKits(const SettingsHandler::Ptr &settingsHandler)
}
if (CMakeProjectManager::CMakeToolManager::cmakeTools().isEmpty()) {
- printMessage(
- Tr::tr(
- "No CMake tool was detected. Add a CMake tool in Edit > Preferences > "
- "Kits > CMake."),
- true);
+ const QString message = Tr::tr("No CMake tool was detected. Add a CMake tool in Edit > Preferences > "
+ "Kits > CMake.");
+ autoGenerationMessages.push_back({qtForMCUsPackage->label(), "", message});
+ printMessage(message, true);
return;
}
@@ -547,7 +550,7 @@ void createAutomaticKits(const SettingsHandler::Ptr &settingsHandler)
// if no kits for this target, create
if (target->isValid())
newKit(target.get(), qtForMCUsPackage);
- target->printPackageProblems();
+ target->handlePackageProblems(autoGenerationMessages);
}
}
if (needsUpgrade)
@@ -556,6 +559,9 @@ void createAutomaticKits(const SettingsHandler::Ptr &settingsHandler)
};
createKits();
+ McuSupportOptions::displayKitCreationMessages(autoGenerationMessages,
+ settingsHandler,
+ qtForMCUsPackage);
}
// Maintenance
@@ -573,6 +579,7 @@ void upgradeKitsByCreatingNewPackage(const SettingsHandler::Ptr &settingsHandler
McuSdkRepository repo{targetsAndPackages(qtForMCUsPackage, settingsHandler)};
+ MessagesList messages;
for (const auto &target : std::as_const(repo.mcuTargets)) {
if (!matchingKits(target.get(), qtForMCUsPackage).empty())
// already up-to-date
@@ -587,9 +594,11 @@ void upgradeKitsByCreatingNewPackage(const SettingsHandler::Ptr &settingsHandler
if (target->isValid())
newKit(target.get(), qtForMCUsPackage);
- target->printPackageProblems();
+ target->handlePackageProblems(messages);
}
}
+ // Open the dialog showing warnings and errors in packages
+ McuSupportOptions::displayKitCreationMessages(messages, settingsHandler, qtForMCUsPackage);
}
// Maintenance
@@ -744,11 +753,17 @@ static bool anyKitDescriptionFileExists(const FilePaths &jsonFiles,
const QRegularExpressionMatch match = re.match(jsonFile.fileName());
QStringList kitsPropertiesFromFileName;
if (match.hasMatch()) {
- const QString toolchain = match.captured(1).replace(
- "gnu", "gcc"); // kitFileName contains gnu while profiles.xml contains gcc
+ QString toolchain = match.captured(1);
const QString vendor = match.captured(2);
const QString device = match.captured(3);
+ /*
+ * file name of kit starts with "gnu" while in profiles.xml name of
+ * toolchain is "gcc" on Linux and "mingw" on Windows
+ */
+ toolchain = HostOsInfo::isLinuxHost() ? toolchain.replace("gnu", "gcc")
+ : toolchain.replace("gnu", "mingw");
+
kitsPropertiesFromFileName << toolchain << vendor << device;
}
diff --git a/src/plugins/mcusupport/mcuqmlprojectnode.h b/src/plugins/mcusupport/mcuqmlprojectnode.h
index 2a06e5cde5..bf40a696f6 100644
--- a/src/plugins/mcusupport/mcuqmlprojectnode.h
+++ b/src/plugins/mcusupport/mcuqmlprojectnode.h
@@ -9,7 +9,7 @@
#include <utils/filepath.h>
#include <utils/osspecificaspects.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectnodes.h>
diff --git a/src/plugins/mcusupport/mcusupport.qbs b/src/plugins/mcusupport/mcusupport.qbs
index e6aec151a0..173fe55e6e 100644
--- a/src/plugins/mcusupport/mcusupport.qbs
+++ b/src/plugins/mcusupport/mcusupport.qbs
@@ -20,6 +20,8 @@ QtcPlugin {
files: [
"mcuabstractpackage.h",
+ "mcubuildstep.cpp",
+ "mcubuildstep.h",
"mcupackage.cpp",
"mcupackage.h",
"mcutarget.cpp",
@@ -56,10 +58,11 @@ QtcPlugin {
"mcuhelpers.h",
"settingshandler.h",
"settingshandler.cpp",
+ "dialogs/mcukitcreationdialog.h",
+ "dialogs/mcukitcreationdialog.cpp",
]
- Group {
- name: "McuSupport test files"
+ QtcTestFiles {
condition: qtc.testsEnabled && (qtc_gtest_gmock.hasRepo || qtc_gtest_gmock.externalLibsPresent)
prefix: "test/"
files: [
diff --git a/src/plugins/mcusupport/mcusupport.qrc b/src/plugins/mcusupport/mcusupport.qrc
index abbfb969e3..7aeb7b9387 100644
--- a/src/plugins/mcusupport/mcusupport.qrc
+++ b/src/plugins/mcusupport/mcusupport.qrc
@@ -20,5 +20,6 @@
<file>wizards/qmlproject/module.qmlproject.tpl</file>
<file>wizards/qmlproject/component.qml.tpl</file>
<file>wizards/qmlproject/wizard.json</file>
+ <file>wizards/qmlproject/Qul.cmake</file>
</qresource>
</RCC>
diff --git a/src/plugins/mcusupport/mcusupport_global.h b/src/plugins/mcusupport/mcusupport_global.h
index c1a20746a2..20de9a7329 100644
--- a/src/plugins/mcusupport/mcusupport_global.h
+++ b/src/plugins/mcusupport/mcusupport_global.h
@@ -30,4 +30,13 @@ static const QVersionNumber newVersion{2, 3};
using Targets = QList<McuTargetPtr>;
using Packages = QSet<McuPackagePtr>;
+struct McuSupportMessage
+{
+ QString packageName;
+ QString platform;
+ QString message;
+ enum Status { Warning, Error } status = Status::Error;
+};
+using MessagesList = QList<McuSupportMessage>;
+
} // namespace McuSupport::Internal
diff --git a/src/plugins/mcusupport/mcusupportconstants.h b/src/plugins/mcusupport/mcusupportconstants.h
index 31e67c7b1c..780880d9d1 100644
--- a/src/plugins/mcusupport/mcusupportconstants.h
+++ b/src/plugins/mcusupport/mcusupportconstants.h
@@ -22,5 +22,7 @@ const char SETTINGS_GROUP[]{"McuSupport"};
const char SETTINGS_KEY_PACKAGE_PREFIX[]{"Package_"};
const char SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK[]{"QtForMCUsSdk"}; // Key known by SDK installer
const char SETTINGS_KEY_AUTOMATIC_KIT_CREATION[]{"AutomaticKitCreation"};
+// Used to decide which platform will be displayed first in the MCU Options page
+const char SETTINGS_KEY_INITIAL_PLATFORM_KEY[]{"McuSupport.InitialPlatform"};
} // namespace McuSupport::Internal::Constants
diff --git a/src/plugins/mcusupport/mcusupportdevice.h b/src/plugins/mcusupport/mcusupportdevice.h
index 262edc39ab..78bf6b68e3 100644
--- a/src/plugins/mcusupport/mcusupportdevice.h
+++ b/src/plugins/mcusupport/mcusupportdevice.h
@@ -4,6 +4,7 @@
#pragma once
#include <projectexplorer/devicesupport/desktopdevice.h>
+#include <projectexplorer/devicesupport/idevicefactory.h>
namespace McuSupport {
namespace Internal {
diff --git a/src/plugins/mcusupport/mcusupportoptions.cpp b/src/plugins/mcusupport/mcusupportoptions.cpp
index 474314b6a6..51cb1eacd8 100644
--- a/src/plugins/mcusupport/mcusupportoptions.cpp
+++ b/src/plugins/mcusupport/mcusupportoptions.cpp
@@ -3,9 +3,11 @@
#include "mcusupportoptions.h"
+#include "dialogs/mcukitcreationdialog.h"
#include "mcuhelpers.h"
#include "mcukitmanager.h"
#include "mcupackage.h"
+#include "mcusupport_global.h"
#include "mcusupportconstants.h"
#include "mcusupportsdk.h"
#include "mcusupporttr.h"
@@ -19,6 +21,7 @@
#include <debugger/debuggerkitinformation.h>
#include <utils/algorithm.h>
#include <utils/filepath.h>
+#include <utils/infobar.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtversionmanager.h>
@@ -252,6 +255,30 @@ McuKitManager::UpgradeOption McuSupportOptions::askForKitUpgrades()
return McuKitManager::UpgradeOption::Ignore;
}
+void McuSupportOptions::displayKitCreationMessages(const MessagesList &messages,
+ const SettingsHandler::Ptr &settingsHandler,
+ McuPackagePtr qtMCUsPackage)
+{
+ if (messages.isEmpty() || !qtMCUsPackage->isValidStatus())
+ return;
+ static const char mcuKitCreationErrorInfoId[] = "ErrorWhileCreatingMCUKits";
+ if (!Core::ICore::infoBar()->canInfoBeAdded(mcuKitCreationErrorInfoId))
+ return;
+
+ Utils::InfoBarEntry info(mcuKitCreationErrorInfoId,
+ Tr::tr("Errors while creating Qt for MCUs kits"),
+ Utils::InfoBarEntry::GlobalSuppression::Enabled);
+
+ info.addCustomButton(Tr::tr("Details"), [=] {
+ auto popup = new McuKitCreationDialog(messages, settingsHandler, qtMCUsPackage);
+ popup->exec();
+ delete popup;
+ Core::ICore::infoBar()->removeInfo(mcuKitCreationErrorInfoId);
+ });
+
+ Core::ICore::infoBar()->addInfo(info);
+}
+
void McuSupportOptions::checkUpgradeableKits()
{
if (!qtForMCUsSdkPackage->isValidStatus() || sdkRepository.mcuTargets.isEmpty())
diff --git a/src/plugins/mcusupport/mcusupportoptions.h b/src/plugins/mcusupport/mcusupportoptions.h
index a442c0864a..1090f87100 100644
--- a/src/plugins/mcusupport/mcusupportoptions.h
+++ b/src/plugins/mcusupport/mcusupportoptions.h
@@ -61,6 +61,9 @@ public:
[[nodiscard]] Utils::FilePath qulDirFromSettings() const;
[[nodiscard]] Utils::FilePath qulDocsDir() const;
static McuKitManager::UpgradeOption askForKitUpgrades();
+ static void displayKitCreationMessages(const MessagesList &messages,
+ const SettingsHandler::Ptr &settingsHandler,
+ McuPackagePtr qtMCUsPackage);
void registerQchFiles() const;
void registerExamples() const;
diff --git a/src/plugins/mcusupport/mcusupportoptionspage.cpp b/src/plugins/mcusupport/mcusupportoptionspage.cpp
index d75568a8f6..a05a3771b0 100644
--- a/src/plugins/mcusupport/mcusupportoptionspage.cpp
+++ b/src/plugins/mcusupport/mcusupportoptionspage.cpp
@@ -350,10 +350,17 @@ void McuSupportOptionsWidget::populateMcuTargetsComboBox()
{
m_options.populatePackagesAndTargets();
m_mcuTargetsComboBox->clear();
+ int initialPlatformIndex = 0;
+ int targetsCounter = -1;
m_mcuTargetsComboBox->addItems(
- Utils::transform<QStringList>(m_options.sdkRepository.mcuTargets, [](const McuTargetPtr &t) {
+ Utils::transform<QStringList>(m_options.sdkRepository.mcuTargets, [&](const McuTargetPtr &t) {
+ if (t->platform().name == m_settingsHandler->initialPlatformName())
+ initialPlatformIndex = m_options.sdkRepository.mcuTargets.indexOf(t);
+ targetsCounter++;
return McuKitManager::generateKitNameFromTarget(t.get());
}));
+ if (targetsCounter != -1)
+ m_mcuTargetsComboBox->setCurrentIndex(initialPlatformIndex);
updateStatus();
}
diff --git a/src/plugins/mcusupport/mcusupportplugin.cpp b/src/plugins/mcusupport/mcusupportplugin.cpp
index 274cd738e1..f2ec2ef69a 100644
--- a/src/plugins/mcusupport/mcusupportplugin.cpp
+++ b/src/plugins/mcusupport/mcusupportplugin.cpp
@@ -28,8 +28,8 @@
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <cmakeprojectmanager/cmakeprojectconstants.h>
@@ -115,8 +115,8 @@ void McuSupportPlugin::initialize()
setObjectName("McuSupportPlugin");
dd = new McuSupportPluginPrivate;
- connect(SessionManager::instance(),
- &SessionManager::projectFinishedParsing,
+ connect(ProjectManager::instance(),
+ &ProjectManager::projectFinishedParsing,
updateMCUProjectTree);
dd->m_options.registerQchFiles();
diff --git a/src/plugins/mcusupport/mcusupportversiondetection.cpp b/src/plugins/mcusupport/mcusupportversiondetection.cpp
index b31d01d8bc..f92a443c4f 100644
--- a/src/plugins/mcusupport/mcusupportversiondetection.cpp
+++ b/src/plugins/mcusupport/mcusupportversiondetection.cpp
@@ -3,7 +3,7 @@
#include "mcusupportversiondetection.h"
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QDir>
#include <QRegularExpression>
@@ -41,7 +41,7 @@ QString McuPackageExecutableVersionDetector::parseVersion(const FilePath &packag
return {};
const int timeout = 3000; // usually runs below 1s, but we want to be on the safe side
- QtcProcess process;
+ Process process;
process.setCommand({binaryPath, m_detectionArgs});
process.start();
if (!process.waitForFinished(timeout) || process.result() != ProcessResult::FinishedWithSuccess)
diff --git a/src/plugins/mcusupport/mcutarget.cpp b/src/plugins/mcusupport/mcutarget.cpp
index 2040c26039..d7a3b0fe39 100644
--- a/src/plugins/mcusupport/mcutarget.cpp
+++ b/src/plugins/mcusupport/mcutarget.cpp
@@ -1,11 +1,12 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "mcutarget.h"
#include "mcukitmanager.h"
#include "mcupackage.h"
+#include "mcusupport_global.h"
#include "mcusupportplugin.h"
#include "mcusupporttr.h"
-#include "mcutarget.h"
#include <utils/algorithm.h>
@@ -82,22 +83,33 @@ QString McuTarget::desktopCompilerId() const
return QLatin1String("invalid");
}
-void McuTarget::printPackageProblems() const
+void McuTarget::handlePackageProblems(MessagesList &messages) const
{
for (auto package : packages()) {
package->updateStatus();
- if (!package->isValidStatus())
+ if (!package->isValidStatus()) {
printMessage(Tr::tr("Error creating kit for target %1, package %2: %3")
.arg(McuKitManager::generateKitNameFromTarget(this),
package->label(),
package->statusText()),
true);
- if (package->status() == McuAbstractPackage::Status::ValidPackageMismatchedVersion)
+ messages.push_back({package->label(),
+ this->platform().name,
+ package->statusText(),
+ McuSupportMessage::Error});
+ }
+ if (package->status() == McuAbstractPackage::Status::ValidPackageMismatchedVersion) {
printMessage(Tr::tr("Warning creating kit for target %1, package %2: %3")
.arg(McuKitManager::generateKitNameFromTarget(this),
package->label(),
package->statusText()),
false);
+
+ messages.push_back({package->label(),
+ this->platform().name,
+ package->statusText(),
+ McuSupportMessage::Warning});
+ }
}
}
diff --git a/src/plugins/mcusupport/mcutarget.h b/src/plugins/mcusupport/mcutarget.h
index 57f28030be..8ee19f54ab 100644
--- a/src/plugins/mcusupport/mcutarget.h
+++ b/src/plugins/mcusupport/mcutarget.h
@@ -54,7 +54,7 @@ public:
int colorDepth() const;
bool isValid() const;
QString desktopCompilerId() const;
- void printPackageProblems() const;
+ void handlePackageProblems(MessagesList &messages) const;
private:
const QVersionNumber m_qulVersion;
diff --git a/src/plugins/mcusupport/settingshandler.cpp b/src/plugins/mcusupport/settingshandler.cpp
index d292ea6cd6..67fbd27f97 100644
--- a/src/plugins/mcusupport/settingshandler.cpp
+++ b/src/plugins/mcusupport/settingshandler.cpp
@@ -68,4 +68,17 @@ void SettingsHandler::setAutomaticKitCreation(bool isEnabled)
settings->setValue(automaticKitCreationSettingsKey, isEnabled);
}
+void SettingsHandler::setInitialPlatformName(const QString &platform)
+{
+ QSettings *settings = Core::ICore::settings(QSettings::UserScope);
+ settings->setValue(Constants::SETTINGS_KEY_INITIAL_PLATFORM_KEY, platform);
+}
+
+QString SettingsHandler::initialPlatformName() const
+{
+ QSettings *settings = Core::ICore::settings(QSettings::UserScope);
+ const QString name
+ = settings->value(Constants::SETTINGS_KEY_INITIAL_PLATFORM_KEY, "").toString();
+ return name;
+}
} // namespace McuSupport::Internal
diff --git a/src/plugins/mcusupport/settingshandler.h b/src/plugins/mcusupport/settingshandler.h
index 02bbbcf26d..145049e31d 100644
--- a/src/plugins/mcusupport/settingshandler.h
+++ b/src/plugins/mcusupport/settingshandler.h
@@ -27,5 +27,8 @@ public:
virtual bool isAutomaticKitCreationEnabled() const;
void setAutomaticKitCreation(bool isEnabled);
+ void setInitialPlatformName(const QString &platform);
+ QString initialPlatformName() const;
+
}; //class SettingsHandler
} // namespace McuSupport::Internal
diff --git a/src/plugins/mcusupport/test/unittest.cpp b/src/plugins/mcusupport/test/unittest.cpp
index b9fc108ef7..a8a8f09139 100644
--- a/src/plugins/mcusupport/test/unittest.cpp
+++ b/src/plugins/mcusupport/test/unittest.cpp
@@ -152,11 +152,7 @@ const QString renesasProgrammerEnvVar{"RenesasFlashProgrammer_PATH"};
const char renesasProgrammerLabel[]{"Renesas Flash Programmer"};
const QString renesasProgrammerDetectionPath{HostOsInfo::withExecutableSuffix("rfp-cli")};
-const char renesasE2StudioCmakeVar[]{"EK_RA6M3G_E2_PROJECT_PATH"};
-const char renesasE2StudioDefaultPath[]{"%{Env:HOME}/e2_studio/workspace"};
const QString renesasE2StudioPath{(FileUtils::homePath() / "/e2_studio/workspace").toUserOutput()};
-const char renesasE2StudioLabel[]{"Path to project for Renesas e2 Studio"};
-const char renesasE2StudioSetting[]{"RenesasE2StudioPath"};
const char cypressProgrammerSetting[]{"CypressAutoFlashUtil"};
const char cypressProgrammerCmakeVar[]{"INFINEON_AUTO_FLASH_UTILITY_DIR"};
@@ -1760,9 +1756,9 @@ void McuSupportTest::test_nonemptyVersionDetector()
// pkgDesc.versionDetection.xmlAttribute left empty
pkgDesc.shouldAddToSystemPath = false;
const auto package = targetFactory.createPackage(pkgDesc);
- QVERIFY(package->getVersionDetector() != nullptr);
- QCOMPARE(typeid(*package->getVersionDetector()).name(),
- typeid(McuPackageExecutableVersionDetector).name());
+ const McuPackageVersionDetector *detector = package->getVersionDetector();
+ QVERIFY(detector != nullptr);
+ QCOMPARE(typeid(*detector).name(), typeid(McuPackageExecutableVersionDetector).name());
}
void McuSupportTest::test_emptyVersionDetector()
diff --git a/src/plugins/mcusupport/wizards/qmlproject/CMakeLists.txt b/src/plugins/mcusupport/wizards/qmlproject/CMakeLists.txt
index 49a4a8f949..47eed40c78 100644
--- a/src/plugins/mcusupport/wizards/qmlproject/CMakeLists.txt
+++ b/src/plugins/mcusupport/wizards/qmlproject/CMakeLists.txt
@@ -4,5 +4,4 @@ project(%{CorrectedProjectName} VERSION 0.0.1 LANGUAGES C CXX ASM)
find_package(Qul)
-qul_add_target(%{CorrectedProjectName} QML_PROJECT %{QmlProjectFile} GENERATE_ENTRYPOINT)
-app_target_setup_os(%{CorrectedProjectName})
+add_subdirectory(qmlproject)
diff --git a/src/plugins/mcusupport/wizards/qmlproject/Qul.cmake b/src/plugins/mcusupport/wizards/qmlproject/Qul.cmake
new file mode 100644
index 0000000000..1ece8f48d6
--- /dev/null
+++ b/src/plugins/mcusupport/wizards/qmlproject/Qul.cmake
@@ -0,0 +1,3 @@
+
+qul_add_target(%{CorrectedProjectName} QML_PROJECT %{QmlProjectFile} GENERATE_ENTRYPOINT)
+app_target_setup_os(%{CorrectedProjectName})
diff --git a/src/plugins/mcusupport/wizards/qmlproject/wizard.json b/src/plugins/mcusupport/wizards/qmlproject/wizard.json
index 173c430eeb..9c1e036e91 100644
--- a/src/plugins/mcusupport/wizards/qmlproject/wizard.json
+++ b/src/plugins/mcusupport/wizards/qmlproject/wizard.json
@@ -3,7 +3,7 @@
"supportedProjectTypes": [ "CMakeProjectManager.CMakeProject" ],
"id": "M.McuSupportApplication",
"category": "D.ApplicationMCU",
- "trDescription": "Suitable for Qt for MCUs versions 2.4 and later. Creates a Qt for MCUs application with a simple UI, based on qmlproject.",
+ "trDescription": "Suitable for Qt for MCUs versions 2.4 and later. Creates an application that uses a subset of Qt QML and Qt Quick Controls types (as supported by Qt for MCUs) that you can deploy, run, and debug on MCU boards.",
"trDisplayName": "Qt for MCUs Application",
"trDisplayCategory": "QmlProject Application (Qt for MCUs)",
"icon": "../icon.png",
@@ -12,6 +12,7 @@
"options":
[
+ { "key": "QmlProjectDirectory", "value": "%{ProjectDirectory}/qmlproject"},
{ "key": "CorrectedProjectName", "value": "%{JS: '%{ProjectName}'.replace(/-/g, '_')}"},
{ "key": "MainQmlFile", "value": "%{CorrectedProjectName}.qml" },
{ "key": "QmlProjectFile", "value": "%{CorrectedProjectName}.qmlproject" },
@@ -56,48 +57,53 @@
"openAsProject": true
},
{
+ "source": "Qul.cmake",
+ "target": "%{QmlProjectDirectory}/CMakeLists.txt",
+ "openInEditor": false
+ },
+ {
"source": "BackendObject.h",
- "target": "%{ProjectDirectory}/src/%{InterfaceFile}",
+ "target": "%{QmlProjectDirectory}/src/%{InterfaceFile}",
"openInEditor": true
},
{
"source": "component.qml.tpl",
- "target": "%{ProjectDirectory}/imports/CustomModule/%{QmlComponent}",
+ "target": "%{QmlProjectDirectory}/imports/CustomModule/%{QmlComponent}",
"openInEditor": true
},
{
"source": "module.qmlproject.tpl",
- "target": "%{ProjectDirectory}/imports/CustomModule/%{ModuleFile}",
+ "target": "%{QmlProjectDirectory}/imports/CustomModule/%{ModuleFile}",
"openInEditor": true
},
{
"source": "project.qmlproject.tpl",
- "target": "%{ProjectDirectory}/%{QmlProjectFile}",
+ "target": "%{QmlProjectDirectory}/%{QmlProjectFile}",
"openInEditor": true
},
{
"source": "main.qml.tpl",
- "target": "%{ProjectDirectory}/%{MainQmlFile}",
+ "target": "%{QmlProjectDirectory}/%{MainQmlFile}",
"openInEditor": true
},
{
"source": "../icon.png",
- "target": "%{ProjectDirectory}/images/icon.png",
+ "target": "%{QmlProjectDirectory}/images/icon.png",
"isBinary": true
},
{
"source": "DejaVuSansMono.ttf",
- "target": "%{ProjectDirectory}/fonts/DejaVuSansMono.ttf",
+ "target": "%{QmlProjectDirectory}/fonts/DejaVuSansMono.ttf",
"isBinary": true
},
{
"source": "LICENSE",
- "target": "%{ProjectDirectory}/fonts/LICENSE",
+ "target": "%{QmlProjectDirectory}/fonts/LICENSE",
"isBinary": true
},
{
"source": "translation.nb_NO.ts",
- "target": "%{ProjectDirectory}/translations/%{TsFile}",
+ "target": "%{QmlProjectDirectory}/translations/%{TsFile}",
"openInEditor": false
},
{
diff --git a/src/plugins/mercurial/authenticationdialog.cpp b/src/plugins/mercurial/authenticationdialog.cpp
index 8538fd2812..d9d552274d 100644
--- a/src/plugins/mercurial/authenticationdialog.cpp
+++ b/src/plugins/mercurial/authenticationdialog.cpp
@@ -24,7 +24,7 @@ AuthenticationDialog::AuthenticationDialog(const QString &username, const QStrin
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Form {
diff --git a/src/plugins/mercurial/mercurialclient.cpp b/src/plugins/mercurial/mercurialclient.cpp
index 43f942a167..6a0c2574d4 100644
--- a/src/plugins/mercurial/mercurialclient.cpp
+++ b/src/plugins/mercurial/mercurialclient.cpp
@@ -12,8 +12,8 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <vcsbase/vcsbasediffeditorcontroller.h>
#include <vcsbase/vcsbaseeditor.h>
@@ -55,17 +55,17 @@ MercurialDiffEditorController::MercurialDiffEditorController(IDocument *document
const TreeStorage<QString> diffInputStorage = inputStorage();
- const auto setupDiff = [=](QtcProcess &process) {
+ const auto setupDiff = [=](Process &process) {
setupCommand(process, {addConfigurationArguments(args)});
VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine());
};
- const auto onDiffDone = [diffInputStorage](const QtcProcess &process) {
- *diffInputStorage.activeStorage() = process.cleanedStdOut();
+ const auto onDiffDone = [diffInputStorage](const Process &process) {
+ *diffInputStorage = process.cleanedStdOut();
};
const Group root {
Storage(diffInputStorage),
- Process(setupDiff, onDiffDone),
+ ProcessTask(setupDiff, onDiffDone),
postProcessTask()
};
setReloadRecipe(root);
@@ -81,7 +81,8 @@ QStringList MercurialDiffEditorController::addConfigurationArguments(const QStri
/////////////////////////////////////////////////////////////
-MercurialClient::MercurialClient(MercurialSettings *settings) : VcsBaseClient(settings)
+MercurialClient::MercurialClient()
+ : VcsBaseClient(&Internal::settings())
{
}
@@ -427,7 +428,7 @@ void MercurialClient::requestReload(const QString &documentId, const FilePath &s
IDocument *document = DiffEditorController::findOrCreateDocument(documentId, title);
QTC_ASSERT(document, return);
auto controller = new MercurialDiffEditorController(document, args);
- controller->setVcsBinary(settings().binaryPath.filePath());
+ controller->setVcsBinary(settings().binaryPath());
controller->setProcessEnvironment(processEnvironment());
controller->setWorkingDirectory(workingDirectory);
diff --git a/src/plugins/mercurial/mercurialclient.h b/src/plugins/mercurial/mercurialclient.h
index 15c31845db..27badd45d0 100644
--- a/src/plugins/mercurial/mercurialclient.h
+++ b/src/plugins/mercurial/mercurialclient.h
@@ -16,8 +16,9 @@ class MercurialDiffEditorController;
class MercurialClient : public VcsBase::VcsBaseClient
{
Q_OBJECT
+
public:
- explicit MercurialClient(MercurialSettings *settings);
+ MercurialClient();
bool synchronousClone(const Utils::FilePath &workingDir,
const QString &srcLocation,
diff --git a/src/plugins/mercurial/mercurialcommitwidget.cpp b/src/plugins/mercurial/mercurialcommitwidget.cpp
index 7fcd333930..c6b35a67d0 100644
--- a/src/plugins/mercurial/mercurialcommitwidget.cpp
+++ b/src/plugins/mercurial/mercurialcommitwidget.cpp
@@ -101,7 +101,7 @@ public:
m_authorLineEdit = new QLineEdit;
m_emailLineEdit = new QLineEdit;
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
@@ -119,8 +119,9 @@ public:
Tr::tr("Email:"), m_emailLineEdit,
},
}
- }
- }.attachTo(this, Utils::Layouting::WithoutMargins);
+ },
+ noMargin
+ }.attachTo(this);
}
QLabel *m_repositoryLabel;
diff --git a/src/plugins/mercurial/mercurialplugin.cpp b/src/plugins/mercurial/mercurialplugin.cpp
index e84c26a6ca..cc294cad57 100644
--- a/src/plugins/mercurial/mercurialplugin.cpp
+++ b/src/plugins/mercurial/mercurialplugin.cpp
@@ -170,8 +170,7 @@ private:
// Variables
MercurialSettings m_settings;
- MercurialClient m_client{&m_settings};
- MercurialSettingsPage m_settingsPage{&m_settings};
+ MercurialClient m_client;
Core::CommandLocator *m_commandLocator = nullptr;
Core::ActionContainer *m_mercurialContainer = nullptr;
@@ -254,7 +253,7 @@ MercurialPluginPrivate::MercurialPluginPrivate()
createMenu(context);
- connect(&m_settings, &AspectContainer::applied, this, &IVersionControl::configurationChanged);
+ connect(&settings(), &AspectContainer::applied, this, &IVersionControl::configurationChanged);
}
void MercurialPluginPrivate::createMenu(const Core::Context &context)
@@ -633,8 +632,8 @@ void MercurialPluginPrivate::showCommitWidget(const QList<VcsBaseClient::StatusI
const QString branch = vcsTopic(m_submitRepository);
commitEditor->setFields(m_submitRepository, branch,
- m_settings.userName.value(),
- m_settings.userEmail.value(), status);
+ settings().userName(),
+ settings().userEmail(), status);
}
void MercurialPluginPrivate::diffFromEditorSelected(const QStringList &files)
@@ -716,7 +715,7 @@ bool MercurialPluginPrivate::managesFile(const FilePath &workingDirectory, const
bool MercurialPluginPrivate::isConfigured() const
{
- const FilePath binary = m_settings.binaryPath.filePath();
+ const FilePath binary = settings().binaryPath();
if (binary.isEmpty())
return false;
QFileInfo fi = binary.toFileInfo();
@@ -784,7 +783,7 @@ VcsCommand *MercurialPluginPrivate::createInitialCheckoutCommand(const QString &
QStringList args;
args << QLatin1String("clone") << extraArgs << url << localName;
auto command = VcsBaseClient::createVcsCommand(baseDirectory, m_client.processEnvironment());
- command->addJob({m_settings.binaryPath.filePath(), args}, -1);
+ command->addJob({settings().binaryPath(), args}, -1);
return command;
}
diff --git a/src/plugins/mercurial/mercurialsettings.cpp b/src/plugins/mercurial/mercurialsettings.cpp
index 6d21550bc7..0b493e277b 100644
--- a/src/plugins/mercurial/mercurialsettings.cpp
+++ b/src/plugins/mercurial/mercurialsettings.cpp
@@ -14,74 +14,64 @@ using namespace Utils;
namespace Mercurial::Internal {
+static MercurialSettings *theSettings;
+
+MercurialSettings &settings()
+{
+ return *theSettings;
+}
+
MercurialSettings::MercurialSettings()
{
+ theSettings = this;
+
+ setId(VcsBase::Constants::VCS_ID_MERCURIAL);
+ setDisplayName(Tr::tr("Mercurial"));
+ setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
setSettingsGroup("Mercurial");
- setAutoApply(false);
- registerAspect(&binaryPath);
- binaryPath.setDisplayStyle(StringAspect::PathChooserDisplay);
binaryPath.setExpectedKind(PathChooser::ExistingCommand);
binaryPath.setDefaultValue(Constants::MERCURIALDEFAULT);
binaryPath.setDisplayName(Tr::tr("Mercurial Command"));
binaryPath.setHistoryCompleter("Bazaar.Command.History");
binaryPath.setLabelText(Tr::tr("Command:"));
- registerAspect(&userName);
userName.setDisplayStyle(StringAspect::LineEditDisplay);
userName.setLabelText(Tr::tr("Default username:"));
userName.setToolTip(Tr::tr("Username to use by default on commit."));
- registerAspect(&userEmail);
userEmail.setDisplayStyle(StringAspect::LineEditDisplay);
userEmail.setLabelText(Tr::tr("Default email:"));
userEmail.setToolTip(Tr::tr("Email to use by default on commit."));
- registerAspect(&diffIgnoreWhiteSpace);
diffIgnoreWhiteSpace.setSettingsKey("diffIgnoreWhiteSpace");
- registerAspect(&diffIgnoreBlankLines);
diffIgnoreBlankLines.setSettingsKey("diffIgnoreBlankLines");
-}
-
-// MercurialSettingsPage
-
-MercurialSettingsPage::MercurialSettingsPage(MercurialSettings *settings)
-{
- setId(VcsBase::Constants::VCS_ID_MERCURIAL);
- setDisplayName(Tr::tr("Mercurial"));
- setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
- setSettings(settings);
- setLayouter([settings](QWidget *widget) {
- MercurialSettings &s = *settings;
+ setLayouter([this] {
using namespace Layouting;
- Column {
+ return Column {
Group {
title(Tr::tr("Configuration")),
- Row { s.binaryPath }
+ Row { binaryPath }
},
Group {
title(Tr::tr("User")),
Form {
- s.userName,
- s.userEmail
+ userName, br,
+ userEmail
}
},
Group {
title(Tr::tr("Miscellaneous")),
- Row {
- s.logCount,
- s.timeout,
- st
- }
+ Row { logCount, timeout, st }
},
st
- }.attachTo(widget);
+ };
});
}
diff --git a/src/plugins/mercurial/mercurialsettings.h b/src/plugins/mercurial/mercurialsettings.h
index 881bcd0447..2102d7037d 100644
--- a/src/plugins/mercurial/mercurialsettings.h
+++ b/src/plugins/mercurial/mercurialsettings.h
@@ -3,8 +3,6 @@
#pragma once
-#include <coreplugin/dialogs/ioptionspage.h>
-
#include <vcsbase/vcsbaseclientsettings.h>
namespace Mercurial::Internal {
@@ -14,14 +12,10 @@ class MercurialSettings : public VcsBase::VcsBaseSettings
public:
MercurialSettings();
- Utils::StringAspect diffIgnoreWhiteSpace;
- Utils::StringAspect diffIgnoreBlankLines;
+ Utils::StringAspect diffIgnoreWhiteSpace{this};
+ Utils::StringAspect diffIgnoreBlankLines{this};
};
-class MercurialSettingsPage final : public Core::IOptionsPage
-{
-public:
- explicit MercurialSettingsPage(MercurialSettings *settings);
-};
+MercurialSettings &settings();
} // Mercurial::Internal
diff --git a/src/plugins/mercurial/revertdialog.cpp b/src/plugins/mercurial/revertdialog.cpp
index c36eb85fe7..29ee250e55 100644
--- a/src/plugins/mercurial/revertdialog.cpp
+++ b/src/plugins/mercurial/revertdialog.cpp
@@ -11,8 +11,6 @@
#include <QGroupBox>
#include <QLineEdit>
-using namespace Utils;
-
namespace Mercurial::Internal {
RevertDialog::RevertDialog(QWidget *parent)
@@ -32,8 +30,8 @@ RevertDialog::RevertDialog(QWidget *parent)
using namespace Layouting;
Form {
- Tr::tr("Revision:"), m_revisionLineEdit,
- }.attachTo(groupBox, WithMargins);
+ Tr::tr("Revision:"), m_revisionLineEdit, normalMargin
+ }.attachTo(groupBox);
Column {
groupBox,
diff --git a/src/plugins/mesonprojectmanager/CMakeLists.txt b/src/plugins/mesonprojectmanager/CMakeLists.txt
index d4d7420d7b..3c541a1219 100644
--- a/src/plugins/mesonprojectmanager/CMakeLists.txt
+++ b/src/plugins/mesonprojectmanager/CMakeLists.txt
@@ -75,8 +75,6 @@ add_qtc_plugin(MesonProjectManager
toolssettingsaccessor.h
toolssettingspage.cpp
toolssettingspage.h
- toolssettingswidget.cpp
- toolssettingswidget.h
tooltreeitem.cpp
tooltreeitem.h
toolwrapper.cpp
diff --git a/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp b/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp
index a24a1f52bf..514c529212 100644
--- a/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp
+++ b/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp
@@ -19,7 +19,7 @@
#include <projectexplorer/projectexplorer.h>
#include <utils/fileutils.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QDir>
@@ -91,7 +91,8 @@ void MesonBuildConfiguration::build(const QString &target)
QStringList MesonBuildConfiguration::mesonConfigArgs()
{
- return Utils::ProcessArgs::splitArgs(m_parameters) + QStringList{QString("-Dbuildtype=%1").arg(mesonBuildTypeName(m_buildType))};
+ return Utils::ProcessArgs::splitArgs(m_parameters, HostOsInfo::hostOs())
+ + QStringList{QString("-Dbuildtype=%1").arg(mesonBuildTypeName(m_buildType))};
}
const QString &MesonBuildConfiguration::parameters() const
diff --git a/src/plugins/mesonprojectmanager/mesonbuildconfiguration.h b/src/plugins/mesonprojectmanager/mesonbuildconfiguration.h
index 6bc4a96afe..a405909189 100644
--- a/src/plugins/mesonprojectmanager/mesonbuildconfiguration.h
+++ b/src/plugins/mesonprojectmanager/mesonbuildconfiguration.h
@@ -2,8 +2,8 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
-#include "projectexplorer/buildconfiguration.h"
-#include "projectexplorer/target.h"
+#include <projectexplorer/buildconfiguration.h>
+#include <projectexplorer/target.h>
namespace MesonProjectManager {
namespace Internal {
diff --git a/src/plugins/mesonprojectmanager/mesonbuildsettingswidget.cpp b/src/plugins/mesonprojectmanager/mesonbuildsettingswidget.cpp
index cedd9c5f3e..32719c08d6 100644
--- a/src/plugins/mesonprojectmanager/mesonbuildsettingswidget.cpp
+++ b/src/plugins/mesonprojectmanager/mesonbuildsettingswidget.cpp
@@ -67,16 +67,18 @@ MesonBuildSettingsWidget::MesonBuildSettingsWidget(MesonBuildConfiguration *buil
buildDirWidget,
optionsFilterLineEdit,
optionsTreeView,
- }.attachTo(details, WithoutMargins);
+ noMargin
+ }.attachTo(details);
Column {
container,
- Row { configureButton, wipeButton, }
- }.attachTo(this, WithoutMargins);
+ Row { configureButton, wipeButton, noMargin }
+ }.attachTo(this);
- Form buildDirWBuilder;
- buildCfg->buildDirectoryAspect()->addToLayout(buildDirWBuilder);
- buildDirWBuilder.attachTo(buildDirWidget, WithoutMargins);
+ Form {
+ buildCfg->buildDirectoryAspect(),
+ noMargin
+ }.attachTo(buildDirWidget);
parametersLineEdit->setText(buildCfg->parameters());
optionsFilterLineEdit->setFiltering(true);
diff --git a/src/plugins/mesonprojectmanager/mesonbuildsystem.cpp b/src/plugins/mesonprojectmanager/mesonbuildsystem.cpp
index 13617fa014..6d3e5f20c4 100644
--- a/src/plugins/mesonprojectmanager/mesonbuildsystem.cpp
+++ b/src/plugins/mesonprojectmanager/mesonbuildsystem.cpp
@@ -192,7 +192,7 @@ void MesonBuildSystem::init()
bool MesonBuildSystem::parseProject()
{
QTC_ASSERT(buildConfiguration(), return false);
- if (!isSetup(buildConfiguration()->buildDirectory()) && Settings::instance()->autorunMeson.value())
+ if (!isSetup(buildConfiguration()->buildDirectory()) && settings().autorunMeson())
return configure();
LEAVE_IF_BUSY();
LOCK();
diff --git a/src/plugins/mesonprojectmanager/mesonpluginconstants.h b/src/plugins/mesonprojectmanager/mesonpluginconstants.h
index 995e41cbb5..108c125f50 100644
--- a/src/plugins/mesonprojectmanager/mesonpluginconstants.h
+++ b/src/plugins/mesonprojectmanager/mesonpluginconstants.h
@@ -18,7 +18,6 @@ const char PARAMETERS_KEY[] = "MesonProjectManager.BuildConfig.Parameters";
// Settings page
namespace SettingsPage {
-const char GENERAL_ID[] = "A.MesonProjectManager.SettingsPage.General";
const char TOOLS_ID[] = "Z.MesonProjectManager.SettingsPage.Tools";
const char CATEGORY[] = "Z.Meson";
} // namespace SettingsPage
diff --git a/src/plugins/mesonprojectmanager/mesonprocess.cpp b/src/plugins/mesonprojectmanager/mesonprocess.cpp
index 0c828588c2..2e4ba02455 100644
--- a/src/plugins/mesonprojectmanager/mesonprocess.cpp
+++ b/src/plugins/mesonprojectmanager/mesonprocess.cpp
@@ -13,7 +13,7 @@
#include <projectexplorer/taskhub.h>
#include <utils/environment.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/stringutils.h>
#include <QLoggingCategory>
@@ -63,12 +63,12 @@ void MesonProcess::setupProcess(const Command &command, const Environment &env,
{
if (m_process)
m_process.release()->deleteLater();
- m_process.reset(new QtcProcess);
- connect(m_process.get(), &QtcProcess::done, this, &MesonProcess::handleProcessDone);
+ m_process.reset(new Process);
+ connect(m_process.get(), &Process::done, this, &MesonProcess::handleProcessDone);
if (!captureStdo) {
- connect(m_process.get(), &QtcProcess::readyReadStandardOutput,
+ connect(m_process.get(), &Process::readyReadStandardOutput,
this, &MesonProcess::processStandardOutput);
- connect(m_process.get(), &QtcProcess::readyReadStandardError,
+ connect(m_process.get(), &Process::readyReadStandardError,
this, &MesonProcess::processStandardError);
}
diff --git a/src/plugins/mesonprojectmanager/mesonprocess.h b/src/plugins/mesonprojectmanager/mesonprocess.h
index 949af3339f..c01427c8fb 100644
--- a/src/plugins/mesonprojectmanager/mesonprocess.h
+++ b/src/plugins/mesonprojectmanager/mesonprocess.h
@@ -12,7 +12,7 @@
namespace Utils {
class Environment;
-class QtcProcess;
+class Process;
}
namespace MesonProjectManager {
@@ -44,7 +44,7 @@ private:
void processStandardOutput();
void processStandardError();
- std::unique_ptr<Utils::QtcProcess> m_process;
+ std::unique_ptr<Utils::Process> m_process;
QElapsedTimer m_elapsed;
QByteArray m_stdo;
QByteArray m_stderr;
diff --git a/src/plugins/mesonprojectmanager/mesonprojectmanager.qbs b/src/plugins/mesonprojectmanager/mesonprojectmanager.qbs
index 9de8b4ccde..c721ab2d20 100644
--- a/src/plugins/mesonprojectmanager/mesonprojectmanager.qbs
+++ b/src/plugins/mesonprojectmanager/mesonprojectmanager.qbs
@@ -95,8 +95,6 @@ Project {
"toolssettingsaccessor.h",
"toolssettingspage.cpp",
"toolssettingspage.h",
- "toolssettingswidget.cpp",
- "toolssettingswidget.h",
"tooltreeitem.cpp",
"tooltreeitem.h",
"versionhelper.h",
diff --git a/src/plugins/mesonprojectmanager/mesonprojectparser.cpp b/src/plugins/mesonprojectmanager/mesonprojectparser.cpp
index 5a9babeedf..c84b5e4b3d 100644
--- a/src/plugins/mesonprojectmanager/mesonprojectparser.cpp
+++ b/src/plugins/mesonprojectmanager/mesonprojectparser.cpp
@@ -12,8 +12,8 @@
#include <projectexplorer/projectexplorer.h>
+#include <utils/async.h>
#include <utils/fileinprojectfinder.h>
-#include <utils/runextensions.h>
#include <QStringList>
#include <QTextStream>
@@ -197,7 +197,7 @@ QList<ProjectExplorer::BuildTargetInfo> MesonProjectParser::appsTargets() const
bool MesonProjectParser::startParser()
{
- m_parserFutureResult = Utils::runAsync(
+ m_parserFutureResult = Utils::asyncRun(
ProjectExplorer::ProjectExplorerPlugin::sharedThreadPool(),
[processOutput = m_process.stdOut(), introType = m_introType,
buildDir = m_buildDir.toString(), srcDir = m_srcDir] {
diff --git a/src/plugins/mesonprojectmanager/mesonprojectplugin.cpp b/src/plugins/mesonprojectmanager/mesonprojectplugin.cpp
index f15f65c4af..87beb589bb 100644
--- a/src/plugins/mesonprojectmanager/mesonprojectplugin.cpp
+++ b/src/plugins/mesonprojectmanager/mesonprojectplugin.cpp
@@ -45,7 +45,7 @@ public:
~MesonProjectPluginPrivate() {}
private:
- GeneralSettingsPage m_generalSettingsPage;
+ Settings m_settings;
ToolsSettingsPage m_toolslSettingsPage;
ToolsSettingsAccessor m_toolsSettings;
MesonToolKitAspect m_mesonKitAspect;
@@ -60,7 +60,6 @@ private:
void saveAll()
{
m_toolsSettings.saveMesonTools(MesonTools::tools(), ICore::dialogParent());
- Settings::instance()->writeSettings(ICore::settings());
}
};
@@ -76,7 +75,6 @@ void MesonProjectPlugin::initialize()
ProjectManager::registerProjectType<MesonProject>(Constants::Project::MIMETYPE);
FileIconProvider::registerIconOverlayForFilename(Constants::Icons::MESON, "meson.build");
FileIconProvider::registerIconOverlayForFilename(Constants::Icons::MESON, "meson_options.txt");
- Settings::instance()->readSettings(ICore::settings());
}
} // MesonProjectManager::Internal
diff --git a/src/plugins/mesonprojectmanager/mesonrunconfiguration.cpp b/src/plugins/mesonprojectmanager/mesonrunconfiguration.cpp
index 3731ec4da6..74919a4969 100644
--- a/src/plugins/mesonprojectmanager/mesonrunconfiguration.cpp
+++ b/src/plugins/mesonprojectmanager/mesonrunconfiguration.cpp
@@ -6,67 +6,65 @@
#include "mesonpluginconstants.h"
#include <projectexplorer/buildsystem.h>
-#include <projectexplorer/desktoprunconfiguration.h>
-#include <projectexplorer/environmentaspect.h>
-#include <projectexplorer/localenvironmentaspect.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/runconfigurationaspects.h>
#include <projectexplorer/target.h>
-#include <utils/environment.h>
#include <utils/hostosinfo.h>
using namespace ProjectExplorer;
+using namespace Utils;
-namespace MesonProjectManager {
-namespace Internal {
+namespace MesonProjectManager::Internal {
-MesonRunConfiguration::MesonRunConfiguration(Target *target, Utils::Id id)
- : RunConfiguration{target, id}
+class MesonRunConfiguration final : public RunConfiguration
{
- auto envAspect = addAspect<LocalEnvironmentAspect>(target);
-
- addAspect<ExecutableAspect>(target, ExecutableAspect::RunDevice);
- addAspect<ArgumentsAspect>(macroExpander());
- addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect);
- addAspect<TerminalAspect>();
-
- auto libAspect = addAspect<UseLibraryPathsAspect>();
- connect(libAspect, &UseLibraryPathsAspect::changed,
- envAspect, &EnvironmentAspect::environmentChanged);
-
- if (Utils::HostOsInfo::isMacHost()) {
- auto dyldAspect = addAspect<UseDyldSuffixAspect>();
- connect(dyldAspect, &UseLibraryPathsAspect::changed,
+public:
+ MesonRunConfiguration(Target *target, Id id)
+ : RunConfiguration(target, id)
+ {
+ auto envAspect = addAspect<EnvironmentAspect>();
+ envAspect->setSupportForBuildEnvironment(target);
+
+ addAspect<ExecutableAspect>(target, ExecutableAspect::RunDevice);
+ addAspect<ArgumentsAspect>(macroExpander());
+ addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect);
+ addAspect<TerminalAspect>();
+
+ auto libAspect = addAspect<UseLibraryPathsAspect>();
+ connect(libAspect, &UseLibraryPathsAspect::changed,
envAspect, &EnvironmentAspect::environmentChanged);
- envAspect->addModifier([dyldAspect](Utils::Environment &env) {
- if (dyldAspect->value())
- env.set(QLatin1String("DYLD_IMAGE_SUFFIX"), QLatin1String("_debug"));
- });
- }
- envAspect->addModifier([this, libAspect](Utils::Environment &env) {
- BuildTargetInfo bti = buildTargetInfo();
- if (bti.runEnvModifier)
- bti.runEnvModifier(env, libAspect->value());
- });
+ if (HostOsInfo::isMacHost()) {
+ auto dyldAspect = addAspect<UseDyldSuffixAspect>();
+ connect(dyldAspect, &UseLibraryPathsAspect::changed,
+ envAspect, &EnvironmentAspect::environmentChanged);
+ envAspect->addModifier([dyldAspect](Utils::Environment &env) {
+ if (dyldAspect->value())
+ env.set(QLatin1String("DYLD_IMAGE_SUFFIX"), QLatin1String("_debug"));
+ });
+ }
+
+ envAspect->addModifier([this, libAspect](Environment &env) {
+ BuildTargetInfo bti = buildTargetInfo();
+ if (bti.runEnvModifier)
+ bti.runEnvModifier(env, libAspect->value());
+ });
- setUpdater([this] { updateTargetInformation(); });
+ setUpdater([this] {
+ if (!activeBuildSystem())
+ return;
- connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update);
-}
+ BuildTargetInfo bti = buildTargetInfo();
+ aspect<TerminalAspect>()->setUseTerminalHint(bti.usesTerminal);
+ aspect<ExecutableAspect>()->setExecutable(bti.targetFilePath);
+ aspect<WorkingDirectoryAspect>()->setDefaultWorkingDirectory(bti.workingDirectory);
+ emit aspect<EnvironmentAspect>()->environmentChanged();
+ });
-void MesonRunConfiguration::updateTargetInformation()
-{
- if (!activeBuildSystem())
- return;
-
- BuildTargetInfo bti = buildTargetInfo();
- aspect<TerminalAspect>()->setUseTerminalHint(bti.usesTerminal);
- aspect<ExecutableAspect>()->setExecutable(bti.targetFilePath);
- aspect<WorkingDirectoryAspect>()->setDefaultWorkingDirectory(bti.workingDirectory);
- emit aspect<LocalEnvironmentAspect>()->environmentChanged();
-}
+ connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update);
+ }
+};
MesonRunConfigurationFactory::MesonRunConfigurationFactory()
{
@@ -75,5 +73,4 @@ MesonRunConfigurationFactory::MesonRunConfigurationFactory()
addSupportedTargetDeviceType(ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE);
}
-} // namespace Internal
-} // namespace MesonProjectManager
+} // MesonProjectManager::Internal
diff --git a/src/plugins/mesonprojectmanager/mesonrunconfiguration.h b/src/plugins/mesonprojectmanager/mesonrunconfiguration.h
index 8a23dcbec4..8c1f758097 100644
--- a/src/plugins/mesonprojectmanager/mesonrunconfiguration.h
+++ b/src/plugins/mesonprojectmanager/mesonrunconfiguration.h
@@ -5,17 +5,7 @@
#include <projectexplorer/runconfiguration.h>
-namespace MesonProjectManager {
-namespace Internal {
-
-class MesonRunConfiguration final : public ProjectExplorer::RunConfiguration
-{
-public:
- MesonRunConfiguration(ProjectExplorer::Target *target, Utils::Id id);
-
-private:
- void updateTargetInformation();
-};
+namespace MesonProjectManager::Internal {
class MesonRunConfigurationFactory final : public ProjectExplorer::RunConfigurationFactory
{
@@ -23,5 +13,4 @@ public:
MesonRunConfigurationFactory();
};
-} // namespace Internal
-} // namespace MesonProjectManager
+} // MesonProjectManager::Internal
diff --git a/src/plugins/mesonprojectmanager/mesonwrapper.h b/src/plugins/mesonprojectmanager/mesonwrapper.h
index b229ab6555..6ec1e57a0f 100644
--- a/src/plugins/mesonprojectmanager/mesonwrapper.h
+++ b/src/plugins/mesonprojectmanager/mesonwrapper.h
@@ -9,7 +9,7 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/id.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QFile>
#include <QFileInfo>
@@ -35,7 +35,7 @@ bool containsFiles(const QString &path, const File_t &file, const T &...files)
inline bool run_meson(const Command &command, QIODevice *output = nullptr)
{
- Utils::QtcProcess process;
+ Utils::Process process;
process.setWorkingDirectory(command.workDir());
process.setCommand(command.cmdLine());
process.start();
diff --git a/src/plugins/mesonprojectmanager/ninjabuildstep.cpp b/src/plugins/mesonprojectmanager/ninjabuildstep.cpp
index 325056d491..ac41aeecfa 100644
--- a/src/plugins/mesonprojectmanager/ninjabuildstep.cpp
+++ b/src/plugins/mesonprojectmanager/ninjabuildstep.cpp
@@ -44,7 +44,7 @@ NinjaBuildStep::NinjaBuildStep(BuildStepList *bsl, Id id)
setUseEnglishOutput();
connect(target(), &ProjectExplorer::Target::parsingFinished, this, &NinjaBuildStep::update);
- connect(&Settings::instance()->verboseNinja, &BaseAspect::changed,
+ connect(&settings().verboseNinja, &BaseAspect::changed,
this, &NinjaBuildStep::commandChanged);
}
@@ -119,17 +119,15 @@ QWidget *NinjaBuildStep::createConfigWidget()
// --verbose is only supported since
// https://github.com/ninja-build/ninja/commit/bf7517505ad1def03e13bec2b4131399331bc5c4
// TODO check when to switch back to --verbose
-Utils::CommandLine NinjaBuildStep::command()
+CommandLine NinjaBuildStep::command()
{
- Utils::CommandLine cmd = [this] {
- auto tool = NinjaToolKitAspect::ninjaTool(kit());
- if (tool)
- return Utils::CommandLine{tool->exe()};
- return Utils::CommandLine{};
- }();
+ CommandLine cmd;
+ if (auto tool = NinjaToolKitAspect::ninjaTool(kit()))
+ cmd.setExecutable(tool->exe());
+
if (!m_commandArgs.isEmpty())
- cmd.addArgs(m_commandArgs, Utils::CommandLine::RawType::Raw);
- if (Settings::instance()->verboseNinja.value())
+ cmd.addArgs(m_commandArgs, CommandLine::RawType::Raw);
+ if (settings().verboseNinja())
cmd.addArg("-v");
cmd.addArg(m_targetName);
return cmd;
diff --git a/src/plugins/mesonprojectmanager/settings.cpp b/src/plugins/mesonprojectmanager/settings.cpp
index b26e09f29b..e1fe290bc5 100644
--- a/src/plugins/mesonprojectmanager/settings.cpp
+++ b/src/plugins/mesonprojectmanager/settings.cpp
@@ -8,13 +8,26 @@
#include <utils/layoutbuilder.h>
-namespace MesonProjectManager {
-namespace Internal {
+namespace MesonProjectManager::Internal {
+
+static Settings *s_instance;
+
+Settings &settings()
+{
+ return *s_instance;
+}
Settings::Settings()
{
+ s_instance = this;
+
setSettingsGroup("MesonProjectManager");
- setAutoApply(false);
+
+ setId("A.MesonProjectManager.SettingsPage.General");
+ setDisplayName(Tr::tr("General"));
+ setDisplayCategory("Meson");
+ setCategory(Constants::SettingsPage::CATEGORY);
+ setCategoryIconPath(Constants::Icons::MESON_BW);
autorunMeson.setSettingsKey("meson.autorun");
autorunMeson.setLabelText(Tr::tr("Autorun Meson"));
@@ -24,36 +37,16 @@ Settings::Settings()
verboseNinja.setLabelText(Tr::tr("Ninja verbose mode"));
verboseNinja.setToolTip(Tr::tr("Enables verbose mode by default when invoking Ninja."));
- registerAspect(&autorunMeson);
- registerAspect(&verboseNinja);
-}
-
-Settings *Settings::instance()
-{
- static Settings m_settings;
- return &m_settings;
-}
-
-GeneralSettingsPage::GeneralSettingsPage()
-{
- setId(Constants::SettingsPage::GENERAL_ID);
- setDisplayName(Tr::tr("General"));
- setDisplayCategory("Meson");
- setCategory(Constants::SettingsPage::CATEGORY);
- setCategoryIconPath(Constants::Icons::MESON_BW);
- setSettings(Settings::instance());
-
- setLayouter([](QWidget *widget) {
- Settings &s = *Settings::instance();
- using namespace Utils::Layouting;
-
- Column {
- s.autorunMeson,
- s.verboseNinja,
+ setLayouter([this] {
+ using namespace Layouting;
+ return Column {
+ autorunMeson,
+ verboseNinja,
st,
- }.attachTo(widget);
+ };
});
+
+ readSettings();
}
-} // namespace Internal
-} // namespace MesonProjectManager
+} // MesonProjectManager::Internal
diff --git a/src/plugins/mesonprojectmanager/settings.h b/src/plugins/mesonprojectmanager/settings.h
index 2e23d0f448..34c2e9e97d 100644
--- a/src/plugins/mesonprojectmanager/settings.h
+++ b/src/plugins/mesonprojectmanager/settings.h
@@ -7,25 +7,17 @@
#include <utils/aspects.h>
-namespace MesonProjectManager {
-namespace Internal {
+namespace MesonProjectManager::Internal {
-class Settings : public Utils::AspectContainer
+class Settings : public Core::PagedSettings
{
public:
Settings();
- static Settings *instance();
-
- Utils::BoolAspect autorunMeson;
- Utils::BoolAspect verboseNinja;
+ Utils::BoolAspect autorunMeson{this};
+ Utils::BoolAspect verboseNinja{this};
};
-class GeneralSettingsPage final : public Core::IOptionsPage
-{
-public:
- GeneralSettingsPage();
-};
+Settings &settings();
-} // namespace Internal
-} // namespace MesonProjectManager
+} // MesonProjectManager::Internal
diff --git a/src/plugins/mesonprojectmanager/toolitemsettings.cpp b/src/plugins/mesonprojectmanager/toolitemsettings.cpp
index 148e3e5fae..a0591b905c 100644
--- a/src/plugins/mesonprojectmanager/toolitemsettings.cpp
+++ b/src/plugins/mesonprojectmanager/toolitemsettings.cpp
@@ -29,7 +29,8 @@ ToolItemSettings::ToolItemSettings(QWidget *parent)
Form {
Tr::tr("Name:"), m_mesonNameLineEdit, br,
Tr::tr("Path:"), m_mesonPathChooser, br,
- }.attachTo(this, WithoutMargins);
+ noMargin
+ }.attachTo(this);
connect(m_mesonPathChooser, &PathChooser::rawPathChanged, this, &ToolItemSettings::store);
connect(m_mesonNameLineEdit, &QLineEdit::textChanged, this, &ToolItemSettings::store);
diff --git a/src/plugins/mesonprojectmanager/toolkitaspectwidget.h b/src/plugins/mesonprojectmanager/toolkitaspectwidget.h
index b403afd057..c327fb4e2b 100644
--- a/src/plugins/mesonprojectmanager/toolkitaspectwidget.h
+++ b/src/plugins/mesonprojectmanager/toolkitaspectwidget.h
@@ -36,11 +36,11 @@ private:
void makeReadOnly() override { m_toolsComboBox->setEnabled(false); }
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &parent) override
{
addMutableAction(m_toolsComboBox);
- builder.addItem(m_toolsComboBox);
- builder.addItem(m_manageButton);
+ parent.addItem(m_toolsComboBox);
+ parent.addItem(m_manageButton);
}
void refresh() override
diff --git a/src/plugins/mesonprojectmanager/toolssettingsaccessor.cpp b/src/plugins/mesonprojectmanager/toolssettingsaccessor.cpp
index fff0259741..a67175742b 100644
--- a/src/plugins/mesonprojectmanager/toolssettingsaccessor.cpp
+++ b/src/plugins/mesonprojectmanager/toolssettingsaccessor.cpp
@@ -21,15 +21,13 @@ namespace Internal {
static QString entryName(int index)
{
- using namespace Constants;
- return QString("%1%2").arg(ToolsSettings::ENTRY_KEY).arg(index);
+ return QString("%1%2").arg(Constants::ToolsSettings::ENTRY_KEY).arg(index);
}
ToolsSettingsAccessor::ToolsSettingsAccessor()
- : UpgradingSettingsAccessor("QtCreatorMesonTools",
- Tr::tr("Meson"),
- Core::Constants::IDE_DISPLAY_NAME)
{
+ setDocType("QtCreatorMesonTools");
+ setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME);
setBaseFilePath(Core::ICore::userResourcePath(Constants::ToolsSettings::FILENAME));
}
diff --git a/src/plugins/mesonprojectmanager/toolssettingspage.cpp b/src/plugins/mesonprojectmanager/toolssettingspage.cpp
index 4ac576b365..67ecd99aac 100644
--- a/src/plugins/mesonprojectmanager/toolssettingspage.cpp
+++ b/src/plugins/mesonprojectmanager/toolssettingspage.cpp
@@ -5,10 +5,120 @@
#include "mesonpluginconstants.h"
#include "mesonprojectmanagertr.h"
-#include "toolssettingswidget.h"
+#include "toolitemsettings.h"
+#include "toolsmodel.h"
+#include "tooltreeitem.h"
+
+#include <utils/detailswidget.h>
+#include <utils/layoutbuilder.h>
+
+#include <QHeaderView>
+#include <QPushButton>
+#include <QTreeView>
+
+using namespace Utils;
+
+namespace MesonProjectManager::Internal {
+
+class ToolsSettingsWidget final : public Core::IOptionsPageWidget
+{
+public:
+ ToolsSettingsWidget();
+
+private:
+ void apply() final { m_model.apply(); }
+
+ void cloneMesonTool();
+ void removeMesonTool();
+ void currentMesonToolChanged(const QModelIndex &newCurrent);
+
+ ToolsModel m_model;
+ ToolItemSettings *m_itemSettings;
+ ToolTreeItem *m_currentItem = nullptr;
+
+ QTreeView *m_mesonList;
+ DetailsWidget *m_mesonDetails;
+ QPushButton *m_cloneButton;
+ QPushButton *m_removeButton;
+};
+
+ToolsSettingsWidget::ToolsSettingsWidget()
+{
+ m_mesonList = new QTreeView;
+ m_mesonList->setModel(&m_model);
+ m_mesonList->expandAll();
+ m_mesonList->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
+ m_mesonList->header()->setSectionResizeMode(1, QHeaderView::Stretch);
+
+ m_itemSettings = new ToolItemSettings;
+
+ m_mesonDetails = new DetailsWidget;
+ m_mesonDetails->setState(DetailsWidget::NoSummary);
+ m_mesonDetails->setVisible(false);
+ m_mesonDetails->setWidget(m_itemSettings);
+
+ auto addButton = new QPushButton(Tr::tr("Add"));
+
+ m_cloneButton = new QPushButton(Tr::tr("Clone"));
+ m_cloneButton->setEnabled(false);
+
+ m_removeButton = new QPushButton(Tr::tr("Remove"));
+ m_removeButton->setEnabled(false);
+
+ auto makeDefaultButton = new QPushButton(Tr::tr("Make Default"));
+ makeDefaultButton->setEnabled(false);
+ makeDefaultButton->setVisible(false);
+ makeDefaultButton->setToolTip(Tr::tr("Set as the default Meson executable to use "
+ "when creating a new kit or when no value is set."));
+
+ using namespace Layouting;
+
+ Row {
+ Column {
+ m_mesonList,
+ m_mesonDetails
+ },
+ Column {
+ addButton,
+ m_cloneButton,
+ m_removeButton,
+ makeDefaultButton,
+ st
+ }
+ }.attachTo(this);
+
+ connect(m_mesonList->selectionModel(), &QItemSelectionModel::currentChanged,
+ this, &ToolsSettingsWidget::currentMesonToolChanged);
+ connect(m_itemSettings, &ToolItemSettings::applyChanges, &m_model, &ToolsModel::updateItem);
+
+ connect(addButton, &QPushButton::clicked, &m_model, &ToolsModel::addMesonTool);
+ connect(m_cloneButton, &QPushButton::clicked, this, &ToolsSettingsWidget::cloneMesonTool);
+ connect(m_removeButton, &QPushButton::clicked, this, &ToolsSettingsWidget::removeMesonTool);
+}
+
+void ToolsSettingsWidget::cloneMesonTool()
+{
+ if (m_currentItem) {
+ auto newItem = m_model.cloneMesonTool(m_currentItem);
+ m_mesonList->setCurrentIndex(newItem->index());
+ }
+}
+
+void ToolsSettingsWidget::removeMesonTool()
+{
+ if (m_currentItem)
+ m_model.removeMesonTool(m_currentItem);
+}
+
+void ToolsSettingsWidget::currentMesonToolChanged(const QModelIndex &newCurrent)
+{
+ m_currentItem = m_model.mesoneToolTreeItem(newCurrent);
+ m_itemSettings->load(m_currentItem);
+ m_mesonDetails->setVisible(m_currentItem);
+ m_cloneButton->setEnabled(m_currentItem);
+ m_removeButton->setEnabled(m_currentItem && !m_currentItem->isAutoDetected());
+}
-namespace MesonProjectManager {
-namespace Internal {
ToolsSettingsPage::ToolsSettingsPage()
{
@@ -18,5 +128,4 @@ ToolsSettingsPage::ToolsSettingsPage()
setWidgetCreator([]() { return new ToolsSettingsWidget; });
}
-} // namespace Internal
} // namespace MesonProjectManager
diff --git a/src/plugins/mesonprojectmanager/toolssettingspage.h b/src/plugins/mesonprojectmanager/toolssettingspage.h
index de62b94d91..815120c30f 100644
--- a/src/plugins/mesonprojectmanager/toolssettingspage.h
+++ b/src/plugins/mesonprojectmanager/toolssettingspage.h
@@ -5,8 +5,7 @@
#include <coreplugin/dialogs/ioptionspage.h>
-namespace MesonProjectManager {
-namespace Internal {
+namespace MesonProjectManager::Internal {
class ToolsSettingsPage final : public Core::IOptionsPage
{
@@ -14,5 +13,4 @@ public:
ToolsSettingsPage();
};
-} // namespace Internal
-} // namespace MesonProjectManager
+} // MesonProjectManager::Internal
diff --git a/src/plugins/mesonprojectmanager/toolssettingswidget.cpp b/src/plugins/mesonprojectmanager/toolssettingswidget.cpp
deleted file mode 100644
index ef30d79ae4..0000000000
--- a/src/plugins/mesonprojectmanager/toolssettingswidget.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright (C) 2020 Alexis Jeandet.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "toolssettingswidget.h"
-
-#include "mesonprojectmanagertr.h"
-#include "toolsmodel.h"
-#include "tooltreeitem.h"
-
-#include <utils/detailswidget.h>
-#include <utils/layoutbuilder.h>
-
-#include <QHeaderView>
-#include <QPushButton>
-#include <QTreeView>
-
-using namespace Utils;
-
-namespace MesonProjectManager::Internal {
-
-ToolsSettingsWidget::ToolsSettingsWidget()
- : Core::IOptionsPageWidget()
-{
- m_mesonList = new QTreeView;
- m_mesonList->setModel(&m_model);
- m_mesonList->expandAll();
- m_mesonList->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
- m_mesonList->header()->setSectionResizeMode(1, QHeaderView::Stretch);
-
- m_itemSettings = new ToolItemSettings;
-
- m_mesonDetails = new DetailsWidget;
- m_mesonDetails->setState(DetailsWidget::NoSummary);
- m_mesonDetails->setVisible(false);
- m_mesonDetails->setWidget(m_itemSettings);
-
- auto addButton = new QPushButton(Tr::tr("Add"));
-
- m_cloneButton = new QPushButton(Tr::tr("Clone"));
- m_cloneButton->setEnabled(false);
-
- m_removeButton = new QPushButton(Tr::tr("Remove"));
- m_removeButton->setEnabled(false);
-
- auto makeDefaultButton = new QPushButton(Tr::tr("Make Default"));
- makeDefaultButton->setEnabled(false);
- makeDefaultButton->setVisible(false);
- makeDefaultButton->setToolTip(Tr::tr("Set as the default Meson executable to use "
- "when creating a new kit or when no value is set."));
-
- using namespace Layouting;
-
- Row {
- Column {
- m_mesonList,
- m_mesonDetails
- },
- Column {
- addButton,
- m_cloneButton,
- m_removeButton,
- makeDefaultButton,
- st
- }
- }.attachTo(this);
-
- connect(m_mesonList->selectionModel(), &QItemSelectionModel::currentChanged,
- this, &ToolsSettingsWidget::currentMesonToolChanged);
- connect(m_itemSettings, &ToolItemSettings::applyChanges, &m_model, &ToolsModel::updateItem);
-
- connect(addButton, &QPushButton::clicked, &m_model, &ToolsModel::addMesonTool);
- connect(m_cloneButton, &QPushButton::clicked, this, &ToolsSettingsWidget::cloneMesonTool);
- connect(m_removeButton, &QPushButton::clicked, this, &ToolsSettingsWidget::removeMesonTool);
-}
-
-ToolsSettingsWidget::~ToolsSettingsWidget() = default;
-
-void ToolsSettingsWidget::cloneMesonTool()
-{
- if (m_currentItem) {
- auto newItem = m_model.cloneMesonTool(m_currentItem);
- m_mesonList->setCurrentIndex(newItem->index());
- }
-}
-
-void ToolsSettingsWidget::removeMesonTool()
-{
- if (m_currentItem) {
- m_model.removeMesonTool(m_currentItem);
- }
-}
-
-void ToolsSettingsWidget::currentMesonToolChanged(const QModelIndex &newCurrent)
-{
- m_currentItem = m_model.mesoneToolTreeItem(newCurrent);
- m_itemSettings->load(m_currentItem);
- m_mesonDetails->setVisible(m_currentItem);
- m_cloneButton->setEnabled(m_currentItem);
- m_removeButton->setEnabled(m_currentItem && !m_currentItem->isAutoDetected());
-}
-
-void ToolsSettingsWidget::apply()
-{
- m_model.apply();
-}
-
-} // MesonProjectManager::Internal
diff --git a/src/plugins/mesonprojectmanager/toolssettingswidget.h b/src/plugins/mesonprojectmanager/toolssettingswidget.h
deleted file mode 100644
index 1c39b7460a..0000000000
--- a/src/plugins/mesonprojectmanager/toolssettingswidget.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2020 Alexis Jeandet.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "toolitemsettings.h"
-#include "toolsmodel.h"
-
-#include <coreplugin/dialogs/ioptionspage.h>
-
-namespace Utils { class DetailsWidget; }
-
-namespace MesonProjectManager::Internal {
-
-class ToolTreeItem;
-
-class ToolsSettingsWidget final : public Core::IOptionsPageWidget
-{
-public:
- explicit ToolsSettingsWidget();
- ~ToolsSettingsWidget();
-
-private:
- void apply() final;
-
- void cloneMesonTool();
- void removeMesonTool();
- void currentMesonToolChanged(const QModelIndex &newCurrent);
-
- ToolsModel m_model;
- ToolItemSettings *m_itemSettings;
- ToolTreeItem *m_currentItem = nullptr;
-
- QTreeView *m_mesonList;
- Utils::DetailsWidget *m_mesonDetails;
- QPushButton *m_cloneButton;
- QPushButton *m_removeButton;
-};
-
-} // MesonProjectManager::Internal
diff --git a/src/plugins/mesonprojectmanager/toolwrapper.cpp b/src/plugins/mesonprojectmanager/toolwrapper.cpp
index 9864fd030b..83ed27f495 100644
--- a/src/plugins/mesonprojectmanager/toolwrapper.cpp
+++ b/src/plugins/mesonprojectmanager/toolwrapper.cpp
@@ -3,7 +3,7 @@
#include "toolwrapper.h"
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
namespace MesonProjectManager {
namespace Internal {
@@ -40,7 +40,7 @@ void ToolWrapper::setExe(const Utils::FilePath &newExe)
Version ToolWrapper::read_version(const Utils::FilePath &toolPath)
{
if (toolPath.toFileInfo().isExecutable()) {
- Utils::QtcProcess process;
+ Utils::Process process;
process.setCommand({ toolPath, { "--version" } });
process.start();
if (process.waitForFinished())
diff --git a/src/plugins/modeleditor/componentviewcontroller.cpp b/src/plugins/modeleditor/componentviewcontroller.cpp
index 4ce7cc16ca..4071c5b015 100644
--- a/src/plugins/modeleditor/componentviewcontroller.cpp
+++ b/src/plugins/modeleditor/componentviewcontroller.cpp
@@ -18,9 +18,9 @@
#include <cppeditor/cppmodelmanager.h>
#include <cplusplus/CppDocument.h>
-#include <projectexplorer/session.h>
-#include <projectexplorer/projectnodes.h>
#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
+#include <projectexplorer/projectnodes.h>
#include <utils/qtcassert.h>
@@ -29,6 +29,7 @@
// TODO implement removing include dependencies that are not longer used
// TODO refactor add/remove relations between ancestor packages into extra controller class
+using namespace ProjectExplorer;
using namespace Utils;
namespace ModelEditor {
@@ -136,7 +137,7 @@ void UpdateIncludeDependenciesVisitor::setModelUtilities(ModelUtilities *modelUt
void UpdateIncludeDependenciesVisitor::updateFilePaths()
{
m_filePaths.clear();
- for (const ProjectExplorer::Project *project : ProjectExplorer::SessionManager::projects()) {
+ for (const ProjectExplorer::Project *project : ProjectExplorer::ProjectManager::projects()) {
ProjectExplorer::ProjectNode *projectNode = project->rootProjectNode();
if (projectNode)
collectElementPaths(projectNode, &m_filePaths);
@@ -217,17 +218,16 @@ QStringList UpdateIncludeDependenciesVisitor::findFilePathOfComponent(const qmt:
void UpdateIncludeDependenciesVisitor::collectElementPaths(const ProjectExplorer::FolderNode *folderNode,
QMultiHash<QString, Node> *filePathsMap)
{
- const QList<ProjectExplorer::FileNode *> fileNodes = folderNode->fileNodes();
- for (const ProjectExplorer::FileNode *fileNode : fileNodes) {
+ folderNode->forEachFileNode([&](FileNode *fileNode) {
QString elementName = qmt::NameController::convertFileNameToElementName(fileNode->filePath().toString());
QFileInfo fileInfo = fileNode->filePath().toFileInfo();
QString nodePath = fileInfo.path();
QStringList elementsPath = qmt::NameController::buildElementsPath(nodePath, false);
filePathsMap->insert(elementName, Node(fileNode->filePath().toString(), elementsPath));
- }
- const QList<ProjectExplorer::FolderNode *> subNodes = folderNode->folderNodes();
- for (const ProjectExplorer::FolderNode *subNode : subNodes)
+ });
+ folderNode->forEachFolderNode([&](FolderNode *subNode) {
collectElementPaths(subNode, filePathsMap);
+ });
}
qmt::MComponent *UpdateIncludeDependenciesVisitor::findComponentFromFilePath(const QString &filePath)
diff --git a/src/plugins/modeleditor/elementtasks.cpp b/src/plugins/modeleditor/elementtasks.cpp
index e0efd6914a..6413686f61 100644
--- a/src/plugins/modeleditor/elementtasks.cpp
+++ b/src/plugins/modeleditor/elementtasks.cpp
@@ -24,15 +24,17 @@
#include "qmt/project/project.h"
#include <extensionsystem/pluginmanager.h>
-#include <cppeditor/cpplocatorfilter.h>
+#include <cppeditor/cpplocatordata.h>
#include <cppeditor/indexitem.h>
#include <cppeditor/searchsymbols.h>
#include <coreplugin/editormanager/editormanager.h>
-#include <coreplugin/locator/ilocatorfilter.h>
#include <utils/qtcassert.h>
#include <QMenu>
+using namespace Core;
+using namespace CppEditor;
+
namespace ModelEditor {
namespace Internal {
@@ -83,23 +85,16 @@ void ElementTasks::openElement(const qmt::DElement *element, const qmt::MDiagram
bool ElementTasks::hasClassDefinition(const qmt::MElement *element) const
{
if (auto klass = dynamic_cast<const qmt::MClass *>(element)) {
- QString qualifiedClassName = klass->umlNamespace().isEmpty()
- ? klass->name()
- : klass->umlNamespace() + "::" + klass->name();
-
- Core::ILocatorFilter *classesFilter
- = CppEditor::CppModelManager::instance()->classesFilter();
- if (!classesFilter)
+ const QString qualifiedClassName = klass->umlNamespace().isEmpty() ? klass->name()
+ : klass->umlNamespace() + "::" + klass->name();
+ auto *locatorData = CppModelManager::instance()->locatorData();
+ if (!locatorData)
return false;
-
- QFutureInterface<Core::LocatorFilterEntry> dummyInterface;
- const QList<Core::LocatorFilterEntry> matches
- = classesFilter->matchesFor(dummyInterface, qualifiedClassName);
- for (const Core::LocatorFilterEntry &entry : matches) {
- CppEditor::IndexItem::Ptr info = qvariant_cast<CppEditor::IndexItem::Ptr>(entry.internalData);
- if (info->scopedSymbolName() != qualifiedClassName)
- continue;
- return true;
+ const QList<IndexItem::Ptr> matches = locatorData->findSymbols(IndexItem::Class,
+ qualifiedClassName);
+ for (const IndexItem::Ptr &info : matches) {
+ if (info->scopedSymbolName() == qualifiedClassName)
+ return true;
}
}
return false;
@@ -120,26 +115,19 @@ bool ElementTasks::hasClassDefinition(const qmt::DElement *element,
void ElementTasks::openClassDefinition(const qmt::MElement *element)
{
if (auto klass = dynamic_cast<const qmt::MClass *>(element)) {
- QString qualifiedClassName = klass->umlNamespace().isEmpty()
- ? klass->name()
- : klass->umlNamespace() + "::" + klass->name();
+ const QString qualifiedClassName = klass->umlNamespace().isEmpty() ? klass->name()
+ : klass->umlNamespace() + "::" + klass->name();
- Core::ILocatorFilter *classesFilter
- = CppEditor::CppModelManager::instance()->classesFilter();
- if (!classesFilter)
+ auto *locatorData = CppModelManager::instance()->locatorData();
+ if (!locatorData)
return;
-
- QFutureInterface<Core::LocatorFilterEntry> dummyInterface;
- const QList<Core::LocatorFilterEntry> matches
- = classesFilter->matchesFor(dummyInterface, qualifiedClassName);
- for (const Core::LocatorFilterEntry &entry : matches) {
- CppEditor::IndexItem::Ptr info = qvariant_cast<CppEditor::IndexItem::Ptr>(entry.internalData);
+ const QList<IndexItem::Ptr> matches = locatorData->findSymbols(IndexItem::Class,
+ qualifiedClassName);
+ for (const IndexItem::Ptr &info : matches) {
if (info->scopedSymbolName() != qualifiedClassName)
continue;
- if (Core::EditorManager::instance()->openEditorAt(
- {info->filePath(), info->line(), info->column()})) {
+ if (EditorManager::openEditorAt({info->filePath(), info->line(), info->column()}))
return;
- }
}
}
}
diff --git a/src/plugins/modeleditor/modelindexer.cpp b/src/plugins/modeleditor/modelindexer.cpp
index c6ace66024..d4471fe0ec 100644
--- a/src/plugins/modeleditor/modelindexer.cpp
+++ b/src/plugins/modeleditor/modelindexer.cpp
@@ -18,7 +18,7 @@
#include "qmt/tasks/findrootdiagramvisitor.h"
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <utils/mimeutils.h>
@@ -34,6 +34,8 @@
#include <QDebug>
#include <QPointer>
+using namespace ProjectExplorer;
+
namespace ModelEditor {
namespace Internal {
@@ -308,9 +310,9 @@ ModelIndexer::ModelIndexer(QObject *parent)
connect(this, &ModelIndexer::filesQueued,
d->indexerThread, &ModelIndexer::IndexerThread::onFilesQueued);
d->indexerThread->start();
- connect(ProjectExplorer::SessionManager::instance(), &ProjectExplorer::SessionManager::projectAdded,
+ connect(ProjectExplorer::ProjectManager::instance(), &ProjectExplorer::ProjectManager::projectAdded,
this, &ModelIndexer::onProjectAdded);
- connect(ProjectExplorer::SessionManager::instance(), &ProjectExplorer::SessionManager::aboutToRemoveProject,
+ connect(ProjectExplorer::ProjectManager::instance(), &ProjectExplorer::ProjectManager::aboutToRemoveProject,
this, &ModelIndexer::onAboutToRemoveProject);
}
@@ -447,18 +449,20 @@ QString ModelIndexer::findFirstModel(ProjectExplorer::FolderNode *folderNode,
{
if (!mimeType.isValid())
return QString();
- const QList<ProjectExplorer::FileNode *> fileNodes = folderNode->fileNodes();
- for (const ProjectExplorer::FileNode *fileNode : fileNodes) {
- if (mimeType.suffixes().contains(fileNode->filePath().completeSuffix()))
- return fileNode->filePath().toString();
- }
- const QList<ProjectExplorer::FolderNode *> subFolderNodes = folderNode->folderNodes();
- for (ProjectExplorer::FolderNode *subFolderNode : subFolderNodes) {
- QString modelFileName = findFirstModel(subFolderNode, mimeType);
- if (!modelFileName.isEmpty())
- return modelFileName;
- }
- return QString();
+
+ const QStringList suffixes = mimeType.suffixes();
+ FileNode *foundFileNode = folderNode->findChildFileNode([&](FileNode *fn) {
+ return suffixes.contains(fn->filePath().completeSuffix());
+ });
+ if (foundFileNode)
+ return foundFileNode->filePath().toString();
+
+ QString modelFileName;
+ folderNode->findChildFolderNode([&](FolderNode *fn) {
+ modelFileName = findFirstModel(fn, mimeType);
+ return !modelFileName.isEmpty();
+ });
+ return modelFileName;
}
void ModelIndexer::forgetProject(ProjectExplorer::Project *project)
diff --git a/src/plugins/nim/editor/nimcompletionassistprovider.cpp b/src/plugins/nim/editor/nimcompletionassistprovider.cpp
index 7179a8a64e..f732c45d17 100644
--- a/src/plugins/nim/editor/nimcompletionassistprovider.cpp
+++ b/src/plugins/nim/editor/nimcompletionassistprovider.cpp
@@ -5,7 +5,7 @@
#include "suggest/nimsuggestcache.h"
#include "suggest/nimsuggest.h"
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <texteditor/codeassist/assistinterface.h>
#include <texteditor/codeassist/assistproposalitem.h>
#include <texteditor/codeassist/genericproposal.h>
@@ -142,8 +142,8 @@ private:
{
int line = 0, column = 0;
Utils::Text::convertPosition(interface->textDocument(), pos, &line, &column);
- QTC_ASSERT(column >= 1, return nullptr);
- return suggest->sug(interface->filePath().toString(), line, column - 1, dirtyFile);
+ QTC_ASSERT(column >= 0, return nullptr);
+ return suggest->sug(interface->filePath().toString(), line, column, dirtyFile);
}
static std::unique_ptr<QTemporaryFile> writeDirtyFile(const TextEditor::AssistInterface *interface)
diff --git a/src/plugins/nim/editor/nimtexteditorwidget.cpp b/src/plugins/nim/editor/nimtexteditorwidget.cpp
index 52bcf64dca..b297607dce 100644
--- a/src/plugins/nim/editor/nimtexteditorwidget.cpp
+++ b/src/plugins/nim/editor/nimtexteditorwidget.cpp
@@ -50,7 +50,7 @@ void NimTextEditorWidget::findLinkAt(const QTextCursor &c, const Utils::LinkHand
std::shared_ptr<NimSuggestClientRequest> request = suggest->def(path.toString(),
line,
- column - 1,
+ column,
dirtyFile->fileName());
if (!request)
diff --git a/src/plugins/nim/images/settingscategory_nim.png b/src/plugins/nim/images/settingscategory_nim.png
index 016c570eb4..bee1123cff 100644
--- a/src/plugins/nim/images/settingscategory_nim.png
+++ b/src/plugins/nim/images/settingscategory_nim.png
Binary files differ
diff --git a/src/plugins/nim/images/settingscategory_nim@2x.png b/src/plugins/nim/images/settingscategory_nim@2x.png
index 4fb9c10188..2cbe6cfdc1 100644
--- a/src/plugins/nim/images/settingscategory_nim@2x.png
+++ b/src/plugins/nim/images/settingscategory_nim@2x.png
Binary files differ
diff --git a/src/plugins/nim/nimplugin.cpp b/src/plugins/nim/nimplugin.cpp
index fcca3ef8d3..b3ef6fc49f 100644
--- a/src/plugins/nim/nimplugin.cpp
+++ b/src/plugins/nim/nimplugin.cpp
@@ -62,7 +62,6 @@ public:
NimCompilerBuildStepFactory buildStepFactory;
NimCompilerCleanStepFactory cleanStepFactory;
NimCodeStyleSettingsPage codeStyleSettingsPage;
- NimToolsSettingsPage toolsSettingsPage{&settings};
NimCodeStylePreferencesFactory codeStylePreferencesPage;
NimToolChainFactory toolChainFactory;
diff --git a/src/plugins/nim/project/nimblebuildsystem.cpp b/src/plugins/nim/project/nimblebuildsystem.cpp
index b1317153a8..3a8ccb08d6 100644
--- a/src/plugins/nim/project/nimblebuildsystem.cpp
+++ b/src/plugins/nim/project/nimblebuildsystem.cpp
@@ -10,8 +10,8 @@
#include <projectexplorer/taskhub.h>
#include <utils/algorithm.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
using namespace ProjectExplorer;
using namespace Utils;
@@ -20,7 +20,7 @@ namespace Nim {
const char C_NIMBLEPROJECT_TASKS[] = "Nim.NimbleProject.Tasks";
-static QList<QByteArray> linesFromProcessOutput(QtcProcess *process)
+static QList<QByteArray> linesFromProcessOutput(Process *process)
{
QList<QByteArray> lines = process->readAllRawStandardOutput().split('\n');
lines = Utils::transform(lines, [](const QByteArray &line){ return line.trimmed(); });
@@ -30,7 +30,7 @@ static QList<QByteArray> linesFromProcessOutput(QtcProcess *process)
static std::vector<NimbleTask> parseTasks(const FilePath &nimblePath, const FilePath &workingDirectory)
{
- QtcProcess process;
+ Process process;
process.setCommand({nimblePath, {"tasks"}});
process.setWorkingDirectory(workingDirectory);
process.start();
@@ -58,7 +58,7 @@ static std::vector<NimbleTask> parseTasks(const FilePath &nimblePath, const File
static NimbleMetadata parseMetadata(const FilePath &nimblePath, const FilePath &workingDirectory)
{
- QtcProcess process;
+ Process process;
process.setCommand({nimblePath, {"dump"}});
process.setWorkingDirectory(workingDirectory);
process.start();
diff --git a/src/plugins/nim/project/nimblerunconfiguration.cpp b/src/plugins/nim/project/nimblerunconfiguration.cpp
index f26a375410..8fa24ee33e 100644
--- a/src/plugins/nim/project/nimblerunconfiguration.cpp
+++ b/src/plugins/nim/project/nimblerunconfiguration.cpp
@@ -7,7 +7,6 @@
#include "nimconstants.h"
#include "nimtr.h"
-#include <projectexplorer/localenvironmentaspect.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/runconfigurationaspects.h>
#include <projectexplorer/target.h>
@@ -27,7 +26,9 @@ public:
NimbleRunConfiguration(Target *target, Utils::Id id)
: RunConfiguration(target, id)
{
- auto envAspect = addAspect<LocalEnvironmentAspect>(target);
+ auto envAspect = addAspect<EnvironmentAspect>();
+ envAspect->setSupportForBuildEnvironment(target);
+
addAspect<ExecutableAspect>(target, ExecutableAspect::RunDevice);
addAspect<ArgumentsAspect>(macroExpander());
addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect);
diff --git a/src/plugins/nim/project/nimbletaskstep.cpp b/src/plugins/nim/project/nimbletaskstep.cpp
index 06cf14a107..1885a9daa1 100644
--- a/src/plugins/nim/project/nimbletaskstep.cpp
+++ b/src/plugins/nim/project/nimbletaskstep.cpp
@@ -47,8 +47,8 @@ private:
bool validate();
- StringAspect *m_taskName = nullptr;
- StringAspect *m_taskArgs = nullptr;
+ StringAspect m_taskName{this};
+ StringAspect m_taskArgs{this};
QStandardItemModel m_tasks;
bool m_selecting = false;
@@ -62,19 +62,17 @@ NimbleTaskStep::NimbleTaskStep(BuildStepList *parentList, Id id)
setDisplayName(display);
setCommandLineProvider([this] {
- QString args = m_taskName->value() + " " + m_taskArgs->value();
+ QString args = m_taskName() + " " + m_taskArgs();
return CommandLine(Nim::nimblePathFromKit(target()->kit()), args, CommandLine::Raw);
});
setWorkingDirectoryProvider([this] { return project()->projectDirectory(); });
- m_taskName = addAspect<StringAspect>();
- m_taskName->setSettingsKey(Constants::C_NIMBLETASKSTEP_TASKNAME);
+ m_taskName.setSettingsKey(Constants::C_NIMBLETASKSTEP_TASKNAME);
- m_taskArgs = addAspect<StringAspect>();
- m_taskArgs->setSettingsKey(Constants::C_NIMBLETASKSTEP_TASKARGS);
- m_taskArgs->setDisplayStyle(StringAspect::LineEditDisplay);
- m_taskArgs->setLabelText(Tr::tr("Task arguments:"));
+ m_taskArgs.setSettingsKey(Constants::C_NIMBLETASKSTEP_TASKARGS);
+ m_taskArgs.setDisplayStyle(StringAspect::LineEditDisplay);
+ m_taskArgs.setLabelText(Tr::tr("Task arguments:"));
}
QWidget *NimbleTaskStep::createConfigWidget()
@@ -88,22 +86,22 @@ QWidget *NimbleTaskStep::createConfigWidget()
using namespace Layouting;
auto widget = Form {
m_taskArgs,
- Tr::tr("Tasks:"), taskList
- }.emerge(WithoutMargins);
+ Tr::tr("Tasks:"), taskList,
+ noMargin
+ }.emerge();
auto buildSystem = dynamic_cast<NimbleBuildSystem *>(this->buildSystem());
QTC_ASSERT(buildSystem, return widget);
updateTaskList();
- selectTask(m_taskName->value());
+ selectTask(m_taskName());
connect(&m_tasks, &QAbstractItemModel::dataChanged, this, &NimbleTaskStep::onDataChanged);
connect(buildSystem, &NimbleBuildSystem::tasksChanged, this, &NimbleTaskStep::updateTaskList);
setSummaryUpdater([this] {
- return QString("<b>%1:</b> nimble %2 %3")
- .arg(displayName(), m_taskName->value(), m_taskArgs->value());
+ return QString("<b>%1:</b> nimble %2 %3").arg(displayName(), m_taskName(), m_taskArgs());
});
return widget;
@@ -198,24 +196,24 @@ void NimbleTaskStep::uncheckedAllDifferentFrom(QStandardItem *toSkip)
void NimbleTaskStep::setTaskName(const QString &name)
{
- if (m_taskName->value() == name)
+ if (m_taskName() == name)
return;
- m_taskName->setValue(name);
+ m_taskName.setValue(name);
selectTask(name);
}
bool NimbleTaskStep::validate()
{
- if (m_taskName->value().isEmpty())
+ if (m_taskName().isEmpty())
return true;
auto nimbleBuildSystem = dynamic_cast<NimbleBuildSystem*>(buildSystem());
QTC_ASSERT(nimbleBuildSystem, return false);
- auto matchName = [this](const NimbleTask &task) { return task.name == m_taskName->value(); };
+ auto matchName = [this](const NimbleTask &task) { return task.name == m_taskName(); };
if (!Utils::contains(nimbleBuildSystem->tasks(), matchName)) {
emit addTask(BuildSystemTask(Task::Error, Tr::tr("Nimble task %1 not found.")
- .arg(m_taskName->value())));
+ .arg(m_taskName())));
emitFaultyConfigurationMessage();
return false;
}
diff --git a/src/plugins/nim/project/nimbuildsystem.cpp b/src/plugins/nim/project/nimbuildsystem.cpp
index 97661644dc..00d68034eb 100644
--- a/src/plugins/nim/project/nimbuildsystem.cpp
+++ b/src/plugins/nim/project/nimbuildsystem.cpp
@@ -14,8 +14,6 @@
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
-#include <QStandardPaths>
-
using namespace ProjectExplorer;
using namespace Utils;
@@ -180,10 +178,10 @@ FilePath nimPathFromKit(Kit *kit)
FilePath nimblePathFromKit(Kit *kit)
{
// There's no extra setting for "nimble", derive it from the "nim" path.
- const QString nimbleFromPath = QStandardPaths::findExecutable("nimble");
+ const FilePath nimbleFromPath = FilePath("nimble").searchInPath();
const FilePath nimPath = nimPathFromKit(kit);
const FilePath nimbleFromKit = nimPath.pathAppended("nimble").withExecutableSuffix();
- return nimbleFromKit.exists() ? nimbleFromKit.canonicalPath() : FilePath::fromString(nimbleFromPath);
+ return nimbleFromKit.exists() ? nimbleFromKit.canonicalPath() : nimbleFromPath;
}
bool NimBuildSystem::supportsAction(Node *context, ProjectAction action, const Node *node) const
diff --git a/src/plugins/nim/project/nimcompilerbuildstep.cpp b/src/plugins/nim/project/nimcompilerbuildstep.cpp
index 3ca151def7..588098619a 100644
--- a/src/plugins/nim/project/nimcompilerbuildstep.cpp
+++ b/src/plugins/nim/project/nimcompilerbuildstep.cpp
@@ -16,8 +16,8 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/toolchain.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QComboBox>
#include <QFormLayout>
@@ -76,7 +76,7 @@ QWidget *NimCompilerBuildStep::createConfigWidget()
auto updateUi = [=] {
const CommandLine cmd = commandLine();
- const QStringList parts = ProcessArgs::splitArgs(cmd.toUserOutput());
+ const QStringList parts = ProcessArgs::splitArgs(cmd.toUserOutput(), HostOsInfo::hostOs());
commandTextEdit->setText(parts.join(QChar::LineFeed));
diff --git a/src/plugins/nim/project/nimcompilercleanstep.cpp b/src/plugins/nim/project/nimcompilercleanstep.cpp
index c17f9437a5..8e13c06813 100644
--- a/src/plugins/nim/project/nimcompilercleanstep.cpp
+++ b/src/plugins/nim/project/nimcompilercleanstep.cpp
@@ -110,7 +110,7 @@ bool NimCompilerCleanStep::removeOutFilePath()
NimCompilerCleanStepFactory::NimCompilerCleanStepFactory()
{
registerStep<NimCompilerCleanStep>(Constants::C_NIMCOMPILERCLEANSTEP_ID);
- setFlags(BuildStepInfo::Unclonable);
+ setFlags(BuildStep::Unclonable);
setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_CLEAN);
setSupportedConfiguration(Constants::C_NIMBUILDCONFIGURATION_ID);
setRepeatable(false);
diff --git a/src/plugins/nim/project/nimrunconfiguration.cpp b/src/plugins/nim/project/nimrunconfiguration.cpp
index 5faaa0871c..79eeeb898d 100644
--- a/src/plugins/nim/project/nimrunconfiguration.cpp
+++ b/src/plugins/nim/project/nimrunconfiguration.cpp
@@ -8,7 +8,6 @@
#include "../nimtr.h"
#include <projectexplorer/buildsystem.h>
-#include <projectexplorer/localenvironmentaspect.h>
#include <projectexplorer/runconfigurationaspects.h>
#include <utils/qtcassert.h>
@@ -27,7 +26,9 @@ public:
NimRunConfiguration(Target *target, Utils::Id id)
: RunConfiguration(target, id)
{
- auto envAspect = addAspect<LocalEnvironmentAspect>(target);
+ auto envAspect = addAspect<EnvironmentAspect>();
+ envAspect->setSupportForBuildEnvironment(target);
+
addAspect<ExecutableAspect>(target, ExecutableAspect::RunDevice);
addAspect<ArgumentsAspect>(macroExpander());
addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect);
diff --git a/src/plugins/nim/project/nimtoolchain.cpp b/src/plugins/nim/project/nimtoolchain.cpp
index b373fc9418..21b441fa6c 100644
--- a/src/plugins/nim/project/nimtoolchain.cpp
+++ b/src/plugins/nim/project/nimtoolchain.cpp
@@ -9,7 +9,7 @@
#include <projectexplorer/abi.h>
#include <utils/environment.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QRegularExpression>
@@ -95,7 +95,7 @@ bool NimToolChain::fromMap(const QVariantMap &data)
bool NimToolChain::parseVersion(const FilePath &path, std::tuple<int, int, int> &result)
{
- QtcProcess process;
+ Process process;
process.setCommand({path, {"--version"}});
process.start();
if (!process.waitForFinished())
diff --git a/src/plugins/nim/project/nimtoolchainfactory.cpp b/src/plugins/nim/project/nimtoolchainfactory.cpp
index 4301140865..01ec5373b5 100644
--- a/src/plugins/nim/project/nimtoolchainfactory.cpp
+++ b/src/plugins/nim/project/nimtoolchainfactory.cpp
@@ -35,10 +35,7 @@ Toolchains NimToolChainFactory::autoDetect(const ToolchainDetector &detector) co
{
Toolchains result;
- IDevice::ConstPtr dev =
- detector.device ? detector.device : DeviceManager::defaultDesktopDevice();
-
- const FilePath compilerPath = dev->searchExecutableInPath("nim");
+ const FilePath compilerPath = detector.device->searchExecutableInPath("nim");
if (compilerPath.isEmpty())
return result;
diff --git a/src/plugins/nim/settings/nimcodestylepreferenceswidget.cpp b/src/plugins/nim/settings/nimcodestylepreferenceswidget.cpp
index beefeb56ac..ec9cfa170e 100644
--- a/src/plugins/nim/settings/nimcodestylepreferenceswidget.cpp
+++ b/src/plugins/nim/settings/nimcodestylepreferenceswidget.cpp
@@ -37,14 +37,15 @@ NimCodeStylePreferencesWidget::NimCodeStylePreferencesWidget(ICodeStylePreferenc
m_previewTextEdit = new SnippetEditorWidget;
m_previewTextEdit->setPlainText(Nim::Constants::C_NIMCODESTYLEPREVIEWSNIPPET);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Row {
Column {
tabPreferencesWidget,
st,
},
m_previewTextEdit,
- }.attachTo(this, WithoutMargins);
+ noMargin,
+ }.attachTo(this);
decorateEditor(TextEditorSettings::fontSettings());
connect(TextEditorSettings::instance(), &TextEditorSettings::fontSettingsChanged,
diff --git a/src/plugins/nim/settings/nimcodestylesettingspage.cpp b/src/plugins/nim/settings/nimcodestylesettingspage.cpp
index 79c3634cf2..56919a6c15 100644
--- a/src/plugins/nim/settings/nimcodestylesettingspage.cpp
+++ b/src/plugins/nim/settings/nimcodestylesettingspage.cpp
@@ -36,13 +36,9 @@ public:
auto layout = new QVBoxLayout(this);
layout->addWidget(editor);
- layout->setContentsMargins(0, 0, 0, 0);
}
private:
- void apply() final {}
- void finish() final {}
-
TextEditor::SimpleCodeStylePreferences *m_nimCodeStylePreferences;
};
diff --git a/src/plugins/nim/settings/nimsettings.cpp b/src/plugins/nim/settings/nimsettings.cpp
index 5904b8b6e6..fac77109fd 100644
--- a/src/plugins/nim/settings/nimsettings.cpp
+++ b/src/plugins/nim/settings/nimsettings.cpp
@@ -26,32 +26,24 @@ static SimpleCodeStylePreferences *m_globalCodeStyle = nullptr;
NimSettings::NimSettings()
{
- setAutoApply(false);
setSettingsGroups("Nim", "NimSuggest");
+ setId(Nim::Constants::C_NIMTOOLSSETTINGSPAGE_ID);
+ setDisplayName(Tr::tr("Tools"));
+ setCategory(Nim::Constants::C_NIMTOOLSSETTINGSPAGE_CATEGORY);
+ setDisplayCategory(Tr::tr("Nim"));
+ setCategoryIconPath(":/nim/images/settingscategory_nim.png");
- InitializeCodeStyleSettings();
-
- registerAspect(&nimSuggestPath);
- nimSuggestPath.setSettingsKey("Command");
- nimSuggestPath.setDisplayStyle(StringAspect::PathChooserDisplay);
- nimSuggestPath.setExpectedKind(PathChooser::ExistingCommand);
- nimSuggestPath.setLabelText(Tr::tr("Path:"));
-
- readSettings(Core::ICore::settings());
-}
-
-NimSettings::~NimSettings()
-{
- TerminateCodeStyleSettings();
-}
-
-SimpleCodeStylePreferences *NimSettings::globalCodeStyle()
-{
- return m_globalCodeStyle;
-}
+ setLayouter([this] {
+ using namespace Layouting;
+ return Column {
+ Group {
+ title("Nimsuggest"),
+ Column { nimSuggestPath }
+ },
+ st
+ };
+ });
-void NimSettings::InitializeCodeStyleSettings()
-{
// code style factory
auto factory = new NimCodeStylePreferencesFactory();
TextEditorSettings::registerCodeStyleFactory(factory);
@@ -93,9 +85,15 @@ void NimSettings::InitializeCodeStyleSettings()
Nim::Constants::C_NIMLANGUAGE_ID);
TextEditorSettings::registerMimeTypeForLanguageId(Nim::Constants::C_NIM_SCRIPT_MIMETYPE,
Nim::Constants::C_NIMLANGUAGE_ID);
+
+ nimSuggestPath.setSettingsKey("Command");
+ nimSuggestPath.setExpectedKind(PathChooser::ExistingCommand);
+ nimSuggestPath.setLabelText(Tr::tr("Path:"));
+
+ readSettings();
}
-void NimSettings::TerminateCodeStyleSettings()
+NimSettings::~NimSettings()
{
TextEditorSettings::unregisterCodeStyle(Nim::Constants::C_NIMLANGUAGE_ID);
TextEditorSettings::unregisterCodeStylePool(Nim::Constants::C_NIMLANGUAGE_ID);
@@ -105,28 +103,9 @@ void NimSettings::TerminateCodeStyleSettings()
m_globalCodeStyle = nullptr;
}
-
-// NimToolSettingsPage
-
-NimToolsSettingsPage::NimToolsSettingsPage(NimSettings *settings)
+SimpleCodeStylePreferences *NimSettings::globalCodeStyle()
{
- setId(Nim::Constants::C_NIMTOOLSSETTINGSPAGE_ID);
- setDisplayName(Tr::tr("Tools"));
- setCategory(Nim::Constants::C_NIMTOOLSSETTINGSPAGE_CATEGORY);
- setDisplayCategory(Tr::tr("Nim"));
- setCategoryIconPath(":/nim/images/settingscategory_nim.png");
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- using namespace Layouting;
- Column {
- Group {
- title("Nimsuggest"),
- Column { settings->nimSuggestPath }
- },
- st
- }.attachTo(widget);
- });
+ return m_globalCodeStyle;
}
} // namespace Nim
diff --git a/src/plugins/nim/settings/nimsettings.h b/src/plugins/nim/settings/nimsettings.h
index d144618f7d..a7c6628b65 100644
--- a/src/plugins/nim/settings/nimsettings.h
+++ b/src/plugins/nim/settings/nimsettings.h
@@ -4,31 +4,20 @@
#pragma once
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
namespace TextEditor { class SimpleCodeStylePreferences; }
namespace Nim {
-class NimSettings : public Utils::AspectContainer
+class NimSettings : public Core::PagedSettings
{
public:
NimSettings();
~NimSettings();
- Utils::StringAspect nimSuggestPath;
+ Utils::FilePathAspect nimSuggestPath{this};
static TextEditor::SimpleCodeStylePreferences *globalCodeStyle();
-
-private:
- void InitializeCodeStyleSettings();
- void TerminateCodeStyleSettings();
-};
-
-class NimToolsSettingsPage final : public Core::IOptionsPage
-{
-public:
- explicit NimToolsSettingsPage(NimSettings *settings);
};
} // Nim
diff --git a/src/plugins/nim/suggest/server.cpp b/src/plugins/nim/suggest/server.cpp
index b891404fe8..7573505f72 100644
--- a/src/plugins/nim/suggest/server.cpp
+++ b/src/plugins/nim/suggest/server.cpp
@@ -10,8 +10,8 @@ namespace Suggest {
NimSuggestServer::NimSuggestServer(QObject *parent) : QObject(parent)
{
- connect(&m_process, &QtcProcess::done, this, &NimSuggestServer::onDone);
- connect(&m_process, &QtcProcess::readyReadStandardOutput, this,
+ connect(&m_process, &Process::done, this, &NimSuggestServer::onDone);
+ connect(&m_process, &Process::readyReadStandardOutput, this,
&NimSuggestServer::onStandardOutputAvailable);
}
diff --git a/src/plugins/nim/suggest/server.h b/src/plugins/nim/suggest/server.h
index 9d8e880663..9eb2bac141 100644
--- a/src/plugins/nim/suggest/server.h
+++ b/src/plugins/nim/suggest/server.h
@@ -7,7 +7,7 @@
#include <QFile>
#include <QObject>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
namespace Nim {
namespace Suggest {
@@ -36,7 +36,7 @@ private:
void clearState();
bool m_portAvailable = false;
- Utils::QtcProcess m_process;
+ Utils::Process m_process;
quint16 m_port = 0;
QString m_projectFilePath;
QString m_executablePath;
diff --git a/src/plugins/perforce/changenumberdialog.cpp b/src/plugins/perforce/changenumberdialog.cpp
index b5a0aa2787..5ba8dcced0 100644
--- a/src/plugins/perforce/changenumberdialog.cpp
+++ b/src/plugins/perforce/changenumberdialog.cpp
@@ -27,7 +27,7 @@ ChangeNumberDialog::ChangeNumberDialog(QWidget *parent)
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row { Tr::tr("Change number:"), m_lineEdit },
diff --git a/src/plugins/perforce/pendingchangesdialog.cpp b/src/plugins/perforce/pendingchangesdialog.cpp
index 589d17b276..84b10a8a07 100644
--- a/src/plugins/perforce/pendingchangesdialog.cpp
+++ b/src/plugins/perforce/pendingchangesdialog.cpp
@@ -48,7 +48,7 @@ PendingChangesDialog::PendingChangesDialog(const QString &data, QWidget *parent)
submitButton->setEnabled(false);
}
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_listWidget,
diff --git a/src/plugins/perforce/perforcechecker.cpp b/src/plugins/perforce/perforcechecker.cpp
index fc2aeae3e1..cf63d9e691 100644
--- a/src/plugins/perforce/perforcechecker.cpp
+++ b/src/plugins/perforce/perforcechecker.cpp
@@ -21,13 +21,15 @@ namespace Internal {
PerforceChecker::PerforceChecker(QObject *parent) : QObject(parent)
{
- connect(&m_process, &QtcProcess::done, this, &PerforceChecker::slotDone);
+ connect(&m_process, &Process::done, this, &PerforceChecker::slotDone);
}
PerforceChecker::~PerforceChecker()
{
- m_process.kill();
- m_process.waitForFinished();
+ if (m_process.isRunning()) {
+ m_process.kill();
+ m_process.waitForFinished();
+ }
resetOverrideCursor();
}
diff --git a/src/plugins/perforce/perforcechecker.h b/src/plugins/perforce/perforcechecker.h
index e9325b4ced..d0f608de70 100644
--- a/src/plugins/perforce/perforcechecker.h
+++ b/src/plugins/perforce/perforcechecker.h
@@ -4,7 +4,7 @@
#pragma once
#include <utils/filepath.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
namespace Perforce::Internal {
@@ -44,7 +44,7 @@ private:
void parseOutput(const QString &);
inline void resetOverrideCursor();
- Utils::QtcProcess m_process;
+ Utils::Process m_process;
Utils::FilePath m_binary;
int m_timeOutMS = -1;
bool m_timedOut = false;
diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp
index ccd4faec16..ae61d12d34 100644
--- a/src/plugins/perforce/perforceplugin.cpp
+++ b/src/plugins/perforce/perforceplugin.cpp
@@ -27,8 +27,8 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/parameteraction.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/temporarydirectory.h>
#include <vcsbase/basevcseditorfactory.h>
@@ -226,6 +226,10 @@ public:
void discardCommit() override { cleanCommitMessageFile(); }
QString commitDisplayName() const final;
+ QString commitAbortTitle() const final;
+ QString commitAbortMessage() const final;
+ QString commitErrorMessage(const QString &error) const final;
+
void p4Diff(const PerforceDiffParameters &p);
void openCurrentFile();
@@ -889,8 +893,8 @@ void PerforcePluginPrivate::filelog(const FilePath &workingDir, const QString &f
QTextCodec *codec = VcsBaseEditor::getCodec(workingDir, QStringList(fileName));
QStringList args;
args << QLatin1String("filelog") << QLatin1String("-li");
- if (m_settings.logCount.value() > 0)
- args << "-m" << QString::number(m_settings.logCount.value());
+ if (m_settings.logCount() > 0)
+ args << "-m" << QString::number(m_settings.logCount());
if (!fileName.isEmpty())
args.append(fileName);
const PerforceResponse result = runP4Cmd(workingDir, args,
@@ -911,8 +915,8 @@ void PerforcePluginPrivate::changelists(const FilePath &workingDir, const QStrin
QTextCodec *codec = VcsBaseEditor::getCodec(workingDir, QStringList(fileName));
QStringList args;
args << QLatin1String("changelists") << QLatin1String("-lit");
- if (m_settings.logCount.value() > 0)
- args << "-m" << QString::number(m_settings.logCount.value());
+ if (m_settings.logCount() > 0)
+ args << "-m" << QString::number(m_settings.logCount());
if (!fileName.isEmpty())
args.append(fileName);
const PerforceResponse result = runP4Cmd(workingDir, args,
@@ -1225,8 +1229,8 @@ PerforceResponse PerforcePluginPrivate::synchronousProcess(const FilePath &worki
QTC_ASSERT(stdInput.isEmpty(), return PerforceResponse()); // Not supported here
// Run, connect stderr to the output window
- QtcProcess process;
- const int timeOutS = (flags & LongTimeOut) ? m_settings.longTimeOutS() : m_settings.timeOutS.value();
+ Process process;
+ const int timeOutS = (flags & LongTimeOut) ? m_settings.longTimeOutS() : m_settings.timeOutS();
process.setTimeoutS(timeOutS);
if (outputCodec)
process.setCodec(outputCodec);
@@ -1283,7 +1287,7 @@ PerforceResponse PerforcePluginPrivate::fullySynchronousProcess(const FilePath &
const QByteArray &stdInput,
QTextCodec *outputCodec) const
{
- QtcProcess process;
+ Process process;
if (flags & OverrideDiffEnvironment)
process.setEnvironment(overrideDiffEnvironmentVariable());
@@ -1303,7 +1307,7 @@ PerforceResponse PerforcePluginPrivate::fullySynchronousProcess(const FilePath &
QByteArray stdOut;
QByteArray stdErr;
- const int timeOutS = (flags & LongTimeOut) ? m_settings.longTimeOutS() : m_settings.timeOutS.value();
+ const int timeOutS = (flags & LongTimeOut) ? m_settings.longTimeOutS() : m_settings.timeOutS();
if (!process.readDataFromProcess(&stdOut, &stdErr, timeOutS)) {
process.stop();
process.waitForFinished();
@@ -1443,9 +1447,27 @@ void PerforceDiffConfig::triggerReRun()
QString PerforcePluginPrivate::commitDisplayName() const
{
+ //: Name of the "commit" action of the VCS
return Tr::tr("Submit");
}
+QString PerforcePluginPrivate::commitAbortTitle() const
+{
+ return Tr::tr("Close Submit Editor");
+}
+
+QString PerforcePluginPrivate::commitAbortMessage() const
+{
+ return Tr::tr("Closing this editor will abort the submit.");
+}
+
+QString PerforcePluginPrivate::commitErrorMessage(const QString &error) const
+{
+ if (error.isEmpty())
+ return Tr::tr("Cannot submit.");
+ return Tr::tr("Cannot submit: %1.").arg(error);
+}
+
void PerforcePluginPrivate::p4Diff(const FilePath &workingDir, const QStringList &files)
{
PerforceDiffParameters p;
diff --git a/src/plugins/perforce/perforcesettings.cpp b/src/plugins/perforce/perforcesettings.cpp
index ac9d0b26b0..47ef5a03b3 100644
--- a/src/plugins/perforce/perforcesettings.cpp
+++ b/src/plugins/perforce/perforcesettings.cpp
@@ -8,6 +8,7 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
+#include <utils/infolabel.h>
#include <utils/layoutbuilder.h>
#include <utils/qtcassert.h>
@@ -34,7 +35,6 @@ PerforceSettings::PerforceSettings()
setSettingsGroup("Perforce");
setAutoApply(false);
- registerAspect(&p4BinaryPath);
p4BinaryPath.setDisplayStyle(StringAspect::PathChooserDisplay);
p4BinaryPath.setSettingsKey("Command");
p4BinaryPath.setDefaultValue(
@@ -44,28 +44,23 @@ PerforceSettings::PerforceSettings()
p4BinaryPath.setDisplayName(Tr::tr("Perforce Command"));
p4BinaryPath.setLabelText(Tr::tr("P4 command:"));
- registerAspect(&p4Port);
p4Port.setDisplayStyle(StringAspect::LineEditDisplay);
p4Port.setSettingsKey("Port");
p4Port.setLabelText(Tr::tr("P4 port:"));
- registerAspect(&p4Client);
p4Client.setDisplayStyle(StringAspect::LineEditDisplay);
p4Client.setSettingsKey("Client");
p4Client.setLabelText(Tr::tr("P4 client:"));
- registerAspect(&p4User);
p4User.setDisplayStyle(StringAspect::LineEditDisplay);
p4User.setSettingsKey("User");
p4User.setLabelText(Tr::tr("P4 user:"));
- registerAspect(&logCount);
logCount.setSettingsKey("LogCount");
logCount.setRange(1000, 10000);
logCount.setDefaultValue(1000);
logCount.setLabelText(Tr::tr("Log count:"));
- registerAspect(&customEnv);
// The settings value has been stored with the opposite meaning for a while.
// Avoid changing the stored value, but flip it on read/write:
customEnv.setSettingsKey("Default");
@@ -73,14 +68,12 @@ PerforceSettings::PerforceSettings()
customEnv.setFromSettingsTransformation(invertBoolVariant);
customEnv.setToSettingsTransformation(invertBoolVariant);
- registerAspect(&timeOutS);
timeOutS.setSettingsKey("TimeOut");
timeOutS.setRange(1, 360);
timeOutS.setDefaultValue(30);
timeOutS.setLabelText(Tr::tr("Timeout:"));
timeOutS.setSuffix(Tr::tr("s"));
- registerAspect(&autoOpen);
autoOpen.setSettingsKey("PromptToOpen");
autoOpen.setDefaultValue(true);
autoOpen.setLabelText(Tr::tr("Automatically open files when editing"));
@@ -106,6 +99,23 @@ QStringList PerforceSettings::commonP4Arguments() const
return lst;
}
+QStringList PerforceSettings::commonP4Arguments_volatile() const
+{
+ QStringList lst;
+ if (customEnv.volatileValue().toBool()) {
+ auto p4C = p4Client.volatileValue().toString();
+ if (!p4C.isEmpty())
+ lst << "-c" << p4C;
+ auto p4P = p4Port.volatileValue().toString();
+ if (!p4P.isEmpty())
+ lst << "-p" << p4P;
+ auto p4U = p4User.volatileValue().toString();
+ if (!p4U.isEmpty())
+ lst << "-u" << p4U;
+ }
+ return lst;
+}
+
bool PerforceSettings::isValid() const
{
return !m_topLevel.isEmpty() && !p4BinaryPath.value().isEmpty();
@@ -206,35 +216,41 @@ PerforceSettingsPage::PerforceSettingsPage(PerforceSettings *settings)
setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
setSettings(settings);
- setLayouter([settings, this](QWidget *widget) {
+ setLayouter([settings] {
PerforceSettings &s = *settings;
using namespace Layouting;
- auto errorLabel = new QLabel;
+ auto errorLabel = new InfoLabel({}, InfoLabel::None);
+ errorLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
+ errorLabel->setFilled(true);
auto testButton = new QPushButton(Tr::tr("Test"));
- connect(testButton, &QPushButton::clicked, this, [settings, errorLabel, testButton] {
+ QObject::connect(testButton, &QPushButton::clicked, errorLabel,
+ [settings, errorLabel, testButton] {
testButton->setEnabled(false);
auto checker = new PerforceChecker(errorLabel);
checker->setUseOverideCursor(true);
- connect(checker, &PerforceChecker::failed, errorLabel,
+ QObject::connect(checker, &PerforceChecker::failed, errorLabel,
[errorLabel, testButton, checker](const QString &t) {
- errorLabel->setStyleSheet("background-color: red");
+ errorLabel->setType(InfoLabel::Error);
errorLabel->setText(t);
testButton->setEnabled(true);
checker->deleteLater();
});
- connect(checker, &PerforceChecker::succeeded, errorLabel,
+ QObject::connect(checker, &PerforceChecker::succeeded, errorLabel,
[errorLabel, testButton, checker](const FilePath &repo) {
- errorLabel->setStyleSheet({});
+ errorLabel->setType(InfoLabel::Ok);
errorLabel->setText(Tr::tr("Test succeeded (%1).")
.arg(repo.toUserOutput()));
testButton->setEnabled(true);
checker->deleteLater();
});
- errorLabel->setStyleSheet(QString());
+ errorLabel->setType(InfoLabel::Information);
errorLabel->setText(Tr::tr("Testing..."));
- checker->start(settings->p4BinaryPath.filePath(), {}, settings->commonP4Arguments(), 10000);
+
+ const FilePath p4Bin = FilePath::fromUserInput(
+ settings->p4BinaryPath.volatileValue().toString());
+ checker->start(p4Bin, {}, settings->commonP4Arguments_volatile(), 10000);
});
Group config {
@@ -243,7 +259,8 @@ PerforceSettingsPage::PerforceSettingsPage(PerforceSettings *settings)
};
Group environment {
- title(Tr::tr("Environment Variables"), &s.customEnv),
+ title(Tr::tr("Environment Variables")),
+ s.customEnv.groupChecker(),
Row { s.p4Port, s.p4Client, s.p4User }
};
@@ -255,13 +272,13 @@ PerforceSettingsPage::PerforceSettingsPage(PerforceSettings *settings)
}
};
- Column {
+ return Column {
config,
environment,
misc,
Row { errorLabel, st, testButton },
st
- }.attachTo(widget);
+ };
});
}
diff --git a/src/plugins/perforce/perforcesettings.h b/src/plugins/perforce/perforcesettings.h
index f6c52957e6..0801c31090 100644
--- a/src/plugins/perforce/perforcesettings.h
+++ b/src/plugins/perforce/perforcesettings.h
@@ -41,8 +41,8 @@ public:
QString *repositoryRoot /* = 0 */,
QString *errorMessage);
- int longTimeOutS() const { return timeOutS.value() * 10; }
- int timeOutMS() const { return timeOutS.value() * 1000; }
+ int longTimeOutS() const { return timeOutS() * 10; }
+ int timeOutMS() const { return timeOutS() * 1000; }
Utils::FilePath topLevel() const;
Utils::FilePath topLevelSymLinkTarget() const;
@@ -64,17 +64,18 @@ public:
// Return basic arguments, including -d and server connection parameters.
QStringList commonP4Arguments() const;
QStringList commonP4Arguments(const QString &workingDir) const;
+ QStringList commonP4Arguments_volatile() const; // remove when auto apply is done
void clearTopLevel();
- Utils::StringAspect p4BinaryPath;
- Utils::StringAspect p4Port;
- Utils::StringAspect p4Client;
- Utils::StringAspect p4User;
- Utils::IntegerAspect logCount;
- Utils::BoolAspect customEnv;
- Utils::IntegerAspect timeOutS;
- Utils::BoolAspect autoOpen;
+ Utils::StringAspect p4BinaryPath{this};
+ Utils::StringAspect p4Port{this};
+ Utils::StringAspect p4Client{this};
+ Utils::StringAspect p4User{this};
+ Utils::IntegerAspect logCount{this};
+ Utils::BoolAspect customEnv{this};
+ Utils::IntegerAspect timeOutS{this};
+ Utils::BoolAspect autoOpen{this};
private:
QStringList workingDirectoryArguments(const QString &workingDir) const;
diff --git a/src/plugins/perforce/perforcesubmiteditorwidget.cpp b/src/plugins/perforce/perforcesubmiteditorwidget.cpp
index ddf4f1feb1..6887472822 100644
--- a/src/plugins/perforce/perforcesubmiteditorwidget.cpp
+++ b/src/plugins/perforce/perforcesubmiteditorwidget.cpp
@@ -22,7 +22,6 @@ public:
, m_clientName(createLabel())
, m_userName(createLabel())
{
- resize(402, 134);
setFlat(true);
setTitle(Tr::tr("Submit"));
diff --git a/src/plugins/perfprofiler/perfconfigeventsmodel.cpp b/src/plugins/perfprofiler/perfconfigeventsmodel.cpp
index a760ffb7bc..a235a6122d 100644
--- a/src/plugins/perfprofiler/perfconfigeventsmodel.cpp
+++ b/src/plugins/perfprofiler/perfconfigeventsmodel.cpp
@@ -8,13 +8,16 @@
#include <QMetaEnum>
+using namespace Utils;
+
namespace PerfProfiler {
namespace Internal {
PerfConfigEventsModel::PerfConfigEventsModel(PerfSettings *settings, QObject *parent) :
QAbstractTableModel(parent), m_settings(settings)
{
- connect(m_settings, &PerfSettings::changed, this, &PerfConfigEventsModel::reset);
+ connect(m_settings, &AspectContainer::changed, this, &PerfConfigEventsModel::reset);
+ connect(m_settings, &AspectContainer::fromMapFinished, this, &PerfConfigEventsModel::reset);
}
int PerfConfigEventsModel::rowCount(const QModelIndex &parent) const
diff --git a/src/plugins/perfprofiler/perfconfigwidget.cpp b/src/plugins/perfprofiler/perfconfigwidget.cpp
index 68c86ff720..e5e3fd3725 100644
--- a/src/plugins/perfprofiler/perfconfigwidget.cpp
+++ b/src/plugins/perfprofiler/perfconfigwidget.cpp
@@ -14,8 +14,8 @@
#include <utils/aspects.h>
#include <utils/layoutbuilder.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QComboBox>
#include <QHeaderView>
@@ -120,9 +120,9 @@ void PerfConfigWidget::setTarget(ProjectExplorer::Target *target)
QTC_ASSERT(device, return);
QTC_CHECK(!m_process || m_process->state() == QProcess::NotRunning);
- m_process.reset(new QtcProcess);
+ m_process.reset(new Process);
m_process->setCommand({device->filePath("perf"), {"probe", "-l"}});
- connect(m_process.get(), &QtcProcess::done,
+ connect(m_process.get(), &Process::done,
this, &PerfConfigWidget::handleProcessDone);
useTracePointsButton->setEnabled(true);
diff --git a/src/plugins/perfprofiler/perfconfigwidget.h b/src/plugins/perfprofiler/perfconfigwidget.h
index 3fc65c4753..5372647064 100644
--- a/src/plugins/perfprofiler/perfconfigwidget.h
+++ b/src/plugins/perfprofiler/perfconfigwidget.h
@@ -14,7 +14,7 @@ class QPushButton;
class QTableView;
QT_END_NAMESPACE
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace PerfProfiler {
namespace Internal {
@@ -37,7 +37,7 @@ private:
void handleProcessDone();
PerfSettings *m_settings;
- std::unique_ptr<Utils::QtcProcess> m_process;
+ std::unique_ptr<Utils::Process> m_process;
QTableView *eventsView;
QPushButton *useTracePointsButton;
diff --git a/src/plugins/perfprofiler/perfdatareader.cpp b/src/plugins/perfprofiler/perfdatareader.cpp
index 603d2ad016..a545dff913 100644
--- a/src/plugins/perfprofiler/perfdatareader.cpp
+++ b/src/plugins/perfprofiler/perfdatareader.cpp
@@ -18,7 +18,7 @@
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
#include <projectexplorer/runcontrol.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/toolchain.h>
diff --git a/src/plugins/perfprofiler/perfloaddialog.cpp b/src/plugins/perfprofiler/perfloaddialog.cpp
index 8bf34316fe..8f162ed596 100644
--- a/src/plugins/perfprofiler/perfloaddialog.cpp
+++ b/src/plugins/perfprofiler/perfloaddialog.cpp
@@ -9,7 +9,7 @@
#include <projectexplorer/kit.h>
#include <projectexplorer/kitchooser.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <utils/layoutbuilder.h>
@@ -109,7 +109,7 @@ void PerfLoadDialog::on_browseExecutableDirButton_pressed()
void PerfLoadDialog::chooseDefaults()
{
- ProjectExplorer::Target *target = ProjectExplorer::SessionManager::startupTarget();
+ ProjectExplorer::Target *target = ProjectExplorer::ProjectManager::startupTarget();
if (!target)
return;
diff --git a/src/plugins/perfprofiler/perfprofiler.qbs b/src/plugins/perfprofiler/perfprofiler.qbs
index f3151ae0a6..7a037e7fa4 100644
--- a/src/plugins/perfprofiler/perfprofiler.qbs
+++ b/src/plugins/perfprofiler/perfprofiler.qbs
@@ -75,9 +75,7 @@ QtcPlugin {
files: [ "PerfProfilerFlameGraphView.qml" ]
}
- Group {
- name: "Unit tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
prefix: "tests/"
files: [
"perfprofilertracefile_test.cpp",
diff --git a/src/plugins/perfprofiler/perfprofilerflamegraphmodel.h b/src/plugins/perfprofiler/perfprofilerflamegraphmodel.h
index d213bba099..4347728536 100644
--- a/src/plugins/perfprofiler/perfprofilerflamegraphmodel.h
+++ b/src/plugins/perfprofiler/perfprofilerflamegraphmodel.h
@@ -19,10 +19,9 @@ class PerfProfilerFlameGraphModel : public QAbstractItemModel
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("use the context property")
- Q_DISABLE_COPY(PerfProfilerFlameGraphModel);
+ Q_DISABLE_COPY_MOVE(PerfProfilerFlameGraphModel);
+
public:
- PerfProfilerFlameGraphModel(PerfProfilerFlameGraphModel &&) = delete;
- PerfProfilerFlameGraphModel &operator=(PerfProfilerFlameGraphModel &&) = delete;
enum Role {
TypeIdRole = Qt::UserRole + 1, // Sort by data, not by displayed string
diff --git a/src/plugins/perfprofiler/perfprofilerruncontrol.cpp b/src/plugins/perfprofiler/perfprofilerruncontrol.cpp
index d14bfb87cd..3d63316013 100644
--- a/src/plugins/perfprofiler/perfprofilerruncontrol.cpp
+++ b/src/plugins/perfprofiler/perfprofilerruncontrol.cpp
@@ -17,7 +17,7 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/target.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QAction>
#include <QMessageBox>
@@ -95,20 +95,19 @@ public:
: RunWorker(runControl)
{
setId("LocalPerfRecordWorker");
-
- auto perfAspect = runControl->aspect<PerfRunConfigurationAspect>();
- QTC_ASSERT(perfAspect, return);
- PerfSettings *settings = static_cast<PerfSettings *>(perfAspect->currentSettings);
- QTC_ASSERT(settings, return);
- m_perfRecordArguments = settings->perfRecordArguments();
}
void start() override
{
- m_process = new QtcProcess(this);
+ auto perfAspect = runControl()->aspect<PerfRunConfigurationAspect>();
+ QTC_ASSERT(perfAspect, reportFailure(); return);
+ PerfSettings *settings = static_cast<PerfSettings *>(perfAspect->currentSettings);
+ QTC_ASSERT(settings, reportFailure(); return);
+
+ m_process = new Process(this);
- connect(m_process, &QtcProcess::started, this, &RunWorker::reportStarted);
- connect(m_process, &QtcProcess::done, this, [this] {
+ connect(m_process, &Process::started, this, &RunWorker::reportStarted);
+ connect(m_process, &Process::done, this, [this] {
// The terminate() below will frequently lead to QProcess::Crashed. We're not interested
// in that. FailedToStart is the only actual failure.
if (m_process->error() == QProcess::FailedToStart) {
@@ -125,7 +124,7 @@ public:
});
CommandLine cmd({device()->filePath("perf"), {"record"}});
- cmd.addArgs(m_perfRecordArguments);
+ settings->addPerfRecordArguments(&cmd);
cmd.addArgs({"-o", "-", "--"});
cmd.addCommandLineAsArgs(runControl()->commandLine(), CommandLine::Raw);
@@ -141,11 +140,10 @@ public:
m_process->terminate();
}
- QtcProcess *recorder() { return m_process; }
+ Process *recorder() { return m_process; }
private:
- QPointer<QtcProcess> m_process;
- QStringList m_perfRecordArguments;
+ QPointer<Process> m_process;
};
@@ -193,12 +191,12 @@ void PerfProfilerRunner::start()
PerfDataReader *reader = m_perfParserWorker->reader();
if (auto prw = qobject_cast<LocalPerfRecordWorker *>(m_perfRecordWorker)) {
// That's the local case.
- QtcProcess *recorder = prw->recorder();
- connect(recorder, &QtcProcess::readyReadStandardError, this, [this, recorder] {
+ Process *recorder = prw->recorder();
+ connect(recorder, &Process::readyReadStandardError, this, [this, recorder] {
appendMessage(QString::fromLocal8Bit(recorder->readAllRawStandardError()),
Utils::StdErrFormat);
});
- connect(recorder, &QtcProcess::readyReadStandardOutput, this, [this, reader, recorder] {
+ connect(recorder, &Process::readyReadStandardOutput, this, [this, reader, recorder] {
if (!reader->feedParser(recorder->readAllRawStandardOutput()))
reportFailure(Tr::tr("Failed to transfer Perf data to perfparser."));
});
diff --git a/src/plugins/perfprofiler/perfprofilertool.cpp b/src/plugins/perfprofiler/perfprofilertool.cpp
index d4515f79b8..60bbcc3ae3 100644
--- a/src/plugins/perfprofiler/perfprofilertool.cpp
+++ b/src/plugins/perfprofiler/perfprofilertool.cpp
@@ -27,13 +27,14 @@
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/runcontrol.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qtsupport/qtkitinformation.h>
#include <utils/algorithm.h>
#include <utils/fancymainwindow.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QFileDialog>
@@ -120,6 +121,7 @@ PerfProfilerTool::PerfProfilerTool()
options->addAction(command);
m_tracePointsButton = new QToolButton;
+ StyleHelper::setPanelWidget(m_tracePointsButton);
m_tracePointsButton->setDefaultAction(tracePointsAction);
m_objectsToDelete << m_tracePointsButton;
@@ -146,14 +148,18 @@ PerfProfilerTool::PerfProfilerTool()
this, &PerfProfilerTool::updateRunActions);
m_recordButton = new QToolButton;
+ StyleHelper::setPanelWidget(m_recordButton);
m_clearButton = new QToolButton;
+ StyleHelper::setPanelWidget(m_clearButton);
m_filterButton = new QToolButton;
+ StyleHelper::setPanelWidget(m_filterButton);
m_filterMenu = new QMenu(m_filterButton);
m_aggregateButton = new QToolButton;
+ StyleHelper::setPanelWidget(m_aggregateButton);
m_recordedLabel = new QLabel;
- m_recordedLabel->setProperty("panelwidget", true);
+ StyleHelper::setPanelWidget(m_recordedLabel);
m_delayLabel = new QLabel;
- m_delayLabel->setProperty("panelwidget", true);
+ StyleHelper::setPanelWidget(m_delayLabel);
m_objectsToDelete << m_recordButton << m_clearButton << m_filterButton << m_aggregateButton
<< m_recordedLabel << m_delayLabel;
@@ -224,7 +230,7 @@ void PerfProfilerTool::createViews()
connect(recordMenu, &QMenu::aboutToShow, recordMenu, [recordMenu] {
recordMenu->hide();
PerfSettings *settings = nullptr;
- Target *target = SessionManager::startupTarget();
+ Target *target = ProjectManager::startupTarget();
if (target) {
if (auto runConfig = target->activeRunConfiguration())
settings = runConfig->currentSettings<PerfSettings>(Constants::PerfSettingsId);
@@ -250,7 +256,7 @@ void PerfProfilerTool::createViews()
m_filterButton->setIcon(Utils::Icons::FILTER.icon());
m_filterButton->setPopupMode(QToolButton::InstantPopup);
- m_filterButton->setProperty("noArrow", true);
+ m_filterButton->setProperty(StyleHelper::C_NO_ARROW, true);
m_filterButton->setMenu(m_filterMenu);
m_aggregateButton->setIcon(Utils::Icons::EXPAND_ALL_TOOLBAR.icon());
@@ -572,7 +578,7 @@ static Utils::FilePaths sourceFiles(const Project *currentProject = nullptr)
if (currentProject)
sourceFiles.append(currentProject->files(Project::SourceFiles));
- const QList<Project *> projects = SessionManager::projects();
+ const QList<Project *> projects = ProjectManager::projects();
for (const Project *project : projects) {
if (project != currentProject)
sourceFiles.append(project->files(Project::SourceFiles));
@@ -609,7 +615,7 @@ void PerfProfilerTool::showLoadTraceDialog()
startLoading();
- const Project *currentProject = SessionManager::startupProject();
+ const Project *currentProject = ProjectManager::startupProject();
const Target *target = currentProject ? currentProject->activeTarget() : nullptr;
const Kit *kit = target ? target->kit() : nullptr;
populateFileFinder(currentProject, kit);
diff --git a/src/plugins/perfprofiler/perfsettings.cpp b/src/plugins/perfprofiler/perfsettings.cpp
index 8175dece0e..59a753e5dd 100644
--- a/src/plugins/perfprofiler/perfsettings.cpp
+++ b/src/plugins/perfprofiler/perfsettings.cpp
@@ -10,7 +10,7 @@
#include <QSettings>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
using namespace Utils;
@@ -25,19 +25,16 @@ PerfSettings::PerfSettings(ProjectExplorer::Target *target)
return widget;
});
- registerAspect(&period);
period.setSettingsKey("Analyzer.Perf.Frequency");
period.setRange(250, 2147483647);
period.setDefaultValue(250);
period.setLabelText(Tr::tr("Sample period:"));
- registerAspect(&stackSize);
stackSize.setSettingsKey("Analyzer.Perf.StackSize");
stackSize.setRange(4096, 65536);
stackSize.setDefaultValue(4096);
stackSize.setLabelText(Tr::tr("Stack snapshot size (kB):"));
- registerAspect(&sampleMode);
sampleMode.setSettingsKey("Analyzer.Perf.SampleMode");
sampleMode.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
sampleMode.setLabelText(Tr::tr("Sample mode:"));
@@ -45,7 +42,6 @@ PerfSettings::PerfSettings(ProjectExplorer::Target *target)
sampleMode.addOption({Tr::tr("event count"), {}, QString("-c")});
sampleMode.setDefaultValue(0);
- registerAspect(&callgraphMode);
callgraphMode.setSettingsKey("Analyzer.Perf.CallgraphMode");
callgraphMode.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
callgraphMode.setLabelText(Tr::tr("Call graph mode:"));
@@ -54,11 +50,9 @@ PerfSettings::PerfSettings(ProjectExplorer::Target *target)
callgraphMode.addOption({Tr::tr("last branch record"), {}, QString("lbr")});
callgraphMode.setDefaultValue(0);
- registerAspect(&events);
events.setSettingsKey("Analyzer.Perf.Events");
events.setDefaultValue({"cpu-cycles"});
- registerAspect(&extraArguments);
extraArguments.setSettingsKey("Analyzer.Perf.ExtraArguments");
extraArguments.setDisplayStyle(StringAspect::DisplayStyle::LineEditDisplay);
extraArguments.setLabelText(Tr::tr("Additional arguments:"));
@@ -68,8 +62,6 @@ PerfSettings::PerfSettings(ProjectExplorer::Target *target)
stackSize.setEnabled(index == 0);
});
- connect(this, &AspectContainer::fromMapFinished, this, &PerfSettings::changed);
-
readGlobalSettings();
}
@@ -103,7 +95,7 @@ void PerfSettings::writeGlobalSettings() const
settings->endGroup();
}
-QStringList PerfSettings::perfRecordArguments() const
+void PerfSettings::addPerfRecordArguments(CommandLine *cmd) const
{
QString callgraphArg = callgraphMode.itemValue().toString();
if (callgraphArg == Constants::PerfCallgraphDwarf)
@@ -118,11 +110,11 @@ QStringList PerfSettings::perfRecordArguments() const
}
}
- return QStringList({"-e", events,
- "--call-graph", callgraphArg,
- sampleMode.itemValue().toString(),
- QString::number(period.value())})
- + ProcessArgs::splitArgs(extraArguments.value());
+ cmd->addArgs({"-e", events,
+ "--call-graph", callgraphArg,
+ sampleMode.itemValue().toString(),
+ QString::number(period.value())});
+ cmd->addArgs(extraArguments(), CommandLine::Raw);
}
void PerfSettings::resetToDefault()
diff --git a/src/plugins/perfprofiler/perfsettings.h b/src/plugins/perfprofiler/perfsettings.h
index fc808c1e11..7c10706098 100644
--- a/src/plugins/perfprofiler/perfsettings.h
+++ b/src/plugins/perfprofiler/perfsettings.h
@@ -14,7 +14,6 @@ namespace PerfProfiler {
class PERFPROFILER_EXPORT PerfSettings final : public ProjectExplorer::ISettingsAspect
{
Q_OBJECT
- Q_PROPERTY(QStringList perfRecordArguments READ perfRecordArguments NOTIFY changed)
public:
explicit PerfSettings(ProjectExplorer::Target *target = nullptr);
@@ -23,19 +22,16 @@ public:
void readGlobalSettings();
void writeGlobalSettings() const;
- QStringList perfRecordArguments() const;
+ void addPerfRecordArguments(Utils::CommandLine *cmd) const;
void resetToDefault();
- Utils::IntegerAspect period;
- Utils::IntegerAspect stackSize;
- Utils::SelectionAspect sampleMode;
- Utils::SelectionAspect callgraphMode;
- Utils::StringListAspect events;
- Utils::StringAspect extraArguments;
-
-signals:
- void changed();
+ Utils::IntegerAspect period{this};
+ Utils::IntegerAspect stackSize{this};
+ Utils::SelectionAspect sampleMode{this};
+ Utils::SelectionAspect callgraphMode{this};
+ Utils::StringListAspect events{this};
+ Utils::StringAspect extraArguments{this};
};
} // namespace PerfProfiler
diff --git a/src/plugins/perfprofiler/perftracepointdialog.cpp b/src/plugins/perfprofiler/perftracepointdialog.cpp
index da3b4c38c3..3920b526b8 100644
--- a/src/plugins/perfprofiler/perftracepointdialog.cpp
+++ b/src/plugins/perfprofiler/perftracepointdialog.cpp
@@ -8,12 +8,12 @@
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
-#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/layoutbuilder.h>
+#include <utils/process.h>
+#include <utils/qtcassert.h>
#include <QComboBox>
#include <QDialogButtonBox>
@@ -41,7 +41,7 @@ PerfTracePointDialog::PerfTracePointDialog()
m_privilegesChooser->addItems({ELEVATE_METHOD_NA, ELEVATE_METHOD_PKEXEC, ELEVATE_METHOD_SUDO});
m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_label,
m_textEdit,
@@ -51,7 +51,7 @@ PerfTracePointDialog::PerfTracePointDialog()
m_buttonBox,
}.attachTo(this);
- if (const Target *target = SessionManager::startupTarget()) {
+ if (const Target *target = ProjectManager::startupTarget()) {
const Kit *kit = target->kit();
QTC_ASSERT(kit, return);
@@ -93,7 +93,7 @@ void PerfTracePointDialog::runScript()
m_privilegesChooser->setEnabled(false);
m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
- m_process.reset(new QtcProcess(this));
+ m_process.reset(new Process(this));
m_process->setWriteData(m_textEdit->toPlainText().toUtf8());
m_textEdit->clear();
@@ -103,7 +103,7 @@ void PerfTracePointDialog::runScript()
else
m_process->setCommand({m_device->filePath("sh"), {}});
- connect(m_process.get(), &QtcProcess::done, this, &PerfTracePointDialog::handleProcessDone);
+ connect(m_process.get(), &Process::done, this, &PerfTracePointDialog::handleProcessDone);
m_process->start();
}
diff --git a/src/plugins/perfprofiler/perftracepointdialog.h b/src/plugins/perfprofiler/perftracepointdialog.h
index 427220e64e..d5affdcc7c 100644
--- a/src/plugins/perfprofiler/perftracepointdialog.h
+++ b/src/plugins/perfprofiler/perftracepointdialog.h
@@ -15,7 +15,7 @@ class QLabel;
class QTextEdit;
QT_END_NAMESPACE
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace PerfProfiler {
namespace Internal {
@@ -40,7 +40,7 @@ private:
QComboBox *m_privilegesChooser;
QDialogButtonBox *m_buttonBox;
ProjectExplorer::IDeviceConstPtr m_device;
- std::unique_ptr<Utils::QtcProcess> m_process;
+ std::unique_ptr<Utils::Process> m_process;
void accept() final;
void reject() final;
diff --git a/src/plugins/plugins.qbs b/src/plugins/plugins.qbs
index b878e76c3e..6deee97295 100644
--- a/src/plugins/plugins.qbs
+++ b/src/plugins/plugins.qbs
@@ -7,6 +7,7 @@ Project {
"android/android.qbs",
"autotest/autotest.qbs",
"autotoolsprojectmanager/autotoolsprojectmanager.qbs",
+ "axivion/axivion.qbs",
"baremetal/baremetal.qbs",
"bazaar/bazaar.qbs",
"beautifier/beautifier.qbs",
@@ -23,6 +24,7 @@ Project {
"coco/coco.qbs",
"compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs",
"conan/conan.qbs",
+ "copilot/copilot.qbs",
"coreplugin/coreplugin.qbs",
"coreplugin/images/logo/logo.qbs",
"cpaster/cpaster.qbs",
@@ -43,6 +45,7 @@ Project {
"git/git.qbs",
"gitlab/gitlab.qbs",
"glsleditor/glsleditor.qbs",
+ "haskell/haskell.qbs",
"helloworld/helloworld.qbs",
"help/help.qbs",
"imageviewer/imageviewer.qbs",
@@ -68,6 +71,7 @@ Project {
"qmlprojectmanager/qmlprojectmanager.qbs",
"qnx/qnx.qbs",
"qmakeprojectmanager/qmakeprojectmanager.qbs",
+ "qmldesignerbase/qmldesignerbase.qbs",
"qtsupport/qtsupport.qbs",
"remotelinux/remotelinux.qbs",
"resourceeditor/resourceeditor.qbs",
@@ -78,12 +82,14 @@ Project {
"squish/squish.qbs",
"studiowelcome/studiowelcome.qbs",
"subversion/subversion.qbs",
+ "terminal/terminal.qbs",
"texteditor/texteditor.qbs",
"todo/todo.qbs",
"updateinfo/updateinfo.qbs",
"valgrind/valgrind.qbs",
+ "vcpkg/vcpkg.qbs",
"vcsbase/vcsbase.qbs",
"webassembly/webassembly.qbs",
- "welcome/welcome.qbs"
+ "welcome/welcome.qbs",
].concat(project.additionalPlugins)
}
diff --git a/src/plugins/projectexplorer/CMakeLists.txt b/src/plugins/projectexplorer/CMakeLists.txt
index d05509636e..d16e868a5c 100644
--- a/src/plugins/projectexplorer/CMakeLists.txt
+++ b/src/plugins/projectexplorer/CMakeLists.txt
@@ -28,6 +28,7 @@ add_qtc_plugin(ProjectExplorer
codestylesettingspropertiespage.cpp codestylesettingspropertiespage.h
compileoutputwindow.cpp compileoutputwindow.h
configtaskhandler.cpp configtaskhandler.h
+ copystep.cpp copystep.h
copytaskhandler.cpp copytaskhandler.h
currentprojectfilter.cpp currentprojectfilter.h
currentprojectfind.cpp currentprojectfind.h
@@ -65,8 +66,7 @@ add_qtc_plugin(ProjectExplorer
devicesupport/idevicefactory.cpp devicesupport/idevicefactory.h
devicesupport/idevicefwd.h
devicesupport/idevicewidget.h
- devicesupport/localprocesslist.cpp devicesupport/localprocesslist.h
- devicesupport/sshdeviceprocesslist.cpp devicesupport/sshdeviceprocesslist.h
+ devicesupport/processlist.cpp devicesupport/processlist.h
devicesupport/sshparameters.cpp devicesupport/sshparameters.h
devicesupport/sshsettings.cpp devicesupport/sshsettings.h
devicesupport/sshsettingspage.cpp devicesupport/sshsettingspage.h
@@ -115,7 +115,6 @@ add_qtc_plugin(ProjectExplorer
ldparser.cpp ldparser.h
lldparser.cpp lldparser.h
linuxiccparser.cpp linuxiccparser.h
- localenvironmentaspect.cpp localenvironmentaspect.h
makestep.cpp makestep.h
miniprojecttargetselector.cpp miniprojecttargetselector.h
msvcparser.cpp msvcparser.h
@@ -135,13 +134,12 @@ add_qtc_plugin(ProjectExplorer
projectexplorerconstants.cpp
projectexplorerconstants.h
projectexplorericons.cpp projectexplorericons.h
- projectexplorersettings.h
- projectexplorersettingspage.cpp projectexplorersettingspage.h
+ projectexplorersettings.cpp projectexplorersettings.h
projectexplorertr.h
projectfilewizardextension.cpp projectfilewizardextension.h
projectimporter.cpp projectimporter.h
projectmacro.cpp projectmacro.h
- projectmanager.h
+ projectmanager.cpp projectmanager.h
projectmodels.cpp projectmodels.h
projectnodes.cpp projectnodes.h
projectpanelfactory.cpp projectpanelfactory.h
@@ -159,10 +157,6 @@ add_qtc_plugin(ProjectExplorer
runsettingspropertiespage.cpp runsettingspropertiespage.h
sanitizerparser.cpp sanitizerparser.h
selectablefilesmodel.cpp selectablefilesmodel.h
- session.cpp session.h
- sessiondialog.cpp sessiondialog.h
- sessionmodel.cpp sessionmodel.h
- sessionview.cpp sessionview.h
showineditortaskhandler.cpp showineditortaskhandler.h
showoutputtaskhandler.cpp showoutputtaskhandler.h
simpleprojectwizard.cpp simpleprojectwizard.h
diff --git a/src/plugins/projectexplorer/ProjectExplorer.json.in b/src/plugins/projectexplorer/ProjectExplorer.json.in
index d96dcc26c6..c9b54ad441 100644
--- a/src/plugins/projectexplorer/ProjectExplorer.json.in
+++ b/src/plugins/projectexplorer/ProjectExplorer.json.in
@@ -24,14 +24,6 @@
\"Name\" : \"-ensure-kit-for-binary\",
\"Parameter\" : \"file path\",
\"Description\" : \"Create kit with architecture matching a given application or library\"
- },
- {
- \"Name\" : \"-lastsession\",
- \"Description\" : \"Restore the last session\"
- },
- {
- \"Name\" : \"<session>\",
- \"Description\" : \"Restore a saved session\"
}
],
$$dependencyList,
diff --git a/src/plugins/projectexplorer/abstractprocessstep.cpp b/src/plugins/projectexplorer/abstractprocessstep.cpp
index c838458aa5..806d9159f9 100644
--- a/src/plugins/projectexplorer/abstractprocessstep.cpp
+++ b/src/plugins/projectexplorer/abstractprocessstep.cpp
@@ -12,14 +12,15 @@
#include <utils/fileutils.h>
#include <utils/outputformatter.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QTextDecoder>
#include <algorithm>
#include <memory>
+using namespace Tasking;
using namespace Utils;
namespace ProjectExplorer {
@@ -76,7 +77,7 @@ public:
void cleanUp(int exitCode, QProcess::ExitStatus status);
AbstractProcessStep *q;
- std::unique_ptr<QtcProcess> m_process;
+ std::unique_ptr<Process> m_process;
std::unique_ptr<TaskTree> m_taskTree;
ProcessParameters m_param;
ProcessParameters *m_displayedParams = &m_param;
@@ -182,9 +183,9 @@ void AbstractProcessStep::doRun()
setupStreams();
- d->m_process.reset(new QtcProcess);
+ d->m_process.reset(new Process);
setupProcess(d->m_process.get());
- connect(d->m_process.get(), &QtcProcess::done, this, &AbstractProcessStep::handleProcessDone);
+ connect(d->m_process.get(), &Process::done, this, &AbstractProcessStep::handleProcessDone);
d->m_process->start();
}
@@ -209,7 +210,7 @@ void AbstractProcessStep::setupStreams()
d->stderrStream = std::make_unique<QTextDecoder>(QTextCodec::codecForLocale());
}
-void AbstractProcessStep::setupProcess(QtcProcess *process)
+void AbstractProcessStep::setupProcess(Process *process)
{
process->setUseCtrlCStub(HostOsInfo::isWindowsHost());
process->setWorkingDirectory(d->m_param.effectiveWorkingDirectory());
@@ -224,15 +225,15 @@ void AbstractProcessStep::setupProcess(QtcProcess *process)
if (d->m_lowPriority && ProjectExplorerPlugin::projectExplorerSettings().lowBuildPriority)
process->setLowPriority();
- connect(process, &QtcProcess::readyReadStandardOutput, this, [this, process] {
+ connect(process, &Process::readyReadStandardOutput, this, [this, process] {
emit addOutput(d->stdoutStream->toUnicode(process->readAllRawStandardOutput()),
OutputFormat::Stdout, DontAppendNewline);
});
- connect(process, &QtcProcess::readyReadStandardError, this, [this, process] {
+ connect(process, &Process::readyReadStandardError, this, [this, process] {
emit addOutput(d->stderrStream->toUnicode(process->readAllRawStandardError()),
OutputFormat::Stderr, DontAppendNewline);
});
- connect(process, &QtcProcess::started, this, [this] {
+ connect(process, &Process::started, this, [this] {
ProcessParameters *params = displayedParameters();
emit addOutput(Tr::tr("Starting: \"%1\" %2")
.arg(params->effectiveCommand().toUserOutput(), params->prettyArguments()),
@@ -240,7 +241,7 @@ void AbstractProcessStep::setupProcess(QtcProcess *process)
});
}
-void AbstractProcessStep::runTaskTree(const Tasking::Group &recipe)
+void AbstractProcessStep::runTaskTree(const Group &recipe)
{
setupStreams();
@@ -306,7 +307,7 @@ bool AbstractProcessStep::setupProcessParameters(ProcessParameters *params) cons
const bool looksGood = executable.isEmpty() || executable.ensureReachable(workingDirectory);
QTC_ASSERT(looksGood, return false);
- params->setWorkingDirectory(workingDirectory.onDevice(executable));
+ params->setWorkingDirectory(executable.withNewPath(workingDirectory.path()));
return true;
}
diff --git a/src/plugins/projectexplorer/abstractprocessstep.h b/src/plugins/projectexplorer/abstractprocessstep.h
index 8112541447..fc1ed13135 100644
--- a/src/plugins/projectexplorer/abstractprocessstep.h
+++ b/src/plugins/projectexplorer/abstractprocessstep.h
@@ -10,10 +10,11 @@
namespace Utils {
class CommandLine;
enum class ProcessResult;
-class QtcProcess;
-namespace Tasking { class Group; }
+class Process;
}
+namespace Tasking { class Group; }
+
namespace ProjectExplorer {
class ProcessParameters;
@@ -51,8 +52,8 @@ protected:
virtual void finish(Utils::ProcessResult result);
bool checkWorkingDirectory();
- void setupProcess(Utils::QtcProcess *process);
- void runTaskTree(const Utils::Tasking::Group &recipe);
+ void setupProcess(Utils::Process *process);
+ void runTaskTree(const Tasking::Group &recipe);
ProcessParameters *displayedParameters() const;
private:
diff --git a/src/plugins/projectexplorer/allprojectsfilter.cpp b/src/plugins/projectexplorer/allprojectsfilter.cpp
index ea4d86dba7..42e110d350 100644
--- a/src/plugins/projectexplorer/allprojectsfilter.cpp
+++ b/src/plugins/projectexplorer/allprojectsfilter.cpp
@@ -3,14 +3,17 @@
#include "allprojectsfilter.h"
+#include "project.h"
#include "projectexplorer.h"
#include "projectexplorertr.h"
-#include "session.h"
-#include "project.h"
+#include "projectmanager.h"
#include <utils/algorithm.h>
+#include <QFuture>
+
using namespace Core;
+using namespace Utils;
namespace ProjectExplorer::Internal {
@@ -18,38 +21,29 @@ AllProjectsFilter::AllProjectsFilter()
{
setId("Files in any project");
setDisplayName(Tr::tr("Files in Any Project"));
- setDescription(Tr::tr("Matches all files of all open projects. Append \"+<number>\" or "
- "\":<number>\" to jump to the given line number. Append another "
- "\"+<number>\" or \":<number>\" to jump to the column number as well."));
+ setDescription(Tr::tr("Locates files of all open projects. Append \"+<number>\" or "
+ "\":<number>\" to jump to the given line number. Append another "
+ "\"+<number>\" or \":<number>\" to jump to the column number as well."));
setDefaultShortcutString("a");
setDefaultIncludedByDefault(true);
+ setRefreshRecipe(Tasking::Sync([this] { m_cache.invalidate(); }));
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::fileListChanged,
- this, &AllProjectsFilter::markFilesAsOutOfDate);
-}
-
-void AllProjectsFilter::markFilesAsOutOfDate()
-{
- setFileIterator(nullptr);
-}
-
-void AllProjectsFilter::prepareSearch(const QString &entry)
-{
- Q_UNUSED(entry)
- if (!fileIterator()) {
- Utils::FilePaths paths;
- for (Project *project : SessionManager::projects())
- paths.append(project->files(Project::SourceFiles));
- Utils::sort(paths);
- setFileIterator(new BaseFileFilter::ListIterator(paths));
- }
- BaseFileFilter::prepareSearch(entry);
-}
-
-void AllProjectsFilter::refresh(QFutureInterface<void> &future)
-{
- Q_UNUSED(future)
- QMetaObject::invokeMethod(this, &AllProjectsFilter::markFilesAsOutOfDate, Qt::QueuedConnection);
+ this, [this] { m_cache.invalidate(); });
+ m_cache.setGeneratorProvider([] {
+ // This body runs in main thread
+ FilePaths filePaths;
+ for (Project *project : ProjectManager::projects())
+ filePaths.append(project->files(Project::SourceFiles));
+ return [filePaths](const QFuture<void> &future) {
+ // This body runs in non-main thread
+ FilePaths sortedPaths = filePaths;
+ if (future.isCanceled())
+ return FilePaths();
+ Utils::sort(sortedPaths);
+ return sortedPaths;
+ };
+ });
}
-} // ProjectExplorer::Internal
+} // namespace ProjectExplorer::Internal
diff --git a/src/plugins/projectexplorer/allprojectsfilter.h b/src/plugins/projectexplorer/allprojectsfilter.h
index ae9fdffe92..6782ef4611 100644
--- a/src/plugins/projectexplorer/allprojectsfilter.h
+++ b/src/plugins/projectexplorer/allprojectsfilter.h
@@ -3,25 +3,18 @@
#pragma once
-#include <coreplugin/locator/basefilefilter.h>
+#include <coreplugin/locator/ilocatorfilter.h>
-#include <QFutureInterface>
+namespace ProjectExplorer::Internal {
-namespace ProjectExplorer {
-namespace Internal {
-
-class AllProjectsFilter : public Core::BaseFileFilter
+class AllProjectsFilter : public Core::ILocatorFilter
{
- Q_OBJECT
-
public:
AllProjectsFilter();
- void refresh(QFutureInterface<void> &future) override;
- void prepareSearch(const QString &entry) override;
private:
- void markFilesAsOutOfDate();
+ Core::LocatorMatcherTasks matchers() final { return {m_cache.matcher()}; }
+ Core::LocatorFileCache m_cache;
};
-} // namespace Internal
-} // namespace ProjectExplorer
+} // namespace ProjectExplorer::Internal
diff --git a/src/plugins/projectexplorer/allprojectsfind.cpp b/src/plugins/projectexplorer/allprojectsfind.cpp
index ac4afc1d6e..d61b156052 100644
--- a/src/plugins/projectexplorer/allprojectsfind.cpp
+++ b/src/plugins/projectexplorer/allprojectsfind.cpp
@@ -7,7 +7,7 @@
#include "project.h"
#include "projectexplorer.h"
#include "projectexplorertr.h"
-#include "session.h"
+#include "projectmanager.h"
#include <coreplugin/editormanager/editormanager.h>
@@ -44,7 +44,7 @@ QString AllProjectsFind::displayName() const
bool AllProjectsFind::isEnabled() const
{
- return BaseFileFind::isEnabled() && SessionManager::hasProjects();
+ return BaseFileFind::isEnabled() && ProjectManager::hasProjects();
}
FileIterator *AllProjectsFind::files(const QStringList &nameFilters,
@@ -52,7 +52,7 @@ FileIterator *AllProjectsFind::files(const QStringList &nameFilters,
const QVariant &additionalParameters) const
{
Q_UNUSED(additionalParameters)
- return filesForProjects(nameFilters, exclusionFilters, SessionManager::projects());
+ return filesForProjects(nameFilters, exclusionFilters, ProjectManager::projects());
}
FileIterator *AllProjectsFind::filesForProjects(const QStringList &nameFilters,
diff --git a/src/plugins/projectexplorer/appoutputpane.cpp b/src/plugins/projectexplorer/appoutputpane.cpp
index 6f7e22bacd..3c76c2e62a 100644
--- a/src/plugins/projectexplorer/appoutputpane.cpp
+++ b/src/plugins/projectexplorer/appoutputpane.cpp
@@ -8,7 +8,6 @@
#include "projectexplorericons.h"
#include "projectexplorertr.h"
#include "runcontrol.h"
-#include "session.h"
#include "showoutputtaskhandler.h"
#include "windebuginterface.h"
@@ -17,6 +16,7 @@
#include <coreplugin/coreconstants.h>
#include <coreplugin/icore.h>
#include <coreplugin/outputwindow.h>
+#include <coreplugin/session.h>
#include <texteditor/behaviorsettings.h>
#include <texteditor/fontsettings.h>
#include <texteditor/texteditorsettings.h>
@@ -46,6 +46,7 @@
static Q_LOGGING_CATEGORY(appOutputLog, "qtc.projectexplorer.appoutput", QtWarningMsg);
+using namespace Core;
using namespace Utils;
namespace ProjectExplorer {
diff --git a/src/plugins/projectexplorer/buildaspects.cpp b/src/plugins/projectexplorer/buildaspects.cpp
index adf37a3362..606911654f 100644
--- a/src/plugins/projectexplorer/buildaspects.cpp
+++ b/src/plugins/projectexplorer/buildaspects.cpp
@@ -41,7 +41,6 @@ BuildDirectoryAspect::BuildDirectoryAspect(const BuildConfiguration *bc)
{
setSettingsKey("ProjectExplorer.BuildConfiguration.BuildDirectory");
setLabelText(Tr::tr("Build directory:"));
- setDisplayStyle(PathChooserDisplay);
setExpectedKind(Utils::PathChooser::Directory);
setValidationFunction([this](FancyLineEdit *edit, QString *error) {
const FilePath fixedDir = fixupDir(FilePath::fromUserInput(edit->text()));
@@ -107,12 +106,12 @@ void BuildDirectoryAspect::fromMap(const QVariantMap &map)
}
}
-void BuildDirectoryAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void BuildDirectoryAspect::addToLayout(Layouting::LayoutItem &parent)
{
- StringAspect::addToLayout(builder);
+ StringAspect::addToLayout(parent);
d->problemLabel = new InfoLabel({}, InfoLabel::Warning);
d->problemLabel->setElideMode(Qt::ElideNone);
- builder.addRow({{}, d->problemLabel.data()});
+ parent.addItems({{}, d->problemLabel.data()});
updateProblemLabel();
if (!d->sourceDir.isEmpty()) {
connect(this, &StringAspect::checkedChanged, this, [this] {
diff --git a/src/plugins/projectexplorer/buildaspects.h b/src/plugins/projectexplorer/buildaspects.h
index 9788cc69ca..bcc8987cd0 100644
--- a/src/plugins/projectexplorer/buildaspects.h
+++ b/src/plugins/projectexplorer/buildaspects.h
@@ -7,12 +7,11 @@
#include <utils/aspects.h>
-namespace Utils { class FilePath; }
-
namespace ProjectExplorer {
+
class BuildConfiguration;
-class PROJECTEXPLORER_EXPORT BuildDirectoryAspect : public Utils::StringAspect
+class PROJECTEXPLORER_EXPORT BuildDirectoryAspect : public Utils::FilePathAspect
{
Q_OBJECT
public:
@@ -23,7 +22,7 @@ public:
bool isShadowBuild() const;
void setProblem(const QString &description);
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
static Utils::FilePath fixupDir(const Utils::FilePath &dir);
diff --git a/src/plugins/projectexplorer/buildconfiguration.cpp b/src/plugins/projectexplorer/buildconfiguration.cpp
index ff14b7ab07..70d5a348c1 100644
--- a/src/plugins/projectexplorer/buildconfiguration.cpp
+++ b/src/plugins/projectexplorer/buildconfiguration.cpp
@@ -17,8 +17,8 @@
#include "projectexplorer.h"
#include "projectexplorertr.h"
#include "project.h"
+#include "projectmanager.h"
#include "projecttree.h"
-#include "session.h"
#include "target.h"
#include <coreplugin/fileutils.h>
@@ -167,6 +167,10 @@ BuildConfiguration::BuildConfiguration(Target *target, Utils::Id id)
expander->registerVariable("buildDir", Tr::tr("Build directory"),
[this] { return buildDirectory().toUserOutput(); });
+ expander->registerFileVariables("BuildConfig:BuildDirectory",
+ Tr::tr("Build directory"),
+ [this] { return buildDirectory(); });
+
expander->registerVariable("BuildConfig:Name", Tr::tr("Name of the build configuration"),
[this] { return displayName(); });
@@ -209,7 +213,7 @@ BuildConfiguration::BuildConfiguration(Target *target, Utils::Id id)
connect(target, &Target::parsingStarted, this, &BuildConfiguration::enabledChanged);
connect(target, &Target::parsingFinished, this, &BuildConfiguration::enabledChanged);
connect(this, &BuildConfiguration::enabledChanged, this, [this] {
- if (isActive() && project() == SessionManager::startupProject()) {
+ if (isActive() && project() == ProjectManager::startupProject()) {
ProjectExplorerPlugin::updateActions();
ProjectExplorerPlugin::updateRunActions();
}
@@ -318,12 +322,15 @@ NamedWidget *BuildConfiguration::createConfigWidget()
widget = named;
}
- Layouting::Form builder;
+ Layouting::Form form;
for (BaseAspect *aspect : aspects()) {
- if (aspect->isVisible())
- aspect->addToLayout(builder.finishRow());
+ if (aspect->isVisible()) {
+ form.addItem(aspect);
+ form.addItem(Layouting::br);
+ }
}
- builder.attachTo(widget, Layouting::WithoutMargins);
+ form.addItem(Layouting::noMargin);
+ form.attachTo(widget);
return named;
}
@@ -612,13 +619,27 @@ FilePath BuildConfiguration::buildDirectoryFromTemplate(const FilePath &projectD
[buildType] { return buildTypeName(buildType); });
exp.registerSubProvider([kit] { return kit->macroExpander(); });
- QString buildDir = ProjectExplorerPlugin::buildDirectoryTemplate();
- qCDebug(bcLog) << "build dir template:" << buildDir;
+ FilePath buildDir = FilePath::fromUserInput(ProjectExplorerPlugin::buildDirectoryTemplate());
+ qCDebug(bcLog) << "build dir template:" << buildDir.toUserOutput();
buildDir = exp.expand(buildDir);
- qCDebug(bcLog) << "expanded build:" << buildDir;
- buildDir.replace(" ", "-");
+ qCDebug(bcLog) << "expanded build:" << buildDir.toUserOutput();
+ buildDir = buildDir.withNewPath(buildDir.path().replace(" ", "-"));
+
+ auto buildDevice = BuildDeviceKitAspect::device(kit);
+
+ if (buildDir.isAbsolutePath()) {
+ bool isReachable = buildDevice->ensureReachable(buildDir);
+ if (!isReachable)
+ return {};
+ return buildDevice->rootPath().withNewMappedPath(buildDir);
+ }
+
+ bool isReachable = buildDevice->ensureReachable(projectDir);
+ if (!isReachable)
+ return {};
- return projectDir.resolvePath(buildDir);
+ const FilePath baseDir = buildDevice->rootPath().withNewMappedPath(projectDir);
+ return baseDir.resolvePath(buildDir);
}
///
// IBuildConfigurationFactory
diff --git a/src/plugins/projectexplorer/buildmanager.cpp b/src/plugins/projectexplorer/buildmanager.cpp
index 57bfde6e46..28169513cb 100644
--- a/src/plugins/projectexplorer/buildmanager.cpp
+++ b/src/plugins/projectexplorer/buildmanager.cpp
@@ -16,8 +16,8 @@
#include "projectexplorerconstants.h"
#include "projectexplorersettings.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "runcontrol.h"
-#include "session.h"
#include "target.h"
#include "task.h"
#include "taskhub.h"
@@ -258,7 +258,7 @@ BuildManager::BuildManager(QObject *parent, QAction *cancelBuildAction)
m_instance = this;
d = new BuildManagerPrivate;
- connect(SessionManager::instance(), &SessionManager::aboutToRemoveProject,
+ connect(ProjectManager::instance(), &ProjectManager::aboutToRemoveProject,
this, &BuildManager::aboutToRemoveProject);
d->m_outputWindow = new Internal::CompileOutputWindow(cancelBuildAction);
@@ -318,19 +318,19 @@ void BuildManager::rebuildProjectWithoutDependencies(Project *project)
void BuildManager::buildProjectWithDependencies(Project *project, ConfigSelection configSelection)
{
- queue(SessionManager::projectOrder(project), {Id(Constants::BUILDSTEPS_BUILD)},
+ queue(ProjectManager::projectOrder(project), {Id(Constants::BUILDSTEPS_BUILD)},
configSelection);
}
void BuildManager::cleanProjectWithDependencies(Project *project, ConfigSelection configSelection)
{
- queue(SessionManager::projectOrder(project), {Id(Constants::BUILDSTEPS_CLEAN)},
+ queue(ProjectManager::projectOrder(project), {Id(Constants::BUILDSTEPS_CLEAN)},
configSelection);
}
void BuildManager::rebuildProjectWithDependencies(Project *project, ConfigSelection configSelection)
{
- queue(SessionManager::projectOrder(project),
+ queue(ProjectManager::projectOrder(project),
{Id(Constants::BUILDSTEPS_CLEAN), Id(Constants::BUILDSTEPS_BUILD)},
configSelection);
}
@@ -384,7 +384,7 @@ BuildForRunConfigStatus BuildManager::potentiallyBuildForRunConfig(RunConfigurat
}
Project * const pro = rc->target()->project();
- const int queueCount = queue(SessionManager::projectOrder(pro), stepIds,
+ const int queueCount = queue(ProjectManager::projectOrder(pro), stepIds,
ConfigSelection::Active, rc);
if (rc->target()->activeBuildConfiguration())
rc->target()->activeBuildConfiguration()->restrictNextBuild(nullptr);
@@ -442,16 +442,6 @@ int BuildManager::getErrorTaskCount()
return errors;
}
-void BuildManager::setCompileOutputSettings(const CompileOutputSettings &settings)
-{
- d->m_outputWindow->setSettings(settings);
-}
-
-const CompileOutputSettings &BuildManager::compileOutputSettings()
-{
- return d->m_outputWindow->settings();
-}
-
QString BuildManager::displayNameForStepId(Id stepId)
{
if (stepId == Constants::BUILDSTEPS_CLEAN) {
@@ -853,7 +843,7 @@ bool BuildManager::buildLists(const QList<BuildStepList *> bsls, const QStringLi
return false;
}
- if (d->m_outputWindow->settings().popUp)
+ if (CompileOutputSettings::instance().popUp())
d->m_outputWindow->popup(IOutputPane::NoModeSwitch);
startBuildQueue();
return true;
@@ -866,7 +856,7 @@ void BuildManager::appendStep(BuildStep *step, const QString &name)
d->m_outputWindow->popup(IOutputPane::NoModeSwitch);
return;
}
- if (d->m_outputWindow->settings().popUp)
+ if (CompileOutputSettings::instance().popUp())
d->m_outputWindow->popup(IOutputPane::NoModeSwitch);
startBuildQueue();
}
diff --git a/src/plugins/projectexplorer/buildmanager.h b/src/plugins/projectexplorer/buildmanager.h
index d7d2c91562..d9478db409 100644
--- a/src/plugins/projectexplorer/buildmanager.h
+++ b/src/plugins/projectexplorer/buildmanager.h
@@ -10,12 +10,10 @@
#include <QStringList>
namespace ProjectExplorer {
-class RunConfiguration;
-
-namespace Internal { class CompileOutputSettings; }
-class Task;
class Project;
+class RunConfiguration;
+class Task;
enum class BuildForRunConfigStatus { Building, NotBuilding, BuildFailed };
enum class ConfigSelection { All, Active };
@@ -66,9 +64,6 @@ public:
static int getErrorTaskCount();
- static void setCompileOutputSettings(const Internal::CompileOutputSettings &settings);
- static const Internal::CompileOutputSettings &compileOutputSettings();
-
static QString displayNameForStepId(Utils::Id stepId);
public slots:
diff --git a/src/plugins/projectexplorer/buildpropertiessettings.cpp b/src/plugins/projectexplorer/buildpropertiessettings.cpp
index a9126551e3..6df8fdd328 100644
--- a/src/plugins/projectexplorer/buildpropertiessettings.cpp
+++ b/src/plugins/projectexplorer/buildpropertiessettings.cpp
@@ -16,15 +16,33 @@ namespace ProjectExplorer {
const char DEFAULT_BUILD_DIRECTORY_TEMPLATE[]
= "../%{JS: Util.asciify(\"build-%{Project:Name}-%{Kit:FileSystemName}-%{BuildConfig:Name}\")}";
-BuildPropertiesSettings::BuildTriStateAspect::BuildTriStateAspect()
- : TriStateAspect{Tr::tr("Enable"), Tr::tr("Disable"), Tr::tr("Use Project Default")}
+BuildPropertiesSettings::BuildTriStateAspect::BuildTriStateAspect(AspectContainer *container)
+ : TriStateAspect(container, Tr::tr("Enable"), Tr::tr("Disable"), Tr::tr("Use Project Default"))
{}
BuildPropertiesSettings::BuildPropertiesSettings()
{
setAutoApply(false);
- registerAspect(&buildDirectoryTemplate);
+ setId("AB.ProjectExplorer.BuildPropertiesSettingsPage");
+ setDisplayName(Tr::tr("Default Build Properties"));
+ setCategory(ProjectExplorer::Constants::BUILD_AND_RUN_SETTINGS_CATEGORY);
+ setSettings(this);
+
+ setLayouter([this] {
+ using namespace Layouting;
+
+ return Column {
+ Form {
+ buildDirectoryTemplate, br,
+ separateDebugInfo, br,
+ qmlDebugging, br,
+ qtQuickCompiler
+ },
+ st
+ };
+ });
+
buildDirectoryTemplate.setDisplayStyle(StringAspect::LineEditDisplay);
buildDirectoryTemplate.setSettingsKey("Directories/BuildDirectory.TemplateV2");
buildDirectoryTemplate.setDefaultValue(DEFAULT_BUILD_DIRECTORY_TEMPLATE);
@@ -32,19 +50,12 @@ BuildPropertiesSettings::BuildPropertiesSettings()
buildDirectoryTemplate.setUseGlobalMacroExpander();
buildDirectoryTemplate.setUseResetButton();
- registerAspect(&buildDirectoryTemplateOld); // TODO: Remove in ~4.16
- buildDirectoryTemplateOld.setSettingsKey("Directories/BuildDirectory.Template");
- buildDirectoryTemplateOld.setDefaultValue(DEFAULT_BUILD_DIRECTORY_TEMPLATE);
-
- registerAspect(&separateDebugInfo);
separateDebugInfo.setSettingsKey("ProjectExplorer/Settings/SeparateDebugInfo");
separateDebugInfo.setLabelText(Tr::tr("Separate debug info:"));
- registerAspect(&qmlDebugging);
qmlDebugging.setSettingsKey("ProjectExplorer/Settings/QmlDebugging");
qmlDebugging.setLabelText(Tr::tr("QML debugging:"));
- registerAspect(&qtQuickCompiler);
qtQuickCompiler.setSettingsKey("ProjectExplorer/Settings/QtQuickCompiler");
qtQuickCompiler.setLabelText(Tr::tr("Use qmlcachegen:"));
@@ -54,51 +65,9 @@ BuildPropertiesSettings::BuildPropertiesSettings()
&qtQuickCompiler, &BaseAspect::setVisible);
}
-void BuildPropertiesSettings::readSettings(QSettings *s)
-{
- AspectContainer::readSettings(s);
-
- // TODO: Remove in ~4.16
- QString v = buildDirectoryTemplate.value();
- if (v.isEmpty())
- v = buildDirectoryTemplateOld.value();
- if (v.isEmpty())
- v = DEFAULT_BUILD_DIRECTORY_TEMPLATE;
- v.replace("%{CurrentProject:Name}", "%{Project:Name}");
- v.replace("%{CurrentKit:FileSystemName}", "%{Kit:FileSystemName}");
- v.replace("%{CurrentBuild:Name}", "%{BuildConfig:Name}");
- buildDirectoryTemplate.setValue(v);
-}
-
QString BuildPropertiesSettings::defaultBuildDirectoryTemplate()
{
return QString(DEFAULT_BUILD_DIRECTORY_TEMPLATE);
}
-namespace Internal {
-
-BuildPropertiesSettingsPage::BuildPropertiesSettingsPage(BuildPropertiesSettings *settings)
-{
- setId("AB.ProjectExplorer.BuildPropertiesSettingsPage");
- setDisplayName(Tr::tr("Default Build Properties"));
- setCategory(ProjectExplorer::Constants::BUILD_AND_RUN_SETTINGS_CATEGORY);
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- BuildPropertiesSettings &s = *settings;
- using namespace Layouting;
-
- Column {
- Form {
- s.buildDirectoryTemplate,
- s.separateDebugInfo,
- s.qmlDebugging,
- s.qtQuickCompiler
- },
- st
- }.attachTo(widget);
- });
-}
-
-} // Internal
} // ProjectExplorer
diff --git a/src/plugins/projectexplorer/buildpropertiessettings.h b/src/plugins/projectexplorer/buildpropertiessettings.h
index 75ec957a4e..3b1b2b7c69 100644
--- a/src/plugins/projectexplorer/buildpropertiessettings.h
+++ b/src/plugins/projectexplorer/buildpropertiessettings.h
@@ -7,11 +7,9 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
-
namespace ProjectExplorer {
-class PROJECTEXPLORER_EXPORT BuildPropertiesSettings : public Utils::AspectContainer
+class PROJECTEXPLORER_EXPORT BuildPropertiesSettings : public Core::PagedSettings
{
public:
BuildPropertiesSettings();
@@ -19,28 +17,16 @@ public:
class BuildTriStateAspect : public Utils::TriStateAspect
{
public:
- BuildTriStateAspect();
+ explicit BuildTriStateAspect(AspectContainer *container);
};
- Utils::StringAspect buildDirectoryTemplate;
- Utils::StringAspect buildDirectoryTemplateOld; // TODO: Remove in ~4.16
- BuildTriStateAspect separateDebugInfo;
- BuildTriStateAspect qmlDebugging;
- BuildTriStateAspect qtQuickCompiler;
+ Utils::StringAspect buildDirectoryTemplate{this};
+ BuildTriStateAspect separateDebugInfo{this};
+ BuildTriStateAspect qmlDebugging{this};
+ BuildTriStateAspect qtQuickCompiler{this};
Utils::BoolAspect showQtSettings;
- void readSettings(QSettings *settings);
-
QString defaultBuildDirectoryTemplate();
};
-namespace Internal {
-
-class BuildPropertiesSettingsPage final : public Core::IOptionsPage
-{
-public:
- explicit BuildPropertiesSettingsPage(BuildPropertiesSettings *settings);
-};
-
-} // namespace Internal
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/buildsettingspropertiespage.cpp b/src/plugins/projectexplorer/buildsettingspropertiespage.cpp
index 410449083c..2caa44712e 100644
--- a/src/plugins/projectexplorer/buildsettingspropertiespage.cpp
+++ b/src/plugins/projectexplorer/buildsettingspropertiespage.cpp
@@ -10,10 +10,10 @@
#include "project.h"
#include "projectconfigurationmodel.h"
#include "projectexplorertr.h"
-#include "session.h"
#include "target.h"
#include <coreplugin/icore.h>
+#include <coreplugin/session.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
@@ -185,7 +185,7 @@ void BuildSettingsWidget::currentIndexChanged(int index)
{
auto buildConfiguration = qobject_cast<BuildConfiguration *>(
m_target->buildConfigurationModel()->projectConfigurationAt(index));
- SessionManager::setActiveBuildConfiguration(m_target, buildConfiguration, SetActive::Cascade);
+ m_target->setActiveBuildConfiguration(buildConfiguration, SetActive::Cascade);
}
void BuildSettingsWidget::updateActiveConfiguration()
@@ -222,7 +222,7 @@ void BuildSettingsWidget::createConfiguration(const BuildInfo &info_)
return;
m_target->addBuildConfiguration(bc);
- SessionManager::setActiveBuildConfiguration(m_target, bc, SetActive::Cascade);
+ m_target->setActiveBuildConfiguration(bc, SetActive::Cascade);
}
QString BuildSettingsWidget::uniqueName(const QString & name)
@@ -286,7 +286,7 @@ void BuildSettingsWidget::cloneConfiguration()
bc->setDisplayName(name);
const FilePath buildDirectory = bc->buildDirectory();
if (buildDirectory != m_target->project()->projectDirectory()) {
- const std::function<bool(const FilePath &)> isBuildDirOk = [this](const FilePath &candidate) {
+ const FilePathPredicate isBuildDirOk = [this](const FilePath &candidate) {
if (candidate.exists())
return false;
return !anyOf(m_target->buildConfigurations(), [&candidate](const BuildConfiguration *bc) {
@@ -295,7 +295,7 @@ void BuildSettingsWidget::cloneConfiguration()
bc->setBuildDirectory(makeUniquelyNumbered(buildDirectory, isBuildDirOk));
}
m_target->addBuildConfiguration(bc);
- SessionManager::setActiveBuildConfiguration(m_target, bc, SetActive::Cascade);
+ m_target->setActiveBuildConfiguration(bc, SetActive::Cascade);
}
void BuildSettingsWidget::deleteConfiguration(BuildConfiguration *deleteConfiguration)
diff --git a/src/plugins/projectexplorer/buildstep.cpp b/src/plugins/projectexplorer/buildstep.cpp
index c078a73d82..32be37048e 100644
--- a/src/plugins/projectexplorer/buildstep.cpp
+++ b/src/plugins/projectexplorer/buildstep.cpp
@@ -147,7 +147,7 @@ QWidget *BuildStep::doCreateConfigWidget()
setSummaryText(m_summaryUpdater());
};
- for (BaseAspect *aspect : std::as_const(m_aspects))
+ for (BaseAspect *aspect : std::as_const(*this))
connect(aspect, &BaseAspect::changed, widget, recreateSummary);
connect(buildConfiguration(), &BuildConfiguration::buildDirectoryChanged,
@@ -160,12 +160,13 @@ QWidget *BuildStep::doCreateConfigWidget()
QWidget *BuildStep::createConfigWidget()
{
- Layouting::Form builder;
- for (BaseAspect *aspect : std::as_const(m_aspects)) {
+ Layouting::Form form;
+ for (BaseAspect *aspect : std::as_const(*this)) {
if (aspect->isVisible())
- aspect->addToLayout(builder.finishRow());
+ form.addItem(aspect);
}
- auto widget = builder.emerge(Layouting::WithoutMargins);
+ form.addItem(Layouting::noMargin);
+ auto widget = form.emerge();
if (m_addMacroExpander)
VariableChooser::addSupportForChildWidgets(widget, macroExpander());
@@ -361,7 +362,7 @@ bool BuildStepFactory::canHandle(BuildStepList *bsl) const
return false;
}
- if (!m_isRepeatable && bsl->contains(m_info.id))
+ if (!m_isRepeatable && bsl->contains(m_stepId))
return false;
if (m_supportedConfiguration.isValid()) {
@@ -375,14 +376,42 @@ bool BuildStepFactory::canHandle(BuildStepList *bsl) const
return true;
}
+QString BuildStepFactory::displayName() const
+{
+ return m_displayName;
+}
+
+void BuildStepFactory::cloneStepCreator(Id exitstingStepId, Id overrideNewStepId)
+{
+ m_stepId = {};
+ m_creator = {};
+ for (BuildStepFactory *factory : BuildStepFactory::allBuildStepFactories()) {
+ if (factory->m_stepId == exitstingStepId) {
+ m_creator = factory->m_creator;
+ m_stepId = factory->m_stepId;
+ m_displayName = factory->m_displayName;
+ // Other bits are intentionally not copied as they are unlikely to be
+ // useful in the cloner's context. The cloner can/has to finish the
+ // setup on its own.
+ break;
+ }
+ }
+ // Existence should be guaranteed by plugin dependencies. In case it fails,
+ // bark and keep the factory in a state where the invalid m_stepId keeps it
+ // inaction.
+ QTC_ASSERT(m_creator, return);
+ if (overrideNewStepId.isValid())
+ m_stepId = overrideNewStepId;
+}
+
void BuildStepFactory::setDisplayName(const QString &displayName)
{
- m_info.displayName = displayName;
+ m_displayName = displayName;
}
-void BuildStepFactory::setFlags(BuildStepInfo::Flags flags)
+void BuildStepFactory::setFlags(BuildStep::Flags flags)
{
- m_info.flags = flags;
+ m_flags = flags;
}
void BuildStepFactory::setSupportedStepList(Id id)
@@ -415,20 +444,21 @@ void BuildStepFactory::setSupportedDeviceTypes(const QList<Id> &ids)
m_supportedDeviceTypes = ids;
}
-BuildStepInfo BuildStepFactory::stepInfo() const
+BuildStep::Flags BuildStepFactory::stepFlags() const
{
- return m_info;
+ return m_flags;
}
Id BuildStepFactory::stepId() const
{
- return m_info.id;
+ return m_stepId;
}
BuildStep *BuildStepFactory::create(BuildStepList *parent)
{
- BuildStep *step = m_info.creator(parent);
- step->setDefaultDisplayName(m_info.displayName);
+ QTC_ASSERT(m_creator, return nullptr);
+ BuildStep *step = m_creator(parent);
+ step->setDefaultDisplayName(m_displayName);
return step;
}
diff --git a/src/plugins/projectexplorer/buildstep.h b/src/plugins/projectexplorer/buildstep.h
index 547a5e2027..46be75f563 100644
--- a/src/plugins/projectexplorer/buildstep.h
+++ b/src/plugins/projectexplorer/buildstep.h
@@ -76,6 +76,12 @@ public:
enum OutputNewlineSetting { DoAppendNewline, DontAppendNewline };
+ enum Flags {
+ Uncreatable = 1 << 0,
+ Unclonable = 1 << 1,
+ UniqueStep = 1 << 8 // Can't be used twice in a BuildStepList
+ };
+
bool widgetExpandedByDefault() const;
void setWidgetExpandedByDefault(bool widgetExpandedByDefault);
@@ -136,23 +142,6 @@ private:
QString m_summaryText;
};
-class PROJECTEXPLORER_EXPORT BuildStepInfo
-{
-public:
- enum Flags {
- Uncreatable = 1 << 0,
- Unclonable = 1 << 1,
- UniqueStep = 1 << 8 // Can't be used twice in a BuildStepList
- };
-
- using BuildStepCreator = std::function<BuildStep *(BuildStepList *)>;
-
- Utils::Id id;
- QString displayName;
- Flags flags = Flags();
- BuildStepCreator creator;
-};
-
class PROJECTEXPLORER_EXPORT BuildStepFactory
{
public:
@@ -163,23 +152,26 @@ public:
static const QList<BuildStepFactory *> allBuildStepFactories();
- BuildStepInfo stepInfo() const;
+ BuildStep::Flags stepFlags() const;
Utils::Id stepId() const;
BuildStep *create(BuildStepList *parent);
BuildStep *restore(BuildStepList *parent, const QVariantMap &map);
bool canHandle(BuildStepList *bsl) const;
+ QString displayName() const;
+
protected:
using BuildStepCreator = std::function<BuildStep *(BuildStepList *)>;
template <class BuildStepType>
void registerStep(Utils::Id id)
{
- QTC_CHECK(!m_info.creator);
- m_info.id = id;
- m_info.creator = [id](BuildStepList *bsl) { return new BuildStepType(bsl, id); };
+ QTC_CHECK(!m_creator);
+ m_stepId = id;
+ m_creator = [id](BuildStepList *bsl) { return new BuildStepType(bsl, id); };
}
+ void cloneStepCreator(Utils::Id exitstingStepId, Utils::Id overrideNewStepId = {});
void setSupportedStepList(Utils::Id id);
void setSupportedStepLists(const QList<Utils::Id> &ids);
@@ -189,10 +181,13 @@ protected:
void setSupportedDeviceTypes(const QList<Utils::Id> &ids);
void setRepeatable(bool on) { m_isRepeatable = on; }
void setDisplayName(const QString &displayName);
- void setFlags(BuildStepInfo::Flags flags);
+ void setFlags(BuildStep::Flags flags);
private:
- BuildStepInfo m_info;
+ Utils::Id m_stepId;
+ QString m_displayName;
+ BuildStep::Flags m_flags = {};
+ BuildStepCreator m_creator;
Utils::Id m_supportedProjectType;
QList<Utils::Id> m_supportedDeviceTypes;
diff --git a/src/plugins/projectexplorer/buildsteplist.cpp b/src/plugins/projectexplorer/buildsteplist.cpp
index 8d6159541e..0094bf0b68 100644
--- a/src/plugins/projectexplorer/buildsteplist.cpp
+++ b/src/plugins/projectexplorer/buildsteplist.cpp
@@ -43,16 +43,6 @@ QVariantMap BuildStepList::toMap() const
{
QVariantMap map;
- {
- // Only written for compatibility reasons within the 4.11 cycle
- const char CONFIGURATION_ID_KEY[] = "ProjectExplorer.ProjectConfiguration.Id";
- const char DISPLAY_NAME_KEY[] = "ProjectExplorer.ProjectConfiguration.DisplayName";
- const char DEFAULT_DISPLAY_NAME_KEY[] = "ProjectExplorer.ProjectConfiguration.DefaultDisplayName";
- map.insert(QLatin1String(CONFIGURATION_ID_KEY), m_id.toSetting());
- map.insert(QLatin1String(DISPLAY_NAME_KEY), displayName());
- map.insert(QLatin1String(DEFAULT_DISPLAY_NAME_KEY), displayName());
- }
-
// Save build steps
map.insert(QString::fromLatin1(STEPS_COUNT_KEY), m_steps.count());
for (int i = 0; i < m_steps.count(); ++i)
diff --git a/src/plugins/projectexplorer/buildstepspage.cpp b/src/plugins/projectexplorer/buildstepspage.cpp
index 241d2a9835..21fb4c2093 100644
--- a/src/plugins/projectexplorer/buildstepspage.cpp
+++ b/src/plugins/projectexplorer/buildstepspage.cpp
@@ -209,14 +209,14 @@ void BuildStepListWidget::updateAddBuildStepMenu()
if (!factory->canHandle(m_buildStepList))
continue;
- const BuildStepInfo &info = factory->stepInfo();
- if (info.flags & BuildStepInfo::Uncreatable)
+ const BuildStep::Flags flags = factory->stepFlags();
+ if (flags & BuildStep::Uncreatable)
continue;
- if ((info.flags & BuildStepInfo::UniqueStep) && m_buildStepList->contains(info.id))
+ if ((flags & BuildStep::UniqueStep) && m_buildStepList->contains(factory->stepId()))
continue;
- QAction *action = menu->addAction(info.displayName);
+ QAction *action = menu->addAction(factory->displayName());
connect(action, &QAction::triggered, this, [factory, this] {
BuildStep *newStep = factory->create(m_buildStepList);
QTC_ASSERT(newStep, return);
diff --git a/src/plugins/projectexplorer/buildsystem.cpp b/src/plugins/projectexplorer/buildsystem.cpp
index 0a2c2f3a85..9a120a9863 100644
--- a/src/plugins/projectexplorer/buildsystem.cpp
+++ b/src/plugins/projectexplorer/buildsystem.cpp
@@ -7,9 +7,9 @@
#include "extracompiler.h"
#include "projectexplorer.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "runconfiguration.h"
#include "runcontrol.h"
-#include "session.h"
#include "target.h"
#include <coreplugin/messagemanager.h>
@@ -64,7 +64,7 @@ BuildSystem::BuildSystem(Target *target)
connect(&d->m_delayedParsingTimer, &QTimer::timeout, this,
[this] {
- if (SessionManager::hasProject(project()))
+ if (ProjectManager::hasProject(project()))
triggerParsing();
else
requestDelayedParse();
@@ -325,7 +325,6 @@ void BuildSystem::setDeploymentData(const DeploymentData &deploymentData)
if (d->m_deploymentData != deploymentData) {
d->m_deploymentData = deploymentData;
emit deploymentDataChanged();
- emit applicationTargetsChanged();
emit target()->deploymentDataChanged();
}
}
@@ -337,10 +336,7 @@ DeploymentData BuildSystem::deploymentData() const
void BuildSystem::setApplicationTargets(const QList<BuildTargetInfo> &appTargets)
{
- if (Utils::toSet(appTargets) != Utils::toSet(d->m_appTargets)) {
- d->m_appTargets = appTargets;
- emit applicationTargetsChanged();
- }
+ d->m_appTargets = appTargets;
}
const QList<BuildTargetInfo> BuildSystem::applicationTargets() const
diff --git a/src/plugins/projectexplorer/buildsystem.h b/src/plugins/projectexplorer/buildsystem.h
index 0d19c9110e..1cd1e41a06 100644
--- a/src/plugins/projectexplorer/buildsystem.h
+++ b/src/plugins/projectexplorer/buildsystem.h
@@ -152,7 +152,6 @@ signals:
void parsingStarted();
void parsingFinished(bool success);
void deploymentDataChanged();
- void applicationTargetsChanged();
void testInformationUpdated();
protected:
diff --git a/src/plugins/projectexplorer/codestylesettingspropertiespage.cpp b/src/plugins/projectexplorer/codestylesettingspropertiespage.cpp
index 2ff0285a61..f7ed131e57 100644
--- a/src/plugins/projectexplorer/codestylesettingspropertiespage.cpp
+++ b/src/plugins/projectexplorer/codestylesettingspropertiespage.cpp
@@ -48,12 +48,13 @@ CodeStyleSettingsWidget::CodeStyleSettingsWidget(Project *project)
connect(languageComboBox, &QComboBox::currentIndexChanged,
stackedWidget, &QStackedWidget::setCurrentIndex);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row { new QLabel(Tr::tr("Language:")), languageComboBox, st },
- stackedWidget
- }.attachTo(this, WithoutMargins);
+ stackedWidget,
+ noMargin
+ }.attachTo(this);
}
} // ProjectExplorer::Internal
diff --git a/src/plugins/projectexplorer/compileoutputwindow.cpp b/src/plugins/projectexplorer/compileoutputwindow.cpp
index f345792ca8..bafc31fc14 100644
--- a/src/plugins/projectexplorer/compileoutputwindow.cpp
+++ b/src/plugins/projectexplorer/compileoutputwindow.cpp
@@ -17,7 +17,9 @@
#include <texteditor/texteditorsettings.h>
#include <texteditor/fontsettings.h>
#include <texteditor/behaviorsettings.h>
+
#include <utils/algorithm.h>
+#include <utils/layoutbuilder.h>
#include <utils/outputformatter.h>
#include <utils/proxyaction.h>
#include <utils/theme/theme.h>
@@ -43,9 +45,6 @@ namespace Internal {
const char SETTINGS_KEY[] = "ProjectExplorer/CompileOutput/Zoom";
const char C_COMPILE_OUTPUT[] = "ProjectExplorer.CompileOutput";
-const char POP_UP_KEY[] = "ProjectExplorer/Settings/ShowCompilerOutput";
-const char WRAP_OUTPUT_KEY[] = "ProjectExplorer/Settings/WrapBuildOutput";
-const char MAX_LINES_KEY[] = "ProjectExplorer/Settings/MaxBuildOutputLines";
const char OPTIONS_PAGE_ID[] = "C.ProjectExplorer.CompileOutputOptions";
CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) :
@@ -101,8 +100,17 @@ CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) :
Tr::tr("O"));
ExtensionSystem::PluginManager::addObject(m_handler);
setupContext(C_COMPILE_OUTPUT, m_outputWindow);
- loadSettings();
updateFromSettings();
+
+ m_outputWindow->setWordWrapEnabled(m_settings.wrapOutput());
+ m_outputWindow->setMaxCharCount(m_settings.maxCharCount());
+
+ connect(&m_settings.wrapOutput, &Utils::BaseAspect::changed, m_outputWindow, [this] {
+ m_outputWindow->setWordWrapEnabled(m_settings.wrapOutput());
+ });
+ connect(&m_settings.maxCharCount, &Utils::BaseAspect::changed, m_outputWindow, [this] {
+ m_outputWindow->setMaxCharCount(m_settings.maxCharCount());
+ });
}
CompileOutputWindow::~CompileOutputWindow()
@@ -115,10 +123,7 @@ CompileOutputWindow::~CompileOutputWindow()
void CompileOutputWindow::updateFromSettings()
{
- m_outputWindow->setWordWrapEnabled(m_settings.wrapOutput);
- m_outputWindow->setMaxCharCount(m_settings.maxCharCount);
}
-
bool CompileOutputWindow::hasFocus() const
{
return m_outputWindow->window()->focusWidget() == m_outputWindow;
@@ -213,13 +218,6 @@ void CompileOutputWindow::reset()
m_outputWindow->reset();
}
-void CompileOutputWindow::setSettings(const CompileOutputSettings &settings)
-{
- m_settings = settings;
- storeSettings();
- updateFromSettings();
-}
-
Utils::OutputFormatter *CompileOutputWindow::outputFormatter() const
{
return m_outputWindow->outputFormatter();
@@ -231,75 +229,49 @@ void CompileOutputWindow::updateFilter()
filterUsesRegexp(), filterIsInverted());
}
-const bool kPopUpDefault = false;
-const bool kWrapOutputDefault = true;
+// CompileOutputSettings
-void CompileOutputWindow::loadSettings()
-{
- QSettings * const s = Core::ICore::settings();
- m_settings.popUp = s->value(POP_UP_KEY, kPopUpDefault).toBool();
- m_settings.wrapOutput = s->value(WRAP_OUTPUT_KEY, kWrapOutputDefault).toBool();
- m_settings.maxCharCount = s->value(MAX_LINES_KEY,
- Core::Constants::DEFAULT_MAX_CHAR_COUNT).toInt() * 100;
-}
+static CompileOutputSettings *s_compileOutputSettings;
-void CompileOutputWindow::storeSettings() const
+CompileOutputSettings &CompileOutputSettings::instance()
{
- Utils::QtcSettings *const s = Core::ICore::settings();
- s->setValueWithDefault(POP_UP_KEY, m_settings.popUp, kPopUpDefault);
- s->setValueWithDefault(WRAP_OUTPUT_KEY, m_settings.wrapOutput, kWrapOutputDefault);
- s->setValueWithDefault(MAX_LINES_KEY,
- m_settings.maxCharCount / 100,
- Core::Constants::DEFAULT_MAX_CHAR_COUNT);
+ return *s_compileOutputSettings;
}
-class CompileOutputSettingsWidget : public Core::IOptionsPageWidget
+CompileOutputSettings::CompileOutputSettings()
{
-public:
- CompileOutputSettingsWidget()
- {
- const CompileOutputSettings &settings = BuildManager::compileOutputSettings();
- m_wrapOutputCheckBox.setText(Tr::tr("Word-wrap output"));
- m_wrapOutputCheckBox.setChecked(settings.wrapOutput);
- m_popUpCheckBox.setText(Tr::tr("Open Compile Output when building"));
- m_popUpCheckBox.setChecked(settings.popUp);
- m_maxCharsBox.setMaximum(100000000);
- m_maxCharsBox.setValue(settings.maxCharCount);
- const auto layout = new QVBoxLayout(this);
- layout->addWidget(&m_wrapOutputCheckBox);
- layout->addWidget(&m_popUpCheckBox);
- const auto maxCharsLayout = new QHBoxLayout;
- const QString msg = Tr::tr("Limit output to %1 characters");
- const QStringList parts = msg.split("%1") << QString() << QString();
- maxCharsLayout->addWidget(new QLabel(parts.at(0).trimmed()));
- maxCharsLayout->addWidget(&m_maxCharsBox);
- maxCharsLayout->addWidget(new QLabel(parts.at(1).trimmed()));
- maxCharsLayout->addStretch(1);
- layout->addLayout(maxCharsLayout);
- layout->addStretch(1);
- }
-
- void apply() final
- {
- CompileOutputSettings s;
- s.wrapOutput = m_wrapOutputCheckBox.isChecked();
- s.popUp = m_popUpCheckBox.isChecked();
- s.maxCharCount = m_maxCharsBox.value();
- BuildManager::setCompileOutputSettings(s);
- }
-
-private:
- QCheckBox m_wrapOutputCheckBox;
- QCheckBox m_popUpCheckBox;
- QSpinBox m_maxCharsBox;
-};
+ s_compileOutputSettings = this;
-CompileOutputSettingsPage::CompileOutputSettingsPage()
-{
setId(OPTIONS_PAGE_ID);
setDisplayName(Tr::tr("Compile Output"));
setCategory(Constants::BUILD_AND_RUN_SETTINGS_CATEGORY);
- setWidgetCreator([] { return new CompileOutputSettingsWidget; });
+
+ wrapOutput.setSettingsKey("ProjectExplorer/Settings/WrapBuildOutput");
+ wrapOutput.setDefaultValue(true);
+ wrapOutput.setLabelText(Tr::tr("Word-wrap output"));
+
+ popUp.setSettingsKey("ProjectExplorer/Settings/ShowCompilerOutput");
+ popUp.setLabelText(Tr::tr("Open Compile Output when building"));
+
+ maxCharCount.setSettingsKey("ProjectExplorer/Settings/MaxBuildOutputLines");
+ maxCharCount.setRange(1, Core::Constants::DEFAULT_MAX_CHAR_COUNT);
+ maxCharCount.setDefaultValue(Core::Constants::DEFAULT_MAX_CHAR_COUNT);
+ maxCharCount.setToSettingsTransformation([](const QVariant &v) { return v.toInt() / 100; });
+ maxCharCount.setFromSettingsTransformation([](const QVariant &v) { return v.toInt() * 100; });
+
+ setLayouter([this] {
+ using namespace Layouting;
+ const QString msg = Tr::tr("Limit output to %1 characters");
+ const QStringList parts = msg.split("%1") << QString() << QString();
+ return Column {
+ wrapOutput,
+ popUp,
+ Row { parts.at(0), maxCharCount, parts.at(1), st },
+ st
+ };
+ });
+
+ readSettings();
}
} // Internal
diff --git a/src/plugins/projectexplorer/compileoutputwindow.h b/src/plugins/projectexplorer/compileoutputwindow.h
index e1f000de9a..89c7b749f2 100644
--- a/src/plugins/projectexplorer/compileoutputwindow.h
+++ b/src/plugins/projectexplorer/compileoutputwindow.h
@@ -26,6 +26,18 @@ namespace Internal {
class ShowOutputTaskHandler;
class CompileOutputTextEdit;
+class CompileOutputSettings final : public Core::PagedSettings
+{
+public:
+ CompileOutputSettings();
+
+ static CompileOutputSettings &instance();
+
+ Utils::BoolAspect popUp{this};
+ Utils::BoolAspect wrapOutput{this};
+ Utils::IntegerAspect maxCharCount{this};
+};
+
class CompileOutputWindow final : public Core::IOutputPane
{
Q_OBJECT
@@ -57,19 +69,13 @@ public:
void flush();
void reset();
- const CompileOutputSettings &settings() const { return m_settings; }
- void setSettings(const CompileOutputSettings &settings);
-
Utils::OutputFormatter *outputFormatter() const;
private:
void updateFilter() override;
const QList<Core::OutputWindow *> outputWindows() const override { return {m_outputWindow}; }
- void loadSettings();
- void storeSettings() const;
void updateFromSettings();
-
Core::OutputWindow *m_outputWindow;
ShowOutputTaskHandler *m_handler;
QToolButton *m_cancelBuildButton;
@@ -77,11 +83,5 @@ private:
CompileOutputSettings m_settings;
};
-class CompileOutputSettingsPage final : public Core::IOptionsPage
-{
-public:
- CompileOutputSettingsPage();
-};
-
} // namespace Internal
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/copystep.cpp b/src/plugins/projectexplorer/copystep.cpp
new file mode 100644
index 0000000000..db3b16098e
--- /dev/null
+++ b/src/plugins/projectexplorer/copystep.cpp
@@ -0,0 +1,114 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "copystep.h"
+
+#include "projectexplorerconstants.h"
+#include "projectexplorertr.h"
+
+#include <utils/aspects.h>
+
+using namespace Utils;
+
+namespace ProjectExplorer::Internal {
+
+const char SOURCE_KEY[] = "ProjectExplorer.CopyStep.Source";
+const char TARGET_KEY[] = "ProjectExplorer.CopyStep.Target";
+
+class CopyStepBase : public BuildStep
+{
+public:
+ CopyStepBase(BuildStepList *bsl, Id id)
+ : BuildStep(bsl, id)
+ {
+ m_sourceAspect.setSettingsKey(SOURCE_KEY);
+ m_sourceAspect.setLabelText(Tr::tr("Source:"));
+
+ m_targetAspect.setSettingsKey(TARGET_KEY);
+ m_targetAspect.setLabelText(Tr::tr("Target:"));
+
+ addMacroExpander();
+ }
+
+protected:
+ bool init() final
+ {
+ m_source = m_sourceAspect();
+ m_target = m_targetAspect();
+ return m_source.exists();
+ }
+
+ void doRun() final
+ {
+ // FIXME: asyncCopy does not handle directories yet.
+ QTC_ASSERT(m_source.isFile(), emit finished(false));
+ m_source.asyncCopy(m_target, this, [this](const expected_str<void> &cont) {
+ if (!cont) {
+ addOutput(cont.error(), OutputFormat::ErrorMessage);
+ addOutput(Tr::tr("Copying failed"), OutputFormat::ErrorMessage);
+ emit finished(false);
+ } else {
+ addOutput(Tr::tr("Copying finished"), OutputFormat::NormalMessage);
+ emit finished(true);
+ }
+ });
+ }
+
+ FilePathAspect m_sourceAspect{this};
+ FilePathAspect m_targetAspect{this};
+
+private:
+ FilePath m_source;
+ FilePath m_target;
+};
+
+class CopyFileStep final : public CopyStepBase
+{
+public:
+ CopyFileStep(BuildStepList *bsl, Id id)
+ : CopyStepBase(bsl, id)
+ {
+ // Expected kind could be stricter in theory, but since this here is
+ // a last stand fallback, better not impose extra "nice to have"
+ // work on the system.
+ m_sourceAspect.setExpectedKind(PathChooser::Any); // "File"
+ m_targetAspect.setExpectedKind(PathChooser::Any); // "SaveFile"
+
+ setSummaryUpdater([] {
+ return QString("<b>" + Tr::tr("Copy file") + "</b>");
+ });
+ }
+};
+
+class CopyDirectoryStep final : public CopyStepBase
+{
+public:
+ CopyDirectoryStep(BuildStepList *bsl, Id id)
+ : CopyStepBase(bsl, id)
+ {
+ m_sourceAspect.setExpectedKind(PathChooser::Directory);
+ m_targetAspect.setExpectedKind(PathChooser::Directory);
+
+ setSummaryUpdater([] {
+ return QString("<b>" + Tr::tr("Copy directory recursively") + "</b>");
+ });
+ }
+};
+
+// Factories
+
+CopyFileStepFactory::CopyFileStepFactory()
+{
+ registerStep<CopyFileStep>(Constants::COPY_FILE_STEP);
+ //: Default CopyStep display name
+ setDisplayName(Tr::tr("Copy file"));
+}
+
+CopyDirectoryStepFactory::CopyDirectoryStepFactory()
+{
+ registerStep<CopyDirectoryStep>(Constants::COPY_DIRECTORY_STEP);
+ //: Default CopyStep display name
+ setDisplayName(Tr::tr("Copy directory recursively"));
+}
+
+} // ProjectExplorer::Internal
diff --git a/src/plugins/projectexplorer/copystep.h b/src/plugins/projectexplorer/copystep.h
new file mode 100644
index 0000000000..07940f3a89
--- /dev/null
+++ b/src/plugins/projectexplorer/copystep.h
@@ -0,0 +1,22 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "buildstep.h"
+
+namespace ProjectExplorer::Internal {
+
+class CopyFileStepFactory final : public BuildStepFactory
+{
+public:
+ CopyFileStepFactory();
+};
+
+class CopyDirectoryStepFactory final : public BuildStepFactory
+{
+public:
+ CopyDirectoryStepFactory();
+};
+
+} // ProjectExplorer::Internal
diff --git a/src/plugins/projectexplorer/currentprojectfilter.cpp b/src/plugins/projectexplorer/currentprojectfilter.cpp
index 69b88ddf46..d0f9ed99b0 100644
--- a/src/plugins/projectexplorer/currentprojectfilter.cpp
+++ b/src/plugins/projectexplorer/currentprojectfilter.cpp
@@ -7,42 +7,28 @@
#include "projectexplorertr.h"
#include "projecttree.h"
-#include <utils/algorithm.h>
-
using namespace Core;
using namespace ProjectExplorer;
using namespace ProjectExplorer::Internal;
+using namespace Utils;
CurrentProjectFilter::CurrentProjectFilter()
- : BaseFileFilter()
{
setId("Files in current project");
setDisplayName(Tr::tr("Files in Current Project"));
- setDescription(Tr::tr("Matches all files from the current document's project. Append \"+<number>\" "
- "or \":<number>\" to jump to the given line number. Append another "
- "\"+<number>\" or \":<number>\" to jump to the column number as well."));
+ setDescription(Tr::tr("Locates files from the current document's project. Append \"+<number>\" "
+ "or \":<number>\" to jump to the given line number. Append another "
+ "\"+<number>\" or \":<number>\" to jump to the column number as well."));
setDefaultShortcutString("p");
- setDefaultIncludedByDefault(false);
+ setRefreshRecipe(Tasking::Sync([this] { invalidate(); }));
connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged,
this, &CurrentProjectFilter::currentProjectChanged);
-}
-
-void CurrentProjectFilter::markFilesAsOutOfDate()
-{
- setFileIterator(nullptr);
-}
-void CurrentProjectFilter::prepareSearch(const QString &entry)
-{
- Q_UNUSED(entry)
- if (!fileIterator()) {
- Utils::FilePaths paths;
- if (m_project)
- paths = m_project->files(Project::SourceFiles);
- setFileIterator(new BaseFileFilter::ListIterator(paths));
- }
- BaseFileFilter::prepareSearch(entry);
+ m_cache.setGeneratorProvider([this] {
+ const FilePaths paths = m_project ? m_project->files(Project::SourceFiles) : FilePaths();
+ return LocatorFileCache::filePathsGenerator(paths);
+ });
}
void CurrentProjectFilter::currentProjectChanged()
@@ -50,21 +36,12 @@ void CurrentProjectFilter::currentProjectChanged()
Project *project = ProjectTree::currentProject();
if (project == m_project)
return;
- if (m_project)
- disconnect(m_project, &Project::fileListChanged,
- this, &CurrentProjectFilter::markFilesAsOutOfDate);
-
- if (project)
- connect(project, &Project::fileListChanged,
- this, &CurrentProjectFilter::markFilesAsOutOfDate);
+ if (m_project)
+ disconnect(m_project, &Project::fileListChanged, this, &CurrentProjectFilter::invalidate);
m_project = project;
- markFilesAsOutOfDate();
-}
+ if (m_project)
+ connect(m_project, &Project::fileListChanged, this, &CurrentProjectFilter::invalidate);
-void CurrentProjectFilter::refresh(QFutureInterface<void> &future)
-{
- Q_UNUSED(future)
- QMetaObject::invokeMethod(this, &CurrentProjectFilter::markFilesAsOutOfDate,
- Qt::QueuedConnection);
+ invalidate();
}
diff --git a/src/plugins/projectexplorer/currentprojectfilter.h b/src/plugins/projectexplorer/currentprojectfilter.h
index b9d63db293..6c50070790 100644
--- a/src/plugins/projectexplorer/currentprojectfilter.h
+++ b/src/plugins/projectexplorer/currentprojectfilter.h
@@ -3,31 +3,24 @@
#pragma once
-#include <coreplugin/locator/basefilefilter.h>
+#include <coreplugin/locator/ilocatorfilter.h>
-#include <QFutureInterface>
+namespace ProjectExplorer { class Project; }
-namespace ProjectExplorer {
+namespace ProjectExplorer::Internal {
-class Project;
-
-namespace Internal {
-
-class CurrentProjectFilter : public Core::BaseFileFilter
+class CurrentProjectFilter : public Core::ILocatorFilter
{
- Q_OBJECT
-
public:
CurrentProjectFilter();
- void refresh(QFutureInterface<void> &future) override;
- void prepareSearch(const QString &entry) override;
private:
+ Core::LocatorMatcherTasks matchers() final { return {m_cache.matcher()}; }
void currentProjectChanged();
- void markFilesAsOutOfDate();
+ void invalidate() { m_cache.invalidate(); }
+ Core::LocatorFileCache m_cache;
Project *m_project = nullptr;
};
-} // namespace Internal
-} // namespace ProjectExplorer
+} // namespace ProjectExplorer::Internal
diff --git a/src/plugins/projectexplorer/currentprojectfind.cpp b/src/plugins/projectexplorer/currentprojectfind.cpp
index 7ecbd99069..3bde2bb62f 100644
--- a/src/plugins/projectexplorer/currentprojectfind.cpp
+++ b/src/plugins/projectexplorer/currentprojectfind.cpp
@@ -5,8 +5,8 @@
#include "project.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "projecttree.h"
-#include "session.h"
#include <utils/qtcassert.h>
#include <utils/filesearch.h>
@@ -23,7 +23,7 @@ CurrentProjectFind::CurrentProjectFind()
{
connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged,
this, &CurrentProjectFind::handleProjectChanged);
- connect(SessionManager::instance(), &SessionManager::projectDisplayNameChanged,
+ connect(ProjectManager::instance(), &ProjectManager::projectDisplayNameChanged,
this, [this](ProjectExplorer::Project *p) {
if (p == ProjectTree::currentProject())
emit displayNameChanged();
@@ -61,14 +61,13 @@ FileIterator *CurrentProjectFind::files(const QStringList &nameFilters,
const QStringList &exclusionFilters,
const QVariant &additionalParameters) const
{
- QTC_ASSERT(additionalParameters.isValid(),
- return new FileListIterator(FilePaths(), QList<QTextCodec *>()));
+ QTC_ASSERT(additionalParameters.isValid(), return new FileListIterator);
const FilePath projectFile = FilePath::fromVariant(additionalParameters);
- for (Project *project : SessionManager::projects()) {
+ for (Project *project : ProjectManager::projects()) {
if (project && projectFile == project->projectFilePath())
return filesForProjects(nameFilters, exclusionFilters, {project});
}
- return new FileListIterator(FilePaths(), QList<QTextCodec *>());
+ return new FileListIterator;
}
QString CurrentProjectFind::label() const
@@ -87,7 +86,7 @@ void CurrentProjectFind::handleProjectChanged()
void CurrentProjectFind::recheckEnabled(Core::SearchResult *search)
{
const FilePath projectFile = FilePath::fromVariant(getAdditionalParameters(search));
- for (Project *project : SessionManager::projects()) {
+ for (Project *project : ProjectManager::projects()) {
if (projectFile == project->projectFilePath()) {
search->setSearchAgainEnabled(true);
return;
diff --git a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp
index 9d25bb7dfe..00f7225d0d 100644
--- a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp
+++ b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp
@@ -3,7 +3,6 @@
#include "customexecutablerunconfiguration.h"
-#include "localenvironmentaspect.h"
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
#include "runconfigurationaspects.h"
@@ -24,7 +23,8 @@ CustomExecutableRunConfiguration::CustomExecutableRunConfiguration(Target *targe
CustomExecutableRunConfiguration::CustomExecutableRunConfiguration(Target *target, Id id)
: RunConfiguration(target, id)
{
- auto envAspect = addAspect<LocalEnvironmentAspect>(target);
+ auto envAspect = addAspect<EnvironmentAspect>();
+ envAspect->setSupportForBuildEnvironment(target);
auto exeAspect = addAspect<ExecutableAspect>(target, ExecutableAspect::HostDevice);
exeAspect->setSettingsKey("ProjectExplorer.CustomExecutableRunConfiguration.Executable");
diff --git a/src/plugins/projectexplorer/customparserconfigdialog.cpp b/src/plugins/projectexplorer/customparserconfigdialog.cpp
index 4ebc880c14..cbfd1adfbc 100644
--- a/src/plugins/projectexplorer/customparserconfigdialog.cpp
+++ b/src/plugins/projectexplorer/customparserconfigdialog.cpp
@@ -101,7 +101,7 @@ CustomParserConfigDialog::CustomParserConfigDialog(QWidget *parent)
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
- using namespace Utils::Layouting;
+ using namespace Layouting;
auto tabWarning = new QWidget;
Column {
diff --git a/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp b/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp
index f2b33378b5..7d1174839d 100644
--- a/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp
+++ b/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp
@@ -8,7 +8,7 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/temporarydirectory.h>
#include <QFileInfo>
@@ -64,7 +64,7 @@ static bool
const QMap<QString, QString> &fieldMap,
QString *stdOut /* = 0 */, QString *errorMessage)
{
- Utils::QtcProcess process;
+ Utils::Process process;
const QString binary = script.front();
QStringList arguments;
const int binarySize = script.size();
diff --git a/src/plugins/projectexplorer/dependenciespanel.cpp b/src/plugins/projectexplorer/dependenciespanel.cpp
index 1799126a5b..daac52b8d7 100644
--- a/src/plugins/projectexplorer/dependenciespanel.cpp
+++ b/src/plugins/projectexplorer/dependenciespanel.cpp
@@ -5,9 +5,10 @@
#include "project.h"
#include "projectexplorertr.h"
-#include "session.h"
+#include "projectmanager.h"
#include <coreplugin/icore.h>
+#include <coreplugin/session.h>
#include <utils/algorithm.h>
#include <utils/detailswidget.h>
@@ -32,19 +33,18 @@ DependenciesModel::DependenciesModel(Project *project, QObject *parent)
{
resetModel();
- SessionManager *sessionManager = SessionManager::instance();
- connect(sessionManager, &SessionManager::projectRemoved,
+ connect(ProjectManager::instance(), &ProjectManager::projectRemoved,
this, &DependenciesModel::resetModel);
- connect(sessionManager, &SessionManager::projectAdded,
+ connect(ProjectManager::instance(), &ProjectManager::projectAdded,
this, &DependenciesModel::resetModel);
- connect(sessionManager, &SessionManager::sessionLoaded,
+ connect(Core::SessionManager::instance(), &Core::SessionManager::sessionLoaded,
this, &DependenciesModel::resetModel);
}
void DependenciesModel::resetModel()
{
beginResetModel();
- m_projects = SessionManager::projects();
+ m_projects = ProjectManager::projects();
m_projects.removeAll(m_project);
Utils::sort(m_projects, [](Project *a, Project *b) {
return a->displayName() < b->displayName();
@@ -77,7 +77,7 @@ QVariant DependenciesModel::data(const QModelIndex &index, int role) const
case Qt::ToolTipRole:
return p->projectFilePath().toUserOutput();
case Qt::CheckStateRole:
- return SessionManager::hasDependency(m_project, p) ? Qt::Checked : Qt::Unchecked;
+ return ProjectManager::hasDependency(m_project, p) ? Qt::Checked : Qt::Unchecked;
case Qt::DecorationRole:
return Utils::FileIconProvider::icon(p->projectFilePath());
default:
@@ -92,7 +92,7 @@ bool DependenciesModel::setData(const QModelIndex &index, const QVariant &value,
const auto c = static_cast<Qt::CheckState>(value.toInt());
if (c == Qt::Checked) {
- if (SessionManager::addDependency(m_project, p)) {
+ if (ProjectManager::addDependency(m_project, p)) {
emit dataChanged(index, index);
return true;
} else {
@@ -100,8 +100,8 @@ bool DependenciesModel::setData(const QModelIndex &index, const QVariant &value,
Tr::tr("This would create a circular dependency."));
}
} else if (c == Qt::Unchecked) {
- if (SessionManager::hasDependency(m_project, p)) {
- SessionManager::removeDependency(m_project, p);
+ if (ProjectManager::hasDependency(m_project, p)) {
+ ProjectManager::removeDependency(m_project, p);
emit dataChanged(index, index);
return true;
}
@@ -215,9 +215,9 @@ DependenciesWidget::DependenciesWidget(Project *project, QWidget *parent) : Proj
m_cascadeSetActiveCheckBox = new QCheckBox;
m_cascadeSetActiveCheckBox->setText(Tr::tr("Synchronize configuration"));
m_cascadeSetActiveCheckBox->setToolTip(Tr::tr("Synchronize active kit, build, and deploy configuration between projects."));
- m_cascadeSetActiveCheckBox->setChecked(SessionManager::isProjectConfigurationCascading());
+ m_cascadeSetActiveCheckBox->setChecked(ProjectManager::isProjectConfigurationCascading());
connect(m_cascadeSetActiveCheckBox, &QCheckBox::toggled,
- SessionManager::instance(), &SessionManager::setProjectConfigurationCascading);
+ ProjectManager::instance(), &ProjectManager::setProjectConfigurationCascading);
layout->addWidget(m_cascadeSetActiveCheckBox, 1, 0, 2, 1);
}
diff --git a/src/plugins/projectexplorer/desktoprunconfiguration.cpp b/src/plugins/projectexplorer/desktoprunconfiguration.cpp
index 82d0123b11..8b8cdf2b79 100644
--- a/src/plugins/projectexplorer/desktoprunconfiguration.cpp
+++ b/src/plugins/projectexplorer/desktoprunconfiguration.cpp
@@ -4,7 +4,6 @@
#include "desktoprunconfiguration.h"
#include "buildsystem.h"
-#include "localenvironmentaspect.h"
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
#include "runconfigurationaspects.h"
@@ -43,7 +42,8 @@ private:
DesktopRunConfiguration::DesktopRunConfiguration(Target *target, Id id, Kind kind)
: RunConfiguration(target, id), m_kind(kind)
{
- auto envAspect = addAspect<LocalEnvironmentAspect>(target);
+ auto envAspect = addAspect<EnvironmentAspect>();
+ envAspect->setSupportForBuildEnvironment(target);
addAspect<ExecutableAspect>(target, ExecutableAspect::RunDevice);
addAspect<ArgumentsAspect>(macroExpander());
@@ -87,7 +87,8 @@ void DesktopRunConfiguration::updateTargetInformation()
BuildTargetInfo bti = buildTargetInfo();
auto terminalAspect = aspect<TerminalAspect>();
- terminalAspect->setUseTerminalHint(bti.usesTerminal);
+ terminalAspect->setUseTerminalHint(bti.targetFilePath.needsDevice() ? false : bti.usesTerminal);
+ terminalAspect->setEnabled(!bti.targetFilePath.needsDevice());
if (m_kind == Qmake) {
@@ -121,7 +122,7 @@ void DesktopRunConfiguration::updateTargetInformation()
aspect<ExecutableAspect>()->setExecutable(bti.targetFilePath);
aspect<WorkingDirectoryAspect>()->setDefaultWorkingDirectory(bti.workingDirectory);
- emit aspect<LocalEnvironmentAspect>()->environmentChanged();
+ emit aspect<EnvironmentAspect>()->environmentChanged();
}
}
diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp
index a09c8bac76..099040d6b2 100644
--- a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp
+++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp
@@ -3,11 +3,11 @@
#include "desktopdevice.h"
-#include "desktopprocesssignaloperation.h"
-#include "deviceprocesslist.h"
-#include "localprocesslist.h"
#include "../projectexplorerconstants.h"
#include "../projectexplorertr.h"
+#include "desktopprocesssignaloperation.h"
+#include "deviceprocesslist.h"
+#include "processlist.h"
#include <coreplugin/fileutils.h>
@@ -15,18 +15,19 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/portlist.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/terminalcommand.h>
+#include <utils/terminalhooks.h>
#include <utils/url.h>
#include <QCoreApplication>
#include <QDateTime>
#ifdef Q_OS_WIN
-#include <windows.h>
-#include <stdlib.h>
#include <cstring>
+#include <stdlib.h>
+#include <windows.h>
#endif
using namespace ProjectExplorer::Constants;
@@ -34,58 +35,11 @@ using namespace Utils;
namespace ProjectExplorer {
-static void startTerminalEmulator(const QString &workingDir, const Environment &env)
-{
-#ifdef Q_OS_WIN
- STARTUPINFO si;
- ZeroMemory(&si, sizeof(si));
- si.cb = sizeof(si);
-
- PROCESS_INFORMATION pinfo;
- ZeroMemory(&pinfo, sizeof(pinfo));
-
- static const auto quoteWinCommand = [](const QString &program) {
- const QChar doubleQuote = QLatin1Char('"');
-
- // add the program as the first arg ... it works better
- QString programName = program;
- programName.replace(QLatin1Char('/'), QLatin1Char('\\'));
- if (!programName.startsWith(doubleQuote) && !programName.endsWith(doubleQuote)
- && programName.contains(QLatin1Char(' '))) {
- programName.prepend(doubleQuote);
- programName.append(doubleQuote);
- }
- return programName;
- };
- const QString cmdLine = quoteWinCommand(qtcEnvironmentVariable("COMSPEC"));
- // cmdLine is assumed to be detached -
- // https://blogs.msdn.microsoft.com/oldnewthing/20090601-00/?p=18083
-
- const QString totalEnvironment = env.toStringList().join(QChar(QChar::Null)) + QChar(QChar::Null);
- LPVOID envPtr = (env != Environment::systemEnvironment())
- ? (WCHAR *)(totalEnvironment.utf16()) : nullptr;
-
- const bool success = CreateProcessW(0, (WCHAR *)cmdLine.utf16(),
- 0, 0, FALSE, CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT,
- envPtr, workingDir.isEmpty() ? 0 : (WCHAR *)workingDir.utf16(),
- &si, &pinfo);
-
- if (success) {
- CloseHandle(pinfo.hThread);
- CloseHandle(pinfo.hProcess);
- }
-#else
- const TerminalCommand term = TerminalCommand::terminalEmulator();
- QProcess process;
- process.setProgram(term.command.nativePath());
- process.setArguments(ProcessArgs::splitArgs(term.openArgs));
- process.setProcessEnvironment(env.toProcessEnvironment());
- process.setWorkingDirectory(workingDir);
- process.startDetached();
-#endif
-}
+class DesktopDevicePrivate : public QObject
+{};
DesktopDevice::DesktopDevice()
+ : d(new DesktopDevicePrivate())
{
setFileAccess(DesktopDeviceFileAccess::instance());
@@ -98,20 +52,26 @@ DesktopDevice::DesktopDevice()
setMachineType(IDevice::Hardware);
setOsType(HostOsInfo::hostOs());
- const QString portRange =
- QString::fromLatin1("%1-%2").arg(DESKTOP_PORT_START).arg(DESKTOP_PORT_END);
+ const QString portRange
+ = QString::fromLatin1("%1-%2").arg(DESKTOP_PORT_START).arg(DESKTOP_PORT_END);
setFreePorts(Utils::PortList::fromString(portRange));
setOpenTerminal([](const Environment &env, const FilePath &path) {
- const QFileInfo fileInfo = path.toFileInfo();
- const QString workingDir = QDir::toNativeSeparators(fileInfo.isDir() ?
- fileInfo.absoluteFilePath() :
- fileInfo.absolutePath());
const Environment realEnv = env.hasChanges() ? env : Environment::systemEnvironment();
- startTerminalEmulator(workingDir, realEnv);
+
+ const FilePath shell = Terminal::defaultShellForDevice(path);
+
+ Process process;
+ process.setTerminalMode(TerminalMode::Detached);
+ process.setEnvironment(realEnv);
+ process.setCommand({shell, {}});
+ process.setWorkingDirectory(path);
+ process.start();
});
}
+DesktopDevice::~DesktopDevice() = default;
+
IDevice::DeviceInfo DesktopDevice::deviceInformation() const
{
return DeviceInfo();
@@ -125,11 +85,6 @@ IDeviceWidget *DesktopDevice::createWidget()
// range can be confusing to the user. Hence, disabling the widget for now.
}
-bool DesktopDevice::canAutoDetectPorts() const
-{
- return true;
-}
-
bool DesktopDevice::canCreateProcessModel() const
{
return true;
@@ -137,7 +92,7 @@ bool DesktopDevice::canCreateProcessModel() const
DeviceProcessList *DesktopDevice::createProcessListModel(QObject *parent) const
{
- return new Internal::LocalProcessList(sharedFromThis(), parent);
+ return new ProcessList(sharedFromThis(), parent);
}
DeviceProcessSignalOperation::Ptr DesktopDevice::signalOperation() const
@@ -145,31 +100,6 @@ DeviceProcessSignalOperation::Ptr DesktopDevice::signalOperation() const
return DeviceProcessSignalOperation::Ptr(new DesktopProcessSignalOperation());
}
-PortsGatheringMethod DesktopDevice::portsGatheringMethod() const
-{
- return {
- [this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine {
- // We might encounter the situation that protocol is given IPv6
- // but the consumer of the free port information decides to open
- // an IPv4(only) port. As a result the next IPv6 scan will
- // report the port again as open (in IPv6 namespace), while the
- // same port in IPv4 namespace might still be blocked, and
- // re-use of this port fails.
- // GDBserver behaves exactly like this.
-
- Q_UNUSED(protocol)
-
- if (HostOsInfo::isWindowsHost() || HostOsInfo::isMacHost())
- return {filePath("netstat"), {"-a", "-n"}};
- if (HostOsInfo::isLinuxHost())
- return {filePath("/bin/sh"), {"-c", "cat /proc/net/tcp*"}};
- return {};
- },
-
- &Port::parseFromNetstatOutput
- };
-}
-
QUrl DesktopDevice::toolControlChannel(const ControlChannelHint &) const
{
QUrl url;
diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.h b/src/plugins/projectexplorer/devicesupport/desktopdevice.h
index ee5ac1ca5e..d9cafbfda9 100644
--- a/src/plugins/projectexplorer/devicesupport/desktopdevice.h
+++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.h
@@ -6,25 +6,27 @@
#include "../projectexplorer_export.h"
#include "idevice.h"
-#include "idevicefactory.h"
#include <QApplication>
+#include <memory>
+
namespace ProjectExplorer {
class ProjectExplorerPlugin;
+class DesktopDevicePrivate;
namespace Internal { class DesktopDeviceFactory; }
class PROJECTEXPLORER_EXPORT DesktopDevice : public IDevice
{
public:
+ ~DesktopDevice() override;
+
IDevice::DeviceInfo deviceInformation() const override;
IDeviceWidget *createWidget() override;
- bool canAutoDetectPorts() const override;
bool canCreateProcessModel() const override;
DeviceProcessList *createProcessListModel(QObject *parent) const override;
- ProjectExplorer::PortsGatheringMethod portsGatheringMethod() const override;
DeviceProcessSignalOperation::Ptr signalOperation() const override;
QUrl toolControlChannel(const ControlChannelHint &) const override;
bool usableAsBuildDevice() const override;
@@ -40,6 +42,8 @@ protected:
friend class ProjectExplorerPlugin;
friend class Internal::DesktopDeviceFactory;
+
+ std::unique_ptr<DesktopDevicePrivate> d;
};
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp b/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp
index acce8bf29b..84e3b27408 100644
--- a/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp
@@ -4,6 +4,7 @@
#include "devicecheckbuildstep.h"
#include "../kitinformation.h"
+#include "../projectexplorerconstants.h"
#include "../projectexplorertr.h"
#include "devicemanager.h"
@@ -12,60 +13,61 @@
#include <QMessageBox>
-using namespace ProjectExplorer;
+namespace ProjectExplorer {
-DeviceCheckBuildStep::DeviceCheckBuildStep(BuildStepList *bsl, Utils::Id id)
- : BuildStep(bsl, id)
+class DeviceCheckBuildStep : public BuildStep
{
- setWidgetExpandedByDefault(false);
-}
+public:
+ DeviceCheckBuildStep(BuildStepList *bsl, Utils::Id id)
+ : BuildStep(bsl, id)
+ {
+ setWidgetExpandedByDefault(false);
+ }
-bool DeviceCheckBuildStep::init()
-{
- IDevice::ConstPtr device = DeviceKitAspect::device(kit());
- if (!device) {
- Utils::Id deviceTypeId = DeviceTypeKitAspect::deviceTypeId(kit());
- IDeviceFactory *factory = IDeviceFactory::find(deviceTypeId);
- if (!factory || !factory->canCreate()) {
- emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage);
- return false;
- }
+ bool init() override
+ {
+ IDevice::ConstPtr device = DeviceKitAspect::device(kit());
+ if (!device) {
+ Utils::Id deviceTypeId = DeviceTypeKitAspect::deviceTypeId(kit());
+ IDeviceFactory *factory = IDeviceFactory::find(deviceTypeId);
+ if (!factory || !factory->canCreate()) {
+ emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage);
+ return false;
+ }
- QMessageBox msgBox(QMessageBox::Question, Tr::tr("Set Up Device"),
- Tr::tr("There is no device set up for this kit. Do you want to add a device?"),
- QMessageBox::Yes|QMessageBox::No);
- msgBox.setDefaultButton(QMessageBox::Yes);
- if (msgBox.exec() == QMessageBox::No) {
- emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage);
- return false;
- }
+ QMessageBox msgBox(QMessageBox::Question, Tr::tr("Set Up Device"),
+ Tr::tr("There is no device set up for this kit. Do you want to add a device?"),
+ QMessageBox::Yes|QMessageBox::No);
+ msgBox.setDefaultButton(QMessageBox::Yes);
+ if (msgBox.exec() == QMessageBox::No) {
+ emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage);
+ return false;
+ }
- IDevice::Ptr newDevice = factory->create();
- if (newDevice.isNull()) {
- emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage);
- return false;
- }
+ IDevice::Ptr newDevice = factory->create();
+ if (newDevice.isNull()) {
+ emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage);
+ return false;
+ }
- DeviceManager *dm = DeviceManager::instance();
- dm->addDevice(newDevice);
+ DeviceManager *dm = DeviceManager::instance();
+ dm->addDevice(newDevice);
- DeviceKitAspect::setDevice(kit(), newDevice);
+ DeviceKitAspect::setDevice(kit(), newDevice);
+ }
+
+ return true;
}
- return true;
-}
+ void doRun() override { emit finished(true); }
+};
-void DeviceCheckBuildStep::doRun()
-{
- emit finished(true);
-}
+// Factory
-Utils::Id DeviceCheckBuildStep::stepId()
+DeviceCheckBuildStepFactory::DeviceCheckBuildStepFactory()
{
- return "ProjectExplorer.DeviceCheckBuildStep";
+ registerStep<DeviceCheckBuildStep>(Constants::DEVICE_CHECK_STEP);
+ setDisplayName(Tr::tr("Check for a configured device"));
}
-QString DeviceCheckBuildStep::displayName()
-{
- return Tr::tr("Check for a configured device");
-}
+} // ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.h b/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.h
index 8b7d3687b0..6b7a8edb11 100644
--- a/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.h
+++ b/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.h
@@ -8,18 +8,10 @@
namespace ProjectExplorer {
-class PROJECTEXPLORER_EXPORT DeviceCheckBuildStep : public BuildStep
+class PROJECTEXPLORER_EXPORT DeviceCheckBuildStepFactory : public BuildStepFactory
{
- Q_OBJECT
-
public:
- DeviceCheckBuildStep(BuildStepList *bsl, Utils::Id id);
-
- bool init() override;
- void doRun() override;
-
- static Utils::Id stepId();
- static QString displayName();
+ DeviceCheckBuildStepFactory();
};
-} // namespace ProjectExplorer
+} // ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.cpp b/src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.cpp
index ed47999432..987f2e0c39 100644
--- a/src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.cpp
@@ -24,7 +24,7 @@ DeviceFactorySelectionDialog::DeviceFactorySelectionDialog(QWidget *parent) :
m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
m_buttonBox->button(QDialogButtonBox::Ok)->setText(Tr::tr("Start Wizard"));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Tr::tr("Available device types:"),
m_listWidget,
diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
index 9f8e0594fd..aa017df46b 100644
--- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
@@ -14,9 +14,10 @@
#include <utils/environment.h>
#include <utils/fsengine/fsengine.h>
#include <utils/persistentsettings.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
+#include <utils/terminalhooks.h>
#include <QHash>
#include <QMutex>
@@ -442,6 +443,13 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager
device->openTerminal(env, filePath);
};
+ deviceHooks.osType = [](const FilePath &filePath) {
+ auto device = DeviceManager::deviceForPath(filePath);
+ if (!device)
+ return OsTypeLinux;
+ return device->osType();
+ };
+
DeviceProcessHooks processHooks;
processHooks.processImplHook = [](const FilePath &filePath) -> ProcessInterface * {
@@ -456,7 +464,23 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager
return device->systemEnvironment();
};
- QtcProcess::setRemoteProcessHooks(processHooks);
+ Process::setRemoteProcessHooks(processHooks);
+
+ Terminal::Hooks::instance().getTerminalCommandsForDevicesHook().set(
+ [this]() -> QList<Terminal::NameAndCommandLine> {
+ QList<Terminal::NameAndCommandLine> result;
+ for (const IDevice::ConstPtr device : d->devices) {
+ if (device->type() == Constants::DESKTOP_DEVICE_TYPE)
+ continue;
+
+ const FilePath shell = Terminal::defaultShellForDevice(device->rootPath());
+
+ if (!shell.isEmpty())
+ result << Terminal::NameAndCommandLine{device->displayName(),
+ CommandLine{shell, {}}};
+ }
+ return result;
+ });
}
DeviceManager::~DeviceManager()
diff --git a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp
index 39af471345..a35387a3c2 100644
--- a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp
@@ -17,13 +17,16 @@
#include <coreplugin/icore.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/layoutbuilder.h>
+#include <utils/optionpushbutton.h>
#include <utils/qtcassert.h>
#include <QComboBox>
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
+#include <QMenu>
#include <QPushButton>
#include <QScrollArea>
#include <QTextStream>
@@ -100,21 +103,47 @@ void DeviceSettingsWidget::initGui()
m_deviceStateTextLabel = new QLabel;
m_osSpecificGroupBox = new QGroupBox(Tr::tr("Type Specific"));
m_osSpecificGroupBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
- m_addConfigButton = new QPushButton(Tr::tr("&Add..."));
m_removeConfigButton = new QPushButton(Tr::tr("&Remove"));
m_defaultDeviceButton = new QPushButton(Tr::tr("Set As Default"));
- auto line = new QFrame;
- line->setFrameShape(QFrame::HLine);
- line->setFrameShadow(QFrame::Sunken);
- auto customButtonsContainer = new QWidget;
- m_buttonsLayout = new QVBoxLayout(customButtonsContainer);
+
+ OptionPushButton *addButton = new OptionPushButton(Tr::tr("&Add..."));
+ connect(addButton, &OptionPushButton::clicked, this, &DeviceSettingsWidget::addDevice);
+
+ QMenu *deviceTypeMenu = new QMenu(addButton);
+ QAction *defaultAction = new QAction(Tr::tr("&Start Wizard to Add Device..."));
+ connect(defaultAction, &QAction::triggered, this, &DeviceSettingsWidget::addDevice);
+ deviceTypeMenu->addAction(defaultAction);
+ deviceTypeMenu->addSeparator();
+
+ for (IDeviceFactory *factory : IDeviceFactory::allDeviceFactories()) {
+ if (!factory->canCreate())
+ continue;
+ if (!factory->quickCreationAllowed())
+ continue;
+
+ QAction *action = new QAction(Tr::tr("Add %1").arg(factory->displayName()));
+ deviceTypeMenu->addAction(action);
+
+ connect(action, &QAction::triggered, this, [factory, this] {
+ IDevice::Ptr device = factory->construct();
+ QTC_ASSERT(device, return);
+ m_deviceManager->addDevice(device);
+ m_removeConfigButton->setEnabled(true);
+ m_configurationComboBox->setCurrentIndex(m_deviceManagerModel->indexOf(device));
+ saveSettings();
+ });
+ }
+
+ addButton->setOptionalMenu(deviceTypeMenu);
+
+ m_buttonsLayout = new QVBoxLayout;
m_buttonsLayout->setContentsMargins({});
auto scrollAreaWidget = new QWidget;
auto scrollArea = new QScrollArea;
scrollArea->setWidgetResizable(true);
scrollArea->setWidget(scrollAreaWidget);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_generalGroupBox,
m_osSpecificGroupBox,
@@ -127,25 +156,27 @@ void DeviceSettingsWidget::initGui()
Tr::tr("Current state:"), Row { m_deviceStateIconLabel, m_deviceStateTextLabel, st, }, br,
}.attachTo(m_generalGroupBox);
+ // clang-format off
Row {
Column {
Form { m_configurationLabel, m_configurationComboBox, br, },
scrollArea,
},
Column {
- m_addConfigButton,
+ addButton,
+ Space(30),
m_removeConfigButton,
m_defaultDeviceButton,
- line,
- customButtonsContainer,
+ m_buttonsLayout,
st,
},
}.attachTo(this);
+ // clang-format on
bool hasDeviceFactories = Utils::anyOf(IDeviceFactory::allDeviceFactories(),
&IDeviceFactory::canCreate);
- m_addConfigButton->setEnabled(hasDeviceFactories);
+ addButton->setEnabled(hasDeviceFactories);
int lastIndex = ICore::settings()
->value(QLatin1String(LastDeviceIndexKey), 0).toInt();
@@ -160,10 +191,10 @@ void DeviceSettingsWidget::initGui()
this, &DeviceSettingsWidget::setDefaultDevice);
connect(m_removeConfigButton, &QAbstractButton::clicked,
this, &DeviceSettingsWidget::removeDevice);
- connect(m_nameLineEdit, &QLineEdit::editingFinished,
- this, &DeviceSettingsWidget::deviceNameEditingFinished);
- connect(m_addConfigButton, &QAbstractButton::clicked,
- this, &DeviceSettingsWidget::addDevice);
+ connect(m_nameLineEdit,
+ &QLineEdit::editingFinished,
+ this,
+ &DeviceSettingsWidget::deviceNameEditingFinished);
}
void DeviceSettingsWidget::addDevice()
@@ -182,6 +213,8 @@ void DeviceSettingsWidget::addDevice()
if (device.isNull())
return;
+ Utils::asyncRun([device] { device->checkOsType(); });
+
m_deviceManager->addDevice(device);
m_removeConfigButton->setEnabled(true);
m_configurationComboBox->setCurrentIndex(m_deviceManagerModel->indexOf(device));
diff --git a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h
index 5cd5cec85e..207b60cf74 100644
--- a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h
+++ b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h
@@ -76,7 +76,6 @@ private:
QLabel *m_deviceStateIconLabel;
QLabel *m_deviceStateTextLabel;
QGroupBox *m_osSpecificGroupBox;
- QPushButton *m_addConfigButton;
QPushButton *m_removeConfigButton;
QPushButton *m_defaultDeviceButton;
QVBoxLayout *m_buttonsLayout;
diff --git a/src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp b/src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp
index 91a83531b4..0277fc827a 100644
--- a/src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp
@@ -43,7 +43,7 @@ DeviceTestDialog::DeviceTestDialog(const IDevice::Ptr &deviceConfiguration,
d->textEdit->setReadOnly(true);
d->buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
d->textEdit,
d->buttonBox,
diff --git a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp
index 8081c9745d..c3f3f195ca 100644
--- a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp
+++ b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp
@@ -9,8 +9,8 @@
#include <utils/port.h>
#include <utils/portlist.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/url.h>
@@ -22,7 +22,7 @@ namespace Internal {
class DeviceUsedPortsGathererPrivate
{
public:
- std::unique_ptr<QtcProcess> process;
+ std::unique_ptr<Process> process;
QList<Port> usedPorts;
IDevice::ConstPtr device;
PortsGatheringMethod portsGatheringMethod;
@@ -54,10 +54,10 @@ void DeviceUsedPortsGatherer::start()
const QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::AnyIPProtocol;
- d->process.reset(new QtcProcess);
+ d->process.reset(new Process);
d->process->setCommand(d->portsGatheringMethod.commandLine(protocol));
- connect(d->process.get(), &QtcProcess::done, this, &DeviceUsedPortsGatherer::handleProcessDone);
+ connect(d->process.get(), &Process::done, this, &DeviceUsedPortsGatherer::handleProcessDone);
d->process->start();
}
@@ -116,12 +116,6 @@ void DeviceUsedPortsGatherer::handleProcessDone()
stop();
}
-DeviceUsedPortsGathererAdapter::DeviceUsedPortsGathererAdapter()
-{
- connect(task(), &DeviceUsedPortsGatherer::portListReady, this, [this] { emit done(true); });
- connect(task(), &DeviceUsedPortsGatherer::error, this, [this] { emit done(false); });
-}
-
// PortGatherer
PortsGatherer::PortsGatherer(RunControl *runControl)
diff --git a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h
index 3519c7d22f..20b0c9c9e8 100644
--- a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h
+++ b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h
@@ -7,8 +7,9 @@
#include <projectexplorer/runcontrol.h>
+#include <solutions/tasking/tasktree.h>
+
#include <utils/portlist.h>
-#include <utils/tasktree.h>
namespace ProjectExplorer {
@@ -43,11 +44,14 @@ private:
Internal::DeviceUsedPortsGathererPrivate * const d;
};
-class PROJECTEXPLORER_EXPORT DeviceUsedPortsGathererAdapter
- : public Utils::Tasking::TaskAdapter<DeviceUsedPortsGatherer>
+class PROJECTEXPLORER_EXPORT DeviceUsedPortsGathererTaskAdapter
+ : public Tasking::TaskAdapter<DeviceUsedPortsGatherer>
{
public:
- DeviceUsedPortsGathererAdapter();
+ DeviceUsedPortsGathererTaskAdapter() {
+ connect(task(), &DeviceUsedPortsGatherer::portListReady, this, [this] { emit done(true); });
+ connect(task(), &DeviceUsedPortsGatherer::error, this, [this] { emit done(false); });
+ }
void start() final { task()->start(); }
};
@@ -85,4 +89,5 @@ private:
} // namespace ProjectExplorer
-QTC_DECLARE_CUSTOM_TASK(PortGatherer, ProjectExplorer::DeviceUsedPortsGathererAdapter);
+TASKING_DECLARE_TASK(DeviceUsedPortsGathererTask,
+ ProjectExplorer::DeviceUsedPortsGathererTaskAdapter);
diff --git a/src/plugins/projectexplorer/devicesupport/filetransfer.cpp b/src/plugins/projectexplorer/devicesupport/filetransfer.cpp
index bd386db0d1..0581579767 100644
--- a/src/plugins/projectexplorer/devicesupport/filetransfer.cpp
+++ b/src/plugins/projectexplorer/devicesupport/filetransfer.cpp
@@ -16,43 +16,18 @@ using namespace Utils;
namespace ProjectExplorer {
-FileTransferDirection FileToTransfer::direction() const
-{
- if (m_source.needsDevice() == m_target.needsDevice())
- return FileTransferDirection::Invalid;
- return m_source.needsDevice() ? FileTransferDirection::Download : FileTransferDirection::Upload;
-}
-
QString FileTransferSetupData::defaultRsyncFlags()
{
return "-av";
}
-static FileTransferDirection transferDirection(const FilesToTransfer &files)
-{
- if (files.isEmpty())
- return FileTransferDirection::Invalid;
-
- const FileTransferDirection direction = files.first().direction();
- for (const FileToTransfer &file : files) {
- if (file.direction() != direction)
- return FileTransferDirection::Invalid;
- }
- return direction;
-}
-
-static const FilePath &remoteFile(FileTransferDirection direction, const FileToTransfer &file)
-{
- return direction == FileTransferDirection::Upload ? file.m_target : file.m_source;
-}
-
-static IDeviceConstPtr matchedDevice(FileTransferDirection direction, const FilesToTransfer &files)
+static IDeviceConstPtr matchedDevice(const FilesToTransfer &files)
{
if (files.isEmpty())
return {};
- const FilePath &filePath = remoteFile(direction, files.first());
+ const FilePath filePath = files.first().m_target;
for (const FileToTransfer &file : files) {
- if (!filePath.isSameDevice(remoteFile(direction, file)))
+ if (!filePath.isSameDevice(file.m_target))
return {};
}
return DeviceManager::deviceForPath(filePath);
@@ -102,15 +77,11 @@ void FileTransferPrivate::start()
if (m_setup.m_files.isEmpty())
return startFailed(Tr::tr("No files to transfer."));
- const FileTransferDirection direction = transferDirection(m_setup.m_files);
-
- IDeviceConstPtr device;
- if (direction != FileTransferDirection::Invalid)
- device = matchedDevice(direction, m_setup.m_files);
+ IDeviceConstPtr device = matchedDevice(m_setup.m_files);
if (!device) {
// Fall back to generic copy.
- const FilePath &filePath = m_setup.m_files.first().m_target;
+ const FilePath filePath = m_setup.m_files.first().m_target;
device = DeviceManager::deviceForPath(filePath);
m_setup.m_method = FileTransferMethod::GenericCopy;
}
@@ -222,7 +193,7 @@ QString FileTransfer::transferMethodName(FileTransferMethod method)
return {};
}
-FileTransferAdapter::FileTransferAdapter()
+FileTransferTaskAdapter::FileTransferTaskAdapter()
{
connect(task(), &FileTransfer::done, this, [this](const ProcessResultData &result) {
emit done(result.m_exitStatus == QProcess::NormalExit
diff --git a/src/plugins/projectexplorer/devicesupport/filetransfer.h b/src/plugins/projectexplorer/devicesupport/filetransfer.h
index dcc04f05c0..f73a374168 100644
--- a/src/plugins/projectexplorer/devicesupport/filetransfer.h
+++ b/src/plugins/projectexplorer/devicesupport/filetransfer.h
@@ -7,7 +7,7 @@
#include "filetransferinterface.h"
#include "idevicefwd.h"
-#include <utils/tasktree.h>
+#include <solutions/tasking/tasktree.h>
namespace Utils { class ProcessResultData; }
@@ -46,14 +46,14 @@ private:
FileTransferPrivate *d;
};
-class PROJECTEXPLORER_EXPORT FileTransferAdapter : public Utils::Tasking::TaskAdapter<FileTransfer>
+class PROJECTEXPLORER_EXPORT FileTransferTaskAdapter : public Tasking::TaskAdapter<FileTransfer>
{
public:
- FileTransferAdapter();
+ FileTransferTaskAdapter();
void start() override { task()->start(); }
};
-class PROJECTEXPLORER_EXPORT FileTransferTestAdapter : public FileTransferAdapter
+class PROJECTEXPLORER_EXPORT FileTransferTestTaskAdapter : public FileTransferTaskAdapter
{
public:
void start() final { task()->test(); }
@@ -61,5 +61,5 @@ public:
} // namespace ProjectExplorer
-QTC_DECLARE_CUSTOM_TASK(Transfer, ProjectExplorer::FileTransferAdapter);
-QTC_DECLARE_CUSTOM_TASK(TransferTest, ProjectExplorer::FileTransferTestAdapter);
+TASKING_DECLARE_TASK(FileTransferTask, ProjectExplorer::FileTransferTaskAdapter);
+TASKING_DECLARE_TASK(FileTransferTestTask, ProjectExplorer::FileTransferTestTaskAdapter);
diff --git a/src/plugins/projectexplorer/devicesupport/filetransferinterface.h b/src/plugins/projectexplorer/devicesupport/filetransferinterface.h
index 2907f9c257..ce4e662db4 100644
--- a/src/plugins/projectexplorer/devicesupport/filetransferinterface.h
+++ b/src/plugins/projectexplorer/devicesupport/filetransferinterface.h
@@ -11,12 +11,6 @@ namespace Utils { class ProcessResultData; }
namespace ProjectExplorer {
-enum class FileTransferDirection {
- Invalid,
- Upload,
- Download
-};
-
enum class FileTransferMethod {
Sftp,
Rsync,
@@ -29,8 +23,6 @@ class PROJECTEXPLORER_EXPORT FileToTransfer
public:
Utils::FilePath m_source;
Utils::FilePath m_target;
-
- FileTransferDirection direction() const;
};
using FilesToTransfer = QList<FileToTransfer>;
diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp
index b884237b3b..50f552444b 100644
--- a/src/plugins/projectexplorer/devicesupport/idevice.cpp
+++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp
@@ -15,6 +15,7 @@
#include <coreplugin/icore.h>
+#include <utils/commandline.h>
#include <utils/devicefileaccess.h>
#include <utils/displayname.h>
#include <utils/icon.h>
@@ -92,6 +93,7 @@ static Id newId()
const char DisplayNameKey[] = "Name";
const char TypeKey[] = "OsType";
+const char ClientOsTypeKey[] = "ClientOsType";
const char IdKey[] = "InternalId";
const char OriginKey[] = "Origin";
const char MachineTypeKey[] = "Type";
@@ -369,6 +371,27 @@ const QList<IDevice::DeviceAction> IDevice::deviceActions() const
return d->deviceActions;
}
+PortsGatheringMethod IDevice::portsGatheringMethod() const
+{
+ return {[this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine {
+ // We might encounter the situation that protocol is given IPv6
+ // but the consumer of the free port information decides to open
+ // an IPv4(only) port. As a result the next IPv6 scan will
+ // report the port again as open (in IPv6 namespace), while the
+ // same port in IPv4 namespace might still be blocked, and
+ // re-use of this port fails.
+ // GDBserver behaves exactly like this.
+
+ Q_UNUSED(protocol)
+
+ if (filePath("/proc/net").isReadableDir())
+ return {filePath("/bin/sh"), {"-c", "cat /proc/net/tcp*"}};
+
+ return {filePath("netstat"), {"-a", "-n"}};
+ },
+ &Port::parseFromCommandOutput};
+};
+
DeviceProcessList *IDevice::createProcessListModel(QObject *parent) const
{
Q_UNUSED(parent)
@@ -425,6 +448,8 @@ void IDevice::fromMap(const QVariantMap &map)
d->type = typeFromMap(map);
d->displayName.fromMap(map, DisplayNameKey);
d->id = Id::fromSetting(map.value(QLatin1String(IdKey)));
+ d->osType = osTypeFromString(
+ map.value(QLatin1String(ClientOsTypeKey), osTypeToString(OsTypeLinux)).toString());
if (!d->id.isValid())
d->id = newId();
d->origin = static_cast<Origin>(map.value(QLatin1String(OriginKey), ManuallyAdded).toInt());
@@ -471,6 +496,7 @@ QVariantMap IDevice::toMap() const
QVariantMap map;
d->displayName.toMap(map, DisplayNameKey);
map.insert(QLatin1String(TypeKey), d->type.toString());
+ map.insert(QLatin1String(ClientOsTypeKey), osTypeToString(d->osType));
map.insert(QLatin1String(IdKey), d->id.toSetting());
map.insert(QLatin1String(OriginKey), d->origin);
@@ -503,9 +529,6 @@ IDevice::Ptr IDevice::clone() const
device->d->deviceState = d->deviceState;
device->d->deviceActions = d->deviceActions;
device->d->deviceIcons = d->deviceIcons;
- // Os type is only set in the constructor, always to the same value.
- // But make sure we notice if that changes in the future (which it shouldn't).
- QTC_CHECK(device->d->osType == d->osType);
device->d->osType = d->osType;
device->fromMap(toMap());
return device;
@@ -578,16 +601,6 @@ void IDevice::setDebugServerPath(const FilePath &path)
d->debugServerPath = path;
}
-FilePath IDevice::debugDumperPath() const
-{
- return d->debugDumperPath;
-}
-
-void IDevice::setDebugDumperPath(const FilePath &path)
-{
- d->debugDumperPath = path;
-}
-
FilePath IDevice::qmlRunCommand() const
{
return d->qmlRunCommand;
@@ -682,12 +695,12 @@ void DeviceProcessKiller::start()
m_signalOperation->killProcess(m_processPath.path());
}
-KillerAdapter::KillerAdapter()
+DeviceProcessKillerTaskAdapter::DeviceProcessKillerTaskAdapter()
{
- connect(task(), &DeviceProcessKiller::done, this, &KillerAdapter::done);
+ connect(task(), &DeviceProcessKiller::done, this, &TaskInterface::done);
}
-void KillerAdapter::start()
+void DeviceProcessKillerTaskAdapter::start()
{
task()->start();
}
diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h
index 6323b8589b..e28f88aaf4 100644
--- a/src/plugins/projectexplorer/devicesupport/idevice.h
+++ b/src/plugins/projectexplorer/devicesupport/idevice.h
@@ -6,11 +6,12 @@
#include "../projectexplorer_export.h"
#include "idevicefwd.h"
+#include <solutions/tasking/tasktree.h>
+
#include <utils/id.h>
#include <utils/expected.h>
#include <utils/filepath.h>
#include <utils/hostosinfo.h>
-#include <utils/tasktree.h>
#include <QAbstractSocket>
#include <QCoreApplication>
@@ -35,7 +36,7 @@ class Icon;
class PortList;
class Port;
class ProcessInterface;
-class QtcProcess;
+class Process;
} // Utils
namespace ProjectExplorer {
@@ -139,10 +140,7 @@ public:
void addDeviceAction(const DeviceAction &deviceAction);
const QList<DeviceAction> deviceActions() const;
- // Devices that can auto detect ports need not return a ports gathering method. Such devices can
- // obtain a free port on demand. eg: Desktop device.
- virtual bool canAutoDetectPorts() const { return false; }
- virtual PortsGatheringMethod portsGatheringMethod() const { return {}; }
+ virtual PortsGatheringMethod portsGatheringMethod() const;
virtual bool canCreateProcessModel() const { return false; }
virtual DeviceProcessList *createProcessListModel(QObject *parent = nullptr) const;
virtual bool hasDeviceTester() const { return false; }
@@ -179,9 +177,6 @@ public:
Utils::FilePath debugServerPath() const;
void setDebugServerPath(const Utils::FilePath &path);
- Utils::FilePath debugDumperPath() const;
- void setDebugDumperPath(const Utils::FilePath &path);
-
Utils::FilePath qmlRunCommand() const;
void setQmlRunCommand(const Utils::FilePath &path);
@@ -221,6 +216,8 @@ public:
virtual bool prepareForBuild(const Target *target);
virtual std::optional<Utils::FilePath> clangdExecutable() const;
+ virtual void checkOsType() {}
+
protected:
IDevice();
@@ -280,13 +277,14 @@ private:
QString m_errorString;
};
-class PROJECTEXPLORER_EXPORT KillerAdapter : public Utils::Tasking::TaskAdapter<DeviceProcessKiller>
+class PROJECTEXPLORER_EXPORT DeviceProcessKillerTaskAdapter
+ : public Tasking::TaskAdapter<DeviceProcessKiller>
{
public:
- KillerAdapter();
+ DeviceProcessKillerTaskAdapter();
void start() final;
};
} // namespace ProjectExplorer
-QTC_DECLARE_CUSTOM_TASK(Killer, ProjectExplorer::KillerAdapter);
+TASKING_DECLARE_TASK(DeviceProcessKillerTask, ProjectExplorer::DeviceProcessKillerTaskAdapter);
diff --git a/src/plugins/projectexplorer/devicesupport/idevicefactory.cpp b/src/plugins/projectexplorer/devicesupport/idevicefactory.cpp
index c33bc125bb..b517844570 100644
--- a/src/plugins/projectexplorer/devicesupport/idevicefactory.cpp
+++ b/src/plugins/projectexplorer/devicesupport/idevicefactory.cpp
@@ -63,12 +63,25 @@ bool IDeviceFactory::canCreate() const
IDevice::Ptr IDeviceFactory::create() const
{
- return m_creator ? m_creator() : IDevice::Ptr();
+ if (!m_creator)
+ return {};
+
+ IDevice::Ptr device = m_creator();
+ if (!device) // e.g. Cancel used on the dialog to create a device
+ return {};
+ device->setDefaultDisplayName(displayName());
+ return device;
}
IDevice::Ptr IDeviceFactory::construct() const
{
- return m_constructor ? m_constructor() : IDevice::Ptr();
+ if (!m_constructor)
+ return {};
+
+ IDevice::Ptr device = m_constructor();
+ QTC_ASSERT(device, return {});
+ device->setDefaultDisplayName(displayName());
+ return device;
}
static QList<IDeviceFactory *> g_deviceFactories;
@@ -105,6 +118,16 @@ void IDeviceFactory::setCreator(const std::function<IDevice::Ptr ()> &creator)
m_creator = creator;
}
+void IDeviceFactory::setQuickCreationAllowed(bool on)
+{
+ m_quickCreationAllowed = on;
+}
+
+bool IDeviceFactory::quickCreationAllowed() const
+{
+ return m_quickCreationAllowed;
+}
+
void IDeviceFactory::setConstructionFunction(const std::function<IDevice::Ptr ()> &constructor)
{
m_constructor = constructor;
diff --git a/src/plugins/projectexplorer/devicesupport/idevicefactory.h b/src/plugins/projectexplorer/devicesupport/idevicefactory.h
index 87a54244ad..665059f5b2 100644
--- a/src/plugins/projectexplorer/devicesupport/idevicefactory.h
+++ b/src/plugins/projectexplorer/devicesupport/idevicefactory.h
@@ -26,6 +26,7 @@ public:
bool canCreate() const;
IDevicePtr construct() const;
IDevicePtr create() const;
+ bool quickCreationAllowed() const;
virtual bool canRestore(const QVariantMap &) const { return true; }
@@ -41,6 +42,7 @@ protected:
void setCombinedIcon(const Utils::FilePath &smallIcon, const Utils::FilePath &largeIcon);
void setConstructionFunction(const std::function<IDevicePtr ()> &constructor);
void setCreator(const std::function<IDevicePtr()> &creator);
+ void setQuickCreationAllowed(bool on);
private:
std::function<IDevicePtr()> m_creator;
@@ -48,6 +50,7 @@ private:
QString m_displayName;
QIcon m_icon;
std::function<IDevicePtr()> m_constructor;
+ bool m_quickCreationAllowed = false;
};
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/localprocesslist.cpp b/src/plugins/projectexplorer/devicesupport/localprocesslist.cpp
deleted file mode 100644
index c6fd49012f..0000000000
--- a/src/plugins/projectexplorer/devicesupport/localprocesslist.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "localprocesslist.h"
-
-#include <projectexplorer/devicesupport/idevice.h>
-#include <utils/processinfo.h>
-
-#include <QTimer>
-
-#if defined(Q_OS_UNIX)
-#include <unistd.h>
-#elif defined(Q_OS_WIN)
-#include <windows.h>
-#endif
-
-using namespace Utils;
-
-namespace ProjectExplorer {
-namespace Internal {
-
-LocalProcessList::LocalProcessList(const IDevice::ConstPtr &device, QObject *parent)
- : DeviceProcessList(device, parent)
-{
-#if defined(Q_OS_UNIX)
- setOwnPid(getpid());
-#elif defined(Q_OS_WIN)
- setOwnPid(GetCurrentProcessId());
-#endif
-}
-
-void LocalProcessList::doKillProcess(const ProcessInfo &processInfo)
-{
- DeviceProcessSignalOperation::Ptr signalOperation = device()->signalOperation();
- connect(signalOperation.data(), &DeviceProcessSignalOperation::finished,
- this, &LocalProcessList::reportDelayedKillStatus);
- signalOperation->killProcess(processInfo.processId);
-}
-
-void LocalProcessList::handleUpdate()
-{
- reportProcessListUpdated(ProcessInfo::processInfoList());
-}
-
-void LocalProcessList::doUpdate()
-{
- QTimer::singleShot(0, this, &LocalProcessList::handleUpdate);
-}
-
-void LocalProcessList::reportDelayedKillStatus(const QString &errorMessage)
-{
- if (errorMessage.isEmpty())
- reportProcessKilled();
- else
- reportError(errorMessage);
-}
-
-} // namespace Internal
-} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/processlist.cpp b/src/plugins/projectexplorer/devicesupport/processlist.cpp
new file mode 100644
index 0000000000..11e0932832
--- /dev/null
+++ b/src/plugins/projectexplorer/devicesupport/processlist.cpp
@@ -0,0 +1,61 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "processlist.h"
+
+#include <projectexplorer/devicesupport/idevice.h>
+#include <utils/processinfo.h>
+
+#include <QTimer>
+
+#if defined(Q_OS_UNIX)
+#include <unistd.h>
+#elif defined(Q_OS_WIN)
+#include <windows.h>
+#endif
+
+using namespace Utils;
+
+namespace ProjectExplorer {
+
+ProcessList::ProcessList(const IDevice::ConstPtr &device, QObject *parent)
+ : DeviceProcessList(device, parent)
+{
+#if defined(Q_OS_UNIX)
+ setOwnPid(getpid());
+#elif defined(Q_OS_WIN)
+ setOwnPid(GetCurrentProcessId());
+#endif
+}
+
+void ProcessList::doKillProcess(const ProcessInfo &processInfo)
+{
+ m_signalOperation = device()->signalOperation();
+ connect(m_signalOperation.data(),
+ &DeviceProcessSignalOperation::finished,
+ this,
+ &ProcessList::reportDelayedKillStatus);
+ m_signalOperation->killProcess(processInfo.processId);
+}
+
+void ProcessList::handleUpdate()
+{
+ reportProcessListUpdated(ProcessInfo::processInfoList(DeviceProcessList::device()->rootPath()));
+}
+
+void ProcessList::doUpdate()
+{
+ QTimer::singleShot(0, this, &ProcessList::handleUpdate);
+}
+
+void ProcessList::reportDelayedKillStatus(const QString &errorMessage)
+{
+ if (errorMessage.isEmpty())
+ reportProcessKilled();
+ else
+ reportError(errorMessage);
+
+ m_signalOperation.reset();
+}
+
+} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/localprocesslist.h b/src/plugins/projectexplorer/devicesupport/processlist.h
index e3445f47b2..caebaf22f9 100644
--- a/src/plugins/projectexplorer/devicesupport/localprocesslist.h
+++ b/src/plugins/projectexplorer/devicesupport/processlist.h
@@ -4,16 +4,16 @@
#pragma once
#include "deviceprocesslist.h"
+#include "idevice.h"
namespace ProjectExplorer {
-namespace Internal {
-class LocalProcessList : public DeviceProcessList
+class PROJECTEXPLORER_EXPORT ProcessList : public DeviceProcessList
{
Q_OBJECT
public:
- explicit LocalProcessList(const IDeviceConstPtr &device, QObject *parent = nullptr);
+ explicit ProcessList(const IDeviceConstPtr &device, QObject *parent = nullptr);
private:
void doUpdate() override;
@@ -22,7 +22,9 @@ private:
private:
void handleUpdate();
void reportDelayedKillStatus(const QString &errorMessage);
+
+private:
+ DeviceProcessSignalOperation::Ptr m_signalOperation;
};
-} // namespace Internal
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp b/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp
deleted file mode 100644
index d64cde6e3e..0000000000
--- a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "sshdeviceprocesslist.h"
-
-#include "idevice.h"
-#include "../projectexplorertr.h"
-
-#include <utils/processinfo.h>
-#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/stringutils.h>
-
-using namespace Utils;
-
-namespace ProjectExplorer {
-
-class SshDeviceProcessListPrivate
-{
-public:
- QtcProcess m_process;
- DeviceProcessSignalOperation::Ptr m_signalOperation;
-};
-
-SshDeviceProcessList::SshDeviceProcessList(const IDevice::ConstPtr &device, QObject *parent) :
- DeviceProcessList(device, parent), d(std::make_unique<SshDeviceProcessListPrivate>())
-{
- connect(&d->m_process, &QtcProcess::done, this, &SshDeviceProcessList::handleProcessDone);
-}
-
-SshDeviceProcessList::~SshDeviceProcessList() = default;
-
-void SshDeviceProcessList::doUpdate()
-{
- d->m_process.close();
- d->m_process.setCommand({device()->filePath("/bin/sh"), {"-c", listProcessesCommandLine()}});
- d->m_process.start();
-}
-
-void SshDeviceProcessList::doKillProcess(const ProcessInfo &process)
-{
- d->m_signalOperation = device()->signalOperation();
- QTC_ASSERT(d->m_signalOperation, return);
- connect(d->m_signalOperation.data(), &DeviceProcessSignalOperation::finished,
- this, &SshDeviceProcessList::handleKillProcessFinished);
- d->m_signalOperation->killProcess(process.processId);
-}
-
-void SshDeviceProcessList::handleProcessDone()
-{
- if (d->m_process.result() == ProcessResult::FinishedWithSuccess) {
- reportProcessListUpdated(buildProcessList(d->m_process.cleanedStdOut()));
- } else {
- const QString errorString = d->m_process.exitStatus() == QProcess::NormalExit
- ? Tr::tr("Process listing command failed with exit code %1.").arg(d->m_process.exitCode())
- : d->m_process.errorString();
- const QString stdErr = d->m_process.cleanedStdErr();
- const QString outputString
- = stdErr.isEmpty() ? stdErr : Tr::tr("Remote stderr was: %1").arg(stdErr);
- reportError(Utils::joinStrings({errorString, outputString}, '\n'));
- }
- setFinished();
-}
-
-void SshDeviceProcessList::handleKillProcessFinished(const QString &errorString)
-{
- if (errorString.isEmpty())
- reportProcessKilled();
- else
- reportError(Tr::tr("Error: Kill process failed: %1").arg(errorString));
- setFinished();
-}
-
-void SshDeviceProcessList::setFinished()
-{
- d->m_process.close();
- if (d->m_signalOperation) {
- d->m_signalOperation->disconnect(this);
- d->m_signalOperation.clear();
- }
-}
-
-} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.h b/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.h
deleted file mode 100644
index fd560f5375..0000000000
--- a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "deviceprocesslist.h"
-
-#include <memory>
-
-namespace ProjectExplorer {
-
-class SshDeviceProcessListPrivate;
-
-class PROJECTEXPLORER_EXPORT SshDeviceProcessList : public DeviceProcessList
-{
- Q_OBJECT
-public:
- explicit SshDeviceProcessList(const IDeviceConstPtr &device, QObject *parent = nullptr);
- ~SshDeviceProcessList() override;
-
-private:
- void handleProcessDone();
- void handleKillProcessFinished(const QString &errorString);
-
- virtual QString listProcessesCommandLine() const = 0;
- virtual QList<Utils::ProcessInfo> buildProcessList(const QString &listProcessesReply) const = 0;
-
- void doUpdate() override;
- void doKillProcess(const Utils::ProcessInfo &process) override;
-
- void setFinished();
-
- const std::unique_ptr<SshDeviceProcessListPrivate> d;
-};
-
-} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/sshparameters.cpp b/src/plugins/projectexplorer/devicesupport/sshparameters.cpp
index 3cc6364a67..744e350eae 100644
--- a/src/plugins/projectexplorer/devicesupport/sshparameters.cpp
+++ b/src/plugins/projectexplorer/devicesupport/sshparameters.cpp
@@ -8,8 +8,8 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDebug>
@@ -19,9 +19,17 @@ using namespace Utils;
namespace ProjectExplorer {
-SshParameters::SshParameters()
+SshParameters::SshParameters() = default;
+
+QString SshParameters::userAtHost() const
{
- url.setPort(0);
+ QString res;
+ if (!m_userName.isEmpty())
+ res = m_userName + '@';
+ res += m_host;
+ if (m_port != 22)
+ res += QString(":%1").arg(m_port);
+ return res;
}
QStringList SshParameters::connectionOptions(const FilePath &binary) const
@@ -61,7 +69,7 @@ QStringList SshParameters::connectionOptions(const FilePath &binary) const
return args;
}
-bool SshParameters::setupSshEnvironment(QtcProcess *process)
+bool SshParameters::setupSshEnvironment(Process *process)
{
Environment env = process->controlEnvironment();
if (!env.hasChanges())
@@ -81,10 +89,11 @@ bool SshParameters::setupSshEnvironment(QtcProcess *process)
return hasDisplay;
}
-
-static inline bool equals(const SshParameters &p1, const SshParameters &p2)
+bool operator==(const SshParameters &p1, const SshParameters &p2)
{
- return p1.url == p2.url
+ return p1.m_host == p2.m_host
+ && p1.m_port == p2.m_port
+ && p1.m_userName == p2.m_userName
&& p1.authenticationType == p2.authenticationType
&& p1.privateKeyFile == p2.privateKeyFile
&& p1.hostKeyCheckingMode == p2.hostKeyCheckingMode
@@ -92,16 +101,6 @@ static inline bool equals(const SshParameters &p1, const SshParameters &p2)
&& p1.timeout == p2.timeout;
}
-bool operator==(const SshParameters &p1, const SshParameters &p2)
-{
- return equals(p1, p2);
-}
-
-bool operator!=(const SshParameters &p1, const SshParameters &p2)
-{
- return !equals(p1, p2);
-}
-
#ifdef WITH_TESTS
namespace SshTest {
const QString getHostFromEnvironment()
diff --git a/src/plugins/projectexplorer/devicesupport/sshparameters.h b/src/plugins/projectexplorer/devicesupport/sshparameters.h
index 00b63e3aac..3ee483d5f3 100644
--- a/src/plugins/projectexplorer/devicesupport/sshparameters.h
+++ b/src/plugins/projectexplorer/devicesupport/sshparameters.h
@@ -7,9 +7,7 @@
#include <utils/filepath.h>
-#include <QUrl>
-
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace ProjectExplorer {
@@ -29,28 +27,34 @@ public:
SshParameters();
- QString host() const { return url.host(); }
- quint16 port() const { return url.port(); }
- QString userName() const { return url.userName(); }
- QString userAtHost() const { return userName().isEmpty() ? host() : userName() + '@' + host(); }
- void setHost(const QString &host) { url.setHost(host); }
- void setPort(int port) { url.setPort(port); }
- void setUserName(const QString &name) { url.setUserName(name); }
+ QString host() const { return m_host; }
+ quint16 port() const { return m_port; }
+ QString userName() const { return m_userName; }
+
+ QString userAtHost() const;
+
+ void setHost(const QString &host) { m_host = host; }
+ void setPort(int port) { m_port = port; }
+ void setUserName(const QString &name) { m_userName = name; }
QStringList connectionOptions(const Utils::FilePath &binary) const;
- QUrl url;
Utils::FilePath privateKeyFile;
QString x11DisplayName;
int timeout = 0; // In seconds.
AuthenticationType authenticationType = AuthenticationTypeAll;
SshHostKeyCheckingMode hostKeyCheckingMode = SshHostKeyCheckingAllowNoMatch;
- static bool setupSshEnvironment(Utils::QtcProcess *process);
-};
+ static bool setupSshEnvironment(Utils::Process *process);
+
+ friend PROJECTEXPLORER_EXPORT bool operator==(const SshParameters &p1, const SshParameters &p2);
+ friend bool operator!=(const SshParameters &p1, const SshParameters &p2) { return !(p1 == p2); }
-PROJECTEXPLORER_EXPORT bool operator==(const SshParameters &p1, const SshParameters &p2);
-PROJECTEXPLORER_EXPORT bool operator!=(const SshParameters &p1, const SshParameters &p2);
+private:
+ QString m_host;
+ quint16 m_port = 22;
+ QString m_userName;
+};
#ifdef WITH_TESTS
namespace SshTest {
diff --git a/src/plugins/projectexplorer/editorconfiguration.cpp b/src/plugins/projectexplorer/editorconfiguration.cpp
index fc3933d734..ff99bc25e8 100644
--- a/src/plugins/projectexplorer/editorconfiguration.cpp
+++ b/src/plugins/projectexplorer/editorconfiguration.cpp
@@ -5,7 +5,7 @@
#include "project.h"
#include "projectexplorertr.h"
-#include "session.h"
+#include "projectmanager.h"
#include <utils/algorithm.h>
@@ -88,7 +88,7 @@ EditorConfiguration::EditorConfiguration() : d(std::make_unique<EditorConfigurat
// if setCurrentDelegate is 0 values are read from *this prefs
d->m_defaultCodeStyle->setCurrentDelegate(TextEditorSettings::codeStyle());
- connect(SessionManager::instance(), &SessionManager::aboutToRemoveProject,
+ connect(ProjectManager::instance(), &ProjectManager::aboutToRemoveProject,
this, &EditorConfiguration::slotAboutToRemoveProject);
}
@@ -263,7 +263,7 @@ void EditorConfiguration::setUseGlobalSettings(bool use)
const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForOpenedDocuments();
for (Core::IEditor *editor : editors) {
if (auto widget = TextEditorWidget::fromEditor(editor)) {
- Project *project = SessionManager::projectForFile(editor->document()->filePath());
+ Project *project = ProjectManager::projectForFile(editor->document()->filePath());
if (project && project->editorConfiguration() == this)
switchSettings(widget);
}
@@ -399,7 +399,7 @@ TabSettings actualTabSettings(const Utils::FilePath &file,
{
if (baseTextdocument)
return baseTextdocument->tabSettings();
- if (Project *project = SessionManager::projectForFile(file))
+ if (Project *project = ProjectManager::projectForFile(file))
return project->editorConfiguration()->codeStyle()->tabSettings();
return TextEditorSettings::codeStyle()->tabSettings();
}
diff --git a/src/plugins/projectexplorer/editorsettingspropertiespage.cpp b/src/plugins/projectexplorer/editorsettingspropertiespage.cpp
index 52450b2bc8..e0c2b93bb2 100644
--- a/src/plugins/projectexplorer/editorsettingspropertiespage.cpp
+++ b/src/plugins/projectexplorer/editorsettingspropertiespage.cpp
@@ -48,7 +48,7 @@ EditorSettingsWidget::EditorSettingsWidget(Project *project) : m_project(project
m_behaviorSettings = new TextEditor::BehaviorSettingsWidget(this);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Row {
m_showWrapColumn,
@@ -63,7 +63,8 @@ EditorSettingsWidget::EditorSettingsWidget(Project *project) : m_project(project
m_displaySettings,
m_behaviorSettings,
st,
- }.attachTo(this, WithoutMargins);
+ noMargin
+ }.attachTo(this);
const EditorConfiguration *config = m_project->editorConfiguration();
settingsToUi(config);
diff --git a/src/plugins/projectexplorer/environmentaspect.cpp b/src/plugins/projectexplorer/environmentaspect.cpp
index 66a2b3bc99..09bfeb9c80 100644
--- a/src/plugins/projectexplorer/environmentaspect.cpp
+++ b/src/plugins/projectexplorer/environmentaspect.cpp
@@ -3,8 +3,11 @@
#include "environmentaspect.h"
+#include "buildconfiguration.h"
#include "environmentaspectwidget.h"
+#include "kit.h"
#include "projectexplorertr.h"
+#include "target.h"
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
@@ -12,6 +15,7 @@
using namespace Utils;
namespace ProjectExplorer {
+const char PRINT_ON_RUN_KEY[] = "PE.EnvironmentAspect.PrintOnRun";
// --------------------------------------------------------------------
// EnvironmentAspect:
@@ -101,16 +105,39 @@ int EnvironmentAspect::addPreferredBaseEnvironment(const QString &displayName,
return index;
}
+void EnvironmentAspect::setSupportForBuildEnvironment(Target *target)
+{
+ setIsLocal(true);
+ addSupportedBaseEnvironment(Tr::tr("Clean Environment"), {});
+
+ addSupportedBaseEnvironment(Tr::tr("System Environment"), [] {
+ return Environment::systemEnvironment();
+ });
+ addPreferredBaseEnvironment(Tr::tr("Build Environment"), [target] {
+ if (BuildConfiguration *bc = target->activeBuildConfiguration())
+ return bc->environment();
+ // Fallback for targets without buildconfigurations:
+ return target->kit()->buildEnvironment();
+ });
+
+ connect(target, &Target::activeBuildConfigurationChanged,
+ this, &EnvironmentAspect::environmentChanged);
+ connect(target, &Target::buildEnvironmentChanged,
+ this, &EnvironmentAspect::environmentChanged);
+}
+
void EnvironmentAspect::fromMap(const QVariantMap &map)
{
m_base = map.value(QLatin1String(BASE_KEY), -1).toInt();
m_userChanges = Utils::EnvironmentItem::fromStringList(map.value(QLatin1String(CHANGES_KEY)).toStringList());
+ m_printOnRun = map.value(PRINT_ON_RUN_KEY).toBool();
}
void EnvironmentAspect::toMap(QVariantMap &data) const
{
data.insert(QLatin1String(BASE_KEY), m_base);
data.insert(QLatin1String(CHANGES_KEY), Utils::EnvironmentItem::toStringList(m_userChanges));
+ data.insert(PRINT_ON_RUN_KEY, m_printOnRun);
}
QString EnvironmentAspect::currentDisplayName() const
diff --git a/src/plugins/projectexplorer/environmentaspect.h b/src/plugins/projectexplorer/environmentaspect.h
index 5b1d0d1c41..97ce26f017 100644
--- a/src/plugins/projectexplorer/environmentaspect.h
+++ b/src/plugins/projectexplorer/environmentaspect.h
@@ -39,6 +39,8 @@ public:
int addPreferredBaseEnvironment(const QString &displayName,
const std::function<Utils::Environment()> &getter);
+ void setSupportForBuildEnvironment(Target *target);
+
QString currentDisplayName() const;
const QStringList displayNames() const;
@@ -48,6 +50,10 @@ public:
bool isLocal() const { return m_isLocal; }
+ bool isPrintOnRunAllowed() const { return m_allowPrintOnRun; }
+ bool isPrintOnRunEnabled() const { return m_printOnRun; }
+ void setPrintOnRun(bool enabled) { m_printOnRun = enabled; }
+
struct Data : BaseAspect::Data
{
Utils::Environment environment;
@@ -66,6 +72,7 @@ protected:
void toMap(QVariantMap &map) const override;
void setIsLocal(bool local) { m_isLocal = local; }
+ void setAllowPrintOnRun(bool allow) { m_allowPrintOnRun = allow; }
static constexpr char BASE_KEY[] = "PE.EnvironmentAspect.Base";
static constexpr char CHANGES_KEY[] = "PE.EnvironmentAspect.Changes";
@@ -84,6 +91,8 @@ private:
QList<BaseEnvironment> m_baseEnvironments;
int m_base = -1;
bool m_isLocal = false;
+ bool m_allowPrintOnRun = true;
+ bool m_printOnRun = false;
};
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/environmentaspectwidget.cpp b/src/plugins/projectexplorer/environmentaspectwidget.cpp
index 18babfcf61..34e48af42c 100644
--- a/src/plugins/projectexplorer/environmentaspectwidget.cpp
+++ b/src/plugins/projectexplorer/environmentaspectwidget.cpp
@@ -9,6 +9,7 @@
#include <utils/environment.h>
#include <utils/qtcassert.h>
+#include <QCheckBox>
#include <QComboBox>
#include <QHBoxLayout>
#include <QLabel>
@@ -63,6 +64,14 @@ EnvironmentAspectWidget::EnvironmentAspectWidget(EnvironmentAspect *aspect)
m_environmentWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
topLayout->addWidget(m_environmentWidget);
+ if (m_aspect->isPrintOnRunAllowed()) {
+ const auto printOnRunCheckBox = new QCheckBox(Tr::tr("Show in output pane when running"));
+ printOnRunCheckBox->setChecked(m_aspect->isPrintOnRunEnabled());
+ connect(printOnRunCheckBox, &QCheckBox::toggled,
+ m_aspect, &EnvironmentAspect::setPrintOnRun);
+ topLayout->addWidget(printOnRunCheckBox);
+ }
+
connect(m_environmentWidget, &EnvironmentWidget::userChangesChanged,
this, &EnvironmentAspectWidget::userChangesEdited);
diff --git a/src/plugins/projectexplorer/extraabi.cpp b/src/plugins/projectexplorer/extraabi.cpp
index 24a2861103..19f970b11a 100644
--- a/src/plugins/projectexplorer/extraabi.cpp
+++ b/src/plugins/projectexplorer/extraabi.cpp
@@ -4,7 +4,6 @@
#include "extraabi.h"
#include "abi.h"
-#include "projectexplorertr.h"
#include <coreplugin/icore.h>
@@ -36,17 +35,15 @@ public:
class AbiFlavorAccessor : public UpgradingSettingsAccessor
{
public:
- AbiFlavorAccessor();
+ AbiFlavorAccessor()
+ {
+ setDocType("QtCreatorExtraAbi");
+ setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME);
+ setBaseFilePath(Core::ICore::installerResourcePath("abi.xml"));
+ addVersionUpgrader(std::make_unique<AbiFlavorUpgraderV0>());
+ }
};
-AbiFlavorAccessor::AbiFlavorAccessor() :
- UpgradingSettingsAccessor("QtCreatorExtraAbi", Tr::tr("ABI"),
- Core::Constants::IDE_DISPLAY_NAME)
-{
- setBaseFilePath(Core::ICore::installerResourcePath("abi.xml"));
-
- addVersionUpgrader(std::make_unique<AbiFlavorUpgraderV0>());
-}
// --------------------------------------------------------------------
// ExtraAbi:
diff --git a/src/plugins/projectexplorer/extracompiler.cpp b/src/plugins/projectexplorer/extracompiler.cpp
index ab72d3608f..67bb4a63fc 100644
--- a/src/plugins/projectexplorer/extracompiler.cpp
+++ b/src/plugins/projectexplorer/extracompiler.cpp
@@ -5,23 +5,26 @@
#include "buildmanager.h"
#include "kitinformation.h"
-#include "session.h"
+#include "projectmanager.h"
#include "target.h"
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/idocument.h>
-#include <utils/asynctask.h>
+#include <extensionsystem/pluginmanager.h>
+
+#include <utils/async.h>
#include <utils/expected.h>
#include <utils/guard.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QDateTime>
-#include <QFutureInterface>
#include <QLoggingCategory>
#include <QThreadPool>
#include <QTimer>
+using namespace Core;
+using namespace Tasking;
using namespace Utils;
namespace ProjectExplorer {
@@ -37,15 +40,14 @@ public:
FilePath source;
FileNameToContentsHash contents;
QDateTime compileTime;
- Core::IEditor *lastEditor = nullptr;
+ IEditor *lastEditor = nullptr;
QMetaObject::Connection activeBuildConfigConnection;
QMetaObject::Connection activeEnvironmentConnection;
- Utils::Guard lock;
+ Guard lock;
bool dirty = false;
QTimer timer;
- FutureSynchronizer m_futureSynchronizer;
std::unique_ptr<TaskTree> m_taskTree;
};
@@ -63,16 +65,16 @@ ExtraCompiler::ExtraCompiler(const Project *project, const FilePath &source,
connect(BuildManager::instance(), &BuildManager::buildStateChanged,
this, &ExtraCompiler::onTargetsBuilt);
- connect(SessionManager::instance(), &SessionManager::projectRemoved,
+ connect(ProjectManager::instance(), &ProjectManager::projectRemoved,
this, [this](Project *project) {
if (project == d->project)
deleteLater();
});
- Core::EditorManager *editorManager = Core::EditorManager::instance();
- connect(editorManager, &Core::EditorManager::currentEditorChanged,
+ EditorManager *editorManager = EditorManager::instance();
+ connect(editorManager, &EditorManager::currentEditorChanged,
this, &ExtraCompiler::onEditorChanged);
- connect(editorManager, &Core::EditorManager::editorAboutToClose,
+ connect(editorManager, &EditorManager::editorAboutToClose,
this, &ExtraCompiler::onEditorAboutToClose);
// Use existing target files, where possible. Otherwise run the compiler.
@@ -135,7 +137,7 @@ QThreadPool *ExtraCompiler::extraCompilerThreadPool()
return s_extraCompilerThreadPool();
}
-Tasking::TaskItem ExtraCompiler::compileFileItem()
+TaskItem ExtraCompiler::compileFileItem()
{
return taskItemImpl(fromFileProvider());
}
@@ -228,12 +230,12 @@ void ExtraCompiler::onTargetsBuilt(Project *project)
});
}
-void ExtraCompiler::onEditorChanged(Core::IEditor *editor)
+void ExtraCompiler::onEditorChanged(IEditor *editor)
{
// Handle old editor
if (d->lastEditor) {
- Core::IDocument *doc = d->lastEditor->document();
- disconnect(doc, &Core::IDocument::contentsChanged,
+ IDocument *doc = d->lastEditor->document();
+ disconnect(doc, &IDocument::contentsChanged,
this, &ExtraCompiler::setDirty);
if (d->dirty) {
@@ -246,7 +248,7 @@ void ExtraCompiler::onEditorChanged(Core::IEditor *editor)
d->lastEditor = editor;
// Handle new editor
- connect(d->lastEditor->document(), &Core::IDocument::contentsChanged,
+ connect(d->lastEditor->document(), &IDocument::contentsChanged,
this, &ExtraCompiler::setDirty);
} else {
d->lastEditor = nullptr;
@@ -259,15 +261,15 @@ void ExtraCompiler::setDirty()
d->timer.start(1000);
}
-void ExtraCompiler::onEditorAboutToClose(Core::IEditor *editor)
+void ExtraCompiler::onEditorAboutToClose(IEditor *editor)
{
if (d->lastEditor != editor)
return;
// Oh no our editor is going to be closed
// get the content first
- Core::IDocument *doc = d->lastEditor->document();
- disconnect(doc, &Core::IDocument::contentsChanged,
+ IDocument *doc = d->lastEditor->document();
+ disconnect(doc, &IDocument::contentsChanged,
this, &ExtraCompiler::setDirty);
if (d->dirty) {
d->dirty = false;
@@ -278,24 +280,17 @@ void ExtraCompiler::onEditorAboutToClose(Core::IEditor *editor)
Environment ExtraCompiler::buildEnvironment() const
{
- if (Target *target = project()->activeTarget()) {
- if (BuildConfiguration *bc = target->activeBuildConfiguration()) {
- return bc->environment();
- } else {
- EnvironmentItems changes =
- EnvironmentKitAspect::environmentChanges(target->kit());
- Environment env = Environment::systemEnvironment();
- env.modify(changes);
- return env;
- }
- }
+ Target *target = project()->activeTarget();
+ if (!target)
+ return Environment::systemEnvironment();
- return Environment::systemEnvironment();
-}
+ if (BuildConfiguration *bc = target->activeBuildConfiguration())
+ return bc->environment();
-Utils::FutureSynchronizer *ExtraCompiler::futureSynchronizer() const
-{
- return &d->m_futureSynchronizer;
+ const EnvironmentItems changes = EnvironmentKitAspect::environmentChanges(target->kit());
+ Environment env = Environment::systemEnvironment();
+ env.modify(changes);
+ return env;
}
void ExtraCompiler::setContent(const FilePath &file, const QByteArray &contents)
@@ -331,15 +326,16 @@ ProcessExtraCompiler::ProcessExtraCompiler(const Project *project, const FilePat
ExtraCompiler(project, source, targets, parent)
{ }
-Tasking::TaskItem ProcessExtraCompiler::taskItemImpl(const ContentProvider &provider)
+TaskItem ProcessExtraCompiler::taskItemImpl(const ContentProvider &provider)
{
- const auto setupTask = [=](AsyncTask<FileNameToContentsHash> &async) {
+ const auto setupTask = [=](Async<FileNameToContentsHash> &async) {
async.setThreadPool(extraCompilerThreadPool());
- async.setAsyncCallData(&ProcessExtraCompiler::runInThread, this, command(),
- workingDirectory(), arguments(), provider, buildEnvironment());
- async.setFutureSynchronizer(futureSynchronizer());
+ // The passed synchronizer has cancelOnWait set to true by default.
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(&ProcessExtraCompiler::runInThread, this, command(),
+ workingDirectory(), arguments(), provider, buildEnvironment());
};
- const auto taskDone = [=](const AsyncTask<FileNameToContentsHash> &async) {
+ const auto taskDone = [=](const Async<FileNameToContentsHash> &async) {
if (!async.isResultAvailable())
return;
const FileNameToContentsHash data = async.result();
@@ -349,7 +345,7 @@ Tasking::TaskItem ProcessExtraCompiler::taskItemImpl(const ContentProvider &prov
setContent(it.key(), it.value());
updateCompileTime();
};
- return Tasking::Async<FileNameToContentsHash>(setupTask, taskDone);
+ return AsyncTask<FileNameToContentsHash>(setupTask, taskDone);
}
FilePath ProcessExtraCompiler::workingDirectory() const
@@ -374,7 +370,7 @@ Tasks ProcessExtraCompiler::parseIssues(const QByteArray &stdErr)
return {};
}
-void ProcessExtraCompiler::runInThread(QFutureInterface<FileNameToContentsHash> &futureInterface,
+void ProcessExtraCompiler::runInThread(QPromise<FileNameToContentsHash> &promise,
const FilePath &cmd, const FilePath &workDir,
const QStringList &args, const ContentProvider &provider,
const Environment &env)
@@ -386,7 +382,7 @@ void ProcessExtraCompiler::runInThread(QFutureInterface<FileNameToContentsHash>
if (sourceContents.isNull() || !prepareToRun(sourceContents))
return;
- QtcProcess process;
+ Process process;
process.setEnvironment(env);
if (!workDir.isEmpty())
@@ -397,15 +393,15 @@ void ProcessExtraCompiler::runInThread(QFutureInterface<FileNameToContentsHash>
if (!process.waitForStarted())
return;
- while (!futureInterface.isCanceled()) {
+ while (!promise.isCanceled()) {
if (process.waitForFinished(200))
break;
}
- if (futureInterface.isCanceled())
+ if (promise.isCanceled())
return;
- futureInterface.reportResult(handleProcessFinished(&process));
+ promise.addResult(handleProcessFinished(&process));
}
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/extracompiler.h b/src/plugins/projectexplorer/extracompiler.h
index 34993f3940..1dbda0e424 100644
--- a/src/plugins/projectexplorer/extracompiler.h
+++ b/src/plugins/projectexplorer/extracompiler.h
@@ -10,7 +10,6 @@
#include <utils/environment.h>
#include <utils/filepath.h>
-#include <utils/tasktree.h>
#include <QByteArray>
#include <QHash>
@@ -21,14 +20,12 @@
QT_BEGIN_NAMESPACE
template <typename T>
-class QFutureInterface;
+class QPromise;
class QThreadPool;
QT_END_NAMESPACE
-namespace Utils {
-class FutureSynchronizer;
-class QtcProcess;
-}
+namespace Tasking { class TaskItem; }
+namespace Utils { class Process; }
namespace ProjectExplorer {
@@ -52,7 +49,7 @@ public:
Utils::FilePaths targets() const;
void forEachTarget(std::function<void(const Utils::FilePath &)> func) const;
- Utils::Tasking::TaskItem compileFileItem();
+ Tasking::TaskItem compileFileItem();
void compileFile();
bool isDirty() const;
void block();
@@ -64,7 +61,6 @@ signals:
protected:
static QThreadPool *extraCompilerThreadPool();
- Utils::FutureSynchronizer *futureSynchronizer() const;
void setContent(const Utils::FilePath &file, const QByteArray &content);
void updateCompileTime();
Utils::Environment buildEnvironment() const;
@@ -79,7 +75,7 @@ private:
void compileContent(const QByteArray &content);
void compileImpl(const ContentProvider &provider);
void compileIfDirty();
- virtual Utils::Tasking::TaskItem taskItemImpl(const ContentProvider &provider) = 0;
+ virtual Tasking::TaskItem taskItemImpl(const ContentProvider &provider) = 0;
const std::unique_ptr<ExtraCompilerPrivate> d;
};
@@ -100,13 +96,13 @@ protected:
virtual bool prepareToRun(const QByteArray &sourceContents);
- virtual FileNameToContentsHash handleProcessFinished(Utils::QtcProcess *process) = 0;
+ virtual FileNameToContentsHash handleProcessFinished(Utils::Process *process) = 0;
virtual Tasks parseIssues(const QByteArray &stdErr);
private:
- Utils::Tasking::TaskItem taskItemImpl(const ContentProvider &provider) final;
- void runInThread(QFutureInterface<FileNameToContentsHash> &futureInterface,
+ Tasking::TaskItem taskItemImpl(const ContentProvider &provider) final;
+ void runInThread(QPromise<FileNameToContentsHash> &promise,
const Utils::FilePath &cmd, const Utils::FilePath &workDir,
const QStringList &args, const ContentProvider &provider,
const Utils::Environment &env);
diff --git a/src/plugins/projectexplorer/fileinsessionfinder.cpp b/src/plugins/projectexplorer/fileinsessionfinder.cpp
index 78f75ce1d5..d8df6c7430 100644
--- a/src/plugins/projectexplorer/fileinsessionfinder.cpp
+++ b/src/plugins/projectexplorer/fileinsessionfinder.cpp
@@ -4,7 +4,7 @@
#include "fileinsessionfinder.h"
#include "project.h"
-#include "session.h"
+#include "projectmanager.h"
#include <utils/fileinprojectfinder.h>
@@ -30,12 +30,12 @@ private:
FileInSessionFinder::FileInSessionFinder()
{
- connect(SessionManager::instance(), &SessionManager::projectAdded,
+ connect(ProjectManager::instance(), &ProjectManager::projectAdded,
this, [this](const Project *p) {
invalidateFinder();
connect(p, &Project::fileListChanged, this, &FileInSessionFinder::invalidateFinder);
});
- connect(SessionManager::instance(), &SessionManager::projectRemoved,
+ connect(ProjectManager::instance(), &ProjectManager::projectRemoved,
this, [this](const Project *p) {
invalidateFinder();
p->disconnect(this);
@@ -45,11 +45,11 @@ FileInSessionFinder::FileInSessionFinder()
FilePaths FileInSessionFinder::doFindFile(const FilePath &filePath)
{
if (!m_finderIsUpToDate) {
- m_finder.setProjectDirectory(SessionManager::startupProject()
- ? SessionManager::startupProject()->projectDirectory()
+ m_finder.setProjectDirectory(ProjectManager::startupProject()
+ ? ProjectManager::startupProject()->projectDirectory()
: FilePath());
FilePaths allFiles;
- for (const Project * const p : SessionManager::projects())
+ for (const Project * const p : ProjectManager::projects())
allFiles << p->files(Project::SourceFiles);
m_finder.setProjectFiles(allFiles);
m_finderIsUpToDate = true;
diff --git a/src/plugins/projectexplorer/filesinallprojectsfind.cpp b/src/plugins/projectexplorer/filesinallprojectsfind.cpp
index 720cfbabae..0f9d6c40e2 100644
--- a/src/plugins/projectexplorer/filesinallprojectsfind.cpp
+++ b/src/plugins/projectexplorer/filesinallprojectsfind.cpp
@@ -5,7 +5,7 @@
#include "project.h"
#include "projectexplorertr.h"
-#include "session.h"
+#include "projectmanager.h"
#include <coreplugin/editormanager/editormanager.h>
#include <utils/algorithm.h>
@@ -52,7 +52,7 @@ Utils::FileIterator *FilesInAllProjectsFind::files(const QStringList &nameFilter
const QVariant &additionalParameters) const
{
Q_UNUSED(additionalParameters)
- const QSet<FilePath> dirs = Utils::transform<QSet>(SessionManager::projects(), [](Project *p) {
+ const QSet<FilePath> dirs = Utils::transform<QSet>(ProjectManager::projects(), [](Project *p) {
return p->projectFilePath().parentDir();
});
return new SubDirFileIterator(FilePaths(dirs.constBegin(), dirs.constEnd()),
diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp
index d2449bd69a..4012fbd669 100644
--- a/src/plugins/projectexplorer/gcctoolchain.cpp
+++ b/src/plugins/projectexplorer/gcctoolchain.cpp
@@ -20,8 +20,8 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QBuffer>
#include <QCheckBox>
@@ -119,7 +119,7 @@ static QString runGcc(const FilePath &gcc, const QStringList &arguments, const E
if (!gcc.isExecutableFile())
return {};
- QtcProcess cpp;
+ Process cpp;
Environment environment(env);
environment.setupEnglishOutput();
@@ -196,7 +196,7 @@ HeaderPaths GccToolChain::gccHeaderPaths(const FilePath &gcc,
}
const FilePath headerPath
- = FilePath::fromString(QString::fromUtf8(line)).onDevice(gcc).canonicalPath();
+ = gcc.withNewPath(QString::fromUtf8(line)).canonicalPath();
if (!headerPath.isEmpty())
builtInHeaderPaths.append({headerPath, thisHeaderKind});
@@ -569,7 +569,7 @@ WarningFlags GccToolChain::warningFlags(const QStringList &cflags) const
return flags;
}
-QStringList GccToolChain::includedFiles(const QStringList &flags, const QString &directoryPath) const
+FilePaths GccToolChain::includedFiles(const QStringList &flags, const FilePath &directoryPath) const
{
return ToolChain::includedFiles("-include", flags, directoryPath, PossiblyConcatenatedFlag::No);
}
@@ -1040,10 +1040,9 @@ GccToolChainFactory::GccToolChainFactory()
Toolchains GccToolChainFactory::autoDetect(const ToolchainDetector &detector) const
{
// GCC is almost never what you want on macOS, but it is by default found in /usr/bin
- if (HostOsInfo::isMacHost()
- && (!detector.device || detector.device->type() == Constants::DESKTOP_DEVICE_TYPE)) {
+ if (HostOsInfo::isMacHost() && detector.device->type() == Constants::DESKTOP_DEVICE_TYPE)
return {};
- }
+
Toolchains tcs;
static const auto tcChecker = [](const ToolChain *tc) {
return tc->targetAbi().osFlavor() != Abi::WindowsMSysFlavor
@@ -1086,7 +1085,7 @@ static FilePaths findCompilerCandidates(const ToolchainDetector &detector,
{
const IDevice::ConstPtr device = detector.device;
const QFileInfo fi(compilerName);
- if (device.isNull() && fi.isAbsolute() && fi.isFile())
+ if (device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE && fi.isAbsolute() && fi.isFile())
return {FilePath::fromString(compilerName)};
QStringList nameFilters(compilerName);
@@ -1385,8 +1384,10 @@ void GccToolChainConfigWidget::setFromToolchain()
QSignalBlocker blocker(this);
auto tc = static_cast<GccToolChain *>(toolChain());
m_compilerCommand->setFilePath(tc->compilerCommand());
- m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->platformCodeGenFlags()));
- m_platformLinkerFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->platformLinkerFlags()));
+ m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->platformCodeGenFlags(),
+ HostOsInfo::hostOs()));
+ m_platformLinkerFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->platformLinkerFlags(),
+ HostOsInfo::hostOs()));
if (m_abiWidget) {
m_abiWidget->setAbis(tc->supportedAbis(), tc->targetAbi());
if (!m_isReadOnly && !m_compilerCommand->filePath().toString().isEmpty())
@@ -1569,7 +1570,7 @@ bool ClangToolChain::matchesCompilerCommand(const FilePath &command) const
m_resolvedCompilerCommand = FilePath();
if (HostOsInfo::isMacHost()
&& compilerCommand().parentDir() == FilePath::fromString("/usr/bin")) {
- QtcProcess xcrun;
+ Process xcrun;
xcrun.setCommand({"/usr/bin/xcrun", {"-f", compilerCommand().fileName()}});
xcrun.runBlocking();
const FilePath output = FilePath::fromString(xcrun.cleanedStdOut().trimmed());
diff --git a/src/plugins/projectexplorer/gcctoolchain.h b/src/plugins/projectexplorer/gcctoolchain.h
index 55ba61e9fb..1e6353d0a7 100644
--- a/src/plugins/projectexplorer/gcctoolchain.h
+++ b/src/plugins/projectexplorer/gcctoolchain.h
@@ -55,8 +55,8 @@ public:
Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override;
Utils::WarningFlags warningFlags(const QStringList &cflags) const override;
- QStringList includedFiles(const QStringList &flags,
- const QString &directoryPath) const override;
+ Utils::FilePaths includedFiles(const QStringList &flags,
+ const Utils::FilePath &directoryPath) const override;
MacroInspectionRunner createMacroInspectionRunner() const override;
BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner(const Utils::Environment &env) const override;
diff --git a/src/plugins/projectexplorer/ipotentialkit.h b/src/plugins/projectexplorer/ipotentialkit.h
index ae032fb9e3..038a3a9fac 100644
--- a/src/plugins/projectexplorer/ipotentialkit.h
+++ b/src/plugins/projectexplorer/ipotentialkit.h
@@ -4,18 +4,16 @@
#pragma once
#include <QObject>
-#include <QMetaType>
+
#include "projectexplorer_export.h"
namespace ProjectExplorer {
-class PROJECTEXPLORER_EXPORT IPotentialKit : public QObject
+class PROJECTEXPLORER_EXPORT IPotentialKit
{
- Q_OBJECT
-
public:
IPotentialKit();
- ~IPotentialKit() override;
+ virtual ~IPotentialKit();
virtual QString displayName() const = 0;
virtual void executeFromMenu() = 0;
@@ -23,4 +21,4 @@ public:
virtual bool isEnabled() const = 0;
};
-}
+} // ProjectExplorer
diff --git a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp
index 25a897bd79..49a9571189 100644
--- a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp
+++ b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp
@@ -16,10 +16,7 @@
#include <utils/algorithm.h>
#include <utils/fancylineedit.h>
-#include <utils/fileutils.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
-#include <utils/stringutils.h>
#include <utils/theme/theme.h>
#include <QApplication>
@@ -29,7 +26,6 @@
#include <QDebug>
#include <QDir>
#include <QFormLayout>
-#include <QFutureWatcher>
#include <QItemSelectionModel>
#include <QLabel>
#include <QListView>
@@ -603,25 +599,21 @@ void LineEditField::setupCompletion(FancyLineEdit *lineEdit)
using namespace Utils;
if (m_completion == Completion::None)
return;
- ILocatorFilter * const classesFilter = findOrDefault(
- ILocatorFilter::allLocatorFilters(),
- equal(&ILocatorFilter::id, Id("Classes")));
- if (!classesFilter)
- return;
- classesFilter->prepareSearch({});
- const auto watcher = new QFutureWatcher<LocatorFilterEntry>;
- const auto handleResults = [this, lineEdit, watcher](int firstIndex, int endIndex) {
+ LocatorMatcher *matcher = new LocatorMatcher;
+ matcher->setParent(lineEdit);
+ matcher->setTasks(LocatorMatcher::matchers(MatcherType::Classes));
+ const auto handleResults = [lineEdit, matcher, completion = m_completion] {
QSet<QString> namespaces;
QStringList classes;
Project * const project = ProjectTree::currentProject();
- for (int i = firstIndex; i < endIndex; ++i) {
+ const LocatorFilterEntries entries = matcher->outputData();
+ for (const LocatorFilterEntry &entry : entries) {
static const auto isReservedName = [](const QString &name) {
static const QRegularExpression rx1("^_[A-Z].*");
static const QRegularExpression rx2(".*::_[A-Z].*");
return name.contains("__") || rx1.match(name).hasMatch()
|| rx2.match(name).hasMatch();
};
- const LocatorFilterEntry &entry = watcher->resultAt(i);
const bool hasNamespace = !entry.extraInfo.isEmpty()
&& !entry.extraInfo.startsWith('<') && !entry.extraInfo.contains("::<")
&& !isReservedName(entry.extraInfo)
@@ -635,7 +627,7 @@ void LineEditField::setupCompletion(FancyLineEdit *lineEdit)
if (hasNamespace) {
if (isBaseClassCandidate)
classes << (entry.extraInfo + "::" + entry.displayName);
- if (m_completion == Completion::Namespaces) {
+ if (completion == Completion::Namespaces) {
if (!project
|| entry.filePath.startsWith(project->projectDirectory().toString())) {
namespaces << entry.extraInfo;
@@ -644,7 +636,7 @@ void LineEditField::setupCompletion(FancyLineEdit *lineEdit)
}
}
QStringList completionList;
- if (m_completion == Completion::Namespaces) {
+ if (completion == Completion::Namespaces) {
completionList = toList(namespaces);
completionList = filtered(completionList, [&classes](const QString &ns) {
return !classes.contains(ns);
@@ -658,16 +650,9 @@ void LineEditField::setupCompletion(FancyLineEdit *lineEdit)
completionList.sort();
lineEdit->setSpecialCompleter(new QCompleter(completionList, lineEdit));
};
- QObject::connect(watcher, &QFutureWatcher<LocatorFilterEntry>::resultsReadyAt, lineEdit,
- handleResults);
- QObject::connect(watcher, &QFutureWatcher<LocatorFilterEntry>::finished,
- watcher, &QFutureWatcher<LocatorFilterEntry>::deleteLater);
- watcher->setFuture(runAsync([classesFilter](QFutureInterface<LocatorFilterEntry> &f) {
- const QList<LocatorFilterEntry> matches = classesFilter->matchesFor(f, {});
- if (!matches.isEmpty())
- f.reportResults(QVector<LocatorFilterEntry>(matches.cbegin(), matches.cend()));
- f.reportFinished();
- }));
+ QObject::connect(matcher, &LocatorMatcher::done, lineEdit, handleResults);
+ QObject::connect(matcher, &LocatorMatcher::done, matcher, &QObject::deleteLater);
+ matcher->start();
}
void LineEditField::setText(const QString &text)
diff --git a/src/plugins/projectexplorer/jsonwizard/jsonsummarypage.cpp b/src/plugins/projectexplorer/jsonwizard/jsonsummarypage.cpp
index 024044d7a0..ec55e3218f 100644
--- a/src/plugins/projectexplorer/jsonwizard/jsonsummarypage.cpp
+++ b/src/plugins/projectexplorer/jsonwizard/jsonsummarypage.cpp
@@ -8,8 +8,8 @@
#include "../projectexplorerconstants.h"
#include "../projectexplorertr.h"
#include "../projectnodes.h"
+#include "../projectmanager.h"
#include "../projecttree.h"
-#include "../session.h"
#include <coreplugin/coreconstants.h>
#include <coreplugin/iversioncontrol.h>
@@ -209,7 +209,7 @@ Node *JsonSummaryPage::findWizardContextNode(Node *contextNode) const
// Static cast from void * to avoid qobject_cast (which needs a valid object) in value().
auto project = static_cast<Project *>(m_wizard->value(Constants::PROJECT_POINTER).value<void *>());
- if (SessionManager::projects().contains(project) && project->rootProjectNode()) {
+ if (ProjectManager::projects().contains(project) && project->rootProjectNode()) {
const FilePath path = FilePath::fromVariant(m_wizard->value(Constants::PREFERRED_PROJECT_NODE_PATH));
contextNode = project->rootProjectNode()->findNode([path](const Node *n) {
return path == n->filePath();
diff --git a/src/plugins/projectexplorer/kitchooser.cpp b/src/plugins/projectexplorer/kitchooser.cpp
index 14c848da74..9e45cacd7d 100644
--- a/src/plugins/projectexplorer/kitchooser.cpp
+++ b/src/plugins/projectexplorer/kitchooser.cpp
@@ -6,7 +6,7 @@
#include "kitmanager.h"
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
-#include "session.h"
+#include "projectmanager.h"
#include "target.h"
#include <coreplugin/icore.h>
@@ -88,7 +88,7 @@ void KitChooser::populate()
const Id lastKit = Id::fromSetting(ICore::settings()->value(lastKitKey));
bool didActivate = false;
- if (Target *target = SessionManager::startupTarget()) {
+ if (Target *target = ProjectManager::startupTarget()) {
Kit *kit = target->kit();
if (m_kitPredicate(kit)) {
QString display = Tr::tr("Kit of Active Project: %1").arg(kitText(kit));
diff --git a/src/plugins/projectexplorer/kitinformation.cpp b/src/plugins/projectexplorer/kitinformation.cpp
index dcb490b70d..fc8039b402 100644
--- a/src/plugins/projectexplorer/kitinformation.cpp
+++ b/src/plugins/projectexplorer/kitinformation.cpp
@@ -34,7 +34,6 @@
#include <QVBoxLayout>
using namespace Utils;
-using namespace Utils::Layouting;
namespace ProjectExplorer {
@@ -65,7 +64,7 @@ public:
private:
void makeReadOnly() override { m_chooser->setReadOnly(true); }
- void addToLayout(LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &builder) override
{
addMutableAction(m_chooser);
builder.addItem(Layouting::Span(2, m_chooser));
@@ -142,7 +141,7 @@ void SysRootKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) con
});
}
-Id SysRootKitAspect::id()
+Utils::Id SysRootKitAspect::id()
{
return "PE.Profile.SysRoot";
}
@@ -231,7 +230,7 @@ public:
}
private:
- void addToLayout(LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &builder) override
{
addMutableAction(m_mainWidget);
builder.addItem(m_mainWidget);
@@ -760,7 +759,7 @@ public:
~DeviceTypeKitAspectWidget() override { delete m_comboBox; }
private:
- void addToLayout(LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &builder) override
{
addMutableAction(m_comboBox);
builder.addItem(m_comboBox);
@@ -795,7 +794,7 @@ DeviceTypeKitAspect::DeviceTypeKitAspect()
{
setObjectName(QLatin1String("DeviceTypeInformation"));
setId(DeviceTypeKitAspect::id());
- setDisplayName(Tr::tr("Device type"));
+ setDisplayName(Tr::tr("Run device type"));
setDescription(Tr::tr("The type of device to run applications on."));
setPriority(33000);
makeEssential();
@@ -896,7 +895,7 @@ public:
}
private:
- void addToLayout(LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &builder) override
{
addMutableAction(m_comboBox);
builder.addItem(m_comboBox);
@@ -942,7 +941,7 @@ DeviceKitAspect::DeviceKitAspect()
{
setObjectName(QLatin1String("DeviceInformation"));
setId(DeviceKitAspect::id());
- setDisplayName(Tr::tr("Device"));
+ setDisplayName(Tr::tr("Run device"));
setDescription(Tr::tr("The device to run the applications on."));
setPriority(32000);
@@ -1023,30 +1022,29 @@ KitAspect::ItemList DeviceKitAspect::toUserOutput(const Kit *k) const
void DeviceKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) const
{
QTC_ASSERT(kit, return);
- expander->registerVariable("Device:HostAddress", Tr::tr("Host address"),
- [kit]() -> QString {
- const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
- return device ? device->sshParameters().host() : QString();
+ expander->registerVariable("Device:HostAddress", Tr::tr("Host address"), [kit] {
+ const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
+ return device ? device->sshParameters().host() : QString();
});
- expander->registerVariable("Device:SshPort", Tr::tr("SSH port"),
- [kit]() -> QString {
- const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
- return device ? QString::number(device->sshParameters().port()) : QString();
+ expander->registerVariable("Device:SshPort", Tr::tr("SSH port"), [kit] {
+ const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
+ return device ? QString::number(device->sshParameters().port()) : QString();
});
- expander->registerVariable("Device:UserName", Tr::tr("User name"),
- [kit]() -> QString {
- const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
- return device ? device->sshParameters().userName() : QString();
+ expander->registerVariable("Device:UserName", Tr::tr("User name"), [kit] {
+ const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
+ return device ? device->sshParameters().userName() : QString();
});
- expander->registerVariable("Device:KeyFile", Tr::tr("Private key file"),
- [kit]() -> QString {
- const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
- return device ? device->sshParameters().privateKeyFile.toString() : QString();
+ expander->registerVariable("Device:KeyFile", Tr::tr("Private key file"), [kit] {
+ const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
+ return device ? device->sshParameters().privateKeyFile.toString() : QString();
});
- expander->registerVariable("Device:Name", Tr::tr("Device name"),
- [kit]() -> QString {
- const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
- return device ? device->displayName() : QString();
+ expander->registerVariable("Device:Name", Tr::tr("Device name"), [kit] {
+ const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
+ return device ? device->displayName() : QString();
+ });
+ expander->registerFileVariables("Device::Root", Tr::tr("Device root directory"), [kit] {
+ const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
+ return device ? device->rootPath() : FilePath{};
});
}
@@ -1157,7 +1155,7 @@ public:
}
private:
- void addToLayout(LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &builder) override
{
addMutableAction(m_comboBox);
builder.addItem(m_comboBox);
@@ -1266,31 +1264,31 @@ KitAspect::ItemList BuildDeviceKitAspect::toUserOutput(const Kit *k) const
void BuildDeviceKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) const
{
QTC_ASSERT(kit, return);
- expander->registerVariable("BuildDevice:HostAddress", Tr::tr("Build host address"),
- [kit]() -> QString {
- const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
- return device ? device->sshParameters().host() : QString();
+ expander->registerVariable("BuildDevice:HostAddress", Tr::tr("Build host address"), [kit] {
+ const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
+ return device ? device->sshParameters().host() : QString();
});
- expander->registerVariable("BuildDevice:SshPort", Tr::tr("Build SSH port"),
- [kit]() -> QString {
- const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
- return device ? QString::number(device->sshParameters().port()) : QString();
+ expander->registerVariable("BuildDevice:SshPort", Tr::tr("Build SSH port"), [kit] {
+ const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
+ return device ? QString::number(device->sshParameters().port()) : QString();
});
- expander->registerVariable("BuildDevice:UserName", Tr::tr("Build user name"),
- [kit]() -> QString {
- const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
- return device ? device->sshParameters().userName() : QString();
+ expander->registerVariable("BuildDevice:UserName", Tr::tr("Build user name"), [kit] {
+ const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
+ return device ? device->sshParameters().userName() : QString();
});
- expander->registerVariable("BuildDevice:KeyFile", Tr::tr("Build private key file"),
- [kit]() -> QString {
- const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
- return device ? device->sshParameters().privateKeyFile.toString() : QString();
+ expander->registerVariable("BuildDevice:KeyFile", Tr::tr("Build private key file"), [kit] {
+ const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
+ return device ? device->sshParameters().privateKeyFile.toString() : QString();
});
- expander->registerVariable("BuildDevice:Name", Tr::tr("Build device name"),
- [kit]() -> QString {
- const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
- return device ? device->displayName() : QString();
+ expander->registerVariable("BuildDevice:Name", Tr::tr("Build device name"), [kit] {
+ const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
+ return device ? device->displayName() : QString();
});
+ expander
+ ->registerFileVariables("BuildDevice::Root", Tr::tr("Build device root directory"), [kit] {
+ const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
+ return device ? device->rootPath() : FilePath{};
+ });
}
Id BuildDeviceKitAspect::id()
@@ -1388,7 +1386,7 @@ public:
}
private:
- void addToLayout(LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &builder) override
{
addMutableAction(m_mainWidget);
builder.addItem(m_mainWidget);
diff --git a/src/plugins/projectexplorer/kitmanager.cpp b/src/plugins/projectexplorer/kitmanager.cpp
index cb67e11fac..86a3b533c1 100644
--- a/src/plugins/projectexplorer/kitmanager.cpp
+++ b/src/plugins/projectexplorer/kitmanager.cpp
@@ -747,7 +747,7 @@ KitAspectWidget::~KitAspectWidget()
delete m_mutableAction;
}
-void KitAspectWidget::addToLayoutWithLabel(QWidget *parent)
+void KitAspectWidget::addToLayoutWithLabel(Layouting::LayoutItem &parentItem, QWidget *parent)
{
QTC_ASSERT(parent, return);
auto label = createSubWidget<QLabel>(m_kitInformation->displayName() + ':');
@@ -756,10 +756,9 @@ void KitAspectWidget::addToLayoutWithLabel(QWidget *parent)
emit labelLinkActivated(link);
});
- Layouting::LayoutExtender builder(parent->layout(), Layouting::WithFormAlignment);
- builder.finishRow();
- builder.addItem(label);
- addToLayout(builder);
+ parentItem.addItem(label);
+ addToLayout(parentItem);
+ parentItem.addItem(Layouting::br);
}
void KitAspectWidget::addMutableAction(QWidget *child)
diff --git a/src/plugins/projectexplorer/kitmanager.h b/src/plugins/projectexplorer/kitmanager.h
index a494fde323..f25f83ca7f 100644
--- a/src/plugins/projectexplorer/kitmanager.h
+++ b/src/plugins/projectexplorer/kitmanager.h
@@ -117,7 +117,7 @@ public:
virtual void makeReadOnly() = 0;
virtual void refresh() = 0;
- void addToLayoutWithLabel(QWidget *parent);
+ void addToLayoutWithLabel(Layouting::LayoutItem &parentItem, QWidget *parent);
static QString msgManage();
diff --git a/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp b/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp
index 908238d3a7..822844f57d 100644
--- a/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp
+++ b/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp
@@ -23,6 +23,7 @@
#include <QRegularExpression>
#include <QRegularExpressionValidator>
#include <QFileDialog>
+#include <QGridLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMenu>
@@ -36,12 +37,14 @@ using namespace Utils;
namespace ProjectExplorer {
namespace Internal {
-KitManagerConfigWidget::KitManagerConfigWidget(Kit *k) :
+KitManagerConfigWidget::KitManagerConfigWidget(Kit *k, bool &isDefaultKit, bool &hasUniqueName) :
m_iconButton(new QToolButton),
m_nameEdit(new QLineEdit),
m_fileSystemFriendlyNameLineEdit(new QLineEdit),
m_kit(k),
- m_modifiedKit(std::make_unique<Kit>(Utils::Id(WORKING_COPY_KIT_ID)))
+ m_modifiedKit(std::make_unique<Kit>(Utils::Id(WORKING_COPY_KIT_ID))),
+ m_isDefaultKit(isDefaultKit),
+ m_hasUniqueName(hasUniqueName)
{
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
@@ -64,10 +67,13 @@ KitManagerConfigWidget::KitManagerConfigWidget(Kit *k) :
this, &KitManagerConfigWidget::setFileSystemFriendlyName);
using namespace Layouting;
- Grid {
+ Grid page {
+ withFormAlignment,
+ columnStretch(1, 2),
label, m_nameEdit, m_iconButton, br,
- fsLabel, m_fileSystemFriendlyNameLineEdit
- }.attachTo(this, WithFormAlignment);
+ fsLabel, m_fileSystemFriendlyNameLineEdit, br,
+ noMargin
+ };
m_iconButton->setToolTip(Tr::tr("Kit icon."));
auto setIconAction = new QAction(Tr::tr("Select Icon..."), this);
@@ -97,7 +103,9 @@ KitManagerConfigWidget::KitManagerConfigWidget(Kit *k) :
chooser->addMacroExpanderProvider([this] { return m_modifiedKit->macroExpander(); });
for (KitAspect *aspect : KitManager::kitAspects())
- addAspectToWorkingCopy(aspect);
+ addAspectToWorkingCopy(page, aspect);
+
+ page.attachTo(this);
updateVisibility();
@@ -187,14 +195,14 @@ QString KitManagerConfigWidget::validityMessage() const
return m_modifiedKit->toHtml(tmp);
}
-void KitManagerConfigWidget::addAspectToWorkingCopy(KitAspect *aspect)
+void KitManagerConfigWidget::addAspectToWorkingCopy(Layouting::LayoutItem &parent, KitAspect *aspect)
{
QTC_ASSERT(aspect, return);
KitAspectWidget *widget = aspect->createConfigWidget(workingCopy());
QTC_ASSERT(widget, return);
QTC_ASSERT(!m_widgets.contains(widget), return);
- widget->addToLayoutWithLabel(this);
+ widget->addToLayoutWithLabel(parent, this);
m_widgets.append(widget);
connect(widget->mutableAction(), &QAction::toggled,
@@ -213,11 +221,6 @@ void KitManagerConfigWidget::updateVisibility()
}
}
-void KitManagerConfigWidget::setHasUniqueName(bool unique)
-{
- m_hasUniqueName = unique;
-}
-
void KitManagerConfigWidget::makeStickySubWidgetsReadOnly()
{
for (KitAspectWidget *w : std::as_const(m_widgets)) {
@@ -231,31 +234,11 @@ Kit *KitManagerConfigWidget::workingCopy() const
return m_modifiedKit.get();
}
-bool KitManagerConfigWidget::configures(Kit *k) const
-{
- return m_kit == k;
-}
-
-void KitManagerConfigWidget::setIsDefaultKit(bool d)
-{
- if (m_isDefaultKit == d)
- return;
- m_isDefaultKit = d;
- emit dirty();
-}
-
bool KitManagerConfigWidget::isDefaultKit() const
{
return m_isDefaultKit;
}
-void KitManagerConfigWidget::removeKit()
-{
- if (!m_kit)
- return;
- KitManager::deregisterKit(m_kit);
-}
-
void KitManagerConfigWidget::setIcon()
{
const Utils::Id deviceType = DeviceTypeKitAspect::deviceTypeId(m_modifiedKit.get());
diff --git a/src/plugins/projectexplorer/kitmanagerconfigwidget.h b/src/plugins/projectexplorer/kitmanagerconfigwidget.h
index 142eb8fa36..94218c5de8 100644
--- a/src/plugins/projectexplorer/kitmanagerconfigwidget.h
+++ b/src/plugins/projectexplorer/kitmanagerconfigwidget.h
@@ -25,7 +25,7 @@ class KitManagerConfigWidget : public QWidget
Q_OBJECT
public:
- explicit KitManagerConfigWidget(Kit *k);
+ explicit KitManagerConfigWidget(Kit *k, bool &isDefaultKit, bool &hasUniqueName);
~KitManagerConfigWidget() override;
QString displayName() const;
@@ -35,19 +35,14 @@ public:
void discard();
bool isDirty() const;
QString validityMessage() const;
- void addAspectToWorkingCopy(KitAspect *aspect);
+ void addAspectToWorkingCopy(Layouting::LayoutItem &parent, KitAspect *aspect);
void makeStickySubWidgetsReadOnly();
Kit *workingCopy() const;
- bool configures(Kit *k) const;
bool isRegistering() const { return m_isRegistering; }
- void setIsDefaultKit(bool d);
bool isDefaultKit() const;
- void removeKit();
void updateVisibility();
- void setHasUniqueName(bool unique);
-
signals:
void dirty();
void isAutoDetectedChanged();
@@ -74,9 +69,9 @@ private:
QList<KitAspectWidget *> m_widgets;
Kit *m_kit;
std::unique_ptr<Kit> m_modifiedKit;
- bool m_isDefaultKit = false;
+ bool &m_isDefaultKit;
bool m_fixingKit = false;
- bool m_hasUniqueName = true;
+ bool &m_hasUniqueName;
bool m_isRegistering = false;
mutable QString m_cachedDisplayName;
};
diff --git a/src/plugins/projectexplorer/kitmodel.cpp b/src/plugins/projectexplorer/kitmodel.cpp
index 4b250f23c9..12f23fb281 100644
--- a/src/plugins/projectexplorer/kitmodel.cpp
+++ b/src/plugins/projectexplorer/kitmodel.cpp
@@ -24,57 +24,122 @@ namespace Internal {
class KitNode : public TreeItem
{
public:
- KitNode(Kit *k, KitModel *m)
+ KitNode(Kit *k, KitModel *m, QBoxLayout *parentLayout)
+ : m_kit(k), m_model(m), m_parentLayout(parentLayout)
+ {}
+
+ ~KitNode() override { delete m_widget; }
+
+ Kit *kit() const { return m_kit; }
+
+ QVariant data(int, int role) const override
{
- widget = new KitManagerConfigWidget(k);
+ if (role == Qt::FontRole) {
+ QFont f = QApplication::font();
+ if (isDirty())
+ f.setBold(!f.bold());
+ if (isDefaultKit())
+ f.setItalic(f.style() != QFont::StyleItalic);
+ return f;
+ }
+ if (role == Qt::DisplayRole) {
+ QString baseName = displayName();
+ if (isDefaultKit())
+ //: Mark up a kit as the default one.
+ baseName = Tr::tr("%1 (default)").arg(baseName);
+ return baseName;
+ }
- QObject::connect(widget, &KitManagerConfigWidget::dirty, m, [this] { update(); });
+ if (role == Qt::DecorationRole)
+ return displayIcon();
- QObject::connect(widget, &KitManagerConfigWidget::isAutoDetectedChanged, m, [this, m] {
- TreeItem *oldParent = parent();
- TreeItem *newParent =
- m->rootItem()->childAt(widget->workingCopy()->isAutoDetected() ? 0 : 1);
- if (oldParent && oldParent != newParent) {
- m->takeItem(this);
- newParent->appendChild(this);
- }
- });
+ if (role == Qt::ToolTipRole)
+ return widget()->validityMessage();
+
+ return {};
}
- ~KitNode() override
+ bool isDirty() const
{
- delete widget;
+ if (m_widget)
+ return m_widget->isDirty();
+ return false;
}
- QVariant data(int, int role) const override
+ QIcon displayIcon() const
{
- if (widget) {
- if (role == Qt::FontRole) {
- QFont f = QApplication::font();
- if (widget->isDirty())
- f.setBold(!f.bold());
- if (widget->isDefaultKit())
- f.setItalic(f.style() != QFont::StyleItalic);
- return f;
- }
- if (role == Qt::DisplayRole) {
- QString baseName = widget->displayName();
- if (widget->isDefaultKit())
- //: Mark up a kit as the default one.
- baseName = Tr::tr("%1 (default)").arg(baseName);
- return baseName;
- }
- if (role == Qt::DecorationRole) {
- return widget->displayIcon();
- }
- if (role == Qt::ToolTipRole) {
- return widget->validityMessage();
+ if (m_widget)
+ return m_widget->displayIcon();
+ return m_kit->displayIcon();
+ }
+
+ QString displayName() const
+ {
+ if (m_widget)
+ return m_widget->displayName();
+ return m_kit->displayName();
+ }
+
+ bool isDefaultKit() const
+ {
+ return m_isDefaultKit;
+ }
+
+ bool isRegistering() const
+ {
+ if (m_widget)
+ return m_widget->isRegistering();
+ return false;
+ }
+
+ void setIsDefaultKit(bool on)
+ {
+ if (m_isDefaultKit == on)
+ return;
+ m_isDefaultKit = on;
+ if (m_widget)
+ emit m_widget->dirty();
+ }
+
+ KitManagerConfigWidget *widget() const
+ {
+ const_cast<KitNode *>(this)->ensureWidget();
+ return m_widget;
+ }
+
+ void setHasUniqueName(bool on)
+ {
+ m_hasUniqueName = on;
+ }
+
+private:
+ void ensureWidget()
+ {
+ if (m_widget)
+ return;
+
+ m_widget = new KitManagerConfigWidget(m_kit, m_isDefaultKit, m_hasUniqueName);
+
+ QObject::connect(m_widget, &KitManagerConfigWidget::dirty, m_model, [this] { update(); });
+
+ QObject::connect(m_widget, &KitManagerConfigWidget::isAutoDetectedChanged, m_model, [this] {
+ TreeItem *oldParent = parent();
+ TreeItem *newParent =
+ m_model->rootItem()->childAt(m_widget->workingCopy()->isAutoDetected() ? 0 : 1);
+ if (oldParent && oldParent != newParent) {
+ m_model->takeItem(this);
+ newParent->appendChild(this);
}
- }
- return QVariant();
+ });
+ m_parentLayout->addWidget(m_widget);
}
- KitManagerConfigWidget *widget;
+ Kit *m_kit = m_kit;
+ KitModel *m_model = nullptr;
+ KitManagerConfigWidget *m_widget = nullptr;
+ QBoxLayout *m_parentLayout = nullptr;
+ bool m_isDefaultKit = false;
+ bool m_hasUniqueName = true;
};
// --------------------------------------------------------------------------
@@ -113,7 +178,7 @@ KitModel::KitModel(QBoxLayout *parentLayout, QObject *parent)
Kit *KitModel::kit(const QModelIndex &index)
{
KitNode *n = kitNode(index);
- return n ? n->widget->workingCopy() : nullptr;
+ return n ? n->widget()->workingCopy() : nullptr;
}
KitNode *KitModel::kitNode(const QModelIndex &index)
@@ -136,20 +201,20 @@ void KitModel::setDefaultKit(const QModelIndex &index)
bool KitModel::isDefaultKit(Kit *k) const
{
- return m_defaultNode && m_defaultNode->widget->workingCopy() == k;
+ return m_defaultNode && m_defaultNode->widget()->workingCopy() == k;
}
KitManagerConfigWidget *KitModel::widget(const QModelIndex &index)
{
KitNode *n = kitNode(index);
- return n ? n->widget : nullptr;
+ return n ? n->widget() : nullptr;
}
void KitModel::validateKitNames()
{
QHash<QString, int> nameHash;
forItemsAtLevel<2>([&nameHash](KitNode *n) {
- const QString displayName = n->widget->displayName();
+ const QString displayName = n->displayName();
if (nameHash.contains(displayName))
++nameHash[displayName];
else
@@ -157,8 +222,8 @@ void KitModel::validateKitNames()
});
forItemsAtLevel<2>([&nameHash](KitNode *n) {
- const QString displayName = n->widget->displayName();
- n->widget->setHasUniqueName(nameHash.value(displayName) == 1);
+ const QString displayName = n->displayName();
+ n->setHasUniqueName(nameHash.value(displayName) == 1);
});
}
@@ -166,8 +231,8 @@ void KitModel::apply()
{
// Add/update dirty nodes before removing kits. This ensures the right kit ends up as default.
forItemsAtLevel<2>([](KitNode *n) {
- if (n->widget->isDirty()) {
- n->widget->apply();
+ if (n->isDirty()) {
+ n->widget()->apply();
n->update();
}
});
@@ -175,7 +240,7 @@ void KitModel::apply()
// Remove unused kits:
const QList<KitNode *> removeList = m_toRemoveList;
for (KitNode *n : removeList)
- n->widget->removeKit();
+ KitManager::deregisterKit(n->kit());
emit layoutChanged(); // Force update.
}
@@ -197,7 +262,7 @@ void KitModel::markForRemoval(Kit *k)
setDefaultNode(findItemAtLevel<2>([node](KitNode *kn) { return kn != node; }));
takeItem(node);
- if (node->widget->configures(nullptr))
+ if (node->kit() == nullptr)
delete node;
else
m_toRemoveList.append(node);
@@ -209,7 +274,7 @@ Kit *KitModel::markForAddition(Kit *baseKit)
const QString newName = newKitName(baseKit ? baseKit->unexpandedDisplayName() : QString());
KitNode *node = createNode(nullptr);
m_manualRoot->appendChild(node);
- Kit *k = node->widget->workingCopy();
+ Kit *k = node->widget()->workingCopy();
KitGuard g(k);
if (baseKit) {
k->copyFrom(baseKit);
@@ -229,7 +294,7 @@ Kit *KitModel::markForAddition(Kit *baseKit)
void KitModel::updateVisibility()
{
forItemsAtLevel<2>([](const TreeItem *ti) {
- static_cast<const KitNode *>(ti)->widget->updateVisibility();
+ static_cast<const KitNode *>(ti)->widget()->updateVisibility();
});
}
@@ -237,32 +302,31 @@ QString KitModel::newKitName(const QString &sourceName) const
{
QList<Kit *> allKits;
forItemsAtLevel<2>([&allKits](const TreeItem *ti) {
- allKits << static_cast<const KitNode *>(ti)->widget->workingCopy();
+ allKits << static_cast<const KitNode *>(ti)->widget()->workingCopy();
});
return Kit::newKitName(sourceName, allKits);
}
KitNode *KitModel::findWorkingCopy(Kit *k) const
{
- return findItemAtLevel<2>([k](KitNode *n) { return n->widget->workingCopy() == k; });
+ return findItemAtLevel<2>([k](KitNode *n) { return n->widget()->workingCopy() == k; });
}
KitNode *KitModel::createNode(Kit *k)
{
- auto node = new KitNode(k, this);
- m_parentLayout->addWidget(node->widget);
+ auto node = new KitNode(k, this, m_parentLayout);
return node;
}
void KitModel::setDefaultNode(KitNode *node)
{
if (m_defaultNode) {
- m_defaultNode->widget->setIsDefaultKit(false);
+ m_defaultNode->setIsDefaultKit(false);
m_defaultNode->update();
}
m_defaultNode = node;
if (m_defaultNode) {
- m_defaultNode->widget->setIsDefaultKit(true);
+ m_defaultNode->setIsDefaultKit(true);
m_defaultNode->update();
}
}
@@ -271,7 +335,7 @@ void KitModel::addKit(Kit *k)
{
for (TreeItem *n : *m_manualRoot) {
// Was added by us
- if (static_cast<KitNode *>(n)->widget->isRegistering())
+ if (static_cast<KitNode *>(n)->isRegistering())
return;
}
@@ -292,7 +356,7 @@ void KitModel::removeKit(Kit *k)
{
QList<KitNode *> nodes = m_toRemoveList;
for (KitNode *n : std::as_const(nodes)) {
- if (n->widget->configures(k)) {
+ if (n->kit() == k) {
m_toRemoveList.removeOne(n);
if (m_defaultNode == n)
m_defaultNode = nullptr;
@@ -303,7 +367,7 @@ void KitModel::removeKit(Kit *k)
}
KitNode *node = findItemAtLevel<2>([k](KitNode *n) {
- return n->widget->configures(k);
+ return n->kit() == k;
});
if (node == m_defaultNode)
@@ -319,7 +383,7 @@ void KitModel::changeDefaultKit()
{
Kit *defaultKit = KitManager::defaultKit();
KitNode *node = findItemAtLevel<2>([defaultKit](KitNode *n) {
- return n->widget->configures(defaultKit);
+ return n->kit() == defaultKit;
});
setDefaultNode(node);
}
diff --git a/src/plugins/projectexplorer/kitoptionspage.cpp b/src/plugins/projectexplorer/kitoptionspage.cpp
index d5c709194a..db578a403e 100644
--- a/src/plugins/projectexplorer/kitoptionspage.cpp
+++ b/src/plugins/projectexplorer/kitoptionspage.cpp
@@ -23,11 +23,13 @@
namespace ProjectExplorer {
namespace Internal {
-// --------------------------------------------------------------------------
-// KitOptionsPageWidget:
-// --------------------------------------------------------------------------
+// KitOptionsPageWidget
+
+class KitOptionsPageWidget;
-class KitOptionsPageWidget : public QWidget
+static QPointer<KitOptionsPageWidget> kitOptionsPageWidget;
+
+class KitOptionsPageWidget : public Core::IOptionsPageWidget
{
public:
KitOptionsPageWidget();
@@ -42,6 +44,8 @@ public:
void makeDefaultKit();
void updateState();
+ void apply() final { m_model->apply(); }
+
public:
QTreeView *m_kitsView = nullptr;
QPushButton *m_addButton = nullptr;
@@ -58,6 +62,7 @@ public:
KitOptionsPageWidget::KitOptionsPageWidget()
{
+ kitOptionsPageWidget = this;
m_kitsView = new QTreeView(this);
m_kitsView->setUniformRowHeights(true);
m_kitsView->header()->setStretchLastSection(true);
@@ -239,38 +244,14 @@ QModelIndex KitOptionsPageWidget::currentIndex() const
// KitOptionsPage:
// --------------------------------------------------------------------------
-static KitOptionsPage *theKitOptionsPage = nullptr;
-
KitOptionsPage::KitOptionsPage()
{
- theKitOptionsPage = this;
setId(Constants::KITS_SETTINGS_PAGE_ID);
setDisplayName(Tr::tr("Kits"));
setCategory(Constants::KITS_SETTINGS_CATEGORY);
setDisplayCategory(Tr::tr("Kits"));
setCategoryIconPath(":/projectexplorer/images/settingscategory_kits.png");
-}
-
-QWidget *KitOptionsPage::widget()
-{
- if (!m_widget)
- m_widget = new Internal::KitOptionsPageWidget;
-
- return m_widget;
-}
-
-void KitOptionsPage::apply()
-{
- if (m_widget)
- m_widget->m_model->apply();
-}
-
-void KitOptionsPage::finish()
-{
- if (m_widget) {
- delete m_widget;
- m_widget = nullptr;
- }
+ setWidgetCreator([] { return new Internal::KitOptionsPageWidget; });
}
void KitOptionsPage::showKit(Kit *k)
@@ -278,18 +259,16 @@ void KitOptionsPage::showKit(Kit *k)
if (!k)
return;
- (void) widget();
- QModelIndex index = m_widget->m_model->indexOf(k);
- m_widget->m_selectionModel->select(index,
+ Internal::KitOptionsPageWidget *widget = Internal::kitOptionsPageWidget;
+ if (!widget)
+ return;
+
+ QModelIndex index = widget->m_model->indexOf(k);
+ widget->m_selectionModel->select(index,
QItemSelectionModel::Clear
| QItemSelectionModel::SelectCurrent
| QItemSelectionModel::Rows);
- m_widget->m_kitsView->scrollTo(index);
-}
-
-KitOptionsPage *KitOptionsPage::instance()
-{
- return theKitOptionsPage;
+ widget->m_kitsView->scrollTo(index);
}
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/kitoptionspage.h b/src/plugins/projectexplorer/kitoptionspage.h
index 5ce589cd6b..a7a4ba29db 100644
--- a/src/plugins/projectexplorer/kitoptionspage.h
+++ b/src/plugins/projectexplorer/kitoptionspage.h
@@ -7,12 +7,8 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <QPointer>
-
namespace ProjectExplorer {
-namespace Internal { class KitOptionsPageWidget; }
-
class Kit;
class PROJECTEXPLORER_EXPORT KitOptionsPage : public Core::IOptionsPage
@@ -20,15 +16,7 @@ class PROJECTEXPLORER_EXPORT KitOptionsPage : public Core::IOptionsPage
public:
KitOptionsPage();
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
- void showKit(Kit *k);
- static KitOptionsPage *instance();
-
-private:
- QPointer<Internal::KitOptionsPageWidget> m_widget;
+ static void showKit(Kit *k);
};
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/localenvironmentaspect.cpp b/src/plugins/projectexplorer/localenvironmentaspect.cpp
deleted file mode 100644
index 7510d636e5..0000000000
--- a/src/plugins/projectexplorer/localenvironmentaspect.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "localenvironmentaspect.h"
-
-#include "buildconfiguration.h"
-#include "kit.h"
-#include "projectexplorertr.h"
-#include "target.h"
-
-using namespace Utils;
-
-namespace ProjectExplorer {
-
-LocalEnvironmentAspect::LocalEnvironmentAspect(Target *target, bool includeBuildEnvironment)
-{
- setIsLocal(true);
- addSupportedBaseEnvironment(Tr::tr("Clean Environment"), {});
-
- addSupportedBaseEnvironment(Tr::tr("System Environment"), [] {
- return Environment::systemEnvironment();
- });
-
- if (includeBuildEnvironment) {
- addPreferredBaseEnvironment(Tr::tr("Build Environment"), [target] {
- Environment env;
- if (BuildConfiguration *bc = target->activeBuildConfiguration()) {
- env = bc->environment();
- } else { // Fallback for targets without buildconfigurations:
- env = target->kit()->buildEnvironment();
- }
- return env;
- });
-
- connect(target,
- &Target::activeBuildConfigurationChanged,
- this,
- &EnvironmentAspect::environmentChanged);
- connect(target,
- &Target::buildEnvironmentChanged,
- this,
- &EnvironmentAspect::environmentChanged);
- }
-}
-
-} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/localenvironmentaspect.h b/src/plugins/projectexplorer/localenvironmentaspect.h
deleted file mode 100644
index ffe2556947..0000000000
--- a/src/plugins/projectexplorer/localenvironmentaspect.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "environmentaspect.h"
-
-namespace ProjectExplorer {
-
-class PROJECTEXPLORER_EXPORT LocalEnvironmentAspect : public EnvironmentAspect
-{
- Q_OBJECT
-
-public:
- explicit LocalEnvironmentAspect(Target *parent, bool includeBuildEnvironment = true);
-};
-
-} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/makestep.cpp b/src/plugins/projectexplorer/makestep.cpp
index 57ebc2490b..ee9ffea832 100644
--- a/src/plugins/projectexplorer/makestep.cpp
+++ b/src/plugins/projectexplorer/makestep.cpp
@@ -18,13 +18,10 @@
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/utilsicons.h>
#include <utils/variablechooser.h>
-#include <QCheckBox>
-#include <QLabel>
-#include <QLineEdit>
#include <QThread>
#include <optional>
@@ -49,9 +46,8 @@ MakeStep::MakeStep(BuildStepList *parent, Id id)
setCommandLineProvider([this] { return effectiveMakeCommand(Execution); });
- m_makeCommandAspect = addAspect<StringAspect>();
+ m_makeCommandAspect = addAspect<FilePathAspect>();
m_makeCommandAspect->setSettingsKey(id.withSuffix(MAKE_COMMAND_SUFFIX).toString());
- m_makeCommandAspect->setDisplayStyle(StringAspect::PathChooserDisplay);
m_makeCommandAspect->setExpectedKind(PathChooser::ExistingCommand);
m_makeCommandAspect->setBaseFileName(PathChooser::homePath());
m_makeCommandAspect->setHistoryCompleter("PE.MakeCommand.History");
@@ -318,14 +314,15 @@ CommandLine MakeStep::effectiveMakeCommand(MakeCommandType type) const
QWidget *MakeStep::createConfigWidget()
{
Layouting::Form builder;
- builder.addRow(m_makeCommandAspect);
- builder.addRow(m_userArgumentsAspect);
+ builder.addRow({m_makeCommandAspect});
+ builder.addRow({m_userArgumentsAspect});
builder.addRow({m_userJobCountAspect, m_overrideMakeflagsAspect, m_nonOverrideWarning});
if (m_disablingForSubDirsSupported)
- builder.addRow(m_disabledForSubdirsAspect);
- builder.addRow(m_buildTargetsAspect);
+ builder.addRow({m_disabledForSubdirsAspect});
+ builder.addRow({m_buildTargetsAspect});
+ builder.addItem(Layouting::noMargin);
- auto widget = builder.emerge(Layouting::WithoutMargins);
+ auto widget = builder.emerge();
VariableChooser::addSupportForChildWidgets(widget, macroExpander());
@@ -383,22 +380,6 @@ QWidget *MakeStep::createConfigWidget()
return widget;
}
-bool MakeStep::buildsTarget(const QString &target) const
-{
- return m_buildTargetsAspect->value().contains(target);
-}
-
-void MakeStep::setBuildTarget(const QString &target, bool on)
-{
- QStringList old = m_buildTargetsAspect->value();
- if (on && !old.contains(target))
- old << target;
- else if (!on && old.contains(target))
- old.removeOne(target);
-
- m_buildTargetsAspect->setValue(old);
-}
-
QStringList MakeStep::availableTargets() const
{
return m_buildTargetsAspect->allValues();
diff --git a/src/plugins/projectexplorer/makestep.h b/src/plugins/projectexplorer/makestep.h
index b08462db2f..2f12894ea6 100644
--- a/src/plugins/projectexplorer/makestep.h
+++ b/src/plugins/projectexplorer/makestep.h
@@ -55,11 +55,6 @@ public:
Utils::Environment makeEnvironment() const;
- // FIXME: All unused, remove in 4.15.
- void setBuildTarget(const QString &buildTarget) { setSelectedBuildTarget(buildTarget); }
- bool buildsTarget(const QString &target) const;
- void setBuildTarget(const QString &target, bool on);
-
protected:
void supportDisablingForSubdirs() { m_disablingForSubDirsSupported = true; }
virtual QStringList displayArguments() const;
@@ -78,7 +73,6 @@ private:
QStringList jobArguments() const;
Utils::MultiSelectionAspect *m_buildTargetsAspect = nullptr;
- QStringList m_availableTargets; // FIXME: Unused, remove in 4.15.
Utils::StringAspect *m_makeCommandAspect = nullptr;
Utils::StringAspect *m_userArgumentsAspect = nullptr;
Utils::IntegerAspect *m_userJobCountAspect = nullptr;
diff --git a/src/plugins/projectexplorer/miniprojecttargetselector.cpp b/src/plugins/projectexplorer/miniprojecttargetselector.cpp
index b169697cd8..3a1b01b724 100644
--- a/src/plugins/projectexplorer/miniprojecttargetselector.cpp
+++ b/src/plugins/projectexplorer/miniprojecttargetselector.cpp
@@ -13,8 +13,8 @@
#include "projectexplorerconstants.h"
#include "projectexplorericons.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "runconfiguration.h"
-#include "session.h"
#include "target.h"
#include <utils/algorithm.h>
@@ -32,6 +32,7 @@
#include <coreplugin/modemanager.h>
#include <QAction>
+#include <QCollator>
#include <QGuiApplication>
#include <QItemDelegate>
#include <QKeyEvent>
@@ -141,8 +142,15 @@ private:
static bool compareItems(const TreeItem *ti1, const TreeItem *ti2)
{
- const int result = caseFriendlyCompare(static_cast<const GenericItem *>(ti1)->rawDisplayName(),
- static_cast<const GenericItem *>(ti2)->rawDisplayName());
+ static const QCollator collator = [] {
+ QCollator collator;
+ collator.setNumericMode(true);
+ collator.setCaseSensitivity(Qt::CaseInsensitive);
+ return collator;
+ }();
+
+ const int result = collator.compare(static_cast<const GenericItem *>(ti1)->rawDisplayName(),
+ static_cast<const GenericItem *>(ti2)->rawDisplayName());
if (result != 0)
return result < 0;
return ti1 < ti2;
@@ -253,9 +261,9 @@ public:
explicit ProjectListView(QWidget *parent = nullptr) : SelectorView(parent)
{
const auto model = new GenericModel(this);
- model->rebuild(transform<QList<QObject *>>(SessionManager::projects(),
+ model->rebuild(transform<QList<QObject *>>(ProjectManager::projects(),
[](Project *p) { return p; }));
- connect(SessionManager::instance(), &SessionManager::projectAdded,
+ connect(ProjectManager::instance(), &ProjectManager::projectAdded,
this, [this, model](Project *project) {
const GenericItem *projectItem = model->addItemForObject(project);
QFontMetrics fn(font());
@@ -264,7 +272,7 @@ public:
setOptimalWidth(width);
restoreCurrentIndex();
});
- connect(SessionManager::instance(), &SessionManager::aboutToRemoveProject,
+ connect(ProjectManager::instance(), &ProjectManager::aboutToRemoveProject,
this, [this, model](const Project *project) {
GenericItem * const item = model->itemForObject(project);
if (!item)
@@ -272,7 +280,7 @@ public:
model->destroyItem(item);
resetOptimalWidth();
});
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, [this, model](const Project *project) {
const GenericItem * const item = model->itemForObject(project);
if (item)
@@ -288,7 +296,7 @@ public:
this, [model](const QModelIndex &index) {
const GenericItem * const item = model->itemForIndex(index);
if (item && item->object())
- SessionManager::setStartupProject(qobject_cast<Project *>(item->object()));
+ ProjectManager::setStartupProject(qobject_cast<Project *>(item->object()));
});
}
@@ -296,7 +304,7 @@ private:
void restoreCurrentIndex()
{
const GenericItem * const itemForStartupProject
- = theModel()->itemForObject(SessionManager::startupProject());
+ = theModel()->itemForObject(ProjectManager::startupProject());
if (itemForStartupProject)
setCurrentIndex(theModel()->indexForItem(itemForStartupProject));
}
@@ -389,6 +397,12 @@ private:
TreeView::mouseReleaseEvent(event);
}
+ void showEvent(QShowEvent* event) override
+ {
+ scrollTo(currentIndex());
+ TreeView::showEvent(event);
+ }
+
QObject *objectAt(const QModelIndex &index) const
{
return theModel()->itemForIndex(index)->object();
@@ -551,6 +565,11 @@ int SelectorView::padding()
/////////
// KitAreaWidget
/////////
+void doLayout(KitAspectWidget *widget, Layouting::LayoutItem &builder)
+{
+ widget->addToLayout(builder);
+}
+
class KitAreaWidget : public QWidget
{
Q_OBJECT
@@ -573,18 +592,15 @@ public:
delete layout();
- Layouting::LayoutBuilder builder(Layouting::LayoutBuilder::GridLayout);
+ Layouting::Grid grid;
for (KitAspect *aspect : KitManager::kitAspects()) {
if (k && k->isMutable(aspect->id())) {
KitAspectWidget *widget = aspect->createConfigWidget(k);
m_widgets << widget;
- QLabel *label = new QLabel(aspect->displayName());
- builder.addItem(label);
- widget->addToLayout(builder);
- builder.finishRow();
+ grid.addItems({aspect->displayName(), widget, Layouting::br});
}
}
- builder.attachTo(this);
+ grid.attachTo(this);
layout()->setContentsMargins(3, 3, 3, 3);
m_kit = k;
@@ -653,7 +669,7 @@ MiniProjectTargetSelector::MiniProjectTargetSelector(QAction *targetSelectorActi
QWidget(parent),
m_projectAction(targetSelectorAction)
{
- setProperty("panelwidget", true);
+ StyleHelper::setPanelWidget(this);
setContentsMargins(QMargins(0, 1, 1, 8));
setWindowFlags(Qt::Popup);
@@ -696,22 +712,22 @@ MiniProjectTargetSelector::MiniProjectTargetSelector(QAction *targetSelectorActi
m_listWidgets[RUN]->viewport()->setAttribute(Qt::WA_Hover);
// Validate state: At this point the session is still empty!
- Project *startup = SessionManager::startupProject();
+ Project *startup = ProjectManager::startupProject();
QTC_CHECK(!startup);
- QTC_CHECK(SessionManager::projects().isEmpty());
+ QTC_CHECK(ProjectManager::projects().isEmpty());
connect(m_summaryLabel, &QLabel::linkActivated,
this, &MiniProjectTargetSelector::switchToProjectsMode);
- SessionManager *sessionManager = SessionManager::instance();
- connect(sessionManager, &SessionManager::startupProjectChanged,
+ ProjectManager *sessionManager = ProjectManager::instance();
+ connect(sessionManager, &ProjectManager::startupProjectChanged,
this, &MiniProjectTargetSelector::changeStartupProject);
- connect(sessionManager, &SessionManager::projectAdded,
+ connect(sessionManager, &ProjectManager::projectAdded,
this, &MiniProjectTargetSelector::projectAdded);
- connect(sessionManager, &SessionManager::projectRemoved,
+ connect(sessionManager, &ProjectManager::projectRemoved,
this, &MiniProjectTargetSelector::projectRemoved);
- connect(sessionManager, &SessionManager::projectDisplayNameChanged,
+ connect(sessionManager, &ProjectManager::projectDisplayNameChanged,
this, &MiniProjectTargetSelector::updateActionAndSummary);
// for icon changes:
@@ -720,17 +736,17 @@ MiniProjectTargetSelector::MiniProjectTargetSelector(QAction *targetSelectorActi
connect(m_listWidgets[TARGET], &GenericListWidget::changeActiveProjectConfiguration,
this, [this](QObject *pc) {
- SessionManager::setActiveTarget(m_project, static_cast<Target *>(pc), SetActive::Cascade);
+ m_project->setActiveTarget(static_cast<Target *>(pc), SetActive::Cascade);
});
connect(m_listWidgets[BUILD], &GenericListWidget::changeActiveProjectConfiguration,
this, [this](QObject *pc) {
- SessionManager::setActiveBuildConfiguration(m_project->activeTarget(),
- static_cast<BuildConfiguration *>(pc), SetActive::Cascade);
+ m_project->activeTarget()->setActiveBuildConfiguration(
+ static_cast<BuildConfiguration *>(pc), SetActive::Cascade);
});
connect(m_listWidgets[DEPLOY], &GenericListWidget::changeActiveProjectConfiguration,
this, [this](QObject *pc) {
- SessionManager::setActiveDeployConfiguration(m_project->activeTarget(),
- static_cast<DeployConfiguration *>(pc), SetActive::Cascade);
+ m_project->activeTarget()->setActiveDeployConfiguration(
+ static_cast<DeployConfiguration *>(pc), SetActive::Cascade);
});
connect(m_listWidgets[RUN], &GenericListWidget::changeActiveProjectConfiguration,
this, [this](QObject *pc) {
@@ -881,7 +897,7 @@ void MiniProjectTargetSelector::doLayout(bool keepSize)
onlySummary = true;
} else {
if (visibleLineCount < 3) {
- if (Utils::anyOf(SessionManager::projects(), &Project::needsConfiguration))
+ if (Utils::anyOf(ProjectManager::projects(), &Project::needsConfiguration))
visibleLineCount = 3;
}
if (visibleLineCount)
@@ -1126,7 +1142,7 @@ void MiniProjectTargetSelector::removedRunConfiguration(RunConfiguration *rc, bo
void MiniProjectTargetSelector::updateProjectListVisible()
{
- int count = SessionManager::projects().size();
+ int count = ProjectManager::projects().size();
bool visible = count > 1;
m_projectListWidget->setVisible(visible);
@@ -1139,7 +1155,7 @@ void MiniProjectTargetSelector::updateProjectListVisible()
void MiniProjectTargetSelector::updateTargetListVisible()
{
int maxCount = 0;
- for (Project *p : SessionManager::projects())
+ for (Project *p : ProjectManager::projects())
maxCount = qMax(p->targets().size(), maxCount);
bool visible = maxCount > 1;
@@ -1152,7 +1168,7 @@ void MiniProjectTargetSelector::updateTargetListVisible()
void MiniProjectTargetSelector::updateBuildListVisible()
{
int maxCount = 0;
- for (Project *p : SessionManager::projects()) {
+ for (Project *p : ProjectManager::projects()) {
const QList<Target *> targets = p->targets();
for (Target *t : targets)
maxCount = qMax(t->buildConfigurations().size(), maxCount);
@@ -1168,7 +1184,7 @@ void MiniProjectTargetSelector::updateBuildListVisible()
void MiniProjectTargetSelector::updateDeployListVisible()
{
int maxCount = 0;
- for (Project *p : SessionManager::projects()) {
+ for (Project *p : ProjectManager::projects()) {
const QList<Target *> targets = p->targets();
for (Target *t : targets)
maxCount = qMax(t->deployConfigurations().size(), maxCount);
@@ -1184,7 +1200,7 @@ void MiniProjectTargetSelector::updateDeployListVisible()
void MiniProjectTargetSelector::updateRunListVisible()
{
int maxCount = 0;
- for (Project *p : SessionManager::projects()) {
+ for (Project *p : ProjectManager::projects()) {
const QList<Target *> targets = p->targets();
for (Target *t : targets)
maxCount = qMax(t->runConfigurations().size(), maxCount);
@@ -1460,10 +1476,10 @@ void MiniProjectTargetSelector::updateActionAndSummary()
? Icons::DESKTOP_DEVICE.icon()
: style()->standardIcon(QStyle::SP_ComputerIcon);
- Project *project = SessionManager::startupProject();
+ Project *project = ProjectManager::startupProject();
if (project) {
projectName = project->displayName();
- for (Project *p : SessionManager::projects()) {
+ for (Project *p : ProjectManager::projects()) {
if (p != project && p->displayName() == projectName) {
fileName = project->projectFilePath().toUserOutput();
break;
@@ -1515,7 +1531,7 @@ void MiniProjectTargetSelector::updateActionAndSummary()
void MiniProjectTargetSelector::updateSummary()
{
QString summary;
- if (Project *startupProject = SessionManager::startupProject()) {
+ if (Project *startupProject = ProjectManager::startupProject()) {
if (!m_projectListWidget->isVisibleTo(this))
summary.append(Tr::tr("Project: <b>%1</b><br/>").arg(startupProject->displayName()));
if (Target *activeTarget = startupProject->activeTarget()) {
diff --git a/src/plugins/projectexplorer/msvcparser.cpp b/src/plugins/projectexplorer/msvcparser.cpp
index 89c59443e1..8452afcc5a 100644
--- a/src/plugins/projectexplorer/msvcparser.cpp
+++ b/src/plugins/projectexplorer/msvcparser.cpp
@@ -61,7 +61,7 @@ static Task handleNmakeJomMessage(const QString &line)
CompileTask task(type, line.mid(matchLength).trimmed());
task.details << line;
- return std::move(task);
+ return task;
}
static Task::TaskType taskType(const QString &category)
diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp
index d0856e29d4..d4d518de44 100644
--- a/src/plugins/projectexplorer/msvctoolchain.cpp
+++ b/src/plugins/projectexplorer/msvctoolchain.cpp
@@ -3,6 +3,7 @@
#include "msvctoolchain.h"
+#include "devicesupport/idevice.h"
#include "gcctoolchain.h"
#include "msvcparser.h"
#include "projectexplorer.h"
@@ -14,12 +15,12 @@
#include <coreplugin/icore.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <utils/temporarydirectory.h>
#include <utils/winutils.h>
@@ -254,7 +255,7 @@ static std::optional<VisualStudioInstallation> detectCppBuildTools2017()
static QVector<VisualStudioInstallation> detectVisualStudioFromVsWhere(const QString &vswhere)
{
QVector<VisualStudioInstallation> installations;
- QtcProcess vsWhereProcess;
+ Process vsWhereProcess;
vsWhereProcess.setCodec(QTextCodec::codecForName("UTF-8"));
const int timeoutS = 5;
vsWhereProcess.setTimeoutS(timeoutS);
@@ -648,7 +649,7 @@ Macros MsvcToolChain::msvcPredefinedMacros(const QStringList &cxxflags,
qWarning("%s: %s", Q_FUNC_INFO, qPrintable(saver.errorString()));
return predefinedMacros;
}
- Utils::QtcProcess cpp;
+ Utils::Process cpp;
cpp.setEnvironment(env);
cpp.setWorkingDirectory(Utils::TemporaryDirectory::masterDirectoryFilePath());
QStringList arguments;
@@ -747,10 +748,8 @@ static QString winExpandDelayedEnvReferences(QString in, const Utils::Environmen
return in;
}
-void MsvcToolChain::environmentModifications(
- QFutureInterface<MsvcToolChain::GenerateEnvResult> &future,
- QString vcvarsBat,
- QString varsBatArg)
+void MsvcToolChain::environmentModifications(QPromise<MsvcToolChain::GenerateEnvResult> &promise,
+ QString vcvarsBat, QString varsBatArg)
{
const Utils::Environment inEnv = Utils::Environment::systemEnvironment();
Utils::Environment outEnv;
@@ -776,7 +775,7 @@ void MsvcToolChain::environmentModifications(
}
}
- future.reportResult({error, diff});
+ promise.addResult(MsvcToolChain::GenerateEnvResult{error, diff});
}
void MsvcToolChain::initEnvModWatcher(const QFuture<GenerateEnvResult> &future)
@@ -1004,10 +1003,8 @@ bool MsvcToolChain::fromMap(const QVariantMap &data)
data.value(QLatin1String(environModsKeyC)).toList());
rescanForCompiler();
- initEnvModWatcher(Utils::runAsync(envModThreadPool(),
- &MsvcToolChain::environmentModifications,
- m_vcvarsBat,
- m_varsBatArg));
+ initEnvModWatcher(Utils::asyncRun(envModThreadPool(), &MsvcToolChain::environmentModifications,
+ m_vcvarsBat, m_varsBatArg));
const bool valid = !m_vcvarsBat.isEmpty() && targetAbi().isValid();
if (!valid)
@@ -1128,8 +1125,8 @@ WarningFlags MsvcToolChain::warningFlags(const QStringList &cflags) const
return flags;
}
-QStringList MsvcToolChain::includedFiles(const QStringList &flags,
- const QString &directoryPath) const
+FilePaths MsvcToolChain::includedFiles(const QStringList &flags,
+ const FilePath &directoryPath) const
{
return ToolChain::includedFiles("/FI", flags, directoryPath, PossiblyConcatenatedFlag::Yes);
}
@@ -1236,10 +1233,8 @@ void MsvcToolChain::setupVarsBat(const Abi &abi, const QString &varsBat, const Q
m_varsBatArg = varsBatArg;
if (!varsBat.isEmpty()) {
- initEnvModWatcher(Utils::runAsync(envModThreadPool(),
- &MsvcToolChain::environmentModifications,
- varsBat,
- varsBatArg));
+ initEnvModWatcher(Utils::asyncRun(envModThreadPool(),
+ &MsvcToolChain::environmentModifications, varsBat, varsBatArg));
}
}
@@ -1560,7 +1555,7 @@ static QVersionNumber clangClVersion(const FilePath &clangClPath)
if (!dllversion.isEmpty())
return QVersionNumber::fromString(dllversion);
- QtcProcess clangClProcess;
+ Process clangClProcess;
clangClProcess.setCommand({clangClPath, {"--version"}});
clangClProcess.runBlocking();
if (clangClProcess.result() != ProcessResult::FinishedWithSuccess)
@@ -1777,7 +1772,7 @@ Macros ClangClToolChain::msvcPredefinedMacros(const QStringList &cxxflags,
if (!cxxflags.contains("--driver-mode=g++"))
return MsvcToolChain::msvcPredefinedMacros(cxxflags, env);
- QtcProcess cpp;
+ Process cpp;
cpp.setEnvironment(env);
cpp.setWorkingDirectory(Utils::TemporaryDirectory::masterDirectoryFilePath());
@@ -1915,7 +1910,7 @@ static void detectCppBuildTools2015(Toolchains *list)
Toolchains MsvcToolChainFactory::autoDetect(const ToolchainDetector &detector) const
{
- if (!detector.device.isNull()) {
+ if (detector.device->type() != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
// FIXME currently no support for msvc toolchains on a device
return {};
}
@@ -2030,7 +2025,7 @@ bool ClangClToolChainFactory::canCreate() const
Toolchains ClangClToolChainFactory::autoDetect(const ToolchainDetector &detector) const
{
- if (!detector.device.isNull()) {
+ if (detector.device->type() != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
// FIXME currently no support for msvc toolchains on a device
return {};
}
@@ -2127,7 +2122,7 @@ std::optional<QString> MsvcToolChain::generateEnvironmentSettings(const Utils::E
return QString();
}
- Utils::QtcProcess run;
+ Utils::Process run;
// As of WinSDK 7.1, there is logic preventing the path from being set
// correctly if "ORIGINALPATH" is already set. That can cause problems
diff --git a/src/plugins/projectexplorer/msvctoolchain.h b/src/plugins/projectexplorer/msvctoolchain.h
index 042bbfe38f..68f8b9dcf1 100644
--- a/src/plugins/projectexplorer/msvctoolchain.h
+++ b/src/plugins/projectexplorer/msvctoolchain.h
@@ -55,8 +55,8 @@ public:
MacroInspectionRunner createMacroInspectionRunner() const override;
Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override;
Utils::WarningFlags warningFlags(const QStringList &cflags) const override;
- QStringList includedFiles(const QStringList &flags,
- const QString &directoryPath) const override;
+ Utils::FilePaths includedFiles(const QStringList &flags,
+ const Utils::FilePath &directoryPath) const override;
BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner(
const Utils::Environment &env) const override;
void addToEnvironment(Utils::Environment &env) const override;
@@ -81,6 +81,8 @@ public:
const QString &batchFile,
const QString &batchArgs,
QMap<QString, QString> &envPairs);
+ bool environmentInitialized() const { return !m_environmentModifications.isEmpty(); }
+
protected:
class WarningFlagAdder
{
@@ -111,9 +113,8 @@ protected:
std::optional<QString> error;
Utils::EnvironmentItems environmentItems;
};
- static void environmentModifications(QFutureInterface<GenerateEnvResult> &future,
- QString vcvarsBat,
- QString varsBatArg);
+ static void environmentModifications(QPromise<GenerateEnvResult> &future,
+ QString vcvarsBat, QString varsBatArg);
void initEnvModWatcher(const QFuture<GenerateEnvResult> &future);
protected:
diff --git a/src/plugins/projectexplorer/processparameters.cpp b/src/plugins/projectexplorer/processparameters.cpp
index 49b50dccb3..0b3d6d83c7 100644
--- a/src/plugins/projectexplorer/processparameters.cpp
+++ b/src/plugins/projectexplorer/processparameters.cpp
@@ -5,7 +5,7 @@
#include <utils/fileutils.h>
#include <utils/macroexpander.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/theme/theme.h>
#include <utils/utilstr.h>
diff --git a/src/plugins/projectexplorer/processstep.cpp b/src/plugins/projectexplorer/processstep.cpp
index 3ced1622d0..a34ff10b61 100644
--- a/src/plugins/projectexplorer/processstep.cpp
+++ b/src/plugins/projectexplorer/processstep.cpp
@@ -34,9 +34,8 @@ public:
ProcessStep::ProcessStep(BuildStepList *bsl, Id id)
: AbstractProcessStep(bsl, id)
{
- auto command = addAspect<StringAspect>();
+ auto command = addAspect<FilePathAspect>();
command->setSettingsKey(PROCESS_COMMAND_KEY);
- command->setDisplayStyle(StringAspect::PathChooserDisplay);
command->setLabelText(Tr::tr("Command:"));
command->setExpectedKind(PathChooser::Command);
command->setHistoryCompleter("PE.ProcessStepCommand.History");
@@ -46,10 +45,9 @@ ProcessStep::ProcessStep(BuildStepList *bsl, Id id)
arguments->setDisplayStyle(StringAspect::LineEditDisplay);
arguments->setLabelText(Tr::tr("Arguments:"));
- auto workingDirectory = addAspect<StringAspect>();
+ auto workingDirectory = addAspect<FilePathAspect>();
workingDirectory->setSettingsKey(PROCESS_WORKINGDIRECTORY_KEY);
workingDirectory->setValue(Constants::DEFAULT_WORKING_DIR);
- workingDirectory->setDisplayStyle(StringAspect::PathChooserDisplay);
workingDirectory->setLabelText(Tr::tr("Working directory:"));
workingDirectory->setExpectedKind(PathChooser::Directory);
diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp
index fbd4f9e28f..c021d9b1a6 100644
--- a/src/plugins/projectexplorer/project.cpp
+++ b/src/plugins/projectexplorer/project.cpp
@@ -11,15 +11,17 @@
#include "environmentaspect.h"
#include "kit.h"
#include "kitinformation.h"
+#include "msvctoolchain.h"
#include "projectexplorer.h"
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "projectnodes.h"
#include "runconfiguration.h"
#include "runconfigurationaspects.h"
-#include "session.h"
#include "target.h"
#include "taskhub.h"
+#include "toolchainmanager.h"
#include "userfileaccessor.h"
#include <coreplugin/idocument.h>
@@ -273,7 +275,7 @@ void Project::addTarget(std::unique_ptr<Target> &&t)
// check activeTarget:
if (!activeTarget())
- SessionManager::setActiveTarget(this, pointer, SetActive::Cascade);
+ setActiveTarget(pointer, SetActive::Cascade);
}
Target *Project::addTargetForDefaultKit()
@@ -309,7 +311,7 @@ bool Project::removeTarget(Target *target)
auto keep = take(d->m_targets, target);
if (target == d->m_activeTarget) {
Target *newActiveTarget = (d->m_targets.size() == 0 ? nullptr : d->m_targets.at(0).get());
- SessionManager::setActiveTarget(this, newActiveTarget, SetActive::Cascade);
+ setActiveTarget(newActiveTarget, SetActive::Cascade);
}
emit removedTarget(target);
@@ -326,7 +328,7 @@ Target *Project::activeTarget() const
return d->m_activeTarget;
}
-void Project::setActiveTarget(Target *target)
+void Project::setActiveTargetHelper(Target *target)
{
if (d->m_activeTarget == target)
return;
@@ -414,6 +416,29 @@ Target *Project::target(Kit *k) const
return findOrDefault(d->m_targets, equal(&Target::kit, k));
}
+void Project::setActiveTarget(Target *target, SetActive cascade)
+{
+ if (isShuttingDown())
+ return;
+
+ setActiveTargetHelper(target);
+
+ if (!target) // never cascade setting no target
+ return;
+
+ if (cascade != SetActive::Cascade || !ProjectManager::isProjectConfigurationCascading())
+ return;
+
+ Utils::Id kitId = target->kit()->id();
+ for (Project *otherProject : ProjectManager::projects()) {
+ if (otherProject == this)
+ continue;
+ if (Target *otherTarget = Utils::findOrDefault(otherProject->targets(),
+ [kitId](Target *t) { return t->kit()->id() == kitId; }))
+ otherProject->setActiveTargetHelper(otherTarget);
+ }
+}
+
Tasks Project::projectIssues(const Kit *k) const
{
Tasks result;
@@ -445,12 +470,12 @@ bool Project::copySteps(Target *sourceTarget, Target *newTarget)
sourceBc->buildSystem()->name()));
newTarget->addBuildConfiguration(newBc);
if (sourceTarget->activeBuildConfiguration() == sourceBc)
- SessionManager::setActiveBuildConfiguration(newTarget, newBc, SetActive::NoCascade);
+ newTarget->setActiveBuildConfiguration(newBc, SetActive::NoCascade);
}
if (!newTarget->activeBuildConfiguration()) {
QList<BuildConfiguration *> bcs = newTarget->buildConfigurations();
if (!bcs.isEmpty())
- SessionManager::setActiveBuildConfiguration(newTarget, bcs.first(), SetActive::NoCascade);
+ newTarget->setActiveBuildConfiguration(bcs.first(), SetActive::NoCascade);
}
for (DeployConfiguration *sourceDc : sourceTarget->deployConfigurations()) {
@@ -462,12 +487,12 @@ bool Project::copySteps(Target *sourceTarget, Target *newTarget)
newDc->setDisplayName(sourceDc->displayName());
newTarget->addDeployConfiguration(newDc);
if (sourceTarget->activeDeployConfiguration() == sourceDc)
- SessionManager::setActiveDeployConfiguration(newTarget, newDc, SetActive::NoCascade);
+ newTarget->setActiveDeployConfiguration(newDc, SetActive::NoCascade);
}
if (!newTarget->activeBuildConfiguration()) {
QList<DeployConfiguration *> dcs = newTarget->deployConfigurations();
if (!dcs.isEmpty())
- SessionManager::setActiveDeployConfiguration(newTarget, dcs.first(), SetActive::NoCascade);
+ newTarget->setActiveDeployConfiguration(dcs.first(), SetActive::NoCascade);
}
for (RunConfiguration *sourceRc : sourceTarget->runConfigurations()) {
@@ -846,6 +871,34 @@ const Node *Project::nodeForFilePath(const FilePath &filePath,
return nullptr;
}
+FilePaths Project::binariesForSourceFile(const FilePath &sourceFile) const
+{
+ if (!rootProjectNode())
+ return {};
+ const QList<Node *> fileNodes = rootProjectNode()->findNodes([&sourceFile](Node *n) {
+ return n->filePath() == sourceFile;
+ });
+ FilePaths binaries;
+ for (const Node * const fileNode : fileNodes) {
+ for (ProjectNode *projectNode = fileNode->parentProjectNode(); projectNode;
+ projectNode = projectNode->parentProjectNode()) {
+ if (!projectNode->isProduct())
+ continue;
+ if (projectNode->productType() == ProductType::App
+ || projectNode->productType() == ProductType::Lib) {
+ const QList<Node *> binaryNodes = projectNode->findNodes([](Node *n) {
+ return n->asFileNode() && (n->asFileNode()->fileType() == FileType::App
+ || n->asFileNode()->fileType() == FileType::Lib);
+
+ });
+ binaries << Utils::transform(binaryNodes, &Node::filePath);
+ }
+ break;
+ }
+ }
+ return binaries;
+}
+
void Project::setProjectLanguages(Context language)
{
if (d->m_projectLanguages == language)
@@ -1130,7 +1183,7 @@ void Project::addVariablesToMacroExpander(const QByteArray &prefix,
});
expander->registerVariable(fullPrefix + "Kit:Name",
//: %1 is something like "Active project"
- Tr::tr("%1: The name the active kit.").arg(descriptor),
+ Tr::tr("%1: The name of the active kit.").arg(descriptor),
[targetGetter]() -> QString {
if (const Target *const target = targetGetter())
return target->kit()->displayName();
@@ -1435,8 +1488,7 @@ void ProjectExplorerPlugin::testProject_multipleBuildConfigs()
Target * const target = theProject.project()->activeTarget();
QVERIFY(target);
QCOMPARE(target->buildConfigurations().size(), 6);
- SessionManager::setActiveBuildConfiguration(target, target->buildConfigurations().at(1),
- SetActive::Cascade);
+ target->setActiveBuildConfiguration(target->buildConfigurations().at(1), SetActive::Cascade);
BuildSystem * const bs = theProject.project()->activeTarget()->buildSystem();
QVERIFY(bs);
QCOMPARE(bs, target->activeBuildConfiguration()->buildSystem());
@@ -1452,12 +1504,94 @@ void ProjectExplorerPlugin::testProject_multipleBuildConfigs()
}
QVERIFY(!bs->isWaitingForParse() && !bs->isParsing());
- QCOMPARE(SessionManager::startupProject(), theProject.project());
+ QCOMPARE(ProjectManager::startupProject(), theProject.project());
QCOMPARE(ProjectTree::currentProject(), theProject.project());
QVERIFY(EditorManager::openEditor(projectDir.pathAppended("main.cpp")));
QVERIFY(ProjectTree::currentNode());
ProjectTree::instance()->expandAll();
- SessionManager::closeAllProjects(); // QTCREATORBUG-25655
+ ProjectManager::closeAllProjects(); // QTCREATORBUG-25655
+}
+
+void ProjectExplorerPlugin::testSourceToBinaryMapping()
+{
+ // Find suitable kit.
+ Kit * const kit = findOr(KitManager::kits(), nullptr, [](const Kit *k) {
+ return k->isValid() && ToolChainKitAspect::cxxToolChain(k);
+ });
+ if (!kit)
+ QSKIP("The test requires at least one kit with a toolchain.");
+
+ const auto toolchain = ToolChainKitAspect::cxxToolChain(kit);
+ QVERIFY(toolchain);
+ if (const auto msvcToolchain = dynamic_cast<Internal::MsvcToolChain *>(toolchain)) {
+ while (!msvcToolchain->environmentInitialized()) {
+ QSignalSpy parsingFinishedSpy(ToolChainManager::instance(),
+ &ToolChainManager::toolChainUpdated);
+ QVERIFY(parsingFinishedSpy.wait(10000));
+ }
+ }
+
+ // Copy project from qrc.
+ QTemporaryDir * const tempDir = TemporaryDirectory::masterTemporaryDirectory();
+ QVERIFY(tempDir->isValid());
+ const FilePath projectDir = FilePath::fromString(tempDir->path() + "/multi-target-project");
+ if (!projectDir.exists()) {
+ const auto result = FilePath(":/projectexplorer/testdata/multi-target-project")
+ .copyRecursively(projectDir);
+ QVERIFY2(result, qPrintable(result.error()));
+ const QFileInfoList files = QDir(projectDir.toString()).entryInfoList(QDir::Files);
+ for (const QFileInfo &f : files)
+ QFile(f.absoluteFilePath()).setPermissions(f.permissions() | QFile::WriteUser);
+ }
+
+ // Load Project.
+ QFETCH(QString, projectFileName);
+ const auto theProject = openProject(projectDir.pathAppended(projectFileName));
+ if (theProject.errorMessage().contains("text/")) {
+ QSKIP("This test requires the presence of the qmake/cmake/qbs project managers "
+ "to be fully functional");
+ }
+
+ QVERIFY2(theProject, qPrintable(theProject.errorMessage()));
+ theProject.project()->configureAsExampleProject(kit);
+ QCOMPARE(theProject.project()->targets().size(), 1);
+ Target * const target = theProject.project()->activeTarget();
+ QVERIFY(target);
+ BuildSystem * const bs = target->buildSystem();
+ QVERIFY(bs);
+ QCOMPARE(bs, target->activeBuildConfiguration()->buildSystem());
+ if (bs->isWaitingForParse() || bs->isParsing()) {
+ QSignalSpy parsingFinishedSpy(bs, &BuildSystem::parsingFinished);
+ QVERIFY(parsingFinishedSpy.wait(10000));
+ }
+ QVERIFY(!bs->isWaitingForParse() && !bs->isParsing());
+
+ if (QLatin1String(QTest::currentDataTag()) == QLatin1String("qbs")) {
+ BuildManager::buildProjectWithoutDependencies(theProject.project());
+ if (BuildManager::isBuilding()) {
+ QSignalSpy buildingFinishedSpy(BuildManager::instance(), &BuildManager::buildQueueFinished);
+ QVERIFY(buildingFinishedSpy.wait(10000));
+ }
+ QVERIFY(!BuildManager::isBuilding());
+ QSignalSpy projectUpdateSpy(theProject.project(), &Project::fileListChanged);
+ QVERIFY(projectUpdateSpy.wait(5000));
+ }
+
+ // Check mapping
+ const auto binariesForSource = [&](const QString &fileName) {
+ return theProject.project()->binariesForSourceFile(projectDir.pathAppended(fileName));
+ };
+ QCOMPARE(binariesForSource("multi-target-project-main.cpp").size(), 1);
+ QCOMPARE(binariesForSource("multi-target-project-lib.cpp").size(), 1);
+ QCOMPARE(binariesForSource("multi-target-project-shared.h").size(), 2);
+}
+
+void ProjectExplorerPlugin::testSourceToBinaryMapping_data()
+{
+ QTest::addColumn<QString>("projectFileName");
+ QTest::addRow("cmake") << "CMakeLists.txt";
+ QTest::addRow("qbs") << "multi-target-project.qbs";
+ QTest::addRow("qmake") << "multi-target-project.pro";
}
#endif // WITH_TESTS
diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h
index ed336b5f7f..832fc3c942 100644
--- a/src/plugins/projectexplorer/project.h
+++ b/src/plugins/projectexplorer/project.h
@@ -36,11 +36,11 @@ class ProjectImporter;
class ProjectNode;
class ProjectPrivate;
class Target;
+enum class SetActive : int;
// Documentation inside.
class PROJECTEXPLORER_EXPORT Project : public QObject
{
- friend class SessionManager; // for setActiveTarget
Q_OBJECT
public:
@@ -89,6 +89,8 @@ public:
Target *activeTarget() const;
Target *target(Utils::Id id) const;
Target *target(Kit *k) const;
+ void setActiveTarget(Target *target, SetActive cascade);
+
virtual Tasks projectIssues(const Kit *k) const;
static bool copySteps(Target *sourceTarget, Target *newTarget);
@@ -107,6 +109,7 @@ public:
bool isKnownFile(const Utils::FilePath &filename) const;
const Node *nodeForFilePath(const Utils::FilePath &filePath,
const NodeMatcher &extraMatcher = {}) const;
+ Utils::FilePaths binariesForSourceFile(const Utils::FilePath &sourceFile) const;
virtual QVariantMap toMap() const;
@@ -226,7 +229,7 @@ private:
void removeProjectLanguage(Utils::Id id);
void handleSubTreeChanged(FolderNode *node);
- void setActiveTarget(Target *target);
+ void setActiveTargetHelper(Target *target);
friend class ContainerNode;
ProjectPrivate *d;
diff --git a/src/plugins/projectexplorer/projectconfiguration.cpp b/src/plugins/projectexplorer/projectconfiguration.cpp
index 6d5fa797a6..841be31731 100644
--- a/src/plugins/projectexplorer/projectconfiguration.cpp
+++ b/src/plugins/projectexplorer/projectconfiguration.cpp
@@ -19,11 +19,9 @@ const char DISPLAY_NAME_KEY[] = "ProjectExplorer.ProjectConfiguration.DisplayNam
// ProjectConfiguration
ProjectConfiguration::ProjectConfiguration(QObject *parent, Utils::Id id)
- : QObject(parent)
+ : AspectContainer(parent)
, m_id(id)
{
- m_aspects.setOwnsSubAspects(true);
-
QTC_CHECK(parent);
QTC_CHECK(id.isValid());
setObjectName(id.toString());
@@ -89,7 +87,7 @@ QVariantMap ProjectConfiguration::toMap() const
QVariantMap map;
map.insert(QLatin1String(CONFIGURATION_ID_KEY), m_id.toSetting());
m_displayName.toMap(map, DISPLAY_NAME_KEY);
- m_aspects.toMap(map);
+ AspectContainer::toMap(map);
return map;
}
@@ -106,15 +104,10 @@ bool ProjectConfiguration::fromMap(const QVariantMap &map)
QTC_ASSERT(id.toString().startsWith(m_id.toString()), return false);
m_displayName.fromMap(map, DISPLAY_NAME_KEY);
- m_aspects.fromMap(map);
+ AspectContainer::fromMap(map);
return true;
}
-BaseAspect *ProjectConfiguration::aspect(Id id) const
-{
- return m_aspects.aspect(id);
-}
-
FilePath ProjectConfiguration::mapFromBuildDeviceToGlobalPath(const FilePath &path) const
{
IDevice::ConstPtr dev = BuildDeviceKitAspect::device(kit());
diff --git a/src/plugins/projectexplorer/projectconfiguration.h b/src/plugins/projectexplorer/projectconfiguration.h
index cb716bf66e..8c5dac1d90 100644
--- a/src/plugins/projectexplorer/projectconfiguration.h
+++ b/src/plugins/projectexplorer/projectconfiguration.h
@@ -21,7 +21,7 @@ class Kit;
class Project;
class Target;
-class PROJECTEXPLORER_EXPORT ProjectConfiguration : public QObject
+class PROJECTEXPLORER_EXPORT ProjectConfiguration : public Utils::AspectContainer
{
Q_OBJECT
@@ -54,26 +54,12 @@ public:
static QString settingsIdKey();
- template<class Aspect, typename ...Args>
- Aspect *addAspect(Args && ...args)
- {
- return m_aspects.addAspect<Aspect>(std::forward<Args>(args)...);
- }
-
- const Utils::AspectContainer &aspects() const { return m_aspects; }
-
- Utils::BaseAspect *aspect(Utils::Id id) const;
- template <typename T> T *aspect() const { return m_aspects.aspect<T>(); }
-
Utils::FilePath mapFromBuildDeviceToGlobalPath(const Utils::FilePath &path) const;
signals:
void displayNameChanged();
void toolTipChanged();
-protected:
- Utils::AspectContainer m_aspects;
-
private:
QPointer<Target> m_target;
const Utils::Id m_id;
diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp
index f76f10175b..d313cb433d 100644
--- a/src/plugins/projectexplorer/projectexplorer.cpp
+++ b/src/plugins/projectexplorer/projectexplorer.cpp
@@ -8,6 +8,7 @@
#include "buildsystem.h"
#include "compileoutputwindow.h"
#include "configtaskhandler.h"
+#include "copystep.h"
#include "customexecutablerunconfiguration.h"
#include "customparserssettingspage.h"
#include "customwizard/customwizard.h"
@@ -34,11 +35,13 @@
#include "dependenciespanel.h"
#include "devicesupport/desktopdevice.h"
#include "devicesupport/desktopdevicefactory.h"
+#include "devicesupport/devicecheckbuildstep.h"
#include "devicesupport/devicemanager.h"
#include "devicesupport/devicesettingspage.h"
#include "devicesupport/sshsettings.h"
#include "devicesupport/sshsettingspage.h"
#include "editorsettingspropertiespage.h"
+#include "environmentaspect.h"
#include "filesinallprojectsfind.h"
#include "jsonwizard/jsonwizardfactory.h"
#include "jsonwizard/jsonwizardgeneratorfactory.h"
@@ -54,7 +57,6 @@
#include "project.h"
#include "projectexplorericons.h"
#include "projectexplorersettings.h"
-#include "projectexplorersettingspage.h"
#include "projectexplorertr.h"
#include "projectfilewizardextension.h"
#include "projectmanager.h"
@@ -63,11 +65,8 @@
#include "projecttreewidget.h"
#include "projectwindow.h"
#include "removetaskhandler.h"
-#include "runconfigurationaspects.h"
#include "sanitizerparser.h"
#include "selectablefilesmodel.h"
-#include "session.h"
-#include "sessiondialog.h"
#include "showineditortaskhandler.h"
#include "simpleprojectwizard.h"
#include "target.h"
@@ -108,6 +107,7 @@
#include <coreplugin/modemanager.h>
#include <coreplugin/navigationwidget.h>
#include <coreplugin/outputpane.h>
+#include <coreplugin/session.h>
#include <coreplugin/vcsmanager.h>
#include <extensionsystem/pluginmanager.h>
@@ -127,6 +127,7 @@
#include <utils/qtcassert.h>
#include <utils/removefiledialog.h>
#include <utils/stringutils.h>
+#include <utils/terminalhooks.h>
#include <utils/tooltip/tooltip.h>
#include <utils/utilsicons.h>
@@ -173,6 +174,7 @@
*/
using namespace Core;
+using namespace ExtensionSystem;
using namespace ProjectExplorer::Internal;
using namespace Utils;
@@ -240,7 +242,6 @@ const int P_ACTION_BUILDPROJECT = 80;
// Menus
const char M_RECENTPROJECTS[] = "ProjectExplorer.Menu.Recent";
const char M_UNLOADPROJECTS[] = "ProjectExplorer.Menu.Unload";
-const char M_SESSION[] = "ProjectExplorer.Menu.Session";
const char M_GENERATORS[] = "ProjectExplorer.Menu.Generators";
const char RUNMENUCONTEXTMENU[] = "Project.RunMenu";
@@ -254,7 +255,6 @@ const char BUILD_BEFORE_DEPLOY_SETTINGS_KEY[] = "ProjectExplorer/Settings/BuildB
const char DEPLOY_BEFORE_RUN_SETTINGS_KEY[] = "ProjectExplorer/Settings/DeployBeforeRun";
const char SAVE_BEFORE_BUILD_SETTINGS_KEY[] = "ProjectExplorer/Settings/SaveBeforeBuild";
const char USE_JOM_SETTINGS_KEY[] = "ProjectExplorer/Settings/UseJom";
-const char AUTO_RESTORE_SESSION_SETTINGS_KEY[] = "ProjectExplorer/Settings/AutoRestoreLastSession";
const char ADD_LIBRARY_PATHS_TO_RUN_ENV_SETTINGS_KEY[] =
"ProjectExplorer/Settings/AddLibraryPathsToRunEnv";
const char PROMPT_TO_STOP_RUN_CONTROL_SETTINGS_KEY[] =
@@ -303,12 +303,12 @@ static const RunConfiguration *runConfigForNode(const Target *target, const Proj
static bool hideBuildMenu()
{
- return Core::ICore::settings()->value(Constants::SETTINGS_MENU_HIDE_BUILD, false).toBool();
+ return ICore::settings()->value(Constants::SETTINGS_MENU_HIDE_BUILD, false).toBool();
}
static bool hideDebugMenu()
{
- return Core::ICore::settings()->value(Constants::SETTINGS_MENU_HIDE_DEBUG, false).toBool();
+ return ICore::settings()->value(Constants::SETTINGS_MENU_HIDE_DEBUG, false).toBool();
}
static bool canOpenTerminalWithRunEnv(const Project *project, const ProjectNode *node)
@@ -337,7 +337,7 @@ static BuildConfiguration *currentBuildConfiguration()
static Target *activeTarget()
{
- const Project * const project = SessionManager::startupProject();
+ const Project * const project = ProjectManager::startupProject();
return project ? project->activeTarget() : nullptr;
}
@@ -405,40 +405,22 @@ protected:
void restoreState(const QJsonObject &object) override;
};
-class RunConfigurationLocatorFilter : public Core::ILocatorFilter
+class RunConfigurationStartFilter final : public ILocatorFilter
{
public:
- RunConfigurationLocatorFilter();
-
- void prepareSearch(const QString &entry) override;
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
+ RunConfigurationStartFilter();
private:
- void targetListUpdated();
- QList<Core::LocatorFilterEntry> m_result;
-};
-
-class RunRunConfigurationLocatorFilter final : public RunConfigurationLocatorFilter
-{
-public:
- RunRunConfigurationLocatorFilter();
-
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText,
- int *selectionStart,
- int *selectionLength) const final;
+ Core::LocatorMatcherTasks matchers() final;
};
-class SwitchToRunConfigurationLocatorFilter final : public RunConfigurationLocatorFilter
+class RunConfigurationSwitchFilter final : public ILocatorFilter
{
public:
- SwitchToRunConfigurationLocatorFilter();
+ RunConfigurationSwitchFilter();
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText,
- int *selectionStart,
- int *selectionLength) const final;
+private:
+ Core::LocatorMatcherTasks matchers() final;
};
class ProjectExplorerPluginPrivate : public QObject
@@ -468,12 +450,7 @@ public:
void unloadProjectContextMenu();
void unloadOtherProjectsContextMenu();
void closeAllProjects();
- void showSessionManager();
- void updateSessionMenu();
- void setSession(QAction *action);
- void determineSessionToRestoreAtStartup();
- void restoreSession();
void runProjectContextMenu(RunConfiguration *rc);
void savePersistentSettings();
@@ -492,7 +469,7 @@ public:
void deleteFile();
void handleRenameFile();
void handleSetStartupProject();
- void setStartupProject(ProjectExplorer::Project *project);
+ void setStartupProject(Project *project);
bool closeAllFilesInProject(const Project *project);
void updateRecentProjectMenu();
@@ -504,11 +481,11 @@ public:
void openTerminalHere(const EnvironmentGetter &env);
void openTerminalHereWithRunEnv();
- void invalidateProject(ProjectExplorer::Project *project);
+ void invalidateProject(Project *project);
- void projectAdded(ProjectExplorer::Project *pro);
- void projectRemoved(ProjectExplorer::Project *pro);
- void projectDisplayNameChanged(ProjectExplorer::Project *pro);
+ void projectAdded(Project *pro);
+ void projectRemoved(Project *pro);
+ void projectDisplayNameChanged(Project *pro);
void doUpdateRunActions();
@@ -525,12 +502,10 @@ public:
void extendFolderNavigationWidgetFactory();
public:
- QMenu *m_sessionMenu;
QMenu *m_openWithMenu;
QMenu *m_openTerminalMenu;
QMultiMap<int, QObject*> m_actionMap;
- QAction *m_sessionManagerAction;
QAction *m_newAction;
QAction *m_loadAction;
ParameterAction *m_unloadAction;
@@ -599,7 +574,6 @@ public:
QAction *m_runSubProject;
ProjectWindow *m_proWindow = nullptr;
- QString m_sessionToRestoreAtStartup;
QStringList m_profileMimeTypes;
int m_activeRunControlCount = 0;
@@ -618,11 +592,9 @@ public:
BuildPropertiesSettings m_buildPropertiesSettings;
QList<CustomParserSettings> m_customParsers;
bool m_shouldHaveRunConfiguration = false;
- bool m_shuttingDown = false;
Id m_runMode = Constants::NO_RUN_MODE;
ToolChainManager *m_toolChainManager = nullptr;
- QStringList m_arguments;
#ifdef WITH_JOURNALD
JournaldWatcher m_journalWatcher;
@@ -667,7 +639,7 @@ public:
RemoveTaskHandler m_removeTaskHandler;
ConfigTaskHandler m_configTaskHandler{Task::compilerMissingTask(), Constants::KITS_SETTINGS_PAGE_ID};
- SessionManager m_sessionManager;
+ ProjectManager m_sessionManager;
AppOutputPane m_outputPane;
ProjectTree m_projectTree;
@@ -675,9 +647,11 @@ public:
AllProjectsFilter m_allProjectsFilter;
CurrentProjectFilter m_currentProjectFilter;
AllProjectFilesFilter m_allProjectDirectoriesFilter;
- RunRunConfigurationLocatorFilter m_runConfigurationLocatorFilter;
- SwitchToRunConfigurationLocatorFilter m_switchRunConfigurationLocatorFilter;
+ RunConfigurationStartFilter m_runConfigurationStartFilter;
+ RunConfigurationSwitchFilter m_runConfigurationSwitchFilter;
+ CopyFileStepFactory m_copyFileStepFactory;
+ CopyDirectoryStepFactory m_copyDirectoryFactory;
ProcessStepFactory m_processStepFactory;
AllProjectsFind m_allProjectsFind;
@@ -691,9 +665,7 @@ public:
// Settings pages
ProjectExplorerSettingsPage m_projectExplorerSettingsPage;
- BuildPropertiesSettingsPage m_buildPropertiesSettingsPage{&m_buildPropertiesSettings};
AppOutputSettingsPage m_appOutputSettingsPage;
- CompileOutputSettingsPage m_compileOutputSettingsPage;
DeviceSettingsPage m_deviceSettingsPage;
SshSettingsPage m_sshSettingsPage;
CustomParsersSettingsPage m_customParsersSettingsPage;
@@ -721,6 +693,7 @@ public:
cmakeRunConfigFactory.runConfigurationId()
}};
+ DeviceCheckBuildStepFactory deviceCheckBuildStepFactory;
SanitizerOutputFormatterFactory sanitizerFormatterFactory;
};
@@ -743,7 +716,7 @@ static void openProjectsInDirectory(const FilePath &filePath)
{
const FilePaths projectFiles = projectsInDirectory(filePath);
if (!projectFiles.isEmpty())
- Core::ICore::openFiles(projectFiles);
+ ICore::openFiles(projectFiles);
}
static QStringList projectNames(const QVector<FolderNode *> &folders)
@@ -766,7 +739,7 @@ static QVector<FolderNode *> renamableFolderNodes(const FilePath &before, const
return folderNodes;
}
-static QVector<FolderNode *> removableFolderNodes(const Utils::FilePath &filePath)
+static QVector<FolderNode *> removableFolderNodes(const FilePath &filePath)
{
QVector<FolderNode *> folderNodes;
ProjectTree::forEachNode([&](Node *node) {
@@ -831,40 +804,33 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
IWizardFactory::registerFeatureProvider(new KitFeatureProvider);
IWizardFactory::registerFactoryCreator([] { return new SimpleProjectWizard; });
- connect(&dd->m_welcomePage, &ProjectWelcomePage::manageSessions,
- dd, &ProjectExplorerPluginPrivate::showSessionManager);
-
- SessionManager *sessionManager = &dd->m_sessionManager;
- connect(sessionManager, &SessionManager::projectAdded,
+ ProjectManager *sessionManager = &dd->m_sessionManager;
+ connect(sessionManager, &ProjectManager::projectAdded,
this, &ProjectExplorerPlugin::fileListChanged);
- connect(sessionManager, &SessionManager::aboutToRemoveProject,
+ connect(sessionManager, &ProjectManager::aboutToRemoveProject,
dd, &ProjectExplorerPluginPrivate::invalidateProject);
- connect(sessionManager, &SessionManager::projectRemoved,
+ connect(sessionManager, &ProjectManager::projectRemoved,
this, &ProjectExplorerPlugin::fileListChanged);
- connect(sessionManager, &SessionManager::projectAdded,
+ connect(sessionManager, &ProjectManager::projectAdded,
dd, &ProjectExplorerPluginPrivate::projectAdded);
- connect(sessionManager, &SessionManager::projectRemoved,
+ connect(sessionManager, &ProjectManager::projectRemoved,
dd, &ProjectExplorerPluginPrivate::projectRemoved);
- connect(sessionManager, &SessionManager::projectDisplayNameChanged,
+ connect(sessionManager, &ProjectManager::projectDisplayNameChanged,
dd, &ProjectExplorerPluginPrivate::projectDisplayNameChanged);
- connect(sessionManager, &SessionManager::dependencyChanged,
+ connect(sessionManager, &ProjectManager::dependencyChanged,
dd, &ProjectExplorerPluginPrivate::updateActions);
- connect(sessionManager, &SessionManager::sessionLoaded,
+ connect(SessionManager::instance(), &SessionManager::sessionLoaded,
dd, &ProjectExplorerPluginPrivate::updateActions);
- connect(sessionManager, &SessionManager::sessionLoaded,
+ connect(SessionManager::instance(), &SessionManager::sessionLoaded,
dd, &ProjectExplorerPluginPrivate::updateWelcomePage);
- connect(sessionManager, &SessionManager::sessionLoaded,
+ connect(SessionManager::instance(), &SessionManager::sessionLoaded,
dd, &ProjectExplorerPluginPrivate::loadSesssionTasks);
-
- connect(sessionManager, &SessionManager::projectAdded, dd, [](ProjectExplorer::Project *project) {
- dd->m_allProjectDirectoriesFilter.addDirectory(project->projectDirectory());
- });
- connect(sessionManager,
- &SessionManager::projectRemoved,
- dd,
- [](ProjectExplorer::Project *project) {
- dd->m_allProjectDirectoriesFilter.removeDirectory(project->projectDirectory());
- });
+ connect(SessionManager::instance(), &SessionManager::sessionCreated,
+ dd, &ProjectExplorerPluginPrivate::updateWelcomePage);
+ connect(SessionManager::instance(), &SessionManager::sessionRenamed,
+ dd, &ProjectExplorerPluginPrivate::updateWelcomePage);
+ connect(SessionManager::instance(), &SessionManager::sessionRemoved,
+ dd, &ProjectExplorerPluginPrivate::updateWelcomePage);
ProjectTree *tree = &dd->m_projectTree;
connect(tree, &ProjectTree::currentProjectChanged, dd, [] {
@@ -903,7 +869,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
ICore::addPreCloseListener([]() -> bool { return coreAboutToClose(); });
- connect(SessionManager::instance(), &SessionManager::projectRemoved,
+ connect(ProjectManager::instance(), &ProjectManager::projectRemoved,
&dd->m_outputPane, &AppOutputPane::projectRemoved);
// ProjectPanelFactories
@@ -1182,23 +1148,6 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
connect(mfile->menu(), &QMenu::aboutToShow,
dd, &ProjectExplorerPluginPrivate::updateRecentProjectMenu);
- // session menu
- ActionContainer *msession = ActionManager::createMenu(Constants::M_SESSION);
- msession->menu()->setTitle(Tr::tr("S&essions"));
- msession->setOnAllDisabledBehavior(ActionContainer::Show);
- mfile->addMenu(msession, Core::Constants::G_FILE_OPEN);
- dd->m_sessionMenu = msession->menu();
- connect(mfile->menu(), &QMenu::aboutToShow,
- dd, &ProjectExplorerPluginPrivate::updateSessionMenu);
-
- // session manager action
- dd->m_sessionManagerAction = new QAction(Tr::tr("&Manage..."), this);
- dd->m_sessionMenu->addAction(dd->m_sessionManagerAction);
- dd->m_sessionMenu->addSeparator();
- cmd = ActionManager::registerAction(dd->m_sessionManagerAction,
- "ProjectExplorer.ManageSessions");
- cmd->setDefaultKeySequence(QKeySequence());
-
// unload action
dd->m_unloadAction = new ParameterAction(Tr::tr("Close Project"), Tr::tr("Close Pro&ject \"%1\""),
ParameterAction::AlwaysEnabled, this);
@@ -1345,7 +1294,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
// without a project loaded.
connect(generatorContainer->menu(), &QMenu::aboutToShow, [menu = generatorContainer->menu()] {
menu->clear();
- if (Project * const project = SessionManager::startupProject()) {
+ if (Project * const project = ProjectManager::startupProject()) {
for (const auto &generator : project->allGenerators()) {
menu->addAction(generator.second, [project, id = generator.first] {
project->runGenerator(id);
@@ -1620,7 +1569,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
Command * const expandCmd = ActionManager::registerAction(
dd->m_projectTreeExpandAllAction, Constants::PROJECTTREE_EXPAND_ALL,
projectTreeContext);
- for (Core::ActionContainer * const ac : {mfileContextMenu, msubProjectContextMenu,
+ for (ActionContainer * const ac : {mfileContextMenu, msubProjectContextMenu,
mfolderContextMenu, mprojectContextMenu, msessionContextMenu}) {
ac->addSeparator(treeGroup);
ac->addAction(expandNodeCmd, treeGroup);
@@ -1657,12 +1606,8 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
connect(ICore::instance(), &ICore::saveSettingsRequested,
dd, &ProjectExplorerPluginPrivate::savePersistentSettings);
- connect(EditorManager::instance(), &EditorManager::autoSaved, this, [] {
- if (!dd->m_shuttingDown && !SessionManager::loadingSession())
- SessionManager::save();
- });
connect(qApp, &QApplication::applicationStateChanged, this, [](Qt::ApplicationState state) {
- if (!dd->m_shuttingDown && state == Qt::ApplicationActive)
+ if (!PluginManager::isShuttingDown() && state == Qt::ApplicationActive)
dd->updateWelcomePage();
});
@@ -1697,10 +1642,6 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
.toBool();
dd->m_projectExplorerSettings.useJom
= s->value(Constants::USE_JOM_SETTINGS_KEY, defaultSettings.useJom).toBool();
- dd->m_projectExplorerSettings.autorestoreLastSession
- = s->value(Constants::AUTO_RESTORE_SESSION_SETTINGS_KEY,
- defaultSettings.autorestoreLastSession)
- .toBool();
dd->m_projectExplorerSettings.addLibraryPathsToRunEnv
= s->value(Constants::ADD_LIBRARY_PATHS_TO_RUN_ENV_SETTINGS_KEY,
defaultSettings.addLibraryPathsToRunEnv)
@@ -1723,7 +1664,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
if (tmp < 0 || tmp > int(StopBeforeBuild::SameApp))
tmp = int(defaultSettings.stopBeforeBuild);
dd->m_projectExplorerSettings.stopBeforeBuild = StopBeforeBuild(tmp);
- dd->m_projectExplorerSettings.terminalMode = static_cast<ProjectExplorer::TerminalMode>(
+ dd->m_projectExplorerSettings.terminalMode = static_cast<TerminalMode>(
s->value(Constants::TERMINAL_MODE_SETTINGS_KEY, int(defaultSettings.terminalMode)).toInt());
dd->m_projectExplorerSettings.closeSourceFilesWithProject
= s->value(Constants::CLOSE_FILES_WITH_PROJECT_SETTINGS_KEY,
@@ -1757,27 +1698,25 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
connect(buildManager, &BuildManager::buildQueueFinished,
dd, &ProjectExplorerPluginPrivate::buildQueueFinished, Qt::QueuedConnection);
- connect(dd->m_sessionManagerAction, &QAction::triggered,
- dd, &ProjectExplorerPluginPrivate::showSessionManager);
connect(dd->m_newAction, &QAction::triggered,
dd, &ProjectExplorerPlugin::openNewProjectDialog);
connect(dd->m_loadAction, &QAction::triggered,
dd, &ProjectExplorerPluginPrivate::loadAction);
connect(dd->m_buildProjectOnlyAction, &QAction::triggered, dd, [] {
- BuildManager::buildProjectWithoutDependencies(SessionManager::startupProject());
+ BuildManager::buildProjectWithoutDependencies(ProjectManager::startupProject());
});
connect(dd->m_buildAction, &QAction::triggered, dd, [] {
- BuildManager::buildProjectWithDependencies(SessionManager::startupProject());
+ BuildManager::buildProjectWithDependencies(ProjectManager::startupProject());
});
connect(dd->m_buildProjectForAllConfigsAction, &QAction::triggered, dd, [] {
- BuildManager::buildProjectWithDependencies(SessionManager::startupProject(),
+ BuildManager::buildProjectWithDependencies(ProjectManager::startupProject(),
ConfigSelection::All);
});
connect(dd->m_buildActionContextMenu, &QAction::triggered, dd, [] {
BuildManager::buildProjectWithoutDependencies(ProjectTree::currentProject());
});
connect(dd->m_buildForRunConfigAction, &QAction::triggered, dd, [] {
- const Project * const project = SessionManager::startupProject();
+ const Project * const project = ProjectManager::startupProject();
QTC_ASSERT(project, return);
const Target * const target = project->activeTarget();
QTC_ASSERT(target, return);
@@ -1792,20 +1731,20 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
BuildManager::buildProjectWithDependencies(ProjectTree::currentProject());
});
connect(dd->m_buildSessionAction, &QAction::triggered, dd, [] {
- BuildManager::buildProjects(SessionManager::projectOrder(), ConfigSelection::Active);
+ BuildManager::buildProjects(ProjectManager::projectOrder(), ConfigSelection::Active);
});
connect(dd->m_buildSessionForAllConfigsAction, &QAction::triggered, dd, [] {
- BuildManager::buildProjects(SessionManager::projectOrder(), ConfigSelection::All);
+ BuildManager::buildProjects(ProjectManager::projectOrder(), ConfigSelection::All);
});
connect(dd->m_rebuildProjectOnlyAction, &QAction::triggered, dd, [] {
- BuildManager::rebuildProjectWithoutDependencies(SessionManager::startupProject());
+ BuildManager::rebuildProjectWithoutDependencies(ProjectManager::startupProject());
});
connect(dd->m_rebuildAction, &QAction::triggered, dd, [] {
- BuildManager::rebuildProjectWithDependencies(SessionManager::startupProject(),
+ BuildManager::rebuildProjectWithDependencies(ProjectManager::startupProject(),
ConfigSelection::Active);
});
connect(dd->m_rebuildProjectForAllConfigsAction, &QAction::triggered, dd, [] {
- BuildManager::rebuildProjectWithDependencies(SessionManager::startupProject(),
+ BuildManager::rebuildProjectWithDependencies(ProjectManager::startupProject(),
ConfigSelection::All);
});
connect(dd->m_rebuildActionContextMenu, &QAction::triggered, dd, [] {
@@ -1816,32 +1755,32 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
ConfigSelection::Active);
});
connect(dd->m_rebuildSessionAction, &QAction::triggered, dd, [] {
- BuildManager::rebuildProjects(SessionManager::projectOrder(), ConfigSelection::Active);
+ BuildManager::rebuildProjects(ProjectManager::projectOrder(), ConfigSelection::Active);
});
connect(dd->m_rebuildSessionForAllConfigsAction, &QAction::triggered, dd, [] {
- BuildManager::rebuildProjects(SessionManager::projectOrder(), ConfigSelection::All);
+ BuildManager::rebuildProjects(ProjectManager::projectOrder(), ConfigSelection::All);
});
connect(dd->m_deployProjectOnlyAction, &QAction::triggered, dd, [] {
- BuildManager::deployProjects({SessionManager::startupProject()});
+ BuildManager::deployProjects({ProjectManager::startupProject()});
});
connect(dd->m_deployAction, &QAction::triggered, dd, [] {
- BuildManager::deployProjects(SessionManager::projectOrder(SessionManager::startupProject()));
+ BuildManager::deployProjects(ProjectManager::projectOrder(ProjectManager::startupProject()));
});
connect(dd->m_deployActionContextMenu, &QAction::triggered, dd, [] {
BuildManager::deployProjects({ProjectTree::currentProject()});
});
connect(dd->m_deploySessionAction, &QAction::triggered, dd, [] {
- BuildManager::deployProjects(SessionManager::projectOrder());
+ BuildManager::deployProjects(ProjectManager::projectOrder());
});
connect(dd->m_cleanProjectOnlyAction, &QAction::triggered, dd, [] {
- BuildManager::cleanProjectWithoutDependencies(SessionManager::startupProject());
+ BuildManager::cleanProjectWithoutDependencies(ProjectManager::startupProject());
});
connect(dd->m_cleanAction, &QAction::triggered, dd, [] {
- BuildManager::cleanProjectWithDependencies(SessionManager::startupProject(),
+ BuildManager::cleanProjectWithDependencies(ProjectManager::startupProject(),
ConfigSelection::Active);
});
connect(dd->m_cleanProjectForAllConfigsAction, &QAction::triggered, dd, [] {
- BuildManager::cleanProjectWithDependencies(SessionManager::startupProject(),
+ BuildManager::cleanProjectWithDependencies(ProjectManager::startupProject(),
ConfigSelection::All);
});
connect(dd->m_cleanActionContextMenu, &QAction::triggered, dd, [] {
@@ -1852,10 +1791,10 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
ConfigSelection::Active);
});
connect(dd->m_cleanSessionAction, &QAction::triggered, dd, [] {
- BuildManager::cleanProjects(SessionManager::projectOrder(), ConfigSelection::Active);
+ BuildManager::cleanProjects(ProjectManager::projectOrder(), ConfigSelection::Active);
});
connect(dd->m_cleanSessionForAllConfigsAction, &QAction::triggered, dd, [] {
- BuildManager::cleanProjects(SessionManager::projectOrder(), ConfigSelection::All);
+ BuildManager::cleanProjects(ProjectManager::projectOrder(), ConfigSelection::All);
});
connect(dd->m_runAction, &QAction::triggered,
dd, [] { runStartupProject(Constants::NORMAL_RUN_MODE); });
@@ -1920,7 +1859,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
connect(dd->m_setStartupProjectAction, &QAction::triggered,
dd, &ProjectExplorerPluginPrivate::handleSetStartupProject);
connect(dd->m_closeProjectFilesActionFileMenu, &QAction::triggered,
- dd, [] { dd->closeAllFilesInProject(SessionManager::projects().first()); });
+ dd, [] { dd->closeAllFilesInProject(ProjectManager::projects().first()); });
connect(dd->m_closeProjectFilesActionContextMenu, &QAction::triggered,
dd, [] { dd->closeAllFilesInProject(ProjectTree::currentProject()); });
connect(dd->m_projectTreeCollapseAllAction, &QAction::triggered,
@@ -1935,6 +1874,18 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
dd->updateContextMenuActions(ProjectTree::currentNode());
});
+ connect(ModeManager::instance(),
+ &ModeManager::currentModeChanged,
+ dd,
+ &ProjectExplorerPluginPrivate::currentModeChanged);
+ connect(&dd->m_welcomePage,
+ &ProjectWelcomePage::requestProject,
+ m_instance,
+ &ProjectExplorerPlugin::openProjectWelcomePage);
+ connect(SessionManager::instance(),
+ &SessionManager::startupSessionRestored,
+ m_instance,
+ &ProjectExplorerPlugin::finishedInitialization);
dd->updateWelcomePage();
MacroExpander *expander = Utils::globalMacroExpander();
@@ -1965,7 +1916,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
Project::addVariablesToMacroExpander("ActiveProject:",
"Active project",
expander,
- &SessionManager::startupProject);
+ &ProjectManager::startupProject);
EnvironmentProvider::addProvider(
{"ActiveProject:BuildConfig:Env", Tr::tr("Active build environment of the active project."), [] {
if (const BuildConfiguration * const bc = activeBuildConfiguration())
@@ -1981,15 +1932,6 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
return Environment::systemEnvironment();
}});
- const auto fileHandler = [] {
- return SessionManager::sessionNameToFileName(SessionManager::activeSession());
- };
- expander->registerFileVariables("Session", Tr::tr("File where current session is saved."),
- fileHandler);
- expander->registerVariable("Session:Name", Tr::tr("Name of current session."), [] {
- return SessionManager::activeSession();
- });
-
DeviceManager::instance()->addDevice(IDevice::Ptr(new DesktopDevice));
if (auto sanitizerTester = SanitizerParser::testCreator())
@@ -2032,7 +1974,7 @@ void ProjectExplorerPluginPrivate::unloadProjectContextMenu()
void ProjectExplorerPluginPrivate::unloadOtherProjectsContextMenu()
{
if (Project *currentProject = ProjectTree::currentProject()) {
- const QList<Project *> projects = SessionManager::projects();
+ const QList<Project *> projects = ProjectManager::projects();
QTC_ASSERT(!projects.isEmpty(), return);
for (Project *p : projects) {
@@ -2045,7 +1987,7 @@ void ProjectExplorerPluginPrivate::unloadOtherProjectsContextMenu()
void ProjectExplorerPluginPrivate::handleUnloadProject()
{
- QList<Project *> projects = SessionManager::projects();
+ QList<Project *> projects = ProjectManager::projects();
QTC_ASSERT(!projects.isEmpty(), return);
ProjectExplorerPlugin::unloadProject(projects.first());
@@ -2072,7 +2014,7 @@ void ProjectExplorerPlugin::unloadProject(Project *project)
dd->addToRecentProjects(project->projectFilePath(), project->displayName());
- SessionManager::removeProject(project);
+ ProjectManager::removeProject(project);
dd->updateActions();
}
@@ -2081,7 +2023,7 @@ void ProjectExplorerPluginPrivate::closeAllProjects()
if (!EditorManager::closeAllDocuments())
return; // Action has been cancelled
- SessionManager::closeAllProjects();
+ ProjectManager::closeAllProjects();
updateActions();
ModeManager::activateMode(Core::Constants::MODE_WELCOME);
@@ -2137,13 +2079,13 @@ void ProjectExplorerPlugin::extensionsInitialized()
Tr::tr("Sanitizer", "Category for sanitizer issues listed under 'Issues'"));
TaskHub::addCategory(Constants::TASK_CATEGORY_TASKLIST_ID, Tr::tr("My Tasks"));
- SshSettings::loadSettings(Core::ICore::settings());
+ SshSettings::loadSettings(ICore::settings());
const auto searchPathRetriever = [] {
- FilePaths searchPaths = {Core::ICore::libexecPath()};
+ FilePaths searchPaths = {ICore::libexecPath()};
if (HostOsInfo::isWindowsHost()) {
- const QString gitBinary = Core::ICore::settings()->value("Git/BinaryPath", "git")
+ const QString gitBinary = ICore::settings()->value("Git/BinaryPath", "git")
.toString();
- const QStringList rawGitSearchPaths = Core::ICore::settings()->value("Git/Path")
+ const QStringList rawGitSearchPaths = ICore::settings()->value("Git/Path")
.toString().split(':', Qt::SkipEmptyParts);
const FilePaths gitSearchPaths = Utils::transform(rawGitSearchPaths,
[](const QString &rawPath) { return FilePath::fromString(rawPath); });
@@ -2177,11 +2119,12 @@ void ProjectExplorerPlugin::extensionsInitialized()
void ProjectExplorerPlugin::restoreKits()
{
- dd->determineSessionToRestoreAtStartup();
ExtraAbi::load(); // Load this before Toolchains!
ToolChainManager::restoreToolChains();
KitManager::restoreKits();
- QTimer::singleShot(0, dd, &ProjectExplorerPluginPrivate::restoreSession); // delay a bit...
+ // restoring startup session is supposed to be done as a result of ICore::coreOpened,
+ // and that is supposed to happen after restoring kits:
+ QTC_CHECK(!SessionManager::isStartupSessionRestored());
}
void ProjectExplorerPluginPrivate::updateRunWithoutDeployMenu()
@@ -2189,15 +2132,13 @@ void ProjectExplorerPluginPrivate::updateRunWithoutDeployMenu()
m_runWithoutDeployAction->setVisible(m_projectExplorerSettings.deployBeforeRun);
}
-ExtensionSystem::IPlugin::ShutdownFlag ProjectExplorerPlugin::aboutToShutdown()
+IPlugin::ShutdownFlag ProjectExplorerPlugin::aboutToShutdown()
{
disconnect(ModeManager::instance(), &ModeManager::currentModeChanged,
dd, &ProjectExplorerPluginPrivate::currentModeChanged);
ProjectTree::aboutToShutDown();
ToolChainManager::aboutToShutdown();
- SessionManager::closeAllProjects();
-
- dd->m_shuttingDown = true;
+ ProjectManager::closeAllProjects();
// Attempt to synchronously shutdown all run controls.
// If that fails, fall back to asynchronous shutdown (Debugger run controls
@@ -2210,11 +2151,6 @@ ExtensionSystem::IPlugin::ShutdownFlag ProjectExplorerPlugin::aboutToShutdown()
return AsynchronousShutdown;
}
-void ProjectExplorerPlugin::showSessionManager()
-{
- dd->showSessionManager();
-}
-
void ProjectExplorerPlugin::openNewProjectDialog()
{
if (!ICore::isNewItemDialogRunning()) {
@@ -2226,25 +2162,11 @@ void ProjectExplorerPlugin::openNewProjectDialog()
}
}
-void ProjectExplorerPluginPrivate::showSessionManager()
-{
- SessionManager::save();
- SessionDialog sessionDialog(ICore::dialogParent());
- sessionDialog.setAutoLoadSession(dd->m_projectExplorerSettings.autorestoreLastSession);
- sessionDialog.exec();
- dd->m_projectExplorerSettings.autorestoreLastSession = sessionDialog.autoLoadSession();
-
- updateActions();
-
- if (ModeManager::currentModeId() == Core::Constants::MODE_WELCOME)
- updateWelcomePage();
-}
-
void ProjectExplorerPluginPrivate::setStartupProject(Project *project)
{
if (!project)
return;
- SessionManager::setStartupProject(project);
+ ProjectManager::setStartupProject(project);
updateActions();
}
@@ -2255,7 +2177,7 @@ bool ProjectExplorerPluginPrivate::closeAllFilesInProject(const Project *project
Utils::erase(openFiles, [project](const DocumentModel::Entry *entry) {
return entry->pinned || !project->isKnownFile(entry->filePath());
});
- for (const Project * const otherProject : SessionManager::projects()) {
+ for (const Project * const otherProject : ProjectManager::projects()) {
if (otherProject == project)
continue;
Utils::erase(openFiles, [otherProject](const DocumentModel::Entry *entry) {
@@ -2267,23 +2189,15 @@ bool ProjectExplorerPluginPrivate::closeAllFilesInProject(const Project *project
void ProjectExplorerPluginPrivate::savePersistentSettings()
{
- if (dd->m_shuttingDown)
+ if (PluginManager::isShuttingDown())
return;
- if (!SessionManager::loadingSession()) {
- for (Project *pro : SessionManager::projects())
+ if (!SessionManager::isLoadingSession()) {
+ for (Project *pro : ProjectManager::projects())
pro->saveSettings();
-
- SessionManager::save();
}
QtcSettings *s = ICore::settings();
- if (SessionManager::isDefaultVirgin()) {
- s->remove(Constants::STARTUPSESSION_KEY);
- } else {
- s->setValue(Constants::STARTUPSESSION_KEY, SessionManager::activeSession());
- s->setValue(Constants::LASTSESSION_KEY, SessionManager::activeSession());
- }
s->remove(QLatin1String("ProjectExplorer/RecentProjects/Files"));
QStringList fileNames;
@@ -2312,9 +2226,6 @@ void ProjectExplorerPluginPrivate::savePersistentSettings()
s->setValueWithDefault(Constants::USE_JOM_SETTINGS_KEY,
dd->m_projectExplorerSettings.useJom,
defaultSettings.useJom);
- s->setValueWithDefault(Constants::AUTO_RESTORE_SESSION_SETTINGS_KEY,
- dd->m_projectExplorerSettings.autorestoreLastSession,
- defaultSettings.autorestoreLastSession);
s->setValueWithDefault(Constants::ADD_LIBRARY_PATHS_TO_RUN_ENV_SETTINGS_KEY,
dd->m_projectExplorerSettings.addLibraryPathsToRunEnv,
defaultSettings.addLibraryPathsToRunEnv);
@@ -2368,7 +2279,7 @@ ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProject(cons
if (!project)
return result;
dd->addToRecentProjects(filePath, project->displayName());
- SessionManager::setStartupProject(project);
+ ProjectManager::setStartupProject(project);
return result;
}
@@ -2420,11 +2331,11 @@ ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProjects(con
QTC_ASSERT(!fileName.isEmpty(), continue);
const FilePath filePath = fileName.absoluteFilePath();
- Project *found = Utils::findOrDefault(SessionManager::projects(),
+ Project *found = Utils::findOrDefault(ProjectManager::projects(),
Utils::equal(&Project::projectFilePath, filePath));
if (found) {
alreadyOpen.append(found);
- SessionManager::reportProjectLoadingProgress();
+ SessionManager::sessionLoadingProgress();
continue;
}
@@ -2439,7 +2350,7 @@ ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProjects(con
if (restoreResult == Project::RestoreResult::Ok) {
connect(pro, &Project::fileListChanged,
m_instance, &ProjectExplorerPlugin::fileListChanged);
- SessionManager::addProject(pro);
+ ProjectManager::addProject(pro);
openedPro += pro;
} else {
if (restoreResult == Project::RestoreResult::Error)
@@ -2453,7 +2364,7 @@ ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProjects(con
.arg(mt.name()));
}
if (filePaths.size() > 1)
- SessionManager::reportProjectLoadingProgress();
+ SessionManager::sessionLoadingProgress();
}
dd->updateActions();
@@ -2496,33 +2407,6 @@ void ProjectExplorerPluginPrivate::currentModeChanged(Id mode, Id oldMode)
updateWelcomePage();
}
-void ProjectExplorerPluginPrivate::determineSessionToRestoreAtStartup()
-{
- // Process command line arguments first:
- const bool lastSessionArg =
- ExtensionSystem::PluginManager::specForPlugin(m_instance)->arguments().contains("-lastsession");
- m_sessionToRestoreAtStartup = lastSessionArg ? SessionManager::startupSession() : QString();
- const QStringList arguments = ExtensionSystem::PluginManager::arguments();
- if (!lastSessionArg) {
- QStringList sessions = SessionManager::sessions();
- // We have command line arguments, try to find a session in them
- // Default to no session loading
- for (const QString &arg : arguments) {
- if (sessions.contains(arg)) {
- // Session argument
- m_sessionToRestoreAtStartup = arg;
- break;
- }
- }
- }
- // Handle settings only after command line arguments:
- if (m_sessionToRestoreAtStartup.isEmpty() && m_projectExplorerSettings.autorestoreLastSession)
- m_sessionToRestoreAtStartup = SessionManager::startupSession();
-
- if (!m_sessionToRestoreAtStartup.isEmpty())
- ModeManager::activateMode(Core::Constants::MODE_EDIT);
-}
-
// Return a list of glob patterns for project files ("*.pro", etc), use first, main pattern only.
QStringList ProjectExplorerPlugin::projectFileGlobs()
{
@@ -2548,70 +2432,6 @@ MiniProjectTargetSelector *ProjectExplorerPlugin::targetSelector()
return dd->m_targetSelector;
}
-/*!
- This function is connected to the ICore::coreOpened signal. If
- there was no session explicitly loaded, it creates an empty new
- default session and puts the list of recent projects and sessions
- onto the welcome page.
-*/
-void ProjectExplorerPluginPrivate::restoreSession()
-{
- // We have command line arguments, try to find a session in them
- QStringList arguments = ExtensionSystem::PluginManager::arguments();
- if (!dd->m_sessionToRestoreAtStartup.isEmpty() && !arguments.isEmpty())
- arguments.removeOne(dd->m_sessionToRestoreAtStartup);
-
- // Massage the argument list.
- // Be smart about directories: If there is a session of that name, load it.
- // Other than that, look for project files in it. The idea is to achieve
- // 'Do what I mean' functionality when starting Creator in a directory with
- // the single command line argument '.' and avoid editor warnings about not
- // being able to open directories.
- // In addition, convert "filename" "+45" or "filename" ":23" into
- // "filename+45" and "filename:23".
- if (!arguments.isEmpty()) {
- const QStringList sessions = SessionManager::sessions();
- for (int a = 0; a < arguments.size(); ) {
- const QString &arg = arguments.at(a);
- const QFileInfo fi(arg);
- if (fi.isDir()) {
- const QDir dir(fi.absoluteFilePath());
- // Does the directory name match a session?
- if (dd->m_sessionToRestoreAtStartup.isEmpty()
- && sessions.contains(dir.dirName())) {
- dd->m_sessionToRestoreAtStartup = dir.dirName();
- arguments.removeAt(a);
- continue;
- }
- } // Done directories.
- // Converts "filename" "+45" or "filename" ":23" into "filename+45" and "filename:23"
- if (a && (arg.startsWith(QLatin1Char('+')) || arg.startsWith(QLatin1Char(':')))) {
- arguments[a - 1].append(arguments.takeAt(a));
- continue;
- }
- ++a;
- } // for arguments
- } // !arguments.isEmpty()
- // Restore latest session or what was passed on the command line
-
- SessionManager::loadSession(!dd->m_sessionToRestoreAtStartup.isEmpty()
- ? dd->m_sessionToRestoreAtStartup : QString(), true);
-
- // update welcome page
- connect(ModeManager::instance(), &ModeManager::currentModeChanged,
- dd, &ProjectExplorerPluginPrivate::currentModeChanged);
- connect(&dd->m_welcomePage, &ProjectWelcomePage::requestProject,
- m_instance, &ProjectExplorerPlugin::openProjectWelcomePage);
- dd->m_arguments = arguments;
- // delay opening projects from the command line even more
- QTimer::singleShot(0, m_instance, [] {
- ICore::openFiles(Utils::transform(dd->m_arguments, &FilePath::fromUserInput),
- ICore::OpenFilesFlags(ICore::CanContainLineAndColumnNumbers | ICore::SwitchMode));
- emit m_instance->finishedInitialization();
- });
- updateActions();
-}
-
void ProjectExplorerPluginPrivate::executeRunConfiguration(RunConfiguration *runConfiguration, Id runMode)
{
const Tasks runConfigIssues = runConfiguration->checkForIssues();
@@ -2675,7 +2495,7 @@ void ProjectExplorerPluginPrivate::checkForShutdown()
{
--m_activeRunControlCount;
QTC_ASSERT(m_activeRunControlCount >= 0, m_activeRunControlCount = 0);
- if (m_shuttingDown && m_activeRunControlCount == 0)
+ if (PluginManager::isShuttingDown() && m_activeRunControlCount == 0)
emit m_instance->asynchronousShutdownFinished();
}
@@ -2732,7 +2552,7 @@ RecentProjectsEntries ProjectExplorerPluginPrivate::recentProjects() const
void ProjectExplorerPluginPrivate::updateActions()
{
- const Project *const project = SessionManager::startupProject();
+ const Project *const project = ProjectManager::startupProject();
const Project *const currentProject = ProjectTree::currentProject(); // for context menu actions
const QPair<bool, QString> buildActionState = buildSettingsEnabled(project);
@@ -2743,10 +2563,10 @@ void ProjectExplorerPluginPrivate::updateActions()
const QString projectName = project ? project->displayName() : QString();
const QString projectNameContextMenu = currentProject ? currentProject->displayName() : QString();
- m_unloadAction->setParameter(SessionManager::projects().size() == 1 ? projectName : QString());
+ m_unloadAction->setParameter(ProjectManager::projects().size() == 1 ? projectName : QString());
m_unloadActionContextMenu->setParameter(projectNameContextMenu);
m_unloadOthersActionContextMenu->setParameter(projectNameContextMenu);
- m_closeProjectFilesActionFileMenu->setParameter(SessionManager::projects().size() == 1
+ m_closeProjectFilesActionFileMenu->setParameter(ProjectManager::projects().size() == 1
? projectName : QString());
m_closeProjectFilesActionContextMenu->setParameter(projectNameContextMenu);
@@ -2791,7 +2611,7 @@ void ProjectExplorerPluginPrivate::updateActions()
m_setStartupProjectAction->setParameter(projectNameContextMenu);
m_setStartupProjectAction->setVisible(currentProject != project);
- const bool hasDependencies = SessionManager::projectOrder(currentProject).size() > 1;
+ const bool hasDependencies = ProjectManager::projectOrder(currentProject).size() > 1;
m_buildActionContextMenu->setVisible(hasDependencies);
m_rebuildActionContextMenu->setVisible(hasDependencies);
m_cleanActionContextMenu->setVisible(hasDependencies);
@@ -2818,17 +2638,17 @@ void ProjectExplorerPluginPrivate::updateActions()
m_cleanProjectOnlyAction->setToolTip(buildActionState.second);
// Session actions
- m_closeAllProjects->setEnabled(SessionManager::hasProjects());
- m_unloadAction->setEnabled(SessionManager::projects().size() <= 1);
- m_unloadAction->setEnabled(SessionManager::projects().size() == 1);
- m_unloadActionContextMenu->setEnabled(SessionManager::hasProjects());
- m_unloadOthersActionContextMenu->setEnabled(SessionManager::projects().size() >= 2);
- m_closeProjectFilesActionFileMenu->setEnabled(SessionManager::projects().size() == 1);
- m_closeProjectFilesActionContextMenu->setEnabled(SessionManager::hasProjects());
+ m_closeAllProjects->setEnabled(ProjectManager::hasProjects());
+ m_unloadAction->setEnabled(ProjectManager::projects().size() <= 1);
+ m_unloadAction->setEnabled(ProjectManager::projects().size() == 1);
+ m_unloadActionContextMenu->setEnabled(ProjectManager::hasProjects());
+ m_unloadOthersActionContextMenu->setEnabled(ProjectManager::projects().size() >= 2);
+ m_closeProjectFilesActionFileMenu->setEnabled(ProjectManager::projects().size() == 1);
+ m_closeProjectFilesActionContextMenu->setEnabled(ProjectManager::hasProjects());
ActionContainer *aci =
ActionManager::actionContainer(Constants::M_UNLOADPROJECTS);
- aci->menu()->menuAction()->setEnabled(SessionManager::hasProjects());
+ aci->menu()->menuAction()->setEnabled(ProjectManager::hasProjects());
m_buildSessionAction->setEnabled(buildSessionState.first);
m_buildSessionForAllConfigsAction->setEnabled(buildSessionState.first);
@@ -2846,7 +2666,7 @@ void ProjectExplorerPluginPrivate::updateActions()
m_cancelBuildAction->setEnabled(BuildManager::isBuilding());
- const bool hasProjects = SessionManager::hasProjects();
+ const bool hasProjects = ProjectManager::hasProjects();
m_projectSelectorAction->setEnabled(hasProjects);
m_projectSelectorActionMenu->setEnabled(hasProjects);
m_projectSelectorActionQuick->setEnabled(hasProjects);
@@ -2923,10 +2743,9 @@ void ProjectExplorerPluginPrivate::extendFolderNavigationWidgetFactory()
"The file \"%1\" was renamed to \"%2\", "
"but the following projects could not be automatically changed: %3")
.arg(before.toUserOutput(), after.toUserOutput(), projects);
- QTimer::singleShot(0, Core::ICore::instance(), [errorMessage] {
- QMessageBox::warning(Core::ICore::dialogParent(),
- Tr::tr("Project Editing Failed"),
- errorMessage);
+ QTimer::singleShot(0, ICore::instance(), [errorMessage] {
+ QMessageBox::warning(ICore::dialogParent(),
+ Tr::tr("Project Editing Failed"), errorMessage);
});
}
});
@@ -2944,8 +2763,8 @@ void ProjectExplorerPluginPrivate::extendFolderNavigationWidgetFactory()
const QString errorMessage
= Tr::tr("The following projects failed to automatically remove the file: %1")
.arg(projects);
- QTimer::singleShot(0, Core::ICore::instance(), [errorMessage] {
- QMessageBox::warning(Core::ICore::dialogParent(),
+ QTimer::singleShot(0, ICore::instance(), [errorMessage] {
+ QMessageBox::warning(ICore::dialogParent(),
Tr::tr("Project Editing Failed"),
errorMessage);
});
@@ -2967,7 +2786,7 @@ void ProjectExplorerPluginPrivate::runProjectContextMenu(RunConfiguration *rc)
static bool hasBuildSettings(const Project *pro)
{
- return Utils::anyOf(SessionManager::projectOrder(pro), [](const Project *project) {
+ return Utils::anyOf(ProjectManager::projectOrder(pro), [](const Project *project) {
return project
&& project->activeTarget()
&& project->activeTarget()->activeBuildConfiguration();
@@ -2979,7 +2798,7 @@ static QPair<bool, QString> subprojectEnabledState(const Project *pro)
QPair<bool, QString> result;
result.first = true;
- const QList<Project *> &projects = SessionManager::projectOrder(pro);
+ const QList<Project *> &projects = ProjectManager::projectOrder(pro);
for (const Project *project : projects) {
if (project && project->activeTarget()
&& project->activeTarget()->activeBuildConfiguration()
@@ -3021,7 +2840,7 @@ QPair<bool, QString> ProjectExplorerPluginPrivate::buildSettingsEnabledForSessio
{
QPair<bool, QString> result;
result.first = true;
- if (!SessionManager::hasProjects()) {
+ if (!ProjectManager::hasProjects()) {
result.first = false;
result.second = Tr::tr("No project loaded.");
} else if (BuildManager::isBuilding()) {
@@ -3078,7 +2897,7 @@ void ProjectExplorerPlugin::handleCommandLineArguments(const QStringList &argume
static bool hasDeploySettings(Project *pro)
{
- return Utils::anyOf(SessionManager::projectOrder(pro), [](Project *project) {
+ return Utils::anyOf(ProjectManager::projectOrder(pro), [](Project *project) {
return project->activeTarget()
&& project->activeTarget()->activeDeployConfiguration();
});
@@ -3096,7 +2915,7 @@ void ProjectExplorerPlugin::runProject(Project *pro, Id mode, const bool forceSk
void ProjectExplorerPlugin::runStartupProject(Id runMode, bool forceSkipDeploy)
{
- runProject(SessionManager::startupProject(), runMode, forceSkipDeploy);
+ runProject(ProjectManager::startupProject(), runMode, forceSkipDeploy);
}
void ProjectExplorerPlugin::runRunConfiguration(RunConfiguration *rc,
@@ -3157,7 +2976,7 @@ void ProjectExplorerPluginPrivate::projectAdded(Project *pro)
void ProjectExplorerPluginPrivate::projectRemoved(Project *pro)
{
Q_UNUSED(pro)
- m_projectsMode.setEnabled(SessionManager::hasProjects());
+ m_projectsMode.setEnabled(ProjectManager::hasProjects());
}
void ProjectExplorerPluginPrivate::projectDisplayNameChanged(Project *pro)
@@ -3168,7 +2987,7 @@ void ProjectExplorerPluginPrivate::projectDisplayNameChanged(Project *pro)
void ProjectExplorerPluginPrivate::updateDeployActions()
{
- Project *project = SessionManager::startupProject();
+ Project *project = ProjectManager::startupProject();
bool enableDeployActions = project
&& !BuildManager::isBuilding(project)
@@ -3187,7 +3006,7 @@ void ProjectExplorerPluginPrivate::updateDeployActions()
enableDeployActionsContextMenu = false;
}
- bool hasProjects = SessionManager::hasProjects();
+ bool hasProjects = ProjectManager::hasProjects();
m_deployAction->setEnabled(enableDeployActions);
@@ -3203,7 +3022,7 @@ void ProjectExplorerPluginPrivate::updateDeployActions()
&& !project->activeTarget()->activeBuildConfiguration()->isEnabled();
};
- if (Utils::anyOf(SessionManager::projectOrder(nullptr), hasDisabledBuildConfiguration))
+ if (Utils::anyOf(ProjectManager::projectOrder(nullptr), hasDisabledBuildConfiguration))
enableDeploySessionAction = false;
}
if (!hasProjects || !hasDeploySettings(nullptr) || BuildManager::isBuilding())
@@ -3215,7 +3034,7 @@ void ProjectExplorerPluginPrivate::updateDeployActions()
bool ProjectExplorerPlugin::canRunStartupProject(Id runMode, QString *whyNot)
{
- Project *project = SessionManager::startupProject();
+ Project *project = ProjectManager::startupProject();
if (!project) {
if (whyNot)
*whyNot = Tr::tr("No active project.");
@@ -3320,7 +3139,7 @@ void ProjectExplorerPluginPrivate::updateUnloadProjectMenu()
ActionContainer *aci = ActionManager::actionContainer(Constants::M_UNLOADPROJECTS);
QMenu *menu = aci->menu();
menu->clear();
- for (Project *project : SessionManager::projects()) {
+ for (Project *project : ProjectManager::projects()) {
QAction *action = menu->addAction(Tr::tr("Close Project \"%1\"").arg(project->displayName()));
connect(action, &QAction::triggered,
[project] { ProjectExplorerPlugin::unloadProject(project); } );
@@ -3419,7 +3238,7 @@ void ProjectExplorerPluginPrivate::updateContextMenuActions(Node *currentNode)
m_removeFileAction->setVisible(true);
m_duplicateFileAction->setVisible(false);
m_deleteFileAction->setVisible(true);
- m_runActionContextMenu->setVisible(false);
+ m_runActionContextMenu->setEnabled(false);
m_defaultRunConfiguration.clear();
m_diffFileAction->setVisible(DiffService::instance());
@@ -3448,7 +3267,7 @@ void ProjectExplorerPluginPrivate::updateContextMenuActions(Node *currentNode)
if (pn && project) {
if (pn == project->rootProjectNode()) {
- m_runActionContextMenu->setVisible(true);
+ m_runActionContextMenu->setEnabled(true);
} else {
QList<RunConfiguration *> runConfigs;
if (Target *t = project->activeTarget()) {
@@ -3459,7 +3278,7 @@ void ProjectExplorerPluginPrivate::updateContextMenuActions(Node *currentNode)
}
}
if (runConfigs.count() == 1) {
- m_runActionContextMenu->setVisible(true);
+ m_runActionContextMenu->setEnabled(true);
m_defaultRunConfiguration = runConfigs.first();
} else if (runConfigs.count() > 1) {
runMenu->menu()->menuAction()->setVisible(true);
@@ -3592,9 +3411,7 @@ void ProjectExplorerPluginPrivate::updateLocationSubMenus()
: Tr::tr("%1 in %2").arg(li.displayName).arg(li.path.toUserOutput());
auto *action = new QAction(displayName, nullptr);
connect(action, &QAction::triggered, this, [line, path] {
- Core::EditorManager::openEditorAt(Link(path, line),
- {},
- Core::EditorManager::AllowExternalEditor);
+ EditorManager::openEditorAt(Link(path, line), {}, EditorManager::AllowExternalEditor);
});
projectMenu->addAction(action);
@@ -3795,6 +3612,13 @@ void ProjectExplorerPluginPrivate::showInFileSystemPane()
Core::FileUtils::showInFileSystemView(currentNode->filePath());
}
+static BuildConfiguration *activeBuildConfiguration(Project *project)
+{
+ if (!project || !project->activeTarget() || !project->activeTarget()->activeBuildConfiguration())
+ return {};
+ return project->activeTarget()->activeBuildConfiguration();
+}
+
void ProjectExplorerPluginPrivate::openTerminalHere(const EnvironmentGetter &env)
{
const Node *currentNode = ProjectTree::currentNode();
@@ -3804,7 +3628,29 @@ void ProjectExplorerPluginPrivate::openTerminalHere(const EnvironmentGetter &env
if (!environment)
return;
- Core::FileUtils::openTerminal(currentNode->directory(), environment.value());
+ BuildConfiguration *bc = activeBuildConfiguration(ProjectTree::projectForNode(currentNode));
+ if (!bc) {
+ Terminal::Hooks::instance().openTerminal({{}, currentNode->directory(), environment});
+ return;
+ }
+
+ IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(bc->target()->kit());
+
+ if (!buildDevice)
+ return;
+
+ FilePath workingDir = currentNode->directory();
+ if (!buildDevice->filePath(workingDir.path()).exists()
+ && !buildDevice->ensureReachable(workingDir))
+ workingDir.clear();
+
+ const FilePath shell = Terminal::defaultShellForDevice(buildDevice->rootPath());
+
+ if (!shell.isEmpty() && buildDevice->rootPath().needsDevice()) {
+ Terminal::Hooks::instance().openTerminal({CommandLine{shell, {}}, workingDir, environment});
+ } else {
+ Terminal::Hooks::instance().openTerminal({std::nullopt, workingDir, environment});
+ }
}
void ProjectExplorerPluginPrivate::openTerminalHereWithRunEnv()
@@ -3825,9 +3671,21 @@ void ProjectExplorerPluginPrivate::openTerminalHereWithRunEnv()
if (!device)
device = DeviceKitAspect::device(target->kit());
QTC_ASSERT(device && device->canOpenTerminal(), return);
- const FilePath workingDir = device->type() == Constants::DESKTOP_DEVICE_TYPE
- ? currentNode->directory() : runnable.workingDirectory;
- device->openTerminal(runnable.environment, workingDir);
+
+ FilePath workingDir = device->type() == Constants::DESKTOP_DEVICE_TYPE
+ ? currentNode->directory()
+ : runnable.workingDirectory;
+
+ if (!device->filePath(workingDir.path()).exists() && !device->ensureReachable(workingDir))
+ workingDir.clear();
+
+ const FilePath shell = Terminal::defaultShellForDevice(device->rootPath());
+ if (!shell.isEmpty() && device->rootPath().needsDevice()) {
+ Terminal::Hooks::instance().openTerminal(
+ {CommandLine{shell, {}}, workingDir, runnable.environment});
+ } else {
+ Terminal::Hooks::instance().openTerminal({std::nullopt, workingDir, runnable.environment});
+ }
}
void ProjectExplorerPluginPrivate::removeFile()
@@ -3852,7 +3710,7 @@ void ProjectExplorerPluginPrivate::removeFile()
if (!siblings.isEmpty()) {
const QMessageBox::StandardButton reply = QMessageBox::question(
- Core::ICore::dialogParent(), Tr::tr("Remove More Files?"),
+ ICore::dialogParent(), Tr::tr("Remove More Files?"),
Tr::tr("Remove these files as well?\n %1")
.arg(Utils::transform<QStringList>(siblings, [](const NodeAndPath &np) {
return np.second.fileName();
@@ -4058,41 +3916,6 @@ void ProjectExplorerPluginPrivate::handleSetStartupProject()
setStartupProject(ProjectTree::currentProject());
}
-void ProjectExplorerPluginPrivate::updateSessionMenu()
-{
- m_sessionMenu->clear();
- dd->m_sessionMenu->addAction(dd->m_sessionManagerAction);
- dd->m_sessionMenu->addSeparator();
- auto *ag = new QActionGroup(m_sessionMenu);
- connect(ag, &QActionGroup::triggered, this, &ProjectExplorerPluginPrivate::setSession);
- const QString activeSession = SessionManager::activeSession();
- const bool isDefaultVirgin = SessionManager::isDefaultVirgin();
-
- QStringList sessions = SessionManager::sessions();
- std::sort(std::next(sessions.begin()), sessions.end(), [](const QString &s1, const QString &s2) {
- return SessionManager::lastActiveTime(s1) > SessionManager::lastActiveTime(s2);
- });
- for (int i = 0; i < sessions.size(); ++i) {
- const QString &session = sessions[i];
-
- const QString actionText = ActionManager::withNumberAccelerator(Utils::quoteAmpersands(
- session),
- i + 1);
- QAction *act = ag->addAction(actionText);
- act->setData(session);
- act->setCheckable(true);
- if (session == activeSession && !isDefaultVirgin)
- act->setChecked(true);
- }
- m_sessionMenu->addActions(ag->actions());
- m_sessionMenu->setEnabled(true);
-}
-
-void ProjectExplorerPluginPrivate::setSession(QAction *action)
-{
- SessionManager::loadSession(action->data().toString());
-}
-
void ProjectExplorerPlugin::setProjectExplorerSettings(const ProjectExplorerSettings &pes)
{
QTC_ASSERT(dd->m_projectExplorerSettings.environmentId == pes.environmentId, return);
@@ -4213,7 +4036,7 @@ void ProjectExplorerPlugin::updateActions()
void ProjectExplorerPlugin::activateProjectPanel(Id panelId)
{
- Core::ModeManager::activateMode(Constants::MODE_SESSION);
+ ModeManager::activateMode(Constants::MODE_SESSION);
dd->m_proWindow->activateProjectPanel(panelId);
}
@@ -4242,9 +4065,8 @@ RecentProjectsEntries ProjectExplorerPlugin::recentProjects()
return dd->recentProjects();
}
-void ProjectExplorerPlugin::renameFilesForSymbol(
- const QString &oldSymbolName, const QString &newSymbolName, const Utils::FilePaths &files,
- bool preferLowerCaseFileNames)
+void ProjectExplorerPlugin::renameFilesForSymbol(const QString &oldSymbolName,
+ const QString &newSymbolName, const FilePaths &files, bool preferLowerCaseFileNames)
{
static const auto isAllLowerCase = [](const QString &text) { return text.toLower() == text; };
@@ -4319,10 +4141,18 @@ AllProjectFilesFilter::AllProjectFilesFilter()
setDefaultIncludedByDefault(false); // but not included in default
setFilters({});
setIsCustomFilter(false);
- setDescription(Tr::tr(
- "Matches all files from all project directories. Append \"+<number>\" or "
- "\":<number>\" to jump to the given line number. Append another "
- "\"+<number>\" or \":<number>\" to jump to the column number as well."));
+ setDescription(Tr::tr("Locates files from all project directories. Append \"+<number>\" or "
+ "\":<number>\" to jump to the given line number. Append another "
+ "\"+<number>\" or \":<number>\" to jump to the column number as well."));
+
+ ProjectManager *projectManager = ProjectManager::instance();
+ QTC_ASSERT(projectManager, return);
+ connect(projectManager, &ProjectManager::projectAdded, this, [this](Project *project) {
+ addDirectory(project->projectDirectory());
+ });
+ connect(projectManager, &ProjectManager::projectRemoved, this, [this](Project *project) {
+ removeDirectory(project->projectDirectory());
+ });
}
const char kDirectoriesKey[] = "directories";
@@ -4346,102 +4176,104 @@ void AllProjectFilesFilter::restoreState(const QJsonObject &object)
DirectoryFilter::restoreState(withoutDirectories);
}
-RunConfigurationLocatorFilter::RunConfigurationLocatorFilter()
+static void setupFilter(ILocatorFilter *filter)
{
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
- this, &RunConfigurationLocatorFilter::targetListUpdated);
-
- targetListUpdated();
+ QObject::connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
+ filter, [filter] { filter->setEnabled(ProjectManager::startupProject()); });
+ filter->setEnabled(ProjectManager::startupProject());
}
-void RunConfigurationLocatorFilter::prepareSearch(const QString &entry)
+using RunAcceptor = std::function<void(RunConfiguration *)>;
+
+static RunConfiguration *runConfigurationForDisplayName(const QString &displayName)
{
- m_result.clear();
- const Target *target = SessionManager::startupTarget();
+ const Target *target = ProjectManager::startupTarget();
if (!target)
- return;
- for (auto rc : target->runConfigurations()) {
- if (rc->displayName().contains(entry, Qt::CaseInsensitive))
- m_result.append(LocatorFilterEntry(this, rc->displayName()));
- }
+ return nullptr;
+ const QList<RunConfiguration *> runconfigs = target->runConfigurations();
+ return Utils::findOrDefault(runconfigs, [displayName](RunConfiguration *rc) {
+ return rc->displayName() == displayName;
+ });
}
-QList<Core::LocatorFilterEntry> RunConfigurationLocatorFilter::matchesFor(
- QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
+static LocatorMatcherTasks runConfigurationMatchers(const RunAcceptor &acceptor)
{
- Q_UNUSED(future)
- Q_UNUSED(entry)
- return m_result;
-}
+ using namespace Tasking;
-void RunConfigurationLocatorFilter::targetListUpdated()
-{
- setEnabled(SessionManager::startupProject()); // at least one project opened
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [storage, acceptor] {
+ const QString input = storage->input();
+ const Target *target = ProjectManager::startupTarget();
+ if (!target)
+ return;
+
+ LocatorFilterEntries entries;
+ for (auto rc : target->runConfigurations()) {
+ if (rc->displayName().contains(input, Qt::CaseInsensitive)) {
+ LocatorFilterEntry entry;
+ entry.displayName = rc->displayName();
+ entry.acceptor = [name = entry.displayName, acceptor = acceptor] {
+ RunConfiguration *config = runConfigurationForDisplayName(name);
+ if (!config)
+ return AcceptResult();
+ acceptor(config);
+ return AcceptResult();
+ };
+ entries.append(entry);
+ }
+ }
+ storage->reportOutput(entries);
+ };
+ return {{Sync(onSetup), storage}};
}
-static RunConfiguration *runConfigurationForDisplayName(const QString &displayName)
+static void runAcceptor(RunConfiguration *config)
{
- const Project *project = SessionManager::instance()->startupProject();
- if (!project)
- return nullptr;
- const QList<RunConfiguration *> runconfigs = project->activeTarget()->runConfigurations();
- return Utils::findOrDefault(runconfigs, [displayName](RunConfiguration *rc) {
- return rc->displayName() == displayName;
- });
+ if (!BuildManager::isBuilding(config->project()))
+ ProjectExplorerPlugin::runRunConfiguration(config, Constants::NORMAL_RUN_MODE, true);
}
-RunRunConfigurationLocatorFilter::RunRunConfigurationLocatorFilter()
+RunConfigurationStartFilter::RunConfigurationStartFilter()
{
setId("Run run configuration");
- setDisplayName(Tr::tr("Run run configuration"));
- setDescription(Tr::tr("Run a run configuration of the current active project"));
+ setDisplayName(Tr::tr("Run Run Configuration"));
+ setDescription(Tr::tr("Runs a run configuration of the active project."));
setDefaultShortcutString("rr");
setPriority(Medium);
+ setupFilter(this);
}
-void RunRunConfigurationLocatorFilter::accept(const LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const
+LocatorMatcherTasks RunConfigurationStartFilter::matchers()
{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
+ return runConfigurationMatchers(&runAcceptor);
+}
- RunConfiguration *toStart = runConfigurationForDisplayName(selection.displayName);
- if (!toStart)
- return;
- if (!BuildManager::isBuilding(toStart->project()))
- ProjectExplorerPlugin::runRunConfiguration(toStart, Constants::NORMAL_RUN_MODE, true);
+static void switchAcceptor(RunConfiguration *config)
+{
+ ProjectManager::startupTarget()->setActiveRunConfiguration(config);
+ QTimer::singleShot(200, ICore::mainWindow(), [name = config->displayName()] {
+ if (auto ks = ICore::mainWindow()->findChild<QWidget *>("KitSelector.Button")) {
+ ToolTip::show(ks->mapToGlobal(QPoint{25, 25}),
+ Tr::tr("Switched run configuration to\n%1").arg(name),
+ ICore::dialogParent());
+ }
+ });
}
-SwitchToRunConfigurationLocatorFilter::SwitchToRunConfigurationLocatorFilter()
+RunConfigurationSwitchFilter::RunConfigurationSwitchFilter()
{
setId("Switch run configuration");
- setDisplayName(Tr::tr("Switch run configuration"));
- setDescription(Tr::tr("Switch active run configuration"));
+ setDisplayName(Tr::tr("Switch Run Configuration"));
+ setDescription(Tr::tr("Switches the active run configuration of the active project."));
setDefaultShortcutString("sr");
setPriority(Medium);
+ setupFilter(this);
}
-void SwitchToRunConfigurationLocatorFilter::accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart,
- int *selectionLength) const
+LocatorMatcherTasks RunConfigurationSwitchFilter::matchers()
{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
-
- RunConfiguration *toSwitchTo = runConfigurationForDisplayName(selection.displayName);
- if (!toSwitchTo)
- return;
-
- SessionManager::startupTarget()->setActiveRunConfiguration(toSwitchTo);
- QTimer::singleShot(200, this, [displayName = selection.displayName] {
- if (auto ks = ICore::mainWindow()->findChild<QWidget *>("KitSelector.Button")) {
- Utils::ToolTip::show(ks->mapToGlobal(QPoint{25, 25}),
- Tr::tr("Switched run configuration to\n%1").arg(displayName),
- ICore::dialogParent());
- }
- });
+ return runConfigurationMatchers(&switchAcceptor);
}
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/projectexplorer.h b/src/plugins/projectexplorer/projectexplorer.h
index baa8bb83a6..0b87f53310 100644
--- a/src/plugins/projectexplorer/projectexplorer.h
+++ b/src/plugins/projectexplorer/projectexplorer.h
@@ -155,7 +155,6 @@ public:
static QThreadPool *sharedThreadPool();
static Internal::MiniProjectTargetSelector *targetSelector();
- static void showSessionManager();
static void openNewProjectDialog();
static void openOpenProjectDialog();
@@ -261,6 +260,9 @@ private slots:
void testProject_projectTree();
void testProject_multipleBuildConfigs();
+ void testSourceToBinaryMapping();
+ void testSourceToBinaryMapping_data();
+
void testSessionSwitch();
#endif // WITH_TESTS
};
diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs
index e65cc1bdc3..c955a48590 100644
--- a/src/plugins/projectexplorer/projectexplorer.qbs
+++ b/src/plugins/projectexplorer/projectexplorer.qbs
@@ -45,6 +45,7 @@ Project {
"codestylesettingspropertiespage.cpp", "codestylesettingspropertiespage.h",
"compileoutputwindow.cpp", "compileoutputwindow.h",
"configtaskhandler.cpp", "configtaskhandler.h",
+ "copystep.cpp", "copystep.h",
"copytaskhandler.cpp", "copytaskhandler.h",
"currentprojectfilter.cpp", "currentprojectfilter.h",
"currentprojectfind.cpp", "currentprojectfind.h",
@@ -91,7 +92,6 @@ Project {
"ldparser.cpp", "ldparser.h",
"lldparser.cpp", "lldparser.h",
"linuxiccparser.cpp", "linuxiccparser.h",
- "localenvironmentaspect.cpp", "localenvironmentaspect.h",
"makestep.cpp", "makestep.h",
"miniprojecttargetselector.cpp", "miniprojecttargetselector.h",
"msvcparser.cpp", "msvcparser.h",
@@ -110,13 +110,12 @@ Project {
"projectexplorerconstants.cpp",
"projectexplorerconstants.h",
"projectexplorericons.h", "projectexplorericons.cpp",
- "projectexplorersettings.h",
- "projectexplorersettingspage.cpp", "projectexplorersettingspage.h",
+ "projectexplorersettings.h", "projectexplorersettings.cpp",
"projectexplorertr.h",
"projectfilewizardextension.cpp", "projectfilewizardextension.h",
"projectimporter.cpp", "projectimporter.h",
"projectmacro.cpp", "projectmacro.h",
- "projectmanager.h",
+ "projectmanager.cpp", "projectmanager.h",
"projectmodels.cpp", "projectmodels.h",
"projectnodes.cpp", "projectnodes.h",
"projectpanelfactory.cpp", "projectpanelfactory.h",
@@ -134,10 +133,6 @@ Project {
"runsettingspropertiespage.cpp", "runsettingspropertiespage.h",
"sanitizerparser.cpp", "sanitizerparser.h",
"selectablefilesmodel.cpp", "selectablefilesmodel.h",
- "session.cpp", "session.h",
- "sessionmodel.cpp", "sessionmodel.h",
- "sessionview.cpp", "sessionview.h",
- "sessiondialog.cpp", "sessiondialog.h",
"showineditortaskhandler.cpp", "showineditortaskhandler.h",
"showoutputtaskhandler.cpp", "showoutputtaskhandler.h",
"simpleprojectwizard.cpp", "simpleprojectwizard.h",
@@ -226,8 +221,7 @@ Project {
"idevicefactory.cpp", "idevicefactory.h",
"idevicefwd.h",
"idevicewidget.h",
- "localprocesslist.cpp", "localprocesslist.h",
- "sshdeviceprocesslist.cpp", "sshdeviceprocesslist.h",
+ "processlist.cpp", "processlist.h",
"sshparameters.cpp", "sshparameters.h",
"sshsettings.cpp", "sshsettings.h",
"sshsettingspage.cpp", "sshsettingspage.h",
@@ -250,9 +244,7 @@ Project {
]
}
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: ["outputparser_test.h", "outputparser_test.cpp"]
}
diff --git a/src/plugins/projectexplorer/projectexplorer.qrc b/src/plugins/projectexplorer/projectexplorer.qrc
index ececb0854b..0cc88e3331 100644
--- a/src/plugins/projectexplorer/projectexplorer.qrc
+++ b/src/plugins/projectexplorer/projectexplorer.qrc
@@ -86,5 +86,13 @@
<file>images/settingscategory_cpp@2x.png</file>
<file>images/importasproject.png</file>
<file>images/importasproject@2x.png</file>
+ <file>testdata/multi-target-project/CMakeLists.txt</file>
+ <file>testdata/multi-target-project/multi-target-project-app.pro</file>
+ <file>testdata/multi-target-project/multi-target-project-lib.cpp</file>
+ <file>testdata/multi-target-project/multi-target-project-lib.pro</file>
+ <file>testdata/multi-target-project/multi-target-project-main.cpp</file>
+ <file>testdata/multi-target-project/multi-target-project-shared.h</file>
+ <file>testdata/multi-target-project/multi-target-project.pro</file>
+ <file>testdata/multi-target-project/multi-target-project.qbs</file>
</qresource>
</RCC>
diff --git a/src/plugins/projectexplorer/projectexplorerconstants.h b/src/plugins/projectexplorer/projectexplorerconstants.h
index 9c86d6d9cf..75054120a8 100644
--- a/src/plugins/projectexplorer/projectexplorerconstants.h
+++ b/src/plugins/projectexplorer/projectexplorerconstants.h
@@ -132,6 +132,10 @@ const char BUILDSTEPS_CLEAN[] = "ProjectExplorer.BuildSteps.Clean";
const char BUILDSTEPS_BUILD[] = "ProjectExplorer.BuildSteps.Build";
const char BUILDSTEPS_DEPLOY[] = "ProjectExplorer.BuildSteps.Deploy";
+const char COPY_FILE_STEP[] = "ProjectExplorer.CopyFileStep";
+const char COPY_DIRECTORY_STEP[] = "ProjectExplorer.CopyDirectoryStep";
+const char DEVICE_CHECK_STEP[] = "ProjectExplorer.DeviceCheckBuildStep";
+
// Language
// Keep these short: These constants are exposed to the MacroExplorer!
@@ -202,8 +206,6 @@ const char FILEOVERLAY_UNKNOWN[]=":/projectexplorer/images/fileoverlay_unknown.p
// Settings
const char ADD_FILES_DIALOG_FILTER_HISTORY_KEY[] = "ProjectExplorer.AddFilesFilterKey";
const char PROJECT_ROOT_PATH_KEY[] = "ProjectExplorer.Project.RootPath";
-const char STARTUPSESSION_KEY[] = "ProjectExplorer/SessionToRestore";
-const char LASTSESSION_KEY[] = "ProjectExplorer/StartupSession";
const char SETTINGS_MENU_HIDE_BUILD[] = "Menu/HideBuild";
const char SETTINGS_MENU_HIDE_DEBUG[] = "Menu/HideDebug";
const char SETTINGS_MENU_HIDE_ANALYZE[] = "Menu/HideAnalyze";
diff --git a/src/plugins/projectexplorer/projectexplorersettingspage.cpp b/src/plugins/projectexplorer/projectexplorersettings.cpp
index 30eb850cd0..14f66b885b 100644
--- a/src/plugins/projectexplorer/projectexplorersettingspage.cpp
+++ b/src/plugins/projectexplorer/projectexplorersettings.cpp
@@ -1,7 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "projectexplorersettingspage.h"
+#include "projectexplorersettings.h"
#include "projectexplorer.h"
#include "projectexplorerconstants.h"
@@ -25,15 +25,14 @@
using namespace Core;
using namespace Utils;
-namespace ProjectExplorer {
-namespace Internal {
+namespace ProjectExplorer::Internal {
enum { UseCurrentDirectory, UseProjectDirectory };
-class ProjectExplorerSettingsWidget : public QWidget
+class ProjectExplorerSettingsWidget : public IOptionsPageWidget
{
public:
- explicit ProjectExplorerSettingsWidget(QWidget *parent = nullptr);
+ ProjectExplorerSettingsWidget();
ProjectExplorerSettings settings() const;
void setSettings(const ProjectExplorerSettings &s);
@@ -44,6 +43,13 @@ public:
bool useProjectsDirectory();
void setUseProjectsDirectory(bool v);
+ void apply() final
+ {
+ ProjectExplorerPlugin::setProjectExplorerSettings(settings());
+ DocumentManager::setProjectsDirectory(projectsDirectory());
+ DocumentManager::setUseProjectsDirectory(useProjectsDirectory());
+ }
+
private:
void slotDirectoryButtonGroupChanged();
@@ -68,8 +74,7 @@ private:
QButtonGroup *m_directoryButtonGroup;
};
-ProjectExplorerSettingsWidget::ProjectExplorerSettingsWidget(QWidget *parent) :
- QWidget(parent)
+ProjectExplorerSettingsWidget::ProjectExplorerSettingsWidget()
{
m_currentDirectoryRadioButton = new QRadioButton(Tr::tr("Current directory"));
m_directoryRadioButton = new QRadioButton(Tr::tr("Directory"));
@@ -117,7 +122,7 @@ ProjectExplorerSettingsWidget::ProjectExplorerSettingsWidget(QWidget *parent) :
"Disable it if you experience problems with your builds.");
jomLabel->setWordWrap(true);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
title(Tr::tr("Projects Directory")),
@@ -165,6 +170,10 @@ ProjectExplorerSettingsWidget::ProjectExplorerSettingsWidget(QWidget *parent) :
connect(m_directoryButtonGroup, &QButtonGroup::buttonClicked,
this, &ProjectExplorerSettingsWidget::slotDirectoryButtonGroupChanged);
+
+ setSettings(ProjectExplorerPlugin::projectExplorerSettings());
+ setProjectsDirectory(DocumentManager::projectsDirectory());
+ setUseProjectsDirectory(DocumentManager::useProjectsDirectory());
}
ProjectExplorerSettings ProjectExplorerSettingsWidget::settings() const
@@ -236,7 +245,8 @@ void ProjectExplorerSettingsWidget::slotDirectoryButtonGroupChanged()
m_projectsDirectoryPathChooser->setEnabled(enable);
}
-// ------------------ ProjectExplorerSettingsPage
+// ProjectExplorerSettingsPage
+
ProjectExplorerSettingsPage::ProjectExplorerSettingsPage()
{
setId(Constants::BUILD_AND_RUN_SETTINGS_PAGE_ID);
@@ -244,32 +254,7 @@ ProjectExplorerSettingsPage::ProjectExplorerSettingsPage()
setCategory(Constants::BUILD_AND_RUN_SETTINGS_CATEGORY);
setDisplayCategory(Tr::tr("Build & Run"));
setCategoryIconPath(":/projectexplorer/images/settingscategory_buildrun.png");
+ setWidgetCreator([] { return new ProjectExplorerSettingsWidget; });
}
-QWidget *ProjectExplorerSettingsPage::widget()
-{
- if (!m_widget) {
- m_widget = new ProjectExplorerSettingsWidget;
- m_widget->setSettings(ProjectExplorerPlugin::projectExplorerSettings());
- m_widget->setProjectsDirectory(DocumentManager::projectsDirectory());
- m_widget->setUseProjectsDirectory(DocumentManager::useProjectsDirectory());
- }
- return m_widget;
-}
-
-void ProjectExplorerSettingsPage::apply()
-{
- if (m_widget) {
- ProjectExplorerPlugin::setProjectExplorerSettings(m_widget->settings());
- DocumentManager::setProjectsDirectory(m_widget->projectsDirectory());
- DocumentManager::setUseProjectsDirectory(m_widget->useProjectsDirectory());
- }
-}
-
-void ProjectExplorerSettingsPage::finish()
-{
- delete m_widget;
-}
-
-} // namespace Internal
-} // namespace ProjectExplorer
+} // ProjectExplorer::Internal
diff --git a/src/plugins/projectexplorer/projectexplorersettings.h b/src/plugins/projectexplorer/projectexplorersettings.h
index b4b80124cd..48cc85c414 100644
--- a/src/plugins/projectexplorer/projectexplorersettings.h
+++ b/src/plugins/projectexplorer/projectexplorersettings.h
@@ -4,6 +4,8 @@
#pragma once
#include <coreplugin/coreconstants.h>
+#include <coreplugin/dialogs/ioptionspage.h>
+
#include <utils/hostosinfo.h>
#include <QUuid>
@@ -23,7 +25,6 @@ public:
&& p1.deployBeforeRun == p2.deployBeforeRun
&& p1.saveBeforeBuild == p2.saveBeforeBuild
&& p1.useJom == p2.useJom
- && p1.autorestoreLastSession == p2.autorestoreLastSession
&& p1.prompToStopRunControl == p2.prompToStopRunControl
&& p1.automaticallyCreateRunConfigurations == p2.automaticallyCreateRunConfigurations
&& p1.addLibraryPathsToRunEnv == p2.addLibraryPathsToRunEnv
@@ -40,7 +41,6 @@ public:
bool deployBeforeRun = true;
bool saveBeforeBuild = false;
bool useJom = true;
- bool autorestoreLastSession = false; // This option is set in the Session Manager!
bool prompToStopRunControl = false;
bool automaticallyCreateRunConfigurations = true;
bool addLibraryPathsToRunEnv = true;
@@ -74,12 +74,10 @@ public:
int maxCharCount = Core::Constants::DEFAULT_MAX_CHAR_COUNT;
};
-class CompileOutputSettings
+class ProjectExplorerSettingsPage : public Core::IOptionsPage
{
public:
- bool popUp = false;
- bool wrapOutput = false;
- int maxCharCount = Core::Constants::DEFAULT_MAX_CHAR_COUNT;
+ ProjectExplorerSettingsPage();
};
} // namespace Internal
diff --git a/src/plugins/projectexplorer/projectexplorersettingspage.h b/src/plugins/projectexplorer/projectexplorersettingspage.h
deleted file mode 100644
index 82764c3850..0000000000
--- a/src/plugins/projectexplorer/projectexplorersettingspage.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <coreplugin/dialogs/ioptionspage.h>
-
-#include <QPointer>
-
-namespace ProjectExplorer {
-namespace Internal {
-
-class ProjectExplorerSettingsWidget;
-
-class ProjectExplorerSettingsPage : public Core::IOptionsPage
-{
-public:
- ProjectExplorerSettingsPage();
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-private:
- QPointer<ProjectExplorerSettingsWidget> m_widget;
-};
-
-} // namespace Internal
-} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/projectfilewizardextension.cpp b/src/plugins/projectexplorer/projectfilewizardextension.cpp
index 53d3eb1578..134db6ea48 100644
--- a/src/plugins/projectexplorer/projectfilewizardextension.cpp
+++ b/src/plugins/projectexplorer/projectfilewizardextension.cpp
@@ -7,10 +7,10 @@
#include "project.h"
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "projectnodes.h"
#include "projecttree.h"
#include "projectwizardpage.h"
-#include "session.h"
#include <coreplugin/icore.h>
@@ -27,7 +27,6 @@
#include <utils/stringutils.h>
#include <QDebug>
-#include <QFileInfo>
#include <QMessageBox>
#include <QPointer>
#include <QTextCursor>
@@ -134,7 +133,7 @@ Node *ProjectFileWizardExtension::findWizardContextNode(Node *contextNode, Proje
const FilePath &path)
{
if (contextNode && !ProjectTree::hasNode(contextNode)) {
- if (SessionManager::projects().contains(project) && project->rootProjectNode()) {
+ if (ProjectManager::projects().contains(project) && project->rootProjectNode()) {
contextNode = project->rootProjectNode()->findNode([path](const Node *n) {
return path == n->filePath();
});
diff --git a/src/plugins/projectexplorer/projectmanager.cpp b/src/plugins/projectexplorer/projectmanager.cpp
new file mode 100644
index 0000000000..e3c8e65c59
--- /dev/null
+++ b/src/plugins/projectexplorer/projectmanager.cpp
@@ -0,0 +1,768 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "projectmanager.h"
+
+
+#include "buildconfiguration.h"
+#include "editorconfiguration.h"
+#include "project.h"
+#include "projectexplorer.h"
+#include "projectexplorerconstants.h"
+#include "projectexplorertr.h"
+#include "projectmanager.h"
+#include "projectnodes.h"
+#include "target.h"
+
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/foldernavigationwidget.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/idocument.h>
+#include <coreplugin/imode.h>
+#include <coreplugin/modemanager.h>
+#include <coreplugin/progressmanager/progressmanager.h>
+#include <coreplugin/session.h>
+
+#include <texteditor/texteditor.h>
+
+#include <utils/algorithm.h>
+#include <utils/filepath.h>
+#include <utils/qtcassert.h>
+#include <utils/stylehelper.h>
+#include <utils/qtcassert.h>
+
+#include <QDebug>
+#include <QMessageBox>
+#include <QPushButton>
+
+#ifdef WITH_TESTS
+#include <QTemporaryFile>
+#include <QTest>
+#include <vector>
+#endif
+
+using namespace Core;
+using namespace Utils;
+using namespace ProjectExplorer::Internal;
+
+namespace ProjectExplorer {
+
+class ProjectManagerPrivate
+{
+public:
+ void loadSession();
+ void saveSession();
+ void restoreDependencies();
+ void restoreStartupProject();
+ void restoreProjects(const FilePaths &fileList);
+ void askUserAboutFailedProjects();
+
+ bool recursiveDependencyCheck(const FilePath &newDep, const FilePath &checkDep) const;
+ FilePaths dependencies(const FilePath &proName) const;
+ FilePaths dependenciesOrder() const;
+ void dependencies(const FilePath &proName, FilePaths &result) const;
+
+ static QString windowTitleAddition(const FilePath &filePath);
+ static QString sessionTitle(const FilePath &filePath);
+
+ bool hasProjects() const { return !m_projects.isEmpty(); }
+
+ bool m_casadeSetActive = false;
+
+ Project *m_startupProject = nullptr;
+ QList<Project *> m_projects;
+ FilePaths m_failedProjects;
+ QMap<FilePath, FilePaths> m_depMap;
+
+private:
+ static QString locationInProject(const FilePath &filePath);
+};
+
+static ProjectManager *m_instance = nullptr;
+static ProjectManagerPrivate *d = nullptr;
+
+static QString projectFolderId(Project *pro)
+{
+ return pro->projectFilePath().toString();
+}
+
+const int PROJECT_SORT_VALUE = 100;
+
+ProjectManager::ProjectManager()
+{
+ m_instance = this;
+ d = new ProjectManagerPrivate;
+
+ connect(EditorManager::instance(), &EditorManager::editorCreated,
+ this, &ProjectManager::configureEditor);
+ connect(this, &ProjectManager::projectAdded,
+ EditorManager::instance(), &EditorManager::updateWindowTitles);
+ connect(this, &ProjectManager::projectRemoved,
+ EditorManager::instance(), &EditorManager::updateWindowTitles);
+ connect(this, &ProjectManager::projectDisplayNameChanged,
+ EditorManager::instance(), &EditorManager::updateWindowTitles);
+
+ EditorManager::setWindowTitleAdditionHandler(&ProjectManagerPrivate::windowTitleAddition);
+ EditorManager::setSessionTitleHandler(&ProjectManagerPrivate::sessionTitle);
+
+ connect(SessionManager::instance(), &SessionManager::aboutToLoadSession, this, [] {
+ d->loadSession();
+ });
+ connect(SessionManager::instance(), &SessionManager::aboutToSaveSession, this, [] {
+ d->saveSession();
+ });
+}
+
+ProjectManager::~ProjectManager()
+{
+ EditorManager::setWindowTitleAdditionHandler({});
+ EditorManager::setSessionTitleHandler({});
+ delete d;
+ d = nullptr;
+}
+
+ProjectManager *ProjectManager::instance()
+{
+ return m_instance;
+}
+
+bool ProjectManagerPrivate::recursiveDependencyCheck(const FilePath &newDep,
+ const FilePath &checkDep) const
+{
+ if (newDep == checkDep)
+ return false;
+
+ const FilePaths depList = m_depMap.value(checkDep);
+ for (const FilePath &dependency : depList) {
+ if (!recursiveDependencyCheck(newDep, dependency))
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * The dependency management exposes an interface based on projects, but
+ * is internally purely string based. This is suboptimal. Probably it would be
+ * nicer to map the filenames to projects on load and only map it back to
+ * filenames when saving.
+ */
+
+QList<Project *> ProjectManager::dependencies(const Project *project)
+{
+ const FilePath proName = project->projectFilePath();
+ const FilePaths proDeps = d->m_depMap.value(proName);
+
+ QList<Project *> projects;
+ for (const FilePath &dep : proDeps) {
+ Project *pro = Utils::findOrDefault(d->m_projects, [&dep](Project *p) {
+ return p->projectFilePath() == dep;
+ });
+ if (pro)
+ projects += pro;
+ }
+
+ return projects;
+}
+
+bool ProjectManager::hasDependency(const Project *project, const Project *depProject)
+{
+ const FilePath proName = project->projectFilePath();
+ const FilePath depName = depProject->projectFilePath();
+
+ const FilePaths proDeps = d->m_depMap.value(proName);
+ return proDeps.contains(depName);
+}
+
+bool ProjectManager::canAddDependency(const Project *project, const Project *depProject)
+{
+ const FilePath newDep = project->projectFilePath();
+ const FilePath checkDep = depProject->projectFilePath();
+
+ return d->recursiveDependencyCheck(newDep, checkDep);
+}
+
+bool ProjectManager::addDependency(Project *project, Project *depProject)
+{
+ const FilePath proName = project->projectFilePath();
+ const FilePath depName = depProject->projectFilePath();
+
+ // check if this dependency is valid
+ if (!d->recursiveDependencyCheck(proName, depName))
+ return false;
+
+ FilePaths proDeps = d->m_depMap.value(proName);
+ if (!proDeps.contains(depName)) {
+ proDeps.append(depName);
+ d->m_depMap[proName] = proDeps;
+ }
+ emit m_instance->dependencyChanged(project, depProject);
+
+ return true;
+}
+
+void ProjectManager::removeDependency(Project *project, Project *depProject)
+{
+ const FilePath proName = project->projectFilePath();
+ const FilePath depName = depProject->projectFilePath();
+
+ FilePaths proDeps = d->m_depMap.value(proName);
+ proDeps.removeAll(depName);
+ if (proDeps.isEmpty())
+ d->m_depMap.remove(proName);
+ else
+ d->m_depMap[proName] = proDeps;
+ emit m_instance->dependencyChanged(project, depProject);
+}
+
+bool ProjectManager::isProjectConfigurationCascading()
+{
+ return d->m_casadeSetActive;
+}
+
+void ProjectManager::setProjectConfigurationCascading(bool b)
+{
+ d->m_casadeSetActive = b;
+ SessionManager::markSessionFileDirty();
+}
+
+void ProjectManager::setStartupProject(Project *startupProject)
+{
+ QTC_ASSERT((!startupProject && d->m_projects.isEmpty())
+ || (startupProject && d->m_projects.contains(startupProject)), return);
+
+ if (d->m_startupProject == startupProject)
+ return;
+
+ d->m_startupProject = startupProject;
+ if (d->m_startupProject && d->m_startupProject->needsConfiguration()) {
+ ModeManager::activateMode(Constants::MODE_SESSION);
+ ModeManager::setFocusToCurrentMode();
+ }
+ FolderNavigationWidgetFactory::setFallbackSyncFilePath(
+ startupProject ? startupProject->projectFilePath().parentDir() : FilePath());
+ emit m_instance->startupProjectChanged(startupProject);
+}
+
+Project *ProjectManager::startupProject()
+{
+ return d->m_startupProject;
+}
+
+Target *ProjectManager::startupTarget()
+{
+ return d->m_startupProject ? d->m_startupProject->activeTarget() : nullptr;
+}
+
+BuildSystem *ProjectManager::startupBuildSystem()
+{
+ Target *t = startupTarget();
+ return t ? t->buildSystem() : nullptr;
+}
+
+/*!
+ * Returns the RunConfiguration of the currently active target
+ * of the startup project, if such exists, or \c nullptr otherwise.
+ */
+
+
+RunConfiguration *ProjectManager::startupRunConfiguration()
+{
+ Target *t = startupTarget();
+ return t ? t->activeRunConfiguration() : nullptr;
+}
+
+void ProjectManager::addProject(Project *pro)
+{
+ QTC_ASSERT(pro, return);
+ QTC_CHECK(!pro->displayName().isEmpty());
+ QTC_CHECK(pro->id().isValid());
+
+ SessionManager::markSessionFileDirty();
+ QTC_ASSERT(!d->m_projects.contains(pro), return);
+
+ d->m_projects.append(pro);
+
+ connect(pro, &Project::displayNameChanged,
+ m_instance, [pro]() { emit m_instance->projectDisplayNameChanged(pro); });
+
+ emit m_instance->projectAdded(pro);
+ const auto updateFolderNavigation = [pro] {
+ // destructing projects might trigger changes, so check if the project is actually there
+ if (QTC_GUARD(d->m_projects.contains(pro))) {
+ const QIcon icon = pro->rootProjectNode() ? pro->rootProjectNode()->icon() : QIcon();
+ FolderNavigationWidgetFactory::insertRootDirectory({projectFolderId(pro),
+ PROJECT_SORT_VALUE,
+ pro->displayName(),
+ pro->projectFilePath().parentDir(),
+ icon});
+ }
+ };
+ updateFolderNavigation();
+ configureEditors(pro);
+ connect(pro, &Project::fileListChanged, m_instance, [pro, updateFolderNavigation]() {
+ configureEditors(pro);
+ updateFolderNavigation(); // update icon
+ });
+ connect(pro, &Project::displayNameChanged, m_instance, updateFolderNavigation);
+
+ if (!startupProject())
+ setStartupProject(pro);
+}
+
+void ProjectManager::removeProject(Project *project)
+{
+ SessionManager::markSessionFileDirty();
+ QTC_ASSERT(project, return);
+ removeProjects({project});
+}
+
+void ProjectManagerPrivate::saveSession()
+{
+ // save the startup project
+ if (d->m_startupProject)
+ SessionManager::setSessionValue("StartupProject",
+ m_startupProject->projectFilePath().toSettings());
+
+ FilePaths projectFiles = Utils::transform(m_projects, &Project::projectFilePath);
+ // Restore information on projects that failed to load:
+ // don't read projects to the list, which the user loaded
+ for (const FilePath &failed : std::as_const(m_failedProjects)) {
+ if (!projectFiles.contains(failed))
+ projectFiles << failed;
+ }
+
+ SessionManager::setSessionValue("ProjectList",
+ Utils::transform<QStringList>(projectFiles,
+ &FilePath::toString));
+ SessionManager::setSessionValue("CascadeSetActive", m_casadeSetActive);
+
+ QVariantMap depMap;
+ auto i = m_depMap.constBegin();
+ while (i != m_depMap.constEnd()) {
+ QString key = i.key().toString();
+ QStringList values;
+ const FilePaths valueList = i.value();
+ for (const FilePath &value : valueList)
+ values << value.toString();
+ depMap.insert(key, values);
+ ++i;
+ }
+ SessionManager::setSessionValue(QLatin1String("ProjectDependencies"), QVariant(depMap));
+}
+
+/*!
+ Closes all projects
+ */
+void ProjectManager::closeAllProjects()
+{
+ removeProjects(projects());
+}
+
+const QList<Project *> ProjectManager::projects()
+{
+ return d->m_projects;
+}
+
+bool ProjectManager::hasProjects()
+{
+ return d->hasProjects();
+}
+
+bool ProjectManager::hasProject(Project *p)
+{
+ return d->m_projects.contains(p);
+}
+
+FilePaths ProjectManagerPrivate::dependencies(const FilePath &proName) const
+{
+ FilePaths result;
+ dependencies(proName, result);
+ return result;
+}
+
+void ProjectManagerPrivate::dependencies(const FilePath &proName, FilePaths &result) const
+{
+ const FilePaths depends = m_depMap.value(proName);
+
+ for (const FilePath &dep : depends)
+ dependencies(dep, result);
+
+ if (!result.contains(proName))
+ result.append(proName);
+}
+
+QString ProjectManagerPrivate::sessionTitle(const FilePath &filePath)
+{
+ const QString sessionName = SessionManager::activeSession();
+ if (SessionManager::isDefaultSession(sessionName)) {
+ if (filePath.isEmpty()) {
+ // use single project's name if there is only one loaded.
+ const QList<Project *> projects = ProjectManager::projects();
+ if (projects.size() == 1)
+ return projects.first()->displayName();
+ }
+ } else {
+ return sessionName.isEmpty() ? Tr::tr("Untitled") : sessionName;
+ }
+ return QString();
+}
+
+QString ProjectManagerPrivate::locationInProject(const FilePath &filePath)
+{
+ const Project *project = ProjectManager::projectForFile(filePath);
+ if (!project)
+ return QString();
+
+ const FilePath parentDir = filePath.parentDir();
+ if (parentDir == project->projectDirectory())
+ return "@ " + project->displayName();
+
+ if (filePath.isChildOf(project->projectDirectory())) {
+ const FilePath dirInProject = parentDir.relativeChildPath(project->projectDirectory());
+ return "(" + dirInProject.toUserOutput() + " @ " + project->displayName() + ")";
+ }
+
+ // For a file that is "outside" the project it belongs to, we display its
+ // dir's full path because it is easier to read than a series of "../../.".
+ // Example: /home/hugo/GenericProject/App.files lists /home/hugo/lib/Bar.cpp
+ return "(" + parentDir.toUserOutput() + " @ " + project->displayName() + ")";
+}
+
+QString ProjectManagerPrivate::windowTitleAddition(const FilePath &filePath)
+{
+ return filePath.isEmpty() ? QString() : locationInProject(filePath);
+}
+
+FilePaths ProjectManagerPrivate::dependenciesOrder() const
+{
+ QList<QPair<FilePath, FilePaths>> unordered;
+ FilePaths ordered;
+
+ // copy the map to a temporary list
+ for (const Project *pro : m_projects) {
+ const FilePath proName = pro->projectFilePath();
+ const FilePaths depList = filtered(m_depMap.value(proName),
+ [this](const FilePath &proPath) {
+ return contains(m_projects, [proPath](const Project *p) {
+ return p->projectFilePath() == proPath;
+ });
+ });
+ unordered.push_back({proName, depList});
+ }
+
+ while (!unordered.isEmpty()) {
+ for (int i = (unordered.count() - 1); i >= 0; --i) {
+ if (unordered.at(i).second.isEmpty()) {
+ ordered << unordered.at(i).first;
+ unordered.removeAt(i);
+ }
+ }
+
+ // remove the handled projects from the dependency lists
+ // of the remaining unordered projects
+ for (int i = 0; i < unordered.count(); ++i) {
+ for (const FilePath &pro : std::as_const(ordered)) {
+ FilePaths depList = unordered.at(i).second;
+ depList.removeAll(pro);
+ unordered[i].second = depList;
+ }
+ }
+ }
+
+ return ordered;
+}
+
+QList<Project *> ProjectManager::projectOrder(const Project *project)
+{
+ QList<Project *> result;
+
+ FilePaths pros;
+ if (project)
+ pros = d->dependencies(project->projectFilePath());
+ else
+ pros = d->dependenciesOrder();
+
+ for (const FilePath &proFile : std::as_const(pros)) {
+ for (Project *pro : projects()) {
+ if (pro->projectFilePath() == proFile) {
+ result << pro;
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+Project *ProjectManager::projectForFile(const FilePath &fileName)
+{
+ if (Project * const project = Utils::findOrDefault(ProjectManager::projects(),
+ [&fileName](const Project *p) { return p->isKnownFile(fileName); })) {
+ return project;
+ }
+ return Utils::findOrDefault(ProjectManager::projects(),
+ [&fileName](const Project *p) {
+ for (const Target * const target : p->targets()) {
+ for (const BuildConfiguration * const bc : target->buildConfigurations()) {
+ if (fileName.isChildOf(bc->buildDirectory()))
+ return false;
+ }
+ }
+ return fileName.isChildOf(p->projectDirectory());
+ });
+}
+
+Project *ProjectManager::projectWithProjectFilePath(const FilePath &filePath)
+{
+ return Utils::findOrDefault(ProjectManager::projects(),
+ [&filePath](const Project *p) { return p->projectFilePath() == filePath; });
+}
+
+void ProjectManager::configureEditor(IEditor *editor, const QString &fileName)
+{
+ if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor*>(editor)) {
+ Project *project = projectForFile(Utils::FilePath::fromString(fileName));
+ // Global settings are the default.
+ if (project)
+ project->editorConfiguration()->configureEditor(textEditor);
+ }
+}
+
+void ProjectManager::configureEditors(Project *project)
+{
+ const QList<IDocument *> documents = DocumentModel::openedDocuments();
+ for (IDocument *document : documents) {
+ if (project->isKnownFile(document->filePath())) {
+ const QList<IEditor *> editors = DocumentModel::editorsForDocument(document);
+ for (IEditor *editor : editors) {
+ if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor*>(editor)) {
+ project->editorConfiguration()->configureEditor(textEditor);
+ }
+ }
+ }
+ }
+}
+
+void ProjectManager::removeProjects(const QList<Project *> &remove)
+{
+ for (Project *pro : remove)
+ emit m_instance->aboutToRemoveProject(pro);
+
+ bool changeStartupProject = false;
+
+ // Delete projects
+ for (Project *pro : remove) {
+ pro->saveSettings();
+ pro->markAsShuttingDown();
+
+ // Remove the project node:
+ d->m_projects.removeOne(pro);
+
+ if (pro == d->m_startupProject)
+ changeStartupProject = true;
+
+ FolderNavigationWidgetFactory::removeRootDirectory(projectFolderId(pro));
+ disconnect(pro, nullptr, m_instance, nullptr);
+ emit m_instance->projectRemoved(pro);
+ }
+
+ if (changeStartupProject)
+ setStartupProject(hasProjects() ? projects().first() : nullptr);
+
+ qDeleteAll(remove);
+}
+
+void ProjectManagerPrivate::restoreDependencies()
+{
+ QMap<QString, QVariant> depMap = SessionManager::sessionValue("ProjectDependencies").toMap();
+ auto i = depMap.constBegin();
+ while (i != depMap.constEnd()) {
+ const QString &key = i.key();
+ FilePaths values;
+ const QStringList valueList = i.value().toStringList();
+ for (const QString &value : valueList)
+ values << FilePath::fromString(value);
+ m_depMap.insert(FilePath::fromString(key), values);
+ ++i;
+ }
+}
+
+void ProjectManagerPrivate::askUserAboutFailedProjects()
+{
+ FilePaths failedProjects = m_failedProjects;
+ if (!failedProjects.isEmpty()) {
+ QString fileList = FilePath::formatFilePaths(failedProjects, "<br>");
+ QMessageBox box(QMessageBox::Warning,
+ Tr::tr("Failed to restore project files"),
+ Tr::tr("Could not restore the following project files:<br><b>%1</b>").
+ arg(fileList));
+ auto keepButton = new QPushButton(Tr::tr("Keep projects in Session"), &box);
+ auto removeButton = new QPushButton(Tr::tr("Remove projects from Session"), &box);
+ box.addButton(keepButton, QMessageBox::AcceptRole);
+ box.addButton(removeButton, QMessageBox::DestructiveRole);
+
+ box.exec();
+
+ if (box.clickedButton() == removeButton)
+ m_failedProjects.clear();
+ }
+}
+
+void ProjectManagerPrivate::restoreStartupProject()
+{
+ const FilePath startupProject = FilePath::fromSettings(
+ SessionManager::sessionValue("StartupProject"));
+ if (!startupProject.isEmpty()) {
+ for (Project *pro : std::as_const(m_projects)) {
+ if (pro->projectFilePath() == startupProject) {
+ m_instance->setStartupProject(pro);
+ break;
+ }
+ }
+ }
+ if (!m_startupProject) {
+ if (!startupProject.isEmpty())
+ qWarning() << "Could not find startup project" << startupProject;
+ if (hasProjects())
+ m_instance->setStartupProject(m_projects.first());
+ }
+}
+
+/*!
+ Loads a session, takes a session name (not filename).
+*/
+void ProjectManagerPrivate::restoreProjects(const FilePaths &fileList)
+{
+ // indirectly adds projects to session
+ // Keep projects that failed to load in the session!
+ m_failedProjects = fileList;
+ if (!fileList.isEmpty()) {
+ ProjectExplorerPlugin::OpenProjectResult result = ProjectExplorerPlugin::openProjects(fileList);
+ if (!result)
+ ProjectExplorerPlugin::showOpenProjectError(result);
+ const QList<Project *> projects = result.projects();
+ for (const Project *p : projects)
+ m_failedProjects.removeAll(p->projectFilePath());
+ }
+}
+
+void ProjectManagerPrivate::loadSession()
+{
+ d->m_failedProjects.clear();
+ d->m_depMap.clear();
+ d->m_casadeSetActive = false;
+
+ // not ideal that this is in ProjectManager
+ Id modeId = Id::fromSetting(SessionManager::value(QLatin1String("ActiveMode")));
+ if (!modeId.isValid())
+ modeId = Id(Core::Constants::MODE_EDIT);
+
+ // find a list of projects to close later
+ const FilePaths fileList = FileUtils::toFilePathList(
+ SessionManager::sessionValue("ProjectList").toStringList());
+ const QList<Project *> projectsToRemove
+ = Utils::filtered(ProjectManager::projects(), [&fileList](Project *p) {
+ return !fileList.contains(p->projectFilePath());
+ });
+ const QList<Project *> openProjects = ProjectManager::projects();
+ const FilePaths projectPathsToLoad
+ = Utils::filtered(fileList, [&openProjects](const FilePath &path) {
+ return !Utils::contains(openProjects,
+ [&path](Project *p) { return p->projectFilePath() == path; });
+ });
+
+ SessionManager::addSessionLoadingSteps(projectPathsToLoad.count());
+
+ d->restoreProjects(projectPathsToLoad);
+ d->restoreDependencies();
+ d->restoreStartupProject();
+
+ // only remove old projects now that the startup project is set!
+ ProjectManager::removeProjects(projectsToRemove);
+
+ // Fall back to Project mode if the startup project is unconfigured and
+ // use the mode saved in the session otherwise
+ if (d->m_startupProject && d->m_startupProject->needsConfiguration())
+ modeId = Id(Constants::MODE_SESSION);
+
+ ModeManager::activateMode(modeId);
+ ModeManager::setFocusToCurrentMode();
+
+ d->m_casadeSetActive = SessionManager::sessionValue("CascadeSetActive", false).toBool();
+
+ // Starts a event loop, better do that at the very end
+ QMetaObject::invokeMethod(m_instance, [this] { askUserAboutFailedProjects(); });
+}
+
+FilePaths ProjectManager::projectsForSessionName(const QString &session)
+{
+ const FilePath fileName = SessionManager::sessionNameToFileName(session);
+ PersistentSettingsReader reader;
+ if (fileName.exists()) {
+ if (!reader.load(fileName)) {
+ qWarning() << "Could not restore session" << fileName.toUserOutput();
+ return {};
+ }
+ }
+ return transform(reader.restoreValue(QLatin1String("ProjectList")).toStringList(),
+ &FilePath::fromUserInput);
+}
+
+#ifdef WITH_TESTS
+
+void ProjectExplorerPlugin::testSessionSwitch()
+{
+ QVERIFY(SessionManager::createSession("session1"));
+ QVERIFY(SessionManager::createSession("session2"));
+ QTemporaryFile cppFile("main.cpp");
+ QVERIFY(cppFile.open());
+ cppFile.close();
+ QTemporaryFile projectFile1("XXXXXX.pro");
+ QTemporaryFile projectFile2("XXXXXX.pro");
+ struct SessionSpec {
+ SessionSpec(const QString &n, QTemporaryFile &f) : name(n), projectFile(f) {}
+ const QString name;
+ QTemporaryFile &projectFile;
+ };
+ std::vector<SessionSpec> sessionSpecs{SessionSpec("session1", projectFile1),
+ SessionSpec("session2", projectFile2)};
+ for (const SessionSpec &sessionSpec : sessionSpecs) {
+ static const QByteArray proFileContents
+ = "TEMPLATE = app\n"
+ "CONFIG -= qt\n"
+ "SOURCES = " + cppFile.fileName().toLocal8Bit();
+ QVERIFY(sessionSpec.projectFile.open());
+ sessionSpec.projectFile.write(proFileContents);
+ sessionSpec.projectFile.close();
+ QVERIFY(SessionManager::loadSession(sessionSpec.name));
+ const OpenProjectResult openResult
+ = ProjectExplorerPlugin::openProject(
+ FilePath::fromString(sessionSpec.projectFile.fileName()));
+ if (openResult.errorMessage().contains("text/plain"))
+ QSKIP("This test requires the presence of QmakeProjectManager to be fully functional");
+ QVERIFY(openResult);
+ QCOMPARE(openResult.projects().count(), 1);
+ QVERIFY(openResult.project());
+ QCOMPARE(ProjectManager::projects().count(), 1);
+ }
+ for (int i = 0; i < 30; ++i) {
+ QVERIFY(SessionManager::loadSession("session1"));
+ QCOMPARE(SessionManager::activeSession(), "session1");
+ QCOMPARE(ProjectManager::projects().count(), 1);
+ QVERIFY(SessionManager::loadSession("session2"));
+ QCOMPARE(SessionManager::activeSession(), "session2");
+ QCOMPARE(ProjectManager::projects().count(), 1);
+ }
+ QVERIFY(SessionManager::loadSession("session1"));
+ ProjectManager::closeAllProjects();
+ QVERIFY(SessionManager::loadSession("session2"));
+ ProjectManager::closeAllProjects();
+ QVERIFY(SessionManager::deleteSession("session1"));
+ QVERIFY(SessionManager::deleteSession("session2"));
+}
+
+#endif // WITH_TESTS
+
+} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/projectmanager.h b/src/plugins/projectexplorer/projectmanager.h
index 9d54eba572..f49cc96e54 100644
--- a/src/plugins/projectexplorer/projectmanager.h
+++ b/src/plugins/projectexplorer/projectmanager.h
@@ -6,20 +6,35 @@
#include "projectexplorer_export.h"
#include <QString>
+#include <QObject>
+
+namespace Core { class IEditor; }
#include <functional>
namespace Utils {
class FilePath;
+using FilePaths = QList<FilePath>;
class MimeType;
} // Utils
namespace ProjectExplorer {
+class BuildSystem;
class Project;
+class RunConfiguration;
+class Target;
-class PROJECTEXPLORER_EXPORT ProjectManager
+class PROJECTEXPLORER_EXPORT ProjectManager : public QObject
{
+ Q_OBJECT
+
+public:
+ ProjectManager();
+ ~ProjectManager() override;
+
+ static ProjectManager *instance();
+
public:
static bool canOpenProjectForMimeType(const Utils::MimeType &mt);
static Project *openProject(const Utils::MimeType &mt, const Utils::FilePath &fileName);
@@ -32,7 +47,59 @@ public:
});
}
+ static void closeAllProjects();
+
+ static void addProject(Project *project);
+ static void removeProject(Project *project);
+ static void removeProjects(const QList<Project *> &remove);
+
+ static void setStartupProject(Project *startupProject);
+
+ static QList<Project *> dependencies(const Project *project);
+ static bool hasDependency(const Project *project, const Project *depProject);
+ static bool canAddDependency(const Project *project, const Project *depProject);
+ static bool addDependency(Project *project, Project *depProject);
+ static void removeDependency(Project *project, Project *depProject);
+
+ static bool isProjectConfigurationCascading();
+ static void setProjectConfigurationCascading(bool b);
+
+ static Project *startupProject();
+ static Target *startupTarget();
+ static BuildSystem *startupBuildSystem();
+ static RunConfiguration *startupRunConfiguration();
+
+ static const QList<Project *> projects();
+ static bool hasProjects();
+ static bool hasProject(Project *p);
+
+ // NBS rewrite projectOrder (dependency management)
+ static QList<Project *> projectOrder(const Project *project = nullptr);
+
+ static Project *projectForFile(const Utils::FilePath &fileName);
+ static Project *projectWithProjectFilePath(const Utils::FilePath &filePath);
+
+ static Utils::FilePaths projectsForSessionName(const QString &session);
+
+signals:
+ void targetAdded(ProjectExplorer::Target *target);
+ void targetRemoved(ProjectExplorer::Target *target);
+ void projectAdded(ProjectExplorer::Project *project);
+ void aboutToRemoveProject(ProjectExplorer::Project *project);
+ void projectDisplayNameChanged(ProjectExplorer::Project *project);
+ void projectRemoved(ProjectExplorer::Project *project);
+
+ void startupProjectChanged(ProjectExplorer::Project *project);
+
+ void dependencyChanged(ProjectExplorer::Project *a, ProjectExplorer::Project *b);
+
+ // for tests only
+ void projectFinishedParsing(ProjectExplorer::Project *project);
+
private:
+ static void configureEditor(Core::IEditor *editor, const QString &fileName);
+ static void configureEditors(Project *project);
+
static void registerProjectCreator(const QString &mimeType,
const std::function<Project *(const Utils::FilePath &)> &);
};
diff --git a/src/plugins/projectexplorer/projectmodels.cpp b/src/plugins/projectexplorer/projectmodels.cpp
index 0a761f91e6..9805e654b7 100644
--- a/src/plugins/projectexplorer/projectmodels.cpp
+++ b/src/plugins/projectexplorer/projectmodels.cpp
@@ -8,8 +8,8 @@
#include "projectnodes.h"
#include "projectexplorer.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "projecttree.h"
-#include "session.h"
#include "target.h"
#include <app/app_version.h>
@@ -17,6 +17,7 @@
#include <coreplugin/documentmanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/iversioncontrol.h>
+#include <coreplugin/session.h>
#include <coreplugin/vcsmanager.h>
#include <utils/utilsicons.h>
@@ -24,8 +25,8 @@
#include <utils/dropsupport.h>
#include <utils/fsengine/fileiconprovider.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/theme/theme.h>
@@ -174,14 +175,15 @@ FlatModel::FlatModel(QObject *parent)
ProjectTree *tree = ProjectTree::instance();
connect(tree, &ProjectTree::subtreeChanged, this, &FlatModel::updateSubtree);
- SessionManager *sm = SessionManager::instance();
- connect(sm, &SessionManager::projectRemoved, this, &FlatModel::handleProjectRemoved);
- connect(sm, &SessionManager::aboutToLoadSession, this, &FlatModel::loadExpandData);
- connect(sm, &SessionManager::aboutToSaveSession, this, &FlatModel::saveExpandData);
- connect(sm, &SessionManager::projectAdded, this, &FlatModel::handleProjectAdded);
- connect(sm, &SessionManager::startupProjectChanged, this, [this] { emit layoutChanged(); });
+ ProjectManager *sm = ProjectManager::instance();
+ SessionManager *sb = SessionManager::instance();
+ connect(sm, &ProjectManager::projectRemoved, this, &FlatModel::handleProjectRemoved);
+ connect(sb, &SessionManager::aboutToLoadSession, this, &FlatModel::loadExpandData);
+ connect(sb, &SessionManager::aboutToSaveSession, this, &FlatModel::saveExpandData);
+ connect(sm, &ProjectManager::projectAdded, this, &FlatModel::handleProjectAdded);
+ connect(sm, &ProjectManager::startupProjectChanged, this, [this] { emit layoutChanged(); });
- for (Project *project : SessionManager::projects())
+ for (Project *project : ProjectManager::projects())
handleProjectAdded(project);
}
@@ -234,7 +236,7 @@ QVariant FlatModel::data(const QModelIndex &index, int role) const
}
case Qt::FontRole: {
QFont font;
- if (project == SessionManager::startupProject())
+ if (project == ProjectManager::startupProject())
font.setBold(true);
return font;
}
@@ -407,7 +409,7 @@ void FlatModel::updateSubtree(FolderNode *node)
void FlatModel::rebuildModel()
{
- const QList<Project *> projects = SessionManager::projects();
+ const QList<Project *> projects = ProjectManager::projects();
for (Project *project : projects)
addOrRebuildProjectModel(project);
}
diff --git a/src/plugins/projectexplorer/projectnodes.cpp b/src/plugins/projectexplorer/projectnodes.cpp
index 10699b7d90..e5c8a9a3c6 100644
--- a/src/plugins/projectexplorer/projectnodes.cpp
+++ b/src/plugins/projectexplorer/projectnodes.cpp
@@ -326,14 +326,13 @@ FilePath Node::pathOrDirectory(bool dir) const
FilePath location;
// Virtual Folder case
// If there are files directly below or no subfolders, take the folder path
- if (!folder->fileNodes().isEmpty() || folder->folderNodes().isEmpty()) {
+ auto Any = [](auto) { return true; };
+ if (folder->findChildFileNode(Any) || !folder->findChildFolderNode(Any)) {
location = m_filePath;
} else {
// Otherwise we figure out a commonPath from the subfolders
FilePaths list;
- const QList<FolderNode *> folders = folder->folderNodes();
- for (FolderNode *f : folders)
- list << f->filePath();
+ folder->forEachFolderNode([&](FolderNode *f) { list << f->filePath(); });
location = FileUtils::commonPath(list);
}
@@ -547,6 +546,22 @@ void FolderNode::forEachProjectNode(const std::function<void(const ProjectNode *
}
}
+void FolderNode::forEachFileNode(const std::function<void (FileNode *)> &fileTask) const
+{
+ for (const std::unique_ptr<Node> &n : m_nodes) {
+ if (FileNode *fn = n->asFileNode())
+ fileTask(fn);
+ }
+}
+
+void FolderNode::forEachFolderNode(const std::function<void (FolderNode *)> &folderTask) const
+{
+ for (const std::unique_ptr<Node> &n : m_nodes) {
+ if (FolderNode *fn = n->asFolderNode())
+ folderTask(fn);
+ }
+}
+
ProjectNode *FolderNode::findProjectNode(const std::function<bool(const ProjectNode *)> &predicate)
{
if (ProjectNode *projectNode = asProjectNode()) {
@@ -563,19 +578,29 @@ ProjectNode *FolderNode::findProjectNode(const std::function<bool(const ProjectN
return nullptr;
}
-const QList<Node *> FolderNode::nodes() const
+FolderNode *FolderNode::findChildFolderNode(const std::function<bool(FolderNode *)> &predicate) const
{
- return Utils::toRawPointer<QList>(m_nodes);
+ for (const std::unique_ptr<Node> &n : m_nodes) {
+ if (FolderNode *fn = n->asFolderNode())
+ if (predicate(fn))
+ return fn;
+ }
+ return nullptr;
}
-QList<FileNode *> FolderNode::fileNodes() const
+FileNode *FolderNode::findChildFileNode(const std::function<bool(FileNode *)> &predicate) const
{
- QList<FileNode *> result;
for (const std::unique_ptr<Node> &n : m_nodes) {
if (FileNode *fn = n->asFileNode())
- result.append(fn);
+ if (predicate(fn))
+ return fn;
}
- return result;
+ return nullptr;
+}
+
+const QList<Node *> FolderNode::nodes() const
+{
+ return Utils::toRawPointer<QList>(m_nodes);
}
FileNode *FolderNode::fileNode(const Utils::FilePath &file) const
@@ -587,16 +612,6 @@ FileNode *FolderNode::fileNode(const Utils::FilePath &file) const
}));
}
-QList<FolderNode *> FolderNode::folderNodes() const
-{
- QList<FolderNode *> result;
- for (const std::unique_ptr<Node> &n : m_nodes) {
- if (FolderNode *fn = n->asFolderNode())
- result.append(fn);
- }
- return result;
-}
-
FolderNode *FolderNode::folderNode(const Utils::FilePath &directory) const
{
Node *node = Utils::findOrDefault(m_nodes, [directory](const std::unique_ptr<Node> &n) {
@@ -669,8 +684,7 @@ void FolderNode::compress()
compress();
} else {
- for (FolderNode *fn : folderNodes())
- fn->compress();
+ forEachFolderNode([&](FolderNode *fn) { fn->compress(); });
}
}
diff --git a/src/plugins/projectexplorer/projectnodes.h b/src/plugins/projectexplorer/projectnodes.h
index 35927c02c8..0e0068ba7b 100644
--- a/src/plugins/projectexplorer/projectnodes.h
+++ b/src/plugins/projectexplorer/projectnodes.h
@@ -32,6 +32,8 @@ enum class FileType : quint16 {
Resource,
QML,
Project,
+ App,
+ Lib,
FileTypeSize
};
@@ -228,11 +230,13 @@ public:
const std::function<bool(const FolderNode *)> &folderFilterTask = {}) const;
void forEachGenericNode(const std::function<void(Node *)> &genericTask) const;
void forEachProjectNode(const std::function<void(const ProjectNode *)> &genericTask) const;
- ProjectNode *findProjectNode(const std::function<bool(const ProjectNode *)> &predicate);
+ void forEachFileNode(const std::function<void(FileNode *)> &fileTask) const;
+ void forEachFolderNode(const std::function<void(FolderNode *)> &folderTask) const;
+ ProjectNode *findProjectNode(const std::function<bool(const ProjectNode *)> &predicate); // recursive
+ FolderNode *findChildFolderNode(const std::function<bool (FolderNode *)> &predicate) const; // non-recursive
+ FileNode *findChildFileNode(const std::function<bool (FileNode *)> &predicate) const; // non-recursive
const QList<Node *> nodes() const;
- QList<FileNode *> fileNodes() const;
FileNode *fileNode(const Utils::FilePath &file) const;
- QList<FolderNode *> folderNodes() const;
FolderNode *folderNode(const Utils::FilePath &directory) const;
using FolderNodeFactory = std::function<std::unique_ptr<FolderNode>(const Utils::FilePath &)>;
diff --git a/src/plugins/projectexplorer/projectnodeshelper.h b/src/plugins/projectexplorer/projectnodeshelper.h
index 727497c6bf..bcb5454fd8 100644
--- a/src/plugins/projectexplorer/projectnodeshelper.h
+++ b/src/plugins/projectexplorer/projectnodeshelper.h
@@ -11,18 +11,19 @@
#include <utils/algorithm.h>
#include <utils/filepath.h>
+#include <QPromise>
+
namespace ProjectExplorer {
template<typename Result>
-QList<FileNode *> scanForFiles(QFutureInterface<Result> &future,
- const Utils::FilePath &directory,
+QList<FileNode *> scanForFiles(QPromise<Result> &promise, const Utils::FilePath &directory,
const std::function<FileNode *(const Utils::FilePath &)> factory);
namespace Internal {
template<typename Result>
QList<FileNode *> scanForFilesRecursively(
- QFutureInterface<Result> &future,
+ QPromise<Result> &promise,
double progressStart,
double progressRange,
const Utils::FilePath &directory,
@@ -46,7 +47,7 @@ QList<FileNode *> scanForFilesRecursively(
const double progressIncrement = progressRange / static_cast<double>(entries.count());
int lastIntProgress = 0;
for (const QFileInfo &entry : entries) {
- if (future.isCanceled())
+ if (promise.isCanceled())
return result;
const Utils::FilePath entryName = Utils::FilePath::fromString(entry.absoluteFilePath());
@@ -54,7 +55,7 @@ QList<FileNode *> scanForFilesRecursively(
return vc->isVcsFileOrDirectory(entryName);
})) {
if (entry.isDir())
- result.append(scanForFilesRecursively(future,
+ result.append(scanForFilesRecursively(promise,
progress,
progressIncrement,
entryName,
@@ -66,26 +67,25 @@ QList<FileNode *> scanForFilesRecursively(
}
progress += progressIncrement;
const int intProgress = std::min(static_cast<int>(progressStart + progress),
- future.progressMaximum());
+ promise.future().progressMaximum());
if (lastIntProgress < intProgress) {
- future.setProgressValue(intProgress);
+ promise.setProgressValue(intProgress);
lastIntProgress = intProgress;
}
}
- future.setProgressValue(
- std::min(static_cast<int>(progressStart + progressRange), future.progressMaximum()));
+ promise.setProgressValue(std::min(static_cast<int>(progressStart + progressRange),
+ promise.future().progressMaximum()));
return result;
}
} // namespace Internal
template<typename Result>
-QList<FileNode *> scanForFiles(QFutureInterface<Result> &future,
- const Utils::FilePath &directory,
+QList<FileNode *> scanForFiles(QPromise<Result> &promise, const Utils::FilePath &directory,
const std::function<FileNode *(const Utils::FilePath &)> factory)
{
QSet<QString> visited;
- future.setProgressRange(0, 1000000);
- return Internal::scanForFilesRecursively(future,
+ promise.setProgressRange(0, 1000000);
+ return Internal::scanForFilesRecursively(promise,
0.0,
1000000.0,
directory,
diff --git a/src/plugins/projectexplorer/projecttree.cpp b/src/plugins/projectexplorer/projecttree.cpp
index 18b03d3ae0..5f81f473fe 100644
--- a/src/plugins/projectexplorer/projecttree.cpp
+++ b/src/plugins/projectexplorer/projecttree.cpp
@@ -6,9 +6,9 @@
#include "project.h"
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "projectnodes.h"
#include "projecttreewidget.h"
-#include "session.h"
#include "target.h"
#include <coreplugin/actionmanager/actioncontainer.h>
@@ -53,11 +53,11 @@ ProjectTree::ProjectTree(QObject *parent) : QObject(parent)
connect(qApp, &QApplication::focusChanged,
this, &ProjectTree::update);
- connect(SessionManager::instance(), &SessionManager::projectAdded,
+ connect(ProjectManager::instance(), &ProjectManager::projectAdded,
this, &ProjectTree::sessionAndTreeChanged);
- connect(SessionManager::instance(), &SessionManager::projectRemoved,
+ connect(ProjectManager::instance(), &ProjectManager::projectRemoved,
this, &ProjectTree::sessionAndTreeChanged);
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, &ProjectTree::sessionChanged);
connect(this, &ProjectTree::subtreeChanged, this, &ProjectTree::treeChanged);
}
@@ -170,7 +170,7 @@ void ProjectTree::updateFromNode(Node *node)
if (node)
project = projectForNode(node);
else
- project = SessionManager::startupProject();
+ project = ProjectManager::startupProject();
setCurrent(node, project);
for (ProjectTreeWidget *widget : std::as_const(m_projectTreeWidgets))
@@ -224,7 +224,7 @@ void ProjectTree::sessionChanged()
{
if (m_currentProject) {
Core::DocumentManager::setDefaultLocationForNewFiles(m_currentProject->projectDirectory());
- } else if (Project *project = SessionManager::startupProject()) {
+ } else if (Project *project = ProjectManager::startupProject()) {
Core::DocumentManager::setDefaultLocationForNewFiles(project->projectDirectory());
updateFromNode(nullptr); // Make startup project current if there is no other current
} else {
@@ -300,7 +300,7 @@ void ProjectTree::updateFileWarning(Core::IDocument *document, const QString &te
if (!infoBar->canInfoBeAdded(infoId))
return;
const FilePath filePath = document->filePath();
- const QList<Project *> projects = SessionManager::projects();
+ const QList<Project *> projects = ProjectManager::projects();
if (projects.isEmpty())
return;
for (Project *project : projects) {
@@ -394,7 +394,7 @@ void ProjectTree::applyTreeManager(FolderNode *folder, ConstructionPhase phase)
bool ProjectTree::hasNode(const Node *node)
{
- return Utils::contains(SessionManager::projects(), [node](const Project *p) {
+ return Utils::contains(ProjectManager::projects(), [node](const Project *p) {
if (!p)
return false;
if (p->containerNode() == node)
@@ -409,7 +409,7 @@ bool ProjectTree::hasNode(const Node *node)
void ProjectTree::forEachNode(const std::function<void(Node *)> &task)
{
- const QList<Project *> projects = SessionManager::projects();
+ const QList<Project *> projects = ProjectManager::projects();
for (Project *project : projects) {
if (ProjectNode *projectNode = project->rootProjectNode()) {
task(projectNode);
@@ -430,7 +430,7 @@ Project *ProjectTree::projectForNode(const Node *node)
while (folder && folder->parentFolderNode())
folder = folder->parentFolderNode();
- return Utils::findOrDefault(SessionManager::projects(), [folder](const Project *pro) {
+ return Utils::findOrDefault(ProjectManager::projects(), [folder](const Project *pro) {
return pro->containerNode() == folder;
});
}
@@ -438,7 +438,7 @@ Project *ProjectTree::projectForNode(const Node *node)
Node *ProjectTree::nodeForFile(const FilePath &fileName)
{
Node *node = nullptr;
- for (const Project *project : SessionManager::projects()) {
+ for (const Project *project : ProjectManager::projects()) {
project->nodeForFilePath(fileName, [&](const Node *n) {
if (!node || (!node->asFileNode() && n->asFileNode()))
node = const_cast<Node *>(n);
diff --git a/src/plugins/projectexplorer/projecttreewidget.cpp b/src/plugins/projectexplorer/projecttreewidget.cpp
index 65f38626fe..a3d608ebd8 100644
--- a/src/plugins/projectexplorer/projecttreewidget.cpp
+++ b/src/plugins/projectexplorer/projecttreewidget.cpp
@@ -6,10 +6,10 @@
#include "project.h"
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "projectmodels.h"
#include "projectnodes.h"
#include "projecttree.h"
-#include "session.h"
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
@@ -24,6 +24,7 @@
#include <utils/navigationtreeview.h>
#include <utils/progressindicator.h>
#include <utils/qtcassert.h>
+#include <utils/stylehelper.h>
#include <utils/tooltip/tooltip.h>
#include <utils/utilsicons.h>
@@ -343,7 +344,7 @@ Node *ProjectTreeWidget::nodeForFile(const FilePath &fileName)
int bestNodeExpandCount = INT_MAX;
// FIXME: Looks like this could be done with less cycles.
- for (Project *project : SessionManager::projects()) {
+ for (Project *project : ProjectManager::projects()) {
if (ProjectNode *projectNode = project->rootProjectNode()) {
projectNode->forEachGenericNode([&](Node *node) {
if (node->filePath() == fileName) {
@@ -420,7 +421,7 @@ QList<QToolButton *> ProjectTreeWidget::createToolButtons()
filter->setIcon(Icons::FILTER.icon());
filter->setToolTip(Tr::tr("Filter Tree"));
filter->setPopupMode(QToolButton::InstantPopup);
- filter->setProperty("noArrow", true);
+ filter->setProperty(StyleHelper::C_NO_ARROW, true);
auto filterMenu = new QMenu(filter);
filterMenu->addAction(m_filterProjectsAction);
diff --git a/src/plugins/projectexplorer/projectwelcomepage.cpp b/src/plugins/projectexplorer/projectwelcomepage.cpp
index 156212a504..4eef637077 100644
--- a/src/plugins/projectexplorer/projectwelcomepage.cpp
+++ b/src/plugins/projectexplorer/projectwelcomepage.cpp
@@ -3,10 +3,9 @@
#include "projectwelcomepage.h"
-#include "session.h"
-#include "sessionmodel.h"
#include "projectexplorer.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
@@ -15,6 +14,8 @@
#include <coreplugin/icontext.h>
#include <coreplugin/icore.h>
#include <coreplugin/iwizardfactory.h>
+#include <coreplugin/session.h>
+#include <coreplugin/sessionmodel.h>
#include <coreplugin/welcomepagehelper.h>
#include <utils/algorithm.h>
@@ -324,7 +325,7 @@ public:
if (expanded) {
painter->setPen(textColor);
painter->setFont(sizedFont(12, option.widget));
- const FilePaths projects = SessionManager::projectsForSessionName(sessionName);
+ const FilePaths projects = ProjectManager::projectsForSessionName(sessionName);
int yy = firstBase + SESSION_LINE_HEIGHT - 3;
QFontMetrics fm(option.widget->font());
for (const FilePath &projectPath : projects) {
@@ -378,7 +379,7 @@ public:
int h = SESSION_LINE_HEIGHT;
QString sessionName = idx.data(Qt::DisplayRole).toString();
if (m_expandedSessions.contains(sessionName)) {
- const FilePaths projects = SessionManager::projectsForSessionName(sessionName);
+ const FilePaths projects = ProjectManager::projectsForSessionName(sessionName);
h += projects.size() * 40 + LINK_HEIGHT - 6;
}
return QSize(380, h + ItemGap);
@@ -579,7 +580,7 @@ public:
auto manageSessionsButton = new WelcomePageButton(this);
manageSessionsButton->setText(Tr::tr("Manage..."));
manageSessionsButton->setWithAccentColor(true);
- manageSessionsButton->setOnClicked([] { ProjectExplorerPlugin::showSessionManager(); });
+ manageSessionsButton->setOnClicked([] { SessionManager::showSessionManager(); });
auto sessionsLabel = new QLabel(this);
sessionsLabel->setFont(brandFont());
diff --git a/src/plugins/projectexplorer/projectwelcomepage.h b/src/plugins/projectexplorer/projectwelcomepage.h
index a9c8145963..7fb2a828db 100644
--- a/src/plugins/projectexplorer/projectwelcomepage.h
+++ b/src/plugins/projectexplorer/projectwelcomepage.h
@@ -12,10 +12,11 @@
#include <QAbstractListModel>
#include <QCoreApplication>
+namespace Core { class SessionModel; }
+
namespace ProjectExplorer {
namespace Internal {
-class SessionModel;
class SessionsPage;
class ProjectModel : public QAbstractListModel
@@ -56,7 +57,6 @@ public slots:
signals:
void requestProject(const Utils::FilePath &project);
- void manageSessions();
private:
void openSessionAt(int index);
@@ -64,7 +64,7 @@ private:
void createActions();
friend class SessionsPage;
- SessionModel *m_sessionModel = nullptr;
+ Core::SessionModel *m_sessionModel = nullptr;
ProjectModel *m_projectModel = nullptr;
};
diff --git a/src/plugins/projectexplorer/projectwindow.cpp b/src/plugins/projectexplorer/projectwindow.cpp
index 8fae44b297..a69ee4c855 100644
--- a/src/plugins/projectexplorer/projectwindow.cpp
+++ b/src/plugins/projectexplorer/projectwindow.cpp
@@ -12,9 +12,9 @@
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
#include "projectimporter.h"
+#include "projectmanager.h"
#include "projectpanelfactory.h"
#include "projectsettingswidget.h"
-#include "session.h"
#include "target.h"
#include "targetsettingspanel.h"
@@ -352,7 +352,7 @@ public:
case Qt::FontRole: {
QFont font;
- font.setBold(m_project == SessionManager::startupProject());
+ font.setBold(m_project == ProjectManager::startupProject());
return font;
}
@@ -392,7 +392,7 @@ public:
if (role == ItemActivatedDirectlyRole) {
// Someone selected the project using the combobox or similar.
- SessionManager::setStartupProject(m_project);
+ ProjectManager::setStartupProject(m_project);
m_currentChildIndex = 0; // Use some Target page by defaults
m_targetsItem->setData(column, dat, ItemActivatedFromAboveRole); // And propagate downwards.
announceChange();
@@ -546,18 +546,18 @@ public:
m_projectSelection->showPopup();
});
- SessionManager *sessionManager = SessionManager::instance();
- connect(sessionManager, &SessionManager::projectAdded,
+ ProjectManager *sessionManager = ProjectManager::instance();
+ connect(sessionManager, &ProjectManager::projectAdded,
this, &ProjectWindowPrivate::registerProject);
- connect(sessionManager, &SessionManager::aboutToRemoveProject,
+ connect(sessionManager, &ProjectManager::aboutToRemoveProject,
this, &ProjectWindowPrivate::deregisterProject);
- connect(sessionManager, &SessionManager::startupProjectChanged,
+ connect(sessionManager, &ProjectManager::startupProjectChanged,
this, &ProjectWindowPrivate::startupProjectChanged);
m_importBuild = new QPushButton(Tr::tr("Import Existing Build..."));
connect(m_importBuild, &QPushButton::clicked,
this, &ProjectWindowPrivate::handleImportBuild);
- connect(sessionManager, &SessionManager::startupProjectChanged, this, [this](Project *project) {
+ connect(sessionManager, &ProjectManager::startupProjectChanged, this, [this](Project *project) {
m_importBuild->setEnabled(project && project->projectImporter());
});
@@ -572,9 +572,6 @@ public:
selectorView->setObjectName("ProjectSelector"); // Needed for dock widget state saving
selectorView->setWindowTitle(Tr::tr("Project Selector"));
selectorView->setAutoFillBackground(true);
- selectorView->setContextMenuPolicy(Qt::CustomContextMenu);
- connect(selectorView, &QWidget::customContextMenuRequested,
- this, &ProjectWindowPrivate::openContextMenu);
auto activeLabel = new QLabel(Tr::tr("Active Project"));
QFont font = activeLabel->font();
@@ -677,7 +674,7 @@ public:
void projectSelected(int index)
{
Project *project = m_comboBoxModel.rootItem()->childAt(index)->m_projectItem->project();
- SessionManager::setStartupProject(project);
+ ProjectManager::setStartupProject(project);
}
ComboBoxItem *itemForProject(Project *project) const
@@ -746,8 +743,7 @@ public:
void handleManageKits()
{
if (ProjectItem *projectItem = m_projectsModel.rootItem()->childAt(0)) {
- if (auto kitPage = KitOptionsPage::instance())
- kitPage->showKit(KitManager::kit(Id::fromSetting(projectItem->data(0, KitIdRole))));
+ KitOptionsPage::showKit(KitManager::kit(Id::fromSetting(projectItem->data(0, KitIdRole))));
}
ICore::showOptionsDialog(Constants::KITS_SETTINGS_PAGE_ID);
}
@@ -780,8 +776,8 @@ public:
}
}
if (lastTarget && lastBc) {
- SessionManager::setActiveBuildConfiguration(lastTarget, lastBc, SetActive::Cascade);
- SessionManager::setActiveTarget(project, lastTarget, SetActive::Cascade);
+ lastTarget->setActiveBuildConfiguration(lastBc, SetActive::Cascade);
+ project->setActiveTarget(lastTarget, SetActive::Cascade);
}
}
diff --git a/src/plugins/projectexplorer/projectwizardpage.cpp b/src/plugins/projectexplorer/projectwizardpage.cpp
index 34dc0b9aaa..83fd4d2cba 100644
--- a/src/plugins/projectexplorer/projectwizardpage.cpp
+++ b/src/plugins/projectexplorer/projectwizardpage.cpp
@@ -5,8 +5,8 @@
#include "project.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "projectmodels.h"
-#include "session.h"
#include <coreplugin/icore.h>
#include <coreplugin/iversioncontrol.h>
@@ -245,12 +245,10 @@ static AddNewTree *buildAddFilesTree(FolderNode *root, const FilePaths &files,
Node *contextNode, BestNodeSelector *selector)
{
QList<AddNewTree *> children;
- const QList<FolderNode *> folderNodes = root->folderNodes();
- for (FolderNode *fn : folderNodes) {
- AddNewTree *child = buildAddFilesTree(fn, files, contextNode, selector);
- if (child)
+ root->forEachFolderNode([&](FolderNode *fn) {
+ if (AddNewTree *child = buildAddFilesTree(fn, files, contextNode, selector))
children.append(child);
- }
+ });
if (root->supportsAction(AddNewFile, root) && !root->supportsAction(InheritedFromParent, root)) {
FolderNode::AddNewInformation info = root->addNewInformation(files, contextNode);
@@ -290,7 +288,7 @@ ProjectWizardPage::ProjectWizardPage(QWidget *parent)
scrollArea->setWidgetResizable(true);
scrollArea->setWidget(m_filesLabel);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Form {
m_projectLabel, m_projectComboBox, br,
@@ -463,7 +461,7 @@ void ProjectWizardPage::initializeProjectTree(Node *context, const FilePaths &pa
TreeItem *root = m_model.rootItem();
root->removeChildren();
- for (Project *project : SessionManager::projects()) {
+ for (Project *project : ProjectManager::projects()) {
if (ProjectNode *pn = project->rootProjectNode()) {
if (kind == IWizardFactory::ProjectWizard) {
if (AddNewTree *child = buildAddProjectTree(pn, paths.first(), context, &selector))
diff --git a/src/plugins/projectexplorer/rawprojectpart.cpp b/src/plugins/projectexplorer/rawprojectpart.cpp
index 6abbedde93..31147c3803 100644
--- a/src/plugins/projectexplorer/rawprojectpart.cpp
+++ b/src/plugins/projectexplorer/rawprojectpart.cpp
@@ -11,13 +11,14 @@
#include "target.h"
#include <ios/iosconstants.h>
+
#include <utils/algorithm.h>
namespace ProjectExplorer {
RawProjectPartFlags::RawProjectPartFlags(const ToolChain *toolChain,
const QStringList &commandLineFlags,
- const QString &includeFileBaseDir)
+ const Utils::FilePath &includeFileBaseDir)
{
// Keep the following cheap/non-blocking for the ui thread. Expensive
// operations are encapsulated in ToolChainInfo as "runners".
@@ -25,7 +26,8 @@ RawProjectPartFlags::RawProjectPartFlags(const ToolChain *toolChain,
if (toolChain) {
warningFlags = toolChain->warningFlags(commandLineFlags);
languageExtensions = toolChain->languageExtensions(commandLineFlags);
- includedFiles = toolChain->includedFiles(commandLineFlags, includeFileBaseDir);
+ includedFiles = Utils::transform(toolChain->includedFiles(commandLineFlags, includeFileBaseDir),
+ &Utils::FilePath::toFSPathString);
}
}
diff --git a/src/plugins/projectexplorer/rawprojectpart.h b/src/plugins/projectexplorer/rawprojectpart.h
index 580c83d439..ca210ed43e 100644
--- a/src/plugins/projectexplorer/rawprojectpart.h
+++ b/src/plugins/projectexplorer/rawprojectpart.h
@@ -37,7 +37,7 @@ class PROJECTEXPLORER_EXPORT RawProjectPartFlags
public:
RawProjectPartFlags() = default;
RawProjectPartFlags(const ToolChain *toolChain, const QStringList &commandLineFlags,
- const QString &includeFileBaseDir);
+ const Utils::FilePath &includeFileBaseDir);
public:
QStringList commandLineFlags;
diff --git a/src/plugins/projectexplorer/runconfiguration.cpp b/src/plugins/projectexplorer/runconfiguration.cpp
index b5f3f8e703..29b59ad898 100644
--- a/src/plugins/projectexplorer/runconfiguration.cpp
+++ b/src/plugins/projectexplorer/runconfiguration.cpp
@@ -12,10 +12,10 @@
#include "projectexplorer.h"
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "projectnodes.h"
#include "runconfigurationaspects.h"
#include "runcontrol.h"
-#include "session.h"
#include "target.h"
#include <coreplugin/icontext.h>
@@ -33,7 +33,6 @@
#include <utils/utilsicons.h>
#include <utils/variablechooser.h>
-#include <QDir>
#include <QHash>
#include <QPushButton>
#include <QTimer>
@@ -217,13 +216,15 @@ bool RunConfiguration::isEnabled() const
QWidget *RunConfiguration::createConfigurationWidget()
{
- Layouting::Form builder;
- for (BaseAspect *aspect : std::as_const(m_aspects)) {
- if (aspect->isVisible())
- aspect->addToLayout(builder.finishRow());
+ Layouting::Form form;
+ for (BaseAspect *aspect : std::as_const(*this)) {
+ if (aspect->isVisible()) {
+ form.addItem(aspect);
+ form.addItem(Layouting::br);
+ }
}
-
- auto widget = builder.emerge(Layouting::WithoutMargins);
+ form.addItem(Layouting::noMargin);
+ auto widget = form.emerge();
VariableChooser::addSupportForChildWidgets(widget, &m_expander);
@@ -246,7 +247,7 @@ void RunConfiguration::addAspectFactory(const AspectFactory &aspectFactory)
QMap<Utils::Id, QVariantMap> RunConfiguration::settingsData() const
{
QMap<Utils::Id, QVariantMap> data;
- for (BaseAspect *aspect : m_aspects)
+ for (BaseAspect *aspect : *this)
aspect->toActiveMap(data[aspect->id()]);
return data;
}
@@ -254,7 +255,7 @@ QMap<Utils::Id, QVariantMap> RunConfiguration::settingsData() const
AspectContainerData RunConfiguration::aspectData() const
{
AspectContainerData data;
- for (BaseAspect *aspect : m_aspects)
+ for (BaseAspect *aspect : *this)
data.append(aspect->extractData());
return data;
}
@@ -299,6 +300,13 @@ CommandLine RunConfiguration::commandLine() const
return m_commandLineGetter();
}
+bool RunConfiguration::isPrintEnvironmentEnabled() const
+{
+ if (const auto envAspect = aspect<EnvironmentAspect>())
+ return envAspect->isPrintOnRunEnabled();
+ return false;
+}
+
void RunConfiguration::setRunnableModifier(const RunnableModifier &runnableModifier)
{
m_runnableModifier = runnableModifier;
@@ -313,7 +321,7 @@ void RunConfiguration::update()
const bool isActive = target()->isActive() && target()->activeRunConfiguration() == this;
- if (isActive && project() == SessionManager::startupProject())
+ if (isActive && project() == ProjectManager::startupProject())
ProjectExplorerPlugin::updateRunActions();
}
@@ -392,7 +400,7 @@ Runnable RunConfiguration::runnable() const
Runnable r;
r.command = commandLine();
if (auto workingDirectoryAspect = aspect<WorkingDirectoryAspect>())
- r.workingDirectory = workingDirectoryAspect->workingDirectory().onDevice(r.command.executable());
+ r.workingDirectory = r.command.executable().withNewPath(workingDirectoryAspect->workingDirectory().path());
if (auto environmentAspect = aspect<EnvironmentAspect>())
r.environment = environmentAspect->environment();
if (m_runnableModifier)
@@ -558,7 +566,7 @@ RunConfiguration *RunConfigurationFactory::create(Target *target) const
// Add the universal aspects.
for (const RunConfiguration::AspectFactory &factory : theAspectFactories)
- rc->m_aspects.registerAspect(factory(target));
+ rc->registerAspect(factory(target));
return rc;
}
diff --git a/src/plugins/projectexplorer/runconfiguration.h b/src/plugins/projectexplorer/runconfiguration.h
index 32f61b5a30..5970414156 100644
--- a/src/plugins/projectexplorer/runconfiguration.h
+++ b/src/plugins/projectexplorer/runconfiguration.h
@@ -116,6 +116,7 @@ public:
using CommandLineGetter = std::function<Utils::CommandLine()>;
void setCommandLineGetter(const CommandLineGetter &cmdGetter);
Utils::CommandLine commandLine() const;
+ bool isPrintEnvironmentEnabled() const;
using RunnableModifier = std::function<void(Runnable &)>;
void setRunnableModifier(const RunnableModifier &extraModifier);
@@ -137,6 +138,7 @@ public:
return nullptr;
}
+ using ProjectConfiguration::registerAspect;
using AspectFactory = std::function<Utils::BaseAspect *(Target *)>;
template <class T> static void registerAspect()
{
diff --git a/src/plugins/projectexplorer/runconfigurationaspects.cpp b/src/plugins/projectexplorer/runconfigurationaspects.cpp
index 9ba24c0467..ce28582d55 100644
--- a/src/plugins/projectexplorer/runconfigurationaspects.cpp
+++ b/src/plugins/projectexplorer/runconfigurationaspects.cpp
@@ -19,8 +19,8 @@
#include <utils/fancylineedit.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/utilsicons.h>
#include <QCheckBox>
@@ -33,7 +33,7 @@
#include <QPushButton>
using namespace Utils;
-using namespace Utils::Layouting;
+using namespace Layouting;
namespace ProjectExplorer {
@@ -62,12 +62,13 @@ TerminalAspect::TerminalAspect()
/*!
\reimp
*/
-void TerminalAspect::addToLayout(LayoutBuilder &builder)
+void TerminalAspect::addToLayout(LayoutItem &parent)
{
QTC_CHECK(!m_checkBox);
- m_checkBox = new QCheckBox(Tr::tr("Run in terminal"));
+ m_checkBox = createSubWidget<QCheckBox>(Tr::tr("Run in terminal"));
m_checkBox->setChecked(m_useTerminal);
- builder.addItems({{}, m_checkBox.data()});
+ m_checkBox->setEnabled(isEnabled());
+ parent.addItems({{}, m_checkBox.data()});
connect(m_checkBox.data(), &QAbstractButton::clicked, this, [this] {
m_userSet = true;
m_useTerminal = m_checkBox->isChecked();
@@ -123,7 +124,7 @@ void TerminalAspect::calculateUseTerminal()
*/
bool TerminalAspect::useTerminal() const
{
- return m_useTerminal;
+ return m_useTerminal && isEnabled();
}
/*!
@@ -163,7 +164,7 @@ WorkingDirectoryAspect::WorkingDirectoryAspect(const MacroExpander *expander,
/*!
\reimp
*/
-void WorkingDirectoryAspect::addToLayout(LayoutBuilder &builder)
+void WorkingDirectoryAspect::addToLayout(LayoutItem &builder)
{
QTC_CHECK(!m_chooser);
m_chooser = new PathChooser;
@@ -435,7 +436,7 @@ QWidget *ArgumentsAspect::setupChooser()
/*!
\reimp
*/
-void ArgumentsAspect::addToLayout(LayoutBuilder &builder)
+void ArgumentsAspect::addToLayout(LayoutItem &builder)
{
QTC_CHECK(!m_chooser && !m_multiLineChooser && !m_multiLineButton);
@@ -501,7 +502,7 @@ ExecutableAspect::ExecutableAspect(Target *target, ExecutionDeviceSelector selec
setId("ExecutableAspect");
addDataExtractor(this, &ExecutableAspect::executable, &Data::executable);
- m_executable.setPlaceHolderText(Tr::tr("<unknown>"));
+ m_executable.setPlaceHolderText(Tr::tr("path to the executable cannot be empty"));
m_executable.setLabelText(Tr::tr("Executable:"));
m_executable.setDisplayStyle(StringAspect::LabelDisplay);
@@ -570,19 +571,12 @@ void ExecutableAspect::setExpectedKind(const PathChooser::Kind expectedKind)
Sets the environment in which paths will be searched when the expected kind
of paths is chosen as PathChooser::Command or PathChooser::ExistingCommand
to \a env.
-
- \sa Utils::StringAspect::setEnvironmentChange()
*/
-void ExecutableAspect::setEnvironmentChange(const EnvironmentChange &change)
-{
- m_executable.setEnvironmentChange(change);
- if (m_alternativeExecutable)
- m_alternativeExecutable->setEnvironmentChange(change);
-}
-
void ExecutableAspect::setEnvironment(const Environment &env)
{
- setEnvironmentChange(EnvironmentChange::fromDictionary(env.toDictionary()));
+ m_executable.setEnvironment(env);
+ if (m_alternativeExecutable)
+ m_alternativeExecutable->setEnvironment(env);
}
/*!
@@ -631,7 +625,7 @@ FilePath ExecutableAspect::executable() const
: m_executable.filePath();
if (const IDevice::ConstPtr dev = executionDevice(m_target, m_selector))
- exe = exe.onDevice(dev->rootPath());
+ exe = dev->rootPath().withNewMappedPath(exe);
return exe;
}
@@ -639,11 +633,11 @@ FilePath ExecutableAspect::executable() const
/*!
\reimp
*/
-void ExecutableAspect::addToLayout(LayoutBuilder &builder)
+void ExecutableAspect::addToLayout(LayoutItem &builder)
{
- m_executable.addToLayout(builder);
+ builder.addItem(m_executable);
if (m_alternativeExecutable)
- m_alternativeExecutable->addToLayout(builder.finishRow());
+ builder.addItems({br, m_alternativeExecutable});
}
/*!
@@ -837,7 +831,7 @@ void InterpreterAspect::toMap(QVariantMap &map) const
saveToMap(map, m_currentId, QString(), settingsKey());
}
-void InterpreterAspect::addToLayout(LayoutBuilder &builder)
+void InterpreterAspect::addToLayout(LayoutItem &builder)
{
if (QTC_GUARD(m_comboBox.isNull()))
m_comboBox = new QComboBox;
diff --git a/src/plugins/projectexplorer/runconfigurationaspects.h b/src/plugins/projectexplorer/runconfigurationaspects.h
index 9e948bc6ec..9fc7364772 100644
--- a/src/plugins/projectexplorer/runconfigurationaspects.h
+++ b/src/plugins/projectexplorer/runconfigurationaspects.h
@@ -29,7 +29,7 @@ class PROJECTEXPLORER_EXPORT TerminalAspect : public Utils::BaseAspect
public:
TerminalAspect();
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
bool useTerminal() const;
void setUseTerminalHint(bool useTerminal);
@@ -62,7 +62,7 @@ public:
explicit WorkingDirectoryAspect(const Utils::MacroExpander *expander,
EnvironmentAspect *envAspect);
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
Utils::FilePath workingDirectory() const;
Utils::FilePath defaultWorkingDirectory() const;
@@ -91,7 +91,7 @@ class PROJECTEXPLORER_EXPORT ArgumentsAspect : public Utils::BaseAspect
public:
explicit ArgumentsAspect(const Utils::MacroExpander *macroExpander);
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
QString arguments() const;
QString unexpandedArguments() const;
@@ -163,12 +163,11 @@ public:
void setSettingsKey(const QString &key);
void makeOverridable(const QString &overridingKey, const QString &useOverridableKey);
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
void setLabelText(const QString &labelText);
void setPlaceHolderText(const QString &placeHolderText);
void setHistoryCompleter(const QString &historyCompleterKey);
void setExpectedKind(const Utils::PathChooser::Kind expectedKind);
- void setEnvironmentChange(const Utils::EnvironmentChange &change);
void setEnvironment(const Utils::Environment &env);
void setDisplayStyle(Utils::StringAspect::DisplayStyle style);
@@ -236,7 +235,7 @@ public:
void fromMap(const QVariantMap &) override;
void toMap(QVariantMap &) const override;
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
struct Data : Utils::BaseAspect::Data { Interpreter interpreter; };
diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp
index 72b2e76e50..f9bafd234a 100644
--- a/src/plugins/projectexplorer/runcontrol.cpp
+++ b/src/plugins/projectexplorer/runcontrol.cpp
@@ -25,9 +25,9 @@
#include <utils/checkablemessagebox.h>
#include <utils/fileinprojectfinder.h>
#include <utils/outputformatter.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/utilsicons.h>
#include <coreplugin/icontext.h>
@@ -255,9 +255,9 @@ public:
// A handle to the actual application process.
ProcessHandle applicationProcessHandle;
- RunControlState state = RunControlState::Initialized;
-
QList<QPointer<RunWorker>> m_workers;
+ RunControlState state = RunControlState::Initialized;
+ bool printEnvironment = false;
};
class RunControlPrivate : public QObject, public RunControlPrivateData
@@ -334,6 +334,7 @@ void RunControl::copyDataFromRunConfiguration(RunConfiguration *runConfig)
d->buildKey = runConfig->buildKey();
d->settingsData = runConfig->settingsData();
d->aspectData = runConfig->aspectData();
+ d->printEnvironment = runConfig->isPrintEnvironmentEnabled();
setTarget(runConfig->target());
@@ -817,6 +818,11 @@ Utils::Id RunControl::runMode() const
return d->runMode;
}
+bool RunControl::isPrintEnvironmentEnabled() const
+{
+ return d->printEnvironment;
+}
+
const Runnable &RunControl::runnable() const
{
return d->runnable;
@@ -1045,35 +1051,31 @@ bool RunControl::showPromptToStopDialog(const QString &title,
{
// Show a question message box where user can uncheck this
// question for this class.
- Utils::CheckableMessageBox messageBox(Core::ICore::dialogParent());
- messageBox.setWindowTitle(title);
- messageBox.setText(text);
- messageBox.setStandardButtons(QDialogButtonBox::Yes|QDialogButtonBox::Cancel);
+ QMap<QMessageBox::StandardButton, QString> buttonTexts;
if (!stopButtonText.isEmpty())
- messageBox.button(QDialogButtonBox::Yes)->setText(stopButtonText);
+ buttonTexts[QMessageBox::Yes] = stopButtonText;
if (!cancelButtonText.isEmpty())
- messageBox.button(QDialogButtonBox::Cancel)->setText(cancelButtonText);
- messageBox.setDefaultButton(QDialogButtonBox::Yes);
- if (prompt) {
- messageBox.setCheckBoxText(Utils::CheckableMessageBox::msgDoNotAskAgain());
- messageBox.setChecked(false);
- } else {
- messageBox.setCheckBoxVisible(false);
- }
- messageBox.exec();
- const bool close = messageBox.clickedStandardButton() == QDialogButtonBox::Yes;
- if (close && prompt && messageBox.isChecked())
- *prompt = false;
- return close;
+ buttonTexts[QMessageBox::Cancel] = cancelButtonText;
+
+ CheckableDecider decider;
+ if (prompt)
+ decider = CheckableDecider(prompt);
+
+ auto selected = CheckableMessageBox::question(Core::ICore::dialogParent(),
+ title,
+ text,
+ decider,
+ QMessageBox::Yes | QMessageBox::Cancel,
+ QMessageBox::Yes);
+
+ return selected == QMessageBox::Yes;
}
void RunControl::provideAskPassEntry(Environment &env)
{
- if (env.value("SUDO_ASKPASS").isEmpty()) {
- const FilePath askpass = SshSettings::askpassFilePath();
- if (askpass.exists())
- env.set("SUDO_ASKPASS", askpass.toUserOutput());
- }
+ const FilePath askpass = SshSettings::askpassFilePath();
+ if (askpass.exists())
+ env.setFallback("SUDO_ASKPASS", askpass.toUserOutput());
}
bool RunControlPrivate::isAllowedTransition(RunControlState from, RunControlState to)
@@ -1175,7 +1177,7 @@ public:
bool m_runAsRoot = false;
- QtcProcess m_process;
+ Process m_process;
QTextCodec *m_outputCodec = nullptr;
QTextCodec::ConverterState m_outputCodecState;
@@ -1212,11 +1214,11 @@ SimpleTargetRunnerPrivate::SimpleTargetRunnerPrivate(SimpleTargetRunner *parent)
: q(parent)
{
m_process.setProcessChannelMode(defaultProcessChannelMode());
- connect(&m_process, &QtcProcess::started, this, &SimpleTargetRunnerPrivate::forwardStarted);
- connect(&m_process, &QtcProcess::done, this, &SimpleTargetRunnerPrivate::handleDone);
- connect(&m_process, &QtcProcess::readyReadStandardError,
+ connect(&m_process, &Process::started, this, &SimpleTargetRunnerPrivate::forwardStarted);
+ connect(&m_process, &Process::done, this, &SimpleTargetRunnerPrivate::handleDone);
+ connect(&m_process, &Process::readyReadStandardError,
this, &SimpleTargetRunnerPrivate::handleStandardError);
- connect(&m_process, &QtcProcess::readyReadStandardOutput,
+ connect(&m_process, &Process::readyReadStandardOutput,
this, &SimpleTargetRunnerPrivate::handleStandardOutput);
if (WinDebugInterface::instance()) {
@@ -1371,7 +1373,7 @@ void SimpleTargetRunnerPrivate::start()
Encapsulates processes running in a console or as GUI processes,
captures debug output of GUI processes on Windows (outputDebugString()).
- \sa Utils::QtcProcess
+ \sa Utils::Process
*/
SimpleTargetRunner::SimpleTargetRunner(RunControl *runControl)
@@ -1433,11 +1435,20 @@ void SimpleTargetRunner::start()
d->m_stopForced = false;
d->m_stopReported = false;
d->disconnect(this);
- d->m_process.setTerminalMode(useTerminal ? Utils::TerminalMode::On : Utils::TerminalMode::Off);
+ d->m_process.setTerminalMode(useTerminal ? Utils::TerminalMode::Run : Utils::TerminalMode::Off);
d->m_runAsRoot = runAsRoot;
const QString msg = Tr::tr("Starting %1...").arg(d->m_command.displayName());
appendMessage(msg, NormalMessageFormat);
+ if (runControl()->isPrintEnvironmentEnabled()) {
+ appendMessage(Tr::tr("Environment:"), NormalMessageFormat);
+ runControl()->runnable().environment
+ .forEachEntry([this](const QString &key, const QString &value, bool enabled) {
+ if (enabled)
+ appendMessage(key + '=' + value, StdOutFormat);
+ });
+ appendMessage({}, StdOutFormat);
+ }
const bool isDesktop = !d->m_command.executable().needsDevice();
if (isDesktop && d->m_command.isEmpty()) {
@@ -1653,9 +1664,9 @@ void RunWorker::reportFailure(const QString &msg)
* Appends a message in the specified \a format to
* the owning RunControl's \uicontrol{Application Output} pane.
*/
-void RunWorker::appendMessage(const QString &msg, OutputFormat format)
+void RunWorker::appendMessage(const QString &msg, OutputFormat format, bool appendNewLine)
{
- if (msg.endsWith('\n'))
+ if (!appendNewLine || msg.endsWith('\n'))
emit d->runControl->appendMessage(msg, format);
else
emit d->runControl->appendMessage(msg + '\n', format);
diff --git a/src/plugins/projectexplorer/runcontrol.h b/src/plugins/projectexplorer/runcontrol.h
index d779b693a8..84a5b5c166 100644
--- a/src/plugins/projectexplorer/runcontrol.h
+++ b/src/plugins/projectexplorer/runcontrol.h
@@ -67,7 +67,7 @@ public:
QVariant recordedData(const QString &channel) const;
// Part of read-only interface of RunControl for convenience.
- void appendMessage(const QString &msg, Utils::OutputFormat format);
+ void appendMessage(const QString &msg, Utils::OutputFormat format, bool appendNewLine = true);
void appendMessageChunk(const QString &msg, Utils::OutputFormat format);
IDeviceConstPtr device() const;
@@ -205,6 +205,7 @@ public:
void setupFormatter(Utils::OutputFormatter *formatter) const;
Utils::Id runMode() const;
+ bool isPrintEnvironmentEnabled() const;
const Runnable &runnable() const;
diff --git a/src/plugins/projectexplorer/runsettingspropertiespage.cpp b/src/plugins/projectexplorer/runsettingspropertiespage.cpp
index bd8b30f2b7..43edee9b66 100644
--- a/src/plugins/projectexplorer/runsettingspropertiespage.cpp
+++ b/src/plugins/projectexplorer/runsettingspropertiespage.cpp
@@ -10,9 +10,10 @@
#include "projectconfigurationmodel.h"
#include "projectexplorertr.h"
#include "runconfiguration.h"
-#include "session.h"
#include "target.h"
+#include <coreplugin/session.h>
+
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
@@ -291,11 +292,10 @@ void RunSettingsWidget::currentDeployConfigurationChanged(int index)
if (m_ignoreChanges.isLocked())
return;
if (index == -1)
- SessionManager::setActiveDeployConfiguration(m_target, nullptr, SetActive::Cascade);
+ m_target->setActiveDeployConfiguration(nullptr, SetActive::Cascade);
else
- SessionManager::setActiveDeployConfiguration(m_target,
- qobject_cast<DeployConfiguration *>(m_target->deployConfigurationModel()->projectConfigurationAt(index)),
- SetActive::Cascade);
+ m_target->setActiveDeployConfiguration(qobject_cast<DeployConfiguration *>(m_target->deployConfigurationModel()->projectConfigurationAt(index)),
+ SetActive::Cascade);
}
void RunSettingsWidget::aboutToShowDeployMenu()
@@ -309,7 +309,7 @@ void RunSettingsWidget::aboutToShowDeployMenu()
if (!newDc)
return;
m_target->addDeployConfiguration(newDc);
- SessionManager::setActiveDeployConfiguration(m_target, newDc, SetActive::Cascade);
+ m_target->setActiveDeployConfiguration(newDc, SetActive::Cascade);
m_removeDeployToolButton->setEnabled(m_target->deployConfigurations().size() > 1);
});
}
diff --git a/src/plugins/projectexplorer/selectablefilesmodel.cpp b/src/plugins/projectexplorer/selectablefilesmodel.cpp
index 365c319992..de5b0b79b4 100644
--- a/src/plugins/projectexplorer/selectablefilesmodel.cpp
+++ b/src/plugins/projectexplorer/selectablefilesmodel.cpp
@@ -9,10 +9,10 @@
#include <coreplugin/icore.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/fancylineedit.h>
#include <utils/fsengine/fileiconprovider.h>
#include <utils/pathchooser.h>
-#include <utils/runextensions.h>
#include <utils/stringutils.h>
#include <QDialogButtonBox>
@@ -51,13 +51,13 @@ void SelectableFilesFromDirModel::startParsing(const Utils::FilePath &baseDir)
m_rootForFuture->fullPath = baseDir;
m_rootForFuture->isDir = true;
- m_watcher.setFuture(Utils::runAsync(&SelectableFilesFromDirModel::run, this));
+ m_watcher.setFuture(Utils::asyncRun(&SelectableFilesFromDirModel::run, this));
}
-void SelectableFilesFromDirModel::run(QFutureInterface<void> &fi)
+void SelectableFilesFromDirModel::run(QPromise<void> &promise)
{
m_futureCount = 0;
- buildTree(m_baseDir, m_rootForFuture, fi, 5);
+ buildTree(m_baseDir, m_rootForFuture, promise, 5);
}
void SelectableFilesFromDirModel::buildTreeFinished()
@@ -97,7 +97,7 @@ SelectableFilesModel::FilterState SelectableFilesModel::filter(Tree *t)
}
void SelectableFilesFromDirModel::buildTree(const Utils::FilePath &baseDir, Tree *tree,
- QFutureInterface<void> &fi, int symlinkDepth)
+ QPromise<void> &promise, int symlinkDepth)
{
if (symlinkDepth == 0)
return;
@@ -111,7 +111,7 @@ void SelectableFilesFromDirModel::buildTree(const Utils::FilePath &baseDir, Tree
Utils::FilePath fn = Utils::FilePath::fromFileInfo(fileInfo);
if (m_futureCount % 100) {
emit parsingProgress(fn);
- if (fi.isCanceled())
+ if (promise.isCanceled())
return;
}
++m_futureCount;
@@ -121,7 +121,7 @@ void SelectableFilesFromDirModel::buildTree(const Utils::FilePath &baseDir, Tree
t->name = fileInfo.fileName();
t->fullPath = fn;
t->isDir = true;
- buildTree(fn, t, fi, symlinkDepth - fileInfo.isSymLink());
+ buildTree(fn, t, promise, symlinkDepth - fileInfo.isSymLink());
allChecked &= t->checked == Qt::Checked;
allUnchecked &= t->checked == Qt::Unchecked;
tree->childDirectories.append(t);
diff --git a/src/plugins/projectexplorer/selectablefilesmodel.h b/src/plugins/projectexplorer/selectablefilesmodel.h
index 6340e2d1a2..8c47fbd990 100644
--- a/src/plugins/projectexplorer/selectablefilesmodel.h
+++ b/src/plugins/projectexplorer/selectablefilesmodel.h
@@ -9,7 +9,6 @@
#include <QAbstractItemModel>
#include <QDialog>
-#include <QFutureInterface>
#include <QFutureWatcher>
#include <QLabel>
#include <QRegularExpression>
@@ -147,11 +146,9 @@ signals:
void parsingProgress(const Utils::FilePath &fileName);
private:
- void buildTree(const Utils::FilePath &baseDir,
- Tree *tree,
- QFutureInterface<void> &fi,
+ void buildTree(const Utils::FilePath &baseDir, Tree *tree, QPromise<void> &promise,
int symlinkDepth);
- void run(QFutureInterface<void> &fi);
+ void run(QPromise<void> &promise);
void buildTreeFinished();
// Used in the future thread need to all not used after calling startParsing
diff --git a/src/plugins/projectexplorer/session.cpp b/src/plugins/projectexplorer/session.cpp
deleted file mode 100644
index 6dcfd9a461..0000000000
--- a/src/plugins/projectexplorer/session.cpp
+++ /dev/null
@@ -1,1262 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "session.h"
-
-#include "buildconfiguration.h"
-#include "deployconfiguration.h"
-#include "editorconfiguration.h"
-#include "kit.h"
-#include "project.h"
-#include "projectexplorer.h"
-#include "projectexplorerconstants.h"
-#include "projectexplorertr.h"
-#include "projectnodes.h"
-#include "target.h"
-
-#include <coreplugin/coreconstants.h>
-#include <coreplugin/editormanager/editormanager.h>
-#include <coreplugin/foldernavigationwidget.h>
-#include <coreplugin/icore.h>
-#include <coreplugin/idocument.h>
-#include <coreplugin/imode.h>
-#include <coreplugin/modemanager.h>
-#include <coreplugin/progressmanager/progressmanager.h>
-
-#include <texteditor/texteditor.h>
-
-#include <utils/algorithm.h>
-#include <utils/filepath.h>
-#include <utils/qtcassert.h>
-#include <utils/stylehelper.h>
-#include <utils/qtcassert.h>
-
-#include <QDebug>
-#include <QMessageBox>
-#include <QPushButton>
-
-#ifdef WITH_TESTS
-#include <QTemporaryFile>
-#include <QTest>
-#include <vector>
-#endif
-
-using namespace Core;
-using namespace Utils;
-using namespace ProjectExplorer::Internal;
-
-namespace ProjectExplorer {
-
-const char DEFAULT_SESSION[] = "default";
-const char LAST_ACTIVE_TIMES_KEY[] = "LastActiveTimes";
-
-/*!
- \class ProjectExplorer::SessionManager
-
- \brief The SessionManager class manages sessions.
-
- TODO the interface of this class is not really great.
- The implementation suffers from that all the functions from the
- public interface just wrap around functions which do the actual work.
- This could be improved.
-*/
-
-class SessionManagerPrivate
-{
-public:
- void restoreValues(const PersistentSettingsReader &reader);
- void restoreDependencies(const PersistentSettingsReader &reader);
- void restoreStartupProject(const PersistentSettingsReader &reader);
- void restoreEditors(const PersistentSettingsReader &reader);
- void restoreProjects(const FilePaths &fileList);
- void askUserAboutFailedProjects();
- void sessionLoadingProgress();
-
- bool recursiveDependencyCheck(const FilePath &newDep, const FilePath &checkDep) const;
- FilePaths dependencies(const FilePath &proName) const;
- FilePaths dependenciesOrder() const;
- void dependencies(const FilePath &proName, FilePaths &result) const;
-
- static QString windowTitleAddition(const FilePath &filePath);
- static QString sessionTitle(const FilePath &filePath);
-
- bool hasProjects() const { return !m_projects.isEmpty(); }
-
- QString m_sessionName = QLatin1String(DEFAULT_SESSION);
- bool m_virginSession = true;
- bool m_loadingSession = false;
- bool m_casadeSetActive = false;
-
- mutable QStringList m_sessions;
- mutable QHash<QString, QDateTime> m_sessionDateTimes;
- QHash<QString, QDateTime> m_lastActiveTimes;
-
- Project *m_startupProject = nullptr;
- QList<Project *> m_projects;
- FilePaths m_failedProjects;
- QMap<FilePath, FilePaths> m_depMap;
- QMap<QString, QVariant> m_values;
- QFutureInterface<void> m_future;
- PersistentSettingsWriter *m_writer = nullptr;
-
-private:
- static QString locationInProject(const FilePath &filePath);
-};
-
-static SessionManager *m_instance = nullptr;
-static SessionManagerPrivate *d = nullptr;
-
-static QString projectFolderId(Project *pro)
-{
- return pro->projectFilePath().toString();
-}
-
-const int PROJECT_SORT_VALUE = 100;
-
-SessionManager::SessionManager(QObject *parent) : QObject(parent)
-{
- m_instance = this;
- d = new SessionManagerPrivate;
-
- connect(ModeManager::instance(), &ModeManager::currentModeChanged,
- this, &SessionManager::saveActiveMode);
-
- connect(ICore::instance(), &ICore::saveSettingsRequested, this, [] {
- QVariantMap times;
- for (auto it = d->m_lastActiveTimes.cbegin(); it != d->m_lastActiveTimes.cend(); ++it)
- times.insert(it.key(), it.value());
- ICore::settings()->setValue(LAST_ACTIVE_TIMES_KEY, times);
- });
-
- connect(EditorManager::instance(), &EditorManager::editorCreated,
- this, &SessionManager::configureEditor);
- connect(this, &SessionManager::projectAdded,
- EditorManager::instance(), &EditorManager::updateWindowTitles);
- connect(this, &SessionManager::projectRemoved,
- EditorManager::instance(), &EditorManager::updateWindowTitles);
- connect(this, &SessionManager::projectDisplayNameChanged,
- EditorManager::instance(), &EditorManager::updateWindowTitles);
- connect(EditorManager::instance(), &EditorManager::editorOpened,
- this, &SessionManager::markSessionFileDirty);
- connect(EditorManager::instance(), &EditorManager::editorsClosed,
- this, &SessionManager::markSessionFileDirty);
-
- EditorManager::setWindowTitleAdditionHandler(&SessionManagerPrivate::windowTitleAddition);
- EditorManager::setSessionTitleHandler(&SessionManagerPrivate::sessionTitle);
-}
-
-SessionManager::~SessionManager()
-{
- EditorManager::setWindowTitleAdditionHandler({});
- EditorManager::setSessionTitleHandler({});
- emit m_instance->aboutToUnloadSession(d->m_sessionName);
- delete d->m_writer;
- delete d;
- d = nullptr;
-}
-
-SessionManager *SessionManager::instance()
-{
- return m_instance;
-}
-
-bool SessionManager::isDefaultVirgin()
-{
- return isDefaultSession(d->m_sessionName) && d->m_virginSession;
-}
-
-bool SessionManager::isDefaultSession(const QString &session)
-{
- return session == QLatin1String(DEFAULT_SESSION);
-}
-
-void SessionManager::saveActiveMode(Id mode)
-{
- if (mode != Core::Constants::MODE_WELCOME)
- setValue(QLatin1String("ActiveMode"), mode.toString());
-}
-
-bool SessionManagerPrivate::recursiveDependencyCheck(const FilePath &newDep,
- const FilePath &checkDep) const
-{
- if (newDep == checkDep)
- return false;
-
- const FilePaths depList = m_depMap.value(checkDep);
- for (const FilePath &dependency : depList) {
- if (!recursiveDependencyCheck(newDep, dependency))
- return false;
- }
-
- return true;
-}
-
-/*
- * The dependency management exposes an interface based on projects, but
- * is internally purely string based. This is suboptimal. Probably it would be
- * nicer to map the filenames to projects on load and only map it back to
- * filenames when saving.
- */
-
-QList<Project *> SessionManager::dependencies(const Project *project)
-{
- const FilePath proName = project->projectFilePath();
- const FilePaths proDeps = d->m_depMap.value(proName);
-
- QList<Project *> projects;
- for (const FilePath &dep : proDeps) {
- Project *pro = Utils::findOrDefault(d->m_projects, [&dep](Project *p) {
- return p->projectFilePath() == dep;
- });
- if (pro)
- projects += pro;
- }
-
- return projects;
-}
-
-bool SessionManager::hasDependency(const Project *project, const Project *depProject)
-{
- const FilePath proName = project->projectFilePath();
- const FilePath depName = depProject->projectFilePath();
-
- const FilePaths proDeps = d->m_depMap.value(proName);
- return proDeps.contains(depName);
-}
-
-bool SessionManager::canAddDependency(const Project *project, const Project *depProject)
-{
- const FilePath newDep = project->projectFilePath();
- const FilePath checkDep = depProject->projectFilePath();
-
- return d->recursiveDependencyCheck(newDep, checkDep);
-}
-
-bool SessionManager::addDependency(Project *project, Project *depProject)
-{
- const FilePath proName = project->projectFilePath();
- const FilePath depName = depProject->projectFilePath();
-
- // check if this dependency is valid
- if (!d->recursiveDependencyCheck(proName, depName))
- return false;
-
- FilePaths proDeps = d->m_depMap.value(proName);
- if (!proDeps.contains(depName)) {
- proDeps.append(depName);
- d->m_depMap[proName] = proDeps;
- }
- emit m_instance->dependencyChanged(project, depProject);
-
- return true;
-}
-
-void SessionManager::removeDependency(Project *project, Project *depProject)
-{
- const FilePath proName = project->projectFilePath();
- const FilePath depName = depProject->projectFilePath();
-
- FilePaths proDeps = d->m_depMap.value(proName);
- proDeps.removeAll(depName);
- if (proDeps.isEmpty())
- d->m_depMap.remove(proName);
- else
- d->m_depMap[proName] = proDeps;
- emit m_instance->dependencyChanged(project, depProject);
-}
-
-bool SessionManager::isProjectConfigurationCascading()
-{
- return d->m_casadeSetActive;
-}
-
-void SessionManager::setProjectConfigurationCascading(bool b)
-{
- d->m_casadeSetActive = b;
- markSessionFileDirty();
-}
-
-void SessionManager::setActiveTarget(Project *project, Target *target, SetActive cascade)
-{
- QTC_ASSERT(project, return);
-
- if (project->isShuttingDown())
- return;
-
- project->setActiveTarget(target);
-
- if (!target) // never cascade setting no target
- return;
-
- if (cascade != SetActive::Cascade || !d->m_casadeSetActive)
- return;
-
- Utils::Id kitId = target->kit()->id();
- for (Project *otherProject : SessionManager::projects()) {
- if (otherProject == project)
- continue;
- if (Target *otherTarget = Utils::findOrDefault(otherProject->targets(),
- [kitId](Target *t) { return t->kit()->id() == kitId; }))
- otherProject->setActiveTarget(otherTarget);
- }
-}
-
-void SessionManager::setActiveBuildConfiguration(Target *target, BuildConfiguration *bc, SetActive cascade)
-{
- QTC_ASSERT(target, return);
- QTC_ASSERT(target->project(), return);
-
- if (target->project()->isShuttingDown() || target->isShuttingDown())
- return;
-
- target->setActiveBuildConfiguration(bc);
-
- if (!bc)
- return;
- if (cascade != SetActive::Cascade || !d->m_casadeSetActive)
- return;
-
- Utils::Id kitId = target->kit()->id();
- QString name = bc->displayName(); // We match on displayname
- for (Project *otherProject : SessionManager::projects()) {
- if (otherProject == target->project())
- continue;
- Target *otherTarget = otherProject->activeTarget();
- if (!otherTarget || otherTarget->kit()->id() != kitId)
- continue;
-
- for (BuildConfiguration *otherBc : otherTarget->buildConfigurations()) {
- if (otherBc->displayName() == name) {
- otherTarget->setActiveBuildConfiguration(otherBc);
- break;
- }
- }
- }
-}
-
-void SessionManager::setActiveDeployConfiguration(Target *target, DeployConfiguration *dc, SetActive cascade)
-{
- QTC_ASSERT(target, return);
- QTC_ASSERT(target->project(), return);
-
- if (target->project()->isShuttingDown() || target->isShuttingDown())
- return;
-
- target->setActiveDeployConfiguration(dc);
-
- if (!dc)
- return;
- if (cascade != SetActive::Cascade || !d->m_casadeSetActive)
- return;
-
- Utils::Id kitId = target->kit()->id();
- QString name = dc->displayName(); // We match on displayname
- for (Project *otherProject : SessionManager::projects()) {
- if (otherProject == target->project())
- continue;
- Target *otherTarget = otherProject->activeTarget();
- if (!otherTarget || otherTarget->kit()->id() != kitId)
- continue;
-
- for (DeployConfiguration *otherDc : otherTarget->deployConfigurations()) {
- if (otherDc->displayName() == name) {
- otherTarget->setActiveDeployConfiguration(otherDc);
- break;
- }
- }
- }
-}
-
-void SessionManager::setStartupProject(Project *startupProject)
-{
- QTC_ASSERT((!startupProject && d->m_projects.isEmpty())
- || (startupProject && d->m_projects.contains(startupProject)), return);
-
- if (d->m_startupProject == startupProject)
- return;
-
- d->m_startupProject = startupProject;
- if (d->m_startupProject && d->m_startupProject->needsConfiguration()) {
- ModeManager::activateMode(Constants::MODE_SESSION);
- ModeManager::setFocusToCurrentMode();
- }
- FolderNavigationWidgetFactory::setFallbackSyncFilePath(
- startupProject ? startupProject->projectFilePath().parentDir() : FilePath());
- emit m_instance->startupProjectChanged(startupProject);
-}
-
-Project *SessionManager::startupProject()
-{
- return d->m_startupProject;
-}
-
-Target *SessionManager::startupTarget()
-{
- return d->m_startupProject ? d->m_startupProject->activeTarget() : nullptr;
-}
-
-BuildSystem *SessionManager::startupBuildSystem()
-{
- Target *t = startupTarget();
- return t ? t->buildSystem() : nullptr;
-}
-
-/*!
- * Returns the RunConfiguration of the currently active target
- * of the startup project, if such exists, or \c nullptr otherwise.
- */
-
-
-RunConfiguration *SessionManager::startupRunConfiguration()
-{
- Target *t = startupTarget();
- return t ? t->activeRunConfiguration() : nullptr;
-}
-
-void SessionManager::addProject(Project *pro)
-{
- QTC_ASSERT(pro, return);
- QTC_CHECK(!pro->displayName().isEmpty());
- QTC_CHECK(pro->id().isValid());
-
- d->m_virginSession = false;
- QTC_ASSERT(!d->m_projects.contains(pro), return);
-
- d->m_projects.append(pro);
-
- connect(pro, &Project::displayNameChanged,
- m_instance, [pro]() { emit m_instance->projectDisplayNameChanged(pro); });
-
- emit m_instance->projectAdded(pro);
- const auto updateFolderNavigation = [pro] {
- // destructing projects might trigger changes, so check if the project is actually there
- if (QTC_GUARD(d->m_projects.contains(pro))) {
- const QIcon icon = pro->rootProjectNode() ? pro->rootProjectNode()->icon() : QIcon();
- FolderNavigationWidgetFactory::insertRootDirectory({projectFolderId(pro),
- PROJECT_SORT_VALUE,
- pro->displayName(),
- pro->projectFilePath().parentDir(),
- icon});
- }
- };
- updateFolderNavigation();
- configureEditors(pro);
- connect(pro, &Project::fileListChanged, m_instance, [pro, updateFolderNavigation]() {
- configureEditors(pro);
- updateFolderNavigation(); // update icon
- });
- connect(pro, &Project::displayNameChanged, m_instance, updateFolderNavigation);
-
- if (!startupProject())
- setStartupProject(pro);
-}
-
-void SessionManager::removeProject(Project *project)
-{
- d->m_virginSession = false;
- QTC_ASSERT(project, return);
- removeProjects({project});
-}
-
-bool SessionManager::loadingSession()
-{
- return d->m_loadingSession;
-}
-
-bool SessionManager::save()
-{
- emit m_instance->aboutToSaveSession();
-
- const FilePath filePath = sessionNameToFileName(d->m_sessionName);
- QVariantMap data;
-
- // See the explanation at loadSession() for how we handle the implicit default session.
- if (isDefaultVirgin()) {
- if (filePath.exists()) {
- PersistentSettingsReader reader;
- if (!reader.load(filePath)) {
- QMessageBox::warning(ICore::dialogParent(), Tr::tr("Error while saving session"),
- Tr::tr("Could not save session %1").arg(filePath.toUserOutput()));
- return false;
- }
- data = reader.restoreValues();
- }
- } else {
- // save the startup project
- if (d->m_startupProject)
- data.insert("StartupProject", d->m_startupProject->projectFilePath().toSettings());
-
- const QColor c = StyleHelper::requestedBaseColor();
- if (c.isValid()) {
- QString tmp = QString::fromLatin1("#%1%2%3")
- .arg(c.red(), 2, 16, QLatin1Char('0'))
- .arg(c.green(), 2, 16, QLatin1Char('0'))
- .arg(c.blue(), 2, 16, QLatin1Char('0'));
- data.insert(QLatin1String("Color"), tmp);
- }
-
- FilePaths projectFiles = Utils::transform(projects(), &Project::projectFilePath);
- // Restore information on projects that failed to load:
- // don't read projects to the list, which the user loaded
- for (const FilePath &failed : std::as_const(d->m_failedProjects)) {
- if (!projectFiles.contains(failed))
- projectFiles << failed;
- }
-
- data.insert("ProjectList", Utils::transform<QStringList>(projectFiles,
- &FilePath::toString));
- data.insert("CascadeSetActive", d->m_casadeSetActive);
-
- QVariantMap depMap;
- auto i = d->m_depMap.constBegin();
- while (i != d->m_depMap.constEnd()) {
- QString key = i.key().toString();
- QStringList values;
- const FilePaths valueList = i.value();
- for (const FilePath &value : valueList)
- values << value.toString();
- depMap.insert(key, values);
- ++i;
- }
- data.insert(QLatin1String("ProjectDependencies"), QVariant(depMap));
- data.insert(QLatin1String("EditorSettings"), EditorManager::saveState().toBase64());
- }
-
- const auto end = d->m_values.constEnd();
- QStringList keys;
- for (auto it = d->m_values.constBegin(); it != end; ++it) {
- data.insert(QLatin1String("value-") + it.key(), it.value());
- keys << it.key();
- }
- data.insert(QLatin1String("valueKeys"), keys);
-
- if (!d->m_writer || d->m_writer->fileName() != filePath) {
- delete d->m_writer;
- d->m_writer = new PersistentSettingsWriter(filePath, "QtCreatorSession");
- }
- const bool result = d->m_writer->save(data, ICore::dialogParent());
- if (result) {
- if (!isDefaultVirgin())
- d->m_sessionDateTimes.insert(activeSession(), QDateTime::currentDateTime());
- } else {
- QMessageBox::warning(ICore::dialogParent(), Tr::tr("Error while saving session"),
- Tr::tr("Could not save session to file %1").arg(d->m_writer->fileName().toUserOutput()));
- }
-
- return result;
-}
-
-/*!
- Closes all projects
- */
-void SessionManager::closeAllProjects()
-{
- removeProjects(projects());
-}
-
-const QList<Project *> SessionManager::projects()
-{
- return d->m_projects;
-}
-
-bool SessionManager::hasProjects()
-{
- return d->hasProjects();
-}
-
-bool SessionManager::hasProject(Project *p)
-{
- return d->m_projects.contains(p);
-}
-
-FilePaths SessionManagerPrivate::dependencies(const FilePath &proName) const
-{
- FilePaths result;
- dependencies(proName, result);
- return result;
-}
-
-void SessionManagerPrivate::dependencies(const FilePath &proName, FilePaths &result) const
-{
- const FilePaths depends = m_depMap.value(proName);
-
- for (const FilePath &dep : depends)
- dependencies(dep, result);
-
- if (!result.contains(proName))
- result.append(proName);
-}
-
-QString SessionManagerPrivate::sessionTitle(const FilePath &filePath)
-{
- if (SessionManager::isDefaultSession(d->m_sessionName)) {
- if (filePath.isEmpty()) {
- // use single project's name if there is only one loaded.
- const QList<Project *> projects = SessionManager::projects();
- if (projects.size() == 1)
- return projects.first()->displayName();
- }
- } else {
- QString sessionName = d->m_sessionName;
- if (sessionName.isEmpty())
- sessionName = Tr::tr("Untitled");
- return sessionName;
- }
- return QString();
-}
-
-QString SessionManagerPrivate::locationInProject(const FilePath &filePath) {
- const Project *project = SessionManager::projectForFile(filePath);
- if (!project)
- return QString();
-
- const FilePath parentDir = filePath.parentDir();
- if (parentDir == project->projectDirectory())
- return "@ " + project->displayName();
-
- if (filePath.isChildOf(project->projectDirectory())) {
- const FilePath dirInProject = parentDir.relativeChildPath(project->projectDirectory());
- return "(" + dirInProject.toUserOutput() + " @ " + project->displayName() + ")";
- }
-
- // For a file that is "outside" the project it belongs to, we display its
- // dir's full path because it is easier to read than a series of "../../.".
- // Example: /home/hugo/GenericProject/App.files lists /home/hugo/lib/Bar.cpp
- return "(" + parentDir.toUserOutput() + " @ " + project->displayName() + ")";
-}
-
-QString SessionManagerPrivate::windowTitleAddition(const FilePath &filePath)
-{
- return filePath.isEmpty() ? QString() : locationInProject(filePath);
-}
-
-FilePaths SessionManagerPrivate::dependenciesOrder() const
-{
- QList<QPair<FilePath, FilePaths>> unordered;
- FilePaths ordered;
-
- // copy the map to a temporary list
- for (const Project *pro : m_projects) {
- const FilePath proName = pro->projectFilePath();
- const FilePaths depList = filtered(m_depMap.value(proName),
- [this](const FilePath &proPath) {
- return contains(m_projects, [proPath](const Project *p) {
- return p->projectFilePath() == proPath;
- });
- });
- unordered.push_back({proName, depList});
- }
-
- while (!unordered.isEmpty()) {
- for (int i = (unordered.count() - 1); i >= 0; --i) {
- if (unordered.at(i).second.isEmpty()) {
- ordered << unordered.at(i).first;
- unordered.removeAt(i);
- }
- }
-
- // remove the handled projects from the dependency lists
- // of the remaining unordered projects
- for (int i = 0; i < unordered.count(); ++i) {
- for (const FilePath &pro : std::as_const(ordered)) {
- FilePaths depList = unordered.at(i).second;
- depList.removeAll(pro);
- unordered[i].second = depList;
- }
- }
- }
-
- return ordered;
-}
-
-QList<Project *> SessionManager::projectOrder(const Project *project)
-{
- QList<Project *> result;
-
- FilePaths pros;
- if (project)
- pros = d->dependencies(project->projectFilePath());
- else
- pros = d->dependenciesOrder();
-
- for (const FilePath &proFile : std::as_const(pros)) {
- for (Project *pro : projects()) {
- if (pro->projectFilePath() == proFile) {
- result << pro;
- break;
- }
- }
- }
-
- return result;
-}
-
-Project *SessionManager::projectForFile(const FilePath &fileName)
-{
- if (Project * const project = Utils::findOrDefault(SessionManager::projects(),
- [&fileName](const Project *p) { return p->isKnownFile(fileName); })) {
- return project;
- }
- return Utils::findOrDefault(SessionManager::projects(),
- [&fileName](const Project *p) {
- for (const Target * const target : p->targets()) {
- for (const BuildConfiguration * const bc : target->buildConfigurations()) {
- if (fileName.isChildOf(bc->buildDirectory()))
- return false;
- }
- }
- return fileName.isChildOf(p->projectDirectory());
- });
-}
-
-Project *SessionManager::projectWithProjectFilePath(const FilePath &filePath)
-{
- return Utils::findOrDefault(SessionManager::projects(),
- [&filePath](const Project *p) { return p->projectFilePath() == filePath; });
-}
-
-void SessionManager::configureEditor(IEditor *editor, const QString &fileName)
-{
- if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor*>(editor)) {
- Project *project = projectForFile(Utils::FilePath::fromString(fileName));
- // Global settings are the default.
- if (project)
- project->editorConfiguration()->configureEditor(textEditor);
- }
-}
-
-void SessionManager::configureEditors(Project *project)
-{
- const QList<IDocument *> documents = DocumentModel::openedDocuments();
- for (IDocument *document : documents) {
- if (project->isKnownFile(document->filePath())) {
- const QList<IEditor *> editors = DocumentModel::editorsForDocument(document);
- for (IEditor *editor : editors) {
- if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor*>(editor)) {
- project->editorConfiguration()->configureEditor(textEditor);
- }
- }
- }
- }
-}
-
-void SessionManager::removeProjects(const QList<Project *> &remove)
-{
- for (Project *pro : remove)
- emit m_instance->aboutToRemoveProject(pro);
-
- bool changeStartupProject = false;
-
- // Delete projects
- for (Project *pro : remove) {
- pro->saveSettings();
- pro->markAsShuttingDown();
-
- // Remove the project node:
- d->m_projects.removeOne(pro);
-
- if (pro == d->m_startupProject)
- changeStartupProject = true;
-
- FolderNavigationWidgetFactory::removeRootDirectory(projectFolderId(pro));
- disconnect(pro, nullptr, m_instance, nullptr);
- emit m_instance->projectRemoved(pro);
- }
-
- if (changeStartupProject)
- setStartupProject(hasProjects() ? projects().first() : nullptr);
-
- qDeleteAll(remove);
-}
-
-/*!
- Lets other plugins store persistent values within the session file.
-*/
-
-void SessionManager::setValue(const QString &name, const QVariant &value)
-{
- if (d->m_values.value(name) == value)
- return;
- d->m_values.insert(name, value);
-}
-
-QVariant SessionManager::value(const QString &name)
-{
- auto it = d->m_values.constFind(name);
- return (it == d->m_values.constEnd()) ? QVariant() : *it;
-}
-
-QString SessionManager::activeSession()
-{
- return d->m_sessionName;
-}
-
-QStringList SessionManager::sessions()
-{
- if (d->m_sessions.isEmpty()) {
- // We are not initialized yet, so do that now
- const FilePaths sessionFiles =
- ICore::userResourcePath().dirEntries({{"*qws"}}, QDir::Time | QDir::Reversed);
- const QVariantMap lastActiveTimes = ICore::settings()->value(LAST_ACTIVE_TIMES_KEY).toMap();
- for (const FilePath &file : sessionFiles) {
- const QString &name = file.completeBaseName();
- d->m_sessionDateTimes.insert(name, file.lastModified());
- const auto lastActiveTime = lastActiveTimes.find(name);
- d->m_lastActiveTimes.insert(name, lastActiveTime != lastActiveTimes.end()
- ? lastActiveTime->toDateTime()
- : file.lastModified());
- if (name != QLatin1String(DEFAULT_SESSION))
- d->m_sessions << name;
- }
- d->m_sessions.prepend(QLatin1String(DEFAULT_SESSION));
- }
- return d->m_sessions;
-}
-
-QDateTime SessionManager::sessionDateTime(const QString &session)
-{
- return d->m_sessionDateTimes.value(session);
-}
-
-QDateTime SessionManager::lastActiveTime(const QString &session)
-{
- return d->m_lastActiveTimes.value(session);
-}
-
-FilePath SessionManager::sessionNameToFileName(const QString &session)
-{
- return ICore::userResourcePath(session + ".qws");
-}
-
-/*!
- Creates \a session, but does not actually create the file.
-*/
-
-bool SessionManager::createSession(const QString &session)
-{
- if (sessions().contains(session))
- return false;
- Q_ASSERT(d->m_sessions.size() > 0);
- d->m_sessions.insert(1, session);
- d->m_lastActiveTimes.insert(session, QDateTime::currentDateTime());
- return true;
-}
-
-bool SessionManager::renameSession(const QString &original, const QString &newName)
-{
- if (!cloneSession(original, newName))
- return false;
- if (original == activeSession())
- loadSession(newName);
- emit instance()->sessionRenamed(original, newName);
- return deleteSession(original);
-}
-
-
-/*!
- \brief Shows a dialog asking the user to confirm deleting the session \p session
-*/
-bool SessionManager::confirmSessionDelete(const QStringList &sessions)
-{
- const QString title = sessions.size() == 1 ? Tr::tr("Delete Session") : Tr::tr("Delete Sessions");
- const QString question = sessions.size() == 1
- ? Tr::tr("Delete session %1?").arg(sessions.first())
- : Tr::tr("Delete these sessions?\n %1").arg(sessions.join("\n "));
- return QMessageBox::question(ICore::dialogParent(),
- title,
- question,
- QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes;
-}
-
-/*!
- Deletes \a session name from session list and the file from disk.
-*/
-bool SessionManager::deleteSession(const QString &session)
-{
- if (!d->m_sessions.contains(session))
- return false;
- d->m_sessions.removeOne(session);
- d->m_lastActiveTimes.remove(session);
- emit instance()->sessionRemoved(session);
- FilePath sessionFile = sessionNameToFileName(session);
- if (sessionFile.exists())
- return sessionFile.removeFile();
- return false;
-}
-
-void SessionManager::deleteSessions(const QStringList &sessions)
-{
- for (const QString &session : sessions)
- deleteSession(session);
-}
-
-bool SessionManager::cloneSession(const QString &original, const QString &clone)
-{
- if (!d->m_sessions.contains(original))
- return false;
-
- FilePath sessionFile = sessionNameToFileName(original);
- // If the file does not exist, we can still clone
- if (!sessionFile.exists() || sessionFile.copyFile(sessionNameToFileName(clone))) {
- d->m_sessions.insert(1, clone);
- d->m_sessionDateTimes.insert(clone, sessionNameToFileName(clone).lastModified());
- return true;
- }
- return false;
-}
-
-void SessionManagerPrivate::restoreValues(const PersistentSettingsReader &reader)
-{
- const QStringList keys = reader.restoreValue(QLatin1String("valueKeys")).toStringList();
- for (const QString &key : keys) {
- QVariant value = reader.restoreValue(QLatin1String("value-") + key);
- m_values.insert(key, value);
- }
-}
-
-void SessionManagerPrivate::restoreDependencies(const PersistentSettingsReader &reader)
-{
- QMap<QString, QVariant> depMap = reader.restoreValue(QLatin1String("ProjectDependencies")).toMap();
- auto i = depMap.constBegin();
- while (i != depMap.constEnd()) {
- const QString &key = i.key();
- FilePaths values;
- const QStringList valueList = i.value().toStringList();
- for (const QString &value : valueList)
- values << FilePath::fromString(value);
- m_depMap.insert(FilePath::fromString(key), values);
- ++i;
- }
-}
-
-void SessionManagerPrivate::askUserAboutFailedProjects()
-{
- FilePaths failedProjects = m_failedProjects;
- if (!failedProjects.isEmpty()) {
- QString fileList = FilePath::formatFilePaths(failedProjects, "<br>");
- QMessageBox box(QMessageBox::Warning,
- Tr::tr("Failed to restore project files"),
- Tr::tr("Could not restore the following project files:<br><b>%1</b>").
- arg(fileList));
- auto keepButton = new QPushButton(Tr::tr("Keep projects in Session"), &box);
- auto removeButton = new QPushButton(Tr::tr("Remove projects from Session"), &box);
- box.addButton(keepButton, QMessageBox::AcceptRole);
- box.addButton(removeButton, QMessageBox::DestructiveRole);
-
- box.exec();
-
- if (box.clickedButton() == removeButton)
- m_failedProjects.clear();
- }
-}
-
-void SessionManagerPrivate::restoreStartupProject(const PersistentSettingsReader &reader)
-{
- const FilePath startupProject = FilePath::fromSettings(reader.restoreValue("StartupProject"));
- if (!startupProject.isEmpty()) {
- for (Project *pro : std::as_const(m_projects)) {
- if (pro->projectFilePath() == startupProject) {
- m_instance->setStartupProject(pro);
- break;
- }
- }
- }
- if (!m_startupProject) {
- if (!startupProject.isEmpty())
- qWarning() << "Could not find startup project" << startupProject;
- if (hasProjects())
- m_instance->setStartupProject(m_projects.first());
- }
-}
-
-void SessionManagerPrivate::restoreEditors(const PersistentSettingsReader &reader)
-{
- const QVariant editorsettings = reader.restoreValue(QLatin1String("EditorSettings"));
- if (editorsettings.isValid()) {
- EditorManager::restoreState(QByteArray::fromBase64(editorsettings.toByteArray()));
- sessionLoadingProgress();
- }
-}
-
-/*!
- Loads a session, takes a session name (not filename).
-*/
-void SessionManagerPrivate::restoreProjects(const FilePaths &fileList)
-{
- // indirectly adds projects to session
- // Keep projects that failed to load in the session!
- m_failedProjects = fileList;
- if (!fileList.isEmpty()) {
- ProjectExplorerPlugin::OpenProjectResult result = ProjectExplorerPlugin::openProjects(fileList);
- if (!result)
- ProjectExplorerPlugin::showOpenProjectError(result);
- const QList<Project *> projects = result.projects();
- for (const Project *p : projects)
- m_failedProjects.removeAll(p->projectFilePath());
- }
-}
-
-/*
- * ========== Notes on storing and loading the default session ==========
- * The default session comes in two flavors: implicit and explicit. The implicit one,
- * also referred to as "default virgin" in the code base, is the one that is active
- * at start-up, if no session has been explicitly loaded due to command-line arguments
- * or the "restore last session" setting in the session manager.
- * The implicit default session silently turns into the explicit default session
- * by loading a project or a file or changing settings in the Dependencies panel. The explicit
- * default session can also be loaded by the user via the Welcome Screen.
- * This mechanism somewhat complicates the handling of session-specific settings such as
- * the ones in the task pane: Users expect that changes they make there become persistent, even
- * when they are in the implicit default session. However, we can't just blindly store
- * the implicit default session, because then we'd overwrite the project list of the explicit
- * default session. Therefore, we use the following logic:
- * - Upon start-up, if no session is to be explicitly loaded, we restore the parts of the
- * explicit default session that are not related to projects, editors etc; the
- * "general settings" of the session, so to speak.
- * - When storing the implicit default session, we overwrite only these "general settings"
- * of the explicit default session and keep the others as they are.
- * - When switching from the implicit to the explicit default session, we keep the
- * "general settings" and load everything else from the session file.
- * This guarantees that user changes are properly transferred and nothing gets lost from
- * either the implicit or the explicit default session.
- *
- */
-bool SessionManager::loadSession(const QString &session, bool initial)
-{
- const bool loadImplicitDefault = session.isEmpty();
- const bool switchFromImplicitToExplicitDefault = session == DEFAULT_SESSION
- && d->m_sessionName == DEFAULT_SESSION && !initial;
-
- // Do nothing if we have that session already loaded,
- // exception if the session is the default virgin session
- // we still want to be able to load the default session
- if (session == d->m_sessionName && !isDefaultVirgin())
- return true;
-
- if (!loadImplicitDefault && !sessions().contains(session))
- return false;
-
- FilePaths fileList;
- // Try loading the file
- FilePath fileName = sessionNameToFileName(loadImplicitDefault ? DEFAULT_SESSION : session);
- PersistentSettingsReader reader;
- if (fileName.exists()) {
- if (!reader.load(fileName)) {
- QMessageBox::warning(ICore::dialogParent(), Tr::tr("Error while restoring session"),
- Tr::tr("Could not restore session %1").arg(fileName.toUserOutput()));
-
- return false;
- }
-
- if (loadImplicitDefault) {
- d->restoreValues(reader);
- emit m_instance->sessionLoaded(DEFAULT_SESSION);
- return true;
- }
-
- fileList = FileUtils::toFilePathList(reader.restoreValue("ProjectList").toStringList());
- } else if (loadImplicitDefault) {
- return true;
- }
-
- d->m_loadingSession = true;
-
- // Allow everyone to set something in the session and before saving
- emit m_instance->aboutToUnloadSession(d->m_sessionName);
-
- if (!save()) {
- d->m_loadingSession = false;
- return false;
- }
-
- // Clean up
- if (!EditorManager::closeAllEditors()) {
- d->m_loadingSession = false;
- return false;
- }
-
- // find a list of projects to close later
- const QList<Project *> projectsToRemove = Utils::filtered(projects(), [&fileList](Project *p) {
- return !fileList.contains(p->projectFilePath());
- });
- const QList<Project *> openProjects = projects();
- const FilePaths projectPathsToLoad = Utils::filtered(fileList, [&openProjects](const FilePath &path) {
- return !Utils::contains(openProjects, [&path](Project *p) {
- return p->projectFilePath() == path;
- });
- });
- d->m_failedProjects.clear();
- d->m_depMap.clear();
- if (!switchFromImplicitToExplicitDefault)
- d->m_values.clear();
- d->m_casadeSetActive = false;
-
- d->m_sessionName = session;
- delete d->m_writer;
- d->m_writer = nullptr;
- EditorManager::updateWindowTitles();
-
- if (fileName.exists()) {
- d->m_virginSession = false;
-
- ProgressManager::addTask(d->m_future.future(), Tr::tr("Loading Session"),
- "ProjectExplorer.SessionFile.Load");
-
- d->m_future.setProgressRange(0, 1);
- d->m_future.setProgressValue(0);
-
- if (!switchFromImplicitToExplicitDefault)
- d->restoreValues(reader);
- emit m_instance->aboutToLoadSession(session);
-
- // retrieve all values before the following code could change them again
- Id modeId = Id::fromSetting(value(QLatin1String("ActiveMode")));
- if (!modeId.isValid())
- modeId = Id(Core::Constants::MODE_EDIT);
-
- QColor c = QColor(reader.restoreValue(QLatin1String("Color")).toString());
- if (c.isValid())
- StyleHelper::setBaseColor(c);
-
- d->m_future.setProgressRange(0, projectPathsToLoad.count() + 1/*initialization above*/ + 1/*editors*/);
- d->m_future.setProgressValue(1);
- QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
-
- d->restoreProjects(projectPathsToLoad);
- d->sessionLoadingProgress();
- d->restoreDependencies(reader);
- d->restoreStartupProject(reader);
-
- removeProjects(projectsToRemove); // only remove old projects now that the startup project is set!
-
- d->restoreEditors(reader);
-
- d->m_future.reportFinished();
- d->m_future = QFutureInterface<void>();
-
- // Fall back to Project mode if the startup project is unconfigured and
- // use the mode saved in the session otherwise
- if (d->m_startupProject && d->m_startupProject->needsConfiguration())
- modeId = Id(Constants::MODE_SESSION);
-
- ModeManager::activateMode(modeId);
- ModeManager::setFocusToCurrentMode();
- } else {
- removeProjects(projects());
- ModeManager::activateMode(Id(Core::Constants::MODE_EDIT));
- ModeManager::setFocusToCurrentMode();
- }
-
- d->m_casadeSetActive = reader.restoreValue(QLatin1String("CascadeSetActive"), false).toBool();
- d->m_lastActiveTimes.insert(session, QDateTime::currentDateTime());
-
- emit m_instance->sessionLoaded(session);
-
- // Starts a event loop, better do that at the very end
- d->askUserAboutFailedProjects();
- d->m_loadingSession = false;
- return true;
-}
-
-/*!
- Returns the last session that was opened by the user.
-*/
-QString SessionManager::lastSession()
-{
- return ICore::settings()->value(Constants::LASTSESSION_KEY).toString();
-}
-
-/*!
- Returns the session that was active when Qt Creator was last closed, if any.
-*/
-QString SessionManager::startupSession()
-{
- return ICore::settings()->value(Constants::STARTUPSESSION_KEY).toString();
-}
-
-void SessionManager::reportProjectLoadingProgress()
-{
- d->sessionLoadingProgress();
-}
-
-void SessionManager::markSessionFileDirty()
-{
- d->m_virginSession = false;
-}
-
-void SessionManagerPrivate::sessionLoadingProgress()
-{
- m_future.setProgressValue(m_future.progressValue() + 1);
- QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
-}
-
-FilePaths SessionManager::projectsForSessionName(const QString &session)
-{
- const FilePath fileName = sessionNameToFileName(session);
- PersistentSettingsReader reader;
- if (fileName.exists()) {
- if (!reader.load(fileName)) {
- qWarning() << "Could not restore session" << fileName.toUserOutput();
- return {};
- }
- }
- return transform(reader.restoreValue(QLatin1String("ProjectList")).toStringList(),
- &FilePath::fromUserInput);
-}
-
-#ifdef WITH_TESTS
-
-void ProjectExplorerPlugin::testSessionSwitch()
-{
- QVERIFY(SessionManager::createSession("session1"));
- QVERIFY(SessionManager::createSession("session2"));
- QTemporaryFile cppFile("main.cpp");
- QVERIFY(cppFile.open());
- cppFile.close();
- QTemporaryFile projectFile1("XXXXXX.pro");
- QTemporaryFile projectFile2("XXXXXX.pro");
- struct SessionSpec {
- SessionSpec(const QString &n, QTemporaryFile &f) : name(n), projectFile(f) {}
- const QString name;
- QTemporaryFile &projectFile;
- };
- std::vector<SessionSpec> sessionSpecs{SessionSpec("session1", projectFile1),
- SessionSpec("session2", projectFile2)};
- for (const SessionSpec &sessionSpec : sessionSpecs) {
- static const QByteArray proFileContents
- = "TEMPLATE = app\n"
- "CONFIG -= qt\n"
- "SOURCES = " + cppFile.fileName().toLocal8Bit();
- QVERIFY(sessionSpec.projectFile.open());
- sessionSpec.projectFile.write(proFileContents);
- sessionSpec.projectFile.close();
- QVERIFY(SessionManager::loadSession(sessionSpec.name));
- const OpenProjectResult openResult
- = ProjectExplorerPlugin::openProject(
- FilePath::fromString(sessionSpec.projectFile.fileName()));
- if (openResult.errorMessage().contains("text/plain"))
- QSKIP("This test requires the presence of QmakeProjectManager to be fully functional");
- QVERIFY(openResult);
- QCOMPARE(openResult.projects().count(), 1);
- QVERIFY(openResult.project());
- QCOMPARE(SessionManager::projects().count(), 1);
- }
- for (int i = 0; i < 30; ++i) {
- QVERIFY(SessionManager::loadSession("session1"));
- QCOMPARE(SessionManager::activeSession(), "session1");
- QCOMPARE(SessionManager::projects().count(), 1);
- QVERIFY(SessionManager::loadSession("session2"));
- QCOMPARE(SessionManager::activeSession(), "session2");
- QCOMPARE(SessionManager::projects().count(), 1);
- }
- QVERIFY(SessionManager::loadSession("session1"));
- SessionManager::closeAllProjects();
- QVERIFY(SessionManager::loadSession("session2"));
- SessionManager::closeAllProjects();
- QVERIFY(SessionManager::deleteSession("session1"));
- QVERIFY(SessionManager::deleteSession("session2"));
-}
-
-#endif // WITH_TESTS
-
-} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/session.h b/src/plugins/projectexplorer/session.h
deleted file mode 100644
index a29e478046..0000000000
--- a/src/plugins/projectexplorer/session.h
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "projectexplorer_export.h"
-
-#include <utils/id.h>
-#include <utils/persistentsettings.h>
-
-#include <QDateTime>
-#include <QString>
-#include <QStringList>
-
-namespace Core { class IEditor; }
-
-namespace ProjectExplorer {
-
-class Project;
-class Target;
-class BuildConfiguration;
-class BuildSystem;
-class DeployConfiguration;
-class RunConfiguration;
-
-enum class SetActive { Cascade, NoCascade };
-
-class PROJECTEXPLORER_EXPORT SessionManager : public QObject
-{
- Q_OBJECT
-
-public:
- explicit SessionManager(QObject *parent = nullptr);
- ~SessionManager() override;
-
- static SessionManager *instance();
-
- // higher level session management
- static QString activeSession();
- static QString lastSession();
- static QString startupSession();
- static QStringList sessions();
- static QDateTime sessionDateTime(const QString &session);
- static QDateTime lastActiveTime(const QString &session);
-
- static bool createSession(const QString &session);
-
- static bool confirmSessionDelete(const QStringList &sessions);
- static bool deleteSession(const QString &session);
- static void deleteSessions(const QStringList &sessions);
-
- static bool cloneSession(const QString &original, const QString &clone);
- static bool renameSession(const QString &original, const QString &newName);
-
- static bool loadSession(const QString &session, bool initial = false);
-
- static bool save();
- static void closeAllProjects();
-
- static void addProject(Project *project);
- static void removeProject(Project *project);
- static void removeProjects(const QList<Project *> &remove);
-
- static void setStartupProject(Project *startupProject);
-
- static QList<Project *> dependencies(const Project *project);
- static bool hasDependency(const Project *project, const Project *depProject);
- static bool canAddDependency(const Project *project, const Project *depProject);
- static bool addDependency(Project *project, Project *depProject);
- static void removeDependency(Project *project, Project *depProject);
-
- static bool isProjectConfigurationCascading();
- static void setProjectConfigurationCascading(bool b);
-
- static void setActiveTarget(Project *p, Target *t, SetActive cascade);
- static void setActiveBuildConfiguration(Target *t, BuildConfiguration *bc, SetActive cascade);
- static void setActiveDeployConfiguration(Target *t, DeployConfiguration *dc, SetActive cascade);
-
- static Utils::FilePath sessionNameToFileName(const QString &session);
- static Project *startupProject();
- static Target *startupTarget();
- static BuildSystem *startupBuildSystem();
- static RunConfiguration *startupRunConfiguration();
-
- static const QList<Project *> projects();
- static bool hasProjects();
- static bool hasProject(Project *p);
-
- static bool isDefaultVirgin();
- static bool isDefaultSession(const QString &session);
-
- // Let other plugins store persistent values within the session file
- static void setValue(const QString &name, const QVariant &value);
- static QVariant value(const QString &name);
-
- // NBS rewrite projectOrder (dependency management)
- static QList<Project *> projectOrder(const Project *project = nullptr);
-
- static Project *projectForFile(const Utils::FilePath &fileName);
- static Project *projectWithProjectFilePath(const Utils::FilePath &filePath);
-
- static Utils::FilePaths projectsForSessionName(const QString &session);
-
- static void reportProjectLoadingProgress();
- static bool loadingSession();
-
-signals:
- void targetAdded(ProjectExplorer::Target *target);
- void targetRemoved(ProjectExplorer::Target *target);
- void projectAdded(ProjectExplorer::Project *project);
- void aboutToRemoveProject(ProjectExplorer::Project *project);
- void projectDisplayNameChanged(ProjectExplorer::Project *project);
- void projectRemoved(ProjectExplorer::Project *project);
-
- void startupProjectChanged(ProjectExplorer::Project *project);
-
- void aboutToUnloadSession(QString sessionName);
- void aboutToLoadSession(QString sessionName);
- void sessionLoaded(QString sessionName);
- void aboutToSaveSession();
- void dependencyChanged(ProjectExplorer::Project *a, ProjectExplorer::Project *b);
-
- void sessionRenamed(const QString &oldName, const QString &newName);
- void sessionRemoved(const QString &name);
-
- // for tests only
- void projectFinishedParsing(ProjectExplorer::Project *project);
-
-private:
- static void saveActiveMode(Utils::Id mode);
- static void configureEditor(Core::IEditor *editor, const QString &fileName);
- static void markSessionFileDirty();
- static void configureEditors(Project *project);
-};
-
-} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/target.cpp b/src/plugins/projectexplorer/target.cpp
index 8f227512ee..d2e59aab06 100644
--- a/src/plugins/projectexplorer/target.cpp
+++ b/src/plugins/projectexplorer/target.cpp
@@ -21,8 +21,8 @@
#include "projectexplorericons.h"
#include "projectexplorersettings.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "runconfiguration.h"
-#include "session.h"
#include <coreplugin/coreconstants.h>
@@ -120,10 +120,10 @@ Target::Target(Project *project, Kit *k, _constructor_tag) :
});
connect(this, &Target::parsingFinished, this, [this, project](bool success) {
- if (success && this == SessionManager::startupTarget())
+ if (success && this == ProjectManager::startupTarget())
updateDefaultRunConfigurations();
// For testing.
- emit SessionManager::instance()->projectFinishedParsing(project);
+ emit ProjectManager::instance()->projectFinishedParsing(project);
emit project->anyParsingFinished(this, success);
}, Qt::QueuedConnection); // Must wait for run configs to change their enabled state.
@@ -242,6 +242,70 @@ QString Target::activeBuildKey() const
return d->m_activeRunConfiguration->buildKey();
}
+void Target::setActiveBuildConfiguration(BuildConfiguration *bc, SetActive cascade)
+{
+ QTC_ASSERT(project(), return);
+
+ if (project()->isShuttingDown() || isShuttingDown())
+ return;
+
+ setActiveBuildConfiguration(bc);
+
+ if (!bc)
+ return;
+ if (cascade != SetActive::Cascade || !ProjectManager::isProjectConfigurationCascading())
+ return;
+
+ Id kitId = kit()->id();
+ QString name = bc->displayName(); // We match on displayname
+ for (Project *otherProject : ProjectManager::projects()) {
+ if (otherProject == project())
+ continue;
+ Target *otherTarget = otherProject->activeTarget();
+ if (!otherTarget || otherTarget->kit()->id() != kitId)
+ continue;
+
+ for (BuildConfiguration *otherBc : otherTarget->buildConfigurations()) {
+ if (otherBc->displayName() == name) {
+ otherTarget->setActiveBuildConfiguration(otherBc);
+ break;
+ }
+ }
+ }
+}
+
+void Target::setActiveDeployConfiguration(DeployConfiguration *dc, SetActive cascade)
+{
+ QTC_ASSERT(project(), return);
+
+ if (project()->isShuttingDown() || isShuttingDown())
+ return;
+
+ setActiveDeployConfiguration(dc);
+
+ if (!dc)
+ return;
+ if (cascade != SetActive::Cascade || !ProjectManager::isProjectConfigurationCascading())
+ return;
+
+ Id kitId = kit()->id();
+ QString name = dc->displayName(); // We match on displayname
+ for (Project *otherProject : ProjectManager::projects()) {
+ if (otherProject == project())
+ continue;
+ Target *otherTarget = otherProject->activeTarget();
+ if (!otherTarget || otherTarget->kit()->id() != kitId)
+ continue;
+
+ for (DeployConfiguration *otherDc : otherTarget->deployConfigurations()) {
+ if (otherDc->displayName() == name) {
+ otherTarget->setActiveDeployConfiguration(otherDc);
+ break;
+ }
+ }
+ }
+}
+
Utils::Id Target::id() const
{
return d->m_kit->id();
@@ -307,9 +371,9 @@ bool Target::removeBuildConfiguration(BuildConfiguration *bc)
if (activeBuildConfiguration() == bc) {
if (d->m_buildConfigurations.isEmpty())
- SessionManager::setActiveBuildConfiguration(this, nullptr, SetActive::Cascade);
+ setActiveBuildConfiguration(nullptr, SetActive::Cascade);
else
- SessionManager::setActiveBuildConfiguration(this, d->m_buildConfigurations.at(0), SetActive::Cascade);
+ setActiveBuildConfiguration(d->m_buildConfigurations.at(0), SetActive::Cascade);
}
emit removedBuildConfiguration(bc);
@@ -377,10 +441,9 @@ bool Target::removeDeployConfiguration(DeployConfiguration *dc)
if (activeDeployConfiguration() == dc) {
if (d->m_deployConfigurations.isEmpty())
- SessionManager::setActiveDeployConfiguration(this, nullptr, SetActive::Cascade);
+ setActiveDeployConfiguration(nullptr, SetActive::Cascade);
else
- SessionManager::setActiveDeployConfiguration(this, d->m_deployConfigurations.at(0),
- SetActive::Cascade);
+ setActiveDeployConfiguration(d->m_deployConfigurations.at(0), SetActive::Cascade);
}
ProjectExplorerPlugin::targetSelector()->removedDeployConfiguration(dc);
diff --git a/src/plugins/projectexplorer/target.h b/src/plugins/projectexplorer/target.h
index 78f0b5f3b5..aeca2fd9e0 100644
--- a/src/plugins/projectexplorer/target.h
+++ b/src/plugins/projectexplorer/target.h
@@ -26,11 +26,13 @@ class Project;
class ProjectConfigurationModel;
class RunConfiguration;
+enum class SetActive : int { Cascade, NoCascade };
+
class TargetPrivate;
class PROJECTEXPLORER_EXPORT Target : public QObject
{
- friend class SessionManager; // for setActiveBuild and setActiveDeployConfiguration
+ friend class ProjectManager; // for setActiveBuild and setActiveDeployConfiguration
Q_OBJECT
public:
@@ -109,6 +111,9 @@ public:
QString activeBuildKey() const; // Build key of active run configuaration
+ void setActiveBuildConfiguration(BuildConfiguration *bc, SetActive cascade);
+ void setActiveDeployConfiguration(DeployConfiguration *dc, SetActive cascade);
+
signals:
void targetEnabled(bool);
void iconChanged();
diff --git a/src/plugins/projectexplorer/targetsettingspanel.cpp b/src/plugins/projectexplorer/targetsettingspanel.cpp
index 6d8c81e634..d2fdc3b43a 100644
--- a/src/plugins/projectexplorer/targetsettingspanel.cpp
+++ b/src/plugins/projectexplorer/targetsettingspanel.cpp
@@ -12,9 +12,9 @@
#include "project.h"
#include "projectexplorericons.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "projectwindow.h"
#include "runsettingspropertiespage.h"
-#include "session.h"
#include "target.h"
#include "targetsetuppage.h"
#include "task.h"
@@ -281,7 +281,7 @@ public:
QFont font = parent()->data(column, role).value<QFont>();
if (TargetItem *targetItem = parent()->currentTargetItem()) {
Target *t = targetItem->target();
- if (t && t->id() == m_kitId && m_project == SessionManager::startupProject())
+ if (t && t->id() == m_kitId && m_project == ProjectManager::startupProject())
font.setBold(true);
}
return font;
@@ -334,7 +334,7 @@ public:
// Go to Run page, when on Run previously etc.
TargetItem *previousItem = parent()->currentTargetItem();
m_currentChild = previousItem ? previousItem->m_currentChild : DefaultPage;
- SessionManager::setActiveTarget(m_project, target(), SetActive::Cascade);
+ m_project->setActiveTarget(target(), SetActive::Cascade);
parent()->setData(column, QVariant::fromValue(static_cast<TreeItem *>(this)),
ItemActivatedFromBelowRole);
}
@@ -346,7 +346,7 @@ public:
int child = indexOf(data.value<TreeItem *>());
QTC_ASSERT(child != -1, return false);
m_currentChild = child; // Triggered from sub-item.
- SessionManager::setActiveTarget(m_project, target(), SetActive::Cascade);
+ m_project->setActiveTarget(target(), SetActive::Cascade);
// Propagate Build/Run selection up.
parent()->setData(column, QVariant::fromValue(static_cast<TreeItem *>(this)),
ItemActivatedFromBelowRole);
@@ -355,7 +355,7 @@ public:
if (role == ItemActivatedFromAboveRole) {
// Usually programmatic activation, e.g. after opening the Project mode.
- SessionManager::setActiveTarget(m_project, target(), SetActive::Cascade);
+ m_project->setActiveTarget(target(), SetActive::Cascade);
return true;
}
return false;
@@ -377,7 +377,7 @@ public:
= menu->addAction(Tr::tr("Enable Kit for All Projects"));
enableForAllAction->setEnabled(isSelectable);
QObject::connect(enableForAllAction, &QAction::triggered, [kit] {
- for (Project * const p : SessionManager::projects()) {
+ for (Project * const p : ProjectManager::projects()) {
if (!p->target(kit))
p->addTargetForKit(kit);
}
@@ -411,7 +411,7 @@ public:
QAction *disableForAllAction = menu->addAction(Tr::tr("Disable Kit for All Projects"));
disableForAllAction->setEnabled(isSelectable);
QObject::connect(disableForAllAction, &QAction::triggered, [kit] {
- for (Project * const p : SessionManager::projects()) {
+ for (Project * const p : ProjectManager::projects()) {
Target * const t = p->target(kit);
if (!t)
continue;
diff --git a/src/plugins/projectexplorer/targetsetuppage.cpp b/src/plugins/projectexplorer/targetsetuppage.cpp
index a17aa58c94..fd1e8a78c6 100644
--- a/src/plugins/projectexplorer/targetsetuppage.cpp
+++ b/src/plugins/projectexplorer/targetsetuppage.cpp
@@ -11,18 +11,17 @@
#include "project.h"
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
-#include "session.h"
#include "target.h"
#include "targetsetupwidget.h"
#include "task.h"
#include <coreplugin/icore.h>
-#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/wizard.h>
#include <utils/algorithm.h>
#include <utils/fancylineedit.h>
+#include <utils/process.h>
+#include <utils/qtcassert.h>
+#include <utils/wizard.h>
#include <QApplication>
#include <QCheckBox>
@@ -658,7 +657,7 @@ bool TargetSetupPage::setupProject(Project *project)
if (m_importer)
activeTarget = m_importer->preferredTarget(project->targets());
if (activeTarget)
- SessionManager::setActiveTarget(project, activeTarget, SetActive::NoCascade);
+ project->setActiveTarget(activeTarget, SetActive::NoCascade);
return true;
}
diff --git a/src/plugins/projectexplorer/targetsetupwidget.cpp b/src/plugins/projectexplorer/targetsetupwidget.cpp
index cae28fc3e1..63b61401d5 100644
--- a/src/plugins/projectexplorer/targetsetupwidget.cpp
+++ b/src/plugins/projectexplorer/targetsetupwidget.cpp
@@ -180,10 +180,8 @@ void TargetSetupWidget::manageKit()
if (!m_kit)
return;
- if (auto kitPage = KitOptionsPage::instance()) {
- kitPage->showKit(m_kit);
- Core::ICore::showOptionsDialog(Constants::KITS_SETTINGS_PAGE_ID, parentWidget());
- }
+ KitOptionsPage::showKit(m_kit);
+ Core::ICore::showOptionsDialog(Constants::KITS_SETTINGS_PAGE_ID, parentWidget());
}
void TargetSetupWidget::setProjectPath(const FilePath &projectPath)
diff --git a/src/plugins/projectexplorer/task.cpp b/src/plugins/projectexplorer/task.cpp
index dab4c7ed17..03c2a282be 100644
--- a/src/plugins/projectexplorer/task.cpp
+++ b/src/plugins/projectexplorer/task.cpp
@@ -8,8 +8,8 @@
#include "projectexplorertr.h"
#include <app/app_version.h>
+#include <texteditor/fontsettings.h>
#include <texteditor/textmark.h>
-
#include <utils/algorithm.h>
#include <utils/utilsicons.h>
#include <utils/qtcassert.h>
@@ -105,11 +105,16 @@ void Task::setFile(const Utils::FilePath &file_)
}
}
-QString Task::description() const
+QString Task::description(DescriptionTags tags) const
{
- QString desc = summary;
- if (!details.isEmpty())
- desc.append('\n').append(details.join('\n'));
+ QString desc;
+ if (tags & WithSummary)
+ desc = summary;
+ if (!details.isEmpty()) {
+ if (!desc.isEmpty())
+ desc.append('\n');
+ desc.append(details.join('\n'));
+ }
return desc;
}
@@ -120,6 +125,39 @@ QIcon Task::icon() const
return m_icon;
}
+QString Task::formattedDescription(DescriptionTags tags, const QString &extraHeading) const
+{
+ if (isNull())
+ return {};
+
+ QString text = description(tags);
+ const int offset = (tags & WithSummary) ? 0 : summary.size() + 1;
+ static const QString linkTagStartPlaceholder("__QTC_LINK_TAG_START__");
+ static const QString linkTagEndPlaceholder("__QTC_LINK_TAG_END__");
+ static const QString linkEndPlaceholder("__QTC_LINK_END__");
+ if (tags & WithLinks) {
+ for (auto formatRange = formats.crbegin(); formatRange != formats.crend(); ++formatRange) {
+ if (!formatRange->format.isAnchor())
+ continue;
+ text.insert(formatRange->start - offset + formatRange->length, linkEndPlaceholder);
+ text.insert(formatRange->start - offset, QString::fromLatin1("%1%2%3").arg(
+ linkTagStartPlaceholder, formatRange->format.anchorHref(), linkTagEndPlaceholder));
+ }
+ }
+ text = text.toHtmlEscaped();
+ if (tags & WithLinks) {
+ text.replace(linkEndPlaceholder, "</a>");
+ text.replace(linkTagStartPlaceholder, "<a href=\"");
+ text.replace(linkTagEndPlaceholder, "\">");
+ }
+ const QString htmlExtraHeading = extraHeading.isEmpty()
+ ? QString()
+ : QString::fromUtf8("<b>%1</b><br/>").arg(extraHeading);
+ return QString::fromUtf8("<html><body>%1<code style=\"white-space:pre;font-family:%2\">"
+ "%3</code></body></html>")
+ .arg(htmlExtraHeading, TextEditor::FontSettings::defaultFixedFontFamily(), text);
+}
+
//
// functions
//
diff --git a/src/plugins/projectexplorer/task.h b/src/plugins/projectexplorer/task.h
index f38302f90f..830cea7939 100644
--- a/src/plugins/projectexplorer/task.h
+++ b/src/plugins/projectexplorer/task.h
@@ -38,6 +38,9 @@ public:
};
using Options = char;
+ enum DescriptionTag { WithSummary = 1, WithLinks = 2 };
+ using DescriptionTags = QFlags<DescriptionTag>;
+
Task() = default;
Task(TaskType type, const QString &description,
const Utils::FilePath &file, int line, Utils::Id category,
@@ -49,8 +52,9 @@ public:
bool isNull() const;
void clear();
void setFile(const Utils::FilePath &file);
- QString description() const;
+ QString description(DescriptionTags tags = WithSummary) const;
QIcon icon() const;
+ QString formattedDescription(DescriptionTags tags, const QString &extraHeading = {}) const;
friend PROJECTEXPLORER_EXPORT bool operator==(const Task &t1, const Task &t2);
friend PROJECTEXPLORER_EXPORT bool operator<(const Task &a, const Task &b);
diff --git a/src/plugins/projectexplorer/taskfile.cpp b/src/plugins/projectexplorer/taskfile.cpp
index 6375d12ee7..c967521d89 100644
--- a/src/plugins/projectexplorer/taskfile.cpp
+++ b/src/plugins/projectexplorer/taskfile.cpp
@@ -6,20 +6,22 @@
#include "projectexplorer.h"
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
-#include "session.h"
#include "taskhub.h"
#include <coreplugin/documentmanager.h>
#include <coreplugin/icore.h>
+#include <coreplugin/messagemanager.h>
+#include <coreplugin/session.h>
#include <utils/algorithm.h>
#include <utils/filepath.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QAction>
#include <QMessageBox>
+using namespace Core;
using namespace Utils;
namespace ProjectExplorer {
@@ -147,6 +149,10 @@ static bool parseTaskFile(QString *errorString, const FilePath &name)
}
description = unescape(description);
+ if (description.trimmed().isEmpty()) {
+ MessageManager::writeFlashing(Tr::tr("Ignoring invalid task (no text)."));
+ continue;
+ }
TaskHub::addTask(Task(type, description, FilePath::fromUserInput(file), line,
Constants::TASK_CATEGORY_TASKLIST_ID));
}
diff --git a/src/plugins/projectexplorer/taskhub.cpp b/src/plugins/projectexplorer/taskhub.cpp
index da839e89f1..58e797de80 100644
--- a/src/plugins/projectexplorer/taskhub.cpp
+++ b/src/plugins/projectexplorer/taskhub.cpp
@@ -51,13 +51,9 @@ public:
: Tr::tr("Warning"));
setPriority(task.type == Task::Error ? TextEditor::TextMark::NormalPriority
: TextEditor::TextMark::LowPriority);
- if (task.category == Constants::TASK_CATEGORY_COMPILE) {
- setToolTip("<html><body><b>" + Tr::tr("Build Issue")
- + "</b><br/><code style=\"white-space:pre;font-family:monospace\">"
- + task.description().toHtmlEscaped() + "</code></body></html>");
- } else {
- setToolTip(task.description());
- }
+ setToolTip(task.formattedDescription({Task::WithSummary | Task::WithLinks},
+ task.category == Constants::TASK_CATEGORY_COMPILE
+ ? Tr::tr("Build Issue") : QString()));
setIcon(task.icon());
setVisible(!task.icon().isNull());
}
diff --git a/src/plugins/projectexplorer/taskmodel.cpp b/src/plugins/projectexplorer/taskmodel.cpp
index b8f7d72306..11591e707d 100644
--- a/src/plugins/projectexplorer/taskmodel.cpp
+++ b/src/plugins/projectexplorer/taskmodel.cpp
@@ -210,58 +210,82 @@ void TaskModel::clearTasks(Utils::Id categoryId)
QModelIndex TaskModel::index(int row, int column, const QModelIndex &parent) const
{
if (parent.isValid())
- return QModelIndex();
+ return createIndex(row, column, quintptr(parent.row() + 1));
return createIndex(row, column);
}
QModelIndex TaskModel::parent(const QModelIndex &child) const
{
- Q_UNUSED(child)
- return QModelIndex();
+ if (child.internalId())
+ return index(child.internalId() - 1, 0);
+ return {};
}
int TaskModel::rowCount(const QModelIndex &parent) const
{
- return parent.isValid() ? 0 : m_tasks.count();
+ if (!parent.isValid())
+ return m_tasks.count();
+ if (parent.column() != 0)
+ return 0;
+ return task(parent).details.isEmpty() ? 0 : 1;
}
int TaskModel::columnCount(const QModelIndex &parent) const
{
- return parent.isValid() ? 0 : 1;
+ return parent.isValid() ? 1 : 2;
}
QVariant TaskModel::data(const QModelIndex &index, int role) const
{
- int row = index.row();
- if (!index.isValid() || row < 0 || row >= m_tasks.count() || index.column() != 0)
- return QVariant();
-
- if (role == TaskModel::File)
- return m_tasks.at(index.row()).file.toString();
- else if (role == TaskModel::Line)
- return m_tasks.at(index.row()).line;
- else if (role == TaskModel::MovedLine)
- return m_tasks.at(index.row()).movedLine;
- else if (role == TaskModel::Description)
- return m_tasks.at(index.row()).description();
- else if (role == TaskModel::FileNotFound)
- return m_fileNotFound.value(m_tasks.at(index.row()).file.toString());
- else if (role == TaskModel::Type)
- return (int)m_tasks.at(index.row()).type;
- else if (role == TaskModel::Category)
- return m_tasks.at(index.row()).category.uniqueIdentifier();
- else if (role == TaskModel::Icon)
- return m_tasks.at(index.row()).icon();
- else if (role == TaskModel::Task_t)
- return QVariant::fromValue(task(index));
- return QVariant();
+ if (!index.isValid() || index.row() < 0 || index.row() >= rowCount(index.parent())
+ || index.column() >= columnCount(index.parent())) {
+ return {};
+ }
+
+ if (index.internalId()) {
+ const Task &task = m_tasks.at(index.internalId() - 1);
+ if (role != Qt::DisplayRole)
+ return {};
+ return task.formattedDescription(Task::WithLinks);
+ }
+
+ static const auto lineString = [](const Task &task) {
+ QString file = task.file.fileName();
+ const int line = task.movedLine > 0 ? task.movedLine : task.line;
+ if (line > 0)
+ file.append(':').append(QString::number(line));
+ return file;
+ };
+
+ const Task &task = m_tasks.at(index.row());
+ if (index.column() == 1) {
+ if (role == Qt::DisplayRole)
+ return lineString(task);
+ if (role == Qt::ToolTipRole)
+ return task.file.toUserOutput();
+ return {};
+ }
+
+ switch (role) {
+ case Qt::DecorationRole:
+ return task.icon();
+ case Qt::DisplayRole:
+ return task.summary;
+ case TaskModel::Description:
+ return task.description();
+ case TaskModel::Type:
+ return int(task.type);
+ }
+ return {};
}
Task TaskModel::task(const QModelIndex &index) const
{
int row = index.row();
- if (!index.isValid() || row < 0 || row >= m_tasks.count())
+ if (!index.isValid() || row < 0 || row >= m_tasks.count() || index.internalId()
+ || index.column() > 0) {
return Task();
+ }
return m_tasks.at(row);
}
@@ -387,7 +411,8 @@ void TaskFilterModel::updateFilterProperties(
bool TaskFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
- Q_UNUSED(source_parent)
+ if (source_parent.isValid())
+ return true;
return filterAcceptsTask(taskModel()->tasks().at(source_row));
}
diff --git a/src/plugins/projectexplorer/taskmodel.h b/src/plugins/projectexplorer/taskmodel.h
index e10833f613..5fa37c02d1 100644
--- a/src/plugins/projectexplorer/taskmodel.h
+++ b/src/plugins/projectexplorer/taskmodel.h
@@ -44,7 +44,7 @@ public:
int sizeOfLineNumber(const QFont &font);
void setFileNotFound(const QModelIndex &index, bool b);
- enum Roles { File = Qt::UserRole, Line, MovedLine, Description, FileNotFound, Type, Category, Icon, Task_t };
+ enum Roles { Description = Qt::UserRole, Type};
int taskCount(Utils::Id categoryId);
int errorTaskCount(Utils::Id categoryId);
diff --git a/src/plugins/projectexplorer/taskwindow.cpp b/src/plugins/projectexplorer/taskwindow.cpp
index 52e3899d65..9ae93c0bbf 100644
--- a/src/plugins/projectexplorer/taskwindow.cpp
+++ b/src/plugins/projectexplorer/taskwindow.cpp
@@ -6,7 +6,6 @@
#include "itaskhandler.h"
#include "projectexplorericons.h"
#include "projectexplorertr.h"
-#include "session.h"
#include "task.h"
#include "taskhub.h"
#include "taskmodel.h"
@@ -17,32 +16,38 @@
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/find/itemviewfind.h>
-#include <coreplugin/icore.h>
#include <coreplugin/icontext.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/session.h>
#include <utils/algorithm.h>
#include <utils/fileinprojectfinder.h>
+#include <utils/hostosinfo.h>
#include <utils/itemviews.h>
#include <utils/outputformatter.h>
#include <utils/qtcassert.h>
#include <utils/stylehelper.h>
#include <utils/theme/theme.h>
+#include <utils/tooltip/tooltip.h>
#include <utils/utilsicons.h>
+#include <QAbstractTextDocumentLayout>
+#include <QApplication>
#include <QDir>
+#include <QLabel>
+#include <QMenu>
#include <QPainter>
+#include <QScrollBar>
#include <QStyledItemDelegate>
-#include <QMenu>
+#include <QTextDocument>
#include <QToolButton>
-#include <QScrollBar>
+#include <QVBoxLayout>
+using namespace Core;
using namespace Utils;
-namespace {
-const int ELLIPSIS_GRADIENT_WIDTH = 16;
const char SESSION_FILTER_CATEGORIES[] = "TaskWindow.Categories";
const char SESSION_FILTER_WARNINGS[] = "TaskWindow.IncludeWarnings";
-}
namespace ProjectExplorer {
@@ -84,195 +89,42 @@ bool ITaskHandler::canHandle(const Tasks &tasks) const
namespace Internal {
-class TaskView : public ListView
+class TaskView : public TreeView
{
public:
- TaskView(QWidget *parent = nullptr);
- ~TaskView() override;
+ TaskView();
+ void resizeColumns();
private:
void resizeEvent(QResizeEvent *e) override;
+ void keyReleaseEvent(QKeyEvent *e) override;
+ bool event(QEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
- void mouseReleaseEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
+ void mouseReleaseEvent(QMouseEvent *e) override;
- Link locationForPos(const QPoint &pos);
+ QString anchorAt(const QPoint &pos);
+ void showToolTip(const Task &task, const QPoint &pos);
- bool m_linksActive = true;
- Qt::MouseButton m_mouseButtonPressed = Qt::NoButton;
+ QString m_hoverAnchor;
+ QString m_clickAnchor;
};
class TaskDelegate : public QStyledItemDelegate
{
- Q_OBJECT
-
- friend class TaskView; // for using Positions::minimumSize()
-
public:
- TaskDelegate(QObject * parent = nullptr);
- ~TaskDelegate() override;
- void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
- QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
-
- // TaskView uses this method if the size of the taskview changes
- void emitSizeHintChanged(const QModelIndex &index);
-
- void currentChanged(const QModelIndex &current, const QModelIndex &previous);
-
- QString hrefForPos(const QPointF &pos);
+ using QStyledItemDelegate::QStyledItemDelegate;
+ QTextDocument &doc() { return m_doc; }
private:
- void generateGradientPixmap(int width, int height, QColor color, bool selected) const;
-
- mutable int m_cachedHeight = 0;
- mutable QFont m_cachedFont;
- mutable QList<QPair<QRectF, QString>> m_hrefs;
-
- /*
- Collapsed:
- +----------------------------------------------------------------------------------------------------+
- | TASKICONAREA TEXTAREA FILEAREA LINEAREA |
- +----------------------------------------------------------------------------------------------------+
-
- Expanded:
- +----------------------------------------------------------------------------------------------------+
- | TASKICONICON TEXTAREA FILEAREA LINEAREA |
- | more text -------------------------------------------------------------------------> |
- +----------------------------------------------------------------------------------------------------+
- */
- class Positions
- {
- public:
- Positions(const QStyleOptionViewItem &options, TaskModel *model) :
- m_totalWidth(options.rect.width()),
- m_maxFileLength(model->sizeOfFile(options.font)),
- m_maxLineLength(model->sizeOfLineNumber(options.font)),
- m_realFileLength(m_maxFileLength),
- m_top(options.rect.top()),
- m_bottom(options.rect.bottom())
- {
- int flexibleArea = lineAreaLeft() - textAreaLeft() - ITEM_SPACING;
- if (m_maxFileLength > flexibleArea / 2)
- m_realFileLength = flexibleArea / 2;
- m_fontHeight = QFontMetrics(options.font).height();
- }
-
- int top() const { return m_top + ITEM_MARGIN; }
- int left() const { return ITEM_MARGIN; }
- int right() const { return m_totalWidth - ITEM_MARGIN; }
- int bottom() const { return m_bottom; }
- int firstLineHeight() const { return m_fontHeight + 1; }
- static int minimumHeight() { return taskIconHeight() + 2 * ITEM_MARGIN; }
-
- int taskIconLeft() const { return left(); }
- static int taskIconWidth() { return TASK_ICON_SIZE; }
- static int taskIconHeight() { return TASK_ICON_SIZE; }
- int taskIconRight() const { return taskIconLeft() + taskIconWidth(); }
- QRect taskIcon() const { return QRect(taskIconLeft(), top(), taskIconWidth(), taskIconHeight()); }
-
- int textAreaLeft() const { return taskIconRight() + ITEM_SPACING; }
- int textAreaWidth() const { return textAreaRight() - textAreaLeft(); }
- int textAreaRight() const { return fileAreaLeft() - ITEM_SPACING; }
- QRect textArea() const { return QRect(textAreaLeft(), top(), textAreaWidth(), firstLineHeight()); }
-
- int fileAreaLeft() const { return fileAreaRight() - fileAreaWidth(); }
- int fileAreaWidth() const { return m_realFileLength; }
- int fileAreaRight() const { return lineAreaLeft() - ITEM_SPACING; }
- QRect fileArea() const { return QRect(fileAreaLeft(), top(), fileAreaWidth(), firstLineHeight()); }
-
- int lineAreaLeft() const { return lineAreaRight() - lineAreaWidth(); }
- int lineAreaWidth() const { return m_maxLineLength; }
- int lineAreaRight() const { return right(); }
- QRect lineArea() const { return QRect(lineAreaLeft(), top(), lineAreaWidth(), firstLineHeight()); }
-
- private:
- int m_totalWidth;
- int m_maxFileLength;
- int m_maxLineLength;
- int m_realFileLength;
- int m_top;
- int m_bottom;
- int m_fontHeight;
-
- static const int TASK_ICON_SIZE = 16;
- static const int ITEM_MARGIN = 2;
- static const int ITEM_SPACING = 2 * ITEM_MARGIN;
- };
-};
-
-TaskView::TaskView(QWidget *parent)
- : ListView(parent)
-{
- setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
- setMouseTracking(true);
- setAutoScroll(false); // QTCREATORBUG-25101
-
- QFontMetrics fm(font());
- int vStepSize = fm.height() + 3;
- if (vStepSize < TaskDelegate::Positions::minimumHeight())
- vStepSize = TaskDelegate::Positions::minimumHeight();
-
- verticalScrollBar()->setSingleStep(vStepSize);
-}
-
-TaskView::~TaskView() = default;
-
-void TaskView::resizeEvent(QResizeEvent *e)
-{
- Q_UNUSED(e)
- static_cast<TaskDelegate *>(itemDelegate())->emitSizeHintChanged(selectionModel()->currentIndex());
-}
-
-void TaskView::mousePressEvent(QMouseEvent *e)
-{
- m_mouseButtonPressed = e->button();
- ListView::mousePressEvent(e);
-}
-
-void TaskView::mouseReleaseEvent(QMouseEvent *e)
-{
- if (m_linksActive && m_mouseButtonPressed == Qt::LeftButton) {
- const Link loc = locationForPos(e->pos());
- if (!loc.targetFilePath.isEmpty()) {
- Core::EditorManager::openEditorAt(loc, {},
- Core::EditorManager::SwitchSplitIfAlreadyVisible);
- }
- }
-
- // Mouse was released, activate links again
- m_linksActive = true;
- m_mouseButtonPressed = Qt::NoButton;
- ListView::mouseReleaseEvent(e);
-}
-
-void TaskView::mouseMoveEvent(QMouseEvent *e)
-{
- // Cursor was dragged, deactivate links
- if (m_mouseButtonPressed != Qt::NoButton)
- m_linksActive = false;
-
- viewport()->setCursor(m_linksActive && !locationForPos(e->pos()).targetFilePath.isEmpty()
- ? Qt::PointingHandCursor : Qt::ArrowCursor);
- ListView::mouseMoveEvent(e);
-}
+ void paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override;
+ QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
-Link TaskView::locationForPos(const QPoint &pos)
-{
- const auto delegate = qobject_cast<TaskDelegate *>(itemDelegate(indexAt(pos)));
- if (!delegate)
- return {};
- OutputFormatter formatter;
- Link loc;
- connect(&formatter, &OutputFormatter::openInEditorRequested, this, [&loc](const Link &link) {
- loc = link;
- });
+ bool needsSpecialHandling(const QModelIndex &index) const;
- const QString href = delegate->hrefForPos(pos);
- if (!href.isEmpty())
- formatter.handleLink(href);
- return loc;
-}
+ mutable QTextDocument m_doc;
+};
/////
// TaskWindow
@@ -289,9 +141,8 @@ public:
Internal::TaskModel *m_model;
Internal::TaskFilterModel *m_filter;
- Internal::TaskView *m_listview;
+ TaskView m_treeView;
Core::IContext *m_taskWindowContext;
- QMenu *m_contextMenu;
QMap<const QAction *, ITaskHandler *> m_actionToHandlerMap;
ITaskHandler *m_defaultHandler = nullptr;
QToolButton *m_filterWarningsButton;
@@ -318,45 +169,45 @@ TaskWindow::TaskWindow() : d(std::make_unique<TaskWindowPrivate>())
{
d->m_model = new Internal::TaskModel(this);
d->m_filter = new Internal::TaskFilterModel(d->m_model);
- d->m_listview = new Internal::TaskView;
+ d->m_filter->setAutoAcceptChildRows(true);
auto agg = new Aggregation::Aggregate;
- agg->add(d->m_listview);
- agg->add(new Core::ItemViewFind(d->m_listview, TaskModel::Description));
-
- d->m_listview->setModel(d->m_filter);
- d->m_listview->setFrameStyle(QFrame::NoFrame);
- d->m_listview->setWindowTitle(displayName());
- d->m_listview->setSelectionMode(QAbstractItemView::ExtendedSelection);
- auto *tld = new Internal::TaskDelegate(this);
- d->m_listview->setItemDelegate(tld);
- d->m_listview->setWindowIcon(Icons::WINDOW.icon());
- d->m_listview->setContextMenuPolicy(Qt::ActionsContextMenu);
- d->m_listview->setAttribute(Qt::WA_MacShowFocusRect, false);
-
- d->m_taskWindowContext = new Core::IContext(d->m_listview);
- d->m_taskWindowContext->setWidget(d->m_listview);
+ agg->add(&d->m_treeView);
+ agg->add(new Core::ItemViewFind(&d->m_treeView, TaskModel::Description));
+
+ d->m_treeView.setHeaderHidden(true);
+ d->m_treeView.setExpandsOnDoubleClick(false);
+ d->m_treeView.setAlternatingRowColors(true);
+ d->m_treeView.setTextElideMode(Qt::ElideMiddle);
+ d->m_treeView.setItemDelegate(new TaskDelegate(this));
+ d->m_treeView.setModel(d->m_filter);
+ d->m_treeView.setFrameStyle(QFrame::NoFrame);
+ d->m_treeView.setWindowTitle(displayName());
+ d->m_treeView.setSelectionMode(QAbstractItemView::ExtendedSelection);
+ d->m_treeView.setWindowIcon(Icons::WINDOW.icon());
+ d->m_treeView.setContextMenuPolicy(Qt::ActionsContextMenu);
+ d->m_treeView.setAttribute(Qt::WA_MacShowFocusRect, false);
+ d->m_treeView.resizeColumns();
+
+ d->m_taskWindowContext = new Core::IContext(&d->m_treeView);
+ d->m_taskWindowContext->setWidget(&d->m_treeView);
d->m_taskWindowContext->setContext(Core::Context(Core::Constants::C_PROBLEM_PANE));
Core::ICore::addContextObject(d->m_taskWindowContext);
- connect(d->m_listview->selectionModel(), &QItemSelectionModel::currentChanged,
- tld, &TaskDelegate::currentChanged);
- connect(d->m_listview->selectionModel(), &QItemSelectionModel::currentChanged,
- this, [this](const QModelIndex &index) { d->m_listview->scrollTo(index); });
- connect(d->m_listview, &QAbstractItemView::activated,
+ connect(d->m_treeView.selectionModel(), &QItemSelectionModel::currentChanged,
+ this, [this](const QModelIndex &index) { d->m_treeView.scrollTo(index); });
+ connect(&d->m_treeView, &QAbstractItemView::activated,
this, &TaskWindow::triggerDefaultHandler);
- connect(d->m_listview->selectionModel(), &QItemSelectionModel::selectionChanged,
+ connect(d->m_treeView.selectionModel(), &QItemSelectionModel::selectionChanged,
this, [this] {
- const Tasks tasks = d->m_filter->tasks(d->m_listview->selectionModel()->selectedIndexes());
+ const Tasks tasks = d->m_filter->tasks(d->m_treeView.selectionModel()->selectedIndexes());
for (QAction * const action : std::as_const(d->m_actions)) {
ITaskHandler * const h = d->handler(action);
action->setEnabled(h && h->canHandle(tasks));
}
});
- d->m_contextMenu = new QMenu(d->m_listview);
-
- d->m_listview->setContextMenuPolicy(Qt::ActionsContextMenu);
+ d->m_treeView.setContextMenuPolicy(Qt::ActionsContextMenu);
d->m_filterWarningsButton = createFilterButton(
Utils::Icons::WARNING_TOOLBAR.icon(),
@@ -365,7 +216,7 @@ TaskWindow::TaskWindow() : d(std::make_unique<TaskWindowPrivate>())
d->m_categoriesButton = new QToolButton;
d->m_categoriesButton->setIcon(Utils::Icons::FILTER.icon());
d->m_categoriesButton->setToolTip(Tr::tr("Filter by categories"));
- d->m_categoriesButton->setProperty("noArrow", true);
+ d->m_categoriesButton->setProperty(StyleHelper::C_NO_ARROW, true);
d->m_categoriesButton->setPopupMode(QToolButton::InstantPopup);
d->m_categoriesMenu = new QMenu(d->m_categoriesButton);
@@ -411,7 +262,6 @@ TaskWindow::TaskWindow() : d(std::make_unique<TaskWindowPrivate>())
TaskWindow::~TaskWindow()
{
delete d->m_filterWarningsButton;
- delete d->m_listview;
delete d->m_filter;
delete d->m_model;
}
@@ -435,7 +285,7 @@ void TaskWindow::delayedInitialization()
connect(action, &QAction::triggered, this, [this, action] {
ITaskHandler *h = d->handler(action);
if (h)
- h->handle(d->m_filter->tasks(d->m_listview->selectionModel()->selectedIndexes()));
+ h->handle(d->m_filter->tasks(d->m_treeView.selectionModel()->selectedIndexes()));
});
d->m_actions << action;
@@ -445,7 +295,7 @@ void TaskWindow::delayedInitialization()
Core::ActionManager::registerAction(action, id, d->m_taskWindowContext->context(), true);
action = cmd->action();
}
- d->m_listview->addAction(action);
+ d->m_treeView.addAction(action);
}
}
@@ -461,7 +311,7 @@ QString TaskWindow::displayName() const
QWidget *TaskWindow::outputWidget(QWidget *)
{
- return d->m_listview;
+ return &d->m_treeView;
}
void TaskWindow::clearTasks(Id categoryId)
@@ -565,7 +415,7 @@ void TaskWindow::showTask(const Task &task)
int sourceRow = d->m_model->rowForTask(task);
QModelIndex sourceIdx = d->m_model->index(sourceRow, 0);
QModelIndex filterIdx = d->m_filter->mapFromSource(sourceIdx);
- d->m_listview->setCurrentIndex(filterIdx);
+ d->m_treeView.setCurrentIndex(filterIdx);
popup(Core::IOutputPane::ModeSwitch);
}
@@ -582,7 +432,10 @@ void TaskWindow::triggerDefaultHandler(const QModelIndex &index)
if (!index.isValid() || !d->m_defaultHandler)
return;
- Task task(d->m_filter->task(index));
+ QModelIndex taskIndex = index;
+ if (index.parent().isValid())
+ taskIndex = index.parent();
+ Task task(d->m_filter->task(taskIndex));
if (task.isNull())
return;
@@ -599,7 +452,7 @@ void TaskWindow::triggerDefaultHandler(const QModelIndex &index)
d->m_defaultHandler->handle(task);
} else {
if (!task.file.exists())
- d->m_model->setFileNotFound(index, true);
+ d->m_model->setFileNotFound(taskIndex, true);
}
}
@@ -665,7 +518,7 @@ void TaskWindow::clearContents()
bool TaskWindow::hasFocus() const
{
- return d->m_listview->window()->focusWidget() == d->m_listview;
+ return d->m_treeView.window()->focusWidget() == &d->m_treeView;
}
bool TaskWindow::canFocus() const
@@ -676,9 +529,13 @@ bool TaskWindow::canFocus() const
void TaskWindow::setFocus()
{
if (d->m_filter->rowCount()) {
- d->m_listview->setFocus();
- if (d->m_listview->currentIndex() == QModelIndex())
- d->m_listview->setCurrentIndex(d->m_filter->index(0,0, QModelIndex()));
+ d->m_treeView.setFocus();
+ if (!d->m_treeView.currentIndex().isValid())
+ d->m_treeView.setCurrentIndex(d->m_filter->index(0,0, QModelIndex()));
+ if (d->m_treeView.selectionModel()->selection().isEmpty()) {
+ d->m_treeView.selectionModel()->setCurrentIndex(d->m_treeView.currentIndex(),
+ QItemSelectionModel::Select);
+ }
}
}
@@ -696,7 +553,7 @@ void TaskWindow::goToNext()
{
if (!canNext())
return;
- QModelIndex startIndex = d->m_listview->currentIndex();
+ QModelIndex startIndex = d->m_treeView.currentIndex();
QModelIndex currentIndex = startIndex;
if (startIndex.isValid()) {
@@ -711,7 +568,7 @@ void TaskWindow::goToNext()
} else {
currentIndex = d->m_filter->index(0, 0);
}
- d->m_listview->setCurrentIndex(currentIndex);
+ d->m_treeView.setCurrentIndex(currentIndex);
triggerDefaultHandler(currentIndex);
}
@@ -719,7 +576,7 @@ void TaskWindow::goToPrev()
{
if (!canPrevious())
return;
- QModelIndex startIndex = d->m_listview->currentIndex();
+ QModelIndex startIndex = d->m_treeView.currentIndex();
QModelIndex currentIndex = startIndex;
if (startIndex.isValid()) {
@@ -734,7 +591,7 @@ void TaskWindow::goToPrev()
} else {
currentIndex = d->m_filter->index(0, 0);
}
- d->m_listview->setCurrentIndex(currentIndex);
+ d->m_treeView.setCurrentIndex(currentIndex);
triggerDefaultHandler(currentIndex);
}
@@ -749,268 +606,167 @@ bool TaskWindow::canNavigate() const
return true;
}
-/////
-// Delegate
-/////
-
-TaskDelegate::TaskDelegate(QObject *parent) :
- QStyledItemDelegate(parent)
-{ }
-
-TaskDelegate::~TaskDelegate() = default;
-
-QSize TaskDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
+void TaskDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
{
- QStyleOptionViewItem opt = option;
- initStyleOption(&opt, index);
-
- auto view = qobject_cast<const QAbstractItemView *>(opt.widget);
- const bool current = view->selectionModel()->currentIndex() == index;
- QSize s;
- s.setWidth(option.rect.width());
-
- if (!current && option.font == m_cachedFont && m_cachedHeight > 0) {
- s.setHeight(m_cachedHeight);
- return s;
+ if (!needsSpecialHandling(index)) {
+ QStyledItemDelegate::paint(painter, option, index);
+ return;
}
- QFontMetrics fm(option.font);
- int fontHeight = fm.height();
- int fontLeading = fm.leading();
-
- auto model = static_cast<TaskFilterModel *>(view->model())->taskModel();
- Positions positions(option, model);
-
- if (current) {
- QString description = index.data(TaskModel::Description).toString();
- // Layout the description
- int leading = fontLeading;
- int height = 0;
- description.replace(QLatin1Char('\n'), QChar::LineSeparator);
- QTextLayout tl(description);
- tl.setFormats(index.data(TaskModel::Task_t).value<Task>().formats);
- tl.beginLayout();
- while (true) {
- QTextLine line = tl.createLine();
- if (!line.isValid())
- break;
- line.setLineWidth(positions.textAreaWidth());
- height += leading;
- line.setPosition(QPoint(0, height));
- height += static_cast<int>(line.height());
- }
- tl.endLayout();
+ QStyleOptionViewItem options = option;
+ initStyleOption(&options, index);
- s.setHeight(height + leading + fontHeight + 3);
- } else {
- s.setHeight(fontHeight + 3);
+ painter->save();
+ m_doc.setHtml(options.text);
+ options.text = "";
+ options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter);
+ painter->translate(options.rect.left(), options.rect.top());
+ QRect clip(0, 0, options.rect.width(), options.rect.height());
+ QAbstractTextDocumentLayout::PaintContext paintContext;
+ paintContext.palette = options.palette;
+ painter->setClipRect(clip);
+ paintContext.clip = clip;
+ if (qobject_cast<const QAbstractItemView *>(options.widget)
+ ->selectionModel()->isSelected(index)) {
+ QAbstractTextDocumentLayout::Selection selection;
+ selection.cursor = QTextCursor(&m_doc);
+ selection.cursor.select(QTextCursor::Document);
+ selection.format.setBackground(options.palette.brush(QPalette::Highlight));
+ selection.format.setForeground(options.palette.brush(QPalette::HighlightedText));
+ paintContext.selections << selection;
}
- if (s.height() < Positions::minimumHeight())
- s.setHeight(Positions::minimumHeight());
+ m_doc.documentLayout()->draw(painter, paintContext);
+ painter->restore();
+}
- if (!current) {
- m_cachedHeight = s.height();
- m_cachedFont = option.font;
- }
+QSize TaskDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ if (!needsSpecialHandling(index))
+ return QStyledItemDelegate::sizeHint(option, index);
- return s;
+ QStyleOptionViewItem options = option;
+ initStyleOption(&options, index);
+ m_doc.setHtml(options.text);
+ m_doc.setTextWidth(options.rect.width());
+ return QSize(m_doc.idealWidth(), m_doc.size().height());
}
-void TaskDelegate::emitSizeHintChanged(const QModelIndex &index)
+bool TaskDelegate::needsSpecialHandling(const QModelIndex &index) const
{
- emit sizeHintChanged(index);
+ QModelIndex sourceIndex = index;
+ if (const auto proxyModel = qobject_cast<const QAbstractProxyModel *>(index.model()))
+ sourceIndex = proxyModel->mapToSource(index);
+ return sourceIndex.internalId();
}
-void TaskDelegate::currentChanged(const QModelIndex &current, const QModelIndex &previous)
+TaskView::TaskView()
{
- m_hrefs.clear();
- emit sizeHintChanged(current);
- emit sizeHintChanged(previous);
+ setMouseTracking(true);
+ setVerticalScrollMode(ScrollPerPixel);
}
-QString TaskDelegate::hrefForPos(const QPointF &pos)
+void TaskView::resizeColumns()
{
- for (const auto &link : std::as_const(m_hrefs)) {
- if (link.first.contains(pos))
- return link.second;
- }
- return {};
+ setColumnWidth(0, width() * 0.85);
+ setColumnWidth(1, width() * 0.15);
}
-void TaskDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
+void TaskView::resizeEvent(QResizeEvent *e)
{
- QStyleOptionViewItem opt = option;
- initStyleOption(&opt, index);
- painter->save();
+ TreeView::resizeEvent(e);
+ resizeColumns();
+}
- QFontMetrics fm(opt.font);
- QColor backgroundColor;
- QColor textColor;
+void TaskView::mousePressEvent(QMouseEvent *e)
+{
+ m_clickAnchor = anchorAt(e->pos());
+ if (m_clickAnchor.isEmpty())
+ TreeView::mousePressEvent(e);
+}
- auto view = qobject_cast<const QAbstractItemView *>(opt.widget);
- const bool selected = view->selectionModel()->isSelected(index);
- const bool current = view->selectionModel()->currentIndex() == index;
+void TaskView::mouseMoveEvent(QMouseEvent *e)
+{
+ const QString anchor = anchorAt(e->pos());
+ if (m_clickAnchor != anchor)
+ m_clickAnchor.clear();
+ if (m_hoverAnchor != anchor) {
+ m_hoverAnchor = anchor;
+ if (!m_hoverAnchor.isEmpty())
+ setCursor(Qt::PointingHandCursor);
+ else
+ unsetCursor();
+ }
+}
- if (selected) {
- painter->setBrush(opt.palette.highlight().color());
- backgroundColor = opt.palette.highlight().color();
- } else {
- painter->setBrush(opt.palette.window().color());
- backgroundColor = opt.palette.window().color();
+void TaskView::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (m_clickAnchor.isEmpty() || e->button() == Qt::RightButton) {
+ TreeView::mouseReleaseEvent(e);
+ return;
}
- painter->setPen(Qt::NoPen);
- painter->drawRect(opt.rect);
- // Set Text Color
- if (selected)
- textColor = opt.palette.highlightedText().color();
- else
- textColor = opt.palette.text().color();
-
- painter->setPen(textColor);
-
- auto model = static_cast<TaskFilterModel *>(view->model())->taskModel();
- Positions positions(opt, model);
-
- // Paint TaskIconArea:
- QIcon icon = index.data(TaskModel::Icon).value<QIcon>();
- painter->drawPixmap(positions.left(), positions.top(),
- icon.pixmap(Positions::taskIconWidth(), Positions::taskIconHeight()));
-
- // Paint TextArea:
- if (!current) {
- // in small mode we lay out differently
- QString bottom = index.data(TaskModel::Description).toString().split(QLatin1Char('\n')).first();
- painter->setClipRect(positions.textArea());
- painter->drawText(positions.textAreaLeft(), positions.top() + fm.ascent(), bottom);
- if (fm.horizontalAdvance(bottom) > positions.textAreaWidth()) {
- // draw a gradient to mask the text
- int gradientStart = positions.textAreaRight() - ELLIPSIS_GRADIENT_WIDTH + 1;
- QLinearGradient lg(gradientStart, 0, gradientStart + ELLIPSIS_GRADIENT_WIDTH, 0);
- lg.setColorAt(0, Qt::transparent);
- lg.setColorAt(1, backgroundColor);
- painter->fillRect(gradientStart, positions.top(), ELLIPSIS_GRADIENT_WIDTH, positions.firstLineHeight(), lg);
- }
- } else {
- // Description
- QString description = index.data(TaskModel::Description).toString();
- // Layout the description
- int leading = fm.leading();
- int height = 0;
- description.replace(QLatin1Char('\n'), QChar::LineSeparator);
- QTextLayout tl(description);
- QVector<QTextLayout::FormatRange> formats = index.data(TaskModel::Task_t).value<Task>().formats;
- for (QTextLayout::FormatRange &format : formats)
- format.format.setForeground(textColor);
- tl.setFormats(formats);
- tl.beginLayout();
- while (true) {
- QTextLine line = tl.createLine();
- if (!line.isValid())
- break;
- line.setLineWidth(positions.textAreaWidth());
- height += leading;
- line.setPosition(QPoint(0, height));
- height += static_cast<int>(line.height());
- }
- tl.endLayout();
- const QPoint indexPos = view->visualRect(index).topLeft();
- tl.draw(painter, QPoint(positions.textAreaLeft(), positions.top()));
- m_hrefs.clear();
- for (const auto &range : tl.formats()) {
- if (!range.format.isAnchor())
- continue;
- const QTextLine &firstLinkLine = tl.lineForTextPosition(range.start);
- const QTextLine &lastLinkLine = tl.lineForTextPosition(range.start + range.length - 1);
- for (int i = firstLinkLine.lineNumber(); i <= lastLinkLine.lineNumber(); ++i) {
- const QTextLine &linkLine = tl.lineAt(i);
- if (!linkLine.isValid())
- break;
- const QPointF linePos = linkLine.position();
- const int linkStartPos = i == firstLinkLine.lineNumber()
- ? range.start : linkLine.textStart();
- const qreal startOffset = linkLine.cursorToX(linkStartPos);
- const int linkEndPos = i == lastLinkLine.lineNumber()
- ? range.start + range.length
- : linkLine.textStart() + linkLine.textLength();
- const qreal endOffset = linkLine.cursorToX(linkEndPos);
- const QPointF linkPos(indexPos.x() + positions.textAreaLeft() + linePos.x()
- + startOffset, positions.top() + linePos.y());
- const QSize linkSize(endOffset - startOffset, linkLine.height());
- const QRectF linkRect(linkPos, linkSize);
- m_hrefs.push_back({linkRect, range.format.anchorHref()});
- }
- }
+ const QString anchor = anchorAt(e->pos());
+ if (anchor == m_clickAnchor) {
+ Core::EditorManager::openEditorAt(OutputLineParser::parseLinkTarget(m_clickAnchor), {},
+ Core::EditorManager::SwitchSplitIfAlreadyVisible);
+ }
+ m_clickAnchor.clear();
+}
- const QColor mix = StyleHelper::mergedColors(textColor, backgroundColor, 70);
- const QString directory = QDir::toNativeSeparators(index.data(TaskModel::File).toString());
- int secondBaseLine = positions.top() + fm.ascent() + height + leading;
- if (index.data(TaskModel::FileNotFound).toBool() && !directory.isEmpty()) {
- const QString fileNotFound = Tr::tr("File not found: %1").arg(directory);
- const QColor errorColor = selected ? mix : creatorTheme()->color(Theme::TextColorError);
- painter->setPen(errorColor);
- painter->drawText(positions.textAreaLeft(), secondBaseLine, fileNotFound);
- } else {
- painter->setPen(mix);
- painter->drawText(positions.textAreaLeft(), secondBaseLine, directory);
+void TaskView::keyReleaseEvent(QKeyEvent *e)
+{
+ TreeView::keyReleaseEvent(e);
+ if (e->key() == Qt::Key_Space) {
+ const Task task = static_cast<TaskFilterModel *>(model())->task(currentIndex());
+ if (!task.isNull()) {
+ const QPoint toolTipPos = mapToGlobal(visualRect(currentIndex()).topLeft());
+ QMetaObject::invokeMethod(this, [this, task, toolTipPos] {
+ showToolTip(task, toolTipPos); }, Qt::QueuedConnection);
}
}
- painter->setPen(textColor);
-
- // Paint FileArea
- QString file = index.data(TaskModel::File).toString();
- const int pos = file.lastIndexOf(QLatin1Char('/'));
- if (pos != -1)
- file = file.mid(pos +1);
- const int realFileWidth = fm.horizontalAdvance(file);
- painter->setClipRect(positions.fileArea());
- painter->drawText(qMin(positions.fileAreaLeft(), positions.fileAreaRight() - realFileWidth),
- positions.top() + fm.ascent(), file);
- if (realFileWidth > positions.fileAreaWidth()) {
- // draw a gradient to mask the text
- int gradientStart = positions.fileAreaLeft() - 1;
- QLinearGradient lg(gradientStart + ELLIPSIS_GRADIENT_WIDTH, 0, gradientStart, 0);
- lg.setColorAt(0, Qt::transparent);
- lg.setColorAt(1, backgroundColor);
- painter->fillRect(gradientStart, positions.top(), ELLIPSIS_GRADIENT_WIDTH, positions.firstLineHeight(), lg);
- }
+}
- // Paint LineArea
- int line = index.data(TaskModel::Line).toInt();
- int movedLine = index.data(TaskModel::MovedLine).toInt();
- QString lineText;
-
- if (line == -1) {
- // No line information at all
- } else if (movedLine == -1) {
- // removed the line, but we had line information, show the line in ()
- QFont f = painter->font();
- f.setItalic(true);
- painter->setFont(f);
- lineText = QLatin1Char('(') + QString::number(line) + QLatin1Char(')');
- } else if (movedLine != line) {
- // The line was moved
- QFont f = painter->font();
- f.setItalic(true);
- painter->setFont(f);
- lineText = QString::number(movedLine);
- } else {
- lineText = QString::number(line);
+bool TaskView::event(QEvent *e)
+{
+ if (e->type() != QEvent::ToolTip)
+ return TreeView::event(e);
+
+ const auto helpEvent = static_cast<QHelpEvent*>(e);
+ const Task task = static_cast<TaskFilterModel *>(model())->task(indexAt(helpEvent->pos()));
+ if (task.isNull())
+ return TreeView::event(e);
+ showToolTip(task, helpEvent->globalPos());
+ e->accept();
+ return true;
+}
+
+void TaskView::showToolTip(const Task &task, const QPoint &pos)
+{
+ if (task.details.isEmpty()) {
+ ToolTip::hideImmediately();
+ return;
}
- painter->setClipRect(positions.lineArea());
- const int realLineWidth = fm.horizontalAdvance(lineText);
- painter->drawText(positions.lineAreaRight() - realLineWidth, positions.top() + fm.ascent(), lineText);
- painter->setClipRect(opt.rect);
+ const auto layout = new QVBoxLayout;
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->addWidget(new QLabel(task.formattedDescription({})));
+ ToolTip::show(pos, layout);
+}
- // Separator lines
- painter->setPen(QColor::fromRgb(150,150,150));
- const QRectF borderRect = QRectF(opt.rect).adjusted(0.5, 0.5, -0.5, -0.5);
- painter->drawLine(borderRect.bottomLeft(), borderRect.bottomRight());
- painter->restore();
+QString TaskView::anchorAt(const QPoint &pos)
+{
+ const QModelIndex index = indexAt(pos);
+ if (!index.isValid() || !index.internalId())
+ return {};
+
+ const QRect itemRect = visualRect(index);
+ QTextDocument &doc = static_cast<TaskDelegate *>(itemDelegate())->doc();
+ doc.setHtml(model()->data(index, Qt::DisplayRole).toString());
+ const QAbstractTextDocumentLayout * const textLayout = doc.documentLayout();
+ QTC_ASSERT(textLayout, return {});
+ return textLayout->anchorAt(pos - itemRect.topLeft());
}
} // namespace Internal
} // namespace ProjectExplorer
-
-#include "taskwindow.moc"
diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/CMakeLists.txt b/src/plugins/projectexplorer/testdata/multi-target-project/CMakeLists.txt
new file mode 100644
index 0000000000..5313539cd7
--- /dev/null
+++ b/src/plugins/projectexplorer/testdata/multi-target-project/CMakeLists.txt
@@ -0,0 +1,3 @@
+project(multi-target-project)
+add_executable(multi-target-project-app multi-target-project-main.cpp multi-target-project-shared.h)
+add_library(multi-target-project-lib STATIC multi-target-project-lib.cpp multi-target-project-shared.h)
diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-app.pro b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-app.pro
new file mode 100644
index 0000000000..96870c05c1
--- /dev/null
+++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-app.pro
@@ -0,0 +1,4 @@
+TARGET = app
+CONFIG -= qt
+SOURCES = multi-target-project-main.cpp
+HEADERS = multi-target-project-shared.h
diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.cpp b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.cpp
new file mode 100644
index 0000000000..9b7a34861c
--- /dev/null
+++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.cpp
@@ -0,0 +1,6 @@
+#include "multi-target-project-shared.h"
+
+int increaseNumber()
+{
+ return getNumber() + 1;
+}
diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.pro b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.pro
new file mode 100644
index 0000000000..4257154051
--- /dev/null
+++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.pro
@@ -0,0 +1,4 @@
+TEMPLATE = lib
+CONFIG += static
+SOURCES = multi-target-project-lib.cpp
+HEADERS = multi-target-project-shared.h
diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-main.cpp b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-main.cpp
new file mode 100644
index 0000000000..306400b350
--- /dev/null
+++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-main.cpp
@@ -0,0 +1,6 @@
+#include "multi-target-project-shared.h"
+
+int main()
+{
+ return getNumber();
+}
diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-shared.h b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-shared.h
new file mode 100644
index 0000000000..9f839e82d0
--- /dev/null
+++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-shared.h
@@ -0,0 +1,3 @@
+#pragma once
+
+inline int getNumber() { return 5; }
diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.pro b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.pro
new file mode 100644
index 0000000000..5e6289d7b2
--- /dev/null
+++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.pro
@@ -0,0 +1,4 @@
+TEMPLATE = subdirs
+app.file = multi-target-project-app.pro
+lib.file = multi-target-project-lib.pro
+SUBDIRS = app lib
diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.qbs b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.qbs
new file mode 100644
index 0000000000..079ac82c95
--- /dev/null
+++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.qbs
@@ -0,0 +1,10 @@
+Project {
+ CppApplication {
+ name: "app"
+ files: ["multi-target-project-main.cpp", "multi-target-project-shared.h"]
+ }
+ StaticLibrary {
+ Depends { name: "cpp" }
+ files: ["multi-target-project-lib.cpp", "multi-target-project-shared.h"]
+ }
+}
diff --git a/src/plugins/projectexplorer/toolchain.cpp b/src/plugins/projectexplorer/toolchain.cpp
index 1a3d3d43f5..8b8cfce2c8 100644
--- a/src/plugins/projectexplorer/toolchain.cpp
+++ b/src/plugins/projectexplorer/toolchain.cpp
@@ -192,7 +192,7 @@ bool ToolChain::isValid() const
return d->m_isValid.value_or(false);
}
-QStringList ToolChain::includedFiles(const QStringList &flags, const QString &directory) const
+FilePaths ToolChain::includedFiles(const QStringList &flags, const FilePath &directory) const
{
Q_UNUSED(flags)
Q_UNUSED(directory)
@@ -466,12 +466,12 @@ Utils::LanguageVersion ToolChain::languageVersion(const Utils::Id &language, con
}
}
-QStringList ToolChain::includedFiles(const QString &option,
- const QStringList &flags,
- const QString &directoryPath,
- PossiblyConcatenatedFlag possiblyConcatenated)
+FilePaths ToolChain::includedFiles(const QString &option,
+ const QStringList &flags,
+ const FilePath &directoryPath,
+ PossiblyConcatenatedFlag possiblyConcatenated)
{
- QStringList result;
+ FilePaths result;
for (int i = 0; i < flags.size(); ++i) {
QString includeFile;
@@ -484,11 +484,8 @@ QStringList ToolChain::includedFiles(const QString &option,
if (includeFile.isEmpty() && flag == option && i + 1 < flags.size())
includeFile = flags[++i];
- if (!includeFile.isEmpty()) {
- if (!QFileInfo(includeFile).isAbsolute())
- includeFile = directoryPath + "/" + includeFile;
- result.append(QDir::cleanPath(includeFile));
- }
+ if (!includeFile.isEmpty())
+ result.append(directoryPath.resolvePath(includeFile));
}
return result;
@@ -677,7 +674,9 @@ ToolchainDetector::ToolchainDetector(const Toolchains &alreadyKnown,
const IDevice::ConstPtr &device,
const FilePaths &searchPaths)
: alreadyKnown(alreadyKnown), device(device), searchPaths(searchPaths)
-{}
+{
+ QTC_CHECK(device);
+}
BadToolchain::BadToolchain(const Utils::FilePath &filePath)
: BadToolchain(filePath, filePath.symLinkTarget(), filePath.lastModified())
diff --git a/src/plugins/projectexplorer/toolchain.h b/src/plugins/projectexplorer/toolchain.h
index 15bee2fdaa..35cad5333e 100644
--- a/src/plugins/projectexplorer/toolchain.h
+++ b/src/plugins/projectexplorer/toolchain.h
@@ -102,7 +102,7 @@ public:
virtual Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const = 0;
virtual Utils::WarningFlags warningFlags(const QStringList &cflags) const = 0;
- virtual QStringList includedFiles(const QStringList &flags, const QString &directory) const;
+ virtual Utils::FilePaths includedFiles(const QStringList &flags, const Utils::FilePath &directory) const;
virtual QString sysRoot() const;
QString explicitCodeModelTargetTriple() const;
@@ -184,10 +184,10 @@ protected:
virtual bool fromMap(const QVariantMap &data);
enum class PossiblyConcatenatedFlag { No, Yes };
- static QStringList includedFiles(const QString &option,
- const QStringList &flags,
- const QString &directoryPath,
- PossiblyConcatenatedFlag possiblyConcatenated);
+ static Utils::FilePaths includedFiles(const QString &option,
+ const QStringList &flags,
+ const Utils::FilePath &directoryPath,
+ PossiblyConcatenatedFlag possiblyConcatenated);
private:
ToolChain(const ToolChain &) = delete;
diff --git a/src/plugins/projectexplorer/toolchainconfigwidget.cpp b/src/plugins/projectexplorer/toolchainconfigwidget.cpp
index ef97ad72cf..84b80b4e04 100644
--- a/src/plugins/projectexplorer/toolchainconfigwidget.cpp
+++ b/src/plugins/projectexplorer/toolchainconfigwidget.cpp
@@ -7,8 +7,8 @@
#include "projectexplorertr.h"
#include <utils/detailswidget.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QFormLayout>
#include <QLineEdit>
diff --git a/src/plugins/projectexplorer/toolchainoptionspage.cpp b/src/plugins/projectexplorer/toolchainoptionspage.cpp
index b8df603850..e0717ab780 100644
--- a/src/plugins/projectexplorer/toolchainoptionspage.cpp
+++ b/src/plugins/projectexplorer/toolchainoptionspage.cpp
@@ -48,20 +48,8 @@ class ToolChainTreeItem : public TreeItem
{
public:
ToolChainTreeItem(QStackedWidget *parentWidget, ToolChain *tc, bool c) :
- toolChain(tc), changed(c)
- {
- widget = tc->createConfigurationWidget().release();
- if (widget) {
- parentWidget->addWidget(widget);
- if (tc->isAutoDetected())
- widget->makeReadOnly();
- QObject::connect(widget, &ToolChainConfigWidget::dirty,
- [this] {
- changed = true;
- update();
- });
- }
- }
+ toolChain(tc), changed(c), m_parentWidget(parentWidget)
+ {}
QVariant data(int column, int role) const override
{
@@ -93,9 +81,30 @@ public:
return QVariant();
}
+ ToolChainConfigWidget *widget()
+ {
+ if (!m_widget) {
+ m_widget = toolChain->createConfigurationWidget().release();
+ if (m_widget) {
+ m_parentWidget->addWidget(m_widget);
+ if (toolChain->isAutoDetected())
+ m_widget->makeReadOnly();
+ QObject::connect(m_widget, &ToolChainConfigWidget::dirty,
+ [this] {
+ changed = true;
+ update();
+ });
+ }
+ }
+ return m_widget;
+ }
+
ToolChain *toolChain;
- ToolChainConfigWidget *widget;
bool changed;
+
+private:
+ ToolChainConfigWidget *m_widget = nullptr;
+ QStackedWidget *m_parentWidget = nullptr;
};
class DetectionSettingsDialog : public QDialog
@@ -423,7 +432,7 @@ void ToolChainOptionsWidget::toolChainSelectionChanged()
{
ToolChainTreeItem *item = currentTreeItem();
- QWidget *currentTcWidget = item ? item->widget : nullptr;
+ QWidget *currentTcWidget = item ? item->widget() : nullptr;
if (currentTcWidget)
m_widgetStack->setCurrentWidget(currentTcWidget);
m_container->setVisible(currentTcWidget);
@@ -447,8 +456,8 @@ void ToolChainOptionsWidget::apply()
for (TreeItem *item : *parent) {
auto tcItem = static_cast<ToolChainTreeItem *>(item);
Q_ASSERT(tcItem->toolChain);
- if (!tcItem->toolChain->isAutoDetected() && tcItem->widget && tcItem->changed)
- tcItem->widget->apply();
+ if (!tcItem->toolChain->isAutoDetected() && tcItem->widget() && tcItem->changed)
+ tcItem->widget()->apply();
tcItem->changed = false;
tcItem->update();
}
diff --git a/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp b/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp
index 4152e9882e..35224e4d02 100644
--- a/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp
+++ b/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp
@@ -3,6 +3,7 @@
#include "toolchainsettingsaccessor.h"
+#include "devicesupport/devicemanager.h"
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
#include "toolchain.h"
@@ -168,11 +169,10 @@ static ToolChainOperations mergeToolChainLists(const Toolchains &systemFileTcs,
// ToolChainSettingsAccessor:
// --------------------------------------------------------------------
-ToolChainSettingsAccessor::ToolChainSettingsAccessor() :
- UpgradingSettingsAccessor("QtCreatorToolChains",
- Tr::tr("Tool Chains"),
- Core::Constants::IDE_DISPLAY_NAME)
+ToolChainSettingsAccessor::ToolChainSettingsAccessor()
{
+ setDocType("QtCreatorToolChains");
+ setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME);
setBaseFilePath(Core::ICore::userResourcePath(TOOLCHAIN_FILENAME));
addVersionUpgrader(std::make_unique<ToolChainSettingsUpgraderV0>());
@@ -192,9 +192,11 @@ Toolchains ToolChainSettingsAccessor::restoreToolChains(QWidget *parent) const
// Autodetect: Pass autodetected toolchains from user file so the information can be reused:
const Toolchains autodetectedUserFileTcs
= Utils::filtered(userFileTcs, &ToolChain::isAutoDetected);
- // FIXME: Use real device?
- const Toolchains autodetectedTcs =
- autoDetectToolChains(ToolchainDetector(autodetectedUserFileTcs, {}, {}));
+
+ // Autodect from system paths on the desktop device.
+ // The restriction is intentional to keep startup and automatic validation a limited effort
+ ToolchainDetector detector(autodetectedUserFileTcs, DeviceManager::defaultDesktopDevice(), {});
+ const Toolchains autodetectedTcs = autoDetectToolChains(detector);
// merge tool chains and register those that we need to keep:
const ToolChainOperations ops = mergeToolChainLists(systemFileTcs, userFileTcs, autodetectedTcs);
diff --git a/src/plugins/projectexplorer/treescanner.cpp b/src/plugins/projectexplorer/treescanner.cpp
index abcc66c3cc..294ef4988e 100644
--- a/src/plugins/projectexplorer/treescanner.cpp
+++ b/src/plugins/projectexplorer/treescanner.cpp
@@ -9,9 +9,9 @@
#include <coreplugin/iversioncontrol.h>
#include <coreplugin/vcsmanager.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
#include <utils/algorithm.h>
-#include <utils/runextensions.h>
#include <memory>
@@ -42,9 +42,9 @@ bool TreeScanner::asyncScanForFiles(const Utils::FilePath &directory)
if (!m_futureWatcher.isFinished())
return false;
- m_scanFuture = Utils::runAsync(
- [directory, filter = m_filter, factory = m_factory] (FutureInterface &fi) {
- TreeScanner::scanForFiles(fi, directory, filter, factory);
+ m_scanFuture = Utils::asyncRun(
+ [directory, filter = m_filter, factory = m_factory] (Promise &promise) {
+ TreeScanner::scanForFiles(promise, directory, filter, factory);
});
m_futureWatcher.setFuture(m_scanFuture);
@@ -139,10 +139,10 @@ static std::unique_ptr<FolderNode> createFolderNode(const Utils::FilePath &direc
return fileSystemNode;
}
-void TreeScanner::scanForFiles(FutureInterface &fi, const Utils::FilePath& directory,
+void TreeScanner::scanForFiles(Promise &promise, const Utils::FilePath& directory,
const FileFilter &filter, const FileTypeFactory &factory)
{
- QList<FileNode *> nodes = ProjectExplorer::scanForFiles(fi, directory,
+ QList<FileNode *> nodes = ProjectExplorer::scanForFiles(promise, directory,
[&filter, &factory](const Utils::FilePath &fn) -> FileNode * {
const Utils::MimeType mimeType = Utils::mimeTypeForFile(fn);
@@ -160,10 +160,10 @@ void TreeScanner::scanForFiles(FutureInterface &fi, const Utils::FilePath& direc
Utils::sort(nodes, ProjectExplorer::Node::sortByPath);
- fi.setProgressValue(fi.progressMaximum());
+ promise.setProgressValue(promise.future().progressMaximum());
Result result{createFolderNode(directory, nodes), nodes};
- fi.reportResult(result);
+ promise.addResult(result);
}
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/treescanner.h b/src/plugins/projectexplorer/treescanner.h
index e324303c17..f8d019121b 100644
--- a/src/plugins/projectexplorer/treescanner.h
+++ b/src/plugins/projectexplorer/treescanner.h
@@ -31,7 +31,7 @@ public:
};
using Future = QFuture<Result>;
using FutureWatcher = QFutureWatcher<Result>;
- using FutureInterface = QFutureInterface<Result>;
+ using Promise = QPromise<Result>;
using FileFilter = std::function<bool(const Utils::MimeType &, const Utils::FilePath &)>;
using FileTypeFactory = std::function<ProjectExplorer::FileType(const Utils::MimeType &, const Utils::FilePath &)>;
@@ -69,7 +69,7 @@ signals:
void finished();
private:
- static void scanForFiles(FutureInterface &fi, const Utils::FilePath &directory,
+ static void scanForFiles(Promise &fi, const Utils::FilePath &directory,
const FileFilter &filter, const FileTypeFactory &factory);
private:
diff --git a/src/plugins/projectexplorer/userfileaccessor.cpp b/src/plugins/projectexplorer/userfileaccessor.cpp
index cf347e032f..ae900ea818 100644
--- a/src/plugins/projectexplorer/userfileaccessor.cpp
+++ b/src/plugins/projectexplorer/userfileaccessor.cpp
@@ -18,8 +18,8 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/persistentsettings.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QRegularExpression>
@@ -283,19 +283,21 @@ FilePaths UserFileBackUpStrategy::readFileCandidates(const FilePath &baseFileNam
// UserFileAccessor:
// --------------------------------------------------------------------
-UserFileAccessor::UserFileAccessor(Project *project) :
- MergingSettingsAccessor(std::make_unique<VersionedBackUpStrategy>(this),
- "QtCreatorProject", project->displayName(),
- Core::Constants::IDE_DISPLAY_NAME),
- m_project(project)
+UserFileAccessor::UserFileAccessor(Project *project)
+ : m_project(project)
{
+ setStrategy(std::make_unique<VersionedBackUpStrategy>(this));
+ setDocType("QtCreatorProject");
+ setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME);
+
// Setup:
const FilePath externalUser = externalUserFile();
const FilePath projectUser = projectUserFile();
setBaseFilePath(externalUser.isEmpty() ? projectUser : externalUser);
- auto secondary
- = std::make_unique<SettingsAccessor>(docType, displayName, applicationDisplayName);
+ auto secondary = std::make_unique<SettingsAccessor>();
+ secondary->setDocType(m_docType);
+ secondary->setApplicationDisplayName(m_applicationDisplayName);
secondary->setBaseFilePath(sharedFile());
secondary->setReadOnly();
setSecondaryAccessor(std::move(secondary));
diff --git a/src/plugins/python/CMakeLists.txt b/src/plugins/python/CMakeLists.txt
index e98190a569..0e4ca94419 100644
--- a/src/plugins/python/CMakeLists.txt
+++ b/src/plugins/python/CMakeLists.txt
@@ -20,4 +20,5 @@ add_qtc_plugin(Python
pythonsettings.cpp pythonsettings.h
pythontr.h
pythonutils.cpp pythonutils.h
+ pythonwizardpage.cpp pythonwizardpage.h
)
diff --git a/src/plugins/python/Python.json.in b/src/plugins/python/Python.json.in
index 3e62d14631..98fd52ec8d 100644
--- a/src/plugins/python/Python.json.in
+++ b/src/plugins/python/Python.json.in
@@ -30,12 +30,16 @@
\" <comment>Python module interface file</comment>\",
\" <glob pattern=\'*.pyi\'/>\",
\" </mime-type>\",
- \" <mime-type type=\'text/x-python-project\'>\",
+ \" <mime-type type=\'text/x-pyqt-project\'>\",
\" <sub-class-of type=\'text/x-python\'/>\",
\" <comment>Qt Creator Python project file</comment>\",
- \" <glob pattern=\'*.pyproject\'/>\",
\" <glob pattern=\'*.pyqtc\'/>\",
\" </mime-type>\",
+ \" <mime-type type=\'text/x-python-project\'>\",
+ \" <sub-class-of type=\'application/json\'/>\",
+ \" <comment>Qt Creator Python project file</comment>\",
+ \" <glob pattern=\'*.pyproject\'/>\",
+ \" </mime-type>\",
\"</mime-info>\"
]
}
diff --git a/src/plugins/python/pipsupport.cpp b/src/plugins/python/pipsupport.cpp
index a282702d9b..b420f24aa5 100644
--- a/src/plugins/python/pipsupport.cpp
+++ b/src/plugins/python/pipsupport.cpp
@@ -10,13 +10,13 @@
#include <coreplugin/progressmanager/progressmanager.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/mimeutils.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
+#include <utils/process.h>
using namespace Utils;
@@ -27,31 +27,39 @@ const char pipInstallTaskId[] = "Python::pipInstallTask";
PipInstallTask::PipInstallTask(const FilePath &python)
: m_python(python)
{
- connect(&m_process, &QtcProcess::done, this, &PipInstallTask::handleDone);
- connect(&m_process, &QtcProcess::readyReadStandardError, this, &PipInstallTask::handleError);
- connect(&m_process, &QtcProcess::readyReadStandardOutput, this, &PipInstallTask::handleOutput);
+ connect(&m_process, &Process::done, this, &PipInstallTask::handleDone);
+ connect(&m_process, &Process::readyReadStandardError, this, &PipInstallTask::handleError);
+ connect(&m_process, &Process::readyReadStandardOutput, this, &PipInstallTask::handleOutput);
connect(&m_killTimer, &QTimer::timeout, this, &PipInstallTask::cancel);
connect(&m_watcher, &QFutureWatcher<void>::canceled, this, &PipInstallTask::cancel);
m_watcher.setFuture(m_future.future());
}
-void PipInstallTask::setPackage(const PipPackage &package)
+void PipInstallTask::addPackage(const PipPackage &package)
{
- m_package = package;
+ m_packages << package;
+}
+
+void PipInstallTask::setPackages(const QList<PipPackage> &packages)
+{
+ m_packages = packages;
}
void PipInstallTask::run()
{
- if (m_package.packageName.isEmpty()) {
+ if (m_packages.isEmpty()) {
emit finished(false);
return;
}
- const QString taskTitle = Tr::tr("Install %1").arg(m_package.displayName);
+ const QString taskTitle = Tr::tr("Install Python Packages");
Core::ProgressManager::addTask(m_future.future(), taskTitle, pipInstallTaskId);
- QString package = m_package.packageName;
- if (!m_package.version.isEmpty())
- package += "==" + m_package.version;
- QStringList arguments = {"-m", "pip", "install", package};
+ QStringList arguments = {"-m", "pip", "install"};
+ for (const PipPackage &package : m_packages) {
+ QString pipPackage = package.packageName;
+ if (!package.version.isEmpty())
+ pipPackage += "==" + package.version;
+ arguments << pipPackage;
+ }
// add --user to global pythons, but skip it for venv pythons
if (!QDir(m_python.parentDir().toString()).exists("activate"))
@@ -62,7 +70,7 @@ void PipInstallTask::run()
Core::MessageManager::writeDisrupting(
Tr::tr("Running \"%1\" to install %2.")
- .arg(m_process.commandLine().toUserOutput(), m_package.displayName));
+ .arg(m_process.commandLine().toUserOutput(), packagesDisplayName()));
m_killTimer.setSingleShot(true);
m_killTimer.start(5 /*minutes*/ * 60 * 1000);
@@ -73,8 +81,10 @@ void PipInstallTask::cancel()
m_process.stop();
m_process.waitForFinished();
Core::MessageManager::writeFlashing(
- Tr::tr("The %1 installation was canceled by %2.")
- .arg(m_package.displayName, m_killTimer.isActive() ? Tr::tr("user") : Tr::tr("time out")));
+ m_killTimer.isActive()
+ ? Tr::tr("The installation of \"%1\" was canceled by timeout.").arg(packagesDisplayName())
+ : Tr::tr("The installation of \"%1\" was canceled by the user.")
+ .arg(packagesDisplayName()));
}
void PipInstallTask::handleDone()
@@ -82,8 +92,8 @@ void PipInstallTask::handleDone()
m_future.reportFinished();
const bool success = m_process.result() == ProcessResult::FinishedWithSuccess;
if (!success) {
- Core::MessageManager::writeFlashing(Tr::tr("Installing the %1 failed with exit code %2")
- .arg(m_package.displayName).arg(m_process.exitCode()));
+ Core::MessageManager::writeFlashing(Tr::tr("Installing %1 failed with exit code %2")
+ .arg(packagesDisplayName()).arg(m_process.exitCode()));
}
emit finished(success);
}
@@ -102,6 +112,11 @@ void PipInstallTask::handleError()
Core::MessageManager::writeSilently(stdErr);
}
+QString PipInstallTask::packagesDisplayName() const
+{
+ return Utils::transform(m_packages, &PipPackage::displayName).join(", ");
+}
+
void PipPackageInfo::parseField(const QString &field, const QStringList &data)
{
if (field.isEmpty())
@@ -147,7 +162,7 @@ static PipPackageInfo infoImpl(const PipPackage &package, const FilePath &python
{
PipPackageInfo result;
- QtcProcess pip;
+ Process pip;
pip.setCommand(CommandLine(python, {"-m", "pip", "show", "-f", package.packageName}));
pip.runBlocking();
QString fieldName;
@@ -175,7 +190,7 @@ static PipPackageInfo infoImpl(const PipPackage &package, const FilePath &python
QFuture<PipPackageInfo> Pip::info(const PipPackage &package)
{
- return Utils::runAsync(infoImpl, package, m_python);
+ return Utils::asyncRun(infoImpl, package, m_python);
}
Pip::Pip(const Utils::FilePath &python)
diff --git a/src/plugins/python/pipsupport.h b/src/plugins/python/pipsupport.h
index 4ef6fcca73..4d08a3b1ea 100644
--- a/src/plugins/python/pipsupport.h
+++ b/src/plugins/python/pipsupport.h
@@ -4,7 +4,7 @@
#pragma once
#include <utils/filepath.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QFutureWatcher>
#include <QTimer>
@@ -63,7 +63,8 @@ class PipInstallTask : public QObject
Q_OBJECT
public:
explicit PipInstallTask(const Utils::FilePath &python);
- void setPackage(const PipPackage &package);
+ void addPackage(const PipPackage &package);
+ void setPackages(const QList<PipPackage> &packages);
void run();
signals:
@@ -75,9 +76,11 @@ private:
void handleOutput();
void handleError();
+ QString packagesDisplayName() const;
+
const Utils::FilePath m_python;
- PipPackage m_package;
- Utils::QtcProcess m_process;
+ QList<PipPackage> m_packages;
+ Utils::Process m_process;
QFutureInterface<void> m_future;
QFutureWatcher<void> m_watcher;
QTimer m_killTimer;
diff --git a/src/plugins/python/pyside.cpp b/src/plugins/python/pyside.cpp
index 592addd308..4b9df185a3 100644
--- a/src/plugins/python/pyside.cpp
+++ b/src/plugins/python/pyside.cpp
@@ -5,7 +5,6 @@
#include "pipsupport.h"
#include "pythonplugin.h"
-#include "pythonsettings.h"
#include "pythontr.h"
#include "pythonutils.h"
@@ -17,10 +16,10 @@
#include <texteditor/textdocument.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/infobar.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <QRegularExpression>
#include <QTextCursor>
@@ -55,7 +54,7 @@ bool PySideInstaller::missingPySideInstallation(const FilePath &pythonPath,
if (pythonWithPyside[pythonPath].contains(pySide))
return false;
- QtcProcess pythonProcess;
+ Process pythonProcess;
pythonProcess.setCommand({pythonPath, {"-c", "import " + pySide}});
pythonProcess.runBlocking();
const bool missing = pythonProcess.result() != ProcessResult::FinishedWithSuccess;
@@ -88,19 +87,10 @@ void PySideInstaller::installPyside(const FilePath &python,
if (success)
emit pySideInstalled(python, pySide);
});
- install->setPackage(PipPackage(pySide));
+ install->setPackages({PipPackage(pySide)});
install->run();
}
-void PySideInstaller::changeInterpreter(const QString &interpreterId,
- RunConfiguration *runConfig)
-{
- if (runConfig) {
- if (auto aspect = runConfig->aspect<InterpreterAspect>())
- aspect->setCurrentInterpreter(PythonSettings::interpreter(interpreterId));
- }
-}
-
void PySideInstaller::handlePySideMissing(const FilePath &python,
const QString &pySide,
TextEditor::TextDocument *document)
@@ -140,8 +130,7 @@ void PySideInstaller::runPySideChecker(const FilePath &python,
handlePySideMissing(python, pySide, document);
watcher->deleteLater();
});
- watcher->setFuture(
- Utils::runAsync(&missingPySideInstallation, python, pySide));
+ watcher->setFuture(Utils::asyncRun(&missingPySideInstallation, python, pySide));
}
} // Python::Internal
diff --git a/src/plugins/python/pyside.h b/src/plugins/python/pyside.h
index 299a095412..3b4f99974a 100644
--- a/src/plugins/python/pyside.h
+++ b/src/plugins/python/pyside.h
@@ -30,7 +30,6 @@ private:
void installPyside(const Utils::FilePath &python,
const QString &pySide, TextEditor::TextDocument *document);
- void changeInterpreter(const QString &interpreterId, ProjectExplorer::RunConfiguration *runConfig);
void handlePySideMissing(const Utils::FilePath &python,
const QString &pySide,
TextEditor::TextDocument *document);
diff --git a/src/plugins/python/pysidebuildconfiguration.cpp b/src/plugins/python/pysidebuildconfiguration.cpp
index 3480be3e87..5f6f7dadea 100644
--- a/src/plugins/python/pysidebuildconfiguration.cpp
+++ b/src/plugins/python/pysidebuildconfiguration.cpp
@@ -23,56 +23,39 @@ namespace Python::Internal {
const char pySideBuildStep[] = "Python.PysideBuildStep";
-PySideBuildConfigurationFactory::PySideBuildConfigurationFactory()
-{
- registerBuildConfiguration<PySideBuildConfiguration>("Python.PySideBuildConfiguration");
- setSupportedProjectType(PythonProjectId);
- setSupportedProjectMimeTypeName(Constants::C_PY_MIMETYPE);
- setBuildGenerator([](const Kit *, const FilePath &projectPath, bool) {
- BuildInfo info;
- info.displayName = "build";
- info.typeName = "build";
- info.buildDirectory = projectPath.parentDir();
- return QList<BuildInfo>{info};
- });
-}
-
PySideBuildStepFactory::PySideBuildStepFactory()
{
registerStep<PySideBuildStep>(pySideBuildStep);
setSupportedProjectType(PythonProjectId);
setDisplayName(Tr::tr("Run PySide6 project tool"));
- setFlags(BuildStepInfo::UniqueStep);
+ setFlags(BuildStep::UniqueStep);
}
PySideBuildStep::PySideBuildStep(BuildStepList *bsl, Id id)
: AbstractProcessStep(bsl, id)
{
- m_pysideProject = addAspect<StringAspect>();
- m_pysideProject->setSettingsKey("Python.PySideProjectTool");
- m_pysideProject->setLabelText(Tr::tr("PySide project tool:"));
- m_pysideProject->setToolTip(Tr::tr("Enter location of PySide project tool."));
- m_pysideProject->setDisplayStyle(StringAspect::PathChooserDisplay);
- m_pysideProject->setExpectedKind(PathChooser::Command);
- m_pysideProject->setHistoryCompleter("Python.PySideProjectTool.History");
-
- const FilePath pySideProjectPath = Environment::systemEnvironment().searchInPath(
- "pyside6-project");
+ m_pysideProject.setSettingsKey("Python.PySideProjectTool");
+ m_pysideProject.setLabelText(Tr::tr("PySide project tool:"));
+ m_pysideProject.setToolTip(Tr::tr("Enter location of PySide project tool."));
+ m_pysideProject.setExpectedKind(PathChooser::Command);
+ m_pysideProject.setHistoryCompleter("Python.PySideProjectTool.History");
+
+ const FilePath pySideProjectPath = FilePath("pyside6-project").searchInPath();
if (pySideProjectPath.isExecutableFile())
- m_pysideProject->setFilePath(pySideProjectPath);
+ m_pysideProject.setFilePath(pySideProjectPath);
- setCommandLineProvider([this] { return CommandLine(m_pysideProject->filePath(), {"build"}); });
+ setCommandLineProvider([this] { return CommandLine(m_pysideProject(), {"build"}); });
setWorkingDirectoryProvider([this] {
- return target()->project()->projectDirectory().onDevice(m_pysideProject->filePath());
+ return m_pysideProject().withNewMappedPath(project()->projectDirectory()); // FIXME: new path needed?
});
setEnvironmentModifier([this](Environment &env) {
- env.prependOrSetPath(m_pysideProject->filePath().parentDir());
+ env.prependOrSetPath(m_pysideProject().parentDir());
});
}
-void PySideBuildStep::updatePySideProjectPath(const Utils::FilePath &pySideProjectPath)
+void PySideBuildStep::updatePySideProjectPath(const FilePath &pySideProjectPath)
{
- m_pysideProject->setFilePath(pySideProjectPath);
+ m_pysideProject.setFilePath(pySideProjectPath);
}
void PySideBuildStep::doRun()
@@ -83,17 +66,38 @@ void PySideBuildStep::doRun()
emit finished(true);
}
-PySideBuildConfiguration::PySideBuildConfiguration(Target *target, Id id)
- : BuildConfiguration(target, id)
+
+// PySideBuildConfiguration
+
+class PySideBuildConfiguration : public BuildConfiguration
{
- setConfigWidgetDisplayName(Tr::tr("General"));
+public:
+ PySideBuildConfiguration(Target *target, Id id)
+ : BuildConfiguration(target, id)
+ {
+ setConfigWidgetDisplayName(Tr::tr("General"));
+
+ setInitializer([this](const BuildInfo &) {
+ buildSteps()->appendStep(pySideBuildStep);
+ updateCacheAndEmitEnvironmentChanged();
+ });
- setInitializer([this](const BuildInfo &) {
- buildSteps()->appendStep(pySideBuildStep);
updateCacheAndEmitEnvironmentChanged();
- });
+ }
+};
- updateCacheAndEmitEnvironmentChanged();
+PySideBuildConfigurationFactory::PySideBuildConfigurationFactory()
+{
+ registerBuildConfiguration<PySideBuildConfiguration>("Python.PySideBuildConfiguration");
+ setSupportedProjectType(PythonProjectId);
+ setSupportedProjectMimeTypeName(Constants::C_PY_MIMETYPE);
+ setBuildGenerator([](const Kit *, const FilePath &projectPath, bool) {
+ BuildInfo info;
+ info.displayName = "build";
+ info.typeName = "build";
+ info.buildDirectory = projectPath.parentDir();
+ return QList<BuildInfo>{info};
+ });
}
} // Python::Internal
diff --git a/src/plugins/python/pysidebuildconfiguration.h b/src/plugins/python/pysidebuildconfiguration.h
index 17022c6be2..e686b9c222 100644
--- a/src/plugins/python/pysidebuildconfiguration.h
+++ b/src/plugins/python/pysidebuildconfiguration.h
@@ -9,18 +9,6 @@
namespace Python::Internal {
-class PySideBuildConfiguration : public ProjectExplorer::BuildConfiguration
-{
-public:
- PySideBuildConfiguration(ProjectExplorer::Target *target, Utils::Id id);
-};
-
-class PySideBuildConfigurationFactory : public ProjectExplorer::BuildConfigurationFactory
-{
-public:
- PySideBuildConfigurationFactory();
-};
-
class PySideBuildStep : public ProjectExplorer::AbstractProcessStep
{
Q_OBJECT
@@ -29,10 +17,9 @@ public:
void updatePySideProjectPath(const Utils::FilePath &pySideProjectPath);
private:
- Utils::StringAspect *m_pysideProject;
-
-private:
void doRun() override;
+
+ Utils::FilePathAspect m_pysideProject{this};
};
class PySideBuildStepFactory : public ProjectExplorer::BuildStepFactory
@@ -41,4 +28,10 @@ public:
PySideBuildStepFactory();
};
+class PySideBuildConfigurationFactory : public ProjectExplorer::BuildConfigurationFactory
+{
+public:
+ PySideBuildConfigurationFactory();
+};
+
} // Python::Internal
diff --git a/src/plugins/python/pysideuicextracompiler.cpp b/src/plugins/python/pysideuicextracompiler.cpp
index b1e7e31bb7..45fb68066f 100644
--- a/src/plugins/python/pysideuicextracompiler.cpp
+++ b/src/plugins/python/pysideuicextracompiler.cpp
@@ -3,7 +3,7 @@
#include "pysideuicextracompiler.h"
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
using namespace ProjectExplorer;
using namespace Utils;
@@ -30,7 +30,7 @@ FilePath PySideUicExtraCompiler::command() const
return m_pySideUic;
}
-FileNameToContentsHash PySideUicExtraCompiler::handleProcessFinished(QtcProcess *process)
+FileNameToContentsHash PySideUicExtraCompiler::handleProcessFinished(Process *process)
{
FileNameToContentsHash result;
if (process->exitStatus() != QProcess::NormalExit && process->exitCode() != 0)
diff --git a/src/plugins/python/pysideuicextracompiler.h b/src/plugins/python/pysideuicextracompiler.h
index f2a09bed78..058717621e 100644
--- a/src/plugins/python/pysideuicextracompiler.h
+++ b/src/plugins/python/pysideuicextracompiler.h
@@ -21,7 +21,7 @@ public:
private:
Utils::FilePath command() const override;
ProjectExplorer::FileNameToContentsHash handleProcessFinished(
- Utils::QtcProcess *process) override;
+ Utils::Process *process) override;
Utils::FilePath m_pySideUic;
};
diff --git a/src/plugins/python/python.qbs b/src/plugins/python/python.qbs
index 9f8288cb11..5186dafcdc 100644
--- a/src/plugins/python/python.qbs
+++ b/src/plugins/python/python.qbs
@@ -49,6 +49,8 @@ QtcPlugin {
"pythontr.h",
"pythonutils.cpp",
"pythonutils.h",
+ "pythonwizardpage.cpp",
+ "pythonwizardpage.h",
]
}
}
diff --git a/src/plugins/python/pythoneditor.cpp b/src/plugins/python/pythoneditor.cpp
index 6551facef5..d23685bc3b 100644
--- a/src/plugins/python/pythoneditor.cpp
+++ b/src/plugins/python/pythoneditor.cpp
@@ -19,12 +19,14 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <texteditor/textdocument.h>
#include <texteditor/texteditoractionhandler.h>
+#include <utils/stylehelper.h>
+
#include <QAction>
#include <QActionGroup>
#include <QComboBox>
@@ -120,7 +122,7 @@ private:
PythonEditorWidget::PythonEditorWidget(QWidget *parent) : TextEditorWidget(parent)
{
auto replButton = new QToolButton(this);
- replButton->setProperty("noArrow", true);
+ replButton->setProperty(StyleHelper::C_NO_ARROW, true);
replButton->setText(Tr::tr("REPL"));
replButton->setPopupMode(QToolButton::InstantPopup);
replButton->setToolTip(Tr::tr("Open interactive Python. Either importing nothing, "
@@ -152,7 +154,7 @@ void PythonEditorWidget::setUserDefinedPython(const Interpreter &interpreter)
QTC_ASSERT(pythonDocument, return);
FilePath documentPath = pythonDocument->filePath();
QTC_ASSERT(!documentPath.isEmpty(), return);
- if (Project *project = SessionManager::projectForFile(documentPath)) {
+ if (Project *project = ProjectManager::projectForFile(documentPath)) {
if (Target *target = project->activeTarget()) {
if (RunConfiguration *rc = target->activeRunConfiguration()) {
if (auto interpretersAspect= rc->aspect<InterpreterAspect>()) {
@@ -163,6 +165,7 @@ void PythonEditorWidget::setUserDefinedPython(const Interpreter &interpreter)
}
}
definePythonForDocument(textDocument()->filePath(), interpreter.command);
+ updateInterpretersSelector();
pythonDocument->checkForPyls();
}
@@ -174,7 +177,7 @@ void PythonEditorWidget::updateInterpretersSelector()
m_interpreters->setMenu(new QMenu(m_interpreters));
m_interpreters->setPopupMode(QToolButton::InstantPopup);
m_interpreters->setToolButtonStyle(Qt::ToolButtonTextOnly);
- m_interpreters->setProperty("noArrow", true);
+ m_interpreters->setProperty(StyleHelper::C_NO_ARROW, true);
}
QMenu *menu = m_interpreters->menu();
@@ -184,7 +187,7 @@ void PythonEditorWidget::updateInterpretersSelector()
disconnect(connection);
m_projectConnections.clear();
const FilePath documentPath = textDocument()->filePath();
- if (Project *project = SessionManager::projectForFile(documentPath)) {
+ if (Project *project = ProjectManager::projectForFile(documentPath)) {
m_projectConnections << connect(project,
&Project::activeTargetChanged,
this,
@@ -212,33 +215,51 @@ void PythonEditorWidget::updateInterpretersSelector()
m_interpreters->setText(text);
};
- const FilePath currentInterpreter = detectPython(textDocument()->filePath());
+ const FilePath currentInterpreterPath = detectPython(textDocument()->filePath());
const QList<Interpreter> configuredInterpreters = PythonSettings::interpreters();
- bool foundCurrentInterpreter = false;
auto interpretersGroup = new QActionGroup(menu);
interpretersGroup->setExclusive(true);
+ std::optional<Interpreter> currentInterpreter;
for (const Interpreter &interpreter : configuredInterpreters) {
QAction *action = interpretersGroup->addAction(interpreter.name);
connect(action, &QAction::triggered, this, [this, interpreter]() {
setUserDefinedPython(interpreter);
});
action->setCheckable(true);
- if (!foundCurrentInterpreter && interpreter.command == currentInterpreter) {
- foundCurrentInterpreter = true;
+ if (!currentInterpreter && interpreter.command == currentInterpreterPath) {
+ currentInterpreter = interpreter;
action->setChecked(true);
setButtonText(interpreter.name);
m_interpreters->setToolTip(interpreter.command.toUserOutput());
}
}
menu->addActions(interpretersGroup->actions());
- if (!foundCurrentInterpreter) {
- if (currentInterpreter.exists())
- setButtonText(currentInterpreter.toUserOutput());
+ if (!currentInterpreter) {
+ if (currentInterpreterPath.exists())
+ setButtonText(currentInterpreterPath.toUserOutput());
else
setButtonText(Tr::tr("No Python Selected"));
}
- if (!interpretersGroup->actions().isEmpty())
- menu->addSeparator();
+ if (!interpretersGroup->actions().isEmpty()) {
+ menu->addSeparator();
+ auto venvAction = menu->addAction(Tr::tr("Create Virtual Environment"));
+ connect(venvAction,
+ &QAction::triggered,
+ this,
+ [self = QPointer<PythonEditorWidget>(this), currentInterpreter]() {
+ if (!currentInterpreter)
+ return;
+ auto callback = [self](const std::optional<Interpreter> &venvInterpreter) {
+ if (self && venvInterpreter)
+ self->setUserDefinedPython(*venvInterpreter);
+ };
+ PythonSettings::createVirtualEnvironmentInteractive(self->textDocument()
+ ->filePath()
+ .parentDir(),
+ *currentInterpreter,
+ callback);
+ });
+ }
auto settingsAction = menu->addAction(Tr::tr("Manage Python Interpreters"));
connect(settingsAction, &QAction::triggered, this, []() {
Core::ICore::showOptionsDialog(Constants::C_PYTHONOPTIONS_PAGE_ID);
diff --git a/src/plugins/python/pythonlanguageclient.cpp b/src/plugins/python/pythonlanguageclient.cpp
index dde324dbd9..9f347d8e48 100644
--- a/src/plugins/python/pythonlanguageclient.cpp
+++ b/src/plugins/python/pythonlanguageclient.cpp
@@ -23,15 +23,15 @@
#include <languageserverprotocol/workspace.h>
#include <projectexplorer/extracompiler.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <texteditor/textdocument.h>
#include <texteditor/texteditor.h>
+#include <utils/async.h>
#include <utils/infobar.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
+#include <utils/process.h>
#include <utils/variablechooser.h>
#include <QCheckBox>
@@ -80,7 +80,7 @@ FilePath getPylsModulePath(CommandLine pylsCommand)
pylsCommand.addArg("-h");
- QtcProcess pythonProcess;
+ Process pythonProcess;
Environment env = pythonProcess.environment();
env.set("PYTHONVERBOSE", "x");
pythonProcess.setEnvironment(env);
@@ -114,7 +114,7 @@ static PythonLanguageServerState checkPythonLanguageServer(const FilePath &pytho
const CommandLine pythonLShelpCommand(python, {"-m", "pylsp", "-h"});
const FilePath &modulePath = getPylsModulePath(pythonLShelpCommand);
- QtcProcess pythonProcess;
+ Process pythonProcess;
pythonProcess.setCommand(pythonLShelpCommand);
pythonProcess.runBlocking();
if (pythonProcess.allOutput().contains("Python Language Server"))
@@ -305,7 +305,7 @@ void PyLSConfigureAssistant::installPythonLanguageServer(const FilePath &python,
install->deleteLater();
});
- install->setPackage(PipPackage{"python-lsp-server[all]", "Python Language Server"});
+ install->setPackages({PipPackage{"python-lsp-server[all]", "Python Language Server"}});
install->run();
}
@@ -341,7 +341,7 @@ void PyLSConfigureAssistant::openDocumentWithPython(const FilePath &python,
instance()->handlePyLSState(python, watcher->result(), document);
watcher->deleteLater();
});
- watcher->setFuture(Utils::runAsync(&checkPythonLanguageServer, python));
+ watcher->setFuture(Utils::asyncRun(&checkPythonLanguageServer, python));
}
void PyLSConfigureAssistant::handlePyLSState(const FilePath &python,
diff --git a/src/plugins/python/pythonplugin.cpp b/src/plugins/python/pythonplugin.cpp
index 3bc19a8725..3cbbe817f9 100644
--- a/src/plugins/python/pythonplugin.cpp
+++ b/src/plugins/python/pythonplugin.cpp
@@ -6,17 +6,17 @@
#include "pysidebuildconfiguration.h"
#include "pythoneditor.h"
#include "pythonproject.h"
-#include "pythonsettings.h"
#include "pythonrunconfiguration.h"
+#include "pythonsettings.h"
+#include "pythonwizardpage.h"
#include <projectexplorer/buildtargetinfo.h>
-#include <projectexplorer/localenvironmentaspect.h>
+#include <projectexplorer/jsonwizard/jsonwizardfactory.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/taskhub.h>
#include <utils/fsengine/fileiconprovider.h>
-#include <utils/futuresynchronizer.h>
#include <utils/theme/theme.h>
using namespace ProjectExplorer;
@@ -36,7 +36,6 @@ public:
PySideBuildConfigurationFactory buildConfigFactory;
SimpleTargetRunnerFactory runWorkerFactory{{runConfigFactory.runConfigurationId()}};
PythonSettings settings;
- FutureSynchronizer m_futureSynchronizer;
};
PythonPlugin::PythonPlugin()
@@ -55,17 +54,13 @@ PythonPlugin *PythonPlugin::instance()
return m_instance;
}
-FutureSynchronizer *PythonPlugin::futureSynchronizer()
-{
- QTC_ASSERT(m_instance, return nullptr);
- return &m_instance->d->m_futureSynchronizer;
-}
-
void PythonPlugin::initialize()
{
d = new PythonPluginPrivate;
ProjectManager::registerProjectType<PythonProject>(PythonMimeType);
+ ProjectManager::registerProjectType<PythonProject>(PythonMimeTypeLegacy);
+ JsonWizardFactory::registerPageFactory(new PythonWizardPageFactory);
}
void PythonPlugin::extensionsInitialized()
diff --git a/src/plugins/python/pythonplugin.h b/src/plugins/python/pythonplugin.h
index 7c8ca121f3..ef0860bbca 100644
--- a/src/plugins/python/pythonplugin.h
+++ b/src/plugins/python/pythonplugin.h
@@ -5,8 +5,6 @@
#include <extensionsystem/iplugin.h>
-namespace Utils { class FutureSynchronizer; }
-
namespace Python::Internal {
class PythonPlugin final : public ExtensionSystem::IPlugin
@@ -19,7 +17,6 @@ public:
~PythonPlugin() final;
static PythonPlugin *instance();
- static Utils::FutureSynchronizer *futureSynchronizer();
private:
void initialize() final;
diff --git a/src/plugins/python/pythonproject.h b/src/plugins/python/pythonproject.h
index 0217a77b61..676e010a01 100644
--- a/src/plugins/python/pythonproject.h
+++ b/src/plugins/python/pythonproject.h
@@ -7,7 +7,8 @@
namespace Python::Internal {
-const char PythonMimeType[] = "text/x-python-project"; // ### FIXME
+const char PythonMimeType[] = "text/x-python-project";
+const char PythonMimeTypeLegacy[] = "text/x-pyqt-project";
const char PythonProjectId[] = "PythonProject";
const char PythonErrorTaskCategory[] = "Task.Category.Python";
diff --git a/src/plugins/python/pythonrunconfiguration.cpp b/src/plugins/python/pythonrunconfiguration.cpp
index d21e7f8bfc..e7c5b4dee1 100644
--- a/src/plugins/python/pythonrunconfiguration.cpp
+++ b/src/plugins/python/pythonrunconfiguration.cpp
@@ -9,7 +9,6 @@
#include "pysideuicextracompiler.h"
#include "pythonconstants.h"
#include "pythonlanguageclient.h"
-#include "pythonplugin.h"
#include "pythonproject.h"
#include "pythonsettings.h"
#include "pythontr.h"
@@ -17,13 +16,14 @@
#include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h>
+#include <extensionsystem/pluginmanager.h>
+
#include <languageclient/languageclientmanager.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/kitinformation.h>
-#include <projectexplorer/localenvironmentaspect.h>
#include <projectexplorer/runconfigurationaspects.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
@@ -189,7 +189,8 @@ PythonRunConfiguration::PythonRunConfiguration(Target *target, Id id)
scriptAspect->setLabelText(Tr::tr("Script:"));
scriptAspect->setDisplayStyle(StringAspect::LabelDisplay);
- addAspect<LocalEnvironmentAspect>(target);
+ auto envAspect = addAspect<EnvironmentAspect>();
+ envAspect->setSupportForBuildEnvironment(target);
auto argumentsAspect = addAspect<ArgumentsAspect>(macroExpander());
@@ -218,7 +219,7 @@ PythonRunConfiguration::PythonRunConfiguration(Target *target, Id id)
currentInterpreterChanged();
setRunnableModifier([](Runnable &r) {
- r.workingDirectory = r.workingDirectory.onDevice(r.command.executable());
+ r.workingDirectory = r.command.executable().withNewMappedPath(r.workingDirectory); // FIXME: Needed?
});
connect(PySideInstaller::instance(), &PySideInstaller::pySideInstalled, this,
@@ -248,7 +249,7 @@ void PythonRunConfigurationPrivate::checkForPySide(const FilePath &python,
});
const auto future = Pip::instance(python)->info(package);
m_watcher.setFuture(future);
- PythonPlugin::futureSynchronizer()->addFuture(future);
+ ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(future);
}
void PythonRunConfigurationPrivate::handlePySidePackageInfo(const PipPackageInfo &pySideInfo,
@@ -279,12 +280,12 @@ void PythonRunConfigurationPrivate::handlePySidePackageInfo(const PipPackageInfo
= OsSpecificAspects::withExecutableSuffix(python.osType(), "pyside6-uic");
for (const FilePath &file : files) {
if (file.fileName() == pySide6ProjectName) {
- result.pySideProjectPath = location.resolvePath(file).onDevice(python);
+ result.pySideProjectPath = python.withNewMappedPath(location.resolvePath(file));
result.pySideProjectPath = result.pySideProjectPath.cleanPath();
if (!result.pySideUicPath.isEmpty())
return result;
} else if (file.fileName() == pySide6UicName) {
- result.pySideUicPath = location.resolvePath(file).onDevice(python);
+ result.pySideUicPath = python.withNewMappedPath(location.resolvePath(file));
result.pySideUicPath = result.pySideUicPath.cleanPath();
if (!result.pySideProjectPath.isEmpty())
return result;
diff --git a/src/plugins/python/pythonsettings.cpp b/src/plugins/python/pythonsettings.cpp
index 9306fe3b6f..0fe6576170 100644
--- a/src/plugins/python/pythonsettings.cpp
+++ b/src/plugins/python/pythonsettings.cpp
@@ -6,6 +6,7 @@
#include "pythonconstants.h"
#include "pythonplugin.h"
#include "pythontr.h"
+#include "pythonutils.h"
#include <coreplugin/dialogs/ioptionspage.h>
#include <coreplugin/icore.h>
@@ -26,23 +27,26 @@
#include <utils/listmodel.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/treemodel.h>
#include <utils/utilsicons.h>
+#include <QCheckBox>
+#include <QComboBox>
+#include <QDialogButtonBox>
#include <QDir>
+#include <QFormLayout>
+#include <QGroupBox>
+#include <QJsonDocument>
+#include <QJsonObject>
#include <QLabel>
-#include <QPushButton>
#include <QPointer>
+#include <QPushButton>
#include <QSettings>
#include <QStackedWidget>
#include <QTreeView>
-#include <QWidget>
#include <QVBoxLayout>
-#include <QGroupBox>
-#include <QCheckBox>
-#include <QJsonDocument>
-#include <QJsonObject>
+#include <QWidget>
using namespace ProjectExplorer;
using namespace Utils;
@@ -58,7 +62,7 @@ static Interpreter createInterpreter(const FilePath &python,
result.id = QUuid::createUuid().toString();
result.command = python;
- QtcProcess pythonProcess;
+ Process pythonProcess;
pythonProcess.setProcessChannelMode(QProcess::MergedChannels);
pythonProcess.setTimeoutS(1);
pythonProcess.setCommand({python, {"--version"}});
@@ -69,7 +73,7 @@ static Interpreter createInterpreter(const FilePath &python,
result.name = defaultName;
QDir pythonDir(python.parentDir().toString());
if (pythonDir.exists() && pythonDir.exists("activate") && pythonDir.cdUp())
- result.name += QString(" (%1 Virtual Environment)").arg(pythonDir.dirName());
+ result.name += QString(" (%1)").arg(pythonDir.dirName());
if (!suffix.isEmpty())
result.name += ' ' + suffix;
@@ -92,8 +96,9 @@ public:
Form {
Tr::tr("Name:"), m_name, br,
- Tr::tr("Executable"), m_executable
- }.attachTo(this, WithoutMargins);
+ Tr::tr("Executable"), m_executable,
+ noMargin
+ }.attachTo(this);
}
void updateInterpreter(const Interpreter &interpreter)
@@ -166,9 +171,10 @@ InterpreterOptionsWidget::InterpreterOptionsWidget()
if (interpreter.command.isEmpty())
return Tr::tr("Executable is empty.");
if (!interpreter.command.exists())
- return Tr::tr("%1 does not exist.").arg(interpreter.command.toUserOutput());
+ return Tr::tr("\"%1\" does not exist.").arg(interpreter.command.toUserOutput());
if (!interpreter.command.isExecutableFile())
- return Tr::tr("%1 is not an executable file.").arg(interpreter.command.toUserOutput());
+ return Tr::tr("\"%1\" is not an executable file.")
+ .arg(interpreter.command.toUserOutput());
break;
case Qt::DecorationRole:
if (column == 0 && !interpreter.command.isExecutableFile())
@@ -314,34 +320,37 @@ public:
setCategory(Constants::C_PYTHON_SETTINGS_CATEGORY);
setDisplayCategory(Tr::tr("Python"));
setCategoryIconPath(":/python/images/settingscategory_python.png");
- setWidgetCreator([]() { return new InterpreterOptionsWidget(); });
+ setWidgetCreator([this] { m_widget = new InterpreterOptionsWidget; return m_widget; });
}
QList<Interpreter> interpreters()
{
- if (auto w = static_cast<InterpreterOptionsWidget *>(widget()))
- return w->interpreters();
+ if (m_widget)
+ return m_widget->interpreters();
return {};
}
void addInterpreter(const Interpreter &interpreter)
{
- if (auto w = static_cast<InterpreterOptionsWidget *>(widget()))
- w->addInterpreter(interpreter);
+ if (m_widget)
+ m_widget->addInterpreter(interpreter);
}
void removeInterpreterFrom(const QString &detectionSource)
{
- if (auto w = static_cast<InterpreterOptionsWidget *>(widget()))
- w->removeInterpreterFrom(detectionSource);
+ if (m_widget)
+ m_widget->removeInterpreterFrom(detectionSource);
}
QList<Interpreter> interpreterFrom(const QString &detectionSource)
{
- if (auto w = static_cast<InterpreterOptionsWidget *>(widget()))
- return w->interpreterFrom(detectionSource);
+ if (m_widget)
+ return m_widget->interpreterFrom(detectionSource);
return {};
}
+
+private:
+ InterpreterOptionsWidget *m_widget = nullptr;
};
static bool alreadyRegistered(const QList<Interpreter> &pythons, const FilePath &pythonExecutable)
@@ -405,11 +414,11 @@ public:
mainGroupLayout->addWidget(m_pluginsGroup);
- const QString labelText = Tr::tr(
- "For a complete list of available options, consult the <a "
- "href=\"https://github.com/python-lsp/python-lsp-server/blob/develop/"
- "CONFIGURATION.md\">Python LSP Server configuration documentation</a>.");
-
+ const QString labelText = Tr::tr("For a complete list of available options, consult the "
+ "[Python LSP Server configuration documentation](%1).")
+ .arg("https://github.com/python-lsp/python-lsp-server/blob/"
+ "develop/CONFIGURATION.md");
+ m_advancedLabel->setTextFormat(Qt::MarkdownText);
m_advancedLabel->setText(labelText);
m_advancedLabel->setOpenExternalLinks(true);
mainGroupLayout->addWidget(m_advancedLabel);
@@ -625,7 +634,8 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons)
const FilePath &executable = FilePath::fromUserInput(regVal.toString());
if (executable.exists() && !alreadyRegistered(pythons, executable)) {
pythons << Interpreter{QUuid::createUuid().toString(),
- name + Tr::tr(" (Windowed)"),
+ //: <python display name> (Windowed)
+ Tr::tr("%1 (Windowed)").arg(name),
FilePath::fromUserInput(regVal.toString())};
}
}
@@ -645,17 +655,15 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons)
static void addPythonsFromPath(QList<Interpreter> &pythons)
{
- const auto &env = Environment::systemEnvironment();
-
if (HostOsInfo::isWindowsHost()) {
- for (const FilePath &executable : env.findAllInPath("python")) {
+ for (const FilePath &executable : FilePath("python").searchAllInPath()) {
// Windows creates empty redirector files that may interfere
if (executable.toFileInfo().size() == 0)
continue;
if (executable.exists() && !alreadyRegistered(pythons, executable))
pythons << createInterpreter(executable, "Python from Path");
}
- for (const FilePath &executable : env.findAllInPath("pythonw")) {
+ for (const FilePath &executable : FilePath("pythonw").searchAllInPath()) {
if (executable.exists() && !alreadyRegistered(pythons, executable))
pythons << createInterpreter(executable, "Python from Path", "(Windowed)");
}
@@ -664,7 +672,8 @@ static void addPythonsFromPath(QList<Interpreter> &pythons)
"python[1-9].[0-9]",
"python[1-9].[1-9][0-9]",
"python[1-9]"};
- for (const FilePath &path : env.path()) {
+ const FilePaths dirs = Environment::systemEnvironment().path();
+ for (const FilePath &path : dirs) {
const QDir dir(path.toString());
for (const QFileInfo &fi : dir.entryInfoList(filters)) {
const FilePath executable = Utils::FilePath::fromFileInfo(fi);
@@ -677,9 +686,9 @@ static void addPythonsFromPath(QList<Interpreter> &pythons)
static QString idForPythonFromPath(const QList<Interpreter> &pythons)
{
- FilePath pythonFromPath = Environment::systemEnvironment().searchInPath("python3");
+ FilePath pythonFromPath = FilePath("python3").searchInPath();
if (pythonFromPath.isEmpty())
- pythonFromPath = Environment::systemEnvironment().searchInPath("python");
+ pythonFromPath = FilePath("python").searchInPath();
if (pythonFromPath.isEmpty())
return {};
const Interpreter &defaultInterpreter
@@ -769,12 +778,86 @@ void PythonSettings::addInterpreter(const Interpreter &interpreter, bool isDefau
saveSettings();
}
+Interpreter PythonSettings::addInterpreter(const FilePath &interpreterPath,
+ bool isDefault,
+ const QString &nameSuffix)
+{
+ const Interpreter interpreter = createInterpreter(interpreterPath, {}, nameSuffix);
+ addInterpreter(interpreter, isDefault);
+ return interpreter;
+}
+
PythonSettings *PythonSettings::instance()
{
QTC_CHECK(settingsInstance);
return settingsInstance;
}
+void PythonSettings::createVirtualEnvironmentInteractive(
+ const FilePath &startDirectory,
+ const Interpreter &defaultInterpreter,
+ const std::function<void(std::optional<Interpreter>)> &callback)
+{
+ QDialog dialog;
+ dialog.setModal(true);
+ auto layout = new QFormLayout(&dialog);
+ auto interpreters = new QComboBox;
+ const QString preselectedId = defaultInterpreter.id.isEmpty()
+ ? PythonSettings::defaultInterpreter().id
+ : defaultInterpreter.id;
+ for (const Interpreter &interpreter : PythonSettings::interpreters()) {
+ interpreters->addItem(interpreter.name, interpreter.id);
+ if (!preselectedId.isEmpty() && interpreter.id == preselectedId)
+ interpreters->setCurrentIndex(interpreters->count() - 1);
+ }
+ layout->addRow(Tr::tr("Python Interpreter"), interpreters);
+ auto pathChooser = new PathChooser();
+ pathChooser->setInitialBrowsePathBackup(startDirectory);
+ pathChooser->setExpectedKind(PathChooser::Directory);
+ pathChooser->setPromptDialogTitle(Tr::tr("New Python Virtual Environment Directory"));
+ layout->addRow(Tr::tr("Virtual Environment Directory"), pathChooser);
+ auto buttons = new QDialogButtonBox(QDialogButtonBox::Cancel);
+ auto createButton = buttons->addButton(Tr::tr("Create"), QDialogButtonBox::AcceptRole);
+ createButton->setEnabled(false);
+ connect(pathChooser,
+ &PathChooser::validChanged,
+ createButton,
+ [createButton](bool valid) { createButton->setEnabled(valid); });
+ connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
+ connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
+ layout->addRow(buttons);
+ dialog.setLayout(layout);
+ if (dialog.exec() == QDialog::Rejected) {
+ callback({});
+ return;
+ }
+
+ const Interpreter interpreter = PythonSettings::interpreter(
+ interpreters->currentData().toString());
+
+ auto venvDir = pathChooser->filePath();
+ createVirtualEnvironment(venvDir, interpreter, callback);
+}
+
+void PythonSettings::createVirtualEnvironment(
+ const FilePath &directory,
+ const Interpreter &interpreter,
+ const std::function<void(std::optional<Interpreter>)> &callback,
+ const QString &nameSuffix)
+{
+ createVenv(interpreter.command, directory, [directory, callback, nameSuffix](bool success) {
+ std::optional<Interpreter> result;
+ if (success) {
+ FilePath venvPython = directory.osType() == Utils::OsTypeWindows ? directory / "Scripts"
+ : directory / "bin";
+ venvPython = venvPython.pathAppended("python").withExecutableSuffix();
+ if (venvPython.exists())
+ result = PythonSettings::addInterpreter(venvPython, false, nameSuffix);
+ }
+ callback(result);
+ });
+}
+
QList<Interpreter> PythonSettings::detectPythonVenvs(const FilePath &path)
{
QList<Interpreter> result;
diff --git a/src/plugins/python/pythonsettings.h b/src/plugins/python/pythonsettings.h
index 693c732208..35939c1ecd 100644
--- a/src/plugins/python/pythonsettings.h
+++ b/src/plugins/python/pythonsettings.h
@@ -24,12 +24,23 @@ public:
static Interpreter interpreter(const QString &interpreterId);
static void setInterpreter(const QList<Interpreter> &interpreters, const QString &defaultId);
static void addInterpreter(const Interpreter &interpreter, bool isDefault = false);
+ static Interpreter addInterpreter(const Utils::FilePath &interpreterPath,
+ bool isDefault = false,
+ const QString &nameSuffix = {});
static void setPyLSConfiguration(const QString &configuration);
static bool pylsEnabled();
static void setPylsEnabled(const bool &enabled);
static QString pylsConfiguration();
static PythonSettings *instance();
-
+ static void createVirtualEnvironmentInteractive(
+ const Utils::FilePath &startDirectory,
+ const Interpreter &defaultInterpreter,
+ const std::function<void(std::optional<Interpreter>)> &callback);
+ static void createVirtualEnvironment(
+ const Utils::FilePath &directory,
+ const Interpreter &interpreter,
+ const std::function<void(std::optional<Interpreter>)> &callback,
+ const QString &nameSuffix = {});
static QList<Interpreter> detectPythonVenvs(const Utils::FilePath &path);
signals:
diff --git a/src/plugins/python/pythonutils.cpp b/src/plugins/python/pythonutils.cpp
index 346c42d183..43d23a256b 100644
--- a/src/plugins/python/pythonutils.cpp
+++ b/src/plugins/python/pythonutils.cpp
@@ -8,14 +8,15 @@
#include "pythontr.h"
#include <coreplugin/messagemanager.h>
+#include <coreplugin/progressmanager/processprogress.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <utils/algorithm.h>
#include <utils/mimeutils.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
using namespace ProjectExplorer;
using namespace Utils;
@@ -31,11 +32,11 @@ static QHash<FilePath, FilePath> &userDefinedPythonsForDocument()
FilePath detectPython(const FilePath &documentPath)
{
Project *project = documentPath.isEmpty() ? nullptr
- : SessionManager::projectForFile(documentPath);
+ : ProjectManager::projectForFile(documentPath);
if (!project)
- project = SessionManager::startupProject();
+ project = ProjectManager::startupProject();
- Environment env = Environment::systemEnvironment();
+ FilePaths dirs = Environment::systemEnvironment().path();
if (project) {
if (auto target = project->activeTarget()) {
@@ -43,7 +44,7 @@ FilePath detectPython(const FilePath &documentPath)
if (auto interpreter = runConfig->aspect<InterpreterAspect>())
return interpreter->currentInterpreter().command;
if (auto environmentAspect = runConfig->aspect<EnvironmentAspect>())
- env = environmentAspect->environment();
+ dirs = environmentAspect->environment().path();
}
}
}
@@ -61,8 +62,9 @@ FilePath detectPython(const FilePath &documentPath)
if (defaultInterpreter.exists())
return defaultInterpreter;
- auto pythonFromPath = [=](const QString toCheck) {
- for (const FilePath &python : env.findAllInPath(toCheck)) {
+ auto pythonFromPath = [dirs](const FilePath &toCheck) {
+ const FilePaths found = toCheck.searchAllInDirectories(dirs);
+ for (const FilePath &python : found) {
// Windows creates empty redirector files that may interfere
if (python.exists() && python.osType() == OsTypeWindows && python.fileSize() != 0)
return python;
@@ -105,9 +107,11 @@ static QStringList replImportArgs(const FilePath &pythonFile, ReplType type)
void openPythonRepl(QObject *parent, const FilePath &file, ReplType type)
{
+ Q_UNUSED(parent)
+
static const auto workingDir = [](const FilePath &file) {
if (file.isEmpty()) {
- if (Project *project = SessionManager::startupProject())
+ if (Project *project = ProjectManager::startupProject())
return project->projectDirectory();
return FilePath::currentWorkingPath();
}
@@ -115,23 +119,21 @@ void openPythonRepl(QObject *parent, const FilePath &file, ReplType type)
};
const auto args = QStringList{"-i"} + replImportArgs(file, type);
- auto process = new QtcProcess(parent);
- process->setTerminalMode(TerminalMode::On);
const FilePath pythonCommand = detectPython(file);
- process->setCommand({pythonCommand, args});
- process->setWorkingDirectory(workingDir(file));
- const QString commandLine = process->commandLine().toUserOutput();
- QObject::connect(process, &QtcProcess::done, process, [process, commandLine] {
- if (process->error() != QProcess::UnknownError) {
- Core::MessageManager::writeDisrupting(Tr::tr(
- (process->error() == QProcess::FailedToStart)
- ? "Failed to run Python (%1): \"%2\"."
- : "Error while running Python (%1): \"%2\".")
- .arg(commandLine, process->errorString()));
- }
- process->deleteLater();
- });
- process->start();
+
+ Process process;
+ process.setCommand({pythonCommand, args});
+ process.setWorkingDirectory(workingDir(file));
+ process.setTerminalMode(TerminalMode::Detached);
+ process.start();
+
+ if (process.error() != QProcess::UnknownError) {
+ Core::MessageManager::writeDisrupting(
+ Tr::tr((process.error() == QProcess::FailedToStart)
+ ? "Failed to run Python (%1): \"%2\"."
+ : "Error while running Python (%1): \"%2\".")
+ .arg(process.commandLine().toUserOutput(), process.errorString()));
+ }
}
QString pythonName(const FilePath &pythonPath)
@@ -141,7 +143,7 @@ QString pythonName(const FilePath &pythonPath)
return {};
QString name = nameForPython.value(pythonPath);
if (name.isEmpty()) {
- QtcProcess pythonProcess;
+ Process pythonProcess;
pythonProcess.setTimeoutS(2);
pythonProcess.setCommand({pythonPath, {"--version"}});
pythonProcess.runBlocking();
@@ -155,7 +157,7 @@ QString pythonName(const FilePath &pythonPath)
PythonProject *pythonProjectForFile(const FilePath &pythonFile)
{
- for (Project *project : SessionManager::projects()) {
+ for (Project *project : ProjectManager::projects()) {
if (auto pythonProject = qobject_cast<PythonProject *>(project)) {
if (pythonProject->isKnownFile(pythonFile))
return pythonProject;
@@ -164,4 +166,24 @@ PythonProject *pythonProjectForFile(const FilePath &pythonFile)
return nullptr;
}
+void createVenv(const Utils::FilePath &python,
+ const Utils::FilePath &venvPath,
+ const std::function<void(bool)> &callback)
+{
+ QTC_ASSERT(python.isExecutableFile(), callback(false); return);
+ QTC_ASSERT(!venvPath.exists() || venvPath.isDir(), callback(false); return);
+
+ const CommandLine command(python, QStringList{"-m", "venv", venvPath.toUserOutput()});
+
+ auto process = new Process;
+ auto progress = new Core::ProcessProgress(process);
+ progress->setDisplayName(Tr::tr("Create Python venv"));
+ QObject::connect(process, &Process::done, [process, callback](){
+ callback(process->result() == ProcessResult::FinishedWithSuccess);
+ process->deleteLater();
+ });
+ process->setCommand(command);
+ process->start();
+}
+
} // Python::Internal
diff --git a/src/plugins/python/pythonutils.h b/src/plugins/python/pythonutils.h
index 8d5b06974e..f3e685b4ae 100644
--- a/src/plugins/python/pythonutils.h
+++ b/src/plugins/python/pythonutils.h
@@ -16,4 +16,8 @@ QString pythonName(const Utils::FilePath &pythonPath);
class PythonProject;
PythonProject *pythonProjectForFile(const Utils::FilePath &pythonFile);
+void createVenv(const Utils::FilePath &python,
+ const Utils::FilePath &venvPath,
+ const std::function<void(bool)> &callback);
+
} // Python::Internal
diff --git a/src/plugins/python/pythonwizardpage.cpp b/src/plugins/python/pythonwizardpage.cpp
new file mode 100644
index 0000000000..bcbca5a707
--- /dev/null
+++ b/src/plugins/python/pythonwizardpage.cpp
@@ -0,0 +1,219 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "pythonwizardpage.h"
+
+#include "pythonconstants.h"
+#include "pythonsettings.h"
+#include "pythontr.h"
+
+#include <coreplugin/generatedfile.h>
+
+#include <utils/algorithm.h>
+#include <utils/layoutbuilder.h>
+#include <utils/mimeutils.h>
+#include <utils/qtcassert.h>
+
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
+#include <projectexplorer/target.h>
+
+using namespace ProjectExplorer;
+using namespace Utils;
+
+namespace Python::Internal {
+
+PythonWizardPageFactory::PythonWizardPageFactory()
+{
+ setTypeIdsSuffix("PythonConfiguration");
+}
+
+WizardPage *PythonWizardPageFactory::create(JsonWizard *wizard, Id typeId, const QVariant &data)
+{
+ Q_UNUSED(wizard)
+
+ QTC_ASSERT(canCreate(typeId), return nullptr);
+
+ QList<QPair<QString, QVariant>> pySideAndData;
+ for (const QVariant &item : data.toMap().value("items").toList()) {
+ const QMap<QString, QVariant> map = item.toMap();
+ const QVariant name = map.value("trKey");
+ if (name.isValid())
+ pySideAndData.emplaceBack(QPair<QString, QVariant>{name.toString(), map.value("value")});
+ }
+ bool validIndex = false;
+ int defaultPySide = data.toMap().value("index").toInt(&validIndex);
+ if (!validIndex)
+ defaultPySide = -1;
+ return new PythonWizardPage(pySideAndData, defaultPySide);
+}
+
+static bool validItem(const QVariant &item)
+{
+ QMap<QString, QVariant> map = item.toMap();
+ if (!map.value("trKey").canConvert<QString>())
+ return false;
+ map = map.value("value").toMap();
+ return map.value("PySideVersion").canConvert<QString>();
+}
+
+bool PythonWizardPageFactory::validateData(Id typeId, const QVariant &data, QString *errorMessage)
+{
+ QTC_ASSERT(canCreate(typeId), return false);
+ const QList<QVariant> items = data.toMap().value("items").toList();
+
+ if (items.isEmpty()) {
+ if (errorMessage) {
+ *errorMessage = Tr::tr("'data' of a Python wizard page expects a map with 'items' "
+ "containing a list of objects");
+ }
+ return false;
+ }
+
+ if (!Utils::allOf(items, &validItem)) {
+ if (errorMessage) {
+ *errorMessage = Tr::tr(
+ "An item of Python wizard page data expects a 'trKey' field containing the ui "
+ "visible string for that python version and an field 'value' containing an object "
+ "with a 'PySideVersion' field used for import statements in the python files.");
+ }
+ return false;
+ }
+ return true;
+}
+
+PythonWizardPage::PythonWizardPage(const QList<QPair<QString, QVariant>> &pySideAndData,
+ const int defaultPyside)
+{
+ using namespace Layouting;
+ m_interpreter.setSettingsDialogId(Constants::C_PYTHONOPTIONS_PAGE_ID);
+ connect(PythonSettings::instance(),
+ &PythonSettings::interpretersChanged,
+ this,
+ &PythonWizardPage::updateInterpreters);
+
+ m_pySideVersion.setLabelText(Tr::tr("PySide version"));
+ m_pySideVersion.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
+ for (auto [name, data] : pySideAndData)
+ m_pySideVersion.addOption(SelectionAspect::Option(name, {}, data));
+ if (defaultPyside >= 0)
+ m_pySideVersion.setDefaultValue(defaultPyside);
+
+ m_createVenv.setLabelText(Tr::tr("Create new Virtual Environment"));
+
+ m_venvPath.setLabelText(Tr::tr("Path to virtual environment"));
+ m_venvPath.setEnabler(&m_createVenv);
+ m_venvPath.setExpectedKind(PathChooser::Directory);
+
+ m_stateLabel = new InfoLabel();
+ m_stateLabel->setWordWrap(true);
+ m_stateLabel->setFilled(true);
+ m_stateLabel->setType(InfoLabel::Error);
+ connect(&m_venvPath, &StringAspect::valueChanged, this, &PythonWizardPage::updateStateLabel);
+ connect(&m_createVenv, &BoolAspect::valueChanged, this, &PythonWizardPage::updateStateLabel);
+
+ Grid {
+ m_pySideVersion, br,
+ m_interpreter, br,
+ m_createVenv, br,
+ m_venvPath, br,
+ m_stateLabel, br,
+ noMargin
+ }.attachTo(this);
+}
+
+void PythonWizardPage::initializePage()
+{
+ auto wiz = qobject_cast<JsonWizard *>(wizard());
+ QTC_ASSERT(wiz, return);
+ connect(wiz, &JsonWizard::filesPolished,
+ this, &PythonWizardPage::setupProject,
+ Qt::UniqueConnection);
+
+ const FilePath projectDir = FilePath::fromString(wiz->property("ProjectDirectory").toString());
+ m_createVenv.setValue(!projectDir.isEmpty());
+ if (m_venvPath.filePath().isEmpty())
+ m_venvPath.setFilePath(projectDir.isEmpty() ? FilePath{} : projectDir / "venv");
+
+ updateInterpreters();
+ updateStateLabel();
+}
+
+bool PythonWizardPage::validatePage()
+{
+ if (m_createVenv.value() && !m_venvPath.pathChooser()->isValid())
+ return false;
+ auto wiz = qobject_cast<JsonWizard *>(wizard());
+ const QMap<QString, QVariant> data = m_pySideVersion.itemValue().toMap();
+ for (auto it = data.begin(), end = data.end(); it != end; ++it)
+ wiz->setValue(it.key(), it.value());
+ return true;
+}
+
+void PythonWizardPage::setupProject(const JsonWizard::GeneratorFiles &files)
+{
+ for (const JsonWizard::GeneratorFile &f : files) {
+ if (f.file.attributes() & Core::GeneratedFile::OpenProjectAttribute) {
+ Interpreter interpreter = m_interpreter.currentInterpreter();
+ Project *project = ProjectManager::openProject(Utils::mimeTypeForFile(f.file.filePath()),
+ f.file.filePath().absoluteFilePath());
+ if (m_createVenv.value()) {
+ auto openProjectWithInterpreter = [f](const std::optional<Interpreter> &interpreter) {
+ if (!interpreter)
+ return;
+ Project *project = ProjectManager::projectWithProjectFilePath(f.file.filePath());
+ if (!project)
+ return;
+ if (Target *target = project->activeTarget()) {
+ if (RunConfiguration *rc = target->activeRunConfiguration()) {
+ if (auto interpreters = rc->aspect<InterpreterAspect>())
+ interpreters->setCurrentInterpreter(*interpreter);
+ }
+ }
+ };
+ PythonSettings::createVirtualEnvironment(m_venvPath.filePath(),
+ interpreter,
+ openProjectWithInterpreter,
+ project ? project->displayName()
+ : QString{});
+ }
+
+ if (project) {
+ project->addTargetForDefaultKit();
+ if (Target *target = project->activeTarget()) {
+ if (RunConfiguration *rc = target->activeRunConfiguration()) {
+ if (auto interpreters = rc->aspect<InterpreterAspect>()) {
+ interpreters->setCurrentInterpreter(interpreter);
+ project->saveSettings();
+ }
+ }
+ }
+ delete project;
+ }
+ }
+ }
+}
+
+void PythonWizardPage::updateInterpreters()
+{
+ m_interpreter.setDefaultInterpreter(PythonSettings::defaultInterpreter());
+ m_interpreter.updateInterpreters(PythonSettings::interpreters());
+}
+
+void PythonWizardPage::updateStateLabel()
+{
+ QTC_ASSERT(m_stateLabel, return);
+ if (m_createVenv.value()) {
+ if (PathChooser *pathChooser = m_venvPath.pathChooser()) {
+ if (!pathChooser->isValid()) {
+ m_stateLabel->show();
+ m_stateLabel->setText(pathChooser->errorMessage());
+ return;
+ }
+ }
+ }
+ m_stateLabel->hide();
+}
+
+} // namespace Python::Internal
+
diff --git a/src/plugins/python/pythonwizardpage.h b/src/plugins/python/pythonwizardpage.h
new file mode 100644
index 0000000000..6cf8a130c5
--- /dev/null
+++ b/src/plugins/python/pythonwizardpage.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <projectexplorer/jsonwizard/jsonwizard.h>
+#include <projectexplorer/jsonwizard/jsonwizardpagefactory.h>
+#include <projectexplorer/runconfigurationaspects.h>
+
+#include <utils/aspects.h>
+#include <utils/wizardpage.h>
+
+namespace Python::Internal {
+
+class PythonWizardPageFactory : public ProjectExplorer::JsonWizardPageFactory
+{
+public:
+ PythonWizardPageFactory();
+
+ Utils::WizardPage *create(ProjectExplorer::JsonWizard *wizard,
+ Utils::Id typeId,
+ const QVariant &data) override;
+ bool validateData(Utils::Id typeId, const QVariant &data, QString *errorMessage) override;
+};
+
+class PythonWizardPage : public Utils::WizardPage
+{
+public:
+ PythonWizardPage(const QList<QPair<QString, QVariant>> &pySideAndData, const int defaultPyside);
+ void initializePage() override;
+ bool validatePage() override;
+
+private:
+ void setupProject(const ProjectExplorer::JsonWizard::GeneratorFiles &files);
+ void updateInterpreters();
+ void updateStateLabel();
+
+ ProjectExplorer::InterpreterAspect m_interpreter;
+ Utils::SelectionAspect m_pySideVersion;
+ Utils::BoolAspect m_createVenv;
+ Utils::FilePathAspect m_venvPath;
+ Utils::InfoLabel *m_stateLabel = nullptr;
+};
+
+} // namespace Python::Internal
diff --git a/src/plugins/qbsprojectmanager/customqbspropertiesdialog.cpp b/src/plugins/qbsprojectmanager/customqbspropertiesdialog.cpp
index 4f3d88907a..78e0634bc5 100644
--- a/src/plugins/qbsprojectmanager/customqbspropertiesdialog.cpp
+++ b/src/plugins/qbsprojectmanager/customqbspropertiesdialog.cpp
@@ -43,7 +43,7 @@ CustomQbsPropertiesDialog::CustomQbsPropertiesDialog(const QVariantMap &properti
m_removeButton = new QPushButton(Tr::tr("&Remove"));
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row {
m_propertiesTable,
diff --git a/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp b/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp
index 46433eca5a..e2d1ae9ee7 100644
--- a/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp
+++ b/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp
@@ -107,7 +107,7 @@ static QStringList toolchainList(const ProjectExplorer::ToolChain *tc)
const Utils::Id type = tc->typeId();
if (type == ProjectExplorer::Constants::CLANG_TOOLCHAIN_TYPEID
|| (type == Android::Constants::ANDROID_TOOLCHAIN_TYPEID
- && tc->compilerCommand().toString().contains("clang"))) {
+ && tc->compilerCommand().fileName().contains("clang"))) {
return {"clang", "llvm", "gcc"};
}
if (type == ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID
@@ -156,7 +156,7 @@ static QString architecture(const ProjectExplorer::Abi &targetAbi)
switch (targetAbi.architecture()) {
case ProjectExplorer::Abi::X86Architecture:
architecture.append(QLatin1Char('_'));
- // fall through
+ [[fallthrough]];
case ProjectExplorer::Abi::ArmArchitecture:
// ARM sub-architectures are currently not handled, which is kind of problematic
case ProjectExplorer::Abi::MipsArchitecture:
diff --git a/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp b/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp
index c5d4b62a0d..41ae75d8b9 100644
--- a/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp
+++ b/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp
@@ -27,8 +27,8 @@
#include <qtsupport/qtkitinformation.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QCoreApplication>
#include <QCryptographicHash>
diff --git a/src/plugins/qbsprojectmanager/qbsbuildstep.cpp b/src/plugins/qbsprojectmanager/qbsbuildstep.cpp
index f8fad01ff7..afb9465241 100644
--- a/src/plugins/qbsprojectmanager/qbsbuildstep.cpp
+++ b/src/plugins/qbsprojectmanager/qbsbuildstep.cpp
@@ -28,8 +28,8 @@
#include <utils/macroexpander.h>
#include <utils/outputformatter.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/variablechooser.h>
#include <QCheckBox>
@@ -63,7 +63,7 @@ public:
ArchitecturesAspect();
void setKit(const ProjectExplorer::Kit *kit) { m_kit = kit; }
- void addToLayout(Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
QStringList selectedArchitectures() const;
void setSelectedArchitectures(const QStringList& architectures);
bool isManagedByTarget() const { return m_isManagedByTarget; }
@@ -86,9 +86,9 @@ ArchitecturesAspect::ArchitecturesAspect()
setAllValues(m_abisToArchMap.keys());
}
-void ArchitecturesAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void ArchitecturesAspect::addToLayout(Layouting::LayoutItem &parent)
{
- MultiSelectionAspect::addToLayout(builder);
+ MultiSelectionAspect::addToLayout(parent);
const auto changeHandler = [this] {
const QtVersion *qtVersion = QtKitAspect::qtVersion(m_kit);
if (!qtVersion) {
@@ -224,7 +224,7 @@ QbsBuildStep::QbsBuildStep(BuildStepList *bsl, Utils::Id id) :
m_keepGoing->setToolTip(
QbsProjectManager::Tr::tr("Keep going when errors occur (if at all possible)."));
m_keepGoing->setLabel(QbsProjectManager::Tr::tr("Keep going"),
- BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ BoolAspect::LabelPlacement::AtCheckBox);
m_maxJobCount = addAspect<IntegerAspect>();
m_maxJobCount->setSettingsKey(QBS_MAXJOBCOUNT);
@@ -235,22 +235,22 @@ QbsBuildStep::QbsBuildStep(BuildStepList *bsl, Utils::Id id) :
m_showCommandLines = addAspect<BoolAspect>();
m_showCommandLines->setSettingsKey(QBS_SHOWCOMMANDLINES);
m_showCommandLines->setLabel(QbsProjectManager::Tr::tr("Show command lines"),
- BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ BoolAspect::LabelPlacement::AtCheckBox);
m_install = addAspect<BoolAspect>();
m_install->setSettingsKey(QBS_INSTALL);
m_install->setValue(true);
- m_install->setLabel(QbsProjectManager::Tr::tr("Install"), BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ m_install->setLabel(QbsProjectManager::Tr::tr("Install"), BoolAspect::LabelPlacement::AtCheckBox);
m_cleanInstallDir = addAspect<BoolAspect>();
m_cleanInstallDir->setSettingsKey(QBS_CLEAN_INSTALL_ROOT);
m_cleanInstallDir->setLabel(QbsProjectManager::Tr::tr("Clean install root"),
- BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ BoolAspect::LabelPlacement::AtCheckBox);
m_forceProbes = addAspect<BoolAspect>();
m_forceProbes->setSettingsKey("Qbs.forceProbesKey");
m_forceProbes->setLabel(QbsProjectManager::Tr::tr("Force probes"),
- BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ BoolAspect::LabelPlacement::AtCheckBox);
m_commandLine = addAspect<StringAspect>();
m_commandLine->setDisplayStyle(StringAspect::TextEditDisplay);
@@ -658,25 +658,27 @@ QbsBuildStepConfigWidget::QbsBuildStepConfigWidget(QbsBuildStep *step)
installDirChooser = new PathChooser(this);
installDirChooser->setExpectedKind(PathChooser::Directory);
- Layouting::Form builder;
- builder.addRow(m_qbsStep->m_buildVariant);
- builder.addRow(m_qbsStep->m_selectedAbis);
- builder.addRow(m_qbsStep->m_maxJobCount);
- builder.addRow({QbsProjectManager::Tr::tr("Properties:"), propertyEdit});
-
- builder.addRow(QbsProjectManager::Tr::tr("Flags:"));
- m_qbsStep->m_keepGoing->addToLayout(builder);
- m_qbsStep->m_showCommandLines->addToLayout(builder);
- m_qbsStep->m_forceProbes->addToLayout(builder);
-
- builder.addRow(QbsProjectManager::Tr::tr("Installation flags:"));
- m_qbsStep->m_install->addToLayout(builder);
- m_qbsStep->m_cleanInstallDir->addToLayout(builder);
- builder.addItem(defaultInstallDirCheckBox);
-
- builder.addRow({QbsProjectManager::Tr::tr("Installation directory:"), installDirChooser});
- builder.addRow(m_qbsStep->m_commandLine);
- builder.attachTo(this, Layouting::WithoutMargins);
+ using namespace Layouting;
+ Form {
+ m_qbsStep->m_buildVariant, br,
+ m_qbsStep->m_selectedAbis, br,
+ m_qbsStep->m_maxJobCount, br,
+ QbsProjectManager::Tr::tr("Properties:"), propertyEdit, br,
+
+ QbsProjectManager::Tr::tr("Flags:"),
+ m_qbsStep->m_keepGoing,
+ m_qbsStep->m_showCommandLines,
+ m_qbsStep->m_forceProbes, br,
+
+ QbsProjectManager::Tr::tr("Installation flags:"),
+ m_qbsStep->m_install,
+ m_qbsStep->m_cleanInstallDir,
+ defaultInstallDirCheckBox, br,
+
+ QbsProjectManager::Tr::tr("Installation directory:"), installDirChooser, br,
+ m_qbsStep->m_commandLine, br,
+ noMargin,
+ }.attachTo(this);
propertyEdit->setToolTip(QbsProjectManager::Tr::tr("Properties to pass to the project."));
defaultInstallDirCheckBox->setText(QbsProjectManager::Tr::tr("Use default location"));
diff --git a/src/plugins/qbsprojectmanager/qbsinstallstep.cpp b/src/plugins/qbsprojectmanager/qbsinstallstep.cpp
index 27c875e868..87fe3ae0f9 100644
--- a/src/plugins/qbsprojectmanager/qbsinstallstep.cpp
+++ b/src/plugins/qbsprojectmanager/qbsinstallstep.cpp
@@ -48,7 +48,7 @@ QbsInstallStep::QbsInstallStep(BuildStepList *bsl, Utils::Id id)
setDisplayName(Tr::tr("Qbs Install"));
setSummaryText(Tr::tr("<b>Qbs:</b> %1").arg("install"));
- const auto labelPlacement = BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel;
+ const auto labelPlacement = BoolAspect::LabelPlacement::AtCheckBox;
m_dryRun = addAspect<BoolAspect>();
m_dryRun->setSettingsKey(QBS_DRY_RUN);
m_dryRun->setLabel(Tr::tr("Dry run"), labelPlacement);
@@ -81,7 +81,7 @@ void QbsInstallStep::doRun()
QJsonObject request;
request.insert("type", "install-project");
- request.insert("install-root", installRoot());
+ request.insert("install-root", installRoot().path());
request.insert("clean-install-root", m_cleanInstallRoot->value());
request.insert("keep-going", m_keepGoing->value());
request.insert("dry-run", m_dryRun->value());
@@ -102,10 +102,10 @@ void QbsInstallStep::doCancel()
m_session->cancelCurrentJob();
}
-QString QbsInstallStep::installRoot() const
+FilePath QbsInstallStep::installRoot() const
{
const QbsBuildStep * const bs = buildConfig()->qbsStep();
- return bs ? bs->installRoot().toString() : QString();
+ return bs ? bs->installRoot() : FilePath();
}
const QbsBuildConfiguration *QbsInstallStep::buildConfig() const
@@ -162,7 +162,7 @@ QWidget *QbsInstallStep::createConfigWidget()
{
auto widget = new QWidget;
- auto installRootValueLabel = new QLabel(installRoot());
+ auto installRootValueLabel = new QLabel(installRoot().toUserOutput());
auto commandLineKeyLabel = new QLabel(Tr::tr("Equivalent command line:"));
commandLineKeyLabel->setAlignment(Qt::AlignTop);
@@ -172,18 +172,15 @@ QWidget *QbsInstallStep::createConfigWidget()
commandLineTextEdit->setTextInteractionFlags(Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse);
commandLineTextEdit->setMinimumHeight(QFontMetrics(widget->font()).height() * 8);
- Layouting::Form builder;
- builder.addRow({Tr::tr("Install root:"), installRootValueLabel});
- builder.addRow(Tr::tr("Flags:"));
- m_dryRun->addToLayout(builder);
- m_keepGoing->addToLayout(builder);
- m_cleanInstallRoot->addToLayout(builder);
-
- builder.addRow({commandLineKeyLabel, commandLineTextEdit});
- builder.attachTo(widget);
+ using namespace Layouting;
+ Form {
+ Tr::tr("Install root:"), installRootValueLabel, br,
+ Tr::tr("Flags:"), m_dryRun, m_keepGoing, m_cleanInstallRoot, br,
+ commandLineKeyLabel, commandLineTextEdit
+ }.attachTo(widget);
const auto updateState = [this, commandLineTextEdit, installRootValueLabel] {
- installRootValueLabel->setText(installRoot());
+ installRootValueLabel->setText(installRoot().toUserOutput());
commandLineTextEdit->setPlainText(buildConfig()->equivalentCommandLine(stepData()));
};
diff --git a/src/plugins/qbsprojectmanager/qbsinstallstep.h b/src/plugins/qbsprojectmanager/qbsinstallstep.h
index 7eef7aa9e1..e0063da818 100644
--- a/src/plugins/qbsprojectmanager/qbsinstallstep.h
+++ b/src/plugins/qbsprojectmanager/qbsinstallstep.h
@@ -25,7 +25,7 @@ public:
QbsInstallStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id);
~QbsInstallStep() override;
- QString installRoot() const;
+ Utils::FilePath installRoot() const;
QbsBuildStepData stepData() const;
private:
diff --git a/src/plugins/qbsprojectmanager/qbskitinformation.cpp b/src/plugins/qbsprojectmanager/qbskitinformation.cpp
index 8bdd801261..b9cb79231d 100644
--- a/src/plugins/qbsprojectmanager/qbskitinformation.cpp
+++ b/src/plugins/qbsprojectmanager/qbskitinformation.cpp
@@ -35,11 +35,11 @@ private:
void makeReadOnly() override { m_changeButton->setEnabled(false); }
void refresh() override { m_contentLabel->setText(QbsKitAspect::representation(kit())); }
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &parent) override
{
addMutableAction(m_contentLabel);
- builder.addItem(m_contentLabel);
- builder.addItem(m_changeButton);
+ parent.addItem(m_contentLabel);
+ parent.addItem(m_changeButton);
}
void changeProperties()
diff --git a/src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp b/src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp
index 55ae7d8ac0..82e896e77a 100644
--- a/src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp
+++ b/src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp
@@ -42,6 +42,10 @@ static FileType fileType(const QJsonObject &artifact)
return FileType::StateChart;
if (fileTags.contains("qt.qml.qml"))
return FileType::QML;
+ if (fileTags.contains("application"))
+ return FileType::App;
+ if (fileTags.contains("staticlibrary") || fileTags.contains("dynamiclibrary"))
+ return FileType::Lib;
return FileType::Unknown;
}
diff --git a/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp b/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp
index c5e658ba54..2d952513ab 100644
--- a/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp
+++ b/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp
@@ -18,8 +18,8 @@
#include <qmljstools/qmljstoolsconstants.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QCryptographicHash>
#include <QJSEngine>
@@ -223,7 +223,7 @@ QString QbsProfileManager::runQbsConfig(QbsConfigOp op, const QString &key, cons
const Utils::FilePath qbsConfigExe = QbsSettings::qbsConfigFilePath();
if (qbsConfigExe.isEmpty() || !qbsConfigExe.exists())
return {};
- Utils::QtcProcess qbsConfig;
+ Utils::Process qbsConfig;
qbsConfig.setCommand({qbsConfigExe, args});
qbsConfig.start();
if (!qbsConfig.waitForFinished(5000)) {
diff --git a/src/plugins/qbsprojectmanager/qbsprofilessettingspage.cpp b/src/plugins/qbsprojectmanager/qbsprofilessettingspage.cpp
index 64c73ed5f0..fb048f9475 100644
--- a/src/plugins/qbsprojectmanager/qbsprofilessettingspage.cpp
+++ b/src/plugins/qbsprojectmanager/qbsprofilessettingspage.cpp
@@ -26,8 +26,7 @@
using namespace ProjectExplorer;
-namespace QbsProjectManager {
-namespace Internal {
+namespace QbsProjectManager::Internal {
class ProfileTreeItem : public Utils::TypedTreeItem<ProfileTreeItem, ProfileTreeItem>
{
@@ -53,7 +52,6 @@ private:
class ProfileModel : public Utils::TreeModel<ProfileTreeItem>
{
- Q_OBJECT
public:
ProfileModel() : TreeModel(static_cast<QObject *>(nullptr))
{
@@ -91,13 +89,14 @@ public:
}
};
-class QbsProfilesSettingsWidget : public QWidget
+class QbsProfilesSettingsWidget : public Core::IOptionsPageWidget
{
- Q_OBJECT
public:
QbsProfilesSettingsWidget();
private:
+ void apply() final {}
+
void refreshKitsList();
void displayCurrentProfile();
@@ -112,19 +111,7 @@ QbsProfilesSettingsPage::QbsProfilesSettingsPage()
setId("Y.QbsProfiles");
setDisplayName(Tr::tr("Profiles"));
setCategory(Constants::QBS_SETTINGS_CATEGORY);
-}
-
-QWidget *QbsProfilesSettingsPage::widget()
-{
- if (!m_widget)
- m_widget = new QbsProfilesSettingsWidget;
- return m_widget;
-}
-
-void QbsProfilesSettingsPage::finish()
-{
- delete m_widget;
- m_widget = nullptr;
+ setWidgetCreator([] { return new QbsProfilesSettingsWidget; });
}
QbsProfilesSettingsWidget::QbsProfilesSettingsWidget()
@@ -133,7 +120,7 @@ QbsProfilesSettingsWidget::QbsProfilesSettingsWidget()
m_profileValueLabel = new QLabel;
m_propertiesView = new QTreeView;
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Form {
Tr::tr("Kit:"), m_kitsComboBox, br,
@@ -211,7 +198,4 @@ void QbsProfilesSettingsWidget::displayCurrentProfile()
}
}
-} // namespace Internal
-} // namespace QbsProjectManager
-
-#include "qbsprofilessettingspage.moc"
+} // QbsProjectManager::Internal
diff --git a/src/plugins/qbsprojectmanager/qbsprofilessettingspage.h b/src/plugins/qbsprojectmanager/qbsprofilessettingspage.h
index 4ffab9bce9..db63e32354 100644
--- a/src/plugins/qbsprojectmanager/qbsprofilessettingspage.h
+++ b/src/plugins/qbsprojectmanager/qbsprofilessettingspage.h
@@ -5,22 +5,12 @@
#include <coreplugin/dialogs/ioptionspage.h>
-namespace QbsProjectManager {
-namespace Internal {
-class QbsProfilesSettingsWidget;
+namespace QbsProjectManager::Internal {
class QbsProfilesSettingsPage : public Core::IOptionsPage
{
public:
QbsProfilesSettingsPage();
-
-private:
- QWidget *widget() override;
- void apply() override { }
- void finish() override;
-
- QbsProfilesSettingsWidget *m_widget = nullptr;
};
-} // namespace Internal
-} // namespace QbsProjectManager
+} // QbsProjectManager::Internal
diff --git a/src/plugins/qbsprojectmanager/qbsproject.cpp b/src/plugins/qbsprojectmanager/qbsproject.cpp
index dc72660e7a..3b59618310 100644
--- a/src/plugins/qbsprojectmanager/qbsproject.cpp
+++ b/src/plugins/qbsprojectmanager/qbsproject.cpp
@@ -41,10 +41,10 @@
#include <projectexplorer/taskhub.h>
#include <projectexplorer/toolchain.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <qmljstools/qmljsmodelmanager.h>
#include <qtsupport/qtcppkitinfo.h>
@@ -483,7 +483,7 @@ void QbsBuildSystem::updateProjectNodes(const std::function<void ()> &continuati
if (continuation)
continuation();
});
- m_treeCreationWatcher->setFuture(runAsync(ProjectExplorerPlugin::sharedThreadPool(),
+ m_treeCreationWatcher->setFuture(Utils::asyncRun(ProjectExplorerPlugin::sharedThreadPool(),
QThread::LowPriority, &QbsNodeTreeBuilder::buildTree,
project()->displayName(), project()->projectFilePath(), project()->projectDirectory(),
projectData()));
@@ -498,7 +498,7 @@ FilePath QbsBuildSystem::installRoot()
if (!step->enabled())
continue;
if (const auto qbsInstallStep = qobject_cast<const QbsInstallStep *>(step))
- return FilePath::fromUserInput(qbsInstallStep->installRoot());
+ return qbsInstallStep->installRoot();
}
}
const QbsBuildStep * const buildStep = m_buildConfiguration->qbsStep();
@@ -744,6 +744,7 @@ static void getExpandedCompilerFlags(QStringList &cFlags, QStringList &cxxFlags,
};
const QJsonValue &enableExceptions = getCppProp("enableExceptions");
const QJsonValue &enableRtti = getCppProp("enableRtti");
+ const QString warningLevel = getCppProp("warningLevel").toString();
QStringList commonFlags = arrayToStringList(getCppProp("platformCommonCompilerFlags"));
commonFlags << arrayToStringList(getCppProp("commonCompilerFlags"))
<< arrayToStringList(getCppProp("platformDriverFlags"))
@@ -773,6 +774,10 @@ static void getExpandedCompilerFlags(QStringList &cFlags, QStringList &cxxFlags,
if (!machineType.isEmpty())
commonFlags << ("-march=" + machineType);
}
+ if (warningLevel == "all")
+ commonFlags << "-Wall" << "-Wextra";
+ else if (warningLevel == "none")
+ commonFlags << "-w";
const QStringList targetOS = arrayToStringList(properties.value("qbs.targetOS"));
if (targetOS.contains("unix")) {
const QVariant positionIndependentCode = getCppProp("positionIndependentCode");
@@ -837,6 +842,10 @@ static void getExpandedCompilerFlags(QStringList &cFlags, QStringList &cxxFlags,
else if (exceptionModel == "externc")
commonFlags << "/EHs";
}
+ if (warningLevel == "all")
+ commonFlags << "/Wall";
+ else if (warningLevel == "none")
+ commonFlags << "/w";
cFlags = cxxFlags = commonFlags;
cFlags << "/TC";
cxxFlags << "/TP";
diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp
index 01a58753a2..3245b609be 100644
--- a/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp
+++ b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp
@@ -33,7 +33,7 @@
#include <projectexplorer/projectexplorericons.h>
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qtsupport/qtsupportconstants.h>
@@ -60,7 +60,7 @@ static Node *currentEditorNode()
static QbsProject *currentEditorProject()
{
Core::IDocument *doc = Core::EditorManager::currentDocument();
- return doc ? qobject_cast<QbsProject *>(SessionManager::projectForFile(doc->filePath())) : nullptr;
+ return doc ? qobject_cast<QbsProject *>(ProjectManager::projectForFile(doc->filePath())) : nullptr;
}
class QbsProjectManagerPluginPrivate
@@ -226,13 +226,13 @@ void QbsProjectManagerPlugin::initialize()
connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
this, &QbsProjectManagerPlugin::updateBuildActions);
- connect(SessionManager::instance(), &SessionManager::targetAdded,
+ connect(ProjectManager::instance(), &ProjectManager::targetAdded,
this, &QbsProjectManagerPlugin::targetWasAdded);
- connect(SessionManager::instance(), &SessionManager::targetRemoved,
+ connect(ProjectManager::instance(), &ProjectManager::targetRemoved,
this, &QbsProjectManagerPlugin::updateBuildActions);
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, &QbsProjectManagerPlugin::updateReparseQbsAction);
- connect(SessionManager::instance(), &SessionManager::projectAdded,
+ connect(ProjectManager::instance(), &ProjectManager::projectAdded,
this, [this](Project *project) {
auto qbsProject = qobject_cast<QbsProject *>(project);
connect(project, &Project::anyParsingStarted,
@@ -283,7 +283,7 @@ void QbsProjectManagerPlugin::updateContextActions(Node *node)
void QbsProjectManagerPlugin::updateReparseQbsAction()
{
- auto project = qobject_cast<QbsProject *>(SessionManager::startupProject());
+ auto project = qobject_cast<QbsProject *>(ProjectManager::startupProject());
m_reparseQbs->setEnabled(project
&& !BuildManager::isBuilding(project)
&& project && project->activeTarget()
@@ -342,7 +342,7 @@ void QbsProjectManagerPlugin::projectChanged(QbsProject *project)
{
auto qbsProject = qobject_cast<QbsProject *>(project);
- if (!qbsProject || qbsProject == SessionManager::startupProject())
+ if (!qbsProject || qbsProject == ProjectManager::startupProject())
updateReparseQbsAction();
if (!qbsProject || qbsProject == ProjectTree::currentProject())
@@ -537,7 +537,7 @@ void QbsProjectManagerPlugin::reparseSelectedProject()
void QbsProjectManagerPlugin::reparseCurrentProject()
{
- reparseProject(dynamic_cast<QbsProject *>(SessionManager::startupProject()));
+ reparseProject(dynamic_cast<QbsProject *>(ProjectManager::startupProject()));
}
void QbsProjectManagerPlugin::reparseProject(QbsProject *project)
diff --git a/src/plugins/qbsprojectmanager/qbsprojectparser.cpp b/src/plugins/qbsprojectmanager/qbsprojectparser.cpp
index 1620b747ac..ee1aafdb6b 100644
--- a/src/plugins/qbsprojectmanager/qbsprojectparser.cpp
+++ b/src/plugins/qbsprojectmanager/qbsprojectparser.cpp
@@ -67,10 +67,10 @@ void QbsProjectParser::parse(const QVariantMap &config, const Environment &env,
request.insert("override-build-graph-data", true);
static const auto envToJson = [](const Environment &env) {
QJsonObject envObj;
- for (auto it = env.constBegin(); it != env.constEnd(); ++it) {
- if (env.isEnabled(it))
- envObj.insert(env.key(it), env.value(it));
- }
+ env.forEachEntry([&](const QString &key, const QString &value, bool enabled) {
+ if (enabled)
+ envObj.insert(key, value);
+ });
return envObj;
};
request.insert("environment", envToJson(env));
diff --git a/src/plugins/qbsprojectmanager/qbssession.cpp b/src/plugins/qbsprojectmanager/qbssession.cpp
index 06c605e842..13121cfb66 100644
--- a/src/plugins/qbsprojectmanager/qbssession.cpp
+++ b/src/plugins/qbsprojectmanager/qbssession.cpp
@@ -14,8 +14,8 @@
#include <projectexplorer/taskhub.h>
#include <utils/algorithm.h>
#include <utils/environment.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDir>
#include <QEventLoop>
@@ -129,7 +129,7 @@ private:
class QbsSession::Private
{
public:
- QtcProcess *qbsProcess = nullptr;
+ Process *qbsProcess = nullptr;
PacketReader *packetReader = nullptr;
QJsonObject currentRequest;
QJsonObject projectData;
@@ -152,16 +152,16 @@ void QbsSession::initialize()
d->packetReader = new PacketReader(this);
- d->qbsProcess = new QtcProcess(this);
+ d->qbsProcess = new Process(this);
d->qbsProcess->setProcessMode(ProcessMode::Writer);
d->qbsProcess->setEnvironment(env);
- connect(d->qbsProcess, &QtcProcess::readyReadStandardOutput, this, [this] {
+ connect(d->qbsProcess, &Process::readyReadStandardOutput, this, [this] {
d->packetReader->handleData(d->qbsProcess->readAllRawStandardOutput());
});
- connect(d->qbsProcess, &QtcProcess::readyReadStandardError, this, [this] {
+ connect(d->qbsProcess, &Process::readyReadStandardError, this, [this] {
qCDebug(qbsPmLog) << "[qbs stderr]: " << d->qbsProcess->readAllRawStandardError();
});
- connect(d->qbsProcess, &QtcProcess::done, this, [this] {
+ connect(d->qbsProcess, &Process::done, this, [this] {
if (d->qbsProcess->result() == ProcessResult::StartFailed) {
d->eventLoop.exit(1);
setError(Error::QbsFailedToStart);
@@ -370,6 +370,7 @@ void QbsSession::insertRequestedModuleProperties(QJsonObject &request)
"cpp.useCxxPrecompiledHeader",
"cpp.useObjcPrecompiledHeader",
"cpp.useObjcxxPrecompiledHeader",
+ "cpp.warningLevel",
"qbs.architecture",
"qbs.architectures",
"qbs.sysroot",
diff --git a/src/plugins/qbsprojectmanager/qbssettings.cpp b/src/plugins/qbsprojectmanager/qbssettings.cpp
index 78eac2b308..91e4763bf2 100644
--- a/src/plugins/qbsprojectmanager/qbssettings.cpp
+++ b/src/plugins/qbsprojectmanager/qbssettings.cpp
@@ -12,7 +12,7 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/pathchooser.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QCoreApplication>
#include <QCheckBox>
@@ -21,8 +21,7 @@
using namespace Utils;
-namespace QbsProjectManager {
-namespace Internal {
+namespace QbsProjectManager::Internal {
const char QBS_EXE_KEY[] = "QbsProjectManager/QbsExecutable";
const char QBS_DEFAULT_INSTALL_DIR_KEY[] = "QbsProjectManager/DefaultInstallDir";
@@ -32,7 +31,7 @@ static QString getQbsVersion(const FilePath &qbsExe)
{
if (qbsExe.isEmpty() || !qbsExe.exists())
return {};
- QtcProcess qbsProc;
+ Process qbsProc;
qbsProc.setCommand({qbsExe, {"--version"}});
qbsProc.start();
if (!qbsProc.waitForFinished(5000) || qbsProc.exitCode() != 0)
@@ -142,10 +141,10 @@ void QbsSettings::storeSettings() const
s->setValue(USE_CREATOR_SETTINGS_KEY, m_settings.useCreatorSettings);
}
-class QbsSettingsPage::SettingsWidget : public QWidget
+class QbsSettingsPageWidget : public Core::IOptionsPageWidget
{
public:
- SettingsWidget()
+ QbsSettingsPageWidget()
{
m_qbsExePathChooser.setExpectedKind(PathChooser::ExistingCommand);
m_qbsExePathChooser.setFilePath(QbsSettings::qbsExecutableFilePath());
@@ -166,7 +165,7 @@ public:
});
}
- void apply()
+ void apply() final
{
QbsSettingsData settings = QbsSettings::rawSettingsData();
if (m_qbsExePathChooser.filePath() != QbsSettings::qbsExecutableFilePath())
@@ -197,26 +196,7 @@ QbsSettingsPage::QbsSettingsPage()
setCategory(Constants::QBS_SETTINGS_CATEGORY);
setDisplayCategory(Tr::tr(Constants::QBS_SETTINGS_TR_CATEGORY));
setCategoryIconPath(":/qbsprojectmanager/images/settingscategory_qbsprojectmanager.png");
+ setWidgetCreator([] { return new QbsSettingsPageWidget; });
}
-QWidget *QbsSettingsPage::widget()
-{
- if (!m_widget)
- m_widget = new SettingsWidget;
- return m_widget;
-
-}
-
-void QbsSettingsPage::apply()
-{
- if (m_widget)
- m_widget->apply();
-}
-
-void QbsSettingsPage::finish()
-{
- delete m_widget;
-}
-
-} // namespace Internal
-} // namespace QbsProjectManager
+} // QbsProjectManager::Internal
diff --git a/src/plugins/qbsprojectmanager/qbssettings.h b/src/plugins/qbsprojectmanager/qbssettings.h
index 8b30e4b247..a91a905965 100644
--- a/src/plugins/qbsprojectmanager/qbssettings.h
+++ b/src/plugins/qbsprojectmanager/qbssettings.h
@@ -4,16 +4,15 @@
#pragma once
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/fileutils.h>
-#include <QObject>
-#include <QPointer>
+#include <utils/filepath.h>
+
#include <QVersionNumber>
-namespace QbsProjectManager {
-namespace Internal {
+namespace QbsProjectManager::Internal {
-class QbsSettingsData {
+class QbsSettingsData
+{
public:
Utils::FilePath qbsExecutableFilePath;
QString defaultInstallDirTemplate;
@@ -51,18 +50,8 @@ private:
class QbsSettingsPage : public Core::IOptionsPage
{
- Q_OBJECT
public:
QbsSettingsPage();
-
-private:
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
- class SettingsWidget;
- QPointer<SettingsWidget> m_widget;
};
-} // namespace Internal
-} // namespace QbsProjectManager
+} // QbsProjectManager::Internal
diff --git a/src/plugins/qmakeprojectmanager/addlibrarywizard.cpp b/src/plugins/qmakeprojectmanager/addlibrarywizard.cpp
index ebf266d701..ff74701cf7 100644
--- a/src/plugins/qmakeprojectmanager/addlibrarywizard.cpp
+++ b/src/plugins/qmakeprojectmanager/addlibrarywizard.cpp
@@ -178,7 +178,6 @@ DetailsPage::DetailsPage(AddLibraryWizard *parent)
: QWizardPage(parent), m_libraryWizard(parent)
{
m_libraryDetailsWidget = new LibraryDetailsWidget(this);
- resize(456, 438);
PathChooser * const libPathChooser = m_libraryDetailsWidget->libraryPathChooser;
libPathChooser->setHistoryCompleter("Qmake.LibDir.History");
diff --git a/src/plugins/qmakeprojectmanager/customwidgetwizard/classdefinition.cpp b/src/plugins/qmakeprojectmanager/customwidgetwizard/classdefinition.cpp
index 05c7c25d78..22958e0819 100644
--- a/src/plugins/qmakeprojectmanager/customwidgetwizard/classdefinition.cpp
+++ b/src/plugins/qmakeprojectmanager/customwidgetwizard/classdefinition.cpp
@@ -21,7 +21,7 @@ ClassDefinition::ClassDefinition(QWidget *parent) :
QTabWidget(parent),
m_domXmlChanged(false)
{
- using namespace Utils::Layouting;
+ using namespace Layouting;
// "Sources" tab
auto sourceTab = new QWidget;
diff --git a/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetpluginwizardpage.cpp b/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetpluginwizardpage.cpp
index 851f07ce71..d0885dfc83 100644
--- a/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetpluginwizardpage.cpp
+++ b/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetpluginwizardpage.cpp
@@ -34,7 +34,7 @@ CustomWidgetPluginWizardPage::CustomWidgetPluginWizardPage(QWidget *parent) :
m_pluginNameEdit = new QLineEdit;
m_resourceFileEdit = new QLineEdit(Tr::tr("icons.qrc"));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Tr::tr("Specify the properties of the plugin library and the collection class."),
Space(10),
diff --git a/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwidgetswizardpage.cpp b/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwidgetswizardpage.cpp
index 8265f0a8b4..20fe10ac13 100644
--- a/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwidgetswizardpage.cpp
+++ b/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwidgetswizardpage.cpp
@@ -39,7 +39,7 @@ CustomWidgetWidgetsWizardPage::CustomWidgetWidgetsWizardPage(QWidget *parent) :
dummy->setEnabled(false);
m_tabStackLayout->addWidget(dummy);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Tr::tr("Specify the list of custom widgets and their properties."),
Space(10),
diff --git a/src/plugins/qmakeprojectmanager/customwidgetwizard/plugingenerator.cpp b/src/plugins/qmakeprojectmanager/customwidgetwizard/plugingenerator.cpp
index ab3f59d851..28e67ec3ec 100644
--- a/src/plugins/qmakeprojectmanager/customwidgetwizard/plugingenerator.cpp
+++ b/src/plugins/qmakeprojectmanager/customwidgetwizard/plugingenerator.cpp
@@ -49,17 +49,10 @@ static Core::GeneratedFile generateIconFile(const FilePath &source,
return rc;
}
-static QString qt4PluginExport(const QString &pluginName, const QString &pluginClassName)
-{
- return QLatin1String("#if QT_VERSION < 0x050000\nQ_EXPORT_PLUGIN2(")
- + pluginName + QLatin1String(", ") + pluginClassName
- + QLatin1String(")\n#endif // QT_VERSION < 0x050000");
-}
-
static QString qt5PluginMetaData(const QString &interfaceName)
{
- return QLatin1String("#if QT_VERSION >= 0x050000\n Q_PLUGIN_METADATA(IID \"org.qt-project.Qt.")
- + interfaceName + QLatin1String("\")\n#endif // QT_VERSION >= 0x050000");
+ return QLatin1String(" Q_PLUGIN_METADATA(IID \"org.qt-project.Qt.")
+ + interfaceName + QLatin1String("\")");
}
QList<Core::GeneratedFile> PluginGenerator::generatePlugin(const GenerationParameters& p, const PluginOptions &options,
@@ -121,10 +114,8 @@ QList<Core::GeneratedFile> PluginGenerator::generatePlugin(const GenerationPara
sm.insert(QLatin1String("WIDGET_TOOLTIP"), cStringQuote(wo.toolTip));
sm.insert(QLatin1String("WIDGET_WHATSTHIS"), cStringQuote(wo.whatsThis));
sm.insert(QLatin1String("WIDGET_ISCONTAINER"), wo.isContainer ? QLatin1String("true") : QLatin1String("false"));
- sm.insert(QLatin1String("WIDGET_DOMXML"), cStringQuote(wo.domXml));
- sm.insert(QLatin1String("SINGLE_PLUGIN_EXPORT"),
- options.widgetOptions.count() == 1 ?
- qt4PluginExport(options.pluginName, wo.pluginClassName) : QString());
+ sm.insert(QLatin1String("WIDGET_DOMXML"), QLatin1String("R\"(")
+ + wo.domXml.trimmed() + QLatin1String(")\""));
const QString pluginSourceContents = processTemplate(p.templatePath + QLatin1String("/tpl_single.cpp"), sm, errorMessage);
if (pluginSourceContents.isEmpty())
@@ -239,7 +230,6 @@ QList<Core::GeneratedFile> PluginGenerator::generatePlugin(const GenerationPara
options.collectionHeaderFile +
QLatin1String("\""));
sm.insert(QLatin1String("PLUGIN_ADDITIONS"), pluginAdditions);
- sm.insert(QLatin1String("COLLECTION_PLUGIN_EXPORT"), qt4PluginExport(options.pluginName, options.collectionClassName));
const QString collectionSourceFileContents = processTemplate(p.templatePath + QLatin1String("/tpl_collection.cpp"), sm, errorMessage);
if (collectionSourceFileContents.isEmpty())
return QList<Core::GeneratedFile>();
diff --git a/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp b/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp
index b1427c84ba..800d7350c1 100644
--- a/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp
+++ b/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp
@@ -3,24 +3,25 @@
#include "librarydetailscontroller.h"
-#include "qmakebuildconfiguration.h"
#include "qmakeparsernodes.h"
#include "qmakeproject.h"
#include "qmakeprojectmanagertr.h"
+#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QCheckBox>
#include <QComboBox>
#include <QDir>
#include <QFileInfo>
#include <QGroupBox>
+#include <QLabel>
#include <QRadioButton>
#include <QTextStream>
@@ -868,7 +869,7 @@ QString PackageLibraryDetailsController::snippet() const
bool PackageLibraryDetailsController::isLinkPackageGenerated() const
{
- const Project *project = SessionManager::projectForFile(proFile());
+ const Project *project = ProjectManager::projectForFile(proFile());
if (!project)
return false;
@@ -1016,7 +1017,7 @@ void InternalLibraryDetailsController::updateProFile()
libraryDetailsWidget()->libraryComboBox->clear();
const QmakeProject *project
- = dynamic_cast<QmakeProject *>(SessionManager::projectForFile(proFile()));
+ = dynamic_cast<QmakeProject *>(ProjectManager::projectForFile(proFile()));
if (!project)
return;
@@ -1104,7 +1105,7 @@ QString InternalLibraryDetailsController::snippet() const
// the build directory of the active build configuration
QDir rootBuildDir = rootDir; // If the project is unconfigured use the project dir
- if (const Project *project = SessionManager::projectForFile(proFile())) {
+ if (const Project *project = ProjectManager::projectForFile(proFile())) {
if (ProjectExplorer::Target *t = project->activeTarget())
if (ProjectExplorer::BuildConfiguration *bc = t->activeBuildConfiguration())
rootBuildDir.setPath(bc->buildDirectory().toString());
diff --git a/src/plugins/qmakeprojectmanager/makefileparse.cpp b/src/plugins/qmakeprojectmanager/makefileparse.cpp
index e9d8c2878e..990fd0ff41 100644
--- a/src/plugins/qmakeprojectmanager/makefileparse.cpp
+++ b/src/plugins/qmakeprojectmanager/makefileparse.cpp
@@ -5,7 +5,7 @@
#include <qtsupport/qtversionmanager.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QDebug>
#include <QDir>
@@ -359,11 +359,12 @@ void MakeFileParse::parseCommandLine(const QString &command, const QString &proj
// Unit tests:
#ifdef WITH_TESTS
-# include <QTest>
-# include "qmakeprojectmanagerplugin.h"
+#include "qmakeprojectmanagerplugin.h"
-# include "projectexplorer/outputparser_test.h"
+#include <projectexplorer/outputparser_test.h>
+
+#include <QTest>
using namespace QmakeProjectManager::Internal;
using namespace ProjectExplorer;
@@ -479,8 +480,8 @@ void QmakeProjectManagerPlugin::testMakefileParser()
MakeFileParse parser("/tmp/something", MakeFileParse::Mode::FilterKnownConfigValues);
parser.parseCommandLine(command, project);
- QCOMPARE(ProcessArgs::splitArgs(parser.unparsedArguments()),
- ProcessArgs::splitArgs(unparsedArguments));
+ QCOMPARE(ProcessArgs::splitArgs(parser.unparsedArguments(), HostOsInfo::hostOs()),
+ ProcessArgs::splitArgs(unparsedArguments, HostOsInfo::hostOs()));
QCOMPARE(parser.effectiveBuildConfig({}), effectiveBuildConfig);
const QMakeStepConfig qmsc = parser.config();
diff --git a/src/plugins/qmakeprojectmanager/profileeditor.cpp b/src/plugins/qmakeprojectmanager/profileeditor.cpp
index 810d5d4f2e..c17d3d9193 100644
--- a/src/plugins/qmakeprojectmanager/profileeditor.cpp
+++ b/src/plugins/qmakeprojectmanager/profileeditor.cpp
@@ -7,18 +7,22 @@
#include "profilehighlighter.h"
#include "profilehoverhandler.h"
#include "qmakenodes.h"
-#include "qmakeproject.h"
#include "qmakeprojectmanagerconstants.h"
#include <coreplugin/coreplugintr.h>
+
#include <extensionsystem/pluginmanager.h>
+
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
+
#include <qtsupport/qtsupportconstants.h>
+
#include <texteditor/textdocument.h>
#include <texteditor/texteditoractionhandler.h>
+
#include <utils/fsengine/fileiconprovider.h>
#include <utils/qtcassert.h>
#include <utils/theme/theme.h>
@@ -65,7 +69,7 @@ QString ProFileEditorWidget::checkForPrfFile(const QString &baseName) const
const QmakePriFileNode *projectNode = nullptr;
// FIXME: Remove this check once project nodes are fully "static".
- for (const Project * const project : SessionManager::projects()) {
+ for (const Project * const project : ProjectManager::projects()) {
static const auto isParsing = [](const Project *project) {
for (const Target * const t : project->targets()) {
for (const BuildConfiguration * const bc : t->buildConfigurations()) {
@@ -113,23 +117,22 @@ void ProFileEditorWidget::findLinkAt(const QTextCursor &cursor,
int line = 0;
int column = 0;
convertPosition(cursor.position(), &line, &column);
- const int positionInBlock = column - 1;
const QString block = cursor.block().text();
// check if the current position is commented out
const int hashPos = block.indexOf(QLatin1Char('#'));
- if (hashPos >= 0 && hashPos < positionInBlock)
+ if (hashPos >= 0 && hashPos < column)
return processLinkCallback(link);
// find the beginning of a filename
QString buffer;
- int beginPos = positionInBlock - 1;
- int endPos = positionInBlock;
+ int beginPos = column - 1;
+ int endPos = column;
// Check is cursor is somewhere on $${PWD}:
- const int chunkStart = std::max(0, positionInBlock - 7);
- const int chunkLength = 14 + std::min(0, positionInBlock - 7);
+ const int chunkStart = std::max(0, column - 7);
+ const int chunkLength = 14 + std::min(0, column - 7);
QString chunk = block.mid(chunkStart, chunkLength);
const QString curlyPwd = "$${PWD}";
@@ -141,7 +144,7 @@ void ProFileEditorWidget::findLinkAt(const QTextCursor &cursor,
if (posCurlyPwd >= 0) {
const int end = chunkStart + posCurlyPwd + curlyPwd.count();
const int start = chunkStart + posCurlyPwd;
- if (start <= positionInBlock && end >= positionInBlock) {
+ if (start <= column && end >= column) {
buffer = pwd;
beginPos = chunkStart + posCurlyPwd - 1;
endPos = end;
@@ -150,7 +153,7 @@ void ProFileEditorWidget::findLinkAt(const QTextCursor &cursor,
} else if (posPwd >= 0) {
const int end = chunkStart + posPwd + pwd.count();
const int start = chunkStart + posPwd;
- if (start <= positionInBlock && end >= positionInBlock) {
+ if (start <= column && end >= column) {
buffer = pwd;
beginPos = start - 1;
endPos = end;
@@ -225,8 +228,8 @@ void ProFileEditorWidget::findLinkAt(const QTextCursor &cursor,
link.targetFilePath = FilePath::fromString(checkForPrfFile(buffer));
}
if (!link.targetFilePath.isEmpty()) {
- link.linkTextStart = cursor.position() - positionInBlock + beginPos + 1;
- link.linkTextEnd = cursor.position() - positionInBlock + endPos;
+ link.linkTextStart = cursor.position() - column + beginPos + 1;
+ link.linkTextEnd = cursor.position() - column + endPos;
}
processLinkCallback(link);
}
diff --git a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
index 779d175b80..9da9f7f65a 100644
--- a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
@@ -37,8 +37,8 @@
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtversionmanager.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDebug>
#include <QInputDialog>
@@ -57,7 +57,8 @@ class RunSystemAspect : public TriStateAspect
{
Q_OBJECT
public:
- RunSystemAspect() : TriStateAspect(Tr::tr("Run"), Tr::tr("Ignore"), Tr::tr("Use global setting"))
+ RunSystemAspect()
+ : TriStateAspect(nullptr, Tr::tr("Run"), Tr::tr("Ignore"), Tr::tr("Use global setting"))
{
setSettingsKey("RunSystemFunction");
setDisplayName(Tr::tr("qmake system() behavior when parsing:"));
@@ -160,7 +161,7 @@ QmakeBuildConfiguration::QmakeBuildConfiguration(Target *target, Id id)
this, &QmakeBuildConfiguration::updateProblemLabel);
connect(this, &QmakeBuildConfiguration::qmakeBuildConfigurationChanged,
this, &QmakeBuildConfiguration::updateProblemLabel);
- connect(&QmakeSettings::instance(), &QmakeSettings::settingsChanged,
+ connect(&settings(), &AspectContainer::changed,
this, &QmakeBuildConfiguration::updateProblemLabel);
connect(target, &Target::parsingFinished, this, &QmakeBuildConfiguration::updateProblemLabel);
connect(target, &Target::kitChanged, this, &QmakeBuildConfiguration::updateProblemLabel);
@@ -267,7 +268,7 @@ void QmakeBuildConfiguration::updateProblemLabel()
}
}
- const bool unalignedBuildDir = QmakeSettings::warnAgainstUnalignedBuildDir()
+ const bool unalignedBuildDir = settings().warnAgainstUnalignedBuildDir()
&& !isBuildDirAtSafeLocation();
if (unalignedBuildDir)
allGood = false;
@@ -426,7 +427,7 @@ bool QmakeBuildConfiguration::runSystemFunction() const
return true;
if (runSystem == TriState::Disabled)
return false;
- return QmakeSettings::runSystemFunction();
+ return settings().runSystemFunction();
}
QStringList QmakeBuildConfiguration::configCommandLineArguments() const
@@ -753,7 +754,7 @@ QmakeBuildConfigurationFactory::QmakeBuildConfigurationFactory()
Tasks issues;
if (version)
issues << version->reportIssues(projectPath, buildDir);
- if (QmakeSettings::warnAgainstUnalignedBuildDir()
+ if (settings().warnAgainstUnalignedBuildDir()
&& !QmakeBuildConfiguration::isBuildDirAtSafeLocation(
projectPath.absolutePath(), buildDir.absoluteFilePath())) {
issues.append(BuildSystemTask(Task::Warning,
diff --git a/src/plugins/qmakeprojectmanager/qmakekitinformation.cpp b/src/plugins/qmakeprojectmanager/qmakekitinformation.cpp
index 06f2011244..c413758048 100644
--- a/src/plugins/qmakeprojectmanager/qmakekitinformation.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakekitinformation.cpp
@@ -40,10 +40,10 @@ public:
~QmakeKitAspectWidget() override { delete m_lineEdit; }
private:
- void addToLayout(Layouting::LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &parent) override
{
addMutableAction(m_lineEdit);
- builder.addItem(m_lineEdit);
+ parent.addItem(m_lineEdit);
}
void makeReadOnly() override { m_lineEdit->setEnabled(false); }
diff --git a/src/plugins/qmakeprojectmanager/qmakemakestep.cpp b/src/plugins/qmakeprojectmanager/qmakemakestep.cpp
index 0fdaecc3db..e72d346758 100644
--- a/src/plugins/qmakeprojectmanager/qmakemakestep.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakemakestep.cpp
@@ -22,7 +22,7 @@
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/xcodebuildparser.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/variablechooser.h>
#include <QDir>
@@ -222,7 +222,7 @@ void QmakeMakeStep::doRun()
void QmakeMakeStep::finish(ProcessResult result)
{
if (!isSuccess(result) && !isCanceled() && m_unalignedBuildDir
- && QmakeSettings::warnAgainstUnalignedBuildDir()) {
+ && settings().warnAgainstUnalignedBuildDir()) {
const QString msg = Tr::tr("The build directory is not at the same level as the source "
"directory, which could be the reason for the build failure.");
emit addTask(BuildSystemTask(Task::Warning, msg));
diff --git a/src/plugins/qmakeprojectmanager/qmakenodes.cpp b/src/plugins/qmakeprojectmanager/qmakenodes.cpp
index 4f59d223ce..144a9cc0c9 100644
--- a/src/plugins/qmakeprojectmanager/qmakenodes.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakenodes.cpp
@@ -115,9 +115,7 @@ bool QmakeBuildSystem::supportsAction(Node *context, ProjectAction action, const
const FolderNode *folder = node->asFolderNode();
if (folder) {
FilePaths list;
- const auto folderNodes = folder->folderNodes();
- for (FolderNode *f : folderNodes)
- list << f->filePath();
+ folder->forEachFolderNode([&](FolderNode *f) { list << f->filePath(); });
if (n->deploysFolder(FileUtils::commonPath(list).toString()))
addExistingFiles = false;
}
diff --git a/src/plugins/qmakeprojectmanager/qmakenodes.h b/src/plugins/qmakeprojectmanager/qmakenodes.h
index 778f84b64f..7dc1133fd0 100644
--- a/src/plugins/qmakeprojectmanager/qmakenodes.h
+++ b/src/plugins/qmakeprojectmanager/qmakenodes.h
@@ -12,7 +12,6 @@
namespace QmakeProjectManager {
class QmakeProFileNode;
-class QmakeProject;
// Implements ProjectNode for qmake .pri files
class QMAKEPROJECTMANAGER_EXPORT QmakePriFileNode : public ProjectExplorer::ProjectNode
diff --git a/src/plugins/qmakeprojectmanager/qmakenodetreebuilder.cpp b/src/plugins/qmakeprojectmanager/qmakenodetreebuilder.cpp
index f261594e59..528b18c95e 100644
--- a/src/plugins/qmakeprojectmanager/qmakenodetreebuilder.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakenodetreebuilder.cpp
@@ -195,14 +195,29 @@ static void createTree(QmakeBuildSystem *buildSystem,
fileNode->setEnabled(fn.second == FileOrigin::ExactParse);
vfolder->addNestedNode(std::move(fileNode));
}
- for (FolderNode *fn : vfolder->folderNodes())
- fn->compress();
+ vfolder->forEachFolderNode([](FolderNode *fn) { fn->compress(); });
}
node->addNode(std::move(vfolder));
}
}
- if (!generatedFiles.empty()) {
+ FileType targetFileType = FileType::Unknown;
+ FilePath targetBinary;
+ if (proFile && proFile->targetInformation().valid) {
+ if (proFile->projectType() == ProjectType::ApplicationTemplate) {
+ targetFileType = FileType::App;
+ targetBinary = buildSystem->executableFor(proFile);
+ } else if (proFile->projectType() == ProjectType::SharedLibraryTemplate
+ || proFile->projectType() == ProjectType::StaticLibraryTemplate) {
+ targetFileType = FileType::Lib;
+ const FilePaths libs = Utils::sorted(buildSystem->allLibraryTargetFiles(proFile),
+ [](const FilePath &fp1, const FilePath &fp2) {
+ return fp1.fileName().length() < fp2.fileName().length(); });
+ if (!libs.isEmpty())
+ targetBinary = libs.last(); // Longest file name is the one that's not a symlink.
+ }
+ }
+ if (!generatedFiles.empty() || !targetBinary.isEmpty()) {
QTC_CHECK(proFile);
const FilePath baseDir = generatedFiles.size() == 1 ? generatedFiles.first().parentDir()
: buildSystem->buildDir(proFile->filePath());
@@ -214,6 +229,11 @@ static void createTree(QmakeBuildSystem *buildSystem,
fileNode->setIsGenerated(true);
genFolder->addNestedNode(std::move(fileNode));
}
+ if (!targetBinary.isEmpty()) {
+ auto targetFileNode = std::make_unique<FileNode>(targetBinary, targetFileType);
+ targetFileNode->setIsGenerated(true);
+ genFolder->addNestedNode(std::move(targetFileNode));
+ }
node->addNode(std::move(genFolder));
}
diff --git a/src/plugins/qmakeprojectmanager/qmakeparser.cpp b/src/plugins/qmakeprojectmanager/qmakeparser.cpp
index e95b403bff..2cb9d941c1 100644
--- a/src/plugins/qmakeprojectmanager/qmakeparser.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeparser.cpp
@@ -71,11 +71,12 @@ OutputLineParser::Result QMakeParser::handleLine(const QString &line, OutputForm
// Unit tests:
#ifdef WITH_TESTS
-# include <QTest>
-# include "qmakeprojectmanagerplugin.h"
+#include "qmakeprojectmanagerplugin.h"
-# include "projectexplorer/outputparser_test.h"
+#include <projectexplorer/outputparser_test.h>
+
+#include <QTest>
using namespace QmakeProjectManager::Internal;
diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
index e15fadb46d..31a55faab0 100644
--- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
@@ -30,10 +30,11 @@
#include <utils/QtConcurrentTools>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/filesystemwatcher.h>
#include <utils/mimeutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/temporarydirectory.h>
@@ -1284,9 +1285,9 @@ void QmakeProFile::asyncUpdate()
if (!includedInExactParse())
m_readerExact->setExact(false);
QmakeEvalInput input = evalInput();
- QFuture<QmakeEvalResultPtr> future = runAsync(ProjectExplorerPlugin::sharedThreadPool(),
- QThread::LowestPriority,
- &QmakeProFile::asyncEvaluate, this, input);
+ QFuture<QmakeEvalResultPtr> future = Utils::asyncRun(ProjectExplorerPlugin::sharedThreadPool(),
+ QThread::LowestPriority,
+ &QmakeProFile::asyncEvaluate, this, input);
m_parseFutureWatcher->setFuture(future);
}
@@ -1630,9 +1631,9 @@ QmakeEvalResultPtr QmakeProFile::evaluate(const QmakeEvalInput &input)
return result;
}
-void QmakeProFile::asyncEvaluate(QFutureInterface<QmakeEvalResultPtr> &fi, QmakeEvalInput input)
+void QmakeProFile::asyncEvaluate(QPromise<QmakeEvalResultPtr> &promise, QmakeEvalInput input)
{
- fi.reportResult(evaluate(input));
+ promise.addResult(evaluate(input));
}
bool sortByParserNodes(Node *a, Node *b)
diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.h b/src/plugins/qmakeprojectmanager/qmakeparsernodes.h
index 34c4958564..9296addfa8 100644
--- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.h
+++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.h
@@ -336,7 +336,7 @@ private:
static Internal::QmakeEvalResultPtr evaluate(const Internal::QmakeEvalInput &input);
void applyEvaluate(const Internal::QmakeEvalResultPtr &parseResult);
- void asyncEvaluate(QFutureInterface<Internal::QmakeEvalResultPtr> &fi,
+ void asyncEvaluate(QPromise<Internal::QmakeEvalResultPtr> &promise,
Internal::QmakeEvalInput input);
void cleanupProFileReaders();
diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp
index 6d84018725..d7aefb50b8 100644
--- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp
@@ -26,6 +26,7 @@
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/buildtargetinfo.h>
#include <projectexplorer/deploymentdata.h>
+#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/extracompiler.h>
#include <projectexplorer/headerpath.h>
#include <projectexplorer/projectexplorer.h>
@@ -40,15 +41,16 @@
#include <proparser/qmakevfs.h>
#include <proparser/qmakeglobals.h>
+#include <qmljs/qmljsmodelmanagerinterface.h>
+
#include <qtsupport/profilereader.h>
#include <qtsupport/qtcppkitinfo.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtversionmanager.h>
#include <utils/algorithm.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
-#include <qmljs/qmljsmodelmanagerinterface.h>
+#include <utils/async.h>
+#include <utils/process.h>
#include <QDebug>
#include <QDir>
@@ -335,7 +337,7 @@ void QmakeBuildSystem::updateCppCodeModel()
rpp.setBuildTargetType(BuildTargetType::Unknown);
break;
}
- const QString includeFileBaseDir = pro->sourceDir().toString();
+ const FilePath includeFileBaseDir = pro->sourceDir();
QStringList cxxArgs = pro->variableValue(Variable::CppFlags);
QStringList cArgs = pro->variableValue(Variable::CFlags);
@@ -776,32 +778,25 @@ Tasks QmakeProject::projectIssues(const Kit *k) const
}
// Find the folder that contains a file with a certain name (recurse down)
-static FolderNode *folderOf(FolderNode *in, const FilePath &fileName)
+static FolderNode *folderOf(FolderNode *in, const FilePath &filePath)
{
- const QList<FileNode*> fileNodeList = in->fileNodes();
- for (FileNode *fn : fileNodeList) {
- if (fn->filePath() == fileName)
- return in;
- }
- const QList<FolderNode *> folderNodeList = in->folderNodes();
- for (FolderNode *folder : folderNodeList) {
- if (FolderNode *pn = folderOf(folder, fileName))
- return pn;
- }
- return {};
+ if (in->findChildFileNode([&filePath](FileNode *fn) { return fn->filePath() == filePath; }))
+ return in;
+
+ return in->findChildFolderNode([&filePath](FolderNode *folder) {
+ return folderOf(folder, filePath);
+ });
}
// Find the QmakeProFileNode that contains a certain file.
// First recurse down to folder, then find the pro-file.
-static FileNode *fileNodeOf(FolderNode *in, const FilePath &fileName)
-{
- for (FolderNode *folder = folderOf(in, fileName); folder; folder = folder->parentFolderNode()) {
- if (auto *proFile = dynamic_cast<QmakeProFileNode *>(folder)) {
- const QList<FileNode*> fileNodeList = proFile->fileNodes();
- for (FileNode *fileNode : fileNodeList) {
- if (fileNode->filePath() == fileName)
- return fileNode;
- }
+static FileNode *fileNodeOf(FolderNode *in, const FilePath &filePath)
+{
+ for (FolderNode *folder = folderOf(in, filePath); folder; folder = folder->parentFolderNode()) {
+ if (auto proFile = dynamic_cast<QmakeProFileNode *>(folder)) {
+ return proFile->findChildFileNode([&filePath](FileNode *fn) {
+ return fn->filePath() == filePath;
+ });
}
}
return nullptr;
@@ -872,9 +867,9 @@ QtSupport::ProFileReader *QmakeBuildSystem::createProFileReader(const QmakeProFi
rootProFileName,
deviceRoot());
- Environment::const_iterator eit = env.constBegin(), eend = env.constEnd();
- for (; eit != eend; ++eit)
- m_qmakeGlobals->environment.insert(env.key(eit), env.expandedValueForKey(env.key(eit)));
+ env.forEachEntry([&](const QString &key, const QString &value, bool) {
+ m_qmakeGlobals->environment.insert(key, env.expandVariables(value));
+ });
m_qmakeGlobals->setCommandLineArguments(rootProFileName, qmakeArgs);
m_qmakeGlobals->runSystemFunction = bc->runSystemFunction();
@@ -923,9 +918,9 @@ const FilePath &QmakeBuildSystem::qmakeSysroot() const
void QmakeBuildSystem::destroyProFileReader(QtSupport::ProFileReader *reader)
{
// The ProFileReader destructor is super expensive (but thread-safe).
- const auto deleteFuture = runAsync(ProjectExplorerPlugin::sharedThreadPool(), QThread::LowestPriority,
- [reader] { delete reader; });
- onFinished(deleteFuture, this, [this](const QFuture<void> &) {
+ const auto deleteFuture = Utils::asyncRun(ProjectExplorerPlugin::sharedThreadPool(),
+ [reader] { delete reader; });
+ Utils::onFinished(deleteFuture, this, [this](const QFuture<void> &) {
if (!--m_qmakeGlobalsRefCnt) {
deregisterFromCacheManager();
m_qmakeGlobals.reset();
@@ -1309,15 +1304,13 @@ static FilePath destDirFor(const TargetInformation &ti)
return ti.destDir;
}
-void QmakeBuildSystem::collectLibraryData(const QmakeProFile *file, DeploymentData &deploymentData)
+FilePaths QmakeBuildSystem::allLibraryTargetFiles(const QmakeProFile *file) const
{
- const QString targetPath = file->installsList().targetPath;
- if (targetPath.isEmpty())
- return;
const ToolChain *const toolchain = ToolChainKitAspect::cxxToolChain(kit());
if (!toolchain)
- return;
+ return {};
+ FilePaths libs;
TargetInformation ti = file->targetInformation();
QString targetFileName = ti.target;
const QStringList config = file->variableValue(Variable::Config);
@@ -1337,7 +1330,7 @@ void QmakeBuildSystem::collectLibraryData(const QmakeProFile *file, DeploymentDa
}
targetFileName += targetVersionExt + QLatin1Char('.');
targetFileName += QLatin1String(isStatic ? "lib" : "dll");
- deploymentData.addFile(destDirFor(ti) / targetFileName, targetPath);
+ libs << FilePath::fromString(targetFileName);
break;
}
case Abi::DarwinOS: {
@@ -1357,10 +1350,10 @@ void QmakeBuildSystem::collectLibraryData(const QmakeProFile *file, DeploymentDa
targetFileName += majorVersion;
}
targetFileName += QLatin1Char('.');
- targetFileName += file->singleVariableValue(isStatic
- ? Variable::StaticLibExtension : Variable::ShLibExtension);
+ targetFileName += file->singleVariableValue(isStatic ? Variable::StaticLibExtension
+ : Variable::ShLibExtension);
}
- deploymentData.addFile(destDir / targetFileName, targetPath);
+ libs << destDir / targetFileName;
break;
}
case Abi::LinuxOS:
@@ -1372,10 +1365,10 @@ void QmakeBuildSystem::collectLibraryData(const QmakeProFile *file, DeploymentDa
targetFileName += QLatin1Char('.');
if (isStatic) {
- targetFileName += QLatin1Char('a');
+ libs << destDirFor(ti) / (targetFileName + QLatin1Char('a'));
} else {
targetFileName += QLatin1String("so");
- deploymentData.addFile(destDirFor(ti) / targetFileName, targetPath);
+ libs << destDirFor(ti) / targetFileName;
if (nameIsVersioned) {
QString version = file->singleVariableValue(Variable::Version);
if (version.isEmpty())
@@ -1386,9 +1379,7 @@ void QmakeBuildSystem::collectLibraryData(const QmakeProFile *file, DeploymentDa
targetFileName += QLatin1Char('.');
while (!versionComponents.isEmpty()) {
const QString versionString = versionComponents.join(QLatin1Char('.'));
- deploymentData.addFile(destDirFor(ti).pathAppended(targetFileName
- + versionString),
- targetPath);
+ libs << destDirFor(ti).pathAppended(targetFileName + versionString);
versionComponents.removeLast();
}
}
@@ -1397,6 +1388,18 @@ void QmakeBuildSystem::collectLibraryData(const QmakeProFile *file, DeploymentDa
default:
break;
}
+
+ return libs;
+}
+
+void QmakeBuildSystem::collectLibraryData(const QmakeProFile *file, DeploymentData &deploymentData)
+{
+ const QString targetPath = file->installsList().targetPath;
+ if (!targetPath.isEmpty()) {
+ const FilePaths libs = allLibraryTargetFiles(file);
+ for (const FilePath &lib : libs)
+ deploymentData.addFile(lib, targetPath);
+ }
}
static FilePath getFullPathOf(const QmakeProFile *pro, Variable variable,
@@ -1450,8 +1453,12 @@ void QmakeBuildSystem::testToolChain(ToolChain *tc, const FilePath &path) const
QString QmakeBuildSystem::deviceRoot() const
{
- if (projectFilePath().needsDevice())
- return projectFilePath().withNewPath("/").toFSPathString();
+ IDeviceConstPtr device = BuildDeviceKitAspect::device(target()->kit());
+ QTC_ASSERT(device, return {});
+ FilePath deviceRoot = device->rootPath();
+ if (deviceRoot.needsDevice())
+ return deviceRoot.toFSPathString();
+
return {};
}
@@ -1588,22 +1595,22 @@ void QmakeBuildSystem::runGenerator(Utils::Id id)
QTC_ASSERT(false, return);
}
if (!outDir.ensureWritableDir()) {
- showError(Tr::tr("Cannot create output directory \"%1\"").arg(outDir.toUserOutput()));
+ showError(Tr::tr("Cannot create output directory \"%1\".").arg(outDir.toUserOutput()));
return;
}
- const auto proc = new QtcProcess(this);
- connect(proc, &QtcProcess::done, proc, &QtcProcess::deleteLater);
- connect(proc, &QtcProcess::readyReadStandardOutput, this, [proc] {
+ const auto proc = new Process(this);
+ connect(proc, &Process::done, proc, &Process::deleteLater);
+ connect(proc, &Process::readyReadStandardOutput, this, [proc] {
Core::MessageManager::writeFlashing(QString::fromLocal8Bit(proc->readAllRawStandardOutput()));
});
- connect(proc, &QtcProcess::readyReadStandardError, this, [proc] {
+ connect(proc, &Process::readyReadStandardError, this, [proc] {
Core::MessageManager::writeDisrupting(QString::fromLocal8Bit(proc->readAllRawStandardError()));
});
proc->setWorkingDirectory(outDir);
proc->setEnvironment(buildConfiguration()->environment());
proc->setCommand(cmdLine);
- Core::MessageManager::writeFlashing(Tr::tr("Running in %1: %2")
- .arg(outDir.toUserOutput(), cmdLine.toUserOutput()));
+ Core::MessageManager::writeFlashing(
+ Tr::tr("Running in \"%1\": %2.").arg(outDir.toUserOutput(), cmdLine.toUserOutput()));
proc->start();
}
diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.h b/src/plugins/qmakeprojectmanager/qmakeproject.h
index 6615baaaca..3097ae7b57 100644
--- a/src/plugins/qmakeprojectmanager/qmakeproject.h
+++ b/src/plugins/qmakeprojectmanager/qmakeproject.h
@@ -106,6 +106,7 @@ public:
void collectData(const QmakeProFile *file, ProjectExplorer::DeploymentData &deploymentData);
void collectApplicationData(const QmakeProFile *file,
ProjectExplorer::DeploymentData &deploymentData);
+ Utils::FilePaths allLibraryTargetFiles(const QmakeProFile *file) const;
void collectLibraryData(const QmakeProFile *file,
ProjectExplorer::DeploymentData &deploymentData);
void startAsyncTimer(QmakeProFile::AsyncUpdateDelay delay);
diff --git a/src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp b/src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp
index 8bcbe2027c..1a929bf9a4 100644
--- a/src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp
@@ -23,8 +23,8 @@
#include <qtsupport/qtversionmanager.h>
#include <utils/algorithm.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDir>
#include <QFileInfo>
diff --git a/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp b/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp
index e557615507..61493bf05c 100644
--- a/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp
@@ -29,7 +29,7 @@
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
@@ -80,7 +80,7 @@ public:
ProFileEditorFactory profileEditorFactory;
- QmakeSettingsPage settingsPage;
+ QmakeSettings settings;
QmakeProject *m_previousStartupProject = nullptr;
Target *m_previousTarget = nullptr;
@@ -244,7 +244,7 @@ void QmakeProjectManagerPlugin::initialize()
connect(BuildManager::instance(), &BuildManager::buildStateChanged,
d, &QmakeProjectManagerPluginPrivate::buildStateChanged);
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
d, &QmakeProjectManagerPluginPrivate::projectChanged);
connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged,
d, &QmakeProjectManagerPluginPrivate::projectChanged);
@@ -294,7 +294,7 @@ void QmakeProjectManagerPluginPrivate::projectChanged()
if (ProjectTree::currentProject())
m_previousStartupProject = qobject_cast<QmakeProject *>(ProjectTree::currentProject());
else
- m_previousStartupProject = qobject_cast<QmakeProject *>(SessionManager::startupProject());
+ m_previousStartupProject = qobject_cast<QmakeProject *>(ProjectManager::startupProject());
if (m_previousStartupProject) {
connect(m_previousStartupProject, &Project::activeTargetChanged,
@@ -357,8 +357,7 @@ void QmakeProjectManagerPluginPrivate::addLibraryImpl(const FilePath &filePath,
// add extra \n in case the last line is not empty
int line, column;
editor->convertPosition(endOfDoc, &line, &column);
- const int positionInBlock = column - 1;
- if (!editor->textAt(endOfDoc - positionInBlock, positionInBlock).simplified().isEmpty())
+ if (!editor->textAt(endOfDoc - column, column).simplified().isEmpty())
snippet = QLatin1Char('\n') + snippet;
editor->insert(snippet);
@@ -366,7 +365,7 @@ void QmakeProjectManagerPluginPrivate::addLibraryImpl(const FilePath &filePath,
void QmakeProjectManagerPluginPrivate::runQMake()
{
- runQMakeImpl(SessionManager::startupProject(), nullptr);
+ runQMakeImpl(ProjectManager::startupProject(), nullptr);
}
void QmakeProjectManagerPluginPrivate::runQMakeContextMenu()
@@ -411,7 +410,7 @@ void QmakeProjectManagerPluginPrivate::buildFile()
FileNode *node = n ? n->asFileNode() : nullptr;
if (!node)
return;
- Project *project = SessionManager::projectForFile(file);
+ Project *project = ProjectManager::projectForFile(file);
if (!project)
return;
Target *target = project->activeTarget();
@@ -563,7 +562,7 @@ void QmakeProjectManagerPluginPrivate::enableBuildFileMenus(const FilePath &file
bool enabled = false;
if (Node *node = ProjectTree::nodeForFile(file)) {
- if (Project *project = SessionManager::projectForFile(file)) {
+ if (Project *project = ProjectManager::projectForFile(file)) {
if (const FileNode *fileNode = node->asFileNode()) {
const FileType type = fileNode->fileType();
visible = qobject_cast<QmakeProject *>(project)
diff --git a/src/plugins/qmakeprojectmanager/qmakesettings.cpp b/src/plugins/qmakeprojectmanager/qmakesettings.cpp
index a9e1de07c3..e69526901f 100644
--- a/src/plugins/qmakeprojectmanager/qmakesettings.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakesettings.cpp
@@ -4,105 +4,61 @@
#include "qmakesettings.h"
#include "qmakeprojectmanagertr.h"
-#include <coreplugin/icore.h>
-
#include <projectexplorer/projectexplorerconstants.h>
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
-#include <QXmlStreamWriter>
-
using namespace Utils;
-namespace QmakeProjectManager {
-namespace Internal {
+namespace QmakeProjectManager::Internal {
-QmakeSettings::QmakeSettings()
-{
- setAutoApply(false);
+static QmakeSettings *theSettings;
- registerAspect(&m_warnAgainstUnalignedBuildDir);
- m_warnAgainstUnalignedBuildDir.setSettingsKey("QmakeProjectManager/WarnAgainstUnalignedBuildDir");
- m_warnAgainstUnalignedBuildDir.setDefaultValue(HostOsInfo::isWindowsHost());
- m_warnAgainstUnalignedBuildDir.setLabelText(Tr::tr("Warn if a project's source and "
- "build directories are not at the same level"));
- m_warnAgainstUnalignedBuildDir.setToolTip(Tr::tr("Qmake has subtle bugs that "
- "can be triggered if source and build directory are not at the same level."));
+QmakeSettings &settings() { return *theSettings; }
- registerAspect(&m_alwaysRunQmake);
- m_alwaysRunQmake.setSettingsKey("QmakeProjectManager/AlwaysRunQmake");
- m_alwaysRunQmake.setLabelText(Tr::tr("Run qmake on every build"));
- m_alwaysRunQmake.setToolTip(Tr::tr("This option can help to prevent failures on "
- "incremental builds, but might slow them down unnecessarily in the general case."));
+QmakeSettings::QmakeSettings()
+{
+ theSettings = this;
- registerAspect(&m_ignoreSystemFunction);
- m_ignoreSystemFunction.setSettingsKey("QmakeProjectManager/RunSystemFunction");
- m_ignoreSystemFunction.setLabelText(Tr::tr("Ignore qmake's system() function when parsing a project"));
- m_ignoreSystemFunction.setToolTip(Tr::tr("Checking this option avoids unwanted side effects, "
- "but may result in inexact parsing results."));
+ setId("K.QmakeProjectManager.QmakeSettings");
+ setDisplayName(Tr::tr("Qmake"));
+ setCategory(ProjectExplorer::Constants::BUILD_AND_RUN_SETTINGS_CATEGORY);
+ setSettingsGroup("QmakeProjectManager");
+
+ warnAgainstUnalignedBuildDir.setSettingsKey("WarnAgainstUnalignedBuildDir");
+ warnAgainstUnalignedBuildDir.setDefaultValue(HostOsInfo::isWindowsHost());
+ warnAgainstUnalignedBuildDir.setLabelText(Tr::tr("Warn if a project's source and "
+ "build directories are not at the same level"));
+ warnAgainstUnalignedBuildDir.setToolTip(Tr::tr("Qmake has subtle bugs that "
+ "can be triggered if source and build directory are not at the same level."));
+
+ alwaysRunQmake.setSettingsKey("AlwaysRunQmake");
+ alwaysRunQmake.setLabelText(Tr::tr("Run qmake on every build"));
+ alwaysRunQmake.setToolTip(Tr::tr("This option can help to prevent failures on "
+ "incremental builds, but might slow them down unnecessarily in the general case."));
+
+ ignoreSystemFunction.setSettingsKey("RunSystemFunction");
+ ignoreSystemFunction.setLabelText(Tr::tr("Ignore qmake's system() function when parsing a project"));
+ ignoreSystemFunction.setToolTip(Tr::tr("Checking this option avoids unwanted side effects, "
+ "but may result in inexact parsing results."));
// The settings value has been stored with the opposite meaning for a while.
// Avoid changing the stored value, but flip it on read/write:
const auto invertBoolVariant = [](const QVariant &v) { return QVariant(!v.toBool()); };
- m_ignoreSystemFunction.setFromSettingsTransformation(invertBoolVariant);
- m_ignoreSystemFunction.setToSettingsTransformation(invertBoolVariant);
-
- readSettings(Core::ICore::settings());
-}
+ ignoreSystemFunction.setFromSettingsTransformation(invertBoolVariant);
+ ignoreSystemFunction.setToSettingsTransformation(invertBoolVariant);
-bool QmakeSettings::warnAgainstUnalignedBuildDir()
-{
- return instance().m_warnAgainstUnalignedBuildDir.value();
-}
-
-bool QmakeSettings::alwaysRunQmake()
-{
- return instance().m_alwaysRunQmake.value();
-}
-
-bool QmakeSettings::runSystemFunction()
-{
- return !instance().m_ignoreSystemFunction.value(); // Note: negated.
-}
-
-QmakeSettings &QmakeSettings::instance()
-{
- static QmakeSettings theSettings;
- return theSettings;
-}
-
-class SettingsWidget final : public Core::IOptionsPageWidget
-{
-public:
- SettingsWidget()
- {
- auto &s = QmakeSettings::instance();
+ setLayouter([this] {
using namespace Layouting;
- Column {
- s.m_warnAgainstUnalignedBuildDir,
- s.m_alwaysRunQmake,
- s.m_ignoreSystemFunction,
+ return Column {
+ warnAgainstUnalignedBuildDir,
+ alwaysRunQmake,
+ ignoreSystemFunction,
st
- }.attachTo(this);
- }
+ };
+ });
- void apply() final
- {
- auto &s = QmakeSettings::instance();
- if (s.isDirty()) {
- s.apply();
- s.writeSettings(Core::ICore::settings());
- }
- }
-};
-
-QmakeSettingsPage::QmakeSettingsPage()
-{
- setId("K.QmakeProjectManager.QmakeSettings");
- setDisplayName(Tr::tr("Qmake"));
- setCategory(ProjectExplorer::Constants::BUILD_AND_RUN_SETTINGS_CATEGORY);
- setWidgetCreator([] { return new SettingsWidget; });
+ readSettings();
}
-} // namespace Internal
-} // namespace QmakeProjectManager
+} // QmakeProjectManager::Internal
diff --git a/src/plugins/qmakeprojectmanager/qmakesettings.h b/src/plugins/qmakeprojectmanager/qmakesettings.h
index 242f6601e2..9c2b277f53 100644
--- a/src/plugins/qmakeprojectmanager/qmakesettings.h
+++ b/src/plugins/qmakeprojectmanager/qmakesettings.h
@@ -5,38 +5,20 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
+namespace QmakeProjectManager::Internal {
-namespace QmakeProjectManager {
-namespace Internal {
-
-class QmakeSettings : public Utils::AspectContainer
+class QmakeSettings : public Core::PagedSettings
{
- Q_OBJECT
-
public:
- static QmakeSettings &instance();
- static bool warnAgainstUnalignedBuildDir();
- static bool alwaysRunQmake();
- static bool runSystemFunction();
-
-signals:
- void settingsChanged();
-
-private:
QmakeSettings();
- friend class SettingsWidget;
- Utils::BoolAspect m_warnAgainstUnalignedBuildDir;
- Utils::BoolAspect m_alwaysRunQmake;
- Utils::BoolAspect m_ignoreSystemFunction;
-};
+ bool runSystemFunction() { return !ignoreSystemFunction(); }
-class QmakeSettingsPage final : public Core::IOptionsPage
-{
-public:
- QmakeSettingsPage();
+ Utils::BoolAspect warnAgainstUnalignedBuildDir{this};
+ Utils::BoolAspect alwaysRunQmake{this};
+ Utils::BoolAspect ignoreSystemFunction{this};
};
-} // namespace Internal
-} // namespace QmakeProjectManager
+QmakeSettings &settings();
+
+} // QmakeProjectManager::Internal
diff --git a/src/plugins/qmakeprojectmanager/qmakestep.cpp b/src/plugins/qmakeprojectmanager/qmakestep.cpp
index c726156e94..0da8bebaad 100644
--- a/src/plugins/qmakeprojectmanager/qmakestep.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakestep.cpp
@@ -36,7 +36,7 @@
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/utilsicons.h>
#include <utils/variablechooser.h>
@@ -225,7 +225,7 @@ bool QMakeStep::init()
}
// Check whether we need to run qmake
- if (m_forced || QmakeSettings::alwaysRunQmake()
+ if (m_forced || settings().alwaysRunQmake()
|| qmakeBc->compareToImportFrom(makeFile) != QmakeBuildConfiguration::MakefileMatches) {
m_needToRunQMake = true;
}
@@ -284,14 +284,14 @@ void QMakeStep::doRun()
using namespace Tasking;
- const auto setupQMake = [this](QtcProcess &process) {
+ const auto setupQMake = [this](Process &process) {
m_outputFormatter->setLineParsers({new QMakeParser});
ProcessParameters *pp = processParameters();
pp->setCommandLine(m_qmakeCommand);
setupProcess(&process);
};
- const auto setupMakeQMake = [this](QtcProcess &process) {
+ const auto setupMakeQMake = [this](Process &process) {
auto *parser = new GnuMakeParser;
parser->addSearchDir(processParameters()->workingDirectory());
m_outputFormatter->setLineParsers({parser});
@@ -300,13 +300,13 @@ void QMakeStep::doRun()
setupProcess(&process);
};
- const auto onDone = [this](const QtcProcess &) {
+ const auto onProcessDone = [this](const Process &) {
const QString command = displayedParameters()->effectiveCommand().toUserOutput();
emit addOutput(Tr::tr("The process \"%1\" exited normally.").arg(command),
OutputFormat::NormalMessage);
};
- const auto onError = [this](const QtcProcess &process) {
+ const auto onProcessError = [this](const Process &process) {
const QString command = displayedParameters()->effectiveCommand().toUserOutput();
if (process.result() == ProcessResult::FinishedWithError) {
emit addOutput(Tr::tr("The process \"%1\" exited with code %2.")
@@ -326,14 +326,14 @@ void QMakeStep::doRun()
m_needToRunQMake = true;
};
- const auto onGroupDone = [this] {
+ const auto onDone = [this] {
emit buildConfiguration()->buildDirectoryInitialized();
};
- QList<TaskItem> processList = {Process(setupQMake, onDone, onError)};
+ QList<TaskItem> processList = {ProcessTask(setupQMake, onProcessDone, onProcessError)};
if (m_runMakeQmake)
- processList << Process(setupMakeQMake, onDone, onError);
- processList << OnGroupDone(onGroupDone);
+ processList << ProcessTask(setupMakeQMake, onProcessDone, onProcessError);
+ processList << onGroupDone(onDone);
runTaskTree(Group(processList));
}
@@ -455,21 +455,6 @@ bool QMakeStep::fromMap(const QVariantMap &map)
{
m_forced = map.value(QMAKE_FORCED_KEY, false).toBool();
m_selectedAbis = map.value(QMAKE_SELECTED_ABIS_KEY).toStringList();
-
- // Backwards compatibility with < Creator 4.12.
- const QVariant separateDebugInfo
- = map.value("QtProjectManager.QMakeBuildStep.SeparateDebugInfo");
- if (separateDebugInfo.isValid())
- qmakeBuildConfiguration()->forceSeparateDebugInfo(separateDebugInfo.toBool());
- const QVariant qmlDebugging
- = map.value("QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary");
- if (qmlDebugging.isValid())
- qmakeBuildConfiguration()->forceQmlDebugging(qmlDebugging.toBool());
- const QVariant useQtQuickCompiler
- = map.value("QtProjectManager.QMakeBuildStep.UseQtQuickCompiler");
- if (useQtQuickCompiler.isValid())
- qmakeBuildConfiguration()->forceQtQuickCompiler(useQtQuickCompiler.toBool());
-
return BuildStep::fromMap(map);
}
@@ -481,11 +466,12 @@ QWidget *QMakeStep::createConfigWidget()
abisListWidget = new QListWidget;
Layouting::Form builder;
- builder.addRow(m_buildType);
- builder.addRow(m_userArgs);
- builder.addRow(m_effectiveCall);
+ builder.addRow({m_buildType});
+ builder.addRow({m_userArgs});
+ builder.addRow({m_effectiveCall});
builder.addRow({abisLabel, abisListWidget});
- auto widget = builder.emerge(Layouting::WithoutMargins);
+ builder.addItem(Layouting::noMargin);
+ auto widget = builder.emerge();
qmakeBuildConfigChanged();
@@ -744,7 +730,7 @@ QMakeStepFactory::QMakeStepFactory()
setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD);
//: QMakeStep default display name
setDisplayName(::QmakeProjectManager::Tr::tr("qmake")); // Fully qualifying for lupdate
- setFlags(BuildStepInfo::UniqueStep);
+ setFlags(BuildStep::UniqueStep);
}
QMakeStepConfig::TargetArchConfig QMakeStepConfig::targetArchFor(const Abi &, const QtVersion *)
diff --git a/src/plugins/qmakeprojectmanager/wizards/qtprojectparameters.cpp b/src/plugins/qmakeprojectmanager/wizards/qtprojectparameters.cpp
index e2db56e824..1612e36753 100644
--- a/src/plugins/qmakeprojectmanager/wizards/qtprojectparameters.cpp
+++ b/src/plugins/qmakeprojectmanager/wizards/qtprojectparameters.cpp
@@ -65,8 +65,10 @@ void QtProjectParameters::writeProFile(QTextStream &str) const
switch (type) {
case ConsoleApp:
// Mac: Command line apps should not be bundles
- str << "CONFIG += console\nCONFIG -= app_bundle\n\n";
- // fallthrough
+ str << "CONFIG += console\n"
+ "CONFIG -= app_bundle\n\n"
+ "TEMPLATE = app\n";
+ break;
case GuiApp:
str << "TEMPLATE = app\n";
break;
diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt
index 16668bf328..7987e50808 100644
--- a/src/plugins/qmldesigner/CMakeLists.txt
+++ b/src/plugins/qmldesigner/CMakeLists.txt
@@ -92,7 +92,6 @@ extend_qtc_library(QmlDesignerCore
CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.5.0 AND Qt6_VERSION VERSION_LESS 6.6.0
PUBLIC_DEFINES QDS_BUILD_QMLPARSER
)
-
extend_qtc_library(QmlDesignerCore
CONDITION UNIX AND NOT APPLE
PUBLIC_DEPENDS rt
@@ -295,19 +294,6 @@ extend_qtc_library(QmlDesignerCore
)
extend_qtc_library(QmlDesignerCore
- PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/designercore/instances
- SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/instances
- SOURCES_PROPERTIES SKIP_AUTOUIC OFF
- SOURCES
- puppetbuildprogressdialog.ui
- puppetdialog.ui
- puppetbuildprogressdialog.cpp
- puppetbuildprogressdialog.h
- puppetdialog.cpp
- puppetdialog.h
-)
-
-extend_qtc_library(QmlDesignerCore
INCLUDES ${CMAKE_CURRENT_LIST_DIR}/designercore/model
SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/model
SOURCES
@@ -538,10 +524,6 @@ function(get_and_add_as_subdirectory name repository git_tag build_dir source_di
endfunction()
if (QTC_STATIC_BUILD AND TARGET QmlDesigner)
- get_target_property(_designerType Qt5::Designer TYPE)
- if (${_designerType} STREQUAL "STATIC_LIBRARY")
- extend_qtc_target(QmlDesigner PUBLIC_DEFINES QT_DESIGNER_STATIC)
- endif()
get_target_property(_designerType Qt::Designer TYPE)
if (${_designerType} STREQUAL "STATIC_LIBRARY")
extend_qtc_target(QmlDesigner PUBLIC_DEFINES QT_DESIGNER_STATIC)
@@ -549,7 +531,6 @@ if (QTC_STATIC_BUILD AND TARGET QmlDesigner)
extend_qtc_target(QmlDesigner PUBLIC_DEPENDS TextEditor)
endif()
-
extend_qtc_plugin(QmlDesigner
SOURCES_PREFIX components
PUBLIC_INCLUDES components
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp
index f3460cbca8..e9b5ab7082 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp
@@ -11,7 +11,7 @@
#include <projectexplorer/task.h>
#include <projectexplorer/taskhub.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/fileutils.h>
#include <utils/outputformatter.h>
@@ -63,7 +63,7 @@ AssetExportDialog::AssetExportDialog(const Utils::FilePath &exportPath,
m_ui->exportPath->setExpectedKind(Utils::PathChooser::Kind::SaveFile);
m_ui->exportPath->setFilePath(
exportPath.pathAppended(
- ProjectExplorer::SessionManager::startupProject()->displayName() + ".metadata"
+ ProjectExplorer::ProjectManager::startupProject()->displayName() + ".metadata"
));
m_ui->exportPath->setPromptDialogTitle(tr("Choose Export File"));
m_ui->exportPath->setPromptDialogFilter(tr("Metadata file (*.metadata)"));
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp
index d9167049a5..7b0604c19a 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp
@@ -10,11 +10,12 @@
#include "rewriterview.h"
#include "qmlitemnode.h"
#include "qmlobjectnode.h"
-#include "coreplugin/editormanager/editormanager.h"
-#include "utils/qtcassert.h"
-#include "utils/runextensions.h"
-#include "projectexplorer/session.h"
-#include "projectexplorer/project.h"
+
+#include <coreplugin/editormanager/editormanager.h>
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
+#include <utils/async.h>
+#include <utils/qtcassert.h>
#include <auxiliarydataproperties.h>
@@ -69,7 +70,7 @@ public:
private:
void addAsset(const QPixmap &p, const Utils::FilePath &path);
- void doDumping(QFutureInterface<void> &fi);
+ void doDumping(QPromise<void> &promise);
void savePixmap(const QPixmap &p, Utils::FilePath &path) const;
QFuture<void> m_dumpFuture;
@@ -406,7 +407,7 @@ void AssetExporter::writeMetadata() const
m_currentState.change(ParsingState::WritingJson);
- auto const startupProject = ProjectExplorer::SessionManager::startupProject();
+ auto const startupProject = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(startupProject, return);
const QString projectName = startupProject->displayName();
@@ -452,7 +453,7 @@ QDebug operator<<(QDebug os, const AssetExporter::ParsingState &s)
AssetDumper::AssetDumper():
m_quitDumper(false)
{
- m_dumpFuture = Utils::runAsync(&AssetDumper::doDumping, this);
+ m_dumpFuture = Utils::asyncRun(&AssetDumper::doDumping, this);
}
AssetDumper::~AssetDumper()
@@ -489,7 +490,7 @@ void AssetDumper::addAsset(const QPixmap &p, const Utils::FilePath &path)
m_assets.push({p, path});
}
-void AssetDumper::doDumping(QFutureInterface<void> &fi)
+void AssetDumper::doDumping(QPromise<void> &promise)
{
auto haveAsset = [this] (std::pair<QPixmap, Utils::FilePath> *asset) {
QMutexLocker locker(&m_queueMutex);
@@ -503,7 +504,7 @@ void AssetDumper::doDumping(QFutureInterface<void> &fi)
forever {
std::pair<QPixmap, Utils::FilePath> asset;
if (haveAsset(&asset)) {
- if (fi.isCanceled())
+ if (promise.isCanceled())
break;
savePixmap(asset.first, asset.second);
} else {
@@ -513,10 +514,9 @@ void AssetDumper::doDumping(QFutureInterface<void> &fi)
m_queueCondition.wait(&m_queueMutex);
}
- if (fi.isCanceled())
+ if (promise.isCanceled())
break;
}
- fi.reportFinished();
}
void AssetDumper::savePixmap(const QPixmap &p, Utils::FilePath &path) const
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp
index 2d6f6c7d80..3ab1d1a686 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp
@@ -19,9 +19,9 @@
#include "coreplugin/documentmanager.h"
#include "qmldesigner/qmldesignerplugin.h"
#include "projectexplorer/projectexplorerconstants.h"
-#include "projectexplorer/session.h"
+#include "projectexplorer/projectmanager.h"
#include "projectexplorer/project.h"
-#include "projectexplorer/session.h"
+#include "projectexplorer/projectmanager.h"
#include "projectexplorer/taskhub.h"
#include "extensionsystem/pluginmanager.h"
@@ -54,8 +54,8 @@ AssetExporterPlugin::AssetExporterPlugin()
// Instantiate actions created by the plugin.
addActions();
- connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::startupProjectChanged,
+ connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged,
this, &AssetExporterPlugin::updateActions);
updateActions();
@@ -68,7 +68,7 @@ QString AssetExporterPlugin::pluginName() const
void AssetExporterPlugin::onExport()
{
- auto startupProject = ProjectExplorer::SessionManager::startupProject();
+ auto startupProject = ProjectExplorer::ProjectManager::startupProject();
if (!startupProject)
return;
@@ -97,7 +97,7 @@ void AssetExporterPlugin::addActions()
void AssetExporterPlugin::updateActions()
{
- auto project = ProjectExplorer::SessionManager::startupProject();
+ auto project = ProjectExplorer::ProjectManager::startupProject();
QAction* const exportAction = Core::ActionManager::command(Constants::EXPORT_QML)->action();
exportAction->setEnabled(project && !project->needsConfiguration());
}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp b/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp
index b50bc3cffb..5020b095a3 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp
+++ b/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp
@@ -4,9 +4,10 @@
#include "exportnotification.h"
-#include "projectexplorer/project.h"
-#include "projectexplorer/projectnodes.h"
-#include "utils/runextensions.h"
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectnodes.h>
+
+#include <utils/async.h>
#include <QLoggingCategory>
#include <QTimer>
@@ -17,19 +18,19 @@ namespace {
Q_LOGGING_CATEGORY(loggerError, "qtc.designer.assetExportPlugin.filePathModel", QtCriticalMsg)
Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.filePathModel", QtInfoMsg)
-void findQmlFiles(QFutureInterface<Utils::FilePath> &f, const Project *project)
+void findQmlFiles(QPromise<Utils::FilePath> &promise, const Project *project)
{
- if (!project || f.isCanceled())
+ if (!project || promise.isCanceled())
return;
int index = 0;
- project->files([&f, &index](const Node* node) ->bool {
- if (f.isCanceled())
+ project->files([&promise, &index](const Node* node) ->bool {
+ if (promise.isCanceled())
return false;
Utils::FilePath path = node->filePath();
bool isComponent = !path.fileName().isEmpty() && path.fileName().front().isUpper();
if (isComponent && node->filePath().endsWith(".ui.qml"))
- f.reportResult(path, index++);
+ promise.addResult(path, index++);
return true;
});
}
@@ -132,7 +133,7 @@ void FilePathModel::processProject()
connect(m_preprocessWatcher.get(), &QFutureWatcher<Utils::FilePath>::finished,
this, &FilePathModel::endResetModel);
- QFuture<Utils::FilePath> f = Utils::runAsync(&findQmlFiles, m_project);
+ QFuture<Utils::FilePath> f = Utils::asyncRun(&findQmlFiles, m_project);
m_preprocessWatcher->setFuture(f);
}
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp
index c8bb96bb1a..6d6efbb5d2 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp
@@ -19,7 +19,7 @@
#include <nodelistproperty.h>
#include <projectexplorer/kit.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <rewriterview.h>
#include <sqlitedatabase.h>
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h
index 90ae46010a..33ad100c0b 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h
@@ -23,7 +23,7 @@ QT_END_NAMESPACE
class StudioQuickWidget;
namespace Utils {
- class QtcProcess;
+ class Process;
}
namespace QmlDesigner {
diff --git a/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp b/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp
index a7f5a06e01..6ef8bbb6c9 100644
--- a/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp
@@ -5,7 +5,7 @@
#include "designermcumanager.h"
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <QComboBox>
#include <QSettings>
@@ -14,7 +14,7 @@ namespace QmlDesigner {
static QString styleConfigFileName(const QString &qmlFileName)
{
- ProjectExplorer::Project *currentProject = ProjectExplorer::SessionManager::projectForFile(Utils::FilePath::fromString(qmlFileName));
+ ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::projectForFile(Utils::FilePath::fromString(qmlFileName));
if (currentProject) {
const QList<Utils::FilePath> fileNames = currentProject->files(
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
index 918b06c637..258ae0ba92 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
@@ -55,8 +55,8 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include "utils/qtcprocess.h"
#include <utils/smallstring.h>
#include <QComboBox>
@@ -1670,10 +1670,10 @@ void openEffectMaker(const QString &filePath)
if (env.osType() == Utils::OsTypeMac)
env.appendOrSet("QSG_RHI_BACKEND", "metal");
- Utils::QtcProcess *qqemProcess = new Utils::QtcProcess();
+ Utils::Process *qqemProcess = new Utils::Process();
qqemProcess->setEnvironment(env);
qqemProcess->setCommand({ effectMakerPath, arguments });
- QObject::connect(qqemProcess, &Utils::QtcProcess::done, [qqemProcess]() {
+ QObject::connect(qqemProcess, &Utils::Process::done, [qqemProcess]() {
qqemProcess->deleteLater();
});
qqemProcess->start();
diff --git a/src/plugins/qmldesigner/components/componentcore/zoomaction.cpp b/src/plugins/qmldesigner/components/componentcore/zoomaction.cpp
index 3d0464778d..e967be04ec 100644
--- a/src/plugins/qmldesigner/components/componentcore/zoomaction.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/zoomaction.cpp
@@ -7,6 +7,8 @@
#include <iterator>
#include <utility>
+#include <utils/stylehelper.h>
+
#include <QAbstractItemView>
#include <QComboBox>
#include <QToolBar>
@@ -123,7 +125,7 @@ QWidget *ZoomAction::createWidget(QWidget *parent)
{
if (!m_combo && parentIsToolBar(parent)) {
m_combo = createZoomComboBox(parent);
- m_combo->setProperty("hideborder", true);
+ m_combo->setProperty(Utils::StyleHelper::C_HIDE_BORDER, true);
m_combo->setCurrentIndex(m_index);
m_combo->setToolTip(m_combo->currentText());
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp
index 105bfed89d..3bb579273f 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp
@@ -20,7 +20,7 @@
#ifndef QMLDESIGNER_TEST
#include <projectexplorer/kit.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
@@ -371,7 +371,7 @@ void ContentLibraryView::updateBundleMaterialsQuick3DVersion()
#ifndef QMLDESIGNER_TEST
if (hasImport && major == -1) {
// Import without specifying version, so we take the kit version
- auto target = ProjectExplorer::SessionManager::startupTarget();
+ auto target = ProjectExplorer::ProjectManager::startupTarget();
if (target) {
QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(target->kit());
if (qtVersion) {
diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp
index 452940aa5f..27a14968c6 100644
--- a/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp
+++ b/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp
@@ -8,7 +8,9 @@
#include "coreplugin/actionmanager/actionmanager.h"
#include "coreplugin/icontext.h"
#include "theme.h"
-#include "utils/id.h"
+
+#include <utils/id.h>
+#include <utils/stylehelper.h>
#include <QAction>
#include <QHBoxLayout>
@@ -154,8 +156,8 @@ CurveEditorToolBar::CurveEditorToolBar(CurveEditorModel *model, QWidget* parent)
m_zoomSlider = new QSlider(Qt::Horizontal);
m_zoomSlider->setRange(0, 100);
- m_zoomSlider->setProperty("panelwidget", true);
- m_zoomSlider->setProperty("panelwidget_singlerow", true);
+ Utils::StyleHelper::setPanelWidget(m_zoomSlider);
+ Utils::StyleHelper::setPanelWidgetSingleRow(m_zoomSlider);
m_zoomSlider->setFixedWidth(120);
connect(m_zoomSlider, &QSlider::valueChanged, [this](int value) {
diff --git a/src/plugins/qmldesigner/components/eventlist/eventlist.cpp b/src/plugins/qmldesigner/components/eventlist/eventlist.cpp
index 23e60dbda2..a0d7cc8981 100644
--- a/src/plugins/qmldesigner/components/eventlist/eventlist.cpp
+++ b/src/plugins/qmldesigner/components/eventlist/eventlist.cpp
@@ -8,7 +8,7 @@
#include "bindingproperty.h"
#include "metainfo.h"
#include "projectexplorer/project.h"
-#include "projectexplorer/session.h"
+#include "projectexplorer/projectmanager.h"
#include "qmldesignerplugin.h"
#include "signalhandlerproperty.h"
#include "utils/fileutils.h"
@@ -23,7 +23,7 @@ namespace QmlDesigner {
Utils::FilePath projectFilePath()
{
if (auto *doc = QmlDesignerPlugin::instance()->documentManager().currentDesignDocument()) {
- if (auto *proj = ProjectExplorer::SessionManager::projectForFile(doc->fileName()))
+ if (auto *proj = ProjectExplorer::ProjectManager::projectForFile(doc->fileName()))
return proj->projectDirectory();
}
return Utils::FilePath();
diff --git a/src/plugins/qmldesigner/components/formeditor/backgroundaction.cpp b/src/plugins/qmldesigner/components/formeditor/backgroundaction.cpp
index 7403c239b3..a9d72be8dd 100644
--- a/src/plugins/qmldesigner/components/formeditor/backgroundaction.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/backgroundaction.cpp
@@ -3,6 +3,8 @@
#include "backgroundaction.h"
+#include <utils/stylehelper.h>
+
#include <QComboBox>
#include <QPainter>
@@ -52,7 +54,7 @@ QWidget *BackgroundAction::createWidget(QWidget *parent)
connect(comboBox, &QComboBox::currentIndexChanged,
this, &BackgroundAction::emitBackgroundChanged);
- comboBox->setProperty("hideborder", true);
+ comboBox->setProperty(Utils::StyleHelper::C_HIDE_BORDER, true);
comboBox->setToolTip(tr("Set the color of the canvas."));
m_comboBox = comboBox;
return comboBox;
diff --git a/src/plugins/qmldesigner/components/formeditor/seekerslider.cpp b/src/plugins/qmldesigner/components/formeditor/seekerslider.cpp
index caa7dcbd30..804f061657 100644
--- a/src/plugins/qmldesigner/components/formeditor/seekerslider.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/seekerslider.cpp
@@ -4,6 +4,7 @@
#include "seekerslider.h"
#include <utils/icon.h>
+#include <utils/stylehelper.h>
#include <QMouseEvent>
#include <QStyleOption>
@@ -15,8 +16,8 @@ namespace QmlDesigner {
SeekerSlider::SeekerSlider(QWidget *parent)
: QSlider(parent)
{
- setProperty("panelwidget", true);
- setProperty("panelwidget_singlerow", true);
+ Utils::StyleHelper::setPanelWidget(this);
+ Utils::StyleHelper::setPanelWidgetSingleRow(this);
setOrientation(Qt::Horizontal);
setFixedWidth(120);
setMaxValue(30);
diff --git a/src/plugins/qmldesigner/components/formeditor/toolbox.cpp b/src/plugins/qmldesigner/components/formeditor/toolbox.cpp
index 6d20f3374b..47a9503b2d 100644
--- a/src/plugins/qmldesigner/components/formeditor/toolbox.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/toolbox.cpp
@@ -5,6 +5,8 @@
#include <theme.h>
+#include <utils/stylehelper.h>
+
#include <QToolBar>
#include <QHBoxLayout>
#include <QDebug>
@@ -17,8 +19,8 @@ ToolBox::ToolBox(QWidget *parentWidget)
, m_leftToolBar(new QToolBar(QLatin1String("LeftSidebar"), this))
, m_rightToolBar(new QToolBar(QLatin1String("RightSidebar"), this))
{
- setProperty("panelwidget", false);
- setProperty("panelwidget_singlerow", false);
+ Utils::StyleHelper::setPanelWidget(this, false);
+ Utils::StyleHelper::setPanelWidgetSingleRow(this, false);
setFixedHeight(Theme::toolbarSize());
m_leftToolBar->setFloatable(true);
@@ -29,18 +31,18 @@ ToolBox::ToolBox(QWidget *parentWidget)
horizontalLayout->setContentsMargins(0, 0, 0, 0);
horizontalLayout->setSpacing(0);
- m_leftToolBar->setProperty("panelwidget", false);
- m_leftToolBar->setProperty("panelwidget_singlerow", false);
+ Utils::StyleHelper::setPanelWidget(m_leftToolBar, false);
+ Utils::StyleHelper::setPanelWidgetSingleRow(m_leftToolBar, false);
m_leftToolBar->setFixedHeight(Theme::toolbarSize());
- m_rightToolBar->setProperty("panelwidget", false);
- m_rightToolBar->setProperty("panelwidget_singlerow", false);
+ Utils::StyleHelper::setPanelWidget(m_rightToolBar, false);
+ Utils::StyleHelper::setPanelWidgetSingleRow(m_rightToolBar, false);
m_rightToolBar->setFixedHeight(Theme::toolbarSize());
m_rightToolBar->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
auto stretchToolbar = new QToolBar(this);
- stretchToolbar->setProperty("panelwidget", false);
- stretchToolbar->setProperty("panelwidget_singlerow", false);
+ Utils::StyleHelper::setPanelWidget(stretchToolbar, false);
+ Utils::StyleHelper::setPanelWidgetSingleRow(stretchToolbar, false);
stretchToolbar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
m_rightToolBar->setOrientation(Qt::Horizontal);
diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp
index aec12e7cee..a61aeb50a0 100644
--- a/src/plugins/qmldesigner/components/integration/designdocument.cpp
+++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp
@@ -21,7 +21,7 @@
#include <projectexplorer/projecttree.h>
#include <projectexplorer/project.h>
#include <projectexplorer/target.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/kit.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtsupportconstants.h>
@@ -409,7 +409,7 @@ bool DesignDocument::isQtForMCUsProject() const
Utils::FilePath DesignDocument::projectFolder() const
{
- ProjectExplorer::Project *currentProject = ProjectExplorer::SessionManager::projectForFile(fileName());
+ ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::projectForFile(fileName());
if (currentProject)
return currentProject->projectDirectory();
@@ -440,7 +440,7 @@ void DesignDocument::changeToInFileComponentModel(ComponentTextModifier *textMod
void DesignDocument::updateQrcFiles()
{
- ProjectExplorer::Project *currentProject = ProjectExplorer::SessionManager::projectForFile(fileName());
+ ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::projectForFile(fileName());
if (currentProject) {
const auto srcFiles = currentProject->files(ProjectExplorer::Project::SourceFiles);
@@ -726,7 +726,7 @@ void DesignDocument::redo()
static Target *getActiveTarget(DesignDocument *designDocument)
{
- Project *currentProject = SessionManager::projectForFile(designDocument->fileName());
+ Project *currentProject = ProjectManager::projectForFile(designDocument->fileName());
if (!currentProject)
currentProject = ProjectTree::currentProject();
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
index 51698fc2a4..91f403f218 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
#include "itemlibraryassetimportdialog.h"
#include "ui_itemlibraryassetimportdialog.h"
@@ -13,7 +14,8 @@
#include "theme.h"
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
+
#include <coreplugin/icore.h>
#include <QFileInfo>
@@ -330,7 +332,7 @@ void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode,
// Unable to find original scene source, launch file dialog to locate it
QString initialPath;
ProjectExplorer::Project *currentProject
- = ProjectExplorer::SessionManager::projectForFile(
+ = ProjectExplorer::ProjectManager::projectForFile(
Utils::FilePath::fromString(compFileName));
if (currentProject)
initialPath = currentProject->projectDirectory().toString();
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
index 3bbe60a0f6..d141b88dc8 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
@@ -19,7 +19,7 @@
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <utils/algorithm.h>
-#include <utils/runextensions.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
#include <QApplication>
@@ -682,7 +682,7 @@ void ItemLibraryAssetImporter::finalizeQuick3DImport()
if (modelManager) {
QmlJS::PathsAndLanguages pathToScan;
pathToScan.maybeInsert(::Utils::FilePath::fromString(m_importPath));
- result = ::Utils::runAsync(&QmlJS::ModelManagerInterface::importScan,
+ result = ::Utils::asyncRun(&QmlJS::ModelManagerInterface::importScan,
modelManager->workingCopy(),
pathToScan,
modelManager,
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
index d6b55cd052..21f2e6aa55 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
@@ -13,7 +13,7 @@
#include <nodehints.h>
#include <nodemetainfo.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include "qmldesignerconstants.h"
#include "qmldesignerplugin.h"
#include <utils/algorithm.h>
@@ -314,7 +314,7 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model)
DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument();
Utils::FilePath qmlFileName = document->fileName();
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(qmlFileName);
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::projectForFile(qmlFileName);
QString projectName = project ? project->displayName() : "";
QString materialBundlePrefix = QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1);
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
index 5afc95564a..d370f6d454 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
@@ -14,7 +14,7 @@
#include <nodelistproperty.h>
#include <projectexplorer/kit.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <rewriterview.h>
#include <sqlitedatabase.h>
diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp
index 97c7285d49..f1a00a36c7 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp
+++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp
@@ -29,7 +29,7 @@
#include <coreplugin/icore.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <utils/algorithm.h>
@@ -470,14 +470,14 @@ const ProjectExplorer::FileNode *NavigatorView::fileNodeForModelNode(const Model
{
QString filename = node.metaInfo().componentFileName();
Utils::FilePath filePath = Utils::FilePath::fromString(filename);
- ProjectExplorer::Project *currentProject = ProjectExplorer::SessionManager::projectForFile(
+ ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::projectForFile(
filePath);
if (!currentProject) {
filePath = Utils::FilePath::fromString(node.model()->fileUrl().toLocalFile());
/* If the component does not belong to the project then we can fallback to the current file */
- currentProject = ProjectExplorer::SessionManager::projectForFile(filePath);
+ currentProject = ProjectExplorer::ProjectManager::projectForFile(filePath);
}
if (!currentProject)
return nullptr;
diff --git a/src/plugins/qmldesigner/components/propertyeditor/aligndistribute.cpp b/src/plugins/qmldesigner/components/propertyeditor/aligndistribute.cpp
index 5d1900c494..5d8400b47e 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/aligndistribute.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/aligndistribute.cpp
@@ -663,14 +663,15 @@ AlignDistribute::Dimension AlignDistribute::getDimension(Target target) const
bool AlignDistribute::executePixelPerfectDialog() const
{
- QDialogButtonBox::StandardButton pressed = Utils::CheckableMessageBox::doNotAskAgainQuestion(
+ Utils::CheckableDecider decider(QString("WarnAboutPixelPerfectDistribution"));
+
+ QMessageBox::StandardButton pressed = Utils::CheckableMessageBox::question(
Core::ICore::dialogParent(),
tr("Cannot Distribute Perfectly"),
tr("These objects cannot be distributed to equal pixel values. "
"Do you want to distribute to the nearest possible values?"),
- Core::ICore::settings(),
- "WarnAboutPixelPerfectDistribution");
- return (pressed == QDialogButtonBox::Yes) ? true : false;
+ decider);
+ return (pressed == QMessageBox::Yes) ? true : false;
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp
index fc9dbb56a6..aae0867c91 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp
@@ -15,7 +15,7 @@
#include <qmlmodelnodeproxy.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
static QString s_lastBrowserPath;
@@ -23,7 +23,7 @@ FileResourcesModel::FileResourcesModel(QObject *parent)
: QObject(parent)
, m_filter(QLatin1String("(*.*)"))
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::projectForFile(
QmlDesigner::DocumentManager::currentFilePath());
if (project) {
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp
index c920b8b428..b4ab0583c7 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp
@@ -22,6 +22,7 @@
#include <coreplugin/icore.h>
#include <utils/algorithm.h>
+#include <utils/stylehelper.h>
#include <QApplication>
#include <QIntValidator>
@@ -430,8 +431,8 @@ void TimelineToolBar::createRightControls()
m_scale = new QSlider(this);
m_scale->setOrientation(Qt::Horizontal);
- m_scale->setProperty("panelwidget", true);
- m_scale->setProperty("panelwidget_singlerow", true);
+ Utils::StyleHelper::setPanelWidget(m_scale);
+ Utils::StyleHelper::setPanelWidgetSingleRow(m_scale);
m_scale->setMaximumWidth(200);
m_scale->setMinimumWidth(100);
m_scale->setMinimum(0);
diff --git a/src/plugins/qmldesigner/components/toolbar/toolbar.cpp b/src/plugins/qmldesigner/components/toolbar/toolbar.cpp
index c8e05999ed..e4f523c55c 100644
--- a/src/plugins/qmldesigner/components/toolbar/toolbar.cpp
+++ b/src/plugins/qmldesigner/components/toolbar/toolbar.cpp
@@ -115,7 +115,7 @@ Utils::UniqueObjectPtr<QWidget> ToolBar::createStatusBar()
quickWidget->setSource(QUrl::fromLocalFile(qmlFilePath.toFSPathString()));
- for (QWidget *w : Core::ICore::statusBar()->findChildren<QWidget *>(Qt::FindDirectChildrenOnly)) {
+ for (QWidget *w : Core::ICore::statusBar()->findChildren<QWidget *>(QString(), Qt::FindDirectChildrenOnly)) {
w->hide();
}
diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp
index 1919aeedad..1b9f1e3271 100644
--- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp
+++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp
@@ -24,7 +24,7 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qmlprojectmanager/qmlproject.h>
@@ -55,7 +55,7 @@ static CrumbleBar *crumbleBar()
static Utils::FilePath getMainUiFile()
{
- auto project = ProjectExplorer::SessionManager::startupProject();
+ auto project = ProjectExplorer::ProjectManager::startupProject();
if (!project)
return {};
@@ -327,8 +327,8 @@ ToolBarBackend::ToolBarBackend(QObject *parent)
emit isDesignModeEnabledChanged();
});
- connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::startupProjectChanged,
+ connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged,
this,
[this](ProjectExplorer::Project *project) {
disconnect(m_kitConnection);
@@ -494,7 +494,7 @@ void ToolBarBackend::setCurrentStyle(int index)
void ToolBarBackend::setCurrentKit(int index)
{
- auto project = ProjectExplorer::SessionManager::startupProject();
+ auto project = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(project, return );
const auto kits = ProjectExplorer::KitManager::kits();
@@ -508,9 +508,8 @@ void ToolBarBackend::setCurrentKit(int index)
if (!newTarget)
newTarget = project->addTargetForKit(kit);
- ProjectExplorer::SessionManager::setActiveTarget(project,
- newTarget,
- ProjectExplorer::SetActive::Cascade);
+ project->setActiveTarget(newTarget,
+ ProjectExplorer::SetActive::Cascade);
emit currentKitChanged();
}
@@ -614,7 +613,7 @@ QStringList ToolBarBackend::kits() const
int ToolBarBackend::currentKit() const
{
- if (auto target = ProjectExplorer::SessionManager::startupTarget()) {
+ if (auto target = ProjectExplorer::ProjectManager::startupTarget()) {
auto kit = target->kit();
if (kit)
return kits().indexOf(kit->displayName());
@@ -624,11 +623,11 @@ int ToolBarBackend::currentKit() const
bool ToolBarBackend::isQt6() const
{
- if (!ProjectExplorer::SessionManager::startupTarget())
+ if (!ProjectExplorer::ProjectManager::startupTarget())
return false;
const QmlProjectManager::QmlBuildSystem *buildSystem = qobject_cast<QmlProjectManager::QmlBuildSystem *>(
- ProjectExplorer::SessionManager::startupTarget()->buildSystem());
+ ProjectExplorer::ProjectManager::startupTarget()->buildSystem());
QTC_ASSERT(buildSystem, return false);
const bool isQt6Project = buildSystem && buildSystem->qt6Project();
@@ -638,7 +637,7 @@ bool ToolBarBackend::isQt6() const
bool ToolBarBackend::projectOpened() const
{
- return ProjectExplorer::SessionManager::instance()->startupProject();
+ return ProjectExplorer::ProjectManager::instance()->startupProject();
}
void ToolBarBackend::launchGlobalAnnotations()
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.cpp
index 81d6b9a11b..194a4ad221 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.cpp
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.cpp
@@ -23,6 +23,7 @@
#include <coreplugin/icore.h>
#include <utils/algorithm.h>
+#include <utils/stylehelper.h>
#include <QApplication>
#include <QComboBox>
@@ -249,8 +250,8 @@ void TransitionEditorToolBar::createRightControls()
addSpacing(10);
m_scale = new QSlider(this);
- m_scale->setProperty("panelwidget", true);
- m_scale->setProperty("panelwidget_singlerow", true);
+ Utils::StyleHelper::setPanelWidget(m_scale);
+ Utils::StyleHelper::setPanelWidgetSingleRow(m_scale);
m_scale->setOrientation(Qt::Horizontal);
m_scale->setMaximumWidth(200);
m_scale->setMinimumWidth(100);
diff --git a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h
index 94d90aad18..d4a087f4c7 100644
--- a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h
+++ b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h
@@ -6,6 +6,9 @@
#include <instances/puppetstartdata.h>
#include <utils/filepath.h>
+#include <QColor>
+#include <QUrl>
+
namespace QmlDesigner {
class DesignerSettings;
diff --git a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h
index 0a9af39fc5..93b4e82a2a 100644
--- a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h
+++ b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h
@@ -34,7 +34,7 @@ class Target;
}
namespace Utils {
-class QtcProcess;
+class Process;
}
namespace QmlDesigner {
@@ -229,7 +229,7 @@ private: // functions
void updateWatcher(const QString &path);
void handleShaderChanges();
- void handleQsbProcessExit(Utils::QtcProcess *qsbProcess, const QString &shader);
+ void handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader);
void updateQsbPathToFilterMap();
void updateRotationBlocks();
void maybeResetOnPropertyChange(const PropertyName &name, const ModelNode &node,
diff --git a/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h b/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h
index a4fa1ab8ed..eb652a4010 100644
--- a/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h
+++ b/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h
@@ -29,6 +29,7 @@ public:
QmlObjectNode(const ModelNode &modelNode)
: QmlModelNodeFacade(modelNode)
{}
+ virtual ~QmlObjectNode() = default;
static bool isValidQmlObjectNode(const ModelNode &modelNode);
bool isValid() const;
diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
index 32c5c2a807..0864e137c2 100644
--- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
@@ -75,8 +75,8 @@
#include <qmlprojectmanager/qmlproject.h>
#include <utils/algorithm.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/theme/theme.h>
#include <utils/threadutils.h>
@@ -870,7 +870,7 @@ NodeInstance NodeInstanceView::activeStateInstance() const
void NodeInstanceView::updateChildren(const NodeAbstractProperty &newPropertyParent)
{
- const QVector<ModelNode> childNodeVector = newPropertyParent.directSubNodes().toVector();
+ const QList<ModelNode> childNodeVector = newPropertyParent.directSubNodes();
qint32 parentInstanceId = newPropertyParent.parentModelNode().internalId();
@@ -1506,7 +1506,7 @@ void NodeInstanceView::pixmapChanged(const PixmapChangedCommand &command)
m_nodeInstanceServer->benchmark(Q_FUNC_INFO + QString::number(renderImageChangeSet.count()));
if (!renderImageChangeSet.isEmpty())
- emitInstancesRenderImageChanged(Utils::toList(renderImageChangeSet).toVector());
+ emitInstancesRenderImageChanged(Utils::toList(renderImageChangeSet));
if (!containerVector.isEmpty()) {
QMultiHash<ModelNode, InformationName> informationChangeHash = informationChanged(
@@ -2071,7 +2071,7 @@ void NodeInstanceView::updateWatcher(const QString &path)
m_generateQsbFilesTimer.start();
}
-void NodeInstanceView::handleQsbProcessExit(Utils::QtcProcess *qsbProcess, const QString &shader)
+void NodeInstanceView::handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader)
{
--m_remainingQsbTargets;
@@ -2172,8 +2172,8 @@ void NodeInstanceView::handleShaderChanges()
QStringList args = baseArgs;
args.append(outPath.toString());
args.append(shader);
- auto qsbProcess = new Utils::QtcProcess(this);
- connect(qsbProcess, &Utils::QtcProcess::done, this, [this, qsbProcess, shader] {
+ auto qsbProcess = new Utils::Process(this);
+ connect(qsbProcess, &Utils::Process::done, this, [this, qsbProcess, shader] {
handleQsbProcessExit(qsbProcess, shader);
});
qsbProcess->setWorkingDirectory(srcPath);
diff --git a/src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.cpp b/src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.cpp
deleted file mode 100644
index 53489fa5b8..0000000000
--- a/src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "puppetbuildprogressdialog.h"
-#include "ui_puppetbuildprogressdialog.h"
-
-#include <QtDebug>
-#include <QApplication>
-#include <coreplugin/icore.h>
-
-namespace QmlDesigner {
-
-PuppetBuildProgressDialog::PuppetBuildProgressDialog() :
- QDialog(Core::ICore::dialogParent()),
- ui(new Ui::PuppetBuildProgressDialog),
- m_lineCount(0),
- m_useFallbackPuppet(false)
-{
- setWindowFlags(Qt::SplashScreen);
- setWindowModality(Qt::ApplicationModal);
- ui->setupUi(this);
- ui->buildProgressBar->setMaximum(85);
- connect(ui->useFallbackPuppetPushButton, &QAbstractButton::clicked, this, &PuppetBuildProgressDialog::setUseFallbackPuppet);
-}
-
-PuppetBuildProgressDialog::~PuppetBuildProgressDialog()
-{
- delete ui;
-}
-
-void PuppetBuildProgressDialog::setProgress(int progress)
-{
- ui->buildProgressBar->setValue(progress);
-}
-
-bool PuppetBuildProgressDialog::useFallbackPuppet() const
-{
- return m_useFallbackPuppet;
-}
-
-void PuppetBuildProgressDialog::setErrorOutputFile(const QString &filePath)
-{
- ui->openErrorOutputFileLabel->setText(QString::fromLatin1("<a href='file:///%1'>%2</a>").arg(
- filePath, ui->openErrorOutputFileLabel->text()));
-}
-
-void PuppetBuildProgressDialog::setErrorMessage(const QString &message)
-{
- ui->label->setText(QString::fromLatin1("<font color='red'>%1</font>").arg(message));
- ui->useFallbackPuppetPushButton->setText(tr("OK"));
- connect(ui->useFallbackPuppetPushButton, &QAbstractButton::clicked, this, &QDialog::accept);
-}
-
-void PuppetBuildProgressDialog::setUseFallbackPuppet()
-{
- m_useFallbackPuppet = true;
-}
-
-void PuppetBuildProgressDialog::newBuildOutput(const QByteArray &standardOutput)
-{
- m_lineCount += standardOutput.count('\n');
- setProgress(m_lineCount);
-}
-
-}
diff --git a/src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.h b/src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.h
deleted file mode 100644
index c7db04eaec..0000000000
--- a/src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <QDialog>
-
-namespace QmlDesigner {
-
-namespace Ui {
-class PuppetBuildProgressDialog;
-}
-
-
-class PuppetBuildProgressDialog : public QDialog
-{
- Q_OBJECT
-
-public:
- explicit PuppetBuildProgressDialog();
- ~PuppetBuildProgressDialog() override;
-
- void setProgress(int progress);
- void newBuildOutput(const QByteArray &standardOutput);
- bool useFallbackPuppet() const;
- void setErrorOutputFile(const QString &filePath);
- void setErrorMessage(const QString &message);
-
-private:
- void setUseFallbackPuppet();
-
-private:
- Ui::PuppetBuildProgressDialog *ui;
- int m_lineCount;
- bool m_useFallbackPuppet;
-};
-
-}
diff --git a/src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.ui b/src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.ui
deleted file mode 100644
index 626eb6b557..0000000000
--- a/src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.ui
+++ /dev/null
@@ -1,78 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>QmlDesigner::PuppetBuildProgressDialog</class>
- <widget class="QDialog" name="QmlDesigner::PuppetBuildProgressDialog">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>695</width>
- <height>99</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>Build Progress</string>
- </property>
- <property name="modal">
- <bool>true</bool>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Building Adapter for the current Qt. Happens only once for every Qt installation.</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QProgressBar" name="buildProgressBar">
- <property name="value">
- <number>0</number>
- </property>
- </widget>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout">
- <item>
- <widget class="QLabel" name="openErrorOutputFileLabel">
- <property name="text">
- <string>Open error output file</string>
- </property>
- <property name="openExternalLinks">
- <bool>true</bool>
- </property>
- <property name="textInteractionFlags">
- <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QPushButton" name="useFallbackPuppetPushButton">
- <property name="text">
- <string>Use Fallback QML Emulation Layer</string>
- </property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/src/plugins/qmldesigner/designercore/instances/puppetdialog.cpp b/src/plugins/qmldesigner/designercore/instances/puppetdialog.cpp
deleted file mode 100644
index 935b502168..0000000000
--- a/src/plugins/qmldesigner/designercore/instances/puppetdialog.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "puppetdialog.h"
-#include "ui_puppetdialog.h"
-
-namespace QmlDesigner {
-
-PuppetDialog::PuppetDialog(QWidget *parent) :
- QDialog(parent),
- ui(new Ui::PuppetDialog)
-{
- ui->setupUi(this);
-}
-
-PuppetDialog::~PuppetDialog()
-{
- delete ui;
-}
-
-void PuppetDialog::setDescription(const QString &description)
-{
- ui->descriptionLabel->setText(description);
-}
-
-void PuppetDialog::setCopyAndPasteCode(const QString &text)
-{
- ui->copyAndPasteTextEdit->setText(text);
-}
-
-void PuppetDialog::warning(QWidget *parent, const QString &title, const QString &description, const QString &copyAndPasteCode)
-{
- PuppetDialog dialog(parent);
-
- dialog.setWindowTitle(title);
- dialog.setDescription(description);
- dialog.setCopyAndPasteCode(copyAndPasteCode);
-
- dialog.exec();
-}
-
-} //QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/instances/puppetdialog.h b/src/plugins/qmldesigner/designercore/instances/puppetdialog.h
deleted file mode 100644
index 32d0b8deb4..0000000000
--- a/src/plugins/qmldesigner/designercore/instances/puppetdialog.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <QDialog>
-
-namespace QmlDesigner {
-
-namespace Ui {
-class PuppetDialog;
-}
-
-
-class PuppetDialog : public QDialog
-{
- Q_OBJECT
-
-public:
- explicit PuppetDialog(QWidget *parent = nullptr);
- ~PuppetDialog() override;
-
- void setDescription(const QString &description);
- void setCopyAndPasteCode(const QString &text);
-
- static void warning(QWidget *parent,
- const QString &title,
- const QString &description,
- const QString &copyAndPasteCode);
-
-private:
- Ui::PuppetDialog *ui;
-};
-
-} //QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/instances/puppetdialog.ui b/src/plugins/qmldesigner/designercore/instances/puppetdialog.ui
deleted file mode 100644
index b7efa8c26c..0000000000
--- a/src/plugins/qmldesigner/designercore/instances/puppetdialog.ui
+++ /dev/null
@@ -1,96 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>QmlDesigner::PuppetDialog</class>
- <widget class="QDialog" name="QmlDesigner::PuppetDialog">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>1148</width>
- <height>344</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>Dialog</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <property name="spacing">
- <number>12</number>
- </property>
- <item>
- <widget class="QLabel" name="descriptionLabel">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>1</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string/>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QTextEdit" name="copyAndPasteTextEdit">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>1</verstretch>
- </sizepolicy>
- </property>
- <property name="readOnly">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="standardButtons">
- <set>QDialogButtonBox::Close</set>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections>
- <connection>
- <sender>buttonBox</sender>
- <signal>accepted()</signal>
- <receiver>QmlDesigner::PuppetDialog</receiver>
- <slot>accept()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>248</x>
- <y>254</y>
- </hint>
- <hint type="destinationlabel">
- <x>157</x>
- <y>274</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>buttonBox</sender>
- <signal>rejected()</signal>
- <receiver>QmlDesigner::PuppetDialog</receiver>
- <slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>316</x>
- <y>260</y>
- </hint>
- <hint type="destinationlabel">
- <x>286</x>
- <y>274</y>
- </hint>
- </hints>
- </connection>
- </connections>
-</ui>
diff --git a/src/plugins/qmldesigner/documentmanager.cpp b/src/plugins/qmldesigner/documentmanager.cpp
index d2bf9eac51..e3460622e7 100644
--- a/src/plugins/qmldesigner/documentmanager.cpp
+++ b/src/plugins/qmldesigner/documentmanager.cpp
@@ -21,16 +21,18 @@
#include <coreplugin/vcsmanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagebox.h>
-#include <projectexplorer/projectnodes.h>
+
#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
+#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
#include <qmakeprojectmanager/qmakenodes.h>
#include <qmakeprojectmanager/qmakeproject.h>
#include <QMessageBox>
+using namespace ProjectExplorer;
using namespace Utils;
namespace QmlDesigner {
@@ -335,11 +337,11 @@ Utils::FilePath DocumentManager::currentProjectDirPath()
Utils::FilePath qmlFileName = QmlDesignerPlugin::instance()->currentDesignDocument()->fileName();
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(qmlFileName);
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::projectForFile(qmlFileName);
if (project)
return project->projectDirectory();
- const QList projects = ProjectExplorer::SessionManager::projects();
+ const QList projects = ProjectExplorer::ProjectManager::projects();
for (auto p : projects) {
if (qmlFileName.startsWith(p->projectDirectory().toString()))
return p->projectDirectory();
@@ -402,7 +404,7 @@ void DocumentManager::findPathToIsoProFile(bool *iconResourceFileAlreadyExists,
QString *resourceFileProPath, const QString &isoIconsQrcFile)
{
Utils::FilePath qmlFileName = QmlDesignerPlugin::instance()->currentDesignDocument()->fileName();
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(qmlFileName);
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::projectForFile(qmlFileName);
ProjectExplorer::Node *node = ProjectExplorer::ProjectTree::nodeForFile(qmlFileName)->parentFolderNode();
ProjectExplorer::Node *iconQrcFileNode = nullptr;
@@ -412,8 +414,10 @@ void DocumentManager::findPathToIsoProFile(bool *iconResourceFileAlreadyExists,
if (node->isVirtualFolderType() && node->displayName() == "Resources") {
ProjectExplorer::FolderNode *virtualFolderNode = node->asFolderNode();
if (QTC_GUARD(virtualFolderNode)) {
- for (int subFolderIndex = 0; subFolderIndex < virtualFolderNode->folderNodes().size() && !iconQrcFileNode; ++subFolderIndex) {
- ProjectExplorer::FolderNode *subFolderNode = virtualFolderNode->folderNodes().at(subFolderIndex);
+ QList<FolderNode *> folderNodes;
+ virtualFolderNode->forEachFolderNode([&](FolderNode *fn) { folderNodes.append(fn); });
+ for (int subFolderIndex = 0; subFolderIndex < folderNodes.size() && !iconQrcFileNode; ++subFolderIndex) {
+ ProjectExplorer::FolderNode *subFolderNode = folderNodes.at(subFolderIndex);
qCDebug(documentManagerLog) << "Checking if" << subFolderNode->displayName() << "("
<< subFolderNode << ") is" << isoIconsQrcFile;
@@ -492,7 +496,7 @@ bool DocumentManager::belongsToQmakeProject()
return false;
Utils::FilePath qmlFileName = QmlDesignerPlugin::instance()->currentDesignDocument()->fileName();
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(qmlFileName);
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::projectForFile(qmlFileName);
if (!project)
return false;
diff --git a/src/plugins/qmldesigner/generateresource.cpp b/src/plugins/qmldesigner/generateresource.cpp
index 1901a8e2ea..439e124b70 100644
--- a/src/plugins/qmldesigner/generateresource.cpp
+++ b/src/plugins/qmldesigner/generateresource.cpp
@@ -12,8 +12,8 @@
#include <coreplugin/messagemanager.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qmlprojectmanager/qmlprojectmanagerconstants.h>
@@ -22,9 +22,9 @@
#include <qtsupport/qtkitinformation.h>
#include <utils/fileutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
-#include <utils/qtcprocess.h>
#include <QAction>
#include <QByteArray>
@@ -177,7 +177,7 @@ QList<GenerateResource::ResourceFile> getFilesFromQrc(QFile *file, bool inProjec
static bool runRcc(const CommandLine &command, const FilePath &workingDir,
const QString &resourceFile)
{
- Utils::QtcProcess rccProcess;
+ Utils::Process rccProcess;
rccProcess.setWorkingDirectory(workingDir);
rccProcess.setCommand(command);
rccProcess.start();
@@ -223,16 +223,16 @@ void GenerateResource::generateMenuEntry(QObject *parent)
auto action = new QAction(QCoreApplication::translate("QmlDesigner::GenerateResource",
"Generate QRC Resource File..."),
parent);
- action->setEnabled(ProjectExplorer::SessionManager::startupProject() != nullptr);
+ action->setEnabled(ProjectExplorer::ProjectManager::startupProject() != nullptr);
// todo make it more intelligent when it gets enabled
- QObject::connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::startupProjectChanged, [action]() {
- action->setEnabled(ProjectExplorer::SessionManager::startupProject());
+ QObject::connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged, [action]() {
+ action->setEnabled(ProjectExplorer::ProjectManager::startupProject());
});
Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.CreateResource");
QObject::connect(action, &QAction::triggered, [] () {
- auto currentProject = ProjectExplorer::SessionManager::startupProject();
+ auto currentProject = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(currentProject, return);
const FilePath projectPath = currentProject->projectFilePath().parentDir();
@@ -331,16 +331,16 @@ void GenerateResource::generateMenuEntry(QObject *parent)
auto rccAction = new QAction(QCoreApplication::translate("QmlDesigner::GenerateResource",
"Generate Deployable Package..."),
parent);
- rccAction->setEnabled(ProjectExplorer::SessionManager::startupProject() != nullptr);
- QObject::connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::startupProjectChanged, [rccAction]() {
- rccAction->setEnabled(ProjectExplorer::SessionManager::startupProject());
+ rccAction->setEnabled(ProjectExplorer::ProjectManager::startupProject() != nullptr);
+ QObject::connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged, [rccAction]() {
+ rccAction->setEnabled(ProjectExplorer::ProjectManager::startupProject());
});
Core::Command *cmd2 = Core::ActionManager::registerAction(rccAction,
"QmlProject.CreateRCCResource");
QObject::connect(rccAction, &QAction::triggered, []() {
- auto currentProject = ProjectExplorer::SessionManager::startupProject();
+ auto currentProject = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(currentProject, return);
const FilePath projectPath = currentProject->projectFilePath().parentDir();
diff --git a/src/plugins/qmldesigner/openuiqmlfiledialog.cpp b/src/plugins/qmldesigner/openuiqmlfiledialog.cpp
index 6c1dcf132c..eacb81aa00 100644
--- a/src/plugins/qmldesigner/openuiqmlfiledialog.cpp
+++ b/src/plugins/qmldesigner/openuiqmlfiledialog.cpp
@@ -30,7 +30,7 @@ OpenUiQmlFileDialog::OpenUiQmlFileDialog(QWidget *parent) :
m_listWidget = new QListWidget;
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
tr("You are opening a .qml file in the designer. Do you want to open a .ui.qml file instead?"),
diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp
index a2ffbdc94b..1bd5899165 100644
--- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp
+++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp
@@ -9,14 +9,13 @@
#include <edit3d/edit3dviewconfig.h>
#include <itemlibraryimport.h>
#include <projectexplorer/kit.h>
-#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <puppetenvironmentbuilder.h>
-#include <qmlprojectmanager/qmlproject.h>
#include <qmlpuppetpaths.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
+#include <qmlprojectmanager/buildsystem/qmlbuildsystem.h>
#include <coreplugin/icore.h>
@@ -112,7 +111,7 @@ QString ExternalDependencies::itemLibraryImportUserComponentsTitle() const
bool ExternalDependencies::isQt6Import() const
{
- auto target = ProjectExplorer::SessionManager::startupTarget();
+ auto target = ProjectExplorer::ProjectManager::startupTarget();
if (target) {
QtSupport::QtVersion *currentQtVersion = QtSupport::QtKitAspect::qtVersion(target->kit());
if (currentQtVersion && currentQtVersion->isValid()) {
@@ -125,7 +124,7 @@ bool ExternalDependencies::isQt6Import() const
bool ExternalDependencies::hasStartupTarget() const
{
- auto target = ProjectExplorer::SessionManager::startupTarget();
+ auto target = ProjectExplorer::ProjectManager::startupTarget();
if (target) {
QtSupport::QtVersion *currentQtVersion = QtSupport::QtKitAspect::qtVersion(target->kit());
if (currentQtVersion && currentQtVersion->isValid()) {
@@ -163,7 +162,7 @@ QString createFreeTypeOption(ProjectExplorer::Target *target)
PuppetStartData ExternalDependencies::puppetStartData(const Model &model) const
{
PuppetStartData data;
- auto target = ProjectExplorer::SessionManager::startupTarget();
+ auto target = ProjectExplorer::ProjectManager::startupTarget();
auto [workingDirectory, puppetPath] = QmlPuppetPaths::qmlPuppetPaths(target, m_designerSettings);
data.puppetPath = puppetPath.toString();
@@ -183,7 +182,7 @@ bool ExternalDependencies::instantQmlTextUpdate() const
Utils::FilePath ExternalDependencies::qmlPuppetPath() const
{
- auto target = ProjectExplorer::SessionManager::startupTarget();
+ auto target = ProjectExplorer::ProjectManager::startupTarget();
auto [workingDirectory, puppetPath] = QmlPuppetPaths::qmlPuppetPaths(target, m_designerSettings);
return puppetPath;
}
@@ -207,7 +206,7 @@ QString qmlPath(ProjectExplorer::Target *target)
std::tuple<ProjectExplorer::Project *, ProjectExplorer::Target *, QmlProjectManager::QmlBuildSystem *>
activeProjectEntries()
{
- auto project = ProjectExplorer::SessionManager::startupProject();
+ auto project = ProjectExplorer::ProjectManager::startupProject();
if (!project)
return {};
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp
index 0cff0ffad8..836b7d2e70 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.cpp
+++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp
@@ -56,7 +56,7 @@
#include <extensionsystem/pluginspec.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <sqlitelibraryinitializer.h>
#include <qmldesignerbase/qmldesignerbaseplugin.h>
@@ -388,7 +388,7 @@ ExtensionSystem::IPlugin::ShutdownFlag QmlDesignerPlugin::aboutToShutdown()
static QStringList allUiQmlFilesforCurrentProject(const Utils::FilePath &fileName)
{
QStringList list;
- ProjectExplorer::Project *currentProject = ProjectExplorer::SessionManager::projectForFile(fileName);
+ ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::projectForFile(fileName);
if (currentProject) {
const QList<Utils::FilePath> fileNames = currentProject->files(ProjectExplorer::Project::SourceFiles);
@@ -404,7 +404,7 @@ static QStringList allUiQmlFilesforCurrentProject(const Utils::FilePath &fileNam
static QString projectPath(const Utils::FilePath &fileName)
{
QString path;
- ProjectExplorer::Project *currentProject = ProjectExplorer::SessionManager::projectForFile(fileName);
+ ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::projectForFile(fileName);
if (currentProject)
path = currentProject->projectDirectory().toString();
diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp
index ef36b3957a..4d327b5491 100644
--- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp
+++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp
@@ -6,7 +6,7 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectstorage/filestatuscache.h>
#include <projectstorage/filesystem.h>
@@ -246,15 +246,15 @@ QmlDesignerProjectManager::QmlDesignerProjectManager(ExternalDependenciesInterfa
QObject::connect(editorManager, &::Core::EditorManager::editorsClosed, [&](const auto &editors) {
editorsClosed(editors);
});
- auto sessionManager = ::ProjectExplorer::SessionManager::instance();
+ auto sessionManager = ::ProjectExplorer::ProjectManager::instance();
QObject::connect(sessionManager,
- &::ProjectExplorer::SessionManager::projectAdded,
+ &::ProjectExplorer::ProjectManager::projectAdded,
[&](auto *project) { projectAdded(project); });
QObject::connect(sessionManager,
- &::ProjectExplorer::SessionManager::aboutToRemoveProject,
+ &::ProjectExplorer::ProjectManager::aboutToRemoveProject,
[&](auto *project) { aboutToRemoveProject(project); });
QObject::connect(sessionManager,
- &::ProjectExplorer::SessionManager::projectRemoved,
+ &::ProjectExplorer::ProjectManager::projectRemoved,
[&](auto *project) { projectRemoved(project); });
QObject::connect(&m_previewImageCacheData->timer,
@@ -488,7 +488,7 @@ QmlDesignerProjectManager::ImageCacheData *QmlDesignerProjectManager::imageCache
imageCacheData->nodeInstanceCollector.setTarget(target);
};
- if (auto project = ProjectExplorer::SessionManager::startupProject(); project) {
+ if (auto project = ProjectExplorer::ProjectManager::startupProject(); project) {
// TODO wrap in function in image cache data
m_imageCacheData->meshImageCollector.setTarget(project->activeTarget());
m_imageCacheData->nodeInstanceCollector.setTarget(project->activeTarget());
@@ -497,8 +497,8 @@ QmlDesignerProjectManager::ImageCacheData *QmlDesignerProjectManager::imageCache
this,
setTargetInImageCache);
}
- QObject::connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::startupProjectChanged,
+ QObject::connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged,
this,
[=](ProjectExplorer::Project *project) {
setTargetInImageCache(activeTarget(project));
diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp
index 969ca8d5a8..ccd9a97009 100644
--- a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp
+++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp
@@ -13,7 +13,7 @@
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/target.h>
@@ -36,7 +36,7 @@ static void handleAction(const SelectionContext &context)
if (context.view()->isAttached()) {
if (context.toggled()) {
bool skipDeploy = false;
- if (const Target *startupTarget = SessionManager::startupTarget()) {
+ if (const Target *startupTarget = ProjectManager::startupTarget()) {
const Kit *kit = startupTarget->kit();
if (kit
&& (kit->supportedPlatforms().contains(Android::Constants::ANDROID_DEVICE_TYPE)
@@ -241,10 +241,10 @@ QWidget *SwitchLanguageComboboxAction::createWidget(QWidget *parent)
}
}
};
- connect(ProjectExplorer::SessionManager::instance(), &ProjectExplorer::SessionManager::startupProjectChanged,
+ connect(ProjectExplorer::ProjectManager::instance(), &ProjectExplorer::ProjectManager::startupProjectChanged,
comboBox, refreshComboBoxFunction);
- if (auto project = SessionManager::startupProject())
+ if (auto project = ProjectManager::startupProject())
refreshComboBoxFunction(project);
// do this after refreshComboBoxFunction so we do not get currentLocaleChanged signals at initialization
diff --git a/src/plugins/qmldesigner/settingspage.cpp b/src/plugins/qmldesigner/settingspage.cpp
index 52a58e01a5..8c3222d72c 100644
--- a/src/plugins/qmldesigner/settingspage.cpp
+++ b/src/plugins/qmldesigner/settingspage.cpp
@@ -196,7 +196,7 @@ SettingsPageWidget::SettingsPageWidget(ExternalDependencies &externalDependencie
m_debugPuppetComboBox = new QComboBox;
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_useDefaultPuppetRadioButton,
diff --git a/src/plugins/qmldesigner/utils/filedownloader.h b/src/plugins/qmldesigner/utils/filedownloader.h
index b1202e57cb..4ecb6b4967 100644
--- a/src/plugins/qmldesigner/utils/filedownloader.h
+++ b/src/plugins/qmldesigner/utils/filedownloader.h
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#pragma once
+#include <QDateTime>
#include <QFile>
#include <QNetworkReply>
#include <QObject>
diff --git a/src/plugins/qmldesigner/utils/multifiledownloader.cpp b/src/plugins/qmldesigner/utils/multifiledownloader.cpp
index de3e8c5251..6a8ac9761e 100644
--- a/src/plugins/qmldesigner/utils/multifiledownloader.cpp
+++ b/src/plugins/qmldesigner/utils/multifiledownloader.cpp
@@ -44,6 +44,11 @@ void MultiFileDownloader::setDownloader(FileDownloader *downloader)
});
}
+FileDownloader *MultiFileDownloader::downloader()
+{
+ return m_downloader;
+}
+
void MultiFileDownloader::start()
{
m_canceled = false;
diff --git a/src/plugins/qmldesigner/utils/multifiledownloader.h b/src/plugins/qmldesigner/utils/multifiledownloader.h
index 548896dbc8..794f85538c 100644
--- a/src/plugins/qmldesigner/utils/multifiledownloader.h
+++ b/src/plugins/qmldesigner/utils/multifiledownloader.h
@@ -13,7 +13,7 @@ class MultiFileDownloader : public QObject
{
Q_OBJECT
- Q_PROPERTY(FileDownloader *downloader WRITE setDownloader)
+ Q_PROPERTY(FileDownloader *downloader READ downloader WRITE setDownloader)
Q_PROPERTY(bool finished READ finished NOTIFY finishedChanged)
Q_PROPERTY(int progress READ progress NOTIFY progressChanged)
Q_PROPERTY(QUrl baseUrl READ baseUrl WRITE setBaseUrl NOTIFY baseUrlChanged)
@@ -34,6 +34,7 @@ public:
void setTargetDirPath(const QString &path);
QString targetDirPath() const;
void setDownloader(FileDownloader *downloader);
+ FileDownloader *downloader();
bool finished() const;
int progress() const;
diff --git a/src/plugins/qmldesignerbase/qmldesignerbase.qbs b/src/plugins/qmldesignerbase/qmldesignerbase.qbs
new file mode 100644
index 0000000000..98f4cdf556
--- /dev/null
+++ b/src/plugins/qmldesignerbase/qmldesignerbase.qbs
@@ -0,0 +1,40 @@
+import qbs
+
+QtcPlugin {
+ name: "QmlDesignerBase"
+
+ Depends { name: "Core" }
+ Depends { name: "ProjectExplorer" }
+ Depends { name: "QtSupport" }
+ Depends { name: "app_version_header" }
+ Depends { name: "Qt.quickwidgets" }
+
+ files: [
+ "qmldesignerbase_global.h",
+ "qmldesignerbaseplugin.cpp",
+ "qmldesignerbaseplugin.h",
+ ]
+
+ Group {
+ prefix: "studio/"
+ files: [
+ "studiosettingspage.cpp",
+ "studiosettingspage.h",
+ "studiostyle.cpp",
+ "studiostyle.h",
+ "studioquickwidget.cpp",
+ "studioquickwidget.h",
+ ]
+ }
+ Group {
+ prefix: "utils/"
+ files: [
+ "designerpaths.cpp",
+ "designerpaths.h",
+ "designersettings.cpp",
+ "designersettings.h",
+ "qmlpuppetpaths.cpp",
+ "qmlpuppetpaths.h",
+ ]
+ }
+}
diff --git a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp
index c9af4f2283..c59fcbb2ea 100644
--- a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp
+++ b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp
@@ -3,7 +3,7 @@
#include "qmldesignerbaseplugin.h"
-#include "studiosettingspage.h"
+#include "studio/studiosettingspage.h"
#include "studio/studiostyle.h"
#include "utils/designersettings.h"
diff --git a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h
index 1418a6421b..997189dacf 100644
--- a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h
+++ b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h
@@ -15,7 +15,6 @@ QT_END_NAMESPACE
namespace QmlDesigner {
-
class QMLDESIGNERBASE_EXPORT QmlDesignerBasePlugin final : public ExtensionSystem::IPlugin
{
Q_OBJECT
diff --git a/src/plugins/qmldesignerbase/studio/studiosettingspage.cpp b/src/plugins/qmldesignerbase/studio/studiosettingspage.cpp
index affe25e7da..d94efbd666 100644
--- a/src/plugins/qmldesignerbase/studio/studiosettingspage.cpp
+++ b/src/plugins/qmldesignerbase/studio/studiosettingspage.cpp
@@ -178,15 +178,15 @@ void StudioSettingsPage::apply()
QSettings *s = Core::ICore::settings();
const QString value = m_pathChooserExamples->filePath().toString();
- if (s->value(Paths::exampleDownloadPath, false).toString() != value) {
- s->setValue(Paths::exampleDownloadPath, value);
+ if (s->value(Paths::exampleDownloadPath.toString(), false).toString() != value) {
+ s->setValue(Paths::exampleDownloadPath.toString(), value);
emit examplesDownloadPathChanged(value);
}
const QString bundlesPath = m_pathChooserBundles->filePath().toString();
- if (s->value(Paths::bundlesDownloadPath).toString() != bundlesPath) {
- s->setValue(Paths::bundlesDownloadPath, bundlesPath);
+ if (s->value(Paths::bundlesDownloadPath.toString()).toString() != bundlesPath) {
+ s->setValue(Paths::bundlesDownloadPath.toString(), bundlesPath);
emit bundlesDownloadPathChanged(bundlesPath);
const QString restartText = tr("Changing bundle path will take effect after restart.");
@@ -202,11 +202,11 @@ StudioConfigSettingsPage::StudioConfigSettingsPage()
setCategory(Core::Constants::SETTINGS_CATEGORY_CORE);
setWidgetCreator([&] {
auto page = new StudioSettingsPage;
- connect(page,
+ QObject::connect(page,
&StudioSettingsPage::examplesDownloadPathChanged,
this,
&StudioConfigSettingsPage::examplesDownloadPathChanged);
- connect(page,
+ QObject::connect(page,
&StudioSettingsPage::bundlesDownloadPathChanged,
this,
&StudioConfigSettingsPage::bundlesDownloadPathChanged);
diff --git a/src/plugins/qmldesignerbase/studio/studiosettingspage.h b/src/plugins/qmldesignerbase/studio/studiosettingspage.h
index 0d0149f116..075367e4bb 100644
--- a/src/plugins/qmldesignerbase/studio/studiosettingspage.h
+++ b/src/plugins/qmldesignerbase/studio/studiosettingspage.h
@@ -34,7 +34,7 @@ private:
Utils::PathChooser *m_pathChooserBundles;
};
-class QMLDESIGNERBASE_EXPORT StudioConfigSettingsPage : public Core::IOptionsPage
+class QMLDESIGNERBASE_EXPORT StudioConfigSettingsPage : public QObject, Core::IOptionsPage
{
Q_OBJECT
diff --git a/src/plugins/qmldesignerbase/studio/studiostyle.h b/src/plugins/qmldesignerbase/studio/studiostyle.h
index 4d6424cbef..c55797354b 100644
--- a/src/plugins/qmldesignerbase/studio/studiostyle.h
+++ b/src/plugins/qmldesignerbase/studio/studiostyle.h
@@ -3,7 +3,7 @@
#pragma once
-#include "qmldesignerbase_global.h"
+#include "../qmldesignerbase_global.h"
#include <QProxyStyle>
diff --git a/src/plugins/qmldesignerbase/utils/designerpaths.cpp b/src/plugins/qmldesignerbase/utils/designerpaths.cpp
index d4ae2a4645..53b4b7ea37 100644
--- a/src/plugins/qmldesignerbase/utils/designerpaths.cpp
+++ b/src/plugins/qmldesignerbase/utils/designerpaths.cpp
@@ -31,14 +31,14 @@ Utils::FilePath defaultBundlesPath()
QString examplesPathSetting()
{
return Core::ICore::settings()
- ->value(exampleDownloadPath, defaultExamplesPath().toString())
+ ->value(exampleDownloadPath.toString(), defaultExamplesPath().toString())
.toString();
}
QString bundlesPathSetting()
{
return Core::ICore::settings()
- ->value(bundlesDownloadPath, defaultBundlesPath().toString())
+ ->value(bundlesDownloadPath.toString(), defaultBundlesPath().toString())
.toString();
}
diff --git a/src/plugins/qmljseditor/qmljscomponentnamedialog.cpp b/src/plugins/qmljseditor/qmljscomponentnamedialog.cpp
index ad370633bd..91bcc0ac89 100644
--- a/src/plugins/qmljseditor/qmljscomponentnamedialog.cpp
+++ b/src/plugins/qmljseditor/qmljscomponentnamedialog.cpp
@@ -34,7 +34,7 @@ ComponentNameDialog::ComponentNameDialog(QWidget *parent) :
m_checkBox = new QCheckBox(Tr::tr("ui.qml file"));
m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Form {
Tr::tr("Component name:"), m_componentNameEdit, br,
diff --git a/src/plugins/qmljseditor/qmljseditingsettingspage.cpp b/src/plugins/qmljseditor/qmljseditingsettingspage.cpp
index 2a0ecb4177..bfb5a002cf 100644
--- a/src/plugins/qmljseditor/qmljseditingsettingspage.cpp
+++ b/src/plugins/qmljseditor/qmljseditingsettingspage.cpp
@@ -7,18 +7,24 @@
#include <coreplugin/coreconstants.h>
#include <coreplugin/icore.h>
+#include <qmljs/qmljscheck.h>
+#include <qmljs/qmljsstaticanalysismessage.h>
+#include <qmljstools/qmljstoolsconstants.h>
+#include <utils/algorithm.h>
#include <utils/layoutbuilder.h>
#include <utils/macroexpander.h>
#include <utils/qtcsettings.h>
+#include <utils/treemodel.h>
#include <utils/variablechooser.h>
-#include <qmljstools/qmljstoolsconstants.h>
#include <QCheckBox>
#include <QComboBox>
#include <QLabel>
#include <QLineEdit>
+#include <QMenu>
#include <QSettings>
#include <QTextStream>
+#include <QTreeView>
const char AUTO_FORMAT_ON_SAVE[] = "QmlJSEditor.AutoFormatOnSave";
const char AUTO_FORMAT_ONLY_CURRENT_PROJECT[] = "QmlJSEditor.AutoFormatOnlyCurrentProject";
@@ -31,11 +37,30 @@ const char UIQML_OPEN_MODE[] = "QmlJSEditor.openUiQmlMode";
const char FORMAT_COMMAND[] = "QmlJSEditor.formatCommand";
const char FORMAT_COMMAND_OPTIONS[] = "QmlJSEditor.formatCommandOptions";
const char CUSTOM_COMMAND[] = "QmlJSEditor.useCustomFormatCommand";
+const char CUSTOM_ANALYZER[] = "QmlJSEditor.useCustomAnalyzer";
+const char DISABLED_MESSAGES[] = "QmlJSEditor.disabledMessages";
+const char DISABLED_MESSAGES_NONQUICKUI[] = "QmlJSEditor.disabledMessagesNonQuickUI";
const char DEFAULT_CUSTOM_FORMAT_COMMAND[] = "%{CurrentDocument:Project:QT_HOST_BINS}/qmlformat";
using namespace QmlJSEditor;
using namespace QmlJSEditor::Internal;
+static QList<int> defaultDisabledMessages()
+{
+ static const QList<int> disabledByDefault = Utils::transform(
+ QmlJS::Check::defaultDisabledMessages(),
+ [](QmlJS::StaticAnalysis::Type t) { return int(t); });
+ return disabledByDefault;
+}
+
+static QList<int> defaultDisabledMessagesNonQuickUi()
+{
+ static const QList<int> disabledForNonQuickUi = Utils::transform(
+ QmlJS::Check::defaultDisabledMessagesForNonQuickUi(),
+ [](QmlJS::StaticAnalysis::Type t){ return int(t); });
+ return disabledForNonQuickUi;
+}
+
void QmlJsEditingSettings::set()
{
if (get() != *this)
@@ -57,6 +82,17 @@ void QmlJsEditingSettings::fromSettings(QSettings *settings)
m_formatCommand = settings->value(FORMAT_COMMAND, {}).toString();
m_formatCommandOptions = settings->value(FORMAT_COMMAND_OPTIONS, {}).toString();
m_useCustomFormatCommand = settings->value(CUSTOM_COMMAND, QVariant(false)).toBool();
+ m_useCustomAnalyzer = settings->value(CUSTOM_ANALYZER, QVariant(false)).toBool();
+
+ m_disabledMessages = Utils::transform<QSet>(
+ settings->value(DISABLED_MESSAGES,
+ QVariant::fromValue(defaultDisabledMessages())).toList(),
+ [](const QVariant &v){ return v.toInt(); });
+ m_disabledMessagesForNonQuickUi = Utils::transform<QSet>(
+ settings->value(DISABLED_MESSAGES_NONQUICKUI,
+ QVariant::fromValue(defaultDisabledMessagesNonQuickUi())).toList(),
+ [](const QVariant &v) { return v.toInt(); });
+
settings->endGroup();
}
@@ -80,6 +116,18 @@ void QmlJsEditingSettings::toSettings(QSettings *settings) const
CUSTOM_COMMAND,
m_useCustomFormatCommand,
false);
+ Utils::QtcSettings::setValueWithDefault(settings,
+ CUSTOM_ANALYZER,
+ m_useCustomAnalyzer,
+ false);
+ Utils::QtcSettings::setValueWithDefault(settings,
+ DISABLED_MESSAGES,
+ Utils::sorted(Utils::toList(m_disabledMessages)),
+ defaultDisabledMessages());
+ Utils::QtcSettings::setValueWithDefault(settings,
+ DISABLED_MESSAGES_NONQUICKUI,
+ Utils::sorted(Utils::toList(m_disabledMessagesForNonQuickUi)),
+ defaultDisabledMessagesNonQuickUi());
settings->endGroup();
QmllsSettingsManager::instance()->checkForChanges();
}
@@ -93,7 +141,10 @@ bool QmlJsEditingSettings::equals(const QmlJsEditingSettings &other) const
&& m_foldAuxData == other.m_foldAuxData && m_qmllsSettings == other.m_qmllsSettings
&& m_uiQmlOpenMode == other.m_uiQmlOpenMode && m_formatCommand == other.m_formatCommand
&& m_formatCommandOptions == other.m_formatCommandOptions
- && m_useCustomFormatCommand == other.m_useCustomFormatCommand;
+ && m_useCustomFormatCommand == other.m_useCustomFormatCommand
+ && m_useCustomAnalyzer == other.m_useCustomAnalyzer
+ && m_disabledMessages == other.m_disabledMessages
+ && m_disabledMessagesForNonQuickUi == other.m_disabledMessagesForNonQuickUi;
}
bool QmlJsEditingSettings::enableContextPane() const
@@ -181,12 +232,12 @@ void QmlJsEditingSettings::setUseCustomFormatCommand(bool customCommand)
m_useCustomFormatCommand = customCommand;
}
-QmllsSettings &QmlJsEditingSettings::qmllsSettigs()
+QmllsSettings &QmlJsEditingSettings::qmllsSettings()
{
return m_qmllsSettings;
}
-const QmllsSettings &QmlJsEditingSettings::qmllsSettigs() const
+const QmllsSettings &QmlJsEditingSettings::qmllsSettings() const
{
return m_qmllsSettings;
}
@@ -201,6 +252,92 @@ void QmlJsEditingSettings::setUiQmlOpenMode(const QString &mode)
m_uiQmlOpenMode = mode;
}
+bool QmlJsEditingSettings::useCustomAnalyzer() const
+{
+ return m_useCustomAnalyzer;
+}
+
+void QmlJsEditingSettings::setUseCustomAnalyzer(bool customAnalyzer)
+{
+ m_useCustomAnalyzer = customAnalyzer;
+}
+
+QSet<int> QmlJsEditingSettings::disabledMessages() const
+{
+ return m_disabledMessages;
+}
+
+void QmlJsEditingSettings::setDisabledMessages(const QSet<int> &disabled)
+{
+ m_disabledMessages = disabled;
+}
+
+QSet<int> QmlJsEditingSettings::disabledMessagesForNonQuickUi() const
+{
+ return m_disabledMessagesForNonQuickUi;
+}
+
+void QmlJsEditingSettings::setDisabledMessagesForNonQuickUi(const QSet<int> &disabled)
+{
+ m_disabledMessagesForNonQuickUi = disabled;
+}
+
+class AnalyzerMessageItem final : public Utils::TreeItem
+{
+public:
+ AnalyzerMessageItem() = default;
+ AnalyzerMessageItem(int number, const QString &message)
+ : m_messageNumber(number)
+ , m_message(message)
+ {}
+
+ QVariant data(int column, int role) const final
+ {
+ if (role == Qt::DisplayRole) {
+ if (column == 0)
+ return QString("M%1").arg(m_messageNumber);
+ if (column == 2)
+ return m_message.split('\n').first();
+ } else if (role == Qt::CheckStateRole) {
+ if (column == 0)
+ return m_checked ? Qt::Checked : Qt::Unchecked;
+ if (column == 1)
+ return m_disabledInNonQuickUi ? Qt::Checked : Qt::Unchecked;
+ }
+ return TreeItem::data(column, role);
+ }
+
+ bool setData(int column, const QVariant &value, int role) final
+ {
+ if (role == Qt::CheckStateRole) {
+ if (column == 0) {
+ m_checked = value.toBool();
+ return true;
+ }
+ if (column == 1) {
+ m_disabledInNonQuickUi = value.toBool();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ Qt::ItemFlags flags(int column) const final
+ {
+ if (column == 0 || column == 1)
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
+ else
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+ }
+
+ int messageNumber() const { return m_messageNumber; }
+private:
+ int m_messageNumber = -1;
+ QString m_message;
+ bool m_checked = true;
+ bool m_disabledInNonQuickUi = false;
+};
+
class QmlJsEditingSettingsPageWidget final : public Core::IOptionsPageWidget
{
public:
@@ -239,17 +376,38 @@ public:
uiQmlOpenComboBox->setSizeAdjustPolicy(QComboBox::QComboBox::AdjustToContents);
useQmlls = new QCheckBox(Tr::tr("Use qmlls (EXPERIMENTAL!)"));
- useQmlls->setChecked(s.qmllsSettigs().useQmlls);
+ useQmlls->setChecked(s.qmllsSettings().useQmlls);
useLatestQmlls = new QCheckBox(Tr::tr("Always use latest qmlls"));
- useLatestQmlls->setChecked(s.qmllsSettigs().useLatestQmlls);
- useLatestQmlls->setEnabled(s.qmllsSettigs().useQmlls);
+ useLatestQmlls->setChecked(s.qmllsSettings().useLatestQmlls);
+ useLatestQmlls->setEnabled(s.qmllsSettings().useQmlls);
QObject::connect(useQmlls, &QCheckBox::stateChanged, this, [this](int checked) {
useLatestQmlls->setEnabled(checked != Qt::Unchecked);
});
- using namespace Utils::Layouting;
+
+ useCustomAnalyzer = new QCheckBox(Tr::tr("Use customized static analyzer"));
+ useCustomAnalyzer->setChecked(s.useCustomAnalyzer());
+ analyzerMessageModel = new Utils::TreeModel<AnalyzerMessageItem>(this);
+ analyzerMessageModel->setHeader({Tr::tr("Enabled"),
+ Tr::tr("Disabled for non Qt Quick UI"),
+ Tr::tr("Message")});
+ analyzerMessagesView = new QTreeView;
+ analyzerMessagesView->setModel(analyzerMessageModel);
+ analyzerMessagesView->setEnabled(s.useCustomAnalyzer());
+ QObject::connect(useCustomAnalyzer, &QCheckBox::stateChanged, this, [this](int checked){
+ analyzerMessagesView->setEnabled(checked != Qt::Unchecked);
+ });
+ analyzerMessagesView->setToolTip(Tr::tr("Enabled checks can be disabled for non Qt Quick UI"
+ " files,\nbut disabled checks cannot get explicitly"
+ " enabled for non Qt Quick UI files."));
+ analyzerMessagesView->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(analyzerMessagesView, &QTreeView::customContextMenuRequested,
+ this, &QmlJsEditingSettingsPageWidget::showContextMenu);
+ using namespace Layouting;
// clang-format off
- const auto formattingGroup =
+ QWidget *formattingGroup = nullptr;
+ Column {
Group {
+ bindTo(&formattingGroup),
title(Tr::tr("Automatic Formatting on File Save")),
Column {
autoFormatOnSave,
@@ -260,10 +418,7 @@ public:
formatCommandOptionsLabel, formatCommandOptions
}
},
- };
-
- Column {
- formattingGroup,
+ },
Group {
title(Tr::tr("Qt Quick Toolbars")),
Column { pinContextPane, enableContextPane },
@@ -279,16 +434,19 @@ public:
title(Tr::tr("Language Server")),
Column{useQmlls, useLatestQmlls},
},
+ Group {
+ title(Tr::tr("Static Analyzer")),
+ Column{ useCustomAnalyzer, analyzerMessagesView },
+ },
st,
}.attachTo(this);
// clang-format on
- Utils::VariableChooser::addSupportForChildWidgets(formattingGroup.widget,
+ Utils::VariableChooser::addSupportForChildWidgets(formattingGroup,
Utils::globalMacroExpander());
const auto updateFormatCommandState = [&, formatCommandLabel, formatCommandOptionsLabel] {
- const bool enabled = useCustomFormatCommand->isChecked()
- && autoFormatOnSave->isChecked();
+ const bool enabled = useCustomFormatCommand->isChecked();
formatCommandLabel->setEnabled(enabled);
formatCommand->setEnabled(enabled);
formatCommandOptionsLabel->setEnabled(enabled);
@@ -298,10 +456,11 @@ public:
connect(autoFormatOnSave, &QCheckBox::toggled, this, [&, updateFormatCommandState]() {
autoFormatOnlyCurrentProject->setEnabled(autoFormatOnSave->isChecked());
- useCustomFormatCommand->setEnabled(autoFormatOnSave->isChecked());
updateFormatCommandState();
});
connect(useCustomFormatCommand, &QCheckBox::toggled, this, updateFormatCommandState);
+
+ populateAnalyzerMessages(s.disabledMessages(), s.disabledMessagesForNonQuickUi());
}
void apply() final
@@ -316,12 +475,55 @@ public:
s.setFormatCommandOptions(formatCommandOptions->text());
s.setFoldAuxData(foldAuxData->isChecked());
s.setUiQmlOpenMode(uiQmlOpenComboBox->currentData().toString());
- s.qmllsSettigs().useQmlls = useQmlls->isChecked();
- s.qmllsSettigs().useLatestQmlls = useLatestQmlls->isChecked();
+ s.qmllsSettings().useQmlls = useQmlls->isChecked();
+ s.qmllsSettings().useLatestQmlls = useLatestQmlls->isChecked();
+ s.setUseCustomAnalyzer(useCustomAnalyzer->isChecked());
+ QSet<int> disabled;
+ QSet<int> disabledForNonQuickUi;
+ analyzerMessageModel->forAllItems(
+ [&disabled, &disabledForNonQuickUi](AnalyzerMessageItem *item){
+ if (item->data(0, Qt::CheckStateRole) == Qt::Unchecked)
+ disabled.insert(item->messageNumber());
+ if (item->data(1, Qt::CheckStateRole) == Qt::Checked)
+ disabledForNonQuickUi.insert(item->messageNumber());
+ });
+ s.setDisabledMessages(disabled);
+ s.setDisabledMessagesForNonQuickUi(disabledForNonQuickUi);
s.set();
}
private:
+ void populateAnalyzerMessages(const QSet<int> &disabled, const QSet<int> &disabledForNonQuickUi)
+ {
+ using namespace QmlJS::StaticAnalysis;
+ auto knownMessages = Utils::sorted(Message::allMessageTypes());
+ auto root = analyzerMessageModel->rootItem();
+ for (auto msgType : knownMessages) {
+ const QString msg = Message::prototypeForMessageType(msgType).message;
+ auto item = new AnalyzerMessageItem(msgType, msg);
+ item->setData(0, !disabled.contains(msgType), Qt::CheckStateRole);
+ item->setData(1, disabledForNonQuickUi.contains(msgType), Qt::CheckStateRole);
+ root->appendChild(item);
+ }
+
+ for (int column = 0; column < 3; ++column)
+ analyzerMessagesView->resizeColumnToContents(column);
+ }
+
+ void showContextMenu(const QPoint &position)
+ {
+ QMenu menu;
+ QAction *reset = new QAction(Tr::tr("Reset to Default"), &menu);
+ menu.addAction(reset);
+ connect(reset, &QAction::triggered, this, [this](){
+ analyzerMessageModel->clear();
+ populateAnalyzerMessages(Utils::toSet(defaultDisabledMessages()),
+ Utils::toSet(defaultDisabledMessagesNonQuickUi()));
+
+ });
+ menu.exec(analyzerMessagesView->mapToGlobal(position));
+ }
+
QCheckBox *autoFormatOnSave;
QCheckBox *autoFormatOnlyCurrentProject;
QCheckBox *useCustomFormatCommand;
@@ -333,6 +535,9 @@ private:
QCheckBox *useQmlls;
QCheckBox *useLatestQmlls;
QComboBox *uiQmlOpenComboBox;
+ QCheckBox *useCustomAnalyzer;
+ QTreeView *analyzerMessagesView;
+ Utils::TreeModel<AnalyzerMessageItem> *analyzerMessageModel;
};
diff --git a/src/plugins/qmljseditor/qmljseditingsettingspage.h b/src/plugins/qmljseditor/qmljseditingsettingspage.h
index 43aa9ee336..d7f8f78bd3 100644
--- a/src/plugins/qmljseditor/qmljseditingsettingspage.h
+++ b/src/plugins/qmljseditor/qmljseditingsettingspage.h
@@ -52,12 +52,21 @@ public:
bool useCustomFormatCommand() const;
void setUseCustomFormatCommand(bool customCommand);
- QmllsSettings &qmllsSettigs();
- const QmllsSettings &qmllsSettigs() const;
+ QmllsSettings &qmllsSettings();
+ const QmllsSettings &qmllsSettings() const;
const QString uiQmlOpenMode() const;
void setUiQmlOpenMode(const QString &mode);
+ bool useCustomAnalyzer() const;
+ void setUseCustomAnalyzer(bool customAnalyzer);
+
+ QSet<int> disabledMessages() const;
+ void setDisabledMessages(const QSet<int> &disabled);
+
+ QSet<int> disabledMessagesForNonQuickUi() const;
+ void setDisabledMessagesForNonQuickUi(const QSet<int> &disabled);
+
friend bool operator==(const QmlJsEditingSettings &s1, const QmlJsEditingSettings &s2)
{ return s1.equals(s2); }
friend bool operator!=(const QmlJsEditingSettings &s1, const QmlJsEditingSettings &s2)
@@ -70,10 +79,13 @@ private:
bool m_autoFormatOnlyCurrentProject = false;
bool m_foldAuxData = true;
bool m_useCustomFormatCommand = false;
+ bool m_useCustomAnalyzer = false;
QmllsSettings m_qmllsSettings;
QString m_uiQmlOpenMode;
QString m_formatCommand;
QString m_formatCommandOptions;
+ QSet<int> m_disabledMessages;
+ QSet<int> m_disabledMessagesForNonQuickUi;
};
namespace Internal {
diff --git a/src/plugins/qmljseditor/qmljseditorplugin.cpp b/src/plugins/qmljseditor/qmljseditorplugin.cpp
index 884670c4fa..d695989825 100644
--- a/src/plugins/qmljseditor/qmljseditorplugin.cpp
+++ b/src/plugins/qmljseditor/qmljseditorplugin.cpp
@@ -210,11 +210,6 @@ void QmlJSEditorPlugin::extensionsInitialized()
QmllsSettingsManager::instance()->setupAutoupdate();
}
-ExtensionSystem::IPlugin::ShutdownFlag QmlJSEditorPlugin::aboutToShutdown()
-{
- return IPlugin::aboutToShutdown();
-}
-
Utils::JsonSchemaManager *QmlJSEditorPlugin::jsonManager()
{
return &m_instance->d->m_jsonManager;
diff --git a/src/plugins/qmljseditor/qmljseditorplugin.h b/src/plugins/qmljseditor/qmljseditorplugin.h
index cc1b63fe68..aa653ac6cb 100644
--- a/src/plugins/qmljseditor/qmljseditorplugin.h
+++ b/src/plugins/qmljseditor/qmljseditorplugin.h
@@ -29,7 +29,6 @@ public:
private:
void initialize() final;
void extensionsInitialized() final;
- ShutdownFlag aboutToShutdown() final;
class QmlJSEditorPluginPrivate *d = nullptr;
};
diff --git a/src/plugins/qmljseditor/qmljsfindreferences.cpp b/src/plugins/qmljseditor/qmljsfindreferences.cpp
index b37d0e8758..5e72cd642b 100644
--- a/src/plugins/qmljseditor/qmljsfindreferences.cpp
+++ b/src/plugins/qmljseditor/qmljsfindreferences.cpp
@@ -12,8 +12,8 @@
#include <extensionsystem/pluginmanager.h>
#include <texteditor/basefilefind.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/filesearch.h>
-#include <utils/runextensions.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <qmljs/qmljsbind.h>
@@ -26,19 +26,10 @@
#include <qmljs/parser/qmljsast_p.h>
#include <qmljstools/qmljsmodelmanager.h>
-#include "qmljseditorconstants.h"
-
-#include <QApplication>
#include <QDebug>
-#include <QDir>
#include <QFuture>
-#include <QLabel>
-#include <QTime>
-#include <QTimer>
#include <QtConcurrentMap>
-#include <functional>
-
using namespace Core;
using namespace QmlJS;
using namespace QmlJS::AST;
@@ -704,7 +695,7 @@ class ProcessFile
using Usage = FindReferences::Usage;
const QString name;
const ObjectValue *scope;
- QFutureInterface<Usage> *future;
+ QPromise<Usage> &m_promise;
public:
// needed by QtConcurrent
@@ -714,16 +705,15 @@ public:
ProcessFile(const ContextPtr &context,
const QString &name,
const ObjectValue *scope,
- QFutureInterface<Usage> *future)
- : context(context), name(name), scope(scope), future(future)
+ QPromise<Usage> &promise)
+ : context(context), name(name), scope(scope), m_promise(promise)
{ }
QList<Usage> operator()(const Utils::FilePath &fileName)
{
QList<Usage> usages;
- if (future->isPaused())
- future->waitForResume();
- if (future->isCanceled())
+ m_promise.suspendIfRequested();
+ if (m_promise.isCanceled())
return usages;
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
Document::Ptr doc = context->snapshot().document(fileName);
@@ -739,8 +729,7 @@ public:
loc.startLine,
loc.startColumn - 1,
loc.length));
- if (future->isPaused())
- future->waitForResume();
+ m_promise.suspendIfRequested();
return usages;
}
};
@@ -751,7 +740,7 @@ class SearchFileForType
using Usage = FindReferences::Usage;
const QString name;
const ObjectValue *scope;
- QFutureInterface<Usage> *future;
+ QPromise<Usage> &m_promise;
public:
// needed by QtConcurrent
@@ -761,16 +750,15 @@ public:
SearchFileForType(const ContextPtr &context,
const QString &name,
const ObjectValue *scope,
- QFutureInterface<Usage> *future)
- : context(context), name(name), scope(scope), future(future)
+ QPromise<Usage> &promise)
+ : context(context), name(name), scope(scope), m_promise(promise)
{ }
QList<Usage> operator()(const Utils::FilePath &fileName)
{
QList<Usage> usages;
- if (future->isPaused())
- future->waitForResume();
- if (future->isCanceled())
+ m_promise.suspendIfRequested();
+ if (m_promise.isCanceled())
return usages;
Document::Ptr doc = context->snapshot().document(fileName);
if (!doc)
@@ -781,8 +769,7 @@ public:
const FindTypeUsages::Result results = findUsages(name, scope);
for (const SourceLocation &loc : results)
usages.append(Usage(fileName, matchingLine(loc.offset, doc->source()), loc.startLine, loc.startColumn - 1, loc.length));
- if (future->isPaused())
- future->waitForResume();
+ m_promise.suspendIfRequested();
return usages;
}
};
@@ -790,7 +777,7 @@ public:
class UpdateUI
{
using Usage = FindReferences::Usage;
- QFutureInterface<Usage> *future;
+ QPromise<Usage> &m_promise;
public:
// needed by QtConcurrent
@@ -798,14 +785,13 @@ public:
using second_argument_type = const QList<Usage> &;
using result_type = void;
- UpdateUI(QFutureInterface<Usage> *future): future(future) {}
+ UpdateUI(QPromise<Usage> &promise): m_promise(promise) {}
void operator()(QList<Usage> &, const QList<Usage> &usages)
{
for (const Usage &u : usages)
- future->reportResult(u);
-
- future->setProgressValue(future->progressValue() + 1);
+ m_promise.addResult(u);
+ m_promise.setProgressValue(m_promise.future().progressValue() + 1);
}
};
@@ -817,12 +803,11 @@ FindReferences::FindReferences(QObject *parent)
m_watcher.setPendingResultsLimit(1);
connect(&m_watcher, &QFutureWatcherBase::resultsReadyAt, this, &FindReferences::displayResults);
connect(&m_watcher, &QFutureWatcherBase::finished, this, &FindReferences::searchFinished);
- m_synchronizer.setCancelOnWait(true);
}
FindReferences::~FindReferences() = default;
-static void find_helper(QFutureInterface<FindReferences::Usage> &future,
+static void find_helper(QPromise<FindReferences::Usage> &promise,
const ModelManagerInterface::WorkingCopy &workingCopy,
Snapshot snapshot,
const Utils::FilePath &fileName,
@@ -885,7 +870,7 @@ static void find_helper(QFutureInterface<FindReferences::Usage> &future,
}
files = Utils::filteredUnique(files);
- future.setProgressRange(0, files.size());
+ promise.setProgressRange(0, files.size());
// report a dummy usage to indicate the search is starting
FindReferences::Usage searchStarting(Utils::FilePath::fromString(replacement), name, 0, 0, 0);
@@ -894,10 +879,10 @@ static void find_helper(QFutureInterface<FindReferences::Usage> &future,
const ObjectValue *typeValue = value_cast<ObjectValue>(findTarget.targetValue());
if (!typeValue)
return;
- future.reportResult(searchStarting);
+ promise.addResult(searchStarting);
- SearchFileForType process(context, name, typeValue, &future);
- UpdateUI reduce(&future);
+ SearchFileForType process(context, name, typeValue, promise);
+ UpdateUI reduce(promise);
QtConcurrent::blockingMappedReduced<QList<FindReferences::Usage> > (files, process, reduce);
} else {
@@ -909,21 +894,21 @@ static void find_helper(QFutureInterface<FindReferences::Usage> &future,
return;
if (!scope->className().isEmpty())
searchStarting.lineText.prepend(scope->className() + QLatin1Char('.'));
- future.reportResult(searchStarting);
+ promise.addResult(searchStarting);
- ProcessFile process(context, name, scope, &future);
- UpdateUI reduce(&future);
+ ProcessFile process(context, name, scope, promise);
+ UpdateUI reduce(promise);
QtConcurrent::blockingMappedReduced<QList<FindReferences::Usage> > (files, process, reduce);
}
- future.setProgressValue(files.size());
+ promise.setProgressValue(files.size());
}
void FindReferences::findUsages(const Utils::FilePath &fileName, quint32 offset)
{
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
- QFuture<Usage> result = Utils::runAsync(&find_helper, ModelManagerInterface::workingCopy(),
+ QFuture<Usage> result = Utils::asyncRun(&find_helper, ModelManagerInterface::workingCopy(),
modelManager->snapshot(), fileName, offset, QString());
m_watcher.setFuture(result);
m_synchronizer.addFuture(result);
@@ -940,7 +925,7 @@ void FindReferences::renameUsages(const Utils::FilePath &fileName,
if (newName.isNull())
newName = QLatin1String("");
- QFuture<Usage> result = Utils::runAsync(&find_helper, ModelManagerInterface::workingCopy(),
+ QFuture<Usage> result = Utils::asyncRun(&find_helper, ModelManagerInterface::workingCopy(),
modelManager->snapshot(), fileName, offset, newName);
m_watcher.setFuture(result);
m_synchronizer.addFuture(result);
@@ -1007,7 +992,7 @@ void FindReferences::displayResults(int first, int last)
this, &FindReferences::onReplaceButtonClicked);
}
connect(m_currentSearch.data(), &SearchResult::activated,
- [](const Core::SearchResultItem& item) {
+ [](const Utils::SearchResultItem &item) {
Core::EditorManager::openEditorAtSearchResult(item);
});
connect(m_currentSearch.data(), &SearchResult::canceled, this, &FindReferences::cancel);
@@ -1028,7 +1013,7 @@ void FindReferences::displayResults(int first, int last)
}
for (int index = first; index != last; ++index) {
Usage result = m_watcher.future().resultAt(index);
- SearchResultItem item;
+ Utils::SearchResultItem item;
item.setFilePath(result.path);
item.setLineText(result.lineText);
item.setMainRange(result.line, result.col, result.len);
@@ -1056,7 +1041,8 @@ void FindReferences::setPaused(bool paused)
m_watcher.setPaused(paused);
}
-void FindReferences::onReplaceButtonClicked(const QString &text, const QList<SearchResultItem> &items, bool preserveCase)
+void FindReferences::onReplaceButtonClicked(const QString &text,
+ const Utils::SearchResultItems &items, bool preserveCase)
{
const Utils::FilePaths filePaths = TextEditor::BaseFileFind::replaceAll(text,
items,
diff --git a/src/plugins/qmljseditor/qmljsfindreferences.h b/src/plugins/qmljseditor/qmljsfindreferences.h
index 22bdff0a95..c45d77ac18 100644
--- a/src/plugins/qmljseditor/qmljsfindreferences.h
+++ b/src/plugins/qmljseditor/qmljsfindreferences.h
@@ -7,6 +7,7 @@
#include <utils/filepath.h>
#include <utils/futuresynchronizer.h>
+#include <utils/searchresultitem.h>
#include <QObject>
#include <QFutureWatcher>
@@ -14,10 +15,7 @@
QT_FORWARD_DECLARE_CLASS(QTimer)
-namespace Core {
-class SearchResultItem;
-class SearchResult;
-} // namespace Core
+namespace Core { class SearchResult; }
namespace QmlJSEditor {
@@ -64,7 +62,8 @@ private:
void searchFinished();
void cancel();
void setPaused(bool paused);
- void onReplaceButtonClicked(const QString &text, const QList<Core::SearchResultItem> &items, bool preserveCase);
+ void onReplaceButtonClicked(const QString &text, const Utils::SearchResultItems &items,
+ bool preserveCase);
QPointer<Core::SearchResult> m_currentSearch;
QFutureWatcher<Usage> m_watcher;
diff --git a/src/plugins/qmljseditor/qmljsquickfix.h b/src/plugins/qmljseditor/qmljsquickfix.h
index 643334a94c..b050ba4d1f 100644
--- a/src/plugins/qmljseditor/qmljsquickfix.h
+++ b/src/plugins/qmljseditor/qmljsquickfix.h
@@ -44,7 +44,7 @@ protected:
const QmlJSTools::SemanticInfo &semanticInfo() const;
- /// \returns The name of the file for for which this operation is invoked.
+ /// Returns The name of the file for for which this operation is invoked.
Utils::FilePath fileName() const;
private:
diff --git a/src/plugins/qmljseditor/qmljssemantichighlighter.cpp b/src/plugins/qmljseditor/qmljssemantichighlighter.cpp
index 0ca826cb00..67e14dfbcd 100644
--- a/src/plugins/qmljseditor/qmljssemantichighlighter.cpp
+++ b/src/plugins/qmljseditor/qmljssemantichighlighter.cpp
@@ -20,9 +20,9 @@
#include <texteditor/texteditorconstants.h>
#include <texteditor/texteditorsettings.h>
#include <texteditor/fontsettings.h>
+#include <utils/async.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <QDebug>
#include <QTextDocument>
@@ -156,11 +156,11 @@ public:
AddMessagesHighlights,
SkipMessagesHighlights,
};
- CollectionTask(QFutureInterface<SemanticHighlighter::Use> &futureInterface,
+ CollectionTask(QPromise<SemanticHighlighter::Use> &promise,
const QmlJSTools::SemanticInfo &semanticInfo,
const TextEditor::FontSettings &fontSettings,
Flags flags)
- : m_futureInterface(futureInterface)
+ : m_promise(promise)
, m_semanticInfo(semanticInfo)
, m_fontSettings(fontSettings)
, m_scopeChain(semanticInfo.scopeChain())
@@ -211,7 +211,7 @@ public:
protected:
void accept(Node *ast)
{
- if (m_futureInterface.isCanceled())
+ if (m_promise.isCanceled())
return;
if (ast)
ast->accept(this);
@@ -219,7 +219,7 @@ protected:
void scopedAccept(Node *ast, Node *child)
{
- if (m_futureInterface.isCanceled())
+ if (m_promise.isCanceled())
return;
m_scopeBuilder.push(ast);
accept(child);
@@ -510,12 +510,13 @@ private:
return;
Utils::sort(m_uses, sortByLinePredicate);
- m_futureInterface.reportResults(m_uses);
+ for (const SemanticHighlighter::Use &use : std::as_const(m_uses))
+ m_promise.addResult(use);
m_uses.clear();
m_uses.reserve(chunkSize);
}
- QFutureInterface<SemanticHighlighter::Use> &m_futureInterface;
+ QPromise<SemanticHighlighter::Use> &m_promise;
const QmlJSTools::SemanticInfo &m_semanticInfo;
const TextEditor::FontSettings &m_fontSettings;
ScopeChain m_scopeChain;
@@ -541,7 +542,6 @@ SemanticHighlighter::SemanticHighlighter(QmlJSEditorDocument *document)
this, &SemanticHighlighter::applyResults);
connect(&m_watcher, &QFutureWatcherBase::finished,
this, &SemanticHighlighter::finished);
- m_futureSynchronizer.setCancelOnWait(true);
}
void SemanticHighlighter::rerun(const QmlJSTools::SemanticInfo &semanticInfo)
@@ -549,11 +549,8 @@ void SemanticHighlighter::rerun(const QmlJSTools::SemanticInfo &semanticInfo)
m_watcher.cancel();
m_startRevision = m_document->document()->revision();
- auto future = Utils::runAsync(QThread::LowestPriority,
- &SemanticHighlighter::run,
- this,
- semanticInfo,
- TextEditor::TextEditorSettings::fontSettings());
+ auto future = Utils::asyncRun(QThread::LowestPriority, &SemanticHighlighter::run, this,
+ semanticInfo, TextEditor::TextEditorSettings::fontSettings());
m_watcher.setFuture(future);
m_futureSynchronizer.addFuture(future);
}
@@ -590,11 +587,11 @@ void SemanticHighlighter::finished()
m_document->syntaxHighlighter(), m_watcher.future());
}
-void SemanticHighlighter::run(QFutureInterface<SemanticHighlighter::Use> &futureInterface,
+void SemanticHighlighter::run(QPromise<Use> &promise,
const QmlJSTools::SemanticInfo &semanticInfo,
const TextEditor::FontSettings &fontSettings)
{
- CollectionTask task(futureInterface,
+ CollectionTask task(promise,
semanticInfo,
fontSettings,
(m_enableWarnings ? CollectionTask::AddMessagesHighlights
diff --git a/src/plugins/qmljseditor/qmljssemantichighlighter.h b/src/plugins/qmljseditor/qmljssemantichighlighter.h
index 6ea1e85c6e..74c51d12d7 100644
--- a/src/plugins/qmljseditor/qmljssemantichighlighter.h
+++ b/src/plugins/qmljseditor/qmljssemantichighlighter.h
@@ -62,7 +62,7 @@ public:
private:
void applyResults(int from, int to);
void finished();
- void run(QFutureInterface<Use> &futureInterface,
+ void run(QPromise<Use> &promise,
const QmlJSTools::SemanticInfo &semanticInfo,
const TextEditor::FontSettings &fontSettings);
diff --git a/src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp b/src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp
index e0a20c1845..764f125e54 100644
--- a/src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp
+++ b/src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp
@@ -104,7 +104,7 @@ QmlJSTools::SemanticInfo SemanticInfoUpdater::makeNewSemanticInfo(const QmlJS::D
semanticInfo.staticAnalysisMessages = jsonChecker(schema);
}
} else {
- Check checker(doc, semanticInfo.context);
+ Check checker(doc, semanticInfo.context, Core::ICore::settings());
semanticInfo.staticAnalysisMessages = checker();
}
diff --git a/src/plugins/qmljseditor/qmllssettings.cpp b/src/plugins/qmljseditor/qmllssettings.cpp
index 572d8aec78..eb98c041fa 100644
--- a/src/plugins/qmljseditor/qmllssettings.cpp
+++ b/src/plugins/qmljseditor/qmllssettings.cpp
@@ -90,7 +90,7 @@ void QmllsSettingsManager::setupAutoupdate()
void QmllsSettingsManager::checkForChanges()
{
FilePath newLatest = evaluateLatestQmlls();
- QmllsSettings newSettings = QmlJsEditingSettings::get().qmllsSettigs();
+ QmllsSettings newSettings = QmlJsEditingSettings::get().qmllsSettings();
if (m_lastSettings == newSettings && newLatest == m_latestQmlls)
return;
qCDebug(qmllsLog) << "qmlls settings changed:" << newSettings.useQmlls
diff --git a/src/plugins/qmljseditor/qmltaskmanager.cpp b/src/plugins/qmljseditor/qmltaskmanager.cpp
index 08019019da..ca985f4022 100644
--- a/src/plugins/qmljseditor/qmltaskmanager.cpp
+++ b/src/plugins/qmljseditor/qmltaskmanager.cpp
@@ -2,9 +2,9 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qmltaskmanager.h"
-#include "qmljseditor.h"
#include "qmljseditorconstants.h"
+#include <coreplugin/icore.h>
#include <coreplugin/idocument.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/taskhub.h>
@@ -13,7 +13,7 @@
#include <qmljs/qmljsconstants.h>
#include <qmljs/qmljslink.h>
#include <qmljs/qmljscheck.h>
-#include <utils/runextensions.h>
+#include <utils/async.h>
#include <QDebug>
#include <QtConcurrentRun>
@@ -57,7 +57,7 @@ static Tasks convertToTasks(const QList<StaticAnalysis::Message> &messages, cons
return convertToTasks(diagnostics, fileName, category);
}
-void QmlTaskManager::collectMessages(QFutureInterface<FileErrorMessages> &future,
+void QmlTaskManager::collectMessages(QPromise<FileErrorMessages> &promise,
Snapshot snapshot,
const QList<ModelManagerInterface::ProjectInfo> &projectInfos,
ViewerContext vContext,
@@ -88,7 +88,7 @@ void QmlTaskManager::collectMessages(QFutureInterface<FileErrorMessages> &future
fileName,
Constants::TASK_CATEGORY_QML_ANALYSIS);
- Check checker(document, context);
+ Check checker(document, context, Core::ICore::settings());
result.tasks += convertToTasks(checker(),
fileName,
Constants::TASK_CATEGORY_QML_ANALYSIS);
@@ -96,8 +96,8 @@ void QmlTaskManager::collectMessages(QFutureInterface<FileErrorMessages> &future
}
if (!result.tasks.isEmpty())
- future.reportResult(result);
- if (future.isCanceled())
+ promise.addResult(result);
+ if (promise.isCanceled())
break;
}
}
@@ -127,8 +127,7 @@ void QmlTaskManager::updateMessagesNow(bool updateSemantic)
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
// process them
- QFuture<FileErrorMessages> future =
- Utils::runAsync(
+ QFuture<FileErrorMessages> future = Utils::asyncRun(
&collectMessages, modelManager->newestSnapshot(), modelManager->projectInfos(),
modelManager->defaultVContext(Dialect::AnyLanguage), updateSemantic);
m_messageCollector.setFuture(future);
diff --git a/src/plugins/qmljseditor/qmltaskmanager.h b/src/plugins/qmljseditor/qmltaskmanager.h
index 26fa3bd347..226fd2203d 100644
--- a/src/plugins/qmljseditor/qmltaskmanager.h
+++ b/src/plugins/qmljseditor/qmltaskmanager.h
@@ -45,7 +45,7 @@ private:
Utils::FilePath fileName;
ProjectExplorer::Tasks tasks;
};
- static void collectMessages(QFutureInterface<FileErrorMessages> &future,
+ static void collectMessages(QPromise<FileErrorMessages> &promise,
QmlJS::Snapshot snapshot,
const QList<QmlJS::ModelManagerInterface::ProjectInfo> &projectInfos,
QmlJS::ViewerContext vContext,
diff --git a/src/plugins/qmljstools/qmljsbundleprovider.cpp b/src/plugins/qmljstools/qmljsbundleprovider.cpp
index b4902aa960..623a6f4684 100644
--- a/src/plugins/qmljstools/qmljsbundleprovider.cpp
+++ b/src/plugins/qmljstools/qmljsbundleprovider.cpp
@@ -25,7 +25,8 @@ BasicBundleProvider::BasicBundleProvider(QObject *parent) :
IBundleProvider(parent)
{ }
-QmlBundle BasicBundleProvider::defaultBundle(const QString &bundleInfoName)
+QmlBundle BasicBundleProvider::defaultBundle(const QString &bundleInfoName,
+ QtSupport::QtVersion *qtVersion)
{
static bool wroteErrors = false;
QmlBundle res;
@@ -37,7 +38,8 @@ QmlBundle BasicBundleProvider::defaultBundle(const QString &bundleInfoName)
return res;
}
QStringList errors;
- if (!res.readFrom(defaultBundlePath.toString(), &errors) && !wroteErrors) {
+ bool stripVersions = qtVersion && qtVersion->qtVersion().majorVersion() > 5;
+ if (!res.readFrom(defaultBundlePath.toString(), stripVersions, &errors) && !wroteErrors) {
qWarning() << "BasicBundleProvider: ERROR reading " << defaultBundlePath
<< " : " << errors;
wroteErrors = true;
@@ -45,24 +47,31 @@ QmlBundle BasicBundleProvider::defaultBundle(const QString &bundleInfoName)
return res;
}
-QmlBundle BasicBundleProvider::defaultQt5QtQuick2Bundle()
+QmlBundle BasicBundleProvider::defaultQt5QtQuick2Bundle(QtSupport::QtVersion *qtVersion)
{
- return defaultBundle(QLatin1String("qt5QtQuick2-bundle.json"));
+ QmlBundle result = defaultBundle(QLatin1String("qt5QtQuick2-bundle.json"), qtVersion);
+ if (!qtVersion || qtVersion->qtVersion().majorVersion() < 6)
+ return result;
+ if (Utils::HostOsInfo::isMacHost())
+ result.merge(defaultBundle(QLatin1String("qt5QtQuick2ext-macos-bundle.json"), qtVersion));
+ if (Utils::HostOsInfo::isWindowsHost())
+ result.merge(defaultBundle(QLatin1String("qt5QtQuick2ext-win-bundle.json"), qtVersion));
+ return result;
}
QmlBundle BasicBundleProvider::defaultQbsBundle()
{
- return defaultBundle(QLatin1String("qbs-bundle.json"));
+ return defaultBundle(QLatin1String("qbs-bundle.json"), nullptr);
}
QmlBundle BasicBundleProvider::defaultQmltypesBundle()
{
- return defaultBundle(QLatin1String("qmltypes-bundle.json"));
+ return defaultBundle(QLatin1String("qmltypes-bundle.json"), nullptr);
}
QmlBundle BasicBundleProvider::defaultQmlprojectBundle()
{
- return defaultBundle(QLatin1String("qmlproject-bundle.json"));
+ return defaultBundle(QLatin1String("qmlproject-bundle.json"), nullptr);
}
void BasicBundleProvider::mergeBundlesForKit(ProjectExplorer::Kit *kit
@@ -77,7 +86,7 @@ void BasicBundleProvider::mergeBundlesForKit(ProjectExplorer::Kit *kit
QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(kit);
if (!qtVersion) {
- QmlBundle b2(defaultQt5QtQuick2Bundle());
+ QmlBundle b2(defaultQt5QtQuick2Bundle(qtVersion));
bundles.mergeBundleForLanguage(Dialect::Qml, b2);
bundles.mergeBundleForLanguage(Dialect::QmlQtQuick2, b2);
bundles.mergeBundleForLanguage(Dialect::QmlQtQuick2Ui, b2);
@@ -90,17 +99,18 @@ void BasicBundleProvider::mergeBundlesForKit(ProjectExplorer::Kit *kit
qtQuick2Bundles.setNameFilters(QStringList(QLatin1String("*-bundle.json")));
QmlBundle qtQuick2Bundle;
QFileInfoList list = qtQuick2Bundles.entryInfoList();
+ bool stripVersions = qtVersion->qtVersion().majorVersion() > 5;
for (int i = 0; i < list.size(); ++i) {
QmlBundle bAtt;
QStringList errors;
- if (!bAtt.readFrom(list.value(i).filePath(), &errors))
+ if (!bAtt.readFrom(list.value(i).filePath(), stripVersions, &errors))
qWarning() << "BasicBundleProvider: ERROR reading " << list[i].filePath() << " : "
<< errors;
qtQuick2Bundle.merge(bAtt);
}
if (!qtQuick2Bundle.supportedImports().contains(QLatin1String("QtQuick 2."),
PersistentTrie::Partial)) {
- qtQuick2Bundle.merge(defaultQt5QtQuick2Bundle());
+ qtQuick2Bundle.merge(defaultQt5QtQuick2Bundle(qtVersion));
}
qtQuick2Bundle.replaceVars(myReplacements);
bundles.mergeBundleForLanguage(Dialect::Qml, qtQuick2Bundle);
diff --git a/src/plugins/qmljstools/qmljsbundleprovider.h b/src/plugins/qmljstools/qmljsbundleprovider.h
index 1f7e6868d0..d8cd835150 100644
--- a/src/plugins/qmljstools/qmljsbundleprovider.h
+++ b/src/plugins/qmljstools/qmljsbundleprovider.h
@@ -19,6 +19,10 @@ class QmlLanguageBundles;
class QmlBundle;
} // namespace QmlJS
+namespace QtSupport {
+class QtVersion;
+}
+
namespace QmlJSTools {
class QMLJSTOOLS_EXPORT IBundleProvider : public QObject
@@ -43,8 +47,9 @@ public:
void mergeBundlesForKit(ProjectExplorer::Kit *kit, QmlJS::QmlLanguageBundles &bundles,
const QHash<QString,QString> &replacements) override;
- static QmlJS::QmlBundle defaultBundle(const QString &bundleInfoName);
- static QmlJS::QmlBundle defaultQt5QtQuick2Bundle();
+ static QmlJS::QmlBundle defaultBundle(const QString &bundleInfoName,
+ QtSupport::QtVersion *qtVersion);
+ static QmlJS::QmlBundle defaultQt5QtQuick2Bundle(QtSupport::QtVersion *qtVersion);
static QmlJS::QmlBundle defaultQbsBundle();
static QmlJS::QmlBundle defaultQmltypesBundle();
static QmlJS::QmlBundle defaultQmlprojectBundle();
diff --git a/src/plugins/qmljstools/qmljscodestylesettingspage.cpp b/src/plugins/qmljstools/qmljscodestylesettingspage.cpp
index cef7df08c3..fc97aa53c8 100644
--- a/src/plugins/qmljstools/qmljscodestylesettingspage.cpp
+++ b/src/plugins/qmljstools/qmljscodestylesettingspage.cpp
@@ -5,7 +5,6 @@
#include "qmljscodestylepreferences.h"
#include "qmljscodestylepreferenceswidget.h"
-#include "qmljsindenter.h"
#include "qmljsqtstylecodeformatter.h"
#include "qmljstoolsconstants.h"
#include "qmljstoolssettings.h"
@@ -25,13 +24,13 @@
#include <utils/layoutbuilder.h>
#include <QTextStream>
+#include <QVBoxLayout>
using namespace TextEditor;
-namespace QmlJSTools {
-namespace Internal {
+namespace QmlJSTools::Internal {
-// ------------------ CppCodeStyleSettingsWidget
+// QmlJSCodeStylePreferencesWidget
QmlJSCodeStylePreferencesWidget::QmlJSCodeStylePreferencesWidget(
const TextEditor::ICodeStylePreferencesFactory *factory, QWidget *parent)
@@ -47,7 +46,7 @@ QmlJSCodeStylePreferencesWidget::QmlJSCodeStylePreferencesWidget(
decorateEditor(TextEditorSettings::fontSettings());
- using namespace Utils::Layouting;
+ using namespace Layouting;
Row {
Column {
m_tabPreferencesWidget,
@@ -55,7 +54,8 @@ QmlJSCodeStylePreferencesWidget::QmlJSCodeStylePreferencesWidget(
st,
},
m_previewTextEdit,
- }.attachTo(this, WithoutMargins);
+ noMargin
+ }.attachTo(this);
connect(TextEditorSettings::instance(), &TextEditorSettings::fontSettingsChanged,
this, &QmlJSCodeStylePreferencesWidget::decorateEditor);
@@ -120,59 +120,60 @@ void QmlJSCodeStylePreferencesWidget::updatePreview()
tc.endEditBlock();
}
-// ------------------ CppCodeStyleSettingsPage
+// QmlJSCodeStyleSettingsPageWidget
-QmlJSCodeStyleSettingsPage::QmlJSCodeStyleSettingsPage()
+class QmlJSCodeStyleSettingsPageWidget : public Core::IOptionsPageWidget
{
- setId(Constants::QML_JS_CODE_STYLE_SETTINGS_ID);
- setDisplayName(Tr::tr(Constants::QML_JS_CODE_STYLE_SETTINGS_NAME));
- setCategory(QmlJSEditor::Constants::SETTINGS_CATEGORY_QML);
- setDisplayCategory(Tr::tr("Qt Quick"));
- setCategoryIconPath(":/qmljstools/images/settingscategory_qml.png");
-}
+public:
+ QmlJSCodeStyleSettingsPageWidget()
+ {
-QWidget *QmlJSCodeStyleSettingsPage::widget()
-{
- if (!m_widget) {
QmlJSCodeStylePreferences *originalPreferences
= QmlJSToolsSettings::globalCodeStyle();
- m_preferences = new QmlJSCodeStylePreferences(m_widget);
- m_preferences->setDelegatingPool(originalPreferences->delegatingPool());
- m_preferences->setCodeStyleSettings(originalPreferences->codeStyleSettings());
- m_preferences->setTabSettings(originalPreferences->tabSettings());
- m_preferences->setCurrentDelegate(originalPreferences->currentDelegate());
- m_preferences->setId(originalPreferences->id());
- m_widget = new CodeStyleEditor(TextEditorSettings::codeStyleFactory(QmlJSTools::Constants::QML_JS_SETTINGS_ID),
- m_preferences);
+ m_preferences.setDelegatingPool(originalPreferences->delegatingPool());
+ m_preferences.setCodeStyleSettings(originalPreferences->codeStyleSettings());
+ m_preferences.setTabSettings(originalPreferences->tabSettings());
+ m_preferences.setCurrentDelegate(originalPreferences->currentDelegate());
+ m_preferences.setId(originalPreferences->id());
+
+ auto vbox = new QVBoxLayout(this);
+ vbox->addWidget(new CodeStyleEditor(
+ TextEditorSettings::codeStyleFactory(QmlJSTools::Constants::QML_JS_SETTINGS_ID),
+ &m_preferences));
}
- return m_widget;
-}
-void QmlJSCodeStyleSettingsPage::apply()
-{
- if (m_widget) {
+ void apply() final
+ {
QSettings *s = Core::ICore::settings();
QmlJSCodeStylePreferences *originalPreferences = QmlJSToolsSettings::globalCodeStyle();
- if (originalPreferences->codeStyleSettings() != m_preferences->codeStyleSettings()) {
- originalPreferences->setCodeStyleSettings(m_preferences->codeStyleSettings());
+ if (originalPreferences->codeStyleSettings() != m_preferences.codeStyleSettings()) {
+ originalPreferences->setCodeStyleSettings(m_preferences.codeStyleSettings());
originalPreferences->toSettings(QLatin1String(QmlJSTools::Constants::QML_JS_SETTINGS_ID), s);
}
- if (originalPreferences->tabSettings() != m_preferences->tabSettings()) {
- originalPreferences->setTabSettings(m_preferences->tabSettings());
+ if (originalPreferences->tabSettings() != m_preferences.tabSettings()) {
+ originalPreferences->setTabSettings(m_preferences.tabSettings());
originalPreferences->toSettings(QLatin1String(QmlJSTools::Constants::QML_JS_SETTINGS_ID), s);
}
- if (originalPreferences->currentDelegate() != m_preferences->currentDelegate()) {
- originalPreferences->setCurrentDelegate(m_preferences->currentDelegate());
+ if (originalPreferences->currentDelegate() != m_preferences.currentDelegate()) {
+ originalPreferences->setCurrentDelegate(m_preferences.currentDelegate());
originalPreferences->toSettings(QLatin1String(QmlJSTools::Constants::QML_JS_SETTINGS_ID), s);
}
}
-}
-void QmlJSCodeStyleSettingsPage::finish()
+ QmlJSCodeStylePreferences m_preferences;
+};
+
+// QmlJSCodeStyleSettingsPage
+
+QmlJSCodeStyleSettingsPage::QmlJSCodeStyleSettingsPage()
{
- delete m_widget;
+ setId(Constants::QML_JS_CODE_STYLE_SETTINGS_ID);
+ setDisplayName(Tr::tr(Constants::QML_JS_CODE_STYLE_SETTINGS_NAME));
+ setCategory(QmlJSEditor::Constants::SETTINGS_CATEGORY_QML);
+ setDisplayCategory(Tr::tr("Qt Quick"));
+ setCategoryIconPath(":/qmljstools/images/settingscategory_qml.png");
+ setWidgetCreator([] { return new QmlJSCodeStyleSettingsPageWidget; });
}
-} // namespace Internal
-} // namespace QmlJSTools
+} // QmlJSTools::Internal
diff --git a/src/plugins/qmljstools/qmljscodestylesettingspage.h b/src/plugins/qmljstools/qmljscodestylesettingspage.h
index e9b244865b..853d482b95 100644
--- a/src/plugins/qmljstools/qmljscodestylesettingspage.h
+++ b/src/plugins/qmljstools/qmljscodestylesettingspage.h
@@ -6,16 +6,8 @@
#include <coreplugin/dialogs/ioptionspage.h>
#include <texteditor/icodestylepreferencesfactory.h>
-#include <QWidget>
-#include <QPointer>
-
-QT_BEGIN_NAMESPACE
-class QSettings;
-QT_END_NAMESPACE
-
namespace TextEditor {
class FontSettings;
- class CodeStyleEditor;
class SimpleCodeStylePreferencesWidget;
class SnippetEditorWidget;
}
@@ -54,14 +46,6 @@ class QmlJSCodeStyleSettingsPage : public Core::IOptionsPage
{
public:
QmlJSCodeStyleSettingsPage();
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-private:
- QmlJSCodeStylePreferences *m_preferences = nullptr;
- QPointer<TextEditor::CodeStyleEditor> m_widget;
};
} // namespace Internal
diff --git a/src/plugins/qmljstools/qmljscodestylesettingswidget.cpp b/src/plugins/qmljstools/qmljscodestylesettingswidget.cpp
index 455ffcd0f8..9fee7205b3 100644
--- a/src/plugins/qmljstools/qmljscodestylesettingswidget.cpp
+++ b/src/plugins/qmljstools/qmljscodestylesettingswidget.cpp
@@ -19,15 +19,18 @@ QmlJSCodeStyleSettingsWidget::QmlJSCodeStyleSettingsWidget(QWidget *parent)
m_lineLengthSpinBox->setMinimum(0);
m_lineLengthSpinBox->setMaximum(999);
- using namespace Utils::Layouting;
+ using namespace Layouting;
+ // clang-format off
Column {
Group {
- title(Tr::tr("Qml JS Code Style")),
+ title(Tr::tr("Other")),
Form {
Tr::tr("&Line length:"), m_lineLengthSpinBox, br,
}
- }
- }.attachTo(this, WithoutMargins);
+ },
+ noMargin
+ }.attachTo(this);
+ // clang-format on
connect(m_lineLengthSpinBox, &QSpinBox::valueChanged,
this, &QmlJSCodeStyleSettingsWidget::slotSettingsChanged);
diff --git a/src/plugins/qmljstools/qmljsfunctionfilter.cpp b/src/plugins/qmljstools/qmljsfunctionfilter.cpp
index 4d4ce05fb7..34a60040f3 100644
--- a/src/plugins/qmljstools/qmljsfunctionfilter.cpp
+++ b/src/plugins/qmljstools/qmljsfunctionfilter.cpp
@@ -5,80 +5,85 @@
#include "qmljslocatordata.h"
#include "qmljstoolstr.h"
-#include <coreplugin/editormanager/editormanager.h>
+#include <extensionsystem/pluginmanager.h>
+
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <QRegularExpression>
-#include <numeric>
-
+using namespace Core;
using namespace QmlJSTools::Internal;
+using namespace Utils;
Q_DECLARE_METATYPE(LocatorData::Entry)
-FunctionFilter::FunctionFilter(LocatorData *data, QObject *parent)
- : Core::ILocatorFilter(parent)
- , m_data(data)
+QmlJSFunctionsFilter::QmlJSFunctionsFilter(LocatorData *data)
+ : m_data(data)
{
setId("Functions");
setDisplayName(Tr::tr("QML Functions"));
+ setDescription(Tr::tr("Locates QML functions in any open project."));
setDefaultShortcutString("m");
- setDefaultIncludedByDefault(false);
}
-FunctionFilter::~FunctionFilter() = default;
-
-QList<Core::LocatorFilterEntry> FunctionFilter::matchesFor(
- QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry)
+static void matches(QPromise<void> &promise, const LocatorStorage &storage,
+ const QHash<FilePath, QList<LocatorData::Entry>> &locatorEntries)
{
- QList<Core::LocatorFilterEntry> entries[int(MatchLevel::Count)];
- const Qt::CaseSensitivity caseSensitivityForPrefix = caseSensitivity(entry);
-
- const QRegularExpression regexp = createRegExp(entry);
+ const QString input = storage.input();
+ const Qt::CaseSensitivity caseSensitivityForPrefix = ILocatorFilter::caseSensitivity(input);
+ const QRegularExpression regexp = ILocatorFilter::createRegExp(input);
if (!regexp.isValid())
- return {};
+ return;
- const QHash<Utils::FilePath, QList<LocatorData::Entry>> locatorEntries = m_data->entries();
+ LocatorFilterEntries entries[int(ILocatorFilter::MatchLevel::Count)];
for (const QList<LocatorData::Entry> &items : locatorEntries) {
- if (future.isCanceled())
- break;
-
for (const LocatorData::Entry &info : items) {
+ if (promise.isCanceled())
+ return;
+
if (info.type != LocatorData::Function)
continue;
const QRegularExpressionMatch match = regexp.match(info.symbolName);
if (match.hasMatch()) {
- QVariant id = QVariant::fromValue(info);
- Core::LocatorFilterEntry filterEntry(this, info.displayName, id/*, info.icon*/);
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = info.displayName;
+ filterEntry.linkForEditor = {info.fileName, info.line, info.column};
filterEntry.extraInfo = info.extraInfo;
- filterEntry.highlightInfo = highlightInfo(match);
+ filterEntry.highlightInfo = ILocatorFilter::highlightInfo(match);
- if (filterEntry.displayName.startsWith(entry, caseSensitivityForPrefix))
- entries[int(MatchLevel::Best)].append(filterEntry);
- else if (filterEntry.displayName.contains(entry, caseSensitivityForPrefix))
- entries[int(MatchLevel::Better)].append(filterEntry);
+ if (filterEntry.displayName.startsWith(input, caseSensitivityForPrefix))
+ entries[int(ILocatorFilter::MatchLevel::Best)].append(filterEntry);
+ else if (filterEntry.displayName.contains(input, caseSensitivityForPrefix))
+ entries[int(ILocatorFilter::MatchLevel::Better)].append(filterEntry);
else
- entries[int(MatchLevel::Good)].append(filterEntry);
+ entries[int(ILocatorFilter::MatchLevel::Good)].append(filterEntry);
}
}
}
for (auto &entry : entries) {
+ if (promise.isCanceled())
+ return;
+
if (entry.size() < 1000)
- Utils::sort(entry, Core::LocatorFilterEntry::compareLexigraphically);
+ Utils::sort(entry, LocatorFilterEntry::compareLexigraphically);
}
-
- return std::accumulate(std::begin(entries), std::end(entries), QList<Core::LocatorFilterEntry>());
+ storage.reportOutput(std::accumulate(std::begin(entries), std::end(entries),
+ LocatorFilterEntries()));
}
-void FunctionFilter::accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const
+LocatorMatcherTasks QmlJSFunctionsFilter::matchers()
{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- const LocatorData::Entry entry = qvariant_cast<LocatorData::Entry>(selection.internalData);
- Core::EditorManager::openEditorAt({entry.fileName, entry.line, entry.column});
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [storage, entries = m_data->entries()](Async<void> &async) {
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(matches, *storage, entries);
+ };
+
+ return {{AsyncTask<void>(onSetup), storage}};
}
diff --git a/src/plugins/qmljstools/qmljsfunctionfilter.h b/src/plugins/qmljstools/qmljsfunctionfilter.h
index 83b93e8f36..3775259fb2 100644
--- a/src/plugins/qmljstools/qmljsfunctionfilter.h
+++ b/src/plugins/qmljstools/qmljsfunctionfilter.h
@@ -5,27 +5,19 @@
#include <coreplugin/locator/ilocatorfilter.h>
-namespace QmlJSTools {
-namespace Internal {
+namespace QmlJSTools::Internal {
class LocatorData;
-class FunctionFilter : public Core::ILocatorFilter
+class QmlJSFunctionsFilter : public Core::ILocatorFilter
{
- Q_OBJECT
-
public:
- explicit FunctionFilter(LocatorData *data, QObject *parent = nullptr);
- ~FunctionFilter() override;
-
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
+ QmlJSFunctionsFilter(LocatorData *data);
private:
+ Core::LocatorMatcherTasks matchers() final;
+
LocatorData *m_data = nullptr;
};
-} // namespace Internal
-} // namespace QmlJSTools
+} // namespace QmlJSTools::Internal
diff --git a/src/plugins/qmljstools/qmljslocatordata.cpp b/src/plugins/qmljstools/qmljslocatordata.cpp
index 679a230e8e..477447c9e3 100644
--- a/src/plugins/qmljstools/qmljslocatordata.cpp
+++ b/src/plugins/qmljstools/qmljslocatordata.cpp
@@ -4,7 +4,7 @@
#include "qmljslocatordata.h"
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <qmljs/qmljsutils.h>
@@ -40,10 +40,10 @@ LocatorData::LocatorData()
connect(manager, &ModelManagerInterface::aboutToRemoveFiles,
this, &LocatorData::onAboutToRemoveFiles);
- ProjectExplorer::SessionManager *session = ProjectExplorer::SessionManager::instance();
+ ProjectExplorer::ProjectManager *session = ProjectExplorer::ProjectManager::instance();
if (session)
connect(session,
- &ProjectExplorer::SessionManager::projectRemoved,
+ &ProjectExplorer::ProjectManager::projectRemoved,
this,
[this](ProjectExplorer::Project *) { m_entries.clear(); });
}
diff --git a/src/plugins/qmljstools/qmljsmodelmanager.cpp b/src/plugins/qmljstools/qmljsmodelmanager.cpp
index da1d82b73d..914a4f7794 100644
--- a/src/plugins/qmljstools/qmljsmodelmanager.cpp
+++ b/src/plugins/qmljstools/qmljsmodelmanager.cpp
@@ -18,10 +18,11 @@
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
#include <projectexplorer/runconfiguration.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qmljs/qmljsbind.h>
@@ -273,9 +274,9 @@ void ModelManager::delayedInitialization()
connect(cppModelManager, &CppEditor::CppModelManager::documentUpdated,
this, &ModelManagerInterface::maybeQueueCppQmlTypeUpdate, Qt::DirectConnection);
- connect(SessionManager::instance(), &SessionManager::projectRemoved,
+ connect(ProjectManager::instance(), &ProjectManager::projectRemoved,
this, &ModelManager::removeProjectInfo);
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, &ModelManager::updateDefaultProjectInfo);
ViewerContext qbsVContext;
@@ -323,7 +324,7 @@ ModelManagerInterface::WorkingCopy ModelManager::workingCopyInternal() const
void ModelManager::updateDefaultProjectInfo()
{
// needs to be performed in the ui thread
- Project *currentProject = SessionManager::startupProject();
+ Project *currentProject = ProjectManager::startupProject();
setDefaultProject(containsProject(currentProject)
? projectInfo(currentProject)
: defaultProjectInfoForProject(currentProject, {}),
diff --git a/src/plugins/qmljstools/qmljsrefactoringchanges.h b/src/plugins/qmljstools/qmljsrefactoringchanges.h
index d16564832b..33545e2bfc 100644
--- a/src/plugins/qmljstools/qmljsrefactoringchanges.h
+++ b/src/plugins/qmljstools/qmljsrefactoringchanges.h
@@ -24,7 +24,7 @@ public:
QmlJS::Document::Ptr qmljsDocument() const;
/*!
- \returns the offset in the document for the start position of the given
+ Returns the offset in the document for the start position of the given
source location.
*/
unsigned startOf(const QmlJS::SourceLocation &loc) const;
diff --git a/src/plugins/qmljstools/qmljstools.qbs b/src/plugins/qmljstools/qmljstools.qbs
index 68c9a9dfc1..d13342e8dd 100644
--- a/src/plugins/qmljstools/qmljstools.qbs
+++ b/src/plugins/qmljstools/qmljstools.qbs
@@ -54,9 +54,7 @@ QtcPlugin {
"qmljstools.qrc"
]
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: ["qmljstools_test.cpp"]
}
diff --git a/src/plugins/qmljstools/qmljstoolsplugin.cpp b/src/plugins/qmljstools/qmljstoolsplugin.cpp
index 574c73190b..88c72b29e6 100644
--- a/src/plugins/qmljstools/qmljstoolsplugin.cpp
+++ b/src/plugins/qmljstools/qmljstoolsplugin.cpp
@@ -38,7 +38,7 @@ public:
QAction resetCodeModelAction{Tr::tr("Reset Code Model"), nullptr};
LocatorData locatorData;
- FunctionFilter functionFilter{&locatorData};
+ QmlJSFunctionsFilter functionsFilter{&locatorData};
QmlJSCodeStyleSettingsPage codeStyleSettingsPage;
BasicBundleProvider basicBundleProvider;
};
diff --git a/src/plugins/qmljstools/qmljstoolssettings.cpp b/src/plugins/qmljstools/qmljstoolssettings.cpp
index 093fb676c2..caeb6dfa26 100644
--- a/src/plugins/qmljstools/qmljstoolssettings.cpp
+++ b/src/plugins/qmljstools/qmljstoolssettings.cpp
@@ -71,45 +71,6 @@ QmlJSToolsSettings::QmlJSToolsSettings()
QSettings *s = Core::ICore::settings();
m_globalCodeStyle->fromSettings(QLatin1String(QmlJSTools::Constants::QML_JS_SETTINGS_ID), s);
- // legacy handling start (Qt Creator Version < 2.4)
- const bool legacyTransformed =
- s->value(QLatin1String("QmlJSTabPreferences/LegacyTransformed"), false).toBool();
-
- if (!legacyTransformed) {
- // creator 2.4 didn't mark yet the transformation (first run of creator 2.4)
-
- // we need to transform the settings only if at least one from
- // below settings was already written - otherwise we use
- // defaults like it would be the first run of creator 2.4 without stored settings
- const QStringList groups = s->childGroups();
- const bool needTransform = groups.contains(QLatin1String("textTabPreferences")) ||
- groups.contains(QLatin1String("QmlJSTabPreferences"));
-
- if (needTransform) {
- const QString currentFallback = s->value(QLatin1String("QmlJSTabPreferences/CurrentFallback")).toString();
- TabSettings legacyTabSettings;
- if (currentFallback == QLatin1String("QmlJSGlobal")) {
- // no delegate, global overwritten
- Utils::fromSettings(QLatin1String("QmlJSTabPreferences"),
- QString(), s, &legacyTabSettings);
- } else {
- // delegating to global
- legacyTabSettings = TextEditorSettings::codeStyle()->currentTabSettings();
- }
-
- // create custom code style out of old settings
- ICodeStylePreferences *oldCreator = pool->createCodeStyle(
- "legacy", legacyTabSettings, QVariant(), Tr::tr("Old Creator"));
-
- // change the current delegate and save
- m_globalCodeStyle->setCurrentDelegate(oldCreator);
- m_globalCodeStyle->toSettings(QLatin1String(QmlJSTools::Constants::QML_JS_SETTINGS_ID), s);
- }
- // mark old settings as transformed
- s->setValue(QLatin1String("QmlJSTabPreferences/LegacyTransformed"), true);
- // legacy handling stop
- }
-
// mimetypes to be handled
TextEditorSettings::registerMimeTypeForLanguageId(Constants::QML_MIMETYPE, Constants::QML_JS_SETTINGS_ID);
TextEditorSettings::registerMimeTypeForLanguageId(Constants::QMLUI_MIMETYPE, Constants::QML_JS_SETTINGS_ID);
diff --git a/src/plugins/qmlpreview/qmlpreview.qbs b/src/plugins/qmlpreview/qmlpreview.qbs
index 7b0f96dde7..e44f432c41 100644
--- a/src/plugins/qmlpreview/qmlpreview.qbs
+++ b/src/plugins/qmlpreview/qmlpreview.qbs
@@ -38,9 +38,7 @@ QtcPlugin {
]
}
- Group {
- name: "Unit tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
prefix: "tests/"
files: [
"qmlpreviewclient_test.cpp",
diff --git a/src/plugins/qmlpreview/qmlpreviewplugin.cpp b/src/plugins/qmlpreview/qmlpreviewplugin.cpp
index 6175eac2a2..8803475d8a 100644
--- a/src/plugins/qmlpreview/qmlpreviewplugin.cpp
+++ b/src/plugins/qmlpreview/qmlpreviewplugin.cpp
@@ -25,10 +25,10 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
#include <projectexplorer/runconfiguration.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <qmljs/qmljsdocument.h>
@@ -150,15 +150,15 @@ QmlPreviewPluginPrivate::QmlPreviewPluginPrivate(QmlPreviewPlugin *parent)
Constants::M_BUILDPROJECT);
QAction *action = new QAction(Tr::tr("QML Preview"), this);
action->setToolTip(Tr::tr("Preview changes to QML code live in your application."));
- action->setEnabled(SessionManager::startupProject() != nullptr);
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged, action,
+ action->setEnabled(ProjectManager::startupProject() != nullptr);
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged, action,
&QAction::setEnabled);
connect(action, &QAction::triggered, this, [this]() {
if (auto multiLanguageAspect = QmlProjectManager::QmlMultiLanguageAspect::current())
m_localeIsoCode = multiLanguageAspect->currentLocale();
bool skipDeploy = false;
- const Kit *kit = SessionManager::startupTarget()->kit();
- if (SessionManager::startupTarget() && kit)
+ const Kit *kit = ProjectManager::startupTarget()->kit();
+ if (ProjectManager::startupTarget() && kit)
skipDeploy = kit->supportedPlatforms().contains(Android::Constants::ANDROID_DEVICE_TYPE)
|| DeviceTypeKitAspect::deviceTypeId(kit) == Android::Constants::ANDROID_DEVICE_TYPE;
ProjectExplorerPlugin::runStartupProject(Constants::QML_PREVIEW_RUN_MODE, skipDeploy);
diff --git a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp
index 57088ad19b..ca43b07a42 100644
--- a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp
+++ b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp
@@ -9,7 +9,7 @@
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qmldebug/qmldebugcommandlinearguments.h>
@@ -18,7 +18,7 @@
#include <utils/filepath.h>
#include <utils/port.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/url.h>
using namespace ProjectExplorer;
@@ -88,11 +88,14 @@ QmlPreviewRunner::QmlPreviewRunner(RunControl *runControl, const QmlPreviewRunne
if (!runControl->isRunning())
return;
- this->connect(runControl, &RunControl::stopped, [this, runControl] {
- auto rc = new RunControl(ProjectExplorer::Constants::QML_PREVIEW_RUN_MODE);
- rc->copyDataFromRunControl(runControl);
- ProjectExplorerPlugin::startRunControl(rc);
- });
+ this->connect(runControl,
+ &RunControl::stopped,
+ ProjectExplorerPlugin::instance(),
+ [runControl] {
+ auto rc = new RunControl(ProjectExplorer::Constants::QML_PREVIEW_RUN_MODE);
+ rc->copyDataFromRunControl(runControl);
+ ProjectExplorerPlugin::startRunControl(rc);
+ });
runControl->initiateStop();
});
@@ -124,7 +127,7 @@ QUrl QmlPreviewRunner::serverUrl() const
QmlPreviewRunWorkerFactory::QmlPreviewRunWorkerFactory(QmlPreviewPlugin *plugin,
const QmlPreviewRunnerSetting *runnerSettings)
{
- setProducer([this, plugin, runnerSettings](RunControl *runControl) {
+ setProducer([plugin, runnerSettings](RunControl *runControl) {
auto runner = new QmlPreviewRunner(runControl, *runnerSettings);
QObject::connect(plugin, &QmlPreviewPlugin::updatePreviews,
runner, &QmlPreviewRunner::loadFile);
diff --git a/src/plugins/qmlprofiler/qmlprofiler.qbs b/src/plugins/qmlprofiler/qmlprofiler.qbs
index 5cc3604a07..56a1e00cd6 100644
--- a/src/plugins/qmlprofiler/qmlprofiler.qbs
+++ b/src/plugins/qmlprofiler/qmlprofiler.qbs
@@ -73,9 +73,7 @@ QtcPlugin {
files: "qml/**"
}
- Group {
- name: "Unit tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
prefix: "tests/"
files: [
"debugmessagesmodel_test.cpp", "debugmessagesmodel_test.h",
diff --git a/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp b/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp
index 396c830568..e9f0b1a780 100644
--- a/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp
@@ -6,7 +6,7 @@
#include <projectexplorer/kit.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/runconfiguration.h>
#include <qmljs/parser/qmljsast_p.h>
diff --git a/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp b/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp
index f5c45789d8..41b67edda7 100644
--- a/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp
@@ -20,8 +20,8 @@
#include <qmldebug/qmldebugcommandlinearguments.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/url.h>
#include <QMessageBox>
diff --git a/src/plugins/qmlprofiler/qmlprofilersettings.cpp b/src/plugins/qmlprofiler/qmlprofilersettings.cpp
index 481a88ab32..1002479baa 100644
--- a/src/plugins/qmlprofiler/qmlprofilersettings.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilersettings.cpp
@@ -17,24 +17,32 @@
using namespace Utils;
-namespace QmlProfiler {
-namespace Internal {
+namespace QmlProfiler::Internal {
-static QWidget *createQmlConfigWidget(QmlProfilerSettings *settings)
+class QmlProfilerOptionsPageWidget : public Core::IOptionsPageWidget
{
- QmlProfilerSettings &s = *settings;
- using namespace Layouting;
-
- return Form {
- s.flushEnabled,
- s.flushInterval,
- s.aggregateTraces
- }.emerge();
-}
+public:
+ explicit QmlProfilerOptionsPageWidget(QmlProfilerSettings *settings)
+ {
+ QmlProfilerSettings &s = *settings;
+
+ using namespace Layouting;
+ Form {
+ s.flushEnabled, br,
+ s.flushInterval, br,
+ s.aggregateTraces, br,
+ }.attachTo(this);
+ }
+
+ void apply() final
+ {
+ QmlProfilerPlugin::globalSettings()->writeGlobalSettings();
+ }
+};
QmlProfilerSettings::QmlProfilerSettings()
{
- setConfigWidgetCreator([this] { return createQmlConfigWidget(this); });
+ setConfigWidgetCreator([this] { return new QmlProfilerOptionsPageWidget(this); });
setSettingsGroup(Constants::ANALYZER);
@@ -85,25 +93,9 @@ QmlProfilerOptionsPage::QmlProfilerOptionsPage()
setCategory("T.Analyzer");
setDisplayCategory(::Debugger::Tr::tr("Analyzer"));
setCategoryIconPath(Analyzer::Icons::SETTINGSCATEGORY_ANALYZER);
+ setWidgetCreator([] {
+ return new QmlProfilerOptionsPageWidget(QmlProfilerPlugin::globalSettings());
+ });
}
-QWidget *QmlProfilerOptionsPage::widget()
-{
- // We cannot parent the widget to the options page as it expects a QWidget as parent
- if (!m_widget)
- m_widget = createQmlConfigWidget(QmlProfilerPlugin::globalSettings());
- return m_widget;
-}
-
-void QmlProfilerOptionsPage::apply()
-{
- QmlProfilerPlugin::globalSettings()->writeGlobalSettings();
-}
-
-void QmlProfilerOptionsPage::finish()
-{
- delete m_widget;
-}
-
-} // Internal
-} // QmlProfiler
+} // QmlProfiler::Internal
diff --git a/src/plugins/qmlprofiler/qmlprofilersettings.h b/src/plugins/qmlprofiler/qmlprofilersettings.h
index 3b4481cac8..14e3567621 100644
--- a/src/plugins/qmlprofiler/qmlprofilersettings.h
+++ b/src/plugins/qmlprofiler/qmlprofilersettings.h
@@ -7,15 +7,10 @@
#include <projectexplorer/runconfiguration.h>
-#include <QPointer>
-
-namespace QmlProfiler {
-namespace Internal {
+namespace QmlProfiler::Internal {
class QmlProfilerSettings : public ProjectExplorer::ISettingsAspect
{
- Q_OBJECT
-
public:
QmlProfilerSettings();
@@ -31,14 +26,6 @@ class QmlProfilerOptionsPage final : public Core::IOptionsPage
{
public:
QmlProfilerOptionsPage();
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-private:
- QPointer<QWidget> m_widget;
};
-} // Internal
-} // QmlProfiler
+} // QmlProfiler::Internal
diff --git a/src/plugins/qmlprofiler/qmlprofilertool.cpp b/src/plugins/qmlprofiler/qmlprofilertool.cpp
index 579a870a4b..f003d5f0e4 100644
--- a/src/plugins/qmlprofiler/qmlprofilertool.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilertool.cpp
@@ -39,8 +39,8 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
-#include <projectexplorer/session.h>
#include <qtsupport/qtkitinformation.h>
@@ -49,6 +49,7 @@
#include <utils/fancymainwindow.h>
#include <utils/fileinprojectfinder.h>
#include <utils/qtcassert.h>
+#include <utils/stylehelper.h>
#include <utils/url.h>
#include <utils/utilsicons.h>
@@ -190,14 +191,14 @@ QmlProfilerTool::QmlProfilerTool()
d->m_displayFeaturesButton->setIcon(Utils::Icons::FILTER.icon());
d->m_displayFeaturesButton->setToolTip(Tr::tr("Hide or show event categories."));
d->m_displayFeaturesButton->setPopupMode(QToolButton::InstantPopup);
- d->m_displayFeaturesButton->setProperty("noArrow", true);
+ d->m_displayFeaturesButton->setProperty(StyleHelper::C_NO_ARROW, true);
d->m_displayFeaturesMenu = new QMenu(d->m_displayFeaturesButton);
d->m_displayFeaturesButton->setMenu(d->m_displayFeaturesMenu);
connect(d->m_displayFeaturesMenu, &QMenu::triggered,
this, &QmlProfilerTool::toggleVisibleFeature);
d->m_timeLabel = new QLabel();
- d->m_timeLabel->setProperty("panelwidget", true);
+ StyleHelper::setPanelWidget(d->m_timeLabel);
d->m_timeLabel->setIndent(10);
updateTimeDisplay();
connect(d->m_timeLabel, &QObject::destroyed, &d->m_recordingTimer, &QTimer::stop);
@@ -540,7 +541,7 @@ ProjectExplorer::RunControl *QmlProfilerTool::attachToWaitingApplication()
d->m_viewContainer->perspective()->select();
auto runControl = new RunControl(ProjectExplorer::Constants::QML_PROFILER_RUN_MODE);
- runControl->copyDataFromRunConfiguration(SessionManager::startupRunConfiguration());
+ runControl->copyDataFromRunConfiguration(ProjectManager::startupRunConfiguration());
auto profiler = new QmlProfilerRunner(runControl);
profiler->setServerUrl(serverUrl);
diff --git a/src/plugins/qmlprofiler/tests/qmlprofilerclientmanager_test.cpp b/src/plugins/qmlprofiler/tests/qmlprofilerclientmanager_test.cpp
index b14b4962e7..366a6028bb 100644
--- a/src/plugins/qmlprofiler/tests/qmlprofilerclientmanager_test.cpp
+++ b/src/plugins/qmlprofiler/tests/qmlprofilerclientmanager_test.cpp
@@ -105,9 +105,9 @@ void QmlProfilerClientManagerTest::testConnectionFailure()
QFETCH(QmlProfilerStateManager *, stateManager);
QFETCH(QUrl, serverUrl);
- QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
- QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
- QSignalSpy failedSpy(&clientManager, SIGNAL(connectionFailed()));
+ QSignalSpy openedSpy(&clientManager, &QmlProfilerClientManager::connectionOpened);
+ QSignalSpy closedSpy(&clientManager, &QmlProfilerClientManager::connectionClosed);
+ QSignalSpy failedSpy(&clientManager, &QmlProfilerClientManager::connectionFailed);
QVERIFY(!clientManager.isConnected());
@@ -137,9 +137,9 @@ void QmlProfilerClientManagerTest::testConnectionFailure()
void QmlProfilerClientManagerTest::testUnresponsiveTcp()
{
- QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
- QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
- QSignalSpy failedSpy(&clientManager, SIGNAL(connectionFailed()));
+ QSignalSpy openedSpy(&clientManager, &QmlProfilerClientManager::connectionOpened);
+ QSignalSpy closedSpy(&clientManager, &QmlProfilerClientManager::connectionClosed);
+ QSignalSpy failedSpy(&clientManager, &QmlProfilerClientManager::connectionFailed);
QVERIFY(!clientManager.isConnected());
@@ -150,7 +150,7 @@ void QmlProfilerClientManagerTest::testUnresponsiveTcp()
QTcpServer server;
server.listen(QHostAddress(serverUrl.host()), serverUrl.port());
- QSignalSpy connectionSpy(&server, SIGNAL(newConnection()));
+ QSignalSpy connectionSpy(&server, &QTcpServer::newConnection);
clientManager.connectToServer(serverUrl);
@@ -165,9 +165,9 @@ void QmlProfilerClientManagerTest::testUnresponsiveTcp()
void QmlProfilerClientManagerTest::testUnresponsiveLocal()
{
- QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
- QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
- QSignalSpy failedSpy(&clientManager, SIGNAL(connectionFailed()));
+ QSignalSpy openedSpy(&clientManager, &QmlProfilerClientManager::connectionOpened);
+ QSignalSpy closedSpy(&clientManager, &QmlProfilerClientManager::connectionClosed);
+ QSignalSpy failedSpy(&clientManager, &QmlProfilerClientManager::connectionFailed);
QVERIFY(!clientManager.isConnected());
@@ -176,7 +176,7 @@ void QmlProfilerClientManagerTest::testUnresponsiveLocal()
QUrl socketUrl = Utils::urlFromLocalSocket();
QLocalSocket socket;
- QSignalSpy connectionSpy(&socket, SIGNAL(connected()));
+ QSignalSpy connectionSpy(&socket, &QLocalSocket::connected);
clientManager.connectToServer(socketUrl);
@@ -209,8 +209,8 @@ void QmlProfilerClientManagerTest::testResponsiveTcp()
QUrl serverUrl = Utils::urlFromLocalHostAndFreePort();
- QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
- QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
+ QSignalSpy openedSpy(&clientManager, &QmlProfilerClientManager::connectionOpened);
+ QSignalSpy closedSpy(&clientManager, &QmlProfilerClientManager::connectionClosed);
QVERIFY(!clientManager.isConnected());
@@ -267,8 +267,8 @@ void QmlProfilerClientManagerTest::testResponsiveLocal()
QUrl socketUrl = Utils::urlFromLocalSocket();
- QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
- QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
+ QSignalSpy openedSpy(&clientManager, &QmlProfilerClientManager::connectionOpened);
+ QSignalSpy closedSpy(&clientManager, &QmlProfilerClientManager::connectionClosed);
QVERIFY(!clientManager.isConnected());
@@ -320,9 +320,9 @@ void QmlProfilerClientManagerTest::testInvalidData()
MessageHandler handler(&invalidHelloMessageHandler);
Q_UNUSED(handler)
- QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
- QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
- QSignalSpy failedSpy(&clientManager, SIGNAL(connectionFailed()));
+ QSignalSpy openedSpy(&clientManager, &QmlProfilerClientManager::connectionOpened);
+ QSignalSpy closedSpy(&clientManager, &QmlProfilerClientManager::connectionClosed);
+ QSignalSpy failedSpy(&clientManager, &QmlProfilerClientManager::connectionFailed);
QVERIFY(!clientManager.isConnected());
@@ -365,8 +365,8 @@ void QmlProfilerClientManagerTest::testStopRecording()
QmlProfilerClientManager clientManager;
clientManager.setRetryInterval(10);
clientManager.setMaximumRetries(10);
- QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
- QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
+ QSignalSpy openedSpy(&clientManager, &QmlProfilerClientManager::connectionOpened);
+ QSignalSpy closedSpy(&clientManager, &QmlProfilerClientManager::connectionClosed);
QVERIFY(!clientManager.isConnected());
diff --git a/src/plugins/qmlprofiler/tests/qmlprofilerdetailsrewriter_test.cpp b/src/plugins/qmlprofiler/tests/qmlprofilerdetailsrewriter_test.cpp
index e01b6ab5ef..d71836319c 100644
--- a/src/plugins/qmlprofiler/tests/qmlprofilerdetailsrewriter_test.cpp
+++ b/src/plugins/qmlprofiler/tests/qmlprofilerdetailsrewriter_test.cpp
@@ -3,15 +3,16 @@
#include "qmlprofilerdetailsrewriter_test.h"
+#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildinfo.h>
#include <projectexplorer/customexecutablerunconfiguration.h>
-#include <projectexplorer/project.h>
-#include <projectexplorer/target.h>
+#include <projectexplorer/kitinformation.h>
#include <projectexplorer/kitmanager.h>
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
-#include <projectexplorer/session.h>
-#include <projectexplorer/kitinformation.h>
-#include <projectexplorer/buildconfiguration.h>
+#include <projectexplorer/target.h>
+
#include <utils/filepath.h>
#include <QLibraryInfo>
@@ -157,15 +158,15 @@ void QmlProfilerDetailsRewriterTest::testPopulateFileFinder()
// Test that the rewriter will populate from available projects if given nullptr as parameter.
DummyProject *project1 = new DummyProject(":/nix.nix");
- ProjectExplorer::SessionManager::addProject(project1);
+ ProjectExplorer::ProjectManager::addProject(project1);
DummyProject *project2 = new DummyProject(":/qmlprofiler/tests/Test.qml");
- ProjectExplorer::SessionManager::addProject(project2);
+ ProjectExplorer::ProjectManager::addProject(project2);
m_rewriter.populateFileFinder(nullptr);
QCOMPARE(m_rewriter.getLocalFile("Test.qml"),
Utils::FilePath::fromString(":/qmlprofiler/tests/Test.qml"));
- ProjectExplorer::SessionManager::removeProject(project1);
- ProjectExplorer::SessionManager::removeProject(project2);
+ ProjectExplorer::ProjectManager::removeProject(project1);
+ ProjectExplorer::ProjectManager::removeProject(project2);
}
void QmlProfilerDetailsRewriterTest::seedRewriter()
@@ -174,12 +175,11 @@ void QmlProfilerDetailsRewriterTest::seedRewriter()
m_modelManager = new QmlJS::ModelManagerInterface(this);
QString filename = ":/qmlprofiler/tests/Test.qml";
- QFutureInterface<void> result;
QmlJS::PathsAndLanguages lPaths;
lPaths.maybeInsert(
Utils::FilePath::fromString(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath)),
QmlJS::Dialect::Qml);
- QmlJS::ModelManagerInterface::importScan(result, QmlJS::ModelManagerInterface::workingCopy(),
+ QmlJS::ModelManagerInterface::importScan(QmlJS::ModelManagerInterface::workingCopy(),
lPaths, m_modelManager, false);
QFile file(filename);
@@ -196,11 +196,11 @@ void QmlProfilerDetailsRewriterTest::seedRewriter()
ProjectExplorer::SysRootKitAspect::setSysRoot(kit.get(), "/nowhere");
DummyProject *project = new DummyProject(Utils::FilePath::fromString(filename));
- ProjectExplorer::SessionManager::addProject(project);
+ ProjectExplorer::ProjectManager::addProject(project);
m_rewriter.populateFileFinder(project->addTargetForKit(kit.get()));
- ProjectExplorer::SessionManager::removeProject(project);
+ ProjectExplorer::ProjectManager::removeProject(project);
}
} // namespace Internal
diff --git a/src/plugins/qmlprojectmanager/CMakeLists.txt b/src/plugins/qmlprojectmanager/CMakeLists.txt
index 21da457c21..ad71e3fbff 100644
--- a/src/plugins/qmlprojectmanager/CMakeLists.txt
+++ b/src/plugins/qmlprojectmanager/CMakeLists.txt
@@ -1,7 +1,7 @@
add_qtc_plugin(QmlProjectManager
- CONDITION TARGET Qt5::QuickWidgets
+ CONDITION TARGET Qt::QuickWidgets
PLUGIN_CLASS QmlProjectPlugin
- DEPENDS QmlJS Qt5::QuickWidgets Utils
+ DEPENDS QmlJS Qt::QuickWidgets Utils
PLUGIN_DEPENDS Core ProjectExplorer QtSupport QmlDesignerBase
SOURCES
qmlprojectgen/qmlprojectgenerator.cpp qmlprojectgen/qmlprojectgenerator.h
diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp
index 86645dc8c5..4ccb9ad48f 100644
--- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp
+++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "qmlbuildsystem.h"
-#include "qmlprojectconstants.h"
+#include "../qmlprojectconstants.h"
#include <QtCore5Compat/qtextcodec.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
@@ -16,6 +16,7 @@
#include <coreplugin/icontext.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
+#include <coreplugin/session.h>
#include <extensionsystem/iplugin.h>
#include <extensionsystem/pluginmanager.h>
@@ -26,7 +27,6 @@
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include "projectitem/qmlprojectitem.h"
@@ -37,6 +37,8 @@
#include "texteditor/textdocument.h"
+#include <QAction>
+
using namespace ProjectExplorer;
namespace QmlProjectManager {
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp
index 99995bc2ab..21a538c7c5 100644
--- a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp
+++ b/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
#include "cmakeprojectconverter.h"
#include "cmakeprojectconverterdialog.h"
#include "generatecmakelists.h"
@@ -11,7 +12,7 @@
#include <coreplugin/icore.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qmlprojectmanager/qmlprojectmanagerconstants.h>
@@ -41,10 +42,10 @@ void CmakeProjectConverter::generateMenuEntry(QObject *parent)
Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.ConvertToCmakeProject");
exportMenu->addAction(cmd, QmlProjectManager::Constants::G_EXPORT_CONVERT);
- action->setEnabled(isProjectConvertable(ProjectExplorer::SessionManager::startupProject()));
- QObject::connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::startupProjectChanged, [action]() {
- action->setEnabled(isProjectConvertable(ProjectExplorer::SessionManager::startupProject()));
+ action->setEnabled(isProjectConvertable(ProjectExplorer::ProjectManager::startupProject()));
+ QObject::connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged, [action]() {
+ action->setEnabled(isProjectConvertable(ProjectExplorer::ProjectManager::startupProject()));
});
}
@@ -83,7 +84,7 @@ bool CmakeProjectConverter::isProjectCurrentFormat(const ProjectExplorer::Projec
void CmakeProjectConverter::onConvertProject()
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
const QmlProjectManager::QmlProject *qmlProject =
qobject_cast<const QmlProjectManager::QmlProject*>(project);
if (qmlProject) {
diff --git a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp b/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp
index 40d634361e..b1ce3aaf34 100644
--- a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp
+++ b/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "generatecmakelists.h"
+
#include "generatecmakelistsconstants.h"
#include "cmakegeneratordialog.h"
#include "../qmlprojectmanagertr.h"
@@ -10,9 +11,9 @@
#include <coreplugin/actionmanager/actioncontainer.h>
#include <projectexplorer/buildsystem.h>
-#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qmlprojectmanager/qmlmainfileaspect.h>
@@ -86,7 +87,7 @@ const QString MENU_ITEM_GENERATE = Tr::tr("Generate CMake Build Files...");
const QmlBuildSystem *getBuildSystem()
{
- auto project = ProjectExplorer::SessionManager::startupProject();
+ auto project = ProjectExplorer::ProjectManager::startupProject();
if (project && project->activeTarget() && project->activeTarget()->buildSystem()) {
return qobject_cast<QmlProjectManager::QmlBuildSystem *>(
project->activeTarget()->buildSystem());
@@ -114,8 +115,8 @@ void generateMenuEntry(QObject *parent)
exportMenu->addAction(cmd, QmlProjectManager::Constants::G_EXPORT_GENERATE);
action->setEnabled(false);
- QObject::connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::startupProjectChanged,
+ QObject::connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged,
[action]() {
if (auto buildSystem = getBuildSystem())
action->setEnabled(!buildSystem->qtForMCUs());
@@ -125,8 +126,7 @@ void generateMenuEntry(QObject *parent)
void onGenerateCmakeLists()
{
trackUsage("generateCMakeProjectDialogOpened");
-
- FilePath rootDir = ProjectExplorer::SessionManager::startupProject()->projectDirectory();
+ FilePath rootDir = ProjectExplorer::ProjectManager::startupProject()->projectDirectory();
int projectDirErrors = isProjectCorrectlyFormed(rootDir);
if (projectDirErrors != NoError) {
@@ -340,7 +340,7 @@ const char ADD_SUBDIR[] = "add_subdirectory(%1)\n";
void CmakeFileGenerator::generateMainCmake(const FilePath &rootDir)
{
//TODO startupProject() may be a terrible way to try to get "current project". It's not necessarily the same thing at all.
- QString projectName = ProjectExplorer::SessionManager::startupProject()->displayName();
+ QString projectName = ProjectExplorer::ProjectManager::startupProject()->displayName();
QString appName = projectName + "App";
QString fileSection = "";
@@ -559,7 +559,7 @@ bool CmakeFileGenerator::isDirBlacklisted(const FilePath &dir)
bool CmakeFileGenerator::includeFile(const FilePath &filePath)
{
if (m_checkFileIsInProject) {
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project->isKnownFile(filePath))
return false;
}
diff --git a/src/plugins/qmlprojectmanager/projectfilecontenttools.cpp b/src/plugins/qmlprojectmanager/projectfilecontenttools.cpp
index 8ae0efe311..8cde4e2c22 100644
--- a/src/plugins/qmlprojectmanager/projectfilecontenttools.cpp
+++ b/src/plugins/qmlprojectmanager/projectfilecontenttools.cpp
@@ -18,7 +18,7 @@ QRegularExpression qdsVerRegexp(R"x(qdsVersion: "(.*)")x");
const Utils::FilePaths rootCmakeFiles(ProjectExplorer::Project *project)
{
if (!project)
- project = ProjectExplorer::SessionManager::startupProject();
+ project = ProjectExplorer::ProjectManager::startupProject();
if (!project)
return {};
return project->projectDirectory().dirEntries({QList<QString>({"CMakeLists.txt"}), QDir::Files});
diff --git a/src/plugins/qmlprojectmanager/projectfilecontenttools.h b/src/plugins/qmlprojectmanager/projectfilecontenttools.h
index 843912eb7c..3c3fcb5847 100644
--- a/src/plugins/qmlprojectmanager/projectfilecontenttools.h
+++ b/src/plugins/qmlprojectmanager/projectfilecontenttools.h
@@ -6,7 +6,7 @@
#include "qmlprojectmanager_global.h"
#include <projectexplorer/projectmanager.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/fileutils.h>
diff --git a/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp b/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp
index 6944f8376a..5d328fd934 100644
--- a/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp
+++ b/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp
@@ -55,7 +55,7 @@ QmlMainFileAspect::~QmlMainFileAspect()
delete m_fileListCombo;
}
-void QmlMainFileAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void QmlMainFileAspect::addToLayout(Layouting::LayoutItem &parent)
{
QTC_ASSERT(!m_fileListCombo, delete m_fileListCombo);
m_fileListCombo = new QComboBox;
@@ -67,7 +67,7 @@ void QmlMainFileAspect::addToLayout(Layouting::LayoutBuilder &builder)
this, &QmlMainFileAspect::updateFileComboBox);
connect(m_fileListCombo, &QComboBox::activated, this, &QmlMainFileAspect::setMainScript);
- builder.addItems({Tr::tr("Main QML file:"), m_fileListCombo.data()});
+ parent.addItems({Tr::tr("Main QML file:"), m_fileListCombo.data()});
}
void QmlMainFileAspect::toMap(QVariantMap &map) const
diff --git a/src/plugins/qmlprojectmanager/qmlmainfileaspect.h b/src/plugins/qmlprojectmanager/qmlmainfileaspect.h
index b71fa6784b..3c75e4744a 100644
--- a/src/plugins/qmlprojectmanager/qmlmainfileaspect.h
+++ b/src/plugins/qmlprojectmanager/qmlmainfileaspect.h
@@ -42,7 +42,7 @@ public:
Utils::FilePath currentFile;
};
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) final;
+ void addToLayout(Layouting::LayoutItem &parent) final;
void toMap(QVariantMap &map) const final;
void fromMap(const QVariantMap &map) final;
diff --git a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp
index 4ca9f9aa62..db0001ac70 100644
--- a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp
+++ b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp
@@ -10,8 +10,8 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/runcontrol.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
static bool isMultilanguagePresent()
@@ -114,7 +114,7 @@ void QmlMultiLanguageAspect::fromMap(const QVariantMap &map)
QmlMultiLanguageAspect *QmlMultiLanguageAspect::current()
{
- if (auto project = ProjectExplorer::SessionManager::startupProject())
+ if (auto project = ProjectExplorer::ProjectManager::startupProject())
return current(project);
return {};
}
diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp
index 4e919005c0..31fa3429d2 100644
--- a/src/plugins/qmlprojectmanager/qmlproject.cpp
+++ b/src/plugins/qmlprojectmanager/qmlproject.cpp
@@ -10,7 +10,7 @@
#include <QTimer>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <coreplugin/editormanager/editormanager.h>
@@ -22,6 +22,21 @@
#include "qmlprojectmanagerconstants.h"
#include "qmlprojectmanagertr.h"
#include "utils/algorithm.h"
+#include <qmljs/qmljsmodelmanagerinterface.h>
+
+#include <texteditor/textdocument.h>
+
+#include <utils/algorithm.h>
+#include <utils/infobar.h>
+#include <utils/process.h>
+#include <utils/qtcassert.h>
+
+#include <QDebug>
+#include <QLoggingCategory>
+#include <QMessageBox>
+#include <QRegularExpression>
+#include <QTextCodec>
+#include <QTimer>
using namespace Core;
using namespace ProjectExplorer;
@@ -37,15 +52,11 @@ QmlProject::QmlProject(const Utils::FilePath &fileName)
setNeedsBuildConfigurations(false);
setBuildSystemCreator([](Target *t) { return new QmlBuildSystem(t); });
- // FIXME: why checking this?
- // this should not even be the case. if that's possible, then what?
- // what are the follow-up actions?
- if (!QmlProject::isQtDesignStudio())
- return;
-
- if (allowOnlySingleProject()) {
- Core::EditorManager::closeAllDocuments();
- SessionManager::closeAllProjects();
+ if (QmlProject::isQtDesignStudio()) {
+ if (allowOnlySingleProject()) {
+ EditorManager::closeAllDocuments();
+ ProjectManager::closeAllProjects();
+ }
}
connect(this, &QmlProject::anyParsingFinished, this, &QmlProject::parsingFinished);
@@ -150,7 +161,7 @@ bool QmlProject::setKitWithVersion(const int qtMajorVersion, const QList<Kit *>
}
if (target)
- SessionManager::setActiveTarget(this, target, SetActive::NoCascade);
+ target->project()->setActiveTarget(target, SetActive::NoCascade);
return true;
}
@@ -237,7 +248,7 @@ bool QmlProject::allowOnlySingleProject()
{
QSettings *settings = Core::ICore::settings();
auto key = "QML/Designer/AllowMultipleProjects";
- return !settings->value(key, false).toBool();
+ return !settings->value(QString::fromUtf8(key), false).toBool();
}
} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs b/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs
index d4f3b32cc3..9e6f3babd0 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs
+++ b/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs
@@ -9,6 +9,7 @@ QtcPlugin {
Depends { name: "Core" }
Depends { name: "ProjectExplorer" }
+ Depends { name: "QmlDesignerBase" }
Depends { name: "QtSupport" }
Depends { name: "TextEditor" }
@@ -25,7 +26,6 @@ QtcPlugin {
"qmlprojectconstants.h",
"qmlprojectmanager_global.h", "qmlprojectmanagertr.h",
"qmlprojectmanagerconstants.h",
- "qmlprojectnodes.cpp", "qmlprojectnodes.h",
"qmlprojectplugin.cpp", "qmlprojectplugin.h",
"qmlprojectrunconfiguration.cpp", "qmlprojectrunconfiguration.h",
project.ide_source_tree + "/src/share/3rdparty/studiofonts/studiofonts.qrc"
@@ -39,7 +39,7 @@ QtcPlugin {
"qmlbuildsystem.cpp", "qmlbuildsystem.h",
"projectitem/filefilteritems.cpp", "projectitem/filefilteritems.h",
"projectitem/qmlprojectitem.cpp", "projectitem/qmlprojectitem.h",
- "projectitem/converters.h",
+ "projectitem/converters.cpp", "projectitem/converters.h",
"projectnode/qmlprojectnodes.cpp", "projectnode/qmlprojectnodes.h"
]
}
diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp
index 0a87ef9bc9..2876c3c366 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp
+++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp
@@ -25,7 +25,7 @@
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
#include <projectexplorer/runcontrol.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
@@ -40,7 +40,7 @@
#include <utils/fileutils.h>
#include <utils/fsengine/fileiconprovider.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QAction>
#include <QDesktopServices>
@@ -115,10 +115,10 @@ void QmlProjectPlugin::openQDS(const Utils::FilePath &fileName)
qputenv(Constants::enviromentLaunchedQDS, "true");
//-a and -client arguments help to append project to open design studio application
if (Utils::HostOsInfo::isMacHost())
- qdsStarted = Utils::QtcProcess::startDetached(
+ qdsStarted = Utils::Process::startDetached(
{"/usr/bin/open", {"-a", qdsPath.path(), fileName.toString()}});
else
- qdsStarted = Utils::QtcProcess::startDetached({qdsPath, {"-client", fileName.toString()}});
+ qdsStarted = Utils::Process::startDetached({qdsPath, {"-client", fileName.toString()}});
if (!qdsStarted) {
QMessageBox::warning(Core::ICore::dialogParent(),
@@ -180,7 +180,7 @@ const Utils::FilePath findQmlProjectUpwards(const Utils::FilePath &folder)
static bool findAndOpenProject(const Utils::FilePath &filePath)
{
ProjectExplorer::Project *project
- = ProjectExplorer::SessionManager::projectForFile(filePath);
+ = ProjectExplorer::ProjectManager::projectForFile(filePath);
if (project) {
if (project->projectFilePath().suffix() == "qmlproject") {
@@ -436,7 +436,7 @@ void QmlProjectPlugin::updateQmlLandingPageProjectInfo(const Utils::FilePath &pr
Utils::FilePath QmlProjectPlugin::projectFilePath()
{
- auto project = ProjectExplorer::SessionManager::startupProject();
+ auto project = ProjectExplorer::ProjectManager::startupProject();
const QmlProjectManager::QmlProject *qmlProject = qobject_cast<const QmlProjectManager::QmlProject*>(project);
if (qmlProject) {
return qmlProject->projectFilePath();
diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
index 31a8a32ee6..1cce1650ce 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
+++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
@@ -21,7 +21,7 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/runconfigurationaspects.h>
#include <projectexplorer/runcontrol.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qmldesignerbase/qmldesignerbaseplugin.h>
@@ -34,7 +34,7 @@
#include <utils/aspects.h>
#include <utils/environment.h>
#include <utils/fileutils.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/winutils.h>
#include <qmljstools/qmljstoolsconstants.h>
@@ -289,7 +289,7 @@ void QmlProjectRunConfiguration::createQtVersionAspect()
if (!newTarget)
newTarget = project->addTargetForKit(kits.first());
- SessionManager::setActiveTarget(project, newTarget, SetActive::Cascade);
+ project->setActiveTarget(newTarget, SetActive::Cascade);
/* Reset the aspect. We changed the target and this aspect should not change. */
m_qtversionAspect->blockSignals(true);
diff --git a/src/plugins/qnx/CMakeLists.txt b/src/plugins/qnx/CMakeLists.txt
index f6c5907b4e..8d0c6751b3 100644
--- a/src/plugins/qnx/CMakeLists.txt
+++ b/src/plugins/qnx/CMakeLists.txt
@@ -4,16 +4,11 @@ add_qtc_plugin(Qnx
SOURCES
qnx.qrc
qnxanalyzesupport.cpp qnxanalyzesupport.h
- qnxconfiguration.cpp qnxconfiguration.h
- qnxconfigurationmanager.cpp qnxconfigurationmanager.h
qnxconstants.h
qnxdebugsupport.cpp qnxdebugsupport.h
qnxdeployqtlibrariesdialog.cpp qnxdeployqtlibrariesdialog.h
qnxdevice.cpp qnxdevice.h
- qnxdeviceprocesslist.cpp qnxdeviceprocesslist.h
- qnxdeviceprocesssignaloperation.cpp qnxdeviceprocesssignaloperation.h
qnxdevicetester.cpp qnxdevicetester.h
- qnxdevicewizard.cpp qnxdevicewizard.h
qnxplugin.cpp
qnxqtversion.cpp qnxqtversion.h
qnxrunconfiguration.cpp qnxrunconfiguration.h
diff --git a/src/plugins/qnx/qnx.qbs b/src/plugins/qnx/qnx.qbs
index 0389f542a6..ad4eae91c8 100644
--- a/src/plugins/qnx/qnx.qbs
+++ b/src/plugins/qnx/qnx.qbs
@@ -20,24 +20,14 @@ QtcPlugin {
"qnxtoolchain.h",
"qnx.qrc",
"qnxconstants.h",
- "qnxconfiguration.cpp",
- "qnxconfiguration.h",
"qnxanalyzesupport.cpp",
"qnxanalyzesupport.h",
"qnxdebugsupport.cpp",
"qnxdebugsupport.h",
"qnxdevice.cpp",
"qnxdevice.h",
- "qnxdevicewizard.cpp",
- "qnxdevicewizard.h",
- "qnxdeviceprocesslist.cpp",
- "qnxdeviceprocesslist.h",
- "qnxdeviceprocesssignaloperation.cpp",
- "qnxdeviceprocesssignaloperation.h",
"qnxdevicetester.cpp",
"qnxdevicetester.h",
- "qnxconfigurationmanager.cpp",
- "qnxconfigurationmanager.h",
"qnxsettingspage.cpp",
"qnxsettingspage.h",
"qnxtr.h",
diff --git a/src/plugins/qnx/qnxanalyzesupport.cpp b/src/plugins/qnx/qnxanalyzesupport.cpp
index 29ea42b7cd..4d5a73b2ab 100644
--- a/src/plugins/qnx/qnxanalyzesupport.cpp
+++ b/src/plugins/qnx/qnxanalyzesupport.cpp
@@ -9,8 +9,7 @@
#include <projectexplorer/devicesupport/deviceusedportsgatherer.h>
-#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <qmldebug/qmldebugcommandlinearguments.h>
diff --git a/src/plugins/qnx/qnxconfiguration.cpp b/src/plugins/qnx/qnxconfiguration.cpp
deleted file mode 100644
index c1b0e498f0..0000000000
--- a/src/plugins/qnx/qnxconfiguration.cpp
+++ /dev/null
@@ -1,468 +0,0 @@
-// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "qnxconfiguration.h"
-
-#include "qnxqtversion.h"
-#include "qnxutils.h"
-#include "qnxtoolchain.h"
-#include "qnxtr.h"
-
-#include "debugger/debuggeritem.h"
-
-#include <coreplugin/icore.h>
-
-#include <projectexplorer/toolchainmanager.h>
-#include <projectexplorer/toolchain.h>
-#include <projectexplorer/kit.h>
-#include <projectexplorer/kitmanager.h>
-
-#include <qtsupport/baseqtversion.h>
-#include <qtsupport/qtversionmanager.h>
-#include <qtsupport/qtkitinformation.h>
-
-#include <qmakeprojectmanager/qmakeprojectmanagerconstants.h>
-
-#include <debugger/debuggeritem.h>
-#include <debugger/debuggeritemmanager.h>
-#include <debugger/debuggerkitinformation.h>
-
-#include <utils/algorithm.h>
-
-#include <QDebug>
-#include <QDomDocument>
-#include <QMessageBox>
-
-using namespace ProjectExplorer;
-using namespace QtSupport;
-using namespace Utils;
-using namespace Debugger;
-
-namespace Qnx::Internal {
-
-const QLatin1String QNXEnvFileKey("EnvFile");
-const QLatin1String QNXVersionKey("QNXVersion");
-// For backward compatibility
-const QLatin1String SdpEnvFileKey("NDKEnvFile");
-
-const QLatin1String QNXConfiguration("QNX_CONFIGURATION");
-const QLatin1String QNXTarget("QNX_TARGET");
-const QLatin1String QNXHost("QNX_HOST");
-
-QnxConfiguration::QnxConfiguration() = default;
-
-QnxConfiguration::QnxConfiguration(const FilePath &sdpEnvFile)
-{
- setDefaultConfiguration(sdpEnvFile);
- readInformation();
-}
-
-QnxConfiguration::QnxConfiguration(const QVariantMap &data)
-{
- QString envFilePath = data.value(QNXEnvFileKey).toString();
- if (envFilePath.isEmpty())
- envFilePath = data.value(SdpEnvFileKey).toString();
-
- m_version = QnxVersionNumber(data.value(QNXVersionKey).toString());
-
- setDefaultConfiguration(FilePath::fromString(envFilePath));
- readInformation();
-}
-
-FilePath QnxConfiguration::envFile() const
-{
- return m_envFile;
-}
-
-FilePath QnxConfiguration::qnxTarget() const
-{
- return m_qnxTarget;
-}
-
-FilePath QnxConfiguration::qnxHost() const
-{
- return m_qnxHost;
-}
-
-FilePath QnxConfiguration::qccCompilerPath() const
-{
- return m_qccCompiler;
-}
-
-EnvironmentItems QnxConfiguration::qnxEnv() const
-{
- return m_qnxEnv;
-}
-
-QnxVersionNumber QnxConfiguration::version() const
-{
- return m_version;
-}
-
-QVariantMap QnxConfiguration::toMap() const
-{
- QVariantMap data;
- data.insert(QLatin1String(QNXEnvFileKey), m_envFile.toString());
- data.insert(QLatin1String(QNXVersionKey), m_version.toString());
- return data;
-}
-
-bool QnxConfiguration::isValid() const
-{
- return !m_qccCompiler.isEmpty() && !m_targets.isEmpty();
-}
-
-QString QnxConfiguration::displayName() const
-{
- return m_configName;
-}
-
-bool QnxConfiguration::activate()
-{
- if (isActive())
- return true;
-
- if (!isValid()) {
- QMessageBox::warning(Core::ICore::dialogParent(), Tr::tr("Cannot Set Up QNX Configuration"),
- validationErrorMessage(), QMessageBox::Ok);
- return false;
- }
-
- for (const Target &target : std::as_const(m_targets))
- createTools(target);
-
- return true;
-}
-
-void QnxConfiguration::deactivate()
-{
- if (!isActive())
- return;
-
- const Toolchains toolChainsToRemove =
- ToolChainManager::toolchains(Utils::equal(&ToolChain::compilerCommand, qccCompilerPath()));
-
- QList<DebuggerItem> debuggersToRemove;
- const QList<DebuggerItem> debuggerItems = DebuggerItemManager::debuggers();
- for (const DebuggerItem &debuggerItem : debuggerItems) {
- if (findTargetByDebuggerPath(debuggerItem.command()))
- debuggersToRemove.append(debuggerItem);
- }
-
- const QList<Kit *> kits = KitManager::kits();
- for (Kit *kit : kits) {
- if (kit->isAutoDetected()
- && DeviceTypeKitAspect::deviceTypeId(kit) == Constants::QNX_QNX_OS_TYPE
- && toolChainsToRemove.contains(ToolChainKitAspect::cxxToolChain(kit))) {
- KitManager::deregisterKit(kit);
- }
- }
-
- for (ToolChain *tc : toolChainsToRemove)
- ToolChainManager::deregisterToolChain(tc);
-
- for (const DebuggerItem &debuggerItem : std::as_const(debuggersToRemove))
- DebuggerItemManager::deregisterDebugger(debuggerItem.id());
-}
-
-bool QnxConfiguration::isActive() const
-{
- const bool hasToolChain = ToolChainManager::toolChain(Utils::equal(&ToolChain::compilerCommand,
- qccCompilerPath()));
- const bool hasDebugger = Utils::contains(DebuggerItemManager::debuggers(), [this](const DebuggerItem &di) {
- return findTargetByDebuggerPath(di.command());
- });
-
- return hasToolChain && hasDebugger;
-}
-
-FilePath QnxConfiguration::sdpPath() const
-{
- return envFile().parentDir();
-}
-
-QnxQtVersion *QnxConfiguration::qnxQtVersion(const Target &target) const
-{
- const QtVersions versions = QtVersionManager::versions(
- Utils::equal(&QtVersion::type, QString::fromLatin1(Constants::QNX_QNX_QT)));
- for (QtVersion *version : versions) {
- auto qnxQt = dynamic_cast<QnxQtVersion *>(version);
- if (qnxQt && qnxQt->sdpPath() == sdpPath()) {
- const Abis abis = version->qtAbis();
- for (const Abi &qtAbi : abis) {
- if ((qtAbi == target.m_abi) && (qnxQt->cpuDir() == target.cpuDir()))
- return qnxQt;
- }
- }
- }
- return nullptr;
-}
-
-QList<ToolChain *> QnxConfiguration::autoDetect(const QList<ToolChain *> &alreadyKnown)
-{
- QList<ToolChain *> result;
-
- for (const Target &target : std::as_const(m_targets))
- result += findToolChain(alreadyKnown, target.m_abi);
-
- return result;
-}
-
-void QnxConfiguration::createTools(const Target &target)
-{
- QnxToolChainMap toolchainMap = createToolChain(target);
- QVariant debuggerId = createDebugger(target);
- createKit(target, toolchainMap, debuggerId);
-}
-
-QVariant QnxConfiguration::createDebugger(const Target &target)
-{
- Environment sysEnv = m_qnxHost.deviceEnvironment();
- sysEnv.modify(qnxEnvironmentItems());
-
- Debugger::DebuggerItem debugger;
- debugger.setCommand(target.m_debuggerPath);
- debugger.reinitializeFromFile(nullptr, &sysEnv);
- debugger.setUnexpandedDisplayName(Tr::tr("Debugger for %1 (%2)")
- .arg(displayName())
- .arg(target.shortDescription()));
- return Debugger::DebuggerItemManager::registerDebugger(debugger);
-}
-
-QnxConfiguration::QnxToolChainMap QnxConfiguration::createToolChain(const Target &target)
-{
- QnxToolChainMap toolChainMap;
-
- for (auto language : { ProjectExplorer::Constants::C_LANGUAGE_ID,
- ProjectExplorer::Constants::CXX_LANGUAGE_ID}) {
- auto toolChain = new QnxToolChain;
- toolChain->setDetection(ToolChain::AutoDetection);
- toolChain->setLanguage(language);
- toolChain->setTargetAbi(target.m_abi);
- toolChain->setDisplayName(Tr::tr("QCC for %1 (%2)")
- .arg(displayName())
- .arg(target.shortDescription()));
- toolChain->setSdpPath(sdpPath());
- toolChain->setCpuDir(target.cpuDir());
- toolChain->resetToolChain(qccCompilerPath());
- ToolChainManager::registerToolChain(toolChain);
-
- toolChainMap.insert({language, toolChain});
- }
-
- return toolChainMap;
-}
-
-QList<ToolChain *> QnxConfiguration::findToolChain(const QList<ToolChain *> &alreadyKnown,
- const Abi &abi)
-{
- return Utils::filtered(alreadyKnown, [this, abi](ToolChain *tc) {
- return tc->typeId() == Constants::QNX_TOOLCHAIN_ID
- && tc->targetAbi() == abi
- && tc->compilerCommand() == m_qccCompiler;
- });
-}
-
-void QnxConfiguration::createKit(const Target &target, const QnxToolChainMap &toolChainMap,
- const QVariant &debugger)
-{
- QnxQtVersion *qnxQt = qnxQtVersion(target); // nullptr is ok.
-
- const auto init = [&](Kit *k) {
- QtKitAspect::setQtVersion(k, qnxQt);
- ToolChainKitAspect::setToolChain(k, toolChainMap.at(ProjectExplorer::Constants::C_LANGUAGE_ID));
- ToolChainKitAspect::setToolChain(k, toolChainMap.at(ProjectExplorer::Constants::CXX_LANGUAGE_ID));
-
- if (debugger.isValid())
- DebuggerKitAspect::setDebugger(k, debugger);
-
- DeviceTypeKitAspect::setDeviceTypeId(k, Constants::QNX_QNX_OS_TYPE);
- // TODO: Add sysroot?
-
- k->setUnexpandedDisplayName(Tr::tr("Kit for %1 (%2)")
- .arg(displayName())
- .arg(target.shortDescription()));
-
- k->setAutoDetected(false);
- k->setAutoDetectionSource(envFile().toString());
- k->setMutable(DeviceKitAspect::id(), true);
-
- k->setSticky(ToolChainKitAspect::id(), true);
- k->setSticky(DeviceTypeKitAspect::id(), true);
- k->setSticky(SysRootKitAspect::id(), true);
- k->setSticky(DebuggerKitAspect::id(), true);
- k->setSticky(QmakeProjectManager::Constants::KIT_INFORMATION_ID, true);
-
- EnvironmentKitAspect::setEnvironmentChanges(k, qnxEnvironmentItems());
- };
-
- // add kit with device and qt version not sticky
- KitManager::registerKit(init);
-}
-
-QString QnxConfiguration::validationErrorMessage() const
-{
- if (isValid())
- return {};
-
- QStringList errorStrings
- = {Tr::tr("The following errors occurred while activating the QNX configuration:")};
- if (m_qccCompiler.isEmpty())
- errorStrings << Tr::tr("- No GCC compiler found.");
- if (m_targets.isEmpty())
- errorStrings << Tr::tr("- No targets found.");
- return errorStrings.join('\n');
-}
-
-void QnxConfiguration::setVersion(const QnxVersionNumber &version)
-{
- m_version = version;
-}
-
-void QnxConfiguration::readInformation()
-{
- const FilePath configPath = m_qnxConfiguration / "qconfig";
- if (!configPath.isDir())
- return;
-
- configPath.iterateDirectory([this, configPath](const FilePath &sdpFile) {
- QFile xmlFile(sdpFile.toFSPathString());
- if (!xmlFile.open(QIODevice::ReadOnly))
- return IterationPolicy::Continue;
-
- QDomDocument doc;
- if (!doc.setContent(&xmlFile)) // Skip error message
- return IterationPolicy::Continue;
-
- QDomElement docElt = doc.documentElement();
- if (docElt.tagName() != QLatin1String("qnxSystemDefinition"))
- return IterationPolicy::Continue;
-
- QDomElement childElt = docElt.firstChildElement(QLatin1String("installation"));
- // The file contains only one installation node
- if (childElt.isNull()) // The file contains only one base node
- return IterationPolicy::Continue;
-
- FilePath host = configPath.withNewPath(
- childElt.firstChildElement(QLatin1String("host")).text()).canonicalPath();
- if (m_qnxHost != host)
- return IterationPolicy::Continue;
-
- FilePath target = configPath.withNewPath(
- childElt.firstChildElement(QLatin1String("target")).text()).canonicalPath();
- if (m_qnxTarget != target)
- return IterationPolicy::Continue;
-
- m_configName = childElt.firstChildElement(QLatin1String("name")).text();
- QString version = childElt.firstChildElement(QLatin1String("version")).text();
- setVersion(QnxVersionNumber(version));
- return IterationPolicy::Stop;
- }, {{"*.xml"}, QDir::Files});
-}
-
-void QnxConfiguration::setDefaultConfiguration(const FilePath &envScript)
-{
- QTC_ASSERT(!envScript.isEmpty(), return);
- m_envFile = envScript;
- m_qnxEnv = QnxUtils::qnxEnvironmentFromEnvFile(m_envFile);
- for (const EnvironmentItem &item : std::as_const(m_qnxEnv)) {
- if (item.name == QNXConfiguration)
- m_qnxConfiguration = envScript.withNewPath(item.value).canonicalPath();
- else if (item.name == QNXTarget)
- m_qnxTarget = envScript.withNewPath(item.value).canonicalPath();
- else if (item.name == QNXHost)
- m_qnxHost = envScript.withNewPath(item.value).canonicalPath();
- }
-
- const FilePath qccPath = m_qnxHost.pathAppended("usr/bin/qcc").withExecutableSuffix();
- if (qccPath.exists())
- m_qccCompiler = qccPath;
-
- // Some fall back in case the qconfig dir with .xml files is not found later
- if (m_configName.isEmpty())
- m_configName = QString("%1 - %2").arg(m_qnxHost.fileName(), m_qnxTarget.fileName());
-
- updateTargets();
- assignDebuggersToTargets();
-
- // Remove debuggerless targets.
- Utils::erase(m_targets, [](const Target &target) {
- if (target.m_debuggerPath.isEmpty())
- qWarning() << "No debugger found for" << target.m_path << "... discarded";
- return target.m_debuggerPath.isEmpty();
- });
-}
-
-EnvironmentItems QnxConfiguration::qnxEnvironmentItems() const
-{
- Utils::EnvironmentItems envList;
- envList.push_back(EnvironmentItem(QNXConfiguration, m_qnxConfiguration.path()));
- envList.push_back(EnvironmentItem(QNXTarget, m_qnxTarget.path()));
- envList.push_back(EnvironmentItem(QNXHost, m_qnxHost.path()));
-
- return envList;
-}
-
-const QnxConfiguration::Target *QnxConfiguration::findTargetByDebuggerPath(
- const FilePath &path) const
-{
- const auto it = std::find_if(m_targets.begin(), m_targets.end(),
- [path](const Target &target) { return target.m_debuggerPath == path; });
- return it == m_targets.end() ? nullptr : &(*it);
-}
-
-void QnxConfiguration::updateTargets()
-{
- m_targets.clear();
- const QList<QnxTarget> targets = QnxUtils::findTargets(m_qnxTarget);
- for (const QnxTarget &target : targets)
- m_targets.append(Target(target.m_abi, target.m_path));
-}
-
-void QnxConfiguration::assignDebuggersToTargets()
-{
- const FilePath hostUsrBinDir = m_qnxHost.pathAppended("usr/bin");
- QString pattern = "nto*-gdb";
- if (m_qnxHost.osType() == Utils::OsTypeWindows)
- pattern += ".exe";
-
- const FilePaths debuggerNames = hostUsrBinDir.dirEntries({{pattern}, QDir::Files});
- Environment sysEnv = m_qnxHost.deviceEnvironment();
- sysEnv.modify(qnxEnvironmentItems());
-
- for (const FilePath &debuggerPath : debuggerNames) {
- DebuggerItem item;
- item.setCommand(debuggerPath);
- item.reinitializeFromFile(nullptr, &sysEnv);
- bool found = false;
- for (const Abi &abi : item.abis()) {
- for (Target &target : m_targets) {
- if (target.m_abi.isCompatibleWith(abi)) {
- found = true;
-
- if (target.m_debuggerPath.isEmpty()) {
- target.m_debuggerPath = debuggerPath;
- } else {
- qWarning() << debuggerPath << "has the same ABI as" << target.m_debuggerPath
- << "... discarded";
- break;
- }
- }
- }
- }
- if (!found)
- qWarning() << "No target found for" << debuggerPath.toUserOutput() << "... discarded";
- }
-}
-
-QString QnxConfiguration::Target::shortDescription() const
-{
- return QnxUtils::cpuDirShortDescription(cpuDir());
-}
-
-QString QnxConfiguration::Target::cpuDir() const
-{
- return m_path.fileName();
-}
-
-} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxconfiguration.h b/src/plugins/qnx/qnxconfiguration.h
deleted file mode 100644
index 34056536c3..0000000000
--- a/src/plugins/qnx/qnxconfiguration.h
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "qnxconstants.h"
-#include "qnxversionnumber.h"
-
-#include <utils/fileutils.h>
-#include <utils/environment.h>
-
-#include <projectexplorer/abi.h>
-
-#include <debugger/debuggeritemmanager.h>
-
-#include <QVariant>
-
-namespace ProjectExplorer
-{
- class Kit;
- class ToolChain;
-}
-
-namespace Qnx::Internal {
-
-class QnxToolChain;
-class QnxQtVersion;
-
-class QnxConfiguration
-{
-public:
- QnxConfiguration();
- QnxConfiguration(const Utils::FilePath &sdpEnvFile);
- QnxConfiguration(const QVariantMap &data);
-
- Utils::FilePath envFile() const;
- Utils::FilePath qnxTarget() const;
- Utils::FilePath qnxHost() const;
- Utils::FilePath qccCompilerPath() const;
- Utils::EnvironmentItems qnxEnv() const;
- QnxVersionNumber version() const;
- QVariantMap toMap() const;
-
- bool isValid() const;
-
- QString displayName() const;
- bool activate();
- void deactivate();
- bool isActive() const;
- Utils::FilePath sdpPath() const;
-
- QList<ProjectExplorer::ToolChain *> autoDetect(
- const QList<ProjectExplorer::ToolChain *> &alreadyKnown);
-
-private:
- QList<ProjectExplorer::ToolChain *> findToolChain(
- const QList<ProjectExplorer::ToolChain *> &alreadyKnown,
- const ProjectExplorer::Abi &abi);
-
- QString validationErrorMessage() const;
-
- void setVersion(const QnxVersionNumber& version);
-
- void readInformation();
-
- void setDefaultConfiguration(const Utils::FilePath &envScript);
-
- Utils::EnvironmentItems qnxEnvironmentItems() const;
-
- QString m_configName;
-
- Utils::FilePath m_envFile;
- Utils::FilePath m_qnxConfiguration;
- Utils::FilePath m_qnxTarget;
- Utils::FilePath m_qnxHost;
- Utils::FilePath m_qccCompiler;
- Utils::EnvironmentItems m_qnxEnv;
- QnxVersionNumber m_version;
-
- class Target
- {
- public:
- Target(const ProjectExplorer::Abi &abi, const Utils::FilePath &path)
- : m_abi(abi), m_path(path)
- {
- }
-
- QString shortDescription() const;
- QString cpuDir() const;
-
- ProjectExplorer::Abi m_abi;
- Utils::FilePath m_path;
- Utils::FilePath m_debuggerPath;
- };
-
- QList<Target> m_targets;
-
- QnxQtVersion *qnxQtVersion(const Target &target) const;
-
- void createTools(const Target &target);
- QVariant createDebugger(const Target &target);
-
- using QnxToolChainMap = std::map<const char*, QnxToolChain*>;
-
- QnxToolChainMap createToolChain(const Target &target);
- void createKit(const Target &target, const QnxToolChainMap &toolChain, const QVariant &debugger);
-
- const Target *findTargetByDebuggerPath(const Utils::FilePath &path) const;
-
- void updateTargets();
- void assignDebuggersToTargets();
-};
-
-} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxconfigurationmanager.cpp b/src/plugins/qnx/qnxconfigurationmanager.cpp
deleted file mode 100644
index da585b69cf..0000000000
--- a/src/plugins/qnx/qnxconfigurationmanager.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "qnxconfigurationmanager.h"
-
-#include "qnxconfiguration.h"
-
-#include <coreplugin/icore.h>
-
-#include <utils/persistentsettings.h>
-#include <utils/qtcassert.h>
-
-using namespace Utils;
-
-namespace Qnx::Internal {
-
-const QLatin1String QNXConfigDataKey("QNXConfiguration.");
-const QLatin1String QNXConfigCountKey("QNXConfiguration.Count");
-const QLatin1String QNXConfigsFileVersionKey("Version");
-
-static FilePath qnxConfigSettingsFileName()
-{
- return Core::ICore::userResourcePath("qnx/qnxconfigurations.xml");
-}
-
-static QnxConfigurationManager *m_instance = nullptr;
-
-QnxConfigurationManager::QnxConfigurationManager()
-{
- m_instance = this;
- m_writer = new PersistentSettingsWriter(qnxConfigSettingsFileName(), "QnxConfigurations");
- connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested,
- this, &QnxConfigurationManager::saveConfigs);
-}
-
-QnxConfigurationManager *QnxConfigurationManager::instance()
-{
- return m_instance;
-}
-
-QnxConfigurationManager::~QnxConfigurationManager()
-{
- m_instance = nullptr;
- qDeleteAll(m_configurations);
- delete m_writer;
-}
-
-QList<QnxConfiguration *> QnxConfigurationManager::configurations() const
-{
- return m_configurations;
-}
-
-void QnxConfigurationManager::removeConfiguration(QnxConfiguration *config)
-{
- if (m_configurations.removeAll(config)) {
- delete config;
- emit configurationsListUpdated();
- }
-}
-
-bool QnxConfigurationManager::addConfiguration(QnxConfiguration *config)
-{
- if (!config || !config->isValid())
- return false;
-
- for (QnxConfiguration *c : std::as_const(m_configurations)) {
- if (c->envFile() == config->envFile())
- return false;
- }
-
- m_configurations.append(config);
- emit configurationsListUpdated();
- return true;
-}
-
-QnxConfiguration *QnxConfigurationManager::configurationFromEnvFile(const FilePath &envFile) const
-{
- for (QnxConfiguration *c : m_configurations) {
- if (c->envFile() == envFile)
- return c;
- }
-
- return nullptr;
-}
-
-void QnxConfigurationManager::saveConfigs()
-{
- QTC_ASSERT(m_writer, return);
- QVariantMap data;
- data.insert(QLatin1String(QNXConfigsFileVersionKey), 1);
- int count = 0;
- for (QnxConfiguration *config : std::as_const(m_configurations)) {
- QVariantMap tmp = config->toMap();
- if (tmp.isEmpty())
- continue;
-
- data.insert(QNXConfigDataKey + QString::number(count), tmp);
- ++count;
- }
-
- data.insert(QLatin1String(QNXConfigCountKey), count);
- m_writer->save(data, Core::ICore::dialogParent());
-}
-
-void QnxConfigurationManager::restoreConfigurations()
-{
- PersistentSettingsReader reader;
- if (!reader.load(qnxConfigSettingsFileName()))
- return;
-
- QVariantMap data = reader.restoreValues();
- int count = data.value(QNXConfigCountKey, 0).toInt();
- for (int i = 0; i < count; ++i) {
- const QString key = QNXConfigDataKey + QString::number(i);
- if (!data.contains(key))
- continue;
-
- const QVariantMap dMap = data.value(key).toMap();
- auto configuration = new QnxConfiguration(dMap);
- addConfiguration(configuration);
- }
-}
-
-} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxconfigurationmanager.h b/src/plugins/qnx/qnxconfigurationmanager.h
deleted file mode 100644
index bc0d02dd98..0000000000
--- a/src/plugins/qnx/qnxconfigurationmanager.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <utils/fileutils.h>
-
-namespace Utils { class PersistentSettingsWriter; }
-
-namespace Qnx::Internal {
-
-class QnxConfiguration;
-class QnxPlugin;
-
-class QnxConfigurationManager: public QObject
-{
- Q_OBJECT
-public:
- QnxConfigurationManager();
- ~QnxConfigurationManager() override;
-
- void restoreConfigurations();
-
- static QnxConfigurationManager *instance();
- QList<QnxConfiguration*> configurations() const;
- void removeConfiguration(QnxConfiguration *config);
- bool addConfiguration(QnxConfiguration *config);
- QnxConfiguration* configurationFromEnvFile(const Utils::FilePath &envFile) const;
-
-protected slots:
- void saveConfigs();
-
-signals:
- void configurationsListUpdated();
-
-private:
- QList<QnxConfiguration*> m_configurations;
- Utils::PersistentSettingsWriter *m_writer;
-};
-
-} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxconstants.h b/src/plugins/qnx/qnxconstants.h
index b70db8c87e..3b8bdd2699 100644
--- a/src/plugins/qnx/qnxconstants.h
+++ b/src/plugins/qnx/qnxconstants.h
@@ -15,5 +15,6 @@ const char QNX_QNX_OS_TYPE[] = "QnxOsType"; // Also used for device type.
const char QNX_TOOLCHAIN_ID[] = "Qnx.QccToolChain";
const char QNX_TMP_DIR[] = "/tmp"; // /var/run is root:root drwxr-xr-x
+const char QNX_DIRECT_UPLOAD_STEP_ID[] ="Qnx.DirectUploadStep";
} // Qnx::Constants
diff --git a/src/plugins/qnx/qnxdebugsupport.cpp b/src/plugins/qnx/qnxdebugsupport.cpp
index cb3932daab..919e9ef1fc 100644
--- a/src/plugins/qnx/qnxdebugsupport.cpp
+++ b/src/plugins/qnx/qnxdebugsupport.cpp
@@ -21,8 +21,8 @@
#include <projectexplorer/kitchooser.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/runconfigurationaspects.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <projectexplorer/toolchain.h>
@@ -33,9 +33,9 @@
#include <utils/fileutils.h>
#include <utils/pathchooser.h>
#include <utils/portlist.h>
+#include <utils/process.h>
#include <utils/processinfo.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDir>
#include <QFormLayout>
@@ -223,7 +223,7 @@ void showAttachToProcessDialog()
return;
// FIXME: That should be somehow related to the selected kit.
- auto runConfig = SessionManager::startupRunConfiguration();
+ auto runConfig = ProjectManager::startupRunConfiguration();
const int pid = dlg.currentProcess().processId;
// QString projectSourceDirectory = dlg.projectSource();
diff --git a/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp b/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp
index 78d752efb0..a5cc9cdad8 100644
--- a/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp
+++ b/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp
@@ -16,9 +16,9 @@
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QComboBox>
#include <QDir>
@@ -31,8 +31,8 @@
using namespace ProjectExplorer;
using namespace QtSupport;
+using namespace Tasking;
using namespace Utils;
-using namespace Utils::Tasking;
namespace Qnx::Internal {
@@ -119,12 +119,12 @@ QList<DeployableFile> collectFilesToUpload(const DeployableFile &deployable)
TaskItem QnxDeployQtLibrariesDialogPrivate::checkDirTask()
{
- const auto setupHandler = [this](QtcProcess &process) {
+ const auto setupHandler = [this](Process &process) {
m_deployLogWindow->appendPlainText(Tr::tr("Checking existence of \"%1\"")
.arg(fullRemoteDirectory()));
process.setCommand({m_device->filePath("test"), {"-d", fullRemoteDirectory()}});
};
- const auto doneHandler = [this](const QtcProcess &process) {
+ const auto doneHandler = [this](const Process &process) {
Q_UNUSED(process)
const int answer = QMessageBox::question(q, q->windowTitle(),
Tr::tr("The remote directory \"%1\" already exists.\n"
@@ -133,7 +133,7 @@ TaskItem QnxDeployQtLibrariesDialogPrivate::checkDirTask()
QMessageBox::Yes | QMessageBox::No);
m_checkResult = answer == QMessageBox::Yes ? CheckResult::RemoveDir : CheckResult::Abort;
};
- const auto errorHandler = [this](const QtcProcess &process) {
+ const auto errorHandler = [this](const Process &process) {
if (process.result() != ProcessResult::FinishedWithError) {
m_deployLogWindow->appendPlainText(Tr::tr("Connection failed: %1")
.arg(process.errorString()));
@@ -142,24 +142,24 @@ TaskItem QnxDeployQtLibrariesDialogPrivate::checkDirTask()
}
m_checkResult = CheckResult::SkipRemoveDir;
};
- return Process(setupHandler, doneHandler, errorHandler);
+ return ProcessTask(setupHandler, doneHandler, errorHandler);
}
TaskItem QnxDeployQtLibrariesDialogPrivate::removeDirTask()
{
- const auto setupHandler = [this](QtcProcess &process) {
+ const auto setupHandler = [this](Process &process) {
if (m_checkResult != CheckResult::RemoveDir)
return TaskAction::StopWithDone;
m_deployLogWindow->appendPlainText(Tr::tr("Removing \"%1\"").arg(fullRemoteDirectory()));
process.setCommand({m_device->filePath("rm"), {"-rf", fullRemoteDirectory()}});
return TaskAction::Continue;
};
- const auto errorHandler = [this](const QtcProcess &process) {
+ const auto errorHandler = [this](const Process &process) {
QTC_ASSERT(process.exitCode() == 0, return);
m_deployLogWindow->appendPlainText(Tr::tr("Connection failed: %1")
.arg(process.errorString()));
};
- return Process(setupHandler, {}, errorHandler);
+ return ProcessTask(setupHandler, {}, errorHandler);
}
TaskItem QnxDeployQtLibrariesDialogPrivate::uploadTask()
@@ -193,16 +193,16 @@ TaskItem QnxDeployQtLibrariesDialogPrivate::uploadTask()
const auto errorHandler = [this](const FileTransfer &transfer) {
emitErrorMessage(transfer.resultData().m_errorString);
};
- return Transfer(setupHandler, {}, errorHandler);
+ return FileTransferTask(setupHandler, {}, errorHandler);
}
TaskItem QnxDeployQtLibrariesDialogPrivate::chmodTask(const DeployableFile &file)
{
- const auto setupHandler = [=](QtcProcess &process) {
+ const auto setupHandler = [=](Process &process) {
process.setCommand({m_device->filePath("chmod"),
{"a+x", Utils::ProcessArgs::quoteArgUnix(file.remoteFilePath())}});
};
- const auto errorHandler = [=](const QtcProcess &process) {
+ const auto errorHandler = [=](const Process &process) {
const QString error = process.errorString();
if (!error.isEmpty()) {
emitWarningMessage(Tr::tr("Remote chmod failed for file \"%1\": %2")
@@ -212,7 +212,7 @@ TaskItem QnxDeployQtLibrariesDialogPrivate::chmodTask(const DeployableFile &file
.arg(file.remoteFilePath(), process.cleanedStdErr()));
}
};
- return Process(setupHandler, {}, errorHandler);
+ return ProcessTask(setupHandler, {}, errorHandler);
}
TaskItem QnxDeployQtLibrariesDialogPrivate::chmodTree()
@@ -223,14 +223,14 @@ TaskItem QnxDeployQtLibrariesDialogPrivate::chmodTree()
if (file.isExecutable())
filesToChmod << file;
}
- QList<TaskItem> chmodList{optional, ParallelLimit(MaxConcurrentStatCalls)};
+ QList<TaskItem> chmodList{finishAllAndDone, parallelLimit(MaxConcurrentStatCalls)};
for (const DeployableFile &file : std::as_const(filesToChmod)) {
QTC_ASSERT(file.isValid(), continue);
chmodList.append(chmodTask(file));
}
tree.setupRoot(chmodList);
};
- return Tree{setupChmodHandler};
+ return TaskTreeTask{setupChmodHandler};
}
Group QnxDeployQtLibrariesDialogPrivate::deployRecipe()
@@ -261,18 +261,18 @@ Group QnxDeployQtLibrariesDialogPrivate::deployRecipe()
return TaskAction::Continue;
};
const Group root {
- OnGroupSetup(setupHandler),
+ onGroupSetup(setupHandler),
Group {
- optional,
+ finishAllAndDone,
checkDirTask()
},
Group {
- OnGroupSetup(subGroupSetupHandler),
+ onGroupSetup(subGroupSetupHandler),
removeDirTask(),
uploadTask(),
chmodTree()
},
- OnGroupDone(doneHandler)
+ onGroupDone(doneHandler)
};
return root;
}
diff --git a/src/plugins/qnx/qnxdevice.cpp b/src/plugins/qnx/qnxdevice.cpp
index 0c7c99a5c3..306b87e861 100644
--- a/src/plugins/qnx/qnxdevice.cpp
+++ b/src/plugins/qnx/qnxdevice.cpp
@@ -6,18 +6,21 @@
#include "qnxconstants.h"
#include "qnxdeployqtlibrariesdialog.h"
#include "qnxdevicetester.h"
-#include "qnxdeviceprocesslist.h"
-#include "qnxdeviceprocesssignaloperation.h"
-#include "qnxdevicewizard.h"
#include "qnxtr.h"
-#include <remotelinux/sshprocessinterface.h>
+#include <coreplugin/icore.h>
+
+#include <projectexplorer/devicesupport/sshparameters.h>
+
+#include <remotelinux/genericlinuxdeviceconfigurationwizardpages.h>
+#include <remotelinux/remotelinuxsignaloperation.h>
+#include <remotelinux/linuxdevice.h>
#include <utils/port.h>
+#include <utils/portlist.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-
-#include <QRegularExpression>
+#include <utils/wizard.h>
using namespace ProjectExplorer;
using namespace RemoteLinux;
@@ -25,149 +28,91 @@ using namespace Utils;
namespace Qnx::Internal {
-class QnxProcessImpl final : public SshProcessInterface
-{
-public:
- QnxProcessImpl(const LinuxDevice *linuxDevice);
- ~QnxProcessImpl() { killIfRunning(); }
-
-private:
- QString fullCommandLine(const CommandLine &commandLine) const final;
- void handleSendControlSignal(Utils::ControlSignal controlSignal) final;
-
- const QString m_pidFile;
-};
-
-static std::atomic_int s_pidFileCounter = 1;
-
-QnxProcessImpl::QnxProcessImpl(const LinuxDevice *linuxDevice)
- : SshProcessInterface(linuxDevice)
- , m_pidFile(QString("%1/qtc.%2.pid").arg(Constants::QNX_TMP_DIR).arg(s_pidFileCounter.fetch_add(1)))
+static QString signalProcessByNameQnxCommandLine(const QString &filePath, int sig)
{
+ QString executable = filePath;
+ return QString::fromLatin1("for PID in $(ps -f -o pid,comm | grep %1 | awk '/%1/ {print $1}'); "
+ "do "
+ "kill -%2 $PID; "
+ "done").arg(executable.replace(QLatin1String("/"), QLatin1String("\\/"))).arg(sig);
}
-QString QnxProcessImpl::fullCommandLine(const CommandLine &commandLine) const
+class QnxDeviceProcessSignalOperation : public RemoteLinuxSignalOperation
{
- QStringList args = ProcessArgs::splitArgs(commandLine.arguments());
- args.prepend(commandLine.executable().toString());
- const QString cmd = ProcessArgs::createUnixArgs(args).toString();
-
- QString fullCommandLine =
- "test -f /etc/profile && . /etc/profile ; "
- "test -f $HOME/profile && . $HOME/profile ; ";
-
- if (!m_setup.m_workingDirectory.isEmpty())
- fullCommandLine += QString::fromLatin1("cd %1 ; ").arg(
- ProcessArgs::quoteArg(m_setup.m_workingDirectory.toString()));
-
- const Environment env = m_setup.m_environment;
- for (auto it = env.constBegin(); it != env.constEnd(); ++it) {
- fullCommandLine += QString::fromLatin1("%1='%2' ")
- .arg(env.key(it)).arg(env.expandedValueForKey(env.key(it)));
+public:
+ explicit QnxDeviceProcessSignalOperation(const IDeviceConstPtr &device)
+ : RemoteLinuxSignalOperation(device)
+ {}
+
+ QString killProcessByNameCommandLine(const QString &filePath) const override
+ {
+ return QString::fromLatin1("%1; %2").arg(signalProcessByNameQnxCommandLine(filePath, 15),
+ signalProcessByNameQnxCommandLine(filePath, 9));
}
- fullCommandLine += QString::fromLatin1("%1 & echo $! > %2").arg(cmd).arg(m_pidFile);
-
- return fullCommandLine;
-}
-
-void QnxProcessImpl::handleSendControlSignal(Utils::ControlSignal controlSignal)
-{
- QTC_ASSERT(controlSignal != ControlSignal::KickOff, return);
- const QString args = QString::fromLatin1("-%1 `cat %2`")
- .arg(controlSignalToInt(controlSignal)).arg(m_pidFile);
- CommandLine command = { "kill", args, CommandLine::Raw };
- // Note: This blocking call takes up to 2 ms for local remote.
- runInShell(command);
-}
-
-const char QnxVersionKey[] = "QnxVersion";
-
-QnxDevice::QnxDevice()
-{
- setDisplayType(Tr::tr("QNX"));
- setDefaultDisplayName(Tr::tr("QNX Device"));
- setOsType(OsTypeOtherUnix);
-
- addDeviceAction({Tr::tr("Deploy Qt libraries..."), [](const IDevice::Ptr &device, QWidget *parent) {
- QnxDeployQtLibrariesDialog dialog(device, parent);
- dialog.exec();
- }});
-}
+ QString interruptProcessByNameCommandLine(const QString &filePath) const override
+ {
+ return signalProcessByNameQnxCommandLine(filePath, 2);
+ }
+};
-int QnxDevice::qnxVersion() const
+class QnxDevice final : public LinuxDevice
{
- if (m_versionNumber == 0)
- updateVersionNumber();
-
- return m_versionNumber;
-}
+public:
+ QnxDevice()
+ {
+ setDisplayType(Tr::tr("QNX"));
+ setDefaultDisplayName(Tr::tr("QNX Device"));
+ setOsType(OsTypeOtherUnix);
+ setupId(IDevice::ManuallyAdded);
+ setType(Constants::QNX_QNX_OS_TYPE);
+ setMachineType(IDevice::Hardware);
+ SshParameters sshParams;
+ sshParams.timeout = 10;
+ setSshParameters(sshParams);
+ setFreePorts(PortList::fromString("10000-10100"));
+
+ addDeviceAction({Tr::tr("Deploy Qt libraries..."), [](const IDevice::Ptr &device, QWidget *parent) {
+ QnxDeployQtLibrariesDialog dialog(device, parent);
+ dialog.exec();
+ }});
+ }
-void QnxDevice::updateVersionNumber() const
-{
- QtcProcess versionNumberProcess;
-
- versionNumberProcess.setCommand({filePath("uname"), {"-r"}});
- versionNumberProcess.runBlocking(EventLoopMode::On);
-
- QByteArray output = versionNumberProcess.readAllRawStandardOutput();
- QString versionMessage = QString::fromLatin1(output);
- const QRegularExpression versionNumberRegExp("(\\d+)\\.(\\d+)\\.(\\d+)");
- const QRegularExpressionMatch match = versionNumberRegExp.match(versionMessage);
- if (match.hasMatch()) {
- int major = match.captured(1).toInt();
- int minor = match.captured(2).toInt();
- int patch = match.captured(3).toInt();
- m_versionNumber = (major << 16)|(minor<<8)|(patch);
+ DeviceProcessSignalOperation::Ptr signalOperation() const final
+ {
+ return DeviceProcessSignalOperation::Ptr(new QnxDeviceProcessSignalOperation(sharedFromThis()));
}
-}
-void QnxDevice::fromMap(const QVariantMap &map)
-{
- m_versionNumber = map.value(QLatin1String(QnxVersionKey), 0).toInt();
- LinuxDevice::fromMap(map);
-}
+ DeviceTester *createDeviceTester() const final { return new QnxDeviceTester; }
+};
-QVariantMap QnxDevice::toMap() const
+class QnxDeviceWizard : public Wizard
{
- QVariantMap map(LinuxDevice::toMap());
- map.insert(QLatin1String(QnxVersionKey), m_versionNumber);
- return map;
-}
+public:
+ QnxDeviceWizard() : Wizard(Core::ICore::dialogParent())
+ {
+ setWindowTitle(Tr::tr("New QNX Device Configuration Setup"));
-PortsGatheringMethod QnxDevice::portsGatheringMethod() const
-{
- return {
- // TODO: The command is probably needlessly complicated because the parsing method
- // used to be fixed. These two can now be matched to each other.
- [this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine {
- Q_UNUSED(protocol)
- return {filePath("netstat"), {"-na"}};
- },
-
- &Port::parseFromNetstatOutput
- };
-}
+ addPage(&m_setupPage);
+ addPage(&m_keyDeploymentPage);
+ addPage(&m_finalPage);
+ m_finalPage.setCommitPage(true);
-DeviceProcessList *QnxDevice::createProcessListModel(QObject *parent) const
-{
- return new QnxDeviceProcessList(sharedFromThis(), parent);
-}
+ m_device.reset(new QnxDevice);
-DeviceTester *QnxDevice::createDeviceTester() const
-{
- return new QnxDeviceTester;
-}
+ m_setupPage.setDevice(m_device);
+ m_keyDeploymentPage.setDevice(m_device);
+ }
-Utils::ProcessInterface *QnxDevice::createProcessInterface() const
-{
- return new QnxProcessImpl(this);
-}
+ IDevice::Ptr device() const { return m_device; }
-DeviceProcessSignalOperation::Ptr QnxDevice::signalOperation() const
-{
- return DeviceProcessSignalOperation::Ptr(new QnxDeviceProcessSignalOperation(sharedFromThis()));
-}
+private:
+ GenericLinuxDeviceConfigurationWizardSetupPage m_setupPage;
+ GenericLinuxDeviceConfigurationWizardKeyDeploymentPage m_keyDeploymentPage;
+ GenericLinuxDeviceConfigurationWizardFinalPage m_finalPage;
+
+ LinuxDevice::Ptr m_device;
+};
// Factory
@@ -176,7 +121,8 @@ QnxDeviceFactory::QnxDeviceFactory() : IDeviceFactory(Constants::QNX_QNX_OS_TYPE
setDisplayName(Tr::tr("QNX Device"));
setCombinedIcon(":/qnx/images/qnxdevicesmall.png",
":/qnx/images/qnxdevice.png");
- setConstructionFunction(&QnxDevice::create);
+ setQuickCreationAllowed(true);
+ setConstructionFunction([] { return IDevice::Ptr(new QnxDevice); });
setCreator([] {
QnxDeviceWizard wizard;
if (wizard.exec() != QDialog::Accepted)
diff --git a/src/plugins/qnx/qnxdevice.h b/src/plugins/qnx/qnxdevice.h
index f4efa3e1f2..c4b484f478 100644
--- a/src/plugins/qnx/qnxdevice.h
+++ b/src/plugins/qnx/qnxdevice.h
@@ -3,42 +3,10 @@
#pragma once
-#include <remotelinux/linuxdevice.h>
+#include <projectexplorer/devicesupport/idevicefactory.h>
namespace Qnx::Internal {
-class QnxDevice final : public RemoteLinux::LinuxDevice
-{
-public:
- using Ptr = QSharedPointer<QnxDevice>;
- using ConstPtr = QSharedPointer<const QnxDevice>;
-
- static Ptr create() { return Ptr(new QnxDevice); }
-
- ProjectExplorer::PortsGatheringMethod portsGatheringMethod() const override;
- ProjectExplorer::DeviceProcessList *createProcessListModel(QObject *parent) const override;
- ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const override;
-
- ProjectExplorer::DeviceTester *createDeviceTester() const override;
- Utils::ProcessInterface *createProcessInterface() const override;
-
- int qnxVersion() const;
-
-protected:
- void fromMap(const QVariantMap &map) final;
- QVariantMap toMap() const final;
-
- QString interruptProcessByNameCommandLine(const QString &filePath) const;
- QString killProcessByNameCommandLine(const QString &filePath) const;
-
-private:
- QnxDevice();
-
- void updateVersionNumber() const;
-
- mutable int m_versionNumber = 0;
-};
-
class QnxDeviceFactory final : public ProjectExplorer::IDeviceFactory
{
public:
diff --git a/src/plugins/qnx/qnxdeviceprocesslist.cpp b/src/plugins/qnx/qnxdeviceprocesslist.cpp
deleted file mode 100644
index 3973532993..0000000000
--- a/src/plugins/qnx/qnxdeviceprocesslist.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "qnxdeviceprocesslist.h"
-
-#include <projectexplorer/devicesupport/idevice.h>
-#include <utils/algorithm.h>
-#include <utils/fileutils.h>
-#include <utils/processinfo.h>
-
-#include <QRegularExpression>
-#include <QStringList>
-
-using namespace Utils;
-
-namespace Qnx::Internal {
-
-QnxDeviceProcessList::QnxDeviceProcessList(
- const ProjectExplorer::IDevice::ConstPtr &device, QObject *parent)
- : ProjectExplorer::SshDeviceProcessList(device, parent)
-{
-}
-
-QString QnxDeviceProcessList::listProcessesCommandLine() const
-{
- return QLatin1String("pidin -F '%a %A {/%n}'");
-}
-
-QList<ProcessInfo> QnxDeviceProcessList::buildProcessList(const QString &listProcessesReply) const
-{
- QList<ProcessInfo> processes;
- QStringList lines = listProcessesReply.split(QLatin1Char('\n'));
- if (lines.isEmpty())
- return processes;
-
- lines.pop_front(); // drop headers
- const QRegularExpression re("\\s*(\\d+)\\s+(.*){(.*)}");
-
- for (const QString &line : std::as_const(lines)) {
- const QRegularExpressionMatch match = re.match(line);
- if (match.hasMatch()) {
- const QStringList captures = match.capturedTexts();
- if (captures.size() == 4) {
- const int pid = captures[1].toInt();
- const QString args = captures[2];
- const QString exe = captures[3];
- ProcessInfo deviceProcess;
- deviceProcess.processId = pid;
- deviceProcess.executable = exe.trimmed();
- deviceProcess.commandLine = args.trimmed();
- processes.append(deviceProcess);
- }
- }
- }
-
- return Utils::sorted(std::move(processes));
-}
-
-} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxdeviceprocesslist.h b/src/plugins/qnx/qnxdeviceprocesslist.h
deleted file mode 100644
index 0e71ae7ab0..0000000000
--- a/src/plugins/qnx/qnxdeviceprocesslist.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <projectexplorer/devicesupport/sshdeviceprocesslist.h>
-
-namespace Qnx::Internal {
-
-class QnxDeviceProcessList : public ProjectExplorer::SshDeviceProcessList
-{
-public:
- explicit QnxDeviceProcessList(
- const ProjectExplorer::IDeviceConstPtr &device, QObject *parent = nullptr);
-
-private:
- QString listProcessesCommandLine() const override;
- QList<Utils::ProcessInfo> buildProcessList(const QString &listProcessesReply) const override;
-};
-
-} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxdeviceprocesssignaloperation.cpp b/src/plugins/qnx/qnxdeviceprocesssignaloperation.cpp
deleted file mode 100644
index 56df01d8a9..0000000000
--- a/src/plugins/qnx/qnxdeviceprocesssignaloperation.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "qnxdeviceprocesssignaloperation.h"
-
-namespace Qnx::Internal {
-
-QnxDeviceProcessSignalOperation::QnxDeviceProcessSignalOperation(
- const ProjectExplorer::IDeviceConstPtr &device)
- : RemoteLinux::RemoteLinuxSignalOperation(device)
-{
-}
-
-static QString signalProcessByNameQnxCommandLine(const QString &filePath, int sig)
-{
- QString executable = filePath;
- return QString::fromLatin1("for PID in $(ps -f -o pid,comm | grep %1 | awk '/%1/ {print $1}'); "
- "do "
- "kill -%2 $PID; "
- "done").arg(executable.replace(QLatin1String("/"), QLatin1String("\\/"))).arg(sig);
-}
-
-QString QnxDeviceProcessSignalOperation::killProcessByNameCommandLine(
- const QString &filePath) const
-{
- return QString::fromLatin1("%1; %2").arg(signalProcessByNameQnxCommandLine(filePath, 15),
- signalProcessByNameQnxCommandLine(filePath, 9));
-}
-
-QString QnxDeviceProcessSignalOperation::interruptProcessByNameCommandLine(
- const QString &filePath) const
-{
- return signalProcessByNameQnxCommandLine(filePath, 2);
-}
-
-} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxdeviceprocesssignaloperation.h b/src/plugins/qnx/qnxdeviceprocesssignaloperation.h
deleted file mode 100644
index 271aea6a4e..0000000000
--- a/src/plugins/qnx/qnxdeviceprocesssignaloperation.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <remotelinux/remotelinuxsignaloperation.h>
-
-namespace Qnx::Internal {
-
-class QnxDeviceProcessSignalOperation : public RemoteLinux::RemoteLinuxSignalOperation
-{
-protected:
- explicit QnxDeviceProcessSignalOperation(const ProjectExplorer::IDeviceConstPtr &device);
-
-private:
- QString killProcessByNameCommandLine(const QString &filePath) const override;
- QString interruptProcessByNameCommandLine(const QString &filePath) const override;
-
- friend class QnxDevice;
-};
-
-} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxdevicetester.cpp b/src/plugins/qnx/qnxdevicetester.cpp
index fa60bfe69b..cbead1aa3d 100644
--- a/src/plugins/qnx/qnxdevicetester.cpp
+++ b/src/plugins/qnx/qnxdevicetester.cpp
@@ -4,87 +4,66 @@
#include "qnxdevicetester.h"
#include "qnxconstants.h"
-#include "qnxdevice.h"
#include "qnxtr.h"
-#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
using namespace Utils;
namespace Qnx::Internal {
QnxDeviceTester::QnxDeviceTester(QObject *parent)
- : ProjectExplorer::DeviceTester(parent)
-{
- m_genericTester = new RemoteLinux::GenericLinuxDeviceTester(this);
- connect(m_genericTester, &DeviceTester::progressMessage,
- this, &DeviceTester::progressMessage);
- connect(m_genericTester, &DeviceTester::errorMessage,
- this, &DeviceTester::errorMessage);
- connect(m_genericTester, &DeviceTester::finished,
- this, &QnxDeviceTester::finished);
-}
+ : RemoteLinux::GenericLinuxDeviceTester(parent)
+{}
-static QStringList versionSpecificCommandsToTest(int versionNumber)
+void QnxDeviceTester::testDevice(const ProjectExplorer::IDevice::Ptr &device)
{
- if (versionNumber > 0x060500)
- return {"slog2info"};
- return {};
-}
+ static const QStringList commandsToTest {
+ "awk",
+ "cat",
+ "cut",
+ "df",
+ "grep",
+ "kill",
+ "netstat",
+ "mkdir",
+ "print",
+ "printf",
+ "pidin",
+ "read",
+ "rm",
+ "sed",
+ "sleep",
+ "tail",
+ "uname",
+ "slog2info"
+ };
-void QnxDeviceTester::testDevice(const ProjectExplorer::IDevice::Ptr &deviceConfiguration)
-{
- static const QStringList s_commandsToTest = {"awk",
- "cat",
- "cut",
- "df",
- "grep",
- "kill",
- "netstat",
- "mkdir",
- "print",
- "printf",
- "pidin",
- "read",
- "rm",
- "sed",
- "sleep",
- "tail",
- "uname"};
- m_deviceConfiguration = deviceConfiguration;
- QnxDevice::ConstPtr qnxDevice = m_deviceConfiguration.dynamicCast<const QnxDevice>();
- m_genericTester->setExtraCommandsToTest(
- s_commandsToTest + versionSpecificCommandsToTest(qnxDevice->qnxVersion()));
+ setExtraCommandsToTest(commandsToTest);
using namespace Tasking;
- auto setupHandler = [this](QtcProcess &process) {
+ auto setupHandler = [device, this](Process &process) {
emit progressMessage(Tr::tr("Checking that files can be created in %1...")
.arg(Constants::QNX_TMP_DIR));
const QString pidFile = QString("%1/qtc_xxxx.pid").arg(Constants::QNX_TMP_DIR);
- const CommandLine cmd(m_deviceConfiguration->filePath("/bin/sh"),
+ const CommandLine cmd(device->filePath("/bin/sh"),
{"-c", QLatin1String("rm %1 > /dev/null 2>&1; echo ABC > %1 && rm %1").arg(pidFile)});
process.setCommand(cmd);
};
- auto doneHandler = [this](const QtcProcess &) {
+ auto doneHandler = [this](const Process &) {
emit progressMessage(Tr::tr("Files can be created in /var/run.") + '\n');
};
- auto errorHandler = [this](const QtcProcess &process) {
+ auto errorHandler = [this](const Process &process) {
const QString message = process.result() == ProcessResult::StartFailed
? Tr::tr("An error occurred while checking that files can be created in %1.")
.arg(Constants::QNX_TMP_DIR) + '\n' + process.errorString()
: Tr::tr("Files cannot be created in %1.").arg(Constants::QNX_TMP_DIR);
emit errorMessage(message + '\n');
};
- m_genericTester->setExtraTests({Process(setupHandler, doneHandler, errorHandler)});
-
- m_genericTester->testDevice(deviceConfiguration);
-}
+ setExtraTests({ProcessTask(setupHandler, doneHandler, errorHandler)});
-void QnxDeviceTester::stopTest()
-{
- m_genericTester->stopTest();
+ RemoteLinux::GenericLinuxDeviceTester::testDevice(device);
}
} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxdevicetester.h b/src/plugins/qnx/qnxdevicetester.h
index 96f31f3841..f8fc4be882 100644
--- a/src/plugins/qnx/qnxdevicetester.h
+++ b/src/plugins/qnx/qnxdevicetester.h
@@ -5,23 +5,14 @@
#include <remotelinux/linuxdevicetester.h>
-namespace Qnx {
-namespace Internal {
+namespace Qnx::Internal {
-class QnxDeviceTester : public ProjectExplorer::DeviceTester
+class QnxDeviceTester : public RemoteLinux::GenericLinuxDeviceTester
{
- Q_OBJECT
-
public:
explicit QnxDeviceTester(QObject *parent = nullptr);
- void testDevice(const ProjectExplorer::IDevice::Ptr &deviceConfiguration) override;
- void stopTest() override;
-
-private:
- RemoteLinux::GenericLinuxDeviceTester *m_genericTester = nullptr;
- ProjectExplorer::IDevice::ConstPtr m_deviceConfiguration;
+ void testDevice(const ProjectExplorer::IDevice::Ptr &device) override;
};
-} // namespace Internal
-} // namespace Qnx
+} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxdevicewizard.cpp b/src/plugins/qnx/qnxdevicewizard.cpp
deleted file mode 100644
index 1e7d1cfcdd..0000000000
--- a/src/plugins/qnx/qnxdevicewizard.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "qnxdevicewizard.h"
-
-#include "qnxconstants.h"
-#include "qnxtr.h"
-
-#include <projectexplorer/devicesupport/sshparameters.h>
-#include <remotelinux/genericlinuxdeviceconfigurationwizardpages.h>
-#include <utils/portlist.h>
-
-using namespace ProjectExplorer;
-
-namespace Qnx::Internal {
-
-QnxDeviceWizard::QnxDeviceWizard(QWidget *parent) :
- Utils::Wizard(parent)
-{
- setWindowTitle(Tr::tr("New QNX Device Configuration Setup"));
-
- m_setupPage = new RemoteLinux::GenericLinuxDeviceConfigurationWizardSetupPage(this);
- m_keyDeploymentPage
- = new RemoteLinux::GenericLinuxDeviceConfigurationWizardKeyDeploymentPage(this);
- m_finalPage = new RemoteLinux::GenericLinuxDeviceConfigurationWizardFinalPage(this);
-
- setPage(SetupPageId, m_setupPage);
- setPage(KeyDeploymenPageId, m_keyDeploymentPage);
- setPage(FinalPageId, m_finalPage);
- m_finalPage->setCommitPage(true);
- SshParameters sshParams;
- sshParams.timeout = 10;
- m_device = QnxDevice::create();
- m_device->setupId(IDevice::ManuallyAdded);
- m_device->setType(Constants::QNX_QNX_OS_TYPE);
- m_device->setMachineType(IDevice::Hardware);
- m_device->setSshParameters(sshParams);
- m_device->setFreePorts(Utils::PortList::fromString(QLatin1String("10000-10100")));
- m_setupPage->setDevice(m_device);
- m_keyDeploymentPage->setDevice(m_device);
-}
-
-IDevice::Ptr QnxDeviceWizard::device()
-{
- return m_device;
-}
-
-} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxdevicewizard.h b/src/plugins/qnx/qnxdevicewizard.h
deleted file mode 100644
index ce30658ce1..0000000000
--- a/src/plugins/qnx/qnxdevicewizard.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "qnxdevice.h"
-
-#include <utils/wizard.h>
-
-namespace RemoteLinux {
-class GenericLinuxDeviceConfigurationWizardSetupPage;
-class GenericLinuxDeviceConfigurationWizardKeyDeploymentPage;
-class GenericLinuxDeviceConfigurationWizardFinalPage;
-}
-
-namespace Qnx::Internal {
-
-class QnxDeviceWizard : public Utils::Wizard
-{
-public:
- explicit QnxDeviceWizard(QWidget *parent = nullptr);
-
- ProjectExplorer::IDevice::Ptr device();
-
-private:
- enum PageId {
- SetupPageId,
- KeyDeploymenPageId,
- FinalPageId
- };
-
- RemoteLinux::GenericLinuxDeviceConfigurationWizardSetupPage *m_setupPage;
- RemoteLinux::GenericLinuxDeviceConfigurationWizardKeyDeploymentPage *m_keyDeploymentPage;
- RemoteLinux::GenericLinuxDeviceConfigurationWizardFinalPage *m_finalPage;
- QnxDevice::Ptr m_device;
-};
-
-} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxplugin.cpp b/src/plugins/qnx/qnxplugin.cpp
index c6664b9f60..09fd528086 100644
--- a/src/plugins/qnx/qnxplugin.cpp
+++ b/src/plugins/qnx/qnxplugin.cpp
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qnxanalyzesupport.h"
-#include "qnxconfigurationmanager.h"
#include "qnxconstants.h"
#include "qnxdebugsupport.h"
#include "qnxdevice.h"
@@ -33,8 +32,6 @@
#include <projectexplorer/target.h>
#include <projectexplorer/toolchain.h>
-#include <remotelinux/genericdirectuploadstep.h>
-#include <remotelinux/makeinstallstep.h>
#include <remotelinux/remotelinux_constants.h>
#include <QAction>
@@ -43,21 +40,12 @@ using namespace ProjectExplorer;
namespace Qnx::Internal {
-class QnxUploadStep : public RemoteLinux::GenericDirectUploadStep
+class QnxDeployStepFactory : public BuildStepFactory
{
public:
- QnxUploadStep(BuildStepList *bsl, Utils::Id id) : GenericDirectUploadStep(bsl, id, false) {}
- static Utils::Id stepId() { return "Qnx.DirectUploadStep"; }
-};
-
-template <class Step>
-class GenericQnxDeployStepFactory : public BuildStepFactory
-{
-public:
- GenericQnxDeployStepFactory()
+ QnxDeployStepFactory(Utils::Id existingStepId, Utils::Id overrideId = {})
{
- registerStep<Step>(Step::stepId());
- setDisplayName(Step::displayName());
+ cloneStepCreator(existingStepId, overrideId);
setSupportedConfiguration(Constants::QNX_QNX_DEPLOYCONFIGURATION_ID);
setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY);
}
@@ -78,8 +66,8 @@ public:
return prj->deploymentKnowledge() == DeploymentKnowledge::Bad
&& prj->hasMakeInstallEquivalent();
});
- addInitialStep(DeviceCheckBuildStep::stepId());
- addInitialStep(QnxUploadStep::stepId());
+ addInitialStep(ProjectExplorer::Constants::DEVICE_CHECK_STEP);
+ addInitialStep(Constants::QNX_DIRECT_UPLOAD_STEP_ID);
}
};
@@ -91,15 +79,14 @@ public:
QAction *m_debugSeparator = nullptr;
QAction m_attachToQnxApplication{Tr::tr("Attach to remote QNX application..."), nullptr};
- QnxConfigurationManager configurationManager;
+ QnxSettingsPage settingsPage;
QnxQtVersionFactory qtVersionFactory;
QnxDeviceFactory deviceFactory;
QnxDeployConfigurationFactory deployConfigFactory;
- GenericQnxDeployStepFactory<QnxUploadStep> directUploadDeployFactory;
- GenericQnxDeployStepFactory<RemoteLinux::MakeInstallStep> makeInstallDeployFactory;
- GenericQnxDeployStepFactory<DeviceCheckBuildStep> checkBuildDeployFactory;
+ QnxDeployStepFactory directUploadDeployFactory{RemoteLinux::Constants::DirectUploadStepId,
+ Constants::QNX_DIRECT_UPLOAD_STEP_ID};
+ QnxDeployStepFactory makeInstallStepFactory{RemoteLinux::Constants::MakeInstallStepId};
QnxRunConfigurationFactory runConfigFactory;
- QnxSettingsPage settingsPage;
QnxToolChainFactory toolChainFactory;
SimpleTargetRunnerFactory runWorkerFactory{{runConfigFactory.runConfigurationId()}};
QnxDebugWorkerFactory debugWorkerFactory;
@@ -123,10 +110,6 @@ private:
void QnxPlugin::extensionsInitialized()
{
- // Can't do yet as not all devices are around.
- connect(DeviceManager::instance(), &DeviceManager::devicesLoaded,
- &d->configurationManager, &QnxConfigurationManager::restoreConfigurations);
-
// Attach support
connect(&d->m_attachToQnxApplication, &QAction::triggered, this, &showAttachToProcessDialog);
diff --git a/src/plugins/qnx/qnxsettingspage.cpp b/src/plugins/qnx/qnxsettingspage.cpp
index 0d35403773..992ba9b87d 100644
--- a/src/plugins/qnx/qnxsettingspage.cpp
+++ b/src/plugins/qnx/qnxsettingspage.cpp
@@ -3,29 +3,512 @@
#include "qnxsettingspage.h"
-#include "qnxconfiguration.h"
-#include "qnxconfigurationmanager.h"
+#include "qnxqtversion.h"
+#include "qnxtoolchain.h"
#include "qnxtr.h"
+#include "qnxutils.h"
+#include "qnxversionnumber.h"
#include <coreplugin/icore.h>
+#include <debugger/debuggeritem.h>
+#include <debugger/debuggeritemmanager.h>
+#include <debugger/debuggerkitinformation.h>
+
+#include <projectexplorer/devicesupport/devicemanager.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/toolchainmanager.h>
+#include <projectexplorer/toolchain.h>
+#include <projectexplorer/kit.h>
+#include <projectexplorer/kitmanager.h>
+#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtversionmanager.h>
+#include <qtsupport/qtkitinformation.h>
+
+#include <qmakeprojectmanager/qmakeprojectmanagerconstants.h>
+#include <utils/algorithm.h>
#include <utils/layoutbuilder.h>
+#include <utils/persistentsettings.h>
+#include <utils/qtcassert.h>
#include <QCheckBox>
#include <QComboBox>
+#include <QDebug>
+#include <QDomDocument>
+#include <QGridLayout>
#include <QGroupBox>
#include <QLabel>
#include <QMessageBox>
#include <QPushButton>
+using namespace ProjectExplorer;
+using namespace QtSupport;
using namespace Utils;
+using namespace Debugger;
namespace Qnx::Internal {
+const QLatin1String QNXEnvFileKey("EnvFile");
+const QLatin1String QNXVersionKey("QNXVersion");
+// For backward compatibility
+const QLatin1String SdpEnvFileKey("NDKEnvFile");
+
+const QLatin1String QNXConfiguration("QNX_CONFIGURATION");
+const QLatin1String QNXTarget("QNX_TARGET");
+const QLatin1String QNXHost("QNX_HOST");
+
+const QLatin1String QNXConfigDataKey("QNXConfiguration.");
+const QLatin1String QNXConfigCountKey("QNXConfiguration.Count");
+const QLatin1String QNXConfigsFileVersionKey("Version");
+
+static FilePath qnxConfigSettingsFileName()
+{
+ return Core::ICore::userResourcePath("qnx/qnxconfigurations.xml");
+}
+
+class QnxConfiguration
+{
+public:
+ QnxConfiguration() = default;
+ explicit QnxConfiguration(const FilePath &envFile) { m_envFile = envFile; }
+
+ void fromMap(const QVariantMap &data)
+ {
+ QString envFilePath = data.value(QNXEnvFileKey).toString();
+ if (envFilePath.isEmpty())
+ envFilePath = data.value(SdpEnvFileKey).toString();
+
+ m_version = QnxVersionNumber(data.value(QNXVersionKey).toString());
+ m_envFile = FilePath::fromString(envFilePath);
+ }
+
+ QVariantMap toMap() const
+ {
+ QVariantMap data;
+ data.insert(QLatin1String(QNXEnvFileKey), m_envFile.toString());
+ data.insert(QLatin1String(QNXVersionKey), m_version.toString());
+ return data;
+ }
+
+ bool isValid() const
+ {
+ return !m_qccCompiler.isEmpty() && !m_targets.isEmpty();
+ }
+
+ bool isActive() const
+ {
+ const bool hasToolChain = ToolChainManager::toolChain(Utils::equal(&ToolChain::compilerCommand,
+ m_qccCompiler));
+ const bool hasDebugger = Utils::contains(DebuggerItemManager::debuggers(), [this](const DebuggerItem &di) {
+ return findTargetByDebuggerPath(di.command());
+ });
+ return hasToolChain && hasDebugger;
+ }
+
+ void activate();
+ void deactivate();
+
+ void ensureContents() const;
+ void mutableEnsureContents();
+
+ QString architectureNames() const
+ {
+ return transform(m_targets, &QnxTarget::shortDescription).join(", ");
+ }
+
+ EnvironmentItems qnxEnvironmentItems() const;
+
+ QnxQtVersion *qnxQtVersion(const QnxTarget &target) const;
+
+ void createKit(const QnxTarget &target);
+ QVariant createDebugger(const QnxTarget &target);
+ Toolchains createToolChains(const QnxTarget &target);
+
+ const QnxTarget *findTargetByDebuggerPath(const Utils::FilePath &path) const;
+
+ bool m_hasContents = false;
+ QString m_configName;
+
+ FilePath m_envFile;
+ FilePath m_qnxConfiguration;
+ FilePath m_qnxTarget;
+ FilePath m_qnxHost;
+ FilePath m_qccCompiler;
+ EnvironmentItems m_qnxEnv;
+ QnxVersionNumber m_version;
+
+ QList<QnxTarget> m_targets;
+};
+
+void QnxConfiguration::activate()
+{
+ ensureContents();
+
+ if (!isValid()) {
+ QStringList errorStrings
+ = {Tr::tr("The following errors occurred while activating the QNX configuration:")};
+ if (m_qccCompiler.isEmpty())
+ errorStrings << Tr::tr("- No GCC compiler found.");
+ if (m_targets.isEmpty())
+ errorStrings << Tr::tr("- No targets found.");
+ const QString msg = errorStrings.join('\n');
+
+ QMessageBox::warning(Core::ICore::dialogParent(), Tr::tr("Cannot Set Up QNX Configuration"),
+ msg, QMessageBox::Ok);
+ return;
+ }
+
+ for (const QnxTarget &target : std::as_const(m_targets))
+ createKit(target);
+}
+
+void QnxConfiguration::deactivate()
+{
+ QTC_ASSERT(isActive(), return);
+
+ const Toolchains toolChainsToRemove =
+ ToolChainManager::toolchains(Utils::equal(&ToolChain::compilerCommand, m_qccCompiler));
+
+ QList<DebuggerItem> debuggersToRemove;
+ const QList<DebuggerItem> debuggerItems = DebuggerItemManager::debuggers();
+ for (const DebuggerItem &debuggerItem : debuggerItems) {
+ if (findTargetByDebuggerPath(debuggerItem.command()))
+ debuggersToRemove.append(debuggerItem);
+ }
+
+ const QList<Kit *> kits = KitManager::kits();
+ for (Kit *kit : kits) {
+ if (kit->isAutoDetected()
+ && DeviceTypeKitAspect::deviceTypeId(kit) == Constants::QNX_QNX_OS_TYPE
+ && toolChainsToRemove.contains(ToolChainKitAspect::cxxToolChain(kit))) {
+ KitManager::deregisterKit(kit);
+ }
+ }
+
+ for (ToolChain *tc : toolChainsToRemove)
+ ToolChainManager::deregisterToolChain(tc);
+
+ for (const DebuggerItem &debuggerItem : std::as_const(debuggersToRemove))
+ DebuggerItemManager::deregisterDebugger(debuggerItem.id());
+}
+
+QnxQtVersion *QnxConfiguration::qnxQtVersion(const QnxTarget &target) const
+{
+ const QtVersions versions = QtVersionManager::versions(
+ Utils::equal(&QtVersion::type, QString::fromLatin1(Constants::QNX_QNX_QT)));
+ for (QtVersion *version : versions) {
+ auto qnxQt = dynamic_cast<QnxQtVersion *>(version);
+ if (qnxQt && qnxQt->sdpPath() == m_envFile.parentDir()) {
+ const Abis abis = version->qtAbis();
+ for (const Abi &qtAbi : abis) {
+ if (qtAbi == target.m_abi && qnxQt->cpuDir() == target.cpuDir())
+ return qnxQt;
+ }
+ }
+ }
+ return nullptr;
+}
+
+QVariant QnxConfiguration::createDebugger(const QnxTarget &target)
+{
+ Environment sysEnv = m_qnxHost.deviceEnvironment();
+ sysEnv.modify(qnxEnvironmentItems());
+
+ Debugger::DebuggerItem debugger;
+ debugger.setCommand(target.m_debuggerPath);
+ debugger.reinitializeFromFile(nullptr, &sysEnv);
+ debugger.setUnexpandedDisplayName(Tr::tr("Debugger for %1 (%2)")
+ .arg(m_configName)
+ .arg(target.shortDescription()));
+ return Debugger::DebuggerItemManager::registerDebugger(debugger);
+}
+
+Toolchains QnxConfiguration::createToolChains(const QnxTarget &target)
+{
+ Toolchains toolChains;
+
+ for (const Id language : {ProjectExplorer::Constants::C_LANGUAGE_ID,
+ ProjectExplorer::Constants::CXX_LANGUAGE_ID}) {
+ auto toolChain = new QnxToolChain;
+ toolChain->setDetection(ToolChain::ManualDetection);
+ toolChain->setLanguage(language);
+ toolChain->setTargetAbi(target.m_abi);
+ toolChain->setDisplayName(Tr::tr("QCC for %1 (%2)")
+ .arg(m_configName)
+ .arg(target.shortDescription()));
+ toolChain->setSdpPath(m_envFile.parentDir());
+ toolChain->setCpuDir(target.cpuDir());
+ toolChain->resetToolChain(m_qccCompiler);
+ ToolChainManager::registerToolChain(toolChain);
+
+ toolChains.append(toolChain);
+ }
+
+ return toolChains;
+}
+
+void QnxConfiguration::createKit(const QnxTarget &target)
+{
+ Toolchains toolChains = createToolChains(target);
+ QVariant debugger = createDebugger(target);
+
+ QnxQtVersion *qnxQt = qnxQtVersion(target); // nullptr is ok.
+
+ const auto init = [&](Kit *k) {
+ QtKitAspect::setQtVersion(k, qnxQt);
+ ToolChainKitAspect::setToolChain(k, toolChains[0]);
+ ToolChainKitAspect::setToolChain(k, toolChains[1]);
+
+ if (debugger.isValid())
+ DebuggerKitAspect::setDebugger(k, debugger);
+
+ DeviceTypeKitAspect::setDeviceTypeId(k, Constants::QNX_QNX_OS_TYPE);
+ // TODO: Add sysroot?
+
+ k->setUnexpandedDisplayName(Tr::tr("Kit for %1 (%2)")
+ .arg(m_configName)
+ .arg(target.shortDescription()));
+
+ k->setAutoDetected(false);
+ k->setAutoDetectionSource(m_envFile.toString());
+ k->setMutable(DeviceKitAspect::id(), true);
+
+ k->setSticky(ToolChainKitAspect::id(), true);
+ k->setSticky(DeviceTypeKitAspect::id(), true);
+ k->setSticky(SysRootKitAspect::id(), true);
+ k->setSticky(DebuggerKitAspect::id(), true);
+ k->setSticky(QmakeProjectManager::Constants::KIT_INFORMATION_ID, true);
+
+ EnvironmentKitAspect::setEnvironmentChanges(k, qnxEnvironmentItems());
+ };
+
+ // add kit with device and qt version not sticky
+ KitManager::registerKit(init);
+}
+
+void QnxConfiguration::ensureContents() const
+{
+ if (!m_hasContents)
+ const_cast<QnxConfiguration *>(this)->mutableEnsureContents();
+}
+
+void QnxConfiguration::mutableEnsureContents()
+{
+ QTC_ASSERT(!m_envFile.isEmpty(), return);
+ m_hasContents = true;
+
+ m_qnxEnv = QnxUtils::qnxEnvironmentFromEnvFile(m_envFile);
+ for (const EnvironmentItem &item : std::as_const(m_qnxEnv)) {
+ if (item.name == QNXConfiguration)
+ m_qnxConfiguration = m_envFile.withNewPath(item.value).canonicalPath();
+ else if (item.name == QNXTarget)
+ m_qnxTarget = m_envFile.withNewPath(item.value).canonicalPath();
+ else if (item.name == QNXHost)
+ m_qnxHost = m_envFile.withNewPath(item.value).canonicalPath();
+ }
+
+ const FilePath qccPath = m_qnxHost.pathAppended("usr/bin/qcc").withExecutableSuffix();
+ if (qccPath.exists())
+ m_qccCompiler = qccPath;
+
+ // Some fallback in case the qconfig dir with .xml files is not found later.
+ if (m_configName.isEmpty())
+ m_configName = QString("%1 - %2").arg(m_qnxHost.fileName(), m_qnxTarget.fileName());
+
+ m_targets = QnxUtils::findTargets(m_qnxTarget);
+
+ // Assign debuggers.
+ const FilePath hostUsrBinDir = m_qnxHost.pathAppended("usr/bin");
+ QString pattern = "nto*-gdb";
+ if (m_qnxHost.osType() == Utils::OsTypeWindows)
+ pattern += ".exe";
+
+ const FilePaths debuggerNames = hostUsrBinDir.dirEntries({{pattern}, QDir::Files});
+ Environment sysEnv = m_qnxHost.deviceEnvironment();
+ sysEnv.modify(qnxEnvironmentItems());
+
+ for (const FilePath &debuggerPath : debuggerNames) {
+ DebuggerItem item;
+ item.setCommand(debuggerPath);
+ item.reinitializeFromFile(nullptr, &sysEnv);
+ bool found = false;
+ for (const Abi &abi : item.abis()) {
+ for (QnxTarget &target : m_targets) {
+ if (target.m_abi.isCompatibleWith(abi)) {
+ found = true;
+
+ if (target.m_debuggerPath.isEmpty()) {
+ target.m_debuggerPath = debuggerPath;
+ } else {
+ qWarning() << debuggerPath << "has the same ABI as" << target.m_debuggerPath
+ << "... discarded";
+ break;
+ }
+ }
+ }
+ }
+ if (!found)
+ qWarning() << "No target found for" << debuggerPath.toUserOutput() << "... discarded";
+ }
+
+ // Remove debuggerless targets.
+ Utils::erase(m_targets, [](const QnxTarget &target) {
+ if (target.m_debuggerPath.isEmpty())
+ qWarning() << "No debugger found for" << target.m_path << "... discarded";
+ return target.m_debuggerPath.isEmpty();
+ });
+
+ const FilePath configPath = m_qnxConfiguration / "qconfig";
+ if (!configPath.isDir())
+ return;
+
+ configPath.iterateDirectory([this, configPath](const FilePath &sdpFile) {
+ QFile xmlFile(sdpFile.toFSPathString());
+ if (!xmlFile.open(QIODevice::ReadOnly))
+ return IterationPolicy::Continue;
+
+ QDomDocument doc;
+ if (!doc.setContent(&xmlFile)) // Skip error message
+ return IterationPolicy::Continue;
+
+ QDomElement docElt = doc.documentElement();
+ if (docElt.tagName() != QLatin1String("qnxSystemDefinition"))
+ return IterationPolicy::Continue;
+
+ QDomElement childElt = docElt.firstChildElement(QLatin1String("installation"));
+ // The file contains only one installation node
+ if (childElt.isNull()) // The file contains only one base node
+ return IterationPolicy::Continue;
+
+ FilePath host = configPath.withNewPath(
+ childElt.firstChildElement(QLatin1String("host")).text()).canonicalPath();
+ if (m_qnxHost != host)
+ return IterationPolicy::Continue;
+
+ FilePath target = configPath.withNewPath(
+ childElt.firstChildElement(QLatin1String("target")).text()).canonicalPath();
+ if (m_qnxTarget != target)
+ return IterationPolicy::Continue;
+
+ m_configName = childElt.firstChildElement(QLatin1String("name")).text();
+ QString version = childElt.firstChildElement(QLatin1String("version")).text();
+ m_version = QnxVersionNumber(version);
+ return IterationPolicy::Stop;
+ }, {{"*.xml"}, QDir::Files});
+}
+
+EnvironmentItems QnxConfiguration::qnxEnvironmentItems() const
+{
+ ensureContents();
+ return {
+ {QNXConfiguration, m_qnxConfiguration.path()},
+ {QNXTarget, m_qnxTarget.path()},
+ {QNXHost, m_qnxHost.path()}
+ };
+}
+
+const QnxTarget *QnxConfiguration::findTargetByDebuggerPath(
+ const FilePath &path) const
+{
+ const auto it = std::find_if(m_targets.begin(), m_targets.end(),
+ [path](const QnxTarget &target) { return target.m_debuggerPath == path; });
+ return it == m_targets.end() ? nullptr : &(*it);
+}
+
+
+// QnxSettingsPagePrivate
+
+class QnxSettingsPagePrivate : public QObject
+{
+public:
+ QnxSettingsPagePrivate()
+ {
+ connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested,
+ this, &QnxSettingsPagePrivate::saveConfigs);
+ // Can't do yet as not all devices are around.
+ connect(DeviceManager::instance(), &DeviceManager::devicesLoaded,
+ this, &QnxSettingsPagePrivate::restoreConfigurations);
+ }
+
+ void saveConfigs()
+ {
+ QVariantMap data;
+ data.insert(QLatin1String(QNXConfigsFileVersionKey), 1);
+ int count = 0;
+ for (const QnxConfiguration &config : std::as_const(m_configurations)) {
+ QVariantMap tmp = config.toMap();
+ if (tmp.isEmpty())
+ continue;
+
+ data.insert(QNXConfigDataKey + QString::number(count), tmp);
+ ++count;
+ }
+
+ data.insert(QLatin1String(QNXConfigCountKey), count);
+ m_writer.save(data, Core::ICore::dialogParent());
+ }
+
+ void restoreConfigurations()
+ {
+ PersistentSettingsReader reader;
+ if (!reader.load(qnxConfigSettingsFileName()))
+ return;
+
+ QVariantMap data = reader.restoreValues();
+ int count = data.value(QNXConfigCountKey, 0).toInt();
+ for (int i = 0; i < count; ++i) {
+ const QString key = QNXConfigDataKey + QString::number(i);
+ if (!data.contains(key))
+ continue;
+
+ QnxConfiguration config;
+ config.fromMap(data.value(key).toMap());
+ m_configurations[config.m_envFile] = config;
+ }
+ }
+
+ QnxConfiguration *configurationFromEnvFile(const FilePath &envFile)
+ {
+ auto it = m_configurations.find(envFile);
+ return it == m_configurations.end() ? nullptr : &*it;
+ }
+
+ QHash<FilePath, QnxConfiguration> m_configurations;
+ PersistentSettingsWriter m_writer{qnxConfigSettingsFileName(), "QnxConfigurations"};
+};
+
+static QnxSettingsPagePrivate *dd = nullptr;
+
+
+// QnxSettingsWidget
+
+class ArchitecturesList final : public QWidget
+{
+public:
+ void setConfiguration(const FilePath &envFile)
+ {
+ m_envFile = envFile;
+ delete layout();
+
+ QnxConfiguration *config = dd->configurationFromEnvFile(envFile);
+ if (!config)
+ return;
+
+ auto l = new QHBoxLayout(this);
+ for (const QnxTarget &target : config->m_targets) {
+ auto button = new QPushButton(tr("Create Kit for %1").arg(target.cpuDir()));
+ connect(button, &QPushButton::clicked, this, [config, target] {
+ config->createKit(target);
+ });
+ l->addWidget(button);
+ }
+ }
+
+ FilePath m_envFile;
+};
+
class QnxSettingsWidget final : public Core::IOptionsPageWidget
{
public:
@@ -42,10 +525,10 @@ public:
public:
bool operator ==(const ConfigState &cs) const
{
- return config == cs.config && state == cs.state;
+ return envFile == cs.envFile && state == cs.state;
}
- QnxConfiguration *config;
+ FilePath envFile;
State state;
};
@@ -53,73 +536,69 @@ public:
void addConfiguration();
void removeConfiguration();
- void generateKits(bool checked);
void updateInformation();
void populateConfigsCombo();
- void setConfigState(QnxConfiguration *config, State state);
+ void setConfigState(const FilePath &envFile, State state);
private:
QComboBox *m_configsCombo = new QComboBox;
- QCheckBox *m_generateKitsCheckBox = new QCheckBox(Tr::tr("Generate kits"));
QLabel *m_configName = new QLabel;
QLabel *m_configVersion = new QLabel;
QLabel *m_configHost = new QLabel;
QLabel *m_configTarget = new QLabel;
+ QLabel *m_compiler = new QLabel;
+ QLabel *m_architectures = new QLabel;
+
+ ArchitecturesList *m_kitCreation = new ArchitecturesList;
- QnxConfigurationManager *m_qnxConfigManager = QnxConfigurationManager::instance();
QList<ConfigState> m_changedConfigs;
};
QnxSettingsWidget::QnxSettingsWidget()
{
- auto addButton = new QPushButton(Tr::tr("Add..."));
- auto removeButton = new QPushButton(Tr::tr("Remove"));
-
using namespace Layouting;
Row {
Column {
m_configsCombo,
- Row { m_generateKitsCheckBox, st },
Group {
title(Tr::tr("Configuration Information:")),
Form {
Tr::tr("Name:"), m_configName, br,
Tr::tr("Version:"), m_configVersion, br,
Tr::tr("Host:"), m_configHost, br,
- Tr::tr("Target:"), m_configTarget
+ Tr::tr("Target:"), m_configTarget, br,
+ Tr::tr("Compiler:"), m_compiler, br,
+ Tr::tr("Architectures:"), m_architectures
}
},
+ Row { m_kitCreation, st },
st
},
Column {
- addButton,
- removeButton,
+ PushButton {
+ text(Tr::tr("Add...")),
+ onClicked([this] { addConfiguration(); }, this)
+ },
+ PushButton {
+ text(Tr::tr("Remove")),
+ onClicked([this] { removeConfiguration(); }, this)
+ },
st
}
}.attachTo(this);
populateConfigsCombo();
- connect(addButton, &QAbstractButton::clicked,
- this, &QnxSettingsWidget::addConfiguration);
- connect(removeButton, &QAbstractButton::clicked,
- this, &QnxSettingsWidget::removeConfiguration);
+
connect(m_configsCombo, &QComboBox::currentIndexChanged,
this, &QnxSettingsWidget::updateInformation);
- connect(m_generateKitsCheckBox, &QAbstractButton::toggled,
- this, &QnxSettingsWidget::generateKits);
- connect(m_qnxConfigManager, &QnxConfigurationManager::configurationsListUpdated,
- this, &QnxSettingsWidget::populateConfigsCombo);
- connect(QtSupport::QtVersionManager::instance(),
- &QtSupport::QtVersionManager::qtVersionsChanged,
- this, &QnxSettingsWidget::updateInformation);
}
void QnxSettingsWidget::addConfiguration()
{
QString filter;
- if (Utils::HostOsInfo::isWindowsHost())
+ if (HostOsInfo::isWindowsHost())
filter = "*.bat file";
else
filter = "*.sh file";
@@ -129,82 +608,85 @@ void QnxSettingsWidget::addConfiguration()
if (envFile.isEmpty())
return;
- QnxConfiguration *config = new QnxConfiguration(envFile);
- if (m_qnxConfigManager->configurations().contains(config) || !config->isValid()) {
+ if (dd->m_configurations.contains(envFile)) {
QMessageBox::warning(Core::ICore::dialogParent(),
Tr::tr("Warning"),
- Tr::tr("Configuration already exists or is invalid."));
- delete config;
+ Tr::tr("Configuration already exists."));
return;
}
- setConfigState(config, Added);
- m_configsCombo->addItem(config->displayName(),
- QVariant::fromValue(static_cast<void*>(config)));
+ // Temporary to be able to check
+ QnxConfiguration config(envFile);
+ config.ensureContents();
+ if (!config.isValid()) {
+ QMessageBox::warning(Core::ICore::dialogParent(),
+ Tr::tr("Warning"),
+ Tr::tr("Configuration is not valid."));
+ return;
+ }
+
+ setConfigState(envFile, Added);
+ m_configsCombo->addItem(config.m_configName, QVariant::fromValue(envFile));
}
void QnxSettingsWidget::removeConfiguration()
{
- const int currentIndex = m_configsCombo->currentIndex();
- auto config = static_cast<QnxConfiguration*>(
- m_configsCombo->itemData(currentIndex).value<void*>());
+ const FilePath envFile = m_configsCombo->currentData().value<FilePath>();
+ QTC_ASSERT(!envFile.isEmpty(), return);
- if (!config)
- return;
+ QnxConfiguration *config = dd->configurationFromEnvFile(envFile);
+ QTC_ASSERT(config, return);
+
+ config->ensureContents();
QMessageBox::StandardButton button =
QMessageBox::question(Core::ICore::dialogParent(),
Tr::tr("Remove QNX Configuration"),
- Tr::tr("Are you sure you want to remove:\n %1?").arg(config->displayName()),
+ Tr::tr("Are you sure you want to remove:\n %1?")
+ .arg(config->m_configName),
QMessageBox::Yes | QMessageBox::No);
if (button == QMessageBox::Yes) {
- setConfigState(config, Removed);
- m_configsCombo->removeItem(currentIndex);
+ setConfigState(envFile, Removed);
+ m_configsCombo->removeItem(m_configsCombo->currentIndex());
+ updateInformation();
}
}
-void QnxSettingsWidget::generateKits(bool checked)
-{
- const int currentIndex = m_configsCombo->currentIndex();
- auto config = static_cast<QnxConfiguration*>(
- m_configsCombo->itemData(currentIndex).value<void*>());
- if (!config)
- return;
-
- setConfigState(config, checked ? Activated : Deactivated);
-}
-
void QnxSettingsWidget::updateInformation()
{
- const int currentIndex = m_configsCombo->currentIndex();
-
- auto config = static_cast<QnxConfiguration*>(
- m_configsCombo->itemData(currentIndex).value<void*>());
-
- // update the checkbox
- m_generateKitsCheckBox->setEnabled(config ? config->isValid() : false);
- m_generateKitsCheckBox->setChecked(config ? config->isActive() : false);
-
- // update information
- m_configName->setText(config ? config->displayName() : QString());
- m_configVersion->setText(config ? config->version().toString() : QString());
- m_configHost->setText(config ? config->qnxHost().toString() : QString());
- m_configTarget->setText(config ? config->qnxTarget().toString() : QString());
+ const FilePath envFile = m_configsCombo->currentData().value<FilePath>();
+
+ if (QnxConfiguration *config = dd->configurationFromEnvFile(envFile)) {
+ config->ensureContents();
+ m_configName->setText(config->m_configName);
+ m_configVersion->setText(config->m_version.toString());
+ m_configHost->setText(config->m_qnxHost.toString());
+ m_configTarget->setText(config->m_qnxTarget.toString());
+ m_compiler->setText(config->m_qccCompiler.toUserOutput());
+ m_architectures->setText(config->architectureNames());
+ m_kitCreation->setConfiguration(envFile);
+ } else {
+ m_configName->setText({});
+ m_configVersion->setText({});
+ m_configHost->setText({});
+ m_compiler->setText({});
+ m_architectures->setText({});
+ m_kitCreation->setConfiguration({});
+ }
}
void QnxSettingsWidget::populateConfigsCombo()
{
m_configsCombo->clear();
- const QList<QnxConfiguration *> configList = m_qnxConfigManager->configurations();
- for (QnxConfiguration *config : configList) {
- m_configsCombo->addItem(config->displayName(),
- QVariant::fromValue(static_cast<void*>(config)));
+ for (const QnxConfiguration &config : std::as_const(dd->m_configurations)) {
+ config.ensureContents();
+ m_configsCombo->addItem(config.m_configName, QVariant::fromValue(config.m_envFile));
}
updateInformation();
}
-void QnxSettingsWidget::setConfigState(QnxConfiguration *config, State state)
+void QnxSettingsWidget::setConfigState(const FilePath &envFile, State state)
{
State stateToRemove = Activated;
switch (state) {
@@ -223,45 +705,79 @@ void QnxSettingsWidget::setConfigState(QnxConfiguration *config, State state)
}
for (const ConfigState &configState : std::as_const(m_changedConfigs)) {
- if (configState.config == config && configState.state == stateToRemove)
+ if (configState.envFile == envFile && configState.state == stateToRemove)
m_changedConfigs.removeAll(configState);
}
- m_changedConfigs.append(ConfigState{config, state});
+ m_changedConfigs.append(ConfigState{envFile, state});
}
void QnxSettingsWidget::apply()
{
for (const ConfigState &configState : std::as_const(m_changedConfigs)) {
switch (configState.state) {
- case Activated :
- configState.config->activate();
+ case Activated: {
+ QnxConfiguration *config = dd->configurationFromEnvFile(configState.envFile);
+ QTC_ASSERT(config, break);
+ config->activate();
break;
- case Deactivated:
- configState.config->deactivate();
+ }
+ case Deactivated: {
+ QnxConfiguration *config = dd->configurationFromEnvFile(configState.envFile);
+ QTC_ASSERT(config, break);
+ config->deactivate();
break;
- case Added:
- m_qnxConfigManager->addConfiguration(configState.config);
+ }
+ case Added: {
+ QnxConfiguration config(configState.envFile);
+ config.ensureContents();
+ dd->m_configurations.insert(configState.envFile, config);
break;
+ }
case Removed:
- configState.config->deactivate();
- m_qnxConfigManager->removeConfiguration(configState.config);
+ QnxConfiguration *config = dd->configurationFromEnvFile(configState.envFile);
+ QTC_ASSERT(config, break);
+ config->deactivate();
+ dd->m_configurations.remove(configState.envFile);
break;
}
}
m_changedConfigs.clear();
+ populateConfigsCombo();
}
-
// QnxSettingsPage
+QList<ToolChain *> QnxSettingsPage::autoDetect(const QList<ToolChain *> &alreadyKnown)
+{
+ QList<ToolChain *> result;
+ for (const QnxConfiguration &config : std::as_const(dd->m_configurations)) {
+ config.ensureContents();
+ for (const QnxTarget &target : std::as_const(config.m_targets)) {
+ result += Utils::filtered(alreadyKnown, [config, target](ToolChain *tc) {
+ return tc->typeId() == Constants::QNX_TOOLCHAIN_ID
+ && tc->targetAbi() == target.m_abi
+ && tc->compilerCommand() == config.m_qccCompiler;
+ });
+ }
+ }
+ return result;
+}
+
QnxSettingsPage::QnxSettingsPage()
{
setId("DD.Qnx Configuration");
setDisplayName(Tr::tr("QNX"));
setCategory(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY);
setWidgetCreator([] { return new QnxSettingsWidget; });
+
+ dd = new QnxSettingsPagePrivate;
+}
+
+QnxSettingsPage::~QnxSettingsPage()
+{
+ delete dd;
}
} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxsettingspage.h b/src/plugins/qnx/qnxsettingspage.h
index cdf6f1ba86..f127d5f35e 100644
--- a/src/plugins/qnx/qnxsettingspage.h
+++ b/src/plugins/qnx/qnxsettingspage.h
@@ -5,12 +5,18 @@
#include <coreplugin/dialogs/ioptionspage.h>
+namespace ProjectExplorer { class ToolChain; }
+
namespace Qnx::Internal {
class QnxSettingsPage final : public Core::IOptionsPage
{
public:
QnxSettingsPage();
+ ~QnxSettingsPage();
+
+ static QList<ProjectExplorer::ToolChain *> autoDetect(
+ const QList<ProjectExplorer::ToolChain *> &alreadyKnown);
};
} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxtoolchain.cpp b/src/plugins/qnx/qnxtoolchain.cpp
index 773efc0c74..8176b3d17c 100644
--- a/src/plugins/qnx/qnxtoolchain.cpp
+++ b/src/plugins/qnx/qnxtoolchain.cpp
@@ -3,9 +3,8 @@
#include "qnxtoolchain.h"
-#include "qnxconfiguration.h"
-#include "qnxconfigurationmanager.h"
#include "qnxconstants.h"
+#include "qnxsettingspage.h"
#include "qnxtr.h"
#include "qnxutils.h"
@@ -54,7 +53,7 @@ static Abis detectTargetAbis(const FilePath &sdpPath)
const EnvironmentItems environment = QnxUtils::qnxEnvironment(sdpPath);
for (const EnvironmentItem &item : environment) {
if (item.name == QLatin1String("QNX_TARGET"))
- qnxTarget = FilePath::fromString(item.value);
+ qnxTarget = sdpPath.withNewPath(item.value);
}
}
@@ -222,10 +221,7 @@ Toolchains QnxToolChainFactory::autoDetect(const ToolchainDetector &detector) co
if (detector.device)
return {};
- Toolchains tcs;
- const auto configurations = QnxConfigurationManager::instance()->configurations();
- for (QnxConfiguration *configuration : configurations)
- tcs += configuration->autoDetect(detector.alreadyKnown);
+ Toolchains tcs = QnxSettingsPage::autoDetect(detector.alreadyKnown);
return tcs;
}
diff --git a/src/plugins/qnx/qnxutils.cpp b/src/plugins/qnx/qnxutils.cpp
index 697e95bebc..ba2c41b345 100644
--- a/src/plugins/qnx/qnxutils.cpp
+++ b/src/plugins/qnx/qnxutils.cpp
@@ -5,7 +5,7 @@
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/temporaryfile.h>
#include <QDebug>
@@ -16,6 +16,15 @@ using namespace Utils;
namespace Qnx::Internal {
+QnxTarget::QnxTarget(const Utils::FilePath &path, const ProjectExplorer::Abi &abi) :
+ m_path(path), m_abi(abi)
+{}
+
+QString QnxTarget::shortDescription() const
+{
+ return QnxUtils::cpuDirShortDescription(cpuDir());
+}
+
QString QnxUtils::cpuDirFromAbi(const Abi &abi)
{
if (abi.os() != Abi::OS::QnxOS)
@@ -89,7 +98,7 @@ EnvironmentItems QnxUtils::qnxEnvironmentFromEnvFile(const FilePath &filePath)
tmpFile->writeFileContents(content.toUtf8());
// running wrapper script
- QtcProcess process;
+ Process process;
if (isWindows)
process.setCommand({filePath.withNewPath("cmd.exe"), {"/C", tmpFile->path()}});
else
diff --git a/src/plugins/qnx/qnxutils.h b/src/plugins/qnx/qnxutils.h
index 4f523953e4..2fea51ccc6 100644
--- a/src/plugins/qnx/qnxutils.h
+++ b/src/plugins/qnx/qnxutils.h
@@ -14,12 +14,14 @@ namespace Qnx::Internal {
class QnxTarget
{
public:
- QnxTarget(const Utils::FilePath &path, const ProjectExplorer::Abi &abi) :
- m_path(path), m_abi(abi)
- {
- }
+ QnxTarget(const Utils::FilePath &path, const ProjectExplorer::Abi &abi);
+
+ QString shortDescription() const;
+ QString cpuDir() const { return m_path.fileName(); }
+
Utils::FilePath m_path;
ProjectExplorer::Abi m_abi;
+ Utils::FilePath m_debuggerPath;
};
namespace QnxUtils {
diff --git a/src/plugins/qnx/slog2inforunner.cpp b/src/plugins/qnx/slog2inforunner.cpp
index 58509782c5..77700804b2 100644
--- a/src/plugins/qnx/slog2inforunner.cpp
+++ b/src/plugins/qnx/slog2inforunner.cpp
@@ -3,13 +3,13 @@
#include "slog2inforunner.h"
-#include "qnxdevice.h"
#include "qnxtr.h"
+#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/runconfigurationaspects.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QRegularExpression>
@@ -31,50 +31,47 @@ Slog2InfoRunner::Slog2InfoRunner(RunControl *runControl)
void Slog2InfoRunner::start()
{
- using namespace Utils::Tasking;
+ using namespace Tasking;
QTC_CHECK(!m_taskTree);
- const auto testStartHandler = [this](QtcProcess &process) {
+ const auto testStartHandler = [this](Process &process) {
process.setCommand({device()->filePath("slog2info"), {}});
};
- const auto testDoneHandler = [this](const QtcProcess &) {
+ const auto testDoneHandler = [this](const Process &) {
m_found = true;
};
- const auto testErrorHandler = [this](const QtcProcess &) {
- QnxDevice::ConstPtr qnxDevice = device().dynamicCast<const QnxDevice>();
- if (qnxDevice && qnxDevice->qnxVersion() > 0x060500) {
- appendMessage(Tr::tr("Warning: \"slog2info\" is not found on the device, "
- "debug output not available."), ErrorMessageFormat);
- }
+ const auto testErrorHandler = [this](const Process &) {
+ appendMessage(Tr::tr("Warning: \"slog2info\" is not found on the device, "
+ "debug output not available."), ErrorMessageFormat);
};
- const auto launchTimeStartHandler = [this](QtcProcess &process) {
+ const auto launchTimeStartHandler = [this](Process &process) {
process.setCommand({device()->filePath("date"), "+\"%d %H:%M:%S\"", CommandLine::Raw});
};
- const auto launchTimeDoneHandler = [this](const QtcProcess &process) {
+ const auto launchTimeDoneHandler = [this](const Process &process) {
QTC_CHECK(!m_applicationId.isEmpty());
QTC_CHECK(m_found);
m_launchDateTime = QDateTime::fromString(process.cleanedStdOut().trimmed(), "dd HH:mm:ss");
};
- const auto logStartHandler = [this](QtcProcess &process) {
+ const auto logStartHandler = [this](Process &process) {
process.setCommand({device()->filePath("slog2info"), {"-w"}});
- connect(&process, &QtcProcess::readyReadStandardOutput, this, [&] {
+ connect(&process, &Process::readyReadStandardOutput, this, [&] {
processLogInput(QString::fromLatin1(process.readAllRawStandardOutput()));
});
- connect(&process, &QtcProcess::readyReadStandardError, this, [&] {
+ connect(&process, &Process::readyReadStandardError, this, [&] {
appendMessage(QString::fromLatin1(process.readAllRawStandardError()), StdErrFormat);
});
};
- const auto logErrorHandler = [this](const QtcProcess &process) {
+ const auto logErrorHandler = [this](const Process &process) {
appendMessage(Tr::tr("Cannot show slog2info output. Error: %1").arg(process.errorString()),
StdErrFormat);
};
- const Tasking::Group root {
- Process(testStartHandler, testDoneHandler, testErrorHandler),
- Process(launchTimeStartHandler, launchTimeDoneHandler),
- Process(logStartHandler, {}, logErrorHandler)
+ const Group root {
+ ProcessTask(testStartHandler, testDoneHandler, testErrorHandler),
+ ProcessTask(launchTimeStartHandler, launchTimeDoneHandler),
+ ProcessTask(logStartHandler, {}, logErrorHandler)
};
m_taskTree.reset(new TaskTree(root));
diff --git a/src/plugins/qnx/slog2inforunner.h b/src/plugins/qnx/slog2inforunner.h
index e89b80b697..83d8d29333 100644
--- a/src/plugins/qnx/slog2inforunner.h
+++ b/src/plugins/qnx/slog2inforunner.h
@@ -7,7 +7,7 @@
#include <QDateTime>
-namespace Utils { class TaskTree; }
+namespace Tasking { class TaskTree; }
namespace Qnx::Internal {
@@ -33,7 +33,7 @@ private:
bool m_currentLogs = false;
QString m_remainingData;
- std::unique_ptr<Utils::TaskTree> m_taskTree;
+ std::unique_ptr<Tasking::TaskTree> m_taskTree;
};
} // Qnx::Internal
diff --git a/src/plugins/qtsupport/baseqtversion.cpp b/src/plugins/qtsupport/baseqtversion.cpp
index 52461fae50..179649c53b 100644
--- a/src/plugins/qtsupport/baseqtversion.cpp
+++ b/src/plugins/qtsupport/baseqtversion.cpp
@@ -21,7 +21,8 @@
#include <projectexplorer/headerpath.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/toolchain.h>
#include <projectexplorer/toolchainmanager.h>
@@ -32,8 +33,8 @@
#include <utils/fileinprojectfinder.h>
#include <utils/hostosinfo.h>
#include <utils/macroexpander.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/winutils.h>
@@ -1252,7 +1253,7 @@ void QtVersionPrivate::updateVersionInfo()
m_qmakeIsExecutable = true;
auto fileProperty = [this](const QByteArray &name) {
- return FilePath::fromUserInput(qmakeProperty(name)).onDevice(m_qmakeCommand);
+ return m_qmakeCommand.withNewPath(qmakeProperty(name)).cleanPath();
};
m_data.prefix = fileProperty("QT_INSTALL_PREFIX");
@@ -1543,10 +1544,10 @@ void QtVersion::populateQmlFileFinder(FileInProjectFinder *finder, const Target
// ... else try the session manager's global startup project ...
if (!startupProject)
- startupProject = SessionManager::startupProject();
+ startupProject = ProjectManager::startupProject();
// ... and if that is null, use the first project available.
- const QList<Project *> projects = SessionManager::projects();
+ const QList<Project *> projects = ProjectManager::projects();
QTC_CHECK(projects.isEmpty() || startupProject);
FilePath projectDirectory;
@@ -1683,7 +1684,7 @@ static QByteArray runQmakeQuery(const FilePath &binary, const Environment &env,
// Prevent e.g. qmake 4.x on MinGW to show annoying errors about missing dll's.
WindowsCrashDialogBlocker crashDialogBlocker;
- QtcProcess process;
+ Process process;
process.setEnvironment(env);
process.setCommand({binary, {"-query"}});
process.start();
@@ -1773,7 +1774,7 @@ FilePath QtVersionPrivate::mkspecDirectoryFromVersionInfo(const QHash<ProKey, Pr
QString dataDir = qmakeProperty(versionInfo, "QT_HOST_DATA", PropertyVariantSrc);
if (dataDir.isEmpty())
return FilePath();
- return FilePath::fromUserInput(dataDir + "/mkspecs").onDevice(qmakeCommand);
+ return qmakeCommand.withNewPath(dataDir + "/mkspecs").cleanPath();
}
FilePath QtVersionPrivate::mkspecFromVersionInfo(const QHash<ProKey, ProString> &versionInfo,
diff --git a/src/plugins/qtsupport/codegensettingspage.cpp b/src/plugins/qtsupport/codegensettingspage.cpp
index 8e0a3cfca0..4a30e5f969 100644
--- a/src/plugins/qtsupport/codegensettingspage.cpp
+++ b/src/plugins/qtsupport/codegensettingspage.cpp
@@ -39,12 +39,10 @@ private:
CodeGenSettingsPageWidget::CodeGenSettingsPageWidget()
{
- resize(340, 232);
-
CodeGenSettings parameters;
parameters.fromSettings(Core::ICore::settings());
- using namespace Utils::Layouting;
+ using namespace Layouting;
m_ptrAggregationRadioButton = new QRadioButton(Tr::tr("Aggregation as a pointer member"));
m_ptrAggregationRadioButton->setChecked
diff --git a/src/plugins/qtsupport/exampleslistmodel.cpp b/src/plugins/qtsupport/exampleslistmodel.cpp
index d3cf3535dd..5beb9c80a3 100644
--- a/src/plugins/qtsupport/exampleslistmodel.cpp
+++ b/src/plugins/qtsupport/exampleslistmodel.cpp
@@ -4,7 +4,6 @@
#include "exampleslistmodel.h"
#include "examplesparser.h"
-#include "qtsupporttr.h"
#include <QBuffer>
#include <QApplication>
@@ -29,6 +28,7 @@
#include <utils/stringutils.h>
#include <utils/stylehelper.h>
+#include <QLoggingCategory>
#include <algorithm>
#include <memory>
@@ -38,6 +38,8 @@ using namespace Utils;
namespace QtSupport {
namespace Internal {
+static QLoggingCategory log = QLoggingCategory("qtc.examples", QtWarningMsg);
+
static bool debugExamples()
{
return qtcEnvironmentVariableIsSet("QTC_DEBUG_EXAMPLESMODEL");
@@ -64,16 +66,16 @@ int ExampleSetModel::readCurrentIndexFromSettings() const
ExampleSetModel::ExampleSetModel()
{
+ if (debugExamples() && !log().isDebugEnabled())
+ log().setEnabled(QtDebugMsg, true);
// read extra example sets settings
QSettings *settings = Core::ICore::settings();
const QStringList list = settings->value("Help/InstalledExamples", QStringList()).toStringList();
- if (debugExamples())
- qWarning() << "Reading Help/InstalledExamples from settings:" << list;
+ qCDebug(log) << "Reading Help/InstalledExamples from settings:" << list;
for (const QString &item : list) {
const QStringList &parts = item.split(QLatin1Char('|'));
if (parts.size() < 3) {
- if (debugExamples())
- qWarning() << "Item" << item << "has less than 3 parts (separated by '|'):" << parts;
+ qCDebug(log) << "Item" << item << "has less than 3 parts (separated by '|'):" << parts;
continue;
}
ExtraExampleSet set;
@@ -82,15 +84,13 @@ ExampleSetModel::ExampleSetModel()
set.examplesPath = parts.at(2);
QFileInfo fi(set.manifestPath);
if (!fi.isDir() || !fi.isReadable()) {
- if (debugExamples())
- qWarning() << "Manifest path " << set.manifestPath << "is not a readable directory, ignoring";
+ qCDebug(log) << "Manifest path " << set.manifestPath
+ << "is not a readable directory, ignoring";
continue;
}
- if (debugExamples()) {
- qWarning() << "Adding examples set displayName=" << set.displayName
- << ", manifestPath=" << set.manifestPath
- << ", examplesPath=" << set.examplesPath;
- }
+ qCDebug(log) << "Adding examples set displayName=" << set.displayName
+ << ", manifestPath=" << set.manifestPath
+ << ", examplesPath=" << set.examplesPath;
if (!Utils::anyOf(m_extraExampleSets, [&set](const ExtraExampleSet &s) {
return FilePath::fromString(s.examplesPath).cleanPath()
== FilePath::fromString(set.examplesPath).cleanPath()
@@ -98,8 +98,8 @@ ExampleSetModel::ExampleSetModel()
== FilePath::fromString(set.manifestPath).cleanPath();
})) {
m_extraExampleSets.append(set);
- } else if (debugExamples()) {
- qWarning() << "Not adding, because example set with same directories exists";
+ } else {
+ qCDebug(log) << "Not adding, because example set with same directories exists";
}
}
m_extraExampleSets += pluginRegisteredExampleSets();
@@ -138,11 +138,9 @@ void ExampleSetModel::recreateModel(const QtVersions &qtVersions)
if (extraManifestDirs.contains(version->docsPath())) {
m_extraExampleSets[extraManifestDirs.value(version->docsPath())].qtVersion
= version->qtVersion();
- if (debugExamples()) {
- qWarning() << "Not showing Qt version because manifest path is already added "
- "through InstalledExamples settings:"
- << version->displayName();
- }
+ qCDebug(log) << "Not showing Qt version because manifest path is already added "
+ "through InstalledExamples settings:"
+ << version->displayName();
continue;
}
auto newItem = new QStandardItem();
@@ -255,7 +253,8 @@ static QPixmap fetchPixmapAndUpdatePixmapCache(const QString &url)
img.convertTo(QImage::Format_RGB32);
const int dpr = qApp->devicePixelRatio();
// boundedTo -> don't scale thumbnails up
- const QSize scaledSize = Core::ListModel::defaultImageSize.boundedTo(img.size()) * dpr;
+ const QSize scaledSize =
+ WelcomePageHelpers::GridItemImageSize.boundedTo(img.size()) * dpr;
pixmap = QPixmap::fromImage(
img.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation));
pixmap.setDevicePixelRatio(dpr);
@@ -318,65 +317,14 @@ static bool isValidExampleOrDemo(ExampleItem *item)
}
if (!ok) {
item->tags.append(QLatin1String("broken"));
- if (debugExamples())
- qWarning() << QString::fromLatin1("ERROR: Item \"%1\" broken: %2").arg(item->name, reason);
+ qCDebug(log) << QString::fromLatin1("ERROR: Item \"%1\" broken: %2").arg(item->name, reason);
}
- if (debugExamples() && item->description.isEmpty())
- qWarning() << QString::fromLatin1("WARNING: Item \"%1\" has no description").arg(item->name);
+ if (item->description.isEmpty())
+ qCDebug(log) << QString::fromLatin1("WARNING: Item \"%1\" has no description")
+ .arg(item->name);
return ok || debugExamples();
}
-static bool sortByHighlightedAndName(ExampleItem *first, ExampleItem *second)
-{
- if (first->isHighlighted && !second->isHighlighted)
- return true;
- if (!first->isHighlighted && second->isHighlighted)
- return false;
- return first->name.compare(second->name, Qt::CaseInsensitive) < 0;
-}
-
-static QList<std::pair<QString, QList<ExampleItem *>>> getCategories(
- const QList<ExampleItem *> &items, bool sortIntoCategories)
-{
- static const QString otherDisplayName = Tr::tr("Other", "Category for all other examples");
- const bool useCategories = sortIntoCategories
- || qtcEnvironmentVariableIsSet("QTC_USE_EXAMPLE_CATEGORIES");
- QList<ExampleItem *> other;
- QMap<QString, QList<ExampleItem *>> categoryMap;
- if (useCategories) {
- for (ExampleItem *item : items) {
- const QStringList itemCategories = item->metaData.value("category");
- for (const QString &category : itemCategories)
- categoryMap[category].append(item);
- if (itemCategories.isEmpty())
- other.append(item);
- }
- }
- QList<std::pair<QString, QList<ExampleItem *>>> categories;
- if (categoryMap.isEmpty()) {
- // The example set doesn't define categories. Consider the "highlighted" ones as "featured"
- QList<ExampleItem *> featured;
- QList<ExampleItem *> allOther;
- std::tie(featured, allOther) = Utils::partition(items, [](ExampleItem *i) {
- return i->isHighlighted;
- });
- if (!featured.isEmpty())
- categories.append({Tr::tr("Featured", "Category for highlighted examples"), featured});
- if (!allOther.isEmpty())
- categories.append({otherDisplayName, allOther});
- } else {
- const auto end = categoryMap.constKeyValueEnd();
- for (auto it = categoryMap.constKeyValueBegin(); it != end; ++it)
- categories.append(*it);
- if (!other.isEmpty())
- categories.append({otherDisplayName, other});
- }
- const auto end = categories.end();
- for (auto it = categories.begin(); it != end; ++it)
- sort(it->second, sortByHighlightedAndName);
- return categories;
-}
-
void ExamplesViewController::updateExamples()
{
QString examplesInstallPath;
@@ -391,10 +339,8 @@ void ExamplesViewController::updateExamples()
QList<ExampleItem *> items;
for (const QString &exampleSource : sources) {
const auto manifest = FilePath::fromUserInput(exampleSource);
- if (debugExamples()) {
- qWarning() << QString::fromLatin1("Reading file \"%1\"...")
- .arg(manifest.absoluteFilePath().toUserOutput());
- }
+ qCDebug(log) << QString::fromLatin1("Reading file \"%1\"...")
+ .arg(manifest.absoluteFilePath().toUserOutput());
const expected_str<QList<ExampleItem *>> result
= parseExamples(manifest,
@@ -402,10 +348,8 @@ void ExamplesViewController::updateExamples()
FilePath::fromUserInput(demosInstallPath),
m_isExamples);
if (!result) {
- if (debugExamples()) {
- qWarning() << "ERROR: Could not read examples from" << exampleSource << ":"
- << result.error();
- }
+ qCDebug(log) << "ERROR: Could not read examples from" << exampleSource << ":"
+ << result.error();
continue;
}
items += filtered(*result, isValidExampleOrDemo);
@@ -423,10 +367,10 @@ void ExamplesViewController::updateExamples()
}
const bool sortIntoCategories = qtVersion >= *minQtVersionForCategories;
- const QList<std::pair<QString, QList<ExampleItem *>>> sections
+ const QList<std::pair<Section, QList<ExampleItem *>>> sections
= getCategories(items, sortIntoCategories);
for (int i = 0; i < sections.size(); ++i) {
- m_view->addSection({sections.at(i).first, i},
+ m_view->addSection(sections.at(i).first,
static_container_cast<ListItem *>(sections.at(i).second));
}
}
@@ -535,7 +479,7 @@ QStringList ExampleSetModel::exampleSources(QString *examplesInstallPath,
const QStringList demosPattern(QLatin1String("demos-manifest.xml"));
QFileInfoList fis;
const QFileInfoList subDirs = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
- for (QFileInfo subDir : subDirs) {
+ for (const QFileInfo &subDir : subDirs) {
fis << QDir(subDir.absoluteFilePath()).entryInfoList(examplesPattern);
fis << QDir(subDir.absoluteFilePath()).entryInfoList(demosPattern);
}
diff --git a/src/plugins/qtsupport/exampleslistmodel.h b/src/plugins/qtsupport/exampleslistmodel.h
index c444f9bc1e..f0465655b7 100644
--- a/src/plugins/qtsupport/exampleslistmodel.h
+++ b/src/plugins/qtsupport/exampleslistmodel.h
@@ -31,7 +31,7 @@ public:
// qtVersion is set by recreateModel for extra sets that correspond to actual Qt versions.
// This is needed for the decision to show categories or not based on the Qt version
// (which is not ideal).
- QVersionNumber qtVersion;
+ QVersionNumber qtVersion = {};
};
static QVector<ExtraExampleSet> pluginRegisteredExampleSets();
diff --git a/src/plugins/qtsupport/examplesparser.cpp b/src/plugins/qtsupport/examplesparser.cpp
index 5fd3161777..84469ca2c4 100644
--- a/src/plugins/qtsupport/examplesparser.cpp
+++ b/src/plugins/qtsupport/examplesparser.cpp
@@ -3,7 +3,10 @@
#include "examplesparser.h"
+#include "qtsupporttr.h"
+
#include <utils/algorithm.h>
+#include <utils/environment.h>
#include <utils/filepath.h>
#include <utils/stylehelper.h>
@@ -24,8 +27,8 @@ static FilePath relativeOrInstallPath(const FilePath &path,
return relativeResolvedPath;
if (installResolvedPath.exists())
return installResolvedPath;
- // doesn't exist, just return relative
- return relativeResolvedPath;
+ // doesn't exist, return the preferred resolved install path
+ return installResolvedPath;
}
static QString fixStringForTags(const QString &string)
@@ -298,4 +301,68 @@ expected_str<QList<ExampleItem *>> parseExamples(const QByteArray &manifestData,
return items;
}
+static bool sortByHighlightedAndName(ExampleItem *first, ExampleItem *second)
+{
+ if (first->isHighlighted && !second->isHighlighted)
+ return true;
+ if (!first->isHighlighted && second->isHighlighted)
+ return false;
+ return first->name.compare(second->name, Qt::CaseInsensitive) < 0;
+}
+
+QList<std::pair<Core::Section, QList<ExampleItem *>>> getCategories(const QList<ExampleItem *> &items,
+ bool sortIntoCategories)
+{
+ static const QString otherDisplayName = Tr::tr("Other", "Category for all other examples");
+ const bool useCategories = sortIntoCategories
+ || qtcEnvironmentVariableIsSet("QTC_USE_EXAMPLE_CATEGORIES");
+ QList<ExampleItem *> other;
+ QMap<QString, QList<ExampleItem *>> categoryMap;
+ if (useCategories) {
+ // Append copies of the items and delete the original ones,
+ // because items might be added to multiple categories and that needs individual items
+ for (ExampleItem *item : items) {
+ const QStringList itemCategories = Utils::filteredUnique(
+ item->metaData.value("category"));
+ for (const QString &category : itemCategories)
+ categoryMap[category].append(new ExampleItem(*item));
+ if (itemCategories.isEmpty())
+ other.append(new ExampleItem(*item));
+ }
+ }
+ QList<std::pair<Core::Section, QList<ExampleItem *>>> categories;
+ if (categoryMap.isEmpty()) {
+ // If we tried sorting into categories, but none were defined, we copied the items
+ // into "other", which we don't use here. Get rid of them again.
+ qDeleteAll(other);
+ // The example set doesn't define categories. Consider the "highlighted" ones as "featured"
+ QList<ExampleItem *> featured;
+ QList<ExampleItem *> allOther;
+ std::tie(featured, allOther) = Utils::partition(items, [](ExampleItem *i) {
+ return i->isHighlighted;
+ });
+ if (!featured.isEmpty()) {
+ categories.append(
+ {{Tr::tr("Featured", "Category for highlighted examples"), 0}, featured});
+ }
+ if (!allOther.isEmpty())
+ categories.append({{otherDisplayName, 1}, allOther});
+ } else {
+ // All original items have been copied into a category or other, delete.
+ qDeleteAll(items);
+ int index = 0;
+ const auto end = categoryMap.constKeyValueEnd();
+ for (auto it = categoryMap.constKeyValueBegin(); it != end; ++it) {
+ categories.append({{it->first, index, /*maxRows=*/index == 0 ? 2 : 1}, it->second});
+ ++index;
+ }
+ if (!other.isEmpty())
+ categories.append({{otherDisplayName, index, /*maxRows=*/1}, other});
+ }
+ const auto end = categories.end();
+ for (auto it = categories.begin(); it != end; ++it)
+ sort(it->second, sortByHighlightedAndName);
+ return categories;
+}
+
} // namespace QtSupport::Internal
diff --git a/src/plugins/qtsupport/examplesparser.h b/src/plugins/qtsupport/examplesparser.h
index 2d1afa5483..54efaf5887 100644
--- a/src/plugins/qtsupport/examplesparser.h
+++ b/src/plugins/qtsupport/examplesparser.h
@@ -13,7 +13,7 @@ namespace QtSupport::Internal {
enum InstructionalType { Example = 0, Demo, Tutorial };
-class QTSUPPORT_EXPORT ExampleItem : public Core::ListItem
+class QTSUPPORT_TEST_EXPORT ExampleItem : public Core::ListItem
{
public:
Utils::FilePath projectPath;
@@ -31,19 +31,22 @@ public:
QHash<QString, QStringList> metaData;
};
-QTSUPPORT_EXPORT Utils::expected_str<QList<ExampleItem *>> parseExamples(
+QTSUPPORT_TEST_EXPORT Utils::expected_str<QList<ExampleItem *>> parseExamples(
const Utils::FilePath &manifest,
const Utils::FilePath &examplesInstallPath,
const Utils::FilePath &demosInstallPath,
bool examples);
-QTSUPPORT_EXPORT Utils::expected_str<QList<ExampleItem *>> parseExamples(
+QTSUPPORT_TEST_EXPORT Utils::expected_str<QList<ExampleItem *>> parseExamples(
const QByteArray &manifestData,
const Utils::FilePath &manifestPath,
const Utils::FilePath &examplesInstallPath,
const Utils::FilePath &demosInstallPath,
bool examples);
+QTSUPPORT_TEST_EXPORT QList<std::pair<Core::Section, QList<ExampleItem *>>> getCategories(
+ const QList<ExampleItem *> &items, bool sortIntoCategories);
+
} // namespace QtSupport::Internal
Q_DECLARE_METATYPE(QtSupport::Internal::ExampleItem *)
diff --git a/src/plugins/qtsupport/externaleditors.cpp b/src/plugins/qtsupport/externaleditors.cpp
index bed8b7618f..dffd730d28 100644
--- a/src/plugins/qtsupport/externaleditors.cpp
+++ b/src/plugins/qtsupport/externaleditors.cpp
@@ -8,8 +8,8 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
-#include <projectexplorer/session.h>
#include <qtsupport/qtkitinformation.h>
@@ -19,8 +19,8 @@
#include <utils/environment.h>
#include <utils/filepath.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDebug>
#include <QMap>
@@ -181,7 +181,7 @@ static bool getEditorLaunchData(const CommandForQtVersion &commandForQtVersion,
// As fallback check PATH
data->workingDirectory.clear();
QVector<QtSupport::QtVersion *> qtVersionsToCheck; // deduplicated after being filled
- if (const Project *project = SessionManager::projectForFile(filePath)) {
+ if (const Project *project = ProjectManager::projectForFile(filePath)) {
data->workingDirectory = project->projectDirectory();
// active kit
if (const Target *target = project->activeTarget()) {
@@ -224,7 +224,7 @@ static bool startEditorProcess(const LaunchData &data, QString *errorMessage)
if (debug)
qDebug() << Q_FUNC_INFO << '\n' << data.binary << data.arguments << data.workingDirectory;
qint64 pid = 0;
- if (!QtcProcess::startDetached({FilePath::fromString(data.binary), data.arguments}, data.workingDirectory, &pid)) {
+ if (!Process::startDetached({FilePath::fromString(data.binary), data.arguments}, data.workingDirectory, &pid)) {
*errorMessage = msgStartFailed(data.binary, data.arguments);
return false;
}
diff --git a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp
index bf19233c21..7bc7192185 100644
--- a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp
+++ b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp
@@ -280,8 +280,8 @@ public:
// for macOS dark mode
pal.setColor(QPalette::Text, Utils::creatorTheme()->color(Theme::Welcome_TextColor));
exampleSetSelector->setPalette(pal);
- exampleSetSelector->setMinimumWidth(Core::ListItemDelegate::GridItemWidth);
- exampleSetSelector->setMaximumWidth(Core::ListItemDelegate::GridItemWidth);
+ exampleSetSelector->setMinimumWidth(Core::WelcomePageHelpers::GridItemWidth);
+ exampleSetSelector->setMaximumWidth(Core::WelcomePageHelpers::GridItemWidth);
exampleSetSelector->setModel(s_exampleSetModel);
exampleSetSelector->setCurrentIndex(s_exampleSetModel->selectedExampleSet());
connect(exampleSetSelector,
diff --git a/src/plugins/qtsupport/qscxmlcgenerator.cpp b/src/plugins/qtsupport/qscxmlcgenerator.cpp
index 85c467281d..7cb67f6171 100644
--- a/src/plugins/qtsupport/qscxmlcgenerator.cpp
+++ b/src/plugins/qtsupport/qscxmlcgenerator.cpp
@@ -91,7 +91,7 @@ bool QScxmlcGenerator::prepareToRun(const QByteArray &sourceContents)
return true;
}
-FileNameToContentsHash QScxmlcGenerator::handleProcessFinished(Utils::QtcProcess *process)
+FileNameToContentsHash QScxmlcGenerator::handleProcessFinished(Utils::Process *process)
{
Q_UNUSED(process)
const Utils::FilePath wd = workingDirectory();
diff --git a/src/plugins/qtsupport/qscxmlcgenerator.h b/src/plugins/qtsupport/qscxmlcgenerator.h
index 633fb1231b..342db3cb09 100644
--- a/src/plugins/qtsupport/qscxmlcgenerator.h
+++ b/src/plugins/qtsupport/qscxmlcgenerator.h
@@ -23,7 +23,7 @@ protected:
private:
Utils::FilePath tmpFile() const;
- ProjectExplorer::FileNameToContentsHash handleProcessFinished(Utils::QtcProcess *process) override;
+ ProjectExplorer::FileNameToContentsHash handleProcessFinished(Utils::Process *process) override;
bool prepareToRun(const QByteArray &sourceContents) override;
ProjectExplorer::Tasks parseIssues(const QByteArray &processStderr) override;
diff --git a/src/plugins/qtsupport/qtbuildaspects.cpp b/src/plugins/qtsupport/qtbuildaspects.cpp
index 95f2c8d432..210ea58ee5 100644
--- a/src/plugins/qtsupport/qtbuildaspects.cpp
+++ b/src/plugins/qtsupport/qtbuildaspects.cpp
@@ -30,13 +30,13 @@ QmlDebuggingAspect::QmlDebuggingAspect(BuildConfiguration *buildConfig)
setValue(ProjectExplorerPlugin::buildPropertiesSettings().qmlDebugging.value());
}
-void QmlDebuggingAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void QmlDebuggingAspect::addToLayout(Layouting::LayoutItem &parent)
{
- SelectionAspect::addToLayout(builder);
+ SelectionAspect::addToLayout(parent);
const auto warningLabel = createSubWidget<InfoLabel>(QString(), InfoLabel::Warning);
warningLabel->setElideMode(Qt::ElideNone);
warningLabel->setVisible(false);
- builder.addRow({{}, warningLabel});
+ parent.addRow({{}, warningLabel});
const auto changeHandler = [this, warningLabel] {
QString warningText;
QTC_ASSERT(m_buildConfig, return);
@@ -67,13 +67,13 @@ QtQuickCompilerAspect::QtQuickCompilerAspect(BuildConfiguration *buildConfig)
setValue(ProjectExplorerPlugin::buildPropertiesSettings().qtQuickCompiler.value());
}
-void QtQuickCompilerAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void QtQuickCompilerAspect::addToLayout(Layouting::LayoutItem &parent)
{
- SelectionAspect::addToLayout(builder);
+ SelectionAspect::addToLayout(parent);
const auto warningLabel = createSubWidget<InfoLabel>(QString(), InfoLabel::Warning);
warningLabel->setElideMode(Qt::ElideNone);
warningLabel->setVisible(false);
- builder.addRow({{}, warningLabel});
+ parent.addRow({{}, warningLabel});
const auto changeHandler = [this, warningLabel] {
QString warningText;
QTC_ASSERT(m_buildConfig, return);
diff --git a/src/plugins/qtsupport/qtbuildaspects.h b/src/plugins/qtsupport/qtbuildaspects.h
index e66fe9bbce..e5e0b3332e 100644
--- a/src/plugins/qtsupport/qtbuildaspects.h
+++ b/src/plugins/qtsupport/qtbuildaspects.h
@@ -18,7 +18,7 @@ class QTSUPPORT_EXPORT QmlDebuggingAspect : public Utils::TriStateAspect
public:
explicit QmlDebuggingAspect(ProjectExplorer::BuildConfiguration *buildConfig);
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
private:
const ProjectExplorer::BuildConfiguration *m_buildConfig = nullptr;
@@ -32,7 +32,7 @@ public:
QtQuickCompilerAspect(ProjectExplorer::BuildConfiguration *buildConfig);
private:
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
const ProjectExplorer::BuildConfiguration *m_buildConfig = nullptr;
};
diff --git a/src/plugins/qtsupport/qtkitinformation.cpp b/src/plugins/qtsupport/qtkitinformation.cpp
index 2489ce5839..7fab3fbbcc 100644
--- a/src/plugins/qtsupport/qtkitinformation.cpp
+++ b/src/plugins/qtsupport/qtkitinformation.cpp
@@ -60,11 +60,11 @@ public:
private:
void makeReadOnly() final { m_combo->setEnabled(false); }
- void addToLayout(Layouting::LayoutBuilder &builder)
+ void addToLayout(Layouting::LayoutItem &parent)
{
addMutableAction(m_combo);
- builder.addItem(m_combo);
- builder.addItem(m_manageButton);
+ parent.addItem(m_combo);
+ parent.addItem(m_manageButton);
}
void refresh() final
diff --git a/src/plugins/qtsupport/qtoptionspage.cpp b/src/plugins/qtsupport/qtoptionspage.cpp
index 25a9f5f3a7..92b439ea1a 100644
--- a/src/plugins/qtsupport/qtoptionspage.cpp
+++ b/src/plugins/qtsupport/qtoptionspage.cpp
@@ -232,8 +232,6 @@ QtOptionsPageWidget::QtOptionsPageWidget()
, m_warningVersionIcon(Utils::Icons::WARNING.icon())
, m_configurationWidget(nullptr)
{
- resize(446, 450);
-
m_qtdirList = new QTreeView(this);
m_qtdirList->setObjectName("qtDirList");
m_qtdirList->setUniformRowHeights(true);
@@ -261,15 +259,16 @@ QtOptionsPageWidget::QtOptionsPageWidget()
m_errorLabel = new QLabel;
- using namespace Utils::Layouting;
+ using namespace Layouting;
auto versionInfoWidget = new QWidget;
// clang-format off
Form {
Tr::tr("Name:"), m_nameEdit, br,
Tr::tr("qmake path:"), Row { m_qmakePath, m_editPathPushButton }, br,
- Span(2, m_errorLabel)
- }.attachTo(versionInfoWidget, WithoutMargins);
+ Span(2, m_errorLabel),
+ noMargin
+ }.attachTo(versionInfoWidget);
// clang-format on
m_formLayout = qobject_cast<QFormLayout*>(versionInfoWidget->layout());
diff --git a/src/plugins/qtsupport/qtsupport_global.h b/src/plugins/qtsupport/qtsupport_global.h
index aca20f5aa2..60c9bf0c12 100644
--- a/src/plugins/qtsupport/qtsupport_global.h
+++ b/src/plugins/qtsupport/qtsupport_global.h
@@ -12,3 +12,15 @@
#else
# define QTSUPPORT_EXPORT Q_DECL_IMPORT
#endif
+
+#if defined(WITH_TESTS)
+# if defined(QTSUPPORT_LIBRARY)
+# define QTSUPPORT_TEST_EXPORT Q_DECL_EXPORT
+# elif defined(QTSUPPORT_STATIC_LIBRARY)
+# define QTSUPPORT_TEST_EXPORT
+# else
+# define QTSUPPORT_TEST_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define QTSUPPORT_TEST_EXPORT
+#endif
diff --git a/src/plugins/qtsupport/qtsupportplugin.cpp b/src/plugins/qtsupport/qtsupportplugin.cpp
index c54c1bfc23..41c88d18e3 100644
--- a/src/plugins/qtsupport/qtsupportplugin.cpp
+++ b/src/plugins/qtsupport/qtsupportplugin.cpp
@@ -24,8 +24,8 @@
#include <projectexplorer/jsonwizard/jsonwizardfactory.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <proparser/qmakeevaluator.h>
@@ -33,7 +33,7 @@
#include <utils/filepath.h>
#include <utils/infobar.h>
#include <utils/macroexpander.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
using namespace Core;
using namespace Utils;
@@ -76,7 +76,7 @@ static void processRunnerCallback(ProcessData *data)
{
FilePath rootPath = FilePath::fromString(data->deviceRoot);
- QtcProcess proc;
+ Process proc;
proc.setProcessChannelMode(data->processChannelMode);
proc.setCommand({rootPath.withNewPath("/bin/sh"), {QString("-c"), data->command}});
proc.setWorkingDirectory(FilePath::fromString(data->workingDirectory));
@@ -170,7 +170,7 @@ void QtSupportPlugin::extensionsInitialized()
});
static const auto activeQtVersion = []() -> const QtVersion * {
- ProjectExplorer::Project *project = SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectManager::startupProject();
if (!project || !project->activeTarget())
return nullptr;
return QtKitAspect::qtVersion(project->activeTarget()->kit());
@@ -197,7 +197,7 @@ void QtSupportPlugin::extensionsInitialized()
expander->registerVariable(
"ActiveProject::QT_HOST_LIBEXECS",
- Tr::tr("Full path to the libexec bin directory of the Qt version in the active kit "
+ Tr::tr("Full path to the libexec directory of the Qt version in the active kit "
"of the active project."),
[]() {
const QtVersion *const qt = activeQtVersion();
@@ -208,7 +208,7 @@ void QtSupportPlugin::extensionsInitialized()
const FilePath filePath = item.filePath();
if (filePath.isEmpty())
return links;
- const Project *project = SessionManager::projectForFile(filePath);
+ const Project *project = ProjectManager::projectForFile(filePath);
Target *target = project ? project->activeTarget() : nullptr;
QtVersion *qt = target ? QtKitAspect::qtVersion(target->kit()) : nullptr;
if (!qt)
diff --git a/src/plugins/qtsupport/qtversionmanager.cpp b/src/plugins/qtsupport/qtversionmanager.cpp
index c5a0f7b315..eedc79e686 100644
--- a/src/plugins/qtsupport/qtversionmanager.cpp
+++ b/src/plugins/qtsupport/qtversionmanager.cpp
@@ -5,7 +5,6 @@
#include "baseqtversion.h"
#include "exampleslistmodel.h"
-#include "qtkitinformation.h"
#include "qtsupportconstants.h"
#include "qtversionfactory.h"
@@ -22,8 +21,8 @@
#include <utils/filesystemwatcher.h>
#include <utils/hostosinfo.h>
#include <utils/persistentsettings.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDir>
#include <QFile>
@@ -369,7 +368,7 @@ static void saveQtVersions()
// Executes qtchooser with arguments in a process and returns its output
static QList<QByteArray> runQtChooser(const QString &qtchooser, const QStringList &arguments)
{
- QtcProcess p;
+ Process p;
p.setCommand({FilePath::fromString(qtchooser), arguments});
p.start();
p.waitForFinished();
diff --git a/src/plugins/qtsupport/uicgenerator.cpp b/src/plugins/qtsupport/uicgenerator.cpp
index 36c109e7b4..f1ff905b83 100644
--- a/src/plugins/qtsupport/uicgenerator.cpp
+++ b/src/plugins/qtsupport/uicgenerator.cpp
@@ -2,20 +2,21 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "uicgenerator.h"
+
#include "baseqtversion.h"
#include "qtkitinformation.h"
+#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/target.h>
-#include <projectexplorer/buildconfiguration.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <QFileInfo>
+#include <QDateTime>
#include <QDir>
+#include <QFileInfo>
#include <QLoggingCategory>
-#include <QDateTime>
using namespace ProjectExplorer;
@@ -48,7 +49,7 @@ QStringList UicGenerator::arguments() const
return {"-p"};
}
-FileNameToContentsHash UicGenerator::handleProcessFinished(Utils::QtcProcess *process)
+FileNameToContentsHash UicGenerator::handleProcessFinished(Utils::Process *process)
{
FileNameToContentsHash result;
if (process->exitStatus() != QProcess::NormalExit && process->exitCode() != 0)
diff --git a/src/plugins/qtsupport/uicgenerator.h b/src/plugins/qtsupport/uicgenerator.h
index 1d9344634a..d68b6814c2 100644
--- a/src/plugins/qtsupport/uicgenerator.h
+++ b/src/plugins/qtsupport/uicgenerator.h
@@ -18,7 +18,7 @@ public:
protected:
Utils::FilePath command() const override;
QStringList arguments() const override;
- ProjectExplorer::FileNameToContentsHash handleProcessFinished(Utils::QtcProcess *process) override;
+ ProjectExplorer::FileNameToContentsHash handleProcessFinished(Utils::Process *process) override;
};
class UicGeneratorFactory : public ProjectExplorer::ExtraCompilerFactory
diff --git a/src/plugins/remotelinux/CMakeLists.txt b/src/plugins/remotelinux/CMakeLists.txt
index 08581c2f67..f81f75350a 100644
--- a/src/plugins/remotelinux/CMakeLists.txt
+++ b/src/plugins/remotelinux/CMakeLists.txt
@@ -5,7 +5,6 @@ add_qtc_plugin(RemoteLinux
abstractremotelinuxdeploystep.cpp abstractremotelinuxdeploystep.h
customcommanddeploystep.cpp customcommanddeploystep.h
deploymenttimeinfo.cpp deploymenttimeinfo.h
- genericdirectuploadservice.cpp genericdirectuploadservice.h
genericdirectuploadstep.cpp genericdirectuploadstep.h
genericlinuxdeviceconfigurationwidget.cpp genericlinuxdeviceconfigurationwidget.h
genericlinuxdeviceconfigurationwizard.cpp genericlinuxdeviceconfigurationwizard.h
@@ -29,7 +28,6 @@ add_qtc_plugin(RemoteLinux
remotelinuxtr.h
rsyncdeploystep.cpp rsyncdeploystep.h
sshkeycreationdialog.cpp sshkeycreationdialog.h
- sshprocessinterface.h
tarpackagecreationstep.cpp tarpackagecreationstep.h
tarpackagedeploystep.cpp tarpackagedeploystep.h
)
diff --git a/src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp b/src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp
index 7e7e3d9a19..afd4e0e96a 100644
--- a/src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp
+++ b/src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp
@@ -12,149 +12,74 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/target.h>
+#include <solutions/tasking/tasktree.h>
+
#include <utils/qtcassert.h>
-#include <utils/tasktree.h>
#include <QDateTime>
#include <QPointer>
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
namespace RemoteLinux {
namespace Internal {
-class AbstractRemoteLinuxDeployServicePrivate
-{
-public:
- IDevice::ConstPtr deviceConfiguration;
- QPointer<Target> target;
-
- DeploymentTimeInfo deployTimes;
- std::unique_ptr<TaskTree> m_taskTree;
-};
-
class AbstractRemoteLinuxDeployStepPrivate
{
public:
bool hasError;
std::function<CheckResult()> internalInit;
std::function<void()> runPreparer;
- AbstractRemoteLinuxDeployService *deployService = nullptr;
+
+ DeploymentTimeInfo deployTimes;
+ std::unique_ptr<TaskTree> m_taskTree;
};
} // Internal
using namespace Internal;
-AbstractRemoteLinuxDeployService::AbstractRemoteLinuxDeployService(QObject *parent)
- : QObject(parent), d(new AbstractRemoteLinuxDeployServicePrivate)
-{
-}
+AbstractRemoteLinuxDeployStep::AbstractRemoteLinuxDeployStep(BuildStepList *bsl, Id id)
+ : BuildStep(bsl, id), d(new AbstractRemoteLinuxDeployStepPrivate)
+{}
-AbstractRemoteLinuxDeployService::~AbstractRemoteLinuxDeployService()
+AbstractRemoteLinuxDeployStep::~AbstractRemoteLinuxDeployStep()
{
delete d;
}
-const Target *AbstractRemoteLinuxDeployService::target() const
-{
- return d->target;
-}
-
-const Kit *AbstractRemoteLinuxDeployService::kit() const
-{
- return d->target ? d->target->kit() : nullptr;
-}
-
-IDevice::ConstPtr AbstractRemoteLinuxDeployService::deviceConfiguration() const
+IDevice::ConstPtr AbstractRemoteLinuxDeployStep::deviceConfiguration() const
{
- return d->deviceConfiguration;
+ return DeviceKitAspect::device(kit());
}
-void AbstractRemoteLinuxDeployService::saveDeploymentTimeStamp(const DeployableFile &deployableFile,
+void AbstractRemoteLinuxDeployStep::saveDeploymentTimeStamp(const DeployableFile &deployableFile,
const QDateTime &remoteTimestamp)
{
d->deployTimes.saveDeploymentTimeStamp(deployableFile, kit(), remoteTimestamp);
}
-bool AbstractRemoteLinuxDeployService::hasLocalFileChanged(
+bool AbstractRemoteLinuxDeployStep::hasLocalFileChanged(
const DeployableFile &deployableFile) const
{
return d->deployTimes.hasLocalFileChanged(deployableFile, kit());
}
-bool AbstractRemoteLinuxDeployService::hasRemoteFileChanged(
+bool AbstractRemoteLinuxDeployStep::hasRemoteFileChanged(
const DeployableFile &deployableFile, const QDateTime &remoteTimestamp) const
{
return d->deployTimes.hasRemoteFileChanged(deployableFile, kit(), remoteTimestamp);
}
-void AbstractRemoteLinuxDeployService::setTarget(Target *target)
-{
- d->target = target;
- d->deviceConfiguration = DeviceKitAspect::device(kit());
-}
-
-void AbstractRemoteLinuxDeployService::start()
-{
- QTC_ASSERT(!d->m_taskTree, return);
-
- const CheckResult check = isDeploymentPossible();
- if (!check) {
- emit errorMessage(check.errorMessage());
- emit finished();
- return;
- }
-
- if (!isDeploymentNecessary()) {
- emit progressMessage(Tr::tr("No deployment action necessary. Skipping."));
- emit finished();
- return;
- }
-
- d->m_taskTree.reset(new TaskTree(deployRecipe()));
- const auto endHandler = [this] {
- d->m_taskTree.release()->deleteLater();
- emit finished();
- };
- connect(d->m_taskTree.get(), &TaskTree::done, this, endHandler);
- connect(d->m_taskTree.get(), &TaskTree::errorOccurred, this, endHandler);
- d->m_taskTree->start();
-}
-
-void AbstractRemoteLinuxDeployService::stop()
-{
- if (!d->m_taskTree)
- return;
- d->m_taskTree.reset();
- emit finished();
-}
-
-CheckResult AbstractRemoteLinuxDeployService::isDeploymentPossible() const
+CheckResult AbstractRemoteLinuxDeployStep::isDeploymentPossible() const
{
if (!deviceConfiguration())
return CheckResult::failure(Tr::tr("No device configuration set."));
return CheckResult::success();
}
-QVariantMap AbstractRemoteLinuxDeployService::exportDeployTimes() const
-{
- return d->deployTimes.exportDeployTimes();
-}
-
-void AbstractRemoteLinuxDeployService::importDeployTimes(const QVariantMap &map)
-{
- d->deployTimes.importDeployTimes(map);
-}
-
-
-
-AbstractRemoteLinuxDeployStep::AbstractRemoteLinuxDeployStep(BuildStepList *bsl, Utils::Id id)
- : BuildStep(bsl, id), d(new Internal::AbstractRemoteLinuxDeployStepPrivate)
-{
-}
-
void AbstractRemoteLinuxDeployStep::setInternalInitializer(const std::function<CheckResult ()> &init)
{
d->internalInit = init;
@@ -165,36 +90,23 @@ void AbstractRemoteLinuxDeployStep::setRunPreparer(const std::function<void ()>
d->runPreparer = prep;
}
-void AbstractRemoteLinuxDeployStep::setDeployService(AbstractRemoteLinuxDeployService *service)
-{
- d->deployService = service;
-}
-
-AbstractRemoteLinuxDeployStep::~AbstractRemoteLinuxDeployStep()
-{
- delete d->deployService;
- delete d;
-}
-
bool AbstractRemoteLinuxDeployStep::fromMap(const QVariantMap &map)
{
if (!BuildStep::fromMap(map))
return false;
- d->deployService->importDeployTimes(map);
+ d->deployTimes.importDeployTimes(map);
return true;
}
QVariantMap AbstractRemoteLinuxDeployStep::toMap() const
{
QVariantMap map = BuildStep::toMap();
- map.insert(d->deployService->exportDeployTimes());
+ map.insert(d->deployTimes.exportDeployTimes());
return map;
}
bool AbstractRemoteLinuxDeployStep::init()
{
- d->deployService->setTarget(target());
-
QTC_ASSERT(d->internalInit, return false);
const CheckResult canDeploy = d->internalInit();
if (!canDeploy) {
@@ -209,21 +121,31 @@ void AbstractRemoteLinuxDeployStep::doRun()
if (d->runPreparer)
d->runPreparer();
- connect(d->deployService, &AbstractRemoteLinuxDeployService::errorMessage,
- this, &AbstractRemoteLinuxDeployStep::handleErrorMessage);
- connect(d->deployService, &AbstractRemoteLinuxDeployService::progressMessage,
- this, &AbstractRemoteLinuxDeployStep::handleProgressMessage);
- connect(d->deployService, &AbstractRemoteLinuxDeployService::warningMessage,
- this, &AbstractRemoteLinuxDeployStep::handleWarningMessage);
- connect(d->deployService, &AbstractRemoteLinuxDeployService::stdOutData,
- this, &AbstractRemoteLinuxDeployStep::handleStdOutData);
- connect(d->deployService, &AbstractRemoteLinuxDeployService::stdErrData,
- this, &AbstractRemoteLinuxDeployStep::handleStdErrData);
- connect(d->deployService, &AbstractRemoteLinuxDeployService::finished,
- this, &AbstractRemoteLinuxDeployStep::handleFinished);
-
d->hasError = false;
- d->deployService->start();
+
+ QTC_ASSERT(!d->m_taskTree, return);
+
+ const CheckResult check = isDeploymentPossible();
+ if (!check) {
+ addErrorMessage(check.errorMessage());
+ handleFinished();
+ return;
+ }
+
+ if (!isDeploymentNecessary()) {
+ addProgressMessage(Tr::tr("No deployment action necessary. Skipping."));
+ handleFinished();
+ return;
+ }
+
+ d->m_taskTree.reset(new TaskTree(deployRecipe()));
+ const auto endHandler = [this] {
+ d->m_taskTree.release()->deleteLater();
+ handleFinished();
+ };
+ connect(d->m_taskTree.get(), &TaskTree::done, this, endHandler);
+ connect(d->m_taskTree.get(), &TaskTree::errorOccurred, this, endHandler);
+ d->m_taskTree->start();
}
void AbstractRemoteLinuxDeployStep::doCancel()
@@ -234,22 +156,26 @@ void AbstractRemoteLinuxDeployStep::doCancel()
emit addOutput(Tr::tr("User requests deployment to stop; cleaning up."),
OutputFormat::NormalMessage);
d->hasError = true;
- d->deployService->stop();
+
+ if (!d->m_taskTree)
+ return;
+ d->m_taskTree.reset();
+ handleFinished();
}
-void AbstractRemoteLinuxDeployStep::handleProgressMessage(const QString &message)
+void AbstractRemoteLinuxDeployStep::addProgressMessage(const QString &message)
{
emit addOutput(message, OutputFormat::NormalMessage);
}
-void AbstractRemoteLinuxDeployStep::handleErrorMessage(const QString &message)
+void AbstractRemoteLinuxDeployStep::addErrorMessage(const QString &message)
{
emit addOutput(message, OutputFormat::ErrorMessage);
emit addTask(DeploymentTask(Task::Error, message), 1); // TODO correct?
d->hasError = true;
}
-void AbstractRemoteLinuxDeployStep::handleWarningMessage(const QString &message)
+void AbstractRemoteLinuxDeployStep::addWarningMessage(const QString &message)
{
emit addOutput(message, OutputFormat::ErrorMessage);
emit addTask(DeploymentTask(Task::Warning, message), 1); // TODO correct?
@@ -261,7 +187,7 @@ void AbstractRemoteLinuxDeployStep::handleFinished()
emit addOutput(Tr::tr("Deploy step failed."), OutputFormat::ErrorMessage);
else
emit addOutput(Tr::tr("Deploy step finished."), OutputFormat::NormalMessage);
- disconnect(d->deployService, nullptr, this, nullptr);
+
emit finished(!d->hasError);
}
@@ -275,4 +201,14 @@ void AbstractRemoteLinuxDeployStep::handleStdErrData(const QString &data)
emit addOutput(data, OutputFormat::Stderr, DontAppendNewline);
}
+bool AbstractRemoteLinuxDeployStep::isDeploymentNecessary() const
+{
+ return true;
+}
+
+Group AbstractRemoteLinuxDeployStep::deployRecipe()
+{
+ return {};
+}
+
} // namespace RemoteLinux
diff --git a/src/plugins/remotelinux/abstractremotelinuxdeploystep.h b/src/plugins/remotelinux/abstractremotelinuxdeploystep.h
index 67855064bb..b0af97f5d2 100644
--- a/src/plugins/remotelinux/abstractremotelinuxdeploystep.h
+++ b/src/plugins/remotelinux/abstractremotelinuxdeploystep.h
@@ -8,55 +8,14 @@
#include <projectexplorer/buildstep.h>
#include <projectexplorer/devicesupport/idevicefwd.h>
-#include <QtCore/qcontainerfwd.h>
#include <QObject>
-namespace ProjectExplorer {
-class DeployableFile;
-class Kit;
-class Target;
-}
-
-namespace Utils::Tasking { class Group; }
+namespace ProjectExplorer { class DeployableFile; }
+namespace Tasking { class Group; }
namespace RemoteLinux {
-class AbstractRemoteLinuxDeployService;
-class CheckResult;
-
namespace Internal { class AbstractRemoteLinuxDeployStepPrivate; }
-namespace Internal { class AbstractRemoteLinuxDeployServicePrivate; }
-
-class REMOTELINUX_EXPORT AbstractRemoteLinuxDeployStep : public ProjectExplorer::BuildStep
-{
- Q_OBJECT
-
-public:
- ~AbstractRemoteLinuxDeployStep() override;
-
-protected:
- bool fromMap(const QVariantMap &map) override;
- QVariantMap toMap() const override;
- bool init() override;
- void doRun() final;
- void doCancel() override;
-
- explicit AbstractRemoteLinuxDeployStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id);
-
- void setInternalInitializer(const std::function<CheckResult()> &init);
- void setRunPreparer(const std::function<void()> &prep);
- void setDeployService(AbstractRemoteLinuxDeployService *service);
-
-private:
- void handleProgressMessage(const QString &message);
- void handleErrorMessage(const QString &message);
- void handleWarningMessage(const QString &message);
- void handleFinished();
- void handleStdOutData(const QString &data);
- void handleStdErrData(const QString &data);
-
- Internal::AbstractRemoteLinuxDeployStepPrivate *d;
-};
class REMOTELINUX_EXPORT CheckResult
{
@@ -74,36 +33,28 @@ private:
QString m_error;
};
-class REMOTELINUX_EXPORT AbstractRemoteLinuxDeployService : public QObject
+class REMOTELINUX_EXPORT AbstractRemoteLinuxDeployStep : public ProjectExplorer::BuildStep
{
- Q_OBJECT
- Q_DISABLE_COPY(AbstractRemoteLinuxDeployService)
public:
- explicit AbstractRemoteLinuxDeployService(QObject *parent = nullptr);
- ~AbstractRemoteLinuxDeployService() override;
-
- void setTarget(ProjectExplorer::Target *bc);
-
- void start();
- void stop();
+ explicit AbstractRemoteLinuxDeployStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id);
+ ~AbstractRemoteLinuxDeployStep() override;
- QVariantMap exportDeployTimes() const;
- void importDeployTimes(const QVariantMap &map);
+ ProjectExplorer::IDeviceConstPtr deviceConfiguration() const;
virtual CheckResult isDeploymentPossible() const;
-signals:
- void errorMessage(const QString &message);
- void progressMessage(const QString &message);
- void warningMessage(const QString &message);
- void stdOutData(const QString &data);
- void stdErrData(const QString &data);
- void finished(); // Used by Qnx.
+ void handleStdOutData(const QString &data);
+ void handleStdErrData(const QString &data);
protected:
- const ProjectExplorer::Target *target() const;
- const ProjectExplorer::Kit *kit() const;
- ProjectExplorer::IDeviceConstPtr deviceConfiguration() const;
+ bool fromMap(const QVariantMap &map) override;
+ QVariantMap toMap() const override;
+ bool init() override;
+ void doRun() final;
+ void doCancel() override;
+
+ void setInternalInitializer(const std::function<CheckResult()> &init);
+ void setRunPreparer(const std::function<void()> &prep);
void saveDeploymentTimeStamp(const ProjectExplorer::DeployableFile &deployableFile,
const QDateTime &remoteTimestamp);
@@ -111,11 +62,17 @@ protected:
bool hasRemoteFileChanged(const ProjectExplorer::DeployableFile &deployableFile,
const QDateTime &remoteTimestamp) const;
+ void addProgressMessage(const QString &message);
+ void addErrorMessage(const QString &message);
+ void addWarningMessage(const QString &message);
+
+ void handleFinished();
+
private:
- virtual bool isDeploymentNecessary() const = 0;
- virtual Utils::Tasking::Group deployRecipe() = 0;
+ virtual bool isDeploymentNecessary() const;
+ virtual Tasking::Group deployRecipe();
- Internal::AbstractRemoteLinuxDeployServicePrivate * const d;
+ Internal::AbstractRemoteLinuxDeployStepPrivate *d;
};
} // RemoteLinux
diff --git a/src/plugins/remotelinux/customcommanddeploystep.cpp b/src/plugins/remotelinux/customcommanddeploystep.cpp
index b7abdd8304..32076d34f2 100644
--- a/src/plugins/remotelinux/customcommanddeploystep.cpp
+++ b/src/plugins/remotelinux/customcommanddeploystep.cpp
@@ -11,95 +11,79 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/runconfigurationaspects.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
-using namespace Utils::Tasking;
namespace RemoteLinux::Internal {
-class CustomCommandDeployService : public AbstractRemoteLinuxDeployService
+class CustomCommandDeployStep : public AbstractRemoteLinuxDeployStep
{
public:
- void setCommandLine(const QString &commandLine);
- CheckResult isDeploymentPossible() const final;
+ CustomCommandDeployStep(BuildStepList *bsl, Id id)
+ : AbstractRemoteLinuxDeployStep(bsl, id)
+ {
+ auto commandLine = addAspect<StringAspect>();
+ commandLine->setSettingsKey("RemoteLinuxCustomCommandDeploymentStep.CommandLine");
+ commandLine->setLabelText(Tr::tr("Command line:"));
+ commandLine->setDisplayStyle(StringAspect::LineEditDisplay);
+ commandLine->setHistoryCompleter("RemoteLinuxCustomCommandDeploymentStep.History");
-protected:
- Group deployRecipe() final;
+ setInternalInitializer([this, commandLine] {
+ m_commandLine = commandLine->value().trimmed();
+ return isDeploymentPossible();
+ });
+
+ addMacroExpander();
+ }
+
+ CheckResult isDeploymentPossible() const final;
private:
- bool isDeploymentNecessary() const final { return true; }
+ Group deployRecipe() final;
QString m_commandLine;
};
-void CustomCommandDeployService::setCommandLine(const QString &commandLine)
-{
- m_commandLine = commandLine;
-}
-
-CheckResult CustomCommandDeployService::isDeploymentPossible() const
+CheckResult CustomCommandDeployStep::isDeploymentPossible() const
{
if (m_commandLine.isEmpty())
return CheckResult::failure(Tr::tr("No command line given."));
- return AbstractRemoteLinuxDeployService::isDeploymentPossible();
+ return AbstractRemoteLinuxDeployStep::isDeploymentPossible();
}
-Group CustomCommandDeployService::deployRecipe()
+Group CustomCommandDeployStep::deployRecipe()
{
- const auto setupHandler = [this](QtcProcess &process) {
- emit progressMessage(Tr::tr("Starting remote command \"%1\"...").arg(m_commandLine));
+ const auto setupHandler = [this](Process &process) {
+ addProgressMessage(Tr::tr("Starting remote command \"%1\"...").arg(m_commandLine));
process.setCommand({deviceConfiguration()->filePath("/bin/sh"),
{"-c", m_commandLine}});
- QtcProcess *proc = &process;
- connect(proc, &QtcProcess::readyReadStandardOutput, this, [this, proc] {
- emit stdOutData(proc->readAllStandardOutput());
+ Process *proc = &process;
+ connect(proc, &Process::readyReadStandardOutput, this, [this, proc] {
+ handleStdOutData(proc->readAllStandardOutput());
});
- connect(proc, &QtcProcess::readyReadStandardError, this, [this, proc] {
- emit stdErrData(proc->readAllStandardError());
+ connect(proc, &Process::readyReadStandardError, this, [this, proc] {
+ handleStdErrData(proc->readAllStandardError());
});
};
- const auto doneHandler = [this](const QtcProcess &) {
- emit progressMessage(Tr::tr("Remote command finished successfully."));
+ const auto doneHandler = [this](const Process &) {
+ addProgressMessage(Tr::tr("Remote command finished successfully."));
};
- const auto errorHandler = [this](const QtcProcess &process) {
+ const auto errorHandler = [this](const Process &process) {
if (process.error() != QProcess::UnknownError
|| process.exitStatus() != QProcess::NormalExit) {
- emit errorMessage(Tr::tr("Remote process failed: %1").arg(process.errorString()));
+ addErrorMessage(Tr::tr("Remote process failed: %1").arg(process.errorString()));
} else if (process.exitCode() != 0) {
- emit errorMessage(Tr::tr("Remote process finished with exit code %1.")
+ addErrorMessage(Tr::tr("Remote process finished with exit code %1.")
.arg(process.exitCode()));
}
};
- return Group { Process(setupHandler, doneHandler, errorHandler) };
+ return Group { ProcessTask(setupHandler, doneHandler, errorHandler) };
}
-class CustomCommandDeployStep : public AbstractRemoteLinuxDeployStep
-{
-public:
- CustomCommandDeployStep(BuildStepList *bsl, Id id)
- : AbstractRemoteLinuxDeployStep(bsl, id)
- {
- auto service = new CustomCommandDeployService;
- setDeployService(service);
-
- auto commandLine = addAspect<StringAspect>();
- commandLine->setSettingsKey("RemoteLinuxCustomCommandDeploymentStep.CommandLine");
- commandLine->setLabelText(Tr::tr("Command line:"));
- commandLine->setDisplayStyle(StringAspect::LineEditDisplay);
- commandLine->setHistoryCompleter("RemoteLinuxCustomCommandDeploymentStep.History");
-
- setInternalInitializer([service, commandLine] {
- service->setCommandLine(commandLine->value().trimmed());
- return service->isDeploymentPossible();
- });
-
- addMacroExpander();
- }
-};
-
// CustomCommandDeployStepFactory
diff --git a/src/plugins/remotelinux/filesystemaccess_test.cpp b/src/plugins/remotelinux/filesystemaccess_test.cpp
index fcc8477974..2edb78fa7b 100644
--- a/src/plugins/remotelinux/filesystemaccess_test.cpp
+++ b/src/plugins/remotelinux/filesystemaccess_test.cpp
@@ -9,7 +9,11 @@
#include <projectexplorer/devicesupport/filetransfer.h>
#include <projectexplorer/devicesupport/sshparameters.h>
#include <utils/filepath.h>
+#include <utils/filestreamer.h>
+#include <utils/filestreamermanager.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
+#include <utils/scopedtimer.h>
#include <QDebug>
#include <QFile>
@@ -86,6 +90,45 @@ void FileSystemAccessTest::initTestCase()
QVERIFY(!filePath.exists());
QVERIFY(filePath.createDir());
QVERIFY(filePath.exists());
+
+ const QString streamerLocalDir("streamerLocalDir");
+ const QString streamerRemoteDir("streamerRemoteDir");
+ const QString sourceDir("source");
+ const QString destDir("dest");
+ const QString localDir("local");
+ const QString remoteDir("remote");
+ const FilePath localRoot;
+ const FilePath remoteRoot = m_device->rootPath();
+ const FilePath localTempDir = *localRoot.tmpDir();
+ const FilePath remoteTempDir = *remoteRoot.tmpDir();
+ m_localStreamerDir = localTempDir / streamerLocalDir;
+ m_remoteStreamerDir = remoteTempDir / streamerRemoteDir;
+ m_localSourceDir = m_localStreamerDir / sourceDir;
+ m_remoteSourceDir = m_remoteStreamerDir / sourceDir;
+ m_localDestDir = m_localStreamerDir / destDir;
+ m_remoteDestDir = m_remoteStreamerDir / destDir;
+ m_localLocalDestDir = m_localDestDir / localDir;
+ m_localRemoteDestDir = m_localDestDir / remoteDir;
+ m_remoteLocalDestDir = m_remoteDestDir / localDir;
+ m_remoteRemoteDestDir = m_remoteDestDir / remoteDir;
+
+ QVERIFY(m_localSourceDir.createDir());
+ QVERIFY(m_remoteSourceDir.createDir());
+ QVERIFY(m_localDestDir.createDir());
+ QVERIFY(m_remoteDestDir.createDir());
+ QVERIFY(m_localLocalDestDir.createDir());
+ QVERIFY(m_localRemoteDestDir.createDir());
+ QVERIFY(m_remoteLocalDestDir.createDir());
+ QVERIFY(m_remoteRemoteDestDir.createDir());
+
+ QVERIFY(m_localSourceDir.exists());
+ QVERIFY(m_remoteSourceDir.exists());
+ QVERIFY(m_localDestDir.exists());
+ QVERIFY(m_remoteDestDir.exists());
+ QVERIFY(m_localLocalDestDir.exists());
+ QVERIFY(m_localRemoteDestDir.exists());
+ QVERIFY(m_remoteLocalDestDir.exists());
+ QVERIFY(m_remoteRemoteDestDir.exists());
}
void FileSystemAccessTest::cleanupTestCase()
@@ -94,6 +137,14 @@ void FileSystemAccessTest::cleanupTestCase()
return;
QVERIFY(baseFilePath().exists());
QVERIFY(baseFilePath().removeRecursively());
+
+ QVERIFY(m_localStreamerDir.removeRecursively());
+ QVERIFY(m_remoteStreamerDir.removeRecursively());
+
+ QVERIFY(!m_localStreamerDir.exists());
+ QVERIFY(!m_remoteStreamerDir.exists());
+
+ FileStreamerManager::stopAll();
}
void FileSystemAccessTest::testCreateRemoteFile_data()
@@ -102,13 +153,13 @@ void FileSystemAccessTest::testCreateRemoteFile_data()
QTest::newRow("Spaces") << QByteArray("Line with spaces");
QTest::newRow("Newlines") << QByteArray("Some \n\n newlines \n");
- QTest::newRow("Carriage return") << QByteArray("Line with carriage \r return");
+ QTest::newRow("CarriageReturn") << QByteArray("Line with carriage \r return");
QTest::newRow("Tab") << QByteArray("Line with \t tab");
QTest::newRow("Apostrophe") << QByteArray("Line with apostrophe's character");
- QTest::newRow("Quotation marks") << QByteArray("Line with \"quotation marks\"");
- QTest::newRow("Backslash 1") << QByteArray("Line with \\ backslash");
- QTest::newRow("Backslash 2") << QByteArray("Line with \\\" backslash");
- QTest::newRow("Command output") << QByteArray("The date is: $(date +%D)");
+ QTest::newRow("QuotationMarks") << QByteArray("Line with \"quotation marks\"");
+ QTest::newRow("Backslash1") << QByteArray("Line with \\ backslash");
+ QTest::newRow("Backslash2") << QByteArray("Line with \\\" backslash");
+ QTest::newRow("CommandOutput") << QByteArray("The date is: $(date +%D)");
const int charSize = sizeof(char) * 0x100;
QByteArray charString(charSize, Qt::Uninitialized);
@@ -132,6 +183,21 @@ void FileSystemAccessTest::testCreateRemoteFile()
QVERIFY(!testFilePath.exists());
}
+void FileSystemAccessTest::testWorkingDirectory()
+{
+ const FilePath dir = baseFilePath() / "testdir with space and 'various' \"quotes\" here";
+ QVERIFY(dir.ensureWritableDir());
+ Process proc;
+ proc.setCommand({"pwd", {}});
+ proc.setWorkingDirectory(dir);
+ proc.start();
+ QVERIFY(proc.waitForFinished());
+ const QString out = proc.readAllStandardOutput().trimmed();
+ QCOMPARE(out, dir.path());
+ const QString err = proc.readAllStandardOutput();
+ QVERIFY(err.isEmpty());
+}
+
void FileSystemAccessTest::testDirStatus()
{
FilePath filePath = baseFilePath();
@@ -201,6 +267,8 @@ void FileSystemAccessTest::testFileTransfer_data()
QTest::addColumn<FileTransferMethod>("fileTransferMethod");
QTest::addRow("Sftp") << FileTransferMethod::Sftp;
+ // TODO: By default rsync doesn't support creating target directories,
+ // needs to be done manually - see RsyncDeployService.
// QTest::addRow("Rsync") << FileTransferMethod::Rsync;
}
@@ -282,5 +350,287 @@ void FileSystemAccessTest::testFileTransfer()
QVERIFY2(remoteDir.removeRecursively(&errorString), qPrintable(errorString));
}
+void FileSystemAccessTest::testFileStreamer_data()
+{
+ QTest::addColumn<QString>("fileName");
+ QTest::addColumn<QByteArray>("data");
+
+ const QByteArray spaces("Line with spaces");
+ const QByteArray newlines("Some \n\n newlines \n");
+ const QByteArray carriageReturn("Line with carriage \r return");
+ const QByteArray tab("Line with \t tab");
+ const QByteArray apostrophe("Line with apostrophe's character");
+ const QByteArray quotationMarks("Line with \"quotation marks\"");
+ const QByteArray backslash1("Line with \\ backslash");
+ const QByteArray backslash2("Line with \\\" backslash");
+ const QByteArray commandOutput("The date is: $(date +%D)");
+
+ const int charSize = sizeof(char) * 0x100;
+ QByteArray charString(charSize, Qt::Uninitialized);
+ char *data = charString.data();
+ for (int c = 0; c < charSize; ++c)
+ data[c] = c;
+
+ const int bigSize = 1024 * 1024; // = 256 * 1024 * 1024 = 268.435.456 bytes
+ QByteArray bigString;
+ for (int i = 0; i < bigSize; ++i)
+ bigString += charString;
+
+ QTest::newRow("Spaces") << QString("spaces") << spaces;
+ QTest::newRow("Newlines") << QString("newlines") << newlines;
+ QTest::newRow("CarriageReturn") << QString("carriageReturn") << carriageReturn;
+ QTest::newRow("Tab") << QString("tab") << tab;
+ QTest::newRow("Apostrophe") << QString("apostrophe") << apostrophe;
+ QTest::newRow("QuotationMarks") << QString("quotationMarks") << quotationMarks;
+ QTest::newRow("Backslash1") << QString("backslash1") << backslash1;
+ QTest::newRow("Backslash2") << QString("backslash2") << backslash2;
+ QTest::newRow("CommandOutput") << QString("commandOutput") << commandOutput;
+ QTest::newRow("AllCharacters") << QString("charString") << charString;
+ QTest::newRow("BigString") << QString("bigString") << bigString;
+}
+
+void FileSystemAccessTest::testFileStreamer()
+{
+ QTC_SCOPED_TIMER("testFileStreamer");
+
+ QFETCH(QString, fileName);
+ QFETCH(QByteArray, data);
+
+ const FilePath localSourcePath = m_localSourceDir / fileName;
+ const FilePath remoteSourcePath = m_remoteSourceDir / fileName;
+ const FilePath localLocalDestPath = m_localDestDir / "local" / fileName;
+ const FilePath localRemoteDestPath = m_localDestDir / "remote" / fileName;
+ const FilePath remoteLocalDestPath = m_remoteDestDir / "local" / fileName;
+ const FilePath remoteRemoteDestPath = m_remoteDestDir / "remote" / fileName;
+
+ localSourcePath.removeFile();
+ remoteSourcePath.removeFile();
+ localLocalDestPath.removeFile();
+ localRemoteDestPath.removeFile();
+ remoteLocalDestPath.removeFile();
+ remoteRemoteDestPath.removeFile();
+
+ QVERIFY(!localSourcePath.exists());
+ QVERIFY(!remoteSourcePath.exists());
+ QVERIFY(!localLocalDestPath.exists());
+ QVERIFY(!localRemoteDestPath.exists());
+ QVERIFY(!remoteLocalDestPath.exists());
+ QVERIFY(!remoteRemoteDestPath.exists());
+
+ std::optional<QByteArray> localData;
+ std::optional<QByteArray> remoteData;
+ std::optional<QByteArray> localLocalData;
+ std::optional<QByteArray> localRemoteData;
+ std::optional<QByteArray> remoteLocalData;
+ std::optional<QByteArray> remoteRemoteData;
+
+ using namespace Tasking;
+
+ const auto localWriter = [&] {
+ const auto setup = [&](FileStreamer &streamer) {
+ streamer.setStreamMode(StreamMode::Writer);
+ streamer.setDestination(localSourcePath);
+ streamer.setWriteData(data);
+ };
+ return FileStreamerTask(setup);
+ };
+ const auto remoteWriter = [&] {
+ const auto setup = [&](FileStreamer &streamer) {
+ streamer.setStreamMode(StreamMode::Writer);
+ streamer.setDestination(remoteSourcePath);
+ streamer.setWriteData(data);
+ };
+ return FileStreamerTask(setup);
+ };
+ const auto localReader = [&] {
+ const auto setup = [&](FileStreamer &streamer) {
+ streamer.setStreamMode(StreamMode::Reader);
+ streamer.setSource(localSourcePath);
+ };
+ const auto onDone = [&](const FileStreamer &streamer) {
+ localData = streamer.readData();
+ };
+ return FileStreamerTask(setup, onDone);
+ };
+ const auto remoteReader = [&] {
+ const auto setup = [&](FileStreamer &streamer) {
+ streamer.setStreamMode(StreamMode::Reader);
+ streamer.setSource(remoteSourcePath);
+ };
+ const auto onDone = [&](const FileStreamer &streamer) {
+ remoteData = streamer.readData();
+ };
+ return FileStreamerTask(setup, onDone);
+ };
+ const auto transfer = [](const FilePath &source, const FilePath &dest,
+ std::optional<QByteArray> *result) {
+ const auto setupTransfer = [=](FileStreamer &streamer) {
+ streamer.setSource(source);
+ streamer.setDestination(dest);
+ };
+ const auto setupReader = [=](FileStreamer &streamer) {
+ streamer.setStreamMode(StreamMode::Reader);
+ streamer.setSource(dest);
+ };
+ const auto onReaderDone = [result](const FileStreamer &streamer) {
+ *result = streamer.readData();
+ };
+ const Group root {
+ FileStreamerTask(setupTransfer),
+ FileStreamerTask(setupReader, onReaderDone)
+ };
+ return root;
+ };
+
+ // In total: 5 local reads, 3 local writes, 5 remote reads, 3 remote writes
+ const Group root {
+ Group {
+ parallel,
+ localWriter(),
+ remoteWriter()
+ },
+ Group {
+ parallel,
+ localReader(),
+ remoteReader()
+ },
+ Group {
+ parallel,
+ transfer(localSourcePath, localLocalDestPath, &localLocalData),
+ transfer(remoteSourcePath, localRemoteDestPath, &localRemoteData),
+ transfer(localSourcePath, remoteLocalDestPath, &remoteLocalData),
+ transfer(remoteSourcePath, remoteRemoteDestPath, &remoteRemoteData),
+ }
+ };
+
+ using namespace std::chrono_literals;
+ QVERIFY(TaskTree::runBlocking(root, 10000ms));
+
+ QVERIFY(localData);
+ QCOMPARE(*localData, data);
+ QVERIFY(remoteData);
+ QCOMPARE(*remoteData, data);
+
+ QVERIFY(localLocalData);
+ QCOMPARE(*localLocalData, data);
+ QVERIFY(localRemoteData);
+ QCOMPARE(*localRemoteData, data);
+ QVERIFY(remoteLocalData);
+ QCOMPARE(*remoteLocalData, data);
+ QVERIFY(remoteRemoteData);
+ QCOMPARE(*remoteRemoteData, data);
+}
+
+void FileSystemAccessTest::testFileStreamerManager_data()
+{
+ testFileStreamer_data();
+}
+
+void FileSystemAccessTest::testFileStreamerManager()
+{
+ QTC_SCOPED_TIMER("testFileStreamerManager");
+
+ QFETCH(QString, fileName);
+ QFETCH(QByteArray, data);
+
+ const FilePath localSourcePath = m_localSourceDir / fileName;
+ const FilePath remoteSourcePath = m_remoteSourceDir / fileName;
+ const FilePath localLocalDestPath = m_localDestDir / "local" / fileName;
+ const FilePath localRemoteDestPath = m_localDestDir / "remote" / fileName;
+ const FilePath remoteLocalDestPath = m_remoteDestDir / "local" / fileName;
+ const FilePath remoteRemoteDestPath = m_remoteDestDir / "remote" / fileName;
+
+ localSourcePath.removeFile();
+ remoteSourcePath.removeFile();
+ localLocalDestPath.removeFile();
+ localRemoteDestPath.removeFile();
+ remoteLocalDestPath.removeFile();
+ remoteRemoteDestPath.removeFile();
+
+ QVERIFY(!localSourcePath.exists());
+ QVERIFY(!remoteSourcePath.exists());
+ QVERIFY(!localLocalDestPath.exists());
+ QVERIFY(!localRemoteDestPath.exists());
+ QVERIFY(!remoteLocalDestPath.exists());
+ QVERIFY(!remoteRemoteDestPath.exists());
+
+ std::optional<QByteArray> localData;
+ std::optional<QByteArray> remoteData;
+ std::optional<QByteArray> localLocalData;
+ std::optional<QByteArray> localRemoteData;
+ std::optional<QByteArray> remoteLocalData;
+ std::optional<QByteArray> remoteRemoteData;
+
+ QEventLoop eventLoop1;
+ QEventLoop *loop = &eventLoop1;
+ int counter = 0;
+ int *hitCount = &counter;
+
+ const auto writeAndRead = [hitCount, loop, data](const FilePath &destination,
+ std::optional<QByteArray> *result) {
+ const auto onWrite = [hitCount, loop, destination, result]
+ (const expected_str<qint64> &writeResult) {
+ QVERIFY(writeResult);
+ const auto onRead = [hitCount, loop, result]
+ (const expected_str<QByteArray> &readResult) {
+ QVERIFY(readResult);
+ *result = *readResult;
+ ++(*hitCount);
+ if (*hitCount == 2)
+ loop->quit();
+ };
+ FileStreamerManager::read(destination, onRead);
+ };
+ FileStreamerManager::write(destination, data, onWrite);
+ };
+
+ writeAndRead(localSourcePath, &localData);
+ writeAndRead(remoteSourcePath, &remoteData);
+ loop->exec();
+
+ QVERIFY(localData);
+ QCOMPARE(*localData, data);
+ QVERIFY(remoteData);
+ QCOMPARE(*remoteData, data);
+
+ QEventLoop eventLoop2;
+ loop = &eventLoop2;
+ counter = 0;
+
+ const auto transferAndRead = [hitCount, loop, data](const FilePath &source,
+ const FilePath &destination,
+ std::optional<QByteArray> *result) {
+ const auto onTransfer = [hitCount, loop, destination, result]
+ (const expected_str<void> &transferResult) {
+ QVERIFY(transferResult);
+ const auto onRead = [hitCount, loop, result]
+ (const expected_str<QByteArray> &readResult) {
+ QVERIFY(readResult);
+ *result = *readResult;
+ ++(*hitCount);
+ if (*hitCount == 4)
+ loop->quit();
+ };
+ FileStreamerManager::read(destination, onRead);
+ };
+ FileStreamerManager::copy(source, destination, onTransfer);
+ };
+
+ transferAndRead(localSourcePath, localLocalDestPath, &localLocalData);
+ transferAndRead(remoteSourcePath, localRemoteDestPath, &localRemoteData);
+ transferAndRead(localSourcePath, remoteLocalDestPath, &remoteLocalData);
+ transferAndRead(remoteSourcePath, remoteRemoteDestPath, &remoteRemoteData);
+ loop->exec();
+
+ QVERIFY(localLocalData);
+ QCOMPARE(*localLocalData, data);
+ QVERIFY(localRemoteData);
+ QCOMPARE(*localRemoteData, data);
+ QVERIFY(remoteLocalData);
+ QCOMPARE(*remoteLocalData, data);
+ QVERIFY(remoteRemoteData);
+ QCOMPARE(*remoteRemoteData, data);
+}
+
} // Internal
} // RemoteLinux
diff --git a/src/plugins/remotelinux/filesystemaccess_test.h b/src/plugins/remotelinux/filesystemaccess_test.h
index 9684cbc926..087eabbcad 100644
--- a/src/plugins/remotelinux/filesystemaccess_test.h
+++ b/src/plugins/remotelinux/filesystemaccess_test.h
@@ -4,6 +4,7 @@
#pragma once
#include <projectexplorer/devicesupport/idevicefactory.h>
+#include <utils/filepath.h>
namespace RemoteLinux {
namespace Internal {
@@ -23,11 +24,16 @@ private slots:
void testCreateRemoteFile_data();
void testCreateRemoteFile();
+ void testWorkingDirectory();
void testDirStatus();
void testBytesAvailable();
void testFileActions();
void testFileTransfer_data();
void testFileTransfer();
+ void testFileStreamer_data();
+ void testFileStreamer();
+ void testFileStreamerManager_data();
+ void testFileStreamerManager();
void cleanupTestCase();
@@ -35,6 +41,16 @@ private:
TestLinuxDeviceFactory m_testLinuxDeviceFactory;
bool m_skippedAtWhole = false;
ProjectExplorer::IDeviceConstPtr m_device;
+ Utils::FilePath m_localStreamerDir;
+ Utils::FilePath m_remoteStreamerDir;
+ Utils::FilePath m_localSourceDir;
+ Utils::FilePath m_remoteSourceDir;
+ Utils::FilePath m_localDestDir;
+ Utils::FilePath m_remoteDestDir;
+ Utils::FilePath m_localLocalDestDir;
+ Utils::FilePath m_localRemoteDestDir;
+ Utils::FilePath m_remoteLocalDestDir;
+ Utils::FilePath m_remoteRemoteDestDir;
};
} // Internal
diff --git a/src/plugins/remotelinux/genericdirectuploadservice.cpp b/src/plugins/remotelinux/genericdirectuploadservice.cpp
deleted file mode 100644
index 6cd3c79937..0000000000
--- a/src/plugins/remotelinux/genericdirectuploadservice.cpp
+++ /dev/null
@@ -1,314 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "genericdirectuploadservice.h"
-
-#include "remotelinuxtr.h"
-
-#include <projectexplorer/deployablefile.h>
-#include <projectexplorer/devicesupport/filetransfer.h>
-#include <projectexplorer/devicesupport/idevice.h>
-
-#include <utils/hostosinfo.h>
-#include <utils/processinterface.h>
-#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-
-#include <QDateTime>
-#include <QDir>
-
-using namespace ProjectExplorer;
-using namespace Utils;
-using namespace Utils::Tasking;
-
-namespace RemoteLinux {
-namespace Internal {
-
-const int MaxConcurrentStatCalls = 10;
-
-struct UploadStorage
-{
- QList<DeployableFile> filesToUpload;
-};
-
-class GenericDirectUploadServicePrivate
-{
-public:
- GenericDirectUploadServicePrivate(GenericDirectUploadService *service) : q(service) {}
-
- QDateTime timestampFromStat(const DeployableFile &file, QtcProcess *statProc);
-
- using FilesToStat = std::function<QList<DeployableFile>(UploadStorage *)>;
- using StatEndHandler
- = std::function<void(UploadStorage *, const DeployableFile &, const QDateTime &)>;
- TaskItem statTask(UploadStorage *storage, const DeployableFile &file,
- StatEndHandler statEndHandler);
- TaskItem statTree(const TreeStorage<UploadStorage> &storage, FilesToStat filesToStat,
- StatEndHandler statEndHandler);
- TaskItem uploadTask(const TreeStorage<UploadStorage> &storage);
- TaskItem chmodTask(const DeployableFile &file);
- TaskItem chmodTree(const TreeStorage<UploadStorage> &storage);
-
- GenericDirectUploadService *q = nullptr;
- IncrementalDeployment incremental = IncrementalDeployment::NotSupported;
- bool ignoreMissingFiles = false;
- QList<DeployableFile> deployableFiles;
-};
-
-QList<DeployableFile> collectFilesToUpload(const DeployableFile &deployable)
-{
- QList<DeployableFile> collected;
- FilePath localFile = deployable.localFilePath();
- if (localFile.isDir()) {
- const FilePaths files = localFile.dirEntries(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
- const QString remoteDir = deployable.remoteDirectory() + '/' + localFile.fileName();
- for (const FilePath &localFilePath : files)
- collected.append(collectFilesToUpload(DeployableFile(localFilePath, remoteDir)));
- } else {
- collected << deployable;
- }
- return collected;
-}
-
-} // namespace Internal
-
-using namespace Internal;
-
-GenericDirectUploadService::GenericDirectUploadService(QObject *parent)
- : AbstractRemoteLinuxDeployService(parent), d(new GenericDirectUploadServicePrivate(this))
-{
-}
-
-GenericDirectUploadService::~GenericDirectUploadService()
-{
- delete d;
-}
-
-void GenericDirectUploadService::setDeployableFiles(const QList<DeployableFile> &deployableFiles)
-{
- d->deployableFiles = deployableFiles;
-}
-
-void GenericDirectUploadService::setIncrementalDeployment(IncrementalDeployment incremental)
-{
- d->incremental = incremental;
-}
-
-void GenericDirectUploadService::setIgnoreMissingFiles(bool ignoreMissingFiles)
-{
- d->ignoreMissingFiles = ignoreMissingFiles;
-}
-
-bool GenericDirectUploadService::isDeploymentNecessary() const
-{
- QList<DeployableFile> collected;
- for (int i = 0; i < d->deployableFiles.count(); ++i)
- collected.append(collectFilesToUpload(d->deployableFiles.at(i)));
-
- QTC_CHECK(collected.size() >= d->deployableFiles.size());
- d->deployableFiles = collected;
- return !d->deployableFiles.isEmpty();
-}
-
-QDateTime GenericDirectUploadServicePrivate::timestampFromStat(const DeployableFile &file,
- QtcProcess *statProc)
-{
- bool succeeded = false;
- QString error;
- if (statProc->error() == QProcess::FailedToStart) {
- error = Tr::tr("Failed to start \"stat\": %1").arg(statProc->errorString());
- } else if (statProc->exitStatus() == QProcess::CrashExit) {
- error = Tr::tr("\"stat\" crashed.");
- } else if (statProc->exitCode() != 0) {
- error = Tr::tr("\"stat\" failed with exit code %1: %2")
- .arg(statProc->exitCode()).arg(statProc->cleanedStdErr());
- } else {
- succeeded = true;
- }
- if (!succeeded) {
- emit q->warningMessage(Tr::tr("Failed to retrieve remote timestamp for file \"%1\". "
- "Incremental deployment will not work. Error message was: %2")
- .arg(file.remoteFilePath(), error));
- return {};
- }
- const QByteArray output = statProc->readAllRawStandardOutput().trimmed();
- const QString warningString(Tr::tr("Unexpected stat output for remote file \"%1\": %2")
- .arg(file.remoteFilePath()).arg(QString::fromUtf8(output)));
- if (!output.startsWith(file.remoteFilePath().toUtf8())) {
- emit q->warningMessage(warningString);
- return {};
- }
- const QByteArrayList columns = output.mid(file.remoteFilePath().toUtf8().size() + 1).split(' ');
- if (columns.size() < 14) { // Normal Linux stat: 16 columns in total, busybox stat: 15 columns
- emit q->warningMessage(warningString);
- return {};
- }
- bool isNumber;
- const qint64 secsSinceEpoch = columns.at(11).toLongLong(&isNumber);
- if (!isNumber) {
- emit q->warningMessage(warningString);
- return {};
- }
- return QDateTime::fromSecsSinceEpoch(secsSinceEpoch);
-}
-
-TaskItem GenericDirectUploadServicePrivate::statTask(UploadStorage *storage,
- const DeployableFile &file,
- StatEndHandler statEndHandler)
-{
- const auto setupHandler = [=](QtcProcess &process) {
- // We'd like to use --format=%Y, but it's not supported by busybox.
- process.setCommand({q->deviceConfiguration()->filePath("stat"),
- {"-t", Utils::ProcessArgs::quoteArgUnix(file.remoteFilePath())}});
- };
- const auto endHandler = [=](const QtcProcess &process) {
- QtcProcess *proc = const_cast<QtcProcess *>(&process);
- const QDateTime timestamp = timestampFromStat(file, proc);
- statEndHandler(storage, file, timestamp);
- };
- return Process(setupHandler, endHandler, endHandler);
-}
-
-TaskItem GenericDirectUploadServicePrivate::statTree(const TreeStorage<UploadStorage> &storage,
- FilesToStat filesToStat, StatEndHandler statEndHandler)
-{
- const auto setupHandler = [=](TaskTree &tree) {
- UploadStorage *storagePtr = storage.activeStorage();
- const QList<DeployableFile> files = filesToStat(storagePtr);
- QList<TaskItem> statList{optional, ParallelLimit(MaxConcurrentStatCalls)};
- for (const DeployableFile &file : std::as_const(files)) {
- QTC_ASSERT(file.isValid(), continue);
- statList.append(statTask(storagePtr, file, statEndHandler));
- }
- tree.setupRoot({statList});
- };
- return Tree(setupHandler);
-}
-
-TaskItem GenericDirectUploadServicePrivate::uploadTask(const TreeStorage<UploadStorage> &storage)
-{
- const auto setupHandler = [this, storage](FileTransfer &transfer) {
- if (storage->filesToUpload.isEmpty()) {
- emit q->progressMessage(Tr::tr("No files need to be uploaded."));
- return TaskAction::StopWithDone;
- }
- emit q->progressMessage(Tr::tr("%n file(s) need to be uploaded.", "",
- storage->filesToUpload.size()));
- FilesToTransfer files;
- for (const DeployableFile &file : std::as_const(storage->filesToUpload)) {
- if (!file.localFilePath().exists()) {
- const QString message = Tr::tr("Local file \"%1\" does not exist.")
- .arg(file.localFilePath().toUserOutput());
- if (ignoreMissingFiles) {
- emit q->warningMessage(message);
- continue;
- }
- emit q->errorMessage(message);
- return TaskAction::StopWithError;
- }
- files.append({file.localFilePath(),
- q->deviceConfiguration()->filePath(file.remoteFilePath())});
- }
- if (files.isEmpty()) {
- emit q->progressMessage(Tr::tr("No files need to be uploaded."));
- return TaskAction::StopWithDone;
- }
- transfer.setFilesToTransfer(files);
- QObject::connect(&transfer, &FileTransfer::progress,
- q, &GenericDirectUploadService::progressMessage);
- return TaskAction::Continue;
- };
- const auto errorHandler = [this](const FileTransfer &transfer) {
- emit q->errorMessage(transfer.resultData().m_errorString);
- };
-
- return Transfer(setupHandler, {}, errorHandler);
-}
-
-TaskItem GenericDirectUploadServicePrivate::chmodTask(const DeployableFile &file)
-{
- const auto setupHandler = [=](QtcProcess &process) {
- process.setCommand({q->deviceConfiguration()->filePath("chmod"),
- {"a+x", Utils::ProcessArgs::quoteArgUnix(file.remoteFilePath())}});
- };
- const auto errorHandler = [=](const QtcProcess &process) {
- const QString error = process.errorString();
- if (!error.isEmpty()) {
- emit q->warningMessage(Tr::tr("Remote chmod failed for file \"%1\": %2")
- .arg(file.remoteFilePath(), error));
- } else if (process.exitCode() != 0) {
- emit q->warningMessage(Tr::tr("Remote chmod failed for file \"%1\": %2")
- .arg(file.remoteFilePath(), process.cleanedStdErr()));
- }
- };
- return Process(setupHandler, {}, errorHandler);
-}
-
-TaskItem GenericDirectUploadServicePrivate::chmodTree(const TreeStorage<UploadStorage> &storage)
-{
- const auto setupChmodHandler = [=](TaskTree &tree) {
- QList<DeployableFile> filesToChmod;
- for (const DeployableFile &file : std::as_const(storage->filesToUpload)) {
- if (file.isExecutable())
- filesToChmod << file;
- }
- QList<TaskItem> chmodList{optional, ParallelLimit(MaxConcurrentStatCalls)};
- for (const DeployableFile &file : std::as_const(filesToChmod)) {
- QTC_ASSERT(file.isValid(), continue);
- chmodList.append(chmodTask(file));
- }
- tree.setupRoot({chmodList});
- };
- return Tree(setupChmodHandler);
-}
-
-Group GenericDirectUploadService::deployRecipe()
-{
- const auto preFilesToStat = [this](UploadStorage *storage) {
- QList<DeployableFile> filesToStat;
- for (const DeployableFile &file : std::as_const(d->deployableFiles)) {
- if (d->incremental != IncrementalDeployment::Enabled || hasLocalFileChanged(file)) {
- storage->filesToUpload.append(file);
- continue;
- }
- if (d->incremental == IncrementalDeployment::NotSupported)
- continue;
- filesToStat << file;
- }
- return filesToStat;
- };
- const auto preStatEndHandler = [this](UploadStorage *storage, const DeployableFile &file,
- const QDateTime &timestamp) {
- if (!timestamp.isValid() || hasRemoteFileChanged(file, timestamp))
- storage->filesToUpload.append(file);
- };
-
- const auto postFilesToStat = [this](UploadStorage *storage) {
- return d->incremental == IncrementalDeployment::NotSupported
- ? QList<DeployableFile>() : storage->filesToUpload;
- };
- const auto postStatEndHandler = [this](UploadStorage *storage, const DeployableFile &file,
- const QDateTime &timestamp) {
- Q_UNUSED(storage)
- if (timestamp.isValid())
- saveDeploymentTimeStamp(file, timestamp);
- };
- const auto doneHandler = [this] {
- emit progressMessage(Tr::tr("All files successfully deployed."));
- };
-
- const TreeStorage<UploadStorage> storage;
- const Group root {
- Storage(storage),
- d->statTree(storage, preFilesToStat, preStatEndHandler),
- d->uploadTask(storage),
- Group {
- d->chmodTree(storage),
- d->statTree(storage, postFilesToStat, postStatEndHandler)
- },
- OnGroupDone(doneHandler)
- };
- return root;
-}
-
-} //namespace RemoteLinux
diff --git a/src/plugins/remotelinux/genericdirectuploadservice.h b/src/plugins/remotelinux/genericdirectuploadservice.h
deleted file mode 100644
index 31799f063a..0000000000
--- a/src/plugins/remotelinux/genericdirectuploadservice.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "remotelinux_export.h"
-
-#include "abstractremotelinuxdeploystep.h"
-
-#include <QList>
-
-namespace ProjectExplorer { class DeployableFile; }
-
-namespace RemoteLinux {
-namespace Internal { class GenericDirectUploadServicePrivate; }
-
-enum class IncrementalDeployment { Enabled, Disabled, NotSupported };
-
-class REMOTELINUX_EXPORT GenericDirectUploadService : public AbstractRemoteLinuxDeployService
-{
- Q_OBJECT
-public:
- GenericDirectUploadService(QObject *parent = nullptr);
- ~GenericDirectUploadService();
-
- void setDeployableFiles(const QList<ProjectExplorer::DeployableFile> &deployableFiles);
- void setIncrementalDeployment(IncrementalDeployment incremental);
- void setIgnoreMissingFiles(bool ignoreMissingFiles);
-
-private:
- bool isDeploymentNecessary() const final;
- Utils::Tasking::Group deployRecipe() final;
-
- friend class Internal::GenericDirectUploadServicePrivate;
- Internal::GenericDirectUploadServicePrivate * const d;
-};
-
-} //namespace RemoteLinux
diff --git a/src/plugins/remotelinux/genericdirectuploadstep.cpp b/src/plugins/remotelinux/genericdirectuploadstep.cpp
index 2b778e2b9c..6074eaabaa 100644
--- a/src/plugins/remotelinux/genericdirectuploadstep.cpp
+++ b/src/plugins/remotelinux/genericdirectuploadstep.cpp
@@ -2,69 +2,325 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "genericdirectuploadstep.h"
+#include "abstractremotelinuxdeploystep.h"
-#include "genericdirectuploadservice.h"
#include "remotelinux_constants.h"
#include "remotelinuxtr.h"
+#include <projectexplorer/deployablefile.h>
#include <projectexplorer/deploymentdata.h>
-#include <projectexplorer/target.h>
+#include <projectexplorer/devicesupport/filetransfer.h>
+#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/runconfigurationaspects.h>
+#include <projectexplorer/target.h>
+
+#include <utils/hostosinfo.h>
+#include <utils/process.h>
+#include <utils/processinterface.h>
+#include <utils/qtcassert.h>
+
+#include <QDateTime>
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
-namespace RemoteLinux {
+namespace RemoteLinux::Internal {
-GenericDirectUploadStep::GenericDirectUploadStep(BuildStepList *bsl, Utils::Id id,
- bool offerIncrementalDeployment)
- : AbstractRemoteLinuxDeployStep(bsl, id)
+const int MaxConcurrentStatCalls = 10;
+
+struct UploadStorage
{
- auto service = new GenericDirectUploadService;
- setDeployService(service);
+ QList<DeployableFile> filesToUpload;
+};
+
+enum class IncrementalDeployment { Enabled, Disabled, NotSupported };
- BoolAspect *incremental = nullptr;
- if (offerIncrementalDeployment) {
- incremental = addAspect<BoolAspect>();
+class GenericDirectUploadStep : public AbstractRemoteLinuxDeployStep
+{
+public:
+ GenericDirectUploadStep(ProjectExplorer::BuildStepList *bsl, Id id)
+ : AbstractRemoteLinuxDeployStep(bsl, id)
+ {
+ auto incremental = addAspect<BoolAspect>();
incremental->setSettingsKey("RemoteLinux.GenericDirectUploadStep.Incremental");
incremental->setLabel(Tr::tr("Incremental deployment"),
BoolAspect::LabelPlacement::AtCheckBox);
incremental->setValue(true);
incremental->setDefaultValue(true);
+
+ auto ignoreMissingFiles = addAspect<BoolAspect>();
+ ignoreMissingFiles->setSettingsKey("RemoteLinux.GenericDirectUploadStep.IgnoreMissingFiles");
+ ignoreMissingFiles->setLabel(Tr::tr("Ignore missing files"),
+ BoolAspect::LabelPlacement::AtCheckBox);
+ ignoreMissingFiles->setValue(false);
+
+ setInternalInitializer([this, incremental, ignoreMissingFiles] {
+ m_incremental = incremental->value()
+ ? IncrementalDeployment::Enabled : IncrementalDeployment::Disabled;
+ m_ignoreMissingFiles = ignoreMissingFiles->value();
+ return isDeploymentPossible();
+ });
+
+ setRunPreparer([this] {
+ m_deployableFiles = target()->deploymentData().allFiles();
+ });
+ }
+
+ bool isDeploymentNecessary() const final;
+ Group deployRecipe() final;
+
+ QDateTime timestampFromStat(const DeployableFile &file, Process *statProc);
+
+ using FilesToStat = std::function<QList<DeployableFile>(UploadStorage *)>;
+ using StatEndHandler
+ = std::function<void(UploadStorage *, const DeployableFile &, const QDateTime &)>;
+ TaskItem statTask(UploadStorage *storage, const DeployableFile &file,
+ StatEndHandler statEndHandler);
+ TaskItem statTree(const TreeStorage<UploadStorage> &storage, FilesToStat filesToStat,
+ StatEndHandler statEndHandler);
+ TaskItem uploadTask(const TreeStorage<UploadStorage> &storage);
+ TaskItem chmodTask(const DeployableFile &file);
+ TaskItem chmodTree(const TreeStorage<UploadStorage> &storage);
+
+ IncrementalDeployment m_incremental = IncrementalDeployment::NotSupported;
+ bool m_ignoreMissingFiles = false;
+ mutable QList<DeployableFile> m_deployableFiles;
+};
+
+static QList<DeployableFile> collectFilesToUpload(const DeployableFile &deployable)
+{
+ QList<DeployableFile> collected;
+ FilePath localFile = deployable.localFilePath();
+ if (localFile.isDir()) {
+ const FilePaths files = localFile.dirEntries(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
+ const QString remoteDir = deployable.remoteDirectory() + '/' + localFile.fileName();
+ for (const FilePath &localFilePath : files)
+ collected.append(collectFilesToUpload(DeployableFile(localFilePath, remoteDir)));
+ } else {
+ collected << deployable;
+ }
+ return collected;
+}
+
+bool GenericDirectUploadStep::isDeploymentNecessary() const
+{
+ QList<DeployableFile> collected;
+ for (int i = 0; i < m_deployableFiles.count(); ++i)
+ collected.append(collectFilesToUpload(m_deployableFiles.at(i)));
+
+ QTC_CHECK(collected.size() >= m_deployableFiles.size());
+ m_deployableFiles = collected;
+ return !m_deployableFiles.isEmpty();
+}
+
+QDateTime GenericDirectUploadStep::timestampFromStat(const DeployableFile &file,
+ Process *statProc)
+{
+ bool succeeded = false;
+ QString error;
+ if (statProc->error() == QProcess::FailedToStart) {
+ error = Tr::tr("Failed to start \"stat\": %1").arg(statProc->errorString());
+ } else if (statProc->exitStatus() == QProcess::CrashExit) {
+ error = Tr::tr("\"stat\" crashed.");
+ } else if (statProc->exitCode() != 0) {
+ error = Tr::tr("\"stat\" failed with exit code %1: %2")
+ .arg(statProc->exitCode()).arg(statProc->cleanedStdErr());
+ } else {
+ succeeded = true;
+ }
+ if (!succeeded) {
+ addWarningMessage(Tr::tr("Failed to retrieve remote timestamp for file \"%1\". "
+ "Incremental deployment will not work. Error message was: %2")
+ .arg(file.remoteFilePath(), error));
+ return {};
}
+ const QByteArray output = statProc->readAllRawStandardOutput().trimmed();
+ const QString warningString(Tr::tr("Unexpected stat output for remote file \"%1\": %2")
+ .arg(file.remoteFilePath()).arg(QString::fromUtf8(output)));
+ if (!output.startsWith(file.remoteFilePath().toUtf8())) {
+ addWarningMessage(warningString);
+ return {};
+ }
+ const QByteArrayList columns = output.mid(file.remoteFilePath().toUtf8().size() + 1).split(' ');
+ if (columns.size() < 14) { // Normal Linux stat: 16 columns in total, busybox stat: 15 columns
+ addWarningMessage(warningString);
+ return {};
+ }
+ bool isNumber;
+ const qint64 secsSinceEpoch = columns.at(11).toLongLong(&isNumber);
+ if (!isNumber) {
+ addWarningMessage(warningString);
+ return {};
+ }
+ return QDateTime::fromSecsSinceEpoch(secsSinceEpoch);
+}
+
+TaskItem GenericDirectUploadStep::statTask(UploadStorage *storage,
+ const DeployableFile &file,
+ StatEndHandler statEndHandler)
+{
+ const auto setupHandler = [=](Process &process) {
+ // We'd like to use --format=%Y, but it's not supported by busybox.
+ process.setCommand({deviceConfiguration()->filePath("stat"),
+ {"-t", Utils::ProcessArgs::quoteArgUnix(file.remoteFilePath())}});
+ };
+ const auto endHandler = [=](const Process &process) {
+ Process *proc = const_cast<Process *>(&process);
+ const QDateTime timestamp = timestampFromStat(file, proc);
+ statEndHandler(storage, file, timestamp);
+ };
+ return ProcessTask(setupHandler, endHandler, endHandler);
+}
- auto ignoreMissingFiles = addAspect<BoolAspect>();
- ignoreMissingFiles->setSettingsKey("RemoteLinux.GenericDirectUploadStep.IgnoreMissingFiles");
- ignoreMissingFiles->setLabel(Tr::tr("Ignore missing files"),
- BoolAspect::LabelPlacement::AtCheckBox);
- ignoreMissingFiles->setValue(false);
-
- setInternalInitializer([incremental, ignoreMissingFiles, service] {
- if (incremental) {
- service->setIncrementalDeployment(incremental->value()
- ? IncrementalDeployment::Enabled : IncrementalDeployment::Disabled);
- } else {
- service->setIncrementalDeployment(IncrementalDeployment::NotSupported);
+TaskItem GenericDirectUploadStep::statTree(const TreeStorage<UploadStorage> &storage,
+ FilesToStat filesToStat, StatEndHandler statEndHandler)
+{
+ const auto setupHandler = [=](TaskTree &tree) {
+ UploadStorage *storagePtr = storage.activeStorage();
+ const QList<DeployableFile> files = filesToStat(storagePtr);
+ QList<TaskItem> statList{finishAllAndDone, parallelLimit(MaxConcurrentStatCalls)};
+ for (const DeployableFile &file : std::as_const(files)) {
+ QTC_ASSERT(file.isValid(), continue);
+ statList.append(statTask(storagePtr, file, statEndHandler));
+ }
+ tree.setupRoot({statList});
+ };
+ return TaskTreeTask(setupHandler);
+}
+
+TaskItem GenericDirectUploadStep::uploadTask(const TreeStorage<UploadStorage> &storage)
+{
+ const auto setupHandler = [this, storage](FileTransfer &transfer) {
+ if (storage->filesToUpload.isEmpty()) {
+ addProgressMessage(Tr::tr("No files need to be uploaded."));
+ return TaskAction::StopWithDone;
+ }
+ addProgressMessage(Tr::tr("%n file(s) need to be uploaded.", "",
+ storage->filesToUpload.size()));
+ FilesToTransfer files;
+ for (const DeployableFile &file : std::as_const(storage->filesToUpload)) {
+ if (!file.localFilePath().exists()) {
+ const QString message = Tr::tr("Local file \"%1\" does not exist.")
+ .arg(file.localFilePath().toUserOutput());
+ if (m_ignoreMissingFiles) {
+ addWarningMessage(message);
+ continue;
+ }
+ addErrorMessage(message);
+ return TaskAction::StopWithError;
+ }
+ files.append({file.localFilePath(),
+ deviceConfiguration()->filePath(file.remoteFilePath())});
}
- service->setIgnoreMissingFiles(ignoreMissingFiles->value());
- return service->isDeploymentPossible();
- });
+ if (files.isEmpty()) {
+ addProgressMessage(Tr::tr("No files need to be uploaded."));
+ return TaskAction::StopWithDone;
+ }
+ transfer.setFilesToTransfer(files);
+ QObject::connect(&transfer, &FileTransfer::progress,
+ this, &GenericDirectUploadStep::addProgressMessage);
+ return TaskAction::Continue;
+ };
+ const auto errorHandler = [this](const FileTransfer &transfer) {
+ addErrorMessage(transfer.resultData().m_errorString);
+ };
- setRunPreparer([this, service] {
- service->setDeployableFiles(target()->deploymentData().allFiles());
- });
+ return FileTransferTask(setupHandler, {}, errorHandler);
}
-GenericDirectUploadStep::~GenericDirectUploadStep() = default;
+TaskItem GenericDirectUploadStep::chmodTask(const DeployableFile &file)
+{
+ const auto setupHandler = [=](Process &process) {
+ process.setCommand({deviceConfiguration()->filePath("chmod"),
+ {"a+x", Utils::ProcessArgs::quoteArgUnix(file.remoteFilePath())}});
+ };
+ const auto errorHandler = [=](const Process &process) {
+ const QString error = process.errorString();
+ if (!error.isEmpty()) {
+ addWarningMessage(Tr::tr("Remote chmod failed for file \"%1\": %2")
+ .arg(file.remoteFilePath(), error));
+ } else if (process.exitCode() != 0) {
+ addWarningMessage(Tr::tr("Remote chmod failed for file \"%1\": %2")
+ .arg(file.remoteFilePath(), process.cleanedStdErr()));
+ }
+ };
+ return ProcessTask(setupHandler, {}, errorHandler);
+}
-Utils::Id GenericDirectUploadStep::stepId()
+TaskItem GenericDirectUploadStep::chmodTree(const TreeStorage<UploadStorage> &storage)
{
- return Constants::DirectUploadStepId;
+ const auto setupChmodHandler = [=](TaskTree &tree) {
+ QList<DeployableFile> filesToChmod;
+ for (const DeployableFile &file : std::as_const(storage->filesToUpload)) {
+ if (file.isExecutable())
+ filesToChmod << file;
+ }
+ QList<TaskItem> chmodList{finishAllAndDone, parallelLimit(MaxConcurrentStatCalls)};
+ for (const DeployableFile &file : std::as_const(filesToChmod)) {
+ QTC_ASSERT(file.isValid(), continue);
+ chmodList.append(chmodTask(file));
+ }
+ tree.setupRoot({chmodList});
+ };
+ return TaskTreeTask(setupChmodHandler);
}
-QString GenericDirectUploadStep::displayName()
+Group GenericDirectUploadStep::deployRecipe()
+{
+ const auto preFilesToStat = [this](UploadStorage *storage) {
+ QList<DeployableFile> filesToStat;
+ for (const DeployableFile &file : std::as_const(m_deployableFiles)) {
+ if (m_incremental != IncrementalDeployment::Enabled || hasLocalFileChanged(file)) {
+ storage->filesToUpload.append(file);
+ continue;
+ }
+ if (m_incremental == IncrementalDeployment::NotSupported)
+ continue;
+ filesToStat << file;
+ }
+ return filesToStat;
+ };
+ const auto preStatEndHandler = [this](UploadStorage *storage, const DeployableFile &file,
+ const QDateTime &timestamp) {
+ if (!timestamp.isValid() || hasRemoteFileChanged(file, timestamp))
+ storage->filesToUpload.append(file);
+ };
+
+ const auto postFilesToStat = [this](UploadStorage *storage) {
+ return m_incremental == IncrementalDeployment::NotSupported
+ ? QList<DeployableFile>() : storage->filesToUpload;
+ };
+ const auto postStatEndHandler = [this](UploadStorage *storage, const DeployableFile &file,
+ const QDateTime &timestamp) {
+ Q_UNUSED(storage)
+ if (timestamp.isValid())
+ saveDeploymentTimeStamp(file, timestamp);
+ };
+ const auto doneHandler = [this] {
+ addProgressMessage(Tr::tr("All files successfully deployed."));
+ };
+
+ const TreeStorage<UploadStorage> storage;
+ const Group root {
+ Storage(storage),
+ statTree(storage, preFilesToStat, preStatEndHandler),
+ uploadTask(storage),
+ Group {
+ chmodTree(storage),
+ statTree(storage, postFilesToStat, postStatEndHandler)
+ },
+ onGroupDone(doneHandler)
+ };
+ return root;
+}
+
+// Factory
+
+GenericDirectUploadStepFactory::GenericDirectUploadStepFactory()
{
- return Tr::tr("Upload files via SFTP");
+ registerStep<GenericDirectUploadStep>(Constants::DirectUploadStepId);
+ setDisplayName(Tr::tr("Upload files via SFTP"));
}
-} //namespace RemoteLinux
+} // RemoteLinux::Internal
diff --git a/src/plugins/remotelinux/genericdirectuploadstep.h b/src/plugins/remotelinux/genericdirectuploadstep.h
index 0c2fb1b67d..3e825caf5c 100644
--- a/src/plugins/remotelinux/genericdirectuploadstep.h
+++ b/src/plugins/remotelinux/genericdirectuploadstep.h
@@ -3,23 +3,14 @@
#pragma once
-#include "remotelinux_export.h"
+#include <projectexplorer/buildstep.h>
-#include "abstractremotelinuxdeploystep.h"
+namespace RemoteLinux::Internal {
-namespace RemoteLinux {
-
-class REMOTELINUX_EXPORT GenericDirectUploadStep : public AbstractRemoteLinuxDeployStep
+class GenericDirectUploadStepFactory : public ProjectExplorer::BuildStepFactory
{
- Q_OBJECT
-
public:
- GenericDirectUploadStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id,
- bool offerIncrementalDeployment = true);
- ~GenericDirectUploadStep() override;
-
- static Utils::Id stepId();
- static QString displayName();
+ GenericDirectUploadStepFactory();
};
-} //namespace RemoteLinux
+} // RemoteLinux::Internal
diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp
index 50c553437f..da7b6138de 100644
--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp
+++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp
@@ -3,9 +3,11 @@
#include "genericlinuxdeviceconfigurationwidget.h"
+#include "remotelinux_constants.h"
#include "remotelinuxtr.h"
#include "sshkeycreationdialog.h"
+#include <projectexplorer/devicesupport/devicemanager.h>
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/devicesupport/sshparameters.h>
@@ -16,6 +18,7 @@
#include <utils/utilsicons.h>
#include <QCheckBox>
+#include <QComboBox>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
@@ -32,14 +35,13 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget(
const IDevice::Ptr &device) :
IDeviceWidget(device)
{
- resize(556, 309);
-
m_defaultAuthButton = new QRadioButton(Tr::tr("Default"), this);
m_keyButton = new QRadioButton(Tr::tr("Specific &key"));
- m_hostLineEdit = new QLineEdit(this);
+ m_hostLineEdit = new FancyLineEdit(this);
m_hostLineEdit->setPlaceholderText(Tr::tr("IP or host name of the device"));
+ m_hostLineEdit->setHistoryCompleter("HostName");
m_sshPortSpinBox = new QSpinBox(this);
m_sshPortSpinBox->setMinimum(0);
@@ -48,8 +50,9 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget(
m_hostKeyCheckBox = new QCheckBox(Tr::tr("&Check host key"));
- m_portsLineEdit = new QLineEdit(this);
+ m_portsLineEdit = new FancyLineEdit(this);
m_portsLineEdit->setToolTip(Tr::tr("You can enter lists and ranges like this: '1024,1026-1028,1030'."));
+ m_portsLineEdit->setHistoryCompleter("PortRange");
m_portsWarningLabel = new QLabel(this);
@@ -59,7 +62,8 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget(
m_timeoutSpinBox->setValue(1000);
m_timeoutSpinBox->setSuffix(Tr::tr("s"));
- m_userLineEdit = new QLineEdit(this);
+ m_userLineEdit = new FancyLineEdit(this);
+ m_userLineEdit->setHistoryCompleter("UserName");
m_keyLabel = new QLabel(Tr::tr("Private key file:"));
@@ -74,11 +78,28 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget(
m_gdbServerLineEdit->setExpectedKind(PathChooser::ExistingCommand);
m_gdbServerLineEdit->setPlaceholderText(hint);
m_gdbServerLineEdit->setToolTip(hint);
+ m_gdbServerLineEdit->setHistoryCompleter("GdbServer");
+ m_gdbServerLineEdit->setAllowPathFromDevice(true);
m_qmlRuntimeLineEdit = new PathChooser(this);
m_qmlRuntimeLineEdit->setExpectedKind(PathChooser::ExistingCommand);
m_qmlRuntimeLineEdit->setPlaceholderText(hint);
m_qmlRuntimeLineEdit->setToolTip(hint);
+ m_qmlRuntimeLineEdit->setHistoryCompleter("QmlRuntime");
+ m_qmlRuntimeLineEdit->setAllowPathFromDevice(true);
+
+ m_sourceProfileCheckBox =
+ new QCheckBox(Tr::tr("Source %1 and %2").arg("/etc/profile").arg("$HOME/.profile"));
+
+ m_linkDeviceComboBox = new QComboBox;
+ m_linkDeviceComboBox->addItem(Tr::tr("Direct"), QVariant());
+
+ auto dm = DeviceManager::instance();
+ const int dmCount = dm->deviceCount();
+ for (int i = 0; i < dmCount; ++i) {
+ IDevice::ConstPtr dev = dm->deviceAt(i);
+ m_linkDeviceComboBox->addItem(dev->displayName(), dev->id().toSetting());
+ }
auto sshPortLabel = new QLabel(Tr::tr("&SSH port:"));
sshPortLabel->setBuddy(m_sshPortSpinBox);
@@ -93,7 +114,9 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget(
Tr::tr("&Username:"), m_userLineEdit, st, br,
m_keyLabel, m_keyFileLineEdit, createKeyButton, br,
Tr::tr("GDB server executable:"), m_gdbServerLineEdit, br,
- Tr::tr("QML runtime executable:"), m_qmlRuntimeLineEdit, br
+ Tr::tr("QML runtime executable:"), m_qmlRuntimeLineEdit, br,
+ QString(), m_sourceProfileCheckBox, br,
+ Tr::tr("Access via:"), m_linkDeviceComboBox
}.attachTo(this);
connect(m_hostLineEdit, &QLineEdit::editingFinished,
@@ -124,6 +147,10 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget(
this, &GenericLinuxDeviceConfigurationWidget::qmlRuntimeEditingFinished);
connect(m_hostKeyCheckBox, &QCheckBox::toggled,
this, &GenericLinuxDeviceConfigurationWidget::hostKeyCheckingChanged);
+ connect(m_sourceProfileCheckBox, &QCheckBox::toggled,
+ this, &GenericLinuxDeviceConfigurationWidget::sourceProfileCheckingChanged);
+ connect(m_linkDeviceComboBox, &QComboBox::currentIndexChanged,
+ this, &GenericLinuxDeviceConfigurationWidget::linkDeviceChanged);
initGui();
}
@@ -214,6 +241,17 @@ void GenericLinuxDeviceConfigurationWidget::hostKeyCheckingChanged(bool doCheck)
device()->setSshParameters(sshParams);
}
+void GenericLinuxDeviceConfigurationWidget::sourceProfileCheckingChanged(bool doCheck)
+{
+ device()->setExtraData(Constants::SourceProfile, doCheck);
+}
+
+void GenericLinuxDeviceConfigurationWidget::linkDeviceChanged(int index)
+{
+ const QVariant deviceId = m_linkDeviceComboBox->itemData(index);
+ device()->setExtraData(Constants::LinkDevice, deviceId);
+}
+
void GenericLinuxDeviceConfigurationWidget::updateDeviceFromUi()
{
hostNameEditingFinished();
@@ -223,6 +261,10 @@ void GenericLinuxDeviceConfigurationWidget::updateDeviceFromUi()
keyFileEditingFinished();
handleFreePortsChanged();
gdbServerEditingFinished();
+ sshPortEditingFinished();
+ timeoutEditingFinished();
+ sourceProfileCheckingChanged(m_sourceProfileCheckBox->isChecked());
+ linkDeviceChanged(m_linkDeviceComboBox->currentIndex());
qmlRuntimeEditingFinished();
}
@@ -261,6 +303,17 @@ void GenericLinuxDeviceConfigurationWidget::initGui()
m_hostLineEdit->setEnabled(!device()->isAutoDetected());
m_sshPortSpinBox->setEnabled(!device()->isAutoDetected());
m_hostKeyCheckBox->setChecked(sshParams.hostKeyCheckingMode != SshHostKeyCheckingNone);
+ m_sourceProfileCheckBox->setChecked(device()->extraData(Constants::SourceProfile).toBool());
+ Id linkDeviceId = Id::fromSetting(device()->extraData(Constants::LinkDevice));
+ auto dm = DeviceManager::instance();
+ int found = -1;
+ for (int i = 0, n = dm->deviceCount(); i < n; ++i) {
+ if (dm->deviceAt(i)->id() == linkDeviceId) {
+ found = i;
+ break;
+ }
+ }
+ m_linkDeviceComboBox->setCurrentIndex(found + 1); // There's the "Direct" entry first.
m_hostLineEdit->setText(sshParams.host());
m_sshPortSpinBox->setValue(sshParams.port());
diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h
index 4e00cedcb6..aba251b969 100644
--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h
+++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h
@@ -7,13 +7,14 @@
QT_BEGIN_NAMESPACE
class QCheckBox;
+class QComboBox;
class QLabel;
-class QLineEdit;
class QRadioButton;
class QSpinBox;
QT_END_NAMESPACE
namespace Utils {
+class FancyLineEdit;
class FilePath;
class PathChooser;
} // Utils
@@ -42,6 +43,8 @@ private:
void setPrivateKey(const Utils::FilePath &path);
void createNewKey();
void hostKeyCheckingChanged(bool doCheck);
+ void sourceProfileCheckingChanged(bool doCheck);
+ void linkDeviceChanged(int index);
void updateDeviceFromUi() override;
void updatePortsWarningLabel();
@@ -50,17 +53,19 @@ private:
QRadioButton *m_defaultAuthButton;
QLabel *m_keyLabel;
QRadioButton *m_keyButton;
- QLineEdit *m_hostLineEdit;
+ Utils::FancyLineEdit *m_hostLineEdit;
QSpinBox *m_sshPortSpinBox;
QCheckBox *m_hostKeyCheckBox;
- QLineEdit *m_portsLineEdit;
+ Utils::FancyLineEdit *m_portsLineEdit;
QLabel *m_portsWarningLabel;
- QLineEdit *m_userLineEdit;
+ Utils::FancyLineEdit *m_userLineEdit;
QSpinBox *m_timeoutSpinBox;
Utils::PathChooser *m_keyFileLineEdit;
QLabel *m_machineTypeValueLabel;
Utils::PathChooser *m_gdbServerLineEdit;
Utils::PathChooser *m_qmlRuntimeLineEdit;
+ QCheckBox *m_sourceProfileCheckBox;
+ QComboBox *m_linkDeviceComboBox;
};
} // RemoteLinux::Internal
diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp
index 4de31d898d..7bd047a590 100644
--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp
+++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp
@@ -11,7 +11,6 @@
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/devicesupport/sshparameters.h>
-#include <utils/portlist.h>
#include <utils/fileutils.h>
using namespace ProjectExplorer;
@@ -45,13 +44,6 @@ GenericLinuxDeviceConfigurationWizard::GenericLinuxDeviceConfigurationWizard(QWi
setPage(Internal::FinalPageId, &d->finalPage);
d->finalPage.setCommitPage(true);
d->device = LinuxDevice::create();
- d->device->setupId(IDevice::ManuallyAdded, Utils::Id());
- d->device->setType(Constants::GenericLinuxOsType);
- d->device->setMachineType(IDevice::Hardware);
- d->device->setFreePorts(Utils::PortList::fromString(QLatin1String("10000-10100")));
- SshParameters sshParams;
- sshParams.timeout = 10;
- d->device->setSshParameters(sshParams);
d->setupPage.setDevice(d->device);
d->keyDeploymentPage.setDevice(d->device);
}
diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp
index 89e4c2e483..0a34683bde 100644
--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp
+++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp
@@ -9,14 +9,15 @@
#include <projectexplorer/devicesupport/sshparameters.h>
+#include <utils/filepath.h>
#include <utils/fileutils.h>
+#include <utils/fancylineedit.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
#include <utils/utilsicons.h>
#include <QHBoxLayout>
#include <QLabel>
-#include <QLineEdit>
#include <QPushButton>
#include <QSpinBox>
@@ -29,10 +30,10 @@ namespace Internal {
class GenericLinuxDeviceConfigurationWizardSetupPagePrivate
{
public:
- QLineEdit *nameLineEdit;
- QLineEdit *hostNameLineEdit;
+ FancyLineEdit *nameLineEdit;
+ FancyLineEdit *hostNameLineEdit;
QSpinBox *sshPortSpinBox;
- QLineEdit *userNameLineEdit;
+ FancyLineEdit *userNameLineEdit;
LinuxDevice::Ptr device;
};
@@ -52,10 +53,16 @@ GenericLinuxDeviceConfigurationWizardSetupPage::GenericLinuxDeviceConfigurationW
setTitle(Tr::tr("Connection"));
setWindowTitle(Tr::tr("WizardPage"));
- d->nameLineEdit = new QLineEdit(this);
- d->hostNameLineEdit = new QLineEdit(this);
+ d->nameLineEdit = new FancyLineEdit(this);
+ d->nameLineEdit->setHistoryCompleter("DeviceName");
+
+ d->hostNameLineEdit = new FancyLineEdit(this);
+ d->hostNameLineEdit->setHistoryCompleter("HostName");
+
d->sshPortSpinBox = new QSpinBox(this);
- d->userNameLineEdit = new QLineEdit(this);
+
+ d->userNameLineEdit = new FancyLineEdit(this);
+ d->userNameLineEdit->setHistoryCompleter("UserName");
using namespace Layouting;
Form {
@@ -97,7 +104,9 @@ bool GenericLinuxDeviceConfigurationWizardSetupPage::validatePage()
{
d->device->setDisplayName(configurationName());
SshParameters sshParams = d->device->sshParameters();
- sshParams.url = url();
+ sshParams.setHost(d->hostNameLineEdit->text().trimmed());
+ sshParams.setUserName(d->userNameLineEdit->text().trimmed());
+ sshParams.setPort(d->sshPortSpinBox->value());
d->device->setSshParameters(sshParams);
return true;
}
@@ -107,15 +116,6 @@ QString GenericLinuxDeviceConfigurationWizardSetupPage::configurationName() cons
return d->nameLineEdit->text().trimmed();
}
-QUrl GenericLinuxDeviceConfigurationWizardSetupPage::url() const
-{
- QUrl url;
- url.setHost(d->hostNameLineEdit->text().trimmed());
- url.setUserName(d->userNameLineEdit->text().trimmed());
- url.setPort(d->sshPortSpinBox->value());
- return url;
-}
-
void GenericLinuxDeviceConfigurationWizardSetupPage::setDevice(const LinuxDevice::Ptr &device)
{
d->device = device;
diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.h b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.h
index 6e5e00119a..2e2d529891 100644
--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.h
+++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.h
@@ -32,7 +32,6 @@ private:
bool validatePage() override;
QString configurationName() const;
- QUrl url() const;
Internal::GenericLinuxDeviceConfigurationWizardSetupPagePrivate * const d;
};
@@ -64,7 +63,7 @@ class REMOTELINUX_EXPORT GenericLinuxDeviceConfigurationWizardFinalPage final :
{
Q_OBJECT
public:
- GenericLinuxDeviceConfigurationWizardFinalPage(QWidget *parent);
+ GenericLinuxDeviceConfigurationWizardFinalPage(QWidget *parent = nullptr);
~GenericLinuxDeviceConfigurationWizardFinalPage() override;
protected:
diff --git a/src/plugins/remotelinux/killappstep.cpp b/src/plugins/remotelinux/killappstep.cpp
index 03473d95a2..d3ec72e889 100644
--- a/src/plugins/remotelinux/killappstep.cpp
+++ b/src/plugins/remotelinux/killappstep.cpp
@@ -15,15 +15,26 @@
#include <utils/qtcassert.h>
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
-using namespace Utils::Tasking;
namespace RemoteLinux::Internal {
-class KillAppService : public AbstractRemoteLinuxDeployService
+class KillAppStep : public AbstractRemoteLinuxDeployStep
{
public:
- void setRemoteExecutable(const FilePath &filePath) { m_remoteExecutable = filePath; }
+ KillAppStep(BuildStepList *bsl, Id id) : AbstractRemoteLinuxDeployStep(bsl, id)
+ {
+ setWidgetExpandedByDefault(false);
+
+ setInternalInitializer([this] {
+ Target * const theTarget = target();
+ QTC_ASSERT(theTarget, return CheckResult::failure());
+ RunConfiguration * const rc = theTarget->activeRunConfiguration();
+ m_remoteExecutable = rc ? rc->runnable().command.executable() : FilePath();
+ return CheckResult::success();
+ });
+ }
private:
bool isDeploymentNecessary() const final { return !m_remoteExecutable.isEmpty(); }
@@ -32,44 +43,23 @@ private:
FilePath m_remoteExecutable;
};
-Group KillAppService::deployRecipe()
+Group KillAppStep::deployRecipe()
{
const auto setupHandler = [this](DeviceProcessKiller &killer) {
killer.setProcessPath(m_remoteExecutable);
- emit progressMessage(Tr::tr("Trying to kill \"%1\" on remote device...")
- .arg(m_remoteExecutable.path()));
+ addProgressMessage(Tr::tr("Trying to kill \"%1\" on remote device...")
+ .arg(m_remoteExecutable.path()));
};
const auto doneHandler = [this](const DeviceProcessKiller &) {
- emit progressMessage(Tr::tr("Remote application killed."));
+ addProgressMessage(Tr::tr("Remote application killed."));
};
const auto errorHandler = [this](const DeviceProcessKiller &) {
- emit progressMessage(Tr::tr("Failed to kill remote application. "
+ addProgressMessage(Tr::tr("Failed to kill remote application. "
"Assuming it was not running."));
};
- return Group { Killer(setupHandler, doneHandler, errorHandler) };
+ return Group { DeviceProcessKillerTask(setupHandler, doneHandler, errorHandler) };
}
-class KillAppStep : public AbstractRemoteLinuxDeployStep
-{
-public:
- KillAppStep(BuildStepList *bsl, Id id) : AbstractRemoteLinuxDeployStep(bsl, id)
- {
- auto service = new Internal::KillAppService;
- setDeployService(service);
-
- setWidgetExpandedByDefault(false);
-
- setInternalInitializer([this, service] {
- Target * const theTarget = target();
- QTC_ASSERT(theTarget, return CheckResult::failure());
- RunConfiguration * const rc = theTarget->activeRunConfiguration();
- const FilePath remoteExe = rc ? rc->runnable().command.executable() : FilePath();
- service->setRemoteExecutable(remoteExe);
- return CheckResult::success();
- });
- }
-};
-
KillAppStepFactory::KillAppStepFactory()
{
registerStep<KillAppStep>(Constants::KillAppStepId);
diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp
index 5d0d2d6b92..cc44dddb3c 100644
--- a/src/plugins/remotelinux/linuxdevice.cpp
+++ b/src/plugins/remotelinux/linuxdevice.cpp
@@ -11,14 +11,14 @@
#include "remotelinux_constants.h"
#include "remotelinuxsignaloperation.h"
#include "remotelinuxtr.h"
-#include "sshprocessinterface.h"
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
+#include <projectexplorer/devicesupport/devicemanager.h>
#include <projectexplorer/devicesupport/filetransfer.h>
#include <projectexplorer/devicesupport/filetransferinterface.h>
-#include <projectexplorer/devicesupport/sshdeviceprocesslist.h>
+#include <projectexplorer/devicesupport/processlist.h>
#include <projectexplorer/devicesupport/sshparameters.h>
#include <projectexplorer/devicesupport/sshsettings.h>
@@ -28,9 +28,10 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/port.h>
+#include <utils/portlist.h>
+#include <utils/process.h>
#include <utils/processinfo.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/temporaryfile.h>
@@ -51,9 +52,6 @@ namespace RemoteLinux {
const QByteArray s_pidMarker = "__qtc";
-const char Delimiter0[] = "x--";
-const char Delimiter1[] = "---";
-
static Q_LOGGING_CATEGORY(linuxDeviceLog, "qtc.remotelinux.device", QtWarningMsg);
#define DEBUG(x) qCDebug(linuxDeviceLog) << x << '\n'
@@ -94,7 +92,7 @@ private:
QStringList connectionArgs(const FilePath &binary) const;
const SshParameters m_sshParameters;
- std::unique_ptr<QtcProcess> m_masterProcess;
+ std::unique_ptr<Process> m_masterProcess;
std::unique_ptr<QTemporaryDir> m_masterSocketDir;
QTimer m_timer;
int m_ref = 0;
@@ -160,11 +158,11 @@ void SshSharedConnection::connectToHost()
return;
}
- m_masterProcess.reset(new QtcProcess);
+ m_masterProcess.reset(new Process);
SshParameters::setupSshEnvironment(m_masterProcess.get());
m_timer.setSingleShot(true);
connect(&m_timer, &QTimer::timeout, this, &SshSharedConnection::autoDestructRequested);
- connect(m_masterProcess.get(), &QtcProcess::readyReadStandardOutput, this, [this] {
+ connect(m_masterProcess.get(), &Process::readyReadStandardOutput, this, [this] {
const QByteArray reply = m_masterProcess->readAllRawStandardOutput();
if (reply == "\n")
emitConnected();
@@ -172,7 +170,7 @@ void SshSharedConnection::connectToHost()
});
// TODO: in case of refused connection we are getting the following on stdErr:
// ssh: connect to host 127.0.0.1 port 22: Connection refused\r\n
- connect(m_masterProcess.get(), &QtcProcess::done, this, [this] {
+ connect(m_masterProcess.get(), &Process::done, this, [this] {
const ProcessResult result = m_masterProcess->result();
const ProcessResultData resultData = m_masterProcess->resultData();
if (result == ProcessResult::StartFailed) {
@@ -276,77 +274,6 @@ private:
IDevice::ConstPtr m_device;
};
-static QString visualizeNull(QString s)
-{
- return s.replace(QLatin1Char('\0'), QLatin1String("<null>"));
-}
-
-class LinuxDeviceProcessList : public SshDeviceProcessList
-{
-public:
- LinuxDeviceProcessList(const IDevice::ConstPtr &device, QObject *parent)
- : SshDeviceProcessList(device, parent)
- {
- }
-
-private:
- QString listProcessesCommandLine() const override
- {
- return QString::fromLatin1(
- "for dir in `ls -d /proc/[0123456789]*`; do "
- "test -d $dir || continue;" // Decrease the likelihood of a race condition.
- "echo $dir;"
- "cat $dir/cmdline;echo;" // cmdline does not end in newline
- "cat $dir/stat;"
- "readlink $dir/exe;"
- "printf '%1''%2';"
- "done").arg(QLatin1String(Delimiter0)).arg(QLatin1String(Delimiter1));
- }
-
- QList<ProcessInfo> buildProcessList(const QString &listProcessesReply) const override
- {
- QList<ProcessInfo> processes;
- const QStringList lines = listProcessesReply.split(QString::fromLatin1(Delimiter0)
- + QString::fromLatin1(Delimiter1), Qt::SkipEmptyParts);
- for (const QString &line : lines) {
- const QStringList elements = line.split(QLatin1Char('\n'));
- if (elements.count() < 4) {
- qDebug("%s: Expected four list elements, got %d. Line was '%s'.", Q_FUNC_INFO,
- int(elements.count()), qPrintable(visualizeNull(line)));
- continue;
- }
- bool ok;
- const int pid = elements.first().mid(6).toInt(&ok);
- if (!ok) {
- qDebug("%s: Expected number in %s. Line was '%s'.", Q_FUNC_INFO,
- qPrintable(elements.first()), qPrintable(visualizeNull(line)));
- continue;
- }
- QString command = elements.at(1);
- command.replace(QLatin1Char('\0'), QLatin1Char(' '));
- if (command.isEmpty()) {
- const QString &statString = elements.at(2);
- const int openParenPos = statString.indexOf(QLatin1Char('('));
- const int closedParenPos = statString.indexOf(QLatin1Char(')'), openParenPos);
- if (openParenPos == -1 || closedParenPos == -1)
- continue;
- command = QLatin1Char('[')
- + statString.mid(openParenPos + 1, closedParenPos - openParenPos - 1)
- + QLatin1Char(']');
- }
-
- ProcessInfo process;
- process.processId = pid;
- process.commandLine = command;
- process.executable = elements.at(3);
- processes.append(process);
- }
-
- return Utils::sorted(std::move(processes));
- }
-};
-
-
// LinuxDevicePrivate
class ShellThreadHandler;
@@ -382,11 +309,13 @@ public:
Environment getEnvironment();
void invalidateEnvironmentCache();
+ void checkOsType();
+ void queryOsType(std::function<RunResult(const CommandLine &)> run);
+
LinuxDevice *q = nullptr;
QThread m_shellThread;
ShellThreadHandler *m_handler = nullptr;
mutable QMutex m_shellMutex;
- QList<QtcProcess *> m_terminals;
LinuxDeviceFileAccess m_fileAccess{this};
QReadWriteLock m_environmentCacheLock;
@@ -410,11 +339,8 @@ Environment LinuxDevicePrivate::getEnvironment()
if (m_environmentCache.has_value())
return m_environmentCache.value();
- QtcProcess getEnvProc;
- getEnvProc.setCommand({FilePath("env").onDevice(q->rootPath()), {}});
- Environment inEnv;
- inEnv.setCombineWithDeviceEnvironment(false);
- getEnvProc.setEnvironment(inEnv);
+ Process getEnvProc;
+ getEnvProc.setCommand({q->filePath("env"), {}});
getEnvProc.runBlocking();
const QString remoteOutput = getEnvProc.cleanedStdOut();
@@ -437,10 +363,8 @@ Environment LinuxDeviceFileAccess::deviceEnvironment() const
class SshProcessInterfacePrivate : public QObject
{
- Q_OBJECT
-
public:
- SshProcessInterfacePrivate(SshProcessInterface *sshInterface, LinuxDevicePrivate *devicePrivate);
+ SshProcessInterfacePrivate(SshProcessInterface *sshInterface, const IDevice::ConstPtr &device);
void start();
@@ -463,47 +387,32 @@ public:
// as this object is alive.
IDevice::ConstPtr m_device;
std::unique_ptr<SshConnectionHandle> m_connectionHandle;
- QtcProcess m_process;
- LinuxDevicePrivate *m_devicePrivate = nullptr;
+ Process m_process;
QString m_socketFilePath;
SshParameters m_sshParameters;
+
bool m_connecting = false;
bool m_killed = false;
ProcessResultData m_result;
+
+ QByteArray m_output;
+ QByteArray m_error;
+ bool m_pidParsed = false;
+ bool m_useConnectionSharing = false;
};
-SshProcessInterface::SshProcessInterface(const LinuxDevice *linuxDevice)
- : d(new SshProcessInterfacePrivate(this, linuxDevice->d))
-{
-}
+SshProcessInterface::SshProcessInterface(const IDevice::ConstPtr &device)
+ : d(new SshProcessInterfacePrivate(this, device))
+{}
SshProcessInterface::~SshProcessInterface()
{
+ killIfRunning();
delete d;
}
-void SshProcessInterface::handleStarted(qint64 processId)
-{
- emitStarted(processId);
-}
-
-void SshProcessInterface::handleDone(const ProcessResultData &resultData)
-{
- emit done(resultData);
-}
-
-void SshProcessInterface::handleReadyReadStandardOutput(const QByteArray &outputData)
-{
- emit readyRead(outputData, {});
-}
-
-void SshProcessInterface::handleReadyReadStandardError(const QByteArray &errorData)
-{
- emit readyRead({}, errorData);
-}
-
void SshProcessInterface::emitStarted(qint64 processId)
{
d->m_processId = processId;
@@ -525,7 +434,7 @@ qint64 SshProcessInterface::processId() const
bool SshProcessInterface::runInShell(const CommandLine &command, const QByteArray &data)
{
- QtcProcess process;
+ Process process;
CommandLine cmd = {d->m_device->filePath("/bin/sh"), {"-c"}};
QString tmp;
ProcessArgs::addArg(&tmp, command.executable().path());
@@ -556,7 +465,7 @@ void SshProcessInterface::sendControlSignal(ControlSignal controlSignal)
d->m_process.closeWriteChannel();
return;
}
- if (d->m_process.usesTerminal()) {
+ if (d->m_process.usesTerminal() || d->m_process.ptyData().has_value()) {
switch (controlSignal) {
case ControlSignal::Terminate: d->m_process.terminate(); break;
case ControlSignal::Kill: d->m_process.kill(); break;
@@ -569,17 +478,7 @@ void SshProcessInterface::sendControlSignal(ControlSignal controlSignal)
handleSendControlSignal(controlSignal);
}
-LinuxProcessInterface::LinuxProcessInterface(const LinuxDevice *linuxDevice)
- : SshProcessInterface(linuxDevice)
-{
-}
-
-LinuxProcessInterface::~LinuxProcessInterface()
-{
- killIfRunning();
-}
-
-void LinuxProcessInterface::handleSendControlSignal(ControlSignal controlSignal)
+void SshProcessInterface::handleSendControlSignal(ControlSignal controlSignal)
{
QTC_ASSERT(controlSignal != ControlSignal::KickOff, return);
QTC_ASSERT(controlSignal != ControlSignal::CloseWriteChannel, return);
@@ -592,75 +491,58 @@ void LinuxProcessInterface::handleSendControlSignal(ControlSignal controlSignal)
runInShell(command);
}
-QString LinuxProcessInterface::fullCommandLine(const CommandLine &commandLine) const
+void SshProcessInterfacePrivate::handleStarted()
{
- CommandLine cmd;
-
- if (!commandLine.isEmpty()) {
- const QStringList rcFilesToSource = {"/etc/profile", "$HOME/.profile"};
- for (const QString &filePath : rcFilesToSource) {
- cmd.addArgs({"test", "-f", filePath});
- cmd.addArgs("&&", CommandLine::Raw);
- cmd.addArgs({".", filePath});
- cmd.addArgs(";", CommandLine::Raw);
- }
- }
-
- if (!m_setup.m_workingDirectory.isEmpty()) {
- cmd.addArgs({"cd", m_setup.m_workingDirectory.path()});
- cmd.addArgs("&&", CommandLine::Raw);
- }
-
- if (m_setup.m_terminalMode == TerminalMode::Off)
- cmd.addArgs(QString("echo ") + s_pidMarker + "$$" + s_pidMarker + " && ", CommandLine::Raw);
-
- const Environment &env = m_setup.m_environment;
- for (auto it = env.constBegin(); it != env.constEnd(); ++it)
- cmd.addArgs(env.key(it) + "='" + env.expandedValueForKey(env.key(it)) + '\'', CommandLine::Raw);
-
- if (m_setup.m_terminalMode == TerminalMode::Off)
- cmd.addArg("exec");
-
- if (!commandLine.isEmpty())
- cmd.addCommandLineAsArgs(commandLine, CommandLine::Raw);
- return cmd.arguments();
-}
+ const qint64 processId = m_process.usesTerminal() ? m_process.processId() : 0;
-void LinuxProcessInterface::handleStarted(qint64 processId)
-{
// Don't emit started() when terminal is off,
// it's being done later inside handleReadyReadStandardOutput().
- if (m_setup.m_terminalMode == TerminalMode::Off)
+ if (q->m_setup.m_terminalMode == TerminalMode::Off && !q->m_setup.m_ptyData)
return;
m_pidParsed = true;
- emitStarted(processId);
+ q->emitStarted(processId);
}
-void LinuxProcessInterface::handleDone(const ProcessResultData &resultData)
+void SshProcessInterfacePrivate::handleDone()
{
- ProcessResultData finalData = resultData;
+ if (m_connectionHandle) // TODO: should it disconnect from signals first?
+ m_connectionHandle.release()->deleteLater();
+
+ ProcessResultData finalData = m_process.resultData();
if (!m_pidParsed) {
finalData.m_error = QProcess::FailedToStart;
finalData.m_errorString = Utils::joinStrings({finalData.m_errorString,
QString::fromLocal8Bit(m_error)}, '\n');
}
- emit done(finalData);
+ emit q->done(finalData);
}
-void LinuxProcessInterface::handleReadyReadStandardOutput(const QByteArray &outputData)
+void SshProcessInterfacePrivate::handleReadyReadStandardOutput()
{
+ // By default this forwards readyRead immediately, but only buffers the
+ // output in case the start signal is not emitted yet.
+ // In case the pid can be parsed now, the delayed started() is
+ // emitted, and any previously buffered output emitted now.
+ const QByteArray outputData = m_process.readAllRawStandardOutput();
+
if (m_pidParsed) {
- emit readyRead(outputData, {});
+ emit q->readyRead(outputData, {});
return;
}
m_output.append(outputData);
static const QByteArray endMarker = s_pidMarker + '\n';
- const int endMarkerOffset = m_output.indexOf(endMarker);
- if (endMarkerOffset == -1)
- return;
+ int endMarkerLength = endMarker.length();
+ int endMarkerOffset = m_output.indexOf(endMarker);
+ if (endMarkerOffset == -1) {
+ static const QByteArray endMarker = s_pidMarker + "\r\n";
+ endMarkerOffset = m_output.indexOf(endMarker);
+ endMarkerLength = endMarker.length();
+ if (endMarkerOffset == -1)
+ return;
+ }
const int startMarkerOffset = m_output.indexOf(s_pidMarker);
if (startMarkerOffset == endMarkerOffset) // Only theoretically possible.
return;
@@ -670,59 +552,109 @@ void LinuxProcessInterface::handleReadyReadStandardOutput(const QByteArray &outp
const qint64 processId = pidString.toLongLong();
// We don't want to show output from e.g. /etc/profile.
- m_output = m_output.mid(endMarkerOffset + endMarker.length());
+ m_output = m_output.mid(endMarkerOffset + endMarkerLength);
- emitStarted(processId);
+ q->emitStarted(processId);
if (!m_output.isEmpty() || !m_error.isEmpty())
- emit readyRead(m_output, m_error);
+ emit q->readyRead(m_output, m_error);
m_output.clear();
m_error.clear();
}
-void LinuxProcessInterface::handleReadyReadStandardError(const QByteArray &errorData)
+void SshProcessInterfacePrivate::handleReadyReadStandardError()
{
+ // By default forwards readyRead immediately, but buffers it in
+ // case the start signal is not emitted yet.
+ const QByteArray errorData = m_process.readAllRawStandardError();
+
if (m_pidParsed) {
- emit readyRead({}, errorData);
+ emit q->readyRead({}, errorData);
return;
}
m_error.append(errorData);
}
SshProcessInterfacePrivate::SshProcessInterfacePrivate(SshProcessInterface *sshInterface,
- LinuxDevicePrivate *devicePrivate)
+ const IDevice::ConstPtr &device)
: QObject(sshInterface)
, q(sshInterface)
- , m_device(devicePrivate->q->sharedFromThis())
+ , m_device(device)
, m_process(this)
- , m_devicePrivate(devicePrivate)
{
- connect(&m_process, &QtcProcess::started, this, &SshProcessInterfacePrivate::handleStarted);
- connect(&m_process, &QtcProcess::done, this, &SshProcessInterfacePrivate::handleDone);
- connect(&m_process, &QtcProcess::readyReadStandardOutput,
+ connect(&m_process, &Process::started, this, &SshProcessInterfacePrivate::handleStarted);
+ connect(&m_process, &Process::done, this, &SshProcessInterfacePrivate::handleDone);
+ connect(&m_process, &Process::readyReadStandardOutput,
this, &SshProcessInterfacePrivate::handleReadyReadStandardOutput);
- connect(&m_process, &QtcProcess::readyReadStandardError,
+ connect(&m_process, &Process::readyReadStandardError,
this, &SshProcessInterfacePrivate::handleReadyReadStandardError);
}
void SshProcessInterfacePrivate::start()
{
clearForStart();
+ m_sshParameters = m_device->sshParameters();
+
+ const Id linkDeviceId = Id::fromSetting(m_device->extraData(Constants::LinkDevice));
+ if (const IDevice::ConstPtr linkDevice = DeviceManager::instance()->find(linkDeviceId)) {
+ CommandLine cmd{linkDevice->filePath("ssh")};
+ if (!m_sshParameters.userName().isEmpty()) {
+ cmd.addArg("-l");
+ cmd.addArg(m_sshParameters.userName());
+ }
+ cmd.addArg(m_sshParameters.host());
+
+ const bool useTerminal = q->m_setup.m_terminalMode != TerminalMode::Off
+ || q->m_setup.m_ptyData;
+ if (useTerminal)
+ cmd.addArg("-tt");
+
+ const CommandLine full = q->m_setup.m_commandLine;
+ if (!full.isEmpty()) { // Empty is ok in case of opening a terminal.
+ CommandLine inner;
+ const QString wd = q->m_setup.m_workingDirectory.path();
+ if (!wd.isEmpty())
+ inner.addCommandLineWithAnd({"cd", {wd}});
+ if (!useTerminal) {
+ const QString pidArg = QString("%1\\$\\$%1").arg(QString::fromLatin1(s_pidMarker));
+ inner.addCommandLineWithAnd({"echo", pidArg, CommandLine::Raw});
+ }
+ inner.addCommandLineWithAnd(full);
+ cmd.addCommandLineAsSingleArg(inner);
+ }
+
+ m_process.setProcessImpl(q->m_setup.m_processImpl);
+ m_process.setProcessMode(q->m_setup.m_processMode);
+ m_process.setTerminalMode(q->m_setup.m_terminalMode);
+ m_process.setPtyData(q->m_setup.m_ptyData);
+ m_process.setReaperTimeout(q->m_setup.m_reaperTimeout);
+ m_process.setWriteData(q->m_setup.m_writeData);
+ m_process.setCreateConsoleOnWindows(q->m_setup.m_createConsoleOnWindows);
+ m_process.setExtraData(q->m_setup.m_extraData);
+
+ m_process.setCommand(cmd);
+ m_process.start();
+ return;
+ }
+
+ m_useConnectionSharing = SshSettings::connectionSharingEnabled();
- m_sshParameters = m_devicePrivate->q->sshParameters();
// TODO: Do we really need it for master process?
m_sshParameters.x11DisplayName
= q->m_setup.m_extraData.value("Ssh.X11ForwardToDisplay").toString();
- if (SshSettings::connectionSharingEnabled()) {
+ if (m_useConnectionSharing) {
m_connecting = true;
- m_connectionHandle.reset(new SshConnectionHandle(m_devicePrivate->q->sharedFromThis()));
+ m_connectionHandle.reset(new SshConnectionHandle(m_device));
m_connectionHandle->setParent(this);
connect(m_connectionHandle.get(), &SshConnectionHandle::connected,
this, &SshProcessInterfacePrivate::handleConnected);
connect(m_connectionHandle.get(), &SshConnectionHandle::disconnected,
this, &SshProcessInterfacePrivate::handleDisconnected);
- m_devicePrivate->attachToSharedConnection(m_connectionHandle.get(), m_sshParameters);
+ auto linuxDevice = m_device.dynamicCast<const LinuxDevice>();
+ QTC_ASSERT(linuxDevice, handleDone(); return);
+ linuxDevice->connectionAccess()
+ ->attachToSharedConnection(m_connectionHandle.get(), m_sshParameters);
} else {
doStart();
}
@@ -749,34 +681,6 @@ void SshProcessInterfacePrivate::handleDisconnected(const ProcessResultData &res
emit q->done(resultData); // TODO: don't emit done() on process finished afterwards
}
-void SshProcessInterfacePrivate::handleStarted()
-{
- const qint64 processId = m_process.usesTerminal() ? m_process.processId() : 0;
- // By default emits started signal, Linux impl doesn't emit it when terminal is off.
- q->handleStarted(processId);
-}
-
-void SshProcessInterfacePrivate::handleDone()
-{
- if (m_connectionHandle) // TODO: should it disconnect from signals first?
- m_connectionHandle.release()->deleteLater();
- q->handleDone(m_process.resultData());
-}
-
-void SshProcessInterfacePrivate::handleReadyReadStandardOutput()
-{
- // By default emits signal. LinuxProcessImpl does custom parsing for processId
- // and emits delayed start() - only when terminal is off.
- q->handleReadyReadStandardOutput(m_process.readAllRawStandardOutput());
-}
-
-void SshProcessInterfacePrivate::handleReadyReadStandardError()
-{
- // By default emits signal. LinuxProcessImpl buffers the error channel until
- // it emits delayed start() - only when terminal is off.
- q->handleReadyReadStandardError(m_process.readAllRawStandardError());
-}
-
void SshProcessInterfacePrivate::clearForStart()
{
m_result = {};
@@ -787,8 +691,11 @@ void SshProcessInterfacePrivate::doStart()
m_process.setProcessImpl(q->m_setup.m_processImpl);
m_process.setProcessMode(q->m_setup.m_processMode);
m_process.setTerminalMode(q->m_setup.m_terminalMode);
+ m_process.setPtyData(q->m_setup.m_ptyData);
m_process.setReaperTimeout(q->m_setup.m_reaperTimeout);
m_process.setWriteData(q->m_setup.m_writeData);
+ m_process.setCreateConsoleOnWindows(q->m_setup.m_createConsoleOnWindows);
+
// TODO: what about other fields from m_setup?
SshParameters::setupSshEnvironment(&m_process);
if (!m_sshParameters.x11DisplayName.isEmpty()) {
@@ -798,32 +705,72 @@ void SshProcessInterfacePrivate::doStart()
env.set("DISPLAY", m_sshParameters.x11DisplayName);
m_process.setControlEnvironment(env);
}
+ m_process.setExtraData(q->m_setup.m_extraData);
m_process.setCommand(fullLocalCommandLine());
m_process.start();
}
CommandLine SshProcessInterfacePrivate::fullLocalCommandLine() const
{
- CommandLine cmd{SshSettings::sshFilePath()};
+ const FilePath sshBinary = SshSettings::sshFilePath();
+ const bool useTerminal = q->m_setup.m_terminalMode != TerminalMode::Off || q->m_setup.m_ptyData;
+ const bool usePidMarker = !useTerminal;
+ const bool sourceProfile = m_device->extraData(Constants::SourceProfile).toBool();
+ const bool useX = !m_sshParameters.x11DisplayName.isEmpty();
+
+ CommandLine cmd{sshBinary};
- if (!m_sshParameters.x11DisplayName.isEmpty())
+ if (useX)
cmd.addArg("-X");
- if (q->m_setup.m_terminalMode != TerminalMode::Off)
+ if (useTerminal)
cmd.addArg("-tt");
cmd.addArg("-q");
- QStringList options = m_sshParameters.connectionOptions(SshSettings::sshFilePath());
+ cmd.addArgs(m_sshParameters.connectionOptions(sshBinary));
if (!m_socketFilePath.isEmpty())
- options << "-o" << ("ControlPath=" + m_socketFilePath);
- options << m_sshParameters.host();
- cmd.addArgs(options);
+ cmd.addArgs({"-o", "ControlPath=" + m_socketFilePath});
+
+ cmd.addArg(m_sshParameters.host());
- CommandLine remoteWithLocalPath = q->m_setup.m_commandLine;
- FilePath executable = FilePath::fromParts({}, {}, remoteWithLocalPath.executable().path());
- remoteWithLocalPath.setExecutable(executable);
+ CommandLine commandLine = q->m_setup.m_commandLine;
+ FilePath executable = FilePath::fromParts({}, {}, commandLine.executable().path());
+ commandLine.setExecutable(executable);
+
+ CommandLine inner;
+
+ if (!commandLine.isEmpty() && sourceProfile) {
+ const QStringList rcFilesToSource = {"/etc/profile", "$HOME/.profile"};
+ for (const QString &filePath : rcFilesToSource) {
+ inner.addArgs({"test", "-f", filePath});
+ inner.addArgs("&&", CommandLine::Raw);
+ inner.addArgs({".", filePath});
+ inner.addArgs(";", CommandLine::Raw);
+ }
+ }
+
+ const FilePath &workingDirectory = q->m_setup.m_workingDirectory;
+ if (!workingDirectory.isEmpty()) {
+ inner.addArgs({"cd", workingDirectory.path()});
+ inner.addArgs("&&", CommandLine::Raw);
+ }
+
+ if (usePidMarker)
+ inner.addArgs(QString("echo ") + s_pidMarker + "$$" + s_pidMarker + " && ", CommandLine::Raw);
+
+ const Environment &env = q->m_setup.m_environment;
+ env.forEachEntry([&](const QString &key, const QString &value, bool) {
+ inner.addArgs(key + "='" + env.expandVariables(value) + '\'', CommandLine::Raw);
+ });
+
+ if (!useTerminal && !commandLine.isEmpty())
+ inner.addArg("exec");
+
+ if (!commandLine.isEmpty())
+ inner.addCommandLineAsArgs(commandLine, CommandLine::Raw);
+
+ cmd.addArg(inner.arguments());
- cmd.addArg(q->fullCommandLine(remoteWithLocalPath));
return cmd;
}
@@ -848,7 +795,7 @@ class ShellThreadHandler : public QObject
}
private:
- void setupShellProcess(QtcProcess *shellProcess) override
+ void setupShellProcess(Process *shellProcess) override
{
SshParameters::setupSshEnvironment(shellProcess);
shellProcess->setCommand(m_cmdLine);
@@ -857,7 +804,7 @@ class ShellThreadHandler : public QObject
CommandLine createFallbackCommand(const CommandLine &cmdLine) override
{
CommandLine result = cmdLine;
- result.setExecutable(cmdLine.executable().onDevice(m_devicePath));
+ result.setExecutable(m_devicePath.withNewMappedPath(cmdLine.executable())); // Needed?
return result;
}
@@ -995,9 +942,16 @@ LinuxDevice::LinuxDevice()
{
setFileAccess(&d->m_fileAccess);
setDisplayType(Tr::tr("Remote Linux"));
- setDefaultDisplayName(Tr::tr("Remote Linux Device"));
setOsType(OsTypeLinux);
+ setupId(IDevice::ManuallyAdded, Utils::Id());
+ setType(Constants::GenericLinuxOsType);
+ setMachineType(IDevice::Hardware);
+ setFreePorts(PortList::fromString(QLatin1String("10000-10100")));
+ SshParameters sshParams;
+ sshParams.timeout = 10;
+ setSshParameters(sshParams);
+
addDeviceAction({Tr::tr("Deploy Public Key..."), [](const IDevice::Ptr &device, QWidget *parent) {
if (auto d = PublicKeyDeploymentDialog::createDialog(device, parent)) {
d->exec();
@@ -1006,45 +960,31 @@ LinuxDevice::LinuxDevice()
}});
setOpenTerminal([this](const Environment &env, const FilePath &workingDir) {
- QtcProcess * const proc = new QtcProcess;
- d->m_terminals.append(proc);
- QObject::connect(proc, &QtcProcess::done, proc, [this, proc] {
- if (proc->error() != QProcess::UnknownError) {
- const QString errorString = proc->errorString();
- QString message;
- if (proc->error() == QProcess::FailedToStart)
- message = Tr::tr("Error starting remote shell.");
- else if (errorString.isEmpty())
- message = Tr::tr("Error running remote shell.");
- else
- message = Tr::tr("Error running remote shell: %1").arg(errorString);
- Core::MessageManager::writeDisrupting(message);
- }
- proc->deleteLater();
- d->m_terminals.removeOne(proc);
- });
+ Process proc;
- // We recreate the same way that QtcProcess uses to create the actual environment.
- const Environment finalEnv = (!env.hasChanges() && env.combineWithDeviceEnvironment())
- ? d->getEnvironment()
- : env;
// If we will not set any environment variables, we can leave out the shell executable
// as the "ssh ..." call will automatically launch the default shell if there are
// no arguments. But if we will set environment variables, we need to explicitly
// specify the shell executable.
- const QString shell = finalEnv.hasChanges() ? finalEnv.value_or("SHELL", "/bin/sh")
- : QString();
-
- proc->setCommand({filePath(shell), {}});
- proc->setTerminalMode(TerminalMode::On);
- proc->setEnvironment(env);
- proc->setWorkingDirectory(workingDir);
- proc->start();
+ const QString shell = env.hasChanges() ? env.value_or("SHELL", "/bin/sh") : QString();
+
+ proc.setCommand({filePath(shell), {}});
+ proc.setTerminalMode(TerminalMode::Detached);
+ proc.setEnvironment(env);
+ proc.setWorkingDirectory(workingDir);
+ proc.start();
});
addDeviceAction({Tr::tr("Open Remote Shell"), [](const IDevice::Ptr &device, QWidget *) {
device->openTerminal(Environment(), FilePath());
}});
+ setQmlRunCommand(filePath("qml"));
+}
+
+void LinuxDevice::_setOsType(Utils::OsType osType)
+{
+ qCDebug(linuxDeviceLog) << "Setting OS type to" << osType << "for" << displayName();
+ IDevice::setOsType(osType);
}
LinuxDevice::~LinuxDevice()
@@ -1057,38 +997,9 @@ IDeviceWidget *LinuxDevice::createWidget()
return new Internal::GenericLinuxDeviceConfigurationWidget(sharedFromThis());
}
-bool LinuxDevice::canAutoDetectPorts() const
-{
- return true;
-}
-
-PortsGatheringMethod LinuxDevice::portsGatheringMethod() const
-{
- return {
- [this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine {
- // We might encounter the situation that protocol is given IPv6
- // but the consumer of the free port information decides to open
- // an IPv4(only) port. As a result the next IPv6 scan will
- // report the port again as open (in IPv6 namespace), while the
- // same port in IPv4 namespace might still be blocked, and
- // re-use of this port fails.
- // GDBserver behaves exactly like this.
-
- Q_UNUSED(protocol)
-
- // /proc/net/tcp* covers /proc/net/tcp and /proc/net/tcp6
- return {filePath("sed"),
- "-e 's/.*: [[:xdigit:]]*:\\([[:xdigit:]]\\{4\\}\\).*/\\1/g' /proc/net/tcp*",
- CommandLine::Raw};
- },
-
- &Port::parseFromSedOutput
- };
-}
-
DeviceProcessList *LinuxDevice::createProcessListModel(QObject *parent) const
{
- return new LinuxDeviceProcessList(sharedFromThis(), parent);
+ return new ProcessList(sharedFromThis(), parent);
}
DeviceTester *LinuxDevice::createDeviceTester() const
@@ -1127,7 +1038,7 @@ bool LinuxDevice::handlesFile(const FilePath &filePath) const
ProcessInterface *LinuxDevice::createProcessInterface() const
{
- return new LinuxProcessInterface(this);
+ return new SshProcessInterface(sharedFromThis());
}
LinuxDevicePrivate::LinuxDevicePrivate(LinuxDevice *parent)
@@ -1142,7 +1053,6 @@ LinuxDevicePrivate::LinuxDevicePrivate(LinuxDevice *parent)
LinuxDevicePrivate::~LinuxDevicePrivate()
{
- qDeleteAll(m_terminals);
auto closeShell = [this] {
m_shellThread.quit();
m_shellThread.wait();
@@ -1153,6 +1063,23 @@ LinuxDevicePrivate::~LinuxDevicePrivate()
QMetaObject::invokeMethod(&m_shellThread, closeShell, Qt::BlockingQueuedConnection);
}
+void LinuxDevicePrivate::queryOsType(std::function<RunResult(const CommandLine &)> runInShell)
+{
+ const RunResult result = runInShell({"uname", {"-s"}, OsType::OsTypeLinux});
+ if (result.exitCode != 0)
+ q->_setOsType(OsTypeOtherUnix);
+ const QString osName = QString::fromUtf8(result.stdOut).trimmed();
+ if (osName == "Darwin")
+ q->_setOsType(OsTypeMac);
+ if (osName == "Linux")
+ q->_setOsType(OsTypeLinux);
+}
+
+void LinuxDevicePrivate::checkOsType()
+{
+ queryOsType([this](const CommandLine &cmd) { return runInShell(cmd); });
+}
+
// Call me with shell mutex locked
bool LinuxDevicePrivate::setupShell()
{
@@ -1166,6 +1093,10 @@ bool LinuxDevicePrivate::setupShell()
QMetaObject::invokeMethod(m_handler, [this, sshParameters] {
return m_handler->start(sshParameters);
}, Qt::BlockingQueuedConnection, &ok);
+
+ if (ok) {
+ queryOsType([this](const CommandLine &cmd) { return m_handler->runInShell(cmd); });
+ }
return ok;
}
@@ -1208,13 +1139,9 @@ static FilePaths dirsToCreate(const FilesToTransfer &files)
return sorted(std::move(dirs));
}
-static QByteArray transferCommand(const FileTransferDirection direction, bool link)
+static QByteArray transferCommand(bool link)
{
- if (direction == FileTransferDirection::Upload)
- return link ? "ln -s" : "put";
- if (direction == FileTransferDirection::Download)
- return "get";
- return {};
+ return link ? "ln -s" : "put";
}
class SshTransferInterface : public FileTransferInterface
@@ -1222,21 +1149,20 @@ class SshTransferInterface : public FileTransferInterface
Q_OBJECT
protected:
- SshTransferInterface(const FileTransferSetupData &setup, LinuxDevicePrivate *devicePrivate)
+ SshTransferInterface(const FileTransferSetupData &setup, const IDevice::ConstPtr &device)
: FileTransferInterface(setup)
- , m_device(devicePrivate->q->sharedFromThis())
- , m_devicePrivate(devicePrivate)
+ , m_device(device)
, m_process(this)
{
- m_direction = m_setup.m_files.isEmpty() ? FileTransferDirection::Invalid
- : m_setup.m_files.first().direction();
SshParameters::setupSshEnvironment(&m_process);
- connect(&m_process, &QtcProcess::readyReadStandardOutput, this, [this] {
+ connect(&m_process, &Process::readyReadStandardOutput, this, [this] {
emit progress(QString::fromLocal8Bit(m_process.readAllRawStandardOutput()));
});
- connect(&m_process, &QtcProcess::done, this, &SshTransferInterface::doneImpl);
+ connect(&m_process, &Process::done, this, &SshTransferInterface::doneImpl);
}
+ IDevice::ConstPtr device() const { return m_device; }
+
bool handleError()
{
ProcessResultData resultData = m_process.resultData();
@@ -1270,10 +1196,9 @@ protected:
}
QString host() const { return m_sshParameters.host(); }
- QString userAtHost() const { return m_sshParameters.userName() + '@' + m_sshParameters.host(); }
+ QString userAtHost() const { return m_sshParameters.userAtHost(); }
- QtcProcess &process() { return m_process; }
- FileTransferDirection direction() const { return m_direction; }
+ Process &process() { return m_process; }
private:
virtual void startImpl() = 0;
@@ -1282,7 +1207,11 @@ private:
void start() final
{
m_sshParameters = displayless(m_device->sshParameters());
- if (SshSettings::connectionSharingEnabled()) {
+ const Id linkDeviceId = Id::fromSetting(m_device->extraData(Constants::LinkDevice));
+ const auto linkDevice = DeviceManager::instance()->find(linkDeviceId);
+ const bool useConnectionSharing = !linkDevice && SshSettings::connectionSharingEnabled();
+
+ if (useConnectionSharing) {
m_connecting = true;
m_connectionHandle.reset(new SshConnectionHandle(m_device));
m_connectionHandle->setParent(this);
@@ -1290,7 +1219,10 @@ private:
this, &SshTransferInterface::handleConnected);
connect(m_connectionHandle.get(), &SshConnectionHandle::disconnected,
this, &SshTransferInterface::handleDisconnected);
- m_devicePrivate->attachToSharedConnection(m_connectionHandle.get(), m_sshParameters);
+ auto linuxDevice = m_device.dynamicCast<const LinuxDevice>();
+ QTC_ASSERT(linuxDevice, startFailed("No Linux device"); return);
+ linuxDevice->connectionAccess()
+ ->attachToSharedConnection(m_connectionHandle.get(), m_sshParameters);
} else {
startImpl();
}
@@ -1318,28 +1250,33 @@ private:
}
IDevice::ConstPtr m_device;
- LinuxDevicePrivate *m_devicePrivate = nullptr;
SshParameters m_sshParameters;
- FileTransferDirection m_direction = FileTransferDirection::Invalid; // helper
// ssh shared connection related
std::unique_ptr<SshConnectionHandle> m_connectionHandle;
QString m_socketFilePath;
bool m_connecting = false;
- QtcProcess m_process;
+ Process m_process;
};
class SftpTransferImpl : public SshTransferInterface
{
public:
- SftpTransferImpl(const FileTransferSetupData &setup, LinuxDevicePrivate *devicePrivate)
- : SshTransferInterface(setup, devicePrivate) { }
+ SftpTransferImpl(const FileTransferSetupData &setup, const IDevice::ConstPtr &device)
+ : SshTransferInterface(setup, device)
+ {}
private:
void startImpl() final
{
- const FilePath sftpBinary = SshSettings::sftpFilePath();
+ FilePath sftpBinary = SshSettings::sftpFilePath();
+
+ // This is a hack. We only test the last hop here.
+ const Id linkDeviceId = Id::fromSetting(device()->extraData(Constants::LinkDevice));
+ if (const auto linkDevice = DeviceManager::instance()->find(linkDeviceId))
+ sftpBinary = linkDevice->filePath(sftpBinary.fileName()).searchInPath();
+
if (!sftpBinary.exists()) {
startFailed(Tr::tr("\"sftp\" binary \"%1\" does not exist.")
.arg(sftpBinary.toUserOutput()));
@@ -1349,35 +1286,26 @@ private:
QByteArray batchData;
const FilePaths dirs = dirsToCreate(m_setup.m_files);
- for (const FilePath &dir : dirs) {
- if (direction() == FileTransferDirection::Upload) {
- batchData += "-mkdir " + ProcessArgs::quoteArgUnix(dir.path()).toLocal8Bit() + '\n';
- } else if (direction() == FileTransferDirection::Download) {
- if (!QDir::root().mkpath(dir.path())) {
- startFailed(Tr::tr("Failed to create local directory \"%1\".")
- .arg(QDir::toNativeSeparators(dir.path())));
- return;
- }
- }
- }
+ for (const FilePath &dir : dirs)
+ batchData += "-mkdir " + ProcessArgs::quoteArgUnix(dir.path()).toLocal8Bit() + '\n';
for (const FileToTransfer &file : m_setup.m_files) {
FilePath sourceFileOrLinkTarget = file.m_source;
bool link = false;
- if (direction() == FileTransferDirection::Upload) {
- const QFileInfo fi(file.m_source.toFileInfo());
- if (fi.isSymLink()) {
- link = true;
- batchData += "-rm " + ProcessArgs::quoteArgUnix(
- file.m_target.path()).toLocal8Bit() + '\n';
- // see QTBUG-5817.
- sourceFileOrLinkTarget =
- sourceFileOrLinkTarget.withNewPath(fi.dir().relativeFilePath(fi.symLinkTarget()));
- }
- }
- batchData += transferCommand(direction(), link) + ' '
- + ProcessArgs::quoteArgUnix(sourceFileOrLinkTarget.path()).toLocal8Bit() + ' '
- + ProcessArgs::quoteArgUnix(file.m_target.path()).toLocal8Bit() + '\n';
+
+ const QFileInfo fi(file.m_source.toFileInfo());
+ if (fi.isSymLink()) {
+ link = true;
+ batchData += "-rm " + ProcessArgs::quoteArgUnix(
+ file.m_target.path()).toLocal8Bit() + '\n';
+ // see QTBUG-5817.
+ sourceFileOrLinkTarget =
+ sourceFileOrLinkTarget.withNewPath(fi.dir().relativeFilePath(fi.symLinkTarget()));
+ }
+
+ batchData += transferCommand(link) + ' '
+ + ProcessArgs::quoteArgUnix(sourceFileOrLinkTarget.path()).toLocal8Bit() + ' '
+ + ProcessArgs::quoteArgUnix(file.m_target.path()).toLocal8Bit() + '\n';
}
process().setCommand({sftpBinary, fullConnectionOptions() << "-b" << "-" << host()});
process().setWriteData(batchData);
@@ -1390,8 +1318,8 @@ private:
class RsyncTransferImpl : public SshTransferInterface
{
public:
- RsyncTransferImpl(const FileTransferSetupData &setup, LinuxDevicePrivate *devicePrivate)
- : SshTransferInterface(setup, devicePrivate)
+ RsyncTransferImpl(const FileTransferSetupData &setup, const IDevice::ConstPtr &device)
+ : SshTransferInterface(setup, device)
{ }
private:
@@ -1442,8 +1370,7 @@ private:
if (!HostOsInfo::isWindowsHost())
return file;
- QString localFilePath = direction() == FileTransferDirection::Upload
- ? file.m_source.path() : file.m_target.path();
+ QString localFilePath = file.m_source.path();
localFilePath = '/' + localFilePath.at(0) + localFilePath.mid(2);
if (anyOf(options, [](const QString &opt) {
return opt.contains("cygwin", Qt::CaseInsensitive); })) {
@@ -1451,30 +1378,19 @@ private:
}
FileToTransfer fixedFile = file;
- if (direction() == FileTransferDirection::Upload)
- fixedFile.m_source = fixedFile.m_source.withNewPath(localFilePath);
- else
- fixedFile.m_target = fixedFile.m_target.withNewPath(localFilePath);
+ fixedFile.m_source = fixedFile.m_source.withNewPath(localFilePath);
return fixedFile;
}
QPair<QString, QString> fixPaths(const FileToTransfer &file, const QString &remoteHost) const
{
- FilePath localPath;
- FilePath remotePath;
- if (direction() == FileTransferDirection::Upload) {
- localPath = file.m_source;
- remotePath = file.m_target;
- } else {
- remotePath = file.m_source;
- localPath = file.m_target;
- }
+ FilePath localPath = file.m_source;
+ FilePath remotePath = file.m_target;
const QString local = (localPath.isDir() && localPath.path().back() != '/')
? localPath.path() + '/' : localPath.path();
const QString remote = remoteHost + ':' + remotePath.path();
- return direction() == FileTransferDirection::Upload ? qMakePair(local, remote)
- : qMakePair(remote, local);
+ return qMakePair(local, remote);
}
int m_currentIndex = 0;
@@ -1483,7 +1399,7 @@ private:
class GenericTransferImpl : public FileTransferInterface
{
public:
- GenericTransferImpl(const FileTransferSetupData &setup, LinuxDevicePrivate *)
+ GenericTransferImpl(const FileTransferSetupData &setup)
: FileTransferInterface(setup)
{}
@@ -1525,8 +1441,9 @@ private:
.arg(m_currentIndex)
.arg(m_fileCount)
.arg(source.toUserOutput(), target.toUserOutput()));
- if (!source.copyFile(target)) {
- result.m_errorString = Tr::tr("Failed.");
+ expected_str<void> copyResult = source.copyFile(target);
+ if (!copyResult) {
+ result.m_errorString = Tr::tr("Failed: %1").arg(copyResult.error());
result.m_exitCode = -1; // Random pick
emit done(result);
return;
@@ -1545,14 +1462,24 @@ FileTransferInterface *LinuxDevice::createFileTransferInterface(
const FileTransferSetupData &setup) const
{
switch (setup.m_method) {
- case FileTransferMethod::Sftp: return new SftpTransferImpl(setup, d);
- case FileTransferMethod::Rsync: return new RsyncTransferImpl(setup, d);
- case FileTransferMethod::GenericCopy: return new GenericTransferImpl(setup, d);
+ case FileTransferMethod::Sftp: return new SftpTransferImpl(setup, sharedFromThis());
+ case FileTransferMethod::Rsync: return new RsyncTransferImpl(setup, sharedFromThis());
+ case FileTransferMethod::GenericCopy: return new GenericTransferImpl(setup);
}
QTC_CHECK(false);
return {};
}
+LinuxDevicePrivate *LinuxDevice::connectionAccess() const
+{
+ return d;
+}
+
+void LinuxDevice::checkOsType()
+{
+ d->checkOsType();
+}
+
namespace Internal {
// Factory
@@ -1563,6 +1490,7 @@ LinuxDeviceFactory::LinuxDeviceFactory()
setDisplayName(Tr::tr("Remote Linux Device"));
setIcon(QIcon());
setConstructionFunction(&LinuxDevice::create);
+ setQuickCreationAllowed(true);
setCreator([] {
GenericLinuxDeviceConfigurationWizard wizard(Core::ICore::dialogParent());
if (wizard.exec() != QDialog::Accepted)
diff --git a/src/plugins/remotelinux/linuxdevice.h b/src/plugins/remotelinux/linuxdevice.h
index ac65d6e89c..2094b426b4 100644
--- a/src/plugins/remotelinux/linuxdevice.h
+++ b/src/plugins/remotelinux/linuxdevice.h
@@ -22,8 +22,6 @@ public:
ProjectExplorer::IDeviceWidget *createWidget() override;
- bool canAutoDetectPorts() const override;
- ProjectExplorer::PortsGatheringMethod portsGatheringMethod() const override;
bool canCreateProcessModel() const override { return true; }
ProjectExplorer::DeviceProcessList *createProcessListModel(QObject *parent) const override;
bool hasDeviceTester() const override { return true; }
@@ -41,12 +39,16 @@ public:
ProjectExplorer::FileTransferInterface *createFileTransferInterface(
const ProjectExplorer::FileTransferSetupData &setup) const override;
+ class LinuxDevicePrivate *connectionAccess() const;
+ void checkOsType() override;
+
protected:
LinuxDevice();
+ void _setOsType(Utils::OsType osType);
+
class LinuxDevicePrivate *d;
- friend class SshProcessInterface;
- friend class SshTransferInterface;
+ friend class LinuxDevicePrivate;
};
namespace Internal {
diff --git a/src/plugins/remotelinux/linuxdevicetester.cpp b/src/plugins/remotelinux/linuxdevicetester.cpp
index 3c34a63820..37aea36112 100644
--- a/src/plugins/remotelinux/linuxdevicetester.cpp
+++ b/src/plugins/remotelinux/linuxdevicetester.cpp
@@ -10,20 +10,21 @@
#include <projectexplorer/devicesupport/filetransfer.h>
#include <utils/algorithm.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
+#include <utils/stringutils.h>
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
-using namespace Utils::Tasking;
namespace RemoteLinux {
namespace Internal {
struct TransferStorage
{
- bool sftpWorks = false;
+ bool useGenericCopy = false;
};
class GenericLinuxDeviceTesterPrivate
@@ -33,7 +34,7 @@ public:
QStringList commandsToTest() const;
- TaskItem echoTask() const;
+ TaskItem echoTask(const QString &contents) const;
TaskItem unameTask() const;
TaskItem gathererTask() const;
TaskItem transferTask(FileTransferMethod method,
@@ -49,8 +50,6 @@ public:
QList<TaskItem> m_extraTests;
};
-static const char s_echoContents[] = "Hello Remote World!";
-
QStringList GenericLinuxDeviceTesterPrivate::commandsToTest() const
{
static const QStringList s_commandsToTest = {"base64",
@@ -91,48 +90,49 @@ QStringList GenericLinuxDeviceTesterPrivate::commandsToTest() const
return commands;
}
-TaskItem GenericLinuxDeviceTesterPrivate::echoTask() const
+TaskItem GenericLinuxDeviceTesterPrivate::echoTask(const QString &contents) const
{
- const auto setup = [this](QtcProcess &process) {
+ const auto setup = [this, contents](Process &process) {
emit q->progressMessage(Tr::tr("Sending echo to device..."));
- process.setCommand({m_device->filePath("echo"), {s_echoContents}});
+ process.setCommand({m_device->filePath("echo"), {contents}});
};
- const auto done = [this](const QtcProcess &process) {
- const QString reply = process.cleanedStdOut().chopped(1); // Remove trailing '\n'
- if (reply != s_echoContents)
- emit q->errorMessage(Tr::tr("Device replied to echo with unexpected contents.") + '\n');
+ const auto done = [this, contents](const Process &process) {
+ const QString reply = Utils::chopIfEndsWith(process.cleanedStdOut(), '\n');
+ if (reply != contents)
+ emit q->errorMessage(Tr::tr("Device replied to echo with unexpected contents: \"%1\"")
+ .arg(reply) + '\n');
else
emit q->progressMessage(Tr::tr("Device replied to echo with expected contents.") + '\n');
};
- const auto error = [this](const QtcProcess &process) {
+ const auto error = [this](const Process &process) {
const QString stdErrOutput = process.cleanedStdErr();
if (!stdErrOutput.isEmpty())
emit q->errorMessage(Tr::tr("echo failed: %1").arg(stdErrOutput) + '\n');
else
emit q->errorMessage(Tr::tr("echo failed.") + '\n');
};
- return Process(setup, done, error);
+ return ProcessTask(setup, done, error);
}
TaskItem GenericLinuxDeviceTesterPrivate::unameTask() const
{
- const auto setup = [this](QtcProcess &process) {
+ const auto setup = [this](Process &process) {
emit q->progressMessage(Tr::tr("Checking kernel version..."));
process.setCommand({m_device->filePath("uname"), {"-rsm"}});
};
- const auto done = [this](const QtcProcess &process) {
+ const auto done = [this](const Process &process) {
emit q->progressMessage(process.cleanedStdOut());
};
- const auto error = [this](const QtcProcess &process) {
+ const auto error = [this](const Process &process) {
const QString stdErrOutput = process.cleanedStdErr();
if (!stdErrOutput.isEmpty())
emit q->errorMessage(Tr::tr("uname failed: %1").arg(stdErrOutput) + '\n');
else
emit q->errorMessage(Tr::tr("uname failed.") + '\n');
};
- return Tasking::Group {
- optional,
- Process(setup, done, error)
+ return Group {
+ finishAllAndDone,
+ ProcessTask(setup, done, error)
};
}
@@ -154,9 +154,14 @@ TaskItem GenericLinuxDeviceTesterPrivate::gathererTask() const
}
};
const auto error = [this](const DeviceUsedPortsGatherer &gatherer) {
- emit q->errorMessage(Tr::tr("Error gathering ports: %1").arg(gatherer.errorString()) + '\n');
+ emit q->errorMessage(Tr::tr("Error gathering ports: %1").arg(gatherer.errorString()) + '\n'
+ + Tr::tr("Some tools will not work out of the box.\n"));
+ };
+
+ return Group {
+ finishAllAndDone,
+ DeviceUsedPortsGathererTask(setup, done, error)
};
- return PortGatherer(setup, done, error);
}
TaskItem GenericLinuxDeviceTesterPrivate::transferTask(FileTransferMethod method,
@@ -173,8 +178,10 @@ TaskItem GenericLinuxDeviceTesterPrivate::transferTask(FileTransferMethod method
emit q->progressMessage(Tr::tr("\"%1\" is functional.\n").arg(methodName));
if (method == FileTransferMethod::Rsync)
m_device->setExtraData(Constants::SupportsRSync, true);
+ else if (method == FileTransferMethod::Sftp)
+ m_device->setExtraData(Constants::SupportsSftp, true);
else
- storage->sftpWorks = true;
+ storage->useGenericCopy = true;
};
const auto error = [this, method, storage](const FileTransfer &transfer) {
const QString methodName = FileTransfer::transferMethodName(method);
@@ -189,28 +196,36 @@ TaskItem GenericLinuxDeviceTesterPrivate::transferTask(FileTransferMethod method
.arg(methodName).arg(resultData.m_exitCode).arg(resultData.m_errorString);
}
emit q->errorMessage(error);
- if (method == FileTransferMethod::Rsync) {
+ if (method == FileTransferMethod::Rsync)
m_device->setExtraData(Constants::SupportsRSync, false);
- if (!storage->sftpWorks)
- return;
+ else if (method == FileTransferMethod::Sftp)
+ m_device->setExtraData(Constants::SupportsSftp, false);
+
+ const QVariant supportsRSync = m_device->extraData(Constants::SupportsRSync);
+ const QVariant supportsSftp = m_device->extraData(Constants::SupportsSftp);
+ if (supportsRSync.isValid() && !supportsRSync.toBool()
+ && supportsSftp.isValid() && !supportsSftp.toBool()) {
+ const QString generic = FileTransfer::transferMethodName(FileTransferMethod::GenericCopy);
const QString sftp = FileTransfer::transferMethodName(FileTransferMethod::Sftp);
- const QString rsync = methodName;
+ const QString rsync = FileTransfer::transferMethodName(FileTransferMethod::Rsync);
emit q->progressMessage(Tr::tr("\"%1\" will be used for deployment, because \"%2\" "
- "is not available.\n").arg(sftp, rsync));
+ "and \"%3\" are not available.\n")
+ .arg(generic, sftp, rsync));
}
};
- return TransferTest(setup, done, error);
+ return FileTransferTestTask(setup, done, error);
}
TaskItem GenericLinuxDeviceTesterPrivate::transferTasks() const
{
TreeStorage<TransferStorage> storage;
- return Tasking::Group {
+ return Group {
continueOnDone,
Storage(storage),
+ transferTask(FileTransferMethod::GenericCopy, storage),
transferTask(FileTransferMethod::Sftp, storage),
transferTask(FileTransferMethod::Rsync, storage),
- OnGroupError([this] { emit q->errorMessage(Tr::tr("Deployment to this device will not "
+ onGroupError([this] { emit q->errorMessage(Tr::tr("Deployment to this device will not "
"work out of the box.\n"));
})
};
@@ -218,34 +233,34 @@ TaskItem GenericLinuxDeviceTesterPrivate::transferTasks() const
TaskItem GenericLinuxDeviceTesterPrivate::commandTask(const QString &commandName) const
{
- const auto setup = [this, commandName](QtcProcess &process) {
+ const auto setup = [this, commandName](Process &process) {
emit q->progressMessage(Tr::tr("%1...").arg(commandName));
CommandLine command{m_device->filePath("/bin/sh"), {"-c"}};
command.addArgs(QLatin1String("\"command -v %1\"").arg(commandName), CommandLine::Raw);
process.setCommand(command);
};
- const auto done = [this, commandName](const QtcProcess &) {
+ const auto done = [this, commandName](const Process &) {
emit q->progressMessage(Tr::tr("%1 found.").arg(commandName));
};
- const auto error = [this, commandName](const QtcProcess &process) {
+ const auto error = [this, commandName](const Process &process) {
const QString message = process.result() == ProcessResult::StartFailed
? Tr::tr("An error occurred while checking for %1.").arg(commandName)
+ '\n' + process.errorString()
: Tr::tr("%1 not found.").arg(commandName);
emit q->errorMessage(message);
};
- return Process(setup, done, error);
+ return ProcessTask(setup, done, error);
}
TaskItem GenericLinuxDeviceTesterPrivate::commandTasks() const
{
QList<TaskItem> tasks {continueOnError};
- tasks.append(OnGroupSetup([this] {
+ tasks.append(onGroupSetup([this] {
emit q->progressMessage(Tr::tr("Checking if required commands are available..."));
}));
for (const QString &commandName : commandsToTest())
tasks.append(commandTask(commandName));
- return Tasking::Group {tasks};
+ return Group {tasks};
}
} // namespace Internal
@@ -281,7 +296,8 @@ void GenericLinuxDeviceTester::testDevice(const IDevice::Ptr &deviceConfiguratio
};
QList<TaskItem> taskItems = {
- d->echoTask(),
+ d->echoTask("Hello"), // No quoting necessary
+ d->echoTask("Hello Remote World!"), // Checks quoting, too.
d->unameTask(),
d->gathererTask(),
d->transferTasks()
@@ -289,8 +305,8 @@ void GenericLinuxDeviceTester::testDevice(const IDevice::Ptr &deviceConfiguratio
if (!d->m_extraTests.isEmpty())
taskItems << Group { d->m_extraTests };
taskItems << d->commandTasks()
- << OnGroupDone(std::bind(allFinished, TestSuccess))
- << OnGroupError(std::bind(allFinished, TestFailure));
+ << onGroupDone(std::bind(allFinished, TestSuccess))
+ << onGroupError(std::bind(allFinished, TestFailure));
d->m_taskTree.reset(new TaskTree(taskItems));
d->m_taskTree->start();
diff --git a/src/plugins/remotelinux/linuxdevicetester.h b/src/plugins/remotelinux/linuxdevicetester.h
index 5c1210f9a1..3fda1ecb3a 100644
--- a/src/plugins/remotelinux/linuxdevicetester.h
+++ b/src/plugins/remotelinux/linuxdevicetester.h
@@ -7,7 +7,7 @@
#include <projectexplorer/devicesupport/idevice.h>
-namespace Utils::Tasking { class TaskItem; }
+namespace Tasking { class TaskItem; }
namespace RemoteLinux {
@@ -22,7 +22,7 @@ public:
~GenericLinuxDeviceTester() override;
void setExtraCommandsToTest(const QStringList &extraCommands);
- void setExtraTests(const QList<Utils::Tasking::TaskItem> &extraTests);
+ void setExtraTests(const QList<Tasking::TaskItem> &extraTests);
void testDevice(const ProjectExplorer::IDevice::Ptr &deviceConfiguration) override;
void stopTest() override;
diff --git a/src/plugins/remotelinux/linuxprocessinterface.h b/src/plugins/remotelinux/linuxprocessinterface.h
index 949c593420..e2a8e82fc8 100644
--- a/src/plugins/remotelinux/linuxprocessinterface.h
+++ b/src/plugins/remotelinux/linuxprocessinterface.h
@@ -5,32 +5,35 @@
#include "remotelinux_export.h"
-#include "sshprocessinterface.h"
+#include "linuxdevice.h"
+
+#include <utils/processinterface.h>
namespace RemoteLinux {
-class LinuxDevice;
class SshProcessInterfacePrivate;
-class REMOTELINUX_EXPORT LinuxProcessInterface : public SshProcessInterface
+class REMOTELINUX_EXPORT SshProcessInterface : public Utils::ProcessInterface
{
public:
- LinuxProcessInterface(const LinuxDevice *linuxDevice);
- ~LinuxProcessInterface();
+ explicit SshProcessInterface(const ProjectExplorer::IDevice::ConstPtr &device);
+ ~SshProcessInterface();
-private:
- void handleSendControlSignal(Utils::ControlSignal controlSignal) override;
+protected:
+ void emitStarted(qint64 processId);
+ void killIfRunning();
+ qint64 processId() const;
+ bool runInShell(const Utils::CommandLine &command, const QByteArray &data = {});
- void handleStarted(qint64 processId) final;
- void handleDone(const Utils::ProcessResultData &resultData) final;
- void handleReadyReadStandardOutput(const QByteArray &outputData) final;
- void handleReadyReadStandardError(const QByteArray &errorData) final;
+private:
+ virtual void handleSendControlSignal(Utils::ControlSignal controlSignal);
- QString fullCommandLine(const Utils::CommandLine &commandLine) const final;
+ void start() final;
+ qint64 write(const QByteArray &data) final;
+ void sendControlSignal(Utils::ControlSignal controlSignal) final;
- QByteArray m_output;
- QByteArray m_error;
- bool m_pidParsed = false;
+ friend class SshProcessInterfacePrivate;
+ SshProcessInterfacePrivate *d = nullptr;
};
} // namespace RemoteLinux
diff --git a/src/plugins/remotelinux/makeinstallstep.cpp b/src/plugins/remotelinux/makeinstallstep.cpp
index ea7b636458..42f0467c94 100644
--- a/src/plugins/remotelinux/makeinstallstep.cpp
+++ b/src/plugins/remotelinux/makeinstallstep.cpp
@@ -20,8 +20,8 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDirIterator>
#include <QFileInfo>
@@ -70,10 +70,9 @@ MakeInstallStep::MakeInstallStep(BuildStepList *parent, Id id) : MakeStep(parent
connect(makeAspect, &ExecutableAspect::changed,
this, &MakeInstallStep::updateCommandFromAspect);
- const auto installRootAspect = addAspect<StringAspect>();
+ const auto installRootAspect = addAspect<FilePathAspect>();
installRootAspect->setId(InstallRootAspectId);
installRootAspect->setSettingsKey(InstallRootAspectId);
- installRootAspect->setDisplayStyle(StringAspect::PathChooserDisplay);
installRootAspect->setExpectedKind(PathChooser::Directory);
installRootAspect->setLabelText(Tr::tr("Install root:"));
installRootAspect->setFilePath(rootPath);
@@ -124,16 +123,6 @@ MakeInstallStep::MakeInstallStep(BuildStepList *parent, Id id) : MakeStep(parent
});
}
-Utils::Id MakeInstallStep::stepId()
-{
- return Constants::MakeInstallStepId;
-}
-
-QString MakeInstallStep::displayName()
-{
- return Tr::tr("Install into temporary host directory");
-}
-
QWidget *MakeInstallStep::createConfigWidget()
{
// Note: this intentionally skips the MakeStep::createConfigWidget() level.
@@ -145,7 +134,7 @@ bool MakeInstallStep::init()
if (!MakeStep::init())
return false;
- const FilePath rootDir = installRoot().onDevice(makeCommand());
+ const FilePath rootDir = makeCommand().withNewPath(installRoot().path()); // FIXME: Needed?
if (rootDir.isEmpty()) {
emit addTask(BuildSystemTask(Task::Error, Tr::tr("You must provide an install root.")));
return false;
@@ -172,12 +161,10 @@ bool MakeInstallStep::init()
const MakeInstallCommand cmd = buildSystem()->makeInstallCommand(rootDir);
if (cmd.environment.hasChanges()) {
Environment env = processParameters()->environment();
- for (auto it = cmd.environment.constBegin(); it != cmd.environment.constEnd(); ++it) {
- if (cmd.environment.isEnabled(it)) {
- const QString key = cmd.environment.key(it);
- env.set(key, cmd.environment.expandedValueForKey(key));
- }
- }
+ cmd.environment.forEachEntry([&](const QString &key, const QString &value, bool enabled) {
+ if (enabled)
+ env.set(key, cmd.environment.expandVariables(value));
+ });
processParameters()->setEnvironment(env);
}
m_noInstallTarget = false;
@@ -193,7 +180,7 @@ bool MakeInstallStep::init()
void MakeInstallStep::finish(ProcessResult result)
{
if (isSuccess(result)) {
- const FilePath rootDir = installRoot().onDevice(makeCommand());
+ const FilePath rootDir = makeCommand().withNewPath(installRoot().path()); // FIXME: Needed?
m_deploymentData = DeploymentData();
m_deploymentData.setLocalInstallRoot(rootDir);
@@ -251,11 +238,8 @@ void MakeInstallStep::updateArgsFromAspect()
void MakeInstallStep::updateFullCommandLine()
{
- // FIXME: Only executable?
- static_cast<StringAspect *>(aspect(FullCommandLineAspectId))->setValue(
- QDir::toNativeSeparators(
- ProcessArgs::quoteArg(makeExecutable().toString()))
- + ' ' + userArguments());
+ CommandLine cmd{makeExecutable(), userArguments(), CommandLine::Raw};
+ static_cast<StringAspect *>(aspect(FullCommandLineAspectId))->setValue(cmd.toUserOutput());
}
void MakeInstallStep::updateFromCustomCommandLineAspect()
@@ -263,7 +247,7 @@ void MakeInstallStep::updateFromCustomCommandLineAspect()
const StringAspect * const aspect = customCommandLineAspect();
if (!aspect->isChecked())
return;
- const QStringList tokens = ProcessArgs::splitArgs(aspect->value());
+ const QStringList tokens = ProcessArgs::splitArgs(aspect->value(), HostOsInfo::hostOs());
setMakeCommand(tokens.isEmpty() ? FilePath() : FilePath::fromString(tokens.first()));
setUserArguments(ProcessArgs::joinArgs(tokens.mid(1)));
}
@@ -283,4 +267,12 @@ bool MakeInstallStep::fromMap(const QVariantMap &map)
return true;
}
-} // namespace RemoteLinux
+// Factory
+
+MakeInstallStepFactory::MakeInstallStepFactory()
+{
+ registerStep<MakeInstallStep>(Constants::MakeInstallStepId);
+ setDisplayName(Tr::tr("Install into temporary host directory"));
+}
+
+} // RemoteLinux
diff --git a/src/plugins/remotelinux/makeinstallstep.h b/src/plugins/remotelinux/makeinstallstep.h
index ec2fc3985a..3be2d5df97 100644
--- a/src/plugins/remotelinux/makeinstallstep.h
+++ b/src/plugins/remotelinux/makeinstallstep.h
@@ -16,9 +16,6 @@ class REMOTELINUX_EXPORT MakeInstallStep : public ProjectExplorer::MakeStep
public:
MakeInstallStep(ProjectExplorer::BuildStepList *parent, Utils::Id id);
- static Utils::Id stepId();
- static QString displayName();
-
private:
bool fromMap(const QVariantMap &map) override;
QWidget *createConfigWidget() override;
@@ -41,4 +38,11 @@ private:
bool m_isCmakeProject = false;
};
+class REMOTELINUX_EXPORT MakeInstallStepFactory
+ : public ProjectExplorer::BuildStepFactory
+{
+public:
+ MakeInstallStepFactory();
+};
+
} // namespace RemoteLinux
diff --git a/src/plugins/remotelinux/publickeydeploymentdialog.cpp b/src/plugins/remotelinux/publickeydeploymentdialog.cpp
index fcff8efd44..c7b4a54810 100644
--- a/src/plugins/remotelinux/publickeydeploymentdialog.cpp
+++ b/src/plugins/remotelinux/publickeydeploymentdialog.cpp
@@ -10,7 +10,7 @@
#include <projectexplorer/devicesupport/sshsettings.h>
#include <utils/filepath.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/stringutils.h>
#include <utils/theme/theme.h>
@@ -23,7 +23,7 @@ namespace Internal {
class PublicKeyDeploymentDialogPrivate
{
public:
- QtcProcess m_process;
+ Process m_process;
bool m_done;
};
} // namespace Internal;
@@ -56,7 +56,7 @@ PublicKeyDeploymentDialog::PublicKeyDeploymentDialog(const IDevice::ConstPtr &de
setValue(0);
connect(this, &PublicKeyDeploymentDialog::canceled, this,
[this] { d->m_done ? accept() : reject(); });
- connect(&d->m_process, &QtcProcess::done, this, [this] {
+ connect(&d->m_process, &Process::done, this, [this] {
const bool succeeded = d->m_process.result() == ProcessResult::FinishedWithSuccess;
QString finalMessage;
if (!succeeded) {
diff --git a/src/plugins/remotelinux/remotelinux.qbs b/src/plugins/remotelinux/remotelinux.qbs
index 7881cab47d..03a6afad10 100644
--- a/src/plugins/remotelinux/remotelinux.qbs
+++ b/src/plugins/remotelinux/remotelinux.qbs
@@ -19,8 +19,6 @@ Project {
"deploymenttimeinfo.h",
"customcommanddeploystep.cpp",
"customcommanddeploystep.h",
- "genericdirectuploadservice.cpp",
- "genericdirectuploadservice.h",
"genericdirectuploadstep.cpp",
"genericdirectuploadstep.h",
"genericlinuxdeviceconfigurationwidget.cpp",
@@ -62,7 +60,6 @@ Project {
"rsyncdeploystep.h",
"sshkeycreationdialog.cpp",
"sshkeycreationdialog.h",
- "sshprocessinterface.h",
"tarpackagecreationstep.cpp",
"tarpackagecreationstep.h",
"tarpackagedeploystep.cpp",
@@ -70,9 +67,7 @@ Project {
"images/embeddedtarget.png",
]
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
"filesystemaccess_test.cpp",
"filesystemaccess_test.h",
diff --git a/src/plugins/remotelinux/remotelinux_constants.h b/src/plugins/remotelinux/remotelinux_constants.h
index 31a9758828..9c428650c7 100644
--- a/src/plugins/remotelinux/remotelinux_constants.h
+++ b/src/plugins/remotelinux/remotelinux_constants.h
@@ -19,7 +19,10 @@ const char RsyncDeployStepId[] = "RemoteLinux.RsyncDeployStep";
const char CustomCommandDeployStepId[] = "RemoteLinux.GenericRemoteLinuxCustomCommandDeploymentStep";
const char KillAppStepId[] = "RemoteLinux.KillAppStep";
-const char SupportsRSync[] = "RemoteLinux.SupportsRSync";
+const char SupportsRSync[] = "RemoteLinux.SupportsRSync";
+const char SupportsSftp[] = "RemoteLinux.SupportsSftp";
+const char SourceProfile[] = "RemoteLinux.SourceProfile";
+const char LinkDevice[] = "RemoteLinux.LinkDevice";
const char RunConfigId[] = "RemoteLinuxRunConfiguration:";
const char CustomRunConfigId[] = "RemoteLinux.CustomRunConfig";
diff --git a/src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp b/src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp
index 36e4b0bda2..fd6f75c076 100644
--- a/src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp
+++ b/src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp
@@ -41,10 +41,9 @@ RemoteLinuxCustomRunConfiguration::RemoteLinuxCustomRunConfiguration(Target *tar
exeAspect->setHistoryCompleter("RemoteLinux.CustomExecutable.History");
exeAspect->setExpectedKind(PathChooser::Any);
- auto symbolsAspect = addAspect<SymbolFileAspect>();
+ auto symbolsAspect = addAspect<FilePathAspect>();
symbolsAspect->setSettingsKey("RemoteLinux.CustomRunConfig.LocalExecutable");
symbolsAspect->setLabelText(Tr::tr("Local executable:"));
- symbolsAspect->setDisplayStyle(SymbolFileAspect::PathChooserDisplay);
addAspect<ArgumentsAspect>(macroExpander());
addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect);
diff --git a/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp b/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp
index 8cacd63ff9..94bfbfb981 100644
--- a/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp
+++ b/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp
@@ -3,19 +3,38 @@
#include "remotelinuxdeployconfiguration.h"
-#include "makeinstallstep.h"
#include "remotelinux_constants.h"
#include "remotelinuxtr.h"
+#include <projectexplorer/devicesupport/filetransferinterface.h>
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
+#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/target.h>
using namespace ProjectExplorer;
namespace RemoteLinux::Internal {
+FileTransferMethod defaultTransferMethod(Kit *kit)
+{
+ auto runDevice = DeviceKitAspect::device(kit);
+ auto buildDevice = BuildDeviceKitAspect::device(kit);
+
+ if (runDevice != buildDevice) {
+ // FIXME: That's not the full truth, we need support from the build
+ // device, too.
+ if (runDevice && runDevice->extraData(Constants::SupportsRSync).toBool())
+ return FileTransferMethod::Rsync;
+ }
+
+ if (runDevice && runDevice->extraData(Constants::SupportsSftp).toBool())
+ return FileTransferMethod::Sftp;
+
+ return FileTransferMethod::GenericCopy;
+}
+
RemoteLinuxDeployConfigurationFactory::RemoteLinuxDeployConfigurationFactory()
{
setConfigBaseId(RemoteLinux::Constants::DeployToGenericLinux);
@@ -32,31 +51,23 @@ RemoteLinuxDeployConfigurationFactory::RemoteLinuxDeployConfigurationFactory()
setPostRestore([needsMakeInstall](DeployConfiguration *dc, const QVariantMap &map) {
// 4.9 -> 4.10. See QTCREATORBUG-22689.
if (map.value("_checkMakeInstall").toBool() && needsMakeInstall(dc->target())) {
- auto step = new MakeInstallStep(dc->stepList(), MakeInstallStep::stepId());
- dc->stepList()->insertStep(0, step);
+ dc->stepList()->insertStep(0, Constants::MakeInstallStepId);
}
});
addInitialStep(Constants::MakeInstallStepId, needsMakeInstall);
addInitialStep(Constants::KillAppStepId);
- // Todo: Check: Instead of having two different steps here, have one
+ // Todo: Check: Instead of having three different steps here, have one
// and shift the logic into the implementation there?
addInitialStep(Constants::RsyncDeployStepId, [](Target *target) {
- auto runDevice = DeviceKitAspect::device(target->kit());
- auto buildDevice = BuildDeviceKitAspect::device(target->kit());
- if (runDevice == buildDevice)
- return false;
- // FIXME: That's not the full truth, we need support from the build
- // device, too.
- return runDevice && runDevice->extraData(Constants::SupportsRSync).toBool();
+ return defaultTransferMethod(target->kit()) == FileTransferMethod::Rsync;
});
addInitialStep(Constants::DirectUploadStepId, [](Target *target) {
- auto runDevice = DeviceKitAspect::device(target->kit());
- auto buildDevice = BuildDeviceKitAspect::device(target->kit());
- if (runDevice == buildDevice)
- return true;
- return runDevice && !runDevice->extraData(Constants::SupportsRSync).toBool();
+ return defaultTransferMethod(target->kit()) == FileTransferMethod::Sftp;
+ });
+ addInitialStep(ProjectExplorer::Constants::COPY_FILE_STEP, [](Target *target) {
+ return defaultTransferMethod(target->kit()) == FileTransferMethod::GenericCopy;
});
}
diff --git a/src/plugins/remotelinux/remotelinuxenvironmentaspect.cpp b/src/plugins/remotelinux/remotelinuxenvironmentaspect.cpp
index cc13e51273..1be0624490 100644
--- a/src/plugins/remotelinux/remotelinuxenvironmentaspect.cpp
+++ b/src/plugins/remotelinux/remotelinuxenvironmentaspect.cpp
@@ -40,7 +40,7 @@ public:
connect(target, &Target::kitChanged, [aspect] { aspect->setRemoteEnvironment({}); });
- connect(fetchButton, &QPushButton::clicked, this, [this, aspect, target] {
+ connect(fetchButton, &QPushButton::clicked, this, [aspect, target] {
if (IDevice::ConstPtr device = DeviceKitAspect::device(target->kit())) {
DeviceFileAccess *access = device->fileAccess();
QTC_ASSERT(access, return);
diff --git a/src/plugins/remotelinux/remotelinuxplugin.cpp b/src/plugins/remotelinux/remotelinuxplugin.cpp
index 73a30b27e3..b3af1fb873 100644
--- a/src/plugins/remotelinux/remotelinuxplugin.cpp
+++ b/src/plugins/remotelinux/remotelinuxplugin.cpp
@@ -34,16 +34,14 @@ using namespace Utils;
namespace RemoteLinux {
namespace Internal {
-template <class Step>
-class GenericDeployStepFactory : public ProjectExplorer::BuildStepFactory
+template <class Factory>
+class RemoteLinuxDeployStepFactory : public Factory
{
public:
- GenericDeployStepFactory()
+ RemoteLinuxDeployStepFactory()
{
- registerStep<Step>(Step::stepId());
- setDisplayName(Step::displayName());
- setSupportedConfiguration(RemoteLinux::Constants::DeployToGenericLinux);
- setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY);
+ Factory::setSupportedConfiguration(RemoteLinux::Constants::DeployToGenericLinux);
+ Factory::setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY);
}
};
@@ -56,11 +54,11 @@ public:
RemoteLinuxDeployConfigurationFactory deployConfigurationFactory;
TarPackageCreationStepFactory tarPackageCreationStepFactory;
TarPackageDeployStepFactory tarPackageDeployStepFactory;
- GenericDeployStepFactory<GenericDirectUploadStep> genericDirectUploadStepFactory;
- GenericDeployStepFactory<RsyncDeployStep> rsyncDeployStepFactory;
+ RemoteLinuxDeployStepFactory<GenericDirectUploadStepFactory> genericDirectUploadStepFactory;
+ RemoteLinuxDeployStepFactory<RsyncDeployStepFactory> rsyncDeployStepFactory;
CustomCommandDeployStepFactory customCommandDeployStepFactory;
KillAppStepFactory killAppStepFactory;
- GenericDeployStepFactory<MakeInstallStep> makeInstallStepFactory;
+ RemoteLinuxDeployStepFactory<MakeInstallStepFactory> makeInstallStepFactory;
RemoteLinuxRunWorkerFactory runWorkerFactory;
RemoteLinuxDebugWorkerFactory debugWorkerFactory;
RemoteLinuxQmlToolingWorkerFactory qmlToolingWorkerFactory;
diff --git a/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp b/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp
index 3e28f24af4..42e80aae41 100644
--- a/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp
+++ b/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp
@@ -10,6 +10,7 @@
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/buildtargetinfo.h>
#include <projectexplorer/deploymentdata.h>
+#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
#include <projectexplorer/runconfigurationaspects.h>
@@ -58,18 +59,17 @@ RemoteLinuxRunConfiguration::RemoteLinuxRunConfiguration(Target *target, Id id)
envAspect, &EnvironmentAspect::environmentChanged);
setUpdater([this, target, exeAspect, symbolsAspect, libAspect] {
- BuildTargetInfo bti = buildTargetInfo();
+ const IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(target->kit());
+ const IDeviceConstPtr runDevice = DeviceKitAspect::device(target->kit());
+ QTC_ASSERT(buildDevice, return);
+ QTC_ASSERT(runDevice, return);
+ const BuildTargetInfo bti = buildTargetInfo();
const FilePath localExecutable = bti.targetFilePath;
- DeployableFile depFile = target->deploymentData().deployableForLocalFile(localExecutable);
+ const DeploymentData deploymentData = target->deploymentData();
+ const DeployableFile depFile = deploymentData.deployableForLocalFile(localExecutable);
- if (depFile.localFilePath().needsDevice()) // a full remote build
- exeAspect->setExecutable(depFile.localFilePath());
- else
- exeAspect->setExecutable(FilePath::fromString(depFile.remoteFilePath()));
+ exeAspect->setExecutable(runDevice->filePath(depFile.remoteFilePath()));
symbolsAspect->setFilePath(localExecutable);
-
- const IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(target->kit());
- const IDeviceConstPtr runDevice = DeviceKitAspect::device(target->kit());
libAspect->setEnabled(buildDevice == runDevice);
});
diff --git a/src/plugins/remotelinux/remotelinuxsignaloperation.cpp b/src/plugins/remotelinux/remotelinuxsignaloperation.cpp
index e073acde05..816e0e3869 100644
--- a/src/plugins/remotelinux/remotelinuxsignaloperation.cpp
+++ b/src/plugins/remotelinux/remotelinuxsignaloperation.cpp
@@ -7,8 +7,8 @@
#include <utils/commandline.h>
#include <utils/fileutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
using namespace ProjectExplorer;
using namespace Utils;
@@ -30,8 +30,8 @@ static QString signalProcessGroupByPidCommandLine(qint64 pid, int signal)
void RemoteLinuxSignalOperation::run(const QString &command)
{
QTC_ASSERT(!m_process, return);
- m_process.reset(new QtcProcess);
- connect(m_process.get(), &QtcProcess::done, this, &RemoteLinuxSignalOperation::runnerDone);
+ m_process.reset(new Process);
+ connect(m_process.get(), &Process::done, this, &RemoteLinuxSignalOperation::runnerDone);
m_process->setCommand({m_device->filePath("/bin/sh"), {"-c", command}});
m_process->start();
diff --git a/src/plugins/remotelinux/remotelinuxsignaloperation.h b/src/plugins/remotelinux/remotelinuxsignaloperation.h
index d3e840e0d3..00967c9414 100644
--- a/src/plugins/remotelinux/remotelinuxsignaloperation.h
+++ b/src/plugins/remotelinux/remotelinuxsignaloperation.h
@@ -32,7 +32,7 @@ private:
void run(const QString &command);
const ProjectExplorer::IDeviceConstPtr m_device;
- std::unique_ptr<Utils::QtcProcess> m_process;
+ std::unique_ptr<Utils::Process> m_process;
friend class LinuxDevice;
};
diff --git a/src/plugins/remotelinux/rsyncdeploystep.cpp b/src/plugins/remotelinux/rsyncdeploystep.cpp
index b20eaf9049..84eb47b8cc 100644
--- a/src/plugins/remotelinux/rsyncdeploystep.cpp
+++ b/src/plugins/remotelinux/rsyncdeploystep.cpp
@@ -16,21 +16,21 @@
#include <projectexplorer/target.h>
#include <utils/algorithm.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
-#include <utils/qtcprocess.h>
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
-using namespace Utils::Tasking;
namespace RemoteLinux {
-class RsyncDeployService : public AbstractRemoteLinuxDeployService
+// RsyncDeployStep
+
+class RsyncDeployStep : public AbstractRemoteLinuxDeployStep
{
public:
- void setDeployableFiles(const QList<DeployableFile> &files);
- void setIgnoreMissingFiles(bool ignore) { m_ignoreMissingFiles = ignore; }
- void setFlags(const QString &flags) { m_flags = flags; }
+ RsyncDeployStep(BuildStepList *bsl, Id id);
private:
bool isDeploymentNecessary() const final;
@@ -43,23 +43,51 @@ private:
QString m_flags;
};
-void RsyncDeployService::setDeployableFiles(const QList<DeployableFile> &files)
+RsyncDeployStep::RsyncDeployStep(BuildStepList *bsl, Id id)
+ : AbstractRemoteLinuxDeployStep(bsl, id)
{
- m_files.clear();
- for (const DeployableFile &f : files)
- m_files.append({f.localFilePath(), deviceConfiguration()->filePath(f.remoteFilePath())});
+ auto flags = addAspect<StringAspect>();
+ flags->setDisplayStyle(StringAspect::LineEditDisplay);
+ flags->setSettingsKey("RemoteLinux.RsyncDeployStep.Flags");
+ flags->setLabelText(Tr::tr("Flags:"));
+ flags->setValue(FileTransferSetupData::defaultRsyncFlags());
+
+ auto ignoreMissingFiles = addAspect<BoolAspect>();
+ ignoreMissingFiles->setSettingsKey("RemoteLinux.RsyncDeployStep.IgnoreMissingFiles");
+ ignoreMissingFiles->setLabel(Tr::tr("Ignore missing files:"),
+ BoolAspect::LabelPlacement::InExtraLabel);
+ ignoreMissingFiles->setValue(false);
+
+ setInternalInitializer([this, ignoreMissingFiles, flags] {
+ if (BuildDeviceKitAspect::device(kit()) == DeviceKitAspect::device(kit())) {
+ // rsync transfer on the same device currently not implemented
+ // and typically not wanted.
+ return CheckResult::failure(
+ Tr::tr("rsync is only supported for transfers between different devices."));
+ }
+ m_ignoreMissingFiles = ignoreMissingFiles->value();
+ m_flags = flags->value();
+ return isDeploymentPossible();
+ });
+
+ setRunPreparer([this] {
+ const QList<DeployableFile> files = target()->deploymentData().allFiles();
+ m_files.clear();
+ for (const DeployableFile &f : files)
+ m_files.append({f.localFilePath(), deviceConfiguration()->filePath(f.remoteFilePath())});
+ });
}
-bool RsyncDeployService::isDeploymentNecessary() const
+bool RsyncDeployStep::isDeploymentNecessary() const
{
if (m_ignoreMissingFiles)
Utils::erase(m_files, [](const FileToTransfer &file) { return !file.m_source.exists(); });
return !m_files.empty();
}
-TaskItem RsyncDeployService::mkdirTask()
+TaskItem RsyncDeployStep::mkdirTask()
{
- const auto setupHandler = [this](QtcProcess &process) {
+ const auto setupHandler = [this](Process &process) {
QStringList remoteDirs;
for (const FileToTransfer &file : std::as_const(m_files))
remoteDirs << file.m_target.parentDir().path();
@@ -67,11 +95,11 @@ TaskItem RsyncDeployService::mkdirTask()
remoteDirs.removeDuplicates();
process.setCommand({deviceConfiguration()->filePath("mkdir"),
QStringList("-p") + remoteDirs});
- connect(&process, &QtcProcess::readyReadStandardError, this, [this, proc = &process] {
- emit stdErrData(QString::fromLocal8Bit(proc->readAllRawStandardError()));
+ connect(&process, &Process::readyReadStandardError, this, [this, proc = &process] {
+ handleStdErrData(QString::fromLocal8Bit(proc->readAllRawStandardError()));
});
};
- const auto errorHandler = [this](const QtcProcess &process) {
+ const auto errorHandler = [this](const Process &process) {
QString finalMessage = process.errorString();
const QString stdErr = process.cleanedStdErr();
if (!stdErr.isEmpty()) {
@@ -79,87 +107,46 @@ TaskItem RsyncDeployService::mkdirTask()
finalMessage += '\n';
finalMessage += stdErr;
}
- emit errorMessage(Tr::tr("Deploy via rsync: failed to create remote directories:")
- + '\n' + finalMessage);
+ addErrorMessage(Tr::tr("Deploy via rsync: failed to create remote directories:")
+ + '\n' + finalMessage);
};
- return Process(setupHandler, {}, errorHandler);
+ return ProcessTask(setupHandler, {}, errorHandler);
}
-TaskItem RsyncDeployService::transferTask()
+TaskItem RsyncDeployStep::transferTask()
{
const auto setupHandler = [this](FileTransfer &transfer) {
transfer.setTransferMethod(FileTransferMethod::Rsync);
transfer.setRsyncFlags(m_flags);
transfer.setFilesToTransfer(m_files);
connect(&transfer, &FileTransfer::progress,
- this, &AbstractRemoteLinuxDeployService::stdOutData);
+ this, &AbstractRemoteLinuxDeployStep::handleStdOutData);
};
const auto errorHandler = [this](const FileTransfer &transfer) {
const ProcessResultData result = transfer.resultData();
if (result.m_error == QProcess::FailedToStart) {
- emit errorMessage(Tr::tr("rsync failed to start: %1").arg(result.m_errorString));
+ addErrorMessage(Tr::tr("rsync failed to start: %1").arg(result.m_errorString));
} else if (result.m_exitStatus == QProcess::CrashExit) {
- emit errorMessage(Tr::tr("rsync crashed."));
+ addErrorMessage(Tr::tr("rsync crashed."));
} else if (result.m_exitCode != 0) {
- emit errorMessage(Tr::tr("rsync failed with exit code %1.").arg(result.m_exitCode)
- + "\n" + result.m_errorString);
+ addErrorMessage(Tr::tr("rsync failed with exit code %1.").arg(result.m_exitCode)
+ + "\n" + result.m_errorString);
}
};
- return Transfer(setupHandler, {}, errorHandler);
+ return FileTransferTask(setupHandler, {}, errorHandler);
}
-Group RsyncDeployService::deployRecipe()
+Group RsyncDeployStep::deployRecipe()
{
return Group { mkdirTask(), transferTask() };
}
-// RsyncDeployStep
-
-RsyncDeployStep::RsyncDeployStep(BuildStepList *bsl, Id id)
- : AbstractRemoteLinuxDeployStep(bsl, id)
-{
- auto service = new RsyncDeployService;
- setDeployService(service);
-
- auto flags = addAspect<StringAspect>();
- flags->setDisplayStyle(StringAspect::LineEditDisplay);
- flags->setSettingsKey("RemoteLinux.RsyncDeployStep.Flags");
- flags->setLabelText(Tr::tr("Flags:"));
- flags->setValue(FileTransferSetupData::defaultRsyncFlags());
-
- auto ignoreMissingFiles = addAspect<BoolAspect>();
- ignoreMissingFiles->setSettingsKey("RemoteLinux.RsyncDeployStep.IgnoreMissingFiles");
- ignoreMissingFiles->setLabel(Tr::tr("Ignore missing files:"),
- BoolAspect::LabelPlacement::InExtraLabel);
- ignoreMissingFiles->setValue(false);
-
- setInternalInitializer([this, service, flags, ignoreMissingFiles] {
- if (BuildDeviceKitAspect::device(kit()) == DeviceKitAspect::device(kit())) {
- // rsync transfer on the same device currently not implemented
- // and typically not wanted.
- return CheckResult::failure(
- Tr::tr("rsync is only supported for transfers between different devices."));
- }
- service->setIgnoreMissingFiles(ignoreMissingFiles->value());
- service->setFlags(flags->value());
- return service->isDeploymentPossible();
- });
-
- setRunPreparer([this, service] {
- service->setDeployableFiles(target()->deploymentData().allFiles());
- });
-}
-
-RsyncDeployStep::~RsyncDeployStep() = default;
-
-Utils::Id RsyncDeployStep::stepId()
-{
- return Constants::RsyncDeployStepId;
-}
+// Factory
-QString RsyncDeployStep::displayName()
+RsyncDeployStepFactory::RsyncDeployStepFactory()
{
- return Tr::tr("Deploy files via rsync");
+ registerStep<RsyncDeployStep>(Constants::RsyncDeployStepId);
+ setDisplayName(Tr::tr("Deploy files via rsync"));
}
} // RemoteLinux
diff --git a/src/plugins/remotelinux/rsyncdeploystep.h b/src/plugins/remotelinux/rsyncdeploystep.h
index a0f8790531..7450d84fcb 100644
--- a/src/plugins/remotelinux/rsyncdeploystep.h
+++ b/src/plugins/remotelinux/rsyncdeploystep.h
@@ -5,18 +5,15 @@
#include "remotelinux_export.h"
-#include "abstractremotelinuxdeploystep.h"
+#include <projectexplorer/buildstep.h>
namespace RemoteLinux {
-class REMOTELINUX_EXPORT RsyncDeployStep : public AbstractRemoteLinuxDeployStep
+class REMOTELINUX_EXPORT RsyncDeployStepFactory
+ : public ProjectExplorer::BuildStepFactory
{
public:
- RsyncDeployStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id);
- ~RsyncDeployStep() override;
-
- static Utils::Id stepId();
- static QString displayName();
+ RsyncDeployStepFactory();
};
} // namespace RemoteLinux
diff --git a/src/plugins/remotelinux/sshkeycreationdialog.cpp b/src/plugins/remotelinux/sshkeycreationdialog.cpp
index e0128d2a2a..441edab889 100644
--- a/src/plugins/remotelinux/sshkeycreationdialog.cpp
+++ b/src/plugins/remotelinux/sshkeycreationdialog.cpp
@@ -10,7 +10,7 @@
#include <utils/fileutils.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QApplication>
#include <QComboBox>
@@ -109,7 +109,7 @@ void SshKeyCreationDialog::generateKeys()
}
const QString keyTypeString = QLatin1String(m_rsa->isChecked() ? "rsa": "ecdsa");
QApplication::setOverrideCursor(Qt::BusyCursor);
- QtcProcess keygen;
+ Process keygen;
const QStringList args{"-t", keyTypeString, "-b", m_comboBox->currentText(),
"-N", QString(), "-f", privateKeyFilePath().path()};
QString errorMsg;
diff --git a/src/plugins/remotelinux/sshprocessinterface.h b/src/plugins/remotelinux/sshprocessinterface.h
deleted file mode 100644
index 9d67d6a4e6..0000000000
--- a/src/plugins/remotelinux/sshprocessinterface.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "remotelinux_export.h"
-
-#include <utils/processinterface.h>
-
-namespace RemoteLinux {
-
-class LinuxDevice;
-class SshProcessInterfacePrivate;
-
-class REMOTELINUX_EXPORT SshProcessInterface : public Utils::ProcessInterface
-{
-public:
- SshProcessInterface(const LinuxDevice *linuxDevice);
- ~SshProcessInterface();
-
-protected:
- void emitStarted(qint64 processId);
- // To be called from leaf destructor.
- // Can't call it from SshProcessInterface destructor as it calls virtual method.
- void killIfRunning();
- qint64 processId() const;
- bool runInShell(const Utils::CommandLine &command, const QByteArray &data = {});
-
-private:
- virtual void handleStarted(qint64 processId);
- virtual void handleDone(const Utils::ProcessResultData &resultData);
- virtual void handleReadyReadStandardOutput(const QByteArray &outputData);
- virtual void handleReadyReadStandardError(const QByteArray &errorData);
- virtual void handleSendControlSignal(Utils::ControlSignal controlSignal) = 0;
-
- virtual QString fullCommandLine(const Utils::CommandLine &commandLine) const = 0;
-
- void start() final;
- qint64 write(const QByteArray &data) final;
- void sendControlSignal(Utils::ControlSignal controlSignal) final;
-
- friend class SshProcessInterfacePrivate;
- SshProcessInterfacePrivate *d = nullptr;
-};
-
-} // namespace RemoteLinux
diff --git a/src/plugins/remotelinux/tarpackagecreationstep.cpp b/src/plugins/remotelinux/tarpackagecreationstep.cpp
index 0d53d36ff5..7c577812da 100644
--- a/src/plugins/remotelinux/tarpackagecreationstep.cpp
+++ b/src/plugins/remotelinux/tarpackagecreationstep.cpp
@@ -13,8 +13,8 @@
#include <projectexplorer/project.h>
#include <projectexplorer/target.h>
+#include <utils/async.h>
#include <utils/futuresynchronizer.h>
-#include <utils/runextensions.h>
#include <QDateTime>
#include <QDir>
@@ -74,9 +74,9 @@ private:
bool isPackagingNeeded() const;
void deployFinished(bool success);
void addNeededDeploymentFiles(const DeployableFile &deployable, const Kit *kit);
- void doPackage(QFutureInterface<bool> &fi, const Utils::FilePath &tarFilePath,
+ void doPackage(QPromise<bool> &promise, const Utils::FilePath &tarFilePath,
bool ignoreMissingFiles);
- bool appendFile(QFutureInterface<bool> &fi, QFile &tarFile, const QFileInfo &fileInfo,
+ bool appendFile(QPromise<bool> &promise, QFile &tarFile, const QFileInfo &fileInfo,
const QString &remoteFilePath, const Utils::FilePath &tarFilePath,
bool ignoreMissingFiles);
@@ -94,7 +94,6 @@ private:
TarPackageCreationStep::TarPackageCreationStep(BuildStepList *bsl, Id id)
: BuildStep(bsl, id)
{
- m_synchronizer.setCancelOnWait(true);
connect(target(), &Target::deploymentDataChanged, this, [this] {
m_deploymentDataModified = true;
});
@@ -167,7 +166,7 @@ void TarPackageCreationStep::doRun()
connect(BuildManager::instance(), &BuildManager::buildQueueFinished,
this, &TarPackageCreationStep::deployFinished);
});
- auto future = Utils::runAsync(&TarPackageCreationStep::doPackage, this,
+ auto future = Utils::asyncRun(&TarPackageCreationStep::doPackage, this,
m_tarFilePath, m_ignoreMissingFilesAspect->value());
watcher->setFuture(future);
m_synchronizer.addFuture(future);
@@ -271,7 +270,7 @@ void TarPackageCreationStep::addNeededDeploymentFiles(
}
}
-void TarPackageCreationStep::doPackage(QFutureInterface<bool> &fi, const FilePath &tarFilePath,
+void TarPackageCreationStep::doPackage(QPromise<bool> &promise, const FilePath &tarFilePath,
bool ignoreMissingFiles)
{
// TODO: Optimization: Only package changed files
@@ -280,7 +279,7 @@ void TarPackageCreationStep::doPackage(QFutureInterface<bool> &fi, const FilePat
if (!tarFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
raiseError(Tr::tr("Error: tar file %1 cannot be opened (%2).")
.arg(tarFilePath.toUserOutput(), tarFile.errorString()));
- fi.reportResult(false);
+ promise.addResult(false);
return;
}
@@ -291,10 +290,10 @@ void TarPackageCreationStep::doPackage(QFutureInterface<bool> &fi, const FilePat
continue;
}
QFileInfo fileInfo = d.localFilePath().toFileInfo();
- if (!appendFile(fi, tarFile, fileInfo,
+ if (!appendFile(promise, tarFile, fileInfo,
d.remoteDirectory() + QLatin1Char('/') + fileInfo.fileName(),
tarFilePath, ignoreMissingFiles)) {
- fi.reportResult(false);
+ promise.addResult(false);
return;
}
}
@@ -303,10 +302,10 @@ void TarPackageCreationStep::doPackage(QFutureInterface<bool> &fi, const FilePat
if (tarFile.write(eofIndicator) != eofIndicator.length()) {
raiseError(Tr::tr("Error writing tar file \"%1\": %2.")
.arg(QDir::toNativeSeparators(tarFile.fileName()), tarFile.errorString()));
- fi.reportResult(false);
+ promise.addResult(false);
return;
}
- fi.reportResult(true);
+ promise.addResult(true);
}
static bool setFilePath(TarFileHeader &header, const QByteArray &filePath)
@@ -388,7 +387,7 @@ static bool writeHeader(QFile &tarFile, const QFileInfo &fileInfo, const QString
return true;
}
-bool TarPackageCreationStep::appendFile(QFutureInterface<bool> &fi,
+bool TarPackageCreationStep::appendFile(QPromise<bool> &promise,
QFile &tarFile,
const QFileInfo &fileInfo,
const QString &remoteFilePath,
@@ -406,7 +405,7 @@ bool TarPackageCreationStep::appendFile(QFutureInterface<bool> &fi,
for (const QString &fileName : files) {
const QString thisLocalFilePath = dir.path() + QLatin1Char('/') + fileName;
const QString thisRemoteFilePath = remoteFilePath + QLatin1Char('/') + fileName;
- if (!appendFile(fi, tarFile, QFileInfo(thisLocalFilePath), thisRemoteFilePath,
+ if (!appendFile(promise, tarFile, QFileInfo(thisLocalFilePath), thisRemoteFilePath,
tarFilePath, ignoreMissingFiles)) {
return false;
}
@@ -437,7 +436,7 @@ bool TarPackageCreationStep::appendFile(QFutureInterface<bool> &fi,
while (!file.atEnd() && file.error() == QFile::NoError && tarFile.error() == QFile::NoError) {
const QByteArray data = file.read(chunkSize);
tarFile.write(data);
- if (fi.isCanceled())
+ if (promise.isCanceled())
return false;
}
if (file.error() != QFile::NoError) {
diff --git a/src/plugins/remotelinux/tarpackagedeploystep.cpp b/src/plugins/remotelinux/tarpackagedeploystep.cpp
index ee276666ac..6b50ca4dce 100644
--- a/src/plugins/remotelinux/tarpackagedeploystep.cpp
+++ b/src/plugins/remotelinux/tarpackagedeploystep.cpp
@@ -12,19 +12,44 @@
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
-#include <utils/qtcprocess.h>
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
-using namespace Utils::Tasking;
namespace RemoteLinux::Internal {
-class TarPackageDeployService : public AbstractRemoteLinuxDeployService
+// TarPackageDeployStep
+
+class TarPackageDeployStep : public AbstractRemoteLinuxDeployStep
{
public:
- void setPackageFilePath(const FilePath &filePath);
+ TarPackageDeployStep(BuildStepList *bsl, Id id)
+ : AbstractRemoteLinuxDeployStep(bsl, id)
+ {
+ setWidgetExpandedByDefault(false);
+
+ setInternalInitializer([this] {
+ const BuildStep *tarCreationStep = nullptr;
+
+ for (BuildStep *step : deployConfiguration()->stepList()->steps()) {
+ if (step == this)
+ break;
+ if (step->id() == Constants::TarPackageCreationStepId) {
+ tarCreationStep = step;
+ break;
+ }
+ }
+ if (!tarCreationStep)
+ return CheckResult::failure(Tr::tr("No tarball creation step found."));
+
+ m_packageFilePath =
+ FilePath::fromVariant(tarCreationStep->data(Constants::TarPackageFilePathId));
+ return isDeploymentPossible();
+ });
+ }
private:
QString remoteFilePath() const;
@@ -36,106 +61,65 @@ private:
FilePath m_packageFilePath;
};
-void TarPackageDeployService::setPackageFilePath(const FilePath &filePath)
-{
- m_packageFilePath = filePath;
-}
-
-QString TarPackageDeployService::remoteFilePath() const
+QString TarPackageDeployStep::remoteFilePath() const
{
return QLatin1String("/tmp/") + m_packageFilePath.fileName();
}
-bool TarPackageDeployService::isDeploymentNecessary() const
+bool TarPackageDeployStep::isDeploymentNecessary() const
{
return hasLocalFileChanged(DeployableFile(m_packageFilePath, {}));
}
-TaskItem TarPackageDeployService::uploadTask()
+TaskItem TarPackageDeployStep::uploadTask()
{
const auto setupHandler = [this](FileTransfer &transfer) {
const FilesToTransfer files {{m_packageFilePath,
deviceConfiguration()->filePath(remoteFilePath())}};
transfer.setFilesToTransfer(files);
- connect(&transfer, &FileTransfer::progress,
- this, &TarPackageDeployService::progressMessage);
- emit progressMessage(Tr::tr("Uploading package to device..."));
+ connect(&transfer, &FileTransfer::progress, this, &TarPackageDeployStep::addProgressMessage);
+ addProgressMessage(Tr::tr("Uploading package to device..."));
};
const auto doneHandler = [this](const FileTransfer &) {
- emit progressMessage(Tr::tr("Successfully uploaded package file."));
+ addProgressMessage(Tr::tr("Successfully uploaded package file."));
};
const auto errorHandler = [this](const FileTransfer &transfer) {
const ProcessResultData result = transfer.resultData();
- emit errorMessage(result.m_errorString);
+ addErrorMessage(result.m_errorString);
};
- return Transfer(setupHandler, doneHandler, errorHandler);
+ return FileTransferTask(setupHandler, doneHandler, errorHandler);
}
-TaskItem TarPackageDeployService::installTask()
+TaskItem TarPackageDeployStep::installTask()
{
- const auto setupHandler = [this](QtcProcess &process) {
+ const auto setupHandler = [this](Process &process) {
const QString cmdLine = QLatin1String("cd / && tar xvf ") + remoteFilePath()
+ " && (rm " + remoteFilePath() + " || :)";
process.setCommand({deviceConfiguration()->filePath("/bin/sh"), {"-c", cmdLine}});
- QtcProcess *proc = &process;
- connect(proc, &QtcProcess::readyReadStandardOutput, this, [this, proc] {
- emit stdOutData(proc->readAllStandardOutput());
+ Process *proc = &process;
+ connect(proc, &Process::readyReadStandardOutput, this, [this, proc] {
+ handleStdOutData(proc->readAllStandardOutput());
});
- connect(proc, &QtcProcess::readyReadStandardError, this, [this, proc] {
- emit stdErrData(proc->readAllStandardError());
+ connect(proc, &Process::readyReadStandardError, this, [this, proc] {
+ handleStdErrData(proc->readAllStandardError());
});
- emit progressMessage(Tr::tr("Installing package to device..."));
+ addProgressMessage(Tr::tr("Installing package to device..."));
};
- const auto doneHandler = [this](const QtcProcess &) {
+ const auto doneHandler = [this](const Process &) {
saveDeploymentTimeStamp(DeployableFile(m_packageFilePath, {}), {});
- emit progressMessage(Tr::tr("Successfully installed package file."));
+ addProgressMessage(Tr::tr("Successfully installed package file."));
};
- const auto errorHandler = [this](const QtcProcess &process) {
- emit errorMessage(Tr::tr("Installing package failed.") + process.errorString());
+ const auto errorHandler = [this](const Process &process) {
+ addErrorMessage(Tr::tr("Installing package failed.") + process.errorString());
};
- return Process(setupHandler, doneHandler, errorHandler);
+ return ProcessTask(setupHandler, doneHandler, errorHandler);
}
-Group TarPackageDeployService::deployRecipe()
+Group TarPackageDeployStep::deployRecipe()
{
return Group { uploadTask(), installTask() };
}
-// TarPackageDeployStep
-
-class TarPackageDeployStep : public AbstractRemoteLinuxDeployStep
-{
-public:
- TarPackageDeployStep(BuildStepList *bsl, Id id)
- : AbstractRemoteLinuxDeployStep(bsl, id)
- {
- auto service = new TarPackageDeployService;
- setDeployService(service);
-
- setWidgetExpandedByDefault(false);
-
- setInternalInitializer([this, service] {
- const BuildStep *tarCreationStep = nullptr;
-
- for (BuildStep *step : deployConfiguration()->stepList()->steps()) {
- if (step == this)
- break;
- if (step->id() == Constants::TarPackageCreationStepId) {
- tarCreationStep = step;
- break;
- }
- }
- if (!tarCreationStep)
- return CheckResult::failure(Tr::tr("No tarball creation step found."));
-
- const FilePath tarFile =
- FilePath::fromVariant(tarCreationStep->data(Constants::TarPackageFilePathId));
- service->setPackageFilePath(tarFile);
- return service->isDeploymentPossible();
- });
- }
-};
-
// TarPackageDeployStepFactory
diff --git a/src/plugins/resourceeditor/qrceditor/qrceditor.cpp b/src/plugins/resourceeditor/qrceditor/qrceditor.cpp
index 86679c52b7..e08f52340f 100644
--- a/src/plugins/resourceeditor/qrceditor/qrceditor.cpp
+++ b/src/plugins/resourceeditor/qrceditor/qrceditor.cpp
@@ -43,7 +43,7 @@ QrcEditor::QrcEditor(RelativeResourceModel *model, QWidget *parent)
m_languageLabel = new QLabel(Tr::tr("Language:"));
m_languageText = new QLineEdit;
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row {
addPrefixButton,
diff --git a/src/plugins/resourceeditor/resourcenode.cpp b/src/plugins/resourceeditor/resourcenode.cpp
index fa0e3b5e4a..d402e3ed89 100644
--- a/src/plugins/resourceeditor/resourcenode.cpp
+++ b/src/plugins/resourceeditor/resourcenode.cpp
@@ -268,9 +268,7 @@ static void compressTree(FolderNode *n)
compressable->compress();
return;
}
- const QList<FolderNode *> childFolders = n->folderNodes();
- for (FolderNode * const c : childFolders)
- compressTree(c);
+ n->forEachFolderNode([](FolderNode *c) { compressTree(c); });
}
void ResourceTopLevelNode::addInternalNodes()
diff --git a/src/plugins/scxmleditor/CMakeLists.txt b/src/plugins/scxmleditor/CMakeLists.txt
index fef49a9082..358c407233 100644
--- a/src/plugins/scxmleditor/CMakeLists.txt
+++ b/src/plugins/scxmleditor/CMakeLists.txt
@@ -42,6 +42,7 @@ add_qtc_plugin(ScxmlEditor
plugin_interface/baseitem.cpp plugin_interface/baseitem.h
plugin_interface/connectableitem.cpp plugin_interface/connectableitem.h
plugin_interface/cornergrabberitem.cpp plugin_interface/cornergrabberitem.h
+ plugin_interface/eventitem.cpp plugin_interface/eventitem.h
plugin_interface/finalstateitem.cpp plugin_interface/finalstateitem.h
plugin_interface/genericscxmlplugin.cpp plugin_interface/genericscxmlplugin.h
plugin_interface/graphicsitemprovider.h
diff --git a/src/plugins/scxmleditor/common/colorpicker.cpp b/src/plugins/scxmleditor/common/colorpicker.cpp
index 04e415224b..88f4d6d22a 100644
--- a/src/plugins/scxmleditor/common/colorpicker.cpp
+++ b/src/plugins/scxmleditor/common/colorpicker.cpp
@@ -34,8 +34,8 @@ ColorPicker::ColorPicker(const QString &key, QWidget *parent)
m_lastUsedColorContainer = new QHBoxLayout(lastUsedColorContainer);
m_lastUsedColorContainer->setContentsMargins(0, 0, 0, 0);
- using namespace Utils::Layouting;
- Grid colorGrid;
+ using namespace Layouting;
+ Grid colorGrid{noMargin};
for (int i = 0; i < colors.count(); ++i) {
QWidget *button = createButton(colors[i]);
colorGrid.addItem(button);
@@ -46,7 +46,7 @@ ColorPicker::ColorPicker(const QString &key, QWidget *parent)
QSizePolicy::MinimumExpanding,
QSizePolicy::Preferred));
}
- colorGrid.attachTo(basicColorContentFrame, WithoutMargins);
+ colorGrid.attachTo(basicColorContentFrame);
Column {
Tr::tr("Basic Colors"),
basicColorContentFrame,
diff --git a/src/plugins/scxmleditor/common/colorsettings.cpp b/src/plugins/scxmleditor/common/colorsettings.cpp
index c2ad9f22ce..1419b481bc 100644
--- a/src/plugins/scxmleditor/common/colorsettings.cpp
+++ b/src/plugins/scxmleditor/common/colorsettings.cpp
@@ -36,7 +36,7 @@ ColorSettings::ColorSettings(QWidget *parent)
s->value(Constants::C_SETTINGS_COLORSETTINGS_CURRENTCOLORTHEME).toString());
selectTheme(m_comboColorThemes->currentIndex());
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row {
m_comboColorThemes,
@@ -44,7 +44,8 @@ ColorSettings::ColorSettings(QWidget *parent)
removeTheme,
},
m_colorThemeView,
- }.attachTo(this, WithoutMargins);
+ noMargin
+ }.attachTo(this);
connect(m_comboColorThemes, &QComboBox::currentIndexChanged,
this, &ColorSettings::selectTheme);
diff --git a/src/plugins/scxmleditor/common/colorthemedialog.cpp b/src/plugins/scxmleditor/common/colorthemedialog.cpp
index 00f224d1f9..2f2b209b77 100644
--- a/src/plugins/scxmleditor/common/colorthemedialog.cpp
+++ b/src/plugins/scxmleditor/common/colorthemedialog.cpp
@@ -21,7 +21,7 @@ ColorThemeDialog::ColorThemeDialog(QWidget *parent)
QDialogButtonBox::Cancel |
QDialogButtonBox::Apply);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_colorSettings,
buttonBox,
diff --git a/src/plugins/scxmleditor/common/navigatorslider.cpp b/src/plugins/scxmleditor/common/navigatorslider.cpp
index 5f3fc345e1..ab3738bc06 100644
--- a/src/plugins/scxmleditor/common/navigatorslider.cpp
+++ b/src/plugins/scxmleditor/common/navigatorslider.cpp
@@ -29,13 +29,15 @@ NavigatorSlider::NavigatorSlider(QWidget *parent)
btn->setAutoRepeatInterval(10);
}
- using namespace Utils::Layouting;
+ using namespace Layouting;
Row {
+ spacing(0),
zoomOut,
m_slider,
zoomIn,
Space(20),
- }.setSpacing(0).attachTo(this, WithoutMargins);
+ noMargin,
+ }.attachTo(this);
connect(zoomOut, &QToolButton::clicked, this, &NavigatorSlider::zoomOut);
connect(zoomIn, &QToolButton::clicked, this, &NavigatorSlider::zoomIn);
diff --git a/src/plugins/scxmleditor/common/search.cpp b/src/plugins/scxmleditor/common/search.cpp
index acfc5cda13..f4d7e4bbc3 100644
--- a/src/plugins/scxmleditor/common/search.cpp
+++ b/src/plugins/scxmleditor/common/search.cpp
@@ -45,11 +45,13 @@ Search::Search(QWidget *parent)
m_searchView->setModel(m_proxyModel);
m_searchView->setFrameShape(QFrame::NoFrame);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
+ spacing(0),
m_searchEdit,
m_searchView,
- }.setSpacing(0).attachTo(this, WithoutMargins);
+ noMargin
+ }.attachTo(this);
connect(m_searchEdit, &Utils::FancyLineEdit::textChanged, this, &Search::setSearchText);
connect(m_searchView, &TableView::pressed, this, &Search::rowActivated);
diff --git a/src/plugins/scxmleditor/common/shapestoolbox.cpp b/src/plugins/scxmleditor/common/shapestoolbox.cpp
index 988c762c34..82def88379 100644
--- a/src/plugins/scxmleditor/common/shapestoolbox.cpp
+++ b/src/plugins/scxmleditor/common/shapestoolbox.cpp
@@ -29,10 +29,12 @@ ShapesToolbox::ShapesToolbox(QWidget *parent)
m_shapeGroupsLayout->setContentsMargins(0, 0, 0, 0);
m_shapeGroupsLayout->setSpacing(0);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
+ spacing(0),
scrollArea,
- }.setSpacing(0).attachTo(this, WithoutMargins);
+ noMargin,
+ }.attachTo(this);
}
void ShapesToolbox::setUIFactory(ScxmlEditor::PluginInterface::ScxmlUiFactory *factory)
diff --git a/src/plugins/scxmleditor/common/stateproperties.cpp b/src/plugins/scxmleditor/common/stateproperties.cpp
index 32d6e43f64..f7a884837e 100644
--- a/src/plugins/scxmleditor/common/stateproperties.cpp
+++ b/src/plugins/scxmleditor/common/stateproperties.cpp
@@ -106,7 +106,6 @@ void StateProperties::createUi()
m_currentTagName = new QLabel;
auto propertiesToolBar = new QToolBar;
- propertiesToolBar->setMinimumHeight(24);
propertiesToolBar->addWidget(titleLabel);
propertiesToolBar->addWidget(m_currentTagName);
diff --git a/src/plugins/scxmleditor/common/stateview.cpp b/src/plugins/scxmleditor/common/stateview.cpp
index 1ee9903441..517f53bd3e 100644
--- a/src/plugins/scxmleditor/common/stateview.cpp
+++ b/src/plugins/scxmleditor/common/stateview.cpp
@@ -31,15 +31,19 @@ StateView::StateView(StateItem *state, QWidget *parent)
m_graphicsView = new GraphicsView;
- using namespace Utils::Layouting;
+ using namespace Layouting;
Row {
PushButton{ text("Back"), onClicked([this] { closeView(); }, this) },
stateNameLabel,
- }.attachTo(titleBar, WithoutMargins);
+ noMargin
+ }.attachTo(titleBar);
Column {
- titleBar, m_graphicsView
- }.setSpacing(0).attachTo(this, WithoutMargins);
+ spacing(0),
+ titleBar,
+ m_graphicsView,
+ noMargin,
+ }.attachTo(this);
initScene();
}
diff --git a/src/plugins/scxmleditor/common/statistics.cpp b/src/plugins/scxmleditor/common/statistics.cpp
index 06c62f70b2..cff7d337c3 100644
--- a/src/plugins/scxmleditor/common/statistics.cpp
+++ b/src/plugins/scxmleditor/common/statistics.cpp
@@ -135,13 +135,14 @@ Statistics::Statistics(QWidget *parent)
m_statisticsView->setAlternatingRowColors(true);
m_statisticsView->setSortingEnabled(true);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Grid {
Tr::tr("File"), m_fileNameLabel, br,
Tr::tr("Time"), m_timeLabel, br,
Tr::tr("Max. levels"), m_levels, br,
- Span(2, m_statisticsView), br
- }.attachTo(this, WithoutMargins);
+ Span(2, m_statisticsView), br,
+ noMargin
+ }.attachTo(this);
}
void Statistics::setDocument(ScxmlDocument *doc)
diff --git a/src/plugins/scxmleditor/common/statisticsdialog.cpp b/src/plugins/scxmleditor/common/statisticsdialog.cpp
index 7cfb6cd463..d9a20dd81d 100644
--- a/src/plugins/scxmleditor/common/statisticsdialog.cpp
+++ b/src/plugins/scxmleditor/common/statisticsdialog.cpp
@@ -21,7 +21,7 @@ StatisticsDialog::StatisticsDialog(QWidget *parent)
m_statistics = new Statistics;
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_statistics,
buttonBox,
diff --git a/src/plugins/scxmleditor/plugin_interface/baseitem.cpp b/src/plugins/scxmleditor/plugin_interface/baseitem.cpp
index 861094321a..7d87a68f91 100644
--- a/src/plugins/scxmleditor/plugin_interface/baseitem.cpp
+++ b/src/plugins/scxmleditor/plugin_interface/baseitem.cpp
@@ -41,7 +41,8 @@ BaseItem::~BaseItem()
void BaseItem::checkParentBoundingRect()
{
BaseItem *parentBaseItem = this->parentBaseItem();
- if (parentBaseItem && type() >= InitialStateType && !parentBaseItem->blockUpdates()) {
+ if ((parentBaseItem && type() >= InitialStateType && !parentBaseItem->blockUpdates())
+ || (parentBaseItem && type() == StateWarningType)) {
auto parentStateItem = qgraphicsitem_cast<StateItem*>(parentBaseItem);
if (parentStateItem && (parentStateItem->type() >= StateType))
parentStateItem->updateBoundingRect();
diff --git a/src/plugins/scxmleditor/plugin_interface/baseitem.h b/src/plugins/scxmleditor/plugin_interface/baseitem.h
index 5c992c632b..42f09ca389 100644
--- a/src/plugins/scxmleditor/plugin_interface/baseitem.h
+++ b/src/plugins/scxmleditor/plugin_interface/baseitem.h
@@ -93,6 +93,7 @@ public:
ScxmlUiFactory *uiFactory() const;
virtual void updateUIProperties();
+ virtual void addChild(ScxmlTag */*tag*/) {};
protected:
virtual void updatePolygon();
diff --git a/src/plugins/scxmleditor/plugin_interface/eventitem.cpp b/src/plugins/scxmleditor/plugin_interface/eventitem.cpp
new file mode 100644
index 0000000000..f61517f7cf
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/eventitem.cpp
@@ -0,0 +1,108 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "eventitem.h"
+
+#include <QColor>
+#include <QFont>
+#include <QGraphicsItem>
+#include <QList>
+#include <QString>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+EventItem::EventItem(const QPointF &pos, BaseItem *parent)
+ : BaseItem(parent)
+{
+ m_eventNameItem = new TextItem(this);
+ m_eventNameItem->setParentItem(this);
+ QFont serifFont("Times", 13, QFont::Normal);
+ m_eventNameItem->setFont(serifFont);
+
+ QString color = editorInfo("fontColor");
+ m_eventNameItem->setDefaultTextColor(color.isEmpty() ? QColor(Qt::black) : QColor(color));
+
+ setPos(pos);
+ m_eventNameItem->setTextInteractionFlags(Qt::NoTextInteraction);
+ setItemBoundingRect(m_eventNameItem->boundingRect());
+}
+
+void EventItem::updateAttributes()
+{
+ QString text = " " + tag()->tagName();
+ if (tag()->attributeNames().size() > 0) {
+ for (int i = 0; i < tag()->attributeNames().size(); ++i)
+ if (tag()->attributeNames().at(i) == "event") {
+ if (tag()->attributeValues().size() > i)
+ text += " / " + tag()->attributeValues().at(i);
+ break;
+ }
+ }
+ m_eventNameItem->setText(text);
+ setItemBoundingRect(m_eventNameItem->boundingRect());
+}
+
+OnEntryExitItem::OnEntryExitItem(BaseItem *parent)
+ : BaseItem(parent)
+{
+ m_eventNameItem = new TextItem(this);
+ m_eventNameItem->setParentItem(this);
+ QFont serifFont("Times", 13, QFont::Normal);
+ m_eventNameItem->setFont(serifFont);
+ m_eventNameItem->setDefaultTextColor(Qt::black);
+ m_eventNameItem->setTextInteractionFlags(Qt::NoTextInteraction);
+}
+
+void OnEntryExitItem::updateAttributes()
+{
+ QString text = tag()->tagName();
+
+ m_eventNameItem->setText(text);
+ setItemBoundingRect(childBoundingRect());
+ checkParentBoundingRect();
+}
+
+void OnEntryExitItem::finalizeCreation()
+{
+ auto children = tag()->allChildren();
+ auto pos = m_eventNameItem->boundingRect().bottomLeft();
+ for (auto child : children) {
+ EventItem *item = new EventItem(pos, this);
+ item->setTag(child);
+ item->updateAttributes();
+ pos = item->pos() + item->boundingRect().bottomLeft();
+ }
+
+ setItemBoundingRect(childBoundingRect());
+}
+
+void OnEntryExitItem::addChild(ScxmlTag *tag)
+{
+ auto pos = childBoundingRect().bottomLeft();
+ EventItem *item = new EventItem(pos, this);
+ item->setTag(tag);
+ item->updateAttributes();
+
+ setItemBoundingRect(childBoundingRect());
+ checkParentBoundingRect();
+}
+
+QRectF OnEntryExitItem::childBoundingRect() const
+{
+ QRectF r = m_eventNameItem->boundingRect();
+
+ const QList<QGraphicsItem *> children = childItems();
+
+ for (const QGraphicsItem *child : children) {
+ QRectF br = child->boundingRect();
+ QPointF p = child->pos() + br.topLeft();
+ br.moveTopLeft(p);
+ r = r.united(br);
+ }
+ return r;
+}
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/eventitem.h b/src/plugins/scxmleditor/plugin_interface/eventitem.h
new file mode 100644
index 0000000000..cf8ff3a9e3
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/eventitem.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "baseitem.h"
+#include "textitem.h"
+
+namespace ScxmlEditor::PluginInterface {
+
+class EventItem : public BaseItem
+{
+public:
+ explicit EventItem(const QPointF &pos = QPointF(), BaseItem *parent = nullptr);
+
+ void updateAttributes() override;
+ void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override {}
+
+private:
+ TextItem *m_eventNameItem;
+};
+
+class OnEntryExitItem : public BaseItem
+{
+public:
+ explicit OnEntryExitItem(BaseItem *parent = nullptr);
+
+ int type() const override { return StateWarningType; }
+
+ void updateAttributes() override;
+ void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override {}
+ void finalizeCreation() override;
+ void addChild(ScxmlTag *tag) override;
+
+ QRectF childBoundingRect() const;
+
+private:
+ TextItem *m_eventNameItem;
+};
+
+} // namespace ScxmlEditor::PluginInterface
diff --git a/src/plugins/scxmleditor/plugin_interface/graphicsscene.cpp b/src/plugins/scxmleditor/plugin_interface/graphicsscene.cpp
index fb0e8252db..94bd8b41ed 100644
--- a/src/plugins/scxmleditor/plugin_interface/graphicsscene.cpp
+++ b/src/plugins/scxmleditor/plugin_interface/graphicsscene.cpp
@@ -467,8 +467,7 @@ void GraphicsScene::endTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag,
break;
}
case ScxmlDocument::TagChangeParent: {
- auto childItem = qobject_cast<ConnectableItem*>(findItem(tag));
-
+ auto childItem = findItem(tag);
if (childItem) {
QTC_ASSERT(tag, break);
BaseItem *newParentItem = findItem(tag->parentTag());
@@ -485,8 +484,11 @@ void GraphicsScene::endTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag,
childItem->setParentItem(newParentItem);
childItem->updateUIProperties();
- childItem->updateTransitions(true);
- childItem->updateTransitionAttributes(true);
+ if (auto childConItem = qobject_cast<ConnectableItem*>(findItem(tag))) {
+ childConItem->updateTransitions(true);
+ childConItem->updateTransitionAttributes(true);
+ }
+
childItem->checkWarnings();
childItem->checkInitial();
if (newParentItem) {
@@ -495,6 +497,8 @@ void GraphicsScene::endTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag,
newParentItem->checkWarnings();
newParentItem->checkOverlapping();
newParentItem->updateUIProperties();
+ if (auto newConItem = qobject_cast<StateItem*>(newParentItem))
+ newConItem->updateBoundingRect();
}
if (oldParentItem)
@@ -549,6 +553,9 @@ void GraphicsScene::endTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag,
}
if (parentItem) {
+ if (childItem == nullptr)
+ parentItem->addChild(childTag);
+
parentItem->updateAttributes();
parentItem->updateUIProperties();
parentItem->checkInitial();
diff --git a/src/plugins/scxmleditor/plugin_interface/stateitem.cpp b/src/plugins/scxmleditor/plugin_interface/stateitem.cpp
index dd1015f9d4..e53bd8c894 100644
--- a/src/plugins/scxmleditor/plugin_interface/stateitem.cpp
+++ b/src/plugins/scxmleditor/plugin_interface/stateitem.cpp
@@ -1,19 +1,18 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "finalstateitem.h"
+#include "stateitem.h"
+
+#include "eventitem.h"
#include "graphicsitemprovider.h"
#include "graphicsscene.h"
#include "idwarningitem.h"
#include "imageprovider.h"
-#include "initialstateitem.h"
-#include "parallelitem.h"
#include "sceneutils.h"
#include "scxmleditorconstants.h"
#include "scxmleditortr.h"
#include "scxmltagutils.h"
#include "scxmluifactory.h"
-#include "stateitem.h"
#include "statewarningitem.h"
#include "textitem.h"
#include "transitionitem.h"
@@ -28,6 +27,7 @@
#include <QTextOption>
#include <QUndoStack>
#include <QtMath>
+#include <QRubberBand>
using namespace ScxmlEditor::PluginInterface;
@@ -171,6 +171,7 @@ void StateItem::updateBoundingRect()
// Check if we need to increase parent boundingrect
if (!r2.isNull()) {
+ positionOnExitItems();
QRectF r = boundingRect();
QRectF r3 = r.united(r2);
@@ -244,7 +245,6 @@ void StateItem::transitionCountChanged()
QRectF StateItem::childItemsBoundingRect() const
{
QRectF r;
- QRectF rr = boundingRect();
QList<QGraphicsItem*> children = childItems();
for (int i = 0; i < children.count(); ++i) {
@@ -256,15 +256,26 @@ QRectF StateItem::childItemsBoundingRect() const
}
}
+ if (m_onEntryItem) {
+ QRectF br = m_onEntryItem->childBoundingRect();
+ QPointF p = m_onEntryItem->pos() + br.topLeft();
+ br.moveTopLeft(p);
+ r = r.united(br);
+ }
+
+ if (m_onExitItem) {
+ QRectF br = m_onExitItem->childBoundingRect();
+ QPointF p = m_onExitItem->pos() + br.topLeft();
+ br.moveTopLeft(p);
+ r = r.united(br);
+ }
+
if (m_transitionRect.isValid()) {
r.setLeft(r.left() - m_transitionRect.width());
r.setHeight(qMax(r.height(), m_transitionRect.height()));
r.moveBottom(qMax(r.bottom(), m_transitionRect.bottom()));
}
- if (!r.isNull())
- r.adjust(-20, -(rr.height() * 0.06 + 40), 20, 20);
-
return r;
}
@@ -418,11 +429,18 @@ void StateItem::updatePolygon()
<< m_drawingRect.bottomLeft()
<< m_drawingRect.topLeft();
- m_titleRect = QRectF(m_drawingRect.left(), m_drawingRect.top(), m_drawingRect.width(), TEXT_ITEM_HEIGHT + m_drawingRect.height() * 0.06);
+ m_titleRect = QRectF(m_drawingRect.left(),
+ m_drawingRect.top(),
+ m_drawingRect.width(),
+ TEXT_ITEM_HEIGHT + m_drawingRect.height() * 0.06);
QFont f = m_stateNameItem->font();
f.setPixelSize(m_titleRect.height() * 0.65);
m_stateNameItem->setFont(f);
+ if (m_onEntryItem)
+ m_onEntryItem->setPos(m_titleRect.x(), m_titleRect.bottom());
+ positionOnExitItems();
+
updateTextPositions();
}
@@ -517,13 +535,41 @@ void StateItem::init(ScxmlTag *tag, BaseItem *parentItem, bool initChildren, boo
if (newItem) {
newItem->init(child, this, initChildren, blockUpdates);
newItem->finalizeCreation();
- }
+ } else
+ addChild(child);
}
}
if (blockUpdates)
setBlockUpdates(false);
}
+
+void StateItem::addChild(ScxmlTag *child)
+{
+ if (child->tagName() == "onentry") {
+ OnEntryExitItem *item = new OnEntryExitItem(this);
+ m_onEntryItem = item;
+ item->setTag(child);
+ item->finalizeCreation();
+ item->updateAttributes();
+ m_onEntryItem->setPos(m_titleRect.x(), m_titleRect.bottom());
+ } else if (child->tagName() == "onexit") {
+ OnEntryExitItem *item = new OnEntryExitItem(this);
+ m_onExitItem = item;
+ item->setTag(child);
+ item->finalizeCreation();
+ item->updateAttributes();
+ positionOnExitItems();
+ }
+}
+
+void StateItem::positionOnExitItems()
+{
+ int offset = m_onEntryItem ? m_onEntryItem->boundingRect().height() : 0;
+ if (m_onExitItem)
+ m_onExitItem->setPos(m_titleRect.x(), m_titleRect.bottom() + offset);
+}
+
QString StateItem::itemId() const
{
return m_stateNameItem ? m_stateNameItem->toPlainText() : QString();
diff --git a/src/plugins/scxmleditor/plugin_interface/stateitem.h b/src/plugins/scxmleditor/plugin_interface/stateitem.h
index 585992f040..beaebe9ebd 100644
--- a/src/plugins/scxmleditor/plugin_interface/stateitem.h
+++ b/src/plugins/scxmleditor/plugin_interface/stateitem.h
@@ -4,7 +4,9 @@
#pragma once
#include "connectableitem.h"
+#include "textitem.h"
#include <QPen>
+#include <QStyleOptionGraphicsItem>
QT_FORWARD_DECLARE_CLASS(QGraphicsSceneMouseEvent)
@@ -16,6 +18,7 @@ class TransitionItem;
class TextItem;
class IdWarningItem;
class StateWarningItem;
+class OnEntryExitItem;
/**
* @brief The StateItem class represents the SCXML-State.
@@ -49,6 +52,8 @@ public:
QRectF childItemsBoundingRect() const;
void connectToParent(BaseItem *parentItem) override;
+ void addChild(ScxmlTag *child) override;
+
protected:
void updatePolygon() override;
void transitionsChanged() override;
@@ -69,6 +74,7 @@ private:
void updateTextPositions();
void checkParentBoundingRect();
void checkWarningItems();
+ void positionOnExitItems();
TextItem *m_stateNameItem;
StateWarningItem *m_stateWarningItem = nullptr;
@@ -76,6 +82,8 @@ private:
QPen m_pen;
bool m_initial = false;
bool m_parallelState = false;
+ QPointer<OnEntryExitItem> m_onEntryItem;
+ QPointer<OnEntryExitItem> m_onExitItem;
QImage m_backgroundImage;
};
diff --git a/src/plugins/scxmleditor/scxmleditor.qbs b/src/plugins/scxmleditor/scxmleditor.qbs
index c110b92f28..39340d8764 100644
--- a/src/plugins/scxmleditor/scxmleditor.qbs
+++ b/src/plugins/scxmleditor/scxmleditor.qbs
@@ -93,6 +93,7 @@ QtcPlugin {
"baseitem.cpp", "baseitem.h",
"connectableitem.cpp", "connectableitem.h",
"cornergrabberitem.cpp", "cornergrabberitem.h",
+ "eventitem.cpp", "eventitem.h",
"finalstateitem.cpp", "finalstateitem.h",
"genericscxmlplugin.cpp", "genericscxmlplugin.h",
"graphicsitemprovider.h",
diff --git a/src/plugins/scxmleditor/scxmleditordocument.cpp b/src/plugins/scxmleditor/scxmleditordocument.cpp
index 9c84910b2d..e10c875908 100644
--- a/src/plugins/scxmleditor/scxmleditordocument.cpp
+++ b/src/plugins/scxmleditor/scxmleditordocument.cpp
@@ -6,7 +6,7 @@
#include "scxmleditorconstants.h"
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <qtsupport/qtkitinformation.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
diff --git a/src/plugins/silversearcher/CMakeLists.txt b/src/plugins/silversearcher/CMakeLists.txt
index 7cc12ebb29..2af582d4a3 100644
--- a/src/plugins/silversearcher/CMakeLists.txt
+++ b/src/plugins/silversearcher/CMakeLists.txt
@@ -2,12 +2,12 @@ add_qtc_plugin(SilverSearcher
PLUGIN_DEPENDS Core TextEditor
SOURCES
findinfilessilversearcher.cpp findinfilessilversearcher.h
- silversearcheroutputparser.cpp silversearcheroutputparser.h
+ silversearcherparser.cpp silversearcherparser.h
silversearcherplugin.cpp silversearcherplugin.h
silversearchertr.h
)
extend_qtc_plugin(SilverSearcher CONDITION WITH_TESTS
SOURCES
- outputparser_test.cpp outputparser_test.h
+ silversearcherparser_test.cpp silversearcherparser_test.h
)
diff --git a/src/plugins/silversearcher/findinfilessilversearcher.cpp b/src/plugins/silversearcher/findinfilessilversearcher.cpp
index b2887cd0a9..6cf428bb64 100644
--- a/src/plugins/silversearcher/findinfilessilversearcher.cpp
+++ b/src/plugins/silversearcher/findinfilessilversearcher.cpp
@@ -2,19 +2,13 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "findinfilessilversearcher.h"
+#include "silversearcherparser.h"
+#include "silversearchertr.h"
-#include <aggregation/aggregate.h>
-#include <coreplugin/progressmanager/progressmanager.h>
#include <texteditor/findinfiles.h>
-#include <utils/algorithm.h>
-#include <utils/environment.h>
-#include <utils/fileutils.h>
+#include <utils/async.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
-
-#include "silversearcheroutputparser.h"
-#include "silversearchertr.h"
#include <QHBoxLayout>
#include <QLabel>
@@ -22,17 +16,13 @@
#include <QSettings>
using namespace Core;
+using namespace SilverSearcher;
using namespace TextEditor;
using namespace Utils;
namespace {
-const QLatin1String silverSearcherName("Silver Searcher");
-
-using FutureInterfaceType = QFutureInterface<FileSearchResultList>;
-
-const QString metacharacters = "+()^$.{}[]|\\";
-
-const QString SearchOptionsString = "SearchOptionsString";
+const QLatin1String s_metaCharacters = QLatin1String("+()^$.{}[]|\\");
+const QLatin1String s_searchOptionsString = QLatin1String("SearchOptionsString");
class SilverSearcherSearchOptions
{
@@ -40,96 +30,80 @@ public:
QString searchOptions;
};
-QString convertWildcardToRegex(const QString &wildcard)
+static QString convertWildcardToRegex(const QString &wildcard)
{
QString regex;
const int wildcardSize = wildcard.size();
regex.append('^');
for (int i = 0; i < wildcardSize; ++i) {
const QChar ch = wildcard[i];
- if (ch == '*') {
+ if (ch == '*')
regex.append(".*");
- } else if (ch == '?') {
+ else if (ch == '?')
regex.append('.');
- } else if (metacharacters.indexOf(ch) != -1) {
- regex.append('\\');
+ else if (s_metaCharacters.indexOf(ch) != -1)
+ regex.append('\\' + ch);
+ else
regex.append(ch);
- } else {
- regex.append(ch);
- }
}
regex.append('$');
-
return regex;
}
-bool isSilverSearcherAvailable()
+static bool isSilverSearcherAvailable()
{
- QtcProcess silverSearcherProcess;
+ Process silverSearcherProcess;
silverSearcherProcess.setCommand({"ag", {"--version"}});
silverSearcherProcess.start();
- if (silverSearcherProcess.waitForFinished(1000)) {
- if (silverSearcherProcess.cleanedStdOut().contains("ag version"))
- return true;
- }
-
- return false;
+ return silverSearcherProcess.waitForFinished(1000)
+ && silverSearcherProcess.cleanedStdOut().contains("ag version");
}
-void runSilverSeacher(FutureInterfaceType &fi, FileFindParameters parameters)
+static void runSilverSeacher(QPromise<SearchResultItems> &promise,
+ const FileFindParameters &parameters)
{
- ProgressTimer progress(fi, 5);
- const FilePath directory = FilePath::fromUserInput(parameters.additionalParameters.toString());
- QStringList arguments = {"--parallel", "--ackmate"};
-
- if (parameters.flags & FindCaseSensitively)
- arguments << "-s";
- else
- arguments << "-i";
-
- if (parameters.flags & FindWholeWords)
- arguments << "-w";
-
- if (!(parameters.flags & FindRegularExpression))
- arguments << "-Q";
-
- for (const QString &filter : std::as_const(parameters.exclusionFilters))
- arguments << "--ignore" << filter;
-
- QString nameFiltersAsRegex;
- for (const QString &filter : std::as_const(parameters.nameFilters))
- nameFiltersAsRegex += QString("(%1)|").arg(convertWildcardToRegex(filter));
- nameFiltersAsRegex.remove(nameFiltersAsRegex.length() - 1, 1);
-
- arguments << "-G" << nameFiltersAsRegex;
-
- SilverSearcherSearchOptions params = parameters.searchEngineParameters
- .value<SilverSearcherSearchOptions>();
- if (!params.searchOptions.isEmpty())
- arguments << params.searchOptions.split(' ');
-
- arguments << "--" << parameters.text << directory.normalizedPathName().toString();
-
- QtcProcess process;
- process.setCommand({"ag", arguments});
- process.start();
- if (process.waitForFinished()) {
- QRegularExpression regexp;
- if (parameters.flags & FindRegularExpression) {
- const QRegularExpression::PatternOptions patternOptions
- = (parameters.flags & FindCaseSensitively)
- ? QRegularExpression::NoPatternOption
- : QRegularExpression::CaseInsensitiveOption;
- regexp.setPattern(parameters.text);
- regexp.setPatternOptions(patternOptions);
- }
- SilverSearcher::SilverSearcherOutputParser parser(process.cleanedStdOut(), regexp);
- FileSearchResultList items = parser.parse();
- if (!items.isEmpty())
- fi.reportResult(items);
- } else {
- fi.reportCanceled();
- }
+ const auto setupProcess = [parameters](Process &process) {
+ const FilePath directory
+ = FilePath::fromUserInput(parameters.additionalParameters.toString());
+ QStringList arguments = {"--parallel", "--ackmate"};
+
+ if (parameters.flags & FindCaseSensitively)
+ arguments << "-s";
+ else
+ arguments << "-i";
+
+ if (parameters.flags & FindWholeWords)
+ arguments << "-w";
+
+ if (!(parameters.flags & FindRegularExpression))
+ arguments << "-Q";
+
+ for (const QString &filter : std::as_const(parameters.exclusionFilters))
+ arguments << "--ignore" << filter;
+
+ QString nameFiltersAsRegExp;
+ for (const QString &filter : std::as_const(parameters.nameFilters))
+ nameFiltersAsRegExp += QString("(%1)|").arg(convertWildcardToRegex(filter));
+ nameFiltersAsRegExp.remove(nameFiltersAsRegExp.length() - 1, 1);
+
+ arguments << "-G" << nameFiltersAsRegExp;
+
+ const SilverSearcherSearchOptions params = parameters.searchEngineParameters
+ .value<SilverSearcherSearchOptions>();
+ if (!params.searchOptions.isEmpty())
+ arguments << params.searchOptions.split(' ');
+
+ arguments << "--" << parameters.text << directory.normalizedPathName().toString();
+ process.setCommand({"ag", arguments});
+ };
+
+ FilePath lastFilePath;
+ const auto outputParser = [&lastFilePath](const QFuture<void> &future, const QString &input,
+ const std::optional<QRegularExpression> &regExp) {
+ return SilverSearcher::parse(future, input, regExp, &lastFilePath);
+ };
+
+ TextEditor::searchInProcessOutput(promise, parameters, setupProcess, outputParser);
}
} // namespace
@@ -154,6 +128,7 @@ FindInFilesSilverSearcher::FindInFilesSilverSearcher(QObject *parent)
QTC_ASSERT(findInFiles, return);
findInFiles->addSearchEngine(this);
+ // TODO: Make disabled by default and run isSilverSearcherAvailable asynchronously
setEnabled(isSilverSearcherAvailable());
if (!isEnabled()) {
QLabel *label = new QLabel(Tr::tr("Silver Searcher is not available on the system."));
@@ -162,10 +137,6 @@ FindInFilesSilverSearcher::FindInFilesSilverSearcher(QObject *parent)
}
}
-FindInFilesSilverSearcher::~FindInFilesSilverSearcher()
-{
-}
-
QVariant FindInFilesSilverSearcher::parameters() const
{
SilverSearcherSearchOptions silverSearcherSearchOptions;
@@ -175,12 +146,12 @@ QVariant FindInFilesSilverSearcher::parameters() const
QString FindInFilesSilverSearcher::title() const
{
- return silverSearcherName;
+ return "Silver Searcher";
}
QString FindInFilesSilverSearcher::toolTip() const
{
- return QString();
+ return {};
}
QWidget *FindInFilesSilverSearcher::widget() const
@@ -190,13 +161,13 @@ QWidget *FindInFilesSilverSearcher::widget() const
void FindInFilesSilverSearcher::writeSettings(QSettings *settings) const
{
- settings->setValue(SearchOptionsString, m_searchOptionsLineEdit->text());
+ settings->setValue(s_searchOptionsString, m_searchOptionsLineEdit->text());
}
-QFuture<FileSearchResultList> FindInFilesSilverSearcher::executeSearch(
+QFuture<SearchResultItems> FindInFilesSilverSearcher::executeSearch(
const FileFindParameters &parameters, BaseFileFind * /*baseFileFind*/)
{
- return Utils::runAsync(runSilverSeacher, parameters);
+ return Utils::asyncRun(runSilverSeacher, parameters);
}
IEditor *FindInFilesSilverSearcher::openEditor(const SearchResultItem & /*item*/,
@@ -207,7 +178,7 @@ IEditor *FindInFilesSilverSearcher::openEditor(const SearchResultItem & /*item*/
void FindInFilesSilverSearcher::readSettings(QSettings *settings)
{
- m_searchOptionsLineEdit->setText(settings->value(SearchOptionsString).toString());
+ m_searchOptionsLineEdit->setText(settings->value(s_searchOptionsString).toString());
}
} // namespace SilverSearcher
diff --git a/src/plugins/silversearcher/findinfilessilversearcher.h b/src/plugins/silversearcher/findinfilessilversearcher.h
index 3a5363d713..bc5e224c21 100644
--- a/src/plugins/silversearcher/findinfilessilversearcher.h
+++ b/src/plugins/silversearcher/findinfilessilversearcher.h
@@ -3,10 +3,9 @@
#pragma once
-#include <coreplugin/find/ifindsupport.h>
#include <texteditor/basefilefind.h>
-#include <utils/fileutils.h>
+#include <utils/filepath.h>
#include <QPointer>
@@ -14,6 +13,8 @@ QT_BEGIN_NAMESPACE
class QLineEdit;
QT_END_NAMESPACE
+namespace Core { class IFindSupport; }
+
namespace SilverSearcher {
class FindInFilesSilverSearcher : public TextEditor::SearchEngine
@@ -22,7 +23,6 @@ class FindInFilesSilverSearcher : public TextEditor::SearchEngine
public:
explicit FindInFilesSilverSearcher(QObject *parent);
- ~FindInFilesSilverSearcher() override;
// TextEditor::FileFindExtension
QString title() const override;
@@ -31,9 +31,9 @@ public:
QVariant parameters() const override;
void readSettings(QSettings *settings) override;
void writeSettings(QSettings *settings) const override;
- QFuture<Utils::FileSearchResultList> executeSearch(
+ QFuture<Utils::SearchResultItems> executeSearch(
const TextEditor::FileFindParameters &parameters, TextEditor::BaseFileFind *) override;
- Core::IEditor *openEditor(const Core::SearchResultItem &item,
+ Core::IEditor *openEditor(const Utils::SearchResultItem &item,
const TextEditor::FileFindParameters &parameters) override;
private:
diff --git a/src/plugins/silversearcher/outputparser_test.cpp b/src/plugins/silversearcher/outputparser_test.cpp
deleted file mode 100644
index a9185b3156..0000000000
--- a/src/plugins/silversearcher/outputparser_test.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "outputparser_test.h"
-#include "silversearcheroutputparser.h"
-
-#include <QtTest>
-
-using namespace Utils;
-
-namespace SilverSearcher {
-namespace Internal {
-
-void OutputParserTest::test_data()
-{
- QTest::addColumn<QString>("parserOutput");
- QTest::addColumn<FileSearchResultList>("results");
-
- QTest::addRow("nothing") << QString("\n") << FileSearchResultList();
- QTest::addRow("oneFileOneMatch")
- << QString(":/file/path/to/filename.h\n"
- "1;1 5:match\n")
- << FileSearchResultList({{"/file/path/to/filename.h", 1, "match", 1, 5, {}}});
- QTest::addRow("multipleFilesWithOneMatch")
- << QString(":/file/path/to/filename1.h\n"
- "1;1 5:match\n"
- "\n"
- ":/file/path/to/filename2.h\n"
- "2;2 5: match\n")
- << FileSearchResultList({{"/file/path/to/filename1.h", 1, "match", 1, 5, {}},
- {"/file/path/to/filename2.h", 2, " match", 2, 5, {}}});
- QTest::addRow("oneFileMultipleMatches")
- << QString(":/file/path/to/filename.h\n"
- "1;1 5,7 5:match match\n")
- << FileSearchResultList({{"/file/path/to/filename.h", 1, "match match", 1, 5, {}},
- {"/file/path/to/filename.h", 1, "match match", 7, 5, {}}});
- QTest::addRow("multipleFilesWithMultipleMatches")
- << QString(":/file/path/to/filename1.h\n"
- "1;1 5,7 5:match match\n"
- "\n"
- ":/file/path/to/filename2.h\n"
- "2;2 5,8 5: match match\n")
- << FileSearchResultList({{"/file/path/to/filename1.h", 1, "match match", 1, 5, {}},
- {"/file/path/to/filename1.h", 1, "match match", 7, 5, {}},
- {"/file/path/to/filename2.h", 2, " match match", 2, 5, {}},
- {"/file/path/to/filename2.h", 2, " match match", 8, 5, {}}});
-}
-
-void OutputParserTest::test()
-{
- QFETCH(QString, parserOutput);
- QFETCH(FileSearchResultList, results);
- SilverSearcher::SilverSearcherOutputParser ssop(parserOutput);
- const FileSearchResultList items = ssop.parse();
- QCOMPARE(items, results);
-}
-
-} // namespace Internal
-} // namespace SilverSearcher
diff --git a/src/plugins/silversearcher/silversearcher.qbs b/src/plugins/silversearcher/silversearcher.qbs
index f099858f6e..08a25367aa 100644
--- a/src/plugins/silversearcher/silversearcher.qbs
+++ b/src/plugins/silversearcher/silversearcher.qbs
@@ -9,16 +9,14 @@ QtcPlugin {
files: [
"findinfilessilversearcher.cpp", "findinfilessilversearcher.h",
- "silversearcheroutputparser.cpp", "silversearcheroutputparser.h",
+ "silversearcherparser.cpp", "silversearcherparser.h",
"silversearcherplugin.cpp", "silversearcherplugin.h",
]
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
- "outputparser_test.cpp",
- "outputparser_test.h",
+ "silversearcherparser_test.cpp",
+ "silversearcherparser_test.h",
]
}
}
diff --git a/src/plugins/silversearcher/silversearcheroutputparser.cpp b/src/plugins/silversearcher/silversearcheroutputparser.cpp
deleted file mode 100644
index 9088310c3f..0000000000
--- a/src/plugins/silversearcher/silversearcheroutputparser.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright (C) 2017 Przemyslaw Gorszkowski <pgorszkowski@gmail.com>.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "silversearcheroutputparser.h"
-
-#include <QString>
-
-namespace SilverSearcher {
-
-SilverSearcherOutputParser::SilverSearcherOutputParser(
- const QString &output, const QRegularExpression &regexp)
- : output(output)
- , regexp(regexp)
- , outputSize(output.size())
-{
- hasRegexp = !regexp.pattern().isEmpty();
-}
-
-Utils::FileSearchResultList SilverSearcherOutputParser::parse()
-{
- while (index < outputSize - 1) {
- if (output[index] == '\n') {
- ++index;
- continue;
- }
- parseFilePath();
- while (index < outputSize && output[index] != '\n') {
- parseLineNumber();
- if (index >= outputSize - 1)
- break;
- int matches = parseMatches();
- if (index >= outputSize - 1)
- break;
- parseText();
- for (int i = 0; i < matches; ++i)
- items[items.size() - i - 1].matchingLine = item.matchingLine;
- }
- }
-
- return items;
-}
-
-bool SilverSearcherOutputParser::parseFilePath()
-{
- int startIndex = ++index;
- while (index < outputSize && output[index] != '\n')
- ++index;
- item.fileName = Utils::FilePath::fromString(QString(output.data() + startIndex, index - startIndex));
- ++index;
- return true;
-}
-
-bool SilverSearcherOutputParser::parseLineNumber()
-{
- int startIndex = index;
- while (index < outputSize && output[++index] != ';') { }
-
- item.lineNumber = QString(output.data() + startIndex, index - startIndex).toInt();
- ++index;
- return true;
-}
-
-bool SilverSearcherOutputParser::parseMatchIndex()
-{
- int startIndex = index;
- while (index < outputSize && output[++index] != ' ') { }
-
- item.matchStart = QString(output.data() + startIndex, index - startIndex).toInt();
- ++index;
- return true;
-}
-
-bool SilverSearcherOutputParser::parseMatchLength()
-{
- int startIndex = index;
- while (index < outputSize && output[++index] != ':' && output[index] != ',') { }
-
- item.matchLength = QString(output.data() + startIndex, index - startIndex).toInt();
- return true;
-}
-
-int SilverSearcherOutputParser::parseMatches()
-{
- int matches = 1;
- const int colon = output.indexOf(':', index);
- QString text;
- if (colon != -1) {
- const int textStart = colon + 1;
- const int newline = output.indexOf('\n', textStart);
- text = output.mid(textStart, newline >= 0 ? newline - textStart : -1);
- }
- while (index < outputSize && output[index] != ':') {
- if (output[index] == ',') {
- ++matches;
- ++index;
- }
- parseMatchIndex();
- parseMatchLength();
- if (hasRegexp) {
- const QString part = QString(text.mid(item.matchStart, item.matchLength));
- item.regexpCapturedTexts = regexp.match(part).capturedTexts();
- }
- items << item;
- }
-
- ++index;
- return matches;
-}
-
-bool SilverSearcherOutputParser::parseText()
-{
- int startIndex = index;
- while (index < outputSize && output[++index] != '\n') { }
- item.matchingLine = QString(output.data() + startIndex, index - startIndex);
- ++index;
- return true;
-}
-
-} // namespace SilverSearcher
diff --git a/src/plugins/silversearcher/silversearcheroutputparser.h b/src/plugins/silversearcher/silversearcheroutputparser.h
deleted file mode 100644
index 187e463938..0000000000
--- a/src/plugins/silversearcher/silversearcheroutputparser.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2017 Przemyslaw Gorszkowski <pgorszkowski@gmail.com>.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <coreplugin/find/searchresultwindow.h>
-#include <utils/filesearch.h>
-
-#include <QList>
-#include <QRegularExpression>
-
-namespace SilverSearcher {
-
-class SilverSearcherOutputParser
-{
-public:
- SilverSearcherOutputParser(const QString &output, const QRegularExpression &regexp = {});
-
- Utils::FileSearchResultList parse();
-
-private:
- int parseMatches();
- bool parseMatchLength();
- bool parseMatchIndex();
- bool parseLineNumber();
- bool parseFilePath();
- bool parseText();
-
- QString output;
- QRegularExpression regexp;
- bool hasRegexp = false;
- int outputSize = 0;
- int index = 0;
- Utils::FileSearchResult item;
- Utils::FileSearchResultList items;
-};
-
-} // namespace SilverSearcher
diff --git a/src/plugins/silversearcher/silversearcherparser.cpp b/src/plugins/silversearcher/silversearcherparser.cpp
new file mode 100644
index 0000000000..1a2f5e55e8
--- /dev/null
+++ b/src/plugins/silversearcher/silversearcherparser.cpp
@@ -0,0 +1,157 @@
+// Copyright (C) 2017 Przemyslaw Gorszkowski <pgorszkowski@gmail.com>.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "silversearcherparser.h"
+
+using namespace Utils;
+
+namespace SilverSearcher {
+
+/*
+// Example output (searching for "ab"):
+
+:/home/jarek/dev/creator-master/src/libs/qmlpuppetcommunication/container/imagecontainer.cpp
+149;60 2: static const bool dontUseSharedMemory = qEnvironmentVariableIsSet("DESIGNER_DONT_USE_SHARED_MEMORY");
+198;65 2: qCInfo(imageContainerDebug()) << Q_FUNC_INFO << "Not able to create image:" << imageWidth << imageHeight << imageFormat;
+
+:/home/jarek/dev/creator-master/src/libs/qmlpuppetcommunication/container/propertyabstractcontainer.cpp
+4;18 2:#include "propertyabstractcontainer.h"
+10;8 2,35 2:PropertyAbstractContainer::PropertyAbstractContainer()
+*/
+
+static QStringView nextLine(QStringView *remainingInput)
+{
+ const int newLinePos = remainingInput->indexOf('\n');
+ if (newLinePos < 0) {
+ QStringView ret = *remainingInput;
+ *remainingInput = QStringView();
+ return ret;
+ }
+ QStringView ret = remainingInput->left(newLinePos);
+ *remainingInput = remainingInput->mid(newLinePos + 1);
+ return ret;
+}
+
+static bool parseNumber(QStringView numberString, int *number)
+{
+ for (const auto &c : numberString) {
+ if (!c.isDigit())
+ return false;
+ }
+ bool ok = false;
+ int parsedNumber = numberString.toInt(&ok);
+ if (ok)
+ *number = parsedNumber;
+ return ok;
+}
+
+static bool parseLineNumber(QStringView *remainingInput, int *lineNumber)
+{
+ const int lineNumberDelimiterPos = remainingInput->indexOf(';');
+ if (lineNumberDelimiterPos < 0)
+ return false;
+
+ if (!parseNumber(remainingInput->left(lineNumberDelimiterPos), lineNumber))
+ return false;
+
+ *remainingInput = remainingInput->mid(lineNumberDelimiterPos + 1);
+ return true;
+}
+
+static bool parseLineHit(QStringView hitString, QPair<int, int> *hit)
+{
+ const int hitSpaceDelimiterPos = hitString.indexOf(' ');
+ if (hitSpaceDelimiterPos < 0)
+ return false;
+
+ int hitStart = -1;
+ if (!parseNumber(hitString.left(hitSpaceDelimiterPos), &hitStart))
+ return false;
+
+ int hitLength = -1;
+ if (!parseNumber(hitString.mid(hitSpaceDelimiterPos + 1), &hitLength))
+ return false;
+
+ *hit = {hitStart, hitLength};
+ return true;
+}
+
+static bool parseLineHits(QStringView *remainingInput, QList<QPair<int, int>> *hits)
+{
+ const int hitsDelimiterPos = remainingInput->indexOf(':');
+ if (hitsDelimiterPos < 0)
+ return false;
+
+ const QStringView hitsString = remainingInput->left(hitsDelimiterPos);
+ const QList<QStringView> hitStrings = hitsString.split(',', Qt::SkipEmptyParts);
+ for (const auto hitString : hitStrings) {
+ QPair<int, int> hit;
+ if (!parseLineHit(hitString, &hit))
+ return false;
+ hits->append(hit);
+ }
+ *remainingInput = remainingInput->mid(hitsDelimiterPos + 1);
+ return true;
+}
+
+SearchResultItems parse(const QFuture<void> &future, const QString &input,
+ const std::optional<QRegularExpression> &regExp, FilePath *lastFilePath)
+{
+ QTC_ASSERT(lastFilePath, return {});
+ SearchResultItems items;
+
+ QStringView remainingInput(input);
+ while (true) {
+ if (future.isCanceled())
+ return {};
+
+ if (remainingInput.isEmpty())
+ break;
+
+ const QStringView filePathLine = nextLine(&remainingInput);
+ if (filePathLine.isEmpty()) {
+ *lastFilePath = {}; // Clear the parser state
+ continue;
+ }
+
+ if (filePathLine.startsWith(':'))
+ *lastFilePath = FilePath::fromPathPart(filePathLine.mid(1));
+
+ while (true) {
+ QStringView hitLine = nextLine(&remainingInput);
+ if (hitLine.isEmpty())
+ break;
+ int lineNumber = -1;
+ if (!parseLineNumber(&hitLine, &lineNumber))
+ break;
+
+ // List of pairs <hit start pos, hit length>
+ QList<QPair<int, int>> hits;
+ if (!parseLineHits(&hitLine, &hits))
+ break;
+
+ SearchResultItem item;
+ item.setFilePath(*lastFilePath);
+ item.setDisplayText(hitLine.toString());
+ item.setUseTextEditorFont(true);
+ for (const QPair<int, int> &hit : hits) {
+ item.setMainRange(lineNumber, hit.first, hit.second);
+ item.setUserData(
+ regExp ? regExp->match(hitLine.mid(hit.first, hit.second)).capturedTexts()
+ : QVariant());
+ items.append(item);
+ }
+ }
+ }
+ return items;
+}
+
+SearchResultItems parse(const QString &input, const std::optional<QRegularExpression> &regExp)
+{
+ QPromise<void> promise;
+ promise.start();
+ FilePath dummy;
+ return SilverSearcher::parse(promise.future(), input, regExp, &dummy);
+}
+
+} // namespace SilverSearcher
diff --git a/src/plugins/silversearcher/silversearcherparser.h b/src/plugins/silversearcher/silversearcherparser.h
new file mode 100644
index 0000000000..67b3e124f4
--- /dev/null
+++ b/src/plugins/silversearcher/silversearcherparser.h
@@ -0,0 +1,22 @@
+// Copyright (C) 2017 Przemyslaw Gorszkowski <pgorszkowski@gmail.com>.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <utils/searchresultitem.h>
+
+#include <QFuture>
+#include <QRegularExpression>
+
+namespace Utils { class FilePath; }
+
+namespace SilverSearcher {
+
+Utils::SearchResultItems parse(const QFuture<void> &future, const QString &input,
+ const std::optional<QRegularExpression> &regExp,
+ Utils::FilePath *lastFilePath);
+
+Utils::SearchResultItems parse(const QString &input,
+ const std::optional<QRegularExpression> &regExp = {});
+
+} // namespace SilverSearcher
diff --git a/src/plugins/silversearcher/silversearcherparser_test.cpp b/src/plugins/silversearcher/silversearcherparser_test.cpp
new file mode 100644
index 0000000000..b23002e191
--- /dev/null
+++ b/src/plugins/silversearcher/silversearcherparser_test.cpp
@@ -0,0 +1,69 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "silversearcherparser.h"
+#include "silversearcherparser_test.h"
+
+#include <QtTest>
+
+using namespace Utils;
+
+namespace SilverSearcher {
+namespace Internal {
+
+SearchResultItem searchResult(const FilePath &fileName, const QString &matchingLine,
+ int lineNumber, int matchStart, int matchLength)
+{
+ SearchResultItem result;
+ result.setFilePath(fileName);
+ result.setLineText(matchingLine);
+ result.setMainRange(lineNumber, matchStart, matchLength);
+ result.setUseTextEditorFont(true);
+ return result;
+}
+
+void OutputParserTest::test_data()
+{
+ QTest::addColumn<QString>("input");
+ QTest::addColumn<SearchResultItems>("results");
+
+ QTest::addRow("nothing") << QString("\n") << SearchResultItems();
+ QTest::addRow("oneFileOneMatch")
+ << QString(":/file/path/to/filename.h\n"
+ "1;1 5:match\n")
+ << SearchResultItems{searchResult("/file/path/to/filename.h", "match", 1, 1, 5)};
+ QTest::addRow("multipleFilesWithOneMatch")
+ << QString(":/file/path/to/filename1.h\n"
+ "1;1 5:match\n"
+ "\n"
+ ":/file/path/to/filename2.h\n"
+ "2;2 5: match\n")
+ << SearchResultItems{searchResult("/file/path/to/filename1.h", "match", 1, 1, 5),
+ searchResult("/file/path/to/filename2.h", " match", 2, 2, 5)};
+ QTest::addRow("oneFileMultipleMatches")
+ << QString(":/file/path/to/filename.h\n"
+ "1;1 5,7 5:match match\n")
+ << SearchResultItems{searchResult("/file/path/to/filename.h", "match match", 1, 1, 5),
+ searchResult("/file/path/to/filename.h", "match match", 1, 7, 5)};
+ QTest::addRow("multipleFilesWithMultipleMatches")
+ << QString(":/file/path/to/filename1.h\n"
+ "1;1 5,7 5:match match\n"
+ "\n"
+ ":/file/path/to/filename2.h\n"
+ "2;2 5,8 5: match match\n")
+ << SearchResultItems{searchResult("/file/path/to/filename1.h", "match match", 1, 1, 5),
+ searchResult("/file/path/to/filename1.h", "match match", 1, 7, 5),
+ searchResult("/file/path/to/filename2.h", " match match", 2, 2, 5),
+ searchResult("/file/path/to/filename2.h", " match match", 2, 8, 5)};
+}
+
+void OutputParserTest::test()
+{
+ QFETCH(QString, input);
+ QFETCH(SearchResultItems, results);
+ const SearchResultItems items = SilverSearcher::parse(input);
+ QCOMPARE(items, results);
+}
+
+} // namespace Internal
+} // namespace SilverSearcher
diff --git a/src/plugins/silversearcher/outputparser_test.h b/src/plugins/silversearcher/silversearcherparser_test.h
index 6d85cd2198..6d85cd2198 100644
--- a/src/plugins/silversearcher/outputparser_test.h
+++ b/src/plugins/silversearcher/silversearcherparser_test.h
diff --git a/src/plugins/silversearcher/silversearcherplugin.cpp b/src/plugins/silversearcher/silversearcherplugin.cpp
index e16ed4a349..1f143407d7 100644
--- a/src/plugins/silversearcher/silversearcherplugin.cpp
+++ b/src/plugins/silversearcher/silversearcherplugin.cpp
@@ -1,9 +1,9 @@
// Copyright (C) 2017 Przemyslaw Gorszkowski <pgorszkowski@gmail.com>.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "silversearcherplugin.h"
#include "findinfilessilversearcher.h"
-#include "outputparser_test.h"
+#include "silversearcherparser_test.h"
+#include "silversearcherplugin.h"
namespace SilverSearcher::Internal {
diff --git a/src/plugins/squish/deletesymbolicnamedialog.cpp b/src/plugins/squish/deletesymbolicnamedialog.cpp
index 44a3b507d5..dbb6804fb5 100644
--- a/src/plugins/squish/deletesymbolicnamedialog.cpp
+++ b/src/plugins/squish/deletesymbolicnamedialog.cpp
@@ -63,7 +63,7 @@ DeleteSymbolicNameDialog::DeleteSymbolicNameDialog(const QString &symbolicName,
updateDetailsLabel(symbolicName);
populateSymbolicNamesList(names);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_detailsLabel,
diff --git a/src/plugins/squish/images/picker.png b/src/plugins/squish/images/picker.png
new file mode 100644
index 0000000000..1e9a13e8a1
--- /dev/null
+++ b/src/plugins/squish/images/picker.png
Binary files differ
diff --git a/src/plugins/squish/images/picker@2x.png b/src/plugins/squish/images/picker@2x.png
new file mode 100644
index 0000000000..390926b891
--- /dev/null
+++ b/src/plugins/squish/images/picker@2x.png
Binary files differ
diff --git a/src/plugins/squish/objectsmapdocument.cpp b/src/plugins/squish/objectsmapdocument.cpp
index eb7266ddc4..1e0b30766d 100644
--- a/src/plugins/squish/objectsmapdocument.cpp
+++ b/src/plugins/squish/objectsmapdocument.cpp
@@ -10,7 +10,7 @@
#include "squishtr.h"
#include <utils/fileutils.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QtCore5Compat/QTextCodec>
@@ -195,7 +195,7 @@ Core::IDocument::OpenResult ObjectsMapDocument::openImpl(QString *error,
text = reader.data();
} else {
- const Utils::FilePath base = SquishPlugin::squishSettings()->squishPath.filePath();
+ const Utils::FilePath base = SquishPlugin::squishSettings()->squishPath();
if (base.isEmpty()) {
if (error)
error->append(Tr::tr("Incomplete Squish settings. "
@@ -209,7 +209,7 @@ Core::IDocument::OpenResult ObjectsMapDocument::openImpl(QString *error,
return OpenResult::ReadError;
}
- Utils::QtcProcess objectMapReader;
+ Utils::Process objectMapReader;
objectMapReader.setCommand({exe, {"--scriptMap", "--mode", "read",
"--scriptedObjectMapPath", realFileName.toUserOutput()}});
objectMapReader.setCodec(QTextCodec::codecForName("UTF-8"));
@@ -233,14 +233,14 @@ bool ObjectsMapDocument::writeFile(const Utils::FilePath &fileName) const
}
// otherwise we need the objectmaptool to write the scripted object map again
- const Utils::FilePath base = SquishPlugin::squishSettings()->squishPath.filePath();
+ const Utils::FilePath base = SquishPlugin::squishSettings()->squishPath();
if (base.isEmpty())
return false;
const Utils::FilePath exe = base.pathAppended("lib/exec/objectmaptool").withExecutableSuffix();
if (!exe.isExecutableFile())
return false;
- Utils::QtcProcess objectMapWriter;
+ Utils::Process objectMapWriter;
objectMapWriter.setCommand({exe, {"--scriptMap", "--mode", "write",
"--scriptedObjectMapPath", fileName.toUserOutput()}});
objectMapWriter.setWriteData(contents());
diff --git a/src/plugins/squish/objectsmapeditorwidget.cpp b/src/plugins/squish/objectsmapeditorwidget.cpp
index 7e215d0d77..e689a5a296 100644
--- a/src/plugins/squish/objectsmapeditorwidget.cpp
+++ b/src/plugins/squish/objectsmapeditorwidget.cpp
@@ -88,7 +88,7 @@ void ObjectsMapEditorWidget::initUi()
m_stackedLayout->addWidget(validPropertiesWidget);
m_stackedLayout->addWidget(invalidPropertiesWidget);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Row {
m_propertiesTree,
diff --git a/src/plugins/squish/opensquishsuitesdialog.cpp b/src/plugins/squish/opensquishsuitesdialog.cpp
index f36d402dca..c32e9d0a39 100644
--- a/src/plugins/squish/opensquishsuitesdialog.cpp
+++ b/src/plugins/squish/opensquishsuitesdialog.cpp
@@ -40,7 +40,7 @@ OpenSquishSuitesDialog::OpenSquishSuitesDialog(QWidget *parent)
m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Open);
m_buttonBox->button(QDialogButtonBox::Open)->setEnabled(false);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
new QLabel(Tr::tr("Base directory:")),
diff --git a/src/plugins/squish/squish.qrc b/src/plugins/squish/squish.qrc
index 1a62ee1c1c..e55323c3cc 100644
--- a/src/plugins/squish/squish.qrc
+++ b/src/plugins/squish/squish.qrc
@@ -8,6 +8,8 @@
<file>images/jumpTo@2x.png</file>
<file>images/data.png</file>
<file>images/data@2x.png</file>
+ <file>images/picker.png</file>
+ <file>images/picker@2x.png</file>
<file>wizard/suite/wizard.json</file>
</qresource>
</RCC>
diff --git a/src/plugins/squish/squishfilehandler.cpp b/src/plugins/squish/squishfilehandler.cpp
index 956f948822..8a726cb9ba 100644
--- a/src/plugins/squish/squishfilehandler.cpp
+++ b/src/plugins/squish/squishfilehandler.cpp
@@ -15,8 +15,10 @@
#include <coreplugin/documentmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
+#include <coreplugin/session.h>
+
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+
#include <utils/algorithm.h>
#include <utils/aspects.h>
#include <utils/layoutbuilder.h>
@@ -31,6 +33,8 @@
#include <QTimer>
#include <QVBoxLayout>
+using namespace Core;
+
namespace Squish {
namespace Internal {
@@ -51,7 +55,7 @@ public:
QWidget *widget = new QWidget(this);
auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Form {
label, &aut, br,
arguments,
@@ -93,8 +97,7 @@ SquishFileHandler::SquishFileHandler(QObject *parent)
: QObject(parent)
{
m_instance = this;
- auto sessionManager = ProjectExplorer::SessionManager::instance();
- connect(sessionManager, &ProjectExplorer::SessionManager::sessionLoaded,
+ connect(SessionManager::instance(), &SessionManager::sessionLoaded,
this, &SquishFileHandler::onSessionLoaded);
}
@@ -258,7 +261,7 @@ void SquishFileHandler::openTestSuites()
}
}
emit suitesOpened();
- ProjectExplorer::SessionManager::setValue(SK_OpenSuites, suitePathsAsStringList());
+ SessionManager::setValue(SK_OpenSuites, suitePathsAsStringList());
}
void SquishFileHandler::openTestSuite(const Utils::FilePath &suiteConfPath, bool isReopen)
@@ -288,7 +291,7 @@ void SquishFileHandler::openTestSuite(const Utils::FilePath &suiteConfPath, bool
m_suites.insert(suiteName, suiteConfPath);
emit testTreeItemCreated(item);
}
- ProjectExplorer::SessionManager::setValue(SK_OpenSuites, suitePathsAsStringList());
+ SessionManager::setValue(SK_OpenSuites, suitePathsAsStringList());
}
static void closeOpenedEditorsFor(const Utils::FilePath &filePath, bool askAboutModifiedEditors)
@@ -310,13 +313,13 @@ void SquishFileHandler::closeTestSuite(const QString &suiteName)
// TODO remove file watcher
m_suites.remove(suiteName);
emit suiteTreeItemRemoved(suiteName);
- ProjectExplorer::SessionManager::setValue(SK_OpenSuites, suitePathsAsStringList());
+ SessionManager::setValue(SK_OpenSuites, suitePathsAsStringList());
}
void SquishFileHandler::closeAllTestSuites()
{
closeAllInternal();
- ProjectExplorer::SessionManager::setValue(SK_OpenSuites, suitePathsAsStringList());
+ SessionManager::setValue(SK_OpenSuites, suitePathsAsStringList());
}
void SquishFileHandler::deleteTestCase(const QString &suiteName, const QString &testCaseName)
@@ -538,7 +541,7 @@ void SquishFileHandler::onSessionLoaded()
// remove currently opened "silently" (without storing into session)
closeAllInternal();
- const QVariant variant = ProjectExplorer::SessionManager::value(SK_OpenSuites);
+ const QVariant variant = SessionManager::value(SK_OpenSuites);
const Utils::FilePaths suitePaths = Utils::transform(variant.toStringList(),
&Utils::FilePath::fromString);
diff --git a/src/plugins/squish/squishnavigationwidget.cpp b/src/plugins/squish/squishnavigationwidget.cpp
index ce96501398..cc9ec76b31 100644
--- a/src/plugins/squish/squishnavigationwidget.cpp
+++ b/src/plugins/squish/squishnavigationwidget.cpp
@@ -185,7 +185,7 @@ void SquishNavigationWidget::contextMenuEvent(QContextMenuEvent *event)
QAction *closeAllSuites = new QAction(Tr::tr("Close All Test Suites"), &menu);
menu.addAction(closeAllSuites);
- connect(closeAllSuites, &QAction::triggered, this, [this] {
+ connect(closeAllSuites, &QAction::triggered, this, [] {
if (SquishMessages::simpleQuestion(Tr::tr("Close All Test Suites"),
Tr::tr("Close all test suites?"
/*"\nThis will close all related files as well."*/))
@@ -296,14 +296,14 @@ void SquishNavigationWidget::onRemoveAllSharedFolderTriggered()
void SquishNavigationWidget::onRecordTestCase(const QString &suiteName, const QString &testCase)
{
- QDialogButtonBox::StandardButton pressed = Utils::CheckableMessageBox::doNotAskAgainQuestion(
- Core::ICore::dialogParent(),
- Tr::tr("Record Test Case"),
- Tr::tr("Do you want to record over the test case \"%1\"? The existing content will "
- "be overwritten by the recorded script.").arg(testCase),
- Core::ICore::settings(),
- "RecordWithoutApproval");
- if (pressed != QDialogButtonBox::Yes)
+ QMessageBox::StandardButton pressed = Utils::CheckableMessageBox::question(
+ Core::ICore::dialogParent(),
+ Tr::tr("Record Test Case"),
+ Tr::tr("Do you want to record over the test case \"%1\"? The existing content will "
+ "be overwritten by the recorded script.")
+ .arg(testCase),
+ QString("RecordWithoutApproval"));
+ if (pressed != QMessageBox::Yes)
return;
SquishFileHandler::instance()->recordTestCase(suiteName, testCase);
@@ -314,7 +314,7 @@ void SquishNavigationWidget::onNewTestCaseTriggered(const QModelIndex &index)
auto settings = SquishPlugin::squishSettings();
QTC_ASSERT(settings, return);
- if (!settings->squishPath.filePath().pathAppended("scriptmodules").exists()) {
+ if (!settings->squishPath().pathAppended("scriptmodules").exists()) {
SquishMessages::criticalMessage(Tr::tr("Set up a valid Squish path to be able to create "
"a new test case.\n(Edit > Preferences > Squish)"));
return;
diff --git a/src/plugins/squish/squishoutputpane.cpp b/src/plugins/squish/squishoutputpane.cpp
index 2b2d90d165..8c8047f47f 100644
--- a/src/plugins/squish/squishoutputpane.cpp
+++ b/src/plugins/squish/squishoutputpane.cpp
@@ -19,6 +19,7 @@
#include <coreplugin/icontext.h>
#include <utils/itemviews.h>
+#include <utils/stylehelper.h>
#include <utils/theme/theme.h>
#include <utils/utilsicons.h>
@@ -305,17 +306,20 @@ void SquishOutputPane::clearOldResults()
void SquishOutputPane::createToolButtons()
{
m_expandAll = new QToolButton(m_treeView);
+ Utils::StyleHelper::setPanelWidget(m_expandAll);
m_expandAll->setIcon(Utils::Icons::EXPAND_TOOLBAR.icon());
m_expandAll->setToolTip(Tr::tr("Expand All"));
m_collapseAll = new QToolButton(m_treeView);
+ Utils::StyleHelper::setPanelWidget(m_collapseAll);
m_collapseAll->setIcon(Utils::Icons::COLLAPSE_TOOLBAR.icon());
m_collapseAll->setToolTip(Tr::tr("Collapse All"));
m_filterButton = new QToolButton(m_treeView);
+ Utils::StyleHelper::setPanelWidget(m_filterButton);
m_filterButton->setIcon(Utils::Icons::FILTER.icon());
m_filterButton->setToolTip(Tr::tr("Filter Test Results"));
- m_filterButton->setProperty("noArrow", true);
+ m_filterButton->setProperty(Utils::StyleHelper::C_NO_ARROW, true);
m_filterButton->setAutoRaise(true);
m_filterButton->setPopupMode(QToolButton::InstantPopup);
m_filterMenu = new QMenu(m_filterButton);
diff --git a/src/plugins/squish/squishperspective.cpp b/src/plugins/squish/squishperspective.cpp
index fca93ce35d..1b762c71a4 100644
--- a/src/plugins/squish/squishperspective.cpp
+++ b/src/plugins/squish/squishperspective.cpp
@@ -26,10 +26,13 @@
namespace Squish {
namespace Internal {
-enum class IconType { StopRecord, Play, Pause, StepIn, StepOver, StepReturn, Stop };
+enum class IconType { StopRecord, Play, Pause, StepIn, StepOver, StepReturn, Stop, Inspect };
static QIcon iconForType(IconType type)
{
+ static const Utils::Icon inspectIcon({{":/squish/images/picker.png",
+ Utils::Theme::IconsBaseColor}});
+
switch (type) {
case IconType::StopRecord:
return Debugger::Icons::RECORD_ON.icon();
@@ -45,6 +48,8 @@ static QIcon iconForType(IconType type)
return Debugger::Icons::STEP_OUT_TOOLBAR.icon();
case IconType::Stop:
return Utils::Icons::STOP_SMALL.icon();
+ case IconType::Inspect:
+ return inspectIcon.icon();
}
return QIcon();
}
@@ -91,6 +96,79 @@ QVariant LocalsItem::data(int column, int role) const
return TreeItem::data(column, role);
}
+QVariant InspectedObjectItem::data(int column, int role) const
+{
+ if (role == Qt::DisplayRole || role == Qt::ToolTipRole) {
+ switch (column) {
+ case 0: return value;
+ case 1: return type;
+ }
+ }
+ return TreeItem::data(column, role);
+}
+
+QVariant InspectedPropertyItem::data(int column, int role) const
+{
+ if (role ==Qt::DisplayRole || role == Qt::ToolTipRole) {
+ switch (column) {
+ case 0: return name;
+ case 1: return value;
+ }
+ }
+ return TreeItem::data(column, role);
+}
+
+void InspectedPropertyItem::parseAndUpdateChildren()
+{
+ const char open = '{';
+ const char close = '}';
+ const char delimiter =',';
+ const char eq = '=';
+
+ if (!value.startsWith(open) || !value.endsWith(close)) // only parse multi-property content
+ return;
+
+ int start = 1;
+ int end = value.size() - 1;
+ do {
+ int endOfName = value.indexOf(eq, start);
+ QTC_ASSERT(endOfName != -1, return);
+ int innerStart = endOfName + 2;
+ QTC_ASSERT(innerStart < end, return);
+ const QString name = value.mid(start, endOfName - start).trimmed();
+ if (value.at(innerStart) != open) {
+ int endOfItemValue = value.indexOf(delimiter, innerStart);
+ if (endOfItemValue == -1)
+ endOfItemValue = end;
+ const QString content = value.mid(innerStart, endOfItemValue - innerStart).trimmed();
+ appendChild(new InspectedPropertyItem(name, content));
+ start = endOfItemValue + 1;
+ } else {
+ int openedBraces = 1;
+ // advance until item's content is complete
+ int pos = innerStart;
+ do {
+ if (++pos > end)
+ break;
+ if (value.at(pos) == open) {
+ ++openedBraces;
+ continue;
+ }
+ if (value.at(pos) == close) {
+ --openedBraces;
+ if (openedBraces == 0)
+ break;
+ }
+ } while (pos < end);
+ ++pos;
+ QTC_ASSERT(pos < end, return);
+ const QString content = value.mid(innerStart, pos - innerStart).trimmed();
+ appendChild(new InspectedPropertyItem(name, content));
+ start = pos + 1;
+ }
+ } while (start < end);
+}
+
class SquishControlBar : public QDialog
{
public:
@@ -209,10 +287,14 @@ void SquishPerspective::initPerspective()
m_stepOutAction->setEnabled(false);
m_stopAction = Debugger::createStopAction();
m_stopAction->setEnabled(false);
+ m_inspectAction = new QAction(this);
+ m_inspectAction->setIcon(iconForType(IconType::Inspect));
+ m_inspectAction->setToolTip(Tr::tr("Inspect"));
+ m_inspectAction->setEnabled(false);
- QVBoxLayout *mainLayout = new QVBoxLayout;
- mainLayout->setContentsMargins(0, 0, 0, 0);
- mainLayout->setSpacing(1);
+ QVBoxLayout *localsMainLayout = new QVBoxLayout;
+ localsMainLayout->setContentsMargins(0, 0, 0, 0);
+ localsMainLayout->setSpacing(1);
m_localsModel.setHeader({Tr::tr("Name"), Tr::tr("Type"), Tr::tr("Value")});
auto localsView = new Utils::TreeView;
@@ -220,11 +302,45 @@ void SquishPerspective::initPerspective()
localsView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
localsView->setModel(&m_localsModel);
localsView->setRootIsDecorated(true);
- mainLayout->addWidget(localsView);
- QWidget *mainWidget = new QWidget;
- mainWidget->setObjectName("SquishLocalsView");
- mainWidget->setWindowTitle(Tr::tr("Squish Locals"));
- mainWidget->setLayout(mainLayout);
+ localsMainLayout->addWidget(localsView);
+ QWidget *localsWidget = new QWidget;
+ localsWidget->setObjectName("SquishLocalsView");
+ localsWidget->setWindowTitle(Tr::tr("Squish Locals"));
+ localsWidget->setLayout(localsMainLayout);
+
+ QVBoxLayout *objectsMainLayout = new QVBoxLayout;
+ objectsMainLayout->setContentsMargins(0, 0, 0, 0);
+ objectsMainLayout->setSpacing(1);
+
+ m_objectsModel.setHeader({Tr::tr("Object"), Tr::tr("Type")});
+ m_objectsView = new Utils::TreeView;
+ m_objectsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ m_objectsView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ m_objectsView->setModel(&m_objectsModel);
+ m_objectsView->setRootIsDecorated(true);
+ objectsMainLayout->addWidget(m_objectsView);
+
+ QWidget *objectWidget = new QWidget;
+ objectWidget->setObjectName("SquishObjectsView");
+ objectWidget->setWindowTitle(Tr::tr("Squish Objects"));
+ objectWidget->setLayout(objectsMainLayout);
+
+ QVBoxLayout *propertiesMainLayout = new QVBoxLayout;
+ propertiesMainLayout->setContentsMargins(0, 0, 0, 0);
+ propertiesMainLayout->setSpacing(1);
+
+ m_propertiesModel.setHeader({Tr::tr("Property"), Tr::tr("Value")});
+ auto propertiesView = new Utils::TreeView;
+ propertiesView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ propertiesView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ propertiesView->setModel(&m_propertiesModel);
+ propertiesView->setRootIsDecorated(true);
+ propertiesMainLayout->addWidget(propertiesView);
+
+ QWidget *propertiesWidget = new QWidget;
+ propertiesWidget->setObjectName("SquishPropertiesView");
+ propertiesWidget->setWindowTitle(Tr::tr("Squish Object Properties"));
+ propertiesWidget->setLayout(propertiesMainLayout);
addToolBarAction(m_pausePlayAction);
addToolBarAction(m_stepInAction);
@@ -232,10 +348,14 @@ void SquishPerspective::initPerspective()
addToolBarAction(m_stepOutAction);
addToolBarAction(m_stopAction);
addToolbarSeparator();
+ addToolBarAction(m_inspectAction);
+ addToolbarSeparator();
m_status = new QLabel;
addToolBarWidget(m_status);
- addWindow(mainWidget, Perspective::AddToTab, nullptr, true, Qt::RightDockWidgetArea);
+ addWindow(objectWidget, Perspective::SplitVertical, nullptr);
+ addWindow(propertiesWidget, Perspective::SplitHorizontal, objectWidget);
+ addWindow(localsWidget, Perspective::AddToTab, nullptr, true, Qt::RightDockWidgetArea);
connect(m_pausePlayAction, &QAction::triggered, this, &SquishPerspective::onPausePlayTriggered);
connect(m_stepInAction, &QAction::triggered, this, [this] {
@@ -250,6 +370,10 @@ void SquishPerspective::initPerspective()
connect(m_stopAction, &QAction::triggered, this, &SquishPerspective::onStopTriggered);
connect(m_stopRecordAction, &QAction::triggered,
this, &SquishPerspective::onStopRecordTriggered);
+ connect(m_inspectAction, &QAction::triggered, this, [this]{
+ m_inspectAction->setEnabled(false);
+ emit inspectTriggered();
+ });
connect(SquishTools::instance(), &SquishTools::localsUpdated,
this, &SquishPerspective::onLocalsUpdated);
@@ -262,6 +386,35 @@ void SquishPerspective::initPerspective()
SquishTools::instance()->requestExpansion(item->name);
}
});
+
+ connect(SquishTools::instance(), &SquishTools::objectPicked,
+ this, &SquishPerspective::onObjectPicked);
+ connect(SquishTools::instance(), &SquishTools::updateChildren,
+ this, &SquishPerspective::onUpdateChildren);
+ connect(SquishTools::instance(), &SquishTools::propertiesFetched,
+ this, &SquishPerspective::onPropertiesFetched);
+ connect(SquishTools::instance(), &SquishTools::autIdRetrieved,
+ this, [this]{
+ m_autIdKnown = true;
+ m_inspectAction->setEnabled(true);
+ });
+ connect(m_objectsView, &QTreeView::expanded, this, [this](const QModelIndex &idx) {
+ InspectedObjectItem *item = m_objectsModel.itemForIndex(idx);
+ if (QTC_GUARD(item)) {
+ if (item->expanded)
+ return;
+ item->expanded = true;
+ SquishTools::instance()->requestExpansionForObject(item->fullName);
+ }
+ });
+ connect(m_objectsView->selectionModel(), &QItemSelectionModel::currentChanged,
+ this, [this](const QModelIndex &current){
+ m_propertiesModel.clear();
+ InspectedObjectItem *item = m_objectsModel.itemForIndex(current);
+ if (!item)
+ return;
+ SquishTools::instance()->requestPropertiesForObject(item->fullName);
+ });
}
void SquishPerspective::onStopTriggered()
@@ -269,6 +422,8 @@ void SquishPerspective::onStopTriggered()
m_stopRecordAction->setEnabled(false);
m_pausePlayAction->setEnabled(false);
m_stopAction->setEnabled(false);
+ m_inspectAction->setEnabled(false);
+ m_autIdKnown = false;
emit stopRequested();
}
@@ -277,6 +432,8 @@ void SquishPerspective::onStopRecordTriggered()
m_stopRecordAction->setEnabled(false);
m_pausePlayAction->setEnabled(false);
m_stopAction->setEnabled(false);
+ m_inspectAction->setEnabled(false);
+ m_autIdKnown = false;
emit stopRecordRequested();
}
@@ -326,6 +483,60 @@ void SquishPerspective::onLocalsUpdated(const QString &output)
}
}
+void SquishPerspective::onObjectPicked(const QString &output)
+{
+ // "+{container=':o_QQuickView' text='2' type='Text' unnamed='1' visible='true'}\tQQuickText_QML_1"
+ static const QRegularExpression regex("^(?<exp>[-+])(?<content>\\{.*\\})\t(?<type>.+)$");
+ const QRegularExpressionMatch match = regex.match(output);
+ if (!match.hasMatch())
+ return;
+ const QString content = match.captured("content");
+ m_objectsModel.clear();
+ InspectedObjectItem *parent = m_objectsModel.rootItem();
+ InspectedObjectItem *obj = new InspectedObjectItem(content, match.captured("type"));
+ obj->fullName = content;
+ if (match.captured("exp") == "+")
+ obj->appendChild(new InspectedObjectItem); // add pseudo child
+ parent->appendChild(obj);
+ m_inspectAction->setEnabled(true);
+ const QModelIndex idx = m_objectsModel.indexForItem(obj);
+ if (idx.isValid())
+ m_objectsView->setCurrentIndex(idx);
+}
+
+void SquishPerspective::onUpdateChildren(const QString &name, const QStringList &children)
+{
+ InspectedObjectItem *item = m_objectsModel.findNonRootItem([name](InspectedObjectItem *it) {
+ return it->fullName == name;
+ });
+ if (!item)
+ return;
+
+ item->removeChildren(); // remove former dummy child
+ static const QRegularExpression regex("(?<exp>[-+])(?<symbolicName>.+)\t(?<type>.+)");
+ for (const QString &child : children) {
+ const QRegularExpressionMatch match = regex.match(child);
+ QTC_ASSERT(match.hasMatch(), continue);
+ const QString symbolicName = match.captured("symbolicName");
+ auto childItem = new InspectedObjectItem(symbolicName, match.captured("type"));
+ childItem->fullName = name + '.' + symbolicName;
+ childItem->appendChild(new InspectedObjectItem); // add dummy child
+ item->appendChild(childItem);
+ }
+}
+
+void SquishPerspective::onPropertiesFetched(const QStringList &properties)
+{
+ static const QRegularExpression regex("(?<name>.+)=(?<exp>[-+])(?<content>.*)");
+
+ for (const QString &line : properties) {
+ const QRegularExpressionMatch match = regex.match(line);
+ QTC_ASSERT(match.hasMatch(), continue);
+ auto item = new InspectedPropertyItem(match.captured("name"), match.captured("content"));
+ m_propertiesModel.rootItem()->appendChild(item);
+ }
+}
+
void SquishPerspective::updateStatus(const QString &status)
{
m_status->setText(status);
@@ -360,6 +571,12 @@ void SquishPerspective::destroyControlBar()
m_controlBar = nullptr;
}
+void SquishPerspective::resetAutId()
+{
+ m_autIdKnown = false;
+ m_inspectAction->setEnabled(false);
+}
+
void SquishPerspective::setPerspectiveMode(PerspectiveMode mode)
{
if (m_mode == mode) // ignore
@@ -376,6 +593,7 @@ void SquishPerspective::setPerspectiveMode(PerspectiveMode mode)
m_stepOverAction->setEnabled(false);
m_stepOutAction->setEnabled(false);
m_stopAction->setEnabled(true);
+ m_inspectAction->setEnabled(false);
break;
case Recording:
m_stopRecordAction->setEnabled(true);
@@ -386,6 +604,7 @@ void SquishPerspective::setPerspectiveMode(PerspectiveMode mode)
m_stepOverAction->setEnabled(false);
m_stepOutAction->setEnabled(false);
m_stopAction->setEnabled(true);
+ m_inspectAction->setEnabled(false);
break;
case Interrupted:
m_pausePlayAction->setEnabled(true);
@@ -395,6 +614,7 @@ void SquishPerspective::setPerspectiveMode(PerspectiveMode mode)
m_stepOverAction->setEnabled(true);
m_stepOutAction->setEnabled(true);
m_stopAction->setEnabled(true);
+ m_inspectAction->setEnabled(m_autIdKnown);
break;
case Configuring:
case Querying:
@@ -407,7 +627,10 @@ void SquishPerspective::setPerspectiveMode(PerspectiveMode mode)
m_stepOverAction->setEnabled(false);
m_stepOutAction->setEnabled(false);
m_stopAction->setEnabled(false);
+ m_inspectAction->setEnabled(false);
m_localsModel.clear();
+ m_objectsModel.clear();
+ m_propertiesModel.clear();
break;
default:
break;
diff --git a/src/plugins/squish/squishperspective.h b/src/plugins/squish/squishperspective.h
index 1e5c4aa515..b1a8929433 100644
--- a/src/plugins/squish/squishperspective.h
+++ b/src/plugins/squish/squishperspective.h
@@ -9,6 +9,8 @@
#include <utils/treemodel.h>
+namespace Utils { class TreeView; }
+
namespace Squish {
namespace Internal {
@@ -26,6 +28,32 @@ public:
bool expanded = false;
};
+class InspectedObjectItem : public Utils::TreeItem
+{
+public:
+ InspectedObjectItem() = default;
+ InspectedObjectItem(const QString &v, const QString &t) : value(v), type(t) {}
+ QVariant data(int column, int role) const override;
+ QString value;
+ QString type;
+ QString fullName; // FIXME this might be non-unique
+ bool expanded = false;
+};
+
+class InspectedPropertyItem : public Utils::TreeItem
+{
+public:
+ InspectedPropertyItem() = default;
+ InspectedPropertyItem(const QString &n, const QString &v)
+ : name(n), value(v) { parseAndUpdateChildren(); }
+ QVariant data(int column, int role) const override;
+ QString name;
+ QString value;
+ bool expanded = false;
+private:
+ void parseAndUpdateChildren();
+};
+
class SquishPerspective : public Utils::Perspective
{
Q_OBJECT
@@ -41,18 +69,23 @@ public:
void showControlBar(SquishXmlOutputHandler *xmlOutputHandler);
void destroyControlBar();
+ void resetAutId();
signals:
void stopRequested();
void stopRecordRequested();
void interruptRequested();
void runRequested(StepMode mode);
+ void inspectTriggered();
private:
void onStopTriggered();
void onStopRecordTriggered();
void onPausePlayTriggered();
void onLocalsUpdated(const QString &output);
+ void onObjectPicked(const QString &output);
+ void onUpdateChildren(const QString &name, const QStringList &children);
+ void onPropertiesFetched(const QStringList &properties);
QAction *m_stopRecordAction = nullptr;
QAction *m_pausePlayAction = nullptr;
@@ -60,10 +93,15 @@ private:
QAction *m_stepOverAction = nullptr;
QAction *m_stepOutAction = nullptr;
QAction *m_stopAction = nullptr;
+ QAction *m_inspectAction = nullptr;
QLabel *m_status = nullptr;
class SquishControlBar *m_controlBar = nullptr;
Utils::TreeModel<LocalsItem> m_localsModel;
+ Utils::TreeModel<InspectedObjectItem> m_objectsModel;
+ Utils::TreeModel<InspectedPropertyItem> m_propertiesModel;
+ Utils::TreeView *m_objectsView = nullptr;
PerspectiveMode m_mode = NoMode;
+ bool m_autIdKnown = false;
friend class SquishControlBar;
};
diff --git a/src/plugins/squish/squishplugin.cpp b/src/plugins/squish/squishplugin.cpp
index f72f70df78..caeb787de6 100644
--- a/src/plugins/squish/squishplugin.cpp
+++ b/src/plugins/squish/squishplugin.cpp
@@ -43,7 +43,6 @@ public:
bool initializeGlobalScripts();
SquishSettings m_squishSettings;
- SquishSettingsPage m_settingsPage{&m_squishSettings};
SquishTestTreeModel m_treeModel;
SquishNavigationWidgetFactory m_navigationWidgetFactory;
ObjectsMapEditorFactory m_objectsMapEditorFactory;
@@ -57,7 +56,6 @@ SquishPluginPrivate::SquishPluginPrivate()
{
qRegisterMetaType<SquishResultItem*>("SquishResultItem*");
- m_squishSettings.readSettings(ICore::settings());
m_outputPane = SquishOutputPane::instance();
m_squishTools = new SquishTools;
initializeMenuEntries();
@@ -109,7 +107,7 @@ bool SquishPluginPrivate::initializeGlobalScripts()
QTC_ASSERT(dd->m_squishTools, return false);
SquishFileHandler::instance()->setSharedFolders({});
- const Utils::FilePath squishserver = dd->m_squishSettings.squishPath.filePath().pathAppended(
+ const Utils::FilePath squishserver = dd->m_squishSettings.squishPath().pathAppended(
Utils::HostOsInfo::withExecutableSuffix("bin/squishserver"));
if (!squishserver.isExecutableFile())
return false;
@@ -134,8 +132,7 @@ void SquishPlugin::initialize()
bool SquishPlugin::delayedInitialize()
{
-
- connect(&dd->m_squishSettings, &SquishSettings::squishPathChanged,
+ connect(&dd->m_squishSettings.squishPath, &Utils::BaseAspect::changed,
dd, &SquishPluginPrivate::initializeGlobalScripts);
return dd->initializeGlobalScripts();
diff --git a/src/plugins/squish/squishprocessbase.cpp b/src/plugins/squish/squishprocessbase.cpp
index 23d3a49aa7..744ea506c1 100644
--- a/src/plugins/squish/squishprocessbase.cpp
+++ b/src/plugins/squish/squishprocessbase.cpp
@@ -8,9 +8,9 @@ namespace Squish::Internal {
SquishProcessBase::SquishProcessBase(QObject *parent)
: QObject(parent)
{
- connect(&m_process, &Utils::QtcProcess::readyReadStandardError,
+ connect(&m_process, &Utils::Process::readyReadStandardError,
this, &SquishProcessBase::onErrorOutput);
- connect(&m_process, &Utils::QtcProcess::done,
+ connect(&m_process, &Utils::Process::done,
this, &SquishProcessBase::onDone);
}
diff --git a/src/plugins/squish/squishprocessbase.h b/src/plugins/squish/squishprocessbase.h
index 088eca8a73..6582c95062 100644
--- a/src/plugins/squish/squishprocessbase.h
+++ b/src/plugins/squish/squishprocessbase.h
@@ -5,7 +5,7 @@
#include "squishconstants.h"
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QObject>
@@ -36,7 +36,7 @@ protected:
virtual void onDone() {}
virtual void onErrorOutput() {}
- Utils::QtcProcess m_process;
+ Utils::Process m_process;
private:
SquishProcessState m_state = Idle;
diff --git a/src/plugins/squish/squishrunnerprocess.cpp b/src/plugins/squish/squishrunnerprocess.cpp
index 3fa6474e26..ca1e330a7c 100644
--- a/src/plugins/squish/squishrunnerprocess.cpp
+++ b/src/plugins/squish/squishrunnerprocess.cpp
@@ -6,6 +6,7 @@
#include "squishtr.h"
#include <debugger/breakhandler.h>
+#include <utils/executeondestruction.h>
#include <QLoggingCategory>
@@ -13,6 +14,14 @@ Q_LOGGING_CATEGORY(runnerLOG, "qtc.squish.squishrunner", QtWarningMsg)
namespace Squish::Internal {
+static QString maskedArgument(const QString &originalArg)
+{
+ QString masked = originalArg;
+ masked.replace('\\', "\\\\");
+ masked.replace(' ', "\\x20");
+ return masked;
+}
+
SquishRunnerProcess::SquishRunnerProcess(QObject *parent)
: SquishProcessBase{parent}
{
@@ -36,6 +45,10 @@ void SquishRunnerProcess::setupProcess(RunnerMode mode)
case Record:
m_process.setProcessMode(Utils::ProcessMode::Writer);
break;
+ case Inspect:
+ m_process.setProcessMode(Utils::ProcessMode::Writer);
+ m_process.setStdOutLineCallback([this](const QString &line) { onInspectorOutput(line); });
+ break;
}
}
@@ -44,6 +57,8 @@ void SquishRunnerProcess::start(const Utils::CommandLine &cmdline, const Utils::
QTC_ASSERT(m_process.state() == QProcess::NotRunning, return);
m_licenseIssues = false;
m_autId = 0;
+ m_outputMode = SingleLine;
+ m_multiLineContent.clear();
SquishProcessBase::start(cmdline, env);
}
@@ -130,11 +145,75 @@ void SquishRunnerProcess::onStdOutput(const QString &lineIn)
isPrompt = true;
m_autId = line.mid(7).toInt();
qCInfo(runnerLOG) << "AUT ID set" << m_autId << "(" << line << ")";
+ emit autIdRetrieved();
}
if (isPrompt)
emit interrupted(fileName, fileLine, fileColumn);
}
+void SquishRunnerProcess::handleMultiLineOutput(OutputMode mode)
+{
+ Utils::ExecuteOnDestruction atExit([this]{
+ m_multiLineContent.clear();
+ m_context.clear();
+ });
+
+ if (mode == MultiLineProperties) {
+ emit propertiesFetched(m_multiLineContent);
+ } else if (mode == MultiLineChildren) {
+ emit updateChildren(m_context, m_multiLineContent);
+ }
+}
+
+void SquishRunnerProcess::onInspectorOutput(const QString &lineIn)
+{
+ QString line = lineIn;
+ line.chop(1); // line has a newline
+ if (line.startsWith("SSPY:"))
+ line = line.mid(5);
+ if (line.isEmpty()) // we have a prompt, that's fine
+ return;
+
+ if (m_outputMode != SingleLine) {
+ const OutputMode originalMode = m_outputMode;
+ if (line.startsWith("@end")) {
+ m_outputMode = SingleLine;
+ if (!QTC_GUARD(line.mid(6).chopped(1) == m_context)) { // messed up output
+ m_multiLineContent.clear();
+ m_context.clear();
+ return;
+ }
+ } else {
+ m_multiLineContent.append(line);
+ }
+ if (m_outputMode == SingleLine) // we reached the @end
+ handleMultiLineOutput(originalMode);
+ return;
+ }
+ if (line == "@ready")
+ return;
+ if (line.startsWith("@picked: ")) {
+ const QString value = line.mid(9);
+ emit objectPicked(value);
+ return;
+ }
+ if (line.startsWith("@startprop")) {
+ m_outputMode = MultiLineProperties;
+ m_context = line.mid(12).chopped(1);
+ return;
+ }
+ if (line.startsWith("@startobj")) {
+ m_outputMode = MultiLineChildren;
+ m_context = line.mid(11).chopped(1);
+ return;
+ }
+ if (line.contains("license acquisition")) {
+ emit logOutputReceived("Inspect: " + line);
+ return;
+ }
+// qDebug() << "unhandled" << line;
+}
+
static QString cmdToString(SquishRunnerProcess::RunnerCommand cmd)
{
switch (cmd) {
@@ -142,6 +221,7 @@ static QString cmdToString(SquishRunnerProcess::RunnerCommand cmd)
case SquishRunnerProcess::EndRecord: return "endrecord\n";
case SquishRunnerProcess::Exit: return "exit\n";
case SquishRunnerProcess::Next: return "next\n";
+ case SquishRunnerProcess::Pick: return "pick\n";
case SquishRunnerProcess::PrintVariables: return "print variables\n";
case SquishRunnerProcess::Return: return "return\n";
case SquishRunnerProcess::Step: return "step\n";
@@ -161,6 +241,16 @@ void SquishRunnerProcess::requestExpanded(const QString &variableName)
m_process.write("print variables +" + variableName + "\n");
}
+void SquishRunnerProcess::requestListObject(const QString &value)
+{
+ m_process.write("list objects " + maskedArgument(value) + "\n");
+}
+
+void SquishRunnerProcess::requestListProperties(const QString &value)
+{
+ m_process.write("list properties " + maskedArgument(value) + "\n");
+}
+
// FIXME: add/removal of breakpoints while debugging not handled yet
// FIXME: enabled state of breakpoints
Utils::Links SquishRunnerProcess::setBreakpoints(const QString &scriptExtension)
@@ -180,8 +270,7 @@ Utils::Links SquishRunnerProcess::setBreakpoints(const QString &scriptExtension)
continue;
// mask backslashes and spaces
- fileName.replace('\\', "\\\\");
- fileName.replace(' ', "\\x20");
+ fileName = maskedArgument(fileName);
auto line = gb->data(BreakpointLineColumn, Qt::DisplayRole).toInt();
QString cmd = "break ";
cmd.append(fileName);
diff --git a/src/plugins/squish/squishrunnerprocess.h b/src/plugins/squish/squishrunnerprocess.h
index b1c98220c1..2d2ae4efd3 100644
--- a/src/plugins/squish/squishrunnerprocess.h
+++ b/src/plugins/squish/squishrunnerprocess.h
@@ -15,8 +15,8 @@ class SquishRunnerProcess : public SquishProcessBase
{
Q_OBJECT
public:
- enum RunnerCommand { Continue, EndRecord, Exit, Next, PrintVariables, Return, Step };
- enum RunnerMode { Run, StartAut, QueryServer, Record };
+ enum RunnerCommand { Continue, EndRecord, Exit, Next, Pick, PrintVariables, Return, Step };
+ enum RunnerMode { Run, StartAut, QueryServer, Record, Inspect };
enum RunnerError { InvalidSocket, MappedAutMissing };
explicit SquishRunnerProcess(QObject *parent = nullptr);
@@ -33,6 +33,8 @@ public:
void writeCommand(RunnerCommand cmd);
void requestExpanded(const QString &variableName);
+ void requestListObject(const QString &value);
+ void requestListProperties(const QString &value);
Utils::Links setBreakpoints(const QString &scriptExtension);
bool lastRunHadLicenseIssues() const { return m_licenseIssues; }
@@ -43,16 +45,27 @@ signals:
void runnerFinished();
void interrupted(const QString &fileName, int line, int column);
void localsUpdated(const QString &output);
+ void propertiesFetched(const QStringList &properties);
+ void objectPicked(const QString &output);
+ void updateChildren(const QString &name, const QStringList &children);
void runnerError(RunnerError error);
+ void autIdRetrieved();
protected:
void onDone() override;
void onErrorOutput() override;
private:
+ enum OutputMode { SingleLine, MultiLineChildren, MultiLineProperties };
+
void onStdOutput(const QString &line);
+ void handleMultiLineOutput(OutputMode mode);
+ void onInspectorOutput(const QString &line);
Utils::FilePath m_currentTestCasePath;
+ QStringList m_multiLineContent;
+ QString m_context;
+ OutputMode m_outputMode = SingleLine;
int m_autId = 0;
bool m_licenseIssues = false;
std::optional<RunnerMode> m_mode;
diff --git a/src/plugins/squish/squishserverprocess.cpp b/src/plugins/squish/squishserverprocess.cpp
index 4d7bb2ef97..9e4b974143 100644
--- a/src/plugins/squish/squishserverprocess.cpp
+++ b/src/plugins/squish/squishserverprocess.cpp
@@ -10,7 +10,7 @@ namespace Squish::Internal {
SquishServerProcess::SquishServerProcess(QObject *parent)
: SquishProcessBase(parent)
{
- connect(&m_process, &Utils::QtcProcess::readyReadStandardOutput,
+ connect(&m_process, &Utils::Process::readyReadStandardOutput,
this, &SquishServerProcess::onStandardOutput);
}
@@ -25,7 +25,7 @@ void SquishServerProcess::start(const Utils::CommandLine &commandLine,
void SquishServerProcess::stop()
{
if (m_process.state() != QProcess::NotRunning && m_serverPort > 0) {
- Utils::QtcProcess serverKiller;
+ Utils::Process serverKiller;
QStringList args;
args << "--stop" << "--port" << QString::number(m_serverPort);
serverKiller.setCommand({m_process.commandLine().executable(), args});
diff --git a/src/plugins/squish/squishsettings.cpp b/src/plugins/squish/squishsettings.cpp
index 144e304608..f7758a844d 100644
--- a/src/plugins/squish/squishsettings.cpp
+++ b/src/plugins/squish/squishsettings.cpp
@@ -33,13 +33,17 @@ namespace Internal {
SquishSettings::SquishSettings()
{
+ setId("A.Squish.General");
+ setDisplayName(Tr::tr("General"));
+ setCategory(Constants::SQUISH_SETTINGS_CATEGORY);
+ setDisplayCategory("Squish");
+ setCategoryIcon(Icon({{":/squish/images/settingscategory_squish.png",
+ Theme::PanelTextColorDark}}, Icon::Tint));
setSettingsGroup("Squish");
setAutoApply(false);
- registerAspect(&squishPath);
squishPath.setSettingsKey("SquishPath");
squishPath.setLabelText(Tr::tr("Squish path:"));
- squishPath.setDisplayStyle(StringAspect::PathChooserDisplay);
squishPath.setExpectedKind(PathChooser::ExistingDirectory);
squishPath.setPlaceHolderText(Tr::tr("Path to Squish installation"));
squishPath.setValidationFunction([this](FancyLineEdit *edit, QString *error) {
@@ -54,37 +58,30 @@ SquishSettings::SquishSettings()
return valid;
});
- registerAspect(&licensePath);
licensePath.setSettingsKey("LicensePath");
licensePath.setLabelText(Tr::tr("License path:"));
- licensePath.setDisplayStyle(StringAspect::PathChooserDisplay);
licensePath.setExpectedKind(PathChooser::ExistingDirectory);
- registerAspect(&local);
local.setSettingsKey("Local");
local.setLabel(Tr::tr("Local Server"));
local.setDefaultValue(true);
- registerAspect(&serverHost);
serverHost.setSettingsKey("ServerHost");
serverHost.setLabelText(Tr::tr("Server host:"));
serverHost.setDisplayStyle(StringAspect::LineEditDisplay);
serverHost.setDefaultValue("localhost");
serverHost.setEnabled(false);
- registerAspect(&serverPort);
serverPort.setSettingsKey("ServerPort");
serverPort.setLabel(Tr::tr("Server Port"));
serverPort.setRange(1, 65535);
serverPort.setDefaultValue(9999);
serverPort.setEnabled(false);
- registerAspect(&verbose);
verbose.setSettingsKey("Verbose");
verbose.setLabel(Tr::tr("Verbose log"));
verbose.setDefaultValue(false);
- registerAspect(&minimizeIDE);
minimizeIDE.setSettingsKey("MinimizeIDE");
minimizeIDE.setLabel(Tr::tr("Minimize IDE"));
minimizeIDE.setToolTip(Tr::tr("Minimize IDE automatically while running or recording test cases."));
@@ -94,13 +91,24 @@ SquishSettings::SquishSettings()
serverHost.setEnabled(!checked);
serverPort.setEnabled(!checked);
});
- connect(&squishPath, &Utils::StringAspect::valueChanged,
- this, &SquishSettings::squishPathChanged);
+
+ setLayouter([this] {
+ using namespace Layouting;
+ return Form {
+ squishPath, br,
+ licensePath, br,
+ local, serverHost, serverPort, br,
+ verbose, br,
+ minimizeIDE, br,
+ };
+ });
+
+ readSettings();
}
Utils::FilePath SquishSettings::scriptsPath(Language language) const
{
- Utils::FilePath scripts = squishPath.filePath().pathAppended("scriptmodules");
+ Utils::FilePath scripts = squishPath().pathAppended("scriptmodules");
switch (language) {
case Language::Python: scripts = scripts.pathAppended("python"); break;
case Language::Perl: scripts = scripts.pathAppended("perl"); break;
@@ -112,35 +120,8 @@ Utils::FilePath SquishSettings::scriptsPath(Language language) const
return scripts.isReadableDir() ? scripts : Utils::FilePath();
}
-SquishSettingsPage::SquishSettingsPage(SquishSettings *settings)
-{
- setId("A.Squish.General");
- setDisplayName(Tr::tr("General"));
- setCategory(Constants::SQUISH_SETTINGS_CATEGORY);
- setDisplayCategory("Squish");
- setCategoryIcon(Icon({{":/squish/images/settingscategory_squish.png",
- Theme::PanelTextColorDark}}, Icon::Tint));
-
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- SquishSettings &s = *settings;
- using namespace Layouting;
-
- Grid grid {
- s.squishPath, br,
- s.licensePath, br,
- Span {2, Row { s.local, s.serverHost, s.serverPort } }, br,
- s.verbose, br,
- s.minimizeIDE, br,
- };
- Column { Row { grid }, st }.attachTo(widget);
- });
-}
-
SquishServerSettings::SquishServerSettings()
{
- registerAspect(&autTimeout);
autTimeout.setLabel(Tr::tr("Maximum startup time:"));
autTimeout.setToolTip(Tr::tr("Specifies how many seconds Squish should wait for a reply from the "
"AUT directly after starting it."));
@@ -148,7 +129,6 @@ SquishServerSettings::SquishServerSettings()
autTimeout.setSuffix("s");
autTimeout.setDefaultValue(20);
- registerAspect(&responseTimeout);
responseTimeout.setLabel(Tr::tr("Maximum response time:"));
responseTimeout.setToolTip(Tr::tr("Specifies how many seconds Squish should wait for a reply from "
"the hooked up AUT before raising a timeout error."));
@@ -156,7 +136,6 @@ SquishServerSettings::SquishServerSettings()
responseTimeout.setDefaultValue(300);
responseTimeout.setSuffix("s");
- registerAspect(&postMortemWaitTime);
postMortemWaitTime.setLabel(Tr::tr("Maximum post-mortem wait time:"));
postMortemWaitTime.setToolTip(Tr::tr("Specifies how many seconds Squish should wait after the the "
"first AUT process has exited."));
@@ -164,7 +143,6 @@ SquishServerSettings::SquishServerSettings()
postMortemWaitTime.setDefaultValue(1500);
postMortemWaitTime.setSuffix("ms");
- registerAspect(&animatedCursor);
animatedCursor.setLabel(Tr::tr("Animate mouse cursor:"));
animatedCursor.setDefaultValue(true);
}
@@ -251,10 +229,10 @@ void SquishServerSettings::setFromXmlOutput(const QString &output)
autPaths = newSettings.autPaths;
attachableAuts = newSettings.attachableAuts;
licensedToolkits = newSettings.licensedToolkits;
- autTimeout.setValue(newSettings.autTimeout.value());
- postMortemWaitTime.setValue(newSettings.postMortemWaitTime.value());
- responseTimeout.setValue(newSettings.responseTimeout.value());
- animatedCursor.setValue(newSettings.animatedCursor.value());
+ autTimeout.setValue(newSettings.autTimeout());
+ postMortemWaitTime.setValue(newSettings.postMortemWaitTime());
+ responseTimeout.setValue(newSettings.responseTimeout());
+ animatedCursor.setValue(newSettings.animatedCursor());
}
class SquishServerItem : public TreeItem
@@ -386,10 +364,10 @@ SquishServerSettingsWidget::SquishServerSettingsWidget(QWidget *parent)
using namespace Layouting;
Form grid {
&m_applicationsView, br,
- &m_serverSettings.autTimeout,
- &m_serverSettings.responseTimeout,
- &m_serverSettings.postMortemWaitTime,
- &m_serverSettings.animatedCursor,
+ &m_serverSettings.autTimeout, br,
+ &m_serverSettings.responseTimeout, br,
+ &m_serverSettings.postMortemWaitTime, br,
+ &m_serverSettings.animatedCursor, br,
};
Column buttonCol {
add,
@@ -626,14 +604,14 @@ QList<QStringList> SquishServerSettingsWidget::toConfigChangeArguments() const
result.append({"addAppPath", path});
}
- if (m_originalSettings.autTimeout.value() != m_serverSettings.autTimeout.value())
- result.append({"setAUTTimeout", QString::number(m_serverSettings.autTimeout.value())});
- if (m_originalSettings.responseTimeout.value() != m_serverSettings.responseTimeout.value())
- result.append({"setResponseTimeout", QString::number(m_serverSettings.responseTimeout.value())});
- if (m_originalSettings.postMortemWaitTime.value() != m_serverSettings.postMortemWaitTime.value())
- result.append({"setAUTPostMortemTimeout", QString::number(m_serverSettings.postMortemWaitTime.value())});
- if (m_originalSettings.animatedCursor.value() != m_serverSettings.animatedCursor.value())
- result.append({"setCursorAnimation", m_serverSettings.animatedCursor.value() ? QString("on") : QString("off")});
+ if (m_originalSettings.autTimeout() != m_serverSettings.autTimeout())
+ result.append({"setAUTTimeout", QString::number(m_serverSettings.autTimeout())});
+ if (m_originalSettings.responseTimeout() != m_serverSettings.responseTimeout())
+ result.append({"setResponseTimeout", QString::number(m_serverSettings.responseTimeout())});
+ if (m_originalSettings.postMortemWaitTime() != m_serverSettings.postMortemWaitTime())
+ result.append({"setAUTPostMortemTimeout", QString::number(m_serverSettings.postMortemWaitTime())});
+ if (m_originalSettings.animatedCursor() != m_serverSettings.animatedCursor())
+ result.append({"setCursorAnimation", m_serverSettings.animatedCursor() ? QString("on") : QString("off")});
return result;
}
diff --git a/src/plugins/squish/squishsettings.h b/src/plugins/squish/squishsettings.h
index 23380521e6..0e25a4215e 100644
--- a/src/plugins/squish/squishsettings.h
+++ b/src/plugins/squish/squishsettings.h
@@ -5,17 +5,10 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
-
#include <QDialog>
#include <QProcess>
-QT_BEGIN_NAMESPACE
-class QSettings;
-QT_END_NAMESPACE
-
-namespace Squish {
-namespace Internal {
+namespace Squish::Internal {
enum class Language;
@@ -30,36 +23,26 @@ public:
QMap<QString, QString> attachableAuts; // name, host:port
QStringList autPaths; // absolute path
QStringList licensedToolkits;
- Utils::IntegerAspect autTimeout;
- Utils::IntegerAspect responseTimeout;
- Utils::IntegerAspect postMortemWaitTime;
- Utils::BoolAspect animatedCursor;
+ Utils::IntegerAspect autTimeout{this};
+ Utils::IntegerAspect responseTimeout{this};
+ Utils::IntegerAspect postMortemWaitTime{this};
+ Utils::BoolAspect animatedCursor{this};
};
-class SquishSettings : public Utils::AspectContainer
+class SquishSettings : public Core::PagedSettings
{
- Q_OBJECT
public:
SquishSettings();
Utils::FilePath scriptsPath(Language language) const;
- Utils::StringAspect squishPath;
- Utils::StringAspect licensePath;
- Utils::StringAspect serverHost;
- Utils::IntegerAspect serverPort;
- Utils::BoolAspect local;
- Utils::BoolAspect verbose;
- Utils::BoolAspect minimizeIDE;
-
-signals:
- void squishPathChanged();
-};
-
-class SquishSettingsPage final : public Core::IOptionsPage
-{
-public:
- SquishSettingsPage(SquishSettings *settings);
+ Utils::FilePathAspect squishPath{this};
+ Utils::FilePathAspect licensePath{this};
+ Utils::StringAspect serverHost{this};
+ Utils::IntegerAspect serverPort{this};
+ Utils::BoolAspect local{this};
+ Utils::BoolAspect verbose{this};
+ Utils::BoolAspect minimizeIDE{this};
};
class SquishServerSettingsDialog : public QDialog
@@ -71,5 +54,4 @@ private:
void configWriteFailed(QProcess::ProcessError error);
};
-} // namespace Internal
-} // namespace Squish
+} // Squish::Internal
diff --git a/src/plugins/squish/squishtesttreeview.cpp b/src/plugins/squish/squishtesttreeview.cpp
index 65e4c15dd9..d01ab0440f 100644
--- a/src/plugins/squish/squishtesttreeview.cpp
+++ b/src/plugins/squish/squishtesttreeview.cpp
@@ -166,7 +166,6 @@ static bool copyScriptTemplates(const SuiteConf &suiteConf, const Utils::FilePat
const SquishSettings *s = SquishPlugin::squishSettings();
QTC_ASSERT(s, return false);
// copy template files
- const Utils::FilePath squishPath = s->squishPath.filePath();
bool ok = destination.ensureWritableDir();
QTC_ASSERT(ok, return false);
diff --git a/src/plugins/squish/squishtools.cpp b/src/plugins/squish/squishtools.cpp
index bc09e2fa6c..2145095306 100644
--- a/src/plugins/squish/squishtools.cpp
+++ b/src/plugins/squish/squishtools.cpp
@@ -133,6 +133,8 @@ SquishTools::SquishTools(QObject *parent)
this, &SquishTools::stopRecorder);
connect(&m_perspective, &SquishPerspective::runRequested,
this, &SquishTools::onRunnerRunRequested);
+ connect(&m_perspective, &SquishPerspective::inspectTriggered,
+ this, &SquishTools::onInspectTriggered);
}
SquishTools::~SquishTools()
@@ -167,7 +169,7 @@ struct SquishToolsSettings
{
const SquishSettings *squishSettings = SquishPlugin::squishSettings();
QTC_ASSERT(squishSettings, return);
- squishPath = squishSettings->squishPath.filePath();
+ squishPath = squishSettings->squishPath();
if (!squishPath.isEmpty()) {
const FilePath squishBin(squishPath.pathAppended("bin"));
@@ -179,12 +181,12 @@ struct SquishToolsSettings
HostOsInfo::withExecutableSuffix("processcomm")).absoluteFilePath();
}
- isLocalServer = squishSettings->local.value();
- serverHost = squishSettings->serverHost.value();
- serverPort = squishSettings->serverPort.value();
- verboseLog = squishSettings->verbose.value();
- licenseKeyPath = squishSettings->licensePath.filePath();
- minimizeIDE = squishSettings->minimizeIDE.value();
+ isLocalServer = squishSettings->local();
+ serverHost = squishSettings->serverHost();
+ serverPort = squishSettings->serverPort();
+ verboseLog = squishSettings->verbose();
+ licenseKeyPath = squishSettings->licensePath();
+ minimizeIDE = squishSettings->minimizeIDE();
}
};
@@ -440,6 +442,7 @@ void SquishTools::onRunnerStopped()
m_request = ServerStopRequested;
qCInfo(LOG) << "Stopping server from RunnerStopped (query)";
stopSquishServer();
+ return;
} else if (m_request == RecordTestRequested) {
if (m_secondaryRunner && m_secondaryRunner->isRunning()) {
stopRecorder();
@@ -448,7 +451,12 @@ void SquishTools::onRunnerStopped()
qCInfo(LOG) << "Stopping server from RunnerStopped (startaut)";
stopSquishServer();
}
- } else if (m_testCases.isEmpty() || (m_squishRunnerState == RunnerState::Canceled)) {
+ return;
+ }
+ // below only normal run of test case(s)
+ exitAndResetSecondaryRunner();
+
+ if (m_testCases.isEmpty() || (m_squishRunnerState == RunnerState::Canceled)) {
m_request = ServerStopRequested;
qCInfo(LOG) << "Stopping server from RunnerStopped";
stopSquishServer();
@@ -613,6 +621,52 @@ void SquishTools::setupAndStartRecorder()
m_secondaryRunner->start(cmd, squishEnvironment());
}
+void SquishTools::setupAndStartInspector()
+{
+ QTC_ASSERT(m_primaryRunner && m_primaryRunner->autId() != 0, return);
+ QTC_ASSERT(!m_secondaryRunner, return);
+
+ QStringList args;
+ if (!toolsSettings.isLocalServer)
+ args << "--host" << toolsSettings.serverHost;
+ args << "--port" << QString::number(m_serverProcess.port());
+ args << "--debugLog" << "alpw"; // TODO make this configurable?
+ args << "--inspect";
+ args << "--suitedir" << m_suitePath.toUserOutput();
+ args << "--autid" << QString::number(m_primaryRunner->autId());
+
+ m_secondaryRunner = new SquishRunnerProcess(this);
+ m_secondaryRunner->setupProcess(SquishRunnerProcess::Inspect);
+ const CommandLine cmd = {toolsSettings.runnerPath, args};
+ connect(m_secondaryRunner, &SquishRunnerProcess::logOutputReceived,
+ this, &SquishTools::logOutputReceived);
+ connect(m_secondaryRunner, &SquishRunnerProcess::objectPicked,
+ this, &SquishTools::objectPicked);
+ connect(m_secondaryRunner, &SquishRunnerProcess::updateChildren,
+ this, &SquishTools::updateChildren);
+ connect(m_secondaryRunner, &SquishRunnerProcess::propertiesFetched,
+ this, &SquishTools::propertiesFetched);
+ qCDebug(LOG) << "Inspector starting:" << cmd.toUserOutput();
+ m_secondaryRunner->start(cmd, squishEnvironment());
+}
+
+void SquishTools::exitAndResetSecondaryRunner()
+{
+ m_perspective.resetAutId();
+ if (m_secondaryRunner) {
+ m_secondaryRunner->writeCommand(SquishRunnerProcess::Exit);
+ m_secondaryRunner->deleteLater();
+ m_secondaryRunner = nullptr;
+ }
+}
+
+void SquishTools::onInspectTriggered()
+{
+ QTC_ASSERT(m_primaryRunner, return);
+ QTC_ASSERT(m_secondaryRunner, return);
+ m_secondaryRunner->writeCommand(SquishRunnerProcess::Pick);
+}
+
void SquishTools::stopRecorder()
{
QTC_ASSERT(m_secondaryRunner && m_secondaryRunner->isRunning(), return);
@@ -865,6 +919,7 @@ void SquishTools::handlePrompt(const QString &fileName, int line, int column)
case RunnerState::CancelRequested:
case RunnerState::CancelRequestedWhileInterrupted:
logAndChangeRunnerState(RunnerState::Canceled);
+ exitAndResetSecondaryRunner();
m_primaryRunner->writeCommand(SquishRunnerProcess::Exit);
clearLocationMarker();
break;
@@ -885,6 +940,9 @@ void SquishTools::handlePrompt(const QString &fileName, int line, int column)
const FilePath filePath = FilePath::fromUserInput(fileName);
Core::EditorManager::openEditorAt({filePath, line, column});
updateLocationMarker(filePath, line);
+ // looks like we need to start inspector while being interrupted?
+ if (!m_secondaryRunner && m_primaryRunner->autId() !=0)
+ setupAndStartInspector();
}
} else { // it's just some output coming from the server
if (m_squishRunnerState == RunnerState::Interrupted && !m_requestVarsTimer) {
@@ -909,6 +967,24 @@ void SquishTools::requestExpansion(const QString &name)
m_primaryRunner->requestExpanded(name);
}
+void SquishTools::requestExpansionForObject(const QString &value)
+{
+ QTC_ASSERT(m_primaryRunner, return);
+ if (m_squishRunnerState != RunnerState::Interrupted)
+ return;
+ QTC_ASSERT(m_secondaryRunner, return);
+ m_secondaryRunner->requestListObject(value);
+}
+
+void SquishTools::requestPropertiesForObject(const QString &value)
+{
+ QTC_ASSERT(m_primaryRunner, return);
+ if (m_squishRunnerState != RunnerState::Interrupted)
+ return;
+ QTC_ASSERT(m_secondaryRunner, return);
+ m_secondaryRunner->requestListProperties(value);
+}
+
bool SquishTools::shutdown()
{
QTC_ASSERT(!m_shutdownInitiated, return true);
@@ -1040,7 +1116,7 @@ void SquishTools::interruptRunner()
QTC_ASSERT(m_primaryRunner, return);
qint64 processId = m_primaryRunner->processId();
const CommandLine cmd(toolsSettings.processComPath, {QString::number(processId), "break"});
- QtcProcess process;
+ Process process;
process.setCommand(cmd);
process.start();
process.waitForFinished();
@@ -1056,7 +1132,7 @@ void SquishTools::terminateRunner()
QTC_ASSERT(m_primaryRunner, return);
qint64 processId = m_primaryRunner->processId();
const CommandLine cmd(toolsSettings.processComPath, {QString::number(processId), "terminate"});
- QtcProcess process;
+ Process process;
process.setCommand(cmd);
process.start();
process.waitForFinished();
@@ -1198,10 +1274,12 @@ bool SquishTools::setupRunnerPath()
void SquishTools::setupAndStartSquishRunnerProcess(const Utils::CommandLine &cmdLine)
{
QTC_ASSERT(m_primaryRunner, return);
- // avoid crashes on fast re-usage of QtcProcess
+ // avoid crashes on fast re-usage of Process
m_primaryRunner->closeProcess();
if (m_request == RunTestRequested) {
+ connect(m_primaryRunner, &SquishRunnerProcess::autIdRetrieved,
+ this, &SquishTools::autIdRetrieved);
// set up the file system watcher for being able to read the results.xml file
m_resultsFileWatcher = new QFileSystemWatcher;
// on 2nd run this directory exists and won't emit changes, so use the current subdirectory
diff --git a/src/plugins/squish/squishtools.h b/src/plugins/squish/squishtools.h
index 19b57a7cb4..099850f5a7 100644
--- a/src/plugins/squish/squishtools.h
+++ b/src/plugins/squish/squishtools.h
@@ -9,7 +9,7 @@
#include "suiteconf.h"
#include <utils/environment.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QObject>
#include <QStringList>
@@ -60,10 +60,13 @@ public:
void requestSetSharedFolders(const Utils::FilePaths &sharedFolders);
void writeServerSettingsChanges(const QList<QStringList> &changes);
void requestExpansion(const QString &name);
+ void requestExpansionForObject(const QString &value);
+ void requestPropertiesForObject(const QString &value);
bool shutdown();
signals:
+ void autIdRetrieved();
void logOutputReceived(const QString &output);
void squishTestRunStarted();
void squishTestRunFinished();
@@ -71,6 +74,9 @@ signals:
void configChangesFailed(QProcess::ProcessError error);
void configChangesWritten();
void localsUpdated(const QString &output);
+ void objectPicked(const QString &output);
+ void updateChildren(const QString &realName, const QStringList &children);
+ void propertiesFetched(const QStringList &properties);
void shutdownFinished();
private:
@@ -103,6 +109,9 @@ private:
void stopSquishServer();
void startSquishRunner();
void setupAndStartRecorder();
+ void setupAndStartInspector();
+ void exitAndResetSecondaryRunner();
+ void onInspectTriggered();
void stopRecorder();
void queryServer(RunnerQuery query);
void executeRunnerQuery();
diff --git a/src/plugins/squish/squishwizardpages.cpp b/src/plugins/squish/squishwizardpages.cpp
index 118c3217f2..70003cb2bf 100644
--- a/src/plugins/squish/squishwizardpages.cpp
+++ b/src/plugins/squish/squishwizardpages.cpp
@@ -48,7 +48,6 @@ bool SquishToolkitsPageFactory::validateData(Utils::Id typeId, const QVariant &,
SquishToolkitsPage::SquishToolkitsPage()
{
- resize(400, 300);
setTitle(Tr::tr("Create New Squish Test Suite"));
auto layout = new QVBoxLayout(this);
@@ -113,7 +112,7 @@ bool SquishToolkitsPage::handleReject()
void SquishToolkitsPage::delayedInitialize()
{
const auto s = SquishPlugin::squishSettings();
- const Utils::FilePath server = s->squishPath.filePath().pathAppended(
+ const Utils::FilePath server = s->squishPath().pathAppended(
Utils::HostOsInfo::withExecutableSuffix("bin/squishserver"));
if (server.isExecutableFile())
fetchServerSettings();
@@ -173,7 +172,6 @@ bool SquishScriptLanguagePageFactory::validateData(Utils::Id typeId, const QVari
SquishScriptLanguagePage::SquishScriptLanguagePage()
{
- resize(400, 300);
setTitle(Tr::tr("Create New Squish Test Suite"));
auto layout = new QHBoxLayout(this);
@@ -229,7 +227,6 @@ bool SquishAUTPageFactory::validateData(Utils::Id typeId, const QVariant &, QStr
SquishAUTPage::SquishAUTPage()
{
- resize(400, 300);
auto layout = new QVBoxLayout(this);
m_autCombo = new QComboBox(this);
layout->addWidget(m_autCombo);
diff --git a/src/plugins/studiowelcome/qdsnewdialog.cpp b/src/plugins/studiowelcome/qdsnewdialog.cpp
index c3f8f44372..4bd6293c19 100644
--- a/src/plugins/studiowelcome/qdsnewdialog.cpp
+++ b/src/plugins/studiowelcome/qdsnewdialog.cpp
@@ -88,10 +88,10 @@ QdsNewDialog::QdsNewDialog(QWidget *parent)
m_dialog->installEventFilter(this);
- QObject::connect(&m_wizard, &WizardHandler::wizardCreationFailed, this, [this]() {
+ QObject::connect(&m_wizard, &WizardHandler::wizardCreationFailed, this, [this] {
QMessageBox::critical(m_dialog, tr("New Project"), tr("Failed to initialize data."));
reject();
- delete this;
+ deleteLater();
});
QObject::connect(m_styleModel.data(), &StyleModel::modelAboutToBeReset, this, [this]() {
diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp
index c53ec48c43..b7a4725f81 100644
--- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp
+++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp
@@ -103,7 +103,7 @@ static StudioWelcomePlugin *s_pluginInstance = nullptr;
static Utils::FilePath getMainUiFileWithFallback()
{
- auto project = ProjectExplorer::SessionManager::startupProject();
+ auto project = ProjectExplorer::ProjectManager::startupProject();
if (!project)
return {};
@@ -492,8 +492,7 @@ private:
void StudioWelcomePlugin::closeSplashScreen()
{
- Utils::CheckableMessageBox::doNotAskAgain(Core::ICore::settings(),
- DO_NOT_SHOW_SPLASHSCREEN_AGAIN_KEY);
+ Utils::CheckableDecider(DO_NOT_SHOW_SPLASHSCREEN_AGAIN_KEY).doNotAskAgain();
if (!s_viewWindow.isNull())
s_viewWindow->deleteLater();
@@ -541,8 +540,7 @@ static bool showSplashScreen()
return true;
}
- return Utils::CheckableMessageBox::shouldAskAgain(Core::ICore::settings(),
- DO_NOT_SHOW_SPLASHSCREEN_AGAIN_KEY);
+ return Utils::CheckableDecider(DO_NOT_SHOW_SPLASHSCREEN_AGAIN_KEY).shouldAskAgain();
}
void StudioWelcomePlugin::extensionsInitialized()
@@ -669,10 +667,7 @@ bool StudioWelcomePlugin::delayedInitialize()
const Utils::FilePath qmlPath = version->qmlPath();
importPaths.maybeInsert(qmlPath, QmlJS::Dialect::QmlQtQuick2);
- QFutureInterface<void> result;
-
- QmlJS::ModelManagerInterface::importScan(result,
- QmlJS::ModelManagerInterface::workingCopy(),
+ QmlJS::ModelManagerInterface::importScan(QmlJS::ModelManagerInterface::workingCopy(),
importPaths,
modelManager,
false);
diff --git a/src/plugins/studiowelcome/stylemodel.cpp b/src/plugins/studiowelcome/stylemodel.cpp
index 79669ff2a5..520129f20d 100644
--- a/src/plugins/studiowelcome/stylemodel.cpp
+++ b/src/plugins/studiowelcome/stylemodel.cpp
@@ -3,8 +3,8 @@
#include "stylemodel.h"
-#include "utils/algorithm.h"
-#include "utils/qtcassert.h"
+#include <utils/algorithm.h>
+#include <utils/qtcassert.h>
#include <QRegularExpression>
diff --git a/src/plugins/studiowelcome/wizardhandler.cpp b/src/plugins/studiowelcome/wizardhandler.cpp
index ee20518e60..d6d750d39e 100644
--- a/src/plugins/studiowelcome/wizardhandler.cpp
+++ b/src/plugins/studiowelcome/wizardhandler.cpp
@@ -11,8 +11,8 @@
#include <projectexplorer/jsonwizard/jsonprojectpage.h>
-#include "utils/wizard.h"
#include <utils/qtcassert.h>
+#include <utils/wizard.h>
using namespace StudioWelcome;
diff --git a/src/plugins/subversion/subversionclient.cpp b/src/plugins/subversion/subversionclient.cpp
index f496054da6..ac664a69b8 100644
--- a/src/plugins/subversion/subversionclient.cpp
+++ b/src/plugins/subversion/subversionclient.cpp
@@ -12,8 +12,7 @@
#include <utils/commandline.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
-#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <vcsbase/vcsbaseconstants.h>
#include <vcsbase/vcsbasediffeditorcontroller.h>
@@ -35,27 +34,22 @@ using namespace VcsBase;
namespace Subversion {
namespace Internal {
-static SubversionSettings *s_settings = nullptr;
-
class SubversionLogConfig : public VcsBaseEditorConfig
{
Q_OBJECT
public:
- SubversionLogConfig(SubversionSettings &settings, QToolBar *toolBar) :
- VcsBaseEditorConfig(toolBar)
+ explicit SubversionLogConfig(QToolBar *toolBar)
+ : VcsBaseEditorConfig(toolBar)
{
mapSetting(addToggleButton("--verbose", Tr::tr("Verbose"),
Tr::tr("Show files changed in each revision")),
- &settings.logVerbose);
+ &settings().logVerbose);
}
};
-SubversionClient::SubversionClient(SubversionSettings *settings) : VcsBaseClient(settings)
+SubversionClient::SubversionClient() : VcsBaseClient(&Internal::settings())
{
- s_settings = settings;
- setLogConfigCreator([settings](QToolBar *toolBar) {
- return new SubversionLogConfig(*settings, toolBar);
- });
+ setLogConfigCreator([](QToolBar *toolBar) { return new SubversionLogConfig(toolBar); });
}
bool SubversionClient::doCommit(const FilePath &repositoryRoot,
@@ -102,11 +96,11 @@ Id SubversionClient::vcsEditorKind(VcsCommandTag cmd) const
// Add authorization options to the command line arguments.
CommandLine &operator<<(Utils::CommandLine &command, SubversionClient::AddAuthOptions)
{
- if (!s_settings->hasAuthentication())
+ if (!settings().hasAuthentication())
return command;
- const QString userName = s_settings->userName.value();
- const QString password = s_settings->password.value();
+ const QString userName = settings().userName();
+ const QString password = settings().password();
if (userName.isEmpty())
return command;
@@ -171,7 +165,7 @@ SubversionDiffEditorController::SubversionDiffEditorController(IDocument *docume
const TreeStorage<QString> diffInputStorage = inputStorage();
- const auto setupDescription = [this](QtcProcess &process) {
+ const auto setupDescription = [this](Process &process) {
if (m_changeNumber == 0)
return TaskAction::StopWithDone;
setupCommand(process, {"log", "-r", QString::number(m_changeNumber)});
@@ -181,14 +175,14 @@ SubversionDiffEditorController::SubversionDiffEditorController(IDocument *docume
setDescription(Tr::tr("Waiting for data..."));
return TaskAction::Continue;
};
- const auto onDescriptionDone = [this](const QtcProcess &process) {
+ const auto onDescriptionDone = [this](const Process &process) {
setDescription(process.cleanedStdOut());
};
- const auto onDescriptionError = [this](const QtcProcess &) {
+ const auto onDescriptionError = [this](const Process &) {
setDescription({});
};
- const auto setupDiff = [this](QtcProcess &process) {
+ const auto setupDiff = [this](Process &process) {
QStringList args = QStringList{"diff"} << "--internal-diff";
if (ignoreWhitespace())
args << "-x" << "-uw";
@@ -202,19 +196,19 @@ SubversionDiffEditorController::SubversionDiffEditorController(IDocument *docume
command << SubversionClient::AddAuthOptions();
process.setCommand(command);
};
- const auto onDiffDone = [diffInputStorage](const QtcProcess &process) {
- *diffInputStorage.activeStorage() = process.cleanedStdOut();
+ const auto onDiffDone = [diffInputStorage](const Process &process) {
+ *diffInputStorage = process.cleanedStdOut();
};
const Group root {
Storage(diffInputStorage),
parallel,
Group {
- optional,
- Process(setupDescription, onDescriptionDone, onDescriptionError)
+ finishAllAndDone,
+ ProcessTask(setupDescription, onDescriptionDone, onDescriptionError)
},
Group {
- Process(setupDiff, onDiffDone),
+ ProcessTask(setupDiff, onDiffDone),
postProcessTask()
}
};
@@ -240,13 +234,13 @@ void SubversionDiffEditorController::setChangeNumber(int changeNumber)
SubversionDiffEditorController *SubversionClient::findOrCreateDiffEditor(const QString &documentId,
const FilePath &source, const QString &title, const FilePath &workingDirectory)
{
- auto &settings = static_cast<SubversionSettings &>(this->settings());
+ SubversionSettings &settings = Internal::settings();
IDocument *document = DiffEditorController::findOrCreateDocument(documentId, title);
auto controller = qobject_cast<SubversionDiffEditorController *>(
DiffEditorController::controller(document));
if (!controller) {
controller = new SubversionDiffEditorController(document);
- controller->setVcsBinary(settings.binaryPath.filePath());
+ controller->setVcsBinary(settings.binaryPath());
controller->setProcessEnvironment(processEnvironment());
controller->setWorkingDirectory(workingDirectory);
}
@@ -278,7 +272,7 @@ void SubversionClient::log(const FilePath &workingDir,
const std::function<void(Utils::CommandLine &)> &addAuthOptions)
{
auto &settings = static_cast<SubversionSettings &>(this->settings());
- const int logCount = settings.logCount.value();
+ const int logCount = settings.logCount();
QStringList svnExtraOptions = extraOptions;
if (logCount > 0)
svnExtraOptions << QLatin1String("-l") << QString::number(logCount);
diff --git a/src/plugins/subversion/subversionclient.h b/src/plugins/subversion/subversionclient.h
index d3beec2b4c..b2ce0f9061 100644
--- a/src/plugins/subversion/subversionclient.h
+++ b/src/plugins/subversion/subversionclient.h
@@ -18,7 +18,7 @@ class SubversionClient : public VcsBase::VcsBaseClient
Q_OBJECT
public:
- SubversionClient(SubversionSettings *settings);
+ SubversionClient();
bool doCommit(const Utils::FilePath &repositoryRoot,
const QStringList &files,
diff --git a/src/plugins/subversion/subversionplugin.cpp b/src/plugins/subversion/subversionplugin.cpp
index 1eedd61606..e584d98181 100644
--- a/src/plugins/subversion/subversionplugin.cpp
+++ b/src/plugins/subversion/subversionplugin.cpp
@@ -289,8 +289,6 @@ private:
QAction *m_menuAction = nullptr;
- SubversionSettingsPage m_settingsPage{&m_settings};
-
public:
VcsSubmitEditorFactory submitEditorFactory {
submitParameters,
@@ -358,7 +356,7 @@ SubversionPluginPrivate::SubversionPluginPrivate()
{
dd = this;
- m_client = new SubversionClient(&m_settings);
+ m_client = new SubversionClient();
setTopicCache(new SubversionTopicCache(this));
@@ -524,7 +522,7 @@ SubversionPluginPrivate::SubversionPluginPrivate()
subversionMenu->addAction(command);
m_commandLocator->appendCommand(command);
- connect(&m_settings, &AspectContainer::applied, this, &IVersionControl::configurationChanged);
+ connect(&settings(), &AspectContainer::applied, this, &IVersionControl::configurationChanged);
}
bool SubversionPluginPrivate::isVcsDirectory(const FilePath &fileName) const
@@ -640,7 +638,7 @@ void SubversionPluginPrivate::revertAll()
QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
return;
// NoteL: Svn "revert ." doesn not work.
- CommandLine args{m_settings.binaryPath.filePath(), {"revert"}};
+ CommandLine args{settings().binaryPath(), {"revert"}};
args << SubversionClient::AddAuthOptions();
args << QLatin1String("--recursive") << state.topLevel().toString();
const auto revertResponse = runSvn(state.topLevel(), args, RunFlags::ShowStdOut);
@@ -657,7 +655,7 @@ void SubversionPluginPrivate::revertCurrentFile()
const VcsBasePluginState state = currentState();
QTC_ASSERT(state.hasFile(), return);
- CommandLine diffArgs{m_settings.binaryPath.filePath(), {"diff"}};
+ CommandLine diffArgs{settings().binaryPath(), {"diff"}};
diffArgs << SubversionClient::AddAuthOptions();
diffArgs << SubversionClient::escapeFile(state.relativeCurrentFile());
@@ -675,7 +673,7 @@ void SubversionPluginPrivate::revertCurrentFile()
FileChangeBlocker fcb(state.currentFile());
// revert
- CommandLine args{m_settings.binaryPath.filePath(), {"revert"}};
+ CommandLine args{settings().binaryPath(), {"revert"}};
args << SubversionClient::AddAuthOptions();
args << SubversionClient::escapeFile(state.relativeCurrentFile());
@@ -736,7 +734,7 @@ void SubversionPluginPrivate::startCommit(const FilePath &workingDir, const QStr
return;
}
- CommandLine args{m_settings.binaryPath.filePath(), {"status"}};
+ CommandLine args{settings().binaryPath(), {"status"}};
args << SubversionClient::AddAuthOptions();
args << SubversionClient::escapeFiles(files);
@@ -815,7 +813,7 @@ void SubversionPluginPrivate::svnStatus(const FilePath &workingDir, const QStrin
{
const VcsBasePluginState state = currentState();
QTC_ASSERT(state.hasTopLevel(), return);
- CommandLine args{m_settings.binaryPath.filePath(), {"status"}};
+ CommandLine args{settings().binaryPath(), {"status"}};
args << SubversionClient::AddAuthOptions();
if (!relativePath.isEmpty())
args << SubversionClient::escapeFile(relativePath);
@@ -841,7 +839,7 @@ void SubversionPluginPrivate::updateProject()
void SubversionPluginPrivate::svnUpdate(const FilePath &workingDir, const QString &relativePath)
{
- CommandLine args{m_settings.binaryPath.filePath(), {"update"}};
+ CommandLine args{settings().binaryPath(), {"update"}};
args << SubversionClient::AddAuthOptions();
args << Constants::NON_INTERACTIVE_OPTION;
if (!relativePath.isEmpty())
@@ -865,9 +863,9 @@ void SubversionPluginPrivate::vcsAnnotateHelper(const FilePath &workingDir, cons
const FilePath source = VcsBaseEditor::getSource(workingDir, file);
QTextCodec *codec = VcsBaseEditor::getCodec(source);
- CommandLine args{m_settings.binaryPath.filePath(), {"annotate"}};
+ CommandLine args{settings().binaryPath(), {"annotate"}};
args << SubversionClient::AddAuthOptions();
- if (m_settings.spaceIgnorantAnnotation.value())
+ if (settings().spaceIgnorantAnnotation.value())
args << "-x" << "-uw";
if (!revision.isEmpty())
args << "-r" << revision;
@@ -949,10 +947,10 @@ CommandResult SubversionPluginPrivate::runSvn(const FilePath &workingDir,
const CommandLine &command, RunFlags flags,
QTextCodec *outputCodec, int timeoutMutiplier) const
{
- if (m_settings.binaryPath.value().isEmpty())
+ if (settings().binaryPath().isEmpty())
return CommandResult(ProcessResult::StartFailed, Tr::tr("No subversion executable specified."));
- const int timeoutS = m_settings.timeout.value() * timeoutMutiplier;
+ const int timeoutS = settings().timeout() * timeoutMutiplier;
return m_client->vcsSynchronousExec(workingDir, command, flags, timeoutS, outputCodec);
}
@@ -1008,7 +1006,7 @@ QString SubversionPluginPrivate::synchronousTopic(const FilePath &repository) co
bool SubversionPluginPrivate::vcsAdd(const FilePath &workingDir, const QString &rawFileName)
{
const QString file = QDir::toNativeSeparators(SubversionClient::escapeFile(rawFileName));
- CommandLine args{m_settings.binaryPath.filePath()};
+ CommandLine args{settings().binaryPath()};
args << "add" << SubversionClient::AddAuthOptions() << "--parents" << file;
return runSvn(workingDir, args, RunFlags::ShowStdOut).result()
== ProcessResult::FinishedWithSuccess;
@@ -1018,7 +1016,7 @@ bool SubversionPluginPrivate::vcsDelete(const FilePath &workingDir, const QStrin
{
const QString file = QDir::toNativeSeparators(SubversionClient::escapeFile(rawFileName));
- CommandLine args{m_settings.binaryPath.filePath()};
+ CommandLine args{settings().binaryPath()};
args << "delete" << SubversionClient::AddAuthOptions() << "--force" << file;
return runSvn(workingDir, args, RunFlags::ShowStdOut).result()
@@ -1027,7 +1025,7 @@ bool SubversionPluginPrivate::vcsDelete(const FilePath &workingDir, const QStrin
bool SubversionPluginPrivate::vcsMove(const FilePath &workingDir, const QString &from, const QString &to)
{
- CommandLine args{m_settings.binaryPath.filePath(), {"move"}};
+ CommandLine args{settings().binaryPath(), {"move"}};
args << SubversionClient::AddAuthOptions()
<< QDir::toNativeSeparators(SubversionClient::escapeFile(from))
<< QDir::toNativeSeparators(SubversionClient::escapeFile(to));
@@ -1040,7 +1038,7 @@ bool SubversionPluginPrivate::vcsCheckout(const FilePath &directory, const QByte
QUrl tempUrl = QUrl::fromEncoded(url);
const QString username = tempUrl.userName();
const QString password = tempUrl.password();
- CommandLine args{m_settings.binaryPath.filePath(), {"checkout"}};
+ CommandLine args{settings().binaryPath(), {"checkout"}};
args << Constants::NON_INTERACTIVE_OPTION;
if (!username.isEmpty()) {
@@ -1087,7 +1085,7 @@ bool SubversionPluginPrivate::managesDirectory(const FilePath &directory, FilePa
bool SubversionPluginPrivate::managesFile(const FilePath &workingDirectory, const QString &fileName) const
{
- CommandLine args{m_settings.binaryPath.filePath()};
+ CommandLine args{settings().binaryPath()};
args << "status" << SubversionClient::AddAuthOptions()
<< QDir::toNativeSeparators(SubversionClient::escapeFile(fileName));
const QString output = runSvn(workingDirectory, args).cleanedStdOut();
@@ -1126,7 +1124,7 @@ bool SubversionPluginPrivate::isVcsFileOrDirectory(const FilePath &filePath) con
bool SubversionPluginPrivate::isConfigured() const
{
- const FilePath binary = m_settings.binaryPath.filePath();
+ const FilePath binary = settings().binaryPath();
if (binary.isEmpty())
return false;
QFileInfo fi = binary.toFileInfo();
@@ -1189,7 +1187,7 @@ VcsCommand *SubversionPluginPrivate::createInitialCheckoutCommand(const QString
const QString &localName,
const QStringList &extraArgs)
{
- CommandLine args{m_settings.binaryPath.filePath()};
+ CommandLine args{settings().binaryPath()};
args << "checkout";
args << SubversionClient::AddAuthOptions();
args << Subversion::Constants::NON_INTERACTIVE_OPTION << extraArgs << url << localName;
diff --git a/src/plugins/subversion/subversionsettings.cpp b/src/plugins/subversion/subversionsettings.cpp
index 5a27512912..9b9225ccae 100644
--- a/src/plugins/subversion/subversionsettings.cpp
+++ b/src/plugins/subversion/subversionsettings.cpp
@@ -3,124 +3,105 @@
#include "subversionsettings.h"
-#include "subversionclient.h"
-#include "subversionplugin.h"
#include "subversiontr.h"
-#include <coreplugin/icore.h>
-#include <coreplugin/dialogs/ioptionspage.h>
-
-#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
#include <vcsbase/vcsbaseconstants.h>
-#include <QSettings>
-
using namespace Utils;
using namespace VcsBase;
-namespace Subversion {
-namespace Internal {
+namespace Subversion::Internal {
+
+static SubversionSettings *theSettings;
-// SubversionSettings
+SubversionSettings &settings()
+{
+ return *theSettings;
+}
SubversionSettings::SubversionSettings()
{
- setAutoApply(false);
+ theSettings = this;
+
+ setId(VcsBase::Constants::VCS_ID_SUBVERSION);
+ setDisplayName(Tr::tr("Subversion"));
+ setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
setSettingsGroup("Subversion");
- registerAspect(&binaryPath);
- binaryPath.setDisplayStyle(StringAspect::PathChooserDisplay);
binaryPath.setExpectedKind(PathChooser::ExistingCommand);
binaryPath.setHistoryCompleter("Subversion.Command.History");
binaryPath.setDefaultValue("svn" QTC_HOST_EXE_SUFFIX);
binaryPath.setDisplayName(Tr::tr("Subversion Command"));
binaryPath.setLabelText(Tr::tr("Subversion command:"));
- registerAspect(&useAuthentication);
useAuthentication.setSettingsKey("Authentication");
- useAuthentication.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ useAuthentication.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox);
- registerAspect(&userName);
userName.setSettingsKey("User");
userName.setDisplayStyle(StringAspect::LineEditDisplay);
userName.setLabelText(Tr::tr("Username:"));
- registerAspect(&password);
password.setSettingsKey("Password");
password.setDisplayStyle(StringAspect::LineEditDisplay);
password.setLabelText(Tr::tr("Password:"));
- registerAspect(&spaceIgnorantAnnotation);
spaceIgnorantAnnotation.setSettingsKey("SpaceIgnorantAnnotation");
spaceIgnorantAnnotation.setDefaultValue(true);
spaceIgnorantAnnotation.setLabelText(Tr::tr("Ignore whitespace changes in annotation"));
- registerAspect(&diffIgnoreWhiteSpace);
diffIgnoreWhiteSpace.setSettingsKey("DiffIgnoreWhiteSpace");
- registerAspect(&logVerbose);
logVerbose.setSettingsKey("LogVerbose");
- registerAspect(&logCount);
logCount.setDefaultValue(1000);
logCount.setLabelText(Tr::tr("Log count:"));
- registerAspect(&timeout);
timeout.setLabelText(Tr::tr("Timeout:"));
timeout.setSuffix(Tr::tr("s"));
QObject::connect(&useAuthentication, &BaseAspect::changed, this, [this] {
- userName.setEnabled(useAuthentication.value());
- password.setEnabled(useAuthentication.value());
+ userName.setEnabled(useAuthentication());
+ password.setEnabled(useAuthentication());
});
-}
-bool SubversionSettings::hasAuthentication() const
-{
- return useAuthentication.value() && !userName.value().isEmpty();
-}
-
-SubversionSettingsPage::SubversionSettingsPage(SubversionSettings *settings)
-{
- setId(VcsBase::Constants::VCS_ID_SUBVERSION);
- setDisplayName(Tr::tr("Subversion"));
- setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- SubversionSettings &s = *settings;
+ setLayouter([this] {
using namespace Layouting;
- Column {
+ return Column {
Group {
title(Tr::tr("Configuration")),
- Column { s.binaryPath }
+ Column { binaryPath }
},
Group {
- title(Tr::tr("Authentication"), &s.useAuthentication),
+ title(Tr::tr("Authentication")),
+ useAuthentication.groupChecker(),
Form {
- s.userName,
- s.password,
+ userName, br,
+ password,
}
},
Group {
title(Tr::tr("Miscellaneous")),
Column {
- Row { s.logCount, s.timeout, st },
- s.spaceIgnorantAnnotation,
+ Row { logCount, timeout, st },
+ spaceIgnorantAnnotation,
}
},
st
- }.attachTo(widget);
+ };
});
}
-} // Internal
-} // Subversion
+bool SubversionSettings::hasAuthentication() const
+{
+ return useAuthentication() && !userName().isEmpty();
+}
+
+} // Subversion::Internal
diff --git a/src/plugins/subversion/subversionsettings.h b/src/plugins/subversion/subversionsettings.h
index 01e3091695..c5bcb8e9cf 100644
--- a/src/plugins/subversion/subversionsettings.h
+++ b/src/plugins/subversion/subversionsettings.h
@@ -3,11 +3,9 @@
#pragma once
-#include <coreplugin/dialogs/ioptionspage.h>
#include <vcsbase/vcsbaseclientsettings.h>
-namespace Subversion {
-namespace Internal {
+namespace Subversion::Internal {
class SubversionSettings : public VcsBase::VcsBaseSettings
{
@@ -16,18 +14,13 @@ public:
bool hasAuthentication() const;
- Utils::BoolAspect useAuthentication;
- Utils::StringAspect password;
- Utils::BoolAspect spaceIgnorantAnnotation;
- Utils::BoolAspect diffIgnoreWhiteSpace;
- Utils::BoolAspect logVerbose;
+ Utils::BoolAspect useAuthentication{this};
+ Utils::StringAspect password{this};
+ Utils::BoolAspect spaceIgnorantAnnotation{this};
+ Utils::BoolAspect diffIgnoreWhiteSpace{this};
+ Utils::BoolAspect logVerbose{this};
};
-class SubversionSettingsPage final : public Core::IOptionsPage
-{
-public:
- explicit SubversionSettingsPage(SubversionSettings *settings);
-};
+SubversionSettings &settings();
-} // namespace Internal
-} // namespace Subversion
+} // Subversion::Internal
diff --git a/src/plugins/terminal/CMakeLists.txt b/src/plugins/terminal/CMakeLists.txt
new file mode 100644
index 0000000000..69da526cdc
--- /dev/null
+++ b/src/plugins/terminal/CMakeLists.txt
@@ -0,0 +1,23 @@
+
+add_qtc_plugin(Terminal
+ PLUGIN_DEPENDS Core ProjectExplorer
+ DEPENDS libvterm
+ SOURCES
+ celliterator.cpp celliterator.h
+ glyphcache.cpp glyphcache.h
+ keys.cpp keys.h
+ scrollback.cpp scrollback.h
+ shellintegration.cpp shellintegration.h
+ shellmodel.cpp shellmodel.h
+ terminal.qrc
+ terminalconstants.h
+ terminalicons.h
+ terminalpane.cpp terminalpane.h
+ terminalplugin.cpp
+ terminalprocessimpl.cpp terminalprocessimpl.h
+ terminalsearch.cpp terminalsearch.h
+ terminalsettings.cpp terminalsettings.h
+ terminalsurface.cpp terminalsurface.h
+ terminaltr.h
+ terminalwidget.cpp terminalwidget.h
+)
diff --git a/src/plugins/terminal/Terminal.json.in b/src/plugins/terminal/Terminal.json.in
new file mode 100644
index 0000000000..5640012c08
--- /dev/null
+++ b/src/plugins/terminal/Terminal.json.in
@@ -0,0 +1,18 @@
+{
+ \"Name\" : \"Terminal\",
+ \"Version\" : \"$$QTCREATOR_VERSION\",
+ \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
+ \"Vendor\" : \"The Qt Company Ltd\",
+ \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\",
+ \"License\" : [ \"Commercial Usage\",
+ \"\",
+ \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\",
+ \"\",
+ \"GNU General Public License Usage\",
+ \"\",
+ \"Alternatively, this plugin 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 plugin. 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.\"
+ ],
+ \"Description\" : \"Terminal window.\",
+ \"Url\" : \"http://www.qt.io\",
+ $$dependencyList
+}
diff --git a/src/plugins/terminal/celliterator.cpp b/src/plugins/terminal/celliterator.cpp
new file mode 100644
index 0000000000..91a70f76ea
--- /dev/null
+++ b/src/plugins/terminal/celliterator.cpp
@@ -0,0 +1,94 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "celliterator.h"
+
+#include "terminalsurface.h"
+
+#include <stdexcept>
+
+namespace Terminal::Internal {
+
+CellIterator::CellIterator(const TerminalSurface *surface, QPoint pos)
+ : CellIterator(surface, pos.x() + (pos.y() * surface->liveSize().width()))
+{}
+
+CellIterator::CellIterator(const TerminalSurface *surface, int pos)
+ : m_state(State::INSIDE)
+ , m_surface(surface)
+{
+ m_maxpos = surface->fullSize().width() * (surface->fullSize().height()) - 1;
+ m_pos = qMax(0, qMin(m_maxpos + 1, pos));
+
+ if (m_pos == 0) {
+ m_state = State::BEGIN;
+ } else if (m_pos == m_maxpos + 1) {
+ m_state = State::END;
+ }
+
+ if (m_state != State::END)
+ updateChar();
+}
+
+CellIterator::CellIterator(const TerminalSurface *surface)
+ : m_state(State::END)
+ , m_surface(surface)
+{
+ m_maxpos = surface->fullSize().width() * (surface->fullSize().height()) - 1;
+ m_pos = m_maxpos + 1;
+}
+
+QPoint CellIterator::gridPos() const
+{
+ return m_surface->posToGrid(m_pos);
+}
+
+bool CellIterator::updateChar()
+{
+ QPoint cell = m_surface->posToGrid(m_pos);
+ m_char = m_surface->fetchCharAt(cell.x(), cell.y());
+ return m_char != 0;
+}
+
+CellIterator &CellIterator::operator-=(int n)
+{
+ if (n == 0)
+ return *this;
+
+ if (m_pos - n < 0)
+ throw new std::runtime_error("-= n too big!");
+
+ m_pos -= n;
+
+ while (!updateChar() && m_pos > 0 && m_skipZeros)
+ m_pos--;
+
+ m_state = State::INSIDE;
+
+ if (m_pos == 0) {
+ m_state = State::BEGIN;
+ }
+
+ return *this;
+}
+
+CellIterator &CellIterator::operator+=(int n)
+{
+ if (n == 0)
+ return *this;
+
+ if (m_pos + n < m_maxpos + 1) {
+ m_state = State::INSIDE;
+ m_pos += n;
+ while (!updateChar() && m_pos < (m_maxpos + 1) && m_skipZeros)
+ m_pos++;
+
+ if (m_pos == m_maxpos + 1)
+ m_state = State::END;
+ } else {
+ *this = m_surface->end();
+ }
+ return *this;
+}
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/celliterator.h b/src/plugins/terminal/celliterator.h
new file mode 100644
index 0000000000..c246aaa311
--- /dev/null
+++ b/src/plugins/terminal/celliterator.h
@@ -0,0 +1,97 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <string>
+
+#include <QPoint>
+
+namespace Terminal::Internal {
+
+class TerminalSurface;
+
+class CellIterator
+{
+public:
+ using iterator_category = std::bidirectional_iterator_tag;
+ using difference_type = std::ptrdiff_t;
+ using value_type = std::u32string::value_type;
+ using pointer = std::u32string::value_type *;
+ // We need to return copies for std::reverse_iterator to work
+ using reference = std::u32string::value_type;
+
+ enum class State { BEGIN, INSIDE, END } m_state{};
+
+public:
+ CellIterator(const TerminalSurface *surface, QPoint pos);
+ CellIterator(const TerminalSurface *surface, int pos);
+ CellIterator(const TerminalSurface *surface);
+
+public:
+ QPoint gridPos() const;
+
+public:
+ CellIterator &operator-=(int n);
+ CellIterator &operator+=(int n);
+
+ reference operator*() const { return m_char; }
+ pointer operator->() { return &m_char; }
+
+ CellIterator &operator++() { return *this += 1; }
+ CellIterator operator++(int)
+ {
+ CellIterator tmp = *this;
+ ++(*this);
+ return tmp;
+ }
+
+ CellIterator &operator--() { return *this -= 1; }
+ CellIterator operator--(int)
+ {
+ CellIterator tmp = *this;
+ --(*this);
+ return tmp;
+ }
+
+ bool operator!=(const CellIterator &other) const
+ {
+ if (other.m_state != m_state)
+ return true;
+
+ if (other.m_pos != m_pos)
+ return true;
+
+ return false;
+ }
+
+ bool operator==(const CellIterator &other) const { return !operator!=(other); }
+
+ CellIterator operator-(int n) const
+ {
+ CellIterator result = *this;
+ result -= n;
+ return result;
+ }
+
+ CellIterator operator+(int n) const
+ {
+ CellIterator result = *this;
+ result += n;
+ return result;
+ }
+
+ int position() const { return m_pos; }
+ void setSkipZeros(bool skipZeros) { m_skipZeros = skipZeros; }
+
+private:
+ bool updateChar();
+
+ const TerminalSurface *m_surface{nullptr};
+ int m_pos{-1};
+ int m_maxpos{-1};
+ bool m_skipZeros{false};
+ mutable std::u32string::value_type m_char;
+};
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/glyphcache.cpp b/src/plugins/terminal/glyphcache.cpp
new file mode 100644
index 0000000000..72a0fd7b9d
--- /dev/null
+++ b/src/plugins/terminal/glyphcache.cpp
@@ -0,0 +1,48 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "glyphcache.h"
+
+#include <QTextLayout>
+
+namespace Terminal::Internal {
+
+size_t qHash(const GlyphCacheKey &key, size_t seed = 0)
+{
+ return qHash(key.font, seed) ^ qHash(key.text, seed);
+}
+
+GlyphCache &GlyphCache::instance()
+{
+ static GlyphCache cache(5000);
+ return cache;
+}
+
+const QGlyphRun *GlyphCache::get(const QFont &font, const QString &text)
+{
+ GlyphCacheKey key{font, text};
+ if (auto *run = object(key))
+ return run;
+
+ QTextLayout layout;
+
+ layout.setText(text);
+ layout.setFont(font);
+
+ layout.beginLayout();
+ layout.createLine().setNumColumns(std::numeric_limits<int>::max());
+ layout.endLayout();
+
+ if (layout.lineCount() > 0) {
+ const auto &line = layout.lineAt(0);
+ const auto runs = line.glyphRuns();
+ if (!runs.isEmpty()) {
+ QGlyphRun *run = new QGlyphRun(layout.lineAt(0).glyphRuns().first());
+ insert(key, run);
+ return run;
+ }
+ }
+ return nullptr;
+}
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/glyphcache.h b/src/plugins/terminal/glyphcache.h
new file mode 100644
index 0000000000..60701098f5
--- /dev/null
+++ b/src/plugins/terminal/glyphcache.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QCache>
+#include <QFont>
+#include <QGlyphRun>
+#include <QString>
+
+namespace Terminal::Internal {
+
+struct GlyphCacheKey
+{
+ QFont font;
+ QString text;
+
+ bool operator==(const GlyphCacheKey &other) const
+ {
+ return font == other.font && text == other.text;
+ }
+};
+
+class GlyphCache : public QCache<GlyphCacheKey, QGlyphRun>
+{
+public:
+ using QCache::QCache;
+
+ static GlyphCache &instance();
+
+ const QGlyphRun *get(const QFont &font, const QString &text);
+};
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/images/settingscategory_terminal.png b/src/plugins/terminal/images/settingscategory_terminal.png
new file mode 100644
index 0000000000..6e8c716778
--- /dev/null
+++ b/src/plugins/terminal/images/settingscategory_terminal.png
Binary files differ
diff --git a/src/plugins/terminal/images/settingscategory_terminal@2x.png b/src/plugins/terminal/images/settingscategory_terminal@2x.png
new file mode 100644
index 0000000000..71e292d8bc
--- /dev/null
+++ b/src/plugins/terminal/images/settingscategory_terminal@2x.png
Binary files differ
diff --git a/src/plugins/terminal/images/terminal.png b/src/plugins/terminal/images/terminal.png
new file mode 100644
index 0000000000..0a1dd311ec
--- /dev/null
+++ b/src/plugins/terminal/images/terminal.png
Binary files differ
diff --git a/src/plugins/terminal/images/terminal@2x.png b/src/plugins/terminal/images/terminal@2x.png
new file mode 100644
index 0000000000..da36b36721
--- /dev/null
+++ b/src/plugins/terminal/images/terminal@2x.png
Binary files differ
diff --git a/src/plugins/terminal/keys.cpp b/src/plugins/terminal/keys.cpp
new file mode 100644
index 0000000000..f6a7a91b13
--- /dev/null
+++ b/src/plugins/terminal/keys.cpp
@@ -0,0 +1,83 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "keys.h"
+
+namespace Terminal::Internal {
+
+VTermModifier qtModifierToVTerm(Qt::KeyboardModifiers mod)
+{
+ int ret = VTERM_MOD_NONE;
+
+ if (mod & Qt::ShiftModifier)
+ ret |= VTERM_MOD_SHIFT;
+
+ if (mod & Qt::AltModifier)
+ ret |= VTERM_MOD_ALT;
+
+#ifdef Q_OS_DARWIN
+ if (mod & Qt::MetaModifier)
+ ret |= VTERM_MOD_CTRL;
+#else
+ if (mod & Qt::ControlModifier)
+ ret |= VTERM_MOD_CTRL;
+#endif
+
+ return static_cast<VTermModifier>(ret);
+}
+
+VTermKey qtKeyToVTerm(Qt::Key key, bool keypad)
+{
+ if (key >= Qt::Key_F1 && key <= Qt::Key_F35)
+ return static_cast<VTermKey>(VTERM_KEY_FUNCTION_0 + key - Qt::Key_F1 + 1);
+
+ switch (key) {
+ case Qt::Key_Return:
+ return VTERM_KEY_ENTER;
+ case Qt::Key_Tab:
+ return VTERM_KEY_TAB;
+ case Qt::Key_Backspace:
+ return VTERM_KEY_BACKSPACE;
+ case Qt::Key_Escape:
+ return VTERM_KEY_ESCAPE;
+ case Qt::Key_Up:
+ return VTERM_KEY_UP;
+ case Qt::Key_Down:
+ return VTERM_KEY_DOWN;
+ case Qt::Key_Left:
+ return VTERM_KEY_LEFT;
+ case Qt::Key_Right:
+ return VTERM_KEY_RIGHT;
+ case Qt::Key_Insert:
+ return VTERM_KEY_INS;
+ case Qt::Key_Delete:
+ return VTERM_KEY_DEL;
+ case Qt::Key_Home:
+ return VTERM_KEY_HOME;
+ case Qt::Key_End:
+ return VTERM_KEY_END;
+ case Qt::Key_PageUp:
+ return VTERM_KEY_PAGEUP;
+ case Qt::Key_PageDown:
+ return VTERM_KEY_PAGEDOWN;
+ case Qt::Key_multiply:
+ return keypad ? VTERM_KEY_KP_MULT : VTERM_KEY_NONE;
+ case Qt::Key_Plus:
+ return keypad ? VTERM_KEY_KP_PLUS : VTERM_KEY_NONE;
+ case Qt::Key_Comma:
+ return keypad ? VTERM_KEY_KP_COMMA : VTERM_KEY_NONE;
+ case Qt::Key_Minus:
+ return keypad ? VTERM_KEY_KP_MINUS : VTERM_KEY_NONE;
+ case Qt::Key_Period:
+ return keypad ? VTERM_KEY_KP_PERIOD : VTERM_KEY_NONE;
+ case Qt::Key_Slash:
+ return keypad ? VTERM_KEY_KP_DIVIDE : VTERM_KEY_NONE;
+ case Qt::Key_Enter:
+ return keypad ? VTERM_KEY_KP_ENTER : VTERM_KEY_NONE;
+ case Qt::Key_Equal:
+ return keypad ? VTERM_KEY_KP_EQUAL : VTERM_KEY_NONE;
+ default:
+ return VTERM_KEY_NONE;
+ }
+}
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/keys.h b/src/plugins/terminal/keys.h
new file mode 100644
index 0000000000..f3df933001
--- /dev/null
+++ b/src/plugins/terminal/keys.h
@@ -0,0 +1,15 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <vterm_keycodes.h>
+
+#include <QKeyEvent>
+
+namespace Terminal::Internal {
+
+VTermKey qtKeyToVTerm(Qt::Key key, bool keypad);
+VTermModifier qtModifierToVTerm(Qt::KeyboardModifiers mod);
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/scrollback.cpp b/src/plugins/terminal/scrollback.cpp
new file mode 100644
index 0000000000..e22d5fa243
--- /dev/null
+++ b/src/plugins/terminal/scrollback.cpp
@@ -0,0 +1,61 @@
+// Copyright (c) 2020, Justin Bronder
+// Copied and modified from: https://github.com/jsbronder/sff
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include "scrollback.h"
+
+#include <cassert>
+#include <cstring>
+#include <future>
+
+namespace Terminal::Internal {
+
+Scrollback::Line::Line(int cols, const VTermScreenCell *cells)
+ : m_cols(cols)
+ , m_cells(std::make_unique<VTermScreenCell[]>(cols))
+{
+ memcpy(m_cells.get(), cells, cols * sizeof(cells[0]));
+}
+
+const VTermScreenCell *Scrollback::Line::cell(int i) const
+{
+ assert(i >= 0 && i < m_cols);
+ return &m_cells[i];
+}
+
+Scrollback::Scrollback(size_t capacity)
+ : m_capacity(capacity)
+{}
+
+void Scrollback::emplace(int cols, const VTermScreenCell *cells)
+{
+ m_deque.emplace_front(cols, cells);
+ while (m_deque.size() > m_capacity) {
+ m_deque.pop_back();
+ }
+}
+
+void Scrollback::popto(int cols, VTermScreenCell *cells)
+{
+ const Line &sbl = m_deque.front();
+
+ int ncells = cols;
+ if (ncells > sbl.cols())
+ ncells = sbl.cols();
+
+ memcpy(cells, sbl.cells(), sizeof(cells[0]) * ncells);
+ for (size_t i = ncells; i < static_cast<size_t>(cols); ++i) {
+ cells[i].chars[0] = '\0';
+ cells[i].width = 1;
+ cells[i].bg = cells[ncells - 1].bg;
+ }
+
+ m_deque.pop_front();
+}
+
+void Scrollback::clear()
+{
+ m_deque.clear();
+}
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/scrollback.h b/src/plugins/terminal/scrollback.h
new file mode 100644
index 0000000000..9ca71eec61
--- /dev/null
+++ b/src/plugins/terminal/scrollback.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2020, Justin Bronder
+// Copied and modified from: https://github.com/jsbronder/sff
+// SPDX-License-Identifier: BSD-3-Clause
+
+#pragma once
+
+#include <vterm.h>
+
+#include <deque>
+#include <future>
+#include <memory>
+
+#include <QFont>
+#include <QTextLayout>
+
+namespace Terminal::Internal {
+
+class Scrollback
+{
+public:
+ class Line
+ {
+ public:
+ Line(int cols, const VTermScreenCell *cells);
+ Line(Line &&other) = default;
+ Line() = delete;
+
+ int cols() const { return m_cols; };
+ const VTermScreenCell *cell(int i) const;
+ const VTermScreenCell *cells() const { return &m_cells[0]; };
+
+ private:
+ int m_cols;
+ std::unique_ptr<VTermScreenCell[]> m_cells;
+ };
+
+public:
+ Scrollback(size_t capacity);
+ Scrollback() = delete;
+
+ int capacity() const { return static_cast<int>(m_capacity); };
+ int size() const { return static_cast<int>(m_deque.size()); };
+
+ const Line &line(size_t index) const { return m_deque.at(index); };
+ const std::deque<Line> &lines() const { return m_deque; };
+
+ void emplace(int cols, const VTermScreenCell *cells);
+ void popto(int cols, VTermScreenCell *cells);
+
+ void clear();
+
+private:
+ size_t m_capacity;
+ std::deque<Line> m_deque;
+};
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/shellintegration.cpp b/src/plugins/terminal/shellintegration.cpp
new file mode 100644
index 0000000000..d8e26f94ce
--- /dev/null
+++ b/src/plugins/terminal/shellintegration.cpp
@@ -0,0 +1,164 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "shellintegration.h"
+
+#include <utils/environment.h>
+#include <utils/filepath.h>
+#include <utils/stringutils.h>
+
+#include <QLoggingCategory>
+
+Q_LOGGING_CATEGORY(integrationLog, "qtc.terminal.shellintegration", QtWarningMsg)
+
+using namespace Utils;
+
+namespace Terminal {
+
+struct FileToCopy
+{
+ FilePath source;
+ QString destName;
+};
+
+// clang-format off
+struct
+{
+ struct
+ {
+ FilePath rcFile{":/terminal/shellintegrations/shellintegration-bash.sh"};
+ } bash;
+ struct
+ {
+ QList<FileToCopy> files{
+ {":/terminal/shellintegrations/shellintegration-env.zsh", ".zshenv"},
+ {":/terminal/shellintegrations/shellintegration-login.zsh", ".zlogin"},
+ {":/terminal/shellintegrations/shellintegration-profile.zsh", ".zprofile"},
+ {":/terminal/shellintegrations/shellintegration-rc.zsh", ".zshrc"}
+ };
+ } zsh;
+ struct
+ {
+ FilePath script{":/terminal/shellintegrations/shellintegration.ps1"};
+ } pwsh;
+ struct
+ {
+ FilePath script{":/terminal/shellintegrations/shellintegration-clink.lua"};
+ } clink;
+
+} filesToCopy;
+// clang-format on
+
+bool ShellIntegration::canIntegrate(const Utils::CommandLine &cmdLine)
+{
+ if (cmdLine.executable().needsDevice())
+ return false; // TODO: Allow integration for remote shells
+
+ if (cmdLine.executable().baseName() == "zsh")
+ return true;
+
+ if (!cmdLine.arguments().isEmpty() && cmdLine.arguments() != "-l")
+ return false;
+
+ if (cmdLine.executable().baseName() == "bash")
+ return true;
+
+ if (cmdLine.executable().baseName() == "pwsh"
+ || cmdLine.executable().baseName() == "powershell") {
+ return true;
+ }
+
+ if (cmdLine.executable().baseName() == "cmd")
+ return true;
+
+ return false;
+}
+
+void ShellIntegration::onOsc(int cmd, const VTermStringFragment &fragment)
+{
+ QString d = QString::fromLocal8Bit(fragment.str, fragment.len);
+ const auto [command, data] = Utils::splitAtFirst(d, ';');
+
+ if (cmd == 1337) {
+ const auto [key, value] = Utils::splitAtFirst(command, '=');
+ if (key == QStringView(u"CurrentDir"))
+ emit currentDirChanged(FilePath::fromUserInput(value.toString()).path());
+
+ } else if (cmd == 7) {
+ emit currentDirChanged(FilePath::fromUserInput(d).path());
+ } else if (cmd == 133) {
+ qCDebug(integrationLog) << "OSC 133:" << data;
+ } else if (cmd == 633 && command.length() == 1) {
+ if (command[0] == 'E') {
+ CommandLine cmdLine = CommandLine::fromUserInput(data.toString());
+ emit commandChanged(cmdLine);
+ } else if (command[0] == 'D') {
+ emit commandChanged({});
+ } else if (command[0] == 'P') {
+ const auto [key, value] = Utils::splitAtFirst(data, '=');
+ if (key == QStringView(u"Cwd"))
+ emit currentDirChanged(value.toString());
+ }
+ }
+}
+
+void ShellIntegration::prepareProcess(Utils::Process &process)
+{
+ Environment env = process.environment().hasChanges() ? process.environment()
+ : Environment::systemEnvironment();
+ CommandLine cmd = process.commandLine();
+
+ if (!canIntegrate(cmd))
+ return;
+
+ env.set("VSCODE_INJECTION", "1");
+ env.set("TERM_PROGRAM", "vscode");
+
+ if (cmd.executable().baseName() == "bash") {
+ const FilePath rcPath = filesToCopy.bash.rcFile;
+ const FilePath tmpRc = FilePath::fromUserInput(
+ m_tempDir.filePath(filesToCopy.bash.rcFile.fileName()));
+ rcPath.copyFile(tmpRc);
+
+ CommandLine newCmd = {cmd.executable(), {"--init-file", tmpRc.nativePath()}};
+
+ if (cmd.arguments() == "-l")
+ newCmd.addArg("-l");
+
+ cmd = newCmd;
+ } else if (cmd.executable().baseName() == "zsh") {
+ for (const FileToCopy &file : filesToCopy.zsh.files) {
+ const auto copyResult = file.source.copyFile(
+ FilePath::fromUserInput(m_tempDir.filePath(file.destName)));
+ QTC_ASSERT_EXPECTED(copyResult, return);
+ }
+
+ const Utils::FilePath originalZdotDir = FilePath::fromUserInput(
+ env.value_or("ZDOTDIR", QDir::homePath()));
+
+ env.set("ZDOTDIR", m_tempDir.path());
+ env.set("USER_ZDOTDIR", originalZdotDir.nativePath());
+ } else if (cmd.executable().baseName() == "pwsh"
+ || cmd.executable().baseName() == "powershell") {
+ const FilePath rcPath = filesToCopy.pwsh.script;
+ const FilePath tmpRc = FilePath::fromUserInput(
+ m_tempDir.filePath(filesToCopy.pwsh.script.fileName()));
+ rcPath.copyFile(tmpRc);
+
+ cmd.addArgs(QString("-noexit -command try { . \"%1\" } catch {}{1}").arg(tmpRc.nativePath()),
+ CommandLine::Raw);
+ } else if (cmd.executable().baseName() == "cmd") {
+ const FilePath rcPath = filesToCopy.clink.script;
+ const FilePath tmpRc = FilePath::fromUserInput(
+ m_tempDir.filePath(filesToCopy.clink.script.fileName()));
+ rcPath.copyFile(tmpRc);
+
+ env.set("CLINK_HISTORY_LABEL", "QtCreator");
+ env.appendOrSet("CLINK_PATH", tmpRc.parentDir().nativePath(), ";");
+ }
+
+ process.setCommand(cmd);
+ process.setEnvironment(env);
+}
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/shellintegration.h b/src/plugins/terminal/shellintegration.h
new file mode 100644
index 0000000000..a4a813c8a6
--- /dev/null
+++ b/src/plugins/terminal/shellintegration.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH
+// Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <utils/commandline.h>
+#include <utils/process.h>
+
+#include <vterm.h>
+
+#include <QTemporaryDir>
+
+namespace Terminal {
+
+class ShellIntegration : public QObject
+{
+ Q_OBJECT
+public:
+ static bool canIntegrate(const Utils::CommandLine &cmdLine);
+
+ void onOsc(int cmd, const VTermStringFragment &fragment);
+
+ void prepareProcess(Utils::Process &process);
+
+signals:
+ void commandChanged(const Utils::CommandLine &command);
+ void currentDirChanged(const QString &dir);
+
+private:
+ QTemporaryDir m_tempDir;
+};
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/shellintegrations/shellintegration-bash.sh b/src/plugins/terminal/shellintegrations/shellintegration-bash.sh
new file mode 100755
index 0000000000..7db188be08
--- /dev/null
+++ b/src/plugins/terminal/shellintegrations/shellintegration-bash.sh
@@ -0,0 +1,252 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# SPDX-License-Identifier: MIT
+
+
+# Prevent the script recursing when setting up
+if [[ -n "$VSCODE_SHELL_INTEGRATION" ]]; then
+ builtin return
+fi
+
+VSCODE_SHELL_INTEGRATION=1
+
+# Run relevant rc/profile only if shell integration has been injected, not when run manually
+if [ "$VSCODE_INJECTION" == "1" ]; then
+ if [ -z "$VSCODE_SHELL_LOGIN" ]; then
+ if [ -r ~/.bashrc ]; then
+ . ~/.bashrc
+ fi
+ else
+ # Imitate -l because --init-file doesn't support it:
+ # run the first of these files that exists
+ if [ -r /etc/profile ]; then
+ . /etc/profile
+ fi
+ # exceute the first that exists
+ if [ -r ~/.bash_profile ]; then
+ . ~/.bash_profile
+ elif [ -r ~/.bash_login ]; then
+ . ~/.bash_login
+ elif [ -r ~/.profile ]; then
+ . ~/.profile
+ fi
+ builtin unset VSCODE_SHELL_LOGIN
+
+ # Apply any explicit path prefix (see #99878)
+ if [ -n "$VSCODE_PATH_PREFIX" ]; then
+ export PATH=$VSCODE_PATH_PREFIX$PATH
+ builtin unset VSCODE_PATH_PREFIX
+ fi
+ fi
+ builtin unset VSCODE_INJECTION
+fi
+
+if [ -z "$VSCODE_SHELL_INTEGRATION" ]; then
+ builtin return
+fi
+
+__vsc_get_trap() {
+ # 'trap -p DEBUG' outputs a shell command like `trap -- '…shellcode…' DEBUG`.
+ # The terms are quoted literals, but are not guaranteed to be on a single line.
+ # (Consider a trap like $'echo foo\necho \'bar\'').
+ # To parse, we splice those terms into an expression capturing them into an array.
+ # This preserves the quoting of those terms: when we `eval` that expression, they are preserved exactly.
+ # This is different than simply exploding the string, which would split everything on IFS, oblivious to quoting.
+ builtin local -a terms
+ builtin eval "terms=( $(trap -p "${1:-DEBUG}") )"
+ # |________________________|
+ # |
+ # \-------------------*--------------------/
+ # terms=( trap -- '…arbitrary shellcode…' DEBUG )
+ # |____||__| |_____________________| |_____|
+ # | | | |
+ # 0 1 2 3
+ # |
+ # \--------*----/
+ builtin printf '%s' "${terms[2]:-}"
+}
+
+# The property (P) and command (E) codes embed values which require escaping.
+# Backslashes are doubled. Non-alphanumeric characters are converted to escaped hex.
+__vsc_escape_value() {
+ # Process text byte by byte, not by codepoint.
+ builtin local LC_ALL=C str="${1}" i byte token out=''
+
+ for (( i=0; i < "${#str}"; ++i )); do
+ byte="${str:$i:1}"
+
+ # Escape backslashes and semi-colons
+ if [ "$byte" = "\\" ]; then
+ token="\\\\"
+ elif [ "$byte" = ";" ]; then
+ token="\\x3b"
+ else
+ token="$byte"
+ fi
+
+ out+="$token"
+ done
+
+ builtin printf '%s\n' "${out}"
+}
+
+# Send the IsWindows property if the environment looks like Windows
+if [[ "$(uname -s)" =~ ^CYGWIN*|MINGW*|MSYS* ]]; then
+ builtin printf '\e]633;P;IsWindows=True\a'
+fi
+
+# Allow verifying $BASH_COMMAND doesn't have aliases resolved via history when the right HISTCONTROL
+# configuration is used
+if [[ "$HISTCONTROL" =~ .*(erasedups|ignoreboth|ignoredups).* ]]; then
+ __vsc_history_verify=0
+else
+ __vsc_history_verify=1
+fi
+
+__vsc_initialized=0
+__vsc_original_PS1="$PS1"
+__vsc_original_PS2="$PS2"
+__vsc_custom_PS1=""
+__vsc_custom_PS2=""
+__vsc_in_command_execution="1"
+__vsc_current_command=""
+
+__vsc_prompt_start() {
+ builtin printf '\e]633;A\a'
+}
+
+__vsc_prompt_end() {
+ builtin printf '\e]633;B\a'
+}
+
+__vsc_update_cwd() {
+ builtin printf '\e]633;P;Cwd=%s\a' "$(__vsc_escape_value "$PWD")"
+}
+
+__vsc_command_output_start() {
+ builtin printf '\e]633;C\a'
+ builtin printf '\e]633;E;%s\a' "$(__vsc_escape_value "${__vsc_current_command}")"
+}
+
+__vsc_continuation_start() {
+ builtin printf '\e]633;F\a'
+}
+
+__vsc_continuation_end() {
+ builtin printf '\e]633;G\a'
+}
+
+__vsc_command_complete() {
+ if [ "$__vsc_current_command" = "" ]; then
+ builtin printf '\e]633;D\a'
+ else
+ builtin printf '\e]633;D;%s\a' "$__vsc_status"
+ fi
+ __vsc_update_cwd
+}
+__vsc_update_prompt() {
+ # in command execution
+ if [ "$__vsc_in_command_execution" = "1" ]; then
+ # Wrap the prompt if it is not yet wrapped, if the PS1 changed this this was last set it
+ # means the user re-exported the PS1 so we should re-wrap it
+ if [[ "$__vsc_custom_PS1" == "" || "$__vsc_custom_PS1" != "$PS1" ]]; then
+ __vsc_original_PS1=$PS1
+ __vsc_custom_PS1="\[$(__vsc_prompt_start)\]$__vsc_original_PS1\[$(__vsc_prompt_end)\]"
+ PS1="$__vsc_custom_PS1"
+ fi
+ if [[ "$__vsc_custom_PS2" == "" || "$__vsc_custom_PS2" != "$PS2" ]]; then
+ __vsc_original_PS2=$PS2
+ __vsc_custom_PS2="\[$(__vsc_continuation_start)\]$__vsc_original_PS2\[$(__vsc_continuation_end)\]"
+ PS2="$__vsc_custom_PS2"
+ fi
+ __vsc_in_command_execution="0"
+ fi
+}
+
+__vsc_precmd() {
+ __vsc_command_complete "$__vsc_status"
+ __vsc_current_command=""
+ __vsc_update_prompt
+}
+
+__vsc_preexec() {
+ __vsc_initialized=1
+ if [[ ! "$BASH_COMMAND" =~ ^__vsc_prompt* ]]; then
+ # Use history if it's available to verify the command as BASH_COMMAND comes in with aliases
+ # resolved
+ if [ "$__vsc_history_verify" = "1" ]; then
+ __vsc_current_command="$(builtin history 1 | sed 's/ *[0-9]* *//')"
+ else
+ __vsc_current_command=$BASH_COMMAND
+ fi
+ else
+ __vsc_current_command=""
+ fi
+ __vsc_command_output_start
+}
+
+# Debug trapping/preexec inspired by starship (ISC)
+if [[ -n "${bash_preexec_imported:-}" ]]; then
+ __vsc_preexec_only() {
+ if [ "$__vsc_in_command_execution" = "0" ]; then
+ __vsc_in_command_execution="1"
+ __vsc_preexec
+ fi
+ }
+ precmd_functions+=(__vsc_prompt_cmd)
+ preexec_functions+=(__vsc_preexec_only)
+else
+ __vsc_dbg_trap="$(__vsc_get_trap DEBUG)"
+
+ if [[ -z "$__vsc_dbg_trap" ]]; then
+ __vsc_preexec_only() {
+ if [ "$__vsc_in_command_execution" = "0" ]; then
+ __vsc_in_command_execution="1"
+ __vsc_preexec
+ fi
+ }
+ trap '__vsc_preexec_only "$_"' DEBUG
+ elif [[ "$__vsc_dbg_trap" != '__vsc_preexec "$_"' && "$__vsc_dbg_trap" != '__vsc_preexec_all "$_"' ]]; then
+ __vsc_preexec_all() {
+ if [ "$__vsc_in_command_execution" = "0" ]; then
+ __vsc_in_command_execution="1"
+ builtin eval "${__vsc_dbg_trap}"
+ __vsc_preexec
+ fi
+ }
+ trap '__vsc_preexec_all "$_"' DEBUG
+ fi
+fi
+
+__vsc_update_prompt
+
+__vsc_restore_exit_code() {
+ return "$1"
+}
+
+__vsc_prompt_cmd_original() {
+ __vsc_status="$?"
+ __vsc_restore_exit_code "${__vsc_status}"
+ # Evaluate the original PROMPT_COMMAND similarly to how bash would normally
+ # See https://unix.stackexchange.com/a/672843 for technique
+ for cmd in "${__vsc_original_prompt_command[@]}"; do
+ eval "${cmd:-}"
+ done
+ __vsc_precmd
+}
+
+__vsc_prompt_cmd() {
+ __vsc_status="$?"
+ __vsc_precmd
+}
+
+# PROMPT_COMMAND arrays and strings seem to be handled the same (handling only the first entry of
+# the array?)
+__vsc_original_prompt_command=$PROMPT_COMMAND
+
+if [[ -z "${bash_preexec_imported:-}" ]]; then
+ if [[ -n "$__vsc_original_prompt_command" && "$__vsc_original_prompt_command" != "__vsc_prompt_cmd" ]]; then
+ PROMPT_COMMAND=__vsc_prompt_cmd_original
+ else
+ PROMPT_COMMAND=__vsc_prompt_cmd
+ fi
+fi
diff --git a/src/plugins/terminal/shellintegrations/shellintegration-clink.lua b/src/plugins/terminal/shellintegrations/shellintegration-clink.lua
new file mode 100644
index 0000000000..ff9d5f3fac
--- /dev/null
+++ b/src/plugins/terminal/shellintegrations/shellintegration-clink.lua
@@ -0,0 +1,52 @@
+-- Copyright (c) 2023 Chris Antos
+-- SPDX-License-Identifier: MIT
+
+-- luacheck: globals vscode_shell_integration NONL
+if vscode_shell_integration == nil then
+ vscode_shell_integration = true
+end
+
+if not vscode_shell_integration then
+ return
+end
+
+local function is_vscode()
+ local term_program = os.getenv("term_program") or ""
+ if term_program:lower() == "vscode" then
+ return true
+ end
+end
+
+local function send_context()
+ if is_vscode() then
+ local codes = ""
+ codes = codes .. "\027]633;D;" .. os.geterrorlevel() .. "\a" -- send command exit code
+ codes = codes .. "\027]633;P;Cwd=" .. os.getcwd() .. "\a" -- send cwd as title
+ clink.print(codes, NONL)
+ end
+end
+
+local p = clink.promptfilter(-999)
+
+function p:filter() -- luacheck: no unused
+ -- Nothing to do here, but the filter function must be defined.
+end
+
+function p:surround() -- luacheck: no unused
+ if is_vscode() then
+ local pre, suf
+ local rpre, rsuf
+
+ -- ESC codes surrounding prompt string
+ pre = "\027]633;A\a" -- copied from shellIntegration-rc.zsh
+ suf = "\027]633;B\a" -- copied from shellIntegration-rc.zsh
+
+ -- ESC codes surrounding right side prompt string
+ rpre = "\027]633;H\a" -- copied from shellIntegration-rc.zsh
+ rsuf = "\027]633;I\a" -- copied from shellIntegration-rc.zsh
+
+ return pre, suf, rpre, rsuf
+ end
+end
+
+clink.onbeginedit(send_context)
diff --git a/src/plugins/terminal/shellintegrations/shellintegration-env.zsh b/src/plugins/terminal/shellintegrations/shellintegration-env.zsh
new file mode 100644
index 0000000000..3c890539ae
--- /dev/null
+++ b/src/plugins/terminal/shellintegrations/shellintegration-env.zsh
@@ -0,0 +1,15 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# SPDX-License-Identifier: MIT
+
+if [[ -f $USER_ZDOTDIR/.zshenv ]]; then
+ VSCODE_ZDOTDIR=$ZDOTDIR
+ ZDOTDIR=$USER_ZDOTDIR
+
+ # prevent recursion
+ if [[ $USER_ZDOTDIR != $VSCODE_ZDOTDIR ]]; then
+ . $USER_ZDOTDIR/.zshenv
+ fi
+
+ USER_ZDOTDIR=$ZDOTDIR
+ ZDOTDIR=$VSCODE_ZDOTDIR
+fi
diff --git a/src/plugins/terminal/shellintegrations/shellintegration-login.zsh b/src/plugins/terminal/shellintegrations/shellintegration-login.zsh
new file mode 100644
index 0000000000..37ff543979
--- /dev/null
+++ b/src/plugins/terminal/shellintegrations/shellintegration-login.zsh
@@ -0,0 +1,7 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# SPDX-License-Identifier: MIT
+
+ZDOTDIR=$USER_ZDOTDIR
+if [[ $options[norcs] = off && -o "login" && -f $ZDOTDIR/.zlogin ]]; then
+ . $ZDOTDIR/.zlogin
+fi
diff --git a/src/plugins/terminal/shellintegrations/shellintegration-profile.zsh b/src/plugins/terminal/shellintegrations/shellintegration-profile.zsh
new file mode 100644
index 0000000000..724e1f2879
--- /dev/null
+++ b/src/plugins/terminal/shellintegrations/shellintegration-profile.zsh
@@ -0,0 +1,15 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# SPDX-License-Identifier: MIT
+
+if [[ $options[norcs] = off && -o "login" && -f $USER_ZDOTDIR/.zprofile ]]; then
+ VSCODE_ZDOTDIR=$ZDOTDIR
+ ZDOTDIR=$USER_ZDOTDIR
+ . $USER_ZDOTDIR/.zprofile
+ ZDOTDIR=$VSCODE_ZDOTDIR
+
+ # Apply any explicit path prefix (see #99878)
+ if (( ${+VSCODE_PATH_PREFIX} )); then
+ export PATH=$VSCODE_PATH_PREFIX$PATH
+ fi
+ builtin unset VSCODE_PATH_PREFIX
+fi
diff --git a/src/plugins/terminal/shellintegrations/shellintegration-rc.zsh b/src/plugins/terminal/shellintegrations/shellintegration-rc.zsh
new file mode 100644
index 0000000000..df4109131a
--- /dev/null
+++ b/src/plugins/terminal/shellintegrations/shellintegration-rc.zsh
@@ -0,0 +1,160 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# SPDX-License-Identifier: MIT
+
+builtin autoload -Uz add-zsh-hook
+
+# Prevent the script recursing when setting up
+if [ -n "$VSCODE_SHELL_INTEGRATION" ]; then
+ ZDOTDIR=$USER_ZDOTDIR
+ builtin return
+fi
+
+# This variable allows the shell to both detect that VS Code's shell integration is enabled as well
+# as disable it by unsetting the variable.
+VSCODE_SHELL_INTEGRATION=1
+
+# By default, zsh will set the $HISTFILE to the $ZDOTDIR location automatically. In the case of the
+# shell integration being injected, this means that the terminal will use a different history file
+# to other terminals. To fix this issue, set $HISTFILE back to the default location before ~/.zshrc
+# is called as that may depend upon the value.
+if [[ "$VSCODE_INJECTION" == "1" ]]; then
+ HISTFILE=$USER_ZDOTDIR/.zsh_history
+fi
+
+# Only fix up ZDOTDIR if shell integration was injected (not manually installed) and has not been called yet
+if [[ "$VSCODE_INJECTION" == "1" ]]; then
+ if [[ $options[norcs] = off && -f $USER_ZDOTDIR/.zshrc ]]; then
+ VSCODE_ZDOTDIR=$ZDOTDIR
+ ZDOTDIR=$USER_ZDOTDIR
+ # A user's custom HISTFILE location might be set when their .zshrc file is sourced below
+ . $USER_ZDOTDIR/.zshrc
+ fi
+fi
+
+# Shell integration was disabled by the shell, exit without warning assuming either the shell has
+# explicitly disabled shell integration as it's incompatible or it implements the protocol.
+if [ -z "$VSCODE_SHELL_INTEGRATION" ]; then
+ builtin return
+fi
+
+# The property (P) and command (E) codes embed values which require escaping.
+# Backslashes are doubled. Non-alphanumeric characters are converted to escaped hex.
+__vsc_escape_value() {
+ builtin emulate -L zsh
+
+ # Process text byte by byte, not by codepoint.
+ builtin local LC_ALL=C str="$1" i byte token out=''
+
+ for (( i = 0; i < ${#str}; ++i )); do
+ byte="${str:$i:1}"
+
+ # Escape backslashes and semi-colons
+ if [ "$byte" = "\\" ]; then
+ token="\\\\"
+ elif [ "$byte" = ";" ]; then
+ token="\\x3b"
+ else
+ token="$byte"
+ fi
+
+ out+="$token"
+ done
+
+ builtin print -r "$out"
+}
+
+__vsc_in_command_execution="1"
+__vsc_current_command=""
+
+__vsc_prompt_start() {
+ builtin printf '\e]633;A\a'
+}
+
+__vsc_prompt_end() {
+ builtin printf '\e]633;B\a'
+}
+
+__vsc_update_cwd() {
+ builtin printf '\e]633;P;Cwd=%s\a' "$(__vsc_escape_value "${PWD}")"
+}
+
+__vsc_command_output_start() {
+ builtin printf '\e]633;C\a'
+ builtin printf '\e]633;E;%s\a' "${__vsc_current_command}"
+}
+
+__vsc_continuation_start() {
+ builtin printf '\e]633;F\a'
+}
+
+__vsc_continuation_end() {
+ builtin printf '\e]633;G\a'
+}
+
+__vsc_right_prompt_start() {
+ builtin printf '\e]633;H\a'
+}
+
+__vsc_right_prompt_end() {
+ builtin printf '\e]633;I\a'
+}
+
+__vsc_command_complete() {
+ if [[ "$__vsc_current_command" == "" ]]; then
+ builtin printf '\e]633;D\a'
+ else
+ builtin printf '\e]633;D;%s\a' "$__vsc_status"
+ fi
+ __vsc_update_cwd
+}
+
+if [[ -o NOUNSET ]]; then
+ if [ -z "${RPROMPT-}" ]; then
+ RPROMPT=""
+ fi
+fi
+__vsc_update_prompt() {
+ __vsc_prior_prompt="$PS1"
+ __vsc_prior_prompt2="$PS2"
+ __vsc_in_command_execution=""
+ PS1="%{$(__vsc_prompt_start)%}$PS1%{$(__vsc_prompt_end)%}"
+ PS2="%{$(__vsc_continuation_start)%}$PS2%{$(__vsc_continuation_end)%}"
+ if [ -n "$RPROMPT" ]; then
+ __vsc_prior_rprompt="$RPROMPT"
+ RPROMPT="%{$(__vsc_right_prompt_start)%}$RPROMPT%{$(__vsc_right_prompt_end)%}"
+ fi
+}
+
+__vsc_precmd() {
+ local __vsc_status="$?"
+ if [ -z "${__vsc_in_command_execution-}" ]; then
+ # not in command execution
+ __vsc_command_output_start
+ fi
+
+ __vsc_command_complete "$__vsc_status"
+ __vsc_current_command=""
+
+ # in command execution
+ if [ -n "$__vsc_in_command_execution" ]; then
+ # non null
+ __vsc_update_prompt
+ fi
+}
+
+__vsc_preexec() {
+ PS1="$__vsc_prior_prompt"
+ PS2="$__vsc_prior_prompt2"
+ if [ -n "$RPROMPT" ]; then
+ RPROMPT="$__vsc_prior_rprompt"
+ fi
+ __vsc_in_command_execution="1"
+ __vsc_current_command=$2
+ __vsc_command_output_start
+}
+add-zsh-hook precmd __vsc_precmd
+add-zsh-hook preexec __vsc_preexec
+
+if [[ $options[login] = off && $USER_ZDOTDIR != $VSCODE_ZDOTDIR ]]; then
+ ZDOTDIR=$USER_ZDOTDIR
+fi
diff --git a/src/plugins/terminal/shellintegrations/shellintegration.fish b/src/plugins/terminal/shellintegrations/shellintegration.fish
new file mode 100644
index 0000000000..7495bab3f4
--- /dev/null
+++ b/src/plugins/terminal/shellintegrations/shellintegration.fish
@@ -0,0 +1,122 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# SPDX-License-Identifier: MIT
+#
+# Visual Studio Code terminal integration for fish
+#
+# Manual installation:
+#
+# (1) Add the following to the end of `$__fish_config_dir/config.fish`:
+#
+# string match -q "$TERM_PROGRAM" "vscode"
+# and . (code --locate-shell-integration-path fish)
+#
+# (2) Restart fish.
+
+# Don't run in scripts, other terminals, or more than once per session.
+status is-interactive
+and string match --quiet "$TERM_PROGRAM" "vscode"
+and ! set --query VSCODE_SHELL_INTEGRATION
+or exit
+
+set --global VSCODE_SHELL_INTEGRATION 1
+
+# Apply any explicit path prefix (see #99878)
+if status --is-login; and set -q VSCODE_PATH_PREFIX
+ fish_add_path -p $VSCODE_PATH_PREFIX
+end
+set -e VSCODE_PATH_PREFIX
+
+# Helper function
+function __vsc_esc -d "Emit escape sequences for VS Code shell integration"
+ builtin printf "\e]633;%s\a" (string join ";" $argv)
+end
+
+# Sent right before executing an interactive command.
+# Marks the beginning of command output.
+function __vsc_cmd_executed --on-event fish_preexec
+ __vsc_esc C
+ __vsc_esc E (__vsc_escape_value "$argv")
+
+ # Creates a marker to indicate a command was run.
+ set --global _vsc_has_cmd
+end
+
+
+# Escape a value for use in the 'P' ("Property") or 'E' ("Command Line") sequences.
+# Backslashes are doubled and non-alphanumeric characters are hex encoded.
+function __vsc_escape_value
+ # Escape backslashes and semi-colons
+ echo $argv \
+ | string replace --all '\\' '\\\\' \
+ | string replace --all ';' '\\x3b' \
+ ;
+end
+
+# Sent right after an interactive command has finished executing.
+# Marks the end of command output.
+function __vsc_cmd_finished --on-event fish_postexec
+ __vsc_esc D $status
+end
+
+# Sent when a command line is cleared or reset, but no command was run.
+# Marks the cleared line with neither success nor failure.
+function __vsc_cmd_clear --on-event fish_cancel
+ __vsc_esc D
+end
+
+# Sent whenever a new fish prompt is about to be displayed.
+# Updates the current working directory.
+function __vsc_update_cwd --on-event fish_prompt
+ __vsc_esc P Cwd=(__vsc_escape_value "$PWD")
+
+ # If a command marker exists, remove it.
+ # Otherwise, the commandline is empty and no command was run.
+ if set --query _vsc_has_cmd
+ set --erase _vsc_has_cmd
+ else
+ __vsc_cmd_clear
+ end
+end
+
+# Sent at the start of the prompt.
+# Marks the beginning of the prompt (and, implicitly, a new line).
+function __vsc_fish_prompt_start
+ __vsc_esc A
+end
+
+# Sent at the end of the prompt.
+# Marks the beginning of the user's command input.
+function __vsc_fish_cmd_start
+ __vsc_esc B
+end
+
+function __vsc_fish_has_mode_prompt -d "Returns true if fish_mode_prompt is defined and not empty"
+ functions fish_mode_prompt | string match -rvq '^ *(#|function |end$|$)'
+end
+
+# Preserve the user's existing prompt, to wrap in our escape sequences.
+functions --copy fish_prompt __vsc_fish_prompt
+
+# Preserve and wrap fish_mode_prompt (which appears to the left of the regular
+# prompt), but only if it's not defined as an empty function (which is the
+# officially documented way to disable that feature).
+if __vsc_fish_has_mode_prompt
+ functions --copy fish_mode_prompt __vsc_fish_mode_prompt
+
+ function fish_mode_prompt
+ __vsc_fish_prompt_start
+ __vsc_fish_mode_prompt
+ end
+
+ function fish_prompt
+ __vsc_fish_prompt
+ __vsc_fish_cmd_start
+ end
+else
+ # No fish_mode_prompt, so put everything in fish_prompt.
+ function fish_prompt
+ __vsc_fish_prompt_start
+ __vsc_fish_prompt
+ __vsc_fish_cmd_start
+ end
+end
diff --git a/src/plugins/terminal/shellintegrations/shellintegration.ps1 b/src/plugins/terminal/shellintegrations/shellintegration.ps1
new file mode 100644
index 0000000000..4fd978a884
--- /dev/null
+++ b/src/plugins/terminal/shellintegrations/shellintegration.ps1
@@ -0,0 +1,158 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# SPDX-License-Identifier: MIT
+
+# Prevent installing more than once per session
+if (Test-Path variable:global:__VSCodeOriginalPrompt) {
+ return;
+}
+
+# Disable shell integration when the language mode is restricted
+if ($ExecutionContext.SessionState.LanguageMode -ne "FullLanguage") {
+ return;
+}
+
+$Global:__VSCodeOriginalPrompt = $function:Prompt
+
+$Global:__LastHistoryId = -1
+
+function Global:__VSCode-Escape-Value([string]$value) {
+ # NOTE: In PowerShell v6.1+, this can be written `$value -replace '…', { … }` instead of `[regex]::Replace`.
+ # Replace any non-alphanumeric characters.
+ [regex]::Replace($value, '[\\\n;]', { param($match)
+ # Encode the (ascii) matches as `\x<hex>`
+ -Join (
+ [System.Text.Encoding]::UTF8.GetBytes($match.Value) | ForEach-Object { '\x{0:x2}' -f $_ }
+ )
+ })
+}
+
+function Global:Prompt() {
+ $FakeCode = [int]!$global:?
+ # NOTE: We disable strict mode for the scope of this function because it unhelpfully throws an
+ # error when $LastHistoryEntry is null, and is not otherwise useful.
+ Set-StrictMode -Off
+ $LastHistoryEntry = Get-History -Count 1
+ # Skip finishing the command if the first command has not yet started
+ if ($Global:__LastHistoryId -ne -1) {
+ if ($LastHistoryEntry.Id -eq $Global:__LastHistoryId) {
+ # Don't provide a command line or exit code if there was no history entry (eg. ctrl+c, enter on no command)
+ $Result = "$([char]0x1b)]633;E`a"
+ $Result += "$([char]0x1b)]633;D`a"
+ } else {
+ # Command finished command line
+ # OSC 633 ; A ; <CommandLine?> ST
+ $Result = "$([char]0x1b)]633;E;"
+ # Sanitize the command line to ensure it can get transferred to the terminal and can be parsed
+ # correctly. This isn't entirely safe but good for most cases, it's important for the Pt parameter
+ # to only be composed of _printable_ characters as per the spec.
+ if ($LastHistoryEntry.CommandLine) {
+ $CommandLine = $LastHistoryEntry.CommandLine
+ } else {
+ $CommandLine = ""
+ }
+ $Result += $(__VSCode-Escape-Value $CommandLine)
+ $Result += "`a"
+ # Command finished exit code
+ # OSC 633 ; D [; <ExitCode>] ST
+ $Result += "$([char]0x1b)]633;D;$FakeCode`a"
+ }
+ }
+ # Prompt started
+ # OSC 633 ; A ST
+ $Result += "$([char]0x1b)]633;A`a"
+ # Current working directory
+ # OSC 633 ; <Property>=<Value> ST
+ $Result += if($pwd.Provider.Name -eq 'FileSystem'){"$([char]0x1b)]633;P;Cwd=$(__VSCode-Escape-Value $pwd.ProviderPath)`a"}
+ # Before running the original prompt, put $? back to what it was:
+ if ($FakeCode -ne 0) {
+ Write-Error "failure" -ea ignore
+ }
+ # Run the original prompt
+ $Result += $Global:__VSCodeOriginalPrompt.Invoke()
+ # Write command started
+ $Result += "$([char]0x1b)]633;B`a"
+ $Global:__LastHistoryId = $LastHistoryEntry.Id
+ return $Result
+}
+
+# Only send the command executed sequence when PSReadLine is loaded, if not shell integration should
+# still work thanks to the command line sequence
+if (Get-Module -Name PSReadLine) {
+ $__VSCodeOriginalPSConsoleHostReadLine = $function:PSConsoleHostReadLine
+ function Global:PSConsoleHostReadLine {
+ $tmp = $__VSCodeOriginalPSConsoleHostReadLine.Invoke()
+ # Write command executed sequence directly to Console to avoid the new line from Write-Host
+ [Console]::Write("$([char]0x1b)]633;C`a")
+ $tmp
+ }
+}
+
+# Set IsWindows property
+[Console]::Write("$([char]0x1b)]633;P;IsWindows=$($IsWindows)`a")
+
+# Set always on key handlers which map to default VS Code keybindings
+function Set-MappedKeyHandler {
+ param ([string[]] $Chord, [string[]]$Sequence)
+ try {
+ $Handler = Get-PSReadLineKeyHandler -Chord $Chord | Select-Object -First 1
+ } catch [System.Management.Automation.ParameterBindingException] {
+ # PowerShell 5.1 ships with PSReadLine 2.0.0 which does not have -Chord,
+ # so we check what's bound and filter it.
+ $Handler = Get-PSReadLineKeyHandler -Bound | Where-Object -FilterScript { $_.Key -eq $Chord } | Select-Object -First 1
+ }
+ if ($Handler) {
+ Set-PSReadLineKeyHandler -Chord $Sequence -Function $Handler.Function
+ }
+}
+
+function Set-MappedKeyHandlers {
+ Set-MappedKeyHandler -Chord Ctrl+Spacebar -Sequence 'F12,a'
+ Set-MappedKeyHandler -Chord Alt+Spacebar -Sequence 'F12,b'
+ Set-MappedKeyHandler -Chord Shift+Enter -Sequence 'F12,c'
+ Set-MappedKeyHandler -Chord Shift+End -Sequence 'F12,d'
+
+ # Conditionally enable suggestions
+ if ($env:VSCODE_SUGGEST -eq '1') {
+ Remove-Item Env:VSCODE_SUGGEST
+
+ # VS Code send completions request (may override Ctrl+Spacebar)
+ Set-PSReadLineKeyHandler -Chord 'F12,e' -ScriptBlock {
+ Send-Completions
+ }
+
+ # Suggest trigger characters
+ Set-PSReadLineKeyHandler -Chord "-" -ScriptBlock {
+ [Microsoft.PowerShell.PSConsoleReadLine]::Insert("-")
+ Send-Completions
+ }
+ }
+}
+
+function Send-Completions {
+ $commandLine = ""
+ $cursorIndex = 0
+ # TODO: Since fuzzy matching exists, should completions be provided only for character after the
+ # last space and then filter on the client side? That would let you trigger ctrl+space
+ # anywhere on a word and have full completions available
+ [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$commandLine, [ref]$cursorIndex)
+ $completionPrefix = $commandLine
+
+ # Get completions
+ $result = "`e]633;Completions"
+ if ($completionPrefix.Length -gt 0) {
+ # Get and send completions
+ $completions = TabExpansion2 -inputScript $completionPrefix -cursorColumn $cursorIndex
+ if ($null -ne $completions.CompletionMatches) {
+ $result += ";$($completions.ReplacementIndex);$($completions.ReplacementLength);$($cursorIndex);"
+ $result += $completions.CompletionMatches | ConvertTo-Json -Compress
+ }
+ }
+ $result += "`a"
+
+ Write-Host -NoNewLine $result
+}
+
+# Register key handlers if PSReadLine is available
+if (Get-Module -Name PSReadLine) {
+ Set-MappedKeyHandlers
+}
diff --git a/src/plugins/terminal/shellmodel.cpp b/src/plugins/terminal/shellmodel.cpp
new file mode 100644
index 0000000000..b4cf53a1e9
--- /dev/null
+++ b/src/plugins/terminal/shellmodel.cpp
@@ -0,0 +1,110 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "shellmodel.h"
+
+#include <utils/algorithm.h>
+#include <utils/environment.h>
+#include <utils/filepath.h>
+
+#include <QFileIconProvider>
+#include <QStandardPaths>
+
+namespace Terminal::Internal {
+
+using namespace Utils;
+
+FilePaths availableShells()
+{
+ if (Utils::HostOsInfo::isWindowsHost()) {
+ FilePaths shells;
+
+ FilePath comspec = FilePath::fromUserInput(qtcEnvironmentVariable("COMSPEC"));
+ shells << comspec;
+
+ if (comspec.fileName() != "cmd.exe") {
+ FilePath cmd = FilePath::fromUserInput(QStandardPaths::findExecutable("cmd.exe"));
+ shells << cmd;
+ }
+
+ FilePath powershell = FilePath::fromUserInput(
+ QStandardPaths::findExecutable("powershell.exe"));
+ if (powershell.exists())
+ shells << powershell;
+
+ FilePath bash = FilePath::fromUserInput(QStandardPaths::findExecutable("bash.exe"));
+ if (bash.exists())
+ shells << bash;
+
+ FilePath git_bash = FilePath::fromUserInput(QStandardPaths::findExecutable("git.exe"));
+ if (git_bash.exists())
+ shells << git_bash.parentDir().parentDir().pathAppended("usr/bin/bash.exe");
+
+ FilePath msys2_bash = FilePath::fromUserInput(QStandardPaths::findExecutable("msys2.exe"));
+ if (msys2_bash.exists())
+ shells << msys2_bash.parentDir().pathAppended("usr/bin/bash.exe");
+
+ return shells;
+ } else {
+ FilePath shellsFile = FilePath::fromString("/etc/shells");
+ const auto shellFileContent = shellsFile.fileContents();
+ QTC_ASSERT_EXPECTED(shellFileContent, return {});
+
+ QString shellFileContentString = QString::fromUtf8(*shellFileContent);
+
+ // Filter out comments ...
+ const QStringList lines
+ = Utils::filtered(shellFileContentString.split('\n', Qt::SkipEmptyParts),
+ [](const QString &line) { return !line.trimmed().startsWith('#'); });
+
+ // Convert lines to file paths ...
+ const FilePaths shells = Utils::transform(lines, [](const QString &line) {
+ return FilePath::fromUserInput(line.trimmed());
+ });
+
+ // ... and filter out non-existing shells.
+ return Utils::filtered(shells, [](const FilePath &shell) { return shell.exists(); });
+ }
+}
+
+struct ShellModelPrivate
+{
+ QList<ShellModelItem> localShells;
+};
+
+ShellModel::ShellModel(QObject *parent)
+ : QObject(parent)
+ , d(new ShellModelPrivate())
+{
+ QFileIconProvider iconProvider;
+
+ const FilePaths shells = availableShells();
+ for (const FilePath &shell : shells) {
+ ShellModelItem item;
+ item.icon = iconProvider.icon(shell.toFileInfo());
+ item.name = shell.toUserOutput();
+ item.openParameters.shellCommand = {shell, {}};
+ d->localShells << item;
+ }
+}
+
+ShellModel::~ShellModel() = default;
+
+QList<ShellModelItem> ShellModel::local() const
+{
+ return d->localShells;
+}
+
+QList<ShellModelItem> ShellModel::remote() const
+{
+ const auto deviceCmds = Utils::Terminal::Hooks::instance().getTerminalCommandsForDevicesHook()();
+
+ const QList<ShellModelItem> deviceItems = Utils::transform(
+ deviceCmds, [](const Utils::Terminal::NameAndCommandLine &item) -> ShellModelItem {
+ return ShellModelItem{item.name, {}, {item.commandLine, std::nullopt, std::nullopt}};
+ });
+
+ return deviceItems;
+}
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/shellmodel.h b/src/plugins/terminal/shellmodel.h
new file mode 100644
index 0000000000..272f3fcd39
--- /dev/null
+++ b/src/plugins/terminal/shellmodel.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <utils/terminalhooks.h>
+
+#include <QIcon>
+#include <QObject>
+#include <QString>
+
+#include <memory>
+
+namespace Terminal::Internal {
+struct ShellModelPrivate;
+
+struct ShellModelItem
+{
+ QString name;
+ QIcon icon;
+ Utils::Terminal::OpenTerminalParameters openParameters;
+};
+
+class ShellModel : public QObject
+{
+public:
+ ShellModel(QObject *parent = nullptr);
+ ~ShellModel();
+
+ QList<ShellModelItem> local() const;
+ QList<ShellModelItem> remote() const;
+
+private:
+ std::unique_ptr<ShellModelPrivate> d;
+};
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/terminal.qbs b/src/plugins/terminal/terminal.qbs
new file mode 100644
index 0000000000..300c1acf19
--- /dev/null
+++ b/src/plugins/terminal/terminal.qbs
@@ -0,0 +1,43 @@
+import qbs 1.0
+
+QtcPlugin {
+ name: "Terminal"
+
+ Depends { name: "Core" }
+ Depends { name: "ProjectExplorer" }
+ Depends { name: "vterm" }
+ Depends { name: "ptyqt" }
+
+ files: [
+ "celliterator.cpp",
+ "celliterator.h",
+ "glyphcache.cpp",
+ "glyphcache.h",
+ "keys.cpp",
+ "keys.h",
+ "scrollback.cpp",
+ "scrollback.h",
+ "shellmodel.cpp",
+ "shellmodel.h",
+ "shellintegration.cpp",
+ "shellintegration.h",
+ "terminal.qrc",
+ "terminalconstants.h",
+ "terminalicons.h",
+ "terminalpane.cpp",
+ "terminalpane.h",
+ "terminalplugin.cpp",
+ "terminalprocessimpl.cpp",
+ "terminalprocessimpl.h",
+ "terminalsearch.cpp",
+ "terminalsearch.h",
+ "terminalsettings.cpp",
+ "terminalsettings.h",
+ "terminalsurface.cpp",
+ "terminalsurface.h",
+ "terminaltr.h",
+ "terminalwidget.cpp",
+ "terminalwidget.h",
+ ]
+}
+
diff --git a/src/plugins/terminal/terminal.qrc b/src/plugins/terminal/terminal.qrc
new file mode 100644
index 0000000000..63c28169df
--- /dev/null
+++ b/src/plugins/terminal/terminal.qrc
@@ -0,0 +1,16 @@
+<RCC>
+ <qresource prefix="/terminal">
+ <file>images/settingscategory_terminal.png</file>
+ <file>images/settingscategory_terminal@2x.png</file>
+ <file>images/terminal.png</file>
+ <file>images/terminal@2x.png</file>
+ <file>shellintegrations/shellintegration-bash.sh</file>
+ <file>shellintegrations/shellintegration-env.zsh</file>
+ <file>shellintegrations/shellintegration-login.zsh</file>
+ <file>shellintegrations/shellintegration-profile.zsh</file>
+ <file>shellintegrations/shellintegration-rc.zsh</file>
+ <file>shellintegrations/shellintegration.fish</file>
+ <file>shellintegrations/shellintegration.ps1</file>
+ <file>shellintegrations/shellintegration-clink.lua</file>
+ </qresource>
+</RCC>
diff --git a/src/plugins/terminal/terminalconstants.h b/src/plugins/terminal/terminalconstants.h
new file mode 100644
index 0000000000..99700475e6
--- /dev/null
+++ b/src/plugins/terminal/terminalconstants.h
@@ -0,0 +1,19 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace Terminal::Constants {
+constexpr char NEWTERMINAL[] = "Terminal.NewTerminal";
+constexpr char NEXTTERMINAL[] = "Terminal.NextTerminal";
+constexpr char PREVTERMINAL[] = "Terminal.PrevTerminal";
+constexpr char MINMAX[] = "Terminal.MinMax";
+
+constexpr char COPY[] = "Terminal.Copy";
+constexpr char PASTE[] = "Terminal.Paste";
+constexpr char CLEARSELECTION[] = "Terminal.ClearSelection";
+constexpr char MOVECURSORWORDLEFT[] = "Terminal.MoveCursorWordLeft";
+constexpr char MOVECURSORWORDRIGHT[] = "Terminal.MoveCursorWordRight";
+constexpr char CLEAR_TERMINAL[] = "Terminal.ClearTerminal";
+
+} // namespace Terminal::Constants
diff --git a/src/plugins/terminal/terminalicons.h b/src/plugins/terminal/terminalicons.h
new file mode 100644
index 0000000000..ca503f50f9
--- /dev/null
+++ b/src/plugins/terminal/terminalicons.h
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <utils/icon.h>
+
+namespace Terminal {
+
+static Utils::Icon NEW_TERMINAL_ICON(
+ {{":/terminal/images/terminal.png", Utils::Theme::IconsBaseColor},
+ {":/utils/images/iconoverlay_add_small.png", Utils::Theme::IconsRunToolBarColor}});
+
+static Utils::Icon CLOSE_TERMINAL_ICON(
+ {{":/terminal/images/terminal.png", Utils::Theme::IconsBaseColor},
+ {":/utils/images/iconoverlay_close_small.png", Utils::Theme::IconsStopToolBarColor}});
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalpane.cpp b/src/plugins/terminal/terminalpane.cpp
new file mode 100644
index 0000000000..7e9cae7539
--- /dev/null
+++ b/src/plugins/terminal/terminalpane.cpp
@@ -0,0 +1,394 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "terminalpane.h"
+
+#include "shellmodel.h"
+#include "terminalconstants.h"
+#include "terminalicons.h"
+#include "terminalsettings.h"
+#include "terminaltr.h"
+#include "terminalwidget.h"
+
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/icontext.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/locator/locatorconstants.h>
+
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
+
+#include <utils/algorithm.h>
+#include <utils/environment.h>
+#include <utils/terminalhooks.h>
+#include <utils/utilsicons.h>
+
+#include <QMenu>
+#include <QStandardPaths>
+#include <QToolButton>
+
+namespace Terminal {
+
+using namespace Utils;
+using namespace Utils::Terminal;
+using namespace Core;
+
+TerminalPane::TerminalPane(QObject *parent)
+ : IOutputPane(parent)
+ , m_context("Terminal.Pane", Core::Constants::C_GLOBAL_CUTOFF)
+{
+ setupContext(m_context, &m_tabWidget);
+ setZoomButtonsEnabled(true);
+
+ connect(this, &IOutputPane::zoomInRequested, this, [this] {
+ if (currentTerminal())
+ currentTerminal()->zoomIn();
+ });
+ connect(this, &IOutputPane::zoomOutRequested, this, [this] {
+ if (currentTerminal())
+ currentTerminal()->zoomOut();
+ });
+
+ initActions();
+
+ m_newTerminalButton = new QToolButton();
+ m_newTerminalButton->setDefaultAction(&newTerminal);
+
+ m_closeTerminalButton = new QToolButton();
+ m_closeTerminalButton->setDefaultAction(&closeTerminal);
+
+ m_openSettingsButton = new QToolButton();
+ m_openSettingsButton->setToolTip(Tr::tr("Configure..."));
+ m_openSettingsButton->setIcon(Icons::SETTINGS_TOOLBAR.icon());
+
+ connect(m_openSettingsButton, &QToolButton::clicked, m_openSettingsButton, []() {
+ ICore::showOptionsDialog("Terminal.General");
+ });
+
+ const auto updateEscButton = [this] {
+ m_escSettingButton->setChecked(TerminalSettings::instance().sendEscapeToTerminal());
+ static const QString escKey
+ = QKeySequence(Qt::Key_Escape).toString(QKeySequence::NativeText);
+ static const QString shiftEsc = QKeySequence(
+ QKeyCombination(Qt::ShiftModifier, Qt::Key_Escape))
+ .toString(QKeySequence::NativeText);
+ if (TerminalSettings::instance().sendEscapeToTerminal.value()) {
+ m_escSettingButton->setText(escKey);
+ m_escSettingButton->setToolTip(Tr::tr("Sending ESC to terminal instead of Qt Creator"));
+ } else {
+ m_escSettingButton->setText(shiftEsc);
+ m_escSettingButton->setToolTip(Tr::tr("Press %1 to send ESC to terminal").arg(shiftEsc));
+ }
+ };
+
+ m_escSettingButton = new QToolButton();
+ m_escSettingButton->setCheckable(true);
+
+ updateEscButton();
+
+ connect(m_escSettingButton, &QToolButton::toggled, this, [this] {
+ TerminalSettings::instance().sendEscapeToTerminal.setValue(m_escSettingButton->isChecked());
+ TerminalSettings::instance().writeSettings(ICore::settings());
+ });
+
+ connect(&TerminalSettings::instance(), &TerminalSettings::applied, this, updateEscButton);
+}
+
+TerminalPane::~TerminalPane() {}
+
+static std::optional<FilePath> startupProjectDirectory()
+{
+ const ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
+ if (!project)
+ return std::nullopt;
+
+ return project->projectDirectory();
+}
+
+void TerminalPane::openTerminal(const OpenTerminalParameters &parameters)
+{
+ OpenTerminalParameters parametersCopy{parameters};
+ if (!m_isVisible)
+ emit showPage(IOutputPane::ModeSwitch);
+
+ if (!parametersCopy.workingDirectory) {
+ const std::optional<FilePath> projectDir = startupProjectDirectory();
+ if (projectDir) {
+ if (!parametersCopy.shellCommand
+ || parametersCopy.shellCommand->executable().ensureReachable(*projectDir)) {
+ parametersCopy.workingDirectory = *projectDir;
+ }
+ }
+ }
+
+ const auto terminalWidget = new TerminalWidget(&m_tabWidget, parametersCopy);
+ m_tabWidget.setCurrentIndex(m_tabWidget.addTab(terminalWidget, Tr::tr("Terminal")));
+ setupTerminalWidget(terminalWidget);
+
+ m_tabWidget.currentWidget()->setFocus();
+
+ emit navigateStateUpdate();
+}
+
+void TerminalPane::addTerminal(TerminalWidget *terminal, const QString &title)
+{
+ if (!m_isVisible)
+ emit showPage(IOutputPane::ModeSwitch);
+ m_tabWidget.setCurrentIndex(m_tabWidget.addTab(terminal, title));
+ setupTerminalWidget(terminal);
+
+ emit navigateStateUpdate();
+}
+
+void TerminalPane::ensureVisible(TerminalWidget *terminal)
+{
+ if (!m_isVisible)
+ emit showPage(IOutputPane::ModeSwitch);
+ m_tabWidget.setCurrentWidget(terminal);
+ terminal->setFocus();
+}
+
+TerminalWidget *TerminalPane::stoppedTerminalWithId(Id identifier) const
+{
+ for (int i = 0; i < m_tabWidget.count(); ++i) {
+ const auto terminal = qobject_cast<TerminalWidget *>(m_tabWidget.widget(i));
+ if (terminal && terminal->processState() == QProcess::NotRunning
+ && terminal->identifier() == identifier)
+ return terminal;
+ }
+
+ return nullptr;
+}
+
+QWidget *TerminalPane::outputWidget(QWidget *parent)
+{
+ Q_UNUSED(parent)
+ if (!m_widgetInitialized) {
+ m_widgetInitialized = true;
+ m_tabWidget.setTabBarAutoHide(false);
+ m_tabWidget.setDocumentMode(true);
+ m_tabWidget.setTabsClosable(true);
+ m_tabWidget.setMovable(true);
+
+ connect(&m_tabWidget, &QTabWidget::tabCloseRequested, this, [this](int index) {
+ removeTab(index);
+ });
+
+ connect(&m_tabWidget, &QTabWidget::currentChanged, this, [this](int index) {
+ if (auto widget = m_tabWidget.widget(index))
+ widget->setFocus();
+ else
+ emit hidePage();
+ });
+ }
+
+ return &m_tabWidget;
+}
+
+TerminalWidget *TerminalPane::currentTerminal() const
+{
+ return static_cast<TerminalWidget *>(m_tabWidget.currentWidget());
+}
+
+void TerminalPane::removeTab(int index)
+{
+ delete m_tabWidget.widget(index);
+ emit navigateStateUpdate();
+}
+
+void TerminalPane::setupTerminalWidget(TerminalWidget *terminal)
+{
+ if (!terminal)
+ return;
+
+ const auto setTabText = [this, terminal]() {
+ const int index = m_tabWidget.indexOf(terminal);
+ const FilePath cwd = terminal->cwd();
+
+ const QString exe = terminal->currentCommand().isEmpty()
+ ? terminal->shellName()
+ : terminal->currentCommand().executable().fileName();
+
+ if (cwd.isEmpty())
+ m_tabWidget.setTabText(index, exe);
+ else
+ m_tabWidget.setTabText(index, exe + " - " + cwd.fileName());
+ };
+
+ connect(terminal, &TerminalWidget::started, this, setTabText);
+ connect(terminal, &TerminalWidget::cwdChanged, this, setTabText);
+ connect(terminal, &TerminalWidget::commandChanged, this, setTabText);
+
+ if (!terminal->shellName().isEmpty())
+ setTabText();
+}
+
+void TerminalPane::initActions()
+{
+ createShellMenu();
+
+ newTerminal.setText(Tr::tr("New Terminal"));
+ newTerminal.setIcon(NEW_TERMINAL_ICON.icon());
+ newTerminal.setToolTip(Tr::tr("Create a new Terminal."));
+ newTerminal.setMenu(&m_shellMenu);
+
+ nextTerminal.setText(Tr::tr("Next Terminal"));
+ prevTerminal.setText(Tr::tr("Previous Terminal"));
+
+ closeTerminal.setIcon(CLOSE_TERMINAL_ICON.icon());
+ closeTerminal.setToolTip(Tr::tr("Close the current Terminal."));
+
+ using namespace Constants;
+
+ ActionManager::registerAction(&newTerminal, NEWTERMINAL, m_context)
+ ->setDefaultKeySequences({QKeySequence(
+ HostOsInfo::isMacHost() ? QLatin1String("Ctrl+T") : QLatin1String("Ctrl+Shift+T"))});
+
+ ActionManager::registerAction(&nextTerminal, NEXTTERMINAL, m_context)
+ ->setDefaultKeySequences(
+ {QKeySequence("Alt+Tab"),
+ QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+Shift+[")
+ : QLatin1String("Ctrl+PgUp"))});
+
+ ActionManager::registerAction(&prevTerminal, PREVTERMINAL, m_context)
+ ->setDefaultKeySequences(
+ {QKeySequence("Alt+Shift+Tab"),
+ QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+Shift+]")
+ : QLatin1String("Ctrl+PgDown"))});
+
+ m_minMax = TerminalWidget::unlockGlobalAction("Coreplugin.OutputPane.minmax", m_context);
+ m_locate = TerminalWidget::unlockGlobalAction(Core::Constants::LOCATE, m_context);
+
+ connect(&newTerminal, &QAction::triggered, this, [this] { openTerminal({}); });
+ connect(&closeTerminal, &QAction::triggered, this, [this] {
+ removeTab(m_tabWidget.currentIndex());
+ });
+ connect(&nextTerminal, &QAction::triggered, this, [this] {
+ if (canNavigate())
+ goToNext();
+ });
+ connect(&prevTerminal, &QAction::triggered, this, [this] {
+ if (canPrevious())
+ goToPrev();
+ });
+}
+
+void TerminalPane::createShellMenu()
+{
+ const Internal::ShellModel *shellModel = new Internal::ShellModel(&m_shellMenu);
+
+ connect(&m_shellMenu, &QMenu::aboutToShow, &m_shellMenu, [shellModel, this] {
+ m_shellMenu.clear();
+
+ const auto addItems = [this](const QList<Internal::ShellModelItem> &items) {
+ for (const Internal::ShellModelItem &item : items) {
+ QAction *action = new QAction(item.icon, item.name, &m_shellMenu);
+
+ connect(action, &QAction::triggered, action, [item, this]() {
+ openTerminal(item.openParameters);
+ });
+
+ m_shellMenu.addAction(action);
+ }
+ };
+
+ addItems(shellModel->local());
+ m_shellMenu.addSection(Tr::tr("Devices"));
+ addItems(shellModel->remote());
+ });
+}
+
+QList<QWidget *> TerminalPane::toolBarWidgets() const
+{
+ QList<QWidget *> widgets = IOutputPane::toolBarWidgets();
+ widgets.prepend(m_newTerminalButton);
+ widgets.prepend(m_closeTerminalButton);
+
+ return widgets << m_openSettingsButton << m_escSettingButton;
+}
+
+QString TerminalPane::displayName() const
+{
+ return Tr::tr("Terminal");
+}
+
+int TerminalPane::priorityInStatusBar() const
+{
+ return 50;
+}
+
+void TerminalPane::clearContents()
+{
+ if (const auto t = currentTerminal())
+ t->clearContents();
+}
+
+void TerminalPane::visibilityChanged(bool visible)
+{
+ if (m_isVisible == visible)
+ return;
+
+ m_isVisible = visible;
+
+ if (visible && m_tabWidget.count() == 0)
+ openTerminal({});
+
+ IOutputPane::visibilityChanged(visible);
+}
+
+void TerminalPane::setFocus()
+{
+ if (const auto t = currentTerminal())
+ t->setFocus();
+}
+
+bool TerminalPane::hasFocus() const
+{
+ if (const auto t = currentTerminal())
+ return t->hasFocus();
+
+ return false;
+}
+
+bool TerminalPane::canFocus() const
+{
+ return true;
+}
+
+bool TerminalPane::canNavigate() const
+{
+ return true;
+}
+
+bool TerminalPane::canNext() const
+{
+ return m_tabWidget.count() > 1;
+}
+
+bool TerminalPane::canPrevious() const
+{
+ return m_tabWidget.count() > 1;
+}
+
+void TerminalPane::goToNext()
+{
+ int nextIndex = m_tabWidget.currentIndex() + 1;
+ if (nextIndex >= m_tabWidget.count())
+ nextIndex = 0;
+
+ m_tabWidget.setCurrentIndex(nextIndex);
+ emit navigateStateUpdate();
+}
+
+void TerminalPane::goToPrev()
+{
+ int prevIndex = m_tabWidget.currentIndex() - 1;
+ if (prevIndex < 0)
+ prevIndex = m_tabWidget.count() - 1;
+
+ m_tabWidget.setCurrentIndex(prevIndex);
+ emit navigateStateUpdate();
+}
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalpane.h b/src/plugins/terminal/terminalpane.h
new file mode 100644
index 0000000000..21f15168f2
--- /dev/null
+++ b/src/plugins/terminal/terminalpane.h
@@ -0,0 +1,82 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "terminalwidget.h"
+
+#include <coreplugin/ioutputpane.h>
+
+#include <utils/terminalhooks.h>
+
+#include <QAction>
+#include <QMenu>
+#include <QTabWidget>
+#include <QToolButton>
+
+namespace Terminal {
+
+class TerminalWidget;
+
+class TerminalPane : public Core::IOutputPane
+{
+ Q_OBJECT
+public:
+ TerminalPane(QObject *parent = nullptr);
+ ~TerminalPane() override;
+
+ QWidget *outputWidget(QWidget *parent) override;
+ QList<QWidget *> toolBarWidgets() const override;
+ QString displayName() const override;
+ int priorityInStatusBar() const override;
+ void clearContents() override;
+ void visibilityChanged(bool visible) override;
+ void setFocus() override;
+ bool hasFocus() const override;
+ bool canFocus() const override;
+ bool canNavigate() const override;
+ bool canNext() const override;
+ bool canPrevious() const override;
+ void goToNext() override;
+ void goToPrev() override;
+
+ void openTerminal(const Utils::Terminal::OpenTerminalParameters &parameters);
+ void addTerminal(TerminalWidget *terminal, const QString &title);
+
+ TerminalWidget *stoppedTerminalWithId(Utils::Id identifier) const;
+
+ void ensureVisible(TerminalWidget *terminal);
+
+private:
+ TerminalWidget *currentTerminal() const;
+
+ void removeTab(int index);
+ void setupTerminalWidget(TerminalWidget *terminal);
+ void initActions();
+ void createShellMenu();
+
+private:
+ QTabWidget m_tabWidget;
+
+ QToolButton *m_newTerminalButton{nullptr};
+ QToolButton *m_closeTerminalButton{nullptr};
+ QToolButton *m_openSettingsButton{nullptr};
+ QToolButton *m_escSettingButton{nullptr};
+
+ UnlockedGlobalAction m_minMax;
+ UnlockedGlobalAction m_locate;
+
+ QAction newTerminal;
+ QAction nextTerminal;
+ QAction prevTerminal;
+ QAction closeTerminal;
+
+ QMenu m_shellMenu;
+
+ Core::Context m_context;
+
+ bool m_widgetInitialized{false};
+ bool m_isVisible{false};
+};
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalplugin.cpp b/src/plugins/terminal/terminalplugin.cpp
new file mode 100644
index 0000000000..38b58b1722
--- /dev/null
+++ b/src/plugins/terminal/terminalplugin.cpp
@@ -0,0 +1,87 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "terminalpane.h"
+#include "terminalprocessimpl.h"
+#include "terminalsettings.h"
+#include "terminalwidget.h"
+
+#include <coreplugin/actionmanager/actioncontainer.h>
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/imode.h>
+#include <coreplugin/modemanager.h>
+
+#include <extensionsystem/iplugin.h>
+#include <extensionsystem/pluginmanager.h>
+
+#include <QAction>
+#include <QDebug>
+#include <QMenu>
+#include <QMessageBox>
+#include <QPushButton>
+
+namespace Terminal::Internal {
+
+class TerminalPlugin final : public ExtensionSystem::IPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Terminal.json")
+
+public:
+ TerminalPlugin() = default;
+
+ ~TerminalPlugin() final
+ {
+ ExtensionSystem::PluginManager::removeObject(m_terminalPane);
+ delete m_terminalPane;
+ m_terminalPane = nullptr;
+ }
+
+ void initialize() final { addManaged<TerminalSettings>(); }
+
+ void extensionsInitialized() final
+ {
+ m_terminalPane = new TerminalPane;
+ ExtensionSystem::PluginManager::addObject(m_terminalPane);
+
+ TerminalWidget::initActions();
+
+ auto enable = [this] {
+ Utils::Terminal::Hooks::instance()
+ .addCallbackSet("Internal",
+ {[this](const Utils::Terminal::OpenTerminalParameters &p) {
+ m_terminalPane->openTerminal(p);
+ },
+ [this] { return new TerminalProcessImpl(m_terminalPane); }});
+ };
+
+ auto disable = [] { Utils::Terminal::Hooks::instance().removeCallbackSet("Internal"); };
+
+ static bool isEnabled = false;
+ auto settingsChanged = [enable, disable] {
+ if (isEnabled != TerminalSettings::instance().enableTerminal()) {
+ isEnabled = TerminalSettings::instance().enableTerminal();
+ if (isEnabled)
+ enable();
+ else
+ disable();
+ }
+ };
+
+ QObject::connect(&TerminalSettings::instance(),
+ &Utils::AspectContainer::applied,
+ this,
+ settingsChanged);
+
+ settingsChanged();
+ }
+
+private:
+ TerminalPane *m_terminalPane{nullptr};
+};
+
+} // namespace Terminal::Internal
+
+#include "terminalplugin.moc"
diff --git a/src/plugins/terminal/terminalprocessimpl.cpp b/src/plugins/terminal/terminalprocessimpl.cpp
new file mode 100644
index 0000000000..9a7f7e7dcd
--- /dev/null
+++ b/src/plugins/terminal/terminalprocessimpl.cpp
@@ -0,0 +1,82 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "terminalprocessimpl.h"
+#include "terminalwidget.h"
+
+#include <utils/externalterminalprocessimpl.h>
+
+#include <QApplication>
+#include <QLocalServer>
+#include <QLocalSocket>
+#include <QLoggingCategory>
+#include <QTemporaryFile>
+#include <QTimer>
+
+Q_LOGGING_CATEGORY(terminalProcessLog, "qtc.terminal.stubprocess", QtDebugMsg)
+
+using namespace Utils;
+using namespace Utils::Terminal;
+
+namespace Terminal {
+
+class ProcessStubCreator : public StubCreator
+{
+public:
+ ProcessStubCreator(TerminalProcessImpl *interface, TerminalPane *terminalPane)
+ : m_terminalPane(terminalPane)
+ , m_process(interface)
+ , m_interface(interface)
+ {}
+
+ expected_str<qint64> startStubProcess(const ProcessSetupData &setup) override
+ {
+ if (QApplication::activeModalWidget()) {
+ m_fallbackStubCreator = std::make_unique<Utils::ProcessStubCreator>(m_interface);
+ return m_fallbackStubCreator->startStubProcess(setup);
+ }
+
+ const Id id = Id::fromString(setup.m_commandLine.executable().toUserOutput());
+
+ TerminalWidget *terminal = m_terminalPane->stoppedTerminalWithId(id);
+
+ const OpenTerminalParameters openParameters{setup.m_commandLine,
+ std::nullopt,
+ std::nullopt,
+ ExitBehavior::Keep,
+ id};
+
+ if (!terminal) {
+ terminal = new TerminalWidget(nullptr, openParameters);
+
+ terminal->setShellName(setup.m_commandLine.executable().fileName());
+ m_terminalPane->addTerminal(terminal, "App");
+ } else {
+ terminal->restart(openParameters);
+ }
+
+ m_terminalPane->ensureVisible(terminal);
+
+ connect(terminal, &TerminalWidget::destroyed, m_process, [process = m_process] {
+ if (process->inferiorProcessId())
+ process->emitFinished(-1, QProcess::CrashExit);
+ });
+
+ return 0;
+ }
+
+ TerminalPane *m_terminalPane;
+ TerminalProcessImpl *m_process;
+ TerminalInterface *m_interface;
+ std::unique_ptr<Utils::ProcessStubCreator> m_fallbackStubCreator;
+};
+
+TerminalProcessImpl::TerminalProcessImpl(TerminalPane *terminalPane)
+ : TerminalInterface(false)
+{
+ auto creator = new ProcessStubCreator(this, terminalPane);
+ creator->moveToThread(qApp->thread());
+ setStubCreator(creator);
+}
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalprocessimpl.h b/src/plugins/terminal/terminalprocessimpl.h
new file mode 100644
index 0000000000..f77f418281
--- /dev/null
+++ b/src/plugins/terminal/terminalprocessimpl.h
@@ -0,0 +1,18 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "terminalpane.h"
+
+#include <utils/terminalinterface.h>
+
+namespace Terminal {
+
+class TerminalProcessImpl : public Utils::TerminalInterface
+{
+public:
+ TerminalProcessImpl(TerminalPane *terminalPane);
+};
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalsearch.cpp b/src/plugins/terminal/terminalsearch.cpp
new file mode 100644
index 0000000000..69a412b194
--- /dev/null
+++ b/src/plugins/terminal/terminalsearch.cpp
@@ -0,0 +1,274 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "terminalsearch.h"
+
+#include <QElapsedTimer>
+#include <QLoggingCategory>
+#include <QRegularExpression>
+
+#include <chrono>
+
+Q_LOGGING_CATEGORY(terminalSearchLog, "qtc.terminal.search", QtWarningMsg)
+
+using namespace std::chrono_literals;
+
+namespace Terminal {
+
+using namespace Terminal::Internal;
+
+constexpr std::chrono::milliseconds debounceInterval = 100ms;
+
+TerminalSearch::TerminalSearch(TerminalSurface *surface)
+ : m_surface(surface)
+{
+ m_debounceTimer.setInterval(debounceInterval);
+ m_debounceTimer.setSingleShot(true);
+
+ connect(surface, &TerminalSurface::invalidated, this, &TerminalSearch::updateHits);
+ connect(&m_debounceTimer, &QTimer::timeout, this, &TerminalSearch::debouncedUpdateHits);
+}
+
+void TerminalSearch::setCurrentSelection(std::optional<SearchHitWithText> selection)
+{
+ m_currentSelection = selection;
+}
+
+void TerminalSearch::setSearchString(const QString &searchString, Core::FindFlags findFlags)
+{
+ if (m_currentSearchString != searchString || m_findFlags != findFlags) {
+ m_currentSearchString = searchString;
+ m_findFlags = findFlags;
+ updateHits();
+ }
+}
+
+void TerminalSearch::nextHit()
+{
+ if (m_hits.isEmpty())
+ return;
+
+ m_currentHit = (m_currentHit + 1) % m_hits.size();
+ emit currentHitChanged();
+}
+
+void TerminalSearch::previousHit()
+{
+ if (m_hits.isEmpty())
+ return;
+
+ m_currentHit = (m_currentHit - 1 + m_hits.size()) % m_hits.size();
+ emit currentHitChanged();
+}
+
+void TerminalSearch::updateHits()
+{
+ if (!m_hits.isEmpty()) {
+ m_hits.clear();
+ m_currentHit = -1;
+ emit hitsChanged();
+ emit currentHitChanged();
+ }
+
+ m_debounceTimer.start();
+}
+
+bool isSpace(char32_t a, char32_t b)
+{
+ if (a == std::numeric_limits<char32_t>::max())
+ return std::isspace(b);
+ else if (b == std::numeric_limits<char32_t>::max())
+ return std::isspace(a);
+
+ return false;
+}
+
+QList<SearchHit> TerminalSearch::search()
+{
+ QList<SearchHit> hits;
+
+ std::function<bool(char32_t, char32_t)> compare;
+
+ if (m_findFlags.testFlag(Core::FindFlag::FindCaseSensitively)) {
+ compare = [](char32_t a, char32_t b) { return a == b || isSpace(a, b); };
+ } else {
+ compare = [](char32_t a, char32_t b) {
+ return std::tolower(a) == std::tolower(b) || isSpace(a, b);
+ };
+ }
+
+ if (!m_currentSearchString.isEmpty()) {
+ const QList<uint> asUcs4 = m_currentSearchString.toUcs4();
+ std::u32string searchString(asUcs4.begin(), asUcs4.end());
+
+ if (m_findFlags.testFlag(Core::FindFlag::FindWholeWords)) {
+ searchString.push_back(std::numeric_limits<char32_t>::max());
+ searchString.insert(searchString.begin(), std::numeric_limits<char32_t>::max());
+ }
+
+ Internal::CellIterator it = m_surface->begin();
+ while (it != m_surface->end()) {
+ it = std::search(it, m_surface->end(), searchString.begin(), searchString.end(), compare);
+
+ if (it != m_surface->end()) {
+ auto hit = SearchHit{it.position(),
+ static_cast<int>(it.position() + searchString.size())};
+ if (m_findFlags.testFlag(Core::FindFlag::FindWholeWords)) {
+ hit.start++;
+ hit.end--;
+ }
+ hits << hit;
+ it += m_currentSearchString.size();
+ }
+ }
+ }
+ return hits;
+}
+
+QList<SearchHit> TerminalSearch::searchRegex()
+{
+ QList<SearchHit> hits;
+
+ QString allText;
+ allText.reserve(1000);
+
+ // Contains offsets at which there are characters > 2 bytes
+ QList<int> adjustTable;
+
+ for (auto it = m_surface->begin(); it != m_surface->end(); ++it) {
+ auto chs = QChar::fromUcs4(*it);
+ if (chs.size() > 1)
+ adjustTable << (allText.size());
+ allText += chs;
+ }
+
+ QRegularExpression re(m_currentSearchString,
+ m_findFlags.testFlag(Core::FindFlag::FindCaseSensitively)
+ ? QRegularExpression::NoPatternOption
+ : QRegularExpression::CaseInsensitiveOption);
+
+ QRegularExpressionMatchIterator it = re.globalMatch(allText);
+ int adjust = 0;
+ auto itAdjust = adjustTable.begin();
+ while (it.hasNext()) {
+ QRegularExpressionMatch match = it.next();
+ int s = match.capturedStart();
+ int e = match.capturedEnd();
+
+ // Update 'adjust' to account for characters > 2 bytes
+ if (itAdjust != adjustTable.end()) {
+ while (s > *itAdjust && itAdjust != adjustTable.end()) {
+ adjust++;
+ itAdjust++;
+ }
+ s -= adjust;
+ while (e > *itAdjust && itAdjust != adjustTable.end()) {
+ adjust++;
+ itAdjust++;
+ }
+ e -= adjust;
+ }
+ hits << SearchHit{s, e};
+ }
+
+ return hits;
+}
+
+void TerminalSearch::debouncedUpdateHits()
+{
+ QElapsedTimer t;
+ t.start();
+
+ m_currentHit = -1;
+
+ const bool regex = m_findFlags.testFlag(Core::FindFlag::FindRegularExpression);
+
+ QList<SearchHit> hits = regex ? searchRegex() : search();
+
+ if (hits != m_hits) {
+ m_currentHit = -1;
+ if (m_currentSelection)
+ m_currentHit = hits.indexOf(*m_currentSelection);
+
+ if (m_currentHit == -1 && !hits.isEmpty())
+ m_currentHit = 0;
+
+ m_hits = hits;
+ emit hitsChanged();
+ emit currentHitChanged();
+ emit changed();
+ }
+ if (!m_currentSearchString.isEmpty())
+ qCDebug(terminalSearchLog) << "Search took" << t.elapsed() << "ms";
+}
+
+Core::FindFlags TerminalSearch::supportedFindFlags() const
+{
+ return Core::FindFlag::FindCaseSensitively | Core::FindFlag::FindBackward
+ | Core::FindFlag::FindRegularExpression | Core::FindFlag::FindWholeWords;
+}
+
+void TerminalSearch::resetIncrementalSearch()
+{
+ m_currentSelection.reset();
+}
+
+void TerminalSearch::clearHighlights()
+{
+ setSearchString("", {});
+}
+
+QString TerminalSearch::currentFindString() const
+{
+ if (m_currentSelection)
+ return m_currentSelection->text;
+ else
+ return m_currentSearchString;
+}
+
+QString TerminalSearch::completedFindString() const
+{
+ return {};
+}
+
+Core::IFindSupport::Result TerminalSearch::findIncremental(const QString &txt,
+ Core::FindFlags findFlags)
+{
+ if (txt == m_currentSearchString) {
+ if (m_debounceTimer.isActive())
+ return Result::NotYetFound;
+ else if (m_hits.isEmpty())
+ return Result::NotFound;
+ else
+ return Result::Found;
+ }
+
+ setSearchString(txt, findFlags);
+ return Result::NotYetFound;
+}
+
+Core::IFindSupport::Result TerminalSearch::findStep(const QString &txt, Core::FindFlags findFlags)
+{
+ if (txt == m_currentSearchString) {
+ if (m_debounceTimer.isActive())
+ return Result::NotYetFound;
+ else if (m_hits.isEmpty())
+ return Result::NotFound;
+
+ if (findFlags.testFlag(Core::FindFlag::FindBackward))
+ previousHit();
+ else
+ nextHit();
+
+ return Result::Found;
+ }
+
+ return findIncremental(txt, findFlags);
+}
+
+void TerminalSearch::highlightAll(const QString &txt, Core::FindFlags findFlags)
+{
+ setSearchString(txt, findFlags);
+}
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalsearch.h b/src/plugins/terminal/terminalsearch.h
new file mode 100644
index 0000000000..3daa05c04d
--- /dev/null
+++ b/src/plugins/terminal/terminalsearch.h
@@ -0,0 +1,82 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "terminalsurface.h"
+
+#include <coreplugin/find/ifindsupport.h>
+#include <coreplugin/find/textfindconstants.h>
+
+#include <QTimer>
+
+namespace Terminal {
+
+struct SearchHit
+{
+ int start{-1};
+ int end{-1};
+
+ bool operator!=(const SearchHit &other) const
+ {
+ return start != other.start || end != other.end;
+ }
+ bool operator==(const SearchHit &other) const { return !operator!=(other); }
+};
+
+struct SearchHitWithText : SearchHit
+{
+ QString text;
+};
+
+class TerminalSearch : public Core::IFindSupport
+{
+ Q_OBJECT
+public:
+ TerminalSearch(Internal::TerminalSurface *surface);
+
+ void setCurrentSelection(std::optional<SearchHitWithText> selection);
+ void setSearchString(const QString &searchString, Core::FindFlags findFlags);
+ void nextHit();
+ void previousHit();
+
+ const QList<SearchHit> &hits() const { return m_hits; }
+ SearchHit currentHit() const
+ {
+ return m_currentHit >= 0 ? m_hits.at(m_currentHit) : SearchHit{};
+ }
+
+public:
+ bool supportsReplace() const override { return false; }
+ Core::FindFlags supportedFindFlags() const override;
+ void resetIncrementalSearch() override;
+ void clearHighlights() override;
+ QString currentFindString() const override;
+ QString completedFindString() const override;
+ Result findIncremental(const QString &txt, Core::FindFlags findFlags) override;
+ Result findStep(const QString &txt, Core::FindFlags findFlags) override;
+
+ void highlightAll(const QString &, Core::FindFlags) override;
+
+signals:
+ void hitsChanged();
+ void currentHitChanged();
+
+protected:
+ void updateHits();
+ void debouncedUpdateHits();
+ QList<SearchHit> search();
+ QList<SearchHit> searchRegex();
+
+private:
+ std::optional<SearchHitWithText> m_currentSelection;
+ QString m_currentSearchString;
+ Core::FindFlags m_findFlags;
+ Internal::TerminalSurface *m_surface;
+
+ int m_currentHit{-1};
+ QList<SearchHit> m_hits;
+ QTimer m_debounceTimer;
+};
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalsettings.cpp b/src/plugins/terminal/terminalsettings.cpp
new file mode 100644
index 0000000000..06903c4d4c
--- /dev/null
+++ b/src/plugins/terminal/terminalsettings.cpp
@@ -0,0 +1,591 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "terminalsettings.h"
+
+#include "terminaltr.h"
+
+#include <coreplugin/icore.h>
+
+#include <utils/dropsupport.h>
+#include <utils/environment.h>
+#include <utils/expected.h>
+#include <utils/fileutils.h>
+#include <utils/hostosinfo.h>
+#include <utils/layoutbuilder.h>
+#include <utils/pathchooser.h>
+#include <utils/theme/theme.h>
+
+#include <QFontComboBox>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QMessageBox>
+#include <QPushButton>
+#include <QRegularExpression>
+#include <QTemporaryFile>
+#include <QXmlStreamReader>
+
+using namespace Utils;
+
+namespace Terminal {
+
+static QString defaultFontFamily()
+{
+ if (HostOsInfo::isMacHost())
+ return QLatin1String("Menlo");
+
+ if (Utils::HostOsInfo::isAnyUnixHost())
+ return QLatin1String("Monospace");
+
+ return QLatin1String("Consolas");
+}
+
+static int defaultFontSize()
+{
+ if (Utils::HostOsInfo::isMacHost())
+ return 12;
+ if (Utils::HostOsInfo::isAnyUnixHost())
+ return 9;
+ return 10;
+}
+
+static QString defaultShell()
+{
+ if (HostOsInfo::isWindowsHost())
+ return qtcEnvironmentVariable("COMSPEC");
+
+ QString defaultShell = qtcEnvironmentVariable("SHELL");
+ if (FilePath::fromUserInput(defaultShell).isExecutableFile())
+ return defaultShell;
+
+ Utils::FilePath shPath = Utils::Environment::systemEnvironment().searchInPath("sh");
+ return shPath.nativePath();
+}
+
+void setupColor(TerminalSettings *settings,
+ ColorAspect &color,
+ const QString &label,
+ const QColor &defaultColor)
+{
+ color.setSettingsKey(label);
+ color.setDefaultValue(defaultColor);
+ color.setToolTip(Tr::tr("The color used for %1.").arg(label));
+
+ settings->registerAspect(&color);
+}
+
+static expected_str<void> loadXdefaults(const FilePath &path)
+{
+ const expected_str<QByteArray> readResult = path.fileContents();
+ if (!readResult)
+ return make_unexpected(readResult.error());
+
+ QRegularExpression re(R"(.*\*(color[0-9]{1,2}|foreground|background):\s*(#[0-9a-f]{6}))");
+
+ for (const QByteArray &line : readResult->split('\n')) {
+ if (line.trimmed().startsWith('!'))
+ continue;
+
+ const auto match = re.match(QString::fromUtf8(line));
+ if (match.hasMatch()) {
+ const QString colorName = match.captured(1);
+ const QColor color(match.captured(2));
+ if (colorName == "foreground") {
+ TerminalSettings::instance().foregroundColor.setVolatileValue(color);
+ } else if (colorName == "background") {
+ TerminalSettings::instance().backgroundColor.setVolatileValue(color);
+ } else {
+ const int colorIndex = colorName.mid(5).toInt();
+ if (colorIndex >= 0 && colorIndex < 16)
+ TerminalSettings::instance().colors[colorIndex].setVolatileValue(color);
+ }
+ }
+ }
+
+ return {};
+}
+
+static expected_str<void> loadItermColors(const FilePath &path)
+{
+ QFile f(path.toFSPathString());
+ const bool opened = f.open(QIODevice::ReadOnly);
+ if (!opened)
+ return make_unexpected(Tr::tr("Failed to open file"));
+
+ QXmlStreamReader reader(&f);
+ while (!reader.atEnd() && reader.readNextStartElement()) {
+ if (reader.name() == u"plist") {
+ while (!reader.atEnd() && reader.readNextStartElement()) {
+ if (reader.name() == u"dict") {
+ QString colorName;
+ while (!reader.atEnd() && reader.readNextStartElement()) {
+ if (reader.name() == u"key") {
+ colorName = reader.readElementText();
+ } else if (reader.name() == u"dict") {
+ QColor color;
+ int component = 0;
+ while (!reader.atEnd() && reader.readNextStartElement()) {
+ if (reader.name() == u"key") {
+ const auto &text = reader.readElementText();
+ if (text == u"Red Component")
+ component = 0;
+ else if (text == u"Green Component")
+ component = 1;
+ else if (text == u"Blue Component")
+ component = 2;
+ else if (text == u"Alpha Component")
+ component = 3;
+ } else if (reader.name() == u"real") {
+ // clang-format off
+ switch (component) {
+ case 0: color.setRedF(reader.readElementText().toDouble()); break;
+ case 1: color.setGreenF(reader.readElementText().toDouble()); break;
+ case 2: color.setBlueF(reader.readElementText().toDouble()); break;
+ case 3: color.setAlphaF(reader.readElementText().toDouble()); break;
+ }
+ // clang-format on
+ } else {
+ reader.skipCurrentElement();
+ }
+ }
+
+ if (colorName.startsWith("Ansi")) {
+ const auto c = colorName.mid(5, 2);
+ const int colorIndex = c.toInt();
+ if (colorIndex >= 0 && colorIndex < 16)
+ TerminalSettings::instance().colors[colorIndex].setVolatileValue(
+ color);
+ } else if (colorName == "Foreground Color") {
+ TerminalSettings::instance().foregroundColor.setVolatileValue(color);
+ } else if (colorName == "Background Color") {
+ TerminalSettings::instance().backgroundColor.setVolatileValue(color);
+ } else if (colorName == "Selection Color") {
+ TerminalSettings::instance().selectionColor.setVolatileValue(color);
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ if (reader.hasError())
+ return make_unexpected(reader.errorString());
+
+ return {};
+}
+
+static expected_str<void> loadVsCodeColors(const FilePath &path)
+{
+ const expected_str<QByteArray> readResult = path.fileContents();
+ if (!readResult)
+ return make_unexpected(readResult.error());
+
+ QJsonParseError error;
+ QJsonDocument doc = QJsonDocument::fromJson(*readResult, &error);
+ if (error.error != QJsonParseError::NoError)
+ return make_unexpected(Tr::tr("JSON parsing error: \"%1\", at offset: %2")
+ .arg(error.errorString())
+ .arg(error.offset));
+
+ const QJsonObject root = doc.object();
+ const auto itColors = root.find("colors");
+ if (itColors == root.end())
+ return make_unexpected(Tr::tr("No colors found"));
+
+ const QJsonObject colors = itColors->toObject();
+
+ // clang-format off
+ const QList<QPair<QStringView, ColorAspect *>> colorKeys = {
+ qMakePair(u"editor.background", &TerminalSettings::instance().backgroundColor),
+ qMakePair(u"terminal.foreground", &TerminalSettings::instance().foregroundColor),
+ qMakePair(u"terminal.selectionBackground", &TerminalSettings::instance().selectionColor),
+
+ qMakePair(u"terminal.ansiBlack", &TerminalSettings::instance().colors[0]),
+ qMakePair(u"terminal.ansiBrightBlack", &TerminalSettings::instance().colors[8]),
+
+ qMakePair(u"terminal.ansiRed", &TerminalSettings::instance().colors[1]),
+ qMakePair(u"terminal.ansiBrightRed", &TerminalSettings::instance().colors[9]),
+
+ qMakePair(u"terminal.ansiGreen", &TerminalSettings::instance().colors[2]),
+ qMakePair(u"terminal.ansiBrightGreen", &TerminalSettings::instance().colors[10]),
+
+ qMakePair(u"terminal.ansiYellow", &TerminalSettings::instance().colors[3]),
+ qMakePair(u"terminal.ansiBrightYellow", &TerminalSettings::instance().colors[11]),
+
+ qMakePair(u"terminal.ansiBlue", &TerminalSettings::instance().colors[4]),
+ qMakePair(u"terminal.ansiBrightBlue", &TerminalSettings::instance().colors[12]),
+
+ qMakePair(u"terminal.ansiMagenta", &TerminalSettings::instance().colors[5]),
+ qMakePair(u"terminal.ansiBrightMagenta", &TerminalSettings::instance().colors[13]),
+
+ qMakePair(u"terminal.ansiCyan", &TerminalSettings::instance().colors[6]),
+ qMakePair(u"terminal.ansiBrightCyan", &TerminalSettings::instance().colors[14]),
+
+ qMakePair(u"terminal.ansiWhite", &TerminalSettings::instance().colors[7]),
+ qMakePair(u"terminal.ansiBrightWhite", &TerminalSettings::instance().colors[15])
+ };
+ // clang-format on
+
+ for (const auto &pair : colorKeys) {
+ const auto it = colors.find(pair.first);
+ if (it != colors.end()) {
+ const QString colorString = it->toString();
+ if (colorString.startsWith("#")) {
+ QColor color(colorString.mid(0, 7));
+ if (colorString.size() > 7) {
+ int alpha = colorString.mid(7).toInt(nullptr, 16);
+ color.setAlpha(alpha);
+ }
+ if (color.isValid())
+ pair.second->setVolatileValue(color);
+ }
+ }
+ }
+
+ return {};
+}
+
+static expected_str<void> loadKonsoleColorScheme(const FilePath &path)
+{
+ QSettings settings(path.toFSPathString(), QSettings::IniFormat);
+
+ auto parseColor = [](const QStringList &parts) -> expected_str<QColor> {
+ if (parts.size() != 3 && parts.size() != 4)
+ return make_unexpected(Tr::tr("Invalid color format"));
+ int alpha = parts.size() == 4 ? parts[3].toInt() : 255;
+ return QColor(parts[0].toInt(), parts[1].toInt(), parts[2].toInt(), alpha);
+ };
+
+ // clang-format off
+ const QList<QPair<QString, ColorAspect *>> colorKeys = {
+ qMakePair(QLatin1String("Background/Color"), &TerminalSettings::instance().backgroundColor),
+ qMakePair(QLatin1String("Foreground/Color"), &TerminalSettings::instance().foregroundColor),
+
+ qMakePair(QLatin1String("Color0/Color"), &TerminalSettings::instance().colors[0]),
+ qMakePair(QLatin1String("Color0Intense/Color"), &TerminalSettings::instance().colors[8]),
+
+ qMakePair(QLatin1String("Color1/Color"), &TerminalSettings::instance().colors[1]),
+ qMakePair(QLatin1String("Color1Intense/Color"), &TerminalSettings::instance().colors[9]),
+
+ qMakePair(QLatin1String("Color2/Color"), &TerminalSettings::instance().colors[2]),
+ qMakePair(QLatin1String("Color2Intense/Color"), &TerminalSettings::instance().colors[10]),
+
+ qMakePair(QLatin1String("Color3/Color"), &TerminalSettings::instance().colors[3]),
+ qMakePair(QLatin1String("Color3Intense/Color"), &TerminalSettings::instance().colors[11]),
+
+ qMakePair(QLatin1String("Color4/Color"), &TerminalSettings::instance().colors[4]),
+ qMakePair(QLatin1String("Color4Intense/Color"), &TerminalSettings::instance().colors[12]),
+
+ qMakePair(QLatin1String("Color5/Color"), &TerminalSettings::instance().colors[5]),
+ qMakePair(QLatin1String("Color5Intense/Color"), &TerminalSettings::instance().colors[13]),
+
+ qMakePair(QLatin1String("Color6/Color"), &TerminalSettings::instance().colors[6]),
+ qMakePair(QLatin1String("Color6Intense/Color"), &TerminalSettings::instance().colors[14]),
+
+ qMakePair(QLatin1String("Color7/Color"), &TerminalSettings::instance().colors[7]),
+ qMakePair(QLatin1String("Color7Intense/Color"), &TerminalSettings::instance().colors[15])
+ };
+ // clang-format on
+
+ for (const auto &colorKey : colorKeys) {
+ if (settings.contains(colorKey.first)) {
+ const auto color = parseColor(settings.value(colorKey.first).toStringList());
+ if (!color)
+ return make_unexpected(color.error());
+
+ colorKey.second->setVolatileValue(*color);
+ }
+ }
+
+ return {};
+}
+
+static expected_str<void> loadXFCE4ColorScheme(const FilePath &path)
+{
+ expected_str<QByteArray> arr = path.fileContents();
+ if (!arr)
+ return make_unexpected(arr.error());
+
+ arr->replace(';', ',');
+
+ QTemporaryFile f;
+ f.open();
+ f.write(*arr);
+ f.close();
+
+ QSettings settings(f.fileName(), QSettings::IniFormat);
+
+ // clang-format off
+ const QList<QPair<QString, ColorAspect *>> colorKeys = {
+ qMakePair(QLatin1String("Scheme/ColorBackground"), &TerminalSettings::instance().backgroundColor),
+ qMakePair(QLatin1String("Scheme/ColorForeground"), &TerminalSettings::instance().foregroundColor),
+ };
+ // clang-format on
+
+ for (const auto &colorKey : colorKeys) {
+ if (settings.contains(colorKey.first)) {
+ colorKey.second->setVolatileValue(QColor(settings.value(colorKey.first).toString()));
+ }
+ }
+
+ QStringList colors = settings.value(QLatin1String("Scheme/ColorPalette")).toStringList();
+ int i = 0;
+ for (const auto &color : colors) {
+ TerminalSettings::instance().colors[i++].setVolatileValue(QColor(color));
+ }
+
+ return {};
+}
+
+static expected_str<void> loadColorScheme(const FilePath &path)
+{
+ if (path.endsWith("Xdefaults"))
+ return loadXdefaults(path);
+ else if (path.suffix() == "itermcolors")
+ return loadItermColors(path);
+ else if (path.suffix() == "json")
+ return loadVsCodeColors(path);
+ else if (path.suffix() == "colorscheme")
+ return loadKonsoleColorScheme(path);
+ else if (path.suffix() == "theme" || path.completeSuffix() == "theme.txt")
+ return loadXFCE4ColorScheme(path);
+
+ return make_unexpected(Tr::tr("Unknown color scheme format"));
+}
+
+static TerminalSettings *s_instance;
+
+TerminalSettings &TerminalSettings::instance()
+{
+ return *s_instance;
+}
+
+TerminalSettings::TerminalSettings()
+{
+ s_instance = this;
+
+ setSettingsGroup("Terminal");
+ setId("Terminal.General");
+ setDisplayName("Terminal");
+ setCategory("ZY.Terminal");
+ setDisplayCategory("Terminal");
+ setCategoryIconPath(":/terminal/images/settingscategory_terminal.png");
+
+ enableTerminal.setSettingsKey("EnableTerminal");
+ enableTerminal.setLabelText(Tr::tr("Use internal terminal"));
+ enableTerminal.setToolTip(
+ Tr::tr("If enabled, use the internal terminal when \"Run In Terminal\" is "
+ "enabled and for \"Open Terminal here\"."));
+ enableTerminal.setDefaultValue(true);
+
+ font.setSettingsKey("FontFamily");
+ font.setLabelText(Tr::tr("Family:"));
+ font.setHistoryCompleter("Terminal.Fonts.History");
+ font.setToolTip(Tr::tr("The font family used in the terminal."));
+ font.setDefaultValue(defaultFontFamily());
+
+ fontSize.setSettingsKey("FontSize");
+ fontSize.setLabelText(Tr::tr("Size:"));
+ fontSize.setToolTip(Tr::tr("The font size used in the terminal. (in points)"));
+ fontSize.setDefaultValue(defaultFontSize());
+ fontSize.setRange(1, 100);
+
+ allowBlinkingCursor.setSettingsKey("AllowBlinkingCursor");
+ allowBlinkingCursor.setLabelText(Tr::tr("Allow blinking cursor"));
+ allowBlinkingCursor.setToolTip(Tr::tr("Allow the cursor to blink."));
+ allowBlinkingCursor.setDefaultValue(false);
+
+ shell.setSettingsKey("ShellPath");
+ shell.setLabelText(Tr::tr("Shell path:"));
+ shell.setExpectedKind(PathChooser::ExistingCommand);
+ shell.setHistoryCompleter("Terminal.Shell.History");
+ shell.setToolTip(Tr::tr("The shell executable to be started."));
+ shell.setDefaultValue(defaultShell());
+
+ shellArguments.setSettingsKey("ShellArguments");
+ shellArguments.setLabelText(Tr::tr("Shell arguments:"));
+ shellArguments.setDisplayStyle(StringAspect::LineEditDisplay);
+ shellArguments.setHistoryCompleter("Terminal.Shell.History");
+ shellArguments.setToolTip(Tr::tr("The arguments to be passed to the shell."));
+ if (!HostOsInfo::isWindowsHost())
+ shellArguments.setDefaultValue(QString("-l"));
+
+ sendEscapeToTerminal.setSettingsKey("SendEscapeToTerminal");
+ sendEscapeToTerminal.setLabelText(Tr::tr("Send escape key to terminal"));
+ sendEscapeToTerminal.setToolTip(
+ Tr::tr("If enabled, pressing the escape key will send it to the terminal "
+ "instead of closing the terminal."));
+ sendEscapeToTerminal.setDefaultValue(false);
+
+ audibleBell.setSettingsKey("AudibleBell");
+ audibleBell.setLabelText(Tr::tr("Audible bell"));
+ audibleBell.setToolTip(Tr::tr("If enabled, the terminal will beep when a bell "
+ "character is received."));
+ audibleBell.setDefaultValue(true);
+
+ setupColor(this,
+ foregroundColor,
+ "Foreground",
+ Utils::creatorTheme()->color(Theme::TerminalForeground));
+ setupColor(this,
+ backgroundColor,
+ "Background",
+ Utils::creatorTheme()->color(Theme::TerminalBackground));
+ setupColor(this,
+ selectionColor,
+ "Selection",
+ Utils::creatorTheme()->color(Theme::TerminalSelection));
+
+ setupColor(this,
+ findMatchColor,
+ "Find matches",
+ Utils::creatorTheme()->color(Theme::TerminalFindMatch));
+
+ setupColor(this, colors[0], "0", Utils::creatorTheme()->color(Theme::TerminalAnsi0));
+ setupColor(this, colors[8], "8", Utils::creatorTheme()->color(Theme::TerminalAnsi8));
+
+ setupColor(this, colors[1], "1", Utils::creatorTheme()->color(Theme::TerminalAnsi1));
+ setupColor(this, colors[9], "9", Utils::creatorTheme()->color(Theme::TerminalAnsi9));
+
+ setupColor(this, colors[2], "2", Utils::creatorTheme()->color(Theme::TerminalAnsi2));
+ setupColor(this, colors[10], "10", Utils::creatorTheme()->color(Theme::TerminalAnsi10));
+
+ setupColor(this, colors[3], "3", Utils::creatorTheme()->color(Theme::TerminalAnsi3));
+ setupColor(this, colors[11], "11", Utils::creatorTheme()->color(Theme::TerminalAnsi11));
+
+ setupColor(this, colors[4], "4", Utils::creatorTheme()->color(Theme::TerminalAnsi4));
+ setupColor(this, colors[12], "12", Utils::creatorTheme()->color(Theme::TerminalAnsi12));
+
+ setupColor(this, colors[5], "5", Utils::creatorTheme()->color(Theme::TerminalAnsi5));
+ setupColor(this, colors[13], "13", Utils::creatorTheme()->color(Theme::TerminalAnsi13));
+
+ setupColor(this, colors[6], "6", Utils::creatorTheme()->color(Theme::TerminalAnsi6));
+ setupColor(this, colors[14], "14", Utils::creatorTheme()->color(Theme::TerminalAnsi14));
+
+ setupColor(this, colors[7], "7", Utils::creatorTheme()->color(Theme::TerminalAnsi7));
+ setupColor(this, colors[15], "15", Utils::creatorTheme()->color(Theme::TerminalAnsi15));
+
+ setLayouter([this] {
+ using namespace Layouting;
+
+ QFontComboBox *fontComboBox = new QFontComboBox;
+ fontComboBox->setFontFilters(QFontComboBox::MonospacedFonts);
+ fontComboBox->setCurrentFont(font());
+
+ connect(fontComboBox, &QFontComboBox::currentFontChanged, this, [this](const QFont &f) {
+ font.setValue(f.family());
+ });
+
+ auto loadThemeButton = new QPushButton(Tr::tr("Load Theme..."));
+ auto resetTheme = new QPushButton(Tr::tr("Reset Theme"));
+
+ connect(loadThemeButton, &QPushButton::clicked, this, [] {
+ const FilePath path = FileUtils::getOpenFilePath(
+ Core::ICore::dialogParent(),
+ "Open Theme",
+ {},
+ "All Scheme formats (*.itermcolors *.json *.colorscheme *.theme *.theme.txt);;"
+ "Xdefaults (.Xdefaults Xdefaults);;"
+ "iTerm Color Schemes(*.itermcolors);;"
+ "VS Code Color Schemes(*.json);;"
+ "Konsole Color Schemes(*.colorscheme);;"
+ "XFCE4 Terminal Color Schemes(*.theme *.theme.txt);;"
+ "All files (*)",
+ nullptr,
+ {},
+ true,
+ false);
+
+ if (path.isEmpty())
+ return;
+
+ const expected_str<void> result = loadColorScheme(path);
+ if (!result)
+ QMessageBox::warning(Core::ICore::dialogParent(), Tr::tr("Error"), result.error());
+ });
+
+ connect(resetTheme, &QPushButton::clicked, this, [this] {
+ foregroundColor.setVolatileValue(foregroundColor.defaultValue());
+ backgroundColor.setVolatileValue(backgroundColor.defaultValue());
+ selectionColor.setVolatileValue(selectionColor.defaultValue());
+
+ for (ColorAspect &color : colors)
+ color.setVolatileValue(color.defaultValue());
+ });
+
+// FIXME: Implement and use a Layouting::DropArea item
+
+// DropSupport *dropSupport = new DropSupport;
+// connect(dropSupport,
+// &DropSupport::filesDropped,
+// this,
+// [this](const QList<DropSupport::FileSpec> &files) {
+// if (files.size() != 1)
+// return;
+
+// const expected_str<void> result = loadColorScheme(files.at(0).filePath);
+// if (!result)
+// QMessageBox::warning(Core::ICore::dialogParent(), Tr::tr("Error"), result.error());
+// });
+
+ // clang-format off
+ return Column {
+ Group {
+ title(Tr::tr("General")),
+ Column {
+ enableTerminal, st,
+ sendEscapeToTerminal, st,
+ audibleBell, st,
+ allowBlinkingCursor, st,
+ },
+ },
+ Group {
+ title(Tr::tr("Font")),
+ Row {
+ font.labelText(), fontComboBox, Space(20),
+ fontSize, st,
+ },
+ },
+ Group {
+ title(Tr::tr("Colors")),
+ Column {
+ Row {
+ Tr::tr("Foreground"), foregroundColor, st,
+ Tr::tr("Background"), backgroundColor, st,
+ Tr::tr("Selection"), selectionColor, st,
+ Tr::tr("Find match"), findMatchColor, st,
+ },
+ Row {
+ colors[0], colors[1],
+ colors[2], colors[3],
+ colors[4], colors[5],
+ colors[6], colors[7]
+ },
+ Row {
+ colors[8], colors[9],
+ colors[10], colors[11],
+ colors[12], colors[13],
+ colors[14], colors[15]
+ },
+ Row {
+ loadThemeButton, resetTheme, st,
+ }
+ },
+ },
+ Group {
+ title(Tr::tr("Default Shell")),
+ Column {
+ shell,
+ shellArguments,
+ },
+ },
+ st,
+ };
+ // clang-format on
+ });
+
+ readSettings();
+}
+
+} // Terminal
diff --git a/src/plugins/terminal/terminalsettings.h b/src/plugins/terminal/terminalsettings.h
new file mode 100644
index 0000000000..4550bbce2a
--- /dev/null
+++ b/src/plugins/terminal/terminalsettings.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <coreplugin/dialogs/ioptionspage.h>
+
+namespace Terminal {
+
+class TerminalSettings : public Core::PagedSettings
+{
+public:
+ TerminalSettings();
+
+ static TerminalSettings &instance();
+
+ Utils::BoolAspect enableTerminal{this};
+
+ Utils::StringAspect font{this};
+ Utils::IntegerAspect fontSize{this};
+ Utils::FilePathAspect shell{this};
+ Utils::StringAspect shellArguments{this};
+
+ Utils::ColorAspect foregroundColor;
+ Utils::ColorAspect backgroundColor;
+ Utils::ColorAspect selectionColor;
+ Utils::ColorAspect findMatchColor;
+
+ Utils::ColorAspect colors[16];
+
+ Utils::BoolAspect allowBlinkingCursor{this};
+
+ Utils::BoolAspect sendEscapeToTerminal{this};
+ Utils::BoolAspect audibleBell{this};
+};
+
+} // Terminal
diff --git a/src/plugins/terminal/terminalsurface.cpp b/src/plugins/terminal/terminalsurface.cpp
new file mode 100644
index 0000000000..ea99430231
--- /dev/null
+++ b/src/plugins/terminal/terminalsurface.cpp
@@ -0,0 +1,531 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "terminalsurface.h"
+
+#include "keys.h"
+#include "scrollback.h"
+
+#include <utils/qtcassert.h>
+
+#include <vterm.h>
+
+#include <QLoggingCategory>
+
+namespace Terminal::Internal {
+
+Q_LOGGING_CATEGORY(log, "qtc.terminal.surface", QtWarningMsg);
+
+QColor toQColor(const VTermColor &c)
+{
+ return QColor(qRgb(c.rgb.red, c.rgb.green, c.rgb.blue));
+};
+
+struct TerminalSurfacePrivate
+{
+ TerminalSurfacePrivate(TerminalSurface *surface,
+ const QSize &initialGridSize,
+ ShellIntegration *shellIntegration)
+ : m_vterm(vterm_new(initialGridSize.height(), initialGridSize.width()), vterm_free)
+ , m_vtermScreen(vterm_obtain_screen(m_vterm.get()))
+ , m_scrollback(std::make_unique<Internal::Scrollback>(5000))
+ , m_shellIntegration(shellIntegration)
+ , q(surface)
+ {}
+
+ void init()
+ {
+ vterm_set_utf8(m_vterm.get(), true);
+
+ static auto writeToPty = [](const char *s, size_t len, void *user) {
+ auto p = static_cast<TerminalSurfacePrivate *>(user);
+ emit p->q->writeToPty(QByteArray(s, static_cast<int>(len)));
+ };
+
+ vterm_output_set_callback(m_vterm.get(), writeToPty, this);
+
+ memset(&m_vtermScreenCallbacks, 0, sizeof(m_vtermScreenCallbacks));
+
+ m_vtermScreenCallbacks.damage = [](VTermRect rect, void *user) {
+ auto p = static_cast<TerminalSurfacePrivate *>(user);
+ p->invalidate(rect);
+ return 1;
+ };
+ m_vtermScreenCallbacks.sb_pushline = [](int cols, const VTermScreenCell *cells, void *user) {
+ auto p = static_cast<TerminalSurfacePrivate *>(user);
+ return p->sb_pushline(cols, cells);
+ };
+ m_vtermScreenCallbacks.sb_popline = [](int cols, VTermScreenCell *cells, void *user) {
+ auto p = static_cast<TerminalSurfacePrivate *>(user);
+ return p->sb_popline(cols, cells);
+ };
+ m_vtermScreenCallbacks.settermprop = [](VTermProp prop, VTermValue *val, void *user) {
+ auto p = static_cast<TerminalSurfacePrivate *>(user);
+ return p->setTerminalProperties(prop, val);
+ };
+ m_vtermScreenCallbacks.movecursor =
+ [](VTermPos pos, VTermPos oldpos, int visible, void *user) {
+ auto p = static_cast<TerminalSurfacePrivate *>(user);
+ return p->movecursor(pos, oldpos, visible);
+ };
+ m_vtermScreenCallbacks.sb_clear = [](void *user) {
+ auto p = static_cast<TerminalSurfacePrivate *>(user);
+ return p->sb_clear();
+ };
+ m_vtermScreenCallbacks.bell = [](void *user) {
+ auto p = static_cast<TerminalSurfacePrivate *>(user);
+ emit p->q->bell();
+ return 1;
+ };
+
+ vterm_screen_set_callbacks(m_vtermScreen, &m_vtermScreenCallbacks, this);
+ vterm_screen_set_damage_merge(m_vtermScreen, VTERM_DAMAGE_SCROLL);
+ vterm_screen_enable_altscreen(m_vtermScreen, true);
+
+ memset(&m_vtermStateFallbacks, 0, sizeof(m_vtermStateFallbacks));
+
+ m_vtermStateFallbacks.osc = [](int cmd, VTermStringFragment fragment, void *user) {
+ auto p = static_cast<TerminalSurfacePrivate *>(user);
+ return p->osc(cmd, fragment);
+ };
+
+ VTermState *vts = vterm_obtain_state(m_vterm.get());
+ vterm_state_set_unrecognised_fallbacks(vts, &m_vtermStateFallbacks, this);
+ vterm_state_set_bold_highbright(vts, true);
+
+ VTermColor fg;
+ VTermColor bg;
+ vterm_color_indexed(&fg, ColorIndex::Foreground);
+ vterm_color_indexed(&bg, ColorIndex::Background);
+ vterm_state_set_default_colors(vts, &fg, &bg);
+
+ for (int i = 0; i < 16; ++i) {
+ VTermColor col;
+ vterm_color_indexed(&col, i);
+ vterm_state_set_palette_color(vts, i, &col);
+ }
+
+ vterm_screen_reset(m_vtermScreen, 1);
+ }
+
+ QSize liveSize() const
+ {
+ int rows;
+ int cols;
+ vterm_get_size(m_vterm.get(), &rows, &cols);
+
+ return QSize(cols, rows);
+ }
+
+ std::variant<int, QColor> toVariantColor(const VTermColor &color)
+ {
+ if (color.type & VTERM_COLOR_DEFAULT_BG)
+ return ColorIndex::Background;
+ else if (color.type & VTERM_COLOR_DEFAULT_FG)
+ return ColorIndex::Foreground;
+ else if (color.type & VTERM_COLOR_INDEXED) {
+ if (color.indexed.idx >= 16) {
+ VTermColor c = color;
+ vterm_state_convert_color_to_rgb(vterm_obtain_state(m_vterm.get()), &c);
+ return toQColor(c);
+ }
+ return color.indexed.idx;
+ } else if (color.type == VTERM_COLOR_RGB)
+ return toQColor(color);
+ else
+ return -1;
+ }
+
+ TerminalCell toCell(const VTermScreenCell &cell)
+ {
+ TerminalCell result;
+ result.width = cell.width;
+ result.text = QString::fromUcs4(cell.chars);
+
+ const VTermColor *bg = &cell.bg;
+ const VTermColor *fg = &cell.fg;
+
+ if (static_cast<bool>(cell.attrs.reverse))
+ std::swap(fg, bg);
+
+ result.backgroundColor = toVariantColor(*bg);
+ result.foregroundColor = toVariantColor(*fg);
+
+ result.bold = cell.attrs.bold;
+ result.strikeOut = cell.attrs.strike;
+
+ if (cell.attrs.underline > 0) {
+ result.underlineStyle = QTextCharFormat::NoUnderline;
+ switch (cell.attrs.underline) {
+ case VTERM_UNDERLINE_SINGLE:
+ result.underlineStyle = QTextCharFormat::SingleUnderline;
+ break;
+ case VTERM_UNDERLINE_DOUBLE:
+ // TODO: Double underline
+ result.underlineStyle = QTextCharFormat::SingleUnderline;
+ break;
+ case VTERM_UNDERLINE_CURLY:
+ result.underlineStyle = QTextCharFormat::WaveUnderline;
+ break;
+ case VTERM_UNDERLINE_DASHED:
+ result.underlineStyle = QTextCharFormat::DashUnderline;
+ break;
+ case VTERM_UNDERLINE_DOTTED:
+ result.underlineStyle = QTextCharFormat::DotLine;
+ break;
+ }
+ }
+
+ result.strikeOut = cell.attrs.strike;
+
+ return result;
+ }
+
+ // Callbacks from vterm
+ void invalidate(VTermRect rect)
+ {
+ if (!m_altscreen) {
+ rect.start_row += m_scrollback->size();
+ rect.end_row += m_scrollback->size();
+ }
+
+ emit q->invalidated(
+ QRect{QPoint{rect.start_col, rect.start_row}, QPoint{rect.end_col, rect.end_row - 1}});
+ }
+
+ int sb_pushline(int cols, const VTermScreenCell *cells)
+ {
+ m_scrollback->emplace(cols, cells);
+ emit q->fullSizeChanged(q->fullSize());
+ return 1;
+ }
+
+ int sb_popline(int cols, VTermScreenCell *cells)
+ {
+ if (m_scrollback->size() == 0)
+ return 0;
+
+ m_scrollback->popto(cols, cells);
+ emit q->fullSizeChanged(q->fullSize());
+ return 1;
+ }
+
+ int sb_clear()
+ {
+ m_scrollback->clear();
+ emit q->fullSizeChanged(q->fullSize());
+ return 1;
+ }
+
+ int osc(int cmd, const VTermStringFragment &fragment)
+ {
+ if (m_shellIntegration)
+ m_shellIntegration->onOsc(cmd, fragment);
+
+ return 1;
+ }
+
+ int setTerminalProperties(VTermProp prop, VTermValue *val)
+ {
+ switch (prop) {
+ case VTERM_PROP_CURSORVISIBLE: {
+ Cursor old = q->cursor();
+ m_cursor.visible = val->boolean;
+ q->cursorChanged(old, q->cursor());
+ break;
+ }
+ case VTERM_PROP_CURSORBLINK: {
+ Cursor old = q->cursor();
+ m_cursor.blink = val->boolean;
+ emit q->cursorChanged(old, q->cursor());
+ break;
+ }
+ case VTERM_PROP_CURSORSHAPE: {
+ Cursor old = q->cursor();
+ m_cursor.shape = (Cursor::Shape) val->number;
+ emit q->cursorChanged(old, q->cursor());
+ break;
+ }
+ case VTERM_PROP_ICONNAME:
+ break;
+ case VTERM_PROP_TITLE:
+ break;
+ case VTERM_PROP_ALTSCREEN:
+ m_altscreen = val->boolean;
+ emit q->altscreenChanged(m_altscreen);
+ break;
+ case VTERM_PROP_MOUSE:
+ qCDebug(log) << "Ignoring VTERM_PROP_MOUSE" << val->number;
+ break;
+ case VTERM_PROP_REVERSE:
+ qCDebug(log) << "Ignoring VTERM_PROP_REVERSE" << val->boolean;
+ break;
+ case VTERM_N_PROPS:
+ break;
+ }
+ return 1;
+ }
+ int movecursor(VTermPos pos, VTermPos oldpos, int visible)
+ {
+ Q_UNUSED(oldpos);
+ Cursor oldCursor = q->cursor();
+ m_cursor.position = {pos.col, pos.row};
+ m_cursor.visible = visible > 0;
+ q->cursorChanged(oldCursor, q->cursor());
+ return 1;
+ }
+
+ const VTermScreenCell *cellAt(int x, int y)
+ {
+ QTC_ASSERT(y >= 0 && x >= 0, return nullptr);
+ QTC_ASSERT(y < q->fullSize().height() && x < liveSize().width(), return nullptr);
+
+ if (!m_altscreen && y < m_scrollback->size()) {
+ const auto &sbl = m_scrollback->line((m_scrollback->size() - 1) - y);
+ if (x < sbl.cols()) {
+ return sbl.cell(x);
+ }
+ return nullptr;
+ }
+
+ if (!m_altscreen)
+ y -= m_scrollback->size();
+
+ static VTermScreenCell refCell{};
+ VTermPos vtp{y, x};
+ vterm_screen_get_cell(m_vtermScreen, vtp, &refCell);
+
+ return &refCell;
+ }
+
+ std::unique_ptr<VTerm, void (*)(VTerm *)> m_vterm;
+ VTermScreen *m_vtermScreen;
+ VTermScreenCallbacks m_vtermScreenCallbacks;
+ VTermStateFallbacks m_vtermStateFallbacks;
+
+ Cursor m_cursor;
+ QString m_currentCommand;
+
+ bool m_altscreen{false};
+
+ std::unique_ptr<Internal::Scrollback> m_scrollback;
+
+ ShellIntegration *m_shellIntegration{nullptr};
+
+ TerminalSurface *q;
+};
+
+TerminalSurface::TerminalSurface(QSize initialGridSize, ShellIntegration *shellIntegration)
+ : d(std::make_unique<TerminalSurfacePrivate>(this, initialGridSize, shellIntegration))
+{
+ d->init();
+}
+
+TerminalSurface::~TerminalSurface() = default;
+
+int TerminalSurface::cellWidthAt(int x, int y) const
+{
+ const VTermScreenCell *cell = d->cellAt(x, y);
+ if (!cell)
+ return 0;
+ return cell->width;
+}
+
+QSize TerminalSurface::liveSize() const
+{
+ return d->liveSize();
+}
+
+QSize TerminalSurface::fullSize() const
+{
+ if (d->m_altscreen)
+ return liveSize();
+ return QSize{d->liveSize().width(), d->liveSize().height() + d->m_scrollback->size()};
+}
+
+std::u32string::value_type TerminalSurface::fetchCharAt(int x, int y) const
+{
+ const VTermScreenCell *cell = d->cellAt(x, y);
+ if (!cell)
+ return 0;
+
+ if (cell->width == 0)
+ return 0;
+
+ QString s = QString::fromUcs4(cell->chars, 6).normalized(QString::NormalizationForm_C);
+ const QList<uint> ucs4 = s.toUcs4();
+ return std::u32string(ucs4.begin(), ucs4.end()).front();
+}
+
+TerminalCell TerminalSurface::fetchCell(int x, int y) const
+{
+ static TerminalCell emptyCell{1,
+ {},
+ {},
+ false,
+ ColorIndex::Foreground,
+ ColorIndex::Background,
+ QTextCharFormat::NoUnderline,
+ false};
+
+ QTC_ASSERT(y >= 0, return emptyCell);
+ QTC_ASSERT(y < fullSize().height() && x < fullSize().width(), return emptyCell);
+
+ const VTermScreenCell *refCell = d->cellAt(x, y);
+ if (!refCell)
+ return emptyCell;
+
+ return d->toCell(*refCell);
+}
+
+void TerminalSurface::clearAll()
+{
+ // Fake a scrollback clearing
+ QByteArray data{"\x1b[3J"};
+ vterm_input_write(d->m_vterm.get(), data.constData(), data.size());
+
+ // Send Ctrl+L which will clear the screen
+ emit writeToPty(QByteArray("\f"));
+}
+
+void TerminalSurface::resize(QSize newSize)
+{
+ vterm_set_size(d->m_vterm.get(), newSize.height(), newSize.width());
+}
+
+QPoint TerminalSurface::posToGrid(int pos) const
+{
+ return {pos % d->liveSize().width(), pos / d->liveSize().width()};
+}
+int TerminalSurface::gridToPos(QPoint gridPos) const
+{
+ return gridPos.y() * d->liveSize().width() + gridPos.x();
+}
+
+void TerminalSurface::dataFromPty(const QByteArray &data)
+{
+ vterm_input_write(d->m_vterm.get(), data.constData(), data.size());
+ vterm_screen_flush_damage(d->m_vtermScreen);
+}
+
+void TerminalSurface::flush()
+{
+ vterm_screen_flush_damage(d->m_vtermScreen);
+}
+
+void TerminalSurface::pasteFromClipboard(const QString &clipboardText)
+{
+ if (clipboardText.isEmpty())
+ return;
+
+ vterm_keyboard_start_paste(d->m_vterm.get());
+ for (unsigned int ch : clipboardText.toUcs4())
+ vterm_keyboard_unichar(d->m_vterm.get(), ch, VTERM_MOD_NONE);
+ vterm_keyboard_end_paste(d->m_vterm.get());
+
+ if (!d->m_altscreen) {
+ emit unscroll();
+ }
+}
+
+void TerminalSurface::sendKey(Qt::Key key)
+{
+ if (key == Qt::Key_Escape)
+ vterm_keyboard_key(d->m_vterm.get(), VTERM_KEY_ESCAPE, VTERM_MOD_NONE);
+}
+
+void TerminalSurface::sendKey(const QString &text)
+{
+ for (const unsigned int ch : text.toUcs4())
+ vterm_keyboard_unichar(d->m_vterm.get(), ch, VTERM_MOD_NONE);
+}
+
+void TerminalSurface::sendKey(QKeyEvent *event)
+{
+ bool keypad = event->modifiers() & Qt::KeypadModifier;
+ VTermModifier mod = Internal::qtModifierToVTerm(event->modifiers());
+ VTermKey key = Internal::qtKeyToVTerm(Qt::Key(event->key()), keypad);
+
+ if (key != VTERM_KEY_NONE) {
+ if (mod == VTERM_MOD_SHIFT && (key == VTERM_KEY_ESCAPE || key == VTERM_KEY_BACKSPACE))
+ mod = VTERM_MOD_NONE;
+
+ vterm_keyboard_key(d->m_vterm.get(), key, mod);
+ } else if (event->text().length() == 1) {
+ // This maps to delete word and is way to easy to mistakenly type
+ // if (event->key() == Qt::Key_Space && mod == VTERM_MOD_SHIFT)
+ // mod = VTERM_MOD_NONE;
+
+ // Per https://github.com/justinmk/neovim/commit/317d5ca7b0f92ef42de989b3556ca9503f0a3bf6
+ // libvterm prefers we send the full keycode rather than sending the
+ // ctrl modifier. This helps with ncurses applications which otherwise
+ // do not recognize ctrl+<key> and in the shell for getting common control characters
+ // like ctrl+i for tab or ctrl+j for newline.
+
+ // Workaround for "ALT+SHIFT+/" (\ on german mac keyboards)
+ if (mod == (VTERM_MOD_SHIFT | VTERM_MOD_ALT) && event->key() == Qt::Key_Slash) {
+ mod = VTERM_MOD_NONE;
+ }
+
+ vterm_keyboard_unichar(d->m_vterm.get(), event->text().toUcs4()[0], VTERM_MOD_NONE);
+ } else if (mod == VTERM_MOD_CTRL && event->key() >= Qt::Key_A && event->key() < Qt::Key_Z) {
+ vterm_keyboard_unichar(d->m_vterm.get(), 'a' + (event->key() - Qt::Key_A), mod);
+ }
+}
+
+Cursor TerminalSurface::cursor() const
+{
+ Cursor cursor = d->m_cursor;
+ if (!d->m_altscreen)
+ cursor.position.setY(cursor.position.y() + d->m_scrollback->size());
+
+ return cursor;
+}
+
+ShellIntegration *TerminalSurface::shellIntegration() const
+{
+ return d->m_shellIntegration;
+}
+
+CellIterator TerminalSurface::begin() const
+{
+ auto res = CellIterator(this, {0, 0});
+ res.m_state = CellIterator::State::BEGIN;
+ return res;
+}
+
+CellIterator TerminalSurface::end() const
+{
+ return CellIterator(this);
+}
+
+std::reverse_iterator<CellIterator> TerminalSurface::rbegin() const
+{
+ return std::make_reverse_iterator(end());
+}
+
+std::reverse_iterator<CellIterator> TerminalSurface::rend() const
+{
+ return std::make_reverse_iterator(begin());
+}
+
+CellIterator TerminalSurface::iteratorAt(QPoint pos) const
+{
+ return CellIterator(this, pos);
+}
+CellIterator TerminalSurface::iteratorAt(int pos) const
+{
+ return CellIterator(this, pos);
+}
+
+std::reverse_iterator<CellIterator> TerminalSurface::rIteratorAt(QPoint pos) const
+{
+ return std::make_reverse_iterator(iteratorAt(pos));
+}
+
+std::reverse_iterator<CellIterator> TerminalSurface::rIteratorAt(int pos) const
+{
+ return std::make_reverse_iterator(iteratorAt(pos));
+}
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/terminalsurface.h b/src/plugins/terminal/terminalsurface.h
new file mode 100644
index 0000000000..a6fc7425d4
--- /dev/null
+++ b/src/plugins/terminal/terminalsurface.h
@@ -0,0 +1,111 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "celliterator.h"
+#include "shellintegration.h"
+
+#include <QKeyEvent>
+#include <QSize>
+#include <QTextCharFormat>
+
+#include <memory>
+
+namespace Terminal::Internal {
+
+class Scrollback;
+
+struct TerminalSurfacePrivate;
+
+enum ColorIndex { Foreground = 16, Background = 17 };
+
+struct TerminalCell
+{
+ int width;
+ QString text;
+ bool bold{false};
+ bool italic{false};
+ std::variant<int, QColor> foregroundColor;
+ std::variant<int, QColor> backgroundColor;
+ QTextCharFormat::UnderlineStyle underlineStyle{QTextCharFormat::NoUnderline};
+ bool strikeOut{false};
+};
+
+struct Cursor
+{
+ enum class Shape {
+ Block = 1,
+ Underline,
+ LeftBar,
+ };
+ QPoint position;
+ bool visible;
+ Shape shape;
+ bool blink{false};
+};
+
+class TerminalSurface : public QObject
+{
+ Q_OBJECT;
+
+public:
+ TerminalSurface(QSize initialGridSize, ShellIntegration *shellIntegration);
+ ~TerminalSurface();
+
+public:
+ CellIterator begin() const;
+ CellIterator end() const;
+ std::reverse_iterator<CellIterator> rbegin() const;
+ std::reverse_iterator<CellIterator> rend() const;
+
+ CellIterator iteratorAt(QPoint pos) const;
+ CellIterator iteratorAt(int pos) const;
+
+ std::reverse_iterator<CellIterator> rIteratorAt(QPoint pos) const;
+ std::reverse_iterator<CellIterator> rIteratorAt(int pos) const;
+
+public:
+ void clearAll();
+
+ void resize(QSize newSize);
+
+ TerminalCell fetchCell(int x, int y) const;
+ std::u32string::value_type fetchCharAt(int x, int y) const;
+ int cellWidthAt(int x, int y) const;
+
+ QSize liveSize() const;
+ QSize fullSize() const;
+
+ QPoint posToGrid(int pos) const;
+ int gridToPos(QPoint gridPos) const;
+
+ void dataFromPty(const QByteArray &data);
+ void flush();
+
+ void pasteFromClipboard(const QString &text);
+
+ void sendKey(Qt::Key key);
+ void sendKey(QKeyEvent *event);
+ void sendKey(const QString &text);
+
+ int invertedScrollOffset() const;
+
+ Cursor cursor() const;
+
+ ShellIntegration *shellIntegration() const;
+
+signals:
+ void writeToPty(const QByteArray &data);
+ void invalidated(QRect grid);
+ void fullSizeChanged(QSize newSize);
+ void cursorChanged(Cursor oldCursor, Cursor newCursor);
+ void altscreenChanged(bool altScreen);
+ void unscroll();
+ void bell();
+
+private:
+ std::unique_ptr<TerminalSurfacePrivate> d;
+};
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/terminaltr.h b/src/plugins/terminal/terminaltr.h
new file mode 100644
index 0000000000..b645284833
--- /dev/null
+++ b/src/plugins/terminal/terminaltr.h
@@ -0,0 +1,15 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QCoreApplication>
+
+namespace Terminal {
+
+struct Tr
+{
+ Q_DECLARE_TR_FUNCTIONS(QtC::Terminal)
+};
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalwidget.cpp b/src/plugins/terminal/terminalwidget.cpp
new file mode 100644
index 0000000000..5027d39029
--- /dev/null
+++ b/src/plugins/terminal/terminalwidget.cpp
@@ -0,0 +1,1568 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "terminalwidget.h"
+#include "glyphcache.h"
+#include "terminalconstants.h"
+#include "terminalsettings.h"
+#include "terminalsurface.h"
+#include "terminaltr.h"
+
+#include <aggregation/aggregate.h>
+
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/fileutils.h>
+#include <coreplugin/icore.h>
+
+#include <utils/algorithm.h>
+#include <utils/environment.h>
+#include <utils/fileutils.h>
+#include <utils/hostosinfo.h>
+#include <utils/processinterface.h>
+#include <utils/proxyaction.h>
+#include <utils/stringutils.h>
+
+#include <vterm.h>
+
+#include <QApplication>
+#include <QCache>
+#include <QClipboard>
+#include <QDesktopServices>
+#include <QElapsedTimer>
+#include <QGlyphRun>
+#include <QLoggingCategory>
+#include <QMenu>
+#include <QMimeData>
+#include <QPaintEvent>
+#include <QPainter>
+#include <QPainterPath>
+#include <QPixmapCache>
+#include <QRawFont>
+#include <QRegularExpression>
+#include <QScrollBar>
+#include <QTextItem>
+#include <QTextLayout>
+#include <QToolTip>
+
+Q_LOGGING_CATEGORY(terminalLog, "qtc.terminal", QtWarningMsg)
+Q_LOGGING_CATEGORY(selectionLog, "qtc.terminal.selection", QtWarningMsg)
+Q_LOGGING_CATEGORY(paintLog, "qtc.terminal.paint", QtWarningMsg)
+
+using namespace Utils;
+using namespace Utils::Terminal;
+using namespace Core;
+
+namespace Terminal {
+
+namespace ColorIndex {
+enum Indices {
+ Foreground = Internal::ColorIndex::Foreground,
+ Background = Internal::ColorIndex::Background,
+ Selection,
+ FindMatch,
+};
+}
+
+using namespace std::chrono_literals;
+
+// Minimum time between two refreshes. (30fps)
+static constexpr std::chrono::milliseconds minRefreshInterval = 1s / 30;
+
+TerminalWidget::TerminalWidget(QWidget *parent, const OpenTerminalParameters &openParameters)
+ : QAbstractScrollArea(parent)
+ , m_context(Utils::Id("TerminalWidget_").withSuffix((size_t) this))
+ , m_openParameters(openParameters)
+ , m_lastFlush(std::chrono::system_clock::now())
+ , m_lastDoubleClick(std::chrono::system_clock::now())
+{
+ auto contextObj = new IContext(this);
+ contextObj->setWidget(this);
+ contextObj->setContext(m_context);
+ ICore::addContextObject(contextObj);
+
+ setupSurface();
+ setupFont();
+ setupColors();
+ setupActions();
+
+ m_cursorBlinkTimer.setInterval(750);
+ m_cursorBlinkTimer.setSingleShot(false);
+
+ connect(&m_cursorBlinkTimer, &QTimer::timeout, this, [this]() {
+ if (hasFocus())
+ m_cursorBlinkState = !m_cursorBlinkState;
+ else
+ m_cursorBlinkState = true;
+ updateViewportRect(gridToViewport(QRect{m_cursor.position, m_cursor.position}));
+ });
+
+ setAttribute(Qt::WA_InputMethodEnabled);
+ setAttribute(Qt::WA_MouseTracking);
+ setAcceptDrops(true);
+
+ setCursor(Qt::IBeamCursor);
+
+ setViewportMargins(1, 1, 1, 1);
+
+ setFocus();
+ setFocusPolicy(Qt::StrongFocus);
+
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+
+ m_flushDelayTimer.setSingleShot(true);
+ m_flushDelayTimer.setInterval(minRefreshInterval);
+
+ connect(&m_flushDelayTimer, &QTimer::timeout, this, [this]() { flushVTerm(true); });
+
+ m_scrollTimer.setSingleShot(false);
+ m_scrollTimer.setInterval(1s / 2);
+ connect(&m_scrollTimer, &QTimer::timeout, this, [this] {
+ if (m_scrollDirection < 0)
+ verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepSub);
+ else if (m_scrollDirection > 0)
+ verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepAdd);
+ });
+
+ connect(&TerminalSettings::instance(), &AspectContainer::applied, this, [this] {
+ // Setup colors first, as setupFont will redraw the screen.
+ setupColors();
+ setupFont();
+ configBlinkTimer();
+ });
+
+ m_aggregate = new Aggregation::Aggregate(this);
+ m_aggregate->add(this);
+ m_aggregate->add(m_search.get());
+}
+
+void TerminalWidget::setupPty()
+{
+ m_process = std::make_unique<Process>();
+
+ CommandLine shellCommand = m_openParameters.shellCommand.value_or(
+ CommandLine{TerminalSettings::instance().shell.filePath(),
+ TerminalSettings::instance().shellArguments.value(),
+ CommandLine::Raw});
+
+ Environment env = m_openParameters.environment.value_or(
+ shellCommand.executable().deviceEnvironment());
+
+ // For git bash on Windows
+ env.prependOrSetPath(shellCommand.executable().parentDir());
+ if (env.hasKey("CLINK_NOAUTORUN"))
+ env.unset("CLINK_NOAUTORUN");
+
+ m_process->setProcessMode(ProcessMode::Writer);
+ m_process->setPtyData(Utils::Pty::Data());
+ m_process->setCommand(shellCommand);
+ if (m_openParameters.workingDirectory.has_value())
+ m_process->setWorkingDirectory(*m_openParameters.workingDirectory);
+ m_process->setEnvironment(env);
+
+ if (m_surface->shellIntegration()) {
+ m_surface->shellIntegration()->prepareProcess(*m_process.get());
+ }
+
+ connect(m_process.get(), &Process::readyReadStandardOutput, this, [this]() {
+ onReadyRead(false);
+ });
+
+ connect(m_process.get(), &Process::done, this, [this] {
+ if (m_process) {
+ if (m_process->exitCode() != 0) {
+ QByteArray msg = QString("\r\n\033[31mProcess exited with code: %1")
+ .arg(m_process->exitCode())
+ .toUtf8();
+
+ if (!m_process->errorString().isEmpty())
+ msg += QString(" (%1)").arg(m_process->errorString()).toUtf8();
+
+ m_surface->dataFromPty(msg);
+
+ return;
+ }
+ }
+
+ if (m_openParameters.m_exitBehavior == ExitBehavior::Restart) {
+ QMetaObject::invokeMethod(
+ this,
+ [this] {
+ m_process.reset();
+ setupSurface();
+ setupPty();
+ },
+ Qt::QueuedConnection);
+ }
+
+ if (m_openParameters.m_exitBehavior == ExitBehavior::Close)
+ deleteLater();
+
+ if (m_openParameters.m_exitBehavior == ExitBehavior::Keep) {
+ QByteArray msg = QString("\r\nProcess exited with code: %1")
+ .arg(m_process ? m_process->exitCode() : -1)
+ .toUtf8();
+
+ m_surface->dataFromPty(msg);
+ }
+ });
+
+ connect(m_process.get(), &Process::started, this, [this] {
+ if (m_shellName.isEmpty())
+ m_shellName = m_process->commandLine().executable().fileName();
+ if (HostOsInfo::isWindowsHost() && m_shellName.endsWith(QTC_WIN_EXE_SUFFIX))
+ m_shellName.chop(QStringLiteral(QTC_WIN_EXE_SUFFIX).size());
+
+ applySizeChange();
+ emit started(m_process->processId());
+ });
+
+ m_process->start();
+}
+
+void TerminalWidget::setupFont()
+{
+ QFont f;
+ f.setFixedPitch(true);
+ f.setFamily(TerminalSettings::instance().font.value());
+ f.setPointSize(TerminalSettings::instance().fontSize.value());
+
+ setFont(f);
+}
+
+void TerminalWidget::setupColors()
+{
+ // Check if the colors have changed.
+ std::array<QColor, 20> newColors;
+ for (int i = 0; i < 16; ++i) {
+ newColors[i] = TerminalSettings::instance().colors[i].value();
+ }
+ newColors[ColorIndex::Background] = TerminalSettings::instance().backgroundColor.value();
+ newColors[ColorIndex::Foreground] = TerminalSettings::instance().foregroundColor.value();
+ newColors[ColorIndex::Selection] = TerminalSettings::instance().selectionColor.value();
+ newColors[ColorIndex::FindMatch] = TerminalSettings::instance().findMatchColor.value();
+
+ if (m_currentColors == newColors)
+ return;
+
+ m_currentColors = newColors;
+
+ updateViewport();
+ update();
+}
+
+void TerminalWidget::setupActions()
+{
+ ActionManager::registerAction(&m_copy, Constants::COPY, m_context);
+ ActionManager::registerAction(&m_paste, Constants::PASTE, m_context);
+ ActionManager::registerAction(&m_close, Core::Constants::CLOSE, m_context);
+ ActionManager::registerAction(&m_clearTerminal, Constants::CLEAR_TERMINAL, m_context);
+ ActionManager::registerAction(&m_clearSelection, Constants::CLEARSELECTION, m_context);
+ ActionManager::registerAction(&m_moveCursorWordLeft, Constants::MOVECURSORWORDLEFT, m_context);
+ ActionManager::registerAction(&m_moveCursorWordRight, Constants::MOVECURSORWORDRIGHT, m_context);
+
+ connect(&m_copy, &QAction::triggered, this, &TerminalWidget::copyToClipboard);
+ connect(&m_paste, &QAction::triggered, this, &TerminalWidget::pasteFromClipboard);
+ connect(&m_close, &QAction::triggered, this, &TerminalWidget::closeTerminal);
+ connect(&m_clearTerminal, &QAction::triggered, this, &TerminalWidget::clearContents);
+ connect(&m_clearSelection, &QAction::triggered, this, &TerminalWidget::clearSelection);
+ connect(&m_moveCursorWordLeft, &QAction::triggered, this, &TerminalWidget::moveCursorWordLeft);
+ connect(&m_moveCursorWordRight, &QAction::triggered, this, &TerminalWidget::moveCursorWordRight);
+
+ m_exit = unlockGlobalAction(Core::Constants::EXIT, m_context);
+ m_options = unlockGlobalAction(Core::Constants::OPTIONS, m_context);
+ m_settings = unlockGlobalAction("Preferences.Terminal.General", m_context);
+ m_findInDocument = unlockGlobalAction(Core::Constants::FIND_IN_DOCUMENT, m_context);
+}
+
+void TerminalWidget::closeTerminal()
+{
+ deleteLater();
+}
+
+void TerminalWidget::writeToPty(const QByteArray &data)
+{
+ if (m_process && m_process->isRunning())
+ m_process->writeRaw(data);
+}
+
+void TerminalWidget::setupSurface()
+{
+ m_shellIntegration.reset(new ShellIntegration());
+ m_surface = std::make_unique<Internal::TerminalSurface>(QSize{80, 60}, m_shellIntegration.get());
+ m_search = TerminalSearchPtr(new TerminalSearch(m_surface.get()), [this](TerminalSearch *p) {
+ m_aggregate->remove(p);
+ delete p;
+ });
+
+ connect(m_search.get(), &TerminalSearch::hitsChanged, this, &TerminalWidget::updateViewport);
+ connect(m_search.get(), &TerminalSearch::currentHitChanged, this, [this] {
+ SearchHit hit = m_search->currentHit();
+ if (hit.start >= 0) {
+ setSelection(Selection{hit.start, hit.end, true}, hit != m_lastSelectedHit);
+ m_lastSelectedHit = hit;
+ }
+ });
+
+ connect(m_surface.get(),
+ &Internal::TerminalSurface::writeToPty,
+ this,
+ &TerminalWidget::writeToPty);
+ connect(m_surface.get(), &Internal::TerminalSurface::fullSizeChanged, this, [this] {
+ updateScrollBars();
+ });
+ connect(m_surface.get(),
+ &Internal::TerminalSurface::invalidated,
+ this,
+ [this](const QRect &rect) {
+ setSelection(std::nullopt);
+ updateViewportRect(gridToViewport(rect));
+ verticalScrollBar()->setValue(m_surface->fullSize().height());
+ });
+ connect(m_surface.get(),
+ &Internal::TerminalSurface::cursorChanged,
+ this,
+ [this](const Internal::Cursor &oldCursor, const Internal::Cursor &newCursor) {
+ int startX = oldCursor.position.x();
+ int endX = newCursor.position.x();
+
+ if (startX > endX)
+ std::swap(startX, endX);
+
+ int startY = oldCursor.position.y();
+ int endY = newCursor.position.y();
+ if (startY > endY)
+ std::swap(startY, endY);
+
+ m_cursor = newCursor;
+
+ updateViewportRect(
+ gridToViewport(QRect{QPoint{startX, startY}, QPoint{endX, endY}}));
+ configBlinkTimer();
+ });
+ connect(m_surface.get(), &Internal::TerminalSurface::altscreenChanged, this, [this] {
+ updateScrollBars();
+ if (!setSelection(std::nullopt))
+ updateViewport();
+ });
+ connect(m_surface.get(), &Internal::TerminalSurface::unscroll, this, [this] {
+ verticalScrollBar()->setValue(verticalScrollBar()->maximum());
+ });
+ connect(m_surface.get(), &Internal::TerminalSurface::bell, this, [] {
+ if (TerminalSettings::instance().audibleBell.value())
+ QApplication::beep();
+ });
+
+ if (m_shellIntegration) {
+ connect(m_shellIntegration.get(),
+ &ShellIntegration::commandChanged,
+ this,
+ [this](const CommandLine &command) {
+ m_currentCommand = command;
+ emit commandChanged(m_currentCommand);
+ });
+ connect(m_shellIntegration.get(),
+ &ShellIntegration::currentDirChanged,
+ this,
+ [this](const QString &currentDir) {
+ m_cwd = FilePath::fromUserInput(currentDir);
+ emit cwdChanged(m_cwd);
+ });
+ }
+}
+
+void TerminalWidget::configBlinkTimer()
+{
+ bool shouldRun = m_cursor.visible && m_cursor.blink && hasFocus()
+ && TerminalSettings::instance().allowBlinkingCursor.value();
+ if (shouldRun != m_cursorBlinkTimer.isActive()) {
+ if (shouldRun)
+ m_cursorBlinkTimer.start();
+ else
+ m_cursorBlinkTimer.stop();
+ }
+}
+
+QColor TerminalWidget::toQColor(std::variant<int, QColor> color) const
+{
+ if (std::holds_alternative<int>(color)) {
+ int idx = std::get<int>(color);
+ if (idx >= 0 && idx < 18)
+ return m_currentColors[idx];
+
+ return m_currentColors[ColorIndex::Background];
+ }
+ return std::get<QColor>(color);
+}
+
+void TerminalWidget::updateCopyState()
+{
+ if (!hasFocus())
+ return;
+
+ m_copy.setEnabled(m_selection.has_value());
+}
+
+void TerminalWidget::setFont(const QFont &font)
+{
+ m_font = font;
+
+ QFontMetricsF qfm{m_font};
+ const qreal w = [qfm]() -> qreal {
+ if (HostOsInfo::isMacHost())
+ return qfm.maxWidth();
+ return qfm.averageCharWidth();
+ }();
+
+ qCInfo(terminalLog) << font.family() << font.pointSize() << w << viewport()->size();
+
+ m_cellSize = {w, (double) qCeil(qfm.height())};
+
+ QAbstractScrollArea::setFont(m_font);
+
+ if (m_process) {
+ applySizeChange();
+ }
+}
+
+void TerminalWidget::copyToClipboard()
+{
+ QTC_ASSERT(m_selection.has_value(), return);
+
+ QString text = textFromSelection();
+
+ qCDebug(selectionLog) << "Copied to clipboard: " << text;
+
+ setClipboardAndSelection(text);
+}
+
+void TerminalWidget::pasteFromClipboard()
+{
+ QClipboard *clipboard = QApplication::clipboard();
+ const QString clipboardText = clipboard->text(QClipboard::Clipboard);
+
+ if (clipboardText.isEmpty())
+ return;
+
+ m_surface->pasteFromClipboard(clipboardText);
+}
+
+void TerminalWidget::copyLinkToClipboard()
+{
+ if (m_linkSelection)
+ setClipboardAndSelection(m_linkSelection->link.targetFilePath.toUserOutput());
+}
+
+void TerminalWidget::clearSelection()
+{
+ setSelection(std::nullopt);
+ m_surface->sendKey(Qt::Key_Escape);
+}
+
+void TerminalWidget::zoomIn()
+{
+ m_font.setPointSize(m_font.pointSize() + 1);
+ setFont(m_font);
+}
+
+void TerminalWidget::zoomOut()
+{
+ m_font.setPointSize(qMax(m_font.pointSize() - 1, 1));
+ setFont(m_font);
+}
+
+void TerminalWidget::moveCursorWordLeft()
+{
+ writeToPty("\x1b\x62");
+}
+
+void TerminalWidget::moveCursorWordRight()
+{
+ writeToPty("\x1b\x66");
+}
+
+void TerminalWidget::clearContents()
+{
+ m_surface->clearAll();
+}
+
+void TerminalWidget::onReadyRead(bool forceFlush)
+{
+ QByteArray data = m_process->readAllRawStandardOutput();
+
+ m_surface->dataFromPty(data);
+
+ flushVTerm(forceFlush);
+}
+
+void TerminalWidget::flushVTerm(bool force)
+{
+ const std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
+ const std::chrono::milliseconds timeSinceLastFlush
+ = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_lastFlush);
+
+ const bool shouldFlushImmediately = timeSinceLastFlush > minRefreshInterval;
+ if (force || shouldFlushImmediately) {
+ if (m_flushDelayTimer.isActive())
+ m_flushDelayTimer.stop();
+
+ m_lastFlush = now;
+ m_surface->flush();
+ return;
+ }
+
+ if (!m_flushDelayTimer.isActive()) {
+ const std::chrono::milliseconds timeToNextFlush = (minRefreshInterval - timeSinceLastFlush);
+ m_flushDelayTimer.start(timeToNextFlush.count());
+ }
+}
+
+QString TerminalWidget::textFromSelection() const
+{
+ if (!m_selection)
+ return {};
+
+ Internal::CellIterator it = m_surface->iteratorAt(m_selection->start);
+ Internal::CellIterator end = m_surface->iteratorAt(m_selection->end);
+
+ QTC_ASSERT(it.position() < end.position(), return {});
+
+ std::u32string s;
+ bool previousWasZero = false;
+ for (; it != end; ++it) {
+ if (it.gridPos().x() == 0 && !s.empty() && previousWasZero)
+ s += U'\n';
+
+ if (*it != 0) {
+ previousWasZero = false;
+ s += *it;
+ } else {
+ previousWasZero = true;
+ }
+ }
+
+ return QString::fromUcs4(s.data(), static_cast<int>(s.size()));
+}
+
+bool TerminalWidget::setSelection(const std::optional<Selection> &selection, bool scroll)
+{
+ qCDebug(selectionLog) << "setSelection" << selection.has_value();
+ if (selection.has_value())
+ qCDebug(selectionLog) << "start:" << selection->start << "end:" << selection->end
+ << "final:" << selection->final;
+
+ if (selectionLog().isDebugEnabled())
+ updateViewport();
+
+ if (selection == m_selection)
+ return false;
+
+ m_selection = selection;
+
+ updateCopyState();
+
+ if (m_selection && m_selection->final) {
+ qCDebug(selectionLog) << "Copy enabled:" << selection.has_value();
+ QString text = textFromSelection();
+
+ QClipboard *clipboard = QApplication::clipboard();
+ if (clipboard->supportsSelection()) {
+ qCDebug(selectionLog) << "Selection set to clipboard: " << text;
+ clipboard->setText(text, QClipboard::Selection);
+ }
+
+ if (scroll) {
+ QPoint start = m_surface->posToGrid(m_selection->start);
+ QPoint end = m_surface->posToGrid(m_selection->end);
+ QRect viewRect = gridToViewport(QRect{start, end});
+ if (viewRect.y() >= viewport()->height() || viewRect.y() < 0) {
+ // Selection is outside of the viewport, scroll to it.
+ verticalScrollBar()->setValue(start.y());
+ }
+ }
+
+ m_search->setCurrentSelection(SearchHitWithText{{selection->start, selection->end}, text});
+ }
+
+ if (!selectionLog().isDebugEnabled())
+ updateViewport();
+
+ return true;
+}
+
+void TerminalWidget::setShellName(const QString &shellName)
+{
+ m_shellName = shellName;
+}
+
+QString TerminalWidget::shellName() const
+{
+ return m_shellName;
+}
+
+FilePath TerminalWidget::cwd() const
+{
+ return m_cwd;
+}
+
+CommandLine TerminalWidget::currentCommand() const
+{
+ return m_currentCommand;
+}
+
+std::optional<Id> TerminalWidget::identifier() const
+{
+ return m_openParameters.identifier;
+}
+
+QProcess::ProcessState TerminalWidget::processState() const
+{
+ if (m_process)
+ return m_process->state();
+
+ return QProcess::NotRunning;
+}
+
+void TerminalWidget::restart(const OpenTerminalParameters &openParameters)
+{
+ QTC_ASSERT(!m_process || !m_process->isRunning(), return);
+ m_openParameters = openParameters;
+
+ m_process.reset();
+ setupSurface();
+ setupPty();
+}
+
+QPoint TerminalWidget::viewportToGlobal(QPoint p) const
+{
+ int y = p.y() - topMargin();
+ const double offset = verticalScrollBar()->value() * m_cellSize.height();
+ y += offset;
+
+ return {p.x(), y};
+}
+
+QPoint TerminalWidget::globalToViewport(QPoint p) const
+{
+ int y = p.y() + topMargin();
+ const double offset = verticalScrollBar()->value() * m_cellSize.height();
+ y -= offset;
+
+ return {p.x(), y};
+}
+
+QPoint TerminalWidget::globalToGrid(QPointF p) const
+{
+ return QPoint(p.x() / m_cellSize.width(), p.y() / m_cellSize.height());
+}
+
+QPointF TerminalWidget::gridToGlobal(QPoint p, bool bottom, bool right) const
+{
+ QPointF result = QPointF(p.x() * m_cellSize.width(), p.y() * m_cellSize.height());
+ if (bottom || right)
+ result += {right ? m_cellSize.width() : 0, bottom ? m_cellSize.height() : 0};
+ return result;
+}
+
+qreal TerminalWidget::topMargin() const
+{
+ return viewport()->size().height() - (m_surface->liveSize().height() * m_cellSize.height());
+}
+
+static QPixmap generateWavyPixmap(qreal maxRadius, const QPen &pen)
+{
+ const qreal radiusBase = qMax(qreal(1), maxRadius);
+ const qreal pWidth = pen.widthF();
+
+ const QString key = QLatin1String("WaveUnderline-") % pen.color().name()
+ % QString::number(int(radiusBase), 16)
+ % QString::number(int(pWidth), 16);
+
+ QPixmap pixmap;
+ if (QPixmapCache::find(key, &pixmap))
+ return pixmap;
+
+ const qreal halfPeriod = qMax(qreal(2), qreal(radiusBase * 1.61803399)); // the golden ratio
+ const int width = qCeil(100 / (2 * halfPeriod)) * (2 * halfPeriod);
+ const qreal radius = qFloor(radiusBase * 2) / 2.;
+
+ QPainterPath path;
+
+ qreal xs = 0;
+ qreal ys = radius;
+
+ while (xs < width) {
+ xs += halfPeriod;
+ ys = -ys;
+ path.quadTo(xs - halfPeriod / 2, ys, xs, 0);
+ }
+
+ pixmap = QPixmap(width, radius * 2);
+ pixmap.fill(Qt::transparent);
+ {
+ QPen wavePen = pen;
+ wavePen.setCapStyle(Qt::SquareCap);
+
+ // This is to protect against making the line too fat, as happens on macOS
+ // due to it having a rather thick width for the regular underline.
+ const qreal maxPenWidth = .8 * radius;
+ if (wavePen.widthF() > maxPenWidth)
+ wavePen.setWidthF(maxPenWidth);
+
+ QPainter imgPainter(&pixmap);
+ imgPainter.setPen(wavePen);
+ imgPainter.setRenderHint(QPainter::Antialiasing);
+ imgPainter.translate(0, radius);
+ imgPainter.drawPath(path);
+ }
+
+ QPixmapCache::insert(key, pixmap);
+
+ return pixmap;
+}
+
+// Copied from qpainter.cpp
+static void drawTextItemDecoration(QPainter &painter,
+ const QPointF &pos,
+ QTextCharFormat::UnderlineStyle underlineStyle,
+ QTextItem::RenderFlags flags,
+ qreal width,
+ const QColor &underlineColor,
+ const QRawFont &font)
+{
+ if (underlineStyle == QTextCharFormat::NoUnderline
+ && !(flags & (QTextItem::StrikeOut | QTextItem::Overline)))
+ return;
+
+ const QPen oldPen = painter.pen();
+ const QBrush oldBrush = painter.brush();
+ painter.setBrush(Qt::NoBrush);
+ QPen pen = oldPen;
+ pen.setStyle(Qt::SolidLine);
+ pen.setWidthF(font.lineThickness());
+ pen.setCapStyle(Qt::FlatCap);
+
+ QLineF line(qFloor(pos.x()), pos.y(), qFloor(pos.x() + width), pos.y());
+
+ const qreal underlineOffset = font.underlinePosition();
+
+ /*if (underlineStyle == QTextCharFormat::SpellCheckUnderline) {
+ QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme();
+ if (theme)
+ underlineStyle = QTextCharFormat::UnderlineStyle(
+ theme->themeHint(QPlatformTheme::SpellCheckUnderlineStyle).toInt());
+ if (underlineStyle == QTextCharFormat::SpellCheckUnderline) // still not resolved
+ underlineStyle = QTextCharFormat::WaveUnderline;
+ }*/
+
+ if (underlineStyle == QTextCharFormat::WaveUnderline) {
+ painter.save();
+ painter.translate(0, pos.y() + 1);
+ qreal maxHeight = font.descent() - qreal(1);
+
+ QColor uc = underlineColor;
+ if (uc.isValid())
+ pen.setColor(uc);
+
+ // Adapt wave to underlineOffset or pen width, whatever is larger, to make it work on all platforms
+ const QPixmap wave = generateWavyPixmap(qMin(qMax(underlineOffset, pen.widthF()),
+ maxHeight / qreal(2.)),
+ pen);
+ const int descent = qFloor(maxHeight);
+
+ painter.setBrushOrigin(painter.brushOrigin().x(), 0);
+ painter.fillRect(pos.x(), 0, qCeil(width), qMin(wave.height(), descent), wave);
+ painter.restore();
+ } else if (underlineStyle != QTextCharFormat::NoUnderline) {
+ // Deliberately ceil the offset to avoid the underline coming too close to
+ // the text above it, but limit it to stay within descent.
+ qreal adjustedUnderlineOffset = std::ceil(underlineOffset) + 0.5;
+ if (underlineOffset <= font.descent())
+ adjustedUnderlineOffset = qMin(adjustedUnderlineOffset, font.descent() - qreal(0.5));
+ const qreal underlinePos = pos.y() + adjustedUnderlineOffset;
+ QColor uc = underlineColor;
+ if (uc.isValid())
+ pen.setColor(uc);
+
+ pen.setStyle((Qt::PenStyle)(underlineStyle));
+ painter.setPen(pen);
+ QLineF underline(line.x1(), underlinePos, line.x2(), underlinePos);
+ painter.drawLine(underline);
+ }
+
+ pen.setStyle(Qt::SolidLine);
+ pen.setColor(oldPen.color());
+
+ if (flags & QTextItem::StrikeOut) {
+ QLineF strikeOutLine = line;
+ strikeOutLine.translate(0., -font.ascent() / 3.);
+ QColor uc = underlineColor;
+ if (uc.isValid())
+ pen.setColor(uc);
+ painter.setPen(pen);
+ painter.drawLine(strikeOutLine);
+ }
+
+ if (flags & QTextItem::Overline) {
+ QLineF overline = line;
+ overline.translate(0., -font.ascent());
+ QColor uc = underlineColor;
+ if (uc.isValid())
+ pen.setColor(uc);
+ painter.setPen(pen);
+ painter.drawLine(overline);
+ }
+
+ painter.setPen(oldPen);
+ painter.setBrush(oldBrush);
+}
+
+bool TerminalWidget::paintFindMatches(QPainter &p,
+ QList<SearchHit>::const_iterator &it,
+ const QRectF &cellRect,
+ const QPoint gridPos) const
+{
+ if (it == m_search->hits().constEnd())
+ return false;
+
+ const int pos = m_surface->gridToPos(gridPos);
+ while (it != m_search->hits().constEnd()) {
+ if (pos < it->start)
+ return false;
+
+ if (pos >= it->end) {
+ ++it;
+ continue;
+ }
+ break;
+ }
+
+ if (it == m_search->hits().constEnd())
+ return false;
+
+ p.fillRect(cellRect, m_currentColors[ColorIndex::FindMatch]);
+
+ return true;
+}
+
+bool TerminalWidget::paintSelection(QPainter &p, const QRectF &cellRect, const QPoint gridPos) const
+{
+ bool isInSelection = false;
+ const int pos = m_surface->gridToPos(gridPos);
+
+ if (m_selection)
+ isInSelection = pos >= m_selection->start && pos < m_selection->end;
+
+ if (isInSelection)
+ p.fillRect(cellRect, m_currentColors[ColorIndex::Selection]);
+
+ return isInSelection;
+}
+
+int TerminalWidget::paintCell(QPainter &p,
+ const QRectF &cellRect,
+ QPoint gridPos,
+ const Internal::TerminalCell &cell,
+ QFont &f,
+ QList<SearchHit>::const_iterator &searchIt) const
+{
+ bool paintBackground = !paintSelection(p, cellRect, gridPos)
+ && !paintFindMatches(p, searchIt, cellRect, gridPos);
+
+ bool isDefaultBg = std::holds_alternative<int>(cell.backgroundColor)
+ && std::get<int>(cell.backgroundColor) == 17;
+
+ if (paintBackground && !isDefaultBg)
+ p.fillRect(cellRect, toQColor(cell.backgroundColor));
+
+ p.setPen(toQColor(cell.foregroundColor));
+
+ f.setBold(cell.bold);
+ f.setItalic(cell.italic);
+
+ if (!cell.text.isEmpty()) {
+ const auto r = Internal::GlyphCache::instance().get(f, cell.text);
+
+ if (r) {
+ const auto brSize = r->boundingRect().size();
+ QPointF brOffset;
+ if (brSize.width() > cellRect.size().width())
+ brOffset.setX(-(brSize.width() - cellRect.size().width()) / 2.0);
+ if (brSize.height() > cellRect.size().height())
+ brOffset.setY(-(brSize.height() - cellRect.size().height()) / 2.0);
+
+ QPointF finalPos = cellRect.topLeft() + brOffset;
+
+ p.drawGlyphRun(finalPos, *r);
+
+ bool tempLink = false;
+ if (m_linkSelection) {
+ int chPos = m_surface->gridToPos(gridPos);
+ tempLink = chPos >= m_linkSelection->start && chPos < m_linkSelection->end;
+ }
+ if (cell.underlineStyle != QTextCharFormat::NoUnderline || cell.strikeOut || tempLink) {
+ QTextItem::RenderFlags flags;
+ //flags.setFlag(QTextItem::RenderFlag::Underline, cell.format.fontUnderline());
+ flags.setFlag(QTextItem::StrikeOut, cell.strikeOut);
+ finalPos.setY(finalPos.y() + r->rawFont().ascent());
+ drawTextItemDecoration(p,
+ finalPos,
+ tempLink ? QTextCharFormat::DashUnderline
+ : cell.underlineStyle,
+ flags,
+ cellRect.size().width(),
+ {},
+ r->rawFont());
+ }
+ }
+ }
+
+ return cell.width;
+}
+
+void TerminalWidget::paintCursor(QPainter &p) const
+{
+ if (!m_process || !m_process->isRunning())
+ return;
+
+ auto cursor = m_surface->cursor();
+
+ const bool blinkState = !cursor.blink || m_cursorBlinkState
+ || !TerminalSettings::instance().allowBlinkingCursor.value();
+
+ if (cursor.visible && blinkState) {
+ const int cursorCellWidth = m_surface->cellWidthAt(cursor.position.x(), cursor.position.y());
+
+ QRectF cursorRect = QRectF(gridToGlobal(cursor.position),
+ gridToGlobal({cursor.position.x() + cursorCellWidth,
+ cursor.position.y()},
+ true))
+ .toAlignedRect();
+
+ cursorRect.adjust(1, 1, -1, -1);
+
+ QPen pen(Qt::white, 0, Qt::SolidLine);
+ p.setPen(pen);
+
+ if (hasFocus()) {
+ QPainter::CompositionMode oldMode = p.compositionMode();
+ p.setCompositionMode(QPainter::RasterOp_NotDestination);
+ switch (cursor.shape) {
+ case Internal::Cursor::Shape::Block:
+ p.fillRect(cursorRect, p.pen().brush());
+ break;
+ case Internal::Cursor::Shape::Underline:
+ p.drawLine(cursorRect.bottomLeft(), cursorRect.bottomRight());
+ break;
+ case Internal::Cursor::Shape::LeftBar:
+ p.drawLine(cursorRect.topLeft(), cursorRect.bottomLeft());
+ break;
+ }
+ p.setCompositionMode(oldMode);
+ } else {
+ p.drawRect(cursorRect);
+ }
+ }
+}
+
+void TerminalWidget::paintPreedit(QPainter &p) const
+{
+ auto cursor = m_surface->cursor();
+ if (!m_preEditString.isEmpty()) {
+ QRectF rect = QRectF(gridToGlobal(cursor.position),
+ gridToGlobal({cursor.position.x(), cursor.position.y()}, true, true));
+
+ p.fillRect(rect, QColor::fromRgb(0, 0, 0));
+ p.setPen(Qt::white);
+ p.drawText(rect, m_preEditString);
+ }
+}
+
+void TerminalWidget::paintCells(QPainter &p, QPaintEvent *event) const
+{
+ QFont f = m_font;
+
+ const int scrollOffset = verticalScrollBar()->value();
+
+ const int maxRow = m_surface->fullSize().height();
+ const int startRow = qFloor((qreal) event->rect().y() / m_cellSize.height()) + scrollOffset;
+ const int endRow = qMin(maxRow,
+ qCeil((event->rect().y() + event->rect().height()) / m_cellSize.height())
+ + scrollOffset);
+
+ QList<SearchHit>::const_iterator searchIt
+ = std::lower_bound(m_search->hits().constBegin(),
+ m_search->hits().constEnd(),
+ startRow,
+ [this](const SearchHit &hit, int value) {
+ return m_surface->posToGrid(hit.start).y() < value;
+ });
+
+ for (int cellY = startRow; cellY < endRow; ++cellY) {
+ for (int cellX = 0; cellX < m_surface->liveSize().width();) {
+ const auto cell = m_surface->fetchCell(cellX, cellY);
+
+ QRectF cellRect(gridToGlobal({cellX, cellY}),
+ QSizeF{m_cellSize.width() * cell.width, m_cellSize.height()});
+
+ int numCells = paintCell(p, cellRect, {cellX, cellY}, cell, f, searchIt);
+
+ cellX += numCells;
+ }
+ }
+}
+
+void TerminalWidget::paintDebugSelection(QPainter &p, const Selection &selection) const
+{
+ auto s = globalToViewport(gridToGlobal(m_surface->posToGrid(selection.start)).toPoint());
+ const auto e = globalToViewport(
+ gridToGlobal(m_surface->posToGrid(selection.end), true).toPoint());
+
+ p.setPen(QPen(Qt::green, 1, Qt::DashLine));
+ p.drawLine(s.x(), 0, s.x(), height());
+ p.drawLine(0, s.y(), width(), s.y());
+
+ p.setPen(QPen(Qt::red, 1, Qt::DashLine));
+
+ p.drawLine(e.x(), 0, e.x(), height());
+ p.drawLine(0, e.y(), width(), e.y());
+}
+
+void TerminalWidget::paintEvent(QPaintEvent *event)
+{
+ QElapsedTimer t;
+ t.start();
+ event->accept();
+ QPainter p(viewport());
+
+ p.save();
+
+ if (paintLog().isDebugEnabled())
+ p.fillRect(event->rect(), QColor::fromRgb(rand() % 60, rand() % 60, rand() % 60));
+ else
+ p.fillRect(event->rect(), m_currentColors[ColorIndex::Background]);
+
+ int scrollOffset = verticalScrollBar()->value();
+ int offset = -(scrollOffset * m_cellSize.height());
+
+ qreal margin = topMargin();
+
+ p.translate(QPointF{0.0, offset + margin});
+
+ paintCells(p, event);
+ paintCursor(p);
+ paintPreedit(p);
+
+ p.restore();
+
+ p.fillRect(QRectF{{0, 0}, QSizeF{(qreal) width(), topMargin()}},
+ m_currentColors[ColorIndex::Background]);
+
+ if (selectionLog().isDebugEnabled()) {
+ if (m_selection)
+ paintDebugSelection(p, *m_selection);
+ if (m_linkSelection)
+ paintDebugSelection(p, *m_linkSelection);
+ }
+
+ if (paintLog().isDebugEnabled()) {
+ QToolTip::showText(this->mapToGlobal(QPoint(width() - 200, 0)),
+ QString("Paint: %1ms").arg(t.elapsed()));
+ }
+}
+
+void TerminalWidget::keyPressEvent(QKeyEvent *event)
+{
+ // Don't blink during typing
+ if (m_cursorBlinkTimer.isActive()) {
+ m_cursorBlinkTimer.start();
+ m_cursorBlinkState = true;
+ }
+
+ if (event->key() == Qt::Key_Escape) {
+ bool sendToTerminal = TerminalSettings::instance().sendEscapeToTerminal.value();
+ bool send = false;
+ if (sendToTerminal && event->modifiers() == Qt::NoModifier)
+ send = true;
+ else if (!sendToTerminal && event->modifiers() == Qt::ShiftModifier)
+ send = true;
+
+ if (send) {
+ event->setModifiers(Qt::NoModifier);
+ m_surface->sendKey(event);
+ return;
+ }
+
+ if (m_selection)
+ m_clearSelection.trigger();
+ else {
+ QAction *returnAction = ActionManager::command(Core::Constants::S_RETURNTOEDITOR)
+ ->actionForContext(Core::Constants::C_GLOBAL);
+ QTC_ASSERT(returnAction, return);
+ returnAction->trigger();
+ }
+ return;
+ }
+
+ if (event->key() == Qt::Key_Control) {
+ if (!m_linkSelection.has_value() && checkLinkAt(mapFromGlobal(QCursor::pos()))) {
+ setCursor(Qt::PointingHandCursor);
+ }
+ }
+
+ event->accept();
+
+ m_surface->sendKey(event);
+}
+
+void TerminalWidget::keyReleaseEvent(QKeyEvent *event)
+{
+ if (event->key() == Qt::Key_Control && m_linkSelection.has_value()) {
+ m_linkSelection.reset();
+ updateCopyState();
+ setCursor(Qt::IBeamCursor);
+ updateViewport();
+ }
+}
+
+void TerminalWidget::applySizeChange()
+{
+ QSize newLiveSize = {
+ qFloor((qreal) (viewport()->size().width()) / (qreal) m_cellSize.width()),
+ qFloor((qreal) (viewport()->size().height()) / m_cellSize.height()),
+ };
+
+ if (newLiveSize.height() <= 0)
+ newLiveSize.setHeight(1);
+
+ if (newLiveSize.width() <= 0)
+ newLiveSize.setWidth(1);
+
+ if (m_process && m_process->ptyData())
+ m_process->ptyData()->resize(newLiveSize);
+
+ m_surface->resize(newLiveSize);
+ flushVTerm(true);
+}
+
+void TerminalWidget::updateScrollBars()
+{
+ int scrollSize = m_surface->fullSize().height() - m_surface->liveSize().height();
+ verticalScrollBar()->setRange(0, scrollSize);
+ verticalScrollBar()->setValue(verticalScrollBar()->maximum());
+ updateViewport();
+}
+
+void TerminalWidget::resizeEvent(QResizeEvent *event)
+{
+ event->accept();
+
+ // If increasing in size, we'll trigger libvterm to call sb_popline in
+ // order to pull lines out of the history. This will cause the scrollback
+ // to decrease in size which reduces the size of the verticalScrollBar.
+ // That will trigger a scroll offset increase which we want to ignore.
+ m_ignoreScroll = true;
+
+ applySizeChange();
+
+ setSelection(std::nullopt);
+ m_ignoreScroll = false;
+}
+
+QRect TerminalWidget::gridToViewport(QRect rect) const
+{
+ int offset = verticalScrollBar()->value();
+
+ int startRow = rect.y() - offset;
+ int numRows = rect.height();
+ int numCols = rect.width();
+
+ QRectF r{rect.x() * m_cellSize.width(),
+ startRow * m_cellSize.height(),
+ numCols * m_cellSize.width(),
+ numRows * m_cellSize.height()};
+
+ r.translate(0, topMargin());
+
+ return r.toAlignedRect();
+}
+
+void TerminalWidget::updateViewport()
+{
+ viewport()->update();
+}
+
+void TerminalWidget::updateViewportRect(const QRect &rect)
+{
+ viewport()->update(rect);
+}
+
+void TerminalWidget::wheelEvent(QWheelEvent *event)
+{
+ verticalScrollBar()->event(event);
+}
+
+void TerminalWidget::focusInEvent(QFocusEvent *)
+{
+ updateViewport();
+ configBlinkTimer();
+ updateCopyState();
+}
+void TerminalWidget::focusOutEvent(QFocusEvent *)
+{
+ updateViewport();
+ configBlinkTimer();
+}
+
+void TerminalWidget::inputMethodEvent(QInputMethodEvent *event)
+{
+ m_preEditString = event->preeditString();
+
+ if (event->commitString().isEmpty()) {
+ updateViewport();
+ return;
+ }
+
+ m_surface->sendKey(event->commitString());
+}
+
+void TerminalWidget::mousePressEvent(QMouseEvent *event)
+{
+ m_scrollDirection = 0;
+
+ m_activeMouseSelect.start = viewportToGlobal(event->pos());
+
+ if (event->button() == Qt::LeftButton && event->modifiers() & Qt::ControlModifier) {
+ if (m_linkSelection) {
+ if (event->modifiers() & Qt::ShiftModifier) {
+ copyLinkToClipboard();
+ return;
+ }
+
+ if (m_linkSelection->link.targetFilePath.scheme().toString().startsWith("http")) {
+ QDesktopServices::openUrl(m_linkSelection->link.targetFilePath.toUrl());
+ return;
+ }
+
+ if (m_linkSelection->link.targetFilePath.isDir())
+ Core::FileUtils::showInFileSystemView(m_linkSelection->link.targetFilePath);
+ else
+ EditorManager::openEditorAt(m_linkSelection->link);
+ }
+ return;
+ }
+
+ if (event->button() == Qt::LeftButton) {
+ if (std::chrono::system_clock::now() - m_lastDoubleClick < 500ms) {
+ m_selectLineMode = true;
+ const Selection newSelection{m_surface->gridToPos(
+ {0, m_surface->posToGrid(m_selection->start).y()}),
+ m_surface->gridToPos(
+ {m_surface->liveSize().width(),
+ m_surface->posToGrid(m_selection->end).y()}),
+ false};
+ setSelection(newSelection);
+ } else {
+ m_selectLineMode = false;
+ int pos = m_surface->gridToPos(globalToGrid(viewportToGlobal(event->pos())));
+ setSelection(Selection{pos, pos, false});
+ }
+ event->accept();
+ updateViewport();
+ } else if (event->button() == Qt::RightButton) {
+ if (event->modifiers() & Qt::ShiftModifier) {
+ QMenu *contextMenu = new QMenu(this);
+ QAction *configureAction = new QAction(contextMenu);
+ configureAction->setText(Tr::tr("Configure..."));
+ connect(configureAction, &QAction::triggered, this, [] {
+ ICore::showOptionsDialog("Terminal.General");
+ });
+
+ contextMenu->addAction(ActionManager::command(Constants::COPY)->action());
+ contextMenu->addAction(ActionManager::command(Constants::PASTE)->action());
+ contextMenu->addSeparator();
+ contextMenu->addAction(ActionManager::command(Constants::CLEAR_TERMINAL)->action());
+ contextMenu->addSeparator();
+ contextMenu->addAction(configureAction);
+
+ contextMenu->popup(event->globalPos());
+ } else if (m_selection) {
+ copyToClipboard();
+ setSelection(std::nullopt);
+ } else {
+ pasteFromClipboard();
+ }
+ } else if (event->button() == Qt::MiddleButton) {
+ QClipboard *clipboard = QApplication::clipboard();
+ if (clipboard->supportsSelection()) {
+ const QString selectionText = clipboard->text(QClipboard::Selection);
+ if (!selectionText.isEmpty())
+ m_surface->pasteFromClipboard(selectionText);
+ } else {
+ m_surface->pasteFromClipboard(textFromSelection());
+ }
+ }
+}
+void TerminalWidget::mouseMoveEvent(QMouseEvent *event)
+{
+ if (m_selection && event->buttons() & Qt::LeftButton) {
+ Selection newSelection = *m_selection;
+ int scrollVelocity = 0;
+ if (event->pos().y() < 0) {
+ scrollVelocity = (event->pos().y());
+ } else if (event->pos().y() > viewport()->height()) {
+ scrollVelocity = (event->pos().y() - viewport()->height());
+ }
+
+ if ((scrollVelocity != 0) != m_scrollTimer.isActive()) {
+ if (scrollVelocity != 0)
+ m_scrollTimer.start();
+ else
+ m_scrollTimer.stop();
+ }
+
+ m_scrollDirection = scrollVelocity;
+
+ if (m_scrollTimer.isActive() && scrollVelocity != 0) {
+ const std::chrono::milliseconds scrollInterval = 1000ms / qAbs(scrollVelocity);
+ if (m_scrollTimer.intervalAsDuration() != scrollInterval)
+ m_scrollTimer.setInterval(scrollInterval);
+ }
+
+ QPoint posBoundedToViewport = event->pos();
+ posBoundedToViewport.setX(qBound(0, posBoundedToViewport.x(), viewport()->width()));
+
+ int start = m_surface->gridToPos(globalToGrid(m_activeMouseSelect.start));
+ int newEnd = m_surface->gridToPos(globalToGrid(viewportToGlobal(posBoundedToViewport)));
+
+ if (start > newEnd) {
+ std::swap(start, newEnd);
+ }
+ if (start < 0)
+ start = 0;
+
+ if (m_selectLineMode) {
+ newSelection.start = m_surface->gridToPos({0, m_surface->posToGrid(start).y()});
+ newSelection.end = m_surface->gridToPos(
+ {m_surface->liveSize().width(), m_surface->posToGrid(newEnd).y()});
+ } else {
+ newSelection.start = start;
+ newSelection.end = newEnd;
+ }
+
+ setSelection(newSelection);
+ } else if (event->modifiers() & Qt::ControlModifier) {
+ checkLinkAt(event->pos());
+ } else if (m_linkSelection) {
+ m_linkSelection.reset();
+ updateCopyState();
+ updateViewport();
+ }
+
+ if (m_linkSelection) {
+ setCursor(Qt::PointingHandCursor);
+ } else {
+ setCursor(Qt::IBeamCursor);
+ }
+}
+
+bool TerminalWidget::checkLinkAt(const QPoint &pos)
+{
+ const TextAndOffsets hit = textAt(pos);
+
+ if (hit.text.size() > 0) {
+ QString t = QString::fromUcs4(hit.text.c_str(), hit.text.size()).trimmed();
+ t = chopIfEndsWith(t, ':');
+
+ if (!t.isEmpty()) {
+ if (t.startsWith("~/"))
+ t = QDir::homePath() + t.mid(1);
+
+ Link link = Link::fromString(t, true);
+
+ if (!link.targetFilePath.isEmpty() && !link.targetFilePath.isAbsolutePath())
+ link.targetFilePath = m_cwd.pathAppended(link.targetFilePath.path());
+
+ if (link.hasValidTarget()
+ && (link.targetFilePath.scheme().toString().startsWith("http")
+ || link.targetFilePath.exists())) {
+ const LinkSelection newSelection = LinkSelection{{hit.start, hit.end}, link};
+ if (!m_linkSelection || *m_linkSelection != newSelection) {
+ m_linkSelection = newSelection;
+ updateViewport();
+ updateCopyState();
+ }
+ return true;
+ }
+ }
+ }
+
+ if (m_linkSelection) {
+ m_linkSelection.reset();
+ updateCopyState();
+ updateViewport();
+ }
+ return false;
+}
+
+void TerminalWidget::mouseReleaseEvent(QMouseEvent *event)
+{
+ m_scrollTimer.stop();
+
+ if (m_selection && event->button() == Qt::LeftButton) {
+ if (m_selection->end - m_selection->start == 0)
+ setSelection(std::nullopt);
+ else
+ setSelection(Selection{m_selection->start, m_selection->end, true});
+ }
+}
+
+TerminalWidget::TextAndOffsets TerminalWidget::textAt(const QPoint &pos) const
+{
+ auto it = m_surface->iteratorAt(globalToGrid(viewportToGlobal(pos)));
+ auto itRev = m_surface->rIteratorAt(globalToGrid(viewportToGlobal(pos)));
+
+ std::u32string whiteSpaces = U" \t\x00a0";
+
+ const bool inverted = whiteSpaces.find(*it) != std::u32string::npos || *it == 0;
+
+ auto predicate = [inverted, whiteSpaces](const std::u32string::value_type &ch) {
+ if (inverted)
+ return ch != 0 && whiteSpaces.find(ch) == std::u32string::npos;
+ else
+ return ch == 0 || whiteSpaces.find(ch) != std::u32string::npos;
+ };
+
+ auto itRight = std::find_if(it, m_surface->end(), predicate);
+ auto itLeft = std::find_if(itRev, m_surface->rend(), predicate);
+
+ std::u32string text;
+ std::copy(itLeft.base(), it, std::back_inserter(text));
+ std::copy(it, itRight, std::back_inserter(text));
+ std::transform(text.begin(), text.end(), text.begin(), [](const char32_t &ch) {
+ return ch == 0 ? U' ' : ch;
+ });
+
+ return {(itLeft.base()).position(), itRight.position(), text};
+}
+
+void TerminalWidget::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ const auto hit = textAt(event->pos());
+
+ setSelection(Selection{hit.start, hit.end, true});
+
+ m_lastDoubleClick = std::chrono::system_clock::now();
+
+ event->accept();
+}
+
+void TerminalWidget::dragEnterEvent(QDragEnterEvent *event)
+{
+ if (event->mimeData()->hasUrls()) {
+ event->setDropAction(Qt::CopyAction);
+ event->accept();
+ }
+}
+
+void TerminalWidget::dropEvent(QDropEvent *event)
+{
+ QString urls = Utils::transform(event->mimeData()->urls(), [](const QUrl &url) {
+ return QString("\"%1\"").arg(url.toDisplayString(QUrl::PreferLocalFile));
+ }).join(" ");
+
+ writeToPty(urls.toUtf8());
+ event->setDropAction(Qt::CopyAction);
+ event->accept();
+}
+
+void TerminalWidget::showEvent(QShowEvent *event)
+{
+ Q_UNUSED(event);
+
+ if (!m_process)
+ setupPty();
+
+ QAbstractScrollArea::showEvent(event);
+}
+
+bool TerminalWidget::event(QEvent *event)
+{
+ if (event->type() == QEvent::Paint) {
+ QPainter p(this);
+ p.fillRect(QRect(QPoint(0, 0), size()), m_currentColors[ColorIndex::Background]);
+ return true;
+ }
+
+ if (event->type() == QEvent::KeyPress) {
+ QKeyEvent *k = (QKeyEvent *) event;
+ keyPressEvent(k);
+ return true;
+ }
+ if (event->type() == QEvent::KeyRelease) {
+ QKeyEvent *k = (QKeyEvent *) event;
+ keyReleaseEvent(k);
+ return true;
+ }
+
+ return QAbstractScrollArea::event(event);
+}
+
+void TerminalWidget::initActions()
+{
+ Core::Context context(Utils::Id("TerminalWidget"));
+
+ static QAction copy;
+ static QAction paste;
+ static QAction clearSelection;
+ static QAction clearTerminal;
+ static QAction moveCursorWordLeft;
+ static QAction moveCursorWordRight;
+ static QAction close;
+
+ copy.setText(Tr::tr("Copy"));
+ paste.setText(Tr::tr("Paste"));
+ clearSelection.setText(Tr::tr("Clear Selection"));
+ clearTerminal.setText(Tr::tr("Clear Terminal"));
+ moveCursorWordLeft.setText(Tr::tr("Move Cursor Word Left"));
+ moveCursorWordRight.setText(Tr::tr("Move Cursor Word Right"));
+ close.setText(Tr::tr("Close Terminal"));
+
+ ActionManager::registerAction(&copy, Constants::COPY, context)
+ ->setDefaultKeySequences({QKeySequence(
+ HostOsInfo::isMacHost() ? QLatin1String("Ctrl+C") : QLatin1String("Ctrl+Shift+C"))});
+
+ ActionManager::registerAction(&paste, Constants::PASTE, context)
+ ->setDefaultKeySequences({QKeySequence(
+ HostOsInfo::isMacHost() ? QLatin1String("Ctrl+V") : QLatin1String("Ctrl+Shift+V"))});
+
+ ActionManager::registerAction(&clearSelection, Constants::CLEARSELECTION, context);
+
+ ActionManager::registerAction(&moveCursorWordLeft, Constants::MOVECURSORWORDLEFT, context)
+ ->setDefaultKeySequences({QKeySequence("Alt+Left")});
+
+ ActionManager::registerAction(&moveCursorWordRight, Constants::MOVECURSORWORDRIGHT, context)
+ ->setDefaultKeySequences({QKeySequence("Alt+Right")});
+
+ ActionManager::registerAction(&clearTerminal, Constants::CLEAR_TERMINAL, context);
+}
+
+UnlockedGlobalAction TerminalWidget::unlockGlobalAction(const Utils::Id &commandId,
+ const Context &context)
+{
+ QAction *srcAction = ActionManager::command(commandId)->actionForContext(
+ Core::Constants::C_GLOBAL);
+
+ ProxyAction *proxy = ProxyAction::proxyActionWithIcon(srcAction, srcAction->icon());
+ ActionManager::registerAction(proxy, commandId, context);
+
+ UnlockedGlobalAction registeredAction(proxy, [commandId](QAction *a) {
+ ActionManager::unregisterAction(a, commandId);
+ delete a;
+ });
+
+ return registeredAction;
+}
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalwidget.h b/src/plugins/terminal/terminalwidget.h
new file mode 100644
index 0000000000..4b82e4355a
--- /dev/null
+++ b/src/plugins/terminal/terminalwidget.h
@@ -0,0 +1,256 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "terminalsearch.h"
+#include "terminalsurface.h"
+
+#include <aggregation/aggregate.h>
+
+#include <coreplugin/icontext.h>
+
+#include <utils/link.h>
+#include <utils/process.h>
+#include <utils/terminalhooks.h>
+
+#include <QAbstractScrollArea>
+#include <QAction>
+#include <QTextLayout>
+#include <QTimer>
+
+#include <chrono>
+#include <memory>
+
+namespace Terminal {
+
+using UnlockedGlobalAction = std::unique_ptr<QAction, std::function<void(QAction *)>>;
+
+class TerminalWidget : public QAbstractScrollArea
+{
+ friend class CellIterator;
+ Q_OBJECT
+public:
+ TerminalWidget(QWidget *parent = nullptr,
+ const Utils::Terminal::OpenTerminalParameters &openParameters = {});
+
+ void setFont(const QFont &font);
+
+ void copyToClipboard();
+ void pasteFromClipboard();
+ void copyLinkToClipboard();
+
+ void clearSelection();
+
+ void zoomIn();
+ void zoomOut();
+
+ void moveCursorWordLeft();
+ void moveCursorWordRight();
+
+ void clearContents();
+
+ void closeTerminal();
+
+ TerminalSearch *search() { return m_search.get(); }
+
+ struct Selection
+ {
+ int start;
+ int end;
+ bool final{false};
+
+ bool operator!=(const Selection &other) const
+ {
+ return start != other.start || end != other.end || final != other.final;
+ }
+
+ bool operator==(const Selection &other) const { return !operator!=(other); }
+ };
+
+ struct LinkSelection : public Selection
+ {
+ Utils::Link link;
+
+ bool operator!=(const LinkSelection &other) const
+ {
+ return link != other.link || Selection::operator!=(other);
+ }
+ };
+
+ void setShellName(const QString &shellName);
+ QString shellName() const;
+
+ Utils::FilePath cwd() const;
+ Utils::CommandLine currentCommand() const;
+ std::optional<Utils::Id> identifier() const;
+ QProcess::ProcessState processState() const;
+
+ void restart(const Utils::Terminal::OpenTerminalParameters &openParameters);
+
+ static void initActions();
+
+ [[nodiscard]] static UnlockedGlobalAction unlockGlobalAction(const Utils::Id &commandId,
+ const Core::Context &context);
+
+signals:
+ void started(qint64 pid);
+ void cwdChanged(const Utils::FilePath &cwd);
+ void commandChanged(const Utils::CommandLine &cmd);
+
+protected:
+ void paintEvent(QPaintEvent *event) override;
+ void keyPressEvent(QKeyEvent *event) override;
+ void keyReleaseEvent(QKeyEvent *event) override;
+ void resizeEvent(QResizeEvent *event) override;
+ void wheelEvent(QWheelEvent *event) override;
+ void focusInEvent(QFocusEvent *event) override;
+ void focusOutEvent(QFocusEvent *event) override;
+ void inputMethodEvent(QInputMethodEvent *event) override;
+
+ void mousePressEvent(QMouseEvent *event) override;
+ void mouseMoveEvent(QMouseEvent *event) override;
+ void mouseReleaseEvent(QMouseEvent *event) override;
+ void mouseDoubleClickEvent(QMouseEvent *event) override;
+
+ void dragEnterEvent(QDragEnterEvent *event) override;
+ void dropEvent(QDropEvent *event) override;
+
+ void showEvent(QShowEvent *event) override;
+
+ bool event(QEvent *event) override;
+
+protected:
+ void onReadyRead(bool forceFlush);
+ void setupSurface();
+ void setupFont();
+ void setupPty();
+ void setupColors();
+ void setupActions();
+
+ void writeToPty(const QByteArray &data);
+
+ int paintCell(QPainter &p,
+ const QRectF &cellRect,
+ QPoint gridPos,
+ const Internal::TerminalCell &cell,
+ QFont &f,
+ QList<SearchHit>::const_iterator &searchIt) const;
+ void paintCells(QPainter &painter, QPaintEvent *event) const;
+ void paintCursor(QPainter &painter) const;
+ void paintPreedit(QPainter &painter) const;
+ bool paintFindMatches(QPainter &painter,
+ QList<SearchHit>::const_iterator &searchIt,
+ const QRectF &cellRect,
+ const QPoint gridPos) const;
+ bool paintSelection(QPainter &painter, const QRectF &cellRect, const QPoint gridPos) const;
+ void paintDebugSelection(QPainter &painter, const Selection &selection) const;
+
+ qreal topMargin() const;
+
+ QPoint viewportToGlobal(QPoint p) const;
+ QPoint globalToViewport(QPoint p) const;
+ QPoint globalToGrid(QPointF p) const;
+ QPointF gridToGlobal(QPoint p, bool bottom = false, bool right = false) const;
+ QRect gridToViewport(QRect rect) const;
+
+ void updateViewport();
+ void updateViewportRect(const QRect &rect);
+
+ int textLineFromPixel(int y) const;
+ std::optional<int> textPosFromPoint(const QTextLayout &textLayout, QPoint p) const;
+
+ std::optional<QTextLayout::FormatRange> selectionToFormatRange(
+ TerminalWidget::Selection selection, const QTextLayout &layout, int rowOffset) const;
+
+ bool checkLinkAt(const QPoint &pos);
+
+ struct TextAndOffsets
+ {
+ int start;
+ int end;
+ std::u32string text;
+ };
+
+ TextAndOffsets textAt(const QPoint &pos) const;
+
+ void applySizeChange();
+ void updateScrollBars();
+
+ void flushVTerm(bool force);
+
+ bool setSelection(const std::optional<Selection> &selection, bool scroll = true);
+ QString textFromSelection() const;
+
+ void configBlinkTimer();
+
+ QColor toQColor(std::variant<int, QColor> color) const;
+
+ void updateCopyState();
+
+private:
+ Core::Context m_context;
+ std::unique_ptr<Utils::Process> m_process;
+ std::unique_ptr<Internal::TerminalSurface> m_surface;
+ std::unique_ptr<ShellIntegration> m_shellIntegration;
+
+ QString m_shellName;
+ Utils::Id m_identifier;
+
+ QFont m_font;
+ QSizeF m_cellSize;
+
+ bool m_ignoreScroll{false};
+
+ QString m_preEditString;
+
+ std::optional<Selection> m_selection;
+ std::optional<LinkSelection> m_linkSelection;
+
+ struct
+ {
+ QPoint start;
+ QPoint end;
+ } m_activeMouseSelect;
+
+ QTimer m_flushDelayTimer;
+
+ QTimer m_scrollTimer;
+ int m_scrollDirection{0};
+
+ std::array<QColor, 20> m_currentColors;
+
+ Utils::Terminal::OpenTerminalParameters m_openParameters;
+
+ std::chrono::system_clock::time_point m_lastFlush;
+ std::chrono::system_clock::time_point m_lastDoubleClick;
+ bool m_selectLineMode{false};
+
+ Internal::Cursor m_cursor;
+ QTimer m_cursorBlinkTimer;
+ bool m_cursorBlinkState{true};
+
+ Utils::FilePath m_cwd;
+ Utils::CommandLine m_currentCommand;
+
+ using TerminalSearchPtr = std::unique_ptr<TerminalSearch, std::function<void(TerminalSearch *)>>;
+ TerminalSearchPtr m_search;
+
+ Aggregation::Aggregate *m_aggregate{nullptr};
+ SearchHit m_lastSelectedHit{};
+
+ QAction m_copy;
+ QAction m_paste;
+ QAction m_clearSelection;
+ QAction m_clearTerminal;
+ QAction m_moveCursorWordLeft;
+ QAction m_moveCursorWordRight;
+ QAction m_close;
+
+ UnlockedGlobalAction m_findInDocument;
+ UnlockedGlobalAction m_exit;
+ UnlockedGlobalAction m_options;
+ UnlockedGlobalAction m_settings;
+};
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/tests/colors b/src/plugins/terminal/tests/colors
new file mode 100755
index 0000000000..a1910d45cc
--- /dev/null
+++ b/src/plugins/terminal/tests/colors
@@ -0,0 +1,112 @@
+#!/usr/bin/python3
+# Source: https://gist.github.com/lilydjwg/fdeaf79e921c2f413f44b6f613f6ad53
+
+from functools import partial
+
+
+def colors16():
+ for bold in [0, 1]:
+ for i in range(30, 38):
+ for j in range(40, 48):
+ print(f'\x1b[{bold};{i};{j}m {bold};{i};{j} |\x1b[0m', end='')
+ print()
+ print()
+
+ for bold in [0, 1]:
+ for i in range(90, 98):
+ for j in range(100, 108):
+ print(f'\x1b[{bold};{i};{j}m {bold};{i};{j} |\x1b[0m', end='')
+ print()
+ print()
+
+
+def color1(c, n=0):
+ print(f'\x1b[{n};38;5;{c}m{c:4}\x1b[0m', end='')
+
+
+def color1_sep(c):
+ if (c - 15) % 18 == 0:
+ print()
+
+
+def color2(c):
+ print(f'\x1b[48;5;{c}m \x1b[0m', end='')
+
+
+def color2_sep(c):
+ if (c - 15) % 36 == 0:
+ print()
+ elif (c - 15) % 6 == 0:
+ print(' ', end='')
+
+
+def colors256(color, sepfunc):
+ for i in range(0, 8):
+ color(i)
+ print()
+ for i in range(8, 16):
+ color(i)
+ print('\n')
+
+ for i in range(16, 232):
+ color(i)
+ sepfunc(i)
+ print()
+
+ for i in range(232, 256):
+ color(i)
+ print('\n')
+
+
+def colors_gradient():
+ s = '/\\' * 40
+ for col in range(0, 77):
+ r = 255 - col * 255 // 76
+ g = col * 510 // 76
+ b = col * 255 // 76
+ if g > 255:
+ g = 510 - g
+ print(
+ f'\x1b[48;2;{r};{g};{b}m\x1b[38;2;{255-r};{255-g};{255-b}m{s[col]}\x1b[0m', end='')
+ print()
+
+
+def other_attributes():
+ for i in range(0, 10):
+ print(f' \x1b[{i}mSGR {i:2}\x1b[m', end=' ')
+ print(' \x1b[53mSGR 53\x1b[m', end=' ') # overline
+ print('\n')
+ # https://askubuntu.com/a/985386/235132
+ for i in range(1, 6):
+ print(f' \x1b[4:{i}mSGR 4:{i}\x1b[m', end=' ')
+ print(' \x1b[21mSGR 21\x1b[m', end=' ')
+
+ print(
+ ' \x1b[4:3m\x1b[58;2;135;0;255mtruecolor underline\x1b[59m\x1b[4:0m', end=' ')
+ print(' \x1b]8;;https://askubuntu.com/a/985386/235132\x1b\\hyperlink\x1b]8;;\x1b\\')
+
+
+if __name__ == '__main__':
+ print('basic 16 colors, foreground & background:\n')
+ colors16()
+
+ print('256 colors:\n')
+ colors256(color1, color1_sep)
+
+ print('256 colors, bold:\n')
+ colors256(partial(color1, n=1), color1_sep)
+
+ print('256 colors, dim:\n')
+ colors256(partial(color1, n=2), color1_sep)
+
+ print('256 colors, bold dim:\n')
+ colors256(partial(color1, n='1;2'), color1_sep)
+
+ print('256 colors, solid background:\n')
+ colors256(color2, color2_sep)
+
+ print('true colors gradient:\n')
+ colors_gradient()
+
+ print('other attributes:\n')
+ other_attributes()
diff --git a/src/plugins/terminal/tests/cursor/bar b/src/plugins/terminal/tests/cursor/bar
new file mode 100755
index 0000000000..a7bd99b55d
--- /dev/null
+++ b/src/plugins/terminal/tests/cursor/bar
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo -e -n "\x1b[\x36 q" # Steady bar
diff --git a/src/plugins/terminal/tests/cursor/blinkbar b/src/plugins/terminal/tests/cursor/blinkbar
new file mode 100755
index 0000000000..0acf6179d9
--- /dev/null
+++ b/src/plugins/terminal/tests/cursor/blinkbar
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo -e -n "\x1b[\x35 q" # Blinking bar
diff --git a/src/plugins/terminal/tests/cursor/blinkblock b/src/plugins/terminal/tests/cursor/blinkblock
new file mode 100755
index 0000000000..c536c83b8b
--- /dev/null
+++ b/src/plugins/terminal/tests/cursor/blinkblock
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo -e -n "\x1b[\x31 q" # Blinking block (default)
diff --git a/src/plugins/terminal/tests/cursor/blinkunderline b/src/plugins/terminal/tests/cursor/blinkunderline
new file mode 100755
index 0000000000..745d9e2a29
--- /dev/null
+++ b/src/plugins/terminal/tests/cursor/blinkunderline
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo -e -n "\x1b[\x33 q" # Blinking underline
diff --git a/src/plugins/terminal/tests/cursor/block b/src/plugins/terminal/tests/cursor/block
new file mode 100755
index 0000000000..421df3b8c5
--- /dev/null
+++ b/src/plugins/terminal/tests/cursor/block
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo -e -n "\x1b[\x32 q" # Steady block
diff --git a/src/plugins/terminal/tests/cursor/underline b/src/plugins/terminal/tests/cursor/underline
new file mode 100755
index 0000000000..7638b5358d
--- /dev/null
+++ b/src/plugins/terminal/tests/cursor/underline
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo -e -n "\x1b[\x34 q" # Steady underline
diff --git a/src/plugins/terminal/tests/decoration b/src/plugins/terminal/tests/decoration
new file mode 100755
index 0000000000..a584d46092
--- /dev/null
+++ b/src/plugins/terminal/tests/decoration
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+printf "\e[1mbold\e[0m\n"
+printf "\e[3mitalic\e[0m\n"
+printf "\e[3m\e[1mbold italic\e[0m\n"
+printf "\e[4munderline (abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ)\e[0m\n"
+printf "\e[9mstrikethrough\e[0m\n"
+printf "\e[31mHello World\e[0m\n"
+printf "\x1B[31mHello World\e[0m\n"
diff --git a/src/plugins/terminal/tests/filenames b/src/plugins/terminal/tests/filenames
new file mode 100755
index 0000000000..6a4e33e3ed
--- /dev/null
+++ b/src/plugins/terminal/tests/filenames
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+echo "Home:"
+echo "~/"
+
+FULLPATH=$(readlink -f "$0")
+
+echo "This file:"
+echo "$FULLPATH"
+
+echo "This file, this line:"
+echo "$FULLPATH:11"
+
+echo "This file, this line, this word:"
+echo "$FULLPATH:14:34"
+
+echo "This file, with an error message:"
+echo "$FULLPATH:18:23: error: C++ requires a type specifier for all declarations"
+
+echo "A link: http://google.com"
+echo "Another link: https://www.qt.io"
+echo "Another one: https://codereview.qt-project.org/c/qt-creator/qt-creator/+/464740"
diff --git a/src/plugins/terminal/tests/integration b/src/plugins/terminal/tests/integration
new file mode 100755
index 0000000000..ac17432cb6
--- /dev/null
+++ b/src/plugins/terminal/tests/integration
@@ -0,0 +1,70 @@
+#!/bin/bash
+
+echo "Testing integration response, best start this from a terminal that has no builtin integration"
+echo "e.g. 'sh'"
+echo
+
+echo -e "\033[1m ⎆ Current dir should have changed to '/Some/Dir/Here'\033[0m"
+printf "\033]7;file:///Some/Dir/Here\033\\"
+
+read -p " ⎆ Press enter to continue " -n1 -s
+echo
+echo
+
+echo -e "\033[1m ⎆ Current dir should have changed to '/Some/Other/Dir/Here'\033[0m"
+printf "\033]1337;CurrentDir=/Some/Other/Dir/Here\033\\"
+
+read -p " ⎆ Press enter to continue " -n1 -s
+echo
+echo
+
+
+echo -e "\033[1m ⎆ Current dir should have changed to '/VSCode/dir/with space'\033[0m"
+printf "\033]633P;Cwd=/VSCode/dir/with space\033\\"
+
+read -p " ⎆ Press enter to continue " -n1 -s
+echo
+echo
+
+echo -e "\033[1m ⎆ The current process should have changed to 'test'\033[0m"
+printf "\033]633E;test with arguments\033\\"
+
+read -p " ⎆ Press enter to continue " -n1 -s
+echo
+echo
+
+echo -e "\033[1m ⎆ The current process should have changed to 'test with space'\033[0m"
+printf "\033]633E;'test with space'\033\\"
+
+read -p " ⎆ Press enter to continue " -n1 -s
+echo
+echo
+
+echo -e "\033[1m ⎆ The current process should have changed to 'test with space v2'\033[0m"
+printf "\033]633E;\"test with space v2\"\033\\"
+
+read -p " ⎆ Press enter to continue " -n1 -s
+echo
+echo
+
+echo -e "\033[1m ⎆ The current process should have changed to 'test with space v3'\033[0m"
+printf "\033]633E;\"./test/test with space v3\" -argument\033\\"
+
+read -p " ⎆ Press enter to continue " -n1 -s
+echo
+echo
+
+echo -e "\033[1m ⎆ The current process should have changed to 'cat'\033[0m"
+printf "\033]633E;cat /dev/random | base64 -argument\033\\"
+
+read -p " ⎆ Press enter to continue " -n1 -s
+echo
+echo
+
+echo -e "\033[1m ⎆ The current process should have changed to 'cat me'\033[0m"
+printf "\033]633E;cat\\ me args \033\\"
+
+read -p " ⎆ Press enter to continue " -n1 -s
+echo
+echo
+
diff --git a/src/plugins/texteditor/CMakeLists.txt b/src/plugins/texteditor/CMakeLists.txt
index 01636fe754..048cd40b1d 100644
--- a/src/plugins/texteditor/CMakeLists.txt
+++ b/src/plugins/texteditor/CMakeLists.txt
@@ -70,6 +70,7 @@ add_qtc_plugin(TextEditor
ioutlinewidget.h
linenumberfilter.cpp linenumberfilter.h
marginsettings.cpp marginsettings.h
+ markdowneditor.cpp markdowneditor.h
outlinefactory.cpp outlinefactory.h
plaintexteditorfactory.cpp plaintexteditorfactory.h
quickfix.cpp quickfix.h
@@ -112,5 +113,7 @@ add_qtc_plugin(TextEditor
extend_qtc_plugin(TextEditor
CONDITION WITH_TESTS
- SOURCES texteditor_test.cpp
+ SOURCES
+ codeassist/codeassist_test.cpp codeassist/codeassist_test.h
+ texteditor_test.cpp
)
diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp
index 29cccc6c10..99f37d05ea 100644
--- a/src/plugins/texteditor/basefilefind.cpp
+++ b/src/plugins/texteditor/basefilefind.cpp
@@ -21,6 +21,7 @@
#include <utils/fadingindicator.h>
#include <utils/filesearch.h>
#include <utils/futuresynchronizer.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
#include <utils/stylehelper.h>
@@ -38,6 +39,98 @@ using namespace Utils;
using namespace Core;
namespace TextEditor {
+
+static std::optional<QRegularExpression> regExpFromParameters(const FileFindParameters &parameters)
+{
+ if (!(parameters.flags & FindRegularExpression))
+ return {};
+
+ const QRegularExpression::PatternOptions options = parameters.flags & FindCaseSensitively
+ ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption;
+ QRegularExpression regExp;
+ regExp.setPattern(parameters.text);
+ regExp.setPatternOptions(options);
+ return regExp;
+}
+
+void searchInProcessOutput(QPromise<SearchResultItems> &promise,
+ const FileFindParameters &parameters,
+ const ProcessSetupHandler &processSetupHandler,
+ const ProcessOutputParser &processOutputParser)
+{
+ if (promise.isCanceled())
+ return;
+
+ QEventLoop loop;
+
+ Process process;
+ processSetupHandler(process);
+
+ const std::optional<QRegularExpression> regExp = regExpFromParameters(parameters);
+ QStringList outputBuffer;
+ // The states transition exactly in this order:
+ enum State { BelowLimit, AboveLimit, Paused, Resumed };
+ State state = BelowLimit;
+ int reportedItemsCount = 0;
+ QFuture<void> future(promise.future());
+ process.setStdOutCallback([&](const QString &output) {
+ if (promise.isCanceled()) {
+ process.close();
+ loop.quit();
+ return;
+ }
+ // The SearchResultWidget is going to pause the search anyway, so start buffering
+ // the output.
+ if (state == AboveLimit || state == Paused) {
+ outputBuffer.append(output);
+ } else {
+ const SearchResultItems items = processOutputParser(future, output, regExp);
+ if (!items.isEmpty())
+ promise.addResult(items);
+ reportedItemsCount += items.size();
+ if (state == BelowLimit && reportedItemsCount > 200000)
+ state = AboveLimit;
+ }
+ });
+ QObject::connect(&process, &Process::done, &loop, [&loop, &promise, &state] {
+ if (state == BelowLimit || state == Resumed || promise.isCanceled())
+ loop.quit();
+ });
+
+ if (promise.isCanceled())
+ return;
+
+ process.start();
+ if (process.state() == QProcess::NotRunning)
+ return;
+
+ QFutureWatcher<void> watcher;
+ QObject::connect(&watcher, &QFutureWatcherBase::canceled, &loop, [&process, &loop] {
+ process.close();
+ loop.quit();
+ });
+ QObject::connect(&watcher, &QFutureWatcherBase::paused, &loop, [&state] { state = Paused; });
+ QObject::connect(&watcher, &QFutureWatcherBase::resumed, &loop, [&] {
+ state = Resumed;
+ for (const QString &output : outputBuffer) {
+ if (promise.isCanceled()) {
+ process.close();
+ loop.quit();
+ }
+ const SearchResultItems items = processOutputParser(future, output, regExp);
+ if (!items.isEmpty())
+ promise.addResult(items);
+ }
+ outputBuffer.clear();
+ if (process.state() == QProcess::NotRunning)
+ loop.quit();
+ });
+ watcher.setFuture(future);
+ if (promise.isCanceled())
+ return;
+ loop.exec(QEventLoop::ExcludeUserInputEvents);
+}
+
namespace Internal {
class InternalEngine : public TextEditor::SearchEngine
@@ -51,9 +144,8 @@ public:
QVariant parameters() const override { return {}; }
void readSettings(QSettings * /*settings*/) override {}
void writeSettings(QSettings * /*settings*/) const override {}
- QFuture<Utils::FileSearchResultList> executeSearch(
- const TextEditor::FileFindParameters &parameters,
- BaseFileFind *baseFileFind) override
+ QFuture<SearchResultItems> executeSearch(const TextEditor::FileFindParameters &parameters,
+ BaseFileFind *baseFileFind) override
{
const auto func = parameters.flags & FindRegularExpression ? Utils::findInFilesRegExp
: Utils::findInFiles;
@@ -65,7 +157,7 @@ public:
TextDocument::openedTextDocumentContents());
}
- Core::IEditor *openEditor(const Core::SearchResultItem &/*item*/,
+ Core::IEditor *openEditor(const SearchResultItem &/*item*/,
const TextEditor::FileFindParameters &/*parameters*/) override
{
return nullptr;
@@ -91,8 +183,6 @@ public:
class BaseFileFindPrivate
{
public:
- BaseFileFindPrivate() { m_futureSynchronizer.setCancelOnWait(true); }
-
QPointer<IFindSupport> m_currentFindSupport;
Utils::FutureSynchronizer m_futureSynchronizer;
@@ -210,34 +300,6 @@ void BaseFileFind::setCurrentSearchEngine(int index)
emit currentSearchEngineChanged();
}
-static QString displayText(const QString &line)
-{
- QString result = line;
- auto end = result.end();
- for (auto it = result.begin(); it != end; ++it) {
- if (!it->isSpace() && !it->isPrint())
- *it = QChar('?');
- }
- return result;
-}
-
-static void displayResult(QFutureWatcher<FileSearchResultList> *watcher,
- SearchResult *search, int index)
-{
- const FileSearchResultList results = watcher->resultAt(index);
- QList<SearchResultItem> items;
- for (const FileSearchResult &result : results) {
- SearchResultItem item;
- item.setFilePath(result.fileName);
- item.setMainRange(result.lineNumber, result.matchStart, result.matchLength);
- item.setLineText(displayText(result.matchingLine));
- item.setUseTextEditorFont(true);
- item.setUserData(result.regexpCapturedTexts);
- items << item;
- }
- search->addResults(items, SearchResult::AddOrdered);
-}
-
void BaseFileFind::runNewSearch(const QString &txt, FindFlags findFlags,
SearchResultWindow::SearchMode searchMode)
{
@@ -285,7 +347,7 @@ void BaseFileFind::runSearch(SearchResult *search)
{
const FileFindParameters parameters = search->userData().value<FileFindParameters>();
SearchResultWindow::instance()->popup(IOutputPane::Flags(IOutputPane::ModeSwitch|IOutputPane::WithFocus));
- auto watcher = new QFutureWatcher<FileSearchResultList>();
+ auto watcher = new QFutureWatcher<SearchResultItems>;
watcher->setPendingResultsLimit(1);
// search is deleted if it is removed from search panel
connect(search, &QObject::destroyed, watcher, &QFutureWatcherBase::cancel);
@@ -295,14 +357,14 @@ void BaseFileFind::runSearch(SearchResult *search)
watcher->setPaused(paused);
});
connect(watcher, &QFutureWatcherBase::resultReadyAt, search, [watcher, search](int index) {
- displayResult(watcher, search, index);
+ search->addResults(watcher->resultAt(index), SearchResult::AddOrdered);
});
// auto-delete:
connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater);
connect(watcher, &QFutureWatcherBase::finished, search, [watcher, search]() {
search->finishSearch(watcher->isCanceled());
});
- QFuture<FileSearchResultList> future = executeSearch(parameters);
+ QFuture<SearchResultItems> future = executeSearch(parameters);
watcher->setFuture(future);
d->m_futureSynchronizer.addFuture(future);
FutureProgress *progress = ProgressManager::addTask(future,
@@ -332,8 +394,7 @@ void BaseFileFind::addSearchEngine(SearchEngine *searchEngine)
setCurrentSearchEngine(0);
}
-void BaseFileFind::doReplace(const QString &text,
- const QList<SearchResultItem> &items,
+void BaseFileFind::doReplace(const QString &text, const SearchResultItems &items,
bool preserveCase)
{
const FilePaths files = replaceAll(text, items, preserveCase);
@@ -377,7 +438,7 @@ QList<QPair<QWidget *, QWidget *>> BaseFileFind::createPatternWidgets()
syncComboWithSettings(d->m_filterCombo, d->m_filterSetting);
QLabel *exclusionLabel = createLabel(msgExclusionPatternLabel());
d->m_exclusionCombo = createCombo(&d->m_exclusionStrings);
- d->m_exclusionCombo->setToolTip(msgFilePatternToolTip());
+ d->m_exclusionCombo->setToolTip(msgFilePatternToolTip(Utils::InclusionType::Excluded));
exclusionLabel->setBuddy(d->m_exclusionCombo);
syncComboWithSettings(d->m_exclusionCombo, d->m_exclusionSetting);
return {{filterLabel, d->m_filterCombo}, {exclusionLabel, d->m_exclusionCombo}};
@@ -474,8 +535,7 @@ void BaseFileFind::recheckEnabled(SearchResult *search)
search->setSearchAgainEnabled(isEnabled());
}
-FilePaths BaseFileFind::replaceAll(const QString &text,
- const QList<SearchResultItem> &items,
+FilePaths BaseFileFind::replaceAll(const QString &text, const SearchResultItems &items,
bool preserveCase)
{
if (items.isEmpty())
@@ -483,7 +543,7 @@ FilePaths BaseFileFind::replaceAll(const QString &text,
RefactoringChanges refactoring;
- QHash<FilePath, QList<SearchResultItem> > changes;
+ QHash<FilePath, SearchResultItems> changes;
for (const SearchResultItem &item : items)
changes[FilePath::fromUserInput(item.path().constFirst())].append(item);
@@ -504,7 +564,7 @@ FilePaths BaseFileFind::replaceAll(const QString &text,
for (auto it = changes.cbegin(), end = changes.cend(); it != end; ++it) {
const FilePath filePath = it.key();
- const QList<SearchResultItem> changeItems = it.value();
+ const SearchResultItems changeItems = it.value();
ChangeSet changeSet;
RefactoringFilePtr file = refactoring.file(filePath);
@@ -519,9 +579,13 @@ FilePaths BaseFileFind::replaceAll(const QString &text,
if (item.userData().canConvert<QStringList>() && !item.userData().toStringList().isEmpty()) {
replacement = Utils::expandRegExpReplacement(text, item.userData().toStringList());
} else if (preserveCase) {
- const QString originalText = (item.mainRange().length(item.lineText()) == 0)
- ? item.lineText()
- : item.mainRange().mid(item.lineText());
+ Text::Range range = item.mainRange();
+ range.end.line -= range.begin.line - 1;
+ range.begin.line = 1;
+ QString originalText = item.lineText();
+ const int rangeLength = range.length(item.lineText());
+ if (rangeLength > 0)
+ originalText = originalText.mid(range.begin.column, rangeLength);
replacement = Utils::matchCaseReplacement(originalText, text);
} else {
replacement = text;
@@ -545,7 +609,7 @@ QVariant BaseFileFind::getAdditionalParameters(SearchResult *search)
return search->userData().value<FileFindParameters>().additionalParameters;
}
-QFuture<FileSearchResultList> BaseFileFind::executeSearch(const FileFindParameters &parameters)
+QFuture<SearchResultItems> BaseFileFind::executeSearch(const FileFindParameters &parameters)
{
return d->m_searchEngines[parameters.searchEngineIndex]->executeSearch(parameters, this);
}
diff --git a/src/plugins/texteditor/basefilefind.h b/src/plugins/texteditor/basefilefind.h
index 7181197814..c8555f03de 100644
--- a/src/plugins/texteditor/basefilefind.h
+++ b/src/plugins/texteditor/basefilefind.h
@@ -4,20 +4,25 @@
#pragma once
#include "texteditor_global.h"
-#include <utils/filesearch.h>
#include <coreplugin/find/ifindfilter.h>
#include <coreplugin/find/searchresultwindow.h>
+#include <utils/filesearch.h>
+#include <utils/searchresultitem.h>
+
#include <QFuture>
-namespace Utils { class FileIterator; }
namespace Core {
class IEditor;
class SearchResult;
-class SearchResultItem;
} // namespace Core
+namespace Utils {
+class FileIterator;
+class Process;
+}
+
namespace TextEditor {
namespace Internal {
@@ -37,6 +42,16 @@ public:
Core::FindFlags flags;
};
+using ProcessSetupHandler = std::function<void(Utils::Process &)>;
+using ProcessOutputParser = std::function<Utils::SearchResultItems(
+ const QFuture<void> &, const QString &, const std::optional<QRegularExpression> &)>;
+
+// Call it from a non-main thread only, it's a blocking invocation.
+void TEXTEDITOR_EXPORT searchInProcessOutput(QPromise<Utils::SearchResultItems> &promise,
+ const FileFindParameters &parameters,
+ const ProcessSetupHandler &processSetupHandler,
+ const ProcessOutputParser &processOutputParser);
+
class BaseFileFind;
class TEXTEDITOR_EXPORT SearchEngine : public QObject
@@ -52,9 +67,9 @@ public:
virtual QVariant parameters() const = 0;
virtual void readSettings(QSettings *settings) = 0;
virtual void writeSettings(QSettings *settings) const = 0;
- virtual QFuture<Utils::FileSearchResultList> executeSearch(
+ virtual QFuture<Utils::SearchResultItems> executeSearch(
const FileFindParameters &parameters, BaseFileFind *baseFileFind) = 0;
- virtual Core::IEditor *openEditor(const Core::SearchResultItem &item,
+ virtual Core::IEditor *openEditor(const Utils::SearchResultItem &item,
const FileFindParameters &parameters) = 0;
bool isEnabled() const;
void setEnabled(bool enabled);
@@ -81,8 +96,7 @@ public:
void addSearchEngine(SearchEngine *searchEngine);
/* returns the list of unique files that were passed in items */
- static Utils::FilePaths replaceAll(const QString &txt,
- const QList<Core::SearchResultItem> &items,
+ static Utils::FilePaths replaceAll(const QString &txt, const Utils::SearchResultItems &items,
bool preserveCase = false);
virtual Utils::FileIterator *files(const QStringList &nameFilters,
const QStringList &exclusionFilters,
@@ -94,7 +108,7 @@ protected:
virtual QString label() const = 0; // see Core::SearchResultWindow::startNewSearch
virtual QString toolTip() const = 0; // see Core::SearchResultWindow::startNewSearch,
// add %1 placeholder where the find flags should be put
- QFuture<Utils::FileSearchResultList> executeSearch(const FileFindParameters &parameters);
+ QFuture<Utils::SearchResultItems> executeSearch(const FileFindParameters &parameters);
void writeCommonSettings(QSettings *settings);
void readCommonSettings(QSettings *settings, const QString &defaultFilter, const QString &defaultExclusionFilter);
@@ -111,10 +125,8 @@ signals:
void currentSearchEngineChanged();
private:
- void openEditor(Core::SearchResult *result, const Core::SearchResultItem &item);
- void doReplace(const QString &txt,
- const QList<Core::SearchResultItem> &items,
- bool preserveCase);
+ void openEditor(Core::SearchResult *result, const Utils::SearchResultItem &item);
+ void doReplace(const QString &txt, const Utils::SearchResultItems &items, bool preserveCase);
void hideHighlightAll(bool visible);
void searchAgain(Core::SearchResult *search);
virtual void recheckEnabled(Core::SearchResult *search);
diff --git a/src/plugins/texteditor/basehoverhandler.h b/src/plugins/texteditor/basehoverhandler.h
index 9b6d90fd89..c24ae4a1df 100644
--- a/src/plugins/texteditor/basehoverhandler.h
+++ b/src/plugins/texteditor/basehoverhandler.h
@@ -39,7 +39,8 @@ protected:
Priority_None = 0,
Priority_Tooltip = 5,
Priority_Help = 10,
- Priority_Diagnostic = 20
+ Priority_Diagnostic = 20,
+ Priority_Suggestion = 40
};
void setPriority(int priority);
int priority() const;
diff --git a/src/plugins/texteditor/behaviorsettingspage.cpp b/src/plugins/texteditor/behaviorsettingspage.cpp
index 5d39745807..73675e2cdb 100644
--- a/src/plugins/texteditor/behaviorsettingspage.cpp
+++ b/src/plugins/texteditor/behaviorsettingspage.cpp
@@ -10,6 +10,7 @@
#include "simplecodestylepreferences.h"
#include "storagesettings.h"
#include "tabsettings.h"
+#include "tabsettingswidget.h"
#include "texteditorconstants.h"
#include "texteditorsettings.h"
#include "texteditortr.h"
@@ -35,12 +36,12 @@
namespace TextEditor {
-struct BehaviorSettingsPage::BehaviorSettingsPagePrivate : public QObject
+class BehaviorSettingsPagePrivate : public QObject
{
+public:
BehaviorSettingsPagePrivate();
const QString m_settingsPrefix{"text"};
- QPointer<QWidget> m_widget;
TextEditor::BehaviorSettingsWidget *m_behaviorWidget = nullptr;
CodeStylePool *m_defaultCodeStylePool = nullptr;
@@ -52,7 +53,7 @@ struct BehaviorSettingsPage::BehaviorSettingsPagePrivate : public QObject
ExtraEncodingSettings m_extraEncodingSettings;
};
-BehaviorSettingsPage::BehaviorSettingsPagePrivate::BehaviorSettingsPagePrivate()
+BehaviorSettingsPagePrivate::BehaviorSettingsPagePrivate()
{
// global tab preferences for all other languages
m_codeStyle = new SimpleCodeStylePreferences(this);
@@ -71,38 +72,22 @@ BehaviorSettingsPage::BehaviorSettingsPagePrivate::BehaviorSettingsPagePrivate()
m_extraEncodingSettings.fromSettings(m_settingsPrefix, s);
}
-BehaviorSettingsPage::BehaviorSettingsPage()
- : d(new BehaviorSettingsPagePrivate)
+class BehaviorSettingsWidgetImpl : public Core::IOptionsPageWidget
{
- // Add the GUI used to configure the tab, storage and interaction settings
- setId(Constants::TEXT_EDITOR_BEHAVIOR_SETTINGS);
- setDisplayName(Tr::tr("Behavior"));
-
- setCategory(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY);
- setDisplayCategory(Tr::tr("Text Editor"));
- setCategoryIconPath(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY_ICON_PATH);
-}
-
-BehaviorSettingsPage::~BehaviorSettingsPage()
-{
- delete d;
-}
-
-QWidget *BehaviorSettingsPage::widget()
-{
- if (!d->m_widget) {
- d->m_widget = new QWidget;
- d->m_behaviorWidget = new BehaviorSettingsWidget(d->m_widget);
+public:
+ BehaviorSettingsWidgetImpl(BehaviorSettingsPagePrivate *d) : d(d)
+ {
+ d->m_behaviorWidget = new BehaviorSettingsWidget(this);
auto verticalSpacer = new QSpacerItem(20, 13, QSizePolicy::Minimum, QSizePolicy::Expanding);
- auto gridLayout = new QGridLayout(d->m_widget);
+ auto gridLayout = new QGridLayout(this);
if (Utils::HostOsInfo::isMacHost())
gridLayout->setContentsMargins(-1, 0, -1, 0); // don't ask.
gridLayout->addWidget(d->m_behaviorWidget, 0, 0, 1, 1);
gridLayout->addItem(verticalSpacer, 1, 0, 1, 1);
- d->m_pageCodeStyle = new SimpleCodeStylePreferences(d->m_widget);
+ d->m_pageCodeStyle = new SimpleCodeStylePreferences(this);
d->m_pageCodeStyle->setDelegatingPool(d->m_codeStyle->delegatingPool());
d->m_pageCodeStyle->setTabSettings(d->m_codeStyle->tabSettings());
d->m_pageCodeStyle->setCurrentDelegate(d->m_codeStyle->currentDelegate());
@@ -111,14 +96,49 @@ QWidget *BehaviorSettingsPage::widget()
TabSettingsWidget *tabSettingsWidget = d->m_behaviorWidget->tabSettingsWidget();
tabSettingsWidget->setCodingStyleWarningVisible(true);
connect(tabSettingsWidget, &TabSettingsWidget::codingStyleLinkClicked,
- this, &BehaviorSettingsPage::openCodingStylePreferences);
-
- settingsToUI();
+ this, [] (TabSettingsWidget::CodingStyleLink link) {
+ switch (link) {
+ case TabSettingsWidget::CppLink:
+ Core::ICore::showOptionsDialog(CppEditor::Constants::CPP_CODE_STYLE_SETTINGS_ID);
+ break;
+ case TabSettingsWidget::QtQuickLink:
+ Core::ICore::showOptionsDialog(QmlJSTools::Constants::QML_JS_CODE_STYLE_SETTINGS_ID);
+ break;
+ }
+ });
+
+ d->m_behaviorWidget->setAssignedTypingSettings(d->m_typingSettings);
+ d->m_behaviorWidget->setAssignedStorageSettings(d->m_storageSettings);
+ d->m_behaviorWidget->setAssignedBehaviorSettings(d->m_behaviorSettings);
+ d->m_behaviorWidget->setAssignedExtraEncodingSettings(d->m_extraEncodingSettings);
+ d->m_behaviorWidget->setAssignedCodec(Core::EditorManager::defaultTextCodec());
+ d->m_behaviorWidget->setAssignedLineEnding(Core::EditorManager::defaultLineEnding());
}
- return d->m_widget;
+
+ void apply() final;
+
+ BehaviorSettingsPagePrivate *d;
+};
+
+BehaviorSettingsPage::BehaviorSettingsPage()
+ : d(new BehaviorSettingsPagePrivate)
+{
+ // Add the GUI used to configure the tab, storage and interaction settings
+ setId(Constants::TEXT_EDITOR_BEHAVIOR_SETTINGS);
+ setDisplayName(Tr::tr("Behavior"));
+
+ setCategory(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY);
+ setDisplayCategory(Tr::tr("Text Editor"));
+ setCategoryIconPath(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY_ICON_PATH);
+ setWidgetCreator([this] { return new BehaviorSettingsWidgetImpl(d); });
}
-void BehaviorSettingsPage::apply()
+BehaviorSettingsPage::~BehaviorSettingsPage()
+{
+ delete d;
+}
+
+void BehaviorSettingsWidgetImpl::apply()
{
if (!d->m_behaviorWidget) // page was never shown
return;
@@ -128,8 +148,10 @@ void BehaviorSettingsPage::apply()
BehaviorSettings newBehaviorSettings;
ExtraEncodingSettings newExtraEncodingSettings;
- settingsFromUI(&newTypingSettings, &newStorageSettings,
- &newBehaviorSettings, &newExtraEncodingSettings);
+ d->m_behaviorWidget->assignedTypingSettings(&newTypingSettings);
+ d->m_behaviorWidget->assignedStorageSettings(&newStorageSettings);
+ d->m_behaviorWidget->assignedBehaviorSettings(&newBehaviorSettings);
+ d->m_behaviorWidget->assignedExtraEncodingSettings(&newExtraEncodingSettings);
QSettings *s = Core::ICore::settings();
QTC_ASSERT(s, return);
@@ -178,32 +200,6 @@ void BehaviorSettingsPage::apply()
d->m_behaviorWidget->assignedLineEnding());
}
-void BehaviorSettingsPage::settingsFromUI(TypingSettings *typingSettings,
- StorageSettings *storageSettings,
- BehaviorSettings *behaviorSettings,
- ExtraEncodingSettings *extraEncodingSettings) const
-{
- d->m_behaviorWidget->assignedTypingSettings(typingSettings);
- d->m_behaviorWidget->assignedStorageSettings(storageSettings);
- d->m_behaviorWidget->assignedBehaviorSettings(behaviorSettings);
- d->m_behaviorWidget->assignedExtraEncodingSettings(extraEncodingSettings);
-}
-
-void BehaviorSettingsPage::settingsToUI()
-{
- d->m_behaviorWidget->setAssignedTypingSettings(d->m_typingSettings);
- d->m_behaviorWidget->setAssignedStorageSettings(d->m_storageSettings);
- d->m_behaviorWidget->setAssignedBehaviorSettings(d->m_behaviorSettings);
- d->m_behaviorWidget->setAssignedExtraEncodingSettings(d->m_extraEncodingSettings);
- d->m_behaviorWidget->setAssignedCodec(Core::EditorManager::defaultTextCodec());
- d->m_behaviorWidget->setAssignedLineEnding(Core::EditorManager::defaultLineEnding());
-}
-
-void BehaviorSettingsPage::finish()
-{
- delete d->m_widget;
-}
-
ICodeStylePreferences *BehaviorSettingsPage::codeStyle() const
{
return d->m_codeStyle;
@@ -235,16 +231,4 @@ const ExtraEncodingSettings &BehaviorSettingsPage::extraEncodingSettings() const
}
-void BehaviorSettingsPage::openCodingStylePreferences(TabSettingsWidget::CodingStyleLink link)
-{
- switch (link) {
- case TabSettingsWidget::CppLink:
- Core::ICore::showOptionsDialog(CppEditor::Constants::CPP_CODE_STYLE_SETTINGS_ID);
- break;
- case TabSettingsWidget::QtQuickLink:
- Core::ICore::showOptionsDialog(QmlJSTools::Constants::QML_JS_CODE_STYLE_SETTINGS_ID);
- break;
- }
-}
-
} // namespace TextEditor
diff --git a/src/plugins/texteditor/behaviorsettingspage.h b/src/plugins/texteditor/behaviorsettingspage.h
index 495a800d86..2ecb48ea29 100644
--- a/src/plugins/texteditor/behaviorsettingspage.h
+++ b/src/plugins/texteditor/behaviorsettingspage.h
@@ -3,10 +3,6 @@
#pragma once
-#include "texteditor_global.h"
-
-#include "tabsettingswidget.h"
-
#include <coreplugin/dialogs/ioptionspage.h>
QT_BEGIN_NAMESPACE
@@ -25,17 +21,10 @@ class CodeStylePool;
class BehaviorSettingsPage : public Core::IOptionsPage
{
- Q_OBJECT
-
public:
BehaviorSettingsPage();
~BehaviorSettingsPage() override;
- // IOptionsPage
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
ICodeStylePreferences *codeStyle() const;
CodeStylePool *codeStylePool() const;
const TypingSettings &typingSettings() const;
@@ -44,17 +33,8 @@ public:
const ExtraEncodingSettings &extraEncodingSettings() const;
private:
- void openCodingStylePreferences(TextEditor::TabSettingsWidget::CodingStyleLink link);
-
- void settingsFromUI(TypingSettings *typingSettings,
- StorageSettings *storageSettings,
- BehaviorSettings *behaviorSettings,
- ExtraEncodingSettings *extraEncodingSettings) const;
- void settingsToUI();
-
QList<QTextCodec *> m_codecs;
- struct BehaviorSettingsPagePrivate;
- BehaviorSettingsPagePrivate *d;
+ class BehaviorSettingsPagePrivate *d;
};
} // namespace TextEditor
diff --git a/src/plugins/texteditor/behaviorsettingswidget.cpp b/src/plugins/texteditor/behaviorsettingswidget.cpp
index 518f09c8a5..144afa6eb2 100644
--- a/src/plugins/texteditor/behaviorsettingswidget.cpp
+++ b/src/plugins/texteditor/behaviorsettingswidget.cpp
@@ -65,8 +65,6 @@ BehaviorSettingsWidget::BehaviorSettingsWidget(QWidget *parent)
: QWidget(parent)
, d(new BehaviorSettingsWidgetPrivate)
{
- resize(801, 693);
-
d->tabPreferencesWidget = new SimpleCodeStylePreferencesWidget(this);
d->tabPreferencesWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); // FIXME: Desirable?
@@ -165,7 +163,7 @@ BehaviorSettingsWidget::BehaviorSettingsWidget(QWidget *parent)
d->groupBoxMouse = new QGroupBox(Tr::tr("Mouse and Keyboard"));
- using namespace Utils::Layouting;
+ using namespace Layouting;
const auto indent = [](QWidget *inner) { return Row { Space(30), inner }; };
@@ -207,8 +205,9 @@ BehaviorSettingsWidget::BehaviorSettingsWidget(QWidget *parent)
Row {
Column { d->tabPreferencesWidget, d->groupBoxTyping, st },
- Column { d->groupBoxStorageSettings, d->groupBoxEncodings, d->groupBoxMouse, st }
- }.attachTo(this, WithoutMargins);
+ Column { d->groupBoxStorageSettings, d->groupBoxEncodings, d->groupBoxMouse, st },
+ noMargin,
+ }.attachTo(this);
connect(d->cleanWhitespace, &QCheckBox::toggled,
d->inEntireDocument, &QCheckBox::setEnabled);
diff --git a/src/plugins/texteditor/codeassist/asyncprocessor.cpp b/src/plugins/texteditor/codeassist/asyncprocessor.cpp
index 90f993a39c..3f440bfa70 100644
--- a/src/plugins/texteditor/codeassist/asyncprocessor.cpp
+++ b/src/plugins/texteditor/codeassist/asyncprocessor.cpp
@@ -6,7 +6,7 @@
#include "assistinterface.h"
#include "iassistproposal.h"
-#include <utils/runextensions.h>
+#include <utils/async.h>
namespace TextEditor {
@@ -21,7 +21,7 @@ IAssistProposal *AsyncProcessor::perform()
{
IAssistProposal *result = immediateProposal();
interface()->prepareForAsyncUse();
- m_watcher.setFuture(Utils::runAsync([this] {
+ m_watcher.setFuture(Utils::asyncRun([this] {
interface()->recreateTextDocument();
return performAsync();
}));
diff --git a/src/plugins/texteditor/codeassist/codeassist_test.cpp b/src/plugins/texteditor/codeassist/codeassist_test.cpp
new file mode 100644
index 0000000000..8b724dbe16
--- /dev/null
+++ b/src/plugins/texteditor/codeassist/codeassist_test.cpp
@@ -0,0 +1,152 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifdef WITH_TESTS
+
+#include "codeassist_test.h"
+
+#include "../texteditor.h"
+
+#include "assistinterface.h"
+#include "assistproposaliteminterface.h"
+#include "asyncprocessor.h"
+#include "completionassistprovider.h"
+#include "genericproposal.h"
+#include "genericproposalwidget.h"
+
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/editormanager/editormanager.h>
+#include <utils/temporarydirectory.h>
+
+#include <QtTest/QtTest>
+
+namespace TextEditor::Internal {
+
+class TestProposalItem : public AssistProposalItemInterface
+{
+public:
+ QString m_text = "test";
+ bool m_implicitlyApplies = false;
+ bool m_prematurelyApplies = false;
+ QIcon m_icon;
+ QString m_detail = "detail";
+ bool m_isSnippet = false;
+ bool m_isValid = true;
+
+ QString text() const override { return m_text; }
+ bool implicitlyApplies() const override { return m_implicitlyApplies; }
+ bool prematurelyApplies(const QChar &) const override { return m_prematurelyApplies; }
+ QIcon icon() const override { return m_icon; }
+ QString detail() const override { return m_detail; }
+ bool isSnippet() const override { return m_isSnippet; }
+ bool isValid() const override { return m_isValid; }
+ quint64 hash() const override { return 0; } // used to remove duplicates
+};
+
+class OpenEditorItem : public TestProposalItem
+{
+public:
+ void apply(TextDocumentManipulatorInterface &, int) const override
+ {
+ m_openedEditor = Core::EditorManager::openEditor(m_filePath,
+ Core::Constants::K_DEFAULT_TEXT_EDITOR_ID);
+ }
+
+ mutable Core::IEditor *m_openedEditor = nullptr;
+ Utils::FilePath m_filePath;
+};
+
+class TestProposalWidget : public GenericProposalWidget
+{
+public:
+ void showProposal(const QString &prefix) override
+ {
+ GenericProposalModelPtr proposalModel = model();
+ if (proposalModel && proposalModel->size() == 1) {
+ emit proposalItemActivated(proposalModel->proposalItem(0));
+ deleteLater();
+ return;
+ }
+ GenericProposalWidget::showProposal(prefix);
+ }
+};
+
+class TestProposal : public GenericProposal
+{
+public:
+ TestProposal(int pos, const QList<AssistProposalItemInterface *> &items)
+ : GenericProposal(pos, items)
+ {}
+ IAssistProposalWidget *createWidget() const override { return new TestProposalWidget; }
+};
+
+class TestProcessor : public AsyncProcessor
+{
+public:
+ TestProcessor(const QList<AssistProposalItemInterface *> &items)
+ : m_items(items)
+ {}
+ IAssistProposal *performAsync() override
+ { return new TestProposal(interface()->position(), m_items); }
+ QList<AssistProposalItemInterface *> m_items;
+};
+
+class TestProvider : public CompletionAssistProvider
+{
+public:
+ IAssistProcessor *createProcessor(const AssistInterface *assistInterface) const override
+ {
+ Q_UNUSED(assistInterface);
+ return new TestProcessor(m_items);
+ }
+ QList<AssistProposalItemInterface *> m_items;
+};
+
+void CodeAssistTests::initTestCase()
+{
+ Core::IEditor *editor = Core::EditorManager::openEditorWithContents(
+ Core::Constants::K_DEFAULT_TEXT_EDITOR_ID);
+ QVERIFY(editor);
+ m_editor = qobject_cast<BaseTextEditor *>(editor);
+ QVERIFY(m_editor);
+ m_editorsToClose << m_editor;
+ m_testProvider = new TestProvider();
+}
+
+static Utils::FilePath createBigFile()
+{
+ constexpr int textChunkSize = 65536; // from utils/textfileformat.cpp
+
+ const Utils::FilePath result = Utils::TemporaryDirectory::masterDirectoryFilePath() / "BigFile";
+ QByteArray data;
+ data.reserve(textChunkSize);
+ while (data.size() < textChunkSize)
+ data.append("bigfile line\n");
+ result.writeFileContents(data);
+ return result;
+}
+
+void CodeAssistTests::testFollowSymbolBigFile()
+{
+ auto item = new OpenEditorItem;
+ item->m_filePath = createBigFile();
+ m_testProvider->m_items = {item};
+ auto editorWidget = m_editor->editorWidget();
+
+ editorWidget->invokeAssist(FollowSymbol, m_testProvider);
+ QSignalSpy spy(editorWidget, &TextEditorWidget::assistFinished);
+ QVERIFY(spy.wait(1000));
+ QVERIFY(item->m_openedEditor);
+ m_editorsToClose << item->m_openedEditor;
+}
+
+void CodeAssistTests::cleanupTestCase()
+{
+ m_testProvider->m_items.clear();
+ Core::EditorManager::closeEditors(m_editorsToClose);
+ QVERIFY(Core::EditorManager::currentEditor() == nullptr);
+}
+
+} // namespace TextEditor::Internal
+
+#endif // ifdef WITH_TESTS
diff --git a/src/plugins/texteditor/codeassist/codeassist_test.h b/src/plugins/texteditor/codeassist/codeassist_test.h
new file mode 100644
index 0000000000..3bf734aadf
--- /dev/null
+++ b/src/plugins/texteditor/codeassist/codeassist_test.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#ifdef WITH_TESTS
+
+#include <QObject>
+
+namespace Core { class IEditor; }
+namespace TextEditor { class BaseTextEditor; }
+
+namespace TextEditor::Internal {
+
+class TestProvider;
+
+class CodeAssistTests : public QObject
+{
+ Q_OBJECT
+public:
+
+private slots:
+ void initTestCase();
+
+ void testFollowSymbolBigFile();
+
+ void cleanupTestCase();
+
+private:
+ TextEditor::BaseTextEditor *m_editor = nullptr;
+ QList<Core::IEditor *> m_editorsToClose;
+ TestProvider *m_testProvider = nullptr;
+};
+
+} // namespace TextEditor::Internal
+
+#endif
diff --git a/src/plugins/texteditor/codeassist/codeassistant.cpp b/src/plugins/texteditor/codeassist/codeassistant.cpp
index b3d9d477e2..430b7252cd 100644
--- a/src/plugins/texteditor/codeassist/codeassistant.cpp
+++ b/src/plugins/texteditor/codeassist/codeassistant.cpp
@@ -81,6 +81,7 @@ private:
IAssistProcessor *m_processor = nullptr;
AssistKind m_assistKind = TextEditor::Completion;
IAssistProposalWidget *m_proposalWidget = nullptr;
+ TextEditorWidget::SuggestionBlocker m_suggestionBlocker;
bool m_receivedContentWhileWaiting = false;
QTimer m_automaticProposalTimer;
CompletionSettings m_settings;
@@ -185,26 +186,26 @@ void CodeAssistantPrivate::requestProposal(AssistReason reason,
IAssistProcessor *processor = provider->createProcessor(assistInterface.get());
processor->setAsyncCompletionAvailableHandler([this, reason, processor](
IAssistProposal *newProposal) {
+ if (processor == m_processor) {
+ invalidateCurrentRequestData();
+ if (processor->needsRestart() && m_receivedContentWhileWaiting) {
+ delete newProposal;
+ m_receivedContentWhileWaiting = false;
+ requestProposal(reason, m_assistKind, m_requestProvider);
+ } else {
+ displayProposal(newProposal, reason);
+ if (processor->running())
+ m_processor = processor;
+ else
+ emit q->finished();
+ }
+ }
if (!processor->running()) {
// do not delete this processor directly since this function is called from within the processor
QMetaObject::invokeMethod(QCoreApplication::instance(), [processor] {
delete processor;
}, Qt::QueuedConnection);
}
- if (processor != m_processor)
- return;
- invalidateCurrentRequestData();
- if (processor->needsRestart() && m_receivedContentWhileWaiting) {
- delete newProposal;
- m_receivedContentWhileWaiting = false;
- requestProposal(reason, m_assistKind, m_requestProvider);
- } else {
- displayProposal(newProposal, reason);
- if (processor->running())
- m_processor = processor;
- else
- emit q->finished();
- }
});
if (IAssistProposal *newProposal = processor->start(std::move(assistInterface)))
@@ -251,6 +252,14 @@ void CodeAssistantPrivate::displayProposal(IAssistProposal *newProposal, AssistR
return;
}
+ if (m_editorWidget->suggestionVisible()) {
+ if (reason != ExplicitlyInvoked) {
+ destroyContext();
+ return;
+ }
+ m_editorWidget->clearSuggestion();
+ }
+
const QString prefix = m_editorWidget->textAt(basePosition,
m_editorWidget->position() - basePosition);
if (!newProposal->hasItemsToPropose(prefix, reason)) {
@@ -287,6 +296,7 @@ void CodeAssistantPrivate::displayProposal(IAssistProposal *newProposal, AssistR
m_proposalWidget->setDisplayRect(m_editorWidget->cursorRect(basePosition));
m_proposalWidget->setIsSynchronized(!m_receivedContentWhileWaiting);
m_proposalWidget->showProposal(prefix);
+ m_suggestionBlocker = m_editorWidget->blockSuggestions();
}
void CodeAssistantPrivate::processProposalItem(AssistProposalItemInterface *proposalItem)
@@ -329,6 +339,7 @@ void CodeAssistantPrivate::handlePrefixExpansion(const QString &newPrefix)
void CodeAssistantPrivate::finalizeProposal()
{
stopAutomaticProposalTimer();
+ m_suggestionBlocker.reset();
m_proposalWidget = nullptr;
if (m_receivedContentWhileWaiting)
m_receivedContentWhileWaiting = false;
@@ -445,6 +456,7 @@ void CodeAssistantPrivate::automaticProposalTimeout()
{
if (isWaitingForProposal()
|| m_editorWidget->multiTextCursor().hasMultipleCursors()
+ || m_editorWidget->suggestionVisible()
|| (isDisplayingProposal() && !m_proposalWidget->isFragile())) {
return;
}
diff --git a/src/plugins/texteditor/codestyleeditor.cpp b/src/plugins/texteditor/codestyleeditor.cpp
index ac75f1ad6d..070a230e38 100644
--- a/src/plugins/texteditor/codestyleeditor.cpp
+++ b/src/plugins/texteditor/codestyleeditor.cpp
@@ -29,6 +29,7 @@ CodeStyleEditor::CodeStyleEditor(ICodeStylePreferencesFactory *factory,
, m_codeStyle(codeStyle)
{
m_layout = new QVBoxLayout(this);
+ m_layout->setContentsMargins(0, 0, 0, 0);
auto selector = new CodeStyleSelectorWidget(factory, project, this);
selector->setCodeStyle(codeStyle);
m_additionalGlobalSettingsWidget = factory->createAdditionalGlobalSettings(codeStyle,
diff --git a/src/plugins/texteditor/codestyleselectorwidget.cpp b/src/plugins/texteditor/codestyleselectorwidget.cpp
index 56b8ca4c05..2b21fd12c3 100644
--- a/src/plugins/texteditor/codestyleselectorwidget.cpp
+++ b/src/plugins/texteditor/codestyleselectorwidget.cpp
@@ -33,8 +33,6 @@ CodeStyleSelectorWidget::CodeStyleSelectorWidget(ICodeStylePreferencesFactory *f
, m_factory(factory)
, m_project(project)
{
- resize(536, 59);
-
m_delegateComboBox = new QComboBox(this);
m_delegateComboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
@@ -48,7 +46,7 @@ CodeStyleSelectorWidget::CodeStyleSelectorWidget(ICodeStylePreferencesFactory *f
m_importButton = new QPushButton(Tr::tr("Import..."));
m_importButton->setEnabled(false);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Grid {
@@ -59,8 +57,8 @@ CodeStyleSelectorWidget::CodeStyleSelectorWidget(ICodeStylePreferencesFactory *f
m_exportButton,
m_importButton
},
-
- }.attachTo(this, WithoutMargins);
+ noMargin,
+ }.attachTo(this);
connect(m_delegateComboBox, &QComboBox::activated,
this, &CodeStyleSelectorWidget::slotComboBoxActivated);
diff --git a/src/plugins/texteditor/colorschemeedit.cpp b/src/plugins/texteditor/colorschemeedit.cpp
index 0bd0c54015..c6f661df42 100644
--- a/src/plugins/texteditor/colorschemeedit.cpp
+++ b/src/plugins/texteditor/colorschemeedit.cpp
@@ -6,13 +6,13 @@
#include "texteditortr.h"
#include <utils/layoutbuilder.h>
+#include <utils/qtcolorbutton.h>
#include <utils/theme/theme.h>
#include <utils/utilsicons.h>
#include <QAbstractListModel>
#include <QApplication>
#include <QCheckBox>
-#include <QColorDialog>
#include <QComboBox>
#include <QDoubleSpinBox>
#include <QLabel>
@@ -25,14 +25,6 @@ namespace TextEditor::Internal {
const int layoutSpacing = 6;
-static QString colorButtonStyleSheet(const QColor &bgColor)
-{
- QString rc("border-width: 2px; border-radius: 2px; border-color: black; ");
- rc += bgColor.isValid() ? "border-style: solid; background:" + bgColor.name() + ";"
- : QString("border-style: dotted;");
- return rc;
-}
-
class FormatsModel : public QAbstractListModel
{
public:
@@ -129,10 +121,9 @@ ColorSchemeEdit::ColorSchemeEdit(QWidget *parent) :
m_formatsModel(new FormatsModel(this))
{
setContentsMargins(0, layoutSpacing, 0, 0);
- resize(513, 416);
auto colorButton = [] () {
- auto tb = new QToolButton;
+ auto tb = new Utils::QtColorButton;
tb->setMinimumWidth(56);
return tb;
};
@@ -210,13 +201,14 @@ ColorSchemeEdit::ColorSchemeEdit(QWidget *parent) :
auto bottomSpacer = new QWidget;
bottomSpacer->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Expanding);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Row {
m_itemList,
m_builtinSchemeLabel,
m_fontProperties,
- }.attachTo(this, WithoutMargins);
+ noMargin
+ }.attachTo(this);
Grid {
m_foregroundLabel, m_foregroundToolButton, m_eraseForegroundToolButton, br,
@@ -245,9 +237,9 @@ ColorSchemeEdit::ColorSchemeEdit(QWidget *parent) :
connect(m_itemList->selectionModel(), &QItemSelectionModel::currentRowChanged,
this, &ColorSchemeEdit::currentItemChanged);
- connect(m_foregroundToolButton, &QAbstractButton::clicked,
+ connect(m_foregroundToolButton, &Utils::QtColorButton::colorChanged,
this, &ColorSchemeEdit::changeForeColor);
- connect(m_backgroundToolButton, &QAbstractButton::clicked,
+ connect(m_backgroundToolButton, &Utils::QtColorButton::colorChanged,
this, &ColorSchemeEdit::changeBackColor);
connect(m_eraseBackgroundToolButton, &QAbstractButton::clicked,
this, &ColorSchemeEdit::eraseBackColor);
@@ -265,7 +257,7 @@ ColorSchemeEdit::ColorSchemeEdit(QWidget *parent) :
this, &ColorSchemeEdit::checkCheckBoxes);
connect(m_italicCheckBox, &QAbstractButton::toggled,
this, &ColorSchemeEdit::checkCheckBoxes);
- connect(m_underlineColorToolButton, &QToolButton::clicked,
+ connect(m_underlineColorToolButton, &Utils::QtColorButton::colorChanged,
this, &ColorSchemeEdit::changeUnderlineColor);
connect(m_eraseUnderlineColorToolButton, &QToolButton::clicked,
this, &ColorSchemeEdit::eraseUnderlineColor);
@@ -347,7 +339,7 @@ void ColorSchemeEdit::updateForegroundControls()
m_foregroundToolButton->setVisible(isVisible);
m_eraseForegroundToolButton->setVisible(isVisible);
- m_foregroundToolButton->setStyleSheet(colorButtonStyleSheet(format.foreground()));
+ m_foregroundToolButton->setColor(format.foreground());
m_eraseForegroundToolButton->setEnabled(!m_readOnly
&& m_curItem > 0
&& format.foreground().isValid());
@@ -366,7 +358,7 @@ void ColorSchemeEdit::updateBackgroundControls()
m_backgroundToolButton->setVisible(isVisible);
m_eraseBackgroundToolButton->setVisible(isVisible);
- m_backgroundToolButton->setStyleSheet(colorButtonStyleSheet(format.background()));
+ m_backgroundToolButton->setColor(format.background());
m_eraseBackgroundToolButton->setEnabled(!m_readOnly
&& m_curItem > 0
&& format.background().isValid());
@@ -466,7 +458,7 @@ void ColorSchemeEdit::updateUnderlineControls()
m_eraseUnderlineColorToolButton->setVisible(isVisible);
m_underlineComboBox->setVisible(isVisible);
- m_underlineColorToolButton->setStyleSheet(colorButtonStyleSheet(format.underlineColor()));
+ m_underlineColorToolButton->setColor(format.underlineColor());
m_eraseUnderlineColorToolButton->setEnabled(!m_readOnly
&& m_curItem > 0
&& format.underlineColor().isValid());
@@ -478,11 +470,8 @@ void ColorSchemeEdit::changeForeColor()
{
if (m_curItem == -1)
return;
- QColor color = m_scheme.formatFor(m_descriptions[m_curItem].id()).foreground();
- const QColor newColor = QColorDialog::getColor(color, m_boldCheckBox->window());
- if (!newColor.isValid())
- return;
- m_foregroundToolButton->setStyleSheet(colorButtonStyleSheet(newColor));
+
+ const QColor newColor = m_foregroundToolButton->color();
m_eraseForegroundToolButton->setEnabled(true);
for (const QModelIndex &index : m_itemList->selectionModel()->selectedRows()) {
@@ -498,11 +487,8 @@ void ColorSchemeEdit::changeBackColor()
{
if (m_curItem == -1)
return;
- QColor color = m_scheme.formatFor(m_descriptions[m_curItem].id()).background();
- const QColor newColor = QColorDialog::getColor(color, m_boldCheckBox->window());
- if (!newColor.isValid())
- return;
- m_backgroundToolButton->setStyleSheet(colorButtonStyleSheet(newColor));
+
+ const QColor newColor = m_backgroundToolButton->color();
m_eraseBackgroundToolButton->setEnabled(true);
for (const QModelIndex &index : m_itemList->selectionModel()->selectedRows()) {
@@ -521,14 +507,13 @@ void ColorSchemeEdit::eraseBackColor()
{
if (m_curItem == -1)
return;
- QColor newColor;
- m_backgroundToolButton->setStyleSheet(colorButtonStyleSheet(newColor));
+ m_backgroundToolButton->setColor({});
m_eraseBackgroundToolButton->setEnabled(false);
const QList<QModelIndex> indexes = m_itemList->selectionModel()->selectedRows();
for (const QModelIndex &index : indexes) {
const TextStyle category = m_descriptions[index.row()].id();
- m_scheme.formatFor(category).setBackground(newColor);
+ m_scheme.formatFor(category).setBackground({});
m_formatsModel->emitDataChanged(index);
}
@@ -539,14 +524,13 @@ void ColorSchemeEdit::eraseForeColor()
{
if (m_curItem == -1)
return;
- QColor newColor;
- m_foregroundToolButton->setStyleSheet(colorButtonStyleSheet(newColor));
+ m_foregroundToolButton->setColor({});
m_eraseForegroundToolButton->setEnabled(false);
const QList<QModelIndex> indexes = m_itemList->selectionModel()->selectedRows();
for (const QModelIndex &index : indexes) {
const TextStyle category = m_descriptions[index.row()].id();
- m_scheme.formatFor(category).setForeground(newColor);
+ m_scheme.formatFor(category).setForeground({});
m_formatsModel->emitDataChanged(index);
}
@@ -635,11 +619,8 @@ void ColorSchemeEdit::changeUnderlineColor()
{
if (m_curItem == -1)
return;
- QColor color = m_scheme.formatFor(m_descriptions[m_curItem].id()).underlineColor();
- const QColor newColor = QColorDialog::getColor(color, m_boldCheckBox->window());
- if (!newColor.isValid())
- return;
- m_underlineColorToolButton->setStyleSheet(colorButtonStyleSheet(newColor));
+
+ const QColor newColor = m_underlineColorToolButton->color();
m_eraseUnderlineColorToolButton->setEnabled(true);
for (const QModelIndex &index : m_itemList->selectionModel()->selectedRows()) {
@@ -653,13 +634,12 @@ void ColorSchemeEdit::eraseUnderlineColor()
{
if (m_curItem == -1)
return;
- QColor newColor;
- m_underlineColorToolButton->setStyleSheet(colorButtonStyleSheet(newColor));
+ m_underlineColorToolButton->setColor({});
m_eraseUnderlineColorToolButton->setEnabled(false);
for (const QModelIndex &index : m_itemList->selectionModel()->selectedRows()) {
const TextStyle category = m_descriptions[index.row()].id();
- m_scheme.formatFor(category).setUnderlineColor(newColor);
+ m_scheme.formatFor(category).setUnderlineColor({});
m_formatsModel->emitDataChanged(index);
}
}
diff --git a/src/plugins/texteditor/colorschemeedit.h b/src/plugins/texteditor/colorschemeedit.h
index 645ece9efe..a2d5797079 100644
--- a/src/plugins/texteditor/colorschemeedit.h
+++ b/src/plugins/texteditor/colorschemeedit.h
@@ -20,6 +20,8 @@ class QScrollArea;
class QToolButton;
QT_END_NAMESPACE
+namespace Utils { class QtColorButton; }
+
namespace TextEditor::Internal {
class FormatsModel;
@@ -80,10 +82,10 @@ private:
QLabel *m_builtinSchemeLabel;
QWidget *m_fontProperties;
QLabel *m_foregroundLabel;
- QToolButton *m_foregroundToolButton;
+ Utils::QtColorButton *m_foregroundToolButton;
QAbstractButton *m_eraseForegroundToolButton;
QLabel *m_backgroundLabel;
- QToolButton *m_backgroundToolButton;
+ Utils::QtColorButton *m_backgroundToolButton;
QAbstractButton *m_eraseBackgroundToolButton;
QLabel *m_relativeForegroundHeadline;
QLabel *m_foregroundLightnessLabel;
@@ -100,7 +102,7 @@ private:
QCheckBox *m_italicCheckBox;
QLabel *m_underlineHeadline;
QLabel *m_underlineLabel;
- QToolButton *m_underlineColorToolButton;
+ Utils::QtColorButton *m_underlineColorToolButton;
QAbstractButton *m_eraseUnderlineColorToolButton;
QComboBox *m_underlineComboBox;
diff --git a/src/plugins/texteditor/completionsettingspage.cpp b/src/plugins/texteditor/completionsettingspage.cpp
index 6187688e4e..167cde25a7 100644
--- a/src/plugins/texteditor/completionsettingspage.cpp
+++ b/src/plugins/texteditor/completionsettingspage.cpp
@@ -66,8 +66,6 @@ private:
CompletionSettingsPageWidget::CompletionSettingsPageWidget(CompletionSettingsPage *owner)
: m_owner(owner)
{
- resize(823, 756);
-
m_caseSensitivity = new QComboBox;
m_caseSensitivity->addItem(Tr::tr("Full"));
m_caseSensitivity->addItem(Tr::tr("None"));
diff --git a/src/plugins/texteditor/displaysettingspage.cpp b/src/plugins/texteditor/displaysettingspage.cpp
index 58f9e04068..2449618bf0 100644
--- a/src/plugins/texteditor/displaysettingspage.cpp
+++ b/src/plugins/texteditor/displaysettingspage.cpp
@@ -4,6 +4,7 @@
#include "displaysettingspage.h"
#include "displaysettings.h"
+#include "fontsettings.h"
#include "marginsettings.h"
#include "texteditorconstants.h"
#include "texteditorsettings.h"
@@ -43,30 +44,23 @@ public:
DisplaySettingsWidget(DisplaySettingsPagePrivate *data)
: m_data(data)
{
- resize(452, 458);
-
enableTextWrapping = new QCheckBox(Tr::tr("Enable text &wrapping"));
enableTextWrappingHintLabel = new QLabel(Tr::tr("<i>Set <a href=\"font zoom\">font line spacing</a> "
"to 100% to enable text wrapping option.</i>"));
- fontSettingsPageLineSpacing = fontSettingsPageLineSpacingLink();
-
- if (fontSettingsPageLineSpacing) {
- connect(fontSettingsPageLineSpacing, &QSpinBox::valueChanged,
- this, [this](const int &value) {
- if (value != 100)
- enableTextWrapping->setChecked(false);
- enableTextWrapping->setEnabled(value == 100);
- enableTextWrappingHintLabel->setVisible(value != 100);
- });
-
- if (fontSettingsPageLineSpacing->value() != 100)
+ auto updateWrapping = [this] {
+ const bool normalLineSpacing = TextEditorSettings::fontSettings().relativeLineSpacing() == 100;
+ if (!normalLineSpacing)
enableTextWrapping->setChecked(false);
+ enableTextWrapping->setEnabled(normalLineSpacing);
+ enableTextWrappingHintLabel->setVisible(!normalLineSpacing);
+ };
- enableTextWrapping->setEnabled(fontSettingsPageLineSpacing->value() == 100);
- enableTextWrappingHintLabel->setVisible(fontSettingsPageLineSpacing->value() != 100);
- }
+ updateWrapping();
+
+ connect(TextEditorSettings::instance(), &TextEditorSettings::fontSettingsChanged,
+ this, updateWrapping);
connect(enableTextWrappingHintLabel, &QLabel::linkActivated, [] {
Core::ICore::showOptionsDialog(Constants::TEXT_EDITOR_FONT_SETTINGS); } );
@@ -113,7 +107,7 @@ public:
displayAnnotations = new QGroupBox(Tr::tr("Line annotations")),
displayAnnotations->setCheckable(true);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
leftAligned,
@@ -177,8 +171,6 @@ public:
void settingsToUI();
void setDisplaySettings(const DisplaySettings &, const MarginSettings &newMarginSettings);
- QSpinBox *fontSettingsPageLineSpacingLink();
-
DisplaySettingsPagePrivate *m_data = nullptr;
QCheckBox *enableTextWrapping;
@@ -208,8 +200,6 @@ public:
QRadioButton *atMargin;
QRadioButton *rightAligned;
QRadioButton *betweenLines;
-
- QSpinBox *fontSettingsPageLineSpacing = nullptr;
};
void DisplaySettingsWidget::apply()
@@ -226,10 +216,8 @@ void DisplaySettingsWidget::settingsFromUI(DisplaySettings &displaySettings,
{
displaySettings.m_displayLineNumbers = displayLineNumbers->isChecked();
displaySettings.m_textWrapping = enableTextWrapping->isChecked();
- if (fontSettingsPageLineSpacing) {
- if (fontSettingsPageLineSpacing->value() != 100)
- displaySettings.m_textWrapping = false;
- }
+ if (TextEditorSettings::fontSettings().relativeLineSpacing() != 100)
+ displaySettings.m_textWrapping = false;
marginSettings.m_showMargin = showWrapColumn->isChecked();
marginSettings.m_tintMarginArea = tintMarginArea->isChecked();
marginSettings.m_useIndenter = useIndenter->isChecked();
@@ -268,8 +256,10 @@ void DisplaySettingsWidget::settingsToUI()
enableTextWrapping->setChecked(displaySettings.m_textWrapping);
showWrapColumn->setChecked(marginSettings.m_showMargin);
tintMarginArea->setChecked(marginSettings.m_tintMarginArea);
+ tintMarginArea->setEnabled(marginSettings.m_showMargin);
useIndenter->setChecked(marginSettings.m_useIndenter);
wrapColumn->setValue(marginSettings.m_marginColumn);
+ wrapColumn->setEnabled(marginSettings.m_showMargin);
visualizeWhitespace->setChecked(displaySettings.m_visualizeWhitespace);
visualizeIndent->setChecked(displaySettings.m_visualizeIndent);
displayFoldingMarkers->setChecked(displaySettings.m_displayFoldingMarkers);
@@ -322,23 +312,6 @@ void DisplaySettingsWidget::setDisplaySettings(const DisplaySettings &newDisplay
}
}
- QSpinBox *DisplaySettingsWidget::fontSettingsPageLineSpacingLink()
- {
- for (const auto &page : Core::IOptionsPage::allOptionsPages()) {
- QWidget *widget = page->widget();
-
- if (!widget)
- continue;
-
- for (QSpinBox *spinBox : widget->findChildren<QSpinBox *>()) {
- if (spinBox->objectName() == QLatin1String("FontSettingsPage.LineSpacingSpinBox"))
- return spinBox;
- }
- }
-
- return nullptr;
- }
-
DisplaySettingsPage::DisplaySettingsPage()
: d(new DisplaySettingsPagePrivate)
{
diff --git a/src/plugins/texteditor/fontsettingspage.cpp b/src/plugins/texteditor/fontsettingspage.cpp
index 1e9ec7fafb..b57e23f35b 100644
--- a/src/plugins/texteditor/fontsettingspage.cpp
+++ b/src/plugins/texteditor/fontsettingspage.cpp
@@ -107,8 +107,6 @@ public:
{
m_lastValue = m_value;
- resize(639, 306);
-
m_antialias = new QCheckBox(Tr::tr("Antialias"));
m_antialias->setChecked(m_value.antialias());
@@ -119,7 +117,6 @@ public:
m_zoomSpinBox->setValue(m_value.fontZoom());
m_lineSpacingSpinBox = new QSpinBox;
- m_lineSpacingSpinBox->setObjectName(QLatin1String("FontSettingsPage.LineSpacingSpinBox"));
m_lineSpacingSpinBox->setSuffix(Tr::tr("%"));
m_lineSpacingSpinBox->setRange(50, 3000);
m_lineSpacingSpinBox->setValue(m_value.relativeLineSpacing());
diff --git a/src/plugins/texteditor/formattexteditor.cpp b/src/plugins/texteditor/formattexteditor.cpp
index 6f1139e057..03de5fb415 100644
--- a/src/plugins/texteditor/formattexteditor.cpp
+++ b/src/plugins/texteditor/formattexteditor.cpp
@@ -10,10 +10,10 @@
#include <coreplugin/messagemanager.h>
+#include <utils/async.h>
#include <utils/differ.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <utils/temporarydirectory.h>
#include <utils/textutils.h>
@@ -64,13 +64,12 @@ static FormatTask format(FormatTask task)
// Format temporary file
QStringList options = task.command.options();
options.replaceInStrings(QLatin1String("%file"), sourceFile.filePath().toString());
- QtcProcess process;
+ Process process;
process.setTimeoutS(5);
process.setCommand({executable, options});
process.runBlocking();
if (process.result() != ProcessResult::FinishedWithSuccess) {
- task.error = Tr::tr("TextEditor", "Failed to format: %1.")
- .arg(process.exitMessage());
+ task.error = Tr::tr("Failed to format: %1.").arg(process.exitMessage());
return task;
}
const QString output = process.cleanedStdErr();
@@ -89,7 +88,7 @@ static FormatTask format(FormatTask task)
return task;
case Command::PipeProcessing: {
- QtcProcess process;
+ Process process;
QStringList options = task.command.options();
options.replaceInStrings("%filename", task.filePath.fileName());
options.replaceInStrings("%file", task.filePath.toString());
@@ -249,8 +248,7 @@ void updateEditorText(QPlainTextEdit *editor, const QString &text)
static void showError(const QString &error)
{
- Core::MessageManager::writeFlashing(Tr::tr("TextEditor", "Error in text formatting: %1")
- .arg(error.trimmed()));
+ Core::MessageManager::writeFlashing(Tr::tr("Error in text formatting: %1").arg(error.trimmed()));
}
/**
@@ -324,7 +322,7 @@ void formatEditorAsync(TextEditorWidget *editor, const Command &command, int sta
checkAndApplyTask(watcher->result());
watcher->deleteLater();
});
- watcher->setFuture(Utils::runAsync(&format, FormatTask(editor, doc->filePath(), sd,
+ watcher->setFuture(Utils::asyncRun(&format, FormatTask(editor, doc->filePath(), sd,
command, startPos, endPos)));
}
diff --git a/src/plugins/texteditor/highlighter.cpp b/src/plugins/texteditor/highlighter.cpp
index a3765c4bf5..4512b71910 100644
--- a/src/plugins/texteditor/highlighter.cpp
+++ b/src/plugins/texteditor/highlighter.cpp
@@ -279,15 +279,24 @@ void Highlighter::highlightBlock(const QString &text)
return;
}
QTextBlock block = currentBlock();
- KSyntaxHighlighting::State state;
- TextDocumentLayout::setBraceDepth(block, TextDocumentLayout::braceDepth(block.previous()));
+ const QTextBlock previousBlock = block.previous();
+ TextDocumentLayout::setBraceDepth(block, TextDocumentLayout::braceDepth(previousBlock));
+ KSyntaxHighlighting::State previousLineState;
+ if (TextBlockUserData *data = TextDocumentLayout::textUserData(previousBlock))
+ previousLineState = data->syntaxState();
+ KSyntaxHighlighting::State oldState;
if (TextBlockUserData *data = TextDocumentLayout::textUserData(block)) {
- state = data->syntaxState();
+ oldState = data->syntaxState();
data->setFoldingStartIncluded(false);
data->setFoldingEndIncluded(false);
}
- state = highlightLine(text, state);
- const QTextBlock nextBlock = block.next();
+ KSyntaxHighlighting::State state = highlightLine(text, previousLineState);
+ if (oldState != state) {
+ TextBlockUserData *data = TextDocumentLayout::userData(block);
+ data->setSyntaxState(state);
+ // Toggles the LSB of current block's userState. It forces rehighlight of next block.
+ setCurrentBlockState(currentBlockState() ^ 1);
+ }
Parentheses parentheses;
int pos = 0;
@@ -300,13 +309,9 @@ void Highlighter::highlightBlock(const QString &text)
}
TextDocumentLayout::setParentheses(currentBlock(), parentheses);
+ const QTextBlock nextBlock = block.next();
if (nextBlock.isValid()) {
TextBlockUserData *data = TextDocumentLayout::userData(nextBlock);
- if (data->syntaxState() != state) {
- data->setSyntaxState(state);
- // Toggles the LSB of current block's userState. It forces rehighlight of next block.
- setCurrentBlockState(currentBlockState() ^ 1);
- }
data->setFoldingIndent(TextDocumentLayout::braceDepth(block));
}
diff --git a/src/plugins/texteditor/highlightersettingspage.cpp b/src/plugins/texteditor/highlightersettingspage.cpp
index ceb8f7d48f..4cc16a488a 100644
--- a/src/plugins/texteditor/highlightersettingspage.cpp
+++ b/src/plugins/texteditor/highlightersettingspage.cpp
@@ -13,7 +13,6 @@
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <QApplication>
#include <QDir>
#include <QLabel>
#include <QLineEdit>
@@ -24,24 +23,50 @@
using namespace Utils;
namespace TextEditor {
-namespace Internal {
-class HighlighterSettingsPageWidget : public QWidget
+class HighlighterSettingsPageWidget;
+
+class HighlighterSettingsPagePrivate
+{
+public:
+ void ensureInitialized()
+ {
+ if (m_initialized)
+ return;
+ m_initialized = true;
+ m_settings.fromSettings(m_settingsPrefix, Core::ICore::settings());
+ migrateGenericHighlighterFiles();
+ }
+
+ void migrateGenericHighlighterFiles()
+ {
+ QDir userDefinitionPath(m_settings.definitionFilesPath().toString());
+ if (userDefinitionPath.mkdir("syntax")) {
+ const auto link = Utils::HostOsInfo::isAnyUnixHost()
+ ? static_cast<bool(*)(const QString &, const QString &)>(&QFile::link)
+ : static_cast<bool(*)(const QString &, const QString &)>(&QFile::copy);
+
+ for (const QFileInfo &file : userDefinitionPath.entryInfoList({"*.xml"}, QDir::Files))
+ link(file.filePath(), file.absolutePath() + "/syntax/" + file.fileName());
+ }
+ }
+
+ bool m_initialized = false;
+ const QString m_settingsPrefix{"Text"};
+
+ HighlighterSettings m_settings;
+
+ QPointer<HighlighterSettingsPageWidget> m_widget;
+};
+
+class HighlighterSettingsPageWidget : public Core::IOptionsPageWidget
{
public:
- QLabel *definitionsInfolabel;
- QPushButton *downloadDefinitions;
- QLabel *updateStatus;
- PathChooser *definitionFilesPath;
- QPushButton *reloadDefinitions;
- QPushButton *resetCache;
- QLineEdit *ignoreEdit;
-
- HighlighterSettingsPageWidget()
+ HighlighterSettingsPageWidget(HighlighterSettingsPagePrivate *d) : d(d)
{
- resize(521, 332);
+ d->ensureInitialized();
- definitionsInfolabel = new QLabel(this);
+ auto definitionsInfolabel = new QLabel(this);
definitionsInfolabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
definitionsInfolabel->setTextFormat(Qt::RichText);
definitionsInfolabel->setAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter);
@@ -51,24 +76,25 @@ public:
"<a href=\"https://api.kde.org/frameworks/syntax-highlighting/html/index.html\">"
"KSyntaxHighlighting</a> engine.</p></body></html>"));
- downloadDefinitions = new QPushButton(Tr::tr("Download Definitions"));
+ auto downloadDefinitions = new QPushButton(Tr::tr("Download Definitions"));
downloadDefinitions->setToolTip(Tr::tr("Download missing and update existing syntax definition files."));
- updateStatus = new QLabel;
+ auto updateStatus = new QLabel;
updateStatus->setObjectName("updateStatus");
- definitionFilesPath = new PathChooser;
- definitionFilesPath->setExpectedKind(PathChooser::ExistingDirectory);
- definitionFilesPath->setHistoryCompleter("TextEditor.Highlighter.History");
+ m_definitionFilesPath = new PathChooser;
+ m_definitionFilesPath->setFilePath(d->m_settings.definitionFilesPath());
+ m_definitionFilesPath->setExpectedKind(PathChooser::ExistingDirectory);
+ m_definitionFilesPath->setHistoryCompleter("TextEditor.Highlighter.History");
- reloadDefinitions = new QPushButton(Tr::tr("Reload Definitions"));
+ auto reloadDefinitions = new QPushButton(Tr::tr("Reload Definitions"));
reloadDefinitions->setToolTip(Tr::tr("Reload externally modified definition files."));
- resetCache = new QPushButton(Tr::tr("Reset Remembered Definitions"));
+ auto resetCache = new QPushButton(Tr::tr("Reset Remembered Definitions"));
resetCache->setToolTip(Tr::tr("Reset definitions remembered for files that can be "
"associated with more than one highlighter definition."));
- ignoreEdit = new QLineEdit;
+ m_ignoreEdit = new QLineEdit(d->m_settings.ignoredFilesPatterns());
using namespace Layouting;
Column {
@@ -79,11 +105,11 @@ public:
Column {
Row { downloadDefinitions, updateStatus, st },
Row { Tr::tr("User Highlight Definition Files"),
- definitionFilesPath, reloadDefinitions },
+ m_definitionFilesPath, reloadDefinitions },
Row { st, resetCache }
}
},
- Row { Tr::tr("Ignored file patterns:"), ignoreEdit },
+ Row { Tr::tr("Ignored file patterns:"), m_ignoreEdit },
st
}.attachTo(this);
@@ -102,53 +128,25 @@ public:
Highlighter::clearDefinitionForDocumentCache();
});
}
-};
-
-} // Internal
-
-using namespace Internal;
-
-class HighlighterSettingsPagePrivate
-{
-public:
- HighlighterSettingsPagePrivate() = default;
-
- void ensureInitialized();
- void migrateGenericHighlighterFiles();
-
- void settingsFromUI();
- void settingsToUI();
- bool settingsChanged();
-
- bool m_initialized = false;
- const QString m_settingsPrefix{"Text"};
- HighlighterSettings m_settings;
+ void apply() final
+ {
+ bool changed = d->m_settings.definitionFilesPath() != m_definitionFilesPath->filePath()
+ || d->m_settings.ignoredFilesPatterns() != m_ignoreEdit->text();
+
+ if (changed) {
+ d->m_settings.setDefinitionFilesPath(m_definitionFilesPath->filePath());
+ d->m_settings.setIgnoredFilesPatterns(m_ignoreEdit->text());
+ d->m_settings.toSettings(d->m_settingsPrefix, Core::ICore::settings());
+ }
+ }
- QPointer<HighlighterSettingsPageWidget> m_widget;
+ PathChooser *m_definitionFilesPath;
+ QLineEdit *m_ignoreEdit;
+ HighlighterSettingsPagePrivate *d;
};
-void HighlighterSettingsPagePrivate::migrateGenericHighlighterFiles()
-{
- QDir userDefinitionPath(m_settings.definitionFilesPath().toString());
- if (userDefinitionPath.mkdir("syntax")) {
- const auto link = Utils::HostOsInfo::isAnyUnixHost()
- ? static_cast<bool(*)(const QString &, const QString &)>(&QFile::link)
- : static_cast<bool(*)(const QString &, const QString &)>(&QFile::copy);
-
- for (const QFileInfo &file : userDefinitionPath.entryInfoList({"*.xml"}, QDir::Files))
- link(file.filePath(), file.absolutePath() + "/syntax/" + file.fileName());
- }
-}
-
-void HighlighterSettingsPagePrivate::ensureInitialized()
-{
- if (m_initialized)
- return;
- m_initialized = true;
- m_settings.fromSettings(m_settingsPrefix, Core::ICore::settings());
- migrateGenericHighlighterFiles();
-}
+// HighlighterSettingsPage
HighlighterSettingsPage::HighlighterSettingsPage()
: d(new HighlighterSettingsPagePrivate)
@@ -158,6 +156,7 @@ HighlighterSettingsPage::HighlighterSettingsPage()
setCategory(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY);
setDisplayCategory(Tr::tr("Text Editor"));
setCategoryIconPath(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY_ICON_PATH);
+ setWidgetCreator([this] { return new HighlighterSettingsPageWidget(d); });
}
HighlighterSettingsPage::~HighlighterSettingsPage()
@@ -165,55 +164,10 @@ HighlighterSettingsPage::~HighlighterSettingsPage()
delete d;
}
-QWidget *HighlighterSettingsPage::widget()
-{
- if (!d->m_widget) {
- d->m_widget = new HighlighterSettingsPageWidget;
- d->settingsToUI();
- }
- return d->m_widget;
-}
-
-void HighlighterSettingsPage::apply()
-{
- if (!d->m_widget) // page was not shown
- return;
- if (d->settingsChanged())
- d->settingsFromUI();
-}
-
-void HighlighterSettingsPage::finish()
-{
- delete d->m_widget;
- d->m_widget = nullptr;
-}
-
const HighlighterSettings &HighlighterSettingsPage::highlighterSettings() const
{
d->ensureInitialized();
return d->m_settings;
}
-void HighlighterSettingsPagePrivate::settingsFromUI()
-{
- ensureInitialized();
- m_settings.setDefinitionFilesPath(m_widget->definitionFilesPath->filePath());
- m_settings.setIgnoredFilesPatterns(m_widget->ignoreEdit->text());
- m_settings.toSettings(m_settingsPrefix, Core::ICore::settings());
-}
-
-void HighlighterSettingsPagePrivate::settingsToUI()
-{
- ensureInitialized();
- m_widget->definitionFilesPath->setFilePath(m_settings.definitionFilesPath());
- m_widget->ignoreEdit->setText(m_settings.ignoredFilesPatterns());
-}
-
-bool HighlighterSettingsPagePrivate::settingsChanged()
-{
- ensureInitialized();
- return m_settings.definitionFilesPath() != m_widget->definitionFilesPath->filePath()
- || m_settings.ignoredFilesPatterns() != m_widget->ignoreEdit->text();
-}
-
} // TextEditor
diff --git a/src/plugins/texteditor/highlightersettingspage.h b/src/plugins/texteditor/highlightersettingspage.h
index c56e6d2238..94407c9020 100644
--- a/src/plugins/texteditor/highlightersettingspage.h
+++ b/src/plugins/texteditor/highlightersettingspage.h
@@ -15,10 +15,6 @@ public:
HighlighterSettingsPage();
~HighlighterSettingsPage() override;
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
const HighlighterSettings &highlighterSettings() const;
private:
diff --git a/src/plugins/texteditor/icodestylepreferences.cpp b/src/plugins/texteditor/icodestylepreferences.cpp
index 931714b71c..29a524ebf6 100644
--- a/src/plugins/texteditor/icodestylepreferences.cpp
+++ b/src/plugins/texteditor/icodestylepreferences.cpp
@@ -25,6 +25,7 @@ public:
QString m_displayName;
bool m_readOnly = false;
bool m_temporarilyReadOnly = false;
+ bool m_isAdditionalTabDisabled = false;
QString m_settingsSuffix;
};
@@ -82,6 +83,16 @@ bool ICodeStylePreferences::isTemporarilyReadOnly() const
return d->m_temporarilyReadOnly;
}
+bool ICodeStylePreferences::isAdditionalTabDisabled() const
+{
+ return d->m_isAdditionalTabDisabled;
+}
+
+void ICodeStylePreferences::setIsAdditionalTabDisabled(bool on)
+{
+ d->m_isAdditionalTabDisabled = on;
+}
+
void ICodeStylePreferences::setTabSettings(const TabSettings &settings)
{
if (d->m_tabSettings == settings)
diff --git a/src/plugins/texteditor/icodestylepreferences.h b/src/plugins/texteditor/icodestylepreferences.h
index 1c36390344..7fd616d560 100644
--- a/src/plugins/texteditor/icodestylepreferences.h
+++ b/src/plugins/texteditor/icodestylepreferences.h
@@ -40,6 +40,9 @@ public:
bool isTemporarilyReadOnly() const;
void setTemporarilyReadOnly(bool on);
+ bool isAdditionalTabDisabled() const;
+ void setIsAdditionalTabDisabled(bool on);
+
void setTabSettings(const TabSettings &settings);
TabSettings tabSettings() const;
TabSettings currentTabSettings() const;
diff --git a/src/plugins/texteditor/linenumberfilter.cpp b/src/plugins/texteditor/linenumberfilter.cpp
index 78a65c84d2..b718e5b433 100644
--- a/src/plugins/texteditor/linenumberfilter.cpp
+++ b/src/plugins/texteditor/linenumberfilter.cpp
@@ -5,24 +5,14 @@
#include "texteditortr.h"
-#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
-#include <coreplugin/icore.h>
-#include <coreplugin/modemanager.h>
-
-#include <QMetaType>
-#include <QPair>
-#include <QVariant>
-
-using LineColumn = QPair<int, int>;
-Q_DECLARE_METATYPE(LineColumn)
using namespace Core;
+using namespace Utils;
namespace TextEditor::Internal {
-LineNumberFilter::LineNumberFilter(QObject *parent)
- : ILocatorFilter(parent)
+LineNumberFilter::LineNumberFilter()
{
setId("Line in current document");
setDisplayName(Tr::tr("Line in Current Document"));
@@ -33,57 +23,47 @@ LineNumberFilter::LineNumberFilter(QObject *parent)
setDefaultIncludedByDefault(true);
}
-void LineNumberFilter::prepareSearch(const QString &entry)
+LocatorMatcherTasks LineNumberFilter::matchers()
{
- Q_UNUSED(entry)
- m_hasCurrentEditor = EditorManager::currentEditor() != nullptr;
-}
+ using namespace Tasking;
-QList<LocatorFilterEntry> LineNumberFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &, const QString &entry)
-{
- QList<LocatorFilterEntry> value;
- const QStringList lineAndColumn = entry.split(':');
- int sectionCount = lineAndColumn.size();
- int line = 0;
- int column = 0;
- bool ok = false;
- if (sectionCount > 0)
- line = lineAndColumn.at(0).toInt(&ok);
- if (ok && sectionCount > 1)
- column = lineAndColumn.at(1).toInt(&ok);
- if (!ok)
- return value;
- if (m_hasCurrentEditor && (line > 0 || column > 0)) {
- LineColumn data;
- data.first = line;
- data.second = column - 1; // column API is 0-based
- QString text;
- if (line > 0 && column > 0)
- text = Tr::tr("Line %1, Column %2").arg(line).arg(column);
- else if (line > 0)
- text = Tr::tr("Line %1").arg(line);
- else
- text = Tr::tr("Column %1").arg(column);
- value.append(LocatorFilterEntry(this, text, QVariant::fromValue(data)));
- }
- return value;
-}
+ TreeStorage<LocatorStorage> storage;
-void LineNumberFilter::accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const
-{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- IEditor *editor = EditorManager::currentEditor();
- if (editor) {
- EditorManager::addCurrentPositionToNavigationHistory();
- LineColumn data = selection.internalData.value<LineColumn>();
- if (data.first < 1) // jump to column in same line
- data.first = editor->currentLine();
- editor->gotoLine(data.first, data.second);
- EditorManager::activateEditor(editor);
- }
+ const auto onSetup = [storage] {
+ const QStringList lineAndColumn = storage->input().split(':');
+ int sectionCount = lineAndColumn.size();
+ int line = 0;
+ int column = 0;
+ bool ok = false;
+ if (sectionCount > 0)
+ line = lineAndColumn.at(0).toInt(&ok);
+ if (ok && sectionCount > 1)
+ column = lineAndColumn.at(1).toInt(&ok);
+ if (!ok)
+ return;
+ if (EditorManager::currentEditor() && (line > 0 || column > 0)) {
+ QString text;
+ if (line > 0 && column > 0)
+ text = Tr::tr("Line %1, Column %2").arg(line).arg(column);
+ else if (line > 0)
+ text = Tr::tr("Line %1").arg(line);
+ else
+ text = Tr::tr("Column %1").arg(column);
+ LocatorFilterEntry entry;
+ entry.displayName = text;
+ entry.acceptor = [line, targetColumn = column - 1] {
+ IEditor *editor = EditorManager::currentEditor();
+ if (!editor)
+ return AcceptResult();
+ EditorManager::addCurrentPositionToNavigationHistory();
+ editor->gotoLine(line < 1 ? editor->currentLine() : line, targetColumn);
+ EditorManager::activateEditor(editor);
+ return AcceptResult();
+ };
+ storage->reportOutput({entry});
+ }
+ };
+ return {{Sync(onSetup), storage}};
}
-} // TextEditor::Internal
+} // namespace TextEditor::Internal
diff --git a/src/plugins/texteditor/linenumberfilter.h b/src/plugins/texteditor/linenumberfilter.h
index 03b52f270b..ec72c1bb02 100644
--- a/src/plugins/texteditor/linenumberfilter.h
+++ b/src/plugins/texteditor/linenumberfilter.h
@@ -5,31 +5,15 @@
#include <coreplugin/locator/ilocatorfilter.h>
-#include <QString>
-#include <QList>
-#include <QFutureInterface>
-
-namespace Core { class IEditor; }
-
-namespace TextEditor {
-namespace Internal {
+namespace TextEditor::Internal {
class LineNumberFilter : public Core::ILocatorFilter
{
- Q_OBJECT
-
public:
- explicit LineNumberFilter(QObject *parent = nullptr);
-
- void prepareSearch(const QString &entry) override;
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
+ LineNumberFilter();
private:
- bool m_hasCurrentEditor = false;
+ Core::LocatorMatcherTasks matchers() final;
};
-} // namespace Internal
-} // namespace TextEditor
+} // namespace TextEditor::Internal
diff --git a/src/plugins/texteditor/markdowneditor.cpp b/src/plugins/texteditor/markdowneditor.cpp
new file mode 100644
index 0000000000..9c89636df8
--- /dev/null
+++ b/src/plugins/texteditor/markdowneditor.cpp
@@ -0,0 +1,330 @@
+// Copyright (C) 2023 Tasuku Suzuki
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "markdowneditor.h"
+
+#include "textdocument.h"
+#include "texteditor.h"
+#include "texteditortr.h"
+
+#include <aggregation/aggregate.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/coreplugintr.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/minisplitter.h>
+#include <utils/stringutils.h>
+
+#include <QHBoxLayout>
+#include <QScrollBar>
+#include <QTextBrowser>
+#include <QTimer>
+#include <QToolButton>
+
+#include <optional>
+
+namespace TextEditor::Internal {
+
+const char MARKDOWNVIEWER_ID[] = "Editors.MarkdownViewer";
+const char MARKDOWNVIEWER_TEXT_CONTEXT[] = "Editors.MarkdownViewer.Text";
+const char MARKDOWNVIEWER_MIME_TYPE[] = "text/markdown";
+const char MARKDOWNVIEWER_TEXTEDITOR_RIGHT[] = "Markdown.TextEditorRight";
+const char MARKDOWNVIEWER_SHOW_EDITOR[] = "Markdown.ShowEditor";
+const char MARKDOWNVIEWER_SHOW_PREVIEW[] = "Markdown.ShowPreview";
+const bool kTextEditorRightDefault = true;
+const bool kShowEditorDefault = true;
+const bool kShowPreviewDefault = true;
+
+class MarkdownEditor : public Core::IEditor
+{
+public:
+ MarkdownEditor()
+ : m_document(new TextDocument(MARKDOWNVIEWER_ID))
+ {
+ m_document->setMimeType(MARKDOWNVIEWER_MIME_TYPE);
+
+ QSettings *s = Core::ICore::settings();
+ const bool textEditorRight
+ = s->value(MARKDOWNVIEWER_TEXTEDITOR_RIGHT, kTextEditorRightDefault).toBool();
+ const bool showPreview = s->value(MARKDOWNVIEWER_SHOW_PREVIEW, kShowPreviewDefault).toBool();
+ const bool showEditor = s->value(MARKDOWNVIEWER_SHOW_EDITOR, kShowEditorDefault).toBool()
+ || !showPreview; // ensure at least one is visible
+
+ m_splitter = new Core::MiniSplitter;
+
+ // preview
+ m_previewWidget = new QTextBrowser();
+ m_previewWidget->setOpenExternalLinks(true);
+ m_previewWidget->setFrameShape(QFrame::NoFrame);
+ new Utils::MarkdownHighlighter(m_previewWidget->document());
+
+ // editor
+ m_textEditorWidget = new TextEditorWidget;
+ m_textEditorWidget->setTextDocument(m_document);
+ m_textEditorWidget->setupGenericHighlighter();
+ m_textEditorWidget->setMarksVisible(false);
+ auto context = new Core::IContext(this);
+ context->setWidget(m_textEditorWidget);
+ context->setContext(Core::Context(MARKDOWNVIEWER_TEXT_CONTEXT));
+ Core::ICore::addContextObject(context);
+
+ m_splitter->addWidget(m_previewWidget);
+ m_splitter->addWidget(m_textEditorWidget);
+
+ setContext(Core::Context(MARKDOWNVIEWER_ID));
+
+ auto widget = new QWidget;
+ auto layout = new QVBoxLayout;
+ layout->setContentsMargins(0, 0, 0, 0);
+ widget->setLayout(layout);
+ layout->addWidget(m_splitter);
+ setWidget(widget);
+ m_widget->installEventFilter(this);
+ using namespace Aggregation;
+ Aggregate *agg = Aggregate::parentAggregate(m_textEditorWidget);
+ if (!agg) {
+ agg = new Aggregate;
+ agg->add(m_textEditorWidget);
+ }
+ agg->add(m_widget.get());
+
+ m_togglePreviewVisible = new QToolButton;
+ m_togglePreviewVisible->setText(Tr::tr("Show Preview"));
+ m_togglePreviewVisible->setCheckable(true);
+ m_togglePreviewVisible->setChecked(showPreview);
+ m_previewWidget->setVisible(showPreview);
+
+ m_toggleEditorVisible = new QToolButton;
+ m_toggleEditorVisible->setText(Tr::tr("Show Editor"));
+ m_toggleEditorVisible->setCheckable(true);
+ m_toggleEditorVisible->setChecked(showEditor);
+ m_textEditorWidget->setVisible(showEditor);
+
+ auto swapViews = new QToolButton;
+ swapViews->setText(Tr::tr("Swap Views"));
+ swapViews->setEnabled(showEditor && showPreview);
+
+ m_toolbarLayout = new QHBoxLayout(&m_toolbar);
+ m_toolbarLayout->setSpacing(0);
+ m_toolbarLayout->setContentsMargins(0, 0, 0, 0);
+ m_toolbarLayout->addStretch();
+ m_toolbarLayout->addWidget(m_togglePreviewVisible);
+ m_toolbarLayout->addWidget(m_toggleEditorVisible);
+ m_toolbarLayout->addWidget(swapViews);
+
+ setWidgetOrder(textEditorRight);
+
+ connect(m_document.data(),
+ &TextDocument::mimeTypeChanged,
+ m_document.data(),
+ &TextDocument::changed);
+
+ const auto updatePreview = [this] {
+ // save scroll positions
+ const QPoint positions = m_previewRestoreScrollPosition
+ ? *m_previewRestoreScrollPosition
+ : QPoint(m_previewWidget->horizontalScrollBar()->value(),
+ m_previewWidget->verticalScrollBar()->value());
+ m_previewRestoreScrollPosition.reset();
+
+ m_previewWidget->setMarkdown(m_document->plainText());
+
+ m_previewWidget->horizontalScrollBar()->setValue(positions.x());
+ m_previewWidget->verticalScrollBar()->setValue(positions.y());
+ };
+
+ const auto viewToggled =
+ [swapViews](QWidget *view, bool visible, QWidget *otherView, QToolButton *otherButton) {
+ if (view->isVisible() == visible)
+ return;
+ view->setVisible(visible);
+ if (visible) {
+ view->setFocus();
+ } else if (otherView->isVisible()) {
+ otherView->setFocus();
+ } else {
+ // make sure at least one view is visible
+ otherButton->toggle();
+ }
+ swapViews->setEnabled(view->isVisible() && otherView->isVisible());
+ };
+ const auto saveViewSettings = [this] {
+ Utils::QtcSettings *s = Core::ICore::settings();
+ s->setValueWithDefault(MARKDOWNVIEWER_SHOW_PREVIEW,
+ m_togglePreviewVisible->isChecked(),
+ kShowPreviewDefault);
+ s->setValueWithDefault(MARKDOWNVIEWER_SHOW_EDITOR,
+ m_toggleEditorVisible->isChecked(),
+ kShowEditorDefault);
+ };
+
+ connect(m_toggleEditorVisible,
+ &QToolButton::toggled,
+ this,
+ [this, viewToggled, saveViewSettings](bool visible) {
+ viewToggled(m_textEditorWidget,
+ visible,
+ m_previewWidget,
+ m_togglePreviewVisible);
+ saveViewSettings();
+ });
+ connect(m_togglePreviewVisible,
+ &QToolButton::toggled,
+ this,
+ [this, viewToggled, updatePreview, saveViewSettings](bool visible) {
+ viewToggled(m_previewWidget, visible, m_textEditorWidget, m_toggleEditorVisible);
+ if (visible && m_performDelayedUpdate) {
+ m_performDelayedUpdate = false;
+ updatePreview();
+ }
+ saveViewSettings();
+ });
+
+ connect(swapViews, &QToolButton::clicked, m_textEditorWidget, [this] {
+ const bool textEditorRight = isTextEditorRight();
+ setWidgetOrder(!textEditorRight);
+ // save settings
+ Utils::QtcSettings *s = Core::ICore::settings();
+ s->setValueWithDefault(MARKDOWNVIEWER_TEXTEDITOR_RIGHT,
+ !textEditorRight,
+ kTextEditorRightDefault);
+ });
+
+ // TODO directly update when we build with Qt 6.5.2
+ m_previewTimer.setInterval(500);
+ m_previewTimer.setSingleShot(true);
+ connect(&m_previewTimer, &QTimer::timeout, this, [this, updatePreview] {
+ if (m_togglePreviewVisible->isChecked())
+ updatePreview();
+ else
+ m_performDelayedUpdate = true;
+ });
+
+ connect(m_document->document(), &QTextDocument::contentsChanged, &m_previewTimer, [this] {
+ m_previewTimer.start();
+ });
+ }
+
+ bool isTextEditorRight() const { return m_splitter->widget(0) == m_previewWidget; }
+
+ void setWidgetOrder(bool textEditorRight)
+ {
+ QTC_ASSERT(m_splitter->count() > 1, return);
+ QWidget *left = textEditorRight ? static_cast<QWidget *>(m_previewWidget)
+ : m_textEditorWidget;
+ QWidget *right = textEditorRight ? static_cast<QWidget *>(m_textEditorWidget)
+ : m_previewWidget;
+ m_splitter->insertWidget(0, left);
+ m_splitter->insertWidget(1, right);
+ // buttons
+ QWidget *leftButton = textEditorRight ? m_togglePreviewVisible : m_toggleEditorVisible;
+ QWidget *rightButton = textEditorRight ? m_toggleEditorVisible : m_togglePreviewVisible;
+ const int rightIndex = m_toolbarLayout->count() - 2;
+ m_toolbarLayout->insertWidget(rightIndex, leftButton);
+ m_toolbarLayout->insertWidget(rightIndex, rightButton);
+ }
+
+ QWidget *toolBar() override { return &m_toolbar; }
+
+ Core::IDocument *document() const override { return m_document.data(); }
+ TextEditorWidget *textEditorWidget() const { return m_textEditorWidget; }
+ int currentLine() const override { return textEditorWidget()->textCursor().blockNumber() + 1; };
+ int currentColumn() const override
+ {
+ QTextCursor cursor = textEditorWidget()->textCursor();
+ return cursor.position() - cursor.block().position() + 1;
+ }
+ void gotoLine(int line, int column, bool centerLine) override
+ {
+ if (!m_toggleEditorVisible->isChecked())
+ m_toggleEditorVisible->toggle();
+ textEditorWidget()->gotoLine(line, column, centerLine);
+ }
+
+ bool eventFilter(QObject *obj, QEvent *ev) override
+ {
+ if (obj == m_widget && ev->type() == QEvent::FocusIn) {
+ if (m_splitter->focusWidget())
+ m_splitter->focusWidget()->setFocus();
+ else if (m_textEditorWidget->isVisible())
+ m_textEditorWidget->setFocus();
+ else
+ m_splitter->widget(0)->setFocus();
+ return true;
+ }
+ return Core::IEditor::eventFilter(obj, ev);
+ }
+
+ QByteArray saveState() const override
+ {
+ QByteArray state;
+ QDataStream stream(&state, QIODevice::WriteOnly);
+ stream << 1; // version number
+ stream << m_textEditorWidget->saveState();
+ stream << m_previewWidget->horizontalScrollBar()->value();
+ stream << m_previewWidget->verticalScrollBar()->value();
+ stream << isTextEditorRight();
+ stream << m_togglePreviewVisible->isChecked();
+ stream << m_toggleEditorVisible->isChecked();
+ stream << m_splitter->saveState();
+ return state;
+ }
+
+ void restoreState(const QByteArray &state) override
+ {
+ if (state.isEmpty())
+ return;
+ int version;
+ QByteArray editorState;
+ int previewHV;
+ int previewVV;
+ bool textEditorRight;
+ bool previewShown;
+ bool textEditorShown;
+ QByteArray splitterState;
+ QDataStream stream(state);
+ stream >> version;
+ stream >> editorState;
+ stream >> previewHV;
+ stream >> previewVV;
+ stream >> textEditorRight;
+ stream >> previewShown;
+ stream >> textEditorShown;
+ stream >> splitterState;
+ m_textEditorWidget->restoreState(editorState);
+ m_previewRestoreScrollPosition.emplace(previewHV, previewVV);
+ setWidgetOrder(textEditorRight);
+ m_splitter->restoreState(splitterState);
+ m_togglePreviewVisible->setChecked(previewShown);
+ // ensure at least one is shown
+ m_toggleEditorVisible->setChecked(textEditorShown || !previewShown);
+ }
+
+private:
+ QTimer m_previewTimer;
+ bool m_performDelayedUpdate = false;
+ Core::MiniSplitter *m_splitter;
+ QTextBrowser *m_previewWidget;
+ TextEditorWidget *m_textEditorWidget;
+ TextDocumentPtr m_document;
+ QWidget m_toolbar;
+ QHBoxLayout *m_toolbarLayout;
+ QToolButton *m_toggleEditorVisible;
+ QToolButton *m_togglePreviewVisible;
+ std::optional<QPoint> m_previewRestoreScrollPosition;
+};
+
+MarkdownEditorFactory::MarkdownEditorFactory()
+ : m_actionHandler(MARKDOWNVIEWER_ID,
+ MARKDOWNVIEWER_TEXT_CONTEXT,
+ TextEditor::TextEditorActionHandler::None,
+ [](Core::IEditor *editor) {
+ return static_cast<MarkdownEditor *>(editor)->textEditorWidget();
+ })
+{
+ setId(MARKDOWNVIEWER_ID);
+ setDisplayName(::Core::Tr::tr("Markdown Viewer"));
+ addMimeType(MARKDOWNVIEWER_MIME_TYPE);
+ setEditorCreator([] { return new MarkdownEditor; });
+}
+
+} // namespace TextEditor::Internal
diff --git a/src/plugins/texteditor/markdowneditor.h b/src/plugins/texteditor/markdowneditor.h
new file mode 100644
index 0000000000..de87c558e1
--- /dev/null
+++ b/src/plugins/texteditor/markdowneditor.h
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 Tasuku Suzuki
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <coreplugin/editormanager/ieditorfactory.h>
+
+#include <texteditor/texteditoractionhandler.h>
+
+namespace TextEditor::Internal {
+
+class MarkdownEditorFactory final : public Core::IEditorFactory
+{
+public:
+ MarkdownEditorFactory();
+
+private:
+ TextEditor::TextEditorActionHandler m_actionHandler;
+};
+
+} // TextEditor::Internal
diff --git a/src/plugins/texteditor/outlinefactory.cpp b/src/plugins/texteditor/outlinefactory.cpp
index 76222642c1..bc9aa03c8b 100644
--- a/src/plugins/texteditor/outlinefactory.cpp
+++ b/src/plugins/texteditor/outlinefactory.cpp
@@ -12,6 +12,7 @@
#include <utils/utilsicons.h>
#include <utils/qtcassert.h>
+#include <utils/stylehelper.h>
#include <QDebug>
#include <QLabel>
@@ -63,6 +64,7 @@ OutlineWidgetStack::OutlineWidgetStack(OutlineFactory *factory) :
this, &OutlineWidgetStack::toggleCursorSynchronization);
m_filterButton = new QToolButton(this);
+ Utils::StyleHelper::setPanelWidget(m_filterButton);
// The ToolButton needs a parent because updateFilterMenu() sets
// it visible. That would open a top-level window if the button
// did not have a parent in that moment.
@@ -70,11 +72,12 @@ OutlineWidgetStack::OutlineWidgetStack(OutlineFactory *factory) :
m_filterButton->setIcon(Utils::Icons::FILTER.icon());
m_filterButton->setToolTip(Tr::tr("Filter tree"));
m_filterButton->setPopupMode(QToolButton::InstantPopup);
- m_filterButton->setProperty("noArrow", true);
+ m_filterButton->setProperty(Utils::StyleHelper::C_NO_ARROW, true);
m_filterMenu = new QMenu(m_filterButton);
m_filterButton->setMenu(m_filterMenu);
m_toggleSort = new QToolButton(this);
+ Utils::StyleHelper::setPanelWidget(m_toggleSort);
m_toggleSort->setIcon(Utils::Icons::SORT_ALPHABETICALLY_TOOLBAR.icon());
m_toggleSort->setCheckable(true);
m_toggleSort->setChecked(false);
diff --git a/src/plugins/texteditor/quickfix.h b/src/plugins/texteditor/quickfix.h
index 6469bd7d8b..e456230ac2 100644
--- a/src/plugins/texteditor/quickfix.h
+++ b/src/plugins/texteditor/quickfix.h
@@ -35,8 +35,7 @@ public:
virtual ~QuickFixOperation();
/*!
- \returns The priority for this quick-fix. See the QuickFixCollector for more
- information.
+ Returns The priority for this quick-fix. See the QuickFixCollector for more information.
*/
virtual int priority() const;
@@ -44,8 +43,7 @@ public:
void setPriority(int priority);
/*!
- \returns The description for this quick-fix. This description is shown to the
- user.
+ Returns The description for this quick-fix. This description is shown to the user.
*/
virtual QString description() const;
diff --git a/src/plugins/texteditor/semantichighlighter.cpp b/src/plugins/texteditor/semantichighlighter.cpp
index 68b1792ca8..30cb9cf729 100644
--- a/src/plugins/texteditor/semantichighlighter.cpp
+++ b/src/plugins/texteditor/semantichighlighter.cpp
@@ -82,26 +82,28 @@ void SemanticHighlighter::incrementalApplyExtraAdditionalFormats(SyntaxHighlight
if (to <= from)
return;
- const int firstResultBlockNumber = int(future.resultAt(from).line) - 1;
-
- // blocks between currentBlockNumber and the last block with results will
- // be cleaned of additional extra formats if they have no results
- int currentBlockNumber = 0;
+ const int resultStartLine = future.resultAt(from).line;
+ int formattingStartLine = 1;
+
+ // Find the line on which to start formatting, where "formatting" means to either
+ // clear out formats from outdated document versions (if there is no current result
+ // on that line), or apply the format corresponding to the respective result.
+ // Note that if there are earlier results on the same line, we have to make sure they
+ // get re-applied by adapting the from variable accordingly.
for (int i = from - 1; i >= 0; --i) {
const HighlightingResult &result = future.resultAt(i);
- const int blockNumber = int(result.line) - 1;
- if (blockNumber < firstResultBlockNumber) {
- // stop! found where last format stopped
- currentBlockNumber = blockNumber + 1;
- // add previous results for the same line to avoid undoing their formats
+ if (result.line == resultStartLine) {
+ from = i;
+ } else if (result.line < resultStartLine) {
+ formattingStartLine = result.line + 1;
from = i + 1;
break;
}
}
QTextDocument *doc = highlighter->document();
- QTC_ASSERT(currentBlockNumber < doc->blockCount(), return);
- QTextBlock currentBlock = doc->findBlockByNumber(currentBlockNumber);
+ QTC_ASSERT(formattingStartLine <= doc->blockCount(), return);
+ QTextBlock currentBlock = doc->findBlockByNumber(formattingStartLine - 1);
std::map<QTextBlock, QVector<QTextLayout::FormatRange>> formatRanges;
for (int i = from; i < to; ++i) {
diff --git a/src/plugins/texteditor/snippets/snippet.cpp b/src/plugins/texteditor/snippets/snippet.cpp
index f41156c4ce..74a4dc543c 100644
--- a/src/plugins/texteditor/snippets/snippet.cpp
+++ b/src/plugins/texteditor/snippets/snippet.cpp
@@ -330,7 +330,7 @@ void Internal::TextEditorPlugin::testSnippetParsing_data()
<< QString::fromLatin1("\\\\$test\\\\\\\\$\\\\") << false << Parts();
QTest::newRow("Q_PROPERTY") << QString(
- "Q_PROPERTY($type$ $name$ READ $name$ WRITE set$name:c$ NOTIFY $name$Changed)")
+ "Q_PROPERTY($type$ $name$ READ $name$ WRITE set$name:c$ NOTIFY $name$Changed FINAL)")
<< true
<< Parts{SnippetPart("Q_PROPERTY("),
SnippetPart("type", 0),
@@ -342,7 +342,7 @@ void Internal::TextEditorPlugin::testSnippetParsing_data()
SnippetPart("name", 1, TCMANGLER_ID),
SnippetPart(" NOTIFY "),
SnippetPart("name", 1),
- SnippetPart("Changed)")};
+ SnippetPart("Changed FINAL)")};
QTest::newRow("open identifier") << QString("$test") << false << Parts();
QTest::newRow("wrong mangler") << QString("$test:X$") << false << Parts();
diff --git a/src/plugins/texteditor/snippets/snippetscollection.cpp b/src/plugins/texteditor/snippets/snippetscollection.cpp
index be658c1477..f799b3fe42 100644
--- a/src/plugins/texteditor/snippets/snippetscollection.cpp
+++ b/src/plugins/texteditor/snippets/snippetscollection.cpp
@@ -78,9 +78,7 @@ SnippetsCollection::SnippetsCollection()
m_builtInSnippetsFiles(Core::ICore::resourcePath("snippets")
.dirEntries(FileFilter({"*.xml"})))
{
-
- connect(Core::ICore::instance(), &Core::ICore::coreOpened,
- this, &SnippetsCollection::identifyGroups);
+ identifyGroups();
}
SnippetsCollection::~SnippetsCollection() = default;
diff --git a/src/plugins/texteditor/snippets/snippetssettingspage.cpp b/src/plugins/texteditor/snippets/snippetssettingspage.cpp
index b64910a0f1..e4bfdf7df8 100644
--- a/src/plugins/texteditor/snippets/snippetssettingspage.cpp
+++ b/src/plugins/texteditor/snippets/snippetssettingspage.cpp
@@ -35,15 +35,14 @@
#include <QStackedWidget>
#include <QTextStream>
-namespace TextEditor {
-namespace Internal {
+namespace TextEditor::Internal {
// SnippetsTableModel
+
class SnippetsTableModel : public QAbstractTableModel
{
- Q_OBJECT
public:
- SnippetsTableModel(QObject *parent);
+ SnippetsTableModel();
~SnippetsTableModel() override = default;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
@@ -74,9 +73,8 @@ private:
QString m_activeGroupId;
};
-SnippetsTableModel::SnippetsTableModel(QObject *parent) :
- QAbstractTableModel(parent),
- m_collection(SnippetsCollection::instance())
+SnippetsTableModel::SnippetsTableModel()
+ : m_collection(SnippetsCollection::instance())
{}
int SnippetsTableModel::rowCount(const QModelIndex &) const
@@ -244,19 +242,15 @@ void SnippetsTableModel::replaceSnippet(const Snippet &snippet, const QModelInde
}
}
-// SnippetsSettingsPagePrivate
-class SnippetsSettingsPagePrivate : public QObject
+// SnippetsSettingsWidget
+
+class SnippetsSettingsWidget : public Core::IOptionsPageWidget
{
public:
- SnippetsSettingsPagePrivate();
- ~SnippetsSettingsPagePrivate() override { delete m_model; }
-
- void configureUi(QWidget *parent);
+ SnippetsSettingsWidget();
- void apply();
- void finish();
-
- QPointer<QWidget> m_widget;
+ void apply() final;
+ void finish() final;
private:
void loadSnippetGroup(int index);
@@ -279,9 +273,9 @@ private:
bool settingsChanged() const;
void writeSettings();
- const QString m_settingsPrefix;
- SnippetsTableModel *m_model;
- bool m_snippetsCollectionChanged;
+ const QString m_settingsPrefix{QLatin1String("Text")};
+ SnippetsTableModel m_model;
+ bool m_snippetsCollectionChanged = false;
SnippetsSettings m_settings;
QStackedWidget *m_snippetsEditorStack;
@@ -290,38 +284,22 @@ private:
QPushButton *m_revertButton;
};
-SnippetsSettingsPagePrivate::SnippetsSettingsPagePrivate() :
- m_settingsPrefix(QLatin1String("Text")),
- m_model(new SnippetsTableModel(nullptr)),
- m_snippetsCollectionChanged(false)
-{}
-
-SnippetEditorWidget *SnippetsSettingsPagePrivate::currentEditor() const
-{
- return editorAt(m_snippetsEditorStack->currentIndex());
-}
-
-SnippetEditorWidget *SnippetsSettingsPagePrivate::editorAt(int i) const
-{
- return static_cast<SnippetEditorWidget *>(m_snippetsEditorStack->widget(i));
-}
-
-void SnippetsSettingsPagePrivate::configureUi(QWidget *w)
+SnippetsSettingsWidget::SnippetsSettingsWidget()
{
m_groupCombo = new QComboBox;
m_snippetsEditorStack = new QStackedWidget;
for (const SnippetProvider &provider : SnippetProvider::snippetProviders()) {
m_groupCombo->addItem(provider.displayName(), provider.groupId());
- auto snippetEditor = new SnippetEditorWidget(w);
+ auto snippetEditor = new SnippetEditorWidget(this);
SnippetProvider::decorateEditor(snippetEditor, provider.groupId());
m_snippetsEditorStack->insertWidget(m_groupCombo->count() - 1, snippetEditor);
connect(snippetEditor, &SnippetEditorWidget::snippetContentChanged,
- this, &SnippetsSettingsPagePrivate::setSnippetContent);
+ this, &SnippetsSettingsWidget::setSnippetContent);
}
m_snippetsTable = new Utils::TreeView;
m_snippetsTable->setRootIsDecorated(false);
- m_snippetsTable->setModel(m_model);
+ m_snippetsTable->setModel(&m_model);
m_revertButton = new QPushButton(Tr::tr("Revert Built-in"));
m_revertButton->setEnabled(false);
@@ -332,7 +310,7 @@ void SnippetsSettingsPagePrivate::configureUi(QWidget *w)
snippetSplitter->addWidget(m_snippetsTable);
snippetSplitter->addWidget(m_snippetsEditorStack);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row { Tr::tr("Group:"), m_groupCombo, st },
Row {
@@ -350,40 +328,50 @@ void SnippetsSettingsPagePrivate::configureUi(QWidget *w)
st,
}
}
- }.attachTo(w);
+ }.attachTo(this);
loadSettings();
loadSnippetGroup(m_groupCombo->currentIndex());
- connect(m_model, &QAbstractItemModel::rowsInserted,
- this, &SnippetsSettingsPagePrivate::selectSnippet);
- connect(m_model, &QAbstractItemModel::rowsInserted,
- this, &SnippetsSettingsPagePrivate::markSnippetsCollection);
- connect(m_model, &QAbstractItemModel::rowsRemoved,
- this, &SnippetsSettingsPagePrivate::markSnippetsCollection);
- connect(m_model, &QAbstractItemModel::rowsMoved,
- this, &SnippetsSettingsPagePrivate::selectMovedSnippet);
- connect(m_model, &QAbstractItemModel::rowsMoved,
- this, &SnippetsSettingsPagePrivate::markSnippetsCollection);
- connect(m_model, &QAbstractItemModel::dataChanged,
- this, &SnippetsSettingsPagePrivate::markSnippetsCollection);
- connect(m_model, &QAbstractItemModel::modelReset,
+ connect(&m_model, &QAbstractItemModel::rowsInserted,
+ this, &SnippetsSettingsWidget::selectSnippet);
+ connect(&m_model, &QAbstractItemModel::rowsInserted,
+ this, &SnippetsSettingsWidget::markSnippetsCollection);
+ connect(&m_model, &QAbstractItemModel::rowsRemoved,
+ this, &SnippetsSettingsWidget::markSnippetsCollection);
+ connect(&m_model, &QAbstractItemModel::rowsMoved,
+ this, &SnippetsSettingsWidget::selectMovedSnippet);
+ connect(&m_model, &QAbstractItemModel::rowsMoved,
+ this, &SnippetsSettingsWidget::markSnippetsCollection);
+ connect(&m_model, &QAbstractItemModel::dataChanged,
+ this, &SnippetsSettingsWidget::markSnippetsCollection);
+ connect(&m_model, &QAbstractItemModel::modelReset,
this, [this] { this->updateCurrentSnippetDependent(); });
- connect(m_model, &QAbstractItemModel::modelReset,
- this, &SnippetsSettingsPagePrivate::markSnippetsCollection);
+ connect(&m_model, &QAbstractItemModel::modelReset,
+ this, &SnippetsSettingsWidget::markSnippetsCollection);
connect(m_groupCombo, &QComboBox::currentIndexChanged,
- this, &SnippetsSettingsPagePrivate::loadSnippetGroup);
+ this, &SnippetsSettingsWidget::loadSnippetGroup);
connect(m_revertButton, &QAbstractButton::clicked,
- this, &SnippetsSettingsPagePrivate::revertBuiltInSnippet);
+ this, &SnippetsSettingsWidget::revertBuiltInSnippet);
connect(m_snippetsTable->selectionModel(), &QItemSelectionModel::currentChanged,
- this, &SnippetsSettingsPagePrivate::updateCurrentSnippetDependent);
+ this, &SnippetsSettingsWidget::updateCurrentSnippetDependent);
connect(TextEditorSettings::instance(), &TextEditorSettings::fontSettingsChanged,
- this, &SnippetsSettingsPagePrivate::decorateEditors);
+ this, &SnippetsSettingsWidget::decorateEditors);
+}
+
+SnippetEditorWidget *SnippetsSettingsWidget::currentEditor() const
+{
+ return editorAt(m_snippetsEditorStack->currentIndex());
}
-void SnippetsSettingsPagePrivate::apply()
+SnippetEditorWidget *SnippetsSettingsWidget::editorAt(int i) const
+{
+ return static_cast<SnippetEditorWidget *>(m_snippetsEditorStack->widget(i));
+}
+
+void SnippetsSettingsWidget::apply()
{
if (settingsChanged())
writeSettings();
@@ -402,7 +390,7 @@ void SnippetsSettingsPagePrivate::apply()
}
}
-void SnippetsSettingsPagePrivate::finish()
+void SnippetsSettingsWidget::finish()
{
if (m_snippetsCollectionChanged) {
SnippetsCollection::instance()->reload();
@@ -412,7 +400,7 @@ void SnippetsSettingsPagePrivate::finish()
disconnect(TextEditorSettings::instance(), nullptr, this, nullptr);
}
-void SnippetsSettingsPagePrivate::loadSettings()
+void SnippetsSettingsWidget::loadSettings()
{
if (m_groupCombo->count() == 0)
return;
@@ -426,7 +414,7 @@ void SnippetsSettingsPagePrivate::loadSettings()
m_groupCombo->setCurrentIndex(0);
}
-void SnippetsSettingsPagePrivate::writeSettings()
+void SnippetsSettingsWidget::writeSettings()
{
if (m_groupCombo->count() == 0)
return;
@@ -435,72 +423,72 @@ void SnippetsSettingsPagePrivate::writeSettings()
m_settings.toSettings(m_settingsPrefix, Core::ICore::settings());
}
-bool SnippetsSettingsPagePrivate::settingsChanged() const
+bool SnippetsSettingsWidget::settingsChanged() const
{
if (m_settings.lastUsedSnippetGroup() != m_groupCombo->currentText())
return true;
return false;
}
-void SnippetsSettingsPagePrivate::loadSnippetGroup(int index)
+void SnippetsSettingsWidget::loadSnippetGroup(int index)
{
if (index == -1)
return;
m_snippetsEditorStack->setCurrentIndex(index);
currentEditor()->clear();
- m_model->load(m_groupCombo->itemData(index).toString());
+ m_model.load(m_groupCombo->itemData(index).toString());
}
-void SnippetsSettingsPagePrivate::markSnippetsCollection()
+void SnippetsSettingsWidget::markSnippetsCollection()
{
if (!m_snippetsCollectionChanged)
m_snippetsCollectionChanged = true;
}
-void SnippetsSettingsPagePrivate::addSnippet()
+void SnippetsSettingsWidget::addSnippet()
{
- const QModelIndex &modelIndex = m_model->createSnippet();
+ const QModelIndex &modelIndex = m_model.createSnippet();
selectSnippet(QModelIndex(), modelIndex.row());
m_snippetsTable->edit(modelIndex);
}
-void SnippetsSettingsPagePrivate::removeSnippet()
+void SnippetsSettingsWidget::removeSnippet()
{
const QModelIndex &modelIndex = m_snippetsTable->selectionModel()->currentIndex();
if (!modelIndex.isValid()) {
QMessageBox::critical(Core::ICore::dialogParent(), Tr::tr("Error"), Tr::tr("No snippet selected."));
return;
}
- m_model->removeSnippet(modelIndex);
+ m_model.removeSnippet(modelIndex);
}
-void SnippetsSettingsPagePrivate::restoreRemovedBuiltInSnippets()
+void SnippetsSettingsWidget::restoreRemovedBuiltInSnippets()
{
- m_model->restoreRemovedBuiltInSnippets();
+ m_model.restoreRemovedBuiltInSnippets();
}
-void SnippetsSettingsPagePrivate::revertBuiltInSnippet()
+void SnippetsSettingsWidget::revertBuiltInSnippet()
{
- m_model->revertBuitInSnippet(m_snippetsTable->selectionModel()->currentIndex());
+ m_model.revertBuitInSnippet(m_snippetsTable->selectionModel()->currentIndex());
}
-void SnippetsSettingsPagePrivate::resetAllSnippets()
+void SnippetsSettingsWidget::resetAllSnippets()
{
- m_model->resetSnippets();
+ m_model.resetSnippets();
}
-void SnippetsSettingsPagePrivate::selectSnippet(const QModelIndex &parent, int row)
+void SnippetsSettingsWidget::selectSnippet(const QModelIndex &parent, int row)
{
- QModelIndex topLeft = m_model->index(row, 0, parent);
- QModelIndex bottomRight = m_model->index(row, 1, parent);
+ QModelIndex topLeft = m_model.index(row, 0, parent);
+ QModelIndex bottomRight = m_model.index(row, 1, parent);
QItemSelection selection(topLeft, bottomRight);
m_snippetsTable->selectionModel()->select(selection, QItemSelectionModel::SelectCurrent);
m_snippetsTable->setCurrentIndex(topLeft);
m_snippetsTable->scrollTo(topLeft);
}
-void SnippetsSettingsPagePrivate::selectMovedSnippet(const QModelIndex &,
+void SnippetsSettingsWidget::selectMovedSnippet(const QModelIndex &,
int sourceRow,
int,
const QModelIndex &destinationParent,
@@ -508,17 +496,17 @@ void SnippetsSettingsPagePrivate::selectMovedSnippet(const QModelIndex &,
{
QModelIndex modelIndex;
if (sourceRow < destinationRow)
- modelIndex = m_model->index(destinationRow - 1, 0, destinationParent);
+ modelIndex = m_model.index(destinationRow - 1, 0, destinationParent);
else
- modelIndex = m_model->index(destinationRow, 0, destinationParent);
+ modelIndex = m_model.index(destinationRow, 0, destinationParent);
m_snippetsTable->scrollTo(modelIndex);
- currentEditor()->setPlainText(m_model->snippetAt(modelIndex).content());
+ currentEditor()->setPlainText(m_model.snippetAt(modelIndex).content());
}
-void SnippetsSettingsPagePrivate::updateCurrentSnippetDependent(const QModelIndex &modelIndex)
+void SnippetsSettingsWidget::updateCurrentSnippetDependent(const QModelIndex &modelIndex)
{
if (modelIndex.isValid()) {
- const Snippet &snippet = m_model->snippetAt(modelIndex);
+ const Snippet &snippet = m_model.snippetAt(modelIndex);
currentEditor()->setPlainText(snippet.content());
m_revertButton->setEnabled(snippet.isBuiltIn());
} else {
@@ -527,16 +515,16 @@ void SnippetsSettingsPagePrivate::updateCurrentSnippetDependent(const QModelInde
}
}
-void SnippetsSettingsPagePrivate::setSnippetContent()
+void SnippetsSettingsWidget::setSnippetContent()
{
const QModelIndex &modelIndex = m_snippetsTable->selectionModel()->currentIndex();
if (modelIndex.isValid()) {
- m_model->setSnippetContent(modelIndex, currentEditor()->toPlainText());
+ m_model.setSnippetContent(modelIndex, currentEditor()->toPlainText());
markSnippetsCollection();
}
}
-void SnippetsSettingsPagePrivate::decorateEditors(const TextEditor::FontSettings &fontSettings)
+void SnippetsSettingsWidget::decorateEditors(const TextEditor::FontSettings &fontSettings)
{
for (int i = 0; i < m_groupCombo->count(); ++i) {
SnippetEditorWidget *snippetEditor = editorAt(i);
@@ -550,41 +538,13 @@ void SnippetsSettingsPagePrivate::decorateEditors(const TextEditor::FontSettings
// SnippetsSettingsPage
SnippetsSettingsPage::SnippetsSettingsPage()
- : d(new SnippetsSettingsPagePrivate)
{
setId(Constants::TEXT_EDITOR_SNIPPETS_SETTINGS);
setDisplayName(Tr::tr("Snippets"));
setCategory(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY);
setDisplayCategory(Tr::tr("Text Editor"));
setCategoryIconPath(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY_ICON_PATH);
+ setWidgetCreator([] { return new SnippetsSettingsWidget; });
}
-SnippetsSettingsPage::~SnippetsSettingsPage()
-{
- delete d;
-}
-
-QWidget *SnippetsSettingsPage::widget()
-{
- if (!d->m_widget) {
- d->m_widget = new QWidget;
- d->configureUi(d->m_widget);
- }
- return d->m_widget;
-}
-
-void SnippetsSettingsPage::apply()
-{
- d->apply();
-}
-
-void SnippetsSettingsPage::finish()
-{
- d->finish();
- delete d->m_widget;
-}
-
-} // Internal
-} // TextEditor
-
-#include "snippetssettingspage.moc"
+} // TextEditor::Internal
diff --git a/src/plugins/texteditor/snippets/snippetssettingspage.h b/src/plugins/texteditor/snippets/snippetssettingspage.h
index f7c4b8c3eb..31216d3d30 100644
--- a/src/plugins/texteditor/snippets/snippetssettingspage.h
+++ b/src/plugins/texteditor/snippets/snippetssettingspage.h
@@ -5,24 +5,12 @@
#include <coreplugin/dialogs/ioptionspage.h>
-namespace TextEditor {
-namespace Internal {
-
-class SnippetsSettingsPagePrivate;
+namespace TextEditor::Internal {
class SnippetsSettingsPage final : public Core::IOptionsPage
{
public:
SnippetsSettingsPage();
- ~SnippetsSettingsPage() override;
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-private:
- SnippetsSettingsPagePrivate *d;
};
-} // Internal
-} // TextEditor
+} // TextEditor::Internal
diff --git a/src/plugins/texteditor/syntaxhighlighter.cpp b/src/plugins/texteditor/syntaxhighlighter.cpp
index de72c7b0db..6d2616193e 100644
--- a/src/plugins/texteditor/syntaxhighlighter.cpp
+++ b/src/plugins/texteditor/syntaxhighlighter.cpp
@@ -23,8 +23,12 @@ class SyntaxHighlighterPrivate
Q_DECLARE_PUBLIC(SyntaxHighlighter)
public:
SyntaxHighlighterPrivate()
+ : SyntaxHighlighterPrivate(TextEditorSettings::fontSettings())
+ { }
+
+ SyntaxHighlighterPrivate(const FontSettings &fontSettings)
{
- updateFormats(TextEditorSettings::fontSettings());
+ updateFormats(fontSettings);
}
QPointer<QTextDocument> doc;
@@ -76,6 +80,16 @@ void SyntaxHighlighter::delayedRehighlight()
rehighlight();
}
+#ifdef WITH_TESTS
+SyntaxHighlighter::SyntaxHighlighter(QTextDocument *parent, const FontSettings &fontsettings)
+ : QObject(parent), d_ptr(new SyntaxHighlighterPrivate(fontsettings))
+{
+ d_ptr->q_ptr = this;
+ if (parent)
+ setDocument(parent);
+}
+#endif
+
void SyntaxHighlighterPrivate::applyFormatChanges(int from, int charsRemoved, int charsAdded)
{
bool formatsChanged = false;
diff --git a/src/plugins/texteditor/syntaxhighlighter.h b/src/plugins/texteditor/syntaxhighlighter.h
index 7a1822e466..1b66e2ff56 100644
--- a/src/plugins/texteditor/syntaxhighlighter.h
+++ b/src/plugins/texteditor/syntaxhighlighter.h
@@ -91,6 +91,11 @@ private:
void delayedRehighlight();
QScopedPointer<SyntaxHighlighterPrivate> d_ptr;
+
+#ifdef WITH_TESTS
+ friend class tst_highlighter;
+ SyntaxHighlighter(QTextDocument *parent, const FontSettings &fontsettings);
+#endif
};
} // namespace TextEditor
diff --git a/src/plugins/texteditor/tabsettingswidget.cpp b/src/plugins/texteditor/tabsettingswidget.cpp
index f55d8d6f44..f3476ec5f4 100644
--- a/src/plugins/texteditor/tabsettingswidget.cpp
+++ b/src/plugins/texteditor/tabsettingswidget.cpp
@@ -50,7 +50,6 @@ QString continuationTooltip()
TabSettingsWidget::TabSettingsWidget(QWidget *parent) :
QGroupBox(parent)
{
- resize(254, 189);
setTitle(Tr::tr("Tabs And Indentation"));
m_codingStyleWarning = new QLabel(
@@ -87,7 +86,7 @@ TabSettingsWidget::TabSettingsWidget(QWidget *parent) :
tabSizeLabel->setBuddy(m_tabSize);
indentSizeLabel->setBuddy(m_indentSize);
- using namespace Utils::Layouting;
+ using namespace Layouting;
const auto indent = [](QWidget *inner) { return Row { Space(30), inner }; };
Column {
diff --git a/src/plugins/texteditor/textdocument.cpp b/src/plugins/texteditor/textdocument.cpp
index 61c3b319b3..6276d731d0 100644
--- a/src/plugins/texteditor/textdocument.cpp
+++ b/src/plugins/texteditor/textdocument.cpp
@@ -373,6 +373,16 @@ QAction *TextDocument::createDiffAgainstCurrentFileAction(
return diffAction;
}
+void TextDocument::insertSuggestion(std::unique_ptr<TextSuggestion> &&suggestion)
+{
+ QTextCursor cursor(&d->m_document);
+ cursor.setPosition(suggestion->position());
+ const QTextBlock block = cursor.block();
+ TextDocumentLayout::userData(block)->insertSuggestion(std::move(suggestion));
+ TextDocumentLayout::updateSuggestionFormats(block, fontSettings());
+ updateLayout();
+}
+
#ifdef WITH_TESTS
void TextDocument::setSilentReload()
{
@@ -419,6 +429,12 @@ IAssistProvider *TextDocument::quickFixAssistProvider() const
void TextDocument::applyFontSettings()
{
d->m_fontSettingsNeedsApply = false;
+ QTextBlock block = document()->firstBlock();
+ while (block.isValid()) {
+ TextDocumentLayout::updateSuggestionFormats(block, fontSettings());
+ block = block.next();
+ }
+ updateLayout();
if (d->m_highlighter) {
d->m_highlighter->setFontSettings(d->m_fontSettings);
d->m_highlighter->rehighlight();
@@ -820,15 +836,14 @@ bool TextDocument::reload(QString *errorString, const FilePath &realFilePath)
emit aboutToReload();
auto documentLayout =
qobject_cast<TextDocumentLayout*>(d->m_document.documentLayout());
- TextMarks marks;
if (documentLayout)
- marks = documentLayout->documentClosing(); // removes text marks non-permanently
+ documentLayout->documentAboutToReload(); // removes text marks non-permanently
bool success = openImpl(errorString, filePath(), realFilePath, /*reload =*/true)
== OpenResult::Success;
if (documentLayout)
- documentLayout->documentReloaded(marks, this); // re-adds text marks
+ documentLayout->documentReloaded(this); // re-adds text marks
emit reloadFinished(success);
return success;
}
diff --git a/src/plugins/texteditor/textdocument.h b/src/plugins/texteditor/textdocument.h
index dc52e1e9a8..d1f686f6a0 100644
--- a/src/plugins/texteditor/textdocument.h
+++ b/src/plugins/texteditor/textdocument.h
@@ -37,6 +37,7 @@ class SyntaxHighlighter;
class TabSettings;
class TextDocumentPrivate;
class TextMark;
+class TextSuggestion;
class TypingSettings;
using TextMarks = QList<TextMark *>;
@@ -144,6 +145,9 @@ public:
static QAction *createDiffAgainstCurrentFileAction(QObject *parent,
const std::function<Utils::FilePath()> &filePath);
+ void insertSuggestion(const QString &text, const QTextCursor &cursor);
+ void insertSuggestion(std::unique_ptr<TextSuggestion> &&suggestion);
+
#ifdef WITH_TESTS
void setSilentReload();
#endif
diff --git a/src/plugins/texteditor/textdocumentlayout.cpp b/src/plugins/texteditor/textdocumentlayout.cpp
index 1f26530dbb..703f9563bf 100644
--- a/src/plugins/texteditor/textdocumentlayout.cpp
+++ b/src/plugins/texteditor/textdocumentlayout.cpp
@@ -345,6 +345,21 @@ void TextBlockUserData::setCodeFormatterData(CodeFormatterData *data)
m_codeFormatterData = data;
}
+void TextBlockUserData::insertSuggestion(std::unique_ptr<TextSuggestion> &&suggestion)
+{
+ m_suggestion = std::move(suggestion);
+}
+
+TextSuggestion *TextBlockUserData::suggestion() const
+{
+ return m_suggestion.get();
+}
+
+void TextBlockUserData::clearSuggestion()
+{
+ m_suggestion.reset();
+}
+
void TextBlockUserData::addMark(TextMark *mark)
{
int i = 0;
@@ -355,7 +370,6 @@ void TextBlockUserData::addMark(TextMark *mark)
m_marks.insert(i, mark);
}
-
TextDocumentLayout::TextDocumentLayout(QTextDocument *doc)
: QPlainTextDocumentLayout(doc)
{}
@@ -519,6 +533,81 @@ QByteArray TextDocumentLayout::expectedRawStringSuffix(const QTextBlock &block)
return {};
}
+TextSuggestion *TextDocumentLayout::suggestion(const QTextBlock &block)
+{
+ if (TextBlockUserData *userData = textUserData(block))
+ return userData->suggestion();
+ return nullptr;
+}
+
+void TextDocumentLayout::updateSuggestionFormats(const QTextBlock &block,
+ const FontSettings &fontSettings)
+{
+ if (TextSuggestion *suggestion = TextDocumentLayout::suggestion(block)) {
+ QTextDocument *suggestionDoc = suggestion->document();
+ const QTextCharFormat replacementFormat = fontSettings.toTextCharFormat(
+ TextStyles{C_TEXT, {C_DISABLED_CODE}});
+ QList<QTextLayout::FormatRange> formats = block.layout()->formats();
+ QTextCursor cursor(suggestionDoc);
+ cursor.select(QTextCursor::Document);
+ cursor.setCharFormat(fontSettings.toTextCharFormat(C_TEXT));
+ const int position = suggestion->currentPosition() - block.position();
+ cursor.setPosition(position);
+ const QString trailingText = block.text().mid(position);
+ if (!trailingText.isEmpty()) {
+ const int trailingIndex = suggestionDoc->firstBlock().text().indexOf(trailingText,
+ position);
+ if (trailingIndex >= 0) {
+ cursor.setPosition(trailingIndex, QTextCursor::KeepAnchor);
+ cursor.setCharFormat(replacementFormat);
+ cursor.setPosition(trailingIndex + trailingText.size());
+ const int length = std::max(trailingIndex - position, 0);
+ if (length) {
+ // we have a replacement in the middle of the line adjust all formats that are
+ // behind the replacement
+ QTextLayout::FormatRange rest;
+ rest.start = -1;
+ for (QTextLayout::FormatRange &range : formats) {
+ if (range.start >= position) {
+ range.start += length;
+ } else if (range.start + range.length > position) {
+ // the format range starts before and ends after the position so we need to
+ // split the format into before and after the suggestion format ranges
+ rest.start = trailingIndex;
+ rest.length = range.length - (position - range.start);
+ rest.format = range.format;
+ range.length = position - range.start;
+ }
+ }
+ if (rest.start >= 0)
+ formats += rest;
+ }
+ }
+ }
+ cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
+ cursor.setCharFormat(replacementFormat);
+ suggestionDoc->firstBlock().layout()->setFormats(formats);
+ }
+}
+
+bool TextDocumentLayout::updateSuggestion(const QTextBlock &block,
+ int position,
+ const FontSettings &fontSettings)
+{
+ if (TextSuggestion *suggestion = TextDocumentLayout::suggestion(block)) {
+ auto positionInBlock = position - block.position();
+ const QString start = block.text().left(positionInBlock);
+ const QString end = block.text().mid(positionInBlock);
+ const QString replacement = suggestion->document()->firstBlock().text();
+ if (replacement.startsWith(start) && replacement.indexOf(end, start.size()) >= 0) {
+ suggestion->setCurrentPosition(position);
+ TextDocumentLayout::updateSuggestionFormats(block, fontSettings);
+ return true;
+ }
+ }
+ return false;
+}
+
void TextDocumentLayout::requestExtraAreaUpdate()
{
emit updateExtraArea();
@@ -567,6 +656,7 @@ QSizeF TextDocumentLayout::documentSize() const
TextMarks TextDocumentLayout::documentClosing()
{
+ QTC_ASSERT(m_reloadMarks.isEmpty(), resetReloadMarks());
TextMarks marks;
for (QTextBlock block = document()->begin(); block.isValid(); block = block.next()) {
if (auto data = static_cast<TextBlockUserData *>(block.userData()))
@@ -575,9 +665,18 @@ TextMarks TextDocumentLayout::documentClosing()
return marks;
}
-void TextDocumentLayout::documentReloaded(TextMarks marks, TextDocument *baseTextDocument)
+void TextDocumentLayout::documentAboutToReload()
+{
+ m_reloadMarks = documentClosing();
+ for (TextMark *mark : std::as_const(m_reloadMarks))
+ mark->setDeleteCallback([this, mark] { m_reloadMarks.removeOne(mark); });
+}
+
+void TextDocumentLayout::documentReloaded(TextDocument *baseTextDocument)
{
- for (TextMark *mark : std::as_const(marks)) {
+ const TextMarks marks = m_reloadMarks;
+ resetReloadMarks();
+ for (TextMark *mark : marks) {
int blockNumber = mark->lineNumber() - 1;
QTextBlock block = document()->findBlockByNumber(blockNumber);
if (block.isValid()) {
@@ -632,8 +731,37 @@ void TextDocumentLayout::requestUpdateNow()
requestUpdate();
}
+void TextDocumentLayout::resetReloadMarks()
+{
+ for (TextMark *mark : std::as_const(m_reloadMarks))
+ mark->setDeleteCallback({});
+ m_reloadMarks.clear();
+}
+
+static QRectF replacementBoundingRect(const QTextDocument *replacement)
+{
+ QTC_ASSERT(replacement, return {});
+ auto *layout = static_cast<QPlainTextDocumentLayout *>(replacement->documentLayout());
+ QRectF boundingRect;
+ QTextBlock block = replacement->firstBlock();
+ while (block.isValid()) {
+ const QRectF blockBoundingRect = layout->blockBoundingRect(block);
+ boundingRect.setWidth(std::max(boundingRect.width(), blockBoundingRect.width()));
+ boundingRect.setHeight(boundingRect.height() + blockBoundingRect.height());
+ block = block.next();
+ }
+ return boundingRect;
+}
+
QRectF TextDocumentLayout::blockBoundingRect(const QTextBlock &block) const
{
+ if (TextSuggestion *suggestion = TextDocumentLayout::suggestion(block)) {
+ // since multiple code paths expects that we have a valid block layout after requesting the
+ // block bounding rect explicitly create that layout here
+ ensureBlockLayout(block);
+ return replacementBoundingRect(suggestion->document());
+ }
+
QRectF boundingRect = QPlainTextDocumentLayout::blockBoundingRect(block);
if (TextEditorSettings::fontSettings().relativeLineSpacing() != 100) {
@@ -722,4 +850,12 @@ void insertSorted(Parentheses &list, const Parenthesis &elem)
list.insert(it, elem);
}
+TextSuggestion::TextSuggestion()
+{
+ m_replacementDocument.setDocumentLayout(new TextDocumentLayout(&m_replacementDocument));
+ m_replacementDocument.setDocumentMargin(0);
+}
+
+TextSuggestion::~TextSuggestion() = default;
+
} // namespace TextEditor
diff --git a/src/plugins/texteditor/textdocumentlayout.h b/src/plugins/texteditor/textdocumentlayout.h
index ba31398db7..33387093da 100644
--- a/src/plugins/texteditor/textdocumentlayout.h
+++ b/src/plugins/texteditor/textdocumentlayout.h
@@ -17,8 +17,9 @@
namespace TextEditor {
-struct TEXTEDITOR_EXPORT Parenthesis
+class TEXTEDITOR_EXPORT Parenthesis
{
+public:
enum Type : char { Opened, Closed };
Parenthesis() = default;
@@ -42,6 +43,28 @@ public:
virtual ~CodeFormatterData();
};
+class TEXTEDITOR_EXPORT TextSuggestion
+{
+public:
+ TextSuggestion();
+ virtual ~TextSuggestion();
+ // Returns true if the suggestion was applied completely, false if it was only partially applied.
+ virtual bool apply() = 0;
+ // Returns true if the suggestion was applied completely, false if it was only partially applied.
+ virtual bool applyWord(TextEditorWidget *widget) = 0;
+ virtual void reset() = 0;
+ virtual int position() = 0;
+
+ int currentPosition() const { return m_currentPosition; }
+ void setCurrentPosition(int position) { m_currentPosition = position; }
+
+ QTextDocument *document() { return &m_replacementDocument; }
+
+private:
+ QTextDocument m_replacementDocument;
+ int m_currentPosition = -1;
+};
+
class TEXTEDITOR_EXPORT TextBlockUserData : public QTextBlockUserData
{
public:
@@ -126,6 +149,10 @@ public:
QByteArray expectedRawStringSuffix() { return m_expectedRawStringSuffix; }
void setExpectedRawStringSuffix(const QByteArray &suffix) { m_expectedRawStringSuffix = suffix; }
+ void insertSuggestion(std::unique_ptr<TextSuggestion> &&suggestion);
+ TextSuggestion *suggestion() const;
+ void clearSuggestion();
+
private:
TextMarks m_marks;
int m_foldingIndent : 16;
@@ -139,9 +166,10 @@ private:
CodeFormatterData *m_codeFormatterData;
KSyntaxHighlighting::State m_syntaxState;
QByteArray m_expectedRawStringSuffix; // A bit C++-specific, but let's be pragmatic.
+ std::unique_ptr<QTextDocument> m_replacement;
+ std::unique_ptr<TextSuggestion> m_suggestion;
};
-
class TEXTEDITOR_EXPORT TextDocumentLayout : public QPlainTextDocumentLayout
{
Q_OBJECT
@@ -172,6 +200,12 @@ public:
static void setFolded(const QTextBlock &block, bool folded);
static void setExpectedRawStringSuffix(const QTextBlock &block, const QByteArray &suffix);
static QByteArray expectedRawStringSuffix(const QTextBlock &block);
+ static TextSuggestion *suggestion(const QTextBlock &block);
+ static void updateSuggestionFormats(const QTextBlock &block,
+ const FontSettings &fontSettings);
+ static bool updateSuggestion(const QTextBlock &block,
+ int position,
+ const FontSettings &fontSettings);
class TEXTEDITOR_EXPORT FoldValidator
{
@@ -212,7 +246,8 @@ public:
QRectF blockBoundingRect(const QTextBlock &block) const override;
TextMarks documentClosing();
- void documentReloaded(TextMarks marks, TextDocument *baseextDocument);
+ void documentAboutToReload();
+ void documentReloaded(TextDocument *baseextDocument);
void updateMarksLineNumber();
void updateMarksBlock(const QTextBlock &block);
void scheduleUpdate();
@@ -220,6 +255,8 @@ public:
private:
bool m_updateScheduled = false;
+ TextMarks m_reloadMarks;
+ void resetReloadMarks();
signals:
void updateExtraArea();
diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp
index 17cabc3891..ecb617aed7 100644
--- a/src/plugins/texteditor/texteditor.cpp
+++ b/src/plugins/texteditor/texteditor.cpp
@@ -61,6 +61,7 @@
#include <utils/minimizableinfobars.h>
#include <utils/multitextcursor.h>
#include <utils/qtcassert.h>
+#include <utils/searchresultitem.h>
#include <utils/styledbar.h>
#include <utils/stylehelper.h>
#include <utils/textutils.h>
@@ -657,6 +658,7 @@ public:
uint m_optionalActionMask = TextEditorActionHandler::None;
bool m_contentsChanged = false;
bool m_lastCursorChangeWasInteresting = false;
+ std::shared_ptr<void> m_suggestionBlocker;
QSharedPointer<TextDocument> m_document;
QList<QMetaObject::Connection> m_documentConnections;
@@ -789,7 +791,7 @@ public:
QScopedPointer<AutoCompleter> m_autoCompleter;
CommentDefinition m_commentDefinition;
- QFutureWatcher<FileSearchResultList> *m_searchWatcher = nullptr;
+ QFutureWatcher<SearchResultItems> *m_searchWatcher = nullptr;
QVector<SearchResult> m_searchResults;
QTimer m_scrollBarUpdateTimer;
HighlightScrollBarController *m_highlightScrollBarController = nullptr;
@@ -819,6 +821,11 @@ public:
QStack<UndoMultiCursor> m_undoCursorStack;
QList<int> m_visualIndentCache;
int m_visualIndentOffset = 0;
+
+ void insertSuggestion(std::unique_ptr<TextSuggestion> &&suggestion);
+ void updateSuggestion();
+ void clearCurrentSuggestion();
+ QTextBlock m_suggestionBlock;
};
class TextEditorWidgetFind : public BaseTextFind
@@ -840,10 +847,27 @@ public:
private:
TextEditorWidget * const m_editor;
- static QFutureWatcher<FileSearchResultList> *m_selectWatcher;
+ static QFutureWatcher<SearchResultItems> *m_selectWatcher;
};
-QFutureWatcher<FileSearchResultList> *TextEditorWidgetFind::m_selectWatcher = nullptr;
+static QTextCursor selectRange(QTextDocument *textDocument, const Text::Range &range,
+ TextEditorWidgetPrivate::SearchResult *searchResult = nullptr)
+{
+ const int startLine = qMax(range.begin.line - 1, 0);
+ const int startColumn = qMax(range.begin.column, 0);
+ const int endLine = qMax(range.end.line - 1, 0);
+ const int endColumn = qMax(range.end.column, 0);
+ const int startPosition = textDocument->findBlockByNumber(startLine).position() + startColumn;
+ const int endPosition = textDocument->findBlockByNumber(endLine).position() + endColumn;
+ QTextCursor textCursor(textDocument);
+ textCursor.setPosition(startPosition);
+ textCursor.setPosition(endPosition, QTextCursor::KeepAnchor);
+ if (searchResult)
+ *searchResult = {startPosition + 1, endPosition + 1};
+ return textCursor;
+}
+
+QFutureWatcher<SearchResultItems> *TextEditorWidgetFind::m_selectWatcher = nullptr;
void TextEditorWidgetFind::selectAll(const QString &txt, FindFlags findFlags)
{
@@ -852,26 +876,24 @@ void TextEditorWidgetFind::selectAll(const QString &txt, FindFlags findFlags)
cancelCurrentSelectAll();
- m_selectWatcher = new QFutureWatcher<FileSearchResultList>();
- connect(m_selectWatcher, &QFutureWatcher<Utils::FileSearchResultList>::finished,
- this, [this] {
- const QFuture<FileSearchResultList> future = m_selectWatcher->future();
- m_selectWatcher->deleteLater();
- m_selectWatcher = nullptr;
- if (future.resultCount() <= 0)
- return;
- const FileSearchResultList &results = future.result();
- const QTextCursor c(m_editor->document());
- auto cursorForResult = [c](const FileSearchResult &r) {
- return Utils::Text::selectAt(c, r.lineNumber, r.matchStart + 1, r.matchLength);
- };
- QList<QTextCursor> cursors = Utils::transform(results, cursorForResult);
- cursors = Utils::filtered(cursors, [this](const QTextCursor &c) {
- return m_editor->inFindScope(c);
- });
- m_editor->setMultiTextCursor(MultiTextCursor(cursors));
- m_editor->setFocus();
- });
+ m_selectWatcher = new QFutureWatcher<SearchResultItems>();
+ connect(m_selectWatcher, &QFutureWatcher<SearchResultItems>::finished, this, [this] {
+ const QFuture<SearchResultItems> future = m_selectWatcher->future();
+ m_selectWatcher->deleteLater();
+ m_selectWatcher = nullptr;
+ if (future.resultCount() <= 0)
+ return;
+ const SearchResultItems &results = future.result();
+ const auto cursorForResult = [this](const SearchResultItem &item) {
+ return selectRange(m_editor->document(), item.mainRange());
+ };
+ QList<QTextCursor> cursors = Utils::transform(results, cursorForResult);
+ cursors = Utils::filtered(cursors, [this](const QTextCursor &c) {
+ return m_editor->inFindScope(c);
+ });
+ m_editor->setMultiTextCursor(MultiTextCursor(cursors));
+ m_editor->setFocus();
+ });
const FilePath &fileName = m_editor->textDocument()->filePath();
QMap<FilePath, QString> fileToContentsMap;
@@ -900,6 +922,7 @@ void TextEditorWidgetFind::cancelCurrentSelectAll()
TextEditorWidgetPrivate::TextEditorWidgetPrivate(TextEditorWidget *parent)
: q(parent)
+ , m_suggestionBlocker((void *) this, [](void *) {})
, m_overlay(new TextEditorOverlay(q))
, m_snippetOverlay(new SnippetOverlay(q))
, m_searchResultOverlay(new TextEditorOverlay(q))
@@ -1646,6 +1669,43 @@ void TextEditorWidgetPrivate::handleMoveBlockSelection(QTextCursor::MoveOperatio
q->setMultiTextCursor(MultiTextCursor(cursors));
}
+void TextEditorWidgetPrivate::insertSuggestion(std::unique_ptr<TextSuggestion> &&suggestion)
+{
+ clearCurrentSuggestion();
+
+ if (m_suggestionBlocker.use_count() > 1)
+ return;
+
+ auto cursor = q->textCursor();
+ cursor.setPosition(suggestion->position());
+ m_suggestionBlock = cursor.block();
+ m_document->insertSuggestion(std::move(suggestion));
+}
+
+void TextEditorWidgetPrivate::updateSuggestion()
+{
+ if (!m_suggestionBlock.isValid())
+ return;
+ if (m_cursors.mainCursor().block() != m_suggestionBlock) {
+ clearCurrentSuggestion();
+ } else {
+ if (!TextDocumentLayout::updateSuggestion(m_suggestionBlock,
+ m_cursors.mainCursor().position(),
+ m_document->fontSettings())) {
+ clearCurrentSuggestion();
+ }
+ }
+}
+
+void TextEditorWidgetPrivate::clearCurrentSuggestion()
+{
+ if (TextBlockUserData *userData = TextDocumentLayout::textUserData(m_suggestionBlock)) {
+ userData->clearSuggestion();
+ m_document->updateLayout();
+ }
+ m_suggestionBlock = QTextBlock();
+}
+
void TextEditorWidget::selectEncoding()
{
TextDocument *doc = d->m_document.data();
@@ -1828,6 +1888,8 @@ TextEditorWidget *TextEditorWidget::fromEditor(const IEditor *editor)
void TextEditorWidgetPrivate::editorContentsChange(int position, int charsRemoved, int charsAdded)
{
+ updateSuggestion();
+
if (m_bracketsAnimator)
m_bracketsAnimator->finish();
@@ -2308,6 +2370,11 @@ void TextEditorWidget::renameSymbolUnderCursor()
emit requestRename(textCursor());
}
+void TextEditorWidget::openCallHierarchy()
+{
+ emit requestCallHierarchy(textCursor());
+}
+
void TextEditorWidget::abortAssist()
{
d->m_codeAssistant.destroyContext();
@@ -2488,7 +2555,7 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e)
{
ICore::restartTrimmer();
- ExecuteOnDestruction eod([&]() { d->clearBlockSelection(); });
+ auto clearBlockSelectionGuard = qScopeGuard([&]() { d->clearBlockSelection(); });
if (!isModifier(e) && mouseHidingEnabled())
viewport()->setCursor(Qt::BlankCursor);
@@ -2506,6 +2573,11 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e)
d->m_maybeFakeTooltipEvent = false;
if (e->key() == Qt::Key_Escape ) {
TextEditorWidgetFind::cancelCurrentSelectAll();
+ if (d->m_suggestionBlock.isValid()) {
+ d->clearCurrentSuggestion();
+ e->accept();
+ return;
+ }
if (d->m_snippetOverlay->isVisible()) {
e->accept();
d->m_snippetOverlay->accept();
@@ -2527,6 +2599,21 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e)
const bool inOverwriteMode = overwriteMode();
const bool hasMultipleCursors = cursor.hasMultipleCursors();
+ if (TextSuggestion *suggestion = TextDocumentLayout::suggestion(d->m_suggestionBlock)) {
+ if (e->matches(QKeySequence::MoveToNextWord)) {
+ e->accept();
+ if (suggestion->applyWord(this))
+ d->clearCurrentSuggestion();
+ return;
+ } else if (e->modifiers() == Qt::NoModifier
+ && (e->key() == Qt::Key_Tab || e->key() == Qt::Key_Backtab)) {
+ e->accept();
+ if (suggestion->apply())
+ d->clearCurrentSuggestion();
+ return;
+ }
+ }
+
if (!ro
&& (e == QKeySequence::InsertParagraphSeparator
|| (!d->m_lineSeparatorsAllowed && e == QKeySequence::InsertLineSeparator))) {
@@ -2684,6 +2771,8 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e)
| Qt::AltModifier
| Qt::MetaModifier)) == Qt::NoModifier) {
e->accept();
+ if (d->m_suggestionBlock.isValid())
+ d->clearCurrentSuggestion();
if (cursor.hasSelection()) {
cursor.removeSelectedText();
setMultiTextCursor(cursor);
@@ -2745,8 +2834,7 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e)
}
if (blockSelectionOperation != QTextCursor::NoMove) {
- auto doNothing = [](){};
- eod.reset(doNothing);
+ clearBlockSelectionGuard.dismiss();
d->handleMoveBlockSelection(blockSelectionOperation);
} else if (!d->cursorMoveKeyEvent(e)) {
QTextCursor cursor = textCursor();
@@ -2834,13 +2922,13 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e)
if (!autoText.isEmpty())
cursor.setPosition(autoText.length() == 1 ? cursor.position() : cursor.anchor());
+ setTextCursor(cursor);
+
if (doEditBlock) {
cursor.endEditBlock();
if (cursorWithinSnippet)
d->m_snippetOverlay->updateEquivalentSelections(textCursor());
}
-
- setTextCursor(cursor);
}
if (!ro && e->key() == Qt::Key_Delete && d->m_parenthesesMatchingEnabled)
@@ -3105,7 +3193,9 @@ bool TextEditorWidget::event(QEvent *e)
case QEvent::ShortcutOverride: {
auto ke = static_cast<QKeyEvent *>(e);
if (ke->key() == Qt::Key_Escape
- && (d->m_snippetOverlay->isVisible() || multiTextCursor().hasMultipleCursors())) {
+ && (d->m_snippetOverlay->isVisible()
+ || multiTextCursor().hasMultipleCursors()
+ || d->m_suggestionBlock.isValid())) {
e->accept();
} else {
// hack copied from QInputControl::isCommonTextEditShortcut
@@ -3241,7 +3331,7 @@ void TextEditorWidget::restoreState(const QByteArray &state)
d->m_lastCursorChangeWasInteresting = false; // avoid adding last position to history
// line is 1-based, column is 0-based
- gotoLine(lineVal, columnVal - 1);
+ gotoLine(lineVal, columnVal);
verticalScrollBar()->setValue(vval);
horizontalScrollBar()->setValue(hval);
@@ -3676,7 +3766,10 @@ bool TextEditorWidget::viewportEvent(QEvent *event)
// Only handle tool tip for text cursor if mouse is within the block for the text cursor,
// and not if the mouse is e.g. in the empty space behind a short line.
if (line.isValid()) {
- if (pos.x() <= blockBoundingGeometry(block).left() + line.naturalTextRect().right()) {
+ const QRectF blockGeometry = blockBoundingGeometry(block);
+ const int width = block == d->m_suggestionBlock ? blockGeometry.width()
+ : line.naturalTextRect().right();
+ if (pos.x() <= blockGeometry.left() + width) {
d->processTooltipRequest(tc);
return true;
} else if (d->processAnnotaionTooltipRequest(block, pos)) {
@@ -3999,7 +4092,13 @@ static TextMarks availableMarks(const TextMarks &marks,
QRectF TextEditorWidgetPrivate::getLastLineLineRect(const QTextBlock &block)
{
- const QTextLayout *layout = block.layout();
+ QTextLayout *layout = nullptr;
+ if (TextSuggestion *suggestion = TextDocumentLayout::suggestion(block))
+ layout = suggestion->document()->firstBlock().layout();
+ else
+ layout = block.layout();
+
+ QTC_ASSERT(layout, layout = block.layout());
const int lineCount = layout->lineCount();
if (lineCount < 1)
return {};
@@ -4394,15 +4493,28 @@ void TextEditorWidgetPrivate::paintAdditionalVisualWhitespaces(PaintEventData &d
visualArrow);
}
if (!nextBlockIsValid) { // paint EOF symbol
- QTextLine line = layout->lineAt(lineCount-1);
+ if (TextSuggestion *suggestion = TextDocumentLayout::suggestion(data.block)) {
+ const QTextBlock lastReplacementBlock = suggestion->document()->lastBlock();
+ for (QTextBlock block = suggestion->document()->firstBlock();
+ block != lastReplacementBlock && block.isValid();
+ block = block.next()) {
+ top += suggestion->document()
+ ->documentLayout()
+ ->blockBoundingRect(block)
+ .height();
+ }
+ layout = lastReplacementBlock.layout();
+ lineCount = layout->lineCount();
+ }
+ QTextLine line = layout->lineAt(lineCount - 1);
QRectF lineRect = line.naturalTextRect().translated(data.offset.x(), top);
int h = 4;
lineRect.adjust(0, 0, -1, -1);
QPainterPath path;
- QPointF pos(lineRect.topRight() + QPointF(h+4, line.ascent()));
+ QPointF pos(lineRect.topRight() + QPointF(h + 4, line.ascent()));
path.moveTo(pos);
path.lineTo(pos + QPointF(-h, -h));
- path.lineTo(pos + QPointF(0, -2*h));
+ path.lineTo(pos + QPointF(0, -2 * h));
path.lineTo(pos + QPointF(h, -h));
path.closeSubpath();
painter.setBrush(painter.pen().color());
@@ -4649,6 +4761,21 @@ void TextEditorWidgetPrivate::setupSelections(const PaintEventData &data,
PaintEventBlockData &blockData) const
{
QVector<QTextLayout::FormatRange> prioritySelections;
+
+ int deltaPos = -1;
+ int delta = 0;
+
+ if (TextSuggestion *suggestion = TextDocumentLayout::suggestion(data.block)) {
+ deltaPos = suggestion->currentPosition() - data.block.position();
+ const QString trailingText = data.block.text().mid(deltaPos);
+ if (!trailingText.isEmpty()) {
+ const int trailingIndex
+ = suggestion->document()->firstBlock().text().indexOf(trailingText, deltaPos);
+ if (trailingIndex >= 0)
+ delta = std::max(trailingIndex - deltaPos, 0);
+ }
+ }
+
for (int i = 0; i < data.context.selections.size(); ++i) {
const QAbstractTextDocumentLayout::Selection &range = data.context.selections.at(i);
const int selStart = range.cursor.selectionStart() - blockData.position;
@@ -4659,6 +4786,22 @@ void TextEditorWidgetPrivate::setupSelections(const PaintEventData &data,
o.start = selStart;
o.length = selEnd - selStart;
o.format = range.format;
+ QTextLayout::FormatRange rest;
+ rest.start = -1;
+ if (deltaPos >= 0 && delta != 0) {
+ if (o.start >= deltaPos) {
+ o.start += delta;
+ } else if (o.start + o.length > deltaPos) {
+ // the format range starts before and ends after the position so we need to
+ // split the format into before and after the suggestion format ranges
+ rest.start = deltaPos + delta;
+ rest.length = o.length - (deltaPos - o.start);
+ rest.format = o.format;
+ o.length = deltaPos - o.start;
+ }
+ }
+
+ o.format = range.format;
if (data.textCursor.hasSelection() && data.textCursor == range.cursor
&& data.textCursor.anchor() == range.cursor.anchor()) {
const QTextCharFormat selectionFormat = data.fontSettings.toTextCharFormat(C_SELECTION);
@@ -4670,10 +4813,15 @@ void TextEditorWidgetPrivate::setupSelections(const PaintEventData &data,
|| (o.format.foreground().style() == Qt::NoBrush
&& o.format.underlineStyle() != QTextCharFormat::NoUnderline
&& o.format.background() == Qt::NoBrush)) {
- if (q->selectionVisible(data.block.blockNumber()))
+ if (q->selectionVisible(data.block.blockNumber())) {
prioritySelections.append(o);
+ if (rest.start >= 0)
+ prioritySelections.append(rest);
+ }
} else {
blockData.selections.append(o);
+ if (rest.start >= 0)
+ blockData.selections.append(rest);
}
}
}
@@ -4784,6 +4932,7 @@ void TextEditorWidget::paintEvent(QPaintEvent *e)
if (blockData.boundingRect.bottom() >= data.eventRect.top()
&& blockData.boundingRect.top() <= data.eventRect.bottom()) {
+ data.documentLayout->ensureBlockLayout(data.block);
d->setupBlockLayout(data, painter, blockData);
blockData.position = data.block.position();
blockData.length = data.block.length();
@@ -4868,6 +5017,27 @@ void TextEditorWidget::paintBlock(QPainter *painter,
const QVector<QTextLayout::FormatRange> &selections,
const QRect &clipRect) const
{
+ if (TextSuggestion *suggestion = TextDocumentLayout::suggestion(block)) {
+ QTextBlock suggestionBlock = suggestion->document()->firstBlock();
+ QPointF suggestionOffset = offset;
+ suggestionOffset.rx() += document()->documentMargin();
+ while (suggestionBlock.isValid()) {
+ const QVector<QTextLayout::FormatRange> blockSelections
+ = suggestionBlock.blockNumber() == 0 ? selections
+ : QVector<QTextLayout::FormatRange>{};
+ suggestionBlock.layout()->draw(painter,
+ suggestionOffset,
+ blockSelections,
+ clipRect);
+ suggestionOffset.ry() += suggestion->document()
+ ->documentLayout()
+ ->blockBoundingRect(suggestionBlock)
+ .height();
+ suggestionBlock = suggestionBlock.next();
+ }
+ return;
+ }
+
block.layout()->draw(painter, offset, selections, clipRect);
}
@@ -5407,6 +5577,7 @@ void TextEditorWidget::slotCursorPositionChanged()
setMultiTextCursor(cursor);
d->updateCursorSelections();
d->updateHighlights();
+ d->updateSuggestion();
}
void TextEditorWidgetPrivate::updateHighlights()
@@ -5842,8 +6013,42 @@ void TextEditorWidget::addHoverHandler(BaseHoverHandler *handler)
void TextEditorWidget::removeHoverHandler(BaseHoverHandler *handler)
{
- d->m_hoverHandlers.removeAll(handler);
- d->m_hoverHandlerRunner.handlerRemoved(handler);
+ if (d->m_hoverHandlers.removeAll(handler) > 0)
+ d->m_hoverHandlerRunner.handlerRemoved(handler);
+}
+
+void TextEditorWidget::insertSuggestion(std::unique_ptr<TextSuggestion> &&suggestion)
+{
+ d->insertSuggestion(std::move(suggestion));
+}
+
+void TextEditorWidget::clearSuggestion()
+{
+ d->clearCurrentSuggestion();
+}
+
+TextSuggestion *TextEditorWidget::currentSuggestion() const
+{
+ if (d->m_suggestionBlock.isValid())
+ return TextDocumentLayout::suggestion(d->m_suggestionBlock);
+ return nullptr;
+}
+
+bool TextEditorWidget::suggestionVisible() const
+{
+ return currentSuggestion();
+}
+
+bool TextEditorWidget::suggestionsBlocked() const
+{
+ return d->m_suggestionBlocker.use_count() > 1;
+}
+
+TextEditorWidget::SuggestionBlocker TextEditorWidget::blockSuggestions()
+{
+ if (!suggestionsBlocked())
+ clearSuggestion();
+ return d->m_suggestionBlocker;
}
#ifdef WITH_TESTS
@@ -6482,16 +6687,11 @@ void TextEditorWidgetPrivate::searchResultsReady(int beginIndex, int endIndex)
{
QVector<SearchResult> results;
for (int index = beginIndex; index < endIndex; ++index) {
- const FileSearchResultList resultList = m_searchWatcher->resultAt(index);
- for (FileSearchResult result : resultList) {
- const QTextBlock &block = q->document()->findBlockByNumber(result.lineNumber - 1);
- const int matchStart = block.position() + result.matchStart;
- QTextCursor cursor(block);
- cursor.setPosition(matchStart);
- cursor.setPosition(matchStart + result.matchLength, QTextCursor::KeepAnchor);
- if (!q->inFindScope(cursor))
- continue;
- results << SearchResult{matchStart, result.matchLength};
+ const SearchResultItems resultList = m_searchWatcher->resultAt(index);
+ for (const SearchResultItem &result : resultList) {
+ SearchResult searchResult;
+ if (q->inFindScope(selectRange(q->document(), result.mainRange(), &searchResult)))
+ results << searchResult;
}
}
m_searchResults << results;
@@ -6537,10 +6737,10 @@ void TextEditorWidgetPrivate::highlightSearchResultsInScrollBar()
adjustScrollBarRanges();
- m_searchWatcher = new QFutureWatcher<FileSearchResultList>();
- connect(m_searchWatcher, &QFutureWatcher<FileSearchResultList>::resultsReadyAt,
+ m_searchWatcher = new QFutureWatcher<SearchResultItems>;
+ connect(m_searchWatcher, &QFutureWatcher<SearchResultItems>::resultsReadyAt,
this, &TextEditorWidgetPrivate::searchResultsReady);
- connect(m_searchWatcher, &QFutureWatcher<FileSearchResultList>::finished,
+ connect(m_searchWatcher, &QFutureWatcher<SearchResultItems>::finished,
this, &TextEditorWidgetPrivate::searchFinished);
m_searchWatcher->setPendingResultsLimit(10);
@@ -6586,7 +6786,7 @@ void TextEditorWidgetPrivate::addSearchResultsToScrollBar(const QVector<SearchRe
{
if (!m_highlightScrollBarController)
return;
- for (SearchResult result : results) {
+ for (const SearchResult &result : results) {
const QTextBlock &block = q->document()->findBlock(result.start);
if (block.isValid() && block.isVisible()) {
const int firstLine = block.layout()->lineForTextPosition(result.start - block.position()).lineNumber();
@@ -6639,10 +6839,11 @@ MultiTextCursor TextEditorWidget::multiTextCursor() const
void TextEditorWidget::setMultiTextCursor(const Utils::MultiTextCursor &cursor)
{
+ if (cursor == d->m_cursors)
+ return;
+
const MultiTextCursor oldCursor = d->m_cursors;
const_cast<MultiTextCursor &>(d->m_cursors) = cursor;
- if (oldCursor == d->m_cursors)
- return;
doSetTextCursor(d->m_cursors.mainCursor(), /*keepMultiSelection*/ true);
QRect updateRect = d->cursorUpdateRect(oldCursor);
if (d->m_highlightCurrentLine)
@@ -8068,6 +8269,11 @@ void TextEditorWidget::appendStandardContextMenuActions(QMenu *menu)
if (!menu->actions().contains(findUsage))
menu->addAction(findUsage);
}
+ if (optionalActions() & TextEditorActionHandler::CallHierarchy) {
+ const auto callHierarchy = ActionManager::command(Constants::OPEN_CALL_HIERARCHY)->action();
+ if (!menu->actions().contains(callHierarchy))
+ menu->addAction(callHierarchy);
+ }
menu->addSeparator();
appendMenuActionsFromContext(menu, Constants::M_STANDARDCONTEXTMENU);
diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h
index 98bddb84b9..e7cb5b3434 100644
--- a/src/plugins/texteditor/texteditor.h
+++ b/src/plugins/texteditor/texteditor.h
@@ -42,15 +42,16 @@ class HighlightScrollBarController;
}
namespace TextEditor {
-class TextDocument;
-class TextMark;
-class BaseHoverHandler;
-class RefactorOverlay;
-class SyntaxHighlighter;
class AssistInterface;
+class BaseHoverHandler;
+class CompletionAssistProvider;
class IAssistProvider;
class ICodeStylePreferences;
-class CompletionAssistProvider;
+class RefactorOverlay;
+class SyntaxHighlighter;
+class TextDocument;
+class TextMark;
+class TextSuggestion;
using RefactorMarkers = QList<RefactorMarker>;
using TextMarks = QList<TextMark *>;
@@ -437,6 +438,7 @@ public:
virtual void findUsages();
virtual void renameSymbolUnderCursor();
+ virtual void openCallHierarchy();
/// Abort code assistant if it is running.
void abortAssist();
@@ -469,6 +471,16 @@ public:
void addHoverHandler(BaseHoverHandler *handler);
void removeHoverHandler(BaseHoverHandler *handler);
+ void insertSuggestion(std::unique_ptr<TextSuggestion> &&suggestion);
+ void clearSuggestion();
+ TextSuggestion *currentSuggestion() const;
+ bool suggestionVisible() const;
+ bool suggestionsBlocked() const;
+
+ using SuggestionBlocker = std::shared_ptr<void>;
+ // Returns an object that blocks suggestions until it is destroyed.
+ SuggestionBlocker blockSuggestions();
+
#ifdef WITH_TESTS
void processTooltipRequest(const QTextCursor &c);
#endif
@@ -483,6 +495,7 @@ signals:
bool resolveTarget, bool inNextSplit);
void requestUsages(const QTextCursor &cursor);
void requestRename(const QTextCursor &cursor);
+ void requestCallHierarchy(const QTextCursor &cursor);
void optionalActionMaskChanged();
void toolbarOutlineChanged(QWidget *newOutline);
diff --git a/src/plugins/texteditor/texteditor.qbs b/src/plugins/texteditor/texteditor.qbs
index 971ef31fae..071e0b5fc2 100644
--- a/src/plugins/texteditor/texteditor.qbs
+++ b/src/plugins/texteditor/texteditor.qbs
@@ -94,6 +94,8 @@ Project {
"linenumberfilter.h",
"marginsettings.cpp",
"marginsettings.h",
+ "markdowneditor.cpp",
+ "markdowneditor.h",
"outlinefactory.cpp",
"outlinefactory.h",
"plaintexteditorfactory.cpp",
@@ -220,10 +222,10 @@ Project {
]
}
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
+ "codeassist/codeassist_test.cpp",
+ "codeassist/codeassist_test.h",
"texteditor_test.cpp",
]
}
diff --git a/src/plugins/texteditor/texteditor_test.cpp b/src/plugins/texteditor/texteditor_test.cpp
index e939e51568..3b88ac5efb 100644
--- a/src/plugins/texteditor/texteditor_test.cpp
+++ b/src/plugins/texteditor/texteditor_test.cpp
@@ -3,20 +3,13 @@
#ifdef WITH_TESTS
-#include <QGuiApplication>
-#include <QClipboard>
-#include <QString>
-#include <QtTest/QtTest>
-
-#include <coreplugin/editormanager/editormanager.h>
-#include <coreplugin/coreconstants.h>
-
-#include "texteditor.h"
-#include "texteditorplugin.h"
-#include "textdocument.h"
#include "tabsettings.h"
+#include "texteditorplugin.h"
+
+#include <QTextDocument>
+#include <QtTest/QtTest>
-using namespace TextEditor;
+namespace TextEditor {
QString tabPolicyToString(TabSettings::TabPolicy policy)
{
@@ -149,4 +142,6 @@ void Internal::TextEditorPlugin::testIndentationClean()
QCOMPARE(settings.isIndentationClean(block, indentSize), clean);
}
+} // namespace TextEditor
+
#endif // ifdef WITH_TESTS
diff --git a/src/plugins/texteditor/texteditoractionhandler.cpp b/src/plugins/texteditor/texteditoractionhandler.cpp
index 25116729c9..00ed94a5ee 100644
--- a/src/plugins/texteditor/texteditoractionhandler.cpp
+++ b/src/plugins/texteditor/texteditoractionhandler.cpp
@@ -116,6 +116,7 @@ public:
QAction *m_followSymbolAction = nullptr;
QAction *m_followSymbolInNextSplitAction = nullptr;
QAction *m_findUsageAction = nullptr;
+ QAction *m_openCallHierarchyAction = nullptr;
QAction *m_renameSymbolAction = nullptr;
QAction *m_jumpToFileAction = nullptr;
QAction *m_jumpToFileInNextSplitAction = nullptr;
@@ -228,6 +229,8 @@ void TextEditorActionHandlerPrivate::createActions()
m_jumpToFileInNextSplitAction = registerAction(JUMP_TO_FILE_UNDER_CURSOR_IN_NEXT_SPLIT,
[] (TextEditorWidget *w) { w->openLinkUnderCursorInNextSplit(); }, true, Tr::tr("Jump to File Under Cursor in Next Split"),
QKeySequence(Utils::HostOsInfo::isMacHost() ? Tr::tr("Meta+E, F2") : Tr::tr("Ctrl+E, F2")).toString());
+ m_openCallHierarchyAction = registerAction(OPEN_CALL_HIERARCHY,
+ [] (TextEditorWidget *w) { w->openCallHierarchy(); }, true, Tr::tr("Open Call Hierarchy"));
registerAction(VIEW_PAGE_UP,
[] (TextEditorWidget *w) { w->viewPageUp(); }, true, Tr::tr("Move the View a Page Up and Keep the Cursor Position"),
@@ -484,6 +487,8 @@ void TextEditorActionHandlerPrivate::updateOptionalActions()
optionalActions & TextEditorActionHandler::UnCollapseAll);
m_renameSymbolAction->setEnabled(
optionalActions & TextEditorActionHandler::RenameSymbol);
+ m_openCallHierarchyAction->setEnabled(
+ optionalActions & TextEditorActionHandler::CallHierarchy);
bool formatEnabled = (optionalActions & TextEditorActionHandler::Format)
&& m_currentEditorWidget && !m_currentEditorWidget->isReadOnly();
diff --git a/src/plugins/texteditor/texteditoractionhandler.h b/src/plugins/texteditor/texteditoractionhandler.h
index 277c2cf66f..2bdb8efff3 100644
--- a/src/plugins/texteditor/texteditoractionhandler.h
+++ b/src/plugins/texteditor/texteditoractionhandler.h
@@ -36,7 +36,8 @@ public:
FollowSymbolUnderCursor = 8,
JumpToFileUnderCursor = 16,
RenameSymbol = 32,
- FindUsage = 64
+ FindUsage = 64,
+ CallHierarchy = 128
};
using TextEditorWidgetResolver = std::function<TextEditorWidget *(Core::IEditor *)>;
diff --git a/src/plugins/texteditor/texteditorconstants.h b/src/plugins/texteditor/texteditorconstants.h
index 0560dae9d8..f422630f0d 100644
--- a/src/plugins/texteditor/texteditorconstants.h
+++ b/src/plugins/texteditor/texteditorconstants.h
@@ -208,6 +208,7 @@ const char FOLLOW_SYMBOL_UNDER_CURSOR_IN_NEXT_SPLIT[] = "TextEditor.FollowSymbol
const char FIND_USAGES[] = "TextEditor.FindUsages";
// moved from CppEditor to TextEditor avoid breaking the setting by using the old key
const char RENAME_SYMBOL[] = "CppEditor.RenameSymbolUnderCursor";
+const char OPEN_CALL_HIERARCHY[] = "TextEditor.OpenCallHierarchy";
const char JUMP_TO_FILE_UNDER_CURSOR[] = "TextEditor.JumpToFileUnderCursor";
const char JUMP_TO_FILE_UNDER_CURSOR_IN_NEXT_SPLIT[] = "TextEditor.JumpToFileUnderCursorInNextSplit";
diff --git a/src/plugins/texteditor/texteditoroverlay.cpp b/src/plugins/texteditor/texteditoroverlay.cpp
index 52ab8709de..4257adbbdb 100644
--- a/src/plugins/texteditor/texteditoroverlay.cpp
+++ b/src/plugins/texteditor/texteditoroverlay.cpp
@@ -129,6 +129,7 @@ QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, co
QTextLayout *blockLayout = block.layout();
int pos = begin.position() - begin.block().position();
QTextLine line = blockLayout->lineForTextPosition(pos);
+ QTC_ASSERT(line.isValid(), return {});
QRectF lineRect = line.naturalTextRect();
lineRect = lineRect.translated(blockGeometry.topLeft());
int x = line.cursorToX(pos);
@@ -154,12 +155,12 @@ QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, co
QTextLayout *blockLayout = block.layout();
int firstLine = 0;
- QTextLine line = blockLayout->lineAt(firstLine);
int beginChar = 0;
if (block == begin.block()) {
beginChar = begin.positionInBlock();
- line = blockLayout->lineForTextPosition(beginChar);
+ QTextLine line = blockLayout->lineForTextPosition(beginChar);
+ QTC_ASSERT(line.isValid(), return {});
firstLine = line.lineNumber();
const int lineEnd = line.textStart() + line.textLength();
if (beginChar == lineEnd)
@@ -170,7 +171,9 @@ QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, co
int endChar = -1;
if (block == end.block()) {
endChar = end.positionInBlock();
- lastLine = blockLayout->lineForTextPosition(endChar).lineNumber();
+ QTextLine line = blockLayout->lineForTextPosition(endChar);
+ QTC_ASSERT(line.isValid(), return {});
+ lastLine = line.lineNumber();
if (endChar == beginChar)
break; // Do not expand overlay to empty selection at end
} else {
@@ -181,7 +184,8 @@ QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, co
}
for (int i = firstLine; i <= lastLine; ++i) {
- line = blockLayout->lineAt(i);
+ QTextLine line = blockLayout->lineAt(i);
+ QTC_ASSERT(line.isValid(), return {});
QRectF lineRect = line.naturalTextRect();
if (i == firstLine && beginChar > 0)
lineRect.setLeft(line.cursorToX(beginChar));
diff --git a/src/plugins/texteditor/texteditorplugin.cpp b/src/plugins/texteditor/texteditorplugin.cpp
index e97453adda..4d5c2268d1 100644
--- a/src/plugins/texteditor/texteditorplugin.cpp
+++ b/src/plugins/texteditor/texteditorplugin.cpp
@@ -10,6 +10,7 @@
#include "highlighter.h"
#include "icodestylepreferences.h"
#include "linenumberfilter.h"
+#include "markdowneditor.h"
#include "outlinefactory.h"
#include "plaintexteditorfactory.h"
#include "snippets/snippetprovider.h"
@@ -19,6 +20,10 @@
#include "texteditorsettings.h"
#include "texteditortr.h"
+#ifdef WITH_TESTS
+#include "codeassist/codeassist_test.h"
+#endif
+
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
@@ -66,6 +71,7 @@ public:
FindInOpenFiles findInOpenFilesFilter;
PlainTextEditorFactory plainTextEditorFactory;
+ MarkdownEditorFactory markdownEditorFactory;
};
static TextEditorPlugin *m_instance = nullptr;
@@ -140,6 +146,10 @@ void TextEditorPlugin::initialize()
Tr::tr("Text", "SnippetProvider"));
d->createStandardContextMenu();
+
+#ifdef WITH_TESTS
+ addTest<CodeAssistTests>();
+#endif
}
void TextEditorPluginPrivate::extensionsInitialized()
diff --git a/src/plugins/texteditor/textmark.cpp b/src/plugins/texteditor/textmark.cpp
index 37be854d9b..4f86bc5f94 100644
--- a/src/plugins/texteditor/textmark.cpp
+++ b/src/plugins/texteditor/textmark.cpp
@@ -12,11 +12,13 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/documentmanager.h>
#include <coreplugin/icore.h>
+#include <utils/outputformatter.h>
#include <utils/qtcassert.h>
#include <utils/tooltip/tooltip.h>
#include <utils/utilsicons.h>
#include <QAction>
+#include <QDesktopServices>
#include <QGridLayout>
#include <QPainter>
#include <QToolButton>
@@ -79,6 +81,8 @@ TextMark::~TextMark()
TextMarkRegistry::remove(this);
if (m_baseTextDocument)
m_baseTextDocument->removeMark(this);
+ if (m_deleteCallback)
+ m_deleteCallback();
m_baseTextDocument = nullptr;
}
@@ -282,7 +286,7 @@ void TextMark::addToToolTipLayout(QGridLayout *target) const
if (m_category.id.isValid() && !m_lineAnnotation.isEmpty()) {
auto visibilityAction = new QAction;
const bool isHidden = TextDocument::marksAnnotationHidden(m_category.id);
- visibilityAction->setIcon(Utils::Icons::EYE_OPEN_TOOLBAR.icon());
+ visibilityAction->setIcon(Utils::Icons::EYE_OPEN.icon());
const QString tooltip = (isHidden ? Tr::tr("Show inline annotations for %1")
: Tr::tr("Temporarily hide inline annotations for %1"))
.arg(m_category.displayName);
@@ -298,7 +302,7 @@ void TextMark::addToToolTipLayout(QGridLayout *target) const
}
if (m_settingsPage.isValid()) {
auto settingsAction = new QAction;
- settingsAction->setIcon(Utils::Icons::SETTINGS_TOOLBAR.icon());
+ settingsAction->setIcon(Utils::Icons::SETTINGS.icon());
settingsAction->setToolTip(Tr::tr("Show Diagnostic Settings"));
QObject::connect(settingsAction, &QAction::triggered, Core::ICore::instance(),
[id = m_settingsPage] { Core::ICore::showOptionsDialog(id); },
@@ -338,11 +342,18 @@ bool TextMark::addToolTipContent(QLayout *target) const
}
auto textLabel = new QLabel;
- textLabel->setOpenExternalLinks(true);
textLabel->setText(text);
// Differentiate between tool tips that where explicitly set and default tool tips.
textLabel->setDisabled(useDefaultToolTip);
target->addWidget(textLabel);
+ QObject::connect(textLabel, &QLabel::linkActivated, [](const QString &link) {
+ if (OutputLineParser::isLinkTarget(link)) {
+ Core::EditorManager::openEditorAt(OutputLineParser::parseLinkTarget(link), {},
+ Core::EditorManager::SwitchSplitIfAlreadyVisible);
+ } else {
+ QDesktopServices::openUrl(link);
+ }
+ });
return true;
}
diff --git a/src/plugins/texteditor/textmark.h b/src/plugins/texteditor/textmark.h
index fb000da1a4..b6fb4731cb 100644
--- a/src/plugins/texteditor/textmark.h
+++ b/src/plugins/texteditor/textmark.h
@@ -118,12 +118,15 @@ public:
bool isLocationMarker() const;
void setIsLocationMarker(bool newIsLocationMarker);
+
protected:
void setSettingsPage(Utils::Id settingsPage);
private:
Q_DISABLE_COPY(TextMark)
+ void setDeleteCallback(const std::function<void()> &callback) { m_deleteCallback = callback; };
+
TextDocument *m_baseTextDocument = nullptr;
Utils::FilePath m_fileName;
int m_lineNumber = 0;
@@ -141,6 +144,9 @@ private:
QVector<QAction *> m_actions; // FIXME Remove in master
std::function<QList<QAction *>()> m_actionsProvider;
Utils::Id m_settingsPage;
+ std::function<void()> m_deleteCallback;
+
+ friend class TextDocumentLayout;
};
} // namespace TextEditor
diff --git a/src/plugins/todo/keyworddialog.cpp b/src/plugins/todo/keyworddialog.cpp
index 0f92091a1f..cebeb7b692 100644
--- a/src/plugins/todo/keyworddialog.cpp
+++ b/src/plugins/todo/keyworddialog.cpp
@@ -47,7 +47,7 @@ KeywordDialog::KeywordDialog(const Keyword &keyword, const QSet<QString> &alread
m_buttonBox->setOrientation(Qt::Horizontal);
m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
new QLabel(Tr::tr("Icon")),
diff --git a/src/plugins/todo/optionsdialog.cpp b/src/plugins/todo/optionsdialog.cpp
index f536ca51ed..3b300a7d10 100644
--- a/src/plugins/todo/optionsdialog.cpp
+++ b/src/plugins/todo/optionsdialog.cpp
@@ -73,7 +73,7 @@ OptionsDialog::OptionsDialog(Settings *settings, const std::function<void ()> &o
m_scanInSubprojectRadioButton = new QRadioButton(Tr::tr("Scan the current subproject"));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
diff --git a/src/plugins/todo/todoitemsprovider.cpp b/src/plugins/todo/todoitemsprovider.cpp
index 02790c64df..02ba71e2fc 100644
--- a/src/plugins/todo/todoitemsprovider.cpp
+++ b/src/plugins/todo/todoitemsprovider.cpp
@@ -3,6 +3,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "todoitemsprovider.h"
+
#include "constants.h"
#include "cpptodoitemsscanner.h"
#include "qmljstodoitemsscanner.h"
@@ -13,9 +14,9 @@
#include <coreplugin/idocument.h>
#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
#include <utils/algorithm.h>
@@ -182,8 +183,8 @@ void TodoItemsProvider::updateListTimeoutElapsed()
void TodoItemsProvider::setupStartupProjectBinding()
{
- m_startupProject = SessionManager::startupProject();
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ m_startupProject = ProjectManager::startupProject();
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, &TodoItemsProvider::startupProjectChanged);
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::fileListChanged,
this, &TodoItemsProvider::projectsFilesChanged);
diff --git a/src/plugins/todo/todoprojectsettingswidget.cpp b/src/plugins/todo/todoprojectsettingswidget.cpp
index 0d0e0d732a..6f68ccc37b 100644
--- a/src/plugins/todo/todoprojectsettingswidget.cpp
+++ b/src/plugins/todo/todoprojectsettingswidget.cpp
@@ -32,7 +32,7 @@ TodoProjectSettingsWidget::TodoProjectSettingsWidget(ProjectExplorer::Project *p
auto addExcludedPatternButton = new QPushButton(Tr::tr("Add"));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
diff --git a/src/plugins/updateinfo/settingspage.cpp b/src/plugins/updateinfo/settingspage.cpp
index 587102bce7..18d070092e 100644
--- a/src/plugins/updateinfo/settingspage.cpp
+++ b/src/plugins/updateinfo/settingspage.cpp
@@ -45,7 +45,7 @@ public:
m_nextCheckDateLabel = new QLabel;
m_checkForNewQtVersions = new QCheckBox(Tr::tr("Check for new Qt versions"));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_infoLabel,
diff --git a/src/plugins/updateinfo/settingspage.h b/src/plugins/updateinfo/settingspage.h
index 3ed45a71c6..6da738a9a0 100644
--- a/src/plugins/updateinfo/settingspage.h
+++ b/src/plugins/updateinfo/settingspage.h
@@ -5,18 +5,14 @@
#include <coreplugin/dialogs/ioptionspage.h>
-namespace UpdateInfo {
-namespace Internal {
+namespace UpdateInfo::Internal {
class UpdateInfoPlugin;
class SettingsPage : public Core::IOptionsPage
{
- Q_OBJECT
-
public:
explicit SettingsPage(UpdateInfoPlugin *plugin);
};
-} // namespace Internal
-} // namespace UpdateInfo
+} // UpdateInfo::Internal
diff --git a/src/plugins/updateinfo/updateinfoplugin.cpp b/src/plugins/updateinfo/updateinfoplugin.cpp
index decce3a47a..749cbbbec6 100644
--- a/src/plugins/updateinfo/updateinfoplugin.cpp
+++ b/src/plugins/updateinfo/updateinfoplugin.cpp
@@ -13,8 +13,8 @@
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/taskprogress.h>
#include <utils/infobar.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDate>
#include <QLabel>
@@ -43,6 +43,7 @@ const char InstallQtUpdates[] = "UpdateInfo.InstallQtUpdates";
const char M_MAINTENANCE_TOOL[] = "QtCreator.Menu.Tools.MaintenanceTool";
using namespace Core;
+using namespace Tasking;
using namespace Utils;
namespace UpdateInfo {
@@ -119,7 +120,7 @@ void UpdateInfoPlugin::startCheckForUpdates()
using namespace Tasking;
- const auto doSetup = [this](QtcProcess &process, const QStringList &args) {
+ const auto doSetup = [this](Process &process, const QStringList &args) {
process.setCommand({d->m_maintenanceTool, args});
};
const auto doCleanup = [this] {
@@ -127,22 +128,22 @@ void UpdateInfoPlugin::startCheckForUpdates()
checkForUpdatesStopped();
};
- const auto setupUpdate = [doSetup](QtcProcess &process) {
+ const auto setupUpdate = [doSetup](Process &process) {
doSetup(process, {"ch", "-g", "*=false,ifw.package.*=true"});
};
- const auto updateDone = [this](const QtcProcess &process) {
+ const auto updateDone = [this](const Process &process) {
d->m_updateOutput = process.cleanedStdOut();
};
- QList<TaskItem> tasks { Process(setupUpdate, updateDone) };
+ QList<TaskItem> tasks { ProcessTask(setupUpdate, updateDone) };
if (d->m_settings.checkForQtVersions) {
- const auto setupPackages = [doSetup](QtcProcess &process) {
+ const auto setupPackages = [doSetup](Process &process) {
doSetup(process, {"se", "qt[.]qt[0-9][.][0-9]+$", "-g", "*=false,ifw.package.*=true"});
};
- const auto packagesDone = [this](const QtcProcess &process) {
+ const auto packagesDone = [this](const Process &process) {
d->m_packagesOutput = process.cleanedStdOut();
};
- tasks << Process(setupPackages, packagesDone);
+ tasks << ProcessTask(setupPackages, packagesDone);
}
d->m_taskTree.reset(new TaskTree(Group{tasks}));
@@ -461,7 +462,7 @@ QDate UpdateInfoPlugin::nextCheckDate(CheckUpdateInterval interval) const
void UpdateInfoPlugin::startMaintenanceTool(const QStringList &args) const
{
- QtcProcess::startDetached(CommandLine{d->m_maintenanceTool, args});
+ Process::startDetached(CommandLine{d->m_maintenanceTool, args});
}
void UpdateInfoPlugin::startUpdater() const
diff --git a/src/plugins/valgrind/callgrindengine.cpp b/src/plugins/valgrind/callgrindengine.cpp
index d70c667df6..92b13b4f77 100644
--- a/src/plugins/valgrind/callgrindengine.cpp
+++ b/src/plugins/valgrind/callgrindengine.cpp
@@ -12,8 +12,9 @@
#include <debugger/analyzer/analyzermanager.h>
#include <utils/filepath.h>
+#include <utils/filestreamermanager.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/temporaryfile.h>
#include <QDebug>
@@ -60,34 +61,32 @@ CallgrindToolRunner::~CallgrindToolRunner()
cleanupTempFile();
}
-QStringList CallgrindToolRunner::toolArguments() const
+void CallgrindToolRunner::addToolArguments(CommandLine &cmd) const
{
- QStringList arguments = {"--tool=callgrind"};
+ cmd << "--tool=callgrind";
- if (m_settings.enableCacheSim.value())
- arguments << "--cache-sim=yes";
+ if (m_settings.enableCacheSim())
+ cmd << "--cache-sim=yes";
- if (m_settings.enableBranchSim.value())
- arguments << "--branch-sim=yes";
+ if (m_settings.enableBranchSim())
+ cmd << "--branch-sim=yes";
- if (m_settings.collectBusEvents.value())
- arguments << "--collect-bus=yes";
+ if (m_settings.collectBusEvents())
+ cmd << "--collect-bus=yes";
- if (m_settings.collectSystime.value())
- arguments << "--collect-systime=yes";
+ if (m_settings.collectSystime())
+ cmd << "--collect-systime=yes";
if (m_markAsPaused)
- arguments << "--instr-atstart=no";
+ cmd << "--instr-atstart=no";
// add extra arguments
if (!m_argumentForToggleCollect.isEmpty())
- arguments << m_argumentForToggleCollect;
+ cmd << m_argumentForToggleCollect;
- arguments << "--callgrind-out-file=" + m_valgrindOutputFile.path();
+ cmd << "--callgrind-out-file=" + m_valgrindOutputFile.path();
- arguments << ProcessArgs::splitArgs(m_settings.callgrindArguments.value());
-
- return arguments;
+ cmd.addArgs(m_settings.callgrindArguments(), CommandLine::Raw);
}
QString CallgrindToolRunner::progressTitle() const
@@ -174,7 +173,7 @@ void CallgrindToolRunner::run(Option option)
// save back current running operation
m_lastOption = option;
- m_controllerProcess.reset(new QtcProcess);
+ m_controllerProcess.reset(new Process);
switch (option) {
case CallgrindToolRunner::Dump:
@@ -196,11 +195,11 @@ void CallgrindToolRunner::run(Option option)
#if CALLGRIND_CONTROL_DEBUG
m_controllerProcess->setProcessChannelMode(QProcess::ForwardedChannels);
#endif
- connect(m_controllerProcess.get(), &QtcProcess::done,
+ connect(m_controllerProcess.get(), &Process::done,
this, &CallgrindToolRunner::controllerProcessDone);
const FilePath control =
- FilePath(CALLGRIND_CONTROL_BINARY).onDevice(m_valgrindRunnable.command.executable());
+ m_valgrindRunnable.command.executable().withNewPath(CALLGRIND_CONTROL_BINARY);
m_controllerProcess->setCommand({control, {toOptionString(option), QString::number(m_pid)}});
m_controllerProcess->setWorkingDirectory(m_valgrindRunnable.workingDirectory);
m_controllerProcess->setEnvironment(m_valgrindRunnable.environment);
@@ -257,11 +256,14 @@ void CallgrindToolRunner::triggerParse()
}
const auto afterCopy = [this](expected_str<void> res) {
- QTC_ASSERT_EXPECTED(res, return);
+ if (!res) // failed to run callgrind
+ return;
showStatusMessage(Tr::tr("Parsing Profile Data..."));
m_parser.parse(m_hostOutputFile);
};
- m_valgrindOutputFile.asyncCopyFile(afterCopy, m_hostOutputFile);
+ // TODO: Store the handle and cancel on CallgrindToolRunner destructor?
+ // TODO: Should d'tor of context object cancel the running task?
+ FileStreamerManager::copy(m_valgrindOutputFile, m_hostOutputFile, this, afterCopy);
}
void CallgrindToolRunner::cleanupTempFile()
diff --git a/src/plugins/valgrind/callgrindengine.h b/src/plugins/valgrind/callgrindengine.h
index 2f22280ffa..d5d26e113a 100644
--- a/src/plugins/valgrind/callgrindengine.h
+++ b/src/plugins/valgrind/callgrindengine.h
@@ -8,7 +8,7 @@
#include "callgrind/callgrindparsedata.h"
#include "callgrind/callgrindparser.h"
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
namespace Valgrind {
namespace Internal {
@@ -48,7 +48,7 @@ public:
Q_ENUM(Option)
protected:
- QStringList toolArguments() const override;
+ void addToolArguments(Utils::CommandLine &cmd) const override;
QString progressTitle() const override;
signals:
@@ -73,7 +73,7 @@ private:
bool m_markAsPaused = false;
- std::unique_ptr<Utils::QtcProcess> m_controllerProcess;
+ std::unique_ptr<Utils::Process> m_controllerProcess;
ProjectExplorer::Runnable m_valgrindRunnable;
qint64 m_pid = 0;
diff --git a/src/plugins/valgrind/callgrindtool.cpp b/src/plugins/valgrind/callgrindtool.cpp
index fc50b2ecf0..ab5b0e5560 100644
--- a/src/plugins/valgrind/callgrindtool.cpp
+++ b/src/plugins/valgrind/callgrindtool.cpp
@@ -47,13 +47,13 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorericons.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/taskhub.h>
#include <utils/fancymainwindow.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/styledbar.h>
#include <utils/utilsicons.h>
@@ -256,7 +256,7 @@ CallgrindToolPrivate::CallgrindToolPrivate()
menu->addAction(ActionManager::registerAction(action, CallgrindRemoteActionId),
Debugger::Constants::G_ANALYZER_REMOTE_TOOLS);
QObject::connect(action, &QAction::triggered, this, [this, action] {
- auto runConfig = SessionManager::startupRunConfiguration();
+ auto runConfig = ProjectManager::startupRunConfiguration();
if (!runConfig) {
showCannotStartDialog(action->text());
return;
@@ -361,7 +361,7 @@ CallgrindToolPrivate::CallgrindToolPrivate()
action->setIcon(kCachegrindIcon.icon());
action->setToolTip(Tr::tr("Open results in KCachegrind."));
connect(action, &QAction::triggered, this, [this, settings] {
- QtcProcess::startDetached({FilePath::fromString(settings->kcachegrindExecutable.value()), { m_lastFileName }});
+ Process::startDetached({FilePath::fromString(settings->kcachegrindExecutable.value()), { m_lastFileName }});
});
// dump action
diff --git a/src/plugins/valgrind/memcheckerrorview.cpp b/src/plugins/valgrind/memcheckerrorview.cpp
index 3bb8a66a02..48e9afd801 100644
--- a/src/plugins/valgrind/memcheckerrorview.cpp
+++ b/src/plugins/valgrind/memcheckerrorview.cpp
@@ -14,7 +14,7 @@
#include <coreplugin/editormanager/editormanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/qtcassert.h>
#include <utils/icon.h>
diff --git a/src/plugins/valgrind/memchecktool.cpp b/src/plugins/valgrind/memchecktool.cpp
index d4a5e4a649..e2921603e9 100644
--- a/src/plugins/valgrind/memchecktool.cpp
+++ b/src/plugins/valgrind/memchecktool.cpp
@@ -7,7 +7,6 @@
#include "valgrindengine.h"
#include "valgrindrunner.h"
#include "valgrindsettings.h"
-#include "valgrindsettings.h"
#include "valgrindtr.h"
#include "xmlprotocol/error.h"
@@ -30,8 +29,8 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/runconfiguration.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
#include <projectexplorer/toolchain.h>
@@ -50,8 +49,9 @@
#include <utils/checkablemessagebox.h>
#include <utils/fancymainwindow.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QAction>
@@ -111,7 +111,7 @@ signals:
private:
QString progressTitle() const override;
- QStringList toolArguments() const override;
+ void addToolArguments(CommandLine &cmd) const override;
void startDebugger(qint64 valgrindPid);
void appendLog(const QByteArray &data);
@@ -129,9 +129,9 @@ public:
void start() override
{
QTC_ASSERT(!m_process, return);
- m_process.reset(new QtcProcess);
+ m_process.reset(new Process);
m_process->setCommand({device()->filePath("echo"), "-n $SSH_CLIENT", CommandLine::Raw});
- connect(m_process.get(), &QtcProcess::done, this, [this] {
+ connect(m_process.get(), &Process::done, this, [this] {
if (m_process->error() != QProcess::UnknownError) {
reportFailure();
return;
@@ -159,7 +159,7 @@ public:
}
private:
- std::unique_ptr<QtcProcess> m_process = nullptr;
+ std::unique_ptr<Process> m_process = nullptr;
QHostAddress *m_localServerAddress = nullptr;
};
@@ -181,18 +181,18 @@ void MemcheckToolRunner::stop()
ValgrindToolRunner::stop();
}
-QStringList MemcheckToolRunner::toolArguments() const
+void MemcheckToolRunner::addToolArguments(CommandLine &cmd) const
{
- QStringList arguments = {"--tool=memcheck", "--gen-suppressions=all"};
+ cmd << "--tool=memcheck" << "--gen-suppressions=all";
- if (m_settings.trackOrigins.value())
- arguments << "--track-origins=yes";
+ if (m_settings.trackOrigins())
+ cmd << "--track-origins=yes";
- if (m_settings.showReachable.value())
- arguments << "--show-reachable=yes";
+ if (m_settings.showReachable())
+ cmd << "--show-reachable=yes";
QString leakCheckValue;
- switch (m_settings.leakCheckOnFinish.value()) {
+ switch (m_settings.leakCheckOnFinish()) {
case ValgrindBaseSettings::LeakCheckOnFinishNo:
leakCheckValue = "no";
break;
@@ -204,24 +204,22 @@ QStringList MemcheckToolRunner::toolArguments() const
leakCheckValue = "summary";
break;
}
- arguments << "--leak-check=" + leakCheckValue;
+ cmd << "--leak-check=" + leakCheckValue;
- for (const FilePath &file : m_settings.suppressions.value())
- arguments << QString("--suppressions=%1").arg(file.path());
+ for (const FilePath &file : m_settings.suppressions())
+ cmd << QString("--suppressions=%1").arg(file.path());
- arguments << QString("--num-callers=%1").arg(m_settings.numCallers.value());
+ cmd << QString("--num-callers=%1").arg(m_settings.numCallers());
if (m_withGdb)
- arguments << "--vgdb=yes" << "--vgdb-error=0";
-
- arguments << Utils::ProcessArgs::splitArgs(m_settings.memcheckArguments.value());
+ cmd << "--vgdb=yes" << "--vgdb-error=0";
- return arguments;
+ cmd.addArgs(m_settings.memcheckArguments(), CommandLine::Raw);
}
const FilePaths MemcheckToolRunner::suppressionFiles() const
{
- return m_settings.suppressions.value();
+ return m_settings.suppressions();
}
void MemcheckToolRunner::startDebugger(qint64 valgrindPid)
@@ -337,7 +335,7 @@ bool MemcheckErrorFilterProxyModel::filterAcceptsRow(int sourceRow, const QModel
// ALGORITHM: look at last five stack frames, if none of these is inside any open projects,
// assume this error was created by an external library
QSet<QString> validFolders;
- for (Project *project : SessionManager::projects()) {
+ for (Project *project : ProjectManager::projects()) {
validFolders << project->projectDirectory().toString();
const QList<Target *> targets = project->targets();
for (const Target *target : targets) {
@@ -609,7 +607,7 @@ MemcheckToolPrivate::MemcheckToolPrivate()
filterButton->setIcon(Icons::FILTER.icon());
filterButton->setText(Tr::tr("Error Filter"));
filterButton->setPopupMode(QToolButton::InstantPopup);
- filterButton->setProperty("noArrow", true);
+ filterButton->setProperty(StyleHelper::C_NO_ARROW, true);
m_filterMenu = new QMenu(filterButton);
for (QAction *filterAction : std::as_const(m_errorFilterActions))
@@ -676,7 +674,7 @@ MemcheckToolPrivate::MemcheckToolPrivate()
menu->addAction(ActionManager::registerAction(action, "Memcheck.Remote"),
Debugger::Constants::G_ANALYZER_REMOTE_TOOLS);
QObject::connect(action, &QAction::triggered, this, [this, action] {
- RunConfiguration *runConfig = SessionManager::startupRunConfiguration();
+ RunConfiguration *runConfig = ProjectManager::startupRunConfiguration();
if (!runConfig) {
showCannotStartDialog(action->text());
return;
@@ -718,7 +716,7 @@ void MemcheckToolPrivate::heobAction()
Abi abi;
bool hasLocalRc = false;
Kit *kit = nullptr;
- if (Target *target = SessionManager::startupTarget()) {
+ if (Target *target = ProjectManager::startupTarget()) {
if (RunConfiguration *rc = target->activeRunConfiguration()) {
kit = target->kit();
if (kit) {
@@ -800,19 +798,18 @@ void MemcheckToolPrivate::heobAction()
const QString dwarfstack = QString("dwarfstack%1.dll").arg(abi.wordWidth());
const QString dwarfstackPath = dialog.path() + '/' + dwarfstack;
if (!QFile::exists(dwarfstackPath)
- && CheckableMessageBox::doNotShowAgainInformation(
+ && CheckableMessageBox::information(
Core::ICore::dialogParent(),
Tr::tr("Heob"),
Tr::tr("Heob used with MinGW projects needs the %1 DLLs for proper "
- "stacktrace resolution.")
+ "stacktrace resolution.")
.arg(
"<a "
"href=\"https://github.com/ssbssa/dwarfstack/releases\">Dwarfstack</a>"),
- ICore::settings(),
- "HeobDwarfstackInfo",
- QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
- QDialogButtonBox::Ok)
- != QDialogButtonBox::Ok)
+ QString("HeobDwarfstackInfo"),
+ QMessageBox::Ok | QMessageBox::Cancel,
+ QMessageBox::Ok)
+ != QMessageBox::Ok)
return;
}
@@ -917,22 +914,22 @@ void MemcheckToolPrivate::updateFromSettings()
for (const QVariant &v : actions) {
bool ok;
int kind = v.toInt(&ok);
- if (ok && !m_settings->visibleErrorKinds.value().contains(kind))
+ if (ok && !m_settings->visibleErrorKinds().contains(kind))
contained = false;
}
action->setChecked(contained);
}
- m_filterProjectAction->setChecked(!m_settings->filterExternalIssues.value());
+ m_filterProjectAction->setChecked(!m_settings->filterExternalIssues());
m_errorView->settingsChanged(m_settings);
connect(&m_settings->visibleErrorKinds, &IntegersAspect::valueChanged,
&m_errorProxyModel, &MemcheckErrorFilterProxyModel::setAcceptedKinds);
- m_errorProxyModel.setAcceptedKinds(m_settings->visibleErrorKinds.value());
+ m_errorProxyModel.setAcceptedKinds(m_settings->visibleErrorKinds());
connect(&m_settings->filterExternalIssues, &BoolAspect::valueChanged,
&m_errorProxyModel, &MemcheckErrorFilterProxyModel::setFilterExternalIssues);
- m_errorProxyModel.setFilterExternalIssues(m_settings->filterExternalIssues.value());
+ m_errorProxyModel.setFilterExternalIssues(m_settings->filterExternalIssues());
}
void MemcheckToolPrivate::maybeActiveRunConfigurationChanged()
@@ -940,7 +937,7 @@ void MemcheckToolPrivate::maybeActiveRunConfigurationChanged()
updateRunActions();
ValgrindBaseSettings *settings = nullptr;
- if (Project *project = SessionManager::startupProject())
+ if (Project *project = ProjectManager::startupProject())
if (Target *target = project->activeTarget())
if (RunConfiguration *rc = target->activeRunConfiguration())
settings = rc->currentSettings<ValgrindBaseSettings>(ANALYZER_VALGRIND_SETTINGS);
diff --git a/src/plugins/valgrind/suppressiondialog.cpp b/src/plugins/valgrind/suppressiondialog.cpp
index 08ed8ae5ff..6273580e82 100644
--- a/src/plugins/valgrind/suppressiondialog.cpp
+++ b/src/plugins/valgrind/suppressiondialog.cpp
@@ -12,9 +12,9 @@
#include "xmlprotocol/stack.h"
#include "xmlprotocol/frame.h"
-#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/project.h>
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <utils/algorithm.h>
@@ -182,8 +182,8 @@ void SuppressionDialog::accept()
return;
// Add file to project if there is a project containing this file on the file system.
- if (!ProjectExplorer::SessionManager::projectForFile(path)) {
- for (ProjectExplorer::Project *p : ProjectExplorer::SessionManager::projects()) {
+ if (!ProjectExplorer::ProjectManager::projectForFile(path)) {
+ for (ProjectExplorer::Project *p : ProjectExplorer::ProjectManager::projects()) {
if (path.startsWith(p->projectDirectory().toString())) {
p->rootProjectNode()->addFiles({path});
break;
diff --git a/src/plugins/valgrind/valgrind.qbs b/src/plugins/valgrind/valgrind.qbs
index f0e117ce98..973b1d7b85 100644
--- a/src/plugins/valgrind/valgrind.qbs
+++ b/src/plugins/valgrind/valgrind.qbs
@@ -76,9 +76,7 @@ QtcPlugin {
]
}
- Group {
- name: "Test sources"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
"valgrindmemcheckparsertest.cpp",
"valgrindmemcheckparsertest.h",
diff --git a/src/plugins/valgrind/valgrindengine.cpp b/src/plugins/valgrind/valgrindengine.cpp
index 58919333f1..a228eb0e09 100644
--- a/src/plugins/valgrind/valgrindengine.cpp
+++ b/src/plugins/valgrind/valgrindengine.cpp
@@ -22,8 +22,6 @@
#include <QApplication>
-#define VALGRIND_DEBUG_OUTPUT 0
-
using namespace Debugger;
using namespace Core;
using namespace Utils;
@@ -39,8 +37,10 @@ ValgrindToolRunner::ValgrindToolRunner(RunControl *runControl)
m_settings.fromMap(runControl->settingsData(ANALYZER_VALGRIND_SETTINGS));
- connect(&m_runner, &ValgrindRunner::appendMessage,
- this, &ValgrindToolRunner::appendMessage);
+ connect(&m_runner,
+ &ValgrindRunner::appendMessage,
+ this,
+ [this](const QString &msg, Utils::OutputFormat format) { appendMessage(msg, format); });
connect(&m_runner, &ValgrindRunner::valgrindExecuted,
this, [this](const QString &commandLine) {
appendMessage(commandLine, NormalMessageFormat);
@@ -53,6 +53,19 @@ ValgrindToolRunner::ValgrindToolRunner(RunControl *runControl)
void ValgrindToolRunner::start()
{
+ FilePath valgrindExecutable = m_settings.valgrindExecutable();
+ if (IDevice::ConstPtr dev = DeviceKitAspect::device(runControl()->kit()))
+ valgrindExecutable = dev->filePath(valgrindExecutable.path());
+
+ const FilePath found = valgrindExecutable.searchInPath();
+
+ if (!found.isExecutableFile()) {
+ reportFailure(Tr::tr("Valgrind executable \"%1\" not found or not executable.\n"
+ "Check settings or ensure valgrind is installed and available in PATH.")
+ .arg(valgrindExecutable.toUserOutput()));
+ return;
+ }
+
FutureProgress *fp = ProgressManager::addTimedTask(m_progress, progressTitle(), "valgrind", 100);
connect(fp, &FutureProgress::canceled,
this, &ValgrindToolRunner::handleProgressCanceled);
@@ -60,21 +73,10 @@ void ValgrindToolRunner::start()
this, &ValgrindToolRunner::handleProgressFinished);
m_progress.reportStarted();
-#if VALGRIND_DEBUG_OUTPUT
- emit outputReceived(Tr::tr("Valgrind options: %1").arg(toolArguments().join(' ')), LogMessageFormat);
- emit outputReceived(Tr::tr("Working directory: %1").arg(runnable().workingDirectory), LogMessageFormat);
- emit outputReceived(Tr::tr("Command line arguments: %1").arg(runnable().debuggeeArgs), LogMessageFormat);
-#endif
-
-
- FilePath valgrindExecutable = m_settings.valgrindExecutable.filePath();
- if (IDevice::ConstPtr dev = DeviceKitAspect::device(runControl()->kit()))
- valgrindExecutable = dev->filePath(valgrindExecutable.path());
-
CommandLine valgrind{valgrindExecutable};
- valgrind.addArgs(m_settings.valgrindArguments.value(), CommandLine::Raw);
+ valgrind.addArgs(m_settings.valgrindArguments(), CommandLine::Raw);
valgrind.addArgs(genericToolArguments());
- valgrind.addArgs(toolArguments());
+ addToolArguments(valgrind);
m_runner.setValgrindCommand(valgrind);
m_runner.setDebuggee(runControl()->runnable());
diff --git a/src/plugins/valgrind/valgrindengine.h b/src/plugins/valgrind/valgrindengine.h
index e5894b1783..d491c7a847 100644
--- a/src/plugins/valgrind/valgrindengine.h
+++ b/src/plugins/valgrind/valgrindengine.h
@@ -6,7 +6,6 @@
#include "valgrindsettings.h"
#include <projectexplorer/runcontrol.h>
-#include <utils/environment.h>
#include <valgrind/valgrindrunner.h>
#include <QFutureInterface>
@@ -23,7 +22,7 @@ public:
protected:
virtual QString progressTitle() const = 0;
- virtual QStringList toolArguments() const = 0;
+ virtual void addToolArguments(Utils::CommandLine &cmd) const = 0;
ValgrindProjectSettings m_settings;
QFutureInterface<void> m_progress;
diff --git a/src/plugins/valgrind/valgrindrunner.cpp b/src/plugins/valgrind/valgrindrunner.cpp
index ef84fba675..fd26544422 100644
--- a/src/plugins/valgrind/valgrindrunner.cpp
+++ b/src/plugins/valgrind/valgrindrunner.cpp
@@ -9,8 +9,8 @@
#include <projectexplorer/runcontrol.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QEventLoop>
#include <QTcpServer>
@@ -25,18 +25,18 @@ class ValgrindRunner::Private : public QObject
{
public:
Private(ValgrindRunner *owner) : q(owner) {
- connect(&m_process, &QtcProcess::started, this, [this] {
+ connect(&m_process, &Process::started, this, [this] {
emit q->valgrindStarted(m_process.processId());
});
- connect(&m_process, &QtcProcess::done, this, [this] {
+ connect(&m_process, &Process::done, this, [this] {
if (m_process.result() != ProcessResult::FinishedWithSuccess)
emit q->processErrorReceived(m_process.errorString(), m_process.error());
emit q->finished();
});
- connect(&m_process, &QtcProcess::readyReadStandardOutput, this, [this] {
+ connect(&m_process, &Process::readyReadStandardOutput, this, [this] {
emit q->appendMessage(m_process.readAllStandardOutput(), StdOutFormat);
});
- connect(&m_process, &QtcProcess::readyReadStandardError, this, [this] {
+ connect(&m_process, &Process::readyReadStandardError, this, [this] {
emit q->appendMessage(m_process.readAllStandardError(), StdErrFormat);
});
@@ -53,7 +53,7 @@ public:
Runnable m_debuggee;
CommandLine m_command;
- QtcProcess m_process;
+ Process m_process;
QHostAddress m_localServerAddress;
@@ -197,7 +197,7 @@ void ValgrindRunner::setLocalServerAddress(const QHostAddress &localServerAddres
void ValgrindRunner::setUseTerminal(bool on)
{
- d->m_process.setTerminalMode(on ? TerminalMode::On : TerminalMode::Off);
+ d->m_process.setTerminalMode(on ? TerminalMode::Run : TerminalMode::Off);
}
void ValgrindRunner::waitForFinished() const
diff --git a/src/plugins/valgrind/valgrindsettings.cpp b/src/plugins/valgrind/valgrindsettings.cpp
index fe546ea0cc..5be58ea3b8 100644
--- a/src/plugins/valgrind/valgrindsettings.cpp
+++ b/src/plugins/valgrind/valgrindsettings.cpp
@@ -107,7 +107,8 @@ void SuppressionAspectPrivate::slotSuppressionSelectionChanged()
// SuppressionAspect
//
-SuppressionAspect::SuppressionAspect(bool global)
+SuppressionAspect::SuppressionAspect(AspectContainer *container, bool global)
+ : BaseAspect(container)
{
d = new SuppressionAspectPrivate(this, global);
setSettingsKey("Analyzer.Valgrind.SuppressionFiles");
@@ -128,7 +129,7 @@ void SuppressionAspect::setValue(const FilePaths &val)
BaseAspect::setValue(Utils::transform<QStringList>(val, &FilePath::toString));
}
-void SuppressionAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void SuppressionAspect::addToLayout(Layouting::LayoutItem &parent)
{
QTC_CHECK(!d->addEntry);
QTC_CHECK(!d->removeEntry);
@@ -150,12 +151,12 @@ void SuppressionAspect::addToLayout(Layouting::LayoutBuilder &builder)
connect(d->entryList->selectionModel(), &QItemSelectionModel::selectionChanged,
d, &SuppressionAspectPrivate::slotSuppressionSelectionChanged);
- builder.addItem(Column { Tr::tr("Suppression files:"), st });
+ parent.addItem(Column { Tr::tr("Suppression files:"), st });
Row group {
d->entryList.data(),
Column { d->addEntry.data(), d->removeEntry.data(), st }
};
- builder.addItem(Span { 2, group });
+ parent.addItem(Span { 2, group });
setVolatileValue(BaseAspect::value());
}
@@ -195,19 +196,15 @@ void SuppressionAspect::setVolatileValue(const QVariant &val)
//////////////////////////////////////////////////////////////////
ValgrindBaseSettings::ValgrindBaseSettings(bool global)
- : suppressions(global)
+ : suppressions(this, global)
{
// Note that this is used twice, once for project settings in the .user files
// and once for global settings in QtCreator.ini. This uses intentionally
// the same key to facilitate copying using fromMap/toMap.
QString base = "Analyzer.Valgrind.";
- registerAspect(&suppressions);
-
- registerAspect(&valgrindExecutable);
valgrindExecutable.setSettingsKey(base + "ValgrindExecutable");
valgrindExecutable.setDefaultValue("valgrind");
- valgrindExecutable.setDisplayStyle(StringAspect::PathChooserDisplay);
valgrindExecutable.setExpectedKind(PathChooser::Command);
valgrindExecutable.setHistoryCompleter("Valgrind.Command.History");
valgrindExecutable.setDisplayName(Tr::tr("Valgrind Command"));
@@ -220,12 +217,10 @@ ValgrindBaseSettings::ValgrindBaseSettings(bool global)
//valgrindExecutable. ... buttonAtIndex(0)->hide();
}
- registerAspect(&valgrindArguments);
valgrindArguments.setSettingsKey(base + "ValgrindArguments");
valgrindArguments.setDisplayStyle(StringAspect::LineEditDisplay);
valgrindArguments.setLabelText(Tr::tr("Valgrind arguments:"));
- registerAspect(&selfModifyingCodeDetection);
selfModifyingCodeDetection.setSettingsKey(base + "SelfModifyingCodeDetection");
selfModifyingCodeDetection.setDefaultValue(DetectSmcStackOnly);
selfModifyingCodeDetection.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
@@ -236,31 +231,26 @@ ValgrindBaseSettings::ValgrindBaseSettings(bool global)
selfModifyingCodeDetection.setLabelText(Tr::tr("Detect self-modifying code:"));
// Memcheck
- registerAspect(&memcheckArguments);
memcheckArguments.setSettingsKey(base + "Memcheck.Arguments");
memcheckArguments.setDisplayStyle(StringAspect::LineEditDisplay);
memcheckArguments.setLabelText(Tr::tr("Extra MemCheck arguments:"));
- registerAspect(&filterExternalIssues);
filterExternalIssues.setSettingsKey(base + "FilterExternalIssues");
filterExternalIssues.setDefaultValue(true);
filterExternalIssues.setIcon(Icons::FILTER.icon());
- filterExternalIssues.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ filterExternalIssues.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox);
filterExternalIssues.setLabelText(Tr::tr("Show Project Costs Only"));
filterExternalIssues.setToolTip(Tr::tr("Show only profiling info that originated from this project source."));
- registerAspect(&trackOrigins);
trackOrigins.setSettingsKey(base + "TrackOrigins");
trackOrigins.setDefaultValue(true);
- trackOrigins.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ trackOrigins.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox);
trackOrigins.setLabelText(Tr::tr("Track origins of uninitialized memory"));
- registerAspect(&showReachable);
showReachable.setSettingsKey(base + "ShowReachable");
- showReachable.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ showReachable.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox);
showReachable.setLabelText(Tr::tr("Show reachable and indirectly lost blocks"));
- registerAspect(&leakCheckOnFinish);
leakCheckOnFinish.setSettingsKey(base + "LeakCheckOnFinish");
leakCheckOnFinish.setDefaultValue(LeakCheckOnFinishSummaryOnly);
leakCheckOnFinish.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
@@ -269,35 +259,29 @@ ValgrindBaseSettings::ValgrindBaseSettings(bool global)
leakCheckOnFinish.addOption(Tr::tr("Full"));
leakCheckOnFinish.setLabelText(Tr::tr("Check for leaks on finish:"));
- registerAspect(&numCallers);
numCallers.setSettingsKey(base + "NumCallers");
numCallers.setDefaultValue(25);
numCallers.setLabelText(Tr::tr("Backtrace frame count:"));
// Callgrind
- registerAspect(&kcachegrindExecutable);
kcachegrindExecutable.setSettingsKey(base + "KCachegrindExecutable");
kcachegrindExecutable.setDefaultValue("kcachegrind");
- kcachegrindExecutable.setDisplayStyle(StringAspect::PathChooserDisplay);
kcachegrindExecutable.setLabelText(Tr::tr("KCachegrind executable:"));
kcachegrindExecutable.setExpectedKind(Utils::PathChooser::Command);
kcachegrindExecutable.setDisplayName(Tr::tr("KCachegrind Command"));
- registerAspect(&callgrindArguments);
callgrindArguments.setSettingsKey(base + "Callgrind.Arguments");
callgrindArguments.setDisplayStyle(StringAspect::LineEditDisplay);
callgrindArguments.setLabelText(Tr::tr("Extra CallGrind arguments:"));
- registerAspect(&enableEventToolTips);
enableEventToolTips.setDefaultValue(true);
enableEventToolTips.setSettingsKey(base + "Callgrind.EnableEventToolTips");
- enableEventToolTips.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ enableEventToolTips.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox);
enableEventToolTips.setLabelText(Tr::tr("Show additional information for events in tooltips"));
- registerAspect(&enableCacheSim);
enableCacheSim.setSettingsKey(base + "Callgrind.EnableCacheSim");
- enableCacheSim.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ enableCacheSim.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox);
enableCacheSim.setLabelText(Tr::tr("Enable cache simulation"));
enableCacheSim.setToolTip("<html><head/><body>" + Tr::tr(
"<p>Does full cache simulation.</p>\n"
@@ -309,9 +293,8 @@ ValgrindBaseSettings::ValgrindBaseSettings(bool global)
"<li>Data write accesses (\"Dw\") and related cache misses (\"D1mw\"/\"D2mw\").</li></ul>\n"
"</p>") + "</body></html>");
- registerAspect(&enableBranchSim);
enableBranchSim.setSettingsKey(base + "Callgrind.EnableBranchSim");
- enableBranchSim.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ enableBranchSim.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox);
enableBranchSim.setLabelText(Tr::tr("Enable branch prediction simulation"));
enableBranchSim.setToolTip("<html><head/><body>\n" + Tr::tr(
"<p>Does branch prediction simulation.</p>\n"
@@ -321,20 +304,17 @@ ValgrindBaseSettings::ValgrindBaseSettings(bool global)
"<li>Executed indirect jumps and related misses of the jump address predictor (\n"
"\"Bi\"/\"Bim\").)</li></ul>") + "</body></html>");
- registerAspect(&collectSystime);
collectSystime.setSettingsKey(base + "Callgrind.CollectSystime");
- collectSystime.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ collectSystime.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox);
collectSystime.setLabelText(Tr::tr("Collect system call time"));
collectSystime.setToolTip(Tr::tr("Collects information for system call times."));
- registerAspect(&collectBusEvents);
- collectBusEvents.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ collectBusEvents.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox);
collectBusEvents.setSettingsKey(base + "Callgrind.CollectBusEvents");
collectBusEvents.setLabelText(Tr::tr("Collect global bus events"));
collectBusEvents.setToolTip(Tr::tr("Collect the number of global bus events that are executed. "
"The event type \"Ge\" is used for these events."));
- registerAspect(&minimumInclusiveCostRatio);
minimumInclusiveCostRatio.setSettingsKey(base + "Callgrind.MinimumCostRatio");
minimumInclusiveCostRatio.setDefaultValue(0.01);
minimumInclusiveCostRatio.setSuffix(Tr::tr("%"));
@@ -342,13 +322,11 @@ ValgrindBaseSettings::ValgrindBaseSettings(bool global)
minimumInclusiveCostRatio.setToolTip(Tr::tr("Limits the amount of results the profiler gives you. "
"A lower limit will likely increase performance."));
- registerAspect(&visualizationMinimumInclusiveCostRatio);
visualizationMinimumInclusiveCostRatio.setSettingsKey(base + "Callgrind.VisualisationMinimumCostRatio");
visualizationMinimumInclusiveCostRatio.setDefaultValue(10.0);
visualizationMinimumInclusiveCostRatio.setLabelText(Tr::tr("Visualization: Minimum event cost:"));
visualizationMinimumInclusiveCostRatio.setSuffix(Tr::tr("%"));
- registerAspect(&visibleErrorKinds);
visibleErrorKinds.setSettingsKey(base + "VisibleErrorKinds");
QList<int> defaultErrorKinds;
for (int i = 0; i < Valgrind::XmlProtocol::MemcheckErrorKindCount; ++i)
@@ -372,25 +350,20 @@ ValgrindGlobalSettings::ValgrindGlobalSettings()
const QString base = "Analyzer.Valgrind";
- registerAspect(&lastSuppressionDirectory);
lastSuppressionDirectory.setSettingsKey(base + "LastSuppressionDirectory");
- registerAspect(&lastSuppressionHistory);
lastSuppressionHistory.setSettingsKey(base + "LastSuppressionHistory");
- registerAspect(&detectCycles);
detectCycles.setSettingsKey(base + "Callgrind.CycleDetection");
detectCycles.setDefaultValue(true);
detectCycles.setLabelText("O"); // FIXME: Create a real icon
detectCycles.setToolTip(Tr::tr("Enable cycle detection to properly handle recursive "
"or circular function calls."));
- registerAspect(&costFormat);
costFormat.setSettingsKey(base + "Callgrind.CostFormat");
costFormat.setDefaultValue(CostDelegate::FormatRelative);
costFormat.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
- registerAspect(&shortenTemplates);
shortenTemplates.setSettingsKey(base + "Callgrind.ShortenTemplates");
shortenTemplates.setDefaultValue(true);
shortenTemplates.setLabelText("<>"); // FIXME: Create a real icon
diff --git a/src/plugins/valgrind/valgrindsettings.h b/src/plugins/valgrind/valgrindsettings.h
index 22952d0978..29506b848f 100644
--- a/src/plugins/valgrind/valgrindsettings.h
+++ b/src/plugins/valgrind/valgrindsettings.h
@@ -17,13 +17,14 @@ class SuppressionAspect final : public Utils::BaseAspect
Q_OBJECT
public:
- explicit SuppressionAspect(bool global);
+ SuppressionAspect(Utils::AspectContainer *container, bool global);
~SuppressionAspect() final;
+ Utils::FilePaths operator()() const { return value(); }
Utils::FilePaths value() const;
void setValue(const Utils::FilePaths &val);
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) final;
+ void addToLayout(Layouting::LayoutItem &parent) final;
void fromMap(const QVariantMap &map) final;
void toMap(QVariantMap &map) const final;
@@ -61,16 +62,13 @@ public:
LeakCheckOnFinishYes
};
-signals:
- void changed(); // sent when multiple values have changed simulatenously (e.g. fromMap)
-
/**
* Base valgrind settings
*/
public:
- Utils::StringAspect valgrindExecutable;
- Utils::StringAspect valgrindArguments;
- Utils::SelectionAspect selfModifyingCodeDetection;
+ Utils::FilePathAspect valgrindExecutable{this};
+ Utils::StringAspect valgrindArguments{this};
+ Utils::SelectionAspect selfModifyingCodeDetection{this};
SuppressionAspect suppressions;
@@ -78,13 +76,13 @@ public:
* Base memcheck settings
*/
public:
- Utils::StringAspect memcheckArguments;
- Utils::IntegerAspect numCallers;
- Utils::SelectionAspect leakCheckOnFinish;
- Utils::BoolAspect showReachable;
- Utils::BoolAspect trackOrigins;
- Utils::BoolAspect filterExternalIssues;
- Utils::IntegersAspect visibleErrorKinds;
+ Utils::StringAspect memcheckArguments{this};
+ Utils::IntegerAspect numCallers{this};
+ Utils::SelectionAspect leakCheckOnFinish{this};
+ Utils::BoolAspect showReachable{this};
+ Utils::BoolAspect trackOrigins{this};
+ Utils::BoolAspect filterExternalIssues{this};
+ Utils::IntegersAspect visibleErrorKinds{this};
void setVisibleErrorKinds(const QList<int> &);
@@ -92,16 +90,16 @@ public:
* Base callgrind settings
*/
public:
- Utils::StringAspect callgrindArguments;
- Utils::StringAspect kcachegrindExecutable;
+ Utils::StringAspect callgrindArguments{this};
+ Utils::FilePathAspect kcachegrindExecutable{this};
- Utils::BoolAspect enableCacheSim;
- Utils::BoolAspect enableBranchSim;
- Utils::BoolAspect collectSystime;
- Utils::BoolAspect collectBusEvents;
- Utils::BoolAspect enableEventToolTips;
- Utils::DoubleAspect minimumInclusiveCostRatio;
- Utils::DoubleAspect visualizationMinimumInclusiveCostRatio;
+ Utils::BoolAspect enableCacheSim{this};
+ Utils::BoolAspect enableBranchSim{this};
+ Utils::BoolAspect collectSystime{this};
+ Utils::BoolAspect collectBusEvents{this};
+ Utils::BoolAspect enableEventToolTips{this};
+ Utils::DoubleAspect minimumInclusiveCostRatio{this};
+ Utils::DoubleAspect visualizationMinimumInclusiveCostRatio{this};
QVariantMap defaultSettings() const;
};
@@ -126,16 +124,16 @@ public:
void writeSettings() const;
void readSettings();
- Utils::StringAspect lastSuppressionDirectory;
- Utils::StringAspect lastSuppressionHistory;
+ Utils::StringAspect lastSuppressionDirectory{this};
+ Utils::StringAspect lastSuppressionHistory{this};
/**
* Global callgrind settings
*/
- Utils::SelectionAspect costFormat;
- Utils::BoolAspect detectCycles;
- Utils::BoolAspect shortenTemplates;
+ Utils::SelectionAspect costFormat{this};
+ Utils::BoolAspect detectCycles{this};
+ Utils::BoolAspect shortenTemplates{this};
};
diff --git a/src/plugins/vcpkg/CMakeLists.txt b/src/plugins/vcpkg/CMakeLists.txt
new file mode 100644
index 0000000000..d3fb8d00ed
--- /dev/null
+++ b/src/plugins/vcpkg/CMakeLists.txt
@@ -0,0 +1,17 @@
+add_qtc_plugin(Vcpkg
+ PLUGIN_DEPENDS Core ProjectExplorer
+ SOURCES
+ vcpkg.qrc
+ vcpkgconstants.h
+ vcpkgmanifesteditor.cpp vcpkgmanifesteditor.h
+ vcpkgplugin.cpp vcpkgplugin.h
+ vcpkgsearch.cpp vcpkgsearch.h
+ vcpkgsettings.cpp vcpkgsettings.h
+)
+
+extend_qtc_plugin(Vcpkg
+ CONDITION WITH_TESTS
+ SOURCES
+ vcpkg_test.cpp vcpkg_test.h
+ EXPLICIT_MOC vcpkg_test.h
+)
diff --git a/src/plugins/vcpkg/Vcpkg.json.in b/src/plugins/vcpkg/Vcpkg.json.in
new file mode 100644
index 0000000000..7357c9e845
--- /dev/null
+++ b/src/plugins/vcpkg/Vcpkg.json.in
@@ -0,0 +1,30 @@
+{
+ \"Name\" : \"Vcpkg\",
+ \"Version\" : \"$$QTCREATOR_VERSION\",
+ \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
+ \"Vendor\" : \"The Qt Company Ltd\",
+ \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\",
+ \"License\" : [ \"Commercial Usage\",
+ \"\",
+ \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\",
+ \"\",
+ \"GNU General Public License Usage\",
+ \"\",
+ \"Alternatively, this plugin 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 plugin. 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.\"
+ ],
+ \"Experimental\" : true,
+ \"Description\" : \"vcpkg integration.\",
+ \"Url\" : \"http://www.qt.io\",
+ $$dependencyList,
+
+ \"Mimetypes\" : [
+ \"<?xml version=\'1.0\' encoding=\'UTF-8\'?>\",
+ \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\",
+ \" <mime-type type=\'application/vcpkg.manifest+json\'>\",
+ \" <sub-class-of type=\'application/json\'/>\",
+ \" <comment>Vcpkg Manifest File</comment>\",
+ \" <glob pattern=\'vcpkg.json\' weight=\'71\'/>\",
+ \" </mime-type>\",
+ \"</mime-info>\"
+ ]
+}
diff --git a/src/plugins/vcpkg/vcpkg.qbs b/src/plugins/vcpkg/vcpkg.qbs
new file mode 100644
index 0000000000..dff796ab91
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkg.qbs
@@ -0,0 +1,32 @@
+import qbs 1.0
+
+QtcPlugin {
+ name: "Vcpkg"
+
+ Depends { name: "Qt.widgets" }
+ Depends { name: "Utils" }
+
+ Depends { name: "Core" }
+ Depends { name: "ProjectExplorer" }
+ Depends { name: "TextEditor" }
+
+ files: [
+ "vcpkg.qrc",
+ "vcpkgconstants.h",
+ "vcpkgmanifesteditor.cpp",
+ "vcpkgmanifesteditor.h",
+ "vcpkgplugin.cpp",
+ "vcpkgplugin.h",
+ "vcpkgsearch.cpp",
+ "vcpkgsearch.h",
+ "vcpkgsettings.cpp",
+ "vcpkgsettings.h",
+ ]
+
+ QtcTestFiles {
+ files: [
+ "vcpkg_test.h",
+ "vcpkg_test.cpp",
+ ]
+ }
+}
diff --git a/src/plugins/vcpkg/vcpkg.qrc b/src/plugins/vcpkg/vcpkg.qrc
new file mode 100644
index 0000000000..a377b736db
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkg.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/vcpkg">
+ <file>wizards/manifest/vcpkg.json.tpl</file>
+ <file>wizards/manifest/wizard.json</file>
+ </qresource>
+</RCC>
diff --git a/src/plugins/vcpkg/vcpkg_test.cpp b/src/plugins/vcpkg/vcpkg_test.cpp
new file mode 100644
index 0000000000..65079b96a4
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkg_test.cpp
@@ -0,0 +1,110 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "vcpkg_test.h"
+
+#include "vcpkgsearch.h"
+
+#include <QTest>
+
+namespace Vcpkg::Internal {
+
+VcpkgSearchTest::VcpkgSearchTest(QObject *parent)
+ : QObject(parent)
+{ }
+
+VcpkgSearchTest::~VcpkgSearchTest() = default;
+
+void VcpkgSearchTest::testVcpkgJsonParser_data()
+{
+ QTest::addColumn<QString>("vcpkgManifestJsonData");
+ QTest::addColumn<QString>("name");
+ QTest::addColumn<QString>("version");
+ QTest::addColumn<QString>("license");
+ QTest::addColumn<QString>("shortDescription");
+ QTest::addColumn<QStringList>("description");
+ QTest::addColumn<QUrl>("homepage");
+ QTest::addColumn<bool>("success");
+
+ QTest::newRow("cimg, version, short description")
+ << R"({
+ "name": "cimg",
+ "version": "2.9.9",
+ "description": "The CImg Library is a small, open-source, and modern C++ toolkit for image processing",
+ "homepage": "https://github.com/dtschump/CImg",
+ "dependencies": [
+ {
+ "name": "vcpkg-cmake",
+ "host": true
+ }
+ ]
+ })"
+ << "cimg"
+ << "2.9.9"
+ << ""
+ << "The CImg Library is a small, open-source, and modern C++ toolkit for image processing"
+ << QStringList()
+ << QUrl::fromUserInput("https://github.com/dtschump/CImg")
+ << true;
+
+ QTest::newRow("catch-classic, version-string, complete description")
+ << R"({
+ "name": "catch-classic",
+ "version-string": "1.12.2",
+ "port-version": 1,
+ "description": [
+ "A modern, header-only test framework for unit tests",
+ "This is specifically the legacy 1.x branch provided for compatibility",
+ "with older compilers."
+ ],
+ "homepage": "https://github.com/catchorg/Catch2"
+ })"
+ << "catch-classic"
+ << "1.12.2"
+ << ""
+ << "A modern, header-only test framework for unit tests"
+ << QStringList({"This is specifically the legacy 1.x branch provided for compatibility",
+ "with older compilers."})
+ << QUrl::fromUserInput("https://github.com/catchorg/Catch2")
+ << true;
+
+ QTest::newRow("Incomplete")
+ << R"({
+ "version-semver": "1.0",
+ "description": "foo",
+ "license": "WTFPL"
+ })"
+ << ""
+ << "1.0"
+ << "WTFPL"
+ << "foo"
+ << QStringList()
+ << QUrl()
+ << false;
+}
+
+void VcpkgSearchTest::testVcpkgJsonParser()
+{
+ QFETCH(QString, vcpkgManifestJsonData);
+ QFETCH(QString, name);
+ QFETCH(QString, version);
+ QFETCH(QString, license);
+ QFETCH(QString, shortDescription);
+ QFETCH(QStringList, description);
+ QFETCH(QUrl, homepage);
+ QFETCH(bool, success);
+
+ bool ok = false;
+ const Search::VcpkgManifest mf =
+ Search::parseVcpkgManifest(vcpkgManifestJsonData.toUtf8(), &ok);
+
+ QCOMPARE(mf.name, name);
+ QCOMPARE(mf.version, version);
+ QCOMPARE(mf.license, license);
+ QCOMPARE(mf.shortDescription, shortDescription);
+ QCOMPARE(mf.description, description);
+ QCOMPARE(mf.homepage, homepage);
+ QCOMPARE(ok, success);
+}
+
+} // namespace Vcpkg::Internal
diff --git a/src/plugins/vcpkg/vcpkg_test.h b/src/plugins/vcpkg/vcpkg_test.h
new file mode 100644
index 0000000000..8175e28d59
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkg_test.h
@@ -0,0 +1,24 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <coreplugin/dialogs/ioptionspage.h>
+#include <utils/aspects.h>
+
+namespace Vcpkg::Internal {
+
+class VcpkgSearchTest : public QObject
+{
+ Q_OBJECT
+
+public:
+ VcpkgSearchTest(QObject *parent = nullptr);
+ ~VcpkgSearchTest();
+
+private slots:
+ void testVcpkgJsonParser_data();
+ void testVcpkgJsonParser();
+};
+
+} // namespace Vcpkg::Internal
diff --git a/src/plugins/vcpkg/vcpkgconstants.h b/src/plugins/vcpkg/vcpkgconstants.h
new file mode 100644
index 0000000000..644dfab95f
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkgconstants.h
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace Vcpkg::Constants {
+
+const char TOOLSSETTINGSPAGE_ID[] = "Vcpkg.VcpkgSettings";
+const char WEBSITE_URL[] = "https://vcpkg.io/";
+const char ENVVAR_VCPKG_ROOT[] = "VCPKG_ROOT";
+const char VCPKGMANIFEST_EDITOR_ID[] = "Vcpkg.VcpkgManifestEditor";
+const char VCPKGMANIFEST_MIMETYPE[] = "application/vcpkg.manifest+json";
+
+} // namespace Vcpkg::Constants
diff --git a/src/plugins/vcpkg/vcpkgmanifesteditor.cpp b/src/plugins/vcpkg/vcpkgmanifesteditor.cpp
new file mode 100644
index 0000000000..40e4837cf7
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkgmanifesteditor.cpp
@@ -0,0 +1,72 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "vcpkgmanifesteditor.h"
+
+#include "vcpkgconstants.h"
+#include "vcpkgsearch.h"
+#include "vcpkgsettings.h"
+#include "vcpkgtr.h"
+
+#include <coreplugin/icore.h>
+
+#include <utils/utilsicons.h>
+
+#include <texteditor/textdocument.h>
+
+#include <QToolBar>
+
+namespace Vcpkg::Internal {
+
+class VcpkgManifestEditorWidget : public TextEditor::TextEditorWidget
+{
+public:
+ VcpkgManifestEditorWidget()
+ {
+ m_searchPkgAction = toolBar()->addAction(Utils::Icons::ZOOM_TOOLBAR.icon(),
+ Tr::tr("Search package..."));
+ connect(m_searchPkgAction, &QAction::triggered, this, [this] {
+ const Search::VcpkgManifest package = Search::showVcpkgPackageSearchDialog();
+ if (!package.name.isEmpty())
+ textCursor().insertText(package.name);
+ });
+ updateToolBar();
+
+ QAction *optionsAction = toolBar()->addAction(Utils::Icons::SETTINGS_TOOLBAR.icon(),
+ Core::ICore::msgShowOptionsDialog());
+ connect(optionsAction, &QAction::triggered, [] {
+ Core::ICore::showOptionsDialog(Constants::TOOLSSETTINGSPAGE_ID);
+ });
+
+ connect(&settings().vcpkgRoot, &Utils::BaseAspect::changed,
+ this, &VcpkgManifestEditorWidget::updateToolBar);
+ }
+
+ void updateToolBar()
+ {
+ Utils::FilePath vcpkg = settings().vcpkgRoot().pathAppended("vcpkg").withExecutableSuffix();
+ m_searchPkgAction->setEnabled(vcpkg.isExecutableFile());
+ }
+
+private:
+ QAction *m_searchPkgAction;
+};
+
+static TextEditor::TextDocument *createVcpkgManifestDocument()
+{
+ auto doc = new TextEditor::TextDocument;
+ doc->setId(Constants::VCPKGMANIFEST_EDITOR_ID);
+ return doc;
+}
+
+VcpkgManifestEditorFactory::VcpkgManifestEditorFactory()
+{
+ setId(Constants::VCPKGMANIFEST_EDITOR_ID);
+ setDisplayName(Tr::tr("Vcpkg Manifest Editor"));
+ addMimeType(Constants::VCPKGMANIFEST_MIMETYPE);
+ setDocumentCreator(createVcpkgManifestDocument);
+ setEditorWidgetCreator([] { return new VcpkgManifestEditorWidget; });
+ setUseGenericHighlighter(true);
+}
+
+} // namespace Vcpkg::Internal
diff --git a/src/plugins/vcpkg/vcpkgmanifesteditor.h b/src/plugins/vcpkg/vcpkgmanifesteditor.h
new file mode 100644
index 0000000000..c7762d69df
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkgmanifesteditor.h
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <texteditor/texteditor.h>
+
+namespace Vcpkg::Internal {
+
+class VcpkgManifestEditorFactory : public TextEditor::TextEditorFactory
+{
+public:
+ VcpkgManifestEditorFactory();
+};
+
+} // namespace Vcpkg::Internal
diff --git a/src/plugins/vcpkg/vcpkgplugin.cpp b/src/plugins/vcpkg/vcpkgplugin.cpp
new file mode 100644
index 0000000000..4ef89499a7
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkgplugin.cpp
@@ -0,0 +1,38 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "vcpkgplugin.h"
+
+#ifdef WITH_TESTS
+#include "vcpkg_test.h"
+#endif // WITH_TESTS
+#include "vcpkgmanifesteditor.h"
+#include "vcpkgsettings.h"
+
+#include <projectexplorer/jsonwizard/jsonwizardfactory.h>
+
+namespace Vcpkg::Internal {
+
+class VcpkgPluginPrivate
+{
+public:
+ VcpkgManifestEditorFactory manifestEditorFactory;
+ VcpkgSettings settings;
+};
+
+VcpkgPlugin::~VcpkgPlugin()
+{
+ delete d;
+}
+
+void VcpkgPlugin::initialize()
+{
+ d = new VcpkgPluginPrivate;
+ ProjectExplorer::JsonWizardFactory::addWizardPath(":/vcpkg/wizards/");
+
+#ifdef WITH_TESTS
+ addTest<VcpkgSearchTest>();
+#endif
+}
+
+} // namespace Vcpkg::Internal
diff --git a/src/plugins/vcpkg/vcpkgplugin.h b/src/plugins/vcpkg/vcpkgplugin.h
new file mode 100644
index 0000000000..797083ea95
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkgplugin.h
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <extensionsystem/iplugin.h>
+
+namespace ProjectExplorer { class Project; }
+
+namespace Vcpkg::Internal {
+
+class VcpkgPlugin final : public ExtensionSystem::IPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Vcpkg.json")
+
+public:
+ ~VcpkgPlugin();
+
+ void initialize() final;
+
+private:
+ class VcpkgPluginPrivate *d = nullptr;
+};
+
+} // namespace Vcpkg::Internal
diff --git a/src/plugins/vcpkg/vcpkgsearch.cpp b/src/plugins/vcpkg/vcpkgsearch.cpp
new file mode 100644
index 0000000000..b9315ec540
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkgsearch.cpp
@@ -0,0 +1,214 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "vcpkgsearch.h"
+
+#include "qpushbutton.h"
+#include "vcpkgsettings.h"
+#include "vcpkgtr.h"
+
+#include <utils/algorithm.h>
+#include <utils/fancylineedit.h>
+#include <utils/fileutils.h>
+#include <utils/itemviews.h>
+#include <utils/layoutbuilder.h>
+
+#include <coreplugin/icore.h>
+
+#include <QDialogButtonBox>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QTextBrowser>
+
+using namespace Utils;
+
+namespace Vcpkg::Internal::Search {
+
+class VcpkgPackageSearchDialog : public QDialog
+{
+public:
+ explicit VcpkgPackageSearchDialog(QWidget *parent);
+
+ VcpkgManifest selectedPackage() const;
+
+private:
+ void listPackages(const QString &filter);
+ void showPackageDetails(const QString &packageName);
+
+ VcpkgManifests m_allPackages;
+ VcpkgManifest m_selectedPackage;
+
+ FancyLineEdit *m_packagesFilter;
+ ListWidget *m_packagesList;
+ QLineEdit *m_vcpkgName;
+ QLabel *m_vcpkgVersion;
+ QLabel *m_vcpkgLicense;
+ QTextBrowser *m_vcpkgDescription;
+ QLabel *m_vcpkgHomepage;
+ QDialogButtonBox *m_buttonBox;
+};
+
+VcpkgPackageSearchDialog::VcpkgPackageSearchDialog(QWidget *parent)
+ : QDialog(parent)
+{
+ resize(920, 400);
+
+ m_packagesFilter = new FancyLineEdit;
+ m_packagesFilter->setFiltering(true);
+ m_packagesFilter->setFocus();
+ m_packagesFilter->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
+
+ m_packagesList = new ListWidget;
+ m_packagesList->setMaximumWidth(300);
+
+ m_vcpkgName = new QLineEdit;
+ m_vcpkgName->setReadOnly(true);
+
+ m_vcpkgVersion = new QLabel;
+ m_vcpkgLicense = new QLabel;
+ m_vcpkgDescription = new QTextBrowser;
+
+ m_vcpkgHomepage = new QLabel;
+ m_vcpkgHomepage->setOpenExternalLinks(true);
+ m_vcpkgHomepage->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
+ m_vcpkgHomepage->setTextInteractionFlags(Qt::TextBrowserInteraction);
+
+ m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Close);
+
+ using namespace Layouting;
+ Column {
+ Row {
+ Column {
+ m_packagesFilter,
+ m_packagesList,
+ },
+ Form {
+ Tr::tr("Name:"), m_vcpkgName, br,
+ Tr::tr("Version:"), m_vcpkgVersion, br,
+ Tr::tr("License:"), m_vcpkgLicense, br,
+ Tr::tr("Description:"), m_vcpkgDescription, br,
+ Tr::tr("Homepage:"), m_vcpkgHomepage, br,
+ },
+ },
+ m_buttonBox,
+ }.attachTo(this);
+
+ m_allPackages = vcpkgManifests(settings().vcpkgRoot());
+
+ listPackages({});
+
+ connect(m_packagesFilter, &FancyLineEdit::filterChanged,
+ this, &VcpkgPackageSearchDialog::listPackages);
+ connect(m_packagesList, &ListWidget::currentTextChanged,
+ this, &VcpkgPackageSearchDialog::showPackageDetails);
+ connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+}
+
+VcpkgManifest VcpkgPackageSearchDialog::selectedPackage() const
+{
+ return m_selectedPackage;
+}
+
+void VcpkgPackageSearchDialog::listPackages(const QString &filter)
+{
+ const VcpkgManifests filteredPackages = filtered(m_allPackages,
+ [&filter] (const VcpkgManifest &package) {
+ return filter.isEmpty()
+ || package.name.contains(filter, Qt::CaseInsensitive)
+ || package.shortDescription.contains(filter, Qt::CaseInsensitive)
+ || package.description.contains(filter, Qt::CaseInsensitive);
+ });
+ QStringList names = transform(filteredPackages, [] (const VcpkgManifest &package) {
+ return package.name;
+ });
+ names.sort();
+ m_packagesList->clear();
+ m_packagesList->addItems(names);
+}
+
+void VcpkgPackageSearchDialog::showPackageDetails(const QString &packageName)
+{
+ const VcpkgManifest manifest = findOrDefault(m_allPackages,
+ [&packageName] (const VcpkgManifest &m) {
+ return m.name == packageName;
+ });
+
+ m_vcpkgName->setText(manifest.name);
+ m_vcpkgVersion->setText(manifest.version);
+ m_vcpkgLicense->setText(manifest.license);
+ QString description = manifest.shortDescription;
+ if (!manifest.description.isEmpty())
+ description.append("<p>" + manifest.description.join("</p><p>") + "</p>");
+ m_vcpkgDescription->setText(description);
+ m_vcpkgHomepage->setText(QString::fromLatin1("<a href=\"%1\">%1</a>")
+ .arg(manifest.homepage.toDisplayString()));
+
+ m_selectedPackage = manifest;
+ m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!manifest.name.isEmpty());
+}
+
+VcpkgManifest parseVcpkgManifest(const QByteArray &vcpkgManifestJsonData, bool *ok)
+{
+ // https://learn.microsoft.com/en-us/vcpkg/reference/vcpkg-json
+ VcpkgManifest result;
+ const QJsonObject jsonObject = QJsonDocument::fromJson(vcpkgManifestJsonData).object();
+ if (const QJsonValue name = jsonObject.value("name"); !name.isUndefined())
+ result.name = name.toString();
+ for (const char *key : {"version", "version-semver", "version-date", "version-string"} ) {
+ if (const QJsonValue ver = jsonObject.value(QLatin1String(key)); !ver.isUndefined()) {
+ result.version = ver.toString();
+ break;
+ }
+ }
+ if (const QJsonValue license = jsonObject.value("license"); !license.isUndefined())
+ result.license = license.toString();
+ if (const QJsonValue description = jsonObject.value("description"); !description.isUndefined()) {
+ if (description.isArray()) {
+ const QJsonArray descriptionLines = description.toArray();
+ for (const QJsonValue &val : descriptionLines) {
+ const QString line = val.toString();
+ if (result.shortDescription.isEmpty()) {
+ result.shortDescription = line;
+ continue;
+ }
+ result.description.append(line);
+ }
+ } else {
+ result.shortDescription = description.toString();
+ }
+ }
+ if (const QJsonValue homepage = jsonObject.value("homepage"); !homepage.isUndefined())
+ result.homepage = QUrl::fromUserInput(homepage.toString());
+
+ if (ok)
+ *ok = !(result.name.isEmpty() || result.version.isEmpty());
+
+ return result;
+}
+
+VcpkgManifests vcpkgManifests(const FilePath &vcpkgRoot)
+{
+ const FilePath portsDir = vcpkgRoot / "ports";
+ VcpkgManifests result;
+ const FilePaths manifestFiles =
+ portsDir.dirEntries({{"vcpkg.json"}, QDir::Files, QDirIterator::Subdirectories});
+ for (const FilePath &manifestFile : manifestFiles) {
+ FileReader reader;
+ if (reader.fetch(manifestFile)) {
+ const QByteArray &manifestData = reader.data();
+ const VcpkgManifest manifest = parseVcpkgManifest(manifestData);
+ result.append(manifest);
+ }
+ }
+ return result;
+}
+
+VcpkgManifest showVcpkgPackageSearchDialog(QWidget *parent)
+{
+ VcpkgPackageSearchDialog dlg(parent ? parent : Core::ICore::dialogParent());
+ return (dlg.exec() == QDialog::Accepted) ? dlg.selectedPackage() : VcpkgManifest();
+}
+
+} // namespace Vcpkg::Internal::Search
diff --git a/src/plugins/vcpkg/vcpkgsearch.h b/src/plugins/vcpkg/vcpkgsearch.h
new file mode 100644
index 0000000000..bb2d568a00
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkgsearch.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <coreplugin/dialogs/ioptionspage.h>
+#include <utils/aspects.h>
+
+#include <QUrl>
+
+namespace Vcpkg::Internal::Search {
+
+struct VcpkgManifest
+{
+ QString name;
+ QString version;
+ QString license;
+ QString shortDescription;
+ QStringList description;
+ QUrl homepage;
+};
+
+using VcpkgManifests = QList<VcpkgManifest>;
+
+VcpkgManifest parseVcpkgManifest(const QByteArray &vcpkgManifestJsonData, bool *ok = nullptr);
+VcpkgManifests vcpkgManifests(const Utils::FilePath &vcpkgRoot);
+VcpkgManifest showVcpkgPackageSearchDialog(QWidget *parent = nullptr);
+
+} // namespace Vcpkg::Internal::Search
diff --git a/src/plugins/vcpkg/vcpkgsettings.cpp b/src/plugins/vcpkg/vcpkgsettings.cpp
new file mode 100644
index 0000000000..3542ec4791
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkgsettings.cpp
@@ -0,0 +1,68 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "vcpkgsettings.h"
+
+#include "vcpkgconstants.h"
+#include "vcpkgtr.h"
+
+#include <cmakeprojectmanager/cmakeprojectconstants.h>
+
+#include <utils/environment.h>
+#include <utils/layoutbuilder.h>
+#include <utils/utilsicons.h>
+
+#include <QDesktopServices>
+#include <QToolButton>
+
+namespace Vcpkg::Internal {
+
+static VcpkgSettings *theSettings = nullptr;
+
+VcpkgSettings &settings()
+{
+ return *theSettings;
+}
+
+VcpkgSettings::VcpkgSettings()
+{
+ theSettings = this;
+
+ setSettingsGroup("Vcpkg");
+ setId(Constants::TOOLSSETTINGSPAGE_ID);
+ setDisplayName("Vcpkg");
+ setCategory(CMakeProjectManager::Constants::Settings::CATEGORY);
+
+ vcpkgRoot.setSettingsKey("VcpkgRoot");
+ vcpkgRoot.setExpectedKind(Utils::PathChooser::ExistingDirectory);
+ vcpkgRoot.setDefaultValue(Utils::qtcEnvironmentVariable(Constants::ENVVAR_VCPKG_ROOT));
+
+ setLayouter([this] {
+ using namespace Layouting;
+ auto websiteButton = new QToolButton;
+ websiteButton->setIcon(Utils::Icons::ONLINE.icon());
+ websiteButton->setToolTip(Constants::WEBSITE_URL);
+
+ connect(websiteButton, &QAbstractButton::clicked, [] {
+ QDesktopServices::openUrl(QUrl::fromUserInput(Constants::WEBSITE_URL));
+ });
+
+ // clang-format off
+ using namespace Layouting;
+ return Column {
+ Group {
+ title(Tr::tr("Vcpkg installation")),
+ Form {
+ Utils::PathChooser::label(),
+ Span { 2, Row { vcpkgRoot, websiteButton } },
+ },
+ },
+ st,
+ };
+ // clang-format on
+ });
+
+ readSettings();
+}
+
+} // Vcpkg::Internal
diff --git a/src/plugins/vcpkg/vcpkgsettings.h b/src/plugins/vcpkg/vcpkgsettings.h
new file mode 100644
index 0000000000..6a00fd506f
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkgsettings.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <coreplugin/dialogs/ioptionspage.h>
+
+namespace Vcpkg::Internal {
+
+class VcpkgSettings : public Core::PagedSettings
+{
+public:
+ VcpkgSettings();
+
+ Utils::FilePathAspect vcpkgRoot{this};
+};
+
+VcpkgSettings &settings();
+
+} // Vcpkg::Internal
diff --git a/src/plugins/vcpkg/vcpkgtr.h b/src/plugins/vcpkg/vcpkgtr.h
new file mode 100644
index 0000000000..0914ce6444
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkgtr.h
@@ -0,0 +1,15 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QCoreApplication>
+
+namespace Vcpkg {
+
+struct Tr
+{
+ Q_DECLARE_TR_FUNCTIONS(Vcpkg)
+};
+
+} // namespace Vcpkg
diff --git a/src/plugins/vcpkg/wizards/manifest/vcpkg.json.tpl b/src/plugins/vcpkg/wizards/manifest/vcpkg.json.tpl
new file mode 100644
index 0000000000..1519949d2e
--- /dev/null
+++ b/src/plugins/vcpkg/wizards/manifest/vcpkg.json.tpl
@@ -0,0 +1,6 @@
+{
+ "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
+ "name": "%{Name}",
+ "version-string": "%{VersionString}",
+%{Dependencies}
+}
diff --git a/src/plugins/vcpkg/wizards/manifest/wizard.json b/src/plugins/vcpkg/wizards/manifest/wizard.json
new file mode 100644
index 0000000000..80e8b0fcc5
--- /dev/null
+++ b/src/plugins/vcpkg/wizards/manifest/wizard.json
@@ -0,0 +1,80 @@
+{
+ "version": 1,
+ "supportedProjectTypes": [ ],
+ "id": "VcpkgManifest.Json",
+ "category": "U.VcpkgManifest",
+ "trDescription": "Creates a vcpkg.json manifest file.",
+ "trDisplayName": "vcpkg.json Manifest File",
+ "trDisplayCategory": "vcpkg",
+ "iconText": "json",
+
+ "options": [
+ { "key": "InitialFileName", "value": "vcpkg.json" },
+ { "key": "TargetPath", "value": "%{Path}" }
+ ],
+
+ "pages":
+ [
+ {
+ "trDisplayName": "Location",
+ "trShortTitle": "Location",
+ "typeId": "File"
+ },
+ {
+ "trDisplayName": "vcpkg.json Manifest File",
+ "trShortTitle": "Manifest fields",
+ "typeId": "Fields",
+ "data":
+ [
+ {
+ "name": "Name",
+ "trDisplayName": "Name:",
+ "mandatory": true,
+ "type": "LineEdit",
+ "data":
+ {
+ "trText": "mypackage",
+ "validator": "^[a-z_0-9]+$"
+ }
+ },
+ {
+ "name": "VersionString",
+ "trDisplayName": "Version string:",
+ "mandatory": true,
+ "type": "LineEdit",
+ "data":
+ {
+ "trText": "0.0.1"
+ }
+ },
+ {
+ "name": "Dependencies",
+ "trDisplayName": "Dependencies:",
+ "mandatory": false,
+ "type": "TextEdit",
+ "data":
+ {
+ "trText": " \"dependencies\": [\n \"fmt\"\n ]"
+ }
+ }
+ ]
+ },
+ {
+ "trDisplayName": "Project Management",
+ "trShortTitle": "Summary",
+ "typeId": "Summary"
+ }
+ ],
+ "generators":
+ [
+ {
+ "typeId": "File",
+ "data":
+ {
+ "source": "vcpkg.json.tpl",
+ "target": "%{Path}/vcpkg.json",
+ "openInEditor": true
+ }
+ }
+ ]
+}
diff --git a/src/plugins/vcsbase/cleandialog.cpp b/src/plugins/vcsbase/cleandialog.cpp
index bc5ddafe66..ae0b14a7ba 100644
--- a/src/plugins/vcsbase/cleandialog.cpp
+++ b/src/plugins/vcsbase/cleandialog.cpp
@@ -9,8 +9,8 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/progressmanager/progressmanager.h>
+#include <utils/async.h>
#include <utils/layoutbuilder.h>
-#include <utils/runextensions.h>
#include <QApplication>
#include <QCheckBox>
@@ -38,10 +38,10 @@ enum { nameColumn, columnCount };
enum { fileNameRole = Qt::UserRole, isDirectoryRole = Qt::UserRole + 1 };
// Helper for recursively removing files.
-static void removeFileRecursion(QFutureInterface<void> &futureInterface,
- const QFileInfo &f, QString *errorMessage)
+static void removeFileRecursion(QPromise<void> &promise, const QFileInfo &f,
+ QString *errorMessage)
{
- if (futureInterface.isCanceled())
+ if (promise.isCanceled())
return;
// The version control system might list files/directory in arbitrary
// order, causing files to be removed from parent directories.
@@ -51,7 +51,7 @@ static void removeFileRecursion(QFutureInterface<void> &futureInterface,
const QDir dir(f.absoluteFilePath());
const QList<QFileInfo> infos = dir.entryInfoList(QDir::AllEntries|QDir::NoDotAndDotDot|QDir::Hidden);
for (const QFileInfo &fi : infos)
- removeFileRecursion(futureInterface, fi, errorMessage);
+ removeFileRecursion(promise, fi, errorMessage);
QDir parent = f.absoluteDir();
if (!parent.rmdir(f.fileName()))
errorMessage->append(Tr::tr("The directory %1 could not be deleted.")
@@ -67,18 +67,19 @@ static void removeFileRecursion(QFutureInterface<void> &futureInterface,
}
// Cleaning files in the background
-static void runCleanFiles(QFutureInterface<void> &futureInterface,
- const FilePath &repository, const QStringList &files,
+static void runCleanFiles(QPromise<void> &promise, const FilePath &repository,
+ const QStringList &files,
const std::function<void(const QString&)> &errorHandler)
{
QString errorMessage;
- futureInterface.setProgressRange(0, files.size());
- futureInterface.setProgressValue(0);
+ promise.setProgressRange(0, files.size());
+ promise.setProgressValue(0);
+ int fileIndex = 0;
for (const QString &name : files) {
- removeFileRecursion(futureInterface, QFileInfo(name), &errorMessage);
- if (futureInterface.isCanceled())
+ removeFileRecursion(promise, QFileInfo(name), &errorMessage);
+ if (promise.isCanceled())
break;
- futureInterface.setProgressValue(futureInterface.progressValue() + 1);
+ promise.setProgressValue(++fileIndex);
}
if (!errorMessage.isEmpty()) {
// Format and emit error.
@@ -258,8 +259,8 @@ bool CleanDialog::promptToDelete()
return false;
// Remove in background
- QFuture<void> task = runAsync(Internal::runCleanFiles, d->m_workingDirectory,
- selectedFiles, Internal::handleError);
+ QFuture<void> task = Utils::asyncRun(Internal::runCleanFiles, d->m_workingDirectory,
+ selectedFiles, Internal::handleError);
const QString taskName = Tr::tr("Cleaning \"%1\"").arg(d->m_workingDirectory.toUserOutput());
Core::ProgressManager::addTask(task, taskName, "VcsBase.cleanRepository");
diff --git a/src/plugins/vcsbase/commonvcssettings.cpp b/src/plugins/vcsbase/commonvcssettings.cpp
index 160095d38d..d286164b5b 100644
--- a/src/plugins/vcsbase/commonvcssettings.cpp
+++ b/src/plugins/vcsbase/commonvcssettings.cpp
@@ -20,8 +20,7 @@
using namespace Utils;
-namespace VcsBase {
-namespace Internal {
+namespace VcsBase::Internal {
// Return default for the ssh-askpass command (default to environment)
static QString sshPasswordPromptDefault()
@@ -39,27 +38,21 @@ CommonVcsSettings::CommonVcsSettings()
setSettingsGroup("VCS");
setAutoApply(false);
- registerAspect(&nickNameMailMap);
nickNameMailMap.setSettingsKey("NickNameMailMap");
- nickNameMailMap.setDisplayStyle(StringAspect::PathChooserDisplay);
nickNameMailMap.setExpectedKind(PathChooser::File);
nickNameMailMap.setHistoryCompleter("Vcs.NickMap.History");
nickNameMailMap.setLabelText(Tr::tr("User/&alias configuration file:"));
nickNameMailMap.setToolTip(Tr::tr("A file listing nicknames in a 4-column mailmap format:\n"
"'name <email> alias <email>'."));
- registerAspect(&nickNameFieldListFile);
nickNameFieldListFile.setSettingsKey("NickNameFieldListFile");
- nickNameFieldListFile.setDisplayStyle(StringAspect::PathChooserDisplay);
nickNameFieldListFile.setExpectedKind(PathChooser::File);
nickNameFieldListFile.setHistoryCompleter("Vcs.NickFields.History");
nickNameFieldListFile.setLabelText(Tr::tr("User &fields configuration file:"));
nickNameFieldListFile.setToolTip(Tr::tr("A simple file containing lines with field names like "
"\"Reviewed-By:\" which will be added below the submit editor."));
- registerAspect(&submitMessageCheckScript);
submitMessageCheckScript.setSettingsKey("SubmitMessageCheckScript");
- submitMessageCheckScript.setDisplayStyle(StringAspect::PathChooserDisplay);
submitMessageCheckScript.setExpectedKind(PathChooser::ExistingCommand);
submitMessageCheckScript.setHistoryCompleter("Vcs.MessageCheckScript.History");
submitMessageCheckScript.setLabelText(Tr::tr("Submit message &check script:"));
@@ -67,9 +60,7 @@ CommonVcsSettings::CommonVcsSettings()
"in a temporary file as first argument. It should return with an exit != 0 and a message "
"on standard error to indicate failure."));
- registerAspect(&sshPasswordPrompt);
sshPasswordPrompt.setSettingsKey("SshPasswordPrompt");
- sshPasswordPrompt.setDisplayStyle(StringAspect::PathChooserDisplay);
sshPasswordPrompt.setExpectedKind(PathChooser::ExistingCommand);
sshPasswordPrompt.setHistoryCompleter("Vcs.SshPrompt.History");
sshPasswordPrompt.setDefaultValue(sshPasswordPromptDefault());
@@ -78,12 +69,10 @@ CommonVcsSettings::CommonVcsSettings()
"for a password,\nshould a repository require SSH-authentication "
"(see documentation on SSH and the environment variable SSH_ASKPASS)."));
- registerAspect(&lineWrap);
lineWrap.setSettingsKey("LineWrap");
lineWrap.setDefaultValue(true);
lineWrap.setLabelText(Tr::tr("Wrap submit message at:"));
- registerAspect(&lineWrapWidth);
lineWrapWidth.setSettingsKey("LineWrapWidth");
lineWrapWidth.setSuffix(Tr::tr(" characters"));
lineWrapWidth.setDefaultValue(72);
@@ -94,61 +83,48 @@ CommonVcsSettings::CommonVcsSettings()
class CommonSettingsWidget final : public Core::IOptionsPageWidget
{
public:
- CommonSettingsWidget(CommonOptionsPage *page);
-
- void apply() final;
-
-private:
- void updatePath();
- CommonOptionsPage *m_page;
-};
-
-
-CommonSettingsWidget::CommonSettingsWidget(CommonOptionsPage *page)
- : m_page(page)
-{
- CommonVcsSettings &s = m_page->settings();
-
- auto cacheResetButton = new QPushButton(Tr::tr("Reset VCS Cache"));
- cacheResetButton->setToolTip(Tr::tr("Reset information about which "
- "version control system handles which directory."));
-
- updatePath();
-
- using namespace Layouting;
- Column {
- Row { s.lineWrap, s.lineWrapWidth, st },
- Form {
- s.submitMessageCheckScript,
- s.nickNameMailMap,
- s.nickNameFieldListFile,
- s.sshPasswordPrompt,
- {}, cacheResetButton
- }
- }.attachTo(this);
-
- connect(Core::VcsManager::instance(), &Core::VcsManager::configurationChanged,
- this, &CommonSettingsWidget::updatePath);
- connect(cacheResetButton, &QPushButton::clicked,
- Core::VcsManager::instance(), &Core::VcsManager::clearVersionControlCache);
-}
-
-void CommonSettingsWidget::updatePath()
-{
- EnvironmentChange change;
- change.addAppendToPath(Core::VcsManager::additionalToolsPath());
- m_page->settings().sshPasswordPrompt.setEnvironmentChange(change);
-}
-
-void CommonSettingsWidget::apply()
-{
- CommonVcsSettings &s = m_page->settings();
- if (s.isDirty()) {
- s.apply();
- s.writeSettings(Core::ICore::settings());
- emit m_page->settingsChanged();
+ CommonSettingsWidget(CommonOptionsPage *page)
+ {
+ CommonVcsSettings &s = page->settings();
+
+ auto cacheResetButton = new QPushButton(Tr::tr("Reset VCS Cache"));
+ cacheResetButton->setToolTip(Tr::tr("Reset information about which "
+ "version control system handles which directory."));
+
+ auto updatePath = [&s] {
+ Environment env;
+ env.appendToPath(Core::VcsManager::additionalToolsPath());
+ s.sshPasswordPrompt.setEnvironment(env);
+ };
+
+ using namespace Layouting;
+ Column {
+ Row { s.lineWrap, s.lineWrapWidth, st },
+ Form {
+ s.submitMessageCheckScript, br,
+ s.nickNameMailMap, br,
+ s.nickNameFieldListFile, br,
+ s.sshPasswordPrompt, br,
+ {}, cacheResetButton
+ }
+ }.attachTo(this);
+
+ updatePath();
+
+ connect(Core::VcsManager::instance(), &Core::VcsManager::configurationChanged,
+ this, updatePath);
+ connect(cacheResetButton, &QPushButton::clicked,
+ Core::VcsManager::instance(), &Core::VcsManager::clearVersionControlCache);
+
+ setOnApply([&s] {
+ if (s.isDirty()) {
+ s.apply();
+ s.writeSettings(Core::ICore::settings());
+ emit s.settingsChanged();
+ }
+ });
}
-}
+};
// CommonOptionsPage
@@ -165,5 +141,4 @@ CommonOptionsPage::CommonOptionsPage()
setWidgetCreator([this] { return new CommonSettingsWidget(this); });
}
-} // namespace Internal
-} // namespace VcsBase
+} // VcsBase::Internal
diff --git a/src/plugins/vcsbase/commonvcssettings.h b/src/plugins/vcsbase/commonvcssettings.h
index 8d781a45d7..a5005f252a 100644
--- a/src/plugins/vcsbase/commonvcssettings.h
+++ b/src/plugins/vcsbase/commonvcssettings.h
@@ -7,43 +7,39 @@
#include <utils/aspects.h>
-namespace VcsBase {
-namespace Internal {
+namespace VcsBase::Internal {
class CommonVcsSettings : public Utils::AspectContainer
{
+ Q_OBJECT
+
public:
CommonVcsSettings();
- friend QDebug operator<<(QDebug, const CommonVcsSettings &);
-
- Utils::StringAspect nickNameMailMap;
- Utils::StringAspect nickNameFieldListFile;
+ Utils::FilePathAspect nickNameMailMap{this};
+ Utils::FilePathAspect nickNameFieldListFile{this};
- Utils::StringAspect submitMessageCheckScript;
+ Utils::FilePathAspect submitMessageCheckScript{this};
// Executable run to graphically prompt for a SSH-password.
- Utils::StringAspect sshPasswordPrompt;
+ Utils::FilePathAspect sshPasswordPrompt{this};
- Utils::BoolAspect lineWrap;
- Utils::IntegerAspect lineWrapWidth;
+ Utils::BoolAspect lineWrap{this};
+ Utils::IntegerAspect lineWrapWidth{this};
+
+signals:
+ void settingsChanged();
};
class CommonOptionsPage final : public Core::IOptionsPage
{
- Q_OBJECT
-
public:
- explicit CommonOptionsPage();
+ CommonOptionsPage();
CommonVcsSettings &settings() { return m_settings; }
-signals:
- void settingsChanged();
-
private:
CommonVcsSettings m_settings;
};
-} // namespace Internal
-} // namespace VcsBase
+} // VcsBase::Internal
diff --git a/src/plugins/vcsbase/submiteditorwidget.cpp b/src/plugins/vcsbase/submiteditorwidget.cpp
index eaac1d61d2..00b4200580 100644
--- a/src/plugins/vcsbase/submiteditorwidget.cpp
+++ b/src/plugins/vcsbase/submiteditorwidget.cpp
@@ -127,8 +127,6 @@ struct SubmitEditorWidgetPrivate
SubmitEditorWidget::SubmitEditorWidget() :
d(new SubmitEditorWidgetPrivate)
{
- resize(507, 419);
- setMinimumSize(QSize(0, 0));
setWindowTitle(Tr::tr("Subversion Submit"));
auto scrollAreaWidgetContents = new QWidget();
@@ -208,8 +206,9 @@ SubmitEditorWidget::SubmitEditorWidget() :
using namespace Layouting;
Column {
- scrollArea
- }.attachTo(this, WithoutMargins);
+ scrollArea,
+ noMargin
+ }.attachTo(this);
connect(d->description, &QWidget::customContextMenuRequested,
this, &SubmitEditorWidget::editorCustomContextMenuRequested);
@@ -620,15 +619,16 @@ void SubmitEditorWidget::verifyDescription()
d->descriptionHint->setText(hints.join("<br>"));
if (!d->descriptionHint->text().isEmpty()) {
+ static_assert(MaxSubjectLength == 72); // change the translated message below when changing
d->descriptionHint->setToolTip(
Tr::tr("<p>Writing good commit messages</p>"
"<ul>"
"<li>Avoid very short commit messages.</li>"
- "<li>Consider the first line as subject (like in email) "
- "and keep it shorter than %n characters.</li>"
+ "<li>Consider the first line as a subject (like in emails) "
+ "and keep it shorter than 72 characters.</li>"
"<li>After an empty second line, a longer description can be added.</li>"
"<li>Describe why the change was done, not how it was done.</li>"
- "</ul>", nullptr, MaxSubjectLength));
+ "</ul>"));
}
}
diff --git a/src/plugins/vcsbase/vcsbaseclient.cpp b/src/plugins/vcsbase/vcsbaseclient.cpp
index c51b1292f9..ffa19b24f7 100644
--- a/src/plugins/vcsbase/vcsbaseclient.cpp
+++ b/src/plugins/vcsbase/vcsbaseclient.cpp
@@ -21,6 +21,7 @@
#include <utils/commandline.h>
#include <utils/environment.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
#include <QDebug>
@@ -56,7 +57,7 @@ namespace VcsBase {
VcsBaseClientImpl::VcsBaseClientImpl(VcsBaseSettings *baseSettings)
: m_baseSettings(baseSettings)
{
- m_baseSettings->readSettings(ICore::settings());
+ m_baseSettings->readSettings();
connect(ICore::instance(), &ICore::saveSettingsRequested,
this, &VcsBaseClientImpl::saveSettings);
}
@@ -68,7 +69,7 @@ VcsBaseSettings &VcsBaseClientImpl::settings() const
FilePath VcsBaseClientImpl::vcsBinary() const
{
- return m_baseSettings->binaryPath.filePath();
+ return m_baseSettings->binaryPath();
}
VcsCommand *VcsBaseClientImpl::createCommand(const FilePath &workingDirectory,
@@ -89,6 +90,16 @@ VcsCommand *VcsBaseClientImpl::createCommand(const FilePath &workingDirectory,
return cmd;
}
+void VcsBaseClientImpl::setupCommand(Utils::Process &process,
+ const FilePath &workingDirectory,
+ const QStringList &args) const
+{
+ process.setEnvironment(processEnvironment());
+ process.setWorkingDirectory(workingDirectory);
+ process.setCommand({vcsBinary(), args});
+ process.setUseCtrlCStub(true);
+}
+
void VcsBaseClientImpl::enqueueJob(VcsCommand *cmd, const QStringList &args,
const ExitCodeInterpreter &interpreter) const
{
@@ -193,7 +204,7 @@ void VcsBaseClientImpl::vcsExecWithEditor(const Utils::FilePath &workingDirector
int VcsBaseClientImpl::vcsTimeoutS() const
{
- return m_baseSettings->timeout.value();
+ return m_baseSettings->timeout();
}
VcsCommand *VcsBaseClientImpl::createVcsCommand(const FilePath &defaultWorkingDir,
@@ -224,6 +235,7 @@ VcsBaseEditorWidget *VcsBaseClientImpl::createVcsEditor(Id kind, QString title,
connect(baseEditor, &VcsBaseEditorWidget::annotateRevisionRequested,
this, &VcsBaseClientImpl::annotateRevisionRequested);
baseEditor->setSource(source);
+ baseEditor->setDefaultLineNumber(1);
if (codec)
baseEditor->setCodec(codec);
}
diff --git a/src/plugins/vcsbase/vcsbaseclient.h b/src/plugins/vcsbase/vcsbaseclient.h
index 6703495c37..035e2f0042 100644
--- a/src/plugins/vcsbase/vcsbaseclient.h
+++ b/src/plugins/vcsbase/vcsbaseclient.h
@@ -23,6 +23,10 @@ class QTextCodec;
class QToolBar;
QT_END_NAMESPACE
+namespace Utils {
+class Process;
+}
+
namespace VcsBase {
class CommandResult;
@@ -56,6 +60,10 @@ public:
VcsCommand *createCommand(const Utils::FilePath &workingDirectory,
VcsBaseEditorWidget *editor = nullptr) const;
+ void setupCommand(Utils::Process &process,
+ const Utils::FilePath &workingDirectory,
+ const QStringList &args) const;
+
void enqueueJob(VcsCommand *cmd, const QStringList &args,
const Utils::ExitCodeInterpreter &interpreter = {}) const;
diff --git a/src/plugins/vcsbase/vcsbaseclientsettings.cpp b/src/plugins/vcsbase/vcsbaseclientsettings.cpp
index 7dfd8b3760..170437864a 100644
--- a/src/plugins/vcsbase/vcsbaseclientsettings.cpp
+++ b/src/plugins/vcsbase/vcsbaseclientsettings.cpp
@@ -6,15 +6,7 @@
#include "vcsbasetr.h"
#include <utils/algorithm.h>
-#include <utils/environment.h>
-#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/qtcassert.h>
-#include <utils/qtcsettings.h>
-#include <utils/stringutils.h>
-
-#include <QSettings>
-#include <QVariant>
using namespace Utils;
@@ -22,27 +14,19 @@ namespace VcsBase {
VcsBaseSettings::VcsBaseSettings()
{
- setAutoApply(false);
-
- registerAspect(&binaryPath);
binaryPath.setSettingsKey("BinaryPath");
- registerAspect(&userName);
userName.setSettingsKey("Username");
- registerAspect(&userEmail);
userEmail.setSettingsKey("UserEmail");
- registerAspect(&logCount);
logCount.setSettingsKey("LogCount");
logCount.setRange(0, 1000 * 1000);
logCount.setDefaultValue(100);
logCount.setLabelText(Tr::tr("Log count:"));
- registerAspect(&path);
path.setSettingsKey("Path");
- registerAspect(&timeout);
timeout.setSettingsKey("Timeout");
timeout.setRange(0, 3600 * 24 * 365);
timeout.setDefaultValue(30);
diff --git a/src/plugins/vcsbase/vcsbaseclientsettings.h b/src/plugins/vcsbase/vcsbaseclientsettings.h
index 87835755ca..6e1f227f21 100644
--- a/src/plugins/vcsbase/vcsbaseclientsettings.h
+++ b/src/plugins/vcsbase/vcsbaseclientsettings.h
@@ -5,27 +5,24 @@
#include "vcsbase_global.h"
-#include <utils/aspects.h>
+#include <coreplugin/dialogs/ioptionspage.h>
namespace VcsBase {
-class VCSBASE_EXPORT VcsBaseSettings : public Utils::AspectContainer
+class VCSBASE_EXPORT VcsBaseSettings : public Core::PagedSettings
{
public:
VcsBaseSettings();
~VcsBaseSettings();
- Utils::StringAspect binaryPath;
- Utils::StringAspect userName;
- Utils::StringAspect userEmail;
- Utils::IntegerAspect logCount;
- Utils::IntegerAspect timeout; // Seconds
- Utils::StringAspect path;
+ Utils::FilePathAspect binaryPath{this};
+ Utils::StringAspect userName{this};
+ Utils::StringAspect userEmail{this};
+ Utils::IntegerAspect logCount{this};
+ Utils::IntegerAspect timeout{this}; // Seconds
+ Utils::StringAspect path{this};
Utils::FilePaths searchPathList() const;
-
-private:
- QString m_settingsGroup;
};
} // namespace VcsBase
diff --git a/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp b/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp
index c0d31775af..696e96b291 100644
--- a/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp
+++ b/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp
@@ -2,28 +2,21 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "vcsbasediffeditorcontroller.h"
-#include "vcsplugin.h"
-#include <utils/asynctask.h>
+#include <extensionsystem/pluginmanager.h>
+
+#include <utils/async.h>
#include <utils/environment.h>
#include <utils/futuresynchronizer.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
using namespace DiffEditor;
+using namespace Tasking;
using namespace Utils;
namespace VcsBase {
-static void readPatch(QFutureInterface<QList<FileData>> &futureInterface, const QString &patch)
-{
- bool ok;
- const QList<FileData> &fileDataList = DiffUtils::readPatch(patch, &ok, &futureInterface);
- futureInterface.reportResult(fileDataList);
-}
-
-/////////////////////
-
class VcsBaseDiffEditorControllerPrivate
{
public:
@@ -32,7 +25,7 @@ public:
VcsBaseDiffEditorController *q;
Environment m_processEnvironment;
FilePath m_vcsBinary;
- const Tasking::TreeStorage<QString> m_inputStorage;
+ const TreeStorage<QString> m_inputStorage;
};
/////////////////////
@@ -47,33 +40,32 @@ VcsBaseDiffEditorController::~VcsBaseDiffEditorController()
delete d;
}
-Tasking::TreeStorage<QString> VcsBaseDiffEditorController::inputStorage() const
+TreeStorage<QString> VcsBaseDiffEditorController::inputStorage() const
{
return d->m_inputStorage;
}
-Tasking::TaskItem VcsBaseDiffEditorController::postProcessTask()
+TaskItem VcsBaseDiffEditorController::postProcessTask()
{
- using namespace Tasking;
-
- const auto setupDiffProcessor = [this](AsyncTask<QList<FileData>> &async) {
+ const auto setupDiffProcessor = [this](Async<QList<FileData>> &async) {
const QString *storage = inputStorage().activeStorage();
QTC_ASSERT(storage, qWarning("Using postProcessTask() requires putting inputStorage() "
"into task tree's root group."));
const QString inputData = storage ? *storage : QString();
- async.setAsyncCallData(readPatch, inputData);
- async.setFutureSynchronizer(Internal::VcsPlugin::futureSynchronizer());
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(&DiffUtils::readPatchWithPromise, inputData);
};
- const auto onDiffProcessorDone = [this](const AsyncTask<QList<FileData>> &async) {
- setDiffFiles(async.result());
+ const auto onDiffProcessorDone = [this](const Async<QList<FileData>> &async) {
+ setDiffFiles(async.isResultAvailable() ? async.result() : QList<FileData>());
+ // TODO: We should set the right starting line here
};
- const auto onDiffProcessorError = [this](const AsyncTask<QList<FileData>> &) {
+ const auto onDiffProcessorError = [this](const Async<QList<FileData>> &) {
setDiffFiles({});
};
- return Async<QList<FileData>>(setupDiffProcessor, onDiffProcessorDone, onDiffProcessorError);
+ return AsyncTask<QList<FileData>>(setupDiffProcessor, onDiffProcessorDone, onDiffProcessorError);
}
-void VcsBaseDiffEditorController::setupCommand(QtcProcess &process, const QStringList &args) const
+void VcsBaseDiffEditorController::setupCommand(Process &process, const QStringList &args) const
{
process.setEnvironment(d->m_processEnvironment);
process.setWorkingDirectory(workingDirectory());
diff --git a/src/plugins/vcsbase/vcsbasediffeditorcontroller.h b/src/plugins/vcsbase/vcsbasediffeditorcontroller.h
index 75672edb1f..63976bc26c 100644
--- a/src/plugins/vcsbase/vcsbasediffeditorcontroller.h
+++ b/src/plugins/vcsbase/vcsbasediffeditorcontroller.h
@@ -9,7 +9,7 @@
namespace Utils {
class Environment;
-class QtcProcess;
+class Process;
} // Utils
namespace VcsBase {
@@ -28,10 +28,10 @@ public:
void setVcsBinary(const Utils::FilePath &path);
protected:
- Utils::Tasking::TreeStorage<QString> inputStorage() const;
- Utils::Tasking::TaskItem postProcessTask();
+ Tasking::TreeStorage<QString> inputStorage() const;
+ Tasking::TaskItem postProcessTask();
- void setupCommand(Utils::QtcProcess &process, const QStringList &args) const;
+ void setupCommand(Utils::Process &process, const QStringList &args) const;
private:
friend class VcsBaseDiffEditorControllerPrivate;
diff --git a/src/plugins/vcsbase/vcsbaseeditor.cpp b/src/plugins/vcsbase/vcsbaseeditor.cpp
index c73c14f9f6..26c19c7f91 100644
--- a/src/plugins/vcsbase/vcsbaseeditor.cpp
+++ b/src/plugins/vcsbase/vcsbaseeditor.cpp
@@ -24,7 +24,7 @@
#include <projectexplorer/editorconfiguration.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <texteditor/textdocument.h>
#include <texteditor/textdocumentlayout.h>
@@ -1234,7 +1234,7 @@ static QTextCodec *findProjectCodec(const FilePath &dirPath)
{
typedef QList<ProjectExplorer::Project*> ProjectList;
// Try to find a project under which file tree the file is.
- const ProjectList projects = ProjectExplorer::SessionManager::projects();
+ const ProjectList projects = ProjectExplorer::ProjectManager::projects();
const ProjectExplorer::Project *p
= findOrDefault(projects, equal(&ProjectExplorer::Project::projectDirectory, dirPath));
return p ? p->editorConfiguration()->textCodec() : nullptr;
diff --git a/src/plugins/vcsbase/vcsbaseplugin.cpp b/src/plugins/vcsbase/vcsbaseplugin.cpp
index b277a2d068..b72657cdb3 100644
--- a/src/plugins/vcsbase/vcsbaseplugin.cpp
+++ b/src/plugins/vcsbase/vcsbaseplugin.cpp
@@ -9,14 +9,16 @@
#include "vcsplugin.h"
#include <coreplugin/documentmanager.h>
+#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/idocument.h>
-#include <coreplugin/editormanager/editormanager.h>
-#include <projectexplorer/projecttree.h>
+
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
+#include <projectexplorer/projecttree.h>
+
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QAction>
#include <QDebug>
@@ -195,7 +197,7 @@ StateListener::StateListener(QObject *parent) : QObject(parent)
connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged,
this, &StateListener::slotStateChanged);
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, &StateListener::slotStateChanged);
EditorManager::setWindowTitleVcsTopicHandler(&StateListener::windowTitleVcsTopic);
@@ -213,7 +215,7 @@ QString StateListener::windowTitleVcsTopic(const FilePath &filePath)
searchPath = filePath.absolutePath();
} else {
// use single project's information if there is only one loaded.
- const QList<Project *> projects = SessionManager::projects();
+ const QList<Project *> projects = ProjectManager::projects();
if (projects.size() == 1)
searchPath = projects.first()->projectDirectory();
}
@@ -282,7 +284,7 @@ void StateListener::slotStateChanged()
IVersionControl *projectControl = nullptr;
Project *currentProject = ProjectTree::currentProject();
if (!currentProject)
- currentProject = SessionManager::startupProject();
+ currentProject = ProjectManager::startupProject();
if (currentProject) {
state.currentProjectPath = currentProject->projectDirectory();
@@ -591,9 +593,27 @@ bool VcsBasePluginPrivate::enableMenuAction(ActionState as, QAction *menuAction)
QString VcsBasePluginPrivate::commitDisplayName() const
{
+ //: Name of the "commit" action of the VCS
return Tr::tr("Commit", "name of \"commit\" action of the VCS.");
}
+QString VcsBasePluginPrivate::commitAbortTitle() const
+{
+ return Tr::tr("Close Commit Editor");
+}
+
+QString VcsBasePluginPrivate::commitAbortMessage() const
+{
+ return Tr::tr("Closing this editor will abort the commit.");
+}
+
+QString VcsBasePluginPrivate::commitErrorMessage(const QString &error) const
+{
+ if (error.isEmpty())
+ return Tr::tr("Cannot commit.");
+ return Tr::tr("Cannot commit: %1.").arg(error);
+}
+
void VcsBasePluginPrivate::commitFromEditor()
{
QTC_ASSERT(m_submitEditor, return);
diff --git a/src/plugins/vcsbase/vcsbaseplugin.h b/src/plugins/vcsbase/vcsbaseplugin.h
index af96e12631..5a4c9ae6b3 100644
--- a/src/plugins/vcsbase/vcsbaseplugin.h
+++ b/src/plugins/vcsbase/vcsbaseplugin.h
@@ -135,6 +135,9 @@ public:
const QStringList &extraArgs);
// Display name of the commit action
virtual QString commitDisplayName() const;
+ virtual QString commitAbortTitle() const;
+ virtual QString commitAbortMessage() const;
+ virtual QString commitErrorMessage(const QString &error) const;
void commitFromEditor();
virtual bool activateCommit() = 0;
diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp
index eaa825adb1..5ddab0da77 100644
--- a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp
+++ b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp
@@ -28,8 +28,8 @@
#include <utils/completingtextedit.h>
#include <utils/fileutils.h>
#include <utils/icon.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/temporarydirectory.h>
#include <utils/theme/theme.h>
@@ -37,7 +37,7 @@
#include <texteditor/texteditorsettings.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <QDir>
#include <QFileInfo>
@@ -230,8 +230,8 @@ VcsBaseSubmitEditor::~VcsBaseSubmitEditor()
void VcsBaseSubmitEditor::slotUpdateEditorSettings()
{
const CommonVcsSettings &s = VcsPlugin::instance()->settings();
- setLineWrapWidth(s.lineWrapWidth.value());
- setLineWrap(s.lineWrap.value());
+ setLineWrapWidth(s.lineWrapWidth());
+ setLineWrap(s.lineWrap());
}
// Return a trimmed list of non-empty field texts
@@ -453,11 +453,7 @@ void VcsBaseSubmitEditor::accept(VcsBasePluginPrivate *plugin)
QString errorMessage;
const bool canCommit = checkSubmitMessage(&errorMessage) && submitWidget->canSubmit(&errorMessage);
if (!canCommit) {
- VcsOutputWindow::appendError(
- Tr::tr("Cannot %1%2.",
- "%2 is an optional error message with ': ' prefix. Don't add space in front.")
- .arg(plugin->commitDisplayName().toLower(),
- errorMessage.isEmpty() ? errorMessage : ": " + errorMessage));
+ VcsOutputWindow::appendError(plugin->commitErrorMessage(errorMessage));
} else if (plugin->activateCommit()) {
close();
}
@@ -480,12 +476,10 @@ bool VcsBaseSubmitEditor::promptSubmit(VcsBasePluginPrivate *plugin)
if (!submitWidget->isEnabled() || !submitWidget->isEdited())
return true;
- const QString commitName = plugin->commitDisplayName();
QMessageBox mb(Core::ICore::dialogParent());
- mb.setWindowTitle(Tr::tr("Close %1 %2 Editor").arg(plugin->displayName(), commitName));
+ mb.setWindowTitle(plugin->commitAbortTitle());
mb.setIcon(QMessageBox::Warning);
- mb.setText(Tr::tr("Closing this editor will abort the %1.")
- .arg(commitName.toLower()));
+ mb.setText(plugin->commitAbortMessage());
mb.setStandardButtons(QMessageBox::Close | QMessageBox::Cancel);
// On Windows there is no mnemonic for Close. Set it explicitly.
mb.button(QMessageBox::Close)->setText(Tr::tr("&Close"));
@@ -561,7 +555,7 @@ bool VcsBaseSubmitEditor::runSubmitMessageCheckScript(const QString &checkScript
// Run check process
VcsOutputWindow::appendShellCommandLine(msgCheckScript(d->m_checkScriptWorkingDirectory,
checkScript));
- QtcProcess checkProcess;
+ Process checkProcess;
if (!d->m_checkScriptWorkingDirectory.isEmpty())
checkProcess.setWorkingDirectory(d->m_checkScriptWorkingDirectory);
checkProcess.setCommand({FilePath::fromString(checkScript), {saver.filePath().toString()}});
@@ -606,7 +600,7 @@ void VcsBaseSubmitEditor::filterUntrackedFilesOfProject(const FilePath &reposito
{
for (QStringList::iterator it = untrackedFiles->begin(); it != untrackedFiles->end(); ) {
const FilePath path = repositoryDirectory.resolvePath(*it).absoluteFilePath();
- if (ProjectExplorer::SessionManager::projectForFile(path))
+ if (ProjectExplorer::ProjectManager::projectForFile(path))
++it;
else
it = untrackedFiles->erase(it);
diff --git a/src/plugins/vcsbase/vcscommand.cpp b/src/plugins/vcsbase/vcscommand.cpp
index 5a4f43d28c..885ff004da 100644
--- a/src/plugins/vcsbase/vcscommand.cpp
+++ b/src/plugins/vcsbase/vcscommand.cpp
@@ -10,8 +10,8 @@
#include <utils/environment.h>
#include <utils/globalfilechangeblocker.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/threadutils.h>
#include <QTextCodec>
@@ -55,10 +55,10 @@ public:
void setup();
void cleanup();
- void setupProcess(QtcProcess *process, const Job &job);
- void installStdCallbacks(QtcProcess *process);
+ void setupProcess(Process *process, const Job &job);
+ void installStdCallbacks(Process *process);
EventLoopMode eventLoopMode() const;
- void handleDone(QtcProcess *process);
+ void handleDone(Process *process);
void startAll();
void startNextJob();
void processDone();
@@ -73,7 +73,7 @@ public:
QList<Job> m_jobs;
int m_currentJob = 0;
- std::unique_ptr<QtcProcess> m_process;
+ std::unique_ptr<Process> m_process;
QString m_stdOut;
QString m_stdErr;
ProcessResult m_result = ProcessResult::StartFailed;
@@ -99,7 +99,7 @@ void VcsCommandPrivate::cleanup()
GlobalFileChangeBlocker::instance()->forceBlocked(false);
}
-void VcsCommandPrivate::setupProcess(QtcProcess *process, const Job &job)
+void VcsCommandPrivate::setupProcess(Process *process, const Job &job)
{
process->setExitCodeInterpreter(job.exitCodeInterpreter);
process->setTimeoutS(job.timeoutS);
@@ -127,12 +127,12 @@ void VcsCommandPrivate::setupProcess(QtcProcess *process, const Job &job)
progress->setProgressParser(m_progressParser);
}
-void VcsCommandPrivate::installStdCallbacks(QtcProcess *process)
+void VcsCommandPrivate::installStdCallbacks(Process *process)
{
if (!(m_flags & RunFlags::MergeOutputChannels) && (m_flags & RunFlags::ProgressiveOutput
|| m_progressParser || !(m_flags & RunFlags::SuppressStdErr))) {
process->setTextChannelMode(Channel::Error, TextChannelMode::MultiLine);
- connect(process, &QtcProcess::textOnStandardError, this, [this](const QString &text) {
+ connect(process, &Process::textOnStandardError, this, [this](const QString &text) {
if (!(m_flags & RunFlags::SuppressStdErr))
VcsOutputWindow::appendError(text);
if (m_flags & RunFlags::ProgressiveOutput)
@@ -142,7 +142,7 @@ void VcsCommandPrivate::installStdCallbacks(QtcProcess *process)
if (m_progressParser || m_flags & RunFlags::ProgressiveOutput
|| m_flags & RunFlags::ShowStdOut) {
process->setTextChannelMode(Channel::Output, TextChannelMode::MultiLine);
- connect(process, &QtcProcess::textOnStandardOutput, this, [this](const QString &text) {
+ connect(process, &Process::textOnStandardOutput, this, [this](const QString &text) {
if (m_flags & RunFlags::ShowStdOut)
VcsOutputWindow::append(text);
if (m_flags & RunFlags::ProgressiveOutput)
@@ -158,7 +158,7 @@ EventLoopMode VcsCommandPrivate::eventLoopMode() const
return EventLoopMode::Off;
}
-void VcsCommandPrivate::handleDone(QtcProcess *process)
+void VcsCommandPrivate::handleDone(Process *process)
{
// Success/Fail message in appropriate window?
if (process->result() == ProcessResult::FinishedWithSuccess) {
@@ -187,8 +187,8 @@ void VcsCommandPrivate::startAll()
void VcsCommandPrivate::startNextJob()
{
QTC_ASSERT(m_currentJob < m_jobs.count(), return);
- m_process.reset(new QtcProcess);
- connect(m_process.get(), &QtcProcess::done, this, &VcsCommandPrivate::processDone);
+ m_process.reset(new Process);
+ connect(m_process.get(), &Process::done, this, &VcsCommandPrivate::processDone);
setupProcess(m_process.get(), m_jobs.at(m_currentJob));
m_process->start();
}
@@ -297,7 +297,7 @@ CommandResult VcsCommand::runBlocking(const Utils::FilePath &workingDirectory,
CommandResult VcsCommand::runBlockingHelper(const CommandLine &command, int timeoutS)
{
- QtcProcess process;
+ Process process;
if (command.executable().isEmpty())
return {};
@@ -321,7 +321,7 @@ void VcsCommand::setProgressParser(const ProgressParser &parser)
d->m_progressParser = parser;
}
-CommandResult::CommandResult(const QtcProcess &process)
+CommandResult::CommandResult(const Process &process)
: m_result(process.result())
, m_exitCode(process.exitCode())
, m_exitMessage(process.exitMessage())
diff --git a/src/plugins/vcsbase/vcscommand.h b/src/plugins/vcsbase/vcscommand.h
index 568d109580..1cc4d5db54 100644
--- a/src/plugins/vcsbase/vcscommand.h
+++ b/src/plugins/vcsbase/vcscommand.h
@@ -20,7 +20,7 @@ QT_END_NAMESPACE
namespace Utils {
class CommandLine;
class Environment;
-class QtcProcess;
+class Process;
}
namespace VcsBase {
@@ -33,7 +33,7 @@ class VCSBASE_EXPORT CommandResult
{
public:
CommandResult() = default;
- CommandResult(const Utils::QtcProcess &process);
+ CommandResult(const Utils::Process &process);
CommandResult(const VcsCommand &command);
CommandResult(Utils::ProcessResult result, const QString &exitMessage)
: m_result(result), m_exitMessage(exitMessage) {}
diff --git a/src/plugins/vcsbase/vcsenums.h b/src/plugins/vcsbase/vcsenums.h
index 69f85f2e5d..5a5b2c42a7 100644
--- a/src/plugins/vcsbase/vcsenums.h
+++ b/src/plugins/vcsbase/vcsenums.h
@@ -9,7 +9,7 @@ namespace VcsBase {
enum class RunFlags {
None = 0, // Empty.
- // QtcProcess related
+ // Process related
MergeOutputChannels = (1 << 0), // See QProcess::ProcessChannelMode::MergedChannels.
ForceCLocale = (1 << 1), // Force C-locale, sets LANG and LANGUAGE env vars to "C".
UseEventLoop = (1 << 2), // Use event loop when executed in UI thread with
diff --git a/src/plugins/vcsbase/vcsoutputwindow.cpp b/src/plugins/vcsbase/vcsoutputwindow.cpp
index 245de85134..816aa79f02 100644
--- a/src/plugins/vcsbase/vcsoutputwindow.cpp
+++ b/src/plugins/vcsbase/vcsoutputwindow.cpp
@@ -14,7 +14,7 @@
#include <texteditor/texteditorsettings.h>
#include <utils/filepath.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/theme/theme.h>
#include <QAction>
@@ -444,7 +444,7 @@ QString VcsOutputWindow::msgExecutionLogEntry(const FilePath &workingDir, const
+ ' ' + formatArguments(command.splitArguments());
if (workingDir.isEmpty())
return Tr::tr("Running: %1").arg(maskedCmdline) + '\n';
- return Tr::tr("Running in %1: %2").arg(workingDir.toUserOutput(), maskedCmdline) + '\n';
+ return Tr::tr("Running in \"%1\": %2.").arg(workingDir.toUserOutput(), maskedCmdline) + '\n';
}
void VcsOutputWindow::appendShellCommandLine(const QString &text)
diff --git a/src/plugins/vcsbase/vcsplugin.cpp b/src/plugins/vcsbase/vcsplugin.cpp
index e5108942f5..0596f406b8 100644
--- a/src/plugins/vcsbase/vcsplugin.cpp
+++ b/src/plugins/vcsbase/vcsplugin.cpp
@@ -23,7 +23,6 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projecttree.h>
-#include <utils/futuresynchronizer.h>
#include <utils/macroexpander.h>
#include <utils/qtcassert.h>
@@ -33,15 +32,47 @@ using namespace Core;
using namespace ProjectExplorer;
using namespace Utils;
-namespace VcsBase {
-namespace Internal {
+namespace VcsBase::Internal {
class VcsPluginPrivate
{
public:
+ explicit VcsPluginPrivate(VcsPlugin *plugin)
+ : q(plugin)
+ {
+ QObject::connect(&m_settingsPage.settings(), &CommonVcsSettings::settingsChanged,
+ [this] { slotSettingsChanged(); });
+ slotSettingsChanged();
+ }
+
+ QStandardItemModel *nickNameModel()
+ {
+ if (!m_nickNameModel) {
+ m_nickNameModel = NickNameDialog::createModel(q);
+ populateNickNameModel();
+ }
+ return m_nickNameModel;
+ }
+
+ void populateNickNameModel()
+ {
+ QString errorMessage;
+ if (!NickNameDialog::populateModelFromMailCapFile(m_settingsPage.settings().nickNameMailMap.filePath(),
+ m_nickNameModel,
+ &errorMessage)) {
+ qWarning("%s", qPrintable(errorMessage));
+ }
+ }
+
+ void slotSettingsChanged()
+ {
+ if (m_nickNameModel)
+ populateNickNameModel();
+ }
+
+ VcsPlugin *q;
CommonOptionsPage m_settingsPage;
QStandardItemModel *m_nickNameModel = nullptr;
- FutureSynchronizer m_futureSynchronizer;
};
static VcsPlugin *m_instance = nullptr;
@@ -61,7 +92,7 @@ VcsPlugin::~VcsPlugin()
void VcsPlugin::initialize()
{
- d = new VcsPluginPrivate;
+ d = new VcsPluginPrivate(this);
EditorManager::addCloseEditorListener([this](IEditor *editor) -> bool {
bool result = true;
@@ -70,11 +101,8 @@ void VcsPlugin::initialize()
return result;
});
- connect(&d->m_settingsPage, &CommonOptionsPage::settingsChanged,
+ connect(&d->m_settingsPage.settings(), &CommonVcsSettings::settingsChanged,
this, &VcsPlugin::settingsChanged);
- connect(&d->m_settingsPage, &CommonOptionsPage::settingsChanged,
- this, &VcsPlugin::slotSettingsChanged);
- slotSettingsChanged();
JsonWizardFactory::registerPageFactory(new Internal::VcsConfigurationPageFactory);
JsonWizardFactory::registerPageFactory(new Internal::VcsCommandPageFactory);
@@ -123,37 +151,11 @@ CommonVcsSettings &VcsPlugin::settings() const
return d->m_settingsPage.settings();
}
-FutureSynchronizer *VcsPlugin::futureSynchronizer()
-{
- QTC_ASSERT(m_instance, return nullptr);
- return &m_instance->d->m_futureSynchronizer;
-}
-
/* Delayed creation/update of the nick name model. */
QStandardItemModel *VcsPlugin::nickNameModel()
{
- if (!d->m_nickNameModel) {
- d->m_nickNameModel = NickNameDialog::createModel(this);
- populateNickNameModel();
- }
- return d->m_nickNameModel;
-}
-
-void VcsPlugin::populateNickNameModel()
-{
- QString errorMessage;
- if (!NickNameDialog::populateModelFromMailCapFile(settings().nickNameMailMap.filePath(),
- d->m_nickNameModel,
- &errorMessage)) {
- qWarning("%s", qPrintable(errorMessage));
- }
-}
-
-void VcsPlugin::slotSettingsChanged()
-{
- if (d->m_nickNameModel)
- populateNickNameModel();
+ QTC_ASSERT(d, return nullptr);
+ return d->nickNameModel();
}
-} // namespace Internal
-} // namespace VcsBase
+} // VcsBase::Internal
diff --git a/src/plugins/vcsbase/vcsplugin.h b/src/plugins/vcsbase/vcsplugin.h
index 83157fc140..6d4b35c11f 100644
--- a/src/plugins/vcsbase/vcsplugin.h
+++ b/src/plugins/vcsbase/vcsplugin.h
@@ -5,14 +5,10 @@
#include <extensionsystem/iplugin.h>
-#include <QFuture>
-
QT_BEGIN_NAMESPACE
class QStandardItemModel;
QT_END_NAMESPACE
-namespace Utils { class FutureSynchronizer; }
-
namespace VcsBase {
class VcsBaseSubmitEditor;
@@ -36,8 +32,6 @@ public:
CommonVcsSettings &settings() const;
- static Utils::FutureSynchronizer *futureSynchronizer();
-
// Model of user nick names used for the submit
// editor. Stored centrally here to achieve delayed
// initialization and updating on settings change.
@@ -48,9 +42,6 @@ signals:
void submitEditorAboutToClose(VcsBase::VcsBaseSubmitEditor *e, bool *result);
private:
- void slotSettingsChanged();
- void populateNickNameModel();
-
class VcsPluginPrivate *d = nullptr;
};
diff --git a/src/plugins/vcsbase/wizard/vcscommandpage.cpp b/src/plugins/vcsbase/wizard/vcscommandpage.cpp
index 1fef5540f4..0877e3ffbe 100644
--- a/src/plugins/vcsbase/wizard/vcscommandpage.cpp
+++ b/src/plugins/vcsbase/wizard/vcscommandpage.cpp
@@ -212,7 +212,6 @@ bool VcsCommandPageFactory::validateData(Id typeId, const QVariant &data, QStrin
VcsCommandPage::VcsCommandPage()
: m_startedStatus(Tr::tr("Command started..."))
{
- resize(264, 200);
auto verticalLayout = new QVBoxLayout(this);
m_logPlainTextEdit = new QPlainTextEdit;
m_formatter = new OutputFormatter;
diff --git a/src/plugins/webassembly/CMakeLists.txt b/src/plugins/webassembly/CMakeLists.txt
index 3ee300b09f..6609357f01 100644
--- a/src/plugins/webassembly/CMakeLists.txt
+++ b/src/plugins/webassembly/CMakeLists.txt
@@ -4,16 +4,16 @@ add_qtc_plugin(WebAssembly
SOURCES
webassembly.qrc
webassembly_global.h
- webassemblytr.h
webassemblyconstants.h
webassemblydevice.cpp webassemblydevice.h
webassemblyemsdk.cpp webassemblyemsdk.h
- webassemblyoptionspage.cpp webassemblyoptionspage.h
webassemblyplugin.cpp webassemblyplugin.h
webassemblyqtversion.cpp webassemblyqtversion.h
- webassemblyrunconfigurationaspects.cpp webassemblyrunconfigurationaspects.h
webassemblyrunconfiguration.cpp webassemblyrunconfiguration.h
+ webassemblyrunconfigurationaspects.cpp webassemblyrunconfigurationaspects.h
+ webassemblysettings.cpp webassemblysettings.h
webassemblytoolchain.cpp webassemblytoolchain.h
+ webassemblytr.h
)
extend_qtc_plugin(WebAssembly
diff --git a/src/plugins/webassembly/webassembly.qbs b/src/plugins/webassembly/webassembly.qbs
index 9c6e95f99f..12b639238c 100644
--- a/src/plugins/webassembly/webassembly.qbs
+++ b/src/plugins/webassembly/webassembly.qbs
@@ -13,29 +13,28 @@ QtcPlugin {
files: [
"webassembly.qrc",
- "webassembly_global.h", "webassemblytr.h",
+ "webassembly_global.h",
"webassemblyconstants.h",
"webassemblydevice.cpp",
"webassemblydevice.h",
"webassemblyemsdk.cpp",
"webassemblyemsdk.h",
- "webassemblyoptionspage.cpp",
- "webassemblyoptionspage.h",
"webassemblyplugin.cpp",
"webassemblyplugin.h",
"webassemblyqtversion.cpp",
"webassemblyqtversion.h",
- "webassemblyrunconfigurationaspects.cpp",
- "webassemblyrunconfigurationaspects.h",
"webassemblyrunconfiguration.cpp",
"webassemblyrunconfiguration.h",
+ "webassemblyrunconfigurationaspects.cpp",
+ "webassemblyrunconfigurationaspects.h",
+ "webassemblysettings.cpp",
+ "webassemblysettings.h",
"webassemblytoolchain.cpp",
"webassemblytoolchain.h",
+ "webassemblytr.h",
]
- Group {
- name: "Unit tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
"webassembly_test.cpp",
"webassembly_test.h",
diff --git a/src/plugins/webassembly/webassemblyconstants.h b/src/plugins/webassembly/webassemblyconstants.h
index 40f49f73a4..56315b0b40 100644
--- a/src/plugins/webassembly/webassemblyconstants.h
+++ b/src/plugins/webassembly/webassemblyconstants.h
@@ -14,8 +14,5 @@ const char WEBASSEMBLY_DEVICE_DEVICE_ID[] = "WebAssembly Device";
const char WEBASSEMBLY_QT_VERSION[] = "Qt4ProjectManager.QtVersion.WebAssembly";
const char WEBASSEMBLY_RUNCONFIGURATION_EMRUN[] = "WebAssembly.RunConfiguration.Emrun";
-const char SETTINGS_GROUP[] = "WebAssembly";
-const char SETTINGS_KEY_EMSDK[] = "EmSdk";
-
} // namespace WebAssembly
} // namespace Constants
diff --git a/src/plugins/webassembly/webassemblydevice.h b/src/plugins/webassembly/webassemblydevice.h
index 6ff4a2481e..520d0fb535 100644
--- a/src/plugins/webassembly/webassemblydevice.h
+++ b/src/plugins/webassembly/webassemblydevice.h
@@ -4,6 +4,7 @@
#pragma once
#include <projectexplorer/devicesupport/desktopdevice.h>
+#include <projectexplorer/devicesupport/idevicefactory.h>
namespace WebAssembly {
namespace Internal {
diff --git a/src/plugins/webassembly/webassemblyemsdk.cpp b/src/plugins/webassembly/webassemblyemsdk.cpp
index 4acf6589c4..ad12f12e62 100644
--- a/src/plugins/webassembly/webassemblyemsdk.cpp
+++ b/src/plugins/webassembly/webassemblyemsdk.cpp
@@ -7,29 +7,36 @@
#include <coreplugin/icore.h>
#include <utils/environment.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/hostosinfo.h>
#include <QCache>
-#include <QSettings>
using namespace Utils;
namespace WebAssembly::Internal::WebAssemblyEmSdk {
-using EmSdkEnvCache = QCache<QString, QString>;
-Q_GLOBAL_STATIC_WITH_ARGS(EmSdkEnvCache, emSdkEnvCache, (10))
-using EmSdkVersionCache = QCache<QString, QVersionNumber>;
-Q_GLOBAL_STATIC_WITH_ARGS(EmSdkVersionCache, emSdkVersionCache, (10))
+using EmSdkEnvCache = QCache<FilePath, QString>;
+static EmSdkEnvCache *emSdkEnvCache()
+{
+ static EmSdkEnvCache cache(10);
+ return &cache;
+}
+
+using EmSdkVersionCache = QCache<FilePath, QVersionNumber>;
+static EmSdkVersionCache *emSdkVersionCache()
+{
+ static EmSdkVersionCache cache(10);
+ return &cache;
+}
static QString emSdkEnvOutput(const FilePath &sdkRoot)
{
- const QString cacheKey = sdkRoot.toString();
const bool isWindows = sdkRoot.osType() == OsTypeWindows;
- if (!emSdkEnvCache()->contains(cacheKey)) {
+ if (!emSdkEnvCache()->contains(sdkRoot)) {
const FilePath scriptFile = sdkRoot.pathAppended(QLatin1String("emsdk_env") +
(isWindows ? ".bat" : ".sh"));
- QtcProcess emSdkEnv;
+ Process emSdkEnv;
if (isWindows) {
emSdkEnv.setCommand(CommandLine(scriptFile));
} else {
@@ -38,9 +45,9 @@ static QString emSdkEnvOutput(const FilePath &sdkRoot)
}
emSdkEnv.runBlocking();
const QString output = emSdkEnv.allOutput();
- emSdkEnvCache()->insert(cacheKey, new QString(output));
+ emSdkEnvCache()->insert(sdkRoot, new QString(output));
}
- return *emSdkEnvCache()->object(cacheKey);
+ return *emSdkEnvCache()->object(sdkRoot);
}
void parseEmSdkEnvOutputAndAddToEnv(const QString &output, Environment &env)
@@ -81,37 +88,21 @@ QVersionNumber version(const FilePath &sdkRoot)
{
if (!sdkRoot.exists())
return {};
- const QString cacheKey = sdkRoot.toString();
- if (!emSdkVersionCache()->contains(cacheKey)) {
+ if (!emSdkVersionCache()->contains(sdkRoot)) {
Environment env = sdkRoot.deviceEnvironment();
addToEnvironment(sdkRoot, env);
QLatin1String scriptFile{sdkRoot.osType() == OsType::OsTypeWindows ? "emcc.bat" : "emcc"};
FilePath script = sdkRoot.withNewPath(scriptFile).searchInDirectories(env.path());
const CommandLine command(script, {"-dumpversion"});
- QtcProcess emcc;
+ Process emcc;
emcc.setCommand(command);
emcc.setEnvironment(env);
emcc.runBlocking();
const QString version = emcc.cleanedStdOut();
- emSdkVersionCache()->insert(cacheKey,
+ emSdkVersionCache()->insert(sdkRoot,
new QVersionNumber(QVersionNumber::fromString(version)));
}
- return *emSdkVersionCache()->object(cacheKey);
-}
-
-void registerEmSdk(const FilePath &sdkRoot)
-{
- QSettings *s = Core::ICore::settings();
- s->setValue(QLatin1String(Constants::SETTINGS_GROUP) + '/'
- + QLatin1String(Constants::SETTINGS_KEY_EMSDK), sdkRoot.toString());
-}
-
-FilePath registeredEmSdk()
-{
- QSettings *s = Core::ICore::settings();
- const QString path = s->value(QLatin1String(Constants::SETTINGS_GROUP) + '/'
- + QLatin1String(Constants::SETTINGS_KEY_EMSDK)).toString();
- return FilePath::fromUserInput(path);
+ return *emSdkVersionCache()->object(sdkRoot);
}
void clearCaches()
diff --git a/src/plugins/webassembly/webassemblyemsdk.h b/src/plugins/webassembly/webassemblyemsdk.h
index 8310e0320f..55d83abcd2 100644
--- a/src/plugins/webassembly/webassemblyemsdk.h
+++ b/src/plugins/webassembly/webassemblyemsdk.h
@@ -16,8 +16,6 @@ bool isValid(const Utils::FilePath &sdkRoot);
void parseEmSdkEnvOutputAndAddToEnv(const QString &output, Utils::Environment &env);
void addToEnvironment(const Utils::FilePath &sdkRoot, Utils::Environment &env);
QVersionNumber version(const Utils::FilePath &sdkRoot);
-void registerEmSdk(const Utils::FilePath &sdkRoot);
-Utils::FilePath registeredEmSdk();
void clearCaches();
} // WebAssembly::Internal::WebAssemblyEmSdk
diff --git a/src/plugins/webassembly/webassemblyoptionspage.cpp b/src/plugins/webassembly/webassemblyoptionspage.cpp
deleted file mode 100644
index 1a4524b945..0000000000
--- a/src/plugins/webassembly/webassemblyoptionspage.cpp
+++ /dev/null
@@ -1,175 +0,0 @@
-// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "webassemblyconstants.h"
-#include "webassemblyemsdk.h"
-#include "webassemblyoptionspage.h"
-#include "webassemblyqtversion.h"
-#include "webassemblytoolchain.h"
-#include "webassemblytr.h"
-
-#include <coreplugin/icore.h>
-#include <utils/environment.h>
-#include <utils/infolabel.h>
-#include <utils/pathchooser.h>
-#include <utils/utilsicons.h>
-
-#include <QGroupBox>
-#include <QTextBrowser>
-#include <QVBoxLayout>
-
-using namespace Utils;
-
-namespace WebAssembly {
-namespace Internal {
-
-class WebAssemblyOptionsWidget : public Core::IOptionsPageWidget
-{
-public:
- WebAssemblyOptionsWidget();
-
- void updateStatus();
-
-private:
- void apply() final;
- void showEvent(QShowEvent *event) final;
-
- PathChooser *m_emSdkPathChooser;
- InfoLabel *m_emSdkVersionDisplay;
- QGroupBox *m_emSdkEnvGroupBox;
- QTextBrowser *m_emSdkEnvDisplay;
- InfoLabel *m_qtVersionDisplay;
-};
-
-WebAssemblyOptionsWidget::WebAssemblyOptionsWidget()
-{
- auto mainLayout = new QVBoxLayout(this);
-
- {
- auto pathChooserBox = new QGroupBox(Tr::tr("Emscripten SDK path:"));
- pathChooserBox->setFlat(true);
- auto layout = new QVBoxLayout(pathChooserBox);
- auto instruction = new QLabel(
- Tr::tr("Select the root directory of an installed %1. "
- "Ensure that the activated SDK version is compatible with the %2 "
- "or %3 version that you plan to develop against.")
- .arg(R"(<a href="https://emscripten.org/docs/getting_started/downloads.html">Emscripten SDK</a>)")
- .arg(R"(<a href="https://doc.qt.io/qt-5/wasm.html#install-emscripten">Qt 5</a>)")
- .arg(R"(<a href="https://doc.qt.io/qt-6/wasm.html#install-emscripten">Qt 6</a>)"));
-
- instruction->setOpenExternalLinks(true);
- instruction->setWordWrap(true);
- layout->addWidget(instruction);
- m_emSdkPathChooser = new PathChooser(this);
- m_emSdkPathChooser->setExpectedKind(PathChooser::Directory);
- m_emSdkPathChooser->setInitialBrowsePathBackup(FileUtils::homePath());
- m_emSdkPathChooser->setFilePath(WebAssemblyEmSdk::registeredEmSdk());
- connect(m_emSdkPathChooser, &PathChooser::textChanged,
- this, &WebAssemblyOptionsWidget::updateStatus);
- layout->addWidget(m_emSdkPathChooser);
- m_emSdkVersionDisplay = new InfoLabel(this);
- m_emSdkVersionDisplay->setElideMode(Qt::ElideNone);
- m_emSdkVersionDisplay->setWordWrap(true);
- layout->addWidget(m_emSdkVersionDisplay);
- mainLayout->addWidget(pathChooserBox);
- }
-
- {
- m_emSdkEnvGroupBox = new QGroupBox(Tr::tr("Emscripten SDK environment:"));
- m_emSdkEnvGroupBox->setFlat(true);
- m_emSdkEnvGroupBox->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::MinimumExpanding);
- auto layout = new QVBoxLayout(m_emSdkEnvGroupBox);
- m_emSdkEnvDisplay = new QTextBrowser;
- m_emSdkEnvDisplay->setLineWrapMode(QTextBrowser::NoWrap);
- m_emSdkEnvDisplay->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
- layout->addWidget(m_emSdkEnvDisplay);
- mainLayout->addWidget(m_emSdkEnvGroupBox, 1);
- }
-
- mainLayout->addStretch();
-
- {
- const QString minimumSupportedQtVersion =
- WebAssemblyQtVersion::minimumSupportedQtVersion().toString();
- m_qtVersionDisplay = new InfoLabel(
- Tr::tr("Note: %1 supports Qt %2 for WebAssembly and higher. "
- "Your installed lower Qt version(s) are not supported.")
- .arg(Core::ICore::versionString(), minimumSupportedQtVersion),
- InfoLabel::Warning);
- m_qtVersionDisplay->setElideMode(Qt::ElideNone);
- m_qtVersionDisplay->setWordWrap(true);
- mainLayout->addWidget(m_qtVersionDisplay);
- }
-}
-
-static QString environmentDisplay(const FilePath &sdkRoot)
-{
- Environment env;
- WebAssemblyEmSdk::addToEnvironment(sdkRoot, env);
- QString result;
- auto h4 = [](const QString &text) { return QString("<h4>" + text + "</h4>"); };
- result.append(h4(Tr::tr("Adding directories to PATH:")));
- result.append(env.value("PATH").replace(OsSpecificAspects::pathListSeparator(sdkRoot.osType()), "<br/>"));
- result.append(h4(Tr::tr("Setting environment variables:")));
- for (const QString &envVar : env.toStringList()) {
- if (!envVar.startsWith("PATH")) // Path was already printed out above
- result.append(envVar + "<br/>");
- }
- return result;
-}
-
-void WebAssemblyOptionsWidget::updateStatus()
-{
- WebAssemblyEmSdk::clearCaches();
-
- const FilePath sdkPath = m_emSdkPathChooser->filePath();
- const bool sdkValid = sdkPath.exists() && WebAssemblyEmSdk::isValid(sdkPath);
-
- m_emSdkVersionDisplay->setVisible(sdkValid);
- m_emSdkEnvGroupBox->setVisible(sdkValid);
-
- if (sdkValid) {
- const QVersionNumber sdkVersion = WebAssemblyEmSdk::version(sdkPath);
- const QVersionNumber minVersion = WebAssemblyToolChain::minimumSupportedEmSdkVersion();
- const bool versionTooLow = sdkVersion < minVersion;
- m_emSdkVersionDisplay->setType(versionTooLow ? InfoLabel::NotOk : InfoLabel::Ok);
- auto bold = [](const QString &text) { return QString("<b>" + text + "</b>"); };
- m_emSdkVersionDisplay->setText(
- versionTooLow ? Tr::tr("The activated version %1 is not supported by %2. "
- "Activate version %3 or higher.")
- .arg(bold(sdkVersion.toString()))
- .arg(bold(Core::ICore::versionString()))
- .arg(bold(minVersion.toString()))
- : Tr::tr("Activated version: %1")
- .arg(bold(sdkVersion.toString())));
- m_emSdkEnvDisplay->setText(environmentDisplay(sdkPath));
- }
-
- m_qtVersionDisplay->setVisible(WebAssemblyQtVersion::isUnsupportedQtVersionInstalled());
-}
-
-void WebAssemblyOptionsWidget::showEvent(QShowEvent *event)
-{
- Q_UNUSED(event)
- updateStatus();
-}
-
-void WebAssemblyOptionsWidget::apply()
-{
- const FilePath sdkPath = m_emSdkPathChooser->filePath();
- if (!WebAssemblyEmSdk::isValid(sdkPath))
- return;
- WebAssemblyEmSdk::registerEmSdk(sdkPath);
- WebAssemblyToolChain::registerToolChains();
-}
-
-WebAssemblyOptionsPage::WebAssemblyOptionsPage()
-{
- setId(Id(Constants::SETTINGS_ID));
- setDisplayName(Tr::tr("WebAssembly"));
- setCategory(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY);
- setWidgetCreator([] { return new WebAssemblyOptionsWidget; });
-}
-
-} // Internal
-} // WebAssembly
diff --git a/src/plugins/webassembly/webassemblyoptionspage.h b/src/plugins/webassembly/webassemblyoptionspage.h
deleted file mode 100644
index 62a17a9b15..0000000000
--- a/src/plugins/webassembly/webassemblyoptionspage.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <coreplugin/dialogs/ioptionspage.h>
-
-namespace WebAssembly {
-namespace Internal {
-
-class WebAssemblyOptionsPage final : public Core::IOptionsPage
-{
-public:
- WebAssemblyOptionsPage();
-};
-
-} // namespace Internal
-} // namespace WebAssmbly
diff --git a/src/plugins/webassembly/webassemblyplugin.cpp b/src/plugins/webassembly/webassemblyplugin.cpp
index 0857e43f2f..28227884da 100644
--- a/src/plugins/webassembly/webassemblyplugin.cpp
+++ b/src/plugins/webassembly/webassemblyplugin.cpp
@@ -8,9 +8,9 @@
#endif // WITH_TESTS
#include "webassemblyconstants.h"
#include "webassemblydevice.h"
-#include "webassemblyoptionspage.h"
#include "webassemblyqtversion.h"
#include "webassemblyrunconfiguration.h"
+#include "webassemblysettings.h"
#include "webassemblytoolchain.h"
#include "webassemblytr.h"
@@ -39,7 +39,7 @@ public:
WebAssemblyQtVersionFactory qtVersionFactory;
EmrunRunConfigurationFactory emrunRunConfigurationFactory;
EmrunRunWorkerFactory emrunRunWorkerFactory;
- WebAssemblyOptionsPage optionsPage;
+ WebAssemblySettings settings;
};
static WebAssemblyPluginPrivate *dd = nullptr;
diff --git a/src/plugins/webassembly/webassemblyrunconfigurationaspects.cpp b/src/plugins/webassembly/webassemblyrunconfigurationaspects.cpp
index c1eb243e49..05b7e89e82 100644
--- a/src/plugins/webassembly/webassemblyrunconfigurationaspects.cpp
+++ b/src/plugins/webassembly/webassemblyrunconfigurationaspects.cpp
@@ -8,8 +8,8 @@
#include <projectexplorer/target.h>
#include <utils/layoutbuilder.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QComboBox>
#include <QTextStream>
@@ -29,7 +29,7 @@ static WebBrowserEntries emrunBrowsers(ProjectExplorer::Target *target)
const Utils::Environment environment = bc->environment();
const Utils::FilePath emrunPath = environment.searchInPath("emrun");
- QtcProcess browserLister;
+ Process browserLister;
browserLister.setEnvironment(environment);
browserLister.setCommand({emrunPath, {"--list_browsers"}});
browserLister.start();
@@ -55,7 +55,7 @@ WebBrowserSelectionAspect::WebBrowserSelectionAspect(ProjectExplorer::Target *ta
addDataExtractor(this, &WebBrowserSelectionAspect::currentBrowser, &Data::currentBrowser);
}
-void WebBrowserSelectionAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void WebBrowserSelectionAspect::addToLayout(Layouting::LayoutItem &parent)
{
QTC_CHECK(!m_webBrowserComboBox);
m_webBrowserComboBox = new QComboBox;
@@ -66,7 +66,7 @@ void WebBrowserSelectionAspect::addToLayout(Layouting::LayoutBuilder &builder)
m_currentBrowser = m_webBrowserComboBox->currentData().toString();
emit changed();
});
- builder.addItems({Tr::tr("Web browser:"), m_webBrowserComboBox});
+ parent.addItems({Tr::tr("Web browser:"), m_webBrowserComboBox});
}
void WebBrowserSelectionAspect::fromMap(const QVariantMap &map)
diff --git a/src/plugins/webassembly/webassemblyrunconfigurationaspects.h b/src/plugins/webassembly/webassemblyrunconfigurationaspects.h
index 1d713274ed..8a3cc24bdb 100644
--- a/src/plugins/webassembly/webassemblyrunconfigurationaspects.h
+++ b/src/plugins/webassembly/webassemblyrunconfigurationaspects.h
@@ -20,7 +20,7 @@ class WebBrowserSelectionAspect : public Utils::BaseAspect
public:
WebBrowserSelectionAspect(ProjectExplorer::Target *target);
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
void fromMap(const QVariantMap &map) override;
void toMap(QVariantMap &map) const override;
diff --git a/src/plugins/webassembly/webassemblysettings.cpp b/src/plugins/webassembly/webassemblysettings.cpp
new file mode 100644
index 0000000000..fb9293f2c8
--- /dev/null
+++ b/src/plugins/webassembly/webassemblysettings.cpp
@@ -0,0 +1,168 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "webassemblysettings.h"
+
+#include "webassemblyconstants.h"
+#include "webassemblyemsdk.h"
+#include "webassemblyqtversion.h"
+#include "webassemblytoolchain.h"
+#include "webassemblytr.h"
+
+#include <coreplugin/icore.h>
+
+#include <utils/aspects.h>
+#include <utils/environment.h>
+#include <utils/infolabel.h>
+#include <utils/layoutbuilder.h>
+#include <utils/pathchooser.h>
+#include <utils/utilsicons.h>
+
+#include <QGroupBox>
+#include <QTextBrowser>
+#include <QTimer>
+
+using namespace Utils;
+
+namespace WebAssembly {
+namespace Internal {
+
+static WebAssemblySettings *theSettings = nullptr;
+
+WebAssemblySettings *WebAssemblySettings::instance()
+{
+ return theSettings;
+}
+
+static QString environmentDisplay(const FilePath &sdkRoot)
+{
+ Environment env;
+ WebAssemblyEmSdk::addToEnvironment(sdkRoot, env);
+ QString result;
+ auto h4 = [](const QString &text) { return QString("<h4>" + text + "</h4>"); };
+ result.append(h4(Tr::tr("Adding directories to PATH:")));
+ result.append(env.value("PATH").replace(OsSpecificAspects::pathListSeparator(sdkRoot.osType()), "<br/>"));
+ result.append(h4(Tr::tr("Setting environment variables:")));
+ for (const QString &envVar : env.toStringList()) {
+ if (!envVar.startsWith("PATH")) // Path was already printed out above
+ result.append(envVar + "<br/>");
+ }
+ return result;
+}
+
+WebAssemblySettings::WebAssemblySettings()
+{
+ theSettings = this;
+
+ setSettingsGroup("WebAssembly");
+
+ setId(Id(Constants::SETTINGS_ID));
+ setDisplayName(Tr::tr("WebAssembly"));
+ setCategory(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY);
+
+ registerAspect(&emSdk);
+ emSdk.setSettingsKey("EmSdk");
+ emSdk.setExpectedKind(Utils::PathChooser::ExistingDirectory);
+ emSdk.setDefaultFilePath(FileUtils::homePath());
+
+ connect(this, &Utils::AspectContainer::applied, &WebAssemblyToolChain::registerToolChains);
+
+ setLayouter([this] {
+ auto instruction = new QLabel(
+ Tr::tr("Select the root directory of an installed %1. "
+ "Ensure that the activated SDK version is compatible with the %2 "
+ "or %3 version that you plan to develop against.")
+ .arg(R"(<a href="https://emscripten.org/docs/getting_started/downloads.html">Emscripten SDK</a>)")
+ .arg(R"(<a href="https://doc.qt.io/qt-5/wasm.html#install-emscripten">Qt 5</a>)")
+ .arg(R"(<a href="https://doc.qt.io/qt-6/wasm.html#install-emscripten">Qt 6</a>)"));
+ instruction->setOpenExternalLinks(true);
+ instruction->setWordWrap(true);
+
+ m_emSdkVersionDisplay = new InfoLabel;
+ m_emSdkVersionDisplay->setElideMode(Qt::ElideNone);
+ m_emSdkVersionDisplay->setWordWrap(true);
+
+ m_emSdkEnvDisplay = new QTextBrowser;
+ m_emSdkEnvDisplay->setLineWrapMode(QTextBrowser::NoWrap);
+
+ const QString minimumSupportedQtVersion =
+ WebAssemblyQtVersion::minimumSupportedQtVersion().toString();
+ m_qtVersionDisplay = new InfoLabel(
+ Tr::tr("Note: %1 supports Qt %2 for WebAssembly and higher. "
+ "Your installed lower Qt version(s) are not supported.")
+ .arg(Core::ICore::versionString(), minimumSupportedQtVersion),
+ InfoLabel::Warning);
+ m_qtVersionDisplay->setElideMode(Qt::ElideNone);
+ m_qtVersionDisplay->setWordWrap(true);
+
+ // _clang-format off
+ using namespace Layouting;
+ Column col {
+ Group {
+ title(Tr::tr("Emscripten SDK path:")),
+ Column {
+ instruction,
+ emSdk,
+ m_emSdkVersionDisplay,
+ },
+ },
+ Group {
+ title(Tr::tr("Emscripten SDK environment:")),
+ bindTo(&m_emSdkEnvGroupBox),
+ Column {
+ m_emSdkEnvDisplay,
+ },
+ },
+ m_qtVersionDisplay,
+ };
+ // _clang-format on
+
+ connect(emSdk.pathChooser(), &Utils::PathChooser::textChanged,
+ this, &WebAssemblySettings::updateStatus);
+
+ // updateStatus() uses m_emSdkEnvGroupBox which only exists
+ // after this here emerges. So delay the update a bit.
+ QTimer::singleShot(0, this, &WebAssemblySettings::updateStatus);
+
+ return col;
+ });
+
+ readSettings();
+}
+
+void WebAssemblySettings::updateStatus()
+{
+ WebAssemblyEmSdk::clearCaches();
+
+ const Utils::FilePath newEmSdk = emSdk.pathChooser()->filePath();
+ const bool sdkValid = newEmSdk.exists() && WebAssemblyEmSdk::isValid(newEmSdk);
+
+ QTC_ASSERT(m_emSdkVersionDisplay, return);
+ QTC_ASSERT(m_emSdkEnvGroupBox, return);
+ m_emSdkVersionDisplay->setVisible(sdkValid);
+ m_emSdkEnvGroupBox->setEnabled(sdkValid);
+
+ if (sdkValid) {
+ const QVersionNumber sdkVersion = WebAssemblyEmSdk::version(newEmSdk);
+ const QVersionNumber minVersion = WebAssemblyToolChain::minimumSupportedEmSdkVersion();
+ const bool versionTooLow = sdkVersion < minVersion;
+ m_emSdkVersionDisplay->setType(versionTooLow ? InfoLabel::NotOk : InfoLabel::Ok);
+ auto bold = [](const QString &text) { return QString("<b>" + text + "</b>"); };
+ m_emSdkVersionDisplay->setText(
+ versionTooLow ? Tr::tr("The activated version %1 is not supported by %2. "
+ "Activate version %3 or higher.")
+ .arg(bold(sdkVersion.toString()))
+ .arg(bold(Core::ICore::versionString()))
+ .arg(bold(minVersion.toString()))
+ : Tr::tr("Activated version: %1")
+ .arg(bold(sdkVersion.toString())));
+ m_emSdkEnvDisplay->setText(environmentDisplay(newEmSdk));
+ } else {
+ m_emSdkEnvDisplay->clear();
+ }
+
+ m_qtVersionDisplay->setVisible(WebAssemblyQtVersion::isUnsupportedQtVersionInstalled());
+}
+
+} // Internal
+} // WebAssembly
diff --git a/src/plugins/webassembly/webassemblysettings.h b/src/plugins/webassembly/webassemblysettings.h
new file mode 100644
index 0000000000..c9e207c409
--- /dev/null
+++ b/src/plugins/webassembly/webassemblysettings.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <coreplugin/dialogs/ioptionspage.h>
+
+QT_BEGIN_NAMESPACE
+class QTextBrowser;
+QT_END_NAMESPACE
+
+namespace WebAssembly {
+namespace Internal {
+
+class WebAssemblySettings final : public Core::PagedSettings
+{
+public:
+ WebAssemblySettings();
+
+ static WebAssemblySettings *instance();
+
+ Utils::FilePathAspect emSdk;
+
+private:
+ QWidget *m_emSdkEnvGroupBox = nullptr;
+ Utils::InfoLabel *m_emSdkVersionDisplay = nullptr;
+ QTextBrowser *m_emSdkEnvDisplay = nullptr;
+ Utils::InfoLabel *m_qtVersionDisplay = nullptr;
+
+ void updateStatus();
+};
+
+} // namespace Internal
+} // namespace WebAssmbly
diff --git a/src/plugins/webassembly/webassemblytoolchain.cpp b/src/plugins/webassembly/webassemblytoolchain.cpp
index e38b00dac6..c554ee65cd 100644
--- a/src/plugins/webassembly/webassemblytoolchain.cpp
+++ b/src/plugins/webassembly/webassemblytoolchain.cpp
@@ -1,9 +1,11 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "webassemblytoolchain.h"
+
#include "webassemblyconstants.h"
#include "webassemblyemsdk.h"
-#include "webassemblytoolchain.h"
+#include "webassemblysettings.h"
#include "webassemblytr.h"
#include <projectexplorer/devicesupport/devicemanager.h>
@@ -49,7 +51,8 @@ static void addRegisteredMinGWToEnvironment(Environment &env)
void WebAssemblyToolChain::addToEnvironment(Environment &env) const
{
- WebAssemblyEmSdk::addToEnvironment(WebAssemblyEmSdk::registeredEmSdk(), env);
+ const FilePath emSdk = WebAssemblySettings::instance()->emSdk();
+ WebAssemblyEmSdk::addToEnvironment(emSdk, env);
if (env.osType() == OsTypeWindows)
addRegisteredMinGWToEnvironment(env);
}
@@ -92,11 +95,12 @@ const QVersionNumber &WebAssemblyToolChain::minimumSupportedEmSdkVersion()
static Toolchains doAutoDetect(const ToolchainDetector &detector)
{
- const FilePath sdk = WebAssemblyEmSdk::registeredEmSdk();
+ const FilePath sdk = WebAssemblySettings::instance()->emSdk();
if (!WebAssemblyEmSdk::isValid(sdk))
return {};
- if (detector.device) {
+ if (detector.device
+ && detector.device->type() != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
// Only detect toolchains from the emsdk installation device
const FilePath deviceRoot = detector.device->rootPath();
if (deviceRoot.host() != sdk.host())
diff --git a/src/plugins/welcome/introductionwidget.cpp b/src/plugins/welcome/introductionwidget.cpp
index a18287b0f6..ef56d9ab3e 100644
--- a/src/plugins/welcome/introductionwidget.cpp
+++ b/src/plugins/welcome/introductionwidget.cpp
@@ -26,10 +26,10 @@ const char kTakeTourSetting[] = "TakeUITour";
namespace Welcome {
namespace Internal {
-void IntroductionWidget::askUserAboutIntroduction(QWidget *parent, QSettings *settings)
+void IntroductionWidget::askUserAboutIntroduction(QWidget *parent)
{
// CheckableMessageBox for compatibility with Qt Creator < 4.11
- if (!CheckableMessageBox::shouldAskAgain(settings, kTakeTourSetting)
+ if (!CheckableDecider(QString(kTakeTourSetting)).shouldAskAgain()
|| !Core::ICore::infoBar()->canInfoBeAdded(kTakeTourSetting))
return;
diff --git a/src/plugins/welcome/introductionwidget.h b/src/plugins/welcome/introductionwidget.h
index a11069003a..4b1f99c5ae 100644
--- a/src/plugins/welcome/introductionwidget.h
+++ b/src/plugins/welcome/introductionwidget.h
@@ -11,7 +11,6 @@
QT_BEGIN_NAMESPACE
class QLabel;
-class QSettings;
QT_END_NAMESPACE
namespace Welcome {
@@ -31,7 +30,7 @@ class IntroductionWidget : public QWidget
public:
explicit IntroductionWidget(QWidget *parent = nullptr);
- static void askUserAboutIntroduction(QWidget *parent, QSettings *settings);
+ static void askUserAboutIntroduction(QWidget *parent);
protected:
bool event(QEvent *e) override;
diff --git a/src/plugins/welcome/welcomeplugin.cpp b/src/plugins/welcome/welcomeplugin.cpp
index 1a1a1aa647..915695dafa 100644
--- a/src/plugins/welcome/welcomeplugin.cpp
+++ b/src/plugins/welcome/welcomeplugin.cpp
@@ -132,8 +132,7 @@ public:
if (!arguments.contains("-notour")) {
connect(ICore::instance(), &ICore::coreOpened, this, []() {
- IntroductionWidget::askUserAboutIntroduction(ICore::dialogParent(),
- ICore::settings());
+ IntroductionWidget::askUserAboutIntroduction(ICore::dialogParent());
}, Qt::QueuedConnection);
}