aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build_cmake.yml8
-rw-r--r--cmake/QtCreatorAPIInternal.cmake1
-rw-r--r--coin/instructions/build.yaml47
-rw-r--r--coin/instructions/common_environment.yaml17
-rw-r--r--coin/module_config.yaml3
-rw-r--r--coin/product_dependencies.yaml2
-rw-r--r--dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake6
-rw-r--r--doc/config/macros.qdocconf5
-rw-r--r--doc/qtcreator/config/style/qt5-sidebar.html1
-rw-r--r--doc/qtcreator/images/numbers/01.pngbin430 -> 446 bytes
-rw-r--r--doc/qtcreator/images/numbers/02.pngbin561 -> 571 bytes
-rw-r--r--doc/qtcreator/images/numbers/03.pngbin617 -> 617 bytes
-rw-r--r--doc/qtcreator/images/numbers/04.pngbin520 -> 526 bytes
-rw-r--r--doc/qtcreator/images/numbers/05.pngbin601 -> 595 bytes
-rw-r--r--doc/qtcreator/images/numbers/06.pngbin640 -> 642 bytes
-rw-r--r--doc/qtcreator/images/numbers/07.pngbin518 -> 515 bytes
-rw-r--r--doc/qtcreator/images/numbers/08.pngbin628 -> 630 bytes
-rw-r--r--doc/qtcreator/images/numbers/09.pngbin650 -> 655 bytes
-rw-r--r--doc/qtcreator/images/numbers/10.pngbin707 -> 711 bytes
-rw-r--r--doc/qtcreator/images/qtcreator-configure-project.webpbin0 -> 5298 bytes
-rw-r--r--doc/qtcreator/images/qtcreator-devices-qnx.webpbin0 -> 12656 bytes
-rw-r--r--doc/qtcreator/images/qtcreator-locator-customize.webpbin16400 -> 11824 bytes
-rw-r--r--doc/qtcreator/images/qtcreator-open-project-kits.pngbin11379 -> 0 bytes
-rw-r--r--doc/qtcreator/images/qtcreator-preferences-qnx.webpbin0 -> 19938 bytes
-rw-r--r--doc/qtcreator/src/analyze/cpu-usage-analyzer.qdoc3
-rw-r--r--doc/qtcreator/src/android/androiddev.qdoc11
-rw-r--r--doc/qtcreator/src/appman/creator-appman-how-to-run.qdoc13
-rw-r--r--doc/qtcreator/src/docker/creator-docker.qdoc120
-rw-r--r--doc/qtcreator/src/editors/creator-code-syntax.qdoc2
-rw-r--r--doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc35
-rw-r--r--doc/qtcreator/src/editors/creator-only/creator-locator.qdoc10
-rw-r--r--doc/qtcreator/src/external-resources/external-resources.qdoc16
-rw-r--r--doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc2
-rw-r--r--doc/qtcreator/src/howto/creator-only/creator-cli.qdoc6
-rw-r--r--doc/qtcreator/src/howto/creator-only/creator-how-to-find-settings-files.qdoc34
-rw-r--r--doc/qtcreator/src/howto/creator-only/creator-how-tos.qdoc27
-rw-r--r--doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc17
-rw-r--r--doc/qtcreator/src/howto/creator-only/qtcreator-how-to-change-ui-language.qdoc38
-rw-r--r--doc/qtcreator/src/linux-mobile/b2qtdev.qdoc219
-rw-r--r--doc/qtcreator/src/linux-mobile/creator-deployment-b2qt.qdoc16
-rw-r--r--doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc71
-rw-r--r--doc/qtcreator/src/linux-mobile/creator-projects-settings-run-b2qt.qdoc24
-rw-r--r--doc/qtcreator/src/linux-mobile/linuxdev-keys.qdoc2
-rw-r--r--doc/qtcreator/src/linux-mobile/linuxdev.qdoc74
-rw-r--r--doc/qtcreator/src/linux-mobile/qtcreator-add-linux-device.qdocinc47
-rw-r--r--doc/qtcreator/src/overview/creator-only/creator-configuring.qdoc2
-rw-r--r--doc/qtcreator/src/overview/creator-only/creator-getting-started.qdoc5
-rw-r--r--doc/qtcreator/src/overview/creator-only/creator-issues.qdoc2
-rw-r--r--doc/qtcreator/src/overview/creator-only/creator-mobile-targets.qdoc79
-rw-r--r--doc/qtcreator/src/overview/creator-only/creator-overview.qdoc10
-rw-r--r--doc/qtcreator/src/overview/creator-only/creator-supported-platforms.qdoc4
-rw-r--r--doc/qtcreator/src/projects/creator-only/creator-how-to-add-wizards.qdoc2
-rw-r--r--doc/qtcreator/src/projects/creator-only/creator-projects-build-run-tutorial.qdoc31
-rw-r--r--doc/qtcreator/src/projects/creator-only/creator-projects-building.qdoc2
-rw-r--r--doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc2
-rw-r--r--doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc2
-rw-r--r--doc/qtcreator/src/projects/creator-only/creator-projects-kits.qdoc10
-rw-r--r--doc/qtcreator/src/projects/creator-only/creator-projects-opening.qdoc8
-rw-r--r--doc/qtcreator/src/projects/creator-only/creator-projects-settings-build.qdoc2
-rw-r--r--doc/qtcreator/src/projects/creator-only/creator-projects-settings-run.qdoc2
-rw-r--r--doc/qtcreator/src/qnx/creator-deployment-qnx.qdoc2
-rw-r--r--doc/qtcreator/src/qnx/creator-developing-qnx.qdoc53
-rw-r--r--doc/qtcreator/src/qnx/creator-projects-how-to-run-qnx.qdoc14
-rw-r--r--doc/qtcreator/src/qnx/creator-projects-settings-run-qnx.qdoc11
-rw-r--r--doc/qtcreator/src/qtcreator-toc.qdoc15
-rw-r--r--doc/qtcreator/src/qtcreator.qdoc4
-rw-r--r--doc/qtcreator/src/qtquick/qtquick-live-preview-devices.qdoc10
-rw-r--r--doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc2
-rw-r--r--doc/qtcreator/src/user-interface/creator-only/creator-reference-to-do-entries-view.qdoc2
-rw-r--r--doc/qtcreator/src/vcs/creator-only/creator-vcs-perforce.qdoc25
-rw-r--r--doc/qtcreator/src/webassembly/creator-webassembly.qdoc121
-rw-r--r--doc/qtcreator/src/widgets/creator-faq-qtdesigner.qdocinc4
-rw-r--r--doc/qtcreator/src/widgets/qtdesigner-app-tutorial.qdoc2
-rw-r--r--doc/qtcreator/src/widgets/qtdesigner-overview.qdoc8
-rw-r--r--doc/qtcreator/src/widgets/qtdesigner-plugins.qdoc4
-rw-r--r--doc/qtcreatordev/src/qtcreator-ui-text.qdoc54
-rw-r--r--doc/qtdesignstudio/config/style/qt5-sidebar.html113
-rw-r--r--doc/qtdesignstudio/images/bleed-scale-1.webpbin0 -> 37134 bytes
-rw-r--r--doc/qtdesignstudio/images/bleed-scale-8.webpbin0 -> 34696 bytes
-rw-r--r--doc/qtdesignstudio/images/bleed-scale-no.webpbin0 -> 35312 bytes
-rw-r--r--doc/qtdesignstudio/images/ext-scene-env-navigator.webpbin0 -> 3884 bytes
-rw-r--r--doc/qtdesignstudio/images/glow-additive-blend.webpbin0 -> 77682 bytes
-rw-r--r--doc/qtdesignstudio/images/glow-bicubic.webpbin0 -> 19314 bytes
-rw-r--r--doc/qtdesignstudio/images/glow-example-project.webpbin0 -> 26004 bytes
-rw-r--r--doc/qtdesignstudio/images/glow-example.webpbin0 -> 23438 bytes
-rw-r--r--doc/qtdesignstudio/images/glow-high-quality.webpbin0 -> 21128 bytes
-rw-r--r--doc/qtdesignstudio/images/glow-no-enhancment.webpbin0 -> 18856 bytes
-rw-r--r--doc/qtdesignstudio/images/glow-properties.webpbin0 -> 15626 bytes
-rw-r--r--doc/qtdesignstudio/images/glow-replace-blend.webpbin0 -> 53186 bytes
-rw-r--r--doc/qtdesignstudio/images/glow-screen-blend.webpbin0 -> 77272 bytes
-rw-r--r--doc/qtdesignstudio/images/glow-softlight-blend.webpbin0 -> 72306 bytes
-rw-r--r--doc/qtdesignstudio/images/glow_all_blur_levels.webpbin0 -> 37506 bytes
-rw-r--r--doc/qtdesignstudio/images/glow_high_blur_levels.webpbin0 -> 29482 bytes
-rw-r--r--doc/qtdesignstudio/images/glow_low_blur_levels.webpbin0 -> 7888 bytes
-rw-r--r--doc/qtdesignstudio/images/qtcreator-qt-design-studio-project.webpbin0 -> 8422 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-effects-background-blur.webpbin0 -> 40922 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-effects-drop-shadow.webpbin0 -> 37860 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-effects-inner-shadow.webpbin0 -> 12254 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-effects-layer-blur.webpbin0 -> 34870 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-effects-show-shadow-behind.webpbin0 -> 18410 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-effects.webpbin0 -> 8156 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-flow-decision-preview.pngbin12848 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-flow-decision-preview.webpbin0 -> 10926 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-flow-decision-properties.pngbin12037 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-flow-decision-properties.webpbin0 -> 12178 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-flow-decision.pngbin36350 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-flow-decision.webpbin0 -> 32674 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-flow-events-assign.pngbin15537 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-flow-events-assign.webpbin0 -> 3638 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-flow-events-event-list.webpbin0 -> 2950 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-flow-item-component.webpbin0 -> 4960 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-flow-states-item-properties.webpbin0 -> 12408 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-flow-transition-properties-question.pngbin11673 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-flow-transition-properties-question.webpbin0 -> 11728 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-flow-view.pngbin58274 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-flow-view.webpbin0 -> 41390 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-flow-wildcard-properties.pngbin11673 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-flow-wildcard-properties.webpbin0 -> 12696 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-flow-wildcard.webpbin0 -> 3866 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-project-cmake-generation.webpbin0 -> 5072 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-project-export-advanced-options.webpbin11752 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-project-export-advanced.webpbin2488 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-project-export.webpbin12244 -> 10078 bytes
-rw-r--r--doc/qtdesignstudio/src/best practices/best-practices-glow.qdoc200
-rw-r--r--doc/qtdesignstudio/src/best-practices.qdoc38
-rw-r--r--doc/qtdesignstudio/src/components/qtquick-images.qdoc3
-rw-r--r--doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc1
-rw-r--r--doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc81
-rw-r--r--doc/qtdesignstudio/src/how-to/qtdesignstudio-live-preview-desktop.qdoc5
-rw-r--r--doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc6
-rw-r--r--doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc4
-rw-r--r--doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc4
-rw-r--r--doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc202
-rw-r--r--doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc4
-rw-r--r--doc/qtdesignstudio/src/qtdesignstudio-qt-academy.qdocinc22
-rw-r--r--doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc4
-rw-r--r--doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc4
-rw-r--r--doc/qtdesignstudio/src/qtdesignstudio.qdoc1
-rw-r--r--doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc340
-rw-r--r--doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc2
-rw-r--r--doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc2
-rw-r--r--doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc2
-rw-r--r--doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-property-visual-effects.qdoc136
-rw-r--r--doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-visual-effects.qdoc2
-rw-r--r--doc/qtdesignstudio/src/views/studio-model-editor.qdoc2
-rw-r--r--share/qtcreator/debugger/creatortypes.py2
-rw-r--r--share/qtcreator/debugger/dumper.py39
-rw-r--r--share/qtcreator/debugger/gdbbridge.py120
-rw-r--r--share/qtcreator/debugger/lldbbridge.py93
-rw-r--r--share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsContextMenu.qml2
-rw-r--r--share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml247
-rw-r--r--share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml291
-rw-r--r--share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml727
-rw-r--r--share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml154
-rw-r--r--share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml215
-rw-r--r--share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml206
-rw-r--r--share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml296
-rw-r--r--share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml158
-rw-r--r--share/qtcreator/qmldesigner/collectionEditorQmlSource/IconTextButton.qml97
-rw-r--r--share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml225
-rw-r--r--share/qtcreator/qmldesigner/collectionEditorQmlSource/Message.qml42
-rw-r--r--share/qtcreator/qmldesigner/collectionEditorQmlSource/NewCollectionDialog.qml111
-rw-r--r--share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml4
-rw-r--r--share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml17
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml30
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectContextMenu.qml2
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml2
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryItem.qml (renamed from share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffect.qml)8
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryItemContextMenu.qml71
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml10
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml59
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml13
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTextureContextMenu.qml12
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml49
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/DeleteBundleItemDialog.qml65
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleItemDialog.qml (renamed from share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleMaterialDialog.qml)2
-rw-r--r--share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml4
-rw-r--r--share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml5
-rw-r--r--share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml12
-rw-r--r--share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml84
-rw-r--r--share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml30
-rw-r--r--share/qtcreator/qmldesigner/projectstorage/fake.qmltypes9
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/BusyIndicatorSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ButtonSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckBoxSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckDelegateSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ComboBoxSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ControlSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DelayButtonSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialogSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DrawerSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/FrameSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/GroupBoxSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ItemDelegateSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/LabelSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageIndicatorSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PaneSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PopupSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ProgressBarSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioButtonSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioDelegateSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RangeSliderSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RoundButtonSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ScrollViewSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SliderSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SpinBoxSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/StackViewSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeDelegateSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeViewSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchDelegateSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabBarSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabButtonSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextAreaSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextFieldSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolBarSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolButtonSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolSeparatorSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TumblerSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml64
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml9
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml23
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml4
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml2
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml71
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml2
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckBox.qml1
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml14
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorPicker.qml5
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml10
-rw-r--r--share/qtcreator/qmldesigner/qt4mcu/metadata.qml8
-rw-r--r--share/qtcreator/qmldesigner/qt4mcu/qul-27.qml227
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/files/javascript/wizard.json50
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json2
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json2
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/app_mcu.qmlproject.tpl8
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json2
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json2
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json2
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json2
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json2
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/Colors.json.tpl18
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/DataStore.qml.tpl17
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/models.json.tpl56
-rw-r--r--share/qtcreator/templates/wizards/files/form/wizard.json4
-rw-r--r--share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/wizard.json2
-rw-r--r--share/qtcreator/templates/wizards/projects/qtwidgetsapplication/wizard.json2
-rw-r--r--share/qtcreator/translations/README.md8
-rw-r--r--share/qtcreator/translations/qtcreator_cs.ts28
-rw-r--r--share/qtcreator/translations/qtcreator_da.ts54
-rw-r--r--share/qtcreator/translations/qtcreator_de.ts76
-rw-r--r--share/qtcreator/translations/qtcreator_es.ts30
-rw-r--r--share/qtcreator/translations/qtcreator_fr.ts74
-rw-r--r--share/qtcreator/translations/qtcreator_hr.ts22
-rw-r--r--share/qtcreator/translations/qtcreator_hu.ts28
-rw-r--r--share/qtcreator/translations/qtcreator_it.ts24
-rw-r--r--share/qtcreator/translations/qtcreator_ja.ts58
-rw-r--r--share/qtcreator/translations/qtcreator_pl.ts74
-rw-r--r--share/qtcreator/translations/qtcreator_ru.ts64
-rw-r--r--share/qtcreator/translations/qtcreator_sl.ts30
-rw-r--r--share/qtcreator/translations/qtcreator_uk.ts44
-rw-r--r--share/qtcreator/translations/qtcreator_zh_CN.ts28
-rw-r--r--share/qtcreator/translations/qtcreator_zh_TW.ts4
-rw-r--r--src/libs/3rdparty/cplusplus/TranslationUnit.cpp28
-rw-r--r--src/libs/3rdparty/cplusplus/TranslationUnit.h6
-rw-r--r--src/libs/3rdparty/libptyqt/unixptyprocess.cpp17
-rw-r--r--src/libs/3rdparty/sol2/include/sol/sol.hpp2
-rw-r--r--src/libs/3rdparty/sqlite/sqlite3.c8475
-rw-r--r--src/libs/3rdparty/sqlite/sqlite3.h97
-rw-r--r--src/libs/extensionsystem/pluginmanager.cpp18
-rw-r--r--src/libs/extensionsystem/pluginmanager.h4
-rw-r--r--src/libs/extensionsystem/pluginmanager_p.h1
-rw-r--r--src/libs/extensionsystem/pluginspec.cpp2
-rw-r--r--src/libs/extensionsystem/pluginview.cpp5
-rw-r--r--src/libs/modelinglib/qmt/config/configcontroller.cpp17
-rw-r--r--src/libs/modelinglib/qmt/controller/namecontroller.cpp18
-rw-r--r--src/libs/modelinglib/qmt/diagram/dobject.cpp8
-rw-r--r--src/libs/modelinglib/qmt/diagram/dobject.h8
-rw-r--r--src/libs/modelinglib/qmt/document_controller/documentcontroller.cpp6
-rw-r--r--src/libs/modelinglib/qmt/infrastructure/ioexceptions.cpp16
-rw-r--r--src/libs/modelinglib/qmt/model/mobject.cpp4
-rw-r--r--src/libs/modelinglib/qmt/model/mobject.h8
-rw-r--r--src/libs/modelinglib/qmt/model_ui/treemodel.cpp4
-rw-r--r--src/libs/modelinglib/qmt/model_ui/treemodel.h7
-rw-r--r--src/libs/modelinglib/qmt/project/project.cpp6
-rw-r--r--src/libs/modelinglib/qmt/project_controller/projectcontroller.cpp8
-rw-r--r--src/libs/modelinglib/qmt/serializer/infrastructureserializer.h4
-rw-r--r--src/libs/modelinglib/qmt/serializer/projectserializer.cpp10
-rw-r--r--src/libs/modelinglib/qmt/stereotype/stereotypecontroller.cpp12
-rw-r--r--src/libs/modelinglib/qmt/stereotype/stereotypecontroller.h12
-rw-r--r--src/libs/nanotrace/nanotracehr.h10
-rw-r--r--src/libs/qmljs/jsoncheck.cpp14
-rw-r--r--src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h1
-rw-r--r--src/libs/qmlpuppetcommunication/commands/view3dactioncommand.cpp11
-rw-r--r--src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h6
-rw-r--r--src/libs/qmlpuppetcommunication/types/enumeration.h13
-rw-r--r--src/libs/solutions/spinner/spinner.cpp2
-rw-r--r--src/libs/sqlite/sqlitebasestatement.h11
-rw-r--r--src/libs/sqlite/sqliteids.h27
-rw-r--r--src/libs/sqlite/sqlitevalue.h14
-rw-r--r--src/libs/tracing/CMakeLists.txt4
-rw-r--r--src/libs/tracing/timelinenotesrenderpass.cpp2
-rw-r--r--src/libs/utils/CMakeLists.txt2
-rw-r--r--src/libs/utils/aspects.cpp287
-rw-r--r--src/libs/utils/aspects.h64
-rw-r--r--src/libs/utils/async.h5
-rw-r--r--src/libs/utils/changeset.cpp35
-rw-r--r--src/libs/utils/changeset.h6
-rw-r--r--src/libs/utils/checkablemessagebox.cpp5
-rw-r--r--src/libs/utils/clangutils.cpp2
-rw-r--r--src/libs/utils/commandline.cpp13
-rw-r--r--src/libs/utils/commandline.h16
-rw-r--r--src/libs/utils/crumblepath.cpp4
-rw-r--r--src/libs/utils/detailsbutton.cpp4
-rw-r--r--src/libs/utils/detailswidget.cpp2
-rw-r--r--src/libs/utils/execmenu.cpp3
-rw-r--r--src/libs/utils/externalterminalprocessimpl.cpp4
-rw-r--r--src/libs/utils/fancylineedit.cpp28
-rw-r--r--src/libs/utils/filepath.cpp2
-rw-r--r--src/libs/utils/fileutils.cpp15
-rw-r--r--src/libs/utils/fileutils.h4
-rw-r--r--src/libs/utils/futuresynchronizer.cpp17
-rw-r--r--src/libs/utils/futuresynchronizer.h2
-rw-r--r--src/libs/utils/guiutils.cpp12
-rw-r--r--src/libs/utils/guiutils.h5
-rw-r--r--src/libs/utils/highlightingitemdelegate.cpp2
-rw-r--r--src/libs/utils/icon.cpp6
-rw-r--r--src/libs/utils/iconbutton.cpp2
-rw-r--r--src/libs/utils/infobar.cpp4
-rw-r--r--src/libs/utils/infolabel.cpp2
-rw-r--r--src/libs/utils/layoutbuilder.cpp1056
-rw-r--r--src/libs/utils/layoutbuilder.h536
-rw-r--r--src/libs/utils/lua.cpp30
-rw-r--r--src/libs/utils/lua.h34
-rw-r--r--src/libs/utils/macroexpander.cpp2
-rw-r--r--src/libs/utils/outputformatter.cpp17
-rw-r--r--src/libs/utils/passworddialog.cpp8
-rw-r--r--src/libs/utils/persistentsettings.cpp30
-rw-r--r--src/libs/utils/projectintropage.cpp33
-rw-r--r--src/libs/utils/projectintropage.h1
-rw-r--r--src/libs/utils/qtcolorbutton.cpp4
-rw-r--r--src/libs/utils/qtcprocess.cpp2
-rw-r--r--src/libs/utils/store.cpp6
-rw-r--r--src/libs/utils/stringutils.cpp2
-rw-r--r--src/libs/utils/stylehelper.cpp124
-rw-r--r--src/libs/utils/terminalinterface.cpp2
-rw-r--r--src/libs/utils/theme/theme.cpp6
-rw-r--r--src/libs/utils/theme/theme.h1
-rw-r--r--src/libs/utils/threadutils.cpp5
-rw-r--r--src/libs/utils/treemodel.cpp6
-rw-r--r--src/libs/utils/utility.h14
-rw-r--r--src/libs/utils/utils.qbs2
-rw-r--r--src/libs/utils/wizard.cpp31
-rw-r--r--src/libs/utils/wizard.h4
-rw-r--r--src/libs/utils/wizardpage.h4
-rw-r--r--src/plugins/CMakeLists.txt5
-rw-r--r--src/plugins/android/Android.json.in2
-rw-r--r--src/plugins/android/androidavdmanager.cpp157
-rw-r--r--src/plugins/android/androidavdmanager.h26
-rw-r--r--src/plugins/android/androidbuildapkstep.cpp15
-rw-r--r--src/plugins/android/androidconfigurations.cpp860
-rw-r--r--src/plugins/android/androidconfigurations.h172
-rw-r--r--src/plugins/android/androidcreatekeystorecertificate.cpp6
-rw-r--r--src/plugins/android/androiddebugsupport.cpp5
-rw-r--r--src/plugins/android/androiddeployqtstep.cpp166
-rw-r--r--src/plugins/android/androiddevice.cpp247
-rw-r--r--src/plugins/android/androiddevice.h21
-rw-r--r--src/plugins/android/androidmanager.cpp202
-rw-r--r--src/plugins/android/androidmanager.h1
-rw-r--r--src/plugins/android/androidmanifesteditorwidget.cpp2
-rw-r--r--src/plugins/android/androidplugin.cpp8
-rw-r--r--src/plugins/android/androidqmlpreviewworker.cpp149
-rw-r--r--src/plugins/android/androidqtversion.cpp15
-rw-r--r--src/plugins/android/androidrunner.cpp16
-rw-r--r--src/plugins/android/androidrunnerworker.cpp182
-rw-r--r--src/plugins/android/androidrunnerworker.h35
-rw-r--r--src/plugins/android/androidsdkdownloader.cpp12
-rw-r--r--src/plugins/android/androidsdkmanager.cpp44
-rw-r--r--src/plugins/android/androidsdkmanagerdialog.cpp26
-rw-r--r--src/plugins/android/androidsettingswidget.cpp93
-rw-r--r--src/plugins/android/androidsignaloperation.cpp2
-rw-r--r--src/plugins/android/androidtoolchain.cpp27
-rw-r--r--src/plugins/android/avddialog.cpp93
-rw-r--r--src/plugins/android/avddialog.h3
-rw-r--r--src/plugins/android/avdmanageroutputparser.cpp8
-rw-r--r--src/plugins/android/avdmanageroutputparser.h8
-rw-r--r--src/plugins/android/javalanguageserver.cpp2
-rw-r--r--src/plugins/appstatisticsmonitor/AppStatisticsMonitor.json.in (renamed from src/plugins/luatemplates/LuaTemplates.json.in)10
-rw-r--r--src/plugins/appstatisticsmonitor/CMakeLists.txt10
-rw-r--r--src/plugins/appstatisticsmonitor/appstatisticsmonitor.qbs17
-rw-r--r--src/plugins/appstatisticsmonitor/appstatisticsmonitorplugin.cpp32
-rw-r--r--src/plugins/appstatisticsmonitor/appstatisticsmonitortr.h15
-rw-r--r--src/plugins/appstatisticsmonitor/chart.cpp164
-rw-r--r--src/plugins/appstatisticsmonitor/chart.h40
-rw-r--r--src/plugins/appstatisticsmonitor/idataprovider.cpp237
-rw-r--r--src/plugins/appstatisticsmonitor/idataprovider.h44
-rw-r--r--src/plugins/appstatisticsmonitor/manager.cpp219
-rw-r--r--src/plugins/appstatisticsmonitor/manager.h60
-rw-r--r--src/plugins/autotest/AutoTest.json.in2
-rw-r--r--src/plugins/autotest/ctest/ctesttool.cpp12
-rw-r--r--src/plugins/autotest/ctest/ctesttreeitem.cpp2
-rw-r--r--src/plugins/autotest/projectsettingswidget.cpp12
-rw-r--r--src/plugins/autotest/testnavigationwidget.cpp4
-rw-r--r--src/plugins/autotest/testresult.cpp19
-rw-r--r--src/plugins/autotest/testresultspane.cpp4
-rw-r--r--src/plugins/autotest/testrunner.cpp2
-rw-r--r--src/plugins/autotest/testsettingspage.cpp2
-rw-r--r--src/plugins/autotoolsprojectmanager/AutotoolsProjectManager.json.in2
-rw-r--r--src/plugins/axivion/axivionoutputpane.cpp4
-rw-r--r--src/plugins/axivion/axivionplugin.cpp61
-rw-r--r--src/plugins/axivion/axivionplugin.h2
-rw-r--r--src/plugins/axivion/axivionprojectsettings.cpp17
-rw-r--r--src/plugins/axivion/axivionprojectsettings.h6
-rw-r--r--src/plugins/axivion/axivionsettings.cpp83
-rw-r--r--src/plugins/axivion/axivionsettings.h12
-rw-r--r--src/plugins/axivion/dynamiclistmodel.cpp2
-rw-r--r--src/plugins/baremetal/debugservers/gdb/eblinkgdbserverprovider.cpp2
-rw-r--r--src/plugins/baremetal/debugservers/gdb/stlinkutilgdbserverprovider.cpp2
-rw-r--r--src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp6
-rw-r--r--src/plugins/baremetal/keiltoolchain.cpp6
-rw-r--r--src/plugins/bazaar/Bazaar.json.in2
-rw-r--r--src/plugins/bazaar/bazaarplugin.cpp13
-rw-r--r--src/plugins/beautifier/Beautifier.json.in2
-rw-r--r--src/plugins/beautifier/clangformat/clangformat.cpp3
-rw-r--r--src/plugins/beautifier/generalsettings.cpp2
-rw-r--r--src/plugins/bineditor/BinEditor.json.in2
-rw-r--r--src/plugins/boot2qt/Boot2Qt.json.in2
-rw-r--r--src/plugins/boot2qt/qdbdevice.cpp2
-rw-r--r--src/plugins/boot2qt/qdbplugin.cpp2
-rw-r--r--src/plugins/clangcodemodel/ClangCodeModel.json.in2
-rw-r--r--src/plugins/clangcodemodel/clangcodemodelplugin.cpp16
-rw-r--r--src/plugins/clangcodemodel/clangdclient.cpp104
-rw-r--r--src/plugins/clangcodemodel/clangdclient.h2
-rw-r--r--src/plugins/clangcodemodel/clangdfollowsymbol.cpp38
-rw-r--r--src/plugins/clangcodemodel/clangdlocatorfilters.cpp3
-rw-r--r--src/plugins/clangcodemodel/clangdquickfixes.h2
-rw-r--r--src/plugins/clangcodemodel/clangdsemantichighlighting.cpp762
-rw-r--r--src/plugins/clangcodemodel/clangdsemantichighlighting.h3
-rw-r--r--src/plugins/clangcodemodel/clangfixitoperation.cpp5
-rw-r--r--src/plugins/clangcodemodel/clangmodelmanagersupport.cpp16
-rw-r--r--src/plugins/clangcodemodel/clangutils.cpp30
-rw-r--r--src/plugins/clangcodemodel/clangutils.h25
-rw-r--r--src/plugins/clangcodemodel/test/clangdtests.cpp7
-rw-r--r--src/plugins/clangformat/ClangFormat.json.in2
-rw-r--r--src/plugins/clangformat/clangformatbaseindenter.cpp67
-rw-r--r--src/plugins/clangformat/clangformatbaseindenter.h4
-rw-r--r--src/plugins/clangformat/clangformatglobalconfigwidget.cpp5
-rw-r--r--src/plugins/clangformat/clangformatutils.cpp1
-rw-r--r--src/plugins/clangformat/tests/clangformat-test.cpp132
-rw-r--r--src/plugins/clangtools/ClangTools.json.in2
-rw-r--r--src/plugins/clangtools/clangtool.cpp4
-rw-r--r--src/plugins/clangtools/clangtoolrunner.cpp13
-rw-r--r--src/plugins/clangtools/clangtoolsdiagnosticview.cpp19
-rw-r--r--src/plugins/clangtools/documentquickfixfactory.h2
-rw-r--r--src/plugins/clangtools/executableinfo.cpp5
-rw-r--r--src/plugins/classview/ClassView.json.in2
-rw-r--r--src/plugins/clearcase/ClearCase.json.in2
-rw-r--r--src/plugins/clearcase/clearcaseplugin.cpp6
-rw-r--r--src/plugins/cmakeprojectmanager/CMakeLists.txt2
-rw-r--r--src/plugins/cmakeprojectmanager/CMakeProjectManager.json.in2
-rw-r--r--src/plugins/cmakeprojectmanager/builddirparameters.cpp6
-rw-r--r--src/plugins/cmakeprojectmanager/builddirparameters.h5
-rw-r--r--src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp54
-rw-r--r--src/plugins/cmakeprojectmanager/cmakebuildstep.cpp11
-rw-r--r--src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp17
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeformatter.cpp2
-rw-r--r--src/plugins/cmakeprojectmanager/cmakekitaspect.cpp10
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprocess.cpp13
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeproject.cpp14
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeproject.h4
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectconstants.h3
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp53
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp45
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectmanager.h1
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs1
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp2
-rw-r--r--src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp159
-rw-r--r--src/plugins/cmakeprojectmanager/cmakespecificsettings.h14
-rw-r--r--src/plugins/cmakeprojectmanager/cmaketool.cpp8
-rw-r--r--src/plugins/cmakeprojectmanager/cmaketool.h3
-rw-r--r--src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp21
-rw-r--r--src/plugins/cmakeprojectmanager/cmaketoolmanager.h6
-rw-r--r--src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp6
-rw-r--r--src/plugins/cmakeprojectmanager/configmodel.cpp4
-rw-r--r--src/plugins/cmakeprojectmanager/fileapidataextractor.cpp16
-rw-r--r--src/plugins/cmakeprojectmanager/fileapiparser.cpp7
-rw-r--r--src/plugins/cmakeprojectmanager/fileapireader.cpp7
-rw-r--r--src/plugins/cmakeprojectmanager/presetsparser.cpp41
-rw-r--r--src/plugins/cmakeprojectmanager/presetsparser.h6
-rw-r--r--src/plugins/coco/Coco.json.in2
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/CompilationDatabaseProjectManager.json.in2
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp4
-rw-r--r--src/plugins/compilerexplorer/CompilerExplorer.json.in2
-rw-r--r--src/plugins/compilerexplorer/compilerexplorer.qrc2
-rw-r--r--src/plugins/compilerexplorer/compilerexploreraspects.cpp4
-rw-r--r--src/plugins/compilerexplorer/compilerexploreraspects.h2
-rw-r--r--src/plugins/compilerexplorer/compilerexplorereditor.cpp49
-rw-r--r--src/plugins/compilerexplorer/wizard/qtcpp/file.qtce18
-rw-r--r--src/plugins/compilerexplorer/wizard/qtcpp/wizard.json37
-rw-r--r--src/plugins/conan/Conan.json.in2
-rw-r--r--src/plugins/copilot/Copilot.json.in2
-rw-r--r--src/plugins/coreplugin/Core.json.in2
-rw-r--r--src/plugins/coreplugin/actionmanager/actionmanager.cpp2
-rw-r--r--src/plugins/coreplugin/actionsfilter.cpp3
-rw-r--r--src/plugins/coreplugin/coreconstants.h1
-rw-r--r--src/plugins/coreplugin/corejsextensions.cpp5
-rw-r--r--src/plugins/coreplugin/corejsextensions.h1
-rw-r--r--src/plugins/coreplugin/designmode.cpp12
-rw-r--r--src/plugins/coreplugin/designmode.h7
-rw-r--r--src/plugins/coreplugin/dialogs/shortcutsettings.cpp8
-rw-r--r--src/plugins/coreplugin/editormanager/editormanager.cpp49
-rw-r--r--src/plugins/coreplugin/editormanager/editormanager_p.h5
-rw-r--r--src/plugins/coreplugin/editormanager/editorview.cpp221
-rw-r--r--src/plugins/coreplugin/editormanager/editorview.h12
-rw-r--r--src/plugins/coreplugin/editormanager/ieditorfactory.cpp39
-rw-r--r--src/plugins/coreplugin/editortoolbar.cpp74
-rw-r--r--src/plugins/coreplugin/editortoolbar.h13
-rw-r--r--src/plugins/coreplugin/fancyactionbar.cpp24
-rw-r--r--src/plugins/coreplugin/fancytabwidget.cpp24
-rw-r--r--src/plugins/coreplugin/find/highlightscrollbarcontroller.cpp2
-rw-r--r--src/plugins/coreplugin/find/ifindfilter.cpp8
-rw-r--r--src/plugins/coreplugin/find/searchresultwidget.cpp4
-rw-r--r--src/plugins/coreplugin/find/searchresultwindow.cpp11
-rw-r--r--src/plugins/coreplugin/generalsettings.cpp2
-rw-r--r--src/plugins/coreplugin/helpmanager.cpp6
-rw-r--r--src/plugins/coreplugin/helpmanager.h6
-rw-r--r--src/plugins/coreplugin/helpmanager_implementation.h1
-rw-r--r--src/plugins/coreplugin/icore.cpp6
-rw-r--r--src/plugins/coreplugin/iversioncontrol.cpp2
-rw-r--r--src/plugins/coreplugin/iwizardfactory.cpp2
-rw-r--r--src/plugins/coreplugin/locator/directoryfilter.cpp39
-rw-r--r--src/plugins/coreplugin/locator/directoryfilter.h1
-rw-r--r--src/plugins/coreplugin/locator/filesystemfilter.cpp24
-rw-r--r--src/plugins/coreplugin/locator/filesystemfilter.h1
-rw-r--r--src/plugins/coreplugin/locator/ilocatorfilter.cpp81
-rw-r--r--src/plugins/coreplugin/locator/ilocatorfilter.h6
-rw-r--r--src/plugins/coreplugin/locator/locatorsettingspage.cpp25
-rw-r--r--src/plugins/coreplugin/locator/locatorwidget.cpp4
-rw-r--r--src/plugins/coreplugin/locator/opendocumentsfilter.cpp3
-rw-r--r--src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp3
-rw-r--r--src/plugins/coreplugin/locator/urllocatorfilter.cpp28
-rw-r--r--src/plugins/coreplugin/locator/urllocatorfilter.h1
-rw-r--r--src/plugins/coreplugin/loggingviewer.cpp4
-rw-r--r--src/plugins/coreplugin/manhattanstyle.cpp46
-rw-r--r--src/plugins/coreplugin/messagemanager.cpp8
-rw-r--r--src/plugins/coreplugin/messagemanager.h2
-rw-r--r--src/plugins/coreplugin/minisplitter.cpp2
-rw-r--r--src/plugins/coreplugin/outputpanemanager.cpp22
-rw-r--r--src/plugins/coreplugin/plugindialog.cpp2
-rw-r--r--src/plugins/coreplugin/plugininstallwizard.cpp15
-rw-r--r--src/plugins/coreplugin/plugininstallwizard.h12
-rw-r--r--src/plugins/coreplugin/progressmanager/futureprogress.cpp2
-rw-r--r--src/plugins/coreplugin/progressmanager/progressbar.cpp9
-rw-r--r--src/plugins/coreplugin/progressmanager/progressmanager.cpp2
-rw-r--r--src/plugins/coreplugin/welcomepagehelper.cpp22
-rw-r--r--src/plugins/coreplugin/welcomepagehelper.h5
-rw-r--r--src/plugins/cpaster/CodePaster.json.in2
-rw-r--r--src/plugins/cppcheck/Cppcheck.json.in2
-rw-r--r--src/plugins/cppcheck/cppcheckrunner.cpp13
-rw-r--r--src/plugins/cppcheck/cppchecksettings.cpp2
-rw-r--r--src/plugins/cppcheck/cppchecksettings.h2
-rw-r--r--src/plugins/cppcheck/cppchecktool.cpp8
-rw-r--r--src/plugins/cppcheck/cppchecktool.h1
-rw-r--r--src/plugins/cppeditor/CMakeLists.txt48
-rw-r--r--src/plugins/cppeditor/CppEditor.json.in2
-rw-r--r--src/plugins/cppeditor/clangdsettings.cpp2
-rw-r--r--src/plugins/cppeditor/compileroptionsbuilder.cpp4
-rw-r--r--src/plugins/cppeditor/cppcodegen_test.cpp2
-rw-r--r--src/plugins/cppeditor/cppcodemodelinspectordialog.cpp4
-rw-r--r--src/plugins/cppeditor/cppcodestylesettingspage.cpp4
-rw-r--r--src/plugins/cppeditor/cppeditor.qbs103
-rw-r--r--src/plugins/cppeditor/cppeditor.qrc16
-rw-r--r--src/plugins/cppeditor/cppeditordocument.cpp2
-rw-r--r--src/plugins/cppeditor/cppeditorplugin.cpp14
-rw-r--r--src/plugins/cppeditor/cppeditorwidget.cpp2
-rw-r--r--src/plugins/cppeditor/cppfunctiondecldeflink.cpp10
-rw-r--r--src/plugins/cppeditor/cpplocatorfilter.cpp4
-rw-r--r--src/plugins/cppeditor/cppmodelmanager.cpp10
-rw-r--r--src/plugins/cppeditor/cppoutlinemodel.cpp2
-rw-r--r--src/plugins/cppeditor/cppquickfix.cpp26
-rw-r--r--src/plugins/cppeditor/cppquickfix_test.cpp10051
-rw-r--r--src/plugins/cppeditor/cppquickfix_test.h239
-rw-r--r--src/plugins/cppeditor/cppquickfixes.cpp10675
-rw-r--r--src/plugins/cppeditor/cppquickfixes.h644
-rw-r--r--src/plugins/cppeditor/cpprefactoringchanges.cpp50
-rw-r--r--src/plugins/cppeditor/cpprefactoringchanges.h5
-rw-r--r--src/plugins/cppeditor/cpprenaming_test.cpp2
-rw-r--r--src/plugins/cppeditor/cppsemanticinfoupdater.cpp4
-rw-r--r--src/plugins/cppeditor/cpptoolsreuse.cpp11
-rw-r--r--src/plugins/cppeditor/cppuseselectionsupdater.cpp4
-rw-r--r--src/plugins/cppeditor/fileandtokenactions_test.cpp6
-rw-r--r--src/plugins/cppeditor/quickfixes/assigntolocalvariable.cpp510
-rw-r--r--src/plugins/cppeditor/quickfixes/assigntolocalvariable.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/bringidentifierintoscope.cpp1497
-rw-r--r--src/plugins/cppeditor/quickfixes/bringidentifierintoscope.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/completeswitchstatement.cpp793
-rw-r--r--src/plugins/cppeditor/quickfixes/completeswitchstatement.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/convertfromandtopointer.cpp704
-rw-r--r--src/plugins/cppeditor/quickfixes/convertfromandtopointer.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/convertnumericliteral.cpp199
-rw-r--r--src/plugins/cppeditor/quickfixes/convertnumericliteral.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/convertqt4connect.cpp505
-rw-r--r--src/plugins/cppeditor/quickfixes/convertqt4connect.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/convertstringliteral.cpp746
-rw-r--r--src/plugins/cppeditor/quickfixes/convertstringliteral.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/converttocamelcase.cpp184
-rw-r--r--src/plugins/cppeditor/quickfixes/converttocamelcase.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/converttometamethodcall.cpp273
-rw-r--r--src/plugins/cppeditor/quickfixes/converttometamethodcall.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/cppcodegenerationquickfixes.cpp5072
-rw-r--r--src/plugins/cppeditor/quickfixes/cppcodegenerationquickfixes.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/cppinsertvirtualmethods.cpp (renamed from src/plugins/cppeditor/cppinsertvirtualmethods.cpp)40
-rw-r--r--src/plugins/cppeditor/quickfixes/cppinsertvirtualmethods.h (renamed from src/plugins/cppeditor/cppinsertvirtualmethods.h)16
-rw-r--r--src/plugins/cppeditor/quickfixes/cppquickfix.cpp187
-rw-r--r--src/plugins/cppeditor/quickfixes/cppquickfix.h (renamed from src/plugins/cppeditor/cppquickfix.h)28
-rw-r--r--src/plugins/cppeditor/quickfixes/cppquickfix_test.cpp251
-rw-r--r--src/plugins/cppeditor/quickfixes/cppquickfix_test.h94
-rw-r--r--src/plugins/cppeditor/quickfixes/cppquickfixassistant.cpp (renamed from src/plugins/cppeditor/cppquickfixassistant.cpp)6
-rw-r--r--src/plugins/cppeditor/quickfixes/cppquickfixassistant.h (renamed from src/plugins/cppeditor/cppquickfixassistant.h)2
-rw-r--r--src/plugins/cppeditor/quickfixes/cppquickfixhelpers.cpp199
-rw-r--r--src/plugins/cppeditor/quickfixes/cppquickfixhelpers.h47
-rw-r--r--src/plugins/cppeditor/quickfixes/cppquickfixprojectsettings.cpp (renamed from src/plugins/cppeditor/cppquickfixprojectsettings.cpp)4
-rw-r--r--src/plugins/cppeditor/quickfixes/cppquickfixprojectsettings.h (renamed from src/plugins/cppeditor/cppquickfixprojectsettings.h)0
-rw-r--r--src/plugins/cppeditor/quickfixes/cppquickfixprojectsettingswidget.cpp (renamed from src/plugins/cppeditor/cppquickfixprojectsettingswidget.cpp)4
-rw-r--r--src/plugins/cppeditor/quickfixes/cppquickfixprojectsettingswidget.h (renamed from src/plugins/cppeditor/cppquickfixprojectsettingswidget.h)0
-rw-r--r--src/plugins/cppeditor/quickfixes/cppquickfixsettings.cpp (renamed from src/plugins/cppeditor/cppquickfixsettings.cpp)4
-rw-r--r--src/plugins/cppeditor/quickfixes/cppquickfixsettings.h (renamed from src/plugins/cppeditor/cppquickfixsettings.h)0
-rw-r--r--src/plugins/cppeditor/quickfixes/cppquickfixsettingspage.cpp (renamed from src/plugins/cppeditor/cppquickfixsettingspage.cpp)4
-rw-r--r--src/plugins/cppeditor/quickfixes/cppquickfixsettingspage.h (renamed from src/plugins/cppeditor/cppquickfixsettingspage.h)0
-rw-r--r--src/plugins/cppeditor/quickfixes/cppquickfixsettingswidget.cpp (renamed from src/plugins/cppeditor/cppquickfixsettingswidget.cpp)6
-rw-r--r--src/plugins/cppeditor/quickfixes/cppquickfixsettingswidget.h (renamed from src/plugins/cppeditor/cppquickfixsettingswidget.h)0
-rw-r--r--src/plugins/cppeditor/quickfixes/createdeclarationfromuse.cpp1207
-rw-r--r--src/plugins/cppeditor/quickfixes/createdeclarationfromuse.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/extractfunction.cpp762
-rw-r--r--src/plugins/cppeditor/quickfixes/extractfunction.h7
-rw-r--r--src/plugins/cppeditor/quickfixes/extractliteralasparameter.cpp560
-rw-r--r--src/plugins/cppeditor/quickfixes/extractliteralasparameter.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/insertfunctiondefinition.cpp2124
-rw-r--r--src/plugins/cppeditor/quickfixes/insertfunctiondefinition.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/logicaloperationquickfixes.cpp381
-rw-r--r--src/plugins/cppeditor/quickfixes/logicaloperationquickfixes.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/moveclasstoownfile.cpp721
-rw-r--r--src/plugins/cppeditor/quickfixes/moveclasstoownfile.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/movefunctiondefinition.cpp1893
-rw-r--r--src/plugins/cppeditor/quickfixes/movefunctiondefinition.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/rearrangeparamdeclarationlist.cpp121
-rw-r--r--src/plugins/cppeditor/quickfixes/rearrangeparamdeclarationlist.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/reformatpointerdeclaration.cpp186
-rw-r--r--src/plugins/cppeditor/quickfixes/reformatpointerdeclaration.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/removeusingnamespace.cpp957
-rw-r--r--src/plugins/cppeditor/quickfixes/removeusingnamespace.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/rewritecomment.cpp873
-rw-r--r--src/plugins/cppeditor/quickfixes/rewritecomment.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/rewritecontrolstatements.cpp1323
-rw-r--r--src/plugins/cppeditor/quickfixes/rewritecontrolstatements.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/splitsimpledeclaration.cpp140
-rw-r--r--src/plugins/cppeditor/quickfixes/splitsimpledeclaration.h8
-rw-r--r--src/plugins/cppeditor/quickfixes/synchronizememberfunctionorder.cpp335
-rw-r--r--src/plugins/cppeditor/quickfixes/synchronizememberfunctionorder.h8
-rw-r--r--src/plugins/cppeditor/testcases/reorder-member-impls/already-sorted/already-sorted.pro1
-rw-r--r--src/plugins/cppeditor/testcases/reorder-member-impls/already-sorted/header.h9
-rw-r--r--src/plugins/cppeditor/testcases/reorder-member-impls/already-sorted/header.h_expected9
-rw-r--r--src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/different-locations.pro2
-rw-r--r--src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/header.h15
-rw-r--r--src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/header.h_expected15
-rw-r--r--src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/impl1.cpp5
-rw-r--r--src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/impl1.cpp_expected5
-rw-r--r--src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/impl2.cpp6
-rw-r--r--src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/impl2.cpp_expected6
-rw-r--r--src/plugins/cppeditor/testcases/reorder-member-impls/no-out-of-line/header.h5
-rw-r--r--src/plugins/cppeditor/testcases/reorder-member-impls/no-out-of-line/header.h_expected5
-rw-r--r--src/plugins/cppeditor/testcases/reorder-member-impls/no-out-of-line/no-out-of-line.pro1
-rw-r--r--src/plugins/cppeditor/testcases/reorder-member-impls/templates/header.h10
-rw-r--r--src/plugins/cppeditor/testcases/reorder-member-impls/templates/header.h_expected10
-rw-r--r--src/plugins/cppeditor/testcases/reorder-member-impls/templates/templates.pro1
-rw-r--r--src/plugins/cvs/CVS.json.in2
-rw-r--r--src/plugins/debugger/Debugger.json.in2
-rw-r--r--src/plugins/debugger/cdb/cdbengine.cpp8
-rw-r--r--src/plugins/debugger/commonoptionspage.cpp2
-rw-r--r--src/plugins/debugger/commonoptionspage.h2
-rw-r--r--src/plugins/debugger/console/consoleitemdelegate.cpp11
-rw-r--r--src/plugins/debugger/debuggerengine.h3
-rw-r--r--src/plugins/debugger/debuggeritemmanager.cpp6
-rw-r--r--src/plugins/debugger/debuggerkitaspect.cpp4
-rw-r--r--src/plugins/debugger/debuggerrunconfigurationaspect.cpp2
-rw-r--r--src/plugins/debugger/debuggerruncontrol.cpp2
-rw-r--r--src/plugins/debugger/debuggersourcepathmappingwidget.cpp2
-rw-r--r--src/plugins/debugger/gdb/gdbengine.cpp6
-rw-r--r--src/plugins/debugger/lldb/lldbengine.cpp11
-rw-r--r--src/plugins/debugger/logwindow.cpp15
-rw-r--r--src/plugins/debugger/qml/qmlengine.cpp14
-rw-r--r--src/plugins/debugger/qml/qmlinspectoragent.cpp6
-rw-r--r--src/plugins/debugger/watchdata.h2
-rw-r--r--src/plugins/debugger/watchdelegatewidgets.cpp50
-rw-r--r--src/plugins/debugger/watchdelegatewidgets.h2
-rw-r--r--src/plugins/debugger/watchhandler.cpp26
-rw-r--r--src/plugins/designer/CMakeLists.txt6
-rw-r--r--src/plugins/designer/Designer.json.in4
-rw-r--r--src/plugins/designer/cpp/formclasswizarddialog.cpp2
-rw-r--r--src/plugins/designer/designer.qbs4
-rw-r--r--src/plugins/designer/designerplugin.cpp8
-rw-r--r--src/plugins/designer/formeditor.cpp4
-rw-r--r--src/plugins/designer/formeditor.h2
-rw-r--r--src/plugins/designer/formeditorstack.cpp13
-rw-r--r--src/plugins/designer/formeditorstack.h5
-rw-r--r--src/plugins/designer/formtemplatewizardpage.cpp2
-rw-r--r--src/plugins/designer/formtemplatewizardpage.h2
-rw-r--r--src/plugins/designer/qtcreatorintegration.cpp5
-rw-r--r--src/plugins/designer/qtdesignerformclasscodegenerator.cpp2
-rw-r--r--src/plugins/diffeditor/DiffEditor.json.in2
-rw-r--r--src/plugins/diffeditor/diffeditorplugin.cpp3
-rw-r--r--src/plugins/diffeditor/selectabletexteditorwidget.cpp10
-rw-r--r--src/plugins/diffeditor/sidebysidediffeditorwidget.cpp3
-rw-r--r--src/plugins/diffeditor/unifieddiffeditorwidget.cpp3
-rw-r--r--src/plugins/docker/Docker.json.in2
-rw-r--r--src/plugins/docker/dockerapi.cpp5
-rw-r--r--src/plugins/docker/dockerdevice.cpp6
-rw-r--r--src/plugins/effectcomposer/EffectComposer.json.in4
-rw-r--r--src/plugins/effectcomposer/compositionnode.cpp2
-rw-r--r--src/plugins/effectcomposer/effectcomposermodel.cpp69
-rw-r--r--src/plugins/effectcomposer/effectcomposermodel.h7
-rw-r--r--src/plugins/emacskeys/EmacsKeys.json.in2
-rw-r--r--src/plugins/extensionmanager/CMakeLists.txt10
-rw-r--r--src/plugins/extensionmanager/ExtensionManager.json.in2
-rw-r--r--src/plugins/extensionmanager/extensionmanager.qbs10
-rw-r--r--src/plugins/extensionmanager/extensionmanager_test.cpp40
-rw-r--r--src/plugins/extensionmanager/extensionmanager_test.h14
-rw-r--r--src/plugins/extensionmanager/extensionmanager_test.qrc6
-rw-r--r--src/plugins/extensionmanager/extensionmanagerplugin.cpp8
-rw-r--r--src/plugins/extensionmanager/extensionmanagerwidget.cpp489
-rw-r--r--src/plugins/extensionmanager/extensionmanagerwidget.h18
-rw-r--r--src/plugins/extensionmanager/extensionsbrowser.cpp453
-rw-r--r--src/plugins/extensionmanager/extensionsbrowser.h48
-rw-r--r--src/plugins/extensionmanager/extensionsmodel.cpp411
-rw-r--r--src/plugins/extensionmanager/extensionsmodel.h68
-rw-r--r--src/plugins/extensionmanager/testdata/defaultpacks.json161
-rw-r--r--src/plugins/extensionmanager/testdata/thirdpartyplugins.json38
-rw-r--r--src/plugins/fakevim/FakeVim.json.in2
-rw-r--r--src/plugins/fakevim/fakevimactions.cpp8
-rw-r--r--src/plugins/fakevim/fakevimhandler.cpp2
-rw-r--r--src/plugins/fossil/Fossil.json.in2
-rw-r--r--src/plugins/fossil/fossilplugin.cpp26
-rw-r--r--src/plugins/genericprojectmanager/GenericProjectManager.json.in2
-rw-r--r--src/plugins/git/Git.json.in2
-rw-r--r--src/plugins/git/changeselectiondialog.cpp6
-rw-r--r--src/plugins/git/gerrit/gerritpushdialog.cpp2
-rw-r--r--src/plugins/git/gitclient.cpp6
-rw-r--r--src/plugins/git/gitplugin.cpp6
-rw-r--r--src/plugins/git/gitsettings.cpp2
-rw-r--r--src/plugins/git/gitsubmiteditor.cpp3
-rw-r--r--src/plugins/git/gitsubmiteditorwidget.cpp3
-rw-r--r--src/plugins/git/instantblame.cpp7
-rw-r--r--src/plugins/git/mergetool.cpp4
-rw-r--r--src/plugins/gitlab/GitLab.json.in2
-rw-r--r--src/plugins/gitlab/gitlaboptionspage.cpp2
-rw-r--r--src/plugins/glsleditor/GLSLEditor.json.in2
-rw-r--r--src/plugins/helloworld/HelloWorld.json.in2
-rw-r--r--src/plugins/help/Help.json.in2
-rw-r--r--src/plugins/help/generalsettingspage.cpp9
-rw-r--r--src/plugins/help/helpindexfilter.cpp2
-rw-r--r--src/plugins/help/helpmanager.cpp12
-rw-r--r--src/plugins/help/helpmanager.h2
-rw-r--r--src/plugins/help/helpplugin.cpp4
-rw-r--r--src/plugins/help/helpwidget.cpp26
-rw-r--r--src/plugins/help/localhelpmanager.cpp65
-rw-r--r--src/plugins/help/localhelpmanager.h6
-rw-r--r--src/plugins/imageviewer/ImageViewer.json.in2
-rw-r--r--src/plugins/incredibuild/IncrediBuild.json.in2
-rw-r--r--src/plugins/incredibuild/commandbuilderaspect.cpp2
-rw-r--r--src/plugins/incredibuild/commandbuilderaspect.h2
-rw-r--r--src/plugins/insight/Insight.json.in2
-rw-r--r--src/plugins/ios/Ios.json.in2
-rw-r--r--src/plugins/ios/iosconfigurations.cpp7
-rw-r--r--src/plugins/ios/iosrunconfiguration.cpp2
-rw-r--r--src/plugins/ios/iosrunconfiguration.h2
-rw-r--r--src/plugins/ios/iosrunner.cpp22
-rw-r--r--src/plugins/ios/simulatorcontrol.cpp6
-rw-r--r--src/plugins/languageclient/LanguageClient.json.in2
-rw-r--r--src/plugins/languageclient/client.cpp2
-rw-r--r--src/plugins/languageclient/client.h1
-rw-r--r--src/plugins/languageclient/clientrequest.cpp2
-rw-r--r--src/plugins/languageclient/languageclientmanager.cpp6
-rw-r--r--src/plugins/languageclient/languageclientmanager.h4
-rw-r--r--src/plugins/languageclient/languageclientsettings.cpp10
-rw-r--r--src/plugins/languageclient/languageclientsettings.h5
-rw-r--r--src/plugins/languageclient/languageclientutils.cpp3
-rw-r--r--src/plugins/languageclient/locatorfilter.cpp4
-rw-r--r--src/plugins/languageclient/lualanguageclient/LuaLanguageClient.json.in2
-rw-r--r--src/plugins/languageclient/lualanguageclient/lualanguageclient.cpp83
-rw-r--r--src/plugins/lua/CMakeLists.txt45
-rw-r--r--src/plugins/lua/Lua.json.in2
-rw-r--r--src/plugins/lua/bindings/fetch.cpp25
-rw-r--r--src/plugins/lua/bindings/hook.cpp22
-rw-r--r--src/plugins/lua/bindings/inheritance.h53
-rw-r--r--src/plugins/lua/bindings/install.cpp351
-rw-r--r--src/plugins/lua/bindings/layout.cpp463
-rw-r--r--src/plugins/lua/bindings/qtcprocess.cpp25
-rw-r--r--src/plugins/lua/bindings/settings.cpp4
-rw-r--r--src/plugins/lua/bindings/utils.cpp45
-rw-r--r--src/plugins/lua/lua.qbs4
-rw-r--r--src/plugins/lua/luaengine.cpp121
-rw-r--r--src/plugins/lua/luaengine.h11
-rw-r--r--src/plugins/lua/luaplugin.cpp21
-rw-r--r--src/plugins/lua/luapluginspec.cpp5
-rw-r--r--src/plugins/lua/luapluginspec.h3
-rw-r--r--src/plugins/lua/meta/install.lua29
-rw-r--r--src/plugins/lua/meta/layout.lua113
-rw-r--r--src/plugins/lua/meta/lsp.lua1
-rw-r--r--src/plugins/lua/meta/qtc.lua1
-rw-r--r--src/plugins/lua/meta/utils.lua19
-rw-r--r--src/plugins/lualsp/lualsp/init.lua124
-rw-r--r--src/plugins/lualsp/lualsp/lualsp.lua6
-rw-r--r--src/plugins/luatemplates/CMakeLists.txt10
-rw-r--r--src/plugins/luatemplates/luatemplates.cpp402
-rw-r--r--src/plugins/luatemplates/templates/CMakeLists.txt4
-rw-r--r--src/plugins/luatemplates/templates/basic_templates/basic_templates.lua13
-rw-r--r--src/plugins/luatemplates/templates/basic_templates/init.lua65
-rw-r--r--src/plugins/luatests/luatests/luatests.lua1
-rw-r--r--src/plugins/macros/Macros.json.in2
-rw-r--r--src/plugins/marketplace/Marketplace.json.in2
-rw-r--r--src/plugins/mcusupport/McuSupport.json.in2
-rw-r--r--src/plugins/mcusupport/mcukitaspect.cpp6
-rw-r--r--src/plugins/mercurial/Mercurial.json.in2
-rw-r--r--src/plugins/mercurial/mercurialplugin.cpp10
-rw-r--r--src/plugins/mesonprojectmanager/MesonProjectManager.json.in2
-rw-r--r--src/plugins/mesonprojectmanager/buildoptionsmodel.cpp10
-rw-r--r--src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp2
-rw-r--r--src/plugins/mesonprojectmanager/toolkitaspectwidget.h2
-rw-r--r--src/plugins/modeleditor/ModelEditor.json.in2
-rw-r--r--src/plugins/modeleditor/componentviewcontroller.cpp13
-rw-r--r--src/plugins/modeleditor/elementtasks.cpp15
-rw-r--r--src/plugins/modeleditor/extdocumentcontroller.cpp4
-rw-r--r--src/plugins/modeleditor/extpropertiesmview.cpp57
-rw-r--r--src/plugins/modeleditor/jsextension.cpp4
-rw-r--r--src/plugins/modeleditor/modeldocument.cpp14
-rw-r--r--src/plugins/modeleditor/modeleditor.cpp11
-rw-r--r--src/plugins/modeleditor/modelindexer.cpp12
-rw-r--r--src/plugins/modeleditor/modelsmanager.cpp4
-rw-r--r--src/plugins/modeleditor/pxnodecontroller.cpp14
-rw-r--r--src/plugins/modeleditor/pxnodeutilities.cpp12
-rw-r--r--src/plugins/nim/Nim.json.in2
-rw-r--r--src/plugins/nim/settings/nimsettings.cpp2
-rw-r--r--src/plugins/perforce/Perforce.json.in2
-rw-r--r--src/plugins/perforce/perforcechecker.cpp8
-rw-r--r--src/plugins/perforce/perforcesettings.cpp2
-rw-r--r--src/plugins/perfprofiler/PerfProfiler.json.in2
-rw-r--r--src/plugins/perfprofiler/perfprofilerflamegraphview.cpp2
-rw-r--r--src/plugins/perfprofiler/perftimelinemodel.cpp8
-rw-r--r--src/plugins/perfprofiler/perftimelineresourcesrenderpass.cpp2
-rw-r--r--src/plugins/perfprofiler/perftracepointdialog.cpp2
-rw-r--r--src/plugins/plugins.qbs3
-rw-r--r--src/plugins/projectexplorer/CMakeLists.txt1
-rw-r--r--src/plugins/projectexplorer/ProjectExplorer.json.in9
-rw-r--r--src/plugins/projectexplorer/buildaspects.cpp2
-rw-r--r--src/plugins/projectexplorer/buildaspects.h2
-rw-r--r--src/plugins/projectexplorer/buildconfiguration.cpp4
-rw-r--r--src/plugins/projectexplorer/buildstep.cpp8
-rw-r--r--src/plugins/projectexplorer/codestylesettingspropertiespage.cpp1
-rw-r--r--src/plugins/projectexplorer/customparserconfigdialog.cpp6
-rw-r--r--src/plugins/projectexplorer/devicesupport/desktopdevice.cpp4
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicesettingspage.cpp5
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp4
-rw-r--r--src/plugins/projectexplorer/extracompiler.cpp5
-rw-r--r--src/plugins/projectexplorer/gccparser.cpp278
-rw-r--r--src/plugins/projectexplorer/gccparser.h30
-rw-r--r--src/plugins/projectexplorer/ioutputparser.cpp97
-rw-r--r--src/plugins/projectexplorer/ioutputparser.h20
-rw-r--r--src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp24
-rw-r--r--src/plugins/projectexplorer/jsonwizard/jsonkitspage.cpp6
-rw-r--r--src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp8
-rw-r--r--src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp25
-rw-r--r--src/plugins/projectexplorer/jsonwizard/jsonwizardfilegenerator.cpp2
-rw-r--r--src/plugins/projectexplorer/jsonwizard/jsonwizardpagefactory_p.cpp8
-rw-r--r--src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.cpp2
-rw-r--r--src/plugins/projectexplorer/jsonwizard/wizarddebug.h4
-rw-r--r--src/plugins/projectexplorer/kitaspects.cpp16
-rw-r--r--src/plugins/projectexplorer/kitmanager.cpp2
-rw-r--r--src/plugins/projectexplorer/kitmanager.h4
-rw-r--r--src/plugins/projectexplorer/kitmanagerconfigwidget.cpp2
-rw-r--r--src/plugins/projectexplorer/kitmanagerconfigwidget.h2
-rw-r--r--src/plugins/projectexplorer/ldparser.cpp24
-rw-r--r--src/plugins/projectexplorer/ldparser.h4
-rw-r--r--src/plugins/projectexplorer/linuxiccparser.cpp50
-rw-r--r--src/plugins/projectexplorer/linuxiccparser.h4
-rw-r--r--src/plugins/projectexplorer/makestep.cpp2
-rw-r--r--src/plugins/projectexplorer/miniprojecttargetselector.cpp11
-rw-r--r--src/plugins/projectexplorer/msvcparser.cpp190
-rw-r--r--src/plugins/projectexplorer/msvcparser.h10
-rw-r--r--src/plugins/projectexplorer/msvctoolchain.cpp6
-rw-r--r--src/plugins/projectexplorer/processparameters.cpp2
-rw-r--r--src/plugins/projectexplorer/project.cpp8
-rw-r--r--src/plugins/projectexplorer/project.h4
-rw-r--r--src/plugins/projectexplorer/projectexplorer.cpp60
-rw-r--r--src/plugins/projectexplorer/projectexplorer.h2
-rw-r--r--src/plugins/projectexplorer/projectexplorer.qbs1
-rw-r--r--src/plugins/projectexplorer/projectexplorersettings.cpp4
-rw-r--r--src/plugins/projectexplorer/projectmodels.cpp2
-rw-r--r--src/plugins/projectexplorer/projectnodeshelper.h42
-rw-r--r--src/plugins/projectexplorer/projecttreewidget.cpp2
-rw-r--r--src/plugins/projectexplorer/projectwelcomepage.cpp19
-rw-r--r--src/plugins/projectexplorer/projectwindow.cpp2
-rw-r--r--src/plugins/projectexplorer/projectwizardpage.cpp10
-rw-r--r--src/plugins/projectexplorer/projectwizardpage.h4
-rw-r--r--src/plugins/projectexplorer/runconfiguration.cpp4
-rw-r--r--src/plugins/projectexplorer/runconfigurationaspects.cpp18
-rw-r--r--src/plugins/projectexplorer/runconfigurationaspects.h8
-rw-r--r--src/plugins/projectexplorer/runcontrol.cpp2
-rw-r--r--src/plugins/projectexplorer/targetsettingspanel.cpp2
-rw-r--r--src/plugins/projectexplorer/treescanner.cpp45
-rw-r--r--src/plugins/projectexplorer/treescanner.h11
-rw-r--r--src/plugins/projectexplorer/userfileaccessor.cpp22
-rw-r--r--src/plugins/projectexplorer/workspaceproject.cpp210
-rw-r--r--src/plugins/projectexplorer/workspaceproject.h16
-rw-r--r--src/plugins/python/Python.json.in2
-rw-r--r--src/plugins/python/pythonbuildconfiguration.cpp4
-rw-r--r--src/plugins/python/pythonkitaspect.cpp2
-rw-r--r--src/plugins/python/pythonlanguageclient.cpp2
-rw-r--r--src/plugins/python/pythonrunconfiguration.cpp21
-rw-r--r--src/plugins/python/pythonsettings.cpp2
-rw-r--r--src/plugins/python/pythonutils.cpp6
-rw-r--r--src/plugins/qbsprojectmanager/QbsProjectManager.json.in2
-rw-r--r--src/plugins/qbsprojectmanager/customqbspropertiesdialog.cpp2
-rw-r--r--src/plugins/qbsprojectmanager/qbsbuildstep.cpp2
-rw-r--r--src/plugins/qbsprojectmanager/qbsbuildstep.h2
-rw-r--r--src/plugins/qbsprojectmanager/qbskitaspect.cpp2
-rw-r--r--src/plugins/qbsprojectmanager/qbsprofilemanager.cpp6
-rw-r--r--src/plugins/qbsprojectmanager/qbsprofilessettingspage.cpp4
-rw-r--r--src/plugins/qbsprojectmanager/qbssession.cpp28
-rw-r--r--src/plugins/qbsprojectmanager/qbssession.h2
-rw-r--r--src/plugins/qmakeprojectmanager/QmakeProjectManager.json.in2
-rw-r--r--src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwizarddialog.cpp4
-rw-r--r--src/plugins/qmakeprojectmanager/qmakekitaspect.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/qmakestep.cpp6
-rw-r--r--src/plugins/qmldesigner/CMakeLists.txt36
-rw-r--r--src/plugins/qmldesigner/QmlDesigner.json.in2
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp2
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp48
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h2
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp32
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h5
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp86
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h34
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp965
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h152
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp627
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h106
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp163
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h58
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h19
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp345
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h46
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp521
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h79
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp501
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionview.h124
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp320
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h81
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp511
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h63
-rw-r--r--src/plugins/qmldesigner/components/componentcore/abstractaction.cpp4
-rw-r--r--src/plugins/qmldesigner/components/componentcore/abstractaction.h5
-rw-r--r--src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp10
-rw-r--r--src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h5
-rw-r--r--src/plugins/qmldesigner/components/componentcore/componentcore_constants.h3
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp19
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designeractionmanager.h2
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h21
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp46
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h1
-rw-r--r--src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp4
-rw-r--r--src/plugins/qmldesigner/components/componentcore/viewmanager.cpp18
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp28
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h3
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp3
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp31
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h3
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp159
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h38
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp78
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp10
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h12
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp155
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h35
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.cpp76
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.h (renamed from src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h)18
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp7
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h7
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp179
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h47
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp5
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h2
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp630
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h101
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp622
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h33
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp145
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h36
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp2
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp48
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h3
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dview.cpp205
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dview.h13
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp64
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dwidget.h1
-rw-r--r--src/plugins/qmldesigner/components/formeditor/dragtool.cpp23
-rw-r--r--src/plugins/qmldesigner/components/formeditor/dragtool.h1
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp12
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp2
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp22
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditortoolbutton.cpp2
-rw-r--r--src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp4
-rw-r--r--src/plugins/qmldesigner/components/integration/designdocument.cpp27
-rw-r--r--src/plugins/qmldesigner/components/integration/designdocument.h11
-rw-r--r--src/plugins/qmldesigner/components/integration/designdocumentview.cpp10
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp82
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h37
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp47
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h28
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp281
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h33
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui76
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp109
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h36
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp80
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp4
-rw-r--r--src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp2
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp11
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h5
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp5
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp12
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h5
-rw-r--r--src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp19
-rw-r--r--src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h6
-rw-r--r--src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp101
-rw-r--r--src/plugins/qmldesigner/components/materialeditor/materialeditorview.h2
-rw-r--r--src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp7
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp4
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp8
-rw-r--r--src/plugins/qmldesigner/components/navigator/previewtooltip.cpp3
-rw-r--r--src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp3
-rw-r--r--src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp13
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp3
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp33
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h8
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp22
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h2
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp1
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp22
-rw-r--r--src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.cpp3
-rw-r--r--src/plugins/qmldesigner/components/texteditor/texteditorview.cpp3
-rw-r--r--src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp24
-rw-r--r--src/plugins/qmldesigner/components/texttool/textedititemwidget.h7
-rw-r--r--src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp24
-rw-r--r--src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h8
-rw-r--r--src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp4
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp6
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp4
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp1
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinesettingsmodel.cpp2
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp4
-rw-r--r--src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp160
-rw-r--r--src/plugins/qmldesigner/designercore/generatedcomponentutils.h17
-rw-r--r--src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h1
-rw-r--r--src/plugins/qmldesigner/designercore/include/itemlibraryentry.h13
-rw-r--r--src/plugins/qmldesigner/designercore/include/model.h9
-rw-r--r--src/plugins/qmldesigner/designercore/include/nodehints.h9
-rw-r--r--src/plugins/qmldesigner/designercore/include/nodemetainfo.h9
-rw-r--r--src/plugins/qmldesigner/designercore/include/projectstorageids.h2
-rw-r--r--src/plugins/qmldesigner/designercore/include/rewriterview.h8
-rw-r--r--src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp35
-rw-r--r--src/plugins/qmldesigner/designercore/metainfo/itemlibraryentry.cpp23
-rw-r--r--src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp2
-rw-r--r--src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp35
-rw-r--r--src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp1469
-rw-r--r--src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp5
-rw-r--r--src/plugins/qmldesigner/designercore/model/model.cpp123
-rw-r--r--src/plugins/qmldesigner/designercore/model/model_p.h2
-rw-r--r--src/plugins/qmldesigner/designercore/model/propertycontainer.cpp2
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp27
-rw-r--r--src/plugins/qmldesigner/designercore/model/rewriteactioncompressor.cpp3
-rw-r--r--src/plugins/qmldesigner/designercore/model/rewriterview.cpp98
-rw-r--r--src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp16
-rw-r--r--src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp37
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h247
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/filesystem.cpp13
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/filesystem.h1
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/filesysteminterface.h1
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp752
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h190
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.cpp17
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.h25
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifierinterface.h27
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp18
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h12
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h45
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h18
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h42
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp300
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h24
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp29
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp33
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h2
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h2
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h5
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/storagecache.h2
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp18
-rw-r--r--src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp9
-rw-r--r--src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h16
-rw-r--r--src/plugins/qmldesigner/designercore/uniquename.cpp165
-rw-r--r--src/plugins/qmldesigner/designercore/uniquename.h17
-rw-r--r--src/plugins/qmldesigner/designmodecontext.cpp12
-rw-r--r--src/plugins/qmldesigner/designmodecontext.h9
-rw-r--r--src/plugins/qmldesigner/designmodewidget.h1
-rw-r--r--src/plugins/qmldesigner/documentwarningwidget.cpp4
-rw-r--r--src/plugins/qmldesigner/qmldesignerconstants.h20
-rw-r--r--src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp5
-rw-r--r--src/plugins/qmldesigner/qmldesignerexternaldependencies.h1
-rw-r--r--src/plugins/qmldesigner/qmldesignerplugin.cpp4
-rw-r--r--src/plugins/qmldesigner/qmldesignerprojectmanager.cpp105
-rw-r--r--src/plugins/qmldesigner/qmldesignerprojectmanager.h5
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/frame-animation-16px.pngbin0 -> 319 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px.pngbin0 -> 403 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px@2x.pngbin0 -> 496 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc3
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/quick.metainfo20
-rw-r--r--src/plugins/qmldesigner/settingspage.cpp3
-rw-r--r--src/plugins/qmldesigner/shortcutmanager.cpp6
-rw-r--r--src/plugins/qmldesigner/shortcutmanager.h1
-rw-r--r--src/plugins/qmldesignerbase/QmlDesignerBase.json.in2
-rw-r--r--src/plugins/qmldesignerbase/studio/studiostyle.cpp30
-rw-r--r--src/plugins/qmldesignerbase/studio/studiostyle_p.cpp4
-rw-r--r--src/plugins/qmldesignerlite/QmlDesignerLite.json.in2
-rw-r--r--src/plugins/qmljseditor/QmlJSEditor.json.in2
-rw-r--r--src/plugins/qmljseditor/qmljscomponentfromobjectdef.cpp5
-rw-r--r--src/plugins/qmljseditor/qmljseditingsettingspage.cpp5
-rw-r--r--src/plugins/qmljseditor/qmljsquickfixes.cpp11
-rw-r--r--src/plugins/qmljseditor/qmljswrapinloader.cpp3
-rw-r--r--src/plugins/qmljseditor/qmloutlinemodel.cpp6
-rw-r--r--src/plugins/qmljstools/QmlJSTools.json.in2
-rw-r--r--src/plugins/qmljstools/qmljsfunctionfilter.cpp3
-rw-r--r--src/plugins/qmlpreview/QmlPreview.json.in2
-rw-r--r--src/plugins/qmlprofiler/QmlProfiler.json.in2
-rw-r--r--src/plugins/qmlprofiler/flamegraphview.cpp2
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerbindingloopsrenderpass.cpp2
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp12
-rw-r--r--src/plugins/qmlprofiler/tests/localqmlprofilerrunner_test.cpp4
-rw-r--r--src/plugins/qmlprojectmanager/.clang-format50
-rw-r--r--src/plugins/qmlprojectmanager/QmlProjectManager.json.in2
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp7
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp7
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h2
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp99
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h13
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp43
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h4
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl2
-rw-r--r--src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp2
-rw-r--r--src/plugins/qmlprojectmanager/qmlmainfileaspect.h2
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectconstants.h1
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp6
-rw-r--r--src/plugins/qnx/Qnx.json.in2
-rw-r--r--src/plugins/qnx/slog2inforunner.cpp2
-rw-r--r--src/plugins/qtapplicationmanager/QtApplicationManagerIntegration.json.in2
-rw-r--r--src/plugins/qtsupport/QtSupport.json.in2
-rw-r--r--src/plugins/qtsupport/externaleditors.cpp7
-rw-r--r--src/plugins/qtsupport/qtbuildaspects.cpp8
-rw-r--r--src/plugins/qtsupport/qtbuildaspects.h4
-rw-r--r--src/plugins/qtsupport/qtcreator_tutorials.xml12
-rw-r--r--src/plugins/qtsupport/qtkitaspect.cpp2
-rw-r--r--src/plugins/qtsupport/qtversionmanager.cpp4
-rw-r--r--src/plugins/qtsupport/translationwizardpage.cpp2
-rw-r--r--src/plugins/remotelinux/RemoteLinux.json.in2
-rw-r--r--src/plugins/remotelinux/filesystemaccess_test.cpp2
-rw-r--r--src/plugins/remotelinux/linuxdevice.cpp23
-rw-r--r--src/plugins/remotelinux/linuxdevicetester.cpp3
-rw-r--r--src/plugins/remotelinux/makeinstallstep.cpp2
-rw-r--r--src/plugins/remotelinux/publickeydeploymentdialog.cpp2
-rw-r--r--src/plugins/remotelinux/sshdevicewizard.cpp1
-rw-r--r--src/plugins/remotelinux/sshkeycreationdialog.cpp8
-rw-r--r--src/plugins/resourceeditor/ResourceEditor.json.in2
-rw-r--r--src/plugins/resourceeditor/qrceditor/resourcefile.cpp2
-rw-r--r--src/plugins/rustls/CMakeLists.txt4
-rw-r--r--src/plugins/rustls/rustls/init.lua176
-rw-r--r--src/plugins/rustls/rustls/rustls.lua24
-rw-r--r--src/plugins/screenrecorder/ScreenRecorder.json.in2
-rw-r--r--src/plugins/screenrecorder/cropandtrim.cpp14
-rw-r--r--src/plugins/screenrecorder/export.cpp2
-rw-r--r--src/plugins/screenrecorder/ffmpegutils.cpp4
-rw-r--r--src/plugins/screenrecorder/record.cpp3
-rw-r--r--src/plugins/screenrecorder/screenrecorderplugin.cpp5
-rw-r--r--src/plugins/scxmleditor/ScxmlEditor.json.in2
-rw-r--r--src/plugins/scxmleditor/common/shapestoolbox.cpp2
-rw-r--r--src/plugins/scxmleditor/common/stateview.cpp2
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scattributeitemdelegate.cpp6
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scattributeitemmodel.cpp6
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scxmltypes.h112
-rw-r--r--src/plugins/serialterminal/SerialTerminal.json.in2
-rw-r--r--src/plugins/silversearcher/SilverSearcher.json.in2
-rw-r--r--src/plugins/squish/Squish.json.in2
-rw-r--r--src/plugins/squish/squishoutputpane.cpp4
-rw-r--r--src/plugins/squish/squishperspective.cpp4
-rw-r--r--src/plugins/squish/squishserverprocess.cpp5
-rw-r--r--src/plugins/squish/squishsettings.cpp8
-rw-r--r--src/plugins/squish/squishtools.cpp31
-rw-r--r--src/plugins/squish/squishwizardpages.cpp2
-rw-r--r--src/plugins/squish/testresult.cpp18
-rw-r--r--src/plugins/studiowelcome/StudioWelcome.json.in2
-rw-r--r--src/plugins/studiowelcome/studiowelcomeplugin.cpp38
-rw-r--r--src/plugins/subversion/Subversion.json.in2
-rw-r--r--src/plugins/subversion/subversionclient.cpp1
-rw-r--r--src/plugins/subversion/subversionsettings.cpp2
-rw-r--r--src/plugins/terminal/Terminal.json.in2
-rw-r--r--src/plugins/terminal/shellintegration.cpp6
-rw-r--r--src/plugins/terminal/shellmodel.cpp2
-rw-r--r--src/plugins/terminal/terminalpane.cpp2
-rw-r--r--src/plugins/terminal/terminalsettings.cpp42
-rw-r--r--src/plugins/terminal/terminalwidget.cpp2
-rw-r--r--src/plugins/texteditor/TextEditor.json.in2
-rw-r--r--src/plugins/texteditor/basefilefind.cpp3
-rw-r--r--src/plugins/texteditor/bookmarkmanager.cpp1
-rw-r--r--src/plugins/texteditor/completionsettingspage.cpp2
-rw-r--r--src/plugins/texteditor/displaysettingspage.cpp2
-rw-r--r--src/plugins/texteditor/fontsettings.cpp3
-rw-r--r--src/plugins/texteditor/fontsettingspage.cpp7
-rw-r--r--src/plugins/texteditor/refactoringchanges.cpp11
-rw-r--r--src/plugins/texteditor/refactoringchanges.h1
-rw-r--r--src/plugins/texteditor/textdocument.cpp5
-rw-r--r--src/plugins/texteditor/texteditor.cpp60
-rw-r--r--src/plugins/texteditor/texteditor.h12
-rw-r--r--src/plugins/texteditor/textmark.cpp2
-rw-r--r--src/plugins/todo/Todo.json.in2
-rw-r--r--src/plugins/todo/keyword.cpp2
-rw-r--r--src/plugins/todo/settings.cpp15
-rw-r--r--src/plugins/updateinfo/UpdateInfo.json.in2
-rw-r--r--src/plugins/valgrind/Valgrind.json.in2
-rw-r--r--src/plugins/valgrind/valgrindmemcheckparsertest.cpp8
-rw-r--r--src/plugins/valgrind/valgrindsettings.cpp2
-rw-r--r--src/plugins/valgrind/valgrindsettings.h2
-rw-r--r--src/plugins/valgrind/valgrindtestrunnertest.cpp5
-rw-r--r--src/plugins/valgrind/xmlprotocol/parser.cpp4
-rw-r--r--src/plugins/vcpkg/Vcpkg.json.in2
-rw-r--r--src/plugins/vcsbase/VcsBase.json.in2
-rw-r--r--src/plugins/vcsbase/commonvcssettings.cpp6
-rw-r--r--src/plugins/vcsbase/submiteditorwidget.cpp3
-rw-r--r--src/plugins/vcsbase/submitfilemodel.cpp2
-rw-r--r--src/plugins/vcsbase/vcsbaseclient.cpp2
-rw-r--r--src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp4
-rw-r--r--src/plugins/vcsbase/vcsbaseeditor.cpp4
-rw-r--r--src/plugins/vcsbase/wizard/vcscommandpage.cpp18
-rw-r--r--src/plugins/vcsbase/wizard/vcsconfigurationpage.cpp2
-rw-r--r--src/plugins/webassembly/WebAssembly.json.in2
-rw-r--r--src/plugins/webassembly/webassemblyrunconfiguration.cpp2
-rw-r--r--src/plugins/webassembly/webassemblysettings.cpp1
-rw-r--r--src/plugins/welcome/Welcome.json.in2
-rw-r--r--src/plugins/welcome/welcomeplugin.cpp10
m---------src/shared/qbs0
-rw-r--r--src/shared/qtsingleapplication/qtlocalpeer.cpp1
-rw-r--r--src/tools/qml2puppet/CMakeLists.txt36
-rw-r--r--src/tools/qml2puppet/editor3d_qt6.qrc4
-rw-r--r--src/tools/qml2puppet/mockfiles/images/reflectionprobe.pngbin0 -> 463 bytes
-rw-r--r--src/tools/qml2puppet/mockfiles/images/reflectionprobe@2x.pngbin0 -> 887 bytes
-rw-r--r--src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml33
-rw-r--r--src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml109
-rw-r--r--src/tools/qml2puppet/mockfiles/qt6/IconGizmo.qml4
-rw-r--r--src/tools/qml2puppet/mockfiles/qt6/LookAtGizmo.qml27
-rw-r--r--src/tools/qml2puppet/mockfiles/qt6/OverlayView3D.qml118
-rw-r--r--src/tools/qml2puppet/mockfiles/qt6/ReflectionProbeGizmo.qml9
-rw-r--r--src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml10
-rw-r--r--src/tools/qml2puppet/qml2puppet/appmetadata.cpp51
-rw-r--r--src/tools/qml2puppet/qml2puppet/appmetadata.h55
-rw-r--r--src/tools/qml2puppet/qml2puppet/configcrashpad.h1
-rw-r--r--src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp156
-rw-r--r--src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h18
-rw-r--r--src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.cpp62
-rw-r--r--src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.h42
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp5
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h1
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp3
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp10
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.cpp209
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.h50
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp51
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h2
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp4
-rw-r--r--src/tools/qml2puppet/qml2puppet/qml2puppetmain.cpp31
-rw-r--r--src/tools/qml2puppet/qml2puppet/qmlbase.h25
-rw-r--r--src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp1
-rw-r--r--src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp48
-rw-r--r--src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp4
-rw-r--r--src/tools/sdktool/addkitoperation.cpp2
-rw-r--r--tests/auto/android/tst_avdmanageroutputparser.cpp7
-rw-r--r--tests/auto/debugger/tst_dumpers.cpp45
-rw-r--r--tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp53
-rw-r--r--tests/auto/utils/commandline/tst_commandline.cpp25
-rw-r--r--tests/auto/utils/deviceshell/tst_deviceshell.cpp8
-rw-r--r--tests/auto/utils/persistentsettings/tst_persistentsettings.cpp6
-rw-r--r--tests/auto/utils/process/processtestapp/processtestapp.cpp2
-rw-r--r--tests/auto/utils/process/tst_process.cpp11
-rw-r--r--tests/manual/deviceshell/tst_deviceshell.cpp24
-rw-r--r--tests/manual/layoutbuilder/v2/CMakeLists.txt17
-rw-r--r--tests/manual/layoutbuilder/v2/lb.cpp972
-rw-r--r--tests/manual/layoutbuilder/v2/lb.h553
-rw-r--r--tests/manual/layoutbuilder/v2/main.cpp79
-rw-r--r--tests/manual/widgets/layoutbuilder/tst_manual_widgets_layoutbuilder.cpp8
-rw-r--r--tests/manual/widgets/uifonts/tst_manual_widgets_uifonts.cpp26
-rw-r--r--tests/system/shared/project_explorer.py3
-rw-r--r--tests/system/shared/utils.py13
-rw-r--r--tests/system/suite_editors/tst_clean_whitespaces/test.py2
-rw-r--r--tests/system/suite_editors/tst_delete_externally/test.py3
-rw-r--r--tests/system/suite_editors/tst_edit_externally/test.py2
-rw-r--r--tests/system/suite_editors/tst_generic_highlighter/test.py4
-rw-r--r--tests/system/suite_general/tst_installed_languages/test.py1
-rw-r--r--tests/system/suite_general/tst_remove_kits/test.py4
-rw-r--r--tests/system/suite_tools/tst_designer_autocomplete/test.py13
-rw-r--r--tests/unit/tests/matchers/projectstorage-matcher.h4
-rw-r--r--tests/unit/tests/mocks/CMakeLists.txt1
-rw-r--r--tests/unit/tests/mocks/externaldependenciesmock.h1
-rw-r--r--tests/unit/tests/mocks/filesystemmock.h1
-rw-r--r--tests/unit/tests/mocks/projectstorageerrornotifiermock.h17
-rw-r--r--tests/unit/tests/mocks/projectstoragemock.cpp41
-rw-r--r--tests/unit/tests/mocks/projectstoragemock.h30
-rw-r--r--tests/unit/tests/mocks/qmltypesparsermock.h2
-rw-r--r--tests/unit/tests/printers/gtest-creator-printing.cpp23
-rw-r--r--tests/unit/tests/printers/gtest-creator-printing.h4
-rw-r--r--tests/unit/tests/testdesignercore/CMakeLists.txt4
-rw-r--r--tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp14
-rw-r--r--tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp4
-rw-r--r--tests/unit/tests/unittests/listmodeleditor/listmodeleditor-test.cpp10
-rw-r--r--tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp264
-rw-r--r--tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp126
-rw-r--r--tests/unit/tests/unittests/model/CMakeLists.txt2
-rw-r--r--tests/unit/tests/unittests/model/model-test.cpp61
-rw-r--r--tests/unit/tests/unittests/model/modelutils-test.cpp3
-rw-r--r--tests/unit/tests/unittests/model/nodelistproperty-test.cpp5
-rw-r--r--tests/unit/tests/unittests/model/uniquename-test.cpp112
-rw-r--r--tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp854
-rw-r--r--tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp35
-rw-r--r--tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp1245
-rw-r--r--tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp40
-rw-r--r--tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp83
-rw-r--r--tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp103
-rw-r--r--tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson14
-rw-r--r--tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson7
-rw-r--r--tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson14
-rw-r--r--tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmltojson7
-rw-r--r--tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp15
1349 files changed, 52239 insertions, 45754 deletions
diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml
index e0986c881a..d0c9124fb0 100644
--- a/.github/workflows/build_cmake.yml
+++ b/.github/workflows/build_cmake.yml
@@ -7,7 +7,7 @@ on:
- 'doc/**'
env:
- QT_VERSION: 6.6.2
+ QT_VERSION: 6.7.1
MACOS_DEPLOYMENT_TARGET: 11.0
CLANG_VERSION: 18.1.5
ELFUTILS_VERSION: 0.175
@@ -200,7 +200,7 @@ jobs:
endif()
elseif ("${{ runner.os }}" STREQUAL "Linux")
set(url_os "linux_x64")
- set(qt_package_arch_suffix "gcc_64")
+ set(qt_package_arch_suffix "linux_gcc_64")
set(qt_dir_prefix "${qt_version}/gcc_64")
set(qt_package_suffix "-Linux-RHEL_8_8-GCC-Linux-RHEL_8_8-X86_64")
elseif ("${{ runner.os }}" STREQUAL "macOS")
@@ -267,10 +267,10 @@ jobs:
)
endforeach()
- # uic depends on libicu56.so
+ # uic, qmake depend on libicu*.so libraries
if ("${{ runner.os }}" STREQUAL "Linux")
downloadAndExtract(
- "${qt_base_url}/qt.qt6.${qt_version_dotless}.${qt_package_arch_suffix}/${qt_package_version}icu-linux-Rhel7.2-x64.7z"
+ "${qt_base_url}/qt.qt6.${qt_version_dotless}.${qt_package_arch_suffix}/${qt_package_version}icu-linux-Rhel8.6-x86_64.7z"
icu.7z
)
endif()
diff --git a/cmake/QtCreatorAPIInternal.cmake b/cmake/QtCreatorAPIInternal.cmake
index 0cd3602d54..49761d03ab 100644
--- a/cmake/QtCreatorAPIInternal.cmake
+++ b/cmake/QtCreatorAPIInternal.cmake
@@ -23,6 +23,7 @@ list(APPEND DEFAULT_DEFINES
QT_NO_JAVA_STYLE_ITERATORS
QT_NO_CAST_TO_ASCII QT_RESTRICTED_CAST_FROM_ASCII QT_NO_FOREACH
QT_DISABLE_DEPRECATED_BEFORE=0x050900
+ QT_WARN_DEPRECATED_BEFORE=0x060400
QT_USE_QSTRINGBUILDER
)
diff --git a/coin/instructions/build.yaml b/coin/instructions/build.yaml
index 5434885e1c..e4453c9e71 100644
--- a/coin/instructions/build.yaml
+++ b/coin/instructions/build.yaml
@@ -2,6 +2,15 @@ type: Group
instructions:
- type: MakeDirectory
directory: "{{.AgentWorkingDir}}/build/qt_temp"
+ - type: Group
+ instructions:
+ - type: EnvironmentVariable
+ variableName: QTC_TEST_OPTION
+ variableValue: "--with-tests"
+ enable_if:
+ condition: property
+ property: features
+ not_contains_value: DisableTests
- type: Group
instructions:
@@ -26,7 +35,7 @@ instructions:
maxTimeBetweenOutput: 360
userMessageOnFailure: "Failed to extract LLVM package, check logs."
- type: ExecuteCommand
- command: "python3 -u {{.AgentWorkingDir}}/qt-creator/qt-creator/scripts/build.py --build-type {{.Env.QTC_BUILD_TYPE}} --src {{.AgentWorkingDir}}/qt-creator/qt-creator --build {{.AgentWorkingDir}}/qt-creator/qt-creator_build --qt-path {{.AgentWorkingDir}}/build/qt_install_dir --elfutils-path {{.AgentWorkingDir}}/build/qt_temp/elfutils --llvm-path {{.AgentWorkingDir}}/build/qt_temp/libclang --with-tests --no-zip --add-config=-DCMAKE_C_COMPILER_LAUNCHER=sccache --add-config=-DCMAKE_CXX_COMPILER_LAUNCHER=sccache"
+ command: "python3 -u {{.AgentWorkingDir}}/qt-creator/qt-creator/scripts/build.py --build-type {{.Env.QTC_BUILD_TYPE}} --src {{.AgentWorkingDir}}/qt-creator/qt-creator --build {{.AgentWorkingDir}}/qt-creator/qt-creator_build --qt-path {{.AgentWorkingDir}}/build/qt_install_dir --elfutils-path {{.AgentWorkingDir}}/build/qt_temp/elfutils --llvm-path {{.AgentWorkingDir}}/build/qt_temp/libclang {{.Env.QTC_TEST_OPTION}} --no-zip {{.Env.QTC_SCCACHE_C_OPTION}} {{.Env.QTC_SCCACHE_CXX_OPTION}} {{.Env.QTC_SCCACHE_ENABLE_OPTION}}"
maxTimeInSeconds: 36000
maxTimeBetweenOutput: 3600
userMessageOnFailure: "Failed to run build.py, check logs."
@@ -60,7 +69,7 @@ instructions:
maxTimeBetweenOutput: 360
userMessageOnFailure: "Failed to extract LLVM package, check logs."
- type: ExecuteCommand
- command: "python3 -u {{.AgentWorkingDir}}/qt-creator/qt-creator/scripts/build.py --build-type {{.Env.QTC_BUILD_TYPE}} --src {{.AgentWorkingDir}}/qt-creator/qt-creator --build {{.AgentWorkingDir}}/qt-creator/qt-creator_build --qt-path {{.AgentWorkingDir}}/build/qt_install_dir --llvm-path {{.AgentWorkingDir}}/build/qt_temp/libclang --keychain-unlock-script /Users/qt/unlock-keychain.sh --with-tests --no-zip --add-config=-DCMAKE_C_COMPILER_LAUNCHER=sccache --add-config=-DCMAKE_CXX_COMPILER_LAUNCHER=sccache"
+ command: "python3 -u {{.AgentWorkingDir}}/qt-creator/qt-creator/scripts/build.py --build-type {{.Env.QTC_BUILD_TYPE}} --src {{.AgentWorkingDir}}/qt-creator/qt-creator --build {{.AgentWorkingDir}}/qt-creator/qt-creator_build --qt-path {{.AgentWorkingDir}}/build/qt_install_dir --llvm-path {{.AgentWorkingDir}}/build/qt_temp/libclang --keychain-unlock-script /Users/qt/unlock-keychain.sh {{.Env.QTC_TEST_OPTION}} --no-zip {{.Env.QTC_SCCACHE_C_OPTION}} {{.Env.QTC_SCCACHE_CXX_OPTION}} {{.Env.QTC_SCCACHE_ENABLE_OPTION}}"
maxTimeInSeconds: 36000
maxTimeBetweenOutput: 3600
userMessageOnFailure: "Failed to run build.py, check logs."
@@ -112,7 +121,7 @@ instructions:
maxTimeBetweenOutput: 360
userMessageOnFailure: "Failed to extract LLVM package, check logs."
- type: ExecuteCommand
- command: "python -u {{.AgentWorkingDir}}\\qt-creator\\qt-creator\\scripts\\build.py --build-type {{.Env.QTC_BUILD_TYPE}} --src {{.AgentWorkingDir}}\\qt-creator\\qt-creator --build {{.AgentWorkingDir}}\\qt-creator\\qt-creator_build --qt-path {{.AgentWorkingDir}}/build/qt_install_dir --python-path {{.AgentWorkingDir}}\\build\\qt_temp\\python --elfutils-path {{.AgentWorkingDir}}\\buid\\qt_temp\\elfutils --llvm-path {{.AgentWorkingDir}}\\build\\qt_temp\\libclang --with-tests --no-zip --add-config=-DCMAKE_C_COMPILER_LAUNCHER=sccache --add-config=-DCMAKE_CXX_COMPILER_LAUNCHER=sccache --add-config=-DWITH_SCCACHE_SUPPORT=ON"
+ command: "python -u {{.AgentWorkingDir}}\\qt-creator\\qt-creator\\scripts\\build.py --build-type {{.Env.QTC_BUILD_TYPE}} --src {{.AgentWorkingDir}}\\qt-creator\\qt-creator --build {{.AgentWorkingDir}}\\qt-creator\\qt-creator_build --qt-path {{.AgentWorkingDir}}/build/qt_install_dir --python-path {{.AgentWorkingDir}}\\build\\qt_temp\\python --elfutils-path {{.AgentWorkingDir}}\\buid\\qt_temp\\elfutils --llvm-path {{.AgentWorkingDir}}\\build\\qt_temp\\libclang {{.Env.QTC_TEST_OPTION}} --no-zip {{.Env.QTC_SCCACHE_C_OPTION}} {{.Env.QTC_SCCACHE_CXX_OPTION}} {{.Env.QTC_SCCACHE_ENABLE_OPTION}}"
maxTimeInSeconds: 36000
maxTimeBetweenOutput: 3600
userMessageOnFailure: "Failed to run build.py, check logs."
@@ -133,38 +142,6 @@ instructions:
property: target.arch
equals_value: X86_64
- - type: Group
- instructions:
- - type: MakeDirectory
- directory: "{{.AgentWorkingDir}}/build/qt_temp"
- maxTimeInSeconds: 3600
- maxTimeBetweenOutput: 360
- userMessageOnFailure: "Failed to create folder qt_temp, check logs."
- - type: ExecuteCommand
- command: "curl --fail -L --retry 5 --retry-delay 5 -o {{.AgentWorkingDir}}\\build\\qt_temp\\Python38-win-x86.7z https://master.qt.io/development_releases/prebuilt/python/Python38-win-x86.7z"
- maxTimeInSeconds: 3600
- maxTimeBetweenOutput: 360
- userMessageOnFailure: "Failed to download python package, check logs."
- - type: ExecuteCommand
- command: "C:\\Utils\\sevenzip\\7z.exe x -y {{.AgentWorkingDir}}\\build\\qt_temp\\Python38-win-x86.7z -o{{.AgentWorkingDir}}\\build\\qt_temp\\python"
- maxTimeInSeconds: 3600
- maxTimeBetweenOutput: 360
- userMessageOnFailure: "Failed to extract python package, check logs."
- - type: ExecuteCommand
- command: "python -u {{.AgentWorkingDir}}\\qt-creator\\qt-creator\\scripts\\build.py --build-type {{.Env.QTC_BUILD_TYPE}} --src {{.AgentWorkingDir}}\\qt-creator\\qt-creator --build {{.AgentWorkingDir}}\\qt-creator\\qt-creator_build --python-path {{.AgentWorkingDir}}\\buid\\qt_temp\\python --no-qtcreator --no-zip"
- maxTimeInSeconds: 36000
- maxTimeBetweenOutput: 3600
- userMessageOnFailure: "Failed to run build.py, check logs."
- enable_if:
- condition: and
- conditions:
- - condition: property
- property: target.compiler
- in_values: [MSVC2013, MSVC2015, MSVC2017, MSVC2019]
- - condition: property
- property: target.arch
- equals_value: X86
-
- type: UploadArtifact
archiveDirectory: "{{.AgentWorkingDir}}/qt-creator/qt-creator_build/build"
transferType: UploadModuleBuildArtifact
diff --git a/coin/instructions/common_environment.yaml b/coin/instructions/common_environment.yaml
index 5b4f8ad02b..1410fa79e7 100644
--- a/coin/instructions/common_environment.yaml
+++ b/coin/instructions/common_environment.yaml
@@ -10,7 +10,7 @@ instructions:
variableValue: https://ci-files02-hki.ci.qt.io/packages/jenkins/qtcreator_libclang/libclang-release_18.1.5-based
- type: EnvironmentVariable
variableName: QTC_QT_BASE_URL
- variableValue: "https://ci-files02-hki.ci.qt.io/packages/jenkins/archive/qt/6.6/6.6.2-released/Qt"
+ variableValue: "https://ci-files02-hki.ci.qt.io/packages/jenkins/archive/qt/6.7/6.7.1-released/Qt"
- type: EnvironmentVariable
variableName: QTC_QT_MODULES
variableValue: "qt5compat qtbase qtdeclarative qtimageformats qtquick3d qtquicktimeline qtserialport qtshadertools qtsvg qttools qttranslations qtwebengine"
@@ -59,6 +59,21 @@ instructions:
condition: property
property: target.os
equals_value: MacOS
+ - type: Group
+ instructions:
+ - type: EnvironmentVariable
+ variableName: QTC_SCCACHE_C_OPTION
+ variableValue: --add-config=-DCMAKE_C_COMPILER_LAUNCHER=sccache
+ - type: EnvironmentVariable
+ variableName: QTC_SCCACHE_CXX_OPTION
+ variableValue: --add-config=-DCMAKE_CXX_COMPILER_LAUNCHER=sccache
+ - type: EnvironmentVariable
+ variableName: QTC_SCCACHE_ENABLE_OPTION
+ variableValue: --add-config=-DWITH_SCCACHE_SUPPORT=ON
+ enable_if:
+ condition: property
+ property: features
+ contains_value: Sccache
- type: Group
instructions:
diff --git a/coin/module_config.yaml b/coin/module_config.yaml
index 96428853f2..ecef22e379 100644
--- a/coin/module_config.yaml
+++ b/coin/module_config.yaml
@@ -16,6 +16,9 @@ accept_configuration:
- condition: property
property: target.compiler
equals_value: MSVC2019
+ - condition: property
+ property: target.arch
+ not_equals_value: X86
- condition: and
conditions:
- condition: property
diff --git a/coin/product_dependencies.yaml b/coin/product_dependencies.yaml
index a658f8103b..9a7e164d2b 100644
--- a/coin/product_dependencies.yaml
+++ b/coin/product_dependencies.yaml
@@ -1,4 +1,4 @@
dependencies:
../../qt/qt5.git:
- ref: "6.6"
+ ref: "6.7"
diff --git a/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake b/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake
index 204cedffdb..69479620f8 100644
--- a/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake
+++ b/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake
@@ -1,6 +1,6 @@
-set(IDE_VERSION "4.5.0") # The IDE version.
-set(IDE_VERSION_COMPAT "4.5.0") # The IDE Compatibility version.
-set(IDE_VERSION_DISPLAY "4.5.0") # The IDE display version.
+set(IDE_VERSION "4.6.0") # The IDE version.
+set(IDE_VERSION_COMPAT "4.6.0") # The IDE Compatibility version.
+set(IDE_VERSION_DISPLAY "4.6.0") # The IDE display version.
set(IDE_COPYRIGHT_YEAR "2024") # The IDE current copyright year.
set(IDE_SETTINGSVARIANT "QtProject") # The IDE settings variation.
diff --git a/doc/config/macros.qdocconf b/doc/config/macros.qdocconf
index b93baa5867..751bc80ab7 100644
--- a/doc/config/macros.qdocconf
+++ b/doc/config/macros.qdocconf
@@ -16,8 +16,10 @@ macro.macos = "macOS"
macro.note = "\\b{Note:}"
macro.oslash.HTML = "ø"
macro.ouml.HTML = "ö"
+macro.B2Q = "Boot to Qt"
macro.Q3DS = "Qt 3D Studio"
macro.QA = "Qt Assistant"
+macro.QAC = "Qt Academy"
macro.QB = "Qt Bridge"
macro.QBPS = "Qt Bridge for Adobe Photoshop"
macro.QBXD = "Qt Bridge for Adobe XD"
@@ -25,13 +27,14 @@ macro.QBSK = "Qt Bridge for Sketch"
macro.QBF = "Qt Bridge for Figma"
macro.QC = "$IDE_DISPLAY_NAME"
macro.QCE = "$IDE_DISPLAY_NAME Enterprise"
-macro.QD = "Qt Designer"
+macro.QD = "Qt Widgets Designer"
macro.QDS = "Qt Design Studio"
macro.QQEM = "Qt Quick Effect Maker"
macro.QDV = "Qt Design Viewer"
macro.QL = "Qt Linguist"
macro.QMCU = "Qt for MCUs"
macro.QMLD = "Qt Quick Designer"
+macro.QMLLS = "QML Language Server"
macro.QQV = "Qt QML Viewer"
macro.QSDK = "Qt"
macro.QUL = "Qt Quick Ultralite"
diff --git a/doc/qtcreator/config/style/qt5-sidebar.html b/doc/qtcreator/config/style/qt5-sidebar.html
index 577bd5de42..e7671b2b25 100644
--- a/doc/qtcreator/config/style/qt5-sidebar.html
+++ b/doc/qtcreator/config/style/qt5-sidebar.html
@@ -13,7 +13,6 @@
<li><a href="creator-getting-started.html">Getting Started</a></li>
<li><a href="creator-project-creating.html">Creating Projects</a></li>
<li><a href="creator-configuring-projects.html">Configuring Projects</a></li>
- <li><a href="creator-connecting-mobile.html">Connecting Devices</a></li>
<li><a href="creator-debugging.html">Debugging</a></li>
<li><a href="creator-analyze-mode.html">Analyzing Code</a></li>
</ul>
diff --git a/doc/qtcreator/images/numbers/01.png b/doc/qtcreator/images/numbers/01.png
index 4c8b10b26c..1190a99cc6 100644
--- a/doc/qtcreator/images/numbers/01.png
+++ b/doc/qtcreator/images/numbers/01.png
Binary files differ
diff --git a/doc/qtcreator/images/numbers/02.png b/doc/qtcreator/images/numbers/02.png
index df24ef459e..9ae3bae02c 100644
--- a/doc/qtcreator/images/numbers/02.png
+++ b/doc/qtcreator/images/numbers/02.png
Binary files differ
diff --git a/doc/qtcreator/images/numbers/03.png b/doc/qtcreator/images/numbers/03.png
index 525bd92699..9d29cb93ff 100644
--- a/doc/qtcreator/images/numbers/03.png
+++ b/doc/qtcreator/images/numbers/03.png
Binary files differ
diff --git a/doc/qtcreator/images/numbers/04.png b/doc/qtcreator/images/numbers/04.png
index 6c6b489d5c..206e929462 100644
--- a/doc/qtcreator/images/numbers/04.png
+++ b/doc/qtcreator/images/numbers/04.png
Binary files differ
diff --git a/doc/qtcreator/images/numbers/05.png b/doc/qtcreator/images/numbers/05.png
index 6d831b204d..6baa13123d 100644
--- a/doc/qtcreator/images/numbers/05.png
+++ b/doc/qtcreator/images/numbers/05.png
Binary files differ
diff --git a/doc/qtcreator/images/numbers/06.png b/doc/qtcreator/images/numbers/06.png
index b7adb30f96..4f398dc263 100644
--- a/doc/qtcreator/images/numbers/06.png
+++ b/doc/qtcreator/images/numbers/06.png
Binary files differ
diff --git a/doc/qtcreator/images/numbers/07.png b/doc/qtcreator/images/numbers/07.png
index a160038f0e..c71842702f 100644
--- a/doc/qtcreator/images/numbers/07.png
+++ b/doc/qtcreator/images/numbers/07.png
Binary files differ
diff --git a/doc/qtcreator/images/numbers/08.png b/doc/qtcreator/images/numbers/08.png
index 6ccf88b7eb..f0d894c106 100644
--- a/doc/qtcreator/images/numbers/08.png
+++ b/doc/qtcreator/images/numbers/08.png
Binary files differ
diff --git a/doc/qtcreator/images/numbers/09.png b/doc/qtcreator/images/numbers/09.png
index 0ed195b154..0bf3bc7fb1 100644
--- a/doc/qtcreator/images/numbers/09.png
+++ b/doc/qtcreator/images/numbers/09.png
Binary files differ
diff --git a/doc/qtcreator/images/numbers/10.png b/doc/qtcreator/images/numbers/10.png
index 8c468d9998..bec47399a8 100644
--- a/doc/qtcreator/images/numbers/10.png
+++ b/doc/qtcreator/images/numbers/10.png
Binary files differ
diff --git a/doc/qtcreator/images/qtcreator-configure-project.webp b/doc/qtcreator/images/qtcreator-configure-project.webp
new file mode 100644
index 0000000000..6f77d3a2a9
--- /dev/null
+++ b/doc/qtcreator/images/qtcreator-configure-project.webp
Binary files differ
diff --git a/doc/qtcreator/images/qtcreator-devices-qnx.webp b/doc/qtcreator/images/qtcreator-devices-qnx.webp
new file mode 100644
index 0000000000..a431e883b2
--- /dev/null
+++ b/doc/qtcreator/images/qtcreator-devices-qnx.webp
Binary files differ
diff --git a/doc/qtcreator/images/qtcreator-locator-customize.webp b/doc/qtcreator/images/qtcreator-locator-customize.webp
index 5a6d4e90d3..34b96a8d6f 100644
--- a/doc/qtcreator/images/qtcreator-locator-customize.webp
+++ b/doc/qtcreator/images/qtcreator-locator-customize.webp
Binary files differ
diff --git a/doc/qtcreator/images/qtcreator-open-project-kits.png b/doc/qtcreator/images/qtcreator-open-project-kits.png
deleted file mode 100644
index 8434389fa1..0000000000
--- a/doc/qtcreator/images/qtcreator-open-project-kits.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtcreator/images/qtcreator-preferences-qnx.webp b/doc/qtcreator/images/qtcreator-preferences-qnx.webp
new file mode 100644
index 0000000000..435ce04034
--- /dev/null
+++ b/doc/qtcreator/images/qtcreator-preferences-qnx.webp
Binary files differ
diff --git a/doc/qtcreator/src/analyze/cpu-usage-analyzer.qdoc b/doc/qtcreator/src/analyze/cpu-usage-analyzer.qdoc
index 1c6ba73efc..920bd6d390 100644
--- a/doc/qtcreator/src/analyze/cpu-usage-analyzer.qdoc
+++ b/doc/qtcreator/src/analyze/cpu-usage-analyzer.qdoc
@@ -464,7 +464,8 @@
\list
\li Perf events may be globally disabled on your system.
- The preconfigured \l Boot2Qt images come with Perf events enabled.
+ The preconfigured \l{\B2Q: Documentation}{\B2Q} images come with Perf
+ events enabled.
For a custom configuration you need to make sure that the file
\c {/proc/sys/kernel/perf_event_paranoid} contains a value smaller
than \c {2}. For maximum flexibility in recording traces you can
diff --git a/doc/qtcreator/src/android/androiddev.qdoc b/doc/qtcreator/src/android/androiddev.qdoc
index 3b7a17a6d9..5d1e49de6c 100644
--- a/doc/qtcreator/src/android/androiddev.qdoc
+++ b/doc/qtcreator/src/android/androiddev.qdoc
@@ -112,10 +112,6 @@
\section1 Manual setup
- \note Use the latest Android SDK Command-Line Tools. \QC does not support
- Android SDK Tools version 25.2.5 or earlier because it cannot fully
- integrate them.
-
If the automatic setup does not meet your needs, download
and install Android SDK Command-line Tools, and then install or update the
necessary NDKs, tools, and packages. For more information, see
@@ -282,13 +278,10 @@
Set the preferences in \uicontrol {Emulator command-line startup options}.
For available options, see \l{Start the emulator from the command line}.
- \note The Android Emulator has a bug that prevents it from starting on some
- computers.
-
- To start the emulator manually, enter:
+ To start the emulator manually from the terminal, enter:
\badcode
- cd <ANDROID_SDK>/emulator
+ cd <ANDROID_SDK_ROOT>/emulator
./emulator -avd <AVD_NAME>
\endcode
diff --git a/doc/qtcreator/src/appman/creator-appman-how-to-run.qdoc b/doc/qtcreator/src/appman/creator-appman-how-to-run.qdoc
index 4ca30c6f46..af2ffacaae 100644
--- a/doc/qtcreator/src/appman/creator-appman-how-to-run.qdoc
+++ b/doc/qtcreator/src/appman/creator-appman-how-to-run.qdoc
@@ -11,7 +11,7 @@
If you have set up \l{Qt Application Manager}, you can deploy, run, and
debug applications on the desktop, remote generic SSH Linux targets, or
- \l{Boot2Qt}{Boot2Qt devices}. The applications can be either
+ \B2Q devices. The applications can be either
\e {built-in applications} or \e {third-party applications}. The former
are part of the System UI or the base installation, while the latter
are dynamically installed, updated, and uninstalled.
@@ -118,7 +118,7 @@
\li The path to the controller that installs the application package into
the target system.
- When you run applications on a Boot2Qt device, you can see the device
+ When you run applications on a \B2Q device, you can see the device
ID here.
\row
\li \uicontrol {Application ID}
@@ -148,7 +148,10 @@
slowness and unresponsive, stuttering user interfaces. You cannot profile an
in-process runtime as an individual process.
- \sa {Activate kits for a project}, {Connecting Boot2Qt Devices},
- {Remote Linux}{How To: Develop for remote Linux}, {Enable and disable plugins},
- {Run on many platforms}, {Debugging}, {Debuggers}, {Debugger}, {Profiling QML Applications}
+ \sa {Activate kits for a project}, {Enable and disable plugins},
+ {\B2Q}{How To: Develop for \B2Q},
+ {Remote Linux}{How To: Develop for remote Linux},
+ {Run on many platforms}, {Debugging}, {Debuggers}, {Debugger},
+ {Developing for \B2Q Devices}, {Developing for Remote Linux Devices},
+ {Profiling QML Applications}
*/
diff --git a/doc/qtcreator/src/docker/creator-docker.qdoc b/doc/qtcreator/src/docker/creator-docker.qdoc
index c63ab4b3b5..9524dcd5ec 100644
--- a/doc/qtcreator/src/docker/creator-docker.qdoc
+++ b/doc/qtcreator/src/docker/creator-docker.qdoc
@@ -3,12 +3,13 @@
/*!
\page creator-adding-docker-devices.html
- \previouspage creator-developing-b2qt.html
- \nextpage creator-developing-ios.html
+ \previouspage creator-how-tos.html
- \title Adding Docker Devices
+ \ingroup creator-how-to-docker
- Create Docker devices from \l{ https://docs.docker.com/get-started/overview/}
+ \title Add Docker devices
+
+ Create Docker devices from \l{https://docs.docker.com/get-started/overview/}
{Docker images} and use them to build, run, and debug applications. A Docker
container operates like a virtual machine but uses less system resources at
the cost of being less flexible.
@@ -16,7 +17,7 @@
While Linux, \macos, and Windows hosts are supported in principle, Linux is
the recommended platform.
- To build, run, and debug applications on Docker devices, you must install and
+ To build, run, and debug applications on Docker devices, install and
configure \c docker-cli on the development host. \QC automatically detects
\l{kits-tab}{build and run kit} items, such \l{Add debuggers}
{debuggers} and \l{Add Qt versions}{Qt versions}, in the Docker container
@@ -28,25 +29,61 @@
\l{https://docs.docker.com/engine/reference/commandline/pull/}{docker pull}
command.
- \section1 Adding Docker Images as Devices
+ \section1 Add Docker images as devices
To add a Docker image as a device:
\list 1
- \li Select \preferences > \uicontrol Devices
- > \uicontrol Docker and enter the path to the Docker CLI in
- the \uicontrol Command field.
- \image qtcreator-preferences-devices-docker.webp "Docker tab in Devices preferences"
- \li Select \uicontrol Devices > \uicontrol Add >
- \uicontrol {Docker Device} > \uicontrol {Start Wizard}
- to search for images in your local Docker installation.
- \li Select the Docker image to use, and then select \uicontrol OK.
+ \li Go to \preferences > \uicontrol Devices > \uicontrol Docker.
+ \li In \uicontrol Command, enter the path to the Docker CLI.
+ \image qtcreator-preferences-devices-docker.webp {Docker tab in Devices preferences}
+ \li Go to \uicontrol Devices.
+ \li Select \uicontrol Add > \uicontrol {Docker Device} >
+ \uicontrol {Start Wizard} to search for images in your
+ local Docker installation.
+ \li Select a Docker image, and then select \uicontrol OK.
\li In \uicontrol Devices, check and change Docker device preferences.
- \image qtcreator-preferences-devices-docker-device.png "Docker device preferences"
+ \image qtcreator-preferences-devices-docker-device.png {Docker device preferences}
\li Select \uicontrol Apply to save your changes.
\endlist
- The following table summarizes the options you can set.
+ \section2 Select Docker images
+
+ The \uicontrol {Docker Image Selection} dialog displays a list of Docker
+ images in your local Docker installation. You can sort the images according
+ to the repository name or tag or the image ID or size.
+
+ \image qtcreator-docker-image-selection.webp {Docker Image Selection dialog}
+
+ Select \uicontrol {Show unnamed images} to show images that are not tagged.
+
+ Double-click an image to select it.
+
+ \section1 Edit Docker device kits
+
+ Go to \preferences > \uicontrol Kits to check
+ that the automatically generated kits point to the appropriate kit items.
+
+ \sa {Docker}{How To: Develop for Docker}, {Manage Kits}{How To: Manage Kits}
+*/
+
+/*!
+ \page creator-preferences-docker.html
+ \previouspage creator-how-tos.html
+
+ \ingroup creator-how-to-docker
+
+ \title Set preferences for Docker devices
+
+ To set preferences for Docker devices:
+
+ \list 1
+ \li Go to \preferences > \uicontrol Devices > \uicontrol Devices.
+ \li In \uicontrol Device, select a Docker device.
+ \image qtcreator-preferences-devices-docker-device.png {Docker device preferences}
+ \endlist
+
+ The following table summarizes the preferences you can set.
\table
\header
@@ -59,7 +96,7 @@
on Windows.
\row
\li \uicontrol {Do not modify entry point}
- \li Stops \QC from modifying the \l {Modifying Entry Points}{entry point}
+ \li Stops \QC from modifying the \l {Modify entry points}{entry point}
of the image. Make sure that the entry point starts a shell.
\row
\li \uicontrol {Enable flags needed for LLDB}
@@ -69,33 +106,18 @@
\endcode
\row
\li \uicontrol {Clangd executable}
- \li The path to a remote Clangd executable to use for a remote code
- model.
+ \li The path to a remote Clangd executable for a remote code model.
\row
\li \uicontrol {Paths to mount}
- \li Host directories to \l{Specifying Paths to Mount}{mount} into the
+ \li Host directories to \l{Specify paths to mount}{mount} into the
container, such as the project directory.
\row
\li \uicontrol {Search locations}
- \li Where to \l{Auto-detecting Kit Items}{automatically detect} kit
+ \li Where to \l{Auto-detect kit items}{automatically detect} kit
items.
\endtable
- The following sections describe the Docker device preferences in more detail.
-
- \section2 Selecting Docker Images
-
- The \uicontrol {Docker Image Selection} dialog displays a list of Docker
- images in your local Docker installation. You can sort the images according
- to the repository name or tag or the image ID or size.
-
- \image qtcreator-docker-image-selection.webp "Docker Image Selection dialog"
-
- Select \uicontrol {Show unnamed images} to show images that are not tagged.
-
- Double-click an image to select it.
-
- \section2 Modifying Entry Points
+ \section1 Modify entry points
The entry point of a Docker container is specified in the container settings
and started as the main process when starting the container. The entry point
@@ -107,9 +129,9 @@
\uicontrol {Do not modify entry point}. However, if the entry
point you specify is not a shell, \QC cannot start the container.
- \section2 Specifying Paths to Mount
+ \section1 Specify paths to mount
- You can either copy your project files into the Docker container or specify
+ Copy your project files into the Docker container or specify
paths to them in \uicontrol {Paths to mount}. Shared mounts are restricted
to locations in the host system that can end up in the same absolute location
in the Docker container. On Windows, network mounts cannot be used as shared
@@ -120,7 +142,7 @@
\uicontrol {Delete Line} to delete the selected path or \uicontrol Clear
to delete all paths.
- \section2 Auto-detecting Kit Items
+ \section1 Auto-detect kit items
Select \uicontrol {Auto-detect Kit Items} to find kit items and create kits
for the Docker device. You can search for kit items in the device's PATH or
@@ -143,20 +165,28 @@
\uicontrol {List Auto-Detected Kit Items}. To remove
them, select \uicontrol {Remove Auto-Detected Kit Items}.
- \section1 Editing Docker Device Kits
+ \sa {Docker}{How To: Develop for Docker}, {Manage Kits}{How To: Manage Kits}
+*/
- Select \preferences > \uicontrol Kits to check
- that the automatically generated kits point to the appropriate kit items.
+/*!
+ \page creator-how-to-build-run-docker.html
+ \previouspage creator-how-tos.html
+
+ \ingroup creator-how-to-docker
- To specify build settings:
+ \title Build for and run on Docker devices
+
+ To specify build settings for Docker images:
\list 1
\li Open a project for an application you want to develop for the
device.
- \li Select \uicontrol Projects > \uicontrol {Build & Run} to enable
- the kit that you specified above.
+ \li Go to \uicontrol Projects > \uicontrol {Build & Run}, and
+ activate the kit for Docker devices.
\endlist
Select \uicontrol Run to specify run settings. Usually, you can use
the default settings.
+
+ \sa {Docker}{How To: Develop for Docker}, {Manage Kits}{How To: Manage Kits}
*/
diff --git a/doc/qtcreator/src/editors/creator-code-syntax.qdoc b/doc/qtcreator/src/editors/creator-code-syntax.qdoc
index e2888c25ca..f59ba0fd94 100644
--- a/doc/qtcreator/src/editors/creator-code-syntax.qdoc
+++ b/doc/qtcreator/src/editors/creator-code-syntax.qdoc
@@ -68,7 +68,7 @@
To specify the position where the annotations are displayed, go to
\preferences > \uicontrol {Text Editor} >
- \uicontrol Display > \uicontrol {Line annotations}, and then select
+ \uicontrol Display > \uicontrol {Line Annotations}, and then select
whether to display the annotations directly next to the code, aligned
to the right of the code, or in the right margin. Showing annotations
between lines can be useful if there is usually not enough space to
diff --git a/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc b/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc
index 04690a1923..27a632955c 100644
--- a/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc
+++ b/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc
@@ -62,7 +62,8 @@
\section1 Adding Language Servers
- \QC adds a Python language server by default.
+ \QC adds a \l{Configure Python language servers}{Python language server} by
+ default.
Also, it offers to install language servers for JSON and YAML files
when you open them in the editor if it can find the
@@ -71,18 +72,22 @@
\image qtcreator-language-server-json.webp {Prompt to install JSON language server}
- Add a Java language server for \l{Developing for Android}
- {Android development}. For other languages, add generic stdIO language
- servers.
+ \l{Add a Java language server} for \l{Developing for Android}
+ {Android development}. For other languages,
+ \l{Add generic language servers}{add generic stdIO language servers}.
To add language servers, go to \preferences > \uicontrol {Language Client}
and select \uicontrol Add.
\image qtcreator-language-client-options-java.png {Java language server preferences}
- To enable a language server, select the check box next to the language
+ To enable a language server, select the checkbox next to the language
server name and set server preferences.
+ To turn on \l{Turn on \QMLLS}{\QMLLS}, go to
+ \preferences > \uicontrol {Qt Quick} > \uicontrol {QML/JS Editing} and
+ select \uicontrol {Enable \QMLLS}.
+
To remove language servers from the list, select \uicontrol Delete.
\section1 Supported Locator Filters
@@ -218,23 +223,23 @@
\ingroup creator-how-to-lsp
- \title Turn on QML Language Server
+ \title Turn on \QMLLS
- Since Qt 6.4, the QML language server offers code completion and
+ Since Qt 6.4, \QMLLS offers code completion and
issues warnings for QML. To use it, go to \preferences >
\uicontrol {Qt Quick} > \uicontrol {QML/JS Editing} and select
- \uicontrol {Enable QML Language Server}.
+ \uicontrol {Enable \QMLLS}.
- By default, enabling the QML language server will only enable warning messages
+ By default, enabling \QMLLS will only enable warning messages
and code completion, while advanced features such as renaming and finding usages
will be handled by the embedded code model.
- To disable the embedded code model and use the QML language server for everything,
- select \uicontrol {Use QML Language Server advanced features}.
+ To disable the embedded code model and use \QMLLS for everything,
+ select \uicontrol {Use \QMLLS advanced features}.
- Also, \QC tries to use the QML language server shipped with
- the Qt version in your current kit. To override that behavior and always use the
- QML language server of the highest registered Qt version, select
- \uicontrol {Use QML Language Server from latest Qt version}.
+ Also, \QC tries to use \QMLLS shipped with
+ the Qt version in your current kit. To override that behavior and always use
+ \QMLLS of the highest registered Qt version, select
+ \uicontrol {Use \QMLLS from latest Qt version}.
\image qtcreator-qml-js-editing.webp {QML/JS Editing preferences}
diff --git a/doc/qtcreator/src/editors/creator-only/creator-locator.qdoc b/doc/qtcreator/src/editors/creator-only/creator-locator.qdoc
index eab661647f..d739bd3445 100644
--- a/doc/qtcreator/src/editors/creator-only/creator-locator.qdoc
+++ b/doc/qtcreator/src/editors/creator-only/creator-locator.qdoc
@@ -261,6 +261,16 @@
\endlist
+ \section1 Hiding Long Paths
+
+ To hide the common part of absolute paths in the locator:
+
+ \list 1
+ \li Go to \preferences > \uicontrol Environment > \uicontrol Locator.
+ \li Select \uicontrol {Show Paths in Relation to Active Project} to
+ show relative paths.
+ \endlist
+
\sa {Navigate with locator}, {Search}{How To: Search}, {Perform calculations},
{Locator}
*/
diff --git a/doc/qtcreator/src/external-resources/external-resources.qdoc b/doc/qtcreator/src/external-resources/external-resources.qdoc
index 009c98dd42..05cba5ba53 100644
--- a/doc/qtcreator/src/external-resources/external-resources.qdoc
+++ b/doc/qtcreator/src/external-resources/external-resources.qdoc
@@ -2,12 +2,24 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
+ \externalpage https://www.perforce.com/manuals/cmdref/Content/CmdRef/P4CONFIG.html
+ \title Perforce: P4CONFIG
+*/
+/*!
+ \externalpage https://doc.qt.io/qt-6/qtdesigner-manual.html
+ \title Qt Widgets Designer Manual
+*/
+/*!
+ \externalpage https://doc.qt.io/qt-6/designer-using-custom-widgets.html
+ \title Using Custom Widgets with Qt Widgets Designer
+*/
+/*!
\externalpage https://doc.qt.io/Boot2Qt/index.html
- \title Boot2Qt: Documentation
+ \title \B2Q: Documentation
*/
/*!
\externalpage https://doc.qt.io/Boot2Qt/b2qt-requirements-x11.html#setting-up-usb-access-to-embedded-devices
- \title Boot2Qt: Setting Up USB Access to Embedded Devices
+ \title \B2Q: Setting Up USB Access to Embedded Devices
*/
/*!
\externalpage https://doc.qt.io/qt/qtqml-index.html
diff --git a/doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc b/doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc
index 5fa0f05c1a..a678e09463 100644
--- a/doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc
+++ b/doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc
@@ -747,7 +747,7 @@
\li Re-run tests, as determined by \uicontrol {Repetition mode}. Set the
maximum number of times for repeating a test in \uicontrol {Count}.
\row
- \li \uicontrol {Run in parallel}
+ \li \uicontrol {Run in Parallel}
\li Run the tests in parallel using the specified number of
\uicontrol {Jobs}. In \uicontrol {Test load}, limit the parallel
execution. CTest will not start a new test if it would cause the
diff --git a/doc/qtcreator/src/howto/creator-only/creator-cli.qdoc b/doc/qtcreator/src/howto/creator-only/creator-cli.qdoc
index fbc885df05..a0e9129c6b 100644
--- a/doc/qtcreator/src/howto/creator-only/creator-cli.qdoc
+++ b/doc/qtcreator/src/howto/creator-only/creator-cli.qdoc
@@ -93,7 +93,7 @@
\row
\li -installsettingspath <path>
\li Override the default path from where user-independent settings are read
- (for example written by the installer).
+ (for example, those written by the installer).
\row
\li -temporarycleansettings, -tcs
@@ -104,6 +104,10 @@
\li -language <locale>
\li Set the UI language.
+ Use a lowercase, two-letter
+ \l {https://www.iso.org/iso-639-language-codes.html}
+ {ISO 639 language code}, such as \e de, \e en, or \e fr.
+
\row
\li -test <plugin>[,testfunction[:testdata]] ...
\li For \QC plugin developers: run the plugin's tests using a
diff --git a/doc/qtcreator/src/howto/creator-only/creator-how-to-find-settings-files.qdoc b/doc/qtcreator/src/howto/creator-only/creator-how-to-find-settings-files.qdoc
index 8bfb773a40..81bc78e307 100644
--- a/doc/qtcreator/src/howto/creator-only/creator-how-to-find-settings-files.qdoc
+++ b/doc/qtcreator/src/howto/creator-only/creator-how-to-find-settings-files.qdoc
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
@@ -7,9 +7,9 @@
\ingroup creator-how-to-use
- \title Find settings files
+ \title Reset \QC settings
- \QC creates the following files and directories:
+ To reset all \QC settings, remove the settings files that it creates:
\list
\li QtCreator.db
@@ -29,4 +29,32 @@
\li On Windows, look in
\c {%appdata%\QtProject} and \c {%localappdata%\QtProject}.
\endlist
+
+ To check whether the settings are causing a problem before resetting them,
+ start \QC from the command line with options.
+
+ \section1 Override the default settings path
+
+ To override the default path where user settings are stored, enter:
+
+ \badcode
+ qtcreator -settingspath <path>
+ \endcode
+
+ To override the default path from where user-independent settings are read
+ (for example, those written by the installer), enter:
+
+ \badcode
+ qtcreator -installsettingspath <path>
+ \endcode
+
+ \section1 Start \QC with temporary clean settings
+
+ To use clean settings that are deleted when you quit \QC, enter:
+
+ \badcode
+ qtcreator -tcs
+ \endcode
+
+ \sa {Run Qt Creator from the command line}
*/
diff --git a/doc/qtcreator/src/howto/creator-only/creator-how-tos.qdoc b/doc/qtcreator/src/howto/creator-only/creator-how-tos.qdoc
index 6bbbac0475..baf079c08f 100644
--- a/doc/qtcreator/src/howto/creator-only/creator-how-tos.qdoc
+++ b/doc/qtcreator/src/howto/creator-only/creator-how-tos.qdoc
@@ -103,6 +103,20 @@
\generatelist creator-how-to-bare-metal
+ \section2 \B2Q
+
+ Run, debug, and analyze applications on \l{\B2Q: Documentation}{\B2Q}
+ devices.
+
+ \generatelist creator-how-to-b2qt
+
+ \section2 Docker
+
+ Create \e {Docker devices} from \e {Docker images} and use them to build,
+ run, and debug applications.
+
+ \generatelist creator-how-to-docker
+
\section2 iOS
Install \l {Qt for iOS} and Xcode, and connect iOS devices to the computer.
@@ -115,6 +129,12 @@
\generatelist creator-how-to-mcu
+ \section2 QNX Neutrino
+
+ Install \l {Qt for QNX}, and connect QNX Neutrino devices to the computer.
+
+ \generatelist creator-how-to-qnx
+
\section2 Remote Linux
Add kits for toolchains for building applications for generic Linux
@@ -122,6 +142,13 @@
\generatelist creator-how-to-remote-linux
+ \section2 WebAssembly
+
+ Install \l{Qt for WebAssembly} to build applications for the web and run them
+ in a web browser.
+
+ \generatelist creator-how-to-webassembly
+
\section1 Edit Code
The code editor offers useful features for editing C++ and QML code, such
diff --git a/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc b/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc
index 7126aee0cc..4302cd8e43 100644
--- a/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc
+++ b/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc
@@ -18,14 +18,6 @@
\section1 General Questions
-
- \b {How do I reset all \QC settings?}
-
- Remove the settings files created by \QC.
-
- For more information about where the files are located on each supported
- platform, see \l {Find settings files}.
-
\b {\QC comes with \MinGW, should I use this version with Qt?}
Use the version that was built against the Qt version.
@@ -41,15 +33,6 @@
This is especially relevant for the \macos where \c {/usr/local/bin} might
not be in the path when \QC is started.
- \b {How do I change the interface language for \QC?}
-
- \QC has been localized into several languages. If the system
- language is one of the supported languages, it is automatically selected.
- To change the language, select \preferences >
- \uicontrol Environment and select a language in the \uicontrol Language
- field. Select \uicontrol {Restart Now} to restart \QC and have the change
- take effect.
-
\b {Has a reported issue been addressed?}
You can look up any issue in the
diff --git a/doc/qtcreator/src/howto/creator-only/qtcreator-how-to-change-ui-language.qdoc b/doc/qtcreator/src/howto/creator-only/qtcreator-how-to-change-ui-language.qdoc
new file mode 100644
index 0000000000..913b1b6fa9
--- /dev/null
+++ b/doc/qtcreator/src/howto/creator-only/qtcreator-how-to-change-ui-language.qdoc
@@ -0,0 +1,38 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page creator-how-to-change-ui-language.html
+ \previouspage creator-how-tos.html
+
+ \ingroup creator-how-to-ui
+
+ \title Change the UI language
+
+ \QC has been localized into several languages. If the system
+ language is one of the supported languages, it is automatically selected.
+
+ To change the language:
+
+ \list 1
+ \li Go to \preferences > \uicontrol Environment.
+ \li In \uicontrol Language, select a language.
+ \li Select \uicontrol OK.
+ \li Select \uicontrol {Restart Now} to restart \QC and have the change
+ take effect.
+ \endlist
+
+ \section1 Set the language from the command line
+
+ To set the UI language when you start \QC from the command line, enter:
+
+ \badcode
+ qtcreator -language <locale>
+ \endcode
+
+ Use a lowercase, two-letter
+ \l {https://www.iso.org/iso-639-language-codes.html}
+ {ISO 639 language code}, such as \e de, \e en, or \e fr.
+
+ \sa {Run Qt Creator from the command line}
+*/
diff --git a/doc/qtcreator/src/linux-mobile/b2qtdev.qdoc b/doc/qtcreator/src/linux-mobile/b2qtdev.qdoc
index 4453fe4e4d..447f3067d4 100644
--- a/doc/qtcreator/src/linux-mobile/b2qtdev.qdoc
+++ b/doc/qtcreator/src/linux-mobile/b2qtdev.qdoc
@@ -1,143 +1,79 @@
-// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page creator-developing-b2qt.html
- \previouspage creator-developing-baremetal.html
- \nextpage creator-adding-docker-devices.html
+ \previouspage creator-reference.html
- \title Connecting Boot2Qt Devices
+ \ingroup creator-reference-devices
- You can connect \l{Boot2Qt} devices to the development PC to run, debug,
- and analyze applications built for them from \QC. For this, you need the
- appropriate \l{http://qt.io/licensing/}{Qt license}.
+ \title Developing for \B2Q Devices
- If you have a tool chain for building applications for Boot2Qt devices
- installed on the development PC, you can add it to \QC. You can then
- select a \l{glossary-buildandrun-kit}{kit} with the \uicontrol Boot2Qt
- device type to build applications for and run them on the devices.
+ \brief Connect \B2Q devices to the computer to run,
+ debug, and analyze applications built for them from \QC.
- To be able to run and debug applications on Boot2Qt devices,
- you must add devices and select them in the \QC
- \l{kits-tab}{kit}.
+ The \l{\B2Q: Documentation}{\B2Q} stack runs on a variety of hardware.
+ \l{http://qt.io/licensing/}{Qt license} holders can customize the contents of
+ the stack and take it into production hardware.
- \section1 Enabling the Boot2Qt Plugin
+ \note Enable the \B2Q plugin to use it.
- To enable the Boot2Qt plugin:
-
- \list 1
- \li Select \uicontrol Help > \uicontrol {About Plugins} >
- \uicontrol {Device Support} > \uicontrol Boot2Qt to
- enable the plugin.
- \li Select \uicontrol {Restart Now} to restart \QC and load the plugin.
- \endlist
-
- \section1 Adding Boot2Qt Devices
-
- If \QC does not automatically detect a device you connected with USB, select
- \preferences > \uicontrol Devices >
- \uicontrol Devices > \uicontrol Add > \uicontrol {Boot2Qt Device} to create
- either a network connection or a USB connection to it.
+ If you have a toolchain for building applications for \B2Q devices
+ installed on the computer, add it to a \l{Kits}{kit} with the
+ \uicontrol \B2Q device type to build applications for and run them on
+ the devices.
\image qtcreator-boot2qt-device-configurations.webp {Devices tab in Preferences}
- To add a device without using a wizard, select \uicontrol {Boot2Qt Device} in
- the pull-down menu of the \uicontrol Add button.
-
- \note On Ubuntu Linux, the development user account must have access to the
- plugged-in devices. To grant them access to the device via USB, create a new
- \c udev rule, as described in
- \l{Boot2Qt: Setting Up USB Access to Embedded Devices}.
-
- You can edit the settings later in \preferences >
- \uicontrol Devices > \uicontrol Devices.
+ \include linuxdev.qdoc openssh
- To reboot the selected device, select \uicontrol {Reboot Device}.
-
- To restore the default application to the device, select
- \uicontrol {Restore Default App}.
-
- \section2 Protecting Connections
-
- You can protect the connections between \QC and a device by using an
- \l{OpenSSH} connection. OpenSSH is a
- connectivity tool for remote login using the SSH protocol. The OpenSSH
- suite is not delivered with \QC, so you must download it and install it
- on the development PC. Then, you must configure the paths to the tools in
- \QC. For more information, see \l {Configure SSH connections}.
-
- You need either a password or an SSH public and private key pair for
- authentication. If you do not have an SSH key, you can use the \c ssh-keygen
- tool to create it in \QC. For more information, see \l {Generate SSH keys}.
-
- \QC does not store passwords. If you use password authentication, you may
- need to enter the password upon every connection to the device, or if
- caching is enabled, at every \QC restart. If you frequently run into the
- timeout, consider using key-based authentication. On \macos and Linux, you
- can also select \preferences > \uicontrol Devices > \uicontrol SSH
- and increase the time (in minutes) to use the same SSH connection in the
- \uicontrol {Connection sharing timeout} field. Windows does not support
- shared connections.
+ \sa {\B2Q}{How To: Develop for \B2Q},
+ {Manage Kits}{How To: Manage Kits}, {Run in Qt Application Manager},
+ {\B2Q Deploy Configuration}, {\B2Q Run Settings},
+ {\B2Q: Documentation}
+*/
- \image qtcreator-ssh-options.png {SSH preferences}
+/*!
+ \page creator-how-to-connect-b2qt.html
+ \previouspage creator-how-tos.html
- \section1 Flashing Boot2Qt Devices
+ \ingroup creator-how-to-b2qt
- To flash the Boot2Qt image to an SD card with Flashing Wizard, select
- \uicontrol Tools > \uicontrol {Flash Boot to Qt Device} and follow the
- instructions of the wizard.
+ \title Connect \B2Q devices
- \image qtcreator-boot2qt-flashing-wizard.png {Boot2Qt Flashing Wizard}
+ Create connections between \l{\B2Q: Documentation}{\B2Q} devices and
+ \QC to run, debug, and analyze applications on them.
- \section1 Configuring Connections
+ \note Enable the \B2Q plugin to use it.
- To configure connections between \QC and a Boot2Qt device and to
- specify build and run settings for the device:
+ To configure connections between \QC and a \B2Q device:
\list 1
\li Check that you can reach the IP address of the device, or use USB to
connect it.
- \li Select \preferences > \uicontrol Kits > \uicontrol {Qt Versions} >
- \uicontrol Add to add the Qt version for Boot2Qt.
- \li Select \preferences > \uicontrol Kits >
- \uicontrol Compilers > \uicontrol Add to add the compiler for
- building the applications.
- \li Select \uicontrol Tools > \uicontrol {Flash Boot to Qt Device}
- to flash the Boot2Qt image to an SD card with Flashing Wizard.
- \li To deploy applications and run them remotely on devices, specify
- parameters for connecting to the devices over the network (\QC
- automatically detects devices connected with USB):
- \list 1
- \li Select \preferences > \uicontrol Devices >
- \uicontrol Devices > \uicontrol Add > \uicontrol Boot2Qt.
- \image qtcreator-devices-boot2qt.png {Boot2Qt Network Device Setup wizard}
- \li In the \uicontrol {Device name} field, enter a name for
- the connection.
- \li In the \uicontrol {Device address} field, enter the host
- name or IP address of the device. This value will be
- available in the \c %{Device:HostAddress} variable.
- \li Click \uicontrol {Finish} to test the connection and
- add the device.
-
- You can edit the connection parameters in the
- \uicontrol Devices tab. The wizard does not show
- parameters that have sensible default values. One of
- these is the SSH port number, which is available in
- the variable \c %{Device:SshPort}.
-
- To add a device without using the wizard, select
- \uicontrol {Boot2Qt Device} in the pull-down menu of the
- \uicontrol Add button.
- \endlist
- \li Select \preferences > \uicontrol Kits >
- \uicontrol Add to add a kit for building applications for the
- device. Select the Qt version, compiler, and device that you
- added above, and choose \uicontrol Boot2Qt as the device type.
+ \li Go to \preferences > \uicontrol Kits > \uicontrol {Qt Versions}.
+ \li Select \uicontrol Add to add the Qt version for \B2Q.
+ \li Go to \preferences > \uicontrol Kits > \uicontrol Compilers.
+ \li Select \uicontrol Add to add the compiler for building the
+ applications.
+ \li Go to \uicontrol Tools > \uicontrol {Flash \B2Q}
+ to flash the \B2Q image to an SD card with \B2Q Flashing Wizard.
+ \image qtcreator-boot2qt-flashing-wizard.png {\B2Q Flashing Wizard}
+ \li Follow the instructions of the wizard to flash the image to the SD
+ card.
+ \li Go to \preferences > \uicontrol Devices > \uicontrol Devices.
+ \li Select \uicontrol Add to add a \B2Q device.
+
+ \QC automatically detects devices connected with USB.
+ \li Go to \preferences > \uicontrol Kits.
+ \li Select \uicontrol Add to add a kit for building for the device.
+ \li Select the Qt version, compiler, and device that you added above.
+ \li In \uicontrol {Run device type}, select \uicontrol {Boot2Qt Device}.
\li To specify build settings:
\list 1
\li Open a project for an application you want to develop for the
device.
- \li Select \uicontrol Projects > \uicontrol {Build & Run} to enable
+ \li Go to \uicontrol Projects > \uicontrol {Build & Run} to activate
the kit that you specified above.
\endlist
\li Select \uicontrol Run to specify run settings. Usually, you can use
@@ -145,9 +81,62 @@
When you run the project, \QC deploys the application as
specified by the deploy steps. By default, \QC copies the
- application files to the device. For more information, see
- \l{Boot2Qt Run Settings}.
+ application files to the device.
+ \endlist
+
+ \sa {Configure SSH connections}, {Generate SSH keys},
+ {Enable and disable plugins}, {\B2Q}{How To: Develop for \B2Q},
+ {Manage Kits}{How To: Manage Kits}, {\B2Q Deploy Configuration},
+ {\B2Q Run Settings}, {Developing for \B2Q Devices}
+*/
+
+/*!
+ \page creator-how-to-add-b2qt.html
+ \previouspage creator-how-tos.html
+
+ \ingroup creator-how-to-b2qt
+
+ \title Add \B2Q devices
+
+ \note Enable the \B2Q plugin to use it.
+
+ If \QC does not automatically detect a \l{\B2Q: Documentation}{\B2Q}
+ device you connect with USB:
+
+ \list 1
+ \li Go to \preferences > \uicontrol Devices > \uicontrol Devices.
+ \image qtcreator-boot2qt-device-configurations.webp {Devices tab in Preferences}
+ \li Select \uicontrol Add > \uicontrol {Boot2Qt Device} to create
+ either a network connection or a USB connection to the device.
+ \image qtcreator-devices-boot2qt.png {Boot to Qt Network Device Setup wizard}
+ \li In \uicontrol {Device name}, enter a name for the connection.
+ \li In \uicontrol {Device address}, enter the host
+ name or IP address of the device. This value becomes the value of the
+ \c %{Device:HostAddress} variable.
+ \li Select \uicontrol {Finish} to test the connection and add the device.
\endlist
- \sa {Boot2Qt: Setting Up USB Access to Embedded Devices}
+ The wizard does not show parameters that have sensible default values, such
+ as the SSH port number. It is available in the variable \c %{Device:SshPort}.
+
+ To add a device without using a wizard, select \uicontrol {Boot2Qt Device} in
+ the pull-down menu of the \uicontrol Add button.
+
+ \note On Ubuntu Linux, the development user account must have access to the
+ plugged-in devices. To grant them access to the device via USB, create a new
+ \c udev rule, as described in
+ \l{\B2Q: Setting Up USB Access to Embedded Devices}.
+
+ \section1 Reboot devices
+
+ To reboot the selected device, select \uicontrol {Reboot Device}.
+
+ \section1 Restore default applications
+
+ To restore the default application to the device, select
+ \uicontrol {Restore Default App}.
+
+ \sa {Enable and disable plugins}, {\B2Q}{How To: Develop for \B2Q},
+ {Developing for \B2Q Devices},
+ {\B2Q: Setting Up USB Access to Embedded Devices}
*/
diff --git a/doc/qtcreator/src/linux-mobile/creator-deployment-b2qt.qdoc b/doc/qtcreator/src/linux-mobile/creator-deployment-b2qt.qdoc
index 27ae99c3df..298865d794 100644
--- a/doc/qtcreator/src/linux-mobile/creator-deployment-b2qt.qdoc
+++ b/doc/qtcreator/src/linux-mobile/creator-deployment-b2qt.qdoc
@@ -7,15 +7,15 @@
\ingroup creator-reference-deploy-configurations
- \title Boot2Qt Deploy Configuration
+ \title \B2Q Deploy Configuration
- \brief Copy application files to Boot2Qt devices.
+ \brief Copy application files to \B2Q devices.
- Specify settings for deploying applications to \l{Boot2Qt} devices
- in the project configuration file and in \uicontrol Projects >
- \uicontrol {Run Settings} > \uicontrol Deployment.
+ Specify settings for deploying applications to \l{\B2Q: Documentation}
+ {\B2Q} devices in the project configuration file and in \uicontrol Projects
+ > \uicontrol {Run Settings} > \uicontrol Deployment.
- \image qtcreator-boot2qt-deployment-steps.png "Boot2Qt deployment steps"
+ \image qtcreator-boot2qt-deployment-steps.png {Boot to Qt deployment steps}
The deployment process is described in more detail in
\l{Remote Linux Deploy Configuration}.
@@ -26,5 +26,7 @@
\uicontrol {Add Deploy Step} > \uicontrol {Change default application}
> \uicontrol {Set this application to start by default}.
- \sa {Build and Run}{How To: Build and Run}, {Boot2Qt Run Settings}
+ \sa {Build and Run}{How To: Build and Run},
+ {\B2Q}{How To: Develop for \B2Q}, {\B2Q Run Settings},
+ {Developing for \B2Q Devices}
*/
diff --git a/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc b/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc
deleted file mode 100644
index 2c36fa3fe4..0000000000
--- a/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-
-/*!
- \page creator-embedded-platforms.html
- \previouspage creator-reference.html
-
- \ingroup creator-reference-platforms
-
- \title Embedded Platforms
-
- \brief Embedded platforms that you can develop applications for.
-
- You can develop applications for the following embedded platforms:
-
- \list
- \li Bare Metal
- \li \l {Boot2Qt}
- \li Remote Linux
- \li Microcontrollers (MCU)
- \li \l QNX
- \endlist
-
- You must install the toolchain for building applications for the targeted
- embedded platform on the development PC and use \QMT to
- install Qt libraries that are built for the platform. You can then add a
- \l{glossary-buildandrun-kit}{kit} with the toolchain and the Qt version
- for the device's architecture. When possible, \QMT creates
- suitable kits for you.
-
- You can connect embedded devices to the development PC to run, debug, and
- analyze applications built for them from \QC.
-
- \section1 Boot2Qt
-
- The Boot2Qt stack runs on a variety of hardware. License holders can use
- tools to customize the contents of the stack and to take it into
- production hardware.
-
- The following topics have more information about developing applications
- for Boot2Qt devices:
-
- \list
- \li \l{Boot2Qt: Documentation}
- \li \l{Connecting Boot2Qt Devices}
- \li \l{Boot2Qt Run Settings}
- \li \l{Boot2Qt Deploy Configuration}
- \li \l{Run in Qt Application Manager}
- \endlist
-
- \section1 QNX
-
- The QNX Neutrino RTOS has more command-line tools
- and services, as described in \l {Qt for QNX}.
-
- \note In Qt 6, \QC support for QNX is considered experimental.
-
- The following topics have more information about developing applications
- for QNX devices:
-
- \list
- \li \l{Connecting QNX Devices}
- \li \l{QNX Neutrino Deploy Configuration}
- \li \l{QNX Run Settings}
- \li \l{Run on QNX devices}
- \li \l{Qt for QNX}
- \endlist
-
- \sa {Develop for Devices}{How To: Develop for Devices}, {Devices},
- {Supported Platforms}
-*/
diff --git a/doc/qtcreator/src/linux-mobile/creator-projects-settings-run-b2qt.qdoc b/doc/qtcreator/src/linux-mobile/creator-projects-settings-run-b2qt.qdoc
index 72ba8dd3b5..a8d3093cfb 100644
--- a/doc/qtcreator/src/linux-mobile/creator-projects-settings-run-b2qt.qdoc
+++ b/doc/qtcreator/src/linux-mobile/creator-projects-settings-run-b2qt.qdoc
@@ -1,30 +1,30 @@
-// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
- \page creator-run-settings-boot2qt.html
+ \page creator-run-settings-\B2Q.html
\previouspage creator-reference.html
\ingroup creator-reference-run-configurations
- \title Boot2Qt Run Settings
+ \title \B2Q Run Settings
- \brief Settings for running applications on Boot2Qt devices.
+ \brief Settings for running applications on \B2Q devices.
Specify settings for running applications on the \l {kits-tab}{Run device} that
you select for a kit in \uicontrol Projects > \uicontrol {Build & Run} >
\uicontrol Run > \uicontrol {Run Settings}.
- To run and debug an application on a \l Boot2Qt device (commercial only), you
- must create connections from the development host to the device and add the
- device configurations to kits. Select \uicontrol {Manage Kits} to add devices
- to kits. For more information, see
- \l{http://doc.qt.io/Boot2Qt/b2qt-installation-guides.html}
- {Boot2Qt: Installation Guide}.
+ To run and debug an application on a \l{\B2Q: Documentation}{\B2Q} device
+ (commercial only), create connections from the development host to the device
+ and add the device configurations to kits.
+
+ Select \uicontrol {Manage Kits} to add devices to kits.
The run settings display the path to the executable file on the development
host and on the device.
- \sa {Activate kits for a project}, {Configure projects for running}, {kits-tab}{Kits},
- {Boot2Qt Deploy Configuration}
+ \sa {\B2Q}{How To: Develop for \B2Q}, {Manage Kits}{How To: Manage Kits},
+ {Configure projects for running}, {kits-tab}{Kits},
+ {\B2Q Deploy Configuration}
*/
diff --git a/doc/qtcreator/src/linux-mobile/linuxdev-keys.qdoc b/doc/qtcreator/src/linux-mobile/linuxdev-keys.qdoc
index 3564f1d568..9af4de44ed 100644
--- a/doc/qtcreator/src/linux-mobile/linuxdev-keys.qdoc
+++ b/doc/qtcreator/src/linux-mobile/linuxdev-keys.qdoc
@@ -53,7 +53,7 @@
\ingroup creator-how-to-remote-linux
- \title Generate SSH Keys
+ \title Generate SSH keys
To protect the connections between \QC and a device, use \l{OpenSSH}.
diff --git a/doc/qtcreator/src/linux-mobile/linuxdev.qdoc b/doc/qtcreator/src/linux-mobile/linuxdev.qdoc
index 417b061171..e0ec120b85 100644
--- a/doc/qtcreator/src/linux-mobile/linuxdev.qdoc
+++ b/doc/qtcreator/src/linux-mobile/linuxdev.qdoc
@@ -22,6 +22,7 @@
\image qtcreator-preferences-devices-remote-linux.webp {Remote Linux Device in Devices}
+ //! [openssh]
\section1 Protecting Device Connections
To protect the connections between \QC and a device, use \l{OpenSSH} for
@@ -37,7 +38,18 @@
you may need to enter the password on every connection to the device,
or, if caching is enabled, at every \QC restart.
- \sa {Adding Docker Devices}, {Remote Linux}{How To: Develop for remote Linux},
+ If you frequently run into the timeout, consider using key-based
+ authentication. Create an SSH key in \QC with the \c ssh-keygen tool.
+
+ On \macos and Linux, go to \preferences > \uicontrol Devices > \uicontrol SSH
+ and increase the time (in minutes) for sharing an SSH connection in the
+ \uicontrol {Connection sharing timeout} field. Windows does not support
+ shared connections.
+
+ \image qtcreator-ssh-options.png {SSH preferences}
+ //! [openssh]
+
+ \sa {Add Docker devices}, {Remote Linux}{How To: Develop for remote Linux},
{Run in Qt Application Manager}, {Remote Linux Deploy Configuration},
{Remote Linux Run Settings}
*/
@@ -117,65 +129,13 @@
\title Add remote Linux devices
- Create connections between \QC and generic Linux devices to run, debug, and
+ Create connections between generic Linux devices and \QC to run, debug, and
analyze applications on them.
- \section1 Use a wizard to add a device
-
- To use a wizard to add a remote Linux device:
-
- \list 1
-
- \li Go to \preferences > \uicontrol Devices > \uicontrol Devices.
-
- \li Select \uicontrol Add > \uicontrol {Remote Linux Device}
- > \uicontrol {Start Wizard}.
-
- \image qtcreator-preferences-devices-remote-linux-connection.webp {Connection Data wizard}
-
- \li In \uicontrol {The name to identify this configuration},
- enter a name for the connection.
-
- \li In \uicontrol {The device's host name or IP address},
- enter the host name or IP address of the device.
- This value will be available in the variable \c %{Device:HostAddress}.
-
- \li In \uicontrol {The device's SSH port number}, enter the port
- number to use for SSH connections. This value will be
- available in the variable \c %{Device:SshPort}.
- \li In \uicontrol {The username to log into the device},
- enter the username to log into the device and run the
- application as.
- This value will be available in the variable \c %{Device:UserName}.
-
- \li Select \uicontrol {Next} to open the
- \uicontrol {Key Deployment} dialog.
-
- \image qtcreator-preferences-devices-remote-linux-key-deployment.webp "Key Deployment dialog"
-
- \li In \uicontrol {Private key file}, select a private key file
- to use for authentication. This value will be available in
- the variable \c %{Device:PrivateKeyFile}.
-
- \li If you do not have a public-private key pair, select
- \uicontrol {Create New Key Pair}. For more information,
- see \l{Generate SSH keys}.
-
- \li Select \uicontrol {Deploy Public Key} to copy the public
- key to the device.
-
- \li Select \uicontrol {Next} to create the connection.
-
- \endlist
-
- You can change the preferences later, as well as additional ones that the
- wizard does not show because they have sensible default values.
-
- \section1 Manually add a device
+ \image qtcreator-preferences-devices-remote-linux.webp {Remote Linux Device in Devices}
+ \caption Remote Linux Device preferences
- To add a device without using the wizard, select
- \uicontrol {Add Remote Linux Device} in the pull-down
- menu of the \uicontrol Add button.
+ \include qtcreator-add-linux-device.qdocinc {add linux device} {Remote Linux Device}
\sa {Remote Linux}{How To: Develop for remote Linux},
{Developing for Remote Linux Devices},
diff --git a/doc/qtcreator/src/linux-mobile/qtcreator-add-linux-device.qdocinc b/doc/qtcreator/src/linux-mobile/qtcreator-add-linux-device.qdocinc
new file mode 100644
index 0000000000..28fb464833
--- /dev/null
+++ b/doc/qtcreator/src/linux-mobile/qtcreator-add-linux-device.qdocinc
@@ -0,0 +1,47 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+//! [add linux device]
+
+ \section1 Use a wizard to add a device
+
+ To use a wizard to add \uicontrol {\1}:
+
+ \list 1
+ \li Go to \preferences > \uicontrol Devices > \uicontrol Devices.
+ \li Select \uicontrol Add > \uicontrol {\1} > \uicontrol {Start Wizard}.
+ \image qtcreator-preferences-devices-remote-linux-connection.webp {Connection Data wizard}
+ \li In \uicontrol {The name to identify this configuration},
+ enter a name for the connection.
+ \li In \uicontrol {The device's host name or IP address},
+ enter the host name or IP address of the device.
+ This becomes the value of the \c %{Device:HostAddress} variable.
+ \li In \uicontrol {The device's SSH port number}, enter the port
+ number for SSH connections. This becomes the value of the
+ \c %{Device:SshPort} variable.
+ \li In \uicontrol {The username to log into the device}, enter the
+ username to log into the device and run the application. This
+ becomes the value of the \c %{Device:UserName} variable.
+ \li Select \uicontrol {Next} to open the \uicontrol {Key Deployment}
+ dialog.
+ \image qtcreator-preferences-devices-remote-linux-key-deployment.webp {Key Deployment dialog}
+ \li In \uicontrol {Private key file}, select a private key file
+ for authentication. This becomes the value of the
+ \c %{Device:PrivateKeyFile} variable.
+ \li If you don't have a public-private key pair, select
+ \uicontrol {Create New Key Pair}. For more information,
+ see \l{Generate SSH keys}.
+ \li Select \uicontrol {Deploy Public Key} to copy the public
+ key to the device.
+ \li Select \uicontrol {Next} to create the connection.
+ \endlist
+
+ To change device preferences, go to \preferences > \uicontrol Devices >
+ \uicontrol Devices and select a device in \uicontrol Device.
+
+ \section1 Manually add a device
+
+ To add a device without using the wizard, select \uicontrol {\1} in the
+ pull-down menu of the \uicontrol Add button.
+
+//! [add linux device]
diff --git a/doc/qtcreator/src/overview/creator-only/creator-configuring.qdoc b/doc/qtcreator/src/overview/creator-only/creator-configuring.qdoc
index 1a1c04edfe..c612eb8642 100644
--- a/doc/qtcreator/src/overview/creator-only/creator-configuring.qdoc
+++ b/doc/qtcreator/src/overview/creator-only/creator-configuring.qdoc
@@ -73,7 +73,7 @@
To add devices, select \preferences > \uicontrol Devices >
\uicontrol Devices > \uicontrol Add.
- For more information, see \l{Connecting Devices}.
+ For more information, see \l{Develop for Devices}.
\section1 Changing Keyboard Shortcuts
diff --git a/doc/qtcreator/src/overview/creator-only/creator-getting-started.qdoc b/doc/qtcreator/src/overview/creator-only/creator-getting-started.qdoc
index b1531ae685..e735c95adf 100644
--- a/doc/qtcreator/src/overview/creator-only/creator-getting-started.qdoc
+++ b/doc/qtcreator/src/overview/creator-only/creator-getting-started.qdoc
@@ -14,8 +14,9 @@
\title Getting Started
- To learn the basics of \QC, take the \e {Getting Started with Qt Creator}
- course in \l{https://www.qt.io/courses/}{Qt Learning}.
+ To learn the basics of \QC, take the
+ \l{https://www.qt.io/academy/course-catalog#getting-started-with-qt-creator}
+ {Getting Started with Qt Creator} course in Qt Academy.
For more information about installing \QC, see \l{Install \QC}.
diff --git a/doc/qtcreator/src/overview/creator-only/creator-issues.qdoc b/doc/qtcreator/src/overview/creator-only/creator-issues.qdoc
index de7c8b45d3..8d31098332 100644
--- a/doc/qtcreator/src/overview/creator-only/creator-issues.qdoc
+++ b/doc/qtcreator/src/overview/creator-only/creator-issues.qdoc
@@ -41,7 +41,7 @@
settings are stored locally.
\li The Okteta KDE custom widget plugin might be installed as part of
- some Linux distributions. It can cause Qt Designer to crash. For
+ some Linux distributions. It can cause \QD to crash. For
more information, see:
\list
diff --git a/doc/qtcreator/src/overview/creator-only/creator-mobile-targets.qdoc b/doc/qtcreator/src/overview/creator-only/creator-mobile-targets.qdoc
deleted file mode 100644
index fe4f688ab5..0000000000
--- a/doc/qtcreator/src/overview/creator-only/creator-mobile-targets.qdoc
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-
-// **********************************************************************
-// NOTE: the sections are not ordered by their logical order to avoid
-// reshuffling the file each time the index order changes (i.e., often).
-// Run the fixnavi.pl script to adjust the links to the index order.
-// **********************************************************************
-
-/*!
- \page creator-connecting-mobile.html
- \previouspage creator-deployment-embedded-linux.html
- \nextpage creator-developing-android.html
-
- \title Connecting Devices
-
- You can connect \l{glossary-device}{devices} to the development PC to run, debug,
- and analyze applications built for them from \QC. When you install Qt for a
- target platform, such as Android or QNX,
- the build and run settings for the development targets might be set up
- automatically in \QC.
-
- You can connect the device to the development PC using a USB connection.
- Additionally, you can connect Linux-based devices by using a WLAN
- connection.
-
- The experimental WebAssembly plugin enables you to build your applications
- in WebAssembly format, to deploy them, and to run them in a web browser.
-
- \list
-
- \li \l{Developing for Android}
-
- Connect Android devices to the computer to develop Qt applications
- for them.
-
- \li \l{Developing for Bare Metal Devices}
-
- You can connect bare metal devices to the development PC and use \QC
- to debug applications on them with GDB or a hardware debugger.
-
- \li \l{Connecting Boot2Qt Devices}
-
- You can connect \l{Boot2Qt} devices to the development PC to run,
- debug, and analyze applications built for them from \QC.
-
- \li \l {Adding Docker Devices}
-
- Create Docker devices from Docker images and use them to build, run,
- and debug applications from \QC.
-
- \li \l{Developing for iOS}
-
- You use the tools delivered with Xcode to connect devices to \QC.
- \QC detects the tools and configured devices automatically and uses
- the tools to build, deploy, and run applications.
-
- \li \l{Developing for MCUs}
-
- You can connect MCU boards to a development host to deploy, run, and
- debug applications on them from \QC.
-
- \li \l{Connecting QNX Devices}
-
- You can connect QNX devices to the development PC to deploy, run and
- debug applications on them from within \QC. This is currently only
- supported for QNX Neutrino devices, and requires the QNX SDK to be
- installed on the development PC.
-
- \li \l{Developing for Remote Linux Devices}
-
- If you have a toolchain for building applications for embedded
- Linux devices installed on the development
- PC, you can add it and the device to \QC.
-
- \endlist
-
- \sa {Building Applications for the Web}, {Run in Qt Application Manager}
-*/
diff --git a/doc/qtcreator/src/overview/creator-only/creator-overview.qdoc b/doc/qtcreator/src/overview/creator-only/creator-overview.qdoc
index a77af77c71..127a59cf0d 100644
--- a/doc/qtcreator/src/overview/creator-only/creator-overview.qdoc
+++ b/doc/qtcreator/src/overview/creator-only/creator-overview.qdoc
@@ -198,16 +198,16 @@
For more information, see \l{Build and Run}{How To: Build and Run},
\l{Build Systems}, \l{Build Configurations}, and \l{Run Configurations}.
- \section1 Embedded Platforms
+ \section2 Embedded Platforms
You can develop applications for the following embedded platforms:
\list
- \li \l {Developing for Bare Metal Devices}{Bare Metal}
- \li \l {Boot2Qt}
+ \li \l {Bare Metal}
+ \li \l {\B2Q}
+ \li \l {MCUs}
+ \li \l {QNX Neutrino}
\li \l {Remote Linux}
- \li \l {Developing for MCUs}{Microcontrollers (MCU)}
- \li \l {Connecting QNX Devices}{QNX}
\endlist
Install the toolchain for building applications for the targeted
diff --git a/doc/qtcreator/src/overview/creator-only/creator-supported-platforms.qdoc b/doc/qtcreator/src/overview/creator-only/creator-supported-platforms.qdoc
index e75a5d0cef..43c60144c4 100644
--- a/doc/qtcreator/src/overview/creator-only/creator-supported-platforms.qdoc
+++ b/doc/qtcreator/src/overview/creator-only/creator-supported-platforms.qdoc
@@ -38,7 +38,7 @@
\li \image ok.png
\li \image ok.png
\row
- \li \l Boot2Qt
+ \li \l {\B2Q}
\li \image ok.png
\li \image ok.png
\li \image ok.png
@@ -63,7 +63,7 @@
\li \image ok.png
\li \image ok.png
\row
- \li \l{Building Applications for the Web}{WebAssembly}
+ \li \l{Build applications for the web}{WebAssembly}
\li \image ok.png
\li \image ok.png
\li \image ok.png
diff --git a/doc/qtcreator/src/projects/creator-only/creator-how-to-add-wizards.qdoc b/doc/qtcreator/src/projects/creator-only/creator-how-to-add-wizards.qdoc
index fa7663f02d..e6cb582b9b 100644
--- a/doc/qtcreator/src/projects/creator-only/creator-how-to-add-wizards.qdoc
+++ b/doc/qtcreator/src/projects/creator-only/creator-how-to-add-wizards.qdoc
@@ -44,5 +44,5 @@
to specify the position of the wizard. For example, \c B.MyClass.
\endlist
-\sa {Custom Wizards}, {Find settings files}
+ \sa {Run Qt Creator from the command line}, {Custom Wizards}
*/
diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-build-run-tutorial.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-build-run-tutorial.qdoc
index 457850d402..600b8f3079 100644
--- a/doc/qtcreator/src/projects/creator-only/creator-projects-build-run-tutorial.qdoc
+++ b/doc/qtcreator/src/projects/creator-only/creator-projects-build-run-tutorial.qdoc
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
// **********************************************************************
@@ -25,10 +25,10 @@
the development environment for Android or iOS. For more information, see
\l{Developing for Android} and \l{Developing for iOS}.
- To run an example application on a Boot2Qt device, you must set up
- Boot2Qt on the development host and create connections
+ To run an example application on a \B2Q device, you must set up
+ \B2Q on the development host and create connections
between the host and devices. For more information, see
- \l{Boot2Qt: Documentation}.
+ \l{\B2Q: Documentation}.
If you have \l{Qt Design Studio Manual}{\QDS} installed, you can open
\QDS examples from \QC in \QDS.
@@ -47,13 +47,20 @@
\li Select an example in the list of examples.
You can also use tags (3) to filter examples. For instance, enter
- the \uicontrol Boot2Qt tag (commercial only) in the search field
- (4) to list examples that you can run on Boot2Qt devices.
+ the \uicontrol \B2Q tag (commercial only) in the search field
+ (4) to list examples that you can run on \B2Q devices.
+
+ \li In \uicontrol {Configure Project}, select
+ \l{glossary-buildandrun-kit}{kits} for building the example for the
+ target platforms.
+
+ \image qtcreator-configure-project.webp {Configure Project view}
+
+ \li Select \uicontrol {Configure Project}.
\li To check that you can compile and link the application code for a
- device, click the \uicontrol {Kit Selector} and select a
- \l{glossary-buildandrun-kit}{kit} for the
- device.
+ device, click the \uicontrol {Kit Selector} and select the kit for
+ the device.
\image qtcreator-examples-kit-selector.webp {Selecting a kit to build with}
@@ -61,7 +68,7 @@
automatically detected the installed kit. If you cannot see any kits,
see \l{Add kits}.
- \li Click \inlineimage icons/run_small.png
+ \li Select \inlineimage icons/run_small.png
(\uicontrol Run) to build and run the application.
\li To see the compilation progress, press \key{Alt+4} to open
@@ -78,7 +85,7 @@
\endlist
- \sa {Add compilers}, {Add kits}, {Add Qt versions},
+ \sa {Manage Kits}{How To: Manage Kits}, {Open projects},
{Developing for Android}, {Developing for iOS},
- {Compile Output}, {Boot2Qt: Documentation}, {Qt Design Studio Manual}
+ {Compile Output}, {\B2Q: Documentation}, {Qt Design Studio Manual}
*/
diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-building.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-building.qdoc
index 8dbf04e085..7cfb8f86e0 100644
--- a/doc/qtcreator/src/projects/creator-only/creator-projects-building.qdoc
+++ b/doc/qtcreator/src/projects/creator-only/creator-projects-building.qdoc
@@ -105,7 +105,7 @@
\uicontrol Projects view.
\sa {Configure projects for building}, {Build and Run}{How To: Build and Run},
- {Adding Docker Devices}, {Specifying Build Settings}
+ {Add Docker devices}, {Specifying Build Settings}
*/
/*!
diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc
index 8cea15ccbf..42cc51bef9 100644
--- a/doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc
+++ b/doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc
@@ -104,7 +104,7 @@
\endtable
The emscripten compiler is toolchain for compiling to
- \l{Building Applications for the Web}{WebAssembly}.
+ \l{Build applications for the web}{WebAssembly}.
\section2 Bare-metal compilers
diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc
index 83a1d745f7..5388f7c6e5 100644
--- a/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc
+++ b/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc
@@ -66,7 +66,7 @@
The installers create \l{glossary-buildandrun-kit}{kits} and specify build
and run settings for the installed device types. However, you might need to
install and configure some additional software on the devices to be able to
- \l{Connecting Devices}{connect} to them from the development PC.
+ \l{Develop for Devices}{connect} to them from the computer.
\sa {Manage Projects}{How To: Manage Projects}, {Custom Wizards}
*/
diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-kits.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-kits.qdoc
index 4eb4faf42a..1e2b8ed818 100644
--- a/doc/qtcreator/src/projects/creator-only/creator-projects-kits.qdoc
+++ b/doc/qtcreator/src/projects/creator-only/creator-projects-kits.qdoc
@@ -27,14 +27,14 @@
\list
\li \l{Developing for Android}{Android}
\li \l{Developing for Bare Metal Devices}{Bare Metal}
- \li \l{Boot2Qt: Documentation}{Boot2Q} (commercial only)
- \li \l{Adding Docker Devices}{Docker} (experimental)
+ \li \l{\B2Q: Documentation}{\B2Q} (commercial only)
+ \li \l{Add Docker devices}{Docker} (experimental)
\li \l{Developing for iOS}{iOS}
\li iOS Simulator
\li \l{Developing for MCUs}{MCU} (commercial only)
- \li \l{Connecting QNX Devices}{QNX}
+ \li \l{Add a QNX Neutrino device}{QNX}
\li \l{Developing for Remote Linux Devices}{Remote Linux}
- \li \l{Building Applications for the Web}{WebAssembly Runtime}
+ \li \l{Build applications for the web}{WebAssembly Runtime}
\endlist
To add kits:
@@ -134,7 +134,7 @@
cross-compiling, leave this field empty.
\row
\li \uicontrol {Emulator skin}
- \li Skin to use for the \l {Emulator}{Boot2Qt Emulator Device}.
+ \li Skin to use for the \l {Emulator}{\B2Q Emulator Device}.
\row
\li \uicontrol {Compiler}
\li C or C++ compiler that you use to build the project. You can add
diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-opening.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-opening.qdoc
index f631e67389..66b69a573f 100644
--- a/doc/qtcreator/src/projects/creator-only/creator-projects-opening.qdoc
+++ b/doc/qtcreator/src/projects/creator-only/creator-projects-opening.qdoc
@@ -63,15 +63,15 @@
To re-configure projects:
\list 1
- \li In the \uicontrol {Configure Project} tab, select
+ \li In \uicontrol {Configure Project}, select
\l{glossary-buildandrun-kit}{kits} for building
and running your project.
- \image qtcreator-open-project-kits.png {Configure Project tab}
+ \image qtcreator-configure-project.webp {Configure Project view}
\li Select \uicontrol {Configure Project}.
\endlist
- The \uicontrol {Configure Project} tab displays a list of kits that you
- install on the development PC and configure in \preferences > \uicontrol Kits.
+ \uicontrol {Configure Project} shows a list of kits that you
+ install on the computer and configure in \preferences > \uicontrol Kits.
Even if you do not intend to build the project, the C++ and QML code models
need a Qt version and compiler to offer code completion. To specify them,
diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-build.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-build.qdoc
index 250a5085c4..b3ababb5fb 100644
--- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-build.qdoc
+++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-build.qdoc
@@ -57,7 +57,7 @@
\section1 Build on remote devices
You can build applications on \l{Developing for Remote Linux Devices}
- {remote Linux} or \l{Adding Docker Devices}{Docker} devices if you
+ {remote Linux} or \l{Add Docker devices}{Docker} devices if you
have kits that specify the devices and toolchains to use. When the
build device of the kit is a remote device, such as a remote Linux or
Docker device, the \uicontrol Browse button next to the
diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run.qdoc
index 7f8ba6894b..bf816f4351 100644
--- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run.qdoc
+++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run.qdoc
@@ -27,7 +27,7 @@
\list
\li \l {Android Run Settings}{Android}
- \li \l {Boot2Qt Run Settings}{Boot2Qt}
+ \li \l {\B2Q Run Settings}{\B2Q}
\li \l {Desktop Device Run Settings}{Desktop}
\li \l {Python Run Settings}{Python}
\li \l {QNX Run Settings}{QNX}
diff --git a/doc/qtcreator/src/qnx/creator-deployment-qnx.qdoc b/doc/qtcreator/src/qnx/creator-deployment-qnx.qdoc
index 11b4806d66..eb68e9e0f8 100644
--- a/doc/qtcreator/src/qnx/creator-deployment-qnx.qdoc
+++ b/doc/qtcreator/src/qnx/creator-deployment-qnx.qdoc
@@ -32,7 +32,7 @@
The \uicontrol {Check for a configured device} deployment step looks for a
device that is ready for deployment.
- \sa {Build and Run}{How To: Build and Run}, {Connecting QNX Devices},
+ \sa {Build and Run}{How To: Build and Run}, {QNX Neutrino}{How To: Develop for QNX},
{QNX Run Settings}, {Remote Linux Deploy Configuration},
{Remote Linux Run Settings}
*/
diff --git a/doc/qtcreator/src/qnx/creator-developing-qnx.qdoc b/doc/qtcreator/src/qnx/creator-developing-qnx.qdoc
index 050534c56a..f6990187c3 100644
--- a/doc/qtcreator/src/qnx/creator-developing-qnx.qdoc
+++ b/doc/qtcreator/src/qnx/creator-developing-qnx.qdoc
@@ -1,32 +1,51 @@
// Copyright (C) 2018 Blackberry
-// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
- \previouspage creator-developing-mcu.html
\page creator-developing-qnx.html
- \nextpage creator-developing-generic-linux.html
+ \previouspage creator-how-tos.html
- \title Connecting QNX Devices
+ \ingroup creator-how-to-qnx
- You can connect QNX devices to the development PC to deploy, run and debug
- applications on them from within \QC. The QNX Neutrino RTOS has additional
+ \title Add a QNX Neutrino device
+
+ Create a connection between \QC and a QNX Neutrino device to run, debug, and
+ analyze applications on the device. The QNX Neutrino RTOS has additional
command-line tools and services, as described in \l {Qt for QNX}.
- \note In Qt 6, \QC support for QNX is considered experimental.
+ \image qtcreator-devices-qnx.webp {QNX device preferences}
+ \caption QNX device preferences
+
+ \include qtcreator-add-linux-device.qdocinc {add linux device} {QNX Device}
+
+ \sa {QNX Neutrino}{How To: Develop for QNX},
+ {Remote Linux}{How To: Develop for remote Linux},
+ {Manage Kits}{How To: Manage Kits}
+*/
+
+/*!
+ \page creator-how-to-create-qnx-kits.html
+ \previouspage creator-how-tos.html
- \section1 Adding a QNX Neutrino Device in \QC
+ \ingroup creator-how-to-qnx
- Adding a QNX Neutrino device is very similar to adding a
- \l{Add remote Linux devices}{remote Linux device}, except that
- you need to select \uicontrol {QNX Device} in the
- \uicontrol {Device Configuration} wizard.
+ \title Create kits for QNX Neutrino devices
- \section1 Add kits for QNX Devices
+ To generate a build and run \l {Kits}{kit} for QNX Neutrino devices:
- To view QNX device settings, select \preferences >
- \uicontrol Devices > \uicontrol QNX. Select the \uicontrol {Generate kits}
- check box to allow \QC to generate kits for QNX development.
+ \list 1
+ \li Go to \preferences > \uicontrol Devices > \uicontrol Devices.
+ \li Select \uicontrol Add > \uicontrol {QNX Device} >
+ \uicontrol {Start Wizard} to add a QNX Neutrino device.
+ \li Select \uicontrol Apply to fetch the information needed for
+ creating kits.
+ \li Go to \preferences > \uicontrol Devices > \uicontrol QNX.
+ \li Select \uicontrol {Create Kit} to create a kit for a particular
+ platform.
+ \image qtcreator-preferences-qnx.webp {QNX Preferences}
+ \li Activate the kit for your project.
+ \endlist
- \sa {Add kits}
+ \sa {QNX Neutrino}{How To: Develop for QNX}, {Activate kits for a project}
*/
diff --git a/doc/qtcreator/src/qnx/creator-projects-how-to-run-qnx.qdoc b/doc/qtcreator/src/qnx/creator-projects-how-to-run-qnx.qdoc
index c6b41392c9..13fc502533 100644
--- a/doc/qtcreator/src/qnx/creator-projects-how-to-run-qnx.qdoc
+++ b/doc/qtcreator/src/qnx/creator-projects-how-to-run-qnx.qdoc
@@ -5,15 +5,17 @@
\page creator-how-to-run-on-qnx.html
\previouspage creator-how-tos.html
- \ingroup creator-how-to-run
+ \ingroup creator-how-to-qnx
\title Run on QNX devices
To build an application and run it on a device:
\list 1
- \li Connect the device to the computer or to the Wi-Fi network.
- \li Configure the device and specify a connection to it.
+ \li Connect the device to the computer or to a network.
+ \li Go to \preferences > \uicontrol Devices > \uicontrol Devices,
+ and add a QNX device.
+ \image qtcreator-devices-qnx.webp {QNX device preferences}
\li Make sure that your kit has your QNX device set.
\li Select \inlineimage icons/run_small.png (\uicontrol Run).
\endlist
@@ -32,7 +34,7 @@
\section2 Debug output cannot be shown
- For the command-line output to show up in the \uicontrol{Application Output},
+ For the command-line output to show up in \l {Application Output},
\QC has to create an SSH connection to the device.
This is only possible if QNX Momentics is not running, and the SSH key
configured for the device is a 4096-bit key.
@@ -48,6 +50,6 @@
\c printf, \c ps, \c read, \c sed, \c sleep, \c uname, \c slog2info, and
\c cat.
- \sa {Connecting QNX Devices}, {Run on many platforms}, {Compilers},
- {Embedded Platforms}, {kit-preferences}{Kits}
+ \sa {QNX Neutrino}{How To: Develop for QNX}, {Run on many platforms},
+ {Compilers}, {kit-preferences}{Kits}
*/
diff --git a/doc/qtcreator/src/qnx/creator-projects-settings-run-qnx.qdoc b/doc/qtcreator/src/qnx/creator-projects-settings-run-qnx.qdoc
index c3fd8861c0..4e39314d8f 100644
--- a/doc/qtcreator/src/qnx/creator-projects-settings-run-qnx.qdoc
+++ b/doc/qtcreator/src/qnx/creator-projects-settings-run-qnx.qdoc
@@ -15,13 +15,14 @@
you select for a kit in \uicontrol Projects > \uicontrol {Build & Run} >
\uicontrol Run > \uicontrol {Run Settings}.
- To run and debug an application on a QNX device, you must
- create connections from the development PC to the device. Select
- \uicontrol {Manage device configurations} to create a connection.
+ To run and debug an application on a QNX device, select
+ \uicontrol {Manage device configurations} to create a
+ connection between \QC and the device.
Settings for QNX Neutrino devices are very similar to those for remote linux
devices.
- \sa {Remote Linux Run Settings}, {Activate kits for a project},
- {Configure projects for running}, {kits-tab}{Kits}, {Connecting QNX Devices}
+ \sa {QNX Neutrino}{How To: Develop for QNX}, {Manage Kits}{How To: Manage Kits},
+ {Remote Linux Run Settings}, {Configure projects for running},
+ {kits-tab}{Kits}
*/
diff --git a/doc/qtcreator/src/qtcreator-toc.qdoc b/doc/qtcreator/src/qtcreator-toc.qdoc
index 41d4ef75c8..442ff25835 100644
--- a/doc/qtcreator/src/qtcreator-toc.qdoc
+++ b/doc/qtcreator/src/qtcreator-toc.qdoc
@@ -22,13 +22,6 @@
\endlist
\li \l{Creating Projects}
\li \l{Configuring Projects}
- \li \l{Connecting Devices}
- \list
- \li \l{Connecting Boot2Qt Devices}
- \li \l{Adding Docker Devices}
- \li \l{Connecting QNX Devices}
- \li \l{Building Applications for the Web}
- \endlist
\li \l{Debugging}
\li \l{Analyzing Code}
\endlist
@@ -58,12 +51,20 @@
\generatelist creator-how-to-android
\li Bare Metal
\generatelist creator-how-to-bare-metal
+ \li \B2Q
+ \generatelist creator-how-to-b2qt
+ \li Docker
+ \generatelist creator-how-to-docker
\li iOS
\generatelist creator-how-to-ios
\li MCUs
\generatelist creator-how-to-mcu
+ \li QNX Neutrino
+ \generatelist creator-how-to-qnx
\li Remote Linux
\generatelist creator-how-to-remote-linux
+ \li WebAssembly
+ \generatelist creator-how-to-webassembly
\endlist
\li Edit Code
\generatelist creator-how-to-edit
diff --git a/doc/qtcreator/src/qtcreator.qdoc b/doc/qtcreator/src/qtcreator.qdoc
index 8f0020283e..96bafe421d 100644
--- a/doc/qtcreator/src/qtcreator.qdoc
+++ b/doc/qtcreator/src/qtcreator.qdoc
@@ -21,7 +21,7 @@
operating systems. For more information, see \l{Supported Platforms}.
In addition, you can use the experimental
- \l{Building Applications for the Web}{WebAssembly plugin}
+ \l{Build applications for the web}{WebAssembly plugin}
to build applications in web format and run them in web
browsers.
@@ -39,7 +39,6 @@
\li \l{Getting Started}
\li \l{Creating Projects}
\li \l{Configuring Projects}
- \li \l{Connecting Devices}
\li \l{Debugging}
\li \l{Analyzing Code}
\endlist
@@ -51,6 +50,7 @@
\li \l{Build and Run}
\li \l{Debug}
\li \l{Design UIs}
+ \li \l{Develop for Devices}
\li \l{Edit Code}
\li \l{Manage Projects}
\li \l{Read Documentation}
diff --git a/doc/qtcreator/src/qtquick/qtquick-live-preview-devices.qdoc b/doc/qtcreator/src/qtquick/qtquick-live-preview-devices.qdoc
index 740b237eae..87142f4a51 100644
--- a/doc/qtcreator/src/qtquick/qtquick-live-preview-devices.qdoc
+++ b/doc/qtcreator/src/qtquick/qtquick-live-preview-devices.qdoc
@@ -20,7 +20,7 @@
To preview a UI on an Android device, turn on USB debugging on the device
and connect it to the computer with a USB cable.
- To preview a UI on a Boot2Qt device, connect the device to the computer
+ To preview a UI on a \B2Q device, connect the device to the computer
with a USB cable, or a wired or wireless connection, depending on
the device, and configure a connection to it. The necessary kits have been
predefined, but you need to select the one appropriate for your current
@@ -74,12 +74,12 @@
Learn more about \l{Viewing Applications on Android}.
\endif
- \section2 On Boot2Qt
+ \section2 On \B2Q
- Preview a UI on a supported Boot2Qt device that you configure as
- instructed in the Boot2Qt documentation.
+ Preview a UI on a supported \B2Q device that you configure as
+ instructed in the \B2Q documentation.
- \sa {Boot2Qt: Documentation}, {Support Levels for Target Hardware}
+ \sa {\B2Q: Documentation}, {Support Levels for Target Hardware}
\if defined(qtcreator)
\sa {Design UIs}{How To: Design UIs}, {UI Design}
diff --git a/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc b/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc
index 46651d8748..e1f7e3bce5 100644
--- a/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc
+++ b/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc
@@ -82,7 +82,7 @@
\uicontrol {Reset Code Model} to reset the code model.
\if defined(qtdesignstudio)
For more information about how to show the \uicontrol {Tools} menu, see
- \l{Customizing the Menu}.
+ \l{Customizing the Menu Bar}.
\endif
\if defined(qtcreator)
diff --git a/doc/qtcreator/src/user-interface/creator-only/creator-reference-to-do-entries-view.qdoc b/doc/qtcreator/src/user-interface/creator-only/creator-reference-to-do-entries-view.qdoc
index 5d78d8345c..61613519c3 100644
--- a/doc/qtcreator/src/user-interface/creator-only/creator-reference-to-do-entries-view.qdoc
+++ b/doc/qtcreator/src/user-interface/creator-only/creator-reference-to-do-entries-view.qdoc
@@ -63,7 +63,7 @@
\li To determine whether the keywords in the whole project, in the
current file, or in a subproject are displayed by default, select
- the appropriate option in the \uicontrol {Scanning scope} group.
+ the appropriate option in the \uicontrol {Scanning Scope} group.
\endlist
diff --git a/doc/qtcreator/src/vcs/creator-only/creator-vcs-perforce.qdoc b/doc/qtcreator/src/vcs/creator-only/creator-vcs-perforce.qdoc
index 443aa40be4..6c9c93ff51 100644
--- a/doc/qtcreator/src/vcs/creator-only/creator-vcs-perforce.qdoc
+++ b/doc/qtcreator/src/vcs/creator-only/creator-vcs-perforce.qdoc
@@ -35,11 +35,26 @@
Set workspace details in \uicontrol {P4 user}, \uicontrol {P4 client}, and
\uicontrol {P4 port}.
- To specify the details individually for several projects, use configuration
- files instead. Create a \c {p4config.txt} configuration file for each
- project in the top level project directory, and run
- \c{p4 set P4CONFIG=p4config.txt} once. You must deselect the
- \uicontrol {Environment Variables} check box.
+ \section1 Using Configuration Files
+
+ To specify workspace details individually for several projects, use
+ configuration files:
+
+ \list 1
+ \li Create a \c {p4config.txt} configuration file for each project in the
+ top level project directory.
+ \li Go to \preferences > \uicontrol {Version Control} >
+ \uicontrol Perforce.
+ \li Clear \uicontrol {Environment Variables}.
+ \li To set \c P4CONFIG to use the file that you created, run the
+ following command from the command line once:
+ \badcode
+ p4 set P4CONFIG=p4config.txt
+ \endcode
+ \endlist
+
+ For more information about using the \c P4CONFIG variable, see
+ \l{Perforce: P4CONFIG}.
\section1 Editing Files
diff --git a/doc/qtcreator/src/webassembly/creator-webassembly.qdoc b/doc/qtcreator/src/webassembly/creator-webassembly.qdoc
index e4bea34751..ab757d7add 100644
--- a/doc/qtcreator/src/webassembly/creator-webassembly.qdoc
+++ b/doc/qtcreator/src/webassembly/creator-webassembly.qdoc
@@ -1,116 +1,71 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
- \previouspage creator-developing-generic-linux.html
\page creator-setup-webassembly.html
- \nextpage creator-build-process-customizing.html
+ \previouspage creator-how-tos.html
- \title Building Applications for the Web
+ \ingroup creator-how-to-webassembly
- WebAssembly is a binary format that allows sand-boxed executable code in
- web pages. This format is nearly as fast as native machine code, and is
- now supported by all major web browsers.
+ \title Build applications for the Web
- \l {Qt for WebAssembly} enables building Qt applications so that they can be
- integrated into web pages. It doesn't require any client-side installations
- and reduces the use of server-side resources.
+ WebAssembly is a binary format that allows sand-boxed executable code in
+ web pages. This format is nearly as fast as native machine code. It is
+ supported by all major web browsers.
- The experimental WebAssembly plugin enables you to build your applications
- in WebAssembly format and deploy and run them in the local web browser.
- You can change the web browser in the project's \l{Specifying Run Settings}
- {run settings}.
+ Use \l {Qt for WebAssembly} to build your applications in WebAssembly format
+ and deploy and run them on the local web browser. Change the web browser in
+ the project's \l{Run applications in a web browser}{run settings}.
\note Enable the WebAssembly plugin to use it.
- To build applications for the web and run them in a web browser, you need to
- install Qt for WebAssembly and the toolchain for compiling to WebAssembly.
-
- \section1 Requirements
-
- You need the following software to build Qt applications for the web and run
- them in a browser:
-
- \list
- \li Qt for WebAssembly 5.15, or later
- \li On Windows: \l{http://wiki.qt.io/MinGW}{\MinGW} 7.3.0, or later
- \li \l{https://emscripten.org/docs/introducing_emscripten/index.html}
- {emscripten} toolchain for compiling to WebAssembly
- \endlist
-
- \section1 Setting Up the Development Environment
+ To build applications for the web and run them in a web browser, install
+ Qt for WebAssembly with \QOI. It automatically adds a build and run kit
+ to \QC.
- You need to install and configure Qt for WebAssembly and the toolchain for
- compiling to WebAssembly. \QOI automatically adds a build and
- run kit to \QC.
+ \section1 Set up WebAssembly development environment
- \section2 Setting Up Qt for WebAssembly
-
- To set up Qt for WebAssembly:
-
- \list 1
- \li Use \QMT to install Qt for WebAssembly and, on
- Windows, \MinGW (found in \uicontrol {Developer and Designer Tools}).
- \li Check out a known-good Emscripten version supported by the Qt for
- WebAssembly version that you installed, and install and activate
- \c emscripten, as instructed in
- \l {https://doc.qt.io/qt-5/wasm.html#install-emscripten}
- {Install Emscripten}.
- \endlist
-
- \section2 Specifying WebAssembly Settings
-
- To configure \QC for building Qt apps for the web:
+ To set up the development environment for WebAssembly:
\list 1
- \li Select \preferences > \uicontrol Devices > \uicontrol WebAssembly.
- \li In the \uicontrol {Emscripten SDK path} field, enter the root
- directory where \c emsdk is installed.
+ \li Go to \preferences > \uicontrol Devices > \uicontrol WebAssembly.
+ \li In \uicontrol {Emscripten SDK path}, enter the root directory where
+ you installed \c emsdk.
\li \QC configures the \uicontrol {Emscripten SDK environment} for you
- if the \c emsdk is supported by the Qt for WebAssembly version that
- you will use for developing the application.
- \image qtcreator-webassembly-options.png "Qt for WebAssembly device preferences"
- \li Select \preferences > \uicontrol Kits.
- \image qtcreator-kit-webassembly.png "Qt for WebAssembly kit"
- \li In the \uicontrol Compiler group, \uicontrol {Emscripten Compiler}
- should have been automatically detected for both C++ and C. If not,
- check that emscripten is set up correctly.
+ if your Qt for WebAssembly version supports the \c emsdk.
+ \image qtcreator-webassembly-options.png {Qt for WebAssembly device preferences}
+ \li Go to \preferences > \uicontrol Kits.
+ \image qtcreator-kit-webassembly.png {Qt for WebAssembly kit}
+ \li If \uicontrol {Emscripten Compiler} was not automatically set
+ for both C++ and C, check that emscripten is set up correctly.
\endlist
- \section2 Adding WebAssembly Kits
+ \sa {Run applications in a web browser}
+*/
- The Qt for Web Assembly installation automatically adds build and run kits
- to \QC. To add kits:
+/*!
+ \page creator-how-to-run-webassembly.html
+ \previouspage creator-how-tos.html
- \list 1
- \li Select \preferences > \uicontrol Kits > \uicontrol Add.
- \li In the \uicontrol Name field, specify a name for the kit.
- \li In the \uicontrol {Run device type} field, select
- \uicontrol {WebAssembly Runtime}.
- The value of the \uicontrol Device field is automatically
- set to \uicontrol {Web Browser}.
- \li In the \uicontrol Compiler field, select
- \uicontrol {Emscripten Compiler} for both C and C++.
- \li Select \uicontrol Apply to add the kit.
- \endlist
+ \ingroup creator-how-to-webassembly
- \section1 Running Applications in a Web Browser
+ \title Run applications in a web browser
- To run a project:
+ To build Qt applications in \l{Qt for WebAssembly}{WebAssembly} format and
+ run them in a web browser:
\list 1
\li Open a project for an application you want to run in a web browser.
- \li Select \uicontrol Projects > \uicontrol {Build & Run}, and then
+ \li Go to \uicontrol Projects > \uicontrol {Build & Run}, and then
select the WebAssembly kit as the build and run kit for the project.
\li Select \uicontrol Run to specify run settings.
- \li In the \uicontrol {Web browser} field, select the browser to run
- the application in.
- \image qtcreator-settings-run-webassembly.png "Selecting the browser to run in"
+ \li In \uicontrol {Web browser}, select a browser.
+ \image qtcreator-settings-run-webassembly.png {Selecting the browser to run in}
\endlist
- You can now build Qt applications in WebAssembly format and run them in
+ Build Qt applications in WebAssembly format and run them in
a web browser as described in \l {Build for many platforms} and
\l{Run on many platforms}.
- \sa {Enable and disable plugins}
+ \sa {Build applications for the Web}, {Enable and disable plugins}
*/
diff --git a/doc/qtcreator/src/widgets/creator-faq-qtdesigner.qdocinc b/doc/qtcreator/src/widgets/creator-faq-qtdesigner.qdocinc
index 93ed060e4a..eb93c69a8c 100644
--- a/doc/qtcreator/src/widgets/creator-faq-qtdesigner.qdocinc
+++ b/doc/qtcreator/src/widgets/creator-faq-qtdesigner.qdocinc
@@ -4,7 +4,7 @@
/*!
//! [qt designer faq]
- \section1 Qt Designer Integration Questions
+ \section1 \QD Integration Questions
\b {Why are custom widgets not loaded in the \uicontrol Design mode even though
it works in standalone \QD?}
@@ -13,7 +13,7 @@
that match its build key. The locations are different for standalone and
integrated \QD.
- For more information, see \l{Adding Qt Designer Plugins}.
+ For more information, see \l{Adding \QD Plugins}.
//! [qt designer faq]
*/
diff --git a/doc/qtcreator/src/widgets/qtdesigner-app-tutorial.qdoc b/doc/qtcreator/src/widgets/qtdesigner-app-tutorial.qdoc
index f3717ee049..8f744bced0 100644
--- a/doc/qtcreator/src/widgets/qtdesigner-app-tutorial.qdoc
+++ b/doc/qtcreator/src/widgets/qtdesigner-app-tutorial.qdoc
@@ -197,7 +197,7 @@
\endlist
For more information about designing forms with \QD, see the
- \l{Qt Designer Manual}.
+ \l{\QD Manual}.
\section2 Completing the Header File
diff --git a/doc/qtcreator/src/widgets/qtdesigner-overview.qdoc b/doc/qtcreator/src/widgets/qtdesigner-overview.qdoc
index b2357514fa..29d2c7d34b 100644
--- a/doc/qtcreator/src/widgets/qtdesigner-overview.qdoc
+++ b/doc/qtcreator/src/widgets/qtdesigner-overview.qdoc
@@ -29,7 +29,7 @@
Furthermore, features such as widget promotion and custom plugins
allow you to use your own widgets with \QD.
- For more information about \QD, see the \l{Qt Designer Manual}.
+ For more information about \QD, see the \l{\QD Manual}.
Generally, the integrated \QD has the same functions as the standalone
\QD. The following sections describe the differences.
@@ -62,7 +62,7 @@
\uicontrol {Follow Symbol Under Cursor} in the context menu
or press \key F2 when the cursor is over a string literal.
- \section1 Specifying Settings for Qt Designer
+ \section1 Specifying Settings for \QD
You can drag and drop the views in \QD to new positions on the screen.
@@ -102,7 +102,7 @@
\list 1
\li \preferences > \uicontrol Designer.
- \image qtdesigner-embedded-design.png "Qt Designer Embedded Design preferences"
+ \image qtdesigner-embedded-design.png "Qt Widgets Designer Embedded Design preferences"
\li In \uicontrol {Embedded Design}, select \inlineimage icons/plus.png
to open the \uicontrol {Add Profile} dialog.
\image qtdesigner-add-profile.png "Add Profile dialog"
@@ -119,5 +119,5 @@
To import device profiles from .qdp files, select \uicontrol Open. To save
them as .qdp files, select \uicontrol Save.
- \sa {Creating a Qt Widget Based Application}, {Adding Qt Designer Plugins}
+ \sa {Creating a Qt Widget Based Application}, {Adding \QD Plugins}
*/
diff --git a/doc/qtcreator/src/widgets/qtdesigner-plugins.qdoc b/doc/qtcreator/src/widgets/qtdesigner-plugins.qdoc
index 00f61ab051..3ce3dc02f7 100644
--- a/doc/qtcreator/src/widgets/qtdesigner-plugins.qdoc
+++ b/doc/qtcreator/src/widgets/qtdesigner-plugins.qdoc
@@ -26,7 +26,7 @@
and to change the default plugin path, see \l{How to Create Qt Plugins}.
For more information about how to create plugins for \QD, see
- \l{Using Custom Widgets with Qt Designer}.
+ \l{Using Custom Widgets with \QD}.
\section1 Locating \QD Plugins
@@ -42,7 +42,7 @@
To check which plugins were loaded successfully and which failed, choose
\uicontrol Tools > \uicontrol {Form Editor} >
- \uicontrol {About Qt Designer Plugins}.
+ \uicontrol {About \QD Plugins}.
The standalone \QD is part of the Qt library used for building projects,
located in \c {<Qt_version>\<compiler>\bin} in the Qt installation
diff --git a/doc/qtcreatordev/src/qtcreator-ui-text.qdoc b/doc/qtcreatordev/src/qtcreator-ui-text.qdoc
index f42c80671a..e76ad238e2 100644
--- a/doc/qtcreatordev/src/qtcreator-ui-text.qdoc
+++ b/doc/qtcreatordev/src/qtcreator-ui-text.qdoc
@@ -158,11 +158,11 @@
\section3 Writing Tooltips in Design Mode
- In Qt Designer, use plain text for tooltips. For extra formatting, write
+ In \QD, use plain text for tooltips. For extra formatting, write
short, canonical HTML in the source tab of the rich text editor:
\c {<html><head/><body><b>Note:</b> text.}
- Qt Designer has a feature that simplifies the rich text (on by default),
+ \QD has a feature that simplifies the rich text (on by default),
but still, you should verify by looking at the \uicontrol Source tab.
\section2 Writing Messages
@@ -273,8 +273,36 @@
\section2 Marking UI Text for Translation
- Make sure the text strings presented to the user are easy to translate.
- The user interface text strings are enclosed in \c tr() calls and
+ \section3 Translation Context
+
+ Avoid creating many different translation contexts. By default, \c tr()
+ uses the context of the class, which means that translations break when
+ code is refactored and UI text is moved to a different class.
+
+ Create a single translation context for each plugin instead. Create a header
+ \c myplugintr.h
+
+ \code
+ #pragma once
+
+ #include <QCoreApplication>
+
+ namespace MyPlugin {
+
+ struct Tr
+ {
+ Q_DECLARE_TR_FUNCTIONS(QtC::MyPlugin)
+ };
+
+ } // namespace MyPlugin
+ \endcode
+
+ and use that struct as the translation context for UI text by calling
+ \c {Tr::tr()}. Use disambiguation strings in cases that need it.
+
+ \section3 Translator Comments
+
+ The user interface text strings are enclosed in \c {Tr::tr()} calls and
extracted from the source code during the translation process. Therefore,
the translator might not know the source code context of the messages.
@@ -286,26 +314,24 @@
return tr("Add");
\endcode
- If the class is not Q_OBJECT, use \c {QCoreApplication::translate("class
- context", "message")} or consider using Q_DECLARE_TR_FUNCTIONS. Do not use
- \c {QObject::tr()}, which is confusing because the messages appear grouped
- by class context in Qt Linguist and messages tied to QObject do not have a
- class context.
+ \section3 File Paths
+
+ Use \c{FilePath::toUserOutput()} from the \c Utils library for file and directory
+ names that you pass to \c{Tr::tr().arg()}.
- Use \c{QDir::toNativeSeparators()} for file and directory names that you
- pass to \c{tr().arg()}.
+ \section3 Markup
- Do not use markup that spans the whole string because that can be confusing
+ Avoid including markup within the translated text itself because that can be confusing
for translators. For example, instead of:
\code
- tr("<html><head/><body><span>UI Text</span></body></html>")
+ Tr::tr("<html><head/><body><span>UI Text</span></body></html>")
\endcode
use
\code
- QLatin1String("<html><head/><body><span>") + tr("UI Text") + QLatin1String("/span></body></html>")
+ "<html><head/><body><span>" + Tr::tr("UI Text") + "</span></body></html>"
\endcode
\section2 Features of Languages or Writing Systems
diff --git a/doc/qtdesignstudio/config/style/qt5-sidebar.html b/doc/qtdesignstudio/config/style/qt5-sidebar.html
index 0a7c93702a..994ecd3db3 100644
--- a/doc/qtdesignstudio/config/style/qt5-sidebar.html
+++ b/doc/qtdesignstudio/config/style/qt5-sidebar.html
@@ -1,18 +1,109 @@
<div class="sectionlist normallist">
+ <ul>
+ <li><a href="qtdesignstudio-toc.html">View All Topics</a></li>
+ </ul>
+</div>
+<div class="sectionlist normallist">
<div class="heading">
- <a name="reference"></a>
- <h2 id="reference">Qt Design Studio Manual</h2>
+ <h2>Getting Started</h2>
</div>
<div class="indexboxcont indexboxbar">
<ul>
- <li><a href="qtdesignstudio-toc.html">All Topics</a></li>
- <li><a href="studio-getting-started.html">Getting Started</a></li>
- <li><a href="quick-uis.html">Wireframing</a></li>
- <li><a href="qtquick-prototyping.html">Prototyping</a></li>
- <li><a href="qtquick-motion-design.html">Motion Design</a></li>
- <li><a href="studio-implementing-applications.html">Implementing Applications</a></li>
- <li><a href="studio-advanced.html">Advanced Designer Topics</a></li>
- <li><a href="studio-developer-topics.html">Developer Topics</a></li>
- <li><a href="studio-help.html">Help</a></li>
+ <li><a href="studio-getting-started.html">Overview</a></li>
+ <li><a href="studio-installation.html">Installing Qt Design Studio</a></li>
+ <li><a href="gstutorials.html">Tutorials</a></li>
+ <li><a href="creator-quick-tour.html">User Interface</a></li>
+ <li><a href="studio-projects.html">Creating Projects</a></li>
+ <li><a href="studio-use-cases.html">Use Cases</a></li>
+ <li><a href="studio-terms.html">Concepts and Terms</a></li>
+ <li><a href="best-practices.html">Best Practices</a></li>
+ <li><a href="studioexamples.html">Examples</a></li>
</ul>
</div>
+</div>
+<div class="sectionlist normallist">
+ <div class="heading">
+ <h2>Wireframing</h2>
+ </div>
+ <div class="indexboxcont indexboxbar">
+ <ul>
+ <li><a href="quick-uis.html">Overview</a></li>
+ <li><a href="studio-app-flows.html">Designing Application Flows</a></li>
+ <li><a href="quick-components.html">Using Components</a></li>
+ <li><a href="qtquick-properties.html">Specifying Component Properties</a></li>
+ <li><a href="qtquick-positioning.html">Scalable Layouts</a></li>
+ <li><a href="qtquick-annotations.html">Annotating Designs</a></li>
+ </ul>
+ </div>
+</div>
+<div class="sectionlist normallist">
+ <div class="heading">
+ <h2>Prototyping</h2>
+ </div>
+ <ul>
+ <li><a href="qtquick-prototyping.html">Overview</a></li>
+ <li><a href="qtquick-creating-ui-logic.html">Creating UI Logic</a></li>
+ <li><a href="studio-simulation-overview.html">Simulating Complex Experiences</a></li>
+ <li><a href="qtquick-adding-dynamics.html">Dynamic Behaviors</a></li>
+ <li><a href="creator-live-preview.html">Validating with Target Hardware</a></li>
+ <li><a href="studio-exporting-and-importing.html">Asset Creation with Other Tools</a></li>
+ </ul>
+</div>
+<div class="sectionlist normallist">
+ <div class="heading">
+ <h2>Motion Design</h2>
+ </div>
+ <ul>
+ <li><a href="qtquick-motion-design.html">Overview</a></li>
+ <li><a href="quick-animation-overview.html">Introduction to Animation Techniques</a></li>
+ <li><a href="studio-timeline.html">Creating Timeline Animations</a></li>
+ <li><a href="qtquick-editing-easing-curves.html">Editing Easing Curves</a></li>
+ <li><a href="qtquick-production-quality-animation.html">Production Quality</a></li>
+ <li><a href="qtquick-optimizing-designs.html">Optimizing Designs</a></li>
+ </ul>
+</div>
+<div class="sectionlist normallist">
+ <div class="heading">
+ <h2>Implementing Applications</h2>
+ </div>
+ <ul>
+ <li><a href="studio-implementing-applications.html">Overview</a></li>
+ <li><a href="studio-designer-developer-workflow.html">Designer-Developer Workflow</a></li>
+ <li><a href="qtquick-text-editor.html">Coding</a></li>
+ <li><a href="studio-debugging.html">Debugging and Profiling</a></li>
+ </ul>
+</div>
+<div class="sectionlist normallist">
+ <div class="heading">
+ <h2>Advanced Designer Topics</h2>
+ </div>
+ <ul>
+ <li><a href="studio-advanced.html">Overview</a></li>
+ <li><a href="creator-quick-ui-forms.html">UI Files</a></li>
+ <li><a href="creator-telemetry.html">Manage Data Collection</a></li>
+ <li><a href="studio-packaging.html">Packaging Applications</a></li>
+ </ul>
+</div>
+<div class="sectionlist normallist">
+ <div class="heading">
+ <h2>Developer Topics</h2>
+ </div>
+ <ul>
+ <li><a href="studio-developer-topics.html">Overview</a></li>
+ <li><a href="creator-vcs-git.html">Using Git</a></li>
+ <li><a href="studio-porting-projects.html">Converting Qt 5 Projects into Qt 6 Projects</a></li>
+ <li><a href="quick-converting-ui-projects.html">Converting UI Projects to Applications</a></li>
+ <li><a href="creator-editor-external.html">Using External Tools</a></li>
+ <li><a href="studio-on-mcus.html">Qt Design Studio on MCUs</a></li>
+ </ul>
+</div>
+<div class="sectionlist normallist">
+ <div class="heading">
+ <h2>Help</h2>
+ </div>
+ <ul>
+ <li><a href="studio-help.html">Overview</a></li>
+ <li><a href="creator-how-to-get-help.html">Getting Help</a></li>
+ <li><a href="studio-platforms.html">Supported Platforms</a></li>
+ </ul>
+</div>
diff --git a/doc/qtdesignstudio/images/bleed-scale-1.webp b/doc/qtdesignstudio/images/bleed-scale-1.webp
new file mode 100644
index 0000000000..cfe9d7bd7e
--- /dev/null
+++ b/doc/qtdesignstudio/images/bleed-scale-1.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/bleed-scale-8.webp b/doc/qtdesignstudio/images/bleed-scale-8.webp
new file mode 100644
index 0000000000..3d4ef0b783
--- /dev/null
+++ b/doc/qtdesignstudio/images/bleed-scale-8.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/bleed-scale-no.webp b/doc/qtdesignstudio/images/bleed-scale-no.webp
new file mode 100644
index 0000000000..e102ae7501
--- /dev/null
+++ b/doc/qtdesignstudio/images/bleed-scale-no.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/ext-scene-env-navigator.webp b/doc/qtdesignstudio/images/ext-scene-env-navigator.webp
new file mode 100644
index 0000000000..675a9aedfc
--- /dev/null
+++ b/doc/qtdesignstudio/images/ext-scene-env-navigator.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/glow-additive-blend.webp b/doc/qtdesignstudio/images/glow-additive-blend.webp
new file mode 100644
index 0000000000..418628fc8b
--- /dev/null
+++ b/doc/qtdesignstudio/images/glow-additive-blend.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/glow-bicubic.webp b/doc/qtdesignstudio/images/glow-bicubic.webp
new file mode 100644
index 0000000000..216a248708
--- /dev/null
+++ b/doc/qtdesignstudio/images/glow-bicubic.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/glow-example-project.webp b/doc/qtdesignstudio/images/glow-example-project.webp
new file mode 100644
index 0000000000..2163fee2d9
--- /dev/null
+++ b/doc/qtdesignstudio/images/glow-example-project.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/glow-example.webp b/doc/qtdesignstudio/images/glow-example.webp
new file mode 100644
index 0000000000..4de776aa44
--- /dev/null
+++ b/doc/qtdesignstudio/images/glow-example.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/glow-high-quality.webp b/doc/qtdesignstudio/images/glow-high-quality.webp
new file mode 100644
index 0000000000..80434dde67
--- /dev/null
+++ b/doc/qtdesignstudio/images/glow-high-quality.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/glow-no-enhancment.webp b/doc/qtdesignstudio/images/glow-no-enhancment.webp
new file mode 100644
index 0000000000..87d90da35f
--- /dev/null
+++ b/doc/qtdesignstudio/images/glow-no-enhancment.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/glow-properties.webp b/doc/qtdesignstudio/images/glow-properties.webp
new file mode 100644
index 0000000000..b2d22736eb
--- /dev/null
+++ b/doc/qtdesignstudio/images/glow-properties.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/glow-replace-blend.webp b/doc/qtdesignstudio/images/glow-replace-blend.webp
new file mode 100644
index 0000000000..06c8e8c669
--- /dev/null
+++ b/doc/qtdesignstudio/images/glow-replace-blend.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/glow-screen-blend.webp b/doc/qtdesignstudio/images/glow-screen-blend.webp
new file mode 100644
index 0000000000..6a18367cb9
--- /dev/null
+++ b/doc/qtdesignstudio/images/glow-screen-blend.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/glow-softlight-blend.webp b/doc/qtdesignstudio/images/glow-softlight-blend.webp
new file mode 100644
index 0000000000..3ac9ef2640
--- /dev/null
+++ b/doc/qtdesignstudio/images/glow-softlight-blend.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/glow_all_blur_levels.webp b/doc/qtdesignstudio/images/glow_all_blur_levels.webp
new file mode 100644
index 0000000000..a5cc30d9cc
--- /dev/null
+++ b/doc/qtdesignstudio/images/glow_all_blur_levels.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/glow_high_blur_levels.webp b/doc/qtdesignstudio/images/glow_high_blur_levels.webp
new file mode 100644
index 0000000000..7cdf15a5c5
--- /dev/null
+++ b/doc/qtdesignstudio/images/glow_high_blur_levels.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/glow_low_blur_levels.webp b/doc/qtdesignstudio/images/glow_low_blur_levels.webp
new file mode 100644
index 0000000000..a59ed6cd7e
--- /dev/null
+++ b/doc/qtdesignstudio/images/glow_low_blur_levels.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtcreator-qt-design-studio-project.webp b/doc/qtdesignstudio/images/qtcreator-qt-design-studio-project.webp
new file mode 100644
index 0000000000..3e98d42ce2
--- /dev/null
+++ b/doc/qtdesignstudio/images/qtcreator-qt-design-studio-project.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-effects-background-blur.webp b/doc/qtdesignstudio/images/studio-effects-background-blur.webp
new file mode 100644
index 0000000000..b1fd217263
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-effects-background-blur.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-effects-drop-shadow.webp b/doc/qtdesignstudio/images/studio-effects-drop-shadow.webp
new file mode 100644
index 0000000000..7de04ead32
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-effects-drop-shadow.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-effects-inner-shadow.webp b/doc/qtdesignstudio/images/studio-effects-inner-shadow.webp
new file mode 100644
index 0000000000..3502903551
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-effects-inner-shadow.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-effects-layer-blur.webp b/doc/qtdesignstudio/images/studio-effects-layer-blur.webp
new file mode 100644
index 0000000000..32c2c66779
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-effects-layer-blur.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-effects-show-shadow-behind.webp b/doc/qtdesignstudio/images/studio-effects-show-shadow-behind.webp
new file mode 100644
index 0000000000..5b7411bd2c
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-effects-show-shadow-behind.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-effects.webp b/doc/qtdesignstudio/images/studio-effects.webp
new file mode 100644
index 0000000000..1910dc40af
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-effects.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-flow-decision-preview.png b/doc/qtdesignstudio/images/studio-flow-decision-preview.png
deleted file mode 100644
index c0c367d143..0000000000
--- a/doc/qtdesignstudio/images/studio-flow-decision-preview.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-flow-decision-preview.webp b/doc/qtdesignstudio/images/studio-flow-decision-preview.webp
new file mode 100644
index 0000000000..e3ac10b0b2
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-flow-decision-preview.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-flow-decision-properties.png b/doc/qtdesignstudio/images/studio-flow-decision-properties.png
deleted file mode 100644
index dbaba5338f..0000000000
--- a/doc/qtdesignstudio/images/studio-flow-decision-properties.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-flow-decision-properties.webp b/doc/qtdesignstudio/images/studio-flow-decision-properties.webp
new file mode 100644
index 0000000000..4029d5cd19
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-flow-decision-properties.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-flow-decision.png b/doc/qtdesignstudio/images/studio-flow-decision.png
deleted file mode 100644
index 0880a14559..0000000000
--- a/doc/qtdesignstudio/images/studio-flow-decision.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-flow-decision.webp b/doc/qtdesignstudio/images/studio-flow-decision.webp
new file mode 100644
index 0000000000..df7b18ab93
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-flow-decision.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-flow-events-assign.png b/doc/qtdesignstudio/images/studio-flow-events-assign.png
deleted file mode 100644
index c585dd8033..0000000000
--- a/doc/qtdesignstudio/images/studio-flow-events-assign.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-flow-events-assign.webp b/doc/qtdesignstudio/images/studio-flow-events-assign.webp
new file mode 100644
index 0000000000..53468aca42
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-flow-events-assign.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-flow-events-event-list.webp b/doc/qtdesignstudio/images/studio-flow-events-event-list.webp
new file mode 100644
index 0000000000..b33b940812
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-flow-events-event-list.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-flow-item-component.webp b/doc/qtdesignstudio/images/studio-flow-item-component.webp
new file mode 100644
index 0000000000..8c1ea982b1
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-flow-item-component.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-flow-states-item-properties.webp b/doc/qtdesignstudio/images/studio-flow-states-item-properties.webp
new file mode 100644
index 0000000000..14e024dab5
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-flow-states-item-properties.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-flow-transition-properties-question.png b/doc/qtdesignstudio/images/studio-flow-transition-properties-question.png
deleted file mode 100644
index 80cb7ad1a9..0000000000
--- a/doc/qtdesignstudio/images/studio-flow-transition-properties-question.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-flow-transition-properties-question.webp b/doc/qtdesignstudio/images/studio-flow-transition-properties-question.webp
new file mode 100644
index 0000000000..26250a17c1
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-flow-transition-properties-question.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-flow-view.png b/doc/qtdesignstudio/images/studio-flow-view.png
deleted file mode 100644
index 1eda708fcb..0000000000
--- a/doc/qtdesignstudio/images/studio-flow-view.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-flow-view.webp b/doc/qtdesignstudio/images/studio-flow-view.webp
new file mode 100644
index 0000000000..21db99f043
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-flow-view.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-flow-wildcard-properties.png b/doc/qtdesignstudio/images/studio-flow-wildcard-properties.png
deleted file mode 100644
index 944fd79431..0000000000
--- a/doc/qtdesignstudio/images/studio-flow-wildcard-properties.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-flow-wildcard-properties.webp b/doc/qtdesignstudio/images/studio-flow-wildcard-properties.webp
new file mode 100644
index 0000000000..66ade49942
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-flow-wildcard-properties.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-flow-wildcard.webp b/doc/qtdesignstudio/images/studio-flow-wildcard.webp
new file mode 100644
index 0000000000..8c0805cedc
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-flow-wildcard.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-project-cmake-generation.webp b/doc/qtdesignstudio/images/studio-project-cmake-generation.webp
new file mode 100644
index 0000000000..18058f785a
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-project-cmake-generation.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-project-export-advanced-options.webp b/doc/qtdesignstudio/images/studio-project-export-advanced-options.webp
deleted file mode 100644
index 33be8549a7..0000000000
--- a/doc/qtdesignstudio/images/studio-project-export-advanced-options.webp
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-project-export-advanced.webp b/doc/qtdesignstudio/images/studio-project-export-advanced.webp
deleted file mode 100644
index d6f1189093..0000000000
--- a/doc/qtdesignstudio/images/studio-project-export-advanced.webp
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-project-export.webp b/doc/qtdesignstudio/images/studio-project-export.webp
index 8847dd945f..b79ce329ee 100644
--- a/doc/qtdesignstudio/images/studio-project-export.webp
+++ b/doc/qtdesignstudio/images/studio-project-export.webp
Binary files differ
diff --git a/doc/qtdesignstudio/src/best practices/best-practices-glow.qdoc b/doc/qtdesignstudio/src/best practices/best-practices-glow.qdoc
new file mode 100644
index 0000000000..22a71ec681
--- /dev/null
+++ b/doc/qtdesignstudio/src/best practices/best-practices-glow.qdoc
@@ -0,0 +1,200 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \previouspage best-practices.html
+ \page best-practices-glow.html
+ \nextpage {Examples}
+
+ \title Creating Glow and Bloom Effects
+
+ In \QDS, you can add a glow and bloom effect to your 3D scene using the
+ \uicontrol ExtendedSceneEnvironment component (available in Qt 6.5 and later). With this effect,
+ you can, for example, create glow around illuminated areas (such as material or skyboxes when
+ using image-based lighting) or add ambient light. Using the glow effect is one way to make your
+ scene more realistic.
+
+ \image glow-example.webp
+
+ \section1 Creating a Project with ExtendedSceneEnvironment
+
+ To create a project with \uicontrol ExtendedSceneEnvironment as the default
+ scene environment, use the \uicontrol {3D Extended} project preset.
+
+ For more information about creating projects, see \l{Creating Projects}.
+
+ \section1 Adding ExtendedSceneEnvironment to an Existing Project
+
+ To add \uicontrol {ExtendedSceneEnvironment} to an existing project, drag the
+ \uicontrol {ExtendedSceneEnvironment} component from \uicontrol Components to
+ the \uicontrol 2D or \uicontrol Navigator view.
+
+ \image ext-scene-env-navigator.webp
+
+ \section1 Enabling the Glow Effect
+
+ To enable the glow effect, select \e SceneEnvironment in the \uicontrol Navigator view and
+ then, in the \uicontrol Properties view, select \uicontrol Enabled in the
+ \uicontrol Glow section.
+
+ \image glow-properties.webp
+
+ \note When setting up or experimenting with the glow effect, use the \l {Blend Modes}{Replace}
+ blend mode to see the effect more clearly.
+
+ \section1 The Flashlight Example Project
+
+ The flashlight example used in this documentation is available from the \uicontrol Examples
+ section of the \QDS \uicontrol Welcome page.
+
+ You can use the project to experiment with the \uicontrol Glow properties. When you run the
+ project, you can control most properties with UI controls. Another way to experiment with
+ properties is to change them directly in the \uicontrol Properties view in \QDS and see the
+ changes live in the \uicontrol 2D view.
+
+ \image glow-example-project.webp
+
+ \section1 Basic Properties
+
+ Usually, the best way to achieve a realistic glow effect in your 3D scene is to adjust
+ the \uicontrol {Strength}, \uicontrol {Intensity}, and \uicontrol {Bloom}
+ properties together.
+
+ \section2 Strength
+
+ Sets the strength of the glow. If this property has a 0 value, the glow effect is disabled. The
+ strength is a scale factor (multiplier) applied per level. This means that
+ with more levels enabled in \l {Blur Levels}, a larger \uicontrol {strength} value has
+ a more pronounced effect.
+
+ \section2 Intensity
+
+ Sets the intensity of the glow. The intensity is effectively a scale factor (multiplier) for the
+ accumulated glow color (including all levels).
+
+ \section2 Bloom
+
+ Sets the overall illumination of the whole scene.
+
+ \section2 Lower Threshold
+
+ Sets the minimum level of brightness for the glow.
+
+ \section2 Upper Threshold
+
+ Sets the maximum level of brightness for the glow.
+
+ \section2 HDR Scale
+
+ Sets how much the glow bleeds (or extends) beyond the borders of bright areas
+ in the scene. The range for this property is 0-8. As seen in the example images below, a high
+ value gives a very modest bleed, while a low number results in a much more visible bleed.
+
+ \table
+ \header
+ \li HDR Scale
+ \li Example
+ \row
+ \li Bloom disabled
+ \li \image bleed-scale-no.webp
+ \row
+ \li 8
+ \li \image bleed-scale-8.webp
+ \row
+ \li 1
+ \li \image bleed-scale-1.webp
+ \endtable
+
+ \section1 Blur Levels
+
+ Sets which of the blur passes to apply to the glow effect.
+
+ The higher the level is, the more the glow effect spreads around the light source,
+ and the softer the glow is.
+
+ As seen in the example image below, lower blur levels produce an intense glow within a limited
+ area, while higher blur levels produce a softer glow in a more extensive area. Combine blur
+ levels to get the desired result.
+
+ \table
+ \header
+ \li Blur level
+ \li Example
+ \row
+ \li 1, 2, 3
+ \li \image glow_low_blur_levels.webp
+ \row
+ \li 5, 6, 7
+ \li \image glow_high_blur_levels.webp
+ \row
+ \li 1, 2, 3, 4, 5, 6, 7
+ \li \image glow_all_blur_levels.webp
+ \endtable
+
+ \section2 Blend Modes
+
+ The following blend modes are available:
+
+ \table
+ \header
+ \li Blend mode
+ \li Description
+ \li Example
+ \row
+ \li Additive
+ \li Often recommended for outdoor scenes with a visible sky or sun.
+ \li \image glow-additive-blend.webp
+ \row
+ \li Screen
+ \li Similar to \uicontrol {Additive}, but the result is less bright.
+ \li \image glow-screen-blend.webp
+ \row
+ \li SoftLight
+ \li Often recommended for in-door environments.
+ \li \image glow-softlight-blend.webp
+ \row
+ \li Replace
+ \li Does not perform any blending, but displays only the contribution
+ the glow effect would blend with the actual content. In practice, this is useful for
+ experimenting and troubleshooting when setting up the glow effect.
+ \li \image glow-replace-blend.webp
+ \endtable
+
+ \section1 Improvement Properties
+
+ The \uicontrol{High Quality} and \uicontrol{Bicubical Upsampling}
+ properties improve the quality of the glow blur by upsampling. Using these properties
+ may slow down the performance of the application. You can also try using the
+ \uicontrol Dithering property in the \uicontrol sceneEnvironment instead. In some cases,
+ \uicontrol Dithering renders a similar result but at a lower cost.
+
+ \section2 High Quality
+
+ Increases the samples used for the glow when downsampling to improve the quality of the glow
+ effect.
+
+ \section2 Bicubical Upsampling
+
+ Reduces the aliasing artifacts and boxing in the glow effect.
+
+ \section2 Examples
+
+ These examples show a zoomed-in image of each effect:
+
+ \table
+ \header
+ \li Effect
+ \li Example
+ \row
+ \li No effect
+ \li \image glow-no-enhancment.webp
+ \row
+ \li High Quality
+ \li \image glow-high-quality.webp
+ \row
+ \li Bicubic Upsampling
+ \li \image glow-bicubic.webp
+ \endtable
+
+
+*/
diff --git a/doc/qtdesignstudio/src/best-practices.qdoc b/doc/qtdesignstudio/src/best-practices.qdoc
new file mode 100644
index 0000000000..4abcc80f6a
--- /dev/null
+++ b/doc/qtdesignstudio/src/best-practices.qdoc
@@ -0,0 +1,38 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \previouspage studio-terms.html
+ \page best-practices.html
+ \nextpage best-practices-glow.html
+
+ \title Best Practices
+
+ \section1 Graphics
+
+ \list
+ \li \l {Creating Glow and Bloom Effects}
+ \endlist
+
+ \section1 Performance
+
+ \list
+ \li \l {Optimizing Designs}
+ \li \l {QML Performance Considerations And Suggestions}
+ \li \l {Creating Optimized 3D Scenes}
+ \li \l {Using Components Economically}
+ \endlist
+
+ \section1 Projects
+
+ \list
+ \li \l {Converting Qt 5 Projects into Qt 6 Projects}
+ \li \l {Converting UI Projects to Applications}
+ \endlist
+
+ \section1 Workflow
+
+ \list
+ \li \l {Designer-Developer Workflow}
+ \endlist
+*/
diff --git a/doc/qtdesignstudio/src/components/qtquick-images.qdoc b/doc/qtdesignstudio/src/components/qtquick-images.qdoc
index 176e4409f8..cbece71a4b 100644
--- a/doc/qtdesignstudio/src/components/qtquick-images.qdoc
+++ b/doc/qtdesignstudio/src/components/qtquick-images.qdoc
@@ -44,6 +44,9 @@
If you need to display animated images, such as GIFs, use the
\l {Animated Image} component.
+ \note Currently, the supported image formats include JPEG, JPG, PNG, SVG,
+ HDR, KTX, BMP, TTF, TIFF, WEBP, and GIF.
+
\section1 Image Size
\image qtquick-designer-image-properties.png "Image properties"
diff --git a/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc b/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc
index 4920cf582d..267517cb6c 100644
--- a/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc
+++ b/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc
@@ -31,6 +31,7 @@
\li \l {UI Controls}
\li \l {Lists and Other Data Models}
\li \l {2D Effects}
+ \li \l {Design Effects}
\li \l {Logic Helpers}
\li \l Animations
\endlist
diff --git a/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc b/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc
index e8c6c8e329..e214b7709e 100644
--- a/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc
+++ b/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
@@ -19,10 +19,6 @@
other files that are needed to implement the application logic and to
prepare the application for production.
- Use the \l{Using Git}{Git} version control system to ensure that changes
- are not lost when files are passed back and forth between designers and
- developers.
-
\QDS \l{Creating Projects}{projects} come with boilerplate code for a
working Qt 6 application that you can build and run in Qt Creator using
CMake. Therefore, you can open, build, and run the projects with Qt Creator.
@@ -31,58 +27,53 @@
\e CMakeLists.txt file as the project file. This enables you to share
your project as a fully working C++ application with developers.
- If you use Git, you can clone an example project
- \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/playground/AuroraCluster0}
- {here}.
-
- \section1 Exporting a \QDS Project
-
- \QDS uses a different project format than Qt Creator. \QDS does not build the project,
- it uses a pre-compiled \l{QML runtime} to run the project. To export a \QDS project for the
- Qt Creator, follow the process:
+ To export a \QDS project for Qt Creator, you need:
- \list 1
- \li Open the project you want to export in \QDS.
- \li Select \uicontrol {File} > \uicontrol {Export Project} > \uicontrol {Generate CMake Build Files}.
- \image studio-project-export.webp "Export the \QDS project for Qt Creator"
+ \list
+ \li Qt Creator 13.0 or above.
+ \li \QDS 4.5 or above.
+ \endlist
- \li Select \uicontrol {Details} to access the \uicontrol {Advanced Options}.
- \image studio-project-export-advanced.webp "Access Advanced Options in the project exporter"
+ \section1 Exporting a \QDS Project
- \note The project exporter has default settings selected. This works better if the project
- is combined with an existing Qt project.
+ \list 1
+ \li \l {Creating a Project} {Create} or open your \QDS project with \QDS 4.5 or above.
- \li Select all the options here. This allows to export the
- complete project. So, it can be compiled as a stand-alone application.
- \image studio-project-export-advanced-options.webp "Select all the options in the project exporter"
+ \note If you are creating a new project in \QDS, select the
+ \uicontrol {Target Qt Version} that is not higher than the Qt version
+ used in your Qt Creator.
- \note If you copy this export on top of the existing Qt Creator project
- it overwrites the existing project. Hence, the default selected options in
- the exporter only exports the QML-specific items. You get a list of
- warnings at the bottom part of the exporter that denotes exactly which parts
- of the project gets overwritten.
- \endlist
+ \li Go to \uicontrol {File} > \uicontrol {Export Project}
+ > \uicontrol {Enable Automatic CMake Generation}. This creates a
+ \e {CMakeLists.txt} file in your project folder.
- \section1 Using the Exported Project in Qt Creator
+ \note Enabling this option tracks the changes made to the project in Qt Creator
+ and automatically updates in \QDS. The connection works unless you
+ deactivate the option.
- After exporting the project from the \QDS, you have to open it from Qt Creator.
+ \image studio-project-export.webp "Exporting Qt Design Studio project"
+ \endlist
- If you have used any version before \QDS 4.0 to create the project, manually include this code
- in the \e {CMakeLists.txt} file so the exported project works in Qt Creator.
+ \section1 Opening the \QDS Project in Qt Creator
- \code
- set(BUILD_QDS_COMPONENTS ON CACHE BOOL "Build design studio components")
+ Open the \e {CMakeLists.txt} file in Qt Creator. To open:
- set(CMAKE_INCLUDE_CURRENT_DIR ON)
+ \list 1
+ \li In Qt Creator, select \uicontrol File > \uicontrol {Open File or Project}.
+ \li Browse through your project directory and select the \e {CMakeLists.txt}.
+ Then select \uicontrol Open.
- if (${BUILD_QDS_COMPONENTS})
- include(${CMAKE_CURRENT_SOURCE_DIR}/qmlcomponents)
- endif ()
+ \image studio-project-cmake-generation.webp "Project folder after CMake generation"
- include(${CMAKE_CURRENT_SOURCE_DIR}/qmlmodules)
- \endcode
+ \li Select the Qt version and then \uicontrol {Configure Project}.
- \note If you have created the project with the \QDS version 4.0 or above, you already have this code in
- \e {CMakeLists.txt} by default.
+ \note If your \QDS project was created with a more updated Qt than the one
+ available in Qt Creator, the import doesn't work. Use
+ \l {Get and Install Qt with Qt Online Installer} {Qt Online Installer}
+ to install the latest Qt version. If successfully opened, all the files are
+ accessible in the \uicontrol Projects view.
+ \image qtcreator-qt-design-studio-project.webp "Qt Design studio projects in Qt Creator after successful import"
+ \li To run the project, select \uicontrol Run.
+ \endlist
*/
diff --git a/doc/qtdesignstudio/src/how-to/qtdesignstudio-live-preview-desktop.qdoc b/doc/qtdesignstudio/src/how-to/qtdesignstudio-live-preview-desktop.qdoc
index 5145bc2397..0178d7f666 100644
--- a/doc/qtdesignstudio/src/how-to/qtdesignstudio-live-preview-desktop.qdoc
+++ b/doc/qtdesignstudio/src/how-to/qtdesignstudio-live-preview-desktop.qdoc
@@ -24,9 +24,12 @@
\uicontrol {Preview File}.
\endlist
- To preview the whole UI, select \uicontrol {Live Preview}
+ To preview the whole application, select \uicontrol {Live Preview}
when viewing the main QML file of the project.
+ To preview the application in a different zoom level, right-click the \uicontrol {Live Preview}
+ button and select the zoom level.
+
\section1 Overriding the Preview Tool
By default, the QML runtime is used for previewing.
diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc
index b721acfa9e..798ca9551c 100644
--- a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc
+++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc
@@ -1,4 +1,4 @@
-// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
// Note: The \page value is hard-coded as a link in Qt Bridge for Figma.
@@ -29,5 +29,9 @@
To get the best results when you use \QBF to export designs from
Figma, you should follow the guidelines for working with Figma and
organizing your assets.
+
\endlist
+
+ \include qtdesignstudio-qt-academy.qdocinc qt-academy-using-qt-bridge-for-figma
+
*/
diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc
index 33d130f336..00e9586df2 100644
--- a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc
+++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc
@@ -1,4 +1,4 @@
-// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
@@ -25,4 +25,6 @@
\endlist
You can launch the installed Figma plugin from \uicontrol Plugins > \uicontrol {\QBF} in Figma.
+
+ \include qtdesignstudio-qt-academy.qdocinc qt-academy-using-qt-bridge-for-figma
*/
diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc
index b189500f49..22cc72105e 100644
--- a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc
+++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
@@ -247,4 +247,6 @@
\uicontrol Home tab) to default values. This means that you
will lose all your changes to the settings.
\endtable
+
+ \include qtdesignstudio-qt-academy.qdocinc qt-academy-using-qt-bridge-for-figma
*/
diff --git a/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc
index 5e4a503800..1be7f14311 100644
--- a/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc
+++ b/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc
@@ -8,25 +8,25 @@
\title Designing Application Flows
- \image studio-flow-view.png "Application flow in the 2D view"
+ \image studio-flow-view.webp "Application flow in the 2D view"
In \QDS, a \e {flow view} represents a schematic diagram. It consists of
\e {flow items} that represent the screens in the UI and \e {transition
lines} that connect them, thus illustrating the possible user pathways
- through the UI. You use \e {action areas} as starting points for transition
- lines. You can attach effects to transition lines, such as fade or push,
+ through the UI. \e {Action areas} are clickable starting points for transition
+ lines. Attach effects to transition lines, such as fade or push,
to determine what users see when one flow item changes into another.
- You can use \e {flow decisions} to set up alternative pathways between
- flow items in the UI. For example, if user input determines which flow item
- should open next, you can test the different scenarios in the prototype
- by having a dialog pop up where you can select which flow item to show next.
+ Use \e {flow decisions} to set up alternative pathways between flow items in the UI. For
+ example, if user input determines which flow item should open next, test the different
+ scenarios in the prototype with the decision dialog where you can select which flow item to
+ show next.
Especially on mobile and embedded platforms, the application might need to
react to external events from the platform, such as notifications or other
- applications requiring the users' attention. You can use \e {flow wildcards}
- to determine the priority of flow items by adding them to positive and
- negative lists.
+ applications requiring the users' attention. Use \e {flow wildcards}
+ to determine the priority of flow items by adding them to allow and
+ block lists.
To design application flows:
@@ -40,14 +40,17 @@
\l{Adding Flow Items}.
\li Use context menu commands to add action areas and transitions,
as described in \l{Adding Action Areas and Transitions}.
+ \endlist
+
+ Additionally, to create a more advanced application flow:
+
+ \list
\li Use context menu commands to apply effects to transitions,
as described in \l{Applying Effects to Transitions}.
- \li When you are ready for production, use the event list simulator
- to replace transition lines with connections to real signals
- from UI controls, as described in \l{Simulating Events}.
- \li To set up alternative pathways between flow items, use
- \uicontrol {Flow Decision} components from
- \uicontrol Components > \uicontrol {Flow View}, as described in
+ \li Use the event list simulator to replace transition lines with connections to real
+ signals from UI controls, as described in \l{Simulating Events}.
+ \li Use \uicontrol {Flow Decision} components from \uicontrol Components > \uicontrol {Flow
+ View} to set up alternative pathways between flow items, as described in
\l{Simulating Conditions}.
\li Use \l{Working with States}{states} in flows to modify the appearance
of components on screens in response to user interaction, as
@@ -256,8 +259,9 @@
\li Drag the action area to the UI control that you want to connect
to the other flow item. For example, to a button that opens another
flow item when clicked.
- \li Double-click the action area and drag the transition line to the
- flow item you want to connect to.
+ \li Double-click the action area and drag the transition line to the flow item you want to
+ connect to. Alternatively, select the action area, right-click it to open the
+ context-menu, and then select \uicontrol Connect and the flow item from the list.
\li In \l Properties, modify the properties of the action area
and transition line.
\endlist
@@ -376,13 +380,13 @@
\title Applying Effects to Transitions
- You can apply effects, such as fade, move, or push to
+ You can apply effects, such as fade, move, or push, to
\l{Adding Action Areas and Transitions}{transitions} between
\l{Adding Flow Items}{flow items}. A fade effect makes the first
flow item appear to fade out, while the next flow item fades in.
A move effect makes the second flow item appear to move in over the
- first flow item, while the push effect appears to make a flow item
- push out the previous one. You can also design and use custom effects.
+ first flow item. The push effect makes a flow item appear to push out the previous one.
+ You can also use your own custom effects that you have created with some other tool.
The transition direction determines the direction the new flow item appears
from: left, right, top, bottom. You can set the duration of the effect and
@@ -392,34 +396,42 @@
\list 1
\li Select a transition line in the \l {2D} view.
- \li In the context menu, select \uicontrol {Flow} >
- \uicontrol {Assign Flow Effects}, and then select the effect
- to apply.
+ \li Right-click the transition line to open the context menu, select \uicontrol {Flow} >
+ \uicontrol {Flow Effects}, and then select the effect to apply.
\li In \l Properties, modify the properties of the effect.
\endlist
- To edit effect properties later, select a transition, and then select
- \uicontrol {Flow} > \uicontrol {Select Effect} in the context menu.
+ To edit effect properties later, select a transition, open the context menu, and then select
+ \uicontrol {Flow} > \uicontrol {Select Effect}.
+
+ To use your own custom effects, select a transition, open the context menu, and then select
+ \uicontrol {Flow} > \uicontrol {Flow Effects} > \uicontrol {Assign Custom FlowEffect}.
+ Then specify the path to your custom effect file.
+
+ To remove the current effect from a transition, select a transition, open the context menu,
+ and then select \uicontrol {Flow} > \uicontrol {Flow Effects} >
+ \uicontrol {Assign FlowEffect None}.
\section1 Flow Effect Properties
- You can specify basic properties for a \uicontrol {Flow Effect}
- component in the \l Type and \l ID fields in the \uicontrol Component
- section in the \uicontrol Properties view.
+ Specify basic properties for a \uicontrol {Flow Effect} component in the \l Type and
+ \l ID fields in the \uicontrol Component section in the \uicontrol Properties view.
\image studio-flow-effect-properties.png "Flow Effect properties"
- You can set the duration and easing curve of all flow effects:
+ Set the duration and easing curve of flow effects in the \uicontrol {Transition Effect}
+ section:
\list
\li In the \uicontrol Duration field, specify the duration of the
effect.
\li Select the \inlineimage icons/curve_editor.png
- button to open \uicontrol {Easing Curve Editor} for attaching an
+ button to open \uicontrol {Easing Curve Editor} to attach an
\l{Editing Easing Curves}{easing curve} to the effect.
\endlist
- For a move or push effect, you can set some additional properties:
+ Set some additional properties for a move or push effect in the \uicontrol {Push Effect}
+ or \uicontrol {Move Effect} section:
\image studio-flow-effect-push-properties.png "Flow Push Effect properties"
@@ -431,7 +443,7 @@
\li In the \uicontrol {Incoming opacity} and
\uicontrol {Outgoing opacity} fields, specify the opacity of
the effect as a number between 0 and 1.
- \li Select the \uicontrol Reveal check box to reveal the
+ \li Select the \uicontrol Reveal checkbox to reveal the
\uicontrol {Flow Item} where the transition starts.
\endlist
*/
@@ -453,7 +465,7 @@
keyboard shortcuts to simulate these events when you preview the UI.
When you use the wizard to create a \uicontrol {Flow View} component, select
- the \uicontrol {Use event simulator} check box to add an event simulator to
+ the \uicontrol {Use event simulator} checkbox to add an event simulator to
the flow view.
You can create an event list where you assign keyboard shortcuts to events,
@@ -470,9 +482,8 @@
\li In the \uicontrol {Event List} dialog, select \inlineimage icons/plus.png
to add a keyboard shortcut for triggering an event to the list.
\image studio-flow-event-list.png "Event List dialog"
- \li In the \uicontrol {Event ID} field, enter an identifier for the
- event. You can search for existing events by entering search
- criteria in the \uicontrol Filter field.
+ \li In the \uicontrol {Event ID} field, enter an identifier for the event. To search
+ for existing events, enter search criteria in the \uicontrol Filter field.
\li In the \uicontrol Description field, describe the keyboard shortcut.
\li In the \uicontrol Shortcut field, press the keyboard key that will
trigger the event, and then select \uicontrol R to record the
@@ -486,24 +497,22 @@
To assign events to actions:
\list 1
+ \li Select \uicontrol eventListSimulator in \uicontrol Navigator, and in
+ \uicontrol Properties > \uicontrol {Exposed Custom Properties}, select the
+ \uicontrol active checkbox. If \uicontrol eventListSimulator is not visible
+ in the \uicontrol Navigator, select \inlineimage icons/visibilityon.png.
\li In \uicontrol Navigator, select an action area or transition line.
\li In the context menu, select \uicontrol {Event List} >
\uicontrol {Assign Events to Actions}.
- \image studio-flow-events-assign.png "Assign Events to Actions dialog"
- \li In the \uicontrol ID field, select a transition or an action area
- \inlineimage icons/flow-action-icon.png
- . You can search for events by entering search criteria in the
- \uicontrol Filter field.
+ \image studio-flow-events-assign.webp "Assign Events to Actions dialog"
\li To connect an event, select \uicontrol Connect next to an event in
the list. To release a connected event, select \uicontrol Release.
\li Press \key {Alt+P} to preview the UI.
\li Select action areas in the preview, double-click events in the
event list, or use the keyboard shortcuts to trigger events.
- \image studio-flow-decision-preview.png "Event list in preview"
+ \image studio-flow-events-event-list.webp "Event list in Live Preview"
\endlist
- If the event triggers a \l{Simulating Conditions}{flow decision}, you
- can select the path to take to the next flow item.
*/
/*!
@@ -525,17 +534,17 @@
controls, backend, or sensor data that will be required for the production
version.
- \image studio-flow-decision.png "Flow Decision in the 2D view"
+ \image studio-flow-decision.webp "Flow Decision in the 2D view"
To simulate conditions:
\list 1
\li Drag a \uicontrol {Flow Decision} component from
- \uicontrol Components \uicontrol {Flow View} to a
+ \uicontrol Components > \uicontrol {Flow View} to a
\l{Adding Flow Views}{flow view} in the \l Navigator or \l {2D} view.
\li Select the flow item where you want the application to start in
- the \uicontrol Navigator or \uicontrol {2D} view, and then select
- \uicontrol {Flow} > \uicontrol {Set Flow Start} in the context menu.
+ the \uicontrol Navigator or \uicontrol {2D} view. Then right-click the component
+ to open the context menu, and select \uicontrol Flow > \uicontrol {Set Flow Start}.
\li Create an \l{Adding Action Areas and Transitions}{action area} for
the component that will trigger the condition and connect it to the
flow decision.
@@ -546,10 +555,10 @@
title for the selection dialog that opens when the condition is
triggered.
\li Select a transition line in the \uicontrol Navigator or
- \uicontrol {2D} view and add a descriptive text in the
+ \uicontrol {2D} view, and add a descriptive text in the
\uicontrol {Question} field in \uicontrol Properties to represent
a choice in the selection dialog.
- \image studio-flow-transition-properties-question.png "Flow Transition properties"
+ \image studio-flow-transition-properties-question.webp "Flow Transition properties"
\li Press \key {Alt+P} to preview the UI.
\li Select action areas in the preview, double-click events in the
event list, or use the keyboard shortcuts to trigger events.
@@ -558,21 +567,21 @@
Flow decisions are listed in a dialog where you can select which condition
is met to see the results.
- \image studio-flow-decision-preview.png "Selection dialog for flow decision"
+ \image studio-flow-decision-preview.webp "Selection dialog for flow decision"
\section1 Flow Decision Properties
- You can specify basic properties for a \uicontrol {Flow Decision} component
+ Specify basic properties for a \uicontrol {Flow Decision} component
in the \l Type and \l ID fields in the \uicontrol Component section in the
\uicontrol Properties view. Specify properties for flow decisions in the
\uicontrol {Flow Decision} section.
- \image studio-flow-decision-properties.png "Flow Decision properties"
+ \image studio-flow-decision-properties.webp "Flow Decision properties"
In the \uicontrol {Dialog title} field, enter a title for the selection
dialog that opens when the condition is triggered.
- You can specify the following properties to change the appearance of the
+ Specify the following properties to change the appearance of the
flow decision icon \inlineimage icons/flow-decision-icon.png
:
@@ -582,14 +591,14 @@
component in the \l {2D} view.
\li In the \uicontrol {Label position} field, select the corner of
the flow decision icon to place the label in.
+ \li Use the \l{Picking Colors}{color picker} to set \uicontrol {Outline color} and
+ \uicontrol {Fill color} of the flow decision icon.
\li In the \uicontrol Size field, specify the size of the flow
decision icon.
\li In the \uicontrol Radius field, specify the radius of the flow
decision icon corners.
\endlist
- You can use the \l{Picking Colors}{color picker} to set the outline and
- fill color of the flow decision icon.
*/
/*!
@@ -599,32 +608,34 @@
\title Applying States in Flows
- You can use \l{Working with States}{states} in flows to modify the appearance
- of \l{glossary-component}{components} in flow items in response to user
- interaction, for example. For this purpose, you use the
- \uicontrol {Flow Item} components available in
- \uicontrol Components > \uicontrol {Flow View}.
+ Use \l{Working with States}{states} in flows to modify how the \l{glossary-component}
+ {component} properties change in flow items. For this purpose, use the \uicontrol {Flow Item}
+ component available in \uicontrol Components > \uicontrol {Flow View}.
+
+ To apply states in flows:
\list 1
\li Select \uicontrol File > \uicontrol {New File} >
\uicontrol {Qt Quick Files} >
\uicontrol {Flow Item} to create a flow item.
- \li In \l States, add states to the flow item.
- \li Open the .ui.qml file that contains the \l{Adding Flow Views}
- {flow view} in the \l {2D} view and drag the flow item to the
- flow view in the \l Navigator or \l {2D} view.
- \li Drag an empty \uicontrol {Flow Item} component from
- \uicontrol Components > \uicontrol {Flow View}
- to the flow for each state that you added.
- \li In \l Properties, in the \uicontrol {State change target}
- field, select the flow item that you created using the wizard.
- \image studio-flow-item-properties.png "Flow Item properties"
- \li In the \uicontrol {Target state} field, select the state to
- apply to the flow item.
+ \li In the \l States view, add states to the flow item.
+ \li In the \l Projects view, open the .ui.qml file that contains the \l{Adding Flow Views}
+ {flow view}, and drag the flow item from \uicontrol Components >
+ \uicontrol {My Components} to the flow view in the \l Navigator or \l 2D view.
+ \li From \uicontrol Components > \uicontrol {Flow View}, drag an empty
+ \uicontrol {Flow Item} component (1) to the flow view for each state that you added.
+ \image studio-flow-item-component.webp "Flow Item in the Components view."
+ \li In the \uicontrol Navigator or \uicontrol 2D view, select an empty
+ \uicontrol {Flow Item} to open the \l Properties view.
+ \li In the \uicontrol {State change target} field, select the flow item that you created
+ using the wizard.
+ \li In the \uicontrol {Target state} field, select the state to apply to the flow item.
+ \image studio-flow-states-item-properties.webp "Flow Item properties"
\endlist
- You can now add \l{Adding Action Areas and Transitions}{action areas} and
- \l{Simulating Conditions}{flow decisions} to apply the different states.
+ To apply the different states to your application flow, add
+ \l{Adding Action Areas and Transitions}{action areas} and
+ \l{Simulating Conditions}{flow decisions} to the flow items.
*/
/*!
@@ -639,11 +650,10 @@
time, based on a conditional event. For example, push notifications
appear on mobile devices and incoming call screens on a car's HMI.
- You can use the \uicontrol {Flow Wildcard} component to model this type of
- screens in your \l{Adding Flow Views}{flow view} using real or simulated
- \l{Connecting and Releasing Signals}{signals} and \l{Simulating Conditions}
- {conditions}. You can add \l{Adding Flow Items}{flow items} to a positive
- list to prioritize them or to a negative list to stop some screens from
+ Use the \uicontrol {Flow Wildcard} component to model this type of
+ screens in your \l{Adding Flow Views}{flow view} using real or simulated events.
+ Add \l{Adding Flow Items}{flow items} to an \uicontrol {Allow
+ list} to prioritize them or to a \uicontrol {Block list} to stop some screens from
appearing on others. For example, you could block the incoming call screen
from appearing on a warning screen for the engine if you consider the
warning more important.
@@ -652,42 +662,46 @@
\list 1
\li Drag a \uicontrol {Flow Wildcard} component from
- \uicontrol Components > \uicontrol {Flow View} to an
- \l{Adding Action Areas and Transitions}{action area} in
- the \l Navigator or \l {2D} view.
- \li In \l Properties, select flow items to add them to the
- positive and negative list of the action area.
+ \uicontrol Components > \uicontrol {Flow View} to the \l {2D} view.
+ \li To connect the wildcard to a flow item with a \l{Adding Action Areas and Transitions}
+ {transition line}, double-click the wildcard and drag the transition line to the flow
+ item.
+ \li To add logic to the \uicontrol {Flow Wildcard} component, use
+ \l{Simulating Events}{events}, \l{Simulating Conditions}{Flow Decision} components,
+ or \l{Connecting and Releasing Signals}{signals}.
+ \li To manage the priority of your flow items, add flow items to
+ \uicontrol {Allow list} or \uicontrol {Block list} in \l Properties.
+ \image studio-flow-wildcard.webp "The wildcard component in 2D view."
\endlist
\section1 Flow Wildcard Properties
- You can specify basic properties for a \uicontrol {Flow Wildcard} component
+ Specify basic properties for a \uicontrol {Flow Wildcard} component
in the \l Type and \l ID fields in the \uicontrol Component section in the
\uicontrol Properties view. Specify properties for flow wildcards in the
\uicontrol {Flow Wildcard} section.
- \image studio-flow-wildcard-properties.png "Flow Wildcard properties"
+ \image studio-flow-wildcard-properties.webp "Flow Wildcard properties"
In the \uicontrol {Event IDs} field, specify the IDs of the events to
connect to, such as mouse, touch or keyboard events.
- Select the \uicontrol {Global wildcard} check box to enable triggering
+ Select the \uicontrol {Global wildcard} checkbox to enable triggering
the wildcard from several flows.
To give flow items high priority, select them in the
\uicontrol {Allow list} field. To block flow items,
select them in the \uicontrol {Block list} field.
- You can specify the following properties to change the appearance of the
- wildcard icon \inlineimage icons/flow-wildcard-icon.png
- :
+ Specify the following properties to change the appearance of the
+ wildcard icon \inlineimage icons/flow-wildcard-icon.png:
\list
+ \li To set the outline and fill color of the wildcard icon, use the
+ \l{Picking Colors}{color picker}.
\li In the \uicontrol Size field, specify the size of the wildcard icon.
\li In the \uicontrol Radius field, specify the radius of the wildcard
icon corners.
\endlist
- You can use the \l{Picking Colors}{color picker} to set the outline and
- fill color of the wildcard icon.
*/
diff --git a/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc
index 493242254f..a975dfe7fc 100644
--- a/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc
+++ b/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
@@ -56,4 +56,6 @@
If you would rather be shown things than read about them,
you can watch our extensive video tutorials.
\endlist
+
+ \include qtdesignstudio-qt-academy.qdocinc qt-academy-getting-started-with-qt-design-studio
*/
diff --git a/doc/qtdesignstudio/src/qtdesignstudio-qt-academy.qdocinc b/doc/qtdesignstudio/src/qtdesignstudio-qt-academy.qdocinc
new file mode 100644
index 0000000000..3769ddc5e3
--- /dev/null
+++ b/doc/qtdesignstudio/src/qtdesignstudio-qt-academy.qdocinc
@@ -0,0 +1,22 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+//! [qt-academy-using-qt-bridge-for-figma]
+ To learn the basics of \QBF, take the
+ \l {https://www.qt.io/academy/course-catalog#how-to-use-qt-bridge-for-figma}
+ {How to Use \QBF} course in \QAC.
+//! [qt-academy-using-qt-bridge-for-figma]
+
+//! [qt-academy-3D-with-qt-design-studio]
+ To learn the basics of using 3D in \QDS, take the
+ \l {https://www.qt.io/academy/course-catalog#3d-with-qt-design-studio}
+ {3D with \QDS} course in \QAC.
+//! [qt-academy-3D-with-qt-design-studio]
+
+//! [qt-academy-getting-started-with-qt-design-studio]
+ To learn the basics of using \QDS, take the
+ \l {https://www.qt.io/academy/course-catalog#getting-started-with-qt-design-studio}
+ {Getting Started with \QDS} course in \QAC.
+//! [qt-academy-getting-started-with-qt-design-studio]
+
+*/
diff --git a/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc
index 70ff56517c..00464f7447 100644
--- a/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc
+++ b/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc
@@ -4,7 +4,7 @@
/*!
\page studio-terms.html
\previouspage studio-use-cases.html
- \nextpage {Examples}
+ \nextpage best-practices.html
\title Concepts and Terms
@@ -170,7 +170,7 @@
example, if you create a 3D project, preset 3D components are added to it.
You can add more preset components in \uicontrol {Components}.
- \image studio-project-wizards.png "New Project dialog"
+ \image studio-project-wizards.webp "New Project dialog"
Read more about projects:
diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc
index 659e446bb3..6b3680de54 100644
--- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc
+++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc
@@ -48,6 +48,7 @@
\li \l{Creating Projects}
\li \l{Use Cases}
\li \l{Concepts and Terms}
+ \li \l{Best Practices}
\li \l{Examples}
\endlist
\li \l{Wireframing}
@@ -74,8 +75,9 @@
\li \l{UI Controls}
\li \l{Lists and Other Data Models}
\li \l{2D Effects}
+ \li \l{Design Effects}
\li \l{Logic Helpers}
- \li \l Animations
+ \li \l{Animations}
\li \l{3D Views}
\li \l{Node}
\li \l{Group}
diff --git a/doc/qtdesignstudio/src/qtdesignstudio.qdoc b/doc/qtdesignstudio/src/qtdesignstudio.qdoc
index 6aab55a1ad..d3a0b007d1 100644
--- a/doc/qtdesignstudio/src/qtdesignstudio.qdoc
+++ b/doc/qtdesignstudio/src/qtdesignstudio.qdoc
@@ -31,6 +31,7 @@
\li \l{Creating Projects}
\li \l{Use Cases}
\li \l{Concepts and Terms}
+ \li \l{Best Practices}
\li \l{Examples}
\endlist
\li \b {\l{Wireframing}}
diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc
index ebe842a555..ff67fa14a8 100644
--- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc
+++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc
@@ -13,54 +13,28 @@
\brief Edit a 3D scene.
When editing a 3D scene, you view the scene in the \uicontrol{3D}
- view. You can change the projection of the view by switching between
- \e {perspective camera} and \e {orthographic camera} modes. When using the
- perspective camera mode, components that are far from the camera appear
- smaller than those nearby. In the orthographic camera mode, all components
- appear at the same scale irrespective of their distance from the camera.
- Both of them are free-form camera modes that you can use to orbit around
- the scene.
+ view.
When you import 3D scenes from files that you exported from 3D graphics
tools, you also import a \l{Cameras}{scene camera},
- \l{Lights}{light}, \l{3D Models}{model}, and
- \l {Materials and Shaders}{materials}. If your scene did not contain
- them, you can add the corresponding \l {3D Components}{Qt Quick 3D}
+ \l{Lights}{light}, \l{3D Models}{model}, and \l {Materials and Shaders}{materials}.
+ If your scene does not contain them, add the corresponding \l {3D Components}{Qt Quick 3D}
components from \uicontrol Components > \inlineimage icons/plus.png
> \uicontrol {Qt Quick 3D} > \uicontrol {Qt Quick 3D}.
- You can use the toolbar buttons
- to \e transform 3D components and manipulate the 3D scene. Transformation
- refers to moving, rotating, or scaling of a component. The \e pivot of the
- component is used as the origin for transformations. You can set a
- \l{Managing 3D Transformations}{local pivot offset} for a component in
- \uicontrol Properties to transform the component around a point other than
- its local origin. A line is drawn in the \uicontrol{3D} view from the pivot
- point to the center of the component to provide a visual connection between
- them. Especially when working with complex scenes, it may be useful to use
- the \l {Showing and Hiding Components}{showing and hiding} or the
- \l {Locking Components}{locking} features in \l Navigator to avoid
- transforming components by mistake while editing your scene.
-
- Toggle between local and global orientation to determine whether the gizmos
- affect only the local transformations of the component or whether they
- transform with respect to the global space.
-
- Another helpful feature when editing 3D scenes is the \e {edit light},
- which is a quick way to light the scene.
-
- Additionally, you can toggle the visibility of the grid, selection boxes,
- icon gizmos, and camera frustums in the 3D scene.
+ Use the \uicontrol 3D toolbar buttons to modify the \uicontrol 3D view,
+ manipulate the 3D scene, and to access functionalities to \e transform 3D
+ components. Transformation refers to moving, rotating, or scaling a
+ component. The \e pivot of the component is used as the origin for
+ transformations. Set a \l{Managing 3D Transformations}{local pivot offset}
+ for a component in \uicontrol Properties to transform the component around
+ a point other than its local origin. A line is drawn in the \uicontrol{3D}
+ view from the pivot point to the center of the component to provide a visual
+ connection between them.
- There is a context menu in the \uicontrol 3D view. To open it, right-click
- in the \uicontrol 3D view. From the context menu you can:
- \list
- \li Create cameras, lights, and models.
- \li Open \uicontrol {Material Editor} and edit materials.
- \li Delete components
- \endlist
-
- \image 3d-view-context-menu.webp "The context menu in the 3D view"
+ \note To avoid transforming components by mistake while editing your scene,
+ use the \l {Showing and Hiding Components}{showing and hiding} or the
+ \l {Locking Components}{locking} features in \l Navigator.
To refresh the contents of the \uicontrol{3D} view, press \key P or
select the \inlineimage icons/reset.png
@@ -73,53 +47,76 @@
\youtube SsFWyUeAA_4
+ \include qtdesignstudio-qt-academy.qdocinc qt-academy-3D-with-qt-design-studio
+
+ \section1 Using the Context Menu in the 3D View
+
+ There is a context menu in the \uicontrol 3D view. To open it, right-click
+ in the \uicontrol 3D view. From the context menu you can, for example:
+ \list
+ \li Create cameras, lights, and models.
+ \li Open \uicontrol {Material Editor} and edit materials.
+ \li Delete components.
+ \li Align camera views.
+ \endlist
+
+ \image 3d-view-context-menu.webp "The context menu in the 3D view"
+
\section1 Controlling the 3D View Camera
\section2 Toggling Camera Mode
- To switch to perspective camera mode, select
- \inlineimage perspective_camera.png
- (\uicontrol {Toggle Perspective/Orthographic Edit Camera}).
- To switch to orthographic camera mode, select
- \inlineimage orthographic_camera.png
- . You can also Toggle the camera mode by using the keyboard shortcut \key T.
+ Change the projection of the view by switching between \e {perspective camera}
+ and \e {orthographic camera} modes. When using the perspective camera mode,
+ components that are far from the camera appear smaller than those nearby. In
+ the orthographic camera mode, all components appear at the same scale
+ irrespective of their distance from the camera. Both of them are free-form
+ camera modes that you can use to orbit around the scene.
+
+ To toggle the camera mode, do one of the following:
+ \list
+ \li Select \inlineimage perspective_camera.png
+ (\uicontrol {Toggle Perspective/Orthographic Camera}) to use the
+ perspective camera mode.
+ \li Select \inlineimage orthographic_camera.png to use the orthographic
+ camera mode.
+ \endlist
+ You can also toggle the camera mode by using the keyboard shortcut \key T.
\section2 Navigating in the 3D Scene
- You can navigate the scene by panning, rotating, and zooming the 3D view
+ Navigate the scene by panning, rotating, and zooming the 3D view
camera:
\list
\li To pan, press \key Alt (or \key Option on \macos) and use the
- middle mouse button to click and drag anywhere in the rendered
- view to slide the view around. Alternatively, press and hold \key {right mouse
- button} and \key {left mouse button} and drag anywhere in the view to pan.
- \li To orbit, press \key Alt and click and drag anywhere in the rendered
- view to rotate the view.
+ wheel button to drag anywhere in the \uicontrol 3D view to
+ slide the view around. Alternatively, press and hold \key
+ {right mouse button} and \key {left mouse button} and drag
+ anywhere in the view to pan.
+ \li To orbit, press \key Alt and and drag anywhere in the
+ \uicontrol 3D view to rotate the view.
\li To zoom, use the mouse wheel or press \key Alt and right-click
- anywhere in the rendered view to zoom the view in or out as you drag
+ anywhere in the \uicontrol 3D view to zoom the view in or out as you drag
up or down.
\endlist
- To zoom and focus the 3D view camera on a selected component,
- select \inlineimage fit_selected.png
- (\uicontrol {Fit Selected}) or press \key F.
+ To zoom and focus the 3D view camera on a selected component, select
+ \inlineimage fit_selected.png (\uicontrol {Fit Selected}) or press \key F.
The world axis helper (1) shows the direction of the world axes in the view.
- To point the camera at the currently selected component in the direction of
+ To point the 3D view camera at the currently selected component in the direction of
an axis, click the axis. Clicking the dot at the end of the axis will point
the camera at the opposite direction of the axis. If no component is
- selected, the camera is pointed at the world origin. This does not affect
- the camera zoom level.
+ selected, the camera is pointed at the world origin.
\image studio-3d-editor-axis-helper.webp "Axis helper in the 3D view"
- You can use scene cameras (2) to view the \uicontrol View3D component from a
- specific angle in the \l {2D} view while editing scenes. Different types of
- cameras are available in \uicontrol Components
- > \uicontrol {Qt Quick 3D} > \uicontrol {Qt Quick 3D}. For more information
- about using cameras in the scene, the available camera types, and their
- properties, see \l{Cameras}.
+ Use scene cameras (2) to view the \uicontrol View3D component from a
+ specific angle in the \l {2D} view while editing scenes. Drag a camera
+ component to your scene from \uicontrol Components > \uicontrol {Qt Quick 3D} >
+ \uicontrol {Qt Quick 3D}. For more information about using cameras in the
+ scene and the available camera types, see \l{Cameras}.
\section2 Using Split View
@@ -128,16 +125,16 @@
\image studio-3d-split-view.webp "Split view in the 3D view"
- To select one of the four panes, click on it. The selected pane is marked with
+ To select one of the four panes, click the pane. The selected pane is marked with
a blue frame. Use the world axis helper to change the point of view for each pane
independently. Navigate each split by panning, rotating, and zooming, as
described above.
\section2 Using Fly Mode
- You can move around freely in the 3D scene with fly mode. To navigate the scene with
- fly mode, keep the \key {right mouse button} pressed and use the listed keys to move
- around the scene.
+ Use the fly mode to move around freely in the 3D scene. To navigate the scene with
+ fly mode, right-click and hold in the \uicontrol 3D view, and use the keyboard shortcuts
+ to move around the scene:
\table
\header
@@ -161,10 +158,20 @@
\row
\li \key Q or \key {Page down}
\li Move down.
+ \row
+ \li \key Shift
+ \li Move faster.
+ \row
+ \li \key Alt
+ \li Move slower.
+ \row
+ \li \key Spacebar
+ \li Move to an object in the crosshairs.
\endtable
- To adjust the movement speed, select \inlineimage icons/camera_speed.png in the
- \uicontrol 3D view toolbar to open the configuration dialog.
+ To adjust the movement speed, right-click and hold in the \uicontrol 3D view, and rotate the
+ mouse wheel. Alternatively, select \inlineimage icons/camera_speed.png in the \uicontrol 3D
+ view toolbar to open the configuration dialog.
In the configuration dialog, you can:
\list
@@ -174,10 +181,8 @@
\section1 Using Global and Local Orientation
- To switch between local and global orientation, select
- \inlineimage global.png
- (\uicontrol {Toggle Local/Global Orientation})
- or press \key Y.
+ In \uicontrol 3D view, you view the scene in global or local orientation
+ mode.
In global orientation mode, transformation of a selected component is
presented with respect to the global space. For example, while the move tool
@@ -191,40 +196,40 @@
the axes of global space. Dragging on the red arrow of the gizmo moves the
component in the local x direction in relation to the component.
+ To switch between local and global orientation, select \inlineimage global.png
+ (\uicontrol {Toggle Local/Global Orientation}) or press \key Y.
+
\section1 Using Edit Light
The edit light is an extra point light that can be used to illuminate the
scene. To toggle the edit light on and off, select \inlineimage edit_light_on.png
- or \inlineimage edit_light_off.png
- (\uicontrol {Toggle Edit Light})
- or press \key U.
+ or \inlineimage edit_light_off.png (\uicontrol {Toggle Edit Light}) or
+ press \key U.
- For more information about the available scene light types and their
- properties, see \l{Lights}.
+ For information about the available scene light types and their properties,
+ see \l{Lights}.
\section1 Baking Lights
- Bake lights to light static elements in your scene. To bake lights,
- select \inlineimage icons/bakelights.png to open the
- \uicontrol {Lights Baking Setup} dialog. For more information, see
- \l {Baking Lightmaps}.
+ Bake lights to light static elements in your scene. To bake lights, select
+ \inlineimage icons/bakelights.png to open the \uicontrol {Lights Baking Setup}
+ dialog. For more information, see \l {Baking Lightmaps}.
\section1 Selecting Components
To move, rotate, or scale components in the scene, you need to select them
- first. The selection mode buttons determine how components are selected when
- you click them in the \uicontrol{3D} view:
+ first. Toggle the selection mode to determine how components are selected
+ when you click them in the \uicontrol{3D} view:
\list
- \li In the \inlineimage select_item.png
- (\uicontrol {Single Selection}) mode, a single component is selected.
- \li In the \inlineimage select_group.png
- (\uicontrol {Group Selection}) mode, the top level parent of the
- component is selected. This enables you to move, rotate, or scale a
- group of components.
+ \li Use the \inlineimage select_item.png (\uicontrol {Single Selection})
+ mode to select a single component.
+ \li Use the \inlineimage select_group.png (\uicontrol {Group Selection})
+ mode to select the top level parent of the component, so you can move
+ a group of components simultaneously.
\endlist
- To toggle the selection mode, press \key Q.
+ Alternatively, press \key Q to toggle the selection mode.
To multiselect, hold \key Ctrl and click the components you wish to select.
@@ -241,16 +246,15 @@
y, or z axis or on the top, bottom, left, and right clip planes of the
the \uicontrol{3D} view.
- To move components, select \inlineimage move_off.png
- or press \key W:
+ To move components, select \inlineimage move_off.png or press \key W:
\list
\li To move components along the axes of the move gizmo, click the axis,
and drag the component along the axis.
- \li To move components on a plane, click the plane handle and drag the
+ \li To move components on a plane, drag the plane handle of the move
component on the plane.
- \li To move components freely in the 3D view, click and drag the gray
- handle at the center of the move gizmo.
+ \li To move components freely in the 3D view, drag the gray handle at the
+ center of the move gizmo.
\endlist
\section1 Rotating Components
@@ -261,29 +265,28 @@
or press \key E:
\list
- \li To rotate a component around its rotation gizmo, click the axis ring
- and drag in the direction you want to rotate the component in.
- \li To freely rotate the component, click and drag the inner center
- circle of the gizmo.
+ \li To rotate a component around its rotation gizmo, drag the axis ring
+ in the direction you want to rotate the component in.
+ \li To freely rotate the component, drag the inner center circle of the
+ gizmo.
\endlist
\section1 Scaling Components
\image studio-3d-editor-scale.webp "The 3D view in scale mode"
- You can use the scale handles to adjust the local x, y, or z scale of a
+ Úse the scale handles to adjust the local x, y, or z scale of a
component. You can adjust the scale across one, two, or three axes,
depending on the handle.
- To scale components, select \inlineimage scale_off.png
- or press \key R:
+ To scale components, select \inlineimage scale_off.png or press \key R:
\list
- \li To adjust the scale across one axis, click and drag the scale handle
+ \li To adjust the scale across one axis, drag the scale handle
attached to the axis.
- \li To adjust the scale across a plane, click the plane handle and drag
- the component on the plane.
- \li To uniformly scale a component across all axes, click and drag the
+ \li To adjust the scale across a plane, drag the plane handle of
+ the component.
+ \li To uniformly scale a component across all axes, drag the
gray handle at the center of the component.
\endlist
@@ -292,7 +295,7 @@
With snapping turned on, the objects in the \uicontrol 3D view snap to certain
intervals during transformation (move, rotate, scale).
- You can toggle snapping in the following ways:
+ Toggle snapping in the following ways:
\list
\li Select \inlineimage icons/snapping-3d.png
@@ -300,7 +303,7 @@
\li Hold down the \key Ctrl key.
\endlist
- With snapping turned on, you can press and hold \key Shift to snap objects to one tenth of
+ With snapping turned on, press and hold \key Shift to snap objects to one tenth of
the specified snap interval.
\section2 Configuring Snapping
@@ -327,78 +330,85 @@
To align a camera to the \uicontrol{3D} view:
\list 1
- \li Select a camera in the \uicontrol{3D} or \uicontrol {Navigator} view.
+ \li Select a scene camera in the \uicontrol{3D} or \uicontrol {Navigator} view.
\note If you don't have a camera selected, the most recently selected camera
is aligned to the view.
- \li In the \uicontrol{3D} view,
- select \inlineimage icons/align-camera-on.png
- .
+ \li In the \uicontrol{3D} view, select \inlineimage icons/align-camera-on.png.
\endlist
- This moves and rotates the camera so that the camera shows the same view
- as the current view in the \uicontrol{3D} view.
+ This moves and rotates the scene camera to show the same view as the current view
+ in the \uicontrol{3D} view.
To align the \uicontrol{3D} view to a camera:
- \list 1
- \li Select a camera in the \uicontrol{3D} view or \uicontrol {Navigator}.
+ \list 1
+ \li Select a scene camera in the \uicontrol{3D} view or \uicontrol {Navigator}.
\note If you don't have a camera selected, the view is aligned to the most recently
- selected camera.
- \li In the \uicontrol{3D} view,
- select \inlineimage icons/align-view-on.png
- .
+ selected camera.
+ \li In the \uicontrol{3D} view, select \inlineimage icons/align-view-on.png.
\endlist
- This copies the position as well as x and y rotation values from the
- camera and applies them to the \uicontrol{3D} view.
+ This moves and rotates the 3D view to show the same view as the selected scene camera.
\section1 Toggling Visibility
To toggle the visibility of objects in the \uicontrol{3D} view, select
- \inlineimage icons/visibilityon.png
- in the toolbar. This opens a menu with the following options:
+ \inlineimage icons/visibilityon.png in the toolbar. This opens a menu with the
+ following options:
\table
- \row
- \li Show Grid
- \li Toggles the visibility of the helper grid.
- \row
- \li Show Selection Boxes
- \li Toggles the visibility of selection boxes for selected 3D objects.
- \row
- \li Show Icon Gizmos
- \li Toggles the visibility of icon gizmos for object such as cameras,
- lights, and particle systems.
- \row
- \li Always Show Camera Frustums
- \li Toggles between always showing the camera frustum and showing it
- only for cameras selected in the \uicontrol{3D} view.
- \row
- \li Always Show Particle Emitters and Attractors
- \li Toggle between always showing the particle emitter and attractor
- visualizations and only showing them when the emitter or attractor is
- selected in the \uicontrol{3D} view.
+ \header
+ \li Action
+ \li Description
+ \li Keyboard Shortcut
+ \row
+ \li Show Grid
+ \li Toggles the visibility of the helper grid.
+ \li \key G
+ \row
+ \li Show Selection Boxes
+ \li Toggles the visibility of selection boxes for selected 3D objects.
+ \li \key B
+ \row
+ \li Show Icon Gizmos
+ \li Toggles the visibility of icon gizmos for object such as cameras,
+ lights, and particle systems.
+ \li \key I
+ \row
+ \li Always Show Camera Frustums
+ \li Toggles between always showing the camera frustum and showing it
+ only for cameras selected in the \uicontrol{3D} view.
+ \li \key C
+ \row
+ \li Always Show Particle Emitters and Attractors
+ \li Toggles between always showing the particle emitter and attractor
+ visualizations and only showing them when the emitter or attractor
+ is selected in the \uicontrol{3D} view.
+ \li \key M
\endtable
\section1 Changing Colors
To change the \uicontrol 3D view background or grid color, select
- \inlineimage icons/3d-background-color.png
- in the toolbar. This opens a menu with the following options:
+ \inlineimage icons/3d-background-color.png in the toolbar. This opens a menu
+ with the following options:
\table
+ \header
+ \li Action
+ \li Decription
+ \row
+ \li Select Background Color
+ \li Select a color for the background.
\row
- \li Select Background Color
- \li Select a color for the background.
- \row
- \li Select Grid Color
- \li Select a color for the grid.
+ \li Select Grid Color
+ \li Select a color for the grid.
\row
- \li Use Scene Environment Color
- \li Sets the 3D view to use the scene environment color as background
- color.
+ \li Use Scene Environment Color
+ \li Sets the 3D view to use the scene environment color as background
+ color.
\row
- \li Reset Colors
- \li Resets the background and grid colors to the default colors.
+ \li Reset Colors
+ \li Resets the background and grid colors to the default colors.
\endtable
\section1 Particle Editor
@@ -413,16 +423,14 @@
\li Select a particle system in the \uicontrol Navigator or
\uicontrol{3D} view.
\li In the \uicontrol{3D} view, select
- \inlineimage icons/particle-animation-on.png
- to activate particle animation. Now you can see the particle animation in
- the \uicontrol{3D} view.
+ \inlineimage icons/particle-animation-on.png to activate particle animation.
+ Now you can see the particle animation in the \uicontrol{3D} view.
\endlist
You can pause the particle animation by selecting
- \inlineimage icons/particle-pause.png
- . When the animation is paused, you can use
- \inlineimage icons/particles-seek.png
- to manually seek forward or backward in the particle animation.
+ \inlineimage icons/particle-pause.png. When the animation is paused, use
+ \inlineimage icons/particles-seek.png to manually seek forward or backward in
+ the particle animation.
\section1 Using Viewport Shading
diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc
index 0f0d0634ca..5cf2370cc9 100644
--- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc
+++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc
@@ -142,7 +142,7 @@
\li From \uicontrol Components, drag a \uicontrol Sphere to \e _3DRepeater
in \uicontrol Navigator.
\image repeater3d-listmodel-navigator.png
- \li Select \e sphere in \uicontrol Navigator and in the \Properties view, select
+ \li Select \e sphere in \uicontrol Navigator and in the \uicontrol Properties view, select
\inlineimage icons/action-icon.png
next to \uicontrol Scale > \uicontrol X.
\li Select \uicontrol {Set binding} to open \uicontrol {Binding Editor}.
diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc
index 1ada3625ff..db9961e31d 100644
--- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc
+++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc
@@ -17,7 +17,7 @@
To create a project with many complex effects, use the \uicontrol {Extended 3D} preset
that creates a project with an \uicontrol {Extended View3D} component.
- The extended 3D view includes an {Extended Scene Environment}
+ The extended 3D view includes an \uicontrol{Extended Scene Environment}
component that enables using various effects by defining them as properties.
\note The extended 3D view is available in projects created with Qt 6.5
diff --git a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc
index c928fff58c..72f57f0c17 100644
--- a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc
+++ b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc
@@ -3,7 +3,7 @@
/*!
\page quick-logic-helpers.html
- \previouspage quick-2d-effects.html
+ \previouspage quick-design-effects.html
\nextpage quick-animations.html
\title Logic Helpers
diff --git a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-property-visual-effects.qdoc b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-property-visual-effects.qdoc
new file mode 100644
index 0000000000..3530d4da64
--- /dev/null
+++ b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-property-visual-effects.qdoc
@@ -0,0 +1,136 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page quick-design-effects.html
+ \previouspage quick-2d-effects.html
+ \nextpage quick-logic-helpers.html
+
+ \title Design Effects
+
+ \QDS provides a set of effects in the \uicontrol Properties view that you can apply
+ to the components.
+
+ \image studio-effects.webp "Design Effects in the Properties view"
+
+ \note This feature is a \e Beta release.
+
+ The available set of Design Effects applies to the \QDS components:
+ \list
+ \li \l {Applying Layer Blur on a Component} {Layer Blur}
+ \li \l {Applying Background Blur on a Component} {Background Blur}
+ \li \l {Applying Drop Shadow on a Component} {Drop Shadow}
+ \li \l {Applying Inner Shadow on a Component} {Inner Shadow}
+ \endlist
+
+ \section1 Applying Layer Blur on a Component
+
+ Use \uicontrol {Layer Blur} to make a component blurry. To apply \uicontrol {Layer Blur}
+ to a component, follow these steps:
+ \list 1
+ \li Select the component in the \uicontrol 2D or \uicontrol Navigator view.
+ \li Go to the \uicontrol {Properties} view > \uicontrol Effects
+ and select \uicontrol {Add Effects}.
+ \li Go to \uicontrol {Layer Blur} and enter the level of blurring you need
+ in \uicontrol {Blur}.
+
+ \image studio-effects-layer-blur.webp "Layer Blur Effects in Properties view"
+ \endlist
+ \note The level of \uicontrol {Layer Blur} is adjustable between zero and one hundred. \br
+ To remove the applied \uicontrol {Layer Blur}, select the component,
+ then go to \uicontrol {Properties} view > \uicontrol {Layer Blur}
+ > \uicontrol {Remove Effects}. This also removes all the other effects
+ applied to the component.
+
+ \section1 Applying Background Blur on a Component
+
+ Apply \uicontrol {Background Blur} to a component when you want to blur a selected
+ component behind it. There are a few essential conditions you should consider.
+ \list
+ \li Make the component partially transparent. With solid color,
+ the background component is not visible.
+ \li Use a solid color on the background component. On
+ a transparent background component the \uicontrol {Background Blur} does not
+ function properly.
+ \note Currently, the \uicontrol {Background Blur} functions on top of only one selected
+ background component. All the other components ignore the blurring.
+ \endlist
+
+ After fulfilling the above conditions, follow the next steps to apply the
+ \uicontrol {Background Blur} on a component.
+
+ \list 1
+ \li Select the component in the \uicontrol 2D or \uicontrol Navigator view.
+ \li Go to the \uicontrol {Properties} view > \uicontrol Effects
+ and select \uicontrol {Add Effects}.
+ \li Go to \uicontrol {Background Blur} and enter the level of blurring you need
+ in \uicontrol {Blur}.
+ \li In the \uicontrol Background dropdown menu, select another
+ components as the background component.
+ \li Drag the component on top of the background component. The area from the
+ component covering the component selected as \uicontrol Background gets
+ blurred. However, any other component behind the component doesn't blur.
+ \endlist
+
+ \image studio-effects-background-blur.webp "Applying Background Blur"
+
+ \section1 Applying Drop Shadow on a Component
+
+ Shadows can either fall outside or inside the component.
+ The shadow that falls outside is a drop shadow. To apply
+ a \uicontrol {Drop Shadow} to a component, follow the instructions below.
+
+ \list 1
+ \li Select the component in the \uicontrol 2D or \uicontrol Navigator view.
+ \li Go to the \uicontrol {Properties} view > \uicontrol Effects
+ and select \uicontrol {Add Effects}.
+ \endlist
+
+ This adds the default drop shadow to the component. To adjust this shadow,
+ follow these instructions.
+
+ \list 1
+ \li Select the component and go to the \uicontrol Properties view > \uicontrol Effects.
+ Then, select \inlineimage icons/particle-play.png next to the shadow type
+ selector dropdown menu.
+ \li Adjust \uicontrol Blur, \uicontrol Offset, \uicontrol Spread, and \uicontrol Color
+ to shape the shadow.
+ \endlist
+
+ \image studio-effects-drop-shadow.webp "Drop Shadow Effects in Properties view"
+
+ \note To stack multiple shadows, select \uicontrol {Add Shadow Effect} from the
+ \uicontrol Properties view. After adding multiple shadows, you can adjust them
+ separately from their expandable menu.
+
+ The \uicontrol {Show behind} feature in \uicontrol {Drop Shadow} only works when
+ the component is transparent. To use it:
+
+ \list 1
+ \li Select the component with a drop shadow.
+ \li Go to the \uicontrol Properties view > \uicontrol Effects.
+ Then, select \inlineimage icons/particle-play.png next to the shadow type
+ selector dropdown menu.
+ \li Select \uicontrol {Show Behind}.
+
+ \image studio-effects-show-shadow-behind.webp "Show drop shadow behind the component"
+ \endlist
+
+ \section1 Applying Inner Shadow on a Component
+
+ \uicontrol {Inner shadow} works inside a component. To apply \uicontrol {Inner Shadow},
+ do the following:
+
+ \list 1
+ \li Select the component in the \uicontrol 2D or \uicontrol Navigator view.
+ \li Go to the \uicontrol {Properties} view > \uicontrol Effects
+ and select \uicontrol {Add Effects}.
+ \li From the dropdown menu, select \uicontrol {Inner Shadow}.
+ \li Adjust \uicontrol Blur, \uicontrol Offset, \uicontrol Spread, and \uicontrol Color
+ to shape the shadow.
+
+ \image studio-effects-inner-shadow.webp "Inner shadow of the component"
+ \endlist
+
+
+*/
diff --git a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-visual-effects.qdoc b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-visual-effects.qdoc
index 9cff12750c..52ef110a62 100644
--- a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-visual-effects.qdoc
+++ b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-visual-effects.qdoc
@@ -4,7 +4,7 @@
/*!
\page quick-2d-effects.html
\previouspage quick-data-models.html
- \nextpage quick-logic-helpers.html
+ \nextpage quick-property-effects.html
\title 2D Effects
diff --git a/doc/qtdesignstudio/src/views/studio-model-editor.qdoc b/doc/qtdesignstudio/src/views/studio-model-editor.qdoc
index 4d10f87552..0caebcfb49 100644
--- a/doc/qtdesignstudio/src/views/studio-model-editor.qdoc
+++ b/doc/qtdesignstudio/src/views/studio-model-editor.qdoc
@@ -18,7 +18,7 @@
\image edit-list-model-model-editor.webp
For examples of how to use data models, see
- \l {Adding a Repeater3D Component with a List Model}.
+ \l {Adding a Repeater3D Component with a Model}.
\section1 Creating a Data Model
diff --git a/share/qtcreator/debugger/creatortypes.py b/share/qtcreator/debugger/creatortypes.py
index ba5b53450b..9c2933ba06 100644
--- a/share/qtcreator/debugger/creatortypes.py
+++ b/share/qtcreator/debugger/creatortypes.py
@@ -53,7 +53,7 @@ def readTemplateName(d, value):
def readLiteral(d, value):
if not value.integer():
return "<null>"
- type = typeTarget(value.type.unqualified())
+ type = typeTarget(value.type)
if type and (type.name == "CPlusPlus::TemplateNameId"):
return readTemplateName(d, value)
elif type and (type.name == "CPlusPlus::QualifiedNameId"):
diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py
index 488f1e362e..c2580880f5 100644
--- a/share/qtcreator/debugger/dumper.py
+++ b/share/qtcreator/debugger/dumper.py
@@ -172,9 +172,11 @@ class DumperBase():
self.qtCustomEventFunc = 0
self.qtCustomEventPltFunc = 0
self.qtPropertyFunc = 0
- self.fallbackQtVersion = 0x60200
+ self.qtversion = None
+ self.qtns = None
self.passExceptions = False
self.isTesting = False
+ self.qtLoaded = False
self.isBigEndian = False
self.packCode = '<'
@@ -231,6 +233,8 @@ class DumperBase():
self.useTimeStamps = int(args.get('timestamps', '0'))
self.partialVariable = args.get('partialvar', '')
self.uninitialized = args.get('uninitialized', [])
+ self.qtversion = args.get('qtversion', 0x060602)
+ self.qtnamespace = args.get('qtnamespace', '')
self.uninitialized = list(map(lambda x: self.hexdecode(x), self.uninitialized))
#self.warn('NAMESPACE: "%s"' % self.qtNamespace())
#self.warn('EXPANDED INAMES: %s' % self.expandedINames)
@@ -252,9 +256,11 @@ class DumperBase():
args['partialvar'] = ''
self.fetchVariables(args)
- def setFallbackQtVersion(self, args):
- version = int(args.get('version', self.fallbackQtVersion))
- self.fallbackQtVersion = version
+ def qtVersion(self):
+ return self.qtversion
+
+ def qtNamespace(self):
+ return self.qtnamespace
def resetPerStepCaches(self):
self.perStepCache = {}
@@ -485,7 +491,7 @@ class DumperBase():
self.type_alignment_cache[typeid] = self.type_alignment_cache[target_typeid]
return typeid
- def register_struct(self, name, p5=0, p6=0, s=0):
+ def register_struct(self, name, p5=0, p6=0, s=0, qobject_based=False):
# p5 = n -> n * ptrsize for Qt 5
# p6 = n -> n * ptrsize for Qt 6
#if self.qtVersion() >= 0x060000: # FIXME: Qt 5, ptrSize()
@@ -493,6 +499,7 @@ class DumperBase():
typeid = self.typeid_for_string(name)
self.type_code_cache[typeid] = TypeCode.Struct
self.type_size_cache[typeid] = size
+ self.type_qobject_based_cache[typeid] = qobject_based
self.type_alignment_cache[typeid] = 8
return typeid
@@ -556,7 +563,7 @@ class DumperBase():
self.register_enum('@Qt::ItemDataRole', 4)
- self.register_struct('@QObject', p5=2, p6=2)
+ self.register_struct('@QObject', p5=2, p6=2, qobject_based=True)
self.register_struct('@QObjectPrivate', p5=10, p6=10) # FIXME: Not exact
self.register_struct('@QByteArray', p5=1, p6=3)
@@ -1566,7 +1573,22 @@ class DumperBase():
if address is not None:
self.put('origaddr="0x%x",' % address)
+ def wantQObjectNames(self):
+ return self.showQObjectNames and self.qtLoaded
+
+ def fetchInternalFunctions(self):
+ # Overrridden
+ pass
+
def putQObjectNameValue(self, value):
+ is_qobject_based = self.type_qobject_based_cache.get(value.typeid, None)
+ if is_qobject_based == False:
+ #self.warn("SKIP TEST OBJNAME: %s" % self.type_name(value.typeid))
+ return
+
+ #self.warn("TEST OBJNAME: %s" % self.type_name(value.typeid))
+ self.fetchInternalFunctions()
+
try:
# dd = value['d_ptr']['d'] is just behind the vtable.
(vtable, dd) = self.split('pp', value)
@@ -3022,7 +3044,7 @@ typename))
self.putExpandable()
self.putEmptyValue()
#self.warn('STRUCT GUTS: %s ADDRESS: 0x%x ' % (value.name, value.address()))
- if self.showQObjectNames:
+ if self.wantQObjectNames():
#with self.timer(self.currentIName):
self.putQObjectNameValue(value)
if self.isExpanded():
@@ -3030,7 +3052,7 @@ typename))
self.putField('sortable', 1)
with Children(self, 1, childType=None):
self.putFields(value)
- if self.showQObjectNames:
+ if self.wantQObjectNames():
self.tryPutQObjectGuts(value)
def symbolAddress(self, symbolName):
@@ -3238,6 +3260,7 @@ typename))
self.type_nativetype_cache = {}
self.type_modulename_cache = {}
self.type_encoding_cache = {}
+ self.type_qobject_based_cache = {}
self.typeid_cache = {} # internal typename -> id
self.typeid_current = 100
self.typeid_from_typekey = {} # typename -> id
diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py
index 744d246142..5e2957b181 100644
--- a/share/qtcreator/debugger/gdbbridge.py
+++ b/share/qtcreator/debugger/gdbbridge.py
@@ -380,6 +380,18 @@ class Dumper(DumperBase):
self.type_nativetype_cache[typeid] = nativeType
+ if code == gdb.TYPE_CODE_STRUCT:
+ self.type_qobject_based_cache[typeid] = self.is_qobject_based(nativeType)
+ #res = self.is_qobject_based(nativeType)
+ #if res == False:
+ # self.warn("RECOGNIZED AS NON-QOBJECT: %s" % nativeType)
+ #elif res == True:
+ # self.warn("RECOGNIZED AS QOBJECT: %s" % nativeType)
+ #else:
+ # self.warn("UNRECOGNIZED: %s" % nativeType)
+ elif code != gdb.TYPE_CODE_TYPEDEF:
+ self.type_qobject_based_cache[typeid] = False
+
# FIXME: Field offset caching (or later extraction?) broken
# if code == gdb.TYPE_CODE_STRUCT:
# field_type_name = self.type_name_cache.get(typeid, '')
@@ -413,6 +425,16 @@ class Dumper(DumperBase):
return typeid
+ def is_qobject_based(self, nativeType):
+ if str(nativeType) == self.qtNamespace() + 'QObject':
+ return True
+ fields = nativeType.fields()
+ if len(fields) == 0:
+ return None # No info, can't drill deeper
+ if not fields[0].is_base_class:
+ return False
+ return self.is_qobject_based(fields[0].type)
+
def nativeTemplateParameter(self, typeid, index, nativeType):
try:
targ = nativeType.template_argument(index)
@@ -852,37 +874,6 @@ class Dumper(DumperBase):
except:
return '0x%x' % address
- def qtVersionString(self):
- try:
- return str(gdb.lookup_symbol('qVersion')[0].value()())
- except:
- pass
- try:
- ns = self.qtNamespace()
- return str(gdb.parse_and_eval("((const char*(*)())'%sqVersion')()" % ns))
- except:
- pass
- return None
-
- def qtVersion(self):
- try:
- # Only available with Qt 5.3+
- qtversion = int(str(gdb.parse_and_eval('((void**)&qtHookData)[2]')), 16)
- self.qtVersion = lambda: qtversion
- return qtversion
- except:
- pass
-
- try:
- version = self.qtVersionString()
- (major, minor, patch) = version[version.find('"') + 1:version.rfind('"')].split('.')
- qtversion = 0x10000 * int(major) + 0x100 * int(minor) + int(patch)
- self.qtVersion = lambda: qtversion
- return qtversion
- except:
- # Use fallback until we have a better answer.
- return self.fallbackQtVersion
-
def createSpecialBreakpoints(self, args):
self.specialBreakpoints = []
@@ -1008,10 +999,6 @@ class Dumper(DumperBase):
for obj in gdb.objfiles():
self.importPlainDumpersForObj(obj)
- def qtNamespace(self):
- # This function is replaced by handleQtCoreLoaded()
- return ''
-
def findSymbol(self, symbolName):
try:
return int(gdb.parse_and_eval("(size_t)&'%s'" % symbolName))
@@ -1044,41 +1031,38 @@ class Dumper(DumperBase):
pass
def handleQtCoreLoaded(self, objfile):
- fd, tmppath = tempfile.mkstemp()
- os.close(fd)
- cmd = 'maint print msymbols -objfile "%s" -- %s' % (objfile.filename, tmppath)
- symbols = gdb.execute(cmd, to_string=True)
- ns = ''
- with open(tmppath) as f:
- ns1re = re.compile(r'_ZN?(\d*)(\w*)L17msgHandlerGrabbedE? ')
- ns2re = re.compile(r'_ZN?(\d*)(\w*)L17currentThreadDataE? ')
- for line in f:
- if 'msgHandlerGrabbed ' in line:
- # [11] b 0x7ffff683c000 _ZN4MynsL17msgHandlerGrabbedE
- # section .tbss Myns::msgHandlerGrabbed qlogging.cpp
- ns = ns1re.split(line)[2]
- if len(ns):
- ns += '::'
- break
- if 'currentThreadData ' in line:
- # [ 0] b 0x7ffff67d3000 _ZN2UUL17currentThreadDataE
- # section .tbss UU::currentThreadData qthread_unix.cpp\\n
- ns = ns2re.split(line)[2]
- if len(ns):
- ns += '::'
- break
- os.remove(tmppath)
-
+ self.qtLoaded = True
+ # FIXME: Namespace auto-detection. Is it worth the price?
+ # fd, tmppath = tempfile.mkstemp()
+ # os.close(fd)
+ # cmd = 'maint print msymbols -objfile "%s" -- %s' % (objfile.filename, tmppath)
+ # symbols = gdb.execute(cmd, to_string=True)
+ # ns = ''
+ # with open(tmppath) as f:
+ # ns1re = re.compile(r'_ZN?(\d*)(\w*)L17msgHandlerGrabbedE? ')
+ # ns2re = re.compile(r'_ZN?(\d*)(\w*)L17currentThreadDataE? ')
+ # for line in f:
+ # if 'msgHandlerGrabbed ' in line:
+ # # [11] b 0x7ffff683c000 _ZN4MynsL17msgHandlerGrabbedE
+ # # section .tbss Myns::msgHandlerGrabbed qlogging.cpp
+ # ns = ns1re.split(line)[2]
+ # if len(ns):
+ # ns += '::'
+ # break
+ # if 'currentThreadData ' in line:
+ # # [ 0] b 0x7ffff67d3000 _ZN2UUL17currentThreadDataE
+ # # section .tbss UU::currentThreadData qthread_unix.cpp\\n
+ # ns = ns2re.split(line)[2]
+ # if len(ns):
+ # ns += '::'
+ # break
+ # os.remove(tmppath)
+
+ def fetchInternalFunctions(self):
+ ns = self.qtNamespace()
lenns = len(ns)
strns = ('%d%s' % (lenns - 2, ns[:lenns - 2])) if lenns else ''
-
if lenns:
- # This might be wrong, but we can't do better: We found
- # a libQt5Core and could not extract a namespace.
- # The best guess is that there isn't any.
- self.qtNamespaceToReport = ns
- self.qtNamespace = lambda: ns
-
sym = '_ZN%s7QObject11customEventEPNS_6QEventE' % strns
else:
sym = '_ZN7QObject11customEventEP6QEvent'
@@ -1091,6 +1075,8 @@ class Dumper(DumperBase):
if not self.isWindowsTarget(): # prevent calling the property function on windows
self.qtPropertyFunc = self.findSymbol(sym)
+ self.fetchInternalFunctions = lambda: None
+
def assignValue(self, args):
type_name = self.hexdecode(args['type'])
expr = self.hexdecode(args['expr'])
diff --git a/share/qtcreator/debugger/lldbbridge.py b/share/qtcreator/debugger/lldbbridge.py
index 6b08ac3d32..7cd3bef77e 100644
--- a/share/qtcreator/debugger/lldbbridge.py
+++ b/share/qtcreator/debugger/lldbbridge.py
@@ -338,6 +338,15 @@ class Dumper(DumperBase):
self.ptrSize = lambda: result
return result
+ def is_qobject_based(self, nativeType):
+ if nativeType.GetName() == self.qtNamespace() + 'QObject':
+ return True
+ if nativeType.GetNumberOfDirectBaseClasses() > 0:
+ return self.is_qobject_based(nativeType.GetDirectBaseClassAtIndex(0).GetType())
+ if nativeType.GetNumberOfFields() > 0:
+ return False
+ return None # No info, can't drill deeper
+
def from_native_type(self, nativeType):
self.check(isinstance(nativeType, lldb.SBType))
@@ -458,8 +467,12 @@ class Dumper(DumperBase):
self.nativeTypeEnumDisplay(nativeType, intval, form)
elif code in (lldb.eTypeClassComplexInteger, lldb.eTypeClassComplexFloat):
type_code = TypeCode.Complex
- elif code in (lldb.eTypeClassClass, lldb.eTypeClassStruct, lldb.eTypeClassUnion):
+ elif code in (lldb.eTypeClassClass, lldb.eTypeClassStruct):
type_code = TypeCode.Struct
+ self.type_qobject_based_cache[typeid] = self.is_qobject_based(nativeType)
+ elif code == lldb.eTypeClassUnion:
+ type_code = TypeCode.Struct
+ self.type_qobject_based_cache[typeid] = False
elif code == lldb.eTypeClassFunction:
type_code = TypeCode.Function
elif code == lldb.eTypeClassMemberPointer:
@@ -768,80 +781,7 @@ class Dumper(DumperBase):
symbol = funcs[0].GetSymbol()
self.qtPropertyFunc = symbol.GetStartAddress().GetLoadAddress(self.target)
- def fetchQtVersionAndNamespace(self):
- for func in self.target.FindFunctions('qVersion'):
- name = func.GetSymbol().GetName()
- if name == None:
- continue
- if name.endswith('()'):
- name = name[:-2]
- if name.count(':') > 2:
- continue
-
- qtNamespace = name[:name.find('qVersion')]
- self.qtNamespace = lambda: qtNamespace
-
- options = lldb.SBExpressionOptions()
- res = self.target.EvaluateExpression(name + '()', options)
-
- if not res.IsValid() or not res.GetType().IsPointerType():
- exp = '((const char*())%s)()' % name
- res = self.target.EvaluateExpression(exp, options)
-
- if not res.IsValid() or not res.GetType().IsPointerType():
- exp = '((const char*())_Z8qVersionv)()'
- res = self.target.EvaluateExpression(exp, options)
-
- if not res.IsValid() or not res.GetType().IsPointerType():
- continue
-
- version = str(res)
- if version.count('.') != 2:
- continue
-
- version.replace("'", '"') # Both seem possible
- version = version[version.find('"') + 1:version.rfind('"')]
-
- (major, minor, patch) = version.split('.')
- qtVersion = 0x10000 * int(major) + 0x100 * int(minor) + int(patch)
- self.qtVersion = lambda: qtVersion
-
- return (qtNamespace, qtVersion)
-
- try:
- versionValue = self.target.EvaluateExpression('qtHookData[2]').GetNonSyntheticValue()
- if versionValue.IsValid():
- return ('', versionValue.unsigned)
- except:
- pass
-
- return ('', self.fallbackQtVersion)
-
- def qtVersionAndNamespace(self):
- qtVersionAndNamespace = None
- try:
- qtVersionAndNamespace = self.fetchQtVersionAndNamespace()
- self.report("Detected Qt Version: 0x%0x (namespace='%s')" %
- (qtVersionAndNamespace[1], qtVersionAndNamespace[0] or "no namespace"))
- except Exception as e:
- DumperBase.warn('[lldb] Error detecting Qt version: %s' % e)
-
- try:
- self.fetchInternalFunctions()
- self.report('Found function QObject::property: 0x%0x' % self.qtPropertyFunc)
- self.report('Found function QObject::customEvent: 0x%0x' % self.qtCustomEventFunc)
- except Exception as e:
- DumperBase.warn('[lldb] Error fetching internal Qt functions: %s' % e)
-
- # Cache version information by overriding this function.
- self.qtVersionAndNamespace = lambda: qtVersionAndNamespace
- return qtVersionAndNamespace
-
- def qtNamespace(self):
- return self.qtVersionAndNamespace()[0]
-
- def qtVersion(self):
- return self.qtVersionAndNamespace()[1]
+ self.fetchInternalFunctions = lambda: None
def handleCommand(self, command):
result = lldb.SBCommandReturnObject()
@@ -1360,6 +1300,9 @@ class Dumper(DumperBase):
self.setVariableFetchingOptions(args)
+ self.qtLoaded = True # FIXME: Do that elsewhere
+
+
# Reset certain caches whenever a step over / into / continue
# happens.
# FIXME: Caches are currently also cleared if currently
diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsContextMenu.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsContextMenu.qml
index a8eb5285f5..fe22d7ce51 100644
--- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsContextMenu.qml
+++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsContextMenu.qml
@@ -249,7 +249,7 @@ StudioControls.Menu {
StudioControls.MenuItem {
text: qsTr("Add to Content Library")
- visible: root.rootView.userBundleEnabled() && root.__fileIndex && root.assetsModel.allFilePathsAreTextures(root.__selectedAssetPathsList)
+ visible: root.__fileIndex && root.assetsModel.allFilePathsAreTextures(root.__selectedAssetPathsList)
height: visible ? implicitHeight : 0
onTriggered: root.rootView.addAssetsToContentLibrary(root.__selectedAssetPathsList)
}
diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml
deleted file mode 100644
index fd969382e6..0000000000
--- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml
+++ /dev/null
@@ -1,247 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-import QtQuick
-import CollectionDetails 1.0 as CollectionDetails
-import StudioControls 1.0 as StudioControls
-import StudioHelpers as StudioHelpers
-import StudioTheme 1.0 as StudioTheme
-import QtQuick.Templates as T
-
-Item {
- id: root
-
- required property var columnType
-
- TableView.onCommit: {
- if (editorLoader.changesAccepted && edit !== editorLoader.acceptedValue)
- edit = editorLoader.acceptedValue
- }
-
- onActiveFocusChanged: {
- if (root.activeFocus && !editorLoader.triggered && editorLoader.item) {
- editorLoader.triggered = true
- editorLoader.item.open()
- }
-
- // active focus should be checked again, because it might be affected by editorLoader.item
- if (root.activeFocus && editorLoader.editor)
- editorLoader.editor.forceActiveFocus()
- }
-
- Loader {
- id: editorLoader
-
- active: true
-
- property var editor: editorLoader.item ? editorLoader.item.editor : null
- property var editValue: editorLoader.editor ? editorLoader.editor.editValue : null
- property var acceptedValue: null
- property bool changesAccepted: true
- property bool triggered: false
-
- Connections {
- id: modifierFocusConnection
-
- target: editorLoader.editor
- enabled: editorLoader.item !== undefined
-
- function onActiveFocusChanged() {
- if (!modifierFocusConnection.target.activeFocus) {
- editorLoader.acceptedValue = editorLoader.editValue
- root.TableView.commit()
- }
- }
- }
-
- Component {
- id: textEditor
-
- EditorPopup {
- editor: textField
-
- StudioControls.TextField {
- id: textField
-
- property alias editValue: textField.text
-
- actionIndicator.visible: false
- translationIndicatorVisible: false
-
- onRejected: editorLoader.changesAccepted = false
- }
- }
- }
-
- Component {
- id: realEditor
-
- EditorPopup {
-
- editor: realField
-
- StudioControls.RealSpinBox {
- id: realField
-
- property alias editValue: realField.realValue
-
- actionIndicator.visible: false
- realFrom: -9e9
- realTo: 9e9
- realStepSize: 1.0
- decimals: 6
- trailingZeroes: false
-
- onActiveFocusChanged: {
- if (realField.activeFocus)
- realField.contentItem.focus = true
- }
-
- textFromValue: function (value, locale) {
- locale.numberOptions = Locale.OmitGroupSeparator
- var decimals = realField.trailingZeroes ? realField.decimals : decimalCounter(realField.realValue)
- if (decimals > 0) {
- var text = Number(realField.realValue).toLocaleString(locale, 'f', decimals + 1)
- return text.substring(0, text.length - 1)
- }
- return Number(realField.realValue).toLocaleString(locale, 'f', decimals)
- }
- }
- }
- }
-
- Component {
- id: integerEditor
-
- EditorPopup {
-
- editor: integerField
-
- StudioControls.SpinBox {
- id: integerField
-
- property alias editValue: integerField.value
-
- actionIndicatorVisible: false
- spinBoxIndicatorVisible: true
- from: -2147483647
- to: 2147483647
- decimals: 0
-
- onActiveFocusChanged: {
- if (integerField.activeFocus)
- integerField.contentItem.focus = true
- }
- }
- }
- }
-
- Component {
- id: boolEditor
-
- EditorPopup {
-
- editor: boolField
-
- StudioControls.CheckBox {
- id: boolField
-
- property alias editValue: boolField.checked
-
- actionIndicatorVisible: false
- }
- }
- }
- }
-
- component EditorPopup: T.Popup {
- id: editorPopup
-
- required property Item editor
-
- implicitHeight: contentHeight
- implicitWidth: contentWidth
-
- focus: true
- visible: false
-
- Connections {
- target: editorPopup.editor
-
- function onActiveFocusChanged() {
- if (!editorPopup.editor.activeFocus)
- editorPopup.close()
- else if (edit)
- editorPopup.editor.editValue = edit
- }
- }
-
- Connections {
- target: editorPopup.editor.Keys
-
- function onEscapePressed() {
- editorLoader.changesAccepted = false
- editorPopup.close()
- }
-
- function onReturnPressed() {
- editorPopup.close()
- }
-
- function onEnterPressed() {
- editorPopup.close()
- }
- }
- }
-
- states: [
- State {
- name: "default"
- when: columnType !== CollectionDetails.DataType.Boolean
- && columnType !== CollectionDetails.DataType.Color
- && columnType !== CollectionDetails.DataType.Integer
- && columnType !== CollectionDetails.DataType.Real
-
- PropertyChanges {
- target: editorLoader
- sourceComponent: textEditor
- }
- },
- State {
- name: "integer"
- when: columnType === CollectionDetails.DataType.Integer
-
- PropertyChanges {
- target: editorLoader
- sourceComponent: integerEditor
- }
- },
- State {
- name: "real"
- when: columnType === CollectionDetails.DataType.Real
-
- PropertyChanges {
- target: editorLoader
- sourceComponent: realEditor
- }
- },
- State {
- name: "bool"
- when: columnType === CollectionDetails.DataType.Boolean
-
- PropertyChanges {
- target: editorLoader
- sourceComponent: boolEditor
- }
- },
- State {
- name: "color"
- when: columnType === CollectionDetails.DataType.Color
-
- PropertyChanges {
- target: editorLoader
- sourceComponent: null
- }
- }
- ]
-}
diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml
deleted file mode 100644
index e7997f7eae..0000000000
--- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml
+++ /dev/null
@@ -1,291 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import Qt.labs.platform as PlatformWidgets
-import HelperWidgets 2.0 as HelperWidgets
-import StudioControls 1.0 as StudioControls
-import StudioTheme 1.0 as StudioTheme
-import CollectionDetails
-import CollectionEditorBackend
-
-Rectangle {
- id: root
-
- required property var model
- required property var backend
- property int selectedRow: -1
-
- implicitHeight: StudioTheme.Values.toolbarHeight
- color: StudioTheme.Values.themeToolbarBackground
-
- function addNewColumn() {
- addColumnDialog.popUp(root.model.columnCount())
- }
-
- function addNewRow() {
- root.model.insertRow(root.model.rowCount())
- }
-
- function closeDialogs() {
- addColumnDialog.reject()
- fileDialog.reject()
- }
-
- RowLayout {
- id: container
-
- anchors.fill: parent
- anchors.topMargin: StudioTheme.Values.toolbarVerticalMargin
- anchors.bottomMargin: StudioTheme.Values.toolbarVerticalMargin
-
- spacing: StudioTheme.Values.sectionRowSpacing
-
- RowLayout {
- id: leftSideToolbar
-
- Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
- Layout.leftMargin: StudioTheme.Values.toolbarHorizontalMargin
- spacing: StudioTheme.Values.sectionRowSpacing
-
- IconButton {
- id: addColumnLeftButton
-
- buttonIcon: StudioTheme.Constants.addcolumnleft_medium
- tooltip: qsTr("Add column left")
- enabled: root.model.selectedColumn > -1
- onClicked: addColumnDialog.popUp(root.model.selectedColumn)
- }
-
- IconButton {
- id: addColumnRightButton
-
- buttonIcon: StudioTheme.Constants.addcolumnright_medium
- tooltip: qsTr("Add column right")
- enabled: root.model.selectedColumn > -1
- onClicked: addColumnDialog.popUp(root.model.selectedColumn + 1)
- }
-
- IconButton {
- id: deleteColumnButton
-
- buttonIcon: StudioTheme.Constants.deletecolumn_medium
- tooltip: qsTr("Delete selected column")
- enabled: root.model.selectedColumn > -1
- onClicked: root.model.removeColumn(root.model.selectedColumn)
- }
-
- Item { // spacer
- implicitWidth: StudioTheme.Values.toolbarSpacing
- implicitHeight: 1
- }
-
- IconButton {
- id: addRowBelowButton
-
- buttonIcon: StudioTheme.Constants.addrowbelow_medium
- tooltip: qsTr("Add row below")
- enabled: root.model.selectedRow > -1
- onClicked: root.model.insertRow(root.model.selectedRow + 1)
- }
-
- IconButton {
- id: addRowAboveButton
-
- buttonIcon: StudioTheme.Constants.addrowabove_medium
- tooltip: qsTr("Add row above")
- enabled: root.model.selectedRow > -1
- onClicked: root.model.insertRow(root.model.selectedRow)
- }
-
- IconButton {
- id: deleteSelectedRowButton
-
- buttonIcon: StudioTheme.Constants.deleterow_medium
- tooltip: qsTr("Delete selected row")
- enabled: root.model.selectedRow > -1
- onClicked: root.model.removeRow(root.model.selectedRow)
- }
- }
-
- RowLayout {
- id: rightSideToolbar
-
- spacing: StudioTheme.Values.sectionRowSpacing
- Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
- Layout.rightMargin: StudioTheme.Values.toolbarHorizontalMargin
-
- IconButton {
- id: saveCollectionButton
-
- buttonIcon: StudioTheme.Constants.save_medium
- tooltip: qsTr("Save changes")
- enabled: root.model.collectionName !== "" && root.model.hasUnsavedChanges
- onClicked: root.model.saveDataStoreCollections()
-
- Rectangle {
- width: StudioTheme.Values.smallStatusIndicatorDiameter
- height: StudioTheme.Values.smallStatusIndicatorDiameter
- radius: StudioTheme.Values.smallStatusIndicatorDiameter / 2
- anchors.right: parent.right
- anchors.top: parent.top
- visible: root.model.hasUnsavedChanges
- color: StudioTheme.Values.themeIconColorSelected
- }
- }
-
- IconButton {
- id: exportCollectionButton
-
- buttonIcon: StudioTheme.Constants.export_medium
- tooltip: qsTr("Export model")
- enabled: root.model.collectionName !== ""
- onClicked: fileDialog.open()
- }
- }
- }
-
- PlatformWidgets.FileDialog {
- id: fileDialog
-
- fileMode: PlatformWidgets.FileDialog.SaveFile
-
- nameFilters: ["JSON Files (*.json)",
- "Comma-Separated Values (*.csv)"
- ]
-
- selectedNameFilter.index: 0
-
- onAccepted: {
- let filePath = fileDialog.file.toString()
- root.model.exportCollection(filePath)
- }
- }
-
- component IconButton: HelperWidgets.AbstractButton {
- style: StudioTheme.Values.viewBarButtonStyle
- }
-
- component Spacer: Item {
- implicitWidth: 1
- implicitHeight: StudioTheme.Values.columnGap
- }
-
- RegularExpressionValidator {
- id: nameValidator
- regularExpression: /^\w+$/
- }
-
- StudioControls.Dialog {
- id: addColumnDialog
-
- property int clickedIndex: -1
- property bool nameIsValid
-
- title: qsTr("Add Column")
-
- function popUp(index)
- {
- addColumnDialog.clickedIndex = index
- columnName.text = ""
- columnName.forceActiveFocus()
- addedPropertyType.currentIndex = addedPropertyType.find("String")
-
- addColumnDialog.open()
- }
-
- function addColumnName() {
- if (addColumnDialog.nameIsValid) {
- root.model.addColumn(addColumnDialog.clickedIndex, columnName.text, addedPropertyType.currentText)
- addColumnDialog.accept()
- } else {
- addColumnDialog.reject()
- }
- }
-
- contentItem: ColumnLayout {
- spacing: 2
-
- Text {
- text: qsTr("Column name:")
- color: StudioTheme.Values.themeTextColor
- }
-
- StudioControls.TextField {
- id: columnName
-
- Layout.fillWidth: true
-
- actionIndicator.visible: false
- translationIndicator.visible: false
- validator: nameValidator
-
- Keys.onEnterPressed: addColumnDialog.addColumnName()
- Keys.onReturnPressed: addColumnDialog.addColumnName()
- Keys.onEscapePressed: addColumnDialog.reject()
-
- onTextChanged: {
- addColumnDialog.nameIsValid = (columnName.text !== ""
- && !root.model.isPropertyAvailable(columnName.text))
- }
- }
-
- Spacer { implicitHeight: StudioTheme.Values.controlLabelGap }
-
- Label {
- Layout.fillWidth: true
-
- text: qsTr("The model already contains \"%1\"!").arg(columnName.text)
- visible: columnName.text !== "" && !addColumnDialog.nameIsValid
-
- color: StudioTheme.Values.themeTextColor
- wrapMode: Label.WordWrap
- padding: 5
-
- background: Rectangle {
- color: "transparent"
- border.width: StudioTheme.Values.border
- border.color: StudioTheme.Values.themeWarning
- }
- }
-
- Spacer {}
-
- Text {
- text: qsTr("Type:")
- color: StudioTheme.Values.themeTextColor
- }
-
- StudioControls.ComboBox {
- id: addedPropertyType
-
- Layout.fillWidth: true
-
- model: CollectionDataTypeModel{}
- textRole: "display"
- tooltipRole: "toolTip"
- actionIndicatorVisible: false
- }
-
- Spacer {}
-
- RowLayout {
- Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
- spacing: StudioTheme.Values.sectionRowSpacing
-
- HelperWidgets.Button {
- enabled: addColumnDialog.nameIsValid
- text: qsTr("Add")
- onClicked: addColumnDialog.addColumnName()
- }
-
- HelperWidgets.Button {
- text: qsTr("Cancel")
- onClicked: addColumnDialog.reject()
- }
- }
- }
- }
-}
diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml
deleted file mode 100644
index 2193bd1763..0000000000
--- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml
+++ /dev/null
@@ -1,727 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import CollectionDetails 1.0 as CollectionDetails
-import HelperWidgets 2.0 as HelperWidgets
-import StudioControls 1.0 as StudioControls
-import StudioTheme 1.0 as StudioTheme
-
-Rectangle {
- id: root
-
- required property var model
- required property var backend
- required property var sortedModel
-
- implicitWidth: 300
- implicitHeight: 400
- color: StudioTheme.Values.themeControlBackground
-
- function closeDialogs() {
- editPropertyDialog.reject()
- deleteColumnDialog.reject()
- toolbar.closeDialogs()
- }
-
- MouseArea {
- anchors.fill: parent
- onClicked: tableView.model.deselectAll()
- }
-
- Column {
- id: topRow
- readonly property real maxAvailableHeight: root.height
-
- visible: root.model.collectionName !== ""
- width: parent.width
- spacing: 10
-
- CollectionDetailsToolbar {
- id: toolbar
- model: root.model
- backend: root.backend
- width: parent.width
- }
-
- GridLayout {
- id: gridLayout
- readonly property real maxAvailableHeight: topRow.maxAvailableHeight
- - topRow.spacing
- - toolbar.height
-
- columns: 3
- rowSpacing: 1
- columnSpacing: 1
- width: parent.width
-
- anchors {
- left: parent.left
- leftMargin: StudioTheme.Values.collectionTableHorizontalMargin
- }
-
- Rectangle {
- id: tableTopLeftCorner
-
- clip: true
- visible: !tableView.model.isEmpty
- color: StudioTheme.Values.themeControlBackgroundInteraction
- border.color: StudioTheme.Values.themeControlBackgroundInteraction
- border.width: 2
-
- Layout.preferredWidth: rowIdView.width
- Layout.preferredHeight: headerView.height
- Layout.minimumWidth: rowIdView.width
- Layout.minimumHeight: headerView.height
-
- Text {
- anchors.fill: parent
- font: headerTextMetrics.font
- text: "#"
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- color: StudioTheme.Values.themeTextColor
- }
- }
-
- HorizontalHeaderView {
- id: headerView
-
- property real topPadding: 5
- property real bottomPadding: 5
-
- Layout.preferredHeight: headerTextMetrics.height + topPadding + bottomPadding
- Layout.columnSpan: 2
- syncView: tableView
- clip: true
-
- delegate: HeaderDelegate {
- selectedItem: tableView.model.selectedColumn
- color: StudioTheme.Values.themeControlBackgroundInteraction
-
- MouseArea {
- id: topHeaderMouseArea
-
- anchors.fill: parent
- anchors.leftMargin: StudioTheme.Values.borderHover
- anchors.rightMargin: StudioTheme.Values.borderHover
- acceptedButtons: Qt.LeftButton | Qt.RightButton
- hoverEnabled: true
- onClicked: (mouse) => {
- tableView.model.selectColumn(index)
-
- if (mouse.button === Qt.RightButton) {
- let posX = index === root.model.columnCount() - 1 ? parent.width - editPropertyDialog.width : 0
-
- headerMenu.clickedHeaderIndex = index
- headerMenu.dialogPos = parent.mapToGlobal(posX, parent.height)
- headerMenu.popup()
- } else {
- headerMenu.close()
- }
- }
- }
-
- ToolTip {
- id: topHeaderToolTip
-
- property bool expectedToBeShown: topHeaderMouseArea.containsMouse
- visible: expectedToBeShown && text !== ""
- delay: 1000
-
- onExpectedToBeShownChanged: {
- if (expectedToBeShown)
- text = root.model.propertyType(index)
- }
- }
- }
-
- StudioControls.Menu {
- id: headerMenu
-
- property int clickedHeaderIndex: -1
- property point dialogPos
-
- onClosed: {
- headerMenu.clickedHeaderIndex = -1
- }
-
- StudioControls.MenuItem {
- text: qsTr("Edit")
- onTriggered: editPropertyDialog.openDialog(headerMenu.clickedHeaderIndex,
- headerMenu.dialogPos)
- }
-
- StudioControls.MenuItem {
- text: qsTr("Delete")
- onTriggered: deleteColumnDialog.popUp(headerMenu.clickedHeaderIndex)
- }
-
- StudioControls.MenuItem {
- text: qsTr("Sort Ascending")
- onTriggered: {
- tableView.closeEditor()
- tableView.model.sort(headerMenu.clickedHeaderIndex, Qt.AscendingOrder)
- }
- }
-
- StudioControls.MenuItem {
- text: qsTr("Sort Descending")
- onTriggered: {
- tableView.closeEditor()
- tableView.model.sort(headerMenu.clickedHeaderIndex, Qt.DescendingOrder)
- }
- }
- }
- }
-
- VerticalHeaderView {
- id: rowIdView
-
- syncView: tableView
- clip: true
-
- Layout.preferredHeight: tableView.height
- Layout.rowSpan: 2
- Layout.alignment: Qt.AlignTop + Qt.AlignLeft
- width: implicitWidth // suppresses GridLayout warnings when resizing
-
- delegate: HeaderDelegate {
- selectedItem: tableView.model.selectedRow
- color: StudioTheme.Values.themeControlBackgroundHover
-
- MouseArea {
- anchors.fill: parent
- anchors.topMargin: StudioTheme.Values.borderHover
- anchors.bottomMargin: StudioTheme.Values.borderHover
- acceptedButtons: Qt.LeftButton
- onClicked: tableView.model.selectRow(index)
- }
- }
- }
-
- TableView {
- id: tableView
-
- model: root.sortedModel
- clip: true
-
- readonly property real maxAvailableHeight: gridLayout.maxAvailableHeight
- - addRowButton.height
- - headerView.height
- - (2 * gridLayout.rowSpacing)
- readonly property real maxAvailableWidth: gridLayout.width
- - StudioTheme.Values.collectionTableHorizontalMargin
- - rowIdView.width
- - addColumnButton.width
- - gridLayout.columnSpacing
-
- property real childrenWidth: tableView.contentItem.childrenRect.width
- property real childrenHeight: tableView.contentItem.childrenRect.height
-
- property int targetRow
- property int targetColumn
-
- Layout.alignment: Qt.AlignTop + Qt.AlignLeft
- Layout.preferredWidth: tableView.contentWidth
- Layout.preferredHeight: tableView.contentHeight
- Layout.minimumWidth: 100
- Layout.minimumHeight: 20
- Layout.maximumWidth: maxAvailableWidth
- Layout.maximumHeight: maxAvailableHeight
-
- columnWidthProvider: function(column) {
- if (!isColumnLoaded(column))
- return -1
- let w = explicitColumnWidth(column)
- if (w < 0)
- w = implicitColumnWidth(column)
- return Math.max(w, StudioTheme.Values.collectionCellMinimumWidth)
- }
-
- rowHeightProvider: function(row) {
- if (!isRowLoaded(row))
- return -1
- let h = explicitRowHeight(row)
- if (h < 0)
- h = implicitRowHeight(row)
- return Math.max(h, StudioTheme.Values.collectionCellMinimumHeight)
- }
-
- function ensureRowIsVisible(row) {
- let rows = tableView.model.rowCount()
- let rowIsLoaded = tableView.isRowLoaded(row)
-
- if (row < 0 || row >= rows || rowIsLoaded) {
- if (rowIsLoaded)
- tableView.positionViewAtRow(row, Qt.AlignLeft | Qt.AlignTop)
-
- tableView.targetRow = -1
- return
- }
-
- tableView.targetRow = row
- tableView.positionViewAtRow(row, Qt.AlignLeft | Qt.AlignTop)
- ensureTimer.start()
- }
-
- function ensureColumnIsVisible(column) {
- let columns = tableView.model.columnCount()
- let columnIsLoaded = tableView.isColumnLoaded(column)
-
- if (column < 0 || column >= columns || columnIsLoaded) {
- if (columnIsLoaded)
- tableView.positionViewAtColumn(column, Qt.AlignLeft | Qt.AlignTop)
-
- tableView.targetColumn = -1
- return
- }
-
- tableView.targetColumn = column
- tableView.positionViewAtColumn(column, Qt.AlignLeft | Qt.AlignTop)
- ensureTimer.start()
- }
-
- onMaxAvailableHeightChanged: resetSizeTimer.start()
- onMaxAvailableWidthChanged: resetSizeTimer.start()
- onChildrenWidthChanged: resetSizeTimer.start()
- onChildrenHeightChanged: resetSizeTimer.start()
-
- delegate: Rectangle {
- id: itemCell
-
- clip: true
- implicitWidth: 100
- implicitHeight: StudioTheme.Values.baseHeight
- color: itemSelected ? StudioTheme.Values.themeControlBackgroundInteraction
- : StudioTheme.Values.themeControlBackground
- border.width: 1
- border.color: {
- if (dataTypeWarning !== CollectionDetails.Warning.None)
- return StudioTheme.Values.themeWarning
-
- if (itemSelected)
- return StudioTheme.Values.themeControlOutlineInteraction
-
- return StudioTheme.Values.themeControlBackgroundInteraction
- }
-
- HelperWidgets.ToolTipArea {
- anchors.fill: parent
- text: root.model.warningToString(dataTypeWarning)
- enabled: dataTypeWarning !== CollectionDetails.Warning.None && text !== ""
- hoverEnabled: true
- acceptedButtons: Qt.NoButton
- }
-
- MouseArea {
- anchors.fill: parent
- acceptedButtons: Qt.RightButton
- onClicked: (mouse) => {
- let row = index % tableView.model.rowCount()
-
- tableView.model.selectRow(row)
- cellContextMenu.showMenu(row)
- }
- }
-
- Loader {
- id: cellContentLoader
-
- property int cellColumnType: columnType ? columnType : 0
-
- Component {
- id: cellText
-
- Text {
- text: display ?? ""
- color: itemSelected ? StudioTheme.Values.themeInteraction
- : StudioTheme.Values.themeTextColor
- leftPadding: 5
- topPadding: 3
- bottomPadding: 3
- font.pixelSize: StudioTheme.Values.baseFontSize
- horizontalAlignment: Text.AlignLeft
- verticalAlignment: Text.AlignVCenter
- elide: Text.ElideRight
- }
- }
-
- Component {
- id: colorEditorComponent
-
- ColorViewDelegate {}
- }
-
- function resetSource() {
- if (columnType === CollectionDetails.DataType.Color)
- cellContentLoader.sourceComponent = colorEditorComponent
- else
- cellContentLoader.sourceComponent = cellText
- }
-
- Component.onCompleted: resetSource()
- onCellColumnTypeChanged: resetSource()
- }
-
- TableView.editDelegate: CollectionDetailsEditDelegate {
- anchors {
- top: itemCell.top
- left: itemCell.left
- }
- Component.onCompleted: tableView.model.deselectAll()
- }
- }
-
- Timer {
- id: resetSizeTimer
-
- interval: 100
- repeat: false
- onTriggered: {
- let cWidth = Math.min(tableView.maxAvailableWidth, tableView.childrenWidth)
- let cHeight = Math.min(tableView.maxAvailableHeight, tableView.childrenHeight)
-
- if (tableView.contentWidth !== cWidth || tableView.contentHeight !== cHeight)
- tableView.returnToBounds()
- }
- }
-
- Timer {
- id: ensureTimer
-
- interval: 100
- repeat: false
- onTriggered: {
- tableView.ensureRowIsVisible(tableView.targetRow)
- tableView.ensureColumnIsVisible(tableView.targetColumn)
- }
- }
-
- Connections {
- target: tableView.model
-
- function onModelReset() {
- root.closeDialogs()
- tableView.clearColumnWidths()
- tableView.clearRowHeights()
- }
-
- function onRowsInserted(parent, first, last) {
- tableView.closeEditor()
- tableView.model.selectRow(first)
- tableView.ensureRowIsVisible(first)
- }
-
- function onColumnsInserted(parent, first, last) {
- tableView.closeEditor()
- tableView.model.selectColumn(first)
- tableView.ensureColumnIsVisible(first)
- }
-
- function onRowsRemoved(parent, first, last) {
- let nextRow = first - 1
- if (nextRow < 0 && tableView.model.rowCount(parent) > 0)
- nextRow = 0
-
- tableView.model.selectRow(nextRow)
- }
-
- function onColumnsRemoved(parent, first, last) {
- let nextColumn = first - 1
- if (nextColumn < 0 && tableView.model.columnCount(parent) > 0)
- nextColumn = 0
-
- tableView.model.selectColumn(nextColumn)
- }
- }
-
- HoverHandler { id: hoverHandler }
-
- ScrollBar.horizontal: StudioControls.TransientScrollBar {
- id: horizontalScrollBar
- style: StudioTheme.Values.viewStyle
- orientation: Qt.Horizontal
-
- show: (hoverHandler.hovered || tableView.focus || horizontalScrollBar.inUse)
- && horizontalScrollBar.isNeeded
- }
-
- ScrollBar.vertical: StudioControls.TransientScrollBar {
- id: verticalScrollBar
- style: StudioTheme.Values.viewStyle
- orientation: Qt.Vertical
-
- show: (hoverHandler.hovered || tableView.focus || verticalScrollBar.inUse)
- && verticalScrollBar.isNeeded
- }
- }
-
- HelperWidgets.IconButton {
- id: addColumnButton
-
- iconSize:16
- Layout.preferredWidth: 24
- Layout.preferredHeight: tableView.height
- Layout.minimumHeight: 24
- Layout.alignment: Qt.AlignLeft + Qt.AlignVCenter
-
- icon: StudioTheme.Constants.create_medium
- tooltip: "Add Column"
-
- onClicked: toolbar.addNewColumn()
- }
-
- HelperWidgets.IconButton {
- id: addRowButton
-
- iconSize:16
- Layout.preferredWidth: tableView.width
- Layout.preferredHeight: 24
- Layout.minimumWidth: 24
- Layout.alignment: Qt.AlignTop + Qt.AlignHCenter
-
- icon: StudioTheme.Constants.create_medium
- tooltip: "Add Row"
-
- onClicked: toolbar.addNewRow()
- }
-
- Item {
- Layout.fillWidth: true
- Layout.fillHeight: true
- }
- }
- }
-
- ColumnLayout {
- id: importsProblem
-
- visible: !topRow.visible && rootView.dataStoreExists && !rootView.projectImportExists
- width: parent.width
- anchors.verticalCenter: parent.verticalCenter
- clip: true
-
- Text {
- text: qsTr("Import the project to your design document to make the Model Editor enabled.")
- Layout.alignment: Qt.AlignCenter
- Layout.maximumWidth: parent.width
- leftPadding: StudioTheme.Values.collectionItemTextPadding
- rightPadding: StudioTheme.Values.collectionItemTextPadding
- color: StudioTheme.Values.themeTextColor
- font.pixelSize: StudioTheme.Values.mediumFontSize
- wrapMode: Text.Wrap
- }
-
- HelperWidgets.Button {
- text: qsTr("Enable DataStore (This will add the required import)")
- Layout.alignment: Qt.AlignCenter
- onClicked: rootView.addProjectImport()
- leftPadding: StudioTheme.Values.collectionItemTextPadding
- rightPadding: StudioTheme.Values.collectionItemTextPadding
- }
- }
-
- Text {
- anchors.centerIn: parent
- text: qsTr("There are no models in this project.\nAdd or import a model.")
- visible: !topRow.visible && !importsProblem.visible
- color: StudioTheme.Values.themeTextColor
- font.pixelSize: StudioTheme.Values.mediumFontSize
- }
-
- TextMetrics {
- id: headerTextMetrics
-
- font.pixelSize: StudioTheme.Values.baseFontSize
- text: "Xq"
- }
-
- StudioControls.Menu {
- id: cellContextMenu
-
- property int rowIndex: -1
-
- closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
-
- function showMenu(rowIndex) {
- cellContextMenu.rowIndex = rowIndex
- cellContextMenu.popup()
- }
-
- CellContextMenuItem {
- id: addRowAboveCellMenuItem
-
- itemText: qsTr("Add row above")
- itemIcon: StudioTheme.Constants.addrowabove_medium
- onTriggered: root.model.insertRow(cellContextMenu.rowIndex)
- }
- CellContextMenuItem {
- id: addRowBelowCellMenuItem
-
- itemText: qsTr("Add row below")
- itemIcon: StudioTheme.Constants.addrowbelow_medium
- onTriggered: root.model.insertRow(cellContextMenu.rowIndex + 1)
- }
- CellContextMenuItem {
- id: deleteRowCellMenuItem
-
- itemText: qsTr("Delete row")
- itemIcon: StudioTheme.Constants.deleterow_medium
- onTriggered: root.model.removeRows(cellContextMenu.rowIndex, 1)
- }
- }
-
- component HeaderDelegate: Rectangle {
- id: headerItem
-
- required property int selectedItem
- property alias horizontalAlignment: headerText.horizontalAlignment
- property alias verticalAlignment: headerText.verticalAlignment
-
- implicitWidth: headerText.implicitWidth
- implicitHeight: headerText.implicitHeight
- border.width: 1
- clip: true
-
- Text {
- id: headerText
-
- topPadding: headerView.topPadding
- bottomPadding: headerView.bottomPadding
- leftPadding: 5
- rightPadding: 5
- text: display
- font: headerTextMetrics.font
- color: StudioTheme.Values.themeTextColor
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- elide: Text.ElideRight
- }
-
- states: [
- State {
- name: "default"
- when: index !== selectedItem
- PropertyChanges {
- target: headerItem
- border.color: StudioTheme.Values.themeControlBackgroundInteraction
- }
-
- PropertyChanges {
- target: headerText
- font.bold: false
- }
- },
- State {
- name: "selected"
- when: index === selectedItem
-
- PropertyChanges {
- target: headerItem
- border.color: StudioTheme.Values.themeControlBackground
- }
-
- PropertyChanges {
- target: headerText
- font.bold: true
- }
- }
- ]
- }
-
- component CellContextMenuItem: StudioControls.MenuItem {
- id: cellContextMenuItemComponent
-
- property alias itemText: cellContextMenuText.text
- property alias itemIcon: cellContextMenuIcon.text
- text: ""
-
- implicitWidth: cellContextMenuRow.width
- implicitHeight: cellContextMenuRow.height
-
- Row {
- id: cellContextMenuRow
-
- property color textColor : cellContextMenuItemComponent.enabled
- ? cellContextMenuItemComponent.highlighted
- ? cellContextMenuItemComponent.style.text.selectedText
- : cellContextMenuItemComponent.style.text.idle
- : cellContextMenuItemComponent.style.text.disabled
-
- spacing: 2 * StudioTheme.Values.contextMenuHorizontalPadding
- height: StudioTheme.Values.defaultControlHeight
- leftPadding: StudioTheme.Values.contextMenuHorizontalPadding
- rightPadding: StudioTheme.Values.contextMenuHorizontalPadding
-
- Text {
- id: cellContextMenuIcon
-
- color: cellContextMenuRow.textColor
- text: StudioTheme.Constants.addrowabove_medium
- font.family: StudioTheme.Constants.iconFont.family
- font.pixelSize: StudioTheme.Values.myIconFontSize
- anchors.verticalCenter: parent.verticalCenter
- }
-
- Text {
- id: cellContextMenuText
-
- color: cellContextMenuRow.textColor
- anchors.verticalCenter: parent.verticalCenter
- }
- }
- }
-
- EditPropertyDialog {
- id: editPropertyDialog
- model: root.model
- }
-
- StudioControls.Dialog {
- id: deleteColumnDialog
-
- property int clickedIndex: -1
-
- title: qsTr("Delete Column")
- width: 400
-
- onAccepted: {
- root.model.removeColumn(clickedIndex)
- }
-
- function popUp(index)
- {
- deleteColumnDialog.clickedIndex = index
- deleteColumnDialog.open()
- }
-
- contentItem: ColumnLayout {
- spacing: StudioTheme.Values.sectionColumnSpacing
-
- Text {
- text: qsTr("Are you sure that you want to delete column \"%1\"?").arg(
- root.model.headerData(
- deleteColumnDialog.clickedIndex, Qt.Horizontal))
- color: StudioTheme.Values.themeTextColor
- }
-
- RowLayout {
- Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
- spacing: StudioTheme.Values.sectionRowSpacing
-
- HelperWidgets.Button {
- text: qsTr("Delete")
- onClicked: deleteColumnDialog.accept()
- }
-
- HelperWidgets.Button {
- text: qsTr("Cancel")
- onClicked: deleteColumnDialog.reject()
- }
- }
- }
- }
-}
diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml
deleted file mode 100644
index d963070536..0000000000
--- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml
+++ /dev/null
@@ -1,154 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import HelperWidgets 2.0 as HelperWidgets
-import StudioControls 1.0 as StudioControls
-import StudioTheme as StudioTheme
-import CollectionEditorBackend
-
-Item {
- id: root
-
- implicitWidth: 300
- implicitHeight: boundingRect.height + 3
-
- property color textColor
- readonly property string name: collectionName ?? ""
- readonly property bool isSelected: collectionIsSelected
- readonly property int id: index
-
- function rename(newName) {
- collectionName = newName
- }
-
- signal selectItem(int itemIndex)
- signal deleteItem()
- signal contextMenuRequested()
-
- Item {
- id: boundingRect
-
- width: parent.width
- height: itemLayout.height
- clip: true
-
- MouseArea {
- id: itemMouse
-
- anchors.fill: parent
- acceptedButtons: Qt.LeftButton
- propagateComposedEvents: true
- hoverEnabled: true
- onClicked: (event) => {
- if (!collectionIsSelected) {
- collectionIsSelected = true
- event.accepted = true
- }
- }
- }
-
- Rectangle {
- id: innerRect
- anchors.fill: parent
- }
-
- RowLayout {
- id: itemLayout
-
- width: parent.width
-
- Text {
- id: nameHolder
-
- Layout.fillWidth: true
- Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
- Layout.leftMargin: StudioTheme.Values.collectionItemTextSideMargin
- Layout.topMargin: StudioTheme.Values.collectionItemTextMargin
- Layout.bottomMargin: StudioTheme.Values.collectionItemTextMargin
-
- text: collectionName
- font.pixelSize: StudioTheme.Values.baseFontSize
- color: root.textColor
- topPadding: StudioTheme.Values.collectionItemTextPadding
- bottomPadding: StudioTheme.Values.collectionItemTextPadding
- elide: Text.ElideMiddle
- verticalAlignment: Text.AlignVCenter
- }
-
- Text {
- id: threeDots
-
- Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
- Layout.topMargin: StudioTheme.Values.collectionItemTextMargin
- Layout.bottomMargin: StudioTheme.Values.collectionItemTextMargin
- Layout.rightMargin: StudioTheme.Values.collectionItemTextSideMargin
-
- text: StudioTheme.Constants.more_medium
- font.family: StudioTheme.Constants.iconFont.family
- font.pixelSize: StudioTheme.Values.baseIconFontSize
- color: root.textColor
- padding: StudioTheme.Values.collectionItemTextPadding
-
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
-
- MouseArea {
- anchors.fill: parent
- acceptedButtons: Qt.RightButton | Qt.LeftButton
- onClicked: contextMenuRequested()
- }
- }
- }
- }
-
- states: [
- State {
- name: "default"
- when: !collectionIsSelected && !itemMouse.containsMouse
-
- PropertyChanges {
- target: innerRect
- opacity: 0.6
- color: StudioTheme.Values.themeControlBackground
- }
-
- PropertyChanges {
- target: root
- textColor: StudioTheme.Values.themeTextColor
- }
- },
- State {
- name: "hovered"
- when: !collectionIsSelected && itemMouse.containsMouse
-
- PropertyChanges {
- target: innerRect
- opacity: 0.8
- color: StudioTheme.Values.themeControlBackgroundHover
- }
-
- PropertyChanges {
- target: root
- textColor: StudioTheme.Values.themeTextColor
- }
- },
- State {
- name: "selected"
- when: collectionIsSelected
-
- PropertyChanges {
- target: innerRect
- opacity: 1
- color: StudioTheme.Values.themeIconColorSelected
- }
-
- PropertyChanges {
- target: root
- textColor: StudioTheme.Values.themeTextSelectedTextColor
- }
- }
- ]
-}
diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml
deleted file mode 100644
index 2b95abfc4f..0000000000
--- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml
+++ /dev/null
@@ -1,215 +0,0 @@
-// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import HelperWidgets 2.0 as HelperWidgets
-import StudioControls 1.0 as StudioControls
-import StudioTheme as StudioTheme
-import CollectionEditorBackend
-
-ListView {
- id: root
-
- model: CollectionEditorBackend.model
- clip: true
-
- function closeDialogs() {
- currentCollection.dereference()
- collectionMenu.close()
- deleteDialog.reject()
- renameDialog.reject()
- }
-
- delegate: CollectionItem {
- implicitWidth: root.width
- onDeleteItem: root.model.removeRow(index)
- onContextMenuRequested: collectionMenu.openMenu(this)
- }
-
- QtObject {
- id: currentCollection
-
- property CollectionItem item
- readonly property string name: item ? item.name : ""
- readonly property bool selected: item ? item.isSelected : false
- readonly property int index: item ? item.id : -1
-
- function rename(newName) {
- if (item)
- item.rename(newName)
- }
-
- function deleteItem() {
- if (item)
- item.deleteItem()
- }
-
- function dereference() {
- item = null
- }
- }
-
- StudioControls.Menu {
- id: collectionMenu
-
- closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
-
- function openMenu(item) {
- currentCollection.item = item
- popup()
- }
-
- StudioControls.MenuItem {
- text: qsTr("Delete")
- shortcut: StandardKey.Delete
- onTriggered: deleteDialog.open()
- }
-
- StudioControls.MenuItem {
- text: qsTr("Rename")
- shortcut: StandardKey.Replace
- onTriggered: renameDialog.open()
- }
-
- StudioControls.MenuItem {
- text: qsTr("Assign to the selected node")
- enabled: CollectionEditorBackend.rootView.targetNodeSelected
- onTriggered: rootView.assignCollectionToSelectedNode(currentCollection.name)
- }
- }
-
- StudioControls.Dialog {
- id: deleteDialog
-
- title: qsTr("Deleting the model")
- clip: true
-
- onAccepted: currentCollection.deleteItem()
-
- contentItem: ColumnLayout {
- id: deleteDialogContent // Keep the id here even if it's not used, because the dialog might lose implicitSize
-
- width: 300
- spacing: 2
-
- Text {
- Layout.fillWidth: true
-
- wrapMode: Text.WordWrap
- color: StudioTheme.Values.themeTextColor
- text: qsTr("Are you sure that you want to delete model \"%1\"?"
- + "\nThe model will be deleted permanently.").arg(currentCollection.name)
-
- }
-
- Spacer {}
-
- RowLayout {
- spacing: StudioTheme.Values.sectionRowSpacing
- Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
- Layout.fillWidth: true
- Layout.preferredHeight: 40
-
- HelperWidgets.Button {
- text: qsTr("Delete")
- onClicked: deleteDialog.accept()
- }
-
- HelperWidgets.Button {
- text: qsTr("Cancel")
- onClicked: deleteDialog.reject()
- }
- }
- }
- }
-
- StudioControls.Dialog {
- id: renameDialog
-
- title: qsTr("Rename model")
-
- onAccepted: {
- if (newNameField.text !== "")
- currentCollection.rename(newNameField.text)
- }
-
- onOpened: {
- newNameField.text = currentCollection.name
- }
-
- contentItem: ColumnLayout {
- spacing: 2
-
- Text {
- text: qsTr("Previous name: " + currentCollection.name)
- color: StudioTheme.Values.themeTextColor
- }
-
- Spacer {}
-
- Text {
- Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
- text: qsTr("New name:")
- color: StudioTheme.Values.themeTextColor
- }
-
- StudioControls.TextField {
- id: newNameField
-
- Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
- Layout.fillWidth: true
-
- actionIndicator.visible: false
- translationIndicator.visible: false
- validator: newNameValidator
-
- Keys.onEnterPressed: renameDialog.accept()
- Keys.onReturnPressed: renameDialog.accept()
- Keys.onEscapePressed: renameDialog.reject()
-
- onTextChanged: {
- btnRename.enabled = newNameField.text !== ""
- }
- }
-
- Spacer {}
-
- RowLayout {
- Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
- spacing: StudioTheme.Values.sectionRowSpacing
-
- HelperWidgets.Button {
- id: btnRename
-
- text: qsTr("Rename")
- onClicked: renameDialog.accept()
- }
-
- HelperWidgets.Button {
- text: qsTr("Cancel")
- onClicked: renameDialog.reject()
- }
- }
- }
- }
-
- Connections {
- target: root.model
-
- function onModelReset() {
- root.closeDialogs()
- }
- }
-
- RegularExpressionValidator {
- id: newNameValidator
- regularExpression: /^\w+$/
- }
-
- component Spacer: Item {
- implicitWidth: 1
- implicitHeight: StudioTheme.Values.columnGap
- }
-}
diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml
deleted file mode 100644
index 9d483037ac..0000000000
--- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import HelperWidgets as HelperWidgets
-import StudioTheme as StudioTheme
-import CollectionEditorBackend
-
-Item {
- id: root
- focus: true
-
- property var rootView: CollectionEditorBackend.rootView
- property var model: CollectionEditorBackend.model
- property var collectionDetailsModel: CollectionEditorBackend.collectionDetailsModel
- property var collectionDetailsSortFilterModel: CollectionEditorBackend.collectionDetailsSortFilterModel
-
- function showWarning(title, message) {
- warningDialog.title = title
- warningDialog.message = message
- warningDialog.open()
- }
-
- // called from C++ when using the delete key
- function deleteSelectedCollection() {
- print("TODO: deleteSelectedCollection")
- }
-
- function closeDialogs() {
- importDialog.reject()
- newCollection.reject()
- warningDialog.reject()
- }
-
- ImportDialog {
- id: importDialog
-
- backendValue: root.rootView
- anchors.centerIn: parent
- }
-
- NewCollectionDialog {
- id: newCollection
-
- anchors.centerIn: parent
- }
-
- Message {
- id: warningDialog
-
- title: ""
- message: ""
- }
-
- Rectangle {
- // Covers the toolbar color on top to prevent the background
- // color for the margin of splitter
-
- anchors.top: parent.top
- anchors.left: parent.left
- anchors.right: parent.right
- height: topToolbar.height
- color: topToolbar.color
- }
-
- SplitView {
- id: splitView
-
- readonly property bool isHorizontal: splitView.orientation === Qt.Horizontal
-
- orientation: width >= 500 ? Qt.Horizontal : Qt.Vertical
- anchors.fill: parent
-
- onOrientationChanged: detailsView.closeDialogs()
-
- handle: Item {
- id: handleDelegate
-
- property color color: SplitHandle.pressed ? StudioTheme.Values.themeControlOutlineInteraction
- : SplitHandle.hovered ? StudioTheme.Values.themeControlOutlineHover
- : StudioTheme.Values.themeControlOutline
-
- implicitWidth: 1
- implicitHeight: 1
-
- Rectangle {
- id: handleRect
-
- property real verticalMargin: splitView.isHorizontal ? StudioTheme.Values.splitterMargin : 0
- property real horizontalMargin: splitView.isHorizontal ? 0 : StudioTheme.Values.splitterMargin
-
- anchors.fill: parent
- anchors.topMargin: handleRect.verticalMargin
- anchors.bottomMargin: handleRect.verticalMargin
- anchors.leftMargin: handleRect.horizontalMargin
- anchors.rightMargin: handleRect.horizontalMargin
-
- color: handleDelegate.color
- }
-
- containmentMask: Item {
- x: splitView.isHorizontal ? ((handleDelegate.width - width) / 2) : 0
- y: splitView.isHorizontal ? 0 : ((handleDelegate.height - height) / 2)
- height: splitView.isHorizontal ? handleDelegate.height : StudioTheme.Values.borderHover
- width: splitView.isHorizontal ? StudioTheme.Values.borderHover : handleDelegate.width
- }
- }
-
- ColumnLayout {
- id: collectionsSideBar
- spacing: 0
-
- SplitView.minimumWidth: 200
- SplitView.maximumWidth: 450
- SplitView.minimumHeight: 200
- SplitView.maximumHeight: 400
- SplitView.fillWidth: !splitView.isHorizontal
- SplitView.fillHeight: splitView.isHorizontal
-
- Rectangle {
- id: topToolbar
- color: StudioTheme.Values.themeToolbarBackground
-
- Layout.preferredHeight: StudioTheme.Values.toolbarHeight
- Layout.fillWidth: true
- Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
-
- Text {
- anchors.verticalCenter: parent.verticalCenter
- anchors.left: parent.left
- anchors.leftMargin: StudioTheme.Values.toolbarHorizontalMargin
-
- text: qsTr("Data Models")
- font.pixelSize: StudioTheme.Values.baseFontSize
- color: StudioTheme.Values.themeTextColor
- }
-
- HelperWidgets.AbstractButton {
- id: importCollectionButton
-
- anchors.verticalCenter: parent.verticalCenter
- anchors.right: parent.right
- anchors.rightMargin: StudioTheme.Values.toolbarHorizontalMargin
-
- style: StudioTheme.Values.viewBarButtonStyle
- buttonIcon: StudioTheme.Constants.import_medium
- tooltip: qsTr("Import a model")
-
- onClicked: importDialog.open()
- }
- }
-
- CollectionListView { // Model Groups
- id: collectionListView
-
- Layout.fillWidth: true
- Layout.minimumHeight: bottomSpacer.isExpanded ? 150 : 0
- Layout.fillHeight: !bottomSpacer.isExpanded
- Layout.preferredHeight: contentHeight
- Layout.maximumHeight: contentHeight
- Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
- }
-
- HelperWidgets.IconButton {
- id: addCollectionButton
-
- iconSize: 16
- Layout.fillWidth: true
- Layout.minimumWidth: 24
- Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
-
- tooltip: qsTr("Add a new model")
- icon: StudioTheme.Constants.create_medium
- onClicked: newCollection.open()
- }
-
- Item {
- id: bottomSpacer
-
- readonly property bool isExpanded: height > 0
- Layout.minimumWidth: 1
- Layout.fillHeight: true
- }
- }
-
- CollectionDetailsView {
- id: detailsView
-
- model: root.collectionDetailsModel
- backend: root.model
- sortedModel: root.collectionDetailsSortFilterModel
- SplitView.fillHeight: true
- SplitView.fillWidth: true
- }
- }
-
- Connections {
- target: root.model
-
- function onModelReset() {
- root.closeDialogs()
- }
- }
-}
diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml
deleted file mode 100644
index 16e55acfb4..0000000000
--- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml
+++ /dev/null
@@ -1,296 +0,0 @@
-// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Shapes
-import QtQuick.Templates as T
-import HelperWidgets 2.0 as HelperWidgets
-import StudioTheme as StudioTheme
-import StudioControls as StudioControls
-import QtQuickDesignerTheme
-import QtQuickDesignerColorPalette
-
-Row {
- id: colorEditor
-
- property color color
- property bool supportGradient: false
-
- property QtObject backendValue: QtObject {
- property color value: edit
- readonly property color editColor: edit
-
- function resetValue() {
- if (value)
- value = ""
- }
-
- onValueChanged: {
- if (editColor !== value)
- edit = value
- }
- }
-
- property variant value: {
- if (!colorEditor.backendValue || !colorEditor.backendValue.value)
- return "white" // default color for Rectangle
-
- if (colorEditor.isVector3D) {
- return Qt.rgba(colorEditor.backendValue.value.x,
- colorEditor.backendValue.value.y,
- colorEditor.backendValue.value.z, 1)
- }
-
- return colorEditor.backendValue.value
- }
-
- property alias gradientPropertyName: popupDialog.gradientPropertyName
-
- property alias gradientThumbnail: gradientThumbnail
- property alias shapeGradientThumbnail: shapeGradientThumbnail
-
- property bool shapeGradients: false
- property bool isVector3D: false
- property color originalColor
-
- property bool __block: false
-
- function resetShapeColor() {
- colorEditor.backendValue.resetValue()
- }
-
- function writeColor() {
- if (colorEditor.isVector3D) {
- colorEditor.backendValue.value = Qt.vector3d(colorEditor.color.r,
- colorEditor.color.g,
- colorEditor.color.b)
- } else {
- colorEditor.backendValue.value = colorEditor.color
- }
- }
-
- function initEditor() {
- colorEditor.syncColor()
- }
-
- // Syncing color from backend to frontend and block reflection
- function syncColor() {
- colorEditor.__block = true
- colorEditor.color = colorEditor.value
- hexTextField.syncColor()
- colorEditor.__block = false
- }
-
- Connections {
- id: backendConnection
-
- target: colorEditor
-
- function onValueChanged() {
- if (popupDialog.isSolid())
- colorEditor.syncColor()
- }
-
- function onBackendValueChanged() {
- if (popupDialog.isSolid())
- colorEditor.syncColor()
- }
- }
-
- Timer {
- id: colorEditorTimer
-
- repeat: false
- interval: 100
- running: false
- onTriggered: {
- backendConnection.enabled = false
- colorEditor.writeColor()
- hexTextField.syncColor()
- backendConnection.enabled = true
- }
- }
-
- onColorChanged: {
- if (colorEditor.__block)
- return
-
- if (!popupDialog.isInValidState)
- return
-
- popupDialog.commitToGradient()
-
- // Delay setting the color to keep ui responsive
- if (popupDialog.isSolid())
- colorEditorTimer.restart()
- }
-
- Rectangle {
- id: preview
-
- implicitWidth: StudioTheme.Values.twoControlColumnWidth
- implicitHeight: StudioTheme.Values.height
- color: colorEditor.color
- border.color: StudioTheme.Values.themeControlOutline
- border.width: StudioTheme.Values.border
-
- Rectangle {
- id: gradientThumbnail
-
- anchors.fill: parent
- anchors.margins: StudioTheme.Values.border
- visible: !popupDialog.isSolid()
- && !colorEditor.shapeGradients
- && popupDialog.isLinearGradient()
- }
-
- Shape {
- id: shape
-
- anchors.fill: parent
- anchors.margins: StudioTheme.Values.border
- visible: !popupDialog.isSolid() && colorEditor.shapeGradients
-
- ShapePath {
- id: shapeGradientThumbnail
-
- startX: shape.x - 1
- startY: shape.y - 1
- strokeWidth: -1
- strokeColor: "green"
-
- PathLine {
- x: shape.x - 1
- y: shape.height
- }
- PathLine {
- x: shape.width
- y: shape.height
- }
- PathLine {
- x: shape.width
- y: shape.y - 1
- }
- }
- }
-
- Image {
- anchors.fill: parent
- source: "qrc:/navigator/icon/checkers.png"
- fillMode: Image.Tile
- z: -1
- }
-
- MouseArea {
- anchors.fill: parent
- onClicked: {
- popupDialog.visibility ? popupDialog.close() : popupDialog.open()
- forceActiveFocus()
- }
- }
-
- StudioControls.PopupDialog {
- id: popupDialog
-
- property bool isInValidState: loader.active ? popupDialog.loaderItem.isInValidState : true
- property QtObject loaderItem: loader.item
- property string gradientPropertyName
-
- keepOpen: loader.item?.eyeDropperActive ?? false
-
- width: 260
-
- function commitToGradient() {
- if (!loader.active)
- return
-
- if (colorEditor.supportGradient && popupDialog.loaderItem.gradientModel.hasGradient) {
- var hexColor = convertColorToString(colorEditor.color)
- hexTextField.text = hexColor
- colorEditor.backendValue.value = hexColor
- popupDialog.loaderItem.commitGradientColor()
- }
- }
-
- function isSolid() {
- if (!loader.active)
- return true
-
- return popupDialog.loaderItem.isSolid()
- }
-
- function isLinearGradient(){
- if (!loader.active)
- return false
-
- return popupDialog.loaderItem.isLinearGradient()
- }
-
- function ensureLoader() {
- if (!loader.active)
- loader.active = true
- }
-
- function open() {
- popupDialog.ensureLoader()
- popupDialog.loaderItem.initEditor()
- popupDialog.show(preview)
- }
-
- function determineActiveColorMode() {
- if (loader.active && popupDialog.loaderItem)
- popupDialog.loaderItem.determineActiveColorMode()
- else
- colorEditor.syncColor()
- }
-
- Loader {
- id: loader
-
- active: colorEditor.supportGradient
-
- sourceComponent: HelperWidgets.ColorEditorPopup {
- shapeGradients: colorEditor.shapeGradients
- supportGradient: colorEditor.supportGradient
- width: popupDialog.contentWidth
- }
-
- onLoaded: {
- popupDialog.loaderItem.initEditor()
- popupDialog.titleBar = loader.item.titleBarContent
- }
- }
- }
- }
-
- HelperWidgets.LineEdit {
- id: hexTextField
- implicitWidth: StudioTheme.Values.twoControlColumnWidth
- + StudioTheme.Values.actionIndicatorWidth
- width: hexTextField.implicitWidth
- enabled: popupDialog.isSolid()
- writeValueManually: true
- validator: RegularExpressionValidator {
- regularExpression: /#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?/g
- }
- showTranslateCheckBox: false
- showExtendedFunctionButton: false
- indicatorVisible: false
-
- onAccepted: colorEditor.color = hexTextField.text
- onCommitData: {
- colorEditor.color = hexTextField.text
- if (popupDialog.isSolid())
- colorEditor.writeColor()
- }
-
- function syncColor() {
- hexTextField.text = colorEditor.color
- }
- }
-
- Component.onCompleted: popupDialog.determineActiveColorMode()
-
- onBackendValueChanged: popupDialog.determineActiveColorMode()
-}
diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml
deleted file mode 100644
index 546dc5d770..0000000000
--- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-import QtQuick
-import QtQuick.Layouts
-import StudioTheme 1.0 as StudioTheme
-import StudioControls 1.0 as StudioControls
-import HelperWidgets 2.0 as HelperWidgets
-import CollectionDetails
-
-StudioControls.Dialog {
- id: root
-
- required property var model
- property int __propertyIndex: -1
- property string __currentName
-
- title: qsTr("Edit Column")
-
- function openDialog(index, initialPosition) {
- root.__propertyIndex = index
-
- if (root.__propertyIndex < 0)
- return
-
- root.__currentName = root.model.propertyName(root.__propertyIndex)
- nameTextField.text = root.__currentName
- nameTextField.selectAll()
- nameTextField.forceActiveFocus()
-
- typeComboBox.initialType = root.model.propertyType(root.__propertyIndex)
- typeComboBox.currentIndex = typeComboBox.find(typeComboBox.initialType)
-
- let newPoint = mapFromGlobal(initialPosition.x, initialPosition.y)
- x = newPoint.x
- y = newPoint.y
-
- root.open()
- }
-
- onWidthChanged: {
- if (visible && x > parent.width)
- root.close()
- }
-
- onAccepted: {
- if (nameTextField.text !== "" && nameTextField.text !== root.__currentName)
- root.model.renameColumn(root.__propertyIndex, nameTextField.text)
-
- if (typeComboBox.initialType !== typeComboBox.currentText)
- root.model.setPropertyType(root.__propertyIndex, typeComboBox.currentText)
- }
-
- contentItem: Column {
- spacing: 5
-
- Grid {
- columns: 2
- rows: 2
- rowSpacing: 2
- columnSpacing: 25
- verticalItemAlignment: Grid.AlignVCenter
-
- Text {
- text: qsTr("Name")
- color: StudioTheme.Values.themeTextColor
- verticalAlignment: Text.AlignVCenter
- }
-
- StudioControls.TextField {
- id: nameTextField
-
- actionIndicator.visible: false
- translationIndicator.visible: false
-
- Keys.onEnterPressed: root.accept()
- Keys.onReturnPressed: root.accept()
- Keys.onEscapePressed: root.reject()
-
- validator: RegularExpressionValidator {
- regularExpression: /^\w+$/
- }
- }
-
- Text {
- text: qsTr("Type")
- color: StudioTheme.Values.themeTextColor
- }
-
- StudioControls.ComboBox {
- id: typeComboBox
-
- property string initialType
-
- model: CollectionDataTypeModel{}
- textRole: "display"
- tooltipRole: "toolTip"
- actionIndicatorVisible: false
- }
- }
-
- Rectangle {
- id: warningBox
-
- visible: typeComboBox.initialType !== typeComboBox.currentText
- color: "transparent"
- clip: true
- border.color: StudioTheme.Values.themeWarning
- width: parent.width
- height: warning.height
-
- Row {
- id: warning
-
- padding: 5
- spacing: 5
-
- HelperWidgets.IconLabel {
- icon: StudioTheme.Constants.warning_medium
- anchors.verticalCenter: parent.verticalCenter
- }
-
- Text {
- text: qsTr("Conversion from %1 to %2 may lead to data loss")
- .arg(typeComboBox.initialType)
- .arg(typeComboBox.currentText)
-
- width: warningBox.width - 20
-
- color: StudioTheme.Values.themeTextColor
- wrapMode: Text.WordWrap
- }
- }
- }
-
- Row {
- height: 40
- spacing: 5
-
- HelperWidgets.Button {
- id: editButton
-
- text: qsTr("Apply")
- enabled: nameTextField.text !== ""
- anchors.bottom: parent.bottom
-
- onClicked: root.accept()
- }
-
- HelperWidgets.Button {
- text: qsTr("Cancel")
- anchors.bottom: parent.bottom
-
- onClicked: root.reject()
- }
- }
- }
-}
diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/IconTextButton.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/IconTextButton.qml
deleted file mode 100644
index fe9d094349..0000000000
--- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/IconTextButton.qml
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import StudioTheme as StudioTheme
-
-Rectangle {
- id: root
-
- required property string text
- required property string icon
-
- property alias tooltip: toolTip.text
- property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle
- property int fontSize: StudioTheme.Values.baseFontSize
-
- implicitHeight: style.squareControlSize.height
- implicitWidth: rowAlign.width
-
- signal clicked()
-
- RowLayout {
- id: rowAlign
-
- anchors.verticalCenter: parent.verticalCenter
- spacing: StudioTheme.Values.inputHorizontalPadding
-
- Text {
- id: iconItem
-
- Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
- text: root.icon
- color: StudioTheme.Values.themeTextColor
- font.family: StudioTheme.Constants.iconFont.family
- font.pixelSize: StudioTheme.Values.bigFont
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- leftPadding: StudioTheme.Values.inputHorizontalPadding
- }
-
- Text {
- id: textItem
-
- Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
- text: root.text
- color: StudioTheme.Values.themeTextColor
- font.family: StudioTheme.Constants.font.family
- font.pixelSize: StudioTheme.Values.baseFontSize
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- rightPadding: StudioTheme.Values.inputHorizontalPadding
- }
- }
-
- MouseArea {
- id: mouseArea
- anchors.fill: parent
- hoverEnabled: true
- onClicked: root.clicked()
- }
-
- ToolTip {
- id: toolTip
-
- visible: mouseArea.containsMouse && text !== ""
- delay: 1000
- }
-
- states: [
- State {
- name: "default"
- when: !mouseArea.pressed && !mouseArea.containsMouse
- PropertyChanges {
- target: root
- color: StudioTheme.Values.themeControlBackground
- }
- },
- State {
- name: "Pressed"
- when: mouseArea.pressed
- PropertyChanges {
- target: root
- color: StudioTheme.Values.themeControlBackgroundInteraction
- }
- },
- State {
- name: "Hovered"
- when: !mouseArea.pressed && mouseArea.containsMouse
- PropertyChanges {
- target: root
- color: StudioTheme.Values.themeControlBackgroundHover
- }
- }
- ]
-}
diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml
deleted file mode 100644
index 21a5e4e8e0..0000000000
--- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml
+++ /dev/null
@@ -1,225 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import Qt.labs.platform as PlatformWidgets
-import HelperWidgets as HelperWidgets
-import StudioControls as StudioControls
-import StudioTheme as StudioTheme
-
-StudioControls.Dialog {
- id: root
-
- title: qsTr("Import a model")
- anchors.centerIn: parent
- closePolicy: Popup.CloseOnEscape
- modal: true
-
- required property var backendValue
-
- property bool fileExists: false
-
- onOpened: {
- collectionName.text = "Model"
- fileName.text = qsTr("Model path")
- fileName.selectAll()
- fileName.forceActiveFocus()
- }
-
- onRejected: {
- fileName.text = ""
- }
-
- function acceptIfIsValid() {
- if (btnImport.enabled)
- btnImport.onClicked()
- }
-
- RegularExpressionValidator {
- id: fileNameValidator
- regularExpression: /^(\w[^*><?|]*)[^/\\:*><?|]$/
- }
-
- PlatformWidgets.FileDialog {
- id: fileDialog
- nameFilters : ["All Model Files (*.json *.csv)",
- "JSON Files (*.json)",
- "Comma-Separated Values (*.csv)"]
-
- title: qsTr("Select a model file")
- fileMode: PlatformWidgets.FileDialog.OpenFile
- acceptLabel: qsTr("Open")
-
- onAccepted: fileName.text = fileDialog.file
- }
-
- Message {
- id: creationFailedDialog
-
- title: qsTr("Could not load the file")
- message: qsTr("An error occurred while trying to load the file.")
-
- onClosed: root.reject()
- }
-
- component Spacer: Item {
- implicitWidth: 1
- implicitHeight: StudioTheme.Values.columnGap
- }
-
- contentItem: ColumnLayout {
- spacing: 2
-
- Keys.onEnterPressed: root.acceptIfIsValid()
- Keys.onReturnPressed: root.acceptIfIsValid()
- Keys.onEscapePressed: root.reject()
-
- Text {
- text: qsTr("File name")
- color: StudioTheme.Values.themeTextColor
- }
-
- RowLayout {
- spacing: StudioTheme.Values.sectionRowSpacing
-
- Layout.fillWidth: true
-
- StudioControls.TextField {
- id: fileName
-
- Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
- Layout.fillWidth: true
-
- actionIndicator.visible: false
- translationIndicator.visible: false
- validator: fileNameValidator
-
- onTextChanged: root.fileExists = root.backendValue.isValidUrlToImport(fileName.text)
- }
-
- HelperWidgets.Button {
- id: fileDialogButton
-
- Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
-
- text: qsTr("Open")
- onClicked: fileDialog.open()
- }
- }
-
- Spacer {}
-
- Text {
- text: qsTr("The model name")
- color: StudioTheme.Values.themeTextColor
- }
-
- StudioControls.TextField {
- id: collectionName
-
- Layout.fillWidth: true
-
- actionIndicator.visible: false
- translationIndicator.visible: false
- validator: RegularExpressionValidator {
- regularExpression: /^[\w ]+$/
- }
- }
-
- Spacer { implicitHeight: StudioTheme.Values.controlLabelGap }
-
- Label {
- id: fieldErrorText
-
- Layout.fillWidth: true
-
- color: StudioTheme.Values.themeTextColor
- wrapMode: Label.WordWrap
- padding: 5
-
- background: Rectangle {
- color: "transparent"
- border.width: StudioTheme.Values.border
- border.color: StudioTheme.Values.themeWarning
- }
-
- states: [
- State {
- name: "default"
- when: fileName.text !== "" && collectionName.text !== ""
-
- PropertyChanges {
- target: fieldErrorText
- text: ""
- visible: false
- }
- },
- State {
- name: "fileError"
- when: fileName.text === ""
-
- PropertyChanges {
- target: fieldErrorText
- text: qsTr("File name can not be empty")
- visible: true
- }
- },
- State {
- name: "collectionNameError"
- when: collectionName.text === ""
-
- PropertyChanges {
- target: fieldErrorText
- text: qsTr("The model name can not be empty")
- visible: true
- }
- }
- ]
- }
-
- Spacer {}
-
- StudioControls.CheckBox {
- id: csvFirstRowIsHeader
-
- visible: root.fileExists && fileName.text.endsWith(".csv")
- text: qsTr("Consider first row as headers")
- checked: true
- actionIndicatorVisible: false
- }
-
- Spacer {}
-
- RowLayout {
- spacing: StudioTheme.Values.sectionRowSpacing
-
- Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
-
- HelperWidgets.Button {
- id: btnImport
-
- text: qsTr("Import")
- enabled: root.fileExists && collectionName.text !== ""
-
- onClicked: {
- let collectionImported = root.backendValue.importFile(
- collectionName.text,
- fileName.text,
- csvFirstRowIsHeader.checked)
-
- if (collectionImported)
- root.accept()
- else
- creationFailedDialog.open()
- }
- }
-
- HelperWidgets.Button {
- text: qsTr("Cancel")
- onClicked: root.reject()
- }
- }
- }
-}
diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/Message.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/Message.qml
deleted file mode 100644
index f6e7af4560..0000000000
--- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/Message.qml
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import HelperWidgets 2.0 as HelperWidgets
-import StudioControls 1.0 as StudioControls
-import StudioTheme as StudioTheme
-
-StudioControls.Dialog {
- id: root
-
- required property string message
-
- anchors.centerIn: parent
- closePolicy: Popup.CloseOnEscape
- implicitWidth: 300
- modal: true
-
- contentItem: ColumnLayout {
- spacing: StudioTheme.Values.sectionColumnSpacing
-
- Text {
- Layout.fillWidth: true
- text: root.message
- color: StudioTheme.Values.themeTextColor
- wrapMode: Text.WordWrap
- leftPadding: 10
- rightPadding: 10
- }
-
- HelperWidgets.Button {
- Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
-
- text: qsTr("Close")
- onClicked: root.reject()
- }
- }
-
- onOpened: root.forceActiveFocus()
-}
diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/NewCollectionDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/NewCollectionDialog.qml
deleted file mode 100644
index 6f567a775c..0000000000
--- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/NewCollectionDialog.qml
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import HelperWidgets as HelperWidgets
-import StudioControls as StudioControls
-import StudioTheme as StudioTheme
-import CollectionEditorBackend
-
-StudioControls.Dialog {
- id: root
-
- property bool nameExists: false
- readonly property bool isValid: collectionName.text !== "" && !root.nameExists
-
- title: qsTr("Add a new model")
-
- closePolicy: Popup.CloseOnEscape
- modal: true
-
- onOpened: {
- collectionName.text = CollectionEditorBackend.model.getUniqueCollectionName()
- }
-
- onRejected: {
- collectionName.text = ""
- }
-
- onAccepted: {
- if (root.isValid)
- root.CollectionEditorBackend.rootView.addCollectionToDataStore(collectionName.text);
- }
-
- ColumnLayout {
- spacing: 5
-
- NameField {
- text: qsTr("Name")
- }
-
- StudioControls.TextField {
- id: collectionName
-
- Layout.fillWidth: true
-
- actionIndicator.visible: false
- translationIndicator.visible: false
- validator: RegularExpressionValidator {
- regularExpression: /^[\w ]+$/
- }
-
- Keys.onEnterPressed: btnCreate.onClicked()
- Keys.onReturnPressed: btnCreate.onClicked()
- Keys.onEscapePressed: root.reject()
-
- onTextChanged: {
- root.nameExists = CollectionEditorBackend.model.collectionExists(collectionName.text)
- }
- }
-
- ErrorField {
- id: errorField
- text: {
- if (collectionName.text === "")
- return qsTr("Name can not be empty")
- else if (root.nameExists)
- return qsTr("Name '%1' already exists").arg(collectionName.text)
- else
- return ""
- }
- }
-
- Spacer {}
-
- Row {
- spacing: StudioTheme.Values.sectionRowSpacing
-
- HelperWidgets.Button {
- id: btnCreate
-
- text: qsTr("Create")
- enabled: root.isValid
- onClicked: root.accept()
- }
-
- HelperWidgets.Button {
- text: qsTr("Cancel")
- onClicked: root.reject()
- }
- }
- }
-
- component NameField: Text {
- color: StudioTheme.Values.themeTextColor
- font.family: StudioTheme.Constants.font.family
- font.pixelSize: StudioTheme.Values.baseIconFontSize
- }
-
- component ErrorField: Text {
- color: StudioTheme.Values.themeError
- font.family: StudioTheme.Constants.font.family
- font.pixelSize: StudioTheme.Values.baseIconFontSize
- }
-
- component Spacer: Item {
- width: 1
- height: StudioTheme.Values.columnGap
- }
-}
diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml
index 965f31d21a..299234127a 100644
--- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml
+++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml
@@ -11,6 +11,8 @@ StudioControls.PopupDialog {
property alias backend: form.backend
+ keepOpen: form.keepOpen
+
titleBar: Row {
spacing: 30 // TODO
anchors.fill: parent
@@ -43,6 +45,8 @@ StudioControls.PopupDialog {
ConnectionsDialogForm {
id: form
+ parentWindow: root.window
+
Connections {
target: root.backend
function onPopupShouldClose() {
diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml
index 603903dbdd..a799165375 100644
--- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml
+++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml
@@ -16,6 +16,9 @@ Column {
property var backend
+ property bool keepOpen: expressionDialogLoader.visible
+ property Window parentWindow: null
+
width: parent.width
spacing: root.verticalSpacing
@@ -267,10 +270,8 @@ Column {
horizontalAlignment: code.lineCount === 1 ? Text.AlignHCenter : Text.AlignLeft
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
-
}
-
Loader {
id: expressionDialogLoader
parent: editor
@@ -297,19 +298,19 @@ Column {
id: bindingEditor
onRejected: {
- hideWidget()
+ bindingEditor.hideWidget()
expressionDialogLoader.visible = false
}
onAccepted: {
backend.setNewSource(bindingEditor.text)
- hideWidget()
+ bindingEditor.hideWidget()
expressionDialogLoader.visible = false
}
}
}
- } // loader
- } // rect
- } //col 2
-}//col1
+ }
+ }
+ }
+}
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml
index 2c98b58adc..d3eb29563d 100644
--- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml
@@ -115,25 +115,25 @@ Item {
width: parent.width
height: StudioTheme.Values.toolbarHeight
- Component.onCompleted: {
- var tabs = [
- { name: qsTr("Materials"), icon: StudioTheme.Constants.material_medium },
- { name: qsTr("Textures"), icon: StudioTheme.Constants.textures_medium },
- { name: qsTr("Environments"), icon: StudioTheme.Constants.languageList_medium },
- { name: qsTr("Effects"), icon: StudioTheme.Constants.effects }
- ];
- if (ContentLibraryBackend.rootView.userBundleEnabled())
- tabs.push({ name: qsTr("User Assets"), icon: StudioTheme.Constants.effects });
- tabBar.tabsModel = tabs;
- }
+ tabsModel: [
+ { name: qsTr("Materials"), icon: StudioTheme.Constants.material_medium },
+ { name: qsTr("Textures"), icon: StudioTheme.Constants.textures_medium },
+ { name: qsTr("Environments"), icon: StudioTheme.Constants.languageList_medium },
+ { name: qsTr("Effects"), icon: StudioTheme.Constants.effects },
+ { name: qsTr("User Assets"), icon: StudioTheme.Constants.effects } // TODO: update icon
+ ]
}
}
}
- UnimportBundleMaterialDialog {
+ UnimportBundleItemDialog {
id: confirmUnimportDialog
}
+ DeleteBundleItemDialog {
+ id: confirmDeleteDialog
+ }
+
StackLayout {
id: stackLayout
width: root.width
@@ -246,6 +246,12 @@ Item {
confirmUnimportDialog.open()
}
+ onRemoveFromContentLib: (bundleItem) => {
+ confirmDeleteDialog.targetBundleItem = bundleItem
+ confirmDeleteDialog.targetBundleLabel = "material"
+ confirmDeleteDialog.open()
+ }
+
onCountChanged: root.responsiveResize(stackLayout.width, stackLayout.height)
}
}
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectContextMenu.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectContextMenu.qml
index 1e0bcf1eb4..008f99d4fa 100644
--- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectContextMenu.qml
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectContextMenu.qml
@@ -12,7 +12,7 @@ StudioControls.Menu {
property var targetItem: null
- readonly property bool targetAvailable: targetItem && !ContentLibraryBackend.effectsModel.importerRunning
+ readonly property bool targetAvailable: targetItem && !ContentLibraryBackend.rootView.importerRunning
signal unimport(var bundleEff);
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml
index f6df99156b..199f784db1 100644
--- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml
@@ -91,7 +91,7 @@ HelperWidgets.ScrollView {
id: repeater
model: bundleCategoryItems
- delegate: ContentLibraryEffect {
+ delegate: ContentLibraryItem {
width: root.cellWidth
height: root.cellHeight
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffect.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryItem.qml
index b20cc71e15..e6e5cd05c3 100644
--- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffect.qml
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryItem.qml
@@ -23,8 +23,8 @@ Item {
acceptedButtons: Qt.LeftButton | Qt.RightButton
onPressed: (mouse) => {
- if (mouse.button === Qt.LeftButton && !ContentLibraryBackend.effectsModel.importerRunning)
- ContentLibraryBackend.rootView.startDragEffect(modelData, mapToGlobal(mouse.x, mouse.y))
+ if (mouse.button === Qt.LeftButton && !ContentLibraryBackend.rootView.importerRunning)
+ ContentLibraryBackend.rootView.startDragItem(modelData, mapToGlobal(mouse.x, mouse.y))
else if (mouse.button === Qt.RightButton)
root.showContextMenu()
}
@@ -43,7 +43,7 @@ Item {
source: modelData.bundleItemIcon
cache: false
- Rectangle { // circular indicator for imported bundle effect
+ Rectangle { // circular indicator for imported bundle item
width: 10
height: 10
radius: 5
@@ -57,7 +57,7 @@ Item {
ToolTip {
visible: indicatorMouseArea.containsMouse
- text: qsTr("Effect is imported to project")
+ text: qsTr("Item is imported to the project")
delay: 1000
}
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryItemContextMenu.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryItemContextMenu.qml
new file mode 100644
index 0000000000..6f50512430
--- /dev/null
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryItemContextMenu.qml
@@ -0,0 +1,71 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import QtQuick
+import StudioControls as StudioControls
+import StudioTheme as StudioTheme
+import ContentLibraryBackend
+
+StudioControls.Menu {
+ id: root
+
+ property var targetItem: null
+ property bool enableRemove: false // true: adds an option to remove targetItem
+
+ readonly property bool targetAvailable: targetItem && !ContentLibraryBackend.rootView.importerRunning
+
+ signal unimport();
+ signal addToProject()
+ signal applyToSelected(bool add)
+ signal removeFromContentLib()
+
+ function popupMenu(item = null)
+ {
+ root.targetItem = item
+
+ let isMaterial = root.targetItem.itemType === "material"
+ applyToSelectedReplace.visible = isMaterial
+ applyToSelectedAdd.visible = isMaterial
+
+ popup()
+ }
+
+ closePolicy: StudioControls.Menu.CloseOnEscape | StudioControls.Menu.CloseOnPressOutside
+
+ StudioControls.MenuItem {
+ id: applyToSelectedReplace
+ text: qsTr("Apply to selected (replace)")
+ height: visible ? implicitHeight : 0
+ enabled: root.targetAvailable && ContentLibraryBackend.rootView.hasModelSelection
+ onTriggered: root.applyToSelected(false)
+ }
+
+ StudioControls.MenuItem {
+ id: applyToSelectedAdd
+ text: qsTr("Apply to selected (add)")
+ height: visible ? implicitHeight : 0
+ enabled: root.targetAvailable && ContentLibraryBackend.rootView.hasModelSelection
+ onTriggered: root.applyToSelected(true)
+ }
+
+ StudioControls.MenuSeparator {}
+
+ StudioControls.MenuItem {
+ enabled: root.targetAvailable
+ text: qsTr("Add an instance to project")
+ onTriggered: root.addToProject()
+ }
+
+ StudioControls.MenuItem {
+ enabled: root.targetAvailable && root.targetItem.bundleItemImported
+ text: qsTr("Remove from project")
+ onTriggered: root.unimport()
+ }
+
+ StudioControls.MenuItem {
+ text: qsTr("Remove from Content Library")
+ visible: root.enableRemove && root.targetAvailable
+ height: visible ? implicitHeight : 0
+ onTriggered: root.removeFromContentLib()
+ }
+}
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml
index 0e9fc4903e..20ba17d804 100644
--- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml
@@ -16,8 +16,6 @@ Item {
// "failed"
property string downloadState: modelData.isDownloaded() ? "downloaded" : ""
- property bool importerRunning: false
-
signal showContextMenu()
signal addToProject()
@@ -32,7 +30,7 @@ Item {
acceptedButtons: Qt.LeftButton | Qt.RightButton
onPressed: (mouse) => {
- if (mouse.button === Qt.LeftButton && !root.importerRunning) {
+ if (mouse.button === Qt.LeftButton && !ContentLibraryBackend.rootView.importerRunning) {
if (root.downloadState === "downloaded")
ContentLibraryBackend.rootView.startDragMaterial(modelData, mapToGlobal(mouse.x, mouse.y))
} else if (mouse.button === Qt.RightButton && root.downloadState === "downloaded") {
@@ -74,7 +72,7 @@ Item {
color: "#00ff00"
border.color: "#555555"
border.width: 1
- visible: modelData.bundleMaterialImported
+ visible: modelData.bundleItemImported
ToolTip {
visible: indicatorMouseArea.containsMouse
@@ -99,7 +97,7 @@ Item {
pressColor: Qt.hsla(c.hslHue, c.hslSaturation, c.hslLightness, .4)
anchors.right: img.right
anchors.bottom: img.bottom
- enabled: !root.importerRunning
+ enabled: !ContentLibraryBackend.rootView.importerRunning
visible: root.downloadState === "downloaded"
&& (containsMouse || mouseArea.containsMouse)
@@ -186,7 +184,7 @@ Item {
baseUrl: modelData.bundleMaterialBaseWebUrl
files: modelData.bundleMaterialFiles
- targetDirPath: modelData.bundleMaterialParentPath
+ targetDirPath: modelData.bundleMaterialDirPath
onDownloadStarting: {
root.downloadState = "downloading"
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml
deleted file mode 100644
index b67ec311ef..0000000000
--- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml
+++ /dev/null
@@ -1,59 +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
-
-import QtQuick 2.15
-import HelperWidgets 2.0
-import StudioControls 1.0 as StudioControls
-import StudioTheme 1.0 as StudioTheme
-
-StudioControls.Menu {
- id: root
-
- property var targetMaterial: null
- property bool hasModelSelection: false
- property bool importerRunning: false
-
- readonly property bool targetAvailable: targetMaterial && !importerRunning
-
- signal unimport();
- signal addToProject()
- signal applyToSelected(bool add)
-
- function popupMenu(targetMaterial = null)
- {
- this.targetMaterial = targetMaterial
- popup()
- }
-
- closePolicy: StudioControls.Menu.CloseOnEscape | StudioControls.Menu.CloseOnPressOutside
-
- StudioControls.MenuItem {
- text: qsTr("Apply to selected (replace)")
- enabled: root.targetAvailable && root.hasModelSelection
- onTriggered: root.applyToSelected(false)
- }
-
- StudioControls.MenuItem {
- text: qsTr("Apply to selected (add)")
- enabled: root.targetAvailable && root.hasModelSelection
- onTriggered: root.applyToSelected(true)
- }
-
- StudioControls.MenuSeparator {}
-
- StudioControls.MenuItem {
- enabled: root.targetAvailable
- text: qsTr("Add an instance to project")
-
- onTriggered: {
- root.addToProject()
- }
- }
-
- StudioControls.MenuItem {
- enabled: root.targetAvailable && root.targetMaterial.bundleMaterialImported
- text: qsTr("Remove from project")
-
- onTriggered: root.unimport()
- }
-}
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml
index 9a0e33b8e5..e9fca31973 100644
--- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml
@@ -46,16 +46,13 @@ HelperWidgets.ScrollView {
}
Column {
- ContentLibraryMaterialContextMenu {
+ ContentLibraryItemContextMenu {
id: ctxMenu
- hasModelSelection: root.materialsModel.hasModelSelection
- importerRunning: root.materialsModel.importerRunning
+ onApplyToSelected: (add) => root.materialsModel.applyToSelected(ctxMenu.targetItem, add)
- onApplyToSelected: (add) => root.materialsModel.applyToSelected(ctxMenu.targetMaterial, add)
-
- onUnimport: root.unimport(ctxMenu.targetMaterial)
- onAddToProject: root.materialsModel.addToProject(ctxMenu.targetMaterial)
+ onUnimport: root.unimport(ctxMenu.targetItem)
+ onAddToProject: root.materialsModel.addToProject(ctxMenu.targetItem)
}
Repeater {
@@ -103,8 +100,6 @@ HelperWidgets.ScrollView {
width: root.cellWidth
height: root.cellHeight
- importerRunning: root.materialsModel.importerRunning
-
onShowContextMenu: ctxMenu.popupMenu(modelData)
onAddToProject: root.materialsModel.addToProject(modelData)
}
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTextureContextMenu.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTextureContextMenu.qml
index f804f16d89..b1f690f10c 100644
--- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTextureContextMenu.qml
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTextureContextMenu.qml
@@ -12,6 +12,7 @@ StudioControls.Menu {
property var targetTexture: null
property bool hasSceneEnv: false
+ property bool enableRemove: false // true: adds an option to remove targetTexture
property bool canUse3D: targetTexture && ContentLibraryBackend.rootView.hasQuick3DImport && ContentLibraryBackend.rootView.hasMaterialLibrary
@@ -32,13 +33,20 @@ StudioControls.Menu {
StudioControls.MenuItem {
text: qsTr("Add texture")
- enabled: canUse3D
+ enabled: root.canUse3D
onTriggered: ContentLibraryBackend.rootView.addTexture(root.targetTexture)
}
StudioControls.MenuItem {
text: qsTr("Add light probe")
- enabled: root.hasSceneEnv && canUse3D
+ enabled: root.hasSceneEnv && root.canUse3D
onTriggered: ContentLibraryBackend.rootView.addLightProbe(root.targetTexture)
}
+
+ StudioControls.MenuItem {
+ text: qsTr("Remove from Content Library")
+ visible: root.targetTexture && root.enableRemove
+ height: visible ? implicitHeight : 0
+ onTriggered: ContentLibraryBackend.userModel.removeTexture(root.targetTexture)
+ }
}
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml
index 1288e14c2a..cced9e5f09 100644
--- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml
@@ -12,7 +12,7 @@ HelperWidgets.ScrollView {
id: root
clip: true
- interactive: !ctxMenuMaterial.opened && !ctxMenuTexture.opened
+ interactive: !ctxMenuItem.opened && !ctxMenuTexture.opened
&& !ContentLibraryBackend.rootView.isDragging && !HelperWidgets.Controller.contextMenuOpened
property real cellWidth: 100
@@ -31,9 +31,10 @@ HelperWidgets.ScrollView {
required property var searchBox
signal unimport(var bundleItem);
+ signal removeFromContentLib(var bundleItem);
function closeContextMenu() {
- ctxMenuMaterial.close()
+ ctxMenuItem.close()
ctxMenuTexture.close()
}
@@ -46,21 +47,22 @@ HelperWidgets.ScrollView {
}
Column {
- ContentLibraryMaterialContextMenu {
- id: ctxMenuMaterial
+ ContentLibraryItemContextMenu {
+ id: ctxMenuItem
- hasModelSelection: ContentLibraryBackend.userModel.hasModelSelection
- importerRunning: ContentLibraryBackend.userModel.importerRunning
+ enableRemove: true
- onApplyToSelected: (add) => ContentLibraryBackend.userModel.applyToSelected(ctxMenuMaterial.targetMaterial, add)
+ onApplyToSelected: (add) => ContentLibraryBackend.userModel.applyToSelected(ctxMenuItem.targetItem, add)
- onUnimport: root.unimport(ctxMenuMaterial.targetMaterial)
- onAddToProject: ContentLibraryBackend.userModel.addToProject(ctxMenuMaterial.targetMaterial)
+ onUnimport: root.unimport(ctxMenuItem.targetItem)
+ onAddToProject: ContentLibraryBackend.userModel.addToProject(ctxMenuItem.targetItem)
+ onRemoveFromContentLib: root.removeFromContentLib(ctxMenuItem.targetItem)
}
ContentLibraryTextureContextMenu {
id: ctxMenuTexture
+ enableRemove: true
hasSceneEnv: ContentLibraryBackend.texturesModel.hasSceneEnv
}
@@ -79,7 +81,7 @@ HelperWidgets.ScrollView {
bottomPadding: StudioTheme.Values.sectionPadding
caption: categoryName
- visible: categoryVisible
+ visible: categoryVisible && infoText.text === ""
category: "ContentLib_User"
function expandSection() {
@@ -90,8 +92,6 @@ HelperWidgets.ScrollView {
onCountChanged: root.assignMaxCount()
- property int numVisibleItem: 1 // initially, the tab is invisible so this will be 0
-
Grid {
width: section.width - section.leftPadding - section.rightPadding
spacing: StudioTheme.Values.sectionGridSpacing
@@ -110,14 +110,8 @@ HelperWidgets.ScrollView {
width: root.cellWidth
height: root.cellHeight
- importerRunning: ContentLibraryBackend.userModel.importerRunning
-
- onShowContextMenu: ctxMenuMaterial.popupMenu(modelData)
+ onShowContextMenu: ctxMenuItem.popupMenu(modelData)
onAddToProject: ContentLibraryBackend.userModel.addToProject(modelData)
-
- onVisibleChanged: {
- section.numVisibleItem += visible ? 1 : -1
- }
}
}
DelegateChoice {
@@ -129,6 +123,15 @@ HelperWidgets.ScrollView {
onShowContextMenu: ctxMenuTexture.popupMenu(modelData)
}
}
+ DelegateChoice {
+ roleValue: "item"
+ delegate: ContentLibraryItem {
+ width: root.cellWidth
+ height: root.cellHeight
+
+ onShowContextMenu: ctxMenuItem.popupMenu(modelData)
+ }
+ }
}
onCountChanged: root.assignMaxCount()
@@ -140,7 +143,7 @@ HelperWidgets.ScrollView {
color: StudioTheme.Values.themeTextColor
font.pixelSize: StudioTheme.Values.baseFontSize
leftPadding: 10
- visible: !searchBox.isEmpty() && section.numVisibleItem === 0
+ visible: infoText.text === "" && !searchBox.isEmpty() && categoryNoMatch
}
}
}
@@ -148,9 +151,7 @@ HelperWidgets.ScrollView {
Text {
id: infoText
text: {
- if (!ContentLibraryBackend.effectsModel.bundleExists)
- qsTr("User bundle couldn't be found.")
- else if (!ContentLibraryBackend.rootView.isQt6Project)
+ if (!ContentLibraryBackend.rootView.isQt6Project)
qsTr("<b>Content Library</b> is not supported in Qt5 projects.")
else if (!ContentLibraryBackend.rootView.hasQuick3DImport)
qsTr("To use <b>Content Library</b>, first add the QtQuick3D module in the <b>Components</b> view.")
@@ -163,7 +164,7 @@ HelperWidgets.ScrollView {
font.pixelSize: StudioTheme.Values.baseFontSize
topPadding: 10
leftPadding: 10
- visible: ContentLibraryBackend.effectsModel.isEmpty
+ visible: infoText.text !== ""
}
}
}
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/DeleteBundleItemDialog.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/DeleteBundleItemDialog.qml
new file mode 100644
index 0000000000..d8f6551ae5
--- /dev/null
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/DeleteBundleItemDialog.qml
@@ -0,0 +1,65 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import HelperWidgets
+import StudioControls as StudioControls
+import StudioTheme as StudioTheme
+import ContentLibraryBackend
+
+StudioControls.Dialog {
+ id: root
+
+ property var targetBundleItem
+ property var targetBundleModel
+ property string targetBundleLabel // "effect" or "material"
+
+ title: qsTr("Remove bundle %1").arg(root.targetBundleLabel)
+ anchors.centerIn: parent
+ closePolicy: Popup.CloseOnEscape
+ implicitWidth: 300
+ modal: true
+
+ onOpened: warningText.forceActiveFocus()
+
+ contentItem: Column {
+ spacing: 20
+ width: parent.width
+
+ Text {
+ id: warningText
+
+ text: qsTr("Are you sure you? The action cannot be undone")
+ color: StudioTheme.Values.themeTextColor
+ wrapMode: Text.WordWrap
+ anchors.right: parent.right
+ anchors.left: parent.left
+ leftPadding: 10
+ rightPadding: 10
+
+ Keys.onEnterPressed: btnRemove.onClicked()
+ Keys.onReturnPressed: btnRemove.onClicked()
+ }
+
+ Row {
+ anchors.right: parent.right
+ Button {
+ id: btnRemove
+
+ text: qsTr("Remove")
+
+ onClicked: {
+ ContentLibraryBackend.userModel.removeFromContentLib(root.targetBundleItem)
+ root.accept()
+ }
+ }
+
+ Button {
+ text: qsTr("Cancel")
+ onClicked: root.reject()
+ }
+ }
+ }
+}
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleMaterialDialog.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleItemDialog.qml
index 4385e3bf82..305e1018e3 100644
--- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleMaterialDialog.qml
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleItemDialog.qml
@@ -13,8 +13,8 @@ StudioControls.Dialog {
id: root
property var targetBundleItem
- property var targetBundleLabel // "effect" or "material"
property var targetBundleModel
+ property string targetBundleLabel // "effect" or "material"
title: qsTr("Bundle %1 might be in use").arg(root.targetBundleLabel)
anchors.centerIn: parent
diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml
index 8f98ae25b2..799dbeeddc 100644
--- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml
+++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml
@@ -89,8 +89,8 @@ Rectangle {
2. Adjust the effect nodes properties
3. Change the order of the effects, if you like
4. See the preview
-5. Save in the library, if you wish to reuse the effect later") // TODO: revise with doc engineer
+5. Save in the assets library, if you wish to reuse the effect later")
- onClicked: Qt.openUrlExternally("https://doc.qt.io/qtdesignstudio/qt-using-effect-maker-effects.html")
+ onClicked: Qt.openUrlExternally("https://doc.qt.io/qtdesignstudio/qtquick-effect-composer-view.html")
}
}
diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml
index 969d7e2949..6b723a9bc5 100644
--- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml
+++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml
@@ -42,8 +42,9 @@ Row {
to: uniformMaxValue
value: uniformValue
onMoved: {
- uniformValue = value
- spinBox.value = value // binding isn't working for this property so update it
+ let fixedValue = Number.parseFloat(value).toFixed(slider.decimals)
+ uniformValue = fixedValue
+ spinBox.value = fixedValue // binding isn't working for this property so update it
}
}
}
diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml
index b01aa7d2a3..248e07bf6c 100644
--- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml
+++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml
@@ -128,12 +128,10 @@ StudioControls.Menu {
onTriggered: materialBrowserModel.addNewMaterial()
}
- Component.onCompleted: {
- if (MaterialBrowserBackend.rootView.userBundleEnabled()) {
- var menuItem = Qt.createQmlObject("import StudioControls as StudioControls; StudioControls.MenuItem {}", root)
- menuItem.text = qsTr("Add to Content Library")
- menuItem.onTriggered.connect(MaterialBrowserBackend.rootView.addMaterialToContentLibrary)
- root.addItem(menuItem)
- }
+ StudioControls.MenuItem {
+ text: qsTr("Add to Content Library")
+ enabled: !materialBrowserModel.selectedMaterialIsComponent
+
+ onTriggered: MaterialBrowserBackend.rootView.addMaterialToContentLibrary()
}
}
diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml
index dde7ecd0dc..0ab7e59d01 100644
--- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml
+++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml
@@ -4,8 +4,8 @@
import QtQuick
import HelperWidgets
-PropertyEditorPane {
- id: itemPane
+Item {
+ id: root
width: 420
height: 420
@@ -17,63 +17,75 @@ PropertyEditorPane {
// invoked from C++ to refresh material preview image
function refreshPreview()
{
- topSection.refreshPreview()
+ itemPane.headerItem.refreshPreview()
}
// Called from C++ to close context menu on focus out
function closeContextMenu()
{
- topSection.closeContextMenu()
+ itemPane.headerItem.closeContextMenu()
Controller.closeContextMenu()
}
// Called from C++ to initialize preview menu checkmarks
function initPreviewData(env, model)
{
- topSection.previewEnv = env;
- topSection.previewModel = model
+ itemPane.headerItem.previewEnv = env
+ itemPane.headerItem.previewModel = model
}
- MaterialEditorTopSection {
- id: topSection
+ MaterialEditorToolBar {
+ id: toolbar
- onToolBarAction: (action) => itemPane.toolBarAction(action)
- onPreviewEnvChanged: itemPane.previewEnvChanged(previewEnv)
- onPreviewModelChanged: itemPane.previewModelChanged(previewModel)
+ width: parent.width
+
+ onToolBarAction: (action) => root.toolBarAction(action)
}
- Item { width: 1; height: 10 }
+ PropertyEditorPane {
+ id: itemPane
- DynamicPropertiesSection {
- propertiesModel: MaterialEditorDynamicPropertiesModel {}
- }
+ anchors.top: toolbar.bottom
+ anchors.bottom: parent.bottom
+ width: parent.width
+
+ clip: true
- Loader {
- id: specificsTwo
+ headerComponent: MaterialEditorTopSection {
+ onPreviewEnvChanged: root.previewEnvChanged(previewEnv)
+ onPreviewModelChanged: root.previewModelChanged(previewModel)
+ }
+
+ DynamicPropertiesSection {
+ propertiesModel: MaterialEditorDynamicPropertiesModel {}
+ }
- property string theSource: specificQmlData
+ Loader {
+ id: specificsTwo
- anchors.left: parent.left
- anchors.right: parent.right
- visible: specificsTwo.theSource !== ""
- sourceComponent: specificQmlComponent
+ property string theSource: specificQmlData
- onTheSourceChanged: {
- specificsTwo.active = false
- specificsTwo.active = true
+ width: parent.width
+ visible: specificsTwo.theSource !== ""
+ sourceComponent: specificQmlComponent
+
+ onTheSourceChanged: {
+ specificsTwo.active = false
+ specificsTwo.active = true
+ }
}
- }
- Item {
- width: 1
- height: 10
- visible: specificsTwo.visible
- }
+ Item { // spacer
+ width: 1
+ height: 10
+ visible: specificsTwo.visible
+ }
- Loader {
- id: specificsOne
- anchors.left: parent.left
- anchors.right: parent.right
- source: specificsUrl
+ Loader {
+ id: specificsOne
+ anchors.left: parent.left
+ anchors.right: parent.right
+ source: specificsUrl
+ }
}
}
diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml
index 23311636bc..d60c98933b 100644
--- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml
+++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml
@@ -11,10 +11,11 @@ import StudioTheme as StudioTheme
Column {
id: root
- signal toolBarAction(int action)
-
property string previewEnv
property string previewModel
+
+ property real __horizontalSpacing: 5
+
property StudioTheme.ControlStyle buttonStyle: StudioTheme.ViewBarButtonStyle {
//This is how you can override stuff from the control styles
controlSize: Qt.size(previewOptions.width, previewOptions.width)
@@ -37,14 +38,7 @@ Column {
anchors.left: parent.left
anchors.right: parent.right
- MaterialEditorToolBar {
- width: root.width
-
- onToolBarAction: (action) => root.toolBarAction(action)
- }
-
- Item { width: 1; height: 10 } // spacer
-
+ Item { width: 1; height: 5 } // spacer
StudioControls.Menu {
id: modelMenu
@@ -129,6 +123,19 @@ Column {
width: parent.width
height: previewRect.height
+ StudioControls.AbstractButton {
+ id: pinButton
+
+ x: root.__horizontalSpacing
+
+ style: root.buttonStyle
+ iconSize: StudioTheme.Values.bigFont
+ buttonIcon: pinButton.checked ? StudioTheme.Constants.pin : StudioTheme.Constants.unpin
+ checkable: true
+ checked: itemPane.headerDocked
+ onCheckedChanged: itemPane.headerDocked = pinButton.checked
+ }
+
Rectangle {
id: previewRect
anchors.horizontalCenter: parent.horizontalCenter
@@ -153,6 +160,7 @@ Column {
height: previewRect.height
anchors.top: previewRect.top
anchors.left: previewRect.right
+ anchors.leftMargin: root.__horizontalSpacing
Column {
anchors.horizontalCenter: parent.horizontalCenter
@@ -172,7 +180,6 @@ Column {
}
}
}
-
}
HelperWidgets.Section {
@@ -214,6 +221,7 @@ Column {
model: possibleTypes
showExtendedFunctionButton: false
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ enabled: possibleTypes.length > 1
onActivated: changeTypeName(currentValue)
}
diff --git a/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes b/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes
index f6f2159689..60096cd790 100644
--- a/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes
+++ b/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes
@@ -318,4 +318,13 @@ Component {
Component {
name: "QScxmlError"
}
+
+Component {
+ name: "QList<QQuickLayoutItemProxy*>"
+}
+
+Component {
+ name: "VertexColorMaskFlags"
+}
+
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/BusyIndicatorSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/BusyIndicatorSpecifics.qml
new file mode 100644
index 0000000000..2cc82d7fab
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/BusyIndicatorSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.BusyIndicatorSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ButtonSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ButtonSpecifics.qml
new file mode 100644
index 0000000000..ea32a00594
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ButtonSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.ButtonSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckBoxSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckBoxSpecifics.qml
new file mode 100644
index 0000000000..c38b75b327
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckBoxSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.CheckBoxSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckDelegateSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckDelegateSpecifics.qml
new file mode 100644
index 0000000000..939433f5ea
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckDelegateSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.CheckDelegateSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ComboBoxSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ComboBoxSpecifics.qml
new file mode 100644
index 0000000000..13ad9346d4
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ComboBoxSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.ComboBoxSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ControlSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ControlSpecifics.qml
new file mode 100644
index 0000000000..07cc704a07
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ControlSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.ControlSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DelayButtonSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DelayButtonSpecifics.qml
new file mode 100644
index 0000000000..3818cebc33
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DelayButtonSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.DelayButtonSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialSpecifics.qml
new file mode 100644
index 0000000000..c62608cd00
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.DialSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialogSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialogSpecifics.qml
new file mode 100644
index 0000000000..237cb41862
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialogSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.DialogSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DrawerSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DrawerSpecifics.qml
new file mode 100644
index 0000000000..d5cf0a482e
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DrawerSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.DrawerSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/FrameSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/FrameSpecifics.qml
new file mode 100644
index 0000000000..ac1881a865
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/FrameSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.FrameSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/GroupBoxSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/GroupBoxSpecifics.qml
new file mode 100644
index 0000000000..3067af7640
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/GroupBoxSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.GroupBoxSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ItemDelegateSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ItemDelegateSpecifics.qml
new file mode 100644
index 0000000000..6c7f5dc812
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ItemDelegateSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.ItemDelegateSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/LabelSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/LabelSpecifics.qml
new file mode 100644
index 0000000000..6c826e46ea
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/LabelSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.LabelSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageIndicatorSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageIndicatorSpecifics.qml
new file mode 100644
index 0000000000..7437975f96
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageIndicatorSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.PageIndicatorSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageSpecifics.qml
new file mode 100644
index 0000000000..16e8b51cb5
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.PageSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PaneSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PaneSpecifics.qml
new file mode 100644
index 0000000000..dd07a3cf7f
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PaneSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.PaneSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PopupSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PopupSpecifics.qml
new file mode 100644
index 0000000000..788b2a4359
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PopupSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.PopupSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ProgressBarSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ProgressBarSpecifics.qml
new file mode 100644
index 0000000000..a58f9e2f36
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ProgressBarSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.ProgressBarSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioButtonSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioButtonSpecifics.qml
new file mode 100644
index 0000000000..fc2500dd1b
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioButtonSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.RadioButtonSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioDelegateSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioDelegateSpecifics.qml
new file mode 100644
index 0000000000..d07f17b7e8
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioDelegateSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.RadioDelegateSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RangeSliderSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RangeSliderSpecifics.qml
new file mode 100644
index 0000000000..14e493e69e
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RangeSliderSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.RangeSliderSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RoundButtonSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RoundButtonSpecifics.qml
new file mode 100644
index 0000000000..5bd8776f62
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RoundButtonSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.RoundButtonSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ScrollViewSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ScrollViewSpecifics.qml
new file mode 100644
index 0000000000..2af497fcb7
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ScrollViewSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.ScrollViewSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SliderSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SliderSpecifics.qml
new file mode 100644
index 0000000000..846ca8bb14
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SliderSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.SliderSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SpinBoxSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SpinBoxSpecifics.qml
new file mode 100644
index 0000000000..f75983a9fa
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SpinBoxSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.SpinBoxSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/StackViewSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/StackViewSpecifics.qml
new file mode 100644
index 0000000000..593cba24d7
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/StackViewSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.StackViewSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeDelegateSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeDelegateSpecifics.qml
new file mode 100644
index 0000000000..40648a3431
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeDelegateSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.SwipeDelegateSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeViewSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeViewSpecifics.qml
new file mode 100644
index 0000000000..eaf769135c
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeViewSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.SwipeViewSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchDelegateSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchDelegateSpecifics.qml
new file mode 100644
index 0000000000..cba96b930f
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchDelegateSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.SwitchDelegateSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchSpecifics.qml
new file mode 100644
index 0000000000..e6647f4f7d
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.SwitchSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabBarSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabBarSpecifics.qml
new file mode 100644
index 0000000000..c23580a25e
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabBarSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.TabBarSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabButtonSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabButtonSpecifics.qml
new file mode 100644
index 0000000000..dc5bcb36e2
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabButtonSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.TabButtonSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextAreaSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextAreaSpecifics.qml
new file mode 100644
index 0000000000..a9871612a9
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextAreaSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.TextAreaSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextFieldSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextFieldSpecifics.qml
new file mode 100644
index 0000000000..fbcac9c389
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextFieldSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.TextFieldSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolBarSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolBarSpecifics.qml
new file mode 100644
index 0000000000..63d1f179eb
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolBarSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.ToolBarSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolButtonSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolButtonSpecifics.qml
new file mode 100644
index 0000000000..98aabdd274
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolButtonSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.ToolButtonSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolSeparatorSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolSeparatorSpecifics.qml
new file mode 100644
index 0000000000..060808a2a6
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolSeparatorSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.ToolSeparatorSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TumblerSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TumblerSpecifics.qml
new file mode 100644
index 0000000000..5ca8280642
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TumblerSpecifics.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import ".." as Original
+
+Original.TumblerSpecifics {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml
index 2b7543f1d9..3e9a79f8e0 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml
@@ -33,7 +33,7 @@ Section {
if (!root.hasDesignerEffect)
return
- root.model = modelNodeBackend.allChildren(effect[0]) //ids for all effects
+ root.model = modelNodeBackend.allChildren(effect[0]) // ids for all effects
}
leftPadding: 0
@@ -50,6 +50,8 @@ Section {
}
SectionLayout {
+ x: StudioTheme.Values.sectionLeftPadding
+
PropertyLabel {}
SecondColumnLayout {
@@ -61,7 +63,7 @@ Section {
width: StudioTheme.Values.singleControlColumnWidth
buttonIcon: root.hasDesignerEffect ? qsTr("Remove Effects") : qsTr("Add Effects")
iconFont: StudioTheme.Constants.font
- tooltip: qsTr("Adds a note with a title to explain the component.")
+ tooltip: qsTr("Adds visual effects on the component.")
onClicked: {
if (root.hasDesignerEffect) {
root.effectNodeWrapper.deleteModelNode()
@@ -77,6 +79,7 @@ Section {
PropertyLabel {
text: qsTr("Visible")
+ tooltip: qsTr("Toggles the visibility of visual effects on the component.")
visible: root.hasDesignerEffect
}
@@ -192,7 +195,10 @@ Section {
SectionLayout {
- PropertyLabel { text: qsTr("Visible") }
+ PropertyLabel {
+ text: qsTr("Visible")
+ tooltip: qsTr("Toggles the visibility of the <b>Layer Blur</b> on the component.")
+ }
SecondColumnLayout {
CheckBox {
@@ -204,7 +210,10 @@ Section {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Blur") }
+ PropertyLabel {
+ text: qsTr("Blur")
+ tooltip: qsTr("Sets the intensity of the <b>Layer Blur</b> on the component.")
+ }
SecondColumnLayout {
SpinBox {
@@ -232,7 +241,10 @@ Section {
SectionLayout {
- PropertyLabel { text: qsTr("Visible") }
+ PropertyLabel {
+ text: qsTr("Visible")
+ tooltip: qsTr("Toggles the visibility of blur on the selected background component.")
+ }
SecondColumnLayout {
CheckBox {
@@ -244,7 +256,12 @@ Section {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Blur") }
+ PropertyLabel {
+ text: qsTr("Blur")
+ tooltip: qsTr("Sets the intensity of blur on the selected background component.\n"
+ + "The foreground component should be transparent, and the background "
+ + "component should be opaque.")
+ }
SecondColumnLayout {
SpinBox {
@@ -258,7 +275,12 @@ Section {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Background") }
+ PropertyLabel {
+ text: qsTr("Background")
+ tooltip: qsTr("Sets a component as the background of a transparent component."
+ + "The <b>Background Blur</b> works only on this component. The component should "
+ + "be solid.")
+ }
SecondColumnLayout {
ItemFilterComboBox {
@@ -362,7 +384,10 @@ Section {
id: controlContainer
property bool isDropShadow: shadowComboBox.currentValue === "DesignDropShadow"
- PropertyLabel { text: qsTr("Visible") }
+ PropertyLabel {
+ text: qsTr("Visible")
+ tooltip: qsTr("Toggles the visibility of the component shadow.")
+ }
SecondColumnLayout {
CheckBox {
@@ -374,7 +399,11 @@ Section {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Blur") }
+ PropertyLabel {
+ text: qsTr("Blur")
+ tooltip: qsTr("Sets the softness of the component shadow. A larger value"
+ + " causes the edges of the shadow to appear more blurry.")
+ }
SecondColumnLayout {
SpinBox {
@@ -390,6 +419,9 @@ Section {
PropertyLabel {
text: qsTr("Spread")
+ tooltip: modelNodeBackend.isInstanceOf("Rectangle")
+ ? qsTr("Resizes the base shadow of the component by pixels.")
+ : qsTr("Only supported for Rectangles.")
enabled: modelNodeBackend.isInstanceOf("Rectangle")
}
@@ -408,7 +440,7 @@ Section {
PropertyLabel {
text: qsTr("Color")
- tooltip: qsTr("Sets the color.")
+ tooltip: qsTr("Sets the color of the shadow.")
}
ColorEditor {
@@ -416,7 +448,11 @@ Section {
supportGradient: false
}
- PropertyLabel { text: qsTr("Offset") }
+ PropertyLabel {
+ text: qsTr("Offset")
+ tooltip: qsTr("Moves the shadow with respect to the component in "
+ + "X and Y coordinates by pixels.")
+ }
SecondColumnLayout {
SpinBox {
@@ -458,6 +494,7 @@ Section {
PropertyLabel {
visible: controlContainer.isDropShadow
text: qsTr("Show behind")
+ tooltip: qsTr("Toggles the visibility of the shadow behind a transparent component.")
}
SecondColumnLayout {
@@ -483,6 +520,7 @@ Section {
}
SectionLayout {
+ x: StudioTheme.Values.sectionLeftPadding
visible: root.hasDesignerEffect
PropertyLabel {}
@@ -496,7 +534,7 @@ Section {
width: StudioTheme.Values.singleControlColumnWidth
buttonIcon: qsTr("Add Shadow Effect")
iconFont: StudioTheme.Constants.font
- tooltip: qsTr("Adds a Design Drop Shadow.")
+ tooltip: qsTr("Adds <b>Drop Shadow</b> or <b>Inner Shadow</b> effects to a component.")
onClicked: {
modelNodeBackend.createModelNode(root.effectNode,
"effects",
@@ -504,6 +542,8 @@ Section {
root.invalidate()
}
}
+
+ ExpandingSpacer {}
}
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml
index 4b4d2b8dc6..cc8247d706 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml
@@ -38,6 +38,10 @@ SecondColumnLayout {
property alias showHexTextField: hexTextField.visible
property bool shapeGradients: false
+
+ //for now, gradients on MCUs are limited to Basic and Shape Linear Gradient:
+ property bool mcuGradients: false
+
property color originalColor
property bool isVector3D: false
@@ -219,7 +223,10 @@ SecondColumnLayout {
function open() {
popupDialog.ensureLoader()
+
popupDialog.show(preview)
+
+ popupDialog.loaderItem.aboutToBeShown() //need it for now
}
function determineActiveColorMode() {
@@ -235,9 +242,11 @@ SecondColumnLayout {
sourceComponent: ColorEditorPopup {
shapeGradients: colorEditor.shapeGradients
+ mcuGradients: colorEditor.mcuGradients
supportGradient: colorEditor.supportGradient
width: popupDialog.contentWidth
visible: popupDialog.visible
+ parentWindow: popupDialog.window
}
onLoaded: {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml
index 22be367c37..41f2c433fa 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml
@@ -12,10 +12,20 @@ import QtQuickDesignerColorPalette
Column {
id: root
+ // There seems to be an issue on Windows and MacOS with ColorPickers
+ // Canvases not being painted on initialization
+ // because ColorEditorPopup is invisible at init time,
+ // so we use this signal to explicitly pass visibility status
+ signal aboutToBeShown
+
property bool eyeDropperActive: ColorPaletteBackend.eyeDropperActive
property bool supportGradient: false
property bool shapeGradients: false
+
+ //for now, gradients on MCUs are limited to Basic and Shape Linear Gradient:
+ property bool mcuGradients: false
+
property alias gradientLine: gradientLine
property alias popupHexTextField: popupHexTextField
property alias gradientPropertyName: root.gradientModel.gradientPropertyName
@@ -23,6 +33,8 @@ Column {
property alias gradientModel: gradientModel
+ property Window parentWindow: null
+
property bool isInValidState: false
readonly property real twoColumnWidth: (colorColumn.width - StudioTheme.Values.controlGap) * 0.5
@@ -224,12 +236,12 @@ Column {
ceMode.items.append({
value: "RadialGradient",
text: qsTr("Radial"),
- enabled: root.supportGradient && root.shapeGradients
+ enabled: root.supportGradient && root.shapeGradients && !root.mcuGradients
})
ceMode.items.append({
value: "ConicalGradient",
text: qsTr("Conical"),
- enabled: root.supportGradient && root.shapeGradients
+ enabled: root.supportGradient && root.shapeGradients && !root.mcuGradients
})
}
@@ -429,6 +441,13 @@ Column {
hsvValueSpinBox.value = colorPicker.value
hsvAlphaSpinBox.value = colorPicker.alpha
}
+
+ Connections {
+ target: root
+ onAboutToBeShown: {
+ colorPicker.aboutToBeShown()
+ }
+ }
}
Column {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml
index 88a0debae8..e0c70ff946 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml
@@ -528,6 +528,8 @@ Section {
return intEditor
if (propertyType == "real")
return realEditor
+ if (propertyType == "double")
+ return realEditor
if (propertyType == "string")
return stringEditor
if (propertyType == "bool")
@@ -708,7 +710,7 @@ Section {
StudioControls.ComboBox {
id: comboBox
actionIndicator.visible: false
- model: ["int", "real", "color", "string", "bool", "url", "alias", "signal",
+ model: ["int", "real", "double", "color", "string", "bool", "url", "alias", "signal",
"TextureInput", "vector2d", "vector3d", "vector4d"]
width: cePopup.itemWidth
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml
index 76e6bd8f09..8a8c8c33ce 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml
@@ -103,7 +103,7 @@ Item {
StudioControls.MenuItem {
text: qsTr("Insert Keyframe")
enabled: hasActiveTimeline
- onTriggered: insertKeyframe(backendValue.name)
+ onTriggered: backendValue.insertKeyframe()
}
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml
index 7046ca48e1..309d815d7a 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml
@@ -19,30 +19,83 @@ Rectangle {
default property alias content: mainColumn.children
property alias scrollView: mainScrollView
+ property bool headerDocked: false
+ readonly property Item headerItem: headerDocked ? dockedHeaderLoader.item : undockedHeaderLoader.item
+
+ property Component headerComponent: null
+
// Called from C++ to close context menu on focus out
function closeContextMenu() {
Controller.closeContextMenu()
}
+ Loader {
+ id: dockedHeaderLoader
+
+ anchors.top: itemPane.top
+ z: parent.z + 1
+ height: item ? item.implicitHeight : 0
+ width: parent.width
+
+ active: itemPane.headerDocked
+ sourceComponent: itemPane.headerComponent
+
+ HeaderBackground{}
+ }
+
MouseArea {
anchors.fill: parent
- onClicked: forceActiveFocus()
+ onClicked: itemPane.forceActiveFocus()
}
HelperWidgets.ScrollView {
id: mainScrollView
- //clip: true
- anchors.fill: parent
+
+ clip: true
+ anchors {
+ top: dockedHeaderLoader.bottom
+ bottom: itemPane.bottom
+ left: itemPane.left
+ right: itemPane.right
+ }
interactive: !Controller.contextMenuOpened
- Column {
- id: mainColumn
- y: -1
- width: itemPane.width
+ ColumnLayout {
+ spacing: 2
+ width: mainScrollView.width
- onWidthChanged: StudioTheme.Values.responsiveResize(itemPane.width)
- Component.onCompleted: StudioTheme.Values.responsiveResize(itemPane.width)
+ Loader {
+ id: undockedHeaderLoader
+
+ Layout.fillWidth: true
+ Layout.preferredHeight: item ? item.implicitHeight : 0
+
+ active: !itemPane.headerDocked
+ sourceComponent: itemPane.headerComponent
+
+ HeaderBackground{}
+ }
+
+ Column {
+ id: mainColumn
+
+ Layout.fillWidth: true
+
+ onWidthChanged: StudioTheme.Values.responsiveResize(itemPane.width)
+ Component.onCompleted: StudioTheme.Values.responsiveResize(itemPane.width)
+ }
}
}
+
+ component HeaderBackground: Rectangle {
+ anchors.fill: parent
+ anchors.leftMargin: -StudioTheme.Values.border
+ anchors.rightMargin: -StudioTheme.Values.border
+ z: parent.z - 1
+
+ color: StudioTheme.Values.themeToolbarBackground
+ border.color: StudioTheme.Values.themePanelBackground
+ border.width: StudioTheme.Values.border
+ }
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml
index 0e42515c76..8dadd1db2c 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml
@@ -451,7 +451,7 @@ Row {
// Prefer hiding generated component files rather than other project files
let listIndex = nameMap.get(item.fileName)
let absPath = comboBox.listModel.get(listIndex).absoluteFilePath
- if (absPath.includes("/GeneratedComponents/") || absPath.includes("/asset_imports/")) {
+ if (absPath.includes("/Generated/") || absPath.includes("/asset_imports/")) {
comboBox.listModel.set(listIndex, {
absoluteFilePath: item.absoluteFilePath,
relativeFilePath: item.relativeFilePath,
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckBox.qml
index 573b78012f..7e565e3a47 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckBox.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckBox.qml
@@ -14,7 +14,6 @@ T.CheckBox {
// This property is used to indicate the global hover state
property bool hover: control.hovered && control.enabled
- property bool edit: false
property alias actionIndicatorVisible: actionIndicator.visible
property real __actionIndicatorWidth: control.style.actionIndicatorSize.width
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml
index 07265e41a7..28372e41be 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml
@@ -9,7 +9,7 @@ import StudioTheme 1.0 as StudioTheme
T.MenuItem {
id: control
- property alias shortcut: itemAction.shortcut
+ property alias shortcut: shortcutObserver.shortcutWorkaround
property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
@@ -24,9 +24,6 @@ T.MenuItem {
padding: 0
spacing: 0
horizontalPadding: control.style.contextMenuHorizontalPadding
- action: Action {
- id: itemAction
- }
contentItem: Item {
Text {
@@ -41,16 +38,23 @@ T.MenuItem {
Text {
id: shortcutLabel
+
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
text: shortcutObserver.nativeText
+ ? shortcutObserver.nativeText
+ : control.action
+ ? control.action.fakeShortcut ? control.action.fakeShortcut : ""
+ : ""
font: control.font
color: textLabel.color
Shortcut {
id: shortcutObserver
- property int shortcutWorkaround: control.shortcut ?? 0
+
+ property int shortcutWorkaround: 0
sequence: shortcutObserver.shortcutWorkaround
+ enabled: false
}
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml
index 40c35df27c..821ba3a849 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml
@@ -104,7 +104,7 @@ QtObject {
return root.maximumHeight + (2 * window.margin)
}
visible: false
- flags: Qt.FramelessWindowHint | Qt.Tool | Qt.WindowStaysOnTopHint
+ flags: Qt.FramelessWindowHint | Qt.Tool
color: "transparent"
onClosing: function (close) {
@@ -286,10 +286,8 @@ QtObject {
enabled: root.visible
function onFocusWindowChanged(focusWindow) {
- if (!focusWindow) {
- root.close()
+ if (!focusWindow)
return
- }
if (root.keepOpen)
return
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorPicker.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorPicker.qml
index cd52511426..fc2a025664 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorPicker.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorPicker.qml
@@ -13,6 +13,8 @@ Column {
HSLA
}
+ signal aboutToBeShown
+
property int mode: ColorPicker.Mode.HSVA
property color color: "#303091"
@@ -206,6 +208,8 @@ Column {
target: root
function onHueChanged() { gradientOverlay.requestPaint() }
function onModeChanged() { gradientOverlay.requestPaint() }
+ function onAlphaChanged() { gradientOverlay.requestPaint() }
+ function onAboutToBeShown() { gradientOverlay.requestPaint() }
}
onPaint: {
@@ -249,6 +253,7 @@ Column {
function onColorInvalidated() { pickerCross.requestPaint() }
function onColorChanged() { pickerCross.requestPaint() }
function onModeChanged() { pickerCross.requestPaint() }
+ function onAboutToBeShown() { pickerCross.requestPaint() }
}
onPaint: {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml
index 1a04c8ebc3..69803cf320 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml
@@ -243,16 +243,6 @@ QtObject {
property real dialogButtonSpacing: 10
property real dialogButtonPadding: 4
- // Collection Editor
- property real collectionItemTextSideMargin: 10
- property real collectionItemTextMargin: 5
- property real collectionItemTextPadding: 5
- property real collectionTableHorizontalMargin: 10
- property real collectionTableVerticalMargin: 10
- property real collectionCellMinimumWidth: 60
- property real collectionCellMinimumHeight: 20
- property real smallStatusIndicatorDiameter: 6
-
// NEW NEW NEW
readonly property int flowMargin: 7
readonly property int flowSpacing: 7 // Odd so cursor has a center location
diff --git a/share/qtcreator/qmldesigner/qt4mcu/metadata.qml b/share/qtcreator/qmldesigner/qt4mcu/metadata.qml
index b4ae840bdd..2df700e72a 100644
--- a/share/qtcreator/qmldesigner/qt4mcu/metadata.qml
+++ b/share/qtcreator/qmldesigner/qt4mcu/metadata.qml
@@ -5,7 +5,7 @@
Metadata {
id: metadataFile
- defaultVersion: v26
+ defaultVersion: v27
VersionData {
id: v14
@@ -72,4 +72,10 @@ Metadata {
name: "Qt for MCUs 2.6"
path: "qul-26.qml"
}
+
+ VersionData {
+ id: v27
+ name: "Qt for MCUs 2.7"
+ path: "qul-27.qml"
+ }
}
diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-27.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-27.qml
new file mode 100644
index 0000000000..6cb5640472
--- /dev/null
+++ b/share/qtcreator/qmldesigner/qt4mcu/qul-27.qml
@@ -0,0 +1,227 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+// new import: QUL.Studio.Components
+// new types: ArcItem, Gradients, Basic and LinearGradient
+// new properties: gradients related
+// Layer Application renamed to ApplicationScreens
+
+VersionData {
+ name: "Qt for MCUs 2.7"
+
+ bannedItems: [
+ "QtQuick.AnimatedImage",
+ "QtQuick.Flow",
+ "QtQuick.FocusScope",
+ "QtQuick.Grid",
+ "QtQuick.GridView",
+ "QtQuick.PathView",
+ "QtQuick.TextEdit",
+ "QtQuick.TextInput",
+ "QtQuick.Controls",
+ "QtQuick.Controls.BusyIndicator",
+ "QtQuick.Controls.ButtonGroup",
+ "QtQuick.Controls.CheckDelegate",
+ "QtQuick.Controls.ComboBox",
+ "QtQuick.Controls.Container",
+ "QtQuick.Controls.DelayButton",
+ "QtQuick.Controls.Frame",
+ "QtQuick.Controls.GroupBox",
+ "QtQuick.Controls.ItemDelegate",
+ "QtQuick.Controls.Label",
+ "QtQuick.Controls.Page",
+ "QtQuick.Controls.PageIndicator",
+ "QtQuick.Controls.Pane",
+ "QtQuick.Controls.RadioDelegate",
+ "QtQuick.Controls.RangeSlider",
+ "QtQuick.Controls.RoundButton",
+ "QtQuick.Controls.ScrollView",
+ "QtQuick.Controls.SpinBox",
+ "QtQuick.Controls.StackView",
+ "QtQuick.Controls.SwipeDelegate",
+ "QtQuick.Controls.SwitchDelegate",
+ "QtQuick.Controls.TabBar",
+ "QtQuick.Controls.TabButton",
+ "QtQuick.Controls.TextArea",
+ "QtQuick.Controls.TextField",
+ "QtQuick.Controls.ToolBar",
+ "QtQuick.Controls.ToolButton",
+ "QtQuick.Controls.ToolSeparator",
+ "QtQuick.Controls.Tumbler",
+ "QtQuick.Shapes.ConicalGradient",
+ "QtQuick.Shapes.RadialGradient"
+ ]
+
+ allowedImports: [
+ "QtQuick",
+ "QtQuick.Controls",
+ "QtQuick.Shapes",
+ "QtQuick.Timeline",
+ "QtQuickUltralite.Extras",
+ "QtQuickUltralite.Layers",
+ "QtQuickUltralite.Profiling",
+ "QtQuickUltralite.Studio.Components"
+ ]
+
+ bannedImports: [
+ "FlowView",
+ "SimulinkConnector"
+ ]
+
+ //ComplexProperty is not a type, it's just a way to handle bigger props
+ ComplexProperty {
+ prefix: "font"
+ bannedProperties: ["wordSpacing", "letterSpacing", "hintingPreference",
+ "kerning", "preferShaping", "capitalization",
+ "strikeout", "underline", "styleName"]
+ }
+
+ QtQml.Timer {
+ bannedProperties: ["triggeredOnStart"]
+ }
+
+ QtQuick.Item {
+ bannedProperties: ["layer", "opacity", "smooth", "antialiasing",
+ "baselineOffset", "focus", "activeFocusOnTab",
+ "rotation", "scale", "transformOrigin"]
+ }
+
+ QtQuick.Rectangle {
+ bannedProperties: ["border"]
+ }
+
+ QtQuick.Flickable {
+ bannedProperties: ["boundsMovement", "flickDeceleration",
+ "leftMargin", "rightMargin", "bottomMargin", "topMargin",
+ "originX", "originY", "pixelAligned", "pressDelay", "synchronousDrag"]
+ }
+
+ QtQuick.MouseArea {
+ bannedProperties: ["propagateComposedEvents", "preventStealing", "cursorShape",
+ "scrollGestureEnabled", "drag", "acceptedButtons", "hoverEnabled"]
+ }
+
+ QtQuick.Image {
+ allowChildren: false
+ allowedProperties: ["rotation", "scale", "transformOrigin"]
+ bannedProperties: ["mirror", "mipmap", "cache", "autoTransform", "asynchronous",
+ "sourceSize", "smooth"]
+ }
+
+ QtQuick.BorderImage {
+ bannedProperties: ["asynchronous", "cache", "currentFrame", "frameCount",
+ "horizontalTileMode", "mirror", "progress", "smooth", "sourceSize",
+ "status", "verticalTileMode"]
+ }
+
+ QtQuick.Text {
+ allowChildren: false
+ allowedProperties: ["rotation", "scale", "transformOrigin"]
+ bannedProperties: ["lineHeight", "lineHeightMode", "style",
+ "styleColor", "minimumPointSize", "minimumPixelSize",
+ "fontSizeMode", "renderType", "renderTypeQuality", "maximumLineCount"]
+ }
+
+ QtQuick.Loader {
+ bannedProperties: ["asynchronous", "progress", "status"]
+ }
+
+ //Padding is not an actual item, but rather set of properties in Text
+ Padding {
+ bannedProperties: ["bottomPadding", "topPadding", "leftPadding", "rightPadding"]
+ }
+
+ QtQuick.Column {
+ bannedProperties: ["bottomPadding", "leftPadding", "rightPadding", "topPadding"]
+ }
+
+ QtQuick.Row {
+ bannedProperties: ["bottomPadding", "leftPadding", "rightPadding", "topPadding",
+ "effectiveLayoutDirection", "layoutDirection"]
+ }
+
+ QtQuick.ListView {
+ bannedProperties: ["cacheBuffer", "highlightRangeMode", "highlightMoveDuration",
+ "highlightResizeDuration", "preferredHighlightBegin", "layoutDirection",
+ "preferredHighlightEnd", "highlightFollowsCurrentItem", "keyNavigationWraps",
+ "snapMode", "highlightMoveVelocity", "highlightResizeVelocity"]
+ }
+
+ QtQuick.Animation {
+ bannedProperties: ["paused"]
+ }
+
+ QtQuick.AnimatedSprite {
+ allowedProperties: ["currentFrame", "frameCount", "paused"]
+ bannedProperties: ["finishBehavior", "frameRate", "frameSync",
+ "frameX", "frameY", "interpolate", "reverse"]
+ }
+
+ //Quick Controls2 Items and properties:
+
+ QtQuick.Controls.Control {
+ bannedProperties: ["focusPolicy", "hoverEnabled", "wheelEnabled"]
+ }
+
+ QtQuick.Controls.AbstractButton {
+ bannedProperties: ["display", "autoExclusive", "icon"]
+ }
+
+ QtQuick.Controls.ProgressBar {
+ bannedProperties: ["indeterminate"]
+ }
+
+ QtQuick.Controls.Slider {
+ bannedProperties: ["live", "snapMode", "touchDragThreshold"]
+ }
+
+ //Path and Shapes related:
+
+ QtQuick.Path {
+ bannedProperties: ["scale", "pathElements"]
+ }
+
+ QtQuick.PathArc {
+ bannedProperties: ["relativeX", "relativeY"]
+ }
+
+ QtQuick.PathLine {
+ bannedProperties: ["relativeX", "relativeY"]
+ }
+
+ QtQuick.PathMove {
+ bannedProperties: ["relativeX", "relativeY"]
+ }
+
+ QtQuick.PathQuad {
+ bannedProperties: ["relativeX", "relativeY",
+ "relativeControlX", "relativeControlY"]
+ }
+
+ QtQuick.PathCubic {
+ bannedProperties: ["relativeX", "relativeY",
+ "relativeControl1X", "relativeControl1Y",
+ "relativeControl2X", "relativeControl2Y"]
+ }
+
+ QtQuick.PathElement {
+ //nothing
+ }
+
+ QtQuick.PathSvg {
+ //nothing
+ }
+
+ QtQuick.Shapes.Shape {
+ bannedProperties: ["asynchronous", "containsMode", "data",
+ "renderType", "status", "vendorExtensionsEnabled"]
+ }
+
+ QtQuick.Shapes.ShapePath {
+ bannedProperties: ["dashOffset", "dashPattern", "strokeStyle"]
+ }
+
+ QtQuickUltralite.Extras.ItemBuffer {
+ allowedProperties: ["rotation", "scale", "transformOrigin"]
+ }
+}
diff --git a/share/qtcreator/qmldesigner/studio_templates/files/javascript/wizard.json b/share/qtcreator/qmldesigner/studio_templates/files/javascript/wizard.json
index 7ba90f75f6..f70ed11c1d 100644
--- a/share/qtcreator/qmldesigner/studio_templates/files/javascript/wizard.json
+++ b/share/qtcreator/qmldesigner/studio_templates/files/javascript/wizard.json
@@ -9,13 +9,46 @@
"icon": "file_javascript.png",
"platformIndependent": true,
+ "options":
+ [
+ { "key": "JSFile", "value": "%{Class}.%{JS: Util.preferredSuffix('application/javascript')}" },
+ { "key": "ApplicationImport", "value": "%{QmlProjectName} 1.0" },
+ { "key": "RootItem", "value": "%{JS: %{RootItemCB}.RootItem}" },
+ { "key": "UseImportDefault", "value": "%{JS: false}" },
+ { "key": "UseQtQuickControls2Default", "value": "%{JS: true}" }
+ ],
"pages" :
- [
+ [
{
- "trDisplayName": "Location",
- "trShortTitle": "Location",
- "typeId": "File"
+ "trDisplayName": "Define Class",
+ "trShortTitle": "Details",
+ "typeId": "Fields",
+ "data" :
+ [
+ {
+ "name": "Class",
+ "trDisplayName": "Component name:",
+ "mandatory": true,
+ "type": "LineEdit",
+ "data": {
+ "validator": "(?:[A-Z_][a-zA-Z_0-9]*|)",
+ "fixup": "%{JS: '%{INPUT}'.charAt(0).toUpperCase() + '%{INPUT}'.slice(1) }"
+ }
+ },
+ {
+ "name": "TargetPath",
+ "type": "PathChooser",
+ "trDisplayName": "Path:",
+ "mandatory": true,
+ "data":
+ {
+ "kind": "existingDirectory",
+ "basePath": "%{InitialPath}",
+ "path": "%{InitialPath}"
+ }
+ }
+ ]
},
{
"trDisplayName": "Options",
@@ -34,21 +67,16 @@
}
}
]
- },
- {
- "trDisplayName": "Project Management",
- "trShortTitle": "Summary",
- "typeId": "Summary"
}
],
"generators" :
- [
+ [
{
"typeId": "File",
"data":
{
"source": "file.js.tpl",
- "target": "%{JS: Util.fileName('%{TargetPath}', '%{JS: Util.preferredSuffix('application/javascript')}')}",
+ "target": "%{TargetPath}/%{JSFile}",
"openInEditor": true
}
}
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json
index a5d0d7539c..a897fa421a 100644
--- a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json
+++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json
@@ -18,7 +18,7 @@
{ "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" },
{ "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" },
{ "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" },
- { "key": "AssetDir", "value": "GeneratedComponents" },
+ { "key": "AssetDir", "value": "Generated" },
{ "key": "ContentDir", "value": "%{ProjectName}Content" },
{ "key": "ImportModuleName", "value": "%{ProjectName}" },
{ "key": "UIClassName", "value": "Screen01" },
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json
index 5f2e5bfcaf..ccd522f7c8 100644
--- a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json
+++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json
@@ -18,7 +18,7 @@
{ "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" },
{ "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" },
{ "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" },
- { "key": "AssetDir", "value": "GeneratedComponents" },
+ { "key": "AssetDir", "value": "Generated" },
{ "key": "ContentDir", "value": "%{ProjectName}Content" },
{ "key": "ImportModuleName", "value": "%{ProjectName}" },
{ "key": "UIClassName", "value": "Screen01" },
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/app_mcu.qmlproject.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/app_mcu.qmlproject.tpl
index f17d608f96..7c4453752a 100644
--- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/app_mcu.qmlproject.tpl
+++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/app_mcu.qmlproject.tpl
@@ -42,7 +42,9 @@ Project {
"Controls",
"ControlsTemplates",
"Timeline",
- "Shapes"
+ "Shapes",
+ "Profiling",
+ "StudioComponents"
]
}
@@ -55,8 +57,8 @@ Project {
QDS.qtForMCUs: true
QDS.qt6Project: true
- QDS.qdsVersion: "4.3"
- QDS.quickVersion: "6.5"
+ QDS.qdsVersion: "4.5"
+ QDS.quickVersion: "6.7"
/* List of plugin directories passed to QML runtime */
importPaths: [ "imports" ]
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json
index 41fe2df289..f0f98c3331 100644
--- a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json
+++ b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json
@@ -17,7 +17,7 @@
{ "key": "ProjectPluginName", "value": "%{ProjectName}plugin" },
{ "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" },
{ "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" },
- { "key": "AssetDir", "value": "GeneratedComponents" },
+ { "key": "AssetDir", "value": "Generated" },
{ "key": "ContentDir", "value": "%{ProjectName}Content" },
{ "key": "ImportModuleName", "value": "%{ProjectName}" },
{ "key": "UIClassName", "value": "Screen01" },
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json
index ddaf502154..c15d4dbf74 100644
--- a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json
+++ b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json
@@ -17,7 +17,7 @@
{ "key": "ProjectPluginName", "value": "%{ProjectName}plugin" },
{ "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" },
{ "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" },
- { "key": "AssetDir", "value": "GeneratedComponents" },
+ { "key": "AssetDir", "value": "Generated" },
{ "key": "ContentDir", "value": "%{ProjectName}Content" },
{ "key": "ImportModuleName", "value": "%{ProjectName}" },
{ "key": "UIClassName", "value": "Screen01" },
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json
index 585a73aa90..d9e4b97908 100644
--- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json
+++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json
@@ -17,7 +17,7 @@
{ "key": "ProjectPluginName", "value": "%{ProjectName}plugin" },
{ "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" },
{ "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" },
- { "key": "AssetDir", "value": "GeneratedComponents" },
+ { "key": "AssetDir", "value": "Generated" },
{ "key": "ContentDir", "value": "%{ProjectName}Content" },
{ "key": "ImportModuleName", "value": "%{ProjectName}" },
{ "key": "UIClassName", "value": "Screen01" },
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json
index a44a1429be..910c32a42c 100644
--- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json
+++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json
@@ -17,7 +17,7 @@
{ "key": "ProjectPluginName", "value": "%{ProjectName}plugin" },
{ "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" },
{ "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" },
- { "key": "AssetDir", "value": "GeneratedComponents" },
+ { "key": "AssetDir", "value": "Generated" },
{ "key": "ContentDir", "value": "%{ProjectName}Content" },
{ "key": "ImportModuleName", "value": "%{ProjectName}" },
{ "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" },
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json
index b3f70a8b79..1f5be300e1 100644
--- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json
+++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json
@@ -17,7 +17,7 @@
{ "key": "ProjectPluginName", "value": "%{ProjectName}plugin" },
{ "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" },
{ "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" },
- { "key": "AssetDir", "value": "GeneratedComponents" },
+ { "key": "AssetDir", "value": "Generated" },
{ "key": "ContentDir", "value": "%{ProjectName}Content" },
{ "key": "ImportModuleName", "value": "%{ProjectName}" },
{ "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" },
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/Colors.json.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/Colors.json.tpl
deleted file mode 100644
index 052b7abd01..0000000000
--- a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/Colors.json.tpl
+++ /dev/null
@@ -1,18 +0,0 @@
-[
- {
- "colorCode": "#ff0000",
- "name": "Red"
- },
- {
- "colorCode": "#00ff00",
- "name": "Green"
- },
- {
- "colorCode": "#0000ff",
- "name": "Blue"
- },
- {
- "colorCode": "#ffffff",
- "name": "White"
- }
-]
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/DataStore.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/DataStore.qml.tpl
deleted file mode 100644
index ca8b45ede0..0000000000
--- a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/DataStore.qml.tpl
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-
-pragma Singleton
-import QtQuick 6.5
-import QtQuick.Studio.Utils 1.0
-
-JsonListModel {
- id: models
- source: Qt.resolvedUrl("models.json")
-
- property ChildListModel book: ChildListModel {
- modelName: "book"
- }
-
- property JsonData backend: JsonData {}
-}
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/models.json.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/models.json.tpl
deleted file mode 100644
index ca9c173651..0000000000
--- a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/models.json.tpl
+++ /dev/null
@@ -1,56 +0,0 @@
-{
- "book": {
- "columns": [
- {
- "name": "author",
- "type": "String"
- },
- {
- "name": "category",
- "type": "String"
- },
- {
- "name": "isbn",
- "type": "String"
- },
- {
- "name": "price",
- "type": "Real"
- },
- {
- "name": "title",
- "type": "String"
- }
- ],
- "data": [
- [
- "Nigel Rees",
- "reference",
- "",
- 8.95,
- "Sayings of the Century"
- ],
- [
- "Evelyn Waugh",
- "fiction",
- "",
- 12.99,
- "Sword of Honor"
- ],
- [
- "Herman Melville",
- "fiction",
- "0-553-21311-3",
- 8.99,
- "Moby Dick"
- ],
- [
- "J. R. R. Tolkien",
- "fiction",
- "0-395-19395-8",
- 22.99,
- "The Lord of the Rings"
- ]
- ]
- }
-}
diff --git a/share/qtcreator/templates/wizards/files/form/wizard.json b/share/qtcreator/templates/wizards/files/form/wizard.json
index ec5fd422a3..d0d826b6c6 100644
--- a/share/qtcreator/templates/wizards/files/form/wizard.json
+++ b/share/qtcreator/templates/wizards/files/form/wizard.json
@@ -3,8 +3,8 @@
"supportedProjectTypes": [ ],
"id": "D.Form",
"category": "R.Qt",
- "trDescription": "Creates a Qt Designer form that you can add to a Qt Widget Project. This is useful if you already have an existing class for the UI business logic.",
- "trDisplayName": "Qt Designer Form",
+ "trDescription": "Creates a Qt Widgets Designer form that you can add to a Qt Widget Project. This is useful if you already have an existing class for the UI business logic.",
+ "trDisplayName": "Qt Widgets Designer Form",
"trDisplayCategory": "Qt",
"iconText": "ui",
"enabled": "%{JS: value('Plugins').indexOf('Designer') >= 0}",
diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/wizard.json b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/wizard.json
index c52c05c12c..6d3f968c1b 100644
--- a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/wizard.json
+++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/wizard.json
@@ -3,7 +3,7 @@
"supportedProjectTypes": [ "PythonProject" ],
"id": "F.QtForPythonApplicationWindowWidget",
"category": "F.ApplicationPySide",
- "trDescription": "Creates a Qt for Python application that includes a Qt Designer-based widget (ui file). Requires .ui to Python conversion.",
+ "trDescription": "Creates a Qt for Python application that includes a Qt Widgets Designer-based widget (ui file). Requires .ui to Python conversion.",
"trDisplayName": "Window UI",
"trDisplayCategory": "Application (Qt for Python)",
"icon": "../icons/icon.png",
diff --git a/share/qtcreator/templates/wizards/projects/qtwidgetsapplication/wizard.json b/share/qtcreator/templates/wizards/projects/qtwidgetsapplication/wizard.json
index 098cb4f0ec..48791e4fd8 100644
--- a/share/qtcreator/templates/wizards/projects/qtwidgetsapplication/wizard.json
+++ b/share/qtcreator/templates/wizards/projects/qtwidgetsapplication/wizard.json
@@ -3,7 +3,7 @@
"supportedProjectTypes": [ "MesonProjectManager.MesonProject","CMakeProjectManager.CMakeProject", "Qt4ProjectManager.Qt4Project", "Qbs.QbsProject" ],
"id": "C.QtWidgets",
"category": "D.ApplicationQt",
- "trDescription": "Creates a widget-based Qt application that contains a Qt Designer-based main window and C++ source and header files to implement the application logic.\n\nPreselects a desktop Qt for building the application if available.",
+ "trDescription": "Creates a widget-based Qt application that contains a Qt Widgets Designer-based main window and C++ source and header files to implement the application logic.\n\nPreselects a desktop Qt for building the application if available.",
"trDisplayName": "Qt Widgets Application",
"trDisplayCategory": "Application (Qt)",
"icon": "../../global/guiapplication.png",
diff --git a/share/qtcreator/translations/README.md b/share/qtcreator/translations/README.md
index ff9caddf2a..7261305a4b 100644
--- a/share/qtcreator/translations/README.md
+++ b/share/qtcreator/translations/README.md
@@ -80,9 +80,9 @@ How To add translations to Qt Creator
*Note:* `.qm` files are generated as part of the regular build. They are not
submitted to the repository.
-*Note:* QmlDesigner contains code from the Gradient Editor of Qt Designer. If an
-official translation of Qt for your language exists, you can re-use the
-translation of those messages by merging Qt Creator's and Qt Designer's
+*Note:* QmlDesigner contains code from the Gradient Editor of Qt Widgets Designer.
+If an official translation of Qt for your language exists, you can re-use the
+translation of those messages by merging Qt Creator's and Qt Widgets Designer's
translation using `lconvert`:
lconvert qtcreator_<LANG>.ts $QTDIR/translations/designer_<LANG>.ts > temp.ts
@@ -91,6 +91,6 @@ Move the temporary file back to `qtcreator_<LANG>.ts`, complete the Gradient
Editor's translations and update the file, passing the additional option
`-noobsolete` to `lupdate` (by temporarily modifying
`<qtcreator>/cmake/QtCreatorTranslations.cmake`). This will remove the now
-redundant messages originating from Qt Designer.
+redundant messages originating from Qt Widgets Designer.
[1]: https://lists.qt-project.org/listinfo/localization
diff --git a/share/qtcreator/translations/qtcreator_cs.ts b/share/qtcreator/translations/qtcreator_cs.ts
index 94bbf49d0b..23d62545e1 100644
--- a/share/qtcreator/translations/qtcreator_cs.ts
+++ b/share/qtcreator/translations/qtcreator_cs.ts
@@ -764,7 +764,7 @@
<translation type="obsolete">Automaticky vkládat středníky a uzavírající závorky, kulaté závorky, složené závorky a uvozovky, když je to vhodné.</translation>
</message>
<message>
- <source>&amp;Automatically insert matching characters</source>
+ <source>&amp;Automatically Insert Matching Characters</source>
<translation type="obsolete">&amp;Automaticky vložit odpovídající znaky</translation>
</message>
<message>
@@ -2008,7 +2008,7 @@ Chcete je nechat přepsat?</translation>
<translation>Když je to vhodné, automaticky vkládat středníky a uzavírající závorky, kulaté závorky, složené závorky a uvozovky.</translation>
</message>
<message>
- <source>&amp;Automatically insert matching characters</source>
+ <source>&amp;Automatically Insert Matching Characters</source>
<translation>&amp;Automaticky vložit odpovídající znaky</translation>
</message>
<message>
@@ -4278,8 +4278,8 @@ Rebuilding the project might help.</source>
Zkuste projekt vytvořit znovu.</translation>
</message>
<message>
- <source>Qt Designer Form Class</source>
- <translation>Třída formuláře programu Qt Designer</translation>
+ <source>Qt Widgets Designer Form Class</source>
+ <translation>Třída formuláře programu Qt Widgets Designer</translation>
</message>
<message>
<source>Form Template</source>
@@ -4326,8 +4326,8 @@ Zkuste projekt vytvořit znovu.</translation>
<translation>Shift+F4</translation>
</message>
<message>
- <source>Qt Designer Form</source>
- <translation>Formulář programu Qt Designer</translation>
+ <source>Qt Widgets Designer Form</source>
+ <translation>Formulář programu Qt Widgets Designer</translation>
</message>
<message>
<source>Creates a Qt Designer form that you can add to a Qt Widget Project. This is useful if you already have an existing class for the UI business logic.</source>
@@ -4458,8 +4458,8 @@ Zkuste projekt vytvořit znovu.</translation>
<translation>Alt+Shift+R</translation>
</message>
<message>
- <source>About Qt Designer Plugins...</source>
- <translation>O přídavných modulech programu Qt Designer...</translation>
+ <source>About Qt Widgets Designer Plugins...</source>
+ <translation>O přídavných modulech programu Qt Widgets Designer...</translation>
</message>
<message>
<source>Views</source>
@@ -10752,8 +10752,8 @@ Vybere pro sestavení programu verzi Qt pro stolní poÄítaÄ, je-li dostupná.
<translation>Tento průvodce vytvoří projekt konzolové aplikace v Qt4. Aplikace je odvozena z QCoreApplication a nemá žádné uživatelské rozhraní.</translation>
</message>
<message>
- <source>Qt Designer is not responding (%1).</source>
- <translation>Qt Designer neodpovídá (%1).</translation>
+ <source>Qt Widgets Designer is not responding (%1).</source>
+ <translation>Qt Widgets Designer neodpovídá (%1).</translation>
</message>
<message>
<source>Unable to create server socket: %1</source>
@@ -16025,8 +16025,8 @@ Toho se dosáhne vložením této zkratky v zadávacím poli vyhledávaÄe, nás
<translation type="obsolete">Editor .qmlproject</translation>
</message>
<message>
- <source>Qt Designer</source>
- <translation>Qt Designer</translation>
+ <source>Qt Widgets Designer</source>
+ <translation>Qt Widgets Designer</translation>
</message>
<message>
<source>Qt Linguist</source>
@@ -17504,7 +17504,7 @@ Důvod: %2</translation>
</message>
<message>
<source>Qt Custom Designer Widget</source>
- <translation>Uživatelsky stanovený prvek pro Qt Designer</translation>
+ <translation>Uživatelsky stanovený prvek pro Qt Widgets Designer</translation>
</message>
<message>
<source>Creates a Qt Custom Designer Widget or a Custom Widget Collection.</source>
@@ -40550,7 +40550,7 @@ UrÄuje chování odsazení se zÅ™etelem k navazujícím řádkům.
<translation>Nastavit znovu</translation>
</message>
<message>
- <source>Scanning scope</source>
+ <source>Scanning Scope</source>
<translation>Oblast prohledávání</translation>
</message>
<message>
diff --git a/share/qtcreator/translations/qtcreator_da.ts b/share/qtcreator/translations/qtcreator_da.ts
index ca9e8e47a4..53c0246ee3 100644
--- a/share/qtcreator/translations/qtcreator_da.ts
+++ b/share/qtcreator/translations/qtcreator_da.ts
@@ -338,12 +338,12 @@ Minimum API-niveauet krævet af kittet er %1.</translation>
</translation>
</message>
<message>
- <source>
-Uninstalling the installed package may solve the issue.
-Do you want to uninstall the existing package?</source>
- <translation>
-Afinstallation af den installerede pakke løser måske problemstillingen.
-Vil du afinstallere den eksisterende pakke?</translation>
+ <source>Uninstalling the installed package may solve the issue.</source>
+ <translation>Afinstallation af den installerede pakke løser måske problemstillingen.</translation>
+ </message>
+ <message>
+ <source>Do you want to uninstall the existing package?</source>
+ <translation>Vil du afinstallere den eksisterende pakke?</translation>
</message>
<message>
<source>Install failed</source>
@@ -13547,8 +13547,8 @@ Det hjælper måske at genbygge projektet.</translation>
<translation>Designer</translation>
</message>
<message>
- <source>Qt Designer Form Class</source>
- <translation>Qt Designer-udformningsklasse</translation>
+ <source>Qt Widgets Designer Form Class</source>
+ <translation>Qt Widgets Designer-udformningsklasse</translation>
</message>
<message>
<source>Choose a Class Name</source>
@@ -13571,8 +13571,8 @@ Det hjælper måske at genbygge projektet.</translation>
<translation>Skift kilde/udformning</translation>
</message>
<message>
- <source>Creates a Qt Designer form along with a matching class (C++ header and source file) for implementation purposes. You can add the form and class to an existing Qt Widget Project.</source>
- <translation>Opretter en Qt Designer-udformning sammen med en matchende klasse (C++-header og kildefil) til implementeringsformål. Du kan tilføje udformningen og klassen til et eksisterende Qt widget-projekt.</translation>
+ <source>Creates a Qt Widgets Designer form along with a matching class (C++ header and source file) for implementation purposes. You can add the form and class to an existing Qt Widget Project.</source>
+ <translation>Opretter en Qt Widgets Designer-udformning sammen med en matchende klasse (C++-header og kildefil) til implementeringsformål. Du kan tilføje udformningen og klassen til et eksisterende Qt widget-projekt.</translation>
</message>
<message>
<source>For&amp;m Editor</source>
@@ -14990,8 +14990,8 @@ når de ikke kræves, hvilket i de fleste tilfælde vil forbedre ydelsen.</trans
<translation>Alt+Skift+R</translation>
</message>
<message>
- <source>About Qt Designer Plugins...</source>
- <translation>Om Qt Designer-plugins...</translation>
+ <source>About Qt Widgets Designer Plugins...</source>
+ <translation>Om Qt Widgets Designer-plugins...</translation>
</message>
<message>
<source>Preview in</source>
@@ -19970,8 +19970,8 @@ Fejl: %5</translation>
<translation>Python-redigering</translation>
</message>
<message>
- <source>Qt Designer</source>
- <translation>Qt Designer</translation>
+ <source>Qt Widgets Designer</source>
+ <translation>Qt Widgets Designer</translation>
</message>
<message>
<source>Qt Linguist</source>
@@ -23386,12 +23386,12 @@ til projektet &quot;%2&quot;.</translation>
<translation>Udformningsskabelon</translation>
</message>
<message>
- <source>Creates a Qt Designer form that you can add to a Qt Widget Project. This is useful if you already have an existing class for the UI business logic.</source>
- <translation>Opretter en Qt Designer-udformning som du kan tilføje til et Qt widget-projekt. Dette er nyttigt hvis du allerede har en eksisterende klasse til brugerflade travlhedslogik.</translation>
+ <source>Creates a Qt Widgets Designer form that you can add to a Qt Widget Project. This is useful if you already have an existing class for the UI business logic.</source>
+ <translation>Opretter en Qt Widgets Designer-udformning som du kan tilføje til et Qt widget-projekt. Dette er nyttigt hvis du allerede har en eksisterende klasse til brugerflade travlhedslogik.</translation>
</message>
<message>
- <source>Qt Designer Form</source>
- <translation>Qt Designer-udformning</translation>
+ <source>Qt Widgets Designer Form</source>
+ <translation>Qt Widgets Designer-udformning</translation>
</message>
<message>
<source>Creates a Java file with boilerplate code.</source>
@@ -26266,12 +26266,12 @@ Opdater venligst dit kit eller vælg en mkspec for qmake som matcher dit mål-mi
<translation>Opretter et Qt brugerdefineret Designer-widget eller en brugerdefineret widget-samling.</translation>
</message>
<message>
- <source>This wizard generates a Qt Designer Custom Widget or a Qt Designer Custom Widget Collection project.</source>
- <translation>Denne assistent genererer en Qt Designer brugerdefineret widget eller et Qt Designer brugerdefineret widget-samling-projekt.</translation>
+ <source>This wizard generates a Qt Widgets Designer Custom Widget or a Qt Widgets Designer Custom Widget Collection project.</source>
+ <translation>Denne assistent genererer en Qt Widgets Designer brugerdefineret widget eller et Qt Widgets Designer brugerdefineret widget-samling-projekt.</translation>
</message>
<message>
- <source>Qt Designer is not responding (%1).</source>
- <translation>Qt Designer svarer ikke (%1).</translation>
+ <source>Qt Widgets Designer is not responding (%1).</source>
+ <translation>Qt Widgets Designer svarer ikke (%1).</translation>
</message>
<message>
<source>Unable to create server socket: %1</source>
@@ -26342,10 +26342,10 @@ Opdater venligst dit kit eller vælg en mkspec for qmake som matcher dit mål-mi
<translation>Qt widgets-program</translation>
</message>
<message>
- <source>Creates a Qt application for the desktop. Includes a Qt Designer-based main window.
+ <source>Creates a Qt application for the desktop. Includes a Qt Widgets Designer-based main window.
Preselects a desktop Qt for building the application if available.</source>
- <translation>Opretter et Qt-program til desktop. Inkluderer et Qt Designer-baseret hovedvindue.
+ <translation>Opretter et Qt-program til desktop. Inkluderer et Qt Widgets Designer-baseret hovedvindue.
Prævælger en desktop Qt til bygning af programmet, hvis tilgængeligt.</translation>
</message>
@@ -33804,7 +33804,7 @@ Derudover indsætter Skift+Enter et undvigetegn ved markørens placering og flyt
<translation>Opdel strenge automatisk</translation>
</message>
<message>
- <source>&amp;Automatically insert matching characters</source>
+ <source>&amp;Automatically Insert Matching Characters</source>
<translation>&amp;Indsæt automatisk matchende tegn</translation>
</message>
<message>
@@ -33984,7 +33984,7 @@ Derudover indsætter Skift+Enter et undvigetegn ved markørens placering og flyt
<translation>Justeret til højre side</translation>
</message>
<message>
- <source>Line annotations</source>
+ <source>Line Annotations</source>
<translation>Linjeannotationer</translation>
</message>
<message>
@@ -35591,7 +35591,7 @@ Vil blive anvendt på blanktegn i kommentarer og strenge.</translation>
<translation>Nulstil</translation>
</message>
<message>
- <source>Scanning scope</source>
+ <source>Scanning Scope</source>
<translation>Skanningsområde</translation>
</message>
<message>
diff --git a/share/qtcreator/translations/qtcreator_de.ts b/share/qtcreator/translations/qtcreator_de.ts
index 888a8bb765..3dc80d58d9 100644
--- a/share/qtcreator/translations/qtcreator_de.ts
+++ b/share/qtcreator/translations/qtcreator_de.ts
@@ -10495,7 +10495,7 @@ Locked components cannot be modified or selected.</source>
<translation>Pfad des JDK existiert und ist schreibbar.</translation>
</message>
<message>
- <source>Android OpenSSL settings (Optional)</source>
+ <source>Android OpenSSL Settings (Optional)</source>
<translation>OpenSSL-Einstellungen für Android (optional)</translation>
</message>
<message>
@@ -11245,12 +11245,12 @@ Das Kit unterstützt &quot;%2&quot;, aber das Gerät verwendet &quot;%3&quot;.</
</translation>
</message>
<message>
- <source>
-Uninstalling the installed package may solve the issue.
-Do you want to uninstall the existing package?</source>
- <translation>
-Das installierte Paket zu deinstallieren könnte das Problem lösen.
-Möchten Sie das vorhandene Paket deinstallieren?</translation>
+ <source>Uninstalling the installed package may solve the issue.</source>
+ <translation>Das installierte Paket zu deinstallieren könnte das Problem lösen.</translation>
+ </message>
+ <message>
+ <source>Do you want to uninstall the existing package?</source>
+ <translation>Möchten Sie das vorhandene Paket deinstallieren?</translation>
</message>
<message>
<source>The deployment AVD &quot;%1&quot; cannot be started.</source>
@@ -13108,7 +13108,7 @@ Dies könnte Probleme während der Ausführung verursachen.
<translation>Tests wiederholen</translation>
</message>
<message>
- <source>Run in parallel</source>
+ <source>Run in Parallel</source>
<translation>Parallel ausführen</translation>
</message>
<message>
@@ -16992,8 +16992,8 @@ Das integrierte Codemodell übernimmt das Einrücken.</translation>
<translation>Beachten Sie, dass das aktuelle Projekt eine .clang-format-Datei enthält, welche für das Einrücken und die Formatierung von Code verwendet werden wird.</translation>
</message>
<message>
- <source>ClangFormat settings:</source>
- <translation>Einstellungen für ClangFormat:</translation>
+ <source>ClangFormat Settings</source>
+ <translation>Einstellungen für ClangFormat</translation>
</message>
<message>
<source>Indenting only</source>
@@ -19523,8 +19523,8 @@ Wenn die Systemzeiger für das Verändern der Größe von Ansichten nicht korrek
<translation>QMLJS-Editor</translation>
</message>
<message>
- <source>Qt Designer</source>
- <translation>Qt Designer</translation>
+ <source>Qt Widgets Designer</source>
+ <translation>Qt Widgets Designer</translation>
</message>
<message>
<source>Qt Linguist</source>
@@ -22427,7 +22427,7 @@ Doppelklicken Sie einen Eintrag um ihn zu ändern.</translation>
<translation>#include %1 hinzufügen</translation>
</message>
<message>
- <source>Add forward declaration for %1</source>
+ <source>Add Forward Declaration for %1</source>
<translation>Vorwärtsdeklaration für %1 hinzufügen</translation>
</message>
<message>
@@ -23349,9 +23349,9 @@ Diese Präfixe werden zusätzlich zum Dateinamen beim Wechseln zwischen Header-
<translation>Normalerweise werden Argumente als konstante Referenz übergeben. Das Argument wird als Wert übergeben, wenn der Typ einer der folgenden ist. Namensräume und Vorlagenargumente werden entfernt. Der wirkliche Typ muss den angegebenen Typ enthalten. Zum Beispiel passt &quot;int&quot; zu &quot;int32_t&quot;, aber nicht zu &quot;vector&lt;int&gt;&quot;. &quot;vector&quot; passt zu &quot;std::pmr::vector&lt;int&gt;&quot;, aber nicht zu &quot;std::optional&lt;vector&lt;int&gt;&gt;&quot;</translation>
</message>
<message>
- <source>Value types:</source>
+ <source>Value Types</source>
<translatorcomment>https://learn.microsoft.com/de-de/cpp/cpp/value-types-modern-cpp</translatorcomment>
- <translation>Werttypen:</translation>
+ <translation>Werttypen</translation>
</message>
<message>
<source>Return non-value types by const reference</source>
@@ -23725,7 +23725,7 @@ Das integrierte Codemodell übernimmt Syntaxhervorhebung, Code-Vervollständigun
<translation>Zeitabstand für das Aktualisieren von Dokumenten:</translation>
</message>
<message>
- <source>Sessions with a single clangd instance</source>
+ <source>Sessions with a Single Clangd Instance</source>
<translation>Sitzungen mit einer einzigen Clangd-Instanz</translation>
</message>
<message>
@@ -28977,8 +28977,8 @@ Rebuilding the project might help.</source>
Versuchen Sie, das Projekt neu zu erstellen.</translation>
</message>
<message>
- <source>Qt Designer Form Class</source>
- <translation>Qt-Designer-Formularklasse</translation>
+ <source>Qt Widgets Designer Form Class</source>
+ <translation>Qt-Widgets-Designer-Formularklasse</translation>
</message>
<message>
<source>Class Details</source>
@@ -29009,8 +29009,8 @@ Versuchen Sie, das Projekt neu zu erstellen.</translation>
<translation>Shift+F4</translation>
</message>
<message>
- <source>Creates a Qt Designer form along with a matching class (C++ header and source file) for implementation purposes. You can add the form and class to an existing Qt Widget Project.</source>
- <translation>Erstellt ein Qt-Designer-Formular mit zugehörigem Klassenrumpf (bestehend aus C++-Header- und -Quelldatei) für Implementierungszwecke. Sie können Formular und Klasse zu einem existierenden Qt-Widget-Projekt hinzufügen.</translation>
+ <source>Creates a Qt Widgets Designer form along with a matching class (C++ header and source file) for implementation purposes. You can add the form and class to an existing Qt Widget Project.</source>
+ <translation>Erstellt ein Qt-Widgets-Designer-Formular mit zugehörigem Klassenrumpf (bestehend aus C++-Header- und -Quelldatei) für Implementierungszwecke. Sie können Formular und Klasse zu einem existierenden Qt-Widget-Projekt hinzufügen.</translation>
</message>
<message>
<source>Choose a Form Template</source>
@@ -29161,8 +29161,8 @@ Versuchen Sie, das Projekt neu zu erstellen.</translation>
<translation>Alt+Shift+R</translation>
</message>
<message>
- <source>About Qt Designer Plugins...</source>
- <translation>Ãœber Qt Designer-Plugins...</translation>
+ <source>About Qt Widgets Designer Plugins...</source>
+ <translation>Ãœber Qt Widgets Designer-Plugins...</translation>
</message>
<message>
<source>Preview in</source>
@@ -41285,10 +41285,10 @@ Bitte schließen Sie alle laufenden Instanzen Ihrer Anwendung vor dem Erstellen.
<translation>Pfad</translation>
</message>
<message>
- <source>Creates a widget-based Qt application that contains a Qt Designer-based main window and C++ source and header files to implement the application logic.
+ <source>Creates a widget-based Qt application that contains a Qt Widgets Designer-based main window and C++ source and header files to implement the application logic.
Preselects a desktop Qt for building the application if available.</source>
- <translation>Erstellt eine Widget-basierte Qt-Anwendung mit einem Qt Designer-basierten Hauptfenster und C++-Quell- und Headerdateien zur Implementierung der Applikationslogik.
+ <translation>Erstellt eine Widget-basierte Qt-Anwendung mit einem Qt Widgets Designer-basierten Hauptfenster und C++-Quell- und Headerdateien zur Implementierung der Applikationslogik.
Wählt eine für Desktop-Entwicklung geeignete Qt-Version aus, sofern verfügbar.</translation>
</message>
@@ -41381,11 +41381,11 @@ Wahlweise können Sie ein Projekt erstellen, das in Qt Design Studio geöffnet w
<translation>Formularvorlage</translation>
</message>
<message>
- <source>Creates a Qt Designer form that you can add to a Qt Widget Project. This is useful if you already have an existing class for the UI business logic.</source>
- <translation>Erstellt ein Qt-Designer-Formular, das Sie zu einem Qt Widget-Projekt hinzufügen können. Dies ist nützlich, wenn Sie bereits eine Klasse für die Anwendungslogik haben.</translation>
+ <source>Creates a Qt Widgets Designer form that you can add to a Qt Widget Project. This is useful if you already have an existing class for the UI business logic.</source>
+ <translation>Erstellt ein Qt-Widgets-Designer-Formular, das Sie zu einem Qt Widget-Projekt hinzufügen können. Dies ist nützlich, wenn Sie bereits eine Klasse für die Anwendungslogik haben.</translation>
</message>
<message>
- <source>Qt Designer Form</source>
+ <source>Qt Widgets Designer Form</source>
<translation>Qt-Designer-Formular</translation>
</message>
<message>
@@ -41899,8 +41899,8 @@ Sie sollten nicht mehrere Test-Frameworks im selben Projekt mischen.</translatio
<translation>Leeres Fenster</translation>
</message>
<message>
- <source>Creates a Qt for Python application that includes a Qt Designer-based widget (ui file). Requires .ui to Python conversion.</source>
- <translation>Erstellt eine Qt for Python-Anwendung, die ein Qt Designer-basiertes Widget (ui-Datei) enthält. Erfordert Umwandlung von .ui nach Python.</translation>
+ <source>Creates a Qt for Python application that includes a Qt Widgets Designer-based widget (ui file). Requires .ui to Python conversion.</source>
+ <translation>Erstellt eine Qt for Python-Anwendung, die ein Qt Widgets Designer-basiertes Widget (ui-Datei) enthält. Erfordert Umwandlung von .ui nach Python.</translation>
</message>
<message>
<source>Window UI</source>
@@ -45526,15 +45526,15 @@ Weder der Pfad zur Bibliothek noch der Pfad zu den Headerdateien wird zur .pro-D
</message>
<message>
<source>Qt Custom Designer Widget</source>
- <translation>Benutzerdefiniertes Widget für Qt Designer</translation>
+ <translation>Benutzerdefiniertes Widget für Qt Widgets Designer</translation>
</message>
<message>
<source>Creates a Qt Custom Designer Widget or a Custom Widget Collection.</source>
- <translation>Erstellt ein oder mehrere benutzerdefinierte Widgets für Qt Designer.</translation>
+ <translation>Erstellt ein oder mehrere benutzerdefinierte Widgets für Qt Widgets Designer.</translation>
</message>
<message>
- <source>This wizard generates a Qt Designer Custom Widget or a Qt Designer Custom Widget Collection project.</source>
- <translation>Dieser Assistent erstellt ein Projekt mit einem oder mehreren benutzerdefinierten Widgets für Qt Designer.</translation>
+ <source>This wizard generates a Qt Widgets Designer Custom Widget or a Qt Widgets Designer Custom Widget Collection project.</source>
+ <translation>Dieser Assistent erstellt ein Projekt mit einem oder mehreren benutzerdefinierten Widgets für Qt Widgets Designer.</translation>
</message>
<message>
<source>Creating multiple widget libraries (%1, %2) in one project (%3) is not supported.</source>
@@ -45553,8 +45553,8 @@ Weder der Pfad zur Bibliothek noch der Pfad zu den Headerdateien wird zur .pro-D
<translation>Die Anwendung &quot;%1&quot; konnte nicht gefunden werden.</translation>
</message>
<message>
- <source>Qt Designer is not responding (%1).</source>
- <translation>Qt Designer antwortet nicht (%1).</translation>
+ <source>Qt Widgets Designer is not responding (%1).</source>
+ <translation>Qt Widgets Designer antwortet nicht (%1).</translation>
</message>
<message>
<source>Unable to create server socket: %1</source>
@@ -53709,7 +53709,7 @@ Gibt an, wie sich die Rücktaste bezüglich Einrückung verhält.
<translation>Rechtsbündig</translation>
</message>
<message>
- <source>Line annotations</source>
+ <source>Line Annotations</source>
<translation>Zeilenannotationen</translation>
</message>
<message>
@@ -54643,7 +54643,7 @@ Drücken Sie zusätzlich die Umschalttaste, wird ein Escape-Zeichen an der aktue
<translation>Zeichenketten automatisch teilen</translation>
</message>
<message>
- <source>&amp;Automatically insert matching characters</source>
+ <source>&amp;Automatically Insert Matching Characters</source>
<translation>&amp;Passende Zeichen automatisch einfügen</translation>
</message>
<message>
@@ -55682,7 +55682,7 @@ Außer Leerzeichen innerhalb von Kommentaren und Zeichenketten.</translation>
<translation>Zurücksetzen</translation>
</message>
<message>
- <source>Scanning scope</source>
+ <source>Scanning Scope</source>
<translation>Suchbereich</translation>
</message>
<message>
diff --git a/share/qtcreator/translations/qtcreator_es.ts b/share/qtcreator/translations/qtcreator_es.ts
index 623ffc7e0a..d9de9393a9 100644
--- a/share/qtcreator/translations/qtcreator_es.ts
+++ b/share/qtcreator/translations/qtcreator_es.ts
@@ -3777,8 +3777,8 @@ Es recomendado usar gdb 6.7 o posterior.</translation>
<translation type="obsolete">Editor de signals/slots</translation>
</message>
<message>
- <source>Qt Designer Form Class</source>
- <translation>Clase del formulario Qt Designer</translation>
+ <source>Qt Widgets Designer Form Class</source>
+ <translation>Clase del formulario Qt Widgets Designer</translation>
</message>
<message>
<source>%1 - Error</source>
@@ -3825,20 +3825,20 @@ Es recomendado usar gdb 6.7 o posterior.</translation>
<translation></translation>
</message>
<message>
- <source>Qt Designer Form</source>
+ <source>Qt Widgets Designer Form</source>
<translation>Diseñador de formularios de Qt</translation>
</message>
<message>
- <source>Creates a Qt Designer form file (.ui).</source>
- <translation>Crea un archivo de formulario de Qt Designer (.ui).</translation>
+ <source>Creates a Qt Widgets Designer form file (.ui).</source>
+ <translation>Crea un archivo de formulario de Qt Widgets Designer (.ui).</translation>
</message>
<message>
- <source>Creates a Qt Designer form file (.ui) with a matching class.</source>
- <translation>Crea un archivo de formulario de Qt Designer (.ui) para una clase.</translation>
+ <source>Creates a Qt Widgets Designer form file (.ui) with a matching class.</source>
+ <translation>Crea un archivo de formulario de Qt Widgets Designer (.ui) para una clase.</translation>
</message>
<message>
- <source>Qt Designer Form Class</source>
- <translation>Clase del formulario de Qt Designer</translation>
+ <source>Qt Widgets Designer Form Class</source>
+ <translation>Clase del formulario de Qt Widgets Designer</translation>
</message>
<message>
<source>Object inspector</source>
@@ -3953,8 +3953,8 @@ Es recomendado usar gdb 6.7 o posterior.</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>About Qt Designer plugins....</source>
- <translation>Acerca de los plugins de Qt Designer...</translation>
+ <source>About Qt Widgets Designer plugins....</source>
+ <translation>Acerca de los plugins de Qt Widgets Designer...</translation>
</message>
<message>
<source>Preview in</source>
@@ -3989,8 +3989,8 @@ Es recomendado usar gdb 6.7 o posterior.</translation>
<translation>Imposible escribir a %1: %2</translation>
</message>
<message>
- <source>Qt Designer Form</source>
- <translation>Formulario de Qt Designer</translation>
+ <source>Qt Widgets Designer Form</source>
+ <translation>Formulario de Qt Widgets Designer</translation>
</message>
<message>
<source>The class definition of &apos;%1&apos; could not be found in %2.</source>
@@ -7459,8 +7459,8 @@ al control de versiones (%2)?</translation>
<translation>Este asistente genera un proyecto de aplicación Qt4 para consola. La aplicación es derivada de QCoreApplication y no provee interfaz gráfica de usuario.</translation>
</message>
<message>
- <source>Qt Designer is not responding (%1).</source>
- <translation>Qt Designer no está respondiendo (%1).</translation>
+ <source>Qt Widgets Designer is not responding (%1).</source>
+ <translation>Qt Widgets Designer no está respondiendo (%1).</translation>
</message>
<message>
<source>Unable to create server socket: %1</source>
diff --git a/share/qtcreator/translations/qtcreator_fr.ts b/share/qtcreator/translations/qtcreator_fr.ts
index c7057fc09e..ff5318759a 100644
--- a/share/qtcreator/translations/qtcreator_fr.ts
+++ b/share/qtcreator/translations/qtcreator_fr.ts
@@ -10730,7 +10730,7 @@ dans le navigateur système pour un téléchargement manuel.</translation>
<translation>Liste des NDK Android&#xa0;:</translation>
</message>
<message>
- <source>Android OpenSSL settings (Optional)</source>
+ <source>Android OpenSSL Settings (Optional)</source>
<translation>Paramètres Android d&apos;OpenSSL (optionnel)</translation>
</message>
<message>
@@ -11066,12 +11066,12 @@ Le kit supporte «&#xa0;%2&#xa0;», mais le périphérique utilise «&#xa0;%3&#x
</translation>
</message>
<message>
- <source>
-Uninstalling the installed package may solve the issue.
-Do you want to uninstall the existing package?</source>
- <translation>
-La désinstallation du paquet installé peut résoudre le problème.
-Voulez-vous désinstaller le paquet existant&#xa0;?</translation>
+ <source>Uninstalling the installed package may solve the issue.</source>
+ <translation>La désinstallation du paquet installé peut résoudre le problème.</translation>
+ </message>
+ <message>
+ <source>Do you want to uninstall the existing package?</source>
+ <translation>Voulez-vous désinstaller le paquet existant&#xa0;?</translation>
</message>
<message>
<source>Install failed</source>
@@ -12712,7 +12712,7 @@ Exécutable&#xa0;: %2</translation>
<translation>Répétition des tests</translation>
</message>
<message>
- <source>Run in parallel</source>
+ <source>Run in Parallel</source>
<translation>Exécuter en parallèle</translation>
</message>
<message>
@@ -20440,8 +20440,8 @@ To do this, you type this shortcut and a space in the Locator entry field, and t
<translation>Éditeur QMLJS</translation>
</message>
<message>
- <source>Qt Designer</source>
- <translation>Qt Designer</translation>
+ <source>Qt Widgets Designer</source>
+ <translation>Qt Widgets Designer</translation>
</message>
<message>
<source>Qt Linguist</source>
@@ -22228,7 +22228,7 @@ Le modèle de code intégré gèrera le surlignage, la complétion, etc.</transl
<translation>Seuil de mise à jour du document :</translation>
</message>
<message>
- <source>Sessions with a single clangd instance</source>
+ <source>Sessions with a Single Clangd Instance</source>
<translation>Sessions avec une seule instance de clangd</translation>
</message>
<message>
@@ -22450,7 +22450,7 @@ devraient être gérés par le même processus clangd, ajoutez-les ici.</transla
<translation>Convertir en Camel Case</translation>
</message>
<message>
- <source>Add forward declaration for %1</source>
+ <source>Add Forward Declaration for %1</source>
<translation>Ajouter la déclaration préalable pour %1</translation>
</message>
<message>
@@ -22982,8 +22982,8 @@ p.ex. nom = «&#xa0;m_test_toto_&#xa0;» :
<translation>Modèles de getters et setters personnalisés</translation>
</message>
<message>
- <source>Value types:</source>
- <translation>Types de valeur :</translation>
+ <source>Value Types</source>
+ <translation>Types de valeur</translation>
</message>
<message>
<source>Projects only</source>
@@ -28811,8 +28811,8 @@ L’accès au module ou la mise en place de points d’arrêt par fichier et par
<translation>Éditeur d’interface graphique</translation>
</message>
<message>
- <source>Qt Designer Form Class</source>
- <translation>Classe d’interface graphique Qt Designer</translation>
+ <source>Qt Widgets Designer Form Class</source>
+ <translation>Classe d’interface graphique Qt Widgets Designer</translation>
</message>
<message>
<source>Form Template</source>
@@ -28835,12 +28835,12 @@ L’accès au module ou la mise en place de points d’arrêt par fichier et par
<translation>Sélectionner un nom de classe</translation>
</message>
<message>
- <source>Creates a Qt Designer form along with a matching class (C++ header and source file) for implementation purposes. You can add the form and class to an existing Qt Widget Project.</source>
- <translation>Crée un formulaire Qt Designer avec une classe correspondante (en-tête C++ et fichier source) pour implémentation. Vous pouvez ajouter le formulaire et la classe à un projet Qt Widget existant.</translation>
+ <source>Creates a Qt Widgets Designer form along with a matching class (C++ header and source file) for implementation purposes. You can add the form and class to an existing Qt Widget Project.</source>
+ <translation>Crée un formulaire Qt Widgets Designer avec une classe correspondante (en-tête C++ et fichier source) pour implémentation. Vous pouvez ajouter le formulaire et la classe à un projet Qt Widget existant.</translation>
</message>
<message>
<source>Widget Box</source>
- <translatorcomment>this translation must coherent with the translation of Qt Designer</translatorcomment>
+ <translatorcomment>this translation must coherent with the translation of Qt Widgets Designer</translatorcomment>
<translation>Boite de widget</translation>
</message>
<message>
@@ -28937,8 +28937,8 @@ L’accès au module ou la mise en place de points d’arrêt par fichier et par
<translation>Maj+F4</translation>
</message>
<message>
- <source>About Qt Designer Plugins...</source>
- <translation>À propos des greffons de Qt Designer…</translation>
+ <source>About Qt Widgets Designer Plugins...</source>
+ <translation>À propos des greffons de Qt Widgets Designer…</translation>
</message>
<message>
<source>Signals &amp;&amp; Slots Editor</source>
@@ -40386,8 +40386,8 @@ Présélectionne un Qt optimisé pour le bureau pour compiler l&apos;application
<translation>Définir l&apos;interpréteur Python</translation>
</message>
<message>
- <source>Creates a Qt for Python application that includes a Qt Designer-based widget (ui file). Requires .ui to Python conversion.</source>
- <translation>Génère une application Qt pour Python qui inclût un widget basé sur Qt Designer (fichier ui). Nécessite une conversion de .ui vers Python.</translation>
+ <source>Creates a Qt for Python application that includes a Qt Widgets Designer-based widget (ui file). Requires .ui to Python conversion.</source>
+ <translation>Génère une application Qt pour Python qui inclût un widget basé sur Qt Widgets Designer (fichier ui). Nécessite une conversion de .ui vers Python.</translation>
</message>
<message>
<source>Creates a Qt Quick UI project for previewing and prototyping designs.
@@ -40910,10 +40910,10 @@ Utilisez cette version «&#xa0;de compatibilité&#xa0;» si vous souhaitez utili
<translation>Application Qt Quick (compatibilité)</translation>
</message>
<message>
- <source>Creates a widget-based Qt application that contains a Qt Designer-based main window and C++ source and header files to implement the application logic.
+ <source>Creates a widget-based Qt application that contains a Qt Widgets Designer-based main window and C++ source and header files to implement the application logic.
Preselects a desktop Qt for building the application if available.</source>
- <translation>Génère une application basée sur les widgets qui contient une fenêtre principale basée sur Qt Designer et un ensemble de fichiers d&apos;entêtes et de sources C++ pour implémenter la logique de l&apos;application.
+ <translation>Génère une application basée sur les widgets qui contient une fenêtre principale basée sur Qt Widgets Designer et un ensemble de fichiers d&apos;entêtes et de sources C++ pour implémenter la logique de l&apos;application.
Sélectionne un Qt optimisé pour bureaux pour compiler l&apos;application, si disponible.</translation>
</message>
@@ -41254,12 +41254,12 @@ Sélectionne un Qt optimisé pour bureaux pour compiler l&apos;application, si d
<translation>Modèle d’interface graphique</translation>
</message>
<message>
- <source>Creates a Qt Designer form that you can add to a Qt Widget Project. This is useful if you already have an existing class for the UI business logic.</source>
- <translation>Crée un formulaire Qt Designer que l’on peut ajouter à un projet Qt Widget. Ceci est utile si vous utilisez déjà une classe pour la logique métier de l’interface.</translation>
+ <source>Creates a Qt Widgets Designer form that you can add to a Qt Widget Project. This is useful if you already have an existing class for the UI business logic.</source>
+ <translation>Crée un formulaire Qt Widgets Designer que l’on peut ajouter à un projet Qt Widget. Ceci est utile si vous utilisez déjà une classe pour la logique métier de l’interface.</translation>
</message>
<message>
- <source>Qt Designer Form</source>
- <translation>Interface graphique Qt Designer</translation>
+ <source>Qt Widgets Designer Form</source>
+ <translation>Interface graphique Qt Widgets Designer</translation>
</message>
<message>
<source>Creates a source file that you can add to a C/C++ project.</source>
@@ -45032,8 +45032,8 @@ Les fichiers affectés sont :
<context>
<name>QtC::QmakeProjectManager</name>
<message>
- <source>Qt Designer is not responding (%1).</source>
- <translation>Qt Designer ne répond pas (%1).</translation>
+ <source>Qt Widgets Designer is not responding (%1).</source>
+ <translation>Qt Widgets Designer ne répond pas (%1).</translation>
</message>
<message>
<source>Unable to create server socket: %1</source>
@@ -45404,9 +45404,9 @@ Les fichiers affectés sont :
<translation>Crée un widget personnalisé ou une collection de widgets personnalisés pour Qt4 Designer.</translation>
</message>
<message>
- <source>This wizard generates a Qt Designer Custom Widget or a Qt Designer Custom Widget Collection project.</source>
- <translatorcomment>lourd ? &quot;contenant un widget ou une collection de widgets, personnalisé pour Qt Designer&quot; ?</translatorcomment>
- <translation>Cet assistant génère un projet contenant un widget ou une collection de widgets personnalisés pour Qt Designer.</translation>
+ <source>This wizard generates a Qt Widgets Designer Custom Widget or a Qt Widgets Designer Custom Widget Collection project.</source>
+ <translatorcomment>lourd ? &quot;contenant un widget ou une collection de widgets, personnalisé pour Qt Widgets Designer&quot; ?</translatorcomment>
+ <translation>Cet assistant génère un projet contenant un widget ou une collection de widgets personnalisés pour Qt Widgets Designer.</translation>
</message>
<message>
<source>Custom Widgets</source>
@@ -52717,7 +52717,7 @@ si le commentaire débute avec «&#xa0;/*!&#xa0;» ou «&#xa0;//!&#xa0;».</tran
<translation>À la demande</translation>
</message>
<message>
- <source>&amp;Automatically insert matching characters</source>
+ <source>&amp;Automatically Insert Matching Characters</source>
<translation>Insérer &amp;automatiquement les caractères correspondants</translation>
</message>
<message>
@@ -54974,7 +54974,7 @@ francis : en effet, une erreur de ma part --&gt; validé.</translatorcomment>
<translation>Entre les lignes</translation>
</message>
<message>
- <source>Line annotations</source>
+ <source>Line Annotations</source>
<translation>Annotations de ligne</translation>
</message>
<message>
@@ -55417,7 +55417,7 @@ Influence l’indentation des lignes de continuation.
<translation>Scanner le sous-projet actuel</translation>
</message>
<message>
- <source>Scanning scope</source>
+ <source>Scanning Scope</source>
<translation>Portée de la recherche</translation>
</message>
<message>
diff --git a/share/qtcreator/translations/qtcreator_hr.ts b/share/qtcreator/translations/qtcreator_hr.ts
index ad756fc8ef..acd2770cab 100644
--- a/share/qtcreator/translations/qtcreator_hr.ts
+++ b/share/qtcreator/translations/qtcreator_hr.ts
@@ -8025,7 +8025,7 @@ In addition, Shift+Enter inserts an escape character at the cursor position and
<translation>Automatski rastavi znakovne nizove</translation>
</message>
<message>
- <source>&amp;Automatically insert matching characters</source>
+ <source>&amp;Automatically Insert Matching Characters</source>
<translation>&amp;Automatski umetni poklapajuće znakove</translation>
</message>
<message>
@@ -8193,7 +8193,7 @@ In addition, Shift+Enter inserts an escape character at the cursor position and
<translation>OznaÄi promjene u &amp;tekstu</translation>
</message>
<message>
- <source>Line annotations</source>
+ <source>Line Annotations</source>
<translation>Napomene za retke</translation>
</message>
<message>
@@ -8443,7 +8443,7 @@ UtjeÄe na uvlaÄenje neprekinutih redaka.
<translation>Resetiraj</translation>
</message>
<message>
- <source>Scanning scope</source>
+ <source>Scanning Scope</source>
<translation>Raspon pretrage</translation>
</message>
<message>
@@ -13221,9 +13221,11 @@ The minimum API level required by the kit is %1.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>
-Uninstalling the installed package may solve the issue.
-Do you want to uninstall the existing package?</source>
+ <source>Uninstalling the installed package may solve the issue.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Do you want to uninstall the existing package?</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -15304,8 +15306,8 @@ Check the test environment.</source>
<translation>Python ureÄ‘ivaÄ</translation>
</message>
<message>
- <source>Qt Designer</source>
- <translation>Qt Designer</translation>
+ <source>Qt Widgets Designer</source>
+ <translation>Qt Widgets Designer</translation>
</message>
<message>
<source>Qt Linguist</source>
@@ -20900,8 +20902,8 @@ Rebuilding the project might help.</source>
<translation>Alt+Shift+R</translation>
</message>
<message>
- <source>About Qt Designer Plugins...</source>
- <translation>O Qt Designer dodacima …</translation>
+ <source>About Qt Widgets Designer Plugins...</source>
+ <translation>O Qt Widgets Designer dodacima …</translation>
</message>
<message>
<source>Preview in</source>
diff --git a/share/qtcreator/translations/qtcreator_hu.ts b/share/qtcreator/translations/qtcreator_hu.ts
index 9f29949c84..23c59c4d41 100644
--- a/share/qtcreator/translations/qtcreator_hu.ts
+++ b/share/qtcreator/translations/qtcreator_hu.ts
@@ -5235,8 +5235,8 @@ A projekt újraépítése talán segít.</translation>
<translation>Qt modulnevek használata az #include irányelvekben</translation>
</message>
<message>
- <source>Qt Designer Form Class</source>
- <translation>Qt Designer forma osztály</translation>
+ <source>Qt Widgets Designer Form Class</source>
+ <translation>Qt Widgets Designer forma osztály</translation>
</message>
<message>
<source>Choose a class name</source>
@@ -5259,16 +5259,16 @@ A projekt újraépítése talán segít.</translation>
<translation>Qt</translation>
</message>
<message>
- <source>Qt Designer Form</source>
- <translation>Qt Designer forma</translation>
+ <source>Qt Widgets Designer Form</source>
+ <translation>Qt Widgets Designer forma</translation>
</message>
<message>
- <source>Creates a Qt Designer form file (.ui).</source>
- <translation>Qt Designer forma fájl (.ui) létrehozása.</translation>
+ <source>Creates a Qt Widgets Designer form file (.ui).</source>
+ <translation>Qt Widgets Designer forma fájl (.ui) létrehozása.</translation>
</message>
<message>
- <source>Creates a Qt Designer form file (.ui) with a matching class.</source>
- <translation>Qt Designer forma fájl(.ui) létrehozása illeszkedő osztályokkal.</translation>
+ <source>Creates a Qt Widgets Designer form file (.ui) with a matching class.</source>
+ <translation>Qt Widgets Designer forma fájl(.ui) létrehozása illeszkedő osztályokkal.</translation>
</message>
<message>
<source>Widget Box</source>
@@ -5371,8 +5371,8 @@ A projekt újraépítése talán segít.</translation>
<translation>Ctrl+Alt+R</translation>
</message>
<message>
- <source>About Qt Designer plugins....</source>
- <translation>Névjegy a Qt Designer beépülő moduljairól...</translation>
+ <source>About Qt Widgets Designer plugins....</source>
+ <translation>Névjegy a Qt Widgets Designer beépülő moduljairól...</translation>
</message>
<message>
<source>Preview in</source>
@@ -8651,8 +8651,8 @@ SOURCES *= .../ide/main/bin/dumper/dumper.cpp(new line)
<translation>CVS feltöltési sablon</translation>
</message>
<message>
- <source>Qt Designer file</source>
- <translation>Qt Designer fájl</translation>
+ <source>Qt Widgets Designer file</source>
+ <translation>Qt Widgets Designer fájl</translation>
</message>
<message>
<source>Generic Qt Creator Project file</source>
@@ -12834,8 +12834,8 @@ p, li { white-space: pre-wrap; }
<translation>Ez a varázsló egy Qt4 Designer szokásos Widget-t vagy Qt4 Designer szokásos Widget gyűjtemény projektet generál.</translation>
</message>
<message>
- <source>Qt Designer is not responding (%1).</source>
- <translation>A Qt Designer nem válaszol (%1).</translation>
+ <source>Qt Widgets Designer is not responding (%1).</source>
+ <translation>A Qt Widgets Designer nem válaszol (%1).</translation>
</message>
<message>
<source>Unable to create server socket: %1</source>
diff --git a/share/qtcreator/translations/qtcreator_it.ts b/share/qtcreator/translations/qtcreator_it.ts
index 549bf8f77b..211f409955 100644
--- a/share/qtcreator/translations/qtcreator_it.ts
+++ b/share/qtcreator/translations/qtcreator_it.ts
@@ -3652,8 +3652,8 @@ L&apos;utilizzo di gdb 6.7 o successivi è fortemente consigliato.</translation>
<translation type="obsolete">Editor di segnali e slot</translation>
</message>
<message>
- <source>Qt Designer Form Class</source>
- <translation>Classe Form di Qt Designer</translation>
+ <source>Qt Widgets Designer Form Class</source>
+ <translation>Classe Form di Qt Widgets Designer</translation>
</message>
<message>
<source>Choose a class name</source>
@@ -3700,16 +3700,16 @@ L&apos;utilizzo di gdb 6.7 o successivi è fortemente consigliato.</translation>
<translation>Qt</translation>
</message>
<message>
- <source>Qt Designer Form</source>
- <translation>Form di Qt Designer</translation>
+ <source>Qt Widgets Designer Form</source>
+ <translation>Form di Qt Widgets Designer</translation>
</message>
<message>
- <source>Creates a Qt Designer form file (.ui).</source>
- <translation>Crea un file form Qt Designer (.ui).</translation>
+ <source>Creates a Qt Widgets Designer form file (.ui).</source>
+ <translation>Crea un file form Qt Widgets Designer (.ui).</translation>
</message>
<message>
- <source>Creates a Qt Designer form file (.ui) with a matching class.</source>
- <translation>Crea un file form Qt Designer (.ui) e la relativa classe.</translation>
+ <source>Creates a Qt Widgets Designer form file (.ui) with a matching class.</source>
+ <translation>Crea un file form Qt Widgets Designer (.ui) e la relativa classe.</translation>
</message>
<message>
<source>Designer widgetbox</source>
@@ -3824,8 +3824,8 @@ L&apos;utilizzo di gdb 6.7 o successivi è fortemente consigliato.</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>About Qt Designer plugins....</source>
- <translation>Informazioni sui plugin Qt Designer...</translation>
+ <source>About Qt Widgets Designer plugins....</source>
+ <translation>Informazioni sui plugin Qt Widgets Designer...</translation>
</message>
<message>
<source>Preview in</source>
@@ -7309,8 +7309,8 @@ al VCS (%2)?</translation>
<translation>Questa procedura guidata genera un progetto per applicazione console Qt4. L&apos;applicazione deriva da QCoreApplication e non include una GUI.</translation>
</message>
<message>
- <source>Qt Designer is not responding (%1).</source>
- <translation>Qt Designer non risponde (%1).</translation>
+ <source>Qt Widgets Designer is not responding (%1).</source>
+ <translation>Qt Widgets Designer non risponde (%1).</translation>
</message>
<message>
<source>Unable to create server socket: %1</source>
diff --git a/share/qtcreator/translations/qtcreator_ja.ts b/share/qtcreator/translations/qtcreator_ja.ts
index 74f505017d..22f040580f 100644
--- a/share/qtcreator/translations/qtcreator_ja.ts
+++ b/share/qtcreator/translations/qtcreator_ja.ts
@@ -785,7 +785,7 @@
<translation>SDK ã®ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—</translation>
</message>
<message>
- <source>Android OpenSSL settings (Optional)</source>
+ <source>Android OpenSSL Settings (Optional)</source>
<translation>Android OpenSSL ã®è¨­å®š (オプション)</translation>
</message>
<message>
@@ -1895,7 +1895,7 @@ In addition, Shift+Enter inserts an escape character at the cursor position and
<translation>文字列を自動的ã«åˆ†å‰²ã™ã‚‹</translation>
</message>
<message>
- <source>&amp;Automatically insert matching characters</source>
+ <source>&amp;Automatically Insert Matching Characters</source>
<translation>マッãƒãƒ³ã‚°æ–‡å­—を自動的ã«æŒ¿å…¥ã™ã‚‹(&amp;A)</translation>
</message>
<message>
@@ -6322,7 +6322,7 @@ Backspace キーãŒæŠ¼ã•ã‚ŒãŸæ™‚ã®ã‚¤ãƒ³ãƒ‡ãƒ³ãƒˆã®å‹•ä½œã‚’指定ã—ã¾ã™ã
<translation>ファイル内ã®ãƒŠãƒ“ゲーションをアニメーション化ã™ã‚‹</translation>
</message>
<message>
- <source>Line annotations</source>
+ <source>Line Annotations</source>
<translation>ラインアノテーション</translation>
</message>
<message>
@@ -6678,7 +6678,7 @@ Influences the indentation of continuation lines.
<translation>リセット</translation>
</message>
<message>
- <source>Scanning scope</source>
+ <source>Scanning Scope</source>
<translation>スキャンã™ã‚‹ç¯„囲</translation>
</message>
<message>
@@ -10949,12 +10949,12 @@ with a password, which you can enter below.</source>
</translation>
</message>
<message>
- <source>
-Uninstalling the installed package may solve the issue.
-Do you want to uninstall the existing package?</source>
- <translation>
-インストールã•ã‚Œã¦ã„るパッケージをアンインストールã™ã‚‹ã¨ã€å•é¡ŒãŒè§£æ±ºã™ã‚‹å ´åˆãŒã‚ã‚Šã¾ã™ã€‚
-既存ã®ãƒ‘ッケージをアンインストールã—ã¾ã™ã‹ï¼Ÿ</translation>
+ <source>Uninstalling the installed package may solve the issue.</source>
+ <translation>インストールã•ã‚Œã¦ã„るパッケージをアンインストールã™ã‚‹ã¨ã€å•é¡ŒãŒè§£æ±ºã™ã‚‹å ´åˆãŒã‚ã‚Šã¾ã™ã€‚</translation>
+ </message>
+ <message>
+ <source>Do you want to uninstall the existing package?</source>
+ <translation>既存ã®ãƒ‘ッケージをアンインストールã—ã¾ã™ã‹ï¼Ÿ</translation>
</message>
<message>
<source>Package deploy: Running command &quot;%1&quot;.</source>
@@ -12152,8 +12152,8 @@ in the system&apos;s browser for manual download.</source>
<translation>Python エディタ</translation>
</message>
<message>
- <source>Qt Designer</source>
- <translation>Qt Designer</translation>
+ <source>Qt Widgets Designer</source>
+ <translation>Qt Widgets Designer</translation>
</message>
<message>
<source>Qt Linguist</source>
@@ -14319,7 +14319,7 @@ to version control (%2)
<translation>#include %1 を追加</translation>
</message>
<message>
- <source>Add forward declaration for %1</source>
+ <source>Add Forward Declaration for %1</source>
<translation>%1 ã®å‰æ–¹å®£è¨€ã‚’追加ã™ã‚‹</translation>
</message>
<message>
@@ -18978,8 +18978,8 @@ Rebuilding the project might help.</source>
プロジェクトã®ãƒªãƒ“ルドをãŠå¥¨ã‚ã—ã¾ã™ã€‚</translation>
</message>
<message>
- <source>Qt Designer Form Class</source>
- <translation>Qt Designer フォームクラス</translation>
+ <source>Qt Widgets Designer Form Class</source>
+ <translation>Qt Widgets Designer フォームクラス</translation>
</message>
<message>
<source>Switch Source/Form</source>
@@ -18994,8 +18994,8 @@ Rebuilding the project might help.</source>
<translation>Shift+F4</translation>
</message>
<message>
- <source>Creates a Qt Designer form along with a matching class (C++ header and source file) for implementation purposes. You can add the form and class to an existing Qt Widget Project.</source>
- <translation>既存㮠Qt ウィジェットプロジェクトã«è¿½åŠ å¯èƒ½ãª Qt Designer フォームã¨ãã‚Œã«å¯¾å¿œã—ãŸã‚¯ãƒ©ã‚¹ (C++ ヘッダã¨ã‚½ãƒ¼ã‚¹ãƒ•ã‚¡ã‚¤ãƒ«) を作æˆã—ã¾ã™ã€‚</translation>
+ <source>Creates a Qt Widgets Designer form along with a matching class (C++ header and source file) for implementation purposes. You can add the form and class to an existing Qt Widget Project.</source>
+ <translation>既存㮠Qt ウィジェットプロジェクトã«è¿½åŠ å¯èƒ½ãª Qt Widgets Designer フォームã¨ãã‚Œã«å¯¾å¿œã—ãŸã‚¯ãƒ©ã‚¹ (C++ ヘッダã¨ã‚½ãƒ¼ã‚¹ãƒ•ã‚¡ã‚¤ãƒ«) を作æˆã—ã¾ã™ã€‚</translation>
</message>
<message>
<source>Choose a Form Template</source>
@@ -25088,8 +25088,8 @@ Neither the path to the library nor the path to its includes is added to the .pr
<translation>Qt カスタム Designer ウィジェットã‹ã‚«ã‚¹ã‚¿ãƒ ã‚¦ã‚£ã‚¸ã‚§ãƒƒãƒˆã‚³ãƒ¬ã‚¯ã‚·ãƒ§ãƒ³ã‚’作æˆã—ã¾ã™ã€‚</translation>
</message>
<message>
- <source>This wizard generates a Qt Designer Custom Widget or a Qt Designer Custom Widget Collection project.</source>
- <translation>ã“ã®ã‚¦ã‚£ã‚¶ãƒ¼ãƒ‰ã¯ Qt Designer カスタムウィジェットã‚ã‚‹ã„㯠Qt Designer カスタムウィジェットコレクションプロジェクトを生æˆã—ã¾ã™ã€‚</translation>
+ <source>This wizard generates a Qt Widgets Designer Custom Widget or a Qt Widgets Designer Custom Widget Collection project.</source>
+ <translation>ã“ã®ã‚¦ã‚£ã‚¶ãƒ¼ãƒ‰ã¯ Qt Widgets Designer カスタムウィジェットã‚ã‚‹ã„㯠Qt Widgets Designer カスタムウィジェットコレクションプロジェクトを生æˆã—ã¾ã™ã€‚</translation>
</message>
<message>
<source>Creating multiple widget libraries (%1, %2) in one project (%3) is not supported.</source>
@@ -25140,8 +25140,8 @@ Neither the path to the library nor the path to its includes is added to the .pr
<translation>アプリケーション &quot;%1&quot; ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚</translation>
</message>
<message>
- <source>Qt Designer is not responding (%1).</source>
- <translation>Qt Designer ãŒç„¡å¿œç­”ã§ã™(%1)。</translation>
+ <source>Qt Widgets Designer is not responding (%1).</source>
+ <translation>Qt Widgets Designer ãŒç„¡å¿œç­”ã§ã™(%1)。</translation>
</message>
<message>
<source>Unable to create server socket: %1</source>
@@ -25612,10 +25612,10 @@ Neither the path to the library nor the path to its includes is added to the .pr
<translation>Qt ウィジェットアプリケーション</translation>
</message>
<message>
- <source>Creates a Qt application for the desktop. Includes a Qt Designer-based main window.
+ <source>Creates a Qt application for the desktop. Includes a Qt Widgets Designer-based main window.
Preselects a desktop Qt for building the application if available.</source>
- <translation>Qt Designer ベースã®ãƒ¡ã‚¤ãƒ³ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã‚’æŒã¤ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—用 Qt アプリケーションを作æˆã—ã¾ã™ã€‚
+ <translation>Qt Widgets Designer ベースã®ãƒ¡ã‚¤ãƒ³ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã‚’æŒã¤ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—用 Qt アプリケーションを作æˆã—ã¾ã™ã€‚
デスクトップ用 Qt ãŒå­˜åœ¨ã™ã‚‹å ´åˆã€ã‚¢ãƒ—リケーションã®ãƒ“ルド用ã«é¸æŠžã—ã¾ã™ã€‚</translation>
</message>
@@ -37181,8 +37181,8 @@ Affected are breakpoints %1</source>
<translation>Alt+Shift+R</translation>
</message>
<message>
- <source>About Qt Designer Plugins...</source>
- <translation>Qt Designer プラグインã«ã¤ã„ã¦...</translation>
+ <source>About Qt Widgets Designer Plugins...</source>
+ <translation>Qt Widgets Designer プラグインã«ã¤ã„ã¦...</translation>
</message>
<message>
<source>Preview in</source>
@@ -37506,12 +37506,12 @@ Affected are breakpoints %1</source>
<translation>フォームテンプレート</translation>
</message>
<message>
- <source>Creates a Qt Designer form that you can add to a Qt Widget Project. This is useful if you already have an existing class for the UI business logic.</source>
- <translation>Qt ウィジェットプロジェクトã«è¿½åŠ å¯èƒ½ãª Qt Designer フォームを作æˆã—ã¾ã™ã€‚ã“ã‚Œã¯æ—¢ã« UI ビジãƒã‚¹ãƒ­ã‚¸ãƒƒã‚¯ã‚’実装ã—ãŸã‚¯ãƒ©ã‚¹ã‚’æŒã£ã¦ã„ã‚‹å ´åˆã«å½¹ç«‹ã¡ã¾ã™ã€‚</translation>
+ <source>Creates a Qt Widgets Designer form that you can add to a Qt Widget Project. This is useful if you already have an existing class for the UI business logic.</source>
+ <translation>Qt ウィジェットプロジェクトã«è¿½åŠ å¯èƒ½ãª Qt Widgets Designer フォームを作æˆã—ã¾ã™ã€‚ã“ã‚Œã¯æ—¢ã« UI ビジãƒã‚¹ãƒ­ã‚¸ãƒƒã‚¯ã‚’実装ã—ãŸã‚¯ãƒ©ã‚¹ã‚’æŒã£ã¦ã„ã‚‹å ´åˆã«å½¹ç«‹ã¡ã¾ã™ã€‚</translation>
</message>
<message>
- <source>Qt Designer Form</source>
- <translation>Qt Designer フォーム</translation>
+ <source>Qt Widgets Designer Form</source>
+ <translation>Qt Widgets Designer フォーム</translation>
</message>
<message>
<source>Creates a Java file with boilerplate code.</source>
diff --git a/share/qtcreator/translations/qtcreator_pl.ts b/share/qtcreator/translations/qtcreator_pl.ts
index 465f3f32d3..00278fb998 100644
--- a/share/qtcreator/translations/qtcreator_pl.ts
+++ b/share/qtcreator/translations/qtcreator_pl.ts
@@ -9917,7 +9917,7 @@ in the system&apos;s browser for manual download.</source>
<translation>Lista Android NDK:</translation>
</message>
<message>
- <source>Android OpenSSL settings (Optional)</source>
+ <source>Android OpenSSL Settings (Optional)</source>
<translation>Ustawienia Android OpenSSL (opcjonalne)</translation>
</message>
<message>
@@ -10692,12 +10692,12 @@ The kit supports &quot;%2&quot;, but the device uses &quot;%3&quot;.</source>
<translation>BÅ‚Ä…d instalacji, przyczyna nieznana.</translation>
</message>
<message>
- <source>
-Uninstalling the installed package may solve the issue.
-Do you want to uninstall the existing package?</source>
- <translation>
-Deinstalacja uprzednio zainstalowanego pakietu może rozwiązać problem.
-Czy zdeinstalować istniejący pakiet?</translation>
+ <source>Uninstalling the installed package may solve the issue.</source>
+ <translation>Deinstalacja uprzednio zainstalowanego pakietu może rozwiązać problem.</translation>
+ </message>
+ <message>
+ <source>Do you want to uninstall the existing package?</source>
+ <translation>Czy zdeinstalować istniejący pakiet?</translation>
</message>
<message>
<source>The deployment AVD &quot;%1&quot; cannot be started.</source>
@@ -12496,7 +12496,7 @@ See also Google Test settings.</source>
<translation>Powtarzanie testów</translation>
</message>
<message>
- <source>Run in parallel</source>
+ <source>Run in Parallel</source>
<translation>Uruchomianie równoległe</translation>
</message>
<message>
@@ -15958,8 +15958,8 @@ tylko przez wbudowane narzędzie.</translation>
<translation>Użyj globalnych ustawień</translation>
</message>
<message>
- <source>ClangFormat settings:</source>
- <translation>Ustawienia ClangFormat:</translation>
+ <source>ClangFormat Settings</source>
+ <translation>Ustawienia ClangFormat</translation>
</message>
<message>
<source>Indenting only</source>
@@ -17866,8 +17866,8 @@ Otherwise you need to specify the path to the %2 file from the Copilot neovim pl
<translation>Edytor QMLJS</translation>
</message>
<message>
- <source>Qt Designer</source>
- <translation>Qt Designer</translation>
+ <source>Qt Widgets Designer</source>
+ <translation>Qt Widgets Designer</translation>
</message>
<message>
<source>Qt Linguist</source>
@@ -21384,7 +21384,7 @@ The built-in code model will handle highlighting, completion and so on.</source>
<translation>Próg uaktualniania dokumentu:</translation>
</message>
<message>
- <source>Sessions with a single clangd instance</source>
+ <source>Sessions with a Single Clangd Instance</source>
<translation>Sesje z pojedynczÄ… instancjÄ… clangd</translation>
</message>
<message>
@@ -21537,7 +21537,7 @@ managed by the same clangd process, add them here.</source>
<translation>Skonwertuj do pliku binarnego</translation>
</message>
<message>
- <source>Add forward declaration for %1</source>
+ <source>Add Forward Declaration for %1</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -22069,8 +22069,8 @@ e.g. name = &quot;m_test_foo_&quot;:
<translation>WÅ‚asne szablony pobieraczy i ustawiaczy</translation>
</message>
<message>
- <source>Value types:</source>
- <translation>Typy wartości:</translation>
+ <source>Value Types</source>
+ <translation>Typy wartości</translation>
</message>
<message>
<source>Projects only</source>
@@ -27865,8 +27865,8 @@ Please select a 64 bit Debugger in the kit settings for this kit.</source>
<translation>Podaj nazwÄ™ klasy</translation>
</message>
<message>
- <source>Qt Designer Form Class</source>
- <translation>Klasa formularza Qt Designer</translation>
+ <source>Qt Widgets Designer Form Class</source>
+ <translation>Klasa formularza Qt Widgets Designer</translation>
</message>
<message>
<source>Designer</source>
@@ -27901,8 +27901,8 @@ Spróbuj ponownie przebudować projekt.</translation>
<translation>Shift+F4</translation>
</message>
<message>
- <source>Creates a Qt Designer form along with a matching class (C++ header and source file) for implementation purposes. You can add the form and class to an existing Qt Widget Project.</source>
- <translation>Tworzy formularz Qt Designera wraz z klasą implementującą (plik nagłówkowy i źródłowy C++). Utworzony formularz i klasę można dodać do istniejącego projektu Qt Widget.</translation>
+ <source>Creates a Qt Widgets Designer form along with a matching class (C++ header and source file) for implementation purposes. You can add the form and class to an existing Qt Widget Project.</source>
+ <translation>Tworzy formularz Qt Widgets Designera wraz z klasą implementującą (plik nagłówkowy i źródłowy C++). Utworzony formularz i klasę można dodać do istniejącego projektu Qt Widget.</translation>
</message>
<message>
<source>Choose a Form Template</source>
@@ -28053,8 +28053,8 @@ Przebudowanie projektu może pomóc w ich odnalezieniu.</translation>
<translation>Alt+Shift+R</translation>
</message>
<message>
- <source>About Qt Designer Plugins...</source>
- <translation>Informacje o wtyczkach Qt Designera...</translation>
+ <source>About Qt Widgets Designer Plugins...</source>
+ <translation>Informacje o wtyczkach Qt Widgets Designera...</translation>
</message>
<message>
<source>Preview in</source>
@@ -40741,10 +40741,10 @@ You can select an option to create a project that you can open in Qt Design Stud
<translation>Informacje o klasie</translation>
</message>
<message>
- <source>Creates a widget-based Qt application that contains a Qt Designer-based main window and C++ source and header files to implement the application logic.
+ <source>Creates a widget-based Qt application that contains a Qt Widgets Designer-based main window and C++ source and header files to implement the application logic.
Preselects a desktop Qt for building the application if available.</source>
- <translation>Tworzy aplikację Qt bazującą na widżetach, zawierającą główne okno do edycji w Qt Designerze wraz z plikami: źródłowym oraz nagłówkowym C++, przeznaczonymi do zaimplementowania logiki aplikacji.
+ <translation>Tworzy aplikację Qt bazującą na widżetach, zawierającą główne okno do edycji w Qt Widgets Designerze wraz z plikami: źródłowym oraz nagłówkowym C++, przeznaczonymi do zaimplementowania logiki aplikacji.
Wstępnie wybiera platformę desktopową Qt do budowania aplikacji, jeśli jest ona dostępna.</translation>
</message>
@@ -41115,12 +41115,12 @@ You should not mix multiple test frameworks in a project.</source>
<translation>Szablon formularza</translation>
</message>
<message>
- <source>Creates a Qt Designer form that you can add to a Qt Widget Project. This is useful if you already have an existing class for the UI business logic.</source>
- <translation>Tworzy formularz Qt Designer, który można dodać do projektu typu Qt Widget. Jest to przydatne w sytuacji, kiedy istnieje już klasa zarządzająca logiką UI.</translation>
+ <source>Creates a Qt Widgets Designer form that you can add to a Qt Widget Project. This is useful if you already have an existing class for the UI business logic.</source>
+ <translation>Tworzy formularz Qt Widgets Designer, który można dodać do projektu typu Qt Widget. Jest to przydatne w sytuacji, kiedy istnieje już klasa zarządzająca logiką UI.</translation>
</message>
<message>
- <source>Qt Designer Form</source>
- <translation>Formularz Qt Designer</translation>
+ <source>Qt Widgets Designer Form</source>
+ <translation>Formularz Qt Widgets Designer</translation>
</message>
<message>
<source>Creates a Java file with boilerplate code.</source>
@@ -43958,15 +43958,15 @@ The affected files are:
</message>
<message>
<source>Qt Custom Designer Widget</source>
- <translation>Własny widżet Qt Designera</translation>
+ <translation>Własny widżet Qt Widgets Designera</translation>
</message>
<message>
<source>Creates a Qt Custom Designer Widget or a Custom Widget Collection.</source>
- <translation>Tworzy własny widżet Qt Designera lub kolekcję własnych widżetów.</translation>
+ <translation>Tworzy własny widżet Qt Widgets Designera lub kolekcję własnych widżetów.</translation>
</message>
<message>
- <source>This wizard generates a Qt Designer Custom Widget or a Qt Designer Custom Widget Collection project.</source>
- <translation>Ten kreator generuje projekt własnego widżetu Qt Designera lub projekt kolekcji własnych widżetów Qt4 Designera.</translation>
+ <source>This wizard generates a Qt Widgets Designer Custom Widget or a Qt Widgets Designer Custom Widget Collection project.</source>
+ <translation>Ten kreator generuje projekt własnego widżetu Qt Widgets Designera lub projekt kolekcji własnych widżetów Qt4 Widget Designera.</translation>
</message>
<message>
<source>Creating multiple widget libraries (%1, %2) in one project (%3) is not supported.</source>
@@ -43985,8 +43985,8 @@ The affected files are:
<translation>Nie można odnaleźć aplikacji &quot;%1&quot;.</translation>
</message>
<message>
- <source>Qt Designer is not responding (%1).</source>
- <translation>Qt Designer nie odpowiada (%1).</translation>
+ <source>Qt Widgets Designer is not responding (%1).</source>
+ <translation>Qt Widgets Designer nie odpowiada (%1).</translation>
</message>
<message>
<source>Unable to create server socket: %1</source>
@@ -52771,7 +52771,7 @@ Ustala, jak klawisz &quot;Backspace&quot; reaguje na wcięcia.
<translation>Pomiędzy liniami</translation>
</message>
<message>
- <source>Line annotations</source>
+ <source>Line Annotations</source>
<translation>Adnotacje linii</translation>
</message>
<message>
@@ -53749,7 +53749,7 @@ po naciśnięciu klawisza Enter</translation>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Automatically insert matching characters</source>
+ <source>&amp;Automatically Insert Matching Characters</source>
<translation>&amp;Automatyczne wstawianie znaków</translation>
</message>
<message>
@@ -54144,7 +54144,7 @@ if the comment starts with &quot;/*!&quot; or &quot;//!&quot;.</source>
<translation>Zresetuj</translation>
</message>
<message>
- <source>Scanning scope</source>
+ <source>Scanning Scope</source>
<translation>Zakres skanowania</translation>
</message>
<message>
diff --git a/share/qtcreator/translations/qtcreator_ru.ts b/share/qtcreator/translations/qtcreator_ru.ts
index 8b7bfee3dc..7f57fe4f12 100644
--- a/share/qtcreator/translations/qtcreator_ru.ts
+++ b/share/qtcreator/translations/qtcreator_ru.ts
@@ -933,12 +933,12 @@ The files in the Android package source directory are copied to the build direct
</translation>
</message>
<message>
- <source>
-Uninstalling the installed package may solve the issue.
-Do you want to uninstall the existing package?</source>
- <translation>
-Удаление уÑтановленного пакета может решить проблему.
-Желаете его удалить?</translation>
+ <source>Uninstalling the installed package may solve the issue.</source>
+ <translation>Удаление уÑтановленного пакета может решить проблему.</translation>
+ </message>
+ <message>
+ <source>Do you want to uninstall the existing package?</source>
+ <translation>Желаете его удалить?</translation>
</message>
<message>
<source>Pulling files necessary for debugging.</source>
@@ -2169,7 +2169,7 @@ To hide a sticky splash screen, invoke QtAndroid::hideSplashScreen().</source>
<translation>ÐаÑтроить SDK</translation>
</message>
<message>
- <source>Android OpenSSL settings (Optional)</source>
+ <source>Android OpenSSL Settings (Optional)</source>
<translation>ÐаÑтройки Android OpenSSL (опционально)</translation>
</message>
<message>
@@ -11815,7 +11815,7 @@ Flags: %3</source>
<translation>Добавить #include %1</translation>
</message>
<message>
- <source>Add forward declaration for %1</source>
+ <source>Add Forward Declaration for %1</source>
<translation>Добавление предъобъÑÐ²Ð»ÐµÐ½Ð¸Ñ %1</translation>
</message>
<message>
@@ -16950,8 +16950,8 @@ Rebuilding the project might help.</source>
ПереÑборка проекта может помочь.</translation>
</message>
<message>
- <source>Qt Designer Form Class</source>
- <translation>КлаÑÑ Ñ„Ð¾Ñ€Ð¼Ñ‹ Qt Designer</translation>
+ <source>Qt Widgets Designer Form Class</source>
+ <translation>КлаÑÑ Ñ„Ð¾Ñ€Ð¼Ñ‹ Qt Widgets Designer</translation>
</message>
<message>
<source>Class Details</source>
@@ -16982,7 +16982,7 @@ Rebuilding the project might help.</source>
<translation>Shift+F4</translation>
</message>
<message>
- <source>Creates a Qt Designer form along with a matching class (C++ header and source file) for implementation purposes. You can add the form and class to an existing Qt Widget Project.</source>
+ <source>Creates a Qt Widgets Designer form along with a matching class (C++ header and source file) for implementation purposes. You can add the form and class to an existing Qt Widget Project.</source>
<translation>Создание формы дизайнера Qt и ÑоответÑтвующего клаÑÑа (иÑходный и заголовочный файлы C++) Ð´Ð»Ñ Ñ€ÐµÐ°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ð¸. Их можно будет добавить к ÑущеÑтвующему проекту Qt Widget.</translation>
</message>
<message>
@@ -18681,8 +18681,8 @@ when they are not required, which will improve performance in most cases.</sourc
<translation>Alt+Shift+R</translation>
</message>
<message>
- <source>About Qt Designer Plugins...</source>
- <translation>О модулÑÑ… Qt Designer...</translation>
+ <source>About Qt Widgets Designer Plugins...</source>
+ <translation>О модулÑÑ… Qt Widgets Designer...</translation>
</message>
<message>
<source>Preview in</source>
@@ -25534,8 +25534,8 @@ If set to false, the target will be moved straight to the current mouse position
<translation>Редактор QMLJS</translation>
</message>
<message>
- <source>Qt Designer</source>
- <translation>Qt Designer</translation>
+ <source>Qt Widgets Designer</source>
+ <translation>Qt Widgets Designer</translation>
</message>
<message>
<source>Qt Linguist</source>
@@ -29867,12 +29867,12 @@ Enable this if you plan to create 32-bit x86 binaries without using a dedicated
<translation>Шаблон формы</translation>
</message>
<message>
- <source>Creates a Qt Designer form that you can add to a Qt Widget Project. This is useful if you already have an existing class for the UI business logic.</source>
- <translation>Создание формы Qt Designer, которую можно добавить в проект Qt Widget. Имеет ÑмыÑл, еÑли у Ð²Ð°Ñ ÑƒÐ¶Ðµ еÑÑ‚ÑŒ клаÑÑ Ñ Ð±Ð¸Ð·Ð½ÐµÑ-логикой.</translation>
+ <source>Creates a Qt Widgets Designer form that you can add to a Qt Widget Project. This is useful if you already have an existing class for the UI business logic.</source>
+ <translation>Создание формы Qt Widgets Designer, которую можно добавить в проект Qt Widget. Имеет ÑмыÑл, еÑли у Ð²Ð°Ñ ÑƒÐ¶Ðµ еÑÑ‚ÑŒ клаÑÑ Ñ Ð±Ð¸Ð·Ð½ÐµÑ-логикой.</translation>
</message>
<message>
- <source>Qt Designer Form</source>
- <translation>Форма Qt Designer</translation>
+ <source>Qt Widgets Designer Form</source>
+ <translation>Форма Qt Widgets Designer</translation>
</message>
<message>
<source>Creates a Java file with boilerplate code.</source>
@@ -30627,7 +30627,7 @@ Use this only if you are prototyping. You cannot create a full application with
<translation>Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ клаÑÑе</translation>
</message>
<message>
- <source>Creates a Qt application for the desktop. Includes a Qt Designer-based main window.
+ <source>Creates a Qt application for the desktop. Includes a Qt Widgets Designer-based main window.
Preselects a desktop Qt for building the application if available.</source>
<translation>Создание Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Qt Ð´Ð»Ñ Ð½Ð°Ñтольных компьютеров. Включает оÑновное окно в виде формы дизайнера Qt.
@@ -30707,8 +30707,8 @@ Preselects a desktop Qt for building the application if available.</source>
<translation>Qt Ð´Ð»Ñ Python - Приложение Qt Quick - ПуÑтое</translation>
</message>
<message>
- <source>Creates a Qt for Python application that includes a Qt Designer-based widget (ui file)</source>
- <translation>Создание Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð½Ð° Qt Ð´Ð»Ñ Python, включающее виджет Qt Designer (файл ui)</translation>
+ <source>Creates a Qt for Python application that includes a Qt Widgets Designer-based widget (ui file)</source>
+ <translation>Создание Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð½Ð° Qt Ð´Ð»Ñ Python, включающее виджет Qt Widgets Designer (файл ui)</translation>
</message>
<message>
<source>Qt for Python - Window (UI file)</source>
@@ -33657,19 +33657,19 @@ Please update your kit (%3) or choose a mkspec for qmake that matches your targe
</message>
<message>
<source>Qt Custom Designer Widget</source>
- <translation>ПользовательÑкий виджет Qt Designer</translation>
+ <translation>ПользовательÑкий виджет Qt Widgets Designer</translation>
</message>
<message>
<source>Creates a Qt Custom Designer Widget or a Custom Widget Collection.</source>
- <translation>Создание пользовательÑкого виджета Qt Designer или набора пользовательÑких виджетов.</translation>
+ <translation>Создание пользовательÑкого виджета Qt Widgets Designer или набора пользовательÑких виджетов.</translation>
</message>
<message>
- <source>This wizard generates a Qt Designer Custom Widget or a Qt Designer Custom Widget Collection project.</source>
- <translation>Этот маÑтер ÑоздаÑÑ‚ пользовательÑкий виджет или набор пользовательÑких виджетов Ð´Ð»Ñ Qt Designer.</translation>
+ <source>This wizard generates a Qt Widgets Designer Custom Widget or a Qt Widgets Designer Custom Widget Collection project.</source>
+ <translation>Этот маÑтер ÑоздаÑÑ‚ пользовательÑкий виджет или набор пользовательÑких виджетов Ð´Ð»Ñ Qt Widgets Designer.</translation>
</message>
<message>
- <source>Qt Designer is not responding (%1).</source>
- <translation>Qt Designer не отвечает (%1).</translation>
+ <source>Qt Widgets Designer is not responding (%1).</source>
+ <translation>Qt Widgets Designer не отвечает (%1).</translation>
</message>
<message>
<source>Unable to create server socket: %1</source>
@@ -35211,7 +35211,7 @@ Neither the path to the library nor the path to its includes is added to the .pr
</message>
<message>
<source>Warn about unsupported features of Qt Quick Designer in the code editor</source>
- <translation>Предупреждать о неподдерживаемых оÑобенноÑÑ‚ÑÑ… Qt Designer в редакторе кода</translation>
+ <translation>Предупреждать о неподдерживаемых оÑобенноÑÑ‚ÑÑ… Qt Quick Designer в редакторе кода</translation>
</message>
<message>
<source>Debugging</source>
@@ -43125,7 +43125,7 @@ In addition, Shift+Enter inserts an escape character at the cursor position and
<translation>ÐвтоматичеÑки разделÑÑ‚ÑŒ Ñтроки</translation>
</message>
<message>
- <source>&amp;Automatically insert matching characters</source>
+ <source>&amp;Automatically Insert Matching Characters</source>
<translation>&amp;ÐвтоматичеÑки вÑтавлÑÑ‚ÑŒ парные Ñимволы</translation>
</message>
<message>
@@ -43309,7 +43309,7 @@ In addition, Shift+Enter inserts an escape character at the cursor position and
<translation>Прижать к правому краю</translation>
</message>
<message>
- <source>Line annotations</source>
+ <source>Line Annotations</source>
<translation>ПоÑÑ‚Ñ€Ð¾Ñ‡Ð½Ð°Ñ Ð°Ð½Ð½Ð¾Ñ‚Ð°Ñ†Ð¸Ñ</translation>
</message>
<message>
@@ -45131,7 +45131,7 @@ The trace data is lost.</source>
<translation>СброÑить</translation>
</message>
<message>
- <source>Scanning scope</source>
+ <source>Scanning Scope</source>
<translation>ОблаÑÑ‚ÑŒ поиÑка</translation>
</message>
<message>
diff --git a/share/qtcreator/translations/qtcreator_sl.ts b/share/qtcreator/translations/qtcreator_sl.ts
index 9dba0e365a..837f8267de 100644
--- a/share/qtcreator/translations/qtcreator_sl.ts
+++ b/share/qtcreator/translations/qtcreator_sl.ts
@@ -3532,8 +3532,8 @@ Morda lahko pomaga ponovna gradnja projekta.</translation>
<translation type="obsolete">Urejevalnik XML</translation>
</message>
<message>
- <source>Qt Designer Form Class</source>
- <translation>Razred obrazca Qt Designer</translation>
+ <source>Qt Widgets Designer Form Class</source>
+ <translation>Razred obrazca Qt Widgets Designer</translation>
</message>
<message>
<source>Form Template</source>
@@ -3560,8 +3560,8 @@ Morda lahko pomaga ponovna gradnja projekta.</translation>
<translation type="obsolete">Nastavitve …</translation>
</message>
<message>
- <source>Qt Designer Form</source>
- <translation>Obrazec Qt Designer</translation>
+ <source>Qt Widgets Designer Form</source>
+ <translation>Obrazec Qt Widgets Designer</translation>
</message>
<message>
<source>Creates a Qt Designer form that you can add to a Qt Widget Project. This is useful if you already have an existing class for the UI business logic.</source>
@@ -3676,8 +3676,8 @@ Morda lahko pomaga ponovna gradnja projekta.</translation>
<translation>Urejevalnik signalov in rež</translation>
</message>
<message>
- <source>About Qt Designer plugins....</source>
- <translation>O vstavkih za Qt Designer …</translation>
+ <source>About Qt Widgets Designer plugins....</source>
+ <translation>O vstavkih za Qt Widgets Designer …</translation>
</message>
<message>
<source>Preview in</source>
@@ -10725,8 +10725,8 @@ Za uporabo v polje Iskalnika vtipkajte to bližnjico in presledek ter nato iskan
<translation>Urejevalniku QMLJS</translation>
</message>
<message>
- <source>Qt Designer</source>
- <translation>Qt Designerju</translation>
+ <source>Qt Widgets Designer</source>
+ <translation>Qt Widgets Designerju</translation>
</message>
<message>
<source>Qt Linguist</source>
@@ -11571,15 +11571,15 @@ Razlog: %2</translation>
</message>
<message>
<source>Qt Custom Designer Widget</source>
- <translation>Gradnik za Qt Designer po meri</translation>
+ <translation>Gradnik za Qt Widgets Designer po meri</translation>
</message>
<message>
<source>Creates a Qt Custom Designer Widget or a Custom Widget Collection.</source>
- <translation>Ustvari gradnik za Qt Designer po meri ali pa zbirko gradnikov po meri</translation>
+ <translation>Ustvari gradnik za Qt Widgets Designer po meri ali pa zbirko gradnikov po meri</translation>
</message>
<message>
<source>This wizard generates a Qt4 Designer Custom Widget or a Qt4 Designer Custom Widget Collection project.</source>
- <translation>Ta Äarovnik ustvari projekt gradnika po meri za Qt Designer ali pa projekt zbirke gradnikov po meri za Qt Designer.</translation>
+ <translation>Ta Äarovnik ustvari projekt gradnika po meri za Qt Widgets Designer ali pa projekt zbirke gradnikov po meri za Qt Widgets Designer.</translation>
</message>
<message>
<source>Custom Widgets</source>
@@ -11610,8 +11610,8 @@ Razlog: %2</translation>
<translation>Programa »%1« ni bilo moÄ najti.</translation>
</message>
<message>
- <source>Qt Designer is not responding (%1).</source>
- <translation>Qt Designer se ne odziva (%1).</translation>
+ <source>Qt Widgets Designer is not responding (%1).</source>
+ <translation>Qt Widgets Designer se ne odziva (%1).</translation>
</message>
<message>
<source>Unable to create server socket: %1</source>
@@ -16004,8 +16004,8 @@ Projekte programov QML izvede pregledovalnik QML in jih ni potrebno zgraditi.</t
<translation>Predloga pošiljanja za CVS</translation>
</message>
<message>
- <source>Qt Designer file</source>
- <translation>Datoteka za Qt Designer</translation>
+ <source>Qt Widgets Designer file</source>
+ <translation>Datoteka za Qt Widgets Designer</translation>
</message>
<message>
<source>Generic Qt Creator Project file</source>
diff --git a/share/qtcreator/translations/qtcreator_uk.ts b/share/qtcreator/translations/qtcreator_uk.ts
index bf9deb0e7f..d3da748f62 100644
--- a/share/qtcreator/translations/qtcreator_uk.ts
+++ b/share/qtcreator/translations/qtcreator_uk.ts
@@ -3153,7 +3153,7 @@ to version control (%2)
<translation type="vanished">Ðвтоматично вÑтавлÑти крапки з комами, закриваючі квадратні, круглі, фігурні дужки та лапки, коли необхідно.</translation>
</message>
<message>
- <source>&amp;Automatically insert matching characters</source>
+ <source>&amp;Automatically Insert Matching Characters</source>
<translation>&amp;Ðвтоматично вÑтавлÑти парні Ñимволи</translation>
</message>
<message>
@@ -6843,8 +6843,8 @@ Rebuilding the project might help.</source>
<translation type="vanished">Додавати верÑÑ–ÑŽ Qt в #ifdef Ð´Ð»Ñ Ñ–Ð¼ÐµÐ½ модулів</translation>
</message>
<message>
- <source>Qt Designer Form Class</source>
- <translation>ÐšÐ»Ð°Ñ Ñ„Ð¾Ñ€Ð¼Ð¸ Qt Designer</translation>
+ <source>Qt Widgets Designer Form Class</source>
+ <translation>ÐšÐ»Ð°Ñ Ñ„Ð¾Ñ€Ð¼Ð¸ Qt Widgets Designer</translation>
</message>
<message>
<source>Choose a Class Name</source>
@@ -6883,8 +6883,8 @@ Rebuilding the project might help.</source>
<translation type="vanished">Створює форму Qt Designer, Ñку ви можете додати до проекту Qt Widget. Це кориÑно, Ñкщо ви вже маєте Ñ–Ñнуючий ÐºÐ»Ð°Ñ Ð´Ð»Ñ Ð±Ñ–Ð·Ð½ÐµÑ-логіки UI.</translation>
</message>
<message>
- <source>Creates a Qt Designer form along with a matching class (C++ header and source file) for implementation purposes. You can add the form and class to an existing Qt Widget Project.</source>
- <translation>Створює форму Qt Designer разом з відповідним клаÑом (файли заголовку та коду C++) з метою реалізації.. Ви можете додати форму та ÐºÐ»Ð°Ñ Ð´Ð¾ Ñ–Ñнуючого проекту Qt Widget.</translation>
+ <source>Creates a Qt Widgets Designer form along with a matching class (C++ header and source file) for implementation purposes. You can add the form and class to an existing Qt Widget Project.</source>
+ <translation>Створює форму Qt Widgets Designer разом з відповідним клаÑом (файли заголовку та коду C++) з метою реалізації.. Ви можете додати форму та ÐºÐ»Ð°Ñ Ð´Ð¾ Ñ–Ñнуючого проекту Qt Widget.</translation>
</message>
<message>
<source>Location</source>
@@ -11142,8 +11142,8 @@ Ids must begin with a lowercase letter.</source>
<translation>Редактор QMLJS</translation>
</message>
<message>
- <source>Qt Designer</source>
- <translation>Qt Designer</translation>
+ <source>Qt Widgets Designer</source>
+ <translation>Qt Widgets Designer</translation>
</message>
<message>
<source>Qt Linguist</source>
@@ -15626,19 +15626,19 @@ Preselects a desktop Qt for building the application if available.</source>
</message>
<message>
<source>Qt Custom Designer Widget</source>
- <translation>КориÑтувацький віджет Qt Designer</translation>
+ <translation>КориÑтувацький віджет Qt Widgets Designer</translation>
</message>
<message>
<source>Creates a Qt Custom Designer Widget or a Custom Widget Collection.</source>
- <translation>Створює кориÑтувацький віджет Qt Designer або колекцію кориÑтувацьких віджетів.</translation>
+ <translation>Створює кориÑтувацький віджет Qt Widgets Designer або колекцію кориÑтувацьких віджетів.</translation>
</message>
<message>
- <source>This wizard generates a Qt Designer Custom Widget or a Qt Designer Custom Widget Collection project.</source>
- <translation>Цей майÑтер генерує проект кориÑтувацького віджета Qt Designer або колекції кориÑтувацьких віджетів Qt Designer.</translation>
+ <source>This wizard generates a Qt Widgets Designer Custom Widget or a Qt Widgets Designer Custom Widget Collection project.</source>
+ <translation>Цей майÑтер генерує проект кориÑтувацького віджета Qt Widgets Designer або колекції кориÑтувацьких віджетів Qt Widgets Designer.</translation>
</message>
<message>
- <source>Qt Designer is not responding (%1).</source>
- <translation>Qt Designer не відповідає (%1).</translation>
+ <source>Qt Widgets Designer is not responding (%1).</source>
+ <translation>Qt Widgets Designer не відповідає (%1).</translation>
</message>
<message>
<source>Unable to create server socket: %1</source>
@@ -15717,10 +15717,10 @@ Preselects a desktop Qt for building the application if available.</source>
<translation>Програма Qt Widgets</translation>
</message>
<message>
- <source>Creates a Qt application for the desktop. Includes a Qt Designer-based main window.
+ <source>Creates a Qt application for the desktop. Includes a Qt Widgets Designer-based main window.
Preselects a desktop Qt for building the application if available.</source>
- <translation>Створює програму Qt Ð´Ð»Ñ Ñтаціонарного комп&apos;ютера. Включає головне вікно на оÑнові Qt Designer.
+ <translation>Створює програму Qt Ð´Ð»Ñ Ñтаціонарного комп&apos;ютера. Включає головне вікно на оÑнові Qt Widgets Designer.
Обирає Qt Ð´Ð»Ñ Ñтільниці Ð´Ð»Ñ Ð·Ð±Ñ–Ñ€ÐºÐ¸ програми, Ñкщо доÑтупно.</translation>
</message>
@@ -21894,7 +21894,7 @@ Influences the indentation of continuation lines.
<translation>Скинути</translation>
</message>
<message>
- <source>Scanning scope</source>
+ <source>Scanning Scope</source>
<translation>ОблаÑÑ‚ÑŒ пошуку</translation>
</message>
<message>
@@ -37378,8 +37378,8 @@ The statements may not contain &apos;{&apos; nor &apos;}&apos; characters.</sour
<translation>Alt+Shift+R</translation>
</message>
<message>
- <source>About Qt Designer Plugins...</source>
- <translation>Про додатки Qt Designer....</translation>
+ <source>About Qt Widgets Designer Plugins...</source>
+ <translation>Про додатки Qt Widgets Designer....</translation>
</message>
<message>
<source>Preview in</source>
@@ -37719,12 +37719,12 @@ The statements may not contain &apos;{&apos; nor &apos;}&apos; characters.</sour
<translation>Шаблон форми</translation>
</message>
<message>
- <source>Creates a Qt Designer form that you can add to a Qt Widget Project. This is useful if you already have an existing class for the UI business logic.</source>
- <translation>Створює форму Qt Designer, Ñку ви можете додати до проекту Qt Widget. Це кориÑно, Ñкщо ви вже маєте Ñ–Ñнуючий ÐºÐ»Ð°Ñ Ð´Ð»Ñ Ð±Ñ–Ð·Ð½ÐµÑ-логіки UI.</translation>
+ <source>Creates a Qt Widgets Designer form that you can add to a Qt Widget Project. This is useful if you already have an existing class for the UI business logic.</source>
+ <translation>Створює форму Qt Widgets Designer, Ñку ви можете додати до проекту Qt Widget. Це кориÑно, Ñкщо ви вже маєте Ñ–Ñнуючий ÐºÐ»Ð°Ñ Ð´Ð»Ñ Ð±Ñ–Ð·Ð½ÐµÑ-логіки UI.</translation>
</message>
<message>
- <source>Qt Designer Form</source>
- <translation>Форма Qt Designer</translation>
+ <source>Qt Widgets Designer Form</source>
+ <translation>Форма Qt Widgets Designer</translation>
</message>
<message>
<source>Creates a Java file with boilerplate code.</source>
diff --git a/share/qtcreator/translations/qtcreator_zh_CN.ts b/share/qtcreator/translations/qtcreator_zh_CN.ts
index 3a1b29ef6a..66a622a425 100644
--- a/share/qtcreator/translations/qtcreator_zh_CN.ts
+++ b/share/qtcreator/translations/qtcreator_zh_CN.ts
@@ -787,12 +787,12 @@ The kit supports &quot;%2&quot;, but the device uses &quot;%3&quot;.</source>
</translation>
</message>
<message>
- <source>
-Uninstalling the installed package may solve the issue.
-Do you want to uninstall the existing package?</source>
- <translation>
-å¸è½½å·²å®‰è£…的包å¯èƒ½è§£å†³è¿™ä¸ªé—®é¢˜ã€‚
-你想å¸è½½å·²ç»å­˜åœ¨çš„包å—?</translation>
+ <source>Uninstalling the installed package may solve the issue.</source>
+ <translation>å¸è½½å·²å®‰è£…的包å¯èƒ½è§£å†³è¿™ä¸ªé—®é¢˜ã€‚</translation>
+ </message>
+ <message>
+ <source>Do you want to uninstall the existing package?</source>
+ <translation>你想å¸è½½å·²ç»å­˜åœ¨çš„包å—?</translation>
</message>
<message>
<source>Install failed</source>
@@ -1900,7 +1900,7 @@ in the system&apos;s browser for manual download.</source>
<translation>å®‰å“ NDK 列表:</translation>
</message>
<message>
- <source>Android OpenSSL settings (Optional)</source>
+ <source>Android OpenSSL Settings (Optional)</source>
<translation>å®‰å“ OpenSSL 设置(å¯é€‰ï¼‰</translation>
</message>
<message>
@@ -2714,7 +2714,7 @@ Executable: %2</source>
<translation>é‡å¤æµ‹è¯•</translation>
</message>
<message>
- <source>Run in parallel</source>
+ <source>Run in Parallel</source>
<translation>并行è¿è¡Œ</translation>
</message>
<message>
@@ -11600,7 +11600,7 @@ The built-in code model will handle highlighting, completion and so on.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Sessions with a single clangd instance</source>
+ <source>Sessions with a Single Clangd Instance</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -12511,7 +12511,7 @@ Flags: %3</source>
<translation type="unfinished">添加#include %1</translation>
</message>
<message>
- <source>Add forward declaration for %1</source>
+ <source>Add Forward Declaration for %1</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -12767,7 +12767,7 @@ Flags: %3</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Value types:</source>
+ <source>Value Types:</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -42591,7 +42591,7 @@ Specifies how backspace interacts with indentation.
<translation type="unfinished"></translation>
</message>
<message>
- <source>Line annotations</source>
+ <source>Line Annotations</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -42978,7 +42978,7 @@ In addition, Shift+Enter inserts an escape character at the cursor position and
<translation type="unfinished"></translation>
</message>
<message>
- <source>&amp;Automatically insert matching characters</source>
+ <source>&amp;Automatically Insert Matching Characters</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -44774,7 +44774,7 @@ Will not be applied to whitespace in comments and strings.</source>
<translation>é‡ç½®</translation>
</message>
<message>
- <source>Scanning scope</source>
+ <source>Scanning Scope</source>
<translation>扫æ范围</translation>
</message>
<message>
diff --git a/share/qtcreator/translations/qtcreator_zh_TW.ts b/share/qtcreator/translations/qtcreator_zh_TW.ts
index d21d4d9bca..7b5f5cfba8 100644
--- a/share/qtcreator/translations/qtcreator_zh_TW.ts
+++ b/share/qtcreator/translations/qtcreator_zh_TW.ts
@@ -1715,7 +1715,7 @@
<translation>在é©ç•¶çš„時候自動æ’入分號ã€çµæŸçš„括號ã€å¤§æ‹¬è™Ÿèˆ‡å¼•è™Ÿç­‰ç­‰ã€‚</translation>
</message>
<message>
- <source>&amp;Automatically insert matching characters</source>
+ <source>&amp;Automatically Insert Matching Characters</source>
<translation>自動æ’å…¥å°æ‡‰çš„å­—å…ƒ(&amp;A)</translation>
</message>
<message>
@@ -25376,7 +25376,7 @@ Influences the indentation of continuation lines.
<translation>é‡ç½®</translation>
</message>
<message>
- <source>Scanning scope</source>
+ <source>Scanning Scope</source>
<translation>掃æ範åœ</translation>
</message>
<message>
diff --git a/src/libs/3rdparty/cplusplus/TranslationUnit.cpp b/src/libs/3rdparty/cplusplus/TranslationUnit.cpp
index e680ee2660..8505deb0bd 100644
--- a/src/libs/3rdparty/cplusplus/TranslationUnit.cpp
+++ b/src/libs/3rdparty/cplusplus/TranslationUnit.cpp
@@ -27,7 +27,7 @@
#include "Literals.h"
#include "DiagnosticClient.h"
-#include "cppassert.h"
+#include <utils/qtcassert.h>
#include <utils/textutils.h>
#include <stack>
@@ -88,7 +88,7 @@ int TranslationUnit::sourceLength() const
void TranslationUnit::setSource(const char *source, int size)
{
- CPP_CHECK(source);
+ QTC_ASSERT(source, return);
_firstSourceChar = source;
_lastSourceChar = source + size;
}
@@ -191,6 +191,8 @@ void TranslationUnit::tokenize()
int lineColumnIdx = 0;
Token tk;
+ int macroOffset = -1;
+ int macroLength = -1;
do {
lex(&tk);
@@ -209,17 +211,12 @@ recognize:
lex(&tk);
// Gather where the expansion happens and its length.
- //int macroOffset = static_cast<int>(strtoul(tk.spell(), 0, 0));
+ macroOffset = static_cast<int>(strtoul(tk.spell(), 0, 0));
lex(&tk);
lex(&tk); // Skip the separating comma
- //int macroLength = static_cast<int>(strtoul(tk.spell(), 0, 0));
+ macroLength = static_cast<int>(strtoul(tk.spell(), 0, 0));
lex(&tk);
- // NOTE: We are currently not using the macro offset and length. They
- // are kept here for now because of future use.
- //Q_UNUSED(macroOffset)
- //Q_UNUSED(macroLength)
-
// Now we need to gather the real line and columns from the upcoming
// tokens. But notice this is only relevant for tokens which are expanded
// but not generated.
@@ -307,6 +304,11 @@ recognize:
tk.f.generated = currentGenerated;
_tokens->push_back(tk);
+
+ if (currentExpanded) {
+ QTC_ASSERT(macroOffset != -1 && macroLength != -1, continue);
+ _expansionPositions[_tokens->size() - 1] = std::make_pair(macroOffset, macroLength);
+ }
} while (tk.kind());
for (; ! braces.empty(); braces.pop()) {
@@ -462,6 +464,14 @@ int TranslationUnit::getTokenEndPositionInDocument(const Token &token,
return Utils::Text::positionInText(doc, line, column);
}
+std::pair<int, int> TranslationUnit::getExpansionPosition(int tokenIndex) const
+{
+ QTC_ASSERT(tokenIndex < int(_tokens->size()) && tokenAt(tokenIndex).generated(), return {});
+ const auto it = _expansionPositions.find(tokenIndex);
+ QTC_ASSERT(it != _expansionPositions.end(), return {});
+ return it->second;
+}
+
void TranslationUnit::getPosition(int utf16charOffset,
int *line,
int *column,
diff --git a/src/libs/3rdparty/cplusplus/TranslationUnit.h b/src/libs/3rdparty/cplusplus/TranslationUnit.h
index 40f79d0091..1332549ffd 100644
--- a/src/libs/3rdparty/cplusplus/TranslationUnit.h
+++ b/src/libs/3rdparty/cplusplus/TranslationUnit.h
@@ -133,6 +133,7 @@ public:
const StringLiteral **fileName = nullptr) const;
int getTokenPositionInDocument(const Token token, const QTextDocument *doc) const;
int getTokenEndPositionInDocument(const Token &token, const QTextDocument *doc) const;
+ std::pair<int, int> getExpansionPosition(int tokenIndex) const;
void pushLineOffset(int offset);
void pushPreprocessorLine(int utf16charOffset,
@@ -183,6 +184,11 @@ private:
std::vector<Token> *_comments;
std::vector<int> _lineOffsets;
std::vector<PPLine> _ppLines;
+
+ // Offset and length. Note that in contrast to token offsets, this is a raw file offset
+ // with no preprocessor prefix.
+ std::unordered_map<int, std::pair<int, int>> _expansionPositions;
+
typedef std::unordered_map<unsigned, std::pair<int, int> > TokenLineColumn;
TokenLineColumn _expandedLineColumn;
MemoryPool *_pool;
diff --git a/src/libs/3rdparty/libptyqt/unixptyprocess.cpp b/src/libs/3rdparty/libptyqt/unixptyprocess.cpp
index e9ec1d590f..d76349a49e 100644
--- a/src/libs/3rdparty/libptyqt/unixptyprocess.cpp
+++ b/src/libs/3rdparty/libptyqt/unixptyprocess.cpp
@@ -190,16 +190,17 @@ bool UnixPtyProcess::startProcess(const QString &shellPath,
m_readMasterNotify->disconnect();
});
- QStringList varNames;
- for (const QString &line : std::as_const(environment))
- varNames.append(line.split("=").first());
-
- QProcessEnvironment envFormat;
- for (const QString &line : std::as_const(environment))
- envFormat.insert(line.split("=").first(), line.split("=").last());
+ QProcessEnvironment env;
+ for (const QString &envEntry : environment) {
+ const int idx = envEntry.indexOf('=');
+ if (idx != -1)
+ env.insert(envEntry.left(idx), envEntry.mid(idx + 1));
+ else
+ env.insert(envEntry, QString());
+ }
m_shellProcess.setWorkingDirectory(workingDir);
- m_shellProcess.setProcessEnvironment(envFormat);
+ m_shellProcess.setProcessEnvironment(env);
m_shellProcess.setReadChannel(QProcess::StandardOutput);
m_shellProcess.start(m_shellPath, arguments);
if (!m_shellProcess.waitForStarted())
diff --git a/src/libs/3rdparty/sol2/include/sol/sol.hpp b/src/libs/3rdparty/sol2/include/sol/sol.hpp
index 063ea72165..85665a50c9 100644
--- a/src/libs/3rdparty/sol2/include/sol/sol.hpp
+++ b/src/libs/3rdparty/sol2/include/sol/sol.hpp
@@ -13841,7 +13841,7 @@ namespace sol { namespace stack {
}
static bool max_size_check(std::true_type, T& cont, std::size_t idx) {
- return idx >= cont.max_size();
+ return idx >= std::size_t(cont.max_size());
}
static T get(lua_State* L, int relindex, record& tracking) {
diff --git a/src/libs/3rdparty/sqlite/sqlite3.c b/src/libs/3rdparty/sqlite/sqlite3.c
index 08c593e55c..eaa24a1310 100644
--- a/src/libs/3rdparty/sqlite/sqlite3.c
+++ b/src/libs/3rdparty/sqlite/sqlite3.c
@@ -1,6 +1,6 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
-** version 3.45.3. By combining all the individual C code files into this
+** version 3.46.0. By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit. This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately. Performance improvements
@@ -18,7 +18,7 @@
** separate file. This file contains only code for the core SQLite library.
**
** The content in this amalgamation comes from Fossil check-in
-** 8653b758870e6ef0c98d46b3ace27849054a.
+** 96c92aba00c8375bc32fafcdf12429c58bd8.
*/
#define SQLITE_CORE 1
#define SQLITE_AMALGAMATION 1
@@ -459,9 +459,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.45.3"
-#define SQLITE_VERSION_NUMBER 3045003
-#define SQLITE_SOURCE_ID "2024-04-15 13:34:05 8653b758870e6ef0c98d46b3ace27849054af85da891eb121e9aaa537f1e8355"
+#define SQLITE_VERSION "3.46.0"
+#define SQLITE_VERSION_NUMBER 3046000
+#define SQLITE_SOURCE_ID "2024-05-23 13:25:27 96c92aba00c8375bc32fafcdf12429c58bd8aabfcadab6683e35bbb9cdebf19e"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -1077,11 +1077,11 @@ struct sqlite3_file {
** </ul>
** xLock() upgrades the database file lock. In other words, xLock() moves the
** database file lock in the direction NONE toward EXCLUSIVE. The argument to
-** xLock() is always on of SHARED, RESERVED, PENDING, or EXCLUSIVE, never
+** xLock() is always one of SHARED, RESERVED, PENDING, or EXCLUSIVE, never
** SQLITE_LOCK_NONE. If the database file lock is already at or above the
** requested lock, then the call to xLock() is a no-op.
** xUnlock() downgrades the database file lock to either SHARED or NONE.
-* If the lock is already at or below the requested lock state, then the call
+** If the lock is already at or below the requested lock state, then the call
** to xUnlock() is a no-op.
** The xCheckReservedLock() method checks whether any database connection,
** either in this process or in some other process, is holding a RESERVED,
@@ -3618,8 +3618,8 @@ SQLITE_API int sqlite3_set_authorizer(
#define SQLITE_RECURSIVE 33 /* NULL NULL */
/*
-** CAPI3REF: Tracing And Profiling Functions
-** METHOD: sqlite3
+** CAPI3REF: Deprecated Tracing And Profiling Functions
+** DEPRECATED
**
** These routines are deprecated. Use the [sqlite3_trace_v2()] interface
** instead of the routines described here.
@@ -7200,6 +7200,12 @@ SQLITE_API int sqlite3_autovacuum_pages(
** The exceptions defined in this paragraph might change in a future
** release of SQLite.
**
+** Whether the update hook is invoked before or after the
+** corresponding change is currently unspecified and may differ
+** depending on the type of change. Do not rely on the order of the
+** hook call with regards to the final result of the operation which
+** triggers the hook.
+**
** The update hook implementation must not do anything that will modify
** the database connection that invoked the update hook. Any actions
** to modify the database connection must be deferred until after the
@@ -8670,7 +8676,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
** The sqlite3_keyword_count() interface returns the number of distinct
** keywords understood by SQLite.
**
-** The sqlite3_keyword_name(N,Z,L) interface finds the N-th keyword and
+** The sqlite3_keyword_name(N,Z,L) interface finds the 0-based N-th keyword and
** makes *Z point to that keyword expressed as UTF8 and writes the number
** of bytes in the keyword into *L. The string that *Z points to is not
** zero-terminated. The sqlite3_keyword_name(N,Z,L) routine returns
@@ -10249,24 +10255,45 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
** <li value="2"><p>
** ^(If the sqlite3_vtab_distinct() interface returns 2, that means
** that the query planner does not need the rows returned in any particular
-** order, as long as rows with the same values in all "aOrderBy" columns
-** are adjacent.)^ ^(Furthermore, only a single row for each particular
-** combination of values in the columns identified by the "aOrderBy" field
-** needs to be returned.)^ ^It is always ok for two or more rows with the same
-** values in all "aOrderBy" columns to be returned, as long as all such rows
-** are adjacent. ^The virtual table may, if it chooses, omit extra rows
-** that have the same value for all columns identified by "aOrderBy".
-** ^However omitting the extra rows is optional.
+** order, as long as rows with the same values in all columns identified
+** by "aOrderBy" are adjacent.)^ ^(Furthermore, when two or more rows
+** contain the same values for all columns identified by "colUsed", all but
+** one such row may optionally be omitted from the result.)^
+** The virtual table is not required to omit rows that are duplicates
+** over the "colUsed" columns, but if the virtual table can do that without
+** too much extra effort, it could potentially help the query to run faster.
** This mode is used for a DISTINCT query.
** <li value="3"><p>
-** ^(If the sqlite3_vtab_distinct() interface returns 3, that means
-** that the query planner needs only distinct rows but it does need the
-** rows to be sorted.)^ ^The virtual table implementation is free to omit
-** rows that are identical in all aOrderBy columns, if it wants to, but
-** it is not required to omit any rows. This mode is used for queries
+** ^(If the sqlite3_vtab_distinct() interface returns 3, that means the
+** virtual table must return rows in the order defined by "aOrderBy" as
+** if the sqlite3_vtab_distinct() interface had returned 0. However if
+** two or more rows in the result have the same values for all columns
+** identified by "colUsed", then all but one such row may optionally be
+** omitted.)^ Like when the return value is 2, the virtual table
+** is not required to omit rows that are duplicates over the "colUsed"
+** columns, but if the virtual table can do that without
+** too much extra effort, it could potentially help the query to run faster.
+** This mode is used for queries
** that have both DISTINCT and ORDER BY clauses.
** </ol>
**
+** <p>The following table summarizes the conditions under which the
+** virtual table is allowed to set the "orderByConsumed" flag based on
+** the value returned by sqlite3_vtab_distinct(). This table is a
+** restatement of the previous four paragraphs:
+**
+** <table border=1 cellspacing=0 cellpadding=10 width="90%">
+** <tr>
+** <td valign="top">sqlite3_vtab_distinct() return value
+** <td valign="top">Rows are returned in aOrderBy order
+** <td valign="top">Rows with the same value in all aOrderBy columns are adjacent
+** <td valign="top">Duplicates over all colUsed columns may be omitted
+** <tr><td>0<td>yes<td>yes<td>no
+** <tr><td>1<td>no<td>yes<td>no
+** <tr><td>2<td>no<td>yes<td>yes
+** <tr><td>3<td>yes<td>yes<td>yes
+** </table>
+**
** ^For the purposes of comparing virtual table output values to see if the
** values are same value for sorting purposes, two NULL values are considered
** to be the same. In other words, the comparison operator is "IS"
@@ -12312,6 +12339,30 @@ SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const c
SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
/*
+** CAPI3REF: Add A Single Change To A Changegroup
+** METHOD: sqlite3_changegroup
+**
+** This function adds the single change currently indicated by the iterator
+** passed as the second argument to the changegroup object. The rules for
+** adding the change are just as described for [sqlite3changegroup_add()].
+**
+** If the change is successfully added to the changegroup, SQLITE_OK is
+** returned. Otherwise, an SQLite error code is returned.
+**
+** The iterator must point to a valid entry when this function is called.
+** If it does not, SQLITE_ERROR is returned and no change is added to the
+** changegroup. Additionally, the iterator must not have been opened with
+** the SQLITE_CHANGESETAPPLY_INVERT flag. In this case SQLITE_ERROR is also
+** returned.
+*/
+SQLITE_API int sqlite3changegroup_add_change(
+ sqlite3_changegroup*,
+ sqlite3_changeset_iter*
+);
+
+
+
+/*
** CAPI3REF: Obtain A Composite Changeset From A Changegroup
** METHOD: sqlite3_changegroup
**
@@ -13115,8 +13166,8 @@ struct Fts5PhraseIter {
** EXTENSION API FUNCTIONS
**
** xUserData(pFts):
-** Return a copy of the context pointer the extension function was
-** registered with.
+** Return a copy of the pUserData pointer passed to the xCreateFunction()
+** API when the extension function was registered.
**
** xColumnTotalSize(pFts, iCol, pnToken):
** If parameter iCol is less than zero, set output variable *pnToken
@@ -14314,6 +14365,8 @@ struct fts5_api {
# define SQLITE_OMIT_ALTERTABLE
#endif
+#define SQLITE_DIGIT_SEPARATOR '_'
+
/*
** Return true (non-zero) if the input is an integer that is too large
** to fit in 32-bits. This macro is used inside of various testcase()
@@ -14606,8 +14659,8 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
#define TK_TRUEFALSE 170
#define TK_ISNOT 171
#define TK_FUNCTION 172
-#define TK_UMINUS 173
-#define TK_UPLUS 174
+#define TK_UPLUS 173
+#define TK_UMINUS 174
#define TK_TRUTH 175
#define TK_REGISTER 176
#define TK_VECTOR 177
@@ -14616,8 +14669,9 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
#define TK_ASTERISK 180
#define TK_SPAN 181
#define TK_ERROR 182
-#define TK_SPACE 183
-#define TK_ILLEGAL 184
+#define TK_QNUMBER 183
+#define TK_SPACE 184
+#define TK_ILLEGAL 185
/************** End of parse.h ***********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
@@ -14879,7 +14933,7 @@ typedef INT16_TYPE LogEst;
# define SQLITE_PTRSIZE __SIZEOF_POINTER__
# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \
defined(_M_ARM) || defined(__arm__) || defined(__x86) || \
- (defined(__APPLE__) && defined(__POWERPC__)) || \
+ (defined(__APPLE__) && defined(__ppc__)) || \
(defined(__TOS_AIX__) && !defined(__64BIT__))
# define SQLITE_PTRSIZE 4
# else
@@ -15147,7 +15201,7 @@ SQLITE_PRIVATE u32 sqlite3WhereTrace;
** 0x00000010 Display sqlite3_index_info xBestIndex calls
** 0x00000020 Range an equality scan metrics
** 0x00000040 IN operator decisions
-** 0x00000080 WhereLoop cost adjustements
+** 0x00000080 WhereLoop cost adjustments
** 0x00000100
** 0x00000200 Covering index decisions
** 0x00000400 OR optimization
@@ -16296,6 +16350,7 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck(
sqlite3 *db, /* Database connection that is running the check */
Btree *p, /* The btree to be checked */
Pgno *aRoot, /* An array of root pages numbers for individual trees */
+ sqlite3_value *aCnt, /* OUT: entry counts for each btree in aRoot[] */
int nRoot, /* Number of entries in aRoot[] */
int mxErr, /* Stop reporting errors after this many */
int *pnErr, /* OUT: Write number of errors seen to this variable */
@@ -16566,12 +16621,12 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_Vacuum 5
#define OP_VFilter 6 /* jump, synopsis: iplan=r[P3] zplan='P4' */
#define OP_VUpdate 7 /* synopsis: data=r[P3@P2] */
-#define OP_Init 8 /* jump, synopsis: Start at P2 */
+#define OP_Init 8 /* jump0, synopsis: Start at P2 */
#define OP_Goto 9 /* jump */
#define OP_Gosub 10 /* jump */
-#define OP_InitCoroutine 11 /* jump */
-#define OP_Yield 12 /* jump */
-#define OP_MustBeInt 13 /* jump */
+#define OP_InitCoroutine 11 /* jump0 */
+#define OP_Yield 12 /* jump0 */
+#define OP_MustBeInt 13 /* jump0 */
#define OP_Jump 14 /* jump */
#define OP_Once 15 /* jump */
#define OP_If 16 /* jump */
@@ -16579,22 +16634,22 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_IsType 18 /* jump, synopsis: if typeof(P1.P3) in P5 goto P2 */
#define OP_Not 19 /* same as TK_NOT, synopsis: r[P2]= !r[P1] */
#define OP_IfNullRow 20 /* jump, synopsis: if P1.nullRow then r[P3]=NULL, goto P2 */
-#define OP_SeekLT 21 /* jump, synopsis: key=r[P3@P4] */
-#define OP_SeekLE 22 /* jump, synopsis: key=r[P3@P4] */
-#define OP_SeekGE 23 /* jump, synopsis: key=r[P3@P4] */
-#define OP_SeekGT 24 /* jump, synopsis: key=r[P3@P4] */
+#define OP_SeekLT 21 /* jump0, synopsis: key=r[P3@P4] */
+#define OP_SeekLE 22 /* jump0, synopsis: key=r[P3@P4] */
+#define OP_SeekGE 23 /* jump0, synopsis: key=r[P3@P4] */
+#define OP_SeekGT 24 /* jump0, synopsis: key=r[P3@P4] */
#define OP_IfNotOpen 25 /* jump, synopsis: if( !csr[P1] ) goto P2 */
#define OP_IfNoHope 26 /* jump, synopsis: key=r[P3@P4] */
#define OP_NoConflict 27 /* jump, synopsis: key=r[P3@P4] */
#define OP_NotFound 28 /* jump, synopsis: key=r[P3@P4] */
#define OP_Found 29 /* jump, synopsis: key=r[P3@P4] */
-#define OP_SeekRowid 30 /* jump, synopsis: intkey=r[P3] */
+#define OP_SeekRowid 30 /* jump0, synopsis: intkey=r[P3] */
#define OP_NotExists 31 /* jump, synopsis: intkey=r[P3] */
-#define OP_Last 32 /* jump */
-#define OP_IfSmaller 33 /* jump */
+#define OP_Last 32 /* jump0 */
+#define OP_IfSizeBetween 33 /* jump */
#define OP_SorterSort 34 /* jump */
#define OP_Sort 35 /* jump */
-#define OP_Rewind 36 /* jump */
+#define OP_Rewind 36 /* jump0 */
#define OP_SorterNext 37 /* jump */
#define OP_Prev 38 /* jump */
#define OP_Next 39 /* jump */
@@ -16606,7 +16661,7 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_IdxGE 45 /* jump, synopsis: key=r[P3@P4] */
#define OP_RowSetRead 46 /* jump, synopsis: r[P3]=rowset(P1) */
#define OP_RowSetTest 47 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */
-#define OP_Program 48 /* jump */
+#define OP_Program 48 /* jump0 */
#define OP_FkIfZero 49 /* jump, synopsis: if fkctr[P1]==0 goto P2 */
#define OP_IsNull 50 /* jump, same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */
#define OP_NotNull 51 /* jump, same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */
@@ -16636,7 +16691,7 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_Null 75 /* synopsis: r[P2..P3]=NULL */
#define OP_SoftNull 76 /* synopsis: r[P1]=NULL */
#define OP_Blob 77 /* synopsis: r[P2]=P4 (len=P1) */
-#define OP_Variable 78 /* synopsis: r[P2]=parameter(P1,P4) */
+#define OP_Variable 78 /* synopsis: r[P2]=parameter(P1) */
#define OP_Move 79 /* synopsis: r[P2@P3]=r[P1@P3] */
#define OP_Copy 80 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */
#define OP_SCopy 81 /* synopsis: r[P2]=r[P1] */
@@ -16760,14 +16815,15 @@ typedef struct VdbeOpList VdbeOpList;
#define OPFLG_OUT2 0x10 /* out2: P2 is an output */
#define OPFLG_OUT3 0x20 /* out3: P3 is an output */
#define OPFLG_NCYCLE 0x40 /* ncycle:Cycles count against P1 */
+#define OPFLG_JUMP0 0x80 /* jump0: P2 might be zero */
#define OPFLG_INITIALIZER {\
/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x41, 0x00,\
-/* 8 */ 0x01, 0x01, 0x01, 0x01, 0x03, 0x03, 0x01, 0x01,\
-/* 16 */ 0x03, 0x03, 0x01, 0x12, 0x01, 0x49, 0x49, 0x49,\
-/* 24 */ 0x49, 0x01, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,\
-/* 32 */ 0x41, 0x01, 0x41, 0x41, 0x41, 0x01, 0x41, 0x41,\
+/* 8 */ 0x81, 0x01, 0x01, 0x81, 0x83, 0x83, 0x01, 0x01,\
+/* 16 */ 0x03, 0x03, 0x01, 0x12, 0x01, 0xc9, 0xc9, 0xc9,\
+/* 24 */ 0xc9, 0x01, 0x49, 0x49, 0x49, 0x49, 0xc9, 0x49,\
+/* 32 */ 0xc1, 0x01, 0x41, 0x41, 0xc1, 0x01, 0x41, 0x41,\
/* 40 */ 0x41, 0x41, 0x41, 0x26, 0x26, 0x41, 0x23, 0x0b,\
-/* 48 */ 0x01, 0x01, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\
+/* 48 */ 0x81, 0x01, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\
/* 56 */ 0x0b, 0x0b, 0x01, 0x03, 0x03, 0x03, 0x01, 0x41,\
/* 64 */ 0x01, 0x00, 0x00, 0x02, 0x02, 0x08, 0x00, 0x10,\
/* 72 */ 0x10, 0x10, 0x00, 0x10, 0x00, 0x10, 0x10, 0x00,\
@@ -16927,6 +16983,8 @@ SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord*);
SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *);
SQLITE_PRIVATE int sqlite3VdbeHasSubProgram(Vdbe*);
+SQLITE_PRIVATE void sqlite3MemSetArrayInt64(sqlite3_value *aMem, int iIdx, i64 val);
+
SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*);
#ifdef SQLITE_ENABLE_BYTECODE_VTAB
SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*);
@@ -17514,6 +17572,10 @@ struct FuncDefHash {
};
#define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ)
+#if defined(SQLITE_USER_AUTHENTICATION)
+# warning "The SQLITE_USER_AUTHENTICATION extension is deprecated. \
+ See ext/userauth/user-auth.txt for details."
+#endif
#ifdef SQLITE_USER_AUTHENTICATION
/*
** Information held in the "sqlite3" database connection object and used
@@ -17817,7 +17879,7 @@ struct sqlite3 {
#define SQLITE_CursorHints 0x00000400 /* Add OP_CursorHint opcodes */
#define SQLITE_Stat4 0x00000800 /* Use STAT4 data */
/* TH3 expects this value ^^^^^^^^^^ to be 0x0000800. Don't change it */
-#define SQLITE_PushDown 0x00001000 /* The push-down optimization */
+#define SQLITE_PushDown 0x00001000 /* WHERE-clause push-down opt */
#define SQLITE_SimplifyJoin 0x00002000 /* Convert LEFT JOIN to JOIN */
#define SQLITE_SkipScan 0x00004000 /* Skip-scans */
#define SQLITE_PropagateConst 0x00008000 /* The constant propagation opt */
@@ -18390,8 +18452,7 @@ struct Table {
#define TF_HasStored 0x00000040 /* Has one or more STORED columns */
#define TF_HasGenerated 0x00000060 /* Combo: HasVirtual + HasStored */
#define TF_WithoutRowid 0x00000080 /* No rowid. PRIMARY KEY is the key */
-#define TF_StatsUsed 0x00000100 /* Query planner decisions affected by
- ** Index.aiRowLogEst[] values */
+#define TF_MaybeReanalyze 0x00000100 /* Maybe run ANALYZE on this table */
#define TF_NoVisibleRowid 0x00000200 /* No user-visible "rowid" column */
#define TF_OOOHidden 0x00000400 /* Out-of-Order hidden columns */
#define TF_HasNotNull 0x00000800 /* Contains NOT NULL constraints */
@@ -19191,10 +19252,12 @@ struct IdList {
**
** Union member validity:
**
-** u1.zIndexedBy fg.isIndexedBy && !fg.isTabFunc
-** u1.pFuncArg fg.isTabFunc && !fg.isIndexedBy
-** u2.pIBIndex fg.isIndexedBy && !fg.isCte
-** u2.pCteUse fg.isCte && !fg.isIndexedBy
+** u1.zIndexedBy fg.isIndexedBy && !fg.isTabFunc
+** u1.pFuncArg fg.isTabFunc && !fg.isIndexedBy
+** u1.nRow !fg.isTabFunc && !fg.isIndexedBy
+**
+** u2.pIBIndex fg.isIndexedBy && !fg.isCte
+** u2.pCteUse fg.isCte && !fg.isIndexedBy
*/
struct SrcItem {
Schema *pSchema; /* Schema to which this item is fixed */
@@ -19222,6 +19285,7 @@ struct SrcItem {
unsigned isOn :1; /* u3.pOn was once valid and non-NULL */
unsigned isSynthUsing :1; /* u3.pUsing is synthesized from NATURAL */
unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */
+ unsigned rowidUsed :1; /* The ROWID of this table is referenced */
} fg;
int iCursor; /* The VDBE cursor number used to access this table */
union {
@@ -19232,6 +19296,7 @@ struct SrcItem {
union {
char *zIndexedBy; /* Identifier from "INDEXED BY <zIndex>" clause */
ExprList *pFuncArg; /* Arguments to table-valued-function */
+ u32 nRow; /* Number of rows in a VALUES clause */
} u1;
union {
Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */
@@ -19489,11 +19554,12 @@ struct Select {
#define SF_View 0x0200000 /* SELECT statement is a view */
#define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */
#define SF_UFSrcCheck 0x0800000 /* Check pSrc as required by UPDATE...FROM */
-#define SF_PushDown 0x1000000 /* SELECT has be modified by push-down opt */
+#define SF_PushDown 0x1000000 /* Modified by WHERE-clause push-down opt */
#define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */
#define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */
#define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */
#define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */
+#define SF_Correlated 0x20000000 /* True if references the outer context */
/* True if S exists and has SF_NestedFrom */
#define IsNestedFrom(S) ((S)!=0 && ((S)->selFlags&SF_NestedFrom)!=0)
@@ -19733,6 +19799,7 @@ struct Parse {
u8 disableLookaside; /* Number of times lookaside has been disabled */
u8 prepFlags; /* SQLITE_PREPARE_* flags */
u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */
+ u8 bHasWith; /* True if statement contains WITH */
#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */
#endif
@@ -20412,6 +20479,9 @@ struct Window {
** due to the SQLITE_SUBTYPE flag */
};
+SQLITE_PRIVATE Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow);
+SQLITE_PRIVATE void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal);
+
#ifndef SQLITE_OMIT_WINDOWFUNC
SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3*, Window*);
SQLITE_PRIVATE void sqlite3WindowUnlinkFromSelect(Window*);
@@ -20729,6 +20799,7 @@ SQLITE_PRIVATE int sqlite3ErrorToParser(sqlite3*,int);
SQLITE_PRIVATE void sqlite3Dequote(char*);
SQLITE_PRIVATE void sqlite3DequoteExpr(Expr*);
SQLITE_PRIVATE void sqlite3DequoteToken(Token*);
+SQLITE_PRIVATE void sqlite3DequoteNumber(Parse*, Expr*);
SQLITE_PRIVATE void sqlite3TokenInit(Token*,char*);
SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char*, int);
SQLITE_PRIVATE int sqlite3RunParser(Parse*, const char*);
@@ -20759,7 +20830,7 @@ SQLITE_PRIVATE void sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*)
SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32);
SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*);
SQLITE_PRIVATE void sqlite3ExprDeleteGeneric(sqlite3*,void*);
-SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse*, Expr*);
+SQLITE_PRIVATE int sqlite3ExprDeferredDelete(Parse*, Expr*);
SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse*, Expr*);
SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*);
SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*);
@@ -20982,12 +21053,10 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3*);
SQLITE_PRIVATE u32 sqlite3IsTrueOrFalse(const char*);
SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr*);
SQLITE_PRIVATE int sqlite3ExprTruthValue(const Expr*);
-SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*);
-SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*);
+SQLITE_PRIVATE int sqlite3ExprIsConstant(Parse*,Expr*);
SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8);
SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*);
-SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int);
-SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int);
+SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int,int);
#ifdef SQLITE_ENABLE_CURSOR_HINTS
SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*);
#endif
@@ -21172,7 +21241,9 @@ SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...);
SQLITE_PRIVATE void sqlite3Error(sqlite3*,int);
SQLITE_PRIVATE void sqlite3ErrorClear(sqlite3*);
SQLITE_PRIVATE void sqlite3SystemError(sqlite3*,int);
+#if !defined(SQLITE_OMIT_BLOB_LITERAL)
SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n);
+#endif
SQLITE_PRIVATE u8 sqlite3HexToInt(int h);
SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **);
@@ -24219,13 +24290,14 @@ struct DateTime {
int tz; /* Timezone offset in minutes */
double s; /* Seconds */
char validJD; /* True (1) if iJD is valid */
- char rawS; /* Raw numeric value stored in s */
char validYMD; /* True (1) if Y,M,D are valid */
char validHMS; /* True (1) if h,m,s are valid */
- char validTZ; /* True (1) if tz is valid */
- char tzSet; /* Timezone was set explicitly */
- char isError; /* An overflow has occurred */
- char useSubsec; /* Display subsecond precision */
+ char nFloor; /* Days to implement "floor" */
+ unsigned rawS : 1; /* Raw numeric value stored in s */
+ unsigned isError : 1; /* An overflow has occurred */
+ unsigned useSubsec : 1; /* Display subsecond precision */
+ unsigned isUtc : 1; /* Time is known to be UTC */
+ unsigned isLocal : 1; /* Time is known to be localtime */
};
@@ -24323,6 +24395,8 @@ static int parseTimezone(const char *zDate, DateTime *p){
sgn = +1;
}else if( c=='Z' || c=='z' ){
zDate++;
+ p->isLocal = 0;
+ p->isUtc = 1;
goto zulu_time;
}else{
return c!=0;
@@ -24335,7 +24409,6 @@ static int parseTimezone(const char *zDate, DateTime *p){
p->tz = sgn*(nMn + nHr*60);
zulu_time:
while( sqlite3Isspace(*zDate) ){ zDate++; }
- p->tzSet = 1;
return *zDate!=0;
}
@@ -24379,7 +24452,6 @@ static int parseHhMmSs(const char *zDate, DateTime *p){
p->m = m;
p->s = s + ms;
if( parseTimezone(zDate, p) ) return 1;
- p->validTZ = (p->tz!=0)?1:0;
return 0;
}
@@ -24426,16 +24498,41 @@ static void computeJD(DateTime *p){
p->validJD = 1;
if( p->validHMS ){
p->iJD += p->h*3600000 + p->m*60000 + (sqlite3_int64)(p->s*1000 + 0.5);
- if( p->validTZ ){
+ if( p->tz ){
p->iJD -= p->tz*60000;
p->validYMD = 0;
p->validHMS = 0;
- p->validTZ = 0;
+ p->tz = 0;
+ p->isUtc = 1;
+ p->isLocal = 0;
}
}
}
/*
+** Given the YYYY-MM-DD information current in p, determine if there
+** is day-of-month overflow and set nFloor to the number of days that
+** would need to be subtracted from the date in order to bring the
+** date back to the end of the month.
+*/
+static void computeFloor(DateTime *p){
+ assert( p->validYMD || p->isError );
+ assert( p->D>=0 && p->D<=31 );
+ assert( p->M>=0 && p->M<=12 );
+ if( p->D<=28 ){
+ p->nFloor = 0;
+ }else if( (1<<p->M) & 0x15aa ){
+ p->nFloor = 0;
+ }else if( p->M!=2 ){
+ p->nFloor = (p->D==31);
+ }else if( p->Y%4!=0 || (p->Y%100==0 && p->Y%400!=0) ){
+ p->nFloor = p->D - 28;
+ }else{
+ p->nFloor = p->D - 29;
+ }
+}
+
+/*
** Parse dates of the form
**
** YYYY-MM-DD HH:MM:SS.FFF
@@ -24473,12 +24570,16 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){
p->Y = neg ? -Y : Y;
p->M = M;
p->D = D;
- if( p->validTZ ){
+ computeFloor(p);
+ if( p->tz ){
computeJD(p);
}
return 0;
}
+
+static void clearYMD_HMS_TZ(DateTime *p); /* Forward declaration */
+
/*
** Set the time to the current time reported by the VFS.
**
@@ -24488,6 +24589,9 @@ static int setDateTimeToCurrent(sqlite3_context *context, DateTime *p){
p->iJD = sqlite3StmtCurrentTime(context);
if( p->iJD>0 ){
p->validJD = 1;
+ p->isUtc = 1;
+ p->isLocal = 0;
+ clearYMD_HMS_TZ(p);
return 0;
}else{
return 1;
@@ -24626,7 +24730,7 @@ static void computeYMD_HMS(DateTime *p){
static void clearYMD_HMS_TZ(DateTime *p){
p->validYMD = 0;
p->validHMS = 0;
- p->validTZ = 0;
+ p->tz = 0;
}
#ifndef SQLITE_OMIT_LOCALTIME
@@ -24758,7 +24862,7 @@ static int toLocaltime(
p->validHMS = 1;
p->validJD = 0;
p->rawS = 0;
- p->validTZ = 0;
+ p->tz = 0;
p->isError = 0;
return SQLITE_OK;
}
@@ -24778,12 +24882,12 @@ static const struct {
float rLimit; /* Maximum NNN value for this transform */
float rXform; /* Constant used for this transform */
} aXformType[] = {
- { 6, "second", 4.6427e+14, 1.0 },
- { 6, "minute", 7.7379e+12, 60.0 },
- { 4, "hour", 1.2897e+11, 3600.0 },
- { 3, "day", 5373485.0, 86400.0 },
- { 5, "month", 176546.0, 2592000.0 },
- { 4, "year", 14713.0, 31536000.0 },
+ /* 0 */ { 6, "second", 4.6427e+14, 1.0 },
+ /* 1 */ { 6, "minute", 7.7379e+12, 60.0 },
+ /* 2 */ { 4, "hour", 1.2897e+11, 3600.0 },
+ /* 3 */ { 3, "day", 5373485.0, 86400.0 },
+ /* 4 */ { 5, "month", 176546.0, 30.0*86400.0 },
+ /* 5 */ { 4, "year", 14713.0, 365.0*86400.0 },
};
/*
@@ -24815,14 +24919,20 @@ static void autoAdjustDate(DateTime *p){
** NNN.NNNN seconds
** NNN months
** NNN years
+** +/-YYYY-MM-DD HH:MM:SS.SSS
+** ceiling
+** floor
** start of month
** start of year
** start of week
** start of day
** weekday N
** unixepoch
+** auto
** localtime
** utc
+** subsec
+** subsecond
**
** Return 0 on success and 1 if there is any kind of error. If the error
** is in a system call (i.e. localtime()), then an error message is written
@@ -24853,6 +24963,37 @@ static int parseModifier(
}
break;
}
+ case 'c': {
+ /*
+ ** ceiling
+ **
+ ** Resolve day-of-month overflow by rolling forward into the next
+ ** month. As this is the default action, this modifier is really
+ ** a no-op that is only included for symmetry. See "floor".
+ */
+ if( sqlite3_stricmp(z, "ceiling")==0 ){
+ computeJD(p);
+ clearYMD_HMS_TZ(p);
+ rc = 0;
+ p->nFloor = 0;
+ }
+ break;
+ }
+ case 'f': {
+ /*
+ ** floor
+ **
+ ** Resolve day-of-month overflow by rolling back to the end of the
+ ** previous month.
+ */
+ if( sqlite3_stricmp(z, "floor")==0 ){
+ computeJD(p);
+ p->iJD -= p->nFloor*86400000;
+ clearYMD_HMS_TZ(p);
+ rc = 0;
+ }
+ break;
+ }
case 'j': {
/*
** julianday
@@ -24879,7 +25020,9 @@ static int parseModifier(
** show local time.
*/
if( sqlite3_stricmp(z, "localtime")==0 && sqlite3NotPureFunc(pCtx) ){
- rc = toLocaltime(p, pCtx);
+ rc = p->isLocal ? SQLITE_OK : toLocaltime(p, pCtx);
+ p->isUtc = 0;
+ p->isLocal = 1;
}
break;
}
@@ -24904,7 +25047,7 @@ static int parseModifier(
}
#ifndef SQLITE_OMIT_LOCALTIME
else if( sqlite3_stricmp(z, "utc")==0 && sqlite3NotPureFunc(pCtx) ){
- if( p->tzSet==0 ){
+ if( p->isUtc==0 ){
i64 iOrigJD; /* Original localtime */
i64 iGuess; /* Guess at the corresponding utc time */
int cnt = 0; /* Safety to prevent infinite loop */
@@ -24927,7 +25070,8 @@ static int parseModifier(
memset(p, 0, sizeof(*p));
p->iJD = iGuess;
p->validJD = 1;
- p->tzSet = 1;
+ p->isUtc = 1;
+ p->isLocal = 0;
}
rc = SQLITE_OK;
}
@@ -24947,7 +25091,7 @@ static int parseModifier(
&& r>=0.0 && r<7.0 && (n=(int)r)==r ){
sqlite3_int64 Z;
computeYMD_HMS(p);
- p->validTZ = 0;
+ p->tz = 0;
p->validJD = 0;
computeJD(p);
Z = ((p->iJD + 129600000)/86400000) % 7;
@@ -24987,7 +25131,7 @@ static int parseModifier(
p->h = p->m = 0;
p->s = 0.0;
p->rawS = 0;
- p->validTZ = 0;
+ p->tz = 0;
p->validJD = 0;
if( sqlite3_stricmp(z,"month")==0 ){
p->D = 1;
@@ -25058,6 +25202,7 @@ static int parseModifier(
x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12;
p->Y += x;
p->M -= x*12;
+ computeFloor(p);
computeJD(p);
p->validHMS = 0;
p->validYMD = 0;
@@ -25104,11 +25249,12 @@ static int parseModifier(
z += n;
while( sqlite3Isspace(*z) ) z++;
n = sqlite3Strlen30(z);
- if( n>10 || n<3 ) break;
+ if( n<3 || n>10 ) break;
if( sqlite3UpperToLower[(u8)z[n-1]]=='s' ) n--;
computeJD(p);
assert( rc==1 );
rRounder = r<0 ? -0.5 : +0.5;
+ p->nFloor = 0;
for(i=0; i<ArraySize(aXformType); i++){
if( aXformType[i].nName==n
&& sqlite3_strnicmp(aXformType[i].zName, z, n)==0
@@ -25116,21 +25262,24 @@ static int parseModifier(
){
switch( i ){
case 4: { /* Special processing to add months */
- assert( strcmp(aXformType[i].zName,"month")==0 );
+ assert( strcmp(aXformType[4].zName,"month")==0 );
computeYMD_HMS(p);
p->M += (int)r;
x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12;
p->Y += x;
p->M -= x*12;
+ computeFloor(p);
p->validJD = 0;
r -= (int)r;
break;
}
case 5: { /* Special processing to add years */
int y = (int)r;
- assert( strcmp(aXformType[i].zName,"year")==0 );
+ assert( strcmp(aXformType[5].zName,"year")==0 );
computeYMD_HMS(p);
+ assert( p->M>=0 && p->M<=12 );
p->Y += y;
+ computeFloor(p);
p->validJD = 0;
r -= (int)r;
break;
@@ -25385,21 +25534,82 @@ static void dateFunc(
}
/*
+** Compute the number of days after the most recent January 1.
+**
+** In other words, compute the zero-based day number for the
+** current year:
+**
+** Jan01 = 0, Jan02 = 1, ..., Jan31 = 30, Feb01 = 31, ...
+** Dec31 = 364 or 365.
+*/
+static int daysAfterJan01(DateTime *pDate){
+ DateTime jan01 = *pDate;
+ assert( jan01.validYMD );
+ assert( jan01.validHMS );
+ assert( pDate->validJD );
+ jan01.validJD = 0;
+ jan01.M = 1;
+ jan01.D = 1;
+ computeJD(&jan01);
+ return (int)((pDate->iJD-jan01.iJD+43200000)/86400000);
+}
+
+/*
+** Return the number of days after the most recent Monday.
+**
+** In other words, return the day of the week according
+** to this code:
+**
+** 0=Monday, 1=Tuesday, 2=Wednesday, ..., 6=Sunday.
+*/
+static int daysAfterMonday(DateTime *pDate){
+ assert( pDate->validJD );
+ return (int)((pDate->iJD+43200000)/86400000) % 7;
+}
+
+/*
+** Return the number of days after the most recent Sunday.
+**
+** In other words, return the day of the week according
+** to this code:
+**
+** 0=Sunday, 1=Monday, 2=Tues, ..., 6=Saturday
+*/
+static int daysAfterSunday(DateTime *pDate){
+ assert( pDate->validJD );
+ return (int)((pDate->iJD+129600000)/86400000) % 7;
+}
+
+/*
** strftime( FORMAT, TIMESTRING, MOD, MOD, ...)
**
** Return a string described by FORMAT. Conversions as follows:
**
-** %d day of month
+** %d day of month 01-31
+** %e day of month 1-31
** %f ** fractional seconds SS.SSS
+** %F ISO date. YYYY-MM-DD
+** %G ISO year corresponding to %V 0000-9999.
+** %g 2-digit ISO year corresponding to %V 00-99
** %H hour 00-24
-** %j day of year 000-366
+** %k hour 0-24 (leading zero converted to space)
+** %I hour 01-12
+** %j day of year 001-366
** %J ** julian day number
+** %l hour 1-12 (leading zero converted to space)
** %m month 01-12
** %M minute 00-59
+** %p "am" or "pm"
+** %P "AM" or "PM"
+** %R time as HH:MM
** %s seconds since 1970-01-01
** %S seconds 00-59
-** %w day of week 0-6 Sunday==0
-** %W week of year 00-53
+** %T time as HH:MM:SS
+** %u day of week 1-7 Monday==1, Sunday==7
+** %w day of week 0-6 Sunday==0, Monday==1
+** %U week of year 00-53 (First Sunday is start of week 01)
+** %V week of year 01-53 (First week containing Thursday is week 01)
+** %W week of year 00-53 (First Monday is start of week 01)
** %Y year 0000-9999
** %% %
*/
@@ -25436,7 +25646,7 @@ static void strftimeFunc(
sqlite3_str_appendf(&sRes, cf=='d' ? "%02d" : "%2d", x.D);
break;
}
- case 'f': {
+ case 'f': { /* Fractional seconds. (Non-standard) */
double s = x.s;
if( s>59.999 ) s = 59.999;
sqlite3_str_appendf(&sRes, "%06.3f", s);
@@ -25446,6 +25656,21 @@ static void strftimeFunc(
sqlite3_str_appendf(&sRes, "%04d-%02d-%02d", x.Y, x.M, x.D);
break;
}
+ case 'G': /* Fall thru */
+ case 'g': {
+ DateTime y = x;
+ assert( y.validJD );
+ /* Move y so that it is the Thursday in the same week as x */
+ y.iJD += (3 - daysAfterMonday(&x))*86400000;
+ y.validYMD = 0;
+ computeYMD(&y);
+ if( cf=='g' ){
+ sqlite3_str_appendf(&sRes, "%02d", y.Y%100);
+ }else{
+ sqlite3_str_appendf(&sRes, "%04d", y.Y);
+ }
+ break;
+ }
case 'H':
case 'k': {
sqlite3_str_appendf(&sRes, cf=='H' ? "%02d" : "%2d", x.h);
@@ -25459,25 +25684,11 @@ static void strftimeFunc(
sqlite3_str_appendf(&sRes, cf=='I' ? "%02d" : "%2d", h);
break;
}
- case 'W': /* Fall thru */
- case 'j': {
- int nDay; /* Number of days since 1st day of year */
- DateTime y = x;
- y.validJD = 0;
- y.M = 1;
- y.D = 1;
- computeJD(&y);
- nDay = (int)((x.iJD-y.iJD+43200000)/86400000);
- if( cf=='W' ){
- int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */
- wd = (int)(((x.iJD+43200000)/86400000)%7);
- sqlite3_str_appendf(&sRes,"%02d",(nDay+7-wd)/7);
- }else{
- sqlite3_str_appendf(&sRes,"%03d",nDay+1);
- }
+ case 'j': { /* Day of year. Jan01==1, Jan02==2, and so forth */
+ sqlite3_str_appendf(&sRes,"%03d",daysAfterJan01(&x)+1);
break;
}
- case 'J': {
+ case 'J': { /* Julian day number. (Non-standard) */
sqlite3_str_appendf(&sRes,"%.16g",x.iJD/86400000.0);
break;
}
@@ -25520,13 +25731,33 @@ static void strftimeFunc(
sqlite3_str_appendf(&sRes,"%02d:%02d:%02d", x.h, x.m, (int)x.s);
break;
}
- case 'u': /* Fall thru */
- case 'w': {
- char c = (char)(((x.iJD+129600000)/86400000) % 7) + '0';
+ case 'u': /* Day of week. 1 to 7. Monday==1, Sunday==7 */
+ case 'w': { /* Day of week. 0 to 6. Sunday==0, Monday==1 */
+ char c = (char)daysAfterSunday(&x) + '0';
if( c=='0' && cf=='u' ) c = '7';
sqlite3_str_appendchar(&sRes, 1, c);
break;
}
+ case 'U': { /* Week num. 00-53. First Sun of the year is week 01 */
+ sqlite3_str_appendf(&sRes,"%02d",
+ (daysAfterJan01(&x)-daysAfterSunday(&x)+7)/7);
+ break;
+ }
+ case 'V': { /* Week num. 01-53. First week with a Thur is week 01 */
+ DateTime y = x;
+ /* Adjust y so that is the Thursday in the same week as x */
+ assert( y.validJD );
+ y.iJD += (3 - daysAfterMonday(&x))*86400000;
+ y.validYMD = 0;
+ computeYMD(&y);
+ sqlite3_str_appendf(&sRes,"%02d", daysAfterJan01(&y)/7+1);
+ break;
+ }
+ case 'W': { /* Week num. 00-53. First Mon of the year is week 01 */
+ sqlite3_str_appendf(&sRes,"%02d",
+ (daysAfterJan01(&x)-daysAfterMonday(&x)+7)/7);
+ break;
+ }
case 'Y': {
sqlite3_str_appendf(&sRes,"%04d",x.Y);
break;
@@ -25673,9 +25904,7 @@ static void timediffFunc(
d1.iJD = d2.iJD - d1.iJD;
d1.iJD += (u64)1486995408 * (u64)100000;
}
- d1.validYMD = 0;
- d1.validHMS = 0;
- d1.validTZ = 0;
+ clearYMD_HMS_TZ(&d1);
computeYMD_HMS(&d1);
sqlite3StrAccumInit(&sRes, 0, 0, 0, 100);
sqlite3_str_appendf(&sRes, "%c%04d-%02d-%02d %02d:%02d:%06.3f",
@@ -25744,6 +25973,36 @@ static void currentTimeFunc(
}
#endif
+#if !defined(SQLITE_OMIT_DATETIME_FUNCS) && defined(SQLITE_DEBUG)
+/*
+** datedebug(...)
+**
+** This routine returns JSON that describes the internal DateTime object.
+** Used for debugging and testing only. Subject to change.
+*/
+static void datedebugFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ DateTime x;
+ if( isDate(context, argc, argv, &x)==0 ){
+ char *zJson;
+ zJson = sqlite3_mprintf(
+ "{iJD:%lld,Y:%d,M:%d,D:%d,h:%d,m:%d,tz:%d,"
+ "s:%.3f,validJD:%d,validYMS:%d,validHMS:%d,"
+ "nFloor:%d,rawS:%d,isError:%d,useSubsec:%d,"
+ "isUtc:%d,isLocal:%d}",
+ x.iJD, x.Y, x.M, x.D, x.h, x.m, x.tz,
+ x.s, x.validJD, x.validYMD, x.validHMS,
+ x.nFloor, x.rawS, x.isError, x.useSubsec,
+ x.isUtc, x.isLocal);
+ sqlite3_result_text(context, zJson, -1, sqlite3_free);
+ }
+}
+#endif /* !SQLITE_OMIT_DATETIME_FUNCS && SQLITE_DEBUG */
+
+
/*
** This function registered all of the above C functions as SQL
** functions. This should be the only routine in this file with
@@ -25759,6 +26018,9 @@ SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void){
PURE_DATE(datetime, -1, 0, 0, datetimeFunc ),
PURE_DATE(strftime, -1, 0, 0, strftimeFunc ),
PURE_DATE(timediff, 2, 0, 0, timediffFunc ),
+#ifdef SQLITE_DEBUG
+ PURE_DATE(datedebug, -1, 0, 0, datedebugFunc ),
+#endif
DFUNCTION(current_time, 0, 0, 0, ctimeFunc ),
DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc),
DFUNCTION(current_date, 0, 0, 0, cdateFunc ),
@@ -30174,6 +30436,24 @@ static void sqlite3MallocAlarm(int nByte){
sqlite3_mutex_enter(mem0.mutex);
}
+#ifdef SQLITE_DEBUG
+/*
+** This routine is called whenever an out-of-memory condition is seen,
+** It's only purpose to to serve as a breakpoint for gdb or similar
+** code debuggers when working on out-of-memory conditions, for example
+** caused by PRAGMA hard_heap_limit=N.
+*/
+static SQLITE_NOINLINE void test_oom_breakpoint(u64 n){
+ static u64 nOomFault = 0;
+ nOomFault += n;
+ /* The assert() is never reached in a human lifetime. It is here mostly
+ ** to prevent code optimizers from optimizing out this function. */
+ assert( (nOomFault>>32) < 0xffffffff );
+}
+#else
+# define test_oom_breakpoint(X) /* No-op for production builds */
+#endif
+
/*
** Do a memory allocation with statistics and alarms. Assume the
** lock is already held.
@@ -30200,6 +30480,7 @@ static void mallocWithAlarm(int n, void **pp){
if( mem0.hardLimit ){
nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
if( nUsed >= mem0.hardLimit - nFull ){
+ test_oom_breakpoint(1);
*pp = 0;
return;
}
@@ -30488,6 +30769,7 @@ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){
sqlite3MallocAlarm(nDiff);
if( mem0.hardLimit>0 && nUsed >= mem0.hardLimit - nDiff ){
sqlite3_mutex_leave(mem0.mutex);
+ test_oom_breakpoint(1);
return 0;
}
}
@@ -31390,13 +31672,14 @@ SQLITE_API void sqlite3_str_vappendf(
}
exp = s.iDP-1;
- if( xtype==etGENERIC && precision>0 ) precision--;
/*
** If the field type is etGENERIC, then convert to either etEXP
** or etFLOAT, as appropriate.
*/
if( xtype==etGENERIC ){
+ assert( precision>0 );
+ precision--;
flag_rtz = !flag_alternateform;
if( exp<-4 || exp>precision ){
xtype = etEXP;
@@ -31712,9 +31995,13 @@ SQLITE_API void sqlite3_str_vappendf(
sqlite3_str_appendall(pAccum, pItem->zAlias);
}else{
Select *pSel = pItem->pSelect;
- assert( pSel!=0 );
+ assert( pSel!=0 ); /* Because of tag-20240424-1 */
if( pSel->selFlags & SF_NestedFrom ){
sqlite3_str_appendf(pAccum, "(join-%u)", pSel->selId);
+ }else if( pSel->selFlags & SF_MultiValue ){
+ assert( !pItem->fg.isTabFunc && !pItem->fg.isIndexedBy );
+ sqlite3_str_appendf(pAccum, "%u-ROW VALUES CLAUSE",
+ pItem->u1.nRow);
}else{
sqlite3_str_appendf(pAccum, "(subquery-%u)", pSel->selId);
}
@@ -32491,8 +32778,10 @@ SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc)
x.printfFlags |= SQLITE_PRINTF_INTERNAL;
sqlite3_str_appendf(&x, "{%d:*} %!S", pItem->iCursor, pItem);
if( pItem->pTab ){
- sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx",
- pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab, pItem->colUsed);
+ sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx%s",
+ pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab,
+ pItem->colUsed,
+ pItem->fg.rowidUsed ? "+rowid" : "");
}
if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))==(JT_LEFT|JT_RIGHT) ){
sqlite3_str_appendf(&x, " FULL-OUTER-JOIN");
@@ -32532,12 +32821,14 @@ SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc)
sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING");
}
if( pItem->pSelect ){
+ sqlite3TreeViewPush(&pView, i+1<pSrc->nSrc);
if( pItem->pTab ){
Table *pTab = pItem->pTab;
sqlite3TreeViewColumnList(pView, pTab->aCol, pTab->nCol, 1);
}
assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) );
sqlite3TreeViewSelect(pView, pItem->pSelect, (--n)>0);
+ sqlite3TreeViewPop(&pView);
}
if( pItem->fg.isTabFunc ){
sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:");
@@ -32641,7 +32932,7 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m
sqlite3TreeViewItem(pView, "LIMIT", (n--)>0);
sqlite3TreeViewExpr(pView, p->pLimit->pLeft, p->pLimit->pRight!=0);
if( p->pLimit->pRight ){
- sqlite3TreeViewItem(pView, "OFFSET", (n--)>0);
+ sqlite3TreeViewItem(pView, "OFFSET", 0);
sqlite3TreeViewExpr(pView, p->pLimit->pRight, 0);
sqlite3TreeViewPop(&pView);
}
@@ -34943,6 +35234,44 @@ SQLITE_PRIVATE void sqlite3DequoteExpr(Expr *p){
}
/*
+** Expression p is a QNUMBER (quoted number). Dequote the value in p->u.zToken
+** and set the type to INTEGER or FLOAT. "Quoted" integers or floats are those
+** that contain '_' characters that must be removed before further processing.
+*/
+SQLITE_PRIVATE void sqlite3DequoteNumber(Parse *pParse, Expr *p){
+ assert( p!=0 || pParse->db->mallocFailed );
+ if( p ){
+ const char *pIn = p->u.zToken;
+ char *pOut = p->u.zToken;
+ int bHex = (pIn[0]=='0' && (pIn[1]=='x' || pIn[1]=='X'));
+ int iValue;
+ assert( p->op==TK_QNUMBER );
+ p->op = TK_INTEGER;
+ do {
+ if( *pIn!=SQLITE_DIGIT_SEPARATOR ){
+ *pOut++ = *pIn;
+ if( *pIn=='e' || *pIn=='E' || *pIn=='.' ) p->op = TK_FLOAT;
+ }else{
+ if( (bHex==0 && (!sqlite3Isdigit(pIn[-1]) || !sqlite3Isdigit(pIn[1])))
+ || (bHex==1 && (!sqlite3Isxdigit(pIn[-1]) || !sqlite3Isxdigit(pIn[1])))
+ ){
+ sqlite3ErrorMsg(pParse, "unrecognized token: \"%s\"", p->u.zToken);
+ }
+ }
+ }while( *pIn++ );
+ if( bHex ) p->op = TK_INTEGER;
+
+ /* tag-20240227-a: If after dequoting, the number is an integer that
+ ** fits in 32 bits, then it must be converted into EP_IntValue. Other
+ ** parts of the code expect this. See also tag-20240227-b. */
+ if( p->op==TK_INTEGER && sqlite3GetInt32(p->u.zToken, &iValue) ){
+ p->u.iValue = iValue;
+ p->flags |= EP_IntValue;
+ }
+ }
+}
+
+/*
** If the input token p is quoted, try to adjust the token to remove
** the quotes. This is not always possible:
**
@@ -36881,7 +37210,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 30 */ "SeekRowid" OpHelp("intkey=r[P3]"),
/* 31 */ "NotExists" OpHelp("intkey=r[P3]"),
/* 32 */ "Last" OpHelp(""),
- /* 33 */ "IfSmaller" OpHelp(""),
+ /* 33 */ "IfSizeBetween" OpHelp(""),
/* 34 */ "SorterSort" OpHelp(""),
/* 35 */ "Sort" OpHelp(""),
/* 36 */ "Rewind" OpHelp(""),
@@ -36926,7 +37255,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 75 */ "Null" OpHelp("r[P2..P3]=NULL"),
/* 76 */ "SoftNull" OpHelp("r[P1]=NULL"),
/* 77 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"),
- /* 78 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"),
+ /* 78 */ "Variable" OpHelp("r[P2]=parameter(P1)"),
/* 79 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"),
/* 80 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"),
/* 81 */ "SCopy" OpHelp("r[P2]=r[P1]"),
@@ -39324,8 +39653,12 @@ static int unixLogErrorAtLine(
** available, the error message will often be an empty string. Not a
** huge problem. Incorrectly concluding that the GNU version is available
** could lead to a segfault though.
+ **
+ ** Forum post 3f13857fa4062301 reports that the Android SDK may use
+ ** int-type return, depending on its version.
*/
-#if defined(STRERROR_R_CHAR_P) || defined(__USE_GNU)
+#if (defined(STRERROR_R_CHAR_P) || defined(__USE_GNU)) \
+ && !defined(ANDROID) && !defined(__ANDROID__)
zErr =
# endif
strerror_r(iErrno, aErr, sizeof(aErr)-1);
@@ -44423,12 +44756,19 @@ static int unixOpen(
rc = SQLITE_READONLY_DIRECTORY;
}else if( errno!=EISDIR && isReadWrite ){
/* Failed to open the file for read/write access. Try read-only. */
+ UnixUnusedFd *pReadonly = 0;
flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
openFlags &= ~(O_RDWR|O_CREAT);
flags |= SQLITE_OPEN_READONLY;
openFlags |= O_RDONLY;
isReadonly = 1;
- fd = robust_open(zName, openFlags, openMode);
+ pReadonly = findReusableFd(zName, flags);
+ if( pReadonly ){
+ fd = pReadonly->fd;
+ sqlite3_free(pReadonly);
+ }else{
+ fd = robust_open(zName, openFlags, openMode);
+ }
}
}
if( fd<0 ){
@@ -69879,6 +70219,7 @@ struct IntegrityCk {
StrAccum errMsg; /* Accumulate the error message text here */
u32 *heap; /* Min-heap used for analyzing cell coverage */
sqlite3 *db; /* Database connection running the check */
+ i64 nRow; /* Number of rows visited in current tree */
};
/*
@@ -70353,8 +70694,47 @@ int corruptPageError(int lineno, MemPage *p){
# define SQLITE_CORRUPT_PAGE(pMemPage) SQLITE_CORRUPT_PGNO(pMemPage->pgno)
#endif
+/* Default value for SHARED_LOCK_TRACE macro if shared-cache is disabled
+** or if the lock tracking is disabled. This is always the value for
+** release builds.
+*/
+#define SHARED_LOCK_TRACE(X,MSG,TAB,TYPE) /*no-op*/
+
#ifndef SQLITE_OMIT_SHARED_CACHE
+#if 0
+/* ^---- Change to 1 and recompile to enable shared-lock tracing
+** for debugging purposes.
+**
+** Print all shared-cache locks on a BtShared. Debugging use only.
+*/
+static void sharedLockTrace(
+ BtShared *pBt,
+ const char *zMsg,
+ int iRoot,
+ int eLockType
+){
+ BtLock *pLock;
+ if( iRoot>0 ){
+ printf("%s-%p %u%s:", zMsg, pBt, iRoot, eLockType==READ_LOCK?"R":"W");
+ }else{
+ printf("%s-%p:", zMsg, pBt);
+ }
+ for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){
+ printf(" %p/%u%s", pLock->pBtree, pLock->iTable,
+ pLock->eLock==READ_LOCK ? "R" : "W");
+ while( pLock->pNext && pLock->pBtree==pLock->pNext->pBtree ){
+ pLock = pLock->pNext;
+ printf(",%u%s", pLock->iTable, pLock->eLock==READ_LOCK ? "R" : "W");
+ }
+ }
+ printf("\n");
+ fflush(stdout);
+}
+#undef SHARED_LOCK_TRACE
+#define SHARED_LOCK_TRACE(X,MSG,TAB,TYPE) sharedLockTrace(X,MSG,TAB,TYPE)
+#endif /* Shared-lock tracing */
+
#ifdef SQLITE_DEBUG
/*
**** This function is only used as part of an assert() statement. ***
@@ -70431,6 +70811,8 @@ static int hasSharedCacheTableLock(
iTab = iRoot;
}
+ SHARED_LOCK_TRACE(pBtree->pBt,"hasLock",iRoot,eLockType);
+
/* Search for the required lock. Either a write-lock on root-page iTab, a
** write-lock on the schema table, or (if the client is reading) a
** read-lock on iTab will suffice. Return 1 if any of these are found. */
@@ -70564,6 +70946,8 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){
BtLock *pLock = 0;
BtLock *pIter;
+ SHARED_LOCK_TRACE(pBt,"setLock", iTable, eLock);
+
assert( sqlite3BtreeHoldsMutex(p) );
assert( eLock==READ_LOCK || eLock==WRITE_LOCK );
assert( p->db!=0 );
@@ -70631,6 +71015,8 @@ static void clearAllSharedCacheTableLocks(Btree *p){
assert( p->sharable || 0==*ppIter );
assert( p->inTrans>0 );
+ SHARED_LOCK_TRACE(pBt, "clearAllLocks", 0, 0);
+
while( *ppIter ){
BtLock *pLock = *ppIter;
assert( (pBt->btsFlags & BTS_EXCLUSIVE)==0 || pBt->pWriter==pLock->pBtree );
@@ -70669,6 +71055,9 @@ static void clearAllSharedCacheTableLocks(Btree *p){
*/
static void downgradeAllSharedCacheTableLocks(Btree *p){
BtShared *pBt = p->pBt;
+
+ SHARED_LOCK_TRACE(pBt, "downgradeLocks", 0, 0);
+
if( pBt->pWriter==p ){
BtLock *pLock;
pBt->pWriter = 0;
@@ -75282,9 +75671,12 @@ static int accessPayload(
if( pCur->aOverflow==0
|| nOvfl*(int)sizeof(Pgno) > sqlite3MallocSize(pCur->aOverflow)
){
- Pgno *aNew = (Pgno*)sqlite3Realloc(
- pCur->aOverflow, nOvfl*2*sizeof(Pgno)
- );
+ Pgno *aNew;
+ if( sqlite3FaultSim(413) ){
+ aNew = 0;
+ }else{
+ aNew = (Pgno*)sqlite3Realloc(pCur->aOverflow, nOvfl*2*sizeof(Pgno));
+ }
if( aNew==0 ){
return SQLITE_NOMEM_BKPT;
}else{
@@ -75294,6 +75686,12 @@ static int accessPayload(
memset(pCur->aOverflow, 0, nOvfl*sizeof(Pgno));
pCur->curFlags |= BTCF_ValidOvfl;
}else{
+ /* Sanity check the validity of the overflow page cache */
+ assert( pCur->aOverflow[0]==nextPage
+ || pCur->aOverflow[0]==0
+ || CORRUPT_DB );
+ assert( pCur->aOverflow[0]!=0 || pCur->aOverflow[offset/ovflSize]==0 );
+
/* If the overflow page-list cache has been allocated and the
** entry for the first required overflow page is valid, skip
** directly to it.
@@ -75775,6 +76173,23 @@ SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
return rc;
}
+#ifdef SQLITE_DEBUG
+/* The cursors is CURSOR_VALID and has BTCF_AtLast set. Verify that
+** this flags are true for a consistent database.
+**
+** This routine is is called from within assert() statements only.
+** It is an internal verification routine and does not appear in production
+** builds.
+*/
+static int cursorIsAtLastEntry(BtCursor *pCur){
+ int ii;
+ for(ii=0; ii<pCur->iPage; ii++){
+ if( pCur->aiIdx[ii]!=pCur->apPage[ii]->nCell ) return 0;
+ }
+ return pCur->ix==pCur->pPage->nCell-1 && pCur->pPage->leaf!=0;
+}
+#endif
+
/* Move the cursor to the last entry in the table. Return SQLITE_OK
** on success. Set *pRes to 0 if the cursor actually points to something
** or set *pRes to 1 if the table is empty.
@@ -75803,18 +76218,7 @@ SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
/* If the cursor already points to the last entry, this is a no-op. */
if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){
-#ifdef SQLITE_DEBUG
- /* This block serves to assert() that the cursor really does point
- ** to the last entry in the b-tree. */
- int ii;
- for(ii=0; ii<pCur->iPage; ii++){
- assert( pCur->aiIdx[ii]==pCur->apPage[ii]->nCell );
- }
- assert( pCur->ix==pCur->pPage->nCell-1 || CORRUPT_DB );
- testcase( pCur->ix!=pCur->pPage->nCell-1 );
- /* ^-- dbsqlfuzz b92b72e4de80b5140c30ab71372ca719b8feb618 */
- assert( pCur->pPage->leaf );
-#endif
+ assert( cursorIsAtLastEntry(pCur) || CORRUPT_DB );
*pRes = 0;
return SQLITE_OK;
}
@@ -75867,6 +76271,7 @@ SQLITE_PRIVATE int sqlite3BtreeTableMoveto(
}
if( pCur->info.nKey<intKey ){
if( (pCur->curFlags & BTCF_AtLast)!=0 ){
+ assert( cursorIsAtLastEntry(pCur) || CORRUPT_DB );
*pRes = -1;
return SQLITE_OK;
}
@@ -76333,10 +76738,10 @@ SQLITE_PRIVATE i64 sqlite3BtreeRowCountEst(BtCursor *pCur){
assert( cursorOwnsBtShared(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
- /* Currently this interface is only called by the OP_IfSmaller
- ** opcode, and it that case the cursor will always be valid and
- ** will always point to a leaf node. */
- if( NEVER(pCur->eState!=CURSOR_VALID) ) return -1;
+ /* Currently this interface is only called by the OP_IfSizeBetween
+ ** opcode and the OP_Count opcode with P3=1. In either case,
+ ** the cursor will always be valid unless the btree is empty. */
+ if( pCur->eState!=CURSOR_VALID ) return 0;
if( NEVER(pCur->pPage->leaf==0) ) return -1;
n = pCur->pPage->nCell;
@@ -78467,7 +78872,7 @@ static int balance_nonroot(
** table-interior, index-leaf, or index-interior).
*/
if( pOld->aData[0]!=apOld[0]->aData[0] ){
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PAGE(pOld);
goto balance_cleanup;
}
@@ -78491,7 +78896,7 @@ static int balance_nonroot(
memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow));
if( pOld->nOverflow>0 ){
if( NEVER(limit<pOld->aiOvfl[0]) ){
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PAGE(pOld);
goto balance_cleanup;
}
limit = pOld->aiOvfl[0];
@@ -79134,7 +79539,7 @@ static int anotherValidCursor(BtCursor *pCur){
&& pOther->eState==CURSOR_VALID
&& pOther->pPage==pCur->pPage
){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pCur->pPage);
}
}
return SQLITE_OK;
@@ -79194,7 +79599,7 @@ static int balance(BtCursor *pCur){
/* The page being written is not a root page, and there is currently
** more than one reference to it. This only happens if the page is one
** of its own ancestor pages. Corruption. */
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PAGE(pPage);
}else{
MemPage * const pParent = pCur->apPage[iPage-1];
int const iIdx = pCur->aiIdx[iPage-1];
@@ -79358,7 +79763,7 @@ static SQLITE_NOINLINE int btreeOverwriteOverflowCell(
rc = btreeGetPage(pBt, ovflPgno, &pPage, 0);
if( rc ) return rc;
if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 || pPage->isInit ){
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PAGE(pPage);
}else{
if( iOffset+ovflPageSize<(u32)nTotal ){
ovflPgno = get4byte(pPage->aData);
@@ -79386,7 +79791,7 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd
|| pCur->info.pPayload < pPage->aData + pPage->cellOffset
){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
if( pCur->info.nLocal==nTotal ){
/* The entire cell is local */
@@ -79467,7 +79872,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
** Which can only happen if the SQLITE_NoSchemaError flag was set when
** the schema was loaded. This cannot be asserted though, as a user might
** set the flag, load the schema, and then unset the flag. */
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PGNO(pCur->pgnoRoot);
}
}
@@ -79590,7 +79995,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
if( pPage->nFree<0 ){
if( NEVER(pCur->eState>CURSOR_INVALID) ){
/* ^^^^^--- due to the moveToRoot() call above */
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PAGE(pPage);
}else{
rc = btreeComputeFreeSpace(pPage);
}
@@ -79632,7 +80037,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
CellInfo info;
assert( idx>=0 );
if( idx>=pPage->nCell ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc ){
@@ -79659,10 +80064,10 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
** necessary to add the PTRMAP_OVERFLOW1 pointer-map entry. */
assert( rc==SQLITE_OK ); /* clearCell never fails when nLocal==nPayload */
if( oldCell < pPage->aData+pPage->hdrOffset+10 ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
if( oldCell+szNew > pPage->aDataEnd ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
memcpy(oldCell, newCell, szNew);
return SQLITE_OK;
@@ -79764,7 +80169,7 @@ SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64
nIn = pSrc->info.nLocal;
aIn = pSrc->info.pPayload;
if( aIn+nIn>pSrc->pPage->aDataEnd ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pSrc->pPage);
}
nRem = pSrc->info.nPayload;
if( nIn==nRem && nIn<pDest->pPage->maxLocal ){
@@ -79789,7 +80194,7 @@ SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64
if( nRem>nIn ){
if( aIn+nIn+4>pSrc->pPage->aDataEnd ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pSrc->pPage);
}
ovflIn = get4byte(&pSrc->info.pPayload[nIn]);
}
@@ -79885,7 +80290,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
assert( rc!=SQLITE_OK || CORRUPT_DB || pCur->eState==CURSOR_VALID );
if( rc || pCur->eState!=CURSOR_VALID ) return rc;
}else{
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PGNO(pCur->pgnoRoot);
}
}
assert( pCur->eState==CURSOR_VALID );
@@ -79894,14 +80299,14 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
iCellIdx = pCur->ix;
pPage = pCur->pPage;
if( pPage->nCell<=iCellIdx ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
pCell = findCell(pPage, iCellIdx);
if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
if( pCell<&pPage->aCellIdx[pPage->nCell] ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
/* If the BTREE_SAVEPOSITION bit is on, then the cursor position must
@@ -79992,7 +80397,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
n = pCur->pPage->pgno;
}
pCell = findCell(pLeaf, pLeaf->nCell-1);
- if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_BKPT;
+ if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_PAGE(pLeaf);
nCell = pLeaf->xCellSize(pLeaf, pCell);
assert( MX_CELL_SIZE(pBt) >= nCell );
pTmp = pBt->pTmpSpace;
@@ -80108,7 +80513,7 @@ static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){
*/
sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot);
if( pgnoRoot>btreePagecount(pBt) ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PGNO(pgnoRoot);
}
pgnoRoot++;
@@ -80156,7 +80561,7 @@ static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){
}
rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage);
if( eType==PTRMAP_ROOTPAGE || eType==PTRMAP_FREEPAGE ){
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PGNO(pgnoRoot);
}
if( rc!=SQLITE_OK ){
releasePage(pRoot);
@@ -80246,14 +80651,14 @@ static int clearDatabasePage(
assert( sqlite3_mutex_held(pBt->mutex) );
if( pgno>btreePagecount(pBt) ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PGNO(pgno);
}
rc = getAndInitPage(pBt, pgno, &pPage, 0);
if( rc ) return rc;
if( (pBt->openFlags & BTREE_SINGLE)==0
&& sqlite3PagerPageRefcount(pPage->pDbPage) != (1 + (pgno==1))
){
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PAGE(pPage);
goto cleardatabasepage_out;
}
hdr = pPage->hdrOffset;
@@ -80357,7 +80762,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
assert( p->inTrans==TRANS_WRITE );
assert( iTable>=2 );
if( iTable>btreePagecount(pBt) ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PGNO(iTable);
}
rc = sqlite3BtreeClearTable(p, iTable, 0);
@@ -80951,6 +81356,9 @@ static int checkTreePage(
** number of cells on the page. */
nCell = get2byte(&data[hdr+3]);
assert( pPage->nCell==nCell );
+ if( pPage->leaf || pPage->intKey==0 ){
+ pCheck->nRow += nCell;
+ }
/* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page
** immediately follows the b-tree page header. */
@@ -81062,6 +81470,7 @@ static int checkTreePage(
btreeHeapInsert(heap, (pc<<16)|(pc+size-1));
}
}
+ assert( heap!=0 );
/* Add the freeblocks to the min-heap
**
** EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header
@@ -81161,6 +81570,7 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck(
sqlite3 *db, /* Database connection that is running the check */
Btree *p, /* The btree to be checked */
Pgno *aRoot, /* An array of root pages numbers for individual trees */
+ Mem *aCnt, /* Memory cells to write counts for each tree to */
int nRoot, /* Number of entries in aRoot[] */
int mxErr, /* Stop reporting errors after this many */
int *pnErr, /* OUT: Write number of errors seen to this variable */
@@ -81174,7 +81584,9 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck(
int bPartial = 0; /* True if not checking all btrees */
int bCkFreelist = 1; /* True to scan the freelist */
VVA_ONLY( int nRef );
+
assert( nRoot>0 );
+ assert( aCnt!=0 );
/* aRoot[0]==0 means this is a partial check */
if( aRoot[0]==0 ){
@@ -81247,15 +81659,18 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck(
testcase( pBt->db->flags & SQLITE_CellSizeCk );
pBt->db->flags &= ~(u64)SQLITE_CellSizeCk;
for(i=0; (int)i<nRoot && sCheck.mxErr; i++){
- i64 notUsed;
- if( aRoot[i]==0 ) continue;
+ sCheck.nRow = 0;
+ if( aRoot[i] ){
+ i64 notUsed;
#ifndef SQLITE_OMIT_AUTOVACUUM
- if( pBt->autoVacuum && aRoot[i]>1 && !bPartial ){
- checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0);
- }
+ if( pBt->autoVacuum && aRoot[i]>1 && !bPartial ){
+ checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0);
+ }
#endif
- sCheck.v0 = aRoot[i];
- checkTreePage(&sCheck, aRoot[i], &notUsed, LARGEST_INT64);
+ sCheck.v0 = aRoot[i];
+ checkTreePage(&sCheck, aRoot[i], &notUsed, LARGEST_INT64);
+ }
+ sqlite3MemSetArrayInt64(aCnt, i, sCheck.nRow);
}
pBt->db->flags = savedDbFlags;
@@ -83310,6 +83725,13 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){
}
}
+/*
+** Set the iIdx'th entry of array aMem[] to contain integer value val.
+*/
+SQLITE_PRIVATE void sqlite3MemSetArrayInt64(sqlite3_value *aMem, int iIdx, i64 val){
+ sqlite3VdbeMemSetInt64(&aMem[iIdx], val);
+}
+
/* A no-op destructor */
SQLITE_PRIVATE void sqlite3NoopDestructor(void *p){ UNUSED_PARAMETER(p); }
@@ -83998,14 +84420,20 @@ static int valueFromExpr(
}
/* Handle negative integers in a single step. This is needed in the
- ** case when the value is -9223372036854775808.
- */
- if( op==TK_UMINUS
- && (pExpr->pLeft->op==TK_INTEGER || pExpr->pLeft->op==TK_FLOAT) ){
- pExpr = pExpr->pLeft;
- op = pExpr->op;
- negInt = -1;
- zNeg = "-";
+ ** case when the value is -9223372036854775808. Except - do not do this
+ ** for hexadecimal literals. */
+ if( op==TK_UMINUS ){
+ Expr *pLeft = pExpr->pLeft;
+ if( (pLeft->op==TK_INTEGER || pLeft->op==TK_FLOAT) ){
+ if( ExprHasProperty(pLeft, EP_IntValue)
+ || pLeft->u.zToken[0]!='0' || (pLeft->u.zToken[1] & ~0x20)!='X'
+ ){
+ pExpr = pLeft;
+ op = pExpr->op;
+ negInt = -1;
+ zNeg = "-";
+ }
+ }
}
if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){
@@ -84014,12 +84442,26 @@ static int valueFromExpr(
if( ExprHasProperty(pExpr, EP_IntValue) ){
sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt);
}else{
- zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken);
- if( zVal==0 ) goto no_mem;
- sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC);
+ i64 iVal;
+ if( op==TK_INTEGER && 0==sqlite3DecOrHexToI64(pExpr->u.zToken, &iVal) ){
+ sqlite3VdbeMemSetInt64(pVal, iVal*negInt);
+ }else{
+ zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken);
+ if( zVal==0 ) goto no_mem;
+ sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC);
+ }
}
- if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_BLOB ){
- sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8);
+ if( affinity==SQLITE_AFF_BLOB ){
+ if( op==TK_FLOAT ){
+ assert( pVal && pVal->z && pVal->flags==(MEM_Str|MEM_Term) );
+ sqlite3AtoF(pVal->z, &pVal->u.r, pVal->n, SQLITE_UTF8);
+ pVal->flags = MEM_Real;
+ }else if( op==TK_INTEGER ){
+ /* This case is required by -9223372036854775808 and other strings
+ ** that look like integers but cannot be handled by the
+ ** sqlite3DecOrHexToI64() call above. */
+ sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8);
+ }
}else{
sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8);
}
@@ -84289,17 +84731,17 @@ SQLITE_PRIVATE int sqlite3Stat4Column(
sqlite3_value **ppVal /* OUT: Extracted value */
){
u32 t = 0; /* a column type code */
- int nHdr; /* Size of the header in the record */
- int iHdr; /* Next unread header byte */
- int iField; /* Next unread data byte */
- int szField = 0; /* Size of the current data field */
+ u32 nHdr; /* Size of the header in the record */
+ u32 iHdr; /* Next unread header byte */
+ i64 iField; /* Next unread data byte */
+ u32 szField = 0; /* Size of the current data field */
int i; /* Column index */
u8 *a = (u8*)pRec; /* Typecast byte array */
Mem *pMem = *ppVal; /* Write result into this Mem object */
assert( iCol>0 );
iHdr = getVarint32(a, nHdr);
- if( nHdr>nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT;
+ if( nHdr>(u32)nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT;
iField = nHdr;
for(i=0; i<=iCol; i++){
iHdr += getVarint32(&a[iHdr], t);
@@ -85334,6 +85776,15 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
assert( aLabel!=0 ); /* True because of tag-20230419-1 */
pOp->p2 = aLabel[ADDR(pOp->p2)];
}
+
+ /* OPFLG_JUMP opcodes never have P2==0, though OPFLG_JUMP0 opcodes
+ ** might */
+ assert( pOp->p2>0
+ || (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP0)!=0 );
+
+ /* Jumps never go off the end of the bytecode array */
+ assert( pOp->p2<p->nOp
+ || (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)==0 );
break;
}
}
@@ -87741,7 +88192,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
/* Check for immediate foreign key violations. */
if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
- sqlite3VdbeCheckFk(p, 0);
+ (void)sqlite3VdbeCheckFk(p, 0);
}
/* If the auto-commit flag is set and this is the only active writer
@@ -88911,17 +89362,15 @@ SQLITE_PRIVATE int sqlite3IntFloatCompare(i64 i, double r){
return (x<r) ? -1 : (x>r);
}else{
i64 y;
- double s;
if( r<-9223372036854775808.0 ) return +1;
if( r>=9223372036854775808.0 ) return -1;
y = (i64)r;
if( i<y ) return -1;
if( i>y ) return +1;
- s = (double)i;
- testcase( doubleLt(s,r) );
- testcase( doubleLt(r,s) );
- testcase( doubleEq(r,s) );
- return (s<r) ? -1 : (s>r);
+ testcase( doubleLt(((double)i),r) );
+ testcase( doubleLt(r,((double)i)) );
+ testcase( doubleEq(r,((double)i)) );
+ return (((double)i)<r) ? -1 : (((double)i)>r);
}
}
@@ -92329,7 +92778,6 @@ SQLITE_API int sqlite3_stmt_scanstatus_v2(
}
if( flags & SQLITE_SCANSTAT_COMPLEX ){
idx = iScan;
- pScan = &p->aScan[idx];
}else{
/* If the COMPLEX flag is clear, then this function must ignore any
** ScanStatus structures with ScanStatus.addrLoop set to 0. */
@@ -92342,6 +92790,8 @@ SQLITE_API int sqlite3_stmt_scanstatus_v2(
}
}
if( idx>=p->nScan ) return 1;
+ assert( pScan==0 || pScan==&p->aScan[idx] );
+ pScan = &p->aScan[idx];
switch( iScanStatusOp ){
case SQLITE_SCANSTAT_NLOOP: {
@@ -93790,7 +94240,7 @@ case OP_Return: { /* in1 */
**
** See also: EndCoroutine
*/
-case OP_InitCoroutine: { /* jump */
+case OP_InitCoroutine: { /* jump0 */
assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
assert( pOp->p2>=0 && pOp->p2<p->nOp );
assert( pOp->p3>=0 && pOp->p3<p->nOp );
@@ -93813,7 +94263,9 @@ jump_to_p2:
**
** The instruction at the address in register P1 is a Yield.
** Jump to the P2 parameter of that Yield.
-** After the jump, register P1 becomes undefined.
+** After the jump, the value register P1 is left with a value
+** such that subsequent OP_Yields go back to the this same
+** OP_EndCoroutine instruction.
**
** See also: InitCoroutine
*/
@@ -93825,8 +94277,8 @@ case OP_EndCoroutine: { /* in1 */
pCaller = &aOp[pIn1->u.i];
assert( pCaller->opcode==OP_Yield );
assert( pCaller->p2>=0 && pCaller->p2<p->nOp );
+ pIn1->u.i = (int)(pOp - p->aOp) - 1;
pOp = &aOp[pCaller->p2 - 1];
- pIn1->flags = MEM_Undefined;
break;
}
@@ -93843,7 +94295,7 @@ case OP_EndCoroutine: { /* in1 */
**
** See also: InitCoroutine
*/
-case OP_Yield: { /* in1, jump */
+case OP_Yield: { /* in1, jump0 */
int pcDest;
pIn1 = &aMem[pOp->p1];
assert( VdbeMemDynamic(pIn1)==0 );
@@ -94173,19 +94625,15 @@ case OP_Blob: { /* out2 */
break;
}
-/* Opcode: Variable P1 P2 * P4 *
-** Synopsis: r[P2]=parameter(P1,P4)
+/* Opcode: Variable P1 P2 * * *
+** Synopsis: r[P2]=parameter(P1)
**
** Transfer the values of bound parameter P1 into register P2
-**
-** If the parameter is named, then its name appears in P4.
-** The P4 value is used by sqlite3_bind_parameter_name().
*/
case OP_Variable: { /* out2 */
Mem *pVar; /* Value being transferred */
assert( pOp->p1>0 && pOp->p1<=p->nVar );
- assert( pOp->p4.z==0 || pOp->p4.z==sqlite3VListNumToName(p->pVList,pOp->p1) );
pVar = &p->aVar[pOp->p1 - 1];
if( sqlite3VdbeMemTooBig(pVar) ){
goto too_big;
@@ -94706,7 +95154,7 @@ case OP_AddImm: { /* in1 */
** without data loss, then jump immediately to P2, or if P2==0
** raise an SQLITE_MISMATCH exception.
*/
-case OP_MustBeInt: { /* jump, in1 */
+case OP_MustBeInt: { /* jump0, in1 */
pIn1 = &aMem[pOp->p1];
if( (pIn1->flags & MEM_Int)==0 ){
applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding);
@@ -94747,7 +95195,7 @@ case OP_RealAffinity: { /* in1 */
}
#endif
-#ifndef SQLITE_OMIT_CAST
+#if !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_ANALYZE)
/* Opcode: Cast P1 P2 * * *
** Synopsis: affinity(r[P1])
**
@@ -96319,11 +96767,16 @@ case OP_MakeRecord: {
switch( len ){
default: zPayload[7] = (u8)(v&0xff); v >>= 8;
zPayload[6] = (u8)(v&0xff); v >>= 8;
+ /* no break */ deliberate_fall_through
case 6: zPayload[5] = (u8)(v&0xff); v >>= 8;
zPayload[4] = (u8)(v&0xff); v >>= 8;
+ /* no break */ deliberate_fall_through
case 4: zPayload[3] = (u8)(v&0xff); v >>= 8;
+ /* no break */ deliberate_fall_through
case 3: zPayload[2] = (u8)(v&0xff); v >>= 8;
+ /* no break */ deliberate_fall_through
case 2: zPayload[1] = (u8)(v&0xff); v >>= 8;
+ /* no break */ deliberate_fall_through
case 1: zPayload[0] = (u8)(v&0xff);
}
zPayload += len;
@@ -97242,7 +97695,8 @@ case OP_SequenceTest: {
** is the only cursor opcode that works with a pseudo-table.
**
** P3 is the number of fields in the records that will be stored by
-** the pseudo-table.
+** the pseudo-table. If P2 is 0 or negative then the pseudo-cursor
+** will return NULL for every column.
*/
case OP_OpenPseudo: {
VdbeCursor *pCx;
@@ -97385,10 +97839,10 @@ case OP_ColumnsUsed: {
**
** See also: Found, NotFound, SeekGt, SeekGe, SeekLt
*/
-case OP_SeekLT: /* jump, in3, group, ncycle */
-case OP_SeekLE: /* jump, in3, group, ncycle */
-case OP_SeekGE: /* jump, in3, group, ncycle */
-case OP_SeekGT: { /* jump, in3, group, ncycle */
+case OP_SeekLT: /* jump0, in3, group, ncycle */
+case OP_SeekLE: /* jump0, in3, group, ncycle */
+case OP_SeekGE: /* jump0, in3, group, ncycle */
+case OP_SeekGT: { /* jump0, in3, group, ncycle */
int res; /* Comparison result */
int oc; /* Opcode */
VdbeCursor *pC; /* The cursor to seek */
@@ -98055,7 +98509,7 @@ case OP_Found: { /* jump, in3, ncycle */
**
** See also: Found, NotFound, NoConflict, SeekRowid
*/
-case OP_SeekRowid: { /* jump, in3, ncycle */
+case OP_SeekRowid: { /* jump0, in3, ncycle */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
@@ -98814,7 +99268,7 @@ case OP_NullRow: {
** configured to use Prev, not Next.
*/
case OP_SeekEnd: /* ncycle */
-case OP_Last: { /* jump, ncycle */
+case OP_Last: { /* jump0, ncycle */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
@@ -98848,28 +99302,38 @@ case OP_Last: { /* jump, ncycle */
break;
}
-/* Opcode: IfSmaller P1 P2 P3 * *
+/* Opcode: IfSizeBetween P1 P2 P3 P4 *
**
-** Estimate the number of rows in the table P1. Jump to P2 if that
-** estimate is less than approximately 2**(0.1*P3).
+** Let N be the approximate number of rows in the table or index
+** with cursor P1 and let X be 10*log2(N) if N is positive or -1
+** if N is zero.
+**
+** Jump to P2 if X is in between P3 and P4, inclusive.
*/
-case OP_IfSmaller: { /* jump */
+case OP_IfSizeBetween: { /* jump */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
i64 sz;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ assert( pOp->p4type==P4_INT32 );
+ assert( pOp->p3>=-1 && pOp->p3<=640*2 );
+ assert( pOp->p4.i>=-1 && pOp->p4.i<=640*2 );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
pCrsr = pC->uc.pCursor;
assert( pCrsr );
rc = sqlite3BtreeFirst(pCrsr, &res);
if( rc ) goto abort_due_to_error;
- if( res==0 ){
+ if( res!=0 ){
+ sz = -1; /* -Infinity encoding */
+ }else{
sz = sqlite3BtreeRowCountEst(pCrsr);
- if( ALWAYS(sz>=0) && sqlite3LogEst((u64)sz)<pOp->p3 ) res = 1;
+ assert( sz>0 );
+ sz = sqlite3LogEst((u64)sz);
}
+ res = sz>=pOp->p3 && sz<=pOp->p4.i;
VdbeBranchTaken(res!=0,2);
if( res ) goto jump_to_p2;
break;
@@ -98922,7 +99386,7 @@ case OP_Sort: { /* jump ncycle */
** from the beginning toward the end. In other words, the cursor is
** configured to use Next, not Prev.
*/
-case OP_Rewind: { /* jump, ncycle */
+case OP_Rewind: { /* jump0, ncycle */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
@@ -99569,11 +100033,18 @@ case OP_CreateBtree: { /* out2 */
break;
}
-/* Opcode: SqlExec * * * P4 *
+/* Opcode: SqlExec P1 P2 * P4 *
**
** Run the SQL statement or statements specified in the P4 string.
-** Disable Auth and Trace callbacks while those statements are running if
-** P1 is true.
+**
+** The P1 parameter is a bitmask of options:
+**
+** 0x0001 Disable Auth and Trace callbacks while the statements
+** in P4 are running.
+**
+** 0x0002 Set db->nAnalysisLimit to P2 while the statements in
+** P4 are running.
+**
*/
case OP_SqlExec: {
char *zErr;
@@ -99581,6 +100052,7 @@ case OP_SqlExec: {
sqlite3_xauth xAuth;
#endif
u8 mTrace;
+ int savedAnalysisLimit;
sqlite3VdbeIncrWriteCounter(p, 0);
db->nSqlExec++;
@@ -99589,18 +100061,23 @@ case OP_SqlExec: {
xAuth = db->xAuth;
#endif
mTrace = db->mTrace;
- if( pOp->p1 ){
+ savedAnalysisLimit = db->nAnalysisLimit;
+ if( pOp->p1 & 0x0001 ){
#ifndef SQLITE_OMIT_AUTHORIZATION
db->xAuth = 0;
#endif
db->mTrace = 0;
}
+ if( pOp->p1 & 0x0002 ){
+ db->nAnalysisLimit = pOp->p2;
+ }
rc = sqlite3_exec(db, pOp->p4.z, 0, 0, &zErr);
db->nSqlExec--;
#ifndef SQLITE_OMIT_AUTHORIZATION
db->xAuth = xAuth;
#endif
db->mTrace = mTrace;
+ db->nAnalysisLimit = savedAnalysisLimit;
if( zErr || rc ){
sqlite3VdbeError(p, "%s", zErr);
sqlite3_free(zErr);
@@ -99752,11 +100229,11 @@ case OP_DropTrigger: {
/* Opcode: IntegrityCk P1 P2 P3 P4 P5
**
** Do an analysis of the currently open database. Store in
-** register P1 the text of an error message describing any problems.
-** If no problems are found, store a NULL in register P1.
+** register (P1+1) the text of an error message describing any problems.
+** If no problems are found, store a NULL in register (P1+1).
**
-** The register P3 contains one less than the maximum number of allowed errors.
-** At most reg(P3) errors will be reported.
+** The register (P1) contains one less than the maximum number of allowed
+** errors. At most reg(P1) errors will be reported.
** In other words, the analysis stops as soon as reg(P1) errors are
** seen. Reg(P1) is updated with the number of errors remaining.
**
@@ -99776,19 +100253,21 @@ case OP_IntegrityCk: {
Mem *pnErr; /* Register keeping track of errors remaining */
assert( p->bIsReader );
+ assert( pOp->p4type==P4_INTARRAY );
nRoot = pOp->p2;
aRoot = pOp->p4.ai;
assert( nRoot>0 );
+ assert( aRoot!=0 );
assert( aRoot[0]==(Pgno)nRoot );
- assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
- pnErr = &aMem[pOp->p3];
+ assert( pOp->p1>0 && (pOp->p1+1)<=(p->nMem+1 - p->nCursor) );
+ pnErr = &aMem[pOp->p1];
assert( (pnErr->flags & MEM_Int)!=0 );
assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 );
- pIn1 = &aMem[pOp->p1];
+ pIn1 = &aMem[pOp->p1+1];
assert( pOp->p5<db->nDb );
assert( DbMaskTest(p->btreeMask, pOp->p5) );
- rc = sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1], nRoot,
- (int)pnErr->u.i+1, &nErr, &z);
+ rc = sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1],
+ &aMem[pOp->p3], nRoot, (int)pnErr->u.i+1, &nErr, &z);
sqlite3VdbeMemSetNull(pIn1);
if( nErr==0 ){
assert( z==0 );
@@ -99915,7 +100394,9 @@ case OP_RowSetTest: { /* jump, in1, in3 */
** P1 contains the address of the memory cell that contains the first memory
** cell in an array of values used as arguments to the sub-program. P2
** contains the address to jump to if the sub-program throws an IGNORE
-** exception using the RAISE() function. Register P3 contains the address
+** exception using the RAISE() function. P2 might be zero, if there is
+** no possibility that an IGNORE exception will be raised.
+** Register P3 contains the address
** of a memory cell in this (the parent) VM that is used to allocate the
** memory required by the sub-vdbe at runtime.
**
@@ -99923,7 +100404,7 @@ case OP_RowSetTest: { /* jump, in1, in3 */
**
** If P5 is non-zero, then recursive program invocation is enabled.
*/
-case OP_Program: { /* jump */
+case OP_Program: { /* jump0 */
int nMem; /* Number of memory registers for sub-program */
int nByte; /* Bytes of runtime space required for sub-program */
Mem *pRt; /* Register to allocate runtime space */
@@ -101472,7 +101953,7 @@ case OP_Filter: { /* jump */
** error is encountered.
*/
case OP_Trace:
-case OP_Init: { /* jump */
+case OP_Init: { /* jump0 */
int i;
#ifndef SQLITE_OMIT_TRACE
char *zTrace;
@@ -105373,10 +105854,10 @@ static int bytecodevtabColumn(
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
case 9: /* nexec */
- sqlite3_result_int(ctx, pOp->nExec);
+ sqlite3_result_int64(ctx, pOp->nExec);
break;
case 10: /* ncycle */
- sqlite3_result_int(ctx, pOp->nCycle);
+ sqlite3_result_int64(ctx, pOp->nCycle);
break;
#else
case 9: /* nexec */
@@ -106520,7 +107001,7 @@ static int lookupName(
Parse *pParse, /* The parsing context */
const char *zDb, /* Name of the database containing table, or NULL */
const char *zTab, /* Name of table containing column, or NULL */
- const char *zCol, /* Name of the column. */
+ const Expr *pRight, /* Name of the column. */
NameContext *pNC, /* The name context used to resolve the name */
Expr *pExpr /* Make this EXPR node point to the selected column */
){
@@ -106537,6 +107018,7 @@ static int lookupName(
Table *pTab = 0; /* Table holding the row */
Column *pCol; /* A column of pTab */
ExprList *pFJMatch = 0; /* Matches for FULL JOIN .. USING */
+ const char *zCol = pRight->u.zToken;
assert( pNC ); /* the name context cannot be NULL. */
assert( zCol ); /* The Z in X.Y.Z cannot be NULL */
@@ -106768,7 +107250,8 @@ static int lookupName(
if( pParse->bReturning ){
if( (pNC->ncFlags & NC_UBaseReg)!=0
&& ALWAYS(zTab==0
- || sqlite3StrICmp(zTab,pParse->pTriggerTab->zName)==0)
+ || sqlite3StrICmp(zTab,pParse->pTriggerTab->zName)==0
+ || isValidSchemaTableName(zTab, pParse->pTriggerTab, 0))
){
pExpr->iTable = op!=TK_DELETE;
pTab = pParse->pTriggerTab;
@@ -106872,6 +107355,11 @@ static int lookupName(
&& ALWAYS(VisibleRowid(pMatch->pTab) || pMatch->fg.isNestedFrom)
){
cnt = cntTab;
+#if SQLITE_ALLOW_ROWID_IN_VIEW+0==2
+ if( pMatch->pTab!=0 && IsView(pMatch->pTab) ){
+ eNewExprOp = TK_NULL;
+ }
+#endif
if( pMatch->fg.isNestedFrom==0 ) pExpr->iColumn = -1;
pExpr->affExpr = SQLITE_AFF_INTEGER;
}
@@ -107025,6 +107513,10 @@ static int lookupName(
sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol);
}else if( zTab ){
sqlite3ErrorMsg(pParse, "%s: %s.%s", zErr, zTab, zCol);
+ }else if( cnt==0 && ExprHasProperty(pRight,EP_DblQuoted) ){
+ sqlite3ErrorMsg(pParse, "%s: \"%s\" - should this be a"
+ " string literal in single-quotes?",
+ zErr, zCol);
}else{
sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol);
}
@@ -107058,8 +107550,12 @@ static int lookupName(
** If a generated column is referenced, set bits for every column
** of the table.
*/
- if( pExpr->iColumn>=0 && cnt==1 && pMatch!=0 ){
- pMatch->colUsed |= sqlite3ExprColUsed(pExpr);
+ if( pMatch ){
+ if( pExpr->iColumn>=0 ){
+ pMatch->colUsed |= sqlite3ExprColUsed(pExpr);
+ }else{
+ pMatch->fg.rowidUsed = 1;
+ }
}
pExpr->op = eNewExprOp;
@@ -107302,7 +107798,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
*/
case TK_ID:
case TK_DOT: {
- const char *zColumn;
const char *zTable;
const char *zDb;
Expr *pRight;
@@ -107311,7 +107806,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
zDb = 0;
zTable = 0;
assert( !ExprHasProperty(pExpr, EP_IntValue) );
- zColumn = pExpr->u.zToken;
+ pRight = pExpr;
}else{
Expr *pLeft = pExpr->pLeft;
testcase( pNC->ncFlags & NC_IdxExpr );
@@ -107330,14 +107825,13 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
}
assert( ExprUseUToken(pLeft) && ExprUseUToken(pRight) );
zTable = pLeft->u.zToken;
- zColumn = pRight->u.zToken;
assert( ExprUseYTab(pExpr) );
if( IN_RENAME_OBJECT ){
sqlite3RenameTokenRemap(pParse, (void*)pExpr, (void*)pRight);
sqlite3RenameTokenRemap(pParse, (void*)&pExpr->y.pTab, (void*)pLeft);
}
}
- return lookupName(pParse, zDb, zTable, zColumn, pNC, pExpr);
+ return lookupName(pParse, zDb, zTable, pRight, pNC, pExpr);
}
/* Resolve function names
@@ -107513,11 +108007,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
#endif
}
}
-#ifndef SQLITE_OMIT_WINDOWFUNC
- else if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ else if( ExprHasProperty(pExpr, EP_WinFunc) || pExpr->pLeft ){
is_agg = 1;
}
-#endif
sqlite3WalkExprList(pWalker, pList);
if( is_agg ){
if( pExpr->pLeft ){
@@ -107587,6 +108079,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
testcase( pNC->ncFlags & NC_PartIdx );
testcase( pNC->ncFlags & NC_IdxExpr );
testcase( pNC->ncFlags & NC_GenCol );
+ assert( pExpr->x.pSelect );
if( pNC->ncFlags & NC_SelfRef ){
notValidImpl(pParse, pNC, "subqueries", pExpr, pExpr);
}else{
@@ -107595,6 +108088,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
assert( pNC->nRef>=nRef );
if( nRef!=pNC->nRef ){
ExprSetProperty(pExpr, EP_VarSelect);
+ pExpr->x.pSelect->selFlags |= SF_Correlated;
}
pNC->ncFlags |= NC_Subquery;
}
@@ -108120,6 +108614,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
if( pOuterNC ) pOuterNC->nNestedSelect++;
for(i=0; i<p->pSrc->nSrc; i++){
SrcItem *pItem = &p->pSrc->a[i];
+ assert( pItem->zName!=0 || pItem->pSelect!=0 );/* Test of tag-20240424-1*/
if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){
int nRef = pOuterNC ? pOuterNC->nRef : 0;
const char *zSavedContext = pParse->zAuthContext;
@@ -109426,11 +109921,12 @@ SQLITE_PRIVATE void sqlite3ExprSetErrorOffset(Expr *pExpr, int iOfst){
** appear to be quoted. If the quotes were of the form "..." (double-quotes)
** then the EP_DblQuoted flag is set on the expression node.
**
-** Special case: If op==TK_INTEGER and pToken points to a string that
-** can be translated into a 32-bit integer, then the token is not
-** stored in u.zToken. Instead, the integer values is written
-** into u.iValue and the EP_IntValue flag is set. No extra storage
+** Special case (tag-20240227-a): If op==TK_INTEGER and pToken points to
+** a string that can be translated into a 32-bit integer, then the token is
+** not stored in u.zToken. Instead, the integer values is written
+** into u.iValue and the EP_IntValue flag is set. No extra storage
** is allocated to hold the integer text and the dequote flag is ignored.
+** See also tag-20240227-b.
*/
SQLITE_PRIVATE Expr *sqlite3ExprAlloc(
sqlite3 *db, /* Handle for sqlite3DbMallocRawNN() */
@@ -109446,7 +109942,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAlloc(
if( pToken ){
if( op!=TK_INTEGER || pToken->z==0
|| sqlite3GetInt32(pToken->z, &iValue)==0 ){
- nExtra = pToken->n+1;
+ nExtra = pToken->n+1; /* tag-20240227-a */
assert( iValue>=0 );
}
}
@@ -109878,6 +110374,7 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n
static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
assert( p!=0 );
assert( db!=0 );
+exprDeleteRestart:
assert( !ExprUseUValue(p) || p->u.iValue>=0 );
assert( !ExprUseYWin(p) || !ExprUseYSub(p) );
assert( !ExprUseYWin(p) || p->y.pWin!=0 || db->mallocFailed );
@@ -109893,7 +110390,6 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
if( !ExprHasProperty(p, (EP_TokenOnly|EP_Leaf)) ){
/* The Expr.x union is never used at the same time as Expr.pRight */
assert( (ExprUseXList(p) && p->x.pList==0) || p->pRight==0 );
- if( p->pLeft && p->op!=TK_SELECT_COLUMN ) sqlite3ExprDeleteNN(db, p->pLeft);
if( p->pRight ){
assert( !ExprHasProperty(p, EP_WinFunc) );
sqlite3ExprDeleteNN(db, p->pRight);
@@ -109908,6 +110404,19 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
}
#endif
}
+ if( p->pLeft && p->op!=TK_SELECT_COLUMN ){
+ Expr *pLeft = p->pLeft;
+ if( !ExprHasProperty(p, EP_Static)
+ && !ExprHasProperty(pLeft, EP_Static)
+ ){
+ /* Avoid unnecessary recursion on unary operators */
+ sqlite3DbNNFreeNN(db, p);
+ p = pLeft;
+ goto exprDeleteRestart;
+ }else{
+ sqlite3ExprDeleteNN(db, pLeft);
+ }
+ }
}
if( !ExprHasProperty(p, EP_Static) ){
sqlite3DbNNFreeNN(db, p);
@@ -109940,11 +110449,11 @@ SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){
**
** The pExpr might be deleted immediately on an OOM error.
**
-** The deferred delete is (currently) implemented by adding the
-** pExpr to the pParse->pConstExpr list with a register number of 0.
+** Return 0 if the delete was successfully deferred. Return non-zero
+** if the delete happened immediately because of an OOM.
*/
-SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){
- sqlite3ParserAddCleanup(pParse, sqlite3ExprDeleteGeneric, pExpr);
+SQLITE_PRIVATE int sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){
+ return 0==sqlite3ParserAddCleanup(pParse, sqlite3ExprDeleteGeneric, pExpr);
}
/* Invoke sqlite3RenameExprUnmap() and sqlite3ExprDelete() on the
@@ -110380,17 +110889,19 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int fla
pNewItem->iCursor = pOldItem->iCursor;
pNewItem->addrFillSub = pOldItem->addrFillSub;
pNewItem->regReturn = pOldItem->regReturn;
+ pNewItem->regResult = pOldItem->regResult;
if( pNewItem->fg.isIndexedBy ){
pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy);
+ }else if( pNewItem->fg.isTabFunc ){
+ pNewItem->u1.pFuncArg =
+ sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags);
+ }else{
+ pNewItem->u1.nRow = pOldItem->u1.nRow;
}
pNewItem->u2 = pOldItem->u2;
if( pNewItem->fg.isCte ){
pNewItem->u2.pCteUse->nUse++;
}
- if( pNewItem->fg.isTabFunc ){
- pNewItem->u1.pFuncArg =
- sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags);
- }
pTab = pNewItem->pTab = pOldItem->pTab;
if( pTab ){
pTab->nTabRef++;
@@ -110856,6 +111367,54 @@ SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr *pExpr){
return pExpr;
}
+/*
+** pExpr is a TK_FUNCTION node. Try to determine whether or not the
+** function is a constant function. A function is constant if all of
+** the following are true:
+**
+** (1) It is a scalar function (not an aggregate or window function)
+** (2) It has either the SQLITE_FUNC_CONSTANT or SQLITE_FUNC_SLOCHNG
+** property.
+** (3) All of its arguments are constants
+**
+** This routine sets pWalker->eCode to 0 if pExpr is not a constant.
+** It makes no changes to pWalker->eCode if pExpr is constant. In
+** every case, it returns WRC_Abort.
+**
+** Called as a service subroutine from exprNodeIsConstant().
+*/
+static SQLITE_NOINLINE int exprNodeIsConstantFunction(
+ Walker *pWalker,
+ Expr *pExpr
+){
+ int n; /* Number of arguments */
+ ExprList *pList; /* List of arguments */
+ FuncDef *pDef; /* The function */
+ sqlite3 *db; /* The database */
+
+ assert( pExpr->op==TK_FUNCTION );
+ if( ExprHasProperty(pExpr, EP_TokenOnly)
+ || (pList = pExpr->x.pList)==0
+ ){;
+ n = 0;
+ }else{
+ n = pList->nExpr;
+ sqlite3WalkExprList(pWalker, pList);
+ if( pWalker->eCode==0 ) return WRC_Abort;
+ }
+ db = pWalker->pParse->db;
+ pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0);
+ if( pDef==0
+ || pDef->xFinalize!=0
+ || (pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0
+ || ExprHasProperty(pExpr, EP_WinFunc)
+ ){
+ pWalker->eCode = 0;
+ return WRC_Abort;
+ }
+ return WRC_Prune;
+}
+
/*
** These routines are Walker callbacks used to check expressions to
@@ -110884,6 +111443,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr *pExpr){
** malformed schema error.
*/
static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
+ assert( pWalker->eCode>0 );
/* If pWalker->eCode is 2 then any term of the expression that comes from
** the ON or USING clauses of an outer join disqualifies the expression
@@ -110903,6 +111463,8 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
){
if( pWalker->eCode==5 ) ExprSetProperty(pExpr, EP_FromDDL);
return WRC_Continue;
+ }else if( pWalker->pParse ){
+ return exprNodeIsConstantFunction(pWalker, pExpr);
}else{
pWalker->eCode = 0;
return WRC_Abort;
@@ -110931,9 +111493,11 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
case TK_IF_NULL_ROW:
case TK_REGISTER:
case TK_DOT:
+ case TK_RAISE:
testcase( pExpr->op==TK_REGISTER );
testcase( pExpr->op==TK_IF_NULL_ROW );
testcase( pExpr->op==TK_DOT );
+ testcase( pExpr->op==TK_RAISE );
pWalker->eCode = 0;
return WRC_Abort;
case TK_VARIABLE:
@@ -110955,15 +111519,15 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
return WRC_Continue;
}
}
-static int exprIsConst(Expr *p, int initFlag, int iCur){
+static int exprIsConst(Parse *pParse, Expr *p, int initFlag){
Walker w;
w.eCode = initFlag;
+ w.pParse = pParse;
w.xExprCallback = exprNodeIsConstant;
w.xSelectCallback = sqlite3SelectWalkFail;
#ifdef SQLITE_DEBUG
w.xSelectCallback2 = sqlite3SelectWalkAssert2;
#endif
- w.u.iCur = iCur;
sqlite3WalkExpr(&w, p);
return w.eCode;
}
@@ -110975,9 +111539,15 @@ static int exprIsConst(Expr *p, int initFlag, int iCur){
** For the purposes of this function, a double-quoted string (ex: "abc")
** is considered a variable but a single-quoted string (ex: 'abc') is
** a constant.
+**
+** The pParse parameter may be NULL. But if it is NULL, there is no way
+** to determine if function calls are constant or not, and hence all
+** function calls will be considered to be non-constant. If pParse is
+** not NULL, then a function call might be constant, depending on the
+** function and on its parameters.
*/
-SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr *p){
- return exprIsConst(p, 1, 0);
+SQLITE_PRIVATE int sqlite3ExprIsConstant(Parse *pParse, Expr *p){
+ return exprIsConst(pParse, p, 1);
}
/*
@@ -110993,8 +111563,24 @@ SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr *p){
** can be added to the pParse->pConstExpr list and evaluated once when
** the prepared statement starts up. See sqlite3ExprCodeRunJustOnce().
*/
-SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr *p){
- return exprIsConst(p, 2, 0);
+static int sqlite3ExprIsConstantNotJoin(Parse *pParse, Expr *p){
+ return exprIsConst(pParse, p, 2);
+}
+
+/*
+** This routine examines sub-SELECT statements as an expression is being
+** walked as part of sqlite3ExprIsTableConstant(). Sub-SELECTs are considered
+** constant as long as they are uncorrelated - meaning that they do not
+** contain any terms from outer contexts.
+*/
+static int exprSelectWalkTableConstant(Walker *pWalker, Select *pSelect){
+ assert( pSelect!=0 );
+ assert( pWalker->eCode==3 || pWalker->eCode==0 );
+ if( (pSelect->selFlags & SF_Correlated)!=0 ){
+ pWalker->eCode = 0;
+ return WRC_Abort;
+ }
+ return WRC_Prune;
}
/*
@@ -111002,9 +111588,26 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr *p){
** for any single row of the table with cursor iCur. In other words, the
** expression must not refer to any non-deterministic function nor any
** table other than iCur.
+**
+** Consider uncorrelated subqueries to be constants if the bAllowSubq
+** parameter is true.
*/
-SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){
- return exprIsConst(p, 3, iCur);
+static int sqlite3ExprIsTableConstant(Expr *p, int iCur, int bAllowSubq){
+ Walker w;
+ w.eCode = 3;
+ w.pParse = 0;
+ w.xExprCallback = exprNodeIsConstant;
+ if( bAllowSubq ){
+ w.xSelectCallback = exprSelectWalkTableConstant;
+ }else{
+ w.xSelectCallback = sqlite3SelectWalkFail;
+#ifdef SQLITE_DEBUG
+ w.xSelectCallback2 = sqlite3SelectWalkAssert2;
+#endif
+ }
+ w.u.iCur = iCur;
+ sqlite3WalkExpr(&w, p);
+ return w.eCode;
}
/*
@@ -111022,7 +111625,10 @@ SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){
**
** (1) pExpr cannot refer to any table other than pSrc->iCursor.
**
-** (2) pExpr cannot use subqueries or non-deterministic functions.
+** (2a) pExpr cannot use subqueries unless the bAllowSubq parameter is
+** true and the subquery is non-correlated
+**
+** (2b) pExpr cannot use non-deterministic functions.
**
** (3) pSrc cannot be part of the left operand for a RIGHT JOIN.
** (Is there some way to relax this constraint?)
@@ -111051,7 +111657,8 @@ SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){
SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(
Expr *pExpr, /* The constraint */
const SrcList *pSrcList, /* Complete FROM clause */
- int iSrc /* Which element of pSrcList to use */
+ int iSrc, /* Which element of pSrcList to use */
+ int bAllowSubq /* Allow non-correlated subqueries */
){
const SrcItem *pSrc = &pSrcList->a[iSrc];
if( pSrc->fg.jointype & JT_LTORJ ){
@@ -111076,7 +111683,8 @@ SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(
}
}
}
- return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor); /* rules (1), (2) */
+ /* Rules (1), (2a), and (2b) handled by the following: */
+ return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor, bAllowSubq);
}
@@ -111161,7 +111769,7 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse *pParse, Expr *p, ExprLi
*/
SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr *p, u8 isInit){
assert( isInit==0 || isInit==1 );
- return exprIsConst(p, 4+isInit, 0);
+ return exprIsConst(0, p, 4+isInit);
}
#ifdef SQLITE_ENABLE_CURSOR_HINTS
@@ -111409,13 +112017,13 @@ static void sqlite3SetHasNullFlag(Vdbe *v, int iCur, int regHasNull){
** The argument is an IN operator with a list (not a subquery) on the
** right-hand side. Return TRUE if that list is constant.
*/
-static int sqlite3InRhsIsConstant(Expr *pIn){
+static int sqlite3InRhsIsConstant(Parse *pParse, Expr *pIn){
Expr *pLHS;
int res;
assert( !ExprHasProperty(pIn, EP_xIsSelect) );
pLHS = pIn->pLeft;
pIn->pLeft = 0;
- res = sqlite3ExprIsConstant(pIn);
+ res = sqlite3ExprIsConstant(pParse, pIn);
pIn->pLeft = pLHS;
return res;
}
@@ -111684,7 +112292,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex(
if( eType==0
&& (inFlags & IN_INDEX_NOOP_OK)
&& ExprUseXList(pX)
- && (!sqlite3InRhsIsConstant(pX) || pX->x.pList->nExpr<=2)
+ && (!sqlite3InRhsIsConstant(pParse,pX) || pX->x.pList->nExpr<=2)
){
pParse->nTab--; /* Back out the allocation of the unused cursor */
iTab = -1; /* Cursor is not allocated */
@@ -111967,7 +112575,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
** this code only executes once. Because for a non-constant
** expression we need to rerun this code each time.
*/
- if( addrOnce && !sqlite3ExprIsConstant(pE2) ){
+ if( addrOnce && !sqlite3ExprIsConstant(pParse, pE2) ){
sqlite3VdbeChangeToNoop(v, addrOnce-1);
sqlite3VdbeChangeToNoop(v, addrOnce);
ExprClearProperty(pExpr, EP_Subrtn);
@@ -113131,12 +113739,6 @@ expr_code_doover:
assert( pExpr->u.zToken!=0 );
assert( pExpr->u.zToken[0]!=0 );
sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iColumn, target);
- if( pExpr->u.zToken[1]!=0 ){
- const char *z = sqlite3VListNumToName(pParse->pVList, pExpr->iColumn);
- assert( pExpr->u.zToken[0]=='?' || (z && !strcmp(pExpr->u.zToken, z)) );
- pParse->pVList[0] = 0; /* Indicate VList may no longer be enlarged */
- sqlite3VdbeAppendP4(v, (char*)z, P4_STATIC);
- }
return target;
}
case TK_REGISTER: {
@@ -113310,7 +113912,9 @@ expr_code_doover:
}
#endif
- if( ConstFactorOk(pParse) && sqlite3ExprIsConstantNotJoin(pExpr) ){
+ if( ConstFactorOk(pParse)
+ && sqlite3ExprIsConstantNotJoin(pParse,pExpr)
+ ){
/* SQL functions can be expensive. So try to avoid running them
** multiple times if we know they always give the same result */
return sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1);
@@ -113341,7 +113945,7 @@ expr_code_doover:
}
for(i=0; i<nFarg; i++){
- if( i<32 && sqlite3ExprIsConstant(pFarg->a[i].pExpr) ){
+ if( i<32 && sqlite3ExprIsConstant(pParse, pFarg->a[i].pExpr) ){
testcase( i==31 );
constMask |= MASKBIT32(i);
}
@@ -113483,8 +114087,9 @@ expr_code_doover:
if( !ExprHasProperty(pExpr, EP_Collate) ){
/* A TK_COLLATE Expr node without the EP_Collate tag is a so-called
** "SOFT-COLLATE" that is added to constraints that are pushed down
- ** from outer queries into sub-queries by the push-down optimization.
- ** Clear subtypes as subtypes may not cross a subquery boundary.
+ ** from outer queries into sub-queries by the WHERE-clause push-down
+ ** optimization. Clear subtypes as subtypes may not cross a subquery
+ ** boundary.
*/
assert( pExpr->pLeft );
sqlite3ExprCode(pParse, pExpr->pLeft, target);
@@ -113808,7 +114413,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){
if( ConstFactorOk(pParse)
&& ALWAYS(pExpr!=0)
&& pExpr->op!=TK_REGISTER
- && sqlite3ExprIsConstantNotJoin(pExpr)
+ && sqlite3ExprIsConstantNotJoin(pParse, pExpr)
){
*pReg = 0;
r2 = sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1);
@@ -113872,7 +114477,7 @@ SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse *pParse, Expr *pExpr, int target){
** might choose to code the expression at initialization time.
*/
SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse *pParse, Expr *pExpr, int target){
- if( pParse->okConstFactor && sqlite3ExprIsConstantNotJoin(pExpr) ){
+ if( pParse->okConstFactor && sqlite3ExprIsConstantNotJoin(pParse,pExpr) ){
sqlite3ExprCodeRunJustOnce(pParse, pExpr, target);
}else{
sqlite3ExprCodeCopy(pParse, pExpr, target);
@@ -113931,7 +114536,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeExprList(
sqlite3VdbeAddOp2(v, copyOp, j+srcReg-1, target+i);
}
}else if( (flags & SQLITE_ECEL_FACTOR)!=0
- && sqlite3ExprIsConstantNotJoin(pExpr)
+ && sqlite3ExprIsConstantNotJoin(pParse,pExpr)
){
sqlite3ExprCodeRunJustOnce(pParse, pExpr, target+i);
}else{
@@ -115082,9 +115687,8 @@ static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){
&& pAggInfo->aCol[iAgg].pCExpr==pExpr
){
pExpr = sqlite3ExprDup(db, pExpr, 0);
- if( pExpr ){
+ if( pExpr && !sqlite3ExprDeferredDelete(pParse, pExpr) ){
pAggInfo->aCol[iAgg].pCExpr = pExpr;
- sqlite3ExprDeferredDelete(pParse, pExpr);
}
}
}else{
@@ -115093,9 +115697,8 @@ static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){
&& pAggInfo->aFunc[iAgg].pFExpr==pExpr
){
pExpr = sqlite3ExprDup(db, pExpr, 0);
- if( pExpr ){
+ if( pExpr && !sqlite3ExprDeferredDelete(pParse, pExpr) ){
pAggInfo->aFunc[iAgg].pFExpr = pExpr;
- sqlite3ExprDeferredDelete(pParse, pExpr);
}
}
}
@@ -117796,7 +118399,12 @@ SQLITE_PRIVATE void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, const T
if( i==pTab->iPKey ){
sqlite3VdbeAddOp2(v, OP_Null, 0, regOut);
}else{
+ char aff = pTab->aCol[i].affinity;
+ if( aff==SQLITE_AFF_REAL ){
+ pTab->aCol[i].affinity = SQLITE_AFF_NUMERIC;
+ }
sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOut);
+ pTab->aCol[i].affinity = aff;
}
nField++;
}
@@ -118715,7 +119323,7 @@ static void statGet(
if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1;
sqlite3_str_appendf(&sStat, " %llu", iVal);
#ifdef SQLITE_ENABLE_STAT4
- assert( p->current.anEq[i] );
+ assert( p->current.anEq[i] || p->nRow==0 );
#endif
}
sqlite3ResultStrAccum(context, &sStat);
@@ -118900,7 +119508,7 @@ static void analyzeOneTable(
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int nCol; /* Number of columns in pIdx. "N" */
- int addrRewind; /* Address of "OP_Rewind iIdxCur" */
+ int addrGotoEnd; /* Address of "OP_Rewind iIdxCur" */
int addrNextRow; /* Address of "next_row:" */
const char *zIdxName; /* Name of the index */
int nColTest; /* Number of columns to test for changes */
@@ -118924,9 +119532,14 @@ static void analyzeOneTable(
/*
** Pseudo-code for loop that calls stat_push():
**
- ** Rewind csr
- ** if eof(csr) goto end_of_scan;
** regChng = 0
+ ** Rewind csr
+ ** if eof(csr){
+ ** stat_init() with count = 0;
+ ** goto end_of_scan;
+ ** }
+ ** count()
+ ** stat_init()
** goto chng_addr_0;
**
** next_row:
@@ -118965,41 +119578,36 @@ static void analyzeOneTable(
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
VdbeComment((v, "%s", pIdx->zName));
- /* Invoke the stat_init() function. The arguments are:
+ /* Implementation of the following:
**
+ ** regChng = 0
+ ** Rewind csr
+ ** if eof(csr){
+ ** stat_init() with count = 0;
+ ** goto end_of_scan;
+ ** }
+ ** count()
+ ** stat_init()
+ ** goto chng_addr_0;
+ */
+ assert( regTemp2==regStat+4 );
+ sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2);
+
+ /* Arguments to stat_init():
** (1) the number of columns in the index including the rowid
** (or for a WITHOUT ROWID table, the number of PK columns),
** (2) the number of columns in the key without the rowid/pk
- ** (3) estimated number of rows in the index,
- */
+ ** (3) estimated number of rows in the index. */
sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat+1);
assert( regRowid==regStat+2 );
sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regRowid);
-#ifdef SQLITE_ENABLE_STAT4
- if( OptimizationEnabled(db, SQLITE_Stat4) ){
- sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regTemp);
- addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
- VdbeCoverage(v);
- }else
-#endif
- {
- addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
- VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, 1);
- }
- assert( regTemp2==regStat+4 );
- sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2);
+ sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp,
+ OptimizationDisabled(db, SQLITE_Stat4));
sqlite3VdbeAddFunctionCall(pParse, 0, regStat+1, regStat, 4,
&statInitFuncdef, 0);
+ addrGotoEnd = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
+ VdbeCoverage(v);
- /* Implementation of the following:
- **
- ** Rewind csr
- ** if eof(csr) goto end_of_scan;
- ** regChng = 0
- ** goto next_push_0;
- **
- */
sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng);
addrNextRow = sqlite3VdbeCurrentAddr(v);
@@ -119106,6 +119714,12 @@ static void analyzeOneTable(
}
/* Add the entry to the stat1 table. */
+ if( pIdx->pPartIdxWhere ){
+ /* Partial indexes might get a zero-entry in sqlite_stat1. But
+ ** an empty table is omitted from sqlite_stat1. */
+ sqlite3VdbeJumpHere(v, addrGotoEnd);
+ addrGotoEnd = 0;
+ }
callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1);
assert( "BBB"[0]==SQLITE_AFF_TEXT );
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0);
@@ -119129,6 +119743,13 @@ static void analyzeOneTable(
int addrIsNull;
u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
+ /* No STAT4 data is generated if the number of rows is zero */
+ if( addrGotoEnd==0 ){
+ sqlite3VdbeAddOp2(v, OP_Cast, regStat1, SQLITE_AFF_INTEGER);
+ addrGotoEnd = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1);
+ VdbeCoverage(v);
+ }
+
if( doOnce ){
int mxCol = nCol;
Index *pX;
@@ -119181,7 +119802,7 @@ static void analyzeOneTable(
#endif /* SQLITE_ENABLE_STAT4 */
/* End of analysis */
- sqlite3VdbeJumpHere(v, addrRewind);
+ if( addrGotoEnd ) sqlite3VdbeJumpHere(v, addrGotoEnd);
}
@@ -120930,7 +121551,7 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
}
sqlite3VdbeAddOp0(v, OP_Halt);
-#if SQLITE_USER_AUTHENTICATION
+#if SQLITE_USER_AUTHENTICATION && !defined(SQLITE_OMIT_SHARED_CACHE)
if( pParse->nTableLock>0 && db->init.busy==0 ){
sqlite3UserAuthInit(db);
if( db->auth.authLevel<UAUTH_User ){
@@ -123569,20 +124190,20 @@ SQLITE_PRIVATE void sqlite3EndTable(
int regRowid; /* Rowid of the next row to insert */
int addrInsLoop; /* Top of the loop for inserting rows */
Table *pSelTab; /* A table that describes the SELECT results */
+ int iCsr; /* Write cursor on the new table */
if( IN_SPECIAL_PARSE ){
pParse->rc = SQLITE_ERROR;
pParse->nErr++;
return;
}
+ iCsr = pParse->nTab++;
regYield = ++pParse->nMem;
regRec = ++pParse->nMem;
regRowid = ++pParse->nMem;
- assert(pParse->nTab==1);
sqlite3MayAbort(pParse);
- sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse->regRoot, iDb);
+ sqlite3VdbeAddOp3(v, OP_OpenWrite, iCsr, pParse->regRoot, iDb);
sqlite3VdbeChangeP5(v, OPFLAG_P2ISREG);
- pParse->nTab = 2;
addrTop = sqlite3VdbeCurrentAddr(v) + 1;
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
if( pParse->nErr ) return;
@@ -123603,11 +124224,11 @@ SQLITE_PRIVATE void sqlite3EndTable(
VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_MakeRecord, dest.iSdst, dest.nSdst, regRec);
sqlite3TableAffinity(v, p, 0);
- sqlite3VdbeAddOp2(v, OP_NewRowid, 1, regRowid);
- sqlite3VdbeAddOp3(v, OP_Insert, 1, regRec, regRowid);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, iCsr, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, iCsr, regRec, regRowid);
sqlite3VdbeGoto(v, addrInsLoop);
sqlite3VdbeJumpHere(v, addrInsLoop);
- sqlite3VdbeAddOp1(v, OP_Close, 1);
+ sqlite3VdbeAddOp1(v, OP_Close, iCsr);
}
/* Compute the complete text of the CREATE statement */
@@ -123664,13 +124285,10 @@ SQLITE_PRIVATE void sqlite3EndTable(
/* Test for cycles in generated columns and illegal expressions
** in CHECK constraints and in DEFAULT clauses. */
if( p->tabFlags & TF_HasGenerated ){
- sqlite3VdbeAddOp4(v, OP_SqlExec, 1, 0, 0,
+ sqlite3VdbeAddOp4(v, OP_SqlExec, 0x0001, 0, 0,
sqlite3MPrintf(db, "SELECT*FROM\"%w\".\"%w\"",
db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC);
}
- sqlite3VdbeAddOp4(v, OP_SqlExec, 1, 0, 0,
- sqlite3MPrintf(db, "PRAGMA \"%w\".integrity_check(%Q)",
- db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC);
}
/* Add the table to the in-memory representation of the database.
@@ -132844,6 +133462,195 @@ SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse){
# define autoIncStep(A,B,C)
#endif /* SQLITE_OMIT_AUTOINCREMENT */
+/*
+** If argument pVal is a Select object returned by an sqlite3MultiValues()
+** that was able to use the co-routine optimization, finish coding the
+** co-routine.
+*/
+SQLITE_PRIVATE void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal){
+ if( ALWAYS(pVal) && pVal->pSrc->nSrc>0 ){
+ SrcItem *pItem = &pVal->pSrc->a[0];
+ sqlite3VdbeEndCoroutine(pParse->pVdbe, pItem->regReturn);
+ sqlite3VdbeJumpHere(pParse->pVdbe, pItem->addrFillSub - 1);
+ }
+}
+
+/*
+** Return true if all expressions in the expression-list passed as the
+** only argument are constant.
+*/
+static int exprListIsConstant(Parse *pParse, ExprList *pRow){
+ int ii;
+ for(ii=0; ii<pRow->nExpr; ii++){
+ if( 0==sqlite3ExprIsConstant(pParse, pRow->a[ii].pExpr) ) return 0;
+ }
+ return 1;
+}
+
+/*
+** Return true if all expressions in the expression-list passed as the
+** only argument are both constant and have no affinity.
+*/
+static int exprListIsNoAffinity(Parse *pParse, ExprList *pRow){
+ int ii;
+ if( exprListIsConstant(pParse,pRow)==0 ) return 0;
+ for(ii=0; ii<pRow->nExpr; ii++){
+ Expr *pExpr = pRow->a[ii].pExpr;
+ assert( pExpr->op!=TK_RAISE );
+ assert( pExpr->affExpr==0 );
+ if( 0!=sqlite3ExprAffinity(pExpr) ) return 0;
+ }
+ return 1;
+
+}
+
+/*
+** This function is called by the parser for the second and subsequent
+** rows of a multi-row VALUES clause. Argument pLeft is the part of
+** the VALUES clause already parsed, argument pRow is the vector of values
+** for the new row. The Select object returned represents the complete
+** VALUES clause, including the new row.
+**
+** There are two ways in which this may be achieved - by incremental
+** coding of a co-routine (the "co-routine" method) or by returning a
+** Select object equivalent to the following (the "UNION ALL" method):
+**
+** "pLeft UNION ALL SELECT pRow"
+**
+** If the VALUES clause contains a lot of rows, this compound Select
+** object may consume a lot of memory.
+**
+** When the co-routine method is used, each row that will be returned
+** by the VALUES clause is coded into part of a co-routine as it is
+** passed to this function. The returned Select object is equivalent to:
+**
+** SELECT * FROM (
+** Select object to read co-routine
+** )
+**
+** The co-routine method is used in most cases. Exceptions are:
+**
+** a) If the current statement has a WITH clause. This is to avoid
+** statements like:
+**
+** WITH cte AS ( VALUES('x'), ('y') ... )
+** SELECT * FROM cte AS a, cte AS b;
+**
+** This will not work, as the co-routine uses a hard-coded register
+** for its OP_Yield instructions, and so it is not possible for two
+** cursors to iterate through it concurrently.
+**
+** b) The schema is currently being parsed (i.e. the VALUES clause is part
+** of a schema item like a VIEW or TRIGGER). In this case there is no VM
+** being generated when parsing is taking place, and so generating
+** a co-routine is not possible.
+**
+** c) There are non-constant expressions in the VALUES clause (e.g.
+** the VALUES clause is part of a correlated sub-query).
+**
+** d) One or more of the values in the first row of the VALUES clause
+** has an affinity (i.e. is a CAST expression). This causes problems
+** because the complex rules SQLite uses (see function
+** sqlite3SubqueryColumnTypes() in select.c) to determine the effective
+** affinity of such a column for all rows require access to all values in
+** the column simultaneously.
+*/
+SQLITE_PRIVATE Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow){
+
+ if( pParse->bHasWith /* condition (a) above */
+ || pParse->db->init.busy /* condition (b) above */
+ || exprListIsConstant(pParse,pRow)==0 /* condition (c) above */
+ || (pLeft->pSrc->nSrc==0 &&
+ exprListIsNoAffinity(pParse,pLeft->pEList)==0) /* condition (d) above */
+ || IN_SPECIAL_PARSE
+ ){
+ /* The co-routine method cannot be used. Fall back to UNION ALL. */
+ Select *pSelect = 0;
+ int f = SF_Values | SF_MultiValue;
+ if( pLeft->pSrc->nSrc ){
+ sqlite3MultiValuesEnd(pParse, pLeft);
+ f = SF_Values;
+ }else if( pLeft->pPrior ){
+ /* In this case set the SF_MultiValue flag only if it was set on pLeft */
+ f = (f & pLeft->selFlags);
+ }
+ pSelect = sqlite3SelectNew(pParse, pRow, 0, 0, 0, 0, 0, f, 0);
+ pLeft->selFlags &= ~SF_MultiValue;
+ if( pSelect ){
+ pSelect->op = TK_ALL;
+ pSelect->pPrior = pLeft;
+ pLeft = pSelect;
+ }
+ }else{
+ SrcItem *p = 0; /* SrcItem that reads from co-routine */
+
+ if( pLeft->pSrc->nSrc==0 ){
+ /* Co-routine has not yet been started and the special Select object
+ ** that accesses the co-routine has not yet been created. This block
+ ** does both those things. */
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ Select *pRet = sqlite3SelectNew(pParse, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ /* Ensure the database schema has been read. This is to ensure we have
+ ** the correct text encoding. */
+ if( (pParse->db->mDbFlags & DBFLAG_SchemaKnownOk)==0 ){
+ sqlite3ReadSchema(pParse);
+ }
+
+ if( pRet ){
+ SelectDest dest;
+ pRet->pSrc->nSrc = 1;
+ pRet->pPrior = pLeft->pPrior;
+ pRet->op = pLeft->op;
+ pLeft->pPrior = 0;
+ pLeft->op = TK_SELECT;
+ assert( pLeft->pNext==0 );
+ assert( pRet->pNext==0 );
+ p = &pRet->pSrc->a[0];
+ p->pSelect = pLeft;
+ p->fg.viaCoroutine = 1;
+ p->addrFillSub = sqlite3VdbeCurrentAddr(v) + 1;
+ p->regReturn = ++pParse->nMem;
+ p->iCursor = -1;
+ p->u1.nRow = 2;
+ sqlite3VdbeAddOp3(v,OP_InitCoroutine,p->regReturn,0,p->addrFillSub);
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, p->regReturn);
+
+ /* Allocate registers for the output of the co-routine. Do so so
+ ** that there are two unused registers immediately before those
+ ** used by the co-routine. This allows the code in sqlite3Insert()
+ ** to use these registers directly, instead of copying the output
+ ** of the co-routine to a separate array for processing. */
+ dest.iSdst = pParse->nMem + 3;
+ dest.nSdst = pLeft->pEList->nExpr;
+ pParse->nMem += 2 + dest.nSdst;
+
+ pLeft->selFlags |= SF_MultiValue;
+ sqlite3Select(pParse, pLeft, &dest);
+ p->regResult = dest.iSdst;
+ assert( pParse->nErr || dest.iSdst>0 );
+ pLeft = pRet;
+ }
+ }else{
+ p = &pLeft->pSrc->a[0];
+ assert( !p->fg.isTabFunc && !p->fg.isIndexedBy );
+ p->u1.nRow++;
+ }
+
+ if( pParse->nErr==0 ){
+ assert( p!=0 );
+ if( p->pSelect->pEList->nExpr!=pRow->nExpr ){
+ sqlite3SelectWrongNumTermsError(pParse, p->pSelect);
+ }else{
+ sqlite3ExprCodeExprList(pParse, pRow, p->regResult, 0, 0);
+ sqlite3VdbeAddOp1(pParse->pVdbe, OP_Yield, p->regReturn);
+ }
+ }
+ sqlite3ExprListDelete(pParse->db, pRow);
+ }
+
+ return pLeft;
+}
/* Forward declaration */
static int xferOptimization(
@@ -133180,25 +133987,40 @@ SQLITE_PRIVATE void sqlite3Insert(
if( pSelect ){
/* Data is coming from a SELECT or from a multi-row VALUES clause.
** Generate a co-routine to run the SELECT. */
- int regYield; /* Register holding co-routine entry-point */
- int addrTop; /* Top of the co-routine */
int rc; /* Result code */
- regYield = ++pParse->nMem;
- addrTop = sqlite3VdbeCurrentAddr(v) + 1;
- sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
- sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
- dest.iSdst = bIdListInOrder ? regData : 0;
- dest.nSdst = pTab->nCol;
- rc = sqlite3Select(pParse, pSelect, &dest);
- regFromSelect = dest.iSdst;
- assert( db->pParse==pParse );
- if( rc || pParse->nErr ) goto insert_cleanup;
- assert( db->mallocFailed==0 );
- sqlite3VdbeEndCoroutine(v, regYield);
- sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */
- assert( pSelect->pEList );
- nColumn = pSelect->pEList->nExpr;
+ if( pSelect->pSrc->nSrc==1
+ && pSelect->pSrc->a[0].fg.viaCoroutine
+ && pSelect->pPrior==0
+ ){
+ SrcItem *pItem = &pSelect->pSrc->a[0];
+ dest.iSDParm = pItem->regReturn;
+ regFromSelect = pItem->regResult;
+ nColumn = pItem->pSelect->pEList->nExpr;
+ ExplainQueryPlan((pParse, 0, "SCAN %S", pItem));
+ if( bIdListInOrder && nColumn==pTab->nCol ){
+ regData = regFromSelect;
+ regRowid = regData - 1;
+ regIns = regRowid - (IsVirtual(pTab) ? 1 : 0);
+ }
+ }else{
+ int addrTop; /* Top of the co-routine */
+ int regYield = ++pParse->nMem;
+ addrTop = sqlite3VdbeCurrentAddr(v) + 1;
+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
+ dest.iSdst = bIdListInOrder ? regData : 0;
+ dest.nSdst = pTab->nCol;
+ rc = sqlite3Select(pParse, pSelect, &dest);
+ regFromSelect = dest.iSdst;
+ assert( db->pParse==pParse );
+ if( rc || pParse->nErr ) goto insert_cleanup;
+ assert( db->mallocFailed==0 );
+ sqlite3VdbeEndCoroutine(v, regYield);
+ sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */
+ assert( pSelect->pEList );
+ nColumn = pSelect->pEList->nExpr;
+ }
/* Set useTempTable to TRUE if the result of the SELECT statement
** should be written into a temporary table (template 4). Set to
@@ -137924,6 +138746,34 @@ static const PragmaName aPragmaName[] = {
/************** Continuing where we left off in pragma.c *********************/
/*
+** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands
+** will be run with an analysis_limit set to the lessor of the value of
+** the following macro or to the actual analysis_limit if it is non-zero,
+** in order to prevent PRAGMA optimize from running for too long.
+**
+** The value of 2000 is chosen emperically so that the worst-case run-time
+** for PRAGMA optimize does not exceed 100 milliseconds against a variety
+** of test databases on a RaspberryPI-4 compiled using -Os and without
+** -DSQLITE_DEBUG. Of course, your mileage may vary. For the purpose of
+** this paragraph, "worst-case" means that ANALYZE ends up being
+** run on every table in the database. The worst case typically only
+** happens if PRAGMA optimize is run on a database file for which ANALYZE
+** has not been previously run and the 0x10000 flag is included so that
+** all tables are analyzed. The usual case for PRAGMA optimize is that
+** no ANALYZE commands will be run at all, or if any ANALYZE happens it
+** will be against a single table, so that expected timing for PRAGMA
+** optimize on a PI-4 is more like 1 millisecond or less with the 0x10000
+** flag or less than 100 microseconds without the 0x10000 flag.
+**
+** An analysis limit of 2000 is almost always sufficient for the query
+** planner to fully characterize an index. The additional accuracy from
+** a larger analysis is not usually helpful.
+*/
+#ifndef SQLITE_DEFAULT_OPTIMIZE_LIMIT
+# define SQLITE_DEFAULT_OPTIMIZE_LIMIT 2000
+#endif
+
+/*
** Interpret the given string as a safety level. Return 0 for OFF,
** 1 for ON or NORMAL, 2 for FULL, and 3 for EXTRA. Return 1 for an empty or
** unrecognized string argument. The FULL and EXTRA option is disallowed
@@ -139568,7 +140418,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
/* Set the maximum error count */
mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
if( zRight ){
- if( sqlite3GetInt32(zRight, &mxErr) ){
+ if( sqlite3GetInt32(pValue->z, &mxErr) ){
if( mxErr<=0 ){
mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
}
@@ -139585,7 +140435,6 @@ SQLITE_PRIVATE void sqlite3Pragma(
Hash *pTbls; /* Set of all tables in the schema */
int *aRoot; /* Array of root page numbers of all btrees */
int cnt = 0; /* Number of entries in aRoot[] */
- int mxIdx = 0; /* Maximum number of indexes for any table */
if( OMIT_TEMPDB && i==1 ) continue;
if( iDb>=0 && i!=iDb ) continue;
@@ -139607,7 +140456,6 @@ SQLITE_PRIVATE void sqlite3Pragma(
if( pObjTab && pObjTab!=pTab ) continue;
if( HasRowid(pTab) ) cnt++;
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; }
- if( nIdx>mxIdx ) mxIdx = nIdx;
}
if( cnt==0 ) continue;
if( pObjTab ) cnt++;
@@ -139627,11 +140475,11 @@ SQLITE_PRIVATE void sqlite3Pragma(
aRoot[0] = cnt;
/* Make sure sufficient number of registers have been allocated */
- sqlite3TouchRegister(pParse, 8+mxIdx);
+ sqlite3TouchRegister(pParse, 8+cnt);
sqlite3ClearTempRegCache(pParse);
/* Do the b-tree integrity checks */
- sqlite3VdbeAddOp4(v, OP_IntegrityCk, 2, cnt, 1, (char*)aRoot,P4_INTARRAY);
+ sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY);
sqlite3VdbeChangeP5(v, (u8)i);
addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v);
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
@@ -139641,6 +140489,36 @@ SQLITE_PRIVATE void sqlite3Pragma(
integrityCheckResultRow(v);
sqlite3VdbeJumpHere(v, addr);
+ /* Check that the indexes all have the right number of rows */
+ cnt = pObjTab ? 1 : 0;
+ sqlite3VdbeLoadString(v, 2, "wrong # of entries in index ");
+ for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
+ int iTab = 0;
+ Table *pTab = sqliteHashData(x);
+ Index *pIdx;
+ if( pObjTab && pObjTab!=pTab ) continue;
+ if( HasRowid(pTab) ){
+ iTab = cnt++;
+ }else{
+ iTab = cnt;
+ for(pIdx=pTab->pIndex; ALWAYS(pIdx); pIdx=pIdx->pNext){
+ if( IsPrimaryKeyIndex(pIdx) ) break;
+ iTab++;
+ }
+ }
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ if( pIdx->pPartIdxWhere==0 ){
+ addr = sqlite3VdbeAddOp3(v, OP_Eq, 8+cnt, 0, 8+iTab);
+ VdbeCoverageNeverNull(v);
+ sqlite3VdbeLoadString(v, 4, pIdx->zName);
+ sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3);
+ integrityCheckResultRow(v);
+ sqlite3VdbeJumpHere(v, addr);
+ }
+ cnt++;
+ }
+ }
+
/* Make sure all the indices are constructed correctly.
*/
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
@@ -139964,21 +140842,9 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, loopTop-1);
- if( !isQuick ){
- sqlite3VdbeLoadString(v, 2, "wrong # of entries in index ");
- for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
- if( pPk==pIdx ) continue;
- sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3);
- addr = sqlite3VdbeAddOp3(v, OP_Eq, 8+j, 0, 3); VdbeCoverage(v);
- sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
- sqlite3VdbeLoadString(v, 4, pIdx->zName);
- sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3);
- integrityCheckResultRow(v);
- sqlite3VdbeJumpHere(v, addr);
- }
- if( pPk ){
- sqlite3ReleaseTempRange(pParse, r2, pPk->nKeyCol);
- }
+ if( pPk ){
+ assert( !isQuick );
+ sqlite3ReleaseTempRange(pParse, r2, pPk->nKeyCol);
}
}
@@ -140276,44 +141142,63 @@ SQLITE_PRIVATE void sqlite3Pragma(
**
** The optional argument is a bitmask of optimizations to perform:
**
- ** 0x0001 Debugging mode. Do not actually perform any optimizations
- ** but instead return one line of text for each optimization
- ** that would have been done. Off by default.
+ ** 0x00001 Debugging mode. Do not actually perform any optimizations
+ ** but instead return one line of text for each optimization
+ ** that would have been done. Off by default.
**
- ** 0x0002 Run ANALYZE on tables that might benefit. On by default.
- ** See below for additional information.
+ ** 0x00002 Run ANALYZE on tables that might benefit. On by default.
+ ** See below for additional information.
**
- ** 0x0004 (Not yet implemented) Record usage and performance
- ** information from the current session in the
- ** database file so that it will be available to "optimize"
- ** pragmas run by future database connections.
+ ** 0x00010 Run all ANALYZE operations using an analysis_limit that
+ ** is the lessor of the current analysis_limit and the
+ ** SQLITE_DEFAULT_OPTIMIZE_LIMIT compile-time option.
+ ** The default value of SQLITE_DEFAULT_OPTIMIZE_LIMIT is
+ ** currently (2024-02-19) set to 2000, which is such that
+ ** the worst case run-time for PRAGMA optimize on a 100MB
+ ** database will usually be less than 100 milliseconds on
+ ** a RaspberryPI-4 class machine. On by default.
**
- ** 0x0008 (Not yet implemented) Create indexes that might have
- ** been helpful to recent queries
+ ** 0x10000 Look at tables to see if they need to be reanalyzed
+ ** due to growth or shrinkage even if they have not been
+ ** queried during the current connection. Off by default.
**
- ** The default MASK is and always shall be 0xfffe. 0xfffe means perform all
- ** of the optimizations listed above except Debug Mode, including new
- ** optimizations that have not yet been invented. If new optimizations are
- ** ever added that should be off by default, those off-by-default
- ** optimizations will have bitmasks of 0x10000 or larger.
+ ** The default MASK is and always shall be 0x0fffe. In the current
+ ** implementation, the default mask only covers the 0x00002 optimization,
+ ** though additional optimizations that are covered by 0x0fffe might be
+ ** added in the future. Optimizations that are off by default and must
+ ** be explicitly requested have masks of 0x10000 or greater.
**
** DETERMINATION OF WHEN TO RUN ANALYZE
**
** In the current implementation, a table is analyzed if only if all of
** the following are true:
**
- ** (1) MASK bit 0x02 is set.
+ ** (1) MASK bit 0x00002 is set.
**
- ** (2) The query planner used sqlite_stat1-style statistics for one or
- ** more indexes of the table at some point during the lifetime of
- ** the current connection.
+ ** (2) The table is an ordinary table, not a virtual table or view.
**
- ** (3) One or more indexes of the table are currently unanalyzed OR
- ** the number of rows in the table has increased by 25 times or more
- ** since the last time ANALYZE was run.
+ ** (3) The table name does not begin with "sqlite_".
+ **
+ ** (4) One or more of the following is true:
+ ** (4a) The 0x10000 MASK bit is set.
+ ** (4b) One or more indexes on the table lacks an entry
+ ** in the sqlite_stat1 table.
+ ** (4c) The query planner used sqlite_stat1-style statistics for one
+ ** or more indexes of the table at some point during the lifetime
+ ** of the current connection.
+ **
+ ** (5) One or more of the following is true:
+ ** (5a) One or more indexes on the table lacks an entry
+ ** in the sqlite_stat1 table. (Same as 4a)
+ ** (5b) The number of rows in the table has increased or decreased by
+ ** 10-fold. In other words, the current size of the table is
+ ** 10 times larger than the size in sqlite_stat1 or else the
+ ** current size is less than 1/10th the size in sqlite_stat1.
**
** The rules for when tables are analyzed are likely to change in
- ** future releases.
+ ** future releases. Future versions of SQLite might accept a string
+ ** literal argument to this pragma that contains a mnemonic description
+ ** of the options rather than a bitmap.
*/
case PragTyp_OPTIMIZE: {
int iDbLast; /* Loop termination point for the schema loop */
@@ -140325,6 +141210,10 @@ SQLITE_PRIVATE void sqlite3Pragma(
LogEst szThreshold; /* Size threshold above which reanalysis needed */
char *zSubSql; /* SQL statement for the OP_SqlExec opcode */
u32 opMask; /* Mask of operations to perform */
+ int nLimit; /* Analysis limit to use */
+ int nCheck = 0; /* Number of tables to be optimized */
+ int nBtree = 0; /* Number of btrees to scan */
+ int nIndex; /* Number of indexes on the current table */
if( zRight ){
opMask = (u32)sqlite3Atoi(zRight);
@@ -140332,6 +141221,14 @@ SQLITE_PRIVATE void sqlite3Pragma(
}else{
opMask = 0xfffe;
}
+ if( (opMask & 0x10)==0 ){
+ nLimit = 0;
+ }else if( db->nAnalysisLimit>0
+ && db->nAnalysisLimit<SQLITE_DEFAULT_OPTIMIZE_LIMIT ){
+ nLimit = 0;
+ }else{
+ nLimit = SQLITE_DEFAULT_OPTIMIZE_LIMIT;
+ }
iTabCur = pParse->nTab++;
for(iDbLast = zDb?iDb:db->nDb-1; iDb<=iDbLast; iDb++){
if( iDb==1 ) continue;
@@ -140340,23 +141237,61 @@ SQLITE_PRIVATE void sqlite3Pragma(
for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
pTab = (Table*)sqliteHashData(k);
- /* If table pTab has not been used in a way that would benefit from
- ** having analysis statistics during the current session, then skip it.
- ** This also has the effect of skipping virtual tables and views */
- if( (pTab->tabFlags & TF_StatsUsed)==0 ) continue;
+ /* This only works for ordinary tables */
+ if( !IsOrdinaryTable(pTab) ) continue;
- /* Reanalyze if the table is 25 times larger than the last analysis */
- szThreshold = pTab->nRowLogEst + 46; assert( sqlite3LogEst(25)==46 );
+ /* Do not scan system tables */
+ if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) ) continue;
+
+ /* Find the size of the table as last recorded in sqlite_stat1.
+ ** If any index is unanalyzed, then the threshold is -1 to
+ ** indicate a new, unanalyzed index
+ */
+ szThreshold = pTab->nRowLogEst;
+ nIndex = 0;
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ nIndex++;
if( !pIdx->hasStat1 ){
- szThreshold = 0; /* Always analyze if any index lacks statistics */
- break;
+ szThreshold = -1; /* Always analyze if any index lacks statistics */
}
}
- if( szThreshold ){
- sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead);
- sqlite3VdbeAddOp3(v, OP_IfSmaller, iTabCur,
- sqlite3VdbeCurrentAddr(v)+2+(opMask&1), szThreshold);
+
+ /* If table pTab has not been used in a way that would benefit from
+ ** having analysis statistics during the current session, then skip it,
+ ** unless the 0x10000 MASK bit is set. */
+ if( (pTab->tabFlags & TF_MaybeReanalyze)!=0 ){
+ /* Check for size change if stat1 has been used for a query */
+ }else if( opMask & 0x10000 ){
+ /* Check for size change if 0x10000 is set */
+ }else if( pTab->pIndex!=0 && szThreshold<0 ){
+ /* Do analysis if unanalyzed indexes exists */
+ }else{
+ /* Otherwise, we can skip this table */
+ continue;
+ }
+
+ nCheck++;
+ if( nCheck==2 ){
+ /* If ANALYZE might be invoked two or more times, hold a write
+ ** transaction for efficiency */
+ sqlite3BeginWriteOperation(pParse, 0, iDb);
+ }
+ nBtree += nIndex+1;
+
+ /* Reanalyze if the table is 10 times larger or smaller than
+ ** the last analysis. Unconditional reanalysis if there are
+ ** unanalyzed indexes. */
+ sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead);
+ if( szThreshold>=0 ){
+ const LogEst iRange = 33; /* 10x size change */
+ sqlite3VdbeAddOp4Int(v, OP_IfSizeBetween, iTabCur,
+ sqlite3VdbeCurrentAddr(v)+2+(opMask&1),
+ szThreshold>=iRange ? szThreshold-iRange : -1,
+ szThreshold+iRange);
+ VdbeCoverage(v);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Rewind, iTabCur,
+ sqlite3VdbeCurrentAddr(v)+2+(opMask&1));
VdbeCoverage(v);
}
zSubSql = sqlite3MPrintf(db, "ANALYZE \"%w\".\"%w\"",
@@ -140366,11 +141301,27 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeAddOp4(v, OP_String8, 0, r1, 0, zSubSql, P4_DYNAMIC);
sqlite3VdbeAddOp2(v, OP_ResultRow, r1, 1);
}else{
- sqlite3VdbeAddOp4(v, OP_SqlExec, 0, 0, 0, zSubSql, P4_DYNAMIC);
+ sqlite3VdbeAddOp4(v, OP_SqlExec, nLimit ? 0x02 : 00, nLimit, 0,
+ zSubSql, P4_DYNAMIC);
}
}
}
sqlite3VdbeAddOp0(v, OP_Expire);
+
+ /* In a schema with a large number of tables and indexes, scale back
+ ** the analysis_limit to avoid excess run-time in the worst case.
+ */
+ if( !db->mallocFailed && nLimit>0 && nBtree>100 ){
+ int iAddr, iEnd;
+ VdbeOp *aOp;
+ nLimit = 100*nLimit/nBtree;
+ if( nLimit<100 ) nLimit = 100;
+ aOp = sqlite3VdbeGetOp(v, 0);
+ iEnd = sqlite3VdbeCurrentAddr(v);
+ for(iAddr=0; iAddr<iEnd; iAddr++){
+ if( aOp[iAddr].opcode==OP_SqlExec ) aOp[iAddr].p2 = nLimit;
+ }
+ }
break;
}
@@ -140634,9 +141585,9 @@ static int pragmaVtabBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
seen[0] = 0;
seen[1] = 0;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
- if( pConstraint->usable==0 ) continue;
- if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
if( pConstraint->iColumn < pTab->iHidden ) continue;
+ if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
+ if( pConstraint->usable==0 ) return SQLITE_CONSTRAINT;
j = pConstraint->iColumn - pTab->iHidden;
assert( j < 2 );
seen[j] = i+1;
@@ -140649,16 +141600,13 @@ static int pragmaVtabBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
j = seen[0]-1;
pIdxInfo->aConstraintUsage[j].argvIndex = 1;
pIdxInfo->aConstraintUsage[j].omit = 1;
- if( seen[1]==0 ){
- pIdxInfo->estimatedCost = (double)1000;
- pIdxInfo->estimatedRows = 1000;
- return SQLITE_OK;
- }
pIdxInfo->estimatedCost = (double)20;
pIdxInfo->estimatedRows = 20;
- j = seen[1]-1;
- pIdxInfo->aConstraintUsage[j].argvIndex = 2;
- pIdxInfo->aConstraintUsage[j].omit = 1;
+ if( seen[1] ){
+ j = seen[1]-1;
+ pIdxInfo->aConstraintUsage[j].argvIndex = 2;
+ pIdxInfo->aConstraintUsage[j].omit = 1;
+ }
return SQLITE_OK;
}
@@ -140678,6 +141626,7 @@ static void pragmaVtabCursorClear(PragmaVtabCursor *pCsr){
int i;
sqlite3_finalize(pCsr->pPragma);
pCsr->pPragma = 0;
+ pCsr->iRowid = 0;
for(i=0; i<ArraySize(pCsr->azArg); i++){
sqlite3_free(pCsr->azArg[i]);
pCsr->azArg[i] = 0;
@@ -141478,7 +142427,13 @@ SQLITE_PRIVATE void *sqlite3ParserAddCleanup(
void (*xCleanup)(sqlite3*,void*), /* The cleanup routine */
void *pPtr /* Pointer to object to be cleaned up */
){
- ParseCleanup *pCleanup = sqlite3DbMallocRaw(pParse->db, sizeof(*pCleanup));
+ ParseCleanup *pCleanup;
+ if( sqlite3FaultSim(300) ){
+ pCleanup = 0;
+ sqlite3OomFault(pParse->db);
+ }else{
+ pCleanup = sqlite3DbMallocRaw(pParse->db, sizeof(*pCleanup));
+ }
if( pCleanup ){
pCleanup->pNext = pParse->pCleanup;
pParse->pCleanup = pCleanup;
@@ -143600,9 +144555,16 @@ static void generateSortTail(
int addrExplain; /* Address of OP_Explain instruction */
#endif
- ExplainQueryPlan2(addrExplain, (pParse, 0,
- "USE TEMP B-TREE FOR %sORDER BY", pSort->nOBSat>0?"RIGHT PART OF ":"")
- );
+ nKey = pOrderBy->nExpr - pSort->nOBSat;
+ if( pSort->nOBSat==0 || nKey==1 ){
+ ExplainQueryPlan2(addrExplain, (pParse, 0,
+ "USE TEMP B-TREE FOR %sORDER BY", pSort->nOBSat?"LAST TERM OF ":""
+ ));
+ }else{
+ ExplainQueryPlan2(addrExplain, (pParse, 0,
+ "USE TEMP B-TREE FOR LAST %d TERMS OF ORDER BY", nKey
+ ));
+ }
sqlite3VdbeScanStatusRange(v, addrExplain,pSort->addrPush,pSort->addrPushEnd);
sqlite3VdbeScanStatusCounters(v, addrExplain, addrExplain, pSort->addrPush);
@@ -143640,7 +144602,6 @@ static void generateSortTail(
regRow = sqlite3GetTempRange(pParse, nColumn);
}
}
- nKey = pOrderBy->nExpr - pSort->nOBSat;
if( pSort->sortFlags & SORTFLAG_UseSorter ){
int regSortOut = ++pParse->nMem;
iSortTab = pParse->nTab++;
@@ -144245,8 +145206,7 @@ SQLITE_PRIVATE void sqlite3SubqueryColumnTypes(
NameContext sNC;
assert( pSelect!=0 );
- testcase( (pSelect->selFlags & SF_Resolved)==0 );
- assert( (pSelect->selFlags & SF_Resolved)!=0 || IN_RENAME_OBJECT );
+ assert( (pSelect->selFlags & SF_Resolved)!=0 );
assert( pTab->nCol==pSelect->pEList->nExpr || pParse->nErr>0 );
assert( aff==SQLITE_AFF_NONE || aff==SQLITE_AFF_BLOB );
if( db->mallocFailed || IN_RENAME_OBJECT ) return;
@@ -144257,17 +145217,22 @@ SQLITE_PRIVATE void sqlite3SubqueryColumnTypes(
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
const char *zType;
i64 n;
+ int m = 0;
+ Select *pS2 = pSelect;
pTab->tabFlags |= (pCol->colFlags & COLFLAG_NOINSERT);
p = a[i].pExpr;
/* pCol->szEst = ... // Column size est for SELECT tables never used */
pCol->affinity = sqlite3ExprAffinity(p);
+ while( pCol->affinity<=SQLITE_AFF_NONE && pS2->pNext!=0 ){
+ m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr);
+ pS2 = pS2->pNext;
+ pCol->affinity = sqlite3ExprAffinity(pS2->pEList->a[i].pExpr);
+ }
if( pCol->affinity<=SQLITE_AFF_NONE ){
pCol->affinity = aff;
}
- if( pCol->affinity>=SQLITE_AFF_TEXT && pSelect->pNext ){
- int m = 0;
- Select *pS2;
- for(m=0, pS2=pSelect->pNext; pS2; pS2=pS2->pNext){
+ if( pCol->affinity>=SQLITE_AFF_TEXT && (pS2->pNext || pS2!=pSelect) ){
+ for(pS2=pS2->pNext; pS2; pS2=pS2->pNext){
m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr);
}
if( pCol->affinity==SQLITE_AFF_TEXT && (m&0x01)!=0 ){
@@ -144297,12 +145262,12 @@ SQLITE_PRIVATE void sqlite3SubqueryColumnTypes(
}
}
if( zType ){
- i64 m = sqlite3Strlen30(zType);
+ const i64 k = sqlite3Strlen30(zType);
n = sqlite3Strlen30(pCol->zCnName);
- pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+m+2);
+ pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+k+2);
pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL);
if( pCol->zCnName ){
- memcpy(&pCol->zCnName[n+1], zType, m+1);
+ memcpy(&pCol->zCnName[n+1], zType, k+1);
pCol->colFlags |= COLFLAG_HASTYPE;
}
}
@@ -146699,7 +147664,7 @@ static void constInsert(
){
int i;
assert( pColumn->op==TK_COLUMN );
- assert( sqlite3ExprIsConstant(pValue) );
+ assert( sqlite3ExprIsConstant(pConst->pParse, pValue) );
if( ExprHasProperty(pColumn, EP_FixedCol) ) return;
if( sqlite3ExprAffinity(pValue)!=0 ) return;
@@ -146757,10 +147722,10 @@ static void findConstInWhere(WhereConst *pConst, Expr *pExpr){
pLeft = pExpr->pLeft;
assert( pRight!=0 );
assert( pLeft!=0 );
- if( pRight->op==TK_COLUMN && sqlite3ExprIsConstant(pLeft) ){
+ if( pRight->op==TK_COLUMN && sqlite3ExprIsConstant(pConst->pParse, pLeft) ){
constInsert(pConst,pRight,pLeft,pExpr);
}
- if( pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pRight) ){
+ if( pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pConst->pParse, pRight) ){
constInsert(pConst,pLeft,pRight,pExpr);
}
}
@@ -146981,6 +147946,18 @@ static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){
** The hope is that the terms added to the inner query will make it more
** efficient.
**
+** NAME AMBIGUITY
+**
+** This optimization is called the "WHERE-clause push-down optimization".
+**
+** Do not confuse this optimization with another unrelated optimization
+** with a similar name: The "MySQL push-down optimization" causes WHERE
+** clause terms that can be evaluated using only the index and without
+** reference to the table are run first, so that if they are false,
+** unnecessary table seeks are avoided.
+**
+** RULES
+**
** Do not attempt this optimization if:
**
** (1) (** This restriction was removed on 2017-09-29. We used to
@@ -147046,10 +148023,10 @@ static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){
** (9c) There is a RIGHT JOIN (or FULL JOIN) in between the ON/USING
** clause and the subquery.
**
-** Without this restriction, the push-down optimization might move
-** the ON/USING filter expression from the left side of a RIGHT JOIN
-** over to the right side, which leads to incorrect answers. See
-** also restriction (6) in sqlite3ExprIsSingleTableConstraint().
+** Without this restriction, the WHERE-clause push-down optimization
+** might move the ON/USING filter expression from the left side of a
+** RIGHT JOIN over to the right side, which leads to incorrect answers.
+** See also restriction (6) in sqlite3ExprIsSingleTableConstraint().
**
** (10) The inner query is not the right-hand table of a RIGHT JOIN.
**
@@ -147181,7 +148158,7 @@ static int pushDownWhereTerms(
}
#endif
- if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc) ){
+ if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc, 1) ){
nChng++;
pSubq->selFlags |= SF_PushDown;
while( pSubq ){
@@ -148316,8 +149293,7 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
if( p->selFlags & SF_HasTypeInfo ) return;
p->selFlags |= SF_HasTypeInfo;
pParse = pWalker->pParse;
- testcase( (p->selFlags & SF_Resolved)==0 );
- assert( (p->selFlags & SF_Resolved) || IN_RENAME_OBJECT );
+ assert( (p->selFlags & SF_Resolved) );
pTabList = p->pSrc;
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
Table *pTab = pFrom->pTab;
@@ -148387,6 +149363,8 @@ SQLITE_PRIVATE void sqlite3SelectPrep(
*/
static void printAggInfo(AggInfo *pAggInfo){
int ii;
+ sqlite3DebugPrintf("AggInfo %d/%p:\n",
+ pAggInfo->selId, pAggInfo);
for(ii=0; ii<pAggInfo->nColumn; ii++){
struct AggInfo_col *pCol = &pAggInfo->aCol[ii];
sqlite3DebugPrintf(
@@ -149577,7 +150555,7 @@ SQLITE_PRIVATE int sqlite3Select(
/* Generate code for all sub-queries in the FROM clause
*/
pSub = pItem->pSelect;
- if( pSub==0 ) continue;
+ if( pSub==0 || pItem->addrFillSub!=0 ) continue;
/* The code for a subquery should only be generated once. */
assert( pItem->addrFillSub==0 );
@@ -149608,7 +150586,7 @@ SQLITE_PRIVATE int sqlite3Select(
#endif
assert( pItem->pSelect && (pItem->pSelect->selFlags & SF_PushDown)!=0 );
}else{
- TREETRACE(0x4000,pParse,p,("Push-down not possible\n"));
+ TREETRACE(0x4000,pParse,p,("WHERE-lcause push-down not possible\n"));
}
/* Convert unused result columns of the subquery into simple NULL
@@ -150489,6 +151467,12 @@ select_end:
sqlite3ExprListDelete(db, pMinMaxOrderBy);
#ifdef SQLITE_DEBUG
if( pAggInfo && !db->mallocFailed ){
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x20 ){
+ TREETRACE(0x20,pParse,p,("Finished with AggInfo\n"));
+ printAggInfo(pAggInfo);
+ }
+#endif
for(i=0; i<pAggInfo->nColumn; i++){
Expr *pExpr = pAggInfo->aCol[i].pCExpr;
if( pExpr==0 ) continue;
@@ -151670,6 +152654,72 @@ static ExprList *sqlite3ExpandReturning(
return pNew;
}
+/* If the Expr node is a subquery or an EXISTS operator or an IN operator that
+** uses a subquery, and if the subquery is SF_Correlated, then mark the
+** expression as EP_VarSelect.
+*/
+static int sqlite3ReturningSubqueryVarSelect(Walker *NotUsed, Expr *pExpr){
+ UNUSED_PARAMETER(NotUsed);
+ if( ExprUseXSelect(pExpr)
+ && (pExpr->x.pSelect->selFlags & SF_Correlated)!=0
+ ){
+ testcase( ExprHasProperty(pExpr, EP_VarSelect) );
+ ExprSetProperty(pExpr, EP_VarSelect);
+ }
+ return WRC_Continue;
+}
+
+
+/*
+** If the SELECT references the table pWalker->u.pTab, then do two things:
+**
+** (1) Mark the SELECT as as SF_Correlated.
+** (2) Set pWalker->eCode to non-zero so that the caller will know
+** that (1) has happened.
+*/
+static int sqlite3ReturningSubqueryCorrelated(Walker *pWalker, Select *pSelect){
+ int i;
+ SrcList *pSrc;
+ assert( pSelect!=0 );
+ pSrc = pSelect->pSrc;
+ assert( pSrc!=0 );
+ for(i=0; i<pSrc->nSrc; i++){
+ if( pSrc->a[i].pTab==pWalker->u.pTab ){
+ testcase( pSelect->selFlags & SF_Correlated );
+ pSelect->selFlags |= SF_Correlated;
+ pWalker->eCode = 1;
+ break;
+ }
+ }
+ return WRC_Continue;
+}
+
+/*
+** Scan the expression list that is the argument to RETURNING looking
+** for subqueries that depend on the table which is being modified in the
+** statement that is hosting the RETURNING clause (pTab). Mark all such
+** subqueries as SF_Correlated. If the subqueries are part of an
+** expression, mark the expression as EP_VarSelect.
+**
+** https://sqlite.org/forum/forumpost/2c83569ce8945d39
+*/
+static void sqlite3ProcessReturningSubqueries(
+ ExprList *pEList,
+ Table *pTab
+){
+ Walker w;
+ memset(&w, 0, sizeof(w));
+ w.xExprCallback = sqlite3ExprWalkNoop;
+ w.xSelectCallback = sqlite3ReturningSubqueryCorrelated;
+ w.u.pTab = pTab;
+ sqlite3WalkExprList(&w, pEList);
+ if( w.eCode ){
+ w.xExprCallback = sqlite3ReturningSubqueryVarSelect;
+ w.xSelectCallback = sqlite3SelectWalkNoop;
+ sqlite3WalkExprList(&w, pEList);
+ }
+}
+
/*
** Generate code for the RETURNING trigger. Unlike other triggers
** that invoke a subprogram in the bytecode, the code for RETURNING
@@ -151706,6 +152756,7 @@ static void codeReturningTrigger(
sSelect.pSrc = &sFrom;
sFrom.nSrc = 1;
sFrom.a[0].pTab = pTab;
+ sFrom.a[0].zName = pTab->zName; /* tag-20240424-1 */
sFrom.a[0].iCursor = -1;
sqlite3SelectPrep(pParse, &sSelect, 0);
if( pParse->nErr==0 ){
@@ -151732,6 +152783,7 @@ static void codeReturningTrigger(
int i;
int nCol = pNew->nExpr;
int reg = pParse->nMem+1;
+ sqlite3ProcessReturningSubqueries(pNew, pTab);
pParse->nMem += nCol+2;
pReturning->iRetReg = reg;
for(i=0; i<nCol; i++){
@@ -154942,6 +155994,8 @@ static int vtabCallConstructor(
db->pVtabCtx = &sCtx;
pTab->nTabRef++;
rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr);
+ assert( pTab!=0 );
+ assert( pTab->nTabRef>1 || rc!=SQLITE_OK );
sqlite3DeleteTable(db, pTab);
db->pVtabCtx = sCtx.pPrior;
if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
@@ -154964,7 +156018,7 @@ static int vtabCallConstructor(
pVTable->nRef = 1;
if( sCtx.bDeclared==0 ){
const char *zFormat = "vtable constructor did not declare schema: %s";
- *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName);
+ *pzErr = sqlite3MPrintf(db, zFormat, zModuleName);
sqlite3VtabUnlock(pVTable);
rc = SQLITE_ERROR;
}else{
@@ -155142,12 +156196,30 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
Table *pTab;
Parse sParse;
int initBusy;
+ int i;
+ const unsigned char *z;
+ static const u8 aKeyword[] = { TK_CREATE, TK_TABLE, 0 };
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){
return SQLITE_MISUSE_BKPT;
}
#endif
+
+ /* Verify that the first two keywords in the CREATE TABLE statement
+ ** really are "CREATE" and "TABLE". If this is not the case, then
+ ** sqlite3_declare_vtab() is being misused.
+ */
+ z = (const unsigned char*)zCreateTable;
+ for(i=0; aKeyword[i]; i++){
+ int tokenType = 0;
+ do{ z += sqlite3GetToken(z, &tokenType); }while( tokenType==TK_SPACE );
+ if( tokenType!=aKeyword[i] ){
+ sqlite3ErrorWithMsg(db, SQLITE_ERROR, "syntax error");
+ return SQLITE_ERROR;
+ }
+ }
+
sqlite3_mutex_enter(db->mutex);
pCtx = db->pVtabCtx;
if( !pCtx || pCtx->bDeclared ){
@@ -155155,6 +156227,7 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
sqlite3_mutex_leave(db->mutex);
return SQLITE_MISUSE_BKPT;
}
+
pTab = pCtx->pTab;
assert( IsVirtual(pTab) );
@@ -155168,11 +156241,10 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
initBusy = db->init.busy;
db->init.busy = 0;
sParse.nQueryLoop = 1;
- if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable)
- && ALWAYS(sParse.pNewTable!=0)
- && ALWAYS(!db->mallocFailed)
- && IsOrdinaryTable(sParse.pNewTable)
- ){
+ if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable) ){
+ assert( sParse.pNewTable!=0 );
+ assert( !db->mallocFailed );
+ assert( IsOrdinaryTable(sParse.pNewTable) );
assert( sParse.zErrMsg==0 );
if( !pTab->aCol ){
Table *pNew = sParse.pNewTable;
@@ -157668,6 +158740,27 @@ static SQLITE_NOINLINE void filterPullDown(
}
/*
+** Loop pLoop is a WHERE_INDEXED level that uses at least one IN(...)
+** operator. Return true if level pLoop is guaranteed to visit only one
+** row for each key generated for the index.
+*/
+static int whereLoopIsOneRow(WhereLoop *pLoop){
+ if( pLoop->u.btree.pIndex->onError
+ && pLoop->nSkip==0
+ && pLoop->u.btree.nEq==pLoop->u.btree.pIndex->nKeyCol
+ ){
+ int ii;
+ for(ii=0; ii<pLoop->u.btree.nEq; ii++){
+ if( pLoop->aLTerm[ii]->eOperator & (WO_IS|WO_ISNULL) ){
+ return 0;
+ }
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/*
** Generate code for the start of the iLevel-th loop in the WHERE clause
** implementation described by pWInfo.
*/
@@ -157745,7 +158838,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){
pLevel->iLeftJoin = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin);
- VdbeComment((v, "init LEFT JOIN no-match flag"));
+ VdbeComment((v, "init LEFT JOIN match flag"));
}
/* Compute a safe address to jump to if we discover that the table for
@@ -158414,7 +159507,9 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
}
/* Record the instruction used to terminate the loop. */
- if( pLoop->wsFlags & WHERE_ONEROW ){
+ if( (pLoop->wsFlags & WHERE_ONEROW)
+ || (pLevel->u.in.nIn && regBignull==0 && whereLoopIsOneRow(pLoop))
+ ){
pLevel->op = OP_Noop;
}else if( bRev ){
pLevel->op = OP_Prev;
@@ -158804,6 +159899,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
** iLoop==3: Code all remaining expressions.
**
** An effort is made to skip unnecessary iterations of the loop.
+ **
+ ** This optimization of causing simple query restrictions to occur before
+ ** more complex one is call the "push-down" optimization in MySQL. Here
+ ** in SQLite, the name is "MySQL push-down", since there is also another
+ ** totally unrelated optimization called "WHERE-clause push-down".
+ ** Sometimes the qualifier is omitted, resulting in an ambiguity, so beware.
*/
iLoop = (pIdx ? 1 : 2);
do{
@@ -159054,7 +160155,16 @@ SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3WhereRightJoinLoop(
pRJ->regReturn);
for(k=0; k<iLevel; k++){
int iIdxCur;
+ SrcItem *pRight;
+ assert( pWInfo->a[k].pWLoop->iTab == pWInfo->a[k].iFrom );
+ pRight = &pWInfo->pTabList->a[pWInfo->a[k].iFrom];
mAll |= pWInfo->a[k].pWLoop->maskSelf;
+ if( pRight->fg.viaCoroutine ){
+ sqlite3VdbeAddOp3(
+ v, OP_Null, 0, pRight->regResult,
+ pRight->regResult + pRight->pSelect->pEList->nExpr-1
+ );
+ }
sqlite3VdbeAddOp1(v, OP_NullRow, pWInfo->a[k].iTabCur);
iIdxCur = pWInfo->a[k].iIdxCur;
if( iIdxCur ){
@@ -160111,7 +161221,7 @@ static SQLITE_NOINLINE int exprMightBeIndexed2(
if( pIdx->aiColumn[i]!=XN_EXPR ) continue;
assert( pIdx->bHasExpr );
if( sqlite3ExprCompareSkip(pExpr,pIdx->aColExpr->a[i].pExpr,iCur)==0
- && pExpr->op!=TK_STRING
+ && !sqlite3ExprIsConstant(0,pIdx->aColExpr->a[i].pExpr)
){
aiCurCol[0] = iCur;
aiCurCol[1] = XN_EXPR;
@@ -160760,6 +161870,7 @@ SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3WhereAddLimit(WhereClause *pWC, Selec
continue;
}
if( pWC->a[ii].leftCursor!=iCsr ) return;
+ if( pWC->a[ii].prereqRight!=0 ) return;
}
/* Check condition (5). Return early if it is not met. */
@@ -160774,12 +161885,14 @@ SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3WhereAddLimit(WhereClause *pWC, Selec
/* All conditions are met. Add the terms to the where-clause object. */
assert( p->pLimit->op==TK_LIMIT );
- whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft,
- iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT);
- if( p->iOffset>0 ){
+ if( p->iOffset!=0 && (p->selFlags & SF_Compound)==0 ){
whereAddLimitExpr(pWC, p->iOffset, p->pLimit->pRight,
iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET);
}
+ if( p->iOffset==0 || (p->selFlags & SF_Compound)==0 ){
+ whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft,
+ iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT);
+ }
}
}
@@ -161298,6 +162411,42 @@ static Expr *whereRightSubexprIsColumn(Expr *p){
}
/*
+** Term pTerm is guaranteed to be a WO_IN term. It may be a component term
+** of a vector IN expression of the form "(x, y, ...) IN (SELECT ...)".
+** This function checks to see if the term is compatible with an index
+** column with affinity idxaff (one of the SQLITE_AFF_XYZ values). If so,
+** it returns a pointer to the name of the collation sequence (e.g. "BINARY"
+** or "NOCASE") used by the comparison in pTerm. If it is not compatible
+** with affinity idxaff, NULL is returned.
+*/
+static SQLITE_NOINLINE const char *indexInAffinityOk(
+ Parse *pParse,
+ WhereTerm *pTerm,
+ u8 idxaff
+){
+ Expr *pX = pTerm->pExpr;
+ Expr inexpr;
+
+ assert( pTerm->eOperator & WO_IN );
+
+ if( sqlite3ExprIsVector(pX->pLeft) ){
+ int iField = pTerm->u.x.iField - 1;
+ inexpr.flags = 0;
+ inexpr.op = TK_EQ;
+ inexpr.pLeft = pX->pLeft->x.pList->a[iField].pExpr;
+ assert( ExprUseXSelect(pX) );
+ inexpr.pRight = pX->x.pSelect->pEList->a[iField].pExpr;
+ pX = &inexpr;
+ }
+
+ if( sqlite3IndexAffinityOk(pX, idxaff) ){
+ CollSeq *pRet = sqlite3ExprCompareCollSeq(pParse, pX);
+ return pRet ? pRet->zName : sqlite3StrBINARY;
+ }
+ return 0;
+}
+
+/*
** Advance to the next WhereTerm that matches according to the criteria
** established when the pScan object was initialized by whereScanInit().
** Return NULL if there are no more matching WhereTerms.
@@ -161347,16 +162496,24 @@ static WhereTerm *whereScanNext(WhereScan *pScan){
if( (pTerm->eOperator & pScan->opMask)!=0 ){
/* Verify the affinity and collating sequence match */
if( pScan->zCollName && (pTerm->eOperator & WO_ISNULL)==0 ){
- CollSeq *pColl;
+ const char *zCollName;
Parse *pParse = pWC->pWInfo->pParse;
pX = pTerm->pExpr;
- if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){
- continue;
+
+ if( (pTerm->eOperator & WO_IN) ){
+ zCollName = indexInAffinityOk(pParse, pTerm, pScan->idxaff);
+ if( !zCollName ) continue;
+ }else{
+ CollSeq *pColl;
+ if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){
+ continue;
+ }
+ assert(pX->pLeft);
+ pColl = sqlite3ExprCompareCollSeq(pParse, pX);
+ zCollName = pColl ? pColl->zName : sqlite3StrBINARY;
}
- assert(pX->pLeft);
- pColl = sqlite3ExprCompareCollSeq(pParse, pX);
- if( pColl==0 ) pColl = pParse->db->pDfltColl;
- if( sqlite3StrICmp(pColl->zName, pScan->zCollName) ){
+
+ if( sqlite3StrICmp(zCollName, pScan->zCollName) ){
continue;
}
}
@@ -161708,9 +162865,13 @@ static void translateColumnToCopy(
** are no-ops.
*/
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(WHERETRACE_ENABLED)
-static void whereTraceIndexInfoInputs(sqlite3_index_info *p){
+static void whereTraceIndexInfoInputs(
+ sqlite3_index_info *p, /* The IndexInfo object */
+ Table *pTab /* The TABLE that is the virtual table */
+){
int i;
if( (sqlite3WhereTrace & 0x10)==0 ) return;
+ sqlite3DebugPrintf("sqlite3_index_info inputs for %s:\n", pTab->zName);
for(i=0; i<p->nConstraint; i++){
sqlite3DebugPrintf(
" constraint[%d]: col=%d termid=%d op=%d usabled=%d collseq=%s\n",
@@ -161728,9 +162889,13 @@ static void whereTraceIndexInfoInputs(sqlite3_index_info *p){
p->aOrderBy[i].desc);
}
}
-static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){
+static void whereTraceIndexInfoOutputs(
+ sqlite3_index_info *p, /* The IndexInfo object */
+ Table *pTab /* The TABLE that is the virtual table */
+){
int i;
if( (sqlite3WhereTrace & 0x10)==0 ) return;
+ sqlite3DebugPrintf("sqlite3_index_info outputs for %s:\n", pTab->zName);
for(i=0; i<p->nConstraint; i++){
sqlite3DebugPrintf(" usage[%d]: argvIdx=%d omit=%d\n",
i,
@@ -161744,8 +162909,8 @@ static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){
sqlite3DebugPrintf(" estimatedRows=%lld\n", p->estimatedRows);
}
#else
-#define whereTraceIndexInfoInputs(A)
-#define whereTraceIndexInfoOutputs(A)
+#define whereTraceIndexInfoInputs(A,B)
+#define whereTraceIndexInfoOutputs(A,B)
#endif
/*
@@ -161929,7 +163094,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
** WHERE clause (or the ON clause of a LEFT join) that constrain which
** rows of the target table (pSrc) that can be used. */
if( (pTerm->wtFlags & TERM_VIRTUAL)==0
- && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom)
+ && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom, 0)
){
pPartial = sqlite3ExprAnd(pParse, pPartial,
sqlite3ExprDup(pParse->db, pExpr, 0));
@@ -161971,7 +163136,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
** if they go out of sync.
*/
if( IsView(pTable) ){
- extraCols = ALLBITS;
+ extraCols = ALLBITS & ~idxCols;
}else{
extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1));
}
@@ -162198,7 +163363,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
for(pTerm=pWInfo->sWC.a; pTerm<pWCEnd; pTerm++){
Expr *pExpr = pTerm->pExpr;
if( (pTerm->wtFlags & TERM_VIRTUAL)==0
- && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc)
+ && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc, 0)
){
sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
}
@@ -162324,7 +163489,7 @@ static sqlite3_index_info *allocateIndexInfo(
Expr *pE2;
/* Skip over constant terms in the ORDER BY clause */
- if( sqlite3ExprIsConstant(pExpr) ){
+ if( sqlite3ExprIsConstant(0, pExpr) ){
continue;
}
@@ -162359,7 +163524,7 @@ static sqlite3_index_info *allocateIndexInfo(
}
if( i==n ){
nOrderBy = n;
- if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY) ){
+ if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY) && !pSrc->fg.rowidUsed ){
eDistinct = 2 + ((pWInfo->wctrlFlags & WHERE_SORTBYGROUP)!=0);
}else if( pWInfo->wctrlFlags & WHERE_GROUPBY ){
eDistinct = 1;
@@ -162436,7 +163601,7 @@ static sqlite3_index_info *allocateIndexInfo(
pIdxInfo->nConstraint = j;
for(i=j=0; i<nOrderBy; i++){
Expr *pExpr = pOrderBy->a[i].pExpr;
- if( sqlite3ExprIsConstant(pExpr) ) continue;
+ if( sqlite3ExprIsConstant(0, pExpr) ) continue;
assert( pExpr->op==TK_COLUMN
|| (pExpr->op==TK_COLLATE && pExpr->pLeft->op==TK_COLUMN
&& pExpr->iColumn==pExpr->pLeft->iColumn) );
@@ -162488,11 +163653,11 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab;
int rc;
- whereTraceIndexInfoInputs(p);
+ whereTraceIndexInfoInputs(p, pTab);
pParse->db->nSchemaLock++;
rc = pVtab->pModule->xBestIndex(pVtab, p);
pParse->db->nSchemaLock--;
- whereTraceIndexInfoOutputs(p);
+ whereTraceIndexInfoOutputs(p, pTab);
if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){
if( rc==SQLITE_NOMEM ){
@@ -163970,7 +165135,9 @@ static int whereLoopAddBtreeIndex(
}
if( pProbe->bUnordered || pProbe->bLowQual ){
if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
- if( pProbe->bLowQual ) opMask &= ~(WO_EQ|WO_IN|WO_IS);
+ if( pProbe->bLowQual && pSrc->fg.isIndexedBy==0 ){
+ opMask &= ~(WO_EQ|WO_IN|WO_IS);
+ }
}
assert( pNew->u.btree.nEq<pProbe->nColumn );
@@ -164237,10 +165404,13 @@ static int whereLoopAddBtreeIndex(
}
}
- /* Set rCostIdx to the cost of visiting selected rows in index. Add
- ** it to pNew->rRun, which is currently set to the cost of the index
- ** seek only. Then, if this is a non-covering index, add the cost of
- ** visiting the rows in the main table. */
+ /* Set rCostIdx to the estimated cost of visiting selected rows in the
+ ** index. The estimate is the sum of two values:
+ ** 1. The cost of doing one search-by-key to find the first matching
+ ** entry
+ ** 2. Stepping forward in the index pNew->nOut times to find all
+ ** additional matching entries.
+ */
assert( pSrc->pTab->szTabRow>0 );
if( pProbe->idxType==SQLITE_IDXTYPE_IPK ){
/* The pProbe->szIdxRow is low for an IPK table since the interior
@@ -164251,7 +165421,15 @@ static int whereLoopAddBtreeIndex(
}else{
rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow;
}
- pNew->rRun = sqlite3LogEstAdd(rLogSize, rCostIdx);
+ rCostIdx = sqlite3LogEstAdd(rLogSize, rCostIdx);
+
+ /* Estimate the cost of running the loop. If all data is coming
+ ** from the index, then this is just the cost of doing the index
+ ** lookup and scan. But if some data is coming out of the main table,
+ ** we also have to add in the cost of doing pNew->nOut searches to
+ ** locate the row in the main table that corresponds to the index entry.
+ */
+ pNew->rRun = rCostIdx;
if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK|WHERE_EXPRIDX))==0 ){
pNew->rRun = sqlite3LogEstAdd(pNew->rRun, pNew->nOut + 16);
}
@@ -164357,7 +165535,9 @@ static int indexMightHelpWithOrderBy(
for(ii=0; ii<pOB->nExpr; ii++){
Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr);
if( NEVER(pExpr==0) ) continue;
- if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){
+ if( (pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN)
+ && pExpr->iTable==iCursor
+ ){
if( pExpr->iColumn<0 ) return 1;
for(jj=0; jj<pIndex->nKeyCol; jj++){
if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1;
@@ -164614,7 +165794,7 @@ static void wherePartIdxExpr(
u8 aff;
if( pLeft->op!=TK_COLUMN ) return;
- if( !sqlite3ExprIsConstant(pRight) ) return;
+ if( !sqlite3ExprIsConstant(0, pRight) ) return;
if( !sqlite3IsBinary(sqlite3ExprCompareCollSeq(pParse, pPart)) ) return;
if( pLeft->iColumn<0 ) return;
aff = pIdx->pTable->aCol[pLeft->iColumn].affinity;
@@ -164963,7 +166143,7 @@ static int whereLoopAddBtree(
** unique index is used (making the index functionally non-unique)
** then the sqlite_stat1 data becomes important for scoring the
** plan */
- pTab->tabFlags |= TF_StatsUsed;
+ pTab->tabFlags |= TF_MaybeReanalyze;
}
#ifdef SQLITE_ENABLE_STAT4
sqlite3Stat4ProbeFree(pBuilder->pRec);
@@ -164986,6 +166166,21 @@ static int isLimitTerm(WhereTerm *pTerm){
}
/*
+** Return true if the first nCons constraints in the pUsage array are
+** marked as in-use (have argvIndex>0). False otherwise.
+*/
+static int allConstraintsUsed(
+ struct sqlite3_index_constraint_usage *aUsage,
+ int nCons
+){
+ int ii;
+ for(ii=0; ii<nCons; ii++){
+ if( aUsage[ii].argvIndex<=0 ) return 0;
+ }
+ return 1;
+}
+
+/*
** Argument pIdxInfo is already populated with all constraints that may
** be used by the virtual table identified by pBuilder->pNew->iTab. This
** function marks a subset of those constraints usable, invokes the
@@ -165125,13 +166320,20 @@ static int whereLoopAddVirtualOne(
*pbIn = 1; assert( (mExclude & WO_IN)==0 );
}
+ /* Unless pbRetryLimit is non-NULL, there should be no LIMIT/OFFSET
+ ** terms. And if there are any, they should follow all other terms. */
assert( pbRetryLimit || !isLimitTerm(pTerm) );
- if( isLimitTerm(pTerm) && *pbIn ){
+ assert( !isLimitTerm(pTerm) || i>=nConstraint-2 );
+ assert( !isLimitTerm(pTerm) || i==nConstraint-1 || isLimitTerm(pTerm+1) );
+
+ if( isLimitTerm(pTerm) && (*pbIn || !allConstraintsUsed(pUsage, i)) ){
/* If there is an IN(...) term handled as an == (separate call to
** xFilter for each value on the RHS of the IN) and a LIMIT or
- ** OFFSET term handled as well, the plan is unusable. Set output
- ** variable *pbRetryLimit to true to tell the caller to retry with
- ** LIMIT and OFFSET disabled. */
+ ** OFFSET term handled as well, the plan is unusable. Similarly,
+ ** if there is a LIMIT/OFFSET and there are other unused terms,
+ ** the plan cannot be used. In these cases set variable *pbRetryLimit
+ ** to true to tell the caller to retry with LIMIT and OFFSET
+ ** disabled. */
if( pIdxInfo->needToFreeIdxStr ){
sqlite3_free(pIdxInfo->idxStr);
pIdxInfo->idxStr = 0;
@@ -165988,7 +167190,7 @@ static i8 wherePathSatisfiesOrderBy(
if( MASKBIT(i) & obSat ) continue;
p = pOrderBy->a[i].pExpr;
mTerm = sqlite3WhereExprUsage(&pWInfo->sMaskSet,p);
- if( mTerm==0 && !sqlite3ExprIsConstant(p) ) continue;
+ if( mTerm==0 && !sqlite3ExprIsConstant(0,p) ) continue;
if( (mTerm&~orderDistinctMask)==0 ){
obSat |= MASKBIT(i);
}
@@ -166457,10 +167659,9 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){
pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
}
- if( pWInfo->pSelect->pOrderBy
- && pWInfo->nOBSat > pWInfo->pSelect->pOrderBy->nExpr ){
- pWInfo->nOBSat = pWInfo->pSelect->pOrderBy->nExpr;
- }
+ /* vvv--- See check-in [12ad822d9b827777] on 2023-03-16 ---vvv */
+ assert( pWInfo->pSelect->pOrderBy==0
+ || pWInfo->nOBSat <= pWInfo->pSelect->pOrderBy->nExpr );
}else{
pWInfo->revMask = pFrom->revLoop;
if( pWInfo->nOBSat<=0 ){
@@ -166503,7 +167704,6 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
}
}
-
pWInfo->nRowOut = pFrom->nRow;
/* Free temporary memory and return success */
@@ -166512,6 +167712,83 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
}
/*
+** This routine implements a heuristic designed to improve query planning.
+** This routine is called in between the first and second call to
+** wherePathSolver(). Hence the name "Interstage" "Heuristic".
+**
+** The first call to wherePathSolver() (hereafter just "solver()") computes
+** the best path without regard to the order of the outputs. The second call
+** to the solver() builds upon the first call to try to find an alternative
+** path that satisfies the ORDER BY clause.
+**
+** This routine looks at the results of the first solver() run, and for
+** every FROM clause term in the resulting query plan that uses an equality
+** constraint against an index, disable other WhereLoops for that same
+** FROM clause term that would try to do a full-table scan. This prevents
+** an index search from being converted into a full-table scan in order to
+** satisfy an ORDER BY clause, since even though we might get slightly better
+** performance using the full-scan without sorting if the output size
+** estimates are very precise, we might also get severe performance
+** degradation using the full-scan if the output size estimate is too large.
+** It is better to err on the side of caution.
+**
+** Except, if the first solver() call generated a full-table scan in an outer
+** loop then stop this analysis at the first full-scan, since the second
+** solver() run might try to swap that full-scan for another in order to
+** get the output into the correct order. In other words, we allow a
+** rewrite like this:
+**
+** First Solver() Second Solver()
+** |-- SCAN t1 |-- SCAN t2
+** |-- SEARCH t2 `-- SEARCH t1
+** `-- SORT USING B-TREE
+**
+** The purpose of this routine is to disallow rewrites such as:
+**
+** First Solver() Second Solver()
+** |-- SEARCH t1 |-- SCAN t2 <--- bad!
+** |-- SEARCH t2 `-- SEARCH t1
+** `-- SORT USING B-TREE
+**
+** See test cases in test/whereN.test for the real-world query that
+** originally provoked this heuristic.
+*/
+static SQLITE_NOINLINE void whereInterstageHeuristic(WhereInfo *pWInfo){
+ int i;
+#ifdef WHERETRACE_ENABLED
+ int once = 0;
+#endif
+ for(i=0; i<pWInfo->nLevel; i++){
+ WhereLoop *p = pWInfo->a[i].pWLoop;
+ if( p==0 ) break;
+ if( (p->wsFlags & WHERE_VIRTUALTABLE)!=0 ) continue;
+ if( (p->wsFlags & (WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_IN))!=0 ){
+ u8 iTab = p->iTab;
+ WhereLoop *pLoop;
+ for(pLoop=pWInfo->pLoops; pLoop; pLoop=pLoop->pNextLoop){
+ if( pLoop->iTab!=iTab ) continue;
+ if( (pLoop->wsFlags & (WHERE_CONSTRAINT|WHERE_AUTO_INDEX))!=0 ){
+ /* Auto-index and index-constrained loops allowed to remain */
+ continue;
+ }
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace & 0x80 ){
+ if( once==0 ){
+ sqlite3DebugPrintf("Loops disabled by interstage heuristic:\n");
+ once = 1;
+ }
+ sqlite3WhereLoopPrint(pLoop, &pWInfo->sWC);
+ }
+#endif /* WHERETRACE_ENABLED */
+ pLoop->prereq = ALLBITS; /* Prevent 2nd solver() from using this one */
+ }
+ }else{
+ break;
+ }
+ }
+}
+
+/*
** Most queries use only a single table (they are not joins) and have
** simple == constraints against indexed fields. This routine attempts
** to plan those simple cases using much less ceremony than the
@@ -166799,7 +168076,7 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab];
Table *pTab = pItem->pTab;
if( (pTab->tabFlags & TF_HasStat1)==0 ) break;
- pTab->tabFlags |= TF_StatsUsed;
+ pTab->tabFlags |= TF_MaybeReanalyze;
if( i>=1
&& (pLoop->wsFlags & reqFlags)==reqFlags
/* vvvvvv--- Always the case if WHERE_COLUMN_EQ is defined */
@@ -166821,6 +168098,58 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
}
/*
+** Expression Node callback for sqlite3ExprCanReturnSubtype().
+**
+** Only a function call is able to return a subtype. So if the node
+** is not a function call, return WRC_Prune immediately.
+**
+** A function call is able to return a subtype if it has the
+** SQLITE_RESULT_SUBTYPE property.
+**
+** Assume that every function is able to pass-through a subtype from
+** one of its argument (using sqlite3_result_value()). Most functions
+** are not this way, but we don't have a mechanism to distinguish those
+** that are from those that are not, so assume they all work this way.
+** That means that if one of its arguments is another function and that
+** other function is able to return a subtype, then this function is
+** able to return a subtype.
+*/
+static int exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr){
+ int n;
+ FuncDef *pDef;
+ sqlite3 *db;
+ if( pExpr->op!=TK_FUNCTION ){
+ return WRC_Prune;
+ }
+ assert( ExprUseXList(pExpr) );
+ db = pWalker->pParse->db;
+ n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0;
+ pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0);
+ if( pDef==0 || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){
+ pWalker->eCode = 1;
+ return WRC_Prune;
+ }
+ return WRC_Continue;
+}
+
+/*
+** Return TRUE if expression pExpr is able to return a subtype.
+**
+** A TRUE return does not guarantee that a subtype will be returned.
+** It only indicates that a subtype return is possible. False positives
+** are acceptable as they only disable an optimization. False negatives,
+** on the other hand, can lead to incorrect answers.
+*/
+static int sqlite3ExprCanReturnSubtype(Parse *pParse, Expr *pExpr){
+ Walker w;
+ memset(&w, 0, sizeof(w));
+ w.pParse = pParse;
+ w.xExprCallback = exprNodeCanReturnSubtype;
+ sqlite3WalkExpr(&w, pExpr);
+ return w.eCode;
+}
+
+/*
** The index pIdx is used by a query and contains one or more expressions.
** In other words pIdx is an index on an expression. iIdxCur is the cursor
** number for the index and iDataCur is the cursor number for the corresponding
@@ -166852,20 +168181,12 @@ static SQLITE_NOINLINE void whereAddIndexedExpr(
}else{
continue;
}
- if( sqlite3ExprIsConstant(pExpr) ) continue;
- if( pExpr->op==TK_FUNCTION ){
+ if( sqlite3ExprIsConstant(0,pExpr) ) continue;
+ if( pExpr->op==TK_FUNCTION && sqlite3ExprCanReturnSubtype(pParse,pExpr) ){
/* Functions that might set a subtype should not be replaced by the
** value taken from an expression index since the index omits the
** subtype. https://sqlite.org/forum/forumpost/68d284c86b082c3e */
- int n;
- FuncDef *pDef;
- sqlite3 *db = pParse->db;
- assert( ExprUseXList(pExpr) );
- n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0;
- pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0);
- if( pDef==0 || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){
- continue;
- }
+ continue;
}
p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr));
if( p==0 ) break;
@@ -167130,7 +168451,11 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
){
pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
}
- ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW"));
+ if( ALWAYS(pWInfo->pSelect)
+ && (pWInfo->pSelect->selFlags & SF_MultiValue)==0
+ ){
+ ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW"));
+ }
}else{
/* Assign a bit from the bitmask to every term in the FROM clause.
**
@@ -167283,6 +168608,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
wherePathSolver(pWInfo, 0);
if( db->mallocFailed ) goto whereBeginError;
if( pWInfo->pOrderBy ){
+ whereInterstageHeuristic(pWInfo);
wherePathSolver(pWInfo, pWInfo->nRowOut+1);
if( db->mallocFailed ) goto whereBeginError;
}
@@ -167832,7 +169158,15 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v);
assert( (ws & WHERE_IDX_ONLY)==0 || (ws & WHERE_INDEXED)!=0 );
if( (ws & WHERE_IDX_ONLY)==0 ){
- assert( pLevel->iTabCur==pTabList->a[pLevel->iFrom].iCursor );
+ SrcItem *pSrc = &pTabList->a[pLevel->iFrom];
+ assert( pLevel->iTabCur==pSrc->iCursor );
+ if( pSrc->fg.viaCoroutine ){
+ int m, n;
+ n = pSrc->regResult;
+ assert( pSrc->pTab!=0 );
+ m = pSrc->pTab->nCol;
+ sqlite3VdbeAddOp3(v, OP_Null, 0, n, n+m-1);
+ }
sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iTabCur);
}
if( (ws & WHERE_INDEXED)
@@ -167882,6 +169216,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
*/
if( pTabItem->fg.viaCoroutine ){
testcase( pParse->db->mallocFailed );
+ assert( pTabItem->regResult>=0 );
translateColumnToCopy(pParse, pLevel->addrBody, pLevel->iTabCur,
pTabItem->regResult, 0);
continue;
@@ -169186,7 +170521,7 @@ SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p){
** variable values in the expression tree.
*/
static Expr *sqlite3WindowOffsetExpr(Parse *pParse, Expr *pExpr){
- if( 0==sqlite3ExprIsConstant(pExpr) ){
+ if( 0==sqlite3ExprIsConstant(0,pExpr) ){
if( IN_RENAME_OBJECT ) sqlite3RenameExprUnmap(pParse, pExpr);
sqlite3ExprDelete(pParse->db, pExpr);
pExpr = sqlite3ExprAlloc(pParse->db, TK_NULL, 0, 0);
@@ -171278,6 +172613,14 @@ static void updateDeleteLimitError(
return pSelect;
}
+ /* Memory allocator for parser stack resizing. This is a thin wrapper around
+ ** sqlite3_realloc() that includes a call to sqlite3FaultSim() to facilitate
+ ** testing.
+ */
+ static void *parserStackRealloc(void *pOld, sqlite3_uint64 newSize){
+ return sqlite3FaultSim(700) ? 0 : sqlite3_realloc(pOld, newSize);
+ }
+
/* Construct a new Expr object from a single token */
static Expr *tokenExpr(Parse *pParse, int op, Token t){
@@ -171527,8 +172870,8 @@ static void updateDeleteLimitError(
#define TK_TRUEFALSE 170
#define TK_ISNOT 171
#define TK_FUNCTION 172
-#define TK_UMINUS 173
-#define TK_UPLUS 174
+#define TK_UPLUS 173
+#define TK_UMINUS 174
#define TK_TRUTH 175
#define TK_REGISTER 176
#define TK_VECTOR 177
@@ -171537,8 +172880,9 @@ static void updateDeleteLimitError(
#define TK_ASTERISK 180
#define TK_SPAN 181
#define TK_ERROR 182
-#define TK_SPACE 183
-#define TK_ILLEGAL 184
+#define TK_QNUMBER 183
+#define TK_SPACE 184
+#define TK_ILLEGAL 185
#endif
/**************** End token definitions ***************************************/
@@ -171579,6 +172923,9 @@ static void updateDeleteLimitError(
** sqlite3ParserARG_STORE Code to store %extra_argument into yypParser
** sqlite3ParserARG_FETCH Code to extract %extra_argument from yypParser
** sqlite3ParserCTX_* As sqlite3ParserARG_ except for %extra_context
+** YYREALLOC Name of the realloc() function to use
+** YYFREE Name of the free() function to use
+** YYDYNSTACK True if stack space should be extended on heap
** YYERRORSYMBOL is the code number of the error symbol. If not
** defined, then do no error processing.
** YYNSTATE the combined number of states.
@@ -171592,37 +172939,39 @@ static void updateDeleteLimitError(
** YY_NO_ACTION The yy_action[] code for no-op
** YY_MIN_REDUCE Minimum value for reduce actions
** YY_MAX_REDUCE Maximum value for reduce actions
+** YY_MIN_DSTRCTR Minimum symbol value that has a destructor
+** YY_MAX_DSTRCTR Maximum symbol value that has a destructor
*/
#ifndef INTERFACE
# define INTERFACE 1
#endif
/************* Begin control #defines *****************************************/
#define YYCODETYPE unsigned short int
-#define YYNOCODE 319
+#define YYNOCODE 322
#define YYACTIONTYPE unsigned short int
#define YYWILDCARD 101
#define sqlite3ParserTOKENTYPE Token
typedef union {
int yyinit;
sqlite3ParserTOKENTYPE yy0;
- TriggerStep* yy33;
- Window* yy41;
- Select* yy47;
- SrcList* yy131;
- struct TrigEvent yy180;
- struct {int value; int mask;} yy231;
- IdList* yy254;
- u32 yy285;
- ExprList* yy322;
- Cte* yy385;
- int yy394;
- Upsert* yy444;
- u8 yy516;
- With* yy521;
- const char* yy522;
- Expr* yy528;
- OnOrUsing yy561;
- struct FrameBound yy595;
+ ExprList* yy14;
+ With* yy59;
+ Cte* yy67;
+ Upsert* yy122;
+ IdList* yy132;
+ int yy144;
+ const char* yy168;
+ SrcList* yy203;
+ Window* yy211;
+ OnOrUsing yy269;
+ struct TrigEvent yy286;
+ struct {int value; int mask;} yy383;
+ u32 yy391;
+ TriggerStep* yy427;
+ Expr* yy454;
+ u8 yy462;
+ struct FrameBound yy509;
+ Select* yy555;
} YYMINORTYPE;
#ifndef YYSTACKDEPTH
#define YYSTACKDEPTH 100
@@ -171632,24 +172981,29 @@ typedef union {
#define sqlite3ParserARG_PARAM
#define sqlite3ParserARG_FETCH
#define sqlite3ParserARG_STORE
+#define YYREALLOC parserStackRealloc
+#define YYFREE sqlite3_free
+#define YYDYNSTACK 1
#define sqlite3ParserCTX_SDECL Parse *pParse;
#define sqlite3ParserCTX_PDECL ,Parse *pParse
#define sqlite3ParserCTX_PARAM ,pParse
#define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse;
#define sqlite3ParserCTX_STORE yypParser->pParse=pParse;
#define YYFALLBACK 1
-#define YYNSTATE 579
-#define YYNRULE 405
-#define YYNRULE_WITH_ACTION 340
-#define YYNTOKEN 185
-#define YY_MAX_SHIFT 578
-#define YY_MIN_SHIFTREDUCE 838
-#define YY_MAX_SHIFTREDUCE 1242
-#define YY_ERROR_ACTION 1243
-#define YY_ACCEPT_ACTION 1244
-#define YY_NO_ACTION 1245
-#define YY_MIN_REDUCE 1246
-#define YY_MAX_REDUCE 1650
+#define YYNSTATE 583
+#define YYNRULE 409
+#define YYNRULE_WITH_ACTION 344
+#define YYNTOKEN 186
+#define YY_MAX_SHIFT 582
+#define YY_MIN_SHIFTREDUCE 845
+#define YY_MAX_SHIFTREDUCE 1253
+#define YY_ERROR_ACTION 1254
+#define YY_ACCEPT_ACTION 1255
+#define YY_NO_ACTION 1256
+#define YY_MIN_REDUCE 1257
+#define YY_MAX_REDUCE 1665
+#define YY_MIN_DSTRCTR 205
+#define YY_MAX_DSTRCTR 319
/************* End control #defines *******************************************/
#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))
@@ -171665,6 +173019,22 @@ typedef union {
# define yytestcase(X)
#endif
+/* Macro to determine if stack space has the ability to grow using
+** heap memory.
+*/
+#if YYSTACKDEPTH<=0 || YYDYNSTACK
+# define YYGROWABLESTACK 1
+#else
+# define YYGROWABLESTACK 0
+#endif
+
+/* Guarantee a minimum number of initial stack slots.
+*/
+#if YYSTACKDEPTH<=0
+# undef YYSTACKDEPTH
+# define YYSTACKDEPTH 2 /* Need a minimum stack size */
+#endif
+
/* Next are the tables used to determine what action to take based on the
** current state and lookahead token. These tables are used to implement
@@ -171716,619 +173086,630 @@ typedef union {
** yy_default[] Default action for each state.
**
*********** Begin parsing tables **********************************************/
-#define YY_ACTTAB_COUNT (2100)
+#define YY_ACTTAB_COUNT (2142)
static const YYACTIONTYPE yy_action[] = {
- /* 0 */ 572, 210, 572, 119, 116, 231, 572, 119, 116, 231,
- /* 10 */ 572, 1317, 379, 1296, 410, 566, 566, 566, 572, 411,
- /* 20 */ 380, 1317, 1279, 42, 42, 42, 42, 210, 1529, 72,
- /* 30 */ 72, 974, 421, 42, 42, 495, 305, 281, 305, 975,
- /* 40 */ 399, 72, 72, 126, 127, 81, 1217, 1217, 1054, 1057,
- /* 50 */ 1044, 1044, 124, 124, 125, 125, 125, 125, 480, 411,
- /* 60 */ 1244, 1, 1, 578, 2, 1248, 554, 119, 116, 231,
- /* 70 */ 319, 484, 147, 484, 528, 119, 116, 231, 533, 1330,
- /* 80 */ 419, 527, 143, 126, 127, 81, 1217, 1217, 1054, 1057,
- /* 90 */ 1044, 1044, 124, 124, 125, 125, 125, 125, 119, 116,
- /* 100 */ 231, 329, 123, 123, 123, 123, 122, 122, 121, 121,
- /* 110 */ 121, 120, 117, 448, 286, 286, 286, 286, 446, 446,
- /* 120 */ 446, 1568, 378, 1570, 1193, 377, 1164, 569, 1164, 569,
- /* 130 */ 411, 1568, 541, 261, 228, 448, 102, 146, 453, 318,
- /* 140 */ 563, 242, 123, 123, 123, 123, 122, 122, 121, 121,
- /* 150 */ 121, 120, 117, 448, 126, 127, 81, 1217, 1217, 1054,
- /* 160 */ 1057, 1044, 1044, 124, 124, 125, 125, 125, 125, 143,
- /* 170 */ 296, 1193, 341, 452, 121, 121, 121, 120, 117, 448,
- /* 180 */ 128, 1193, 1194, 1193, 149, 445, 444, 572, 120, 117,
- /* 190 */ 448, 125, 125, 125, 125, 118, 123, 123, 123, 123,
- /* 200 */ 122, 122, 121, 121, 121, 120, 117, 448, 458, 114,
- /* 210 */ 13, 13, 550, 123, 123, 123, 123, 122, 122, 121,
- /* 220 */ 121, 121, 120, 117, 448, 424, 318, 563, 1193, 1194,
- /* 230 */ 1193, 150, 1225, 411, 1225, 125, 125, 125, 125, 123,
- /* 240 */ 123, 123, 123, 122, 122, 121, 121, 121, 120, 117,
- /* 250 */ 448, 469, 344, 1041, 1041, 1055, 1058, 126, 127, 81,
- /* 260 */ 1217, 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125,
- /* 270 */ 125, 125, 1282, 526, 224, 1193, 572, 411, 226, 519,
- /* 280 */ 177, 83, 84, 123, 123, 123, 123, 122, 122, 121,
- /* 290 */ 121, 121, 120, 117, 448, 1010, 16, 16, 1193, 134,
- /* 300 */ 134, 126, 127, 81, 1217, 1217, 1054, 1057, 1044, 1044,
- /* 310 */ 124, 124, 125, 125, 125, 125, 123, 123, 123, 123,
- /* 320 */ 122, 122, 121, 121, 121, 120, 117, 448, 1045, 550,
- /* 330 */ 1193, 375, 1193, 1194, 1193, 254, 1438, 401, 508, 505,
- /* 340 */ 504, 112, 564, 570, 4, 929, 929, 435, 503, 342,
- /* 350 */ 464, 330, 362, 396, 1238, 1193, 1194, 1193, 567, 572,
- /* 360 */ 123, 123, 123, 123, 122, 122, 121, 121, 121, 120,
- /* 370 */ 117, 448, 286, 286, 371, 1581, 1607, 445, 444, 155,
- /* 380 */ 411, 449, 72, 72, 1289, 569, 1222, 1193, 1194, 1193,
- /* 390 */ 86, 1224, 273, 561, 547, 520, 520, 572, 99, 1223,
- /* 400 */ 6, 1281, 476, 143, 126, 127, 81, 1217, 1217, 1054,
- /* 410 */ 1057, 1044, 1044, 124, 124, 125, 125, 125, 125, 554,
- /* 420 */ 13, 13, 1031, 511, 1225, 1193, 1225, 553, 110, 110,
- /* 430 */ 224, 572, 1239, 177, 572, 429, 111, 199, 449, 573,
- /* 440 */ 449, 432, 1555, 1019, 327, 555, 1193, 272, 289, 370,
- /* 450 */ 514, 365, 513, 259, 72, 72, 547, 72, 72, 361,
- /* 460 */ 318, 563, 1613, 123, 123, 123, 123, 122, 122, 121,
- /* 470 */ 121, 121, 120, 117, 448, 1019, 1019, 1021, 1022, 28,
- /* 480 */ 286, 286, 1193, 1194, 1193, 1159, 572, 1612, 411, 904,
- /* 490 */ 192, 554, 358, 569, 554, 940, 537, 521, 1159, 437,
- /* 500 */ 415, 1159, 556, 1193, 1194, 1193, 572, 548, 548, 52,
- /* 510 */ 52, 216, 126, 127, 81, 1217, 1217, 1054, 1057, 1044,
- /* 520 */ 1044, 124, 124, 125, 125, 125, 125, 1193, 478, 136,
- /* 530 */ 136, 411, 286, 286, 1493, 509, 122, 122, 121, 121,
- /* 540 */ 121, 120, 117, 448, 1010, 569, 522, 219, 545, 545,
- /* 550 */ 318, 563, 143, 6, 536, 126, 127, 81, 1217, 1217,
- /* 560 */ 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, 125,
- /* 570 */ 1557, 123, 123, 123, 123, 122, 122, 121, 121, 121,
- /* 580 */ 120, 117, 448, 489, 1193, 1194, 1193, 486, 283, 1270,
- /* 590 */ 960, 254, 1193, 375, 508, 505, 504, 1193, 342, 574,
- /* 600 */ 1193, 574, 411, 294, 503, 960, 879, 193, 484, 318,
- /* 610 */ 563, 386, 292, 382, 123, 123, 123, 123, 122, 122,
- /* 620 */ 121, 121, 121, 120, 117, 448, 126, 127, 81, 1217,
- /* 630 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
- /* 640 */ 125, 411, 396, 1139, 1193, 872, 101, 286, 286, 1193,
- /* 650 */ 1194, 1193, 375, 1096, 1193, 1194, 1193, 1193, 1194, 1193,
- /* 660 */ 569, 459, 33, 375, 235, 126, 127, 81, 1217, 1217,
- /* 670 */ 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, 125,
- /* 680 */ 1437, 962, 572, 230, 961, 123, 123, 123, 123, 122,
- /* 690 */ 122, 121, 121, 121, 120, 117, 448, 1159, 230, 1193,
- /* 700 */ 158, 1193, 1194, 1193, 1556, 13, 13, 303, 960, 1233,
- /* 710 */ 1159, 154, 411, 1159, 375, 1584, 1177, 5, 371, 1581,
- /* 720 */ 431, 1239, 3, 960, 123, 123, 123, 123, 122, 122,
- /* 730 */ 121, 121, 121, 120, 117, 448, 126, 127, 81, 1217,
- /* 740 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
- /* 750 */ 125, 411, 210, 571, 1193, 1032, 1193, 1194, 1193, 1193,
- /* 760 */ 390, 855, 156, 1555, 376, 404, 1101, 1101, 492, 572,
- /* 770 */ 469, 344, 1322, 1322, 1555, 126, 127, 81, 1217, 1217,
- /* 780 */ 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, 125,
- /* 790 */ 130, 572, 13, 13, 532, 123, 123, 123, 123, 122,
- /* 800 */ 122, 121, 121, 121, 120, 117, 448, 304, 572, 457,
- /* 810 */ 229, 1193, 1194, 1193, 13, 13, 1193, 1194, 1193, 1300,
- /* 820 */ 467, 1270, 411, 1320, 1320, 1555, 1015, 457, 456, 436,
- /* 830 */ 301, 72, 72, 1268, 123, 123, 123, 123, 122, 122,
- /* 840 */ 121, 121, 121, 120, 117, 448, 126, 127, 81, 1217,
- /* 850 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
- /* 860 */ 125, 411, 384, 1076, 1159, 286, 286, 421, 314, 280,
- /* 870 */ 280, 287, 287, 461, 408, 407, 1539, 1159, 569, 572,
- /* 880 */ 1159, 1196, 569, 409, 569, 126, 127, 81, 1217, 1217,
- /* 890 */ 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, 125,
- /* 900 */ 457, 1485, 13, 13, 1541, 123, 123, 123, 123, 122,
- /* 910 */ 122, 121, 121, 121, 120, 117, 448, 202, 572, 462,
- /* 920 */ 1587, 578, 2, 1248, 843, 844, 845, 1563, 319, 409,
- /* 930 */ 147, 6, 411, 257, 256, 255, 208, 1330, 9, 1196,
- /* 940 */ 264, 72, 72, 1436, 123, 123, 123, 123, 122, 122,
- /* 950 */ 121, 121, 121, 120, 117, 448, 126, 127, 81, 1217,
- /* 960 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
- /* 970 */ 125, 572, 286, 286, 572, 1213, 411, 577, 315, 1248,
- /* 980 */ 421, 371, 1581, 356, 319, 569, 147, 495, 529, 1644,
- /* 990 */ 397, 935, 495, 1330, 71, 71, 934, 72, 72, 242,
- /* 1000 */ 1328, 105, 81, 1217, 1217, 1054, 1057, 1044, 1044, 124,
- /* 1010 */ 124, 125, 125, 125, 125, 123, 123, 123, 123, 122,
- /* 1020 */ 122, 121, 121, 121, 120, 117, 448, 1117, 286, 286,
- /* 1030 */ 1422, 452, 1528, 1213, 443, 286, 286, 1492, 1355, 313,
- /* 1040 */ 478, 569, 1118, 454, 351, 495, 354, 1266, 569, 209,
- /* 1050 */ 572, 418, 179, 572, 1031, 242, 385, 1119, 523, 123,
- /* 1060 */ 123, 123, 123, 122, 122, 121, 121, 121, 120, 117,
- /* 1070 */ 448, 1020, 108, 72, 72, 1019, 13, 13, 915, 572,
- /* 1080 */ 1498, 572, 286, 286, 98, 530, 1537, 452, 916, 1334,
- /* 1090 */ 1329, 203, 411, 286, 286, 569, 152, 211, 1498, 1500,
- /* 1100 */ 426, 569, 56, 56, 57, 57, 569, 1019, 1019, 1021,
- /* 1110 */ 447, 572, 411, 531, 12, 297, 126, 127, 81, 1217,
- /* 1120 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
- /* 1130 */ 125, 572, 411, 867, 15, 15, 126, 127, 81, 1217,
- /* 1140 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
- /* 1150 */ 125, 373, 529, 264, 44, 44, 126, 115, 81, 1217,
- /* 1160 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
- /* 1170 */ 125, 1498, 478, 1271, 417, 123, 123, 123, 123, 122,
- /* 1180 */ 122, 121, 121, 121, 120, 117, 448, 205, 1213, 495,
- /* 1190 */ 430, 867, 468, 322, 495, 123, 123, 123, 123, 122,
- /* 1200 */ 122, 121, 121, 121, 120, 117, 448, 572, 557, 1140,
- /* 1210 */ 1642, 1422, 1642, 543, 572, 123, 123, 123, 123, 122,
- /* 1220 */ 122, 121, 121, 121, 120, 117, 448, 572, 1422, 572,
- /* 1230 */ 13, 13, 542, 323, 1325, 411, 334, 58, 58, 349,
- /* 1240 */ 1422, 1170, 326, 286, 286, 549, 1213, 300, 895, 530,
- /* 1250 */ 45, 45, 59, 59, 1140, 1643, 569, 1643, 565, 417,
- /* 1260 */ 127, 81, 1217, 1217, 1054, 1057, 1044, 1044, 124, 124,
- /* 1270 */ 125, 125, 125, 125, 1367, 373, 500, 290, 1193, 512,
- /* 1280 */ 1366, 427, 394, 394, 393, 275, 391, 896, 1138, 852,
- /* 1290 */ 478, 258, 1422, 1170, 463, 1159, 12, 331, 428, 333,
- /* 1300 */ 1117, 460, 236, 258, 325, 460, 544, 1544, 1159, 1098,
- /* 1310 */ 491, 1159, 324, 1098, 440, 1118, 335, 516, 123, 123,
- /* 1320 */ 123, 123, 122, 122, 121, 121, 121, 120, 117, 448,
- /* 1330 */ 1119, 318, 563, 1138, 572, 1193, 1194, 1193, 112, 564,
- /* 1340 */ 201, 4, 238, 433, 935, 490, 285, 228, 1517, 934,
- /* 1350 */ 170, 560, 572, 142, 1516, 567, 572, 60, 60, 572,
- /* 1360 */ 416, 572, 441, 572, 535, 302, 875, 8, 487, 572,
- /* 1370 */ 237, 572, 416, 572, 485, 61, 61, 572, 449, 62,
- /* 1380 */ 62, 332, 63, 63, 46, 46, 47, 47, 361, 572,
- /* 1390 */ 561, 572, 48, 48, 50, 50, 51, 51, 572, 295,
- /* 1400 */ 64, 64, 482, 295, 539, 412, 471, 1031, 572, 538,
- /* 1410 */ 318, 563, 65, 65, 66, 66, 409, 475, 572, 1031,
- /* 1420 */ 572, 14, 14, 875, 1020, 110, 110, 409, 1019, 572,
- /* 1430 */ 474, 67, 67, 111, 455, 449, 573, 449, 98, 317,
- /* 1440 */ 1019, 132, 132, 133, 133, 572, 1561, 572, 974, 409,
- /* 1450 */ 6, 1562, 68, 68, 1560, 6, 975, 572, 6, 1559,
- /* 1460 */ 1019, 1019, 1021, 6, 346, 218, 101, 531, 53, 53,
- /* 1470 */ 69, 69, 1019, 1019, 1021, 1022, 28, 1586, 1181, 451,
- /* 1480 */ 70, 70, 290, 87, 215, 31, 1363, 394, 394, 393,
- /* 1490 */ 275, 391, 350, 109, 852, 107, 572, 112, 564, 483,
- /* 1500 */ 4, 1212, 572, 239, 153, 572, 39, 236, 1299, 325,
- /* 1510 */ 112, 564, 1298, 4, 567, 572, 32, 324, 572, 54,
- /* 1520 */ 54, 572, 1135, 353, 398, 165, 165, 567, 166, 166,
- /* 1530 */ 572, 291, 355, 572, 17, 357, 572, 449, 77, 77,
- /* 1540 */ 1313, 55, 55, 1297, 73, 73, 572, 238, 470, 561,
- /* 1550 */ 449, 472, 364, 135, 135, 170, 74, 74, 142, 163,
- /* 1560 */ 163, 374, 561, 539, 572, 321, 572, 886, 540, 137,
- /* 1570 */ 137, 339, 1353, 422, 298, 237, 539, 572, 1031, 572,
- /* 1580 */ 340, 538, 101, 369, 110, 110, 162, 131, 131, 164,
- /* 1590 */ 164, 1031, 111, 368, 449, 573, 449, 110, 110, 1019,
- /* 1600 */ 157, 157, 141, 141, 572, 111, 572, 449, 573, 449,
- /* 1610 */ 412, 288, 1019, 572, 882, 318, 563, 572, 219, 572,
- /* 1620 */ 241, 1012, 477, 263, 263, 894, 893, 140, 140, 138,
- /* 1630 */ 138, 1019, 1019, 1021, 1022, 28, 139, 139, 525, 455,
- /* 1640 */ 76, 76, 78, 78, 1019, 1019, 1021, 1022, 28, 1181,
- /* 1650 */ 451, 572, 1083, 290, 112, 564, 1575, 4, 394, 394,
- /* 1660 */ 393, 275, 391, 572, 1023, 852, 572, 479, 345, 263,
- /* 1670 */ 101, 567, 882, 1376, 75, 75, 1421, 501, 236, 260,
- /* 1680 */ 325, 112, 564, 359, 4, 101, 43, 43, 324, 49,
- /* 1690 */ 49, 901, 902, 161, 449, 101, 977, 978, 567, 1079,
- /* 1700 */ 1349, 260, 965, 932, 263, 114, 561, 1095, 517, 1095,
- /* 1710 */ 1083, 1094, 865, 1094, 151, 933, 1144, 114, 238, 1361,
- /* 1720 */ 558, 449, 1023, 559, 1426, 1278, 170, 1269, 1257, 142,
- /* 1730 */ 1601, 1256, 1258, 561, 1594, 1031, 496, 278, 213, 1346,
- /* 1740 */ 310, 110, 110, 939, 311, 312, 237, 11, 234, 111,
- /* 1750 */ 221, 449, 573, 449, 293, 395, 1019, 1408, 337, 1403,
- /* 1760 */ 1396, 338, 1031, 299, 343, 1413, 1412, 481, 110, 110,
- /* 1770 */ 506, 402, 225, 1296, 206, 367, 111, 1358, 449, 573,
- /* 1780 */ 449, 412, 1359, 1019, 1489, 1488, 318, 563, 1019, 1019,
- /* 1790 */ 1021, 1022, 28, 562, 207, 220, 80, 564, 389, 4,
- /* 1800 */ 1597, 1357, 552, 1356, 1233, 181, 267, 232, 1536, 1534,
- /* 1810 */ 455, 1230, 420, 567, 82, 1019, 1019, 1021, 1022, 28,
- /* 1820 */ 86, 217, 85, 1494, 190, 175, 183, 465, 185, 466,
- /* 1830 */ 36, 1409, 186, 187, 188, 499, 449, 244, 37, 99,
- /* 1840 */ 400, 1415, 1414, 488, 1417, 194, 473, 403, 561, 1483,
- /* 1850 */ 248, 92, 1505, 494, 198, 279, 112, 564, 250, 4,
- /* 1860 */ 348, 497, 405, 352, 1259, 251, 252, 515, 1316, 434,
- /* 1870 */ 1315, 1314, 94, 567, 1307, 886, 1306, 1031, 226, 406,
- /* 1880 */ 1611, 1610, 438, 110, 110, 1580, 1286, 524, 439, 308,
- /* 1890 */ 266, 111, 1285, 449, 573, 449, 449, 309, 1019, 366,
- /* 1900 */ 1284, 1609, 265, 1566, 1565, 442, 372, 1381, 561, 129,
- /* 1910 */ 550, 1380, 10, 1470, 383, 106, 316, 551, 100, 35,
- /* 1920 */ 534, 575, 212, 1339, 381, 387, 1187, 1338, 274, 276,
- /* 1930 */ 1019, 1019, 1021, 1022, 28, 277, 413, 1031, 576, 1254,
- /* 1940 */ 388, 1521, 1249, 110, 110, 167, 1522, 168, 148, 1520,
- /* 1950 */ 1519, 111, 306, 449, 573, 449, 222, 223, 1019, 839,
- /* 1960 */ 169, 79, 450, 214, 414, 233, 320, 145, 1093, 1091,
- /* 1970 */ 328, 182, 171, 1212, 918, 184, 240, 336, 243, 1107,
- /* 1980 */ 189, 172, 173, 423, 425, 88, 180, 191, 89, 90,
- /* 1990 */ 1019, 1019, 1021, 1022, 28, 91, 174, 1110, 245, 1106,
- /* 2000 */ 246, 159, 18, 247, 347, 1099, 263, 195, 1227, 493,
- /* 2010 */ 249, 196, 38, 854, 498, 368, 253, 360, 897, 197,
- /* 2020 */ 502, 93, 19, 20, 507, 884, 363, 510, 95, 307,
- /* 2030 */ 160, 96, 518, 97, 1175, 1060, 1146, 40, 21, 227,
- /* 2040 */ 176, 1145, 282, 284, 969, 200, 963, 114, 262, 1165,
- /* 2050 */ 22, 23, 24, 1161, 1169, 25, 1163, 1150, 34, 26,
- /* 2060 */ 1168, 546, 27, 204, 101, 103, 104, 1074, 7, 1061,
- /* 2070 */ 1059, 1063, 1116, 1064, 1115, 268, 269, 29, 41, 270,
- /* 2080 */ 1024, 866, 113, 30, 568, 392, 1183, 144, 178, 1182,
- /* 2090 */ 271, 928, 1245, 1245, 1245, 1245, 1245, 1245, 1245, 1602,
+ /* 0 */ 576, 128, 125, 232, 1622, 549, 576, 1290, 1281, 576,
+ /* 10 */ 328, 576, 1300, 212, 576, 128, 125, 232, 578, 412,
+ /* 20 */ 578, 391, 1542, 51, 51, 523, 405, 1293, 529, 51,
+ /* 30 */ 51, 983, 51, 51, 81, 81, 1107, 61, 61, 984,
+ /* 40 */ 1107, 1292, 380, 135, 136, 90, 1228, 1228, 1063, 1066,
+ /* 50 */ 1053, 1053, 133, 133, 134, 134, 134, 134, 1577, 412,
+ /* 60 */ 287, 287, 7, 287, 287, 422, 1050, 1050, 1064, 1067,
+ /* 70 */ 289, 556, 492, 573, 524, 561, 573, 497, 561, 482,
+ /* 80 */ 530, 262, 229, 135, 136, 90, 1228, 1228, 1063, 1066,
+ /* 90 */ 1053, 1053, 133, 133, 134, 134, 134, 134, 128, 125,
+ /* 100 */ 232, 1506, 132, 132, 132, 132, 131, 131, 130, 130,
+ /* 110 */ 130, 129, 126, 450, 1204, 1255, 1, 1, 582, 2,
+ /* 120 */ 1259, 1571, 420, 1582, 379, 320, 1174, 153, 1174, 1584,
+ /* 130 */ 412, 378, 1582, 543, 1341, 330, 111, 570, 570, 570,
+ /* 140 */ 293, 1054, 132, 132, 132, 132, 131, 131, 130, 130,
+ /* 150 */ 130, 129, 126, 450, 135, 136, 90, 1228, 1228, 1063,
+ /* 160 */ 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, 287,
+ /* 170 */ 287, 1204, 1205, 1204, 255, 287, 287, 510, 507, 506,
+ /* 180 */ 137, 455, 573, 212, 561, 447, 446, 505, 573, 1616,
+ /* 190 */ 561, 134, 134, 134, 134, 127, 400, 243, 132, 132,
+ /* 200 */ 132, 132, 131, 131, 130, 130, 130, 129, 126, 450,
+ /* 210 */ 282, 471, 345, 132, 132, 132, 132, 131, 131, 130,
+ /* 220 */ 130, 130, 129, 126, 450, 574, 155, 936, 936, 454,
+ /* 230 */ 227, 521, 1236, 412, 1236, 134, 134, 134, 134, 132,
+ /* 240 */ 132, 132, 132, 131, 131, 130, 130, 130, 129, 126,
+ /* 250 */ 450, 130, 130, 130, 129, 126, 450, 135, 136, 90,
+ /* 260 */ 1228, 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134,
+ /* 270 */ 134, 134, 128, 125, 232, 450, 576, 412, 397, 1249,
+ /* 280 */ 180, 92, 93, 132, 132, 132, 132, 131, 131, 130,
+ /* 290 */ 130, 130, 129, 126, 450, 381, 387, 1204, 383, 81,
+ /* 300 */ 81, 135, 136, 90, 1228, 1228, 1063, 1066, 1053, 1053,
+ /* 310 */ 133, 133, 134, 134, 134, 134, 132, 132, 132, 132,
+ /* 320 */ 131, 131, 130, 130, 130, 129, 126, 450, 131, 131,
+ /* 330 */ 130, 130, 130, 129, 126, 450, 556, 1204, 302, 319,
+ /* 340 */ 567, 121, 568, 480, 4, 555, 1149, 1657, 1628, 1657,
+ /* 350 */ 45, 128, 125, 232, 1204, 1205, 1204, 1250, 571, 1169,
+ /* 360 */ 132, 132, 132, 132, 131, 131, 130, 130, 130, 129,
+ /* 370 */ 126, 450, 1169, 287, 287, 1169, 1019, 576, 422, 1019,
+ /* 380 */ 412, 451, 1602, 582, 2, 1259, 573, 44, 561, 95,
+ /* 390 */ 320, 110, 153, 565, 1204, 1205, 1204, 522, 522, 1341,
+ /* 400 */ 81, 81, 7, 44, 135, 136, 90, 1228, 1228, 1063,
+ /* 410 */ 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, 295,
+ /* 420 */ 1149, 1658, 1040, 1658, 1204, 1147, 319, 567, 119, 119,
+ /* 430 */ 343, 466, 331, 343, 287, 287, 120, 556, 451, 577,
+ /* 440 */ 451, 1169, 1169, 1028, 319, 567, 438, 573, 210, 561,
+ /* 450 */ 1339, 1451, 546, 531, 1169, 1169, 1598, 1169, 1169, 416,
+ /* 460 */ 319, 567, 243, 132, 132, 132, 132, 131, 131, 130,
+ /* 470 */ 130, 130, 129, 126, 450, 1028, 1028, 1030, 1031, 35,
+ /* 480 */ 44, 1204, 1205, 1204, 472, 287, 287, 1328, 412, 1307,
+ /* 490 */ 372, 1595, 359, 225, 454, 1204, 195, 1328, 573, 1147,
+ /* 500 */ 561, 1333, 1333, 274, 576, 1188, 576, 340, 46, 196,
+ /* 510 */ 537, 217, 135, 136, 90, 1228, 1228, 1063, 1066, 1053,
+ /* 520 */ 1053, 133, 133, 134, 134, 134, 134, 19, 19, 19,
+ /* 530 */ 19, 412, 581, 1204, 1259, 511, 1204, 319, 567, 320,
+ /* 540 */ 944, 153, 425, 491, 430, 943, 1204, 488, 1341, 1450,
+ /* 550 */ 532, 1277, 1204, 1205, 1204, 135, 136, 90, 1228, 1228,
+ /* 560 */ 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, 134,
+ /* 570 */ 575, 132, 132, 132, 132, 131, 131, 130, 130, 130,
+ /* 580 */ 129, 126, 450, 287, 287, 528, 287, 287, 372, 1595,
+ /* 590 */ 1204, 1205, 1204, 1204, 1205, 1204, 573, 486, 561, 573,
+ /* 600 */ 889, 561, 412, 1204, 1205, 1204, 886, 40, 22, 22,
+ /* 610 */ 220, 243, 525, 1449, 132, 132, 132, 132, 131, 131,
+ /* 620 */ 130, 130, 130, 129, 126, 450, 135, 136, 90, 1228,
+ /* 630 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134,
+ /* 640 */ 134, 412, 180, 454, 1204, 879, 255, 287, 287, 510,
+ /* 650 */ 507, 506, 372, 1595, 1568, 1331, 1331, 576, 889, 505,
+ /* 660 */ 573, 44, 561, 559, 1207, 135, 136, 90, 1228, 1228,
+ /* 670 */ 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, 134,
+ /* 680 */ 81, 81, 422, 576, 377, 132, 132, 132, 132, 131,
+ /* 690 */ 131, 130, 130, 130, 129, 126, 450, 297, 287, 287,
+ /* 700 */ 460, 1204, 1205, 1204, 1204, 534, 19, 19, 448, 448,
+ /* 710 */ 448, 573, 412, 561, 230, 436, 1187, 535, 319, 567,
+ /* 720 */ 363, 432, 1207, 1435, 132, 132, 132, 132, 131, 131,
+ /* 730 */ 130, 130, 130, 129, 126, 450, 135, 136, 90, 1228,
+ /* 740 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134,
+ /* 750 */ 134, 412, 211, 949, 1169, 1041, 1110, 1110, 494, 547,
+ /* 760 */ 547, 1204, 1205, 1204, 7, 539, 1570, 1169, 376, 576,
+ /* 770 */ 1169, 5, 1204, 486, 3, 135, 136, 90, 1228, 1228,
+ /* 780 */ 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, 134,
+ /* 790 */ 576, 513, 19, 19, 427, 132, 132, 132, 132, 131,
+ /* 800 */ 131, 130, 130, 130, 129, 126, 450, 305, 1204, 433,
+ /* 810 */ 225, 1204, 385, 19, 19, 273, 290, 371, 516, 366,
+ /* 820 */ 515, 260, 412, 538, 1568, 549, 1024, 362, 437, 1204,
+ /* 830 */ 1205, 1204, 902, 1552, 132, 132, 132, 132, 131, 131,
+ /* 840 */ 130, 130, 130, 129, 126, 450, 135, 136, 90, 1228,
+ /* 850 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134,
+ /* 860 */ 134, 412, 1435, 514, 1281, 1204, 1205, 1204, 1204, 1205,
+ /* 870 */ 1204, 903, 48, 342, 1568, 1568, 1279, 1627, 1568, 911,
+ /* 880 */ 576, 129, 126, 450, 110, 135, 136, 90, 1228, 1228,
+ /* 890 */ 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, 134,
+ /* 900 */ 265, 576, 459, 19, 19, 132, 132, 132, 132, 131,
+ /* 910 */ 131, 130, 130, 130, 129, 126, 450, 1345, 204, 576,
+ /* 920 */ 459, 458, 50, 47, 19, 19, 49, 434, 1105, 573,
+ /* 930 */ 497, 561, 412, 428, 108, 1224, 1569, 1554, 376, 205,
+ /* 940 */ 550, 550, 81, 81, 132, 132, 132, 132, 131, 131,
+ /* 950 */ 130, 130, 130, 129, 126, 450, 135, 136, 90, 1228,
+ /* 960 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134,
+ /* 970 */ 134, 480, 576, 1204, 576, 1541, 412, 1435, 969, 315,
+ /* 980 */ 1659, 398, 284, 497, 969, 893, 1569, 1569, 376, 376,
+ /* 990 */ 1569, 461, 376, 1224, 459, 80, 80, 81, 81, 497,
+ /* 1000 */ 374, 114, 90, 1228, 1228, 1063, 1066, 1053, 1053, 133,
+ /* 1010 */ 133, 134, 134, 134, 134, 132, 132, 132, 132, 131,
+ /* 1020 */ 131, 130, 130, 130, 129, 126, 450, 1204, 1505, 576,
+ /* 1030 */ 1204, 1205, 1204, 1366, 316, 486, 281, 281, 497, 431,
+ /* 1040 */ 557, 288, 288, 402, 1340, 471, 345, 298, 429, 573,
+ /* 1050 */ 576, 561, 81, 81, 573, 374, 561, 971, 386, 132,
+ /* 1060 */ 132, 132, 132, 131, 131, 130, 130, 130, 129, 126,
+ /* 1070 */ 450, 231, 117, 81, 81, 287, 287, 231, 287, 287,
+ /* 1080 */ 576, 1511, 576, 1336, 1204, 1205, 1204, 139, 573, 556,
+ /* 1090 */ 561, 573, 412, 561, 441, 456, 969, 213, 558, 1511,
+ /* 1100 */ 1513, 1550, 969, 143, 143, 145, 145, 1368, 314, 478,
+ /* 1110 */ 444, 970, 412, 850, 851, 852, 135, 136, 90, 1228,
+ /* 1120 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134,
+ /* 1130 */ 134, 357, 412, 397, 1148, 304, 135, 136, 90, 1228,
+ /* 1140 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134,
+ /* 1150 */ 134, 1575, 323, 6, 862, 7, 135, 124, 90, 1228,
+ /* 1160 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134,
+ /* 1170 */ 134, 409, 408, 1511, 212, 132, 132, 132, 132, 131,
+ /* 1180 */ 131, 130, 130, 130, 129, 126, 450, 411, 118, 1204,
+ /* 1190 */ 116, 10, 352, 265, 355, 132, 132, 132, 132, 131,
+ /* 1200 */ 131, 130, 130, 130, 129, 126, 450, 576, 324, 306,
+ /* 1210 */ 576, 306, 1250, 469, 158, 132, 132, 132, 132, 131,
+ /* 1220 */ 131, 130, 130, 130, 129, 126, 450, 207, 1224, 1126,
+ /* 1230 */ 65, 65, 470, 66, 66, 412, 447, 446, 882, 531,
+ /* 1240 */ 335, 258, 257, 256, 1127, 1233, 1204, 1205, 1204, 327,
+ /* 1250 */ 1235, 874, 159, 576, 16, 480, 1085, 1040, 1234, 1128,
+ /* 1260 */ 136, 90, 1228, 1228, 1063, 1066, 1053, 1053, 133, 133,
+ /* 1270 */ 134, 134, 134, 134, 1029, 576, 81, 81, 1028, 1040,
+ /* 1280 */ 922, 576, 463, 1236, 576, 1236, 1224, 502, 107, 1435,
+ /* 1290 */ 923, 6, 576, 410, 1498, 882, 1029, 480, 21, 21,
+ /* 1300 */ 1028, 332, 1380, 334, 53, 53, 497, 81, 81, 874,
+ /* 1310 */ 1028, 1028, 1030, 445, 259, 19, 19, 533, 132, 132,
+ /* 1320 */ 132, 132, 131, 131, 130, 130, 130, 129, 126, 450,
+ /* 1330 */ 551, 301, 1028, 1028, 1030, 107, 532, 545, 121, 568,
+ /* 1340 */ 1188, 4, 1126, 1576, 449, 576, 462, 7, 1282, 418,
+ /* 1350 */ 462, 350, 1435, 576, 518, 571, 544, 1127, 121, 568,
+ /* 1360 */ 442, 4, 1188, 464, 533, 1180, 1223, 9, 67, 67,
+ /* 1370 */ 487, 576, 1128, 303, 410, 571, 54, 54, 451, 576,
+ /* 1380 */ 123, 944, 576, 417, 576, 333, 943, 1379, 576, 236,
+ /* 1390 */ 565, 576, 1574, 564, 68, 68, 7, 576, 451, 362,
+ /* 1400 */ 419, 182, 69, 69, 541, 70, 70, 71, 71, 540,
+ /* 1410 */ 565, 72, 72, 484, 55, 55, 473, 1180, 296, 1040,
+ /* 1420 */ 56, 56, 296, 493, 541, 119, 119, 410, 1573, 542,
+ /* 1430 */ 569, 418, 7, 120, 1244, 451, 577, 451, 465, 1040,
+ /* 1440 */ 1028, 576, 1557, 552, 476, 119, 119, 527, 259, 121,
+ /* 1450 */ 568, 240, 4, 120, 576, 451, 577, 451, 576, 477,
+ /* 1460 */ 1028, 576, 156, 576, 57, 57, 571, 576, 286, 229,
+ /* 1470 */ 410, 336, 1028, 1028, 1030, 1031, 35, 59, 59, 219,
+ /* 1480 */ 983, 60, 60, 220, 73, 73, 74, 74, 984, 451,
+ /* 1490 */ 75, 75, 1028, 1028, 1030, 1031, 35, 96, 216, 291,
+ /* 1500 */ 552, 565, 1188, 318, 395, 395, 394, 276, 392, 576,
+ /* 1510 */ 485, 859, 474, 1311, 410, 541, 576, 417, 1530, 1144,
+ /* 1520 */ 540, 399, 1188, 292, 237, 1153, 326, 38, 23, 576,
+ /* 1530 */ 1040, 576, 20, 20, 325, 299, 119, 119, 164, 76,
+ /* 1540 */ 76, 1529, 121, 568, 120, 4, 451, 577, 451, 203,
+ /* 1550 */ 576, 1028, 141, 141, 142, 142, 576, 322, 39, 571,
+ /* 1560 */ 341, 1021, 110, 264, 239, 901, 900, 423, 242, 908,
+ /* 1570 */ 909, 370, 173, 77, 77, 43, 479, 1310, 264, 62,
+ /* 1580 */ 62, 369, 451, 1028, 1028, 1030, 1031, 35, 1601, 1192,
+ /* 1590 */ 453, 1092, 238, 291, 565, 163, 1309, 110, 395, 395,
+ /* 1600 */ 394, 276, 392, 986, 987, 859, 481, 346, 264, 110,
+ /* 1610 */ 1032, 489, 576, 1188, 503, 1088, 261, 261, 237, 576,
+ /* 1620 */ 326, 121, 568, 1040, 4, 347, 1376, 413, 325, 119,
+ /* 1630 */ 119, 948, 319, 567, 351, 78, 78, 120, 571, 451,
+ /* 1640 */ 577, 451, 79, 79, 1028, 354, 356, 576, 360, 1092,
+ /* 1650 */ 110, 576, 974, 942, 264, 123, 457, 358, 239, 576,
+ /* 1660 */ 519, 451, 939, 1104, 123, 1104, 173, 576, 1032, 43,
+ /* 1670 */ 63, 63, 1324, 565, 168, 168, 1028, 1028, 1030, 1031,
+ /* 1680 */ 35, 576, 169, 169, 1308, 872, 238, 157, 1589, 576,
+ /* 1690 */ 86, 86, 365, 89, 568, 375, 4, 1103, 941, 1103,
+ /* 1700 */ 123, 576, 1040, 1389, 64, 64, 1188, 1434, 119, 119,
+ /* 1710 */ 571, 576, 82, 82, 563, 576, 120, 165, 451, 577,
+ /* 1720 */ 451, 413, 1362, 1028, 144, 144, 319, 567, 576, 1374,
+ /* 1730 */ 562, 498, 279, 451, 83, 83, 1439, 576, 166, 166,
+ /* 1740 */ 576, 1289, 554, 576, 1280, 565, 576, 12, 576, 1268,
+ /* 1750 */ 457, 146, 146, 1267, 576, 1028, 1028, 1030, 1031, 35,
+ /* 1760 */ 140, 140, 1269, 167, 167, 1609, 160, 160, 1359, 150,
+ /* 1770 */ 150, 149, 149, 311, 1040, 576, 312, 147, 147, 313,
+ /* 1780 */ 119, 119, 222, 235, 576, 1188, 396, 576, 120, 576,
+ /* 1790 */ 451, 577, 451, 1192, 453, 1028, 508, 291, 148, 148,
+ /* 1800 */ 1421, 1612, 395, 395, 394, 276, 392, 85, 85, 859,
+ /* 1810 */ 87, 87, 84, 84, 553, 576, 294, 576, 1426, 338,
+ /* 1820 */ 339, 1425, 237, 300, 326, 1416, 1409, 1028, 1028, 1030,
+ /* 1830 */ 1031, 35, 325, 344, 403, 483, 226, 1307, 52, 52,
+ /* 1840 */ 58, 58, 368, 1371, 1502, 566, 1501, 121, 568, 221,
+ /* 1850 */ 4, 208, 268, 209, 390, 1244, 1549, 1188, 1372, 1370,
+ /* 1860 */ 1369, 1547, 239, 184, 571, 233, 421, 1241, 95, 218,
+ /* 1870 */ 173, 1507, 193, 43, 91, 94, 178, 186, 467, 188,
+ /* 1880 */ 468, 1422, 13, 189, 190, 191, 501, 451, 245, 108,
+ /* 1890 */ 238, 401, 1428, 1427, 1430, 475, 404, 1496, 197, 565,
+ /* 1900 */ 14, 490, 249, 101, 1518, 496, 349, 280, 251, 201,
+ /* 1910 */ 353, 499, 252, 406, 1270, 253, 517, 1327, 1326, 435,
+ /* 1920 */ 1325, 1318, 103, 893, 1296, 413, 227, 407, 1040, 1626,
+ /* 1930 */ 319, 567, 1625, 1297, 119, 119, 439, 367, 1317, 1295,
+ /* 1940 */ 1624, 526, 120, 440, 451, 577, 451, 1594, 309, 1028,
+ /* 1950 */ 310, 373, 266, 267, 457, 1580, 1579, 443, 138, 1394,
+ /* 1960 */ 552, 1393, 11, 1483, 384, 115, 317, 1350, 109, 536,
+ /* 1970 */ 42, 579, 382, 214, 1349, 388, 1198, 389, 275, 277,
+ /* 1980 */ 278, 1028, 1028, 1030, 1031, 35, 580, 1265, 414, 1260,
+ /* 1990 */ 170, 415, 183, 1534, 1535, 1533, 171, 154, 307, 1532,
+ /* 2000 */ 846, 223, 224, 88, 452, 215, 172, 321, 234, 1102,
+ /* 2010 */ 152, 1188, 1100, 329, 185, 174, 1223, 925, 187, 241,
+ /* 2020 */ 337, 244, 1116, 192, 175, 176, 424, 426, 97, 194,
+ /* 2030 */ 98, 99, 100, 177, 1119, 1115, 246, 247, 161, 24,
+ /* 2040 */ 248, 348, 1238, 264, 1108, 250, 495, 199, 198, 15,
+ /* 2050 */ 861, 500, 369, 254, 504, 509, 512, 200, 102, 25,
+ /* 2060 */ 179, 361, 26, 364, 104, 891, 308, 162, 105, 904,
+ /* 2070 */ 520, 106, 1185, 1069, 1155, 17, 228, 27, 1154, 283,
+ /* 2080 */ 285, 263, 978, 202, 972, 123, 28, 1175, 29, 30,
+ /* 2090 */ 1179, 1171, 31, 1173, 1160, 41, 32, 206, 548, 33,
+ /* 2100 */ 110, 1178, 1083, 8, 112, 1070, 113, 1068, 1072, 34,
+ /* 2110 */ 1073, 560, 1125, 269, 1124, 270, 36, 18, 1194, 1033,
+ /* 2120 */ 873, 151, 122, 37, 393, 271, 272, 572, 181, 1193,
+ /* 2130 */ 1256, 1256, 1256, 935, 1256, 1256, 1256, 1256, 1256, 1256,
+ /* 2140 */ 1256, 1617,
};
static const YYCODETYPE yy_lookahead[] = {
- /* 0 */ 193, 193, 193, 274, 275, 276, 193, 274, 275, 276,
- /* 10 */ 193, 223, 219, 225, 206, 210, 211, 212, 193, 19,
- /* 20 */ 219, 233, 216, 216, 217, 216, 217, 193, 295, 216,
- /* 30 */ 217, 31, 193, 216, 217, 193, 228, 213, 230, 39,
- /* 40 */ 206, 216, 217, 43, 44, 45, 46, 47, 48, 49,
- /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 193, 19,
- /* 60 */ 185, 186, 187, 188, 189, 190, 253, 274, 275, 276,
- /* 70 */ 195, 193, 197, 193, 261, 274, 275, 276, 253, 204,
- /* 80 */ 238, 204, 81, 43, 44, 45, 46, 47, 48, 49,
- /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 274, 275,
- /* 100 */ 276, 262, 102, 103, 104, 105, 106, 107, 108, 109,
- /* 110 */ 110, 111, 112, 113, 239, 240, 239, 240, 210, 211,
- /* 120 */ 212, 314, 315, 314, 59, 316, 86, 252, 88, 252,
- /* 130 */ 19, 314, 315, 256, 257, 113, 25, 72, 296, 138,
- /* 140 */ 139, 266, 102, 103, 104, 105, 106, 107, 108, 109,
+ /* 0 */ 194, 276, 277, 278, 216, 194, 194, 217, 194, 194,
+ /* 10 */ 194, 194, 224, 194, 194, 276, 277, 278, 204, 19,
+ /* 20 */ 206, 202, 297, 217, 218, 205, 207, 217, 205, 217,
+ /* 30 */ 218, 31, 217, 218, 217, 218, 29, 217, 218, 39,
+ /* 40 */ 33, 217, 220, 43, 44, 45, 46, 47, 48, 49,
+ /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 312, 19,
+ /* 60 */ 240, 241, 316, 240, 241, 194, 46, 47, 48, 49,
+ /* 70 */ 22, 254, 65, 253, 254, 255, 253, 194, 255, 194,
+ /* 80 */ 263, 258, 259, 43, 44, 45, 46, 47, 48, 49,
+ /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 276, 277,
+ /* 100 */ 278, 285, 102, 103, 104, 105, 106, 107, 108, 109,
+ /* 110 */ 110, 111, 112, 113, 59, 186, 187, 188, 189, 190,
+ /* 120 */ 191, 310, 239, 317, 318, 196, 86, 198, 88, 317,
+ /* 130 */ 19, 319, 317, 318, 205, 264, 25, 211, 212, 213,
+ /* 140 */ 205, 121, 102, 103, 104, 105, 106, 107, 108, 109,
/* 150 */ 110, 111, 112, 113, 43, 44, 45, 46, 47, 48,
- /* 160 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 81,
- /* 170 */ 292, 59, 292, 298, 108, 109, 110, 111, 112, 113,
- /* 180 */ 69, 116, 117, 118, 72, 106, 107, 193, 111, 112,
- /* 190 */ 113, 54, 55, 56, 57, 58, 102, 103, 104, 105,
- /* 200 */ 106, 107, 108, 109, 110, 111, 112, 113, 120, 25,
- /* 210 */ 216, 217, 145, 102, 103, 104, 105, 106, 107, 108,
- /* 220 */ 109, 110, 111, 112, 113, 231, 138, 139, 116, 117,
- /* 230 */ 118, 164, 153, 19, 155, 54, 55, 56, 57, 102,
+ /* 160 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 240,
+ /* 170 */ 241, 116, 117, 118, 119, 240, 241, 122, 123, 124,
+ /* 180 */ 69, 298, 253, 194, 255, 106, 107, 132, 253, 141,
+ /* 190 */ 255, 54, 55, 56, 57, 58, 207, 268, 102, 103,
+ /* 200 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
+ /* 210 */ 214, 128, 129, 102, 103, 104, 105, 106, 107, 108,
+ /* 220 */ 109, 110, 111, 112, 113, 134, 25, 136, 137, 300,
+ /* 230 */ 165, 166, 153, 19, 155, 54, 55, 56, 57, 102,
/* 240 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
- /* 250 */ 113, 128, 129, 46, 47, 48, 49, 43, 44, 45,
+ /* 250 */ 113, 108, 109, 110, 111, 112, 113, 43, 44, 45,
/* 260 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
- /* 270 */ 56, 57, 216, 193, 25, 59, 193, 19, 165, 166,
- /* 280 */ 193, 67, 24, 102, 103, 104, 105, 106, 107, 108,
- /* 290 */ 109, 110, 111, 112, 113, 73, 216, 217, 59, 216,
- /* 300 */ 217, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ /* 270 */ 56, 57, 276, 277, 278, 113, 194, 19, 22, 23,
+ /* 280 */ 194, 67, 24, 102, 103, 104, 105, 106, 107, 108,
+ /* 290 */ 109, 110, 111, 112, 113, 220, 250, 59, 252, 217,
+ /* 300 */ 218, 43, 44, 45, 46, 47, 48, 49, 50, 51,
/* 310 */ 52, 53, 54, 55, 56, 57, 102, 103, 104, 105,
- /* 320 */ 106, 107, 108, 109, 110, 111, 112, 113, 121, 145,
- /* 330 */ 59, 193, 116, 117, 118, 119, 273, 204, 122, 123,
- /* 340 */ 124, 19, 20, 134, 22, 136, 137, 19, 132, 127,
- /* 350 */ 128, 129, 24, 22, 23, 116, 117, 118, 36, 193,
+ /* 320 */ 106, 107, 108, 109, 110, 111, 112, 113, 106, 107,
+ /* 330 */ 108, 109, 110, 111, 112, 113, 254, 59, 205, 138,
+ /* 340 */ 139, 19, 20, 194, 22, 263, 22, 23, 231, 25,
+ /* 350 */ 72, 276, 277, 278, 116, 117, 118, 101, 36, 76,
/* 360 */ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
- /* 370 */ 112, 113, 239, 240, 311, 312, 215, 106, 107, 241,
- /* 380 */ 19, 59, 216, 217, 223, 252, 115, 116, 117, 118,
- /* 390 */ 151, 120, 26, 71, 193, 308, 309, 193, 149, 128,
- /* 400 */ 313, 216, 269, 81, 43, 44, 45, 46, 47, 48,
- /* 410 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 253,
- /* 420 */ 216, 217, 100, 95, 153, 59, 155, 261, 106, 107,
- /* 430 */ 25, 193, 101, 193, 193, 231, 114, 25, 116, 117,
- /* 440 */ 118, 113, 304, 121, 193, 204, 59, 119, 120, 121,
- /* 450 */ 122, 123, 124, 125, 216, 217, 193, 216, 217, 131,
- /* 460 */ 138, 139, 230, 102, 103, 104, 105, 106, 107, 108,
+ /* 370 */ 112, 113, 89, 240, 241, 92, 73, 194, 194, 73,
+ /* 380 */ 19, 59, 188, 189, 190, 191, 253, 81, 255, 151,
+ /* 390 */ 196, 25, 198, 71, 116, 117, 118, 311, 312, 205,
+ /* 400 */ 217, 218, 316, 81, 43, 44, 45, 46, 47, 48,
+ /* 410 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 270,
+ /* 420 */ 22, 23, 100, 25, 59, 101, 138, 139, 106, 107,
+ /* 430 */ 127, 128, 129, 127, 240, 241, 114, 254, 116, 117,
+ /* 440 */ 118, 76, 76, 121, 138, 139, 263, 253, 264, 255,
+ /* 450 */ 205, 275, 87, 19, 89, 89, 194, 92, 92, 199,
+ /* 460 */ 138, 139, 268, 102, 103, 104, 105, 106, 107, 108,
/* 470 */ 109, 110, 111, 112, 113, 153, 154, 155, 156, 157,
- /* 480 */ 239, 240, 116, 117, 118, 76, 193, 23, 19, 25,
- /* 490 */ 22, 253, 23, 252, 253, 108, 87, 204, 89, 261,
- /* 500 */ 198, 92, 261, 116, 117, 118, 193, 306, 307, 216,
- /* 510 */ 217, 150, 43, 44, 45, 46, 47, 48, 49, 50,
- /* 520 */ 51, 52, 53, 54, 55, 56, 57, 59, 193, 216,
- /* 530 */ 217, 19, 239, 240, 283, 23, 106, 107, 108, 109,
- /* 540 */ 110, 111, 112, 113, 73, 252, 253, 142, 308, 309,
- /* 550 */ 138, 139, 81, 313, 145, 43, 44, 45, 46, 47,
+ /* 480 */ 81, 116, 117, 118, 129, 240, 241, 224, 19, 226,
+ /* 490 */ 314, 315, 23, 25, 300, 59, 22, 234, 253, 101,
+ /* 500 */ 255, 236, 237, 26, 194, 183, 194, 152, 72, 22,
+ /* 510 */ 145, 150, 43, 44, 45, 46, 47, 48, 49, 50,
+ /* 520 */ 51, 52, 53, 54, 55, 56, 57, 217, 218, 217,
+ /* 530 */ 218, 19, 189, 59, 191, 23, 59, 138, 139, 196,
+ /* 540 */ 135, 198, 232, 283, 232, 140, 59, 287, 205, 275,
+ /* 550 */ 116, 205, 116, 117, 118, 43, 44, 45, 46, 47,
/* 560 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
- /* 570 */ 307, 102, 103, 104, 105, 106, 107, 108, 109, 110,
- /* 580 */ 111, 112, 113, 281, 116, 117, 118, 285, 23, 193,
- /* 590 */ 25, 119, 59, 193, 122, 123, 124, 59, 127, 203,
- /* 600 */ 59, 205, 19, 268, 132, 25, 23, 22, 193, 138,
- /* 610 */ 139, 249, 204, 251, 102, 103, 104, 105, 106, 107,
+ /* 570 */ 194, 102, 103, 104, 105, 106, 107, 108, 109, 110,
+ /* 580 */ 111, 112, 113, 240, 241, 194, 240, 241, 314, 315,
+ /* 590 */ 116, 117, 118, 116, 117, 118, 253, 194, 255, 253,
+ /* 600 */ 59, 255, 19, 116, 117, 118, 23, 22, 217, 218,
+ /* 610 */ 142, 268, 205, 275, 102, 103, 104, 105, 106, 107,
/* 620 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46,
/* 630 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 640 */ 57, 19, 22, 23, 59, 23, 25, 239, 240, 116,
- /* 650 */ 117, 118, 193, 11, 116, 117, 118, 116, 117, 118,
- /* 660 */ 252, 269, 22, 193, 15, 43, 44, 45, 46, 47,
+ /* 640 */ 57, 19, 194, 300, 59, 23, 119, 240, 241, 122,
+ /* 650 */ 123, 124, 314, 315, 194, 236, 237, 194, 117, 132,
+ /* 660 */ 253, 81, 255, 205, 59, 43, 44, 45, 46, 47,
/* 670 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
- /* 680 */ 273, 143, 193, 118, 143, 102, 103, 104, 105, 106,
- /* 690 */ 107, 108, 109, 110, 111, 112, 113, 76, 118, 59,
- /* 700 */ 241, 116, 117, 118, 304, 216, 217, 292, 143, 60,
- /* 710 */ 89, 241, 19, 92, 193, 193, 23, 22, 311, 312,
- /* 720 */ 231, 101, 22, 143, 102, 103, 104, 105, 106, 107,
+ /* 680 */ 217, 218, 194, 194, 194, 102, 103, 104, 105, 106,
+ /* 690 */ 107, 108, 109, 110, 111, 112, 113, 294, 240, 241,
+ /* 700 */ 120, 116, 117, 118, 59, 194, 217, 218, 211, 212,
+ /* 710 */ 213, 253, 19, 255, 194, 19, 23, 254, 138, 139,
+ /* 720 */ 24, 232, 117, 194, 102, 103, 104, 105, 106, 107,
/* 730 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46,
/* 740 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 750 */ 57, 19, 193, 193, 59, 23, 116, 117, 118, 59,
- /* 760 */ 201, 21, 241, 304, 193, 206, 127, 128, 129, 193,
- /* 770 */ 128, 129, 235, 236, 304, 43, 44, 45, 46, 47,
+ /* 750 */ 57, 19, 264, 108, 76, 23, 127, 128, 129, 311,
+ /* 760 */ 312, 116, 117, 118, 316, 87, 306, 89, 308, 194,
+ /* 770 */ 92, 22, 59, 194, 22, 43, 44, 45, 46, 47,
/* 780 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
- /* 790 */ 22, 193, 216, 217, 193, 102, 103, 104, 105, 106,
- /* 800 */ 107, 108, 109, 110, 111, 112, 113, 231, 193, 193,
- /* 810 */ 193, 116, 117, 118, 216, 217, 116, 117, 118, 226,
- /* 820 */ 80, 193, 19, 235, 236, 304, 23, 211, 212, 231,
- /* 830 */ 204, 216, 217, 205, 102, 103, 104, 105, 106, 107,
+ /* 790 */ 194, 95, 217, 218, 265, 102, 103, 104, 105, 106,
+ /* 800 */ 107, 108, 109, 110, 111, 112, 113, 232, 59, 113,
+ /* 810 */ 25, 59, 194, 217, 218, 119, 120, 121, 122, 123,
+ /* 820 */ 124, 125, 19, 145, 194, 194, 23, 131, 232, 116,
+ /* 830 */ 117, 118, 35, 194, 102, 103, 104, 105, 106, 107,
/* 840 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46,
/* 850 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 860 */ 57, 19, 193, 123, 76, 239, 240, 193, 253, 239,
- /* 870 */ 240, 239, 240, 244, 106, 107, 193, 89, 252, 193,
- /* 880 */ 92, 59, 252, 254, 252, 43, 44, 45, 46, 47,
+ /* 860 */ 57, 19, 194, 66, 194, 116, 117, 118, 116, 117,
+ /* 870 */ 118, 74, 242, 294, 194, 194, 206, 23, 194, 25,
+ /* 880 */ 194, 111, 112, 113, 25, 43, 44, 45, 46, 47,
/* 890 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
- /* 900 */ 284, 161, 216, 217, 193, 102, 103, 104, 105, 106,
- /* 910 */ 107, 108, 109, 110, 111, 112, 113, 231, 193, 244,
- /* 920 */ 187, 188, 189, 190, 7, 8, 9, 309, 195, 254,
- /* 930 */ 197, 313, 19, 127, 128, 129, 262, 204, 22, 117,
- /* 940 */ 24, 216, 217, 273, 102, 103, 104, 105, 106, 107,
+ /* 900 */ 24, 194, 194, 217, 218, 102, 103, 104, 105, 106,
+ /* 910 */ 107, 108, 109, 110, 111, 112, 113, 241, 232, 194,
+ /* 920 */ 212, 213, 242, 242, 217, 218, 242, 130, 11, 253,
+ /* 930 */ 194, 255, 19, 265, 149, 59, 306, 194, 308, 232,
+ /* 940 */ 309, 310, 217, 218, 102, 103, 104, 105, 106, 107,
/* 950 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46,
/* 960 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 970 */ 57, 193, 239, 240, 193, 59, 19, 188, 253, 190,
- /* 980 */ 193, 311, 312, 16, 195, 252, 197, 193, 19, 301,
- /* 990 */ 302, 135, 193, 204, 216, 217, 140, 216, 217, 266,
- /* 1000 */ 204, 159, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 970 */ 57, 194, 194, 59, 194, 239, 19, 194, 25, 254,
+ /* 980 */ 303, 304, 23, 194, 25, 126, 306, 306, 308, 308,
+ /* 990 */ 306, 271, 308, 117, 286, 217, 218, 217, 218, 194,
+ /* 1000 */ 194, 159, 45, 46, 47, 48, 49, 50, 51, 52,
/* 1010 */ 53, 54, 55, 56, 57, 102, 103, 104, 105, 106,
- /* 1020 */ 107, 108, 109, 110, 111, 112, 113, 12, 239, 240,
- /* 1030 */ 193, 298, 238, 117, 253, 239, 240, 238, 259, 260,
- /* 1040 */ 193, 252, 27, 193, 77, 193, 79, 204, 252, 262,
- /* 1050 */ 193, 299, 300, 193, 100, 266, 278, 42, 204, 102,
+ /* 1020 */ 107, 108, 109, 110, 111, 112, 113, 59, 239, 194,
+ /* 1030 */ 116, 117, 118, 260, 254, 194, 240, 241, 194, 233,
+ /* 1040 */ 205, 240, 241, 205, 239, 128, 129, 270, 265, 253,
+ /* 1050 */ 194, 255, 217, 218, 253, 194, 255, 143, 280, 102,
/* 1060 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
- /* 1070 */ 113, 117, 159, 216, 217, 121, 216, 217, 63, 193,
- /* 1080 */ 193, 193, 239, 240, 115, 116, 193, 298, 73, 240,
- /* 1090 */ 238, 231, 19, 239, 240, 252, 22, 24, 211, 212,
- /* 1100 */ 263, 252, 216, 217, 216, 217, 252, 153, 154, 155,
- /* 1110 */ 253, 193, 19, 144, 213, 268, 43, 44, 45, 46,
+ /* 1070 */ 113, 118, 159, 217, 218, 240, 241, 118, 240, 241,
+ /* 1080 */ 194, 194, 194, 239, 116, 117, 118, 22, 253, 254,
+ /* 1090 */ 255, 253, 19, 255, 233, 194, 143, 24, 263, 212,
+ /* 1100 */ 213, 194, 143, 217, 218, 217, 218, 261, 262, 271,
+ /* 1110 */ 254, 143, 19, 7, 8, 9, 43, 44, 45, 46,
/* 1120 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 1130 */ 57, 193, 19, 59, 216, 217, 43, 44, 45, 46,
+ /* 1130 */ 57, 16, 19, 22, 23, 294, 43, 44, 45, 46,
/* 1140 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 1150 */ 57, 193, 19, 24, 216, 217, 43, 44, 45, 46,
+ /* 1150 */ 57, 312, 194, 214, 21, 316, 43, 44, 45, 46,
/* 1160 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 1170 */ 57, 284, 193, 208, 209, 102, 103, 104, 105, 106,
- /* 1180 */ 107, 108, 109, 110, 111, 112, 113, 286, 59, 193,
- /* 1190 */ 232, 117, 291, 193, 193, 102, 103, 104, 105, 106,
- /* 1200 */ 107, 108, 109, 110, 111, 112, 113, 193, 204, 22,
- /* 1210 */ 23, 193, 25, 66, 193, 102, 103, 104, 105, 106,
- /* 1220 */ 107, 108, 109, 110, 111, 112, 113, 193, 193, 193,
- /* 1230 */ 216, 217, 85, 193, 238, 19, 16, 216, 217, 238,
- /* 1240 */ 193, 94, 193, 239, 240, 231, 117, 268, 35, 116,
- /* 1250 */ 216, 217, 216, 217, 22, 23, 252, 25, 208, 209,
+ /* 1170 */ 57, 106, 107, 286, 194, 102, 103, 104, 105, 106,
+ /* 1180 */ 107, 108, 109, 110, 111, 112, 113, 207, 158, 59,
+ /* 1190 */ 160, 22, 77, 24, 79, 102, 103, 104, 105, 106,
+ /* 1200 */ 107, 108, 109, 110, 111, 112, 113, 194, 194, 229,
+ /* 1210 */ 194, 231, 101, 80, 22, 102, 103, 104, 105, 106,
+ /* 1220 */ 107, 108, 109, 110, 111, 112, 113, 288, 59, 12,
+ /* 1230 */ 217, 218, 293, 217, 218, 19, 106, 107, 59, 19,
+ /* 1240 */ 16, 127, 128, 129, 27, 115, 116, 117, 118, 194,
+ /* 1250 */ 120, 59, 22, 194, 24, 194, 123, 100, 128, 42,
/* 1260 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- /* 1270 */ 54, 55, 56, 57, 193, 193, 19, 5, 59, 66,
- /* 1280 */ 193, 263, 10, 11, 12, 13, 14, 74, 101, 17,
- /* 1290 */ 193, 46, 193, 146, 193, 76, 213, 77, 263, 79,
- /* 1300 */ 12, 260, 30, 46, 32, 264, 87, 193, 89, 29,
- /* 1310 */ 263, 92, 40, 33, 232, 27, 193, 108, 102, 103,
+ /* 1270 */ 54, 55, 56, 57, 117, 194, 217, 218, 121, 100,
+ /* 1280 */ 63, 194, 245, 153, 194, 155, 117, 19, 115, 194,
+ /* 1290 */ 73, 214, 194, 256, 161, 116, 117, 194, 217, 218,
+ /* 1300 */ 121, 77, 194, 79, 217, 218, 194, 217, 218, 117,
+ /* 1310 */ 153, 154, 155, 254, 46, 217, 218, 144, 102, 103,
/* 1320 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
- /* 1330 */ 42, 138, 139, 101, 193, 116, 117, 118, 19, 20,
- /* 1340 */ 255, 22, 70, 130, 135, 65, 256, 257, 193, 140,
- /* 1350 */ 78, 63, 193, 81, 193, 36, 193, 216, 217, 193,
- /* 1360 */ 115, 193, 263, 193, 145, 268, 59, 48, 193, 193,
- /* 1370 */ 98, 193, 115, 193, 291, 216, 217, 193, 59, 216,
- /* 1380 */ 217, 161, 216, 217, 216, 217, 216, 217, 131, 193,
- /* 1390 */ 71, 193, 216, 217, 216, 217, 216, 217, 193, 260,
- /* 1400 */ 216, 217, 19, 264, 85, 133, 244, 100, 193, 90,
- /* 1410 */ 138, 139, 216, 217, 216, 217, 254, 244, 193, 100,
- /* 1420 */ 193, 216, 217, 116, 117, 106, 107, 254, 121, 193,
- /* 1430 */ 115, 216, 217, 114, 162, 116, 117, 118, 115, 244,
- /* 1440 */ 121, 216, 217, 216, 217, 193, 309, 193, 31, 254,
- /* 1450 */ 313, 309, 216, 217, 309, 313, 39, 193, 313, 309,
- /* 1460 */ 153, 154, 155, 313, 193, 150, 25, 144, 216, 217,
- /* 1470 */ 216, 217, 153, 154, 155, 156, 157, 0, 1, 2,
- /* 1480 */ 216, 217, 5, 149, 150, 22, 193, 10, 11, 12,
- /* 1490 */ 13, 14, 193, 158, 17, 160, 193, 19, 20, 116,
- /* 1500 */ 22, 25, 193, 24, 22, 193, 24, 30, 226, 32,
- /* 1510 */ 19, 20, 226, 22, 36, 193, 53, 40, 193, 216,
- /* 1520 */ 217, 193, 23, 193, 25, 216, 217, 36, 216, 217,
- /* 1530 */ 193, 99, 193, 193, 22, 193, 193, 59, 216, 217,
- /* 1540 */ 193, 216, 217, 193, 216, 217, 193, 70, 129, 71,
- /* 1550 */ 59, 129, 193, 216, 217, 78, 216, 217, 81, 216,
- /* 1560 */ 217, 193, 71, 85, 193, 133, 193, 126, 90, 216,
- /* 1570 */ 217, 152, 258, 61, 152, 98, 85, 193, 100, 193,
- /* 1580 */ 23, 90, 25, 121, 106, 107, 23, 216, 217, 216,
- /* 1590 */ 217, 100, 114, 131, 116, 117, 118, 106, 107, 121,
- /* 1600 */ 216, 217, 216, 217, 193, 114, 193, 116, 117, 118,
- /* 1610 */ 133, 22, 121, 193, 59, 138, 139, 193, 142, 193,
- /* 1620 */ 141, 23, 23, 25, 25, 120, 121, 216, 217, 216,
- /* 1630 */ 217, 153, 154, 155, 156, 157, 216, 217, 19, 162,
- /* 1640 */ 216, 217, 216, 217, 153, 154, 155, 156, 157, 1,
- /* 1650 */ 2, 193, 59, 5, 19, 20, 318, 22, 10, 11,
- /* 1660 */ 12, 13, 14, 193, 59, 17, 193, 23, 23, 25,
- /* 1670 */ 25, 36, 117, 193, 216, 217, 193, 23, 30, 25,
- /* 1680 */ 32, 19, 20, 23, 22, 25, 216, 217, 40, 216,
- /* 1690 */ 217, 7, 8, 23, 59, 25, 83, 84, 36, 23,
- /* 1700 */ 193, 25, 23, 23, 25, 25, 71, 153, 145, 155,
- /* 1710 */ 117, 153, 23, 155, 25, 23, 97, 25, 70, 193,
- /* 1720 */ 193, 59, 117, 236, 193, 193, 78, 193, 193, 81,
- /* 1730 */ 141, 193, 193, 71, 193, 100, 288, 287, 242, 255,
- /* 1740 */ 255, 106, 107, 108, 255, 255, 98, 243, 297, 114,
- /* 1750 */ 214, 116, 117, 118, 245, 191, 121, 271, 293, 267,
- /* 1760 */ 267, 246, 100, 246, 245, 271, 271, 293, 106, 107,
- /* 1770 */ 220, 271, 229, 225, 249, 219, 114, 259, 116, 117,
- /* 1780 */ 118, 133, 259, 121, 219, 219, 138, 139, 153, 154,
- /* 1790 */ 155, 156, 157, 280, 249, 243, 19, 20, 245, 22,
- /* 1800 */ 196, 259, 140, 259, 60, 297, 141, 297, 200, 200,
- /* 1810 */ 162, 38, 200, 36, 294, 153, 154, 155, 156, 157,
- /* 1820 */ 151, 150, 294, 283, 22, 43, 234, 18, 237, 200,
- /* 1830 */ 270, 272, 237, 237, 237, 18, 59, 199, 270, 149,
- /* 1840 */ 246, 272, 272, 200, 234, 234, 246, 246, 71, 246,
- /* 1850 */ 199, 158, 290, 62, 22, 200, 19, 20, 199, 22,
- /* 1860 */ 289, 221, 221, 200, 200, 199, 199, 115, 218, 64,
- /* 1870 */ 218, 218, 22, 36, 227, 126, 227, 100, 165, 221,
- /* 1880 */ 224, 224, 24, 106, 107, 312, 218, 305, 113, 282,
- /* 1890 */ 91, 114, 220, 116, 117, 118, 59, 282, 121, 218,
- /* 1900 */ 218, 218, 200, 317, 317, 82, 221, 265, 71, 148,
- /* 1910 */ 145, 265, 22, 277, 200, 158, 279, 140, 147, 25,
- /* 1920 */ 146, 202, 248, 250, 249, 247, 13, 250, 194, 194,
- /* 1930 */ 153, 154, 155, 156, 157, 6, 303, 100, 192, 192,
- /* 1940 */ 246, 213, 192, 106, 107, 207, 213, 207, 222, 213,
- /* 1950 */ 213, 114, 222, 116, 117, 118, 214, 214, 121, 4,
- /* 1960 */ 207, 213, 3, 22, 303, 15, 163, 16, 23, 23,
- /* 1970 */ 139, 151, 130, 25, 20, 142, 24, 16, 144, 1,
- /* 1980 */ 142, 130, 130, 61, 37, 53, 300, 151, 53, 53,
- /* 1990 */ 153, 154, 155, 156, 157, 53, 130, 116, 34, 1,
- /* 2000 */ 141, 5, 22, 115, 161, 68, 25, 68, 75, 41,
- /* 2010 */ 141, 115, 24, 20, 19, 131, 125, 23, 28, 22,
- /* 2020 */ 67, 22, 22, 22, 67, 59, 24, 96, 22, 67,
- /* 2030 */ 23, 149, 22, 25, 23, 23, 23, 22, 34, 141,
- /* 2040 */ 37, 97, 23, 23, 116, 22, 143, 25, 34, 75,
- /* 2050 */ 34, 34, 34, 88, 75, 34, 86, 23, 22, 34,
- /* 2060 */ 93, 24, 34, 25, 25, 142, 142, 23, 44, 23,
- /* 2070 */ 23, 23, 23, 11, 23, 25, 22, 22, 22, 141,
- /* 2080 */ 23, 23, 22, 22, 25, 15, 1, 23, 25, 1,
- /* 2090 */ 141, 135, 319, 319, 319, 319, 319, 319, 319, 141,
- /* 2100 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2110 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2120 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2130 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2140 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2150 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2160 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2170 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2180 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2190 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2200 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2210 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2220 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2230 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2240 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2250 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2260 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2270 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2280 */ 319, 319, 319, 319, 319,
+ /* 1330 */ 232, 270, 153, 154, 155, 115, 116, 66, 19, 20,
+ /* 1340 */ 183, 22, 12, 312, 254, 194, 262, 316, 209, 210,
+ /* 1350 */ 266, 239, 194, 194, 108, 36, 85, 27, 19, 20,
+ /* 1360 */ 265, 22, 183, 245, 144, 94, 25, 48, 217, 218,
+ /* 1370 */ 293, 194, 42, 270, 256, 36, 217, 218, 59, 194,
+ /* 1380 */ 25, 135, 194, 115, 194, 161, 140, 194, 194, 15,
+ /* 1390 */ 71, 194, 312, 63, 217, 218, 316, 194, 59, 131,
+ /* 1400 */ 301, 302, 217, 218, 85, 217, 218, 217, 218, 90,
+ /* 1410 */ 71, 217, 218, 19, 217, 218, 245, 146, 262, 100,
+ /* 1420 */ 217, 218, 266, 265, 85, 106, 107, 256, 312, 90,
+ /* 1430 */ 209, 210, 316, 114, 60, 116, 117, 118, 194, 100,
+ /* 1440 */ 121, 194, 194, 145, 115, 106, 107, 19, 46, 19,
+ /* 1450 */ 20, 24, 22, 114, 194, 116, 117, 118, 194, 245,
+ /* 1460 */ 121, 194, 164, 194, 217, 218, 36, 194, 258, 259,
+ /* 1470 */ 256, 194, 153, 154, 155, 156, 157, 217, 218, 150,
+ /* 1480 */ 31, 217, 218, 142, 217, 218, 217, 218, 39, 59,
+ /* 1490 */ 217, 218, 153, 154, 155, 156, 157, 149, 150, 5,
+ /* 1500 */ 145, 71, 183, 245, 10, 11, 12, 13, 14, 194,
+ /* 1510 */ 116, 17, 129, 227, 256, 85, 194, 115, 194, 23,
+ /* 1520 */ 90, 25, 183, 99, 30, 97, 32, 22, 22, 194,
+ /* 1530 */ 100, 194, 217, 218, 40, 152, 106, 107, 23, 217,
+ /* 1540 */ 218, 194, 19, 20, 114, 22, 116, 117, 118, 257,
+ /* 1550 */ 194, 121, 217, 218, 217, 218, 194, 133, 53, 36,
+ /* 1560 */ 23, 23, 25, 25, 70, 120, 121, 61, 141, 7,
+ /* 1570 */ 8, 121, 78, 217, 218, 81, 23, 227, 25, 217,
+ /* 1580 */ 218, 131, 59, 153, 154, 155, 156, 157, 0, 1,
+ /* 1590 */ 2, 59, 98, 5, 71, 23, 227, 25, 10, 11,
+ /* 1600 */ 12, 13, 14, 83, 84, 17, 23, 23, 25, 25,
+ /* 1610 */ 59, 194, 194, 183, 23, 23, 25, 25, 30, 194,
+ /* 1620 */ 32, 19, 20, 100, 22, 194, 194, 133, 40, 106,
+ /* 1630 */ 107, 108, 138, 139, 194, 217, 218, 114, 36, 116,
+ /* 1640 */ 117, 118, 217, 218, 121, 194, 194, 194, 23, 117,
+ /* 1650 */ 25, 194, 23, 23, 25, 25, 162, 194, 70, 194,
+ /* 1660 */ 145, 59, 23, 153, 25, 155, 78, 194, 117, 81,
+ /* 1670 */ 217, 218, 194, 71, 217, 218, 153, 154, 155, 156,
+ /* 1680 */ 157, 194, 217, 218, 194, 23, 98, 25, 321, 194,
+ /* 1690 */ 217, 218, 194, 19, 20, 194, 22, 153, 23, 155,
+ /* 1700 */ 25, 194, 100, 194, 217, 218, 183, 194, 106, 107,
+ /* 1710 */ 36, 194, 217, 218, 237, 194, 114, 243, 116, 117,
+ /* 1720 */ 118, 133, 194, 121, 217, 218, 138, 139, 194, 194,
+ /* 1730 */ 194, 290, 289, 59, 217, 218, 194, 194, 217, 218,
+ /* 1740 */ 194, 194, 140, 194, 194, 71, 194, 244, 194, 194,
+ /* 1750 */ 162, 217, 218, 194, 194, 153, 154, 155, 156, 157,
+ /* 1760 */ 217, 218, 194, 217, 218, 194, 217, 218, 257, 217,
+ /* 1770 */ 218, 217, 218, 257, 100, 194, 257, 217, 218, 257,
+ /* 1780 */ 106, 107, 215, 299, 194, 183, 192, 194, 114, 194,
+ /* 1790 */ 116, 117, 118, 1, 2, 121, 221, 5, 217, 218,
+ /* 1800 */ 273, 197, 10, 11, 12, 13, 14, 217, 218, 17,
+ /* 1810 */ 217, 218, 217, 218, 140, 194, 246, 194, 273, 295,
+ /* 1820 */ 247, 273, 30, 247, 32, 269, 269, 153, 154, 155,
+ /* 1830 */ 156, 157, 40, 246, 273, 295, 230, 226, 217, 218,
+ /* 1840 */ 217, 218, 220, 261, 220, 282, 220, 19, 20, 244,
+ /* 1850 */ 22, 250, 141, 250, 246, 60, 201, 183, 261, 261,
+ /* 1860 */ 261, 201, 70, 299, 36, 299, 201, 38, 151, 150,
+ /* 1870 */ 78, 285, 22, 81, 296, 296, 43, 235, 18, 238,
+ /* 1880 */ 201, 274, 272, 238, 238, 238, 18, 59, 200, 149,
+ /* 1890 */ 98, 247, 274, 274, 235, 247, 247, 247, 235, 71,
+ /* 1900 */ 272, 201, 200, 158, 292, 62, 291, 201, 200, 22,
+ /* 1910 */ 201, 222, 200, 222, 201, 200, 115, 219, 219, 64,
+ /* 1920 */ 219, 228, 22, 126, 221, 133, 165, 222, 100, 225,
+ /* 1930 */ 138, 139, 225, 219, 106, 107, 24, 219, 228, 219,
+ /* 1940 */ 219, 307, 114, 113, 116, 117, 118, 315, 284, 121,
+ /* 1950 */ 284, 222, 201, 91, 162, 320, 320, 82, 148, 267,
+ /* 1960 */ 145, 267, 22, 279, 201, 158, 281, 251, 147, 146,
+ /* 1970 */ 25, 203, 250, 249, 251, 248, 13, 247, 195, 195,
+ /* 1980 */ 6, 153, 154, 155, 156, 157, 193, 193, 305, 193,
+ /* 1990 */ 208, 305, 302, 214, 214, 214, 208, 223, 223, 214,
+ /* 2000 */ 4, 215, 215, 214, 3, 22, 208, 163, 15, 23,
+ /* 2010 */ 16, 183, 23, 139, 151, 130, 25, 20, 142, 24,
+ /* 2020 */ 16, 144, 1, 142, 130, 130, 61, 37, 53, 151,
+ /* 2030 */ 53, 53, 53, 130, 116, 1, 34, 141, 5, 22,
+ /* 2040 */ 115, 161, 75, 25, 68, 141, 41, 115, 68, 24,
+ /* 2050 */ 20, 19, 131, 125, 67, 67, 96, 22, 22, 22,
+ /* 2060 */ 37, 23, 22, 24, 22, 59, 67, 23, 149, 28,
+ /* 2070 */ 22, 25, 23, 23, 23, 22, 141, 34, 97, 23,
+ /* 2080 */ 23, 34, 116, 22, 143, 25, 34, 75, 34, 34,
+ /* 2090 */ 75, 88, 34, 86, 23, 22, 34, 25, 24, 34,
+ /* 2100 */ 25, 93, 23, 44, 142, 23, 142, 23, 23, 22,
+ /* 2110 */ 11, 25, 23, 25, 23, 22, 22, 22, 1, 23,
+ /* 2120 */ 23, 23, 22, 22, 15, 141, 141, 25, 25, 1,
+ /* 2130 */ 322, 322, 322, 135, 322, 322, 322, 322, 322, 322,
+ /* 2140 */ 322, 141, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2150 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2160 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2170 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2180 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2190 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2200 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2210 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2220 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2230 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2240 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2250 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2260 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2270 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2280 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2290 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2300 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2310 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2320 */ 322, 322, 322, 322, 322, 322, 322, 322,
};
-#define YY_SHIFT_COUNT (578)
+#define YY_SHIFT_COUNT (582)
#define YY_SHIFT_MIN (0)
-#define YY_SHIFT_MAX (2088)
+#define YY_SHIFT_MAX (2128)
static const unsigned short int yy_shift_ofst[] = {
- /* 0 */ 1648, 1477, 1272, 322, 322, 1, 1319, 1478, 1491, 1837,
- /* 10 */ 1837, 1837, 471, 0, 0, 214, 1093, 1837, 1837, 1837,
- /* 20 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
- /* 30 */ 1837, 271, 271, 1219, 1219, 216, 88, 1, 1, 1,
- /* 40 */ 1, 1, 40, 111, 258, 361, 469, 512, 583, 622,
- /* 50 */ 693, 732, 803, 842, 913, 1073, 1093, 1093, 1093, 1093,
- /* 60 */ 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093,
- /* 70 */ 1093, 1093, 1093, 1093, 1113, 1093, 1216, 957, 957, 1635,
- /* 80 */ 1662, 1777, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
- /* 90 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
- /* 100 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
- /* 110 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
- /* 120 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
- /* 130 */ 1837, 137, 181, 181, 181, 181, 181, 181, 181, 94,
- /* 140 */ 430, 66, 65, 112, 366, 533, 533, 740, 1257, 533,
- /* 150 */ 533, 79, 79, 533, 412, 412, 412, 77, 412, 123,
- /* 160 */ 113, 113, 113, 22, 22, 2100, 2100, 328, 328, 328,
- /* 170 */ 239, 468, 468, 468, 468, 1015, 1015, 409, 366, 1187,
- /* 180 */ 1232, 533, 533, 533, 533, 533, 533, 533, 533, 533,
- /* 190 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 533,
- /* 200 */ 533, 969, 621, 621, 533, 642, 788, 788, 1133, 1133,
- /* 210 */ 822, 822, 67, 1193, 2100, 2100, 2100, 2100, 2100, 2100,
- /* 220 */ 2100, 1307, 954, 954, 585, 472, 640, 387, 695, 538,
- /* 230 */ 541, 700, 533, 533, 533, 533, 533, 533, 533, 533,
- /* 240 */ 533, 533, 222, 533, 533, 533, 533, 533, 533, 533,
- /* 250 */ 533, 533, 533, 533, 533, 1213, 1213, 1213, 533, 533,
- /* 260 */ 533, 565, 533, 533, 533, 916, 1147, 533, 533, 1288,
- /* 270 */ 533, 533, 533, 533, 533, 533, 533, 533, 639, 1280,
- /* 280 */ 209, 1129, 1129, 1129, 1129, 580, 209, 209, 1209, 768,
- /* 290 */ 917, 649, 1315, 1334, 405, 1334, 1383, 249, 1315, 1315,
- /* 300 */ 249, 1315, 405, 1383, 1441, 464, 1245, 1417, 1417, 1417,
- /* 310 */ 1323, 1323, 1323, 1323, 184, 184, 1335, 1476, 856, 1482,
- /* 320 */ 1744, 1744, 1665, 1665, 1773, 1773, 1665, 1669, 1671, 1802,
- /* 330 */ 1782, 1809, 1809, 1809, 1809, 1665, 1817, 1690, 1671, 1671,
- /* 340 */ 1690, 1802, 1782, 1690, 1782, 1690, 1665, 1817, 1693, 1791,
- /* 350 */ 1665, 1817, 1832, 1665, 1817, 1665, 1817, 1832, 1752, 1752,
- /* 360 */ 1752, 1805, 1850, 1850, 1832, 1752, 1749, 1752, 1805, 1752,
- /* 370 */ 1752, 1713, 1858, 1775, 1775, 1832, 1665, 1799, 1799, 1823,
- /* 380 */ 1823, 1761, 1765, 1890, 1665, 1757, 1761, 1771, 1774, 1690,
- /* 390 */ 1894, 1913, 1913, 1929, 1929, 1929, 2100, 2100, 2100, 2100,
- /* 400 */ 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100,
- /* 410 */ 2100, 207, 1220, 331, 620, 967, 806, 1074, 1499, 1432,
- /* 420 */ 1463, 1479, 1419, 1422, 1557, 1512, 1598, 1599, 1644, 1645,
- /* 430 */ 1654, 1660, 1555, 1505, 1684, 1462, 1670, 1563, 1619, 1593,
- /* 440 */ 1676, 1679, 1613, 1680, 1554, 1558, 1689, 1692, 1605, 1589,
- /* 450 */ 1955, 1959, 1941, 1803, 1950, 1951, 1945, 1946, 1831, 1820,
- /* 460 */ 1842, 1948, 1948, 1952, 1833, 1954, 1834, 1961, 1978, 1838,
- /* 470 */ 1851, 1948, 1852, 1922, 1947, 1948, 1836, 1932, 1935, 1936,
- /* 480 */ 1942, 1866, 1881, 1964, 1859, 1998, 1996, 1980, 1888, 1843,
- /* 490 */ 1937, 1981, 1939, 1933, 1968, 1869, 1896, 1988, 1993, 1995,
- /* 500 */ 1884, 1891, 1997, 1953, 1999, 2000, 1994, 2001, 1957, 1966,
- /* 510 */ 2002, 1931, 1990, 2006, 1962, 2003, 2007, 2004, 1882, 2010,
- /* 520 */ 2011, 2012, 2008, 2013, 2015, 1944, 1898, 2019, 2020, 1928,
- /* 530 */ 2014, 2023, 1903, 2022, 2016, 2017, 2018, 2021, 1965, 1974,
- /* 540 */ 1970, 2024, 1979, 1967, 2025, 2034, 2036, 2037, 2038, 2039,
- /* 550 */ 2028, 1923, 1924, 2044, 2022, 2046, 2047, 2048, 2049, 2050,
- /* 560 */ 2051, 2054, 2062, 2055, 2056, 2057, 2058, 2060, 2061, 2059,
- /* 570 */ 1956, 1938, 1949, 1958, 2063, 2064, 2070, 2085, 2088,
+ /* 0 */ 1792, 1588, 1494, 322, 322, 399, 306, 1319, 1339, 1430,
+ /* 10 */ 1828, 1828, 1828, 580, 399, 399, 399, 399, 399, 0,
+ /* 20 */ 0, 214, 1093, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
+ /* 30 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1130, 1130,
+ /* 40 */ 365, 365, 55, 278, 436, 713, 713, 201, 201, 201,
+ /* 50 */ 201, 40, 111, 258, 361, 469, 512, 583, 622, 693,
+ /* 60 */ 732, 803, 842, 913, 1073, 1093, 1093, 1093, 1093, 1093,
+ /* 70 */ 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093,
+ /* 80 */ 1093, 1093, 1093, 1113, 1093, 1216, 957, 957, 1523, 1602,
+ /* 90 */ 1674, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
+ /* 100 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
+ /* 110 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
+ /* 120 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
+ /* 130 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
+ /* 140 */ 137, 181, 181, 181, 181, 181, 181, 181, 96, 222,
+ /* 150 */ 143, 477, 713, 1133, 1268, 713, 713, 79, 79, 713,
+ /* 160 */ 770, 83, 65, 65, 65, 288, 162, 162, 2142, 2142,
+ /* 170 */ 696, 696, 696, 238, 474, 474, 474, 474, 1217, 1217,
+ /* 180 */ 678, 477, 324, 398, 713, 713, 713, 713, 713, 713,
+ /* 190 */ 713, 713, 713, 713, 713, 713, 713, 713, 713, 713,
+ /* 200 */ 713, 713, 713, 1220, 366, 366, 713, 917, 283, 283,
+ /* 210 */ 434, 434, 605, 605, 1298, 2142, 2142, 2142, 2142, 2142,
+ /* 220 */ 2142, 2142, 1179, 1157, 1157, 487, 527, 585, 645, 749,
+ /* 230 */ 914, 968, 752, 713, 713, 713, 713, 713, 713, 713,
+ /* 240 */ 713, 713, 713, 303, 713, 713, 713, 713, 713, 713,
+ /* 250 */ 713, 713, 713, 713, 713, 713, 797, 797, 797, 713,
+ /* 260 */ 713, 713, 959, 713, 713, 713, 1169, 1271, 713, 713,
+ /* 270 */ 1330, 713, 713, 713, 713, 713, 713, 713, 713, 629,
+ /* 280 */ 7, 91, 876, 876, 876, 876, 953, 91, 91, 1246,
+ /* 290 */ 1065, 1106, 1374, 1329, 1348, 468, 1348, 1394, 785, 1329,
+ /* 300 */ 1329, 785, 1329, 468, 1394, 859, 854, 1402, 1449, 1449,
+ /* 310 */ 1449, 1173, 1173, 1173, 1173, 1355, 1355, 1030, 1341, 405,
+ /* 320 */ 1230, 1795, 1795, 1711, 1711, 1829, 1829, 1711, 1717, 1719,
+ /* 330 */ 1850, 1833, 1860, 1860, 1860, 1860, 1711, 1868, 1740, 1719,
+ /* 340 */ 1719, 1740, 1850, 1833, 1740, 1833, 1740, 1711, 1868, 1745,
+ /* 350 */ 1843, 1711, 1868, 1887, 1711, 1868, 1711, 1868, 1887, 1801,
+ /* 360 */ 1801, 1801, 1855, 1900, 1900, 1887, 1801, 1797, 1801, 1855,
+ /* 370 */ 1801, 1801, 1761, 1912, 1830, 1830, 1887, 1711, 1862, 1862,
+ /* 380 */ 1875, 1875, 1810, 1815, 1940, 1711, 1807, 1810, 1821, 1823,
+ /* 390 */ 1740, 1945, 1963, 1963, 1974, 1974, 1974, 2142, 2142, 2142,
+ /* 400 */ 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142,
+ /* 410 */ 2142, 2142, 20, 1224, 256, 1111, 1115, 1114, 1192, 1496,
+ /* 420 */ 1424, 1505, 1427, 355, 1383, 1537, 1506, 1538, 1553, 1583,
+ /* 430 */ 1584, 1591, 1625, 541, 1445, 1562, 1450, 1572, 1515, 1428,
+ /* 440 */ 1532, 1592, 1629, 1520, 1630, 1639, 1510, 1544, 1662, 1675,
+ /* 450 */ 1551, 48, 1996, 2001, 1983, 1844, 1993, 1994, 1986, 1989,
+ /* 460 */ 1874, 1863, 1885, 1991, 1991, 1995, 1876, 1997, 1877, 2004,
+ /* 470 */ 2021, 1881, 1894, 1991, 1895, 1965, 1990, 1991, 1878, 1975,
+ /* 480 */ 1977, 1978, 1979, 1903, 1918, 2002, 1896, 2034, 2033, 2017,
+ /* 490 */ 1925, 1880, 1976, 2018, 1980, 1967, 2005, 1904, 1932, 2025,
+ /* 500 */ 2030, 2032, 1921, 1928, 2035, 1987, 2036, 2037, 2038, 2040,
+ /* 510 */ 1988, 2006, 2039, 1960, 2041, 2042, 1999, 2023, 2044, 2043,
+ /* 520 */ 1919, 2048, 2049, 2050, 2046, 2051, 2053, 1981, 1935, 2056,
+ /* 530 */ 2057, 1966, 2047, 2061, 1941, 2060, 2052, 2054, 2055, 2058,
+ /* 540 */ 2003, 2012, 2007, 2059, 2015, 2008, 2062, 2071, 2073, 2074,
+ /* 550 */ 2072, 2075, 2065, 1962, 1964, 2079, 2060, 2082, 2084, 2085,
+ /* 560 */ 2087, 2086, 2089, 2088, 2091, 2093, 2099, 2094, 2095, 2096,
+ /* 570 */ 2097, 2100, 2101, 2102, 1998, 1984, 1985, 2000, 2103, 2098,
+ /* 580 */ 2109, 2117, 2128,
};
-#define YY_REDUCE_COUNT (410)
-#define YY_REDUCE_MIN (-271)
-#define YY_REDUCE_MAX (1753)
+#define YY_REDUCE_COUNT (411)
+#define YY_REDUCE_MIN (-275)
+#define YY_REDUCE_MAX (1798)
static const short yy_reduce_ofst[] = {
- /* 0 */ -125, 733, 789, 241, 293, -123, -193, -191, -183, -187,
- /* 10 */ 166, 238, 133, -207, -199, -267, -176, -6, 204, 489,
- /* 20 */ 576, 598, -175, 686, 860, 615, 725, 1014, 778, 781,
- /* 30 */ 857, 616, 887, 87, 240, -192, 408, 626, 796, 843,
- /* 40 */ 854, 1004, -271, -271, -271, -271, -271, -271, -271, -271,
- /* 50 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271,
- /* 60 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271,
- /* 70 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, 80,
- /* 80 */ 83, 313, 886, 888, 918, 938, 1021, 1034, 1036, 1141,
- /* 90 */ 1159, 1163, 1166, 1168, 1170, 1176, 1178, 1180, 1184, 1196,
- /* 100 */ 1198, 1205, 1215, 1225, 1227, 1236, 1252, 1254, 1264, 1303,
- /* 110 */ 1309, 1312, 1322, 1325, 1328, 1337, 1340, 1343, 1353, 1371,
- /* 120 */ 1373, 1384, 1386, 1411, 1413, 1420, 1424, 1426, 1458, 1470,
- /* 130 */ 1473, -271, -271, -271, -271, -271, -271, -271, -271, -271,
- /* 140 */ -271, -271, 138, 459, 396, -158, 470, 302, -212, 521,
- /* 150 */ 201, -195, -92, 559, 630, 632, 630, -271, 632, 901,
- /* 160 */ 63, 407, 670, -271, -271, -271, -271, 161, 161, 161,
- /* 170 */ 251, 335, 847, 979, 1097, 537, 588, 618, 628, 688,
- /* 180 */ 688, -166, -161, 674, 787, 794, 799, 852, 996, -122,
- /* 190 */ 837, -120, 1018, 1035, 415, 1047, 1001, 958, 1082, 400,
- /* 200 */ 1099, 779, 1137, 1142, 263, 1083, 1145, 1150, 1041, 1139,
- /* 210 */ 965, 1050, 362, 849, 752, 629, 675, 1162, 1173, 1090,
- /* 220 */ 1195, -194, 56, 185, -135, 232, 522, 560, 571, 601,
- /* 230 */ 617, 669, 683, 711, 850, 893, 1000, 1040, 1049, 1081,
- /* 240 */ 1087, 1101, 392, 1114, 1123, 1155, 1161, 1175, 1271, 1293,
- /* 250 */ 1299, 1330, 1339, 1342, 1347, 593, 1282, 1286, 1350, 1359,
- /* 260 */ 1368, 1314, 1480, 1483, 1507, 1085, 1338, 1526, 1527, 1487,
- /* 270 */ 1531, 560, 1532, 1534, 1535, 1538, 1539, 1541, 1448, 1450,
- /* 280 */ 1496, 1484, 1485, 1489, 1490, 1314, 1496, 1496, 1504, 1536,
- /* 290 */ 1564, 1451, 1486, 1492, 1509, 1493, 1465, 1515, 1494, 1495,
- /* 300 */ 1517, 1500, 1519, 1474, 1550, 1543, 1548, 1556, 1565, 1566,
- /* 310 */ 1518, 1523, 1542, 1544, 1525, 1545, 1513, 1553, 1552, 1604,
- /* 320 */ 1508, 1510, 1608, 1609, 1520, 1528, 1612, 1540, 1559, 1560,
- /* 330 */ 1592, 1591, 1595, 1596, 1597, 1629, 1638, 1594, 1569, 1570,
- /* 340 */ 1600, 1568, 1610, 1601, 1611, 1603, 1643, 1651, 1562, 1571,
- /* 350 */ 1655, 1659, 1640, 1663, 1666, 1664, 1667, 1641, 1650, 1652,
- /* 360 */ 1653, 1647, 1656, 1657, 1658, 1668, 1672, 1681, 1649, 1682,
- /* 370 */ 1683, 1573, 1582, 1607, 1615, 1685, 1702, 1586, 1587, 1642,
- /* 380 */ 1646, 1673, 1675, 1636, 1714, 1637, 1677, 1674, 1678, 1694,
- /* 390 */ 1719, 1734, 1735, 1746, 1747, 1750, 1633, 1661, 1686, 1738,
- /* 400 */ 1728, 1733, 1736, 1737, 1740, 1726, 1730, 1742, 1743, 1748,
- /* 410 */ 1753,
+ /* 0 */ -71, 194, 343, 835, -180, -177, 838, -194, -188, -185,
+ /* 10 */ -183, 82, 183, -65, 133, 245, 346, 407, 458, -178,
+ /* 20 */ 75, -275, -4, 310, 312, 489, 575, 596, 463, 686,
+ /* 30 */ 707, 725, 780, 1098, 856, 778, 1059, 1090, 708, 887,
+ /* 40 */ 86, 448, 980, 630, 680, 681, 684, 796, 801, 796,
+ /* 50 */ 801, -261, -261, -261, -261, -261, -261, -261, -261, -261,
+ /* 60 */ -261, -261, -261, -261, -261, -261, -261, -261, -261, -261,
+ /* 70 */ -261, -261, -261, -261, -261, -261, -261, -261, -261, -261,
+ /* 80 */ -261, -261, -261, -261, -261, -261, -261, -261, 391, 886,
+ /* 90 */ 888, 1013, 1016, 1081, 1087, 1151, 1159, 1177, 1185, 1188,
+ /* 100 */ 1190, 1194, 1197, 1203, 1247, 1260, 1264, 1267, 1269, 1273,
+ /* 110 */ 1315, 1322, 1335, 1337, 1356, 1362, 1418, 1425, 1453, 1457,
+ /* 120 */ 1465, 1473, 1487, 1495, 1507, 1517, 1521, 1534, 1543, 1546,
+ /* 130 */ 1549, 1552, 1554, 1560, 1581, 1590, 1593, 1595, 1621, 1623,
+ /* 140 */ -261, -261, -261, -261, -261, -261, -261, -261, -261, -261,
+ /* 150 */ -261, -186, -117, 260, 263, 460, 631, -74, 497, -181,
+ /* 160 */ -261, 939, 176, 274, 338, 676, -261, -261, -261, -261,
+ /* 170 */ -212, -212, -212, -184, 149, 777, 1061, 1103, 265, 419,
+ /* 180 */ -254, 670, 677, 677, -11, -129, 184, 488, 736, 789,
+ /* 190 */ 805, 844, 403, 529, 579, 668, 783, 841, 1158, 1112,
+ /* 200 */ 806, 861, 1095, 846, 839, 1031, -189, 1077, 1080, 1116,
+ /* 210 */ 1084, 1156, 1139, 1221, 46, 1099, 1037, 1118, 1171, 1214,
+ /* 220 */ 1210, 1258, -210, -190, -176, -115, 117, 262, 376, 490,
+ /* 230 */ 511, 520, 618, 639, 743, 901, 907, 958, 1014, 1055,
+ /* 240 */ 1108, 1193, 1244, 720, 1248, 1277, 1324, 1347, 1417, 1431,
+ /* 250 */ 1432, 1440, 1451, 1452, 1463, 1478, 1286, 1350, 1369, 1490,
+ /* 260 */ 1498, 1501, 773, 1509, 1513, 1528, 1292, 1367, 1535, 1536,
+ /* 270 */ 1477, 1542, 376, 1547, 1550, 1555, 1559, 1568, 1571, 1441,
+ /* 280 */ 1443, 1474, 1511, 1516, 1519, 1522, 773, 1474, 1474, 1503,
+ /* 290 */ 1567, 1594, 1484, 1527, 1556, 1570, 1557, 1524, 1573, 1545,
+ /* 300 */ 1548, 1576, 1561, 1587, 1540, 1575, 1606, 1611, 1622, 1624,
+ /* 310 */ 1626, 1582, 1597, 1598, 1599, 1601, 1603, 1563, 1608, 1605,
+ /* 320 */ 1604, 1564, 1566, 1655, 1660, 1578, 1579, 1665, 1586, 1607,
+ /* 330 */ 1610, 1642, 1641, 1645, 1646, 1647, 1679, 1688, 1644, 1618,
+ /* 340 */ 1619, 1648, 1628, 1659, 1649, 1663, 1650, 1700, 1702, 1612,
+ /* 350 */ 1615, 1706, 1708, 1689, 1709, 1712, 1713, 1715, 1691, 1698,
+ /* 360 */ 1699, 1701, 1693, 1704, 1707, 1705, 1714, 1703, 1718, 1710,
+ /* 370 */ 1720, 1721, 1632, 1634, 1664, 1666, 1729, 1751, 1635, 1636,
+ /* 380 */ 1692, 1694, 1716, 1722, 1684, 1763, 1685, 1723, 1724, 1727,
+ /* 390 */ 1730, 1768, 1783, 1784, 1793, 1794, 1796, 1683, 1686, 1690,
+ /* 400 */ 1782, 1779, 1780, 1781, 1785, 1788, 1774, 1775, 1786, 1787,
+ /* 410 */ 1789, 1798,
};
static const YYACTIONTYPE yy_default[] = {
- /* 0 */ 1648, 1648, 1648, 1478, 1243, 1354, 1243, 1243, 1243, 1478,
- /* 10 */ 1478, 1478, 1243, 1384, 1384, 1531, 1276, 1243, 1243, 1243,
- /* 20 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1477, 1243,
- /* 30 */ 1243, 1243, 1243, 1564, 1564, 1243, 1243, 1243, 1243, 1243,
- /* 40 */ 1243, 1243, 1243, 1393, 1243, 1400, 1243, 1243, 1243, 1243,
- /* 50 */ 1243, 1479, 1480, 1243, 1243, 1243, 1530, 1532, 1495, 1407,
- /* 60 */ 1406, 1405, 1404, 1513, 1372, 1398, 1391, 1395, 1474, 1475,
- /* 70 */ 1473, 1626, 1480, 1479, 1243, 1394, 1442, 1458, 1441, 1243,
- /* 80 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
- /* 90 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
- /* 100 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
- /* 110 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
- /* 120 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
- /* 130 */ 1243, 1450, 1457, 1456, 1455, 1464, 1454, 1451, 1444, 1443,
- /* 140 */ 1445, 1446, 1243, 1243, 1267, 1243, 1243, 1264, 1318, 1243,
- /* 150 */ 1243, 1243, 1243, 1243, 1550, 1549, 1243, 1447, 1243, 1276,
- /* 160 */ 1435, 1434, 1433, 1461, 1448, 1460, 1459, 1538, 1600, 1599,
- /* 170 */ 1496, 1243, 1243, 1243, 1243, 1243, 1243, 1564, 1243, 1243,
- /* 180 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
- /* 190 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
- /* 200 */ 1243, 1374, 1564, 1564, 1243, 1276, 1564, 1564, 1375, 1375,
- /* 210 */ 1272, 1272, 1378, 1243, 1545, 1345, 1345, 1345, 1345, 1354,
- /* 220 */ 1345, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
- /* 230 */ 1243, 1243, 1243, 1243, 1243, 1243, 1535, 1533, 1243, 1243,
- /* 240 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
- /* 250 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
- /* 260 */ 1243, 1243, 1243, 1243, 1243, 1350, 1243, 1243, 1243, 1243,
- /* 270 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1593, 1243, 1508,
- /* 280 */ 1332, 1350, 1350, 1350, 1350, 1352, 1333, 1331, 1344, 1277,
- /* 290 */ 1250, 1640, 1410, 1399, 1351, 1399, 1637, 1397, 1410, 1410,
- /* 300 */ 1397, 1410, 1351, 1637, 1293, 1615, 1288, 1384, 1384, 1384,
- /* 310 */ 1374, 1374, 1374, 1374, 1378, 1378, 1476, 1351, 1344, 1243,
- /* 320 */ 1640, 1640, 1360, 1360, 1639, 1639, 1360, 1496, 1623, 1419,
- /* 330 */ 1321, 1327, 1327, 1327, 1327, 1360, 1261, 1397, 1623, 1623,
- /* 340 */ 1397, 1419, 1321, 1397, 1321, 1397, 1360, 1261, 1512, 1634,
- /* 350 */ 1360, 1261, 1486, 1360, 1261, 1360, 1261, 1486, 1319, 1319,
- /* 360 */ 1319, 1308, 1243, 1243, 1486, 1319, 1293, 1319, 1308, 1319,
- /* 370 */ 1319, 1582, 1243, 1490, 1490, 1486, 1360, 1574, 1574, 1387,
- /* 380 */ 1387, 1392, 1378, 1481, 1360, 1243, 1392, 1390, 1388, 1397,
- /* 390 */ 1311, 1596, 1596, 1592, 1592, 1592, 1645, 1645, 1545, 1608,
- /* 400 */ 1276, 1276, 1276, 1276, 1608, 1295, 1295, 1277, 1277, 1276,
- /* 410 */ 1608, 1243, 1243, 1243, 1243, 1243, 1243, 1603, 1243, 1540,
- /* 420 */ 1497, 1364, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
- /* 430 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1551, 1243,
- /* 440 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1424,
- /* 450 */ 1243, 1246, 1542, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
- /* 460 */ 1243, 1401, 1402, 1365, 1243, 1243, 1243, 1243, 1243, 1243,
- /* 470 */ 1243, 1416, 1243, 1243, 1243, 1411, 1243, 1243, 1243, 1243,
- /* 480 */ 1243, 1243, 1243, 1243, 1636, 1243, 1243, 1243, 1243, 1243,
- /* 490 */ 1243, 1511, 1510, 1243, 1243, 1362, 1243, 1243, 1243, 1243,
- /* 500 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1291,
- /* 510 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
- /* 520 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
- /* 530 */ 1243, 1243, 1243, 1389, 1243, 1243, 1243, 1243, 1243, 1243,
- /* 540 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1579, 1379,
- /* 550 */ 1243, 1243, 1243, 1243, 1627, 1243, 1243, 1243, 1243, 1243,
- /* 560 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1619,
- /* 570 */ 1335, 1425, 1243, 1428, 1265, 1243, 1255, 1243, 1243,
+ /* 0 */ 1663, 1663, 1663, 1491, 1254, 1367, 1254, 1254, 1254, 1254,
+ /* 10 */ 1491, 1491, 1491, 1254, 1254, 1254, 1254, 1254, 1254, 1397,
+ /* 20 */ 1397, 1544, 1287, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 30 */ 1254, 1254, 1254, 1254, 1254, 1490, 1254, 1254, 1254, 1254,
+ /* 40 */ 1578, 1578, 1254, 1254, 1254, 1254, 1254, 1563, 1562, 1254,
+ /* 50 */ 1254, 1254, 1406, 1254, 1413, 1254, 1254, 1254, 1254, 1254,
+ /* 60 */ 1492, 1493, 1254, 1254, 1254, 1543, 1545, 1508, 1420, 1419,
+ /* 70 */ 1418, 1417, 1526, 1385, 1411, 1404, 1408, 1487, 1488, 1486,
+ /* 80 */ 1641, 1493, 1492, 1254, 1407, 1455, 1471, 1454, 1254, 1254,
+ /* 90 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 100 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 110 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 120 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 130 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 140 */ 1463, 1470, 1469, 1468, 1477, 1467, 1464, 1457, 1456, 1458,
+ /* 150 */ 1459, 1278, 1254, 1275, 1329, 1254, 1254, 1254, 1254, 1254,
+ /* 160 */ 1460, 1287, 1448, 1447, 1446, 1254, 1474, 1461, 1473, 1472,
+ /* 170 */ 1551, 1615, 1614, 1509, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 180 */ 1578, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 190 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 200 */ 1254, 1254, 1254, 1387, 1578, 1578, 1254, 1287, 1578, 1578,
+ /* 210 */ 1388, 1388, 1283, 1283, 1391, 1558, 1358, 1358, 1358, 1358,
+ /* 220 */ 1367, 1358, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 230 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1548, 1546, 1254,
+ /* 240 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 250 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 260 */ 1254, 1254, 1254, 1254, 1254, 1254, 1363, 1254, 1254, 1254,
+ /* 270 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1608, 1254,
+ /* 280 */ 1521, 1343, 1363, 1363, 1363, 1363, 1365, 1344, 1342, 1357,
+ /* 290 */ 1288, 1261, 1655, 1423, 1412, 1364, 1412, 1652, 1410, 1423,
+ /* 300 */ 1423, 1410, 1423, 1364, 1652, 1304, 1630, 1299, 1397, 1397,
+ /* 310 */ 1397, 1387, 1387, 1387, 1387, 1391, 1391, 1489, 1364, 1357,
+ /* 320 */ 1254, 1655, 1655, 1373, 1373, 1654, 1654, 1373, 1509, 1638,
+ /* 330 */ 1432, 1332, 1338, 1338, 1338, 1338, 1373, 1272, 1410, 1638,
+ /* 340 */ 1638, 1410, 1432, 1332, 1410, 1332, 1410, 1373, 1272, 1525,
+ /* 350 */ 1649, 1373, 1272, 1499, 1373, 1272, 1373, 1272, 1499, 1330,
+ /* 360 */ 1330, 1330, 1319, 1254, 1254, 1499, 1330, 1304, 1330, 1319,
+ /* 370 */ 1330, 1330, 1596, 1254, 1503, 1503, 1499, 1373, 1588, 1588,
+ /* 380 */ 1400, 1400, 1405, 1391, 1494, 1373, 1254, 1405, 1403, 1401,
+ /* 390 */ 1410, 1322, 1611, 1611, 1607, 1607, 1607, 1660, 1660, 1558,
+ /* 400 */ 1623, 1287, 1287, 1287, 1287, 1623, 1306, 1306, 1288, 1288,
+ /* 410 */ 1287, 1623, 1254, 1254, 1254, 1254, 1254, 1254, 1618, 1254,
+ /* 420 */ 1553, 1510, 1377, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 430 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1564,
+ /* 440 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 450 */ 1254, 1437, 1254, 1257, 1555, 1254, 1254, 1254, 1254, 1254,
+ /* 460 */ 1254, 1254, 1254, 1414, 1415, 1378, 1254, 1254, 1254, 1254,
+ /* 470 */ 1254, 1254, 1254, 1429, 1254, 1254, 1254, 1424, 1254, 1254,
+ /* 480 */ 1254, 1254, 1254, 1254, 1254, 1254, 1651, 1254, 1254, 1254,
+ /* 490 */ 1254, 1254, 1254, 1524, 1523, 1254, 1254, 1375, 1254, 1254,
+ /* 500 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 510 */ 1254, 1302, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 520 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 530 */ 1254, 1254, 1254, 1254, 1254, 1402, 1254, 1254, 1254, 1254,
+ /* 540 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 550 */ 1593, 1392, 1254, 1254, 1254, 1254, 1642, 1254, 1254, 1254,
+ /* 560 */ 1254, 1352, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 570 */ 1254, 1254, 1254, 1634, 1346, 1438, 1254, 1441, 1276, 1254,
+ /* 580 */ 1266, 1254, 1254,
};
/********** End of lemon-generated parsing tables *****************************/
@@ -172521,8 +173902,8 @@ static const YYCODETYPE yyFallback[] = {
0, /* TRUEFALSE => nothing */
0, /* ISNOT => nothing */
0, /* FUNCTION => nothing */
- 0, /* UMINUS => nothing */
0, /* UPLUS => nothing */
+ 0, /* UMINUS => nothing */
0, /* TRUTH => nothing */
0, /* REGISTER => nothing */
0, /* VECTOR => nothing */
@@ -172531,6 +173912,7 @@ static const YYCODETYPE yyFallback[] = {
0, /* ASTERISK => nothing */
0, /* SPAN => nothing */
0, /* ERROR => nothing */
+ 0, /* QNUMBER => nothing */
0, /* SPACE => nothing */
0, /* ILLEGAL => nothing */
};
@@ -172573,14 +173955,9 @@ struct yyParser {
#endif
sqlite3ParserARG_SDECL /* A place to hold %extra_argument */
sqlite3ParserCTX_SDECL /* A place to hold %extra_context */
-#if YYSTACKDEPTH<=0
- int yystksz; /* Current side of the stack */
- yyStackEntry *yystack; /* The parser's stack */
- yyStackEntry yystk0; /* First stack entry */
-#else
- yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */
- yyStackEntry *yystackEnd; /* Last entry in the stack */
-#endif
+ yyStackEntry *yystackEnd; /* Last entry in the stack */
+ yyStackEntry *yystack; /* The parser stack */
+ yyStackEntry yystk0[YYSTACKDEPTH]; /* Initial stack space */
};
typedef struct yyParser yyParser;
@@ -172794,8 +174171,8 @@ static const char *const yyTokenName[] = {
/* 170 */ "TRUEFALSE",
/* 171 */ "ISNOT",
/* 172 */ "FUNCTION",
- /* 173 */ "UMINUS",
- /* 174 */ "UPLUS",
+ /* 173 */ "UPLUS",
+ /* 174 */ "UMINUS",
/* 175 */ "TRUTH",
/* 176 */ "REGISTER",
/* 177 */ "VECTOR",
@@ -172804,142 +174181,145 @@ static const char *const yyTokenName[] = {
/* 180 */ "ASTERISK",
/* 181 */ "SPAN",
/* 182 */ "ERROR",
- /* 183 */ "SPACE",
- /* 184 */ "ILLEGAL",
- /* 185 */ "input",
- /* 186 */ "cmdlist",
- /* 187 */ "ecmd",
- /* 188 */ "cmdx",
- /* 189 */ "explain",
- /* 190 */ "cmd",
- /* 191 */ "transtype",
- /* 192 */ "trans_opt",
- /* 193 */ "nm",
- /* 194 */ "savepoint_opt",
- /* 195 */ "create_table",
- /* 196 */ "create_table_args",
- /* 197 */ "createkw",
- /* 198 */ "temp",
- /* 199 */ "ifnotexists",
- /* 200 */ "dbnm",
- /* 201 */ "columnlist",
- /* 202 */ "conslist_opt",
- /* 203 */ "table_option_set",
- /* 204 */ "select",
- /* 205 */ "table_option",
- /* 206 */ "columnname",
- /* 207 */ "carglist",
- /* 208 */ "typetoken",
- /* 209 */ "typename",
- /* 210 */ "signed",
- /* 211 */ "plus_num",
- /* 212 */ "minus_num",
- /* 213 */ "scanpt",
- /* 214 */ "scantok",
- /* 215 */ "ccons",
- /* 216 */ "term",
- /* 217 */ "expr",
- /* 218 */ "onconf",
- /* 219 */ "sortorder",
- /* 220 */ "autoinc",
- /* 221 */ "eidlist_opt",
- /* 222 */ "refargs",
- /* 223 */ "defer_subclause",
- /* 224 */ "generated",
- /* 225 */ "refarg",
- /* 226 */ "refact",
- /* 227 */ "init_deferred_pred_opt",
- /* 228 */ "conslist",
- /* 229 */ "tconscomma",
- /* 230 */ "tcons",
- /* 231 */ "sortlist",
- /* 232 */ "eidlist",
- /* 233 */ "defer_subclause_opt",
- /* 234 */ "orconf",
- /* 235 */ "resolvetype",
- /* 236 */ "raisetype",
- /* 237 */ "ifexists",
- /* 238 */ "fullname",
- /* 239 */ "selectnowith",
- /* 240 */ "oneselect",
- /* 241 */ "wqlist",
- /* 242 */ "multiselect_op",
- /* 243 */ "distinct",
- /* 244 */ "selcollist",
- /* 245 */ "from",
- /* 246 */ "where_opt",
- /* 247 */ "groupby_opt",
- /* 248 */ "having_opt",
- /* 249 */ "orderby_opt",
- /* 250 */ "limit_opt",
- /* 251 */ "window_clause",
- /* 252 */ "values",
- /* 253 */ "nexprlist",
- /* 254 */ "sclp",
- /* 255 */ "as",
- /* 256 */ "seltablist",
- /* 257 */ "stl_prefix",
- /* 258 */ "joinop",
- /* 259 */ "on_using",
- /* 260 */ "indexed_by",
- /* 261 */ "exprlist",
- /* 262 */ "xfullname",
- /* 263 */ "idlist",
- /* 264 */ "indexed_opt",
- /* 265 */ "nulls",
- /* 266 */ "with",
- /* 267 */ "where_opt_ret",
- /* 268 */ "setlist",
- /* 269 */ "insert_cmd",
- /* 270 */ "idlist_opt",
- /* 271 */ "upsert",
- /* 272 */ "returning",
- /* 273 */ "filter_over",
- /* 274 */ "likeop",
- /* 275 */ "between_op",
- /* 276 */ "in_op",
- /* 277 */ "paren_exprlist",
- /* 278 */ "case_operand",
- /* 279 */ "case_exprlist",
- /* 280 */ "case_else",
- /* 281 */ "uniqueflag",
- /* 282 */ "collate",
- /* 283 */ "vinto",
- /* 284 */ "nmnum",
- /* 285 */ "trigger_decl",
- /* 286 */ "trigger_cmd_list",
- /* 287 */ "trigger_time",
- /* 288 */ "trigger_event",
- /* 289 */ "foreach_clause",
- /* 290 */ "when_clause",
- /* 291 */ "trigger_cmd",
- /* 292 */ "trnm",
- /* 293 */ "tridxby",
- /* 294 */ "database_kw_opt",
- /* 295 */ "key_opt",
- /* 296 */ "add_column_fullname",
- /* 297 */ "kwcolumn_opt",
- /* 298 */ "create_vtab",
- /* 299 */ "vtabarglist",
- /* 300 */ "vtabarg",
- /* 301 */ "vtabargtoken",
- /* 302 */ "lp",
- /* 303 */ "anylist",
- /* 304 */ "wqitem",
- /* 305 */ "wqas",
- /* 306 */ "windowdefn_list",
- /* 307 */ "windowdefn",
- /* 308 */ "window",
- /* 309 */ "frame_opt",
- /* 310 */ "part_opt",
- /* 311 */ "filter_clause",
- /* 312 */ "over_clause",
- /* 313 */ "range_or_rows",
- /* 314 */ "frame_bound",
- /* 315 */ "frame_bound_s",
- /* 316 */ "frame_bound_e",
- /* 317 */ "frame_exclude_opt",
- /* 318 */ "frame_exclude",
+ /* 183 */ "QNUMBER",
+ /* 184 */ "SPACE",
+ /* 185 */ "ILLEGAL",
+ /* 186 */ "input",
+ /* 187 */ "cmdlist",
+ /* 188 */ "ecmd",
+ /* 189 */ "cmdx",
+ /* 190 */ "explain",
+ /* 191 */ "cmd",
+ /* 192 */ "transtype",
+ /* 193 */ "trans_opt",
+ /* 194 */ "nm",
+ /* 195 */ "savepoint_opt",
+ /* 196 */ "create_table",
+ /* 197 */ "create_table_args",
+ /* 198 */ "createkw",
+ /* 199 */ "temp",
+ /* 200 */ "ifnotexists",
+ /* 201 */ "dbnm",
+ /* 202 */ "columnlist",
+ /* 203 */ "conslist_opt",
+ /* 204 */ "table_option_set",
+ /* 205 */ "select",
+ /* 206 */ "table_option",
+ /* 207 */ "columnname",
+ /* 208 */ "carglist",
+ /* 209 */ "typetoken",
+ /* 210 */ "typename",
+ /* 211 */ "signed",
+ /* 212 */ "plus_num",
+ /* 213 */ "minus_num",
+ /* 214 */ "scanpt",
+ /* 215 */ "scantok",
+ /* 216 */ "ccons",
+ /* 217 */ "term",
+ /* 218 */ "expr",
+ /* 219 */ "onconf",
+ /* 220 */ "sortorder",
+ /* 221 */ "autoinc",
+ /* 222 */ "eidlist_opt",
+ /* 223 */ "refargs",
+ /* 224 */ "defer_subclause",
+ /* 225 */ "generated",
+ /* 226 */ "refarg",
+ /* 227 */ "refact",
+ /* 228 */ "init_deferred_pred_opt",
+ /* 229 */ "conslist",
+ /* 230 */ "tconscomma",
+ /* 231 */ "tcons",
+ /* 232 */ "sortlist",
+ /* 233 */ "eidlist",
+ /* 234 */ "defer_subclause_opt",
+ /* 235 */ "orconf",
+ /* 236 */ "resolvetype",
+ /* 237 */ "raisetype",
+ /* 238 */ "ifexists",
+ /* 239 */ "fullname",
+ /* 240 */ "selectnowith",
+ /* 241 */ "oneselect",
+ /* 242 */ "wqlist",
+ /* 243 */ "multiselect_op",
+ /* 244 */ "distinct",
+ /* 245 */ "selcollist",
+ /* 246 */ "from",
+ /* 247 */ "where_opt",
+ /* 248 */ "groupby_opt",
+ /* 249 */ "having_opt",
+ /* 250 */ "orderby_opt",
+ /* 251 */ "limit_opt",
+ /* 252 */ "window_clause",
+ /* 253 */ "values",
+ /* 254 */ "nexprlist",
+ /* 255 */ "mvalues",
+ /* 256 */ "sclp",
+ /* 257 */ "as",
+ /* 258 */ "seltablist",
+ /* 259 */ "stl_prefix",
+ /* 260 */ "joinop",
+ /* 261 */ "on_using",
+ /* 262 */ "indexed_by",
+ /* 263 */ "exprlist",
+ /* 264 */ "xfullname",
+ /* 265 */ "idlist",
+ /* 266 */ "indexed_opt",
+ /* 267 */ "nulls",
+ /* 268 */ "with",
+ /* 269 */ "where_opt_ret",
+ /* 270 */ "setlist",
+ /* 271 */ "insert_cmd",
+ /* 272 */ "idlist_opt",
+ /* 273 */ "upsert",
+ /* 274 */ "returning",
+ /* 275 */ "filter_over",
+ /* 276 */ "likeop",
+ /* 277 */ "between_op",
+ /* 278 */ "in_op",
+ /* 279 */ "paren_exprlist",
+ /* 280 */ "case_operand",
+ /* 281 */ "case_exprlist",
+ /* 282 */ "case_else",
+ /* 283 */ "uniqueflag",
+ /* 284 */ "collate",
+ /* 285 */ "vinto",
+ /* 286 */ "nmnum",
+ /* 287 */ "trigger_decl",
+ /* 288 */ "trigger_cmd_list",
+ /* 289 */ "trigger_time",
+ /* 290 */ "trigger_event",
+ /* 291 */ "foreach_clause",
+ /* 292 */ "when_clause",
+ /* 293 */ "trigger_cmd",
+ /* 294 */ "trnm",
+ /* 295 */ "tridxby",
+ /* 296 */ "database_kw_opt",
+ /* 297 */ "key_opt",
+ /* 298 */ "add_column_fullname",
+ /* 299 */ "kwcolumn_opt",
+ /* 300 */ "create_vtab",
+ /* 301 */ "vtabarglist",
+ /* 302 */ "vtabarg",
+ /* 303 */ "vtabargtoken",
+ /* 304 */ "lp",
+ /* 305 */ "anylist",
+ /* 306 */ "wqitem",
+ /* 307 */ "wqas",
+ /* 308 */ "withnm",
+ /* 309 */ "windowdefn_list",
+ /* 310 */ "windowdefn",
+ /* 311 */ "window",
+ /* 312 */ "frame_opt",
+ /* 313 */ "part_opt",
+ /* 314 */ "filter_clause",
+ /* 315 */ "over_clause",
+ /* 316 */ "range_or_rows",
+ /* 317 */ "frame_bound",
+ /* 318 */ "frame_bound_s",
+ /* 319 */ "frame_bound_e",
+ /* 320 */ "frame_exclude_opt",
+ /* 321 */ "frame_exclude",
};
#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */
@@ -173042,351 +174422,363 @@ static const char *const yyRuleName[] = {
/* 92 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt",
/* 93 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt",
/* 94 */ "values ::= VALUES LP nexprlist RP",
- /* 95 */ "values ::= values COMMA LP nexprlist RP",
- /* 96 */ "distinct ::= DISTINCT",
- /* 97 */ "distinct ::= ALL",
- /* 98 */ "distinct ::=",
- /* 99 */ "sclp ::=",
- /* 100 */ "selcollist ::= sclp scanpt expr scanpt as",
- /* 101 */ "selcollist ::= sclp scanpt STAR",
- /* 102 */ "selcollist ::= sclp scanpt nm DOT STAR",
- /* 103 */ "as ::= AS nm",
- /* 104 */ "as ::=",
- /* 105 */ "from ::=",
- /* 106 */ "from ::= FROM seltablist",
- /* 107 */ "stl_prefix ::= seltablist joinop",
- /* 108 */ "stl_prefix ::=",
- /* 109 */ "seltablist ::= stl_prefix nm dbnm as on_using",
- /* 110 */ "seltablist ::= stl_prefix nm dbnm as indexed_by on_using",
- /* 111 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using",
- /* 112 */ "seltablist ::= stl_prefix LP select RP as on_using",
- /* 113 */ "seltablist ::= stl_prefix LP seltablist RP as on_using",
- /* 114 */ "dbnm ::=",
- /* 115 */ "dbnm ::= DOT nm",
- /* 116 */ "fullname ::= nm",
- /* 117 */ "fullname ::= nm DOT nm",
- /* 118 */ "xfullname ::= nm",
- /* 119 */ "xfullname ::= nm DOT nm",
- /* 120 */ "xfullname ::= nm DOT nm AS nm",
- /* 121 */ "xfullname ::= nm AS nm",
- /* 122 */ "joinop ::= COMMA|JOIN",
- /* 123 */ "joinop ::= JOIN_KW JOIN",
- /* 124 */ "joinop ::= JOIN_KW nm JOIN",
- /* 125 */ "joinop ::= JOIN_KW nm nm JOIN",
- /* 126 */ "on_using ::= ON expr",
- /* 127 */ "on_using ::= USING LP idlist RP",
- /* 128 */ "on_using ::=",
- /* 129 */ "indexed_opt ::=",
- /* 130 */ "indexed_by ::= INDEXED BY nm",
- /* 131 */ "indexed_by ::= NOT INDEXED",
- /* 132 */ "orderby_opt ::=",
- /* 133 */ "orderby_opt ::= ORDER BY sortlist",
- /* 134 */ "sortlist ::= sortlist COMMA expr sortorder nulls",
- /* 135 */ "sortlist ::= expr sortorder nulls",
- /* 136 */ "sortorder ::= ASC",
- /* 137 */ "sortorder ::= DESC",
- /* 138 */ "sortorder ::=",
- /* 139 */ "nulls ::= NULLS FIRST",
- /* 140 */ "nulls ::= NULLS LAST",
- /* 141 */ "nulls ::=",
- /* 142 */ "groupby_opt ::=",
- /* 143 */ "groupby_opt ::= GROUP BY nexprlist",
- /* 144 */ "having_opt ::=",
- /* 145 */ "having_opt ::= HAVING expr",
- /* 146 */ "limit_opt ::=",
- /* 147 */ "limit_opt ::= LIMIT expr",
- /* 148 */ "limit_opt ::= LIMIT expr OFFSET expr",
- /* 149 */ "limit_opt ::= LIMIT expr COMMA expr",
- /* 150 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret",
- /* 151 */ "where_opt ::=",
- /* 152 */ "where_opt ::= WHERE expr",
- /* 153 */ "where_opt_ret ::=",
- /* 154 */ "where_opt_ret ::= WHERE expr",
- /* 155 */ "where_opt_ret ::= RETURNING selcollist",
- /* 156 */ "where_opt_ret ::= WHERE expr RETURNING selcollist",
- /* 157 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret",
- /* 158 */ "setlist ::= setlist COMMA nm EQ expr",
- /* 159 */ "setlist ::= setlist COMMA LP idlist RP EQ expr",
- /* 160 */ "setlist ::= nm EQ expr",
- /* 161 */ "setlist ::= LP idlist RP EQ expr",
- /* 162 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert",
- /* 163 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning",
- /* 164 */ "upsert ::=",
- /* 165 */ "upsert ::= RETURNING selcollist",
- /* 166 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert",
- /* 167 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert",
- /* 168 */ "upsert ::= ON CONFLICT DO NOTHING returning",
- /* 169 */ "upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning",
- /* 170 */ "returning ::= RETURNING selcollist",
- /* 171 */ "insert_cmd ::= INSERT orconf",
- /* 172 */ "insert_cmd ::= REPLACE",
- /* 173 */ "idlist_opt ::=",
- /* 174 */ "idlist_opt ::= LP idlist RP",
- /* 175 */ "idlist ::= idlist COMMA nm",
- /* 176 */ "idlist ::= nm",
- /* 177 */ "expr ::= LP expr RP",
- /* 178 */ "expr ::= ID|INDEXED|JOIN_KW",
- /* 179 */ "expr ::= nm DOT nm",
- /* 180 */ "expr ::= nm DOT nm DOT nm",
- /* 181 */ "term ::= NULL|FLOAT|BLOB",
- /* 182 */ "term ::= STRING",
- /* 183 */ "term ::= INTEGER",
- /* 184 */ "expr ::= VARIABLE",
- /* 185 */ "expr ::= expr COLLATE ID|STRING",
- /* 186 */ "expr ::= CAST LP expr AS typetoken RP",
- /* 187 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP",
- /* 188 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP",
- /* 189 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP",
- /* 190 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over",
- /* 191 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over",
- /* 192 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over",
- /* 193 */ "term ::= CTIME_KW",
- /* 194 */ "expr ::= LP nexprlist COMMA expr RP",
- /* 195 */ "expr ::= expr AND expr",
- /* 196 */ "expr ::= expr OR expr",
- /* 197 */ "expr ::= expr LT|GT|GE|LE expr",
- /* 198 */ "expr ::= expr EQ|NE expr",
- /* 199 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
- /* 200 */ "expr ::= expr PLUS|MINUS expr",
- /* 201 */ "expr ::= expr STAR|SLASH|REM expr",
- /* 202 */ "expr ::= expr CONCAT expr",
- /* 203 */ "likeop ::= NOT LIKE_KW|MATCH",
- /* 204 */ "expr ::= expr likeop expr",
- /* 205 */ "expr ::= expr likeop expr ESCAPE expr",
- /* 206 */ "expr ::= expr ISNULL|NOTNULL",
- /* 207 */ "expr ::= expr NOT NULL",
- /* 208 */ "expr ::= expr IS expr",
- /* 209 */ "expr ::= expr IS NOT expr",
- /* 210 */ "expr ::= expr IS NOT DISTINCT FROM expr",
- /* 211 */ "expr ::= expr IS DISTINCT FROM expr",
- /* 212 */ "expr ::= NOT expr",
- /* 213 */ "expr ::= BITNOT expr",
- /* 214 */ "expr ::= PLUS|MINUS expr",
- /* 215 */ "expr ::= expr PTR expr",
- /* 216 */ "between_op ::= BETWEEN",
- /* 217 */ "between_op ::= NOT BETWEEN",
- /* 218 */ "expr ::= expr between_op expr AND expr",
- /* 219 */ "in_op ::= IN",
- /* 220 */ "in_op ::= NOT IN",
- /* 221 */ "expr ::= expr in_op LP exprlist RP",
- /* 222 */ "expr ::= LP select RP",
- /* 223 */ "expr ::= expr in_op LP select RP",
- /* 224 */ "expr ::= expr in_op nm dbnm paren_exprlist",
- /* 225 */ "expr ::= EXISTS LP select RP",
- /* 226 */ "expr ::= CASE case_operand case_exprlist case_else END",
- /* 227 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
- /* 228 */ "case_exprlist ::= WHEN expr THEN expr",
- /* 229 */ "case_else ::= ELSE expr",
- /* 230 */ "case_else ::=",
- /* 231 */ "case_operand ::=",
- /* 232 */ "exprlist ::=",
- /* 233 */ "nexprlist ::= nexprlist COMMA expr",
- /* 234 */ "nexprlist ::= expr",
- /* 235 */ "paren_exprlist ::=",
- /* 236 */ "paren_exprlist ::= LP exprlist RP",
- /* 237 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt",
- /* 238 */ "uniqueflag ::= UNIQUE",
- /* 239 */ "uniqueflag ::=",
- /* 240 */ "eidlist_opt ::=",
- /* 241 */ "eidlist_opt ::= LP eidlist RP",
- /* 242 */ "eidlist ::= eidlist COMMA nm collate sortorder",
- /* 243 */ "eidlist ::= nm collate sortorder",
- /* 244 */ "collate ::=",
- /* 245 */ "collate ::= COLLATE ID|STRING",
- /* 246 */ "cmd ::= DROP INDEX ifexists fullname",
- /* 247 */ "cmd ::= VACUUM vinto",
- /* 248 */ "cmd ::= VACUUM nm vinto",
- /* 249 */ "vinto ::= INTO expr",
- /* 250 */ "vinto ::=",
- /* 251 */ "cmd ::= PRAGMA nm dbnm",
- /* 252 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
- /* 253 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
- /* 254 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
- /* 255 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
- /* 256 */ "plus_num ::= PLUS INTEGER|FLOAT",
- /* 257 */ "minus_num ::= MINUS INTEGER|FLOAT",
- /* 258 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
- /* 259 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
- /* 260 */ "trigger_time ::= BEFORE|AFTER",
- /* 261 */ "trigger_time ::= INSTEAD OF",
- /* 262 */ "trigger_time ::=",
- /* 263 */ "trigger_event ::= DELETE|INSERT",
- /* 264 */ "trigger_event ::= UPDATE",
- /* 265 */ "trigger_event ::= UPDATE OF idlist",
- /* 266 */ "when_clause ::=",
- /* 267 */ "when_clause ::= WHEN expr",
- /* 268 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
- /* 269 */ "trigger_cmd_list ::= trigger_cmd SEMI",
- /* 270 */ "trnm ::= nm DOT nm",
- /* 271 */ "tridxby ::= INDEXED BY nm",
- /* 272 */ "tridxby ::= NOT INDEXED",
- /* 273 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt",
- /* 274 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt",
- /* 275 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt",
- /* 276 */ "trigger_cmd ::= scanpt select scanpt",
- /* 277 */ "expr ::= RAISE LP IGNORE RP",
- /* 278 */ "expr ::= RAISE LP raisetype COMMA nm RP",
- /* 279 */ "raisetype ::= ROLLBACK",
- /* 280 */ "raisetype ::= ABORT",
- /* 281 */ "raisetype ::= FAIL",
- /* 282 */ "cmd ::= DROP TRIGGER ifexists fullname",
- /* 283 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
- /* 284 */ "cmd ::= DETACH database_kw_opt expr",
- /* 285 */ "key_opt ::=",
- /* 286 */ "key_opt ::= KEY expr",
- /* 287 */ "cmd ::= REINDEX",
- /* 288 */ "cmd ::= REINDEX nm dbnm",
- /* 289 */ "cmd ::= ANALYZE",
- /* 290 */ "cmd ::= ANALYZE nm dbnm",
- /* 291 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
- /* 292 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
- /* 293 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm",
- /* 294 */ "add_column_fullname ::= fullname",
- /* 295 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm",
- /* 296 */ "cmd ::= create_vtab",
- /* 297 */ "cmd ::= create_vtab LP vtabarglist RP",
- /* 298 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
- /* 299 */ "vtabarg ::=",
- /* 300 */ "vtabargtoken ::= ANY",
- /* 301 */ "vtabargtoken ::= lp anylist RP",
- /* 302 */ "lp ::= LP",
- /* 303 */ "with ::= WITH wqlist",
- /* 304 */ "with ::= WITH RECURSIVE wqlist",
- /* 305 */ "wqas ::= AS",
- /* 306 */ "wqas ::= AS MATERIALIZED",
- /* 307 */ "wqas ::= AS NOT MATERIALIZED",
- /* 308 */ "wqitem ::= nm eidlist_opt wqas LP select RP",
- /* 309 */ "wqlist ::= wqitem",
- /* 310 */ "wqlist ::= wqlist COMMA wqitem",
- /* 311 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn",
- /* 312 */ "windowdefn ::= nm AS LP window RP",
- /* 313 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt",
- /* 314 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt",
- /* 315 */ "window ::= ORDER BY sortlist frame_opt",
- /* 316 */ "window ::= nm ORDER BY sortlist frame_opt",
- /* 317 */ "window ::= nm frame_opt",
- /* 318 */ "frame_opt ::=",
- /* 319 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt",
- /* 320 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt",
- /* 321 */ "range_or_rows ::= RANGE|ROWS|GROUPS",
- /* 322 */ "frame_bound_s ::= frame_bound",
- /* 323 */ "frame_bound_s ::= UNBOUNDED PRECEDING",
- /* 324 */ "frame_bound_e ::= frame_bound",
- /* 325 */ "frame_bound_e ::= UNBOUNDED FOLLOWING",
- /* 326 */ "frame_bound ::= expr PRECEDING|FOLLOWING",
- /* 327 */ "frame_bound ::= CURRENT ROW",
- /* 328 */ "frame_exclude_opt ::=",
- /* 329 */ "frame_exclude_opt ::= EXCLUDE frame_exclude",
- /* 330 */ "frame_exclude ::= NO OTHERS",
- /* 331 */ "frame_exclude ::= CURRENT ROW",
- /* 332 */ "frame_exclude ::= GROUP|TIES",
- /* 333 */ "window_clause ::= WINDOW windowdefn_list",
- /* 334 */ "filter_over ::= filter_clause over_clause",
- /* 335 */ "filter_over ::= over_clause",
- /* 336 */ "filter_over ::= filter_clause",
- /* 337 */ "over_clause ::= OVER LP window RP",
- /* 338 */ "over_clause ::= OVER nm",
- /* 339 */ "filter_clause ::= FILTER LP WHERE expr RP",
- /* 340 */ "input ::= cmdlist",
- /* 341 */ "cmdlist ::= cmdlist ecmd",
- /* 342 */ "cmdlist ::= ecmd",
- /* 343 */ "ecmd ::= SEMI",
- /* 344 */ "ecmd ::= cmdx SEMI",
- /* 345 */ "ecmd ::= explain cmdx SEMI",
- /* 346 */ "trans_opt ::=",
- /* 347 */ "trans_opt ::= TRANSACTION",
- /* 348 */ "trans_opt ::= TRANSACTION nm",
- /* 349 */ "savepoint_opt ::= SAVEPOINT",
- /* 350 */ "savepoint_opt ::=",
- /* 351 */ "cmd ::= create_table create_table_args",
- /* 352 */ "table_option_set ::= table_option",
- /* 353 */ "columnlist ::= columnlist COMMA columnname carglist",
- /* 354 */ "columnlist ::= columnname carglist",
- /* 355 */ "nm ::= ID|INDEXED|JOIN_KW",
- /* 356 */ "nm ::= STRING",
- /* 357 */ "typetoken ::= typename",
- /* 358 */ "typename ::= ID|STRING",
- /* 359 */ "signed ::= plus_num",
- /* 360 */ "signed ::= minus_num",
- /* 361 */ "carglist ::= carglist ccons",
- /* 362 */ "carglist ::=",
- /* 363 */ "ccons ::= NULL onconf",
- /* 364 */ "ccons ::= GENERATED ALWAYS AS generated",
- /* 365 */ "ccons ::= AS generated",
- /* 366 */ "conslist_opt ::= COMMA conslist",
- /* 367 */ "conslist ::= conslist tconscomma tcons",
- /* 368 */ "conslist ::= tcons",
- /* 369 */ "tconscomma ::=",
- /* 370 */ "defer_subclause_opt ::= defer_subclause",
- /* 371 */ "resolvetype ::= raisetype",
- /* 372 */ "selectnowith ::= oneselect",
- /* 373 */ "oneselect ::= values",
- /* 374 */ "sclp ::= selcollist COMMA",
- /* 375 */ "as ::= ID|STRING",
- /* 376 */ "indexed_opt ::= indexed_by",
- /* 377 */ "returning ::=",
- /* 378 */ "expr ::= term",
- /* 379 */ "likeop ::= LIKE_KW|MATCH",
- /* 380 */ "case_operand ::= expr",
- /* 381 */ "exprlist ::= nexprlist",
- /* 382 */ "nmnum ::= plus_num",
- /* 383 */ "nmnum ::= nm",
- /* 384 */ "nmnum ::= ON",
- /* 385 */ "nmnum ::= DELETE",
- /* 386 */ "nmnum ::= DEFAULT",
- /* 387 */ "plus_num ::= INTEGER|FLOAT",
- /* 388 */ "foreach_clause ::=",
- /* 389 */ "foreach_clause ::= FOR EACH ROW",
- /* 390 */ "trnm ::= nm",
- /* 391 */ "tridxby ::=",
- /* 392 */ "database_kw_opt ::= DATABASE",
- /* 393 */ "database_kw_opt ::=",
- /* 394 */ "kwcolumn_opt ::=",
- /* 395 */ "kwcolumn_opt ::= COLUMNKW",
- /* 396 */ "vtabarglist ::= vtabarg",
- /* 397 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
- /* 398 */ "vtabarg ::= vtabarg vtabargtoken",
- /* 399 */ "anylist ::=",
- /* 400 */ "anylist ::= anylist LP anylist RP",
- /* 401 */ "anylist ::= anylist ANY",
- /* 402 */ "with ::=",
- /* 403 */ "windowdefn_list ::= windowdefn",
- /* 404 */ "window ::= frame_opt",
+ /* 95 */ "oneselect ::= mvalues",
+ /* 96 */ "mvalues ::= values COMMA LP nexprlist RP",
+ /* 97 */ "mvalues ::= mvalues COMMA LP nexprlist RP",
+ /* 98 */ "distinct ::= DISTINCT",
+ /* 99 */ "distinct ::= ALL",
+ /* 100 */ "distinct ::=",
+ /* 101 */ "sclp ::=",
+ /* 102 */ "selcollist ::= sclp scanpt expr scanpt as",
+ /* 103 */ "selcollist ::= sclp scanpt STAR",
+ /* 104 */ "selcollist ::= sclp scanpt nm DOT STAR",
+ /* 105 */ "as ::= AS nm",
+ /* 106 */ "as ::=",
+ /* 107 */ "from ::=",
+ /* 108 */ "from ::= FROM seltablist",
+ /* 109 */ "stl_prefix ::= seltablist joinop",
+ /* 110 */ "stl_prefix ::=",
+ /* 111 */ "seltablist ::= stl_prefix nm dbnm as on_using",
+ /* 112 */ "seltablist ::= stl_prefix nm dbnm as indexed_by on_using",
+ /* 113 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using",
+ /* 114 */ "seltablist ::= stl_prefix LP select RP as on_using",
+ /* 115 */ "seltablist ::= stl_prefix LP seltablist RP as on_using",
+ /* 116 */ "dbnm ::=",
+ /* 117 */ "dbnm ::= DOT nm",
+ /* 118 */ "fullname ::= nm",
+ /* 119 */ "fullname ::= nm DOT nm",
+ /* 120 */ "xfullname ::= nm",
+ /* 121 */ "xfullname ::= nm DOT nm",
+ /* 122 */ "xfullname ::= nm DOT nm AS nm",
+ /* 123 */ "xfullname ::= nm AS nm",
+ /* 124 */ "joinop ::= COMMA|JOIN",
+ /* 125 */ "joinop ::= JOIN_KW JOIN",
+ /* 126 */ "joinop ::= JOIN_KW nm JOIN",
+ /* 127 */ "joinop ::= JOIN_KW nm nm JOIN",
+ /* 128 */ "on_using ::= ON expr",
+ /* 129 */ "on_using ::= USING LP idlist RP",
+ /* 130 */ "on_using ::=",
+ /* 131 */ "indexed_opt ::=",
+ /* 132 */ "indexed_by ::= INDEXED BY nm",
+ /* 133 */ "indexed_by ::= NOT INDEXED",
+ /* 134 */ "orderby_opt ::=",
+ /* 135 */ "orderby_opt ::= ORDER BY sortlist",
+ /* 136 */ "sortlist ::= sortlist COMMA expr sortorder nulls",
+ /* 137 */ "sortlist ::= expr sortorder nulls",
+ /* 138 */ "sortorder ::= ASC",
+ /* 139 */ "sortorder ::= DESC",
+ /* 140 */ "sortorder ::=",
+ /* 141 */ "nulls ::= NULLS FIRST",
+ /* 142 */ "nulls ::= NULLS LAST",
+ /* 143 */ "nulls ::=",
+ /* 144 */ "groupby_opt ::=",
+ /* 145 */ "groupby_opt ::= GROUP BY nexprlist",
+ /* 146 */ "having_opt ::=",
+ /* 147 */ "having_opt ::= HAVING expr",
+ /* 148 */ "limit_opt ::=",
+ /* 149 */ "limit_opt ::= LIMIT expr",
+ /* 150 */ "limit_opt ::= LIMIT expr OFFSET expr",
+ /* 151 */ "limit_opt ::= LIMIT expr COMMA expr",
+ /* 152 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret",
+ /* 153 */ "where_opt ::=",
+ /* 154 */ "where_opt ::= WHERE expr",
+ /* 155 */ "where_opt_ret ::=",
+ /* 156 */ "where_opt_ret ::= WHERE expr",
+ /* 157 */ "where_opt_ret ::= RETURNING selcollist",
+ /* 158 */ "where_opt_ret ::= WHERE expr RETURNING selcollist",
+ /* 159 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret",
+ /* 160 */ "setlist ::= setlist COMMA nm EQ expr",
+ /* 161 */ "setlist ::= setlist COMMA LP idlist RP EQ expr",
+ /* 162 */ "setlist ::= nm EQ expr",
+ /* 163 */ "setlist ::= LP idlist RP EQ expr",
+ /* 164 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert",
+ /* 165 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning",
+ /* 166 */ "upsert ::=",
+ /* 167 */ "upsert ::= RETURNING selcollist",
+ /* 168 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert",
+ /* 169 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert",
+ /* 170 */ "upsert ::= ON CONFLICT DO NOTHING returning",
+ /* 171 */ "upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning",
+ /* 172 */ "returning ::= RETURNING selcollist",
+ /* 173 */ "insert_cmd ::= INSERT orconf",
+ /* 174 */ "insert_cmd ::= REPLACE",
+ /* 175 */ "idlist_opt ::=",
+ /* 176 */ "idlist_opt ::= LP idlist RP",
+ /* 177 */ "idlist ::= idlist COMMA nm",
+ /* 178 */ "idlist ::= nm",
+ /* 179 */ "expr ::= LP expr RP",
+ /* 180 */ "expr ::= ID|INDEXED|JOIN_KW",
+ /* 181 */ "expr ::= nm DOT nm",
+ /* 182 */ "expr ::= nm DOT nm DOT nm",
+ /* 183 */ "term ::= NULL|FLOAT|BLOB",
+ /* 184 */ "term ::= STRING",
+ /* 185 */ "term ::= INTEGER",
+ /* 186 */ "expr ::= VARIABLE",
+ /* 187 */ "expr ::= expr COLLATE ID|STRING",
+ /* 188 */ "expr ::= CAST LP expr AS typetoken RP",
+ /* 189 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP",
+ /* 190 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP",
+ /* 191 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP",
+ /* 192 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over",
+ /* 193 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over",
+ /* 194 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over",
+ /* 195 */ "term ::= CTIME_KW",
+ /* 196 */ "expr ::= LP nexprlist COMMA expr RP",
+ /* 197 */ "expr ::= expr AND expr",
+ /* 198 */ "expr ::= expr OR expr",
+ /* 199 */ "expr ::= expr LT|GT|GE|LE expr",
+ /* 200 */ "expr ::= expr EQ|NE expr",
+ /* 201 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
+ /* 202 */ "expr ::= expr PLUS|MINUS expr",
+ /* 203 */ "expr ::= expr STAR|SLASH|REM expr",
+ /* 204 */ "expr ::= expr CONCAT expr",
+ /* 205 */ "likeop ::= NOT LIKE_KW|MATCH",
+ /* 206 */ "expr ::= expr likeop expr",
+ /* 207 */ "expr ::= expr likeop expr ESCAPE expr",
+ /* 208 */ "expr ::= expr ISNULL|NOTNULL",
+ /* 209 */ "expr ::= expr NOT NULL",
+ /* 210 */ "expr ::= expr IS expr",
+ /* 211 */ "expr ::= expr IS NOT expr",
+ /* 212 */ "expr ::= expr IS NOT DISTINCT FROM expr",
+ /* 213 */ "expr ::= expr IS DISTINCT FROM expr",
+ /* 214 */ "expr ::= NOT expr",
+ /* 215 */ "expr ::= BITNOT expr",
+ /* 216 */ "expr ::= PLUS|MINUS expr",
+ /* 217 */ "expr ::= expr PTR expr",
+ /* 218 */ "between_op ::= BETWEEN",
+ /* 219 */ "between_op ::= NOT BETWEEN",
+ /* 220 */ "expr ::= expr between_op expr AND expr",
+ /* 221 */ "in_op ::= IN",
+ /* 222 */ "in_op ::= NOT IN",
+ /* 223 */ "expr ::= expr in_op LP exprlist RP",
+ /* 224 */ "expr ::= LP select RP",
+ /* 225 */ "expr ::= expr in_op LP select RP",
+ /* 226 */ "expr ::= expr in_op nm dbnm paren_exprlist",
+ /* 227 */ "expr ::= EXISTS LP select RP",
+ /* 228 */ "expr ::= CASE case_operand case_exprlist case_else END",
+ /* 229 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
+ /* 230 */ "case_exprlist ::= WHEN expr THEN expr",
+ /* 231 */ "case_else ::= ELSE expr",
+ /* 232 */ "case_else ::=",
+ /* 233 */ "case_operand ::=",
+ /* 234 */ "exprlist ::=",
+ /* 235 */ "nexprlist ::= nexprlist COMMA expr",
+ /* 236 */ "nexprlist ::= expr",
+ /* 237 */ "paren_exprlist ::=",
+ /* 238 */ "paren_exprlist ::= LP exprlist RP",
+ /* 239 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt",
+ /* 240 */ "uniqueflag ::= UNIQUE",
+ /* 241 */ "uniqueflag ::=",
+ /* 242 */ "eidlist_opt ::=",
+ /* 243 */ "eidlist_opt ::= LP eidlist RP",
+ /* 244 */ "eidlist ::= eidlist COMMA nm collate sortorder",
+ /* 245 */ "eidlist ::= nm collate sortorder",
+ /* 246 */ "collate ::=",
+ /* 247 */ "collate ::= COLLATE ID|STRING",
+ /* 248 */ "cmd ::= DROP INDEX ifexists fullname",
+ /* 249 */ "cmd ::= VACUUM vinto",
+ /* 250 */ "cmd ::= VACUUM nm vinto",
+ /* 251 */ "vinto ::= INTO expr",
+ /* 252 */ "vinto ::=",
+ /* 253 */ "cmd ::= PRAGMA nm dbnm",
+ /* 254 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
+ /* 255 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
+ /* 256 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
+ /* 257 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
+ /* 258 */ "plus_num ::= PLUS INTEGER|FLOAT",
+ /* 259 */ "minus_num ::= MINUS INTEGER|FLOAT",
+ /* 260 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
+ /* 261 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
+ /* 262 */ "trigger_time ::= BEFORE|AFTER",
+ /* 263 */ "trigger_time ::= INSTEAD OF",
+ /* 264 */ "trigger_time ::=",
+ /* 265 */ "trigger_event ::= DELETE|INSERT",
+ /* 266 */ "trigger_event ::= UPDATE",
+ /* 267 */ "trigger_event ::= UPDATE OF idlist",
+ /* 268 */ "when_clause ::=",
+ /* 269 */ "when_clause ::= WHEN expr",
+ /* 270 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
+ /* 271 */ "trigger_cmd_list ::= trigger_cmd SEMI",
+ /* 272 */ "trnm ::= nm DOT nm",
+ /* 273 */ "tridxby ::= INDEXED BY nm",
+ /* 274 */ "tridxby ::= NOT INDEXED",
+ /* 275 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt",
+ /* 276 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt",
+ /* 277 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt",
+ /* 278 */ "trigger_cmd ::= scanpt select scanpt",
+ /* 279 */ "expr ::= RAISE LP IGNORE RP",
+ /* 280 */ "expr ::= RAISE LP raisetype COMMA nm RP",
+ /* 281 */ "raisetype ::= ROLLBACK",
+ /* 282 */ "raisetype ::= ABORT",
+ /* 283 */ "raisetype ::= FAIL",
+ /* 284 */ "cmd ::= DROP TRIGGER ifexists fullname",
+ /* 285 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
+ /* 286 */ "cmd ::= DETACH database_kw_opt expr",
+ /* 287 */ "key_opt ::=",
+ /* 288 */ "key_opt ::= KEY expr",
+ /* 289 */ "cmd ::= REINDEX",
+ /* 290 */ "cmd ::= REINDEX nm dbnm",
+ /* 291 */ "cmd ::= ANALYZE",
+ /* 292 */ "cmd ::= ANALYZE nm dbnm",
+ /* 293 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
+ /* 294 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
+ /* 295 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm",
+ /* 296 */ "add_column_fullname ::= fullname",
+ /* 297 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm",
+ /* 298 */ "cmd ::= create_vtab",
+ /* 299 */ "cmd ::= create_vtab LP vtabarglist RP",
+ /* 300 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
+ /* 301 */ "vtabarg ::=",
+ /* 302 */ "vtabargtoken ::= ANY",
+ /* 303 */ "vtabargtoken ::= lp anylist RP",
+ /* 304 */ "lp ::= LP",
+ /* 305 */ "with ::= WITH wqlist",
+ /* 306 */ "with ::= WITH RECURSIVE wqlist",
+ /* 307 */ "wqas ::= AS",
+ /* 308 */ "wqas ::= AS MATERIALIZED",
+ /* 309 */ "wqas ::= AS NOT MATERIALIZED",
+ /* 310 */ "wqitem ::= withnm eidlist_opt wqas LP select RP",
+ /* 311 */ "withnm ::= nm",
+ /* 312 */ "wqlist ::= wqitem",
+ /* 313 */ "wqlist ::= wqlist COMMA wqitem",
+ /* 314 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn",
+ /* 315 */ "windowdefn ::= nm AS LP window RP",
+ /* 316 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt",
+ /* 317 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt",
+ /* 318 */ "window ::= ORDER BY sortlist frame_opt",
+ /* 319 */ "window ::= nm ORDER BY sortlist frame_opt",
+ /* 320 */ "window ::= nm frame_opt",
+ /* 321 */ "frame_opt ::=",
+ /* 322 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt",
+ /* 323 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt",
+ /* 324 */ "range_or_rows ::= RANGE|ROWS|GROUPS",
+ /* 325 */ "frame_bound_s ::= frame_bound",
+ /* 326 */ "frame_bound_s ::= UNBOUNDED PRECEDING",
+ /* 327 */ "frame_bound_e ::= frame_bound",
+ /* 328 */ "frame_bound_e ::= UNBOUNDED FOLLOWING",
+ /* 329 */ "frame_bound ::= expr PRECEDING|FOLLOWING",
+ /* 330 */ "frame_bound ::= CURRENT ROW",
+ /* 331 */ "frame_exclude_opt ::=",
+ /* 332 */ "frame_exclude_opt ::= EXCLUDE frame_exclude",
+ /* 333 */ "frame_exclude ::= NO OTHERS",
+ /* 334 */ "frame_exclude ::= CURRENT ROW",
+ /* 335 */ "frame_exclude ::= GROUP|TIES",
+ /* 336 */ "window_clause ::= WINDOW windowdefn_list",
+ /* 337 */ "filter_over ::= filter_clause over_clause",
+ /* 338 */ "filter_over ::= over_clause",
+ /* 339 */ "filter_over ::= filter_clause",
+ /* 340 */ "over_clause ::= OVER LP window RP",
+ /* 341 */ "over_clause ::= OVER nm",
+ /* 342 */ "filter_clause ::= FILTER LP WHERE expr RP",
+ /* 343 */ "term ::= QNUMBER",
+ /* 344 */ "input ::= cmdlist",
+ /* 345 */ "cmdlist ::= cmdlist ecmd",
+ /* 346 */ "cmdlist ::= ecmd",
+ /* 347 */ "ecmd ::= SEMI",
+ /* 348 */ "ecmd ::= cmdx SEMI",
+ /* 349 */ "ecmd ::= explain cmdx SEMI",
+ /* 350 */ "trans_opt ::=",
+ /* 351 */ "trans_opt ::= TRANSACTION",
+ /* 352 */ "trans_opt ::= TRANSACTION nm",
+ /* 353 */ "savepoint_opt ::= SAVEPOINT",
+ /* 354 */ "savepoint_opt ::=",
+ /* 355 */ "cmd ::= create_table create_table_args",
+ /* 356 */ "table_option_set ::= table_option",
+ /* 357 */ "columnlist ::= columnlist COMMA columnname carglist",
+ /* 358 */ "columnlist ::= columnname carglist",
+ /* 359 */ "nm ::= ID|INDEXED|JOIN_KW",
+ /* 360 */ "nm ::= STRING",
+ /* 361 */ "typetoken ::= typename",
+ /* 362 */ "typename ::= ID|STRING",
+ /* 363 */ "signed ::= plus_num",
+ /* 364 */ "signed ::= minus_num",
+ /* 365 */ "carglist ::= carglist ccons",
+ /* 366 */ "carglist ::=",
+ /* 367 */ "ccons ::= NULL onconf",
+ /* 368 */ "ccons ::= GENERATED ALWAYS AS generated",
+ /* 369 */ "ccons ::= AS generated",
+ /* 370 */ "conslist_opt ::= COMMA conslist",
+ /* 371 */ "conslist ::= conslist tconscomma tcons",
+ /* 372 */ "conslist ::= tcons",
+ /* 373 */ "tconscomma ::=",
+ /* 374 */ "defer_subclause_opt ::= defer_subclause",
+ /* 375 */ "resolvetype ::= raisetype",
+ /* 376 */ "selectnowith ::= oneselect",
+ /* 377 */ "oneselect ::= values",
+ /* 378 */ "sclp ::= selcollist COMMA",
+ /* 379 */ "as ::= ID|STRING",
+ /* 380 */ "indexed_opt ::= indexed_by",
+ /* 381 */ "returning ::=",
+ /* 382 */ "expr ::= term",
+ /* 383 */ "likeop ::= LIKE_KW|MATCH",
+ /* 384 */ "case_operand ::= expr",
+ /* 385 */ "exprlist ::= nexprlist",
+ /* 386 */ "nmnum ::= plus_num",
+ /* 387 */ "nmnum ::= nm",
+ /* 388 */ "nmnum ::= ON",
+ /* 389 */ "nmnum ::= DELETE",
+ /* 390 */ "nmnum ::= DEFAULT",
+ /* 391 */ "plus_num ::= INTEGER|FLOAT",
+ /* 392 */ "foreach_clause ::=",
+ /* 393 */ "foreach_clause ::= FOR EACH ROW",
+ /* 394 */ "trnm ::= nm",
+ /* 395 */ "tridxby ::=",
+ /* 396 */ "database_kw_opt ::= DATABASE",
+ /* 397 */ "database_kw_opt ::=",
+ /* 398 */ "kwcolumn_opt ::=",
+ /* 399 */ "kwcolumn_opt ::= COLUMNKW",
+ /* 400 */ "vtabarglist ::= vtabarg",
+ /* 401 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
+ /* 402 */ "vtabarg ::= vtabarg vtabargtoken",
+ /* 403 */ "anylist ::=",
+ /* 404 */ "anylist ::= anylist LP anylist RP",
+ /* 405 */ "anylist ::= anylist ANY",
+ /* 406 */ "with ::=",
+ /* 407 */ "windowdefn_list ::= windowdefn",
+ /* 408 */ "window ::= frame_opt",
};
#endif /* NDEBUG */
-#if YYSTACKDEPTH<=0
+#if YYGROWABLESTACK
/*
** Try to increase the size of the parser stack. Return the number
** of errors. Return 0 on success.
*/
static int yyGrowStack(yyParser *p){
+ int oldSize = 1 + (int)(p->yystackEnd - p->yystack);
int newSize;
int idx;
yyStackEntry *pNew;
- newSize = p->yystksz*2 + 100;
- idx = p->yytos ? (int)(p->yytos - p->yystack) : 0;
- if( p->yystack==&p->yystk0 ){
- pNew = malloc(newSize*sizeof(pNew[0]));
- if( pNew ) pNew[0] = p->yystk0;
+ newSize = oldSize*2 + 100;
+ idx = (int)(p->yytos - p->yystack);
+ if( p->yystack==p->yystk0 ){
+ pNew = YYREALLOC(0, newSize*sizeof(pNew[0]));
+ if( pNew==0 ) return 1;
+ memcpy(pNew, p->yystack, oldSize*sizeof(pNew[0]));
}else{
- pNew = realloc(p->yystack, newSize*sizeof(pNew[0]));
+ pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0]));
+ if( pNew==0 ) return 1;
}
- if( pNew ){
- p->yystack = pNew;
- p->yytos = &p->yystack[idx];
+ p->yystack = pNew;
+ p->yytos = &p->yystack[idx];
#ifndef NDEBUG
- if( yyTraceFILE ){
- fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n",
- yyTracePrompt, p->yystksz, newSize);
- }
-#endif
- p->yystksz = newSize;
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n",
+ yyTracePrompt, oldSize, newSize);
}
- return pNew==0;
+#endif
+ p->yystackEnd = &p->yystack[newSize-1];
+ return 0;
}
+#endif /* YYGROWABLESTACK */
+
+#if !YYGROWABLESTACK
+/* For builds that do no have a growable stack, yyGrowStack always
+** returns an error.
+*/
+# define yyGrowStack(X) 1
#endif
/* Datatype of the argument to the memory allocated passed as the
@@ -173406,24 +174798,14 @@ SQLITE_PRIVATE void sqlite3ParserInit(void *yypRawParser sqlite3ParserCTX_PDECL)
#ifdef YYTRACKMAXSTACKDEPTH
yypParser->yyhwm = 0;
#endif
-#if YYSTACKDEPTH<=0
- yypParser->yytos = NULL;
- yypParser->yystack = NULL;
- yypParser->yystksz = 0;
- if( yyGrowStack(yypParser) ){
- yypParser->yystack = &yypParser->yystk0;
- yypParser->yystksz = 1;
- }
-#endif
+ yypParser->yystack = yypParser->yystk0;
+ yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1];
#ifndef YYNOERRORRECOVERY
yypParser->yyerrcnt = -1;
#endif
yypParser->yytos = yypParser->yystack;
yypParser->yystack[0].stateno = 0;
yypParser->yystack[0].major = 0;
-#if YYSTACKDEPTH>0
- yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1];
-#endif
}
#ifndef sqlite3Parser_ENGINEALWAYSONSTACK
@@ -173477,97 +174859,98 @@ static void yy_destructor(
** inside the C code.
*/
/********* Begin destructor definitions ***************************************/
- case 204: /* select */
- case 239: /* selectnowith */
- case 240: /* oneselect */
- case 252: /* values */
+ case 205: /* select */
+ case 240: /* selectnowith */
+ case 241: /* oneselect */
+ case 253: /* values */
+ case 255: /* mvalues */
{
-sqlite3SelectDelete(pParse->db, (yypminor->yy47));
-}
- break;
- case 216: /* term */
- case 217: /* expr */
- case 246: /* where_opt */
- case 248: /* having_opt */
- case 267: /* where_opt_ret */
- case 278: /* case_operand */
- case 280: /* case_else */
- case 283: /* vinto */
- case 290: /* when_clause */
- case 295: /* key_opt */
- case 311: /* filter_clause */
+sqlite3SelectDelete(pParse->db, (yypminor->yy555));
+}
+ break;
+ case 217: /* term */
+ case 218: /* expr */
+ case 247: /* where_opt */
+ case 249: /* having_opt */
+ case 269: /* where_opt_ret */
+ case 280: /* case_operand */
+ case 282: /* case_else */
+ case 285: /* vinto */
+ case 292: /* when_clause */
+ case 297: /* key_opt */
+ case 314: /* filter_clause */
{
-sqlite3ExprDelete(pParse->db, (yypminor->yy528));
-}
- break;
- case 221: /* eidlist_opt */
- case 231: /* sortlist */
- case 232: /* eidlist */
- case 244: /* selcollist */
- case 247: /* groupby_opt */
- case 249: /* orderby_opt */
- case 253: /* nexprlist */
- case 254: /* sclp */
- case 261: /* exprlist */
- case 268: /* setlist */
- case 277: /* paren_exprlist */
- case 279: /* case_exprlist */
- case 310: /* part_opt */
+sqlite3ExprDelete(pParse->db, (yypminor->yy454));
+}
+ break;
+ case 222: /* eidlist_opt */
+ case 232: /* sortlist */
+ case 233: /* eidlist */
+ case 245: /* selcollist */
+ case 248: /* groupby_opt */
+ case 250: /* orderby_opt */
+ case 254: /* nexprlist */
+ case 256: /* sclp */
+ case 263: /* exprlist */
+ case 270: /* setlist */
+ case 279: /* paren_exprlist */
+ case 281: /* case_exprlist */
+ case 313: /* part_opt */
{
-sqlite3ExprListDelete(pParse->db, (yypminor->yy322));
+sqlite3ExprListDelete(pParse->db, (yypminor->yy14));
}
break;
- case 238: /* fullname */
- case 245: /* from */
- case 256: /* seltablist */
- case 257: /* stl_prefix */
- case 262: /* xfullname */
+ case 239: /* fullname */
+ case 246: /* from */
+ case 258: /* seltablist */
+ case 259: /* stl_prefix */
+ case 264: /* xfullname */
{
-sqlite3SrcListDelete(pParse->db, (yypminor->yy131));
+sqlite3SrcListDelete(pParse->db, (yypminor->yy203));
}
break;
- case 241: /* wqlist */
+ case 242: /* wqlist */
{
-sqlite3WithDelete(pParse->db, (yypminor->yy521));
+sqlite3WithDelete(pParse->db, (yypminor->yy59));
}
break;
- case 251: /* window_clause */
- case 306: /* windowdefn_list */
+ case 252: /* window_clause */
+ case 309: /* windowdefn_list */
{
-sqlite3WindowListDelete(pParse->db, (yypminor->yy41));
+sqlite3WindowListDelete(pParse->db, (yypminor->yy211));
}
break;
- case 263: /* idlist */
- case 270: /* idlist_opt */
+ case 265: /* idlist */
+ case 272: /* idlist_opt */
{
-sqlite3IdListDelete(pParse->db, (yypminor->yy254));
+sqlite3IdListDelete(pParse->db, (yypminor->yy132));
}
break;
- case 273: /* filter_over */
- case 307: /* windowdefn */
- case 308: /* window */
- case 309: /* frame_opt */
- case 312: /* over_clause */
+ case 275: /* filter_over */
+ case 310: /* windowdefn */
+ case 311: /* window */
+ case 312: /* frame_opt */
+ case 315: /* over_clause */
{
-sqlite3WindowDelete(pParse->db, (yypminor->yy41));
+sqlite3WindowDelete(pParse->db, (yypminor->yy211));
}
break;
- case 286: /* trigger_cmd_list */
- case 291: /* trigger_cmd */
+ case 288: /* trigger_cmd_list */
+ case 293: /* trigger_cmd */
{
-sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy33));
+sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy427));
}
break;
- case 288: /* trigger_event */
+ case 290: /* trigger_event */
{
-sqlite3IdListDelete(pParse->db, (yypminor->yy180).b);
+sqlite3IdListDelete(pParse->db, (yypminor->yy286).b);
}
break;
- case 314: /* frame_bound */
- case 315: /* frame_bound_s */
- case 316: /* frame_bound_e */
+ case 317: /* frame_bound */
+ case 318: /* frame_bound_s */
+ case 319: /* frame_bound_e */
{
-sqlite3ExprDelete(pParse->db, (yypminor->yy595).pExpr);
+sqlite3ExprDelete(pParse->db, (yypminor->yy509).pExpr);
}
break;
/********* End destructor definitions *****************************************/
@@ -173601,9 +174984,26 @@ static void yy_pop_parser_stack(yyParser *pParser){
*/
SQLITE_PRIVATE void sqlite3ParserFinalize(void *p){
yyParser *pParser = (yyParser*)p;
- while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser);
-#if YYSTACKDEPTH<=0
- if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack);
+
+ /* In-lined version of calling yy_pop_parser_stack() for each
+ ** element left in the stack */
+ yyStackEntry *yytos = pParser->yytos;
+ while( yytos>pParser->yystack ){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sPopping %s\n",
+ yyTracePrompt,
+ yyTokenName[yytos->major]);
+ }
+#endif
+ if( yytos->major>=YY_MIN_DSTRCTR ){
+ yy_destructor(pParser, yytos->major, &yytos->minor);
+ }
+ yytos--;
+ }
+
+#if YYGROWABLESTACK
+ if( pParser->yystack!=pParser->yystk0 ) YYFREE(pParser->yystack);
#endif
}
@@ -173786,7 +175186,7 @@ static void yyStackOverflow(yyParser *yypParser){
** stack every overflows */
/******** Begin %stack_overflow code ******************************************/
- sqlite3ErrorMsg(pParse, "parser stack overflow");
+ sqlite3OomFault(pParse->db);
/******** End %stack_overflow code ********************************************/
sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument var */
sqlite3ParserCTX_STORE
@@ -173830,25 +175230,19 @@ static void yy_shift(
assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) );
}
#endif
-#if YYSTACKDEPTH>0
- if( yypParser->yytos>yypParser->yystackEnd ){
- yypParser->yytos--;
- yyStackOverflow(yypParser);
- return;
- }
-#else
- if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz] ){
+ yytos = yypParser->yytos;
+ if( yytos>yypParser->yystackEnd ){
if( yyGrowStack(yypParser) ){
yypParser->yytos--;
yyStackOverflow(yypParser);
return;
}
+ yytos = yypParser->yytos;
+ assert( yytos <= yypParser->yystackEnd );
}
-#endif
if( yyNewState > YY_MAX_SHIFT ){
yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
}
- yytos = yypParser->yytos;
yytos->stateno = yyNewState;
yytos->major = yyMajor;
yytos->minor.yy0 = yyMinor;
@@ -173858,411 +175252,415 @@ static void yy_shift(
/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
** of that rule */
static const YYCODETYPE yyRuleInfoLhs[] = {
- 189, /* (0) explain ::= EXPLAIN */
- 189, /* (1) explain ::= EXPLAIN QUERY PLAN */
- 188, /* (2) cmdx ::= cmd */
- 190, /* (3) cmd ::= BEGIN transtype trans_opt */
- 191, /* (4) transtype ::= */
- 191, /* (5) transtype ::= DEFERRED */
- 191, /* (6) transtype ::= IMMEDIATE */
- 191, /* (7) transtype ::= EXCLUSIVE */
- 190, /* (8) cmd ::= COMMIT|END trans_opt */
- 190, /* (9) cmd ::= ROLLBACK trans_opt */
- 190, /* (10) cmd ::= SAVEPOINT nm */
- 190, /* (11) cmd ::= RELEASE savepoint_opt nm */
- 190, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
- 195, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */
- 197, /* (14) createkw ::= CREATE */
- 199, /* (15) ifnotexists ::= */
- 199, /* (16) ifnotexists ::= IF NOT EXISTS */
- 198, /* (17) temp ::= TEMP */
- 198, /* (18) temp ::= */
- 196, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */
- 196, /* (20) create_table_args ::= AS select */
- 203, /* (21) table_option_set ::= */
- 203, /* (22) table_option_set ::= table_option_set COMMA table_option */
- 205, /* (23) table_option ::= WITHOUT nm */
- 205, /* (24) table_option ::= nm */
- 206, /* (25) columnname ::= nm typetoken */
- 208, /* (26) typetoken ::= */
- 208, /* (27) typetoken ::= typename LP signed RP */
- 208, /* (28) typetoken ::= typename LP signed COMMA signed RP */
- 209, /* (29) typename ::= typename ID|STRING */
- 213, /* (30) scanpt ::= */
- 214, /* (31) scantok ::= */
- 215, /* (32) ccons ::= CONSTRAINT nm */
- 215, /* (33) ccons ::= DEFAULT scantok term */
- 215, /* (34) ccons ::= DEFAULT LP expr RP */
- 215, /* (35) ccons ::= DEFAULT PLUS scantok term */
- 215, /* (36) ccons ::= DEFAULT MINUS scantok term */
- 215, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */
- 215, /* (38) ccons ::= NOT NULL onconf */
- 215, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */
- 215, /* (40) ccons ::= UNIQUE onconf */
- 215, /* (41) ccons ::= CHECK LP expr RP */
- 215, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */
- 215, /* (43) ccons ::= defer_subclause */
- 215, /* (44) ccons ::= COLLATE ID|STRING */
- 224, /* (45) generated ::= LP expr RP */
- 224, /* (46) generated ::= LP expr RP ID */
- 220, /* (47) autoinc ::= */
- 220, /* (48) autoinc ::= AUTOINCR */
- 222, /* (49) refargs ::= */
- 222, /* (50) refargs ::= refargs refarg */
- 225, /* (51) refarg ::= MATCH nm */
- 225, /* (52) refarg ::= ON INSERT refact */
- 225, /* (53) refarg ::= ON DELETE refact */
- 225, /* (54) refarg ::= ON UPDATE refact */
- 226, /* (55) refact ::= SET NULL */
- 226, /* (56) refact ::= SET DEFAULT */
- 226, /* (57) refact ::= CASCADE */
- 226, /* (58) refact ::= RESTRICT */
- 226, /* (59) refact ::= NO ACTION */
- 223, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
- 223, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
- 227, /* (62) init_deferred_pred_opt ::= */
- 227, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */
- 227, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
- 202, /* (65) conslist_opt ::= */
- 229, /* (66) tconscomma ::= COMMA */
- 230, /* (67) tcons ::= CONSTRAINT nm */
- 230, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
- 230, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */
- 230, /* (70) tcons ::= CHECK LP expr RP onconf */
- 230, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
- 233, /* (72) defer_subclause_opt ::= */
- 218, /* (73) onconf ::= */
- 218, /* (74) onconf ::= ON CONFLICT resolvetype */
- 234, /* (75) orconf ::= */
- 234, /* (76) orconf ::= OR resolvetype */
- 235, /* (77) resolvetype ::= IGNORE */
- 235, /* (78) resolvetype ::= REPLACE */
- 190, /* (79) cmd ::= DROP TABLE ifexists fullname */
- 237, /* (80) ifexists ::= IF EXISTS */
- 237, /* (81) ifexists ::= */
- 190, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
- 190, /* (83) cmd ::= DROP VIEW ifexists fullname */
- 190, /* (84) cmd ::= select */
- 204, /* (85) select ::= WITH wqlist selectnowith */
- 204, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */
- 204, /* (87) select ::= selectnowith */
- 239, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */
- 242, /* (89) multiselect_op ::= UNION */
- 242, /* (90) multiselect_op ::= UNION ALL */
- 242, /* (91) multiselect_op ::= EXCEPT|INTERSECT */
- 240, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
- 240, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
- 252, /* (94) values ::= VALUES LP nexprlist RP */
- 252, /* (95) values ::= values COMMA LP nexprlist RP */
- 243, /* (96) distinct ::= DISTINCT */
- 243, /* (97) distinct ::= ALL */
- 243, /* (98) distinct ::= */
- 254, /* (99) sclp ::= */
- 244, /* (100) selcollist ::= sclp scanpt expr scanpt as */
- 244, /* (101) selcollist ::= sclp scanpt STAR */
- 244, /* (102) selcollist ::= sclp scanpt nm DOT STAR */
- 255, /* (103) as ::= AS nm */
- 255, /* (104) as ::= */
- 245, /* (105) from ::= */
- 245, /* (106) from ::= FROM seltablist */
- 257, /* (107) stl_prefix ::= seltablist joinop */
- 257, /* (108) stl_prefix ::= */
- 256, /* (109) seltablist ::= stl_prefix nm dbnm as on_using */
- 256, /* (110) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */
- 256, /* (111) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */
- 256, /* (112) seltablist ::= stl_prefix LP select RP as on_using */
- 256, /* (113) seltablist ::= stl_prefix LP seltablist RP as on_using */
- 200, /* (114) dbnm ::= */
- 200, /* (115) dbnm ::= DOT nm */
- 238, /* (116) fullname ::= nm */
- 238, /* (117) fullname ::= nm DOT nm */
- 262, /* (118) xfullname ::= nm */
- 262, /* (119) xfullname ::= nm DOT nm */
- 262, /* (120) xfullname ::= nm DOT nm AS nm */
- 262, /* (121) xfullname ::= nm AS nm */
- 258, /* (122) joinop ::= COMMA|JOIN */
- 258, /* (123) joinop ::= JOIN_KW JOIN */
- 258, /* (124) joinop ::= JOIN_KW nm JOIN */
- 258, /* (125) joinop ::= JOIN_KW nm nm JOIN */
- 259, /* (126) on_using ::= ON expr */
- 259, /* (127) on_using ::= USING LP idlist RP */
- 259, /* (128) on_using ::= */
- 264, /* (129) indexed_opt ::= */
- 260, /* (130) indexed_by ::= INDEXED BY nm */
- 260, /* (131) indexed_by ::= NOT INDEXED */
- 249, /* (132) orderby_opt ::= */
- 249, /* (133) orderby_opt ::= ORDER BY sortlist */
- 231, /* (134) sortlist ::= sortlist COMMA expr sortorder nulls */
- 231, /* (135) sortlist ::= expr sortorder nulls */
- 219, /* (136) sortorder ::= ASC */
- 219, /* (137) sortorder ::= DESC */
- 219, /* (138) sortorder ::= */
- 265, /* (139) nulls ::= NULLS FIRST */
- 265, /* (140) nulls ::= NULLS LAST */
- 265, /* (141) nulls ::= */
- 247, /* (142) groupby_opt ::= */
- 247, /* (143) groupby_opt ::= GROUP BY nexprlist */
- 248, /* (144) having_opt ::= */
- 248, /* (145) having_opt ::= HAVING expr */
- 250, /* (146) limit_opt ::= */
- 250, /* (147) limit_opt ::= LIMIT expr */
- 250, /* (148) limit_opt ::= LIMIT expr OFFSET expr */
- 250, /* (149) limit_opt ::= LIMIT expr COMMA expr */
- 190, /* (150) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
- 246, /* (151) where_opt ::= */
- 246, /* (152) where_opt ::= WHERE expr */
- 267, /* (153) where_opt_ret ::= */
- 267, /* (154) where_opt_ret ::= WHERE expr */
- 267, /* (155) where_opt_ret ::= RETURNING selcollist */
- 267, /* (156) where_opt_ret ::= WHERE expr RETURNING selcollist */
- 190, /* (157) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
- 268, /* (158) setlist ::= setlist COMMA nm EQ expr */
- 268, /* (159) setlist ::= setlist COMMA LP idlist RP EQ expr */
- 268, /* (160) setlist ::= nm EQ expr */
- 268, /* (161) setlist ::= LP idlist RP EQ expr */
- 190, /* (162) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
- 190, /* (163) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
- 271, /* (164) upsert ::= */
- 271, /* (165) upsert ::= RETURNING selcollist */
- 271, /* (166) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
- 271, /* (167) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
- 271, /* (168) upsert ::= ON CONFLICT DO NOTHING returning */
- 271, /* (169) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
- 272, /* (170) returning ::= RETURNING selcollist */
- 269, /* (171) insert_cmd ::= INSERT orconf */
- 269, /* (172) insert_cmd ::= REPLACE */
- 270, /* (173) idlist_opt ::= */
- 270, /* (174) idlist_opt ::= LP idlist RP */
- 263, /* (175) idlist ::= idlist COMMA nm */
- 263, /* (176) idlist ::= nm */
- 217, /* (177) expr ::= LP expr RP */
- 217, /* (178) expr ::= ID|INDEXED|JOIN_KW */
- 217, /* (179) expr ::= nm DOT nm */
- 217, /* (180) expr ::= nm DOT nm DOT nm */
- 216, /* (181) term ::= NULL|FLOAT|BLOB */
- 216, /* (182) term ::= STRING */
- 216, /* (183) term ::= INTEGER */
- 217, /* (184) expr ::= VARIABLE */
- 217, /* (185) expr ::= expr COLLATE ID|STRING */
- 217, /* (186) expr ::= CAST LP expr AS typetoken RP */
- 217, /* (187) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
- 217, /* (188) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */
- 217, /* (189) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
- 217, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
- 217, /* (191) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */
- 217, /* (192) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
- 216, /* (193) term ::= CTIME_KW */
- 217, /* (194) expr ::= LP nexprlist COMMA expr RP */
- 217, /* (195) expr ::= expr AND expr */
- 217, /* (196) expr ::= expr OR expr */
- 217, /* (197) expr ::= expr LT|GT|GE|LE expr */
- 217, /* (198) expr ::= expr EQ|NE expr */
- 217, /* (199) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
- 217, /* (200) expr ::= expr PLUS|MINUS expr */
- 217, /* (201) expr ::= expr STAR|SLASH|REM expr */
- 217, /* (202) expr ::= expr CONCAT expr */
- 274, /* (203) likeop ::= NOT LIKE_KW|MATCH */
- 217, /* (204) expr ::= expr likeop expr */
- 217, /* (205) expr ::= expr likeop expr ESCAPE expr */
- 217, /* (206) expr ::= expr ISNULL|NOTNULL */
- 217, /* (207) expr ::= expr NOT NULL */
- 217, /* (208) expr ::= expr IS expr */
- 217, /* (209) expr ::= expr IS NOT expr */
- 217, /* (210) expr ::= expr IS NOT DISTINCT FROM expr */
- 217, /* (211) expr ::= expr IS DISTINCT FROM expr */
- 217, /* (212) expr ::= NOT expr */
- 217, /* (213) expr ::= BITNOT expr */
- 217, /* (214) expr ::= PLUS|MINUS expr */
- 217, /* (215) expr ::= expr PTR expr */
- 275, /* (216) between_op ::= BETWEEN */
- 275, /* (217) between_op ::= NOT BETWEEN */
- 217, /* (218) expr ::= expr between_op expr AND expr */
- 276, /* (219) in_op ::= IN */
- 276, /* (220) in_op ::= NOT IN */
- 217, /* (221) expr ::= expr in_op LP exprlist RP */
- 217, /* (222) expr ::= LP select RP */
- 217, /* (223) expr ::= expr in_op LP select RP */
- 217, /* (224) expr ::= expr in_op nm dbnm paren_exprlist */
- 217, /* (225) expr ::= EXISTS LP select RP */
- 217, /* (226) expr ::= CASE case_operand case_exprlist case_else END */
- 279, /* (227) case_exprlist ::= case_exprlist WHEN expr THEN expr */
- 279, /* (228) case_exprlist ::= WHEN expr THEN expr */
- 280, /* (229) case_else ::= ELSE expr */
- 280, /* (230) case_else ::= */
- 278, /* (231) case_operand ::= */
- 261, /* (232) exprlist ::= */
- 253, /* (233) nexprlist ::= nexprlist COMMA expr */
- 253, /* (234) nexprlist ::= expr */
- 277, /* (235) paren_exprlist ::= */
- 277, /* (236) paren_exprlist ::= LP exprlist RP */
- 190, /* (237) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
- 281, /* (238) uniqueflag ::= UNIQUE */
- 281, /* (239) uniqueflag ::= */
- 221, /* (240) eidlist_opt ::= */
- 221, /* (241) eidlist_opt ::= LP eidlist RP */
- 232, /* (242) eidlist ::= eidlist COMMA nm collate sortorder */
- 232, /* (243) eidlist ::= nm collate sortorder */
- 282, /* (244) collate ::= */
- 282, /* (245) collate ::= COLLATE ID|STRING */
- 190, /* (246) cmd ::= DROP INDEX ifexists fullname */
- 190, /* (247) cmd ::= VACUUM vinto */
- 190, /* (248) cmd ::= VACUUM nm vinto */
- 283, /* (249) vinto ::= INTO expr */
- 283, /* (250) vinto ::= */
- 190, /* (251) cmd ::= PRAGMA nm dbnm */
- 190, /* (252) cmd ::= PRAGMA nm dbnm EQ nmnum */
- 190, /* (253) cmd ::= PRAGMA nm dbnm LP nmnum RP */
- 190, /* (254) cmd ::= PRAGMA nm dbnm EQ minus_num */
- 190, /* (255) cmd ::= PRAGMA nm dbnm LP minus_num RP */
- 211, /* (256) plus_num ::= PLUS INTEGER|FLOAT */
- 212, /* (257) minus_num ::= MINUS INTEGER|FLOAT */
- 190, /* (258) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
- 285, /* (259) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
- 287, /* (260) trigger_time ::= BEFORE|AFTER */
- 287, /* (261) trigger_time ::= INSTEAD OF */
- 287, /* (262) trigger_time ::= */
- 288, /* (263) trigger_event ::= DELETE|INSERT */
- 288, /* (264) trigger_event ::= UPDATE */
- 288, /* (265) trigger_event ::= UPDATE OF idlist */
- 290, /* (266) when_clause ::= */
- 290, /* (267) when_clause ::= WHEN expr */
- 286, /* (268) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
- 286, /* (269) trigger_cmd_list ::= trigger_cmd SEMI */
- 292, /* (270) trnm ::= nm DOT nm */
- 293, /* (271) tridxby ::= INDEXED BY nm */
- 293, /* (272) tridxby ::= NOT INDEXED */
- 291, /* (273) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
- 291, /* (274) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
- 291, /* (275) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
- 291, /* (276) trigger_cmd ::= scanpt select scanpt */
- 217, /* (277) expr ::= RAISE LP IGNORE RP */
- 217, /* (278) expr ::= RAISE LP raisetype COMMA nm RP */
- 236, /* (279) raisetype ::= ROLLBACK */
- 236, /* (280) raisetype ::= ABORT */
- 236, /* (281) raisetype ::= FAIL */
- 190, /* (282) cmd ::= DROP TRIGGER ifexists fullname */
- 190, /* (283) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
- 190, /* (284) cmd ::= DETACH database_kw_opt expr */
- 295, /* (285) key_opt ::= */
- 295, /* (286) key_opt ::= KEY expr */
- 190, /* (287) cmd ::= REINDEX */
- 190, /* (288) cmd ::= REINDEX nm dbnm */
- 190, /* (289) cmd ::= ANALYZE */
- 190, /* (290) cmd ::= ANALYZE nm dbnm */
- 190, /* (291) cmd ::= ALTER TABLE fullname RENAME TO nm */
- 190, /* (292) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
- 190, /* (293) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
- 296, /* (294) add_column_fullname ::= fullname */
- 190, /* (295) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
- 190, /* (296) cmd ::= create_vtab */
- 190, /* (297) cmd ::= create_vtab LP vtabarglist RP */
- 298, /* (298) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
- 300, /* (299) vtabarg ::= */
- 301, /* (300) vtabargtoken ::= ANY */
- 301, /* (301) vtabargtoken ::= lp anylist RP */
- 302, /* (302) lp ::= LP */
- 266, /* (303) with ::= WITH wqlist */
- 266, /* (304) with ::= WITH RECURSIVE wqlist */
- 305, /* (305) wqas ::= AS */
- 305, /* (306) wqas ::= AS MATERIALIZED */
- 305, /* (307) wqas ::= AS NOT MATERIALIZED */
- 304, /* (308) wqitem ::= nm eidlist_opt wqas LP select RP */
- 241, /* (309) wqlist ::= wqitem */
- 241, /* (310) wqlist ::= wqlist COMMA wqitem */
- 306, /* (311) windowdefn_list ::= windowdefn_list COMMA windowdefn */
- 307, /* (312) windowdefn ::= nm AS LP window RP */
- 308, /* (313) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
- 308, /* (314) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
- 308, /* (315) window ::= ORDER BY sortlist frame_opt */
- 308, /* (316) window ::= nm ORDER BY sortlist frame_opt */
- 308, /* (317) window ::= nm frame_opt */
- 309, /* (318) frame_opt ::= */
- 309, /* (319) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
- 309, /* (320) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
- 313, /* (321) range_or_rows ::= RANGE|ROWS|GROUPS */
- 315, /* (322) frame_bound_s ::= frame_bound */
- 315, /* (323) frame_bound_s ::= UNBOUNDED PRECEDING */
- 316, /* (324) frame_bound_e ::= frame_bound */
- 316, /* (325) frame_bound_e ::= UNBOUNDED FOLLOWING */
- 314, /* (326) frame_bound ::= expr PRECEDING|FOLLOWING */
- 314, /* (327) frame_bound ::= CURRENT ROW */
- 317, /* (328) frame_exclude_opt ::= */
- 317, /* (329) frame_exclude_opt ::= EXCLUDE frame_exclude */
- 318, /* (330) frame_exclude ::= NO OTHERS */
- 318, /* (331) frame_exclude ::= CURRENT ROW */
- 318, /* (332) frame_exclude ::= GROUP|TIES */
- 251, /* (333) window_clause ::= WINDOW windowdefn_list */
- 273, /* (334) filter_over ::= filter_clause over_clause */
- 273, /* (335) filter_over ::= over_clause */
- 273, /* (336) filter_over ::= filter_clause */
- 312, /* (337) over_clause ::= OVER LP window RP */
- 312, /* (338) over_clause ::= OVER nm */
- 311, /* (339) filter_clause ::= FILTER LP WHERE expr RP */
- 185, /* (340) input ::= cmdlist */
- 186, /* (341) cmdlist ::= cmdlist ecmd */
- 186, /* (342) cmdlist ::= ecmd */
- 187, /* (343) ecmd ::= SEMI */
- 187, /* (344) ecmd ::= cmdx SEMI */
- 187, /* (345) ecmd ::= explain cmdx SEMI */
- 192, /* (346) trans_opt ::= */
- 192, /* (347) trans_opt ::= TRANSACTION */
- 192, /* (348) trans_opt ::= TRANSACTION nm */
- 194, /* (349) savepoint_opt ::= SAVEPOINT */
- 194, /* (350) savepoint_opt ::= */
- 190, /* (351) cmd ::= create_table create_table_args */
- 203, /* (352) table_option_set ::= table_option */
- 201, /* (353) columnlist ::= columnlist COMMA columnname carglist */
- 201, /* (354) columnlist ::= columnname carglist */
- 193, /* (355) nm ::= ID|INDEXED|JOIN_KW */
- 193, /* (356) nm ::= STRING */
- 208, /* (357) typetoken ::= typename */
- 209, /* (358) typename ::= ID|STRING */
- 210, /* (359) signed ::= plus_num */
- 210, /* (360) signed ::= minus_num */
- 207, /* (361) carglist ::= carglist ccons */
- 207, /* (362) carglist ::= */
- 215, /* (363) ccons ::= NULL onconf */
- 215, /* (364) ccons ::= GENERATED ALWAYS AS generated */
- 215, /* (365) ccons ::= AS generated */
- 202, /* (366) conslist_opt ::= COMMA conslist */
- 228, /* (367) conslist ::= conslist tconscomma tcons */
- 228, /* (368) conslist ::= tcons */
- 229, /* (369) tconscomma ::= */
- 233, /* (370) defer_subclause_opt ::= defer_subclause */
- 235, /* (371) resolvetype ::= raisetype */
- 239, /* (372) selectnowith ::= oneselect */
- 240, /* (373) oneselect ::= values */
- 254, /* (374) sclp ::= selcollist COMMA */
- 255, /* (375) as ::= ID|STRING */
- 264, /* (376) indexed_opt ::= indexed_by */
- 272, /* (377) returning ::= */
- 217, /* (378) expr ::= term */
- 274, /* (379) likeop ::= LIKE_KW|MATCH */
- 278, /* (380) case_operand ::= expr */
- 261, /* (381) exprlist ::= nexprlist */
- 284, /* (382) nmnum ::= plus_num */
- 284, /* (383) nmnum ::= nm */
- 284, /* (384) nmnum ::= ON */
- 284, /* (385) nmnum ::= DELETE */
- 284, /* (386) nmnum ::= DEFAULT */
- 211, /* (387) plus_num ::= INTEGER|FLOAT */
- 289, /* (388) foreach_clause ::= */
- 289, /* (389) foreach_clause ::= FOR EACH ROW */
- 292, /* (390) trnm ::= nm */
- 293, /* (391) tridxby ::= */
- 294, /* (392) database_kw_opt ::= DATABASE */
- 294, /* (393) database_kw_opt ::= */
- 297, /* (394) kwcolumn_opt ::= */
- 297, /* (395) kwcolumn_opt ::= COLUMNKW */
- 299, /* (396) vtabarglist ::= vtabarg */
- 299, /* (397) vtabarglist ::= vtabarglist COMMA vtabarg */
- 300, /* (398) vtabarg ::= vtabarg vtabargtoken */
- 303, /* (399) anylist ::= */
- 303, /* (400) anylist ::= anylist LP anylist RP */
- 303, /* (401) anylist ::= anylist ANY */
- 266, /* (402) with ::= */
- 306, /* (403) windowdefn_list ::= windowdefn */
- 308, /* (404) window ::= frame_opt */
+ 190, /* (0) explain ::= EXPLAIN */
+ 190, /* (1) explain ::= EXPLAIN QUERY PLAN */
+ 189, /* (2) cmdx ::= cmd */
+ 191, /* (3) cmd ::= BEGIN transtype trans_opt */
+ 192, /* (4) transtype ::= */
+ 192, /* (5) transtype ::= DEFERRED */
+ 192, /* (6) transtype ::= IMMEDIATE */
+ 192, /* (7) transtype ::= EXCLUSIVE */
+ 191, /* (8) cmd ::= COMMIT|END trans_opt */
+ 191, /* (9) cmd ::= ROLLBACK trans_opt */
+ 191, /* (10) cmd ::= SAVEPOINT nm */
+ 191, /* (11) cmd ::= RELEASE savepoint_opt nm */
+ 191, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
+ 196, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */
+ 198, /* (14) createkw ::= CREATE */
+ 200, /* (15) ifnotexists ::= */
+ 200, /* (16) ifnotexists ::= IF NOT EXISTS */
+ 199, /* (17) temp ::= TEMP */
+ 199, /* (18) temp ::= */
+ 197, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */
+ 197, /* (20) create_table_args ::= AS select */
+ 204, /* (21) table_option_set ::= */
+ 204, /* (22) table_option_set ::= table_option_set COMMA table_option */
+ 206, /* (23) table_option ::= WITHOUT nm */
+ 206, /* (24) table_option ::= nm */
+ 207, /* (25) columnname ::= nm typetoken */
+ 209, /* (26) typetoken ::= */
+ 209, /* (27) typetoken ::= typename LP signed RP */
+ 209, /* (28) typetoken ::= typename LP signed COMMA signed RP */
+ 210, /* (29) typename ::= typename ID|STRING */
+ 214, /* (30) scanpt ::= */
+ 215, /* (31) scantok ::= */
+ 216, /* (32) ccons ::= CONSTRAINT nm */
+ 216, /* (33) ccons ::= DEFAULT scantok term */
+ 216, /* (34) ccons ::= DEFAULT LP expr RP */
+ 216, /* (35) ccons ::= DEFAULT PLUS scantok term */
+ 216, /* (36) ccons ::= DEFAULT MINUS scantok term */
+ 216, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */
+ 216, /* (38) ccons ::= NOT NULL onconf */
+ 216, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */
+ 216, /* (40) ccons ::= UNIQUE onconf */
+ 216, /* (41) ccons ::= CHECK LP expr RP */
+ 216, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */
+ 216, /* (43) ccons ::= defer_subclause */
+ 216, /* (44) ccons ::= COLLATE ID|STRING */
+ 225, /* (45) generated ::= LP expr RP */
+ 225, /* (46) generated ::= LP expr RP ID */
+ 221, /* (47) autoinc ::= */
+ 221, /* (48) autoinc ::= AUTOINCR */
+ 223, /* (49) refargs ::= */
+ 223, /* (50) refargs ::= refargs refarg */
+ 226, /* (51) refarg ::= MATCH nm */
+ 226, /* (52) refarg ::= ON INSERT refact */
+ 226, /* (53) refarg ::= ON DELETE refact */
+ 226, /* (54) refarg ::= ON UPDATE refact */
+ 227, /* (55) refact ::= SET NULL */
+ 227, /* (56) refact ::= SET DEFAULT */
+ 227, /* (57) refact ::= CASCADE */
+ 227, /* (58) refact ::= RESTRICT */
+ 227, /* (59) refact ::= NO ACTION */
+ 224, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
+ 224, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
+ 228, /* (62) init_deferred_pred_opt ::= */
+ 228, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */
+ 228, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
+ 203, /* (65) conslist_opt ::= */
+ 230, /* (66) tconscomma ::= COMMA */
+ 231, /* (67) tcons ::= CONSTRAINT nm */
+ 231, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
+ 231, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */
+ 231, /* (70) tcons ::= CHECK LP expr RP onconf */
+ 231, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
+ 234, /* (72) defer_subclause_opt ::= */
+ 219, /* (73) onconf ::= */
+ 219, /* (74) onconf ::= ON CONFLICT resolvetype */
+ 235, /* (75) orconf ::= */
+ 235, /* (76) orconf ::= OR resolvetype */
+ 236, /* (77) resolvetype ::= IGNORE */
+ 236, /* (78) resolvetype ::= REPLACE */
+ 191, /* (79) cmd ::= DROP TABLE ifexists fullname */
+ 238, /* (80) ifexists ::= IF EXISTS */
+ 238, /* (81) ifexists ::= */
+ 191, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
+ 191, /* (83) cmd ::= DROP VIEW ifexists fullname */
+ 191, /* (84) cmd ::= select */
+ 205, /* (85) select ::= WITH wqlist selectnowith */
+ 205, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */
+ 205, /* (87) select ::= selectnowith */
+ 240, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */
+ 243, /* (89) multiselect_op ::= UNION */
+ 243, /* (90) multiselect_op ::= UNION ALL */
+ 243, /* (91) multiselect_op ::= EXCEPT|INTERSECT */
+ 241, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
+ 241, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
+ 253, /* (94) values ::= VALUES LP nexprlist RP */
+ 241, /* (95) oneselect ::= mvalues */
+ 255, /* (96) mvalues ::= values COMMA LP nexprlist RP */
+ 255, /* (97) mvalues ::= mvalues COMMA LP nexprlist RP */
+ 244, /* (98) distinct ::= DISTINCT */
+ 244, /* (99) distinct ::= ALL */
+ 244, /* (100) distinct ::= */
+ 256, /* (101) sclp ::= */
+ 245, /* (102) selcollist ::= sclp scanpt expr scanpt as */
+ 245, /* (103) selcollist ::= sclp scanpt STAR */
+ 245, /* (104) selcollist ::= sclp scanpt nm DOT STAR */
+ 257, /* (105) as ::= AS nm */
+ 257, /* (106) as ::= */
+ 246, /* (107) from ::= */
+ 246, /* (108) from ::= FROM seltablist */
+ 259, /* (109) stl_prefix ::= seltablist joinop */
+ 259, /* (110) stl_prefix ::= */
+ 258, /* (111) seltablist ::= stl_prefix nm dbnm as on_using */
+ 258, /* (112) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */
+ 258, /* (113) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */
+ 258, /* (114) seltablist ::= stl_prefix LP select RP as on_using */
+ 258, /* (115) seltablist ::= stl_prefix LP seltablist RP as on_using */
+ 201, /* (116) dbnm ::= */
+ 201, /* (117) dbnm ::= DOT nm */
+ 239, /* (118) fullname ::= nm */
+ 239, /* (119) fullname ::= nm DOT nm */
+ 264, /* (120) xfullname ::= nm */
+ 264, /* (121) xfullname ::= nm DOT nm */
+ 264, /* (122) xfullname ::= nm DOT nm AS nm */
+ 264, /* (123) xfullname ::= nm AS nm */
+ 260, /* (124) joinop ::= COMMA|JOIN */
+ 260, /* (125) joinop ::= JOIN_KW JOIN */
+ 260, /* (126) joinop ::= JOIN_KW nm JOIN */
+ 260, /* (127) joinop ::= JOIN_KW nm nm JOIN */
+ 261, /* (128) on_using ::= ON expr */
+ 261, /* (129) on_using ::= USING LP idlist RP */
+ 261, /* (130) on_using ::= */
+ 266, /* (131) indexed_opt ::= */
+ 262, /* (132) indexed_by ::= INDEXED BY nm */
+ 262, /* (133) indexed_by ::= NOT INDEXED */
+ 250, /* (134) orderby_opt ::= */
+ 250, /* (135) orderby_opt ::= ORDER BY sortlist */
+ 232, /* (136) sortlist ::= sortlist COMMA expr sortorder nulls */
+ 232, /* (137) sortlist ::= expr sortorder nulls */
+ 220, /* (138) sortorder ::= ASC */
+ 220, /* (139) sortorder ::= DESC */
+ 220, /* (140) sortorder ::= */
+ 267, /* (141) nulls ::= NULLS FIRST */
+ 267, /* (142) nulls ::= NULLS LAST */
+ 267, /* (143) nulls ::= */
+ 248, /* (144) groupby_opt ::= */
+ 248, /* (145) groupby_opt ::= GROUP BY nexprlist */
+ 249, /* (146) having_opt ::= */
+ 249, /* (147) having_opt ::= HAVING expr */
+ 251, /* (148) limit_opt ::= */
+ 251, /* (149) limit_opt ::= LIMIT expr */
+ 251, /* (150) limit_opt ::= LIMIT expr OFFSET expr */
+ 251, /* (151) limit_opt ::= LIMIT expr COMMA expr */
+ 191, /* (152) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
+ 247, /* (153) where_opt ::= */
+ 247, /* (154) where_opt ::= WHERE expr */
+ 269, /* (155) where_opt_ret ::= */
+ 269, /* (156) where_opt_ret ::= WHERE expr */
+ 269, /* (157) where_opt_ret ::= RETURNING selcollist */
+ 269, /* (158) where_opt_ret ::= WHERE expr RETURNING selcollist */
+ 191, /* (159) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
+ 270, /* (160) setlist ::= setlist COMMA nm EQ expr */
+ 270, /* (161) setlist ::= setlist COMMA LP idlist RP EQ expr */
+ 270, /* (162) setlist ::= nm EQ expr */
+ 270, /* (163) setlist ::= LP idlist RP EQ expr */
+ 191, /* (164) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
+ 191, /* (165) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
+ 273, /* (166) upsert ::= */
+ 273, /* (167) upsert ::= RETURNING selcollist */
+ 273, /* (168) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
+ 273, /* (169) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
+ 273, /* (170) upsert ::= ON CONFLICT DO NOTHING returning */
+ 273, /* (171) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
+ 274, /* (172) returning ::= RETURNING selcollist */
+ 271, /* (173) insert_cmd ::= INSERT orconf */
+ 271, /* (174) insert_cmd ::= REPLACE */
+ 272, /* (175) idlist_opt ::= */
+ 272, /* (176) idlist_opt ::= LP idlist RP */
+ 265, /* (177) idlist ::= idlist COMMA nm */
+ 265, /* (178) idlist ::= nm */
+ 218, /* (179) expr ::= LP expr RP */
+ 218, /* (180) expr ::= ID|INDEXED|JOIN_KW */
+ 218, /* (181) expr ::= nm DOT nm */
+ 218, /* (182) expr ::= nm DOT nm DOT nm */
+ 217, /* (183) term ::= NULL|FLOAT|BLOB */
+ 217, /* (184) term ::= STRING */
+ 217, /* (185) term ::= INTEGER */
+ 218, /* (186) expr ::= VARIABLE */
+ 218, /* (187) expr ::= expr COLLATE ID|STRING */
+ 218, /* (188) expr ::= CAST LP expr AS typetoken RP */
+ 218, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
+ 218, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */
+ 218, /* (191) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
+ 218, /* (192) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
+ 218, /* (193) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */
+ 218, /* (194) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
+ 217, /* (195) term ::= CTIME_KW */
+ 218, /* (196) expr ::= LP nexprlist COMMA expr RP */
+ 218, /* (197) expr ::= expr AND expr */
+ 218, /* (198) expr ::= expr OR expr */
+ 218, /* (199) expr ::= expr LT|GT|GE|LE expr */
+ 218, /* (200) expr ::= expr EQ|NE expr */
+ 218, /* (201) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
+ 218, /* (202) expr ::= expr PLUS|MINUS expr */
+ 218, /* (203) expr ::= expr STAR|SLASH|REM expr */
+ 218, /* (204) expr ::= expr CONCAT expr */
+ 276, /* (205) likeop ::= NOT LIKE_KW|MATCH */
+ 218, /* (206) expr ::= expr likeop expr */
+ 218, /* (207) expr ::= expr likeop expr ESCAPE expr */
+ 218, /* (208) expr ::= expr ISNULL|NOTNULL */
+ 218, /* (209) expr ::= expr NOT NULL */
+ 218, /* (210) expr ::= expr IS expr */
+ 218, /* (211) expr ::= expr IS NOT expr */
+ 218, /* (212) expr ::= expr IS NOT DISTINCT FROM expr */
+ 218, /* (213) expr ::= expr IS DISTINCT FROM expr */
+ 218, /* (214) expr ::= NOT expr */
+ 218, /* (215) expr ::= BITNOT expr */
+ 218, /* (216) expr ::= PLUS|MINUS expr */
+ 218, /* (217) expr ::= expr PTR expr */
+ 277, /* (218) between_op ::= BETWEEN */
+ 277, /* (219) between_op ::= NOT BETWEEN */
+ 218, /* (220) expr ::= expr between_op expr AND expr */
+ 278, /* (221) in_op ::= IN */
+ 278, /* (222) in_op ::= NOT IN */
+ 218, /* (223) expr ::= expr in_op LP exprlist RP */
+ 218, /* (224) expr ::= LP select RP */
+ 218, /* (225) expr ::= expr in_op LP select RP */
+ 218, /* (226) expr ::= expr in_op nm dbnm paren_exprlist */
+ 218, /* (227) expr ::= EXISTS LP select RP */
+ 218, /* (228) expr ::= CASE case_operand case_exprlist case_else END */
+ 281, /* (229) case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ 281, /* (230) case_exprlist ::= WHEN expr THEN expr */
+ 282, /* (231) case_else ::= ELSE expr */
+ 282, /* (232) case_else ::= */
+ 280, /* (233) case_operand ::= */
+ 263, /* (234) exprlist ::= */
+ 254, /* (235) nexprlist ::= nexprlist COMMA expr */
+ 254, /* (236) nexprlist ::= expr */
+ 279, /* (237) paren_exprlist ::= */
+ 279, /* (238) paren_exprlist ::= LP exprlist RP */
+ 191, /* (239) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ 283, /* (240) uniqueflag ::= UNIQUE */
+ 283, /* (241) uniqueflag ::= */
+ 222, /* (242) eidlist_opt ::= */
+ 222, /* (243) eidlist_opt ::= LP eidlist RP */
+ 233, /* (244) eidlist ::= eidlist COMMA nm collate sortorder */
+ 233, /* (245) eidlist ::= nm collate sortorder */
+ 284, /* (246) collate ::= */
+ 284, /* (247) collate ::= COLLATE ID|STRING */
+ 191, /* (248) cmd ::= DROP INDEX ifexists fullname */
+ 191, /* (249) cmd ::= VACUUM vinto */
+ 191, /* (250) cmd ::= VACUUM nm vinto */
+ 285, /* (251) vinto ::= INTO expr */
+ 285, /* (252) vinto ::= */
+ 191, /* (253) cmd ::= PRAGMA nm dbnm */
+ 191, /* (254) cmd ::= PRAGMA nm dbnm EQ nmnum */
+ 191, /* (255) cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ 191, /* (256) cmd ::= PRAGMA nm dbnm EQ minus_num */
+ 191, /* (257) cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ 212, /* (258) plus_num ::= PLUS INTEGER|FLOAT */
+ 213, /* (259) minus_num ::= MINUS INTEGER|FLOAT */
+ 191, /* (260) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ 287, /* (261) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ 289, /* (262) trigger_time ::= BEFORE|AFTER */
+ 289, /* (263) trigger_time ::= INSTEAD OF */
+ 289, /* (264) trigger_time ::= */
+ 290, /* (265) trigger_event ::= DELETE|INSERT */
+ 290, /* (266) trigger_event ::= UPDATE */
+ 290, /* (267) trigger_event ::= UPDATE OF idlist */
+ 292, /* (268) when_clause ::= */
+ 292, /* (269) when_clause ::= WHEN expr */
+ 288, /* (270) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ 288, /* (271) trigger_cmd_list ::= trigger_cmd SEMI */
+ 294, /* (272) trnm ::= nm DOT nm */
+ 295, /* (273) tridxby ::= INDEXED BY nm */
+ 295, /* (274) tridxby ::= NOT INDEXED */
+ 293, /* (275) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
+ 293, /* (276) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+ 293, /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
+ 293, /* (278) trigger_cmd ::= scanpt select scanpt */
+ 218, /* (279) expr ::= RAISE LP IGNORE RP */
+ 218, /* (280) expr ::= RAISE LP raisetype COMMA nm RP */
+ 237, /* (281) raisetype ::= ROLLBACK */
+ 237, /* (282) raisetype ::= ABORT */
+ 237, /* (283) raisetype ::= FAIL */
+ 191, /* (284) cmd ::= DROP TRIGGER ifexists fullname */
+ 191, /* (285) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ 191, /* (286) cmd ::= DETACH database_kw_opt expr */
+ 297, /* (287) key_opt ::= */
+ 297, /* (288) key_opt ::= KEY expr */
+ 191, /* (289) cmd ::= REINDEX */
+ 191, /* (290) cmd ::= REINDEX nm dbnm */
+ 191, /* (291) cmd ::= ANALYZE */
+ 191, /* (292) cmd ::= ANALYZE nm dbnm */
+ 191, /* (293) cmd ::= ALTER TABLE fullname RENAME TO nm */
+ 191, /* (294) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ 191, /* (295) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
+ 298, /* (296) add_column_fullname ::= fullname */
+ 191, /* (297) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+ 191, /* (298) cmd ::= create_vtab */
+ 191, /* (299) cmd ::= create_vtab LP vtabarglist RP */
+ 300, /* (300) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ 302, /* (301) vtabarg ::= */
+ 303, /* (302) vtabargtoken ::= ANY */
+ 303, /* (303) vtabargtoken ::= lp anylist RP */
+ 304, /* (304) lp ::= LP */
+ 268, /* (305) with ::= WITH wqlist */
+ 268, /* (306) with ::= WITH RECURSIVE wqlist */
+ 307, /* (307) wqas ::= AS */
+ 307, /* (308) wqas ::= AS MATERIALIZED */
+ 307, /* (309) wqas ::= AS NOT MATERIALIZED */
+ 306, /* (310) wqitem ::= withnm eidlist_opt wqas LP select RP */
+ 308, /* (311) withnm ::= nm */
+ 242, /* (312) wqlist ::= wqitem */
+ 242, /* (313) wqlist ::= wqlist COMMA wqitem */
+ 309, /* (314) windowdefn_list ::= windowdefn_list COMMA windowdefn */
+ 310, /* (315) windowdefn ::= nm AS LP window RP */
+ 311, /* (316) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+ 311, /* (317) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+ 311, /* (318) window ::= ORDER BY sortlist frame_opt */
+ 311, /* (319) window ::= nm ORDER BY sortlist frame_opt */
+ 311, /* (320) window ::= nm frame_opt */
+ 312, /* (321) frame_opt ::= */
+ 312, /* (322) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+ 312, /* (323) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+ 316, /* (324) range_or_rows ::= RANGE|ROWS|GROUPS */
+ 318, /* (325) frame_bound_s ::= frame_bound */
+ 318, /* (326) frame_bound_s ::= UNBOUNDED PRECEDING */
+ 319, /* (327) frame_bound_e ::= frame_bound */
+ 319, /* (328) frame_bound_e ::= UNBOUNDED FOLLOWING */
+ 317, /* (329) frame_bound ::= expr PRECEDING|FOLLOWING */
+ 317, /* (330) frame_bound ::= CURRENT ROW */
+ 320, /* (331) frame_exclude_opt ::= */
+ 320, /* (332) frame_exclude_opt ::= EXCLUDE frame_exclude */
+ 321, /* (333) frame_exclude ::= NO OTHERS */
+ 321, /* (334) frame_exclude ::= CURRENT ROW */
+ 321, /* (335) frame_exclude ::= GROUP|TIES */
+ 252, /* (336) window_clause ::= WINDOW windowdefn_list */
+ 275, /* (337) filter_over ::= filter_clause over_clause */
+ 275, /* (338) filter_over ::= over_clause */
+ 275, /* (339) filter_over ::= filter_clause */
+ 315, /* (340) over_clause ::= OVER LP window RP */
+ 315, /* (341) over_clause ::= OVER nm */
+ 314, /* (342) filter_clause ::= FILTER LP WHERE expr RP */
+ 217, /* (343) term ::= QNUMBER */
+ 186, /* (344) input ::= cmdlist */
+ 187, /* (345) cmdlist ::= cmdlist ecmd */
+ 187, /* (346) cmdlist ::= ecmd */
+ 188, /* (347) ecmd ::= SEMI */
+ 188, /* (348) ecmd ::= cmdx SEMI */
+ 188, /* (349) ecmd ::= explain cmdx SEMI */
+ 193, /* (350) trans_opt ::= */
+ 193, /* (351) trans_opt ::= TRANSACTION */
+ 193, /* (352) trans_opt ::= TRANSACTION nm */
+ 195, /* (353) savepoint_opt ::= SAVEPOINT */
+ 195, /* (354) savepoint_opt ::= */
+ 191, /* (355) cmd ::= create_table create_table_args */
+ 204, /* (356) table_option_set ::= table_option */
+ 202, /* (357) columnlist ::= columnlist COMMA columnname carglist */
+ 202, /* (358) columnlist ::= columnname carglist */
+ 194, /* (359) nm ::= ID|INDEXED|JOIN_KW */
+ 194, /* (360) nm ::= STRING */
+ 209, /* (361) typetoken ::= typename */
+ 210, /* (362) typename ::= ID|STRING */
+ 211, /* (363) signed ::= plus_num */
+ 211, /* (364) signed ::= minus_num */
+ 208, /* (365) carglist ::= carglist ccons */
+ 208, /* (366) carglist ::= */
+ 216, /* (367) ccons ::= NULL onconf */
+ 216, /* (368) ccons ::= GENERATED ALWAYS AS generated */
+ 216, /* (369) ccons ::= AS generated */
+ 203, /* (370) conslist_opt ::= COMMA conslist */
+ 229, /* (371) conslist ::= conslist tconscomma tcons */
+ 229, /* (372) conslist ::= tcons */
+ 230, /* (373) tconscomma ::= */
+ 234, /* (374) defer_subclause_opt ::= defer_subclause */
+ 236, /* (375) resolvetype ::= raisetype */
+ 240, /* (376) selectnowith ::= oneselect */
+ 241, /* (377) oneselect ::= values */
+ 256, /* (378) sclp ::= selcollist COMMA */
+ 257, /* (379) as ::= ID|STRING */
+ 266, /* (380) indexed_opt ::= indexed_by */
+ 274, /* (381) returning ::= */
+ 218, /* (382) expr ::= term */
+ 276, /* (383) likeop ::= LIKE_KW|MATCH */
+ 280, /* (384) case_operand ::= expr */
+ 263, /* (385) exprlist ::= nexprlist */
+ 286, /* (386) nmnum ::= plus_num */
+ 286, /* (387) nmnum ::= nm */
+ 286, /* (388) nmnum ::= ON */
+ 286, /* (389) nmnum ::= DELETE */
+ 286, /* (390) nmnum ::= DEFAULT */
+ 212, /* (391) plus_num ::= INTEGER|FLOAT */
+ 291, /* (392) foreach_clause ::= */
+ 291, /* (393) foreach_clause ::= FOR EACH ROW */
+ 294, /* (394) trnm ::= nm */
+ 295, /* (395) tridxby ::= */
+ 296, /* (396) database_kw_opt ::= DATABASE */
+ 296, /* (397) database_kw_opt ::= */
+ 299, /* (398) kwcolumn_opt ::= */
+ 299, /* (399) kwcolumn_opt ::= COLUMNKW */
+ 301, /* (400) vtabarglist ::= vtabarg */
+ 301, /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */
+ 302, /* (402) vtabarg ::= vtabarg vtabargtoken */
+ 305, /* (403) anylist ::= */
+ 305, /* (404) anylist ::= anylist LP anylist RP */
+ 305, /* (405) anylist ::= anylist ANY */
+ 268, /* (406) with ::= */
+ 309, /* (407) windowdefn_list ::= windowdefn */
+ 311, /* (408) window ::= frame_opt */
};
/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
@@ -174363,316 +175761,320 @@ static const signed char yyRuleInfoNRhs[] = {
-9, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
-10, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
-4, /* (94) values ::= VALUES LP nexprlist RP */
- -5, /* (95) values ::= values COMMA LP nexprlist RP */
- -1, /* (96) distinct ::= DISTINCT */
- -1, /* (97) distinct ::= ALL */
- 0, /* (98) distinct ::= */
- 0, /* (99) sclp ::= */
- -5, /* (100) selcollist ::= sclp scanpt expr scanpt as */
- -3, /* (101) selcollist ::= sclp scanpt STAR */
- -5, /* (102) selcollist ::= sclp scanpt nm DOT STAR */
- -2, /* (103) as ::= AS nm */
- 0, /* (104) as ::= */
- 0, /* (105) from ::= */
- -2, /* (106) from ::= FROM seltablist */
- -2, /* (107) stl_prefix ::= seltablist joinop */
- 0, /* (108) stl_prefix ::= */
- -5, /* (109) seltablist ::= stl_prefix nm dbnm as on_using */
- -6, /* (110) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */
- -8, /* (111) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */
- -6, /* (112) seltablist ::= stl_prefix LP select RP as on_using */
- -6, /* (113) seltablist ::= stl_prefix LP seltablist RP as on_using */
- 0, /* (114) dbnm ::= */
- -2, /* (115) dbnm ::= DOT nm */
- -1, /* (116) fullname ::= nm */
- -3, /* (117) fullname ::= nm DOT nm */
- -1, /* (118) xfullname ::= nm */
- -3, /* (119) xfullname ::= nm DOT nm */
- -5, /* (120) xfullname ::= nm DOT nm AS nm */
- -3, /* (121) xfullname ::= nm AS nm */
- -1, /* (122) joinop ::= COMMA|JOIN */
- -2, /* (123) joinop ::= JOIN_KW JOIN */
- -3, /* (124) joinop ::= JOIN_KW nm JOIN */
- -4, /* (125) joinop ::= JOIN_KW nm nm JOIN */
- -2, /* (126) on_using ::= ON expr */
- -4, /* (127) on_using ::= USING LP idlist RP */
- 0, /* (128) on_using ::= */
- 0, /* (129) indexed_opt ::= */
- -3, /* (130) indexed_by ::= INDEXED BY nm */
- -2, /* (131) indexed_by ::= NOT INDEXED */
- 0, /* (132) orderby_opt ::= */
- -3, /* (133) orderby_opt ::= ORDER BY sortlist */
- -5, /* (134) sortlist ::= sortlist COMMA expr sortorder nulls */
- -3, /* (135) sortlist ::= expr sortorder nulls */
- -1, /* (136) sortorder ::= ASC */
- -1, /* (137) sortorder ::= DESC */
- 0, /* (138) sortorder ::= */
- -2, /* (139) nulls ::= NULLS FIRST */
- -2, /* (140) nulls ::= NULLS LAST */
- 0, /* (141) nulls ::= */
- 0, /* (142) groupby_opt ::= */
- -3, /* (143) groupby_opt ::= GROUP BY nexprlist */
- 0, /* (144) having_opt ::= */
- -2, /* (145) having_opt ::= HAVING expr */
- 0, /* (146) limit_opt ::= */
- -2, /* (147) limit_opt ::= LIMIT expr */
- -4, /* (148) limit_opt ::= LIMIT expr OFFSET expr */
- -4, /* (149) limit_opt ::= LIMIT expr COMMA expr */
- -6, /* (150) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
- 0, /* (151) where_opt ::= */
- -2, /* (152) where_opt ::= WHERE expr */
- 0, /* (153) where_opt_ret ::= */
- -2, /* (154) where_opt_ret ::= WHERE expr */
- -2, /* (155) where_opt_ret ::= RETURNING selcollist */
- -4, /* (156) where_opt_ret ::= WHERE expr RETURNING selcollist */
- -9, /* (157) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
- -5, /* (158) setlist ::= setlist COMMA nm EQ expr */
- -7, /* (159) setlist ::= setlist COMMA LP idlist RP EQ expr */
- -3, /* (160) setlist ::= nm EQ expr */
- -5, /* (161) setlist ::= LP idlist RP EQ expr */
- -7, /* (162) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
- -8, /* (163) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
- 0, /* (164) upsert ::= */
- -2, /* (165) upsert ::= RETURNING selcollist */
- -12, /* (166) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
- -9, /* (167) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
- -5, /* (168) upsert ::= ON CONFLICT DO NOTHING returning */
- -8, /* (169) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
- -2, /* (170) returning ::= RETURNING selcollist */
- -2, /* (171) insert_cmd ::= INSERT orconf */
- -1, /* (172) insert_cmd ::= REPLACE */
- 0, /* (173) idlist_opt ::= */
- -3, /* (174) idlist_opt ::= LP idlist RP */
- -3, /* (175) idlist ::= idlist COMMA nm */
- -1, /* (176) idlist ::= nm */
- -3, /* (177) expr ::= LP expr RP */
- -1, /* (178) expr ::= ID|INDEXED|JOIN_KW */
- -3, /* (179) expr ::= nm DOT nm */
- -5, /* (180) expr ::= nm DOT nm DOT nm */
- -1, /* (181) term ::= NULL|FLOAT|BLOB */
- -1, /* (182) term ::= STRING */
- -1, /* (183) term ::= INTEGER */
- -1, /* (184) expr ::= VARIABLE */
- -3, /* (185) expr ::= expr COLLATE ID|STRING */
- -6, /* (186) expr ::= CAST LP expr AS typetoken RP */
- -5, /* (187) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
- -8, /* (188) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */
- -4, /* (189) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
- -6, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
- -9, /* (191) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */
- -5, /* (192) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
- -1, /* (193) term ::= CTIME_KW */
- -5, /* (194) expr ::= LP nexprlist COMMA expr RP */
- -3, /* (195) expr ::= expr AND expr */
- -3, /* (196) expr ::= expr OR expr */
- -3, /* (197) expr ::= expr LT|GT|GE|LE expr */
- -3, /* (198) expr ::= expr EQ|NE expr */
- -3, /* (199) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
- -3, /* (200) expr ::= expr PLUS|MINUS expr */
- -3, /* (201) expr ::= expr STAR|SLASH|REM expr */
- -3, /* (202) expr ::= expr CONCAT expr */
- -2, /* (203) likeop ::= NOT LIKE_KW|MATCH */
- -3, /* (204) expr ::= expr likeop expr */
- -5, /* (205) expr ::= expr likeop expr ESCAPE expr */
- -2, /* (206) expr ::= expr ISNULL|NOTNULL */
- -3, /* (207) expr ::= expr NOT NULL */
- -3, /* (208) expr ::= expr IS expr */
- -4, /* (209) expr ::= expr IS NOT expr */
- -6, /* (210) expr ::= expr IS NOT DISTINCT FROM expr */
- -5, /* (211) expr ::= expr IS DISTINCT FROM expr */
- -2, /* (212) expr ::= NOT expr */
- -2, /* (213) expr ::= BITNOT expr */
- -2, /* (214) expr ::= PLUS|MINUS expr */
- -3, /* (215) expr ::= expr PTR expr */
- -1, /* (216) between_op ::= BETWEEN */
- -2, /* (217) between_op ::= NOT BETWEEN */
- -5, /* (218) expr ::= expr between_op expr AND expr */
- -1, /* (219) in_op ::= IN */
- -2, /* (220) in_op ::= NOT IN */
- -5, /* (221) expr ::= expr in_op LP exprlist RP */
- -3, /* (222) expr ::= LP select RP */
- -5, /* (223) expr ::= expr in_op LP select RP */
- -5, /* (224) expr ::= expr in_op nm dbnm paren_exprlist */
- -4, /* (225) expr ::= EXISTS LP select RP */
- -5, /* (226) expr ::= CASE case_operand case_exprlist case_else END */
- -5, /* (227) case_exprlist ::= case_exprlist WHEN expr THEN expr */
- -4, /* (228) case_exprlist ::= WHEN expr THEN expr */
- -2, /* (229) case_else ::= ELSE expr */
- 0, /* (230) case_else ::= */
- 0, /* (231) case_operand ::= */
- 0, /* (232) exprlist ::= */
- -3, /* (233) nexprlist ::= nexprlist COMMA expr */
- -1, /* (234) nexprlist ::= expr */
- 0, /* (235) paren_exprlist ::= */
- -3, /* (236) paren_exprlist ::= LP exprlist RP */
- -12, /* (237) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
- -1, /* (238) uniqueflag ::= UNIQUE */
- 0, /* (239) uniqueflag ::= */
- 0, /* (240) eidlist_opt ::= */
- -3, /* (241) eidlist_opt ::= LP eidlist RP */
- -5, /* (242) eidlist ::= eidlist COMMA nm collate sortorder */
- -3, /* (243) eidlist ::= nm collate sortorder */
- 0, /* (244) collate ::= */
- -2, /* (245) collate ::= COLLATE ID|STRING */
- -4, /* (246) cmd ::= DROP INDEX ifexists fullname */
- -2, /* (247) cmd ::= VACUUM vinto */
- -3, /* (248) cmd ::= VACUUM nm vinto */
- -2, /* (249) vinto ::= INTO expr */
- 0, /* (250) vinto ::= */
- -3, /* (251) cmd ::= PRAGMA nm dbnm */
- -5, /* (252) cmd ::= PRAGMA nm dbnm EQ nmnum */
- -6, /* (253) cmd ::= PRAGMA nm dbnm LP nmnum RP */
- -5, /* (254) cmd ::= PRAGMA nm dbnm EQ minus_num */
- -6, /* (255) cmd ::= PRAGMA nm dbnm LP minus_num RP */
- -2, /* (256) plus_num ::= PLUS INTEGER|FLOAT */
- -2, /* (257) minus_num ::= MINUS INTEGER|FLOAT */
- -5, /* (258) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
- -11, /* (259) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
- -1, /* (260) trigger_time ::= BEFORE|AFTER */
- -2, /* (261) trigger_time ::= INSTEAD OF */
- 0, /* (262) trigger_time ::= */
- -1, /* (263) trigger_event ::= DELETE|INSERT */
- -1, /* (264) trigger_event ::= UPDATE */
- -3, /* (265) trigger_event ::= UPDATE OF idlist */
- 0, /* (266) when_clause ::= */
- -2, /* (267) when_clause ::= WHEN expr */
- -3, /* (268) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
- -2, /* (269) trigger_cmd_list ::= trigger_cmd SEMI */
- -3, /* (270) trnm ::= nm DOT nm */
- -3, /* (271) tridxby ::= INDEXED BY nm */
- -2, /* (272) tridxby ::= NOT INDEXED */
- -9, /* (273) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
- -8, /* (274) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
- -6, /* (275) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
- -3, /* (276) trigger_cmd ::= scanpt select scanpt */
- -4, /* (277) expr ::= RAISE LP IGNORE RP */
- -6, /* (278) expr ::= RAISE LP raisetype COMMA nm RP */
- -1, /* (279) raisetype ::= ROLLBACK */
- -1, /* (280) raisetype ::= ABORT */
- -1, /* (281) raisetype ::= FAIL */
- -4, /* (282) cmd ::= DROP TRIGGER ifexists fullname */
- -6, /* (283) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
- -3, /* (284) cmd ::= DETACH database_kw_opt expr */
- 0, /* (285) key_opt ::= */
- -2, /* (286) key_opt ::= KEY expr */
- -1, /* (287) cmd ::= REINDEX */
- -3, /* (288) cmd ::= REINDEX nm dbnm */
- -1, /* (289) cmd ::= ANALYZE */
- -3, /* (290) cmd ::= ANALYZE nm dbnm */
- -6, /* (291) cmd ::= ALTER TABLE fullname RENAME TO nm */
- -7, /* (292) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
- -6, /* (293) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
- -1, /* (294) add_column_fullname ::= fullname */
- -8, /* (295) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
- -1, /* (296) cmd ::= create_vtab */
- -4, /* (297) cmd ::= create_vtab LP vtabarglist RP */
- -8, /* (298) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
- 0, /* (299) vtabarg ::= */
- -1, /* (300) vtabargtoken ::= ANY */
- -3, /* (301) vtabargtoken ::= lp anylist RP */
- -1, /* (302) lp ::= LP */
- -2, /* (303) with ::= WITH wqlist */
- -3, /* (304) with ::= WITH RECURSIVE wqlist */
- -1, /* (305) wqas ::= AS */
- -2, /* (306) wqas ::= AS MATERIALIZED */
- -3, /* (307) wqas ::= AS NOT MATERIALIZED */
- -6, /* (308) wqitem ::= nm eidlist_opt wqas LP select RP */
- -1, /* (309) wqlist ::= wqitem */
- -3, /* (310) wqlist ::= wqlist COMMA wqitem */
- -3, /* (311) windowdefn_list ::= windowdefn_list COMMA windowdefn */
- -5, /* (312) windowdefn ::= nm AS LP window RP */
- -5, /* (313) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
- -6, /* (314) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
- -4, /* (315) window ::= ORDER BY sortlist frame_opt */
- -5, /* (316) window ::= nm ORDER BY sortlist frame_opt */
- -2, /* (317) window ::= nm frame_opt */
- 0, /* (318) frame_opt ::= */
- -3, /* (319) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
- -6, /* (320) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
- -1, /* (321) range_or_rows ::= RANGE|ROWS|GROUPS */
- -1, /* (322) frame_bound_s ::= frame_bound */
- -2, /* (323) frame_bound_s ::= UNBOUNDED PRECEDING */
- -1, /* (324) frame_bound_e ::= frame_bound */
- -2, /* (325) frame_bound_e ::= UNBOUNDED FOLLOWING */
- -2, /* (326) frame_bound ::= expr PRECEDING|FOLLOWING */
- -2, /* (327) frame_bound ::= CURRENT ROW */
- 0, /* (328) frame_exclude_opt ::= */
- -2, /* (329) frame_exclude_opt ::= EXCLUDE frame_exclude */
- -2, /* (330) frame_exclude ::= NO OTHERS */
- -2, /* (331) frame_exclude ::= CURRENT ROW */
- -1, /* (332) frame_exclude ::= GROUP|TIES */
- -2, /* (333) window_clause ::= WINDOW windowdefn_list */
- -2, /* (334) filter_over ::= filter_clause over_clause */
- -1, /* (335) filter_over ::= over_clause */
- -1, /* (336) filter_over ::= filter_clause */
- -4, /* (337) over_clause ::= OVER LP window RP */
- -2, /* (338) over_clause ::= OVER nm */
- -5, /* (339) filter_clause ::= FILTER LP WHERE expr RP */
- -1, /* (340) input ::= cmdlist */
- -2, /* (341) cmdlist ::= cmdlist ecmd */
- -1, /* (342) cmdlist ::= ecmd */
- -1, /* (343) ecmd ::= SEMI */
- -2, /* (344) ecmd ::= cmdx SEMI */
- -3, /* (345) ecmd ::= explain cmdx SEMI */
- 0, /* (346) trans_opt ::= */
- -1, /* (347) trans_opt ::= TRANSACTION */
- -2, /* (348) trans_opt ::= TRANSACTION nm */
- -1, /* (349) savepoint_opt ::= SAVEPOINT */
- 0, /* (350) savepoint_opt ::= */
- -2, /* (351) cmd ::= create_table create_table_args */
- -1, /* (352) table_option_set ::= table_option */
- -4, /* (353) columnlist ::= columnlist COMMA columnname carglist */
- -2, /* (354) columnlist ::= columnname carglist */
- -1, /* (355) nm ::= ID|INDEXED|JOIN_KW */
- -1, /* (356) nm ::= STRING */
- -1, /* (357) typetoken ::= typename */
- -1, /* (358) typename ::= ID|STRING */
- -1, /* (359) signed ::= plus_num */
- -1, /* (360) signed ::= minus_num */
- -2, /* (361) carglist ::= carglist ccons */
- 0, /* (362) carglist ::= */
- -2, /* (363) ccons ::= NULL onconf */
- -4, /* (364) ccons ::= GENERATED ALWAYS AS generated */
- -2, /* (365) ccons ::= AS generated */
- -2, /* (366) conslist_opt ::= COMMA conslist */
- -3, /* (367) conslist ::= conslist tconscomma tcons */
- -1, /* (368) conslist ::= tcons */
- 0, /* (369) tconscomma ::= */
- -1, /* (370) defer_subclause_opt ::= defer_subclause */
- -1, /* (371) resolvetype ::= raisetype */
- -1, /* (372) selectnowith ::= oneselect */
- -1, /* (373) oneselect ::= values */
- -2, /* (374) sclp ::= selcollist COMMA */
- -1, /* (375) as ::= ID|STRING */
- -1, /* (376) indexed_opt ::= indexed_by */
- 0, /* (377) returning ::= */
- -1, /* (378) expr ::= term */
- -1, /* (379) likeop ::= LIKE_KW|MATCH */
- -1, /* (380) case_operand ::= expr */
- -1, /* (381) exprlist ::= nexprlist */
- -1, /* (382) nmnum ::= plus_num */
- -1, /* (383) nmnum ::= nm */
- -1, /* (384) nmnum ::= ON */
- -1, /* (385) nmnum ::= DELETE */
- -1, /* (386) nmnum ::= DEFAULT */
- -1, /* (387) plus_num ::= INTEGER|FLOAT */
- 0, /* (388) foreach_clause ::= */
- -3, /* (389) foreach_clause ::= FOR EACH ROW */
- -1, /* (390) trnm ::= nm */
- 0, /* (391) tridxby ::= */
- -1, /* (392) database_kw_opt ::= DATABASE */
- 0, /* (393) database_kw_opt ::= */
- 0, /* (394) kwcolumn_opt ::= */
- -1, /* (395) kwcolumn_opt ::= COLUMNKW */
- -1, /* (396) vtabarglist ::= vtabarg */
- -3, /* (397) vtabarglist ::= vtabarglist COMMA vtabarg */
- -2, /* (398) vtabarg ::= vtabarg vtabargtoken */
- 0, /* (399) anylist ::= */
- -4, /* (400) anylist ::= anylist LP anylist RP */
- -2, /* (401) anylist ::= anylist ANY */
- 0, /* (402) with ::= */
- -1, /* (403) windowdefn_list ::= windowdefn */
- -1, /* (404) window ::= frame_opt */
+ -1, /* (95) oneselect ::= mvalues */
+ -5, /* (96) mvalues ::= values COMMA LP nexprlist RP */
+ -5, /* (97) mvalues ::= mvalues COMMA LP nexprlist RP */
+ -1, /* (98) distinct ::= DISTINCT */
+ -1, /* (99) distinct ::= ALL */
+ 0, /* (100) distinct ::= */
+ 0, /* (101) sclp ::= */
+ -5, /* (102) selcollist ::= sclp scanpt expr scanpt as */
+ -3, /* (103) selcollist ::= sclp scanpt STAR */
+ -5, /* (104) selcollist ::= sclp scanpt nm DOT STAR */
+ -2, /* (105) as ::= AS nm */
+ 0, /* (106) as ::= */
+ 0, /* (107) from ::= */
+ -2, /* (108) from ::= FROM seltablist */
+ -2, /* (109) stl_prefix ::= seltablist joinop */
+ 0, /* (110) stl_prefix ::= */
+ -5, /* (111) seltablist ::= stl_prefix nm dbnm as on_using */
+ -6, /* (112) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */
+ -8, /* (113) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */
+ -6, /* (114) seltablist ::= stl_prefix LP select RP as on_using */
+ -6, /* (115) seltablist ::= stl_prefix LP seltablist RP as on_using */
+ 0, /* (116) dbnm ::= */
+ -2, /* (117) dbnm ::= DOT nm */
+ -1, /* (118) fullname ::= nm */
+ -3, /* (119) fullname ::= nm DOT nm */
+ -1, /* (120) xfullname ::= nm */
+ -3, /* (121) xfullname ::= nm DOT nm */
+ -5, /* (122) xfullname ::= nm DOT nm AS nm */
+ -3, /* (123) xfullname ::= nm AS nm */
+ -1, /* (124) joinop ::= COMMA|JOIN */
+ -2, /* (125) joinop ::= JOIN_KW JOIN */
+ -3, /* (126) joinop ::= JOIN_KW nm JOIN */
+ -4, /* (127) joinop ::= JOIN_KW nm nm JOIN */
+ -2, /* (128) on_using ::= ON expr */
+ -4, /* (129) on_using ::= USING LP idlist RP */
+ 0, /* (130) on_using ::= */
+ 0, /* (131) indexed_opt ::= */
+ -3, /* (132) indexed_by ::= INDEXED BY nm */
+ -2, /* (133) indexed_by ::= NOT INDEXED */
+ 0, /* (134) orderby_opt ::= */
+ -3, /* (135) orderby_opt ::= ORDER BY sortlist */
+ -5, /* (136) sortlist ::= sortlist COMMA expr sortorder nulls */
+ -3, /* (137) sortlist ::= expr sortorder nulls */
+ -1, /* (138) sortorder ::= ASC */
+ -1, /* (139) sortorder ::= DESC */
+ 0, /* (140) sortorder ::= */
+ -2, /* (141) nulls ::= NULLS FIRST */
+ -2, /* (142) nulls ::= NULLS LAST */
+ 0, /* (143) nulls ::= */
+ 0, /* (144) groupby_opt ::= */
+ -3, /* (145) groupby_opt ::= GROUP BY nexprlist */
+ 0, /* (146) having_opt ::= */
+ -2, /* (147) having_opt ::= HAVING expr */
+ 0, /* (148) limit_opt ::= */
+ -2, /* (149) limit_opt ::= LIMIT expr */
+ -4, /* (150) limit_opt ::= LIMIT expr OFFSET expr */
+ -4, /* (151) limit_opt ::= LIMIT expr COMMA expr */
+ -6, /* (152) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
+ 0, /* (153) where_opt ::= */
+ -2, /* (154) where_opt ::= WHERE expr */
+ 0, /* (155) where_opt_ret ::= */
+ -2, /* (156) where_opt_ret ::= WHERE expr */
+ -2, /* (157) where_opt_ret ::= RETURNING selcollist */
+ -4, /* (158) where_opt_ret ::= WHERE expr RETURNING selcollist */
+ -9, /* (159) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
+ -5, /* (160) setlist ::= setlist COMMA nm EQ expr */
+ -7, /* (161) setlist ::= setlist COMMA LP idlist RP EQ expr */
+ -3, /* (162) setlist ::= nm EQ expr */
+ -5, /* (163) setlist ::= LP idlist RP EQ expr */
+ -7, /* (164) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
+ -8, /* (165) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
+ 0, /* (166) upsert ::= */
+ -2, /* (167) upsert ::= RETURNING selcollist */
+ -12, /* (168) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
+ -9, /* (169) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
+ -5, /* (170) upsert ::= ON CONFLICT DO NOTHING returning */
+ -8, /* (171) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
+ -2, /* (172) returning ::= RETURNING selcollist */
+ -2, /* (173) insert_cmd ::= INSERT orconf */
+ -1, /* (174) insert_cmd ::= REPLACE */
+ 0, /* (175) idlist_opt ::= */
+ -3, /* (176) idlist_opt ::= LP idlist RP */
+ -3, /* (177) idlist ::= idlist COMMA nm */
+ -1, /* (178) idlist ::= nm */
+ -3, /* (179) expr ::= LP expr RP */
+ -1, /* (180) expr ::= ID|INDEXED|JOIN_KW */
+ -3, /* (181) expr ::= nm DOT nm */
+ -5, /* (182) expr ::= nm DOT nm DOT nm */
+ -1, /* (183) term ::= NULL|FLOAT|BLOB */
+ -1, /* (184) term ::= STRING */
+ -1, /* (185) term ::= INTEGER */
+ -1, /* (186) expr ::= VARIABLE */
+ -3, /* (187) expr ::= expr COLLATE ID|STRING */
+ -6, /* (188) expr ::= CAST LP expr AS typetoken RP */
+ -5, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
+ -8, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */
+ -4, /* (191) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
+ -6, /* (192) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
+ -9, /* (193) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */
+ -5, /* (194) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
+ -1, /* (195) term ::= CTIME_KW */
+ -5, /* (196) expr ::= LP nexprlist COMMA expr RP */
+ -3, /* (197) expr ::= expr AND expr */
+ -3, /* (198) expr ::= expr OR expr */
+ -3, /* (199) expr ::= expr LT|GT|GE|LE expr */
+ -3, /* (200) expr ::= expr EQ|NE expr */
+ -3, /* (201) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
+ -3, /* (202) expr ::= expr PLUS|MINUS expr */
+ -3, /* (203) expr ::= expr STAR|SLASH|REM expr */
+ -3, /* (204) expr ::= expr CONCAT expr */
+ -2, /* (205) likeop ::= NOT LIKE_KW|MATCH */
+ -3, /* (206) expr ::= expr likeop expr */
+ -5, /* (207) expr ::= expr likeop expr ESCAPE expr */
+ -2, /* (208) expr ::= expr ISNULL|NOTNULL */
+ -3, /* (209) expr ::= expr NOT NULL */
+ -3, /* (210) expr ::= expr IS expr */
+ -4, /* (211) expr ::= expr IS NOT expr */
+ -6, /* (212) expr ::= expr IS NOT DISTINCT FROM expr */
+ -5, /* (213) expr ::= expr IS DISTINCT FROM expr */
+ -2, /* (214) expr ::= NOT expr */
+ -2, /* (215) expr ::= BITNOT expr */
+ -2, /* (216) expr ::= PLUS|MINUS expr */
+ -3, /* (217) expr ::= expr PTR expr */
+ -1, /* (218) between_op ::= BETWEEN */
+ -2, /* (219) between_op ::= NOT BETWEEN */
+ -5, /* (220) expr ::= expr between_op expr AND expr */
+ -1, /* (221) in_op ::= IN */
+ -2, /* (222) in_op ::= NOT IN */
+ -5, /* (223) expr ::= expr in_op LP exprlist RP */
+ -3, /* (224) expr ::= LP select RP */
+ -5, /* (225) expr ::= expr in_op LP select RP */
+ -5, /* (226) expr ::= expr in_op nm dbnm paren_exprlist */
+ -4, /* (227) expr ::= EXISTS LP select RP */
+ -5, /* (228) expr ::= CASE case_operand case_exprlist case_else END */
+ -5, /* (229) case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ -4, /* (230) case_exprlist ::= WHEN expr THEN expr */
+ -2, /* (231) case_else ::= ELSE expr */
+ 0, /* (232) case_else ::= */
+ 0, /* (233) case_operand ::= */
+ 0, /* (234) exprlist ::= */
+ -3, /* (235) nexprlist ::= nexprlist COMMA expr */
+ -1, /* (236) nexprlist ::= expr */
+ 0, /* (237) paren_exprlist ::= */
+ -3, /* (238) paren_exprlist ::= LP exprlist RP */
+ -12, /* (239) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ -1, /* (240) uniqueflag ::= UNIQUE */
+ 0, /* (241) uniqueflag ::= */
+ 0, /* (242) eidlist_opt ::= */
+ -3, /* (243) eidlist_opt ::= LP eidlist RP */
+ -5, /* (244) eidlist ::= eidlist COMMA nm collate sortorder */
+ -3, /* (245) eidlist ::= nm collate sortorder */
+ 0, /* (246) collate ::= */
+ -2, /* (247) collate ::= COLLATE ID|STRING */
+ -4, /* (248) cmd ::= DROP INDEX ifexists fullname */
+ -2, /* (249) cmd ::= VACUUM vinto */
+ -3, /* (250) cmd ::= VACUUM nm vinto */
+ -2, /* (251) vinto ::= INTO expr */
+ 0, /* (252) vinto ::= */
+ -3, /* (253) cmd ::= PRAGMA nm dbnm */
+ -5, /* (254) cmd ::= PRAGMA nm dbnm EQ nmnum */
+ -6, /* (255) cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ -5, /* (256) cmd ::= PRAGMA nm dbnm EQ minus_num */
+ -6, /* (257) cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ -2, /* (258) plus_num ::= PLUS INTEGER|FLOAT */
+ -2, /* (259) minus_num ::= MINUS INTEGER|FLOAT */
+ -5, /* (260) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ -11, /* (261) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ -1, /* (262) trigger_time ::= BEFORE|AFTER */
+ -2, /* (263) trigger_time ::= INSTEAD OF */
+ 0, /* (264) trigger_time ::= */
+ -1, /* (265) trigger_event ::= DELETE|INSERT */
+ -1, /* (266) trigger_event ::= UPDATE */
+ -3, /* (267) trigger_event ::= UPDATE OF idlist */
+ 0, /* (268) when_clause ::= */
+ -2, /* (269) when_clause ::= WHEN expr */
+ -3, /* (270) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ -2, /* (271) trigger_cmd_list ::= trigger_cmd SEMI */
+ -3, /* (272) trnm ::= nm DOT nm */
+ -3, /* (273) tridxby ::= INDEXED BY nm */
+ -2, /* (274) tridxby ::= NOT INDEXED */
+ -9, /* (275) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
+ -8, /* (276) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+ -6, /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
+ -3, /* (278) trigger_cmd ::= scanpt select scanpt */
+ -4, /* (279) expr ::= RAISE LP IGNORE RP */
+ -6, /* (280) expr ::= RAISE LP raisetype COMMA nm RP */
+ -1, /* (281) raisetype ::= ROLLBACK */
+ -1, /* (282) raisetype ::= ABORT */
+ -1, /* (283) raisetype ::= FAIL */
+ -4, /* (284) cmd ::= DROP TRIGGER ifexists fullname */
+ -6, /* (285) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ -3, /* (286) cmd ::= DETACH database_kw_opt expr */
+ 0, /* (287) key_opt ::= */
+ -2, /* (288) key_opt ::= KEY expr */
+ -1, /* (289) cmd ::= REINDEX */
+ -3, /* (290) cmd ::= REINDEX nm dbnm */
+ -1, /* (291) cmd ::= ANALYZE */
+ -3, /* (292) cmd ::= ANALYZE nm dbnm */
+ -6, /* (293) cmd ::= ALTER TABLE fullname RENAME TO nm */
+ -7, /* (294) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ -6, /* (295) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
+ -1, /* (296) add_column_fullname ::= fullname */
+ -8, /* (297) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+ -1, /* (298) cmd ::= create_vtab */
+ -4, /* (299) cmd ::= create_vtab LP vtabarglist RP */
+ -8, /* (300) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ 0, /* (301) vtabarg ::= */
+ -1, /* (302) vtabargtoken ::= ANY */
+ -3, /* (303) vtabargtoken ::= lp anylist RP */
+ -1, /* (304) lp ::= LP */
+ -2, /* (305) with ::= WITH wqlist */
+ -3, /* (306) with ::= WITH RECURSIVE wqlist */
+ -1, /* (307) wqas ::= AS */
+ -2, /* (308) wqas ::= AS MATERIALIZED */
+ -3, /* (309) wqas ::= AS NOT MATERIALIZED */
+ -6, /* (310) wqitem ::= withnm eidlist_opt wqas LP select RP */
+ -1, /* (311) withnm ::= nm */
+ -1, /* (312) wqlist ::= wqitem */
+ -3, /* (313) wqlist ::= wqlist COMMA wqitem */
+ -3, /* (314) windowdefn_list ::= windowdefn_list COMMA windowdefn */
+ -5, /* (315) windowdefn ::= nm AS LP window RP */
+ -5, /* (316) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+ -6, /* (317) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+ -4, /* (318) window ::= ORDER BY sortlist frame_opt */
+ -5, /* (319) window ::= nm ORDER BY sortlist frame_opt */
+ -2, /* (320) window ::= nm frame_opt */
+ 0, /* (321) frame_opt ::= */
+ -3, /* (322) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+ -6, /* (323) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+ -1, /* (324) range_or_rows ::= RANGE|ROWS|GROUPS */
+ -1, /* (325) frame_bound_s ::= frame_bound */
+ -2, /* (326) frame_bound_s ::= UNBOUNDED PRECEDING */
+ -1, /* (327) frame_bound_e ::= frame_bound */
+ -2, /* (328) frame_bound_e ::= UNBOUNDED FOLLOWING */
+ -2, /* (329) frame_bound ::= expr PRECEDING|FOLLOWING */
+ -2, /* (330) frame_bound ::= CURRENT ROW */
+ 0, /* (331) frame_exclude_opt ::= */
+ -2, /* (332) frame_exclude_opt ::= EXCLUDE frame_exclude */
+ -2, /* (333) frame_exclude ::= NO OTHERS */
+ -2, /* (334) frame_exclude ::= CURRENT ROW */
+ -1, /* (335) frame_exclude ::= GROUP|TIES */
+ -2, /* (336) window_clause ::= WINDOW windowdefn_list */
+ -2, /* (337) filter_over ::= filter_clause over_clause */
+ -1, /* (338) filter_over ::= over_clause */
+ -1, /* (339) filter_over ::= filter_clause */
+ -4, /* (340) over_clause ::= OVER LP window RP */
+ -2, /* (341) over_clause ::= OVER nm */
+ -5, /* (342) filter_clause ::= FILTER LP WHERE expr RP */
+ -1, /* (343) term ::= QNUMBER */
+ -1, /* (344) input ::= cmdlist */
+ -2, /* (345) cmdlist ::= cmdlist ecmd */
+ -1, /* (346) cmdlist ::= ecmd */
+ -1, /* (347) ecmd ::= SEMI */
+ -2, /* (348) ecmd ::= cmdx SEMI */
+ -3, /* (349) ecmd ::= explain cmdx SEMI */
+ 0, /* (350) trans_opt ::= */
+ -1, /* (351) trans_opt ::= TRANSACTION */
+ -2, /* (352) trans_opt ::= TRANSACTION nm */
+ -1, /* (353) savepoint_opt ::= SAVEPOINT */
+ 0, /* (354) savepoint_opt ::= */
+ -2, /* (355) cmd ::= create_table create_table_args */
+ -1, /* (356) table_option_set ::= table_option */
+ -4, /* (357) columnlist ::= columnlist COMMA columnname carglist */
+ -2, /* (358) columnlist ::= columnname carglist */
+ -1, /* (359) nm ::= ID|INDEXED|JOIN_KW */
+ -1, /* (360) nm ::= STRING */
+ -1, /* (361) typetoken ::= typename */
+ -1, /* (362) typename ::= ID|STRING */
+ -1, /* (363) signed ::= plus_num */
+ -1, /* (364) signed ::= minus_num */
+ -2, /* (365) carglist ::= carglist ccons */
+ 0, /* (366) carglist ::= */
+ -2, /* (367) ccons ::= NULL onconf */
+ -4, /* (368) ccons ::= GENERATED ALWAYS AS generated */
+ -2, /* (369) ccons ::= AS generated */
+ -2, /* (370) conslist_opt ::= COMMA conslist */
+ -3, /* (371) conslist ::= conslist tconscomma tcons */
+ -1, /* (372) conslist ::= tcons */
+ 0, /* (373) tconscomma ::= */
+ -1, /* (374) defer_subclause_opt ::= defer_subclause */
+ -1, /* (375) resolvetype ::= raisetype */
+ -1, /* (376) selectnowith ::= oneselect */
+ -1, /* (377) oneselect ::= values */
+ -2, /* (378) sclp ::= selcollist COMMA */
+ -1, /* (379) as ::= ID|STRING */
+ -1, /* (380) indexed_opt ::= indexed_by */
+ 0, /* (381) returning ::= */
+ -1, /* (382) expr ::= term */
+ -1, /* (383) likeop ::= LIKE_KW|MATCH */
+ -1, /* (384) case_operand ::= expr */
+ -1, /* (385) exprlist ::= nexprlist */
+ -1, /* (386) nmnum ::= plus_num */
+ -1, /* (387) nmnum ::= nm */
+ -1, /* (388) nmnum ::= ON */
+ -1, /* (389) nmnum ::= DELETE */
+ -1, /* (390) nmnum ::= DEFAULT */
+ -1, /* (391) plus_num ::= INTEGER|FLOAT */
+ 0, /* (392) foreach_clause ::= */
+ -3, /* (393) foreach_clause ::= FOR EACH ROW */
+ -1, /* (394) trnm ::= nm */
+ 0, /* (395) tridxby ::= */
+ -1, /* (396) database_kw_opt ::= DATABASE */
+ 0, /* (397) database_kw_opt ::= */
+ 0, /* (398) kwcolumn_opt ::= */
+ -1, /* (399) kwcolumn_opt ::= COLUMNKW */
+ -1, /* (400) vtabarglist ::= vtabarg */
+ -3, /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */
+ -2, /* (402) vtabarg ::= vtabarg vtabargtoken */
+ 0, /* (403) anylist ::= */
+ -4, /* (404) anylist ::= anylist LP anylist RP */
+ -2, /* (405) anylist ::= anylist ANY */
+ 0, /* (406) with ::= */
+ -1, /* (407) windowdefn_list ::= windowdefn */
+ -1, /* (408) window ::= frame_opt */
};
static void yy_accept(yyParser*); /* Forward Declaration */
@@ -174724,16 +176126,16 @@ static YYACTIONTYPE yy_reduce(
{ sqlite3FinishCoding(pParse); }
break;
case 3: /* cmd ::= BEGIN transtype trans_opt */
-{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy394);}
+{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy144);}
break;
case 4: /* transtype ::= */
-{yymsp[1].minor.yy394 = TK_DEFERRED;}
+{yymsp[1].minor.yy144 = TK_DEFERRED;}
break;
case 5: /* transtype ::= DEFERRED */
case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6);
case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7);
- case 321: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==321);
-{yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/}
+ case 324: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==324);
+{yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-X*/}
break;
case 8: /* cmd ::= COMMIT|END trans_opt */
case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9);
@@ -174756,7 +176158,7 @@ static YYACTIONTYPE yy_reduce(
break;
case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */
{
- sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy394,0,0,yymsp[-2].minor.yy394);
+ sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy144,0,0,yymsp[-2].minor.yy144);
}
break;
case 14: /* createkw ::= CREATE */
@@ -174768,40 +176170,40 @@ static YYACTIONTYPE yy_reduce(
case 62: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==62);
case 72: /* defer_subclause_opt ::= */ yytestcase(yyruleno==72);
case 81: /* ifexists ::= */ yytestcase(yyruleno==81);
- case 98: /* distinct ::= */ yytestcase(yyruleno==98);
- case 244: /* collate ::= */ yytestcase(yyruleno==244);
-{yymsp[1].minor.yy394 = 0;}
+ case 100: /* distinct ::= */ yytestcase(yyruleno==100);
+ case 246: /* collate ::= */ yytestcase(yyruleno==246);
+{yymsp[1].minor.yy144 = 0;}
break;
case 16: /* ifnotexists ::= IF NOT EXISTS */
-{yymsp[-2].minor.yy394 = 1;}
+{yymsp[-2].minor.yy144 = 1;}
break;
case 17: /* temp ::= TEMP */
-{yymsp[0].minor.yy394 = pParse->db->init.busy==0;}
+{yymsp[0].minor.yy144 = pParse->db->init.busy==0;}
break;
case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_option_set */
{
- sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy285,0);
+ sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy391,0);
}
break;
case 20: /* create_table_args ::= AS select */
{
- sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy47);
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy47);
+ sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy555);
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555);
}
break;
case 21: /* table_option_set ::= */
-{yymsp[1].minor.yy285 = 0;}
+{yymsp[1].minor.yy391 = 0;}
break;
case 22: /* table_option_set ::= table_option_set COMMA table_option */
-{yylhsminor.yy285 = yymsp[-2].minor.yy285|yymsp[0].minor.yy285;}
- yymsp[-2].minor.yy285 = yylhsminor.yy285;
+{yylhsminor.yy391 = yymsp[-2].minor.yy391|yymsp[0].minor.yy391;}
+ yymsp[-2].minor.yy391 = yylhsminor.yy391;
break;
case 23: /* table_option ::= WITHOUT nm */
{
if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){
- yymsp[-1].minor.yy285 = TF_WithoutRowid | TF_NoVisibleRowid;
+ yymsp[-1].minor.yy391 = TF_WithoutRowid | TF_NoVisibleRowid;
}else{
- yymsp[-1].minor.yy285 = 0;
+ yymsp[-1].minor.yy391 = 0;
sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z);
}
}
@@ -174809,20 +176211,20 @@ static YYACTIONTYPE yy_reduce(
case 24: /* table_option ::= nm */
{
if( yymsp[0].minor.yy0.n==6 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"strict",6)==0 ){
- yylhsminor.yy285 = TF_Strict;
+ yylhsminor.yy391 = TF_Strict;
}else{
- yylhsminor.yy285 = 0;
+ yylhsminor.yy391 = 0;
sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z);
}
}
- yymsp[0].minor.yy285 = yylhsminor.yy285;
+ yymsp[0].minor.yy391 = yylhsminor.yy391;
break;
case 25: /* columnname ::= nm typetoken */
{sqlite3AddColumn(pParse,yymsp[-1].minor.yy0,yymsp[0].minor.yy0);}
break;
case 26: /* typetoken ::= */
case 65: /* conslist_opt ::= */ yytestcase(yyruleno==65);
- case 104: /* as ::= */ yytestcase(yyruleno==104);
+ case 106: /* as ::= */ yytestcase(yyruleno==106);
{yymsp[1].minor.yy0.n = 0; yymsp[1].minor.yy0.z = 0;}
break;
case 27: /* typetoken ::= typename LP signed RP */
@@ -174841,7 +176243,7 @@ static YYACTIONTYPE yy_reduce(
case 30: /* scanpt ::= */
{
assert( yyLookahead!=YYNOCODE );
- yymsp[1].minor.yy522 = yyLookaheadToken.z;
+ yymsp[1].minor.yy168 = yyLookaheadToken.z;
}
break;
case 31: /* scantok ::= */
@@ -174855,17 +176257,17 @@ static YYACTIONTYPE yy_reduce(
{pParse->constraintName = yymsp[0].minor.yy0;}
break;
case 33: /* ccons ::= DEFAULT scantok term */
-{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy528,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
+{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy454,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
break;
case 34: /* ccons ::= DEFAULT LP expr RP */
-{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy528,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);}
+{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy454,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);}
break;
case 35: /* ccons ::= DEFAULT PLUS scantok term */
-{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy528,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
+{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy454,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
break;
case 36: /* ccons ::= DEFAULT MINUS scantok term */
{
- Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy528, 0);
+ Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy454, 0);
sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);
}
break;
@@ -174880,151 +176282,151 @@ static YYACTIONTYPE yy_reduce(
}
break;
case 38: /* ccons ::= NOT NULL onconf */
-{sqlite3AddNotNull(pParse, yymsp[0].minor.yy394);}
+{sqlite3AddNotNull(pParse, yymsp[0].minor.yy144);}
break;
case 39: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */
-{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy394,yymsp[0].minor.yy394,yymsp[-2].minor.yy394);}
+{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy144,yymsp[0].minor.yy144,yymsp[-2].minor.yy144);}
break;
case 40: /* ccons ::= UNIQUE onconf */
-{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy394,0,0,0,0,
+{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy144,0,0,0,0,
SQLITE_IDXTYPE_UNIQUE);}
break;
case 41: /* ccons ::= CHECK LP expr RP */
-{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy528,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);}
+{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy454,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);}
break;
case 42: /* ccons ::= REFERENCES nm eidlist_opt refargs */
-{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy322,yymsp[0].minor.yy394);}
+{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy14,yymsp[0].minor.yy144);}
break;
case 43: /* ccons ::= defer_subclause */
-{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy394);}
+{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy144);}
break;
case 44: /* ccons ::= COLLATE ID|STRING */
{sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);}
break;
case 45: /* generated ::= LP expr RP */
-{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy528,0);}
+{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy454,0);}
break;
case 46: /* generated ::= LP expr RP ID */
-{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy528,&yymsp[0].minor.yy0);}
+{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy454,&yymsp[0].minor.yy0);}
break;
case 48: /* autoinc ::= AUTOINCR */
-{yymsp[0].minor.yy394 = 1;}
+{yymsp[0].minor.yy144 = 1;}
break;
case 49: /* refargs ::= */
-{ yymsp[1].minor.yy394 = OE_None*0x0101; /* EV: R-19803-45884 */}
+{ yymsp[1].minor.yy144 = OE_None*0x0101; /* EV: R-19803-45884 */}
break;
case 50: /* refargs ::= refargs refarg */
-{ yymsp[-1].minor.yy394 = (yymsp[-1].minor.yy394 & ~yymsp[0].minor.yy231.mask) | yymsp[0].minor.yy231.value; }
+{ yymsp[-1].minor.yy144 = (yymsp[-1].minor.yy144 & ~yymsp[0].minor.yy383.mask) | yymsp[0].minor.yy383.value; }
break;
case 51: /* refarg ::= MATCH nm */
-{ yymsp[-1].minor.yy231.value = 0; yymsp[-1].minor.yy231.mask = 0x000000; }
+{ yymsp[-1].minor.yy383.value = 0; yymsp[-1].minor.yy383.mask = 0x000000; }
break;
case 52: /* refarg ::= ON INSERT refact */
-{ yymsp[-2].minor.yy231.value = 0; yymsp[-2].minor.yy231.mask = 0x000000; }
+{ yymsp[-2].minor.yy383.value = 0; yymsp[-2].minor.yy383.mask = 0x000000; }
break;
case 53: /* refarg ::= ON DELETE refact */
-{ yymsp[-2].minor.yy231.value = yymsp[0].minor.yy394; yymsp[-2].minor.yy231.mask = 0x0000ff; }
+{ yymsp[-2].minor.yy383.value = yymsp[0].minor.yy144; yymsp[-2].minor.yy383.mask = 0x0000ff; }
break;
case 54: /* refarg ::= ON UPDATE refact */
-{ yymsp[-2].minor.yy231.value = yymsp[0].minor.yy394<<8; yymsp[-2].minor.yy231.mask = 0x00ff00; }
+{ yymsp[-2].minor.yy383.value = yymsp[0].minor.yy144<<8; yymsp[-2].minor.yy383.mask = 0x00ff00; }
break;
case 55: /* refact ::= SET NULL */
-{ yymsp[-1].minor.yy394 = OE_SetNull; /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy144 = OE_SetNull; /* EV: R-33326-45252 */}
break;
case 56: /* refact ::= SET DEFAULT */
-{ yymsp[-1].minor.yy394 = OE_SetDflt; /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy144 = OE_SetDflt; /* EV: R-33326-45252 */}
break;
case 57: /* refact ::= CASCADE */
-{ yymsp[0].minor.yy394 = OE_Cascade; /* EV: R-33326-45252 */}
+{ yymsp[0].minor.yy144 = OE_Cascade; /* EV: R-33326-45252 */}
break;
case 58: /* refact ::= RESTRICT */
-{ yymsp[0].minor.yy394 = OE_Restrict; /* EV: R-33326-45252 */}
+{ yymsp[0].minor.yy144 = OE_Restrict; /* EV: R-33326-45252 */}
break;
case 59: /* refact ::= NO ACTION */
-{ yymsp[-1].minor.yy394 = OE_None; /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy144 = OE_None; /* EV: R-33326-45252 */}
break;
case 60: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
-{yymsp[-2].minor.yy394 = 0;}
+{yymsp[-2].minor.yy144 = 0;}
break;
case 61: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
case 76: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==76);
- case 171: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==171);
-{yymsp[-1].minor.yy394 = yymsp[0].minor.yy394;}
+ case 173: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==173);
+{yymsp[-1].minor.yy144 = yymsp[0].minor.yy144;}
break;
case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80);
- case 217: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==217);
- case 220: /* in_op ::= NOT IN */ yytestcase(yyruleno==220);
- case 245: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==245);
-{yymsp[-1].minor.yy394 = 1;}
+ case 219: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==219);
+ case 222: /* in_op ::= NOT IN */ yytestcase(yyruleno==222);
+ case 247: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==247);
+{yymsp[-1].minor.yy144 = 1;}
break;
case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
-{yymsp[-1].minor.yy394 = 0;}
+{yymsp[-1].minor.yy144 = 0;}
break;
case 66: /* tconscomma ::= COMMA */
{pParse->constraintName.n = 0;}
break;
case 68: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
-{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy322,yymsp[0].minor.yy394,yymsp[-2].minor.yy394,0);}
+{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy14,yymsp[0].minor.yy144,yymsp[-2].minor.yy144,0);}
break;
case 69: /* tcons ::= UNIQUE LP sortlist RP onconf */
-{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy322,yymsp[0].minor.yy394,0,0,0,0,
+{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy14,yymsp[0].minor.yy144,0,0,0,0,
SQLITE_IDXTYPE_UNIQUE);}
break;
case 70: /* tcons ::= CHECK LP expr RP onconf */
-{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy528,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);}
+{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy454,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);}
break;
case 71: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
{
- sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy322, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy322, yymsp[-1].minor.yy394);
- sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy394);
+ sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy14, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy14, yymsp[-1].minor.yy144);
+ sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy144);
}
break;
case 73: /* onconf ::= */
case 75: /* orconf ::= */ yytestcase(yyruleno==75);
-{yymsp[1].minor.yy394 = OE_Default;}
+{yymsp[1].minor.yy144 = OE_Default;}
break;
case 74: /* onconf ::= ON CONFLICT resolvetype */
-{yymsp[-2].minor.yy394 = yymsp[0].minor.yy394;}
+{yymsp[-2].minor.yy144 = yymsp[0].minor.yy144;}
break;
case 77: /* resolvetype ::= IGNORE */
-{yymsp[0].minor.yy394 = OE_Ignore;}
+{yymsp[0].minor.yy144 = OE_Ignore;}
break;
case 78: /* resolvetype ::= REPLACE */
- case 172: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==172);
-{yymsp[0].minor.yy394 = OE_Replace;}
+ case 174: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==174);
+{yymsp[0].minor.yy144 = OE_Replace;}
break;
case 79: /* cmd ::= DROP TABLE ifexists fullname */
{
- sqlite3DropTable(pParse, yymsp[0].minor.yy131, 0, yymsp[-1].minor.yy394);
+ sqlite3DropTable(pParse, yymsp[0].minor.yy203, 0, yymsp[-1].minor.yy144);
}
break;
case 82: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
{
- sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy322, yymsp[0].minor.yy47, yymsp[-7].minor.yy394, yymsp[-5].minor.yy394);
+ sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy14, yymsp[0].minor.yy555, yymsp[-7].minor.yy144, yymsp[-5].minor.yy144);
}
break;
case 83: /* cmd ::= DROP VIEW ifexists fullname */
{
- sqlite3DropTable(pParse, yymsp[0].minor.yy131, 1, yymsp[-1].minor.yy394);
+ sqlite3DropTable(pParse, yymsp[0].minor.yy203, 1, yymsp[-1].minor.yy144);
}
break;
case 84: /* cmd ::= select */
{
SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0};
- sqlite3Select(pParse, yymsp[0].minor.yy47, &dest);
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy47);
+ sqlite3Select(pParse, yymsp[0].minor.yy555, &dest);
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555);
}
break;
case 85: /* select ::= WITH wqlist selectnowith */
-{yymsp[-2].minor.yy47 = attachWithToSelect(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy521);}
+{yymsp[-2].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);}
break;
case 86: /* select ::= WITH RECURSIVE wqlist selectnowith */
-{yymsp[-3].minor.yy47 = attachWithToSelect(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy521);}
+{yymsp[-3].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);}
break;
case 87: /* select ::= selectnowith */
{
- Select *p = yymsp[0].minor.yy47;
+ Select *p = yymsp[0].minor.yy555;
if( p ){
parserDoubleLinkSelect(pParse, p);
}
@@ -175032,8 +176434,8 @@ static YYACTIONTYPE yy_reduce(
break;
case 88: /* selectnowith ::= selectnowith multiselect_op oneselect */
{
- Select *pRhs = yymsp[0].minor.yy47;
- Select *pLhs = yymsp[-2].minor.yy47;
+ Select *pRhs = yymsp[0].minor.yy555;
+ Select *pLhs = yymsp[-2].minor.yy555;
if( pRhs && pRhs->pPrior ){
SrcList *pFrom;
Token x;
@@ -175043,148 +176445,145 @@ static YYACTIONTYPE yy_reduce(
pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0);
}
if( pRhs ){
- pRhs->op = (u8)yymsp[-1].minor.yy394;
+ pRhs->op = (u8)yymsp[-1].minor.yy144;
pRhs->pPrior = pLhs;
if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue;
pRhs->selFlags &= ~SF_MultiValue;
- if( yymsp[-1].minor.yy394!=TK_ALL ) pParse->hasCompound = 1;
+ if( yymsp[-1].minor.yy144!=TK_ALL ) pParse->hasCompound = 1;
}else{
sqlite3SelectDelete(pParse->db, pLhs);
}
- yymsp[-2].minor.yy47 = pRhs;
+ yymsp[-2].minor.yy555 = pRhs;
}
break;
case 89: /* multiselect_op ::= UNION */
case 91: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==91);
-{yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-OP*/}
+{yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-OP*/}
break;
case 90: /* multiselect_op ::= UNION ALL */
-{yymsp[-1].minor.yy394 = TK_ALL;}
+{yymsp[-1].minor.yy144 = TK_ALL;}
break;
case 92: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
{
- yymsp[-8].minor.yy47 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy322,yymsp[-5].minor.yy131,yymsp[-4].minor.yy528,yymsp[-3].minor.yy322,yymsp[-2].minor.yy528,yymsp[-1].minor.yy322,yymsp[-7].minor.yy394,yymsp[0].minor.yy528);
+ yymsp[-8].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy14,yymsp[-5].minor.yy203,yymsp[-4].minor.yy454,yymsp[-3].minor.yy14,yymsp[-2].minor.yy454,yymsp[-1].minor.yy14,yymsp[-7].minor.yy144,yymsp[0].minor.yy454);
}
break;
case 93: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
{
- yymsp[-9].minor.yy47 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy322,yymsp[-6].minor.yy131,yymsp[-5].minor.yy528,yymsp[-4].minor.yy322,yymsp[-3].minor.yy528,yymsp[-1].minor.yy322,yymsp[-8].minor.yy394,yymsp[0].minor.yy528);
- if( yymsp[-9].minor.yy47 ){
- yymsp[-9].minor.yy47->pWinDefn = yymsp[-2].minor.yy41;
+ yymsp[-9].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy14,yymsp[-6].minor.yy203,yymsp[-5].minor.yy454,yymsp[-4].minor.yy14,yymsp[-3].minor.yy454,yymsp[-1].minor.yy14,yymsp[-8].minor.yy144,yymsp[0].minor.yy454);
+ if( yymsp[-9].minor.yy555 ){
+ yymsp[-9].minor.yy555->pWinDefn = yymsp[-2].minor.yy211;
}else{
- sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy41);
+ sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy211);
}
}
break;
case 94: /* values ::= VALUES LP nexprlist RP */
{
- yymsp[-3].minor.yy47 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy322,0,0,0,0,0,SF_Values,0);
+ yymsp[-3].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy14,0,0,0,0,0,SF_Values,0);
}
break;
- case 95: /* values ::= values COMMA LP nexprlist RP */
+ case 95: /* oneselect ::= mvalues */
{
- Select *pRight, *pLeft = yymsp[-4].minor.yy47;
- pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy322,0,0,0,0,0,SF_Values|SF_MultiValue,0);
- if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue;
- if( pRight ){
- pRight->op = TK_ALL;
- pRight->pPrior = pLeft;
- yymsp[-4].minor.yy47 = pRight;
- }else{
- yymsp[-4].minor.yy47 = pLeft;
- }
+ sqlite3MultiValuesEnd(pParse, yymsp[0].minor.yy555);
+}
+ break;
+ case 96: /* mvalues ::= values COMMA LP nexprlist RP */
+ case 97: /* mvalues ::= mvalues COMMA LP nexprlist RP */ yytestcase(yyruleno==97);
+{
+ yymsp[-4].minor.yy555 = sqlite3MultiValues(pParse, yymsp[-4].minor.yy555, yymsp[-1].minor.yy14);
}
break;
- case 96: /* distinct ::= DISTINCT */
-{yymsp[0].minor.yy394 = SF_Distinct;}
+ case 98: /* distinct ::= DISTINCT */
+{yymsp[0].minor.yy144 = SF_Distinct;}
break;
- case 97: /* distinct ::= ALL */
-{yymsp[0].minor.yy394 = SF_All;}
+ case 99: /* distinct ::= ALL */
+{yymsp[0].minor.yy144 = SF_All;}
break;
- case 99: /* sclp ::= */
- case 132: /* orderby_opt ::= */ yytestcase(yyruleno==132);
- case 142: /* groupby_opt ::= */ yytestcase(yyruleno==142);
- case 232: /* exprlist ::= */ yytestcase(yyruleno==232);
- case 235: /* paren_exprlist ::= */ yytestcase(yyruleno==235);
- case 240: /* eidlist_opt ::= */ yytestcase(yyruleno==240);
-{yymsp[1].minor.yy322 = 0;}
+ case 101: /* sclp ::= */
+ case 134: /* orderby_opt ::= */ yytestcase(yyruleno==134);
+ case 144: /* groupby_opt ::= */ yytestcase(yyruleno==144);
+ case 234: /* exprlist ::= */ yytestcase(yyruleno==234);
+ case 237: /* paren_exprlist ::= */ yytestcase(yyruleno==237);
+ case 242: /* eidlist_opt ::= */ yytestcase(yyruleno==242);
+{yymsp[1].minor.yy14 = 0;}
break;
- case 100: /* selcollist ::= sclp scanpt expr scanpt as */
+ case 102: /* selcollist ::= sclp scanpt expr scanpt as */
{
- yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[-2].minor.yy528);
- if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy322, &yymsp[0].minor.yy0, 1);
- sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy322,yymsp[-3].minor.yy522,yymsp[-1].minor.yy522);
+ yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy14, yymsp[-2].minor.yy454);
+ if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy14, &yymsp[0].minor.yy0, 1);
+ sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy14,yymsp[-3].minor.yy168,yymsp[-1].minor.yy168);
}
break;
- case 101: /* selcollist ::= sclp scanpt STAR */
+ case 103: /* selcollist ::= sclp scanpt STAR */
{
Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0);
sqlite3ExprSetErrorOffset(p, (int)(yymsp[0].minor.yy0.z - pParse->zTail));
- yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy322, p);
+ yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy14, p);
}
break;
- case 102: /* selcollist ::= sclp scanpt nm DOT STAR */
+ case 104: /* selcollist ::= sclp scanpt nm DOT STAR */
{
Expr *pRight, *pLeft, *pDot;
pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0);
sqlite3ExprSetErrorOffset(pRight, (int)(yymsp[0].minor.yy0.z - pParse->zTail));
pLeft = tokenExpr(pParse, TK_ID, yymsp[-2].minor.yy0);
pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight);
- yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, pDot);
+ yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, pDot);
}
break;
- case 103: /* as ::= AS nm */
- case 115: /* dbnm ::= DOT nm */ yytestcase(yyruleno==115);
- case 256: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==256);
- case 257: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==257);
+ case 105: /* as ::= AS nm */
+ case 117: /* dbnm ::= DOT nm */ yytestcase(yyruleno==117);
+ case 258: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==258);
+ case 259: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==259);
{yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;}
break;
- case 105: /* from ::= */
- case 108: /* stl_prefix ::= */ yytestcase(yyruleno==108);
-{yymsp[1].minor.yy131 = 0;}
+ case 107: /* from ::= */
+ case 110: /* stl_prefix ::= */ yytestcase(yyruleno==110);
+{yymsp[1].minor.yy203 = 0;}
break;
- case 106: /* from ::= FROM seltablist */
+ case 108: /* from ::= FROM seltablist */
{
- yymsp[-1].minor.yy131 = yymsp[0].minor.yy131;
- sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy131);
+ yymsp[-1].minor.yy203 = yymsp[0].minor.yy203;
+ sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy203);
}
break;
- case 107: /* stl_prefix ::= seltablist joinop */
+ case 109: /* stl_prefix ::= seltablist joinop */
{
- if( ALWAYS(yymsp[-1].minor.yy131 && yymsp[-1].minor.yy131->nSrc>0) ) yymsp[-1].minor.yy131->a[yymsp[-1].minor.yy131->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy394;
+ if( ALWAYS(yymsp[-1].minor.yy203 && yymsp[-1].minor.yy203->nSrc>0) ) yymsp[-1].minor.yy203->a[yymsp[-1].minor.yy203->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy144;
}
break;
- case 109: /* seltablist ::= stl_prefix nm dbnm as on_using */
+ case 111: /* seltablist ::= stl_prefix nm dbnm as on_using */
{
- yymsp[-4].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy131,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561);
+ yymsp[-4].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy203,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269);
}
break;
- case 110: /* seltablist ::= stl_prefix nm dbnm as indexed_by on_using */
+ case 112: /* seltablist ::= stl_prefix nm dbnm as indexed_by on_using */
{
- yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy561);
- sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy131, &yymsp[-1].minor.yy0);
+ yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy269);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy203, &yymsp[-1].minor.yy0);
}
break;
- case 111: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */
+ case 113: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */
{
- yymsp[-7].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy131,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561);
- sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy131, yymsp[-3].minor.yy322);
+ yymsp[-7].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy203,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269);
+ sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy203, yymsp[-3].minor.yy14);
}
break;
- case 112: /* seltablist ::= stl_prefix LP select RP as on_using */
+ case 114: /* seltablist ::= stl_prefix LP select RP as on_using */
{
- yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy47,&yymsp[0].minor.yy561);
+ yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy555,&yymsp[0].minor.yy269);
}
break;
- case 113: /* seltablist ::= stl_prefix LP seltablist RP as on_using */
+ case 115: /* seltablist ::= stl_prefix LP seltablist RP as on_using */
{
- if( yymsp[-5].minor.yy131==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy561.pOn==0 && yymsp[0].minor.yy561.pUsing==0 ){
- yymsp[-5].minor.yy131 = yymsp[-3].minor.yy131;
- }else if( ALWAYS(yymsp[-3].minor.yy131!=0) && yymsp[-3].minor.yy131->nSrc==1 ){
- yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561);
- if( yymsp[-5].minor.yy131 ){
- SrcItem *pNew = &yymsp[-5].minor.yy131->a[yymsp[-5].minor.yy131->nSrc-1];
- SrcItem *pOld = yymsp[-3].minor.yy131->a;
+ if( yymsp[-5].minor.yy203==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy269.pOn==0 && yymsp[0].minor.yy269.pUsing==0 ){
+ yymsp[-5].minor.yy203 = yymsp[-3].minor.yy203;
+ }else if( ALWAYS(yymsp[-3].minor.yy203!=0) && yymsp[-3].minor.yy203->nSrc==1 ){
+ yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269);
+ if( yymsp[-5].minor.yy203 ){
+ SrcItem *pNew = &yymsp[-5].minor.yy203->a[yymsp[-5].minor.yy203->nSrc-1];
+ SrcItem *pOld = yymsp[-3].minor.yy203->a;
pNew->zName = pOld->zName;
pNew->zDatabase = pOld->zDatabase;
pNew->pSelect = pOld->pSelect;
@@ -175200,153 +176599,153 @@ static YYACTIONTYPE yy_reduce(
pOld->zName = pOld->zDatabase = 0;
pOld->pSelect = 0;
}
- sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy131);
+ sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy203);
}else{
Select *pSubquery;
- sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy131);
- pSubquery = sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy131,0,0,0,0,SF_NestedFrom,0);
- yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy561);
+ sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy203);
+ pSubquery = sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy203,0,0,0,0,SF_NestedFrom,0);
+ yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy269);
}
}
break;
- case 114: /* dbnm ::= */
- case 129: /* indexed_opt ::= */ yytestcase(yyruleno==129);
+ case 116: /* dbnm ::= */
+ case 131: /* indexed_opt ::= */ yytestcase(yyruleno==131);
{yymsp[1].minor.yy0.z=0; yymsp[1].minor.yy0.n=0;}
break;
- case 116: /* fullname ::= nm */
+ case 118: /* fullname ::= nm */
{
- yylhsminor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0);
- if( IN_RENAME_OBJECT && yylhsminor.yy131 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy131->a[0].zName, &yymsp[0].minor.yy0);
+ yylhsminor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0);
+ if( IN_RENAME_OBJECT && yylhsminor.yy203 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy203->a[0].zName, &yymsp[0].minor.yy0);
}
- yymsp[0].minor.yy131 = yylhsminor.yy131;
+ yymsp[0].minor.yy203 = yylhsminor.yy203;
break;
- case 117: /* fullname ::= nm DOT nm */
+ case 119: /* fullname ::= nm DOT nm */
{
- yylhsminor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
- if( IN_RENAME_OBJECT && yylhsminor.yy131 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy131->a[0].zName, &yymsp[0].minor.yy0);
+ yylhsminor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
+ if( IN_RENAME_OBJECT && yylhsminor.yy203 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy203->a[0].zName, &yymsp[0].minor.yy0);
}
- yymsp[-2].minor.yy131 = yylhsminor.yy131;
+ yymsp[-2].minor.yy203 = yylhsminor.yy203;
break;
- case 118: /* xfullname ::= nm */
-{yymsp[0].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/}
+ case 120: /* xfullname ::= nm */
+{yymsp[0].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/}
break;
- case 119: /* xfullname ::= nm DOT nm */
-{yymsp[-2].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/}
+ case 121: /* xfullname ::= nm DOT nm */
+{yymsp[-2].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
- case 120: /* xfullname ::= nm DOT nm AS nm */
+ case 122: /* xfullname ::= nm DOT nm AS nm */
{
- yymsp[-4].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/
- if( yymsp[-4].minor.yy131 ) yymsp[-4].minor.yy131->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
+ yymsp[-4].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/
+ if( yymsp[-4].minor.yy203 ) yymsp[-4].minor.yy203->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
}
break;
- case 121: /* xfullname ::= nm AS nm */
+ case 123: /* xfullname ::= nm AS nm */
{
- yymsp[-2].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/
- if( yymsp[-2].minor.yy131 ) yymsp[-2].minor.yy131->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
+ yymsp[-2].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/
+ if( yymsp[-2].minor.yy203 ) yymsp[-2].minor.yy203->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
}
break;
- case 122: /* joinop ::= COMMA|JOIN */
-{ yymsp[0].minor.yy394 = JT_INNER; }
+ case 124: /* joinop ::= COMMA|JOIN */
+{ yymsp[0].minor.yy144 = JT_INNER; }
break;
- case 123: /* joinop ::= JOIN_KW JOIN */
-{yymsp[-1].minor.yy394 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/}
+ case 125: /* joinop ::= JOIN_KW JOIN */
+{yymsp[-1].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/}
break;
- case 124: /* joinop ::= JOIN_KW nm JOIN */
-{yymsp[-2].minor.yy394 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
+ case 126: /* joinop ::= JOIN_KW nm JOIN */
+{yymsp[-2].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
break;
- case 125: /* joinop ::= JOIN_KW nm nm JOIN */
-{yymsp[-3].minor.yy394 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
+ case 127: /* joinop ::= JOIN_KW nm nm JOIN */
+{yymsp[-3].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
break;
- case 126: /* on_using ::= ON expr */
-{yymsp[-1].minor.yy561.pOn = yymsp[0].minor.yy528; yymsp[-1].minor.yy561.pUsing = 0;}
+ case 128: /* on_using ::= ON expr */
+{yymsp[-1].minor.yy269.pOn = yymsp[0].minor.yy454; yymsp[-1].minor.yy269.pUsing = 0;}
break;
- case 127: /* on_using ::= USING LP idlist RP */
-{yymsp[-3].minor.yy561.pOn = 0; yymsp[-3].minor.yy561.pUsing = yymsp[-1].minor.yy254;}
+ case 129: /* on_using ::= USING LP idlist RP */
+{yymsp[-3].minor.yy269.pOn = 0; yymsp[-3].minor.yy269.pUsing = yymsp[-1].minor.yy132;}
break;
- case 128: /* on_using ::= */
-{yymsp[1].minor.yy561.pOn = 0; yymsp[1].minor.yy561.pUsing = 0;}
+ case 130: /* on_using ::= */
+{yymsp[1].minor.yy269.pOn = 0; yymsp[1].minor.yy269.pUsing = 0;}
break;
- case 130: /* indexed_by ::= INDEXED BY nm */
+ case 132: /* indexed_by ::= INDEXED BY nm */
{yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;}
break;
- case 131: /* indexed_by ::= NOT INDEXED */
+ case 133: /* indexed_by ::= NOT INDEXED */
{yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;}
break;
- case 133: /* orderby_opt ::= ORDER BY sortlist */
- case 143: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==143);
-{yymsp[-2].minor.yy322 = yymsp[0].minor.yy322;}
+ case 135: /* orderby_opt ::= ORDER BY sortlist */
+ case 145: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==145);
+{yymsp[-2].minor.yy14 = yymsp[0].minor.yy14;}
break;
- case 134: /* sortlist ::= sortlist COMMA expr sortorder nulls */
+ case 136: /* sortlist ::= sortlist COMMA expr sortorder nulls */
{
- yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322,yymsp[-2].minor.yy528);
- sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy322,yymsp[-1].minor.yy394,yymsp[0].minor.yy394);
+ yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14,yymsp[-2].minor.yy454);
+ sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy14,yymsp[-1].minor.yy144,yymsp[0].minor.yy144);
}
break;
- case 135: /* sortlist ::= expr sortorder nulls */
+ case 137: /* sortlist ::= expr sortorder nulls */
{
- yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy528); /*A-overwrites-Y*/
- sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy322,yymsp[-1].minor.yy394,yymsp[0].minor.yy394);
+ yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy454); /*A-overwrites-Y*/
+ sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy14,yymsp[-1].minor.yy144,yymsp[0].minor.yy144);
}
break;
- case 136: /* sortorder ::= ASC */
-{yymsp[0].minor.yy394 = SQLITE_SO_ASC;}
+ case 138: /* sortorder ::= ASC */
+{yymsp[0].minor.yy144 = SQLITE_SO_ASC;}
break;
- case 137: /* sortorder ::= DESC */
-{yymsp[0].minor.yy394 = SQLITE_SO_DESC;}
+ case 139: /* sortorder ::= DESC */
+{yymsp[0].minor.yy144 = SQLITE_SO_DESC;}
break;
- case 138: /* sortorder ::= */
- case 141: /* nulls ::= */ yytestcase(yyruleno==141);
-{yymsp[1].minor.yy394 = SQLITE_SO_UNDEFINED;}
+ case 140: /* sortorder ::= */
+ case 143: /* nulls ::= */ yytestcase(yyruleno==143);
+{yymsp[1].minor.yy144 = SQLITE_SO_UNDEFINED;}
break;
- case 139: /* nulls ::= NULLS FIRST */
-{yymsp[-1].minor.yy394 = SQLITE_SO_ASC;}
+ case 141: /* nulls ::= NULLS FIRST */
+{yymsp[-1].minor.yy144 = SQLITE_SO_ASC;}
break;
- case 140: /* nulls ::= NULLS LAST */
-{yymsp[-1].minor.yy394 = SQLITE_SO_DESC;}
+ case 142: /* nulls ::= NULLS LAST */
+{yymsp[-1].minor.yy144 = SQLITE_SO_DESC;}
break;
- case 144: /* having_opt ::= */
- case 146: /* limit_opt ::= */ yytestcase(yyruleno==146);
- case 151: /* where_opt ::= */ yytestcase(yyruleno==151);
- case 153: /* where_opt_ret ::= */ yytestcase(yyruleno==153);
- case 230: /* case_else ::= */ yytestcase(yyruleno==230);
- case 231: /* case_operand ::= */ yytestcase(yyruleno==231);
- case 250: /* vinto ::= */ yytestcase(yyruleno==250);
-{yymsp[1].minor.yy528 = 0;}
+ case 146: /* having_opt ::= */
+ case 148: /* limit_opt ::= */ yytestcase(yyruleno==148);
+ case 153: /* where_opt ::= */ yytestcase(yyruleno==153);
+ case 155: /* where_opt_ret ::= */ yytestcase(yyruleno==155);
+ case 232: /* case_else ::= */ yytestcase(yyruleno==232);
+ case 233: /* case_operand ::= */ yytestcase(yyruleno==233);
+ case 252: /* vinto ::= */ yytestcase(yyruleno==252);
+{yymsp[1].minor.yy454 = 0;}
break;
- case 145: /* having_opt ::= HAVING expr */
- case 152: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==152);
- case 154: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==154);
- case 229: /* case_else ::= ELSE expr */ yytestcase(yyruleno==229);
- case 249: /* vinto ::= INTO expr */ yytestcase(yyruleno==249);
-{yymsp[-1].minor.yy528 = yymsp[0].minor.yy528;}
+ case 147: /* having_opt ::= HAVING expr */
+ case 154: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==154);
+ case 156: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==156);
+ case 231: /* case_else ::= ELSE expr */ yytestcase(yyruleno==231);
+ case 251: /* vinto ::= INTO expr */ yytestcase(yyruleno==251);
+{yymsp[-1].minor.yy454 = yymsp[0].minor.yy454;}
break;
- case 147: /* limit_opt ::= LIMIT expr */
-{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,0);}
+ case 149: /* limit_opt ::= LIMIT expr */
+{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy454,0);}
break;
- case 148: /* limit_opt ::= LIMIT expr OFFSET expr */
-{yymsp[-3].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);}
+ case 150: /* limit_opt ::= LIMIT expr OFFSET expr */
+{yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);}
break;
- case 149: /* limit_opt ::= LIMIT expr COMMA expr */
-{yymsp[-3].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,yymsp[-2].minor.yy528);}
+ case 151: /* limit_opt ::= LIMIT expr COMMA expr */
+{yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy454,yymsp[-2].minor.yy454);}
break;
- case 150: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
+ case 152: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
{
- sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy131, &yymsp[-1].minor.yy0);
- sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy131,yymsp[0].minor.yy528,0,0);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy203, &yymsp[-1].minor.yy0);
+ sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy203,yymsp[0].minor.yy454,0,0);
}
break;
- case 155: /* where_opt_ret ::= RETURNING selcollist */
-{sqlite3AddReturning(pParse,yymsp[0].minor.yy322); yymsp[-1].minor.yy528 = 0;}
+ case 157: /* where_opt_ret ::= RETURNING selcollist */
+{sqlite3AddReturning(pParse,yymsp[0].minor.yy14); yymsp[-1].minor.yy454 = 0;}
break;
- case 156: /* where_opt_ret ::= WHERE expr RETURNING selcollist */
-{sqlite3AddReturning(pParse,yymsp[0].minor.yy322); yymsp[-3].minor.yy528 = yymsp[-2].minor.yy528;}
+ case 158: /* where_opt_ret ::= WHERE expr RETURNING selcollist */
+{sqlite3AddReturning(pParse,yymsp[0].minor.yy14); yymsp[-3].minor.yy454 = yymsp[-2].minor.yy454;}
break;
- case 157: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
+ case 159: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
{
- sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy131, &yymsp[-4].minor.yy0);
- sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy322,"set list");
- if( yymsp[-1].minor.yy131 ){
- SrcList *pFromClause = yymsp[-1].minor.yy131;
+ sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy203, &yymsp[-4].minor.yy0);
+ sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy14,"set list");
+ if( yymsp[-1].minor.yy203 ){
+ SrcList *pFromClause = yymsp[-1].minor.yy203;
if( pFromClause->nSrc>1 ){
Select *pSubquery;
Token as;
@@ -175355,92 +176754,92 @@ static YYACTIONTYPE yy_reduce(
as.z = 0;
pFromClause = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0);
}
- yymsp[-5].minor.yy131 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy131, pFromClause);
+ yymsp[-5].minor.yy203 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy203, pFromClause);
}
- sqlite3Update(pParse,yymsp[-5].minor.yy131,yymsp[-2].minor.yy322,yymsp[0].minor.yy528,yymsp[-6].minor.yy394,0,0,0);
+ sqlite3Update(pParse,yymsp[-5].minor.yy203,yymsp[-2].minor.yy14,yymsp[0].minor.yy454,yymsp[-6].minor.yy144,0,0,0);
}
break;
- case 158: /* setlist ::= setlist COMMA nm EQ expr */
+ case 160: /* setlist ::= setlist COMMA nm EQ expr */
{
- yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[0].minor.yy528);
- sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, 1);
+ yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy14, yymsp[0].minor.yy454);
+ sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy14, &yymsp[-2].minor.yy0, 1);
}
break;
- case 159: /* setlist ::= setlist COMMA LP idlist RP EQ expr */
+ case 161: /* setlist ::= setlist COMMA LP idlist RP EQ expr */
{
- yymsp[-6].minor.yy322 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy322, yymsp[-3].minor.yy254, yymsp[0].minor.yy528);
+ yymsp[-6].minor.yy14 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy14, yymsp[-3].minor.yy132, yymsp[0].minor.yy454);
}
break;
- case 160: /* setlist ::= nm EQ expr */
+ case 162: /* setlist ::= nm EQ expr */
{
- yylhsminor.yy322 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy528);
- sqlite3ExprListSetName(pParse, yylhsminor.yy322, &yymsp[-2].minor.yy0, 1);
+ yylhsminor.yy14 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy454);
+ sqlite3ExprListSetName(pParse, yylhsminor.yy14, &yymsp[-2].minor.yy0, 1);
}
- yymsp[-2].minor.yy322 = yylhsminor.yy322;
+ yymsp[-2].minor.yy14 = yylhsminor.yy14;
break;
- case 161: /* setlist ::= LP idlist RP EQ expr */
+ case 163: /* setlist ::= LP idlist RP EQ expr */
{
- yymsp[-4].minor.yy322 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy254, yymsp[0].minor.yy528);
+ yymsp[-4].minor.yy14 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy132, yymsp[0].minor.yy454);
}
break;
- case 162: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
+ case 164: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
{
- sqlite3Insert(pParse, yymsp[-3].minor.yy131, yymsp[-1].minor.yy47, yymsp[-2].minor.yy254, yymsp[-5].minor.yy394, yymsp[0].minor.yy444);
+ sqlite3Insert(pParse, yymsp[-3].minor.yy203, yymsp[-1].minor.yy555, yymsp[-2].minor.yy132, yymsp[-5].minor.yy144, yymsp[0].minor.yy122);
}
break;
- case 163: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
+ case 165: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
{
- sqlite3Insert(pParse, yymsp[-4].minor.yy131, 0, yymsp[-3].minor.yy254, yymsp[-6].minor.yy394, 0);
+ sqlite3Insert(pParse, yymsp[-4].minor.yy203, 0, yymsp[-3].minor.yy132, yymsp[-6].minor.yy144, 0);
}
break;
- case 164: /* upsert ::= */
-{ yymsp[1].minor.yy444 = 0; }
+ case 166: /* upsert ::= */
+{ yymsp[1].minor.yy122 = 0; }
break;
- case 165: /* upsert ::= RETURNING selcollist */
-{ yymsp[-1].minor.yy444 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy322); }
+ case 167: /* upsert ::= RETURNING selcollist */
+{ yymsp[-1].minor.yy122 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy14); }
break;
- case 166: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
-{ yymsp[-11].minor.yy444 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy322,yymsp[-6].minor.yy528,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528,yymsp[0].minor.yy444);}
+ case 168: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
+{ yymsp[-11].minor.yy122 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy14,yymsp[-6].minor.yy454,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454,yymsp[0].minor.yy122);}
break;
- case 167: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
-{ yymsp[-8].minor.yy444 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy322,yymsp[-3].minor.yy528,0,0,yymsp[0].minor.yy444); }
+ case 169: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
+{ yymsp[-8].minor.yy122 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy14,yymsp[-3].minor.yy454,0,0,yymsp[0].minor.yy122); }
break;
- case 168: /* upsert ::= ON CONFLICT DO NOTHING returning */
-{ yymsp[-4].minor.yy444 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); }
+ case 170: /* upsert ::= ON CONFLICT DO NOTHING returning */
+{ yymsp[-4].minor.yy122 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); }
break;
- case 169: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
-{ yymsp[-7].minor.yy444 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528,0);}
+ case 171: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
+{ yymsp[-7].minor.yy122 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454,0);}
break;
- case 170: /* returning ::= RETURNING selcollist */
-{sqlite3AddReturning(pParse,yymsp[0].minor.yy322);}
+ case 172: /* returning ::= RETURNING selcollist */
+{sqlite3AddReturning(pParse,yymsp[0].minor.yy14);}
break;
- case 173: /* idlist_opt ::= */
-{yymsp[1].minor.yy254 = 0;}
+ case 175: /* idlist_opt ::= */
+{yymsp[1].minor.yy132 = 0;}
break;
- case 174: /* idlist_opt ::= LP idlist RP */
-{yymsp[-2].minor.yy254 = yymsp[-1].minor.yy254;}
+ case 176: /* idlist_opt ::= LP idlist RP */
+{yymsp[-2].minor.yy132 = yymsp[-1].minor.yy132;}
break;
- case 175: /* idlist ::= idlist COMMA nm */
-{yymsp[-2].minor.yy254 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy254,&yymsp[0].minor.yy0);}
+ case 177: /* idlist ::= idlist COMMA nm */
+{yymsp[-2].minor.yy132 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy132,&yymsp[0].minor.yy0);}
break;
- case 176: /* idlist ::= nm */
-{yymsp[0].minor.yy254 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
+ case 178: /* idlist ::= nm */
+{yymsp[0].minor.yy132 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
break;
- case 177: /* expr ::= LP expr RP */
-{yymsp[-2].minor.yy528 = yymsp[-1].minor.yy528;}
+ case 179: /* expr ::= LP expr RP */
+{yymsp[-2].minor.yy454 = yymsp[-1].minor.yy454;}
break;
- case 178: /* expr ::= ID|INDEXED|JOIN_KW */
-{yymsp[0].minor.yy528=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
+ case 180: /* expr ::= ID|INDEXED|JOIN_KW */
+{yymsp[0].minor.yy454=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
- case 179: /* expr ::= nm DOT nm */
+ case 181: /* expr ::= nm DOT nm */
{
Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0);
Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0);
- yylhsminor.yy528 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
+ yylhsminor.yy454 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
}
- yymsp[-2].minor.yy528 = yylhsminor.yy528;
+ yymsp[-2].minor.yy454 = yylhsminor.yy454;
break;
- case 180: /* expr ::= nm DOT nm DOT nm */
+ case 182: /* expr ::= nm DOT nm DOT nm */
{
Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-4].minor.yy0);
Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0);
@@ -175449,27 +176848,27 @@ static YYACTIONTYPE yy_reduce(
if( IN_RENAME_OBJECT ){
sqlite3RenameTokenRemap(pParse, 0, temp1);
}
- yylhsminor.yy528 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
+ yylhsminor.yy454 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
}
- yymsp[-4].minor.yy528 = yylhsminor.yy528;
+ yymsp[-4].minor.yy454 = yylhsminor.yy454;
break;
- case 181: /* term ::= NULL|FLOAT|BLOB */
- case 182: /* term ::= STRING */ yytestcase(yyruleno==182);
-{yymsp[0].minor.yy528=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
+ case 183: /* term ::= NULL|FLOAT|BLOB */
+ case 184: /* term ::= STRING */ yytestcase(yyruleno==184);
+{yymsp[0].minor.yy454=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
- case 183: /* term ::= INTEGER */
+ case 185: /* term ::= INTEGER */
{
- yylhsminor.yy528 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
- if( yylhsminor.yy528 ) yylhsminor.yy528->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail);
+ yylhsminor.yy454 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
+ if( yylhsminor.yy454 ) yylhsminor.yy454->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail);
}
- yymsp[0].minor.yy528 = yylhsminor.yy528;
+ yymsp[0].minor.yy454 = yylhsminor.yy454;
break;
- case 184: /* expr ::= VARIABLE */
+ case 186: /* expr ::= VARIABLE */
{
if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){
u32 n = yymsp[0].minor.yy0.n;
- yymsp[0].minor.yy528 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
- sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy528, n);
+ yymsp[0].minor.yy454 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
+ sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy454, n);
}else{
/* When doing a nested parse, one can include terms in an expression
** that look like this: #1 #2 ... These terms refer to registers
@@ -175478,194 +176877,203 @@ static YYACTIONTYPE yy_reduce(
assert( t.n>=2 );
if( pParse->nested==0 ){
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t);
- yymsp[0].minor.yy528 = 0;
+ yymsp[0].minor.yy454 = 0;
}else{
- yymsp[0].minor.yy528 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
- if( yymsp[0].minor.yy528 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy528->iTable);
+ yymsp[0].minor.yy454 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
+ if( yymsp[0].minor.yy454 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy454->iTable);
}
}
}
break;
- case 185: /* expr ::= expr COLLATE ID|STRING */
+ case 187: /* expr ::= expr COLLATE ID|STRING */
{
- yymsp[-2].minor.yy528 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy528, &yymsp[0].minor.yy0, 1);
+ yymsp[-2].minor.yy454 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy454, &yymsp[0].minor.yy0, 1);
}
break;
- case 186: /* expr ::= CAST LP expr AS typetoken RP */
+ case 188: /* expr ::= CAST LP expr AS typetoken RP */
{
- yymsp[-5].minor.yy528 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
- sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy528, yymsp[-3].minor.yy528, 0);
+ yymsp[-5].minor.yy454 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
+ sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy454, yymsp[-3].minor.yy454, 0);
}
break;
- case 187: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
+ case 189: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
{
- yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy394);
+ yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy14, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy144);
}
- yymsp[-4].minor.yy528 = yylhsminor.yy528;
+ yymsp[-4].minor.yy454 = yylhsminor.yy454;
break;
- case 188: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */
+ case 190: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */
{
- yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-4].minor.yy322, &yymsp[-7].minor.yy0, yymsp[-5].minor.yy394);
- sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy528, yymsp[-1].minor.yy322);
+ yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-4].minor.yy14, &yymsp[-7].minor.yy0, yymsp[-5].minor.yy144);
+ sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy454, yymsp[-1].minor.yy14);
}
- yymsp[-7].minor.yy528 = yylhsminor.yy528;
+ yymsp[-7].minor.yy454 = yylhsminor.yy454;
break;
- case 189: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
+ case 191: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
{
- yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
+ yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
}
- yymsp[-3].minor.yy528 = yylhsminor.yy528;
+ yymsp[-3].minor.yy454 = yylhsminor.yy454;
break;
- case 190: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
+ case 192: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
{
- yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy322, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy394);
- sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41);
+ yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy14, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy144);
+ sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211);
}
- yymsp[-5].minor.yy528 = yylhsminor.yy528;
+ yymsp[-5].minor.yy454 = yylhsminor.yy454;
break;
- case 191: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */
+ case 193: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */
{
- yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-5].minor.yy322, &yymsp[-8].minor.yy0, yymsp[-6].minor.yy394);
- sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41);
- sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy528, yymsp[-2].minor.yy322);
+ yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-5].minor.yy14, &yymsp[-8].minor.yy0, yymsp[-6].minor.yy144);
+ sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211);
+ sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy454, yymsp[-2].minor.yy14);
}
- yymsp[-8].minor.yy528 = yylhsminor.yy528;
+ yymsp[-8].minor.yy454 = yylhsminor.yy454;
break;
- case 192: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
+ case 194: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
{
- yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
- sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41);
+ yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
+ sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211);
}
- yymsp[-4].minor.yy528 = yylhsminor.yy528;
+ yymsp[-4].minor.yy454 = yylhsminor.yy454;
break;
- case 193: /* term ::= CTIME_KW */
+ case 195: /* term ::= CTIME_KW */
{
- yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
+ yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
}
- yymsp[0].minor.yy528 = yylhsminor.yy528;
+ yymsp[0].minor.yy454 = yylhsminor.yy454;
break;
- case 194: /* expr ::= LP nexprlist COMMA expr RP */
+ case 196: /* expr ::= LP nexprlist COMMA expr RP */
{
- ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528);
- yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
- if( yymsp[-4].minor.yy528 ){
- yymsp[-4].minor.yy528->x.pList = pList;
+ ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy14, yymsp[-1].minor.yy454);
+ yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
+ if( yymsp[-4].minor.yy454 ){
+ yymsp[-4].minor.yy454->x.pList = pList;
if( ALWAYS(pList->nExpr) ){
- yymsp[-4].minor.yy528->flags |= pList->a[0].pExpr->flags & EP_Propagate;
+ yymsp[-4].minor.yy454->flags |= pList->a[0].pExpr->flags & EP_Propagate;
}
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
}
break;
- case 195: /* expr ::= expr AND expr */
-{yymsp[-2].minor.yy528=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);}
+ case 197: /* expr ::= expr AND expr */
+{yymsp[-2].minor.yy454=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);}
break;
- case 196: /* expr ::= expr OR expr */
- case 197: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==197);
- case 198: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==198);
- case 199: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==199);
- case 200: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==200);
- case 201: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==201);
- case 202: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==202);
-{yymsp[-2].minor.yy528=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);}
+ case 198: /* expr ::= expr OR expr */
+ case 199: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==199);
+ case 200: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==200);
+ case 201: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==201);
+ case 202: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==202);
+ case 203: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==203);
+ case 204: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==204);
+{yymsp[-2].minor.yy454=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);}
break;
- case 203: /* likeop ::= NOT LIKE_KW|MATCH */
+ case 205: /* likeop ::= NOT LIKE_KW|MATCH */
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/}
break;
- case 204: /* expr ::= expr likeop expr */
+ case 206: /* expr ::= expr likeop expr */
{
ExprList *pList;
int bNot = yymsp[-1].minor.yy0.n & 0x80000000;
yymsp[-1].minor.yy0.n &= 0x7fffffff;
- pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy528);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy528);
- yymsp[-2].minor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
- if( bNot ) yymsp[-2].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy528, 0);
- if( yymsp[-2].minor.yy528 ) yymsp[-2].minor.yy528->flags |= EP_InfixFunc;
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy454);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy454);
+ yymsp[-2].minor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
+ if( bNot ) yymsp[-2].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy454, 0);
+ if( yymsp[-2].minor.yy454 ) yymsp[-2].minor.yy454->flags |= EP_InfixFunc;
}
break;
- case 205: /* expr ::= expr likeop expr ESCAPE expr */
+ case 207: /* expr ::= expr likeop expr ESCAPE expr */
{
ExprList *pList;
int bNot = yymsp[-3].minor.yy0.n & 0x80000000;
yymsp[-3].minor.yy0.n &= 0x7fffffff;
- pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy528);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528);
- yymsp[-4].minor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
- if( bNot ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
- if( yymsp[-4].minor.yy528 ) yymsp[-4].minor.yy528->flags |= EP_InfixFunc;
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy454);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy454);
+ yymsp[-4].minor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
+ if( bNot ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0);
+ if( yymsp[-4].minor.yy454 ) yymsp[-4].minor.yy454->flags |= EP_InfixFunc;
}
break;
- case 206: /* expr ::= expr ISNULL|NOTNULL */
-{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy528,0);}
+ case 208: /* expr ::= expr ISNULL|NOTNULL */
+{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy454,0);}
break;
- case 207: /* expr ::= expr NOT NULL */
-{yymsp[-2].minor.yy528 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy528,0);}
+ case 209: /* expr ::= expr NOT NULL */
+{yymsp[-2].minor.yy454 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy454,0);}
break;
- case 208: /* expr ::= expr IS expr */
+ case 210: /* expr ::= expr IS expr */
{
- yymsp[-2].minor.yy528 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-2].minor.yy528, TK_ISNULL);
+ yymsp[-2].minor.yy454 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-2].minor.yy454, TK_ISNULL);
}
break;
- case 209: /* expr ::= expr IS NOT expr */
+ case 211: /* expr ::= expr IS NOT expr */
{
- yymsp[-3].minor.yy528 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy528,yymsp[0].minor.yy528);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-3].minor.yy528, TK_NOTNULL);
+ yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy454,yymsp[0].minor.yy454);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-3].minor.yy454, TK_NOTNULL);
}
break;
- case 210: /* expr ::= expr IS NOT DISTINCT FROM expr */
+ case 212: /* expr ::= expr IS NOT DISTINCT FROM expr */
{
- yymsp[-5].minor.yy528 = sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy528,yymsp[0].minor.yy528);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-5].minor.yy528, TK_ISNULL);
+ yymsp[-5].minor.yy454 = sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy454,yymsp[0].minor.yy454);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-5].minor.yy454, TK_ISNULL);
}
break;
- case 211: /* expr ::= expr IS DISTINCT FROM expr */
+ case 213: /* expr ::= expr IS DISTINCT FROM expr */
{
- yymsp[-4].minor.yy528 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy528,yymsp[0].minor.yy528);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-4].minor.yy528, TK_NOTNULL);
+ yymsp[-4].minor.yy454 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy454,yymsp[0].minor.yy454);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-4].minor.yy454, TK_NOTNULL);
}
break;
- case 212: /* expr ::= NOT expr */
- case 213: /* expr ::= BITNOT expr */ yytestcase(yyruleno==213);
-{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy528, 0);/*A-overwrites-B*/}
+ case 214: /* expr ::= NOT expr */
+ case 215: /* expr ::= BITNOT expr */ yytestcase(yyruleno==215);
+{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy454, 0);/*A-overwrites-B*/}
break;
- case 214: /* expr ::= PLUS|MINUS expr */
+ case 216: /* expr ::= PLUS|MINUS expr */
{
- yymsp[-1].minor.yy528 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy528, 0);
- /*A-overwrites-B*/
+ Expr *p = yymsp[0].minor.yy454;
+ u8 op = yymsp[-1].major + (TK_UPLUS-TK_PLUS);
+ assert( TK_UPLUS>TK_PLUS );
+ assert( TK_UMINUS == TK_MINUS + (TK_UPLUS - TK_PLUS) );
+ if( p && p->op==TK_UPLUS ){
+ p->op = op;
+ yymsp[-1].minor.yy454 = p;
+ }else{
+ yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, op, p, 0);
+ /*A-overwrites-B*/
+ }
}
break;
- case 215: /* expr ::= expr PTR expr */
+ case 217: /* expr ::= expr PTR expr */
{
- ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy528);
- pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy528);
- yylhsminor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
+ ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy454);
+ pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy454);
+ yylhsminor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
}
- yymsp[-2].minor.yy528 = yylhsminor.yy528;
+ yymsp[-2].minor.yy454 = yylhsminor.yy454;
break;
- case 216: /* between_op ::= BETWEEN */
- case 219: /* in_op ::= IN */ yytestcase(yyruleno==219);
-{yymsp[0].minor.yy394 = 0;}
+ case 218: /* between_op ::= BETWEEN */
+ case 221: /* in_op ::= IN */ yytestcase(yyruleno==221);
+{yymsp[0].minor.yy144 = 0;}
break;
- case 218: /* expr ::= expr between_op expr AND expr */
+ case 220: /* expr ::= expr between_op expr AND expr */
{
- ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528);
- yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy528, 0);
- if( yymsp[-4].minor.yy528 ){
- yymsp[-4].minor.yy528->x.pList = pList;
+ ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy454);
+ yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy454, 0);
+ if( yymsp[-4].minor.yy454 ){
+ yymsp[-4].minor.yy454->x.pList = pList;
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
- if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
+ if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0);
}
break;
- case 221: /* expr ::= expr in_op LP exprlist RP */
+ case 223: /* expr ::= expr in_op LP exprlist RP */
{
- if( yymsp[-1].minor.yy322==0 ){
+ if( yymsp[-1].minor.yy14==0 ){
/* Expressions of the form
**
** expr1 IN ()
@@ -175674,208 +177082,208 @@ static YYACTIONTYPE yy_reduce(
** simplify to constants 0 (false) and 1 (true), respectively,
** regardless of the value of expr1.
*/
- sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy528);
- yymsp[-4].minor.yy528 = sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy394 ? "true" : "false");
- if( yymsp[-4].minor.yy528 ) sqlite3ExprIdToTrueFalse(yymsp[-4].minor.yy528);
- }else{
- Expr *pRHS = yymsp[-1].minor.yy322->a[0].pExpr;
- if( yymsp[-1].minor.yy322->nExpr==1 && sqlite3ExprIsConstant(pRHS) && yymsp[-4].minor.yy528->op!=TK_VECTOR ){
- yymsp[-1].minor.yy322->a[0].pExpr = 0;
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322);
+ sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy454);
+ yymsp[-4].minor.yy454 = sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy144 ? "true" : "false");
+ if( yymsp[-4].minor.yy454 ) sqlite3ExprIdToTrueFalse(yymsp[-4].minor.yy454);
+ }else{
+ Expr *pRHS = yymsp[-1].minor.yy14->a[0].pExpr;
+ if( yymsp[-1].minor.yy14->nExpr==1 && sqlite3ExprIsConstant(pParse,pRHS) && yymsp[-4].minor.yy454->op!=TK_VECTOR ){
+ yymsp[-1].minor.yy14->a[0].pExpr = 0;
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14);
pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0);
- yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy528, pRHS);
- }else if( yymsp[-1].minor.yy322->nExpr==1 && pRHS->op==TK_SELECT ){
- yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pRHS->x.pSelect);
+ yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy454, pRHS);
+ }else if( yymsp[-1].minor.yy14->nExpr==1 && pRHS->op==TK_SELECT ){
+ yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pRHS->x.pSelect);
pRHS->x.pSelect = 0;
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322);
- }else{
- yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0);
- if( yymsp[-4].minor.yy528==0 ){
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322);
- }else if( yymsp[-4].minor.yy528->pLeft->op==TK_VECTOR ){
- int nExpr = yymsp[-4].minor.yy528->pLeft->x.pList->nExpr;
- Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy322);
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14);
+ }else{
+ yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0);
+ if( yymsp[-4].minor.yy454==0 ){
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14);
+ }else if( yymsp[-4].minor.yy454->pLeft->op==TK_VECTOR ){
+ int nExpr = yymsp[-4].minor.yy454->pLeft->x.pList->nExpr;
+ Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy14);
if( pSelectRHS ){
parserDoubleLinkSelect(pParse, pSelectRHS);
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pSelectRHS);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pSelectRHS);
}
}else{
- yymsp[-4].minor.yy528->x.pList = yymsp[-1].minor.yy322;
- sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy528);
+ yymsp[-4].minor.yy454->x.pList = yymsp[-1].minor.yy14;
+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy454);
}
}
- if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
+ if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0);
}
}
break;
- case 222: /* expr ::= LP select RP */
+ case 224: /* expr ::= LP select RP */
{
- yymsp[-2].minor.yy528 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy528, yymsp[-1].minor.yy47);
+ yymsp[-2].minor.yy454 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy454, yymsp[-1].minor.yy555);
}
break;
- case 223: /* expr ::= expr in_op LP select RP */
+ case 225: /* expr ::= expr in_op LP select RP */
{
- yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, yymsp[-1].minor.yy47);
- if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
+ yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, yymsp[-1].minor.yy555);
+ if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0);
}
break;
- case 224: /* expr ::= expr in_op nm dbnm paren_exprlist */
+ case 226: /* expr ::= expr in_op nm dbnm paren_exprlist */
{
SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);
Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0);
- if( yymsp[0].minor.yy322 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy322);
- yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pSelect);
- if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
+ if( yymsp[0].minor.yy14 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy14);
+ yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pSelect);
+ if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0);
}
break;
- case 225: /* expr ::= EXISTS LP select RP */
+ case 227: /* expr ::= EXISTS LP select RP */
{
Expr *p;
- p = yymsp[-3].minor.yy528 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
- sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy47);
+ p = yymsp[-3].minor.yy454 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
+ sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy555);
}
break;
- case 226: /* expr ::= CASE case_operand case_exprlist case_else END */
+ case 228: /* expr ::= CASE case_operand case_exprlist case_else END */
{
- yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy528, 0);
- if( yymsp[-4].minor.yy528 ){
- yymsp[-4].minor.yy528->x.pList = yymsp[-1].minor.yy528 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528) : yymsp[-2].minor.yy322;
- sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy528);
+ yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy454, 0);
+ if( yymsp[-4].minor.yy454 ){
+ yymsp[-4].minor.yy454->x.pList = yymsp[-1].minor.yy454 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454) : yymsp[-2].minor.yy14;
+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy454);
}else{
- sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy322);
- sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528);
+ sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy14);
+ sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy454);
}
}
break;
- case 227: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ case 229: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
{
- yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[-2].minor.yy528);
- yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[0].minor.yy528);
+ yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, yymsp[-2].minor.yy454);
+ yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, yymsp[0].minor.yy454);
}
break;
- case 228: /* case_exprlist ::= WHEN expr THEN expr */
+ case 230: /* case_exprlist ::= WHEN expr THEN expr */
{
- yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528);
- yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy322, yymsp[0].minor.yy528);
+ yymsp[-3].minor.yy14 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454);
+ yymsp[-3].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy14, yymsp[0].minor.yy454);
}
break;
- case 233: /* nexprlist ::= nexprlist COMMA expr */
-{yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[0].minor.yy528);}
+ case 235: /* nexprlist ::= nexprlist COMMA expr */
+{yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[0].minor.yy454);}
break;
- case 234: /* nexprlist ::= expr */
-{yymsp[0].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy528); /*A-overwrites-Y*/}
+ case 236: /* nexprlist ::= expr */
+{yymsp[0].minor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy454); /*A-overwrites-Y*/}
break;
- case 236: /* paren_exprlist ::= LP exprlist RP */
- case 241: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==241);
-{yymsp[-2].minor.yy322 = yymsp[-1].minor.yy322;}
+ case 238: /* paren_exprlist ::= LP exprlist RP */
+ case 243: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==243);
+{yymsp[-2].minor.yy14 = yymsp[-1].minor.yy14;}
break;
- case 237: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ case 239: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
{
sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0,
- sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy322, yymsp[-10].minor.yy394,
- &yymsp[-11].minor.yy0, yymsp[0].minor.yy528, SQLITE_SO_ASC, yymsp[-8].minor.yy394, SQLITE_IDXTYPE_APPDEF);
+ sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy14, yymsp[-10].minor.yy144,
+ &yymsp[-11].minor.yy0, yymsp[0].minor.yy454, SQLITE_SO_ASC, yymsp[-8].minor.yy144, SQLITE_IDXTYPE_APPDEF);
if( IN_RENAME_OBJECT && pParse->pNewIndex ){
sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0);
}
}
break;
- case 238: /* uniqueflag ::= UNIQUE */
- case 280: /* raisetype ::= ABORT */ yytestcase(yyruleno==280);
-{yymsp[0].minor.yy394 = OE_Abort;}
+ case 240: /* uniqueflag ::= UNIQUE */
+ case 282: /* raisetype ::= ABORT */ yytestcase(yyruleno==282);
+{yymsp[0].minor.yy144 = OE_Abort;}
break;
- case 239: /* uniqueflag ::= */
-{yymsp[1].minor.yy394 = OE_None;}
+ case 241: /* uniqueflag ::= */
+{yymsp[1].minor.yy144 = OE_None;}
break;
- case 242: /* eidlist ::= eidlist COMMA nm collate sortorder */
+ case 244: /* eidlist ::= eidlist COMMA nm collate sortorder */
{
- yymsp[-4].minor.yy322 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394);
+ yymsp[-4].minor.yy14 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy14, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy144, yymsp[0].minor.yy144);
}
break;
- case 243: /* eidlist ::= nm collate sortorder */
+ case 245: /* eidlist ::= nm collate sortorder */
{
- yymsp[-2].minor.yy322 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); /*A-overwrites-Y*/
+ yymsp[-2].minor.yy14 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy144, yymsp[0].minor.yy144); /*A-overwrites-Y*/
}
break;
- case 246: /* cmd ::= DROP INDEX ifexists fullname */
-{sqlite3DropIndex(pParse, yymsp[0].minor.yy131, yymsp[-1].minor.yy394);}
+ case 248: /* cmd ::= DROP INDEX ifexists fullname */
+{sqlite3DropIndex(pParse, yymsp[0].minor.yy203, yymsp[-1].minor.yy144);}
break;
- case 247: /* cmd ::= VACUUM vinto */
-{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy528);}
+ case 249: /* cmd ::= VACUUM vinto */
+{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy454);}
break;
- case 248: /* cmd ::= VACUUM nm vinto */
-{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy528);}
+ case 250: /* cmd ::= VACUUM nm vinto */
+{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy454);}
break;
- case 251: /* cmd ::= PRAGMA nm dbnm */
+ case 253: /* cmd ::= PRAGMA nm dbnm */
{sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);}
break;
- case 252: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
+ case 254: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);}
break;
- case 253: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ case 255: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);}
break;
- case 254: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
+ case 256: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);}
break;
- case 255: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ case 257: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);}
break;
- case 258: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ case 260: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
{
Token all;
all.z = yymsp[-3].minor.yy0.z;
all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n;
- sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy33, &all);
+ sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy427, &all);
}
break;
- case 259: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ case 261: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
{
- sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy394, yymsp[-4].minor.yy180.a, yymsp[-4].minor.yy180.b, yymsp[-2].minor.yy131, yymsp[0].minor.yy528, yymsp[-10].minor.yy394, yymsp[-8].minor.yy394);
+ sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy144, yymsp[-4].minor.yy286.a, yymsp[-4].minor.yy286.b, yymsp[-2].minor.yy203, yymsp[0].minor.yy454, yymsp[-10].minor.yy144, yymsp[-8].minor.yy144);
yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/
}
break;
- case 260: /* trigger_time ::= BEFORE|AFTER */
-{ yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/ }
+ case 262: /* trigger_time ::= BEFORE|AFTER */
+{ yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-X*/ }
break;
- case 261: /* trigger_time ::= INSTEAD OF */
-{ yymsp[-1].minor.yy394 = TK_INSTEAD;}
+ case 263: /* trigger_time ::= INSTEAD OF */
+{ yymsp[-1].minor.yy144 = TK_INSTEAD;}
break;
- case 262: /* trigger_time ::= */
-{ yymsp[1].minor.yy394 = TK_BEFORE; }
+ case 264: /* trigger_time ::= */
+{ yymsp[1].minor.yy144 = TK_BEFORE; }
break;
- case 263: /* trigger_event ::= DELETE|INSERT */
- case 264: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==264);
-{yymsp[0].minor.yy180.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy180.b = 0;}
+ case 265: /* trigger_event ::= DELETE|INSERT */
+ case 266: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==266);
+{yymsp[0].minor.yy286.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy286.b = 0;}
break;
- case 265: /* trigger_event ::= UPDATE OF idlist */
-{yymsp[-2].minor.yy180.a = TK_UPDATE; yymsp[-2].minor.yy180.b = yymsp[0].minor.yy254;}
+ case 267: /* trigger_event ::= UPDATE OF idlist */
+{yymsp[-2].minor.yy286.a = TK_UPDATE; yymsp[-2].minor.yy286.b = yymsp[0].minor.yy132;}
break;
- case 266: /* when_clause ::= */
- case 285: /* key_opt ::= */ yytestcase(yyruleno==285);
-{ yymsp[1].minor.yy528 = 0; }
+ case 268: /* when_clause ::= */
+ case 287: /* key_opt ::= */ yytestcase(yyruleno==287);
+{ yymsp[1].minor.yy454 = 0; }
break;
- case 267: /* when_clause ::= WHEN expr */
- case 286: /* key_opt ::= KEY expr */ yytestcase(yyruleno==286);
-{ yymsp[-1].minor.yy528 = yymsp[0].minor.yy528; }
+ case 269: /* when_clause ::= WHEN expr */
+ case 288: /* key_opt ::= KEY expr */ yytestcase(yyruleno==288);
+{ yymsp[-1].minor.yy454 = yymsp[0].minor.yy454; }
break;
- case 268: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ case 270: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
{
- assert( yymsp[-2].minor.yy33!=0 );
- yymsp[-2].minor.yy33->pLast->pNext = yymsp[-1].minor.yy33;
- yymsp[-2].minor.yy33->pLast = yymsp[-1].minor.yy33;
+ assert( yymsp[-2].minor.yy427!=0 );
+ yymsp[-2].minor.yy427->pLast->pNext = yymsp[-1].minor.yy427;
+ yymsp[-2].minor.yy427->pLast = yymsp[-1].minor.yy427;
}
break;
- case 269: /* trigger_cmd_list ::= trigger_cmd SEMI */
+ case 271: /* trigger_cmd_list ::= trigger_cmd SEMI */
{
- assert( yymsp[-1].minor.yy33!=0 );
- yymsp[-1].minor.yy33->pLast = yymsp[-1].minor.yy33;
+ assert( yymsp[-1].minor.yy427!=0 );
+ yymsp[-1].minor.yy427->pLast = yymsp[-1].minor.yy427;
}
break;
- case 270: /* trnm ::= nm DOT nm */
+ case 272: /* trnm ::= nm DOT nm */
{
yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;
sqlite3ErrorMsg(pParse,
@@ -175883,367 +177291,377 @@ static YYACTIONTYPE yy_reduce(
"statements within triggers");
}
break;
- case 271: /* tridxby ::= INDEXED BY nm */
+ case 273: /* tridxby ::= INDEXED BY nm */
{
sqlite3ErrorMsg(pParse,
"the INDEXED BY clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
- case 272: /* tridxby ::= NOT INDEXED */
+ case 274: /* tridxby ::= NOT INDEXED */
{
sqlite3ErrorMsg(pParse,
"the NOT INDEXED clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
- case 273: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
-{yylhsminor.yy33 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy131, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528, yymsp[-7].minor.yy394, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy522);}
- yymsp[-8].minor.yy33 = yylhsminor.yy33;
+ case 275: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
+{yylhsminor.yy427 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy203, yymsp[-3].minor.yy14, yymsp[-1].minor.yy454, yymsp[-7].minor.yy144, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy168);}
+ yymsp[-8].minor.yy427 = yylhsminor.yy427;
break;
- case 274: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+ case 276: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
{
- yylhsminor.yy33 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy254,yymsp[-2].minor.yy47,yymsp[-6].minor.yy394,yymsp[-1].minor.yy444,yymsp[-7].minor.yy522,yymsp[0].minor.yy522);/*yylhsminor.yy33-overwrites-yymsp[-6].minor.yy394*/
+ yylhsminor.yy427 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy132,yymsp[-2].minor.yy555,yymsp[-6].minor.yy144,yymsp[-1].minor.yy122,yymsp[-7].minor.yy168,yymsp[0].minor.yy168);/*yylhsminor.yy427-overwrites-yymsp[-6].minor.yy144*/
}
- yymsp[-7].minor.yy33 = yylhsminor.yy33;
+ yymsp[-7].minor.yy427 = yylhsminor.yy427;
break;
- case 275: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
-{yylhsminor.yy33 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy528, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy522);}
- yymsp[-5].minor.yy33 = yylhsminor.yy33;
+ case 277: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
+{yylhsminor.yy427 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy454, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy168);}
+ yymsp[-5].minor.yy427 = yylhsminor.yy427;
break;
- case 276: /* trigger_cmd ::= scanpt select scanpt */
-{yylhsminor.yy33 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy47, yymsp[-2].minor.yy522, yymsp[0].minor.yy522); /*yylhsminor.yy33-overwrites-yymsp[-1].minor.yy47*/}
- yymsp[-2].minor.yy33 = yylhsminor.yy33;
+ case 278: /* trigger_cmd ::= scanpt select scanpt */
+{yylhsminor.yy427 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy555, yymsp[-2].minor.yy168, yymsp[0].minor.yy168); /*yylhsminor.yy427-overwrites-yymsp[-1].minor.yy555*/}
+ yymsp[-2].minor.yy427 = yylhsminor.yy427;
break;
- case 277: /* expr ::= RAISE LP IGNORE RP */
+ case 279: /* expr ::= RAISE LP IGNORE RP */
{
- yymsp[-3].minor.yy528 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
- if( yymsp[-3].minor.yy528 ){
- yymsp[-3].minor.yy528->affExpr = OE_Ignore;
+ yymsp[-3].minor.yy454 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
+ if( yymsp[-3].minor.yy454 ){
+ yymsp[-3].minor.yy454->affExpr = OE_Ignore;
}
}
break;
- case 278: /* expr ::= RAISE LP raisetype COMMA nm RP */
+ case 280: /* expr ::= RAISE LP raisetype COMMA nm RP */
{
- yymsp[-5].minor.yy528 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1);
- if( yymsp[-5].minor.yy528 ) {
- yymsp[-5].minor.yy528->affExpr = (char)yymsp[-3].minor.yy394;
+ yymsp[-5].minor.yy454 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1);
+ if( yymsp[-5].minor.yy454 ) {
+ yymsp[-5].minor.yy454->affExpr = (char)yymsp[-3].minor.yy144;
}
}
break;
- case 279: /* raisetype ::= ROLLBACK */
-{yymsp[0].minor.yy394 = OE_Rollback;}
+ case 281: /* raisetype ::= ROLLBACK */
+{yymsp[0].minor.yy144 = OE_Rollback;}
break;
- case 281: /* raisetype ::= FAIL */
-{yymsp[0].minor.yy394 = OE_Fail;}
+ case 283: /* raisetype ::= FAIL */
+{yymsp[0].minor.yy144 = OE_Fail;}
break;
- case 282: /* cmd ::= DROP TRIGGER ifexists fullname */
+ case 284: /* cmd ::= DROP TRIGGER ifexists fullname */
{
- sqlite3DropTrigger(pParse,yymsp[0].minor.yy131,yymsp[-1].minor.yy394);
+ sqlite3DropTrigger(pParse,yymsp[0].minor.yy203,yymsp[-1].minor.yy144);
}
break;
- case 283: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ case 285: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
{
- sqlite3Attach(pParse, yymsp[-3].minor.yy528, yymsp[-1].minor.yy528, yymsp[0].minor.yy528);
+ sqlite3Attach(pParse, yymsp[-3].minor.yy454, yymsp[-1].minor.yy454, yymsp[0].minor.yy454);
}
break;
- case 284: /* cmd ::= DETACH database_kw_opt expr */
+ case 286: /* cmd ::= DETACH database_kw_opt expr */
{
- sqlite3Detach(pParse, yymsp[0].minor.yy528);
+ sqlite3Detach(pParse, yymsp[0].minor.yy454);
}
break;
- case 287: /* cmd ::= REINDEX */
+ case 289: /* cmd ::= REINDEX */
{sqlite3Reindex(pParse, 0, 0);}
break;
- case 288: /* cmd ::= REINDEX nm dbnm */
+ case 290: /* cmd ::= REINDEX nm dbnm */
{sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
- case 289: /* cmd ::= ANALYZE */
+ case 291: /* cmd ::= ANALYZE */
{sqlite3Analyze(pParse, 0, 0);}
break;
- case 290: /* cmd ::= ANALYZE nm dbnm */
+ case 292: /* cmd ::= ANALYZE nm dbnm */
{sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
- case 291: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
+ case 293: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
{
- sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy131,&yymsp[0].minor.yy0);
+ sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy203,&yymsp[0].minor.yy0);
}
break;
- case 292: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ case 294: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
{
yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n;
sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0);
}
break;
- case 293: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
+ case 295: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
{
- sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy131, &yymsp[0].minor.yy0);
+ sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy203, &yymsp[0].minor.yy0);
}
break;
- case 294: /* add_column_fullname ::= fullname */
+ case 296: /* add_column_fullname ::= fullname */
{
disableLookaside(pParse);
- sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy131);
+ sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy203);
}
break;
- case 295: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+ case 297: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
{
- sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy131, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
+ sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy203, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
}
break;
- case 296: /* cmd ::= create_vtab */
+ case 298: /* cmd ::= create_vtab */
{sqlite3VtabFinishParse(pParse,0);}
break;
- case 297: /* cmd ::= create_vtab LP vtabarglist RP */
+ case 299: /* cmd ::= create_vtab LP vtabarglist RP */
{sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);}
break;
- case 298: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ case 300: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
{
- sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy394);
+ sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy144);
}
break;
- case 299: /* vtabarg ::= */
+ case 301: /* vtabarg ::= */
{sqlite3VtabArgInit(pParse);}
break;
- case 300: /* vtabargtoken ::= ANY */
- case 301: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==301);
- case 302: /* lp ::= LP */ yytestcase(yyruleno==302);
+ case 302: /* vtabargtoken ::= ANY */
+ case 303: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==303);
+ case 304: /* lp ::= LP */ yytestcase(yyruleno==304);
{sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);}
break;
- case 303: /* with ::= WITH wqlist */
- case 304: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==304);
-{ sqlite3WithPush(pParse, yymsp[0].minor.yy521, 1); }
+ case 305: /* with ::= WITH wqlist */
+ case 306: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==306);
+{ sqlite3WithPush(pParse, yymsp[0].minor.yy59, 1); }
break;
- case 305: /* wqas ::= AS */
-{yymsp[0].minor.yy516 = M10d_Any;}
+ case 307: /* wqas ::= AS */
+{yymsp[0].minor.yy462 = M10d_Any;}
break;
- case 306: /* wqas ::= AS MATERIALIZED */
-{yymsp[-1].minor.yy516 = M10d_Yes;}
+ case 308: /* wqas ::= AS MATERIALIZED */
+{yymsp[-1].minor.yy462 = M10d_Yes;}
break;
- case 307: /* wqas ::= AS NOT MATERIALIZED */
-{yymsp[-2].minor.yy516 = M10d_No;}
+ case 309: /* wqas ::= AS NOT MATERIALIZED */
+{yymsp[-2].minor.yy462 = M10d_No;}
break;
- case 308: /* wqitem ::= nm eidlist_opt wqas LP select RP */
+ case 310: /* wqitem ::= withnm eidlist_opt wqas LP select RP */
{
- yymsp[-5].minor.yy385 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy322, yymsp[-1].minor.yy47, yymsp[-3].minor.yy516); /*A-overwrites-X*/
+ yymsp[-5].minor.yy67 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy14, yymsp[-1].minor.yy555, yymsp[-3].minor.yy462); /*A-overwrites-X*/
}
break;
- case 309: /* wqlist ::= wqitem */
+ case 311: /* withnm ::= nm */
+{pParse->bHasWith = 1;}
+ break;
+ case 312: /* wqlist ::= wqitem */
{
- yymsp[0].minor.yy521 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy385); /*A-overwrites-X*/
+ yymsp[0].minor.yy59 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy67); /*A-overwrites-X*/
}
break;
- case 310: /* wqlist ::= wqlist COMMA wqitem */
+ case 313: /* wqlist ::= wqlist COMMA wqitem */
{
- yymsp[-2].minor.yy521 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy521, yymsp[0].minor.yy385);
+ yymsp[-2].minor.yy59 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy59, yymsp[0].minor.yy67);
}
break;
- case 311: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
+ case 314: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
{
- assert( yymsp[0].minor.yy41!=0 );
- sqlite3WindowChain(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy41);
- yymsp[0].minor.yy41->pNextWin = yymsp[-2].minor.yy41;
- yylhsminor.yy41 = yymsp[0].minor.yy41;
+ assert( yymsp[0].minor.yy211!=0 );
+ sqlite3WindowChain(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy211);
+ yymsp[0].minor.yy211->pNextWin = yymsp[-2].minor.yy211;
+ yylhsminor.yy211 = yymsp[0].minor.yy211;
}
- yymsp[-2].minor.yy41 = yylhsminor.yy41;
+ yymsp[-2].minor.yy211 = yylhsminor.yy211;
break;
- case 312: /* windowdefn ::= nm AS LP window RP */
+ case 315: /* windowdefn ::= nm AS LP window RP */
{
- if( ALWAYS(yymsp[-1].minor.yy41) ){
- yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n);
+ if( ALWAYS(yymsp[-1].minor.yy211) ){
+ yymsp[-1].minor.yy211->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n);
}
- yylhsminor.yy41 = yymsp[-1].minor.yy41;
+ yylhsminor.yy211 = yymsp[-1].minor.yy211;
}
- yymsp[-4].minor.yy41 = yylhsminor.yy41;
+ yymsp[-4].minor.yy211 = yylhsminor.yy211;
break;
- case 313: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+ case 316: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
{
- yymsp[-4].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, 0);
+ yymsp[-4].minor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy14, yymsp[-1].minor.yy14, 0);
}
break;
- case 314: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+ case 317: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
{
- yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, &yymsp[-5].minor.yy0);
+ yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy14, yymsp[-1].minor.yy14, &yymsp[-5].minor.yy0);
}
- yymsp[-5].minor.yy41 = yylhsminor.yy41;
+ yymsp[-5].minor.yy211 = yylhsminor.yy211;
break;
- case 315: /* window ::= ORDER BY sortlist frame_opt */
+ case 318: /* window ::= ORDER BY sortlist frame_opt */
{
- yymsp[-3].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, 0);
+ yymsp[-3].minor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, yymsp[-1].minor.yy14, 0);
}
break;
- case 316: /* window ::= nm ORDER BY sortlist frame_opt */
+ case 319: /* window ::= nm ORDER BY sortlist frame_opt */
{
- yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0);
+ yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, yymsp[-1].minor.yy14, &yymsp[-4].minor.yy0);
}
- yymsp[-4].minor.yy41 = yylhsminor.yy41;
+ yymsp[-4].minor.yy211 = yylhsminor.yy211;
break;
- case 317: /* window ::= nm frame_opt */
+ case 320: /* window ::= nm frame_opt */
{
- yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, 0, &yymsp[-1].minor.yy0);
+ yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, 0, &yymsp[-1].minor.yy0);
}
- yymsp[-1].minor.yy41 = yylhsminor.yy41;
+ yymsp[-1].minor.yy211 = yylhsminor.yy211;
break;
- case 318: /* frame_opt ::= */
+ case 321: /* frame_opt ::= */
{
- yymsp[1].minor.yy41 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0);
+ yymsp[1].minor.yy211 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0);
}
break;
- case 319: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+ case 322: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
{
- yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy394, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy516);
+ yylhsminor.yy211 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy144, yymsp[-1].minor.yy509.eType, yymsp[-1].minor.yy509.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy462);
}
- yymsp[-2].minor.yy41 = yylhsminor.yy41;
+ yymsp[-2].minor.yy211 = yylhsminor.yy211;
break;
- case 320: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+ case 323: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
{
- yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy394, yymsp[-3].minor.yy595.eType, yymsp[-3].minor.yy595.pExpr, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, yymsp[0].minor.yy516);
+ yylhsminor.yy211 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy144, yymsp[-3].minor.yy509.eType, yymsp[-3].minor.yy509.pExpr, yymsp[-1].minor.yy509.eType, yymsp[-1].minor.yy509.pExpr, yymsp[0].minor.yy462);
}
- yymsp[-5].minor.yy41 = yylhsminor.yy41;
+ yymsp[-5].minor.yy211 = yylhsminor.yy211;
break;
- case 322: /* frame_bound_s ::= frame_bound */
- case 324: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==324);
-{yylhsminor.yy595 = yymsp[0].minor.yy595;}
- yymsp[0].minor.yy595 = yylhsminor.yy595;
+ case 325: /* frame_bound_s ::= frame_bound */
+ case 327: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==327);
+{yylhsminor.yy509 = yymsp[0].minor.yy509;}
+ yymsp[0].minor.yy509 = yylhsminor.yy509;
break;
- case 323: /* frame_bound_s ::= UNBOUNDED PRECEDING */
- case 325: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==325);
- case 327: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==327);
-{yylhsminor.yy595.eType = yymsp[-1].major; yylhsminor.yy595.pExpr = 0;}
- yymsp[-1].minor.yy595 = yylhsminor.yy595;
+ case 326: /* frame_bound_s ::= UNBOUNDED PRECEDING */
+ case 328: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==328);
+ case 330: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==330);
+{yylhsminor.yy509.eType = yymsp[-1].major; yylhsminor.yy509.pExpr = 0;}
+ yymsp[-1].minor.yy509 = yylhsminor.yy509;
break;
- case 326: /* frame_bound ::= expr PRECEDING|FOLLOWING */
-{yylhsminor.yy595.eType = yymsp[0].major; yylhsminor.yy595.pExpr = yymsp[-1].minor.yy528;}
- yymsp[-1].minor.yy595 = yylhsminor.yy595;
+ case 329: /* frame_bound ::= expr PRECEDING|FOLLOWING */
+{yylhsminor.yy509.eType = yymsp[0].major; yylhsminor.yy509.pExpr = yymsp[-1].minor.yy454;}
+ yymsp[-1].minor.yy509 = yylhsminor.yy509;
break;
- case 328: /* frame_exclude_opt ::= */
-{yymsp[1].minor.yy516 = 0;}
+ case 331: /* frame_exclude_opt ::= */
+{yymsp[1].minor.yy462 = 0;}
break;
- case 329: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
-{yymsp[-1].minor.yy516 = yymsp[0].minor.yy516;}
+ case 332: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
+{yymsp[-1].minor.yy462 = yymsp[0].minor.yy462;}
break;
- case 330: /* frame_exclude ::= NO OTHERS */
- case 331: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==331);
-{yymsp[-1].minor.yy516 = yymsp[-1].major; /*A-overwrites-X*/}
+ case 333: /* frame_exclude ::= NO OTHERS */
+ case 334: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==334);
+{yymsp[-1].minor.yy462 = yymsp[-1].major; /*A-overwrites-X*/}
break;
- case 332: /* frame_exclude ::= GROUP|TIES */
-{yymsp[0].minor.yy516 = yymsp[0].major; /*A-overwrites-X*/}
+ case 335: /* frame_exclude ::= GROUP|TIES */
+{yymsp[0].minor.yy462 = yymsp[0].major; /*A-overwrites-X*/}
break;
- case 333: /* window_clause ::= WINDOW windowdefn_list */
-{ yymsp[-1].minor.yy41 = yymsp[0].minor.yy41; }
+ case 336: /* window_clause ::= WINDOW windowdefn_list */
+{ yymsp[-1].minor.yy211 = yymsp[0].minor.yy211; }
break;
- case 334: /* filter_over ::= filter_clause over_clause */
+ case 337: /* filter_over ::= filter_clause over_clause */
{
- if( yymsp[0].minor.yy41 ){
- yymsp[0].minor.yy41->pFilter = yymsp[-1].minor.yy528;
+ if( yymsp[0].minor.yy211 ){
+ yymsp[0].minor.yy211->pFilter = yymsp[-1].minor.yy454;
}else{
- sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528);
+ sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy454);
}
- yylhsminor.yy41 = yymsp[0].minor.yy41;
+ yylhsminor.yy211 = yymsp[0].minor.yy211;
}
- yymsp[-1].minor.yy41 = yylhsminor.yy41;
+ yymsp[-1].minor.yy211 = yylhsminor.yy211;
break;
- case 335: /* filter_over ::= over_clause */
+ case 338: /* filter_over ::= over_clause */
{
- yylhsminor.yy41 = yymsp[0].minor.yy41;
+ yylhsminor.yy211 = yymsp[0].minor.yy211;
}
- yymsp[0].minor.yy41 = yylhsminor.yy41;
+ yymsp[0].minor.yy211 = yylhsminor.yy211;
break;
- case 336: /* filter_over ::= filter_clause */
+ case 339: /* filter_over ::= filter_clause */
{
- yylhsminor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
- if( yylhsminor.yy41 ){
- yylhsminor.yy41->eFrmType = TK_FILTER;
- yylhsminor.yy41->pFilter = yymsp[0].minor.yy528;
+ yylhsminor.yy211 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
+ if( yylhsminor.yy211 ){
+ yylhsminor.yy211->eFrmType = TK_FILTER;
+ yylhsminor.yy211->pFilter = yymsp[0].minor.yy454;
}else{
- sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy528);
+ sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy454);
}
}
- yymsp[0].minor.yy41 = yylhsminor.yy41;
+ yymsp[0].minor.yy211 = yylhsminor.yy211;
break;
- case 337: /* over_clause ::= OVER LP window RP */
+ case 340: /* over_clause ::= OVER LP window RP */
{
- yymsp[-3].minor.yy41 = yymsp[-1].minor.yy41;
- assert( yymsp[-3].minor.yy41!=0 );
+ yymsp[-3].minor.yy211 = yymsp[-1].minor.yy211;
+ assert( yymsp[-3].minor.yy211!=0 );
}
break;
- case 338: /* over_clause ::= OVER nm */
+ case 341: /* over_clause ::= OVER nm */
{
- yymsp[-1].minor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
- if( yymsp[-1].minor.yy41 ){
- yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
+ yymsp[-1].minor.yy211 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
+ if( yymsp[-1].minor.yy211 ){
+ yymsp[-1].minor.yy211->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
}
}
break;
- case 339: /* filter_clause ::= FILTER LP WHERE expr RP */
-{ yymsp[-4].minor.yy528 = yymsp[-1].minor.yy528; }
+ case 342: /* filter_clause ::= FILTER LP WHERE expr RP */
+{ yymsp[-4].minor.yy454 = yymsp[-1].minor.yy454; }
+ break;
+ case 343: /* term ::= QNUMBER */
+{
+ yylhsminor.yy454=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0);
+ sqlite3DequoteNumber(pParse, yylhsminor.yy454);
+}
+ yymsp[0].minor.yy454 = yylhsminor.yy454;
break;
default:
- /* (340) input ::= cmdlist */ yytestcase(yyruleno==340);
- /* (341) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==341);
- /* (342) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=342);
- /* (343) ecmd ::= SEMI */ yytestcase(yyruleno==343);
- /* (344) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==344);
- /* (345) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=345);
- /* (346) trans_opt ::= */ yytestcase(yyruleno==346);
- /* (347) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==347);
- /* (348) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==348);
- /* (349) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==349);
- /* (350) savepoint_opt ::= */ yytestcase(yyruleno==350);
- /* (351) cmd ::= create_table create_table_args */ yytestcase(yyruleno==351);
- /* (352) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=352);
- /* (353) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==353);
- /* (354) columnlist ::= columnname carglist */ yytestcase(yyruleno==354);
- /* (355) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==355);
- /* (356) nm ::= STRING */ yytestcase(yyruleno==356);
- /* (357) typetoken ::= typename */ yytestcase(yyruleno==357);
- /* (358) typename ::= ID|STRING */ yytestcase(yyruleno==358);
- /* (359) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=359);
- /* (360) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=360);
- /* (361) carglist ::= carglist ccons */ yytestcase(yyruleno==361);
- /* (362) carglist ::= */ yytestcase(yyruleno==362);
- /* (363) ccons ::= NULL onconf */ yytestcase(yyruleno==363);
- /* (364) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==364);
- /* (365) ccons ::= AS generated */ yytestcase(yyruleno==365);
- /* (366) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==366);
- /* (367) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==367);
- /* (368) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=368);
- /* (369) tconscomma ::= */ yytestcase(yyruleno==369);
- /* (370) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=370);
- /* (371) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=371);
- /* (372) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=372);
- /* (373) oneselect ::= values */ yytestcase(yyruleno==373);
- /* (374) sclp ::= selcollist COMMA */ yytestcase(yyruleno==374);
- /* (375) as ::= ID|STRING */ yytestcase(yyruleno==375);
- /* (376) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=376);
- /* (377) returning ::= */ yytestcase(yyruleno==377);
- /* (378) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=378);
- /* (379) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==379);
- /* (380) case_operand ::= expr */ yytestcase(yyruleno==380);
- /* (381) exprlist ::= nexprlist */ yytestcase(yyruleno==381);
- /* (382) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=382);
- /* (383) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=383);
- /* (384) nmnum ::= ON */ yytestcase(yyruleno==384);
- /* (385) nmnum ::= DELETE */ yytestcase(yyruleno==385);
- /* (386) nmnum ::= DEFAULT */ yytestcase(yyruleno==386);
- /* (387) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==387);
- /* (388) foreach_clause ::= */ yytestcase(yyruleno==388);
- /* (389) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==389);
- /* (390) trnm ::= nm */ yytestcase(yyruleno==390);
- /* (391) tridxby ::= */ yytestcase(yyruleno==391);
- /* (392) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==392);
- /* (393) database_kw_opt ::= */ yytestcase(yyruleno==393);
- /* (394) kwcolumn_opt ::= */ yytestcase(yyruleno==394);
- /* (395) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==395);
- /* (396) vtabarglist ::= vtabarg */ yytestcase(yyruleno==396);
- /* (397) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==397);
- /* (398) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==398);
- /* (399) anylist ::= */ yytestcase(yyruleno==399);
- /* (400) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==400);
- /* (401) anylist ::= anylist ANY */ yytestcase(yyruleno==401);
- /* (402) with ::= */ yytestcase(yyruleno==402);
- /* (403) windowdefn_list ::= windowdefn (OPTIMIZED OUT) */ assert(yyruleno!=403);
- /* (404) window ::= frame_opt (OPTIMIZED OUT) */ assert(yyruleno!=404);
+ /* (344) input ::= cmdlist */ yytestcase(yyruleno==344);
+ /* (345) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==345);
+ /* (346) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=346);
+ /* (347) ecmd ::= SEMI */ yytestcase(yyruleno==347);
+ /* (348) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==348);
+ /* (349) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=349);
+ /* (350) trans_opt ::= */ yytestcase(yyruleno==350);
+ /* (351) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==351);
+ /* (352) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==352);
+ /* (353) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==353);
+ /* (354) savepoint_opt ::= */ yytestcase(yyruleno==354);
+ /* (355) cmd ::= create_table create_table_args */ yytestcase(yyruleno==355);
+ /* (356) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=356);
+ /* (357) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==357);
+ /* (358) columnlist ::= columnname carglist */ yytestcase(yyruleno==358);
+ /* (359) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==359);
+ /* (360) nm ::= STRING */ yytestcase(yyruleno==360);
+ /* (361) typetoken ::= typename */ yytestcase(yyruleno==361);
+ /* (362) typename ::= ID|STRING */ yytestcase(yyruleno==362);
+ /* (363) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=363);
+ /* (364) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=364);
+ /* (365) carglist ::= carglist ccons */ yytestcase(yyruleno==365);
+ /* (366) carglist ::= */ yytestcase(yyruleno==366);
+ /* (367) ccons ::= NULL onconf */ yytestcase(yyruleno==367);
+ /* (368) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==368);
+ /* (369) ccons ::= AS generated */ yytestcase(yyruleno==369);
+ /* (370) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==370);
+ /* (371) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==371);
+ /* (372) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=372);
+ /* (373) tconscomma ::= */ yytestcase(yyruleno==373);
+ /* (374) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=374);
+ /* (375) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=375);
+ /* (376) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=376);
+ /* (377) oneselect ::= values */ yytestcase(yyruleno==377);
+ /* (378) sclp ::= selcollist COMMA */ yytestcase(yyruleno==378);
+ /* (379) as ::= ID|STRING */ yytestcase(yyruleno==379);
+ /* (380) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=380);
+ /* (381) returning ::= */ yytestcase(yyruleno==381);
+ /* (382) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=382);
+ /* (383) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==383);
+ /* (384) case_operand ::= expr */ yytestcase(yyruleno==384);
+ /* (385) exprlist ::= nexprlist */ yytestcase(yyruleno==385);
+ /* (386) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=386);
+ /* (387) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=387);
+ /* (388) nmnum ::= ON */ yytestcase(yyruleno==388);
+ /* (389) nmnum ::= DELETE */ yytestcase(yyruleno==389);
+ /* (390) nmnum ::= DEFAULT */ yytestcase(yyruleno==390);
+ /* (391) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==391);
+ /* (392) foreach_clause ::= */ yytestcase(yyruleno==392);
+ /* (393) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==393);
+ /* (394) trnm ::= nm */ yytestcase(yyruleno==394);
+ /* (395) tridxby ::= */ yytestcase(yyruleno==395);
+ /* (396) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==396);
+ /* (397) database_kw_opt ::= */ yytestcase(yyruleno==397);
+ /* (398) kwcolumn_opt ::= */ yytestcase(yyruleno==398);
+ /* (399) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==399);
+ /* (400) vtabarglist ::= vtabarg */ yytestcase(yyruleno==400);
+ /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==401);
+ /* (402) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==402);
+ /* (403) anylist ::= */ yytestcase(yyruleno==403);
+ /* (404) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==404);
+ /* (405) anylist ::= anylist ANY */ yytestcase(yyruleno==405);
+ /* (406) with ::= */ yytestcase(yyruleno==406);
+ /* (407) windowdefn_list ::= windowdefn (OPTIMIZED OUT) */ assert(yyruleno!=407);
+ /* (408) window ::= frame_opt (OPTIMIZED OUT) */ assert(yyruleno!=408);
break;
/********** End reduce actions ************************************************/
};
@@ -176430,19 +177848,12 @@ SQLITE_PRIVATE void sqlite3Parser(
(int)(yypParser->yytos - yypParser->yystack));
}
#endif
-#if YYSTACKDEPTH>0
if( yypParser->yytos>=yypParser->yystackEnd ){
- yyStackOverflow(yypParser);
- break;
- }
-#else
- if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){
if( yyGrowStack(yypParser) ){
yyStackOverflow(yypParser);
break;
}
}
-#endif
}
yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor sqlite3ParserCTX_PARAM);
}else if( yyact <= YY_MAX_SHIFTREDUCE ){
@@ -177513,27 +178924,58 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
*tokenType = TK_INTEGER;
#ifndef SQLITE_OMIT_HEX_INTEGER
if( z[0]=='0' && (z[1]=='x' || z[1]=='X') && sqlite3Isxdigit(z[2]) ){
- for(i=3; sqlite3Isxdigit(z[i]); i++){}
- return i;
- }
+ for(i=3; 1; i++){
+ if( sqlite3Isxdigit(z[i])==0 ){
+ if( z[i]==SQLITE_DIGIT_SEPARATOR ){
+ *tokenType = TK_QNUMBER;
+ }else{
+ break;
+ }
+ }
+ }
+ }else
#endif
- for(i=0; sqlite3Isdigit(z[i]); i++){}
+ {
+ for(i=0; 1; i++){
+ if( sqlite3Isdigit(z[i])==0 ){
+ if( z[i]==SQLITE_DIGIT_SEPARATOR ){
+ *tokenType = TK_QNUMBER;
+ }else{
+ break;
+ }
+ }
+ }
#ifndef SQLITE_OMIT_FLOATING_POINT
- if( z[i]=='.' ){
- i++;
- while( sqlite3Isdigit(z[i]) ){ i++; }
- *tokenType = TK_FLOAT;
- }
- if( (z[i]=='e' || z[i]=='E') &&
- ( sqlite3Isdigit(z[i+1])
- || ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2]))
- )
- ){
- i += 2;
- while( sqlite3Isdigit(z[i]) ){ i++; }
- *tokenType = TK_FLOAT;
- }
+ if( z[i]=='.' ){
+ if( *tokenType==TK_INTEGER ) *tokenType = TK_FLOAT;
+ for(i++; 1; i++){
+ if( sqlite3Isdigit(z[i])==0 ){
+ if( z[i]==SQLITE_DIGIT_SEPARATOR ){
+ *tokenType = TK_QNUMBER;
+ }else{
+ break;
+ }
+ }
+ }
+ }
+ if( (z[i]=='e' || z[i]=='E') &&
+ ( sqlite3Isdigit(z[i+1])
+ || ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2]))
+ )
+ ){
+ if( *tokenType==TK_INTEGER ) *tokenType = TK_FLOAT;
+ for(i+=2; 1; i++){
+ if( sqlite3Isdigit(z[i])==0 ){
+ if( z[i]==SQLITE_DIGIT_SEPARATOR ){
+ *tokenType = TK_QNUMBER;
+ }else{
+ break;
+ }
+ }
+ }
+ }
#endif
+ }
while( IdChar(z[i]) ){
*tokenType = TK_ILLEGAL;
i++;
@@ -177698,10 +179140,13 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql){
if( tokenType>=TK_WINDOW ){
assert( tokenType==TK_SPACE || tokenType==TK_OVER || tokenType==TK_FILTER
|| tokenType==TK_ILLEGAL || tokenType==TK_WINDOW
+ || tokenType==TK_QNUMBER
);
#else
if( tokenType>=TK_SPACE ){
- assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL );
+ assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL
+ || tokenType==TK_QNUMBER
+ );
#endif /* SQLITE_OMIT_WINDOWFUNC */
if( AtomicLoad(&db->u1.isInterrupted) ){
pParse->rc = SQLITE_INTERRUPT;
@@ -177734,7 +179179,7 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql){
assert( n==6 );
tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed);
#endif /* SQLITE_OMIT_WINDOWFUNC */
- }else{
+ }else if( tokenType!=TK_QNUMBER ){
Token x;
x.z = zSql;
x.n = n;
@@ -188729,22 +190174,24 @@ static int fts3IntegrityMethod(
char **pzErr /* Write error message here */
){
Fts3Table *p = (Fts3Table*)pVtab;
- int rc;
+ int rc = SQLITE_OK;
int bOk = 0;
UNUSED_PARAMETER(isQuick);
rc = sqlite3Fts3IntegrityCheck(p, &bOk);
- assert( rc!=SQLITE_CORRUPT_VTAB || bOk==0 );
- if( rc!=SQLITE_OK && rc!=SQLITE_CORRUPT_VTAB ){
+ assert( rc!=SQLITE_CORRUPT_VTAB );
+ if( rc==SQLITE_ERROR || (rc&0xFF)==SQLITE_CORRUPT ){
*pzErr = sqlite3_mprintf("unable to validate the inverted index for"
" FTS%d table %s.%s: %s",
p->bFts4 ? 4 : 3, zSchema, zTabname, sqlite3_errstr(rc));
- }else if( bOk==0 ){
+ if( *pzErr ) rc = SQLITE_OK;
+ }else if( rc==SQLITE_OK && bOk==0 ){
*pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s",
p->bFts4 ? 4 : 3, zSchema, zTabname);
+ if( *pzErr==0 ) rc = SQLITE_NOMEM;
}
sqlite3Fts3SegmentsClose(p);
- return SQLITE_OK;
+ return rc;
}
@@ -200406,7 +201853,12 @@ SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){
sqlite3_finalize(pStmt);
}
- *pbOk = (rc==SQLITE_OK && cksum1==cksum2);
+ if( rc==SQLITE_CORRUPT_VTAB ){
+ rc = SQLITE_OK;
+ *pbOk = 0;
+ }else{
+ *pbOk = (rc==SQLITE_OK && cksum1==cksum2);
+ }
return rc;
}
@@ -201312,7 +202764,7 @@ static void fts3SnippetDetails(
}
mCover |= mPhrase;
- for(j=0; j<pPhrase->nToken; j++){
+ for(j=0; j<pPhrase->nToken && j<pIter->nSnippet; j++){
mHighlight |= (mPos>>j);
}
@@ -203973,7 +205425,6 @@ static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){
}
}
-
/* Append formatted text (not to exceed N bytes) to the JsonString.
*/
static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){
@@ -204031,6 +205482,40 @@ static void jsonAppendSeparator(JsonString *p){
jsonAppendChar(p, ',');
}
+/* c is a control character. Append the canonical JSON representation
+** of that control character to p.
+**
+** This routine assumes that the output buffer has already been enlarged
+** sufficiently to hold the worst-case encoding plus a nul terminator.
+*/
+static void jsonAppendControlChar(JsonString *p, u8 c){
+ static const char aSpecial[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ assert( sizeof(aSpecial)==32 );
+ assert( aSpecial['\b']=='b' );
+ assert( aSpecial['\f']=='f' );
+ assert( aSpecial['\n']=='n' );
+ assert( aSpecial['\r']=='r' );
+ assert( aSpecial['\t']=='t' );
+ assert( c>=0 && c<sizeof(aSpecial) );
+ assert( p->nUsed+7 <= p->nAlloc );
+ if( aSpecial[c] ){
+ p->zBuf[p->nUsed] = '\\';
+ p->zBuf[p->nUsed+1] = aSpecial[c];
+ p->nUsed += 2;
+ }else{
+ p->zBuf[p->nUsed] = '\\';
+ p->zBuf[p->nUsed+1] = 'u';
+ p->zBuf[p->nUsed+2] = '0';
+ p->zBuf[p->nUsed+3] = '0';
+ p->zBuf[p->nUsed+4] = "0123456789abcdef"[c>>4];
+ p->zBuf[p->nUsed+5] = "0123456789abcdef"[c&0xf];
+ p->nUsed += 6;
+ }
+}
+
/* Append the N-byte string in zIn to the end of the JsonString string
** under construction. Enclose the string in double-quotes ("...") and
** escape any double-quotes or backslash characters contained within the
@@ -204090,35 +205575,14 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
}
c = z[0];
if( c=='"' || c=='\\' ){
- json_simple_escape:
if( (p->nUsed+N+3 > p->nAlloc) && jsonStringGrow(p,N+3)!=0 ) return;
p->zBuf[p->nUsed++] = '\\';
p->zBuf[p->nUsed++] = c;
}else if( c=='\'' ){
p->zBuf[p->nUsed++] = c;
}else{
- static const char aSpecial[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- };
- assert( sizeof(aSpecial)==32 );
- assert( aSpecial['\b']=='b' );
- assert( aSpecial['\f']=='f' );
- assert( aSpecial['\n']=='n' );
- assert( aSpecial['\r']=='r' );
- assert( aSpecial['\t']=='t' );
- assert( c>=0 && c<sizeof(aSpecial) );
- if( aSpecial[c] ){
- c = aSpecial[c];
- goto json_simple_escape;
- }
if( (p->nUsed+N+7 > p->nAlloc) && jsonStringGrow(p,N+7)!=0 ) return;
- p->zBuf[p->nUsed++] = '\\';
- p->zBuf[p->nUsed++] = 'u';
- p->zBuf[p->nUsed++] = '0';
- p->zBuf[p->nUsed++] = '0';
- p->zBuf[p->nUsed++] = "0123456789abcdef"[c>>4];
- p->zBuf[p->nUsed++] = "0123456789abcdef"[c&0xf];
+ jsonAppendControlChar(p, c);
}
z++;
N--;
@@ -204819,7 +206283,10 @@ static u32 jsonbValidityCheck(
if( !jsonIsOk[z[j]] && z[j]!='\'' ){
if( z[j]=='"' ){
if( x==JSONB_TEXTJ ) return j+1;
- }else if( z[j]!='\\' || j+1>=k ){
+ }else if( z[j]<=0x1f ){
+ /* Control characters in JSON5 string literals are ok */
+ if( x==JSONB_TEXTJ ) return j+1;
+ }else if( NEVER(z[j]!='\\') || j+1>=k ){
return j+1;
}else if( strchr("\"\\/bfnrt",z[j+1])!=0 ){
j++;
@@ -205114,9 +206581,14 @@ json_parse_restart:
return -1;
}
}else if( c<=0x1f ){
- /* Control characters are not allowed in strings */
- pParse->iErr = j;
- return -1;
+ if( c==0 ){
+ pParse->iErr = j;
+ return -1;
+ }
+ /* Control characters are not allowed in canonical JSON string
+ ** literals, but are allowed in JSON5 string literals. */
+ opcode = JSONB_TEXT5;
+ pParse->hasNonstd = 1;
}else if( c=='"' ){
opcode = JSONB_TEXT5;
}
@@ -205332,6 +206804,7 @@ json_parse_restart:
return i+4;
}
/* fall-through into the default case that checks for NaN */
+ /* no break */ deliberate_fall_through
}
default: {
u32 k;
@@ -205600,7 +207073,7 @@ static u32 jsonTranslateBlobToText(
zIn = (const char*)&pParse->aBlob[i+n];
jsonAppendChar(pOut, '"');
while( sz2>0 ){
- for(k=0; k<sz2 && zIn[k]!='\\' && zIn[k]!='"'; k++){}
+ for(k=0; k<sz2 && (jsonIsOk[(u8)zIn[k]] || zIn[k]=='\''); k++){}
if( k>0 ){
jsonAppendRawNZ(pOut, zIn, k);
if( k>=sz2 ){
@@ -205615,6 +207088,13 @@ static u32 jsonTranslateBlobToText(
sz2--;
continue;
}
+ if( zIn[0]<=0x1f ){
+ if( pOut->nUsed+7>pOut->nAlloc && jsonStringGrow(pOut,7) ) break;
+ jsonAppendControlChar(pOut, zIn[0]);
+ zIn++;
+ sz2--;
+ continue;
+ }
assert( zIn[0]=='\\' );
assert( sz2>=1 );
if( sz2<2 ){
@@ -205717,6 +207197,112 @@ static u32 jsonTranslateBlobToText(
return i+n+sz;
}
+/* Context for recursion of json_pretty()
+*/
+typedef struct JsonPretty JsonPretty;
+struct JsonPretty {
+ JsonParse *pParse; /* The BLOB being rendered */
+ JsonString *pOut; /* Generate pretty output into this string */
+ const char *zIndent; /* Use this text for indentation */
+ u32 szIndent; /* Bytes in zIndent[] */
+ u32 nIndent; /* Current level of indentation */
+};
+
+/* Append indentation to the pretty JSON under construction */
+static void jsonPrettyIndent(JsonPretty *pPretty){
+ u32 jj;
+ for(jj=0; jj<pPretty->nIndent; jj++){
+ jsonAppendRaw(pPretty->pOut, pPretty->zIndent, pPretty->szIndent);
+ }
+}
+
+/*
+** Translate the binary JSONB representation of JSON beginning at
+** pParse->aBlob[i] into a JSON text string. Append the JSON
+** text onto the end of pOut. Return the index in pParse->aBlob[]
+** of the first byte past the end of the element that is translated.
+**
+** This is a variant of jsonTranslateBlobToText() that "pretty-prints"
+** the output. Extra whitespace is inserted to make the JSON easier
+** for humans to read.
+**
+** If an error is detected in the BLOB input, the pOut->eErr flag
+** might get set to JSTRING_MALFORMED. But not all BLOB input errors
+** are detected. So a malformed JSONB input might either result
+** in an error, or in incorrect JSON.
+**
+** The pOut->eErr JSTRING_OOM flag is set on a OOM.
+*/
+static u32 jsonTranslateBlobToPrettyText(
+ JsonPretty *pPretty, /* Pretty-printing context */
+ u32 i /* Start rendering at this index */
+){
+ u32 sz, n, j, iEnd;
+ const JsonParse *pParse = pPretty->pParse;
+ JsonString *pOut = pPretty->pOut;
+ n = jsonbPayloadSize(pParse, i, &sz);
+ if( n==0 ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ return pParse->nBlob+1;
+ }
+ switch( pParse->aBlob[i] & 0x0f ){
+ case JSONB_ARRAY: {
+ j = i+n;
+ iEnd = j+sz;
+ jsonAppendChar(pOut, '[');
+ if( j<iEnd ){
+ jsonAppendChar(pOut, '\n');
+ pPretty->nIndent++;
+ while( pOut->eErr==0 ){
+ jsonPrettyIndent(pPretty);
+ j = jsonTranslateBlobToPrettyText(pPretty, j);
+ if( j>=iEnd ) break;
+ jsonAppendRawNZ(pOut, ",\n", 2);
+ }
+ jsonAppendChar(pOut, '\n');
+ pPretty->nIndent--;
+ jsonPrettyIndent(pPretty);
+ }
+ jsonAppendChar(pOut, ']');
+ i = iEnd;
+ break;
+ }
+ case JSONB_OBJECT: {
+ j = i+n;
+ iEnd = j+sz;
+ jsonAppendChar(pOut, '{');
+ if( j<iEnd ){
+ jsonAppendChar(pOut, '\n');
+ pPretty->nIndent++;
+ while( pOut->eErr==0 ){
+ jsonPrettyIndent(pPretty);
+ j = jsonTranslateBlobToText(pParse, j, pOut);
+ if( j>iEnd ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ break;
+ }
+ jsonAppendRawNZ(pOut, ": ", 2);
+ j = jsonTranslateBlobToPrettyText(pPretty, j);
+ if( j>=iEnd ) break;
+ jsonAppendRawNZ(pOut, ",\n", 2);
+ }
+ jsonAppendChar(pOut, '\n');
+ pPretty->nIndent--;
+ jsonPrettyIndent(pPretty);
+ }
+ jsonAppendChar(pOut, '}');
+ i = iEnd;
+ break;
+ }
+ default: {
+ i = jsonTranslateBlobToText(pParse, i, pOut);
+ break;
+ }
+ }
+ return i;
+}
+
+
/* Return true if the input pJson
**
** For performance reasons, this routine does not do a detailed check of the
@@ -206967,11 +208553,12 @@ static void jsonParseFunc(
if( p==0 ) return;
if( argc==1 ){
jsonDebugPrintBlob(p, 0, p->nBlob, 0, &out);
- sqlite3_result_text64(ctx, out.zText, out.nChar, SQLITE_DYNAMIC, SQLITE_UTF8);
+ sqlite3_result_text64(ctx,out.zText,out.nChar,SQLITE_TRANSIENT,SQLITE_UTF8);
}else{
jsonShowParse(p);
}
jsonParseFree(p);
+ sqlite3_str_reset(&out);
}
#endif /* SQLITE_DEBUG */
@@ -207070,13 +208657,6 @@ static void jsonArrayLengthFunc(
jsonParseFree(p);
}
-/* True if the string is all digits */
-static int jsonAllDigits(const char *z, int n){
- int i;
- for(i=0; i<n && sqlite3Isdigit(z[i]); i++){}
- return i==n;
-}
-
/* True if the string is all alphanumerics and underscores */
static int jsonAllAlphanum(const char *z, int n){
int i;
@@ -207141,7 +208721,7 @@ static void jsonExtractFunc(
** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience
*/
jsonStringInit(&jx, ctx);
- if( jsonAllDigits(zPath, nPath) ){
+ if( sqlite3_value_type(argv[i])==SQLITE_INTEGER ){
jsonAppendRawNZ(&jx, "[", 1);
jsonAppendRaw(&jx, zPath, nPath);
jsonAppendRawNZ(&jx, "]", 2);
@@ -207636,6 +209216,40 @@ json_type_done:
}
/*
+** json_pretty(JSON)
+** json_pretty(JSON, INDENT)
+**
+** Return text that is a pretty-printed rendering of the input JSON.
+** If the argument is not valid JSON, return NULL.
+**
+** The INDENT argument is text that is used for indentation. If omitted,
+** it defaults to four spaces (the same as PostgreSQL).
+*/
+static void jsonPrettyFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonString s; /* The output string */
+ JsonPretty x; /* Pretty printing context */
+
+ memset(&x, 0, sizeof(x));
+ x.pParse = jsonParseFuncArg(ctx, argv[0], 0);
+ if( x.pParse==0 ) return;
+ x.pOut = &s;
+ jsonStringInit(&s, ctx);
+ if( argc==1 || (x.zIndent = (const char*)sqlite3_value_text(argv[1]))==0 ){
+ x.zIndent = " ";
+ x.szIndent = 4;
+ }else{
+ x.szIndent = (u32)strlen(x.zIndent);
+ }
+ jsonTranslateBlobToPrettyText(&x, 0);
+ jsonReturnString(&s, 0, 0);
+ jsonParseFree(x.pParse);
+}
+
+/*
** json_valid(JSON)
** json_valid(JSON, FLAGS)
**
@@ -208649,6 +210263,8 @@ SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void){
JFUNCTION(jsonb_object, -1,0,1, 1,1,0, jsonObjectFunc),
JFUNCTION(json_patch, 2,1,1, 0,0,0, jsonPatchFunc),
JFUNCTION(jsonb_patch, 2,1,0, 0,1,0, jsonPatchFunc),
+ JFUNCTION(json_pretty, 1,1,0, 0,0,0, jsonPrettyFunc),
+ JFUNCTION(json_pretty, 2,1,0, 0,0,0, jsonPrettyFunc),
JFUNCTION(json_quote, 1,0,1, 1,0,0, jsonQuoteFunc),
JFUNCTION(json_remove, -1,1,1, 0,0,0, jsonRemoveFunc),
JFUNCTION(jsonb_remove, -1,1,0, 0,1,0, jsonRemoveFunc),
@@ -210548,6 +212164,8 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){
return SQLITE_OK;
}
+SQLITE_PRIVATE int sqlite3IntFloatCompare(i64,double);
+
/*
** Rtree virtual table module xFilter method.
*/
@@ -210577,7 +212195,8 @@ static int rtreeFilter(
i64 iNode = 0;
int eType = sqlite3_value_numeric_type(argv[0]);
if( eType==SQLITE_INTEGER
- || (eType==SQLITE_FLOAT && sqlite3_value_double(argv[0])==iRowid)
+ || (eType==SQLITE_FLOAT
+ && 0==sqlite3IntFloatCompare(iRowid,sqlite3_value_double(argv[0])))
){
rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode);
}else{
@@ -211932,6 +213551,7 @@ constraint:
*/
static int rtreeBeginTransaction(sqlite3_vtab *pVtab){
Rtree *pRtree = (Rtree *)pVtab;
+ assert( pRtree->inWrTrans==0 );
pRtree->inWrTrans = 1;
return SQLITE_OK;
}
@@ -215486,7 +217106,7 @@ static void icuLoadCollation(
UCollator *pUCollator; /* ICU library collation object */
int rc; /* Return code from sqlite3_create_collation_x() */
- assert(nArg==2);
+ assert(nArg==2 || nArg==3);
(void)nArg; /* Unused parameter */
zLocale = (const char *)sqlite3_value_text(apArg[0]);
zName = (const char *)sqlite3_value_text(apArg[1]);
@@ -215501,7 +217121,39 @@ static void icuLoadCollation(
return;
}
assert(p);
-
+ if(nArg==3){
+ const char *zOption = (const char*)sqlite3_value_text(apArg[2]);
+ static const struct {
+ const char *zName;
+ UColAttributeValue val;
+ } aStrength[] = {
+ { "PRIMARY", UCOL_PRIMARY },
+ { "SECONDARY", UCOL_SECONDARY },
+ { "TERTIARY", UCOL_TERTIARY },
+ { "DEFAULT", UCOL_DEFAULT_STRENGTH },
+ { "QUARTERNARY", UCOL_QUATERNARY },
+ { "IDENTICAL", UCOL_IDENTICAL },
+ };
+ unsigned int i;
+ for(i=0; i<sizeof(aStrength)/sizeof(aStrength[0]); i++){
+ if( sqlite3_stricmp(zOption,aStrength[i].zName)==0 ){
+ ucol_setStrength(pUCollator, aStrength[i].val);
+ break;
+ }
+ }
+ if( i>=sizeof(aStrength)/sizeof(aStrength[0]) ){
+ sqlite3_str *pStr = sqlite3_str_new(sqlite3_context_db_handle(p));
+ sqlite3_str_appendf(pStr,
+ "unknown collation strength \"%s\" - should be one of:",
+ zOption);
+ for(i=0; i<sizeof(aStrength)/sizeof(aStrength[0]); i++){
+ sqlite3_str_appendf(pStr, " %s", aStrength[i].zName);
+ }
+ sqlite3_result_error(p, sqlite3_str_value(pStr), -1);
+ sqlite3_free(sqlite3_str_finish(pStr));
+ return;
+ }
+ }
rc = sqlite3_create_collation_v2(db, zName, SQLITE_UTF16, (void *)pUCollator,
icuCollationColl, icuCollationDel
);
@@ -215524,6 +217176,7 @@ SQLITE_PRIVATE int sqlite3IcuInit(sqlite3 *db){
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
} scalars[] = {
{"icu_load_collation",2,SQLITE_UTF8|SQLITE_DIRECTONLY,1, icuLoadCollation},
+ {"icu_load_collation",3,SQLITE_UTF8|SQLITE_DIRECTONLY,1, icuLoadCollation},
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU)
{"regexp", 2, SQLITE_ANY|SQLITEICU_EXTRAFLAGS, 0, icuRegexpFunc},
{"lower", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
@@ -216674,6 +218327,7 @@ typedef unsigned int u32;
typedef unsigned short u16;
typedef unsigned char u8;
typedef sqlite3_int64 i64;
+typedef sqlite3_uint64 u64;
#endif
/*
@@ -217360,6 +219014,7 @@ static int rbuObjIterNext(sqlite3rbu *p, RbuObjIter *pIter){
if( rc!=SQLITE_ROW ){
rc = resetAndCollectError(pIter->pTblIter, &p->zErrmsg);
pIter->zTbl = 0;
+ pIter->zDataTbl = 0;
}else{
pIter->zTbl = (const char*)sqlite3_column_text(pIter->pTblIter, 0);
pIter->zDataTbl = (const char*)sqlite3_column_text(pIter->pTblIter,1);
@@ -219454,7 +221109,7 @@ static i64 rbuShmChecksum(sqlite3rbu *p){
u32 volatile *ptr;
p->rc = pDb->pMethods->xShmMap(pDb, 0, 32*1024, 0, (void volatile**)&ptr);
if( p->rc==SQLITE_OK ){
- iRet = ((i64)ptr[10] << 32) + ptr[11];
+ iRet = (i64)(((u64)ptr[10] << 32) + ptr[11]);
}
}
return iRet;
@@ -226925,14 +228580,14 @@ static int sessionChangesetNextOne(
p->rc = sessionInputBuffer(&p->in, 2);
if( p->rc!=SQLITE_OK ) return p->rc;
+ sessionDiscardData(&p->in);
+ p->in.iCurrent = p->in.iNext;
+
/* If the iterator is already at the end of the changeset, return DONE. */
if( p->in.iNext>=p->in.nData ){
return SQLITE_DONE;
}
- sessionDiscardData(&p->in);
- p->in.iCurrent = p->in.iNext;
-
op = p->in.aData[p->in.iNext++];
while( op=='T' || op=='P' ){
if( pbNew ) *pbNew = 1;
@@ -228667,6 +230322,7 @@ struct sqlite3_changegroup {
int rc; /* Error code */
int bPatch; /* True to accumulate patchsets */
SessionTable *pList; /* List of tables in current patch */
+ SessionBuffer rec;
sqlite3 *db; /* Configured by changegroup_schema() */
char *zDb; /* Configured by changegroup_schema() */
@@ -228965,108 +230621,128 @@ static int sessionChangesetExtendRecord(
}
/*
-** Add all changes in the changeset traversed by the iterator passed as
-** the first argument to the changegroup hash tables.
+** Locate or create a SessionTable object that may be used to add the
+** change currently pointed to by iterator pIter to changegroup pGrp.
+** If successful, set output variable (*ppTab) to point to the table
+** object and return SQLITE_OK. Otherwise, if some error occurs, return
+** an SQLite error code and leave (*ppTab) set to NULL.
*/
-static int sessionChangesetToHash(
- sqlite3_changeset_iter *pIter, /* Iterator to read from */
- sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */
- int bRebase /* True if hash table is for rebasing */
+static int sessionChangesetFindTable(
+ sqlite3_changegroup *pGrp,
+ const char *zTab,
+ sqlite3_changeset_iter *pIter,
+ SessionTable **ppTab
){
- u8 *aRec;
- int nRec;
int rc = SQLITE_OK;
SessionTable *pTab = 0;
- SessionBuffer rec = {0, 0, 0};
-
- while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, 0) ){
- const char *zNew;
- int nCol;
- int op;
- int iHash;
- int bIndirect;
- SessionChange *pChange;
- SessionChange *pExist = 0;
- SessionChange **pp;
-
- /* Ensure that only changesets, or only patchsets, but not a mixture
- ** of both, are being combined. It is an error to try to combine a
- ** changeset and a patchset. */
- if( pGrp->pList==0 ){
- pGrp->bPatch = pIter->bPatchset;
- }else if( pIter->bPatchset!=pGrp->bPatch ){
- rc = SQLITE_ERROR;
- break;
- }
+ int nTab = (int)strlen(zTab);
+ u8 *abPK = 0;
+ int nCol = 0;
- sqlite3changeset_op(pIter, &zNew, &nCol, &op, &bIndirect);
- if( !pTab || sqlite3_stricmp(zNew, pTab->zName) ){
- /* Search the list for a matching table */
- int nNew = (int)strlen(zNew);
- u8 *abPK;
+ *ppTab = 0;
+ sqlite3changeset_pk(pIter, &abPK, &nCol);
- sqlite3changeset_pk(pIter, &abPK, 0);
- for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){
- if( 0==sqlite3_strnicmp(pTab->zName, zNew, nNew+1) ) break;
- }
- if( !pTab ){
- SessionTable **ppTab;
+ /* Search the list for an existing table */
+ for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){
+ if( 0==sqlite3_strnicmp(pTab->zName, zTab, nTab+1) ) break;
+ }
- pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nNew+1);
- if( !pTab ){
- rc = SQLITE_NOMEM;
- break;
- }
- memset(pTab, 0, sizeof(SessionTable));
- pTab->nCol = nCol;
- pTab->abPK = (u8*)&pTab[1];
- memcpy(pTab->abPK, abPK, nCol);
- pTab->zName = (char*)&pTab->abPK[nCol];
- memcpy(pTab->zName, zNew, nNew+1);
-
- if( pGrp->db ){
- pTab->nCol = 0;
- rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb);
- if( rc ){
- assert( pTab->azCol==0 );
- sqlite3_free(pTab);
- break;
- }
- }
+ /* If one was not found above, create a new table now */
+ if( !pTab ){
+ SessionTable **ppNew;
- /* The new object must be linked on to the end of the list, not
- ** simply added to the start of it. This is to ensure that the
- ** tables within the output of sqlite3changegroup_output() are in
- ** the right order. */
- for(ppTab=&pGrp->pList; *ppTab; ppTab=&(*ppTab)->pNext);
- *ppTab = pTab;
- }
+ pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nTab+1);
+ if( !pTab ){
+ return SQLITE_NOMEM;
+ }
+ memset(pTab, 0, sizeof(SessionTable));
+ pTab->nCol = nCol;
+ pTab->abPK = (u8*)&pTab[1];
+ memcpy(pTab->abPK, abPK, nCol);
+ pTab->zName = (char*)&pTab->abPK[nCol];
+ memcpy(pTab->zName, zTab, nTab+1);
- if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){
- rc = SQLITE_SCHEMA;
- break;
+ if( pGrp->db ){
+ pTab->nCol = 0;
+ rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb);
+ if( rc ){
+ assert( pTab->azCol==0 );
+ sqlite3_free(pTab);
+ return rc;
}
}
- if( nCol<pTab->nCol ){
- assert( pGrp->db );
- rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, &rec);
- if( rc ) break;
- aRec = rec.aBuf;
- nRec = rec.nBuf;
- }
+ /* The new object must be linked on to the end of the list, not
+ ** simply added to the start of it. This is to ensure that the
+ ** tables within the output of sqlite3changegroup_output() are in
+ ** the right order. */
+ for(ppNew=&pGrp->pList; *ppNew; ppNew=&(*ppNew)->pNext);
+ *ppNew = pTab;
+ }
- if( sessionGrowHash(0, pIter->bPatchset, pTab) ){
- rc = SQLITE_NOMEM;
- break;
- }
+ /* Check that the table is compatible. */
+ if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){
+ rc = SQLITE_SCHEMA;
+ }
+
+ *ppTab = pTab;
+ return rc;
+}
+
+/*
+** Add the change currently indicated by iterator pIter to the hash table
+** belonging to changegroup pGrp.
+*/
+static int sessionOneChangeToHash(
+ sqlite3_changegroup *pGrp,
+ sqlite3_changeset_iter *pIter,
+ int bRebase
+){
+ int rc = SQLITE_OK;
+ int nCol = 0;
+ int op = 0;
+ int iHash = 0;
+ int bIndirect = 0;
+ SessionChange *pChange = 0;
+ SessionChange *pExist = 0;
+ SessionChange **pp = 0;
+ SessionTable *pTab = 0;
+ u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2];
+ int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2;
+
+ /* Ensure that only changesets, or only patchsets, but not a mixture
+ ** of both, are being combined. It is an error to try to combine a
+ ** changeset and a patchset. */
+ if( pGrp->pList==0 ){
+ pGrp->bPatch = pIter->bPatchset;
+ }else if( pIter->bPatchset!=pGrp->bPatch ){
+ rc = SQLITE_ERROR;
+ }
+
+ if( rc==SQLITE_OK ){
+ const char *zTab = 0;
+ sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
+ rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab);
+ }
+
+ if( rc==SQLITE_OK && nCol<pTab->nCol ){
+ SessionBuffer *pBuf = &pGrp->rec;
+ rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, pBuf);
+ aRec = pBuf->aBuf;
+ nRec = pBuf->nBuf;
+ assert( pGrp->db );
+ }
+
+ if( rc==SQLITE_OK && sessionGrowHash(0, pIter->bPatchset, pTab) ){
+ rc = SQLITE_NOMEM;
+ }
+
+ if( rc==SQLITE_OK ){
+ /* Search for existing entry. If found, remove it from the hash table.
+ ** Code below may link it back in. */
iHash = sessionChangeHash(
pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange
);
-
- /* Search for existing entry. If found, remove it from the hash table.
- ** Code below may link it back in.
- */
for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){
int bPkOnly1 = 0;
int bPkOnly2 = 0;
@@ -229081,19 +230757,41 @@ static int sessionChangesetToHash(
break;
}
}
+ }
+ if( rc==SQLITE_OK ){
rc = sessionChangeMerge(pTab, bRebase,
pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange
);
- if( rc ) break;
- if( pChange ){
- pChange->pNext = pTab->apChange[iHash];
- pTab->apChange[iHash] = pChange;
- pTab->nEntry++;
- }
+ }
+ if( rc==SQLITE_OK && pChange ){
+ pChange->pNext = pTab->apChange[iHash];
+ pTab->apChange[iHash] = pChange;
+ pTab->nEntry++;
+ }
+
+ if( rc==SQLITE_OK ) rc = pIter->rc;
+ return rc;
+}
+
+/*
+** Add all changes in the changeset traversed by the iterator passed as
+** the first argument to the changegroup hash tables.
+*/
+static int sessionChangesetToHash(
+ sqlite3_changeset_iter *pIter, /* Iterator to read from */
+ sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */
+ int bRebase /* True if hash table is for rebasing */
+){
+ u8 *aRec;
+ int nRec;
+ int rc = SQLITE_OK;
+
+ while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){
+ rc = sessionOneChangeToHash(pGrp, pIter, bRebase);
+ if( rc!=SQLITE_OK ) break;
}
- sqlite3_free(rec.aBuf);
if( rc==SQLITE_OK ) rc = pIter->rc;
return rc;
}
@@ -229222,6 +230920,23 @@ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup *pGrp, int nData, void
}
/*
+** Add a single change to a changeset-group.
+*/
+SQLITE_API int sqlite3changegroup_add_change(
+ sqlite3_changegroup *pGrp,
+ sqlite3_changeset_iter *pIter
+){
+ if( pIter->in.iCurrent==pIter->in.iNext
+ || pIter->rc!=SQLITE_OK
+ || pIter->bInvert
+ ){
+ /* Iterator does not point to any valid entry or is an INVERT iterator. */
+ return SQLITE_ERROR;
+ }
+ return sessionOneChangeToHash(pGrp, pIter, 0);
+}
+
+/*
** Obtain a buffer containing a changeset representing the concatenation
** of all changesets added to the group so far.
*/
@@ -229270,6 +230985,7 @@ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){
if( pGrp ){
sqlite3_free(pGrp->zDb);
sessionDeleteTable(0, pGrp->pList);
+ sqlite3_free(pGrp->rec.aBuf);
sqlite3_free(pGrp);
}
}
@@ -229671,6 +231387,7 @@ SQLITE_API int sqlite3rebaser_rebase_strm(
SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p){
if( p ){
sessionDeleteTable(0, p->grp.pList);
+ sqlite3_free(p->grp.rec.aBuf);
sqlite3_free(p);
}
}
@@ -229768,8 +231485,8 @@ struct Fts5PhraseIter {
** EXTENSION API FUNCTIONS
**
** xUserData(pFts):
-** Return a copy of the context pointer the extension function was
-** registered with.
+** Return a copy of the pUserData pointer passed to the xCreateFunction()
+** API when the extension function was registered.
**
** xColumnTotalSize(pFts, iCol, pnToken):
** If parameter iCol is less than zero, set output variable *pnToken
@@ -231365,6 +233082,9 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*);
** sqlite3Fts5ParserARG_STORE Code to store %extra_argument into fts5yypParser
** sqlite3Fts5ParserARG_FETCH Code to extract %extra_argument from fts5yypParser
** sqlite3Fts5ParserCTX_* As sqlite3Fts5ParserARG_ except for %extra_context
+** fts5YYREALLOC Name of the realloc() function to use
+** fts5YYFREE Name of the free() function to use
+** fts5YYDYNSTACK True if stack space should be extended on heap
** fts5YYERRORSYMBOL is the code number of the error symbol. If not
** defined, then do no error processing.
** fts5YYNSTATE the combined number of states.
@@ -231378,6 +233098,8 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*);
** fts5YY_NO_ACTION The fts5yy_action[] code for no-op
** fts5YY_MIN_REDUCE Minimum value for reduce actions
** fts5YY_MAX_REDUCE Maximum value for reduce actions
+** fts5YY_MIN_DSTRCTR Minimum symbol value that has a destructor
+** fts5YY_MAX_DSTRCTR Maximum symbol value that has a destructor
*/
#ifndef INTERFACE
# define INTERFACE 1
@@ -231404,6 +233126,9 @@ typedef union {
#define sqlite3Fts5ParserARG_PARAM ,pParse
#define sqlite3Fts5ParserARG_FETCH Fts5Parse *pParse=fts5yypParser->pParse;
#define sqlite3Fts5ParserARG_STORE fts5yypParser->pParse=pParse;
+#define fts5YYREALLOC realloc
+#define fts5YYFREE free
+#define fts5YYDYNSTACK 0
#define sqlite3Fts5ParserCTX_SDECL
#define sqlite3Fts5ParserCTX_PDECL
#define sqlite3Fts5ParserCTX_PARAM
@@ -231421,6 +233146,8 @@ typedef union {
#define fts5YY_NO_ACTION 82
#define fts5YY_MIN_REDUCE 83
#define fts5YY_MAX_REDUCE 110
+#define fts5YY_MIN_DSTRCTR 16
+#define fts5YY_MAX_DSTRCTR 24
/************* End control #defines *******************************************/
#define fts5YY_NLOOKAHEAD ((int)(sizeof(fts5yy_lookahead)/sizeof(fts5yy_lookahead[0])))
@@ -231436,6 +233163,22 @@ typedef union {
# define fts5yytestcase(X)
#endif
+/* Macro to determine if stack space has the ability to grow using
+** heap memory.
+*/
+#if fts5YYSTACKDEPTH<=0 || fts5YYDYNSTACK
+# define fts5YYGROWABLESTACK 1
+#else
+# define fts5YYGROWABLESTACK 0
+#endif
+
+/* Guarantee a minimum number of initial stack slots.
+*/
+#if fts5YYSTACKDEPTH<=0
+# undef fts5YYSTACKDEPTH
+# define fts5YYSTACKDEPTH 2 /* Need a minimum stack size */
+#endif
+
/* Next are the tables used to determine what action to take based on the
** current state and lookahead token. These tables are used to implement
@@ -231596,14 +233339,9 @@ struct fts5yyParser {
#endif
sqlite3Fts5ParserARG_SDECL /* A place to hold %extra_argument */
sqlite3Fts5ParserCTX_SDECL /* A place to hold %extra_context */
-#if fts5YYSTACKDEPTH<=0
- int fts5yystksz; /* Current side of the stack */
- fts5yyStackEntry *fts5yystack; /* The parser's stack */
- fts5yyStackEntry fts5yystk0; /* First stack entry */
-#else
- fts5yyStackEntry fts5yystack[fts5YYSTACKDEPTH]; /* The parser's stack */
- fts5yyStackEntry *fts5yystackEnd; /* Last entry in the stack */
-#endif
+ fts5yyStackEntry *fts5yystackEnd; /* Last entry in the stack */
+ fts5yyStackEntry *fts5yystack; /* The parser stack */
+ fts5yyStackEntry fts5yystk0[fts5YYSTACKDEPTH]; /* Initial stack space */
};
typedef struct fts5yyParser fts5yyParser;
@@ -231710,37 +233448,45 @@ static const char *const fts5yyRuleName[] = {
#endif /* NDEBUG */
-#if fts5YYSTACKDEPTH<=0
+#if fts5YYGROWABLESTACK
/*
** Try to increase the size of the parser stack. Return the number
** of errors. Return 0 on success.
*/
static int fts5yyGrowStack(fts5yyParser *p){
+ int oldSize = 1 + (int)(p->fts5yystackEnd - p->fts5yystack);
int newSize;
int idx;
fts5yyStackEntry *pNew;
- newSize = p->fts5yystksz*2 + 100;
- idx = p->fts5yytos ? (int)(p->fts5yytos - p->fts5yystack) : 0;
- if( p->fts5yystack==&p->fts5yystk0 ){
- pNew = malloc(newSize*sizeof(pNew[0]));
- if( pNew ) pNew[0] = p->fts5yystk0;
+ newSize = oldSize*2 + 100;
+ idx = (int)(p->fts5yytos - p->fts5yystack);
+ if( p->fts5yystack==p->fts5yystk0 ){
+ pNew = fts5YYREALLOC(0, newSize*sizeof(pNew[0]));
+ if( pNew==0 ) return 1;
+ memcpy(pNew, p->fts5yystack, oldSize*sizeof(pNew[0]));
}else{
- pNew = realloc(p->fts5yystack, newSize*sizeof(pNew[0]));
+ pNew = fts5YYREALLOC(p->fts5yystack, newSize*sizeof(pNew[0]));
+ if( pNew==0 ) return 1;
}
- if( pNew ){
- p->fts5yystack = pNew;
- p->fts5yytos = &p->fts5yystack[idx];
+ p->fts5yystack = pNew;
+ p->fts5yytos = &p->fts5yystack[idx];
#ifndef NDEBUG
- if( fts5yyTraceFILE ){
- fprintf(fts5yyTraceFILE,"%sStack grows from %d to %d entries.\n",
- fts5yyTracePrompt, p->fts5yystksz, newSize);
- }
-#endif
- p->fts5yystksz = newSize;
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sStack grows from %d to %d entries.\n",
+ fts5yyTracePrompt, oldSize, newSize);
}
- return pNew==0;
+#endif
+ p->fts5yystackEnd = &p->fts5yystack[newSize-1];
+ return 0;
}
+#endif /* fts5YYGROWABLESTACK */
+
+#if !fts5YYGROWABLESTACK
+/* For builds that do no have a growable stack, fts5yyGrowStack always
+** returns an error.
+*/
+# define fts5yyGrowStack(X) 1
#endif
/* Datatype of the argument to the memory allocated passed as the
@@ -231760,24 +233506,14 @@ static void sqlite3Fts5ParserInit(void *fts5yypRawParser sqlite3Fts5ParserCTX_PD
#ifdef fts5YYTRACKMAXSTACKDEPTH
fts5yypParser->fts5yyhwm = 0;
#endif
-#if fts5YYSTACKDEPTH<=0
- fts5yypParser->fts5yytos = NULL;
- fts5yypParser->fts5yystack = NULL;
- fts5yypParser->fts5yystksz = 0;
- if( fts5yyGrowStack(fts5yypParser) ){
- fts5yypParser->fts5yystack = &fts5yypParser->fts5yystk0;
- fts5yypParser->fts5yystksz = 1;
- }
-#endif
+ fts5yypParser->fts5yystack = fts5yypParser->fts5yystk0;
+ fts5yypParser->fts5yystackEnd = &fts5yypParser->fts5yystack[fts5YYSTACKDEPTH-1];
#ifndef fts5YYNOERRORRECOVERY
fts5yypParser->fts5yyerrcnt = -1;
#endif
fts5yypParser->fts5yytos = fts5yypParser->fts5yystack;
fts5yypParser->fts5yystack[0].stateno = 0;
fts5yypParser->fts5yystack[0].major = 0;
-#if fts5YYSTACKDEPTH>0
- fts5yypParser->fts5yystackEnd = &fts5yypParser->fts5yystack[fts5YYSTACKDEPTH-1];
-#endif
}
#ifndef sqlite3Fts5Parser_ENGINEALWAYSONSTACK
@@ -231891,9 +233627,26 @@ static void fts5yy_pop_parser_stack(fts5yyParser *pParser){
*/
static void sqlite3Fts5ParserFinalize(void *p){
fts5yyParser *pParser = (fts5yyParser*)p;
- while( pParser->fts5yytos>pParser->fts5yystack ) fts5yy_pop_parser_stack(pParser);
-#if fts5YYSTACKDEPTH<=0
- if( pParser->fts5yystack!=&pParser->fts5yystk0 ) free(pParser->fts5yystack);
+
+ /* In-lined version of calling fts5yy_pop_parser_stack() for each
+ ** element left in the stack */
+ fts5yyStackEntry *fts5yytos = pParser->fts5yytos;
+ while( fts5yytos>pParser->fts5yystack ){
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sPopping %s\n",
+ fts5yyTracePrompt,
+ fts5yyTokenName[fts5yytos->major]);
+ }
+#endif
+ if( fts5yytos->major>=fts5YY_MIN_DSTRCTR ){
+ fts5yy_destructor(pParser, fts5yytos->major, &fts5yytos->minor);
+ }
+ fts5yytos--;
+ }
+
+#if fts5YYGROWABLESTACK
+ if( pParser->fts5yystack!=pParser->fts5yystk0 ) fts5YYFREE(pParser->fts5yystack);
#endif
}
@@ -232120,25 +233873,19 @@ static void fts5yy_shift(
assert( fts5yypParser->fts5yyhwm == (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack) );
}
#endif
-#if fts5YYSTACKDEPTH>0
- if( fts5yypParser->fts5yytos>fts5yypParser->fts5yystackEnd ){
- fts5yypParser->fts5yytos--;
- fts5yyStackOverflow(fts5yypParser);
- return;
- }
-#else
- if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz] ){
+ fts5yytos = fts5yypParser->fts5yytos;
+ if( fts5yytos>fts5yypParser->fts5yystackEnd ){
if( fts5yyGrowStack(fts5yypParser) ){
fts5yypParser->fts5yytos--;
fts5yyStackOverflow(fts5yypParser);
return;
}
+ fts5yytos = fts5yypParser->fts5yytos;
+ assert( fts5yytos <= fts5yypParser->fts5yystackEnd );
}
-#endif
if( fts5yyNewState > fts5YY_MAX_SHIFT ){
fts5yyNewState += fts5YY_MIN_REDUCE - fts5YY_MIN_SHIFTREDUCE;
}
- fts5yytos = fts5yypParser->fts5yytos;
fts5yytos->stateno = fts5yyNewState;
fts5yytos->major = fts5yyMajor;
fts5yytos->minor.fts5yy0 = fts5yyMinor;
@@ -232575,19 +234322,12 @@ static void sqlite3Fts5Parser(
(int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack));
}
#endif
-#if fts5YYSTACKDEPTH>0
if( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystackEnd ){
- fts5yyStackOverflow(fts5yypParser);
- break;
- }
-#else
- if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz-1] ){
if( fts5yyGrowStack(fts5yypParser) ){
fts5yyStackOverflow(fts5yypParser);
break;
}
}
-#endif
}
fts5yyact = fts5yy_reduce(fts5yypParser,fts5yyruleno,fts5yymajor,fts5yyminor sqlite3Fts5ParserCTX_PARAM);
}else if( fts5yyact <= fts5YY_MAX_SHIFTREDUCE ){
@@ -250799,7 +252539,7 @@ static void fts5SourceIdFunc(
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
- sqlite3_result_text(pCtx, "fts5: 2024-04-15 13:34:05 8653b758870e6ef0c98d46b3ace27849054af85da891eb121e9aaa537f1e8355", -1, SQLITE_TRANSIENT);
+ sqlite3_result_text(pCtx, "fts5: 2024-05-23 13:25:27 96c92aba00c8375bc32fafcdf12429c58bd8aabfcadab6683e35bbb9cdebf19e", -1, SQLITE_TRANSIENT);
}
/*
@@ -250838,6 +252578,7 @@ static int fts5IntegrityMethod(
if( (rc&0xff)==SQLITE_CORRUPT ){
*pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s",
zSchema, zTabname);
+ rc = (*pzErr) ? SQLITE_OK : SQLITE_NOMEM;
}else if( rc!=SQLITE_OK ){
*pzErr = sqlite3_mprintf("unable to validate the inverted index for"
" FTS5 table %s.%s: %s",
@@ -250845,7 +252586,7 @@ static int fts5IntegrityMethod(
}
sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
- return SQLITE_OK;
+ return rc;
}
static int fts5Init(sqlite3 *db){
diff --git a/src/libs/3rdparty/sqlite/sqlite3.h b/src/libs/3rdparty/sqlite/sqlite3.h
index 2618b37a7b..57df8dcf20 100644
--- a/src/libs/3rdparty/sqlite/sqlite3.h
+++ b/src/libs/3rdparty/sqlite/sqlite3.h
@@ -146,9 +146,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.45.3"
-#define SQLITE_VERSION_NUMBER 3045003
-#define SQLITE_SOURCE_ID "2024-04-15 13:34:05 8653b758870e6ef0c98d46b3ace27849054af85da891eb121e9aaa537f1e8355"
+#define SQLITE_VERSION "3.46.0"
+#define SQLITE_VERSION_NUMBER 3046000
+#define SQLITE_SOURCE_ID "2024-05-23 13:25:27 96c92aba00c8375bc32fafcdf12429c58bd8aabfcadab6683e35bbb9cdebf19e"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -764,11 +764,11 @@ struct sqlite3_file {
** </ul>
** xLock() upgrades the database file lock. In other words, xLock() moves the
** database file lock in the direction NONE toward EXCLUSIVE. The argument to
-** xLock() is always on of SHARED, RESERVED, PENDING, or EXCLUSIVE, never
+** xLock() is always one of SHARED, RESERVED, PENDING, or EXCLUSIVE, never
** SQLITE_LOCK_NONE. If the database file lock is already at or above the
** requested lock, then the call to xLock() is a no-op.
** xUnlock() downgrades the database file lock to either SHARED or NONE.
-* If the lock is already at or below the requested lock state, then the call
+** If the lock is already at or below the requested lock state, then the call
** to xUnlock() is a no-op.
** The xCheckReservedLock() method checks whether any database connection,
** either in this process or in some other process, is holding a RESERVED,
@@ -3305,8 +3305,8 @@ SQLITE_API int sqlite3_set_authorizer(
#define SQLITE_RECURSIVE 33 /* NULL NULL */
/*
-** CAPI3REF: Tracing And Profiling Functions
-** METHOD: sqlite3
+** CAPI3REF: Deprecated Tracing And Profiling Functions
+** DEPRECATED
**
** These routines are deprecated. Use the [sqlite3_trace_v2()] interface
** instead of the routines described here.
@@ -6887,6 +6887,12 @@ SQLITE_API int sqlite3_autovacuum_pages(
** The exceptions defined in this paragraph might change in a future
** release of SQLite.
**
+** Whether the update hook is invoked before or after the
+** corresponding change is currently unspecified and may differ
+** depending on the type of change. Do not rely on the order of the
+** hook call with regards to the final result of the operation which
+** triggers the hook.
+**
** The update hook implementation must not do anything that will modify
** the database connection that invoked the update hook. Any actions
** to modify the database connection must be deferred until after the
@@ -8357,7 +8363,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
** The sqlite3_keyword_count() interface returns the number of distinct
** keywords understood by SQLite.
**
-** The sqlite3_keyword_name(N,Z,L) interface finds the N-th keyword and
+** The sqlite3_keyword_name(N,Z,L) interface finds the 0-based N-th keyword and
** makes *Z point to that keyword expressed as UTF8 and writes the number
** of bytes in the keyword into *L. The string that *Z points to is not
** zero-terminated. The sqlite3_keyword_name(N,Z,L) routine returns
@@ -9936,24 +9942,45 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
** <li value="2"><p>
** ^(If the sqlite3_vtab_distinct() interface returns 2, that means
** that the query planner does not need the rows returned in any particular
-** order, as long as rows with the same values in all "aOrderBy" columns
-** are adjacent.)^ ^(Furthermore, only a single row for each particular
-** combination of values in the columns identified by the "aOrderBy" field
-** needs to be returned.)^ ^It is always ok for two or more rows with the same
-** values in all "aOrderBy" columns to be returned, as long as all such rows
-** are adjacent. ^The virtual table may, if it chooses, omit extra rows
-** that have the same value for all columns identified by "aOrderBy".
-** ^However omitting the extra rows is optional.
+** order, as long as rows with the same values in all columns identified
+** by "aOrderBy" are adjacent.)^ ^(Furthermore, when two or more rows
+** contain the same values for all columns identified by "colUsed", all but
+** one such row may optionally be omitted from the result.)^
+** The virtual table is not required to omit rows that are duplicates
+** over the "colUsed" columns, but if the virtual table can do that without
+** too much extra effort, it could potentially help the query to run faster.
** This mode is used for a DISTINCT query.
** <li value="3"><p>
-** ^(If the sqlite3_vtab_distinct() interface returns 3, that means
-** that the query planner needs only distinct rows but it does need the
-** rows to be sorted.)^ ^The virtual table implementation is free to omit
-** rows that are identical in all aOrderBy columns, if it wants to, but
-** it is not required to omit any rows. This mode is used for queries
+** ^(If the sqlite3_vtab_distinct() interface returns 3, that means the
+** virtual table must return rows in the order defined by "aOrderBy" as
+** if the sqlite3_vtab_distinct() interface had returned 0. However if
+** two or more rows in the result have the same values for all columns
+** identified by "colUsed", then all but one such row may optionally be
+** omitted.)^ Like when the return value is 2, the virtual table
+** is not required to omit rows that are duplicates over the "colUsed"
+** columns, but if the virtual table can do that without
+** too much extra effort, it could potentially help the query to run faster.
+** This mode is used for queries
** that have both DISTINCT and ORDER BY clauses.
** </ol>
**
+** <p>The following table summarizes the conditions under which the
+** virtual table is allowed to set the "orderByConsumed" flag based on
+** the value returned by sqlite3_vtab_distinct(). This table is a
+** restatement of the previous four paragraphs:
+**
+** <table border=1 cellspacing=0 cellpadding=10 width="90%">
+** <tr>
+** <td valign="top">sqlite3_vtab_distinct() return value
+** <td valign="top">Rows are returned in aOrderBy order
+** <td valign="top">Rows with the same value in all aOrderBy columns are adjacent
+** <td valign="top">Duplicates over all colUsed columns may be omitted
+** <tr><td>0<td>yes<td>yes<td>no
+** <tr><td>1<td>no<td>yes<td>no
+** <tr><td>2<td>no<td>yes<td>yes
+** <tr><td>3<td>yes<td>yes<td>yes
+** </table>
+**
** ^For the purposes of comparing virtual table output values to see if the
** values are same value for sorting purposes, two NULL values are considered
** to be the same. In other words, the comparison operator is "IS"
@@ -11999,6 +12026,30 @@ SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const c
SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
/*
+** CAPI3REF: Add A Single Change To A Changegroup
+** METHOD: sqlite3_changegroup
+**
+** This function adds the single change currently indicated by the iterator
+** passed as the second argument to the changegroup object. The rules for
+** adding the change are just as described for [sqlite3changegroup_add()].
+**
+** If the change is successfully added to the changegroup, SQLITE_OK is
+** returned. Otherwise, an SQLite error code is returned.
+**
+** The iterator must point to a valid entry when this function is called.
+** If it does not, SQLITE_ERROR is returned and no change is added to the
+** changegroup. Additionally, the iterator must not have been opened with
+** the SQLITE_CHANGESETAPPLY_INVERT flag. In this case SQLITE_ERROR is also
+** returned.
+*/
+SQLITE_API int sqlite3changegroup_add_change(
+ sqlite3_changegroup*,
+ sqlite3_changeset_iter*
+);
+
+
+
+/*
** CAPI3REF: Obtain A Composite Changeset From A Changegroup
** METHOD: sqlite3_changegroup
**
@@ -12802,8 +12853,8 @@ struct Fts5PhraseIter {
** EXTENSION API FUNCTIONS
**
** xUserData(pFts):
-** Return a copy of the context pointer the extension function was
-** registered with.
+** Return a copy of the pUserData pointer passed to the xCreateFunction()
+** API when the extension function was registered.
**
** xColumnTotalSize(pFts, iCol, pnToken):
** If parameter iCol is less than zero, set output variable *pnToken
diff --git a/src/libs/extensionsystem/pluginmanager.cpp b/src/libs/extensionsystem/pluginmanager.cpp
index f0c7e930b4..4fbaba01e5 100644
--- a/src/libs/extensionsystem/pluginmanager.cpp
+++ b/src/libs/extensionsystem/pluginmanager.cpp
@@ -17,8 +17,8 @@
#include <utils/futuresynchronizer.h>
#include <utils/hostosinfo.h>
#include <utils/mimeutils.h>
-#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcsettings.h>
#include <utils/threadutils.h>
@@ -434,11 +434,6 @@ QString PluginManager::systemInformation()
return result;
}
-FutureSynchronizer *PluginManager::futureSynchronizer()
-{
- return d->m_futureSynchronizer.get();
-}
-
/*!
The list of paths were the plugin manager searches for plugins.
@@ -981,10 +976,7 @@ void PluginManagerPrivate::startDelayedInitialize()
*/
PluginManagerPrivate::PluginManagerPrivate(PluginManager *pluginManager) :
q(pluginManager)
-{
- m_futureSynchronizer.reset(new FutureSynchronizer);
-}
-
+{}
/*!
\internal
@@ -1047,7 +1039,11 @@ void PluginManagerPrivate::stopAll()
*/
void PluginManagerPrivate::deleteAll()
{
- m_futureSynchronizer.reset(); // Synchronize all futures from all plugins
+ // Guard against someone playing with the setting
+ QTC_ASSERT(
+ Utils::futureSynchronizer()->isCancelOnWait(),
+ Utils::futureSynchronizer()->cancelAllFutures());
+ Utils::futureSynchronizer()->waitForFinished(); // Synchronize all futures from all plugins
Utils::reverseForeach(loadQueue(), [this](PluginSpec *spec) {
loadPlugin(spec, PluginSpec::Deleted);
});
diff --git a/src/libs/extensionsystem/pluginmanager.h b/src/libs/extensionsystem/pluginmanager.h
index c5045e7187..68bba7a83c 100644
--- a/src/libs/extensionsystem/pluginmanager.h
+++ b/src/libs/extensionsystem/pluginmanager.h
@@ -16,8 +16,6 @@ QT_BEGIN_NAMESPACE
class QTextStream;
QT_END_NAMESPACE
-namespace Utils { class FutureSynchronizer; }
-
namespace ExtensionSystem {
class IPlugin;
class PluginSpec;
@@ -139,8 +137,6 @@ public:
static QString systemInformation();
- static Utils::FutureSynchronizer *futureSynchronizer();
-
signals:
void objectAdded(QObject *obj);
void aboutToRemoveObject(QObject *obj);
diff --git a/src/libs/extensionsystem/pluginmanager_p.h b/src/libs/extensionsystem/pluginmanager_p.h
index a761614223..ad9d4f81ee 100644
--- a/src/libs/extensionsystem/pluginmanager_p.h
+++ b/src/libs/extensionsystem/pluginmanager_p.h
@@ -136,7 +136,6 @@ public:
QWaitCondition m_scenarioWaitCondition;
PluginManager::ProcessData m_creatorProcessData;
- std::unique_ptr<Utils::FutureSynchronizer> m_futureSynchronizer;
private:
PluginManager *q;
diff --git a/src/libs/extensionsystem/pluginspec.cpp b/src/libs/extensionsystem/pluginspec.cpp
index 5c65dd5192..2158b99ac2 100644
--- a/src/libs/extensionsystem/pluginspec.cpp
+++ b/src/libs/extensionsystem/pluginspec.cpp
@@ -560,7 +560,7 @@ QString PluginSpec::errorString() const
/*!
Returns whether this plugin can be used to fill in a dependency of the given
- \a pluginName and \a version.
+ \a pluginName and \a pluginVersion.
\sa PluginSpec::dependencies()
*/
diff --git a/src/libs/extensionsystem/pluginview.cpp b/src/libs/extensionsystem/pluginview.cpp
index f4a86a41d4..0c6aeb2dff 100644
--- a/src/libs/extensionsystem/pluginview.cpp
+++ b/src/libs/extensionsystem/pluginview.cpp
@@ -50,8 +50,9 @@
*/
/*!
- \fn void ExtensionSystem::PluginView::pluginSettingsChanged(ExtensionSystem::PluginSpec *spec)
- The settings for the plugin list entry corresponding to \a spec changed.
+ \fn void ExtensionSystem::PluginView::pluginsChanged(const QSet<ExtensionSystem::PluginSpec *> &spec, bool enabled)
+ The value of \a enabled for the plugin list entry corresponding to \a spec
+ changed.
*/
using namespace Utils;
diff --git a/src/libs/modelinglib/qmt/config/configcontroller.cpp b/src/libs/modelinglib/qmt/config/configcontroller.cpp
index 2337c6873b..c584bce1c2 100644
--- a/src/libs/modelinglib/qmt/config/configcontroller.cpp
+++ b/src/libs/modelinglib/qmt/config/configcontroller.cpp
@@ -10,7 +10,9 @@
#include "qmt/stereotype/stereotypecontroller.h"
#include <QDebug>
-#include <QFile>
+
+using Utils::FilePath;
+using Utils::FilePaths;
namespace qmt {
@@ -36,7 +38,7 @@ void ConfigController::setStereotypeController(StereotypeController *stereotypeC
d->m_stereotypeController = stereotypeController;
}
-void ConfigController::readStereotypeDefinitions(const Utils::FilePath &path)
+void ConfigController::readStereotypeDefinitions(const FilePath &path)
{
if (path.isEmpty()) {
// TODO add error handling
@@ -51,7 +53,7 @@ void ConfigController::readStereotypeDefinitions(const Utils::FilePath &path)
connect(&parser, &StereotypeDefinitionParser::toolbarParsed,
this, &ConfigController::onToolbarParsed);
- Utils::FilePaths paths;
+ FilePaths paths;
if (path.isFile()) {
paths.append(path);
} else if (path.isDir()) {
@@ -60,11 +62,10 @@ void ConfigController::readStereotypeDefinitions(const Utils::FilePath &path)
// TODO add error handling
return;
}
- for (const auto &filePath : std::as_const(paths)) {
- QFile file(filePath.toString());
- if (file.open(QIODevice::ReadOnly)) {
- QString text = QString::fromUtf8(file.readAll());
- file.close();
+ for (const FilePath &filePath : std::as_const(paths)) {
+ auto data = filePath.fileContents();
+ if (data.has_value()) {
+ QString text = QString::fromUtf8(data.value());
StringTextSource source;
source.setSourceId(1);
source.setText(text);
diff --git a/src/libs/modelinglib/qmt/controller/namecontroller.cpp b/src/libs/modelinglib/qmt/controller/namecontroller.cpp
index a6ffe99d58..2f9506d3fe 100644
--- a/src/libs/modelinglib/qmt/controller/namecontroller.cpp
+++ b/src/libs/modelinglib/qmt/controller/namecontroller.cpp
@@ -5,6 +5,8 @@
#include <QDebug>
+using Utils::FilePath;
+
namespace qmt {
NameController::NameController(QObject *parent)
@@ -16,7 +18,7 @@ NameController::~NameController()
{
}
-QString NameController::convertFileNameToElementName(const Utils::FilePath &fileName)
+QString NameController::convertFileNameToElementName(const FilePath &fileName)
{
QString baseName = fileName.baseName().trimmed();
QString elementName;
@@ -63,12 +65,12 @@ QString NameController::convertElementNameToBaseFileName(const QString &elementN
return baseFileName;
}
-Utils::FilePath NameController::calcRelativePath(const Utils::FilePath &absoluteFileName,
- const Utils::FilePath &anchorPath)
+FilePath NameController::calcRelativePath(const FilePath &absoluteFileName,
+ const FilePath &anchorPath)
{
// TODO try using Utils::FilePath::relativePath
- QString absoluteFilePath = absoluteFileName.toString();
- QString anchorPathString = anchorPath.toString();
+ QString absoluteFilePath = absoluteFileName.path();
+ QString anchorPathString = anchorPath.path();
int secondLastSlashIndex = -1;
int slashIndex = -1;
int i = 0;
@@ -100,7 +102,7 @@ Utils::FilePath NameController::calcRelativePath(const Utils::FilePath &absolute
relativePath = absoluteFilePath.mid(slashIndex + 1);
}
- return Utils::FilePath::fromString(relativePath);
+ return FilePath::fromString(relativePath);
}
QString NameController::calcElementNameSearchId(const QString &elementName)
@@ -113,7 +115,7 @@ QString NameController::calcElementNameSearchId(const QString &elementName)
return searchId;
}
-QStringList NameController::buildElementsPath(const Utils::FilePath &filePath,
+QStringList NameController::buildElementsPath(const FilePath &filePath,
bool ignoreLastFilePathPart)
{
QList<QString> relativeElements;
@@ -123,7 +125,7 @@ QStringList NameController::buildElementsPath(const Utils::FilePath &filePath,
if (ignoreLastFilePathPart || split.last().isEmpty())
--splitEnd;
for (auto it = split.constBegin(); it != splitEnd; ++it) {
- QString packageName = qmt::NameController::convertFileNameToElementName(Utils::FilePath::fromString(*it));
+ QString packageName = qmt::NameController::convertFileNameToElementName(FilePath::fromString(*it));
relativeElements.append(packageName);
}
return relativeElements;
diff --git a/src/libs/modelinglib/qmt/diagram/dobject.cpp b/src/libs/modelinglib/qmt/diagram/dobject.cpp
index 68e954b857..543e6af30f 100644
--- a/src/libs/modelinglib/qmt/diagram/dobject.cpp
+++ b/src/libs/modelinglib/qmt/diagram/dobject.cpp
@@ -6,11 +6,11 @@
#include "dvisitor.h"
#include "dconstvisitor.h"
+using Utils::FilePath;
+
namespace qmt {
-DObject::DObject()
-{
-}
+DObject::DObject() {}
DObject::DObject(const DObject &rhs)
: DElement(rhs),
@@ -129,7 +129,7 @@ bool DObject::hasImage() const
return !m_image.isNull();
}
-void DObject::setImagePath(const QString &path)
+void DObject::setImagePath(const FilePath &path)
{
m_imagePath = path;
}
diff --git a/src/libs/modelinglib/qmt/diagram/dobject.h b/src/libs/modelinglib/qmt/diagram/dobject.h
index bf43edea12..7364f15749 100644
--- a/src/libs/modelinglib/qmt/diagram/dobject.h
+++ b/src/libs/modelinglib/qmt/diagram/dobject.h
@@ -7,6 +7,8 @@
#include "qmt/infrastructure/uid.h"
+#include <utils/filepath.h>
+
#include <QImage>
#include <QList>
#include <QPointF>
@@ -81,8 +83,8 @@ public:
void setVisualEmphasized(bool visualEmphasized);
bool hasLinkedFile() const { return m_hasLinkedFile; }
void setLinkedFile(bool linkedFile);
- QString imagePath() const { return m_imagePath; }
- void setImagePath(const QString &path);
+ Utils::FilePath imagePath() const { return m_imagePath; }
+ void setImagePath(const Utils::FilePath &path);
bool hasImage() const;
QImage image() const { return m_image; }
void setImage(const QImage &image);
@@ -104,7 +106,7 @@ private:
bool m_isAutoSized = true;
bool m_isVisualEmphasized = false;
bool m_hasLinkedFile = false;
- QString m_imagePath;
+ Utils::FilePath m_imagePath;
QImage m_image;
};
diff --git a/src/libs/modelinglib/qmt/document_controller/documentcontroller.cpp b/src/libs/modelinglib/qmt/document_controller/documentcontroller.cpp
index 12ec26048e..696fc30558 100644
--- a/src/libs/modelinglib/qmt/document_controller/documentcontroller.cpp
+++ b/src/libs/modelinglib/qmt/document_controller/documentcontroller.cpp
@@ -32,6 +32,8 @@
#include "../../modelinglibtr.h"
+using Utils::FilePath;
+
namespace qmt {
DocumentController::DocumentController(QObject *parent) :
@@ -231,7 +233,7 @@ MDiagram *DocumentController::findOrCreateRootDiagram()
return rootDiagram;
}
-void DocumentController::createNewProject(const Utils::FilePath &fileName)
+void DocumentController::createNewProject(const FilePath &fileName)
{
m_diagramsManager->removeAllDiagrams();
m_treeModel->setModelController(nullptr);
@@ -244,7 +246,7 @@ void DocumentController::createNewProject(const Utils::FilePath &fileName)
m_modelController->setRootPackage(m_projectController->project()->rootPackage());
}
-void DocumentController::loadProject(const Utils::FilePath &fileName)
+void DocumentController::loadProject(const FilePath &fileName)
{
m_diagramsManager->removeAllDiagrams();
m_treeModel->setModelController(nullptr);
diff --git a/src/libs/modelinglib/qmt/infrastructure/ioexceptions.cpp b/src/libs/modelinglib/qmt/infrastructure/ioexceptions.cpp
index 04778cd0e3..bb11a4d566 100644
--- a/src/libs/modelinglib/qmt/infrastructure/ioexceptions.cpp
+++ b/src/libs/modelinglib/qmt/infrastructure/ioexceptions.cpp
@@ -7,6 +7,8 @@
#include <QObject>
+using Utils::FilePath;
+
namespace qmt {
IOException::IOException(const QString &errorMsg)
@@ -14,39 +16,39 @@ IOException::IOException(const QString &errorMsg)
{
}
-FileIOException::FileIOException(const QString &errorMsg, const Utils::FilePath &fileName, int lineNumber)
+FileIOException::FileIOException(const QString &errorMsg, const FilePath &fileName, int lineNumber)
: IOException(errorMsg),
m_fileName(fileName),
m_lineNumber(lineNumber)
{
}
-FileNotFoundException::FileNotFoundException(const Utils::FilePath &fileName)
+FileNotFoundException::FileNotFoundException(const FilePath &fileName)
: FileIOException(Tr::tr("File not found."), fileName)
{
}
-FileCreationException::FileCreationException(const Utils::FilePath &fileName)
+FileCreationException::FileCreationException(const FilePath &fileName)
: FileIOException(Tr::tr("Unable to create file."), fileName)
{
}
-FileWriteError::FileWriteError(const Utils::FilePath &fileName, int lineNumber)
+FileWriteError::FileWriteError(const FilePath &fileName, int lineNumber)
: FileIOException(Tr::tr("Writing to file failed."), fileName, lineNumber)
{
}
-FileReadError::FileReadError(const Utils::FilePath &fileName, int lineNumber)
+FileReadError::FileReadError(const FilePath &fileName, int lineNumber)
: FileIOException(Tr::tr("Reading from file failed."), fileName, lineNumber)
{
}
-IllegalXmlFile::IllegalXmlFile(const Utils::FilePath &fileName, int lineNumber)
+IllegalXmlFile::IllegalXmlFile(const FilePath &fileName, int lineNumber)
: FileIOException(Tr::tr("Illegal XML file."), fileName, lineNumber)
{
}
-UnknownFileVersion::UnknownFileVersion(int version, const Utils::FilePath &fileName, int lineNumber)
+UnknownFileVersion::UnknownFileVersion(int version, const FilePath &fileName, int lineNumber)
: FileIOException(Tr::tr("Unable to handle file version %1.")
.arg(version), fileName, lineNumber)
{
diff --git a/src/libs/modelinglib/qmt/model/mobject.cpp b/src/libs/modelinglib/qmt/model/mobject.cpp
index 1e6bf8d0b3..f5b4e2b36f 100644
--- a/src/libs/modelinglib/qmt/model/mobject.cpp
+++ b/src/libs/modelinglib/qmt/model/mobject.cpp
@@ -8,6 +8,8 @@
#include "mvisitor.h"
#include "mconstvisitor.h"
+using Utils::FilePath;
+
namespace qmt {
MObject::MObject()
@@ -46,7 +48,7 @@ void MObject::setName(const QString &name)
m_name = name;
}
-void MObject::setLinkedFileName(const QString &linkedfilename)
+void MObject::setLinkedFileName(const FilePath &linkedfilename)
{
m_linkedfilename = linkedfilename;
}
diff --git a/src/libs/modelinglib/qmt/model/mobject.h b/src/libs/modelinglib/qmt/model/mobject.h
index 33a475ed6e..29d10da4ba 100644
--- a/src/libs/modelinglib/qmt/model/mobject.h
+++ b/src/libs/modelinglib/qmt/model/mobject.h
@@ -6,6 +6,8 @@
#include "melement.h"
#include "qmt/infrastructure/handles.h"
+#include <utils/filepath.h>
+
#include <QString>
namespace qmt {
@@ -23,8 +25,8 @@ public:
QString name() const { return m_name; }
void setName(const QString &name);
- QString linkedFileName() const { return m_linkedfilename; }
- void setLinkedFileName(const QString &linkedfilename);
+ Utils::FilePath linkedFileName() const { return m_linkedfilename; }
+ void setLinkedFileName(const Utils::FilePath &linkedfilename);
const Handles<MObject> &children() const { return m_children; }
void setChildren(const Handles<MObject> &children);
@@ -50,7 +52,7 @@ public:
private:
QString m_name;
- QString m_linkedfilename;
+ Utils::FilePath m_linkedfilename;
Handles<MObject> m_children;
Handles<MRelation> m_relations;
};
diff --git a/src/libs/modelinglib/qmt/model_ui/treemodel.cpp b/src/libs/modelinglib/qmt/model_ui/treemodel.cpp
index 3e8d5a59b6..23d589a654 100644
--- a/src/libs/modelinglib/qmt/model_ui/treemodel.cpp
+++ b/src/libs/modelinglib/qmt/model_ui/treemodel.cpp
@@ -30,6 +30,8 @@
#include <QStandardItem>
+using Utils::FilePath;
+
namespace qmt {
class TreeModel::ModelItem : public QStandardItem
@@ -807,7 +809,7 @@ QString TreeModel::createRelationLabel(const MRelation *relation)
}
QIcon TreeModel::createIcon(StereotypeIcon::Element stereotypeIconElement, StyleEngine::ElementType styleElementType,
- const QStringList &stereotypes, const QString &defaultIconPath)
+ const QStringList &stereotypes, const FilePath &defaultIconPath)
{
const Style *style = m_styleController->adaptStyle(styleElementType);
return m_stereotypeController->createIcon(stereotypeIconElement, stereotypes, defaultIconPath, style,
diff --git a/src/libs/modelinglib/qmt/model_ui/treemodel.h b/src/libs/modelinglib/qmt/model_ui/treemodel.h
index add325a94a..5ea3596348 100644
--- a/src/libs/modelinglib/qmt/model_ui/treemodel.h
+++ b/src/libs/modelinglib/qmt/model_ui/treemodel.h
@@ -9,6 +9,8 @@
#include <qmt/stereotype/stereotypeicon.h>
#include <qmt/style/styleengine.h>
+#include <utils/filepath.h>
+
#include <QScopedPointer>
#include <QHash>
@@ -93,8 +95,9 @@ private:
QString createObjectLabel(const MObject *object);
QString createRelationLabel(const MRelation *relation);
QIcon createIcon(StereotypeIcon::Element stereotypeIconElement,
- StyleEngine::ElementType styleElementType, const QStringList &stereotypes,
- const QString &defaultIconPath);
+ StyleEngine::ElementType styleElementType,
+ const QStringList &stereotypes,
+ const Utils::FilePath &defaultIconPath);
enum Busy {
NotBusy,
diff --git a/src/libs/modelinglib/qmt/project/project.cpp b/src/libs/modelinglib/qmt/project/project.cpp
index bc0ed449a3..2e08adc6df 100644
--- a/src/libs/modelinglib/qmt/project/project.cpp
+++ b/src/libs/modelinglib/qmt/project/project.cpp
@@ -3,6 +3,8 @@
#include "project.h"
+using Utils::FilePath;
+
namespace qmt {
Project::Project()
@@ -23,7 +25,7 @@ bool Project::hasFileName() const
return !m_fileName.isEmpty();
}
-void Project::setFileName(const Utils::FilePath &fileName)
+void Project::setFileName(const FilePath &fileName)
{
m_fileName = fileName;
}
@@ -33,7 +35,7 @@ void Project::setRootPackage(MPackage *rootPackage)
m_rootPackage = rootPackage;
}
-void Project::setConfigPath(const Utils::FilePath &configPath)
+void Project::setConfigPath(const FilePath &configPath)
{
m_configPath = configPath;
}
diff --git a/src/libs/modelinglib/qmt/project_controller/projectcontroller.cpp b/src/libs/modelinglib/qmt/project_controller/projectcontroller.cpp
index f5d5225d03..47eb01138e 100644
--- a/src/libs/modelinglib/qmt/project_controller/projectcontroller.cpp
+++ b/src/libs/modelinglib/qmt/project_controller/projectcontroller.cpp
@@ -10,6 +10,8 @@
#include "../../modelinglibtr.h"
+using Utils::FilePath;
+
namespace qmt {
NoFileNameException::NoFileNameException()
@@ -31,7 +33,7 @@ ProjectController::~ProjectController()
{
}
-void ProjectController::newProject(const Utils::FilePath &fileName)
+void ProjectController::newProject(const FilePath &fileName)
{
m_project.reset(new Project());
auto rootPackage = new MPackage();
@@ -43,7 +45,7 @@ void ProjectController::newProject(const Utils::FilePath &fileName)
emit changed();
}
-void ProjectController::setFileName(const Utils::FilePath &fileName)
+void ProjectController::setFileName(const FilePath &fileName)
{
if (fileName != m_project->fileName()) {
m_project->setFileName(fileName);
@@ -82,7 +84,7 @@ void ProjectController::save()
emit changed();
}
-void ProjectController::saveAs(const Utils::FilePath &fileName)
+void ProjectController::saveAs(const FilePath &fileName)
{
setFileName(fileName);
save();
diff --git a/src/libs/modelinglib/qmt/serializer/infrastructureserializer.h b/src/libs/modelinglib/qmt/serializer/infrastructureserializer.h
index 2313ab8aba..59103b3fa9 100644
--- a/src/libs/modelinglib/qmt/serializer/infrastructureserializer.h
+++ b/src/libs/modelinglib/qmt/serializer/infrastructureserializer.h
@@ -58,7 +58,7 @@ inline void serialize(Archive &archive, qmt::Handles<T> &handles)
template<class Archive>
inline void save(Archive &archive, const Utils::FilePath &filePath)
{
- archive.write(filePath.toString());
+ archive.write(filePath.toFSPathString());
}
template<class Archive>
@@ -66,7 +66,7 @@ inline void load(Archive &archive, Utils::FilePath &filePath)
{
QString s;
archive.read(&s);
- filePath = Utils::FilePath::fromString(s);
+ filePath = Utils::FilePath::fromUserInput(s);
}
} // namespace qark
diff --git a/src/libs/modelinglib/qmt/serializer/projectserializer.cpp b/src/libs/modelinglib/qmt/serializer/projectserializer.cpp
index ecb5538f0c..832d47309b 100644
--- a/src/libs/modelinglib/qmt/serializer/projectserializer.cpp
+++ b/src/libs/modelinglib/qmt/serializer/projectserializer.cpp
@@ -20,6 +20,8 @@
#include <QFile>
+using Utils::FilePath;
+
namespace qark {
using namespace qmt;
@@ -48,11 +50,11 @@ ProjectSerializer::~ProjectSerializer()
{
}
-void ProjectSerializer::save(const Utils::FilePath &fileName, const Project *project)
+void ProjectSerializer::save(const FilePath &fileName, const Project *project)
{
QMT_ASSERT(project, return);
- QFile file(fileName.toString());
+ QFile file(fileName.toFSPathString());
if (!file.open(QIODevice::WriteOnly))
throw FileCreationException(fileName);
@@ -84,11 +86,11 @@ QByteArray ProjectSerializer::save(const Project *project)
return buffer;
}
-void ProjectSerializer::load(const Utils::FilePath &fileName, Project *project)
+void ProjectSerializer::load(const FilePath &fileName, Project *project)
{
QMT_ASSERT(project, return);
- QFile file(fileName.toString());
+ QFile file(fileName.toFSPathString());
if (!file.open(QIODevice::ReadOnly))
throw FileNotFoundException(fileName);
diff --git a/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.cpp b/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.cpp
index f557e80c4b..2f679df117 100644
--- a/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.cpp
+++ b/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.cpp
@@ -10,6 +10,8 @@
#include "qmt/infrastructure/qmtassert.h"
#include "qmt/style/style.h"
+
+#include <utils/filepath.h>
#include <utils/algorithm.h>
#include <QHash>
@@ -19,12 +21,14 @@
#include <algorithm>
+using Utils::FilePath;
+
namespace qmt {
namespace {
struct IconKey {
- IconKey(StereotypeIcon::Element element, const QList<QString> &stereotypes, const QString &defaultIconPath,
+ IconKey(StereotypeIcon::Element element, const QList<QString> &stereotypes, const FilePath &defaultIconPath,
const Uid &styleUid, const QSize &size, const QMarginsF &margins, qreal lineWidth)
: m_element(element),
m_stereotypes(stereotypes),
@@ -53,7 +57,7 @@ struct IconKey {
const StereotypeIcon::Element m_element;
const QList<QString> m_stereotypes;
- const QString m_defaultIconPath;
+ const FilePath m_defaultIconPath;
const Uid m_styleUid;
const QSize m_size;
const QMarginsF m_margins;
@@ -157,7 +161,7 @@ CustomRelation StereotypeController::findCustomRelationByStereotype(const QStrin
}
QIcon StereotypeController::createIcon(StereotypeIcon::Element element, const QList<QString> &stereotypes,
- const QString &defaultIconPath, const Style *style, const QSize &size,
+ const FilePath &defaultIconPath, const Style *style, const QSize &size,
const QMarginsF &margins, qreal lineWidth)
{
IconKey key(element, stereotypes, defaultIconPath, style->uid(), size, margins, lineWidth);
@@ -231,7 +235,7 @@ QIcon StereotypeController::createIcon(StereotypeIcon::Element element, const QL
icon = QIcon(pixmap);
}
if (icon.isNull() && !defaultIconPath.isEmpty())
- icon = QIcon(defaultIconPath);
+ icon = QIcon(defaultIconPath.toFSPathString());
d->m_iconMap.insert(key, icon);
return icon;
diff --git a/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.h b/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.h
index 45bc3bbd18..49acc5f937 100644
--- a/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.h
+++ b/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.h
@@ -5,6 +5,8 @@
#include "stereotypeicon.h"
+#include <utils/filepath.h>
+
#include <QMarginsF>
#include <QObject>
@@ -34,9 +36,13 @@ public:
StereotypeIcon findStereotypeIcon(const QString &stereotypeIconId) const;
CustomRelation findCustomRelation(const QString &customRelationId) const;
CustomRelation findCustomRelationByStereotype(const QString &steoreotype) const;
- QIcon createIcon(StereotypeIcon::Element element, const QList<QString> &stereotypes,
- const QString &defaultIconPath, const Style *style,
- const QSize &size, const QMarginsF &margins, qreal lineWidth);
+ QIcon createIcon(StereotypeIcon::Element element,
+ const QList<QString> &stereotypes,
+ const Utils::FilePath &defaultIconPath,
+ const Style *style,
+ const QSize &size,
+ const QMarginsF &margins,
+ qreal lineWidth);
void addStereotypeIcon(const StereotypeIcon &stereotypeIcon);
void addCustomRelation(const CustomRelation &customRelation);
diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h
index 810e4e3c5d..15f8109fe9 100644
--- a/src/libs/nanotrace/nanotracehr.h
+++ b/src/libs/nanotrace/nanotracehr.h
@@ -9,6 +9,7 @@
#include <utils/smallstring.h>
#include <utils/span.h>
+#include <utils/utility.h>
#include <QByteArrayView>
#include <QList>
@@ -144,17 +145,10 @@ void convertToString(String &string, Number number)
string.append(number);
}
-template<typename Enumeration>
-constexpr std::underlying_type_t<Enumeration> to_underlying(Enumeration enumeration) noexcept
-{
- static_assert(std::is_enum_v<Enumeration>, "to_underlying expect an enumeration");
- return static_cast<std::underlying_type_t<Enumeration>>(enumeration);
-}
-
template<typename String, typename Enumeration, typename std::enable_if_t<std::is_enum_v<Enumeration>, bool> = true>
void convertToString(String &string, Enumeration enumeration)
{
- string.append(to_underlying(enumeration));
+ string.append(Utils::to_underlying(enumeration));
}
template<typename String>
diff --git a/src/libs/qmljs/jsoncheck.cpp b/src/libs/qmljs/jsoncheck.cpp
index 1d7c04e50c..3cb44111f9 100644
--- a/src/libs/qmljs/jsoncheck.cpp
+++ b/src/libs/qmljs/jsoncheck.cpp
@@ -447,7 +447,7 @@ JsonValue *JsonValue::build(const QVariant &variant, JsonMemoryPool *pool)
{
switch (variant.typeId()) {
- case QVariant::List: {
+ case QMetaType::QVariantList: {
auto newValue = new (pool) JsonArrayValue;
const QList<QVariant> list = variant.toList();
for (const QVariant &element : list)
@@ -455,7 +455,7 @@ JsonValue *JsonValue::build(const QVariant &variant, JsonMemoryPool *pool)
return newValue;
}
- case QVariant::Map: {
+ case QMetaType::QVariantMap: {
auto newValue = new (pool) JsonObjectValue;
const QVariantMap variantMap = variant.toMap();
for (QVariantMap::const_iterator it = variantMap.begin(); it != variantMap.end(); ++it)
@@ -463,19 +463,19 @@ JsonValue *JsonValue::build(const QVariant &variant, JsonMemoryPool *pool)
return newValue;
}
- case QVariant::String:
+ case QMetaType::QString:
return new (pool) JsonStringValue(variant.toString());
- case QVariant::Int:
+ case QMetaType::Int:
return new (pool) JsonIntValue(variant.toInt());
- case QVariant::Double:
+ case QMetaType::Double:
return new (pool) JsonDoubleValue(variant.toDouble());
- case QVariant::Bool:
+ case QMetaType::Bool:
return new (pool) JsonBooleanValue(variant.toBool());
- case QVariant::Invalid:
+ case QMetaType::UnknownType:
return new (pool) JsonNullValue;
default:
diff --git a/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h b/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h
index 1e8d82e632..b0012f3eb2 100644
--- a/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h
+++ b/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h
@@ -18,6 +18,7 @@ public:
ActiveSceneChanged,
ActiveSplitChanged,
RenderModelNodePreviewImage,
+ Import3DPreviewImage,
Import3DSupport,
NodeAtPos,
BakeLightsProgress,
diff --git a/src/libs/qmlpuppetcommunication/commands/view3dactioncommand.cpp b/src/libs/qmlpuppetcommunication/commands/view3dactioncommand.cpp
index eac0961393..a522499ffa 100644
--- a/src/libs/qmlpuppetcommunication/commands/view3dactioncommand.cpp
+++ b/src/libs/qmlpuppetcommunication/commands/view3dactioncommand.cpp
@@ -3,6 +3,8 @@
#include "view3dactioncommand.h"
+#include <utils/utility.h>
+
#include <QDebug>
#include <QDataStream>
@@ -64,16 +66,9 @@ QDebug operator<<(QDebug debug, const View3DActionCommand &command)
<< command.m_value << ")\n";
}
-template<typename Enumeration>
-constexpr std::underlying_type_t<Enumeration> to_underlying(Enumeration enumeration) noexcept
-{
- static_assert(std::is_enum_v<Enumeration>, "to_underlying expect an enumeration");
- return static_cast<std::underlying_type_t<Enumeration>>(enumeration);
-}
-
QDebug operator<<(QDebug debug, View3DActionType type)
{
- return debug.nospace() << to_underlying(type);
+ return debug.nospace() << Utils::to_underlying(type);
}
} // namespace QmlDesigner
diff --git a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h
index a3012c99f5..d248e5e6fd 100644
--- a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h
+++ b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h
@@ -36,6 +36,7 @@ enum class View3DActionType {
OrientationToggle,
EditLightToggle,
ShowGrid,
+ ShowLookAt,
ShowSelectionBox,
ShowIconGizmo,
ShowCameraFrustum,
@@ -54,7 +55,10 @@ enum class View3DActionType {
FlyModeToggle,
EditCameraRotation,
EditCameraMove,
- EditCameraStopAllMoves
+ EditCameraStopAllMoves,
+ SetLastSceneEnvData,
+ Import3dUpdatePreviewImage,
+ Import3dRotatePreviewModel
};
constexpr bool isNanotraceEnabled()
diff --git a/src/libs/qmlpuppetcommunication/types/enumeration.h b/src/libs/qmlpuppetcommunication/types/enumeration.h
index 57bfe6c0ef..5fe2294621 100644
--- a/src/libs/qmlpuppetcommunication/types/enumeration.h
+++ b/src/libs/qmlpuppetcommunication/types/enumeration.h
@@ -43,17 +43,20 @@ public:
EnumerationNameView scope() const
{
- auto found = std::find(m_enumerationName.begin(), m_enumerationName.end(), '.');
- return {m_enumerationName.begin(), found};
+ auto found = std::find(m_enumerationName.rbegin(), m_enumerationName.rend(), '.');
+ if (found != m_enumerationName.rend())
+ return {m_enumerationName.begin(), std::prev(found.base())};
+
+ return {m_enumerationName.end(), m_enumerationName.end()};
}
EnumerationNameView toScope() const { return scope().toByteArray(); }
EnumerationNameView name() const
{
- auto found = std::find(m_enumerationName.begin(), m_enumerationName.end(), '.');
- if (found != m_enumerationName.end())
- return {std::next(found), m_enumerationName.end()};
+ auto found = std::find(m_enumerationName.rbegin(), m_enumerationName.rend(), '.');
+ if (found != m_enumerationName.rend())
+ return {found.base(), m_enumerationName.end()};
return {m_enumerationName.end(), m_enumerationName.end()};
}
diff --git a/src/libs/solutions/spinner/spinner.cpp b/src/libs/solutions/spinner/spinner.cpp
index 9c1c43b440..479467ae58 100644
--- a/src/libs/solutions/spinner/spinner.cpp
+++ b/src/libs/solutions/spinner/spinner.cpp
@@ -247,7 +247,7 @@ void Spinner::setSize(SpinnerSize size)
}
/*!
- Sets the color of the spinner to the given color.
+ Sets the color of the spinner to \a color.
*/
void Spinner::setColor(const QColor &color)
{
diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h
index 3710021ff5..64acba0a27 100644
--- a/src/libs/sqlite/sqlitebasestatement.h
+++ b/src/libs/sqlite/sqlitebasestatement.h
@@ -36,13 +36,6 @@ class DatabaseBackend;
enum class Type : char { Invalid, Integer, Float, Text, Blob, Null };
-template<typename Enumeration>
-constexpr static std::underlying_type_t<Enumeration> to_underlying(Enumeration enumeration) noexcept
-{
- static_assert(std::is_enum_v<Enumeration>, "to_underlying expect an enumeration");
- return static_cast<std::underlying_type_t<Enumeration>>(enumeration);
-}
-
class SQLITE_EXPORT BaseStatement
{
public:
@@ -87,7 +80,7 @@ public:
template<typename Type, typename = std::enable_if_t<Type::IsBasicId::value>>
void bind(int index, Type id)
{
- if (id)
+ if (!id.isNull())
bind(index, id.internalId());
else
bindNull(index);
@@ -96,7 +89,7 @@ public:
template<typename Enumeration, std::enable_if_t<std::is_enum_v<Enumeration>, bool> = true>
void bind(int index, Enumeration enumeration)
{
- bind(index, to_underlying(enumeration));
+ bind(index, Utils::to_underlying(enumeration));
}
void bind(int index, uint value) { bind(index, static_cast<long long>(value)); }
diff --git a/src/libs/sqlite/sqliteids.h b/src/libs/sqlite/sqliteids.h
index 1ffd546d9f..abbb13d7c1 100644
--- a/src/libs/sqlite/sqliteids.h
+++ b/src/libs/sqlite/sqliteids.h
@@ -4,6 +4,7 @@
#pragma once
#include <utils/span.h>
+#include <utils/utility.h>
#include <nanotrace/nanotracehr.h>
#include <type_traits>
@@ -27,7 +28,15 @@ public:
return id;
}
- constexpr friend bool compareInvalidAreTrue(BasicId first, BasicId second)
+ template<typename Enumeration>
+ static constexpr BasicId createSpecialState(Enumeration specialState)
+ {
+ BasicId id;
+ id.id = ::Utils::to_underlying(specialState);
+ return id;
+ }
+
+ friend constexpr bool compareInvalidAreTrue(BasicId first, BasicId second)
{
return first.id == second.id;
}
@@ -57,6 +66,14 @@ public:
constexpr bool isValid() const { return id > 0; }
+ constexpr bool isNull() const { return id == 0; }
+
+ template<typename Enumeration>
+ constexpr bool hasSpecialState(Enumeration specialState) const
+ {
+ return id == ::Utils::to_underlying(specialState);
+ }
+
explicit operator bool() const { return isValid(); }
explicit operator std::size_t() const { return static_cast<std::size_t>(id); }
@@ -68,13 +85,13 @@ public:
template<typename String>
friend void convertToString(String &string, BasicId id)
{
- if (id.isValid())
- NanotraceHR::convertToString(string, id.internalId());
+ if (id.isNull())
+ NanotraceHR::convertToString(string, "invalid null");
else
- NanotraceHR::convertToString(string, "invalid");
+ NanotraceHR::convertToString(string, id.internalId());
}
-private:
+protected:
InternalIntegerType id = 0;
};
diff --git a/src/libs/sqlite/sqlitevalue.h b/src/libs/sqlite/sqlitevalue.h
index 725682494b..7adadb43b3 100644
--- a/src/libs/sqlite/sqlitevalue.h
+++ b/src/libs/sqlite/sqlitevalue.h
@@ -373,18 +373,18 @@ private:
if (value.isNull())
return VariantType{NullValue{}};
- switch (value.userType()) {
- case QVariant::Int:
+ switch (value.typeId()) {
+ case QMetaType::Int:
return VariantType{static_cast<long long>(value.toInt())};
- case QVariant::LongLong:
+ case QMetaType::LongLong:
return VariantType{value.toLongLong()};
- case QVariant::UInt:
+ case QMetaType::UInt:
return VariantType{static_cast<long long>(value.toUInt())};
- case QVariant::Double:
+ case QMetaType::Double:
return VariantType{value.toFloat()};
- case QVariant::String:
+ case QMetaType::QString:
return VariantType{value.toString()};
- case QVariant::ByteArray:
+ case QMetaType::QByteArray:
return VariantType{Blob{value.toByteArray()}};
default:
throw CannotConvert();
diff --git a/src/libs/tracing/CMakeLists.txt b/src/libs/tracing/CMakeLists.txt
index e89f31f9cc..cd0014f0d2 100644
--- a/src/libs/tracing/CMakeLists.txt
+++ b/src/libs/tracing/CMakeLists.txt
@@ -105,3 +105,7 @@ qt_add_qml_module(Tracing
SOURCES
${TRACING_CPP_SOURCES}
)
+extend_qtc_library(Tracing
+ INCLUDES
+ ../utils/theme
+)
diff --git a/src/libs/tracing/timelinenotesrenderpass.cpp b/src/libs/tracing/timelinenotesrenderpass.cpp
index 5a3939b4f3..c0cfa368b5 100644
--- a/src/libs/tracing/timelinenotesrenderpass.cpp
+++ b/src/libs/tracing/timelinenotesrenderpass.cpp
@@ -217,7 +217,7 @@ NotesMaterialShader::NotesMaterialShader()
static QColor notesColor()
{
return Utils::creatorTheme()
- ? Utils::creatorTheme()->color(Utils::Theme::Timeline_HighlightColor)
+ ? Utils::creatorColor(Utils::Theme::Timeline_HighlightColor)
: QColor(255, 165, 0);
}
diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt
index c1cf3f86a8..ca5e88b66c 100644
--- a/src/libs/utils/CMakeLists.txt
+++ b/src/libs/utils/CMakeLists.txt
@@ -94,6 +94,7 @@ add_qtc_library(Utils
link.cpp link.h
listmodel.h
listutils.h
+ lua.cpp lua.h
macroexpander.cpp macroexpander.h
mathutils.cpp mathutils.h
mimeconstants.h
@@ -198,6 +199,7 @@ add_qtc_library(Utils
utilstr.h
utilsicons.cpp utilsicons.h
utiltypes.h
+ utility.h
variablechooser.cpp variablechooser.h
winutils.cpp winutils.h
wizard.cpp wizard.h
diff --git a/src/libs/utils/aspects.cpp b/src/libs/utils/aspects.cpp
index 6ef84c119e..25a21fb739 100644
--- a/src/libs/utils/aspects.cpp
+++ b/src/libs/utils/aspects.cpp
@@ -27,6 +27,7 @@
#include <QDebug>
#include <QGroupBox>
#include <QLabel>
+#include <QLayout>
#include <QLineEdit>
#include <QListWidget>
#include <QPaintEvent>
@@ -38,6 +39,7 @@
#include <QSpinBox>
#include <QStandardItemModel>
#include <QTextEdit>
+#include <QTreeWidget>
#include <QUndoStack>
using namespace Layouting;
@@ -103,16 +105,32 @@ public:
\brief The \c BaseAspect class provides a common base for classes implementing
aspects.
- An aspect is a hunk of data like a property or collection of related
+ An \e aspect is a hunk of data like a property or collection of related
properties of some object, together with a description of its behavior
for common operations like visualizing or persisting.
- Simple aspects are for example a boolean property represented by a QCheckBox
+ Simple aspects are, for example, a boolean property represented by a QCheckBox
in the user interface, or a string property represented by a PathChooser,
- selecting directories in the filesystem.
+ for selecting directories in the filesystem.
- While aspects implementations usually have the ability to visualize and to persist
+ While aspects implementations usually can visualize and persist
their data, or use an ID, neither of these is mandatory.
+
+ The derived classes can implement addToLayout() to create a UI.
+
+ Implement \c guiToBuffer() and \c bufferToGui() to synchronize the UI with
+ the internal data.
+*/
+
+/*!
+ \enum Utils::BaseAspect::Announcement
+
+ Whether to emit a signal when a value changes.
+
+ \value DoEmit
+ Emit a signal.
+ \value BeQuiet
+ Don't emit a signal.
*/
/*!
@@ -159,7 +177,9 @@ QVariant BaseAspect::variantValue() const
/*!
Sets \a value.
- Prefer the typed setValue() of derived classes.
+ If \a howToAnnounce is set to \c DoEmit, emits the \c valueChanged signal.
+
+ Prefer the typed \c setValue() of the derived classes.
*/
void BaseAspect::setVariantValue(const QVariant &value, Announcement howToAnnounce)
{
@@ -185,7 +205,20 @@ QVariant BaseAspect::defaultVariantValue() const
}
/*!
- \fn TypedAspect::setDefaultValue(const ValueType &value)
+ \class Utils::TypedAspect
+ \inheaderfile utils/aspects.h
+ \inmodule QtCreator
+
+ \brief The \c TypedAspect class is a helper class for implementing a simple
+ aspect.
+
+ A typed aspect contains a single piece of data that is of the type
+ \c ValueType.
+*/
+
+
+/*!
+ \fn template <typename ValueType> void Utils::TypedAspect<ValueType>::setDefaultValue(const ValueType &value)
Sets a default \a value and the current value for this aspect.
@@ -248,14 +281,14 @@ QLabel *BaseAspect::createLabel()
return label;
}
-void BaseAspect::addLabeledItem(LayoutItem &parent, QWidget *widget)
+void BaseAspect::addLabeledItem(Layout &parent, QWidget *widget)
{
if (QLabel *l = createLabel()) {
l->setBuddy(widget);
parent.addItem(l);
- parent.addItem(Span(std::max(d->m_spanX - 1, 1), LayoutItem(widget)));
+ parent.addItem(Span(std::max(d->m_spanX - 1, 1), widget));
} else {
- parent.addItem(LayoutItem(widget));
+ parent.addItem(widget);
}
}
@@ -501,21 +534,20 @@ AspectContainer *BaseAspect::container() const
Adds the visual representation of this aspect to the layout with the
specified \a parent using a layout builder.
*/
-void BaseAspect::addToLayout(LayoutItem &)
+void BaseAspect::addToLayout(Layout &)
{
}
-void createItem(Layouting::LayoutItem *item, const BaseAspect &aspect)
+void addToLayout(Layouting::Layout *iface, BaseAspect &aspect)
{
- const_cast<BaseAspect &>(aspect).addToLayout(*item);
+ aspect.addToLayout(*iface);
}
-void createItem(Layouting::LayoutItem *item, const BaseAspect *aspect)
+void addToLayout(Layouting::Layout *item, BaseAspect *aspect)
{
- const_cast<BaseAspect *>(aspect)->addToLayout(*item);
+ aspect->addToLayout(*item);
}
-
/*!
Updates this aspect's value from user-initiated changes in the widget.
@@ -827,15 +859,15 @@ public:
aspect->bufferToGui();
}
- void addToLayoutFirst(LayoutItem &parent)
+ void addToLayoutFirst(Layout &parent)
{
if (m_checked && m_checkBoxPlacement == CheckBoxPlacement::Top) {
m_checked->addToLayout(parent);
- parent.addItem(br);
+ parent.flush();
}
}
- void addToLayoutLast(LayoutItem &parent)
+ void addToLayoutLast(Layout &parent)
{
if (m_checked && m_checkBoxPlacement == CheckBoxPlacement::Right)
m_checked->addToLayout(parent);
@@ -900,6 +932,10 @@ public:
class StringListAspectPrivate
{
public:
+ UndoableValue<QStringList> undoable;
+ bool allowAdding{true};
+ bool allowRemoving{true};
+ bool allowEditing{true};
};
class FilePathListAspectPrivate
@@ -939,9 +975,6 @@ public:
Based on QTextEdit, used for user-editable strings that often
do not fit on a line.
- \value PathChooserDisplay
- Based on Utils::PathChooser.
-
\value PasswordLineEditDisplay
Based on QLineEdit, used for password strings
@@ -1112,7 +1145,7 @@ void StringAspect::setAutoApplyOnEditingFinished(bool applyOnEditingFinished)
d->m_autoApplyOnEditingFinished = applyOnEditingFinished;
}
-void StringAspect::addToLayout(LayoutItem &parent)
+void StringAspect::addToLayout(Layout &parent)
{
d->m_checkerImpl.addToLayoutFirst(parent);
@@ -1416,7 +1449,10 @@ FilePath FilePathAspect::operator()() const
FilePath FilePathAspect::expandedValue() const
{
- return FilePath::fromUserInput(TypedAspect::value());
+ const auto value = TypedAspect::value();
+ if (!value.isEmpty() && d->m_expanderProvider)
+ return FilePath::fromUserInput(d->m_expanderProvider()->expand(value));
+ return FilePath::fromUserInput(value);
}
QString FilePathAspect::value() const
@@ -1425,7 +1461,9 @@ QString FilePathAspect::value() const
}
/*!
- Sets the value of this file path aspect to \a value.
+ Sets the value of this file path aspect to \a filePath.
+
+ If \a howToAnnounce is set to \c DoEmit, emits the \c valueChanged signal.
\note This does not use any check that the value is actually
a file path.
@@ -1518,7 +1556,7 @@ PathChooser *FilePathAspect::pathChooser() const
return d->m_pathChooserDisplay.data();
}
-void FilePathAspect::addToLayout(Layouting::LayoutItem &parent)
+void FilePathAspect::addToLayout(Layouting::Layout &parent)
{
d->m_checkerImpl.addToLayoutFirst(parent);
@@ -1736,7 +1774,7 @@ ColorAspect::ColorAspect(AspectContainer *container)
ColorAspect::~ColorAspect() = default;
-void ColorAspect::addToLayout(Layouting::LayoutItem &parent)
+void ColorAspect::addToLayout(Layouting::Layout &parent)
{
QTC_CHECK(!d->m_colorButton);
d->m_colorButton = createSubWidget<QtColorButton>();
@@ -1913,7 +1951,7 @@ BoolAspect::BoolAspect(AspectContainer *container)
*/
BoolAspect::~BoolAspect() = default;
-void BoolAspect::addToLayoutHelper(Layouting::LayoutItem &parent, QAbstractButton *button)
+void BoolAspect::addToLayoutHelper(Layouting::Layout &parent, QAbstractButton *button)
{
switch (d->m_labelPlacement) {
case LabelPlacement::Compact:
@@ -1922,7 +1960,7 @@ void BoolAspect::addToLayoutHelper(Layouting::LayoutItem &parent, QAbstractButto
break;
case LabelPlacement::AtCheckBox:
button->setText(labelText());
- parent.addItem(empty());
+ parent.addItem(empty);
parent.addItem(button);
break;
case LabelPlacement::InExtraLabel:
@@ -1940,20 +1978,18 @@ void BoolAspect::addToLayoutHelper(Layouting::LayoutItem &parent, QAbstractButto
});
}
-LayoutItem BoolAspect::adoptButton(QAbstractButton *button)
+std::function<void(Layouting::Layout *)> BoolAspect::adoptButton(QAbstractButton *button)
{
- LayoutItem parent;
-
- addToLayoutHelper(parent, button);
-
- bufferToGui();
- return parent;
+ return [this, button](Layouting::Layout *layout) {
+ addToLayoutHelper(*layout, button);
+ bufferToGui();
+ };
}
/*!
\reimp
*/
-void BoolAspect::addToLayout(Layouting::LayoutItem &parent)
+void BoolAspect::addToLayout(Layouting::Layout &parent)
{
QCheckBox *checkBox = createSubWidget<QCheckBox>();
addToLayoutHelper(parent, checkBox);
@@ -2059,7 +2095,7 @@ SelectionAspect::~SelectionAspect() = default;
/*!
\reimp
*/
-void SelectionAspect::addToLayout(Layouting::LayoutItem &parent)
+void SelectionAspect::addToLayout(Layouting::Layout &parent)
{
QTC_CHECK(d->m_buttonGroup == nullptr);
QTC_CHECK(!d->m_comboBox);
@@ -2233,7 +2269,7 @@ MultiSelectionAspect::~MultiSelectionAspect() = default;
/*!
\reimp
*/
-void MultiSelectionAspect::addToLayout(LayoutItem &builder)
+void MultiSelectionAspect::addToLayout(Layout &builder)
{
QTC_CHECK(d->m_listView == nullptr);
if (d->m_allValues.isEmpty())
@@ -2342,7 +2378,7 @@ IntegerAspect::~IntegerAspect() = default;
/*!
\reimp
*/
-void IntegerAspect::addToLayout(Layouting::LayoutItem &parent)
+void IntegerAspect::addToLayout(Layouting::Layout &parent)
{
QTC_CHECK(!d->m_spinBox);
d->m_spinBox = createSubWidget<QSpinBox>();
@@ -2444,7 +2480,7 @@ DoubleAspect::~DoubleAspect() = default;
/*!
\reimp
*/
-void DoubleAspect::addToLayout(LayoutItem &builder)
+void DoubleAspect::addToLayout(Layout &builder)
{
QTC_CHECK(!d->m_spinBox);
d->m_spinBox = createSubWidget<QDoubleSpinBox>();
@@ -2598,13 +2634,115 @@ StringListAspect::StringListAspect(AspectContainer *container)
*/
StringListAspect::~StringListAspect() = default;
-/*!
- \reimp
-*/
-void StringListAspect::addToLayout(LayoutItem &parent)
+bool StringListAspect::guiToBuffer()
{
- Q_UNUSED(parent)
- // TODO - when needed.
+ const QStringList newValue = d->undoable.get();
+ if (newValue != m_buffer) {
+ m_buffer = newValue;
+ return true;
+ }
+ return false;
+}
+
+void StringListAspect::bufferToGui()
+{
+ d->undoable.setWithoutUndo(m_buffer);
+}
+
+void StringListAspect::addToLayout(Layout &parent)
+{
+ d->undoable.setSilently(value());
+
+ auto editor = new QTreeWidget();
+ editor->setHeaderHidden(true);
+ editor->setRootIsDecorated(false);
+ editor->setEditTriggers(
+ d->allowEditing ? QAbstractItemView::AllEditTriggers : QAbstractItemView::NoEditTriggers);
+
+ QPushButton *add = d->allowAdding ? new QPushButton(Tr::tr("Add")) : nullptr;
+ QPushButton *remove = d->allowRemoving ? new QPushButton(Tr::tr("Remove")) : nullptr;
+
+ auto itemsToStringList = [editor] {
+ QStringList items;
+ const QTreeWidgetItem *rootItem = editor->invisibleRootItem();
+ for (int i = 0, count = rootItem->childCount(); i < count; ++i) {
+ auto expr = rootItem->child(i)->data(0, Qt::DisplayRole).toString();
+ items.append(expr);
+ }
+ return items;
+ };
+
+ auto populate = [editor, this] {
+ editor->clear();
+ for (const QString &entry : d->undoable.get()) {
+ auto item = new QTreeWidgetItem(editor, {entry});
+ item->setData(0, Qt::ToolTipRole, entry);
+ item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
+ }
+ };
+
+ connect(add, &QPushButton::clicked, this, [this, populate, editor] {
+ d->undoable.setSilently(d->undoable.get() << "");
+ populate();
+ const QTreeWidgetItem *root = editor->invisibleRootItem();
+ QTreeWidgetItem *lastChild = root->child(root->childCount() - 1);
+ const QModelIndex index = editor->indexFromItem(lastChild, 0);
+ editor->edit(index);
+ });
+
+ connect(remove, &QPushButton::clicked, this, [this, editor, itemsToStringList] {
+ const QList<QTreeWidgetItem *> selected = editor->selectedItems();
+ QTC_ASSERT(selected.size() == 1, return);
+ editor->invisibleRootItem()->removeChild(selected.first());
+ delete selected.first();
+ d->undoable.set(undoStack(), itemsToStringList());
+ });
+
+ connect(
+ &d->undoable.m_signal, &UndoSignaller::changed, editor, [this, populate, itemsToStringList] {
+ if (itemsToStringList() != d->undoable.get())
+ populate();
+
+ handleGuiChanged();
+ });
+
+ connect(
+ editor->model(),
+ &QAbstractItemModel::dataChanged,
+ this,
+ [this,
+ itemsToStringList](const QModelIndex &tl, const QModelIndex &br, const QList<int> &roles) {
+ if (!roles.contains(Qt::DisplayRole))
+ return;
+ if (tl != br)
+ return;
+ d->undoable.set(undoStack(), itemsToStringList());
+ });
+
+ populate();
+
+ parent.addItem(
+ // clang-format off
+ Column {
+ createLabel(),
+ Row {
+ editor,
+ If { d->allowAdding || d->allowRemoving, {
+ Column {
+ If { d->allowAdding, {add}, {}},
+ If { d->allowRemoving, {remove}, {}},
+ st,
+ }
+ }, {}},
+ }
+ } // clang-format on
+ );
+
+ registerSubWidget(editor);
+ if (d->allowAdding)
+ registerSubWidget(add);
+ if (d->allowRemoving)
+ registerSubWidget(remove);
}
void StringListAspect::appendValue(const QString &s, bool allowDuplicates)
@@ -2640,6 +2778,32 @@ void StringListAspect::removeValues(const QStringList &values)
setValue(val);
}
+void StringListAspect::setUiAllowAdding(bool allowAdding)
+{
+ d->allowAdding = allowAdding;
+}
+void StringListAspect::setUiAllowRemoving(bool allowRemoving)
+{
+ d->allowRemoving = allowRemoving;
+}
+void StringListAspect::setUiAllowEditing(bool allowEditing)
+{
+ d->allowEditing = allowEditing;
+}
+
+bool StringListAspect::uiAllowAdding() const
+{
+ return d->allowAdding;
+}
+bool StringListAspect::uiAllowRemoving() const
+{
+ return d->allowRemoving;
+}
+bool StringListAspect::uiAllowEditing() const
+{
+ return d->allowEditing;
+}
+
/*!
\class Utils::FilePathListAspect
\inmodule QtCreator
@@ -2677,7 +2841,7 @@ void FilePathListAspect::bufferToGui()
d->undoable.setWithoutUndo(m_buffer);
}
-void FilePathListAspect::addToLayout(LayoutItem &parent)
+void FilePathListAspect::addToLayout(Layout &parent)
{
d->undoable.setSilently(value());
@@ -2771,7 +2935,7 @@ IntegersAspect::~IntegersAspect() = default;
/*!
\reimp
*/
-void IntegersAspect::addToLayout(Layouting::LayoutItem &parent)
+void IntegersAspect::addToLayout(Layouting::Layout &parent)
{
Q_UNUSED(parent)
// TODO - when needed.
@@ -2790,8 +2954,8 @@ void IntegersAspect::addToLayout(Layouting::LayoutItem &parent)
*/
/*!
- Constructs a text display showing the \a message with an icon representing
- type \a type.
+ Constructs a text display with the parent \a container. The display shows
+ \a message and an icon representing the type \a type.
*/
TextDisplay::TextDisplay(AspectContainer *container, const QString &message, InfoLabel::InfoType type)
: BaseAspect(container), d(new Internal::TextDisplayPrivate)
@@ -2808,7 +2972,7 @@ TextDisplay::~TextDisplay() = default;
/*!
\reimp
*/
-void TextDisplay::addToLayout(LayoutItem &parent)
+void TextDisplay::addToLayout(Layout &parent)
{
if (!d->m_label) {
d->m_label = createSubWidget<InfoLabel>(d->m_message, d->m_type);
@@ -2860,7 +3024,7 @@ public:
QList<BaseAspect *> m_items; // Both owned and non-owned.
QList<BaseAspect *> m_ownedItems; // Owned only.
QStringList m_settingsGroup;
- std::function<Layouting::LayoutItem ()> m_layouter;
+ std::function<Layouting::Layout()> m_layouter;
};
AspectContainer::AspectContainer()
@@ -2875,6 +3039,11 @@ AspectContainer::~AspectContainer()
qDeleteAll(d->m_ownedItems);
}
+void AspectContainer::addToLayout(Layouting::Layout &parent)
+{
+ parent.addItem(layouter()());
+}
+
/*!
\internal
*/
@@ -2884,6 +3053,8 @@ void AspectContainer::registerAspect(BaseAspect *aspect, bool takeOwnership)
d->m_items.append(aspect);
if (takeOwnership)
d->m_ownedItems.append(aspect);
+
+ connect(aspect, &BaseAspect::changed, this, [this]() { emit changed(); });
}
void AspectContainer::registerAspects(const AspectContainer &aspects)
@@ -2912,12 +3083,12 @@ AspectContainer::const_iterator AspectContainer::end() const
return d->m_items.cend();
}
-void AspectContainer::setLayouter(const std::function<Layouting::LayoutItem ()> &layouter)
+void AspectContainer::setLayouter(const std::function<Layouting::Layout ()> &layouter)
{
d->m_layouter = layouter;
}
-std::function<LayoutItem ()> AspectContainer::layouter() const
+std::function<Layout ()> AspectContainer::layouter() const
{
return d->m_layouter;
}
@@ -3414,7 +3585,7 @@ private:
int m_index;
};
-void AspectList::addToLayout(Layouting::LayoutItem &parent)
+void AspectList::addToLayout(Layouting::Layout &parent)
{
using namespace Layouting;
@@ -3433,7 +3604,7 @@ void AspectList::addToLayout(Layouting::LayoutItem &parent)
addItem(d->createItem());
});
- Column column{noMargin()};
+ Column column{noMargin};
forEachItem<BaseAspect>([&column, this](const std::shared_ptr<BaseAspect> &item, int idx) {
auto removeBtn = new IconButton;
@@ -3524,7 +3695,7 @@ bool StringSelectionAspect::guiToBuffer()
return oldBuffer != m_buffer;
}
-void StringSelectionAspect::addToLayout(Layouting::LayoutItem &parent)
+void StringSelectionAspect::addToLayout(Layouting::Layout &parent)
{
QTC_ASSERT(m_fillCallback, return);
@@ -3597,4 +3768,4 @@ void StringSelectionAspect::addToLayout(Layouting::LayoutItem &parent)
return addLabeledItem(parent, comboBox);
}
-} // namespace Utils
+} // Utils
diff --git a/src/libs/utils/aspects.h b/src/libs/utils/aspects.h
index a5c22bb858..47aeb35458 100644
--- a/src/libs/utils/aspects.h
+++ b/src/libs/utils/aspects.h
@@ -29,9 +29,7 @@ class QStandardItemModel;
class QItemSelectionModel;
QT_END_NAMESPACE
-namespace Layouting {
-class LayoutItem;
-}
+namespace Layouting { class Layout; }
namespace Utils {
@@ -64,6 +62,7 @@ class QTCREATOR_UTILS_EXPORT BaseAspect : public QObject
public:
BaseAspect(AspectContainer *container = nullptr);
+ BaseAspect(const BaseAspect &) = delete;
~BaseAspect() override;
Id id() const;
@@ -125,9 +124,7 @@ public:
virtual void toMap(Store &map) const;
virtual void toActiveMap(Store &map) const { toMap(map); }
virtual void volatileToMap(Store &map) const;
-
- virtual void addToLayout(Layouting::LayoutItem &parent);
-
+ virtual void addToLayout(Layouting::Layout &parent);
virtual void readSettings();
virtual void writeSettings() const;
@@ -223,7 +220,7 @@ protected:
virtual void handleGuiChanged();
QLabel *createLabel();
- void addLabeledItem(Layouting::LayoutItem &parent, QWidget *widget);
+ void addLabeledItem(Layouting::Layout &parent, QWidget *widget);
void setDataCreatorHelper(const DataCreator &creator) const;
void setDataClonerHelper(const DataCloner &cloner) const;
@@ -276,8 +273,8 @@ private:
friend class Internal::CheckableAspectImplementation;
};
-QTCREATOR_UTILS_EXPORT void createItem(Layouting::LayoutItem *item, const BaseAspect &aspect);
-QTCREATOR_UTILS_EXPORT void createItem(Layouting::LayoutItem *item, const BaseAspect *aspect);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layouting::Layout *layout, BaseAspect *aspect);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layouting::Layout *layout, BaseAspect &aspect);
template<typename ValueType>
class
@@ -439,7 +436,7 @@ public:
BoolAspect(AspectContainer *container = nullptr);
~BoolAspect() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
std::function<void(QObject *)> groupChecker();
Utils::CheckableDecider askAgainCheckableDecider();
@@ -452,10 +449,10 @@ public:
LabelPlacement labelPlacement = LabelPlacement::InExtraLabel);
void setLabelPlacement(LabelPlacement labelPlacement);
- Layouting::LayoutItem adoptButton(QAbstractButton *button);
+ std::function<void(Layouting::Layout *)> adoptButton(QAbstractButton *button);
private:
- void addToLayoutHelper(Layouting::LayoutItem &parent, QAbstractButton *button);
+ void addToLayoutHelper(Layouting::Layout &parent, QAbstractButton *button);
void bufferToGui() override;
bool guiToBuffer() override;
@@ -504,7 +501,7 @@ public:
ColorAspect(AspectContainer *container = nullptr);
~ColorAspect() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
private:
void bufferToGui() override;
@@ -521,7 +518,7 @@ public:
SelectionAspect(AspectContainer *container = nullptr);
~SelectionAspect() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
void finish() override;
QString stringValue() const;
@@ -569,7 +566,7 @@ public:
MultiSelectionAspect(AspectContainer *container = nullptr);
~MultiSelectionAspect() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
enum class DisplayStyle { ListView };
void setDisplayStyle(DisplayStyle style);
@@ -596,7 +593,7 @@ public:
StringAspect(AspectContainer *container = nullptr);
~StringAspect() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
QString operator()() const { return expandedValue(); }
QString expandedValue() const;
@@ -703,7 +700,7 @@ public:
PathChooser *pathChooser() const; // Avoid to use.
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
void fromMap(const Utils::Store &map) override;
void toMap(Utils::Store &map) const override;
@@ -730,7 +727,7 @@ public:
IntegerAspect(AspectContainer *container = nullptr);
~IntegerAspect() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
void setRange(qint64 min, qint64 max);
void setLabel(const QString &label); // FIXME: Use setLabelText
@@ -759,7 +756,7 @@ public:
DoubleAspect(AspectContainer *container = nullptr);
~DoubleAspect() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
void setRange(double min, double max);
void setPrefix(const QString &prefix);
@@ -831,13 +828,24 @@ public:
StringListAspect(AspectContainer *container = nullptr);
~StringListAspect() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ bool guiToBuffer() override;
+ void bufferToGui() override;
+
+ void addToLayout(Layouting::Layout &parent) override;
void appendValue(const QString &value, bool allowDuplicates = true);
void removeValue(const QString &value);
void appendValues(const QStringList &values, bool allowDuplicates = true);
void removeValues(const QStringList &values);
+ void setUiAllowAdding(bool allowAdding);
+ void setUiAllowRemoving(bool allowRemoving);
+ void setUiAllowEditing(bool allowEditing);
+
+ bool uiAllowAdding() const;
+ bool uiAllowRemoving() const;
+ bool uiAllowEditing() const;
+
private:
std::unique_ptr<Internal::StringListAspectPrivate> d;
};
@@ -855,7 +863,7 @@ public:
bool guiToBuffer() override;
void bufferToGui() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
void setPlaceHolderText(const QString &placeHolderText);
void appendValue(const FilePath &path, bool allowDuplicates = true);
@@ -875,7 +883,7 @@ public:
IntegersAspect(AspectContainer *container = nullptr);
~IntegersAspect() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
};
class QTCREATOR_UTILS_EXPORT TextDisplay : public BaseAspect
@@ -888,7 +896,7 @@ public:
InfoLabel::InfoType type = InfoLabel::None);
~TextDisplay() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
void setIconType(InfoLabel::InfoType t);
void setText(const QString &message);
@@ -939,6 +947,8 @@ public:
AspectContainer(const AspectContainer &) = delete;
AspectContainer &operator=(const AspectContainer &) = delete;
+ void addToLayout(Layouting::Layout &parent) override;
+
void registerAspect(BaseAspect *aspect, bool takeOwnership = false);
void registerAspects(const AspectContainer &aspects);
@@ -989,8 +999,8 @@ public:
const_iterator begin() const;
const_iterator end() const;
- void setLayouter(const std::function<Layouting::LayoutItem()> &layouter);
- std::function<Layouting::LayoutItem()> layouter() const;
+ void setLayouter(const std::function<Layouting::Layout()> &layouter);
+ std::function<Layouting::Layout()> layouter() const;
signals:
void applied();
@@ -1131,7 +1141,7 @@ public:
QVariant volatileVariantValue() const override { return {}; }
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
private:
std::unique_ptr<Internal::AspectListPrivate> d;
@@ -1143,7 +1153,7 @@ class QTCREATOR_UTILS_EXPORT StringSelectionAspect : public Utils::TypedAspect<Q
public:
StringSelectionAspect(Utils::AspectContainer *container = nullptr);
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
using ResultCallback = std::function<void(QList<QStandardItem *> items)>;
using FillCallback = std::function<void(ResultCallback)>;
diff --git a/src/libs/utils/async.h b/src/libs/utils/async.h
index af6fb3fd85..8c7155bc13 100644
--- a/src/libs/utils/async.h
+++ b/src/libs/utils/async.h
@@ -7,6 +7,7 @@
#include "futuresynchronizer.h"
#include "qtcassert.h"
+#include "threadutils.h"
#include <solutions/tasking/tasktree.h>
@@ -130,7 +131,9 @@ template <typename ResultType>
class Async : public AsyncBase
{
public:
- Async() {
+ Async()
+ : m_synchronizer(isMainThread() ? futureSynchronizer() : nullptr)
+ {
connect(&m_watcher, &QFutureWatcherBase::finished, this, &AsyncBase::done);
connect(&m_watcher, &QFutureWatcherBase::resultReadyAt, this, &AsyncBase::resultReadyAt);
}
diff --git a/src/libs/utils/changeset.cpp b/src/libs/utils/changeset.cpp
index c0c54b1e48..881097c6b7 100644
--- a/src/libs/utils/changeset.cpp
+++ b/src/libs/utils/changeset.cpp
@@ -100,6 +100,41 @@ void ChangeSet::clear()
m_error = false;
}
+ChangeSet ChangeSet::makeReplace(const Range &range, const QString &replacement)
+{
+ ChangeSet c;
+ c.replace(range, replacement);
+ return c;
+}
+
+ChangeSet ChangeSet::makeReplace(int start, int end, const QString &replacement)
+{
+ ChangeSet c;
+ c.replace(start, end, replacement);
+ return c;
+}
+
+ChangeSet ChangeSet::makeRemove(const Range &range)
+{
+ ChangeSet c;
+ c.remove(range);
+ return c;
+}
+
+ChangeSet ChangeSet::makeFlip(int start1, int end1, int start2, int end2)
+{
+ ChangeSet c;
+ c.flip(start1, end1, start2, end2);
+ return c;
+}
+
+ChangeSet ChangeSet::makeInsert(int pos, const QString &text)
+{
+ ChangeSet c;
+ c.insert(pos, text);
+ return c;
+}
+
bool ChangeSet::replace_helper(int pos, int length, const QString &replacement)
{
if (hasOverlap(pos, length))
diff --git a/src/libs/utils/changeset.h b/src/libs/utils/changeset.h
index 6370b20d3c..aee9c99dab 100644
--- a/src/libs/utils/changeset.h
+++ b/src/libs/utils/changeset.h
@@ -76,6 +76,12 @@ public:
void clear();
+ static ChangeSet makeReplace(const Range &range, const QString &replacement);
+ static ChangeSet makeReplace(int start, int end, const QString &replacement);
+ static ChangeSet makeRemove(const Range &range);
+ static ChangeSet makeFlip(int start1, int end1, int start2, int end2);
+ static ChangeSet makeInsert(int pos, const QString &text);
+
bool replace(const Range &range, const QString &replacement);
bool remove(const Range &range);
bool move(const Range &range, int to);
diff --git a/src/libs/utils/checkablemessagebox.cpp b/src/libs/utils/checkablemessagebox.cpp
index 19d3669ada..1d721e04c8 100644
--- a/src/libs/utils/checkablemessagebox.cpp
+++ b/src/libs/utils/checkablemessagebox.cpp
@@ -3,6 +3,7 @@
#include "checkablemessagebox.h"
+#include "guiutils.h"
#include "hostosinfo.h"
#include "qtcassert.h"
#include "qtcsettings.h"
@@ -95,7 +96,7 @@ static QMessageBox::StandardButton exec(
return acceptButton;
}
- QMessageBox msgBox(parent);
+ QMessageBox msgBox(dialogParent(parent));
prepare(icon, title, text, decider, buttons, defaultButton, buttonTextOverrides, msg, msgBox);
msgBox.exec();
@@ -131,7 +132,7 @@ static void show(QWidget *parent,
return;
}
- QMessageBox *msgBox = new QMessageBox(parent);
+ QMessageBox *msgBox = new QMessageBox(dialogParent(parent));
prepare(icon, title, text, decider, buttons, defaultButton, buttonTextOverrides, msg, *msgBox);
std::optional<QPointer<QObject>> guardPtr;
diff --git a/src/libs/utils/clangutils.cpp b/src/libs/utils/clangutils.cpp
index 4c8c7d801d..86e8e6204e 100644
--- a/src/libs/utils/clangutils.cpp
+++ b/src/libs/utils/clangutils.cpp
@@ -65,7 +65,7 @@ bool checkClangdVersion(const FilePath &clangd, QString *error)
QVersionNumber minimumClangdVersion()
{
- return QVersionNumber(14);
+ return QVersionNumber(17);
}
} // namespace Utils
diff --git a/src/libs/utils/commandline.cpp b/src/libs/utils/commandline.cpp
index 8e6bc60277..b4fa60481e 100644
--- a/src/libs/utils/commandline.cpp
+++ b/src/libs/utils/commandline.cpp
@@ -1425,6 +1425,19 @@ CommandLine::CommandLine(const FilePath &exe, const QStringList &args)
addArgs(args);
}
+CommandLine::CommandLine(const FilePath &exe, std::initializer_list<ArgRef> args)
+ : m_executable(exe)
+{
+ for (const ArgRef &arg : args) {
+ if (const auto ptr = std::get_if<const char *>(&arg.m_arg))
+ addArg(QString::fromUtf8(*ptr));
+ else if (const auto ptr = std::get_if<std::reference_wrapper<const QString>>(&arg.m_arg))
+ addArg(*ptr);
+ else if (const auto ptr = std::get_if<std::reference_wrapper<const QStringList>>(&arg.m_arg))
+ addArgs(*ptr);
+ }
+}
+
CommandLine::CommandLine(const FilePath &exe, const QStringList &args, OsType osType)
: m_executable(exe)
{
diff --git a/src/libs/utils/commandline.h b/src/libs/utils/commandline.h
index 52ff8c5496..3718f80bca 100644
--- a/src/libs/utils/commandline.h
+++ b/src/libs/utils/commandline.h
@@ -11,6 +11,8 @@
#include <QPair>
#include <QStringList>
+#include <variant>
+
namespace Utils {
class AbstractMacroExpander;
@@ -120,8 +122,22 @@ public:
CommandLine();
~CommandLine();
+ struct ArgRef
+ {
+ ArgRef(const char *arg) : m_arg(arg) {}
+ ArgRef(const QString &arg) : m_arg(arg) {}
+ ArgRef(const QStringList &args) : m_arg(args) {}
+
+ private:
+ friend class CommandLine;
+ const std::variant<const char *,
+ std::reference_wrapper<const QString>,
+ std::reference_wrapper<const QStringList>> m_arg;
+ };
+
explicit CommandLine(const FilePath &executable);
CommandLine(const FilePath &exe, const QStringList &args);
+ CommandLine(const FilePath &exe, std::initializer_list<ArgRef> args);
CommandLine(const FilePath &exe, const QStringList &args, OsType osType);
CommandLine(const FilePath &exe, const QString &unparsedArgs, RawType);
diff --git a/src/libs/utils/crumblepath.cpp b/src/libs/utils/crumblepath.cpp
index 779b1c9e55..d434350460 100644
--- a/src/libs/utils/crumblepath.cpp
+++ b/src/libs/utils/crumblepath.cpp
@@ -111,9 +111,9 @@ void CrumblePathButton::paintEvent(QPaintEvent*)
p.drawPixmap(width() - overlapSize, segmentRect.top(), middleSegmentPixmap);
if (option.state & QStyle::State_Enabled)
- option.palette.setColor(QPalette::ButtonText, creatorTheme()->color(Theme::PanelTextColorLight));
+ option.palette.setColor(QPalette::ButtonText, creatorColor(Theme::PanelTextColorLight));
else
- option.palette.setColor(QPalette::Disabled, QPalette::ButtonText, creatorTheme()->color(Theme::IconsDisabledColor));
+ option.palette.setColor(QPalette::Disabled, QPalette::ButtonText, creatorColor(Theme::IconsDisabledColor));
QStylePainter sp(this);
if (option.state & QStyle::State_Sunken)
diff --git a/src/libs/utils/detailsbutton.cpp b/src/libs/utils/detailsbutton.cpp
index 88c0ca273e..c6e1b6da3f 100644
--- a/src/libs/utils/detailsbutton.cpp
+++ b/src/libs/utils/detailsbutton.cpp
@@ -87,8 +87,8 @@ QColor DetailsButton::outlineColor()
{
return HostOsInfo::isMacHost()
? QGuiApplication::palette().color(QPalette::Mid)
- : StyleHelper::mergedColors(creatorTheme()->color(Theme::TextColorNormal),
- creatorTheme()->color(Theme::BackgroundColorNormal), 15);
+ : StyleHelper::mergedColors(creatorColor(Theme::TextColorNormal),
+ creatorColor(Theme::BackgroundColorNormal), 15);
}
void DetailsButton::paintEvent(QPaintEvent *e)
diff --git a/src/libs/utils/detailswidget.cpp b/src/libs/utils/detailswidget.cpp
index ec7a3d22eb..d278a609ff 100644
--- a/src/libs/utils/detailswidget.cpp
+++ b/src/libs/utils/detailswidget.cpp
@@ -224,7 +224,7 @@ void DetailsWidget::paintEvent(QPaintEvent *paintEvent)
QPainter p(this);
if (creatorTheme()->flag(Theme::FlatProjectsMode) || HostOsInfo::isMacHost()) {
const QColor bgColor = creatorTheme()->flag(Theme::FlatProjectsMode) ?
- creatorTheme()->color(Theme::DetailsWidgetBackgroundColor)
+ creatorColor(Theme::DetailsWidgetBackgroundColor)
: palette().color(QPalette::Window);
p.fillRect(rect(), bgColor);
}
diff --git a/src/libs/utils/execmenu.cpp b/src/libs/utils/execmenu.cpp
index 13d7915295..4e463bdd6f 100644
--- a/src/libs/utils/execmenu.cpp
+++ b/src/libs/utils/execmenu.cpp
@@ -44,7 +44,8 @@ QAction *execMenuAtWidget(QMenu *menu, QWidget *widget)
}
/*!
- Adds tool tips to the menu that show the actions tool tip when hovering over an entry.
+ Adds tool tips to the \a menu that show the action's tool tip when hovering
+ over an entry.
*/
void addToolTipsToMenu(QMenu *menu)
{
diff --git a/src/libs/utils/externalterminalprocessimpl.cpp b/src/libs/utils/externalterminalprocessimpl.cpp
index 41b7259b8f..693905bddc 100644
--- a/src/libs/utils/externalterminalprocessimpl.cpp
+++ b/src/libs/utils/externalterminalprocessimpl.cpp
@@ -181,7 +181,7 @@ expected_str<qint64> ProcessStubCreator::startStubProcess(const ProcessSetupData
process->setProcessMode(ProcessMode::Writer);
} else {
QString extraArgsFromOptions = terminal.executeArgs;
- CommandLine cmdLine = {terminal.command, {}};
+ CommandLine cmdLine{terminal.command};
if (!extraArgsFromOptions.isEmpty())
cmdLine.addArgs(extraArgsFromOptions, CommandLine::Raw);
cmdLine.addCommandLineAsArgs(setupData.m_commandLine, CommandLine::Raw);
@@ -190,8 +190,6 @@ expected_str<qint64> ProcessStubCreator::startStubProcess(const ProcessSetupData
process->setEnvironment(
setupData.m_environment.appliedToEnvironment(Environment::systemEnvironment()));
- process->setEnvironment(setupData.m_environment);
-
process->start();
process->waitForStarted();
if (process->error() != QProcess::UnknownError) {
diff --git a/src/libs/utils/fancylineedit.cpp b/src/libs/utils/fancylineedit.cpp
index 44a9423608..69d7209c3a 100644
--- a/src/libs/utils/fancylineedit.cpp
+++ b/src/libs/utils/fancylineedit.cpp
@@ -5,6 +5,7 @@
#include "camelcasecursor.h"
#include "execmenu.h"
+#include "futuresynchronizer.h"
#include "historycompleter.h"
#include "hostosinfo.h"
#include "icon.h"
@@ -98,6 +99,7 @@ class FancyLineEditPrivate : public QObject
{
public:
explicit FancyLineEditPrivate(FancyLineEdit *parent);
+ ~FancyLineEditPrivate();
bool eventFilter(QObject *obj, QEvent *event) override;
@@ -134,8 +136,8 @@ FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent)
: QObject(parent)
, m_lineEdit(parent)
, m_completionShortcut(completionShortcut()->key(), parent)
- , m_okTextColor(creatorTheme()->color(Theme::TextColorNormal))
- , m_errorTextColor(creatorTheme()->color(Theme::TextColorError))
+ , m_okTextColor(creatorColor(Theme::TextColorNormal))
+ , m_errorTextColor(creatorColor(Theme::TextColorError))
, m_placeholderTextColor(QApplication::palette().color(QPalette::PlaceholderText))
, m_spinner(new SpinnerSolution::Spinner(SpinnerSolution::SpinnerSize::Small, m_lineEdit))
{
@@ -163,6 +165,12 @@ FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent)
}
}
+FancyLineEditPrivate::~FancyLineEditPrivate()
+{
+ if (m_validatorWatcher)
+ m_validatorWatcher->cancel();
+}
+
bool FancyLineEditPrivate::eventFilter(QObject *obj, QEvent *event)
{
int buttonIndex = -1;
@@ -446,12 +454,27 @@ void FancyLineEdit::setFiltering(bool on)
}
}
+/*!
+ Set a synchronous or asynchronous validation function \a fn.
+ Asynchronous validation functions can continue to run after destruction of the
+ FancyLineEdit instance. During shutdown asynchronous validation functions can continue
+ to run until before the plugin instances are deleted (at that point the plugin manager
+ waits for them to finish before continuing).
+
+ \sa defaultValidationFunction()
+ */
void FancyLineEdit::setValidationFunction(const FancyLineEdit::ValidationFunction &fn)
{
d->m_validationFunction = fn;
validate();
}
+/*!
+ Returns the default validation function, which synchonously executes the line edit's
+ validator.
+
+ \sa setValidationFunction()
+*/
FancyLineEdit::ValidationFunction FancyLineEdit::defaultValidationFunction()
{
return &FancyLineEdit::validateWithValidator;
@@ -581,6 +604,7 @@ void FancyLineEdit::validate()
AsyncValidationFuture future = validationFunction(text());
d->m_validatorWatcher->setFuture(future);
+ Utils::futureSynchronizer()->addFuture(future);
return;
}
diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp
index b62f222475..0965a52b1f 100644
--- a/src/libs/utils/filepath.cpp
+++ b/src/libs/utils/filepath.cpp
@@ -1367,7 +1367,7 @@ FilePath FilePath::fromUtf8(const char *filename, int filenameSize)
FilePath FilePath::fromSettings(const QVariant &variant)
{
- if (variant.type() == QVariant::Url) {
+ if (variant.typeId() == QMetaType::QUrl) {
const QUrl url = variant.toUrl();
return FilePath::fromParts(url.scheme(), url.host(), url.path());
}
diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp
index b3649a457d..d82353e246 100644
--- a/src/libs/utils/fileutils.cpp
+++ b/src/libs/utils/fileutils.cpp
@@ -5,7 +5,6 @@
#include "savefile.h"
#include "algorithm.h"
-#include "devicefileaccess.h"
#include "environment.h"
#include "qtcassert.h"
#include "utilstr.h"
@@ -24,6 +23,8 @@
#include <qplatformdefs.h>
#ifdef QT_GUI_LIB
+#include "guiutils.h"
+
#include <QMessageBox>
#include <QGuiApplication>
#endif
@@ -414,18 +415,6 @@ void withNtfsPermissions(const std::function<void()> &task)
#ifdef QT_WIDGETS_LIB
-static std::function<QWidget *()> s_dialogParentGetter;
-
-void FileUtils::setDialogParentGetter(const std::function<QWidget *()> &getter)
-{
- s_dialogParentGetter = getter;
-}
-
-static QWidget *dialogParent(QWidget *parent)
-{
- return parent ? parent : s_dialogParentGetter ? s_dialogParentGetter() : nullptr;
-}
-
static QUrl filePathToQUrl(const FilePath &filePath)
{
return QUrl::fromLocalFile(filePath.toFSPathString());
diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h
index b20f7568db..bdf7a7e3ba 100644
--- a/src/libs/utils/fileutils.h
+++ b/src/libs/utils/fileutils.h
@@ -89,8 +89,6 @@ public:
static FilePaths usefulExtraSearchPaths();
#ifdef QT_WIDGETS_LIB
- static void setDialogParentGetter(const std::function<QWidget *()> &getter);
-
static bool hasNativeFileDialog();
static FilePath getOpenFilePath(QWidget *parent,
@@ -236,7 +234,5 @@ private:
QTCREATOR_UTILS_EXPORT QTextStream &operator<<(QTextStream &s, const FilePath &fn);
-bool isRelativePathHelper(const QString &path, OsType osType);
-
} // namespace Utils
diff --git a/src/libs/utils/futuresynchronizer.cpp b/src/libs/utils/futuresynchronizer.cpp
index da3347f35e..912de677b9 100644
--- a/src/libs/utils/futuresynchronizer.cpp
+++ b/src/libs/utils/futuresynchronizer.cpp
@@ -3,6 +3,9 @@
#include "futuresynchronizer.h"
+#include "qtcassert.h"
+#include "threadutils.h"
+
/*!
\class Utils::FutureSynchronizer
\inmodule QtCreator
@@ -62,4 +65,18 @@ void FutureSynchronizer::flushFinishedFutures()
m_futures = newFutures;
}
+Q_GLOBAL_STATIC(FutureSynchronizer, s_futureSynchronizer);
+
+/*!
+ Returns a global FutureSynchronizer.
+ The application should cancel and wait for the tasks in this synchronizer before actually
+ unloading any libraries. This is for example done by the plugin manager in Qt Creator.
+ May only be accessed by the main thread.
+*/
+FutureSynchronizer *futureSynchronizer()
+{
+ QTC_ASSERT(isMainThread(), return nullptr);
+ return s_futureSynchronizer;
+}
+
} // namespace Utils
diff --git a/src/libs/utils/futuresynchronizer.h b/src/libs/utils/futuresynchronizer.h
index 29b1f5e456..83391dbc72 100644
--- a/src/libs/utils/futuresynchronizer.h
+++ b/src/libs/utils/futuresynchronizer.h
@@ -42,4 +42,6 @@ private:
bool m_cancelOnWait = true;
};
+QTCREATOR_UTILS_EXPORT FutureSynchronizer *futureSynchronizer();
+
} // namespace Utils
diff --git a/src/libs/utils/guiutils.cpp b/src/libs/utils/guiutils.cpp
index 17cc905300..d7d9cb5f84 100644
--- a/src/libs/utils/guiutils.cpp
+++ b/src/libs/utils/guiutils.cpp
@@ -46,4 +46,16 @@ void QTCREATOR_UTILS_EXPORT setWheelScrollingWithoutFocusBlocked(QWidget *widget
widget->setFocusPolicy(Qt::StrongFocus);
}
+static QWidget *(*s_dialogParentGetter)() = nullptr;
+
+void setDialogParentGetter(QWidget *(*getter)())
+{
+ s_dialogParentGetter = getter;
+}
+
+QWidget *dialogParent(QWidget *parent)
+{
+ return parent ? parent : s_dialogParentGetter ? s_dialogParentGetter() : nullptr;
+}
+
} // namespace Utils
diff --git a/src/libs/utils/guiutils.h b/src/libs/utils/guiutils.h
index 8316377718..fea37f98f3 100644
--- a/src/libs/utils/guiutils.h
+++ b/src/libs/utils/guiutils.h
@@ -9,6 +9,9 @@ class QWidget;
namespace Utils {
-void QTCREATOR_UTILS_EXPORT setWheelScrollingWithoutFocusBlocked(QWidget *widget);
+QTCREATOR_UTILS_EXPORT void setWheelScrollingWithoutFocusBlocked(QWidget *widget);
+
+QTCREATOR_UTILS_EXPORT QWidget *dialogParent(QWidget *parent);
+QTCREATOR_UTILS_EXPORT void setDialogParentGetter(QWidget *(*getter)());
} // namespace Utils
diff --git a/src/libs/utils/highlightingitemdelegate.cpp b/src/libs/utils/highlightingitemdelegate.cpp
index 7fe0507311..9ad7abb01a 100644
--- a/src/libs/utils/highlightingitemdelegate.cpp
+++ b/src/libs/utils/highlightingitemdelegate.cpp
@@ -91,7 +91,7 @@ int HighlightingItemDelegate::drawLineNumber(QPainter *painter, const QStyleOpti
return 0;
const bool isSelected = option.state & QStyle::State_Selected;
const QString lineText = QString::number(lineNumber);
- const int minimumLineNumberDigits = qMax(kMinimumLineNumberDigits, lineText.count());
+ const int minimumLineNumberDigits = qMax(kMinimumLineNumberDigits, lineText.size());
const int fontWidth =
painter->fontMetrics().horizontalAdvance(QString(minimumLineNumberDigits, '0'));
const int lineNumberAreaWidth = lineNumberAreaHorizontalPadding + fontWidth
diff --git a/src/libs/utils/icon.cpp b/src/libs/utils/icon.cpp
index 5bd862c8f1..132494e825 100644
--- a/src/libs/utils/icon.cpp
+++ b/src/libs/utils/icon.cpp
@@ -42,7 +42,7 @@ static MasksAndColors masksAndColors(const QList<IconMaskAndColor> &icon, int dp
MasksAndColors result;
for (const IconMaskAndColor &i: icon) {
const QString &fileName = i.first.toString();
- const QColor color = creatorTheme()->color(i.second);
+ const QColor color = creatorColor(i.second);
const QString dprFileName = StyleHelper::availableImageResolutions(i.first.toString())
.contains(dpr)
? StyleHelper::imageFileWithResolution(fileName, dpr)
@@ -165,7 +165,7 @@ QIcon Icon::icon() const
const QPixmap combinedMask = Utils::combinedMask(masks, m_style);
m_lastIcon.addPixmap(masksToIcon(masks, combinedMask, m_style));
- const QColor disabledColor = creatorTheme()->color(Theme::IconsDisabledColor);
+ const QColor disabledColor = creatorColor(Theme::IconsDisabledColor);
m_lastIcon.addPixmap(maskToColorAndAlpha(combinedMask, disabledColor), QIcon::Disabled);
}
return m_lastIcon;
@@ -182,7 +182,7 @@ QPixmap Icon::pixmap(QIcon::Mode iconMode) const
masksAndColors(m_iconSourceList, qRound(qApp->devicePixelRatio()));
const QPixmap combinedMask = Utils::combinedMask(masks, m_style);
return iconMode == QIcon::Disabled
- ? maskToColorAndAlpha(combinedMask, creatorTheme()->color(Theme::IconsDisabledColor))
+ ? maskToColorAndAlpha(combinedMask, creatorColor(Theme::IconsDisabledColor))
: masksToIcon(masks, combinedMask, m_style);
}
}
diff --git a/src/libs/utils/iconbutton.cpp b/src/libs/utils/iconbutton.cpp
index 746cf0ecb1..3be368c43b 100644
--- a/src/libs/utils/iconbutton.cpp
+++ b/src/libs/utils/iconbutton.cpp
@@ -26,7 +26,7 @@ void IconButton::paintEvent(QPaintEvent *e)
QRect r(QPoint(), size());
if (m_containsMouse && isEnabled()) {
- QColor c = creatorTheme()->color(Theme::TextColorDisabled);
+ QColor c = creatorColor(Theme::TextColorDisabled);
c.setAlphaF(c.alphaF() * .5);
StyleHelper::drawPanelBgRect(&p, r, c);
}
diff --git a/src/libs/utils/infobar.cpp b/src/libs/utils/infobar.cpp
index bd6429a762..dc5fb4878d 100644
--- a/src/libs/utils/infobar.cpp
+++ b/src/libs/utils/infobar.cpp
@@ -47,10 +47,10 @@ void InfoBarWidget::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
QPainter p(this);
- p.fillRect(rect(), creatorTheme()->color(Theme::InfoBarBackground));
+ p.fillRect(rect(), creatorColor(Theme::InfoBarBackground));
const QRectF adjustedRect = QRectF(rect()).adjusted(0.5, 0.5, -0.5, -0.5);
const bool topEdge = m_edge == Qt::TopEdge;
- p.setPen(creatorTheme()->color(Theme::FancyToolBarSeparatorColor));
+ p.setPen(creatorColor(Theme::FancyToolBarSeparatorColor));
p.drawLine(QLineF(topEdge ? adjustedRect.bottomLeft() : adjustedRect.topLeft(),
topEdge ? adjustedRect.bottomRight() : adjustedRect.topRight()));
}
diff --git a/src/libs/utils/infolabel.cpp b/src/libs/utils/infolabel.cpp
index dbc762d9fe..9eb9f80106 100644
--- a/src/libs/utils/infolabel.cpp
+++ b/src/libs/utils/infolabel.cpp
@@ -114,7 +114,7 @@ void InfoLabel::paintEvent(QPaintEvent *event)
if (m_filled && isEnabled()) {
p.save();
p.setOpacity(0.175);
- p.fillRect(rect(), creatorTheme()->color(fillColorForType(m_type)));
+ p.fillRect(rect(), creatorColor(fillColorForType(m_type)));
p.restore();
}
const QIcon &icon = iconForType(m_type);
diff --git a/src/libs/utils/layoutbuilder.cpp b/src/libs/utils/layoutbuilder.cpp
index 31c67e4d0f..cd4fde8110 100644
--- a/src/libs/utils/layoutbuilder.cpp
+++ b/src/libs/utils/layoutbuilder.cpp
@@ -3,7 +3,6 @@
#include "layoutbuilder.h"
-#include <QApplication>
#include <QDebug>
#include <QFormLayout>
#include <QGridLayout>
@@ -29,10 +28,23 @@ namespace Layouting {
#define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_STRING(#cond); action; } do {} while (0)
#define QTC_CHECK(cond) if (cond) {} else { QTC_STRING(#cond); } do {} while (0)
-class FlowLayout final : public QLayout
+template <typename X>
+typename X::Implementation *access(const X *x)
{
- Q_OBJECT
+ return static_cast<typename X::Implementation *>(x->ptr);
+}
+
+template <typename X>
+void apply(X *x, std::initializer_list<typename X::I> ps)
+{
+ for (auto && p : ps)
+ p.apply(x);
+}
+
+// FlowLayout
+class FlowLayout : public QLayout
+{
public:
explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1)
: QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
@@ -181,29 +193,49 @@ private:
\namespace Layouting
\inmodule QtCreator
- \brief The Layouting namespace contains classes for use with layout builders.
+ \brief The Layouting namespace contains classes and functions to conveniently
+ create layouts in code.
+
+ Classes in the namespace help to create create QLayout or QWidget derived class,
+ instances should be used locally within a function and never stored.
+
+ \sa Layouting::Widget, Layouting::Layout
*/
/*!
- \class Layouting::LayoutItem
+ \class Layouting::Layout
\inmodule QtCreator
- \brief The LayoutItem class represents widgets, layouts, and aggregate
- items for use in conjunction with layout builders.
+ The Layout class is a base class for more specific builder
+ classes to create QLayout derived objects.
+ */
+
+/*!
+ \class Layouting::Widget
+ \inmodule QtCreator
- Layout items are typically implicitly constructed when adding items to a
- \c LayoutBuilder instance using \c LayoutBuilder::addItem() or
- \c LayoutBuilder::addItems() and never stored in user code.
+ The Widget class is a base class for more specific builder
+ classes to create QWidget derived objects.
*/
/*!
- Constructs a layout item instance representing an empty cell.
- */
+ \class Layouting::LayoutItem
+ \inmodule QtCreator
+
+ The LayoutItem class is used for intermediate results
+ while creating layouts with a concept of rows and spans, such
+ as Form and Grid.
+*/
+
LayoutItem::LayoutItem() = default;
LayoutItem::~LayoutItem() = default;
+LayoutItem::LayoutItem(const LayoutModifier &inner)
+{
+ ownerModifier = inner;
+}
/*!
\fn template <class T> LayoutItem(const T &t)
@@ -217,44 +249,15 @@ LayoutItem::~LayoutItem() = default;
\li \c {QWidget *}
\li \c {QLayout *}
\endlist
- */
-
-struct ResultItem
-{
- ResultItem() = default;
- explicit ResultItem(QLayout *l) : layout(l), empty(!l) {}
- explicit ResultItem(QWidget *w) : widget(w), empty(!w) {}
+*/
- QString text;
- QLayout *layout = nullptr;
- QWidget *widget = nullptr;
- int space = -1;
- int stretch = -1;
- int span = 1;
- bool empty = false;
-};
+// Object
-struct Slice
+Object::Object(std::initializer_list<I> ps)
{
- Slice() = default;
- Slice(QLayout *l) : layout(l) {}
- Slice(QWidget *w, bool isLayouting=false) : widget(w), isLayouting(isLayouting) {}
-
- QLayout *layout = nullptr;
- QWidget *widget = nullptr;
-
- void flush();
-
- // Grid-specific
- int currentGridColumn = 0;
- int currentGridRow = 0;
- bool isFormAlignment = false;
- bool isLayouting = false;
- Qt::Alignment align = {}; // Can be changed to
-
- // Grid or Form
- QList<ResultItem> pendingItems;
-};
+ ptr = new Implementation;
+ apply(this, ps);
+}
static QWidget *widgetForItem(QLayoutItem *item)
{
@@ -262,12 +265,11 @@ static QWidget *widgetForItem(QLayoutItem *item)
return w;
if (item->spacerItem())
return nullptr;
- QLayout *l = item->layout();
- if (!l)
- return nullptr;
- for (int i = 0, n = l->count(); i < n; ++i) {
- if (QWidget *w = widgetForItem(l->itemAt(i)))
- return w;
+ if (QLayout *l = item->layout()) {
+ for (int i = 0, n = l->count(); i < n; ++i) {
+ if (QWidget *w = widgetForItem(l->itemAt(i)))
+ return w;
+ }
}
return nullptr;
}
@@ -279,7 +281,7 @@ static QLabel *createLabel(const QString &text)
return label;
}
-static void addItemToBoxLayout(QBoxLayout *layout, const ResultItem &item)
+static void addItemToBoxLayout(QBoxLayout *layout, const LayoutItem &item)
{
if (QWidget *w = item.widget) {
layout->addWidget(w);
@@ -287,8 +289,6 @@ static void addItemToBoxLayout(QBoxLayout *layout, const ResultItem &item)
layout->addLayout(l);
} else if (item.stretch != -1) {
layout->addStretch(item.stretch);
- } else if (item.space != -1) {
- layout->addSpacing(item.space);
} else if (!item.text.isEmpty()) {
layout->addWidget(createLabel(item.text));
} else if (item.empty) {
@@ -298,7 +298,7 @@ static void addItemToBoxLayout(QBoxLayout *layout, const ResultItem &item)
}
}
-static void addItemToFlowLayout(FlowLayout *layout, const ResultItem &item)
+static void addItemToFlowLayout(FlowLayout *layout, const LayoutItem &item)
{
if (QWidget *w = item.widget) {
layout->addWidget(w);
@@ -306,8 +306,6 @@ static void addItemToFlowLayout(FlowLayout *layout, const ResultItem &item)
layout->addItem(l);
// } else if (item.stretch != -1) {
// layout->addStretch(item.stretch);
-// } else if (item.space != -1) {
-// layout->addSpacing(item.space);
} else if (item.empty) {
// Nothing to do, but no reason to warn, either
} else if (!item.text.isEmpty()) {
@@ -317,697 +315,615 @@ static void addItemToFlowLayout(FlowLayout *layout, const ResultItem &item)
}
}
-void Slice::flush()
-{
- if (pendingItems.empty())
- return;
-
- if (auto formLayout = qobject_cast<QFormLayout *>(layout)) {
-
- // If there are more than two items, we cram the last ones in one hbox.
- if (pendingItems.size() > 2) {
- auto hbox = new QHBoxLayout;
- hbox->setContentsMargins(0, 0, 0, 0);
- for (int i = 1; i < pendingItems.size(); ++i)
- addItemToBoxLayout(hbox, pendingItems.at(i));
- while (pendingItems.size() > 1)
- pendingItems.pop_back();
- pendingItems.append(ResultItem(hbox));
- }
-
- if (pendingItems.size() == 1) { // One one item given, so this spans both columns.
- const ResultItem &f0 = pendingItems.at(0);
- if (auto layout = f0.layout)
- formLayout->addRow(layout);
- else if (auto widget = f0.widget)
- formLayout->addRow(widget);
- } else if (pendingItems.size() == 2) { // Normal case, both columns used.
- ResultItem &f1 = pendingItems[1];
- const ResultItem &f0 = pendingItems.at(0);
- if (!f1.widget && !f1.layout && !f1.text.isEmpty())
- f1.widget = createLabel(f1.text);
-
- if (f0.widget) {
- if (f1.layout)
- formLayout->addRow(f0.widget, f1.layout);
- else if (f1.widget)
- formLayout->addRow(f0.widget, f1.widget);
- } else {
- if (f1.layout)
- formLayout->addRow(createLabel(f0.text), f1.layout);
- else if (f1.widget)
- formLayout->addRow(createLabel(f0.text), f1.widget);
- }
- } else {
- QTC_CHECK(false);
- }
-
- // Set up label as buddy if possible.
- const int lastRow = formLayout->rowCount() - 1;
- QLayoutItem *l = formLayout->itemAt(lastRow, QFormLayout::LabelRole);
- QLayoutItem *f = formLayout->itemAt(lastRow, QFormLayout::FieldRole);
- if (l && f) {
- if (QLabel *label = qobject_cast<QLabel *>(l->widget())) {
- if (QWidget *widget = widgetForItem(f))
- label->setBuddy(widget);
- }
- }
-
- } else if (auto gridLayout = qobject_cast<QGridLayout *>(layout)) {
-
- for (const ResultItem &item : std::as_const(pendingItems)) {
- Qt::Alignment a = currentGridColumn == 0 ? align : Qt::Alignment();
- if (item.widget)
- gridLayout->addWidget(item.widget, currentGridRow, currentGridColumn, 1, item.span, a);
- else if (item.layout)
- gridLayout->addLayout(item.layout, currentGridRow, currentGridColumn, 1, item.span, a);
- else if (!item.text.isEmpty())
- gridLayout->addWidget(createLabel(item.text), currentGridRow, currentGridColumn, 1, 1, a);
- currentGridColumn += item.span;
- }
- ++currentGridRow;
- currentGridColumn = 0;
+/*!
+ \class Layouting::Space
+ \inmodule QtCreator
- } else if (auto boxLayout = qobject_cast<QBoxLayout *>(layout)) {
+ \brief The Space class represents some empty space in a layout.
+ */
- for (const ResultItem &item : std::as_const(pendingItems))
- addItemToBoxLayout(boxLayout, item);
+/*!
+ \class Layouting::Stretch
+ \inmodule QtCreator
- } else if (auto flowLayout = qobject_cast<FlowLayout *>(layout)) {
+ \brief The Stretch class represents some stretch in a layout.
+ */
- for (const ResultItem &item : std::as_const(pendingItems))
- addItemToFlowLayout(flowLayout, item);
- } else {
- QTC_CHECK(false);
- }
+// Layout
- pendingItems.clear();
+void Layout::span(int cols, int rows)
+{
+ QTC_ASSERT(!pendingItems.empty(), return);
+ pendingItems.back().spanCols = cols;
+ pendingItems.back().spanRows = rows;
}
-// LayoutBuilder
-
-class LayoutBuilder
+void Layout::noMargin()
{
- Q_DISABLE_COPY_MOVE(LayoutBuilder)
-
-public:
- LayoutBuilder();
- ~LayoutBuilder();
-
- void addItem(const LayoutItem &item);
- void addItems(const LayoutItems &items);
-
- QList<Slice> stack;
-};
+ customMargin({});
+}
-static void addItemHelper(LayoutBuilder &builder, const LayoutItem &item)
+void Layout::normalMargin()
{
- if (item.onAdd)
- item.onAdd(builder);
-
- if (item.setter) {
- if (QWidget *widget = builder.stack.last().widget)
- item.setter(widget);
- else if (QLayout *layout = builder.stack.last().layout)
- item.setter(layout);
- else
- QTC_CHECK(false);
- }
-
- for (const LayoutItem &subItem : item.subItems)
- addItemHelper(builder, subItem);
-
- if (item.onExit)
- item.onExit(builder);
+ customMargin({9, 9, 9, 9});
}
-void doAddText(LayoutBuilder &builder, const QString &text)
+void Layout::customMargin(const QMargins &margin)
{
- ResultItem fi;
- fi.text = text;
- builder.stack.last().pendingItems.append(fi);
+ access(this)->setContentsMargins(margin);
}
-void doAddSpace(LayoutBuilder &builder, const Space &space)
+/*!
+ Attaches the constructed layout to the provided QWidget \a w.
+
+ This operation can only be performed once per LayoutBuilder instance.
+ */
+void Layout::attachTo(QWidget *widget)
{
- ResultItem fi;
- fi.space = space.space;
- builder.stack.last().pendingItems.append(fi);
+ flush();
+ widget->setLayout(access(this));
}
-void doAddStretch(LayoutBuilder &builder, const Stretch &stretch)
+/*!
+ Adds the layout item \a item as sub items.
+ */
+void Layout::addItem(I item)
{
- ResultItem fi;
- fi.stretch = stretch.stretch;
- builder.stack.last().pendingItems.append(fi);
+ item.apply(this);
}
-void doAddLayout(LayoutBuilder &builder, QLayout *layout)
+void Layout::addLayoutItem(const LayoutItem &item)
{
- builder.stack.last().pendingItems.append(ResultItem(layout));
+ if (QBoxLayout *lt = asBox())
+ addItemToBoxLayout(lt, item);
+ else if (FlowLayout *lt = asFlow())
+ addItemToFlowLayout(lt, item);
+ else
+ pendingItems.push_back(item);
}
-void doAddWidget(LayoutBuilder &builder, QWidget *widget)
+/*!
+ Adds the layout items \a items as sub items.
+ */
+void Layout::addItems(std::initializer_list<I> items)
{
- builder.stack.last().pendingItems.append(ResultItem(widget));
+ for (const I &item : items)
+ item.apply(this);
}
-
/*!
- \class Layouting::Space
- \inmodule QtCreator
+ Starts a new row containing \a items. The row can be further extended by
+ other items using \c addItem() or \c addItems().
- \brief The Space class represents some empty space in a layout.
+ \sa addItem(), addItems()
*/
-/*!
- \class Layouting::Stretch
- \inmodule QtCreator
+void Layout::addRow(std::initializer_list<I> items)
+{
+ for (const I &item : items)
+ item.apply(this);
+ flush();
+}
- \brief The Stretch class represents some stretch in a layout.
- */
+void Layout::setSpacing(int spacing)
+{
+ access(this)->setSpacing(spacing);
+}
-/*!
- \class Layouting::LayoutBuilder
- \internal
- \inmodule QtCreator
+void Layout::setColumnStretch(int column, int stretch)
+{
+ if (auto grid = qobject_cast<QGridLayout *>(access(this))) {
+ grid->setColumnStretch(column, stretch);
+ } else {
+ QTC_CHECK(false);
+ }
+}
- \brief The LayoutBuilder class provides a convenient way to fill \c QFormLayout
- and \c QGridLayouts with contents.
+void addToWidget(Widget *widget, const Layout &layout)
+{
+ layout.flush_();
+ access(widget)->setLayout(access(&layout));
+}
- Filling a layout with items happens item-by-item, row-by-row.
+void addToLayout(Layout *layout, const Widget &inner)
+{
+ LayoutItem item;
+ item.widget = access(&inner);
+ layout->addLayoutItem(item);
+}
- A LayoutBuilder instance is typically used locally within a function and never stored.
+void addToLayout(Layout *layout, QWidget *inner)
+{
+ LayoutItem item;
+ item.widget = inner;
+ layout->addLayoutItem(item);
+}
- \sa addItem(), addItems()
-*/
+void addToLayout(Layout *layout, QLayout *inner)
+{
+ LayoutItem item;
+ item.layout = inner;
+ layout->addLayoutItem(item);
+}
+void addToLayout(Layout *layout, const Layout &inner)
+{
+ inner.flush_();
+ LayoutItem item;
+ item.layout = access(&inner);
+ layout->addLayoutItem(item);
+}
-LayoutBuilder::LayoutBuilder() = default;
+void addToLayout(Layout *layout, const LayoutModifier &inner)
+{
+ inner(layout);
+}
-/*!
- \internal
- Destructs a layout builder.
- */
-LayoutBuilder::~LayoutBuilder() = default;
+void addToLayout(Layout *layout, const QString &inner)
+{
+ LayoutItem item;
+ item.text = inner;
+ layout->addLayoutItem(item);
+}
-void LayoutBuilder::addItem(const LayoutItem &item)
+void empty(Layout *iface)
{
- addItemHelper(*this, item);
+ LayoutItem item;
+ item.empty = true;
+ iface->addLayoutItem(item);
}
-void LayoutBuilder::addItems(const LayoutItems &items)
+void hr(Layout *layout)
{
- for (const LayoutItem &item : items)
- addItemHelper(*this, item);
+ layout->addLayoutItem(createHr());
}
-/*!
- Starts a new row containing \a items. The row can be further extended by
- other items using \c addItem() or \c addItems().
+void br(Layout *iface)
+{
+ iface->flush();
+}
- \sa addItem(), addItems()
- */
-void LayoutItem::addRow(const LayoutItems &items)
+void st(Layout *iface)
{
- addItem(br);
- addItems(items);
+ LayoutItem item;
+ item.stretch = 1;
+ iface->addLayoutItem(item);
}
-/*!
- Adds the layout item \a item as sub items.
- */
-void LayoutItem::addItem(const LayoutItem &item)
+void noMargin(Layout *iface)
{
- subItems.append(item);
+ iface->noMargin();
}
-/*!
- Adds the layout items \a items as sub items.
- */
-void LayoutItem::addItems(const LayoutItems &items)
+void normalMargin(Layout *iface)
{
- subItems.append(items);
+ iface->normalMargin();
}
-/*!
- Attaches the constructed layout to the provided QWidget \a w.
+QFormLayout *Layout::asForm()
+{
+ return qobject_cast<QFormLayout *>(access(this));
+}
- This operation can only be performed once per LayoutBuilder instance.
- */
+QGridLayout *Layout::asGrid()
+{
+ return qobject_cast<QGridLayout *>(access(this));
+}
-void LayoutItem::attachTo(QWidget *w) const
+QBoxLayout *Layout::asBox()
{
- LayoutBuilder builder;
+ return qobject_cast<QBoxLayout *>(access(this));
+}
- builder.stack.append(w);
- addItemHelper(builder, *this);
+FlowLayout *Layout::asFlow()
+{
+ return dynamic_cast<FlowLayout *>(access(this));
}
-QWidget *LayoutItem::emerge()
+void Layout::flush()
{
- LayoutBuilder builder;
+ if (pendingItems.empty())
+ return;
+
+ if (QGridLayout *lt = asGrid()) {
+ for (const LayoutItem &item : std::as_const(pendingItems)) {
+ Qt::Alignment a;
+ if (currentGridColumn == 0 && useFormAlignment) {
+ // if (auto widget = builder.stack.at(builder.stack.size() - 2).widget) {
+ // a = widget->style()->styleHint(QStyle::SH_FormLayoutLabelAlignment);
+ }
+ if (item.widget)
+ lt->addWidget(item.widget, currentGridRow, currentGridColumn, item.spanRows, item.spanCols, a);
+ else if (item.layout)
+ lt->addLayout(item.layout, currentGridRow, currentGridColumn, item.spanRows, item.spanCols, a);
+ else if (!item.text.isEmpty())
+ lt->addWidget(createLabel(item.text), currentGridRow, currentGridColumn, item.spanRows, item.spanCols, a);
+ currentGridColumn += item.spanCols;
+ // Intentionally not used, use 'br'/'empty' for vertical progress.
+ // currentGridRow += item.spanRows;
+ }
+ ++currentGridRow;
+ currentGridColumn = 0;
+ pendingItems.clear();
+ return;
+ }
- builder.stack.append(Slice());
- addItemHelper(builder, *this);
+ if (QFormLayout *fl = asForm()) {
+ if (pendingItems.size() > 2) {
+ auto hbox = new QHBoxLayout;
+ hbox->setContentsMargins(0, 0, 0, 0);
+ for (size_t i = 1; i < pendingItems.size(); ++i)
+ addItemToBoxLayout(hbox, pendingItems.at(i));
+ while (pendingItems.size() > 1)
+ pendingItems.pop_back();
+ pendingItems.push_back(hbox);
+ }
- if (builder.stack.empty())
- return nullptr;
+ if (pendingItems.size() == 1) { // Only one item given, so this spans both columns.
+ const LayoutItem &f0 = pendingItems.at(0);
+ if (auto layout = f0.layout)
+ fl->addRow(layout);
+ else if (auto widget = f0.widget)
+ fl->addRow(widget);
+ } else if (pendingItems.size() == 2) { // Normal case, both columns used.
+ LayoutItem &f1 = pendingItems[1];
+ const LayoutItem &f0 = pendingItems.at(0);
+ if (!f1.widget && !f1.layout && !f1.text.isEmpty())
+ f1.widget = createLabel(f1.text);
- QTC_ASSERT(builder.stack.last().pendingItems.size() == 1, return nullptr);
- ResultItem ri = builder.stack.last().pendingItems.takeFirst();
+ // QFormLayout accepts only widgets or text in the first column.
+ // FIXME: Should we be more generous?
+ if (f0.widget) {
+ if (f1.layout)
+ fl->addRow(f0.widget, f1.layout);
+ else if (f1.widget)
+ fl->addRow(f0.widget, f1.widget);
+ } else {
+ if (f1.layout)
+ fl->addRow(createLabel(f0.text), f1.layout);
+ else if (f1.widget)
+ fl->addRow(createLabel(f0.text), f1.widget);
+ }
+ } else {
+ QTC_CHECK(false);
+ }
- QTC_ASSERT(ri.layout || ri.widget, return nullptr);
+ // Set up label as buddy if possible.
+ const int lastRow = fl->rowCount() - 1;
+ QLayoutItem *l = fl->itemAt(lastRow, QFormLayout::LabelRole);
+ QLayoutItem *f = fl->itemAt(lastRow, QFormLayout::FieldRole);
+ if (l && f) {
+ if (QLabel *label = qobject_cast<QLabel *>(l->widget())) {
+ if (QWidget *widget = widgetForItem(f))
+ label->setBuddy(widget);
+ }
+ }
- if (ri.layout) {
- auto w = new QWidget;
- w->setLayout(ri.layout);
- return w;
+ pendingItems.clear();
+ return;
}
- return ri.widget;
+ QTC_CHECK(false); // The other layouts shouldn't use flush()
}
-static void layoutExit(LayoutBuilder &builder)
+void Layout::flush_() const
{
- builder.stack.last().flush();
- QLayout *layout = builder.stack.last().layout;
- builder.stack.pop_back();
-
- if (builder.stack.last().isLayouting) {
- builder.stack.last().pendingItems.append(ResultItem(layout));
- } else if (QWidget *widget = builder.stack.last().widget) {
- widget->setLayout(layout);
- } else
- builder.stack.last().pendingItems.append(ResultItem(layout));
+ const_cast<Layout *>(this)->flush();
}
-template<class T>
-static void layoutingWidgetExit(LayoutBuilder &builder)
+void withFormAlignment(Layout *iface)
{
- const Slice slice = builder.stack.last();
- T *w = qobject_cast<T *>(slice.widget);
- for (const ResultItem &ri : slice.pendingItems) {
- if (ri.widget) {
- w->addWidget(ri.widget);
- } else if (ri.layout) {
- auto child = new QWidget;
- child->setLayout(ri.layout);
- w->addWidget(child);
- }
- }
- builder.stack.pop_back();
- builder.stack.last().pendingItems.append(ResultItem(w));
+ iface->useFormAlignment = true;
}
-static void widgetExit(LayoutBuilder &builder)
+// Flow
+
+Flow::Flow(std::initializer_list<I> ps)
{
- QWidget *widget = builder.stack.last().widget;
- builder.stack.pop_back();
- builder.stack.last().pendingItems.append(ResultItem(widget));
+ ptr = new FlowLayout;
+ apply(this, ps);
+ flush();
}
-Column::Column(std::initializer_list<LayoutItem> items)
+// Row & Column
+
+Row::Row(std::initializer_list<I> ps)
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QVBoxLayout); };
- onExit = layoutExit;
+ ptr = new QHBoxLayout;
+ apply(this, ps);
+ flush();
}
-Row::Row(std::initializer_list<LayoutItem> items)
+Column::Column(std::initializer_list<I> ps)
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QHBoxLayout); };
- onExit = layoutExit;
+ ptr = new QVBoxLayout;
+ apply(this, ps);
+ flush();
}
-Flow::Flow(std::initializer_list<LayoutItem> items)
+// Grid
+
+Grid::Grid()
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(new FlowLayout); };
- onExit = layoutExit;
+ ptr = new QGridLayout;
}
-Grid::Grid(std::initializer_list<LayoutItem> items)
+Grid::Grid(std::initializer_list<I> ps)
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QGridLayout); };
- onExit = layoutExit;
+ ptr = new QGridLayout;
+ apply(this, ps);
+ flush();
}
-static QFormLayout *newFormLayout()
+// Form
+
+Form::Form()
{
- auto formLayout = new QFormLayout;
- formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
- return formLayout;
+ ptr = new QFormLayout;
}
-Form::Form(std::initializer_list<LayoutItem> items)
+Form::Form(std::initializer_list<I> ps)
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(newFormLayout()); };
- onExit = layoutExit;
+ auto lt = new QFormLayout;
+ ptr = lt;
+ lt->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
+ apply(this, ps);
+ flush();
}
-LayoutItem br()
+void Layout::fieldGrowthPolicy(int policy)
{
- LayoutItem item;
- item.onAdd = [](LayoutBuilder &builder) {
- builder.stack.last().flush();
- };
- return item;
+ if (auto lt = asForm())
+ lt->setFieldGrowthPolicy(QFormLayout::FieldGrowthPolicy(policy));
}
-LayoutItem empty()
+QWidget *Layout::emerge() const
{
- LayoutItem item;
- item.onAdd = [](LayoutBuilder &builder) {
- ResultItem ri;
- ri.empty = true;
- builder.stack.last().pendingItems.append(ri);
- };
- return item;
+ const_cast<Layout *>(this)->flush();
+ QWidget *widget = new QWidget;
+ widget->setLayout(access(this));
+ return widget;
}
-LayoutItem hr()
+void Layout::show() const
{
- LayoutItem item;
- item.onAdd = [](LayoutBuilder &builder) { doAddWidget(builder, createHr()); };
- return item;
+ return emerge()->show();
}
-LayoutItem st()
+// "Widgets"
+
+Widget::Widget(std::initializer_list<I> ps)
{
- LayoutItem item;
- item.onAdd = [](LayoutBuilder &builder) { doAddStretch(builder, Stretch(1)); };
- return item;
+ ptr = new Implementation;
+ apply(this, ps);
}
-LayoutItem noMargin()
+void Widget::resize(int w, int h)
{
- return customMargin({});
+ access(this)->resize(w, h);
}
-LayoutItem normalMargin()
+void Widget::setLayout(const Layout &layout)
{
- return customMargin({9, 9, 9, 9});
+ access(this)->setLayout(access(&layout));
}
-LayoutItem customMargin(const QMargins &margin)
+void Widget::setWindowTitle(const QString &title)
{
- LayoutItem item;
- item.onAdd = [margin](LayoutBuilder &builder) {
- if (auto layout = builder.stack.last().layout)
- layout->setContentsMargins(margin);
- else if (auto widget = builder.stack.last().widget)
- widget->setContentsMargins(margin);
- };
- return item;
+ access(this)->setWindowTitle(title);
}
-LayoutItem withFormAlignment()
+void Widget::setToolTip(const QString &title)
{
- LayoutItem item;
- item.onAdd = [](LayoutBuilder &builder) {
- if (builder.stack.size() >= 2) {
- if (auto widget = builder.stack.at(builder.stack.size() - 2).widget) {
- const Qt::Alignment align(widget->style()->styleHint(QStyle::SH_FormLayoutLabelAlignment));
- builder.stack.last().align = align;
- }
- }
- };
- return item;
+ access(this)->setToolTip(title);
}
-// "Widgets"
+void Widget::show()
+{
+ access(this)->show();
+}
-template <class T>
-void setupWidget(LayoutItem *item)
+void Widget::noMargin(int)
{
- item->onAdd = [](LayoutBuilder &builder) { builder.stack.append(new T); };
- item->onExit = widgetExit;
-};
+ customMargin({});
+}
-Widget::Widget(std::initializer_list<LayoutItem> items)
+void Widget::normalMargin(int)
{
- this->subItems = items;
- setupWidget<QWidget>(this);
+ customMargin({9, 9, 9, 9});
}
-Group::Group(std::initializer_list<LayoutItem> items)
+void Widget::customMargin(const QMargins &margin)
{
- this->subItems = items;
- setupWidget<QGroupBox>(this);
+ access(this)->setContentsMargins(margin);
}
-Stack::Stack(std::initializer_list<LayoutItem> items)
+QWidget *Widget::emerge() const
{
- // We use a QStackedWidget instead of a QStackedLayout here because the latter will call
- // "setVisible()" when a child is added, which can lead to the widget being spawned as a
- // top-level widget. This can lead to the focus shifting away from the main application.
- subItems = items;
- onAdd = [](LayoutBuilder &builder) {
- builder.stack.append(Slice(new QStackedWidget, true));
- };
- onExit = layoutingWidgetExit<QStackedWidget>;
+ return access(this);
}
-PushButton::PushButton(std::initializer_list<LayoutItem> items)
+// Label
+
+Label::Label(std::initializer_list<I> ps)
{
- this->subItems = items;
- setupWidget<QPushButton>(this);
+ ptr = new Implementation;
+ apply(this, ps);
}
-SpinBox::SpinBox(std::initializer_list<LayoutItem> items)
+Label::Label(const QString &text)
{
- this->subItems = items;
- setupWidget<QSpinBox>(this);
+ ptr = new Implementation;
+ setText(text);
}
-TextEdit::TextEdit(std::initializer_list<LayoutItem> items)
+void Label::setText(const QString &text)
{
- this->subItems = items;
- setupWidget<QTextEdit>(this);
+ access(this)->setText(text);
}
-Splitter::Splitter(std::initializer_list<LayoutItem> items)
+// Group
+
+Group::Group(std::initializer_list<I> ps)
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) {
- auto splitter = new QSplitter;
- splitter->setOrientation(Qt::Vertical);
- builder.stack.append(Slice(splitter, true));
- };
- onExit = layoutingWidgetExit<QSplitter>;
+ ptr = new Implementation;
+ apply(this, ps);
}
-ToolBar::ToolBar(std::initializer_list<LayoutItem> items)
+void Group::setTitle(const QString &title)
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) {
- auto toolbar = new QToolBar;
- toolbar->setOrientation(Qt::Horizontal);
- builder.stack.append(Slice(toolbar, true));
- };
- onExit = layoutingWidgetExit<QToolBar>;
+ access(this)->setTitle(title);
+ access(this)->setObjectName(title);
}
-TabWidget::TabWidget(std::initializer_list<LayoutItem> items)
+void Group::setGroupChecker(const std::function<void (QObject *)> &checker)
{
- this->subItems = items;
- setupWidget<QTabWidget>(this);
+ checker(access(this));
}
-// Special Tab
+// SpinBox
-Tab::Tab(const QString &tabName, const LayoutItem &item)
+SpinBox::SpinBox(std::initializer_list<I> ps)
{
- onAdd = [item](LayoutBuilder &builder) {
- auto tab = new QWidget;
- builder.stack.append(tab);
- item.attachTo(tab);
- };
- onExit = [tabName](LayoutBuilder &builder) {
- QWidget *inner = builder.stack.last().widget;
- builder.stack.pop_back();
- auto tabWidget = qobject_cast<QTabWidget *>(builder.stack.last().widget);
- QTC_ASSERT(tabWidget, return);
- tabWidget->addTab(inner, tabName);
- };
+ ptr = new Implementation;
+ apply(this, ps);
}
-// Special If
+void SpinBox::setValue(int val)
+{
+ access(this)->setValue(val);
+}
-If::If(bool condition, const LayoutItems &items, const LayoutItems &other)
+void SpinBox::onTextChanged(const std::function<void (QString)> &func)
{
- subItems.append(condition ? items : other);
+ QObject::connect(access(this), &QSpinBox::textChanged, func);
}
-// Special Application
+// TextEdit
-Application::Application(std::initializer_list<LayoutItem> items)
+TextEdit::TextEdit(std::initializer_list<I> ps)
{
- subItems = items;
- setupWidget<QWidget>(this);
- onExit = {}; // Hack: Don't dropp the last slice, we need the resulting widget.
+ ptr = new Implementation;
+ apply(this, ps);
}
-int Application::exec(int &argc, char *argv[])
+void TextEdit::setText(const QString &text)
{
- QApplication app(argc, argv);
- LayoutBuilder builder;
- addItemHelper(builder, *this);
- if (QWidget *widget = builder.stack.last().widget)
- widget->show();
- return app.exec();
+ access(this)->setText(text);
}
-// "Properties"
+// PushButton
-LayoutItem title(const QString &title)
+PushButton::PushButton(std::initializer_list<I> ps)
{
- return [title](QObject *target) {
- if (auto groupBox = qobject_cast<QGroupBox *>(target)) {
- groupBox->setTitle(title);
- groupBox->setObjectName(title);
- } else if (auto widget = qobject_cast<QWidget *>(target)) {
- widget->setWindowTitle(title);
- } else {
- QTC_CHECK(false);
- }
- };
+ ptr = new Implementation;
+ apply(this, ps);
}
-LayoutItem windowTitle(const QString &windowTitle)
+void PushButton::setText(const QString &text)
{
- return [windowTitle](QObject *target) {
- if (auto widget = qobject_cast<QWidget *>(target)) {
- widget->setWindowTitle(windowTitle);
- } else {
- QTC_CHECK(false);
- }
- };
+ access(this)->setText(text);
}
-LayoutItem text(const QString &text)
+void PushButton::onClicked(const std::function<void ()> &func, QObject *guard)
{
- return [text](QObject *target) {
- if (auto button = qobject_cast<QAbstractButton *>(target)) {
- button->setText(text);
- } else if (auto textEdit = qobject_cast<QTextEdit *>(target)) {
- textEdit->setText(text);
- } else {
- QTC_CHECK(false);
- }
- };
+ QObject::connect(access(this), &QAbstractButton::clicked, guard, func);
}
-LayoutItem tooltip(const QString &toolTip)
+// Stack
+
+// We use a QStackedWidget instead of a QStackedLayout here because the latter will call
+// "setVisible()" when a child is added, which can lead to the widget being spawned as a
+// top-level widget. This can lead to the focus shifting away from the main application.
+Stack::Stack(std::initializer_list<I> ps)
{
- return [toolTip](QObject *target) {
- if (auto widget = qobject_cast<QWidget *>(target)) {
- widget->setToolTip(toolTip);
- } else {
- QTC_CHECK(false);
- }
- };
+ ptr = new Implementation;
+ apply(this, ps);
}
-LayoutItem spacing(int spacing)
+void addToStack(Stack *stack, const Widget &inner)
{
- return [spacing](QObject *target) {
- if (auto layout = qobject_cast<QLayout *>(target)) {
- layout->setSpacing(spacing);
- } else {
- QTC_CHECK(false);
- }
- };
+ access(stack)->addWidget(inner.emerge());
}
-LayoutItem resize(int w, int h)
+void addToStack(Stack *stack, const Layout &inner)
{
- return [w, h](QObject *target) {
- if (auto widget = qobject_cast<QWidget *>(target)) {
- widget->resize(w, h);
- } else {
- QTC_CHECK(false);
- }
- };
+ inner.flush_();
+ access(stack)->addWidget(inner.emerge());
}
-LayoutItem columnStretch(int column, int stretch)
+void addToStack(Stack *stack, QWidget *inner)
{
- return [column, stretch](QObject *target) {
- if (auto grid = qobject_cast<QGridLayout *>(target)) {
- grid->setColumnStretch(column, stretch);
- } else {
- QTC_CHECK(false);
- }
- };
+ access(stack)->addWidget(inner);
}
-LayoutItem fieldGrowthPolicy(QFormLayout::FieldGrowthPolicy policy)
+// Splitter
+
+Splitter::Splitter(std::initializer_list<I> ps)
{
- return [policy](QObject *target) {
- if (auto form = qobject_cast<QFormLayout *>(target)) {
- form->setFieldGrowthPolicy(policy);
- } else {
- QTC_CHECK(false);
- }
- };
+ ptr = new Implementation;
+ access(this)->setOrientation(Qt::Vertical);
+ apply(this, ps);
}
+void addToSplitter(Splitter *splitter, QWidget *inner)
+{
+ access(splitter)->addWidget(inner);
+}
-// Id based setters
+void addToSplitter(Splitter *splitter, const Widget &inner)
+{
+ access(splitter)->addWidget(inner.emerge());
+}
-LayoutItem id(ID &out)
+void addToSplitter(Splitter *splitter, const Layout &inner)
{
- return [&out](QObject *target) { out.ob = target; };
+ inner.flush_();
+ access(splitter)->addWidget(inner.emerge());
}
-void setText(ID id, const QString &text)
+// ToolBar
+
+ToolBar::ToolBar(std::initializer_list<I> ps)
{
- if (auto textEdit = qobject_cast<QTextEdit *>(id.ob))
- textEdit->setText(text);
+ ptr = new Implementation;
+ apply(this, ps);
+ access(this)->setOrientation(Qt::Horizontal);
}
-// Signals
+// TabWidget
-LayoutItem onClicked(const std::function<void ()> &func, QObject *guard)
+TabWidget::TabWidget(std::initializer_list<I> ps)
{
- return [func, guard](QObject *target) {
- if (auto button = qobject_cast<QAbstractButton *>(target)) {
- QObject::connect(button, &QAbstractButton::clicked, guard ? guard : target, func);
- } else {
- QTC_CHECK(false);
- }
- };
+ ptr = new Implementation;
+ apply(this, ps);
}
-LayoutItem onTextChanged(const std::function<void (const QString &)> &func, QObject *guard)
+Tab::Tab(const QString &tabName, const Layout &inner)
+ : tabName(tabName), inner(inner)
+{}
+
+void addToTabWidget(TabWidget *tabWidget, const Tab &tab)
{
- return [func, guard](QObject *target) {
- if (auto button = qobject_cast<QSpinBox *>(target)) {
- QObject::connect(button, &QSpinBox::textChanged, guard ? guard : target, func);
- } else {
- QTC_CHECK(false);
- }
- };
+ access(tabWidget)->addTab(tab.inner.emerge(), tab.tabName);
}
-LayoutItem onValueChanged(const std::function<void (int)> &func, QObject *guard)
+// Special If
+
+If::If(bool condition,
+ const std::initializer_list<Layout::I> ifcase,
+ const std::initializer_list<Layout::I> thencase)
+ : used(condition ? ifcase : thencase)
+{}
+
+void addToLayout(Layout *layout, const If &inner)
{
- return [func, guard](QObject *target) {
- if (auto button = qobject_cast<QSpinBox *>(target)) {
- QObject::connect(button, &QSpinBox::valueChanged, guard ? guard : target, func);
- } else {
- QTC_CHECK(false);
- }
- };
+ for (const Layout::I &item : inner.used)
+ item.apply(layout);
}
-// Convenience
+// Specials
QWidget *createHr(QWidget *parent)
{
@@ -1017,59 +933,49 @@ QWidget *createHr(QWidget *parent)
return frame;
}
-// Singletons.
+Span::Span(int cols, const Layout::I &item)
+ : item(item), spanCols(cols)
+{}
-LayoutItem::LayoutItem(const LayoutItem &t)
-{
- operator=(t);
-}
-
-void createItem(LayoutItem *item, LayoutItem(*t)())
-{
- *item = t();
-}
+Span::Span(int cols, int rows, const Layout::I &item)
+ : item(item), spanCols(cols), spanRows(rows)
+{}
-void createItem(LayoutItem *item, const std::function<void(QObject *target)> &t)
+void addToLayout(Layout *layout, const Span &inner)
{
- item->setter = t;
+ layout->addItem(inner.item);
+ if (layout->pendingItems.empty()) {
+ QTC_CHECK(inner.spanCols == 1 && inner.spanRows == 1);
+ return;
+ }
+ layout->pendingItems.back().spanCols = inner.spanCols;
+ layout->pendingItems.back().spanRows = inner.spanRows;
}
-void createItem(LayoutItem *item, QWidget *t)
+LayoutModifier spacing(int space)
{
- if (auto l = qobject_cast<QLabel *>(t))
- l->setTextInteractionFlags(l->textInteractionFlags() | Qt::TextSelectableByMouse);
-
- item->onAdd = [t](LayoutBuilder &builder) { doAddWidget(builder, t); };
+ return [space](Layout *iface) { iface->setSpacing(space); };
}
-void createItem(LayoutItem *item, QLayout *t)
+void addToLayout(Layout *layout, const Space &inner)
{
- item->onAdd = [t](LayoutBuilder &builder) { doAddLayout(builder, t); };
+ if (auto lt = layout->asBox())
+ lt->addSpacing(inner.space);
}
-void createItem(LayoutItem *item, const QString &t)
+void addToLayout(Layout *layout, const Stretch &inner)
{
- item->onAdd = [t](LayoutBuilder &builder) { doAddText(builder, t); };
+ if (auto lt = layout->asBox())
+ lt->addStretch(inner.stretch);
}
-void createItem(LayoutItem *item, const Space &t)
-{
- item->onAdd = [t](LayoutBuilder &builder) { doAddSpace(builder, t); };
-}
+// void createItem(LayoutItem *item, QWidget *t)
+// {
+// if (auto l = qobject_cast<QLabel *>(t))
+// l->setTextInteractionFlags(l->textInteractionFlags() | Qt::TextSelectableByMouse);
-void createItem(LayoutItem *item, const Stretch &t)
-{
- item->onAdd = [t](LayoutBuilder &builder) { doAddStretch(builder, t); };
-}
+// item->onAdd = [t](LayoutBuilder &builder) { doAddWidget(builder, t); };
+// }
-void createItem(LayoutItem *item, const Span &t)
-{
- item->onAdd = [t](LayoutBuilder &builder) {
- addItemHelper(builder, t.item);
- builder.stack.last().pendingItems.last().span = t.span;
- };
-}
} // Layouting
-
-#include "layoutbuilder.moc"
diff --git a/src/libs/utils/layoutbuilder.h b/src/libs/utils/layoutbuilder.h
index c9fac7d838..3a7eca26a5 100644
--- a/src/libs/utils/layoutbuilder.h
+++ b/src/libs/utils/layoutbuilder.h
@@ -3,12 +3,12 @@
#pragma once
-#include <QFormLayout>
-#include <QList>
+#include <QMargins>
#include <QString>
-#include <QtGlobal>
-#include <optional>
+#include <functional>
+#include <initializer_list>
+#include <vector>
#if defined(UTILS_LIBRARY)
# define QTCREATOR_UTILS_EXPORT Q_DECL_EXPORT
@@ -19,251 +19,537 @@
#endif
QT_BEGIN_NAMESPACE
+class QBoxLayout;
+class QFormLayout;
+class QGridLayout;
+class QGroupBox;
+class QHBoxLayout;
+class QLabel;
class QLayout;
class QMargins;
class QObject;
+class QPushButton;
+class QSpinBox;
+class QSplitter;
+class QStackedWidget;
+class QTabWidget;
+class QTextEdit;
+class QToolBar;
+class QVBoxLayout;
class QWidget;
-template <class T> T qobject_cast(QObject *object);
QT_END_NAMESPACE
namespace Layouting {
-// LayoutItem
+class NestId {};
-class LayoutBuilder;
-class LayoutItem;
-using LayoutItems = QList<LayoutItem>;
-
-class QTCREATOR_UTILS_EXPORT LayoutItem
+template <typename T1, typename T2>
+class IdAndArg
{
public:
- using Setter = std::function<void(QObject *target)>;
+ IdAndArg(const T1 &id, const T2 &arg) : id(id), arg(arg) {}
+ const T1 id;
+ const T2 arg; // FIXME: Could be const &, but this would currently break bindTo().
+};
- LayoutItem();
- ~LayoutItem();
+// The main dispatcher
+
+void doit(auto x, auto id, auto p);
- LayoutItem(const LayoutItem &t);
- LayoutItem &operator=(const LayoutItem &t) = default;
+template <typename X> class BuilderItem
+{
+public:
+ // Nested child object
+ template <typename Inner>
+ BuilderItem(Inner && p)
+ {
+ apply = [&p](X *x) { doit(x, NestId{}, std::forward<Inner>(p)); };
+ }
- template <class T> LayoutItem(const T &t)
+ // Property setter
+ template <typename Id, typename Arg>
+ BuilderItem(IdAndArg<Id, Arg> && idarg)
{
- if constexpr (std::is_base_of_v<LayoutItem, T>)
- LayoutItem::operator=(t);
- else
- createItem(this, t);
+ apply = [&idarg](X *x) { doit(x, idarg.id, idarg.arg); };
}
- void attachTo(QWidget *w) const;
- QWidget *emerge();
+ std::function<void(X *)> apply;
+};
- void addItem(const LayoutItem &item);
- void addItems(const LayoutItems &items);
- void addRow(const LayoutItems &items);
- std::function<void(LayoutBuilder &)> onAdd;
- std::function<void(LayoutBuilder &)> onExit;
- std::function<void(QObject *target)> setter;
- LayoutItems subItems;
-};
+//////////////////////////////////////////////
-// Special items
+//
+// Basic
+//
-class QTCREATOR_UTILS_EXPORT Space
+class QTCREATOR_UTILS_EXPORT Thing
{
public:
- explicit Space(int space) : space(space) {}
- const int space;
+ void *ptr; // The product.
};
-class QTCREATOR_UTILS_EXPORT Stretch
+class QTCREATOR_UTILS_EXPORT Object : public Thing
{
public:
- explicit Stretch(int stretch = 1) : stretch(stretch) {}
- const int stretch;
+ using Implementation = QObject;
+ using I = BuilderItem<Object>;
+
+ Object() = default;
+ Object(std::initializer_list<I> ps);
};
-class QTCREATOR_UTILS_EXPORT Span
+//
+// Layouts
+//
+
+class FlowLayout;
+class Layout;
+using LayoutModifier = std::function<void(Layout *)>;
+// using LayoutModifier = void(*)(Layout *);
+
+class QTCREATOR_UTILS_EXPORT LayoutItem
{
public:
- Span(int span, const LayoutItem &item) : span(span), item(item) {}
- const int span;
- LayoutItem item;
+ ~LayoutItem();
+ LayoutItem();
+ LayoutItem(QLayout *l) : layout(l) {}
+ LayoutItem(QWidget *w) : widget(w) {}
+ LayoutItem(const QString &t) : text(t) {}
+ LayoutItem(const LayoutModifier &inner);
+
+ QString text;
+ QLayout *layout = nullptr;
+ QWidget *widget = nullptr;
+ int stretch = -1;
+ int spanCols = 1;
+ int spanRows = 1;
+ bool empty = false;
+ LayoutModifier ownerModifier;
+ //Qt::Alignment align = {};
};
-class QTCREATOR_UTILS_EXPORT Column : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Layout : public Object
{
public:
- Column(std::initializer_list<LayoutItem> items);
+ using Implementation = QLayout;
+ using I = BuilderItem<Layout>;
+
+ Layout() = default;
+ Layout(Implementation *w) { ptr = w; }
+
+ void span(int cols, int rows);
+ void noMargin();
+ void normalMargin();
+ void customMargin(const QMargins &margin);
+ void setColumnStretch(int cols, int rows);
+ void setSpacing(int space);
+
+ void attachTo(QWidget *);
+ void addItem(I item);
+ void addItems(std::initializer_list<I> items);
+ void addRow(std::initializer_list<I> items);
+ void addLayoutItem(const LayoutItem &item);
+
+ void flush();
+ void flush_() const;
+ void fieldGrowthPolicy(int policy);
+
+ QWidget *emerge() const;
+ void show() const;
+
+ QFormLayout *asForm();
+ QGridLayout *asGrid();
+ QBoxLayout *asBox();
+ FlowLayout *asFlow();
+
+ // Grid-only
+ int currentGridColumn = 0;
+ int currentGridRow = 0;
+ //Qt::Alignment align = {};
+ bool useFormAlignment = false;
+
+ std::vector<LayoutItem> pendingItems;
};
-class QTCREATOR_UTILS_EXPORT Row : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Column : public Layout
{
public:
- Row(std::initializer_list<LayoutItem> items);
+ using Implementation = QVBoxLayout;
+ using I = BuilderItem<Column>;
+
+ Column(std::initializer_list<I> ps);
};
-class QTCREATOR_UTILS_EXPORT Flow : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Row : public Layout
{
public:
- Flow(std::initializer_list<LayoutItem> items);
+ using Implementation = QHBoxLayout;
+ using I = BuilderItem<Row>;
+
+ Row(std::initializer_list<I> ps);
};
-class QTCREATOR_UTILS_EXPORT Grid : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Form : public Layout
{
public:
- Grid() : Grid({}) {}
- Grid(std::initializer_list<LayoutItem> items);
+ using Implementation = QFormLayout;
+ using I = BuilderItem<Form>;
+
+ Form();
+ Form(std::initializer_list<I> ps);
};
-class QTCREATOR_UTILS_EXPORT Form : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Grid : public Layout
{
public:
- Form() : Form({}) {}
- Form(std::initializer_list<LayoutItem> items);
+ using Implementation = QGridLayout;
+ using I = BuilderItem<Grid>;
+
+ Grid();
+ Grid(std::initializer_list<I> ps);
};
-class QTCREATOR_UTILS_EXPORT Widget : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Flow : public Layout
{
public:
- Widget(std::initializer_list<LayoutItem> items);
+ Flow(std::initializer_list<I> ps);
};
-class QTCREATOR_UTILS_EXPORT Stack : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Stretch
{
public:
- Stack() : Stack({}) {}
- Stack(std::initializer_list<LayoutItem> items);
+ explicit Stretch(int stretch) : stretch(stretch) {}
+
+ int stretch;
};
-class QTCREATOR_UTILS_EXPORT Tab : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Space
{
public:
- Tab(const QString &tabName, const LayoutItem &item);
+ explicit Space(int space) : space(space) {}
+
+ int space;
};
-class QTCREATOR_UTILS_EXPORT If : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Span
{
public:
- If(bool condition, const LayoutItems &item, const LayoutItems &other = {});
+ Span(int cols, const Layout::I &item);
+ Span(int cols, int rows, const Layout::I &item);
+
+ Layout::I item;
+ int spanCols = 1;
+ int spanRows = 1;
};
-class QTCREATOR_UTILS_EXPORT Group : public LayoutItem
+//
+// Widgets
+//
+
+class QTCREATOR_UTILS_EXPORT Widget : public Object
{
public:
- Group(std::initializer_list<LayoutItem> items);
+ using Implementation = QWidget;
+ using I = BuilderItem<Widget>;
+
+ Widget() = default;
+ Widget(std::initializer_list<I> ps);
+ Widget(Implementation *w) { ptr = w; }
+
+ QWidget *emerge() const;
+
+ void show();
+ void resize(int, int);
+ void setLayout(const Layout &layout);
+ void setWindowTitle(const QString &);
+ void setToolTip(const QString &);
+ void noMargin(int = 0);
+ void normalMargin(int = 0);
+ void customMargin(const QMargins &margin);
};
-class QTCREATOR_UTILS_EXPORT TextEdit : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Label : public Widget
{
public:
- TextEdit(std::initializer_list<LayoutItem> items);
+ using Implementation = QLabel;
+ using I = BuilderItem<Label>;
+
+ Label(std::initializer_list<I> ps);
+ Label(const QString &text);
+
+ void setText(const QString &);
};
-class QTCREATOR_UTILS_EXPORT PushButton : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Group : public Widget
{
public:
- PushButton(std::initializer_list<LayoutItem> items);
+ using Implementation = QGroupBox;
+ using I = BuilderItem<Group>;
+
+ Group(std::initializer_list<I> ps);
+
+ void setTitle(const QString &);
+ void setGroupChecker(const std::function<void(QObject *)> &);
};
-class QTCREATOR_UTILS_EXPORT SpinBox : public LayoutItem
+class QTCREATOR_UTILS_EXPORT SpinBox : public Widget
{
public:
- SpinBox(std::initializer_list<LayoutItem> items);
+ using Implementation = QSpinBox;
+ using I = BuilderItem<SpinBox>;
+
+ SpinBox(std::initializer_list<I> ps);
+
+ void setValue(int);
+ void onTextChanged(const std::function<void(QString)> &);
};
-class QTCREATOR_UTILS_EXPORT Splitter : public LayoutItem
+class QTCREATOR_UTILS_EXPORT PushButton : public Widget
{
public:
- Splitter(std::initializer_list<LayoutItem> items);
+ using Implementation = QPushButton;
+ using I = BuilderItem<PushButton>;
+
+ PushButton(std::initializer_list<I> ps);
+
+ void setText(const QString &);
+ void onClicked(const std::function<void()> &, QObject *guard);
};
-class QTCREATOR_UTILS_EXPORT ToolBar : public LayoutItem
+class QTCREATOR_UTILS_EXPORT TextEdit : public Widget
{
public:
- ToolBar(std::initializer_list<LayoutItem> items);
+ using Implementation = QTextEdit;
+ using I = BuilderItem<TextEdit>;
+ using Id = Implementation *;
+
+ TextEdit(std::initializer_list<I> ps);
+
+ void setText(const QString &);
};
-class QTCREATOR_UTILS_EXPORT TabWidget : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Splitter : public Widget
{
public:
- TabWidget(std::initializer_list<LayoutItem> items);
+ using Implementation = QSplitter;
+ using I = BuilderItem<Splitter>;
+
+ Splitter(std::initializer_list<I> items);
};
-class QTCREATOR_UTILS_EXPORT Application : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Stack : public Widget
{
public:
- Application(std::initializer_list<LayoutItem> items);
+ using Implementation = QStackedWidget;
+ using I = BuilderItem<Stack>;
- int exec(int &argc, char *argv[]);
+ Stack() : Stack({}) {}
+ Stack(std::initializer_list<I> items);
};
+class QTCREATOR_UTILS_EXPORT Tab : public Widget
+{
+public:
+ using Implementation = QWidget;
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const std::function<void(QObject *target)> &t);
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, QWidget *t);
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, QLayout *t);
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, LayoutItem(*t)());
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const QString &t);
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const Span &t);
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const Space &t);
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const Stretch &t);
-
+ Tab(const QString &tabName, const Layout &inner);
-// "Singletons"
+ const QString tabName;
+ const Layout inner;
+};
-QTCREATOR_UTILS_EXPORT LayoutItem br();
-QTCREATOR_UTILS_EXPORT LayoutItem st();
-QTCREATOR_UTILS_EXPORT LayoutItem empty();
-QTCREATOR_UTILS_EXPORT LayoutItem hr();
-QTCREATOR_UTILS_EXPORT LayoutItem noMargin();
-QTCREATOR_UTILS_EXPORT LayoutItem normalMargin();
-QTCREATOR_UTILS_EXPORT LayoutItem customMargin(const QMargins &margin);
-QTCREATOR_UTILS_EXPORT LayoutItem withFormAlignment();
+class QTCREATOR_UTILS_EXPORT TabWidget : public Widget
+{
+public:
+ using Implementation = QTabWidget;
+ using I = BuilderItem<TabWidget>;
-// "Setters"
+ TabWidget(std::initializer_list<I> items);
+};
-QTCREATOR_UTILS_EXPORT LayoutItem title(const QString &title);
-QTCREATOR_UTILS_EXPORT LayoutItem text(const QString &text);
-QTCREATOR_UTILS_EXPORT LayoutItem tooltip(const QString &toolTip);
-QTCREATOR_UTILS_EXPORT LayoutItem resize(int, int);
-QTCREATOR_UTILS_EXPORT LayoutItem columnStretch(int column, int stretch);
-QTCREATOR_UTILS_EXPORT LayoutItem spacing(int);
-QTCREATOR_UTILS_EXPORT LayoutItem windowTitle(const QString &windowTitle);
-QTCREATOR_UTILS_EXPORT LayoutItem fieldGrowthPolicy(QFormLayout::FieldGrowthPolicy policy);
+class QTCREATOR_UTILS_EXPORT ToolBar : public Widget
+{
+public:
+ using Implementation = QToolBar;
+ using I = Layouting::BuilderItem<ToolBar>;
+ ToolBar(std::initializer_list<I> items);
+};
-// "Getters"
+// Special
-class ID
+class QTCREATOR_UTILS_EXPORT If
{
public:
- QObject *ob = nullptr;
+ If(bool condition,
+ const std::initializer_list<Layout::I> ifcase,
+ const std::initializer_list<Layout::I> thencase = {});
+
+ const std::initializer_list<Layout::I> used;
};
-QTCREATOR_UTILS_EXPORT LayoutItem id(ID &out);
+//
+// Dispatchers
+//
-QTCREATOR_UTILS_EXPORT void setText(ID id, const QString &text);
+// We need one 'Id' (and a corresponding function wrapping arguments into a
+// tuple marked by this id) per 'name' of "backend" setter member function,
+// i.e. one 'text' is sufficient for QLabel::setText, QLineEdit::setText.
+// The name of the Id does not have to match the backend names as it
+// is mapped per-backend-type in the respective setter implementation
+// but we assume that it generally makes sense to stay close to the
+// wrapped API name-wise.
+// These are free functions overloaded on the type of builder object
+// and setter id. The function implementations are independent, but
+// the base expectation is that they will forwards to the backend
+// type's setter.
-// "Signals"
+// Special dispatchers
-QTCREATOR_UTILS_EXPORT LayoutItem onClicked(const std::function<void()> &,
- QObject *guard = nullptr);
-QTCREATOR_UTILS_EXPORT LayoutItem onTextChanged(const std::function<void(const QString &)> &,
- QObject *guard = nullptr);
-QTCREATOR_UTILS_EXPORT LayoutItem onValueChanged(const std::function<void(int)> &,
- QObject *guard = nullptr);
-QTCREATOR_UTILS_EXPORT LayoutItem onTextChanged(ID &id, QVariant(*sig)(QObject *));
+class BindToId {};
-// Convenience
+template <typename T>
+auto bindTo(T **p)
+{
+ return IdAndArg{BindToId{}, p};
+}
-QTCREATOR_UTILS_EXPORT QWidget *createHr(QWidget *parent = nullptr);
+template <typename Interface>
+void doit(Interface *x, BindToId, auto p)
+{
+ *p = static_cast<typename Interface::Implementation *>(x->ptr);
+}
+
+class IdId {};
+auto id(auto p) { return IdAndArg{IdId{}, p}; }
-template <class T>
-LayoutItem bindTo(T **out)
+template <typename Interface>
+void doit(Interface *x, IdId, auto p)
{
- return [out](QObject *target) { *out = qobject_cast<T *>(target); };
+ **p = static_cast<typename Interface::Implementation *>(x->ptr);
}
+// Setter dispatchers
+
+class SizeId {};
+auto size(auto w, auto h) { return IdAndArg{SizeId{}, std::pair{w, h}}; }
+void doit(auto x, SizeId, auto p) { x->resize(p->first, p->second); }
+
+class TextId {};
+auto text(auto p) { return IdAndArg{TextId{}, p}; }
+void doit(auto x, TextId, auto p) { x->setText(p); }
+
+class TitleId {};
+auto title(auto p) { return IdAndArg{TitleId{}, p}; }
+void doit(auto x, TitleId, auto p) { x->setTitle(p); }
+
+class GroupCheckerId {};
+auto groupChecker(auto p) { return IdAndArg{GroupCheckerId{}, p}; }
+void doit(auto x, GroupCheckerId, auto p) { x->setGroupChecker(p); }
+
+class ToolTipId {};
+auto toolTip(auto p) { return IdAndArg{ToolTipId{}, p}; }
+void doit(auto x, ToolTipId, auto p) { x->setToolTip(p); }
+
+class WindowTitleId {};
+auto windowTitle(auto p) { return IdAndArg{WindowTitleId{}, p}; }
+void doit(auto x, WindowTitleId, auto p) { x->setWindowTitle(p); }
+
+class OnTextChangedId {};
+auto onTextChanged(auto p) { return IdAndArg{OnTextChangedId{}, p}; }
+void doit(auto x, OnTextChangedId, auto p) { x->onTextChanged(p); }
+
+class OnClickedId {};
+auto onClicked(auto p, auto guard) { return IdAndArg{OnClickedId{}, std::pair{p, guard}}; }
+void doit(auto x, OnClickedId, auto p) { x->onClicked(p.first, p.second); }
+
+class CustomMarginId {};
+inline auto customMargin(const QMargins &p) { return IdAndArg{CustomMarginId{}, p}; }
+void doit(auto x, CustomMarginId, auto p) { x->customMargin(p); }
+
+class FieldGrowthPolicyId {};
+inline auto fieldGrowthPolicy(auto p) { return IdAndArg{FieldGrowthPolicyId{}, p}; }
+void doit(auto x, FieldGrowthPolicyId, auto p) { x->fieldGrowthPolicy(p); }
+
+class ColumnStretchId {};
+inline auto columnStretch(int column, int stretch) { return IdAndArg{ColumnStretchId{}, std::pair{column, stretch}}; }
+void doit(auto x, ColumnStretchId, auto p) { x->setColumnStretch(p.first, p.second); }
+
+// Nesting dispatchers
+
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Layout &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Widget &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, QWidget *inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, QLayout *inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const LayoutModifier &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const QString &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Space &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Stretch &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const If &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Span &inner);
+// ... can be added to anywhere later to support "user types"
+
+QTCREATOR_UTILS_EXPORT void addToWidget(Widget *widget, const Layout &layout);
+
+QTCREATOR_UTILS_EXPORT void addToTabWidget(TabWidget *tabWidget, const Tab &inner);
+
+QTCREATOR_UTILS_EXPORT void addToSplitter(Splitter *splitter, QWidget *inner);
+QTCREATOR_UTILS_EXPORT void addToSplitter(Splitter *splitter, const Widget &inner);
+QTCREATOR_UTILS_EXPORT void addToSplitter(Splitter *splitter, const Layout &inner);
+
+QTCREATOR_UTILS_EXPORT void addToStack(Stack *stack, QWidget *inner);
+QTCREATOR_UTILS_EXPORT void addToStack(Stack *stack, const Widget &inner);
+QTCREATOR_UTILS_EXPORT void addToStack(Stack *stack, const Layout &inner);
+
+template <class Inner>
+void doit_nested(Layout *outer, Inner && inner)
+{
+ addToLayout(outer, std::forward<Inner>(inner));
+}
+
+void doit_nested(Widget *outer, auto inner)
+{
+ addToWidget(outer, inner);
+}
+
+void doit_nested(TabWidget *outer, auto inner)
+{
+ addToTabWidget(outer, inner);
+}
+
+void doit_nested(Stack *outer, auto inner)
+{
+ addToStack(outer, inner);
+}
+
+void doit_nested(Splitter *outer, auto inner)
+{
+ addToSplitter(outer, inner);
+}
+
+template <class Inner>
+void doit(auto outer, NestId, Inner && inner)
+{
+ doit_nested(outer, std::forward<Inner>(inner));
+}
+
+// Special layout items
+
+QTCREATOR_UTILS_EXPORT void empty(Layout *);
+QTCREATOR_UTILS_EXPORT void br(Layout *);
+QTCREATOR_UTILS_EXPORT void st(Layout *);
+QTCREATOR_UTILS_EXPORT void noMargin(Layout *);
+QTCREATOR_UTILS_EXPORT void normalMargin(Layout *);
+QTCREATOR_UTILS_EXPORT void withFormAlignment(Layout *);
+QTCREATOR_UTILS_EXPORT void hr(Layout *);
+
+QTCREATOR_UTILS_EXPORT LayoutModifier spacing(int space);
+
+// Convenience
+
+QTCREATOR_UTILS_EXPORT QWidget *createHr(QWidget *parent = nullptr);
} // Layouting
diff --git a/src/libs/utils/lua.cpp b/src/libs/utils/lua.cpp
new file mode 100644
index 0000000000..4f12c626f1
--- /dev/null
+++ b/src/libs/utils/lua.cpp
@@ -0,0 +1,30 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "lua.h"
+
+#include "utilstr.h"
+
+namespace Utils {
+
+static LuaInterface *s_luaInterface = nullptr;
+
+void setLuaInterface(LuaInterface *luaInterface)
+{
+ s_luaInterface = luaInterface;
+}
+
+LuaInterface *luaInterface()
+{
+ return s_luaInterface;
+}
+
+expected_str<std::unique_ptr<LuaState>> runScript(const QString &script, const QString &name)
+{
+ if (!s_luaInterface)
+ return make_unexpected(Tr::tr("No Lua interface set"));
+
+ return s_luaInterface->runScript(script, name);
+}
+
+} // namespace Utils
diff --git a/src/libs/utils/lua.h b/src/libs/utils/lua.h
new file mode 100644
index 0000000000..7a3b8f7833
--- /dev/null
+++ b/src/libs/utils/lua.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QObject>
+
+#include "expected.h"
+#include "utils_global.h"
+
+namespace Utils {
+
+class LuaState
+{
+public:
+ virtual ~LuaState() = default;
+};
+
+class QTCREATOR_UTILS_EXPORT LuaInterface
+{
+public:
+ virtual ~LuaInterface() = default;
+ virtual expected_str<std::unique_ptr<LuaState>> runScript(
+ const QString &script, const QString &name)
+ = 0;
+};
+
+QTCREATOR_UTILS_EXPORT void setLuaInterface(LuaInterface *luaInterface);
+QTCREATOR_UTILS_EXPORT LuaInterface *luaInterface();
+
+QTCREATOR_UTILS_EXPORT expected_str<std::unique_ptr<LuaState>> runScript(
+ const QString &script, const QString &name);
+
+} // namespace Utils
diff --git a/src/libs/utils/macroexpander.cpp b/src/libs/utils/macroexpander.cpp
index dd57cd6b5a..fc13897474 100644
--- a/src/libs/utils/macroexpander.cpp
+++ b/src/libs/utils/macroexpander.cpp
@@ -284,7 +284,7 @@ QByteArray MacroExpander::expand(const QByteArray &stringWithVariables) const
QVariant MacroExpander::expandVariant(const QVariant &v) const
{
- const auto type = QMetaType::Type(v.type());
+ const int type = v.typeId();
if (type == QMetaType::QString) {
return expand(v.toString());
} else if (type == QMetaType::QStringList) {
diff --git a/src/libs/utils/outputformatter.cpp b/src/libs/utils/outputformatter.cpp
index a318bb1cca..59c29e7fa5 100644
--- a/src/libs/utils/outputformatter.cpp
+++ b/src/libs/utils/outputformatter.cpp
@@ -465,7 +465,7 @@ void OutputFormatter::append(const QString &text, const QTextCharFormat &format)
QTextCharFormat OutputFormatter::linkFormat(const QTextCharFormat &inputFormat, const QString &href)
{
QTextCharFormat result = inputFormat;
- result.setForeground(creatorTheme()->color(Theme::TextColorLink));
+ result.setForeground(creatorColor(Theme::TextColorLink));
result.setUnderlineStyle(QTextCharFormat::SingleUnderline);
result.setAnchor(true);
result.setAnchorHref(href);
@@ -500,14 +500,13 @@ void OutputFormatter::initFormats()
if (!plainTextEdit())
return;
- Theme *theme = creatorTheme();
- d->formats[NormalMessageFormat].setForeground(theme->color(Theme::OutputPanes_NormalMessageTextColor));
- d->formats[ErrorMessageFormat].setForeground(theme->color(Theme::OutputPanes_ErrorMessageTextColor));
- d->formats[LogMessageFormat].setForeground(theme->color(Theme::OutputPanes_WarningMessageTextColor));
- d->formats[StdOutFormat].setForeground(theme->color(Theme::OutputPanes_StdOutTextColor));
- d->formats[StdErrFormat].setForeground(theme->color(Theme::OutputPanes_StdErrTextColor));
- d->formats[DebugFormat].setForeground(theme->color(Theme::OutputPanes_DebugTextColor));
- d->formats[GeneralMessageFormat].setForeground(theme->color(Theme::OutputPanes_DebugTextColor));
+ d->formats[NormalMessageFormat].setForeground(creatorColor(Theme::OutputPanes_NormalMessageTextColor));
+ d->formats[ErrorMessageFormat].setForeground(creatorColor(Theme::OutputPanes_ErrorMessageTextColor));
+ d->formats[LogMessageFormat].setForeground(creatorColor(Theme::OutputPanes_WarningMessageTextColor));
+ d->formats[StdOutFormat].setForeground(creatorColor(Theme::OutputPanes_StdOutTextColor));
+ d->formats[StdErrFormat].setForeground(creatorColor(Theme::OutputPanes_StdErrTextColor));
+ d->formats[DebugFormat].setForeground(creatorColor(Theme::OutputPanes_DebugTextColor));
+ d->formats[GeneralMessageFormat].setForeground(creatorColor(Theme::OutputPanes_DebugTextColor));
setBoldFontEnabled(d->boldFontEnabled);
}
diff --git a/src/libs/utils/passworddialog.cpp b/src/libs/utils/passworddialog.cpp
index 7958f7d6bd..94de5f7f0f 100644
--- a/src/libs/utils/passworddialog.cpp
+++ b/src/libs/utils/passworddialog.cpp
@@ -34,10 +34,9 @@ void ShowPasswordButton::paintEvent(QPaintEvent *e)
QRect r(QPoint(), size());
if (m_containsMouse && isEnabled())
- StyleHelper::drawPanelBgRect(&p, r, creatorTheme()->color(Theme::FancyToolButtonHoverColor));
+ StyleHelper::drawPanelBgRect(&p, r, creatorColor(Theme::FancyToolButtonHoverColor));
- QWindow *window = this->window()->windowHandle();
- QSize s = icon.actualSize(window, QSize(32, 16));
+ QSize s = icon.actualSize(QSize(32, 16));
QPixmap px = icon.pixmap(s);
QRect iRect(QPoint(), s);
@@ -61,8 +60,7 @@ void ShowPasswordButton::leaveEvent(QEvent *e)
QSize ShowPasswordButton::sizeHint() const
{
- QWindow *window = this->window()->windowHandle();
- QSize s = Utils::Icons::EYE_OPEN_TOOLBAR.icon().actualSize(window, QSize(32, 16)) + QSize(8, 8);
+ QSize s = Utils::Icons::EYE_OPEN_TOOLBAR.icon().actualSize(QSize(32, 16)) + QSize(8, 8);
if (StyleHelper::toolbarStyle() == StyleHelper::ToolbarStyleRelaxed)
s += QSize(5, 5);
diff --git a/src/libs/utils/persistentsettings.cpp b/src/libs/utils/persistentsettings.cpp
index 54a829004b..3702ecf960 100644
--- a/src/libs/utils/persistentsettings.cpp
+++ b/src/libs/utils/persistentsettings.cpp
@@ -106,13 +106,13 @@ const QString keyAttribute("key");
struct ParseValueStackEntry
{
- explicit ParseValueStackEntry(QVariant::Type t = QVariant::Invalid, const QString &k = {}) : type(t), key(k) {}
+ explicit ParseValueStackEntry(QMetaType::Type t = QMetaType::UnknownType, const QString &k = {}) : typeId(t), key(k) {}
explicit ParseValueStackEntry(const QVariant &aSimpleValue, const QString &k);
QVariant value() const;
void addChild(const QString &key, const QVariant &v);
- QVariant::Type type;
+ QMetaType::Type typeId;
QString key;
QVariant simpleValue;
QVariantList listValue;
@@ -120,19 +120,19 @@ struct ParseValueStackEntry
};
ParseValueStackEntry::ParseValueStackEntry(const QVariant &aSimpleValue, const QString &k)
- : type(aSimpleValue.type()), key(k), simpleValue(aSimpleValue)
+ : typeId(QMetaType::Type(aSimpleValue.typeId())), key(k), simpleValue(aSimpleValue)
{
QTC_ASSERT(simpleValue.isValid(), return);
}
QVariant ParseValueStackEntry::value() const
{
- switch (type) {
- case QVariant::Invalid:
+ switch (typeId) {
+ case QMetaType::UnknownType:
return QVariant();
- case QVariant::Map:
+ case QMetaType::QVariantMap:
return QVariant(mapValue);
- case QVariant::List:
+ case QMetaType::QVariantList:
return QVariant(listValue);
default:
break;
@@ -142,16 +142,16 @@ QVariant ParseValueStackEntry::value() const
void ParseValueStackEntry::addChild(const QString &key, const QVariant &v)
{
- switch (type) {
- case QVariant::Map:
+ switch (typeId) {
+ case QMetaType::QVariantMap:
mapValue.insert(key, v);
break;
- case QVariant::List:
+ case QMetaType::QVariantList:
listValue.push_back(v);
break;
default:
qWarning() << "ParseValueStackEntry::Internal error adding " << key << v << " to "
- << QVariant::typeToName(type) << value();
+ << QMetaType(typeId).name() << value();
break;
}
}
@@ -226,14 +226,14 @@ bool ParseContext::handleStartElement(QXmlStreamReader &r)
const QXmlStreamAttributes attributes = r.attributes();
const QString key = attributes.hasAttribute(keyAttribute) ?
attributes.value(keyAttribute).toString() : QString();
- m_valueStack.push_back(ParseValueStackEntry(QVariant::List, key));
+ m_valueStack.push_back(ParseValueStackEntry(QMetaType::QVariantList, key));
return false;
}
if (name == valueMapElement) {
const QXmlStreamAttributes attributes = r.attributes();
const QString key = attributes.hasAttribute(keyAttribute) ?
attributes.value(keyAttribute).toString() : QString();
- m_valueStack.push_back(ParseValueStackEntry(QVariant::Map, key));
+ m_valueStack.push_back(ParseValueStackEntry(QMetaType::QVariantMap, key));
return false;
}
return false;
@@ -369,8 +369,8 @@ static void writeVariantValue(QXmlStreamWriter &w, const QVariant &variant, cons
w.writeAttribute(typeAttribute, QLatin1String(variant.typeName()));
if (!key.isEmpty())
w.writeAttribute(keyAttribute, xmlAttrFromKey(key));
- switch (variant.type()) {
- case QVariant::Rect:
+ switch (variant.typeId()) {
+ case QMetaType::QRect:
w.writeCharacters(rectangleToString(variant.toRect()));
break;
default:
diff --git a/src/libs/utils/projectintropage.cpp b/src/libs/utils/projectintropage.cpp
index 1f32cce6bc..2c8b43b66a 100644
--- a/src/libs/utils/projectintropage.cpp
+++ b/src/libs/utils/projectintropage.cpp
@@ -110,7 +110,8 @@ ProjectIntroPage::ProjectIntroPage(QWidget *parent) :
using namespace Layouting;
Form {
- Tr::tr("Name:"), d->m_nameLineEdit, br,
+ Tr::tr("Name:"), d->m_nameLineEdit,
+ br,
d->m_projectLabel, d->m_projectComboBox, br,
Column { Space(12) }, br,
Tr::tr("Create in:"), d->m_pathChooser, br,
@@ -135,7 +136,7 @@ ProjectIntroPage::ProjectIntroPage(QWidget *parent) :
connect(d->m_nameLineEdit, &FancyLineEdit::validReturnPressed,
this, &ProjectIntroPage::slotActivated);
connect(d->m_projectComboBox, &QComboBox::currentIndexChanged,
- this, &ProjectIntroPage::slotChanged);
+ this, &ProjectIntroPage::onCurrentProjectIndexChanged);
setProperty(SHORT_TITLE_PROPERTY, Tr::tr("Location"));
registerFieldWithName(QLatin1String("Path"), d->m_pathChooser, "path", SIGNAL(textChanged(QString)));
@@ -192,15 +193,10 @@ bool ProjectIntroPage::isComplete() const
bool ProjectIntroPage::validate()
{
- if (d->m_forceSubProject) {
- int index = d->m_projectComboBox->currentIndex();
- if (index == 0)
- return false;
- d->m_pathChooser->setFilePath(d->m_projectDirectories.at(index));
- }
// Validate and display status
if (!d->m_pathChooser->isValid()) {
- displayStatusMessage(InfoLabel::Error, d->m_pathChooser->errorMessage());
+ if (const QString msg = d->m_pathChooser->errorMessage(); !msg.isEmpty())
+ displayStatusMessage(InfoLabel::Error, msg);
return false;
}
@@ -259,6 +255,25 @@ void ProjectIntroPage::slotActivated()
emit activated();
}
+void ProjectIntroPage::onCurrentProjectIndexChanged(int index)
+{
+ if (d->m_forceSubProject) {
+ const int available = d->m_projectDirectories.size();
+ if (available == 0)
+ return;
+ QTC_ASSERT(index < available, return);
+ if (index < 0)
+ return;
+
+ const FilePath current = d->m_projectDirectories.at(index);
+ const FilePath visible = d->m_pathChooser->filePath();
+ if (visible != current && !visible.isChildOf(current))
+ d->m_pathChooser->setFilePath(current);
+
+ fieldsUpdated();
+ }
+}
+
bool ProjectIntroPage::forceSubProject() const
{
return d->m_forceSubProject;
diff --git a/src/libs/utils/projectintropage.h b/src/libs/utils/projectintropage.h
index 48b2f6b036..562b5b06e4 100644
--- a/src/libs/utils/projectintropage.h
+++ b/src/libs/utils/projectintropage.h
@@ -58,6 +58,7 @@ public slots:
private:
void slotChanged();
void slotActivated();
+ void onCurrentProjectIndexChanged(int index);
bool validate();
void displayStatusMessage(InfoLabel::InfoType t, const QString &);
diff --git a/src/libs/utils/qtcolorbutton.cpp b/src/libs/utils/qtcolorbutton.cpp
index 6a8537bd5f..5be9c9c069 100644
--- a/src/libs/utils/qtcolorbutton.cpp
+++ b/src/libs/utils/qtcolorbutton.cpp
@@ -171,7 +171,7 @@ void QtColorButton::paintEvent(QPaintEvent *event)
constexpr int size = 11;
const qreal horPadding = (width() - size) / 2.0;
const qreal verPadding = (height() - size) / 2.0;
- const QPen pen(creatorTheme()->color(overlayColor), 2);
+ const QPen pen(creatorColor(overlayColor), 2);
p.save();
p.setOpacity(overlayOpacity);
@@ -202,7 +202,7 @@ void QtColorButton::paintEvent(QPaintEvent *event)
p.setPen(pen);
p.setCompositionMode(QPainter::CompositionMode_Difference);
} else {
- p.setPen(creatorTheme()->color(overlayColor));
+ p.setPen(creatorColor(overlayColor));
p.setOpacity(overlayOpacity);
}
p.drawRect(rect().adjusted(0, 0, -1, -1));
diff --git a/src/libs/utils/qtcprocess.cpp b/src/libs/utils/qtcprocess.cpp
index 3f06e04fc1..dca106a7d5 100644
--- a/src/libs/utils/qtcprocess.cpp
+++ b/src/libs/utils/qtcprocess.cpp
@@ -1360,7 +1360,7 @@ QString Process::toStandaloneCommandLine() const
parts.append("/usr/bin/env");
if (!d->m_setup.m_workingDirectory.isEmpty()) {
parts.append("-C");
- d->m_setup.m_workingDirectory.path();
+ parts.append(d->m_setup.m_workingDirectory.path());
}
parts.append("-i");
if (d->m_setup.m_environment.hasChanges()) {
diff --git a/src/libs/utils/store.cpp b/src/libs/utils/store.cpp
index fa8d4232c0..8c0e2a6046 100644
--- a/src/libs/utils/store.cpp
+++ b/src/libs/utils/store.cpp
@@ -50,10 +50,10 @@ static QVariantList mapListFromStoreList(const QVariantList &storeList);
QVariant storeEntryFromMapEntry(const QVariant &mapEntry)
{
- if (mapEntry.type() == QVariant::Map)
+ if (mapEntry.typeId() == QMetaType::QVariantMap)
return QVariant::fromValue(storeFromMap(mapEntry.toMap()));
- if (mapEntry.type() == QVariant::List)
+ if (mapEntry.typeId() == QMetaType::QVariantList)
return QVariant::fromValue(storeListFromMapList(mapEntry.toList()));
return mapEntry;
@@ -64,7 +64,7 @@ QVariant mapEntryFromStoreEntry(const QVariant &storeEntry)
if (storeEntry.metaType() == QMetaType::fromType<Store>())
return QVariant::fromValue(mapFromStore(storeEntry.value<Store>()));
- if (storeEntry.type() == QVariant::List)
+ if (storeEntry.typeId() == QMetaType::QVariantList)
return QVariant::fromValue(mapListFromStoreList(storeEntry.toList()));
return storeEntry;
diff --git a/src/libs/utils/stringutils.cpp b/src/libs/utils/stringutils.cpp
index 55a2902acc..d21c7cb9bc 100644
--- a/src/libs/utils/stringutils.cpp
+++ b/src/libs/utils/stringutils.cpp
@@ -637,7 +637,7 @@ void MarkdownHighlighter::highlightBlock(const QString &text)
image.fill(QColor(0, 0, 0, 0).rgba());
image.setPixel(0,
height - 1,
- Utils::creatorTheme()->color(Theme::TextColorDisabled).rgba());
+ Utils::creatorColor(Theme::TextColorDisabled).rgba());
h2Brush = QBrush(image);
}
diff --git a/src/libs/utils/stylehelper.cpp b/src/libs/utils/stylehelper.cpp
index 07f526a4af..617dac09a8 100644
--- a/src/libs/utils/stylehelper.cpp
+++ b/src/libs/utils/stylehelper.cpp
@@ -107,7 +107,7 @@ QColor StyleHelper::notTooBrightHighlightColor()
QPalette StyleHelper::sidebarFontPalette(const QPalette &original)
{
QPalette palette = original;
- const QColor textColor = creatorTheme()->color(Theme::ProgressBarTitleColor);
+ const QColor textColor = creatorColor(Theme::ProgressBarTitleColor);
palette.setColor(QPalette::WindowText, textColor);
palette.setColor(QPalette::Text, textColor);
return palette;
@@ -137,7 +137,7 @@ QColor StyleHelper::requestedBaseColor()
QColor StyleHelper::toolbarBaseColor(bool lightColored)
{
if (creatorTheme()->flag(Theme::QDSTheme))
- return creatorTheme()->color(Utils::Theme::DStoolbarBackground);
+ return creatorColor(Utils::Theme::DStoolbarBackground);
else
return StyleHelper::baseColor(lightColored);
}
@@ -194,7 +194,7 @@ void StyleHelper::setBaseColor(const QColor &newcolor)
{
s_requestedBaseColor = newcolor;
- const QColor themeBaseColor = creatorTheme()->color(Theme::PanelStatusBarBackgroundColor);
+ const QColor themeBaseColor = creatorColor(Theme::PanelStatusBarBackgroundColor);
const QColor defaultBaseColor = QColor(DEFAULT_BASE_COLOR);
QColor color;
@@ -366,11 +366,11 @@ void StyleHelper::drawArrow(QStyle::PrimitiveElement element, QPainter *painter,
};
if (!enabled) {
- drawCommonStyleArrow(image.rect(), creatorTheme()->color(Theme::IconsDisabledColor));
+ drawCommonStyleArrow(image.rect(), creatorColor(Theme::IconsDisabledColor));
} else {
if (creatorTheme()->flag(Theme::ToolBarIconShadow))
drawCommonStyleArrow(image.rect().translated(0, devicePixelRatio), toolBarDropShadowColor());
- drawCommonStyleArrow(image.rect(), creatorTheme()->color(Theme::IconsBaseColor));
+ drawCommonStyleArrow(image.rect(), creatorColor(Theme::IconsBaseColor));
}
painter.end();
pixmap = QPixmap::fromImage(image);
@@ -463,9 +463,9 @@ void StyleHelper::drawMinimalArrow(QStyle::PrimitiveElement element, QPainter *p
if (enabled) {
if (creatorTheme()->flag(Theme::ToolBarIconShadow))
drawArrow(image.rect().translated(0, devicePixelRatio), toolBarDropShadowColor());
- drawArrow(image.rect(), creatorTheme()->color(Theme::IconsBaseColor));
+ drawArrow(image.rect(), creatorColor(Theme::IconsBaseColor));
} else {
- drawArrow(image.rect(), creatorTheme()->color(Theme::IconsDisabledColor));
+ drawArrow(image.rect(), creatorColor(Theme::IconsDisabledColor));
}
painter.end();
pixmap = QPixmap::fromImage(image);
@@ -716,12 +716,7 @@ Qt::HighDpiScaleFactorRoundingPolicy StyleHelper::defaultHighDpiScaleFactorRound
QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QList<IconFontHelper> &parameters)
{
- QFontDatabase a;
-
- QTC_ASSERT(a.hasFamily(fontName), {});
-
- if (!a.hasFamily(fontName))
- return {};
+ QTC_ASSERT(QFontDatabase::hasFamily(fontName), {});
QIcon icon;
@@ -751,38 +746,31 @@ QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QList<Icon
QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize, QColor color)
{
- QFontDatabase a;
-
- QTC_ASSERT(a.hasFamily(fontName), {});
-
- if (a.hasFamily(fontName)) {
-
- QIcon icon;
- QSize size(iconSize, iconSize);
+ QTC_ASSERT(QFontDatabase::hasFamily(fontName), {});
- const int maxDpr = qRound(qApp->devicePixelRatio());
- for (int dpr = 1; dpr <= maxDpr; dpr++) {
- QPixmap pixmap(size * dpr);
- pixmap.setDevicePixelRatio(dpr);
- pixmap.fill(Qt::transparent);
+ QIcon icon;
+ QSize size(iconSize, iconSize);
- QFont font(fontName);
- font.setPixelSize(fontSize);
+ const int maxDpr = qRound(qApp->devicePixelRatio());
+ for (int dpr = 1; dpr <= maxDpr; dpr++) {
+ QPixmap pixmap(size * dpr);
+ pixmap.setDevicePixelRatio(dpr);
+ pixmap.fill(Qt::transparent);
- QPainter painter(&pixmap);
- painter.save();
- painter.setPen(color);
- painter.setFont(font);
- painter.drawText(QRectF(QPoint(0, 0), size), Qt::AlignCenter, iconSymbol);
- painter.restore();
+ QFont font(fontName);
+ font.setPixelSize(fontSize);
- icon.addPixmap(pixmap);
- }
+ QPainter painter(&pixmap);
+ painter.save();
+ painter.setPen(color);
+ painter.setFont(font);
+ painter.drawText(QRectF(QPoint(0, 0), size), Qt::AlignCenter, iconSymbol);
+ painter.restore();
- return icon;
+ icon.addPixmap(pixmap);
}
- return {};
+ return icon;
}
QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize)
@@ -794,55 +782,47 @@ QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QString &i
QIcon StyleHelper::getCursorFromIconFont(const QString &fontName, const QString &cursorFill, const QString &cursorOutline,
int fontSize, int iconSize)
{
- QFontDatabase a;
-
- QTC_ASSERT(a.hasFamily(fontName), {});
+ QTC_ASSERT(QFontDatabase::hasFamily(fontName), {});
const QColor outlineColor = Qt::black;
const QColor fillColor = Qt::white;
- if (a.hasFamily(fontName)) {
-
- QIcon icon;
- QSize size(iconSize, iconSize);
-
- const int maxDpr = qRound(qApp->devicePixelRatio());
- for (int dpr = 1; dpr <= maxDpr; dpr++) {
- QPixmap pixmap(size * dpr);
- pixmap.setDevicePixelRatio(dpr);
- pixmap.fill(Qt::transparent);
+ QIcon icon;
+ QSize size(iconSize, iconSize);
- QFont font(fontName);
- font.setPixelSize(fontSize);
+ const int maxDpr = qRound(qApp->devicePixelRatio());
+ for (int dpr = 1; dpr <= maxDpr; dpr++) {
+ QPixmap pixmap(size * dpr);
+ pixmap.setDevicePixelRatio(dpr);
+ pixmap.fill(Qt::transparent);
- QPainter painter(&pixmap);
- painter.save();
- painter.setRenderHint(QPainter::Antialiasing, true);
- painter.setRenderHint(QPainter::TextAntialiasing, true);
- painter.setRenderHint(QPainter::LosslessImageRendering, true);
- painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
+ QFont font(fontName);
+ font.setPixelSize(fontSize);
- painter.setFont(font);
- painter.setPen(outlineColor);
- painter.drawText(QRectF(QPointF(0.0, 0.0), size),
- Qt::AlignCenter, cursorOutline);
+ QPainter painter(&pixmap);
+ painter.save();
+ painter.setRenderHint(QPainter::Antialiasing, true);
+ painter.setRenderHint(QPainter::TextAntialiasing, true);
+ painter.setRenderHint(QPainter::LosslessImageRendering, true);
+ painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
- painter.setPen(fillColor);
- painter.drawText(QRectF(QPointF(0.0, 0.0), size),
- Qt::AlignCenter, cursorFill);
+ painter.setFont(font);
+ painter.setPen(outlineColor);
+ painter.drawText(QRectF(QPointF(0.0, 0.0), size),
+ Qt::AlignCenter, cursorOutline);
- painter.restore();
+ painter.setPen(fillColor);
+ painter.drawText(QRectF(QPointF(0.0, 0.0), size),
+ Qt::AlignCenter, cursorFill);
- icon.addPixmap(pixmap);
- }
+ painter.restore();
- return icon;
+ icon.addPixmap(pixmap);
}
- return {};
+ return icon;
}
-
QString StyleHelper::dpiSpecificImageFile(const QString &fileName)
{
// See QIcon::addFile()
diff --git a/src/libs/utils/terminalinterface.cpp b/src/libs/utils/terminalinterface.cpp
index 0d5a3be020..0cd7ceb1df 100644
--- a/src/libs/utils/terminalinterface.cpp
+++ b/src/libs/utils/terminalinterface.cpp
@@ -402,7 +402,7 @@ void TerminalInterface::start()
m_setup.m_commandLine.executable().fileName());
if (m_setup.m_runAsRoot && !HostOsInfo::isWindowsHost()) {
- CommandLine rootCommand("sudo", {});
+ CommandLine rootCommand("sudo");
rootCommand.addCommandLineAsArgs(cmd);
stubSetupData.m_commandLine = rootCommand;
} else {
diff --git a/src/libs/utils/theme/theme.cpp b/src/libs/utils/theme/theme.cpp
index 536deafc22..ef20ddddb7 100644
--- a/src/libs/utils/theme/theme.cpp
+++ b/src/libs/utils/theme/theme.cpp
@@ -38,6 +38,12 @@ Theme *proxyTheme()
return new Theme(m_creatorTheme);
}
+// Convenience
+QColor creatorColor(Theme::Color role)
+{
+ return m_creatorTheme->color(role);
+}
+
static bool paletteIsDark(const QPalette &pal)
{
return pal.color(QPalette::Window).lightnessF() < pal.color(QPalette::WindowText).lightnessF();
diff --git a/src/libs/utils/theme/theme.h b/src/libs/utils/theme/theme.h
index cd89c3ae48..59475aff82 100644
--- a/src/libs/utils/theme/theme.h
+++ b/src/libs/utils/theme/theme.h
@@ -577,5 +577,6 @@ private:
QTCREATOR_UTILS_EXPORT Theme *creatorTheme();
QTCREATOR_UTILS_EXPORT Theme *proxyTheme();
+QTCREATOR_UTILS_EXPORT QColor creatorColor(Theme::Color role);
} // namespace Utils
diff --git a/src/libs/utils/threadutils.cpp b/src/libs/utils/threadutils.cpp
index 344779dc93..cfb18059a6 100644
--- a/src/libs/utils/threadutils.cpp
+++ b/src/libs/utils/threadutils.cpp
@@ -10,7 +10,12 @@ namespace Utils {
bool isMainThread()
{
+#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
+ // TODO: Remove threadutils.h and replace all usages with:
+ return QThread::isMainThread();
+#else
return QThread::currentThread() == qApp->thread();
+#endif
}
} // namespace Utils
diff --git a/src/libs/utils/treemodel.cpp b/src/libs/utils/treemodel.cpp
index 4649bb43c8..edddb2d484 100644
--- a/src/libs/utils/treemodel.cpp
+++ b/src/libs/utils/treemodel.cpp
@@ -459,13 +459,13 @@ void ModelTest::data()
// General Purpose roles that should return a QString
QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole);
if (variant.isValid())
- Q_ASSERT(variant.canConvert(QVariant::String));
+ Q_ASSERT(variant.canConvert(QMetaType::QString));
variant = model->data(model->index(0, 0), Qt::StatusTipRole);
if (variant.isValid())
- Q_ASSERT(variant.canConvert(QVariant::String));
+ Q_ASSERT(variant.canConvert(QMetaType::QString));
variant = model->data(model->index(0, 0), Qt::WhatsThisRole);
if (variant.isValid())
- Q_ASSERT(variant.canConvert(QVariant::String));
+ Q_ASSERT(variant.canConvert(QMetaType::QString));
// General Purpose roles that should return a QSize
variant = model->data(model->index(0, 0), Qt::SizeHintRole);
diff --git a/src/libs/utils/utility.h b/src/libs/utils/utility.h
new file mode 100644
index 0000000000..27de88d339
--- /dev/null
+++ b/src/libs/utils/utility.h
@@ -0,0 +1,14 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace Utils {
+
+template<typename Enumeration>
+[[nodiscard]] constexpr std::underlying_type_t<Enumeration> to_underlying(Enumeration enumeration) noexcept
+{
+ return static_cast<std::underlying_type_t<Enumeration>>(enumeration);
+}
+
+} // namespace Utils
diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs
index 9ef9108a2f..afc0f202aa 100644
--- a/src/libs/utils/utils.qbs
+++ b/src/libs/utils/utils.qbs
@@ -185,6 +185,8 @@ QtcLibrary {
"link.h",
"listmodel.h",
"listutils.h",
+ "lua.cpp",
+ "lua.h",
"macroexpander.cpp",
"macroexpander.h",
"mathutils.cpp",
diff --git a/src/libs/utils/wizard.cpp b/src/libs/utils/wizard.cpp
index 10d801c521..71069e60f6 100644
--- a/src/libs/utils/wizard.cpp
+++ b/src/libs/utils/wizard.cpp
@@ -276,6 +276,7 @@ public:
bool m_automaticProgressCreation = true;
WizardProgress *m_wizardProgress = nullptr;
QSet<QString> m_fieldNames;
+ bool m_skipForSubproject = false;
};
Wizard::Wizard(QWidget *parent, Qt::WindowFlags flags) :
@@ -365,8 +366,8 @@ QHash<QString, QVariant> Wizard::variables() const
QString typeOf(const QVariant &v)
{
QString result;
- switch (v.type()) {
- case QVariant::Map:
+ switch (v.typeId()) {
+ case QMetaType::QVariantMap:
result = QLatin1String("Object");
break;
default:
@@ -525,6 +526,32 @@ void Wizard::_q_pageRemoved(int pageId)
d->m_wizardProgress->removeItem(item);
}
+void Wizard::setSkipForSubprojects(bool skip)
+{
+ Q_D(Wizard);
+ d->m_skipForSubproject = skip;
+}
+
+int Wizard::nextId() const
+{
+ Q_D(const Wizard);
+ if (!d->m_skipForSubproject)
+ return QWizard::nextId();
+
+ const QList<int> allIds = pageIds();
+ int index = allIds.indexOf(currentId());
+ QTC_ASSERT(index > -1, return QWizard::nextId());
+
+ while (++index < allIds.size()) {
+ if (auto wp = qobject_cast<WizardPage *>(page(index))) {
+ if (!wp->skipForSubprojects())
+ return index;
+ }
+ }
+ QTC_CHECK(false); // should not happen
+ return QWizard::nextId();
+}
+
class WizardProgressPrivate
{
WizardProgress *q_ptr;
diff --git a/src/libs/utils/wizard.h b/src/libs/utils/wizard.h
index 35abff2f6d..ac3ad795bf 100644
--- a/src/libs/utils/wizard.h
+++ b/src/libs/utils/wizard.h
@@ -50,6 +50,10 @@ public:
void showVariables();
+ // allows to skip pages
+ void setSkipForSubprojects(bool skip);
+ int nextId() const override;
+
protected:
virtual QString stringify(const QVariant &v) const;
virtual QString evaluate(const QVariant &v) const;
diff --git a/src/libs/utils/wizardpage.h b/src/libs/utils/wizardpage.h
index 76d3039a67..1ab23f92b5 100644
--- a/src/libs/utils/wizardpage.h
+++ b/src/libs/utils/wizardpage.h
@@ -74,6 +74,9 @@ public:
void registerFieldWithName(const QString &name, QWidget *widget,
const char *property = nullptr, const char *changedSignal = nullptr);
+ void setSkipForSubprojects(bool skip) { m_skipForSubproject = skip; }
+ bool skipForSubprojects() const { return m_skipForSubproject; }
+
virtual bool handleReject();
virtual bool handleAccept();
@@ -85,6 +88,7 @@ private:
void registerFieldName(const QString &name);
QSet<QString> m_toRegister;
+ bool m_skipForSubproject = false;
};
} // namespace Utils
diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
index 605bdc3931..8523b392e8 100644
--- a/src/plugins/CMakeLists.txt
+++ b/src/plugins/CMakeLists.txt
@@ -48,7 +48,6 @@ add_subdirectory(autotoolsprojectmanager)
add_subdirectory(bazaar)
add_subdirectory(beautifier)
add_subdirectory(clearcase)
-add_subdirectory(cmakeprojectmanager)
add_subdirectory(cvs)
add_subdirectory(designer)
add_subdirectory(docker)
@@ -80,6 +79,7 @@ if (WITH_QMLDESIGNER)
endif()
add_subdirectory(python)
add_subdirectory(clangformat)
+add_subdirectory(cmakeprojectmanager)
# Level 7:
add_subdirectory(android)
@@ -99,6 +99,7 @@ add_subdirectory(perfprofiler)
add_subdirectory(qbsprojectmanager)
add_subdirectory(ctfvisualizer)
add_subdirectory(squish)
+add_subdirectory(appstatisticsmonitor)
# Level 8:
add_subdirectory(boot2qt)
@@ -121,4 +122,4 @@ add_subdirectory(qtapplicationmanager)
add_subdirectory(luatests)
add_subdirectory(tellajoke)
add_subdirectory(lualsp)
-add_subdirectory(luatemplates)
+add_subdirectory(rustls)
diff --git a/src/plugins/android/Android.json.in b/src/plugins/android/Android.json.in
index 7a020c5061..f68a25468c 100644
--- a/src/plugins/android/Android.json.in
+++ b/src/plugins/android/Android.json.in
@@ -14,7 +14,7 @@
],
"Category" : "Device Support",
"Description" : "Support for deployment to and execution on Android Devices.",
- "Url" : "http://necessitas.kde.org",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp
index fcc56980bc..38cfa2cdbf 100644
--- a/src/plugins/android/androidavdmanager.cpp
+++ b/src/plugins/android/androidavdmanager.cpp
@@ -4,12 +4,9 @@
#include "androidavdmanager.h"
#include "androidconfigurations.h"
#include "androidtr.h"
-#include "avdmanageroutputparser.h"
#include <coreplugin/icore.h>
-#include <utils/algorithm.h>
-#include <utils/async.h>
#include <utils/qtcprocess.h>
#include <QLoggingCategory>
@@ -19,98 +16,11 @@
using namespace Utils;
using namespace std::chrono_literals;
-namespace Android::Internal {
+namespace Android::Internal::AndroidAvdManager {
static Q_LOGGING_CATEGORY(avdManagerLog, "qtc.android.avdManager", QtWarningMsg)
-/*!
- Runs the \c avdmanager tool specific to configuration \a config with arguments \a args. Returns
- \c true if the command is successfully executed. Output is copied into \a output. The function
- blocks the calling thread.
- */
-bool AndroidAvdManager::avdManagerCommand(const QStringList &args, QString *output)
-{
- CommandLine cmd(androidConfig().avdManagerToolPath(), args);
- Process proc;
- proc.setEnvironment(androidConfig().toolsEnvironment());
- qCDebug(avdManagerLog).noquote() << "Running AVD Manager command:" << cmd.toUserOutput();
- proc.setCommand(cmd);
- proc.runBlocking();
- if (proc.result() == ProcessResult::FinishedWithSuccess) {
- if (output)
- *output = proc.allOutput();
- return true;
- }
- return false;
-}
-
-static void avdConfigEditManufacturerTag(const FilePath &avdPath, bool recoverMode = false)
-{
- if (!avdPath.exists())
- return;
-
- const FilePath configFilePath = avdPath / "config.ini";
- FileReader reader;
- if (!reader.fetch(configFilePath, QIODevice::ReadOnly | QIODevice::Text))
- return;
-
- FileSaver saver(configFilePath);
- QTextStream textStream(reader.data());
- while (!textStream.atEnd()) {
- QString line = textStream.readLine();
- if (line.contains("hw.device.manufacturer")) {
- if (recoverMode)
- line.replace("#", "");
- else
- line.prepend("#");
- }
- line.append("\n");
- saver.write(line.toUtf8());
- }
- saver.finalize();
-}
-
-static AndroidDeviceInfoList listVirtualDevices()
-{
- QString output;
- AndroidDeviceInfoList avdList;
- /*
- Currenly avdmanager tool fails to parse some AVDs because the correct
- device definitions at devices.xml does not have some of the newest devices.
- Particularly, failing because of tag "hw.device.manufacturer", thus removing
- it would make paring successful. However, it has to be returned afterwards,
- otherwise, Android Studio would give an error during parsing also. So this fix
- aim to keep support for Qt Creator and Android Studio.
- */
- FilePaths allAvdErrorPaths;
- FilePaths avdErrorPaths;
-
- do {
- if (!AndroidAvdManager::avdManagerCommand({"list", "avd"}, &output)) {
- qCDebug(avdManagerLog)
- << "Avd list command failed" << output << androidConfig().sdkToolsVersion();
- return {};
- }
-
- avdErrorPaths.clear();
- avdList = parseAvdList(output, &avdErrorPaths);
- allAvdErrorPaths << avdErrorPaths;
- for (const FilePath &avdPath : std::as_const(avdErrorPaths))
- avdConfigEditManufacturerTag(avdPath); // comment out manufacturer tag
- } while (!avdErrorPaths.isEmpty()); // try again
-
- for (const FilePath &avdPath : std::as_const(allAvdErrorPaths))
- avdConfigEditManufacturerTag(avdPath, true); // re-add manufacturer tag
-
- return avdList;
-}
-
-QFuture<AndroidDeviceInfoList> AndroidAvdManager::avdList() const
-{
- return Utils::asyncRun(listVirtualDevices);
-}
-
-QString AndroidAvdManager::startAvd(const QString &name) const
+QString startAvd(const QString &name)
{
if (!findAvd(name).isEmpty() || startAvdAsync(name))
return waitForAvd(name);
@@ -133,9 +43,9 @@ static bool is32BitUserSpace()
return false;
}
-bool AndroidAvdManager::startAvdAsync(const QString &avdName) const
+bool startAvdAsync(const QString &avdName)
{
- const FilePath emulator = androidConfig().emulatorToolPath();
+ const FilePath emulator = AndroidConfig::emulatorToolPath();
if (!emulator.exists()) {
QMetaObject::invokeMethod(Core::ICore::mainWindow(), [emulator] {
QMessageBox::critical(Core::ICore::dialogParent(),
@@ -165,11 +75,11 @@ bool AndroidAvdManager::startAvdAsync(const QString &avdName) const
});
// start the emulator
- CommandLine cmd(androidConfig().emulatorToolPath());
+ CommandLine cmd(emulator);
if (is32BitUserSpace())
cmd.addArg("-force-32bit");
- cmd.addArgs(androidConfig().emulatorArgs(), CommandLine::Raw);
+ cmd.addArgs(AndroidConfig::emulatorArgs(), CommandLine::Raw);
cmd.addArgs({"-avd", avdName});
qCDebug(avdManagerLog).noquote() << "Running command (startAvdAsync):" << cmd.toUserOutput();
avdProcess->setCommand(cmd);
@@ -177,9 +87,9 @@ bool AndroidAvdManager::startAvdAsync(const QString &avdName) const
return avdProcess->waitForStarted(QDeadlineTimer::Forever);
}
-QString AndroidAvdManager::findAvd(const QString &avdName) const
+QString findAvd(const QString &avdName)
{
- const QList<AndroidDeviceInfo> devices = androidConfig().connectedDevices();
+ const QList<AndroidDeviceInfo> devices = AndroidConfig::connectedDevices();
for (const AndroidDeviceInfo &device : devices) {
if (device.type != ProjectExplorer::IDevice::Emulator)
continue;
@@ -189,8 +99,22 @@ QString AndroidAvdManager::findAvd(const QString &avdName) const
return {};
}
-QString AndroidAvdManager::waitForAvd(const QString &avdName,
- const std::optional<QFuture<void>> &future) const
+static bool waitForBooted(const QString &serialNumber, const std::optional<QFuture<void>> &future)
+{
+ // found a serial number, now wait until it's done booting...
+ for (int i = 0; i < 60; ++i) {
+ if (future && future->isCanceled())
+ return false;
+ if (isAvdBooted(serialNumber))
+ return true;
+ QThread::sleep(2);
+ if (!AndroidConfig::isConnected(serialNumber)) // device was disconnected
+ return false;
+ }
+ return false;
+}
+
+QString waitForAvd(const QString &avdName, const std::optional<QFuture<void>> &future)
{
// 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
@@ -206,36 +130,17 @@ QString AndroidAvdManager::waitForAvd(const QString &avdName,
return {};
}
-bool AndroidAvdManager::isAvdBooted(const QString &device) const
+bool isAvdBooted(const QString &device)
{
- QStringList arguments = AndroidDeviceInfo::adbSelector(device);
- arguments << "shell" << "getprop" << "init.svc.bootanim";
-
- const CommandLine command({androidConfig().adbToolPath(), arguments});
- qCDebug(avdManagerLog).noquote() << "Running command (isAvdBooted):" << command.toUserOutput();
+ const CommandLine cmd{AndroidConfig::adbToolPath(), {AndroidDeviceInfo::adbSelector(device),
+ "shell", "getprop", "init.svc.bootanim"}};
+ qCDebug(avdManagerLog).noquote() << "Running command (isAvdBooted):" << cmd.toUserOutput();
Process adbProc;
- adbProc.setCommand(command);
+ adbProc.setCommand(cmd);
adbProc.runBlocking();
if (adbProc.result() != ProcessResult::FinishedWithSuccess)
return false;
- QString value = adbProc.allOutput().trimmed();
- return value == "stopped";
-}
-
-bool AndroidAvdManager::waitForBooted(const QString &serialNumber,
- 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 (future && future->isCanceled())
- return false;
- if (isAvdBooted(serialNumber))
- return true;
- QThread::sleep(2);
- if (!androidConfig().isConnected(serialNumber)) // device was disconnected
- return false;
- }
- return false;
+ return adbProc.allOutput().trimmed() == "stopped";
}
-} // Android::Internal
+} // namespace Android::Internal::AndroidAvdManager
diff --git a/src/plugins/android/androidavdmanager.h b/src/plugins/android/androidavdmanager.h
index 81686748ed..5a1f188038 100644
--- a/src/plugins/android/androidavdmanager.h
+++ b/src/plugins/android/androidavdmanager.h
@@ -2,28 +2,16 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
-#include "androiddeviceinfo.h"
-
#include <QFuture>
#include <optional>
-namespace Android::Internal {
-
-class AndroidAvdManager
-{
-public:
- QFuture<AndroidDeviceInfoList> avdList() const;
-
- QString startAvd(const QString &name) const;
- bool startAvdAsync(const QString &avdName) const;
- QString findAvd(const QString &avdName) const;
- QString waitForAvd(const QString &avdName, const std::optional<QFuture<void>> &future = {}) const;
- bool isAvdBooted(const QString &device) const;
- static bool avdManagerCommand(const QStringList &args, QString *output);
+namespace Android::Internal::AndroidAvdManager {
-private:
- bool waitForBooted(const QString &serialNumber, const std::optional<QFuture<void>> &future = {}) const;
-};
+QString startAvd(const QString &name);
+bool startAvdAsync(const QString &avdName);
+QString findAvd(const QString &avdName);
+QString waitForAvd(const QString &avdName, const std::optional<QFuture<void>> &future = {});
+bool isAvdBooted(const QString &device);
-} // Android::Internal
+} // namespace Android::Internal::AndroidAvdManager
diff --git a/src/plugins/android/androidbuildapkstep.cpp b/src/plugins/android/androidbuildapkstep.cpp
index 317b72722b..51f7318097 100644
--- a/src/plugins/android/androidbuildapkstep.cpp
+++ b/src/plugins/android/androidbuildapkstep.cpp
@@ -415,7 +415,7 @@ bool AndroidBuildApkWidget::isOpenSslLibsIncluded()
QString AndroidBuildApkWidget::openSslIncludeFileContent(const FilePath &projectPath)
{
- QString openSslPath = androidConfig().openSslLocation().toString();
+ QString openSslPath = AndroidConfig::openSslLocation().toString();
if (projectPath.endsWith(".pro"))
return "android: include(" + openSslPath + "/openssl.pri)";
if (projectPath.endsWith("CMakeLists.txt"))
@@ -541,7 +541,7 @@ bool AndroidBuildApkStep::init()
QStringList arguments = {"--input", m_inputFile.path(),
"--output", outputDir.path(),
"--android-platform", m_buildTargetSdk,
- "--jdk", androidConfig().openJDKLocation().path()};
+ "--jdk", AndroidConfig::openJDKLocation().path()};
if (verboseOutput())
arguments << "--verbose";
@@ -929,17 +929,16 @@ QVariant AndroidBuildApkStep::data(Utils::Id id) const
{
if (id == Constants::AndroidNdkPlatform) {
if (auto qtVersion = QtKitAspect::qtVersion(kit()))
- return androidConfig()
- .bestNdkPlatformMatch(AndroidManager::minimumSDK(target()), qtVersion);
+ return AndroidConfig::bestNdkPlatformMatch(AndroidManager::minimumSDK(target()), qtVersion);
return {};
}
if (id == Constants::NdkLocation) {
if (auto qtVersion = QtKitAspect::qtVersion(kit()))
- return QVariant::fromValue(androidConfig().ndkLocation(qtVersion));
+ return QVariant::fromValue(AndroidConfig::ndkLocation(qtVersion));
return {};
}
if (id == Constants::SdkLocation)
- return QVariant::fromValue(androidConfig().sdkLocation());
+ return QVariant::fromValue(AndroidConfig::sdkLocation());
if (id == Constants::AndroidMkSpecAbis)
return AndroidManager::applicationAbis(target());
@@ -1000,9 +999,9 @@ QAbstractItemModel *AndroidBuildApkStep::keystoreCertificates()
"-storepass", m_keystorePasswd, "-J-Duser.language=en"};
Process keytoolProc;
- keytoolProc.setCommand({androidConfig().keytoolPath(), params});
+ keytoolProc.setCommand({AndroidConfig::keytoolPath(), params});
using namespace std::chrono_literals;
- keytoolProc.runBlocking(30s, EventLoopMode::On);
+ keytoolProc.runBlocking(30s);
if (keytoolProc.result() > ProcessResult::FinishedWithError)
QMessageBox::critical(nullptr, Tr::tr("Error"), Tr::tr("Failed to run keytool."));
else
diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp
index f9835e17b0..8a4a0465c3 100644
--- a/src/plugins/android/androidconfigurations.cpp
+++ b/src/plugins/android/androidconfigurations.cpp
@@ -59,8 +59,8 @@
# include <QTest>
#endif // WITH_TESTS
-using namespace QtSupport;
using namespace ProjectExplorer;
+using namespace QtSupport;
using namespace Utils;
namespace {
@@ -141,43 +141,177 @@ static QString buildToolsPackageMarker()
return QLatin1String(Constants::buildToolsPackageName) + ";";
}
-//////////////////////////////////
-// AndroidConfig
-//////////////////////////////////
+static QString getAvdName(const QString &serialnumber)
+{
+ const int index = serialnumber.indexOf(QLatin1String("-"));
+ if (index == -1)
+ return {};
+ bool ok;
+ const int port = serialnumber.mid(index + 1).toInt(&ok);
+ if (!ok)
+ return {};
+
+ QTcpSocket tcpSocket;
+ tcpSocket.connectToHost(QHostAddress(QHostAddress::LocalHost), port);
+ if (!tcpSocket.waitForConnected(100)) // Don't wait more than 100ms for a local connection
+ return {};
+
+ tcpSocket.write("avd name\nexit\n");
+ tcpSocket.waitForDisconnected(500);
+
+ const QByteArrayList response = tcpSocket.readAll().split('\n');
+ // The input "avd name" might not be echoed as-is, but contain ASCII control sequences.
+ for (int i = response.size() - 1; i > 1; --i) {
+ if (response.at(i).startsWith("OK"))
+ return QString::fromLatin1(response.at(i - 1)).trimmed();
+ }
+ return {};
+}
+
+static QString getDeviceProperty(const QString &device, const QString &property)
+{
+ // workaround for '????????????' serial numbers
+ Process adbProc;
+ adbProc.setCommand({AndroidConfig::adbToolPath(),
+ {AndroidDeviceInfo::adbSelector(device), "shell", "getprop", property}});
+ adbProc.runBlocking();
+ if (adbProc.result() == ProcessResult::FinishedWithSuccess)
+ return adbProc.allOutput();
+ return {};
+}
-QLatin1String AndroidConfig::toolchainPrefix(const Abi &abi)
+static QLatin1String toolsPrefix(const Abi &abi)
{
switch (abi.architecture()) {
case Abi::ArmArchitecture:
if (abi.wordWidth() == 64)
- return AArch64ToolchainPrefix;
- return ArmToolchainPrefix;
+ return AArch64ToolsPrefix;
+ return ArmToolsPrefix;
case Abi::X86Architecture:
if (abi.wordWidth() == 64)
- return X86_64ToolchainPrefix;
- return X86ToolchainPrefix;
+ return X86_64ToolsPrefix;
+ return X86ToolsPrefix;
default:
return Unknown;
}
}
-QLatin1String AndroidConfig::toolsPrefix(const Abi &abi)
+static QLatin1String toolchainPrefix(const Abi &abi)
{
switch (abi.architecture()) {
case Abi::ArmArchitecture:
if (abi.wordWidth() == 64)
- return AArch64ToolsPrefix;
- return ArmToolsPrefix;
+ return AArch64ToolchainPrefix;
+ return ArmToolchainPrefix;
case Abi::X86Architecture:
if (abi.wordWidth() == 64)
- return X86_64ToolsPrefix;
- return X86ToolsPrefix;
+ return X86_64ToolchainPrefix;
+ return X86ToolchainPrefix;
default:
return Unknown;
}
}
-QLatin1String AndroidConfig::displayName(const Abi &abi)
+static FilePath gdbPathFromNdk(const Abi &abi, const FilePath &ndkLocation)
+{
+ const FilePath path = ndkLocation.pathAppended(
+ QString("prebuilt/%1/bin/gdb%2").arg(AndroidConfig::toolchainHostFromNdk(ndkLocation),
+ QString(QTC_HOST_EXE_SUFFIX)));
+ if (path.exists())
+ return path;
+ // fallback for old NDKs (e.g. 10e)
+ return ndkLocation.pathAppended(QString("toolchains/%1-4.9/prebuilt/%2/bin/%3-gdb%4")
+ .arg(toolchainPrefix(abi),
+ AndroidConfig::toolchainHostFromNdk(ndkLocation),
+ toolsPrefix(abi),
+ QString(QTC_HOST_EXE_SUFFIX)));
+}
+
+static FilePath lldbPathFromNdk(const FilePath &ndkLocation)
+{
+ const FilePath path = ndkLocation.pathAppended(
+ QString("toolchains/llvm/prebuilt/%1/bin/lldb%2")
+ .arg(AndroidConfig::toolchainHostFromNdk(ndkLocation), QString(QTC_HOST_EXE_SUFFIX)));
+ return path.exists() ? path : FilePath();
+}
+
+namespace AndroidConfig {
+
+struct SdkForQtVersions
+{
+ QList<QVersionNumber> versions;
+ QStringList essentialPackages;
+
+ bool containsVersion(const QVersionNumber &qtVersion) const
+ {
+ return versions.contains(qtVersion)
+ || versions.contains(QVersionNumber(qtVersion.majorVersion(),
+ qtVersion.minorVersion()));
+ }
+};
+
+struct AndroidConfigData
+{
+ void load(const QtcSettings &settings);
+ void save(QtcSettings &settings) const;
+ void parseDependenciesJson();
+
+ FilePath m_sdkLocation;
+ FilePath m_temporarySdkToolsPath;
+ QStringList m_sdkManagerToolArgs;
+ FilePath m_openJDKLocation;
+ FilePath m_keystoreLocation;
+ FilePath m_openSslLocation;
+ QString m_emulatorArgs;
+ bool m_automaticKitCreation = true;
+ QUrl m_sdkToolsUrl;
+ QByteArray m_sdkToolsSha256;
+ QStringList m_commonEssentialPkgs;
+ SdkForQtVersions m_defaultSdkDepends;
+ QList<SdkForQtVersions> m_specificQtVersions;
+ QStringList m_customNdkList;
+ FilePath m_defaultNdk;
+ bool m_sdkFullyConfigured = false;
+ QHash<QString, QString> m_serialNumberToDeviceName; // cache
+};
+
+static AndroidConfigData &config()
+{
+ static AndroidConfigData theAndroidConfig;
+ return theAndroidConfig;
+}
+
+static FilePath ndkSubPath(const SdkForQtVersions &packages)
+{
+ const QString ndkPrefix = ndkPackageMarker();
+ for (const QString &package : packages.essentialPackages)
+ if (package.startsWith(ndkPrefix))
+ return FilePath::fromString(NdksSubDir) / package.sliced(ndkPrefix.length());
+
+ return {};
+}
+
+static FilePath ndkSubPathFromQtVersion(const QtVersion &version)
+{
+ if (auto androidQtVersion = dynamic_cast<const AndroidQtVersion *>(&version)) {
+ bool ok;
+ const AndroidQtVersion::BuiltWith bw = androidQtVersion->builtWith(&ok);
+ if (ok)
+ return FilePath::fromString(NdksSubDir) / bw.ndkVersion.toString();
+ }
+
+ for (const SdkForQtVersions &item : config().m_specificQtVersions) {
+ if (item.containsVersion(version.qtVersion()))
+ return ndkSubPath(item);
+ }
+ return ndkSubPath(config().m_defaultSdkDepends);
+}
+
+//////////////////////////////////
+// AndroidConfig
+//////////////////////////////////
+
+QLatin1String displayName(const Abi &abi)
{
switch (abi.architecture()) {
case Abi::ArmArchitecture:
@@ -193,11 +327,11 @@ QLatin1String AndroidConfig::displayName(const Abi &abi)
}
}
-void AndroidConfig::load(const QtcSettings &settings)
+void AndroidConfigData::load(const QtcSettings &settings)
{
// user settings
QVariant emulatorArgs = settings.value(EmulatorArgsKey, QString("-netdelay none -netspeed full"));
- if (emulatorArgs.typeId() == QVariant::StringList) // Changed in 8.0 from QStringList to QString.
+ if (emulatorArgs.typeId() == QMetaType::QStringList) // Changed in 8.0 from QStringList to QString.
emulatorArgs = ProcessArgs::joinArgs(emulatorArgs.toStringList());
m_emulatorArgs = emulatorArgs.toString();
m_sdkLocation = FilePath::fromUserInput(settings.value(SDKLocationKey).toString()).cleanPath();
@@ -224,7 +358,7 @@ void AndroidConfig::load(const QtcSettings &settings)
// persistent settings
}
m_customNdkList.removeAll("");
- if (!m_defaultNdk.isEmpty() && ndkVersion(m_defaultNdk).isNull()) {
+ if (!m_defaultNdk.isEmpty() && AndroidConfig::ndkVersion(m_defaultNdk).isNull()) {
if (avdConfigLog().isDebugEnabled())
qCDebug(avdConfigLog).noquote() << "Clearing invalid default NDK setting:"
<< m_defaultNdk.toUserOutput();
@@ -233,7 +367,7 @@ void AndroidConfig::load(const QtcSettings &settings)
parseDependenciesJson();
}
-void AndroidConfig::save(QtcSettings &settings) const
+void AndroidConfigData::save(QtcSettings &settings) const
{
QFileInfo fileInfo(sdkSettingsFileName());
if (fileInfo.exists())
@@ -251,7 +385,7 @@ void AndroidConfig::save(QtcSettings &settings) const
settings.setValue(SdkFullyConfiguredKey, m_sdkFullyConfigured);
}
-void AndroidConfig::parseDependenciesJson()
+void AndroidConfigData::parseDependenciesJson()
{
const FilePath sdkConfigUserFile = Core::ICore::userResourcePath(JsonFilePath);
const FilePath sdkConfigFile = Core::ICore::resourcePath(JsonFilePath);
@@ -368,7 +502,7 @@ static QList<int> availableNdkPlatformsV21Plus(const FilePath &ndkLocation, cons
if (abis.isEmpty())
return {};
- const QString abi = AndroidConfig::toolsPrefix(abis.first());
+ const QString abi = toolsPrefix(abis.first());
const FilePath libPath =
AndroidConfig::toolchainPathFromNdk(ndkLocation, hostOs) / "sysroot/usr/lib" / abi;
const FilePaths dirEntries = libPath.dirEntries(QDir::Dirs | QDir::NoDotAndDotDot);
@@ -389,125 +523,90 @@ static QList<int> availableNdkPlatformsImpl(const FilePath &ndkLocation, const A
return Utils::sorted(std::move(result), std::greater<>());
}
-QList<int> AndroidConfig::availableNdkPlatforms(const QtVersion *qtVersion) const
+static QList<int> availableNdkPlatforms(const QtVersion *qtVersion)
{
- return availableNdkPlatformsImpl(ndkLocation(qtVersion), qtVersion->qtAbis(),
+ return availableNdkPlatformsImpl(AndroidConfig::ndkLocation(qtVersion), qtVersion->qtAbis(),
HostOsInfo::hostOs());
}
-QStringList AndroidConfig::getCustomNdkList() const
-{
- return m_customNdkList;
-}
+QStringList getCustomNdkList() { return config().m_customNdkList; }
-void AndroidConfig::addCustomNdk(const QString &customNdk)
+void addCustomNdk(const QString &customNdk)
{
- if (!m_customNdkList.contains(customNdk))
- m_customNdkList.append(customNdk);
+ if (!config().m_customNdkList.contains(customNdk))
+ config().m_customNdkList.append(customNdk);
}
-void AndroidConfig::removeCustomNdk(const QString &customNdk)
+void removeCustomNdk(const QString &customNdk)
{
- m_customNdkList.removeAll(customNdk);
+ config().m_customNdkList.removeAll(customNdk);
}
-void AndroidConfig::setDefaultNdk(const Utils::FilePath &defaultNdk)
-{
- m_defaultNdk = defaultNdk;
-}
+void setDefaultNdk(const FilePath &defaultNdk) { config().m_defaultNdk = defaultNdk; }
-FilePath AndroidConfig::defaultNdk() const
-{
- return m_defaultNdk;
-}
+FilePath defaultNdk() { return config().m_defaultNdk; }
-FilePath AndroidConfig::openSslLocation() const
-{
- return m_openSslLocation;
-}
+FilePath openSslLocation() { return config().m_openSslLocation; }
-void AndroidConfig::setOpenSslLocation(const FilePath &openSslLocation)
+void setOpenSslLocation(const FilePath &openSslLocation)
{
- m_openSslLocation = openSslLocation;
+ config().m_openSslLocation = openSslLocation;
}
-QStringList AndroidConfig::apiLevelNamesFor(const SdkPlatformList &platforms)
+QStringList apiLevelNamesFor(const SdkPlatformList &platforms)
{
return Utils::transform(platforms, AndroidConfig::apiLevelNameFor);
}
-QString AndroidConfig::apiLevelNameFor(const SdkPlatform *platform)
+QString apiLevelNameFor(const SdkPlatform *platform)
{
if (platform && platform->apiLevel() > 0) {
QString sdkStylePath = platform->sdkStylePath();
return sdkStylePath.remove("platforms;");
}
-
return {};
}
-FilePath AndroidConfig::adbToolPath() const
+FilePath adbToolPath()
{
- return m_sdkLocation.pathAppended("platform-tools/adb").withExecutableSuffix();
+ return config().m_sdkLocation.pathAppended("platform-tools/adb").withExecutableSuffix();
}
-FilePath AndroidConfig::emulatorToolPath() const
+FilePath emulatorToolPath()
{
- const FilePath emulatorFile = m_sdkLocation.pathAppended("emulator/emulator")
- .withExecutableSuffix();
- if (emulatorFile.exists())
- return emulatorFile;
-
- return {};
+ const FilePath emulatorFile
+ = config().m_sdkLocation.pathAppended("emulator/emulator").withExecutableSuffix();
+ return emulatorFile.exists() ? emulatorFile : FilePath();
}
-FilePath AndroidConfig::sdkManagerToolPath() const
+FilePath sdkManagerToolPath()
{
- const FilePath sdkmanagerPath = m_sdkLocation.pathAppended(Constants::cmdlineToolsName)
+ const FilePath sdkmanagerPath = config()
+ .m_sdkLocation.pathAppended(Constants::cmdlineToolsName)
.pathAppended("latest/bin/sdkmanager" ANDROID_BAT_SUFFIX);
if (sdkmanagerPath.exists())
return sdkmanagerPath;
// If it's a first time install use the path of Constants::cmdlineToolsName temporary download
- const FilePath sdkmanagerTmpPath = m_temporarySdkToolsPath.pathAppended(
+ const FilePath sdkmanagerTmpPath = config().m_temporarySdkToolsPath.pathAppended(
"/bin/sdkmanager" ANDROID_BAT_SUFFIX);
- if (sdkmanagerTmpPath.exists())
- return sdkmanagerTmpPath;
-
- return {};
+ return sdkmanagerTmpPath.exists() ? sdkmanagerTmpPath : FilePath();
}
-FilePath AndroidConfig::avdManagerToolPath() const
+FilePath avdManagerToolPath()
{
- const FilePath sdkmanagerPath = m_sdkLocation.pathAppended(Constants::cmdlineToolsName)
+ const FilePath sdkmanagerPath = config()
+ .m_sdkLocation.pathAppended(Constants::cmdlineToolsName)
.pathAppended("/latest/bin/avdmanager" ANDROID_BAT_SUFFIX);
- if (sdkmanagerPath.exists())
- return sdkmanagerPath;
-
- return {};
+ return sdkmanagerPath.exists() ? sdkmanagerPath : FilePath();
}
-void AndroidConfig::setTemporarySdkToolsPath(const Utils::FilePath &path)
+void setTemporarySdkToolsPath(const FilePath &path)
{
- m_temporarySdkToolsPath = path;
+ config().m_temporarySdkToolsPath = path;
}
-FilePath AndroidConfig::sdkToolsVersionPath() const
-{
- const FilePath sdkVersionPaths = m_sdkLocation.pathAppended(Constants::cmdlineToolsName)
- .pathAppended("/latest/source.properties");
- if (sdkVersionPaths.exists())
- return sdkVersionPaths;
-
- // If it's a first time install use the path of Constants::cmdlineToolsName temporary download
- const FilePath tmpSdkPath = m_temporarySdkToolsPath.pathAppended("source.properties");
- if (tmpSdkPath.exists())
- return tmpSdkPath;
-
- return {};
-}
-
-FilePath AndroidConfig::toolchainPathFromNdk(const FilePath &ndkLocation, OsType hostOs)
+FilePath toolchainPathFromNdk(const FilePath &ndkLocation, OsType hostOs)
{
const FilePath tcPath = ndkLocation / "toolchains/";
FilePath toolchainPath;
@@ -544,12 +643,12 @@ FilePath AndroidConfig::toolchainPathFromNdk(const FilePath &ndkLocation, OsType
return {};
}
-FilePath AndroidConfig::toolchainPath(const QtVersion *qtVersion) const
+FilePath toolchainPath(const QtVersion *qtVersion)
{
return toolchainPathFromNdk(ndkLocation(qtVersion));
}
-FilePath AndroidConfig::clangPathFromNdk(const FilePath &ndkLocation)
+FilePath clangPathFromNdk(const FilePath &ndkLocation)
{
const FilePath path = toolchainPathFromNdk(ndkLocation);
if (path.isEmpty())
@@ -557,57 +656,25 @@ FilePath AndroidConfig::clangPathFromNdk(const FilePath &ndkLocation)
return path.pathAppended("bin/clang").withExecutableSuffix();
}
-FilePath AndroidConfig::gdbPath(const Abi &abi, const QtVersion *qtVersion) const
-{
- return gdbPathFromNdk(abi, ndkLocation(qtVersion));
-}
-
-FilePath AndroidConfig::gdbPathFromNdk(const Abi &abi, const FilePath &ndkLocation)
-{
- const FilePath path = ndkLocation.pathAppended(
- QString("prebuilt/%1/bin/gdb%2").arg(toolchainHostFromNdk(ndkLocation),
- QString(QTC_HOST_EXE_SUFFIX)));
- if (path.exists())
- return path;
- // fallback for old NDKs (e.g. 10e)
- return ndkLocation.pathAppended(QString("toolchains/%1-4.9/prebuilt/%2/bin/%3-gdb%4")
- .arg(toolchainPrefix(abi),
- toolchainHostFromNdk(ndkLocation),
- toolsPrefix(abi),
- QString(QTC_HOST_EXE_SUFFIX)));
-}
-
-FilePath AndroidConfig::lldbPathFromNdk(const FilePath &ndkLocation)
-{
- const FilePath path = ndkLocation.pathAppended(
- QString("toolchains/llvm/prebuilt/%1/bin/lldb%2").arg(toolchainHostFromNdk(ndkLocation),
- QString(QTC_HOST_EXE_SUFFIX)));
- if (path.exists())
- return path;
- return {};
-}
-
-FilePath AndroidConfig::makePathFromNdk(const FilePath &ndkLocation)
+FilePath makePathFromNdk(const FilePath &ndkLocation)
{
return ndkLocation.pathAppended(
QString("prebuilt/%1/bin/make%2").arg(toolchainHostFromNdk(ndkLocation),
QString(QTC_HOST_EXE_SUFFIX)));
}
-FilePath AndroidConfig::openJDKBinPath() const
+static FilePath openJDKBinPath()
{
- const FilePath path = m_openJDKLocation;
- if (!path.isEmpty())
- return path.pathAppended("bin");
- return path;
+ const FilePath path = config().m_openJDKLocation;
+ return path.isEmpty() ? path : path.pathAppended("bin");
}
-FilePath AndroidConfig::keytoolPath() const
+FilePath keytoolPath()
{
return openJDKBinPath().pathAppended(keytoolName).withExecutableSuffix();
}
-QList<AndroidDeviceInfo> AndroidConfig::connectedDevices(QString *error) const
+QList<AndroidDeviceInfo> connectedDevices(QString *error)
{
QList<AndroidDeviceInfo> devices;
Process adbProc;
@@ -662,7 +729,7 @@ QList<AndroidDeviceInfo> AndroidConfig::connectedDevices(QString *error) const
return devices;
}
-bool AndroidConfig::isConnected(const QString &serialNumber) const
+bool isConnected(const QString &serialNumber)
{
const QList<AndroidDeviceInfo> devices = connectedDevices();
for (const AndroidDeviceInfo &device : devices) {
@@ -672,90 +739,46 @@ bool AndroidConfig::isConnected(const QString &serialNumber) const
return false;
}
-QString AndroidConfig::getDeviceProperty(const QString &device, const QString &property)
-{
- // workaround for '????????????' serial numbers
- CommandLine cmd(androidConfig().adbToolPath(), AndroidDeviceInfo::adbSelector(device));
- cmd.addArgs({"shell", "getprop", property});
+bool sdkFullyConfigured() { return config().m_sdkFullyConfigured; }
- Process adbProc;
- adbProc.setCommand(cmd);
- adbProc.runBlocking();
- if (adbProc.result() != ProcessResult::FinishedWithSuccess)
- return {};
-
- return adbProc.allOutput();
-}
-
-int AndroidConfig::getSDKVersion(const QString &device)
+void setSdkFullyConfigured(bool allEssentialsInstalled)
{
- QString tmp = getDeviceProperty(device, "ro.build.version.sdk");
- if (tmp.isEmpty())
- return -1;
- return tmp.trimmed().toInt();
+ config().m_sdkFullyConfigured = allEssentialsInstalled;
}
-QString AndroidConfig::getAvdName(const QString &serialnumber)
+int getSDKVersion(const QString &device)
{
- int index = serialnumber.indexOf(QLatin1String("-"));
- if (index == -1)
- return {};
- bool ok;
- int port = serialnumber.mid(index + 1).toInt(&ok);
- if (!ok)
- return {};
-
- const QByteArray avdName = "avd name\n";
-
- QTcpSocket tcpSocket;
- tcpSocket.connectToHost(QHostAddress(QHostAddress::LocalHost), port);
- if (!tcpSocket.waitForConnected(100)) // Don't wait more than 100ms for a local connection
- return {};
-
- tcpSocket.write(avdName + "exit\n");
- tcpSocket.waitForDisconnected(500);
-
- QByteArray name;
- const QByteArrayList response = tcpSocket.readAll().split('\n');
- // The input "avd name" might not be echoed as-is, but contain ASCII
- // control sequences.
- for (int i = response.size() - 1; i > 1; --i) {
- if (response.at(i).startsWith("OK")) {
- name = response.at(i - 1);
- break;
- }
- }
- return QString::fromLatin1(name).trimmed();
+ const QString tmp = getDeviceProperty(device, "ro.build.version.sdk");
+ return tmp.isEmpty() ? -1 : tmp.trimmed().toInt();
}
//!
-//! \brief AndroidConfigurations::getProductModel
+//! \brief AndroidConfig::getProductModel
//! \param device serial number
//! \return the produce model of the device or if that cannot be read the serial number
//!
-QString AndroidConfig::getProductModel(const QString &device) const
+QString getProductModel(const QString &device)
{
- if (m_serialNumberToDeviceName.contains(device))
- return m_serialNumberToDeviceName.value(device);
+ if (config().m_serialNumberToDeviceName.contains(device))
+ return config().m_serialNumberToDeviceName.value(device);
- QString model = getDeviceProperty(device, "ro.product.model").trimmed();
+ const QString model = getDeviceProperty(device, "ro.product.model").trimmed();
if (model.isEmpty())
return device;
- if (!device.startsWith(QLatin1String("????")))
- m_serialNumberToDeviceName.insert(device, model);
+ if (!device.startsWith("????"))
+ config().m_serialNumberToDeviceName.insert(device, model);
return model;
}
-QStringList AndroidConfig::getAbis(const QString &device)
+QStringList getAbis(const QString &device)
{
- const FilePath adbTool = androidConfig().adbToolPath();
+ const FilePath adbTool = AndroidConfig::adbToolPath();
QStringList result;
// First try via ro.product.cpu.abilist
- QStringList arguments = AndroidDeviceInfo::adbSelector(device);
- arguments << "shell" << "getprop" << "ro.product.cpu.abilist";
Process adbProc;
- adbProc.setCommand({adbTool, arguments});
+ adbProc.setCommand({adbTool,
+ {AndroidDeviceInfo::adbSelector(device), "shell", "getprop", "ro.product.cpu.abilist"}});
adbProc.runBlocking();
if (adbProc.result() != ProcessResult::FinishedWithSuccess)
return result;
@@ -769,15 +792,13 @@ QStringList AndroidConfig::getAbis(const QString &device)
// Fall back to ro.product.cpu.abi, ro.product.cpu.abi2 ...
for (int i = 1; i < 6; ++i) {
- QStringList arguments = AndroidDeviceInfo::adbSelector(device);
- arguments << QLatin1String("shell") << QLatin1String("getprop");
+ CommandLine cmd{adbTool, {AndroidDeviceInfo::adbSelector(device), "shell", "getprop"}};
if (i == 1)
- arguments << QLatin1String("ro.product.cpu.abi");
+ cmd.addArg("ro.product.cpu.abi");
else
- arguments << QString::fromLatin1("ro.product.cpu.abi%1").arg(i);
-
+ cmd.addArg(QString::fromLatin1("ro.product.cpu.abi%1").arg(i));
Process abiProc;
- abiProc.setCommand({adbTool, arguments});
+ abiProc.setCommand(cmd);
abiProc.runBlocking();
if (abiProc.result() != ProcessResult::FinishedWithSuccess)
return result;
@@ -790,17 +811,14 @@ QStringList AndroidConfig::getAbis(const QString &device)
return result;
}
-bool AndroidConfig::isValidNdk(const QString &ndkLocation) const
+bool isValidNdk(const QString &ndkLocation)
{
- auto ndkPath = Utils::FilePath::fromUserInput(ndkLocation);
-
- if (!ndkPath.exists())
- return false;
+ const FilePath ndkPath = FilePath::fromUserInput(ndkLocation);
- if (!ndkPath.pathAppended("toolchains").exists())
+ if (!ndkPath.exists() || !ndkPath.pathAppended("toolchains").exists())
return false;
- const QVersionNumber version = ndkVersion(ndkPath);
+ const QVersionNumber version = AndroidConfig::ndkVersion(ndkPath);
if (version.isNull())
return false;
@@ -808,11 +826,10 @@ bool AndroidConfig::isValidNdk(const QString &ndkLocation) const
if (version.majorVersion() <= 22
&& (!ndkPlatformsDir.exists() || ndkPlatformsDir.toString().contains(' ')))
return false;
-
return true;
}
-QString AndroidConfig::bestNdkPlatformMatch(int target, const QtVersion *qtVersion) const
+QString bestNdkPlatformMatch(int target, const QtVersion *qtVersion)
{
target = std::max(AndroidManager::defaultMinimumSDK(qtVersion), target);
const QList<int> platforms = availableNdkPlatforms(qtVersion);
@@ -823,19 +840,35 @@ QString AndroidConfig::bestNdkPlatformMatch(int target, const QtVersion *qtVersi
return QString("android-%1").arg(AndroidManager::defaultMinimumSDK(qtVersion));
}
-FilePath AndroidConfig::sdkLocation() const
+FilePath sdkLocation()
{
- return m_sdkLocation;
+ return config().m_sdkLocation;
}
-void AndroidConfig::setSdkLocation(const FilePath &sdkLocation)
+void setSdkLocation(const FilePath &sdkLocation)
{
- m_sdkLocation = sdkLocation;
+ config().m_sdkLocation = sdkLocation;
}
-QVersionNumber AndroidConfig::sdkToolsVersion() const
+static FilePath sdkToolsVersionPath()
{
- if (!m_sdkLocation.exists())
+ const FilePath sdkVersionPaths = config()
+ .m_sdkLocation.pathAppended(Constants::cmdlineToolsName)
+ .pathAppended("/latest/source.properties");
+ if (sdkVersionPaths.exists())
+ return sdkVersionPaths;
+
+ // If it's a first time install use the path of Constants::cmdlineToolsName temporary download
+ const FilePath tmpSdkPath = config().m_temporarySdkToolsPath.pathAppended("source.properties");
+ if (tmpSdkPath.exists())
+ return tmpSdkPath;
+
+ return {};
+}
+
+QVersionNumber sdkToolsVersion()
+{
+ if (!config().m_sdkLocation.exists())
return {};
const FilePath sdkToolsPropertiesPath = sdkToolsVersionPath();
@@ -843,40 +876,34 @@ QVersionNumber AndroidConfig::sdkToolsVersion() const
return QVersionNumber::fromString(settings.value(sdkToolsVersionKey).toString());
}
-QVersionNumber AndroidConfig::buildToolsVersion() const
+QVersionNumber buildToolsVersion()
{
//TODO: return version according to qt version
QVersionNumber maxVersion;
- QDir buildToolsDir(m_sdkLocation.pathAppended("build-tools").toString());
+ QDir buildToolsDir(config().m_sdkLocation.pathAppended("build-tools").toString());
const auto files = buildToolsDir.entryInfoList(QDir::Dirs|QDir::NoDotAndDotDot);
for (const QFileInfo &file: files)
maxVersion = qMax(maxVersion, QVersionNumber::fromString(file.fileName()));
return maxVersion;
}
-QStringList AndroidConfig::sdkManagerToolArgs() const
-{
- return m_sdkManagerToolArgs;
-}
+QStringList sdkManagerToolArgs() { return config().m_sdkManagerToolArgs; }
-void AndroidConfig::setSdkManagerToolArgs(const QStringList &args)
-{
- m_sdkManagerToolArgs = args;
-}
+void setSdkManagerToolArgs(const QStringList &args) { config().m_sdkManagerToolArgs = args; }
-FilePath AndroidConfig::ndkLocation(const QtVersion *qtVersion) const
+FilePath ndkLocation(const QtVersion *qtVersion)
{
- if (!m_defaultNdk.isEmpty())
- return m_defaultNdk; // A selected default NDK is good for any Qt version
+ if (!config().m_defaultNdk.isEmpty())
+ return config().m_defaultNdk; // A selected default NDK is good for any Qt version
return sdkLocation().resolvePath(ndkSubPathFromQtVersion(*qtVersion));
}
-QVersionNumber AndroidConfig::ndkVersion(const QtVersion *qtVersion) const
+QVersionNumber ndkVersion(const QtVersion *qtVersion)
{
return ndkVersion(ndkLocation(qtVersion));
}
-QVersionNumber AndroidConfig::ndkVersion(const FilePath &ndkPath)
+QVersionNumber ndkVersion(const FilePath &ndkPath)
{
QVersionNumber version;
if (!ndkPath.exists()) {
@@ -921,48 +948,13 @@ QVersionNumber AndroidConfig::ndkVersion(const FilePath &ndkPath)
return version;
}
-QStringList AndroidConfig::allEssentials() const
-{
- QtVersions installedVersions = QtVersionManager::versions(
- [](const QtVersion *v) {
- return v->targetDeviceTypes().contains(Android::Constants::ANDROID_DEVICE_TYPE);
- });
-
- QStringList allPackages(defaultEssentials());
- for (const QtVersion *version : installedVersions)
- allPackages.append(essentialsFromQtVersion(*version));
- allPackages.removeDuplicates();
-
- return allPackages;
-}
-
-static QStringList packagesWithoutNdks(const QStringList &packages)
-{
- return Utils::filtered(packages, [] (const QString &p) {
- return !p.startsWith(ndkPackageMarker()); });
-}
+QUrl sdkToolsUrl() { return config().m_sdkToolsUrl; }
-bool AndroidConfig::allEssentialsInstalled(AndroidSdkManager *sdkManager)
-{
- QStringList essentialPkgs(allEssentials());
- const auto installedPkgs = sdkManager->installedSdkPackages();
- for (const AndroidSdkPackage *pkg : installedPkgs) {
- if (essentialPkgs.contains(pkg->sdkStylePath()))
- essentialPkgs.removeOne(pkg->sdkStylePath());
- if (essentialPkgs.isEmpty())
- break;
- }
- if (!m_defaultNdk.isEmpty())
- essentialPkgs = packagesWithoutNdks(essentialPkgs);
- return essentialPkgs.isEmpty() ? true : false;
-}
+QByteArray getSdkToolsSha256() { return config().m_sdkToolsSha256; }
-bool AndroidConfig::sdkToolsOk() const
+static QStringList defaultEssentials()
{
- bool exists = sdkLocation().exists();
- bool writable = sdkLocation().isWritableDir();
- bool sdkToolsExist = !sdkToolsVersion().isNull();
- return exists && writable && sdkToolsExist;
+ return config().m_defaultSdkDepends.essentialPackages + config().m_commonEssentialPkgs;
}
static QStringList packagesExcludingBuiltWithDefaults(const QStringList &packages)
@@ -1004,7 +996,7 @@ static QString essentialBuiltWithBuildToolsPackage(int builtWithApiVersion)
return installedBuildTool;
}
-QStringList AndroidConfig::essentialsFromQtVersion(const QtVersion &version) const
+static QStringList essentialsFromQtVersion(const QtVersion &version)
{
if (auto androidQtVersion = dynamic_cast<const AndroidQtVersion *>(&version)) {
bool ok;
@@ -1017,72 +1009,76 @@ QStringList AndroidConfig::essentialsFromQtVersion(const QtVersion &version) con
builtWithPackages.append(essentialBuiltWithBuildToolsPackage(bw.apiVersion));
return builtWithPackages + packagesExcludingBuiltWithDefaults(
- m_defaultSdkDepends.essentialPackages);
+ config().m_defaultSdkDepends.essentialPackages);
}
}
- QVersionNumber qtVersion = version.qtVersion();
- for (const SdkForQtVersions &item : m_specificQtVersions)
+ const QVersionNumber qtVersion = version.qtVersion();
+ for (const SdkForQtVersions &item : config().m_specificQtVersions) {
if (item.containsVersion(qtVersion))
return item.essentialPackages;
-
- return m_defaultSdkDepends.essentialPackages;
-}
-
-static FilePath ndkSubPath(const SdkForQtVersions &packages)
-{
- const QString ndkPrefix = ndkPackageMarker();
- for (const QString &package : packages.essentialPackages)
- if (package.startsWith(ndkPrefix))
- return FilePath::fromString(NdksSubDir) / package.sliced(ndkPrefix.length());
-
- return {};
+ }
+ return config().m_defaultSdkDepends.essentialPackages;
}
-FilePath AndroidConfig::ndkSubPathFromQtVersion(const QtVersion &version) const
+QStringList allEssentials()
{
- if (auto androidQtVersion = dynamic_cast<const AndroidQtVersion *>(&version)) {
- bool ok;
- const AndroidQtVersion::BuiltWith bw = androidQtVersion->builtWith(&ok);
- if (ok)
- return FilePath::fromString(NdksSubDir) / bw.ndkVersion.toString();
- }
+ QtVersions installedVersions = QtVersionManager::versions(
+ [](const QtVersion *v) {
+ return v->targetDeviceTypes().contains(Android::Constants::ANDROID_DEVICE_TYPE);
+ });
- for (const SdkForQtVersions &item : m_specificQtVersions)
- if (item.containsVersion(version.qtVersion()))
- return ndkSubPath(item);
+ QStringList allPackages(defaultEssentials());
+ for (const QtVersion *version : installedVersions)
+ allPackages.append(essentialsFromQtVersion(*version));
+ allPackages.removeDuplicates();
- return ndkSubPath(m_defaultSdkDepends);
+ return allPackages;
}
-QStringList AndroidConfig::defaultEssentials() const
+static QStringList packagesWithoutNdks(const QStringList &packages)
{
- return m_defaultSdkDepends.essentialPackages + m_commonEssentialPkgs;
+ return Utils::filtered(packages, [] (const QString &p) {
+ return !p.startsWith(ndkPackageMarker());
+ });
}
-bool SdkForQtVersions::containsVersion(const QVersionNumber &qtVersion) const
+bool allEssentialsInstalled(AndroidSdkManager *sdkManager)
{
- return versions.contains(qtVersion)
- || versions.contains(QVersionNumber(qtVersion.majorVersion(),
- qtVersion.minorVersion()));
+ QStringList essentialPkgs(allEssentials());
+ const auto installedPkgs = sdkManager->installedSdkPackages();
+ for (const AndroidSdkPackage *pkg : installedPkgs) {
+ if (essentialPkgs.contains(pkg->sdkStylePath()))
+ essentialPkgs.removeOne(pkg->sdkStylePath());
+ if (essentialPkgs.isEmpty())
+ break;
+ }
+ if (!config().m_defaultNdk.isEmpty())
+ essentialPkgs = packagesWithoutNdks(essentialPkgs);
+ return essentialPkgs.isEmpty() ? true : false;
}
-FilePath AndroidConfig::openJDKLocation() const
+bool sdkToolsOk()
{
- return m_openJDKLocation;
+ const bool exists = sdkLocation().exists();
+ const bool writable = sdkLocation().isWritableDir();
+ const bool sdkToolsExist = !sdkToolsVersion().isNull();
+ return exists && writable && sdkToolsExist;
}
-void AndroidConfig::setOpenJDKLocation(const FilePath &openJDKLocation)
+FilePath openJDKLocation() { return config().m_openJDKLocation; }
+
+void setOpenJDKLocation(const FilePath &openJDKLocation)
{
- m_openJDKLocation = openJDKLocation;
+ config().m_openJDKLocation = openJDKLocation;
}
-QString AndroidConfig::toolchainHost(const QtVersion *qtVersion) const
+QString toolchainHost(const QtVersion *qtVersion)
{
return toolchainHostFromNdk(ndkLocation(qtVersion));
}
-QString AndroidConfig::toolchainHostFromNdk(const FilePath &ndkPath)
+QString toolchainHostFromNdk(const FilePath &ndkPath)
{
// detect toolchain host
QString toolchainHost;
@@ -1112,27 +1108,15 @@ QString AndroidConfig::toolchainHostFromNdk(const FilePath &ndkPath)
return toolchainHost;
}
-QString AndroidConfig::emulatorArgs() const
-{
- return m_emulatorArgs;
-}
+QString emulatorArgs() { return config().m_emulatorArgs; }
-void AndroidConfig::setEmulatorArgs(const QString &args)
-{
- m_emulatorArgs = args;
-}
+void setEmulatorArgs(const QString &args) { config().m_emulatorArgs = args; }
-bool AndroidConfig::automaticKitCreation() const
-{
- return m_automaticKitCreation;
-}
+bool automaticKitCreation() { return config().m_automaticKitCreation; }
-void AndroidConfig::setAutomaticKitCreation(bool b)
-{
- m_automaticKitCreation = b;
-}
+void setAutomaticKitCreation(bool b) { config().m_automaticKitCreation = b; }
-FilePath AndroidConfig::defaultSdkPath()
+FilePath defaultSdkPath()
{
QString sdkFromEnvVar = QString::fromLocal8Bit(getenv("ANDROID_SDK_ROOT"));
if (!sdkFromEnvVar.isEmpty())
@@ -1153,6 +1137,101 @@ FilePath AndroidConfig::defaultSdkPath()
QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/Android/Sdk");
}
+Environment toolsEnvironment()
+{
+ Environment env = Environment::systemEnvironment();
+ FilePath jdkLocation = openJDKLocation();
+ if (!jdkLocation.isEmpty()) {
+ env.set(Constants::JAVA_HOME_ENV_VAR, jdkLocation.toUserOutput());
+ env.prependOrSetPath(jdkLocation.pathAppended("bin"));
+ }
+ return env;
+}
+
+static FilePath androidStudioPath()
+{
+#if defined(Q_OS_WIN)
+ const QLatin1String registryKey("HKEY_LOCAL_MACHINE\\SOFTWARE\\Android Studio");
+ const QLatin1String valueName("Path");
+ const QSettings settings64(registryKey, QSettings::Registry64Format);
+ const QSettings settings32(registryKey, QSettings::Registry32Format);
+ return FilePath::fromUserInput(
+ settings64.value(valueName, settings32.value(valueName).toString()).toString());
+#endif
+ return {}; // TODO non-Windows
+}
+
+FilePath getJdkPath()
+{
+ FilePath jdkHome = FilePath::fromString(qtcEnvironmentVariable(Constants::JAVA_HOME_ENV_VAR));
+ if (jdkHome.exists())
+ return jdkHome;
+
+ if (HostOsInfo::isWindowsHost()) {
+ // Look for Android Studio's jdk first
+ const FilePath androidStudioSdkPath = androidStudioPath();
+ if (!androidStudioSdkPath.isEmpty()) {
+ const QStringList allVersions{"jbr", "jre"};
+ for (const QString &version : allVersions) {
+ const FilePath androidStudioSdkJbrPath = androidStudioSdkPath / version;
+ if (androidStudioSdkJbrPath.exists())
+ return androidStudioSdkJbrPath;
+ }
+ }
+
+ if (jdkHome.isEmpty()) {
+ QStringList allVersions;
+ QSettings settings("HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\JDK\\",
+ QSettings::NativeFormat);
+ allVersions = settings.childGroups();
+#ifdef Q_OS_WIN
+ if (allVersions.isEmpty()) {
+ settings.setDefaultFormat(QSettings::Registry64Format);
+ allVersions = settings.childGroups();
+ }
+#endif // Q_OS_WIN
+
+ // Look for the highest existing JDK
+ allVersions.sort();
+ std::reverse(allVersions.begin(), allVersions.end()); // Order descending
+ for (const QString &version : std::as_const(allVersions)) {
+ settings.beginGroup(version);
+ jdkHome = FilePath::fromUserInput(settings.value("JavaHome").toString());
+ settings.endGroup();
+ if (jdkHome.exists())
+ break;
+ }
+ }
+ } else {
+ QStringList args;
+ if (HostOsInfo::isMacHost())
+ args << "-c"
+ << "/usr/libexec/java_home";
+ else
+ args << "-c"
+ << "readlink -f $(which java)";
+
+ Process findJdkPathProc;
+ findJdkPathProc.setCommand({"sh", args});
+ findJdkPathProc.start();
+ findJdkPathProc.waitForFinished();
+ QByteArray jdkPath = findJdkPathProc.rawStdOut().trimmed();
+
+ if (HostOsInfo::isMacHost()) {
+ jdkHome = FilePath::fromUtf8(jdkPath);
+ } else {
+ jdkPath.replace("bin/java", ""); // For OpenJDK 11
+ jdkPath.replace("jre", "");
+ jdkPath.replace("//", "/");
+ jdkHome = FilePath::fromUtf8(jdkPath);
+ }
+ }
+
+ return jdkHome;
+}
+
+} // namespace AndroidConfig
+
///////////////////////////////////
// AndroidConfigurations
///////////////////////////////////
@@ -1169,11 +1248,9 @@ AndroidConfigurations::AndroidConfigurations()
m_instance = this;
}
-void AndroidConfigurations::setConfig(const AndroidConfig &devConfigs)
+void AndroidConfigurations::applyConfig()
{
emit m_instance->aboutToUpdate();
- androidConfig() = devConfigs;
-
m_instance->save();
updateAndroidDevice();
registerNewToolchains();
@@ -1227,12 +1304,12 @@ void AndroidConfigurations::removeUnusedDebuggers()
QList<FilePath> uniqueNdks;
for (const QtVersion *qt : qtVersions) {
- FilePath ndkLocation = androidConfig().ndkLocation(qt);
+ FilePath ndkLocation = AndroidConfig::ndkLocation(qt);
if (!uniqueNdks.contains(ndkLocation))
uniqueNdks.append(ndkLocation);
}
- uniqueNdks.append(FileUtils::toFilePathList(androidConfig().getCustomNdkList()));
+ uniqueNdks.append(FileUtils::toFilePathList(AndroidConfig::getCustomNdkList()));
const QList<Debugger::DebuggerItem> allDebuggers = Debugger::DebuggerItemManager::debuggers();
for (const Debugger::DebuggerItem &debugger : allDebuggers) {
@@ -1297,14 +1374,14 @@ static QVariant findOrRegisterDebugger(Toolchain *tc,
bool customDebugger = false)
{
const FilePath ndk = static_cast<AndroidToolchain *>(tc)->ndkLocation();
- const FilePath lldbCommand = androidConfig().lldbPathFromNdk(ndk);
+ const FilePath lldbCommand = lldbPathFromNdk(ndk);
const Debugger::DebuggerItem *existingLldb = existingDebugger(lldbCommand,
Debugger::LldbEngineType);
// Return existing debugger with same command - prefer lldb (limit to sdk/ndk min version?)
if (existingLldb)
return existingLldb->id();
- const FilePath gdbCommand = androidConfig().gdbPathFromNdk(tc->targetAbi(), ndk);
+ const FilePath gdbCommand = gdbPathFromNdk(tc->targetAbi(), ndk);
// check if the debugger is already registered, but ignoring the display name
const Debugger::DebuggerItem *existingGdb = existingDebugger(gdbCommand,
@@ -1324,7 +1401,7 @@ static QVariant findOrRegisterDebugger(Toolchain *tc,
debugger.setEngineType(Debugger::LldbEngineType);
debugger.setUnexpandedDisplayName(custom + mainName
.arg(getMultiOrSingleAbiString(allSupportedAbis()))
- .arg(androidConfig().ndkVersion(ndk).toString())
+ .arg(AndroidConfig::ndkVersion(ndk).toString())
+ ' ' + debugger.engineTypeName());
debugger.setAutoDetected(true);
debugger.reinitializeFromFile();
@@ -1343,10 +1420,10 @@ static QVariant findOrRegisterDebugger(Toolchain *tc,
debugger.setEngineType(Debugger::GdbEngineType);
// NDK 10 and older have multiple gdb versions per ABI, so check for that.
- const bool oldNdkVersion = androidConfig().ndkVersion(ndk) <= QVersionNumber{11};
+ const bool oldNdkVersion = AndroidConfig::ndkVersion(ndk) <= QVersionNumber{11};
debugger.setUnexpandedDisplayName(custom + mainName
.arg(getMultiOrSingleAbiString(oldNdkVersion ? abisList : allSupportedAbis()))
- .arg(androidConfig().ndkVersion(ndk).toString())
+ .arg(AndroidConfig::ndkVersion(ndk).toString())
+ ' ' + debugger.engineTypeName());
debugger.setAutoDetected(true);
debugger.reinitializeFromFile();
@@ -1357,9 +1434,9 @@ static QVariant findOrRegisterDebugger(Toolchain *tc,
void AndroidConfigurations::registerCustomToolchainsAndDebuggers()
{
const Toolchains existingAndroidToolchains = ToolchainManager::toolchains(
- Utils::equal(&Toolchain::typeId, Utils::Id(Constants::ANDROID_TOOLCHAIN_TYPEID)));
+ Utils::equal(&Toolchain::typeId, Id(Constants::ANDROID_TOOLCHAIN_TYPEID)));
- const FilePaths customNdks = FileUtils::toFilePathList(androidConfig().getCustomNdkList());
+ const FilePaths customNdks = FileUtils::toFilePathList(AndroidConfig::getCustomNdkList());
const Toolchains customToolchains
= autodetectToolchainsFromNdks(existingAndroidToolchains, customNdks, true);
@@ -1378,8 +1455,8 @@ void AndroidConfigurations::updateAutomaticKitList()
if (DeviceTypeKitAspect::deviceTypeId(k) == Constants::ANDROID_DEVICE_TYPE) {
if (k->value(Constants::ANDROID_KIT_NDK).isNull() || k->value(Constants::ANDROID_KIT_SDK).isNull()) {
if (QtVersion *qt = QtKitAspect::qtVersion(k)) {
- k->setValueSilently(Constants::ANDROID_KIT_NDK, androidConfig().ndkLocation(qt).toString());
- k->setValue(Constants::ANDROID_KIT_SDK, androidConfig().sdkLocation().toString());
+ k->setValueSilently(Constants::ANDROID_KIT_NDK, AndroidConfig::ndkLocation(qt).toString());
+ k->setValue(Constants::ANDROID_KIT_SDK, AndroidConfig::sdkLocation().toString());
}
}
}
@@ -1419,7 +1496,7 @@ void AndroidConfigurations::updateAutomaticKitList()
for (const QtVersion *qt : qtVersionsForArch.value(tc->targetAbi())) {
FilePath tcNdk = static_cast<const AndroidToolchain *>(tc)->ndkLocation();
- if (tcNdk != androidConfig().ndkLocation(qt))
+ if (tcNdk != AndroidConfig::ndkLocation(qt))
continue;
const Toolchains allLanguages
@@ -1461,8 +1538,8 @@ void AndroidConfigurations::updateAutomaticKitList()
k->setUnexpandedDisplayName(Tr::tr("Android %1 Clang %2")
.arg(versionStr)
.arg(getMultiOrSingleAbiString(abis)));
- k->setValueSilently(Constants::ANDROID_KIT_NDK, androidConfig().ndkLocation(qt).toString());
- k->setValueSilently(Constants::ANDROID_KIT_SDK, androidConfig().sdkLocation().toString());
+ k->setValueSilently(Constants::ANDROID_KIT_NDK, AndroidConfig::ndkLocation(qt).toString());
+ k->setValueSilently(Constants::ANDROID_KIT_SDK, AndroidConfig::sdkLocation().toString());
};
if (existingKit) {
@@ -1478,23 +1555,6 @@ void AndroidConfigurations::updateAutomaticKitList()
KitManager::deregisterKits(unhandledKits);
}
-Environment AndroidConfig::toolsEnvironment() const
-{
- Environment env = Environment::systemEnvironment();
- FilePath jdkLocation = openJDKLocation();
- if (!jdkLocation.isEmpty()) {
- env.set(Constants::JAVA_HOME_ENV_VAR, jdkLocation.toUserOutput());
- env.prependOrSetPath(jdkLocation.pathAppended("bin"));
- }
- return env;
-}
-
-AndroidConfig &androidConfig()
-{
- static AndroidConfig theCurrentConfig;
- return theCurrentConfig;
-}
-
AndroidSdkManager *AndroidConfigurations::sdkManager()
{
return m_instance->m_sdkManager.get();
@@ -1509,97 +1569,15 @@ void AndroidConfigurations::save()
{
QtcSettings *settings = Core::ICore::settings();
settings->beginGroup(SettingsGroup);
- androidConfig().save(*settings);
+ AndroidConfig::config().save(*settings);
settings->endGroup();
}
-static FilePath androidStudioPath()
-{
-#if defined(Q_OS_WIN)
- const QLatin1String registryKey("HKEY_LOCAL_MACHINE\\SOFTWARE\\Android Studio");
- const QLatin1String valueName("Path");
- const QSettings settings64(registryKey, QSettings::Registry64Format);
- const QSettings settings32(registryKey, QSettings::Registry32Format);
- return FilePath::fromUserInput(
- settings64.value(valueName, settings32.value(valueName).toString()).toString());
-#endif
- return {}; // TODO non-Windows
-}
-
-FilePath AndroidConfig::getJdkPath()
-{
- FilePath jdkHome = FilePath::fromString(qtcEnvironmentVariable(Constants::JAVA_HOME_ENV_VAR));
- if (jdkHome.exists())
- return jdkHome;
-
- if (HostOsInfo::isWindowsHost()) {
- // Look for Android Studio's jdk first
- const FilePath androidStudioSdkPath = androidStudioPath();
- if (!androidStudioSdkPath.isEmpty()) {
- const QStringList allVersions{"jbr", "jre"};
- for (const QString &version : allVersions) {
- const FilePath androidStudioSdkJbrPath = androidStudioSdkPath / version;
- if (androidStudioSdkJbrPath.exists())
- return androidStudioSdkJbrPath;
- }
- }
-
- if (jdkHome.isEmpty()) {
- QStringList allVersions;
- QSettings settings("HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\JDK\\",
- QSettings::NativeFormat);
- allVersions = settings.childGroups();
-#ifdef Q_OS_WIN
- if (allVersions.isEmpty()) {
- settings.setDefaultFormat(QSettings::Registry64Format);
- allVersions = settings.childGroups();
- }
-#endif // Q_OS_WIN
-
- // Look for the highest existing JDK
- allVersions.sort();
- std::reverse(allVersions.begin(), allVersions.end()); // Order descending
- for (const QString &version : std::as_const(allVersions)) {
- settings.beginGroup(version);
- jdkHome = FilePath::fromUserInput(settings.value("JavaHome").toString());
- settings.endGroup();
- if (jdkHome.exists())
- break;
- }
- }
- } else {
- QStringList args;
- if (HostOsInfo::isMacHost())
- args << "-c"
- << "/usr/libexec/java_home";
- else
- args << "-c"
- << "readlink -f $(which java)";
-
- Process findJdkPathProc;
- findJdkPathProc.setCommand({"sh", args});
- findJdkPathProc.start();
- findJdkPathProc.waitForFinished();
- QByteArray jdkPath = findJdkPathProc.rawStdOut().trimmed();
-
- if (HostOsInfo::isMacHost()) {
- jdkHome = FilePath::fromUtf8(jdkPath);
- } else {
- jdkPath.replace("bin/java", ""); // For OpenJDK 11
- jdkPath.replace("jre", "");
- jdkPath.replace("//", "/");
- jdkHome = FilePath::fromUtf8(jdkPath);
- }
- }
-
- return jdkHome;
-}
-
void AndroidConfigurations::load()
{
QtcSettings *settings = Core::ICore::settings();
settings->beginGroup(SettingsGroup);
- androidConfig().load(*settings);
+ AndroidConfig::config().load(*settings);
settings->endGroup();
}
@@ -1677,7 +1655,7 @@ void AndroidConfigurationsTest::testAndroidConfigAvailableNdkPlatforms()
QFETCH(OsType, hostOs);
QFETCH(QList<int>, expectedPlatforms);
- const QList<int> foundPlatforms = availableNdkPlatformsImpl(ndkPath, abis, hostOs);
+ const QList<int> foundPlatforms = AndroidConfig::availableNdkPlatformsImpl(ndkPath, abis, hostOs);
QCOMPARE(foundPlatforms, expectedPlatforms);
}
diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h
index f45801c361..ca7825cf03 100644
--- a/src/plugins/android/androidconfigurations.h
+++ b/src/plugins/android/androidconfigurations.h
@@ -36,143 +36,87 @@ public:
int sdcardSize = 0;
};
-struct SdkForQtVersions
-{
- QList<QVersionNumber> versions;
- QStringList essentialPackages;
-
-public:
- bool containsVersion(const QVersionNumber &qtVersion) const;
-};
+namespace AndroidConfig {
-class AndroidConfig
-{
-public:
- void load(const Utils::QtcSettings &settings);
- void save(Utils::QtcSettings &settings) const;
+QStringList apiLevelNamesFor(const SdkPlatformList &platforms);
+QString apiLevelNameFor(const SdkPlatform *platform);
- static QStringList apiLevelNamesFor(const SdkPlatformList &platforms);
- static QString apiLevelNameFor(const SdkPlatform *platform);
+Utils::FilePath sdkLocation();
+void setSdkLocation(const Utils::FilePath &sdkLocation);
+QVersionNumber sdkToolsVersion();
+QVersionNumber buildToolsVersion();
+QStringList sdkManagerToolArgs();
+void setSdkManagerToolArgs(const QStringList &args);
- Utils::FilePath sdkLocation() const;
- void setSdkLocation(const Utils::FilePath &sdkLocation);
- QVersionNumber sdkToolsVersion() const;
- Utils::FilePath sdkToolsVersionPath() const;
- QVersionNumber buildToolsVersion() const;
- QStringList sdkManagerToolArgs() const;
- void setSdkManagerToolArgs(const QStringList &args);
+Utils::FilePath ndkLocation(const QtSupport::QtVersion *qtVersion);
+QVersionNumber ndkVersion(const QtSupport::QtVersion *qtVersion);
+QVersionNumber ndkVersion(const Utils::FilePath &ndkPath);
- Utils::FilePath ndkLocation(const QtSupport::QtVersion *qtVersion) const;
- QVersionNumber ndkVersion(const QtSupport::QtVersion *qtVersion) const;
- static QVersionNumber ndkVersion(const Utils::FilePath &ndkPath);
+QUrl sdkToolsUrl();
+QByteArray getSdkToolsSha256();
- QUrl sdkToolsUrl() const { return m_sdkToolsUrl; }
- QByteArray getSdkToolsSha256() const { return m_sdkToolsSha256; }
- Utils::FilePath ndkSubPathFromQtVersion(const QtSupport::QtVersion &version) const; // relative!
+QStringList allEssentials();
+bool allEssentialsInstalled(Internal::AndroidSdkManager *sdkManager);
+bool sdkToolsOk();
- QStringList defaultEssentials() const;
- QStringList essentialsFromQtVersion(const QtSupport::QtVersion &version) const;
- QStringList allEssentials() const;
- bool allEssentialsInstalled(Internal::AndroidSdkManager *sdkManager);
- bool sdkToolsOk() const;
+Utils::FilePath openJDKLocation();
+void setOpenJDKLocation(const Utils::FilePath &openJDKLocation);
- Utils::FilePath openJDKLocation() const;
- void setOpenJDKLocation(const Utils::FilePath &openJDKLocation);
+QString toolchainHost(const QtSupport::QtVersion *qtVersion);
+QString toolchainHostFromNdk(const Utils::FilePath &ndkPath);
- Utils::FilePath keystoreLocation() const;
+QString emulatorArgs();
+void setEmulatorArgs(const QString &args);
- QString toolchainHost(const QtSupport::QtVersion *qtVersion) const;
- static QString toolchainHostFromNdk(const Utils::FilePath &ndkPath);
+bool automaticKitCreation();
+void setAutomaticKitCreation(bool b);
- QString emulatorArgs() const;
- void setEmulatorArgs(const QString &args);
+Utils::FilePath defaultSdkPath();
+Utils::FilePath adbToolPath();
+Utils::FilePath emulatorToolPath();
+Utils::FilePath sdkManagerToolPath();
+Utils::FilePath avdManagerToolPath();
- bool automaticKitCreation() const;
- void setAutomaticKitCreation(bool b);
+void setTemporarySdkToolsPath(const Utils::FilePath &path);
- static Utils::FilePath defaultSdkPath();
- Utils::FilePath adbToolPath() const;
- Utils::FilePath emulatorToolPath() const;
- Utils::FilePath sdkManagerToolPath() const;
- Utils::FilePath avdManagerToolPath() const;
+Utils::FilePath toolchainPath(const QtSupport::QtVersion *qtVersion);
+Utils::FilePath toolchainPathFromNdk(
+ const Utils::FilePath &ndkLocation, Utils::OsType hostOs = Utils::HostOsInfo::hostOs());
+Utils::FilePath clangPathFromNdk(const Utils::FilePath &ndkLocation);
- void setTemporarySdkToolsPath(const Utils::FilePath &path);
+Utils::FilePath makePathFromNdk(const Utils::FilePath &ndkLocation);
- Utils::FilePath toolchainPath(const QtSupport::QtVersion *qtVersion) const;
- static Utils::FilePath toolchainPathFromNdk(const Utils::FilePath &ndkLocation,
- Utils::OsType hostOs = Utils::HostOsInfo::hostOs());
- static Utils::FilePath clangPathFromNdk(const Utils::FilePath &ndkLocation);
+Utils::FilePath keytoolPath();
- Utils::FilePath gdbPath(const ProjectExplorer::Abi &abi, const QtSupport::QtVersion *qtVersion) const;
- static Utils::FilePath gdbPathFromNdk(const ProjectExplorer::Abi &abi,
- const Utils::FilePath &ndkLocation);
- static Utils::FilePath lldbPathFromNdk(const Utils::FilePath &ndkLocation);
- static Utils::FilePath makePathFromNdk(const Utils::FilePath &ndkLocation);
+QList<AndroidDeviceInfo> connectedDevices(QString *error = nullptr);
- Utils::FilePath keytoolPath() const;
+QString bestNdkPlatformMatch(int target, const QtSupport::QtVersion *qtVersion);
- QList<AndroidDeviceInfo> connectedDevices(QString *error = nullptr) const;
+QLatin1String displayName(const ProjectExplorer::Abi &abi);
- QString bestNdkPlatformMatch(int target, const QtSupport::QtVersion *qtVersion) const;
+QString getProductModel(const QString &device);
+bool isConnected(const QString &serialNumber);
- static QLatin1String toolchainPrefix(const ProjectExplorer::Abi &abi);
- static QLatin1String toolsPrefix(const ProjectExplorer::Abi &abi);
- static QLatin1String displayName(const ProjectExplorer::Abi &abi);
+bool sdkFullyConfigured();
+void setSdkFullyConfigured(bool allEssentialsInstalled);
- QString getProductModel(const QString &device) const;
- bool isConnected(const QString &serialNumber) const;
+bool isValidNdk(const QString &ndkLocation);
+QStringList getCustomNdkList();
+void addCustomNdk(const QString &customNdk);
+void removeCustomNdk(const QString &customNdk);
+void setDefaultNdk(const Utils::FilePath &defaultNdk);
+Utils::FilePath defaultNdk();
- bool sdkFullyConfigured() const { return m_sdkFullyConfigured; }
- void setSdkFullyConfigured(bool allEssentialsInstalled) { m_sdkFullyConfigured = allEssentialsInstalled; }
+Utils::FilePath openSslLocation();
+void setOpenSslLocation(const Utils::FilePath &openSslLocation);
- bool isValidNdk(const QString &ndkLocation) const;
- QStringList getCustomNdkList() const;
- void addCustomNdk(const QString &customNdk);
- void removeCustomNdk(const QString &customNdk);
- void setDefaultNdk(const Utils::FilePath &defaultNdk);
- Utils::FilePath defaultNdk() const;
+Utils::FilePath getJdkPath();
+QStringList getAbis(const QString &device);
+int getSDKVersion(const QString &device);
- Utils::FilePath openSslLocation() const;
- void setOpenSslLocation(const Utils::FilePath &openSslLocation);
-
- static Utils::FilePath getJdkPath();
- static QStringList getAbis(const QString &device);
- static int getSDKVersion(const QString &device);
-
- Utils::Environment toolsEnvironment() const;
-
-private:
- static QString getDeviceProperty(const QString &device, const QString &property);
-
- Utils::FilePath openJDKBinPath() const;
- static QString getAvdName(const QString &serialnumber);
-
- void parseDependenciesJson();
-
- QList<int> availableNdkPlatforms(const QtSupport::QtVersion *qtVersion) const;
-
- Utils::FilePath m_sdkLocation;
- Utils::FilePath m_temporarySdkToolsPath;
- QStringList m_sdkManagerToolArgs;
- Utils::FilePath m_openJDKLocation;
- Utils::FilePath m_keystoreLocation;
- Utils::FilePath m_openSslLocation;
- QString m_emulatorArgs;
- bool m_automaticKitCreation = true;
- QUrl m_sdkToolsUrl;
- QByteArray m_sdkToolsSha256;
- QStringList m_commonEssentialPkgs;
- SdkForQtVersions m_defaultSdkDepends;
- QList<SdkForQtVersions> m_specificQtVersions;
- QStringList m_customNdkList;
- Utils::FilePath m_defaultNdk;
- bool m_sdkFullyConfigured = false;
-
- //caches
- mutable QHash<QString, QString> m_serialNumberToDeviceName;
-};
+Utils::Environment toolsEnvironment();
-AndroidConfig &androidConfig();
+} // namespace AndroidConfig
class AndroidConfigurations : public QObject
{
@@ -180,7 +124,7 @@ class AndroidConfigurations : public QObject
public:
static Internal::AndroidSdkManager *sdkManager();
- static void setConfig(const AndroidConfig &config);
+ static void applyConfig();
static AndroidConfigurations *instance();
static void registerNewToolchains();
diff --git a/src/plugins/android/androidcreatekeystorecertificate.cpp b/src/plugins/android/androidcreatekeystorecertificate.cpp
index 77aed2a6ad..004ce94f7b 100644
--- a/src/plugins/android/androidcreatekeystorecertificate.cpp
+++ b/src/plugins/android/androidcreatekeystorecertificate.cpp
@@ -262,8 +262,8 @@ void AndroidCreateKeystoreCertificate::buttonBoxAccepted()
if (!m_stateNameLineEdit->text().isEmpty())
distinguishedNames += QLatin1String(", S=") + m_stateNameLineEdit->text().replace(',', QLatin1String("\\,"));
- const CommandLine command(androidConfig().keytoolPath(),
- { "-genkey", "-keyalg", "RSA",
+ const CommandLine command(AndroidConfig::keytoolPath(),
+ {"-genkey", "-keyalg", "RSA",
"-keystore", m_keystoreFilePath.toString(),
"-storepass", keystorePassword(),
"-alias", certificateAlias(),
@@ -275,7 +275,7 @@ void AndroidCreateKeystoreCertificate::buttonBoxAccepted()
Process genKeyCertProc;
genKeyCertProc.setCommand(command);
using namespace std::chrono_literals;
- genKeyCertProc.runBlocking(15s, EventLoopMode::On);
+ genKeyCertProc.runBlocking(15s);
if (genKeyCertProc.result() != ProcessResult::FinishedWithSuccess) {
QMessageBox::critical(this, Tr::tr("Error"),
diff --git a/src/plugins/android/androiddebugsupport.cpp b/src/plugins/android/androiddebugsupport.cpp
index cea404ef6f..cb3d58a068 100644
--- a/src/plugins/android/androiddebugsupport.cpp
+++ b/src/plugins/android/androiddebugsupport.cpp
@@ -110,8 +110,7 @@ void AndroidDebugSupport::start()
QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(kit);
if (!HostOsInfo::isWindowsHost()
- && (qtVersion
- && androidConfig().ndkVersion(qtVersion) >= QVersionNumber(11, 0, 0))) {
+ && (qtVersion && AndroidConfig::ndkVersion(qtVersion) >= QVersionNumber(11, 0, 0))) {
qCDebug(androidDebugSupportLog) << "UseTargetAsync: " << true;
setUseTargetAsync(true);
}
@@ -165,7 +164,7 @@ void AndroidDebugSupport::start()
int sdkVersion = qMax(AndroidManager::minimumSDK(kit), minimumNdk);
if (qtVersion) {
- const FilePath ndkLocation = androidConfig().ndkLocation(qtVersion);
+ const FilePath ndkLocation = AndroidConfig::ndkLocation(qtVersion);
FilePath sysRoot = ndkLocation
/ "platforms"
/ QString("android-%1").arg(sdkVersion)
diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp
index 459bdb6eea..9c5681d086 100644
--- a/src/plugins/android/androiddeployqtstep.cpp
+++ b/src/plugins/android/androiddeployqtstep.cpp
@@ -66,27 +66,45 @@ const QLatin1String InstallFailedUpdateIncompatible("INSTALL_FAILED_UPDATE_INCOM
const QLatin1String InstallFailedPermissionModelDowngrade("INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE");
const QLatin1String InstallFailedVersionDowngrade("INSTALL_FAILED_VERSION_DOWNGRADE");
+enum DeployErrorFlag
+{
+ NoError = 0,
+ InconsistentCertificates = 0x0001,
+ UpdateIncompatible = 0x0002,
+ PermissionModelDowngrade = 0x0004,
+ VersionDowngrade = 0x0008,
+ Failure = 0x0010
+};
+
+Q_DECLARE_FLAGS(DeployErrorFlags, DeployErrorFlag)
+
+static DeployErrorFlags parseDeployErrors(const QString &deployOutputLine)
+{
+ DeployErrorFlags errorCode = NoError;
+
+ if (deployOutputLine.contains(InstallFailedInconsistentCertificatesString))
+ errorCode |= InconsistentCertificates;
+ if (deployOutputLine.contains(InstallFailedUpdateIncompatible))
+ errorCode |= UpdateIncompatible;
+ if (deployOutputLine.contains(InstallFailedPermissionModelDowngrade))
+ errorCode |= PermissionModelDowngrade;
+ if (deployOutputLine.contains(InstallFailedVersionDowngrade))
+ errorCode |= VersionDowngrade;
+
+ return errorCode;
+}
+
// AndroidDeployQtStep
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);
+ void askForUninstall(DeployErrorFlags errorCode);
private:
void runCommand(const CommandLine &command);
@@ -94,26 +112,17 @@ private:
bool init() override;
Tasking::GroupItem runRecipe() final;
void gatherFilesToPull();
- DeployErrorCode runDeploy(QPromise<void> &promise);
- void slotAskForUninstall(DeployErrorCode errorCode);
+ DeployErrorFlags runDeploy(QPromise<void> &promise);
+ void slotAskForUninstall(DeployErrorFlags errorFlags);
void runImpl(QPromise<void> &promise);
QWidget *createConfigWidget() override;
- void processReadyReadStdOutput(DeployErrorCode &errorCode);
+ void processReadyReadStdOutput(DeployErrorFlags &errorCode);
void stdOutput(const QString &line);
- void processReadyReadStdError(DeployErrorCode &errorCode);
+ void processReadyReadStdError(DeployErrorFlags &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);
@@ -169,7 +178,7 @@ bool AndroidDeployQtStep::init()
return false;
}
- m_androiddeployqtArgs = CommandLine();
+ m_androiddeployqtArgs = {};
m_androidABIs = AndroidManager::applicationAbis(target());
if (m_androidABIs.isEmpty()) {
@@ -288,7 +297,7 @@ bool AndroidDeployQtStep::init()
m_apkPath = FilePath::fromString(node->data(Constants::AndroidApk).toString());
if (!m_apkPath.isEmpty()) {
m_manifestName = FilePath::fromString(node->data(Constants::AndroidManifest).toString());
- m_command = androidConfig().adbToolPath();
+ m_command = AndroidConfig::adbToolPath();
AndroidManager::setManifestPath(target(), m_manifestName);
} else {
QString jsonFile = AndroidQtVersion::androidDeploymentSettings(target()).toString();
@@ -326,22 +335,21 @@ bool AndroidDeployQtStep::init()
}
} else {
m_uninstallPreviousPackageRun = true;
- m_command = androidConfig().adbToolPath();
+ m_command = AndroidConfig::adbToolPath();
m_apkPath = AndroidManager::packagePath(target());
m_workingDirectory = bc ? AndroidManager::buildDirectory(target()): FilePath();
}
m_environment = bc ? bc->environment() : Environment();
- m_adbPath = androidConfig().adbToolPath();
+ m_adbPath = AndroidConfig::adbToolPath();
- AndroidAvdManager avdManager;
// Start the AVD if not running.
- if (!m_avdName.isEmpty() && avdManager.findAvd(m_avdName).isEmpty())
- avdManager.startAvdAsync(m_avdName);
+ if (!m_avdName.isEmpty() && AndroidAvdManager::findAvd(m_avdName).isEmpty())
+ AndroidAvdManager::startAvdAsync(m_avdName);
return true;
}
-AndroidDeployQtStep::DeployErrorCode AndroidDeployQtStep::runDeploy(QPromise<void> &promise)
+DeployErrorFlags AndroidDeployQtStep::runDeploy(QPromise<void> &promise)
{
CommandLine cmd(m_command);
if (m_useAndroiddeployqt && m_apkPath.isEmpty()) {
@@ -356,23 +364,24 @@ AndroidDeployQtStep::DeployErrorCode AndroidDeployQtStep::runDeploy(QPromise<voi
} else {
RunConfiguration *rc = target()->activeRunConfiguration();
- QTC_ASSERT(rc, return DeployErrorCode::Failure);
+ QTC_ASSERT(rc, return Failure);
QString packageName;
if (m_uninstallPreviousPackageRun) {
- packageName = AndroidManager::packageName(m_manifestName);
+ packageName = AndroidManager::packageName(target());
if (packageName.isEmpty()) {
- reportWarningOrError(Tr::tr("Cannot find the package name from the Android Manifest "
- "file \"%1\".").arg(m_manifestName.toUserOutput()),
- Task::Error);
+ reportWarningOrError(
+ Tr::tr("Cannot find the package name from AndroidManifest.xml nor "
+ "build.gradle files at \"%1\".")
+ .arg(AndroidManager::androidBuildDirectory(target()).toUserOutput()),
+ Task::Error);
return Failure;
}
const QString msg = Tr::tr("Uninstalling the previous package \"%1\".").arg(packageName);
qCDebug(deployStepLog) << msg;
emit addOutput(msg, OutputFormat::NormalMessage);
runCommand({m_adbPath,
- AndroidDeviceInfo::adbSelector(m_serialNumber)
- << "uninstall" << packageName});
+ {AndroidDeviceInfo::adbSelector(m_serialNumber), "uninstall", packageName}});
}
cmd.addArgs(AndroidDeviceInfo::adbSelector(m_serialNumber));
@@ -385,7 +394,7 @@ AndroidDeployQtStep::DeployErrorCode AndroidDeployQtStep::runDeploy(QPromise<voi
process.setEnvironment(m_environment);
process.setUseCtrlCStub(true);
- DeployErrorCode deployError = NoError;
+ DeployErrorFlags deployError = NoError;
process.setStdOutLineCallback([this, &deployError](const QString &line) {
deployError |= parseDeployErrors(line);
@@ -442,46 +451,32 @@ AndroidDeployQtStep::DeployErrorCode AndroidDeployQtStep::runDeploy(QPromise<voi
return deployError;
}
-void AndroidDeployQtStep::slotAskForUninstall(DeployErrorCode errorCode)
+void AndroidDeployQtStep::slotAskForUninstall(DeployErrorFlags errorFlags)
{
- Q_ASSERT(errorCode > 0);
-
- QString uninstallMsg = Tr::tr("Deployment failed with the following errors:\n\n");
- uint errorCodeFlags = errorCode;
- uint mask = 1;
- while (errorCodeFlags) {
- switch (errorCodeFlags & mask) {
- case DeployErrorCode::PermissionModelDowngrade:
- uninstallMsg += InstallFailedPermissionModelDowngrade+"\n";
- break;
- case InconsistentCertificates:
- uninstallMsg += InstallFailedInconsistentCertificatesString+"\n";
- break;
- case UpdateIncompatible:
- uninstallMsg += InstallFailedUpdateIncompatible+"\n";
- break;
- case VersionDowngrade:
- uninstallMsg += InstallFailedVersionDowngrade+"\n";
- break;
- default:
- break;
- }
- errorCodeFlags &= ~mask;
- mask <<= 1;
- }
-
- uninstallMsg.append(Tr::tr("\nUninstalling the installed package may solve the issue.\n"
- "Do you want to uninstall the existing package?"));
- int button = QMessageBox::critical(nullptr, Tr::tr("Install failed"), uninstallMsg,
- QMessageBox::Yes, QMessageBox::No);
- m_askForUninstall = button == QMessageBox::Yes;
+ Q_ASSERT(errorFlags > 0);
+
+ QString uninstallMsg = Tr::tr("Deployment failed with the following errors:") + "\n\n";
+ if (errorFlags & InconsistentCertificates)
+ uninstallMsg += InstallFailedInconsistentCertificatesString + '\n';
+ if (errorFlags & UpdateIncompatible)
+ uninstallMsg += InstallFailedUpdateIncompatible + '\n';
+ if (errorFlags & PermissionModelDowngrade)
+ uninstallMsg += InstallFailedPermissionModelDowngrade + '\n';
+ if (errorFlags & VersionDowngrade)
+ uninstallMsg += InstallFailedVersionDowngrade + '\n';
+ uninstallMsg += '\n';
+ uninstallMsg.append(Tr::tr("Uninstalling the installed package may solve the issue.") + '\n');
+ uninstallMsg.append(Tr::tr("Do you want to uninstall the existing package?"));
+
+ m_askForUninstall = QMessageBox::critical(nullptr, Tr::tr("Install failed"), uninstallMsg,
+ QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes;
}
// TODO: This implementation is not thread safe.
void AndroidDeployQtStep::runImpl(QPromise<void> &promise)
{
if (!m_avdName.isEmpty()) {
- const QString serialNumber = AndroidAvdManager().waitForAvd(m_avdName, promise.future());
+ const QString serialNumber = AndroidAvdManager::waitForAvd(m_avdName, promise.future());
qCDebug(deployStepLog) << "Deploying to AVD:" << m_avdName << serialNumber;
if (serialNumber.isEmpty()) {
reportWarningOrError(Tr::tr("The deployment AVD \"%1\" cannot be started.")
@@ -494,8 +489,8 @@ void AndroidDeployQtStep::runImpl(QPromise<void> &promise)
AndroidManager::setDeviceSerialNumber(target(), serialNumber);
}
- DeployErrorCode returnValue = runDeploy(promise);
- if (returnValue > DeployErrorCode::NoError && returnValue < DeployErrorCode::Failure) {
+ DeployErrorFlags returnValue = runDeploy(promise);
+ if (returnValue > NoError && returnValue < Failure) {
emit askForUninstall(returnValue);
if (m_askForUninstall) {
m_uninstallPreviousPackageRun = true;
@@ -519,9 +514,8 @@ void AndroidDeployQtStep::runImpl(QPromise<void> &promise)
reportWarningOrError(error, Task::Error);
}
- runCommand({m_adbPath,
- AndroidDeviceInfo::adbSelector(m_serialNumber)
- << "pull" << itr.key() << itr.value().nativePath()});
+ runCommand({m_adbPath, {AndroidDeviceInfo::adbSelector(m_serialNumber), "pull", itr.key(),
+ itr.value().nativePath()}});
if (!itr.value().exists()) {
const QString error = Tr::tr("Package deploy: Failed to pull \"%1\" to \"%2\".")
.arg(itr.key())
@@ -635,22 +629,6 @@ void AndroidDeployQtStep::stdError(const QString &line)
}
}
-AndroidDeployQtStep::DeployErrorCode AndroidDeployQtStep::parseDeployErrors(
- const QString &deployOutputLine) const
-{
- DeployErrorCode errorCode = NoError;
-
- if (deployOutputLine.contains(InstallFailedInconsistentCertificatesString))
- errorCode |= InconsistentCertificates;
- if (deployOutputLine.contains(InstallFailedUpdateIncompatible))
- errorCode |= UpdateIncompatible;
- if (deployOutputLine.contains(InstallFailedPermissionModelDowngrade))
- errorCode |= PermissionModelDowngrade;
- if (deployOutputLine.contains(InstallFailedVersionDowngrade))
- errorCode |= VersionDowngrade;
-
- return errorCode;
-}
void AndroidDeployQtStep::reportWarningOrError(const QString &message, Task::TaskType type)
{
diff --git a/src/plugins/android/androiddevice.cpp b/src/plugins/android/androiddevice.cpp
index 0242fdeded..c5b7b78a80 100644
--- a/src/plugins/android/androiddevice.cpp
+++ b/src/plugins/android/androiddevice.cpp
@@ -2,14 +2,16 @@
// Copyright (C) 2016 BogDan Vatra <bog_dan_ro@yahoo.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "androiddevice.h"
+
#include "androidavdmanager.h"
#include "androidconfigurations.h"
#include "androidconstants.h"
-#include "androiddevice.h"
#include "androidmanager.h"
#include "androidsignaloperation.h"
#include "androidtr.h"
#include "avddialog.h"
+#include "avdmanageroutputparser.h"
#include <coreplugin/icore.h>
@@ -21,6 +23,8 @@
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
+#include <solutions/tasking/tasktree.h>
+
#include <utils/async.h>
#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
@@ -47,6 +51,12 @@ static constexpr char ipRegexStr[] = "(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}
static const QRegularExpression ipRegex = QRegularExpression(ipRegexStr);
static constexpr char wifiDevicePort[] = "5555";
+static QString displayNameFromInfo(const AndroidDeviceInfo &info)
+{
+ return info.type == IDevice::Hardware ? AndroidConfig::getProductModel(info.serialNumber)
+ : info.avdName;
+}
+
class AndroidDeviceWidget : public IDeviceWidget
{
public:
@@ -241,13 +251,6 @@ AndroidDeviceInfo AndroidDevice::androidDeviceInfoFromIDevice(const IDevice *dev
return info;
}
-QString AndroidDevice::displayNameFromInfo(const AndroidDeviceInfo &info)
-{
- return info.type == IDevice::Hardware
- ? androidConfig().getProductModel(info.serialNumber)
- : info.avdName;
-}
-
Id AndroidDevice::idFromDeviceInfo(const AndroidDeviceInfo &info)
{
const QString id = (info.type == IDevice::Hardware ? info.serialNumber : info.avdName);
@@ -410,10 +413,10 @@ void AndroidDevice::initAvdSettings()
m_avdSettings.reset(new QSettings(configPath.toUserOutput(), QSettings::IniFormat));
}
-void AndroidDeviceManager::updateAvdsList()
+void AndroidDeviceManager::updateAvdList()
{
- if (!m_avdsFutureWatcher.isRunning() && androidConfig().adbToolPath().exists())
- m_avdsFutureWatcher.setFuture(m_avdManager.avdList());
+ if (AndroidConfig::adbToolPath().exists())
+ m_avdListRunner.start(m_avdListRecipe);
}
IDevice::DeviceState AndroidDeviceManager::getDeviceState(const QString &serial,
@@ -441,14 +444,58 @@ void AndroidDeviceManager::updateDeviceState(const ProjectExplorer::IDevice::Con
devMgr->setDeviceState(id, IDevice::DeviceConnected);
}
+expected_str<void> AndroidDeviceManager::createAvd(const CreateAvdInfo &info, bool force)
+{
+ CommandLine cmd(AndroidConfig::avdManagerToolPath(), {"create", "avd", "-n", info.name});
+ cmd.addArgs({"-k", info.sdkStylePath});
+ if (info.sdcardSize > 0)
+ cmd.addArgs({"-c", QString("%1M").arg(info.sdcardSize)});
+
+ const QString deviceDef = info.deviceDefinition;
+ if (!deviceDef.isEmpty() && deviceDef != "Custom")
+ cmd.addArgs({"-d", deviceDef});
+
+ if (force)
+ cmd.addArg("-f");
+
+ Process process;
+ process.setProcessMode(ProcessMode::Writer);
+ process.setEnvironment(AndroidConfig::toolsEnvironment());
+ process.setCommand(cmd);
+ process.setWriteData("yes\n"); // yes to "Do you wish to create a custom hardware profile"
+
+ QByteArray buffer;
+ QObject::connect(&process, &Process::readyReadStandardOutput, &process, [&process, &buffer] {
+ // This interaction is needed only if there is no "-d" arg for the avdmanager command.
+ buffer += process.readAllRawStandardOutput();
+ if (buffer.endsWith(QByteArray("]:"))) {
+ // truncate to last line
+ const int index = buffer.lastIndexOf('\n');
+ if (index != -1)
+ buffer = buffer.mid(index);
+ if (buffer.contains("hw.gpu.enabled"))
+ process.write("yes\n");
+ else
+ process.write("\n");
+ buffer.clear();
+ }
+ });
+
+ GuardLocker locker(m_avdPathGuard);
+ process.runBlocking();
+ if (process.result() != ProcessResult::FinishedWithSuccess)
+ return Utils::make_unexpected(process.exitMessage());
+ return {};
+}
+
void AndroidDeviceManager::startAvd(const ProjectExplorer::IDevice::Ptr &device, QWidget *parent)
{
Q_UNUSED(parent)
const AndroidDevice *androidDev = static_cast<const AndroidDevice *>(device.get());
const QString name = androidDev->avdName();
qCDebug(androidDeviceLog, "Starting Android AVD id \"%s\".", qPrintable(name));
- auto future = Utils::asyncRun([this, name, device] {
- const QString serialNumber = m_avdManager.startAvd(name);
+ auto future = Utils::asyncRun([name, device] {
+ const QString serialNumber = AndroidAvdManager::startAvd(name);
// Mark the AVD as ReadyToUse once we know it's started
if (!serialNumber.isEmpty()) {
DeviceManager *const devMgr = DeviceManager::instance();
@@ -474,9 +521,9 @@ void AndroidDeviceManager::eraseAvd(const IDevice::Ptr &device, QWidget *parent)
qCDebug(androidDeviceLog) << QString("Erasing Android AVD \"%1\" from the system.").arg(name);
m_removeAvdProcess.reset(new Process);
- const CommandLine command(androidConfig().avdManagerToolPath(), {"delete", "avd", "-n", name});
+ const CommandLine command(AndroidConfig::avdManagerToolPath(), {"delete", "avd", "-n", name});
qCDebug(androidDeviceLog).noquote() << "Running command (removeAvd):" << command.toUserOutput();
- m_removeAvdProcess->setEnvironment(androidConfig().toolsEnvironment());
+ m_removeAvdProcess->setEnvironment(AndroidConfig::toolsEnvironment());
m_removeAvdProcess->setCommand(command);
connect(m_removeAvdProcess.get(), &Process::done, this, [this, device] {
const QString name = device->displayName();
@@ -571,7 +618,7 @@ void AndroidDeviceManager::setEmulatorArguments(QWidget *parent)
dialog.setLabelText(Tr::tr("Emulator command-line startup options "
"(<a href=\"%1\">Help Web Page</a>):")
.arg(helpUrl));
- dialog.setTextValue(androidConfig().emulatorArgs());
+ dialog.setTextValue(AndroidConfig::emulatorArgs());
if (auto label = dialog.findChild<QLabel*>()) {
label->setOpenExternalLinks(true);
@@ -581,12 +628,12 @@ void AndroidDeviceManager::setEmulatorArguments(QWidget *parent)
if (dialog.exec() != QDialog::Accepted)
return;
- androidConfig().setEmulatorArgs(dialog.textValue());
+ AndroidConfig::setEmulatorArgs(dialog.textValue());
}
QString AndroidDeviceManager::getRunningAvdsSerialNumber(const QString &name) const
{
- for (const AndroidDeviceInfo &dev : androidConfig().connectedDevices()) {
+ for (const AndroidDeviceInfo &dev : AndroidConfig::connectedDevices()) {
if (!dev.serialNumber.startsWith("emulator"))
continue;
const QString stdOut = emulatorName(dev.serialNumber);
@@ -600,9 +647,21 @@ QString AndroidDeviceManager::getRunningAvdsSerialNumber(const QString &name) co
return {};
}
+static FilePath avdFilePath()
+{
+ QString avdEnvVar = qtcEnvironmentVariable("ANDROID_AVD_HOME");
+ if (avdEnvVar.isEmpty()) {
+ avdEnvVar = qtcEnvironmentVariable("ANDROID_SDK_HOME");
+ if (avdEnvVar.isEmpty())
+ avdEnvVar = qtcEnvironmentVariable("HOME");
+ avdEnvVar.append("/.android/avd");
+ }
+ return FilePath::fromUserInput(avdEnvVar);
+}
+
void AndroidDeviceManager::setupDevicesWatcher()
{
- if (!androidConfig().adbToolPath().exists()) {
+ if (!AndroidConfig::adbToolPath().exists()) {
qCDebug(androidDeviceLog) << "Cannot start ADB device watcher"
<< "because adb path does not exist.";
return;
@@ -631,38 +690,46 @@ void AndroidDeviceManager::setupDevicesWatcher()
m_adbDeviceWatcherProcess->setStdErrLineCallback([](const QString &error) {
qCDebug(androidDeviceLog) << "ADB device watcher error" << error; });
m_adbDeviceWatcherProcess->setStdOutLineCallback([this](const QString &output) {
- HandleDevicesListChange(output);
+ handleDevicesListChange(output);
});
- const CommandLine command = CommandLine(androidConfig().adbToolPath(), {"track-devices"});
+ const CommandLine command{AndroidConfig::adbToolPath(), {"track-devices"}};
m_adbDeviceWatcherProcess->setCommand(command);
m_adbDeviceWatcherProcess->setWorkingDirectory(command.executable().parentDir());
- m_adbDeviceWatcherProcess->setEnvironment(androidConfig().toolsEnvironment());
+ m_adbDeviceWatcherProcess->setEnvironment(AndroidConfig::toolsEnvironment());
m_adbDeviceWatcherProcess->start();
// Setup AVD filesystem watcher to listen for changes when an avd is created/deleted,
// or started/stopped
- QString avdEnvVar = qtcEnvironmentVariable("ANDROID_AVD_HOME");
- if (avdEnvVar.isEmpty()) {
- avdEnvVar = qtcEnvironmentVariable("ANDROID_SDK_HOME");
- if (avdEnvVar.isEmpty())
- avdEnvVar = qtcEnvironmentVariable("HOME");
- avdEnvVar.append("/.android/avd");
- }
- const FilePath avdPath = FilePath::fromUserInput(avdEnvVar);
- m_avdFileSystemWatcher.addPath(avdPath.toString());
- connect(&m_avdsFutureWatcher, &QFutureWatcherBase::finished,
- this, &AndroidDeviceManager::HandleAvdsListChange);
+ m_avdFileSystemWatcher.addPath(avdFilePath().toString());
connect(&m_avdFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, [this] {
- // If the avd list upate command is running no need to call it again.
- if (!m_avdsFutureWatcher.isRunning())
- updateAvdsList();
+ if (!m_avdPathGuard.isLocked())
+ updateAvdList();
});
// Call initial update
- updateAvdsList();
+ updateAvdList();
+}
+
+IDevice::Ptr AndroidDeviceManager::createDeviceFromInfo(const CreateAvdInfo &info)
+{
+ if (info.apiLevel < 0) {
+ qCWarning(androidDeviceLog) << "System image of the created AVD is nullptr";
+ return IDevice::Ptr();
+ }
+ AndroidDevice *dev = new AndroidDevice;
+ const Utils::Id deviceId = AndroidDevice::idFromAvdInfo(info);
+ dev->setupId(IDevice::AutoDetected, deviceId);
+ dev->setMachineType(IDevice::Emulator);
+ dev->settings()->displayName.setValue(info.name);
+ dev->setDeviceState(IDevice::DeviceConnected);
+ dev->setAvdPath(avdFilePath() / (info.name + ".avd"));
+ dev->setExtraData(Constants::AndroidAvdName, info.name);
+ dev->setExtraData(Constants::AndroidCpuAbi, {info.abi});
+ dev->setExtraData(Constants::AndroidSdk, info.apiLevel);
+ return IDevice::Ptr(dev);
}
-void AndroidDeviceManager::HandleAvdsListChange()
+void AndroidDeviceManager::handleAvdListChange(const AndroidDeviceInfoList &avdList)
{
DeviceManager *const devMgr = DeviceManager::instance();
@@ -675,9 +742,9 @@ void AndroidDeviceManager::HandleAvdsListChange()
}
QList<Id> connectedDevs;
- for (const AndroidDeviceInfo &item : m_avdsFutureWatcher.result()) {
+ for (const AndroidDeviceInfo &item : avdList) {
const Id deviceId = AndroidDevice::idFromDeviceInfo(item);
- const QString displayName = AndroidDevice::displayNameFromInfo(item);
+ const QString displayName = displayNameFromInfo(item);
IDevice::ConstPtr dev = devMgr->find(deviceId);
if (dev) {
const auto androidDev = static_cast<const AndroidDevice *>(dev.get());
@@ -734,7 +801,7 @@ void AndroidDeviceManager::HandleAvdsListChange()
}
}
-void AndroidDeviceManager::HandleDevicesListChange(const QString &serialNumber)
+void AndroidDeviceManager::handleDevicesListChange(const QString &serialNumber)
{
DeviceManager *const devMgr = DeviceManager::instance();
const QStringList serialBits = serialNumber.split('\t');
@@ -771,7 +838,7 @@ void AndroidDeviceManager::HandleDevicesListChange(const QString &serialNumber)
devMgr->setDeviceState(avdId, state);
} else {
const Id id = Id(Constants::ANDROID_DEVICE_ID).withSuffix(':' + serial);
- QString displayName = androidConfig().getProductModel(serial);
+ QString displayName = AndroidConfig::getProductModel(serial);
// Check if the device is connected via WiFi. A sample serial of such devices can be
// like: "192.168.1.190:5555"
static const auto ipRegex = QRegularExpression(ipRegexStr + QStringLiteral(":(\\d{1,5})"));
@@ -793,8 +860,8 @@ void AndroidDeviceManager::HandleDevicesListChange(const QString &serialNumber)
newDev->setDeviceState(state);
newDev->setExtraData(Constants::AndroidSerialNumber, serial);
- newDev->setExtraData(Constants::AndroidCpuAbi, androidConfig().getAbis(serial));
- newDev->setExtraData(Constants::AndroidSdk, androidConfig().getSDKVersion(serial));
+ newDev->setExtraData(Constants::AndroidCpuAbi, AndroidConfig::getAbis(serial));
+ newDev->setExtraData(Constants::AndroidSdk, AndroidConfig::getSDKVersion(serial));
qCDebug(androidDeviceLog, "Registering new Android device id \"%s\".",
newDev->id().toString().toUtf8().data());
@@ -810,16 +877,93 @@ AndroidDeviceManager *AndroidDeviceManager::instance()
return s_instance;
}
+enum TagModification { CommentOut, Uncomment };
+
+static void modifyManufacturerTag(const FilePath &avdPath, TagModification modification)
+{
+ if (!avdPath.exists())
+ return;
+
+ const FilePath configFilePath = avdPath / "config.ini";
+ FileReader reader;
+ if (!reader.fetch(configFilePath, QIODevice::ReadOnly | QIODevice::Text))
+ return;
+
+ FileSaver saver(configFilePath);
+ QTextStream textStream(reader.data());
+ while (!textStream.atEnd()) {
+ QString line = textStream.readLine();
+ if (line.contains("hw.device.manufacturer")) {
+ if (modification == Uncomment)
+ line.replace("#", "");
+ else
+ line.prepend("#");
+ }
+ line.append("\n");
+ saver.write(line.toUtf8());
+ }
+ saver.finalize();
+}
+
AndroidDeviceManager::AndroidDeviceManager(QObject *parent)
: QObject(parent)
+ , m_avdListRecipe{}
{
QTC_ASSERT(!s_instance, return);
s_instance = this;
+
+ using namespace Tasking;
+
+ const Storage<FilePaths> storage;
+
+ const LoopUntil iterator([storage](int iteration) {
+ return iteration == 0 || storage->count() > 0;
+ });
+
+ const auto onProcessSetup = [](Process &process) {
+ const CommandLine cmd(AndroidConfig::avdManagerToolPath(), {"list", "avd"});
+ qCDebug(androidDeviceLog).noquote() << "Running AVD Manager command:" << cmd.toUserOutput();
+ process.setEnvironment(AndroidConfig::toolsEnvironment());
+ process.setCommand(cmd);
+ };
+ const auto onProcessDone = [this, storage](const Process &process, DoneWith result) {
+ const QString output = process.allOutput();
+ if (result != DoneWith::Success) {
+ qCDebug(androidDeviceLog)
+ << "Avd list command failed" << output << AndroidConfig::sdkToolsVersion();
+ return DoneResult::Error;
+ }
+
+ const auto parsedAvdList = parseAvdList(output);
+ if (parsedAvdList.errorPaths.isEmpty()) {
+ for (const FilePath &avdPath : *storage)
+ modifyManufacturerTag(avdPath, Uncomment);
+ storage->clear(); // Don't repeat anymore
+ handleAvdListChange(parsedAvdList.avdList);
+ } else {
+ for (const FilePath &avdPath : parsedAvdList.errorPaths)
+ modifyManufacturerTag(avdPath, CommentOut);
+ storage->append(parsedAvdList.errorPaths);
+ }
+ return DoneResult::Success; // Repeat
+ };
+
+ // Currenly avdmanager tool fails to parse some AVDs because the correct
+ // device definitions at devices.xml does not have some of the newest devices.
+ // Particularly, failing because of tag "hw.device.manufacturer", thus removing
+ // it would make paring successful. However, it has to be returned afterwards,
+ // otherwise, Android Studio would give an error during parsing also. So this fix
+ // aim to keep support for Qt Creator and Android Studio.
+
+ m_avdListRecipe = Group {
+ storage,
+ iterator,
+ ProcessTask(onProcessSetup, onProcessDone)
+ };
}
AndroidDeviceManager::~AndroidDeviceManager()
{
- m_avdsFutureWatcher.waitForFinished();
QTC_ASSERT(s_instance == this, return);
s_instance = nullptr;
}
@@ -837,7 +981,7 @@ public:
":/android/images/androiddevice.png");
setConstructionFunction(&AndroidDevice::create);
setCreator([] {
- if (!androidConfig().sdkToolsOk()) {
+ if (!AndroidConfig::sdkToolsOk()) {
AndroidDeviceWidget::infoDialog(Tr::tr("Android support is not yet configured."));
return IDevice::Ptr();
}
@@ -846,16 +990,15 @@ public:
if (dialog.exec() != QDialog::Accepted)
return IDevice::Ptr();
- const IDevice::Ptr dev = dialog.device();
+ const IDevice::Ptr dev = AndroidDeviceManager::createDeviceFromInfo(dialog.avdInfo());
if (const auto androidDev = static_cast<AndroidDevice *>(dev.get())) {
qCDebug(androidDeviceLog, "Created new Android AVD id \"%s\".",
qPrintable(androidDev->avdName()));
- } else {
- AndroidDeviceWidget::criticalDialog(
- Tr::tr("The device info returned from AvdDialog is invalid."));
+ return dev;
}
-
- return IDevice::Ptr(dev);
+ AndroidDeviceWidget::criticalDialog(
+ Tr::tr("The device info returned from AvdDialog is invalid."));
+ return IDevice::Ptr();
});
}
};
diff --git a/src/plugins/android/androiddevice.h b/src/plugins/android/androiddevice.h
index 57b386dc4d..789fa75ff1 100644
--- a/src/plugins/android/androiddevice.h
+++ b/src/plugins/android/androiddevice.h
@@ -4,14 +4,16 @@
#pragma once
-#include "androidavdmanager.h"
#include "androidconfigurations.h"
#include "androiddeviceinfo.h"
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/devicesupport/idevicefactory.h>
-#include <QFutureWatcher>
+#include <solutions/tasking/tasktreerunner.h>
+
+#include <utils/guard.h>
+
#include <QFileSystemWatcher>
#include <QSettings>
@@ -27,7 +29,6 @@ public:
static IDevice::Ptr create();
static AndroidDeviceInfo androidDeviceInfoFromIDevice(const IDevice *dev);
- static QString displayNameFromInfo(const AndroidDeviceInfo &info);
static Utils::Id idFromDeviceInfo(const AndroidDeviceInfo &info);
static Utils::Id idFromAvdInfo(const CreateAvdInfo &info);
@@ -74,10 +75,11 @@ class AndroidDeviceManager : public QObject
public:
static AndroidDeviceManager *instance();
void setupDevicesWatcher();
- void updateAvdsList();
+ void updateAvdList();
IDevice::DeviceState getDeviceState(const QString &serial, IDevice::MachineType type) const;
void updateDeviceState(const ProjectExplorer::IDevice::ConstPtr &device);
+ Utils::expected_str<void> createAvd(const CreateAvdInfo &info, bool force);
void startAvd(const ProjectExplorer::IDevice::Ptr &device, QWidget *parent = nullptr);
void eraseAvd(const ProjectExplorer::IDevice::Ptr &device, QWidget *parent = nullptr);
void setupWifiForDevice(const ProjectExplorer::IDevice::Ptr &device, QWidget *parent = nullptr);
@@ -86,20 +88,23 @@ public:
QString getRunningAvdsSerialNumber(const QString &name) const;
+ static ProjectExplorer::IDevice::Ptr createDeviceFromInfo(const CreateAvdInfo &info);
+
private:
explicit AndroidDeviceManager(QObject *parent);
~AndroidDeviceManager();
- void HandleDevicesListChange(const QString &serialNumber);
- void HandleAvdsListChange();
+ void handleDevicesListChange(const QString &serialNumber);
+ void handleAvdListChange(const AndroidDeviceInfoList &avdList);
QString emulatorName(const QString &serialNumber) const;
- QFutureWatcher<AndroidDeviceInfoList> m_avdsFutureWatcher;
+ Tasking::Group m_avdListRecipe;
+ Tasking::TaskTreeRunner m_avdListRunner;
std::unique_ptr<Utils::Process> m_removeAvdProcess;
QFileSystemWatcher m_avdFileSystemWatcher;
+ Utils::Guard m_avdPathGuard;
std::unique_ptr<Utils::Process> m_adbDeviceWatcherProcess;
- AndroidAvdManager m_avdManager;
friend void setupAndroidDeviceManager(QObject *guard);
};
diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp
index 5128f761a0..9cca27242d 100644
--- a/src/plugins/android/androidmanager.cpp
+++ b/src/plugins/android/androidmanager.cpp
@@ -34,6 +34,7 @@
#include <QVersionNumber>
using namespace Android::Internal;
+using namespace Core;
using namespace ProjectExplorer;
using namespace Utils;
@@ -50,19 +51,32 @@ const char qtcSignature[] = "This file is generated by QtCreator to be read by "
static Q_LOGGING_CATEGORY(androidManagerLog, "qtc.android.androidManager", QtWarningMsg)
-class Library
+static std::optional<QDomElement> documentElement(const FilePath &fileName)
{
-public:
- int level = -1;
- QStringList dependencies;
- QString name;
-};
-
-using LibrariesMap = QMap<QString, Library>;
+ QFile file(fileName.toString());
+ if (!file.open(QIODevice::ReadOnly)) {
+ MessageManager::writeDisrupting(Tr::tr("Cannot open: %1").arg(fileName.toUserOutput()));
+ return {};
+ }
+ QDomDocument doc;
+ if (!doc.setContent(file.readAll())) {
+ MessageManager::writeDisrupting(Tr::tr("Cannot parse: %1").arg(fileName.toUserOutput()));
+ return {};
+ }
+ return doc.documentElement();
+}
-static bool openXmlFile(QDomDocument &doc, const FilePath &fileName);
-static bool openManifest(const Target *target, QDomDocument &doc);
-static int parseMinSdk(const QDomElement &manifestElem);
+static int parseMinSdk(const QDomElement &manifestElem)
+{
+ const QDomElement usesSdk = manifestElem.firstChildElement("uses-sdk");
+ if (!usesSdk.isNull() && usesSdk.hasAttribute("android:minSdkVersion")) {
+ bool ok;
+ int tmp = usesSdk.attribute("android:minSdkVersion").toInt(&ok);
+ if (ok)
+ return tmp;
+ }
+ return 0;
+}
static const ProjectNode *currentProjectNode(const Target *target)
{
@@ -71,30 +85,62 @@ static const ProjectNode *currentProjectNode(const Target *target)
QString packageName(const Target *target)
{
- QDomDocument doc;
- if (!openManifest(target, doc))
- return {};
- QDomElement manifestElem = doc.documentElement();
- return manifestElem.attribute(QLatin1String("package"));
-}
+ QString packageName;
+
+ // Check build.gradle
+ auto isComment = [](const QByteArray &trimmed) {
+ return trimmed.startsWith("//") || trimmed.startsWith('*') || trimmed.startsWith("/*");
+ };
+
+ const FilePath androidBuildDir = androidBuildDirectory(target);
+ const expected_str<QByteArray> gradleContents = androidBuildDir.pathAppended("build.gradle")
+ .fileContents();
+ if (gradleContents) {
+ const auto lines = gradleContents->split('\n');
+ for (const auto &line : lines) {
+ const QByteArray trimmed = line.trimmed();
+ if (isComment(trimmed) || !trimmed.contains("namespace"))
+ continue;
+
+ int idx = trimmed.indexOf('=');
+ if (idx == -1)
+ idx = trimmed.indexOf(' ');
+ if (idx > -1) {
+ packageName = QString::fromUtf8(trimmed.mid(idx + 1).trimmed());
+ if (packageName == "androidPackageName") {
+ // Check gradle.properties
+ const QSettings gradleProperties = QSettings(
+ androidBuildDir.pathAppended("gradle.properties").toFSPathString(),
+ QSettings::IniFormat);
+ packageName = gradleProperties.value("androidPackageName").toString();
+ } else {
+ // Remove quotes
+ if (packageName.size() > 2)
+ packageName = packageName.remove(0, 1).chopped(1);
+ }
-QString packageName(const FilePath &manifestFile)
-{
- QDomDocument doc;
- if (!openXmlFile(doc, manifestFile))
- return {};
- QDomElement manifestElem = doc.documentElement();
- return manifestElem.attribute(QLatin1String("package"));
+ break;
+ }
+ }
+ }
+
+ if (packageName.isEmpty()) {
+ // Check AndroidManifest.xml
+ const auto element = documentElement(AndroidManager::manifestPath(target));
+ if (element)
+ packageName = element->attribute("package");
+ }
+
+ return packageName;
}
QString activityName(const Target *target)
{
- QDomDocument doc;
- if (!openManifest(target, doc))
+ const auto element = documentElement(AndroidManager::manifestPath(target));
+ if (!element)
return {};
- QDomElement activityElem = doc.documentElement().firstChildElement(
- QLatin1String("application")).firstChildElement(QLatin1String("activity"));
- return activityElem.attribute(QLatin1String("android:name"));
+ return element->firstChildElement("application").firstChildElement("activity")
+ .attribute("android:name");
}
static FilePath manifestSourcePath(const Target *target)
@@ -118,10 +164,11 @@ static FilePath manifestSourcePath(const Target *target)
*/
int minimumSDK(const Target *target)
{
- QDomDocument doc;
- if (!openXmlFile(doc, manifestSourcePath(target)))
+ const auto element = documentElement(manifestSourcePath(target));
+ if (!element)
return minimumSDK(target->kit());
- const int minSdkVersion = parseMinSdk(doc.documentElement());
+
+ const int minSdkVersion = parseMinSdk(*element);
if (minSdkVersion == 0)
return AndroidManager::defaultMinimumSDK(QtSupport::QtKitAspect::qtVersion(target->kit()));
return minSdkVersion;
@@ -136,12 +183,12 @@ int minimumSDK(const Kit *kit)
int minSdkVersion = -1;
QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(kit);
if (version && version->targetDeviceTypes().contains(Constants::ANDROID_DEVICE_TYPE)) {
- FilePath stockManifestFilePath = FilePath::fromUserInput(
+ const FilePath stockManifestFilePath = FilePath::fromUserInput(
version->prefix().toString() + "/src/android/templates/AndroidManifest.xml");
- QDomDocument doc;
- if (openXmlFile(doc, stockManifestFilePath)) {
- minSdkVersion = parseMinSdk(doc.documentElement());
- }
+
+ const auto element = documentElement(stockManifestFilePath);
+ if (element)
+ minSdkVersion = parseMinSdk(*element);
}
if (minSdkVersion == 0)
return AndroidManager::defaultMinimumSDK(version);
@@ -190,25 +237,23 @@ QJsonObject deploymentSettings(const Target *target)
QJsonObject settings;
settings["_description"] = qtcSignature;
settings["qt"] = qt->prefix().toString();
- settings["ndk"] = androidConfig().ndkLocation(qt).toString();
- settings["sdk"] = androidConfig().sdkLocation().toString();
+ settings["ndk"] = AndroidConfig::ndkLocation(qt).toString();
+ settings["sdk"] = AndroidConfig::sdkLocation().toString();
if (!qt->supportsMultipleQtAbis()) {
const QStringList abis = applicationAbis(target);
QTC_ASSERT(abis.size() == 1, return {});
- settings["stdcpp-path"] = (androidConfig().toolchainPath(qt)
+ settings["stdcpp-path"] = (AndroidConfig::toolchainPath(qt)
/ "sysroot/usr/lib"
/ archTriplet(abis.first())
/ "libc++_shared.so").toString();
} else {
- settings["stdcpp-path"] = androidConfig()
- .toolchainPath(qt)
- .pathAppended("sysroot/usr/lib")
- .toString();
+ settings["stdcpp-path"]
+ = AndroidConfig::toolchainPath(qt).pathAppended("sysroot/usr/lib").toString();
}
settings["toolchain-prefix"] = "llvm";
settings["tool-prefix"] = "llvm";
settings["useLLVM"] = true;
- settings["ndk-host"] = androidConfig().toolchainHost(qt);
+ settings["ndk-host"] = AndroidConfig::toolchainHost(qt);
return settings;
}
@@ -240,7 +285,7 @@ bool isQt5CmakeProject(const ProjectExplorer::Target *target)
{
const QtSupport::QtVersion *qt = QtSupport::QtKitAspect::qtVersion(target->kit());
const bool isQt5 = qt && qt->qtVersion() < QVersionNumber(6, 0, 0);
- const Core::Context cmakeCtx = Core::Context(CMakeProjectManager::Constants::CMAKE_PROJECT_ID);
+ const Context cmakeCtx(CMakeProjectManager::Constants::CMAKE_PROJECT_ID);
const bool isCmakeProject = (target->project()->projectContext() == cmakeCtx);
return isQt5 && isCmakeProject;
}
@@ -371,7 +416,7 @@ bool skipInstallationAndPackageSteps(const Target *target)
const Project *p = target->project();
- const Core::Context cmakeCtx = Core::Context(CMakeProjectManager::Constants::CMAKE_PROJECT_ID);
+ const Context cmakeCtx(CMakeProjectManager::Constants::CMAKE_PROJECT_ID);
const bool isCmakeProject = p->projectContext() == cmakeCtx;
if (isCmakeProject)
return false; // CMake reports ProductType::Other for Android Apps
@@ -538,43 +583,6 @@ QString androidNameForApiLevel(int x)
}
}
-static void raiseError(const QString &reason)
-{
- QMessageBox::critical(nullptr, Tr::tr("Error creating Android templates."), reason);
-}
-
-static bool openXmlFile(QDomDocument &doc, const FilePath &fileName)
-{
- QFile f(fileName.toString());
- if (!f.open(QIODevice::ReadOnly))
- return false;
-
- if (!doc.setContent(f.readAll())) {
- raiseError(Tr::tr("Cannot parse \"%1\".").arg(fileName.toUserOutput()));
- return false;
- }
- return true;
-}
-
-static bool openManifest(const Target *target, QDomDocument &doc)
-{
- return openXmlFile(doc, AndroidManager::manifestPath(target));
-}
-
-static int parseMinSdk(const QDomElement &manifestElem)
-{
- QDomElement usesSdk = manifestElem.firstChildElement(QLatin1String("uses-sdk"));
- if (usesSdk.isNull())
- return 0;
- if (usesSdk.hasAttribute(QLatin1String("android:minSdkVersion"))) {
- bool ok;
- int tmp = usesSdk.attribute(QLatin1String("android:minSdkVersion")).toInt(&ok);
- if (ok)
- return tmp;
- }
- return 0;
-}
-
void installQASIPackage(Target *target, const FilePath &packagePath)
{
const QStringList appAbis = AndroidManager::applicationAbis(target);
@@ -587,9 +595,9 @@ void installQASIPackage(Target *target, const FilePath &packagePath)
QString deviceSerialNumber = info.serialNumber;
if (info.type == IDevice::Emulator) {
- deviceSerialNumber = AndroidAvdManager().startAvd(info.avdName);
+ deviceSerialNumber = AndroidAvdManager::startAvd(info.avdName);
if (deviceSerialNumber.isEmpty())
- Core::MessageManager::writeDisrupting(Tr::tr("Starting Android virtual device failed."));
+ MessageManager::writeDisrupting(Tr::tr("Starting Android virtual device failed."));
}
QStringList arguments = AndroidDeviceInfo::adbSelector(deviceSerialNumber);
@@ -600,7 +608,7 @@ void installQASIPackage(Target *target, const FilePath &packagePath)
// TODO: Potential leak when the process is still running on Creator shutdown.
QObject::connect(process, &Process::done, process, &QObject::deleteLater);
} else {
- Core::MessageManager::writeDisrupting(
+ MessageManager::writeDisrupting(
Tr::tr("Android package installation failed.\n%1").arg(error));
}
}
@@ -609,12 +617,12 @@ bool checkKeystorePassword(const FilePath &keystorePath, const QString &keystore
{
if (keystorePasswd.isEmpty())
return false;
- const CommandLine cmd(androidConfig().keytoolPath(),
+ const CommandLine cmd(AndroidConfig::keytoolPath(),
{"-list", "-keystore", keystorePath.toUserOutput(),
"--storepass", keystorePasswd});
Process proc;
proc.setCommand(cmd);
- proc.runBlocking(10s, EventLoopMode::On);
+ proc.runBlocking(10s);
return proc.result() == ProcessResult::FinishedWithSuccess;
}
@@ -630,8 +638,8 @@ bool checkCertificatePassword(const FilePath &keystorePath, const QString &keyst
arguments << certificatePasswd;
Process proc;
- proc.setCommand({androidConfig().keytoolPath(), arguments});
- proc.runBlocking(10s, EventLoopMode::On);
+ proc.setCommand({AndroidConfig::keytoolPath(), arguments});
+ proc.runBlocking(10s);
return proc.result() == ProcessResult::FinishedWithSuccess;
}
@@ -639,19 +647,19 @@ bool checkCertificateExists(const FilePath &keystorePath, const QString &keystor
const QString &alias)
{
// assumes that the keystore password is correct
- QStringList arguments = { "-list", "-keystore", keystorePath.toUserOutput(),
- "--storepass", keystorePasswd, "-alias", alias };
+ const QStringList arguments = {"-list", "-keystore", keystorePath.toUserOutput(),
+ "--storepass", keystorePasswd, "-alias", alias};
Process proc;
- proc.setCommand({androidConfig().keytoolPath(), arguments});
- proc.runBlocking(10s, EventLoopMode::On);
+ proc.setCommand({AndroidConfig::keytoolPath(), arguments});
+ proc.runBlocking(10s);
return proc.result() == ProcessResult::FinishedWithSuccess;
}
Process *startAdbProcess(const QStringList &args, QString *err)
{
std::unique_ptr<Process> process(new Process);
- const FilePath adb = androidConfig().adbToolPath();
+ const FilePath adb = AndroidConfig::adbToolPath();
const CommandLine command{adb, args};
qCDebug(androidManagerLog).noquote() << "Running command (async):" << command.toUserOutput();
process->setCommand(command);
@@ -689,7 +697,7 @@ static SdkToolResult runCommand(const CommandLine &command, const QByteArray &wr
SdkToolResult runAdbCommand(const QStringList &args, const QByteArray &writeData, int timeoutS)
{
- return runCommand({androidConfig().adbToolPath(), args}, writeData, timeoutS);
+ return runCommand({AndroidConfig::adbToolPath(), args}, writeData, timeoutS);
}
} // namespace Android::AndroidManager
diff --git a/src/plugins/android/androidmanager.h b/src/plugins/android/androidmanager.h
index f3b9302d13..382768e898 100644
--- a/src/plugins/android/androidmanager.h
+++ b/src/plugins/android/androidmanager.h
@@ -39,7 +39,6 @@ namespace AndroidManager
constexpr auto firstQtWithAndroidDeployQt = {5, 4, 0};
QString packageName(const ProjectExplorer::Target *target);
-QString packageName(const Utils::FilePath &manifestFile);
QString activityName(const ProjectExplorer::Target *target);
QString deviceSerialNumber(const ProjectExplorer::Target *target);
diff --git a/src/plugins/android/androidmanifesteditorwidget.cpp b/src/plugins/android/androidmanifesteditorwidget.cpp
index 608f0b5262..049cd67404 100644
--- a/src/plugins/android/androidmanifesteditorwidget.cpp
+++ b/src/plugins/android/androidmanifesteditorwidget.cpp
@@ -592,7 +592,7 @@ void AndroidManifestEditorWidget::postSave()
const FilePath docPath = m_textEditorWidget->textDocument()->filePath();
if (Target *target = androidTarget(docPath)) {
if (BuildConfiguration *bc = target->activeBuildConfiguration()) {
- QString androidNdkPlatform = androidConfig().bestNdkPlatformMatch(
+ QString androidNdkPlatform = AndroidConfig::bestNdkPlatformMatch(
AndroidManager::minimumSDK(target),
QtSupport::QtKitAspect::qtVersion(
androidTarget(m_textEditorWidget->textDocument()->filePath())->kit()));
diff --git a/src/plugins/android/androidplugin.cpp b/src/plugins/android/androidplugin.cpp
index e1217188bb..2ab9e2f068 100644
--- a/src/plugins/android/androidplugin.cpp
+++ b/src/plugins/android/androidplugin.cpp
@@ -112,8 +112,8 @@ class AndroidPlugin final : public ExtensionSystem::IPlugin
setupJavaEditor();
setupAndroidManifestEditor();
- connect(KitManager::instance(), &KitManager::kitsLoaded,
- this, &AndroidPlugin::kitsRestored);
+ connect(KitManager::instance(), &KitManager::kitsLoaded, this, &AndroidPlugin::kitsRestored,
+ Qt::SingleShotConnection);
LanguageClient::LanguageClientSettings::registerClientType(
{Android::Constants::JLS_SETTINGS_ID,
@@ -135,7 +135,7 @@ class AndroidPlugin final : public ExtensionSystem::IPlugin
return v->targetDeviceTypes().contains(Android::Constants::ANDROID_DEVICE_TYPE);
}).isEmpty();
- if (!androidConfig().sdkFullyConfigured() && qtForAndroidInstalled)
+ if (!AndroidConfig::sdkFullyConfigured() && qtForAndroidInstalled)
askUserAboutAndroidSetup();
AndroidConfigurations::registerNewToolchains();
@@ -145,8 +145,6 @@ class AndroidPlugin final : public ExtensionSystem::IPlugin
AndroidConfigurations::registerNewToolchains();
AndroidConfigurations::updateAutomaticKitList();
});
- disconnect(KitManager::instance(), &KitManager::kitsLoaded,
- this, &AndroidPlugin::kitsRestored);
}
void askUserAboutAndroidSetup()
diff --git a/src/plugins/android/androidqmlpreviewworker.cpp b/src/plugins/android/androidqmlpreviewworker.cpp
index d37986e97f..5570e54096 100644
--- a/src/plugins/android/androidqmlpreviewworker.cpp
+++ b/src/plugins/android/androidqmlpreviewworker.cpp
@@ -14,30 +14,27 @@
#include <coreplugin/icore.h>
#include <projectexplorer/buildsystem.h>
-#include <projectexplorer/devicesupport/devicemanager.h>
#include <projectexplorer/environmentaspect.h>
-#include <projectexplorer/kit.h>
-#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/runcontrol.h>
#include <projectexplorer/target.h>
+#include <solutions/tasking/tasktreerunner.h>
+
#include <qmlprojectmanager/qmlprojectconstants.h>
-#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitaspect.h>
-#include <utils/async.h>
#include <utils/qtcprocess.h>
#include <QDateTime>
-#include <QDeadlineTimer>
-#include <QFutureWatcher>
-#include <QThread>
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
+using namespace std::chrono_literals;
+
namespace Android::Internal {
#define APP_ID "io.qt.qtdesignviewer"
@@ -45,7 +42,17 @@ namespace Android::Internal {
class ApkInfo
{
public:
- ApkInfo();
+ ApkInfo()
+ : abis{ProjectExplorer::Constants::ANDROID_ABI_X86,
+ ProjectExplorer::Constants::ANDROID_ABI_X86_64,
+ ProjectExplorer::Constants::ANDROID_ABI_ARM64_V8A,
+ ProjectExplorer::Constants::ANDROID_ABI_ARMEABI_V7A}
+ , appId(APP_ID)
+ , uploadDir("/data/local/tmp/" APP_ID "/")
+ // TODO Add possibility to run Qt5 built version of Qt Design Viewer
+ , activityId(APP_ID "/org.qtproject.qt.android.bindings.QtActivity")
+ , name("Qt Design Viewer")
+ {}
const QStringList abis;
const QString appId;
const QString uploadDir;
@@ -53,18 +60,6 @@ public:
const QString name;
};
-ApkInfo::ApkInfo() :
- abis({ProjectExplorer::Constants::ANDROID_ABI_X86,
- ProjectExplorer::Constants::ANDROID_ABI_X86_64,
- ProjectExplorer::Constants::ANDROID_ABI_ARM64_V8A,
- ProjectExplorer::Constants::ANDROID_ABI_ARMEABI_V7A}),
- appId(APP_ID),
- uploadDir("/data/local/tmp/" APP_ID "/"),
- // TODO Add possibility to run Qt5 built version of Qt Design Viewer
- activityId(APP_ID "/org.qtproject.qt.android.bindings.QtActivity"),
- name("Qt Design Viewer")
-{
-}
Q_GLOBAL_STATIC(ApkInfo, apkInfo)
@@ -82,7 +77,6 @@ class AndroidQmlPreviewWorker : public RunWorker
Q_OBJECT
public:
AndroidQmlPreviewWorker(RunControl *runControl);
- ~AndroidQmlPreviewWorker();
signals:
void previewPidChanged();
@@ -96,10 +90,9 @@ private:
bool preparePreviewArtefacts();
bool uploadPreviewArtefacts();
+ CommandLine adbCommand(const QStringList &arguments) const;
SdkToolResult runAdbCommand(const QStringList &arguments) const;
SdkToolResult runAdbShellCommand(const QStringList &arguments) const;
- int pidofPreview() const;
- bool isPreviewRunning(int lastKnownPid = -1) const;
void startPidWatcher();
void startLogcat();
@@ -108,15 +101,15 @@ private:
bool startPreviewApp();
bool stopPreviewApp();
- Utils::FilePath designViewerApkPath(const QString &abi) const;
- Utils::FilePath createQmlrcFile(const Utils::FilePath &workFolder, const QString &basename);
+ FilePath designViewerApkPath(const QString &abi) const;
+ FilePath createQmlrcFile(const FilePath &workFolder, const QString &basename);
RunControl *m_rc = nullptr;
QString m_serialNumber;
QStringList m_avdAbis;
int m_viewerPid = -1;
- QFutureWatcher<void> m_pidFutureWatcher;
- Utils::Process m_logcatProcess;
+ TaskTreeRunner m_pidRunner;
+ Process m_logcatProcess;
QString m_logcatStartTimeStamp;
UploadInfo m_uploadInfo;
};
@@ -133,6 +126,16 @@ FilePath AndroidQmlPreviewWorker::designViewerApkPath(const QString &abi) const
return {};
}
+CommandLine AndroidQmlPreviewWorker::adbCommand(const QStringList &arguments) const
+{
+ CommandLine cmd{AndroidConfig::adbToolPath()};
+ if (!m_serialNumber.isEmpty())
+ cmd.addArgs(AndroidDeviceInfo::adbSelector(m_serialNumber));
+ cmd.addArg("shell");
+ cmd.addArgs(arguments);
+ return cmd;
+}
+
SdkToolResult AndroidQmlPreviewWorker::runAdbCommand(const QStringList &arguments) const
{
QStringList args;
@@ -147,44 +150,49 @@ SdkToolResult AndroidQmlPreviewWorker::runAdbShellCommand(const QStringList &arg
return runAdbCommand(QStringList() << "shell" << arguments);
}
-int AndroidQmlPreviewWorker::pidofPreview() const
+void AndroidQmlPreviewWorker::startPidWatcher()
{
- const QStringList command{"pidof", apkInfo()->appId};
- const SdkToolResult res = runAdbShellCommand(command);
- return res.success() ? res.stdOut().toInt() : -1;
-}
+ const LoopUntil pidIterator([this](int) { return m_viewerPid <= 0; });
+ const LoopUntil alivePidIterator([this](int) { return m_viewerPid > 0; });
-bool AndroidQmlPreviewWorker::isPreviewRunning(int lastKnownPid) const
-{
- const int pid = pidofPreview();
- return (lastKnownPid > 1) ? lastKnownPid == pid : pid > 1;
-}
+ const auto onPidSetup = [this](Process &process) {
+ process.setCommand(adbCommand({"pidof", apkInfo()->appId}));
+ };
+ const auto onPidDone = [this](const Process &process) {
+ bool ok = false;
+ const int pid = process.cleanedStdOut().trimmed().toInt(&ok);
+ if (ok && pid > 0) {
+ m_viewerPid = pid;
+ // TODO: make a continuation task (logcat)
+ emit previewPidChanged();
+ }
+ };
-void AndroidQmlPreviewWorker::startPidWatcher()
-{
- m_pidFutureWatcher.setFuture(Utils::asyncRun([this] {
- // wait for started
- const int sleepTimeMs = 2000;
- QDeadlineTimer deadline(20000);
- while (!m_pidFutureWatcher.isCanceled() && !deadline.hasExpired()) {
- if (m_viewerPid == -1) {
- m_viewerPid = pidofPreview();
- if (m_viewerPid > 0) {
- emit previewPidChanged();
- break;
- }
- }
- QThread::msleep(sleepTimeMs);
+ const auto onAlivePidDone = [this](const Process &process) {
+ bool ok = false;
+ const int pid = process.cleanedStdOut().trimmed().toInt(&ok);
+ if (!ok || pid != m_viewerPid) {
+ m_viewerPid = -1;
+ stop();
}
+ };
- while (!m_pidFutureWatcher.isCanceled()) {
- if (!isPreviewRunning(m_viewerPid)) {
- stop();
- break;
- }
- QThread::msleep(sleepTimeMs);
+ const TimeoutTask timeout([](std::chrono::milliseconds &timeout) { timeout = 2s; });
+
+ const Group root {
+ Group {
+ pidIterator,
+ ProcessTask(onPidSetup, onPidDone, CallDoneIf::Success),
+ timeout
+ }.withTimeout(20s),
+ Group {
+ alivePidIterator,
+ ProcessTask(onPidSetup, onAlivePidDone),
+ timeout
}
- }));
+ };
+
+ m_pidRunner.start(root);
}
void AndroidQmlPreviewWorker::startLogcat()
@@ -192,7 +200,7 @@ void AndroidQmlPreviewWorker::startLogcat()
QString args = QString("logcat --pid=%1").arg(m_viewerPid);
if (!m_logcatStartTimeStamp.isEmpty())
args += QString(" -T '%1'").arg(m_logcatStartTimeStamp);
- CommandLine cmd(androidConfig().adbToolPath());
+ CommandLine cmd(AndroidConfig::adbToolPath());
cmd.setArguments(args);
m_logcatProcess.setCommand(cmd);
m_logcatProcess.setUseCtrlCStub(true);
@@ -220,7 +228,7 @@ AndroidQmlPreviewWorker::AndroidQmlPreviewWorker(RunControl *runControl)
m_rc(runControl)
{
connect(this, &RunWorker::started, this, &AndroidQmlPreviewWorker::startPidWatcher);
- connect(this, &RunWorker::stopped, &m_pidFutureWatcher, &QFutureWatcher<void>::cancel);
+ connect(this, &RunWorker::stopped, &m_pidRunner, &TaskTreeRunner::reset);
connect(this, &AndroidQmlPreviewWorker::previewPidChanged,
this, &AndroidQmlPreviewWorker::startLogcat);
@@ -230,12 +238,6 @@ AndroidQmlPreviewWorker::AndroidQmlPreviewWorker(RunControl *runControl)
});
}
-AndroidQmlPreviewWorker::~AndroidQmlPreviewWorker()
-{
- m_pidFutureWatcher.cancel();
- m_pidFutureWatcher.waitForFinished();
-}
-
void AndroidQmlPreviewWorker::start()
{
const SdkToolResult dateResult = runAdbCommand({"shell", "date", "+%s"});
@@ -254,7 +256,7 @@ void AndroidQmlPreviewWorker::start()
void AndroidQmlPreviewWorker::stop()
{
- if (!isPreviewRunning(m_viewerPid) || stopPreviewApp())
+ if (m_viewerPid <= 0 || stopPreviewApp())
appendMessage(Tr::tr("%1 has been stopped.").arg(apkInfo()->name), NormalMessageFormat);
m_viewerPid = -1;
reportStopped();
@@ -262,13 +264,12 @@ void AndroidQmlPreviewWorker::stop()
bool AndroidQmlPreviewWorker::ensureAvdIsRunning()
{
- AndroidAvdManager avdMananager;
QString devSN = AndroidManager::deviceSerialNumber(m_rc->target());
if (devSN.isEmpty())
devSN = m_serialNumber;
- if (!avdMananager.isAvdBooted(devSN)) {
+ if (!AndroidAvdManager::isAvdBooted(devSN)) {
const IDevice *dev = DeviceKitAspect::device(m_rc->target()->kit()).get();
if (!dev) {
appendMessage(Tr::tr("Selected device is invalid."), ErrorMessageFormat);
@@ -283,13 +284,13 @@ bool AndroidQmlPreviewWorker::ensureAvdIsRunning()
if (devInfoLocal.isValid()) {
if (dev->machineType() == IDevice::Emulator) {
appendMessage(Tr::tr("Launching AVD."), NormalMessageFormat);
- devInfoLocal.serialNumber = avdMananager.startAvd(devInfoLocal.avdName);
+ devInfoLocal.serialNumber = AndroidAvdManager::startAvd(devInfoLocal.avdName);
}
if (devInfoLocal.serialNumber.isEmpty()) {
appendMessage(Tr::tr("Could not start AVD."), ErrorMessageFormat);
} else {
m_serialNumber = devInfoLocal.serialNumber;
- m_avdAbis = androidConfig().getAbis(m_serialNumber);
+ m_avdAbis = AndroidConfig::getAbis(m_serialNumber);
}
return !devInfoLocal.serialNumber.isEmpty();
} else {
@@ -297,7 +298,7 @@ bool AndroidQmlPreviewWorker::ensureAvdIsRunning()
}
return false;
}
- m_avdAbis = androidConfig().getAbis(m_serialNumber);
+ m_avdAbis = AndroidConfig::getAbis(m_serialNumber);
return true;
}
diff --git a/src/plugins/android/androidqtversion.cpp b/src/plugins/android/androidqtversion.cpp
index eb9f180138..67232b0c9d 100644
--- a/src/plugins/android/androidqtversion.cpp
+++ b/src/plugins/android/androidqtversion.cpp
@@ -61,9 +61,9 @@ QString AndroidQtVersion::invalidReason() const
{
QString tmp = QtVersion::invalidReason();
if (tmp.isEmpty()) {
- if (androidConfig().ndkLocation(this).isEmpty())
+ if (AndroidConfig::ndkLocation(this).isEmpty())
return Tr::tr("NDK is not configured in Devices > Android.");
- if (androidConfig().sdkLocation().isEmpty())
+ if (AndroidConfig::sdkLocation().isEmpty())
return Tr::tr("SDK is not configured in Devices > Android.");
if (qtAbis().isEmpty())
return Tr::tr("Failed to detect the ABIs used by the Qt version. Check the settings in "
@@ -79,7 +79,7 @@ bool AndroidQtVersion::supportsMultipleQtAbis() const
Abis AndroidQtVersion::detectQtAbis() const
{
- const bool conf = androidConfig().sdkFullyConfigured();
+ const bool conf = AndroidConfig::sdkFullyConfigured();
return conf ? Utils::transform<Abis>(androidAbis(), &AndroidManager::androidAbi2Abi) : Abis();
}
@@ -87,18 +87,17 @@ void AndroidQtVersion::addToEnvironment(const Kit *k, Utils::Environment &env) c
{
QtVersion::addToEnvironment(k, env);
- const AndroidConfig &config = androidConfig();
// this env vars are used by qmake mkspecs to generate makefiles (check QTDIR/mkspecs/android-g++/qmake.conf for more info)
- env.set(QLatin1String("ANDROID_NDK_HOST"), config.toolchainHost(this));
- env.set(QLatin1String("ANDROID_NDK_ROOT"), config.ndkLocation(this).toUserOutput());
+ env.set(QLatin1String("ANDROID_NDK_HOST"), AndroidConfig::toolchainHost(this));
+ env.set(QLatin1String("ANDROID_NDK_ROOT"), AndroidConfig::ndkLocation(this).toUserOutput());
env.set(QLatin1String("ANDROID_NDK_PLATFORM"),
- config.bestNdkPlatformMatch(qMax(minimumNDK(), AndroidManager::minimumSDK(k)), this));
+ AndroidConfig::bestNdkPlatformMatch(qMax(minimumNDK(), AndroidManager::minimumSDK(k)), this));
}
void AndroidQtVersion::setupQmakeRunEnvironment(Utils::Environment &env) const
{
env.set(QLatin1String("ANDROID_NDK_ROOT"),
- androidConfig().ndkLocation(this).toUserOutput());
+ AndroidConfig::ndkLocation(this).toUserOutput());
}
QString AndroidQtVersion::description() const
diff --git a/src/plugins/android/androidrunner.cpp b/src/plugins/android/androidrunner.cpp
index 72b786fa73..260bef647f 100644
--- a/src/plugins/android/androidrunner.cpp
+++ b/src/plugins/android/androidrunner.cpp
@@ -167,28 +167,24 @@ void AndroidRunner::launchAVD()
AndroidManager::setDeviceSerialNumber(m_target, info.serialNumber);
emit androidDeviceInfoChanged(info);
if (info.isValid()) {
- AndroidAvdManager avdManager;
- if (!info.avdName.isEmpty() && avdManager.findAvd(info.avdName).isEmpty()) {
- bool launched = avdManager.startAvdAsync(info.avdName);
- m_launchedAVDName = launched ? info.avdName:"";
- } else {
+ if (!info.avdName.isEmpty() && AndroidAvdManager::findAvd(info.avdName).isEmpty())
+ m_launchedAVDName = AndroidAvdManager::startAvdAsync(info.avdName) ? info.avdName : "";
+ else
m_launchedAVDName.clear();
- }
}
}
void AndroidRunner::checkAVD()
{
- AndroidAvdManager avdManager;
- QString serialNumber = avdManager.findAvd(m_launchedAVDName);
+ const QString serialNumber = AndroidAvdManager::findAvd(m_launchedAVDName);
if (!serialNumber.isEmpty())
return; // try again on next timer hit
- if (avdManager.isAvdBooted(serialNumber)) {
+ if (AndroidAvdManager::isAvdBooted(serialNumber)) {
m_checkAVDTimer.stop();
AndroidManager::setDeviceSerialNumber(m_target, serialNumber);
emit asyncStart();
- } else if (!androidConfig().isConnected(serialNumber)) {
+ } else if (!AndroidConfig::isConnected(serialNumber)) {
// device was disconnected
m_checkAVDTimer.stop();
}
diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp
index 34688de9a1..7202c93f4c 100644
--- a/src/plugins/android/androidrunnerworker.cpp
+++ b/src/plugins/android/androidrunnerworker.cpp
@@ -20,20 +20,15 @@
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitaspect.h>
-#include <utils/async.h>
-#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <utils/qtcprocess.h>
-#include <utils/stringutils.h>
-#include <utils/temporaryfile.h>
#include <utils/url.h>
#include <QDate>
#include <QLoggingCategory>
-#include <QScopeGuard>
#include <QRegularExpression>
+#include <QScopeGuard>
#include <QTcpServer>
-#include <QThread>
#include <chrono>
@@ -57,17 +52,6 @@ static const QRegularExpression userIdPattern("u(\\d+)_a");
static const std::chrono::milliseconds s_jdbTimeout = 5s;
-static int APP_START_TIMEOUT = 45000;
-static bool isTimedOut(const chrono::high_resolution_clock::time_point &start,
- int msecs = APP_START_TIMEOUT)
-{
- bool timedOut = false;
- auto end = chrono::high_resolution_clock::now();
- if (chrono::duration_cast<chrono::milliseconds>(end-start).count() > msecs)
- timedOut = true;
- return timedOut;
-}
-
static qint64 extractPID(const QString &output, const QString &packageName)
{
qint64 pid = -1;
@@ -82,67 +66,6 @@ static qint64 extractPID(const QString &output, const QString &packageName)
return pid;
}
-static void findProcessPIDAndUser(QPromise<PidUserPair> &promise,
- const QStringList &selector,
- const QString &packageName,
- bool preNougat)
-{
- if (packageName.isEmpty())
- return;
-
- static const QString pidScript = "pidof -s '%1'";
- static const QString pidScriptPreNougat = QStringLiteral("for p in /proc/[0-9]*; "
- "do cat <$p/cmdline && echo :${p##*/}; done");
- QStringList args = {selector};
- FilePath adbPath = androidConfig().adbToolPath();
- args.append("shell");
- args.append(preNougat ? pidScriptPreNougat : pidScript.arg(packageName));
-
- qint64 processPID = -1;
- chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now();
- do {
- QThread::msleep(200);
- Process proc;
- proc.setCommand({adbPath, args});
- proc.runBlocking();
- const QString out = proc.allOutput();
- if (preNougat) {
- processPID = extractPID(out, packageName);
- } else {
- if (!out.isEmpty())
- processPID = out.trimmed().toLongLong();
- }
- } while ((processPID == -1 || processPID == 0) && !isTimedOut(start) && !promise.isCanceled());
-
- qCDebug(androidRunWorkerLog) << "PID found:" << processPID << ", PreNougat:" << preNougat;
-
- qint64 processUser = 0;
- if (processPID > 0 && !promise.isCanceled()) {
- args = {selector};
- args.append({"shell", "ps", "-o", "user", "-p"});
- args.append(QString::number(processPID));
- Process proc;
- proc.setCommand({adbPath, args});
- proc.runBlocking();
- const QString out = proc.allOutput();
- if (!out.isEmpty()) {
- QRegularExpressionMatch match;
- qsizetype matchPos = out.indexOf(userIdPattern, 0, &match);
- if (matchPos >= 0 && match.capturedLength(1) > 0) {
- bool ok = false;
- processUser = match.captured(1).toInt(&ok);
- if (!ok)
- processUser = 0;
- }
- }
- }
-
- qCDebug(androidRunWorkerLog) << "USER found:" << processUser;
-
- if (!promise.isCanceled())
- promise.addResult(PidUserPair(processPID, processUser));
-}
-
static QString gdbServerArch(const QString &androidAbi)
{
if (androidAbi == ProjectExplorer::Constants::ANDROID_ABI_ARM64_V8A)
@@ -170,11 +93,9 @@ static FilePath debugServer(bool useLldb, const Target *target)
QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(target->kit());
QString preferredAbi = AndroidManager::apkDevicePreferredAbi(target);
- const AndroidConfig &config = androidConfig();
-
if (useLldb) {
// Search suitable lldb-server binary.
- const FilePath prebuilt = config.ndkLocation(qtVersion) / "toolchains/llvm/prebuilt";
+ const FilePath prebuilt = AndroidConfig::ndkLocation(qtVersion) / "toolchains/llvm/prebuilt";
const QString abiNeedle = lldbServerArch2(preferredAbi);
// The new, built-in LLDB.
@@ -194,7 +115,7 @@ static FilePath debugServer(bool useLldb, const Target *target)
return lldbServer;
} else {
// Search suitable gdbserver binary.
- const FilePath path = config.ndkLocation(qtVersion)
+ const FilePath path = AndroidConfig::ndkLocation(qtVersion)
.pathAppended(QString("prebuilt/android-%1/gdbserver/gdbserver")
.arg(gdbServerArch(preferredAbi)));
if (path.exists())
@@ -251,14 +172,14 @@ AndroidRunnerWorker::AndroidRunnerWorker(RunWorker *runner, const QString &packa
if (const Store sd = runControl->settingsData(Constants::ANDROID_AM_START_ARGS);
!sd.values().isEmpty()) {
- QTC_CHECK(sd.first().type() == QVariant::String);
+ QTC_CHECK(sd.first().typeId() == QMetaType::QString);
const QString startArgs = sd.first().toString();
m_amStartExtraArgs = ProcessArgs::splitArgs(startArgs, OsTypeOtherUnix);
}
if (const Store sd = runControl->settingsData(Constants::ANDROID_PRESTARTSHELLCMDLIST);
!sd.values().isEmpty()) {
- QTC_CHECK(sd.first().type() == QVariant::String);
+ QTC_CHECK(sd.first().typeId() == QMetaType::QString);
const QStringList commands = sd.first().toString().split('\n', Qt::SkipEmptyParts);
for (const QString &shellCmd : commands)
m_beforeStartAdbCommands.append(QString("shell %1").arg(shellCmd));
@@ -266,7 +187,7 @@ AndroidRunnerWorker::AndroidRunnerWorker(RunWorker *runner, const QString &packa
if (const Store sd = runControl->settingsData(Constants::ANDROID_POSTFINISHSHELLCMDLIST);
!sd.values().isEmpty()) {
- QTC_CHECK(sd.first().type() == QVariant::String);
+ QTC_CHECK(sd.first().typeId() == QMetaType::QString);
const QStringList commands = sd.first().toString().split('\n', Qt::SkipEmptyParts);
for (const QString &shellCmd : commands)
m_afterFinishAdbCommands.append(QString("shell %1").arg(shellCmd));
@@ -288,9 +209,6 @@ AndroidRunnerWorker::~AndroidRunnerWorker()
{
if (m_processPID != -1)
forceStop();
-
- if (!m_pidFinder.isFinished())
- m_pidFinder.cancel();
}
bool AndroidRunnerWorker::runAdb(const QStringList &args, QString *stdOut,
@@ -367,12 +285,6 @@ bool AndroidRunnerWorker::packageFileExists(const QString &filePath)
return success && !output.trimmed().isEmpty();
}
-void AndroidRunnerWorker::adbKill(qint64 pid)
-{
- if (!runAdb({"shell", "run-as", m_packageName, "kill", "-9", QString::number(pid)}))
- runAdb({"shell", "kill", "-9", QString::number(pid)});
-}
-
QStringList AndroidRunnerWorker::selector() const
{
return AndroidDeviceInfo::adbSelector(m_deviceSerialNumber);
@@ -385,8 +297,11 @@ void AndroidRunnerWorker::forceStop()
// try killing it via kill -9
QString output;
runAdb({"shell", "pidof", m_packageName}, &output);
- if (m_processPID != -1 && output == QString::number(m_processPID))
- adbKill(m_processPID);
+ const QString pidString = QString::number(m_processPID);
+ if (m_processPID != -1 && output == pidString
+ && !runAdb({"shell", "run-as", m_packageName, "kill", "-9", pidString})) {
+ runAdb({"shell", "kill", "-9", pidString});
+ }
}
void AndroidRunnerWorker::logcatReadStandardError()
@@ -512,7 +427,7 @@ void Android::Internal::AndroidRunnerWorker::asyncStartLogcat()
}
const QStringList logcatArgs = selector() << "logcat" << timeArg;
- const FilePath adb = androidConfig().adbToolPath();
+ const FilePath adb = AndroidConfig::adbToolPath();
qCDebug(androidRunWorkerLog).noquote() << "Running logcat command (async):"
<< CommandLine(adb, logcatArgs).toUserOutput();
m_adbLogcatProcess->setCommand({adb, logcatArgs});
@@ -710,19 +625,70 @@ void AndroidRunnerWorker::asyncStart()
{
asyncStartHelper();
- m_pidFinder = Utils::onResultReady(Utils::asyncRun(findProcessPIDAndUser,
- selector(),
- m_packageName,
- m_isPreNougat),
- this,
- bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1));
+ using namespace Tasking;
+
+ const Storage<PidUserPair> pidStorage;
+ const LoopUntil iterator([pidStorage](int) { return pidStorage->first <= 0; });
+
+ const FilePath adbPath = AndroidConfig::adbToolPath();
+ const QStringList args = selector();
+
+ const auto onPidSetup = [adbPath, args, packageName = m_packageName,
+ isPreNougat = m_isPreNougat](Process &process) {
+ const QString pidScript = isPreNougat
+ ? QString("for p in /proc/[0-9]*; do cat <$p/cmdline && echo :${p##*/}; done")
+ : QString("pidof -s '%1'").arg(packageName);
+ process.setCommand({adbPath, args + QStringList{"shell", pidScript}});
+ };
+ const auto onPidDone = [pidStorage, packageName = m_packageName,
+ isPreNougat = m_isPreNougat](const Process &process) {
+ const QString out = process.allOutput();
+ if (isPreNougat)
+ pidStorage->first = extractPID(out, packageName);
+ else if (!out.isEmpty())
+ pidStorage->first = out.trimmed().toLongLong();
+ };
+
+ const auto onUserSetup = [pidStorage, adbPath, args](Process &process) {
+ process.setCommand({adbPath, args
+ + QStringList{"shell", "ps", "-o", "user", "-p", QString::number(pidStorage->first)}});
+ };
+ const auto onUserDone = [pidStorage](const Process &process) {
+ const QString out = process.allOutput();
+ if (out.isEmpty())
+ return DoneResult::Error;
+
+ QRegularExpressionMatch match;
+ qsizetype matchPos = out.indexOf(userIdPattern, 0, &match);
+ if (matchPos >= 0 && match.capturedLength(1) > 0) {
+ bool ok = false;
+ const qint64 processUser = match.captured(1).toInt(&ok);
+ if (ok) {
+ pidStorage->second = processUser;
+ return DoneResult::Success;
+ }
+ }
+ return DoneResult::Error;
+ };
+
+ const Group root {
+ pidStorage,
+ onGroupSetup([pidStorage] { *pidStorage = {-1, 0}; }),
+ Group {
+ iterator,
+ ProcessTask(onPidSetup, onPidDone, CallDoneIf::Success),
+ TimeoutTask([](std::chrono::milliseconds &timeout) { timeout = 200ms; })
+ }.withTimeout(45s),
+ ProcessTask(onUserSetup, onUserDone, CallDoneIf::Success),
+ onGroupDone([pidStorage, this] { onProcessIdChanged(*pidStorage); })
+ };
+
+ m_pidRunner.start(root);
}
void AndroidRunnerWorker::asyncStop()
{
- if (!m_pidFinder.isFinished())
- m_pidFinder.cancel();
-
+ m_pidRunner.reset();
if (m_processPID != -1)
forceStop();
@@ -742,7 +708,7 @@ void AndroidRunnerWorker::handleJdbWaiting()
}
m_afterFinishAdbCommands.push_back(removeForward.join(' '));
- const FilePath jdbPath = androidConfig().openJDKLocation()
+ const FilePath jdbPath = AndroidConfig::openJDKLocation()
.pathAppended("bin/jdb").withExecutableSuffix();
QStringList jdbArgs("-connect");
@@ -823,8 +789,6 @@ void AndroidRunnerWorker::removeForwardPort(const QString &port)
void AndroidRunnerWorker::onProcessIdChanged(const PidUserPair &pidUser)
{
- // Don't write to m_psProc from a different thread
- QTC_ASSERT(QThread::currentThread() == thread(), return);
qCDebug(androidRunWorkerLog) << "Process ID changed from:" << m_processPID
<< "to:" << pidUser.first;
m_processPID = pidUser.first;
diff --git a/src/plugins/android/androidrunnerworker.h b/src/plugins/android/androidrunnerworker.h
index ac995fe162..bba8d35104 100644
--- a/src/plugins/android/androidrunnerworker.h
+++ b/src/plugins/android/androidrunnerworker.h
@@ -6,12 +6,11 @@
#include <qmldebug/qmldebugcommandlinearguments.h>
+#include <solutions/tasking/tasktreerunner.h>
+
#include <utils/environment.h>
#include <utils/port.h>
-#include <QFuture>
-#include <utility>
-
namespace Utils {
class FilePath;
class Process;
@@ -35,33 +34,33 @@ public:
AndroidRunnerWorker(ProjectExplorer::RunWorker *runner, const QString &packageName);
~AndroidRunnerWorker() override;
+ void setAndroidDeviceInfo(const AndroidDeviceInfo &info);
+ void asyncStart();
+ void asyncStop();
+ void setIsPreNougat(bool isPreNougat) { m_isPreNougat = isPreNougat; }
+ void setIntentName(const QString &intentName) { m_intentName = intentName; }
+
+signals:
+ void remoteProcessStarted(Utils::Port debugServerPort, const QUrl &qmlServer, qint64 pid);
+ void remoteProcessFinished(const QString &errString = QString());
+
+ void remoteOutput(const QString &output);
+ void remoteErrorOutput(const QString &output);
+
+private:
bool runAdb(const QStringList &args, QString *stdOut = nullptr, QString *stdErr = nullptr,
const QByteArray &writeData = {});
- void adbKill(qint64 pid);
QStringList selector() const;
void forceStop();
void logcatReadStandardError();
void logcatReadStandardOutput();
void logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError);
- void setAndroidDeviceInfo(const AndroidDeviceInfo &info);
- void setIsPreNougat(bool isPreNougat) { m_isPreNougat = isPreNougat; }
- void setIntentName(const QString &intentName) { m_intentName = intentName; }
- void asyncStart();
- void asyncStop();
void handleJdbWaiting();
void handleJdbSettled();
void removeForwardPort(const QString &port);
-signals:
- void remoteProcessStarted(Utils::Port debugServerPort, const QUrl &qmlServer, qint64 pid);
- void remoteProcessFinished(const QString &errString = QString());
-
- void remoteOutput(const QString &output);
- void remoteErrorOutput(const QString &output);
-
-private:
void asyncStartHelper();
void startNativeDebugging();
bool startDebuggerServer(const QString &packageDir, const QString &debugServerFile, QString *errorStr = nullptr);
@@ -90,7 +89,7 @@ private:
std::unique_ptr<Utils::Process> m_psIsAlive;
QByteArray m_stdoutBuffer;
QByteArray m_stderrBuffer;
- QFuture<PidUserPair> m_pidFinder;
+ Tasking::TaskTreeRunner m_pidRunner;
bool m_useCppDebugger = false;
bool m_useLldb = false; // FIXME: Un-implemented currently.
QmlDebug::QmlDebugServicesPreset m_qmlDebugServices;
diff --git a/src/plugins/android/androidsdkdownloader.cpp b/src/plugins/android/androidsdkdownloader.cpp
index f3238b8c66..8f0993363e 100644
--- a/src/plugins/android/androidsdkdownloader.cpp
+++ b/src/plugins/android/androidsdkdownloader.cpp
@@ -8,14 +8,11 @@
#include <coreplugin/icore.h>
-#include <extensionsystem/pluginmanager.h>
-
#include <solutions/tasking/barrier.h>
#include <solutions/tasking/networkquery.h>
#include <utils/async.h>
#include <utils/filepath.h>
-#include <utils/futuresynchronizer.h>
#include <utils/networkaccessmanager.h>
#include <utils/unarchiver.h>
@@ -109,7 +106,7 @@ GroupItem downloadSdkRecipe()
Storage<StorageStruct> storage;
const auto onSetup = [] {
- if (androidConfig().sdkToolsUrl().isEmpty()) {
+ if (AndroidConfig::sdkToolsUrl().isEmpty()) {
logError(Tr::tr("The SDK Tools download URL is empty."));
return SetupResult::StopWithError;
}
@@ -117,7 +114,7 @@ GroupItem downloadSdkRecipe()
};
const auto onQuerySetup = [storage](NetworkQuery &query) {
- query.setRequest(QNetworkRequest(androidConfig().sdkToolsUrl()));
+ query.setRequest(QNetworkRequest(AndroidConfig::sdkToolsUrl()));
query.setNetworkAccessManager(NetworkAccessManager::instance());
NetworkQuery *queryPtr = &query;
QProgressDialog *progressDialog = storage->progressDialog.get();
@@ -169,8 +166,7 @@ GroupItem downloadSdkRecipe()
if (!storage->sdkFileName)
return SetupResult::StopWithError;
async.setConcurrentCallData(validateFileIntegrity, *storage->sdkFileName,
- androidConfig().getSdkToolsSha256());
- async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ AndroidConfig::getSdkToolsSha256());
storage->progressDialog->setRange(0, 0);
storage->progressDialog->setLabelText(Tr::tr("Verifying package integrity..."));
return SetupResult::Continue;
@@ -201,7 +197,7 @@ GroupItem downloadSdkRecipe()
logError(Tr::tr("Unarchiving error."));
return;
}
- androidConfig().setTemporarySdkToolsPath(
+ AndroidConfig::setTemporarySdkToolsPath(
storage->sdkFileName->parentDir().pathAppended(Constants::cmdlineToolsName));
};
const auto onCancelSetup = [storage] { return std::make_pair(storage->progressDialog.get(),
diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp
index d62cc9e50e..76b4b5db72 100644
--- a/src/plugins/android/androidsdkmanager.cpp
+++ b/src/plugins/android/androidsdkmanager.cpp
@@ -113,9 +113,9 @@ private:
OutputFormatter *m_formatter = nullptr;
};
-static QString sdkRootArg(const AndroidConfig &config)
+static QString sdkRootArg()
{
- return "--sdk_root=" + config.sdkLocation().toString();
+ return "--sdk_root=" + AndroidConfig::sdkLocation().toString();
}
const QRegularExpression &assertionRegExp()
@@ -172,9 +172,9 @@ static GroupItem licensesRecipe(const Storage<DialogStorage> &dialogStorage)
"respective licenses are not accepted.") + "\n\n",
LogMessageFormat);
process.setProcessMode(ProcessMode::Writer);
- process.setEnvironment(androidConfig().toolsEnvironment());
- process.setCommand(CommandLine(androidConfig().sdkManagerToolPath(),
- {"--licenses", sdkRootArg(androidConfig())}));
+ process.setEnvironment(AndroidConfig::toolsEnvironment());
+ process.setCommand(CommandLine(AndroidConfig::sdkManagerToolPath(),
+ {"--licenses", sdkRootArg()}));
process.setUseCtrlCStub(true);
Process *processPtr = &process;
@@ -227,9 +227,9 @@ static GroupItem licensesRecipe(const Storage<DialogStorage> &dialogStorage)
static void setupSdkProcess(const QStringList &args, Process *process,
QuestionProgressDialog *dialog, int current, int total)
{
- process->setEnvironment(androidConfig().toolsEnvironment());
- process->setCommand({androidConfig().sdkManagerToolPath(),
- args + androidConfig().sdkManagerToolArgs()});
+ process->setEnvironment(AndroidConfig::toolsEnvironment());
+ process->setCommand({AndroidConfig::sdkManagerToolPath(),
+ args + AndroidConfig::sdkManagerToolArgs()});
QObject::connect(process, &Process::readyReadStandardOutput, dialog,
[process, dialog, current, total] {
QTextCodec *codec = QTextCodec::codecForLocale();
@@ -269,7 +269,7 @@ static GroupItem installationRecipe(const Storage<DialogStorage> &dialogStorage,
const int total = change.count();
const LoopList uninstallIterator(change.toUninstall);
const auto onUninstallSetup = [dialogStorage, uninstallIterator, total](Process &process) {
- const QStringList args = {"--uninstall", *uninstallIterator, sdkRootArg(androidConfig())};
+ const QStringList args = {"--uninstall", *uninstallIterator, sdkRootArg()};
QuestionProgressDialog *dialog = dialogStorage->m_dialog.get();
setupSdkProcess(args, &process, dialog, uninstallIterator.iteration(), total);
dialog->appendMessage(Tr::tr("Uninstalling %1...").arg(*uninstallIterator) + '\n',
@@ -280,7 +280,7 @@ static GroupItem installationRecipe(const Storage<DialogStorage> &dialogStorage,
const LoopList installIterator(change.toInstall);
const int offset = change.toUninstall.count();
const auto onInstallSetup = [dialogStorage, installIterator, offset, total](Process &process) {
- const QStringList args = {*installIterator, sdkRootArg(androidConfig())};
+ const QStringList args = {*installIterator, sdkRootArg()};
QuestionProgressDialog *dialog = dialogStorage->m_dialog.get();
setupSdkProcess(args, &process, dialog, offset + installIterator.iteration(), total);
dialog->appendMessage(Tr::tr("Installing %1...").arg(*installIterator) + '\n',
@@ -310,7 +310,7 @@ static GroupItem installationRecipe(const Storage<DialogStorage> &dialogStorage,
static GroupItem updateRecipe(const Storage<DialogStorage> &dialogStorage)
{
const auto onUpdateSetup = [dialogStorage](Process &process) {
- const QStringList args = {"--update", sdkRootArg(androidConfig())};
+ const QStringList args = {"--update", sdkRootArg()};
QuestionProgressDialog *dialog = dialogStorage->m_dialog.get();
setupSdkProcess(args, &process, dialog, 0, 1);
dialog->appendMessage(Tr::tr("Updating installed packages....") + '\n', NormalMessageFormat);
@@ -369,7 +369,7 @@ const AndroidSdkPackageList &AndroidSdkManager::allSdkPackages()
QStringList AndroidSdkManager::notFoundEssentialSdkPackages()
{
- QStringList essentials = androidConfig().allEssentials();
+ QStringList essentials = AndroidConfig::allEssentials();
const AndroidSdkPackageList &packages = allSdkPackages();
for (AndroidSdkPackage *package : packages) {
essentials.removeOne(package->sdkStylePath());
@@ -381,7 +381,7 @@ QStringList AndroidSdkManager::notFoundEssentialSdkPackages()
QStringList AndroidSdkManager::missingEssentialSdkPackages()
{
- const QStringList essentials = androidConfig().allEssentials();
+ const QStringList essentials = AndroidConfig::allEssentials();
const AndroidSdkPackageList &packages = allSdkPackages();
QStringList missingPackages;
for (AndroidSdkPackage *package : packages) {
@@ -461,7 +461,7 @@ BuildToolsList AndroidSdkManager::filteredBuildTools(int minApiLevel,
void AndroidSdkManager::refreshPackages()
{
- if (androidConfig().sdkManagerToolPath() != m_d->lastSdkManagerPath)
+ if (AndroidConfig::sdkManagerToolPath() != m_d->lastSdkManagerPath)
reloadPackages();
}
@@ -479,14 +479,14 @@ bool AndroidSdkManager::packageListingSuccessful() const
Runs the \c sdkmanger tool with arguments \a args. Returns \c true if the command is
successfully executed. Output is copied into \a output. The function blocks the calling thread.
*/
-static bool sdkManagerCommand(const AndroidConfig &config, const QStringList &args, QString *output)
+static bool sdkManagerCommand(const QStringList &args, QString *output)
{
QStringList newArgs = args;
- newArgs.append(sdkRootArg(config));
+ newArgs.append(sdkRootArg());
Process proc;
- proc.setEnvironment(config.toolsEnvironment());
+ proc.setEnvironment(AndroidConfig::toolsEnvironment());
proc.setTimeOutMessageBoxEnabled(true);
- proc.setCommand({config.sdkManagerToolPath(), newArgs});
+ proc.setCommand({AndroidConfig::sdkManagerToolPath(), newArgs});
qCDebug(sdkManagerLog).noquote() << "Running SDK Manager command (sync):"
<< proc.commandLine().toUserOutput();
proc.runBlocking(60s, EventLoopMode::On);
@@ -516,10 +516,10 @@ void AndroidSdkManagerPrivate::reloadSdkPackages()
qDeleteAll(m_allPackages);
m_allPackages.clear();
- lastSdkManagerPath = androidConfig().sdkManagerToolPath();
+ lastSdkManagerPath = AndroidConfig::sdkManagerToolPath();
m_packageListingSuccessful = false;
- if (androidConfig().sdkToolsVersion().isNull()) {
+ if (AndroidConfig::sdkToolsVersion().isNull()) {
// Configuration has invalid sdk path or corrupt installation.
emit m_sdkManager.packageReloadFinished();
return;
@@ -527,8 +527,8 @@ void AndroidSdkManagerPrivate::reloadSdkPackages()
QString packageListing;
QStringList args({"--list", "--verbose"});
- args << androidConfig().sdkManagerToolArgs();
- m_packageListingSuccessful = sdkManagerCommand(androidConfig(), args, &packageListing);
+ args << AndroidConfig::sdkManagerToolArgs();
+ m_packageListingSuccessful = sdkManagerCommand(args, &packageListing);
if (m_packageListingSuccessful) {
SdkManagerOutputParser parser(m_allPackages);
parser.parsePackageListing(packageListing);
diff --git a/src/plugins/android/androidsdkmanagerdialog.cpp b/src/plugins/android/androidsdkmanagerdialog.cpp
index a2297bdac3..8ae1cfd77b 100644
--- a/src/plugins/android/androidsdkmanagerdialog.cpp
+++ b/src/plugins/android/androidsdkmanagerdialog.cpp
@@ -40,9 +40,9 @@ public:
m_argumentDetailsEdit = new QPlainTextEdit;
m_argumentDetailsEdit->setReadOnly(true);
- m_process.setEnvironment(androidConfig().toolsEnvironment());
- m_process.setCommand({androidConfig().sdkManagerToolPath(),
- {"--help", "--sdk_root=" + androidConfig().sdkLocation().toString()}});
+ m_process.setEnvironment(AndroidConfig::toolsEnvironment());
+ m_process.setCommand({AndroidConfig::sdkManagerToolPath(),
+ {"--help", "--sdk_root=" + AndroidConfig::sdkLocation().toString()}});
connect(&m_process, &Process::done, this, [this] {
const QString output = m_process.allOutput();
QString argumentDetails;
@@ -64,7 +64,7 @@ public:
connect(dialogButtons, &QDialogButtonBox::rejected, this, &OptionsDialog::reject);
m_argumentsEdit = new QLineEdit;
- m_argumentsEdit->setText(androidConfig().sdkManagerToolArgs().join(" "));
+ m_argumentsEdit->setText(AndroidConfig::sdkManagerToolArgs().join(" "));
using namespace Layouting;
@@ -235,8 +235,8 @@ AndroidSdkManagerDialog::AndroidSdkManagerDialog(AndroidSdkManager *sdkManager,
OptionsDialog dlg(m_sdkManager, this);
if (dlg.exec() == QDialog::Accepted) {
QStringList arguments = dlg.sdkManagerArguments();
- if (arguments != androidConfig().sdkManagerToolArgs()) {
- androidConfig().setSdkManagerToolArgs(arguments);
+ if (arguments != AndroidConfig::sdkManagerToolArgs()) {
+ AndroidConfig::setSdkManagerToolArgs(arguments);
m_sdkManager->reloadPackages();
}
}
@@ -244,19 +244,19 @@ AndroidSdkManagerDialog::AndroidSdkManagerDialog(AndroidSdkManager *sdkManager,
connect(obsoleteCheckBox, &QCheckBox::stateChanged, this, [this](int state) {
const QString obsoleteArg = "--include_obsolete";
- QStringList args = androidConfig().sdkManagerToolArgs();
+ QStringList args = AndroidConfig::sdkManagerToolArgs();
if (state == Qt::Checked && !args.contains(obsoleteArg)) {
args.append(obsoleteArg);
- androidConfig().setSdkManagerToolArgs(args);
+ AndroidConfig::setSdkManagerToolArgs(args);
} else if (state == Qt::Unchecked && args.contains(obsoleteArg)) {
args.removeAll(obsoleteArg);
- androidConfig().setSdkManagerToolArgs(args);
+ AndroidConfig::setSdkManagerToolArgs(args);
}
m_sdkManager->reloadPackages();
});
connect(channelCheckbox, &QComboBox::currentIndexChanged, this, [this](int index) {
- QStringList args = androidConfig().sdkManagerToolArgs();
+ QStringList args = AndroidConfig::sdkManagerToolArgs();
QString existingArg;
for (int i = 0; i < 4; ++i) {
const QString arg = "--channel=" + QString::number(i);
@@ -268,17 +268,17 @@ AndroidSdkManagerDialog::AndroidSdkManagerDialog(AndroidSdkManager *sdkManager,
if (index == 0 && !existingArg.isEmpty()) {
args.removeAll(existingArg);
- androidConfig().setSdkManagerToolArgs(args);
+ AndroidConfig::setSdkManagerToolArgs(args);
} else if (index > 0) {
// Add 1 to account for Stable (second item) being channel 0
const QString channelArg = "--channel=" + QString::number(index - 1);
if (existingArg != channelArg) {
if (!existingArg.isEmpty()) {
args.removeAll(existingArg);
- androidConfig().setSdkManagerToolArgs(args);
+ AndroidConfig::setSdkManagerToolArgs(args);
}
args.append(channelArg);
- androidConfig().setSdkManagerToolArgs(args);
+ AndroidConfig::setSdkManagerToolArgs(args);
}
}
m_sdkManager->reloadPackages();
diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp
index 785d0c5ac9..cdfdd5c029 100644
--- a/src/plugins/android/androidsettingswidget.cpp
+++ b/src/plugins/android/androidsettingswidget.cpp
@@ -314,7 +314,7 @@ AndroidSettingsWidget::AndroidSettingsWidget()
"and extracted to the selected path.\n"
"After the SDK Tools are properly set up, you are prompted to install any essential\n"
"packages required for Qt to build for Android.")
- .arg(androidConfig().sdkToolsUrl().toString()));
+ .arg(AndroidConfig::sdkToolsUrl().toString()));
auto sdkManagerToolButton = new QPushButton(Tr::tr("SDK Manager"));
@@ -380,22 +380,22 @@ AndroidSettingsWidget::AndroidSettingsWidget()
connect(m_openJdkLocationPathChooser, &PathChooser::rawPathChanged,
this, &AndroidSettingsWidget::validateJdk);
- if (androidConfig().openJDKLocation().isEmpty())
- androidConfig().setOpenJDKLocation(AndroidConfig::getJdkPath());
- m_openJdkLocationPathChooser->setFilePath(androidConfig().openJDKLocation());
+ if (AndroidConfig::openJDKLocation().isEmpty())
+ AndroidConfig::setOpenJDKLocation(AndroidConfig::getJdkPath());
+ m_openJdkLocationPathChooser->setFilePath(AndroidConfig::openJDKLocation());
m_openJdkLocationPathChooser->setPromptDialogTitle(Tr::tr("Select JDK Path"));
- if (androidConfig().sdkLocation().isEmpty())
- androidConfig().setSdkLocation(AndroidConfig::defaultSdkPath());
- m_sdkLocationPathChooser->setFilePath(androidConfig().sdkLocation());
+ if (AndroidConfig::sdkLocation().isEmpty())
+ AndroidConfig::setSdkLocation(AndroidConfig::defaultSdkPath());
+ m_sdkLocationPathChooser->setFilePath(AndroidConfig::sdkLocation());
m_sdkLocationPathChooser->setPromptDialogTitle(Tr::tr("Select Android SDK Folder"));
m_openSslPathChooser->setPromptDialogTitle(Tr::tr("Select OpenSSL Include Project File"));
- if (androidConfig().openSslLocation().isEmpty())
- androidConfig().setOpenSslLocation(androidConfig().sdkLocation() / ("android_openssl"));
- m_openSslPathChooser->setFilePath(androidConfig().openSslLocation());
+ if (AndroidConfig::openSslLocation().isEmpty())
+ AndroidConfig::setOpenSslLocation(AndroidConfig::sdkLocation() / ("android_openssl"));
+ m_openSslPathChooser->setFilePath(AndroidConfig::openSslLocation());
- m_createKitCheckBox->setChecked(androidConfig().automaticKitCreation());
+ m_createKitCheckBox->setChecked(AndroidConfig::automaticKitCreation());
downloadNdkToolButton->setIcon(downloadIcon);
@@ -435,7 +435,7 @@ AndroidSettingsWidget::AndroidSettingsWidget()
}
},
Group {
- title(Tr::tr("Android OpenSSL settings (Optional)")),
+ title(Tr::tr("Android OpenSSL Settings (Optional)")),
Grid {
Tr::tr("OpenSSL binaries location:"),
m_openSslPathChooser,
@@ -454,21 +454,21 @@ AndroidSettingsWidget::AndroidSettingsWidget()
connect(m_ndkListWidget, &QListWidget::currentTextChanged,
this, [this, removeCustomNdkButton](const QString &ndk) {
updateUI();
- removeCustomNdkButton->setEnabled(androidConfig().getCustomNdkList().contains(ndk));
+ removeCustomNdkButton->setEnabled(AndroidConfig::getCustomNdkList().contains(ndk));
});
connect(addCustomNdkButton, &QPushButton::clicked, this,
&AndroidSettingsWidget::addCustomNdkItem);
connect(removeCustomNdkButton, &QPushButton::clicked, this, [this] {
if (isDefaultNdkSelected())
- androidConfig().setDefaultNdk({});
- androidConfig().removeCustomNdk(m_ndkListWidget->currentItem()->text());
+ AndroidConfig::setDefaultNdk({});
+ AndroidConfig::removeCustomNdk(m_ndkListWidget->currentItem()->text());
m_ndkListWidget->takeItem(m_ndkListWidget->currentRow());
});
connect(m_makeDefaultNdkButton, &QPushButton::clicked, this, [this] {
const FilePath defaultNdk = isDefaultNdkSelected()
? FilePath()
: FilePath::fromUserInput(m_ndkListWidget->currentItem()->text());
- androidConfig().setDefaultNdk(defaultNdk);
+ AndroidConfig::setDefaultNdk(defaultNdk);
updateUI();
});
@@ -503,7 +503,7 @@ AndroidSettingsWidget::AndroidSettingsWidget()
if (result != Tasking::DoneWith::Success)
return;
// Make sure the sdk path is created before installing packages
- const FilePath sdkPath = androidConfig().sdkLocation();
+ const FilePath sdkPath = AndroidConfig::sdkLocation();
if (!sdkPath.createDir()) {
QMessageBox::warning(this, Android::Internal::dialogTitle(),
Tr::tr("Failed to create the SDK Tools path %1.")
@@ -518,7 +518,7 @@ AndroidSettingsWidget::AndroidSettingsWidget()
}, Qt::SingleShotConnection);
});
- setOnApply([] { AndroidConfigurations::setConfig(androidConfig()); });
+ setOnApply([] { AndroidConfigurations::applyConfig(); });
}
void AndroidSettingsWidget::showEvent(QShowEvent *event)
@@ -543,12 +543,12 @@ void AndroidSettingsWidget::updateNdkList()
ndk->installedLocation().toUserOutput()));
}
- const auto customNdks = androidConfig().getCustomNdkList();
+ const auto customNdks = AndroidConfig::getCustomNdkList();
for (const QString &ndk : customNdks) {
- if (androidConfig().isValidNdk(ndk)) {
+ if (AndroidConfig::isValidNdk(ndk)) {
m_ndkListWidget->addItem(new QListWidgetItem(Icons::UNLOCKED.icon(), ndk));
} else {
- androidConfig().removeCustomNdk(ndk);
+ AndroidConfig::removeCustomNdk(ndk);
}
}
@@ -563,8 +563,8 @@ void AndroidSettingsWidget::addCustomNdkItem()
.constFirst();
const QString ndkPath = QFileDialog::getExistingDirectory(this, Tr::tr("Select an NDK"), homePath);
- if (androidConfig().isValidNdk(ndkPath)) {
- androidConfig().addCustomNdk(ndkPath);
+ if (AndroidConfig::isValidNdk(ndkPath)) {
+ AndroidConfig::addCustomNdk(ndkPath);
if (m_ndkListWidget->findItems(ndkPath, Qt::MatchExactly).size() == 0) {
m_ndkListWidget->addItem(new QListWidgetItem(Icons::UNLOCKED.icon(), ndkPath));
}
@@ -582,9 +582,9 @@ void AndroidSettingsWidget::addCustomNdkItem()
bool AndroidSettingsWidget::isDefaultNdkSelected() const
{
- if (!androidConfig().defaultNdk().isEmpty()) {
+ if (!AndroidConfig::defaultNdk().isEmpty()) {
if (const QListWidgetItem *item = m_ndkListWidget->currentItem()) {
- return FilePath::fromUserInput(item->text()) == androidConfig().defaultNdk();
+ return FilePath::fromUserInput(item->text()) == AndroidConfig::defaultNdk();
}
}
return false;
@@ -592,8 +592,8 @@ bool AndroidSettingsWidget::isDefaultNdkSelected() const
void AndroidSettingsWidget::validateJdk()
{
- androidConfig().setOpenJDKLocation(m_openJdkLocationPathChooser->filePath());
- expected_str<void> test = testJavaC(androidConfig().openJDKLocation());
+ AndroidConfig::setOpenJDKLocation(m_openJdkLocationPathChooser->filePath());
+ expected_str<void> test = testJavaC(AndroidConfig::openJDKLocation());
m_androidSummary->setPointValid(JavaPathExistsAndWritableRow, test);
@@ -605,14 +605,14 @@ void AndroidSettingsWidget::validateJdk()
void AndroidSettingsWidget::validateOpenSsl()
{
- androidConfig().setOpenSslLocation(m_openSslPathChooser->filePath());
+ AndroidConfig::setOpenSslLocation(m_openSslPathChooser->filePath());
- m_openSslSummary->setPointValid(OpenSslPathExistsRow, androidConfig().openSslLocation().exists());
+ m_openSslSummary->setPointValid(OpenSslPathExistsRow, AndroidConfig::openSslLocation().exists());
- const bool priFileExists = androidConfig().openSslLocation().pathAppended("openssl.pri").exists();
+ const bool priFileExists = AndroidConfig::openSslLocation().pathAppended("openssl.pri").exists();
m_openSslSummary->setPointValid(OpenSslPriPathExists, priFileExists);
const bool cmakeListsExists
- = androidConfig().openSslLocation().pathAppended("CMakeLists.txt").exists();
+ = AndroidConfig::openSslLocation().pathAppended("CMakeLists.txt").exists();
m_openSslSummary->setPointValid(OpenSslCmakeListsPathExists, cmakeListsExists);
updateUI();
@@ -621,8 +621,8 @@ void AndroidSettingsWidget::validateOpenSsl()
void AndroidSettingsWidget::onSdkPathChanged()
{
const FilePath sdkPath = m_sdkLocationPathChooser->filePath().cleanPath();
- androidConfig().setSdkLocation(sdkPath);
- FilePath currentOpenSslPath = androidConfig().openSslLocation();
+ AndroidConfig::setSdkLocation(sdkPath);
+ FilePath currentOpenSslPath = AndroidConfig::openSslLocation();
if (currentOpenSslPath.isEmpty() || !currentOpenSslPath.exists())
currentOpenSslPath = sdkPath.pathAppended("android_openssl");
m_openSslPathChooser->setFilePath(currentOpenSslPath);
@@ -633,24 +633,24 @@ void AndroidSettingsWidget::onSdkPathChanged()
void AndroidSettingsWidget::validateSdk()
{
const FilePath sdkPath = m_sdkLocationPathChooser->filePath().cleanPath();
- androidConfig().setSdkLocation(sdkPath);
+ AndroidConfig::setSdkLocation(sdkPath);
- const FilePath path = androidConfig().sdkLocation();
+ const FilePath path = AndroidConfig::sdkLocation();
m_androidSummary->setPointValid(SdkPathExistsAndWritableRow,
path.exists() && path.isWritableDir());
m_androidSummary->setPointValid(SdkToolsInstalledRow,
- !androidConfig().sdkToolsVersion().isNull());
+ !AndroidConfig::sdkToolsVersion().isNull());
m_androidSummary->setPointValid(PlatformToolsInstalledRow,
- androidConfig().adbToolPath().exists());
+ AndroidConfig::adbToolPath().exists());
m_androidSummary->setPointValid(BuildToolsInstalledRow,
- !androidConfig().buildToolsVersion().isNull());
+ !AndroidConfig::buildToolsVersion().isNull());
m_androidSummary->setPointValid(SdkManagerSuccessfulRow, m_sdkManager.packageListingSuccessful());
// installedSdkPlatforms should not trigger a package reload as validate SDK is only called
// after AndroidSdkManager::packageReloadFinished.
m_androidSummary->setPointValid(PlatformSdkInstalledRow,
!m_sdkManager.installedSdkPlatforms().isEmpty());
m_androidSummary->setPointValid(AllEssentialsInstalledRow,
- androidConfig().allEssentialsInstalled(&m_sdkManager));
+ AndroidConfig::allEssentialsInstalled(&m_sdkManager));
const bool sdkToolsOk = m_androidSummary->rowsOk({SdkPathExistsAndWritableRow,
SdkToolsInstalledRow,
@@ -659,7 +659,7 @@ void AndroidSettingsWidget::validateSdk()
BuildToolsInstalledRow,
PlatformSdkInstalledRow,
AllEssentialsInstalledRow});
- androidConfig().setSdkFullyConfigured(sdkToolsOk && componentsOk);
+ AndroidConfig::setSdkFullyConfigured(sdkToolsOk && componentsOk);
if (sdkToolsOk && !componentsOk) {
const QStringList notFoundEssentials = m_sdkManager.notFoundEssentialSdkPackages();
if (!notFoundEssentials.isEmpty()) {
@@ -733,7 +733,8 @@ void AndroidSettingsWidget::downloadOpenSslRepo(const bool silent)
const QString openSslRepo("https://github.com/KDAB/android_openssl.git");
Process *gitCloner = new Process(this);
- CommandLine gitCloneCommand("git", {"clone", "--depth=1", openSslRepo, openSslPath.toString()});
+ const CommandLine gitCloneCommand("git", {"clone", "--depth=1", openSslRepo,
+ openSslPath.toString()});
gitCloner->setCommand(gitCloneCommand);
qCDebug(androidsettingswidget) << "Cloning OpenSSL repo: " << gitCloneCommand.toUserOutput();
@@ -782,7 +783,7 @@ void AndroidSettingsWidget::downloadOpenSslRepo(const bool silent)
void AndroidSettingsWidget::createKitToggled()
{
- androidConfig().setAutomaticKitCreation(m_createKitCheckBox->isChecked());
+ AndroidConfig::setAutomaticKitCreation(m_createKitCheckBox->isChecked());
}
void AndroidSettingsWidget::updateUI()
@@ -793,8 +794,8 @@ void AndroidSettingsWidget::updateUI()
const QListWidgetItem *currentItem = m_ndkListWidget->currentItem();
const FilePath currentNdk = FilePath::fromUserInput(currentItem ? currentItem->text() : "");
const QString infoText = Tr::tr("(SDK Version: %1, NDK Version: %2)")
- .arg(androidConfig().sdkToolsVersion().toString())
- .arg(currentNdk.isEmpty() ? "" : androidConfig().ndkVersion(currentNdk).toString());
+ .arg(AndroidConfig::sdkToolsVersion().toString())
+ .arg(currentNdk.isEmpty() ? "" : AndroidConfig::ndkVersion(currentNdk).toString());
m_androidSummary->setInfoText(androidSetupOk ? infoText : "");
m_androidSummary->setSetupOk(androidSetupOk);
@@ -808,7 +809,7 @@ void AndroidSettingsWidget::updateUI()
for (int row = 0; row < m_ndkListWidget->count(); ++row) {
QListWidgetItem *item = m_ndkListWidget->item(row);
const bool isDefaultNdk =
- FilePath::fromUserInput(item->text()) == androidConfig().defaultNdk();
+ FilePath::fromUserInput(item->text()) == AndroidConfig::defaultNdk();
item->setFont(isDefaultNdk ? markedFont : font);
}
}
@@ -820,7 +821,7 @@ void AndroidSettingsWidget::updateUI()
void AndroidSettingsWidget::downloadSdk()
{
- if (androidConfig().sdkToolsOk()) {
+ if (AndroidConfig::sdkToolsOk()) {
QMessageBox::warning(this, Android::Internal::dialogTitle(),
Tr::tr("The selected path already has a valid SDK Tools package."));
validateSdk();
diff --git a/src/plugins/android/androidsignaloperation.cpp b/src/plugins/android/androidsignaloperation.cpp
index 5016b2573e..9e92e860b4 100644
--- a/src/plugins/android/androidsignaloperation.cpp
+++ b/src/plugins/android/androidsignaloperation.cpp
@@ -13,7 +13,7 @@ namespace Android {
namespace Internal {
AndroidSignalOperation::AndroidSignalOperation()
- : m_adbPath(androidConfig().adbToolPath())
+ : m_adbPath(AndroidConfig::adbToolPath())
, m_timeout(new QTimer(this))
{
m_timeout->setInterval(5000);
diff --git a/src/plugins/android/androidtoolchain.cpp b/src/plugins/android/androidtoolchain.cpp
index a83a99a4d8..e430249bdb 100644
--- a/src/plugins/android/androidtoolchain.cpp
+++ b/src/plugins/android/androidtoolchain.cpp
@@ -77,7 +77,7 @@ bool AndroidToolchain::isValid() const
}
const bool isChildofNdk = compilerCommand().isChildOf(m_ndkLocation);
- const bool isChildofSdk = compilerCommand().isChildOf(androidConfig().sdkLocation());
+ const bool isChildofSdk = compilerCommand().isChildOf(AndroidConfig::sdkLocation());
return GccToolchain::isValid() && typeId() == Constants::ANDROID_TOOLCHAIN_TYPEID
&& targetAbi().isValid() && (isChildofNdk || isChildofSdk)
@@ -86,9 +86,8 @@ bool AndroidToolchain::isValid() const
void AndroidToolchain::addToEnvironment(Environment &env) const
{
- const AndroidConfig &config = androidConfig();
- env.set(QLatin1String("ANDROID_NDK_HOST"), config.toolchainHostFromNdk(m_ndkLocation));
- const FilePath javaHome = config.openJDKLocation();
+ env.set(QLatin1String("ANDROID_NDK_HOST"), AndroidConfig::toolchainHostFromNdk(m_ndkLocation));
+ const FilePath javaHome = AndroidConfig::openJDKLocation();
if (javaHome.exists()) {
env.set(Constants::JAVA_HOME_ENV_VAR, javaHome.toUserOutput());
const FilePath javaBin = javaHome.pathAppended("bin");
@@ -97,8 +96,8 @@ void AndroidToolchain::addToEnvironment(Environment &env) const
if (!currentJavaFilePath.isChildOf(javaBin))
env.prependOrSetPath(javaBin);
}
- env.set(QLatin1String("ANDROID_HOME"), config.sdkLocation().toUserOutput());
- env.set(QLatin1String("ANDROID_SDK_ROOT"), config.sdkLocation().toUserOutput());
+ env.set(QLatin1String("ANDROID_HOME"), AndroidConfig::sdkLocation().toUserOutput());
+ env.set(QLatin1String("ANDROID_SDK_ROOT"), AndroidConfig::sdkLocation().toUserOutput());
}
void AndroidToolchain::fromMap(const Store &data)
@@ -118,7 +117,7 @@ QStringList AndroidToolchain::suggestedMkspecList() const
FilePath AndroidToolchain::makeCommand(const Environment &env) const
{
Q_UNUSED(env)
- FilePath makePath = androidConfig().makePathFromNdk(m_ndkLocation);
+ FilePath makePath = AndroidConfig::makePathFromNdk(m_ndkLocation);
return makePath.exists() ? makePath : FilePath("make");
}
@@ -147,7 +146,7 @@ static FilePaths uniqueNdksForCurrentQtVersions()
FilePaths uniqueNdks;
for (const QtSupport::QtVersion *version : androidQtVersions) {
- FilePath ndk = androidConfig().ndkLocation(version);
+ FilePath ndk = AndroidConfig::ndkLocation(version);
if (!uniqueNdks.contains(ndk))
uniqueNdks.append(ndk);
}
@@ -161,15 +160,13 @@ ToolchainList autodetectToolchainsFromNdks(
const bool isCustom)
{
QList<Toolchain *> result;
- const AndroidConfig config = androidConfig();
-
const Id LanguageIds[] {
ProjectExplorer::Constants::CXX_LANGUAGE_ID,
ProjectExplorer::Constants::C_LANGUAGE_ID
};
for (const FilePath &ndkLocation : ndkLocations) {
- FilePath clangPath = config.clangPathFromNdk(ndkLocation);
+ const FilePath clangPath = AndroidConfig::clangPathFromNdk(ndkLocation);
if (!clangPath.exists()) {
qCDebug(androidTCLog) << "Clang toolchains detection fails. Can not find Clang"
<< clangPath;
@@ -193,11 +190,11 @@ ToolchainList autodetectToolchainsFromNdks(
const QString target = targetItr.key();
Toolchain *tc = findToolchain(compilerCommand, lang, target, alreadyKnown);
- QLatin1String customStr = isCustom ? QLatin1String("Custom ") : QLatin1String();
+ const QString customStr = isCustom ? "Custom " : QString();
const QString displayName(customStr + QString("Android Clang (%1, %2, NDK %3)")
- .arg(ToolchainManager::displayNameOfLanguageId(lang),
- AndroidConfig::displayName(abi),
- config.ndkVersion(ndkLocation).toString()));
+ .arg(ToolchainManager::displayNameOfLanguageId(lang),
+ AndroidConfig::displayName(abi),
+ AndroidConfig::ndkVersion(ndkLocation).toString()));
if (tc) {
// make sure to update the toolchain with current name format
if (tc->displayName() != displayName)
diff --git a/src/plugins/android/avddialog.cpp b/src/plugins/android/avddialog.cpp
index 2e439c339c..a6c08ffb05 100644
--- a/src/plugins/android/avddialog.cpp
+++ b/src/plugins/android/avddialog.cpp
@@ -3,9 +3,7 @@
#include "avddialog.h"
#include "androidtr.h"
-#include "androidavdmanager.h"
#include "androidconfigurations.h"
-#include "androidconstants.h"
#include "androiddevice.h"
#include "androidsdkmanager.h"
@@ -131,53 +129,18 @@ int AvdDialog::exec()
return QDialog::Rejected;
}
- CommandLine cmd(androidConfig().avdManagerToolPath(), {"create", "avd", "-n", name()});
- cmd.addArgs({"-k", si->sdkStylePath()});
- if (sdcardSize() > 0)
- cmd.addArgs({"-c", QString("%1M").arg(sdcardSize())});
-
- const QString deviceDef = deviceDefinition();
- if (!deviceDef.isEmpty() && deviceDef != "Custom")
- cmd.addArgs({"-d", deviceDef});
-
- if (m_overwriteCheckBox->isChecked())
- cmd.addArg("-f");
-
- Process process;
- process.setProcessMode(ProcessMode::Writer);
- process.setEnvironment(androidConfig().toolsEnvironment());
- process.setCommand(cmd);
- process.setWriteData("yes\n"); // yes to "Do you wish to create a custom hardware profile"
-
- QByteArray buffer;
- QObject::connect(&process, &Process::readyReadStandardOutput, &process, [&process, &buffer] {
- // This interaction is needed only if there is no "-d" arg for the avdmanager command.
- buffer += process.readAllRawStandardOutput();
- if (buffer.endsWith(QByteArray("]:"))) {
- // truncate to last line
- const int index = buffer.lastIndexOf('\n');
- if (index != -1)
- buffer = buffer.mid(index);
- if (buffer.contains("hw.gpu.enabled"))
- process.write("yes\n");
- else
- process.write("\n");
- buffer.clear();
- }
- });
-
- using namespace std::chrono_literals;
- process.runBlocking(10s, EventLoopMode::On);
- if (process.result() != ProcessResult::FinishedWithSuccess) {
+ const CreateAvdInfo avdInfo{si->sdkStylePath(), si->apiLevel(), name(), abi(),
+ deviceDefinition(), sdcardSize()};
+ const auto result = AndroidDeviceManager::instance()->createAvd(
+ avdInfo, m_overwriteCheckBox->isChecked());
+ if (!result) {
QMessageBox::warning(Core::ICore::dialogParent(), Tr::tr("Create new AVD"),
- process.exitMessage());
+ result.error());
return QDialog::Rejected;
}
- m_createdAvdInfo = {si->sdkStylePath(), si->apiLevel(), name(), abi(), deviceDef,
- sdcardSize()};
- AndroidDeviceManager::instance()->updateAvdsList();
+ m_createdAvdInfo = avdInfo;
+ AndroidDeviceManager::instance()->updateAvdList();
}
-
return execResult;
}
@@ -186,22 +149,9 @@ bool AvdDialog::isValid() const
return !name().isEmpty() && systemImage() && systemImage()->isValid() && !abi().isEmpty();
}
-IDevice::Ptr AvdDialog::device() const
+CreateAvdInfo AvdDialog::avdInfo() const
{
- if (m_createdAvdInfo.apiLevel < 0) {
- qCWarning(avdDialogLog) << "System image of the created AVD is nullptr";
- return IDevice::Ptr();
- }
- AndroidDevice *dev = new AndroidDevice;
- const Utils::Id deviceId = AndroidDevice::idFromAvdInfo(m_createdAvdInfo);
- dev->setupId(IDevice::AutoDetected, deviceId);
- dev->setMachineType(IDevice::Emulator);
- dev->settings()->displayName.setValue(m_createdAvdInfo.name);
- dev->setDeviceState(IDevice::DeviceConnected);
- dev->setExtraData(Constants::AndroidAvdName, m_createdAvdInfo.name);
- dev->setExtraData(Constants::AndroidCpuAbi, {m_createdAvdInfo.abi});
- dev->setExtraData(Constants::AndroidSdk, m_createdAvdInfo.apiLevel);
- return IDevice::Ptr(dev);
+ return m_createdAvdInfo;
}
AvdDialog::DeviceType AvdDialog::tagToDeviceType(const QString &type_tag)
@@ -214,17 +164,32 @@ AvdDialog::DeviceType AvdDialog::tagToDeviceType(const QString &type_tag)
return AvdDialog::Automotive;
else if (type_tag.contains("android-desktop"))
return AvdDialog::Desktop;
- else
- return AvdDialog::PhoneOrTablet;
+ return AvdDialog::PhoneOrTablet;
+}
+
+static bool avdManagerCommand(const QStringList &args, QString *output)
+{
+ CommandLine cmd(AndroidConfig::avdManagerToolPath(), args);
+ Process proc;
+ proc.setEnvironment(AndroidConfig::toolsEnvironment());
+ qCDebug(avdDialogLog).noquote() << "Running AVD Manager command:" << cmd.toUserOutput();
+ proc.setCommand(cmd);
+ proc.runBlocking();
+ if (proc.result() == ProcessResult::FinishedWithSuccess) {
+ if (output)
+ *output = proc.allOutput();
+ return true;
+ }
+ return false;
}
void AvdDialog::parseDeviceDefinitionsList()
{
QString output;
- if (!AndroidAvdManager::avdManagerCommand({"list", "device"}, &output)) {
+ if (!avdManagerCommand({"list", "device"}, &output)) {
qCDebug(avdDialogLog) << "Avd list command failed" << output
- << androidConfig().sdkToolsVersion();
+ << AndroidConfig::sdkToolsVersion();
return;
}
diff --git a/src/plugins/android/avddialog.h b/src/plugins/android/avddialog.h
index bdcddc5243..2e92da15a0 100644
--- a/src/plugins/android/avddialog.h
+++ b/src/plugins/android/avddialog.h
@@ -20,7 +20,6 @@ QT_END_NAMESPACE
namespace Utils { class InfoLabel; }
namespace Android {
-class AndroidConfig;
class SdkPlatform;
namespace Internal {
@@ -35,7 +34,7 @@ public:
enum DeviceType { Phone, Tablet, Automotive, TV, Wear, Desktop, PhoneOrTablet };
- ProjectExplorer::IDevice::Ptr device() const;
+ CreateAvdInfo avdInfo() const;
const SystemImage *systemImage() const;
QString name() const;
diff --git a/src/plugins/android/avdmanageroutputparser.cpp b/src/plugins/android/avdmanageroutputparser.cpp
index 2727680d08..7a91b99f19 100644
--- a/src/plugins/android/avdmanageroutputparser.cpp
+++ b/src/plugins/android/avdmanageroutputparser.cpp
@@ -86,10 +86,10 @@ static std::optional<AndroidDeviceInfo> parseAvd(const QStringList &deviceInfo)
return {};
}
-AndroidDeviceInfoList parseAvdList(const QString &output, Utils::FilePaths *avdErrorPaths)
+ParsedAvdList parseAvdList(const QString &output)
{
- QTC_CHECK(avdErrorPaths);
AndroidDeviceInfoList avdList;
+ Utils::FilePaths errorPaths;
QStringList avdInfo;
using ErrorPath = Utils::FilePath;
using AvdResult = std::variant<std::monostate, AndroidDeviceInfo, ErrorPath>;
@@ -120,14 +120,14 @@ AndroidDeviceInfoList parseAvdList(const QString &output, Utils::FilePaths *avdE
if (auto info = std::get_if<AndroidDeviceInfo>(&result))
avdList << *info;
else if (auto errorPath = std::get_if<ErrorPath>(&result))
- *avdErrorPaths << *errorPath;
+ errorPaths << *errorPath;
avdInfo.clear();
} else {
avdInfo << line;
}
}
- return Utils::sorted(std::move(avdList));
+ return {Utils::sorted(std::move(avdList)), errorPaths};
}
int platformNameToApiLevel(const QString &platformName)
diff --git a/src/plugins/android/avdmanageroutputparser.h b/src/plugins/android/avdmanageroutputparser.h
index b3a74f8fc8..81cbe64eaa 100644
--- a/src/plugins/android/avdmanageroutputparser.h
+++ b/src/plugins/android/avdmanageroutputparser.h
@@ -10,7 +10,13 @@ namespace Internal {
const char avdManufacturerError[] = "no longer exists as a device";
-AndroidDeviceInfoList parseAvdList(const QString &output, Utils::FilePaths *avdErrorPaths);
+struct ParsedAvdList
+{
+ AndroidDeviceInfoList avdList;
+ Utils::FilePaths errorPaths;
+};
+
+ParsedAvdList parseAvdList(const QString &output);
int platformNameToApiLevel(const QString &platformName);
QString convertNameToExtension(const QString &name);
diff --git a/src/plugins/android/javalanguageserver.cpp b/src/plugins/android/javalanguageserver.cpp
index 671786756e..90d536dcae 100644
--- a/src/plugins/android/javalanguageserver.cpp
+++ b/src/plugins/android/javalanguageserver.cpp
@@ -305,7 +305,7 @@ void JLSClient::updateProjectFiles()
const QStringList classPaths = node->data(Constants::AndroidClassPaths).toStringList();
- const FilePath &sdkLocation = androidConfig().sdkLocation();
+ const FilePath &sdkLocation = AndroidConfig::sdkLocation();
const QString &targetSDK = AndroidManager::buildTargetSDK(m_currentTarget);
const FilePath androidJar = sdkLocation / QString("platforms/%2/android.jar")
.arg(targetSDK);
diff --git a/src/plugins/luatemplates/LuaTemplates.json.in b/src/plugins/appstatisticsmonitor/AppStatisticsMonitor.json.in
index 400630aa93..58b68ede58 100644
--- a/src/plugins/luatemplates/LuaTemplates.json.in
+++ b/src/plugins/appstatisticsmonitor/AppStatisticsMonitor.json.in
@@ -1,9 +1,8 @@
{
- "Name" : "LuaTemplates",
+ "Name" : "AppStatisticsMonitor",
"Version" : "${IDE_VERSION}",
- "DisabledByDefault" : true,
- "SoftLoadable" : true,
"CompatVersion" : "${IDE_VERSION_COMPAT}",
+ "Experimental" : true,
"Vendor" : "The Qt Company Ltd",
"Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd",
"License" : [ "Commercial Usage",
@@ -14,8 +13,7 @@
"",
"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" : "Scripting",
- "Description" : "Allows writing template wizards using lua",
- "Url" : "http://www.qt.io",
+ "Description" : "AppStatisticsMonitor, a plugin designed to enhance your Qt Creator experience. With its seamless integration, this plugin adds a dedicated tab to your Navigation Widget, providing insightful visualizations of CPU and Memory consumption for running applications within Qt Creator. Simply select the desired application from the combobox to monitor its performance in real-time.",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/appstatisticsmonitor/CMakeLists.txt b/src/plugins/appstatisticsmonitor/CMakeLists.txt
new file mode 100644
index 0000000000..891da1f812
--- /dev/null
+++ b/src/plugins/appstatisticsmonitor/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_qtc_plugin(AppStatisticsMonitor
+ SKIP_TRANSLATION
+ PLUGIN_DEPENDS Core ProjectExplorer
+ SOURCES
+ appstatisticsmonitorplugin.cpp
+ chart.cpp chart.h
+ manager.cpp manager.h
+ idataprovider.h idataprovider.cpp
+)
+
diff --git a/src/plugins/appstatisticsmonitor/appstatisticsmonitor.qbs b/src/plugins/appstatisticsmonitor/appstatisticsmonitor.qbs
new file mode 100644
index 0000000000..3905a41a0a
--- /dev/null
+++ b/src/plugins/appstatisticsmonitor/appstatisticsmonitor.qbs
@@ -0,0 +1,17 @@
+QtcPlugin {
+ name: "AppStatisticsMonitor"
+
+ Depends { name: "Core" }
+ Depends { name: "ProjectExplorer" }
+
+ files: [
+ "appstatisticsmonitorplugin.cpp",
+ "appstatisticsmonitortr.h",
+ "chart.h",
+ "chart.cpp",
+ "idataprovider.h",
+ "idataprovider.cpp",
+ "manager.h",
+ "manager.cpp",
+ ]
+}
diff --git a/src/plugins/appstatisticsmonitor/appstatisticsmonitorplugin.cpp b/src/plugins/appstatisticsmonitor/appstatisticsmonitorplugin.cpp
new file mode 100644
index 0000000000..e91822e110
--- /dev/null
+++ b/src/plugins/appstatisticsmonitor/appstatisticsmonitorplugin.cpp
@@ -0,0 +1,32 @@
+// 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 "manager.h"
+
+#include <extensionsystem/iplugin.h>
+
+#include <QString>
+
+namespace AppStatisticsMonitor::Internal {
+
+class AppStatisticsMonitorPlugin final : public ExtensionSystem::IPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "AppStatisticsMonitor.json")
+
+private:
+ void initialize() final;
+ std::unique_ptr<AppStatisticsMonitorManager> m_appstatisticsmonitorManager;
+ std::unique_ptr<AppStatisticsMonitorViewFactory> m_appstatisticsmonitorViewFactory;
+};
+
+void AppStatisticsMonitorPlugin::initialize()
+{
+ m_appstatisticsmonitorManager = std::make_unique<AppStatisticsMonitorManager>();
+ m_appstatisticsmonitorViewFactory = std::make_unique<AppStatisticsMonitorViewFactory>(
+ m_appstatisticsmonitorManager.get());
+}
+
+} // namespace AppStatisticsMonitor::Internal
+
+#include "appstatisticsmonitorplugin.moc"
diff --git a/src/plugins/appstatisticsmonitor/appstatisticsmonitortr.h b/src/plugins/appstatisticsmonitor/appstatisticsmonitortr.h
new file mode 100644
index 0000000000..fad1692e07
--- /dev/null
+++ b/src/plugins/appstatisticsmonitor/appstatisticsmonitortr.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 <QCoreApplication>
+
+namespace AppStatisticsMonitor {
+
+struct Tr
+{
+ Q_DECLARE_TR_FUNCTIONS(AppStatisticsMonitor)
+};
+
+} // namespace AppStatisticsMonitor
diff --git a/src/plugins/appstatisticsmonitor/chart.cpp b/src/plugins/appstatisticsmonitor/chart.cpp
new file mode 100644
index 0000000000..2abf6d1ceb
--- /dev/null
+++ b/src/plugins/appstatisticsmonitor/chart.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 "chart.h"
+
+#include <utils/theme/theme.h>
+
+#include <QPaintEvent>
+#include <QPainter>
+#include <QPen>
+#include <QPointF>
+#include <QString>
+
+namespace AppStatisticsMonitor::Internal {
+
+static const int padding = 40;
+static const int numPadding = 10;
+static const QRectF dataRangeDefault = QRectF(0, 0, 5, 1);
+
+Chart::Chart(const QString &name, QWidget *parent)
+ : QWidget(parent)
+ , m_name(name)
+{
+ setMinimumHeight(200);
+ setMinimumWidth(400);
+}
+
+void Chart::addNewPoint(const QPointF &point)
+{
+ m_points.append(point);
+ update();
+}
+
+void Chart::loadNewProcessData(QList<double> data)
+{
+ clear();
+ for (long i = 0; i < data.size(); ++i) {
+ m_points.append(QPointF(i + 1, data[i]));
+ }
+ update();
+}
+
+double Chart::lastPointX() const
+{
+ if (m_points.isEmpty())
+ return 0;
+ return m_points.last().x();
+}
+
+void Chart::clear()
+{
+ m_points.clear();
+ addNewPoint({0, 0});
+ update();
+}
+
+void Chart::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event);
+ QPainter painter(this);
+
+ painter.fillRect(rect(), Utils::creatorColor(Utils::Theme::Token_Background_Default));
+
+ // add the name of the chart in the middle of the widget width and on the top
+ painter.drawText(
+ rect(),
+ Qt::AlignHCenter | Qt::AlignTop,
+ m_name + QString::number(m_points.last().y(), 'g', 4) + "%");
+
+ const QRectF dataRange = calculateDataRange();
+ updateScalingFactors(dataRange);
+
+ for (double x = dataRange.left(); x <= dataRange.right(); x += m_xGridStep) {
+ double xPos = padding + (x - dataRange.left()) * m_xScale;
+ if (xPos < padding || xPos > width() - padding)
+ continue;
+ painter.setPen(Utils::creatorColor(Utils::Theme::Token_Foreground_Default));
+ painter.drawLine(xPos, padding, xPos, height() - padding);
+
+ painter.setPen(Utils::creatorColor(Utils::Theme::Token_Text_Muted));
+ painter.drawText(xPos, height() - numPadding, QString::number(x));
+ }
+
+ for (double y = dataRange.top(); y <= dataRange.bottom(); y += m_yGridStep) {
+ double yPos = height() - padding - (y - (int) dataRange.top()) * m_yScale;
+ if (yPos < padding || yPos > height() - padding)
+ continue;
+
+ painter.setPen(Utils::creatorColor(Utils::Theme::Token_Foreground_Default));
+ painter.drawLine(padding, yPos, width() - padding, yPos);
+
+ painter.setPen(Utils::creatorColor(Utils::Theme::Token_Text_Muted));
+ painter.drawText(numPadding, yPos, QString::number(y));
+ }
+
+ painter.setPen(Utils::creatorColor(Utils::Theme::Token_Foreground_Default));
+ painter.drawLine(padding, height() - padding, width() - padding, height() - padding); // X axis
+ painter.drawLine(padding, height() - padding, padding, padding); // Y axis
+
+ QPen pen(Utils::creatorColor(Utils::Theme::Token_Accent_Default));
+ pen.setWidth(2);
+ painter.setPen(pen);
+ painter.setRenderHint(QPainter::Antialiasing);
+ for (int i = 1; i < m_points.size(); ++i) {
+ QPointF startPoint(
+ padding + (m_points[i - 1].x() - dataRange.left()) * m_xScale,
+ height() - padding - (m_points[i - 1].y() - dataRange.top()) * m_yScale);
+ QPointF endPoint(
+ padding + (m_points[i].x() - dataRange.left()) * m_xScale,
+ height() - padding - (m_points[i].y() - dataRange.top()) * m_yScale);
+ painter.drawLine(startPoint, endPoint);
+ }
+}
+
+void Chart::addPoint()
+{
+ for (int i = 0; i < 10; ++i) {
+ double x = m_points.size();
+ double y = sin(x) + 10 * cos(x / 10) + 10;
+ m_points.append(QPointF(x, y));
+ }
+ update();
+}
+
+QRectF Chart::calculateDataRange() const
+{
+ QRectF dataRange = QRectF(0, 0, 0, 0);
+
+ if (m_points.isEmpty())
+ return dataRange;
+
+ for (const QPointF &point : m_points) {
+ dataRange.setLeft(qMin(dataRange.left(), point.x()));
+ dataRange.setRight(qMax(dataRange.right(), point.x()));
+
+ dataRange.setBottom(qMin(dataRange.bottom(), point.y()));
+ dataRange.setTop(qMax(dataRange.top(), point.y()));
+ }
+ dataRange.setRight(round(dataRange.right()) + 1);
+ dataRange.setTop(round(dataRange.top()) + 1);
+
+ dataRange = dataRange.united(dataRangeDefault);
+ return dataRange;
+}
+
+void Chart::updateScalingFactors(const QRectF &dataRange)
+{
+ const double xRange = dataRange.right() - dataRange.left();
+ double yRange = dataRange.bottom() - dataRange.top();
+ yRange = yRange == 0 ? dataRange.top() : yRange;
+
+ m_xGridStep = qRound(xRange / 10);
+ m_xGridStep = m_xGridStep == 0 ? 1 : m_xGridStep;
+
+ m_yGridStep = yRange / 5;
+ m_yGridStep = qRound(m_yGridStep * 10.0) / 10.0;
+ if (yRange > 10)
+ m_yGridStep = qRound(m_yGridStep);
+ m_yGridStep = qMax(m_yGridStep, 0.1);
+
+ m_xScale = (width() - 2 * padding) / xRange;
+ m_yScale = (height() - 2 * padding) / yRange;
+}
+} // namespace AppStatisticsMonitor::Internal
diff --git a/src/plugins/appstatisticsmonitor/chart.h b/src/plugins/appstatisticsmonitor/chart.h
new file mode 100644
index 0000000000..0a9ed92751
--- /dev/null
+++ b/src/plugins/appstatisticsmonitor/chart.h
@@ -0,0 +1,40 @@
+// 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 <QList>
+#include <QPaintEvent>
+#include <QPointF>
+#include <QRectF>
+#include <QWidget>
+
+namespace AppStatisticsMonitor::Internal {
+
+class Chart : public QWidget
+{
+public:
+ Chart(const QString &name, QWidget *parent = nullptr);
+
+ void addNewPoint(const QPointF &point);
+ void loadNewProcessData(QList<double> data);
+ double lastPointX() const;
+
+ void clear();
+
+private:
+ void paintEvent(QPaintEvent *event) override;
+ void addPoint();
+ QRectF calculateDataRange() const;
+ void updateScalingFactors(const QRectF &dataRange);
+
+private:
+ QList<QPointF> m_points;
+ QString m_name;
+
+ double m_xScale = 1;
+ double m_yScale = 1;
+ double m_xGridStep = 1;
+ double m_yGridStep = 1;
+};
+
+} // namespace AppStatisticsMonitor::Internal
diff --git a/src/plugins/appstatisticsmonitor/idataprovider.cpp b/src/plugins/appstatisticsmonitor/idataprovider.cpp
new file mode 100644
index 0000000000..b2026e67cd
--- /dev/null
+++ b/src/plugins/appstatisticsmonitor/idataprovider.cpp
@@ -0,0 +1,237 @@
+// 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 "idataprovider.h"
+
+#include <utils/expected.h>
+#include <utils/fileutils.h>
+#include <utils/hostosinfo.h>
+
+#include <QByteArray>
+#include <QRegularExpression>
+#include <QString>
+
+#ifdef Q_OS_LINUX
+#include <unistd.h>
+#endif
+
+using namespace Utils;
+
+namespace AppStatisticsMonitor::Internal {
+
+IDataProvider::IDataProvider(qint64 pid, QObject *parent)
+ : QObject(parent)
+ , m_pid(pid)
+{
+ m_timer.setInterval(1000);
+ connect(&m_timer, &QTimer::timeout, this, [this] { handleTimeout(); });
+ m_timer.start();
+}
+
+void IDataProvider::handleTimeout()
+{
+ m_memoryConsumption.append(getMemoryConsumption());
+ m_cpuConsumption.append(getCpuConsumption());
+ emit newDataAvailable();
+}
+
+QList<double> IDataProvider::memoryConsumptionHistory() const
+{
+ return m_memoryConsumption;
+}
+
+QList<double> IDataProvider::cpuConsumptionHistory() const
+{
+ return m_cpuConsumption;
+}
+
+double IDataProvider::memoryConsumptionLast() const
+{
+ return m_memoryConsumption.isEmpty() ? 0 : m_memoryConsumption.last();
+}
+
+double IDataProvider::cpuConsumptionLast() const
+{
+ return m_cpuConsumption.isEmpty() ? 0 : m_cpuConsumption.last();
+}
+
+// ------------------------- LinuxDataProvider --------------------------------
+#ifdef Q_OS_LINUX
+class LinuxDataProvider : public IDataProvider
+{
+public:
+ LinuxDataProvider(qint64 pid, QObject *parent = nullptr)
+ : IDataProvider(pid, parent)
+ {}
+
+ double getMemoryConsumption()
+ {
+ const FilePath statusMemory = FilePath::fromString(
+ QStringLiteral("/proc/%1/status").arg(m_pid));
+ const expected_str<QByteArray> statusMemoryContent = statusMemory.fileContents();
+
+ if (!statusMemoryContent)
+ return 0;
+
+ int vmPeak = 0;
+ const static QRegularExpression numberRX(QLatin1String("[^0-9]+"));
+ for (const QByteArray &element : statusMemoryContent.value().split('\n')) {
+ if (element.startsWith("VmHWM")) {
+ const QString p = QString::fromUtf8(element);
+ vmPeak = p.split(numberRX, Qt::SkipEmptyParts)[0].toLong();
+ }
+ }
+
+ const FilePath meminfoFile("/proc/meminfo");
+ const expected_str<QByteArray> meminfoContent = meminfoFile.fileContents();
+ if (!meminfoContent)
+ return 0;
+
+ const auto meminfo = meminfoContent.value().split('\n');
+ if (meminfo.isEmpty())
+ return 0;
+
+ const auto parts = QString::fromUtf8(meminfo.front()).split(numberRX, Qt::SkipEmptyParts);
+ if (parts.isEmpty())
+ return 0;
+
+ return double(vmPeak) / parts[0].toDouble() * 100;
+ }
+
+ // Provides the CPU usage from the last request
+ double getCpuConsumption()
+ {
+ const FilePath status = FilePath::fromString(QStringLiteral("/proc/%1/stat").arg(m_pid));
+ const FilePath uptimeFile = FilePath::fromString(QStringLiteral("/proc/uptime"));
+ const double clkTck = static_cast<double>(sysconf(_SC_CLK_TCK));
+ const expected_str<QByteArray> statusFileContent = status.fileContents();
+ const expected_str<QByteArray> uptimeFileContent = uptimeFile.fileContents();
+
+ if (!statusFileContent.has_value() || !uptimeFileContent.has_value() || clkTck == 0)
+ return 0;
+
+ const QList<QByteArray> processStatus = statusFileContent.value().split(' ');
+ if (processStatus.isEmpty() || processStatus.size() < 22)
+ return 0;
+
+ const double uptime = uptimeFileContent.value().split(' ')[0].toDouble();
+
+ const double utime = processStatus[13].toDouble() / clkTck;
+ const double stime = processStatus[14].toDouble() / clkTck;
+ const double cutime = processStatus[15].toDouble() / clkTck;
+ const double cstime = processStatus[16].toDouble() / clkTck;
+ const double starttime = processStatus[21].toDouble() / clkTck;
+
+ // Calculate CPU usage for last request
+ const double currentTotalTime = utime + stime + cutime + cstime;
+
+ const double elapsed = uptime - starttime;
+ const double clicks = (currentTotalTime - m_lastTotalTime) * clkTck;
+ const double timeClicks = (elapsed - m_lastRequestStartTime) * clkTck;
+
+ m_lastTotalTime = currentTotalTime;
+ m_lastRequestStartTime = elapsed;
+
+ return timeClicks > 0 ? 100 * (clicks / timeClicks) : 0;
+ }
+
+ // Provides the usage all over the process lifetime
+ // Can be used in the future for the process lifetime statistics
+
+ // double LinuxDataProvider::getCpuConsumption()
+ // {
+ // const FilePath status = FilePath::fromString(
+ // QStringLiteral("/proc/%1/stat").arg(m_pid));
+ // const FilePath uptimeFile = FilePath::fromString(QStringLiteral("/proc/uptime"));
+ // const double clkTck = static_cast<double>(sysconf(_SC_CLK_TCK));
+
+ // if (!status.fileContents().has_value() || !uptimeFile.fileContents().has_value() || clkTck == 0)
+ // return 0;
+
+ // const QVector<QByteArray> processStatus = status.fileContents().value().split(' ').toVector();
+ // const double uptime = uptimeFile.fileContents().value().split(' ')[0].toDouble();
+
+ // const double utime = processStatus[13].toDouble() / clkTck;
+ // const double stime = processStatus[14].toDouble() / clkTck;
+ // const double cutime = processStatus[15].toDouble() / clkTck;
+ // const double cstime = processStatus[16].toDouble() / clkTck;
+ // const double starttime = processStatus[21].toDouble() / clkTck;
+
+ // const double elapsed = uptime - starttime;
+ // const double usage_sec = utime + stime + cutime + cstime;
+ // const double usage = 100 * usage_sec / elapsed;
+
+ // return usage;
+ // }
+};
+
+#endif
+
+// ------------------------- WindowsDataProvider --------------------------------
+
+#ifdef Q_OS_WIN
+class WindowsDataProvider : public IDataProvider
+{
+public:
+ WindowsDataProvider(qint64 pid, QObject *parent = nullptr)
+ : IDataProvider(pid, parent)
+ {}
+
+ double getMemoryConsumption() { return 0; }
+
+ double getCpuConsumption() { return 0; }
+
+#if 0
+ double getMemoryConsumptionWindows(qint64 pid)
+ {
+ HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
+ if (hProcess == NULL) {
+ std::cerr << "Failed to open process. Error code: " << GetLastError() << std::endl;
+ return 1;
+ }
+
+ PROCESS_MEMORY_COUNTERS_EX pmc;
+ if (!GetProcessMemoryInfo(hProcess, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) {
+ std::cerr << "Failed to retrieve process memory information. Error code: " << GetLastError() << std::endl;
+ CloseHandle(hProcess);
+ return 1;
+ }
+
+ std::cout << "Process ID: " << pid << std::endl;
+ std::cout << "Memory consumption: " << pmc.PrivateUsage << " bytes" << std::endl;
+
+ CloseHandle(hProcess);
+ return pmc.PrivateUsage;
+ }
+#endif
+};
+#endif
+
+// ------------------------- MacDataProvider --------------------------------
+
+#ifdef Q_OS_MACOS
+class MacDataProvider : public IDataProvider
+{
+public:
+ MacDataProvider(qint64 pid, QObject *parent = nullptr)
+ : IDataProvider(pid, parent)
+ {}
+
+ double getMemoryConsumption() { return 0; }
+
+ double getCpuConsumption() { return 0; }
+};
+#endif
+
+IDataProvider *createDataProvider(qint64 pid)
+{
+#ifdef Q_OS_WIN
+ return new WindowsDataProvider(pid);
+#elif defined(Q_OS_MACOS)
+ return new MacDataProvider(pid);
+#else // Q_OS_LINUX
+ return new LinuxDataProvider(pid);
+#endif
+}
+
+} // namespace AppStatisticsMonitor::Internal
diff --git a/src/plugins/appstatisticsmonitor/idataprovider.h b/src/plugins/appstatisticsmonitor/idataprovider.h
new file mode 100644
index 0000000000..42e60ebb8b
--- /dev/null
+++ b/src/plugins/appstatisticsmonitor/idataprovider.h
@@ -0,0 +1,44 @@
+// 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 <QList>
+#include <QObject>
+#include <QTimer>
+
+namespace AppStatisticsMonitor::Internal {
+
+class IDataProvider : public QObject
+{
+ Q_OBJECT
+public:
+ IDataProvider(qint64 pid, QObject *parent = nullptr);
+
+ QList<double> memoryConsumptionHistory() const;
+ QList<double> cpuConsumptionHistory() const;
+
+ double memoryConsumptionLast() const;
+ double cpuConsumptionLast() const;
+
+protected:
+ virtual double getMemoryConsumption() = 0;
+ virtual double getCpuConsumption() = 0;
+
+ QList<double> m_memoryConsumption;
+ QList<double> m_cpuConsumption;
+ qint64 m_pid;
+ double m_lastTotalTime;
+ double m_lastRequestStartTime;
+
+signals:
+ void newDataAvailable();
+
+private:
+ void handleTimeout();
+
+ QTimer m_timer;
+};
+
+IDataProvider *createDataProvider(qint64 pid);
+
+} // AppStatisticsMonitor::Internal
diff --git a/src/plugins/appstatisticsmonitor/manager.cpp b/src/plugins/appstatisticsmonitor/manager.cpp
new file mode 100644
index 0000000000..ad17b80dd7
--- /dev/null
+++ b/src/plugins/appstatisticsmonitor/manager.cpp
@@ -0,0 +1,219 @@
+// 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 "manager.h"
+
+#include "chart.h"
+#include "idataprovider.h"
+
+#include <coreplugin/session.h>
+
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/runcontrol.h>
+
+#include <QFormLayout>
+#include <QGraphicsItem>
+#include <QHash>
+
+using namespace ProjectExplorer;
+using namespace Utils;
+
+namespace AppStatisticsMonitor::Internal {
+
+class AppStatisticsMonitorView : public QWidget
+{
+public:
+ explicit AppStatisticsMonitorView(
+ AppStatisticsMonitorManager *appStatisticManager, QWidget *parent = nullptr);
+
+ ~AppStatisticsMonitorView() override;
+
+private:
+ QComboBox *m_comboBox;
+
+ Chart *m_memChart;
+ Chart *m_cpuChart;
+
+ AppStatisticsMonitorManager *m_manager;
+};
+
+AppStatisticsMonitorManager::AppStatisticsMonitorManager()
+{
+ connect(
+ ProjectExplorer::ProjectExplorerPlugin::instance(),
+ &ProjectExplorer::ProjectExplorerPlugin::runControlStarted,
+ this,
+ [this](RunControl *runControl) {
+ qint64 pid = runControl->applicationProcessHandle().pid();
+
+ m_pidNameMap[pid] = runControl->displayName();
+ m_rcPidMap[runControl] = pid;
+
+ m_currentDataProvider = createDataProvider(pid);
+ m_pidDataProviders.insert(pid, m_currentDataProvider);
+
+ emit appStarted(runControl->displayName(), pid);
+ });
+
+ connect(
+ ProjectExplorer::ProjectExplorerPlugin::instance(),
+ &ProjectExplorer::ProjectExplorerPlugin::runControlStoped,
+ this,
+ [this](RunControl *runControl) {
+ const auto pidIt = m_rcPidMap.constFind(runControl);
+ if (pidIt == m_rcPidMap.constEnd())
+ return;
+
+ const qint64 pid = pidIt.value();
+ m_rcPidMap.erase(pidIt);
+
+ m_pidNameMap.remove(pid);
+ delete m_pidDataProviders[pid];
+ m_pidDataProviders.remove(pid);
+ if (m_pidDataProviders.isEmpty())
+ setCurrentDataProvider(-1);
+ else
+ setCurrentDataProvider(m_pidDataProviders.keys().last());
+
+ emit appStoped(pid);
+ });
+}
+
+QString AppStatisticsMonitorManager::nameForPid(qint64 pid) const
+{
+ const auto pidIt = m_pidNameMap.constFind(pid);
+ if (pidIt == m_pidNameMap.constEnd())
+ return {};
+
+ return pidIt.value();
+}
+
+IDataProvider *AppStatisticsMonitorManager::currentDataProvider() const
+{
+ return m_currentDataProvider;
+}
+
+void AppStatisticsMonitorManager::setCurrentDataProvider(qint64 pid)
+{
+ m_currentDataProvider = nullptr;
+ const auto pidIt = m_pidDataProviders.constFind(pid);
+ if (pidIt == m_pidDataProviders.constEnd())
+ return;
+
+ m_currentDataProvider = pidIt.value();
+ connect(
+ m_currentDataProvider,
+ &IDataProvider::newDataAvailable,
+ this,
+ &AppStatisticsMonitorManager::newDataAvailable);
+}
+
+QHash<qint64, QString> AppStatisticsMonitorManager::pidNameMap() const
+{
+ return m_pidNameMap;
+}
+
+// AppStatisticsMonitorView
+AppStatisticsMonitorView::AppStatisticsMonitorView(AppStatisticsMonitorManager *dapManager, QWidget *)
+ : m_manager(dapManager)
+{
+ auto layout = new QVBoxLayout;
+ auto form = new QFormLayout;
+ setLayout(layout);
+
+ m_comboBox = new QComboBox();
+ form->addRow(m_comboBox);
+
+ m_memChart = new Chart("Memory consumption ");
+ m_memChart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ m_memChart->clear();
+ form->addRow(m_memChart);
+
+ m_cpuChart = new Chart("CPU consumption ");
+ m_cpuChart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ m_cpuChart->clear();
+ form->addRow(m_cpuChart);
+
+ layout->addLayout(form);
+
+ for (auto pidName : m_manager->pidNameMap().asKeyValueRange()) {
+ qint64 pid = pidName.first;
+ m_comboBox->addItem(pidName.second + " : " + QString::number(pid), pid);
+ }
+ m_comboBox->setCurrentIndex(m_comboBox->count() - 1);
+
+ m_memChart->clear();
+ m_cpuChart->clear();
+
+ auto updateCharts = [this](int index) {
+ m_manager->setCurrentDataProvider(m_comboBox->itemData(index).toLongLong());
+ if (m_manager->currentDataProvider() != nullptr) {
+ m_memChart->loadNewProcessData(
+ m_manager->currentDataProvider()->memoryConsumptionHistory());
+ m_cpuChart->loadNewProcessData(
+ m_manager->currentDataProvider()->cpuConsumptionHistory());
+ }
+ };
+
+ if (m_comboBox->count() != 0)
+ updateCharts(m_comboBox->currentIndex());
+
+ connect(m_comboBox, &QComboBox::currentIndexChanged, this, [updateCharts](int index) {
+ updateCharts(index);
+ });
+
+ connect(
+ m_manager,
+ &AppStatisticsMonitorManager::appStarted,
+ this,
+ [this](const QString &name, qint64 pid) {
+ if (pid != m_comboBox->currentData()) {
+ m_comboBox->addItem(name + " : " + QString::number(pid), pid);
+
+ m_memChart->clear();
+ m_cpuChart->clear();
+
+ m_comboBox->setCurrentIndex(m_comboBox->count() - 1);
+ }
+ });
+
+ connect(m_manager, &AppStatisticsMonitorManager::appStoped, this, [this](qint64 pid) {
+ m_memChart->addNewPoint({m_memChart->lastPointX() + 1, 0});
+ m_cpuChart->addNewPoint({m_cpuChart->lastPointX() + 1, 0});
+ const int indx = m_comboBox->findData(pid);
+ if (indx != -1)
+ m_comboBox->removeItem(indx);
+ });
+
+ connect(m_manager, &AppStatisticsMonitorManager::newDataAvailable, this, [this] {
+ const IDataProvider *currentDataProvider = m_manager->currentDataProvider();
+ if (currentDataProvider != nullptr) {
+ m_memChart->addNewPoint(
+ {(double) currentDataProvider->memoryConsumptionHistory().size(),
+ currentDataProvider->memoryConsumptionLast()});
+ m_cpuChart->addNewPoint(
+ {(double) currentDataProvider->cpuConsumptionHistory().size(),
+ currentDataProvider->cpuConsumptionLast()});
+ }
+ });
+}
+
+AppStatisticsMonitorView::~AppStatisticsMonitorView() = default;
+
+// AppStatisticsMonitorViewFactory
+AppStatisticsMonitorViewFactory::AppStatisticsMonitorViewFactory(
+ AppStatisticsMonitorManager *appStatisticManager)
+ : m_manager(appStatisticManager)
+{
+ setDisplayName(("AppStatisticsMonitor"));
+ setPriority(300);
+ setId("AppStatisticsMonitor");
+ setActivationSequence(QKeySequence("Alt+S"));
+}
+
+Core::NavigationView AppStatisticsMonitorViewFactory::createWidget()
+{
+ return {new AppStatisticsMonitorView(m_manager), {}};
+}
+
+} // namespace AppStatisticsMonitor::Internal
diff --git a/src/plugins/appstatisticsmonitor/manager.h b/src/plugins/appstatisticsmonitor/manager.h
new file mode 100644
index 0000000000..beba9cec72
--- /dev/null
+++ b/src/plugins/appstatisticsmonitor/manager.h
@@ -0,0 +1,60 @@
+// 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 "chart.h"
+
+#include "idataprovider.h"
+
+#include <coreplugin/inavigationwidgetfactory.h>
+
+#include <QComboBox>
+#include <QList>
+#include <QMap>
+
+namespace Core { class IContext; }
+namespace ProjectExplorer { class RunControl; }
+
+namespace AppStatisticsMonitor::Internal {
+
+class AppStatisticsMonitorManager : public QObject
+{
+ Q_OBJECT
+public:
+ AppStatisticsMonitorManager();
+
+ QString nameForPid(qint64 pid) const;
+ QHash<qint64, QString> pidNameMap() const;
+
+ double memoryConsumption(qint64 pid) const;
+ double cpuConsumption(qint64 pid) const;
+
+ IDataProvider *currentDataProvider() const;
+ void setCurrentDataProvider(qint64 pid);
+
+signals:
+ void newDataAvailable();
+ void appStarted(const QString &name, qint64 pid);
+ void appStoped(qint64 pid);
+
+private:
+ QHash<qint64, QString> m_pidNameMap;
+ QHash<ProjectExplorer::RunControl *, int> m_rcPidMap;
+
+ QMap<qint64, IDataProvider *> m_pidDataProviders;
+ IDataProvider *m_currentDataProvider;
+};
+
+class AppStatisticsMonitorViewFactory : public Core::INavigationWidgetFactory
+{
+public:
+ AppStatisticsMonitorViewFactory(AppStatisticsMonitorManager *appStatisticManager);
+
+private:
+ Core::NavigationView createWidget() override;
+
+ AppStatisticsMonitorManager *m_manager;
+};
+
+} // namespace AppStatisticsMonitor::Internal
diff --git a/src/plugins/autotest/AutoTest.json.in b/src/plugins/autotest/AutoTest.json.in
index 230a9ee0f6..a3bc0d1405 100644
--- a/src/plugins/autotest/AutoTest.json.in
+++ b/src/plugins/autotest/AutoTest.json.in
@@ -13,6 +13,6 @@
"Alternatively, this file may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this file. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html."
],
"Description" : "Auto Test plugin.",
-"Url" : "http://www.qt.io",
+"Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/autotest/ctest/ctesttool.cpp b/src/plugins/autotest/ctest/ctesttool.cpp
index 8b82953e2c..69e75e6a01 100644
--- a/src/plugins/autotest/ctest/ctesttool.cpp
+++ b/src/plugins/autotest/ctest/ctesttool.cpp
@@ -34,6 +34,7 @@ CTestTool::CTestTool()
setId("AutoTest.Framework.CTest");
setDisplayName(Tr::tr("CTest"));
+ // clang-format off
setLayouter([this] {
return Row { Form {
outputOnFail, br,
@@ -41,20 +42,21 @@ CTestTool::CTestTool()
stopOnFailure, br,
outputMode, br,
Group {
- title(Tr::tr("Repeat tests")),
- repeat.groupChecker(),
+ title(Tr::tr("Repeat Tests")),
+ groupChecker(repeat.groupChecker()),
Row { repetitionMode, repetitionCount},
}, br,
Group {
- title(Tr::tr("Run in parallel")),
- parallel.groupChecker(),
+ title(Tr::tr("Run in Parallel")),
+ groupChecker(parallel.groupChecker()),
Column {
Row { jobs }, br,
- Row { testLoad, threshold}
+ Row { testLoad, threshold }
}
}
}, st };
});
+ // clang-format on
outputOnFail.setSettingsKey("OutputOnFail");
outputOnFail.setLabelText(Tr::tr("Output on failure"));
diff --git a/src/plugins/autotest/ctest/ctesttreeitem.cpp b/src/plugins/autotest/ctest/ctesttreeitem.cpp
index a9f68d6895..2216a64cb2 100644
--- a/src/plugins/autotest/ctest/ctesttreeitem.cpp
+++ b/src/plugins/autotest/ctest/ctesttreeitem.cpp
@@ -93,7 +93,7 @@ QList<ITestConfiguration *> CTestTreeItem::testConfigurationsFor(const QStringLi
<< QString::number(testSettings().timeout() / 1000);
}
options << theCTestTool().activeSettingsAsOptions();
- CommandLine command = buildSystem->commandLineForTests(selected, options);
+ const CommandLine command = buildSystem->commandLineForTests(selected, options);
if (command.executable().isEmpty())
return {};
diff --git a/src/plugins/autotest/projectsettingswidget.cpp b/src/plugins/autotest/projectsettingswidget.cpp
index 01b6c06d00..b6347f52a7 100644
--- a/src/plugins/autotest/projectsettingswidget.cpp
+++ b/src/plugins/autotest/projectsettingswidget.cpp
@@ -74,6 +74,7 @@ ProjectTestSettingsWidget::ProjectTestSettingsWidget(Project *project)
QPushButton *removeFilter = new QPushButton(Tr::tr("Remove"), this);
removeFilter->setEnabled(false);
+ // clang-format off
using namespace Layouting;
Column {
Widget {
@@ -81,7 +82,7 @@ ProjectTestSettingsWidget::ProjectTestSettingsWidget(Project *project)
Column {
Row {
Group {
- title(Tr::tr("Active frameworks:")),
+ title(Tr::tr("Active Test Frameworks")),
Column { m_activeFrameworks },
},
st,
@@ -91,13 +92,13 @@ ProjectTestSettingsWidget::ProjectTestSettingsWidget(Project *project)
m_runAfterBuild,
st,
},
- noMargin(),
+ noMargin,
},
},
Row { // explicitly outside of the global settings
Group {
- title(Tr::tr("Limit files to path patterns")),
- m_applyFilter.groupChecker(),
+ title(Tr::tr("Limit Files to Path Patterns")),
+ groupChecker(m_applyFilter.groupChecker()),
Column {
filterLabel,
Row {
@@ -107,8 +108,9 @@ ProjectTestSettingsWidget::ProjectTestSettingsWidget(Project *project)
},
},
},
- noMargin(),
+ noMargin,
}.attachTo(this);
+ // clang-format on
generalWidget->setDisabled(m_projectSettings->useGlobalSettings());
diff --git a/src/plugins/autotest/testnavigationwidget.cpp b/src/plugins/autotest/testnavigationwidget.cpp
index 435b510f85..a8e1d98e05 100644
--- a/src/plugins/autotest/testnavigationwidget.cpp
+++ b/src/plugins/autotest/testnavigationwidget.cpp
@@ -88,8 +88,8 @@ TestNavigationWidget::TestNavigationWidget()
m_view->setItemDelegate(new TestTreeItemDelegate(this));
QPalette pal;
- pal.setColor(QPalette::Window, creatorTheme()->color(Theme::InfoBarBackground));
- pal.setColor(QPalette::WindowText, creatorTheme()->color(Theme::InfoBarText));
+ pal.setColor(QPalette::Window, creatorColor(Theme::InfoBarBackground));
+ pal.setColor(QPalette::WindowText, creatorColor(Theme::InfoBarText));
m_missingFrameworksWidget = new QFrame;
m_missingFrameworksWidget->setPalette(pal);
m_missingFrameworksWidget->setAutoFillBackground(true);
diff --git a/src/plugins/autotest/testresult.cpp b/src/plugins/autotest/testresult.cpp
index 75c3d45f6f..e1a1c6bc8a 100644
--- a/src/plugins/autotest/testresult.cpp
+++ b/src/plugins/autotest/testresult.cpp
@@ -135,33 +135,32 @@ QColor TestResult::colorForType(const ResultType type)
if (type >= ResultType::INTERNAL_MESSAGES_BEGIN && type <= ResultType::INTERNAL_MESSAGES_END)
return QColor("transparent");
- const Theme *theme = creatorTheme();
switch (type) {
case ResultType::Pass:
- return theme->color(Theme::OutputPanes_TestPassTextColor);
+ return creatorColor(Theme::OutputPanes_TestPassTextColor);
case ResultType::Fail:
- return theme->color(Theme::OutputPanes_TestFailTextColor);
+ return creatorColor(Theme::OutputPanes_TestFailTextColor);
case ResultType::ExpectedFail:
- return theme->color(Theme::OutputPanes_TestXFailTextColor);
+ return creatorColor(Theme::OutputPanes_TestXFailTextColor);
case ResultType::UnexpectedPass:
- return theme->color(Theme::OutputPanes_TestXPassTextColor);
+ return creatorColor(Theme::OutputPanes_TestXPassTextColor);
case ResultType::Skip:
- return theme->color(Theme::OutputPanes_TestSkipTextColor);
+ return creatorColor(Theme::OutputPanes_TestSkipTextColor);
case ResultType::MessageDebug:
case ResultType::MessageInfo:
- return theme->color(Theme::OutputPanes_TestDebugTextColor);
+ return creatorColor(Theme::OutputPanes_TestDebugTextColor);
case ResultType::MessageWarn:
- return theme->color(Theme::OutputPanes_TestWarnTextColor);
+ return creatorColor(Theme::OutputPanes_TestWarnTextColor);
case ResultType::MessageFatal:
case ResultType::MessageSystem:
case ResultType::MessageError:
- return theme->color(Theme::OutputPanes_TestFatalTextColor);
+ return creatorColor(Theme::OutputPanes_TestFatalTextColor);
case ResultType::BlacklistedPass:
case ResultType::BlacklistedFail:
case ResultType::BlacklistedXPass:
case ResultType::BlacklistedXFail:
default:
- return theme->color(Theme::OutputPanes_StdOutTextColor);
+ return creatorColor(Theme::OutputPanes_StdOutTextColor);
}
}
diff --git a/src/plugins/autotest/testresultspane.cpp b/src/plugins/autotest/testresultspane.cpp
index aacc1808bf..894107d5d4 100644
--- a/src/plugins/autotest/testresultspane.cpp
+++ b/src/plugins/autotest/testresultspane.cpp
@@ -86,8 +86,8 @@ TestResultsPane::TestResultsPane(QObject *parent) :
visualOutputWidget->setLayout(outputLayout);
QPalette pal;
- pal.setColor(QPalette::Window, creatorTheme()->color(Theme::InfoBarBackground));
- pal.setColor(QPalette::WindowText, creatorTheme()->color(Theme::InfoBarText));
+ pal.setColor(QPalette::Window, creatorColor(Theme::InfoBarBackground));
+ pal.setColor(QPalette::WindowText, creatorColor(Theme::InfoBarText));
m_summaryWidget = new QFrame;
m_summaryWidget->setPalette(pal);
m_summaryWidget->setAutoFillBackground(true);
diff --git a/src/plugins/autotest/testrunner.cpp b/src/plugins/autotest/testrunner.cpp
index fe3fe22fdd..6c8f19c1bb 100644
--- a/src/plugins/autotest/testrunner.cpp
+++ b/src/plugins/autotest/testrunner.cpp
@@ -383,7 +383,7 @@ void TestRunner::runTestsHelper()
connect(testStorage->m_outputReader.get(), &TestOutputReader::newOutputLineAvailable,
TestResultsPane::instance(), &TestResultsPane::addOutputLine);
- CommandLine command{config->testExecutable(), {}};
+ CommandLine command{config->testExecutable()};
if (config->testBase()->type() == ITestBase::Framework) {
TestConfiguration *current = static_cast<TestConfiguration *>(config);
QStringList omitted;
diff --git a/src/plugins/autotest/testsettingspage.cpp b/src/plugins/autotest/testsettingspage.cpp
index f74716b8e2..1b2c7cbd6e 100644
--- a/src/plugins/autotest/testsettingspage.cpp
+++ b/src/plugins/autotest/testsettingspage.cpp
@@ -75,7 +75,7 @@ TestSettingsWidget::TestSettingsWidget()
PushButton resetChoicesButton {
text(Tr::tr("Reset Cached Choices")),
- tooltip(Tr::tr("Clear all cached choices of run configurations for "
+ Layouting::toolTip(Tr::tr("Clear all cached choices of run configurations for "
"tests where the executable could not be deduced.")),
onClicked(&clearChoiceCache, this)
};
diff --git a/src/plugins/autotoolsprojectmanager/AutotoolsProjectManager.json.in b/src/plugins/autotoolsprojectmanager/AutotoolsProjectManager.json.in
index 399a70dd3a..ab615b4f61 100644
--- a/src/plugins/autotoolsprojectmanager/AutotoolsProjectManager.json.in
+++ b/src/plugins/autotoolsprojectmanager/AutotoolsProjectManager.json.in
@@ -15,6 +15,6 @@
],
"Category" : "Build Systems",
"Description" : "Autotools project integration.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp
index fd35ed61ff..80dd5103c1 100644
--- a/src/plugins/axivion/axivionoutputpane.cpp
+++ b/src/plugins/axivion/axivionoutputpane.cpp
@@ -717,7 +717,7 @@ void IssuesWidget::showNoDataOverlay()
iconRect.moveCenter(that->rect().center());
icon.paint(&p, iconRect);
p.save();
- p.setPen(Utils::creatorTheme()->color(Theme::TextColorDisabled));
+ p.setPen(Utils::creatorColor(Theme::TextColorDisabled));
p.drawText(iconRect.bottomRight() + QPoint{10, p.fontMetrics().height() / 2 - 16},
Tr::tr("No Data"));
p.restore();
@@ -750,7 +750,7 @@ public:
m_outputWidget->addWidget(issuesWidget);
QPalette pal = m_outputWidget->palette();
- pal.setColor(QPalette::Window, creatorTheme()->color(Theme::Color::BackgroundColorNormal));
+ pal.setColor(QPalette::Window, creatorColor(Theme::Color::BackgroundColorNormal));
m_outputWidget->setPalette(pal);
m_showDashboard = new QToolButton(m_outputWidget);
diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp
index 062da02feb..e7bb3d176e 100644
--- a/src/plugins/axivion/axivionplugin.cpp
+++ b/src/plugins/axivion/axivionplugin.cpp
@@ -19,7 +19,6 @@
#include <coreplugin/navigationwidget.h>
#include <extensionsystem/iplugin.h>
-#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/project.h>
@@ -106,6 +105,9 @@ QString anyToSimpleString(const Dto::Any &any)
value = anyMap.find("name");
if (value != anyMap.end())
return anyToSimpleString(value->second);
+ value = anyMap.find("tag");
+ if (value != anyMap.end())
+ return anyToSimpleString(value->second);
}
return {};
}
@@ -126,9 +128,9 @@ static QString escapeKey(const QString &string)
return escaped.replace('\\', "\\\\").replace('@', "\\@");
}
-static QString credentialKey()
+static QString credentialKey(const AxivionServer &server)
{
- return escapeKey(settings().server.username) + '@' + escapeKey(settings().server.dashboard);
+ return escapeKey(server.username) + '@' + escapeKey(server.dashboard);
}
template <typename DtoType>
@@ -222,6 +224,9 @@ signals:
void issueDetailsChanged(const QString &issueDetailsHtml);
public:
+ // active id used for any network communication, defaults to settings' default
+ // set to projects settings' dashboard id on open project
+ Id m_dashboardServerId;
// TODO: Should be set to Unknown on server address change in settings.
ServerAccess m_serverAccess = ServerAccess::Unknown;
// TODO: Should be cleared on username change in settings.
@@ -279,10 +284,10 @@ std::optional<Dto::ProjectInfoDto> projectInfo()
// FIXME: extend to give some details?
// FIXME: move when curl is no more in use?
-bool handleCertificateIssue()
+bool handleCertificateIssue(const Utils::Id &serverId)
{
QTC_ASSERT(dd, return false);
- const QString serverHost = QUrl(settings().server.dashboard).host();
+ const QString serverHost = QUrl(settings().serverForId(serverId).dashboard).host();
if (QMessageBox::question(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"
@@ -291,7 +296,7 @@ bool handleCertificateIssue()
!= QMessageBox::Yes) {
return false;
}
- settings().server.validateCert = false;
+ settings().disableCertificateValidation(serverId);
settings().apply();
return true;
@@ -307,6 +312,7 @@ AxivionPluginPrivate::AxivionPluginPrivate()
void AxivionPluginPrivate::handleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
{
+ QTC_ASSERT(dd, return);
#if QT_CONFIG(ssl)
const QList<QSslError::SslError> accepted{
QSslError::CertificateNotYetValid, QSslError::CertificateExpired,
@@ -315,7 +321,8 @@ void AxivionPluginPrivate::handleSslErrors(QNetworkReply *reply, const QList<QSs
};
if (Utils::allOf(errors,
[&accepted](const QSslError &e) { return accepted.contains(e.error()); })) {
- if (!settings().server.validateCert || handleCertificateIssue())
+ const bool shouldValidate = settings().serverForId(dd->m_dashboardServerId).validateCert;
+ if (!shouldValidate || handleCertificateIssue(dd->m_dashboardServerId))
reply->ignoreSslErrors(errors);
}
#else // ssl
@@ -349,6 +356,7 @@ void AxivionPluginPrivate::onStartupProjectChanged(Project *project)
handleOpenedDocs();
});
const AxivionProjectSettings *projSettings = AxivionProjectSettings::projectSettings(m_project);
+ switchActiveDashboardId(projSettings->dashboardId());
fetchProjectInfo(projSettings->dashboardProjectName());
}
@@ -493,7 +501,6 @@ static Group dtoRecipe(const Storage<DtoStorageType<DtoType>> &dtoStorage)
const auto deserialize = [](QPromise<expected_str<DtoType>> &promise, const QByteArray &input) {
promise.addResult(DtoType::deserializeExpected(input));
};
- task.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
task.setConcurrentCallData(deserialize, **storage);
return SetupResult::Continue;
};
@@ -544,16 +551,17 @@ static void handleCredentialError(const CredentialQuery &credential)
static Group authorizationRecipe()
{
+ const Id serverId = dd->m_dashboardServerId;
const Storage<QUrl> serverUrlStorage;
const Storage<GetDtoStorage<Dto::DashboardInfoDto>> unauthorizedDashboardStorage;
const auto onUnauthorizedGroupSetup = [serverUrlStorage, unauthorizedDashboardStorage] {
unauthorizedDashboardStorage->url = *serverUrlStorage;
return isServerAccessEstablished() ? SetupResult::StopWithSuccess : SetupResult::Continue;
};
- const auto onUnauthorizedDashboard = [unauthorizedDashboardStorage] {
+ const auto onUnauthorizedDashboard = [unauthorizedDashboardStorage, serverId] {
if (unauthorizedDashboardStorage->dtoData) {
const Dto::DashboardInfoDto &dashboardInfo = *unauthorizedDashboardStorage->dtoData;
- const QString &username = settings().server.username;
+ const QString &username = settings().serverForId(serverId).username;
if (username.isEmpty()
|| (dashboardInfo.username && *dashboardInfo.username == username)) {
dd->m_serverAccess = ServerAccess::NoAuthorization;
@@ -570,10 +578,10 @@ static Group authorizationRecipe()
const auto onCredentialLoopCondition = [](int) {
return dd->m_serverAccess == ServerAccess::WithAuthorization && !dd->m_apiToken;
};
- const auto onGetCredentialSetup = [](CredentialQuery &credential) {
+ const auto onGetCredentialSetup = [serverId](CredentialQuery &credential) {
credential.setOperation(CredentialOperation::Get);
credential.setService(s_axivionKeychainService);
- credential.setKey(credentialKey());
+ credential.setKey(credentialKey(settings().serverForId(serverId)));
};
const auto onGetCredentialDone = [](const CredentialQuery &credential, DoneWith result) {
if (result == DoneWith::Success)
@@ -587,19 +595,21 @@ static Group authorizationRecipe()
const Storage<QString> passwordStorage;
const Storage<GetDtoStorage<Dto::DashboardInfoDto>> dashboardStorage;
- const auto onPasswordGroupSetup = [serverUrlStorage, passwordStorage, dashboardStorage] {
+ const auto onPasswordGroupSetup
+ = [serverId, serverUrlStorage, passwordStorage, dashboardStorage] {
if (dd->m_apiToken)
return SetupResult::StopWithSuccess;
bool ok = false;
+ const AxivionServer server = settings().serverForId(serverId);
const QString text(Tr::tr("Enter the password for:\nDashboard: %1\nUser: %2")
- .arg(settings().server.dashboard, settings().server.username));
+ .arg(server.dashboard, server.username));
*passwordStorage = QInputDialog::getText(ICore::mainWindow(),
Tr::tr("Axivion Server Password"), text, QLineEdit::Password, {}, &ok);
if (!ok)
return SetupResult::StopWithError;
- const QString credential = settings().server.username + ':' + *passwordStorage;
+ const QString credential = server.username + ':' + *passwordStorage;
dashboardStorage->credential = "Basic " + credential.toUtf8().toBase64();
dashboardStorage->url = *serverUrlStorage;
return SetupResult::Continue;
@@ -626,14 +636,14 @@ static Group authorizationRecipe()
return SetupResult::Continue;
};
- const auto onSetCredentialSetup = [apiTokenStorage](CredentialQuery &credential) {
+ const auto onSetCredentialSetup = [apiTokenStorage, serverId](CredentialQuery &credential) {
if (!apiTokenStorage->dtoData || !apiTokenStorage->dtoData->token)
return SetupResult::StopWithSuccess;
dd->m_apiToken = apiTokenStorage->dtoData->token->toUtf8();
credential.setOperation(CredentialOperation::Set);
credential.setService(s_axivionKeychainService);
- credential.setKey(credentialKey());
+ credential.setKey(credentialKey(settings().serverForId(serverId)));
credential.setData(*dd->m_apiToken);
return SetupResult::Continue;
};
@@ -653,7 +663,7 @@ static Group authorizationRecipe()
dashboardStorage->url = *serverUrlStorage;
return SetupResult::Continue;
};
- const auto onDeleteCredentialSetup = [dashboardStorage](CredentialQuery &credential) {
+ const auto onDeleteCredentialSetup = [dashboardStorage, serverId](CredentialQuery &credential) {
if (dashboardStorage->dtoData) {
dd->m_dashboardInfo = toDashboardInfo(*dashboardStorage);
return SetupResult::StopWithSuccess;
@@ -663,13 +673,15 @@ static Group authorizationRecipe()
.arg(Tr::tr("The stored ApiToken is not valid anymore, removing it.")));
credential.setOperation(CredentialOperation::Delete);
credential.setService(s_axivionKeychainService);
- credential.setKey(credentialKey());
+ credential.setKey(credentialKey(settings().serverForId(serverId)));
return SetupResult::Continue;
};
return {
serverUrlStorage,
- onGroupSetup([serverUrlStorage] { *serverUrlStorage = QUrl(settings().server.dashboard); }),
+ onGroupSetup([serverUrlStorage, serverId] {
+ *serverUrlStorage = QUrl(settings().serverForId(serverId).dashboard);
+ }),
Group {
unauthorizedDashboardStorage,
onGroupSetup(onUnauthorizedGroupSetup),
@@ -1047,6 +1059,15 @@ void fetchIssueInfo(const QString &id)
dd->fetchIssueInfo(id);
}
+void switchActiveDashboardId(const Id &toDashboardId)
+{
+ QTC_ASSERT(dd, return);
+ dd->m_dashboardServerId = toDashboardId;
+ dd->m_serverAccess = ServerAccess::Unknown;
+ dd->m_apiToken.reset();
+ dd->m_dashboardInfo.reset();
+}
+
const std::optional<DashboardInfo> currentDashboardInfo()
{
QTC_ASSERT(dd, return std::nullopt);
diff --git a/src/plugins/axivion/axivionplugin.h b/src/plugins/axivion/axivionplugin.h
index 1a69c4ff37..1b25f7a781 100644
--- a/src/plugins/axivion/axivionplugin.h
+++ b/src/plugins/axivion/axivionplugin.h
@@ -6,6 +6,7 @@
#include "dashboard/dto.h"
#include <utils/expected.h>
+#include <utils/id.h>
#include <QHash>
#include <QUrl>
@@ -83,6 +84,7 @@ QIcon iconForIssue(const std::optional<Dto::IssueKind> &issueKind);
QString anyToSimpleString(const Dto::Any &any);
void fetchIssueInfo(const QString &id);
+void switchActiveDashboardId(const Utils::Id &toDashboardId);
const std::optional<DashboardInfo> currentDashboardInfo();
Utils::FilePath findFileForIssuePath(const Utils::FilePath &issuePath);
diff --git a/src/plugins/axivion/axivionprojectsettings.cpp b/src/plugins/axivion/axivionprojectsettings.cpp
index 023c251ea4..7a3a690c78 100644
--- a/src/plugins/axivion/axivionprojectsettings.cpp
+++ b/src/plugins/axivion/axivionprojectsettings.cpp
@@ -30,6 +30,7 @@ using namespace Utils;
namespace Axivion::Internal {
const char PSK_PROJECTNAME[] = "Axivion.ProjectName";
+const char PSK_DASHBOARDID[] = "Axivion.DashboardId";
// AxivionProjectSettingsHandler
@@ -82,11 +83,15 @@ void AxivionProjectSettings::destroyProjectSettings()
void AxivionProjectSettings::load()
{
m_dashboardProjectName = m_project->namedSettings(PSK_PROJECTNAME).toString();
+ m_dashboardId = Id::fromSetting(m_project->namedSettings(PSK_DASHBOARDID));
+ if (!m_dashboardId.isValid())
+ m_dashboardId = settings().defaultDashboardId();
}
void AxivionProjectSettings::save()
{
m_project->setNamedSettings(PSK_PROJECTNAME, m_dashboardProjectName);
+ m_project->setNamedSettings(PSK_DASHBOARDID, m_dashboardId.toSetting());
}
// AxivionProjectSettingsWidget
@@ -187,6 +192,12 @@ void AxivionProjectSettingsWidget::onSettingsChanged()
{
m_dashboardProjects->clear();
m_infoLabel->setVisible(false);
+ // check if serverId vanished - reset to default
+ const Id serverId = settings().defaultDashboardId();
+ if (m_projectSettings->dashboardId() != serverId) {
+ m_projectSettings->setDashboardId(serverId);
+ switchActiveDashboardId(serverId);
+ }
updateUi();
}
@@ -197,6 +208,9 @@ void AxivionProjectSettingsWidget::linkProject()
const QString projectName = selected.first()->text(0);
m_projectSettings->setDashboardProjectName(projectName);
+ const Id serverId = settings().defaultDashboardId();
+ m_projectSettings->setDashboardId(serverId);
+ switchActiveDashboardId(serverId);
updateUi();
fetchProjectInfo(projectName);
}
@@ -222,7 +236,8 @@ void AxivionProjectSettingsWidget::updateUi()
void AxivionProjectSettingsWidget::updateEnabledStates()
{
- const bool hasDashboardSettings = !settings().server.dashboard.isEmpty();
+ const bool hasDashboardSettings = !settings().serverForId(m_projectSettings->dashboardId())
+ .dashboard.isEmpty();
const bool linked = !m_projectSettings->dashboardProjectName().isEmpty();
const bool linkable = m_dashboardProjects->topLevelItemCount()
&& !m_dashboardProjects->selectedItems().isEmpty();
diff --git a/src/plugins/axivion/axivionprojectsettings.h b/src/plugins/axivion/axivionprojectsettings.h
index 7cbdfbbf47..7639213e87 100644
--- a/src/plugins/axivion/axivionprojectsettings.h
+++ b/src/plugins/axivion/axivionprojectsettings.h
@@ -3,6 +3,8 @@
#pragma once
+#include <utils/id.h>
+
#include <QObject>
namespace ProjectExplorer { class Project; }
@@ -16,6 +18,8 @@ public:
void setDashboardProjectName(const QString &name) { m_dashboardProjectName = name; }
QString dashboardProjectName() const { return m_dashboardProjectName; }
+ void setDashboardId(const Utils::Id &dashboardId) { m_dashboardId = dashboardId; }
+ Utils::Id dashboardId() const { return m_dashboardId; }
static AxivionProjectSettings *projectSettings(ProjectExplorer::Project *project);
static void destroyProjectSettings();
@@ -27,6 +31,8 @@ private:
ProjectExplorer::Project *m_project = nullptr;
QString m_dashboardProjectName;
+ // id of the dashboard in use for this project, or default from settings on initialization
+ Utils::Id m_dashboardId;
};
} // Axivion::Internal
diff --git a/src/plugins/axivion/axivionsettings.cpp b/src/plugins/axivion/axivionsettings.cpp
index 2af22e7cb9..72591133b8 100644
--- a/src/plugins/axivion/axivionsettings.cpp
+++ b/src/plugins/axivion/axivionsettings.cpp
@@ -8,12 +8,14 @@
#include <coreplugin/dialogs/ioptionspage.h>
#include <coreplugin/icore.h>
+#include <utils/algorithm.h>
#include <utils/id.h>
#include <utils/layoutbuilder.h>
#include <utils/stringutils.h>
#include <QDialog>
#include <QDialogButtonBox>
+#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QPushButton>
@@ -72,16 +74,19 @@ static FilePath tokensFilePath()
.pathAppended("qtcreator/axivion.json");
}
-static void writeTokenFile(const FilePath &filePath, const AxivionServer &server)
+static void writeTokenFile(const FilePath &filePath, const QList<AxivionServer> &servers)
{
QJsonDocument doc;
- doc.setObject(server.toJson());
+ QJsonArray serverArray;
+ for (const AxivionServer &server : servers)
+ serverArray.append(server.toJson());
+ doc.setArray(serverArray);
// FIXME error handling?
filePath.writeFileContents(doc.toJson());
filePath.setPermissions(QFile::ReadUser | QFile::WriteUser);
}
-static AxivionServer readTokenFile(const FilePath &filePath)
+static QList<AxivionServer> readTokenFile(const FilePath &filePath)
{
if (!filePath.exists())
return {};
@@ -89,9 +94,19 @@ static AxivionServer readTokenFile(const FilePath &filePath)
if (!contents)
return {};
const QJsonDocument doc = QJsonDocument::fromJson(*contents);
- if (!doc.isObject())
+ if (doc.isObject()) // old approach
+ return { AxivionServer::fromJson(doc.object()) };
+ if (!doc.isArray())
return {};
- return AxivionServer::fromJson(doc.object());
+
+ QList<AxivionServer> result;
+ const QJsonArray serverArray = doc.array();
+ for (const auto &serverValue : serverArray) {
+ if (!serverValue.isObject())
+ continue;
+ result.append(AxivionServer::fromJson(serverValue.toObject()));
+ }
+ return result;
}
// AxivionSetting
@@ -112,15 +127,63 @@ AxivionSettings::AxivionSettings()
highlightMarks.setDefaultValue(false);
AspectContainer::readSettings();
- server = readTokenFile(tokensFilePath());
+ m_allServers = readTokenFile(tokensFilePath());
+
+ if (m_allServers.size() == 1 && !m_defaultServerId.isValid()) // handle settings transition
+ m_defaultServerId = m_allServers.first().id;
}
void AxivionSettings::toSettings() const
{
- writeTokenFile(tokensFilePath(), server);
+ writeTokenFile(tokensFilePath(), m_allServers);
AspectContainer::writeSettings();
}
+Id AxivionSettings::defaultDashboardId() const
+{
+ return m_defaultServerId;
+}
+
+const AxivionServer AxivionSettings::defaultServer() const
+{
+ return serverForId(defaultDashboardId());
+}
+
+const AxivionServer AxivionSettings::serverForId(const Utils::Id &id) const
+{
+ return Utils::findOrDefault(m_allServers, [&id](const AxivionServer &server) {
+ return id == server.id;
+ });
+}
+
+void AxivionSettings::disableCertificateValidation(const Utils::Id &id)
+{
+ const int index = Utils::indexOf(m_allServers, [&id](const AxivionServer &server) {
+ return id == server.id;
+ });
+ if (index == -1)
+ return;
+
+ m_allServers[index].validateCert = false;
+}
+
+void AxivionSettings::modifyDashboardServer(const Utils::Id &id, const AxivionServer &other)
+{
+ const int index = Utils::indexOf(m_allServers, [&id](const AxivionServer &server) {
+ return id == server.id;
+ });
+ if (index == -1 && !id.isValid() && m_allServers.isEmpty()) { // temporary? hack
+ m_allServers.append(other);
+ m_defaultServerId = other.id;
+ emit changed();
+ }
+ if (index == -1)
+ return;
+
+ m_allServers.replace(index, other);
+ emit changed();
+}
+
// AxivionSettingsPage
// may allow some invalid, but does some minimal check for legality
@@ -242,7 +305,7 @@ AxivionSettingsWidget::AxivionSettingsWidget()
using namespace Layouting;
m_dashboardDisplay = new DashboardSettingsWidget(DashboardSettingsWidget::Display, this);
- m_dashboardDisplay->setDashboardServer(settings().server);
+ m_dashboardDisplay->setDashboardServer(settings().defaultServer());
m_edit = new QPushButton(Tr::tr("Edit..."), this);
Column {
Row {
@@ -260,8 +323,8 @@ AxivionSettingsWidget::AxivionSettingsWidget()
void AxivionSettingsWidget::apply()
{
- settings().server = m_dashboardDisplay->dashboardServer();
- emit settings().changed(); // ugly but needed
+ settings().modifyDashboardServer(settings().defaultDashboardId(),
+ m_dashboardDisplay->dashboardServer());
settings().toSettings();
}
diff --git a/src/plugins/axivion/axivionsettings.h b/src/plugins/axivion/axivionsettings.h
index 257b4056c5..193982dd34 100644
--- a/src/plugins/axivion/axivionsettings.h
+++ b/src/plugins/axivion/axivionsettings.h
@@ -23,6 +23,8 @@ public:
QJsonObject toJson() const;
static AxivionServer fromJson(const QJsonObject &json);
+ // id starts empty == invalid; set to generated uuid on first apply of valid dashboard config,
+ // never changes afterwards
Utils::Id id;
QString dashboard;
QString username;
@@ -37,8 +39,16 @@ public:
void toSettings() const;
- AxivionServer server; // shall we have more than one?
+ Utils::Id defaultDashboardId() const;
+ const AxivionServer defaultServer() const;
+ const AxivionServer serverForId(const Utils::Id &id) const;
+ void disableCertificateValidation(const Utils::Id &id);
+ void modifyDashboardServer(const Utils::Id &id, const AxivionServer &other);
+
Utils::BoolAspect highlightMarks{this};
+private:
+ Utils::Id m_defaultServerId; // holds the current selected
+ QList<AxivionServer> m_allServers;
};
AxivionSettings &settings();
diff --git a/src/plugins/axivion/dynamiclistmodel.cpp b/src/plugins/axivion/dynamiclistmodel.cpp
index 80419a3223..ecd8b361b2 100644
--- a/src/plugins/axivion/dynamiclistmodel.cpp
+++ b/src/plugins/axivion/dynamiclistmodel.cpp
@@ -73,7 +73,7 @@ QVariant DynamicListModel::data(const QModelIndex &index, int role) const
if (role == Qt::DisplayRole && index.column() == 0)
return Tr::tr("Fetching..."); // TODO improve/customize?
if (role == Qt::ForegroundRole && index.column() == 0)
- return Utils::creatorTheme()->color(Utils::Theme::TextColorDisabled);
+ return Utils::creatorColor(Utils::Theme::TextColorDisabled);
return {};
}
diff --git a/src/plugins/baremetal/debugservers/gdb/eblinkgdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/eblinkgdbserverprovider.cpp
index 7ce65d689e..c31ed27dea 100644
--- a/src/plugins/baremetal/debugservers/gdb/eblinkgdbserverprovider.cpp
+++ b/src/plugins/baremetal/debugservers/gdb/eblinkgdbserverprovider.cpp
@@ -151,7 +151,7 @@ QString EBlinkGdbServerProvider::channelString() const
CommandLine EBlinkGdbServerProvider::command() const
{
- CommandLine cmd{m_executableFile, {}};
+ CommandLine cmd{m_executableFile};
QStringList interFaceTypeStrings = {"swd", "jtag"};
// Obligatorily -I
diff --git a/src/plugins/baremetal/debugservers/gdb/stlinkutilgdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/stlinkutilgdbserverprovider.cpp
index b94012c9dc..96a94dc609 100644
--- a/src/plugins/baremetal/debugservers/gdb/stlinkutilgdbserverprovider.cpp
+++ b/src/plugins/baremetal/debugservers/gdb/stlinkutilgdbserverprovider.cpp
@@ -131,7 +131,7 @@ QString StLinkUtilGdbServerProvider::channelString() const
CommandLine StLinkUtilGdbServerProvider::command() const
{
- CommandLine cmd{m_executableFile, {}};
+ CommandLine cmd{m_executableFile};
if (m_extendedMode)
cmd.addArg("--multi");
diff --git a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp
index ed84508322..3709736d84 100644
--- a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp
+++ b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp
@@ -210,10 +210,8 @@ ProjectExplorer::RunWorker *UvscServerProvider::targetRunner(RunControl *runCont
{
// Get uVision executable path.
const ProcessRunData uv = DebuggerKitAspect::runnable(runControl->kit());
- CommandLine server(uv.command.executable());
- server.addArg("-j0");
- server.addArg(QStringLiteral("-s%1").arg(m_channel.port()));
-
+ const CommandLine server{uv.command.executable(),
+ {"-j0", QStringLiteral("-s%1").arg(m_channel.port())}};
ProcessRunData r;
r.command = server;
return new UvscServerProviderRunner(runControl, r);
diff --git a/src/plugins/baremetal/keiltoolchain.cpp b/src/plugins/baremetal/keiltoolchain.cpp
index 0ca7495c94..bc260130b2 100644
--- a/src/plugins/baremetal/keiltoolchain.cpp
+++ b/src/plugins/baremetal/keiltoolchain.cpp
@@ -246,11 +246,7 @@ static Macros dumpArmPredefinedMacros(const FilePath &compiler, const QStringLis
{
Process cpp;
cpp.setEnvironment(env);
-
- QStringList args = extraArgs;
- args.push_back("-E");
- args.push_back("--list-macros");
- cpp.setCommand({compiler, args});
+ cpp.setCommand({compiler, {extraArgs, "-E", "--list-macros"}});
cpp.runBlocking();
if (cpp.result() != ProcessResult::FinishedWithSuccess) {
diff --git a/src/plugins/bazaar/Bazaar.json.in b/src/plugins/bazaar/Bazaar.json.in
index 82bb55661f..2d658debd9 100644
--- a/src/plugins/bazaar/Bazaar.json.in
+++ b/src/plugins/bazaar/Bazaar.json.in
@@ -14,6 +14,6 @@
],
"Category" : "Version Control",
"Description" : "Bazaar integration.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/bazaar/bazaarplugin.cpp b/src/plugins/bazaar/bazaarplugin.cpp
index 590cfd2327..47c4bd8f8c 100644
--- a/src/plugins/bazaar/bazaarplugin.cpp
+++ b/src/plugins/bazaar/bazaarplugin.cpp
@@ -947,24 +947,21 @@ VcsCommand *BazaarPluginPrivate::createInitialCheckoutCommand(const QString &url
const QString &localName,
const QStringList &extraArgs)
{
- QStringList args;
- args << m_client.vcsCommandString(BazaarClient::CloneCommand)
- << extraArgs << url << localName;
-
Environment env = m_client.processEnvironment(baseDirectory);
env.set("BZR_PROGRESS_BAR", "text");
auto command = VcsBaseClient::createVcsCommand(this, baseDirectory, env);
- command->addJob({m_client.vcsBinary(baseDirectory), args}, -1);
+ command->addJob({m_client.vcsBinary(baseDirectory),
+ {m_client.vcsCommandString(BazaarClient::CloneCommand), extraArgs, url, localName}}, -1);
return command;
}
void BazaarPluginPrivate::changed(const QVariant &v)
{
- switch (v.type()) {
- case QVariant::String:
+ switch (v.typeId()) {
+ case QMetaType::QString:
emit repositoryChanged(FilePath::fromVariant(v));
break;
- case QVariant::StringList:
+ case QMetaType::QStringList:
emit filesChanged(v.toStringList());
break;
default:
diff --git a/src/plugins/beautifier/Beautifier.json.in b/src/plugins/beautifier/Beautifier.json.in
index 79de488b61..b1e4e552d3 100644
--- a/src/plugins/beautifier/Beautifier.json.in
+++ b/src/plugins/beautifier/Beautifier.json.in
@@ -15,6 +15,6 @@
],
"Category" : "C++",
"Description" : "Format source files with the help of beautifiers like AStyle, uncrustify or clang-format.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/beautifier/clangformat/clangformat.cpp b/src/plugins/beautifier/clangformat/clangformat.cpp
index 22bcea77f7..d964fe30b1 100644
--- a/src/plugins/beautifier/clangformat/clangformat.cpp
+++ b/src/plugins/beautifier/clangformat/clangformat.cpp
@@ -270,7 +270,8 @@ public:
title(Tr::tr("Options")),
bindTo(&options),
Form {
- s.usePredefinedStyle.adoptButton(predefinedStyleButton), predefinedBlob, br,
+ s.usePredefinedStyle.adoptButton(predefinedStyleButton),
+ predefinedBlob, br,
customizedStyleButton, configurations,
},
},
diff --git a/src/plugins/beautifier/generalsettings.cpp b/src/plugins/beautifier/generalsettings.cpp
index be80de3938..413e80698f 100644
--- a/src/plugins/beautifier/generalsettings.cpp
+++ b/src/plugins/beautifier/generalsettings.cpp
@@ -50,7 +50,7 @@ GeneralSettings::GeneralSettings()
return Column {
Group {
title(Tr::tr("Automatic Formatting on File Save")),
- autoFormatOnSave.groupChecker(),
+ groupChecker(autoFormatOnSave.groupChecker()),
Form {
autoFormatTools, br,
autoFormatMime, br,
diff --git a/src/plugins/bineditor/BinEditor.json.in b/src/plugins/bineditor/BinEditor.json.in
index 5d05094b0f..2d10a21164 100644
--- a/src/plugins/bineditor/BinEditor.json.in
+++ b/src/plugins/bineditor/BinEditor.json.in
@@ -14,6 +14,6 @@
],
"Category" : "Qt Creator",
"Description" : "Binary editor component.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/boot2qt/Boot2Qt.json.in b/src/plugins/boot2qt/Boot2Qt.json.in
index 6ab0b2a7d6..34e7bc6d7f 100644
--- a/src/plugins/boot2qt/Boot2Qt.json.in
+++ b/src/plugins/boot2qt/Boot2Qt.json.in
@@ -16,6 +16,6 @@
"Category" : "Device Support",
"Description" : "Support for the Boot2Qt Device access using the Qt Debug Bridge.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/boot2qt/qdbdevice.cpp b/src/plugins/boot2qt/qdbdevice.cpp
index 1ede480417..d9cbd3ed2a 100644
--- a/src/plugins/boot2qt/qdbdevice.cpp
+++ b/src/plugins/boot2qt/qdbdevice.cpp
@@ -111,7 +111,7 @@ QdbDevice::QdbDevice()
setType(Constants::QdbLinuxOsType);
addDeviceAction({Tr::tr("Reboot Device"), [](const IDevice::Ptr &device, QWidget *) {
- (void) new DeviceApplicationObserver(device, {device->filePath("reboot"), {}});
+ (void) new DeviceApplicationObserver(device, CommandLine{device->filePath("reboot")});
}});
addDeviceAction({Tr::tr("Restore Default App"), [](const IDevice::Ptr &device, QWidget *) {
diff --git a/src/plugins/boot2qt/qdbplugin.cpp b/src/plugins/boot2qt/qdbplugin.cpp
index c0a5bb25ca..d3943e94b1 100644
--- a/src/plugins/boot2qt/qdbplugin.cpp
+++ b/src/plugins/boot2qt/qdbplugin.cpp
@@ -48,7 +48,7 @@ static void startFlashingWizard()
if (HostOsInfo::isWindowsHost()) {
if (Process::startDetached({"explorer.exe", {filePath.toUserOutput()}}))
return;
- } else if (Process::startDetached({filePath, {}})) {
+ } else if (Process::startDetached(CommandLine{filePath})) {
return;
}
const QString message = Tr::tr("Flash wizard \"%1\" failed to start.");
diff --git a/src/plugins/clangcodemodel/ClangCodeModel.json.in b/src/plugins/clangcodemodel/ClangCodeModel.json.in
index 4702372b83..36bd5f9375 100644
--- a/src/plugins/clangcodemodel/ClangCodeModel.json.in
+++ b/src/plugins/clangcodemodel/ClangCodeModel.json.in
@@ -14,6 +14,6 @@
],
"Category" : "C++",
"Description" : "Clang Code Model plugin.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp
index 28d00d84a2..62ef434a2e 100644
--- a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp
+++ b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp
@@ -68,6 +68,7 @@ private:
ClangCodeModelPlugin::~ClangCodeModelPlugin()
{
+ m_generatorWatcher.cancel();
m_generatorWatcher.waitForFinished();
}
@@ -140,13 +141,18 @@ void ClangCodeModelPlugin::createCompilationDBAction()
connect(&m_generatorWatcher, &QFutureWatcher<GenerateCompilationDbResult>::finished,
this, [this] {
- const GenerateCompilationDbResult result = m_generatorWatcher.result();
QString message;
- if (result.error.isEmpty()) {
- message = Tr::tr("Clang compilation database generated at \"%1\".")
- .arg(QDir::toNativeSeparators(result.filePath));
+ if (m_generatorWatcher.future().resultCount()) {
+ const GenerateCompilationDbResult result = m_generatorWatcher.result();
+ if (result) {
+ message = Tr::tr("Clang compilation database generated at \"%1\".")
+ .arg(result->toUserOutput());
+ } else {
+ message
+ = Tr::tr("Generating Clang compilation database failed: %1").arg(result.error());
+ }
} else {
- message = Tr::tr("Generating Clang compilation database failed: %1").arg(result.error);
+ message = Tr::tr("Generating Clang compilation database canceled.");
}
MessageManager::writeFlashing(message);
m_generateCompilationDBAction->setEnabled(true);
diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp
index d3ede30b10..32ee429c4d 100644
--- a/src/plugins/clangcodemodel/clangdclient.cpp
+++ b/src/plugins/clangcodemodel/clangdclient.cpp
@@ -46,6 +46,7 @@
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/kitaspects.h>
#include <projectexplorer/project.h>
+#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
@@ -143,7 +144,7 @@ private:
case Qt::ForegroundRole:
if ((detail().endsWith("class") || detail().endsWith("struct"))
&& range().end() == selectionRange().end()) {
- return creatorTheme()->color(Theme::TextColorDisabled);
+ return creatorColor(Theme::TextColorDisabled);
}
break;
}
@@ -208,12 +209,11 @@ static BaseClientInterface *clientInterface(Project *project, const Utils::FileP
"--clang-tidy=0"}};
if (settings.workerThreadLimit() != 0)
cmd.addArg("-j=" + QString::number(settings.workerThreadLimit()));
- if (indexingEnabled && Utils::clangdVersion(clangdExePath) >= QVersionNumber(15)) {
+ if (indexingEnabled) {
cmd.addArg("--background-index-priority="
+ ClangdSettings::priorityToString(indexingPriority));
}
- if (Utils::clangdVersion(clangdExePath) >= QVersionNumber(16))
- cmd.addArg("--rename-file-limit=0");
+ cmd.addArg("--rename-file-limit=0");
if (!jsonDbDir.isEmpty())
cmd.addArg("--compile-commands-dir=" + clangdExePath.withNewMappedPath(jsonDbDir).path());
if (clangdLogServer().isDebugEnabled())
@@ -557,9 +557,7 @@ void ClangdClient::findUsages(const CppEditor::CursorInEditor &cursor,
if (searchTerm.isEmpty())
return;
- if (replacement && versionNumber() >= QVersionNumber(16)
- && Utils::qtcEnvironmentVariable("QTC_CLANGD_RENAMING") != "0") {
-
+ if (replacement && Utils::qtcEnvironmentVariable("QTC_CLANGD_RENAMING") != "0") {
// If we have up-to-date highlighting data, we can prevent giving clangd
// macros or namespaces to rename, which it can't cope with.
// TODO: Fix this upstream for macros; see https://github.com/clangd/clangd/issues/729.
@@ -767,6 +765,15 @@ QList<Text::Range> ClangdClient::additionalDocumentHighlights(
qobject_cast<CppEditor::CppEditorWidget *>(editorWidget), cursor);
}
+bool ClangdClient::shouldSendDidSave(const TextEditor::TextDocument *doc) const
+{
+ for (const Project * const p : ProjectManager::projects()) {
+ if (const Node * const n = p->nodeForFilePath(doc->filePath()))
+ return n->asFileNode() && n->asFileNode()->fileType() == FileType::Header;
+ }
+ return CppEditor::ProjectFile::isHeader(doc->filePath());
+}
+
RefactoringFilePtr ClangdClient::createRefactoringFile(const FilePath &filePath) const
{
return CppEditor::CppRefactoringChanges(CppEditor::CppModelManager::snapshot()).file(filePath);
@@ -1513,7 +1520,7 @@ void ClangdClient::Private::handleSemanticTokens(TextDocument *doc,
return;
}
force = force || isTesting;
- const auto data = highlightingData.find(doc);
+ auto data = highlightingData.find(doc);
if (data != highlightingData.end()) {
if (!force && data->previousTokens.first == tokens
&& data->previousTokens.second == version) {
@@ -1523,61 +1530,50 @@ void ClangdClient::Private::handleSemanticTokens(TextDocument *doc,
data->previousTokens.first = tokens;
data->previousTokens.second = version;
} else {
- highlightingData.insert(doc, {{tokens, version}, {}});
+ data = highlightingData.insert(doc, {{tokens, version}, {}});
}
for (const ExpandedSemanticToken &t : tokens)
qCDebug(clangdLogHighlight()) << '\t' << t.line << t.column << t.length << t.type
<< t.modifiers;
- const auto astHandler = [this, tokens, doc, version](const ClangdAstNode &ast, const MessageId &) {
- FinalizingSubtaskTimer t(highlightingTimer);
- if (!q->documentOpen(doc))
- return;
- if (version != q->documentVersion(doc->filePath())) {
- qCInfo(clangdLogHighlight) << "AST not up to date; aborting highlighting procedure"
- << version << q->documentVersion(doc->filePath());
- return;
- }
- if (clangdLogAst().isDebugEnabled())
- ast.print();
-
- const auto runner = [tokens, filePath = doc->filePath(),
- text = doc->document()->toPlainText(), ast,
- doc = QPointer(doc), rev = doc->document()->revision(),
- clangdVersion = q->versionNumber(),
- this] {
- try {
- 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";
- return QFuture<HighlightingResult>();
- }
- };
+ FinalizingSubtaskTimer ft(highlightingTimer);
+ if (!q->documentOpen(doc))
+ return;
+ if (version != q->documentVersion(doc->filePath())) {
+ qCInfo(clangdLogHighlight) << "AST not up to date; aborting highlighting procedure"
+ << version << q->documentVersion(doc->filePath());
+ return;
+ }
- if (isTesting) {
- const auto watcher = new QFutureWatcher<HighlightingResult>(q);
- connect(watcher, &QFutureWatcher<HighlightingResult>::finished,
- q, [this, watcher, fp = doc->filePath()] {
- emit q->highlightingResultsReady(watcher->future().results(), fp);
- watcher->deleteLater();
- });
- watcher->setFuture(runner());
- return;
+ const auto runner = [tokens, filePath = doc->filePath(),
+ text = doc->document()->toPlainText(),
+ rev = doc->document()->revision(), this] {
+ try {
+ return Utils::asyncRun(doSemanticHighlighting, filePath, tokens, text,
+ rev, highlightingTimer);
+ } catch (const std::exception &e) {
+ qWarning() << "caught" << e.what() << "in main highlighting thread";
+ return QFuture<HighlightingResult>();
}
-
- auto &data = highlightingData[doc];
- if (!data.highlighter)
- data.highlighter = new CppEditor::SemanticHighlighter(doc);
- else
- data.highlighter->updateFormatMapFromFontSettings();
- data.highlighter->setHighlightingRunner(runner);
- data.highlighter->run();
};
- if (q->versionNumber().majorVersion() >= 17)
- astHandler({}, {});
+
+ if (isTesting) {
+ const auto watcher = new QFutureWatcher<HighlightingResult>(q);
+ connect(watcher, &QFutureWatcher<HighlightingResult>::finished,
+ q, [this, watcher, fp = doc->filePath()] {
+ emit q->highlightingResultsReady(watcher->future().results(), fp);
+ watcher->deleteLater();
+ });
+ watcher->setFuture(runner());
+ return;
+ }
+
+ if (!data->highlighter)
+ data->highlighter = new CppEditor::SemanticHighlighter(doc);
else
- getAndHandleAst(doc, astHandler, AstCallbackMode::SyncIfPossible);
+ data->highlighter->updateFormatMapFromFontSettings();
+ data->highlighter->setHighlightingRunner(runner);
+ data->highlighter->run();
}
std::optional<QList<CodeAction> > ClangdDiagnostic::codeActions() const
diff --git a/src/plugins/clangcodemodel/clangdclient.h b/src/plugins/clangcodemodel/clangdclient.h
index c0f687ea1f..5a12c8f56d 100644
--- a/src/plugins/clangcodemodel/clangdclient.h
+++ b/src/plugins/clangcodemodel/clangdclient.h
@@ -148,7 +148,7 @@ private:
bool fileBelongsToProject(const Utils::FilePath &filePath) const override;
QList<Utils::Text::Range> additionalDocumentHighlights(
TextEditor::TextEditorWidget *editorWidget, const QTextCursor &cursor) override;
-
+ bool shouldSendDidSave(const TextEditor::TextDocument *doc) const override;
class Private;
class VirtualFunctionAssistProcessor;
diff --git a/src/plugins/clangcodemodel/clangdfollowsymbol.cpp b/src/plugins/clangcodemodel/clangdfollowsymbol.cpp
index 0da02cba33..2bedda4d98 100644
--- a/src/plugins/clangcodemodel/clangdfollowsymbol.cpp
+++ b/src/plugins/clangcodemodel/clangdfollowsymbol.cpp
@@ -459,8 +459,6 @@ void ClangdFollowSymbol::Private::handleGotoImplementationResult(
// Make a symbol info request for each link to get the class names.
// Also get the AST for the base declaration, so we can find out whether it's
// pure virtual and mark it accordingly.
- // In addition, we need to follow all override links, because for these, clangd
- // gives us the declaration instead of the definition (until clangd 16).
for (const Link &link : std::as_const(allLinks)) {
if (!client->documentForFilePath(link.targetFilePath) && addOpenFile(link.targetFilePath))
client->openExtraFile(link.targetFilePath);
@@ -487,42 +485,6 @@ void ClangdFollowSymbol::Private::handleGotoImplementationResult(
if (link == defLink)
continue;
-
- if (client->versionNumber().majorVersion() >= 17)
- continue;
-
- const TextDocumentIdentifier doc(client->hostPathToServerUri(link.targetFilePath));
- const TextDocumentPositionParams params(doc, pos);
- GotoDefinitionRequest defReq(params);
- defReq.setResponseCallback(
- [this, link, transformLink, sentinel = QPointer(q), reqId = defReq.id()](
- const GotoDefinitionRequest::Response &response) {
- qCDebug(clangdLog) << "handling additional go to definition reply for"
- << link.targetFilePath << link.targetLine;
- if (!sentinel)
- return;
- Link newLink;
- if (std::optional<GotoResult> _result = response.result()) {
- const GotoResult result = _result.value();
- if (const auto ploc = std::get_if<Location>(&result)) {
- newLink = transformLink(*ploc);
- } else if (const auto plloc = std::get_if<QList<Location>>(&result)) {
- if (!plloc->isEmpty())
- newLink = transformLink(plloc->value(0));
- }
- }
- qCDebug(clangdLog) << "def link is" << newLink.targetFilePath << newLink.targetLine;
- declDefMap.insert(link, newLink);
- pendingGotoDefRequests.removeOne(reqId);
- if (pendingSymbolInfoRequests.isEmpty() && pendingGotoDefRequests.isEmpty()
- && defLinkNode.isValid()) {
- handleDocumentInfoResults();
- }
- });
- pendingGotoDefRequests << defReq.id();
- qCDebug(clangdLog) << "sending additional go to definition request"
- << link.targetFilePath << link.targetLine;
- client->sendMessage(defReq, ClangdClient::SendDocUpdates::Ignore);
}
const FilePath defLinkFilePath = defLink.targetFilePath;
diff --git a/src/plugins/clangcodemodel/clangdlocatorfilters.cpp b/src/plugins/clangcodemodel/clangdlocatorfilters.cpp
index 469dfeec95..5bf5915044 100644
--- a/src/plugins/clangcodemodel/clangdlocatorfilters.cpp
+++ b/src/plugins/clangcodemodel/clangdlocatorfilters.cpp
@@ -10,8 +10,6 @@
#include <cppeditor/cppeditortr.h>
#include <cppeditor/cpplocatorfilter.h>
-#include <extensionsystem/pluginmanager.h>
-
#include <languageclient/currentdocumentsymbolsrequest.h>
#include <languageclient/locatorfilter.h>
@@ -186,7 +184,6 @@ static LocatorMatcherTask currentDocumentMatcher()
};
const auto onFilterSetup = [=](Async<void> &async) {
- async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
async.setConcurrentCallData(filterCurrentResults, *storage, *resultStorage,
TextDocument::currentTextDocument()->plainText());
};
diff --git a/src/plugins/clangcodemodel/clangdquickfixes.h b/src/plugins/clangcodemodel/clangdquickfixes.h
index cdd2269266..088fb5e7be 100644
--- a/src/plugins/clangcodemodel/clangdquickfixes.h
+++ b/src/plugins/clangcodemodel/clangdquickfixes.h
@@ -3,7 +3,7 @@
#pragma once
-#include <cppeditor/cppquickfix.h>
+#include <cppeditor/quickfixes/cppquickfix.h>
#include <languageclient/languageclientquickfix.h>
namespace ClangCodeModel {
diff --git a/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp b/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp
index c5bc3d7235..2b52ee4d14 100644
--- a/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp
+++ b/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp
@@ -28,117 +28,19 @@ using namespace TextEditor;
namespace ClangCodeModel::Internal {
Q_LOGGING_CATEGORY(clangdLogHighlight, "qtc.clangcodemodel.clangd.highlight", QtWarningMsg);
-// clangd reports also the #ifs, #elses and #endifs around the disabled code as disabled,
-// and not even in a consistent manner. We don't want this, so we have to clean up here.
-// But note that we require this behavior, as otherwise we would not be able to grey out
-// e.g. empty lines after an #ifdef, due to the lack of symbols.
-static QList<BlockRange> cleanupDisabledCode(HighlightingResults &results, const QTextDocument *doc,
- const QString &docContent)
-{
- QList<BlockRange> ifdefedOutRanges;
- int rangeStartPos = -1;
- for (auto it = results.begin(); it != results.end();) {
- const bool wasIfdefedOut = rangeStartPos != -1;
- const bool isIfDefedOut = it->textStyles.mainStyle == C_DISABLED_CODE;
- if (!isIfDefedOut) {
- if (wasIfdefedOut) {
- const QTextBlock block = doc->findBlockByNumber(it->line - 1);
- ifdefedOutRanges << BlockRange(rangeStartPos, block.position());
- rangeStartPos = -1;
- }
- ++it;
- continue;
- }
-
- if (!wasIfdefedOut)
- rangeStartPos = doc->findBlockByNumber(it->line - 1).position();
-
- // Does the current line contain a potential "ifdefed-out switcher"?
- // If not, no state change is possible and we continue with the next line.
- const auto isPreprocessorControlStatement = [&] {
- const int pos = Utils::Text::positionInText(doc, it->line, it->column);
- const QStringView content = subViewLen(docContent, pos, it->length).trimmed();
- if (content.isEmpty() || content.first() != '#')
- return false;
- int offset = 1;
- while (offset < content.size() && content.at(offset).isSpace())
- ++offset;
- if (offset == content.size())
- return false;
- const QStringView ppDirective = content.mid(offset);
- return ppDirective.startsWith(QLatin1String("if"))
- || ppDirective.startsWith(QLatin1String("elif"))
- || ppDirective.startsWith(QLatin1String("else"))
- || ppDirective.startsWith(QLatin1String("endif"));
- };
- if (!isPreprocessorControlStatement()) {
- ++it;
- continue;
- }
-
- if (!wasIfdefedOut) {
- // The #if or #else that starts disabled code should not be disabled.
- const QTextBlock nextBlock = doc->findBlockByNumber(it->line);
- rangeStartPos = nextBlock.isValid() ? nextBlock.position() : -1;
- it = results.erase(it);
- continue;
- }
-
- if (wasIfdefedOut && (it + 1 == results.end()
- || (it + 1)->textStyles.mainStyle != C_DISABLED_CODE
- || (it + 1)->line != it->line + 1)) {
- // The #else or #endif that ends disabled code should not be disabled.
- const QTextBlock block = doc->findBlockByNumber(it->line - 1);
- ifdefedOutRanges << BlockRange(rangeStartPos, block.position());
- rangeStartPos = -1;
- it = results.erase(it);
- continue;
- }
- ++it;
- }
-
- if (rangeStartPos != -1)
- ifdefedOutRanges << BlockRange(rangeStartPos, doc->characterCount());
-
- qCDebug(clangdLogHighlight) << "found" << ifdefedOutRanges.size() << "ifdefed-out ranges";
- if (clangdLogHighlight().isDebugEnabled()) {
- for (const BlockRange &r : std::as_const(ifdefedOutRanges))
- qCDebug(clangdLogHighlight) << r.first() << r.last();
- }
-
- return ifdefedOutRanges;
-}
-
class ExtraHighlightingResultsCollector
{
public:
- ExtraHighlightingResultsCollector(QPromise<HighlightingResult> &promise,
- HighlightingResults &results,
- const Utils::FilePath &filePath, const ClangdAstNode &ast,
- const QTextDocument *doc, const QString &docContent,
- const QVersionNumber &clangdVersion);
+ ExtraHighlightingResultsCollector(HighlightingResults &results,
+ const Utils::FilePath &filePath,
+ const QTextDocument *doc, const QString &docContent);
void collect();
private:
- static bool lessThan(const HighlightingResult &r1, const HighlightingResult &r2);
- static int onlyIndexOf(const QStringView &text, const QStringView &subString, int from = 0);
- int posForNodeStart(const ClangdAstNode &node) const;
- int posForNodeEnd(const ClangdAstNode &node) const;
- void insertResult(const HighlightingResult &result);
- void insertResult(const ClangdAstNode &node, TextStyle style);
- void insertAngleBracketInfo(int searchStart1, int searchEnd1, int searchStart2, int searchEnd2);
- void setResultPosFromRange(HighlightingResult &result, const Range &range);
- void collectFromNode(const ClangdAstNode &node);
- void visitNode(const ClangdAstNode&node);
-
- QPromise<HighlightingResult> &m_promise;
HighlightingResults &m_results;
const Utils::FilePath m_filePath;
- const ClangdAstNode &m_ast;
const QTextDocument * const m_doc;
const QString &m_docContent;
- const int m_clangdVersion;
- ClangdAstNode::FileStatus m_currentFileStatus = ClangdAstNode::FileStatus::Unknown;
};
void doSemanticHighlighting(
@@ -146,10 +48,7 @@ void doSemanticHighlighting(
const Utils::FilePath &filePath,
const QList<ExpandedSemanticToken> &tokens,
const QString &docContents,
- const ClangdAstNode &ast,
- const QPointer<TextDocument> &textDocument,
int docRevision,
- const QVersionNumber &clangdVersion,
const TaskTimer &taskTimer)
{
ThreadedSubtaskTimer t("highlighting", taskTimer);
@@ -157,111 +56,6 @@ void doSemanticHighlighting(
return;
const QTextDocument doc(docContents);
- const auto tokenRange = [&doc](const ExpandedSemanticToken &token) {
- const Position startPos(token.line - 1, token.column - 1);
- const Position endPos = startPos.withOffset(token.length, &doc);
- return Range(startPos, endPos);
- };
- const int clangdMajorVersion = clangdVersion.majorVersion();
- const auto isOutputParameter = [&ast, &tokenRange, clangdMajorVersion]
- (const ExpandedSemanticToken &token) {
- if (token.modifiers.contains(QLatin1String("usedAsMutableReference")))
- return true;
- if (token.modifiers.contains(QLatin1String("usedAsMutablePointer")))
- return true;
- if (clangdMajorVersion >= 16)
- return false;
- if (token.type != "variable" && token.type != "property" && token.type != "parameter")
- return false;
- const Range range = tokenRange(token);
- const ClangdAstPath path = getAstPath(ast, range);
- if (path.size() < 2)
- return false;
- if (token.type == "property"
- && (path.rbegin()->kind() == "MemberInitializer"
- || path.rbegin()->kind() == "CXXConstruct")) {
- return false;
- }
- if (path.rbegin()->hasConstType())
- return false;
- for (auto it = path.rbegin() + 1; it != path.rend(); ++it) {
- if (it->kind() == "CXXConstruct" || it->kind() == "MemberInitializer")
- return true;
-
- if (it->kind() == "Call") {
- // The first child is e.g. a called lambda or an object on which
- // the call happens, and should not be highlighted as an output argument.
- // If the call is not fully resolved (as in templates), we don't
- // know whether the argument is passed as const or not.
- if (it->arcanaContains("dependent type"))
- return false;
- const QList<ClangdAstNode> children = it->children().value_or(QList<ClangdAstNode>());
- return children.isEmpty()
- || (children.first().range() != (it - 1)->range()
- && children.first().kind() != "UnresolvedLookup");
- }
-
- // The token should get marked for e.g. lambdas, but not for assignment operators,
- // where the user sees that it's being written.
- if (it->kind() == "CXXOperatorCall") {
- const QList<ClangdAstNode> children = it->children().value_or(QList<ClangdAstNode>());
-
- // Child 1 is the call itself, Child 2 is the named entity on which the call happens
- // (a lambda or a class instance), after that follow the actual call arguments.
- if (children.size() < 2)
- return false;
-
- // The call itself is never modifiable.
- if (children.first().range() == range)
- return false;
-
- // The callable is never displayed as an output parameter.
- // TODO: A good argument can be made to display objects on which a non-const
- // operator or function is called as output parameters.
- if (children.at(1).range().contains(range))
- return false;
-
- QList<ClangdAstNode> firstChildTree{children.first()};
- while (!firstChildTree.isEmpty()) {
- const ClangdAstNode n = firstChildTree.takeFirst();
- const QString detail = n.detail().value_or(QString());
- if (detail.startsWith("operator")) {
- return !detail.contains('=')
- && !detail.contains("++") && !detail.contains("--")
- && !detail.contains("<<") && !detail.contains(">>")
- && !detail.contains("*");
- }
- firstChildTree << n.children().value_or(QList<ClangdAstNode>());
- }
- return true;
- }
-
- if (it->kind() == "Lambda")
- return false;
- if (it->kind() == "BinaryOperator")
- return false;
- if (it->hasConstType())
- return false;
-
- if (it->kind() == "CXXMemberCall") {
- if (it == path.rbegin())
- return false;
- const QList<ClangdAstNode> children = it->children().value_or(QList<ClangdAstNode>());
- QTC_ASSERT(!children.isEmpty(), return false);
-
- // The called object is never displayed as an output parameter.
- // TODO: A good argument can be made to display objects on which a non-const
- // operator or function is called as output parameters.
- return (it - 1)->range() != children.first().range();
- }
-
- if (it->kind() == "Member" && it->arcanaContains("(")
- && !it->arcanaContains("bound member function type")) {
- return false;
- }
- }
- return false;
- };
const std::function<HighlightingResult(const ExpandedSemanticToken &)> toResult
= [&](const ExpandedSemanticToken &token) {
@@ -278,40 +72,12 @@ void doSemanticHighlighting(
} else if (token.type == "function" || token.type == "method") {
styles.mainStyle = token.modifiers.contains(QLatin1String("virtual"))
? C_VIRTUAL_METHOD : C_FUNCTION;
- if (token.modifiers.contains("definition")) {
+ if (token.modifiers.contains("definition"))
styles.mixinStyles.push_back(C_FUNCTION_DEFINITION);
- } else if (clangdMajorVersion < 16 && ast.isValid()) {
- const ClangdAstPath path = getAstPath(ast, tokenRange(token));
- if (path.length() > 1) {
- const ClangdAstNode declNode = path.at(path.length() - 2);
- if ((declNode.kind() == "Function" || declNode.kind() == "CXXMethod")
- && declNode.hasChildWithRole("statement")) {
- styles.mixinStyles.push_back(C_FUNCTION_DEFINITION);
- }
- }
- }
} else if (token.type == "class") {
styles.mainStyle = C_TYPE;
- if (token.modifiers.contains("constructorOrDestructor")) {
+ if (token.modifiers.contains("constructorOrDestructor"))
styles.mainStyle = C_FUNCTION;
- } else if (clangdMajorVersion < 16 && ast.isValid()) {
- const ClangdAstPath path = getAstPath(ast, tokenRange(token));
- if (!path.isEmpty()) {
- if (path.last().kind() == "CXXConstructor") {
- if (!path.last().arcanaContains("implicit"))
- styles.mainStyle = C_FUNCTION;
- } else if (path.last().kind() == "Record" && path.length() > 1) {
- const ClangdAstNode node = path.at(path.length() - 2);
- if (node.kind() == "CXXDestructor" && !node.arcanaContains("implicit")) {
- styles.mainStyle = C_FUNCTION;
-
- // https://github.com/clangd/clangd/issues/872
- if (node.role() == "declaration")
- styles.mixinStyles.push_back(C_DECLARATION);
- }
- }
- }
- }
} else if (token.type == "comment") { // "comment" means code disabled via the preprocessor
styles.mainStyle = C_DISABLED_CODE;
} else if (token.type == "namespace") {
@@ -384,8 +150,10 @@ void doSemanticHighlighting(
styles.mainStyle = C_STATIC_MEMBER;
}
}
- if (isOutputParameter(token))
+ if (token.modifiers.contains(QLatin1String("usedAsMutableReference"))
+ || token.modifiers.contains(QLatin1String("usedAsMutablePointer"))) {
styles.mixinStyles.push_back(C_OUTPUT_ARGUMENT);
+ }
return HighlightingResult(token.line, token.column, token.length, styles);
};
@@ -398,24 +166,13 @@ void doSemanticHighlighting(
}
};
auto results = QtConcurrent::blockingMapped<HighlightingResults>(tokens, safeToResult);
- const bool handleInactiveCode = clangdMajorVersion < 17;
- QList<BlockRange> ifdefedOutBlocks;
- if (handleInactiveCode)
- ifdefedOutBlocks = cleanupDisabledCode(results, &doc, docContents);
- ExtraHighlightingResultsCollector(promise, results, filePath, ast, &doc, docContents,
- clangdVersion).collect();
+ ExtraHighlightingResultsCollector(results, filePath, &doc, docContents).collect();
Utils::erase(results, [](const HighlightingResult &res) {
// QTCREATORBUG-28639
return res.textStyles.mainStyle == C_TEXT && res.textStyles.mixinStyles.empty();
});
if (!promise.isCanceled()) {
qCInfo(clangdLogHighlight) << "reporting" << results.size() << "highlighting results";
- if (handleInactiveCode) {
- QMetaObject::invokeMethod(textDocument, [textDocument, ifdefedOutBlocks, docRevision] {
- if (textDocument && textDocument->document()->revision() == docRevision)
- textDocument->setIfdefedOutBlocks(ifdefedOutBlocks);
- }, Qt::QueuedConnection);
- }
QList<Range> virtualRanges;
for (const HighlightingResult &r : results) {
qCDebug(clangdLogHighlight)
@@ -440,11 +197,11 @@ void doSemanticHighlighting(
}
ExtraHighlightingResultsCollector::ExtraHighlightingResultsCollector(
- QPromise<HighlightingResult> &promise, HighlightingResults &results,
- const Utils::FilePath &filePath, const ClangdAstNode &ast, const QTextDocument *doc,
- const QString &docContent, const QVersionNumber &clangdVersion)
- : m_promise(promise), m_results(results), m_filePath(filePath), m_ast(ast), m_doc(doc),
- m_docContent(docContent), m_clangdVersion(clangdVersion.majorVersion())
+ HighlightingResults &results,
+ const Utils::FilePath &filePath, const QTextDocument *doc,
+ const QString &docContent)
+ : m_results(results), m_filePath(filePath), m_doc(doc),
+ m_docContent(docContent)
{
}
@@ -469,497 +226,6 @@ void ExtraHighlightingResultsCollector::collect()
for (const HighlightingResult &newRes : propHighlighter.highlight())
m_results.insert(++i, newRes);
}
-
- if (!m_ast.isValid())
- return;
- QTC_ASSERT(m_clangdVersion < 17, return);
- visitNode(m_ast);
-}
-
-bool ExtraHighlightingResultsCollector::lessThan(const HighlightingResult &r1,
- const HighlightingResult &r2)
-{
- return r1.line < r2.line || (r1.line == r2.line && r1.column < r2.column)
- || (r1.line == r2.line && r1.column == r2.column && r1.length < r2.length);
-}
-
-int ExtraHighlightingResultsCollector::onlyIndexOf(const QStringView &text,
- const QStringView &subString, int from)
-{
- const int firstIndex = text.indexOf(subString, from);
- if (firstIndex == -1)
- return -1;
- const int nextIndex = text.indexOf(subString, firstIndex + 1);
-
- // The second condion deals with the off-by-one error in TemplateSpecialization nodes;
- // see collectFromNode().
- return nextIndex == -1 || nextIndex == firstIndex + 1 ? firstIndex : -1;
-}
-
-// Unfortunately, the exact position of a specific token is usually not
-// recorded in the AST, so if we need that, we have to search for it textually.
-// In corner cases, this might get sabotaged by e.g. comments, in which case we give up.
-int ExtraHighlightingResultsCollector::posForNodeStart(const ClangdAstNode &node) const
-{
- return Utils::Text::positionInText(m_doc, node.range().start().line() + 1,
- node.range().start().character() + 1);
-}
-
-int ExtraHighlightingResultsCollector::posForNodeEnd(const ClangdAstNode &node) const
-{
- return Utils::Text::positionInText(m_doc, node.range().end().line() + 1,
- node.range().end().character() + 1);
-}
-
-void ExtraHighlightingResultsCollector::insertResult(const HighlightingResult &result)
-{
- if (!result.isValid()) // Some nodes don't have a range.
- return;
- const auto it = std::lower_bound(m_results.begin(), m_results.end(), result, lessThan);
- if (it == m_results.end() || *it != result) {
-
- // Prevent inserting expansions for function-like macros. For instance:
- // #define TEST() "blubb"
- // const char *s = TEST();
- // The macro name is always shorter than the expansion and starts at the same
- // location, so it should occur right before the insertion position.
- if (it > m_results.begin() && (it - 1)->line == result.line
- && (it - 1)->column == result.column
- && (it - 1)->textStyles.mainStyle == C_MACRO) {
- return;
- }
-
- // Bogus ranges; e.g. QTCREATORBUG-27601
- if (it != m_results.end()) {
- const int nextStartPos = Utils::Text::positionInText(m_doc, it->line, it->column);
- const int resultEndPos = Utils::Text::positionInText(m_doc, result.line, result.column)
- + result.length;
- if (resultEndPos > nextStartPos)
- return;
- }
-
- qCDebug(clangdLogHighlight) << "adding additional highlighting result"
- << result.line << result.column << result.length;
- m_results.insert(it, result);
- return;
- }
-}
-
-void ExtraHighlightingResultsCollector::insertResult(const ClangdAstNode &node, TextStyle style)
-{
- HighlightingResult result;
- result.useTextSyles = true;
- result.textStyles.mainStyle = style;
- setResultPosFromRange(result, node.range());
- insertResult(result);
- return;
-}
-
-// For matching the "<" and ">" brackets of template declarations, specializations
-// and instantiations.
-void ExtraHighlightingResultsCollector::insertAngleBracketInfo(int searchStart1, int searchEnd1,
- int searchStart2, int searchEnd2)
-{
- const int openingAngleBracketPos = onlyIndexOf(
- subViewEnd(m_docContent, searchStart1, searchEnd1),
- QStringView(QStringLiteral("<")));
- if (openingAngleBracketPos == -1)
- return;
- const int absOpeningAngleBracketPos = searchStart1 + openingAngleBracketPos;
- if (absOpeningAngleBracketPos > searchStart2)
- searchStart2 = absOpeningAngleBracketPos + 1;
- if (searchStart2 >= searchEnd2)
- return;
- const int closingAngleBracketPos = onlyIndexOf(
- subViewEnd(m_docContent, searchStart2, searchEnd2),
- QStringView(QStringLiteral(">")));
- if (closingAngleBracketPos == -1)
- return;
-
- const int absClosingAngleBracketPos = searchStart2 + closingAngleBracketPos;
- if (absOpeningAngleBracketPos > absClosingAngleBracketPos)
- return;
-
- HighlightingResult result;
- result.useTextSyles = true;
- result.textStyles.mainStyle = C_PUNCTUATION;
- Utils::Text::convertPosition(m_doc, absOpeningAngleBracketPos, &result.line, &result.column);
- ++result.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);
-}
-
-void ExtraHighlightingResultsCollector::setResultPosFromRange(HighlightingResult &result,
- const Range &range)
-{
- if (!range.isValid())
- return;
- const Position startPos = range.start();
- const Position endPos = range.end();
- result.line = startPos.line() + 1;
- result.column = startPos.character() + 1;
- result.length = endPos.toPositionInDocument(m_doc) - startPos.toPositionInDocument(m_doc);
-}
-
-void ExtraHighlightingResultsCollector::collectFromNode(const ClangdAstNode &node)
-{
- if (node.kind().endsWith("Literal"))
- return;
- if (node.role() == "type" && node.kind() == "Builtin")
- return;
-
- if (m_clangdVersion < 16 && node.role() == "attribute"
- && (node.kind() == "Override" || node.kind() == "Final")) {
- insertResult(node, C_KEYWORD);
- return;
- }
-
- const bool isExpression = node.role() == "expression";
- if (m_clangdVersion < 16 && isExpression && node.kind() == "Predefined") {
- insertResult(node, C_LOCAL);
- return;
- }
-
- const bool isDeclaration = node.role() == "declaration";
- const int nodeStartPos = posForNodeStart(node);
- const int nodeEndPos = posForNodeEnd(node);
- const QList<ClangdAstNode> children = node.children().value_or(QList<ClangdAstNode>());
-
- // Match question mark and colon in ternary operators.
- if (m_clangdVersion < 16 && isExpression && node.kind() == "ConditionalOperator") {
- if (children.size() != 3)
- return;
-
- // The question mark is between sub-expressions 1 and 2, the colon is between
- // sub-expressions 2 and 3.
- const int searchStartPosQuestionMark = posForNodeEnd(children.first());
- const int searchEndPosQuestionMark = posForNodeStart(children.at(1));
- QStringView content = subViewEnd(m_docContent, searchStartPosQuestionMark,
- searchEndPosQuestionMark);
- const int questionMarkPos = onlyIndexOf(content, QStringView(QStringLiteral("?")));
- if (questionMarkPos == -1)
- return;
- const int searchStartPosColon = posForNodeEnd(children.at(1));
- const int searchEndPosColon = posForNodeStart(children.at(2));
- content = subViewEnd(m_docContent, searchStartPosColon, searchEndPosColon);
- const int colonPos = onlyIndexOf(content, QStringView(QStringLiteral(":")));
- if (colonPos == -1)
- return;
-
- const int absQuestionMarkPos = searchStartPosQuestionMark + questionMarkPos;
- const int absColonPos = searchStartPosColon + colonPos;
- if (absQuestionMarkPos > absColonPos)
- return;
-
- HighlightingResult result;
- result.useTextSyles = true;
- result.textStyles.mainStyle = C_PUNCTUATION;
- result.textStyles.mixinStyles.push_back(C_OPERATOR);
- Utils::Text::convertPosition(m_doc, absQuestionMarkPos, &result.line, &result.column);
- ++result.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;
- }
-
- if (isDeclaration && (node.kind() == "FunctionTemplate" || node.kind() == "ClassTemplate")) {
- // The child nodes are the template parameters and and the function or class.
- // The opening angle bracket is before the first child node, the closing angle
- // bracket is before the function child node and after the last param node.
- const QString classOrFunctionKind = QLatin1String(node.kind() == "FunctionTemplate"
- ? "Function" : "CXXRecord");
- const auto functionOrClassIt = std::find_if(children.begin(), children.end(),
- [&classOrFunctionKind](const ClangdAstNode &n) {
- return n.role() == "declaration" && n.kind() == classOrFunctionKind;
- });
- if (functionOrClassIt == children.end() || functionOrClassIt == children.begin())
- return;
- const int firstTemplateParamStartPos = posForNodeStart(children.first());
- const int lastTemplateParamEndPos = posForNodeEnd(*(functionOrClassIt - 1));
- const int functionOrClassStartPos = posForNodeStart(*functionOrClassIt);
- insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos,
- lastTemplateParamEndPos, functionOrClassStartPos);
- return;
- }
-
- const auto isTemplateParamDecl = [](const ClangdAstNode &node) {
- return node.isTemplateParameterDeclaration();
- };
- if (isDeclaration && node.kind() == "TypeAliasTemplate") {
- // Children are one node of type TypeAlias and the template parameters.
- // The opening angle bracket is before the first parameter and the closing
- // angle bracket is after the last parameter.
- // The TypeAlias node seems to appear first in the AST, even though lexically
- // is comes after the parameters. We don't rely on the order here.
- // Note that there is a second pair of angle brackets. That one is part of
- // a TemplateSpecialization, which is handled further below.
- const auto firstTemplateParam = std::find_if(children.begin(), children.end(),
- isTemplateParamDecl);
- if (firstTemplateParam == children.end())
- return;
- const auto lastTemplateParam = std::find_if(children.rbegin(), children.rend(),
- isTemplateParamDecl);
- QTC_ASSERT(lastTemplateParam != children.rend(), return);
- const auto typeAlias = std::find_if(children.begin(), children.end(),
- [](const ClangdAstNode &n) { return n.kind() == "TypeAlias"; });
- if (typeAlias == children.end())
- return;
-
- const int firstTemplateParamStartPos = posForNodeStart(*firstTemplateParam);
- const int lastTemplateParamEndPos = posForNodeEnd(*lastTemplateParam);
- const int searchEndPos = posForNodeStart(*typeAlias);
- insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos,
- lastTemplateParamEndPos, searchEndPos);
- return;
- }
-
- if (isDeclaration && node.kind() == "ClassTemplateSpecialization") {
- // There is one child of kind TemplateSpecialization. The first pair
- // of angle brackets comes before that.
- if (children.size() == 1) {
- const int childNodePos = posForNodeStart(children.first());
- insertAngleBracketInfo(nodeStartPos, childNodePos, nodeStartPos, childNodePos);
- }
- return;
- }
-
- if (isDeclaration && node.kind() == "TemplateTemplateParm") {
- // The child nodes are template arguments and template parameters.
- // Arguments seem to appear before parameters in the AST, even though they
- // come after them in the source code. We don't rely on the order here.
- const auto firstTemplateParam = std::find_if(children.begin(), children.end(),
- isTemplateParamDecl);
- if (firstTemplateParam == children.end())
- return;
- const auto lastTemplateParam = std::find_if(children.rbegin(), children.rend(),
- isTemplateParamDecl);
- QTC_ASSERT(lastTemplateParam != children.rend(), return);
- const auto templateArg = std::find_if(children.begin(), children.end(),
- [](const ClangdAstNode &n) { return n.role() == "template argument"; });
-
- const int firstTemplateParamStartPos = posForNodeStart(*firstTemplateParam);
- const int lastTemplateParamEndPos = posForNodeEnd(*lastTemplateParam);
- const int searchEndPos = templateArg == children.end()
- ? nodeEndPos : posForNodeStart(*templateArg);
- insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos,
- lastTemplateParamEndPos, searchEndPos);
- return;
- }
-
- // {static,dynamic,reinterpret}_cast<>().
- if (isExpression && node.kind().startsWith("CXX") && node.kind().endsWith("Cast")) {
- // First child is type, second child is expression.
- // The opening angle bracket is before the first child, the closing angle bracket
- // is between the two children.
- if (children.size() == 2) {
- insertAngleBracketInfo(nodeStartPos, posForNodeStart(children.first()),
- posForNodeEnd(children.first()),
- posForNodeStart(children.last()));
- }
- return;
- }
-
- if (node.kind() == "TemplateSpecialization") {
- // First comes the template type, then the template arguments.
- // The opening angle bracket is before the first template argument,
- // the closing angle bracket is after the last template argument.
- // The first child node has no range, so we start searching at the parent node.
- if (children.size() >= 2) {
- int searchStart2 = posForNodeEnd(children.last());
- int searchEnd2 = nodeEndPos;
-
- // There is a weird off-by-one error on the clang side: If there is a
- // nested template instantiation *and* there is no space between
- // the closing angle brackets, then the inner TemplateSpecialization node's range
- // will extend one character too far, covering the outer's closing angle bracket.
- // This is what we are correcting for here.
- // This issue is tracked at https://github.com/clangd/clangd/issues/871.
- if (searchStart2 == searchEnd2)
- --searchStart2;
- insertAngleBracketInfo(nodeStartPos, posForNodeStart(children.at(1)),
- searchStart2, searchEnd2);
- }
- return;
- }
-
- if (!isExpression && !isDeclaration)
- return;
-
- if (m_clangdVersion >= 16)
- return;
-
- // Operators, overloaded ones in particular.
- static const QString operatorPrefix = "operator";
- QString detail = node.detail().value_or(QString());
- const bool isCallToNew = node.kind() == "CXXNew";
- const bool isCallToDelete = node.kind() == "CXXDelete";
- const auto isProperOperator = [&] {
- if (isCallToNew || isCallToDelete)
- return true;
- if (!detail.startsWith(operatorPrefix))
- return false;
- if (detail == operatorPrefix)
- return false;
- const QChar nextChar = detail.at(operatorPrefix.length());
- return !nextChar.isLetterOrNumber() && nextChar != '_';
- };
- if (!isProperOperator())
- return;
-
- if (!isCallToNew && !isCallToDelete)
- detail.remove(0, operatorPrefix.length());
-
- if (node.kind() == "CXXConversion")
- return;
-
- HighlightingResult result;
- result.useTextSyles = true;
- const bool isOverloaded = isDeclaration || ((!isCallToNew && !isCallToDelete)
- || node.arcanaContains("CXXMethod"));
- result.textStyles.mainStyle = isCallToNew || isCallToDelete || detail.at(0).isSpace()
- ? C_KEYWORD : C_PUNCTUATION;
- result.textStyles.mixinStyles.push_back(C_OPERATOR);
- if (isOverloaded)
- result.textStyles.mixinStyles.push_back(C_OVERLOADED_OPERATOR);
- if (isDeclaration)
- result.textStyles.mixinStyles.push_back(C_DECLARATION);
-
- const QStringView nodeText = subViewEnd(m_docContent, nodeStartPos, nodeEndPos);
-
- if (isCallToNew || isCallToDelete) {
- result.line = node.range().start().line() + 1;
- result.column = node.range().start().character() + 1;
- result.length = isCallToNew ? 3 : 6;
- insertResult(result);
- if (node.arcanaContains("array")) {
- const int openingBracketOffset = nodeText.indexOf('[');
- if (openingBracketOffset == -1)
- return;
- const int closingBracketOffset = nodeText.lastIndexOf(']');
- if (closingBracketOffset == -1 || closingBracketOffset < openingBracketOffset)
- return;
-
- result.textStyles.mainStyle = C_PUNCTUATION;
- result.length = 1;
- Utils::Text::convertPosition(m_doc,
- nodeStartPos + openingBracketOffset,
- &result.line, &result.column);
- ++result.column;
- insertResult(result);
- Utils::Text::convertPosition(m_doc,
- nodeStartPos + closingBracketOffset,
- &result.line, &result.column);
- ++result.column;
- insertResult(result);
- }
- return;
- }
-
- if (isExpression && (detail == QLatin1String("()") || detail == QLatin1String("[]"))) {
- result.line = node.range().start().line() + 1;
- result.column = node.range().start().character() + 1;
- result.length = 1;
- insertResult(result);
- result.line = node.range().end().line() + 1;
- result.column = node.range().end().character();
- insertResult(result);
- return;
- }
-
- const int opStringLen = detail.at(0).isSpace() ? detail.length() - 1 : detail.length();
-
- // The simple case: Call to operator+, +=, * etc.
- if (nodeEndPos - nodeStartPos == opStringLen) {
- setResultPosFromRange(result, node.range());
- insertResult(result);
- return;
- }
-
- const int prefixOffset = nodeText.indexOf(operatorPrefix);
- if (prefixOffset == -1)
- return;
-
- const bool isArray = detail == "[]";
- const bool isCall = detail == "()";
- const bool isArrayNew = detail == " new[]";
- const bool isArrayDelete = detail == " delete[]";
- const QStringView searchTerm = isArray || isCall
- ? QStringView(detail).chopped(1) : isArrayNew || isArrayDelete
- ? QStringView(detail).chopped(2) : detail;
- const int opStringOffset = nodeText.indexOf(searchTerm, prefixOffset
- + operatorPrefix.length());
- if (opStringOffset == -1 || nodeText.indexOf(operatorPrefix, opStringOffset) != -1)
- return;
-
- const int opStringOffsetInDoc = nodeStartPos + opStringOffset
- + detail.length() - opStringLen;
- Utils::Text::convertPosition(m_doc, opStringOffsetInDoc, &result.line, &result.column);
- ++result.column;
- result.length = opStringLen;
- if (isArray || isCall)
- result.length = 1;
- else if (isArrayNew || isArrayDelete)
- result.length -= 2;
- if (!isArray && !isCall)
- insertResult(result);
- if (!isArray && !isCall && !isArrayNew && !isArrayDelete)
- return;
-
- result.textStyles.mainStyle = C_PUNCTUATION;
- result.length = 1;
- const int openingParenOffset = nodeText.indexOf(
- isCall ? '(' : '[', prefixOffset + operatorPrefix.length());
- if (openingParenOffset == -1)
- return;
- const int closingParenOffset = nodeText.indexOf(isCall ? ')' : ']', openingParenOffset + 1);
- if (closingParenOffset == -1 || closingParenOffset < openingParenOffset)
- return;
- Utils::Text::convertPosition(m_doc, nodeStartPos + openingParenOffset,
- &result.line, &result.column);
- ++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_promise.isCanceled())
- return;
- const ClangdAstNode::FileStatus prevFileStatus = m_currentFileStatus;
- m_currentFileStatus = node.fileStatus(m_filePath);
- if (m_currentFileStatus == ClangdAstNode::FileStatus::Unknown
- && prevFileStatus != ClangdAstNode::FileStatus::Ours) {
- m_currentFileStatus = prevFileStatus;
- }
- switch (m_currentFileStatus) {
- case ClangdAstNode::FileStatus::Ours:
- case ClangdAstNode::FileStatus::Unknown:
- collectFromNode(node);
- [[fallthrough]];
- case ClangdAstNode::FileStatus::Foreign:
- case ClangCodeModel::Internal::ClangdAstNode::FileStatus::Mixed: {
- const auto children = node.children();
- if (!children)
- return;
- for (const ClangdAstNode &childNode : *children)
- visitNode(childNode);
- break;
- }
- }
- m_currentFileStatus = prevFileStatus;
}
class InactiveRegionsParams : public JsonObject
diff --git a/src/plugins/clangcodemodel/clangdsemantichighlighting.h b/src/plugins/clangcodemodel/clangdsemantichighlighting.h
index 285ba2323e..b303f920f3 100644
--- a/src/plugins/clangcodemodel/clangdsemantichighlighting.h
+++ b/src/plugins/clangcodemodel/clangdsemantichighlighting.h
@@ -33,10 +33,7 @@ void doSemanticHighlighting(
const Utils::FilePath &filePath,
const QList<LanguageClient::ExpandedSemanticToken> &tokens,
const QString &docContents,
- const ClangdAstNode &ast,
- const QPointer<TextEditor::TextDocument> &textDocument,
int docRevision,
- const QVersionNumber &clangdVersion,
const TaskTimer &taskTimer
);
diff --git a/src/plugins/clangcodemodel/clangfixitoperation.cpp b/src/plugins/clangcodemodel/clangfixitoperation.cpp
index e173592ec0..802bdd17bb 100644
--- a/src/plugins/clangcodemodel/clangfixitoperation.cpp
+++ b/src/plugins/clangcodemodel/clangfixitoperation.cpp
@@ -71,10 +71,7 @@ QString ClangFixItOperation::firstRefactoringFileContent_forTestOnly() const
void ClangFixItOperation::applyFixitsToFile(TextEditor::RefactoringFile &refactoringFile,
const QList<ClangFixIt> fixIts)
{
- const Utils::ChangeSet changeSet = toChangeSet(refactoringFile, fixIts);
-
- refactoringFile.setChangeSet(changeSet);
- refactoringFile.apply();
+ refactoringFile.apply(toChangeSet(refactoringFile, fixIts));
}
Utils::ChangeSet ClangFixItOperation::toChangeSet(
diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
index a68f566309..f800979eae 100644
--- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
+++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
@@ -540,11 +540,15 @@ void ClangModelManagerSupport::updateLanguageClient(Project *project)
generatorWatcher->deleteLater();
if (!isProjectDataUpToDate(project, projectInfo, jsonDbDir))
return;
- const GenerateCompilationDbResult result = generatorWatcher->result();
- if (!result.error.isEmpty()) {
+ if (generatorWatcher->future().resultCount() == 0) {
MessageManager::writeDisrupting(
- Tr::tr("Cannot use clangd: Failed to generate compilation database:\n%1")
- .arg(result.error));
+ Tr::tr("Cannot use clangd: Generating compilation database canceled."));
+ return;
+ }
+ const GenerateCompilationDbResult result = generatorWatcher->result();
+ if (!result) {
+ MessageManager::writeDisrupting(Tr::tr("Cannot use clangd: "
+ "Failed to generate compilation database:\n%1").arg(result.error()));
return;
}
Id previousId;
@@ -724,8 +728,8 @@ void ClangModelManagerSupport::updateStaleIndexEntries()
const QDateTime sourceIndexedTime = indexFilesIt->minLastModifiedTime;
bool rescan = false;
- QSet<FilePath> allIncludes = snapshot.allIncludesForDocument(sourceFile);
- for (const FilePath &includeFile : qAsConst(allIncludes)) {
+ const QSet<FilePath> allIncludes = snapshot.allIncludesForDocument(sourceFile);
+ for (const FilePath &includeFile : allIncludes) {
auto includeFileTimeIt = lastModifiedCache.find(includeFile);
if (includeFileTimeIt == lastModifiedCache.end()) {
includeFileTimeIt = lastModifiedCache.insert(includeFile,
diff --git a/src/plugins/clangcodemodel/clangutils.cpp b/src/plugins/clangcodemodel/clangutils.cpp
index 1b2550cbd3..b27117f056 100644
--- a/src/plugins/clangcodemodel/clangutils.cpp
+++ b/src/plugins/clangcodemodel/clangutils.cpp
@@ -146,23 +146,26 @@ static QJsonObject createFileObject(const FilePath &buildDir,
return fileObject;
}
-GenerateCompilationDbResult generateCompilationDB(QList<ProjectInfo::ConstPtr> projectInfoList,
- FilePath baseDir,
- CompilationDbPurpose purpose,
- ClangDiagnosticConfig warningsConfig,
- QStringList projectOptions,
- FilePath clangIncludeDir)
+void generateCompilationDB(
+ QPromise<expected_str<FilePath>> &promise,
+ const QList<ProjectInfo::ConstPtr> &projectInfoList,
+ const FilePath &baseDir,
+ CompilationDbPurpose purpose,
+ const ClangDiagnosticConfig &warningsConfig,
+ const QStringList &projectOptions,
+ const FilePath &clangIncludeDir)
{
- QTC_ASSERT(!baseDir.isEmpty(), return GenerateCompilationDbResult(QString(),
- Tr::tr("Could not retrieve build directory.")));
+ QTC_ASSERT(!baseDir.isEmpty(),
+ promise.addResult(make_unexpected(Tr::tr("Could not retrieve build directory."))); return);
QTC_ASSERT(!projectInfoList.isEmpty(),
- return GenerateCompilationDbResult(QString(), "Could not retrieve project info."));
+ promise.addResult(make_unexpected(Tr::tr("Could not retrieve project info."))); return);
QTC_CHECK(baseDir.ensureWritableDir());
QFile compileCommandsFile(baseDir.pathAppended("compile_commands.json").toFSPathString());
const bool fileOpened = compileCommandsFile.open(QIODevice::WriteOnly | QIODevice::Truncate);
if (!fileOpened) {
- return GenerateCompilationDbResult(QString(), Tr::tr("Could not create \"%1\": %2")
- .arg(compileCommandsFile.fileName(), compileCommandsFile.errorString()));
+ promise.addResult(make_unexpected(Tr::tr("Could not create \"%1\": %2")
+ .arg(compileCommandsFile.fileName(), compileCommandsFile.errorString())));
+ return;
}
compileCommandsFile.write("[");
@@ -182,6 +185,8 @@ GenerateCompilationDbResult generateCompilationDB(QList<ProjectInfo::ConstPtr> p
jsonProjectOptions);
}
for (const ProjectFile &projFile : projectPart->files) {
+ if (promise.isCanceled())
+ return;
const QJsonObject json
= createFileObject(baseDir,
args,
@@ -200,7 +205,7 @@ GenerateCompilationDbResult generateCompilationDB(QList<ProjectInfo::ConstPtr> p
compileCommandsFile.write("]");
compileCommandsFile.close();
- return GenerateCompilationDbResult(compileCommandsFile.fileName(), QString());
+ promise.addResult(FilePath::fromUserInput(compileCommandsFile.fileName()));
}
FilePath currentCppEditorDocumentFilePath()
@@ -262,7 +267,6 @@ QString DiagnosticTextInfo::clazyCheckName(const QString &option)
return option;
}
-
QJsonArray clangOptionsForFile(const ProjectFile &file, const ProjectPart &projectPart,
const QJsonArray &generalOptions, UsePrecompiledHeaders usePch,
bool clStyle)
diff --git a/src/plugins/clangcodemodel/clangutils.h b/src/plugins/clangcodemodel/clangutils.h
index 6b99be52e6..d78dab7231 100644
--- a/src/plugins/clangcodemodel/clangutils.h
+++ b/src/plugins/clangcodemodel/clangutils.h
@@ -53,23 +53,16 @@ Utils::FilePath currentCppEditorDocumentFilePath();
QString diagnosticCategoryPrefixRemoved(const QString &text);
-class GenerateCompilationDbResult
-{
-public:
- GenerateCompilationDbResult() = default;
- GenerateCompilationDbResult(const QString &filePath, const QString &error)
- : filePath(filePath), error(error)
- {}
-
- QString filePath;
- QString error;
-};
-
+using GenerateCompilationDbResult = Utils::expected_str<Utils::FilePath>;
enum class CompilationDbPurpose { Project, CodeModel };
-GenerateCompilationDbResult generateCompilationDB(QList<CppEditor::ProjectInfo::ConstPtr> projectInfo,
- Utils::FilePath baseDir, CompilationDbPurpose purpose,
- CppEditor::ClangDiagnosticConfig warningsConfig, QStringList projectOptions,
- Utils::FilePath clangIncludeDir);
+void generateCompilationDB(
+ QPromise<GenerateCompilationDbResult> &promise,
+ const QList<CppEditor::ProjectInfo::ConstPtr> &projectInfoList,
+ const Utils::FilePath &baseDir,
+ CompilationDbPurpose purpose,
+ const CppEditor::ClangDiagnosticConfig &warningsConfig,
+ const QStringList &projectOptions,
+ const Utils::FilePath &clangIncludeDir);
class DiagnosticTextInfo
{
diff --git a/src/plugins/clangcodemodel/test/clangdtests.cpp b/src/plugins/clangcodemodel/test/clangdtests.cpp
index f18c164155..84a155b535 100644
--- a/src/plugins/clangcodemodel/test/clangdtests.cpp
+++ b/src/plugins/clangcodemodel/test/clangdtests.cpp
@@ -1107,8 +1107,7 @@ void ClangdTestHighlighting::test_data()
QTest::newRow("call to function pointer alias") << 344 << 5 << 344 << 13
<< QList<int>{C_TYPE} << 0;
QTest::newRow("friend class declaration") << 350 << 18 << 350 << 27
- << (client()->versionNumber().majorVersion() >= 16
- ? QList<int>{C_TYPE, C_DECLARATION}: QList<int>{C_TYPE}) << 0;
+ << QList<int>{C_TYPE, C_DECLARATION} << 0;
QTest::newRow("friend class reference") << 351 << 34 << 351 << 43
<< QList<int>{C_TYPE} << 0;
QTest::newRow("function parameter of friend class type") << 351 << 45 << 351 << 50
@@ -1374,10 +1373,6 @@ void ClangdTestHighlighting::test_data()
<< QList<int>{C_PUNCTUATION} << int(CppEditor::SemanticHighlighter::AngleBracketClose);
QTest::newRow("macro in struct") << 795 << 9 << 795 << 14
<< QList<int>{C_MACRO, C_DECLARATION} << 0;
- if (client()->versionNumber() < QVersionNumber(17)) {
- QTest::newRow("#ifdef'ed out code") << 800 << 1 << 800 << 17
- << QList<int>{C_DISABLED_CODE} << 0;
- }
QTest::newRow("static function call (object)") << 819 << 5 << 819 << 6
<< QList<int>{C_LOCAL} << 0;
QTest::newRow("static function call (argument)") << 819 << 18 << 819 << 19
diff --git a/src/plugins/clangformat/ClangFormat.json.in b/src/plugins/clangformat/ClangFormat.json.in
index c444af0fcd..1e9a26b4d6 100644
--- a/src/plugins/clangformat/ClangFormat.json.in
+++ b/src/plugins/clangformat/ClangFormat.json.in
@@ -14,6 +14,6 @@
],
"Category" : "C++",
"Description" : "clang-format indentation plugin.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/clangformat/clangformatbaseindenter.cpp b/src/plugins/clangformat/clangformatbaseindenter.cpp
index b491bd293f..4d89ea8aa2 100644
--- a/src/plugins/clangformat/clangformatbaseindenter.cpp
+++ b/src/plugins/clangformat/clangformatbaseindenter.cpp
@@ -34,6 +34,8 @@ using namespace std::chrono_literals;
namespace ClangFormat {
+Q_LOGGING_CATEGORY(clangIndenterLog, "qtc.dbg.clangformat", QtWarningMsg)
+
enum class ReplacementsToKeep { OnlyIndent, IndentAndBefore, All };
static Internal::LlvmFileSystemAdapter llvmFileSystemAdapter = {};
@@ -166,6 +168,7 @@ enum class CharacterContext {
LastAfterComma,
NewStatementOrContinuation,
IfOrElseWithoutScope,
+ BracketAfterFunctionCall,
Unknown
};
@@ -217,6 +220,10 @@ static CharacterContext characterContext(const QTextBlock &currentBlock)
if (prevLineText.isEmpty())
return CharacterContext::NewStatementOrContinuation;
+ if ((currentBlock.text().trimmed().isEmpty() || currentBlock.text().trimmed().endsWith(")"))
+ && prevLineText.endsWith("{"))
+ return CharacterContext::BracketAfterFunctionCall;
+
const QChar firstNonWhitespaceChar = findFirstNonWhitespaceCharacter(currentBlock);
if (prevLineText.endsWith(',')) {
if (firstNonWhitespaceChar == '}') {
@@ -266,6 +273,8 @@ static QByteArray dummyTextForContext(CharacterContext context, bool closingBrac
return "a";
case CharacterContext::IfOrElseWithoutScope:
return ";";
+ case CharacterContext::BracketAfterFunctionCall:
+ return ";";
case CharacterContext::NewStatementOrContinuation:
return "/*//*/";
case CharacterContext::Unknown:
@@ -296,6 +305,8 @@ static int forceIndentWithExtraText(QByteArray &buffer,
int firstNonWhitespace = Utils::indexOf(blockText,
[](const QChar &ch) { return !ch.isSpace(); });
int utf8Offset = Text::utf8NthLineOffset(block.document(), buffer, block.blockNumber() + 1);
+ int utf8EndOfLineOffset = utf8Offset + blockText.length();
+
if (firstNonWhitespace >= 0)
utf8Offset += firstNonWhitespace;
else
@@ -312,7 +323,9 @@ static int forceIndentWithExtraText(QByteArray &buffer,
&& nextBlockExistsAndEmpty(block)) {
// If the next line is also empty it's safer to use a comment line.
dummyText = "//";
- } else if (firstNonWhitespace < 0 || closingParenBlock || closingBraceBlock) {
+ } else if (
+ firstNonWhitespace < 0 || closingParenBlock || closingBraceBlock
+ || charContext == CharacterContext::BracketAfterFunctionCall) {
dummyText = dummyTextForContext(charContext, closingBraceBlock);
}
@@ -327,6 +340,13 @@ static int forceIndentWithExtraText(QByteArray &buffer,
extraLength += 3;
}
}
+
+ if (charContext == CharacterContext::BracketAfterFunctionCall) {
+ buffer.insert(utf8EndOfLineOffset + extraLength, dummyText);
+ extraLength += dummyText.length();
+ return extraLength;
+ }
+
buffer.insert(utf8Offset + extraLength, dummyText);
extraLength += dummyText.length();
@@ -551,6 +571,37 @@ ClangFormatBaseIndenter::~ClangFormatBaseIndenter()
delete d;
}
+static void printBuffer(QString str)
+{
+ for (const auto &line : str.split("\n")) {
+ qCDebug(clangIndenterLog) << line;
+ }
+}
+
+static void printDebugInfo(
+ const QByteArray &buffer,
+ clang::tooling::Replacements replacements,
+ const QString &additionalInfo)
+{
+ if (!clangIndenterLog().isInfoEnabled())
+ return;
+
+ QString str = QString::fromStdString(buffer.data());
+
+ if (replacements.empty()) {
+ std::string code = buffer.data();
+ llvm::Expected<std::string> code_new
+ = clang::tooling::applyAllReplacements(code, replacements);
+ if (!code_new)
+ return;
+
+ str = QString::fromStdString(code_new.get());
+ }
+ qCDebug(clangIndenterLog) << additionalInfo << str;
+
+ printBuffer(str);
+}
+
ChangeSet ClangFormatBaseIndenterPrivate::replacements(QByteArray buffer,
const QTextBlock &startBlock,
const QTextBlock &endBlock,
@@ -584,6 +635,8 @@ ChangeSet ClangFormatBaseIndenterPrivate::replacements(QByteArray buffer,
}
}
+ printDebugInfo(buffer, {}, "before");
+
if (replacementsToKeep != ReplacementsToKeep::IndentAndBefore || utf8Offset < rangeStart)
rangeStart = utf8Offset;
@@ -594,6 +647,8 @@ ChangeSet ClangFormatBaseIndenterPrivate::replacements(QByteArray buffer,
clang::tooling::Replacements clangReplacements = clang::format::reformat(
style, buffer.data(), ranges, m_fileName->toFSPathString().toStdString(), &status);
+ printDebugInfo(buffer, clangReplacements, "after");
+
clang::tooling::Replacements filtered;
if (status.FormatComplete) {
filtered = filteredReplacements(buffer,
@@ -602,6 +657,9 @@ ChangeSet ClangFormatBaseIndenterPrivate::replacements(QByteArray buffer,
utf8Length,
replacementsToKeep);
}
+
+ printDebugInfo(buffer, filtered, "filtered");
+
const bool canTryAgain = replacementsToKeep == ReplacementsToKeep::OnlyIndent
&& typedChar == QChar::Null && !secondTry;
if (canTryAgain && filtered.empty()) {
@@ -648,7 +706,7 @@ EditOperations ClangFormatBaseIndenter::format(const RangesInLines &rangesInLine
assumedFileName);
auto changedCode = clang::tooling::applyAllReplacements(buffer.data(), clangReplacements);
QTC_ASSERT(changedCode, {
- qDebug() << QString::fromStdString(llvm::toString(changedCode.takeError()));
+ qCDebug(clangIndenterLog) << QString::fromStdString(llvm::toString(changedCode.takeError()));
return {};
});
ranges = clang::tooling::calculateRangesAfterReplacements(clangReplacements, ranges);
@@ -724,12 +782,15 @@ void ClangFormatBaseIndenterPrivate::indent(const QTextCursor &cursor,
const QChar &typedChar,
int cursorPositionInEditor)
{
+ const QString blockText = cursor.block().text().trimmed();
if (cursor.hasSelection()) {
indentBlocks(m_doc->findBlock(cursor.selectionStart()),
m_doc->findBlock(cursor.selectionEnd()),
typedChar,
cursorPositionInEditor);
- } else {
+ } else if (
+ typedChar == QChar::Null || blockText.startsWith(typedChar) || blockText.endsWith(typedChar)
+ || blockText.isEmpty()) {
indentBlocks(cursor.block(), cursor.block(), typedChar, cursorPositionInEditor);
}
}
diff --git a/src/plugins/clangformat/clangformatbaseindenter.h b/src/plugins/clangformat/clangformatbaseindenter.h
index cb8041cf43..e6512f7862 100644
--- a/src/plugins/clangformat/clangformatbaseindenter.h
+++ b/src/plugins/clangformat/clangformatbaseindenter.h
@@ -5,10 +5,14 @@
#include <texteditor/indenter.h>
+#include <QLoggingCategory>
+
namespace clang::format { struct FormatStyle; }
namespace ClangFormat {
+Q_DECLARE_LOGGING_CATEGORY(clangIndenterLog)
+
class ClangFormatBaseIndenter : public TextEditor::Indenter
{
public:
diff --git a/src/plugins/clangformat/clangformatglobalconfigwidget.cpp b/src/plugins/clangformat/clangformatglobalconfigwidget.cpp
index 4dc344601d..2762982342 100644
--- a/src/plugins/clangformat/clangformatglobalconfigwidget.cpp
+++ b/src/plugins/clangformat/clangformatglobalconfigwidget.cpp
@@ -26,6 +26,7 @@
#include <QCheckBox>
#include <QComboBox>
+#include <QGroupBox>
#include <QLabel>
#include <QSpinBox>
@@ -106,9 +107,10 @@ ClangFormatGlobalConfigWidget::ClangFormatGlobalConfigWidget(ICodeStylePreferenc
QWidget *globalSettingsGroupBoxWidget = nullptr;
+ // clang-format off
Group globalSettingsGroupBox {
bindTo(&globalSettingsGroupBoxWidget),
- title(Tr::tr("ClangFormat settings:")),
+ title(Tr::tr("ClangFormat Settings:")),
Column {
m_useGlobalSettings,
Form {
@@ -127,6 +129,7 @@ ClangFormatGlobalConfigWidget::ClangFormatGlobalConfigWidget(ICodeStylePreferenc
globalSettingsGroupBox,
noMargin
}.attachTo(this);
+ // clang-format on
initCheckBoxes();
initIndentationOrFormattingCombobox();
diff --git a/src/plugins/clangformat/clangformatutils.cpp b/src/plugins/clangformat/clangformatutils.cpp
index 7ccc9ad718..6732524f41 100644
--- a/src/plugins/clangformat/clangformatutils.cpp
+++ b/src/plugins/clangformat/clangformatutils.cpp
@@ -22,6 +22,7 @@
#include <utils/expected.h>
#include <QCryptographicHash>
+#include <QLoggingCategory>
using namespace clang;
using namespace format;
diff --git a/src/plugins/clangformat/tests/clangformat-test.cpp b/src/plugins/clangformat/tests/clangformat-test.cpp
index 51f78f90af..c52767baa2 100644
--- a/src/plugins/clangformat/tests/clangformat-test.cpp
+++ b/src/plugins/clangformat/tests/clangformat-test.cpp
@@ -111,6 +111,11 @@ private slots:
void testIndentFunctionArgumentOnNewLine();
void testIndentCommentOnNewLine();
void testUtf8SymbolLine();
+ void testFunctionCallClosingParenthesis();
+ void testFunctionCallClosingParenthesisEmptyLine();
+ void testNoIndentationInMiddleOfLine();
+ void testIndentationInTheBegginingOfLine();
+ void testIndentationInMiddleOfLine();
private:
void insertLines(const std::vector<QString> &lines);
@@ -833,6 +838,133 @@ void ClangFormatTest::testUtf8SymbolLine()
"}"}));
}
+void ClangFormatTest::testFunctionCallClosingParenthesis()
+{
+ insertLines(
+ {"class X {",
+ "public:",
+ " QString add() const;",
+ " QString sub() const;",
+ "};",
+ "",
+ "QString X::add() const",
+ "{",
+ " return QString()",
+ "}",
+ "",
+ "QString X::sub() const",
+ "{}"});
+ m_indenter->indentBlock(m_doc->findBlockByNumber(8), ')', TextEditor::TabSettings());
+ QCOMPARE(
+ documentLines(),
+ (std::vector<QString>{
+ "class X {",
+ "public:",
+ " QString add() const;",
+ " QString sub() const;",
+ "};",
+ "",
+ "QString X::add() const",
+ "{",
+ " return QString()",
+ "}",
+ "",
+ "QString X::sub() const",
+ "{}",
+ }));
+}
+
+void ClangFormatTest::testFunctionCallClosingParenthesisEmptyLine()
+{
+ insertLines(
+ {"class X {",
+ "public:",
+ " QString add() const;",
+ " QString sub() const;",
+ "};",
+ "",
+ "QString X::add() const",
+ "{",
+ "",
+ " return QString()",
+ "}",
+ "",
+ "QString X::sub() const",
+ "{}"});
+ m_indenter->indentBlock(m_doc->findBlockByNumber(8), QChar::Null, TextEditor::TabSettings());
+ QCOMPARE(
+ documentLines(),
+ (std::vector<QString>{
+ "class X {",
+ "public:",
+ " QString add() const;",
+ " QString sub() const;",
+ "};",
+ "",
+ "QString X::add() const",
+ "{",
+ " ",
+ " return QString()",
+ "}",
+ "",
+ "QString X::sub() const",
+ "{}",
+ }));
+}
+
+void ClangFormatTest::testNoIndentationInMiddleOfLine()
+{
+ insertLines({"int main()",
+ "{",
+ " S s = {.i = 1}, .l = 2, .f = 3, .d = 4};",
+ " return 0;",
+ "}"});
+ m_cursor->setPosition(30);
+ m_extendedIndenter->indent(*m_cursor, '}', TextEditor::TabSettings(), 30);
+ QCOMPARE(documentLines(),
+ (std::vector<QString>{"int main()",
+ "{",
+ " S s = {.i = 1}, .l = 2, .f = 3, .d = 4};",
+ " return 0;",
+ "}"}));
+}
+
+void ClangFormatTest::testIndentationInMiddleOfLine()
+{
+ insertLines({"int main()",
+ "{",
+ " S s = {.i = 1,",
+ ".l = 2, .f = 3, .d = 4};",
+ " return 0;",
+ "}"});
+ m_cursor->setPosition(32);
+ m_extendedIndenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettings(), 32);
+ QCOMPARE(documentLines(),
+ (std::vector<QString>{"int main()",
+ "{",
+ " S s = {.i = 1,",
+ " .l = 2, .f = 3, .d = 4};",
+ " return 0;",
+ "}"}));
+}
+
+void ClangFormatTest::testIndentationInTheBegginingOfLine()
+{
+ insertLines({"int main()",
+ "{",
+ " if () {",
+ " } else",
+ "}"});
+ m_cursor->setPosition(35);
+ m_extendedIndenter->indent(*m_cursor, '}', TextEditor::TabSettings(), 35);
+ QCOMPARE(documentLines(),
+ (std::vector<QString>{"int main()",
+ "{",
+ " if () {",
+ " } else",
+ "}"}));
+}
+
QObject *createClangFormatTest()
{
return new ClangFormatTest;
diff --git a/src/plugins/clangtools/ClangTools.json.in b/src/plugins/clangtools/ClangTools.json.in
index d6d1280c40..c50706d86d 100644
--- a/src/plugins/clangtools/ClangTools.json.in
+++ b/src/plugins/clangtools/ClangTools.json.in
@@ -14,6 +14,6 @@
],
"Category" : "Code Analyzer",
"Description" : "ClangTools Plugin.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/clangtools/clangtool.cpp b/src/plugins/clangtools/clangtool.cpp
index dd4c7612bd..21b5ea6fbd 100644
--- a/src/plugins/clangtools/clangtool.cpp
+++ b/src/plugins/clangtools/clangtool.cpp
@@ -136,8 +136,8 @@ public:
setLayout(layout);
QPalette pal;
- pal.setColor(QPalette::Window, Utils::creatorTheme()->color(Theme::InfoBarBackground));
- pal.setColor(QPalette::WindowText, Utils::creatorTheme()->color(Theme::InfoBarText));
+ pal.setColor(QPalette::Window, Utils::creatorColor(Theme::InfoBarBackground));
+ pal.setColor(QPalette::WindowText, Utils::creatorColor(Theme::InfoBarText));
setPalette(pal);
setAutoFillBackground(true);
diff --git a/src/plugins/clangtools/clangtoolrunner.cpp b/src/plugins/clangtools/clangtoolrunner.cpp
index af3539b2ac..c91d9e9cfa 100644
--- a/src/plugins/clangtools/clangtoolrunner.cpp
+++ b/src/plugins/clangtools/clangtoolrunner.cpp
@@ -14,8 +14,6 @@
#include <cppeditor/cppprojectfile.h>
#include <cppeditor/cpptoolsreuse.h>
-#include <extensionsystem/pluginmanager.h>
-
#include <utils/async.h>
#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
@@ -163,13 +161,9 @@ GroupItem clangToolTask(const AnalyzeUnits &units,
process.setWorkingDirectory(input.outputDirPath); // Current clang-cl puts log file into working dir.
const ClangToolStorage &data = *storage;
-
- const QStringList args = checksArguments(unit, input)
- + mainToolArguments(data)
- + QStringList{"--"}
- + clangArguments(unit, input);
- const CommandLine commandLine = {data.executable, args};
-
+ const CommandLine commandLine{data.executable, {checksArguments(unit, input),
+ mainToolArguments(data), "--",
+ clangArguments(unit, input)}};
qCDebug(LOG).noquote() << "Starting" << commandLine.toUserOutput();
process.setCommand(commandLine);
};
@@ -208,7 +202,6 @@ GroupItem clangToolTask(const AnalyzeUnits &units,
data.setConcurrentCallData(&parseDiagnostics,
storage->outputFilePath,
input.diagnosticsFilter);
- data.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
};
const auto onReadDone = [storage, input, outputHandler, iterator](
const Async<expected_str<Diagnostics>> &data, DoneWith result) {
diff --git a/src/plugins/clangtools/clangtoolsdiagnosticview.cpp b/src/plugins/clangtools/clangtoolsdiagnosticview.cpp
index e3d63e1ec9..47d891ae3b 100644
--- a/src/plugins/clangtools/clangtoolsdiagnosticview.cpp
+++ b/src/plugins/clangtools/clangtoolsdiagnosticview.cpp
@@ -14,10 +14,8 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/manhattanstyle.h>
-#include <cppeditor/cppeditorwidget.h>
#include <cppeditor/cppmodelmanager.h>
#include <cppeditor/cpprefactoringchanges.h>
-#include <cppeditor/cppsemanticinfo.h>
#include <debugger/analyzer/diagnosticlocation.h>
@@ -256,19 +254,7 @@ void DiagnosticView::suppressCurrentDiagnosticInline()
CppRefactoringChanges changes(CppModelManager::snapshot());
for (auto it = diagnosticsPerFileAndLine.cbegin(); it != diagnosticsPerFileAndLine.cend(); ++it) {
- const Utils::FilePath filePath = it.key();
- CppEditorWidget *editorWidget = nullptr;
- const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForFilePath(filePath);
- for (Core::IEditor *editor : editors) {
- const auto textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor);
- if (textEditor)
- editorWidget = qobject_cast<CppEditorWidget *>(textEditor->editorWidget());
- if (editorWidget)
- break;
- }
- CppRefactoringFilePtr refactoringFile
- = editorWidget ? changes.file(editorWidget, editorWidget->semanticInfo().doc)
- : changes.cppFile(filePath);
+ const CppRefactoringFilePtr refactoringFile = changes.cppFile(it.key());
Utils::ChangeSet changeSet;
for (auto it2 = it.value().cbegin(); it2 != it.value().cend(); ++it2) {
@@ -328,8 +314,7 @@ void DiagnosticView::suppressCurrentDiagnosticInline()
changeSet.insert(insertStart, newText);
}
}
- refactoringFile->setChangeSet(changeSet);
- refactoringFile->apply();
+ refactoringFile->apply(changeSet);
}
filterModel->addSuppressedDiagnostics(diags);
diff --git a/src/plugins/clangtools/documentquickfixfactory.h b/src/plugins/clangtools/documentquickfixfactory.h
index 98f45af46e..6e99b1d345 100644
--- a/src/plugins/clangtools/documentquickfixfactory.h
+++ b/src/plugins/clangtools/documentquickfixfactory.h
@@ -3,7 +3,7 @@
#pragma once
-#include <cppeditor/cppquickfix.h>
+#include <cppeditor/quickfixes/cppquickfix.h>
namespace ClangTools {
namespace Internal {
diff --git a/src/plugins/clangtools/executableinfo.cpp b/src/plugins/clangtools/executableinfo.cpp
index 7cda766e7d..23dc807367 100644
--- a/src/plugins/clangtools/executableinfo.cpp
+++ b/src/plugins/clangtools/executableinfo.cpp
@@ -106,15 +106,14 @@ static ClazyChecks querySupportedClazyChecks(const FilePath &executablePath)
};
static const QString queryFlag = "-supported-checks-json";
- DataFromProcess<ClazyChecks>::Parameters params(CommandLine(executablePath, {queryFlag}),
- parser);
+ DataFromProcess<ClazyChecks>::Parameters params({executablePath, {queryFlag}}, parser);
params.environment.setupEnglishOutput();
params.errorHandler = handleProcessError;
auto checks = DataFromProcess<ClazyChecks>::getData(params);
if (!checks) {
// Some clazy 1.6.x versions have a bug where they expect an argument after the
// option.
- params.commandLine = CommandLine(executablePath, {queryFlag, "dummy"});
+ params.commandLine = {executablePath, {queryFlag, "dummy"}};
checks = DataFromProcess<ClazyChecks>::getData(params);
}
if (checks)
diff --git a/src/plugins/classview/ClassView.json.in b/src/plugins/classview/ClassView.json.in
index 753824248b..a92497e7c2 100644
--- a/src/plugins/classview/ClassView.json.in
+++ b/src/plugins/classview/ClassView.json.in
@@ -14,6 +14,6 @@
],
"Category" : "C++",
"Description" : "Class View component.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/clearcase/ClearCase.json.in b/src/plugins/clearcase/ClearCase.json.in
index 8bcfbb66bc..1c45ab6665 100644
--- a/src/plugins/clearcase/ClearCase.json.in
+++ b/src/plugins/clearcase/ClearCase.json.in
@@ -16,7 +16,7 @@
],
"Category" : "Version Control",
"Description" : "ClearCase integration.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/clearcase/clearcaseplugin.cpp b/src/plugins/clearcase/clearcaseplugin.cpp
index bc4cc82958..9fa1a06c8a 100644
--- a/src/plugins/clearcase/clearcaseplugin.cpp
+++ b/src/plugins/clearcase/clearcaseplugin.cpp
@@ -2265,14 +2265,10 @@ void ClearCasePluginPrivate::diffGraphical(const QString &file1, const QString &
QString ClearCasePluginPrivate::runExtDiff(const FilePath &workingDir, const QStringList &arguments,
int timeOutS, QTextCodec *outputCodec)
{
- CommandLine diff("diff");
- diff.addArgs(m_settings.diffArgs.split(' ', Qt::SkipEmptyParts));
- diff.addArgs(arguments);
-
Process process;
process.setWorkingDirectory(workingDir);
process.setCodec(outputCodec ? outputCodec : QTextCodec::codecForName("UTF-8"));
- process.setCommand(diff);
+ process.setCommand({"diff", {m_settings.diffArgs.split(' ', Qt::SkipEmptyParts), arguments}});
process.runBlocking(seconds(timeOutS), EventLoopMode::On);
if (process.result() != ProcessResult::FinishedWithSuccess)
return {};
diff --git a/src/plugins/cmakeprojectmanager/CMakeLists.txt b/src/plugins/cmakeprojectmanager/CMakeLists.txt
index 492d628414..2c58b4f08b 100644
--- a/src/plugins/cmakeprojectmanager/CMakeLists.txt
+++ b/src/plugins/cmakeprojectmanager/CMakeLists.txt
@@ -1,7 +1,7 @@
add_qtc_plugin(CMakeProjectManager
PLUGIN_CLASS CMakeProjectPlugin
DEPENDS QmlJS
- PLUGIN_DEPENDS Core CppEditor ProjectExplorer TextEditor QtSupport
+ PLUGIN_DEPENDS Core CppEditor Debugger ProjectExplorer TextEditor QtSupport
SYSTEM_INCLUDES 3dparty/cmake
SOURCES
builddirparameters.cpp builddirparameters.h
diff --git a/src/plugins/cmakeprojectmanager/CMakeProjectManager.json.in b/src/plugins/cmakeprojectmanager/CMakeProjectManager.json.in
index fc239c6a57..8de40cc02e 100644
--- a/src/plugins/cmakeprojectmanager/CMakeProjectManager.json.in
+++ b/src/plugins/cmakeprojectmanager/CMakeProjectManager.json.in
@@ -14,7 +14,7 @@
],
"Category" : "Build Systems",
"Description" : "CMake support.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/cmakeprojectmanager/builddirparameters.cpp b/src/plugins/cmakeprojectmanager/builddirparameters.cpp
index 71f6b82088..d78e7180a6 100644
--- a/src/plugins/cmakeprojectmanager/builddirparameters.cpp
+++ b/src/plugins/cmakeprojectmanager/builddirparameters.cpp
@@ -50,13 +50,13 @@ BuildDirParameters::BuildDirParameters(CMakeBuildSystem *buildSystem)
});
const Target *t = bc->target();
const Kit *k = t->kit();
- const Project *p = t->project();
- projectName = p->displayName();
+ project = t->project();
+ projectName = project->displayName();
sourceDirectory = bc->sourceDirectory();
if (sourceDirectory.isEmpty())
- sourceDirectory = p->projectDirectory();
+ sourceDirectory = project->projectDirectory();
buildDirectory = bc->buildDirectory();
cmakeBuildType = buildSystem->cmakeBuildType();
diff --git a/src/plugins/cmakeprojectmanager/builddirparameters.h b/src/plugins/cmakeprojectmanager/builddirparameters.h
index f2648f7773..b73993eb37 100644
--- a/src/plugins/cmakeprojectmanager/builddirparameters.h
+++ b/src/plugins/cmakeprojectmanager/builddirparameters.h
@@ -15,6 +15,10 @@ class MacroExpander;
class OutputLineParser;
} // namespace Utils
+namespace ProjectExplorer {
+class Project;
+}
+
namespace CMakeProjectManager::Internal {
class CMakeBuildSystem;
@@ -29,6 +33,7 @@ public:
CMakeTool *cmakeTool() const;
QString projectName;
+ ProjectExplorer::Project *project = nullptr;
Utils::FilePath sourceDirectory;
Utils::FilePath buildDirectory;
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp
index 0106ec5896..ee2658076e 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp
@@ -128,6 +128,7 @@ private:
void kitCMakeConfiguration();
void updateConfigureDetailsWidgetsSummary(
const QStringList &configurationArguments = QStringList());
+ void updatePackageManagerAutoSetup(CMakeConfig &initialList);
CMakeBuildConfiguration *m_buildConfig;
QTreeView *m_configView;
@@ -166,6 +167,16 @@ static QModelIndex mapToSource(const QAbstractItemView *view, const QModelIndex
return result;
}
+static CMakeConfigItem getPackageManagerAutoSetupParameter()
+{
+ const QByteArray key("CMAKE_PROJECT_INCLUDE_BEFORE");
+ const QByteArray value = QString(
+ "%{BuildConfig:BuildDirectory:NativeFilePath}/%1/auto-setup.cmake")
+ .arg(Constants::PACKAGE_MANAGER_DIR)
+ .toUtf8();
+ return CMakeConfigItem(key, CMakeConfigItem::FILEPATH, value);
+}
+
CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc) :
NamedWidget(Tr::tr("CMake")),
m_buildConfig(bc),
@@ -297,7 +308,8 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc)
m_batchEditButton->setToolTip(Tr::tr("Set or reset multiple values in the CMake configuration."));
m_showAdvancedCheckBox = new QCheckBox(Tr::tr("Advanced"));
- m_showAdvancedCheckBox->setChecked(settings().showAdvancedOptionsByDefault());
+ m_showAdvancedCheckBox->setChecked(
+ settings(m_buildConfig->project()).showAdvancedOptionsByDefault());
connect(m_configView->selectionModel(), &QItemSelectionModel::selectionChanged,
this, [this](const QItemSelection &, const QItemSelection &) {
@@ -342,7 +354,7 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc)
Column {
cmakeConfiguration,
Row {
- bc->initialCMakeArguments, br,
+ bc->initialCMakeArguments,
bc->additionalCMakeOptions
},
m_reconfigureButton,
@@ -586,11 +598,11 @@ void CMakeBuildSettingsWidget::reconfigureWithInitialParameters()
Core::ICore::dialogParent(),
Tr::tr("Re-configure with Initial Parameters"),
Tr::tr("Clear CMake configuration and configure with initial parameters?"),
- settings().askBeforeReConfigureInitialParams.askAgainCheckableDecider(),
+ settings(m_buildConfig->project()).askBeforeReConfigureInitialParams.askAgainCheckableDecider(),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::Yes);
- settings().writeSettings();
+ settings(m_buildConfig->project()).writeSettings();
if (reply != QMessageBox::Yes)
return;
@@ -603,6 +615,24 @@ void CMakeBuildSettingsWidget::reconfigureWithInitialParameters()
m_buildConfig->cmakeBuildSystem()->runCMake();
}
+void CMakeBuildSettingsWidget::updatePackageManagerAutoSetup(CMakeConfig &initialList)
+{
+ const bool usePackageManagerAutoSetup
+ = settings(m_buildConfig->project()).packageManagerAutoSetup();
+
+ const auto autoSetupParameter = getPackageManagerAutoSetupParameter();
+ auto it
+ = std::find_if(initialList.begin(), initialList.end(), [&autoSetupParameter](const CMakeConfigItem &item) {
+ return item.key == autoSetupParameter.key;
+ });
+ if (it != initialList.end()) {
+ if (!usePackageManagerAutoSetup && it->value == autoSetupParameter.value)
+ initialList.erase(it);
+ } else if (usePackageManagerAutoSetup) {
+ initialList.push_back(autoSetupParameter);
+ }
+}
+
void CMakeBuildSettingsWidget::updateInitialCMakeArguments()
{
CMakeConfig initialList = m_buildConfig->initialCMakeArguments.cmakeConfiguration();
@@ -636,6 +666,8 @@ void CMakeBuildSettingsWidget::updateInitialCMakeArguments()
}
}
+ updatePackageManagerAutoSetup(initialList);
+
m_buildConfig->initialCMakeArguments.setCMakeConfiguration(initialList);
// value() will contain only the unknown arguments (the non -D/-U arguments)
@@ -1113,7 +1145,8 @@ static bool isWindowsARM64(const Kit *k)
&& targetAbi.wordWidth() == 64;
}
-static CommandLine defaultInitialCMakeCommand(const Kit *k, const QString &buildType)
+static CommandLine defaultInitialCMakeCommand(
+ const Kit *k, Project *project, const QString &buildType)
{
// Generator:
CMakeTool *tool = CMakeKitAspect::cmakeTool(k);
@@ -1127,11 +1160,8 @@ static CommandLine defaultInitialCMakeCommand(const Kit *k, const QString &build
cmd.addArg("-DCMAKE_BUILD_TYPE:STRING=" + buildType);
// Package manager auto setup
- if (settings().packageManagerAutoSetup()) {
- cmd.addArg(QString("-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH="
- "%{BuildConfig:BuildDirectory:NativeFilePath}/%1/auto-setup.cmake")
- .arg(Constants::PACKAGE_MANAGER_DIR));
- }
+ if (settings(project).packageManagerAutoSetup())
+ cmd.addArg(getPackageManagerAutoSetupParameter().toArgument());
// Cross-compilation settings:
if (!CMakeBuildConfiguration::isIos(k)) { // iOS handles this differently
@@ -1454,7 +1484,7 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Id id)
? extraInfoMap.value(CMAKE_BUILD_TYPE).toString()
: info.typeName;
- CommandLine cmd = defaultInitialCMakeCommand(k, buildType);
+ CommandLine cmd = defaultInitialCMakeCommand(k, target->project(), buildType);
m_buildSystem->setIsMultiConfig(CMakeGeneratorKitAspect::isMultiConfigGenerator(k));
// Android magic:
@@ -2038,7 +2068,7 @@ void CMakeBuildConfiguration::addToEnvironment(Utils::Environment &env) const
if (tool && tool->cmakeExecutable().needsDevice())
return;
- const FilePath ninja = settings().ninjaPath();
+ const FilePath ninja = settings(nullptr).ninjaPath();
if (!ninja.isEmpty())
env.appendOrSetPath(ninja.isFile() ? ninja.parentDir() : ninja);
}
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp
index 6663b311a4..e992dc8e9e 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp
@@ -436,10 +436,15 @@ CommandLine CMakeBuildStep::cmakeCommand() const
CommandLine cmd{cmakeExecutable()};
FilePath buildDirectory = ".";
- if (buildConfiguration())
+ Project *project = nullptr;
+ if (buildConfiguration()) {
buildDirectory = buildConfiguration()->buildDirectory();
+ project = buildConfiguration()->project();
+ }
- cmd.addArgs({"--build", CMakeToolManager::mappedFilePath(buildDirectory).path()});
+ cmd.addArgs(
+ {"--build",
+ CMakeToolManager::mappedFilePath(project, buildDirectory).path()});
cmd.addArg("--target");
cmd.addArgs(Utils::transform(m_buildTargets, [this](const QString &s) {
@@ -600,7 +605,7 @@ QWidget *CMakeBuildStep::createConfigWidget()
if (!isCleanStep() && !m_buildPreset.isEmpty())
createAndAddEnvironmentWidgets(builder);
- builder.addItem(Layouting::noMargin);
+ builder.noMargin();
auto widget = builder.emerge();
updateDetails();
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
index 219000f284..7b2520cc90 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
@@ -1643,7 +1643,7 @@ void CMakeBuildSystem::wireUpConnections()
connect(project(), &Project::projectFileIsDirty, this, [this] {
const bool isBuilding = BuildManager::isBuilding(project());
if (buildConfiguration()->isActive() && !isParsing() && !isBuilding) {
- if (settings().autorunCMake()) {
+ if (settings(project()).autorunCMake()) {
qCDebug(cmakeBuildSystemLog) << "Requesting parse due to dirty project file";
reparse(CMakeBuildSystem::REPARSE_FORCE_CMAKE_RUN);
}
@@ -2056,19 +2056,17 @@ const QList<TestCaseInfo> CMakeBuildSystem::testcasesInfo() const
CommandLine CMakeBuildSystem::commandLineForTests(const QList<QString> &tests,
const QStringList &options) const
{
- QStringList args = options;
const QSet<QString> testsSet = Utils::toSet(tests);
- auto current = Utils::transform<QSet<QString>>(m_testNames, &TestCaseInfo::name);
+ const auto current = Utils::transform<QSet<QString>>(m_testNames, &TestCaseInfo::name);
if (tests.isEmpty() || current == testsSet)
- return {m_ctestPath, args};
+ return {m_ctestPath, options};
QString testNumbers("0,0,0"); // start, end, stride
for (const TestCaseInfo &info : m_testNames) {
if (testsSet.contains(info.name))
testNumbers += QString(",%1").arg(info.number);
}
- args << "-I" << testNumbers;
- return {m_ctestPath, args};
+ return {m_ctestPath, {options, "-I", testNumbers}};
}
DeploymentData CMakeBuildSystem::deploymentDataFromFile() const
@@ -2302,11 +2300,14 @@ MakeInstallCommand CMakeBuildSystem::makeInstallCommand(const FilePath &installR
installTarget = "INSTALL";
FilePath buildDirectory = ".";
- if (auto bc = buildConfiguration())
+ Project *project = nullptr;
+ if (auto bc = buildConfiguration()) {
buildDirectory = bc->buildDirectory();
+ project = bc->project();
+ }
cmd.command.addArg("--build");
- cmd.command.addArg(CMakeToolManager::mappedFilePath(buildDirectory).path());
+ cmd.command.addArg(CMakeToolManager::mappedFilePath(project, buildDirectory).path());
cmd.command.addArg("--target");
cmd.command.addArg(installTarget);
diff --git a/src/plugins/cmakeprojectmanager/cmakeformatter.cpp b/src/plugins/cmakeprojectmanager/cmakeformatter.cpp
index 4cbd4d2d50..c21b12a93a 100644
--- a/src/plugins/cmakeprojectmanager/cmakeformatter.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeformatter.cpp
@@ -79,7 +79,7 @@ public:
Space(10),
Group {
title(Tr::tr("Automatic Formatting on File Save")),
- autoFormatOnSave.groupChecker(),
+ groupChecker(autoFormatOnSave.groupChecker()),
// Conceptually, that's a Form, but this would look odd:
// xxxxxx [____]
// [x] xxxxxxxxxxxxxx
diff --git a/src/plugins/cmakeprojectmanager/cmakekitaspect.cpp b/src/plugins/cmakeprojectmanager/cmakekitaspect.cpp
index 3d8aa65475..bfafe8b2ea 100644
--- a/src/plugins/cmakeprojectmanager/cmakekitaspect.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakekitaspect.cpp
@@ -148,7 +148,7 @@ private:
// KitAspectWidget interface
void makeReadOnly() override { m_comboBox->setEnabled(false); }
- void addToLayoutImpl(Layouting::LayoutItem &builder) override
+ void addToLayoutImpl(Layouting::Layout &builder) override
{
addMutableAction(m_comboBox);
builder.addItem(m_comboBox);
@@ -376,7 +376,7 @@ private:
// KitAspectWidget interface
void makeReadOnly() override { m_changeButton->setEnabled(false); }
- void addToLayoutImpl(Layouting::LayoutItem &parent) override
+ void addToLayoutImpl(Layouting::Layout &parent) override
{
addMutableAction(m_label);
parent.addItem(m_label);
@@ -665,7 +665,7 @@ QVariant CMakeGeneratorKitAspectFactory::defaultValue(const Kit *k) const
});
if (it != known.constEnd()) {
const bool hasNinja = [k, tool] {
- if (Internal::settings().ninjaPath().isEmpty()) {
+ if (Internal::settings(nullptr).ninjaPath().isEmpty()) {
auto findNinja = [](const Environment &env) -> bool {
return !env.searchInPath("ninja").isEmpty();
};
@@ -800,7 +800,7 @@ void CMakeGeneratorKitAspectFactory::upgrade(Kit *k)
QTC_ASSERT(k, return);
const QVariant value = k->value(GENERATOR_ID);
- if (value.type() != QVariant::Map) {
+ if (value.typeId() != QMetaType::QVariantMap) {
GeneratorInfo info;
const QString fullName = value.toString();
const int pos = fullName.indexOf(" - ");
@@ -875,7 +875,7 @@ public:
private:
// KitAspectWidget interface
- void addToLayoutImpl(Layouting::LayoutItem &parent) override
+ void addToLayoutImpl(Layouting::Layout &parent) override
{
addMutableAction(m_summaryLabel);
parent.addItem(m_summaryLabel);
diff --git a/src/plugins/cmakeprojectmanager/cmakeprocess.cpp b/src/plugins/cmakeprojectmanager/cmakeprocess.cpp
index 9b4f5cd2ec..618c42892f 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprocess.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeprocess.cpp
@@ -103,7 +103,7 @@ void CMakeProcess::run(const BuildDirParameters &parameters, const QStringList &
}
// Copy the "package-manager" CMake code from the ${IDE:ResourcePath} to the build directory
- if (settings().packageManagerAutoSetup()) {
+ if (settings(parameters.project).packageManagerAutoSetup()) {
const FilePath localPackageManagerDir = buildDirectory.pathAppended(Constants::PACKAGE_MANAGER_DIR);
const FilePath idePackageManagerDir = FilePath::fromString(
parameters.expander->expand(QStringLiteral("%{IDE:ResourcePath}/package-manager")));
@@ -149,10 +149,11 @@ void CMakeProcess::run(const BuildDirParameters &parameters, const QStringList &
});
CommandLine commandLine(cmakeExecutable);
- commandLine.addArgs({"-S",
- CMakeToolManager::mappedFilePath(sourceDirectory).path(),
- "-B",
- CMakeToolManager::mappedFilePath(buildDirectory).path()});
+ commandLine.addArgs(
+ {"-S",
+ CMakeToolManager::mappedFilePath(parameters.project, sourceDirectory).path(),
+ "-B",
+ CMakeToolManager::mappedFilePath(parameters.project, buildDirectory).path()});
commandLine.addArgs(arguments);
TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM);
@@ -178,7 +179,7 @@ void CMakeProcess::stop()
QString addCMakePrefix(const QString &str)
{
static const QString prefix
- = ansiColoredText(Constants::OUTPUT_PREFIX, creatorTheme()->color(Theme::Token_Text_Muted));
+ = ansiColoredText(Constants::OUTPUT_PREFIX, creatorColor(Theme::Token_Text_Muted));
return prefix + str;
}
diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp
index 9e119bba88..1fafbc7d93 100644
--- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp
@@ -34,6 +34,7 @@ namespace CMakeProjectManager {
*/
CMakeProject::CMakeProject(const FilePath &fileName)
: Project(Utils::Constants::CMAKE_MIMETYPE, fileName)
+ , m_settings(this, true)
{
setId(CMakeProjectManager::Constants::CMAKE_PROJECT_ID);
setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID));
@@ -120,6 +121,14 @@ Internal::PresetsData CMakeProject::combinePresets(Internal::PresetsData &cmakeP
result.include = cmakeUserPresetsData.include;
}
+ result.vendor = cmakePresetsData.vendor;
+ if (result.vendor) {
+ if (cmakeUserPresetsData.vendor)
+ result.vendor->insert(cmakeUserPresetsData.vendor.value());
+ } else {
+ result.vendor = cmakeUserPresetsData.vendor;
+ }
+
auto combinePresetsInternal = [](auto &presetsHash,
auto &presets,
auto &userPresets,
@@ -232,6 +241,11 @@ void CMakeProject::setupBuildPresets(Internal::PresetsData &presetsData)
}
}
+Internal::CMakeSpecificSettings &CMakeProject::settings()
+{
+ return m_settings;
+}
+
void CMakeProject::readPresets()
{
auto parsePreset = [](const Utils::FilePath &presetFile) -> Internal::PresetsData {
diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.h b/src/plugins/cmakeprojectmanager/cmakeproject.h
index 6540dd4964..832977c41a 100644
--- a/src/plugins/cmakeprojectmanager/cmakeproject.h
+++ b/src/plugins/cmakeprojectmanager/cmakeproject.h
@@ -4,6 +4,7 @@
#pragma once
#include "cmake_global.h"
+#include "cmakespecificsettings.h"
#include "presetsparser.h"
#include <projectexplorer/project.h>
@@ -34,6 +35,8 @@ public:
void setOldPresetKits(const QList<ProjectExplorer::Kit *> &presetKits) const;
QList<ProjectExplorer::Kit *> oldPresetKits() const;
+ Internal::CMakeSpecificSettings &settings();
+
protected:
bool setupTarget(ProjectExplorer::Target *t) final;
@@ -50,6 +53,7 @@ private:
ProjectExplorer::Tasks m_issues;
Internal::PresetsData m_presetsData;
+ Internal::CMakeSpecificSettings m_settings;
};
} // namespace CMakeProjectManager
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h b/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h
index ea9461ba32..04922b6e9d 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h
@@ -41,10 +41,11 @@ const char CMAKE_BUILDCONFIGURATION_ID[] = "CMakeProjectManager.CMakeBuildConfig
const char M_CONTEXT[] = "CMakeEditor.ContextMenu";
namespace Settings {
-const char GENERAL_ID[] = "CMakeSpecifcSettings";
+const char GENERAL_ID[] = "CMakeSpecificSettings";
const char TOOLS_ID[] = "K.CMake.Tools";
const char FORMATTER_ID[] = "K.CMake.Formatter";
const char CATEGORY[] = "K.CMake";
+const char USE_GLOBAL_SETTINGS[] = "UseGlobalSettings";
} // namespace Settings
// Snippets
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp
index 6df1e7452d..5189c7934f 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp
@@ -12,6 +12,9 @@
#include "presetsmacros.h"
#include <coreplugin/messagemanager.h>
+#include <debugger/debuggeritem.h>
+#include <debugger/debuggeritemmanager.h>
+#include <debugger/debuggerkitaspect.h>
#include <projectexplorer/buildinfo.h>
#include <projectexplorer/kitaspects.h>
@@ -30,7 +33,9 @@
#include <QApplication>
#include <QLoggingCategory>
+#include <QUuid>
+using namespace Debugger;
using namespace ProjectExplorer;
using namespace QtSupport;
using namespace Utils;
@@ -60,6 +65,7 @@ struct DirectoryData
FilePath sysroot;
QtProjectImporter::QtVersionData qt;
QVector<ToolchainDescription> toolchains;
+ QVariant debugger;
};
static FilePaths scanDirectory(const FilePath &path, const QString &prefix)
@@ -205,6 +211,48 @@ FilePaths CMakeProjectImporter::presetCandidates()
return candidates;
}
+static QVariant findOrRegisterDebugger(
+ Environment &env, const std::optional<QVariantMap> &vendor, const QString &presetName)
+{
+ const QString debuggerKey("debugger");
+ if (!vendor || !vendor.value().contains(debuggerKey))
+ return {};
+
+ const QVariant debuggerVariant = vendor.value().value(debuggerKey);
+ FilePath debuggerPath = FilePath::fromUserInput(debuggerVariant.toString());
+ if (!debuggerPath.isEmpty()) {
+ if (debuggerPath.isRelativePath())
+ debuggerPath = env.searchInPath(debuggerPath.fileName());
+
+ const QString mainName = Tr::tr("CMake Preset (%1) %2 Debugger");
+ DebuggerItem debugger;
+ debugger.setCommand(debuggerPath);
+ debugger.setUnexpandedDisplayName(
+ mainName.arg(presetName).arg(debuggerPath.completeBaseName()));
+ debugger.setAutoDetected(false);
+ QString errorMessage;
+ debugger.reinitializeFromFile(&errorMessage, &env);
+ if (!errorMessage.isEmpty())
+ qCWarning(cmInputLog()) << "Error reinitializing debugger" << debuggerPath.toString()
+ << "Error:" << errorMessage;
+
+ return DebuggerItemManager::registerDebugger(debugger);
+ } else {
+ auto debuggerMap = debuggerVariant.toMap();
+ if (debuggerMap.isEmpty())
+ return {};
+
+ // Manually create an Id, otrhewise the Kit will not have a debugger set
+ if (!debuggerMap.contains("Id"))
+ debuggerMap.insert("Id", QUuid::createUuid().toString());
+
+ auto store = storeFromMap(debuggerMap);
+ DebuggerItem debugger(store);
+
+ return DebuggerItemManager::registerDebugger(debugger);
+ }
+}
+
Target *CMakeProjectImporter::preferredTarget(const QList<Target *> &possibleTargets)
{
for (Kit *kit : m_project->oldPresetKits()) {
@@ -835,6 +883,8 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
data->hasQmlDebugging = CMakeBuildConfiguration::hasQmlDebugging(config);
+ data->debugger = findOrRegisterDebugger(env, configurePreset.vendor, configurePreset.name);
+
QByteArrayList buildConfigurationTypes = {cache.valueOf("CMAKE_BUILD_TYPE")};
if (buildConfigurationTypes.front().isEmpty()) {
buildConfigurationTypes.clear();
@@ -1055,6 +1105,9 @@ Kit *CMakeProjectImporter::createKit(void *directoryData) const
if (!data->cmakePreset.isEmpty())
ensureBuildDirectory(*data, k);
+ if (data->debugger.isValid())
+ DebuggerKitAspect::setDebugger(k, data->debugger);
+
qCInfo(cmInputLog) << "Temporary Kit created.";
});
}
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp
index 9730c957fb..f3e512eebb 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp
@@ -18,6 +18,7 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/icore.h>
+#include <coreplugin/helpmanager.h>
#include <coreplugin/messagemanager.h>
#include <coreplugin/modemanager.h>
@@ -41,6 +42,7 @@
#include <utils/checkablemessagebox.h>
#include <utils/utilsicons.h>
+#include <QDesktopServices>
#include <QMessageBox>
using namespace Core;
@@ -55,6 +57,9 @@ class CMakeManager final : public QObject
public:
CMakeManager();
+ static bool isCMakeUrl(const QUrl &url);
+ static void openCMakeUrl(const QUrl &url);
+
private:
void updateCmakeActions(Node *node);
void clearCMakeCache(BuildSystem *buildSystem);
@@ -80,6 +85,29 @@ private:
bool m_canDebugCMake = false;
};
+bool CMakeManager::isCMakeUrl(const QUrl &url)
+{
+ const QString address = url.toString();
+ return address.startsWith("qthelp://org.cmake.");
+}
+
+void CMakeManager::openCMakeUrl(const QUrl &url)
+{
+ QString urlPrefix = "https://cmake.org/cmake/help/";
+
+ QRegularExpression version("^.*\\.([0-9])\\.([0-9]+)\\.[0-9]+$");
+ auto match = version.match(url.authority());
+ if (match.hasMatch())
+ urlPrefix.append(QString("v%1.%2").arg(match.captured(1)).arg(match.captured(2)));
+ else
+ urlPrefix.append("latest");
+
+ const QString address = url.toString();
+ const QString doc("/doc");
+ QDesktopServices::openUrl(
+ QUrl(urlPrefix + address.mid(address.lastIndexOf(doc) + doc.length())));
+}
+
CMakeManager::CMakeManager()
{
namespace PEC = ProjectExplorer::Constants;
@@ -321,12 +349,16 @@ void CMakeManager::enableBuildFileMenus(Node *node)
void CMakeManager::reloadCMakePresets()
{
+ CMakeProject *project = qobject_cast<CMakeProject *>(ProjectTree::currentProject());
+ if (!project)
+ return;
+
QMessageBox::StandardButton clickedButton = CheckableMessageBox::question(
Core::ICore::dialogParent(),
Tr::tr("Reload CMake Presets"),
Tr::tr("Re-generates the kits that were created for CMake presets. All manual "
"modifications to the CMake project settings will be lost."),
- settings().askBeforePresetsReload.askAgainCheckableDecider(),
+ settings(project).askBeforePresetsReload.askAgainCheckableDecider(),
QMessageBox::Yes | QMessageBox::Cancel,
QMessageBox::Yes,
QMessageBox::Yes,
@@ -334,15 +366,11 @@ void CMakeManager::reloadCMakePresets()
{QMessageBox::Yes, Tr::tr("Reload")},
});
- settings().writeSettings();
+ settings(project).writeSettings();
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;
@@ -458,4 +486,9 @@ void setupCMakeManager()
static CMakeManager theCMakeManager;
}
+void setupOnlineHelpManager()
+{
+ Core::HelpManager::addOnlineHelpHandler({CMakeManager::isCMakeUrl, CMakeManager::openCMakeUrl});
+}
+
} // CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h
index ba3a025f13..42e6a1c4e3 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h
@@ -6,5 +6,6 @@
namespace CMakeProjectManager::Internal {
void setupCMakeManager();
+void setupOnlineHelpManager();
} // CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs
index 1cbaac7b6d..98849a71f5 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs
@@ -8,6 +8,7 @@ QtcPlugin {
Depends { name: "Core" }
Depends { name: "CppEditor" }
+ Depends { name: "Debugger" }
Depends { name: "QmlJS" }
Depends { name: "ProjectExplorer" }
Depends { name: "TextEditor" }
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp
index 9c2a23dc06..f91c1438d2 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp
@@ -105,6 +105,8 @@ class CMakeProjectPlugin final : public ExtensionSystem::IPlugin
{
// Delay the restoration to allow the devices to load first.
QTimer::singleShot(0, this, [] { CMakeToolManager::restoreCMakeTools(); });
+
+ setupOnlineHelpManager();
}
void updateContextActions(ProjectExplorer::Node *node)
diff --git a/src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp b/src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp
index 28668b3567..eeab46f946 100644
--- a/src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp
@@ -1,30 +1,45 @@
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "cmakeproject.h"
#include "cmakespecificsettings.h"
#include "cmakeprojectconstants.h"
#include "cmakeprojectmanagertr.h"
-#include <coreplugin/icore.h>
#include <coreplugin/dialogs/ioptionspage.h>
+#include <coreplugin/icore.h>
+#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectimporter.h>
+#include <projectexplorer/projectpanelfactory.h>
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
+#include <QVBoxLayout>
+
+using namespace ProjectExplorer;
using namespace Utils;
namespace CMakeProjectManager::Internal {
-CMakeSpecificSettings &settings()
+CMakeSpecificSettings &settings(Project *project)
{
- static CMakeSpecificSettings theSettings;
- return theSettings;
+ static CMakeSpecificSettings theSettings(nullptr, false);
+ if (!project)
+ return theSettings;
+
+ CMakeProject *cmakeProject = qobject_cast<CMakeProject *>(project);
+ if (!cmakeProject || cmakeProject->settings().useGlobalSettings)
+ return theSettings;
+
+ return cmakeProject->settings();
}
-CMakeSpecificSettings::CMakeSpecificSettings()
+CMakeSpecificSettings::CMakeSpecificSettings(Project *p, bool autoApply)
+ : project(p)
{
setLayouter([this] {
using namespace Layouting;
@@ -43,8 +58,8 @@ CMakeSpecificSettings::CMakeSpecificSettings()
// TODO: fixup of QTCREATORBUG-26289 , remove in Qt Creator 7 or so
Core::ICore::settings()->remove("CMakeSpecificSettings/NinjaPath");
- setSettingsGroup("CMakeSpecificSettings");
- setAutoApply(false);
+ setSettingsGroup(Constants::Settings::GENERAL_ID);
+ setAutoApply(autoApply);
autorunCMake.setSettingsKey("AutorunCMake");
autorunCMake.setDefaultValue(true);
@@ -105,6 +120,57 @@ CMakeSpecificSettings::CMakeSpecificSettings()
"Junctions are used for CMake configure, build and install operations."));
readSettings();
+
+ if (project) {
+ // Re-read the settings. Reading in constructor is too early
+ connect(project, &Project::settingsLoaded, this, [this] { readSettings(); });
+
+ connect(project->projectImporter(), &ProjectImporter::cmakePresetsUpdated, this, [this] {
+ // clear settings first
+ Store data;
+ project->setNamedSettings(Constants::Settings::GENERAL_ID, variantFromStore(data));
+
+ readSettings();
+ });
+ }
+}
+
+void CMakeSpecificSettings::readSettings()
+{
+ if (!project) {
+ AspectContainer::readSettings();
+ } else {
+ Store data = storeFromVariant(project->namedSettings(Constants::Settings::GENERAL_ID));
+ if (data.isEmpty()) {
+ CMakeProject *cmakeProject = static_cast<CMakeProject *>(project);
+ if (cmakeProject->presetsData().havePresets && cmakeProject->presetsData().vendor) {
+ useGlobalSettings = false;
+ data = storeFromMap(cmakeProject->presetsData().vendor.value());
+ fromMap(data);
+
+ // Write the new loaded CMakePresets settings into .user file
+ writeSettings();
+ } else {
+ useGlobalSettings = true;
+ AspectContainer::readSettings();
+ }
+ } else {
+ useGlobalSettings = data.value(Constants::Settings::USE_GLOBAL_SETTINGS, true).toBool();
+ fromMap(data);
+ }
+ }
+}
+
+void CMakeSpecificSettings::writeSettings() const
+{
+ if (!project) {
+ AspectContainer::writeSettings();
+ } else {
+ Store data;
+ toMap(data);
+ data.insert(Constants::Settings::USE_GLOBAL_SETTINGS, useGlobalSettings);
+ project->setNamedSettings(Constants::Settings::GENERAL_ID, variantFromStore(data));
+ }
}
class CMakeSpecificSettingsPage final : public Core::IOptionsPage
@@ -117,10 +183,87 @@ public:
setDisplayCategory("CMake");
setCategory(Constants::Settings::CATEGORY);
setCategoryIconPath(Constants::Icons::SETTINGS_CATEGORY);
- setSettingsProvider([] { return &settings(); });
+ setSettingsProvider([] { return &settings(nullptr); });
}
};
const CMakeSpecificSettingsPage settingsPage;
+class CMakeProjectSettingsWidget : public ProjectSettingsWidget
+{
+public:
+ explicit CMakeProjectSettingsWidget(Project *project)
+ : m_widget(new QWidget)
+ , m_project(qobject_cast<CMakeProject *>(project))
+ , m_displayedSettings(project, true)
+ {
+ setGlobalSettingsId(Constants::Settings::GENERAL_ID);
+
+ // Construct the widget layout from the aspect container
+ const auto layout = new QVBoxLayout(this);
+ layout->setContentsMargins(0, 0, 0, 0);
+ if (auto layouter = m_displayedSettings.layouter())
+ layouter().attachTo(m_widget);
+ layout->addWidget(m_widget);
+
+ setUseGlobalSettings(m_displayedSettings.useGlobalSettings);
+ m_widget->setEnabled(!useGlobalSettings());
+
+ if (m_project) {
+ connect(
+ this, &ProjectSettingsWidget::useGlobalSettingsChanged, this, [this](bool useGlobal) {
+ m_widget->setEnabled(!useGlobal);
+ m_displayedSettings.useGlobalSettings = useGlobal;
+ m_displayedSettings.copyFrom(
+ useGlobal ? settings(nullptr) : m_project->settings());
+
+ m_project->settings().useGlobalSettings = useGlobal;
+ m_project->settings().writeSettings();
+ });
+
+ // React on Global settings changes
+ connect(&settings(nullptr), &AspectContainer::changed, this, [this] {
+ if (m_displayedSettings.useGlobalSettings)
+ m_displayedSettings.copyFrom(settings(nullptr));
+ });
+
+ // Reflect changes to the project settings in the displayed settings
+ connect(&m_project->settings(), &AspectContainer::changed, this, [this] {
+ if (!m_displayedSettings.useGlobalSettings)
+ m_displayedSettings.copyFrom(m_project->settings());
+ });
+
+ // React on project settings changes in the "CMake" project settings
+ connect(&m_displayedSettings, &AspectContainer::changed, this, [this] {
+ if (!m_displayedSettings.useGlobalSettings) {
+ m_project->settings().copyFrom(m_displayedSettings);
+ m_project->settings().writeSettings();
+ }
+ });
+ } else {
+ // Only for CMake projects
+ setUseGlobalSettingsCheckBoxEnabled(false);
+ }
+ }
+
+ QWidget *m_widget = nullptr;
+ CMakeProject *m_project = nullptr;
+ CMakeSpecificSettings m_displayedSettings;
+};
+
+class CMakeProjectSettingsPanelFactory final : public ProjectPanelFactory
+{
+public:
+ CMakeProjectSettingsPanelFactory()
+ {
+ setPriority(120);
+ setDisplayName("CMake");
+ setCreateWidgetFunction([](Project *project) {
+ return new CMakeProjectSettingsWidget(project);
+ });
+ }
+};
+
+const CMakeProjectSettingsPanelFactory projectSettingsPane;
+
} // CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmakespecificsettings.h b/src/plugins/cmakeprojectmanager/cmakespecificsettings.h
index cbec63dffe..653234a889 100644
--- a/src/plugins/cmakeprojectmanager/cmakespecificsettings.h
+++ b/src/plugins/cmakeprojectmanager/cmakespecificsettings.h
@@ -5,12 +5,20 @@
#include <utils/aspects.h>
+namespace ProjectExplorer {
+class Project;
+}
+
namespace CMakeProjectManager::Internal {
class CMakeSpecificSettings final : public Utils::AspectContainer
{
+ ProjectExplorer::Project *project{nullptr};
public:
- CMakeSpecificSettings();
+ CMakeSpecificSettings(ProjectExplorer::Project *project, bool autoApply);
+
+ void readSettings() final;
+ void writeSettings() const final;
Utils::BoolAspect autorunCMake{this};
Utils::FilePathAspect ninjaPath{this};
@@ -20,8 +28,10 @@ public:
Utils::BoolAspect showSourceSubFolders{this};
Utils::BoolAspect showAdvancedOptionsByDefault{this};
Utils::BoolAspect useJunctionsForSourceAndBuildDirectories{this};
+
+ bool useGlobalSettings{true};
};
-CMakeSpecificSettings &settings();
+CMakeSpecificSettings &settings(ProjectExplorer::Project *project);
} // CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmaketool.cpp b/src/plugins/cmakeprojectmanager/cmaketool.cpp
index 4cb638659a..444ff299f9 100644
--- a/src/plugins/cmakeprojectmanager/cmaketool.cpp
+++ b/src/plugins/cmakeprojectmanager/cmaketool.cpp
@@ -36,7 +36,6 @@ static Q_LOGGING_CATEGORY(cmakeToolLog, "qtc.cmake.tool", QtWarningMsg);
const char CMAKE_INFORMATION_ID[] = "Id";
const char CMAKE_INFORMATION_COMMAND[] = "Binary";
const char CMAKE_INFORMATION_DISPLAYNAME[] = "DisplayName";
-const char CMAKE_INFORMATION_AUTORUN[] = "AutoRun";
const char CMAKE_INFORMATION_QCH_FILE_PATH[] = "QchFile";
// obsolete since Qt Creator 5. Kept for backward compatibility
const char CMAKE_INFORMATION_AUTO_CREATE_BUILD_DIRECTORY[] = "AutoCreateBuildDirectory";
@@ -113,7 +112,6 @@ CMakeTool::CMakeTool(const Store &map, bool fromSdk) :
Id::fromSetting(map.value(CMAKE_INFORMATION_ID)))
{
m_displayName = map.value(CMAKE_INFORMATION_DISPLAYNAME).toString();
- m_isAutoRun = map.value(CMAKE_INFORMATION_AUTORUN, true).toBool();
m_autoCreateBuildDirectory = map.value(CMAKE_INFORMATION_AUTO_CREATE_BUILD_DIRECTORY, false).toBool();
m_readerType = Internal::readerTypeFromString(
map.value(CMAKE_INFORMATION_READERTYPE).toString());
@@ -183,7 +181,6 @@ Store CMakeTool::toMap() const
data.insert(CMAKE_INFORMATION_ID, m_id.toSetting());
data.insert(CMAKE_INFORMATION_COMMAND, m_executable.toString());
data.insert(CMAKE_INFORMATION_QCH_FILE_PATH, m_qchFilePath.toString());
- data.insert(CMAKE_INFORMATION_AUTORUN, m_isAutoRun);
data.insert(CMAKE_INFORMATION_AUTO_CREATE_BUILD_DIRECTORY, m_autoCreateBuildDirectory);
if (m_readerType)
data.insert(CMAKE_INFORMATION_READERTYPE,
@@ -233,11 +230,6 @@ FilePath CMakeTool::cmakeExecutable(const FilePath &path)
return resolvedPath;
}
-bool CMakeTool::isAutoRun() const
-{
- return m_isAutoRun;
-}
-
QList<CMakeTool::Generator> CMakeTool::supportedGenerators() const
{
return isValid() ? m_introspection->m_generators : QList<CMakeTool::Generator>();
diff --git a/src/plugins/cmakeprojectmanager/cmaketool.h b/src/plugins/cmakeprojectmanager/cmaketool.h
index cf13ad49c5..51012e699c 100644
--- a/src/plugins/cmakeprojectmanager/cmaketool.h
+++ b/src/plugins/cmakeprojectmanager/cmaketool.h
@@ -79,8 +79,6 @@ public:
Utils::Id id() const { return m_id; }
Utils::Store toMap () const;
- void setAutorun(bool autoRun) { m_isAutoRun = autoRun; }
-
void setFilePath(const Utils::FilePath &executable);
Utils::FilePath filePath() const;
Utils::FilePath cmakeExecutable() const;
@@ -130,7 +128,6 @@ private:
Utils::FilePath m_executable;
Utils::FilePath m_qchFilePath;
- bool m_isAutoRun = true;
bool m_isAutoDetected = false;
QString m_detectionSource;
bool m_autoCreateBuildDirectory = false;
diff --git a/src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp b/src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp
index e20c3c58eb..fa184e016b 100644
--- a/src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp
+++ b/src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp
@@ -342,15 +342,6 @@ void CMakeToolManager::restoreCMakeTools()
updateDocumentation();
emit m_instance->cmakeToolsLoaded();
-
- // Store the default CMake tool "Autorun CMake" value globally
- // TODO: Remove in Qt Creator 13
- Internal::CMakeSpecificSettings &s = Internal::settings();
- if (s.autorunCMake() == s.autorunCMake.defaultValue()) {
- CMakeTool *cmake = defaultCMakeTool();
- s.autorunCMake.setValue(cmake ? cmake->isAutoRun() : true);
- s.writeSettings();
- }
}
void CMakeToolManager::updateDocumentation()
@@ -445,7 +436,7 @@ QString CMakeToolManager::toolTipForRstHelpFile(const FilePath &helpFile)
return tooltip;
}
-FilePath CMakeToolManager::mappedFilePath(const FilePath &path)
+FilePath CMakeToolManager::mappedFilePath(Project *project, const FilePath &path)
{
if (!HostOsInfo::isWindowsHost())
return path;
@@ -453,16 +444,14 @@ FilePath CMakeToolManager::mappedFilePath(const FilePath &path)
if (path.needsDevice())
return path;
- auto project = ProjectManager::startupProject();
auto environment = Environment::systemEnvironment();
if (project)
environment.modify(project->additionalEnvironment());
const bool enableJunctions
- = QVariant(
- environment.value_or("QTC_CMAKE_USE_JUNCTIONS",
- Internal::settings().useJunctionsForSourceAndBuildDirectories()
- ? "1"
- : "0"))
+ = QVariant(environment.value_or(
+ "QTC_CMAKE_USE_JUNCTIONS",
+ Internal::settings(project).useJunctionsForSourceAndBuildDirectories() ? "1"
+ : "0"))
.toBool();
if (!enableJunctions)
diff --git a/src/plugins/cmakeprojectmanager/cmaketoolmanager.h b/src/plugins/cmakeprojectmanager/cmaketoolmanager.h
index 1836f36c86..0917c2739f 100644
--- a/src/plugins/cmakeprojectmanager/cmaketoolmanager.h
+++ b/src/plugins/cmakeprojectmanager/cmaketoolmanager.h
@@ -14,6 +14,10 @@
#include <memory>
+namespace ProjectExplorer {
+class Project;
+}
+
namespace CMakeProjectManager {
class CMAKE_EXPORT CMakeToolManager : public QObject
@@ -44,7 +48,7 @@ public:
static QString toolTipForRstHelpFile(const Utils::FilePath &helpFile);
- static Utils::FilePath mappedFilePath(const Utils::FilePath &path);
+ static Utils::FilePath mappedFilePath(ProjectExplorer::Project *project, const Utils::FilePath &path);
public slots:
QList<Utils::Id> autoDetectCMakeForDevice(const Utils::FilePaths &searchPaths,
diff --git a/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp b/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp
index b877998a07..e8e5a8a64e 100644
--- a/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp
+++ b/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp
@@ -174,14 +174,8 @@ void CMakeToolSettingsAccessor::saveCMakeTools(const QList<CMakeTool *> &cmakeTo
data.insert(CMAKE_TOOL_DEFAULT_KEY, defaultId.toSetting());
int count = 0;
- const bool autoRun = settings().autorunCMake();
for (CMakeTool *item : cmakeTools) {
Utils::FilePath fi = item->cmakeExecutable();
-
- // Gobal Autorun value will be set for all tools
- // TODO: Remove in Qt Creator 13
- item->setAutorun(autoRun);
-
if (fi.needsDevice() || fi.isExecutableFile()) { // be graceful for device related stuff
Store tmp = item->toMap();
if (tmp.isEmpty())
diff --git a/src/plugins/cmakeprojectmanager/configmodel.cpp b/src/plugins/cmakeprojectmanager/configmodel.cpp
index 953486e2da..287615650c 100644
--- a/src/plugins/cmakeprojectmanager/configmodel.cpp
+++ b/src/plugins/cmakeprojectmanager/configmodel.cpp
@@ -570,8 +570,8 @@ QVariant ConfigModelTreeItem::data(int column, int role) const
mismatch = !dataItem->kitValue.isEmpty() && dataItem->kitValue != value;
else
mismatch = !dataItem->initialValue.isEmpty() && dataItem->initialValue != value;
- return Utils::creatorTheme()->color(mismatch ? Utils::Theme::TextColorError
- : Utils::Theme::TextColorNormal);
+ return Utils::creatorColor(mismatch ? Utils::Theme::TextColorError
+ : Utils::Theme::TextColorNormal);
};
const QString value = currentValue();
diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
index 4134da09e7..777bb0e46c 100644
--- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
+++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
@@ -308,7 +308,8 @@ static CMakeBuildTarget toBuildTarget(const TargetDetails &t,
std::optional<QString> dllName;
if (buildDir.osType() == OsTypeWindows && (f.role == "libraries")) {
- part = FilePath::fromUserInput(part).fileName();
+ const auto partAsFilePath = FilePath::fromUserInput(part);
+ part = partAsFilePath.fileName();
// Skip object libraries on Windows. This case can happen with static qml plugins
if (part.endsWith(".obj") || part.endsWith(".o"))
@@ -322,12 +323,15 @@ static CMakeBuildTarget toBuildTarget(const TargetDetails &t,
}
// MinGW has libQt6Core.a -> Qt6Core.dll
+ // but libFoo.dll.a was already handled above
const QString mingwPrefix("lib");
- const QString mingwSuffix(".a");
- if (part.startsWith(mingwPrefix) && part.endsWith(mingwSuffix))
- dllName = part.chopped(mingwSuffix.length())
+ const QString mingwSuffix("a");
+ const QString completeSuffix = partAsFilePath.completeSuffix();
+ if (part.startsWith(mingwPrefix) && completeSuffix == mingwSuffix) {
+ dllName = part.chopped(mingwSuffix.length() + 1/*the '.'*/)
.sliced(mingwPrefix.length())
.append(".dll");
+ }
}
if (!tmp.isEmpty() && tmp.isDir()) {
@@ -700,7 +704,7 @@ static void addCompileGroups(ProjectNode *targetRoot,
if (buildDirQmldirOrRcc || otherDirQmldirOrMetatypes || buildDirPluginCpp)
node->setIsGenerated(true);
- const bool showSourceFolders = settings().showSourceSubFolders()
+ const bool showSourceFolders = settings(targetRoot->getProject()).showSourceSubFolders()
&& defaultCMakeSourceGroupFolder(td.sourceGroups[si.sourceGroup]);
// Where does the file node need to go?
@@ -714,7 +718,7 @@ static void addCompileGroups(ProjectNode *targetRoot,
}
for (size_t i = 0; i < sourceGroupFileNodes.size(); ++i) {
- const bool showSourceFolders = settings().showSourceSubFolders()
+ const bool showSourceFolders = settings(targetRoot->getProject()).showSourceSubFolders()
&& defaultCMakeSourceGroupFolder(td.sourceGroups[i]);
std::vector<std::unique_ptr<FileNode>> &current = sourceGroupFileNodes[i];
diff --git a/src/plugins/cmakeprojectmanager/fileapiparser.cpp b/src/plugins/cmakeprojectmanager/fileapiparser.cpp
index f72bcd5a68..d404d124db 100644
--- a/src/plugins/cmakeprojectmanager/fileapiparser.cpp
+++ b/src/plugins/cmakeprojectmanager/fileapiparser.cpp
@@ -874,10 +874,11 @@ FileApiData FileApiParser::parseData(QPromise<std::shared_ptr<FileApiQtcData>> &
errorMessage);
if (codeModels.size() == 0) {
- errorMessage = Tr::tr("CMake project configuration failed. No CMake configuration for "
- "build type \"%1\" found.")
+ //: General Messages refers to the output view
+ errorMessage = Tr::tr(
+ "CMake project configuration failed. No CMake configuration for "
+ "build type \"%1\" found. Check General Messages for more information.")
.arg(cmakeBuildType);
- errorMessage += Tr::tr(" Check General messages for more information.");
return result;
}
diff --git a/src/plugins/cmakeprojectmanager/fileapireader.cpp b/src/plugins/cmakeprojectmanager/fileapireader.cpp
index cb76705cdc..b859469cbc 100644
--- a/src/plugins/cmakeprojectmanager/fileapireader.cpp
+++ b/src/plugins/cmakeprojectmanager/fileapireader.cpp
@@ -12,8 +12,6 @@
#include <coreplugin/messagemanager.h>
-#include <extensionsystem/pluginmanager.h>
-
#include <projectexplorer/projectexplorer.h>
#include <utils/algorithm.h>
@@ -132,7 +130,8 @@ void FileApiReader::parse(bool forceCMakeRun,
// * A query file is newer than the reply file
const bool hasArguments = !args.isEmpty();
const bool replyFileMissing = !replyFile.exists();
- const bool cmakeFilesChanged = m_parameters.cmakeTool() && settings().autorunCMake()
+ const bool cmakeFilesChanged = m_parameters.cmakeTool()
+ && settings(m_parameters.project).autorunCMake()
&& anyOf(m_cmakeFiles, [&replyFile](const CMakeFileInfo &info) {
return !info.isGenerated
&& info.path.lastModified() > replyFile.lastModified();
@@ -172,7 +171,7 @@ void FileApiReader::stop()
if (m_future) {
m_future->cancel();
- ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(*m_future);
+ Utils::futureSynchronizer()->addFuture(*m_future);
}
m_future = {};
m_isParsing = false;
diff --git a/src/plugins/cmakeprojectmanager/presetsparser.cpp b/src/plugins/cmakeprojectmanager/presetsparser.cpp
index 3befe4d99d..6c15f217f0 100644
--- a/src/plugins/cmakeprojectmanager/presetsparser.cpp
+++ b/src/plugins/cmakeprojectmanager/presetsparser.cpp
@@ -148,6 +148,30 @@ std::optional<PresetsDetails::Condition> parseCondition(const QJsonValue &jsonVa
return condition;
}
+bool parseVendor(const QJsonValue &jsonValue, std::optional<QVariantMap> &vendorSettings)
+{
+ // The whole section is optional
+ if (jsonValue.isUndefined())
+ return true;
+ if (!jsonValue.isObject())
+ return false;
+
+ const QJsonObject object = jsonValue.toObject();
+ const QJsonValue qtIo = object.value("qt.io/QtCreator/1.0");
+ if (qtIo.isUndefined())
+ return true;
+ if (!qtIo.isObject())
+ return false;
+
+ const QJsonObject qtIoObject = qtIo.toObject();
+ vendorSettings = QVariantMap();
+ for (const QString &settingKey : qtIoObject.keys()) {
+ const QJsonValue settingValue = qtIoObject.value(settingKey);
+ vendorSettings->insert(settingKey, settingValue.toVariant());
+ }
+ return true;
+}
+
bool parseConfigurePresets(const QJsonValue &jsonValue,
QList<PresetsDetails::ConfigurePreset> &configurePresets,
const Utils::FilePath &fileDir)
@@ -188,6 +212,9 @@ bool parseConfigurePresets(const QJsonValue &jsonValue,
if (object.contains("condition"))
preset.condition = parseCondition(object.value("condition"));
+ if (object.contains("vendor"))
+ parseVendor(object.value("vendor"), preset.vendor);
+
if (object.contains("displayName"))
preset.displayName = object.value("displayName").toString();
if (object.contains("description"))
@@ -378,6 +405,9 @@ bool parseBuildPresets(const QJsonValue &jsonValue,
if (object.contains("condition"))
preset.condition = parseCondition(object.value("condition"));
+ if (object.contains("vendor"))
+ parseVendor(object.value("vendor"), preset.vendor);
+
if (object.contains("displayName"))
preset.displayName = object.value("displayName").toString();
if (object.contains("description"))
@@ -502,13 +532,18 @@ bool PresetsParser::parse(const Utils::FilePath &jsonFile, QString &errorMessage
return false;
}
+ // optional
+ if (!parseVendor(root.value("vendor"), m_presetsData.vendor)) {
+ errorMessage = ::CMakeProjectManager::Tr::tr("Invalid \"vendor\" section in %1 file")
+ .arg(jsonFile.fileName());
+ }
+
return true;
}
-static QHash<QString, QString> merge(const QHash<QString, QString> &first,
- const QHash<QString, QString> &second)
+static QVariantMap merge(const QVariantMap &first, const QVariantMap &second)
{
- QHash<QString, QString> result = first;
+ QVariantMap result = first;
for (auto it = second.constKeyValueBegin(); it != second.constKeyValueEnd(); ++it) {
result[it->first] = it->second;
}
diff --git a/src/plugins/cmakeprojectmanager/presetsparser.h b/src/plugins/cmakeprojectmanager/presetsparser.h
index 6df09014af..cfbf565489 100644
--- a/src/plugins/cmakeprojectmanager/presetsparser.h
+++ b/src/plugins/cmakeprojectmanager/presetsparser.h
@@ -94,7 +94,7 @@ public:
std::optional<bool> hidden = false;
std::optional<QStringList> inherits;
std::optional<Condition> condition;
- std::optional<QHash<QString, QString>> vendor;
+ std::optional<QVariantMap> vendor;
std::optional<QString> displayName;
std::optional<QString> description;
std::optional<QString> generator;
@@ -120,7 +120,7 @@ public:
std::optional<bool> hidden = false;
std::optional<QStringList> inherits;
std::optional<Condition> condition;
- std::optional<QHash<QString, QString>> vendor;
+ std::optional<QVariantMap> vendor;
std::optional<QString> displayName;
std::optional<QString> description;
std::optional<Utils::Environment> environment;
@@ -142,7 +142,7 @@ public:
int version = 0;
bool havePresets = false;
QVersionNumber cmakeMinimimRequired;
- QHash<QString, QString> vendor;
+ std::optional<QVariantMap> vendor;
std::optional<QStringList> include;
Utils::FilePath fileDir;
QList<PresetsDetails::ConfigurePreset> configurePresets;
diff --git a/src/plugins/coco/Coco.json.in b/src/plugins/coco/Coco.json.in
index 01beb8c498..20206aacb9 100644
--- a/src/plugins/coco/Coco.json.in
+++ b/src/plugins/coco/Coco.json.in
@@ -14,6 +14,6 @@
"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" : "Squish Coco support. Squish Coco is a code coverage tool for Tcl, QML, C# and C/C++.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/compilationdatabaseprojectmanager/CompilationDatabaseProjectManager.json.in b/src/plugins/compilationdatabaseprojectmanager/CompilationDatabaseProjectManager.json.in
index 4842c3d73c..62aba49a22 100644
--- a/src/plugins/compilationdatabaseprojectmanager/CompilationDatabaseProjectManager.json.in
+++ b/src/plugins/compilationdatabaseprojectmanager/CompilationDatabaseProjectManager.json.in
@@ -15,7 +15,7 @@
],
"Category" : "Build Systems",
"Description" : "Compilation Database project support.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
"<?xml version='1.0'?>",
diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp
index 4ab893a9e6..1ba5a9a1f1 100644
--- a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp
+++ b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp
@@ -8,8 +8,6 @@
#include <coreplugin/progressmanager/progressmanager.h>
-#include <extensionsystem/pluginmanager.h>
-
#include <projectexplorer/treescanner.h>
#include <utils/async.h>
@@ -190,7 +188,7 @@ void CompilationDbParser::start()
"CompilationDatabase.Parse");
++m_runningParserJobs;
m_parserWatcher.setFuture(future);
- ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(future);
+ Utils::futureSynchronizer()->addFuture(future);
}
void CompilationDbParser::stop()
diff --git a/src/plugins/compilerexplorer/CompilerExplorer.json.in b/src/plugins/compilerexplorer/CompilerExplorer.json.in
index db771bd9f9..e7e0938fa8 100644
--- a/src/plugins/compilerexplorer/CompilerExplorer.json.in
+++ b/src/plugins/compilerexplorer/CompilerExplorer.json.in
@@ -15,7 +15,7 @@
"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" : "Integrates https://godbolt.org into Qt Creator.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
"<?xml version='1.0'?>",
diff --git a/src/plugins/compilerexplorer/compilerexplorer.qrc b/src/plugins/compilerexplorer/compilerexplorer.qrc
index ea46de321c..c4a75150d6 100644
--- a/src/plugins/compilerexplorer/compilerexplorer.qrc
+++ b/src/plugins/compilerexplorer/compilerexplorer.qrc
@@ -4,5 +4,7 @@
<file>wizard/cpp/file.qtce</file>
<file>wizard/python/wizard.json</file>
<file>wizard/python/file.qtce</file>
+ <file>wizard/qtcpp/file.qtce</file>
+ <file>wizard/qtcpp/wizard.json</file>
</qresource>
</RCC>
diff --git a/src/plugins/compilerexplorer/compilerexploreraspects.cpp b/src/plugins/compilerexplorer/compilerexploreraspects.cpp
index 9096b7dabe..1c1a8e6b96 100644
--- a/src/plugins/compilerexplorer/compilerexploreraspects.cpp
+++ b/src/plugins/compilerexplorer/compilerexploreraspects.cpp
@@ -91,7 +91,7 @@ void LibrarySelectionAspect::setVariantValue(const QVariant &value, Announcement
setValue(map, howToAnnounce);
}
-void LibrarySelectionAspect::addToLayout(Layouting::LayoutItem &parent)
+void LibrarySelectionAspect::addToLayout(Layouting::Layout &parent)
{
using namespace Layouting;
@@ -223,12 +223,12 @@ void LibrarySelectionAspect::addToLayout(Layouting::LayoutItem &parent)
// clang-format off
QStackedWidget *stack = static_cast<QStackedWidget*>(
Stack {
- noMargin,
Row { noMargin, displayLabel, editBtn },
Row { noMargin, nameCombo, versionCombo, clearBtn }
}.emerge()
);
// clang-format on
+ stack->setContentsMargins({});
connect(editBtn, &QPushButton::clicked, stack, [stack] { stack->setCurrentIndex(1); });
connect(this, &LibrarySelectionAspect::returnToDisplay, stack, [stack] {
stack->setCurrentIndex(0);
diff --git a/src/plugins/compilerexplorer/compilerexploreraspects.h b/src/plugins/compilerexplorer/compilerexploreraspects.h
index c89604b7aa..bb2e86a268 100644
--- a/src/plugins/compilerexplorer/compilerexploreraspects.h
+++ b/src/plugins/compilerexplorer/compilerexploreraspects.h
@@ -68,7 +68,7 @@ public:
LibrarySelectionAspect(Utils::AspectContainer *container = nullptr);
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
using ResultCallback = std::function<void(QList<QStandardItem *>)>;
using FillCallback = std::function<void(ResultCallback)>;
diff --git a/src/plugins/compilerexplorer/compilerexplorereditor.cpp b/src/plugins/compilerexplorer/compilerexplorereditor.cpp
index e1e564c733..340cc7ba2c 100644
--- a/src/plugins/compilerexplorer/compilerexplorereditor.cpp
+++ b/src/plugins/compilerexplorer/compilerexplorereditor.cpp
@@ -264,7 +264,7 @@ SourceEditorWidget::SourceEditorWidget(const std::shared_ptr<SourceSettings> &se
Column {
toolBar,
m_codeEditor,
- noMargin(), spacing(0),
+ noMargin, spacing(0),
}.attachTo(this);
// clang-format on
@@ -370,7 +370,6 @@ CompilerWidget::CompilerWidget(const std::shared_ptr<SourceSettings> &sourceSett
connect(m_asmEditor, &AsmEditorWidget::gotFocus, this, &CompilerWidget::gotFocus);
auto advButton = new QToolButton;
- QSplitter *splitter{nullptr};
auto advDlg = new QAction;
advDlg->setIcon(Utils::Icons::SETTINGS_TOOLBAR.icon());
@@ -400,7 +399,6 @@ CompilerWidget::CompilerWidget(const std::shared_ptr<SourceSettings> &sourceSett
});
// clang-format off
-
Row {
m_compilerSettings->compiler,
advButton,
@@ -411,11 +409,10 @@ CompilerWidget::CompilerWidget(const std::shared_ptr<SourceSettings> &sourceSett
Column {
toolBar,
Splitter {
- bindTo(&splitter),
m_asmEditor,
createTerminal()
},
- noMargin(), spacing(0),
+ noMargin, spacing(0),
}.attachTo(this);
// clang-format on
@@ -426,27 +423,27 @@ SearchableTerminal *CompilerWidget::createTerminal()
{
m_resultTerminal = new SearchableTerminal();
m_resultTerminal->setAllowBlinkingCursor(false);
- std::array<QColor, 20> colors{Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi0),
- Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi1),
- Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi2),
- Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi3),
- Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi4),
- Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi5),
- Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi6),
- Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi7),
- Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi8),
- Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi9),
- Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi10),
- Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi11),
- Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi12),
- Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi13),
- Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi14),
- Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi15),
-
- Utils::creatorTheme()->color(Utils::Theme::TerminalForeground),
- Utils::creatorTheme()->color(Utils::Theme::TerminalBackground),
- Utils::creatorTheme()->color(Utils::Theme::TerminalSelection),
- Utils::creatorTheme()->color(Utils::Theme::TerminalFindMatch)};
+ std::array<QColor, 20> colors{Utils::creatorColor(Utils::Theme::TerminalAnsi0),
+ Utils::creatorColor(Utils::Theme::TerminalAnsi1),
+ Utils::creatorColor(Utils::Theme::TerminalAnsi2),
+ Utils::creatorColor(Utils::Theme::TerminalAnsi3),
+ Utils::creatorColor(Utils::Theme::TerminalAnsi4),
+ Utils::creatorColor(Utils::Theme::TerminalAnsi5),
+ Utils::creatorColor(Utils::Theme::TerminalAnsi6),
+ Utils::creatorColor(Utils::Theme::TerminalAnsi7),
+ Utils::creatorColor(Utils::Theme::TerminalAnsi8),
+ Utils::creatorColor(Utils::Theme::TerminalAnsi9),
+ Utils::creatorColor(Utils::Theme::TerminalAnsi10),
+ Utils::creatorColor(Utils::Theme::TerminalAnsi11),
+ Utils::creatorColor(Utils::Theme::TerminalAnsi12),
+ Utils::creatorColor(Utils::Theme::TerminalAnsi13),
+ Utils::creatorColor(Utils::Theme::TerminalAnsi14),
+ Utils::creatorColor(Utils::Theme::TerminalAnsi15),
+
+ Utils::creatorColor(Utils::Theme::TerminalForeground),
+ Utils::creatorColor(Utils::Theme::TerminalBackground),
+ Utils::creatorColor(Utils::Theme::TerminalSelection),
+ Utils::creatorColor(Utils::Theme::TerminalFindMatch)};
m_resultTerminal->setColors(colors);
diff --git a/src/plugins/compilerexplorer/wizard/qtcpp/file.qtce b/src/plugins/compilerexplorer/wizard/qtcpp/file.qtce
new file mode 100644
index 0000000000..896dacbbfe
--- /dev/null
+++ b/src/plugins/compilerexplorer/wizard/qtcpp/file.qtce
@@ -0,0 +1,18 @@
+{
+ "Sources": [
+ {
+ "Compilers": [
+ {
+ "ExecuteCode": true,
+ "Id": "clang_trunk",
+ "Libraries": {
+ "pcre2": "1042",
+ "qt": "660"
+ },
+ "Options": "-O3"
+ }
+ ],
+ "Source": "#include <QString>\\n#include <QDebug>\\n\\nint main() {\\n QString msg = \\"Hello World!\\";\\n qDebug() << msg;\\n return 0;\\n}"
+ }
+ ]
+}
diff --git a/src/plugins/compilerexplorer/wizard/qtcpp/wizard.json b/src/plugins/compilerexplorer/wizard/qtcpp/wizard.json
new file mode 100644
index 0000000000..e0d6edab85
--- /dev/null
+++ b/src/plugins/compilerexplorer/wizard/qtcpp/wizard.json
@@ -0,0 +1,37 @@
+{
+ "version": 1,
+ "supportedProjectTypes": [],
+ "id": "QTCE.QtCppSource",
+ "category": "U.CompilerExplorer",
+ "trDescription": "Creates an example CompilerExplorer setup for C++ with Qt Libraries.",
+ "trDisplayName": "Compiler Explorer Qt & C++ Source",
+ "trDisplayCategory": "Compiler Explorer",
+ "icon": ":/compilerexplorer/logos/ce.ico",
+ "iconKind": "Plain",
+ "options": {
+ "key": "DefaultSuffix",
+ "value": "%{JS: Util.preferredSuffix('application/compiler-explorer')}"
+ },
+ "pages": [
+ {
+ "trDisplayName": "Location",
+ "trShortTitle": "Location",
+ "typeId": "File"
+ },
+ {
+ "trDisplayName": "Project Management",
+ "trShortTitle": "Summary",
+ "typeId": "Summary"
+ }
+ ],
+ "generators": [
+ {
+ "typeId": "File",
+ "data": {
+ "source": "file.qtce",
+ "target": "%{JS: Util.fileName(value('TargetPath'), value('DefaultSuffix'))}",
+ "openInEditor": true
+ }
+ }
+ ]
+}
diff --git a/src/plugins/conan/Conan.json.in b/src/plugins/conan/Conan.json.in
index b81813c0a6..7fa154aed5 100644
--- a/src/plugins/conan/Conan.json.in
+++ b/src/plugins/conan/Conan.json.in
@@ -14,6 +14,6 @@
],
"Experimental" : true,
"Description" : "Conan integration.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/copilot/Copilot.json.in b/src/plugins/copilot/Copilot.json.in
index 81f326770d..449a751e3b 100644
--- a/src/plugins/copilot/Copilot.json.in
+++ b/src/plugins/copilot/Copilot.json.in
@@ -14,6 +14,6 @@
"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",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/coreplugin/Core.json.in b/src/plugins/coreplugin/Core.json.in
index 8f121d61a3..aa99a3a586 100644
--- a/src/plugins/coreplugin/Core.json.in
+++ b/src/plugins/coreplugin/Core.json.in
@@ -15,7 +15,7 @@
],
"Category" : "Core",
"Description" : "The core plugin for the Qt IDE.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
"Arguments" : [
{
"Name" : "-color",
diff --git a/src/plugins/coreplugin/actionmanager/actionmanager.cpp b/src/plugins/coreplugin/actionmanager/actionmanager.cpp
index 9176ad5660..b9bbf23f85 100644
--- a/src/plugins/coreplugin/actionmanager/actionmanager.cpp
+++ b/src/plugins/coreplugin/actionmanager/actionmanager.cpp
@@ -875,7 +875,7 @@ void ActionManagerPrivate::readUserSettings(Id id, Command *cmd)
settings->beginGroup(kKeyboardSettingsKeyV2);
if (settings->contains(id.toKey())) {
const QVariant v = settings->value(id.toKey());
- if (QMetaType::Type(v.type()) == QMetaType::QStringList) {
+ if (v.typeId() == QMetaType::QStringList) {
cmd->setKeySequences(Utils::transform<QList>(v.toStringList(), [](const QString &s) {
return QKeySequence::fromString(s);
}));
diff --git a/src/plugins/coreplugin/actionsfilter.cpp b/src/plugins/coreplugin/actionsfilter.cpp
index 93efa1dd2c..8b5f23df71 100644
--- a/src/plugins/coreplugin/actionsfilter.cpp
+++ b/src/plugins/coreplugin/actionsfilter.cpp
@@ -10,8 +10,6 @@
#include "icore.h"
#include "locator/locatormanager.h"
-#include <extensionsystem/pluginmanager.h>
-
#include <utils/algorithm.h>
#include <utils/async.h>
#include <utils/fuzzymatcher.h>
@@ -192,7 +190,6 @@ LocatorMatcherTasks ActionsFilter::matchers()
storage->reportOutput(m_entries);
return SetupResult::StopWithSuccess;
}
- async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
async.setConcurrentCallData(matches, *storage, m_entries);
return SetupResult::Continue;
};
diff --git a/src/plugins/coreplugin/coreconstants.h b/src/plugins/coreplugin/coreconstants.h
index a259717ba8..d8d2388fb1 100644
--- a/src/plugins/coreplugin/coreconstants.h
+++ b/src/plugins/coreplugin/coreconstants.h
@@ -119,6 +119,7 @@ const char GOTOPREVINHISTORY[] = "QtCreator.GotoPreviousInHistory";
const char GO_BACK[] = "QtCreator.GoBack";
const char GO_FORWARD[] = "QtCreator.GoForward";
const char GOTOLASTEDIT[] = "QtCreator.GotoLastEdit";
+const char REOPEN_CLOSED_EDITOR[] = "QtCreator.ReopenClosedEditor";
const char ABOUT_QTCREATOR[] = "QtCreator.AboutQtCreator";
const char ABOUT_PLUGINS[] = "QtCreator.AboutPlugins";
const char CHANGE_LOG[] = "QtCreator.ChangeLog";
diff --git a/src/plugins/coreplugin/corejsextensions.cpp b/src/plugins/coreplugin/corejsextensions.cpp
index 28f4790c2b..05c3ea82e5 100644
--- a/src/plugins/coreplugin/corejsextensions.cpp
+++ b/src/plugins/coreplugin/corejsextensions.cpp
@@ -29,6 +29,11 @@ QString UtilsJsExtension::qtCreatorVersion() const
return appInfo().displayVersion;
}
+QString UtilsJsExtension::qtCreatorIdeVersion() const
+{
+ return QCoreApplication::applicationVersion();
+}
+
QString UtilsJsExtension::toNativeSeparators(const QString &in) const
{
return QDir::toNativeSeparators(in);
diff --git a/src/plugins/coreplugin/corejsextensions.h b/src/plugins/coreplugin/corejsextensions.h
index 13dc81ccfd..1ca8685fdb 100644
--- a/src/plugins/coreplugin/corejsextensions.h
+++ b/src/plugins/coreplugin/corejsextensions.h
@@ -20,6 +20,7 @@ public:
// General information
Q_INVOKABLE QString qtVersion() const;
Q_INVOKABLE QString qtCreatorVersion() const;
+ Q_INVOKABLE QString qtCreatorIdeVersion() const;
// File name conversions:
Q_INVOKABLE QString toNativeSeparators(const QString &in) const;
diff --git a/src/plugins/coreplugin/designmode.cpp b/src/plugins/coreplugin/designmode.cpp
index cb5a6129df..f69e3d0e03 100644
--- a/src/plugins/coreplugin/designmode.cpp
+++ b/src/plugins/coreplugin/designmode.cpp
@@ -25,6 +25,18 @@
using namespace Utils;
+/*!
+ \class Core::DesignMode
+ \inmodule QtCreator
+
+ \brief The DesignMode class implements the mode for the Design mode, which is
+ for example used by \QMLD and \QD.
+
+ Other plugins can register themselves with registerDesignWidget(),
+ giving a list of MIME types that the editor understands, as well as an instance
+ to the main editor widget itself.
+*/
+
namespace Core {
struct DesignEditorInfo
diff --git a/src/plugins/coreplugin/designmode.h b/src/plugins/coreplugin/designmode.h
index 1f90d08dc5..7a656353e4 100644
--- a/src/plugins/coreplugin/designmode.h
+++ b/src/plugins/coreplugin/designmode.h
@@ -12,13 +12,6 @@ class FancyMainWindow;
namespace Core {
class IEditor;
-/**
- * A global mode for Design pane - used by Bauhaus (QML Designer) and
- * Qt Designer. Other plugins can register themselves by registerDesignWidget()
- * and giving a list of mimetypes that the editor understands, as well as an instance
- * to the main editor widget itself.
- */
-
class CORE_EXPORT DesignMode final : public IMode
{
Q_OBJECT
diff --git a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp
index 4f7c0fb006..8649adc167 100644
--- a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp
+++ b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp
@@ -341,7 +341,7 @@ ShortcutInput::ShortcutInput()
QPalette palette = m_warningLabel->palette();
palette.setColor(QPalette::Active,
QPalette::WindowText,
- Utils::creatorTheme()->color(Utils::Theme::TextColorError));
+ Utils::creatorColor(Utils::Theme::TextColorError));
m_warningLabel->setPalette(palette);
connect(m_warningLabel, &QLabel::linkActivated, this, &ShortcutInput::showConflictsRequested);
@@ -718,15 +718,15 @@ bool ShortcutSettingsWidget::markCollisions(ShortcutItem *item, int index)
}
if (currentIsConflicting) {
currentItem->m_item->setForeground(2,
- Utils::creatorTheme()->color(
- Utils::Theme::TextColorError));
+ Utils::creatorColor(
+ Utils::Theme::TextColorError));
hasCollision = true;
}
}
}
item->m_item->setForeground(2,
hasCollision
- ? Utils::creatorTheme()->color(Utils::Theme::TextColorError)
+ ? Utils::creatorColor(Utils::Theme::TextColorError)
: commandList()->palette().windowText());
return hasCollision;
}
diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp
index 95eb0def6c..c8ef2a4f1a 100644
--- a/src/plugins/coreplugin/editormanager/editormanager.cpp
+++ b/src/plugins/coreplugin/editormanager/editormanager.cpp
@@ -261,10 +261,10 @@ void EditorManagerPlaceHolder::showEvent(QShowEvent *)
*/
/*!
- \fn void Core::EditorManager::editorCreated(Core::IEditor *editor, const QString &fileName)
+ \fn void Core::EditorManager::editorCreated(Core::IEditor *editor, const Utils::FilePath &filePath)
- This signal is emitted after an \a editor was created for \a fileName, but
- before it was opened in an editor view.
+ This signal is emitted after an \a editor was created for the file at
+ \a filePath, but before it was opened in an editor view.
*/
/*!
\fn void Core::EditorManager::editorOpened(Core::IEditor *editor)
@@ -596,6 +596,14 @@ void EditorManagerPrivate::init()
goForward.addToContainer(Constants::M_WINDOW, Constants::G_WINDOW_NAVIGATE);
goForward.addOnTriggered(this, &EditorManager::goForwardInNavigationHistory);
+ // Reopen last closed document
+ ActionBuilder reopenLastClosedDocument(this, Constants::REOPEN_CLOSED_EDITOR);
+ reopenLastClosedDocument.setText(::Core::Tr::tr("Reopen Last Closed Document"));
+ reopenLastClosedDocument.bindContextAction(&m_reopenLastClosedDocumenAction);
+ reopenLastClosedDocument.setContext(editDesignContext);
+ reopenLastClosedDocument.addToContainer(Constants::M_WINDOW, Constants::G_WINDOW_NAVIGATE);
+ reopenLastClosedDocument.addOnTriggered(this, &EditorManagerPrivate::reopenLastClosedDocument);
+
// Go to last edit
ActionBuilder gotoLastEdit(this, Constants::GOTOLASTEDIT);
gotoLastEdit.setText(::Core::Tr::tr("Go to Last Edit"));
@@ -1480,6 +1488,7 @@ bool EditorManagerPrivate::activateEditorForEntry(EditorView *view, DocumentMode
void EditorManagerPrivate::closeEditorOrDocument(IEditor *editor)
{
QTC_ASSERT(editor, return);
+ EditorManager::addCurrentPositionToNavigationHistory();
QList<IEditor *> visible = EditorManager::visibleEditors();
if (Utils::contains(visible,
[&editor](IEditor *other) {
@@ -2074,6 +2083,7 @@ void EditorManagerPrivate::updateActions()
EditorView *view = currentEditorView();
d->m_goBackAction->setEnabled(view ? view->canGoBack() : false);
d->m_goForwardAction->setEnabled(view ? view->canGoForward() : false);
+ d->m_reopenLastClosedDocumenAction->setEnabled(view ? view->canReopen() : false);
SplitterOrView *viewParent = (view ? view->parentSplitterOrView() : nullptr);
SplitterOrView *parentSplitter = (viewParent ? viewParent->findParentSplitter() : nullptr);
@@ -2226,6 +2236,22 @@ void EditorManagerPrivate::gotoPreviousSplit()
activateView(prevView);
}
+void EditorManagerPrivate::addClosedDocumentToCloseHistory(IEditor *editor)
+{
+ EditorView *view = EditorManagerPrivate::viewForEditor(editor);
+ QTC_ASSERT(view, return);
+ view->addClosedEditorToCloseHistory(editor);
+ EditorManagerPrivate::updateActions();
+}
+
+void EditorManagerPrivate::reopenLastClosedDocument()
+{
+ EditorView *view = EditorManagerPrivate::currentEditorView();
+ QTC_ASSERT(view, return);
+ view->reopenLastClosedDocument();
+ EditorManagerPrivate::updateActions();
+}
+
void EditorManagerPrivate::makeCurrentEditorWritable()
{
if (IDocument* doc = EditorManager::currentDocument())
@@ -2777,7 +2803,6 @@ void EditorManager::slotCloseCurrentEditorOrDocument()
{
if (!d->m_currentEditor)
return;
- addCurrentPositionToNavigationHistory();
d->closeEditorOrDocument(d->m_currentEditor);
}
@@ -3031,6 +3056,9 @@ bool EditorManager::closeDocuments(const QList<DocumentModel::Entry *> &entries)
*/
bool EditorManager::closeEditors(const QList<IEditor*> &editorsToClose, bool askAboutModifiedEditors)
{
+ for (IEditor *editor : editorsToClose)
+ EditorManagerPrivate::addClosedDocumentToCloseHistory(editor);
+
return EditorManagerPrivate::closeEditors(editorsToClose,
askAboutModifiedEditors ? EditorManagerPrivate::CloseFlag::CloseWithAsking
: EditorManagerPrivate::CloseFlag::CloseWithoutAsking);
@@ -3444,18 +3472,7 @@ void EditorManager::addCurrentPositionToNavigationHistory(const QByteArray &save
*/
void EditorManager::setLastEditLocation(const IEditor* editor)
{
- IDocument *document = editor->document();
- if (!document)
- return;
-
- const QByteArray &state = editor->saveState();
- EditLocation location;
- location.document = document;
- location.filePath = document->filePath();
- location.id = document->id();
- location.state = state;
-
- d->m_globalLastEditLocation = location;
+ d->m_globalLastEditLocation = EditLocation::forEditor(editor);
}
/*!
diff --git a/src/plugins/coreplugin/editormanager/editormanager_p.h b/src/plugins/coreplugin/editormanager/editormanager_p.h
index db5bf759ae..34a6745721 100644
--- a/src/plugins/coreplugin/editormanager/editormanager_p.h
+++ b/src/plugins/coreplugin/editormanager/editormanager_p.h
@@ -124,6 +124,8 @@ public:
const Utils::FilePath &newFilePath,
Utils::Id originalType = {});
+ static void addClosedDocumentToCloseHistory(IEditor *editor);
+
public slots:
static bool saveDocument(Core::IDocument *document);
static bool saveDocumentAs(Core::IDocument *document);
@@ -133,6 +135,8 @@ public slots:
static void gotoPreviousSplit();
static void gotoNextSplit();
+ static void reopenLastClosedDocument();
+
void handleDocumentStateChange(Core::IDocument *document);
void editorAreaDestroyed(QObject *area);
@@ -216,6 +220,7 @@ private:
QAction *m_gotoPreviousDocHistoryAction = nullptr;
QAction *m_goBackAction = nullptr;
QAction *m_goForwardAction = nullptr;
+ QAction *m_reopenLastClosedDocumenAction = nullptr;
QAction *m_gotoLastEditAction = nullptr;
QAction *m_splitAction = nullptr;
QAction *m_splitSideBySideAction = nullptr;
diff --git a/src/plugins/coreplugin/editormanager/editorview.cpp b/src/plugins/coreplugin/editormanager/editorview.cpp
index ae9c047a77..fd9d375d81 100644
--- a/src/plugins/coreplugin/editormanager/editorview.cpp
+++ b/src/plugins/coreplugin/editormanager/editorview.cpp
@@ -39,14 +39,16 @@ namespace Core::Internal {
// EditorView
-EditorView::EditorView(SplitterOrView *parentSplitterOrView, QWidget *parent) :
- QWidget(parent),
- m_parentSplitterOrView(parentSplitterOrView),
- m_toolBar(new EditorToolBar(this)),
- m_container(new QStackedWidget(this)),
- m_infoBarDisplay(new InfoBarDisplay(this)),
- m_statusHLine(Layouting::createHr(this)),
- m_statusWidget(new QFrame(this))
+EditorView::EditorView(SplitterOrView *parentSplitterOrView, QWidget *parent)
+ : QWidget(parent)
+ , m_parentSplitterOrView(parentSplitterOrView)
+ , m_toolBar(new EditorToolBar(this))
+ , m_container(new QStackedWidget(this))
+ , m_infoBarDisplay(new InfoBarDisplay(this))
+ , m_statusHLine(Layouting::createHr(this))
+ , m_statusWidget(new QFrame(this))
+ , m_backMenu(new QMenu(this))
+ , m_forwardMenu(new QMenu(this))
{
auto tl = new QVBoxLayout(this);
tl->setSpacing(0);
@@ -67,6 +69,8 @@ EditorView::EditorView(SplitterOrView *parentSplitterOrView, QWidget *parent) :
connect(m_toolBar, &EditorToolBar::splitNewWindowClicked, this, &EditorView::splitNewWindow);
connect(m_toolBar, &EditorToolBar::closeSplitClicked, this, &EditorView::closeSplit);
m_toolBar->setMenuProvider([this](QMenu *menu) { fillListContextMenu(menu); });
+ m_toolBar->setGoBackMenu(m_backMenu);
+ m_toolBar->setGoForwardMenu(m_forwardMenu);
tl->addWidget(m_toolBar);
}
@@ -260,22 +264,17 @@ bool EditorView::canGoBack() const
return m_currentNavigationHistoryPosition > 0;
}
-void EditorView::updateEditorHistory(IEditor *editor, QList<EditLocation> &history)
+bool EditorView::canReopen() const
{
- if (!editor)
- return;
- IDocument *document = editor->document();
+ return !m_closedEditorHistory.isEmpty();
+}
- if (!document)
- return;
+void EditorView::updateEditorHistory(IEditor *editor, QList<EditLocation> &history)
+{
+ IDocument *document = editor ? editor->document() : nullptr;
+ QTC_ASSERT(document, return);
- QByteArray state = editor->saveState();
-
- EditLocation location;
- location.document = document;
- location.filePath = document->filePath();
- location.id = document->id();
- location.state = state;
+ const auto location = EditLocation::forEditor(editor);
for (int i = 0; i < history.size(); ++i) {
const EditLocation &item = history.at(i);
@@ -303,11 +302,11 @@ void EditorView::paintEvent(QPaintEvent *)
QRect rect = m_container->geometry();
if (creatorTheme()->flag(Theme::FlatToolBars)) {
- painter.fillRect(rect, creatorTheme()->color(Theme::EditorPlaceholderColor));
+ painter.fillRect(rect, creatorColor(Theme::EditorPlaceholderColor));
} else {
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setPen(Qt::NoPen);
- painter.setBrush(creatorTheme()->color(Theme::EditorPlaceholderColor));
+ painter.setBrush(creatorColor(Theme::EditorPlaceholderColor));
const int r = 3;
painter.drawRoundedRect(rect.adjusted(r , r, -r, -r), r * 2, r * 2);
}
@@ -491,24 +490,11 @@ constexpr int navigationHistorySize = 100;
void EditorView::addCurrentPositionToNavigationHistory(const QByteArray &saveState)
{
IEditor *editor = currentEditor();
- if (!editor)
- return;
- IDocument *document = editor->document();
- if (!document)
+ if (!editor || !editor->document())
return;
- QByteArray state;
- if (saveState.isNull())
- state = editor->saveState();
- else
- state = saveState;
-
- EditLocation location;
- location.document = document;
- location.filePath = document->filePath();
- location.id = document->id();
- location.state = state;
+ const auto location = EditLocation::forEditor(editor, saveState);
m_currentNavigationHistoryPosition = qMin(m_currentNavigationHistoryPosition, m_navigationHistory.size()); // paranoia
m_navigationHistory.insert(m_currentNavigationHistoryPosition, location);
++m_currentNavigationHistoryPosition;
@@ -524,6 +510,21 @@ void EditorView::addCurrentPositionToNavigationHistory(const QByteArray &saveSta
updateNavigatorActions();
}
+void EditorView::addClosedEditorToCloseHistory(IEditor *editor)
+{
+ static const int MAX_ITEMS = 20;
+
+ if (!editor || !editor->document())
+ return;
+
+ EditLocation location = EditLocation::forEditor(editor);
+
+ m_closedEditorHistory.push_back(location);
+
+ if (m_closedEditorHistory.size() > MAX_ITEMS)
+ m_closedEditorHistory.removeFirst();
+}
+
void EditorView::cutForwardNavigationHistory()
{
while (m_currentNavigationHistoryPosition < m_navigationHistory.size() - 1)
@@ -532,6 +533,35 @@ void EditorView::cutForwardNavigationHistory()
void EditorView::updateNavigatorActions()
{
+ static const int MAX_ITEMS = 20;
+ FilePath last;
+ m_backMenu->clear();
+ int count = 0;
+ for (int i = m_currentNavigationHistoryPosition - 1; i >= 0; i--) {
+ const EditLocation &loc = m_navigationHistory.at(i);
+ if (loc.filePath != last) {
+ m_backMenu->addAction(loc.displayName(), this, [this, i] { goToNavigationHistory(i); });
+ last = loc.filePath;
+ ++count;
+ if (count >= MAX_ITEMS)
+ break;
+ }
+ }
+ last = {};
+ m_forwardMenu->clear();
+ count = 0;
+ for (int i = m_currentNavigationHistoryPosition + 1; i < m_navigationHistory.size(); i++) {
+ const EditLocation &loc = m_navigationHistory.at(i);
+ if (loc.filePath != last) {
+ m_forwardMenu->addAction(loc.displayName(), this, [this, i] {
+ goToNavigationHistory(i);
+ });
+ last = loc.filePath;
+ ++count;
+ if (count >= MAX_ITEMS)
+ break;
+ }
+ }
m_toolBar->setCanGoBack(canGoBack());
m_toolBar->setCanGoForward(canGoForward());
}
@@ -552,7 +582,6 @@ void EditorView::updateCurrentPositionInNavigationHistory()
if (!editor || !editor->document())
return;
- IDocument *document = editor->document();
EditLocation *location;
if (m_currentNavigationHistoryPosition < m_navigationHistory.size()) {
location = &m_navigationHistory[m_currentNavigationHistoryPosition];
@@ -560,10 +589,7 @@ void EditorView::updateCurrentPositionInNavigationHistory()
m_navigationHistory.append(EditLocation());
location = &m_navigationHistory[m_navigationHistory.size()-1];
}
- location->document = document;
- location->filePath = document->filePath();
- location->id = document->id();
- location->state = editor->saveState();
+ *location = EditLocation::forEditor(editor);
}
static bool fileNameWasRemoved(const FilePath &filePath)
@@ -571,31 +597,45 @@ static bool fileNameWasRemoved(const FilePath &filePath)
return !filePath.isEmpty() && !filePath.exists();
}
+bool EditorView::openEditorFromNavigationHistory(int index)
+{
+ EditLocation location = m_navigationHistory.at(index);
+ IEditor *editor = nullptr;
+ if (location.document) {
+ editor = EditorManagerPrivate::activateEditorForDocument(
+ this, location.document, EditorManager::IgnoreNavigationHistory);
+ }
+ if (!editor) {
+ if (fileNameWasRemoved(location.filePath))
+ return false;
+ editor = EditorManagerPrivate::openEditor(
+ this, location.filePath, location.id, EditorManager::IgnoreNavigationHistory);
+ if (!editor)
+ return false;
+ }
+ editor->restoreState(location.state);
+ return true;
+}
+
+void EditorView::goToNavigationHistory(int index)
+{
+ if (index >= m_navigationHistory.size())
+ return;
+ updateCurrentPositionInNavigationHistory();
+ if (!openEditorFromNavigationHistory(index))
+ m_navigationHistory.removeAt(index);
+ m_currentNavigationHistoryPosition = index;
+ updateNavigatorActions();
+}
+
void EditorView::goBackInNavigationHistory()
{
updateCurrentPositionInNavigationHistory();
while (m_currentNavigationHistoryPosition > 0) {
--m_currentNavigationHistoryPosition;
- EditLocation location = m_navigationHistory.at(m_currentNavigationHistoryPosition);
- IEditor *editor = nullptr;
- if (location.document) {
- editor = EditorManagerPrivate::activateEditorForDocument(this, location.document,
- EditorManager::IgnoreNavigationHistory);
- }
- if (!editor) {
- if (fileNameWasRemoved(location.filePath)) {
- m_navigationHistory.removeAt(m_currentNavigationHistoryPosition);
- continue;
- }
- editor = EditorManagerPrivate::openEditor(this, location.filePath, location.id,
- EditorManager::IgnoreNavigationHistory);
- if (!editor) {
- m_navigationHistory.removeAt(m_currentNavigationHistoryPosition);
- continue;
- }
- }
- editor->restoreState(location.state);
- break;
+ if (openEditorFromNavigationHistory(m_currentNavigationHistoryPosition))
+ break;
+ m_navigationHistory.removeAt(m_currentNavigationHistoryPosition);
}
updateNavigatorActions();
}
@@ -607,32 +647,22 @@ void EditorView::goForwardInNavigationHistory()
return;
++m_currentNavigationHistoryPosition;
while (m_currentNavigationHistoryPosition < m_navigationHistory.size()) {
- IEditor *editor = nullptr;
- EditLocation location = m_navigationHistory.at(m_currentNavigationHistoryPosition);
- if (location.document) {
- editor = EditorManagerPrivate::activateEditorForDocument(this, location.document,
- EditorManager::IgnoreNavigationHistory);
- }
- if (!editor) {
- if (fileNameWasRemoved(location.filePath)) {
- m_navigationHistory.removeAt(m_currentNavigationHistoryPosition);
- continue;
- }
- editor = EditorManagerPrivate::openEditor(this, location.filePath, location.id,
- EditorManager::IgnoreNavigationHistory);
- if (!editor) {
- m_navigationHistory.removeAt(m_currentNavigationHistoryPosition);
- continue;
- }
- }
- editor->restoreState(location.state);
- break;
+ if (openEditorFromNavigationHistory(m_currentNavigationHistoryPosition))
+ break;
+ m_navigationHistory.removeAt(m_currentNavigationHistoryPosition);
}
if (m_currentNavigationHistoryPosition >= m_navigationHistory.size())
m_currentNavigationHistoryPosition = qMax<int>(m_navigationHistory.size() - 1, 0);
updateNavigatorActions();
}
+void EditorView::reopenLastClosedDocument()
+{
+ if (m_closedEditorHistory.isEmpty())
+ return;
+ goToEditLocation(m_closedEditorHistory.takeLast());
+}
+
void EditorView::goToEditLocation(const EditLocation &location)
{
IEditor *editor = nullptr;
@@ -1033,4 +1063,29 @@ EditLocation EditLocation::load(const QByteArray &data)
return loc;
}
+EditLocation EditLocation::forEditor(const IEditor *editor, const QByteArray &saveState)
+{
+ QTC_ASSERT(editor, return EditLocation());
+
+ IDocument *document = editor->document();
+ QTC_ASSERT(document, return EditLocation());
+
+ const QByteArray &state = saveState.isEmpty() ? editor->saveState() : saveState;
+
+ EditLocation location;
+ location.document = document;
+ location.filePath = document->filePath();
+ location.id = document->id();
+ location.state = state;
+
+ return location;
+}
+
+QString EditLocation::displayName() const
+{
+ if (document)
+ return document->displayName();
+ return filePath.fileName();
+}
+
} // Core::Internal
diff --git a/src/plugins/coreplugin/editormanager/editorview.h b/src/plugins/coreplugin/editormanager/editorview.h
index 6a81a7b4b7..374c6df21f 100644
--- a/src/plugins/coreplugin/editormanager/editorview.h
+++ b/src/plugins/coreplugin/editormanager/editorview.h
@@ -40,6 +40,9 @@ class EditLocation
public:
QByteArray save() const;
static EditLocation load(const QByteArray &data);
+ static EditLocation forEditor(const IEditor *editor, const QByteArray &saveState = {});
+
+ QString displayName() const;
QPointer<IDocument> document;
Utils::FilePath filePath;
@@ -82,13 +85,17 @@ public:
bool canGoForward() const;
bool canGoBack() const;
+ bool canReopen() const;
void goBackInNavigationHistory();
void goForwardInNavigationHistory();
+ void reopenLastClosedDocument();
+
void goToEditLocation(const EditLocation &location);
void addCurrentPositionToNavigationHistory(const QByteArray &saveState = QByteArray());
+ void addClosedEditorToCloseHistory(IEditor *editor);
void cutForwardNavigationHistory();
QList<EditLocation> editorHistory() const { return m_editorHistory; }
@@ -124,6 +131,8 @@ private:
void checkProjectLoaded(IEditor *editor);
void updateCurrentPositionInNavigationHistory();
+ bool openEditorFromNavigationHistory(int index);
+ void goToNavigationHistory(int index);
SplitterOrView *m_parentSplitterOrView;
EditorToolBar *m_toolBar;
@@ -140,7 +149,10 @@ private:
QLabel *m_emptyViewLabel;
QList<EditLocation> m_navigationHistory;
+ QMenu *m_backMenu;
+ QMenu *m_forwardMenu;
QList<EditLocation> m_editorHistory;
+ QList<EditLocation> m_closedEditorHistory;
int m_currentNavigationHistoryPosition = 0;
};
diff --git a/src/plugins/coreplugin/editormanager/ieditorfactory.cpp b/src/plugins/coreplugin/editormanager/ieditorfactory.cpp
index 7528e12a85..22aa24e737 100644
--- a/src/plugins/coreplugin/editormanager/ieditorfactory.cpp
+++ b/src/plugins/coreplugin/editormanager/ieditorfactory.cpp
@@ -47,23 +47,23 @@ static void mimeTypeFactoryLookup(const Utils::MimeType &mimeType,
\brief The IEditorFactory class creates suitable editors for documents
according to their MIME type.
- Whenever a user wants to edit or create a document, the EditorManager
- scans all IEditorFactory instances for suitable editors. The selected
- IEditorFactory is then asked to create an editor.
+ When a user wants to edit or create a document, the EditorManager
+ scans all IEditorFactory instances for suitable editors and selects one
+ to create an editor.
Implementations should set the properties of the IEditorFactory subclass in
their constructor with IEditorFactory::setId(), IEditorFactory::setDisplayName(),
- IEditorFactory::setMimeTypes(), and setEditorCreator()
+ IEditorFactory::setMimeTypes(), and IEditorFactory::setEditorCreator().
IEditorFactory instances automatically register themselves in \QC in their
constructor.
- There are two varieties of editors: Internal and external. Internal editors
- open within the main editing area of Qt Creator. An IEditorFactory defines
- an internal editor by using the \c setEditorCreator function. External
+ There are two varieties of editors: internal and external. Internal editors
+ open within the main editing area of \QC. An IEditorFactory instance defines
+ an internal editor by using the setEditorCreator() function. External
editors are external applications and are defined by using the
- \c setEditorStarter function. They are accessible by the user using
- the \uicontrol{Open With} dialog
+ setEditorStarter() function. The user can access them from the
+ \uicontrol{Open With} dialog.
\sa Core::IEditor
\sa Core::IDocument
@@ -167,7 +167,7 @@ IEditorFactory *IEditorFactory::editorFactoryForId(const Utils::Id &id)
/*!
Returns all available internal and external editors for the \a mimeType in the
- default order: Editor types ordered by MIME type hierarchy, internal editors
+ default order: editor types are ordered by MIME type hierarchy, internal editors
first.
*/
const EditorFactories IEditorFactory::defaultEditorFactories(const MimeType &mimeType)
@@ -283,9 +283,10 @@ IEditor *IEditorFactory::createEditor() const
}
/*!
- Starts an external editor.
+ Opens the file at \a filePath in an external editor.
- Uses the function set with setEditorStarter() to start the editor.
+ Returns \c true on success or \c false on failure with the error in
+ \a errorMessage.
\sa setEditorStarter()
*/
@@ -299,7 +300,7 @@ bool IEditorFactory::startEditor(const FilePath &filePath, QString *errorMessage
Sets the function that is used to create an editor instance in
createEditor() to \a creator.
- This is mutually exclusive with the use of setEditorStarter.
+ This is mutually exclusive with the use of setEditorStarter().
\sa createEditor()
*/
@@ -313,12 +314,16 @@ void IEditorFactory::setEditorCreator(const std::function<IEditor *()> &creator)
}
/*!
- Opens the editor with \a fileName. Returns \c true on success or \c false
- on failure along with the error in \a errorMessage.
+ \fn void Core::IEditorFactory::setEditorStarter(const std::function<bool(const Utils::FilePath &, QString *)> &starter);
- This is mutually exclusive with the use of setEditorCreator.
-*/
+ Sets the function that is used to open a file for a given \c FilePath to
+ \a starter.
+
+ The function should return \c true on success, or return \c false and set the
+ \c QString to an error message at failure.
+ This is mutually exclusive with the use of setEditorCreator().
+*/
void IEditorFactory::setEditorStarter(const std::function<bool(const FilePath &, QString *)> &starter)
{
QTC_CHECK(!m_starter);
diff --git a/src/plugins/coreplugin/editortoolbar.cpp b/src/plugins/coreplugin/editortoolbar.cpp
index ee4d912d85..857a32b637 100644
--- a/src/plugins/coreplugin/editortoolbar.cpp
+++ b/src/plugins/coreplugin/editortoolbar.cpp
@@ -39,6 +39,24 @@ enum {
namespace Core {
+class ButtonWithMenu : public QToolButton
+{
+public:
+ ButtonWithMenu(QWidget *parent = nullptr)
+ : QToolButton(parent)
+ {}
+
+protected:
+ void mousePressEvent(QMouseEvent *e) override
+ {
+ if (e->button() == Qt::RightButton) {
+ showMenu();
+ return;
+ }
+ QToolButton::mousePressEvent(e);
+ }
+};
+
struct EditorToolBarPrivate
{
explicit EditorToolBarPrivate(QWidget *parent, EditorToolBar *q);
@@ -51,8 +69,8 @@ struct EditorToolBarPrivate
EditorToolBar::MenuProvider m_menuProvider;
QAction *m_goBackAction;
QAction *m_goForwardAction;
- QToolButton *m_backButton;
- QToolButton *m_forwardButton;
+ ButtonWithMenu *m_backButton;
+ ButtonWithMenu *m_forwardButton;
QToolButton *m_splitButton;
QAction *m_horizontalSplitAction;
QAction *m_verticalSplitAction;
@@ -68,27 +86,27 @@ struct EditorToolBarPrivate
bool m_isStandalone;
};
-EditorToolBarPrivate::EditorToolBarPrivate(QWidget *parent, EditorToolBar *q) :
- m_editorList(new QComboBox(q)),
- m_closeEditorButton(new QToolButton(q)),
- m_lockButton(new QToolButton(q)),
- m_dragHandle(new QToolButton(q)),
- m_dragHandleMenu(nullptr),
- m_goBackAction(new QAction(Utils::Icons::PREV_TOOLBAR.icon(), Tr::tr("Go Back"), parent)),
- m_goForwardAction(new QAction(Utils::Icons::NEXT_TOOLBAR.icon(), Tr::tr("Go Forward"), parent)),
- m_backButton(new QToolButton(q)),
- m_forwardButton(new QToolButton(q)),
- m_splitButton(new QToolButton(q)),
- m_horizontalSplitAction(new QAction(Utils::Icons::SPLIT_HORIZONTAL.icon(),
- Tr::tr("Split"), parent)),
- m_verticalSplitAction(new QAction(Utils::Icons::SPLIT_VERTICAL.icon(),
- Tr::tr("Split Side by Side"), parent)),
- m_splitNewWindowAction(new QAction(Tr::tr("Open in New Window"), parent)),
- m_closeSplitButton(new QToolButton(q)),
- m_activeToolBar(nullptr),
- m_toolBarPlaceholder(new QWidget(q)),
- m_defaultToolBar(new QWidget(q)),
- m_isStandalone(false)
+EditorToolBarPrivate::EditorToolBarPrivate(QWidget *parent, EditorToolBar *q)
+ : m_editorList(new QComboBox(q))
+ , m_closeEditorButton(new QToolButton(q))
+ , m_lockButton(new QToolButton(q))
+ , m_dragHandle(new QToolButton(q))
+ , m_dragHandleMenu(nullptr)
+ , m_goBackAction(new QAction(Utils::Icons::PREV_TOOLBAR.icon(), Tr::tr("Go Back"), parent))
+ , m_goForwardAction(new QAction(Utils::Icons::NEXT_TOOLBAR.icon(), Tr::tr("Go Forward"), parent))
+ , m_backButton(new ButtonWithMenu(q))
+ , m_forwardButton(new ButtonWithMenu(q))
+ , m_splitButton(new QToolButton(q))
+ , m_horizontalSplitAction(
+ new QAction(Utils::Icons::SPLIT_HORIZONTAL.icon(), Tr::tr("Split"), parent))
+ , m_verticalSplitAction(
+ new QAction(Utils::Icons::SPLIT_VERTICAL.icon(), Tr::tr("Split Side by Side"), parent))
+ , m_splitNewWindowAction(new QAction(Tr::tr("Open in New Window"), parent))
+ , m_closeSplitButton(new QToolButton(q))
+ , m_activeToolBar(nullptr)
+ , m_toolBarPlaceholder(new QWidget(q))
+ , m_defaultToolBar(new QWidget(q))
+ , m_isStandalone(false)
{
}
@@ -347,6 +365,16 @@ void EditorToolBar::setCanGoForward(bool canGoForward)
d->m_goForwardAction->setEnabled(canGoForward);
}
+void EditorToolBar::setGoBackMenu(QMenu *menu)
+{
+ d->m_backButton->setMenu(menu);
+}
+
+void EditorToolBar::setGoForwardMenu(QMenu *menu)
+{
+ d->m_forwardButton->setMenu(menu);
+}
+
void EditorToolBar::updateActionShortcuts()
{
d->m_closeEditorButton->setToolTip(ActionManager::command(Constants::CLOSE)->stringWithAppendedShortcut(Tr::tr("Close Document")));
diff --git a/src/plugins/coreplugin/editortoolbar.h b/src/plugins/coreplugin/editortoolbar.h
index a2aa089da7..72925c0fd6 100644
--- a/src/plugins/coreplugin/editortoolbar.h
+++ b/src/plugins/coreplugin/editortoolbar.h
@@ -19,10 +19,13 @@ class IDocument;
struct EditorToolBarPrivate;
-/**
- * Fakes an IEditor-like toolbar for design mode widgets such as Qt Designer and Bauhaus.
- * Creates a combobox for open files and lock and close buttons on the right.
- */
+/*!
+ \class Core::EditorToolBar
+ \inmodule QtCreator
+
+ Implements the editor toolbar for editor views and for design mode widgets such as
+ \QD and \QMLD. Creates a combobox for open files and lock and close buttons on the right.
+*/
class CORE_EXPORT EditorToolBar : public Utils::StyledBar
{
Q_OBJECT
@@ -56,6 +59,8 @@ public:
void setNavigationVisible(bool isVisible);
void setCanGoBack(bool canGoBack);
void setCanGoForward(bool canGoForward);
+ void setGoBackMenu(QMenu *menu);
+ void setGoForwardMenu(QMenu *menu);
void removeToolbarForEditor(IEditor *editor);
void setCloseSplitEnabled(bool enable);
void setCloseSplitIcon(const QIcon &icon);
diff --git a/src/plugins/coreplugin/fancyactionbar.cpp b/src/plugins/coreplugin/fancyactionbar.cpp
index 7197236100..9a873b1af3 100644
--- a/src/plugins/coreplugin/fancyactionbar.cpp
+++ b/src/plugins/coreplugin/fancyactionbar.cpp
@@ -141,7 +141,7 @@ void FancyToolButton::paintEvent(QPaintEvent *event)
&& m_fader > 0 && isEnabled() && !isDown() && !isChecked()) {
painter.save();
if (creatorTheme()->flag(Theme::FlatToolBars)) {
- const QColor hoverColor = creatorTheme()->color(Theme::FancyToolButtonHoverColor);
+ const QColor hoverColor = creatorColor(Theme::FancyToolButtonHoverColor);
QColor fadedHoverColor = hoverColor;
fadedHoverColor.setAlpha(int(m_fader * hoverColor.alpha()));
painter.fillRect(rect(), fadedHoverColor);
@@ -152,7 +152,7 @@ void FancyToolButton::paintEvent(QPaintEvent *event)
painter.restore();
} else if (isDown() || isChecked()) {
painter.save();
- const QColor selectedColor = creatorTheme()->color(Theme::FancyToolButtonSelectedColor);
+ const QColor selectedColor = creatorColor(Theme::FancyToolButtonSelectedColor);
if (creatorTheme()->flag(Theme::FlatToolBars)) {
painter.fillRect(rect(), selectedColor);
} else {
@@ -200,8 +200,8 @@ void FancyToolButton::paintEvent(QPaintEvent *event)
- QPoint(iconRect.width() / 2, iconRect.height() / 2);
textOffset = textOffset - QPoint(0, lineHeight + 3);
const QRectF r(0, textOffset.y(), rect().width(), lineHeight);
- painter.setPen(creatorTheme()->color(isEnabled() ? Theme::PanelTextColorLight
- : Theme::IconsDisabledColor));
+ painter.setPen(creatorColor(isEnabled() ? Theme::PanelTextColorLight
+ : Theme::IconsDisabledColor));
// draw project name
const int margin = 6;
@@ -227,12 +227,12 @@ void FancyToolButton::paintEvent(QPaintEvent *event)
// draw the two text lines for the build configuration
painter.setPen(
- creatorTheme()->color(isEnabled()
- // Intentionally using the "Unselected" colors,
- // because the text color won't change in the pressed
- // state as they would do on the mode buttons.
- ? Theme::FancyTabWidgetEnabledUnselectedTextColor
- : Theme::FancyTabWidgetDisabledUnselectedTextColor));
+ creatorColor(isEnabled()
+ // Intentionally using the "Unselected" colors,
+ // because the text color won't change in the pressed
+ // state as they would do on the mode buttons.
+ ? Theme::FancyTabWidgetEnabledUnselectedTextColor
+ : Theme::FancyTabWidgetDisabledUnselectedTextColor));
for (int i = 0; i < 2; ++i) {
const QString &buildConfigText = splitBuildConfiguration[i];
@@ -264,7 +264,7 @@ void FancyActionBar::paintEvent(QPaintEvent *event)
// this paints the background of the bottom portion of the
// left tab bar
painter.fillRect(event->rect(), StyleHelper::baseColor());
- painter.setPen(creatorTheme()->color(Theme::FancyToolBarSeparatorColor));
+ painter.setPen(creatorColor(Theme::FancyToolBarSeparatorColor));
painter.drawLine(borderRect.topLeft(), borderRect.topRight());
} else {
painter.setPen(StyleHelper::sidebarShadow());
@@ -320,7 +320,7 @@ void FancyToolButton::hoverOverlay(QPainter *painter, const QRect &spanRect)
overlay.fill(Qt::transparent);
overlay.setDevicePixelRatio(dpr);
- const QColor hoverColor = creatorTheme()->color(Theme::FancyToolButtonHoverColor);
+ const QColor hoverColor = creatorColor(Theme::FancyToolButtonHoverColor);
const QRect rect(QPoint(), logicalSize);
const QRectF borderRect = QRectF(rect).adjusted(0.5, 0.5, -0.5, -0.5);
diff --git a/src/plugins/coreplugin/fancytabwidget.cpp b/src/plugins/coreplugin/fancytabwidget.cpp
index 05fb7e3e13..18d4603e19 100644
--- a/src/plugins/coreplugin/fancytabwidget.cpp
+++ b/src/plugins/coreplugin/fancytabwidget.cpp
@@ -272,7 +272,7 @@ static void paintIcon(QPainter *painter, const QRect &rect,
painter->setOpacity(1.0);
QRect accentRect = rect;
accentRect.setWidth(2);
- painter->fillRect(accentRect, creatorTheme()->color(Theme::IconsBaseColor));
+ painter->fillRect(accentRect, creatorColor(Theme::IconsBaseColor));
}
}
@@ -302,16 +302,16 @@ static void paintIconAndText(QPainter *painter, const QRect &rect,
if (selected && creatorTheme()->flag(Theme::FlatToolBars)) {
QRect accentRect = rect;
accentRect.setWidth(2);
- painter->fillRect(accentRect, creatorTheme()->color(Theme::IconsBaseColor));
+ painter->fillRect(accentRect, creatorColor(Theme::IconsBaseColor));
}
if (enabled) {
painter->setPen(
- selected ? creatorTheme()->color(Theme::FancyTabWidgetEnabledSelectedTextColor)
- : creatorTheme()->color(Theme::FancyTabWidgetEnabledUnselectedTextColor));
+ selected ? creatorColor(Theme::FancyTabWidgetEnabledSelectedTextColor)
+ : creatorColor(Theme::FancyTabWidgetEnabledUnselectedTextColor));
} else {
painter->setPen(
- selected ? creatorTheme()->color(Theme::FancyTabWidgetDisabledSelectedTextColor)
- : creatorTheme()->color(Theme::FancyTabWidgetDisabledUnselectedTextColor));
+ selected ? creatorColor(Theme::FancyTabWidgetDisabledSelectedTextColor)
+ : creatorColor(Theme::FancyTabWidgetDisabledUnselectedTextColor));
}
painter->translate(0, -1);
@@ -338,7 +338,7 @@ void FancyTabBar::paintTab(QPainter *painter, int tabIndex) const
if (selected) {
if (creatorTheme()->flag(Theme::FlatToolBars)) {
// background color of a fancy tab that is active
- painter->fillRect(rect, creatorTheme()->color(Theme::FancyTabBarSelectedBackgroundColor));
+ painter->fillRect(rect, creatorColor(Theme::FancyTabBarSelectedBackgroundColor));
} else {
paintSelectedTabBackground(painter, rect);
}
@@ -349,7 +349,7 @@ void FancyTabBar::paintTab(QPainter *painter, int tabIndex) const
painter->save();
painter->setOpacity(fader);
if (creatorTheme()->flag(Theme::FlatToolBars))
- painter->fillRect(rect, creatorTheme()->color(Theme::FancyToolButtonHoverColor));
+ painter->fillRect(rect, creatorColor(Theme::FancyToolButtonHoverColor));
else
FancyToolButton::hoverOverlay(painter, rect);
painter->restore();
@@ -461,7 +461,7 @@ FancyTabWidget::FancyTabWidget(QWidget *parent)
QVBoxLayout *vlayout;
using namespace Layouting;
- Row { fancyButton, noMargin() }.attachTo(bar);
+ Row { fancyButton, noMargin }.attachTo(bar);
Row {
Widget {
bindTo(&m_selectionWidget),
@@ -471,13 +471,13 @@ FancyTabWidget::FancyTabWidget(QWidget *parent)
st,
Widget {
bindTo(&m_cornerWidgetContainer),
- Column { st, spacing(0), noMargin() },
+ Column { st, spacing(0), noMargin },
},
- spacing(0), noMargin(),
+ spacing(0), noMargin,
},
},
Column { bindTo(&vlayout), m_modesStack, m_statusBar, spacing(0) },
- spacing(1), noMargin(),
+ spacing(1), noMargin,
}.attachTo(this);
m_selectionWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
diff --git a/src/plugins/coreplugin/find/highlightscrollbarcontroller.cpp b/src/plugins/coreplugin/find/highlightscrollbarcontroller.cpp
index a88b8e4fba..dc45883b91 100644
--- a/src/plugins/coreplugin/find/highlightscrollbarcontroller.cpp
+++ b/src/plugins/coreplugin/find/highlightscrollbarcontroller.cpp
@@ -193,7 +193,7 @@ void HighlightScrollBarOverlay::drawHighlights(QPainter *painter,
for (const QMap<Utils::Theme::Color, QMap<int, int>> &colors : std::as_const(m_highlightCache)) {
const auto itColorEnd = colors.constEnd();
for (auto itColor = colors.constBegin(); itColor != itColorEnd; ++itColor) {
- const QColor &color = creatorTheme()->color(itColor.key());
+ const QColor &color = creatorColor(itColor.key());
const QMap<int, int> &positions = itColor.value();
const auto itPosEnd = positions.constEnd();
const auto firstPos = int(docStart / lineHeight);
diff --git a/src/plugins/coreplugin/find/ifindfilter.cpp b/src/plugins/coreplugin/find/ifindfilter.cpp
index 704f827bf8..49a891e659 100644
--- a/src/plugins/coreplugin/find/ifindfilter.cpp
+++ b/src/plugins/coreplugin/find/ifindfilter.cpp
@@ -30,7 +30,7 @@ using namespace Utils;
and \uicontrol {Files in File System} where the user provides a directory and file
patterns to search.
- \image qtcreator-search-filesystem.png
+ \image qtcreator-search-reg-exp.webp {Search Results view with search criteria}
To make your find scope available to the user, you need to implement this
class, and register an instance of your subclass in the plugin manager.
@@ -38,7 +38,7 @@ using namespace Utils;
A common way to present the search results to the user, is to use the
shared \uicontrol{Search Results} pane.
- \image qtcreator-search-results.webp {Search Results view}
+ \image qtcreator-search-results-reg-exp.webp {Search Results view with search results}
If you want to implement a find filter that is doing a file based text
search, you should use \l Core::BaseTextFind, which already implements all
@@ -178,13 +178,13 @@ using namespace Utils;
*/
/*!
- \fn void Core::IFindFilter::writeSettings(QSettings *settings)
+ \fn void Core::IFindFilter::writeSettings(Utils::QtcSettings *settings)
Called at shutdown to write the state of the additional options
for this find filter to the \a settings.
*/
/*!
- \fn void Core::IFindFilter::readSettings(QSettings *settings)
+ \fn void Core::IFindFilter::readSettings(Utils::QtcSettings *settings)
Called at startup to read the state of the additional options
for this find filter from the \a settings.
*/
diff --git a/src/plugins/coreplugin/find/searchresultwidget.cpp b/src/plugins/coreplugin/find/searchresultwidget.cpp
index c7827e6118..e206fb3879 100644
--- a/src/plugins/coreplugin/find/searchresultwidget.cpp
+++ b/src/plugins/coreplugin/find/searchresultwidget.cpp
@@ -67,8 +67,8 @@ SearchResultWidget::SearchResultWidget(QWidget *parent) :
QFrame *topWidget = new QFrame;
QPalette pal;
- pal.setColor(QPalette::Window, creatorTheme()->color(Theme::InfoBarBackground));
- pal.setColor(QPalette::WindowText, creatorTheme()->color(Theme::InfoBarText));
+ pal.setColor(QPalette::Window, creatorColor(Theme::InfoBarBackground));
+ pal.setColor(QPalette::WindowText, creatorColor(Theme::InfoBarText));
topWidget->setPalette(pal);
if (creatorTheme()->flag(Theme::DrawSearchResultWidgetFrame)) {
topWidget->setFrameStyle(QFrame::Panel | QFrame::Raised);
diff --git a/src/plugins/coreplugin/find/searchresultwindow.cpp b/src/plugins/coreplugin/find/searchresultwindow.cpp
index 8412a57d70..eea0c43d4e 100644
--- a/src/plugins/coreplugin/find/searchresultwindow.cpp
+++ b/src/plugins/coreplugin/find/searchresultwindow.cpp
@@ -288,10 +288,13 @@ using namespace Core::Internal;
This enum type specifies whether the search results should be sorted or
ordered:
- \value AddSorted
- The search results are sorted.
+ \value AddSortedByContent
+ The search results are sorted alphabetically.
+ \value AddSortedByPosition
+ The search results are sorted by the search results' reported line
+ numbers.
\value AddOrdered
- The search results are ordered.
+ The search results are ordered as they are reported.
*/
/*!
@@ -331,7 +334,7 @@ using namespace Core::Internal;
\brief The SearchResultWindow class is the implementation of a commonly
shared \uicontrol{Search Results} output pane.
- \image qtcreator-search-results.webp {Search Results view}
+ \image qtcreator-search-results-reg-exp.webp {Search Results view with search results}
Whenever you want to show the user a list of search results, or want
to present UI for a global search and replace, use the single instance
diff --git a/src/plugins/coreplugin/generalsettings.cpp b/src/plugins/coreplugin/generalsettings.cpp
index 71cfacbb40..1ff36dc805 100644
--- a/src/plugins/coreplugin/generalsettings.cpp
+++ b/src/plugins/coreplugin/generalsettings.cpp
@@ -240,7 +240,7 @@ void GeneralSettingsWidget::fillLanguageBox() const
if (hasQmFilesForLocale(locale, creatorTrPath.toString())) {
QLocale tmpLocale(locale);
QString languageItem = QLocale::languageToString(tmpLocale.language()) + QLatin1String(" (")
- + QLocale::countryToString(tmpLocale.country()) + QLatin1Char(')');
+ + QLocale::territoryToString(tmpLocale.territory()) + QLatin1Char(')');
m_languageBox->addItem(languageItem, locale);
if (locale == currentLocale)
m_languageBox->setCurrentIndex(m_languageBox->count() - 1);
diff --git a/src/plugins/coreplugin/helpmanager.cpp b/src/plugins/coreplugin/helpmanager.cpp
index 01fc7eff47..9f694c2802 100644
--- a/src/plugins/coreplugin/helpmanager.cpp
+++ b/src/plugins/coreplugin/helpmanager.cpp
@@ -103,5 +103,11 @@ void setBlockedDocumentation(const QStringList &fileNames)
m_instance->setBlockedDocumentation(fileNames);
}
+void addOnlineHelpHandler(const OnlineHelpHandler &handler)
+{
+ if (checkInstance())
+ m_instance->addOnlineHelpHandler(handler);
+}
+
} // HelpManager
} // Core
diff --git a/src/plugins/coreplugin/helpmanager.h b/src/plugins/coreplugin/helpmanager.h
index 55d449e696..2d24116d23 100644
--- a/src/plugins/coreplugin/helpmanager.h
+++ b/src/plugins/coreplugin/helpmanager.h
@@ -48,5 +48,11 @@ CORE_EXPORT QByteArray fileData(const QUrl &url);
CORE_EXPORT void showHelpUrl(const QUrl &url, HelpViewerLocation location = HelpModeAlways);
CORE_EXPORT void showHelpUrl(const QString &url, HelpViewerLocation location = HelpModeAlways);
+struct CORE_EXPORT OnlineHelpHandler {
+ std::function<bool(QUrl)> handlesUrl;
+ std::function<void(QUrl)> openUrl;
+};
+CORE_EXPORT void addOnlineHelpHandler(const OnlineHelpHandler &handler);
+
} // HelpManager
} // Core
diff --git a/src/plugins/coreplugin/helpmanager_implementation.h b/src/plugins/coreplugin/helpmanager_implementation.h
index 798a5a340e..d3b097f1f8 100644
--- a/src/plugins/coreplugin/helpmanager_implementation.h
+++ b/src/plugins/coreplugin/helpmanager_implementation.h
@@ -23,6 +23,7 @@ public:
virtual QMultiMap<QString, QUrl> linksForKeyword(const QString &keyword) = 0;
virtual QByteArray fileData(const QUrl &url) = 0;
virtual void showHelpUrl(const QUrl &url, HelpViewerLocation location = HelpModeAlways) = 0;
+ virtual void addOnlineHelpHandler(const OnlineHelpHandler &handler) = 0;
};
} // HelpManager
diff --git a/src/plugins/coreplugin/icore.cpp b/src/plugins/coreplugin/icore.cpp
index a625895b68..5c0a1a8574 100644
--- a/src/plugins/coreplugin/icore.cpp
+++ b/src/plugins/coreplugin/icore.cpp
@@ -54,9 +54,9 @@
#include <utils/checkablemessagebox.h>
#include <utils/dropsupport.h>
#include <utils/environment.h>
-#include <utils/fileutils.h>
#include <utils/fsengine/fileiconprovider.h>
#include <utils/fsengine/fsengine.h>
+#include <utils/guiutils.h>
#include <utils/historycompleter.h>
#include <utils/hostosinfo.h>
#include <utils/mimeutils.h>
@@ -397,7 +397,7 @@ ICore::ICore()
QCoreApplication::exit(exitCode);
});
- Utils::FileUtils::setDialogParentGetter(&ICore::dialogParent);
+ Utils::setDialogParentGetter(&ICore::dialogParent);
d->m_progressManager->init(); // needs the status bar manager
MessageManager::init();
@@ -570,8 +570,6 @@ bool ICore::showWarningWithOptions(const QString &title, const QString &text,
If \a scope is \c QSettings::SystemScope, only the installation settings
shipped with the current version of \QC will be read. This
functionality exists for internal purposes only.
-
- \sa settingsDatabase()
*/
QtcSettings *ICore::settings(QSettings::Scope scope)
{
diff --git a/src/plugins/coreplugin/iversioncontrol.cpp b/src/plugins/coreplugin/iversioncontrol.cpp
index f70e4be492..3ff7de5bcd 100644
--- a/src/plugins/coreplugin/iversioncontrol.cpp
+++ b/src/plugins/coreplugin/iversioncontrol.cpp
@@ -143,7 +143,7 @@ QString IVersionControl::refreshTopic(const FilePath &repository)
it will be used. Otherwise it will be refreshed using the items provided by
\c setTopicFileTracker() and \c setTopicRefresher().
- \sa setTopicFileTracker(), setTopicRefresher().
+ \sa setTopicFileTracker(), setTopicRefresher()
*/
QString IVersionControl::vcsTopic(const FilePath &topLevel)
diff --git a/src/plugins/coreplugin/iwizardfactory.cpp b/src/plugins/coreplugin/iwizardfactory.cpp
index 20adfda16b..544c25a17e 100644
--- a/src/plugins/coreplugin/iwizardfactory.cpp
+++ b/src/plugins/coreplugin/iwizardfactory.cpp
@@ -464,7 +464,7 @@ static QIcon iconWithText(const QIcon &icon, const QString &text)
font.setPixelSize(fontSize);
font.setStretch(85);
QPainter p(&pixmap);
- p.setPen(Utils::creatorTheme()->color(Theme::PanelTextColorDark));
+ p.setPen(Utils::creatorColor(Theme::PanelTextColorDark));
p.setFont(font);
QTextOption textOption(Qt::AlignHCenter | Qt::AlignBottom);
textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
diff --git a/src/plugins/coreplugin/locator/directoryfilter.cpp b/src/plugins/coreplugin/locator/directoryfilter.cpp
index 871e89753e..23b853682d 100644
--- a/src/plugins/coreplugin/locator/directoryfilter.cpp
+++ b/src/plugins/coreplugin/locator/directoryfilter.cpp
@@ -6,8 +6,6 @@
#include "locator.h"
#include "../coreplugintr.h"
-#include <extensionsystem/pluginmanager.h>
-
#include <utils/algorithm.h>
#include <utils/async.h>
#include <utils/fileutils.h>
@@ -89,7 +87,6 @@ DirectoryFilter::DirectoryFilter(Id id)
return SetupResult::StopWithSuccess; // Group stops, skips async task
};
const auto onSetup = [this](Async<FilePaths> &async) {
- async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
async.setConcurrentCallData(&refresh, m_directories, m_filters, m_exclusionFilters,
displayName());
};
@@ -150,42 +147,6 @@ void DirectoryFilter::restoreState(const QJsonObject &object)
.toArray(QJsonArray::fromStringList(kExclusionFiltersDefault)));
}
-void DirectoryFilter::restoreState(const QByteArray &state)
-{
- if (isOldSetting(state)) {
- // TODO read old settings, remove some time after Qt Creator 4.15
- QString name;
- QStringList directories;
- QString shortcut;
- bool defaultFilter;
- QStringList files;
-
- QDataStream in(state);
- in >> name;
- in >> directories;
- in >> m_filters;
- in >> shortcut;
- in >> defaultFilter;
- in >> files;
- m_cache.setFilePaths(FileUtils::toFilePathList(files));
- if (!in.atEnd()) // Qt Creator 4.3 and later
- in >> m_exclusionFilters;
- else
- m_exclusionFilters.clear();
-
- if (m_isCustomFilter) {
- m_directories = Utils::transform(directories, [](const QString &d) {
- return FilePath::fromString(d);
- });
- }
- setDisplayName(name);
- setShortcutString(shortcut);
- setIncludedByDefault(defaultFilter);
- } else {
- ILocatorFilter::restoreState(state);
- }
-}
-
class DirectoryFilterOptions : public QDialog
{
public:
diff --git a/src/plugins/coreplugin/locator/directoryfilter.h b/src/plugins/coreplugin/locator/directoryfilter.h
index 452a1b20e7..8300d03d46 100644
--- a/src/plugins/coreplugin/locator/directoryfilter.h
+++ b/src/plugins/coreplugin/locator/directoryfilter.h
@@ -12,7 +12,6 @@ class CORE_EXPORT DirectoryFilter : public ILocatorFilter
{
public:
DirectoryFilter(Utils::Id id);
- void restoreState(const QByteArray &state) override;
bool openConfigDialog(QWidget *parent, bool &needsRefresh) override;
protected:
diff --git a/src/plugins/coreplugin/locator/filesystemfilter.cpp b/src/plugins/coreplugin/locator/filesystemfilter.cpp
index 00890829b4..6b484635e4 100644
--- a/src/plugins/coreplugin/locator/filesystemfilter.cpp
+++ b/src/plugins/coreplugin/locator/filesystemfilter.cpp
@@ -10,8 +10,6 @@
#include "../vcsmanager.h"
#include "locatormanager.h"
-#include <extensionsystem/pluginmanager.h>
-
#include <utils/algorithm.h>
#include <utils/async.h>
#include <utils/checkablemessagebox.h>
@@ -312,7 +310,6 @@ LocatorMatcherTasks FileSystemFilter::matchers()
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);
};
@@ -392,25 +389,4 @@ void FileSystemFilter::restoreState(const QJsonObject &object)
m_includeHidden = object.value(kIncludeHiddenKey).toBool(s_includeHiddenDefault);
}
-void FileSystemFilter::restoreState(const QByteArray &state)
-{
- if (isOldSetting(state)) {
- // TODO read old settings, remove some time after Qt Creator 4.15
- QDataStream in(state);
- in >> m_includeHidden;
-
- // An attempt to prevent setting this on old configuration
- if (!in.atEnd()) {
- QString shortcut;
- bool defaultFilter;
- in >> shortcut;
- in >> defaultFilter;
- setShortcutString(shortcut);
- setIncludedByDefault(defaultFilter);
- }
- } else {
- ILocatorFilter::restoreState(state);
- }
-}
-
} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/filesystemfilter.h b/src/plugins/coreplugin/locator/filesystemfilter.h
index 3dfac11b3a..74198ec9ae 100644
--- a/src/plugins/coreplugin/locator/filesystemfilter.h
+++ b/src/plugins/coreplugin/locator/filesystemfilter.h
@@ -11,7 +11,6 @@ class FileSystemFilter : public ILocatorFilter
{
public:
FileSystemFilter();
- void restoreState(const QByteArray &state) final;
bool openConfigDialog(QWidget *parent, bool &needsRefresh) final;
protected:
diff --git a/src/plugins/coreplugin/locator/ilocatorfilter.cpp b/src/plugins/coreplugin/locator/ilocatorfilter.cpp
index e9a999d536..0fa19f2b6e 100644
--- a/src/plugins/coreplugin/locator/ilocatorfilter.cpp
+++ b/src/plugins/coreplugin/locator/ilocatorfilter.cpp
@@ -5,8 +5,6 @@
#include "../coreplugintr.h"
-#include <extensionsystem/pluginmanager.h>
-
#include <solutions/tasking/tasktreerunner.h>
#include <utils/algorithm.h>
@@ -277,8 +275,8 @@ ResultsCollector::~ResultsCollector()
return;
m_deduplicator->cancel();
- if (ExtensionSystem::PluginManager::futureSynchronizer()) {
- ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(m_watcher->future());
+ if (Utils::futureSynchronizer()) {
+ Utils::futureSynchronizer()->addFuture(m_watcher->future());
return;
}
m_watcher->future().waitForFinished();
@@ -427,15 +425,12 @@ void LocatorMatcher::start()
QTC_ASSERT(!isRunning(), return);
d->m_output = {};
- struct CollectorStorage
- {
- ResultsCollector *m_collector = nullptr;
- };
- Storage<CollectorStorage> collectorStorage;
+ const Storage<ResultsCollector *> collectorStorage;
+ const LoopList iterator(d->m_tasks);
- const int filterCount = d->m_tasks.size();
- const auto onCollectorSetup = [this, filterCount, collectorStorage](ResultsCollector &collector) {
- collectorStorage->m_collector = &collector;
+ const auto onCollectorSetup = [this, filterCount = d->m_tasks.size(), collectorStorage](
+ ResultsCollector &collector) {
+ *collectorStorage = &collector;
collector.setFilterCount(filterCount);
connect(&collector, &ResultsCollector::serialOutputDataReady,
this, [this](const LocatorFilterEntries &serialOutputData) {
@@ -443,42 +438,31 @@ void LocatorMatcher::start()
emit serialOutputDataReady(serialOutputData);
});
};
- const auto onCollectorDone = [collectorStorage] {
- collectorStorage->m_collector = nullptr;
- };
-
- QList<GroupItem> parallelTasks {parallelLimit(d->m_parallelLimit)};
+ const auto onCollectorDone = [collectorStorage] { *collectorStorage = nullptr; };
- const auto onSetup = [this, collectorStorage](const Storage<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 onTaskTreeSetup = [iterator, input = d->m_input, collectorStorage](TaskTree &taskTree) {
+ const std::shared_ptr<ResultsDeduplicator> deduplicator = (*collectorStorage)->deduplicator();
+ const Storage<LocatorStorage> storage = iterator->storage;
+ const auto onSetup = [storage, input, index = iterator.iteration(), deduplicator] {
+ *storage = std::make_shared<LocatorStoragePrivate>(input, index, deduplicator);
};
- };
-
- int index = 0;
- for (const LocatorMatcherTask &task : std::as_const(d->m_tasks)) {
- const auto storage = task.storage;
- const Group group {
+ taskTree.setRecipe({
finishAllAndSuccess,
storage,
- onGroupSetup(onSetup(storage, index)),
- onGroupDone([storage] { storage->finalize(); }),
- task.task
- };
- parallelTasks << group;
- ++index;
- }
+ onGroupSetup(onSetup),
+ iterator->task,
+ onGroupDone([storage] { storage->finalize(); })
+ });
+ };
const Group root {
parallel,
collectorStorage,
ResultsCollectorTask(onCollectorSetup, onCollectorDone),
Group {
- parallelTasks
+ parallelLimit(d->m_parallelLimit),
+ iterator,
+ TaskTreeTask(onTaskTreeSetup)
}
};
d->m_taskTreeRunner.start(root, {}, [this](DoneWith result) {
@@ -671,15 +655,6 @@ void ILocatorFilter::restoreState(const QByteArray &state)
setShortcutString(obj.value(kShortcutStringKey).toString(m_defaultShortcut));
setIncludedByDefault(obj.value(kIncludedByDefaultKey).toBool(m_defaultIncludedByDefault));
restoreState(obj);
- } else {
- // TODO read old settings, remove some time after Qt Creator 4.15
- m_shortcut = m_defaultShortcut;
- m_includedByDefault = m_defaultIncludedByDefault;
-
- // TODO this reads legacy settings from Qt Creator < 4.15
- QDataStream in(state);
- in >> m_shortcut;
- in >> m_includedByDefault;
}
}
@@ -1061,17 +1036,6 @@ void ILocatorFilter::restoreState(const QJsonObject &object)
}
/*!
- Returns if \a state must be restored via pre-4.15 settings reading.
-*/
-bool ILocatorFilter::isOldSetting(const QByteArray &state)
-{
- if (state.isEmpty())
- return false;
- const QJsonDocument doc = QJsonDocument::fromJson(state);
- return !doc.isObject();
-}
-
-/*!
\enum Core::ILocatorFilter::Priority
This enum value holds the priority that is used for ordering the results
@@ -1484,7 +1448,6 @@ LocatorMatcherTask LocatorFileCache::matcher() const
// no provider is set or it returned empty generator
that->bumpExecutionId();
- async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
async.setConcurrentCallData(&filter, *storage, *that);
return SetupResult::Continue;
};
diff --git a/src/plugins/coreplugin/locator/ilocatorfilter.h b/src/plugins/coreplugin/locator/ilocatorfilter.h
index 4a4bcc5c0e..368f031381 100644
--- a/src/plugins/coreplugin/locator/ilocatorfilter.h
+++ b/src/plugins/coreplugin/locator/ilocatorfilter.h
@@ -226,8 +226,8 @@ public:
std::optional<QString> defaultSearchText() const;
void setDefaultSearchText(const QString &defaultSearchText);
- virtual QByteArray saveState() const;
- virtual void restoreState(const QByteArray &state);
+ QByteArray saveState() const;
+ void restoreState(const QByteArray &state);
virtual bool openConfigDialog(QWidget *parent, bool &needsRefresh);
bool isConfigurable() const;
@@ -272,8 +272,6 @@ protected:
void setRefreshRecipe(const std::optional<Tasking::GroupItem> &recipe);
std::optional<Tasking::GroupItem> refreshRecipe() const;
- static bool isOldSetting(const QByteArray &state);
-
private:
virtual LocatorMatcherTasks matchers() = 0;
diff --git a/src/plugins/coreplugin/locator/locatorsettingspage.cpp b/src/plugins/coreplugin/locator/locatorsettingspage.cpp
index d379a5c858..4eeeb1a0ac 100644
--- a/src/plugins/coreplugin/locator/locatorsettingspage.cpp
+++ b/src/plugins/coreplugin/locator/locatorsettingspage.cpp
@@ -274,11 +274,9 @@ public:
m_refreshInterval->setSingleStep(5);
m_refreshInterval->setValue(60);
- auto relativePathsLabel = new QLabel(Tr::tr("Show Paths in Relation to Active Project:"));
- relativePathsLabel->setToolTip(Tr::tr("Locator filters show relative paths to the active project when possible."));
-
- m_relativePaths = new QCheckBox;
- m_relativePaths->setToolTip(relativePathsLabel->toolTip());
+ m_relativePaths = new QCheckBox(Tr::tr("Show Paths in Relation to Active Project"));
+ m_relativePaths->setToolTip(
+ Tr::tr("Locator filters show relative paths to the active project when possible."));
auto filterEdit = new FancyLineEdit;
filterEdit->setFiltering(true);
@@ -322,15 +320,14 @@ public:
Column buttons{addButton, m_removeButton, m_editButton, st};
- Grid{filterEdit,
- br,
- m_filterList,
- buttons,
- br,
- Span(2, Row{refreshIntervalLabel, m_refreshInterval, st}),
- br,
- Span(2, Row{relativePathsLabel, m_relativePaths, st})}
- .attachTo(this);
+ // clang-format off
+ Grid {
+ filterEdit, br,
+ m_filterList, buttons, br,
+ Span(2, Row{refreshIntervalLabel, m_refreshInterval, st}), br,
+ Span(2, Row{m_relativePaths, st})
+ }.attachTo(this);
+ // clang-format on
connect(filterEdit, &FancyLineEdit::filterChanged, this, &LocatorSettingsWidget::setFilter);
connect(m_filterList->selectionModel(),
diff --git a/src/plugins/coreplugin/locator/locatorwidget.cpp b/src/plugins/coreplugin/locator/locatorwidget.cpp
index e6acfee42f..c5444344a6 100644
--- a/src/plugins/coreplugin/locator/locatorwidget.cpp
+++ b/src/plugins/coreplugin/locator/locatorwidget.cpp
@@ -57,8 +57,8 @@ public:
LocatorModel(QObject *parent = nullptr)
: QAbstractListModel(parent)
- , m_backgroundColor(Utils::creatorTheme()->color(Theme::TextColorHighlightBackground))
- , m_foregroundColor(Utils::creatorTheme()->color(Theme::TextColorNormal))
+ , m_backgroundColor(Utils::creatorColor(Theme::TextColorHighlightBackground))
+ , m_foregroundColor(Utils::creatorColor(Theme::TextColorNormal))
{}
void clear();
diff --git a/src/plugins/coreplugin/locator/opendocumentsfilter.cpp b/src/plugins/coreplugin/locator/opendocumentsfilter.cpp
index 719de23098..ede24d5b89 100644
--- a/src/plugins/coreplugin/locator/opendocumentsfilter.cpp
+++ b/src/plugins/coreplugin/locator/opendocumentsfilter.cpp
@@ -6,8 +6,6 @@
#include "../coreplugintr.h"
#include "../editormanager/documentmodel.h"
-#include <extensionsystem/pluginmanager.h>
-
#include <utils/algorithm.h>
#include <utils/async.h>
#include <utils/link.h>
@@ -77,7 +75,6 @@ LocatorMatcherTasks OpenDocumentsFilter::matchers()
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);
};
diff --git a/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp b/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp
index b360ad595d..d8f07178b4 100644
--- a/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp
+++ b/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp
@@ -6,8 +6,6 @@
#include "../coreplugintr.h"
#include "../messagemanager.h"
-#include <extensionsystem/pluginmanager.h>
-
#include <utils/algorithm.h>
#include <utils/async.h>
#include <utils/commandline.h>
@@ -198,7 +196,6 @@ LocatorMatcherTasks SpotlightLocatorFilter::matchers()
? insensArgs : sensArgs;
const CommandLine cmd(FilePath::fromString(command), expander->expand(args),
CommandLine::Raw);
- async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
async.setConcurrentCallData(matches, *storage, cmd, sortResults);
return SetupResult::Continue;
};
diff --git a/src/plugins/coreplugin/locator/urllocatorfilter.cpp b/src/plugins/coreplugin/locator/urllocatorfilter.cpp
index 1c67d9e032..7457f0907c 100644
--- a/src/plugins/coreplugin/locator/urllocatorfilter.cpp
+++ b/src/plugins/coreplugin/locator/urllocatorfilter.cpp
@@ -207,34 +207,6 @@ void UrlLocatorFilter::restoreState(const QJsonObject &object)
&QVariant::toString);
}
-void UrlLocatorFilter::restoreState(const QByteArray &state)
-{
- if (isOldSetting(state)) {
- // TODO read old settings, remove some time after Qt Creator 4.15
- QDataStream in(state);
-
- QString value;
- in >> value;
- m_remoteUrls = value.split('^', Qt::SkipEmptyParts);
-
- QString shortcut;
- in >> shortcut;
- setShortcutString(shortcut);
-
- bool defaultFilter;
- in >> defaultFilter;
- setIncludedByDefault(defaultFilter);
-
- if (!in.atEnd()) {
- QString name;
- in >> name;
- setDisplayName(name);
- }
- } else {
- ILocatorFilter::restoreState(state);
- }
-}
-
bool UrlLocatorFilter::openConfigDialog(QWidget *parent, bool &needsRefresh)
{
Q_UNUSED(needsRefresh)
diff --git a/src/plugins/coreplugin/locator/urllocatorfilter.h b/src/plugins/coreplugin/locator/urllocatorfilter.h
index 18535569c5..cc57903479 100644
--- a/src/plugins/coreplugin/locator/urllocatorfilter.h
+++ b/src/plugins/coreplugin/locator/urllocatorfilter.h
@@ -24,7 +24,6 @@ public:
UrlLocatorFilter(Utils::Id id);
UrlLocatorFilter(const QString &displayName, Utils::Id id);
- void restoreState(const QByteArray &state) final;
bool openConfigDialog(QWidget *parent, bool &needsRefresh) final;
void addDefaultUrl(const QString &urlTemplate);
diff --git a/src/plugins/coreplugin/loggingviewer.cpp b/src/plugins/coreplugin/loggingviewer.cpp
index d6aad89714..427b0a064c 100644
--- a/src/plugins/coreplugin/loggingviewer.cpp
+++ b/src/plugins/coreplugin/loggingviewer.cpp
@@ -715,7 +715,7 @@ LoggingViewManagerWidget::LoggingViewManagerWidget(QWidget *parent)
Splitter {
bindTo(&splitter),
Column {
- noMargin(),
+ noMargin,
Row {
spacing(0),
save,
@@ -729,7 +729,7 @@ LoggingViewManagerWidget::LoggingViewManagerWidget(QWidget *parent)
m_logView
},
Column {
- noMargin(),
+ noMargin,
Row {
qtInternal,
filterEdit,
diff --git a/src/plugins/coreplugin/manhattanstyle.cpp b/src/plugins/coreplugin/manhattanstyle.cpp
index c4cba4845b..390970eb84 100644
--- a/src/plugins/coreplugin/manhattanstyle.cpp
+++ b/src/plugins/coreplugin/manhattanstyle.cpp
@@ -269,15 +269,15 @@ void ManhattanStyle::unpolish(QApplication *app)
QPalette panelPalette(const QPalette &oldPalette, bool lightColored = false)
{
- QColor color = creatorTheme()->color(lightColored ? Theme::PanelTextColorDark
- : Theme::PanelTextColorLight);
+ QColor color = creatorColor(lightColored ? Theme::PanelTextColorDark
+ : Theme::PanelTextColorLight);
QPalette pal = oldPalette;
pal.setBrush(QPalette::All, QPalette::WindowText, color);
pal.setBrush(QPalette::All, QPalette::ButtonText, color);
if (lightColored)
color.setAlpha(100);
else
- color = creatorTheme()->color(Theme::IconsDisabledColor);
+ color = creatorColor(Theme::IconsDisabledColor);
pal.setBrush(QPalette::Disabled, QPalette::WindowText, color);
pal.setBrush(QPalette::Disabled, QPalette::ButtonText, color);
return pal;
@@ -328,7 +328,7 @@ void ManhattanStyle::polish(QWidget *widget)
QPalette palette = panelPalette(widget->palette(), isLightColored);
if (!isLightColored)
palette.setBrush(QPalette::All, QPalette::WindowText,
- creatorTheme()->color(Theme::ComboBoxTextColor));
+ creatorColor(Theme::ComboBoxTextColor));
widget->setPalette(palette);
widget->setMaximumHeight(height - 2);
widget->setAttribute(Qt::WA_Hover);
@@ -450,9 +450,9 @@ static void drawPrimitiveTweakedForDarkTheme(QStyle::PrimitiveElement element,
const bool isSunken = option->state & QStyle::State_Sunken;
const QColor frameColor = isEnabled ? option->palette.color(QPalette::Mid).darker(132)
- : creatorTheme()->color(Theme::BackgroundColorDisabled);
+ : creatorColor(Theme::BackgroundColorDisabled);
const QColor indicatorColor = isEnabled ? option->palette.color(QPalette::Mid).darker(90)
- : creatorTheme()->color(Theme::BackgroundColorDisabled);
+ : creatorColor(Theme::BackgroundColorDisabled);
const QColor bgColor = isSunken ? option->palette.color(QPalette::Mid).darker()
: option->palette.color(QPalette::Window);
const QColor hlColor = option->palette.color(QPalette::Highlight);
@@ -613,7 +613,7 @@ void ManhattanStyle::drawPrimitiveForPanelWidget(PrimitiveElement element,
QRect rect = option->rect;
switch (element) {
case PE_IndicatorDockWidgetResizeHandle:
- painter->fillRect(option->rect, creatorTheme()->color(Theme::DockWidgetResizeHandleColor));
+ painter->fillRect(option->rect, creatorColor(Theme::DockWidgetResizeHandleColor));
break;
case PE_FrameDockWidget:
QCommonStyle::drawPrimitive(element, option, painter, widget);
@@ -631,7 +631,7 @@ void ManhattanStyle::drawPrimitiveForPanelWidget(PrimitiveElement element,
painter->setOpacity(0.75);
QBrush baseBrush = option->palette.base();
if (widget && qobject_cast<const QSpinBox *>(widget->parentWidget()))
- baseBrush = creatorTheme()->color(Theme::DScontrolBackgroundDisabled);
+ baseBrush = creatorColor(Theme::DScontrolBackgroundDisabled);
painter->fillRect(backgroundRect, baseBrush);
painter->restore();
} else {
@@ -725,7 +725,7 @@ void ManhattanStyle::drawPrimitiveForPanelWidget(PrimitiveElement element,
painter->setPen(StyleHelper::sidebarShadow());
if (pressed) {
StyleHelper::drawPanelBgRect(
- painter, rect, creatorTheme()->color(Theme::FancyToolButtonSelectedColor));
+ painter, rect, creatorColor(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);
@@ -736,13 +736,13 @@ void ManhattanStyle::drawPrimitiveForPanelWidget(PrimitiveElement element,
} else if (option->state & State_Enabled && option->state & State_MouseOver) {
if (widget->property(StyleHelper::C_TOOLBAR_ACTIONWIDGET).toBool()) {
painter->save();
- painter->setBrush(creatorTheme()->color(Theme::FancyToolButtonHoverColor));
+ painter->setBrush(creatorColor(Theme::FancyToolButtonHoverColor));
painter->drawRoundedRect(rect, 5, 5);
painter->restore();
} else {
StyleHelper::drawPanelBgRect(painter,
rect,
- creatorTheme()->color(
+ creatorColor(
Theme::FancyToolButtonHoverColor));
}
}
@@ -868,9 +868,8 @@ void ManhattanStyle::drawControl(
const bool enabled = mbi->state & State_Enabled;
QStyleOptionMenuItem item = *mbi;
item.rect = mbi->rect;
- const QColor color = creatorTheme()->color(enabled
- ? Theme::MenuItemTextColorNormal
- : Theme::MenuItemTextColorDisabled);
+ const QColor color = creatorColor(enabled ? Theme::MenuItemTextColorNormal
+ : Theme::MenuItemTextColorDisabled);
if (color.isValid()) {
QPalette pal = mbi->palette;
pal.setBrush(QPalette::Text, color);
@@ -897,24 +896,23 @@ void ManhattanStyle::drawControl(
item.rect = mbi->rect;
QPalette pal = mbi->palette;
pal.setBrush(QPalette::ButtonText, dis
- ? creatorTheme()->color(Theme::MenuBarItemTextColorDisabled)
- : creatorTheme()->color(Theme::MenuBarItemTextColorNormal));
+ ? creatorColor(Theme::MenuBarItemTextColorDisabled)
+ : creatorColor(Theme::MenuBarItemTextColorNormal));
item.palette = pal;
QCommonStyle::drawControl(element, &item, painter, widget);
if (act) {
// Fill|
const QColor fillColor = StyleHelper::alphaBlendedColors(
- StyleHelper::baseColor(), creatorTheme()->color(Theme::FancyToolButtonHoverColor));
+ StyleHelper::baseColor(), creatorColor(Theme::FancyToolButtonHoverColor));
painter->fillRect(option->rect, fillColor);
QPalette pal = mbi->palette;
uint alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
if (!styleHint(SH_UnderlineShortcut, mbi, widget))
alignment |= Qt::TextHideMnemonic;
- pal.setBrush(QPalette::Text, creatorTheme()->color(dis
- ? Theme::IconsDisabledColor
- : Theme::PanelTextColorLight));
+ pal.setBrush(QPalette::Text, creatorColor(dis ? Theme::IconsDisabledColor
+ : Theme::PanelTextColorLight));
drawItemText(painter, item.rect, alignment, pal, !dis, mbi->text, QPalette::Text);
}
}
@@ -981,7 +979,7 @@ void ManhattanStyle::drawControl(
}
painter->setPen((option->state & State_Enabled)
? option->palette.color(QPalette::WindowText)
- : creatorTheme()->color(Theme::IconsDisabledColor));
+ : creatorColor(Theme::IconsDisabledColor));
painter->drawText(editRect.adjusted(1, 0, -1, 0), Qt::AlignLeft | Qt::AlignVCenter, text);
painter->restore();
@@ -1072,7 +1070,7 @@ void ManhattanStyle::drawControl(
// toolbar should draw the top or bottom outline
// (needed for the find toolbar for instance)
const QColor hightLight = creatorTheme()->flag(Theme::FlatToolBars)
- ? creatorTheme()->color(Theme::FancyToolBarSeparatorColor)
+ ? creatorColor(Theme::FancyToolBarSeparatorColor)
: StyleHelper::sidebarHighlight();
const QColor borderColor = drawLightColored
? QColor(255, 255, 255, 180) : hightLight;
@@ -1158,7 +1156,7 @@ void ManhattanStyle::drawComplexControl(ComplexControl control, const QStyleOpti
label.palette = panelPalette(option->palette, lightColored(widget));
if (widget && widget->property(StyleHelper::C_HIGHLIGHT_WIDGET).toBool()) {
label.palette.setColor(QPalette::ButtonText,
- creatorTheme()->color(Theme::IconsWarningToolBarColor));
+ creatorColor(Theme::IconsWarningToolBarColor));
}
int fw = pixelMetric(PM_DefaultFrameWidth, option, widget);
label.rect = button.adjusted(fw, fw, -fw, -fw);
@@ -1270,7 +1268,7 @@ void ManhattanStyle::drawButtonSeparator(QPainter *painter, const QRect &rect, b
const QRectF borderRect = QRectF(rect).adjusted(0.5, 0.5, -0.5, -0.5);
if (creatorTheme()->flag(Theme::FlatToolBars)) {
const int margin = 3;
- painter->setPen(creatorTheme()->color(Theme::FancyToolBarSeparatorColor));
+ painter->setPen(creatorColor(Theme::FancyToolBarSeparatorColor));
painter->drawLine(borderRect.topRight() + QPointF(0, margin),
borderRect.bottomRight() - QPointF(0, margin));
} else {
diff --git a/src/plugins/coreplugin/messagemanager.cpp b/src/plugins/coreplugin/messagemanager.cpp
index 4eca635b84..464d3fce71 100644
--- a/src/plugins/coreplugin/messagemanager.cpp
+++ b/src/plugins/coreplugin/messagemanager.cpp
@@ -27,14 +27,6 @@ namespace Core {
static MessageManager *m_instance = nullptr;
static Internal::MessageOutputWindow *m_messageOutputWindow = nullptr;
-/*!
- \internal
-*/
-MessageManager *MessageManager::instance()
-{
- return m_instance;
-}
-
enum class Flag { Silent, Flash, Disrupt };
static void showOutputPane(Flag flags)
diff --git a/src/plugins/coreplugin/messagemanager.h b/src/plugins/coreplugin/messagemanager.h
index 98eb0635a1..f28b707230 100644
--- a/src/plugins/coreplugin/messagemanager.h
+++ b/src/plugins/coreplugin/messagemanager.h
@@ -26,8 +26,6 @@ class CORE_EXPORT MessageManager : public QObject
Q_OBJECT
public:
- static MessageManager *instance();
-
static void setFont(const QFont &font);
static void setWheelZoomEnabled(bool enabled);
diff --git a/src/plugins/coreplugin/minisplitter.cpp b/src/plugins/coreplugin/minisplitter.cpp
index cdf6d16d8e..56adc7a608 100644
--- a/src/plugins/coreplugin/minisplitter.cpp
+++ b/src/plugins/coreplugin/minisplitter.cpp
@@ -138,7 +138,7 @@ void MiniSplitterHandle::resizeEvent(QResizeEvent *event)
void MiniSplitterHandle::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
- const QColor color = Utils::creatorTheme()->color(
+ const QColor color = Utils::creatorColor(
m_lightColored ? Utils::Theme::FancyToolBarSeparatorColor
: Utils::Theme::SplitterColor);
painter.fillRect(event->rect(), color);
diff --git a/src/plugins/coreplugin/outputpanemanager.cpp b/src/plugins/coreplugin/outputpanemanager.cpp
index 48ba622418..a5495612d5 100644
--- a/src/plugins/coreplugin/outputpanemanager.cpp
+++ b/src/plugins/coreplugin/outputpanemanager.cpp
@@ -410,14 +410,14 @@ OutputPaneManager::OutputPaneManager(QWidget *parent) :
m_opToolBarWidgets,
minMaxButton,
closeButton,
- spacing(0), noMargin(),
+ spacing(0), noMargin,
}.attachTo(toolBar);
Column {
toolBar,
m_outputWidgetPane,
new FindToolBarPlaceHolder(this),
- spacing(0), noMargin(),
+ spacing(0), noMargin,
}.attachTo(this);
Row {
@@ -503,7 +503,7 @@ void OutputPaneManager::initialize()
QWidget *toolButtonsContainer = new QWidget(m_instance->m_opToolBarWidgets);
using namespace Layouting;
- Row toolButtonsRow { spacing(0), noMargin() };
+ Row toolButtonsRow { spacing(0), noMargin };
const QList<QWidget *> toolBarWidgets = outPane->toolBarWidgets();
for (QWidget *toolButton : toolBarWidgets)
toolButtonsRow.addItem(toolButton);
@@ -940,7 +940,7 @@ void OutputPaneToggleButton::paintEvent(QPaintEvent*)
c = Theme::BackgroundColorSelected;
if (c != Theme::BackgroundColorDark)
- StyleHelper::drawPanelBgRect(&p, bgRect(rect()), creatorTheme()->color(c));
+ StyleHelper::drawPanelBgRect(&p, bgRect(rect()), creatorColor(c));
} else {
const QImage *image = nullptr;
if (isDown()) {
@@ -974,7 +974,7 @@ void OutputPaneToggleButton::paintEvent(QPaintEvent*)
if (m_flashTimer->state() == QTimeLine::Running)
{
- QColor c = creatorTheme()->color(Theme::OutputPaneButtonFlashColor);
+ QColor c = creatorColor(Theme::OutputPaneButtonFlashColor);
c.setAlpha (m_flashTimer->currentFrame());
if (creatorTheme()->flag(Theme::FlatToolBars))
StyleHelper::drawPanelBgRect(&p, bgRect(rect()), c);
@@ -983,10 +983,10 @@ void OutputPaneToggleButton::paintEvent(QPaintEvent*)
}
p.setFont(font());
- p.setPen(creatorTheme()->color(Theme::OutputPaneToggleButtonTextColorChecked));
+ p.setPen(creatorColor(Theme::OutputPaneToggleButtonTextColorChecked));
p.drawText((numberAreaWidth() - numberWidth) / 2, baseLine, m_number);
if (!isChecked())
- p.setPen(creatorTheme()->color(Theme::OutputPaneToggleButtonTextColorUnchecked));
+ p.setPen(creatorColor(Theme::OutputPaneToggleButtonTextColorUnchecked));
int leftPart = numberAreaWidth() + buttonBorderWidth;
int labelWidth = 0;
if (!m_badgeNumberLabel.text().isEmpty()) {
@@ -1083,15 +1083,15 @@ void BadgeLabel::paint(QPainter *p, int x, int y, bool isChecked)
const QRectF rect(QRect(QPoint(x, y), m_size));
p->save();
- p->setBrush(creatorTheme()->color(isChecked? Theme::BadgeLabelBackgroundColorChecked
- : Theme::BadgeLabelBackgroundColorUnchecked));
+ p->setBrush(creatorColor(isChecked? Theme::BadgeLabelBackgroundColorChecked
+ : Theme::BadgeLabelBackgroundColorUnchecked));
p->setPen(Qt::NoPen);
p->setRenderHint(QPainter::Antialiasing, true);
p->drawRoundedRect(rect, m_padding, m_padding, Qt::AbsoluteSize);
p->setFont(m_font);
- p->setPen(creatorTheme()->color(isChecked ? Theme::BadgeLabelTextColorChecked
- : Theme::BadgeLabelTextColorUnchecked));
+ p->setPen(creatorColor(isChecked ? Theme::BadgeLabelTextColorChecked
+ : Theme::BadgeLabelTextColorUnchecked));
p->drawText(rect, Qt::AlignCenter, m_text);
p->restore();
diff --git a/src/plugins/coreplugin/plugindialog.cpp b/src/plugins/coreplugin/plugindialog.cpp
index 05d63aedc7..edca2360b3 100644
--- a/src/plugins/coreplugin/plugindialog.cpp
+++ b/src/plugins/coreplugin/plugindialog.cpp
@@ -98,7 +98,7 @@ void PluginDialog::closeDialog()
void PluginDialog::showInstallWizard()
{
- if (PluginInstallWizard::exec())
+ if (executePluginInstallWizard())
m_isRestartRequired = true;
}
diff --git a/src/plugins/coreplugin/plugininstallwizard.cpp b/src/plugins/coreplugin/plugininstallwizard.cpp
index 4d4ba69de9..250ed0a31a 100644
--- a/src/plugins/coreplugin/plugininstallwizard.cpp
+++ b/src/plugins/coreplugin/plugininstallwizard.cpp
@@ -71,7 +71,6 @@ static FilePath pluginInstallPath(bool installIntoApplication)
}
namespace Core {
-namespace Internal {
class SourcePage : public WizardPage
{
@@ -145,7 +144,7 @@ struct ArchiveIssue
// Async. Result is set if any issue was found.
void checkContents(QPromise<ArchiveIssue> &promise, const FilePath &tempDir)
{
- PluginSpec *coreplugin = PluginManager::specForPlugin(CorePlugin::instance());
+ PluginSpec *coreplugin = PluginManager::specForPlugin(Internal::CorePlugin::instance());
// look for plugin
QDirIterator it(tempDir.path(), libraryNameFilter(), QDir::Files | QDir::NoSymLinks,
@@ -242,7 +241,6 @@ public:
return SetupResult::StopWithError;
async.setConcurrentCallData(checkContents, m_tempDir->path());
- async.setFutureSynchronizer(PluginManager::futureSynchronizer());
return SetupResult::Continue;
};
const auto onCheckerDone = [this](const Async<ArchiveIssue> &async) {
@@ -398,15 +396,19 @@ static bool copyPluginFile(const FilePath &src, const FilePath &dest)
return true;
}
-bool PluginInstallWizard::exec()
+bool executePluginInstallWizard(const FilePath &archive)
{
Wizard wizard(ICore::dialogParent());
wizard.setWindowTitle(Tr::tr("Install Plugin"));
Data data;
- auto filePage = new SourcePage(&data, &wizard);
- wizard.addPage(filePage);
+ if (archive.isEmpty()) {
+ auto filePage = new SourcePage(&data, &wizard);
+ wizard.addPage(filePage);
+ } else {
+ data.sourcePath = archive;
+ }
auto checkArchivePage = new CheckArchivePage(&data, &wizard);
wizard.addPage(checkArchivePage);
@@ -439,5 +441,4 @@ bool PluginInstallWizard::exec()
return false;
}
-} // namespace Internal
} // namespace Core
diff --git a/src/plugins/coreplugin/plugininstallwizard.h b/src/plugins/coreplugin/plugininstallwizard.h
index 6f9b80af8a..c5b1fb5d65 100644
--- a/src/plugins/coreplugin/plugininstallwizard.h
+++ b/src/plugins/coreplugin/plugininstallwizard.h
@@ -3,16 +3,14 @@
#pragma once
+#include "core_global.h"
+
+#include <utils/filepath.h>
+
#include <QCoreApplication>
namespace Core {
-namespace Internal {
-class PluginInstallWizard
-{
-public:
- static bool exec();
-};
+CORE_EXPORT bool executePluginInstallWizard(const Utils::FilePath &archive = {});
-} // namespace Internal
} // namespace Core
diff --git a/src/plugins/coreplugin/progressmanager/futureprogress.cpp b/src/plugins/coreplugin/progressmanager/futureprogress.cpp
index 62459bcfea..c798575af2 100644
--- a/src/plugins/coreplugin/progressmanager/futureprogress.cpp
+++ b/src/plugins/coreplugin/progressmanager/futureprogress.cpp
@@ -308,7 +308,7 @@ void FutureProgress::paintEvent(QPaintEvent *)
QPainter p(this);
if (creatorTheme()->flag(Theme::FlatToolBars)) {
p.fillRect(rect(), StyleHelper::baseColor());
- p.fillRect(rect(), creatorTheme()->color(Theme::FancyToolButtonSelectedColor));
+ p.fillRect(rect(), creatorColor(Theme::FancyToolButtonSelectedColor));
} else {
QLinearGradient grad = StyleHelper::statusBarGradient(rect());
p.fillRect(rect(), grad);
diff --git a/src/plugins/coreplugin/progressmanager/progressbar.cpp b/src/plugins/coreplugin/progressmanager/progressbar.cpp
index e63a21efdd..5047fa6600 100644
--- a/src/plugins/coreplugin/progressmanager/progressbar.cpp
+++ b/src/plugins/coreplugin/progressmanager/progressbar.cpp
@@ -245,7 +245,7 @@ void ProgressBar::paintEvent(QPaintEvent *)
textRect.setHeight(fm.height() + 4);
p.setFont(fnt);
- p.setPen(creatorTheme()->color(Theme::ProgressBarTitleColor));
+ p.setPen(creatorColor(Theme::ProgressBarTitleColor));
p.drawText(textRect, alignment | Qt::AlignBottom, elidedtitle);
if (!m_subtitle.isEmpty()) {
@@ -255,7 +255,7 @@ void ProgressBar::paintEvent(QPaintEvent *)
subtextRect.moveTop(progressY + progressHeight);
p.setFont(fnt);
- p.setPen(creatorTheme()->color(Theme::ProgressBarTitleColor));
+ p.setPen(creatorColor(Theme::ProgressBarTitleColor));
p.drawText(subtextRect, alignment | Qt::AlignBottom, elidedsubtitle);
}
}
@@ -274,12 +274,11 @@ void ProgressBar::paintEvent(QPaintEvent *)
themeColor = Theme::ProgressBarColorError;
else if (m_finished)
themeColor = Theme::ProgressBarColorFinished;
- const QColor c = creatorTheme()->color(themeColor);
+ const QColor c = creatorColor(themeColor);
//draw the progress bar
if (creatorTheme()->flag(Theme::FlatToolBars)) {
- p.fillRect(rect.adjusted(2, 2, -2, -2),
- creatorTheme()->color(Theme::ProgressBarBackgroundColor));
+ p.fillRect(rect.adjusted(2, 2, -2, -2), creatorColor(Theme::ProgressBarBackgroundColor));
p.fillRect(inner, c);
} else {
const static QImage bar(StyleHelper::dpiSpecificImageFile(
diff --git a/src/plugins/coreplugin/progressmanager/progressmanager.cpp b/src/plugins/coreplugin/progressmanager/progressmanager.cpp
index 5aee0ad266..a3e17b0036 100644
--- a/src/plugins/coreplugin/progressmanager/progressmanager.cpp
+++ b/src/plugins/coreplugin/progressmanager/progressmanager.cpp
@@ -768,7 +768,7 @@ FutureProgress *ProgressManager::addTask(const QFuture<void> &future, const QStr
Shows a progress indicator for task given by the QFutureInterface object
\a futureInterface.
The progress indicator shows the specified \a title along with the progress bar.
- The progress indicator will increase monotonically with time, at \a expectedSeconds
+ The progress indicator will increase monotonically with time, at \a expectedDuration
it will reach about 80%, and continue to increase with a decreasingly slower rate.
The \a type of a task will specify a logical grouping with other
diff --git a/src/plugins/coreplugin/welcomepagehelper.cpp b/src/plugins/coreplugin/welcomepagehelper.cpp
index c2b28c1f83..63ec6745b5 100644
--- a/src/plugins/coreplugin/welcomepagehelper.cpp
+++ b/src/plugins/coreplugin/welcomepagehelper.cpp
@@ -41,7 +41,7 @@ using namespace StyleHelper::SpacingTokens;
static QColor themeColor(Theme::Color role)
{
- return creatorTheme()->color(role);
+ return creatorColor(role);
}
namespace WelcomePageHelpers {
@@ -51,7 +51,7 @@ void setBackgroundColor(QWidget *widget, Theme::Color colorRole)
QPalette palette = creatorTheme()->palette();
const QPalette::ColorRole role = QPalette::Window;
palette.setBrush(role, {});
- palette.setColor(role, creatorTheme()->color(colorRole));
+ palette.setColor(role, creatorColor(colorRole));
widget->setPalette(palette);
widget->setBackgroundRole(role);
widget->setAutoFillBackground(true);
@@ -195,23 +195,23 @@ void Button::paintEvent(QPaintEvent *event)
switch (m_role) {
case MediumPrimary:
case SmallPrimary: {
- const QBrush fill(creatorTheme()->color(isDown()
- ? Theme::Token_Accent_Subtle
- : hovered ? Theme::Token_Accent_Muted
- : Theme::Token_Accent_Default));
+ const QBrush fill(creatorColor(isDown()
+ ? Theme::Token_Accent_Subtle
+ : hovered ? Theme::Token_Accent_Muted
+ : Theme::Token_Accent_Default));
drawCardBackground(&p, bgR, fill, QPen(Qt::NoPen), brRectRounding);
break;
}
case MediumSecondary:
case SmallSecondary: {
- const QPen outline(creatorTheme()->color(Theme::Token_Text_Default), hovered ? 2 : 1);
+ const QPen outline(creatorColor(Theme::Token_Text_Default), hovered ? 2 : 1);
drawCardBackground(&p, bgR, QBrush(Qt::NoBrush), outline, brRectRounding);
break;
}
case SmallList: {
if (isChecked() || hovered) {
- const QBrush fill(creatorTheme()->color(isChecked() ? Theme::Token_Foreground_Muted
- : Theme::Token_Foreground_Subtle));
+ const QBrush fill(creatorColor(isChecked() ? Theme::Token_Foreground_Muted
+ : Theme::Token_Foreground_Subtle));
drawCardBackground(&p, bgR, fill, QPen(Qt::NoPen), brRectRounding);
}
break;
@@ -329,11 +329,11 @@ void SearchBox::leaveEvent(QEvent *event)
static void paintCommonBackground(QPainter *p, const QRectF &rect, const QWidget *widget)
{
- const QBrush fill(creatorTheme()->color(Theme::Token_Background_Muted));
+ const QBrush fill(creatorColor(Theme::Token_Background_Muted));
const Theme::Color c = widget->hasFocus() ? Theme::Token_Stroke_Strong :
widget->underMouse() ? Theme::Token_Stroke_Muted
: Theme::Token_Stroke_Subtle;
- const QPen pen(creatorTheme()->color(c));
+ const QPen pen(creatorColor(c));
drawCardBackground(p, rect, fill, pen);
}
diff --git a/src/plugins/coreplugin/welcomepagehelper.h b/src/plugins/coreplugin/welcomepagehelper.h
index c32e803029..eb3082129a 100644
--- a/src/plugins/coreplugin/welcomepagehelper.h
+++ b/src/plugins/coreplugin/welcomepagehelper.h
@@ -31,11 +31,12 @@ namespace WelcomePageHelpers {
constexpr QSize WelcomeThumbnailSize(214, 160);
-class CORE_EXPORT TextFormat {
+class CORE_EXPORT TextFormat
+{
public:
QColor color() const
{
- return Utils::creatorTheme()->color(themeColor);
+ return Utils::creatorColor(themeColor);
}
QFont font(bool underlined = false) const
diff --git a/src/plugins/cpaster/CodePaster.json.in b/src/plugins/cpaster/CodePaster.json.in
index af6025becb..a89c0287f2 100644
--- a/src/plugins/cpaster/CodePaster.json.in
+++ b/src/plugins/cpaster/CodePaster.json.in
@@ -13,6 +13,6 @@
"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" : "Codepaster plugin for pushing/fetching diff from server.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/cppcheck/Cppcheck.json.in b/src/plugins/cppcheck/Cppcheck.json.in
index f2206e0fca..78103f3a3b 100644
--- a/src/plugins/cppcheck/Cppcheck.json.in
+++ b/src/plugins/cppcheck/Cppcheck.json.in
@@ -15,6 +15,6 @@
],
"Category" : "Code Analyzer",
"Description" : "Cppcheck static analyzer tool integration. See http://cppcheck.sourceforge.net.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/cppcheck/cppcheckrunner.cpp b/src/plugins/cppcheck/cppcheckrunner.cpp
index cf8633c8dd..d3f034e80b 100644
--- a/src/plugins/cppcheck/cppcheckrunner.cpp
+++ b/src/plugins/cppcheck/cppcheckrunner.cpp
@@ -113,16 +113,17 @@ void CppcheckRunner::checkQueued()
if (m_queue.isEmpty() || !m_binary.isExecutableFile())
return;
+ CommandLine commandLine{m_binary, m_arguments, CommandLine::Raw};
FilePaths files = m_queue.begin().value();
- QString arguments = m_arguments + ' ' + m_queue.begin().key();
+ commandLine.addArg(m_queue.begin().key());
m_currentFiles.clear();
- int argumentsLength = arguments.length();
+ int argumentsLength = commandLine.arguments().length();
while (!files.isEmpty()) {
- argumentsLength += files.first().toString().size() + 1; // +1 for separator
+ argumentsLength += files.first().toString().size() + 3; // +1 for separator +2 for quotes
if (argumentsLength >= m_maxArgumentsLength)
break;
m_currentFiles.push_back(files.first());
- arguments += ' ' + files.first().toString();
+ commandLine.addArg(files.first().toString());
files.pop_front();
}
@@ -131,7 +132,7 @@ void CppcheckRunner::checkQueued()
else
m_queue.begin().value() = files;
- m_process.setCommand(CommandLine(m_binary, arguments, CommandLine::Raw));
+ m_process.setCommand(commandLine);
m_process.start();
}
@@ -140,7 +141,7 @@ void CppcheckRunner::handleDone()
if (m_process.result() == ProcessResult::FinishedWithSuccess)
m_tool.finishParsing();
else
- Core::MessageManager::writeSilently(m_process.exitMessage());
+ m_tool.finishWithFail(m_process.exitMessage());
m_currentFiles.clear();
m_process.close();
diff --git a/src/plugins/cppcheck/cppchecksettings.cpp b/src/plugins/cppcheck/cppchecksettings.cpp
index 142d3ac236..144ed112f3 100644
--- a/src/plugins/cppcheck/cppchecksettings.cpp
+++ b/src/plugins/cppcheck/cppchecksettings.cpp
@@ -111,7 +111,7 @@ CppcheckSettings::CppcheckSettings()
readSettings();
}
-std::function<Layouting::LayoutItem()> CppcheckSettings::layouter()
+std::function<Layouting::Layout()> CppcheckSettings::layouter()
{
return [this] {
using namespace Layouting;
diff --git a/src/plugins/cppcheck/cppchecksettings.h b/src/plugins/cppcheck/cppchecksettings.h
index 8842b2b65f..c5b4a78a67 100644
--- a/src/plugins/cppcheck/cppchecksettings.h
+++ b/src/plugins/cppcheck/cppchecksettings.h
@@ -14,7 +14,7 @@ class CppcheckSettings final : public Utils::AspectContainer
public:
CppcheckSettings();
- std::function<Layouting::LayoutItem()> layouter();
+ std::function<Layouting::Layout()> layouter();
Utils::FilePathAspect binary{this};
Utils::BoolAspect warning{this};
diff --git a/src/plugins/cppcheck/cppchecktool.cpp b/src/plugins/cppcheck/cppchecktool.cpp
index 5bfcf941a3..84479a78be 100644
--- a/src/plugins/cppcheck/cppchecktool.cpp
+++ b/src/plugins/cppcheck/cppchecktool.cpp
@@ -304,4 +304,12 @@ void CppcheckTool::finishParsing()
m_progress->reportFinished();
}
+void CppcheckTool::finishWithFail(const QString &exitMessage)
+{
+ if (!exitMessage.isEmpty())
+ Core::MessageManager::writeSilently(exitMessage);
+ QTC_ASSERT(m_progress, return);
+ m_progress->cancelAndFinish();
+}
+
} // Cppcheck::Internal
diff --git a/src/plugins/cppcheck/cppchecktool.h b/src/plugins/cppcheck/cppchecktool.h
index d14485fd5e..e8e61efaef 100644
--- a/src/plugins/cppcheck/cppchecktool.h
+++ b/src/plugins/cppcheck/cppchecktool.h
@@ -42,6 +42,7 @@ public:
void parseOutputLine(const QString &line);
void parseErrorLine(const QString &line);
void finishParsing();
+ void finishWithFail(const QString &exitMessage);
private:
void updateArguments();
diff --git a/src/plugins/cppeditor/CMakeLists.txt b/src/plugins/cppeditor/CMakeLists.txt
index 3a137a4d64..aef17fe4e3 100644
--- a/src/plugins/cppeditor/CMakeLists.txt
+++ b/src/plugins/cppeditor/CMakeLists.txt
@@ -53,7 +53,6 @@ add_qtc_plugin(CppEditor
cppincludehierarchy.cpp cppincludehierarchy.h
cppincludesfilter.cpp cppincludesfilter.h
cppindexingsupport.cpp cppindexingsupport.h
- cppinsertvirtualmethods.cpp cppinsertvirtualmethods.h
cpplocalrenaming.cpp cpplocalrenaming.h
cpplocalsymbols.cpp cpplocalsymbols.h
cpplocatordata.cpp cpplocatordata.h
@@ -71,14 +70,6 @@ add_qtc_plugin(CppEditor
cppprojectpartchooser.cpp cppprojectpartchooser.h
cppprojectupdater.cpp cppprojectupdater.h
cppqtstyleindenter.cpp cppqtstyleindenter.h
- cppquickfix.cpp cppquickfix.h
- cppquickfixassistant.cpp cppquickfixassistant.h
- cppquickfixes.cpp cppquickfixes.h
- cppquickfixprojectsettings.cpp cppquickfixprojectsettings.h
- cppquickfixprojectsettingswidget.cpp cppquickfixprojectsettingswidget.h
- cppquickfixsettings.cpp cppquickfixsettings.h
- cppquickfixsettingspage.cpp cppquickfixsettingspage.h
- cppquickfixsettingswidget.cpp cppquickfixsettingswidget.h
cpprefactoringchanges.cpp cpprefactoringchanges.h
cppselectionchanger.cpp cppselectionchanger.h
cppsemanticinfo.h
@@ -104,6 +95,39 @@ add_qtc_plugin(CppEditor
insertionpointlocator.cpp insertionpointlocator.h
projectinfo.cpp projectinfo.h
projectpart.cpp projectpart.h
+ quickfixes/assigntolocalvariable.cpp quickfixes/assigntolocalvariable.h
+ quickfixes/bringidentifierintoscope.cpp quickfixes/bringidentifierintoscope.h
+ quickfixes/completeswitchstatement.cpp quickfixes/completeswitchstatement.h
+ quickfixes/convertnumericliteral.cpp quickfixes/convertnumericliteral.h
+ quickfixes/convertqt4connect.cpp quickfixes/convertqt4connect.h
+ quickfixes/convertstringliteral.cpp quickfixes/convertstringliteral.h
+ quickfixes/converttocamelcase.cpp quickfixes/converttocamelcase.h
+ quickfixes/converttometamethodcall.cpp quickfixes/converttometamethodcall.h
+ quickfixes/cppcodegenerationquickfixes.cpp quickfixes/cppcodegenerationquickfixes.h
+ quickfixes/cppinsertvirtualmethods.cpp quickfixes/cppinsertvirtualmethods.h
+ quickfixes/cppquickfix.cpp quickfixes/cppquickfix.h
+ quickfixes/cppquickfixassistant.cpp quickfixes/cppquickfixassistant.h
+ quickfixes/cppquickfixhelpers.h quickfixes/cppquickfixhelpers.cpp
+ quickfixes/cppquickfixprojectsettings.cpp quickfixes/cppquickfixprojectsettings.h
+ quickfixes/cppquickfixprojectsettingswidget.cpp quickfixes/cppquickfixprojectsettingswidget.h
+ quickfixes/cppquickfixsettings.cpp quickfixes/cppquickfixsettings.h
+ quickfixes/cppquickfixsettingspage.cpp quickfixes/cppquickfixsettingspage.h
+ quickfixes/cppquickfixsettingswidget.cpp quickfixes/cppquickfixsettingswidget.h
+ quickfixes/convertfromandtopointer.cpp quickfixes/convertfromandtopointer.h
+ quickfixes/createdeclarationfromuse.cpp quickfixes/createdeclarationfromuse.h
+ quickfixes/extractfunction.cpp quickfixes/extractfunction.h
+ quickfixes/extractliteralasparameter.cpp quickfixes/extractliteralasparameter.h
+ quickfixes/insertfunctiondefinition.cpp quickfixes/insertfunctiondefinition.h
+ quickfixes/logicaloperationquickfixes.cpp quickfixes/logicaloperationquickfixes.h
+ quickfixes/moveclasstoownfile.cpp quickfixes/moveclasstoownfile.h
+ quickfixes/movefunctiondefinition.cpp quickfixes/movefunctiondefinition.h
+ quickfixes/rearrangeparamdeclarationlist.cpp quickfixes/rearrangeparamdeclarationlist.h
+ quickfixes/reformatpointerdeclaration.cpp quickfixes/reformatpointerdeclaration.h
+ quickfixes/removeusingnamespace.cpp quickfixes/removeusingnamespace.h
+ quickfixes/rewritecomment.cpp quickfixes/rewritecomment.cpp
+ quickfixes/rewritecontrolstatements.cpp quickfixes/rewritecontrolstatements.h
+ quickfixes/splitsimpledeclaration.cpp quickfixes/splitsimpledeclaration.h
+ quickfixes/synchronizememberfunctionorder.cpp quickfixes/synchronizememberfunctionorder.h
resourcepreviewhoverhandler.cpp resourcepreviewhoverhandler.h
searchsymbols.cpp searchsymbols.h
semantichighlighter.cpp semantichighlighter.h
@@ -112,7 +136,7 @@ add_qtc_plugin(CppEditor
typehierarchybuilder.cpp typehierarchybuilder.h
wrappablelineedit.cpp wrappablelineedit.h
EXPLICIT_MOC
- cppquickfixsettingswidget.h
+ quickfixes/cppquickfixsettingswidget.h
)
extend_qtc_plugin(CppEditor
@@ -127,7 +151,6 @@ extend_qtc_plugin(CppEditor
cpplocatorfilter_test.cpp cpplocatorfilter_test.h
cppmodelmanager_test.cpp cppmodelmanager_test.h
cpppointerdeclarationformatter_test.cpp cpppointerdeclarationformatter_test.h
- cppquickfix_test.cpp cppquickfix_test.h
cpprenaming_test.cpp cpprenaming_test.h
cppsourceprocessertesthelper.cpp cppsourceprocessertesthelper.h
cppsourceprocessor_test.cpp cppsourceprocessor_test.h
@@ -137,9 +160,10 @@ extend_qtc_plugin(CppEditor
followsymbol_switchmethoddecldef_test.cpp followsymbol_switchmethoddecldef_test.h
modelmanagertesthelper.cpp modelmanagertesthelper.h
projectinfo_test.cpp projectinfo_test.h
+ quickfixes/cppquickfix_test.cpp quickfixes/cppquickfix_test.h
symbolsearcher_test.cpp symbolsearcher_test.h
typehierarchybuilder_test.cpp typehierarchybuilder_test.h
EXPLICIT_MOC
cppdoxygen_test.h
- cppquickfix_test.h
+ quickfixes/cppquickfix_test.h
)
diff --git a/src/plugins/cppeditor/CppEditor.json.in b/src/plugins/cppeditor/CppEditor.json.in
index 0e9d50eb4b..dcedb0f73d 100644
--- a/src/plugins/cppeditor/CppEditor.json.in
+++ b/src/plugins/cppeditor/CppEditor.json.in
@@ -14,7 +14,7 @@
],
"Category" : "C++",
"Description" : "C/C++ editor component.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/cppeditor/clangdsettings.cpp b/src/plugins/cppeditor/clangdsettings.cpp
index fe3bc84b23..daa78aad7e 100644
--- a/src/plugins/cppeditor/clangdsettings.cpp
+++ b/src/plugins/cppeditor/clangdsettings.cpp
@@ -770,7 +770,7 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD
if (!isForProject) {
m_sessionsModel.setStringList(settingsData.sessionsWithOneClangd);
m_sessionsModel.sort(0);
- m_sessionsGroupBox = new QGroupBox(Tr::tr("Sessions with a single clangd instance"));
+ m_sessionsGroupBox = new QGroupBox(Tr::tr("Sessions with a Single Clangd Instance"));
const auto sessionsView = new Utils::ListView;
sessionsView->setModel(&m_sessionsModel);
sessionsView->setToolTip(
diff --git a/src/plugins/cppeditor/compileroptionsbuilder.cpp b/src/plugins/cppeditor/compileroptionsbuilder.cpp
index a860edbee3..8bd1219a5d 100644
--- a/src/plugins/cppeditor/compileroptionsbuilder.cpp
+++ b/src/plugins/cppeditor/compileroptionsbuilder.cpp
@@ -899,8 +899,8 @@ void CompilerOptionsBuilder::evaluateCompilerFlags()
continue;
}
- // GCC option that clang doesn't know.
- if (option.contains("direct-extern-access"))
+ // GCC options that clang doesn't know.
+ if (option.contains("direct-extern-access") || option == "-fnothrow-opt")
continue;
// These were already parsed into ProjectPart::includedFiles.
diff --git a/src/plugins/cppeditor/cppcodegen_test.cpp b/src/plugins/cppeditor/cppcodegen_test.cpp
index 468e703930..b48ffa77ba 100644
--- a/src/plugins/cppeditor/cppcodegen_test.cpp
+++ b/src/plugins/cppeditor/cppcodegen_test.cpp
@@ -261,7 +261,7 @@ void CodegenTest::testProtectedBetweenPublicAndPrivate()
Should insert at line 18, column 1, with "private slots:\n" as prefix and "\n"
as suffix.
- This is the typical Qt Designer case, with test-input like what the integration
+ This is the typical \QD case, with test-input like what the integration
generates.
*/
void CodegenTest::testQtdesignerIntegration()
diff --git a/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp b/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp
index 542edbd57a..56c62dfca0 100644
--- a/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp
+++ b/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp
@@ -1445,11 +1445,11 @@ CppCodeModelInspectorDialog::CppCodeModelInspectorDialog(QWidget *parent)
Tab("&Defines",
Column {
Group {
- title("Toolchain Defines"),
+ title(QString("Toolchain Defines")),
Column { m_partToolchainDefinesEdit },
},
Group {
- title("Project Defines"),
+ title(QString("Project Defines")),
Column { m_partProjectDefinesEdit },
}
}
diff --git a/src/plugins/cppeditor/cppcodestylesettingspage.cpp b/src/plugins/cppeditor/cppcodestylesettingspage.cpp
index edcca95a34..2364dee814 100644
--- a/src/plugins/cppeditor/cppcodestylesettingspage.cpp
+++ b/src/plugins/cppeditor/cppcodestylesettingspage.cpp
@@ -246,10 +246,12 @@ public:
m_statementMacros->setToolTip(
Tr::tr("Macros that can be used as statements without a trailing semicolon."));
m_statementMacros->setSizePolicy(sizePolicy);
+ // clang-format off
const Group statementMacrosGroup {
- title(Tr::tr("Statement macros")),
+ title(Tr::tr("Statement Macros")),
Column { m_statementMacros}
};
+ // clang-format on
QObject::connect(m_statementMacros, &QPlainTextEdit::textChanged, q, [this] {
m_handlingStatementMacroChange = true;
q->slotCodeStyleSettingsChanged();
diff --git a/src/plugins/cppeditor/cppeditor.qbs b/src/plugins/cppeditor/cppeditor.qbs
index c535488de9..c563c76097 100644
--- a/src/plugins/cppeditor/cppeditor.qbs
+++ b/src/plugins/cppeditor/cppeditor.qbs
@@ -119,8 +119,6 @@ QtcPlugin {
"cppincludesfilter.h",
"cppindexingsupport.cpp",
"cppindexingsupport.h",
- "cppinsertvirtualmethods.cpp",
- "cppinsertvirtualmethods.h",
"cpplocalrenaming.cpp",
"cpplocalrenaming.h",
"cpplocalsymbols.cpp",
@@ -153,22 +151,6 @@ QtcPlugin {
"cppprojectinfogenerator.h",
"cppprojectupdater.cpp",
"cppprojectupdater.h",
- "cppquickfix.cpp",
- "cppquickfix.h",
- "cppquickfixassistant.cpp",
- "cppquickfixassistant.h",
- "cppquickfixes.cpp",
- "cppquickfixes.h",
- "cppquickfixprojectsettings.cpp",
- "cppquickfixprojectsettings.h",
- "cppquickfixprojectsettingswidget.cpp",
- "cppquickfixprojectsettingswidget.h",
- "cppquickfixsettings.cpp",
- "cppquickfixsettings.h",
- "cppquickfixsettingspage.cpp",
- "cppquickfixsettingspage.h",
- "cppquickfixsettingswidget.cpp",
- "cppquickfixsettingswidget.h",
"cppqtstyleindenter.cpp",
"cppqtstyleindenter.h",
"cpprefactoringchanges.cpp",
@@ -234,6 +216,79 @@ QtcPlugin {
]
Group {
+ name: "Quickfixes"
+ prefix: "quickfixes/"
+ files: [
+ "assigntolocalvariable.cpp",
+ "assigntolocalvariable.h",
+ "bringidentifierintoscope.cpp",
+ "bringidentifierintoscope.h",
+ "completeswitchstatement.cpp",
+ "completeswitchstatement.h",
+ "convertfromandtopointer.cpp",
+ "convertfromandtopointer.h",
+ "convertnumericliteral.cpp",
+ "convertnumericliteral.h",
+ "convertqt4connect.cpp",
+ "convertqt4connect.h",
+ "convertstringliteral.cpp",
+ "convertstringliteral.h",
+ "converttocamelcase.cpp",
+ "converttocamelcase.h",
+ "converttometamethodcall.cpp",
+ "converttometamethodcall.h",
+ "cppcodegenerationquickfixes.cpp",
+ "cppcodegenerationquickfixes.h",
+ "cppinsertvirtualmethods.cpp",
+ "cppinsertvirtualmethods.h",
+ "cppquickfix.cpp",
+ "cppquickfix.h",
+ "cppquickfixassistant.cpp",
+ "cppquickfixassistant.h",
+ "cppquickfixhelpers.cpp",
+ "cppquickfixhelpers.h",
+ "cppquickfixprojectsettings.cpp",
+ "cppquickfixprojectsettings.h",
+ "cppquickfixprojectsettingswidget.cpp",
+ "cppquickfixprojectsettingswidget.h",
+ "cppquickfixsettings.cpp",
+ "cppquickfixsettings.h",
+ "cppquickfixsettingspage.cpp",
+ "cppquickfixsettingspage.h",
+ "cppquickfixsettingswidget.cpp",
+ "cppquickfixsettingswidget.h",
+ "createdeclarationfromuse.cpp",
+ "createdeclarationfromuse.h",
+ "extractfunction.cpp",
+ "extractfunction.h",
+ "extractliteralasparameter.cpp",
+ "extractliteralasparameter.h",
+ "insertfunctiondefinition.cpp",
+ "insertfunctiondefinition.h",
+ "logicaloperationquickfixes.cpp",
+ "logicaloperationquickfixes.h",
+ "moveclasstoownfile.cpp",
+ "moveclasstoownfile.h",
+ "movefunctiondefinition.cpp",
+ "movefunctiondefinition.h",
+ "rearrangeparamdeclarationlist.cpp",
+ "rearrangeparamdeclarationlist.h",
+ "reformatpointerdeclaration.cpp",
+ "reformatpointerdeclaration.h",
+ "removeusingnamespace.cpp",
+ "removeusingnamespace.h",
+ "rewritecomment.cpp",
+ "rewritecomment.h",
+ "rewritecontrolstatements.cpp",
+ "rewritecontrolstatements.h",
+ "splitsimpledeclaration.cpp",
+ "splitsimpledeclaration.h",
+ "synchronizememberfunctionorder.cpp",
+ "synchronizememberfunctionorder.h",
+ ]
+ }
+
+ Group {
name: "TestCase"
condition: qtc.withPluginTests || qtc.withAutotests
files: [
@@ -244,6 +299,16 @@ QtcPlugin {
QtcTestFiles {
cpp.defines: outer.concat(['SRCDIR="' + FileInfo.path(filePath) + '"'])
+
+ Group {
+ name: "Quickfix tests"
+ prefix: "quickfixes/"
+ files: [
+ "cppquickfix_test.cpp",
+ "cppquickfix_test.h",
+ ]
+ }
+
files: [
"compileroptionsbuilder_test.cpp",
"compileroptionsbuilder_test.h",
@@ -263,8 +328,6 @@ QtcPlugin {
"cppmodelmanager_test.h",
"cpppointerdeclarationformatter_test.cpp",
"cpppointerdeclarationformatter_test.h",
- "cppquickfix_test.cpp",
- "cppquickfix_test.h",
"cpprenaming_test.cpp",
"cpprenaming_test.h",
"cppsourceprocessor_test.cpp",
diff --git a/src/plugins/cppeditor/cppeditor.qrc b/src/plugins/cppeditor/cppeditor.qrc
index e90b587dc2..3a5ace117d 100644
--- a/src/plugins/cppeditor/cppeditor.qrc
+++ b/src/plugins/cppeditor/cppeditor.qrc
@@ -41,5 +41,21 @@
<file>testcases/move-class/template/theheader.h</file>
<file>testcases/move-class/template/theheader.h_expected</file>
<file>testcases/move-class/complex/theclass.h_expected</file>
+ <file>testcases/reorder-member-impls/different-locations/different-locations.pro</file>
+ <file>testcases/reorder-member-impls/different-locations/header.h</file>
+ <file>testcases/reorder-member-impls/different-locations/header.h_expected</file>
+ <file>testcases/reorder-member-impls/different-locations/impl1.cpp</file>
+ <file>testcases/reorder-member-impls/different-locations/impl1.cpp_expected</file>
+ <file>testcases/reorder-member-impls/different-locations/impl2.cpp</file>
+ <file>testcases/reorder-member-impls/different-locations/impl2.cpp_expected</file>
+ <file>testcases/reorder-member-impls/already-sorted/already-sorted.pro</file>
+ <file>testcases/reorder-member-impls/already-sorted/header.h</file>
+ <file>testcases/reorder-member-impls/no-out-of-line/header.h</file>
+ <file>testcases/reorder-member-impls/no-out-of-line/no-out-of-line.pro</file>
+ <file>testcases/reorder-member-impls/already-sorted/header.h_expected</file>
+ <file>testcases/reorder-member-impls/no-out-of-line/header.h_expected</file>
+ <file>testcases/reorder-member-impls/templates/header.h</file>
+ <file>testcases/reorder-member-impls/templates/header.h_expected</file>
+ <file>testcases/reorder-member-impls/templates/templates.pro</file>
</qresource>
</RCC>
diff --git a/src/plugins/cppeditor/cppeditordocument.cpp b/src/plugins/cppeditor/cppeditordocument.cpp
index 6f2a67fede..1f5adc4baa 100644
--- a/src/plugins/cppeditor/cppeditordocument.cpp
+++ b/src/plugins/cppeditor/cppeditordocument.cpp
@@ -11,7 +11,7 @@
#include "cppeditorconstants.h"
#include "cppeditortr.h"
#include "cpphighlighter.h"
-#include "cppquickfixassistant.h"
+#include "quickfixes/cppquickfixassistant.h"
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/session.h>
diff --git a/src/plugins/cppeditor/cppeditorplugin.cpp b/src/plugins/cppeditor/cppeditorplugin.cpp
index 6718d4cbc8..63122a538b 100644
--- a/src/plugins/cppeditor/cppeditorplugin.cpp
+++ b/src/plugins/cppeditor/cppeditorplugin.cpp
@@ -16,12 +16,12 @@
#include "cppmodelmanager.h"
#include "cppoutline.h"
#include "cppprojectupdater.h"
-#include "cppquickfixes.h"
-#include "cppquickfixprojectsettingswidget.h"
-#include "cppquickfixsettingspage.h"
#include "cpptoolsreuse.h"
#include "cpptoolssettings.h"
#include "cpptypehierarchy.h"
+#include "quickfixes/cppquickfix.h"
+#include "quickfixes/cppquickfixprojectsettingswidget.h"
+#include "quickfixes/cppquickfixsettingspage.h"
#include "resourcepreviewhoverhandler.h"
#ifdef WITH_TESTS
@@ -31,12 +31,10 @@
#include "cppdoxygen_test.h"
#include "cpphighlighter.h"
#include "cppincludehierarchy_test.h"
-#include "cppinsertvirtualmethods.h"
#include "cpplocalsymbols_test.h"
#include "cpplocatorfilter_test.h"
#include "cppmodelmanager_test.h"
#include "cpppointerdeclarationformatter_test.h"
-#include "cppquickfix_test.h"
#include "cpprenaming_test.h"
#include "cppsourceprocessor_test.h"
#include "cppuseselections_test.h"
@@ -177,7 +175,7 @@ class CppEditorPlugin final : public ExtensionSystem::IPlugin
public:
~CppEditorPlugin() final
{
- destroyCppQuickFixes();
+ destroyCppQuickFixFactories();
delete d;
d = nullptr;
}
@@ -214,7 +212,7 @@ void CppEditorPlugin::initialize()
setupMenus();
registerVariables();
- createCppQuickFixes();
+ createCppQuickFixFactories();
registerTests();
SnippetProvider::registerGroup(Constants::CPP_SNIPPETS_GROUP_ID, Tr::tr("C++", "SnippetProvider"),
@@ -514,8 +512,6 @@ void CppEditorPlugin::registerTests()
addTest<Tests::FileAndTokenActionsTest>();
addTest<Tests::FollowSymbolTest>();
addTest<Tests::IncludeHierarchyTest>();
- addTest<Tests::InsertVirtualMethodsTest>();
- addTest<Tests::QuickfixTest>();
addTest<Tests::GlobalRenamingTest>();
addTest<Tests::SelectionsTest>();
#endif
diff --git a/src/plugins/cppeditor/cppeditorwidget.cpp b/src/plugins/cppeditor/cppeditorwidget.cpp
index 5882c8d164..d5d64597b9 100644
--- a/src/plugins/cppeditor/cppeditorwidget.cpp
+++ b/src/plugins/cppeditor/cppeditorwidget.cpp
@@ -14,11 +14,11 @@
#include "cpplocalrenaming.h"
#include "cppmodelmanager.h"
#include "cpppreprocessordialog.h"
-#include "cppquickfixassistant.h"
#include "cppselectionchanger.h"
#include "cppsemanticinfo.h"
#include "cppuseselectionsupdater.h"
#include "doxygengenerator.h"
+#include "quickfixes/cppquickfixassistant.h"
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
diff --git a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp
index e489045f35..e6722206df 100644
--- a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp
+++ b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp
@@ -8,8 +8,8 @@
#include "cppeditortr.h"
#include "cppeditorwidget.h"
#include "cpplocalsymbols.h"
-#include "cppquickfixassistant.h"
#include "cpptoolsreuse.h"
+#include "quickfixes/cppquickfixassistant.h"
#include "symbolfinder.h"
#include <coreplugin/actionmanager/actionmanager.h>
@@ -21,8 +21,6 @@
#include <cplusplus/Overview.h>
#include <cplusplus/TypeOfExpression.h>
-#include <extensionsystem/pluginmanager.h>
-
#include <texteditor/refactoroverlay.h>
#include <texteditor/texteditorconstants.h>
@@ -239,7 +237,7 @@ void FunctionDeclDefLinkFinder::startFindLinkAt(
m_watcher.reset(new QFutureWatcher<std::shared_ptr<FunctionDeclDefLink> >());
connect(m_watcher.get(), &QFutureWatcherBase::finished, this, &FunctionDeclDefLinkFinder::onFutureDone);
m_watcher->setFuture(Utils::asyncRun(findLinkHelper, result, refactoringChanges));
- ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(m_watcher->future());
+ Utils::futureSynchronizer()->addFuture(m_watcher->future());
}
bool FunctionDeclDefLink::isValid() const
@@ -269,13 +267,11 @@ void FunctionDeclDefLink::apply(CppEditorWidget *editor, bool jumpToMatch)
const int targetStart = newTargetFile->position(targetLine, targetColumn);
const int targetEnd = targetStart + targetInitial.size();
if (targetInitial == newTargetFile->textOf(targetStart, targetEnd)) {
- const ChangeSet changeset = changes(snapshot, targetStart);
- newTargetFile->setChangeSet(changeset);
if (jumpToMatch) {
const int jumpTarget = newTargetFile->position(targetFunction->line(), targetFunction->column());
newTargetFile->setOpenEditor(true, jumpTarget);
}
- newTargetFile->apply();
+ newTargetFile->apply(changes(snapshot, targetStart));
} else {
ToolTip::show(editor->toolTipPosition(linkSelection),
Tr::tr("Target file was changed, could not apply changes"));
diff --git a/src/plugins/cppeditor/cpplocatorfilter.cpp b/src/plugins/cppeditor/cpplocatorfilter.cpp
index 8fab8f4dcd..7526d3dc9f 100644
--- a/src/plugins/cppeditor/cpplocatorfilter.cpp
+++ b/src/plugins/cppeditor/cpplocatorfilter.cpp
@@ -11,8 +11,6 @@
#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>
@@ -108,7 +106,6 @@ LocatorMatcherTask locatorMatcher(IndexItem::ItemType type, const EntryFromIndex
Storage<LocatorStorage> storage;
const auto onSetup = [=](Async<void> &async) {
- async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
async.setConcurrentCallData(matchesFor, *storage, type, converter);
};
return {AsyncTask<void>(onSetup), storage};
@@ -306,7 +303,6 @@ LocatorMatcherTask currentDocumentMatcher()
Storage<LocatorStorage> storage;
const auto onSetup = [=](Async<void> &async) {
- async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
async.setConcurrentCallData(matchesForCurrentDocument, *storage, currentFileName());
};
return {AsyncTask<void>(onSetup), storage};
diff --git a/src/plugins/cppeditor/cppmodelmanager.cpp b/src/plugins/cppeditor/cppmodelmanager.cpp
index fbb727eb72..6f2aebcca3 100644
--- a/src/plugins/cppeditor/cppmodelmanager.cpp
+++ b/src/plugins/cppeditor/cppmodelmanager.cpp
@@ -1330,11 +1330,8 @@ static QSet<QString> filteredFilesRemoved(const QSet<QString> &files,
for (const QRegularExpression &rx: std::as_const(regexes)) {
QRegularExpressionMatch match = rx.match(filePath.absoluteFilePath().path());
if (match.hasMatch()) {
- const QString msg = Tr::tr("C++ Indexer: Skipping file \"%1\" "
- "because its path matches the ignore pattern.")
- .arg(filePath.displayName());
- QMetaObject::invokeMethod(MessageManager::instance(),
- [msg] { MessageManager::writeSilently(msg); });
+ MessageManager::writeSilently(Tr::tr("C++ Indexer: Skipping file \"%1\" "
+ "because its path matches the ignore pattern.").arg(filePath.displayName()));
skip = true;
break;
}
@@ -2013,8 +2010,7 @@ void CppModelManager::renameIncludes(const QList<std::pair<FilePath, FilePath>>
newString);
}
}
- file->setChangeSet(changeSet);
- file->apply();
+ file->apply(changeSet);
}
}
diff --git a/src/plugins/cppeditor/cppoutlinemodel.cpp b/src/plugins/cppeditor/cppoutlinemodel.cpp
index c03236f274..6cd11c6df5 100644
--- a/src/plugins/cppeditor/cppoutlinemodel.cpp
+++ b/src/plugins/cppeditor/cppoutlinemodel.cpp
@@ -117,7 +117,7 @@ public:
return false;
};
if (isFwdDecl())
- return Utils::creatorTheme()->color(Utils::Theme::TextColorDisabled);
+ return Utils::creatorColor(Utils::Theme::TextColorDisabled);
return TreeItem::data(column, role);
}
diff --git a/src/plugins/cppeditor/cppquickfix.cpp b/src/plugins/cppeditor/cppquickfix.cpp
deleted file mode 100644
index 0741c842ba..0000000000
--- a/src/plugins/cppeditor/cppquickfix.cpp
+++ /dev/null
@@ -1,26 +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 "cppquickfix.h"
-
-#include "cppquickfixassistant.h"
-#include "cpprefactoringchanges.h"
-
-using namespace CPlusPlus;
-using namespace TextEditor;
-
-namespace CppEditor::Internal {
-
-const QStringList magicQObjectFunctions()
-{
- static QStringList list{"metaObject", "qt_metacast", "qt_metacall", "qt_static_metacall"};
- return list;
-}
-
-CppQuickFixOperation::CppQuickFixOperation(const CppQuickFixInterface &interface, int priority)
- : QuickFixOperation(priority), CppQuickFixInterface(interface)
-{}
-
-CppQuickFixOperation::~CppQuickFixOperation() = default;
-
-} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/cppquickfix_test.cpp b/src/plugins/cppeditor/cppquickfix_test.cpp
deleted file mode 100644
index 781febee6f..0000000000
--- a/src/plugins/cppeditor/cppquickfix_test.cpp
+++ /dev/null
@@ -1,10051 +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 "cppquickfix_test.h"
-
-#include "cppcodestylepreferences.h"
-#include "cppeditorwidget.h"
-#include "cppmodelmanager.h"
-#include "cppquickfixassistant.h"
-#include "cppquickfixes.h"
-#include "cppquickfixsettings.h"
-#include "cppsourceprocessertesthelper.h"
-#include "cpptoolssettings.h"
-
-#include <projectexplorer/kitmanager.h>
-#include <projectexplorer/projectexplorer.h>
-#include <texteditor/textdocument.h>
-#include <utils/fileutils.h>
-
-#include <QDebug>
-#include <QDir>
-#include <QtTest>
-
-/*!
- Tests for quick-fixes.
- */
-using namespace Core;
-using namespace CPlusPlus;
-using namespace ProjectExplorer;
-using namespace TextEditor;
-using namespace Utils;
-
-using CppEditor::Tests::TemporaryDir;
-using CppEditor::Tests::Internal::TestIncludePaths;
-
-typedef QByteArray _;
-
-namespace CppEditor::Internal::Tests {
-typedef QList<TestDocumentPtr> QuickFixTestDocuments;
-}
-Q_DECLARE_METATYPE(CppEditor::Internal::Tests::QuickFixTestDocuments)
-
-namespace CppEditor {
-namespace Internal {
-namespace Tests {
-
-/// Tests the offered operations provided by a given CppQuickFixFactory
-class QuickFixOfferedOperationsTest : public BaseQuickFixTestCase
-{
-public:
- QuickFixOfferedOperationsTest(const QList<TestDocumentPtr> &testDocuments,
- CppQuickFixFactory *factory,
- const ProjectExplorer::HeaderPaths &headerPaths
- = ProjectExplorer::HeaderPaths(),
- const QStringList &expectedOperations = QStringList());
-};
-
-QList<TestDocumentPtr> singleDocument(const QByteArray &original,
- const QByteArray &expected)
-{
- return {CppTestDocument::create("file.cpp", original, expected)};
-}
-
-BaseQuickFixTestCase::BaseQuickFixTestCase(const QList<TestDocumentPtr> &testDocuments,
- const ProjectExplorer::HeaderPaths &headerPaths,
- const QByteArray &clangFormatSettings)
- : m_testDocuments(testDocuments)
- , m_cppCodeStylePreferences(0)
- , m_restoreHeaderPaths(false)
-{
- QVERIFY(succeededSoFar());
- m_succeededSoFar = false;
-
- // Check if there is exactly one cursor marker
- unsigned cursorMarkersCount = 0;
- for (const TestDocumentPtr &document : std::as_const(m_testDocuments)) {
- if (document->hasCursorMarker())
- ++cursorMarkersCount;
- }
- QVERIFY2(cursorMarkersCount == 1, "Exactly one cursor marker is allowed.");
-
- // Write documents to disk
- m_temporaryDirectory.reset(new TemporaryDir);
- QVERIFY(m_temporaryDirectory->isValid());
- for (const TestDocumentPtr &document : std::as_const(m_testDocuments)) {
- if (QFileInfo(document->m_fileName).isRelative())
- document->setBaseDirectory(m_temporaryDirectory->path());
- document->writeToDisk();
- }
-
- // Create .clang-format file
- if (!clangFormatSettings.isEmpty())
- m_temporaryDirectory->createFile(".clang-format", clangFormatSettings);
-
- // Set appropriate include paths
- if (!headerPaths.isEmpty()) {
- m_restoreHeaderPaths = true;
- m_headerPathsToRestore = CppModelManager::headerPaths();
- CppModelManager::setHeaderPaths(headerPaths);
- }
-
- // Update Code Model
- QSet<FilePath> filePaths;
- for (const TestDocumentPtr &document : std::as_const(m_testDocuments))
- filePaths << document->filePath();
- QVERIFY(parseFiles(filePaths));
-
- // Open Files
- for (const TestDocumentPtr &document : std::as_const(m_testDocuments)) {
- QVERIFY(openCppEditor(document->filePath(), &document->m_editor,
- &document->m_editorWidget));
- closeEditorAtEndOfTestCase(document->m_editor);
-
- // Set cursor position
- if (document->hasCursorMarker()) {
- if (document->hasAnchorMarker()) {
- document->m_editor->setCursorPosition(document->m_anchorPosition);
- document->m_editor->select(document->m_cursorPosition);
- } else {
- document->m_editor->setCursorPosition(document->m_cursorPosition);
- }
- } else {
- document->m_editor->setCursorPosition(0);
- }
-
- // Rehighlight
- waitForRehighlightedSemanticDocument(document->m_editorWidget);
- }
-
- // Enforce the default cpp code style, so we are independent of config file settings.
- // This is needed by e.g. the GenerateGetterSetter quick fix.
- m_cppCodeStylePreferences = CppToolsSettings::cppCodeStyle();
- QVERIFY(m_cppCodeStylePreferences);
- m_cppCodeStylePreferencesOriginalDelegateId = m_cppCodeStylePreferences->currentDelegateId();
- m_cppCodeStylePreferences->setCurrentDelegate("qt");
-
- // Find the document having the cursor marker
- for (const TestDocumentPtr &document : std::as_const(m_testDocuments)) {
- if (document->hasCursorMarker()){
- m_documentWithMarker = document;
- break;
- }
- }
-
- QVERIFY(m_documentWithMarker);
- m_succeededSoFar = true;
-}
-
-BaseQuickFixTestCase::~BaseQuickFixTestCase()
-{
- // Restore default cpp code style
- if (m_cppCodeStylePreferences)
- m_cppCodeStylePreferences->setCurrentDelegate(m_cppCodeStylePreferencesOriginalDelegateId);
-
- // Restore include paths
- if (m_restoreHeaderPaths)
- CppModelManager::setHeaderPaths(m_headerPathsToRestore);
-
- // Remove created files from file system
- for (const TestDocumentPtr &testDocument : std::as_const(m_testDocuments))
- QVERIFY(testDocument->filePath().removeFile());
-}
-
-/// Leading whitespace is not removed, so we can check if the indetation ranges
-/// have been set correctly by the quick-fix.
-static QString &removeTrailingWhitespace(QString &input)
-{
- const QStringList lines = input.split(QLatin1Char('\n'));
- input.resize(0);
- for (int i = 0, total = lines.size(); i < total; ++i) {
- QString line = lines.at(i);
- while (line.length() > 0) {
- QChar lastChar = line[line.length() - 1];
- if (lastChar == QLatin1Char(' ') || lastChar == QLatin1Char('\t'))
- line.chop(1);
- else
- break;
- }
- input.append(line);
-
- const bool isLastLine = i == lines.size() - 1;
- if (!isLastLine)
- input.append(QLatin1Char('\n'));
- }
- return input;
-}
-
-QuickFixOperationTest::QuickFixOperationTest(const QList<TestDocumentPtr> &testDocuments,
- CppQuickFixFactory *factory,
- const ProjectExplorer::HeaderPaths &headerPaths,
- int operationIndex,
- const QByteArray &expectedFailMessage,
- const QByteArray &clangFormatSettings)
- : BaseQuickFixTestCase(testDocuments, headerPaths, clangFormatSettings)
-{
- if (factory->clangdReplacement() && CppModelManager::isClangCodeModelActive())
- return;
-
- QVERIFY(succeededSoFar());
-
- // Perform operation if there is one
- CppQuickFixInterface quickFixInterface(m_documentWithMarker->m_editorWidget, ExplicitlyInvoked);
- QuickFixOperations operations;
- factory->match(quickFixInterface, operations);
- if (operations.isEmpty()) {
- QEXPECT_FAIL("CompleteSwitchCaseStatement_QTCREATORBUG-25998", "FIXME", Abort);
- QVERIFY(testDocuments.first()->m_expectedSource.isEmpty());
- return;
- }
-
- QVERIFY(operationIndex < operations.size());
- const QuickFixOperation::Ptr operation = operations.at(operationIndex);
- operation->perform();
-
- // Compare all files
- for (const TestDocumentPtr &testDocument : std::as_const(m_testDocuments)) {
- // Check
- QString result = testDocument->m_editorWidget->document()->toPlainText();
- removeTrailingWhitespace(result);
- QEXPECT_FAIL("escape string literal: raw string literal", "FIXME", Continue);
- QEXPECT_FAIL("escape string literal: unescape adjacent literals", "FIXME", Continue);
- if (!expectedFailMessage.isEmpty())
- QEXPECT_FAIL("", expectedFailMessage.data(), Continue);
- else if (result != testDocument->m_expectedSource) {
- qDebug() << "---" << testDocument->m_expectedSource;
- qDebug() << "+++" << result;
- }
- QCOMPARE(result, testDocument->m_expectedSource);
-
- // Undo the change
- for (int i = 0; i < 100; ++i)
- testDocument->m_editorWidget->undo();
- result = testDocument->m_editorWidget->document()->toPlainText();
- QCOMPARE(result, testDocument->m_source);
- }
-}
-
-void QuickFixOperationTest::run(const QList<TestDocumentPtr> &testDocuments,
- CppQuickFixFactory *factory,
- const QString &headerPath,
- int operationIndex)
-{
- ProjectExplorer::HeaderPaths headerPaths;
- headerPaths.push_back(ProjectExplorer::HeaderPath::makeUser(headerPath));
- QuickFixOperationTest(testDocuments, factory, headerPaths, operationIndex);
-}
-
-QuickFixOfferedOperationsTest::QuickFixOfferedOperationsTest(
- const QList<TestDocumentPtr> &testDocuments,
- CppQuickFixFactory *factory,
- const ProjectExplorer::HeaderPaths &headerPaths,
- const QStringList &expectedOperations)
- : BaseQuickFixTestCase(testDocuments, headerPaths)
-{
- // Get operations
- CppQuickFixInterface quickFixInterface(m_documentWithMarker->m_editorWidget, ExplicitlyInvoked);
- QuickFixOperations actualOperations;
- factory->match(quickFixInterface, actualOperations);
-
- // Convert to QStringList
- QStringList actualOperationsAsStringList;
- for (const QuickFixOperation::Ptr &operation : std::as_const(actualOperations))
- actualOperationsAsStringList << operation->description();
-
- QCOMPARE(actualOperationsAsStringList, expectedOperations);
-}
-
-/// Delegates directly to AddIncludeForUndefinedIdentifierOp for easier testing.
-class AddIncludeForUndefinedIdentifierTestFactory : public CppQuickFixFactory
-{
-public:
- AddIncludeForUndefinedIdentifierTestFactory(const QString &include)
- : m_include(include) {}
-
- void doMatch(const CppQuickFixInterface &cppQuickFixInterface, QuickFixOperations &result) override
- {
- result << new AddIncludeForUndefinedIdentifierOp(cppQuickFixInterface, 0, m_include);
- }
-
-private:
- const QString m_include;
-};
-
-class AddForwardDeclForUndefinedIdentifierTestFactory : public CppQuickFixFactory
-{
-public:
- AddForwardDeclForUndefinedIdentifierTestFactory(const QString &className, int symbolPos)
- : m_className(className), m_symbolPos(symbolPos) {}
-
- void doMatch(const CppQuickFixInterface &cppQuickFixInterface, QuickFixOperations &result) override
- {
- result << new AddForwardDeclForUndefinedIdentifierOp(cppQuickFixInterface, 0,
- m_className, m_symbolPos);
- }
-
-private:
- const QString m_className;
- const int m_symbolPos;
-};
-
-} // namespace Tests
-} // namespace Internal
-
-typedef QSharedPointer<CppQuickFixFactory> CppQuickFixFactoryPtr;
-
-} // namespace CppEditor
-
-namespace CppEditor::Internal::Tests {
-
-class QuickFixSettings
-{
- const CppQuickFixSettings original = *CppQuickFixSettings::instance();
-
-public:
- CppQuickFixSettings *operator->() { return CppQuickFixSettings::instance(); }
- ~QuickFixSettings() { *CppQuickFixSettings::instance() = original; }
-};
-
-void QuickfixTest::testGeneric_data()
-{
- QTest::addColumn<CppQuickFixFactoryPtr>("factory");
- QTest::addColumn<QByteArray>("original");
- QTest::addColumn<QByteArray>("expected");
-
- // Checks: All enum values are added as case statements for a blank switch.
- QTest::newRow("CompleteSwitchCaseStatement_basic1")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "enum EnumType { V1, V2 };\n"
- "\n"
- "void f()\n"
- "{\n"
- " EnumType t;\n"
- " @switch (t) {\n"
- " }\n"
- "}\n"
- ) << _(
- "enum EnumType { V1, V2 };\n"
- "\n"
- "void f()\n"
- "{\n"
- " EnumType t;\n"
- " switch (t) {\n"
- " case V1:\n"
- " break;\n"
- " case V2:\n"
- " break;\n"
- " }\n"
- "}\n"
- );
-
- // Same as above for enum class.
- QTest::newRow("CompleteSwitchCaseStatement_basic1_enum class")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "enum class EnumType { V1, V2 };\n"
- "\n"
- "void f()\n"
- "{\n"
- " EnumType t;\n"
- " @switch (t) {\n"
- " }\n"
- "}\n"
- ) << _(
- "enum class EnumType { V1, V2 };\n"
- "\n"
- "void f()\n"
- "{\n"
- " EnumType t;\n"
- " switch (t) {\n"
- " case EnumType::V1:\n"
- " break;\n"
- " case EnumType::V2:\n"
- " break;\n"
- " }\n"
- "}\n"
- );
-
- // Same as above with the cursor somewhere in the body.
- QTest::newRow("CompleteSwitchCaseStatement_basic1_enum class, cursor in the body")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "enum class EnumType { V1, V2 };\n"
- "\n"
- "void f()\n"
- "{\n"
- " EnumType t;\n"
- " switch (t) {\n"
- " @}\n"
- "}\n"
- ) << _(
- "enum class EnumType { V1, V2 };\n"
- "\n"
- "void f()\n"
- "{\n"
- " EnumType t;\n"
- " switch (t) {\n"
- " case EnumType::V1:\n"
- " break;\n"
- " case EnumType::V2:\n"
- " break;\n"
- " }\n"
- "}\n"
- );
-
- // Checks: All enum values are added as case statements for a blank switch when
- // the variable is declared alongside the enum definition.
- QTest::newRow("CompleteSwitchCaseStatement_basic1_enum_with_declaration")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "enum EnumType { V1, V2 } t;\n"
- "\n"
- "void f()\n"
- "{\n"
- " @switch (t) {\n"
- " }\n"
- "}\n"
- ) << _(
- "enum EnumType { V1, V2 } t;\n"
- "\n"
- "void f()\n"
- "{\n"
- " switch (t) {\n"
- " case V1:\n"
- " break;\n"
- " case V2:\n"
- " break;\n"
- " }\n"
- "}\n"
- );
-
- // Same as above for enum class.
- QTest::newRow("CompleteSwitchCaseStatement_basic1_enum_with_declaration_enumClass")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "enum class EnumType { V1, V2 } t;\n"
- "\n"
- "void f()\n"
- "{\n"
- " @switch (t) {\n"
- " }\n"
- "}\n"
- ) << _(
- "enum class EnumType { V1, V2 } t;\n"
- "\n"
- "void f()\n"
- "{\n"
- " switch (t) {\n"
- " case EnumType::V1:\n"
- " break;\n"
- " case EnumType::V2:\n"
- " break;\n"
- " }\n"
- "}\n"
- );
-
- // Checks: All enum values are added as case statements for a blank switch
- // for anonymous enums.
- QTest::newRow("CompleteSwitchCaseStatement_basic1_anonymous_enum")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "enum { V1, V2 } t;\n"
- "\n"
- "void f()\n"
- "{\n"
- " @switch (t) {\n"
- " }\n"
- "}\n"
- ) << _(
- "enum { V1, V2 } t;\n"
- "\n"
- "void f()\n"
- "{\n"
- " switch (t) {\n"
- " case V1:\n"
- " break;\n"
- " case V2:\n"
- " break;\n"
- " }\n"
- "}\n"
- );
-
- // Checks: All enum values are added as case statements for a blank switch with a default case.
- QTest::newRow("CompleteSwitchCaseStatement_basic2")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "enum EnumType { V1, V2 };\n"
- "\n"
- "void f()\n"
- "{\n"
- " EnumType t;\n"
- " @switch (t) {\n"
- " default:\n"
- " break;\n"
- " }\n"
- "}\n"
- ) << _(
- "enum EnumType { V1, V2 };\n"
- "\n"
- "void f()\n"
- "{\n"
- " EnumType t;\n"
- " switch (t) {\n"
- " case V1:\n"
- " break;\n"
- " case V2:\n"
- " break;\n"
- " default:\n"
- " break;\n"
- " }\n"
- "}\n"
- );
-
- // Same as above for enum class.
- QTest::newRow("CompleteSwitchCaseStatement_basic2_enumClass")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "enum class EnumType { V1, V2 };\n"
- "\n"
- "void f()\n"
- "{\n"
- " EnumType t;\n"
- " @switch (t) {\n"
- " default:\n"
- " break;\n"
- " }\n"
- "}\n"
- ) << _(
- "enum class EnumType { V1, V2 };\n"
- "\n"
- "void f()\n"
- "{\n"
- " EnumType t;\n"
- " switch (t) {\n"
- " case EnumType::V1:\n"
- " break;\n"
- " case EnumType::V2:\n"
- " break;\n"
- " default:\n"
- " break;\n"
- " }\n"
- "}\n"
- );
-
- // Checks: Enum type in class is found.
- QTest::newRow("CompleteSwitchCaseStatement_enumTypeInClass")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "struct C { enum EnumType { V1, V2 }; };\n"
- "\n"
- "void f(C::EnumType t) {\n"
- " @switch (t) {\n"
- " }\n"
- "}\n"
- ) << _(
- "struct C { enum EnumType { V1, V2 }; };\n"
- "\n"
- "void f(C::EnumType t) {\n"
- " switch (t) {\n"
- " case C::V1:\n"
- " break;\n"
- " case C::V2:\n"
- " break;\n"
- " }\n"
- "}\n"
- );
-
- // Same as above for enum class.
- QTest::newRow("CompleteSwitchCaseStatement_enumClassInClass")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "struct C { enum class EnumType { V1, V2 }; };\n"
- "\n"
- "void f(C::EnumType t) {\n"
- " @switch (t) {\n"
- " }\n"
- "}\n"
- ) << _(
- "struct C { enum class EnumType { V1, V2 }; };\n"
- "\n"
- "void f(C::EnumType t) {\n"
- " switch (t) {\n"
- " case C::EnumType::V1:\n"
- " break;\n"
- " case C::EnumType::V2:\n"
- " break;\n"
- " }\n"
- "}\n"
- );
-
- // Checks: Enum type in namespace is found.
- QTest::newRow("CompleteSwitchCaseStatement_enumTypeInNamespace")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "namespace N { enum EnumType { V1, V2 }; };\n"
- "\n"
- "void f(N::EnumType t) {\n"
- " @switch (t) {\n"
- " }\n"
- "}\n"
- ) << _(
- "namespace N { enum EnumType { V1, V2 }; };\n"
- "\n"
- "void f(N::EnumType t) {\n"
- " switch (t) {\n"
- " case N::V1:\n"
- " break;\n"
- " case N::V2:\n"
- " break;\n"
- " }\n"
- "}\n"
- );
-
- // Same as above for enum class.
- QTest::newRow("CompleteSwitchCaseStatement_enumClassInNamespace")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "namespace N { enum class EnumType { V1, V2 }; };\n"
- "\n"
- "void f(N::EnumType t) {\n"
- " @switch (t) {\n"
- " }\n"
- "}\n"
- ) << _(
- "namespace N { enum class EnumType { V1, V2 }; };\n"
- "\n"
- "void f(N::EnumType t) {\n"
- " switch (t) {\n"
- " case N::EnumType::V1:\n"
- " break;\n"
- " case N::EnumType::V2:\n"
- " break;\n"
- " }\n"
- "}\n"
- );
-
- // Checks: The missing enum value is added.
- QTest::newRow("CompleteSwitchCaseStatement_oneValueMissing")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "enum EnumType { V1, V2 };\n"
- "\n"
- "void f()\n"
- "{\n"
- " EnumType t;\n"
- " @switch (t) {\n"
- " case V2:\n"
- " break;\n"
- " default:\n"
- " break;\n"
- " }\n"
- "}\n"
- ) << _(
- "enum EnumType { V1, V2 };\n"
- "\n"
- "void f()\n"
- "{\n"
- " EnumType t;\n"
- " switch (t) {\n"
- " case V1:\n"
- " break;\n"
- " case V2:\n"
- " break;\n"
- " default:\n"
- " break;\n"
- " }\n"
- "}\n"
- );
-
- // Checks: Same as above for enum class.
- QTest::newRow("CompleteSwitchCaseStatement_oneValueMissing_enumClass")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "enum class EnumType { V1, V2 };\n"
- "\n"
- "void f()\n"
- "{\n"
- " EnumType t;\n"
- " @switch (t) {\n"
- " case EnumType::V2:\n"
- " break;\n"
- " default:\n"
- " break;\n"
- " }\n"
- "}\n"
- ) << _(
- "enum class EnumType { V1, V2 };\n"
- "\n"
- "void f()\n"
- "{\n"
- " EnumType t;\n"
- " switch (t) {\n"
- " case EnumType::V1:\n"
- " break;\n"
- " case EnumType::V2:\n"
- " break;\n"
- " default:\n"
- " break;\n"
- " }\n"
- "}\n"
- );
-
- // Checks: Find the correct enum type despite there being a declaration with the same name.
- QTest::newRow("CompleteSwitchCaseStatement_QTCREATORBUG10366_1")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "enum test { TEST_1, TEST_2 };\n"
- "\n"
- "void f() {\n"
- " enum test test;\n"
- " @switch (test) {\n"
- " }\n"
- "}\n"
- ) << _(
- "enum test { TEST_1, TEST_2 };\n"
- "\n"
- "void f() {\n"
- " enum test test;\n"
- " switch (test) {\n"
- " case TEST_1:\n"
- " break;\n"
- " case TEST_2:\n"
- " break;\n"
- " }\n"
- "}\n"
- );
-
- // Same as above for enum class.
- QTest::newRow("CompleteSwitchCaseStatement_QTCREATORBUG10366_1_enumClass")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "enum class test { TEST_1, TEST_2 };\n"
- "\n"
- "void f() {\n"
- " enum test test;\n"
- " @switch (test) {\n"
- " }\n"
- "}\n"
- ) << _(
- "enum class test { TEST_1, TEST_2 };\n"
- "\n"
- "void f() {\n"
- " enum test test;\n"
- " switch (test) {\n"
- " case test::TEST_1:\n"
- " break;\n"
- " case test::TEST_2:\n"
- " break;\n"
- " }\n"
- "}\n"
- );
-
- // Checks: Find the correct enum type despite there being a declaration with the same name.
- QTest::newRow("CompleteSwitchCaseStatement_QTCREATORBUG10366_2")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "enum test1 { Wrong11, Wrong12 };\n"
- "enum test { Right1, Right2 };\n"
- "enum test2 { Wrong21, Wrong22 };\n"
- "\n"
- "int main() {\n"
- " enum test test;\n"
- " @switch (test) {\n"
- " }\n"
- "}\n"
- ) << _(
- "enum test1 { Wrong11, Wrong12 };\n"
- "enum test { Right1, Right2 };\n"
- "enum test2 { Wrong21, Wrong22 };\n"
- "\n"
- "int main() {\n"
- " enum test test;\n"
- " switch (test) {\n"
- " case Right1:\n"
- " break;\n"
- " case Right2:\n"
- " break;\n"
- " }\n"
- "}\n"
- );
-
- // Same as above for enum class.
- QTest::newRow("CompleteSwitchCaseStatement_QTCREATORBUG10366_2_enumClass")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "enum class test1 { Wrong11, Wrong12 };\n"
- "enum class test { Right1, Right2 };\n"
- "enum class test2 { Wrong21, Wrong22 };\n"
- "\n"
- "int main() {\n"
- " enum test test;\n"
- " @switch (test) {\n"
- " }\n"
- "}\n"
- ) << _(
- "enum class test1 { Wrong11, Wrong12 };\n"
- "enum class test { Right1, Right2 };\n"
- "enum class test2 { Wrong21, Wrong22 };\n"
- "\n"
- "int main() {\n"
- " enum test test;\n"
- " switch (test) {\n"
- " case test::Right1:\n"
- " break;\n"
- " case test::Right2:\n"
- " break;\n"
- " }\n"
- "}\n"
- );
-
- // Checks: Do not crash on incomplete case statetement.
- QTest::newRow("CompleteSwitchCaseStatement_doNotCrashOnIncompleteCase")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "enum E {};\n"
- "void f(E o)\n"
- "{\n"
- " @switch (o)\n"
- " {\n"
- " case\n"
- " }\n"
- "}\n"
- ) << _(
- ""
- );
-
- // Same as above for enum class.
- QTest::newRow("CompleteSwitchCaseStatement_doNotCrashOnIncompleteCase_enumClass")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "enum class E {};\n"
- "void f(E o)\n"
- "{\n"
- " @switch (o)\n"
- " {\n"
- " case\n"
- " }\n"
- "}\n"
- ) << _(
- ""
- );
-
- // Checks: complete switch statement where enum is goes via a template type parameter
- QTest::newRow("CompleteSwitchCaseStatement_QTCREATORBUG-24752")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "enum E {EA, EB};\n"
- "template<typename T> struct S {\n"
- " static T theType() { return T(); }\n"
- "};\n"
- "int main() {\n"
- " @switch (S<E>::theType()) {\n"
- " }\n"
- "}\n"
- ) << _(
- "enum E {EA, EB};\n"
- "template<typename T> struct S {\n"
- " static T theType() { return T(); }\n"
- "};\n"
- "int main() {\n"
- " switch (S<E>::theType()) {\n"
- " case EA:\n"
- " break;\n"
- " case EB:\n"
- " break;\n"
- " }\n"
- "}\n"
- );
-
- // Same as above for enum class.
- QTest::newRow("CompleteSwitchCaseStatement_QTCREATORBUG-24752_enumClass")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "enum class E {A, B};\n"
- "template<typename T> struct S {\n"
- " static T theType() { return T(); }\n"
- "};\n"
- "int main() {\n"
- " @switch (S<E>::theType()) {\n"
- " }\n"
- "}\n"
- ) << _(
- "enum class E {A, B};\n"
- "template<typename T> struct S {\n"
- " static T theType() { return T(); }\n"
- "};\n"
- "int main() {\n"
- " switch (S<E>::theType()) {\n"
- " case E::A:\n"
- " break;\n"
- " case E::B:\n"
- " break;\n"
- " }\n"
- "}\n"
- );
-
- // Checks: Complete switch statement where enum is return type of a template function
- // which is outside the scope of the return value.
- // TODO: Type minimization.
- QTest::newRow("CompleteSwitchCaseStatement_QTCREATORBUG-25998")
- << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
- "template <typename T> T enumCast(int value) { return static_cast<T>(value); }\n"
- "class Test {\n"
- " enum class E { V1, V2 };"
- " void func(int i) {\n"
- " @switch (enumCast<E>(i)) {\n"
- " }\n"
- " }\n"
- "};\n"
- ) << _(
- "template <typename T> T enumCast(int value) { return static_cast<T>(value); }\n"
- "class Test {\n"
- " enum class E { V1, V2 };"
- " void func(int i) {\n"
- " switch (enumCast<E>(i)) {\n"
- " case Test::E::V1:\n"
- " break;\n"
- " case Test::E::V2:\n"
- " break;\n"
- " }\n"
- " }\n"
- "};\n"
- );
-
- // Checks: No special treatment for reference to non const.
-
- // Check: Quick fix is not triggered on a member function.
- QTest::newRow("GenerateGetterSetter_notTriggeringOnMemberFunction")
- << CppQuickFixFactoryPtr(new GenerateGetterSetter)
- << _("class Something { void @f(); };\n") << _();
-
- // Check: Quick fix is not triggered on an member array;
- QTest::newRow("GenerateGetterSetter_notTriggeringOnMemberArray")
- << CppQuickFixFactoryPtr(new GenerateGetterSetter)
- << _("class Something { void @a[10]; };\n") << _();
-
- QTest::newRow("MoveDeclarationOutOfIf_ifOnly")
- << CppQuickFixFactoryPtr(new MoveDeclarationOutOfIf) << _(
- "void f()\n"
- "{\n"
- " if (Foo *@foo = g())\n"
- " h();\n"
- "}\n"
- ) << _(
- "void f()\n"
- "{\n"
- " Foo *foo = g();\n"
- " if (foo)\n"
- " h();\n"
- "}\n"
- );
-
- QTest::newRow("MoveDeclarationOutOfIf_ifElse")
- << CppQuickFixFactoryPtr(new MoveDeclarationOutOfIf) << _(
- "void f()\n"
- "{\n"
- " if (Foo *@foo = g())\n"
- " h();\n"
- " else\n"
- " i();\n"
- "}\n"
- ) << _(
- "void f()\n"
- "{\n"
- " Foo *foo = g();\n"
- " if (foo)\n"
- " h();\n"
- " else\n"
- " i();\n"
- "}\n"
- );
-
- QTest::newRow("MoveDeclarationOutOfIf_ifElseIf")
- << CppQuickFixFactoryPtr(new MoveDeclarationOutOfIf) << _(
- "void f()\n"
- "{\n"
- " if (Foo *foo = g()) {\n"
- " if (Bar *@bar = x()) {\n"
- " h();\n"
- " j();\n"
- " }\n"
- " } else {\n"
- " i();\n"
- " }\n"
- "}\n"
- ) << _(
- "void f()\n"
- "{\n"
- " if (Foo *foo = g()) {\n"
- " Bar *bar = x();\n"
- " if (bar) {\n"
- " h();\n"
- " j();\n"
- " }\n"
- " } else {\n"
- " i();\n"
- " }\n"
- "}\n"
- );
-
- QTest::newRow("MoveDeclarationOutOfWhile_singleWhile")
- << CppQuickFixFactoryPtr(new MoveDeclarationOutOfWhile) << _(
- "void f()\n"
- "{\n"
- " while (Foo *@foo = g())\n"
- " j();\n"
- "}\n"
- ) << _(
- "void f()\n"
- "{\n"
- " Foo *foo;\n"
- " while ((foo = g()) != 0)\n"
- " j();\n"
- "}\n"
- );
-
- QTest::newRow("MoveDeclarationOutOfWhile_whileInWhile")
- << CppQuickFixFactoryPtr(new MoveDeclarationOutOfWhile) << _(
- "void f()\n"
- "{\n"
- " while (Foo *foo = g()) {\n"
- " while (Bar *@bar = h()) {\n"
- " i();\n"
- " j();\n"
- " }\n"
- " }\n"
- "}\n"
- ) << _(
- "void f()\n"
- "{\n"
- " while (Foo *foo = g()) {\n"
- " Bar *bar;\n"
- " while ((bar = h()) != 0) {\n"
- " i();\n"
- " j();\n"
- " }\n"
- " }\n"
- "}\n"
- );
-
- // Check: Just a basic test since the main functionality is tested in
- // cpppointerdeclarationformatter_test.cpp
- QTest::newRow("ReformatPointerDeclaration")
- << CppQuickFixFactoryPtr(new ReformatPointerDeclaration)
- << _("char@*s;")
- << _("char *s;");
-
- // Check from source file: If there is no header file, insert the definition after the class.
- QByteArray original =
- "struct Foo\n"
- "{\n"
- " Foo();@\n"
- "};\n";
-
- QTest::newRow("InsertDefFromDecl_basic")
- << CppQuickFixFactoryPtr(new InsertDefFromDecl) << original
- << original + _(
- "\n"
- "Foo::Foo()\n"
- "{\n\n"
- "}\n"
- );
-
- QTest::newRow("InsertDefFromDecl_freeFunction")
- << CppQuickFixFactoryPtr(new InsertDefFromDecl)
- << _("void free()@;\n")
- << _(
- "void free()\n"
- "{\n\n"
- "}\n"
- );
-
- // Check not triggering when it is a statement
- QTest::newRow("InsertDefFromDecl_notTriggeringStatement")
- << CppQuickFixFactoryPtr(new InsertDefFromDecl) << _(
- "class Foo {\n"
- "public:\n"
- " Foo() {}\n"
- "};\n"
- "void freeFunc() {\n"
- " Foo @f();"
- "}\n"
- ) << _();
-
- // Check: Add local variable for a free function.
- QTest::newRow("AssignToLocalVariable_freeFunction")
- << CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
- "int foo() {return 1;}\n"
- "void bar() {fo@o();}\n"
- ) << _(
- "int foo() {return 1;}\n"
- "void bar() {auto localFoo = foo();}\n"
- );
-
- // Check: Add local variable for a member function.
- QTest::newRow("AssignToLocalVariable_memberFunction")
- << CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
- "class Foo {public: int* fooFunc();}\n"
- "void bar() {\n"
- " Foo *f = new Foo;\n"
- " @f->fooFunc();\n"
- "}\n"
- ) << _(
- "class Foo {public: int* fooFunc();}\n"
- "void bar() {\n"
- " Foo *f = new Foo;\n"
- " auto localFooFunc = f->fooFunc();\n"
- "}\n"
- );
-
- // Check: Add local variable for a member function, cursor in the middle (QTCREATORBUG-10355)
- QTest::newRow("AssignToLocalVariable_memberFunction2ndGrade1")
- << CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
- "struct Foo {int* func();};\n"
- "struct Baz {Foo* foo();};\n"
- "void bar() {\n"
- " Baz *b = new Baz;\n"
- " b->foo@()->func();\n"
- "}"
- ) << _(
- "struct Foo {int* func();};\n"
- "struct Baz {Foo* foo();};\n"
- "void bar() {\n"
- " Baz *b = new Baz;\n"
- " auto localFunc = b->foo()->func();\n"
- "}"
- );
-
- // Check: Add local variable for a member function, cursor on function call (QTCREATORBUG-10355)
- QTest::newRow("AssignToLocalVariable_memberFunction2ndGrade2")
- << CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
- "struct Foo {int* func();};\n"
- "struct Baz {Foo* foo();};\n"
- "void bar() {\n"
- " Baz *b = new Baz;\n"
- " b->foo()->f@unc();\n"
- "}"
- ) << _(
- "struct Foo {int* func();};\n"
- "struct Baz {Foo* foo();};\n"
- "void bar() {\n"
- " Baz *b = new Baz;\n"
- " auto localFunc = b->foo()->func();\n"
- "}"
- );
-
- // Check: Add local variable for a static member function.
- QTest::newRow("AssignToLocalVariable_staticMemberFunction")
- << CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
- "class Foo {public: static int* fooFunc();}\n"
- "void bar() {\n"
- " Foo::fooF@unc();\n"
- "}"
- ) << _(
- "class Foo {public: static int* fooFunc();}\n"
- "void bar() {\n"
- " auto localFooFunc = Foo::fooFunc();\n"
- "}"
- );
-
- // Check: Add local variable for a new Expression.
- QTest::newRow("AssignToLocalVariable_newExpression")
- << CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
- "class Foo {}\n"
- "void bar() {\n"
- " new Fo@o;\n"
- "}"
- ) << _(
- "class Foo {}\n"
- "void bar() {\n"
- " auto localFoo = new Foo;\n"
- "}"
- );
-
- // Check: No trigger for function inside member initialization list.
- QTest::newRow("AssignToLocalVariable_noInitializationList")
- << CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
- "class Foo\n"
- "{\n"
- " public: Foo : m_i(fooF@unc()) {}\n"
- " int fooFunc() {return 2;}\n"
- " int m_i;\n"
- "};\n"
- ) << _();
-
- // Check: No trigger for void functions.
- QTest::newRow("AssignToLocalVariable_noVoidFunction")
- << CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
- "void foo() {}\n"
- "void bar() {fo@o();}"
- ) << _();
-
- // Check: No trigger for void member functions.
- QTest::newRow("AssignToLocalVariable_noVoidMemberFunction")
- << CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
- "class Foo {public: void fooFunc();}\n"
- "void bar() {\n"
- " Foo *f = new Foo;\n"
- " @f->fooFunc();\n"
- "}"
- ) << _();
-
- // Check: No trigger for void static member functions.
- QTest::newRow("AssignToLocalVariable_noVoidStaticMemberFunction")
- << CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
- "class Foo {public: static void fooFunc();}\n"
- "void bar() {\n"
- " Foo::fo@oFunc();\n"
- "}"
- ) << _();
-
- // Check: No trigger for functions in expressions.
- QTest::newRow("AssignToLocalVariable_noFunctionInExpression")
- << CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
- "int foo(int a) {return a;}\n"
- "int bar() {return 1;}"
- "void baz() {foo(@bar() + bar());}"
- ) << _();
-
- // Check: No trigger for functions in functions. (QTCREATORBUG-9510)
- QTest::newRow("AssignToLocalVariable_noFunctionInFunction")
- << CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
- "int foo(int a, int b) {return a + b;}\n"
- "int bar(int a) {return a;}\n"
- "void baz() {\n"
- " int a = foo(ba@r(), bar());\n"
- "}\n"
- ) << _();
-
- // Check: No trigger for functions in return statements (classes).
- QTest::newRow("AssignToLocalVariable_noReturnClass1")
- << CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
- "class Foo {public: static void fooFunc();}\n"
- "Foo* bar() {\n"
- " return new Fo@o;\n"
- "}"
- ) << _();
-
- // Check: No trigger for functions in return statements (classes). (QTCREATORBUG-9525)
- QTest::newRow("AssignToLocalVariable_noReturnClass2")
- << CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
- "class Foo {public: int fooFunc();}\n"
- "int bar() {\n"
- " return (new Fo@o)->fooFunc();\n"
- "}"
- ) << _();
-
- // Check: No trigger for functions in return statements (functions).
- QTest::newRow("AssignToLocalVariable_noReturnFunc1")
- << CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
- "class Foo {public: int fooFunc();}\n"
- "int bar() {\n"
- " return Foo::fooFu@nc();\n"
- "}"
- ) << _();
-
- // Check: No trigger for functions in return statements (functions). (QTCREATORBUG-9525)
- QTest::newRow("AssignToLocalVariable_noReturnFunc2")
- << CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
- "int bar() {\n"
- " return list.firs@t().foo;\n"
- "}\n"
- ) << _();
-
- // Check: No trigger for functions which does not match in signature.
- QTest::newRow("AssignToLocalVariable_noSignatureMatch")
- << CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
- "int someFunc(int);\n"
- "\n"
- "void f()\n"
- "{\n"
- " some@Func();\n"
- "}"
- ) << _();
-
- QTest::newRow("ExtractLiteralAsParameter_freeFunction")
- << CppQuickFixFactoryPtr(new ExtractLiteralAsParameter) << _(
- "void foo(const char *a, long b = 1)\n"
- "{return 1@56 + 123 + 156;}\n"
- ) << _(
- "void foo(const char *a, long b = 1, int newParameter = 156)\n"
- "{return newParameter + 123 + newParameter;}\n"
- );
-
- QTest::newRow("ExtractLiteralAsParameter_memberFunction")
- << CppQuickFixFactoryPtr(new ExtractLiteralAsParameter) << _(
- "class Narf {\n"
- "public:\n"
- " int zort();\n"
- "};\n\n"
- "int Narf::zort()\n"
- "{ return 15@5 + 1; }\n"
- ) << _(
- "class Narf {\n"
- "public:\n"
- " int zort(int newParameter = 155);\n"
- "};\n\n"
- "int Narf::zort(int newParameter)\n"
- "{ return newParameter + 1; }\n"
- );
-
- QTest::newRow("ExtractLiteralAsParameter_memberFunctionInline")
- << CppQuickFixFactoryPtr(new ExtractLiteralAsParameter) << _(
- "class Narf {\n"
- "public:\n"
- " int zort()\n"
- " { return 15@5 + 1; }\n"
- "};\n"
- ) << _(
- "class Narf {\n"
- "public:\n"
- " int zort(int newParameter = 155)\n"
- " { return newParameter + 1; }\n"
- "};\n"
- );
-
- // Check: optimize postcrement
- QTest::newRow("OptimizeForLoop_postcrement")
- << CppQuickFixFactoryPtr(new OptimizeForLoop)
- << _("void foo() {f@or (int i = 0; i < 3; i++) {}}\n")
- << _("void foo() {for (int i = 0; i < 3; ++i) {}}\n");
-
- // Check: optimize condition
- QTest::newRow("OptimizeForLoop_condition")
- << CppQuickFixFactoryPtr(new OptimizeForLoop)
- << _("void foo() {f@or (int i = 0; i < 3 + 5; ++i) {}}\n")
- << _("void foo() {for (int i = 0, total = 3 + 5; i < total; ++i) {}}\n");
-
- // Check: optimize fliped condition
- QTest::newRow("OptimizeForLoop_flipedCondition")
- << CppQuickFixFactoryPtr(new OptimizeForLoop)
- << _("void foo() {f@or (int i = 0; 3 + 5 > i; ++i) {}}\n")
- << _("void foo() {for (int i = 0, total = 3 + 5; total > i; ++i) {}}\n");
-
- // Check: if "total" used, create other name.
- QTest::newRow("OptimizeForLoop_alterVariableName")
- << CppQuickFixFactoryPtr(new OptimizeForLoop)
- << _("void foo() {f@or (int i = 0, total = 0; i < 3 + 5; ++i) {}}\n")
- << _("void foo() {for (int i = 0, total = 0, totalX = 3 + 5; i < totalX; ++i) {}}\n");
-
- // Check: optimize postcrement and condition
- QTest::newRow("OptimizeForLoop_optimizeBoth")
- << CppQuickFixFactoryPtr(new OptimizeForLoop)
- << _("void foo() {f@or (int i = 0; i < 3 + 5; i++) {}}\n")
- << _("void foo() {for (int i = 0, total = 3 + 5; i < total; ++i) {}}\n");
-
- // Check: empty initializier
- QTest::newRow("OptimizeForLoop_emptyInitializer")
- << CppQuickFixFactoryPtr(new OptimizeForLoop)
- << _("int i; void foo() {f@or (; i < 3 + 5; ++i) {}}\n")
- << _("int i; void foo() {for (int total = 3 + 5; i < total; ++i) {}}\n");
-
- // Check: wrong initializier type -> no trigger
- QTest::newRow("OptimizeForLoop_wrongInitializer")
- << CppQuickFixFactoryPtr(new OptimizeForLoop)
- << _("int i; void foo() {f@or (double a = 0; i < 3 + 5; ++i) {}}\n")
- << _();
-
- // Check: No trigger when numeric
- QTest::newRow("OptimizeForLoop_noTriggerNumeric1")
- << CppQuickFixFactoryPtr(new OptimizeForLoop)
- << _("void foo() {fo@r (int i = 0; i < 3; ++i) {}}\n")
- << _();
-
- // Check: No trigger when numeric
- QTest::newRow("OptimizeForLoop_noTriggerNumeric2")
- << CppQuickFixFactoryPtr(new OptimizeForLoop)
- << _("void foo() {fo@r (int i = 0; i < -3; ++i) {}}\n")
- << _();
-
- // Escape String Literal as UTF-8 (no-trigger)
- QTest::newRow("EscapeStringLiteral_notrigger")
- << CppQuickFixFactoryPtr(new EscapeStringLiteral)
- << _("const char *notrigger = \"@abcdef \\a\\n\\\\\";\n")
- << _();
-
- // Escape String Literal as UTF-8
- QTest::newRow("EscapeStringLiteral")
- << CppQuickFixFactoryPtr(new EscapeStringLiteral)
- << _("const char *utf8 = \"@\xe3\x81\x82\xe3\x81\x84\";\n")
- << _("const char *utf8 = \"\\xe3\\x81\\x82\\xe3\\x81\\x84\";\n");
-
- // Unescape String Literal as UTF-8 (from hexdecimal escape sequences)
- QTest::newRow("UnescapeStringLiteral_hex")
- << CppQuickFixFactoryPtr(new EscapeStringLiteral)
- << _("const char *hex_escaped = \"@\\xe3\\x81\\x82\\xe3\\x81\\x84\";\n")
- << _("const char *hex_escaped = \"\xe3\x81\x82\xe3\x81\x84\";\n");
-
- // Unescape String Literal as UTF-8 (from octal escape sequences)
- QTest::newRow("UnescapeStringLiteral_oct")
- << CppQuickFixFactoryPtr(new EscapeStringLiteral)
- << _("const char *oct_escaped = \"@\\343\\201\\202\\343\\201\\204\";\n")
- << _("const char *oct_escaped = \"\xe3\x81\x82\xe3\x81\x84\";\n");
-
- // Unescape String Literal as UTF-8 (triggered but no change)
- QTest::newRow("UnescapeStringLiteral_noconv")
- << CppQuickFixFactoryPtr(new EscapeStringLiteral)
- << _("const char *escaped_ascii = \"@\\x1b\";\n")
- << _("const char *escaped_ascii = \"\\x1b\";\n");
-
- // Unescape String Literal as UTF-8 (no conversion because of invalid utf-8)
- QTest::newRow("UnescapeStringLiteral_invalid")
- << CppQuickFixFactoryPtr(new EscapeStringLiteral)
- << _("const char *escaped = \"@\\xe3\\x81\";\n")
- << _("const char *escaped = \"\\xe3\\x81\";\n");
-
- QTest::newRow("ConvertFromPointer")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _("void foo() {\n"
- " QString *@str;\n"
- " if (!str->isEmpty())\n"
- " str->clear();\n"
- " f1(*str);\n"
- " f2(str);\n"
- "}\n")
- << _("void foo() {\n"
- " QString str;\n"
- " if (!str.isEmpty())\n"
- " str.clear();\n"
- " f1(str);\n"
- " f2(&str);\n"
- "}\n");
-
- QTest::newRow("ConvertToPointer")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _("void foo() {\n"
- " QString @str;\n"
- " if (!str.isEmpty())\n"
- " str.clear();\n"
- " f1(str);\n"
- " f2(&str);\n"
- "}\n")
- << _("void foo() {\n"
- " QString *str = new QString;\n"
- " if (!str->isEmpty())\n"
- " str->clear();\n"
- " f1(*str);\n"
- " f2(str);\n"
- "}\n");
-
- QTest::newRow("ConvertReferenceToPointer")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _("void foo() {\n"
- " QString narf;"
- " QString &@str = narf;\n"
- " if (!str.isEmpty())\n"
- " str.clear();\n"
- " f1(str);\n"
- " f2(&str);\n"
- "}\n")
- << _("void foo() {\n"
- " QString narf;"
- " QString *str = &narf;\n"
- " if (!str->isEmpty())\n"
- " str->clear();\n"
- " f1(*str);\n"
- " f2(str);\n"
- "}\n");
-
- QTest::newRow("ConvertFromPointer_withInitializer")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _("void foo() {\n"
- " QString *@str = new QString(QLatin1String(\"schnurz\"));\n"
- " if (!str->isEmpty())\n"
- " str->clear();\n"
- "}\n")
- << _("void foo() {\n"
- " QString str(QLatin1String(\"schnurz\"));\n"
- " if (!str.isEmpty())\n"
- " str.clear();\n"
- "}\n");
-
- QTest::newRow("ConvertFromPointer_withBareInitializer")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _("void foo() {\n"
- " QString *@str = new QString;\n"
- " if (!str->isEmpty())\n"
- " str->clear();\n"
- "}\n")
- << _("void foo() {\n"
- " QString str;\n"
- " if (!str.isEmpty())\n"
- " str.clear();\n"
- "}\n");
-
- QTest::newRow("ConvertFromPointer_withEmptyInitializer")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _("void foo() {\n"
- " QString *@str = new QString();\n"
- " if (!str->isEmpty())\n"
- " str->clear();\n"
- "}\n")
- << _("void foo() {\n"
- " QString str;\n"
- " if (!str.isEmpty())\n"
- " str.clear();\n"
- "}\n");
-
- QTest::newRow("ConvertFromPointer_structWithPointer")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _("struct Bar{ QString *str; };\n"
- "void foo() {\n"
- " Bar *@bar = new Bar;\n"
- " bar->str = new QString;\n"
- " delete bar->str;\n"
- " delete bar;\n"
- "}\n")
- << _("struct Bar{ QString *str; };\n"
- "void foo() {\n"
- " Bar bar;\n"
- " bar.str = new QString;\n"
- " delete bar.str;\n"
- " // delete bar;\n"
- "}\n");
-
- QTest::newRow("ConvertToPointer_withInitializer")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _("void foo() {\n"
- " QString @str = QLatin1String(\"narf\");\n"
- " if (!str.isEmpty())\n"
- " str.clear();\n"
- "}\n")
- << _("void foo() {\n"
- " QString *str = new QString(QLatin1String(\"narf\"));\n"
- " if (!str->isEmpty())\n"
- " str->clear();\n"
- "}\n");
-
- QTest::newRow("ConvertToPointer_withParenInitializer")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _("void foo() {\n"
- " QString @str(QLatin1String(\"narf\"));\n"
- " if (!str.isEmpty())\n"
- " str.clear();\n"
- "}\n")
- << _("void foo() {\n"
- " QString *str = new QString(QLatin1String(\"narf\"));\n"
- " if (!str->isEmpty())\n"
- " str->clear();\n"
- "}\n");
-
- QTest::newRow("ConvertToPointer_noTriggerRValueRefs")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _("void foo(Narf &&@narf) {}\n")
- << _();
-
- QTest::newRow("ConvertToPointer_noTriggerGlobal")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _("int @global;\n")
- << _();
-
- QTest::newRow("ConvertToPointer_noTriggerClassMember")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _("struct C { int @member; };\n")
- << _();
-
- QTest::newRow("ConvertToPointer_noTriggerClassMember2")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _("void f() { struct C { int @member; }; }\n")
- << _();
-
- QTest::newRow("ConvertToPointer_functionOfFunctionLocalClass")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _("void f() {\n"
- " struct C {\n"
- " void g() { int @member; }\n"
- " };\n"
- "}\n")
- << _("void f() {\n"
- " struct C {\n"
- " void g() { int *member; }\n"
- " };\n"
- "}\n");
-
- QTest::newRow("ConvertToPointer_redeclaredVariable_block")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _("void foo() {\n"
- " QString @str;\n"
- " str.clear();\n"
- " {\n"
- " QString str;\n"
- " str.clear();\n"
- " }\n"
- " f1(str);\n"
- "}\n")
- << _("void foo() {\n"
- " QString *str = new QString;\n"
- " str->clear();\n"
- " {\n"
- " QString str;\n"
- " str.clear();\n"
- " }\n"
- " f1(*str);\n"
- "}\n");
-
- QTest::newRow("ConvertAutoFromPointer")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _("void foo() {\n"
- " auto @str = new QString(QLatin1String(\"foo\"));\n"
- " if (!str->isEmpty())\n"
- " str->clear();\n"
- " f1(*str);\n"
- " f2(str);\n"
- "}\n")
- << _("void foo() {\n"
- " auto str = QString(QLatin1String(\"foo\"));\n"
- " if (!str.isEmpty())\n"
- " str.clear();\n"
- " f1(str);\n"
- " f2(&str);\n"
- "}\n");
-
- QTest::newRow("ConvertAutoFromPointer2")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _("void foo() {\n"
- " auto *@str = new QString;\n"
- " if (!str->isEmpty())\n"
- " str->clear();\n"
- " f1(*str);\n"
- " f2(str);\n"
- "}\n")
- << _("void foo() {\n"
- " auto str = QString();\n"
- " if (!str.isEmpty())\n"
- " str.clear();\n"
- " f1(str);\n"
- " f2(&str);\n"
- "}\n");
-
- QTest::newRow("ConvertAutoToPointer")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _("void foo() {\n"
- " auto @str = QString(QLatin1String(\"foo\"));\n"
- " if (!str.isEmpty())\n"
- " str.clear();\n"
- " f1(str);\n"
- " f2(&str);\n"
- "}\n")
- << _("void foo() {\n"
- " auto @str = new QString(QLatin1String(\"foo\"));\n"
- " if (!str->isEmpty())\n"
- " str->clear();\n"
- " f1(*str);\n"
- " f2(str);\n"
- "}\n");
-
- QTest::newRow("ConvertToPointerWithMacro")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _("#define BAR bar\n"
- "void func()\n"
- "{\n"
- " int @foo = 42;\n"
- " int bar;\n"
- " BAR = foo;\n"
- "}\n")
- << _("#define BAR bar\n"
- "void func()\n"
- "{\n"
- " int *foo = 42;\n"
- " int bar;\n"
- " BAR = *foo;\n"
- "}\n");
-
- QString testObjAndFunc = "struct Object\n"
- "{\n"
- " Object(%1){}\n"
- "};\n"
- "void func()\n"
- "{\n"
- " %2\n"
- "}\n";
-
- QTest::newRow("ConvertToStack1_QTCREATORBUG23181")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _(testObjAndFunc.arg("int").arg("Object *@obj = new Object(0);").toUtf8())
- << _(testObjAndFunc.arg("int").arg("Object obj(0);").toUtf8());
-
- QTest::newRow("ConvertToStack2_QTCREATORBUG23181")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _(testObjAndFunc.arg("int").arg("Object *@obj = new Object{0};").toUtf8())
- << _(testObjAndFunc.arg("int").arg("Object obj{0};").toUtf8());
-
- QTest::newRow("ConvertToPointer1_QTCREATORBUG23181")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _(testObjAndFunc.arg("").arg("Object @obj;").toUtf8())
- << _(testObjAndFunc.arg("").arg("Object *obj = new Object;").toUtf8());
-
- QTest::newRow("ConvertToPointer2_QTCREATORBUG23181")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _(testObjAndFunc.arg("").arg("Object @obj();").toUtf8())
- << _(testObjAndFunc.arg("").arg("Object *obj = new Object();").toUtf8());
-
- QTest::newRow("ConvertToPointer3_QTCREATORBUG23181")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _(testObjAndFunc.arg("").arg("Object @obj{};").toUtf8())
- << _(testObjAndFunc.arg("").arg("Object *obj = new Object{};").toUtf8());
-
- QTest::newRow("ConvertToPointer4_QTCREATORBUG23181")
- << CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
- << _(testObjAndFunc.arg("int").arg("Object @obj(0);").toUtf8())
- << _(testObjAndFunc.arg("int").arg("Object *obj = new Object(0);").toUtf8());
-
- QTest::newRow("InsertQtPropertyMembers_noTriggerInvalidCode")
- << CppQuickFixFactoryPtr(new InsertQtPropertyMembers)
- << _("class C { @Q_PROPERTY(typeid foo READ foo) };\n")
- << _();
-
- QTest::newRow("convert to camel case: normal")
- << CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
- << _("void @lower_case_function();\n")
- << _("void lowerCaseFunction();\n");
- QTest::newRow("convert to camel case: already camel case")
- << CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
- << _("void @camelCaseFunction();\n")
- << _();
- QTest::newRow("convert to camel case: no underscores (lower case)")
- << CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
- << _("void @lowercasefunction();\n")
- << _();
- QTest::newRow("convert to camel case: no underscores (upper case)")
- << CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
- << _("void @UPPERCASEFUNCTION();\n")
- << _();
- QTest::newRow("convert to camel case: non-applicable underscore")
- << CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
- << _("void @m_a_member;\n")
- << _("void m_aMember;\n");
- QTest::newRow("convert to camel case: upper case")
- << CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
- << _("void @UPPER_CASE_FUNCTION();\n")
- << _("void upperCaseFunction();\n");
- QTest::newRow("convert to camel case: partially camel case already")
- << CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
- << _("void mixed@_andCamelCase();\n")
- << _("void mixedAndCamelCase();\n");
- QTest::newRow("convert to camel case: wild mix")
- << CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
- << _("void @WhAt_TODO_hErE();\n")
- << _("void WhAtTODOHErE();\n");
- QTest::newRow("escape string literal: simple case")
- << CppQuickFixFactoryPtr(new EscapeStringLiteral)
- << _(R"(const char *str = @"àxyz";)")
- << _(R"(const char *str = "\xc3\xa0xyz";)");
- QTest::newRow("escape string literal: simple case reverse")
- << CppQuickFixFactoryPtr(new EscapeStringLiteral)
- << _(R"(const char *str = @"\xc3\xa0xyz";)")
- << _(R"(const char *str = "àxyz";)");
- QTest::newRow("escape string literal: raw string literal")
- << CppQuickFixFactoryPtr(new EscapeStringLiteral)
- << _(R"x(const char *str = @R"(àxyz)";)x")
- << _(R"x(const char *str = R"(\xc3\xa0xyz)";)x");
- QTest::newRow("escape string literal: splitting required")
- << CppQuickFixFactoryPtr(new EscapeStringLiteral)
- << _(R"(const char *str = @"àf23бgб1";)")
- << _(R"(const char *str = "\xc3\xa0""f23\xd0\xb1g\xd0\xb1""1";)");
- QTest::newRow("escape string literal: unescape adjacent literals")
- << CppQuickFixFactoryPtr(new EscapeStringLiteral)
- << _(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 AddDeclarationForUndeclaredIdentifier)
- << _("void func() {\n"
- " QStringList list;\n"
- " @it = list.cbegin();\n"
- "}\n")
- << _("void func() {\n"
- " QStringList list;\n"
- " auto it = list.cbegin();\n"
- "}\n");
-}
-
-void QuickfixTest::testGeneric()
-{
- QFETCH(CppQuickFixFactoryPtr, factory);
- QFETCH(QByteArray, original);
- QFETCH(QByteArray, expected);
-
- QuickFixOperationTest(singleDocument(original, expected), factory.data());
-}
-
-void QuickfixTest::testGenerateGetterSetterNamespaceHandlingCreate_data()
-{
- QTest::addColumn<QByteArrayList>("headers");
- QTest::addColumn<QByteArrayList>("sources");
-
- QByteArray originalSource;
- QByteArray expectedSource;
-
- const QByteArray originalHeader =
- "namespace N1 {\n"
- "namespace N2 {\n"
- "class Something\n"
- "{\n"
- " int @it;\n"
- "};\n"
- "}\n"
- "}\n";
- const QByteArray expectedHeader =
- "namespace N1 {\n"
- "namespace N2 {\n"
- "class Something\n"
- "{\n"
- " int it;\n"
- "\n"
- "public:\n"
- " int getIt() const;\n"
- " void setIt(int value);\n"
- "};\n"
- "}\n"
- "}\n";
-
- originalSource = "#include \"file.h\"\n";
- expectedSource =
- "#include \"file.h\"\n\n\n"
- "namespace N1 {\n"
- "namespace N2 {\n"
- "int Something::getIt() const\n"
- "{\n"
- " return it;\n"
- "}\n"
- "\n"
- "void Something::setIt(int value)\n"
- "{\n"
- " it = value;\n"
- "}\n\n"
- "}\n"
- "}\n";
- QTest::addRow("insert new namespaces")
- << QByteArrayList{originalHeader, expectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-
- originalSource =
- "#include \"file.h\"\n"
- "namespace N2 {} // decoy\n";
- expectedSource =
- "#include \"file.h\"\n"
- "namespace N2 {} // decoy\n\n\n"
- "namespace N1 {\n"
- "namespace N2 {\n"
- "int Something::getIt() const\n"
- "{\n"
- " return it;\n"
- "}\n"
- "\n"
- "void Something::setIt(int value)\n"
- "{\n"
- " it = value;\n"
- "}\n\n"
- "}\n"
- "}\n";
- QTest::addRow("insert new namespaces (with decoy)")
- << QByteArrayList{originalHeader, expectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-
- originalSource = "#include \"file.h\"\n"
- "namespace N2 {} // decoy\n"
- "namespace {\n"
- "namespace N1 {\n"
- "namespace {\n"
- "}\n"
- "}\n"
- "}\n";
- expectedSource = "#include \"file.h\"\n"
- "namespace N2 {} // decoy\n"
- "namespace {\n"
- "namespace N1 {\n"
- "namespace {\n"
- "}\n"
- "}\n"
- "}\n"
- "\n"
- "\n"
- "namespace N1 {\n"
- "namespace N2 {\n"
- "int Something::getIt() const\n"
- "{\n"
- " return it;\n"
- "}\n"
- "\n"
- "void Something::setIt(int value)\n"
- "{\n"
- " it = value;\n"
- "}\n"
- "\n"
- "}\n"
- "}\n";
- QTest::addRow("insert inner namespace (with decoy and unnamed)")
- << QByteArrayList{originalHeader, expectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-
- const QByteArray unnamedOriginalHeader = "namespace {\n" + originalHeader + "}\n";
- const QByteArray unnamedExpectedHeader = "namespace {\n" + expectedHeader + "}\n";
-
- originalSource = "#include \"file.h\"\n"
- "namespace N2 {} // decoy\n"
- "namespace {\n"
- "namespace N1 {\n"
- "namespace {\n"
- "}\n"
- "}\n"
- "}\n";
- expectedSource = "#include \"file.h\"\n"
- "namespace N2 {} // decoy\n"
- "namespace {\n"
- "namespace N1 {\n"
- "\n"
- "namespace N2 {\n"
- "int Something::getIt() const\n"
- "{\n"
- " return it;\n"
- "}\n"
- "\n"
- "void Something::setIt(int value)\n"
- "{\n"
- " it = value;\n"
- "}\n"
- "\n"
- "}\n"
- "\n"
- "namespace {\n"
- "}\n"
- "}\n"
- "}\n";
- QTest::addRow("insert inner namespace in unnamed (with decoy)")
- << QByteArrayList{unnamedOriginalHeader, unnamedExpectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-
- originalSource =
- "#include \"file.h\"\n"
- "namespace N1 {\n"
- "namespace N2 {\n"
- "namespace N3 {\n"
- "}\n"
- "}\n"
- "}\n";
- expectedSource =
- "#include \"file.h\"\n"
- "namespace N1 {\n"
- "namespace N2 {\n"
- "namespace N3 {\n"
- "}\n\n"
- "int Something::getIt() const\n"
- "{\n"
- " return it;\n"
- "}\n"
- "\n"
- "void Something::setIt(int value)\n"
- "{\n"
- " it = value;\n"
- "}\n\n"
- "}\n"
- "}\n";
- QTest::addRow("all namespaces already present")
- << QByteArrayList{originalHeader, expectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-
- originalSource = "#include \"file.h\"\n"
- "namespace N1 {\n"
- "using namespace N2::N3;\n"
- "using namespace N2;\n"
- "using namespace N2;\n"
- "using namespace N3;\n"
- "}\n";
- expectedSource = "#include \"file.h\"\n"
- "namespace N1 {\n"
- "using namespace N2::N3;\n"
- "using namespace N2;\n"
- "using namespace N2;\n"
- "using namespace N3;\n"
- "\n"
- "int Something::getIt() const\n"
- "{\n"
- " return it;\n"
- "}\n"
- "\n"
- "void Something::setIt(int value)\n"
- "{\n"
- " it = value;\n"
- "}\n\n"
- "}\n";
- QTest::addRow("namespaces present and using namespace")
- << QByteArrayList{originalHeader, expectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-
- originalSource = "#include \"file.h\"\n"
- "using namespace N1::N2::N3;\n"
- "using namespace N1::N2;\n"
- "namespace N1 {\n"
- "using namespace N3;\n"
- "}\n";
- expectedSource = "#include \"file.h\"\n"
- "using namespace N1::N2::N3;\n"
- "using namespace N1::N2;\n"
- "namespace N1 {\n"
- "using namespace N3;\n"
- "\n"
- "int Something::getIt() const\n"
- "{\n"
- " return it;\n"
- "}\n"
- "\n"
- "void Something::setIt(int value)\n"
- "{\n"
- " it = value;\n"
- "}\n"
- "\n"
- "}\n";
- QTest::addRow("namespaces present and outer using namespace")
- << QByteArrayList{originalHeader, expectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-
- originalSource = "#include \"file.h\"\n"
- "using namespace N1;\n"
- "using namespace N2;\n"
- "namespace N3 {\n"
- "}\n";
- expectedSource = "#include \"file.h\"\n"
- "using namespace N1;\n"
- "using namespace N2;\n"
- "namespace N3 {\n"
- "}\n"
- "\n"
- "int Something::getIt() const\n"
- "{\n"
- " return it;\n"
- "}\n"
- "\n"
- "void Something::setIt(int value)\n"
- "{\n"
- " it = value;\n"
- "}\n";
- QTest::addRow("namespaces present and outer using namespace")
- << QByteArrayList{originalHeader, expectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-}
-
-void QuickfixTest::testGenerateGetterSetterNamespaceHandlingCreate()
-{
- QFETCH(QByteArrayList, headers);
- QFETCH(QByteArrayList, sources);
-
- QList<TestDocumentPtr> testDocuments(
- {CppTestDocument::create("file.h", headers.at(0), headers.at(1)),
- CppTestDocument::create("file.cpp", sources.at(0), sources.at(1))});
-
- QuickFixSettings s;
- s->cppFileNamespaceHandling = CppQuickFixSettings::MissingNamespaceHandling::CreateMissing;
- s->setterParameterNameTemplate = "value";
- s->getterNameTemplate = "get<Name>";
- s->setterInCppFileFrom = 1;
- s->getterInCppFileFrom = 1;
- GenerateGetterSetter factory;
- QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 2);
-}
-
-void QuickfixTest::testGenerateGetterSetterNamespaceHandlingAddUsing_data()
-{
- QTest::addColumn<QByteArrayList>("headers");
- QTest::addColumn<QByteArrayList>("sources");
-
- QByteArray originalSource;
- QByteArray expectedSource;
-
- const QByteArray originalHeader = "namespace N1 {\n"
- "namespace N2 {\n"
- "class Something\n"
- "{\n"
- " int @it;\n"
- "};\n"
- "}\n"
- "}\n";
- const QByteArray expectedHeader = "namespace N1 {\n"
- "namespace N2 {\n"
- "class Something\n"
- "{\n"
- " int it;\n"
- "\n"
- "public:\n"
- " void setIt(int value);\n"
- "};\n"
- "}\n"
- "}\n";
-
- originalSource = "#include \"file.h\"\n";
- expectedSource = "#include \"file.h\"\n\n"
- "using namespace N1::N2;\n"
- "void Something::setIt(int value)\n"
- "{\n"
- " it = value;\n"
- "}\n";
- QTest::addRow("add using namespaces") << QByteArrayList{originalHeader, expectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-
- const QByteArray unnamedOriginalHeader = "namespace {\n" + originalHeader + "}\n";
- const QByteArray unnamedExpectedHeader = "namespace {\n" + expectedHeader + "}\n";
-
- originalSource = "#include \"file.h\"\n"
- "namespace N2 {} // decoy\n"
- "namespace {\n"
- "namespace N1 {\n"
- "namespace {\n"
- "}\n"
- "}\n"
- "}\n";
- expectedSource = "#include \"file.h\"\n"
- "namespace N2 {} // decoy\n"
- "namespace {\n"
- "namespace N1 {\n"
- "using namespace N2;\n"
- "void Something::setIt(int value)\n"
- "{\n"
- " it = value;\n"
- "}\n"
- "namespace {\n"
- "}\n"
- "}\n"
- "}\n";
- QTest::addRow("insert using namespace into unnamed nested (with decoy)")
- << QByteArrayList{unnamedOriginalHeader, unnamedExpectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-
- originalSource = "#include \"file.h\"\n";
- expectedSource = "#include \"file.h\"\n\n"
- "using namespace N1::N2;\n"
- "void Something::setIt(int value)\n"
- "{\n"
- " it = value;\n"
- "}\n";
- QTest::addRow("insert using namespace into unnamed")
- << QByteArrayList{unnamedOriginalHeader, unnamedExpectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-
- originalSource = "#include \"file.h\"\n"
- "namespace N2 {} // decoy\n"
- "namespace {\n"
- "namespace N1 {\n"
- "namespace {\n"
- "}\n"
- "}\n"
- "}\n";
- expectedSource = "#include \"file.h\"\n"
- "namespace N2 {} // decoy\n"
- "namespace {\n"
- "namespace N1 {\n"
- "namespace {\n"
- "}\n"
- "}\n"
- "}\n"
- "\n"
- "using namespace N1::N2;\n"
- "void Something::setIt(int value)\n"
- "{\n"
- " it = value;\n"
- "}\n";
- QTest::addRow("insert using namespace (with decoy)")
- << QByteArrayList{originalHeader, expectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-}
-
-void QuickfixTest::testGenerateGetterSetterNamespaceHandlingAddUsing()
-{
- QFETCH(QByteArrayList, headers);
- QFETCH(QByteArrayList, sources);
-
- QList<TestDocumentPtr> testDocuments(
- {CppTestDocument::create("file.h", headers.at(0), headers.at(1)),
- CppTestDocument::create("file.cpp", sources.at(0), sources.at(1))});
-
- QuickFixSettings s;
- s->cppFileNamespaceHandling = CppQuickFixSettings::MissingNamespaceHandling::AddUsingDirective;
- s->setterParameterNameTemplate = "value";
- s->setterInCppFileFrom = 1;
-
- if (std::strstr(QTest::currentDataTag(), "unnamed nested") != nullptr)
- QSKIP("TODO"); // FIXME
- GenerateGetterSetter factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testGenerateGetterSetterNamespaceHandlingFullyQualify_data()
-{
- QTest::addColumn<QByteArrayList>("headers");
- QTest::addColumn<QByteArrayList>("sources");
-
- QByteArray originalSource;
- QByteArray expectedSource;
-
- const QByteArray originalHeader = "namespace N1 {\n"
- "namespace N2 {\n"
- "class Something\n"
- "{\n"
- " int @it;\n"
- "};\n"
- "}\n"
- "}\n";
- const QByteArray expectedHeader = "namespace N1 {\n"
- "namespace N2 {\n"
- "class Something\n"
- "{\n"
- " int it;\n"
- "\n"
- "public:\n"
- " void setIt(int value);\n"
- "};\n"
- "}\n"
- "}\n";
-
- originalSource = "#include \"file.h\"\n";
- expectedSource = "#include \"file.h\"\n\n"
- "void N1::N2::Something::setIt(int value)\n"
- "{\n"
- " it = value;\n"
- "}\n";
- QTest::addRow("fully qualify") << QByteArrayList{originalHeader, expectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-
- originalSource = "#include \"file.h\"\n"
- "namespace N2 {} // decoy\n";
- expectedSource = "#include \"file.h\"\n"
- "namespace N2 {} // decoy\n"
- "\n"
- "void N1::N2::Something::setIt(int value)\n"
- "{\n"
- " it = value;\n"
- "}\n";
- QTest::addRow("fully qualify (with decoy)") << QByteArrayList{originalHeader, expectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-
- originalSource = "#include \"file.h\"\n"
- "namespace N2 {} // decoy\n"
- "namespace {\n"
- "namespace N1 {\n"
- "namespace {\n"
- "}\n"
- "}\n"
- "}\n";
- expectedSource = "#include \"file.h\"\n"
- "namespace N2 {} // decoy\n"
- "namespace {\n"
- "namespace N1 {\n"
- "namespace {\n"
- "}\n"
- "}\n"
- "}\n"
- "\n"
- "void N1::N2::Something::setIt(int value)\n"
- "{\n"
- " it = value;\n"
- "}\n";
- QTest::addRow("qualify in inner namespace (with decoy)")
- << QByteArrayList{originalHeader, expectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-
- const QByteArray unnamedOriginalHeader = "namespace {\n" + originalHeader + "}\n";
- const QByteArray unnamedExpectedHeader = "namespace {\n" + expectedHeader + "}\n";
-
- originalSource = "#include \"file.h\"\n"
- "namespace N2 {} // decoy\n"
- "namespace {\n"
- "namespace N1 {\n"
- "namespace {\n"
- "}\n"
- "}\n"
- "}\n";
- expectedSource = "#include \"file.h\"\n"
- "namespace N2 {} // decoy\n"
- "namespace {\n"
- "namespace N1 {\n"
- "void N2::Something::setIt(int value)\n"
- "{\n"
- " it = value;\n"
- "}\n"
- "namespace {\n"
- "}\n"
- "}\n"
- "}\n";
- QTest::addRow("qualify in inner namespace unnamed nested (with decoy)")
- << QByteArrayList{unnamedOriginalHeader, unnamedExpectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-
- originalSource = "#include \"file.h\"\n";
- expectedSource = "#include \"file.h\"\n\n"
- "void N1::N2::Something::setIt(int value)\n"
- "{\n"
- " it = value;\n"
- "}\n";
- QTest::addRow("qualify in unnamed namespace")
- << QByteArrayList{unnamedOriginalHeader, unnamedExpectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-}
-
-void QuickfixTest::testGenerateGetterSetterNamespaceHandlingFullyQualify()
-{
- QFETCH(QByteArrayList, headers);
- QFETCH(QByteArrayList, sources);
-
- QList<TestDocumentPtr> testDocuments(
- {CppTestDocument::create("file.h", headers.at(0), headers.at(1)),
- CppTestDocument::create("file.cpp", sources.at(0), sources.at(1))});
-
- QuickFixSettings s;
- s->cppFileNamespaceHandling = CppQuickFixSettings::MissingNamespaceHandling::RewriteType;
- s->setterParameterNameTemplate = "value";
- s->setterInCppFileFrom = 1;
-
- if (std::strstr(QTest::currentDataTag(), "unnamed nested") != nullptr)
- QSKIP("TODO"); // FIXME
- GenerateGetterSetter factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testGenerateGetterSetterCustomNames_data()
-{
- QTest::addColumn<QByteArrayList>("headers");
- QTest::addColumn<int>("operation");
-
- QByteArray originalSource;
- QByteArray expectedSource;
-
- // Check if right names are created
- originalSource = R"-(
-class Test {
- int m_fooBar_test@;
-};
-)-";
- expectedSource = R"-(
-class Test {
- int m_fooBar_test;
-
-public:
- int give_me_foo_bar_test() const
- {
- return m_fooBar_test;
- }
- void Seet_FooBar_test(int New_Foo_Bar_Test)
- {
- if (m_fooBar_test == New_Foo_Bar_Test)
- return;
- m_fooBar_test = New_Foo_Bar_Test;
- emit newFooBarTestValue();
- }
- void set_fooBarTest_toDefault()
- {
- Seet_FooBar_test({}); // TODO: Adapt to use your actual default value
- }
-
-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 FINAL)
-};
-)-";
- QTest::addRow("create right names") << QByteArrayList{originalSource, expectedSource} << 4;
-
- // Check if not triggered with custom names
- originalSource = R"-(
-class Test {
- int m_fooBar_test@;
-
-public:
- int give_me_foo_bar_test() const
- {
- return m_fooBar_test;
- }
- void Seet_FooBar_test(int New_Foo_Bar_Test)
- {
- if (m_fooBar_test == New_Foo_Bar_Test)
- return;
- m_fooBar_test = New_Foo_Bar_Test;
- emit newFooBarTestValue();
- }
- void set_fooBarTest_toDefault()
- {
- Seet_FooBar_test({}); // TODO: Adapt to use your actual default value
- }
-
-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 FINAL)
-};
-)-";
- expectedSource = "";
- QTest::addRow("everything already exists") << QByteArrayList{originalSource, expectedSource} << 4;
-
- // 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 FINAL)
-
-public:
- int give_me_foo_bar_test() const
- {
- return mem_fooBar_test;
- }
- void Seet_FooBar_test(int New_Foo_Bar_Test)
- {
- if (mem_fooBar_test == New_Foo_Bar_Test)
- return;
- mem_fooBar_test = New_Foo_Bar_Test;
- emit newFooBarTestValue();
- }
- void set_fooBarTest_toDefault()
- {
- Seet_FooBar_test({}); // TODO: Adapt to use your actual default value
- }
-
-signals:
- void newFooBarTestValue();
-};
-)-";
- 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 FINAL)
-
-public:
- int give_me_foo_bar_test() const
- {
- return mem_fooBar_test;
- }
- void Seet_FooBar_test(int New_Foo_Bar_Test)
- {
- if (mem_fooBar_test == New_Foo_Bar_Test)
- return;
- mem_fooBar_test = New_Foo_Bar_Test;
- emit newFooBarTestValue();
- }
- void set_fooBarTest_toDefault()
- {
- Seet_FooBar_test({}); // TODO: Adapt to use your actual default value
- }
-
-signals:
- void newFooBarTestValue();
-private:
- int mem_fooBar_test;
-};
-)-";
- QTest::addRow("create only member variable")
- << QByteArrayList{originalSource, expectedSource} << 0;
-
- // 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 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 FINAL)
- int mem_fooBar_test;
-public:
- int give_me_foo_bar_test() const
- {
- return mem_fooBar_test;
- }
- void Seet_FooBar_test(int New_Foo_Bar_Test)
- {
- if (mem_fooBar_test == New_Foo_Bar_Test)
- return;
- mem_fooBar_test = New_Foo_Bar_Test;
- emit newFooBarTestValue();
- }
- void set_fooBarTest_toDefault()
- {
- Seet_FooBar_test({}); // TODO: Adapt to use your actual default value
- }
-signals:
- void newFooBarTestValue();
-};
-)-";
- QTest::addRow("create methods with given member variable")
- << QByteArrayList{originalSource, expectedSource} << 0;
-}
-
-void QuickfixTest::testGenerateGetterSetterCustomNames()
-{
- QFETCH(QByteArrayList, headers);
- QFETCH(int, operation);
-
- QList<TestDocumentPtr> testDocuments(
- {CppTestDocument::create("file.h", headers.at(0), headers.at(1))});
-
- QuickFixSettings s;
- s->setterInCppFileFrom = 0;
- s->getterInCppFileFrom = 0;
- s->setterNameTemplate = "Seet_<Name>";
- s->getterNameTemplate = "give_me_<snake>";
- s->signalNameTemplate = "new<Camel>Value";
- s->setterParameterNameTemplate = "New_<Snake>";
- s->resetNameTemplate = "set_<camel>_toDefault";
- s->memberVariableNameTemplate = "mem_<name>";
- if (operation == 0) {
- InsertQtPropertyMembers factory;
- QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), operation);
- } else {
- GenerateGetterSetter factory;
- QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), operation);
- }
-}
-
-void QuickfixTest::testGenerateGetterSetterValueTypes_data()
-{
- QTest::addColumn<QByteArrayList>("headers");
- QTest::addColumn<int>("operation");
-
- QByteArray originalSource;
- QByteArray expectedSource;
-
- // int should be a value type
- originalSource = R"-(
-class Test {
- int i@;
-};
-)-";
- expectedSource = R"-(
-class Test {
- int i;
-
-public:
- int getI() const
- {
- return i;
- }
-};
-)-";
- QTest::addRow("int") << QByteArrayList{originalSource, expectedSource} << 1;
-
- // return type should be only int without const
- originalSource = R"-(
-class Test {
- const int i@;
-};
-)-";
- expectedSource = R"-(
-class Test {
- const int i;
-
-public:
- int getI() const
- {
- return i;
- }
-};
-)-";
- QTest::addRow("const int") << QByteArrayList{originalSource, expectedSource} << 0;
-
- // float should be a value type
- originalSource = R"-(
-class Test {
- float f@;
-};
-)-";
- expectedSource = R"-(
-class Test {
- float f;
-
-public:
- float getF() const
- {
- return f;
- }
-};
-)-";
- QTest::addRow("float") << QByteArrayList{originalSource, expectedSource} << 1;
-
- // pointer should be a value type
- originalSource = R"-(
-class Test {
- void* v@;
-};
-)-";
- expectedSource = R"-(
-class Test {
- void* v;
-
-public:
- void *getV() const
- {
- return v;
- }
-};
-)-";
- QTest::addRow("pointer") << QByteArrayList{originalSource, expectedSource} << 1;
-
- // reference should be a value type (setter is const ref)
- originalSource = R"-(
-class Test {
- int& r@;
-};
-)-";
- expectedSource = R"-(
-class Test {
- int& r;
-
-public:
- int &getR() const
- {
- return r;
- }
- void setR(const int &newR)
- {
- r = newR;
- }
-};
-)-";
- QTest::addRow("reference to value type") << QByteArrayList{originalSource, expectedSource} << 2;
-
- // reference should be a value type
- originalSource = R"-(
-using bar = int;
-class Test {
- bar i@;
-};
-)-";
- expectedSource = R"-(
-using bar = int;
-class Test {
- bar i;
-
-public:
- bar getI() const
- {
- return i;
- }
-};
-)-";
- QTest::addRow("value type through using") << QByteArrayList{originalSource, expectedSource} << 1;
-
- // enum should be a value type
- originalSource = R"-(
-enum Foo{V1, V2};
-class Test {
- Foo e@;
-};
-)-";
- expectedSource = R"-(
-enum Foo{V1, V2};
-class Test {
- Foo e;
-
-public:
- Foo getE() const
- {
- return e;
- }
-};
-)-";
- QTest::addRow("enum") << QByteArrayList{originalSource, expectedSource} << 1;
-
- // class should not be a value type
- originalSource = R"-(
-class NoVal{};
-class Test {
- NoVal n@;
-};
-)-";
- expectedSource = R"-(
-class NoVal{};
-class Test {
- NoVal n;
-
-public:
- const NoVal &getN() const
- {
- return n;
- }
-};
-)-";
- QTest::addRow("class") << QByteArrayList{originalSource, expectedSource} << 1;
-
- // custom classes can be a value type
- originalSource = R"-(
-class Value{};
-class Test {
- Value v@;
-};
-)-";
- expectedSource = R"-(
-class Value{};
-class Test {
- Value v;
-
-public:
- Value getV() const
- {
- return v;
- }
-};
-)-";
- QTest::addRow("value class") << QByteArrayList{originalSource, expectedSource} << 1;
-
- // custom classes (in namespace) can be a value type
- originalSource = R"-(
-namespace N1{
-class Value{};
-}
-class Test {
- N1::Value v@;
-};
-)-";
- expectedSource = R"-(
-namespace N1{
-class Value{};
-}
-class Test {
- N1::Value v;
-
-public:
- N1::Value getV() const
- {
- return v;
- }
-};
-)-";
- QTest::addRow("value class in namespace") << QByteArrayList{originalSource, expectedSource} << 1;
-
- // custom template class can be a value type
- originalSource = R"-(
-template<typename T>
-class Value{};
-class Test {
- Value<int> v@;
-};
-)-";
- expectedSource = R"-(
-template<typename T>
-class Value{};
-class Test {
- Value<int> v;
-
-public:
- Value<int> getV() const
- {
- return v;
- }
-};
-)-";
- QTest::addRow("value template class") << QByteArrayList{originalSource, expectedSource} << 1;
-}
-
-void QuickfixTest::testGenerateGetterSetterValueTypes()
-{
- QFETCH(QByteArrayList, headers);
- QFETCH(int, operation);
-
- QList<TestDocumentPtr> testDocuments(
- {CppTestDocument::create("file.h", headers.at(0), headers.at(1))});
-
- QuickFixSettings s;
- s->setterInCppFileFrom = 0;
- s->getterInCppFileFrom = 0;
- s->getterNameTemplate = "get<Name>";
- s->valueTypes << "Value";
- s->returnByConstRef = true;
-
- GenerateGetterSetter factory;
- QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), operation);
-}
-
-/// Checks: Use template for a custom type
-void QuickfixTest::testGenerateGetterSetterCustomTemplate()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- const _ customTypeDecl = R"--(
-namespace N1 {
-namespace N2 {
-struct test{};
-}
-template<typename T>
-struct custom {
- void assign(const custom<T>&);
- bool equals(const custom<T>&);
- T* get();
-};
-)--";
- // Header File
- original = customTypeDecl + R"--(
-class Foo
-{
-public:
- custom<N2::test> bar@;
-};
-})--";
- expected = customTypeDecl + R"--(
-class Foo
-{
-public:
- custom<N2::test> bar@;
- N2::test *getBar() const;
- void setBar(const custom<N2::test> &newBar);
-signals:
- void barChanged(N2::test *bar);
-private:
- Q_PROPERTY(N2::test *bar READ getBar NOTIFY barChanged FINAL)
-};
-})--";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original = "";
- expected = R"-(
-using namespace N1;
-N2::test *Foo::getBar() const
-{
- return bar.get();
-}
-
-void Foo::setBar(const custom<N2::test> &newBar)
-{
- if (bar.equals(newBar))
- return;
- bar.assign(newBar);
- emit barChanged(bar.get());
-}
-)-";
-
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- QuickFixSettings s;
- s->cppFileNamespaceHandling = CppQuickFixSettings::MissingNamespaceHandling::AddUsingDirective;
- s->getterNameTemplate = "get<Name>";
- s->getterInCppFileFrom = 1;
- s->signalWithNewValue = true;
- CppQuickFixSettings::CustomTemplate t;
- t.types.append("custom");
- t.equalComparison = "<cur>.equals(<new>)";
- t.returnExpression = "<cur>.get()";
- t.returnType = "<T> *";
- t.assignment = "<cur>.assign(<new>)";
- s->customTemplates.push_back(t);
-
- GenerateGetterSetter factory;
- QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 5);
-}
-
-/// Checks: if the setter parameter name is the same as the member variable name, this-> is needed
-void QuickfixTest::testGenerateGetterSetterNeedThis()
-{
- QList<TestDocumentPtr> testDocuments;
-
- // Header File
- const QByteArray original = R"-(
-class Foo {
- int bar@;
-public:
-};
-)-";
- const QByteArray expected = R"-(
-class Foo {
- int bar@;
-public:
- void setBar(int bar)
- {
- this->bar = bar;
- }
-};
-)-";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- QuickFixSettings s;
- s->setterParameterNameTemplate = "<name>";
- s->setterInCppFileFrom = 0;
-
- GenerateGetterSetter factory;
- QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 0);
-}
-
-void QuickfixTest::testGenerateGetterSetterOfferedFixes_data()
-{
- QTest::addColumn<QByteArray>("header");
- QTest::addColumn<QStringList>("offered");
-
- QByteArray header;
- QStringList offered;
- const QString setter = QStringLiteral("Generate Setter");
- const QString getter = QStringLiteral("Generate Getter");
- const QString getset = QStringLiteral("Generate Getter and Setter");
- const QString constQandMissing = QStringLiteral(
- "Generate Constant Q_PROPERTY and Missing Members");
- const QString qAndResetAndMissing = QStringLiteral(
- "Generate Q_PROPERTY and Missing Members with Reset Function");
- const QString qAndMissing = QStringLiteral("Generate Q_PROPERTY and Missing Members");
- const QStringList all{setter, getter, getset, constQandMissing, qAndResetAndMissing, qAndMissing};
-
- header = R"-(
-class Foo {
- static int bar@;
-};
-)-";
- offered = QStringList{setter, getter, getset, constQandMissing};
- QTest::addRow("static") << header << offered;
-
- header = R"-(
-class Foo {
- static const int bar@;
-};
-)-";
- offered = QStringList{getter, constQandMissing};
- QTest::addRow("const static") << header << offered;
-
- header = R"-(
-class Foo {
- const int bar@;
-};
-)-";
- offered = QStringList{getter, constQandMissing};
- QTest::addRow("const") << header << offered;
-
- header = R"-(
-class Foo {
- const int bar@;
- int getBar() const;
-};
-)-";
- offered = QStringList{constQandMissing};
- QTest::addRow("const + getter") << header << offered;
-
- header = R"-(
-class Foo {
- const int bar@;
- int getBar() const;
- void setBar(int value);
-};
-)-";
- offered = QStringList{};
- QTest::addRow("const + getter + setter") << header << offered;
-
- header = R"-(
-class Foo {
- const int* bar@;
-};
-)-";
- offered = all;
- QTest::addRow("pointer to const") << header << offered;
-
- header = R"-(
-class Foo {
- int bar@;
-public:
- int bar();
-};
-)-";
- offered = QStringList{setter, constQandMissing, qAndResetAndMissing, qAndMissing};
- QTest::addRow("existing getter") << header << offered;
-
- header = R"-(
-class Foo {
- int bar@;
-public:
- set setBar(int);
-};
-)-";
- offered = QStringList{getter};
- QTest::addRow("existing setter") << header << offered;
-
- header = R"-(
-class Foo {
- int bar@;
-signals:
- void barChanged(int);
-};
-)-";
- offered = QStringList{setter, getter, getset, qAndResetAndMissing, qAndMissing};
- QTest::addRow("existing signal (no const Q_PROPERTY)") << header << offered;
-
- header = R"-(
-class Foo {
- int m_bar@;
- Q_PROPERTY(int bar)
-};
-)-";
- offered = QStringList{}; // user should use "InsertQPropertyMembers", no duplicated code
- QTest::addRow("existing Q_PROPERTY") << header << offered;
-}
-
-void QuickfixTest::testGenerateGetterSetterOfferedFixes()
-{
- QFETCH(QByteArray, header);
- QFETCH(QStringList, offered);
-
- QList<TestDocumentPtr> testDocuments(
- {CppTestDocument::create("file.h", header, header)});
-
- GenerateGetterSetter factory;
- QuickFixOfferedOperationsTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), offered);
-}
-
-void QuickfixTest::testGenerateGetterSetterGeneralTests_data()
-{
- QTest::addColumn<int>("operation");
- QTest::addColumn<QByteArray>("original");
- QTest::addColumn<QByteArray>("expected");
-
- QTest::newRow("GenerateGetterSetter_referenceToNonConst")
- << 2
- << _("\n"
- "class Something\n"
- "{\n"
- " int &it@;\n"
- "};\n")
- << _("\n"
- "class Something\n"
- "{\n"
- " int &it;\n"
- "\n"
- "public:\n"
- " int &getIt() const;\n"
- " void setIt(const int &it);\n"
- "};\n"
- "\n"
- "int &Something::getIt() const\n"
- "{\n"
- " return it;\n"
- "}\n"
- "\n"
- "void Something::setIt(const int &it)\n"
- "{\n"
- " this->it = it;\n"
- "}\n");
-
- // Checks: No special treatment for reference to const.
- QTest::newRow("GenerateGetterSetter_referenceToConst")
- << 2
- << _("\n"
- "class Something\n"
- "{\n"
- " const int &it@;\n"
- "};\n")
- << _("\n"
- "class Something\n"
- "{\n"
- " const int &it;\n"
- "\n"
- "public:\n"
- " const int &getIt() const;\n"
- " void setIt(const int &it);\n"
- "};\n"
- "\n"
- "const int &Something::getIt() const\n"
- "{\n"
- " return it;\n"
- "}\n"
- "\n"
- "void Something::setIt(const int &it)\n"
- "{\n"
- " this->it = it;\n"
- "}\n");
-
- // Checks:
- // 1. Setter: Setter is a static function.
- // 2. Getter: Getter is a static, non const function.
- QTest::newRow("GenerateGetterSetter_staticMember")
- << 2
- << _("\n"
- "class Something\n"
- "{\n"
- " static int @m_member;\n"
- "};\n")
- << _("\n"
- "class Something\n"
- "{\n"
- " static int m_member;\n"
- "\n"
- "public:\n"
- " static int member();\n"
- " static void setMember(int member);\n"
- "};\n"
- "\n"
- "int Something::member()\n"
- "{\n"
- " return m_member;\n"
- "}\n"
- "\n"
- "void Something::setMember(int member)\n"
- "{\n"
- " m_member = member;\n"
- "}\n");
-
- // Check: Check if it works on the second declarator
- // clang-format off
- QTest::newRow("GenerateGetterSetter_secondDeclarator") << 2
- << _("\n"
- "class Something\n"
- "{\n"
- " int *foo, @it;\n"
- "};\n")
- << _("\n"
- "class Something\n"
- "{\n"
- " int *foo, it;\n"
- "\n"
- "public:\n"
- " int getIt() const;\n"
- " void setIt(int it);\n"
- "};\n"
- "\n"
- "int Something::getIt() const\n"
- "{\n"
- " return it;\n"
- "}\n"
- "\n"
- "void Something::setIt(int it)\n"
- "{\n"
- " this->it = it;\n"
- "}\n");
- // clang-format on
-
- // Check: Quick fix is offered for "int *@it;" ('@' denotes the text cursor position)
- QTest::newRow("GenerateGetterSetter_triggeringRightAfterPointerSign")
- << 2
- << _("\n"
- "class Something\n"
- "{\n"
- " int *@it;\n"
- "};\n")
- << _("\n"
- "class Something\n"
- "{\n"
- " int *it;\n"
- "\n"
- "public:\n"
- " int *getIt() const;\n"
- " void setIt(int *it);\n"
- "};\n"
- "\n"
- "int *Something::getIt() const\n"
- "{\n"
- " return it;\n"
- "}\n"
- "\n"
- "void Something::setIt(int *it)\n"
- "{\n"
- " this->it = it;\n"
- "}\n");
-
- // Checks if "m_" is recognized as "m" with the postfix "_" and not simply as "m_" prefix.
- QTest::newRow("GenerateGetterSetter_recognizeMasVariableName")
- << 2
- << _("\n"
- "class Something\n"
- "{\n"
- " int @m_;\n"
- "};\n")
- << _("\n"
- "class Something\n"
- "{\n"
- " int m_;\n"
- "\n"
- "public:\n"
- " int m() const;\n"
- " void setM(int m);\n"
- "};\n"
- "\n"
- "int Something::m() const\n"
- "{\n"
- " return m_;\n"
- "}\n"
- "\n"
- "void Something::setM(int m)\n"
- "{\n"
- " m_ = m;\n"
- "}\n");
-
- // Checks if "m" followed by an upper character is recognized as a prefix
- QTest::newRow("GenerateGetterSetter_recognizeMFollowedByCapital")
- << 2
- << _("\n"
- "class Something\n"
- "{\n"
- " int @mFoo;\n"
- "};\n")
- << _("\n"
- "class Something\n"
- "{\n"
- " int mFoo;\n"
- "\n"
- "public:\n"
- " int foo() const;\n"
- " void setFoo(int foo);\n"
- "};\n"
- "\n"
- "int Something::foo() const\n"
- "{\n"
- " return mFoo;\n"
- "}\n"
- "\n"
- "void Something::setFoo(int foo)\n"
- "{\n"
- " mFoo = foo;\n"
- "}\n");
-}
-void QuickfixTest::testGenerateGetterSetterGeneralTests()
-{
- QFETCH(int, operation);
- QFETCH(QByteArray, original);
- QFETCH(QByteArray, expected);
-
- QuickFixSettings s;
- s->setterParameterNameTemplate = "<name>";
- s->getterInCppFileFrom = 1;
- s->setterInCppFileFrom = 1;
-
- GenerateGetterSetter factory;
- QuickFixOperationTest(singleDocument(original, expected),
- &factory,
- ProjectExplorer::HeaderPaths(),
- operation);
-}
-/// Checks: Only generate getter
-void QuickfixTest::testGenerateGetterSetterOnlyGetter()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "class Foo\n"
- "{\n"
- "public:\n"
- " int bar@;\n"
- "};\n";
- expected =
- "class Foo\n"
- "{\n"
- "public:\n"
- " int bar@;\n"
- " int getBar() const;\n"
- "};\n";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original.resize(0);
- expected =
- "\n"
- "int Foo::getBar() const\n"
- "{\n"
- " return bar;\n"
- "}\n";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- QuickFixSettings s;
- s->getterInCppFileFrom = 1;
- s->getterNameTemplate = "get<Name>";
- GenerateGetterSetter factory;
- QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 1);
-}
-
-/// Checks: Only generate setter
-void QuickfixTest::testGenerateGetterSetterOnlySetter()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
- QuickFixSettings s;
- s->setterAsSlot = true; // To be ignored, as we don't have QObjects here.
-
- // Header File
- original =
- "class Foo\n"
- "{\n"
- "public:\n"
- " int bar@;\n"
- "};\n";
- expected =
- "class Foo\n"
- "{\n"
- "public:\n"
- " int bar@;\n"
- " void setBar(int value);\n"
- "};\n";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original.resize(0);
- expected =
- "\n"
- "void Foo::setBar(int value)\n"
- "{\n"
- " bar = value;\n"
- "}\n";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- s->setterInCppFileFrom = 1;
- s->setterParameterNameTemplate = "value";
-
- GenerateGetterSetter factory;
- QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 0);
-}
-
-void QuickfixTest::testGenerateGetterSetterAnonymousClass()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
- QuickFixSettings s;
- s->setterInCppFileFrom = 1;
- s->setterParameterNameTemplate = "value";
-
- // Header File
- original = R"(
- class {
- int @m_foo;
- } bar;
-)";
- expected = R"(
- class {
- int m_foo;
-
- public:
- int foo() const
- {
- return m_foo;
- }
- void setFoo(int value)
- {
- if (m_foo == value)
- return;
- m_foo = value;
- emit fooChanged();
- }
- void resetFoo()
- {
- setFoo({}); // TODO: Adapt to use your actual default defaultValue
- }
-
- signals:
- void fooChanged();
-
- private:
- Q_PROPERTY(int foo READ foo WRITE setFoo RESET resetFoo NOTIFY fooChanged FINAL)
- } bar;
-)";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- testDocuments << CppTestDocument::create("file.cpp", {}, {});
-
- GenerateGetterSetter factory;
- QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 4);
-}
-
-void QuickfixTest::testGenerateGetterSetterInlineInHeaderFile()
-{
- QList<TestDocumentPtr> testDocuments;
- const QByteArray original = R"-(
-class Foo {
-public:
- int bar@;
-};
-)-";
- const QByteArray expected = R"-(
-class Foo {
-public:
- int bar;
- int getBar() const;
- void setBar(int value);
- void resetBar();
-signals:
- void barChanged();
-private:
- Q_PROPERTY(int bar READ getBar WRITE setBar RESET resetBar NOTIFY barChanged FINAL)
-};
-
-inline int Foo::getBar() const
-{
- return bar;
-}
-
-inline void Foo::setBar(int value)
-{
- if (bar == value)
- return;
- bar = value;
- emit barChanged();
-}
-
-inline void Foo::resetBar()
-{
- setBar({}); // TODO: Adapt to use your actual default defaultValue
-}
-)-";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- QuickFixSettings s;
- s->setterOutsideClassFrom = 1;
- s->getterOutsideClassFrom = 1;
- s->setterParameterNameTemplate = "value";
- s->getterNameTemplate = "get<Name>";
-
- GenerateGetterSetter factory;
- QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 4);
-}
-
-void QuickfixTest::testGenerateGetterSetterOnlySetterHeaderFileWithIncludeGuard()
-{
- QList<TestDocumentPtr> testDocuments;
- const QByteArray original =
- "#ifndef FILE__H__DECLARED\n"
- "#define FILE__H__DECLARED\n"
- "class Foo\n"
- "{\n"
- "public:\n"
- " int bar@;\n"
- "};\n"
- "#endif\n";
- const QByteArray expected =
- "#ifndef FILE__H__DECLARED\n"
- "#define FILE__H__DECLARED\n"
- "class Foo\n"
- "{\n"
- "public:\n"
- " int bar@;\n"
- " void setBar(int value);\n"
- "};\n\n"
- "inline void Foo::setBar(int value)\n"
- "{\n"
- " bar = value;\n"
- "}\n"
- "#endif\n";
-
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- QuickFixSettings s;
- s->setterOutsideClassFrom = 1;
- s->setterParameterNameTemplate = "value";
-
- GenerateGetterSetter factory;
- QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 0);
-}
-
-void QuickfixTest::testGenerateGetterFunctionAsTemplateArg()
-{
- QList<TestDocumentPtr> testDocuments;
- const QByteArray original = R"(
-template<typename T> class TS {};
-template<typename T, typename U> class TS<T(U)> {};
-
-class S2 {
- TS<int(int)> @member;
-};
-)";
- const QByteArray expected = R"(
-template<typename T> class TS {};
-template<typename T, typename U> class TS<T(U)> {};
-
-class S2 {
- TS<int(int)> member;
-
-public:
- const TS<int (int)> &getMember() const
- {
- return member;
- }
-};
-)";
-
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- QuickFixSettings s;
- s->getterOutsideClassFrom = 0;
- s->getterInCppFileFrom = 0;
- s->getterNameTemplate = "get<Name>";
- s->returnByConstRef = true;
-
- GenerateGetterSetter factory;
- QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 1);
-}
-
-class CppCodeStyleSettingsChanger {
-public:
- CppCodeStyleSettingsChanger(const CppCodeStyleSettings &settings);
- ~CppCodeStyleSettingsChanger(); // Restore original
-
- static CppCodeStyleSettings currentSettings();
-
-private:
- void setSettings(const CppCodeStyleSettings &settings);
-
- CppCodeStyleSettings m_originalSettings;
-};
-
-CppCodeStyleSettingsChanger::CppCodeStyleSettingsChanger(const CppCodeStyleSettings &settings)
-{
- m_originalSettings = currentSettings();
- setSettings(settings);
-}
-
-CppCodeStyleSettingsChanger::~CppCodeStyleSettingsChanger()
-{
- setSettings(m_originalSettings);
-}
-
-void CppCodeStyleSettingsChanger::setSettings(const CppCodeStyleSettings &settings)
-{
- QVariant variant;
- variant.setValue(settings);
-
- CppToolsSettings::cppCodeStyle()->currentDelegate()->setValue(variant);
-}
-
-CppCodeStyleSettings CppCodeStyleSettingsChanger::currentSettings()
-{
- return CppToolsSettings::cppCodeStyle()->currentDelegate()->value().value<CppCodeStyleSettings>();
-}
-
-void QuickfixTest::testGenerateGettersSetters_data()
-{
- QTest::addColumn<QByteArray>("original");
- QTest::addColumn<QByteArray>("expected");
-
- const QByteArray onlyReset = R"(
-class Foo {
-public:
- int bar() const;
- void setBar(int bar);
-private:
- int m_bar;
-@};)";
-
- const QByteArray onlyResetAfter = R"(
-class @Foo {
-public:
- int bar() const;
- void setBar(int bar);
- void resetBar();
-
-private:
- int m_bar;
-};
-inline void Foo::resetBar()
-{
- setBar({}); // TODO: Adapt to use your actual default defaultValue
-}
-)";
- QTest::addRow("only reset") << onlyReset << onlyResetAfter;
-
- const QByteArray withCandidates = R"(
-class @Foo {
-public:
- int bar() const;
- void setBar(int bar) { m_bar = bar; }
-
- int getBar2() const;
-
- int m_alreadyPublic;
-
-private:
- friend void distraction();
- class AnotherDistraction {};
- enum EvenMoreDistraction { val1, val2 };
-
- int m_bar;
- int bar2_;
- QString bar3;
-};)";
- const QByteArray after = R"(
-class Foo {
-public:
- int bar() const;
- void setBar(int bar) { m_bar = bar; }
-
- int getBar2() const;
-
- int m_alreadyPublic;
-
- void resetBar();
- void setBar2(int value);
- void resetBar2();
- const QString &getBar3() const;
- void setBar3(const QString &value);
- void resetBar3();
-
-signals:
- void bar2Changed();
- void bar3Changed();
-
-private:
- friend void distraction();
- class AnotherDistraction {};
- enum EvenMoreDistraction { val1, val2 };
-
- int m_bar;
- int bar2_;
- QString bar3;
- 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()
-{
- setBar({}); // TODO: Adapt to use your actual default defaultValue
-}
-
-inline void Foo::setBar2(int value)
-{
- if (bar2_ == value)
- return;
- bar2_ = value;
- emit bar2Changed();
-}
-
-inline void Foo::resetBar2()
-{
- setBar2({}); // TODO: Adapt to use your actual default defaultValue
-}
-
-inline const QString &Foo::getBar3() const
-{
- return bar3;
-}
-
-inline void Foo::setBar3(const QString &value)
-{
- if (bar3 == value)
- return;
- bar3 = value;
- emit bar3Changed();
-}
-
-inline void Foo::resetBar3()
-{
- setBar3({}); // TODO: Adapt to use your actual default defaultValue
-}
-)";
- QTest::addRow("with candidates") << withCandidates << after;
-}
-
-void QuickfixTest::testGenerateGettersSetters()
-{
- class TestFactory : public GenerateGettersSettersForClass
- {
- public:
- TestFactory() { setTest(); }
- };
-
- QFETCH(QByteArray, original);
- QFETCH(QByteArray, expected);
-
- QuickFixSettings s;
- s->getterNameTemplate = "get<Name>";
- s->setterParameterNameTemplate = "value";
- s->setterOutsideClassFrom = 1;
- s->getterOutsideClassFrom = 1;
- s->returnByConstRef = true;
-
- TestFactory factory;
- QuickFixOperationTest({CppTestDocument::create("file.h", original, expected)}, &factory);
-}
-
-void QuickfixTest::testInsertQtPropertyMembers_data()
-{
- QTest::addColumn<QByteArray>("original");
- QTest::addColumn<QByteArray>("expected");
-
- QTest::newRow("InsertQtPropertyMembers")
- << _("struct QObject { void connect(); }\n"
- "struct XmarksTheSpot : public QObject {\n"
- " @Q_PROPERTY(int it READ getIt WRITE setIt RESET resetIt NOTIFY itChanged)\n"
- "};\n")
- << _("struct QObject { void connect(); }\n"
- "struct XmarksTheSpot : public QObject {\n"
- " Q_PROPERTY(int it READ getIt WRITE setIt RESET resetIt NOTIFY itChanged)\n"
- "\n"
- "public:\n"
- " int getIt() const;\n"
- "\n"
- "public slots:\n"
- " void setIt(int it)\n"
- " {\n"
- " if (m_it == it)\n"
- " return;\n"
- " m_it = it;\n"
- " emit itChanged(m_it);\n"
- " }\n"
- " void resetIt()\n"
- " {\n"
- " setIt({}); // TODO: Adapt to use your actual default value\n"
- " }\n"
- "\n"
- "signals:\n"
- " void itChanged(int it);\n"
- "\n"
- "private:\n"
- " int m_it;\n"
- "};\n"
- "\n"
- "int XmarksTheSpot::getIt() const\n"
- "{\n"
- " return m_it;\n"
- "}\n");
-
- QTest::newRow("InsertQtPropertyMembersResetWithoutSet")
- << _("struct QObject { void connect(); }\n"
- "struct XmarksTheSpot : public QObject {\n"
- " @Q_PROPERTY(int it READ getIt RESET resetIt NOTIFY itChanged)\n"
- "};\n")
- << _("struct QObject { void connect(); }\n"
- "struct XmarksTheSpot : public QObject {\n"
- " Q_PROPERTY(int it READ getIt RESET resetIt NOTIFY itChanged)\n"
- "\n"
- "public:\n"
- " int getIt() const;\n"
- "\n"
- "public slots:\n"
- " void resetIt()\n"
- " {\n"
- " static int defaultValue{}; // TODO: Adapt to use your actual default "
- "value\n"
- " if (m_it == defaultValue)\n"
- " return;\n"
- " m_it = defaultValue;\n"
- " emit itChanged(m_it);\n"
- " }\n"
- "\n"
- "signals:\n"
- " void itChanged(int it);\n"
- "\n"
- "private:\n"
- " int m_it;\n"
- "};\n"
- "\n"
- "int XmarksTheSpot::getIt() const\n"
- "{\n"
- " return m_it;\n"
- "}\n");
-
- QTest::newRow("InsertQtPropertyMembersResetWithoutSetAndNotify")
- << _("struct QObject { void connect(); }\n"
- "struct XmarksTheSpot : public QObject {\n"
- " @Q_PROPERTY(int it READ getIt RESET resetIt)\n"
- "};\n")
- << _("struct QObject { void connect(); }\n"
- "struct XmarksTheSpot : public QObject {\n"
- " Q_PROPERTY(int it READ getIt RESET resetIt)\n"
- "\n"
- "public:\n"
- " int getIt() const;\n"
- "\n"
- "public slots:\n"
- " void resetIt()\n"
- " {\n"
- " static int defaultValue{}; // TODO: Adapt to use your actual default "
- "value\n"
- " m_it = defaultValue;\n"
- " }\n"
- "\n"
- "private:\n"
- " int m_it;\n"
- "};\n"
- "\n"
- "int XmarksTheSpot::getIt() const\n"
- "{\n"
- " return m_it;\n"
- "}\n");
-
- QTest::newRow("InsertQtPropertyMembersPrivateBeforePublic")
- << _("struct QObject { void connect(); }\n"
- "class XmarksTheSpot : public QObject {\n"
- "private:\n"
- " @Q_PROPERTY(int it READ getIt WRITE setIt NOTIFY itChanged)\n"
- "public:\n"
- " void find();\n"
- "};\n")
- << _("struct QObject { void connect(); }\n"
- "class XmarksTheSpot : public QObject {\n"
- "private:\n"
- " Q_PROPERTY(int it READ getIt WRITE setIt NOTIFY itChanged)\n"
- " int m_it;\n"
- "\n"
- "public:\n"
- " void find();\n"
- " int getIt() const;\n"
- "public slots:\n"
- " void setIt(int it)\n"
- " {\n"
- " if (m_it == it)\n"
- " return;\n"
- " m_it = it;\n"
- " emit itChanged(m_it);\n"
- " }\n"
- "signals:\n"
- " void itChanged(int it);\n"
- "};\n"
- "\n"
- "int XmarksTheSpot::getIt() const\n"
- "{\n"
- " return m_it;\n"
- "}\n");
-}
-
-void QuickfixTest::testInsertQtPropertyMembers()
-{
- QFETCH(QByteArray, original);
- QFETCH(QByteArray, expected);
-
- QuickFixSettings s;
- s->setterAsSlot = true;
- s->setterInCppFileFrom = 0;
- s->setterParameterNameTemplate = "<name>";
- s->signalWithNewValue = true;
-
- InsertQtPropertyMembers factory;
- QuickFixOperationTest({CppTestDocument::create("file.cpp", original, expected)}, &factory);
-}
-
-void QuickfixTest::testInsertMemberFromUse_data()
-{
- QTest::addColumn<QByteArray>("original");
- QTest::addColumn<QByteArray>("expected");
-
- QByteArray original;
- QByteArray expected;
-
- original =
- "class C {\n"
- "public:\n"
- " C(int x) : @m_x(x) {}\n"
- "private:\n"
- " int m_y;\n"
- "};\n";
- expected =
- "class C {\n"
- "public:\n"
- " C(int x) : m_x(x) {}\n"
- "private:\n"
- " int m_y;\n"
- " int m_x;\n"
- "};\n";
- QTest::addRow("inline constructor") << original << expected;
-
- original =
- "class C {\n"
- "public:\n"
- " C(int x, double d);\n"
- "private:\n"
- " int m_x;\n"
- "};\n"
- "C::C(int x, double d) : m_x(x), @m_d(d)\n";
- expected =
- "class C {\n"
- "public:\n"
- " C(int x, double d);\n"
- "private:\n"
- " int m_x;\n"
- " double m_d;\n"
- "};\n"
- "C::C(int x, double d) : m_x(x), m_d(d)\n";
- QTest::addRow("out-of-line constructor") << original << expected;
-
- original =
- "class C {\n"
- "public:\n"
- " C(int x) : @m_x(x) {}\n"
- "private:\n"
- " int m_x;\n"
- "};\n";
- expected = "";
- QTest::addRow("member already present") << original << expected;
-
- original =
- "int func() { return 0; }\n"
- "class C {\n"
- "public:\n"
- " C() : @m_x(func()) {}\n"
- "private:\n"
- " int m_y;\n"
- "};\n";
- expected =
- "int func() { return 0; }\n"
- "class C {\n"
- "public:\n"
- " C() : m_x(func()) {}\n"
- "private:\n"
- " int m_y;\n"
- " 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"
- " int value() const { return @valueInternal(); }\n"
- "};\n";
- expected =
- "class C {\n"
- "public:\n"
- " int value() const { return valueInternal(); }\n"
- "private:\n"
- " int valueInternal() const;\n"
- "};\n";
- QTest::addRow("add const 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::testInsertMemberFromUse()
-{
- QFETCH(QByteArray, original);
- QFETCH(QByteArray, expected);
-
- QList<TestDocumentPtr> testDocuments({
- CppTestDocument::create("file.h", original, expected)
- });
-
- AddDeclarationForUndeclaredIdentifier factory;
- factory.setMembersOnly();
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-/// Check if definition is inserted right after class for insert definition outside
-void QuickfixTest::testInsertDefFromDeclAfterClass()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "class Foo\n"
- "{\n"
- " Foo();\n"
- " void a@();\n"
- "};\n"
- "\n"
- "class Bar {};\n";
- expected =
- "class Foo\n"
- "{\n"
- " Foo();\n"
- " void a();\n"
- "};\n"
- "\n"
- "inline void Foo::a()\n"
- "{\n\n}\n"
- "\n"
- "class Bar {};\n";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n"
- "\n"
- "Foo::Foo()\n"
- "{\n\n"
- "}\n";
- expected = original;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 1);
-}
-
-/// Check from header file: If there is a source file, insert the definition in the source file.
-/// Case: Source file is empty.
-void QuickfixTest::testInsertDefFromDeclHeaderSourceBasic1()
-{
- QList<TestDocumentPtr> testDocuments;
-
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "struct Foo\n"
- "{\n"
- " Foo()@;\n"
- "};\n";
- expected = original;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original.resize(0);
- expected =
- "\n"
- "Foo::Foo()\n"
- "{\n\n"
- "}\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-/// Check from header file: If there is a source file, insert the definition in the source file.
-/// Case: Source file is not empty.
-void QuickfixTest::testInsertDefFromDeclHeaderSourceBasic2()
-{
- QList<TestDocumentPtr> testDocuments;
-
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original = "void f(const std::vector<int> &v)@;\n";
- expected = original;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n"
- "\n"
- "int x;\n"
- ;
- expected =
- "#include \"file.h\"\n"
- "\n"
- "int x;\n"
- "\n"
- "void f(const std::vector<int> &v)\n"
- "{\n"
- "\n"
- "}\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-/// Check from source file: Insert in source file, not header file.
-void QuickfixTest::testInsertDefFromDeclHeaderSourceBasic3()
-{
- QList<TestDocumentPtr> testDocuments;
-
- QByteArray original;
- QByteArray expected;
-
- // Empty Header File
- testDocuments << CppTestDocument::create("file.h", "", "");
-
- // Source File
- original =
- "struct Foo\n"
- "{\n"
- " Foo()@;\n"
- "};\n";
- expected = original +
- "\n"
- "Foo::Foo()\n"
- "{\n\n"
- "}\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-/// Check from header file: If the class is in a namespace, the added function definition
-/// name must be qualified accordingly.
-void QuickfixTest::testInsertDefFromDeclHeaderSourceNamespace1()
-{
- QList<TestDocumentPtr> testDocuments;
-
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "namespace N {\n"
- "struct Foo\n"
- "{\n"
- " Foo()@;\n"
- "};\n"
- "}\n";
- expected = original;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original.resize(0);
- expected =
- "\n"
- "N::Foo::Foo()\n"
- "{\n\n"
- "}\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-/// Check from header file: If the class is in namespace N and the source file has a
-/// "using namespace N" line, the function definition name must be qualified accordingly.
-void QuickfixTest::testInsertDefFromDeclHeaderSourceNamespace2()
-{
- QList<TestDocumentPtr> testDocuments;
-
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "namespace N {\n"
- "struct Foo\n"
- "{\n"
- " Foo()@;\n"
- "};\n"
- "}\n";
- expected = original;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n"
- "using namespace N;\n"
- ;
- expected = original +
- "\n"
- "Foo::Foo()\n"
- "{\n\n"
- "}\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-/// Check definition insert inside class
-void QuickfixTest::testInsertDefFromDeclInsideClass()
-{
- const QByteArray original =
- "class Foo {\n"
- " void b@ar();\n"
- "};";
- const QByteArray expected =
- "class Foo {\n"
- " void bar()\n"
- " {\n\n"
- " }\n"
- "};";
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory, ProjectExplorer::HeaderPaths(),
- 1);
-}
-
-/// Check not triggering when definition exists
-void QuickfixTest::testInsertDefFromDeclNotTriggeringWhenDefinitionExists()
-{
- const QByteArray original =
- "class Foo {\n"
- " void b@ar();\n"
- "};\n"
- "void Foo::bar() {}\n";
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(singleDocument(original, ""), &factory, ProjectExplorer::HeaderPaths(), 1);
-}
-
-/// Find right implementation file.
-void QuickfixTest::testInsertDefFromDeclFindRightImplementationFile()
-{
- QList<TestDocumentPtr> testDocuments;
-
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "struct Foo\n"
- "{\n"
- " Foo();\n"
- " void a();\n"
- " void b@();\n"
- "};\n"
- "}\n";
- expected = original;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File #1
- original =
- "#include \"file.h\"\n"
- "\n"
- "Foo::Foo()\n"
- "{\n\n"
- "}\n";
- expected = original;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
-
- // Source File #2
- original =
- "#include \"file.h\"\n"
- "\n"
- "void Foo::a()\n"
- "{\n\n"
- "}\n";
- expected = original +
- "\n"
- "void Foo::b()\n"
- "{\n\n"
- "}\n";
- testDocuments << CppTestDocument::create("file2.cpp", original, expected);
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-/// Ignore generated functions declarations when looking at the surrounding
-/// functions declarations in order to find the right implementation file.
-void QuickfixTest::testInsertDefFromDeclIgnoreSurroundingGeneratedDeclarations()
-{
- QList<TestDocumentPtr> testDocuments;
-
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "#define DECLARE_HIDDEN_FUNCTION void hidden();\n"
- "struct Foo\n"
- "{\n"
- " void a();\n"
- " DECLARE_HIDDEN_FUNCTION\n"
- " void b@();\n"
- "};\n"
- "}\n";
- expected = original;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File #1
- original =
- "#include \"file.h\"\n"
- "\n"
- "void Foo::a()\n"
- "{\n\n"
- "}\n";
- expected =
- "#include \"file.h\"\n"
- "\n"
- "void Foo::a()\n"
- "{\n\n"
- "}\n"
- "\n"
- "void Foo::b()\n"
- "{\n\n"
- "}\n";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- // Source File #2
- original =
- "#include \"file.h\"\n"
- "\n"
- "void Foo::hidden()\n"
- "{\n\n"
- "}\n";
- expected = original;
- testDocuments << CppTestDocument::create("file2.cpp", original, expected);
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-/// Check if whitespace is respected for operator functions
-void QuickfixTest::testInsertDefFromDeclRespectWsInOperatorNames1()
-{
- QByteArray original =
- "class Foo\n"
- "{\n"
- " Foo &opera@tor =();\n"
- "};\n";
- QByteArray expected =
- "class Foo\n"
- "{\n"
- " Foo &operator =();\n"
- "};\n"
- "\n"
- "Foo &Foo::operator =()\n"
- "{\n"
- "\n"
- "}\n";
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-}
-
-/// Check if whitespace is respected for operator functions
-void QuickfixTest::testInsertDefFromDeclRespectWsInOperatorNames2()
-{
- QByteArray original =
- "class Foo\n"
- "{\n"
- " Foo &opera@tor=();\n"
- "};\n";
- QByteArray expected =
- "class Foo\n"
- "{\n"
- " Foo &operator=();\n"
- "};\n"
- "\n"
- "Foo &Foo::operator=()\n"
- "{\n"
- "\n"
- "}\n";
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-}
-
-/// Check that the noexcept exception specifier is transferred
-void QuickfixTest::testInsertDefFromDeclNoexceptSpecifier()
-{
- QByteArray original =
- "class Foo\n"
- "{\n"
- " void @foo() noexcept(false);\n"
- "};\n";
- QByteArray expected =
- "class Foo\n"
- "{\n"
- " void foo() noexcept(false);\n"
- "};\n"
- "\n"
- "void Foo::foo() noexcept(false)\n"
- "{\n"
- "\n"
- "}\n";
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-}
-
-/// Check if a function like macro use is not separated by the function to insert
-/// Case: Macro preceded by preproceesor directives and declaration.
-void QuickfixTest::testInsertDefFromDeclMacroUsesAtEndOfFile1()
-{
- QList<TestDocumentPtr> testDocuments;
-
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original = "void f()@;\n";
- expected = original;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n"
- "#define MACRO(X) X x;\n"
- "int lala;\n"
- "\n"
- "MACRO(int)\n"
- ;
- expected =
- "#include \"file.h\"\n"
- "#define MACRO(X) X x;\n"
- "int lala;\n"
- "\n"
- "\n"
- "\n"
- "void f()\n"
- "{\n"
- "\n"
- "}\n"
- "\n"
- "MACRO(int)\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-/// Check if a function like macro use is not separated by the function to insert
-/// Case: Marco preceded only by preprocessor directives.
-void QuickfixTest::testInsertDefFromDeclMacroUsesAtEndOfFile2()
-{
- QList<TestDocumentPtr> testDocuments;
-
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original = "void f()@;\n";
- expected = original;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n"
- "#define MACRO(X) X x;\n"
- "\n"
- "MACRO(int)\n"
- ;
- expected =
- "#include \"file.h\"\n"
- "#define MACRO(X) X x;\n"
- "\n"
- "\n"
- "\n"
- "void f()\n"
- "{\n"
- "\n"
- "}\n"
- "\n"
- "MACRO(int)\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-/// Check if insertion happens before syntactically erroneous statements at end of file.
-void QuickfixTest::testInsertDefFromDeclErroneousStatementAtEndOfFile()
-{
- QList<TestDocumentPtr> testDocuments;
-
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original = "void f()@;\n";
- expected = original;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n"
- "\n"
- "MissingSemicolon(int)\n"
- ;
- expected =
- "#include \"file.h\"\n"
- "\n"
- "\n"
- "\n"
- "void f()\n"
- "{\n"
- "\n"
- "}\n"
- "\n"
- "MissingSemicolon(int)\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-/// Check: Respect rvalue references
-void QuickfixTest::testInsertDefFromDeclRvalueReference()
-{
- QList<TestDocumentPtr> testDocuments;
-
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original = "void f(Foo &&)@;\n";
- expected = original;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original = "";
- expected =
- "\n"
- "void f(Foo &&)\n"
- "{\n"
- "\n"
- "}\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testInsertDefFromDeclFunctionTryBlock()
-{
- QList<TestDocumentPtr> testDocuments;
-
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original = R"(
-struct Foo {
- void tryCatchFunc();
- void @otherFunc();
-};
-)";
- expected = original;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original = R"(
-#include "file.h"
-
-void Foo::tryCatchFunc() try {} catch (...) {}
-)";
- expected = R"(
-#include "file.h"
-
-void Foo::tryCatchFunc() try {} catch (...) {}
-
-void Foo::otherFunc()
-{
-
-}
-)";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testInsertDefFromDeclUsingDecl()
-{
- QList<TestDocumentPtr> testDocuments;
-
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original = R"(
-namespace N { struct S; }
-using N::S;
-
-void @func(const S &s);
-)";
- expected = original;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original = R"(
-#include "file.h"
-)";
- expected = R"(
-#include "file.h"
-
-void func(const S &s)
-{
-
-}
-)";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(testDocuments, &factory);
-
- testDocuments.clear();
- original = R"(
-namespace N1 {
-namespace N2 { struct S; }
-using N2::S;
-}
-
-void @func(const N1::S &s);
-)";
- expected = original;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original = R"(
-#include "file.h"
-)";
- expected = R"(
-#include "file.h"
-
-void func(const N1::S &s)
-{
-
-}
-)";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QuickFixOperationTest(testDocuments, &factory);
-
- // No using declarations here, but the code model has one. No idea why.
- testDocuments.clear();
- original = R"(
-class B {};
-class D : public B {
- @D();
-};
-)";
- expected = original;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original = R"(
-#include "file.h"
-)";
- expected = R"(
-#include "file.h"
-
-D::D()
-{
-
-}
-)";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QuickFixOperationTest(testDocuments, &factory);
-
- testDocuments.clear();
- original = R"(
-namespace ns1 { template<typename T> class span {}; }
-
-namespace ns {
-using ns1::span;
-class foo
-{
- void @bar(ns::span<int>);
-};
-}
-)";
- expected = R"(
-namespace ns1 { template<typename T> class span {}; }
-
-namespace ns {
-using ns1::span;
-class foo
-{
- void bar(ns::span<int>);
-};
-
-void foo::bar(ns::span<int>)
-{
-
-}
-
-}
-)";
- // TODO: Unneeded namespace gets inserted in RewriteName::visit(const QualifiedNameId *)
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-/// Find right implementation file. (QTCREATORBUG-10728)
-void QuickfixTest::testInsertDefFromDeclFindImplementationFile()
-{
- QList<TestDocumentPtr> testDocuments;
-
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "class Foo {\n"
- " void bar();\n"
- " void ba@z();\n"
- "};\n"
- "\n"
- "void Foo::bar()\n"
- "{}\n";
- expected = original;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n"
- ;
- expected =
- "#include \"file.h\"\n"
- "\n"
- "void Foo::baz()\n"
- "{\n"
- "\n"
- "}\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testInsertDefFromDeclUnicodeIdentifier()
-{
- QList<TestDocumentPtr> testDocuments;
-
- QByteArray original;
- QByteArray expected;
-
- //
- // The following "non-latin1" code points are used in the tests:
- //
- // U+00FC - 2 code units in UTF8, 1 in UTF16 - LATIN SMALL LETTER U WITH DIAERESIS
- // U+4E8C - 3 code units in UTF8, 1 in UTF16 - CJK UNIFIED IDEOGRAPH-4E8C
- // U+10302 - 4 code units in UTF8, 2 in UTF16 - OLD ITALIC LETTER KE
- //
-
-#define UNICODE_U00FC "\xc3\xbc"
-#define UNICODE_U4E8C "\xe4\xba\x8c"
-#define UNICODE_U10302 "\xf0\x90\x8c\x82"
-#define TEST_UNICODE_IDENTIFIER UNICODE_U00FC UNICODE_U4E8C UNICODE_U10302
-
- original =
- "class Foo {\n"
- " void @" TEST_UNICODE_IDENTIFIER "();\n"
- "};\n";
- ;
- expected = original;
- expected +=
- "\n"
- "void Foo::" TEST_UNICODE_IDENTIFIER "()\n"
- "{\n"
- "\n"
- "}\n";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
-#undef UNICODE_U00FC
-#undef UNICODE_U4E8C
-#undef UNICODE_U10302
-#undef TEST_UNICODE_IDENTIFIER
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testInsertDefFromDeclTemplateClass()
-{
- QByteArray original =
- "template<class T>\n"
- "class Foo\n"
- "{\n"
- " void fun@c1();\n"
- " void func2();\n"
- "};\n\n"
- "template<class T>\n"
- "void Foo<T>::func2() {}\n";
- QByteArray expected =
- "template<class T>\n"
- "class Foo\n"
- "{\n"
- " void func1();\n"
- " void func2();\n"
- "};\n\n"
- "template<class T>\n"
- "void Foo<T>::func1()\n"
- "{\n"
- "\n"
- "}\n\n"
- "template<class T>\n"
- "void Foo<T>::func2() {}\n";
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-}
-
-void QuickfixTest::testInsertDefFromDeclTemplateClassWithValueParam()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original =
- "template<typename T, int size> struct MyArray {};\n"
- "MyArray<int, 1> @foo();";
- QByteArray expected = original;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- original = "#include \"file.h\"\n";
- expected =
- "#include \"file.h\"\n\n"
- "MyArray<int, 1> foo()\n"
- "{\n\n"
- "}\n";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testInsertDefFromDeclTemplateFunction()
-{
- QByteArray original =
- "class Foo\n"
- "{\n"
- " template<class T>\n"
- " void fun@c();\n"
- "};\n";
- QByteArray expected =
- "class Foo\n"
- "{\n"
- " template<class T>\n"
- " void fun@c();\n"
- "};\n"
- "\n"
- "template<class T>\n"
- "void Foo::func()\n"
- "{\n"
- "\n"
- "}\n";
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-}
-
-void QuickfixTest::testInsertDefFromDeclTemplateClassAndTemplateFunction()
-{
- QByteArray original =
- "template<class T>"
- "class Foo\n"
- "{\n"
- " template<class U>\n"
- " T fun@c(U u);\n"
- "};\n";
- QByteArray expected =
- "template<class T>"
- "class Foo\n"
- "{\n"
- " template<class U>\n"
- " T fun@c(U u);\n"
- "};\n"
- "\n"
- "template<class T>\n"
- "template<class U>\n"
- "T Foo<T>::func(U u)\n"
- "{\n"
- "\n"
- "}\n";
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-}
-
-void QuickfixTest::testInsertDefFromDeclTemplateClassAndFunctionInsideNamespace()
-{
- QByteArray original =
- "namespace N {\n"
- "template<class T>"
- "class Foo\n"
- "{\n"
- " template<class U>\n"
- " T fun@c(U u);\n"
- "};\n"
- "}\n";
- QByteArray expected =
- "namespace N {\n"
- "template<class T>"
- "class Foo\n"
- "{\n"
- " template<class U>\n"
- " T fun@c(U u);\n"
- "};\n"
- "\n"
- "template<class T>\n"
- "template<class U>\n"
- "T Foo<T>::func(U u)\n"
- "{\n"
- "\n"
- "}\n"
- "\n"
- "}\n";
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-}
-
-void QuickfixTest::testInsertDefFromDeclFunctionWithSignedUnsignedArgument()
-{
- QByteArray original;
- QByteArray expected;
- InsertDefFromDecl factory;
-
- original =R"--(
-class myclass
-{
- myc@lass(QVector<signed> g);
- myclass(QVector<unsigned> g);
-}
-)--";
- expected =R"--(
-class myclass
-{
- myclass(QVector<signed> g);
- myclass(QVector<unsigned> g);
-}
-
-myclass::myclass(QVector<signed int> g)
-{
-
-}
-)--";
-
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-
- original =R"--(
-class myclass
-{
- myclass(QVector<signed> g);
- myc@lass(QVector<unsigned> g);
-}
-)--";
- expected =R"--(
-class myclass
-{
- myclass(QVector<signed> g);
- myclass(QVector<unsigned> g);
-}
-
-myclass::myclass(QVector<unsigned int> g)
-{
-
-}
-)--";
-
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-
- original =R"--(
-class myclass
-{
- unsigned f@oo(unsigned);
-}
-)--";
- expected =R"--(
-class myclass
-{
- unsigned foo(unsigned);
-}
-
-unsigned int myclass::foo(unsigned int)
-{
-
-}
-)--";
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-
- original =R"--(
-class myclass
-{
- signed f@oo(signed);
-}
-)--";
- expected =R"--(
-class myclass
-{
- signed foo(signed);
-}
-
-signed int myclass::foo(signed int)
-{
-
-}
-)--";
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-}
-
-void QuickfixTest::testInsertDefFromDeclNotTriggeredForFriendFunc()
-{
- const QByteArray contents =
- "class Foo\n"
- "{\n"
- " friend void f@unc();\n"
- "};\n"
- "\n";
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(singleDocument(contents, ""), &factory);
-}
-
-void QuickfixTest::testInsertDefFromDeclMinimalFunctionParameterType()
-{
- QList<TestDocumentPtr> testDocuments;
-
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original = R"(
-class C {
- typedef int A;
- A @foo(A);
-};
-)";
- expected = original;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original = R"(
-#include "file.h"
-)";
- expected = R"(
-#include "file.h"
-
-C::A C::foo(A)
-{
-
-}
-)";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(testDocuments, &factory);
-
- testDocuments.clear();
- // Header File
- original = R"(
-namespace N {
- struct S;
- S @foo(const S &s);
-};
-)";
- expected = original;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original = R"(
-#include "file.h"
-)";
- expected = R"(
-#include "file.h"
-
-N::S N::foo(const S &s)
-{
-
-}
-)";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testInsertDefFromDeclAliasTemplateAsReturnType()
-{
- QList<TestDocumentPtr> testDocuments;
-
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original = R"(
-struct foo {
- struct foo2 {
- template <typename T> using MyType = T;
- MyType<int> @bar();
- };
-};
-)";
- expected = original;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original = R"(
-#include "file.h"
-)";
- expected = R"(
-#include "file.h"
-
-foo::foo2::MyType<int> foo::foo2::bar()
-{
-
-}
-)";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- InsertDefFromDecl factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testInsertDefsFromDecls_data()
-{
- QTest::addColumn<QByteArrayList>("headers");
- QTest::addColumn<QByteArrayList>("sources");
- QTest::addColumn<int>("mode");
-
- QByteArray origHeader = R"(
-namespace N {
-class @C
-{
-public:
- friend void ignoredFriend();
- void ignoredImplemented() {};
- void ignoredImplemented2(); // Below
- void ignoredImplemented3(); // In cpp file
- void funcNotSelected();
- void funcInline();
- void funcBelow();
- void funcCppFile();
-
-signals:
- void ignoredSignal();
-};
-
-inline void C::ignoredImplemented2() {}
-
-} // namespace N)";
- QByteArray origSource = R"(
-#include "file.h"
-
-namespace N {
-
-void C::ignoredImplemented3() {}
-
-} // namespace N)";
-
- QByteArray expectedHeader = R"(
-namespace N {
-class C
-{
-public:
- friend void ignoredFriend();
- void ignoredImplemented() {};
- void ignoredImplemented2(); // Below
- void ignoredImplemented3(); // In cpp file
- void funcNotSelected();
- void funcInline()
- {
-
- }
- void funcBelow();
- void funcCppFile();
-
-signals:
- void ignoredSignal();
-};
-
-inline void C::ignoredImplemented2() {}
-
-inline void C::funcBelow()
-{
-
-}
-
-} // namespace N)";
- QByteArray expectedSource = R"(
-#include "file.h"
-
-namespace N {
-
-void C::ignoredImplemented3() {}
-
-void C::funcCppFile()
-{
-
-}
-
-} // namespace N)";
- QTest::addRow("normal case")
- << QByteArrayList{origHeader, expectedHeader}
- << QByteArrayList{origSource, expectedSource}
- << int(InsertDefsFromDecls::Mode::Alternating);
- QTest::addRow("aborted dialog")
- << QByteArrayList{origHeader, origHeader}
- << QByteArrayList{origSource, origSource}
- << int(InsertDefsFromDecls::Mode::Off);
-
- origHeader = R"(
- namespace N {
- class @C
- {
- public:
- friend void ignoredFriend();
- void ignoredImplemented() {};
- void ignoredImplemented2(); // Below
- void ignoredImplemented3(); // In cpp file
-
- signals:
- void ignoredSignal();
- };
-
- inline void C::ignoredImplemented2() {}
-
- } // namespace N)";
- QTest::addRow("no candidates")
- << QByteArrayList{origHeader, origHeader}
- << QByteArrayList{origSource, origSource}
- << int(InsertDefsFromDecls::Mode::Alternating);
-
- origHeader = R"(
- namespace N {
- class @C
- {
- public:
- friend void ignoredFriend();
- void ignoredImplemented() {};
-
- signals:
- void ignoredSignal();
- };
- } // namespace N)";
- QTest::addRow("no member functions")
- << QByteArrayList{origHeader, ""}
- << QByteArrayList{origSource, ""}
- << int(InsertDefsFromDecls::Mode::Alternating);
-}
-
-void QuickfixTest::testInsertDefsFromDecls()
-{
- QFETCH(QByteArrayList, headers);
- QFETCH(QByteArrayList, sources);
- QFETCH(int, mode);
-
- QList<TestDocumentPtr> testDocuments({
- CppTestDocument::create("file.h", headers.at(0), headers.at(1)),
- CppTestDocument::create("file.cpp", sources.at(0), sources.at(1))});
- InsertDefsFromDecls factory;
- factory.setMode(static_cast<InsertDefsFromDecls::Mode>(mode));
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testInsertAndFormatDefsFromDecls()
-{
- if (!isClangFormatPresent())
- QSKIP("This test reqires ClangFormat");
-
- const QByteArray origHeader = R"(
-class @C
-{
-public:
- void func1 (int const &i);
- void func2 (double const d);
-};
-)";
- const QByteArray origSource = R"(
-#include "file.h"
-)";
-
- const QByteArray expectedSource = R"(
-#include "file.h"
-
-void C::func1 (int const &i)
-{
-
-}
-
-void C::func2 (double const d)
-{
-
-}
-)";
-
- const QByteArray clangFormatSettings = R"(
-BreakBeforeBraces: Allman
-QualifierAlignment: Right
-SpaceBeforeParens: Always
-)";
-
- const QList<TestDocumentPtr> testDocuments({
- CppTestDocument::create("file.h", origHeader, origHeader),
- CppTestDocument::create("file.cpp", origSource, expectedSource)});
- InsertDefsFromDecls factory;
- factory.setMode(InsertDefsFromDecls::Mode::Impl);
- CppCodeStylePreferences * const prefs = CppToolsSettings::cppCodeStyle();
- const CppCodeStyleSettings settings = prefs->codeStyleSettings();
- CppCodeStyleSettings tempSettings = settings;
- tempSettings.forceFormatting = true;
- prefs->setCodeStyleSettings(tempSettings);
- QuickFixOperationTest(testDocuments, &factory, {}, {}, {}, clangFormatSettings);
- prefs->setCodeStyleSettings(settings);
-}
-
-QList<TestDocumentPtr> singleHeader(const QByteArray &original, const QByteArray &expected)
-{
- return {CppTestDocument::create("file.h", original, expected)};
-}
-
-void QuickfixTest::testInsertDefOutsideFromDeclTemplateClassAndTemplateFunction()
-{
- QByteArray original =
- "template<class T>"
- "class Foo\n"
- "{\n"
- " template<class U>\n"
- " void fun@c();\n"
- "};\n";
- QByteArray expected =
- "template<class T>"
- "class Foo\n"
- "{\n"
- " template<class U>\n"
- " void fun@c();\n"
- "};\n"
- "\n"
- "template<class T>\n"
- "template<class U>\n"
- "inline void Foo<T>::func()\n"
- "{\n"
- "\n"
- "}\n";
-
- InsertDefFromDecl factory;
- factory.m_defPosOutsideClass = true;
- QuickFixOperationTest(singleHeader(original, expected), &factory);
-}
-
-void QuickfixTest::testInsertDefOutsideFromDeclTemplateClass()
-{
- QByteArray original =
- "template<class T>"
- "class Foo\n"
- "{\n"
- " void fun@c();\n"
- "};\n";
- QByteArray expected =
- "template<class T>"
- "class Foo\n"
- "{\n"
- " void fun@c();\n"
- "};\n"
- "\n"
- "template<class T>\n"
- "inline void Foo<T>::func()\n"
- "{\n"
- "\n"
- "}\n";
-
- InsertDefFromDecl factory;
- factory.m_defPosOutsideClass = true;
- QuickFixOperationTest(singleHeader(original, expected), &factory);
-}
-
-void QuickfixTest::testInsertDefOutsideFromDeclTemplateFunction()
-{
- QByteArray original =
- "class Foo\n"
- "{\n"
- " template<class U>\n"
- " void fun@c();\n"
- "};\n";
- QByteArray expected =
- "class Foo\n"
- "{\n"
- " template<class U>\n"
- " void fun@c();\n"
- "};\n"
- "\n"
- "template<class U>\n"
- "inline void Foo::func()\n"
- "{\n"
- "\n"
- "}\n";
-
- InsertDefFromDecl factory;
- factory.m_defPosOutsideClass = true;
- QuickFixOperationTest(singleHeader(original, expected), &factory);
-}
-
-void QuickfixTest::testInsertDefOutsideFromDeclFunction()
-{
- QByteArray original =
- "class Foo\n"
- "{\n"
- " void fun@c();\n"
- "};\n";
- QByteArray expected =
- "class Foo\n"
- "{\n"
- " void fun@c();\n"
- "};\n"
- "\n"
- "inline void Foo::func()\n"
- "{\n"
- "\n"
- "}\n";
-
- InsertDefFromDecl factory;
- factory.m_defPosOutsideClass = true;
- QuickFixOperationTest(singleHeader(original, expected), &factory);
-}
-
-// Function for one of InsertDeclDef section cases
-void insertToSectionDeclFromDef(const QByteArray &section, int sectionIndex)
-{
- QList<TestDocumentPtr> testDocuments;
-
- QByteArray original;
- QByteArray expected;
- QByteArray sectionString = section + ":\n";
- if (sectionIndex == 4)
- sectionString.clear();
-
- // Header File
- original =
- "class Foo\n"
- "{\n"
- "};\n";
- expected =
- "class Foo\n"
- "{\n"
- + sectionString +
- " Foo();\n"
- "@};\n";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n"
- "\n"
- "Foo::Foo@()\n"
- "{\n"
- "}\n"
- ;
- expected = original;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- InsertDeclFromDef factory;
- QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), sectionIndex);
-}
-
-/// Check from source file: Insert in header file.
-void QuickfixTest::testInsertDeclFromDef()
-{
- insertToSectionDeclFromDef("public", 0);
- insertToSectionDeclFromDef("public slots", 1);
- insertToSectionDeclFromDef("protected", 2);
- insertToSectionDeclFromDef("protected slots", 3);
- insertToSectionDeclFromDef("private", 4);
- insertToSectionDeclFromDef("private slots", 5);
-}
-
-void QuickfixTest::testInsertDeclFromDefTemplateFuncTypename()
-{
- QByteArray original =
- "class Foo\n"
- "{\n"
- "};\n"
- "\n"
- "template<class T>\n"
- "void Foo::fu@nc() {}\n";
-
- QByteArray expected =
- "class Foo\n"
- "{\n"
- "public:\n"
- " template<class T>\n"
- " void func();\n"
- "};\n"
- "\n"
- "template<class T>\n"
- "void Foo::fu@nc() {}\n";
-
- InsertDeclFromDef factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory, {}, 0);
-}
-
-void QuickfixTest::testInsertDeclFromDefTemplateFuncInt()
-{
- QByteArray original =
- "class Foo\n"
- "{\n"
- "};\n"
- "\n"
- "template<int N>\n"
- "void Foo::fu@nc() {}\n";
-
- QByteArray expected =
- "class Foo\n"
- "{\n"
- "public:\n"
- " template<int N>\n"
- " void func();\n"
- "};\n"
- "\n"
- "template<int N>\n"
- "void Foo::fu@nc() {}\n";
-
- InsertDeclFromDef factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory, {}, 0);
-}
-
-void QuickfixTest::testInsertDeclFromDefTemplateReturnType()
-{
- QByteArray original =
- "class Foo\n"
- "{\n"
- "};\n"
- "\n"
- "std::vector<int> Foo::fu@nc() const {}\n";
-
- QByteArray expected =
- "class Foo\n"
- "{\n"
- "public:\n"
- " std::vector<int> func() const;\n"
- "};\n"
- "\n"
- "std::vector<int> Foo::func() const {}\n";
-
- InsertDeclFromDef factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory, {}, 0);
-}
-
-void QuickfixTest::testInsertDeclFromDefNotTriggeredForTemplateFunc()
-{
- QByteArray contents =
- "class Foo\n"
- "{\n"
- " template<class T>\n"
- " void func();\n"
- "};\n"
- "\n"
- "template<class T>\n"
- "void Foo::fu@nc() {}\n";
-
- InsertDeclFromDef factory;
- QuickFixOperationTest(singleDocument(contents, ""), &factory);
-}
-
-void QuickfixTest::testAddIncludeForUndefinedIdentifier_data()
-{
- QTest::addColumn<QString>("headerPath");
- QTest::addColumn<QuickFixTestDocuments>("testDocuments");
- QTest::addColumn<int>("refactoringOperationIndex");
- QTest::addColumn<QString>("includeForTestFactory");
-
- const int firstRefactoringOperation = 0;
- const int secondRefactoringOperation = 1;
-
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- // -------------------------------------------------------------------------------------------
-
- // Header File
- original = "class Foo {};\n";
- expected = original;
- testDocuments << CppTestDocument::create("afile.h", original, expected);
-
- // Source File
- original =
- "#include \"header.h\"\n"
- "\n"
- "void f()\n"
- "{\n"
- " Fo@o foo;\n"
- "}\n"
- ;
- expected =
- "#include \"afile.h\"\n"
- "#include \"header.h\"\n"
- "\n"
- "void f()\n"
- "{\n"
- " Foo foo;\n"
- "}\n"
- ;
- testDocuments << CppTestDocument::create("afile.cpp", original, expected);
- QTest::newRow("onSimpleName")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- // Header File
- original = "namespace N { class Foo {}; }\n";
- expected = original;
- testDocuments << CppTestDocument::create("afile.h", original, expected);
-
- // Source File
- original =
- "#include \"header.h\"\n"
- "\n"
- "void f()\n"
- "{\n"
- " N::Fo@o foo;\n"
- "}\n"
- ;
- expected =
- "#include \"afile.h\"\n"
- "#include \"header.h\"\n"
- "\n"
- "void f()\n"
- "{\n"
- " N::Foo foo;\n"
- "}\n"
- ;
- testDocuments << CppTestDocument::create("afile.cpp", original, expected);
- QTest::newRow("onNameOfQualifiedName")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- // Header File
- original = "namespace N { class Foo {}; }\n";
- expected = original;
- testDocuments << CppTestDocument::create("afile.h", original, expected);
-
- // Source File
- original =
- "#include \"header.h\"\n"
- "\n"
- "void f()\n"
- "{\n"
- " @N::Foo foo;\n"
- "}\n"
- ;
- expected =
- "#include \"afile.h\"\n"
- "#include \"header.h\"\n"
- "\n"
- "void f()\n"
- "{\n"
- " N::Foo foo;\n"
- "}\n"
- ;
- testDocuments << CppTestDocument::create("afile.cpp", original, expected);
- QTest::newRow("onBaseOfQualifiedName")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- // Header File
- original = "class Foo { static void bar() {} };\n";
- expected = original;
- testDocuments << CppTestDocument::create("afile.h", original, expected);
-
- // Source File
- original =
- "#include \"header.h\"\n"
- "\n"
- "void f()\n"
- "{\n"
- " @Foo::bar();\n"
- "}\n"
- ;
- expected =
- "#include \"afile.h\"\n"
- "#include \"header.h\"\n"
- "\n"
- "void f()\n"
- "{\n"
- " Foo::bar();\n"
- "}\n"
- ;
- testDocuments << CppTestDocument::create("afile.cpp", original, expected);
- QTest::newRow("onBaseOfQualifiedClassName")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- // Header File
- original = "template <typename T> class Foo { static void bar() {} };\n";
- expected = original;
- testDocuments << CppTestDocument::create("afile.h", original, expected);
-
- // Source File
- original =
- "#include \"header.h\"\n"
- "\n"
- "void f()\n"
- "{\n"
- " @Foo<int>::bar();\n"
- "}\n"
- ;
- expected =
- "#include \"afile.h\"\n"
- "#include \"header.h\"\n"
- "\n"
- "void f()\n"
- "{\n"
- " Foo<int>::bar();\n"
- "}\n"
- ;
- testDocuments << CppTestDocument::create("afile.cpp", original, expected);
- QTest::newRow("onBaseOfQualifiedTemplateClassName")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- // Header File
- original = "namespace N { template <typename T> class Foo {}; }\n";
- expected = original;
- testDocuments << CppTestDocument::create("afile.h", original, expected);
-
- // Source File
- original =
- "#include \"header.h\"\n"
- "\n"
- "void f()\n"
- "{\n"
- " @N::Foo<Bar> foo;\n"
- "}\n"
- ;
- expected =
- "#include \"afile.h\"\n"
- "#include \"header.h\"\n"
- "\n"
- "void f()\n"
- "{\n"
- " N::Foo<Bar> foo;\n"
- "}\n"
- ;
- testDocuments << CppTestDocument::create("afile.cpp", original, expected);
- QTest::newRow("onTemplateName")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- // Header File
- original = "namespace N { template <typename T> class Foo {}; }\n";
- expected = original;
- testDocuments << CppTestDocument::create("afile.h", original, expected);
-
- // Source File
- original =
- "#include \"header.h\"\n"
- "\n"
- "void f()\n"
- "{\n"
- " N::Bar<@Foo> foo;\n"
- "}\n"
- ;
- expected =
- "#include \"afile.h\"\n"
- "#include \"header.h\"\n"
- "\n"
- "void f()\n"
- "{\n"
- " N::Bar<Foo> foo;\n"
- "}\n"
- ;
- testDocuments << CppTestDocument::create("afile.cpp", original, expected);
- QTest::newRow("onTemplateNameInsideArguments")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- // Header File
- original = "class Foo {};\n";
- expected = original;
- testDocuments << CppTestDocument::create("afile.h", original, expected);
-
- // Source File
- original =
- "#include \"header.h\"\n"
- "\n"
- "class Foo;\n"
- "\n"
- "void f()\n"
- "{\n"
- " @Foo foo;\n"
- "}\n"
- ;
- expected =
- "#include \"afile.h\"\n"
- "#include \"header.h\"\n"
- "\n"
- "class Foo;\n"
- "\n"
- "void f()\n"
- "{\n"
- " Foo foo;\n"
- "}\n"
- ;
- testDocuments << CppTestDocument::create("afile.cpp", original, expected);
- QTest::newRow("withForwardDeclaration")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- // Header File
- original = "template<class T> class Foo {};\n";
- expected = original;
- testDocuments << CppTestDocument::create("afile.h", original, expected);
-
- // Source File
- original =
- "#include \"header.h\"\n"
- "\n"
- "template<class T> class Foo;\n"
- "\n"
- "void f()\n"
- "{\n"
- " @Foo foo;\n"
- "}\n"
- ;
- expected =
- "#include \"afile.h\"\n"
- "#include \"header.h\"\n"
- "\n"
- "template<class T> class Foo;\n"
- "\n"
- "void f()\n"
- "{\n"
- " Foo foo;\n"
- "}\n"
- ;
- testDocuments << CppTestDocument::create("afile.cpp", original, expected);
- QTest::newRow("withForwardDeclaration2")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- // Header File
- original = "template<class T> class QMyClass {};\n";
- expected = original;
- testDocuments << CppTestDocument::create("qmyclass.h", original, expected);
-
- // Forward Header File
- original = "#include \"qmyclass.h\"\n";
- expected = original;
- testDocuments << CppTestDocument::create("QMyClass", original, expected);
-
- // Source File
- original =
- "#include \"header.h\"\n"
- "\n"
- "void f()\n"
- "{\n"
- " @QMyClass c;\n"
- "}\n"
- ;
- expected =
- "#include \"QMyClass\"\n"
- "#include \"header.h\"\n"
- "\n"
- "void f()\n"
- "{\n"
- " QMyClass c;\n"
- "}\n"
- ;
- testDocuments << CppTestDocument::create("afile.cpp", original, expected);
- QTest::newRow("withForwardHeader")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << secondRefactoringOperation << "";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- original =
- "void @f();\n"
- "#include \"file.moc\";\n"
- ;
- expected =
- "#include \"file.h\"\n"
- "\n"
- "void f();\n"
- "#include \"file.moc\";\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("insertingIgnoreMoc")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "\"file.h\"";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- original =
- "#include \"y.h\"\n"
- "#include \"z.h\"\n"
- "\n@"
- ;
- expected =
- "#include \"file.h\"\n"
- "#include \"y.h\"\n"
- "#include \"z.h\"\n"
- "\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("insertingSortingTop")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "\"file.h\"";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- original =
- "#include \"a.h\"\n"
- "#include \"z.h\"\n"
- "\n@"
- ;
- expected =
- "#include \"a.h\"\n"
- "#include \"file.h\"\n"
- "#include \"z.h\"\n"
- "\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("insertingSortingMiddle")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "\"file.h\"";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- original =
- "#include \"a.h\"\n"
- "#include \"b.h\"\n"
- "\n@"
- ;
- expected =
- "#include \"a.h\"\n"
- "#include \"b.h\"\n"
- "#include \"file.h\"\n"
- "\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("insertingSortingBottom")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "\"file.h\"";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- original =
- "#include \"b.h\"\n"
- "#include \"a.h\"\n"
- "\n@"
- ;
- expected =
- "#include \"b.h\"\n"
- "#include \"a.h\"\n"
- "#include \"file.h\"\n"
- "\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("inserting_appendToUnsorted")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "\"file.h\"";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- original =
- "#include <a.h>\n"
- "#include <b.h>\n"
- "\n@"
- ;
- expected =
- "#include \"file.h\"\n"
- "\n"
- "#include <a.h>\n"
- "#include <b.h>\n"
- "\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("inserting_firstLocalIncludeAtFront")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "\"file.h\"";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- original =
- "#include \"a.h\"\n"
- "#include \"b.h\"\n"
- "\n"
- "void @f();\n"
- ;
- expected =
- "#include \"a.h\"\n"
- "#include \"b.h\"\n"
- "\n"
- "#include <file.h>\n"
- "\n"
- "void f();\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("firstGlobalIncludeAtBack")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "<file.h>";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- original =
- "#include \"prefixa.h\"\n"
- "#include \"prefixb.h\"\n"
- "\n"
- "#include \"foo.h\"\n"
- "\n@"
- ;
- expected =
- "#include \"prefixa.h\"\n"
- "#include \"prefixb.h\"\n"
- "#include \"prefixc.h\"\n"
- "\n"
- "#include \"foo.h\"\n"
- "\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("inserting_preferGroupWithLongerMatchingPrefix")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "\"prefixc.h\"";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- original =
- "#include \"lib/file.h\"\n"
- "#include \"lib/fileother.h\"\n"
- "\n@"
- ;
- expected =
- "#include \"lib/file.h\"\n"
- "#include \"lib/fileother.h\"\n"
- "\n"
- "#include \"file.h\"\n"
- "\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("inserting_newGroupIfOnlyDifferentIncludeDirs")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "\"file.h\"";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- original =
- "#include <lib/file.h>\n"
- "#include <otherlib/file.h>\n"
- "#include <utils/file.h>\n"
- "\n@"
- ;
- expected =
- "#include <firstlib/file.h>\n"
- "#include <lib/file.h>\n"
- "#include <otherlib/file.h>\n"
- "#include <utils/file.h>\n"
- "\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("inserting_mixedDirsSorted")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "<firstlib/file.h>";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- original =
- "#include <otherlib/file.h>\n"
- "#include <lib/file.h>\n"
- "#include <utils/file.h>\n"
- "\n@"
- ;
- expected =
- "#include <otherlib/file.h>\n"
- "#include <lib/file.h>\n"
- "#include <utils/file.h>\n"
- "#include <lastlib/file.h>\n"
- "\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("inserting_mixedDirsUnsorted")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "<lastlib/file.h>";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- original =
- "#include \"a.h\"\n"
- "#include <global.h>\n"
- "\n@"
- ;
- expected =
- "#include \"a.h\"\n"
- "#include \"z.h\"\n"
- "#include <global.h>\n"
- "\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("inserting_mixedIncludeTypes1")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "\"z.h\"";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- original =
- "#include \"z.h\"\n"
- "#include <global.h>\n"
- "\n@"
- ;
- expected =
- "#include \"a.h\"\n"
- "#include \"z.h\"\n"
- "#include <global.h>\n"
- "\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("inserting_mixedIncludeTypes2")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "\"a.h\"";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- original =
- "#include \"z.h\"\n"
- "#include <global.h>\n"
- "\n@"
- ;
- expected =
- "#include \"z.h\"\n"
- "#include \"lib/file.h\"\n"
- "#include <global.h>\n"
- "\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("inserting_mixedIncludeTypes3")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "\"lib/file.h\"";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- original =
- "#include \"z.h\"\n"
- "#include <global.h>\n"
- "\n@"
- ;
- expected =
- "#include \"z.h\"\n"
- "#include <global.h>\n"
- "#include <lib/file.h>\n"
- "\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("inserting_mixedIncludeTypes4")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "<lib/file.h>";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- original =
- "void @f();\n"
- ;
- expected =
- "#include \"file.h\"\n"
- "\n"
- "void f();\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("inserting_noinclude")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "\"file.h\"";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- original =
- "#ifndef FOO_H\n"
- "#define FOO_H\n"
- "void @f();\n"
- "#endif\n"
- ;
- expected =
- "#ifndef FOO_H\n"
- "#define FOO_H\n"
- "\n"
- "#include \"file.h\"\n"
- "\n"
- "void f();\n"
- "#endif\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("inserting_onlyIncludeGuard")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "\"file.h\"";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- original =
- "\n"
- "// comment\n"
- "\n"
- "void @f();\n"
- ;
- expected =
- "\n"
- "// comment\n"
- "\n"
- "#include \"file.h\"\n"
- "\n"
- "void @f();\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("inserting_veryFirstIncludeCppStyleCommentOnTop")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "\"file.h\"";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- original =
- "\n"
- "/*\n"
- " comment\n"
- " */\n"
- "\n"
- "void @f();\n"
- ;
- expected =
- "\n"
- "/*\n"
- " comment\n"
- " */\n"
- "\n"
- "#include \"file.h\"\n"
- "\n"
- "void @f();\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("inserting_veryFirstIncludeCStyleCommentOnTop")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "\"file.h\"";
- testDocuments.clear();
-
- // -------------------------------------------------------------------------------------------
-
- original =
- "@QDir dir;\n"
- ;
- expected =
- "#include <QDir>\n"
- "\n"
- "QDir dir;\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("inserting_checkQSomethingInQtIncludePaths")
- << TestIncludePaths::globalQtCoreIncludePath()
- << testDocuments << firstRefactoringOperation << "";
- testDocuments.clear();
-
- original =
- "std::s@tring s;\n"
- ;
- expected =
- "#include <string>\n"
- "\n"
- "std::string s;\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
- QTest::newRow("inserting_std::string")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "";
- testDocuments.clear();
-
- original = "class A{};";
- testDocuments << CppTestDocument::create("a.h", original, original);
- original = "class B{};";
- testDocuments << CppTestDocument::create("b.h", original, original);
- original =
- "#include \"b.h\"\n"
- "@A a;\n"
- "B b;";
- expected =
- "#include \"b.h\"\n\n"
- "#include \"a.h\"\n"
- "A a;\n"
- "B b;";
- testDocuments << CppTestDocument::create("b.cpp", original, expected);
- QTest::newRow("preserve first header")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "";
- testDocuments.clear();
-
- original = "class C{};";
- testDocuments << CppTestDocument::create("c.h", original, original);
- original = "class B{};";
- testDocuments << CppTestDocument::create("b.h", original, original);
- original =
- "#include \"c.h\"\n"
- "C c;\n"
- "@B b;";
- expected =
- "#include \"b.h\"\n"
- "#include \"c.h\"\n"
- "C c;\n"
- "B b;";
- testDocuments << CppTestDocument::create("x.cpp", original, expected);
- QTest::newRow("do not preserve first header")
- << TestIncludePaths::globalIncludePath()
- << testDocuments << firstRefactoringOperation << "";
- testDocuments.clear();
-}
-
-void QuickfixTest::testAddIncludeForUndefinedIdentifier()
-{
- QFETCH(QString, headerPath);
- QFETCH(QuickFixTestDocuments, testDocuments);
- QFETCH(int, refactoringOperationIndex);
- QFETCH(QString, includeForTestFactory);
-
- TemporaryDir temporaryDir;
- QVERIFY(temporaryDir.isValid());
- for (const TestDocumentPtr &testDocument : std::as_const(testDocuments))
- testDocument->setBaseDirectory(temporaryDir.path());
-
- QScopedPointer<CppQuickFixFactory> factory;
- if (includeForTestFactory.isEmpty())
- factory.reset(new AddIncludeForUndefinedIdentifier);
- else
- factory.reset(new AddIncludeForUndefinedIdentifierTestFactory(includeForTestFactory));
-
- QuickFixOperationTest::run(testDocuments, factory.data(), headerPath,
- refactoringOperationIndex);
-}
-
-void QuickfixTest::testAddIncludeForUndefinedIdentifierNoDoubleQtHeaderInclude()
-{
- TemporaryDir temporaryDir;
- QVERIFY(temporaryDir.isValid());
-
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- const QByteArray base = temporaryDir.path().toUtf8();
-
- // This file makes the QDir definition available so that locator finds it.
- original = expected = "#include <QDir>\n"
- "void avoidBeingRecognizedAsForwardingHeader();";
- testDocuments << CppTestDocument::create(base + "/fileUsingQDir.cpp", original, expected);
-
- original = expected = "@QDir dir;\n";
- testDocuments << CppTestDocument::create(base + "/fileWantsToUseQDir.cpp", original, expected);
-
- AddIncludeForUndefinedIdentifier factory;
- const QStringList expectedOperations = QStringList("Add #include <QDir>");
- QuickFixOfferedOperationsTest(testDocuments, &factory, ProjectExplorer::toUserHeaderPaths(
- QStringList{TestIncludePaths::globalQtCoreIncludePath()}), expectedOperations);
-}
-
-void QuickfixTest::testAddForwardDeclForUndefinedIdentifier_data()
-{
- QTest::addColumn<QuickFixTestDocuments>("testDocuments");
- QTest::addColumn<QString>("symbol");
- QTest::addColumn<int>("symbolPos");
-
- QByteArray original;
- QByteArray expected;
-
- original =
- "#pragma once\n"
- "\n"
- "void f(const Blu@bb &b)\n"
- "{\n"
- "}\n"
- ;
- expected =
- "#pragma once\n"
- "\n"
- "\n"
- "class Blubb;\n"
- "void f(const Blubb &b)\n"
- "{\n"
- "}\n"
- ;
- QTest::newRow("unqualified symbol")
- << QuickFixTestDocuments{CppTestDocument::create("theheader.h", original, expected)}
- << "Blubb" << original.indexOf('@');
-
- original =
- "#pragma once\n"
- "\n"
- "namespace NS {\n"
- "class C;\n"
- "}\n"
- "void f(const NS::Blu@bb &b)\n"
- "{\n"
- "}\n"
- ;
- expected =
- "#pragma once\n"
- "\n"
- "namespace NS {\n"
- "\n"
- "class Blubb;\n"
- "class C;\n"
- "}\n"
- "void f(const NS::Blubb &b)\n"
- "{\n"
- "}\n"
- ;
- QTest::newRow("qualified symbol, full namespace present")
- << QuickFixTestDocuments{CppTestDocument::create("theheader.h", original, expected)}
- << "NS::Blubb" << original.indexOf('@');
-
- original =
- "#pragma once\n"
- "\n"
- "namespace NS {\n"
- "class C;\n"
- "}\n"
- "void f(const NS::NS2::Blu@bb &b)\n"
- "{\n"
- "}\n"
- ;
- expected =
- "#pragma once\n"
- "\n"
- "namespace NS {\n"
- "\n"
- "namespace NS2 { class Blubb; }\n"
- "class C;\n"
- "}\n"
- "void f(const NS::NS2::Blubb &b)\n"
- "{\n"
- "}\n"
- ;
- QTest::newRow("qualified symbol, partial namespace present")
- << QuickFixTestDocuments{CppTestDocument::create("theheader.h", original, expected)}
- << "NS::NS2::Blubb" << original.indexOf('@');
-
- original =
- "#pragma once\n"
- "\n"
- "namespace NS {\n"
- "class C;\n"
- "}\n"
- "void f(const NS2::Blu@bb &b)\n"
- "{\n"
- "}\n"
- ;
- expected =
- "#pragma once\n"
- "\n"
- "\n"
- "namespace NS2 { class Blubb; }\n"
- "namespace NS {\n"
- "class C;\n"
- "}\n"
- "void f(const NS2::Blubb &b)\n"
- "{\n"
- "}\n"
- ;
- QTest::newRow("qualified symbol, other namespace present")
- << QuickFixTestDocuments{CppTestDocument::create("theheader.h", original, expected)}
- << "NS2::Blubb" << original.indexOf('@');
-
- original =
- "#pragma once\n"
- "\n"
- "void f(const NS2::Blu@bb &b)\n"
- "{\n"
- "}\n"
- ;
- expected =
- "#pragma once\n"
- "\n"
- "\n"
- "namespace NS2 { class Blubb; }\n"
- "void f(const NS2::Blubb &b)\n"
- "{\n"
- "}\n"
- ;
- QTest::newRow("qualified symbol, no namespace present")
- << QuickFixTestDocuments{CppTestDocument::create("theheader.h", original, expected)}
- << "NS2::Blubb" << original.indexOf('@');
-
- original =
- "#pragma once\n"
- "\n"
- "void f(const NS2::Blu@bb &b)\n"
- "{\n"
- "}\n"
- "namespace NS2 {}\n"
- ;
- expected =
- "#pragma once\n"
- "\n"
- "\n"
- "namespace NS2 { class Blubb; }\n"
- "void f(const NS2::Blubb &b)\n"
- "{\n"
- "}\n"
- "namespace NS2 {}\n"
- ;
- QTest::newRow("qualified symbol, existing namespace after symbol")
- << QuickFixTestDocuments{CppTestDocument::create("theheader.h", original, expected)}
- << "NS2::Blubb" << original.indexOf('@');
-}
-
-void QuickfixTest::testAddForwardDeclForUndefinedIdentifier()
-{
- QFETCH(QuickFixTestDocuments, testDocuments);
- QFETCH(QString, symbol);
- QFETCH(int, symbolPos);
-
- TemporaryDir temporaryDir;
- QVERIFY(temporaryDir.isValid());
- testDocuments.first()->setBaseDirectory(temporaryDir.path());
-
- QScopedPointer<CppQuickFixFactory> factory(
- new AddForwardDeclForUndefinedIdentifierTestFactory(symbol, symbolPos));
- QuickFixOperationTest::run({testDocuments}, factory.data(), ".", 0);
-}
-
-/// Check: Move definition from header to cpp.
-void QuickfixTest::testMoveFuncDefOutsideMemberFuncToCpp()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "class Foo {\n"
- " inline int numbe@r() const\n"
- " {\n"
- " return 5;\n"
- " }\n"
- "\n"
- " void bar();\n"
- "};\n";
- expected =
- "class Foo {\n"
- " int number() const;\n"
- "\n"
- " void bar();\n"
- "};\n";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n";
- expected =
- "#include \"file.h\"\n"
- "\n"
- "int Foo::number() const\n"
- "{\n"
- " return 5;\n"
- "}\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testMoveFuncDefOutsideMemberFuncToCppStatic()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "class Foo {\n"
- " static inline int numbe@r() const\n"
- " {\n"
- " return 5;\n"
- " }\n"
- "\n"
- " void bar();\n"
- "};\n";
- expected =
- "class Foo {\n"
- " static int number() const;\n"
- "\n"
- " void bar();\n"
- "};\n";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n";
- expected =
- "#include \"file.h\"\n"
- "\n"
- "int Foo::number() const\n"
- "{\n"
- " return 5;\n"
- "}\n";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testMoveFuncDefOutsideMemberFuncToCppWithInlinePartOfName()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "class Foo {\n"
- " static inline int numbe@r_inline () const\n"
- " {\n"
- " return 5;\n"
- " }\n"
- "\n"
- " void bar();\n"
- "};\n";
- expected =
- "class Foo {\n"
- " static int number_inline () const;\n"
- "\n"
- " void bar();\n"
- "};\n";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n";
- expected =
- "#include \"file.h\"\n"
- "\n"
- "int Foo::number_inline() const\n"
- "{\n"
- " return 5;\n"
- "}\n";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testMoveFuncDefOutsideMixedQualifiers()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original = R"(
-struct Base {
- virtual auto func() const && noexcept -> void = 0;
-};
-struct Derived : public Base {
- auto @func() const && noexcept -> void override {}
-};)";
- expected = R"(
-struct Base {
- virtual auto func() const && noexcept -> void = 0;
-};
-struct Derived : public Base {
- auto func() const && noexcept -> void override;
-};)";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original = "#include \"file.h\"\n";
- expected = R"DELIM(#include "file.h"
-
-auto Derived::func() const && noexcept -> void {}
-)DELIM";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testMoveFuncDefOutsideMemberFuncToCppInsideNS()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "namespace SomeNamespace {\n"
- "class Foo {\n"
- " int ba@r()\n"
- " {\n"
- " return 5;\n"
- " }\n"
- "};\n"
- "}\n";
- expected =
- "namespace SomeNamespace {\n"
- "class Foo {\n"
- " int ba@r();\n"
- "};\n"
- "}\n";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n"
- "namespace SomeNamespace {\n"
- "\n"
- "}\n";
- expected =
- "#include \"file.h\"\n"
- "namespace SomeNamespace {\n"
- "\n"
- "int Foo::bar()\n"
- "{\n"
- " return 5;\n"
- "}\n"
- "\n"
- "}\n";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-/// Check: Move definition outside class
-void QuickfixTest::testMoveFuncDefOutsideMemberFuncOutside1()
-{
- QByteArray original =
- "class Foo {\n"
- " void f1();\n"
- " inline int f2@() const\n"
- " {\n"
- " return 1;\n"
- " }\n"
- " void f3();\n"
- " void f4();\n"
- "};\n"
- "\n"
- "void Foo::f4() {}\n";
- QByteArray expected =
- "class Foo {\n"
- " void f1();\n"
- " int f2@() const;\n"
- " void f3();\n"
- " void f4();\n"
- "};\n"
- "\n"
- "int Foo::f2() const\n"
- "{\n"
- " return 1;\n"
- "}\n"
- "\n"
- "void Foo::f4() {}\n";
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-}
-
-/// Check: Move definition outside class
-void QuickfixTest::testMoveFuncDefOutsideMemberFuncOutside2()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "class Foo {\n"
- " void f1();\n"
- " int f2@()\n"
- " {\n"
- " return 1;\n"
- " }\n"
- " void f3();\n"
- "};\n";
- expected =
- "class Foo {\n"
- " void f1();\n"
- " int f2();\n"
- " void f3();\n"
- "};\n"
- "\n"
- "inline int Foo::f2()\n"
- "{\n"
- " return 1;\n"
- "}\n";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n"
- "void Foo::f1() {}\n"
- "void Foo::f3() {}\n";
- expected = original;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 1);
-}
-
-/// Check: Move definition from header to cpp (with namespace).
-void QuickfixTest::testMoveFuncDefOutsideMemberFuncToCppNS()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "namespace MyNs {\n"
- "class Foo {\n"
- " inline int numbe@r() const\n"
- " {\n"
- " return 5;\n"
- " }\n"
- "};\n"
- "}\n";
- expected =
- "namespace MyNs {\n"
- "class Foo {\n"
- " int number() const;\n"
- "};\n"
- "}\n";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n";
- expected =
- "#include \"file.h\"\n"
- "\n"
- "int MyNs::Foo::number() const\n"
- "{\n"
- " return 5;\n"
- "}\n";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-/// Check: Move definition from header to cpp (with namespace + using).
-void QuickfixTest::testMoveFuncDefOutsideMemberFuncToCppNSUsing()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "namespace MyNs {\n"
- "class Foo {\n"
- " inline int numbe@r() const\n"
- " {\n"
- " return 5;\n"
- " }\n"
- "};\n"
- "}\n";
- expected =
- "namespace MyNs {\n"
- "class Foo {\n"
- " int number() const;\n"
- "};\n"
- "}\n";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n"
- "using namespace MyNs;\n";
- expected =
- "#include \"file.h\"\n"
- "using namespace MyNs;\n"
- "\n"
- "int Foo::number() const\n"
- "{\n"
- " return 5;\n"
- "}\n";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-/// Check: Move definition outside class with Namespace
-void QuickfixTest::testMoveFuncDefOutsideMemberFuncOutsideWithNs()
-{
- QByteArray original =
- "namespace MyNs {\n"
- "class Foo {\n"
- " inline int numbe@r() const\n"
- " {\n"
- " return 5;\n"
- " }\n"
- "};}\n";
- QByteArray expected =
- "namespace MyNs {\n"
- "class Foo {\n"
- " int number() const;\n"
- "};\n"
- "\n"
- "int Foo::number() const\n"
- "{\n"
- " return 5;\n"
- "}\n"
- "\n}\n";
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-}
-
-/// Check: Move free function from header to cpp.
-void QuickfixTest::testMoveFuncDefOutsideFreeFuncToCpp()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "int numbe@r() const\n"
- "{\n"
- " return 5;\n"
- "}\n";
- expected =
- "int number() const;\n"
- ;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n";
- expected =
- "#include \"file.h\"\n"
- "\n"
- "int number() const\n"
- "{\n"
- " return 5;\n"
- "}\n";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-/// Check: Move free function from header to cpp (with namespace).
-void QuickfixTest::testMoveFuncDefOutsideFreeFuncToCppNS()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "namespace MyNamespace {\n"
- "int numbe@r() const\n"
- "{\n"
- " return 5;\n"
- "}\n"
- "}\n";
- expected =
- "namespace MyNamespace {\n"
- "int number() const;\n"
- "}\n";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n";
- expected =
- "#include \"file.h\"\n"
- "\n"
- "int MyNamespace::number() const\n"
- "{\n"
- " return 5;\n"
- "}\n";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-/// Check: Move Ctor with member initialization list (QTCREATORBUG-9157).
-void QuickfixTest::testMoveFuncDefOutsideCtorWithInitialization1()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "class Foo {\n"
- "public:\n"
- " Fo@o() : a(42), b(3.141) {}\n"
- "private:\n"
- " int a;\n"
- " float b;\n"
- "};\n";
- expected =
- "class Foo {\n"
- "public:\n"
- " Foo();\n"
- "private:\n"
- " int a;\n"
- " float b;\n"
- "};\n";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original ="#include \"file.h\"\n";
- expected =
- "#include \"file.h\"\n"
- "\n"
- "Foo::Foo() : a(42), b(3.141) {}\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-/// Check: Move Ctor with member initialization list (QTCREATORBUG-9462).
-void QuickfixTest::testMoveFuncDefOutsideCtorWithInitialization2()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "class Foo\n"
- "{\n"
- "public:\n"
- " Fo@o() : member(2)\n"
- " {\n"
- " }\n"
- "\n"
- " int member;\n"
- "};\n";
-
- expected =
- "class Foo\n"
- "{\n"
- "public:\n"
- " Foo();\n"
- "\n"
- " int member;\n"
- "};\n";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original ="#include \"file.h\"\n";
- expected =
- "#include \"file.h\"\n"
- "\n"
- "Foo::Foo() : member(2)\n"
- "{\n"
- "}\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-/// Check if definition is inserted right after class for move definition outside
-void QuickfixTest::testMoveFuncDefOutsideAfterClass()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "class Foo\n"
- "{\n"
- " Foo();\n"
- " void a@() {}\n"
- "};\n"
- "\n"
- "class Bar {};\n";
- expected =
- "class Foo\n"
- "{\n"
- " Foo();\n"
- " void a();\n"
- "};\n"
- "\n"
- "inline void Foo::a() {}\n"
- "\n"
- "class Bar {};\n";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n"
- "\n"
- "Foo::Foo()\n"
- "{\n\n"
- "}\n";
- expected = original;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 1);
-}
-
-/// Check if whitespace is respected for operator functions
-void QuickfixTest::testMoveFuncDefOutsideRespectWsInOperatorNames1()
-{
- QByteArray original =
- "class Foo\n"
- "{\n"
- " Foo &opera@tor =() {}\n"
- "};\n";
- QByteArray expected =
- "class Foo\n"
- "{\n"
- " Foo &operator =();\n"
- "};\n"
- "\n"
- "Foo &Foo::operator =() {}\n"
- ;
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-}
-
-/// Check if whitespace is respected for operator functions
-void QuickfixTest::testMoveFuncDefOutsideRespectWsInOperatorNames2()
-{
- QByteArray original =
- "class Foo\n"
- "{\n"
- " Foo &opera@tor=() {}\n"
- "};\n";
- QByteArray expected =
- "class Foo\n"
- "{\n"
- " Foo &operator=();\n"
- "};\n"
- "\n"
- "Foo &Foo::operator=() {}\n"
- ;
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-}
-
-void QuickfixTest::testMoveFuncDefOutsideMacroUses()
-{
- QByteArray original =
- "#define CONST const\n"
- "#define VOLATILE volatile\n"
- "class Foo\n"
- "{\n"
- " int fu@nc(int a, int b) CONST VOLATILE\n"
- " {\n"
- " return 42;\n"
- " }\n"
- "};\n";
- QByteArray expected =
- "#define CONST const\n"
- "#define VOLATILE volatile\n"
- "class Foo\n"
- "{\n"
- " int func(int a, int b) CONST VOLATILE;\n"
- "};\n"
- "\n"
- "\n"
- // const volatile become lowercase: QTCREATORBUG-12620
- "int Foo::func(int a, int b) const volatile\n"
- "{\n"
- " return 42;\n"
- "}\n"
- ;
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory,
- ProjectExplorer::HeaderPaths(), 0, "QTCREATORBUG-12314");
-}
-
-void QuickfixTest::testMoveFuncDefOutsideTemplate()
-{
- QByteArray original =
- "template<class T>\n"
- "class Foo { void fu@nc() {} };\n";
- QByteArray expected =
- "template<class T>\n"
- "class Foo { void fu@nc(); };\n"
- "\n"
- "template<class T>\n"
- "void Foo<T>::func() {}\n";
- ;
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-}
-
-void QuickfixTest::testMoveFuncDefOutsideMemberFunctionTemplate()
-{
- const QByteArray original = R"(
-struct S {
- template<typename In>
- void @foo(In in) { (void)in; }
-};
-)";
- const QByteArray expected = R"(
-struct S {
- template<typename In>
- void foo(In in);
-};
-
-template<typename In>
-void S::foo(In in) { (void)in; }
-)";
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-}
-
-void QuickfixTest::testMoveFuncDefOutsideTemplateSpecializedClass()
-{
- QByteArray original = R"(
-template<typename T> class base {};
-template<>
-class base<int>
-{
-public:
- void @bar() {}
-};
-)";
- QByteArray expected = R"(
-template<typename T> class base {};
-template<>
-class base<int>
-{
-public:
- void bar();
-};
-
-void base<int>::bar() {}
-)";
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-}
-
-void QuickfixTest::testMoveFuncDefOutsideUnnamedTemplate()
-{
- QByteArray original =
- "template<typename T, typename>\n"
- "class Foo { void fu@nc() {} };\n";
- QByteArray expected =
- "template<typename T, typename>\n"
- "class Foo { void fu@nc(); };\n"
- "\n"
- "template<typename T, typename T2>\n"
- "void Foo<T, T2>::func() {}\n";
- ;
-
- MoveFuncDefOutside factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-}
-
-void QuickfixTest::testMoveFuncDefToDecl_data()
-{
- QTest::addColumn<QByteArrayList>("headers");
- QTest::addColumn<QByteArrayList>("sources");
-
- QByteArray originalHeader;
- QByteArray expectedHeader;
- QByteArray originalSource;
- QByteArray expectedSource;
-
- originalHeader =
- "class Foo {\n"
- " inline int @number() const;\n"
- "};\n";
- expectedHeader =
- "class Foo {\n"
- " inline int number() const {return 5;}\n"
- "};\n";
- originalSource =
- "#include \"file.h\"\n"
- "\n"
- "int Foo::num@ber() const {return 5;}\n";
- expectedSource =
- "#include \"file.h\"\n"
- "\n\n";
- QTest::newRow("member function, two files") << QByteArrayList{originalHeader, expectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-
- originalSource =
- "class Foo {\n"
- " inline int @number() const;\n"
- "};\n"
- "\n"
- "int Foo::num@ber() const\n"
- "{\n"
- " return 5;\n"
- "}\n";
-
- expectedSource =
- "class Foo {\n"
- " inline int number() const\n"
- " {\n"
- " return 5;\n"
- " }\n"
- "};\n\n\n";
- QTest::newRow("member function, one file") << QByteArrayList()
- << QByteArrayList{originalSource, expectedSource};
-
- originalHeader =
- "namespace MyNs {\n"
- "class Foo {\n"
- " inline int @number() const;\n"
- "};\n"
- "}\n";
- expectedHeader =
- "namespace MyNs {\n"
- "class Foo {\n"
- " inline int number() const\n"
- " {\n"
- " return 5;\n"
- " }\n"
- "};\n"
- "}\n";
- originalSource =
- "#include \"file.h\"\n"
- "\n"
- "int MyNs::Foo::num@ber() const\n"
- "{\n"
- " return 5;\n"
- "}\n";
- expectedSource = "#include \"file.h\"\n\n\n";
- QTest::newRow("member function, two files, namespace")
- << QByteArrayList{originalHeader, expectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-
- originalHeader =
- "namespace MyNs {\n"
- "class Foo {\n"
- " inline int numbe@r() const;\n"
- "};\n"
- "}\n";
- expectedHeader =
- "namespace MyNs {\n"
- "class Foo {\n"
- " inline int number() const\n"
- " {\n"
- " return 5;\n"
- " }\n"
- "};\n"
- "}\n";
- originalSource =
- "#include \"file.h\"\n"
- "using namespace MyNs;\n"
- "\n"
- "int Foo::num@ber() const\n"
- "{\n"
- " return 5;\n"
- "}\n";
- expectedSource =
- "#include \"file.h\"\n"
- "using namespace MyNs;\n"
- "\n\n";
- QTest::newRow("member function, two files, namespace with using-directive")
- << QByteArrayList{originalHeader, expectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-
- originalSource =
- "namespace MyNs {\n"
- "class Foo {\n"
- " inline int @number() const;\n"
- "};\n"
- "\n"
- "int Foo::numb@er() const\n"
- "{\n"
- " return 5;\n"
- "}"
- "\n}\n";
- expectedSource =
- "namespace MyNs {\n"
- "class Foo {\n"
- " inline int number() const\n"
- " {\n"
- " return 5;\n"
- " }\n"
- "};\n\n\n}\n";
-
- QTest::newRow("member function, one file, namespace")
- << QByteArrayList() << QByteArrayList{originalSource, expectedSource};
-
- originalHeader = "int nu@mber() const;\n";
- expectedHeader =
- "inline int number() const\n"
- "{\n"
- " return 5;\n"
- "}\n";
- originalSource =
- "#include \"file.h\"\n"
- "\n"
- "\n"
- "int numb@er() const\n"
- "{\n"
- " return 5;\n"
- "}\n";
- expectedSource = "#include \"file.h\"\n\n\n\n";
- QTest::newRow("free function") << QByteArrayList{originalHeader, expectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-
- originalHeader =
- "namespace MyNamespace {\n"
- "int n@umber() const;\n"
- "}\n";
- expectedHeader =
- "namespace MyNamespace {\n"
- "inline int number() const\n"
- "{\n"
- " return 5;\n"
- "}\n"
- "}\n";
- originalSource =
- "#include \"file.h\"\n"
- "\n"
- "int MyNamespace::nu@mber() const\n"
- "{\n"
- " return 5;\n"
- "}\n";
- expectedSource =
- "#include \"file.h\"\n"
- "\n\n";
- QTest::newRow("free function, namespace") << QByteArrayList{originalHeader, expectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-
- originalHeader =
- "class Foo {\n"
- "public:\n"
- " Fo@o();\n"
- "private:\n"
- " int a;\n"
- " float b;\n"
- "};\n";
- expectedHeader =
- "class Foo {\n"
- "public:\n"
- " Foo() : a(42), b(3.141) {}\n"
- "private:\n"
- " int a;\n"
- " float b;\n"
- "};\n";
- originalSource =
- "#include \"file.h\"\n"
- "\n"
- "Foo::F@oo() : a(42), b(3.141) {}"
- ;
- expectedSource ="#include \"file.h\"\n\n";
- QTest::newRow("constructor") << QByteArrayList{originalHeader, expectedHeader}
- << QByteArrayList{originalSource, expectedSource};
-
- originalSource =
- "struct Foo\n"
- "{\n"
- " void f@oo();\n"
- "} bar;\n"
- "void Foo::fo@o()\n"
- "{\n"
- " return;\n"
- "}";
- expectedSource =
- "struct Foo\n"
- "{\n"
- " void foo()\n"
- " {\n"
- " return;\n"
- " }\n"
- "} bar;\n";
- QTest::newRow("QTCREATORBUG-10303") << QByteArrayList()
- << QByteArrayList{originalSource, expectedSource};
-
- originalSource =
- "struct Base {\n"
- " virtual int foo() = 0;\n"
- "};\n"
- "struct Derived : Base {\n"
- " int @foo() override;\n"
- "};\n"
- "\n"
- "int Derived::fo@o()\n"
- "{\n"
- " return 5;\n"
- "}\n";
- expectedSource =
- "struct Base {\n"
- " virtual int foo() = 0;\n"
- "};\n"
- "struct Derived : Base {\n"
- " int foo() override\n"
- " {\n"
- " return 5;\n"
- " }\n"
- "};\n\n\n";
- QTest::newRow("overridden virtual") << QByteArrayList()
- << QByteArrayList{originalSource, expectedSource};
-
- originalSource =
- "template<class T>\n"
- "class Foo { void @func(); };\n"
- "\n"
- "template<class T>\n"
- "void Foo<T>::fu@nc() {}\n";
- expectedSource =
- "template<class T>\n"
- "class Foo { void fu@nc() {} };\n\n\n";
- QTest::newRow("class template") << QByteArrayList()
- << QByteArrayList{originalSource, expectedSource};
-
- originalSource =
- "class Foo\n"
- "{\n"
- " template<class T>\n"
- " void @func();\n"
- "};\n"
- "\n"
- "template<class T>\n"
- "void Foo::fu@nc() {}\n";
- expectedSource =
- "class Foo\n"
- "{\n"
- " template<class T>\n"
- " void func() {}\n"
- "};\n\n\n";
- QTest::newRow("function template") << QByteArrayList()
- << QByteArrayList{originalSource, expectedSource};
-}
-
-void QuickfixTest::testMoveFuncDefToDecl()
-{
- QFETCH(QByteArrayList, headers);
- QFETCH(QByteArrayList, sources);
-
- QVERIFY(headers.isEmpty() || headers.size() == 2);
- QVERIFY(sources.size() == 2);
-
- QByteArray &declDoc = !headers.empty() ? headers.first() : sources.first();
- const int declCursorPos = declDoc.indexOf('@');
- QVERIFY(declCursorPos != -1);
- const int defCursorPos = sources.first().lastIndexOf('@');
- QVERIFY(defCursorPos != -1);
- QVERIFY(declCursorPos != defCursorPos);
-
- declDoc.remove(declCursorPos, 1);
- QList<TestDocumentPtr> testDocuments;
- if (!headers.isEmpty())
- testDocuments << CppTestDocument::create("file.h", headers.first(), headers.last());
- testDocuments << CppTestDocument::create("file.cpp", sources.first(), sources.last());
-
- MoveFuncDefToDeclPush pushFactory;
- QuickFixOperationTest(testDocuments, &pushFactory);
-
- declDoc.insert(declCursorPos, '@');
- sources.first().remove(defCursorPos, 1);
- testDocuments.clear();
- if (!headers.isEmpty())
- testDocuments << CppTestDocument::create("file.h", headers.first(), headers.last());
- testDocuments << CppTestDocument::create("file.cpp", sources.first(), sources.last());
-
- MoveFuncDefToDeclPull pullFactory;
- QuickFixOperationTest(testDocuments, &pullFactory);
-}
-
-void QuickfixTest::testMoveFuncDefToDeclMacroUses()
-{
- QByteArray original =
- "#define CONST const\n"
- "#define VOLATILE volatile\n"
- "class Foo\n"
- "{\n"
- " int func(int a, int b) CONST VOLATILE;\n"
- "};\n"
- "\n"
- "\n"
- "int Foo::fu@nc(int a, int b) CONST VOLATILE"
- "{\n"
- " return 42;\n"
- "}\n";
- QByteArray expected =
- "#define CONST const\n"
- "#define VOLATILE volatile\n"
- "class Foo\n"
- "{\n"
- " int func(int a, int b) CONST VOLATILE\n"
- " {\n"
- " return 42;\n"
- " }\n"
- "};\n\n\n\n";
-
- MoveFuncDefToDeclPush factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory,
- ProjectExplorer::HeaderPaths(), 0, "QTCREATORBUG-12314");
-}
-
-/// Check: Move all definitions from header to cpp.
-void QuickfixTest::testMoveAllFuncDefOutsideMemberFuncToCpp()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "class Foo {@\n"
- " int numberA() const\n"
- " {\n"
- " return 5;\n"
- " }\n"
- " int numberB() const\n"
- " {\n"
- " return 5;\n"
- " }\n"
- "};\n";
- expected =
- "class Foo {\n"
- " int numberA() const;\n"
- " int numberB() const;\n"
- "};\n";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n";
- expected =
- "#include \"file.h\"\n"
- "\n"
- "int Foo::numberA() const\n"
- "{\n"
- " return 5;\n"
- "}\n"
- "\n"
- "int Foo::numberB() const\n"
- "{\n"
- " return 5;\n"
- "}\n"
- ;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- MoveAllFuncDefOutside factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-/// Check: Move all definition outside class
-void QuickfixTest::testMoveAllFuncDefOutsideMemberFuncOutside()
-{
- QByteArray original =
- "class F@oo {\n"
- " int f1()\n"
- " {\n"
- " return 1;\n"
- " }\n"
- " int f2() const\n"
- " {\n"
- " return 2;\n"
- " }\n"
- "};\n";
- QByteArray expected =
- "class Foo {\n"
- " int f1();\n"
- " int f2() const;\n"
- "};\n"
- "\n"
- "int Foo::f1()\n"
- "{\n"
- " return 1;\n"
- "}\n"
- "\n"
- "int Foo::f2() const\n"
- "{\n"
- " return 2;\n"
- "}\n";
-
- MoveAllFuncDefOutside factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-}
-
-/// Check: Move all definition outside class
-void QuickfixTest::testMoveAllFuncDefOutsideDoNotTriggerOnBaseClass()
-{
- QByteArray original =
- "class Bar;\n"
- "class Foo : public Ba@r {\n"
- " int f1()\n"
- " {\n"
- " return 1;\n"
- " }\n"
- "};\n";
-
- MoveAllFuncDefOutside factory;
- QuickFixOperationTest(singleDocument(original, ""), &factory);
-}
-
-/// Check: Move all definition outside class
-void QuickfixTest::testMoveAllFuncDefOutsideClassWithBaseClass()
-{
- QByteArray original =
- "class Bar;\n"
- "class Fo@o : public Bar {\n"
- " int f1()\n"
- " {\n"
- " return 1;\n"
- " }\n"
- "};\n";
- QByteArray expected =
- "class Bar;\n"
- "class Foo : public Bar {\n"
- " int f1();\n"
- "};\n"
- "\n"
- "int Foo::f1()\n"
- "{\n"
- " return 1;\n"
- "}\n";
-
- MoveAllFuncDefOutside factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-}
-
-/// Check: Do not take macro expanded code into account (QTCREATORBUG-13900)
-void QuickfixTest::testMoveAllFuncDefOutsideIgnoreMacroCode()
-{
- QByteArray original =
- "#define FAKE_Q_OBJECT int bar() {return 5;}\n"
- "class Fo@o {\n"
- " FAKE_Q_OBJECT\n"
- " int f1()\n"
- " {\n"
- " return 1;\n"
- " }\n"
- "};\n";
- QByteArray expected =
- "#define FAKE_Q_OBJECT int bar() {return 5;}\n"
- "class Foo {\n"
- " FAKE_Q_OBJECT\n"
- " int f1();\n"
- "};\n"
- "\n"
- "int Foo::f1()\n"
- "{\n"
- " return 1;\n"
- "}\n";
-
- MoveAllFuncDefOutside factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-}
-
-void QuickfixTest::testAssignToLocalVariableTemplates()
-{
-
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "template <typename T>\n"
- "class List {\n"
- "public:\n"
- " T first();"
- "};\n"
- ;
- expected = original;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n"
- "void foo() {\n"
- " List<int> list;\n"
- " li@st.first();\n"
- "}\n";
- expected =
- "#include \"file.h\"\n"
- "void foo() {\n"
- " List<int> list;\n"
- " auto localFirst = list.first();\n"
- "}\n";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- AssignToLocalVariable factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testExtractFunction_data()
-{
- QTest::addColumn<QByteArray>("original");
- QTest::addColumn<QByteArray>("expected");
-
- QTest::newRow("basic")
- << _("// Documentation for f\n"
- "void f()\n"
- "{\n"
- " @{start}g();@{end}\n"
- "}\n")
- << _("inline void extracted()\n"
- "{\n"
- " g();\n"
- "}\n"
- "\n"
- "// Documentation for f\n"
- "void f()\n"
- "{\n"
- " extracted();\n"
- "}\n");
-
- QTest::newRow("class function")
- << _("class Foo\n"
- "{\n"
- "private:\n"
- " void bar();\n"
- "};\n\n"
- "void Foo::bar()\n"
- "{\n"
- " @{start}g();@{end}\n"
- "}\n")
- << _("class Foo\n"
- "{\n"
- "public:\n"
- " void extracted();\n\n"
- "private:\n"
- " void bar();\n"
- "};\n\n"
- "inline void Foo::extracted()\n"
- "{\n"
- " g();\n"
- "}\n\n"
- "void Foo::bar()\n"
- "{\n"
- " extracted();\n"
- "}\n");
-
- QTest::newRow("class in namespace")
- << _("namespace NS {\n"
- "class C {\n"
- " void f(C &c);\n"
- "};\n"
- "}\n"
- "void NS::C::f(NS::C &c)\n"
- "{\n"
- " @{start}C *c2 = &c;@{end}\n"
- "}\n")
- << _("namespace NS {\n"
- "class C {\n"
- " void f(C &c);\n"
- "\n"
- "public:\n"
- " void extracted(NS::C &c);\n" // TODO: Remove non-required qualification
- "};\n"
- "}\n"
- "inline void NS::C::extracted(NS::C &c)\n"
- "{\n"
- " C *c2 = &c;\n"
- "}\n"
- "\n"
- "void NS::C::f(NS::C &c)\n"
- "{\n"
- " extracted(c);\n"
- "}\n");
-
- QTest::newRow("if-block")
- << _("inline void func()\n"
- "{\n"
- " int dummy = 0;\n"
- " @{start}if@{end} (dummy < 10) {\n"
- " ++dummy;\n"
- " }\n"
- "}\n")
- << _("inline void extracted(int dummy)\n"
- "{\n"
- " if (dummy < 10) {\n"
- " ++dummy;\n"
- " }\n"
- "}\n\n"
- "inline void func()\n"
- "{\n"
- " int dummy = 0;\n"
- " extracted(dummy);\n"
- "}\n");
-}
-
-void QuickfixTest::testExtractFunction()
-{
- QFETCH(QByteArray, original);
- QFETCH(QByteArray, expected);
-
- QList<TestDocumentPtr> testDocuments;
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- ExtractFunction factory([]() { return QLatin1String("extracted"); });
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testExtractLiteralAsParameterTypeDeduction_data()
-{
- QTest::addColumn<QByteArray>("typeString");
- QTest::addColumn<QByteArray>("literal");
- QTest::newRow("int")
- << QByteArray("int ") << QByteArray("156");
- QTest::newRow("unsigned int")
- << QByteArray("unsigned int ") << QByteArray("156u");
- QTest::newRow("long")
- << QByteArray("long ") << QByteArray("156l");
- QTest::newRow("unsigned long")
- << QByteArray("unsigned long ") << QByteArray("156ul");
- QTest::newRow("long long")
- << QByteArray("long long ") << QByteArray("156ll");
- QTest::newRow("unsigned long long")
- << QByteArray("unsigned long long ") << QByteArray("156ull");
- QTest::newRow("float")
- << QByteArray("float ") << QByteArray("3.14159f");
- QTest::newRow("double")
- << QByteArray("double ") << QByteArray("3.14159");
- QTest::newRow("long double")
- << QByteArray("long double ") << QByteArray("3.14159L");
- QTest::newRow("bool")
- << QByteArray("bool ") << QByteArray("true");
- QTest::newRow("bool")
- << QByteArray("bool ") << QByteArray("false");
- QTest::newRow("char")
- << QByteArray("char ") << QByteArray("'X'");
- QTest::newRow("wchar_t")
- << QByteArray("wchar_t ") << QByteArray("L'X'");
- QTest::newRow("char16_t")
- << QByteArray("char16_t ") << QByteArray("u'X'");
- QTest::newRow("char32_t")
- << QByteArray("char32_t ") << QByteArray("U'X'");
- QTest::newRow("const char *")
- << QByteArray("const char *") << QByteArray("\"narf\"");
- QTest::newRow("const wchar_t *")
- << QByteArray("const wchar_t *") << QByteArray("L\"narf\"");
- QTest::newRow("const char16_t *")
- << QByteArray("const char16_t *") << QByteArray("u\"narf\"");
- QTest::newRow("const char32_t *")
- << QByteArray("const char32_t *") << QByteArray("U\"narf\"");
-}
-
-void QuickfixTest::testExtractLiteralAsParameterTypeDeduction()
-{
- QFETCH(QByteArray, typeString);
- QFETCH(QByteArray, literal);
- const QByteArray original = QByteArray("void foo() {return @") + literal + QByteArray(";}\n");
- const QByteArray expected = QByteArray("void foo(") + typeString + QByteArray("newParameter = ")
- + literal + QByteArray(") {return newParameter;}\n");
-
- if (literal == "3.14159") {
- qWarning("Literal 3.14159 is wrongly reported as int. Skipping.");
- return;
- } else if (literal == "3.14159L") {
- qWarning("Literal 3.14159L is wrongly reported as long. Skipping.");
- return;
- }
-
- ExtractLiteralAsParameter factory;
- QuickFixOperationTest(singleDocument(original, expected), &factory);
-}
-
-void QuickfixTest::testExtractLiteralAsParameterFreeFunctionSeparateFiles()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "void foo(const char *a, long b = 1);\n";
- expected =
- "void foo(const char *a, long b = 1, int newParameter = 156);\n";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "void foo(const char *a, long b)\n"
- "{return 1@56 + 123 + 156;}\n";
- expected =
- "void foo(const char *a, long b, int newParameter)\n"
- "{return newParameter + 123 + newParameter;}\n";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- ExtractLiteralAsParameter factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testExtractLiteralAsParameterMemberFunctionSeparateFiles()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- QByteArray expected;
-
- // Header File
- original =
- "class Narf {\n"
- "public:\n"
- " int zort();\n"
- "};\n";
- expected =
- "class Narf {\n"
- "public:\n"
- " int zort(int newParameter = 155);\n"
- "};\n";
- testDocuments << CppTestDocument::create("file.h", original, expected);
-
- // Source File
- original =
- "#include \"file.h\"\n\n"
- "int Narf::zort()\n"
- "{ return 15@5 + 1; }\n";
- expected =
- "#include \"file.h\"\n\n"
- "int Narf::zort(int newParameter)\n"
- "{ return newParameter + 1; }\n";
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- ExtractLiteralAsParameter factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testExtractLiteralAsParameterNotTriggeringForInvalidCode()
-{
- QList<TestDocumentPtr> testDocuments;
- QByteArray original;
- original =
- "T(\"test\")\n"
- "{\n"
- " const int i = @14;\n"
- "}\n";
- testDocuments << CppTestDocument::create("file.cpp", original, "");
-
- ExtractLiteralAsParameter factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testAddCurlyBraces_data()
-{
- QTest::addColumn<QByteArray>("original");
- QTest::addColumn<QByteArray>("expected");
-
- QByteArray original = R"delim(
-void MyObject::f()
-{
- @if (true)
- emit mySig();
-})delim";
- QByteArray expected = R"delim(
-void MyObject::f()
-{
- if (true) {
- emit mySig();
- }
-})delim";
- QTest::newRow("if") << original << expected;
-
- original = R"delim(
-void MyObject::f()
-{
- @if (true)
- emit mySig();
- else
- emit otherSig();
-})delim";
- expected = R"delim(
-void MyObject::f()
-{
- @if (true) {
- emit mySig();
- } else {
- emit otherSig();
- }
-})delim";
- QTest::newRow("if with one else, unbraced") << original << expected;
-
- original = R"delim(
-void MyObject::f()
-{
- @if (true) {
- emit mySig();
- } else
- emit otherSig();
-})delim";
- expected = R"delim(
-void MyObject::f()
-{
- @if (true) {
- emit mySig();
- } else {
- emit otherSig();
- }
-})delim";
- QTest::newRow("if with one else, if braced") << original << expected;
-
- original = R"delim(
-void MyObject::f()
-{
- @if (true)
- emit mySig();
- else {
- emit otherSig();
- }
-})delim";
- expected = R"delim(
-void MyObject::f()
-{
- @if (true) {
- emit mySig();
- } else {
- emit otherSig();
- }
-})delim";
- QTest::newRow("if with one else, else braced") << original << expected;
-
- original = R"delim(
-void MyObject::f()
-{
- @if (true) {
- emit mySig();
- } else {
- emit otherSig();
- }
-})delim";
- expected.clear();
- QTest::newRow("if with one else, both braced") << original << expected;
-
- original = R"delim(
-void MyObject::f()
-{
- @if (x == 1)
- emit sig1();
- else if (x == 2)
- emit sig2();
-})delim";
- expected = R"delim(
-void MyObject::f()
-{
- if (x == 1) {
- emit sig1();
- } else if (x == 2) {
- emit sig2();
- }
-})delim";
- QTest::newRow("if-else chain without final else, unbraced") << original << expected;
-
- original = R"delim(
-void MyObject::f()
-{
- @if (x == 1) {
- emit sig1();
- } else if (x == 2)
- emit sig2();
-})delim";
- expected = R"delim(
-void MyObject::f()
-{
- if (x == 1) {
- emit sig1();
- } else if (x == 2) {
- emit sig2();
- }
-})delim";
- QTest::newRow("if-else chain without final else, partially braced 1") << original << expected;
-
- original = R"delim(
-void MyObject::f()
-{
- @if (x == 1)
- emit sig1();
- else if (x == 2) {
- emit sig2();
- }
-})delim";
- expected = R"delim(
-void MyObject::f()
-{
- if (x == 1) {
- emit sig1();
- } else if (x == 2) {
- emit sig2();
- }
-})delim";
- QTest::newRow("if-else chain without final else, partially braced 2") << original << expected;
-
- original = R"delim(
-void MyObject::f()
-{
- @if (x == 1) {
- emit sig1();
- } else if (x == 2) {
- emit sig2();
- }
-})delim";
- expected.clear();
- QTest::newRow("if-else chain without final else, fully braced") << original << expected;
-
- original = R"delim(
-void MyObject::f()
-{
- @if (x == 1)
- emit sig1();
- else if (x == 2)
- emit sig2();
- else if (x == 3)
- emit sig3();
- else
- emit otherSig();
-})delim";
- expected = R"delim(
-void MyObject::f()
-{
- if (x == 1) {
- emit sig1();
- } else if (x == 2) {
- emit sig2();
- } else if (x == 3) {
- emit sig3();
- } else {
- emit otherSig();
- }
-})delim";
- QTest::newRow("if-else chain, unbraced") << original << expected;
-
- original = R"delim(
-void MyObject::f()
-{
- @if (x == 1) {
- emit sig1();
- } else if (x == 2)
- emit sig2();
- else if (x == 3)
- emit sig3();
- else
- emit otherSig();
-})delim";
- expected = R"delim(
-void MyObject::f()
-{
- if (x == 1) {
- emit sig1();
- } else if (x == 2) {
- emit sig2();
- } else if (x == 3) {
- emit sig3();
- } else {
- emit otherSig();
- }
-})delim";
- QTest::newRow("if-else chain, partially braced 1") << original << expected;
-
- original = R"delim(
-void MyObject::f()
-{
- @if (x == 1)
- emit sig1();
- else if (x == 2) {
- emit sig2();
- } else if (x == 3)
- emit sig3();
- else
- emit otherSig();
-})delim";
- expected = R"delim(
-void MyObject::f()
-{
- if (x == 1) {
- emit sig1();
- } else if (x == 2) {
- emit sig2();
- } else if (x == 3) {
- emit sig3();
- } else {
- emit otherSig();
- }
-})delim";
- QTest::newRow("if-else chain, partially braced 2") << original << expected;
-
- original = R"delim(
-void MyObject::f()
-{
- @if (x == 1)
- emit sig1();
- else if (x == 2)
- emit sig2();
- else if (x == 3) {
- emit sig3();
- } else
- emit otherSig();
-})delim";
- expected = R"delim(
-void MyObject::f()
-{
- if (x == 1) {
- emit sig1();
- } else if (x == 2) {
- emit sig2();
- } else if (x == 3) {
- emit sig3();
- } else {
- emit otherSig();
- }
-})delim";
- QTest::newRow("if-else chain, partially braced 3") << original << expected;
-
- original = R"delim(
-void MyObject::f()
-{
- @if (x == 1)
- emit sig1();
- else if (x == 2)
- emit sig2();
- else if (x == 3)
- emit sig3();
- else {
- emit otherSig();
- }
-})delim";
- expected = R"delim(
-void MyObject::f()
-{
- if (x == 1) {
- emit sig1();
- } else if (x == 2) {
- emit sig2();
- } else if (x == 3) {
- emit sig3();
- } else {
- emit otherSig();
- }
-})delim";
- QTest::newRow("if-else chain, partially braced 4") << original << expected;
-
- original = R"delim(
-void MyObject::f()
-{
- @if (x == 1) {
- emit sig1();
- } else if (x == 2) {
- emit sig2();
- } else if (x == 3) {
- emit sig3();
- } else {
- emit otherSig();
- }
-})delim";
- expected.clear();
- QTest::newRow("if-else chain, fully braced") << original << expected;
-
- original = R"delim(
-void MyObject::f()
-{
- @while (true)
- emit mySig();
-})delim";
- expected = R"delim(
-void MyObject::f()
-{
- while (true) {
- emit mySig();
- }
-})delim";
- QTest::newRow("while") << original << expected;
-
- original = R"delim(
-void MyObject::f()
-{
- @for (int i = 0; i < 10; ++i)
- emit mySig();
-})delim";
- expected = R"delim(
-void MyObject::f()
-{
- for (int i = 0; i < 10; ++i) {
- emit mySig();
- }
-})delim";
- QTest::newRow("for") << original << expected;
-
- original = R"delim(
-void MyObject::f()
-{
- @for (int i : list)
- emit mySig();
-})delim";
- expected = R"delim(
-void MyObject::f()
-{
- for (int i : list) {
- emit mySig();
- }
-})delim";
- QTest::newRow("range-based for") << original << expected;
-
- original = R"delim(
-void MyObject::f()
-{
- @do
- emit mySig();
- while (true);
-})delim";
- expected = R"delim(
-void MyObject::f()
-{
- do {
- emit mySig();
- } while (true);
-})delim";
- QTest::newRow("do") << original << expected;
-
- original = R"delim(
-void MyObject::f()
-{
- @do {
- emit mySig();
- } while (true);
-})delim";
- expected.clear();
- QTest::newRow("already has braces") << original << expected;
-}
-
-void QuickfixTest::testAddCurlyBraces()
-{
- QFETCH(QByteArray, original);
- QFETCH(QByteArray, expected);
-
- AddBracesToControlStatement factory;
- QuickFixOperationTest({CppTestDocument::create("file.cpp", original, expected)}, &factory);
-}
-
-void QuickfixTest::testConvertQt4ConnectConnectOutOfClass()
-{
- QByteArray prefix =
- "class QObject {};\n"
- "class TestClass : public QObject\n"
- "{\n"
- "public:\n"
- " void setProp(int) {}\n"
- " void sigFoo(int) {}\n"
- "};\n"
- "\n"
- "int foo()\n"
- "{\n";
-
- QByteArray suffix = "\n}\n";
-
- QByteArray original = prefix
- + " TestClass obj;\n"
- " conne@ct(&obj, SIGNAL(sigFoo(int)), &obj, SLOT(setProp(int)));"
- + suffix;
-
- QByteArray expected = prefix
- + " TestClass obj;\n"
- " connect(&obj, &TestClass::sigFoo, &obj, &TestClass::setProp);"
- + suffix;
-
- QList<TestDocumentPtr> testDocuments;
- testDocuments << CppTestDocument::create("file.cpp", original, expected);
-
- ConvertQt4Connect factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testConvertQt4ConnectConnectWithinClass_data()
-{
- QTest::addColumn<QByteArray>("original");
- QTest::addColumn<QByteArray>("expected");
-
- QTest::newRow("four-args-connect")
- << QByteArray("conne@ct(this, SIGNAL(sigFoo(int)), this, SLOT(setProp(int)));")
- << QByteArray("connect(this, &TestClass::sigFoo, this, &TestClass::setProp);");
-
- QTest::newRow("four-args-disconnect")
- << QByteArray("disconne@ct(this, SIGNAL(sigFoo(int)), this, SLOT(setProp(int)));")
- << QByteArray("disconnect(this, &TestClass::sigFoo, this, &TestClass::setProp);");
-
- QTest::newRow("three-args-connect")
- << QByteArray("conne@ct(this, SIGNAL(sigFoo(int)), SLOT(setProp(int)));")
- << QByteArray("connect(this, &TestClass::sigFoo, this, &TestClass::setProp);");
-
- QTest::newRow("template-value")
- << QByteArray("Pointer<TestClass> p;\n"
- "conne@ct(p.t, SIGNAL(sigFoo(int)), p.t, SLOT(setProp(int)));")
- << QByteArray("Pointer<TestClass> p;\n"
- "connect(p.t, &TestClass::sigFoo, p.t, &TestClass::setProp);");
-
- QTest::newRow("implicit-pointer")
- << QByteArray("Pointer<TestClass> p;\n"
- "conne@ct(p, SIGNAL(sigFoo(int)), p, SLOT(setProp(int)));")
- << QByteArray("Pointer<TestClass> p;\n"
- "connect(p.data(), &TestClass::sigFoo, p.data(), &TestClass::setProp);");
-}
-
-void QuickfixTest::testConvertQt4ConnectConnectWithinClass()
-{
- QFETCH(QByteArray, original);
- QFETCH(QByteArray, expected);
-
- QByteArray prefix =
- "template<class T>\n"
- "struct Pointer\n"
- "{\n"
- " T *t;\n"
- " operator T*() const { return t; }\n"
- " T *data() const { return t; }\n"
- "};\n"
- "class QObject {};\n"
- "class TestClass : public QObject\n"
- "{\n"
- "public:\n"
- " void setProp(int) {}\n"
- " void sigFoo(int) {}\n"
- " void setupSignals();\n"
- "};\n"
- "\n"
- "int TestClass::setupSignals()\n"
- "{\n";
-
- QByteArray suffix = "\n}\n";
-
- QList<TestDocumentPtr> testDocuments;
- testDocuments << CppTestDocument::create("file.cpp",
- prefix + original + suffix,
- prefix + expected + suffix);
-
- ConvertQt4Connect factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testConvertQt4ConnectDifferentNamespace()
-{
- const QByteArray prefix =
- "namespace NsA {\n"
- "class ClassA : public QObject\n"
- "{\n"
- " static ClassA *instance();\n"
- "signals:\n"
- " void sig();\n"
- "};\n"
- "}\n"
- "\n"
- "namespace NsB {\n"
- "class ClassB : public QObject\n"
- "{\n"
- " void slot();\n"
- " void connector() {\n";
-
- const QByteArray suffix = " }\n};\n}";
-
- const QByteArray original = "co@nnect(NsA::ClassA::instance(), SIGNAL(sig()),\n"
- " this, SLOT(slot()));\n";
- const QByteArray expected = "connect(NsA::ClassA::instance(), &NsA::ClassA::sig,\n"
- " this, &ClassB::slot);\n";
- QList<TestDocumentPtr> testDocuments;
- testDocuments << CppTestDocument::create("file.cpp",
- prefix + original + suffix,
- prefix + expected + suffix);
-
- ConvertQt4Connect factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testRemoveUsingNamespace_data()
-{
- QTest::addColumn<QByteArray>("header1");
- QTest::addColumn<QByteArray>("header2");
- QTest::addColumn<QByteArray>("header3");
- QTest::addColumn<QByteArray>("expected1");
- QTest::addColumn<QByteArray>("expected2");
- QTest::addColumn<QByteArray>("expected3");
- QTest::addColumn<int>("operation");
-
- const QByteArray header1 = "namespace std{\n"
- " template<typename T>\n"
- " class vector{};\n"
- " namespace chrono{\n"
- " using seconds = int;\n"
- " }\n"
- "}\n"
- "using namespace std;\n"
- "namespace test{\n"
- " class vector{\n"
- " std::vector<int> ints;\n"
- " };\n"
- "}\n";
- const QByteArray header2 = "#include \"header1.h\"\n"
- "using foo = test::vector;\n"
- "using namespace std;\n"
- "using namespace test;\n"
- "vector<int> others;\n";
-
- const QByteArray header3 = "#include \"header2.h\"\n"
- "using namespace std;\n"
- "using namespace chrono;\n"
- "namespace test{\n"
- " vector vec;\n"
- " seconds t;\n"
- "}\n"
- "void scope(){\n"
- " for (;;) {\n"
- " using namespace std;\n"
- " vector<int> fori;\n"
- " }\n"
- " vector<int> no;\n"
- " using namespace std;\n"
- " vector<int> _no_change;\n"
- "}\n"
- "foo foos;\n";
-
- QByteArray h3 = "#include \"header2.h\"\n"
- "using namespace s@td;\n"
- "using namespace chrono;\n"
- "namespace test{\n"
- " vector vec;\n"
- " seconds t;\n"
- "}\n"
- "void scope(){\n"
- " for (;;) {\n"
- " using namespace std;\n"
- " vector<int> fori;\n"
- " }\n"
- " vector<int> no;\n"
- " using namespace std;\n"
- " vector<int> _no_change;\n"
- "}\n"
- "foo foos;\n";
-
- // like header1 but without "using namespace std;\n"
- QByteArray expected1 = "namespace std{\n"
- " template<typename T>\n"
- " class vector{};\n"
- " namespace chrono{\n"
- " using seconds = int;\n"
- " }\n"
- "}\n"
- "namespace test{\n"
- " class vector{\n"
- " std::vector<int> ints;\n"
- " };\n"
- "}\n";
-
- // like header2 but without "using namespace std;\n" and with std::vector
- QByteArray expected2 = "#include \"header1.h\"\n"
- "using foo = test::vector;\n"
- "using namespace test;\n"
- "std::vector<int> others;\n";
-
- QByteArray expected3 = "#include \"header2.h\"\n"
- "using namespace std::chrono;\n"
- "namespace test{\n"
- " vector vec;\n"
- " seconds t;\n"
- "}\n"
- "void scope(){\n"
- " for (;;) {\n"
- " using namespace std;\n"
- " vector<int> fori;\n"
- " }\n"
- " std::vector<int> no;\n"
- " using namespace std;\n"
- " vector<int> _no_change;\n"
- "}\n"
- "foo foos;\n";
-
- QTest::newRow("remove only in one file local")
- << header1 << header2 << h3 << header1 << header2 << expected3 << 0;
- QTest::newRow("remove only in one file globally")
- << header1 << header2 << h3 << expected1 << expected2 << expected3 << 1;
-
- QByteArray h2 = "#include \"header1.h\"\n"
- "using foo = test::vector;\n"
- "using namespace s@td;\n"
- "using namespace test;\n"
- "vector<int> others;\n";
-
- QTest::newRow("remove across two files only this")
- << header1 << h2 << header3 << header1 << expected2 << header3 << 0;
- QTest::newRow("remove across two files globally1")
- << header1 << h2 << header3 << expected1 << expected2 << expected3 << 1;
-
- QByteArray h1 = "namespace std{\n"
- " template<typename T>\n"
- " class vector{};\n"
- " namespace chrono{\n"
- " using seconds = int;\n"
- " }\n"
- "}\n"
- "using namespace s@td;\n"
- "namespace test{\n"
- " class vector{\n"
- " std::vector<int> ints;\n"
- " };\n"
- "}\n";
-
- QTest::newRow("remove across tree files only this")
- << h1 << header2 << header3 << expected1 << header2 << header3 << 0;
- QTest::newRow("remove across tree files globally")
- << h1 << header2 << header3 << expected1 << expected2 << expected3 << 1;
-
- expected3 = "#include \"header2.h\"\n"
- "using namespace std::chrono;\n"
- "namespace test{\n"
- " vector vec;\n"
- " seconds t;\n"
- "}\n"
- "void scope(){\n"
- " for (;;) {\n"
- " using namespace s@td;\n"
- " vector<int> fori;\n"
- " }\n"
- " std::vector<int> no;\n"
- " using namespace std;\n"
- " vector<int> _no_change;\n"
- "}\n"
- "foo foos;\n";
-
- QByteArray expected3_new = "#include \"header2.h\"\n"
- "using namespace std::chrono;\n"
- "namespace test{\n"
- " vector vec;\n"
- " seconds t;\n"
- "}\n"
- "void scope(){\n"
- " for (;;) {\n"
- " std::vector<int> fori;\n"
- " }\n"
- " std::vector<int> no;\n"
- " using namespace std;\n"
- " vector<int> _no_change;\n"
- "}\n"
- "foo foos;\n";
-
- QTest::newRow("scoped remove")
- << expected1 << expected2 << expected3 << expected1 << expected2 << expected3_new << 0;
-
- h2 = "#include \"header1.h\"\n"
- "using foo = test::vector;\n"
- "using namespace std;\n"
- "using namespace t@est;\n"
- "vector<int> others;\n";
- expected2 = "#include \"header1.h\"\n"
- "using foo = test::vector;\n"
- "using namespace std;\n"
- "vector<int> others;\n";
-
- QTest::newRow("existing namespace")
- << header1 << h2 << header3 << header1 << expected2 << header3 << 1;
-
- // test: remove using directive at global scope in every file
- h1 = "using namespace tes@t;";
- h2 = "using namespace test;";
- h3 = "using namespace test;";
-
- expected1 = expected2 = expected3 = "";
- QTest::newRow("global scope remove in every file")
- << h1 << h2 << h3 << expected1 << expected2 << expected3 << 1;
-
- // test: dont print inline namespaces
- h1 = R"--(
-namespace test {
- inline namespace test {
- class Foo{
- void foo1();
- void foo2();
- };
- inline int TEST = 42;
- }
-}
-)--";
- h2 = R"--(
-#include "header1.h"
-using namespace tes@t;
-)--";
- h3 = R"--(
-#include "header2.h"
-Foo f1;
-test::Foo f2;
-using T1 = Foo;
-using T2 = test::Foo;
-int i1 = TEST;
-int i2 = test::TEST;
-void Foo::foo1(){};
-void test::Foo::foo2(){};
-)--";
-
- expected1 = h1;
- expected2 = R"--(
-#include "header1.h"
-)--";
- expected3 = R"--(
-#include "header2.h"
-test::Foo f1;
-test::Foo f2;
-using T1 = test::Foo;
-using T2 = test::Foo;
-int i1 = test::TEST;
-int i2 = test::TEST;
-void test::Foo::foo1(){};
-void test::Foo::foo2(){};
-)--";
- QTest::newRow("don't insert inline namespaces")
- << h1 << h2 << h3 << expected1 << expected2 << expected3 << 0;
-}
-
-void QuickfixTest::testRemoveUsingNamespace()
-{
- QFETCH(QByteArray, header1);
- QFETCH(QByteArray, header2);
- QFETCH(QByteArray, header3);
- QFETCH(QByteArray, expected1);
- QFETCH(QByteArray, expected2);
- QFETCH(QByteArray, expected3);
- QFETCH(int, operation);
-
- QList<TestDocumentPtr> testDocuments;
- testDocuments << CppTestDocument::create("header1.h", header1, expected1);
- testDocuments << CppTestDocument::create("header2.h", header2, expected2);
- testDocuments << CppTestDocument::create("header3.h", header3, expected3);
-
- RemoveUsingNamespace factory;
- QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), operation);
-}
-
-void QuickfixTest::testRemoveUsingNamespaceSimple_data()
-{
- QTest::addColumn<QByteArray>("header");
- QTest::addColumn<QByteArray>("expected");
-
- const QByteArray common = R"--(
-namespace N{
- template<typename T>
- struct vector{
- using iterator = T*;
- };
- using int_vector = vector<int>;
-}
-)--";
- const QByteArray header = common + R"--(
-using namespace N@;
-int_vector ints;
-int_vector::iterator intIter;
-using vec = vector<int>;
-vec::iterator it;
-)--";
- const QByteArray expected = common + R"--(
-N::int_vector ints;
-N::int_vector::iterator intIter;
-using vec = N::vector<int>;
-vec::iterator it;
-)--";
-
- QTest::newRow("nested typedefs with Namespace") << header << expected;
-}
-
-void QuickfixTest::testRemoveUsingNamespaceSimple()
-{
- QFETCH(QByteArray, header);
- QFETCH(QByteArray, expected);
-
- QList<TestDocumentPtr> testDocuments;
- testDocuments << CppTestDocument::create("header.h", header, expected);
-
- RemoveUsingNamespace factory;
- QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths());
-}
-
-void QuickfixTest::testRemoveUsingNamespaceDifferentSymbols()
-{
- QByteArray header = "namespace test{\n"
- " struct foo{\n"
- " foo();\n"
- " void bar();\n"
- " };\n"
- " void func();\n"
- " enum E {E1, E2};\n"
- " int bar;\n"
- "}\n"
- "using namespace t@est;\n"
- "foo::foo(){}\n"
- "void foo::bar(){}\n"
- "void test(){\n"
- " int i = bar * 4;\n"
- " E val = E1;\n"
- " auto p = &foo::bar;\n"
- " func()\n"
- "}\n";
- QByteArray expected = "namespace test{\n"
- " struct foo{\n"
- " foo();\n"
- " void bar();\n"
- " };\n"
- " void func();\n"
- " enum E {E1, E2};\n"
- " int bar;\n"
- "}\n"
- "test::foo::foo(){}\n"
- "void test::foo::bar(){}\n"
- "void test(){\n"
- " int i = test::bar * 4;\n"
- " test::E val = test::E1;\n"
- " auto p = &test::foo::bar;\n"
- " test::func()\n"
- "}\n";
-
- QList<TestDocumentPtr> testDocuments;
- testDocuments << CppTestDocument::create("file.h", header, expected);
- RemoveUsingNamespace factory;
- QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 0);
-}
-
-enum ConstructorLocation { Inside, Outside, CppGenNamespace, CppGenUsingDirective, CppRewriteType };
-
-void QuickfixTest::testGenerateConstructor_data()
-{
- QTest::addColumn<QByteArray>("original_header");
- QTest::addColumn<QByteArray>("expected_header");
- QTest::addColumn<QByteArray>("original_source");
- QTest::addColumn<QByteArray>("expected_source");
- QTest::addColumn<int>("location");
- const int Inside = ConstructorLocation::Inside;
- const int Outside = ConstructorLocation::Outside;
- const int CppGenNamespace = ConstructorLocation::CppGenNamespace;
- const int CppGenUsingDirective = ConstructorLocation::CppGenUsingDirective;
- const int CppRewriteType = ConstructorLocation::CppRewriteType;
-
- QByteArray header = R"--(
-class@ Foo{
- int test;
- static int s;
-};
-)--";
- QByteArray expected = R"--(
-class Foo{
- int test;
- static int s;
-public:
- Foo(int test) : test(test)
- {}
-};
-)--";
- QTest::newRow("ignore static") << header << expected << QByteArray() << QByteArray() << Inside;
-
- header = R"--(
-class@ Foo{
- CustomType test;
-};
-)--";
- expected = R"--(
-class Foo{
- CustomType test;
-public:
- Foo(CustomType test) : test(std::move(test))
- {}
-};
-)--";
- QTest::newRow("Move custom value types")
- << header << expected << QByteArray() << QByteArray() << Inside;
-
- header = R"--(
-class@ Foo{
- int test;
-protected:
- Foo() = default;
-};
-)--";
- expected = R"--(
-class Foo{
- int test;
-public:
- Foo(int test) : test(test)
- {}
-
-protected:
- Foo() = default;
-};
-)--";
-
- QTest::newRow("new section before existing")
- << header << expected << QByteArray() << QByteArray() << Inside;
-
- header = R"--(
-class@ Foo{
- int test;
-};
-)--";
- expected = R"--(
-class Foo{
- int test;
-public:
- Foo(int test) : test(test)
- {}
-};
-)--";
- QTest::newRow("new section at end")
- << header << expected << QByteArray() << QByteArray() << Inside;
-
- header = R"--(
-class@ Foo{
- int test;
-public:
- /**
- * Random comment
- */
- Foo(int i, int i2);
-};
-)--";
- expected = R"--(
-class Foo{
- int test;
-public:
- Foo(int test) : test(test)
- {}
- /**
- * Random comment
- */
- Foo(int i, int i2);
-};
-)--";
- QTest::newRow("in section before")
- << header << expected << QByteArray() << QByteArray() << Inside;
-
- header = R"--(
-class@ Foo{
- int test;
-public:
- Foo() = default;
-};
-)--";
- expected = R"--(
-class Foo{
- int test;
-public:
- Foo() = default;
- Foo(int test) : test(test)
- {}
-};
-)--";
- QTest::newRow("in section after")
- << header << expected << QByteArray() << QByteArray() << Inside;
-
- header = R"--(
-class@ Foo{
- int test1;
- int test2;
- int test3;
-public:
-};
-)--";
- expected = R"--(
-class Foo{
- int test1;
- int test2;
- int test3;
-public:
- Foo(int test2, int test3, int test1) : test1(test1),
- test2(test2),
- test3(test3)
- {}
-};
-)--";
- // No worry, that is not the default behavior.
- // Move first member to the back when testing with 3 or more members
- QTest::newRow("changed parameter order")
- << header << expected << QByteArray() << QByteArray() << Inside;
-
- header = R"--(
-class@ Foo{
- int test;
- int di_test;
-public:
-};
-)--";
- expected = R"--(
-class Foo{
- int test;
- int di_test;
-public:
- Foo(int test, int di_test = 42) : test(test),
- di_test(di_test)
- {}
-};
-)--";
- QTest::newRow("default parameters")
- << header << expected << QByteArray() << QByteArray() << Inside;
-
- header = R"--(
-struct Bar{
- Bar(int i);
-};
-class@ Foo : public Bar{
- int test;
-public:
-};
-)--";
- expected = R"--(
-struct Bar{
- Bar(int i);
-};
-class Foo : public Bar{
- int test;
-public:
- Foo(int test, int i) : Bar(i),
- test(test)
- {}
-};
-)--";
- QTest::newRow("parent constructor")
- << header << expected << QByteArray() << QByteArray() << Inside;
-
- header = R"--(
-struct Bar{
- Bar(int use_i = 6);
-};
-class@ Foo : public Bar{
- int test;
-public:
-};
-)--";
- expected = R"--(
-struct Bar{
- Bar(int use_i = 6);
-};
-class Foo : public Bar{
- int test;
-public:
- Foo(int test, int use_i = 6) : Bar(use_i),
- test(test)
- {}
-};
-)--";
- QTest::newRow("parent constructor with default")
- << header << expected << QByteArray() << QByteArray() << Inside;
-
- header = R"--(
-struct Bar{
- Bar(int use_i = L'A', int use_i2 = u8"B");
-};
-class@ Foo : public Bar{
-public:
-};
-)--";
- expected = R"--(
-struct Bar{
- Bar(int use_i = L'A', int use_i2 = u8"B");
-};
-class Foo : public Bar{
-public:
- Foo(int use_i = L'A', int use_i2 = u8"B") : Bar(use_i, use_i2)
- {}
-};
-)--";
- QTest::newRow("parent constructor with char/string default value")
- << header << expected << QByteArray() << QByteArray() << Inside;
-
- const QByteArray common = R"--(
-namespace N{
- template<typename T>
- struct vector{
- };
-}
-)--";
- header = common + R"--(
-namespace M{
-enum G{g};
-class@ Foo{
- N::vector<G> g;
- enum E{e}e;
-public:
-};
-}
-)--";
-
- expected = common + R"--(
-namespace M{
-enum G{g};
-class@ Foo{
- N::vector<G> g;
- enum E{e}e;
-public:
- Foo(const N::vector<G> &g, E e);
-};
-
-Foo::Foo(const N::vector<G> &g, Foo::E e) : g(g),
- e(e)
-{}
-
-}
-)--";
- QTest::newRow("source: right type outside class ")
- << QByteArray() << QByteArray() << header << expected << Outside;
- expected = common + R"--(
-namespace M{
-enum G{g};
-class@ Foo{
- N::vector<G> g;
- enum E{e}e;
-public:
- Foo(const N::vector<G> &g, E e);
-};
-}
-
-
-inline M::Foo::Foo(const N::vector<M::G> &g, M::Foo::E e) : g(g),
- e(e)
-{}
-
-)--";
- QTest::newRow("header: right type outside class ")
- << header << expected << QByteArray() << QByteArray() << Outside;
-
- expected = common + R"--(
-namespace M{
-enum G{g};
-class@ Foo{
- N::vector<G> g;
- enum E{e}e;
-public:
- Foo(const N::vector<G> &g, E e);
-};
-}
-)--";
- const QByteArray source = R"--(
-#include "file.h"
-)--";
- QByteArray expected_source = R"--(
-#include "file.h"
-
-
-namespace M {
-Foo::Foo(const N::vector<G> &g, Foo::E e) : g(g),
- e(e)
-{}
-
-}
-)--";
- QTest::newRow("source: right type inside namespace")
- << header << expected << source << expected_source << CppGenNamespace;
-
- expected_source = R"--(
-#include "file.h"
-
-using namespace M;
-Foo::Foo(const N::vector<G> &g, Foo::E e) : g(g),
- e(e)
-{}
-)--";
- QTest::newRow("source: right type with using directive")
- << header << expected << source << expected_source << CppGenUsingDirective;
-
- expected_source = R"--(
-#include "file.h"
-
-M::Foo::Foo(const N::vector<M::G> &g, M::Foo::E e) : g(g),
- e(e)
-{}
-)--";
- QTest::newRow("source: right type while rewritung types")
- << header << expected << source << expected_source << CppRewriteType;
-}
-
-void QuickfixTest::testGenerateConstructor()
-{
- class TestFactory : public GenerateConstructor
- {
- public:
- TestFactory() { setTest(); }
- };
-
- QFETCH(QByteArray, original_header);
- QFETCH(QByteArray, expected_header);
- QFETCH(QByteArray, original_source);
- QFETCH(QByteArray, expected_source);
- QFETCH(int, location);
-
- QuickFixSettings s;
- s->valueTypes << "CustomType";
- using L = ConstructorLocation;
- if (location == L::Inside) {
- s->setterInCppFileFrom = -1;
- s->setterOutsideClassFrom = -1;
- } else if (location == L::Outside) {
- s->setterInCppFileFrom = -1;
- s->setterOutsideClassFrom = 1;
- } else if (location >= L::CppGenNamespace && location <= L::CppRewriteType) {
- s->setterInCppFileFrom = 1;
- s->setterOutsideClassFrom = -1;
- using Handling = CppQuickFixSettings::MissingNamespaceHandling;
- if (location == L::CppGenNamespace)
- s->cppFileNamespaceHandling = Handling::CreateMissing;
- else if (location == L::CppGenUsingDirective)
- s->cppFileNamespaceHandling = Handling::AddUsingDirective;
- else if (location == L::CppRewriteType)
- s->cppFileNamespaceHandling = Handling::RewriteType;
- } else {
- QFAIL("location is none of the values of the ConstructorLocation enum");
- }
-
- QList<TestDocumentPtr> testDocuments;
- testDocuments << CppTestDocument::create("file.h", original_header, expected_header);
- testDocuments << CppTestDocument::create("file.cpp", original_source, expected_source);
- TestFactory factory;
- QuickFixOperationTest(testDocuments, &factory);
-}
-
-void QuickfixTest::testChangeCommentType_data()
-{
- QTest::addColumn<QString>("input");
- QTest::addColumn<QString>("expectedOutput");
-
- QTest::newRow("C -> C++ / no selection / single line") << R"(
-int var1;
-/* Other comment, unaffected */
-/* Our @comment */
-/* Another unaffected comment */
-int var2;)" << R"(
-int var1;
-/* Other comment, unaffected */
-// Our comment
-/* Another unaffected comment */
-int var2;)";
-
- QTest::newRow("C -> C++ / no selection / multi-line / preserved header and footer") << R"(
-/****************************************************
- * some info
- * more @info
- ***************************************************/)" << R"(
-/////////////////////////////////////////////////////
-// some info
-// more info
-/////////////////////////////////////////////////////)";
-
- QTest::newRow("C -> C++ / no selection / multi-line / non-preserved header and footer") << R"(
-/*
- * some info
- * more @info
- */)" << R"(
-// some info
-// more info
-)";
-
- QTest::newRow("C -> C++ / no selection / qdoc") << R"(
-/*!
- \qmlproperty string Type::element.name
- \qmlproperty int Type::element.id
-
- \brief Holds the @element name and id.
-*/)" << R"(
-//! \qmlproperty string Type::element.name
-//! \qmlproperty @int Type::element.id
-//!
-//! \brief Holds the element name and id.
-)";
-
- QTest::newRow("C -> C++ / no selection / doxygen") << R"(
-/*! \class Test
- \brief A test class.
-
- A more detailed @class description.
-*/)" << R"(
-//! \class Test
-//! \brief A test class.
-//!
-//! A more detailed class description.
-)";
-
- QTest::newRow("C -> C++ / selection / single line") << R"(
-int var1;
-/* Other comment, unaffected */
-@{start}/* Our comment */@{end}
-/* Another unaffected comment */
-int var2;)" << R"(
-int var1;
-/* Other comment, unaffected */
-// Our comment
-/* Another unaffected comment */
-int var2;)";
-
- QTest::newRow("C -> C++ / selection / multi-line / preserved header and footer") << R"(
-/****************************************************
- * @{start}some info
- * more info@{end}
- ***************************************************/)" << R"(
-/////////////////////////////////////////////////////
-// some info
-// more info
-/////////////////////////////////////////////////////)";
-
- QTest::newRow("C -> C++ / selection / multi-line / non-preserved header and footer") << R"(
-/*@{start}
- * some in@{end}fo
- * more info
- */)" << R"(
-// some info
-// more info
-)";
-
- QTest::newRow("C -> C++ / selection / qdoc") << R"(
-/*!@{start}
- \qmlproperty string Type::element.name
- \qmlproperty int Type::element.id
-
- \brief Holds the element name and id.
-*/@{end})" << R"(
-//! \qmlproperty string Type::element.name
-//! \qmlproperty int Type::element.id
-//!
-//! \brief Holds the element name and id.
-)";
-
- QTest::newRow("C -> C++ / selection / doxygen") << R"(
-/** Expand envi@{start}ronment variables in a string.
- *
- * Environment variables are accepted in the @{end}following forms:
- * $SOMEVAR, ${SOMEVAR} on Unix and %SOMEVAR% on Windows.
- * No escapes and quoting are supported.
- * If a variable is not found, it is not substituted.
- */)" << R"(
-//! Expand environment variables in a string.
-//!
-//! Environment variables are accepted in the following forms:
-//! $SOMEVAR, ${SOMEVAR} on Unix and %SOMEVAR% on Windows.
-//! No escapes and quoting are supported.
-//! If a variable is not found, it is not substituted.
-)";
-
- QTest::newRow("C -> C++ / selection / multiple comments") << R"(
-@{start}/* Affected comment */
-/* Another affected comment */
-/* A third affected comment */@{end}
-/* An unaffected comment */)" << R"(
-// Affected comment
-// Another affected comment
-// A third affected comment
-/* An unaffected comment */)";
-
- // FIXME: Remove adjacent newline along with last block
- // FIXME: Use CppRefactoringFile to auto-indent continuation lines?
- QTest::newRow("C -> C++, indented") << R"(
-struct S {
- /*
- * @This is an
- * indented comment.
- */
- void func();
-)" << R"(
-struct S {
- // This is an
-// indented comment.
-
- void func();
-)";
-
- QTest::newRow("C++ -> C / no selection / single line") << R"(
-// Other comment, unaffected
-// Our @comment
-// Another unaffected comment)" << R"(
-// Other comment, unaffected
-/* Our comment */
-// Another unaffected comment)";
-
- QTest::newRow("C++ -> C / selection / single line") << R"(
-// Other comment, unaffected
-@{start}// Our comment@{end}
-// Another unaffected comment)" << R"(
-// Other comment, unaffected
-/* Our comment */
-// Another unaffected comment)";
-
- QTest::newRow("C++ -> C / selection / multi-line / preserved header and footer") << R"(
-@{start}/////////////////////////////////////////////////////
-// some info
-// more info
-/////////////////////////////////////////////////////@{end})" << R"(
-/****************************************************/
-/* some info */
-/* more info */
-/****************************************************/)";
-
- QTest::newRow("C++ -> C / selection / qdoc") << R"(
-@{start}//! \qmlproperty string Type::element.name
-//! \qmlproperty int Type::element.id
-//!
-//! \brief Holds the element name and id.@{end}
-)" << R"(
-/*!
- \qmlproperty string Type::element.name
- \qmlproperty int Type::element.id
-
- \brief Holds the element name and id.
-*/
-)";
-
- QTest::newRow("C++ -> C / selection / doxygen") << R"(
-@{start}//! \class Test
-//! \brief A test class.
-//!
-//! A more detailed class description.@{end}
-)" << R"(
-/*!
- \class Test
- \brief A test class.
-
- A more detailed class description.
-*/
-)";
-}
-
-void QuickfixTest::testChangeCommentType()
-{
- QFETCH(QString, input);
- QFETCH(QString, expectedOutput);
-
- ConvertCommentStyle factory;
- QuickFixOperationTest(
- {CppTestDocument::create("file.h", input.toUtf8(), expectedOutput.toUtf8())},
- &factory);
-}
-
-void QuickfixTest::testMoveComments_data()
-{
- QTest::addColumn<QByteArrayList>("headers");
- QTest::addColumn<QByteArrayList>("sources");
-
- const QByteArrayList headersFuncDecl2Def{R"(
-// Function comment
-void @aFunction();
-)", R"(
-void aFunction();
-)"};
- const QByteArrayList sourcesFuncDecl2Def{R"(
-#include "file.h"
-
-void aFunction() {}
-)", R"(
-#include "file.h"
-
-// Function comment
-void aFunction() {}
-)"};
- QTest::newRow("function: from decl to def") << headersFuncDecl2Def << sourcesFuncDecl2Def;
-
- const QByteArrayList headersFuncDef2Decl{R"(
-void aFunction();
-)", R"(
-/* function */
-/* comment */
-void aFunction();
-)"};
- const QByteArrayList sourcesFuncDef2Decl{R"(
-#include "file.h"
-
-/* function */
-/* comment */
-void a@Function() {}
-)", R"(
-#include "file.h"
-
-void aFunction() {}
-)"};
- QTest::newRow("function: from def to decl") << headersFuncDef2Decl << sourcesFuncDef2Decl;
-
- const QByteArrayList headersFuncNoDef{R"(
-// Function comment
-void @aFunction();
-)", R"(
-// Function comment
-void aFunction();
-)"};
- QTest::newRow("function: no def") << headersFuncNoDef << QByteArrayList();
-
- const QByteArrayList headersFuncNoDecl{R"(
-// Function comment
-inline void @aFunction() {}
-)", R"(
-// Function comment
-inline void aFunction() {}
-)"};
- QTest::newRow("function: no decl") << headersFuncNoDecl << QByteArrayList();
-
- const QByteArrayList headersFuncTemplateDecl2Def{R"(
-// Function comment
-template<typename T> T @aFunction();
-
-template<typename T> inline T aFunction() { return T(); }
-)", R"(
-template<typename T> T aFunction();
-
-// Function comment
-template<typename T> inline T aFunction() { return T(); }
-)"};
- QTest::newRow("function template: from decl to def") << headersFuncTemplateDecl2Def
- << QByteArrayList();
-
- const QByteArrayList headersFuncTemplateDef2Decl{R"(
-template<typename T> T aFunction();
-
-// Function comment
-template<typename T> inline T @aFunction() { return T(); }
-)", R"(
-// Function comment
-template<typename T> T aFunction();
-
-template<typename T> inline T aFunction() { return T(); }
-)"};
- QTest::newRow("function template: from def to decl") << headersFuncTemplateDef2Decl
- << QByteArrayList();
-
- const QByteArrayList headersMemberDecl2Def{R"(
-class C {
- /**
- * \brief Foo::aMember
- */
- void @aMember();
-)", R"(
-class C {
- void aMember();
-)"};
- const QByteArrayList sourcesMemberDecl2Def{R"(
-#include "file.h"
-
-void C::aMember() {}
-)", R"(
-#include "file.h"
-
-/**
- * \brief Foo::aMember
- */
-void C::aMember() {}
-)"};
- QTest::newRow("member function: from decl to def") << headersMemberDecl2Def
- << sourcesMemberDecl2Def;
-
- const QByteArrayList headersMemberDef2Decl{R"(
-class C {
- void aMember();
-)", R"(
-class C {
- /**
- * \brief Foo::aMember
- */
- void aMember();
-)"};
- const QByteArrayList sourcesMemberDef2Decl{R"(
-#include "file.h"
-
-/**
- * \brief Foo::aMember
- */
-void C::aMember() {@}
-)", R"(
-#include "file.h"
-
-void C::aMember() {}
-)"};
- QTest::newRow("member function: from def to decl") << headersMemberDef2Decl
- << sourcesMemberDef2Decl;
-}
-
-void QuickfixTest::testMoveComments()
-{
- QFETCH(QByteArrayList, headers);
- QFETCH(QByteArrayList, sources);
-
- QList<TestDocumentPtr> documents;
- QCOMPARE(headers.size(), 2);
- documents << CppTestDocument::create("file.h", headers.at(0), headers.at(1));
- if (!sources.isEmpty()) {
- QCOMPARE(sources.size(), 2);
- documents << CppTestDocument::create("file.cpp", sources.at(0), sources.at(1));
- }
- MoveFunctionComments factory;
- QByteArray failMessage;
- if (QByteArray(QTest::currentDataTag()) == "function template: from def to decl")
- failMessage = "decl/def switch doesn't work for templates";
- QuickFixOperationTest(documents, &factory, {}, {}, failMessage);
-}
-
-void QuickfixTest::testConvertToMetaMethodInvocation_data()
-{
- QTest::addColumn<QByteArray>("input");
- QTest::addColumn<QByteArray>("expected");
-
- // ^ marks the cursor locations.
- // $ marks the replacement regions.
- // The quoted string in the comment is the data tag.
- // The rest of the comment is the replacement string.
- const QByteArray allCases = R"(
-class C {
-public:
- C() {
- $this->^aSignal()$; // "signal from region on pointer to object" QMetaObject::invokeMethod(this, "aSignal")
- C c;
- $c.^aSignal()$; // "signal from region on object value" QMetaObject::invokeMethod(&c, "aSignal")
- $(new C)->^aSignal()$; // "signal from region on expression" QMetaObject::invokeMethod((new C), "aSignal")
- $emit this->^aSignal()$; // "signal from region, with emit" QMetaObject::invokeMethod(this, "aSignal")
- $Q_EMIT this->^aSignal()$; // "signal from region, with Q_EMIT" QMetaObject::invokeMethod(this, "aSignal")
- $this->^aSlot()$; // "slot from region" QMetaObject::invokeMethod(this, "aSlot")
- $this->^noArgs()$; // "Q_SIGNAL, no arguments" QMetaObject::invokeMethod(this, "noArgs")
- $this->^oneArg(0)$; // "Q_SLOT, one argument" QMetaObject::invokeMethod(this, "oneArg", Q_ARG(int, 0))
- $this->^twoArgs(0, c)$; // "Q_INVOKABLE, two arguments" QMetaObject::invokeMethod(this, "twoArgs", Q_ARG(int, 0), Q_ARG(C, c))
- this->^notInvokable(); // "not invokable"
- }
-
-signals:
- void aSignal();
-
-private slots:
- void aSlot();
-
-private:
- Q_SIGNAL void noArgs();
- Q_SLOT void oneArg(int index);
- Q_INVOKABLE void twoArgs(int index, const C &value);
- void notInvokable();
-};
-)";
-
- qsizetype nextCursor = allCases.indexOf('^');
- while (nextCursor != -1) {
- const int commentStart = allCases.indexOf("//", nextCursor);
- QVERIFY(commentStart != -1);
- const int tagStart = allCases.indexOf('"', commentStart + 2);
- QVERIFY(tagStart != -1);
- const int tagEnd = allCases.indexOf('"', tagStart + 1);
- QVERIFY(tagEnd != -1);
- QByteArray input = allCases;
- QByteArray output = allCases;
- input.replace(nextCursor, 1, "@");
- const QByteArray tag = allCases.mid(tagStart + 1, tagEnd - tagStart - 1);
- const int prevNewline = allCases.lastIndexOf('\n', nextCursor);
- const int regionStart = allCases.lastIndexOf('$', nextCursor);
- bool hasReplacement = false;
- if (regionStart != -1 && regionStart > prevNewline) {
- const int regionEnd = allCases.indexOf('$', regionStart + 1);
- QVERIFY(regionEnd != -1);
- const int nextNewline = allCases.indexOf('\n', tagEnd);
- QVERIFY(nextNewline != -1);
- const QByteArray replacement
- = allCases.mid(tagEnd + 1, nextNewline - tagEnd - 1).trimmed();
- output.replace(regionStart, regionEnd - regionStart, replacement);
- hasReplacement = true;
- }
- static const auto matcher = [](char c) { return c == '^' || c == '$'; };
- input.removeIf(matcher);
- if (hasReplacement) {
- output.removeIf(matcher);
- output.prepend("#include <QMetaObject>\n\n");
- } else {
- output.clear();
- }
- QTest::newRow(tag.data()) << input << output;
- nextCursor = allCases.indexOf('^', nextCursor + 1);
- }
-}
-
-void QuickfixTest::testConvertToMetaMethodInvocation()
-{
- QFETCH(QByteArray, input);
- QFETCH(QByteArray, expected);
-
- ConvertToMetaMethodCall factory;
- QuickFixOperationTest({CppTestDocument::create("file.cpp", input, expected)}, &factory);
-}
-
-void QuickfixTest::testMoveClassToOwnFile_data()
-{
- QTest::addColumn<QString>("projectName");
- QTest::addColumn<QString>("fileName");
- QTest::addColumn<QString>("className");
- QTest::addColumn<bool>("applicable");
-
- QTest::newRow("nested") << "nested" << "main.cpp" << "Inner" << false;
- QTest::newRow("file name match 1") << "match1" << "TheClass.h" << "TheClass" << false;
- QTest::newRow("file name match 2") << "match2" << "theclass.h" << "TheClass" << false;
- QTest::newRow("file name match 3") << "match3" << "the_class.h" << "TheClass" << false;
- QTest::newRow("single") << "single" << "theheader.h" << "TheClass" << false;
- QTest::newRow("complex") << "complex" << "theheader.h" << "TheClass" << true;
- QTest::newRow("header only") << "header-only" << "theheader.h" << "TheClass" << true;
- QTest::newRow("decl in source file") << "decl-in-source" << "thesource.cpp" << "TheClass" << true;
- QTest::newRow("template") << "template" << "theheader.h" << "TheClass" << true;
-}
-
-void QuickfixTest::testMoveClassToOwnFile()
-{
- QFETCH(QString, projectName);
- QFETCH(QString, fileName);
- QFETCH(QString, className);
- QFETCH(bool, applicable);
- using namespace CppEditor::Tests;
-
- // Set up project.
- Kit * const kit = Utils::findOr(KitManager::kits(), nullptr, [](const Kit *k) {
- return k->isValid() && !k->hasWarning() && k->value("QtSupport.QtInformation").isValid();
- });
- if (!kit)
- QSKIP("The test requires at least one valid kit with a valid Qt");
- const auto projectDir = std::make_unique<TemporaryCopiedDir>(
- ":/cppeditor/testcases/move-class/" + projectName);
- SourceFilesRefreshGuard refreshGuard;
- ProjectOpenerAndCloser projectMgr;
- QVERIFY(projectMgr.open(projectDir->absolutePath(projectName + ".pro"), true, kit));
- QVERIFY(refreshGuard.wait());
-
- // Open header file and locate class.
- const auto headerFilePath = projectDir->absolutePath(fileName);
- QVERIFY2(headerFilePath.exists(), qPrintable(headerFilePath.toUserOutput()));
- const auto editor = qobject_cast<BaseTextEditor *>(EditorManager::openEditor(headerFilePath));
- QVERIFY(editor);
- const auto doc = qobject_cast<TextEditor::TextDocument *>(editor->document());
- QVERIFY(doc);
- QTextCursor classCursor = doc->document()->find("class " + className);
- QVERIFY(!classCursor.isNull());
- editor->setCursorPosition(classCursor.position());
- const auto editorWidget = qobject_cast<CppEditorWidget *>(editor->editorWidget());
- QVERIFY(editorWidget);
- QVERIFY(TestCase::waitForRehighlightedSemanticDocument(editorWidget));
-
- // Query factory.
- MoveClassToOwnFile factory;
- factory.setNonInteractive();
- CppQuickFixInterface quickFixInterface(editorWidget, ExplicitlyInvoked);
- QuickFixOperations operations;
- factory.match(quickFixInterface, operations);
- QCOMPARE(operations.isEmpty(), !applicable);
- if (!applicable)
- return;
- operations.first()->perform();
- QVERIFY(waitForSignalOrTimeout(doc, &IDocument::saved, 30000));
- QTest::qWait(1000);
-
- // Compare all files.
- const FileFilter filter({"*_expected"}, QDir::Files);
- const FilePaths expectedDocuments = projectDir->filePath().dirEntries(filter);
- QVERIFY(!expectedDocuments.isEmpty());
- for (const FilePath &expected : expectedDocuments) {
- static const QString suffix = "_expected";
- const FilePath actual = expected.parentDir()
- .pathAppended(expected.fileName().chopped(suffix.length()));
- QVERIFY(actual.exists());
- const auto actualContents = actual.fileContents();
- QVERIFY(actualContents);
- const auto expectedContents = expected.fileContents();
- const QByteArrayList actualLines = actualContents->split('\n');
- const QByteArrayList expectedLines = expectedContents->split('\n');
- if (actualLines.size() != expectedLines.size()) {
- qDebug().noquote().nospace() << "---\n" << *expectedContents << "EOF";
- qDebug().noquote().nospace() << "+++\n" << *actualContents << "EOF";
- }
- QCOMPARE(actualLines.size(), expectedLines.size());
- for (int i = 0; i < actualLines.size(); ++i) {
- const QByteArray actualLine = actualLines.at(i);
- const QByteArray expectedLine = expectedLines.at(i);
- if (actualLine != expectedLine)
- qDebug() << "Unexpected content in line" << (i + 1) << "of file"
- << actual.fileName();
- QCOMPARE(actualLine, expectedLine);
- }
- }
-}
-
-} // namespace CppEditor::Internal::Tests
diff --git a/src/plugins/cppeditor/cppquickfix_test.h b/src/plugins/cppeditor/cppquickfix_test.h
deleted file mode 100644
index 27ba90f0d0..0000000000
--- a/src/plugins/cppeditor/cppquickfix_test.h
+++ /dev/null
@@ -1,239 +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 "cppquickfix.h"
-#include "cpptoolstestcase.h"
-
-#include <projectexplorer/headerpath.h>
-
-#include <QByteArray>
-#include <QList>
-#include <QObject>
-#include <QSharedPointer>
-#include <QStringList>
-
-namespace TextEditor { class QuickFixOperation; }
-
-namespace CppEditor {
-class CppCodeStylePreferences;
-
-namespace Internal {
-namespace Tests {
-
-class BaseQuickFixTestCase : public CppEditor::Tests::TestCase
-{
-public:
- /// Exactly one QuickFixTestDocument must contain the cursor position marker '@'
- /// or "@{start}" and "@{end}"
- BaseQuickFixTestCase(const QList<TestDocumentPtr> &testDocuments,
- const ProjectExplorer::HeaderPaths &headerPaths,
- const QByteArray &clangFormatSettings = {});
-
- ~BaseQuickFixTestCase();
-
-protected:
- TestDocumentPtr m_documentWithMarker;
- QList<TestDocumentPtr> m_testDocuments;
-
-private:
- QScopedPointer<CppEditor::Tests::TemporaryDir> m_temporaryDirectory;
-
- CppCodeStylePreferences *m_cppCodeStylePreferences;
- QByteArray m_cppCodeStylePreferencesOriginalDelegateId;
-
- ProjectExplorer::HeaderPaths m_headerPathsToRestore;
- bool m_restoreHeaderPaths;
-};
-
-/// Tests a concrete QuickFixOperation of a given CppQuickFixFactory
-class QuickFixOperationTest : public BaseQuickFixTestCase
-{
-public:
- QuickFixOperationTest(const QList<TestDocumentPtr> &testDocuments,
- CppQuickFixFactory *factory,
- const ProjectExplorer::HeaderPaths &headerPaths
- = ProjectExplorer::HeaderPaths(),
- int operationIndex = 0,
- const QByteArray &expectedFailMessage = {},
- const QByteArray &clangFormatSettings = {});
-
- static void run(const QList<TestDocumentPtr> &testDocuments,
- CppQuickFixFactory *factory,
- const QString &headerPath,
- int operationIndex = 0);
-};
-
-QList<TestDocumentPtr> singleDocument(const QByteArray &original,
- const QByteArray &expected);
-
-class QuickfixTest : public QObject
-{
- Q_OBJECT
-
-private slots:
- void testGeneric_data();
- void testGeneric();
-
- void testGenerateGetterSetterNamespaceHandlingCreate_data();
- void testGenerateGetterSetterNamespaceHandlingCreate();
- void testGenerateGetterSetterNamespaceHandlingAddUsing_data();
- void testGenerateGetterSetterNamespaceHandlingAddUsing();
- void testGenerateGetterSetterNamespaceHandlingFullyQualify_data();
- void testGenerateGetterSetterNamespaceHandlingFullyQualify();
- void testGenerateGetterSetterCustomNames_data();
- void testGenerateGetterSetterCustomNames();
- void testGenerateGetterSetterValueTypes_data();
- void testGenerateGetterSetterValueTypes();
- void testGenerateGetterSetterCustomTemplate();
- void testGenerateGetterSetterNeedThis();
- void testGenerateGetterSetterOfferedFixes_data();
- void testGenerateGetterSetterOfferedFixes();
- void testGenerateGetterSetterGeneralTests_data();
- void testGenerateGetterSetterGeneralTests();
- void testGenerateGetterSetterOnlyGetter();
- void testGenerateGetterSetterOnlySetter();
- void testGenerateGetterSetterAnonymousClass();
- void testGenerateGetterSetterInlineInHeaderFile();
- void testGenerateGetterSetterOnlySetterHeaderFileWithIncludeGuard();
- void testGenerateGetterFunctionAsTemplateArg();
- void testGenerateGettersSetters_data();
- void testGenerateGettersSetters();
-
- void testInsertQtPropertyMembers_data();
- void testInsertQtPropertyMembers();
-
- void testInsertMemberFromUse_data();
- void testInsertMemberFromUse();
-
- void testConvertQt4ConnectConnectOutOfClass();
- void testConvertQt4ConnectConnectWithinClass_data();
- void testConvertQt4ConnectConnectWithinClass();
- void testConvertQt4ConnectDifferentNamespace();
-
- void testInsertDefFromDeclAfterClass();
- void testInsertDefFromDeclHeaderSourceBasic1();
- void testInsertDefFromDeclHeaderSourceBasic2();
- void testInsertDefFromDeclHeaderSourceBasic3();
- void testInsertDefFromDeclHeaderSourceNamespace1();
- void testInsertDefFromDeclHeaderSourceNamespace2();
- void testInsertDefFromDeclInsideClass();
- void testInsertDefFromDeclNotTriggeringWhenDefinitionExists();
- void testInsertDefFromDeclFindRightImplementationFile();
- void testInsertDefFromDeclIgnoreSurroundingGeneratedDeclarations();
- void testInsertDefFromDeclRespectWsInOperatorNames1();
- void testInsertDefFromDeclRespectWsInOperatorNames2();
- void testInsertDefFromDeclNoexceptSpecifier();
- void testInsertDefFromDeclMacroUsesAtEndOfFile1();
- void testInsertDefFromDeclMacroUsesAtEndOfFile2();
- void testInsertDefFromDeclErroneousStatementAtEndOfFile();
- void testInsertDefFromDeclRvalueReference();
- void testInsertDefFromDeclFunctionTryBlock();
- void testInsertDefFromDeclUsingDecl();
- void testInsertDefFromDeclFindImplementationFile();
- void testInsertDefFromDeclUnicodeIdentifier();
- void testInsertDefFromDeclTemplateClass();
- void testInsertDefFromDeclTemplateClassWithValueParam();
- void testInsertDefFromDeclTemplateFunction();
- void testInsertDefFromDeclTemplateClassAndTemplateFunction();
- void testInsertDefFromDeclTemplateClassAndFunctionInsideNamespace();
- void testInsertDefFromDeclFunctionWithSignedUnsignedArgument();
- void testInsertDefFromDeclNotTriggeredForFriendFunc();
- void testInsertDefFromDeclMinimalFunctionParameterType();
- void testInsertDefFromDeclAliasTemplateAsReturnType();
- void testInsertDefsFromDecls_data();
- void testInsertDefsFromDecls();
- void testInsertAndFormatDefsFromDecls();
-
- void testInsertDefOutsideFromDeclTemplateClassAndTemplateFunction();
- void testInsertDefOutsideFromDeclTemplateClass();
- void testInsertDefOutsideFromDeclTemplateFunction();
- void testInsertDefOutsideFromDeclFunction();
-
- void testInsertDeclFromDef();
- void testInsertDeclFromDefTemplateFuncTypename();
- void testInsertDeclFromDefTemplateFuncInt();
- void testInsertDeclFromDefTemplateReturnType();
- void testInsertDeclFromDefNotTriggeredForTemplateFunc();
-
- void testAddIncludeForUndefinedIdentifier_data();
- void testAddIncludeForUndefinedIdentifier();
- void testAddIncludeForUndefinedIdentifierNoDoubleQtHeaderInclude();
-
- void testAddForwardDeclForUndefinedIdentifier_data();
- void testAddForwardDeclForUndefinedIdentifier();
-
- void testMoveFuncDefOutsideMemberFuncToCpp();
- void testMoveFuncDefOutsideMemberFuncToCppInsideNS();
- void testMoveFuncDefOutsideMemberFuncOutside1();
- void testMoveFuncDefOutsideMemberFuncOutside2();
- void testMoveFuncDefOutsideMemberFuncToCppNS();
- void testMoveFuncDefOutsideMemberFuncToCppNSUsing();
- void testMoveFuncDefOutsideMemberFuncOutsideWithNs();
- void testMoveFuncDefOutsideFreeFuncToCpp();
- void testMoveFuncDefOutsideFreeFuncToCppNS();
- void testMoveFuncDefOutsideCtorWithInitialization1();
- void testMoveFuncDefOutsideCtorWithInitialization2();
- void testMoveFuncDefOutsideAfterClass();
- void testMoveFuncDefOutsideRespectWsInOperatorNames1();
- void testMoveFuncDefOutsideRespectWsInOperatorNames2();
- void testMoveFuncDefOutsideMacroUses();
- void testMoveFuncDefOutsideTemplate();
- void testMoveFuncDefOutsideMemberFunctionTemplate();
- void testMoveFuncDefOutsideTemplateSpecializedClass();
- void testMoveFuncDefOutsideUnnamedTemplate();
- void testMoveFuncDefOutsideMemberFuncToCppStatic();
- void testMoveFuncDefOutsideMemberFuncToCppWithInlinePartOfName();
- void testMoveFuncDefOutsideMixedQualifiers();
-
- void testMoveAllFuncDefOutsideMemberFuncToCpp();
- void testMoveAllFuncDefOutsideMemberFuncOutside();
- void testMoveAllFuncDefOutsideDoNotTriggerOnBaseClass();
- void testMoveAllFuncDefOutsideClassWithBaseClass();
- void testMoveAllFuncDefOutsideIgnoreMacroCode();
-
- void testMoveFuncDefToDecl_data();
- void testMoveFuncDefToDecl();
-
- void testMoveFuncDefToDeclMacroUses();
-
- void testAssignToLocalVariableTemplates();
-
- void testExtractFunction_data();
- void testExtractFunction();
-
- void testExtractLiteralAsParameterTypeDeduction_data();
- void testExtractLiteralAsParameterTypeDeduction();
- void testExtractLiteralAsParameterFreeFunctionSeparateFiles();
- void testExtractLiteralAsParameterMemberFunctionSeparateFiles();
- void testExtractLiteralAsParameterNotTriggeringForInvalidCode();
-
- void testAddCurlyBraces_data();
- void testAddCurlyBraces();
-
- void testRemoveUsingNamespace_data();
- void testRemoveUsingNamespace();
- void testRemoveUsingNamespaceSimple_data();
- void testRemoveUsingNamespaceSimple();
- void testRemoveUsingNamespaceDifferentSymbols();
-
- void testGenerateConstructor_data();
- void testGenerateConstructor();
-
- void testChangeCommentType_data();
- void testChangeCommentType();
-
- void testMoveComments_data();
- void testMoveComments();
-
- void testConvertToMetaMethodInvocation_data();
- void testConvertToMetaMethodInvocation();
-
- void testMoveClassToOwnFile_data();
- void testMoveClassToOwnFile();
-};
-
-} // namespace Tests
-} // namespace Internal
-} // namespace CppEditor
diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp
deleted file mode 100644
index 6bb1627676..0000000000
--- a/src/plugins/cppeditor/cppquickfixes.cpp
+++ /dev/null
@@ -1,10675 +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 "cppquickfixes.h"
-
-#include "baseeditordocumentprocessor.h"
-#include "cppcodestylesettings.h"
-#include "cppeditordocument.h"
-#include "cppeditortr.h"
-#include "cppeditorwidget.h"
-#include "cppfilesettingspage.h"
-#include "cppfunctiondecldeflink.h"
-#include "cppinsertvirtualmethods.h"
-#include "cpplocatordata.h"
-#include "cpppointerdeclarationformatter.h"
-#include "cppquickfixassistant.h"
-#include "cppquickfixprojectsettings.h"
-#include "cpprefactoringchanges.h"
-#include "cpptoolsreuse.h"
-#include "includeutils.h"
-#include "indexitem.h"
-#include "insertionpointlocator.h"
-#include "symbolfinder.h"
-
-#include <coreplugin/icore.h>
-#include <coreplugin/messagemanager.h>
-
-#include <cplusplus/ASTPath.h>
-#include <cplusplus/CPlusPlusForwardDeclarations.h>
-#include <cplusplus/CppRewriter.h>
-#include <cplusplus/declarationcomments.h>
-#include <cplusplus/NamePrettyPrinter.h>
-#include <cplusplus/TypeOfExpression.h>
-#include <cplusplus/TypePrettyPrinter.h>
-
-#include <extensionsystem/pluginmanager.h>
-
-#include <projectexplorer/editorconfiguration.h>
-#include <projectexplorer/projectnodes.h>
-#include <projectexplorer/projecttree.h>
-#include <projectexplorer/projectmanager.h>
-
-#include <texteditor/tabsettings.h>
-
-#include <utils/algorithm.h>
-#include <utils/basetreeview.h>
-#include <utils/codegeneration.h>
-#include <utils/layoutbuilder.h>
-#include <utils/fancylineedit.h>
-#include <utils/fileutils.h>
-#include <utils/pathchooser.h>
-#include <utils/qtcassert.h>
-#include <utils/treemodel.h>
-#include <utils/treeviewcombobox.h>
-
-#ifdef WITH_TESTS
-#include <QAbstractItemModelTester>
-#endif
-#include <QApplication>
-#include <QCheckBox>
-#include <QComboBox>
-#include <QDialog>
-#include <QDialogButtonBox>
-#include <QDir>
-#include <QFileInfo>
-#include <QFormLayout>
-#include <QGridLayout>
-#include <QHash>
-#include <QHeaderView>
-#include <QInputDialog>
-#include <QMimeData>
-#include <QPair>
-#include <QProxyStyle>
-#include <QPushButton>
-#include <QRegularExpression>
-#include <QSharedPointer>
-#include <QStack>
-#include <QStyledItemDelegate>
-#include <QTableView>
-#include <QTextCodec>
-#include <QTextCursor>
-#include <QVBoxLayout>
-
-#include <bitset>
-#include <cctype>
-#include <functional>
-#include <limits>
-#include <vector>
-
-using namespace CPlusPlus;
-using namespace ProjectExplorer;
-using namespace TextEditor;
-using namespace Utils;
-
-namespace CppEditor {
-
-static QList<CppQuickFixFactory *> g_cppQuickFixFactories;
-
-CppQuickFixFactory::CppQuickFixFactory()
-{
- g_cppQuickFixFactories.append(this);
-}
-
-CppQuickFixFactory::~CppQuickFixFactory()
-{
- g_cppQuickFixFactories.removeOne(this);
-}
-
-void CppQuickFixFactory::match(const Internal::CppQuickFixInterface &interface,
- QuickFixOperations &result)
-{
- if (m_clangdReplacement) {
- if (const auto clangdVersion = CppModelManager::usesClangd(
- interface.currentFile()->editor()->textDocument());
- clangdVersion && clangdVersion >= m_clangdReplacement) {
- return;
- }
- }
-
- doMatch(interface, result);
-}
-
-const QList<CppQuickFixFactory *> &CppQuickFixFactory::cppQuickFixFactories()
-{
- return g_cppQuickFixFactories;
-}
-
-namespace Internal {
-
-QString inlinePrefix(const FilePath &targetFile, const std::function<bool()> &extraCondition = {})
-{
- if (ProjectFile::isHeader(ProjectFile::classify(targetFile.path()))
- && (!extraCondition || extraCondition())) {
- return "inline ";
- }
- return {};
-}
-
-// In the following anonymous namespace all functions are collected, which could be of interest for
-// different quick fixes.
-namespace {
-
-enum DefPos {
- DefPosInsideClass,
- DefPosOutsideClass,
- DefPosImplementationFile
-};
-
-
-inline bool isQtStringLiteral(const QByteArray &id)
-{
- return id == "QLatin1String" || id == "QLatin1Literal" || id == "QStringLiteral"
- || id == "QByteArrayLiteral";
-}
-
-inline bool isQtStringTranslation(const QByteArray &id)
-{
- return id == "tr" || id == "trUtf8" || id == "translate" || id == "QT_TRANSLATE_NOOP";
-}
-
-Class *isMemberFunction(const LookupContext &context, Function *function)
-{
- QTC_ASSERT(function, return nullptr);
-
- Scope *enclosingScope = function->enclosingScope();
- while (!(enclosingScope->asNamespace() || enclosingScope->asClass()))
- enclosingScope = enclosingScope->enclosingScope();
- QTC_ASSERT(enclosingScope != nullptr, return nullptr);
-
- const Name *functionName = function->name();
- if (!functionName)
- return nullptr;
-
- if (!functionName->asQualifiedNameId())
- return nullptr; // trying to add a declaration for a global function
-
- const QualifiedNameId *q = functionName->asQualifiedNameId();
- if (!q->base())
- return nullptr;
-
- if (ClassOrNamespace *binding = context.lookupType(q->base(), enclosingScope)) {
- const QList<Symbol *> symbols = binding->symbols();
- for (Symbol *s : symbols) {
- if (Class *matchingClass = s->asClass())
- return matchingClass;
- }
- }
-
- return nullptr;
-}
-
-Namespace *isNamespaceFunction(const LookupContext &context, Function *function)
-{
- QTC_ASSERT(function, return nullptr);
- if (isMemberFunction(context, function))
- return nullptr;
-
- Scope *enclosingScope = function->enclosingScope();
- while (!(enclosingScope->asNamespace() || enclosingScope->asClass()))
- enclosingScope = enclosingScope->enclosingScope();
- QTC_ASSERT(enclosingScope != nullptr, return nullptr);
-
- const Name *functionName = function->name();
- if (!functionName)
- return nullptr;
-
- // global namespace
- if (!functionName->asQualifiedNameId()) {
- const QList<Symbol *> symbols = context.globalNamespace()->symbols();
- for (Symbol *s : symbols) {
- if (Namespace *matchingNamespace = s->asNamespace())
- return matchingNamespace;
- }
- return nullptr;
- }
-
- const QualifiedNameId *q = functionName->asQualifiedNameId();
- if (!q->base())
- return nullptr;
-
- if (ClassOrNamespace *binding = context.lookupType(q->base(), enclosingScope)) {
- const QList<Symbol *> symbols = binding->symbols();
- for (Symbol *s : symbols) {
- if (Namespace *matchingNamespace = s->asNamespace())
- return matchingNamespace;
- }
- }
-
- return nullptr;
-}
-
-// Given include is e.g. "afile.h" or <afile.h> (quotes/angle brackets included!).
-static void insertNewIncludeDirective(const QString &include,
- CppRefactoringFilePtr file,
- const Document::Ptr &cppDocument,
- ChangeSet &changes)
-{
- // Find optimal position
- unsigned newLinesToPrepend = 0;
- unsigned newLinesToAppend = 0;
- const int insertLine = lineForNewIncludeDirective(file->filePath(), file->document(),
- cppDocument, IgnoreMocIncludes, AutoDetect,
- include,
- &newLinesToPrepend, &newLinesToAppend);
- QTC_ASSERT(insertLine >= 1, return);
- const int insertPosition = file->position(insertLine, 1);
- QTC_ASSERT(insertPosition >= 0, return);
-
- // Construct text to insert
- const QString includeLine = QLatin1String("#include ") + include + QLatin1Char('\n');
- QString prependedNewLines, appendedNewLines;
- while (newLinesToAppend--)
- appendedNewLines += QLatin1String("\n");
- while (newLinesToPrepend--)
- prependedNewLines += QLatin1String("\n");
- const QString textToInsert = prependedNewLines + includeLine + appendedNewLines;
-
- // Insert
- changes.insert(insertPosition, textToInsert);
-}
-
-bool nameIncludesOperatorName(const Name *name)
-{
- return name->asOperatorNameId()
- || (name->asQualifiedNameId() && name->asQualifiedNameId()->name()->asOperatorNameId());
-}
-
-QString memberBaseName(const QString &name)
-{
- const auto validName = [](const QString &name) {
- return !name.isEmpty() && !name.at(0).isDigit();
- };
- QString baseName = name;
-
- CppQuickFixSettings *settings = CppQuickFixProjectsSettings::getQuickFixSettings(
- ProjectExplorer::ProjectTree::currentProject());
- const QString &nameTemplate = settings->memberVariableNameTemplate;
- const QString prefix = nameTemplate.left(nameTemplate.indexOf('<'));
- const QString postfix = nameTemplate.mid(nameTemplate.lastIndexOf('>') + 1);
- if (name.startsWith(prefix) && name.endsWith(postfix)) {
- const QString base = name.mid(prefix.length(), name.length() - postfix.length());
- if (validName(base))
- return base;
- }
-
- // Remove leading and trailing "_"
- while (baseName.startsWith(QLatin1Char('_')))
- baseName.remove(0, 1);
- while (baseName.endsWith(QLatin1Char('_')))
- baseName.chop(1);
- if (baseName != name && validName(baseName))
- return baseName;
-
- // If no leading/trailing "_": remove "m_" and "m" prefix
- if (baseName.startsWith(QLatin1String("m_"))) {
- baseName.remove(0, 2);
- } else if (baseName.startsWith(QLatin1Char('m')) && baseName.length() > 1
- && baseName.at(1).isUpper()) {
- baseName.remove(0, 1);
- baseName[0] = baseName.at(0).toLower();
- }
-
- return validName(baseName) ? baseName : name;
-}
-
-// Returns a non-null value if and only if the cursor is on the name of a (proper) class
-// declaration or at some place inside the body of a class declaration that does not
-// correspond to an AST of its own, i.e. on "empty space".
-ClassSpecifierAST *astForClassOperations(const CppQuickFixInterface &interface)
-{
- const QList<AST *> &path = interface.path();
- if (path.isEmpty())
- return nullptr;
- if (const auto classSpec = path.last()->asClassSpecifier()) // Cursor inside class decl?
- return classSpec;
-
- // Cursor on a class name?
- if (path.size() < 2)
- return nullptr;
- const SimpleNameAST * const nameAST = path.at(path.size() - 1)->asSimpleName();
- if (!nameAST || !interface.isCursorOn(nameAST))
- return nullptr;
- if (const auto classSpec = path.at(path.size() - 2)->asClassSpecifier())
- return classSpec;
- return nullptr;
-}
-
-QString nameString(const NameAST *name)
-{
- return CppCodeStyleSettings::currentProjectCodeStyleOverview().prettyName(name->name);
-}
-
-static FullySpecifiedType typeOfExpr(const ExpressionAST *expr,
- const CppRefactoringFilePtr &file,
- const Snapshot &snapshot,
- const LookupContext &context)
-{
- 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().get();
- return rewriteType(result.first().type(), &env, control);
-}
-
-// 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, bool makeConst)
-{
- 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 {
- return typeOfExpr(expr, file, snapshot, context);
- };
-
- 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);
- func.setConst(makeConst);
- 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 {
-
-class InverseLogicalComparisonOp: public CppQuickFixOperation
-{
-public:
- InverseLogicalComparisonOp(const CppQuickFixInterface &interface,
- int priority,
- BinaryExpressionAST *binary,
- Kind invertToken)
- : CppQuickFixOperation(interface, priority)
- , binary(binary)
- {
- Token tok;
- tok.f.kind = invertToken;
- replacement = QLatin1String(tok.spell());
-
- // check for enclosing nested expression
- if (priority - 1 >= 0)
- nested = interface.path()[priority - 1]->asNestedExpression();
-
- // check for ! before parentheses
- if (nested && priority - 2 >= 0) {
- negation = interface.path()[priority - 2]->asUnaryExpression();
- if (negation && !interface.currentFile()->tokenAt(negation->unary_op_token).is(T_EXCLAIM))
- negation = nullptr;
- }
- }
-
- QString description() const override
- {
- return Tr::tr("Rewrite Using %1").arg(replacement);
- }
-
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
-
- ChangeSet changes;
- if (negation) {
- // can't remove parentheses since that might break precedence
- changes.remove(currentFile->range(negation->unary_op_token));
- } else if (nested) {
- changes.insert(currentFile->startOf(nested), QLatin1String("!"));
- } else {
- changes.insert(currentFile->startOf(binary), QLatin1String("!("));
- changes.insert(currentFile->endOf(binary), QLatin1String(")"));
- }
- changes.replace(currentFile->range(binary->binary_op_token), replacement);
- currentFile->setChangeSet(changes);
- currentFile->apply();
- }
-
-private:
- BinaryExpressionAST *binary = nullptr;
- NestedExpressionAST *nested = nullptr;
- UnaryExpressionAST *negation = nullptr;
-
- QString replacement;
-};
-
-} // anonymous namespace
-
-void InverseLogicalComparison::doMatch(const CppQuickFixInterface &interface,
- QuickFixOperations &result)
-{
- CppRefactoringFilePtr file = interface.currentFile();
-
- const QList<AST *> &path = interface.path();
- if (path.isEmpty())
- return;
- int index = path.size() - 1;
- BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
- if (!binary)
- return;
- if (!interface.isCursorOn(binary->binary_op_token))
- return;
-
- Kind invertToken;
- switch (file->tokenAt(binary->binary_op_token).kind()) {
- case T_LESS_EQUAL:
- invertToken = T_GREATER;
- break;
- case T_LESS:
- invertToken = T_GREATER_EQUAL;
- break;
- case T_GREATER:
- invertToken = T_LESS_EQUAL;
- break;
- case T_GREATER_EQUAL:
- invertToken = T_LESS;
- break;
- case T_EQUAL_EQUAL:
- invertToken = T_EXCLAIM_EQUAL;
- break;
- case T_EXCLAIM_EQUAL:
- invertToken = T_EQUAL_EQUAL;
- break;
- default:
- return;
- }
-
- result << new InverseLogicalComparisonOp(interface, index, binary, invertToken);
-}
-
-namespace {
-
-class FlipLogicalOperandsOp: public CppQuickFixOperation
-{
-public:
- FlipLogicalOperandsOp(const CppQuickFixInterface &interface, int priority,
- BinaryExpressionAST *binary, QString replacement)
- : CppQuickFixOperation(interface)
- , binary(binary)
- , replacement(replacement)
- {
- setPriority(priority);
- }
-
- QString description() const override
- {
- if (replacement.isEmpty())
- return Tr::tr("Swap Operands");
- else
- return Tr::tr("Rewrite Using %1").arg(replacement);
- }
-
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
-
- ChangeSet changes;
- changes.flip(currentFile->range(binary->left_expression),
- currentFile->range(binary->right_expression));
- if (!replacement.isEmpty())
- changes.replace(currentFile->range(binary->binary_op_token), replacement);
-
- currentFile->setChangeSet(changes);
- currentFile->apply();
- }
-
-private:
- BinaryExpressionAST *binary;
- QString replacement;
-};
-
-} // anonymous namespace
-
-void FlipLogicalOperands::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- const QList<AST *> &path = interface.path();
- if (path.isEmpty())
- return;
- CppRefactoringFilePtr file = interface.currentFile();
-
- int index = path.size() - 1;
- BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
- if (!binary)
- return;
- if (!interface.isCursorOn(binary->binary_op_token))
- return;
-
- Kind flipToken;
- switch (file->tokenAt(binary->binary_op_token).kind()) {
- case T_LESS_EQUAL:
- flipToken = T_GREATER_EQUAL;
- break;
- case T_LESS:
- flipToken = T_GREATER;
- break;
- case T_GREATER:
- flipToken = T_LESS;
- break;
- case T_GREATER_EQUAL:
- flipToken = T_LESS_EQUAL;
- break;
- case T_EQUAL_EQUAL:
- case T_EXCLAIM_EQUAL:
- case T_AMPER_AMPER:
- case T_PIPE_PIPE:
- flipToken = T_EOF_SYMBOL;
- break;
- default:
- return;
- }
-
- QString replacement;
- if (flipToken != T_EOF_SYMBOL) {
- Token tok;
- tok.f.kind = flipToken;
- replacement = QLatin1String(tok.spell());
- }
-
- result << new FlipLogicalOperandsOp(interface, index, binary, replacement);
-}
-
-namespace {
-
-class RewriteLogicalAndOp: public CppQuickFixOperation
-{
-public:
- std::shared_ptr<ASTPatternBuilder> mk;
- UnaryExpressionAST *left;
- UnaryExpressionAST *right;
- BinaryExpressionAST *pattern;
-
- RewriteLogicalAndOp(const CppQuickFixInterface &interface)
- : CppQuickFixOperation(interface)
- , mk(new ASTPatternBuilder)
- {
- left = mk->UnaryExpression();
- right = mk->UnaryExpression();
- pattern = mk->BinaryExpression(left, right);
- }
-
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
-
- ChangeSet changes;
- changes.replace(currentFile->range(pattern->binary_op_token), QLatin1String("||"));
- changes.remove(currentFile->range(left->unary_op_token));
- changes.remove(currentFile->range(right->unary_op_token));
- const int start = currentFile->startOf(pattern);
- const int end = currentFile->endOf(pattern);
- changes.insert(start, QLatin1String("!("));
- changes.insert(end, QLatin1String(")"));
-
- currentFile->setChangeSet(changes);
- currentFile->apply();
- }
-};
-
-} // anonymous namespace
-
-void RewriteLogicalAnd::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- BinaryExpressionAST *expression = nullptr;
- const QList<AST *> &path = interface.path();
- CppRefactoringFilePtr file = interface.currentFile();
-
- int index = path.size() - 1;
- for (; index != -1; --index) {
- expression = path.at(index)->asBinaryExpression();
- if (expression)
- break;
- }
-
- if (!expression)
- return;
-
- if (!interface.isCursorOn(expression->binary_op_token))
- return;
-
- QSharedPointer<RewriteLogicalAndOp> op(new RewriteLogicalAndOp(interface));
-
- ASTMatcher matcher;
-
- if (expression->match(op->pattern, &matcher) &&
- file->tokenAt(op->pattern->binary_op_token).is(T_AMPER_AMPER) &&
- file->tokenAt(op->left->unary_op_token).is(T_EXCLAIM) &&
- file->tokenAt(op->right->unary_op_token).is(T_EXCLAIM)) {
- op->setDescription(Tr::tr("Rewrite Condition Using ||"));
- op->setPriority(index);
- result.append(op);
- }
-}
-
-static bool checkDeclarationForSplit(SimpleDeclarationAST *declaration)
-{
- if (!declaration->semicolon_token)
- return false;
-
- if (!declaration->decl_specifier_list)
- return false;
-
- for (SpecifierListAST *it = declaration->decl_specifier_list; it; it = it->next) {
- SpecifierAST *specifier = it->value;
- if (specifier->asEnumSpecifier() || specifier->asClassSpecifier())
- return false;
- }
-
- return declaration->declarator_list && declaration->declarator_list->next;
-}
-
-namespace {
-
-class SplitSimpleDeclarationOp: public CppQuickFixOperation
-{
-public:
- SplitSimpleDeclarationOp(const CppQuickFixInterface &interface, int priority,
- SimpleDeclarationAST *decl)
- : CppQuickFixOperation(interface, priority)
- , declaration(decl)
- {
- setDescription(Tr::tr("Split Declaration"));
- }
-
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
-
- ChangeSet changes;
-
- SpecifierListAST *specifiers = declaration->decl_specifier_list;
- int declSpecifiersStart = currentFile->startOf(specifiers->firstToken());
- int declSpecifiersEnd = currentFile->endOf(specifiers->lastToken() - 1);
- int insertPos = currentFile->endOf(declaration->semicolon_token);
-
- DeclaratorAST *prevDeclarator = declaration->declarator_list->value;
-
- for (DeclaratorListAST *it = declaration->declarator_list->next; it; it = it->next) {
- DeclaratorAST *declarator = it->value;
-
- changes.insert(insertPos, QLatin1String("\n"));
- changes.copy(declSpecifiersStart, declSpecifiersEnd, insertPos);
- changes.insert(insertPos, QLatin1String(" "));
- changes.move(currentFile->range(declarator), insertPos);
- changes.insert(insertPos, QLatin1String(";"));
-
- const int prevDeclEnd = currentFile->endOf(prevDeclarator);
- changes.remove(prevDeclEnd, currentFile->startOf(declarator));
-
- prevDeclarator = declarator;
- }
-
- currentFile->setChangeSet(changes);
- currentFile->apply();
- }
-
-private:
- SimpleDeclarationAST *declaration;
-};
-
-} // anonymous namespace
-
-void SplitSimpleDeclaration::doMatch(const CppQuickFixInterface &interface,
- QuickFixOperations &result)
-{
- CoreDeclaratorAST *core_declarator = nullptr;
- const QList<AST *> &path = interface.path();
- CppRefactoringFilePtr file = interface.currentFile();
- const int cursorPosition = file->cursor().selectionStart();
-
- for (int index = path.size() - 1; index != -1; --index) {
- AST *node = path.at(index);
-
- if (CoreDeclaratorAST *coreDecl = node->asCoreDeclarator()) {
- core_declarator = coreDecl;
- } else if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
- if (checkDeclarationForSplit(simpleDecl)) {
- SimpleDeclarationAST *declaration = simpleDecl;
-
- const int startOfDeclSpecifier = file->startOf(declaration->decl_specifier_list->firstToken());
- const int endOfDeclSpecifier = file->endOf(declaration->decl_specifier_list->lastToken() - 1);
-
- if (cursorPosition >= startOfDeclSpecifier && cursorPosition <= endOfDeclSpecifier) {
- // the AST node under cursor is a specifier.
- result << new SplitSimpleDeclarationOp(interface, index, declaration);
- return;
- }
-
- if (core_declarator && interface.isCursorOn(core_declarator)) {
- // got a core-declarator under the text cursor.
- result << new SplitSimpleDeclarationOp(interface, index, declaration);
- return;
- }
- }
-
- return;
- }
- }
-}
-
-namespace {
-template<typename Statement> Statement *asControlStatement(AST *node)
-{
- if constexpr (std::is_same_v<Statement, IfStatementAST>)
- return node->asIfStatement();
- if constexpr (std::is_same_v<Statement, WhileStatementAST>)
- return node->asWhileStatement();
- if constexpr (std::is_same_v<Statement, ForStatementAST>)
- return node->asForStatement();
- if constexpr (std::is_same_v<Statement, RangeBasedForStatementAST>)
- return node->asRangeBasedForStatement();
- if constexpr (std::is_same_v<Statement, DoStatementAST>)
- return node->asDoStatement();
- return nullptr;
-}
-
-template<typename Statement>
-int triggerToken(const Statement *statement)
-{
- if constexpr (std::is_same_v<Statement, IfStatementAST>)
- return statement->if_token;
- if constexpr (std::is_same_v<Statement, WhileStatementAST>)
- return statement->while_token;
- if constexpr (std::is_same_v<Statement, DoStatementAST>)
- return statement->do_token;
- if constexpr (std::is_same_v<Statement, ForStatementAST>
- || std::is_same_v<Statement, RangeBasedForStatementAST>) {
- return statement->for_token;
- }
-}
-
-template<typename Statement>
-int tokenToInsertOpeningBraceAfter(const Statement *statement)
-{
- if constexpr (std::is_same_v<Statement, DoStatementAST>)
- return statement->do_token;
- return statement->rparen_token;
-}
-
-template<typename Statement> class AddBracesToControlStatementOp : public CppQuickFixOperation
-{
-public:
- AddBracesToControlStatementOp(const CppQuickFixInterface &interface,
- const QList<Statement *> &statements,
- StatementAST *elseStatement,
- int elseToken)
- : CppQuickFixOperation(interface, 0)
- , m_statements(statements), m_elseStatement(elseStatement), m_elseToken(elseToken)
- {
- setDescription(Tr::tr("Add Curly Braces"));
- }
-
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
-
- ChangeSet changes;
- for (Statement * const statement : m_statements) {
- const int start = currentFile->endOf(tokenToInsertOpeningBraceAfter(statement));
- changes.insert(start, QLatin1String(" {"));
- if constexpr (std::is_same_v<Statement, DoStatementAST>) {
- const int end = currentFile->startOf(statement->while_token);
- changes.insert(end, QLatin1String("} "));
- } else if constexpr (std::is_same_v<Statement, IfStatementAST>) {
- if (statement->else_statement) {
- changes.insert(currentFile->startOf(statement->else_token), "} ");
- } else {
- changes.insert(currentFile->endOf(statement->statement->lastToken() - 1),
- "\n}");
- }
-
- } else {
- const int end = currentFile->endOf(statement->statement->lastToken() - 1);
- changes.insert(end, QLatin1String("\n}"));
- }
- }
- if (m_elseStatement) {
- changes.insert(currentFile->endOf(m_elseToken), " {");
- changes.insert(currentFile->endOf(m_elseStatement->lastToken() - 1), "\n}");
- }
-
- currentFile->setChangeSet(changes);
- currentFile->apply();
- }
-
-private:
- const QList<Statement *> m_statements;
- StatementAST * const m_elseStatement;
- const int m_elseToken;
-};
-
-} // anonymous namespace
-
-template<typename Statement>
-bool checkControlStatementsHelper(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- Statement * const statement = asControlStatement<Statement>(interface.path().last());
- if (!statement)
- return false;
-
- QList<Statement *> statements;
- if (interface.isCursorOn(triggerToken(statement)) && statement->statement
- && !statement->statement->asCompoundStatement()) {
- statements << statement;
- }
-
- StatementAST *elseStmt = nullptr;
- int elseToken = 0;
- if constexpr (std::is_same_v<Statement, IfStatementAST>) {
- IfStatementAST *currentIfStmt = statement;
- for (elseStmt = currentIfStmt->else_statement, elseToken = currentIfStmt->else_token;
- elseStmt && (currentIfStmt = elseStmt->asIfStatement());
- elseStmt = currentIfStmt->else_statement, elseToken = currentIfStmt->else_token) {
- if (currentIfStmt->statement && !currentIfStmt->statement->asCompoundStatement())
- statements << currentIfStmt;
- }
- if (elseStmt && (elseStmt->asIfStatement() || elseStmt->asCompoundStatement())) {
- elseStmt = nullptr;
- elseToken = 0;
- }
- }
-
- if (!statements.isEmpty() || elseStmt)
- result << new AddBracesToControlStatementOp(interface, statements, elseStmt, elseToken);
- return true;
-}
-
-template<typename ...Statements>
-void checkControlStatements(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- (... || checkControlStatementsHelper<Statements>(interface, result));
-}
-
-void AddBracesToControlStatement::doMatch(const CppQuickFixInterface &interface,
- QuickFixOperations &result)
-{
- if (interface.path().isEmpty())
- return;
- checkControlStatements<IfStatementAST,
- WhileStatementAST,
- ForStatementAST,
- RangeBasedForStatementAST,
- DoStatementAST>(interface, result);
-}
-
-namespace {
-
-class MoveDeclarationOutOfIfOp: public CppQuickFixOperation
-{
-public:
- MoveDeclarationOutOfIfOp(const CppQuickFixInterface &interface)
- : CppQuickFixOperation(interface)
- {
- setDescription(Tr::tr("Move Declaration out of Condition"));
-
- reset();
- }
-
- void reset()
- {
- condition = mk.Condition();
- pattern = mk.IfStatement(condition);
- }
-
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
-
- ChangeSet changes;
-
- changes.copy(currentFile->range(core), currentFile->startOf(condition));
-
- int insertPos = currentFile->startOf(pattern);
- changes.move(currentFile->range(condition), insertPos);
- changes.insert(insertPos, QLatin1String(";\n"));
-
- currentFile->setChangeSet(changes);
- currentFile->apply();
- }
-
- ASTMatcher matcher;
- ASTPatternBuilder mk;
- ConditionAST *condition = nullptr;
- IfStatementAST *pattern = nullptr;
- CoreDeclaratorAST *core = nullptr;
-};
-
-} // anonymous namespace
-
-void MoveDeclarationOutOfIf::doMatch(const CppQuickFixInterface &interface,
- QuickFixOperations &result)
-{
- const QList<AST *> &path = interface.path();
- using Ptr = QSharedPointer<MoveDeclarationOutOfIfOp>;
- Ptr op(new MoveDeclarationOutOfIfOp(interface));
-
- int index = path.size() - 1;
- for (; index != -1; --index) {
- if (IfStatementAST *statement = path.at(index)->asIfStatement()) {
- if (statement->match(op->pattern, &op->matcher) && op->condition->declarator) {
- DeclaratorAST *declarator = op->condition->declarator;
- op->core = declarator->core_declarator;
- if (!op->core)
- return;
-
- if (interface.isCursorOn(op->core)) {
- op->setPriority(index);
- result.append(op);
- return;
- }
-
- op->reset();
- }
- }
- }
-}
-
-namespace {
-
-class MoveDeclarationOutOfWhileOp: public CppQuickFixOperation
-{
-public:
- MoveDeclarationOutOfWhileOp(const CppQuickFixInterface &interface)
- : CppQuickFixOperation(interface)
- {
- setDescription(Tr::tr("Move Declaration out of Condition"));
- reset();
- }
-
- void reset()
- {
- condition = mk.Condition();
- pattern = mk.WhileStatement(condition);
- }
-
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
-
- ChangeSet changes;
-
- changes.insert(currentFile->startOf(condition), QLatin1String("("));
- changes.insert(currentFile->endOf(condition), QLatin1String(") != 0"));
-
- int insertPos = currentFile->startOf(pattern);
- const int conditionStart = currentFile->startOf(condition);
- changes.move(conditionStart, currentFile->startOf(core), insertPos);
- changes.copy(currentFile->range(core), insertPos);
- changes.insert(insertPos, QLatin1String(";\n"));
-
- currentFile->setChangeSet(changes);
- currentFile->apply();
- }
-
- ASTMatcher matcher;
- ASTPatternBuilder mk;
- ConditionAST *condition = nullptr;
- WhileStatementAST *pattern = nullptr;
- CoreDeclaratorAST *core = nullptr;
-};
-
-} // anonymous namespace
-
-void MoveDeclarationOutOfWhile::doMatch(const CppQuickFixInterface &interface,
- QuickFixOperations &result)
-{
- const QList<AST *> &path = interface.path();
- QSharedPointer<MoveDeclarationOutOfWhileOp> op(new MoveDeclarationOutOfWhileOp(interface));
-
- int index = path.size() - 1;
- for (; index != -1; --index) {
- if (WhileStatementAST *statement = path.at(index)->asWhileStatement()) {
- if (statement->match(op->pattern, &op->matcher) && op->condition->declarator) {
- DeclaratorAST *declarator = op->condition->declarator;
- op->core = declarator->core_declarator;
-
- if (!op->core)
- return;
-
- if (!declarator->equal_token)
- return;
-
- if (!declarator->initializer)
- return;
-
- if (interface.isCursorOn(op->core)) {
- op->setPriority(index);
- result.append(op);
- return;
- }
-
- op->reset();
- }
- }
- }
-}
-
-namespace {
-
-class SplitIfStatementOp: public CppQuickFixOperation
-{
-public:
- SplitIfStatementOp(const CppQuickFixInterface &interface, int priority,
- IfStatementAST *pattern, BinaryExpressionAST *condition)
- : CppQuickFixOperation(interface, priority)
- , pattern(pattern)
- , condition(condition)
- {
- setDescription(Tr::tr("Split if Statement"));
- }
-
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
-
- const Token binaryToken = currentFile->tokenAt(condition->binary_op_token);
-
- if (binaryToken.is(T_AMPER_AMPER))
- splitAndCondition(currentFile);
- else
- splitOrCondition(currentFile);
- }
-
- void splitAndCondition(CppRefactoringFilePtr currentFile) const
- {
- ChangeSet changes;
-
- int startPos = currentFile->startOf(pattern);
- changes.insert(startPos, QLatin1String("if ("));
- changes.move(currentFile->range(condition->left_expression), startPos);
- changes.insert(startPos, QLatin1String(") {\n"));
-
- const int lExprEnd = currentFile->endOf(condition->left_expression);
- changes.remove(lExprEnd, currentFile->startOf(condition->right_expression));
- changes.insert(currentFile->endOf(pattern), QLatin1String("\n}"));
-
- currentFile->setChangeSet(changes);
- currentFile->apply();
- }
-
- void splitOrCondition(CppRefactoringFilePtr currentFile) const
- {
- ChangeSet changes;
-
- StatementAST *ifTrueStatement = pattern->statement;
- CompoundStatementAST *compoundStatement = ifTrueStatement->asCompoundStatement();
-
- int insertPos = currentFile->endOf(ifTrueStatement);
- if (compoundStatement)
- changes.insert(insertPos, QLatin1String(" "));
- else
- changes.insert(insertPos, QLatin1String("\n"));
- changes.insert(insertPos, QLatin1String("else if ("));
-
- const int rExprStart = currentFile->startOf(condition->right_expression);
- changes.move(rExprStart, currentFile->startOf(pattern->rparen_token), insertPos);
- changes.insert(insertPos, QLatin1String(")"));
-
- const int rParenEnd = currentFile->endOf(pattern->rparen_token);
- changes.copy(rParenEnd, currentFile->endOf(pattern->statement), insertPos);
-
- const int lExprEnd = currentFile->endOf(condition->left_expression);
- changes.remove(lExprEnd, currentFile->startOf(condition->right_expression));
-
- currentFile->setChangeSet(changes);
- currentFile->apply();
- }
-
-private:
- IfStatementAST *pattern;
- BinaryExpressionAST *condition;
-};
-
-} // anonymous namespace
-
-void SplitIfStatement::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- IfStatementAST *pattern = nullptr;
- const QList<AST *> &path = interface.path();
-
- int index = path.size() - 1;
- for (; index != -1; --index) {
- AST *node = path.at(index);
- if (IfStatementAST *stmt = node->asIfStatement()) {
- pattern = stmt;
- break;
- }
- }
-
- if (!pattern || !pattern->statement)
- return;
-
- unsigned splitKind = 0;
- for (++index; index < path.size(); ++index) {
- AST *node = path.at(index);
- BinaryExpressionAST *condition = node->asBinaryExpression();
- if (!condition)
- return;
-
- Token binaryToken = interface.currentFile()->tokenAt(condition->binary_op_token);
-
- // only accept a chain of ||s or &&s - no mixing
- if (!splitKind) {
- splitKind = binaryToken.kind();
- if (splitKind != T_AMPER_AMPER && splitKind != T_PIPE_PIPE)
- return;
- // we can't reliably split &&s in ifs with an else branch
- if (splitKind == T_AMPER_AMPER && pattern->else_statement)
- return;
- } else if (splitKind != binaryToken.kind()) {
- return;
- }
-
- if (interface.isCursorOn(condition->binary_op_token)) {
- result << new SplitIfStatementOp(interface, index, pattern, condition);
- return;
- }
- }
-}
-
-/* Analze a string/character literal like "x", QLatin1String("x") and return the literal
- * (StringLiteral or NumericLiteral for characters) and its type
- * and the enclosing function (QLatin1String, tr...) */
-
-enum StringLiteralType { TypeString, TypeObjCString, TypeChar, TypeNone };
-
-enum ActionFlags {
- EncloseInQLatin1CharAction = 0x1,
- EncloseInQLatin1StringAction = 0x2,
- EncloseInQStringLiteralAction = 0x4,
- EncloseInQByteArrayLiteralAction = 0x8,
- EncloseActionMask = EncloseInQLatin1CharAction | EncloseInQLatin1StringAction
- | EncloseInQStringLiteralAction | EncloseInQByteArrayLiteralAction,
- TranslateTrAction = 0x10,
- TranslateQCoreApplicationAction = 0x20,
- TranslateNoopAction = 0x40,
- TranslationMask = TranslateTrAction | TranslateQCoreApplicationAction | TranslateNoopAction,
- RemoveObjectiveCAction = 0x100,
- ConvertEscapeSequencesToCharAction = 0x200,
- ConvertEscapeSequencesToStringAction = 0x400,
- SingleQuoteAction = 0x800,
- DoubleQuoteAction = 0x1000
-};
-
-/* Convert single-character string literals into character literals with some
- * special cases "a" --> 'a', "'" --> '\'', "\n" --> '\n', "\"" --> '"'. */
-static QByteArray stringToCharEscapeSequences(const QByteArray &content)
-{
- if (content.size() == 1)
- return content.at(0) == '\'' ? QByteArray("\\'") : content;
- if (content.size() == 2 && content.at(0) == '\\')
- return content == "\\\"" ? QByteArray(1, '"') : content;
- return QByteArray();
-}
-
-/* Convert character literal into a string literal with some special cases
- * 'a' -> "a", '\n' -> "\n", '\'' --> "'", '"' --> "\"". */
-static QByteArray charToStringEscapeSequences(const QByteArray &content)
-{
- if (content.size() == 1)
- return content.at(0) == '"' ? QByteArray("\\\"") : content;
- if (content.size() == 2)
- return content == "\\'" ? QByteArray("'") : content;
- return QByteArray();
-}
-
-static QString msgQtStringLiteralDescription(const QString &replacement)
-{
- return Tr::tr("Enclose in %1(...)").arg(replacement);
-}
-
-static QString stringLiteralReplacement(unsigned actions)
-{
- if (actions & EncloseInQLatin1CharAction)
- return QLatin1String("QLatin1Char");
- if (actions & EncloseInQLatin1StringAction)
- return QLatin1String("QLatin1String");
- if (actions & EncloseInQStringLiteralAction)
- return QLatin1String("QStringLiteral");
- if (actions & EncloseInQByteArrayLiteralAction)
- return QLatin1String("QByteArrayLiteral");
- if (actions & TranslateTrAction)
- return QLatin1String("tr");
- if (actions & TranslateQCoreApplicationAction)
- return QLatin1String("QCoreApplication::translate");
- if (actions & TranslateNoopAction)
- return QLatin1String("QT_TRANSLATE_NOOP");
- return QString();
-}
-
-static ExpressionAST *analyzeStringLiteral(const QList<AST *> &path,
- const CppRefactoringFilePtr &file, StringLiteralType *type,
- QByteArray *enclosingFunction = nullptr,
- CallAST **enclosingFunctionCall = nullptr)
-{
- *type = TypeNone;
- if (enclosingFunction)
- enclosingFunction->clear();
- if (enclosingFunctionCall)
- *enclosingFunctionCall = nullptr;
-
- if (path.isEmpty())
- return nullptr;
-
- ExpressionAST *literal = path.last()->asExpression();
- if (literal) {
- if (literal->asStringLiteral()) {
- // Check for Objective C string (@"bla")
- const QChar firstChar = file->charAt(file->startOf(literal));
- *type = firstChar == QLatin1Char('@') ? TypeObjCString : TypeString;
- } else if (NumericLiteralAST *numericLiteral = literal->asNumericLiteral()) {
- // character ('c') constants are numeric.
- if (file->tokenAt(numericLiteral->literal_token).is(T_CHAR_LITERAL))
- *type = TypeChar;
- }
- }
-
- if (*type != TypeNone && enclosingFunction && path.size() > 1) {
- if (CallAST *call = path.at(path.size() - 2)->asCall()) {
- if (call->base_expression) {
- if (IdExpressionAST *idExpr = call->base_expression->asIdExpression()) {
- if (SimpleNameAST *functionName = idExpr->name->asSimpleName()) {
- *enclosingFunction = file->tokenAt(functionName->identifier_token).identifier->chars();
- if (enclosingFunctionCall)
- *enclosingFunctionCall = call;
- }
- }
- }
- }
- }
- return literal;
-}
-
-namespace {
-
-/// Operation performs the operations of type ActionFlags passed in as actions.
-class WrapStringLiteralOp : public CppQuickFixOperation
-{
-public:
- WrapStringLiteralOp(const CppQuickFixInterface &interface, int priority,
- unsigned actions, const QString &description, ExpressionAST *literal,
- const QString &translationContext = QString())
- : CppQuickFixOperation(interface, priority), m_actions(actions), m_literal(literal),
- m_translationContext(translationContext)
- {
- setDescription(description);
- }
-
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
-
- ChangeSet changes;
-
- const int startPos = currentFile->startOf(m_literal);
- const int endPos = currentFile->endOf(m_literal);
-
- // kill leading '@'. No need to adapt endPos, that is done by ChangeSet
- if (m_actions & RemoveObjectiveCAction)
- changes.remove(startPos, startPos + 1);
-
- // Fix quotes
- if (m_actions & (SingleQuoteAction | DoubleQuoteAction)) {
- const QString newQuote((m_actions & SingleQuoteAction)
- ? QLatin1Char('\'') : QLatin1Char('"'));
- changes.replace(startPos, startPos + 1, newQuote);
- changes.replace(endPos - 1, endPos, newQuote);
- }
-
- // Convert single character strings into character constants
- if (m_actions & ConvertEscapeSequencesToCharAction) {
- StringLiteralAST *stringLiteral = m_literal->asStringLiteral();
- QTC_ASSERT(stringLiteral, return ;);
- const QByteArray oldContents(currentFile->tokenAt(stringLiteral->literal_token).identifier->chars());
- const QByteArray newContents = stringToCharEscapeSequences(oldContents);
- QTC_ASSERT(!newContents.isEmpty(), return ;);
- if (oldContents != newContents)
- changes.replace(startPos + 1, endPos -1, QString::fromLatin1(newContents));
- }
-
- // Convert character constants into strings constants
- if (m_actions & ConvertEscapeSequencesToStringAction) {
- NumericLiteralAST *charLiteral = m_literal->asNumericLiteral(); // char 'c' constants are numerical.
- QTC_ASSERT(charLiteral, return ;);
- const QByteArray oldContents(currentFile->tokenAt(charLiteral->literal_token).identifier->chars());
- const QByteArray newContents = charToStringEscapeSequences(oldContents);
- QTC_ASSERT(!newContents.isEmpty(), return ;);
- if (oldContents != newContents)
- changes.replace(startPos + 1, endPos -1, QString::fromLatin1(newContents));
- }
-
- // Enclose in literal or translation function, macro.
- if (m_actions & (EncloseActionMask | TranslationMask)) {
- changes.insert(endPos, QString(QLatin1Char(')')));
- QString leading = stringLiteralReplacement(m_actions);
- leading += QLatin1Char('(');
- if (m_actions
- & (TranslateQCoreApplicationAction | TranslateNoopAction)) {
- leading += QLatin1Char('"');
- leading += m_translationContext;
- leading += QLatin1String("\", ");
- }
- changes.insert(startPos, leading);
- }
-
- currentFile->setChangeSet(changes);
- currentFile->apply();
- }
-
-private:
- const unsigned m_actions;
- ExpressionAST *m_literal;
- const QString m_translationContext;
-};
-
-} // anonymous namespace
-
-void WrapStringLiteral::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- StringLiteralType type = TypeNone;
- QByteArray enclosingFunction;
- const QList<AST *> &path = interface.path();
- CppRefactoringFilePtr file = interface.currentFile();
- ExpressionAST *literal = analyzeStringLiteral(path, file, &type, &enclosingFunction);
- if (!literal || type == TypeNone)
- return;
- if ((type == TypeChar && enclosingFunction == "QLatin1Char")
- || isQtStringLiteral(enclosingFunction)
- || isQtStringTranslation(enclosingFunction))
- return;
-
- const int priority = path.size() - 1; // very high priority
- if (type == TypeChar) {
- unsigned actions = EncloseInQLatin1CharAction;
- QString description = msgQtStringLiteralDescription(stringLiteralReplacement(actions));
- result << new WrapStringLiteralOp(interface, priority, actions, description, literal);
- if (NumericLiteralAST *charLiteral = literal->asNumericLiteral()) {
- const QByteArray contents(file->tokenAt(charLiteral->literal_token).identifier->chars());
- if (!charToStringEscapeSequences(contents).isEmpty()) {
- actions = DoubleQuoteAction | ConvertEscapeSequencesToStringAction;
- description = Tr::tr("Convert to String Literal");
- result << new WrapStringLiteralOp(interface, priority, actions,
- description, literal);
- }
- }
- } else {
- const unsigned objectiveCActions = type == TypeObjCString ?
- unsigned(RemoveObjectiveCAction) : 0u;
- unsigned actions = 0;
- if (StringLiteralAST *stringLiteral = literal->asStringLiteral()) {
- const QByteArray contents(file->tokenAt(stringLiteral->literal_token).identifier->chars());
- if (!stringToCharEscapeSequences(contents).isEmpty()) {
- actions = EncloseInQLatin1CharAction | SingleQuoteAction
- | ConvertEscapeSequencesToCharAction | objectiveCActions;
- QString description =
- Tr::tr("Convert to Character Literal and Enclose in QLatin1Char(...)");
- result << new WrapStringLiteralOp(interface, priority, actions,
- description, literal);
- actions &= ~EncloseInQLatin1CharAction;
- description = Tr::tr("Convert to Character Literal");
- result << new WrapStringLiteralOp(interface, priority, actions,
- description, literal);
- }
- }
- actions = EncloseInQLatin1StringAction | objectiveCActions;
- result << new WrapStringLiteralOp(interface, priority, actions,
- msgQtStringLiteralDescription(stringLiteralReplacement(actions)), literal);
- actions = EncloseInQStringLiteralAction | objectiveCActions;
- result << new WrapStringLiteralOp(interface, priority, actions,
- msgQtStringLiteralDescription(stringLiteralReplacement(actions)), literal);
- actions = EncloseInQByteArrayLiteralAction | objectiveCActions;
- result << new WrapStringLiteralOp(interface, priority, actions,
- msgQtStringLiteralDescription(stringLiteralReplacement(actions)), literal);
- }
-}
-
-void TranslateStringLiteral::doMatch(const CppQuickFixInterface &interface,
- QuickFixOperations &result)
-{
- // Initialize
- StringLiteralType type = TypeNone;
- QByteArray enclosingFunction;
- const QList<AST *> &path = interface.path();
- CppRefactoringFilePtr file = interface.currentFile();
- ExpressionAST *literal = analyzeStringLiteral(path, file, &type, &enclosingFunction);
- if (!literal || type != TypeString
- || isQtStringLiteral(enclosingFunction) || isQtStringTranslation(enclosingFunction))
- return;
-
- QString trContext;
-
- std::shared_ptr<Control> control = interface.context().bindings()->control();
- const Name *trName = control->identifier("tr");
-
- // Check whether we are in a function:
- const QString description = Tr::tr("Mark as Translatable");
- for (int i = path.size() - 1; i >= 0; --i) {
- if (FunctionDefinitionAST *definition = path.at(i)->asFunctionDefinition()) {
- Function *function = definition->symbol;
- ClassOrNamespace *b = interface.context().lookupType(function);
- if (b) {
- // Do we have a tr function?
- const QList<LookupItem> items = b->find(trName);
- for (const LookupItem &r : items) {
- Symbol *s = r.declaration();
- if (s->type()->asFunctionType()) {
- // no context required for tr
- result << new WrapStringLiteralOp(interface, path.size() - 1,
- TranslateTrAction,
- description, literal);
- return;
- }
- }
- }
- // We need to do a QCA::translate, so we need a context.
- // Use fully qualified class name:
- Overview oo;
- const QList<const Name *> names = LookupContext::path(function);
- for (const Name *n : names) {
- if (!trContext.isEmpty())
- trContext.append(QLatin1String("::"));
- trContext.append(oo.prettyName(n));
- }
- // ... or global if none available!
- if (trContext.isEmpty())
- trContext = QLatin1String("GLOBAL");
- result << new WrapStringLiteralOp(interface, path.size() - 1,
- TranslateQCoreApplicationAction,
- description, literal, trContext);
- return;
- }
- }
-
- // We need to use Q_TRANSLATE_NOOP
- result << new WrapStringLiteralOp(interface, path.size() - 1,
- TranslateNoopAction,
- description, literal, trContext);
-}
-
-namespace {
-
-class ConvertCStringToNSStringOp: public CppQuickFixOperation
-{
-public:
- ConvertCStringToNSStringOp(const CppQuickFixInterface &interface, int priority,
- StringLiteralAST *stringLiteral, CallAST *qlatin1Call)
- : CppQuickFixOperation(interface, priority)
- , stringLiteral(stringLiteral)
- , qlatin1Call(qlatin1Call)
- {
- setDescription(Tr::tr("Convert to Objective-C String Literal"));
- }
-
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
-
- ChangeSet changes;
-
- if (qlatin1Call) {
- changes.replace(currentFile->startOf(qlatin1Call), currentFile->startOf(stringLiteral),
- QLatin1String("@"));
- changes.remove(currentFile->endOf(stringLiteral), currentFile->endOf(qlatin1Call));
- } else {
- changes.insert(currentFile->startOf(stringLiteral), QLatin1String("@"));
- }
-
- currentFile->setChangeSet(changes);
- currentFile->apply();
- }
-
-private:
- StringLiteralAST *stringLiteral;
- CallAST *qlatin1Call;
-};
-
-} // anonymous namespace
-
-void ConvertCStringToNSString::doMatch(const CppQuickFixInterface &interface,
- QuickFixOperations &result)
-{
- CppRefactoringFilePtr file = interface.currentFile();
-
- if (!interface.editor()->cppEditorDocument()->isObjCEnabled())
- return;
-
- StringLiteralType type = TypeNone;
- QByteArray enclosingFunction;
- CallAST *qlatin1Call;
- const QList<AST *> &path = interface.path();
- ExpressionAST *literal = analyzeStringLiteral(path, file, &type, &enclosingFunction,
- &qlatin1Call);
- if (!literal || type != TypeString)
- return;
- if (!isQtStringLiteral(enclosingFunction))
- qlatin1Call = nullptr;
-
- result << new ConvertCStringToNSStringOp(interface, path.size() - 1, literal->asStringLiteral(),
- qlatin1Call);
-}
-
-namespace {
-
-class ConvertNumericLiteralOp: public CppQuickFixOperation
-{
-public:
- ConvertNumericLiteralOp(const CppQuickFixInterface &interface, int start, int end,
- const QString &replacement)
- : CppQuickFixOperation(interface)
- , start(start)
- , end(end)
- , replacement(replacement)
- {}
-
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
-
- ChangeSet changes;
- changes.replace(start, end, replacement);
- currentFile->setChangeSet(changes);
- currentFile->apply();
- }
-
-private:
- int start, end;
- QString replacement;
-};
-
-} // anonymous namespace
-
-void ConvertNumericLiteral::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- const QList<AST *> &path = interface.path();
- CppRefactoringFilePtr file = interface.currentFile();
-
- if (path.isEmpty())
- return;
-
- NumericLiteralAST *literal = path.last()->asNumericLiteral();
-
- if (!literal)
- return;
-
- Token token = file->tokenAt(literal->asNumericLiteral()->literal_token);
- if (!token.is(T_NUMERIC_LITERAL))
- return;
- const NumericLiteral *numeric = token.number;
- if (numeric->isDouble() || numeric->isFloat())
- return;
-
- // remove trailing L or U and stuff
- const char * const spell = numeric->chars();
- int numberLength = numeric->size();
- while (numberLength > 0 && !std::isxdigit(spell[numberLength - 1]))
- --numberLength;
- if (numberLength < 1)
- return;
-
- // convert to number
- bool valid;
- ulong value = 0;
- const QString x = QString::fromUtf8(spell).left(numberLength);
- if (x.startsWith("0b", Qt::CaseInsensitive))
- value = x.mid(2).toULong(&valid, 2);
- else
- value = x.toULong(&valid, 0);
-
- if (!valid)
- return;
-
- const int priority = path.size() - 1; // very high priority
- const int start = file->startOf(literal);
- const char * const str = numeric->chars();
-
- const bool isBinary = numberLength > 2 && str[0] == '0' && (str[1] == 'b' || str[1] == 'B');
- const bool isOctal = numberLength >= 2 && str[0] == '0' && str[1] >= '0' && str[1] <= '7';
- const bool isDecimal = !(isBinary || isOctal || numeric->isHex());
-
- if (!numeric->isHex()) {
- /*
- Convert integer literal to hex representation.
- Replace
- 0b100000
- 32
- 040
- With
- 0x20
-
- */
- const QString replacement = QString::asprintf("0x%lX", value);
- auto op = new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement);
- op->setDescription(Tr::tr("Convert to Hexadecimal"));
- op->setPriority(priority);
- result << op;
- }
-
- if (!isOctal) {
- /*
- Convert integer literal to octal representation.
- Replace
- 0b100000
- 32
- 0x20
- With
- 040
- */
- const QString replacement = QString::asprintf("0%lo", value);
- auto op = new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement);
- op->setDescription(Tr::tr("Convert to Octal"));
- op->setPriority(priority);
- result << op;
- }
-
- if (!isDecimal) {
- /*
- Convert integer literal to decimal representation.
- Replace
- 0b100000
- 0x20
- 040
- With
- 32
- */
- const QString replacement = QString::asprintf("%lu", value);
- auto op = new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement);
- op->setDescription(Tr::tr("Convert to Decimal"));
- op->setPriority(priority);
- result << op;
- }
-
- if (!isBinary) {
- /*
- Convert integer literal to binary representation.
- Replace
- 32
- 0x20
- 040
- With
- 0b100000
- */
- QString replacement = "0b";
- if (value == 0) {
- replacement.append('0');
- } else {
- std::bitset<std::numeric_limits<decltype (value)>::digits> b(value);
- QRegularExpression re("^[0]*");
- replacement.append(QString::fromStdString(b.to_string()).remove(re));
- }
- auto op = new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement);
- op->setDescription(Tr::tr("Convert to Binary"));
- op->setPriority(priority);
- result << op;
- }
-}
-
-namespace {
-
-class AddLocalDeclarationOp: public CppQuickFixOperation
-{
-public:
- AddLocalDeclarationOp(const CppQuickFixInterface &interface,
- int priority,
- const BinaryExpressionAST *binaryAST,
- const SimpleNameAST *simpleNameAST)
- : CppQuickFixOperation(interface, priority)
- , binaryAST(binaryAST)
- , simpleNameAST(simpleNameAST)
- {
- setDescription(Tr::tr("Add Local Declaration"));
- }
-
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
- QString declaration = getDeclaration();
-
- if (!declaration.isEmpty()) {
- ChangeSet changes;
- changes.replace(currentFile->startOf(binaryAST),
- currentFile->endOf(simpleNameAST),
- declaration);
- currentFile->setChangeSet(changes);
- currentFile->apply();
- }
- }
-
-private:
- QString getDeclaration()
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
- Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
- const auto settings = CppQuickFixProjectsSettings::getQuickFixSettings(
- ProjectExplorer::ProjectTree::currentProject());
-
- if (currentFile->cppDocument()->languageFeatures().cxx11Enabled && settings->useAuto)
- return "auto " + oo.prettyName(simpleNameAST->name);
- return declFromExpr(binaryAST->right_expression, nullptr, simpleNameAST, snapshot(),
- context(), currentFile, false);
- }
-
- const BinaryExpressionAST *binaryAST;
- const SimpleNameAST *simpleNameAST;
-};
-
-} // anonymous namespace
-
-namespace {
-
-class ConvertToCamelCaseOp: public CppQuickFixOperation
-{
-public:
- ConvertToCamelCaseOp(const CppQuickFixInterface &interface, const QString &name,
- const AST *nameAst, bool test)
- : CppQuickFixOperation(interface, -1)
- , m_name(name)
- , m_nameAst(nameAst)
- , m_isAllUpper(name.isUpper())
- , m_test(test)
- {
- setDescription(Tr::tr("Convert to Camel Case"));
- }
-
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
-
- QString newName = m_isAllUpper ? m_name.toLower() : m_name;
- for (int i = 1; i < newName.length(); ++i) {
- const QChar c = newName.at(i);
- if (c.isUpper() && m_isAllUpper) {
- newName[i] = c.toLower();
- } else if (i < newName.length() - 1 && isConvertibleUnderscore(newName, i)) {
- newName.remove(i, 1);
- newName[i] = newName.at(i).toUpper();
- }
- }
- if (m_test) {
- ChangeSet changeSet;
- changeSet.replace(currentFile->range(m_nameAst), newName);
- currentFile->setChangeSet(changeSet);
- currentFile->apply();
- } else {
- editor()->renameUsages(newName);
- }
- }
-
- static bool isConvertibleUnderscore(const QString &name, int pos)
- {
- return name.at(pos) == QLatin1Char('_') && name.at(pos+1).isLetter()
- && !(pos == 1 && name.at(0) == QLatin1Char('m'));
- }
-
-private:
- const QString m_name;
- const AST * const m_nameAst;
- const bool m_isAllUpper;
- const bool m_test;
-};
-
-} // anonymous namespace
-
-void ConvertToCamelCase::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- const QList<AST *> &path = interface.path();
-
- if (path.isEmpty())
- return;
-
- AST * const ast = path.last();
- const Name *name = nullptr;
- const AST *astForName = nullptr;
- if (const NameAST * const nameAst = ast->asName()) {
- if (nameAst->name && nameAst->name->asNameId()) {
- astForName = nameAst;
- name = nameAst->name;
- }
- } else if (const NamespaceAST * const namespaceAst = ast->asNamespace()) {
- astForName = namespaceAst;
- name = namespaceAst->symbol->name();
- }
-
- if (!name)
- return;
-
- QString nameString = QString::fromUtf8(name->identifier()->chars());
- if (nameString.length() < 3)
- return;
- for (int i = 1; i < nameString.length() - 1; ++i) {
- if (ConvertToCamelCaseOp::isConvertibleUnderscore(nameString, i)) {
- result << new ConvertToCamelCaseOp(interface, nameString, astForName, m_test);
- return;
- }
- }
-}
-
-AddIncludeForUndefinedIdentifierOp::AddIncludeForUndefinedIdentifierOp(
- const CppQuickFixInterface &interface, int priority, const QString &include)
- : CppQuickFixOperation(interface, priority)
- , m_include(include)
-{
- setDescription(Tr::tr("Add #include %1").arg(m_include));
-}
-
-void AddIncludeForUndefinedIdentifierOp::perform()
-{
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr file = refactoring.cppFile(filePath());
-
- ChangeSet changes;
- insertNewIncludeDirective(m_include, file, semanticInfo().doc, changes);
- file->setChangeSet(changes);
- file->apply();
-}
-
-AddForwardDeclForUndefinedIdentifierOp::AddForwardDeclForUndefinedIdentifierOp(
- const CppQuickFixInterface &interface,
- int priority,
- const QString &fqClassName,
- int symbolPos)
- : CppQuickFixOperation(interface, priority), m_className(fqClassName), m_symbolPos(symbolPos)
-{
- setDescription(Tr::tr("Add forward declaration for %1").arg(m_className));
-}
-
-void AddForwardDeclForUndefinedIdentifierOp::perform()
-{
- const QStringList parts = m_className.split("::");
- QTC_ASSERT(!parts.isEmpty(), return);
- const QStringList namespaces = parts.mid(0, parts.length() - 1);
-
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr file = refactoring.cppFile(filePath());
-
- NSVisitor visitor(file.data(), namespaces, m_symbolPos);
- visitor.accept(file->cppDocument()->translationUnit()->ast());
- const auto stringToInsert = [&visitor, symbol = parts.last()] {
- QString s = "\n";
- for (const QString &ns : visitor.remainingNamespaces())
- s += "namespace " + ns + " { ";
- s += "class " + symbol + ';';
- for (int i = 0; i < visitor.remainingNamespaces().size(); ++i)
- s += " }";
- return s;
- };
-
- int insertPos = 0;
-
- // Find the position to insert:
- // If we have a matching namespace, we do the insertion there.
- // If we don't have a matching namespace, but there is another namespace in the file,
- // we assume that to be a good position for our insertion.
- // Otherwise, do the insertion after the last include that comes before the use of the symbol.
- // If there is no such include, do the insertion before the first token.
- if (visitor.enclosingNamespace()) {
- insertPos = file->startOf(visitor.enclosingNamespace()->linkage_body) + 1;
- } else if (visitor.firstNamespace()) {
- insertPos = file->startOf(visitor.firstNamespace());
- } else {
- const QTextCursor tc = file->document()->find(
- QRegularExpression("^\\s*#include .*$"),
- m_symbolPos,
- QTextDocument::FindBackward | QTextDocument::FindCaseSensitively);
- if (!tc.isNull())
- insertPos = tc.position() + 1;
- else if (visitor.firstToken())
- insertPos = file->startOf(visitor.firstToken());
- }
-
- QString insertion = stringToInsert();
- if (file->charAt(insertPos - 1) != QChar::ParagraphSeparator)
- insertion.prepend('\n');
- if (file->charAt(insertPos) != QChar::ParagraphSeparator)
- insertion.append('\n');
- ChangeSet s;
- s.insert(insertPos, insertion);
- file->setChangeSet(s);
- file->apply();
-}
-
-namespace {
-
-QString findShortestInclude(const QString currentDocumentFilePath,
- const QString candidateFilePath,
- const ProjectExplorer::HeaderPaths &headerPaths)
-{
- QString result;
-
- const QFileInfo fileInfo(candidateFilePath);
-
- if (fileInfo.path() == QFileInfo(currentDocumentFilePath).path()) {
- result = QLatin1Char('"') + fileInfo.fileName() + QLatin1Char('"');
- } else {
- for (const ProjectExplorer::HeaderPath &headerPath : headerPaths) {
- if (!candidateFilePath.startsWith(headerPath.path))
- continue;
- QString relativePath = candidateFilePath.mid(headerPath.path.size());
- if (!relativePath.isEmpty() && relativePath.at(0) == QLatin1Char('/'))
- relativePath = relativePath.mid(1);
- if (result.isEmpty() || relativePath.size() + 2 < result.size())
- result = QLatin1Char('<') + relativePath + QLatin1Char('>');
- }
- }
-
- return result;
-}
-
-QString findMatchingInclude(const QString &className,
- const ProjectExplorer::HeaderPaths &headerPaths)
-{
- const QStringList candidateFileNames{className, className + ".h", className + ".hpp",
- className.toLower(), className.toLower() + ".h", className.toLower() + ".hpp"};
- for (const QString &fileName : candidateFileNames) {
- for (const ProjectExplorer::HeaderPath &headerPath : headerPaths) {
- const QString headerPathCandidate = headerPath.path + QLatin1Char('/') + fileName;
- const QFileInfo fileInfo(headerPathCandidate);
- if (fileInfo.exists() && fileInfo.isFile())
- return '<' + fileName + '>';
- }
- }
- return {};
-}
-
-ProjectExplorer::HeaderPaths relevantHeaderPaths(const QString &filePath)
-{
- ProjectExplorer::HeaderPaths headerPaths;
-
- const QList<ProjectPart::ConstPtr> projectParts = CppModelManager::projectPart(filePath);
- if (projectParts.isEmpty()) { // Not part of any project, better use all include paths than none
- headerPaths += CppModelManager::headerPaths();
- } else {
- for (const ProjectPart::ConstPtr &part : projectParts)
- headerPaths += part->headerPaths;
- }
-
- return headerPaths;
-}
-
-NameAST *nameUnderCursor(const QList<AST *> &path)
-{
- if (path.isEmpty())
- return nullptr;
-
- NameAST *nameAst = nullptr;
- for (int i = path.size() - 1; i >= 0; --i) {
- AST * const ast = path.at(i);
- if (SimpleNameAST *simpleName = ast->asSimpleName()) {
- nameAst = simpleName;
- } else if (TemplateIdAST *templateId = ast->asTemplateId()) {
- nameAst = templateId;
- } else if (nameAst && ast->asNamedTypeSpecifier()) {
- break; // Stop at "Foo" for "N::Bar<@Foo>"
- } else if (QualifiedNameAST *qualifiedName = ast->asQualifiedName()) {
- nameAst = qualifiedName;
- break;
- }
- }
-
- return nameAst;
-}
-
-enum class LookupResult { Declared, ForwardDeclared, NotDeclared };
-LookupResult lookUpDefinition(const CppQuickFixInterface &interface, const NameAST *nameAst)
-{
- QTC_ASSERT(nameAst && nameAst->name, return LookupResult::NotDeclared);
-
- // Find the enclosing scope
- int line, column;
- const Document::Ptr doc = interface.semanticInfo().doc;
- doc->translationUnit()->getTokenPosition(nameAst->firstToken(), &line, &column);
- Scope *scope = doc->scopeAt(line, column);
- if (!scope)
- return LookupResult::NotDeclared;
-
- // Try to find the class/template definition
- const Name *name = nameAst->name;
- const QList<LookupItem> results = interface.context().lookup(name, scope);
- LookupResult best = LookupResult::NotDeclared;
- for (const LookupItem &item : results) {
- if (Symbol *declaration = item.declaration()) {
- if (declaration->asClass())
- return LookupResult::Declared;
- if (declaration->asForwardClassDeclaration()) {
- best = LookupResult::ForwardDeclared;
- continue;
- }
- if (Template *templ = declaration->asTemplate()) {
- if (Symbol *declaration = templ->declaration()) {
- if (declaration->asClass())
- return LookupResult::Declared;
- if (declaration->asForwardClassDeclaration()) {
- best = LookupResult::ForwardDeclared;
- continue;
- }
- }
- }
- return LookupResult::Declared;
- }
- }
-
- return best;
-}
-
-QString templateNameAsString(const TemplateNameId *templateName)
-{
- const Identifier *id = templateName->identifier();
- return QString::fromUtf8(id->chars(), id->size());
-}
-
-Snapshot forwardingHeaders(const CppQuickFixInterface &interface)
-{
- Snapshot result;
-
- const Snapshot docs = interface.snapshot();
- for (Document::Ptr doc : docs) {
- if (doc->globalSymbolCount() == 0 && doc->resolvedIncludes().size() == 1)
- result.insert(doc);
- }
-
- return result;
-}
-
-QList<IndexItem::Ptr> matchName(const Name *name, QString *className)
-{
- if (!name)
- return {};
-
- QString simpleName;
- QList<IndexItem::Ptr> matches;
- CppLocatorData *locatorData = CppModelManager::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 {
- 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);
- }
-
- if (matches.isEmpty())
- matches = locatorData->findSymbols(IndexItem::Class, *className);
-
- if (matches.isEmpty() && !simpleName.isEmpty())
- *className = simpleName;
-
- return matches;
-}
-
-} // anonymous namespace
-
-void AddIncludeForUndefinedIdentifier::doMatch(const CppQuickFixInterface &interface,
- QuickFixOperations &result)
-{
- const NameAST *nameAst = nameUnderCursor(interface.path());
- if (!nameAst || !nameAst->name)
- return;
-
- const LookupResult lookupResult = lookUpDefinition(interface, nameAst);
- if (lookupResult == LookupResult::Declared)
- return;
-
- QString className;
- 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 (!matches.isEmpty()) {
- QList<IndexItem::Ptr> indexItems;
- const Snapshot forwardHeaders = forwardingHeaders(interface);
- for (const IndexItem::Ptr &info : matches) {
- if (!info || info->symbolName() != className)
- continue;
- indexItems << info;
-
- Snapshot localForwardHeaders = forwardHeaders;
- localForwardHeaders.insert(interface.snapshot().document(info->filePath()));
- FilePaths headerAndItsForwardingHeaders;
- headerAndItsForwardingHeaders << info->filePath();
- headerAndItsForwardingHeaders += localForwardHeaders.filesDependingOn(info->filePath());
-
- for (const FilePath &header : std::as_const(headerAndItsForwardingHeaders)) {
- const QString include = findShortestInclude(currentDocumentFilePath,
- header.toString(),
- headerPaths);
- if (include.size() > 2) {
- const QString headerFileName = info->filePath().fileName();
- QTC_ASSERT(!headerFileName.isEmpty(), break);
-
- int priority = 0;
- if (headerFileName == className)
- priority = 2;
- else if (headerFileName.at(1).isUpper())
- priority = 1;
-
- result << new AddIncludeForUndefinedIdentifierOp(interface, priority,
- include);
- headers << header;
- }
- }
- }
-
- if (lookupResult == LookupResult::NotDeclared && indexItems.size() == 1) {
- QString qualifiedName = Overview().prettyName(nameAst->name);
- if (qualifiedName.startsWith("::"))
- qualifiedName.remove(0, 2);
- if (indexItems.first()->scopedSymbolName().endsWith(qualifiedName)) {
- const ProjectExplorer::Node * const node = ProjectExplorer::ProjectTree
- ::nodeForFile(interface.filePath());
- ProjectExplorer::FileType fileType = node && node->asFileNode()
- ? node->asFileNode()->fileType() : ProjectExplorer::FileType::Unknown;
- if (fileType == ProjectExplorer::FileType::Unknown
- && ProjectFile::isHeader(ProjectFile::classify(interface.filePath().toString()))) {
- fileType = ProjectExplorer::FileType::Header;
- }
- if (fileType == ProjectExplorer::FileType::Header) {
- result << new AddForwardDeclForUndefinedIdentifierOp(
- interface, 0, indexItems.first()->scopedSymbolName(),
- interface.currentFile()->startOf(nameAst));
- }
- }
- }
- }
-
- if (className.isEmpty())
- return;
-
- // Fallback: Check the include paths for files that look like candidates
- // for the given name.
- if (!Utils::contains(headers,
- [&className](const Utils::FilePath &fp) { return fp.fileName() == className; })) {
- const QString include = findMatchingInclude(className, headerPaths);
- const auto matcher = [&include](const QuickFixOperation::Ptr &o) {
- const auto includeOp = o.dynamicCast<AddIncludeForUndefinedIdentifierOp>();
- return includeOp && includeOp->include() == include;
- };
- if (!include.isEmpty() && !Utils::contains(result, matcher))
- result << new AddIncludeForUndefinedIdentifierOp(interface, 1, include);
- }
-}
-
-namespace {
-
-class RearrangeParamDeclarationListOp: public CppQuickFixOperation
-{
-public:
- enum Target { TargetPrevious, TargetNext };
-
- RearrangeParamDeclarationListOp(const CppQuickFixInterface &interface, AST *currentParam,
- AST *targetParam, Target target)
- : CppQuickFixOperation(interface)
- , m_currentParam(currentParam)
- , m_targetParam(targetParam)
- {
- QString targetString;
- if (target == TargetPrevious)
- targetString = Tr::tr("Switch with Previous Parameter");
- else
- targetString = Tr::tr("Switch with Next Parameter");
- setDescription(targetString);
- }
-
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
-
- int targetEndPos = currentFile->endOf(m_targetParam);
- ChangeSet changes;
- changes.flip(currentFile->startOf(m_currentParam), currentFile->endOf(m_currentParam),
- currentFile->startOf(m_targetParam), targetEndPos);
- currentFile->setChangeSet(changes);
- currentFile->setOpenEditor(false, targetEndPos);
- currentFile->apply();
- }
-
-private:
- AST *m_currentParam;
- AST *m_targetParam;
-};
-
-} // anonymous namespace
-
-void RearrangeParamDeclarationList::doMatch(const CppQuickFixInterface &interface,
- QuickFixOperations &result)
-{
- const QList<AST *> path = interface.path();
-
- ParameterDeclarationAST *paramDecl = nullptr;
- int index = path.size() - 1;
- for (; index != -1; --index) {
- paramDecl = path.at(index)->asParameterDeclaration();
- if (paramDecl)
- break;
- }
-
- if (index < 1)
- return;
-
- ParameterDeclarationClauseAST *paramDeclClause = path.at(index-1)->asParameterDeclarationClause();
- QTC_ASSERT(paramDeclClause && paramDeclClause->parameter_declaration_list, return);
-
- ParameterDeclarationListAST *paramListNode = paramDeclClause->parameter_declaration_list;
- ParameterDeclarationListAST *prevParamListNode = nullptr;
- while (paramListNode) {
- if (paramDecl == paramListNode->value)
- break;
- prevParamListNode = paramListNode;
- paramListNode = paramListNode->next;
- }
-
- if (!paramListNode)
- return;
-
- if (prevParamListNode)
- result << new RearrangeParamDeclarationListOp(interface, paramListNode->value,
- prevParamListNode->value, RearrangeParamDeclarationListOp::TargetPrevious);
- if (paramListNode->next)
- result << new RearrangeParamDeclarationListOp(interface, paramListNode->value,
- paramListNode->next->value, RearrangeParamDeclarationListOp::TargetNext);
-}
-
-namespace {
-
-class ReformatPointerDeclarationOp: public CppQuickFixOperation
-{
-public:
- ReformatPointerDeclarationOp(const CppQuickFixInterface &interface, const ChangeSet change)
- : CppQuickFixOperation(interface)
- , m_change(change)
- {
- QString description;
- if (m_change.operationList().size() == 1) {
- description = Tr::tr(
- "Reformat to \"%1\"").arg(m_change.operationList().constFirst().text());
- } else { // > 1
- description = Tr::tr("Reformat Pointers or References");
- }
- setDescription(description);
- }
-
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
- currentFile->setChangeSet(m_change);
- currentFile->apply();
- }
-
-private:
- ChangeSet m_change;
-};
-
-/// Filter the results of ASTPath.
-/// The resulting list contains the supported AST types only once.
-/// For this, the results of ASTPath are iterated in reverse order.
-class ReformatPointerDeclarationASTPathResultsFilter
-{
-public:
- QList<AST*> filter(const QList<AST*> &astPathList)
- {
- QList<AST*> filtered;
-
- for (int i = astPathList.size() - 1; i >= 0; --i) {
- AST *ast = astPathList.at(i);
-
- if (!m_hasSimpleDeclaration && ast->asSimpleDeclaration()) {
- m_hasSimpleDeclaration = true;
- filtered.append(ast);
- } else if (!m_hasFunctionDefinition && ast->asFunctionDefinition()) {
- m_hasFunctionDefinition = true;
- filtered.append(ast);
- } else if (!m_hasParameterDeclaration && ast->asParameterDeclaration()) {
- m_hasParameterDeclaration = true;
- filtered.append(ast);
- } else if (!m_hasIfStatement && ast->asIfStatement()) {
- m_hasIfStatement = true;
- filtered.append(ast);
- } else if (!m_hasWhileStatement && ast->asWhileStatement()) {
- m_hasWhileStatement = true;
- filtered.append(ast);
- } else if (!m_hasForStatement && ast->asForStatement()) {
- m_hasForStatement = true;
- filtered.append(ast);
- } else if (!m_hasForeachStatement && ast->asForeachStatement()) {
- m_hasForeachStatement = true;
- filtered.append(ast);
- }
- }
-
- return filtered;
- }
-
-private:
- bool m_hasSimpleDeclaration = false;
- bool m_hasFunctionDefinition = false;
- bool m_hasParameterDeclaration = false;
- bool m_hasIfStatement = false;
- bool m_hasWhileStatement = false;
- bool m_hasForStatement = false;
- bool m_hasForeachStatement = false;
-};
-
-} // anonymous namespace
-
-void ReformatPointerDeclaration::doMatch(const CppQuickFixInterface &interface,
- QuickFixOperations &result)
-{
- const QList<AST *> &path = interface.path();
- CppRefactoringFilePtr file = interface.currentFile();
-
- Overview overview = CppCodeStyleSettings::currentProjectCodeStyleOverview();
- overview.showArgumentNames = true;
- overview.showReturnTypes = true;
-
- const QTextCursor cursor = file->cursor();
- ChangeSet change;
- PointerDeclarationFormatter formatter(file, overview,
- PointerDeclarationFormatter::RespectCursor);
-
- if (cursor.hasSelection()) {
- // This will no work always as expected since this function is only called if
- // interface-path() is not empty. If the user selects the whole document via
- // ctrl-a and there is an empty line in the end, then the cursor is not on
- // any AST and therefore no quick fix will be triggered.
- change = formatter.format(file->cppDocument()->translationUnit()->ast());
- if (!change.isEmpty())
- result << new ReformatPointerDeclarationOp(interface, change);
- } else {
- const QList<AST *> suitableASTs
- = ReformatPointerDeclarationASTPathResultsFilter().filter(path);
- for (AST *ast : suitableASTs) {
- change = formatter.format(ast);
- if (!change.isEmpty()) {
- result << new ReformatPointerDeclarationOp(interface, change);
- return;
- }
- }
- }
-}
-
-namespace {
-
-class CaseStatementCollector : public ASTVisitor
-{
-public:
- CaseStatementCollector(Document::Ptr document, const Snapshot &snapshot,
- Scope *scope)
- : ASTVisitor(document->translationUnit()),
- document(document),
- scope(scope)
- {
- typeOfExpression.init(document, snapshot);
- }
-
- QStringList operator ()(AST *ast)
- {
- values.clear();
- foundCaseStatementLevel = false;
- accept(ast);
- return values;
- }
-
- bool preVisit(AST *ast) override {
- if (CaseStatementAST *cs = ast->asCaseStatement()) {
- foundCaseStatementLevel = true;
- if (ExpressionAST *csExpression = cs->expression) {
- if (ExpressionAST *expression = csExpression->asIdExpression()) {
- QList<LookupItem> candidates = typeOfExpression(expression, document, scope);
- if (!candidates.isEmpty() && candidates.first().declaration()) {
- Symbol *decl = candidates.first().declaration();
- values << prettyPrint.prettyName(LookupContext::fullyQualifiedName(decl));
- }
- }
- }
- return true;
- } else if (foundCaseStatementLevel) {
- return false;
- }
- return true;
- }
-
- Overview prettyPrint;
- bool foundCaseStatementLevel = false;
- QStringList values;
- TypeOfExpression typeOfExpression;
- Document::Ptr document;
- Scope *scope;
-};
-
-class CompleteSwitchCaseStatementOp: public CppQuickFixOperation
-{
-public:
- CompleteSwitchCaseStatementOp(const CppQuickFixInterface &interface,
- int priority, CompoundStatementAST *compoundStatement, const QStringList &values)
- : CppQuickFixOperation(interface, priority)
- , compoundStatement(compoundStatement)
- , values(values)
- {
- setDescription(Tr::tr("Complete Switch Statement"));
- }
-
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
-
- ChangeSet changes;
- int start = currentFile->endOf(compoundStatement->lbrace_token);
- changes.insert(start, QLatin1String("\ncase ")
- + values.join(QLatin1String(":\nbreak;\ncase "))
- + QLatin1String(":\nbreak;"));
- currentFile->setChangeSet(changes);
- currentFile->apply();
- }
-
- CompoundStatementAST *compoundStatement;
- QStringList values;
-};
-
-static Enum *findEnum(const QList<LookupItem> &results, const LookupContext &ctxt)
-{
- for (const LookupItem &result : results) {
- const FullySpecifiedType fst = result.type();
-
- Type *type = result.declaration() ? result.declaration()->type().type()
- : fst.type();
-
- if (!type)
- continue;
- if (Enum *e = type->asEnumType())
- return e;
- if (const NamedType *namedType = type->asNamedType()) {
- if (ClassOrNamespace *con = ctxt.lookupType(namedType->name(), result.scope())) {
- QList<Enum *> enums = con->unscopedEnums();
- const QList<Symbol *> symbols = con->symbols();
- for (Symbol * const s : symbols) {
- if (const auto e = s->asEnum())
- enums << e;
- }
- const Name *referenceName = namedType->name();
- if (const QualifiedNameId *qualifiedName = referenceName->asQualifiedNameId())
- referenceName = qualifiedName->name();
- for (Enum *e : std::as_const(enums)) {
- if (const Name *candidateName = e->name()) {
- if (candidateName->match(referenceName))
- return e;
- }
- }
- }
- }
- }
-
- return nullptr;
-}
-
-Enum *conditionEnum(const CppQuickFixInterface &interface, SwitchStatementAST *statement)
-{
- Block *block = statement->symbol;
- Scope *scope = interface.semanticInfo().doc->scopeAt(block->line(), block->column());
- TypeOfExpression typeOfExpression;
- typeOfExpression.setExpandTemplates(true);
- typeOfExpression.init(interface.semanticInfo().doc, interface.snapshot());
- const QList<LookupItem> results = typeOfExpression(statement->condition,
- interface.semanticInfo().doc,
- scope);
-
- return findEnum(results, typeOfExpression.context());
-}
-
-} // anonymous namespace
-
-void CompleteSwitchCaseStatement::doMatch(const CppQuickFixInterface &interface,
- QuickFixOperations &result)
-{
- const QList<AST *> &path = interface.path();
-
- if (path.isEmpty())
- return;
-
- // look for switch statement
- for (int depth = path.size() - 1; depth >= 0; --depth) {
- AST *ast = path.at(depth);
- SwitchStatementAST *switchStatement = ast->asSwitchStatement();
- if (switchStatement) {
- if (!switchStatement->statement || !switchStatement->symbol)
- return;
- CompoundStatementAST *compoundStatement = switchStatement->statement->asCompoundStatement();
- if (!compoundStatement) // we ignore pathologic case "switch (t) case A: ;"
- return;
- // look if the condition's type is an enum
- if (Enum *e = conditionEnum(interface, switchStatement)) {
- // check the possible enum values
- QStringList values;
- Overview prettyPrint;
- for (int i = 0; i < e->memberCount(); ++i) {
- if (Declaration *decl = e->memberAt(i)->asDeclaration())
- values << prettyPrint.prettyName(LookupContext::fullyQualifiedName(decl));
- }
- // Get the used values
- Block *block = switchStatement->symbol;
- CaseStatementCollector caseValues(interface.semanticInfo().doc, interface.snapshot(),
- interface.semanticInfo().doc->scopeAt(block->line(), block->column()));
- const QStringList usedValues = caseValues(switchStatement);
- // save the values that would be added
- for (const QString &usedValue : usedValues)
- values.removeAll(usedValue);
- if (!values.isEmpty())
- result << new CompleteSwitchCaseStatementOp(interface, depth,
- compoundStatement, values);
- return;
- }
-
- return;
- }
- }
-}
-
-namespace {
-
-class InsertDeclOperation: public CppQuickFixOperation
-{
-public:
- InsertDeclOperation(const CppQuickFixInterface &interface,
- const FilePath &targetFilePath, const Class *targetSymbol,
- InsertionPointLocator::AccessSpec xsSpec, const QString &decl, int priority)
- : CppQuickFixOperation(interface, priority)
- , m_targetFilePath(targetFilePath)
- , m_targetSymbol(targetSymbol)
- , m_xsSpec(xsSpec)
- , m_decl(decl)
- {
- setDescription(Tr::tr("Add %1 Declaration")
- .arg(InsertionPointLocator::accessSpecToString(xsSpec)));
- }
-
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
-
- InsertionPointLocator locator(refactoring);
- const InsertionLocation loc = locator.methodDeclarationInClass(
- m_targetFilePath, m_targetSymbol, m_xsSpec);
- QTC_ASSERT(loc.isValid(), return);
-
- CppRefactoringFilePtr targetFile = refactoring.cppFile(m_targetFilePath);
- int targetPosition = targetFile->position(loc.line(), loc.column());
-
- ChangeSet target;
- target.insert(targetPosition, loc.prefix() + m_decl);
- targetFile->setChangeSet(target);
- targetFile->setOpenEditor(true, targetPosition);
- targetFile->apply();
- }
-
- static QString generateDeclaration(const Function *function);
-
-private:
- FilePath m_targetFilePath;
- const Class *m_targetSymbol;
- InsertionPointLocator::AccessSpec m_xsSpec;
- QString m_decl;
-};
-
-class DeclOperationFactory
-{
-public:
- DeclOperationFactory(const CppQuickFixInterface &interface, const FilePath &filePath,
- const Class *matchingClass, const QString &decl)
- : m_interface(interface)
- , m_filePath(filePath)
- , m_matchingClass(matchingClass)
- , m_decl(decl)
- {}
-
- QuickFixOperation *operator()(InsertionPointLocator::AccessSpec xsSpec, int priority)
- {
- return new InsertDeclOperation(m_interface, m_filePath, m_matchingClass, xsSpec, m_decl, priority);
- }
-
-private:
- const CppQuickFixInterface &m_interface;
- const FilePath &m_filePath;
- const Class *m_matchingClass;
- const QString &m_decl;
-};
-
-} // anonymous namespace
-
-void InsertDeclFromDef::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- const QList<AST *> &path = interface.path();
- CppRefactoringFilePtr file = interface.currentFile();
-
- FunctionDefinitionAST *funDef = nullptr;
- int idx = 0;
- for (; idx < path.size(); ++idx) {
- AST *node = path.at(idx);
- if (idx > 1) {
- if (DeclaratorIdAST *declId = node->asDeclaratorId()) {
- if (file->isCursorOn(declId)) {
- if (FunctionDefinitionAST *candidate = path.at(idx - 2)->asFunctionDefinition()) {
- funDef = candidate;
- break;
- }
- }
- }
- }
-
- if (node->asClassSpecifier())
- return;
- }
-
- if (!funDef || !funDef->symbol)
- return;
-
- Function *fun = funDef->symbol;
- if (Class *matchingClass = isMemberFunction(interface.context(), fun)) {
- const QualifiedNameId *qName = fun->name()->asQualifiedNameId();
- for (Symbol *symbol = matchingClass->find(qName->identifier());
- symbol; symbol = symbol->next()) {
- Symbol *s = symbol;
- if (fun->enclosingScope()->asTemplate()) {
- if (const Template *templ = s->type()->asTemplateType()) {
- if (Symbol *decl = templ->declaration()) {
- if (decl->type()->asFunctionType())
- s = decl;
- }
- }
- }
- if (!s->name()
- || !qName->identifier()->match(s->identifier())
- || !s->type()->asFunctionType())
- continue;
-
- if (s->type().match(fun->type())) {
- // Declaration exists.
- return;
- }
- }
- const FilePath fileName = matchingClass->filePath();
- const QString decl = InsertDeclOperation::generateDeclaration(fun);
-
- // Add several possible insertion locations for declaration
- DeclOperationFactory operation(interface, fileName, matchingClass, decl);
-
- result << operation(InsertionPointLocator::Public, 5)
- << operation(InsertionPointLocator::PublicSlot, 4)
- << operation(InsertionPointLocator::Protected, 3)
- << operation(InsertionPointLocator::ProtectedSlot, 2)
- << operation(InsertionPointLocator::Private, 1)
- << operation(InsertionPointLocator::PrivateSlot, 0);
- }
-}
-
-QString InsertDeclOperation::generateDeclaration(const Function *function)
-{
- Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
- oo.showFunctionSignatures = true;
- oo.showReturnTypes = true;
- oo.showArgumentNames = true;
- oo.showEnclosingTemplate = true;
-
- QString decl;
- decl += oo.prettyType(function->type(), function->unqualifiedName());
- decl += QLatin1String(";\n");
-
- return decl;
-}
-
-namespace {
-
-class InsertDefOperation: public CppQuickFixOperation
-{
-public:
- // Make sure that either loc is valid or targetFileName is not empty.
- InsertDefOperation(const CppQuickFixInterface &interface,
- Declaration *decl, DeclaratorAST *declAST, const InsertionLocation &loc,
- const DefPos defpos, const FilePath &targetFileName = {},
- bool freeFunction = false)
- : CppQuickFixOperation(interface, 0)
- , m_decl(decl)
- , m_declAST(declAST)
- , m_loc(loc)
- , m_defpos(defpos)
- , m_targetFilePath(targetFileName)
- {
- if (m_defpos == DefPosImplementationFile) {
- const FilePath declFile = decl->filePath();
- const FilePath targetFile = m_loc.isValid() ? m_loc.filePath() : m_targetFilePath;
- const FilePath resolved = targetFile.relativePathFrom(declFile.parentDir());
- setPriority(2);
- setDescription(Tr::tr("Add Definition in %1").arg(resolved.displayName()));
- } else if (freeFunction) {
- setDescription(Tr::tr("Add Definition Here"));
- } else if (m_defpos == DefPosInsideClass) {
- setDescription(Tr::tr("Add Definition Inside Class"));
- } else if (m_defpos == DefPosOutsideClass) {
- setPriority(1);
- setDescription(Tr::tr("Add Definition Outside Class"));
- }
- }
-
- static void insertDefinition(
- const CppQuickFixOperation *op,
- InsertionLocation loc,
- DefPos defPos,
- DeclaratorAST *declAST,
- Declaration *decl,
- const FilePath &targetFilePath,
- ChangeSet *changeSet = nullptr)
- {
- CppRefactoringChanges refactoring(op->snapshot());
- if (!loc.isValid())
- loc = insertLocationForMethodDefinition(decl, true, NamespaceHandling::Ignore,
- refactoring, targetFilePath);
- QTC_ASSERT(loc.isValid(), return);
-
- CppRefactoringFilePtr targetFile = refactoring.cppFile(loc.filePath());
- Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
- oo.showFunctionSignatures = true;
- oo.showReturnTypes = true;
- oo.showArgumentNames = true;
- oo.showEnclosingTemplate = true;
-
- // What we really want is to show template parameters for the class, but not for the
- // function, but we cannot express that. This is an approximation that will work
- // as long as either the surrounding class or the function is not a template.
- oo.showTemplateParameters = decl->enclosingClass()
- && decl->enclosingClass()->enclosingTemplate();
-
- if (defPos == DefPosInsideClass) {
- const int targetPos = targetFile->position(loc.line(), loc.column());
- ChangeSet localChangeSet;
- ChangeSet * const target = changeSet ? changeSet : &localChangeSet;
- target->replace(targetPos - 1, targetPos, QLatin1String("\n {\n\n}")); // replace ';'
-
- if (!changeSet) {
- targetFile->setChangeSet(*target);
- targetFile->setOpenEditor(true, targetPos);
- targetFile->apply();
-
- // Move cursor inside definition
- QTextCursor c = targetFile->cursor();
- c.setPosition(targetPos);
- c.movePosition(QTextCursor::Down);
- c.movePosition(QTextCursor::EndOfLine);
- op->editor()->setTextCursor(c);
- }
- } else {
- // make target lookup context
- Document::Ptr targetDoc = targetFile->cppDocument();
- Scope *targetScope = targetDoc->scopeAt(loc.line(), loc.column());
-
- // Correct scope in case of a function try-block. See QTCREATORBUG-14661.
- if (targetScope && targetScope->asBlock()) {
- if (Class * const enclosingClass = targetScope->enclosingClass())
- targetScope = enclosingClass;
- else
- targetScope = targetScope->enclosingNamespace();
- }
-
- LookupContext targetContext(targetDoc, op->snapshot());
- ClassOrNamespace *targetCoN = targetContext.lookupType(targetScope);
- if (!targetCoN)
- targetCoN = targetContext.globalNamespace();
-
- // setup rewriting to get minimally qualified names
- SubstitutionEnvironment env;
- env.setContext(op->context());
- env.switchScope(decl->enclosingScope());
- UseMinimalNames q(targetCoN);
- env.enter(&q);
- Control *control = op->context().bindings()->control().get();
-
- // rewrite the function type
- const FullySpecifiedType tn = rewriteType(decl->type(), &env, control);
-
- // rewrite the function name
- if (nameIncludesOperatorName(decl->name())) {
- CppRefactoringFilePtr file = refactoring.cppFile(op->filePath());
- const QString operatorNameText = file->textOf(declAST->core_declarator);
- oo.includeWhiteSpaceInOperatorName = operatorNameText.contains(QLatin1Char(' '));
- }
- const QString name = oo.prettyName(LookupContext::minimalName(decl, targetCoN,
- control));
-
- const QString inlinePref = inlinePrefix(targetFilePath, [defPos] {
- return defPos == DefPosOutsideClass;
- });
-
- const QString prettyType = oo.prettyType(tn, name);
-
- QString input = prettyType;
- int index = 0;
- while (input.startsWith("template")) {
- QRegularExpression templateRegex("template\\s*<[^>]*>");
- QRegularExpressionMatch match = templateRegex.match(input);
- if (match.hasMatch()) {
- index += match.captured().size() + 1;
- input = input.mid(match.captured().size() + 1);
- }
- }
-
- QString defText = prettyType;
- defText.insert(index, inlinePref);
- defText += QLatin1String("\n{\n\n}");
-
- ChangeSet localChangeSet;
- ChangeSet * const target = changeSet ? changeSet : &localChangeSet;
- const int targetPos = targetFile->position(loc.line(), loc.column());
- target->insert(targetPos, loc.prefix() + defText + loc.suffix());
-
- if (!changeSet) {
- targetFile->setChangeSet(*target);
- targetFile->setOpenEditor(true, targetPos);
- targetFile->apply();
-
- // Move cursor inside definition
- QTextCursor c = targetFile->cursor();
- c.setPosition(targetPos);
- c.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor,
- loc.prefix().count(QLatin1String("\n")) + 2);
- c.movePosition(QTextCursor::EndOfLine);
- if (defPos == DefPosImplementationFile) {
- if (targetFile->editor())
- targetFile->editor()->setTextCursor(c);
- } else {
- op->editor()->setTextCursor(c);
- }
- }
- }
- }
-
-private:
- void perform() override
- {
- insertDefinition(this, m_loc, m_defpos, m_declAST, m_decl, m_targetFilePath);
- }
-
- Declaration *m_decl;
- DeclaratorAST *m_declAST;
- InsertionLocation m_loc;
- const DefPos m_defpos;
- const FilePath m_targetFilePath;
-};
-
-} // anonymous namespace
-
-void InsertDefFromDecl::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- const QList<AST *> &path = interface.path();
-
- int idx = path.size() - 1;
- for (; idx >= 0; --idx) {
- AST *node = path.at(idx);
- if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
- if (idx > 0 && path.at(idx - 1)->asStatement())
- return;
- if (simpleDecl->symbols && !simpleDecl->symbols->next) {
- if (Symbol *symbol = simpleDecl->symbols->value) {
- if (Declaration *decl = symbol->asDeclaration()) {
- if (Function *func = decl->type()->asFunctionType()) {
- if (func->isSignal() || func->isPureVirtual() || func->isFriend())
- return;
-
- // Check if there is already a definition
- SymbolFinder symbolFinder;
- if (symbolFinder.findMatchingDefinition(decl, interface.snapshot(),
- true)) {
- return;
- }
-
- // Insert Position: Implementation File
- DeclaratorAST *declAST = simpleDecl->declarator_list->value;
- InsertDefOperation *op = nullptr;
- ProjectFile::Kind kind = ProjectFile::classify(interface.filePath().toString());
- const bool isHeaderFile = ProjectFile::isHeader(kind);
- if (isHeaderFile) {
- CppRefactoringChanges refactoring(interface.snapshot());
- InsertionPointLocator locator(refactoring);
- // find appropriate implementation file, but do not use this
- // location, because insertLocationForMethodDefinition() should
- // be used in perform() to get consistent insert positions.
- for (const InsertionLocation &location :
- locator.methodDefinition(decl, false, {})) {
- if (!location.isValid())
- continue;
-
- const FilePath filePath = location.filePath();
- if (ProjectFile::isHeader(ProjectFile::classify(filePath.path()))) {
- const FilePath source = correspondingHeaderOrSource(filePath);
- if (!source.isEmpty()) {
- op = new InsertDefOperation(interface, decl, declAST,
- InsertionLocation(),
- DefPosImplementationFile,
- source);
- }
- } else {
- op = new InsertDefOperation(interface, decl, declAST,
- InsertionLocation(),
- DefPosImplementationFile,
- filePath);
- }
-
- if (op)
- result << op;
- break;
- }
- }
-
- // Determine if we are dealing with a free function
- const bool isFreeFunction = func->enclosingClass() == nullptr;
-
- // Insert Position: Outside Class
- if (!isFreeFunction || m_defPosOutsideClass) {
- result << new InsertDefOperation(interface, decl, declAST,
- InsertionLocation(),
- DefPosOutsideClass,
- interface.filePath());
- }
-
- // Insert Position: Inside Class
- // Determine insert location direct after the declaration.
- int line, column;
- const CppRefactoringFilePtr file = interface.currentFile();
- file->lineAndColumn(file->endOf(simpleDecl), &line, &column);
- const InsertionLocation loc
- = InsertionLocation(interface.filePath(), QString(),
- QString(), line, column);
- result << new InsertDefOperation(interface, decl, declAST, loc,
- DefPosInsideClass, FilePath(),
- isFreeFunction);
-
- return;
- }
- }
- }
- }
- break;
- }
- }
-}
-
-class InsertMemberFromInitializationOp : public CppQuickFixOperation
-{
-public:
- InsertMemberFromInitializationOp(
- const CppQuickFixInterface &interface,
- const Class *theClass,
- const NameAST *memberName,
- const TypeOrExpr &typeOrExpr,
- const CallAST *call,
- InsertionPointLocator::AccessSpec accessSpec,
- bool makeStatic,
- bool makeConst)
- : CppQuickFixOperation(interface),
- m_class(theClass), m_memberName(memberName), m_typeOrExpr(typeOrExpr), m_call(call),
- m_accessSpec(accessSpec), m_makeStatic(makeStatic), m_makeConst(makeConst)
- {
- 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 decl = declFromExpr(m_typeOrExpr, m_call, m_memberName, snapshot(), context(),
- currentFile(), m_makeConst);
- 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, m_accessSpec);
- QTC_ASSERT(loc.isValid(), return);
-
- CppRefactoringFilePtr targetFile = refactoring.cppFile(filePath);
- const int targetPosition = targetFile->position(loc.line(), loc.column());
- ChangeSet target;
- target.insert(targetPosition, loc.prefix() + decl + ";\n");
- targetFile->setChangeSet(target);
- targetFile->apply();
- }
-
- const Class * const m_class;
- const NameAST * const m_memberName;
- const TypeOrExpr m_typeOrExpr;
- const CallAST * m_call;
- const InsertionPointLocator::AccessSpec m_accessSpec;
- const bool m_makeStatic;
- const bool m_makeConst;
-};
-
-void AddDeclarationForUndeclaredIdentifier::doMatch(const CppQuickFixInterface &interface,
- QuickFixOperations &result)
-{
- // Are we on a name?
- const QList<AST *> &path = interface.path();
- if (path.isEmpty())
- return;
- 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,
- FollowSymbolMode::Exact,
- 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 true;
- if (!path.at(size - 3)->asCtorInitializer())
- return true;
- const FunctionDefinitionAST * ctor = path.at(size - 4)->asFunctionDefinition();
- if (!ctor)
- return false;
-
- // Now find the class.
- const Class *theClass = nullptr;
- if (size > 4) {
- const ClassSpecifierAST * const classSpec = path.at(size - 5)->asClassSpecifier();
- if (classSpec) // Inline constructor. We get the class directly.
- theClass = classSpec->symbol;
- }
- if (!theClass) {
- // 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);
- if (!matches.isEmpty())
- theClass = matches.first()->enclosingClass();
- }
-
- if (!theClass)
- 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 false;
- }
-
- result << new InsertMemberFromInitializationOp(
- interface, theClass, memInitializer->name->asSimpleName(), memInitializer->expression,
- nullptr, InsertionPointLocator::Private, false, 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;
-
- 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,
- func->symbol->isConst());
-}
-
-void AddDeclarationForUndeclaredIdentifier::maybeAddStaticMember(
- const CppQuickFixInterface &interface, const QualifiedNameAST *qualName,
- const TypeOrExpr &typeOrExpr, const CallAST *call, TextEditor::QuickFixOperations &result)
-{
- 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;
- }
-
- 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, false);
- }
-}
-
-class MemberFunctionImplSetting
-{
-public:
- Symbol *func = nullptr;
- DefPos defPos = DefPosImplementationFile;
-};
-using MemberFunctionImplSettings = QList<MemberFunctionImplSetting>;
-
-class AddImplementationsDialog : public QDialog
-{
-public:
- AddImplementationsDialog(const QList<Symbol *> &candidates, const FilePath &implFile)
- : QDialog(Core::ICore::dialogParent()), m_candidates(candidates)
- {
- setWindowTitle(Tr::tr("Member Function Implementations"));
-
- const auto defaultImplTargetComboBox = new QComboBox;
- QStringList implTargetStrings{Tr::tr("None"), Tr::tr("Inline"), Tr::tr("Outside Class")};
- if (!implFile.isEmpty())
- implTargetStrings.append(implFile.fileName());
- defaultImplTargetComboBox->insertItems(0, implTargetStrings);
- connect(defaultImplTargetComboBox, &QComboBox::currentIndexChanged, this,
- [this](int index) {
- for (int i = 0; i < m_implTargetBoxes.size(); ++i) {
- if (!m_candidates.at(i)->type()->asFunctionType()->isPureVirtual())
- static_cast<QComboBox *>(m_implTargetBoxes.at(i))->setCurrentIndex(index);
- }
- });
- const auto defaultImplTargetLayout = new QHBoxLayout;
- defaultImplTargetLayout->addWidget(new QLabel(Tr::tr("Default implementation location:")));
- defaultImplTargetLayout->addWidget(defaultImplTargetComboBox);
-
- const auto candidatesLayout = new QGridLayout;
- Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
- oo.showFunctionSignatures = true;
- oo.showReturnTypes = true;
- for (int i = 0; i < m_candidates.size(); ++i) {
- const Function * const func = m_candidates.at(i)->type()->asFunctionType();
- QTC_ASSERT(func, continue);
- const auto implTargetComboBox = new QComboBox;
- m_implTargetBoxes.append(implTargetComboBox);
- implTargetComboBox->insertItems(0, implTargetStrings);
- if (func->isPureVirtual())
- implTargetComboBox->setCurrentIndex(0);
- candidatesLayout->addWidget(new QLabel(oo.prettyType(func->type(), func->name())),
- i, 0);
- candidatesLayout->addWidget(implTargetComboBox, i, 1);
- }
-
- const auto buttonBox
- = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
- connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
- connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
-
- defaultImplTargetComboBox->setCurrentIndex(implTargetStrings.size() - 1);
- const auto mainLayout = new QVBoxLayout(this);
- mainLayout->addLayout(defaultImplTargetLayout);
- mainLayout->addWidget(Layouting::createHr(this));
- mainLayout->addLayout(candidatesLayout);
- mainLayout->addWidget(buttonBox);
- }
-
- MemberFunctionImplSettings settings() const
- {
- QTC_ASSERT(m_candidates.size() == m_implTargetBoxes.size(), return {});
- MemberFunctionImplSettings settings;
- for (int i = 0; i < m_candidates.size(); ++i) {
- MemberFunctionImplSetting setting;
- const int index = m_implTargetBoxes.at(i)->currentIndex();
- const bool addImplementation = index != 0;
- if (!addImplementation)
- continue;
- setting.func = m_candidates.at(i);
- setting.defPos = static_cast<DefPos>(index - 1);
- settings << setting;
- }
- return settings;
- }
-
-private:
- const QList<Symbol *> m_candidates;
- QList<QComboBox *> m_implTargetBoxes;
-};
-
-class InsertDefsOperation: public CppQuickFixOperation
-{
-public:
- InsertDefsOperation(const CppQuickFixInterface &interface)
- : CppQuickFixOperation(interface)
- {
- setDescription(Tr::tr("Create Implementations for Member Functions"));
-
- m_classAST = astForClassOperations(interface);
- if (!m_classAST)
- return;
- const Class * const theClass = m_classAST->symbol;
- if (!theClass)
- return;
-
- // Collect all member functions.
- for (auto it = theClass->memberBegin(); it != theClass->memberEnd(); ++it) {
- Symbol * const s = *it;
- if (!s->identifier() || !s->type() || !s->asDeclaration() || s->asFunction())
- continue;
- Function * const func = s->type()->asFunctionType();
- if (!func || func->isSignal() || func->isFriend())
- continue;
- Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
- oo.showFunctionSignatures = true;
- if (magicQObjectFunctions().contains(oo.prettyName(func->name())))
- continue;
- m_declarations << s;
- }
- }
-
- bool isApplicable() const { return !m_declarations.isEmpty(); }
- void setMode(InsertDefsFromDecls::Mode mode) { m_mode = mode; }
-
-private:
- void perform() override
- {
- QList<Symbol *> unimplemented;
- SymbolFinder symbolFinder;
- for (Symbol * const s : std::as_const(m_declarations)) {
- if (!symbolFinder.findMatchingDefinition(s, snapshot()))
- unimplemented << s;
- }
- if (unimplemented.isEmpty())
- return;
-
- CppRefactoringChanges refactoring(snapshot());
- const bool isHeaderFile = ProjectFile::isHeader(ProjectFile::classify(filePath().toString()));
- FilePath cppFile; // Only set if the class is defined in a header file.
- if (isHeaderFile) {
- InsertionPointLocator locator(refactoring);
- for (const InsertionLocation &location
- : locator.methodDefinition(unimplemented.first(), false, {})) {
- if (!location.isValid())
- continue;
- const FilePath filePath = location.filePath();
- if (ProjectFile::isHeader(ProjectFile::classify(filePath.path()))) {
- const FilePath source = correspondingHeaderOrSource(filePath);
- if (!source.isEmpty())
- cppFile = source;
- } else {
- cppFile = filePath;
- }
- break;
- }
- }
-
- MemberFunctionImplSettings settings;
- switch (m_mode) {
- case InsertDefsFromDecls::Mode::User: {
- AddImplementationsDialog dlg(unimplemented, cppFile);
- if (dlg.exec() == QDialog::Accepted)
- settings = dlg.settings();
- break;
- }
- case InsertDefsFromDecls::Mode::Impl: {
- for (Symbol * const func : std::as_const(unimplemented)) {
- MemberFunctionImplSetting setting;
- setting.func = func;
- setting.defPos = DefPosImplementationFile;
- settings << setting;
- }
- break;
- }
- case InsertDefsFromDecls::Mode::Alternating: {
- int defPos = DefPosImplementationFile;
- const auto incDefPos = [&defPos] {
- defPos = (defPos + 1) % (DefPosImplementationFile + 2);
- };
- for (Symbol * const func : std::as_const(unimplemented)) {
- incDefPos();
- if (defPos > DefPosImplementationFile)
- continue;
- MemberFunctionImplSetting setting;
- setting.func = func;
- setting.defPos = static_cast<DefPos>(defPos);
- settings << setting;
- }
- break;
- }
- case InsertDefsFromDecls::Mode::Off:
- break;
- }
-
- if (settings.isEmpty())
- return;
-
- class DeclFinder : public ASTVisitor
- {
- public:
- DeclFinder(const CppRefactoringFile *file, const Symbol *func)
- : ASTVisitor(file->cppDocument()->translationUnit()), m_func(func) {}
-
- SimpleDeclarationAST *decl() const { return m_decl; }
-
- private:
- bool visit(SimpleDeclarationAST *decl) override
- {
- if (m_decl)
- return false;
- if (decl->symbols && decl->symbols->value == m_func)
- m_decl = decl;
- return !m_decl;
- }
-
- const Symbol * const m_func;
- SimpleDeclarationAST *m_decl = nullptr;
- };
-
- QHash<FilePath, ChangeSet> changeSets;
- for (const MemberFunctionImplSetting &setting : std::as_const(settings)) {
- DeclFinder finder(currentFile().data(), setting.func);
- finder.accept(m_classAST);
- QTC_ASSERT(finder.decl(), continue);
- InsertionLocation loc;
- const FilePath targetFilePath = setting.defPos == DefPosImplementationFile
- ? cppFile : filePath();
- QTC_ASSERT(!targetFilePath.isEmpty(), continue);
- if (setting.defPos == DefPosInsideClass) {
- int line, column;
- currentFile()->lineAndColumn(currentFile()->endOf(finder.decl()), &line, &column);
- loc = InsertionLocation(filePath(), QString(), QString(), line, column);
- }
- ChangeSet &changeSet = changeSets[targetFilePath];
- InsertDefOperation::insertDefinition(
- this, loc, setting.defPos, finder.decl()->declarator_list->value,
- setting.func->asDeclaration(),targetFilePath, &changeSet);
- }
- for (auto it = changeSets.cbegin(); it != changeSets.cend(); ++it) {
- const CppRefactoringFilePtr file = refactoring.cppFile(it.key());
- file->setChangeSet(it.value());
- file->apply();
- }
- }
-
- ClassSpecifierAST *m_classAST = nullptr;
- InsertDefsFromDecls::Mode m_mode;
- QList<Symbol *> m_declarations;
-};
-
-
-void InsertDefsFromDecls::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- const auto op = QSharedPointer<InsertDefsOperation>::create(interface);
- op->setMode(m_mode);
- if (op->isApplicable())
- result << op;
-}
-
-namespace {
-
-std::optional<FullySpecifiedType> getFirstTemplateParameter(const Name *name)
-{
- if (const QualifiedNameId *qualifiedName = name->asQualifiedNameId())
- return getFirstTemplateParameter(qualifiedName->name());
-
- if (const TemplateNameId *templateName = name->asTemplateNameId()) {
- if (templateName->templateArgumentCount() > 0)
- return templateName->templateArgumentAt(0).type();
- }
- return {};
-}
-
-std::optional<FullySpecifiedType> getFirstTemplateParameter(Type *type)
-{
- if (NamedType *namedType = type->asNamedType())
- return getFirstTemplateParameter(namedType->name());
-
- return {};
-}
-
-std::optional<FullySpecifiedType> getFirstTemplateParameter(FullySpecifiedType type)
-{
- return getFirstTemplateParameter(type.type());
-}
-
-QString symbolAtDifferentLocation(const CppQuickFixInterface &interface,
- Symbol *symbol,
- const CppRefactoringFilePtr &targetFile,
- InsertionLocation targetLocation)
-{
- QTC_ASSERT(symbol, return QString());
- Scope *scopeAtInsertPos = targetFile->cppDocument()->scopeAt(targetLocation.line(),
- targetLocation.column());
-
- LookupContext cppContext(targetFile->cppDocument(), interface.snapshot());
- ClassOrNamespace *cppCoN = cppContext.lookupType(scopeAtInsertPos);
- if (!cppCoN)
- cppCoN = cppContext.globalNamespace();
- SubstitutionEnvironment env;
- env.setContext(interface.context());
- env.switchScope(symbol->enclosingScope());
- UseMinimalNames q(cppCoN);
- env.enter(&q);
- Control *control = interface.context().bindings()->control().get();
- Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
- return oo.prettyName(LookupContext::minimalName(symbol, cppCoN, control));
-}
-
-FullySpecifiedType typeAtDifferentLocation(const CppQuickFixInterface &interface,
- FullySpecifiedType type,
- Scope *originalScope,
- const CppRefactoringFilePtr &targetFile,
- InsertionLocation targetLocation,
- const QStringList &newNamespaceNamesAtLoc = {})
-{
- Scope *scopeAtInsertPos = targetFile->cppDocument()->scopeAt(targetLocation.line(),
- targetLocation.column());
- for (const QString &nsName : newNamespaceNamesAtLoc) {
- const QByteArray utf8Name = nsName.toUtf8();
- Control *control = targetFile->cppDocument()->control();
- const Name *name = control->identifier(utf8Name.data(), utf8Name.size());
- Namespace *ns = control->newNamespace(0, name);
- ns->setEnclosingScope(scopeAtInsertPos);
- scopeAtInsertPos = ns;
- }
- LookupContext cppContext(targetFile->cppDocument(), interface.snapshot());
- ClassOrNamespace *cppCoN = cppContext.lookupType(scopeAtInsertPos);
- if (!cppCoN)
- cppCoN = cppContext.globalNamespace();
- SubstitutionEnvironment env;
- env.setContext(interface.context());
- env.switchScope(originalScope);
- UseMinimalNames q(cppCoN);
- env.enter(&q);
- Control *control = interface.context().bindings()->control().get();
- return rewriteType(type, &env, control);
-}
-
-struct ExistingGetterSetterData
-{
- Class *clazz = nullptr;
- Declaration *declarationSymbol = nullptr;
- QString getterName;
- QString setterName;
- QString resetName;
- QString signalName;
- QString qPropertyName;
- QString memberVariableName;
- Document::Ptr doc;
-
- int computePossibleFlags() const;
-};
-
-class GetterSetterRefactoringHelper
-{
-public:
- GetterSetterRefactoringHelper(CppQuickFixOperation *operation,
- const FilePath &filePath,
- Class *clazz)
- : m_operation(operation)
- , m_changes(m_operation->snapshot())
- , m_locator(m_changes)
- , m_headerFile(m_changes.cppFile(filePath))
- , m_sourceFile([&] {
- FilePath cppFilePath = correspondingHeaderOrSource(filePath, &m_isHeaderHeaderFile);
- if (!m_isHeaderHeaderFile || !cppFilePath.exists()) {
- // there is no "source" file
- return m_headerFile;
- } else {
- return m_changes.cppFile(cppFilePath);
- }
- }())
- , m_class(clazz)
- {}
-
- void performGeneration(ExistingGetterSetterData data, int generationFlags);
-
- void applyChanges()
- {
- const auto classLayout = {
- InsertionPointLocator::Public,
- InsertionPointLocator::PublicSlot,
- InsertionPointLocator::Signals,
- InsertionPointLocator::Protected,
- InsertionPointLocator::ProtectedSlot,
- InsertionPointLocator::PrivateSlot,
- InsertionPointLocator::Private,
- };
- for (auto spec : classLayout) {
- const auto iter = m_headerFileCode.find(spec);
- if (iter != m_headerFileCode.end()) {
- const InsertionLocation loc = headerLocationFor(spec);
- m_headerFile->setOpenEditor(true, m_headerFile->position(loc.line(), loc.column()));
- insertAndIndent(m_headerFile, loc, *iter);
- }
- }
- if (!m_sourceFileCode.isEmpty() && m_sourceFileInsertionPoint.isValid()) {
- m_sourceFile->setOpenEditor(true, m_sourceFile->position(
- m_sourceFileInsertionPoint.line(),
- m_sourceFileInsertionPoint.column()));
- insertAndIndent(m_sourceFile, m_sourceFileInsertionPoint, m_sourceFileCode);
- }
-
- if (!m_headerFileChangeSet.isEmpty()) {
- m_headerFile->setChangeSet(m_headerFileChangeSet);
- m_headerFile->apply();
- }
- if (!m_sourceFileChangeSet.isEmpty()) {
- m_sourceFile->setChangeSet(m_sourceFileChangeSet);
- m_sourceFile->apply();
- }
- }
-
- bool hasSourceFile() const { return m_headerFile != m_sourceFile; }
-
-protected:
- void insertAndIndent(const RefactoringFilePtr &file,
- const InsertionLocation &loc,
- const QString &text)
- {
- int targetPosition = file->position(loc.line(), loc.column());
- ChangeSet &changeSet = file == m_headerFile ? m_headerFileChangeSet : m_sourceFileChangeSet;
- changeSet.insert(targetPosition, loc.prefix() + text + loc.suffix());
- }
-
- FullySpecifiedType makeConstRef(FullySpecifiedType type)
- {
- type.setConst(true);
- return m_operation->currentFile()->cppDocument()->control()->referenceType(type, false);
- }
-
- FullySpecifiedType addConstToReference(FullySpecifiedType type)
- {
- if (ReferenceType *ref = type.type()->asReferenceType()) {
- FullySpecifiedType elemType = ref->elementType();
- if (elemType.isConst())
- return type;
- elemType.setConst(true);
- return m_operation->currentFile()->cppDocument()->control()->referenceType(elemType,
- false);
- }
- return type;
- }
-
- QString symbolAt(Symbol *symbol,
- const CppRefactoringFilePtr &targetFile,
- InsertionLocation targetLocation)
- {
- return symbolAtDifferentLocation(*m_operation, symbol, targetFile, targetLocation);
- }
-
- FullySpecifiedType typeAt(FullySpecifiedType type,
- Scope *originalScope,
- const CppRefactoringFilePtr &targetFile,
- InsertionLocation targetLocation,
- const QStringList &newNamespaceNamesAtLoc = {})
- {
- return typeAtDifferentLocation(*m_operation,
- type,
- originalScope,
- targetFile,
- targetLocation,
- newNamespaceNamesAtLoc);
- }
-
- /**
- * @brief checks if the type in the enclosing scope in the header is a value type
- * @param type a type in the m_headerFile
- * @param enclosingScope the enclosing scope
- * @param customValueType if not nullptr set to true when value type comes
- * from CppQuickFixSettings::isValueType
- * @return true if it is a pointer, enum, integer, floating point, reference, custom value type
- */
- bool isValueType(FullySpecifiedType type, Scope *enclosingScope, bool *customValueType = nullptr)
- {
- if (customValueType)
- *customValueType = false;
- // a type is a value type if it is one of the following
- const auto isTypeValueType = [](const FullySpecifiedType &t) {
- return t->asPointerType() || t->asEnumType() || t->asIntegerType() || t->asFloatType()
- || t->asReferenceType();
- };
- if (type->asNamedType()) {
- // we need a recursive search and a lookup context
- LookupContext context(m_headerFile->cppDocument(), m_changes.snapshot());
- auto isValueType = [settings = m_settings,
- &customValueType,
- &context,
- &isTypeValueType](const Name *name,
- Scope *scope,
- auto &isValueType) {
- // maybe the type is a custom value type by name
- if (const Identifier *id = name->identifier()) {
- if (settings->isValueType(QString::fromUtf8(id->chars(), id->size()))) {
- if (customValueType)
- *customValueType = true;
- return true;
- }
- }
- // search for the type declaration
- QList<LookupItem> localLookup = context.lookup(name, scope);
- for (auto &&i : localLookup) {
- if (isTypeValueType(i.type()))
- return true;
- if (i.type()->asNamedType()) { // check if we have to search recursively
- const Name *newName = i.type()->asNamedType()->name();
- Scope *newScope = i.declaration()->enclosingScope();
- if (Matcher::match(newName, name)
- && Matcher::match(newScope->name(), scope->name())) {
- continue; // we have found the start location of the search
- }
- return isValueType(newName, newScope, isValueType);
- }
- return false;
- }
- return false;
- };
- // start recursion
- return isValueType(type->asNamedType()->name(), enclosingScope, isValueType);
- }
- return isTypeValueType(type);
- }
-
- bool isValueType(Symbol *symbol, bool *customValueType = nullptr)
- {
- return isValueType(symbol->type(), symbol->enclosingScope(), customValueType);
- }
-
- void addHeaderCode(InsertionPointLocator::AccessSpec spec, QString code)
- {
- QString &existing = m_headerFileCode[spec];
- existing += code;
- if (!existing.endsWith('\n'))
- existing += '\n';
- }
-
- InsertionLocation headerLocationFor(InsertionPointLocator::AccessSpec spec)
- {
- const auto insertionPoint = m_headerInsertionPoints.find(spec);
- if (insertionPoint != m_headerInsertionPoints.end())
- return *insertionPoint;
- const InsertionLocation loc = m_locator.methodDeclarationInClass(
- m_headerFile->filePath(), m_class, spec,
- InsertionPointLocator::ForceAccessSpec::Yes);
- m_headerInsertionPoints.insert(spec, loc);
- return loc;
- }
-
- InsertionLocation sourceLocationFor(Symbol *symbol, QStringList *insertedNamespaces = nullptr)
- {
- if (m_sourceFileInsertionPoint.isValid())
- return m_sourceFileInsertionPoint;
- m_sourceFileInsertionPoint
- = insertLocationForMethodDefinition(symbol,
- false,
- m_settings->createMissingNamespacesinCppFile()
- ? NamespaceHandling::CreateMissing
- : NamespaceHandling::Ignore,
- m_changes,
- m_sourceFile->filePath(),
- insertedNamespaces);
- if (m_settings->addUsingNamespaceinCppFile()) {
- // check if we have to insert a using namespace ...
- auto requiredNamespaces = getNamespaceNames(
- symbol->asClass() ? symbol : symbol->enclosingClass());
- NSCheckerVisitor visitor(m_sourceFile.get(),
- requiredNamespaces,
- m_sourceFile->position(m_sourceFileInsertionPoint.line(),
- m_sourceFileInsertionPoint.column()));
- visitor.accept(m_sourceFile->cppDocument()->translationUnit()->ast());
- if (insertedNamespaces)
- insertedNamespaces->clear();
- if (auto rns = visitor.remainingNamespaces(); !rns.empty()) {
- QString ns = "using namespace ";
- for (auto &n : rns) {
- if (!n.isEmpty()) { // we have to ignore unnamed namespaces
- ns += n;
- ns += "::";
- if (insertedNamespaces)
- insertedNamespaces->append(n);
- }
- }
- ns.resize(ns.size() - 2); // remove last '::'
- ns += ";\n";
- const auto &loc = m_sourceFileInsertionPoint;
- m_sourceFileInsertionPoint = InsertionLocation(loc.filePath(),
- loc.prefix() + ns,
- loc.suffix(),
- loc.line(),
- loc.column());
- }
- }
- return m_sourceFileInsertionPoint;
- }
-
- void addSourceFileCode(QString code)
- {
- while (!m_sourceFileCode.isEmpty() && !m_sourceFileCode.endsWith("\n\n"))
- m_sourceFileCode += '\n';
- m_sourceFileCode += code;
- }
-
-protected:
- CppQuickFixOperation *const m_operation;
- const CppRefactoringChanges m_changes;
- const InsertionPointLocator m_locator;
- const CppRefactoringFilePtr m_headerFile;
- bool m_isHeaderHeaderFile = false; // the "header" (where the class is defined) can be a source file
- const CppRefactoringFilePtr m_sourceFile;
- CppQuickFixSettings *const m_settings = CppQuickFixProjectsSettings::getQuickFixSettings(
- ProjectExplorer::ProjectTree::currentProject());
- Class *const m_class;
-
-private:
- ChangeSet m_headerFileChangeSet;
- ChangeSet m_sourceFileChangeSet;
- QMap<InsertionPointLocator::AccessSpec, InsertionLocation> m_headerInsertionPoints;
- InsertionLocation m_sourceFileInsertionPoint;
- QString m_sourceFileCode;
- QMap<InsertionPointLocator::AccessSpec, QString> m_headerFileCode;
-};
-
-class GenerateGetterSetterOp : public CppQuickFixOperation
-{
-public:
- enum GenerateFlag {
- GenerateGetter = 1 << 0,
- GenerateSetter = 1 << 1,
- GenerateSignal = 1 << 2,
- GenerateMemberVariable = 1 << 3,
- GenerateReset = 1 << 4,
- GenerateProperty = 1 << 5,
- GenerateConstantProperty = 1 << 6,
- HaveExistingQProperty = 1 << 7,
- Invalid = -1,
- };
-
- GenerateGetterSetterOp(const CppQuickFixInterface &interface,
- ExistingGetterSetterData data,
- int generateFlags,
- int priority,
- const QString &description)
- : CppQuickFixOperation(interface)
- , m_generateFlags(generateFlags)
- , m_data(data)
- {
- setDescription(description);
- setPriority(priority);
- }
-
- static void generateQuickFixes(QuickFixOperations &results,
- const CppQuickFixInterface &interface,
- const ExistingGetterSetterData &data,
- const int possibleFlags)
- {
- // flags can have the value HaveExistingQProperty or a combination of all other values
- // of the enum 'GenerateFlag'
- int p = 0;
- if (possibleFlags & HaveExistingQProperty) {
- const QString desc = Tr::tr("Generate Missing Q_PROPERTY Members");
- results << new GenerateGetterSetterOp(interface, data, possibleFlags, ++p, desc);
- } else {
- if (possibleFlags & GenerateSetter) {
- const QString desc = Tr::tr("Generate Setter");
- results << new GenerateGetterSetterOp(interface, data, GenerateSetter, ++p, desc);
- }
- if (possibleFlags & GenerateGetter) {
- const QString desc = Tr::tr("Generate Getter");
- results << new GenerateGetterSetterOp(interface, data, GenerateGetter, ++p, desc);
- }
- if (possibleFlags & GenerateGetter && possibleFlags & GenerateSetter) {
- const QString desc = Tr::tr("Generate Getter and Setter");
- const int flags = GenerateGetter | GenerateSetter;
- results << new GenerateGetterSetterOp(interface, data, flags, ++p, desc);
- }
-
- if (possibleFlags & GenerateConstantProperty) {
- const QString desc = Tr::tr("Generate Constant Q_PROPERTY and Missing Members");
- const int flags = possibleFlags & ~(GenerateSetter | GenerateSignal | GenerateReset);
- results << new GenerateGetterSetterOp(interface, data, flags, ++p, desc);
- }
- if (possibleFlags & GenerateProperty) {
- if (possibleFlags & GenerateReset) {
- const QString desc = Tr::tr(
- "Generate Q_PROPERTY and Missing Members with Reset Function");
- const int flags = possibleFlags & ~GenerateConstantProperty;
- results << new GenerateGetterSetterOp(interface, data, flags, ++p, desc);
- }
- const QString desc = Tr::tr("Generate Q_PROPERTY and Missing Members");
- const int flags = possibleFlags & ~GenerateConstantProperty & ~GenerateReset;
- results << new GenerateGetterSetterOp(interface, data, flags, ++p, desc);
- }
- }
- }
-
- void perform() override
- {
- GetterSetterRefactoringHelper helper(this, currentFile()->filePath(), m_data.clazz);
- helper.performGeneration(m_data, m_generateFlags);
- helper.applyChanges();
- }
-
-private:
- int m_generateFlags;
- ExistingGetterSetterData m_data;
-};
-
-int ExistingGetterSetterData::computePossibleFlags() const
-{
- const bool isConst = declarationSymbol->type().isConst();
- const bool isStatic = declarationSymbol->type().isStatic();
- using Flag = GenerateGetterSetterOp::GenerateFlag;
- int generateFlags = 0;
- if (getterName.isEmpty())
- generateFlags |= Flag::GenerateGetter;
- if (!isConst) {
- if (resetName.isEmpty())
- generateFlags |= Flag::GenerateReset;
- if (!isStatic && signalName.isEmpty() && setterName.isEmpty())
- generateFlags |= Flag::GenerateSignal;
- if (setterName.isEmpty())
- generateFlags |= Flag::GenerateSetter;
- }
- if (!isStatic) {
- const bool hasSignal = !signalName.isEmpty() || generateFlags & Flag::GenerateSignal;
- if (!isConst && hasSignal)
- generateFlags |= Flag::GenerateProperty;
- }
- if (setterName.isEmpty() && signalName.isEmpty())
- generateFlags |= Flag::GenerateConstantProperty;
- return generateFlags;
-}
-
-void GetterSetterRefactoringHelper::performGeneration(ExistingGetterSetterData data, int generateFlags)
-{
- using Flag = GenerateGetterSetterOp::GenerateFlag;
-
- if (generateFlags & Flag::GenerateGetter && data.getterName.isEmpty()) {
- data.getterName = m_settings->getGetterName(data.qPropertyName);
- if (data.getterName == data.memberVariableName) {
- data.getterName = "get" + data.memberVariableName.left(1).toUpper()
- + data.memberVariableName.mid(1);
- }
- }
- if (generateFlags & Flag::GenerateSetter && data.setterName.isEmpty())
- data.setterName = m_settings->getSetterName(data.qPropertyName);
- if (generateFlags & Flag::GenerateSignal && data.signalName.isEmpty())
- data.signalName = m_settings->getSignalName(data.qPropertyName);
- if (generateFlags & Flag::GenerateReset && data.resetName.isEmpty())
- data.resetName = m_settings->getResetName(data.qPropertyName);
-
- FullySpecifiedType memberVariableType = data.declarationSymbol->type();
- memberVariableType.setConst(false);
- const bool isMemberVariableStatic = memberVariableType.isStatic();
- memberVariableType.setStatic(false);
- Overview overview = CppCodeStyleSettings::currentProjectCodeStyleOverview();
- overview.showTemplateParameters = false;
- // TODO does not work with using. e.g. 'using foo = std::unique_ptr<int>'
- // TODO must be fully qualified
- auto getSetTemplate = m_settings->findGetterSetterTemplate(overview.prettyType(memberVariableType));
- overview.showTemplateParameters = true;
-
- // Ok... - If a type is a Named type we have to search recusive for the real type
- const bool isValueType = this->isValueType(memberVariableType,
- data.declarationSymbol->enclosingScope());
- const FullySpecifiedType parameterType = isValueType ? memberVariableType
- : makeConstRef(memberVariableType);
-
- QString baseName = memberBaseName(data.memberVariableName);
- if (baseName.isEmpty())
- baseName = data.memberVariableName;
-
- const QString parameterName = m_settings->getSetterParameterName(baseName);
- if (parameterName == data.memberVariableName)
- data.memberVariableName = "this->" + data.memberVariableName;
-
- getSetTemplate.replacePlaceholders(data.memberVariableName, parameterName);
-
- using Pattern = CppQuickFixSettings::GetterSetterTemplate;
- std::optional<FullySpecifiedType> returnTypeTemplateParameter;
- if (getSetTemplate.returnTypeTemplate.has_value()) {
- QString returnTypeTemplate = getSetTemplate.returnTypeTemplate.value();
- if (returnTypeTemplate.contains(Pattern::TEMPLATE_PARAMETER_PATTERN)) {
- returnTypeTemplateParameter = getFirstTemplateParameter(data.declarationSymbol->type());
- if (!returnTypeTemplateParameter.has_value())
- return; // Maybe report error to the user
- }
- }
- const FullySpecifiedType returnTypeHeader = [&] {
- if (!getSetTemplate.returnTypeTemplate.has_value())
- return m_settings->returnByConstRef ? parameterType : memberVariableType;
- QString typeTemplate = getSetTemplate.returnTypeTemplate.value();
- if (returnTypeTemplateParameter.has_value())
- typeTemplate.replace(Pattern::TEMPLATE_PARAMETER_PATTERN,
- overview.prettyType(returnTypeTemplateParameter.value()));
- if (typeTemplate.contains(Pattern::TYPE_PATTERN))
- typeTemplate.replace(Pattern::TYPE_PATTERN,
- overview.prettyType(data.declarationSymbol->type()));
- Control *control = m_operation->currentFile()->cppDocument()->control();
- std::string utf8TypeName = typeTemplate.toUtf8().toStdString();
- return FullySpecifiedType(control->namedType(control->identifier(utf8TypeName.c_str())));
- }();
-
- // getter declaration
- if (generateFlags & Flag::GenerateGetter) {
- // maybe we added 'this->' to memberVariableName because of a collision with parameterName
- // but here the 'this->' is not needed
- const QString returnExpression = QString{getSetTemplate.returnExpression}.replace("this->",
- "");
- QString getterInClassDeclaration = overview.prettyType(returnTypeHeader, data.getterName)
- + QLatin1String("()");
- if (isMemberVariableStatic)
- getterInClassDeclaration.prepend(QLatin1String("static "));
- else
- getterInClassDeclaration += QLatin1String(" const");
- getterInClassDeclaration.prepend(m_settings->getterAttributes + QLatin1Char(' '));
-
- auto getterLocation = m_settings->determineGetterLocation(1);
- // if we have an anonymous class we must add code inside the class
- if (data.clazz->name()->asAnonymousNameId())
- getterLocation = CppQuickFixSettings::FunctionLocation::InsideClass;
-
- if (getterLocation == CppQuickFixSettings::FunctionLocation::InsideClass) {
- getterInClassDeclaration += QLatin1String("\n{\nreturn ") + returnExpression
- + QLatin1String(";\n}\n");
- } else {
- getterInClassDeclaration += QLatin1String(";\n");
- }
- addHeaderCode(InsertionPointLocator::Public, getterInClassDeclaration);
- if (getterLocation == CppQuickFixSettings::FunctionLocation::CppFile && !hasSourceFile())
- getterLocation = CppQuickFixSettings::FunctionLocation::OutsideClass;
-
- if (getterLocation != CppQuickFixSettings::FunctionLocation::InsideClass) {
- const auto getReturnTypeAt = [&](CppRefactoringFilePtr targetFile,
- InsertionLocation targetLoc) {
- if (getSetTemplate.returnTypeTemplate.has_value()) {
- QString returnType = getSetTemplate.returnTypeTemplate.value();
- if (returnTypeTemplateParameter.has_value()) {
- const QString templateTypeName = overview.prettyType(typeAt(
- returnTypeTemplateParameter.value(), data.clazz, targetFile, targetLoc));
- returnType.replace(Pattern::TEMPLATE_PARAMETER_PATTERN, templateTypeName);
- }
- if (returnType.contains(Pattern::TYPE_PATTERN)) {
- const QString declarationType = overview.prettyType(
- typeAt(memberVariableType, data.clazz, targetFile, targetLoc));
- returnType.replace(Pattern::TYPE_PATTERN, declarationType);
- }
- Control *control = m_operation->currentFile()->cppDocument()->control();
- std::string utf8String = returnType.toUtf8().toStdString();
- return FullySpecifiedType(
- control->namedType(control->identifier(utf8String.c_str())));
- } else {
- FullySpecifiedType returnType = typeAt(memberVariableType,
- data.clazz,
- targetFile,
- targetLoc);
- if (m_settings->returnByConstRef && !isValueType)
- return makeConstRef(returnType);
- return returnType;
- }
- };
- const QString constSpec = isMemberVariableStatic ? QLatin1String("")
- : QLatin1String(" const");
- if (getterLocation == CppQuickFixSettings::FunctionLocation::CppFile) {
- InsertionLocation loc = sourceLocationFor(data.declarationSymbol);
- FullySpecifiedType returnType;
- QString clazz;
- if (m_settings->rewriteTypesinCppFile()) {
- returnType = getReturnTypeAt(m_sourceFile, loc);
- clazz = symbolAt(data.clazz, m_sourceFile, loc);
- } else {
- returnType = returnTypeHeader;
- const Identifier *identifier = data.clazz->name()->identifier();
- clazz = QString::fromUtf8(identifier->chars(), identifier->size());
- }
- const QString code = overview.prettyType(returnType, clazz + "::" + data.getterName)
- + "()" + constSpec + "\n{\nreturn " + returnExpression + ";\n}";
- addSourceFileCode(code);
- } else if (getterLocation == CppQuickFixSettings::FunctionLocation::OutsideClass) {
- InsertionLocation loc
- = insertLocationForMethodDefinition(data.declarationSymbol,
- false,
- NamespaceHandling::Ignore,
- m_changes,
- m_headerFile->filePath());
- const FullySpecifiedType returnType = getReturnTypeAt(m_headerFile, loc);
- const QString clazz = symbolAt(data.clazz, m_headerFile, loc);
- QString code = overview.prettyType(returnType, clazz + "::" + data.getterName)
- + "()" + constSpec + "\n{\nreturn " + returnExpression + ";\n}";
- if (m_isHeaderHeaderFile)
- code.prepend("inline ");
- insertAndIndent(m_headerFile, loc, code);
- }
- }
- }
-
- // setter declaration
- InsertionPointLocator::AccessSpec setterAccessSpec = InsertionPointLocator::Public;
- if (m_settings->setterAsSlot) {
- const QByteArray connectName = "connect";
- const Identifier connectId(connectName.data(), connectName.size());
- const QList<LookupItem> items = m_operation->context().lookup(&connectId, data.clazz);
- for (const LookupItem &item : items) {
- if (item.declaration() && item.declaration()->enclosingClass()
- && overview.prettyName(item.declaration()->enclosingClass()->name())
- == "QObject") {
- setterAccessSpec = InsertionPointLocator::PublicSlot;
- break;
- }
- }
- }
- const auto createSetterBodyWithSignal = [this, &getSetTemplate, &data] {
- QString body;
- QTextStream setter(&body);
- setter << "if (" << getSetTemplate.equalComparison << ")\nreturn;\n";
-
- setter << getSetTemplate.assignment << ";\n";
- if (m_settings->signalWithNewValue)
- setter << "emit " << data.signalName << "(" << getSetTemplate.returnExpression << ");\n";
- else
- setter << "emit " << data.signalName << "();\n";
-
- return body;
- };
- if (generateFlags & Flag::GenerateSetter) {
- QString headerDeclaration = "void " + data.setterName + '('
- + overview.prettyType(addConstToReference(parameterType),
- parameterName)
- + ")";
- if (isMemberVariableStatic)
- headerDeclaration.prepend("static ");
- QString body = "\n{\n";
- if (data.signalName.isEmpty())
- body += getSetTemplate.assignment + ";\n";
- else
- body += createSetterBodyWithSignal();
-
- body += "}";
-
- auto setterLocation = m_settings->determineSetterLocation(body.count('\n') - 2);
- // if we have an anonymous class we must add code inside the class
- if (data.clazz->name()->asAnonymousNameId())
- setterLocation = CppQuickFixSettings::FunctionLocation::InsideClass;
-
- if (setterLocation == CppQuickFixSettings::FunctionLocation::CppFile && !hasSourceFile())
- setterLocation = CppQuickFixSettings::FunctionLocation::OutsideClass;
-
- if (setterLocation == CppQuickFixSettings::FunctionLocation::InsideClass) {
- headerDeclaration += body;
- } else {
- headerDeclaration += ";\n";
- if (setterLocation == CppQuickFixSettings::FunctionLocation::CppFile) {
- InsertionLocation loc = sourceLocationFor(data.declarationSymbol);
- QString clazz;
- FullySpecifiedType newParameterType = parameterType;
- if (m_settings->rewriteTypesinCppFile()) {
- newParameterType = typeAt(memberVariableType, data.clazz, m_sourceFile, loc);
- if (!isValueType)
- newParameterType = makeConstRef(newParameterType);
- clazz = symbolAt(data.clazz, m_sourceFile, loc);
- } else {
- const Identifier *identifier = data.clazz->name()->identifier();
- clazz = QString::fromUtf8(identifier->chars(), identifier->size());
- }
- newParameterType = addConstToReference(newParameterType);
- const QString code = "void " + clazz + "::" + data.setterName + '('
- + overview.prettyType(newParameterType, parameterName) + ')'
- + body;
- addSourceFileCode(code);
- } else if (setterLocation == CppQuickFixSettings::FunctionLocation::OutsideClass) {
- InsertionLocation loc
- = insertLocationForMethodDefinition(data.declarationSymbol,
- false,
- NamespaceHandling::Ignore,
- m_changes,
- m_headerFile->filePath());
-
- FullySpecifiedType newParameterType = typeAt(data.declarationSymbol->type(),
- data.clazz,
- m_headerFile,
- loc);
- if (!isValueType)
- newParameterType = makeConstRef(newParameterType);
- newParameterType = addConstToReference(newParameterType);
- QString clazz = symbolAt(data.clazz, m_headerFile, loc);
-
- QString code = "void " + clazz + "::" + data.setterName + '('
- + overview.prettyType(newParameterType, parameterName) + ')' + body;
- if (m_isHeaderHeaderFile)
- code.prepend("inline ");
- insertAndIndent(m_headerFile, loc, code);
- }
- }
- addHeaderCode(setterAccessSpec, headerDeclaration);
- }
-
- // reset declaration
- if (generateFlags & Flag::GenerateReset) {
- QString headerDeclaration = "void " + data.resetName + "()";
- if (isMemberVariableStatic)
- headerDeclaration.prepend("static ");
- QString body = "\n{\n";
- if (!data.setterName.isEmpty()) {
- body += data.setterName + "({}); // TODO: Adapt to use your actual default value\n";
- } else {
- body += "static $TYPE defaultValue{}; "
- "// TODO: Adapt to use your actual default value\n";
- if (data.signalName.isEmpty())
- body += getSetTemplate.assignment + ";\n";
- else
- body += createSetterBodyWithSignal();
- }
- body += "}";
-
- // the template use <parameterName> as new value name, but we want to use 'defaultValue'
- body.replace(QRegularExpression("\\b" + parameterName + "\\b"), "defaultValue");
- // body.count('\n') - 2 : do not count the 2 at start
- auto resetLocation = m_settings->determineSetterLocation(body.count('\n') - 2);
- // if we have an anonymous class we must add code inside the class
- if (data.clazz->name()->asAnonymousNameId())
- resetLocation = CppQuickFixSettings::FunctionLocation::InsideClass;
-
- if (resetLocation == CppQuickFixSettings::FunctionLocation::CppFile && !hasSourceFile())
- resetLocation = CppQuickFixSettings::FunctionLocation::OutsideClass;
-
- if (resetLocation == CppQuickFixSettings::FunctionLocation::InsideClass) {
- headerDeclaration += body.replace("$TYPE", overview.prettyType(memberVariableType));
- } else {
- headerDeclaration += ";\n";
- if (resetLocation == CppQuickFixSettings::FunctionLocation::CppFile) {
- const InsertionLocation loc = sourceLocationFor(data.declarationSymbol);
- QString clazz;
- FullySpecifiedType type = memberVariableType;
- if (m_settings->rewriteTypesinCppFile()) {
- type = typeAt(memberVariableType, data.clazz, m_sourceFile, loc);
- clazz = symbolAt(data.clazz, m_sourceFile, loc);
- } else {
- const Identifier *identifier = data.clazz->name()->identifier();
- clazz = QString::fromUtf8(identifier->chars(), identifier->size());
- }
- const QString code = "void " + clazz + "::" + data.resetName + "()"
- + body.replace("$TYPE", overview.prettyType(type));
- addSourceFileCode(code);
- } else if (resetLocation == CppQuickFixSettings::FunctionLocation::OutsideClass) {
- const InsertionLocation loc = insertLocationForMethodDefinition(
- data.declarationSymbol,
- false,
- NamespaceHandling::Ignore,
- m_changes,
- m_headerFile->filePath());
- const FullySpecifiedType type = typeAt(data.declarationSymbol->type(),
- data.clazz,
- m_headerFile,
- loc);
- const QString clazz = symbolAt(data.clazz, m_headerFile, loc);
- QString code = "void " + clazz + "::" + data.resetName + "()"
- + body.replace("$TYPE", overview.prettyType(type));
- if (m_isHeaderHeaderFile)
- code.prepend("inline ");
- insertAndIndent(m_headerFile, loc, code);
- }
- }
- addHeaderCode(setterAccessSpec, headerDeclaration);
- }
-
- // signal declaration
- if (generateFlags & Flag::GenerateSignal) {
- const auto &parameter = overview.prettyType(returnTypeHeader, data.qPropertyName);
- const QString newValue = m_settings->signalWithNewValue ? parameter : QString();
- const QString declaration = QString("void %1(%2);\n").arg(data.signalName, newValue);
- addHeaderCode(InsertionPointLocator::Signals, declaration);
- }
-
- // member variable
- if (generateFlags & Flag::GenerateMemberVariable) {
- QString storageDeclaration = overview.prettyType(memberVariableType, data.memberVariableName);
- if (memberVariableType->asPointerType()
- && m_operation->semanticInfo().doc->translationUnit()->languageFeatures().cxx11Enabled) {
- storageDeclaration.append(" = nullptr");
- }
- storageDeclaration.append(";\n");
- addHeaderCode(InsertionPointLocator::Private, storageDeclaration);
- }
-
- // Q_PROPERTY
- if (generateFlags & Flag::GenerateProperty || generateFlags & Flag::GenerateConstantProperty) {
- // Use the returnTypeHeader as base because of custom types in getSetTemplates.
- // Remove const reference from type.
- FullySpecifiedType type = returnTypeHeader;
- if (ReferenceType *ref = type.type()->asReferenceType())
- type = ref->elementType();
- type.setConst(false);
-
- QString propertyDeclaration = QLatin1String("Q_PROPERTY(")
- + overview.prettyType(type,
- memberBaseName(data.memberVariableName));
- bool needMember = false;
- if (data.getterName.isEmpty())
- needMember = true;
- else
- propertyDeclaration += QLatin1String(" READ ") + data.getterName;
- if (generateFlags & Flag::GenerateConstantProperty) {
- if (needMember)
- propertyDeclaration += QLatin1String(" MEMBER ") + data.memberVariableName;
- propertyDeclaration.append(QLatin1String(" CONSTANT"));
- } else {
- if (data.setterName.isEmpty()) {
- needMember = true;
- } else if (!getSetTemplate.returnTypeTemplate.has_value()) {
- // if the return type of the getter and then Q_PROPERTY is different than
- // the setter type, we should not add WRITE to the Q_PROPERTY
- propertyDeclaration.append(QLatin1String(" WRITE ")).append(data.setterName);
- }
- if (needMember)
- propertyDeclaration += QLatin1String(" MEMBER ") + data.memberVariableName;
- if (!data.resetName.isEmpty())
- propertyDeclaration += QLatin1String(" RESET ") + data.resetName;
- propertyDeclaration.append(QLatin1String(" NOTIFY ")).append(data.signalName);
- }
-
- propertyDeclaration.append(QLatin1String(" FINAL)\n"));
- addHeaderCode(InsertionPointLocator::Private, propertyDeclaration);
- }
-}
-
-QStringList toStringList(const QList<Symbol *> names)
-{
- QStringList list;
- list.reserve(names.size());
- for (const auto symbol : names) {
- const Identifier *const id = symbol->identifier();
- list << QString::fromUtf8(id->chars(), id->size());
- }
- return list;
-}
-
-void findExistingFunctions(ExistingGetterSetterData &existing, QStringList memberFunctionNames)
-{
- const CppQuickFixSettings *settings = CppQuickFixProjectsSettings::getQuickFixSettings(
- ProjectExplorer::ProjectTree::currentProject());
- const QString lowerBaseName = memberBaseName(existing.memberVariableName).toLower();
- const QStringList getterNames{lowerBaseName,
- "get_" + lowerBaseName,
- "get" + lowerBaseName,
- "is_" + lowerBaseName,
- "is" + lowerBaseName,
- settings->getGetterName(lowerBaseName)};
- const QStringList setterNames{"set_" + lowerBaseName,
- "set" + lowerBaseName,
- settings->getSetterName(lowerBaseName)};
- const QStringList resetNames{"reset_" + lowerBaseName,
- "reset" + lowerBaseName,
- settings->getResetName(lowerBaseName)};
- const QStringList signalNames{lowerBaseName + "_changed",
- lowerBaseName + "changed",
- settings->getSignalName(lowerBaseName)};
- for (const auto &memberFunctionName : memberFunctionNames) {
- const QString lowerName = memberFunctionName.toLower();
- if (getterNames.contains(lowerName))
- existing.getterName = memberFunctionName;
- else if (setterNames.contains(lowerName))
- existing.setterName = memberFunctionName;
- else if (resetNames.contains(lowerName))
- existing.resetName = memberFunctionName;
- else if (signalNames.contains(lowerName))
- existing.signalName = memberFunctionName;
- }
-}
-
-QList<Symbol *> getMemberFunctions(const Class *clazz)
-{
- QList<Symbol *> memberFunctions;
- for (auto it = clazz->memberBegin(); it != clazz->memberEnd(); ++it) {
- Symbol *const s = *it;
- if (!s->identifier() || !s->type())
- continue;
- if ((s->asDeclaration() && s->type()->asFunctionType()) || s->asFunction())
- memberFunctions << s;
- }
- return memberFunctions;
-}
-
-} // anonymous namespace
-
-void GenerateGetterSetter::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- ExistingGetterSetterData existing;
-
- const QList<AST *> &path = interface.path();
- // We expect something like
- // [0] TranslationUnitAST
- // [1] NamespaceAST
- // [2] LinkageBodyAST
- // [3] SimpleDeclarationAST
- // [4] ClassSpecifierAST
- // [5] SimpleDeclarationAST
- // [6] DeclaratorAST
- // [7] DeclaratorIdAST
- // [8] SimpleNameAST
-
- const int n = path.size();
- if (n < 6)
- return;
-
- int i = 1;
- const auto variableNameAST = path.at(n - i++)->asSimpleName();
- const auto declaratorId = path.at(n - i++)->asDeclaratorId();
- // DeclaratorAST might be preceded by PointerAST, e.g. for the case
- // "class C { char *@s; };", where '@' denotes the text cursor position.
- auto declarator = path.at(n - i++)->asDeclarator();
- if (!declarator) {
- --i;
- if (path.at(n - i++)->asPointer()) {
- if (n < 7)
- return;
- declarator = path.at(n - i++)->asDeclarator();
- }
- if (!declarator)
- return;
- }
- const auto variableDecl = path.at(n - i++)->asSimpleDeclaration();
- const auto classSpecifier = path.at(n - i++)->asClassSpecifier();
- const auto classDecl = path.at(n - i++)->asSimpleDeclaration();
-
- if (!(variableNameAST && declaratorId && variableDecl && classSpecifier && classDecl))
- return;
-
- // Do not get triggered on member functconstions and arrays
- if (declarator->postfix_declarator_list) {
- return;
- }
-
- // Construct getter and setter names
- const Name *variableName = variableNameAST->name;
- if (!variableName) {
- return;
- }
- const Identifier *variableId = variableName->identifier();
- if (!variableId) {
- return;
- }
- existing.memberVariableName = QString::fromUtf8(variableId->chars(), variableId->size());
-
- // Find the right symbol (for typeName) in the simple declaration
- Symbol *symbol = nullptr;
- const List<Symbol *> *symbols = variableDecl->symbols;
- QTC_ASSERT(symbols, return );
- for (; symbols; symbols = symbols->next) {
- Symbol *s = symbols->value;
- if (const Name *name = s->name()) {
- if (const Identifier *id = name->identifier()) {
- const QString symbolName = QString::fromUtf8(id->chars(), id->size());
- if (symbolName == existing.memberVariableName) {
- symbol = s;
- break;
- }
- }
- }
- }
- if (!symbol) {
- // no type can be determined
- return;
- }
- if (!symbol->asDeclaration()) {
- return;
- }
- existing.declarationSymbol = symbol->asDeclaration();
-
- existing.clazz = classSpecifier->symbol;
- if (!existing.clazz)
- return;
-
- auto file = interface.currentFile();
- // check if a Q_PROPERTY exist
- const QString baseName = memberBaseName(existing.memberVariableName);
- // eg: we have 'int m_test' and now 'Q_PROPERTY(int foo WRITE setTest MEMBER m_test NOTIFY tChanged)'
- for (auto it = classSpecifier->member_specifier_list; it; it = it->next) {
- if (it->value->asQtPropertyDeclaration()) {
- auto propDecl = it->value->asQtPropertyDeclaration();
- // iterator over 'READ ...', ...
- auto p = propDecl->property_declaration_item_list;
- // first check, if we have a MEMBER and the member is equal to the baseName
- for (; p; p = p->next) {
- const char *tokenString = file->tokenAt(p->value->item_name_token).spell();
- if (!qstrcmp(tokenString, "MEMBER")) {
- if (baseName == file->textOf(p->value->expression))
- return;
- }
- }
- // no MEMBER, but maybe the property name is the same
- const QString propertyName = file->textOf(propDecl->property_name);
- // we compare the baseName. e.g. 'test' instead of 'm_test'
- if (propertyName == baseName)
- return; // TODO Maybe offer quick fix "Add missing Q_PROPERTY Members"
- }
- }
-
- findExistingFunctions(existing, toStringList(getMemberFunctions(existing.clazz)));
- existing.qPropertyName = memberBaseName(existing.memberVariableName);
-
- const int possibleFlags = existing.computePossibleFlags();
- GenerateGetterSetterOp::generateQuickFixes(result, interface, existing, possibleFlags);
-}
-
-class MemberInfo
-{
-public:
- MemberInfo(ExistingGetterSetterData data, int possibleFlags)
- : data(data)
- , possibleFlags(possibleFlags)
- {}
-
- ExistingGetterSetterData data;
- int possibleFlags;
- int requestedFlags = 0;
-};
-using GetterSetterCandidates = std::vector<MemberInfo>;
-
-class CandidateTreeItem : public TreeItem
-{
-public:
- enum Column {
- NameColumn,
- GetterColumn,
- SetterColumn,
- SignalColumn,
- ResetColumn,
- QPropertyColumn,
- ConstantQPropertyColumn
- };
- using Flag = GenerateGetterSetterOp::GenerateFlag;
- constexpr static Flag ColumnFlag[] = {
- Flag::Invalid,
- Flag::GenerateGetter,
- Flag::GenerateSetter,
- Flag::GenerateSignal,
- Flag::GenerateReset,
- Flag::GenerateProperty,
- Flag::GenerateConstantProperty,
- };
-
- CandidateTreeItem(MemberInfo *memberInfo)
- : m_memberInfo(memberInfo)
- {}
-
-private:
- QVariant data(int column, int role) const override
- {
- if (role == Qt::DisplayRole && column == NameColumn)
- return m_memberInfo->data.memberVariableName;
- if (role == Qt::CheckStateRole && column > 0
- && column <= static_cast<int>(std::size(ColumnFlag))) {
- return m_memberInfo->requestedFlags & ColumnFlag[column] ? Qt::Checked : Qt::Unchecked;
- }
- return {};
- }
-
- bool setData(int column, const QVariant &data, int role) override
- {
- if (column < 1 || column > static_cast<int>(std::size(ColumnFlag)))
- return false;
- if (role != Qt::CheckStateRole)
- return false;
- if (!(m_memberInfo->possibleFlags & ColumnFlag[column]))
- return false;
- const bool nowChecked = data.toInt() == Qt::Checked;
- if (nowChecked)
- m_memberInfo->requestedFlags |= ColumnFlag[column];
- else
- m_memberInfo->requestedFlags &= ~ColumnFlag[column];
-
- if (nowChecked) {
- if (column == QPropertyColumn) {
- m_memberInfo->requestedFlags |= Flag::GenerateGetter;
- m_memberInfo->requestedFlags |= Flag::GenerateSetter;
- m_memberInfo->requestedFlags |= Flag::GenerateSignal;
- m_memberInfo->requestedFlags &= ~Flag::GenerateConstantProperty;
- } else if (column == ConstantQPropertyColumn) {
- m_memberInfo->requestedFlags |= Flag::GenerateGetter;
- m_memberInfo->requestedFlags &= ~Flag::GenerateSetter;
- m_memberInfo->requestedFlags &= ~Flag::GenerateSignal;
- m_memberInfo->requestedFlags &= ~Flag::GenerateReset;
- m_memberInfo->requestedFlags &= ~Flag::GenerateProperty;
- } else if (column == SetterColumn || column == SignalColumn || column == ResetColumn) {
- m_memberInfo->requestedFlags &= ~Flag::GenerateConstantProperty;
- }
- } else {
- if (column == SignalColumn)
- m_memberInfo->requestedFlags &= ~Flag::GenerateProperty;
- }
- for (int i = 0; i < 16; ++i) {
- const bool allowed = m_memberInfo->possibleFlags & (1 << i);
- if (!allowed)
- m_memberInfo->requestedFlags &= ~(1 << i); // clear bit
- }
- update();
- return true;
- }
-
- Qt::ItemFlags flags(int column) const override
- {
- if (column == NameColumn)
- return Qt::ItemIsEnabled;
- if (column < 1 || column > static_cast<int>(std::size(ColumnFlag)))
- return {};
- if (m_memberInfo->possibleFlags & ColumnFlag[column])
- return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
- return {};
- }
-
- MemberInfo *const m_memberInfo;
-};
-
-class GenerateGettersSettersDialog : public QDialog
-{
- static constexpr CandidateTreeItem::Column CheckBoxColumn[4]
- = {CandidateTreeItem::Column::GetterColumn,
- CandidateTreeItem::Column::SetterColumn,
- CandidateTreeItem::Column::SignalColumn,
- CandidateTreeItem::Column::QPropertyColumn};
-
-public:
- GenerateGettersSettersDialog(const GetterSetterCandidates &candidates)
- : QDialog()
- , m_candidates(candidates)
- {
- using Flags = GenerateGetterSetterOp::GenerateFlag;
- setWindowTitle(Tr::tr("Getters and Setters"));
- const auto model = new TreeModel<TreeItem, CandidateTreeItem>(this);
- model->setHeader(QStringList({
- Tr::tr("Member"),
- Tr::tr("Getter"),
- Tr::tr("Setter"),
- Tr::tr("Signal"),
- Tr::tr("Reset"),
- Tr::tr("QProperty"),
- Tr::tr("Constant QProperty"),
- }));
- for (MemberInfo &candidate : m_candidates)
- model->rootItem()->appendChild(new CandidateTreeItem(&candidate));
- const auto view = new BaseTreeView(this);
- view->setModel(model);
- int optimalWidth = 0;
- for (int i = 0; i < model->columnCount(QModelIndex{}); ++i) {
- view->resizeColumnToContents(i);
- optimalWidth += view->columnWidth(i);
- }
-
- const auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
- connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
- connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
-
- const auto setCheckStateForAll = [model](int column, int checkState) {
- for (int i = 0; i < model->rowCount(); ++i)
- model->setData(model->index(i, column), checkState, Qt::CheckStateRole);
- };
- const auto preventPartiallyChecked = [](QCheckBox *checkbox) {
- if (checkbox->checkState() == Qt::PartiallyChecked)
- checkbox->setCheckState(Qt::Checked);
- };
- using Column = CandidateTreeItem::Column;
- const auto createConnections = [this, setCheckStateForAll, preventPartiallyChecked](
- QCheckBox *checkbox, Column column) {
- connect(checkbox, &QCheckBox::stateChanged, this, [setCheckStateForAll, column](int state) {
- if (state != Qt::PartiallyChecked)
- setCheckStateForAll(column, state);
- });
- connect(checkbox, &QCheckBox::clicked, this, [checkbox, preventPartiallyChecked] {
- preventPartiallyChecked(checkbox);
- });
- };
- std::array<QCheckBox *, 4> checkBoxes = {};
-
- static_assert(std::size(CheckBoxColumn) == checkBoxes.size(),
- "Must contain the same number of elements");
- for (std::size_t i = 0; i < checkBoxes.size(); ++i) {
- if (Utils::anyOf(candidates, [i](const MemberInfo &mi) {
- return mi.possibleFlags & CandidateTreeItem::ColumnFlag[CheckBoxColumn[i]];
- })) {
- const Column column = CheckBoxColumn[i];
- if (column == Column::GetterColumn)
- checkBoxes[i] = new QCheckBox(Tr::tr("Create getters for all members"));
- else if (column == Column::SetterColumn)
- checkBoxes[i] = new QCheckBox(Tr::tr("Create setters for all members"));
- else if (column == Column::SignalColumn)
- checkBoxes[i] = new QCheckBox(Tr::tr("Create signals for all members"));
- else if (column == Column::QPropertyColumn)
- checkBoxes[i] = new QCheckBox(Tr::tr("Create Q_PROPERTY for all members"));
-
- createConnections(checkBoxes[i], column);
- }
- }
- connect(model, &QAbstractItemModel::dataChanged, this, [this, checkBoxes] {
- const auto countExisting = [this](Flags flag) {
- return Utils::count(m_candidates, [flag](const MemberInfo &mi) {
- return !(mi.possibleFlags & flag);
- });
- };
- const auto countRequested = [this](Flags flag) {
- return Utils::count(m_candidates, [flag](const MemberInfo &mi) {
- return mi.requestedFlags & flag;
- });
- };
- const auto countToState = [this](int requestedCount, int alreadyExistsCount) {
- if (requestedCount == 0)
- return Qt::Unchecked;
- if (int(m_candidates.size()) - requestedCount == alreadyExistsCount)
- return Qt::Checked;
- return Qt::PartiallyChecked;
- };
- for (std::size_t i = 0; i < checkBoxes.size(); ++i) {
- if (checkBoxes[i]) {
- const Flags flag = CandidateTreeItem::ColumnFlag[CheckBoxColumn[i]];
- checkBoxes[i]->setCheckState(
- countToState(countRequested(flag), countExisting(flag)));
- }
- }
- });
-
- const auto mainLayout = new QVBoxLayout(this);
- mainLayout->addWidget(new QLabel(Tr::tr("Select the getters and setters "
- "to be created.")));
- for (auto checkBox : checkBoxes) {
- if (checkBox)
- mainLayout->addWidget(checkBox);
- }
- mainLayout->addWidget(view);
- mainLayout->addWidget(buttonBox);
- int left, right;
- mainLayout->getContentsMargins(&left, nullptr, &right, nullptr);
- optimalWidth += left + right;
- resize(optimalWidth, mainLayout->sizeHint().height());
- }
-
- GetterSetterCandidates candidates() const { return m_candidates; }
-
-private:
- GetterSetterCandidates m_candidates;
-};
-
-class GenerateGettersSettersOperation : public CppQuickFixOperation
-{
-public:
- GenerateGettersSettersOperation(const CppQuickFixInterface &interface)
- : CppQuickFixOperation(interface)
- {
- setDescription(Tr::tr("Create Getter and Setter Member Functions"));
-
- m_classAST = astForClassOperations(interface);
- if (!m_classAST)
- return;
- Class * const theClass = m_classAST->symbol;
- if (!theClass)
- return;
-
- // Go through all data members and try to find out whether they have getters and/or setters.
- QList<Symbol *> dataMembers;
- QList<Symbol *> memberFunctions;
- for (auto it = theClass->memberBegin(); it != theClass->memberEnd(); ++it) {
- Symbol *const s = *it;
- if (!s->identifier() || !s->type() || s->type().isTypedef())
- continue;
- if ((s->asDeclaration() && s->type()->asFunctionType()) || s->asFunction())
- memberFunctions << s;
- else if (s->asDeclaration() && (s->isPrivate() || s->isProtected()))
- dataMembers << s;
- }
-
- auto file = interface.currentFile();
- QStringList qPropertyNames; // name after MEMBER or name of the property
- for (auto it = m_classAST->member_specifier_list; it; it = it->next) {
- if (it->value->asQtPropertyDeclaration()) {
- auto propDecl = it->value->asQtPropertyDeclaration();
- // iterator over 'READ ...', ... and check if we have a MEMBER
- for (auto p = propDecl->property_declaration_item_list; p; p = p->next) {
- const char *tokenString = file->tokenAt(p->value->item_name_token).spell();
- if (!qstrcmp(tokenString, "MEMBER"))
- qPropertyNames << file->textOf(p->value->expression);
- }
- // no MEMBER, but maybe the property name is the same
- qPropertyNames << file->textOf(propDecl->property_name);
- }
- }
- const QStringList memberFunctionsAsStrings = toStringList(memberFunctions);
-
- for (Symbol *const member : std::as_const(dataMembers)) {
- ExistingGetterSetterData existing;
- existing.memberVariableName = QString::fromUtf8(member->identifier()->chars(),
- member->identifier()->size());
- existing.declarationSymbol = member->asDeclaration();
- existing.clazz = theClass;
-
- // check if a Q_PROPERTY exist
- const QString baseName = memberBaseName(existing.memberVariableName);
- if (qPropertyNames.contains(baseName)
- || qPropertyNames.contains(existing.memberVariableName))
- continue;
-
- findExistingFunctions(existing, memberFunctionsAsStrings);
- existing.qPropertyName = baseName;
-
- int possibleFlags = existing.computePossibleFlags();
- if (possibleFlags == 0)
- continue;
- m_candidates.emplace_back(existing, possibleFlags);
- }
- }
-
- GetterSetterCandidates candidates() const { return m_candidates; }
- bool isApplicable() const { return !m_candidates.empty(); }
-
- void setGetterSetterData(const GetterSetterCandidates &data)
- {
- m_candidates = data;
- m_hasData = true;
- }
-
-private:
- void perform() override
- {
- if (!m_hasData) {
- GenerateGettersSettersDialog dlg(m_candidates);
- if (dlg.exec() == QDialog::Rejected)
- return;
- m_candidates = dlg.candidates();
- }
- if (m_candidates.empty())
- return;
- GetterSetterRefactoringHelper helper(this,
- currentFile()->filePath(),
- m_candidates.front().data.clazz);
- for (MemberInfo &mi : m_candidates) {
- if (mi.requestedFlags != 0) {
- helper.performGeneration(mi.data, mi.requestedFlags);
- }
- }
- helper.applyChanges();
- }
-
- GetterSetterCandidates m_candidates;
- const ClassSpecifierAST *m_classAST = nullptr;
- bool m_hasData = false;
-};
-
-void GenerateGettersSettersForClass::doMatch(const CppQuickFixInterface &interface,
- QuickFixOperations &result)
-{
- const auto op = QSharedPointer<GenerateGettersSettersOperation>::create(interface);
- if (!op->isApplicable())
- return;
- if (m_test) {
- GetterSetterCandidates candidates = op->candidates();
- for (MemberInfo &mi : candidates) {
- mi.requestedFlags = mi.possibleFlags;
- using Flag = GenerateGetterSetterOp::GenerateFlag;
- mi.requestedFlags &= ~Flag::GenerateConstantProperty;
- }
- op->setGetterSetterData(candidates);
- }
- result << op;
-}
-
-
-namespace {
-
-class ExtractFunctionOptions
-{
-public:
- static bool isValidFunctionName(const QString &name)
- {
- return !name.isEmpty() && isValidIdentifier(name);
- }
-
- bool hasValidFunctionName() const
- {
- return isValidFunctionName(funcName);
- }
-
- QString funcName;
- InsertionPointLocator::AccessSpec access = InsertionPointLocator::Public;
-};
-
-class ExtractFunctionOperation : public CppQuickFixOperation
-{
-public:
- ExtractFunctionOperation(const CppQuickFixInterface &interface,
- int extractionStart,
- int extractionEnd,
- FunctionDefinitionAST *refFuncDef,
- Symbol *funcReturn,
- QList<QPair<QString, QString> > relevantDecls,
- ExtractFunction::FunctionNameGetter functionNameGetter
- = ExtractFunction::FunctionNameGetter())
- : CppQuickFixOperation(interface)
- , m_extractionStart(extractionStart)
- , m_extractionEnd(extractionEnd)
- , m_refFuncDef(refFuncDef)
- , m_funcReturn(funcReturn)
- , m_relevantDecls(relevantDecls)
- , m_functionNameGetter(functionNameGetter)
- {
- setDescription(Tr::tr("Extract Function"));
- }
-
- void perform() override
- {
- QTC_ASSERT(!m_funcReturn || !m_relevantDecls.isEmpty(), return);
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
-
- ExtractFunctionOptions options;
- if (m_functionNameGetter)
- options.funcName = m_functionNameGetter();
- else
- options = getOptions();
-
- if (!options.hasValidFunctionName())
- return;
- const QString &funcName = options.funcName;
-
- Function *refFunc = m_refFuncDef->symbol;
-
- // We don't need to rewrite the type for declarations made inside the reference function,
- // since their scope will remain the same. Then we preserve the original spelling style.
- // However, we must do so for the return type in the definition.
- SubstitutionEnvironment env;
- env.setContext(context());
- env.switchScope(refFunc);
- ClassOrNamespace *targetCoN = context().lookupType(refFunc->enclosingScope());
- if (!targetCoN)
- targetCoN = context().globalNamespace();
- UseMinimalNames subs(targetCoN);
- env.enter(&subs);
-
- Overview printer = CppCodeStyleSettings::currentProjectCodeStyleOverview();
- Control *control = context().bindings()->control().get();
- QString funcDef;
- QString funcDecl; // We generate a declaration only in the case of a member function.
- QString funcCall;
-
- Class *matchingClass = isMemberFunction(context(), refFunc);
-
- // Write return type.
- if (!m_funcReturn) {
- funcDef.append(QLatin1String("void "));
- if (matchingClass)
- funcDecl.append(QLatin1String("void "));
- } else {
- const FullySpecifiedType &fullType = rewriteType(m_funcReturn->type(), &env, control);
- funcDef.append(printer.prettyType(fullType) + QLatin1Char(' '));
- funcDecl.append(printer.prettyType(m_funcReturn->type()) + QLatin1Char(' '));
- }
-
- // Write class qualification, if any.
- if (matchingClass) {
- const Scope *current = matchingClass;
- QVector<const Name *> classes{matchingClass->name()};
- while (current->enclosingScope()->asClass()) {
- current = current->enclosingScope()->asClass();
- classes.prepend(current->name());
- }
- while (current->enclosingScope() && current->enclosingScope()->asNamespace()) {
- current = current->enclosingScope()->asNamespace();
- if (current->name())
- classes.prepend(current->name());
- }
- for (const Name *n : classes) {
- const Name *name = rewriteName(n, &env, control);
- funcDef.append(printer.prettyName(name));
- funcDef.append(QLatin1String("::"));
- }
- }
-
- // Write the extracted function itself and its call.
- funcDef.append(funcName);
- if (matchingClass)
- funcDecl.append(funcName);
- funcCall.append(funcName);
- funcDef.append(QLatin1Char('('));
- if (matchingClass)
- funcDecl.append(QLatin1Char('('));
- funcCall.append(QLatin1Char('('));
- for (int i = m_funcReturn ? 1 : 0; i < m_relevantDecls.length(); ++i) {
- QPair<QString, QString> p = m_relevantDecls.at(i);
- funcCall.append(p.first);
- funcDef.append(p.second);
- if (matchingClass)
- funcDecl.append(p.second);
- if (i < m_relevantDecls.length() - 1) {
- funcCall.append(QLatin1String(", "));
- funcDef.append(QLatin1String(", "));
- if (matchingClass)
- funcDecl.append(QLatin1String(", "));
- }
- }
- funcDef.append(QLatin1Char(')'));
- if (matchingClass)
- funcDecl.append(QLatin1Char(')'));
- funcCall.append(QLatin1Char(')'));
- if (refFunc->isConst()) {
- funcDef.append(QLatin1String(" const"));
- funcDecl.append(QLatin1String(" const"));
- }
- funcDef.append(QLatin1String("\n{\n"));
- QString extract = currentFile->textOf(m_extractionStart, m_extractionEnd);
- extract.replace(QChar::ParagraphSeparator, QLatin1String("\n"));
- if (!extract.endsWith(QLatin1Char('\n')) && m_funcReturn)
- extract.append(QLatin1Char('\n'));
- funcDef.append(extract);
- if (matchingClass)
- funcDecl.append(QLatin1String(";\n"));
- if (m_funcReturn) {
- funcDef.append(QLatin1String("\nreturn ")
- + m_relevantDecls.at(0).first
- + QLatin1Char(';'));
- funcCall.prepend(m_relevantDecls.at(0).second + QLatin1String(" = "));
- }
- funcDef.append(QLatin1String("\n}\n\n"));
- funcDef.replace(QChar::ParagraphSeparator, QLatin1String("\n"));
- funcDef.prepend(inlinePrefix(currentFile->filePath()));
- funcCall.append(QLatin1Char(';'));
-
- // Do not insert right between the function and an associated comment.
- int position = currentFile->startOf(m_refFuncDef);
- const QList<Token> functionDoc = commentsForDeclaration(
- m_refFuncDef->symbol, m_refFuncDef, *currentFile->document(),
- currentFile->cppDocument());
- if (!functionDoc.isEmpty()) {
- position = currentFile->cppDocument()->translationUnit()->getTokenPositionInDocument(
- functionDoc.first(), currentFile->document());
- }
-
- ChangeSet change;
- change.insert(position, funcDef);
- change.replace(m_extractionStart, m_extractionEnd, funcCall);
- currentFile->setChangeSet(change);
- currentFile->apply();
-
- // Write declaration, if necessary.
- if (matchingClass) {
- InsertionPointLocator locator(refactoring);
- const FilePath filePath = FilePath::fromUtf8(matchingClass->fileName());
- const InsertionLocation &location =
- locator.methodDeclarationInClass(filePath, matchingClass, options.access);
- CppRefactoringFilePtr declFile = refactoring.cppFile(filePath);
- change.clear();
- position = declFile->position(location.line(), location.column());
- change.insert(position, location.prefix() + funcDecl + location.suffix());
- declFile->setChangeSet(change);
- declFile->apply();
- }
- }
-
- ExtractFunctionOptions getOptions() const
- {
- QDialog dlg(Core::ICore::dialogParent());
- dlg.setWindowTitle(Tr::tr("Extract Function Refactoring"));
- auto layout = new QFormLayout(&dlg);
-
- auto funcNameEdit = new FancyLineEdit;
- funcNameEdit->setValidationFunction([](FancyLineEdit *edit, QString *) {
- return ExtractFunctionOptions::isValidFunctionName(edit->text());
- });
- layout->addRow(Tr::tr("Function name"), funcNameEdit);
-
- auto accessCombo = new QComboBox;
- accessCombo->addItem(
- InsertionPointLocator::accessSpecToString(InsertionPointLocator::Public),
- InsertionPointLocator::Public);
- accessCombo->addItem(
- InsertionPointLocator::accessSpecToString(InsertionPointLocator::PublicSlot),
- InsertionPointLocator::PublicSlot);
- accessCombo->addItem(
- InsertionPointLocator::accessSpecToString(InsertionPointLocator::Protected),
- InsertionPointLocator::Protected);
- accessCombo->addItem(
- InsertionPointLocator::accessSpecToString(InsertionPointLocator::ProtectedSlot),
- InsertionPointLocator::ProtectedSlot);
- accessCombo->addItem(
- InsertionPointLocator::accessSpecToString(InsertionPointLocator::Private),
- InsertionPointLocator::Private);
- accessCombo->addItem(
- InsertionPointLocator::accessSpecToString(InsertionPointLocator::PrivateSlot),
- InsertionPointLocator::PrivateSlot);
- layout->addRow(Tr::tr("Access"), accessCombo);
-
- auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
- QObject::connect(buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
- QObject::connect(buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
- QPushButton *ok = buttonBox->button(QDialogButtonBox::Ok);
- ok->setEnabled(false);
- QObject::connect(funcNameEdit, &Utils::FancyLineEdit::validChanged,
- ok, &QPushButton::setEnabled);
- layout->addWidget(buttonBox);
-
- if (dlg.exec() == QDialog::Accepted) {
- ExtractFunctionOptions options;
- options.funcName = funcNameEdit->text();
- options.access = static_cast<InsertionPointLocator::AccessSpec>(accessCombo->
- currentData().toInt());
- return options;
- }
- return ExtractFunctionOptions();
- }
-
- int m_extractionStart;
- int m_extractionEnd;
- FunctionDefinitionAST *m_refFuncDef;
- Symbol *m_funcReturn;
- QList<QPair<QString, QString> > m_relevantDecls;
- ExtractFunction::FunctionNameGetter m_functionNameGetter;
-};
-
-QPair<QString, QString> assembleDeclarationData(const QString &specifiers, DeclaratorAST *decltr,
- const CppRefactoringFilePtr &file,
- const Overview &printer)
-{
- QTC_ASSERT(decltr, return (QPair<QString, QString>()));
- if (decltr->core_declarator
- && decltr->core_declarator->asDeclaratorId()
- && decltr->core_declarator->asDeclaratorId()->name) {
- QString decltrText = file->textOf(file->startOf(decltr),
- file->endOf(decltr->core_declarator));
- if (!decltrText.isEmpty()) {
- const QString &name = printer.prettyName(
- decltr->core_declarator->asDeclaratorId()->name->name);
- QString completeDecl = specifiers;
- if (!decltrText.contains(QLatin1Char(' ')))
- completeDecl.append(QLatin1Char(' ') + decltrText);
- else
- completeDecl.append(decltrText);
- return {name, completeDecl};
- }
- }
- return QPair<QString, QString>();
-}
-
-class FunctionExtractionAnalyser : public ASTVisitor
-{
-public:
- FunctionExtractionAnalyser(TranslationUnit *unit,
- const int selStart,
- const int selEnd,
- const CppRefactoringFilePtr &file,
- const Overview &printer)
- : ASTVisitor(unit)
- , m_done(false)
- , m_failed(false)
- , m_selStart(selStart)
- , m_selEnd(selEnd)
- , m_extractionStart(0)
- , m_extractionEnd(0)
- , m_file(file)
- , m_printer(printer)
- {}
-
- bool operator()(FunctionDefinitionAST *refFunDef)
- {
- accept(refFunDef);
-
- if (!m_failed && m_extractionStart == m_extractionEnd)
- m_failed = true;
-
- return !m_failed;
- }
-
- bool preVisit(AST *) override
- {
- return !m_done;
- }
-
- void statement(StatementAST *stmt)
- {
- if (!stmt)
- return;
-
- const int stmtStart = m_file->startOf(stmt);
- const int stmtEnd = m_file->endOf(stmt);
-
- if (stmtStart >= m_selEnd
- || (m_extractionStart && stmtEnd > m_selEnd)) {
- m_done = true;
- return;
- }
-
- if (stmtStart >= m_selStart && !m_extractionStart)
- m_extractionStart = stmtStart;
- if (stmtEnd > m_extractionEnd && m_extractionStart)
- m_extractionEnd = stmtEnd;
-
- accept(stmt);
- }
-
- bool visit(CaseStatementAST *stmt) override
- {
- statement(stmt->statement);
- return false;
- }
-
- bool visit(CompoundStatementAST *stmt) override
- {
- for (StatementListAST *it = stmt->statement_list; it; it = it->next) {
- statement(it->value);
- if (m_done)
- break;
- }
- return false;
- }
-
- bool visit(DoStatementAST *stmt) override
- {
- statement(stmt->statement);
- return false;
- }
-
- bool visit(ForeachStatementAST *stmt) override
- {
- statement(stmt->statement);
- return false;
- }
-
- bool visit(RangeBasedForStatementAST *stmt) override
- {
- statement(stmt->statement);
- return false;
- }
-
- bool visit(ForStatementAST *stmt) override
- {
- statement(stmt->initializer);
- if (!m_done)
- statement(stmt->statement);
- return false;
- }
-
- bool visit(IfStatementAST *stmt) override
- {
- statement(stmt->statement);
- if (!m_done)
- statement(stmt->else_statement);
- return false;
- }
-
- bool visit(TryBlockStatementAST *stmt) override
- {
- statement(stmt->statement);
- for (CatchClauseListAST *it = stmt->catch_clause_list; it; it = it->next) {
- statement(it->value);
- if (m_done)
- break;
- }
- return false;
- }
-
- bool visit(WhileStatementAST *stmt) override
- {
- statement(stmt->statement);
- return false;
- }
-
- bool visit(DeclarationStatementAST *declStmt) override
- {
- // We need to collect the declarations we see before the extraction or even inside it.
- // They might need to be used as either a parameter or return value. Actually, we could
- // still obtain their types from the local uses, but it's good to preserve the original
- // typing style.
- if (declStmt
- && declStmt->declaration
- && declStmt->declaration->asSimpleDeclaration()) {
- SimpleDeclarationAST *simpleDecl = declStmt->declaration->asSimpleDeclaration();
- if (simpleDecl->decl_specifier_list
- && simpleDecl->declarator_list) {
- const QString &specifiers =
- m_file->textOf(m_file->startOf(simpleDecl),
- m_file->endOf(simpleDecl->decl_specifier_list->lastValue()));
- for (DeclaratorListAST *decltrList = simpleDecl->declarator_list;
- decltrList;
- decltrList = decltrList->next) {
- const QPair<QString, QString> p =
- assembleDeclarationData(specifiers, decltrList->value, m_file, m_printer);
- if (!p.first.isEmpty())
- m_knownDecls.insert(p.first, p.second);
- }
- }
- }
-
- return false;
- }
-
- bool visit(ReturnStatementAST *) override
- {
- if (m_extractionStart) {
- m_done = true;
- m_failed = true;
- }
-
- return false;
- }
-
- bool m_done;
- bool m_failed;
- const int m_selStart;
- const int m_selEnd;
- int m_extractionStart;
- int m_extractionEnd;
- QHash<QString, QString> m_knownDecls;
- CppRefactoringFilePtr m_file;
- const Overview &m_printer;
-};
-
-} // anonymous namespace
-
-ExtractFunction::ExtractFunction(FunctionNameGetter functionNameGetter)
- : m_functionNameGetter(functionNameGetter)
-{
-}
-
-void ExtractFunction::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- const CppRefactoringFilePtr file = interface.currentFile();
-
- // TODO: Fix upstream and uncomment; see QTCREATORBUG-28030.
-// if (CppModelManager::usesClangd(file->editor()->textDocument())
-// && file->cppDocument()->languageFeatures().cxxEnabled) {
-// return;
-// }
-
- QTextCursor cursor = file->cursor();
- if (!cursor.hasSelection())
- return;
-
- const QList<AST *> &path = interface.path();
- FunctionDefinitionAST *refFuncDef = nullptr; // The "reference" function, which we will extract from.
- for (int i = path.size() - 1; i >= 0; --i) {
- refFuncDef = path.at(i)->asFunctionDefinition();
- if (refFuncDef)
- break;
- }
-
- if (!refFuncDef
- || !refFuncDef->function_body
- || !refFuncDef->function_body->asCompoundStatement()
- || !refFuncDef->function_body->asCompoundStatement()->statement_list
- || !refFuncDef->symbol
- || !refFuncDef->symbol->name()
- || refFuncDef->symbol->enclosingScope()->asTemplate() /* TODO: Templates... */) {
- return;
- }
-
- // Adjust selection ends.
- int selStart = cursor.selectionStart();
- int selEnd = cursor.selectionEnd();
- if (selStart > selEnd)
- std::swap(selStart, selEnd);
-
- Overview printer;
-
- // Analyze the content to be extracted, which consists of determining the statements
- // which are complete and collecting the declarations seen.
- FunctionExtractionAnalyser analyser(interface.semanticInfo().doc->translationUnit(),
- selStart, selEnd,
- file,
- printer);
- if (!analyser(refFuncDef))
- return;
-
- // We also need to collect the declarations of the parameters from the reference function.
- QSet<QString> refFuncParams;
- if (refFuncDef->declarator->postfix_declarator_list
- && refFuncDef->declarator->postfix_declarator_list->value
- && refFuncDef->declarator->postfix_declarator_list->value->asFunctionDeclarator()) {
- FunctionDeclaratorAST *funcDecltr =
- refFuncDef->declarator->postfix_declarator_list->value->asFunctionDeclarator();
- if (funcDecltr->parameter_declaration_clause
- && funcDecltr->parameter_declaration_clause->parameter_declaration_list) {
- for (ParameterDeclarationListAST *it =
- funcDecltr->parameter_declaration_clause->parameter_declaration_list;
- it;
- it = it->next) {
- ParameterDeclarationAST *paramDecl = it->value->asParameterDeclaration();
- if (paramDecl->declarator) {
- const QString &specifiers =
- file->textOf(file->startOf(paramDecl),
- file->endOf(paramDecl->type_specifier_list->lastValue()));
- const QPair<QString, QString> &p =
- assembleDeclarationData(specifiers, paramDecl->declarator,
- file, printer);
- if (!p.first.isEmpty()) {
- analyser.m_knownDecls.insert(p.first, p.second);
- refFuncParams.insert(p.first);
- }
- }
- }
- }
- }
-
- // Identify what would be parameters for the new function and its return value, if any.
- Symbol *funcReturn = nullptr;
- QList<QPair<QString, QString> > relevantDecls;
- const SemanticInfo::LocalUseMap localUses = interface.semanticInfo().localUses;
- for (auto it = localUses.cbegin(), end = localUses.cend(); it != end; ++it) {
- bool usedBeforeExtraction = false;
- bool usedAfterExtraction = false;
- bool usedInsideExtraction = false;
- const QList<SemanticInfo::Use> &uses = it.value();
- for (const SemanticInfo::Use &use : uses) {
- if (use.isInvalid())
- continue;
-
- const int position = file->position(use.line, use.column);
- if (position < analyser.m_extractionStart)
- usedBeforeExtraction = true;
- else if (position >= analyser.m_extractionEnd)
- usedAfterExtraction = true;
- else
- usedInsideExtraction = true;
- }
-
- const QString &name = printer.prettyName(it.key()->name());
-
- if ((usedBeforeExtraction && usedInsideExtraction)
- || (usedInsideExtraction && refFuncParams.contains(name))) {
- QTC_ASSERT(analyser.m_knownDecls.contains(name), return);
- relevantDecls.push_back({name, analyser.m_knownDecls.value(name)});
- }
-
- // We assume that the first use of a local corresponds to its declaration.
- if (usedInsideExtraction && usedAfterExtraction && !usedBeforeExtraction) {
- if (!funcReturn) {
- QTC_ASSERT(analyser.m_knownDecls.contains(name), return);
- // The return, if any, is stored as the first item in the list.
- relevantDecls.push_front({name, analyser.m_knownDecls.value(name)});
- funcReturn = it.key();
- } else {
- // Would require multiple returns. (Unless we do fancy things, as pointed below.)
- return;
- }
- }
- }
-
- // The current implementation doesn't try to be too smart since it preserves the original form
- // of the declarations. This might be or not the desired effect. An improvement would be to
- // let the user somehow customize the function interface.
- result << new ExtractFunctionOperation(interface,
- analyser.m_extractionStart,
- analyser.m_extractionEnd,
- refFuncDef, funcReturn, relevantDecls,
- m_functionNameGetter);
-}
-
-namespace {
-
-struct ReplaceLiteralsResult
-{
- Token token;
- QString literalText;
-};
-
-template <class T>
-class ReplaceLiterals : private ASTVisitor
-{
-public:
- ReplaceLiterals(const CppRefactoringFilePtr &file, ChangeSet *changes, T *literal)
- : ASTVisitor(file->cppDocument()->translationUnit()), m_file(file), m_changes(changes),
- m_literal(literal)
- {
- m_result.token = m_file->tokenAt(literal->firstToken());
- m_literalTokenText = m_result.token.spell();
- m_result.literalText = QLatin1String(m_literalTokenText);
- if (m_result.token.isCharLiteral()) {
- m_result.literalText.prepend(QLatin1Char('\''));
- m_result.literalText.append(QLatin1Char('\''));
- if (m_result.token.kind() == T_WIDE_CHAR_LITERAL)
- m_result.literalText.prepend(QLatin1Char('L'));
- else if (m_result.token.kind() == T_UTF16_CHAR_LITERAL)
- m_result.literalText.prepend(QLatin1Char('u'));
- else if (m_result.token.kind() == T_UTF32_CHAR_LITERAL)
- m_result.literalText.prepend(QLatin1Char('U'));
- } else if (m_result.token.isStringLiteral()) {
- m_result.literalText.prepend(QLatin1Char('"'));
- m_result.literalText.append(QLatin1Char('"'));
- if (m_result.token.kind() == T_WIDE_STRING_LITERAL)
- m_result.literalText.prepend(QLatin1Char('L'));
- else if (m_result.token.kind() == T_UTF16_STRING_LITERAL)
- m_result.literalText.prepend(QLatin1Char('u'));
- else if (m_result.token.kind() == T_UTF32_STRING_LITERAL)
- m_result.literalText.prepend(QLatin1Char('U'));
- }
- }
-
- ReplaceLiteralsResult apply(AST *ast)
- {
- ast->accept(this);
- return m_result;
- }
-
-private:
- bool visit(T *ast) override
- {
- if (ast != m_literal
- && strcmp(m_file->tokenAt(ast->firstToken()).spell(), m_literalTokenText) != 0) {
- return true;
- }
- int start, end;
- m_file->startAndEndOf(ast->firstToken(), &start, &end);
- m_changes->replace(start, end, QLatin1String("newParameter"));
- return true;
- }
-
- const CppRefactoringFilePtr &m_file;
- ChangeSet *m_changes;
- T *m_literal;
- const char *m_literalTokenText;
- ReplaceLiteralsResult m_result;
-};
-
-class ExtractLiteralAsParameterOp : public CppQuickFixOperation
-{
-public:
- ExtractLiteralAsParameterOp(const CppQuickFixInterface &interface, int priority,
- ExpressionAST *literal, FunctionDefinitionAST *function)
- : CppQuickFixOperation(interface, priority),
- m_literal(literal),
- m_functionDefinition(function)
- {
- setDescription(Tr::tr("Extract Constant as Function Parameter"));
- }
-
- struct FoundDeclaration
- {
- FunctionDeclaratorAST *ast = nullptr;
- CppRefactoringFilePtr file;
- };
-
- FoundDeclaration findDeclaration(const CppRefactoringChanges &refactoring,
- FunctionDefinitionAST *ast)
- {
- FoundDeclaration result;
- Function *func = ast->symbol;
- if (Class *matchingClass = isMemberFunction(context(), func)) {
- // Dealing with member functions
- const QualifiedNameId *qName = func->name()->asQualifiedNameId();
- for (Symbol *s = matchingClass->find(qName->identifier()); s; s = s->next()) {
- if (!s->name()
- || !qName->identifier()->match(s->identifier())
- || !s->type()->asFunctionType()
- || !s->type().match(func->type())
- || s->asFunction()) {
- continue;
- }
-
- const FilePath declFilePath = matchingClass->filePath();
- result.file = refactoring.cppFile(declFilePath);
- ASTPath astPath(result.file->cppDocument());
- const QList<AST *> path = astPath(s->line(), s->column());
- SimpleDeclarationAST *simpleDecl = nullptr;
- for (AST *node : path) {
- simpleDecl = node->asSimpleDeclaration();
- if (simpleDecl) {
- if (simpleDecl->symbols && !simpleDecl->symbols->next) {
- result.ast = functionDeclarator(simpleDecl);
- return result;
- }
- }
- }
-
- if (simpleDecl)
- break;
- }
- } else if (Namespace *matchingNamespace = isNamespaceFunction(context(), func)) {
- // Dealing with free functions and inline member functions.
- bool isHeaderFile;
- FilePath declFilePath = correspondingHeaderOrSource(filePath(), &isHeaderFile);
- if (!declFilePath.exists())
- return FoundDeclaration();
- result.file = refactoring.cppFile(declFilePath);
- if (!result.file)
- return FoundDeclaration();
- const LookupContext lc(result.file->cppDocument(), snapshot());
- const QList<LookupItem> candidates = lc.lookup(func->name(), matchingNamespace);
- for (const LookupItem &candidate : candidates) {
- if (Symbol *s = candidate.declaration()) {
- if (s->asDeclaration()) {
- ASTPath astPath(result.file->cppDocument());
- const QList<AST *> path = astPath(s->line(), s->column());
- for (AST *node : path) {
- SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration();
- if (simpleDecl) {
- result.ast = functionDeclarator(simpleDecl);
- return result;
- }
- }
- }
- }
- }
- }
- return result;
- }
-
- void perform() override
- {
- FunctionDeclaratorAST *functionDeclaratorOfDefinition
- = functionDeclarator(m_functionDefinition);
- const CppRefactoringChanges refactoring(snapshot());
- const CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
- deduceTypeNameOfLiteral(currentFile->cppDocument());
-
- ChangeSet changes;
- if (NumericLiteralAST *concreteLiteral = m_literal->asNumericLiteral()) {
- m_literalInfo = ReplaceLiterals<NumericLiteralAST>(currentFile, &changes,
- concreteLiteral)
- .apply(m_functionDefinition->function_body);
- } else if (StringLiteralAST *concreteLiteral = m_literal->asStringLiteral()) {
- m_literalInfo = ReplaceLiterals<StringLiteralAST>(currentFile, &changes,
- concreteLiteral)
- .apply(m_functionDefinition->function_body);
- } else if (BoolLiteralAST *concreteLiteral = m_literal->asBoolLiteral()) {
- m_literalInfo = ReplaceLiterals<BoolLiteralAST>(currentFile, &changes,
- concreteLiteral)
- .apply(m_functionDefinition->function_body);
- }
- const FoundDeclaration functionDeclaration
- = findDeclaration(refactoring, m_functionDefinition);
- appendFunctionParameter(functionDeclaratorOfDefinition, currentFile, &changes,
- !functionDeclaration.ast);
- if (functionDeclaration.ast) {
- if (currentFile->filePath() != functionDeclaration.file->filePath()) {
- ChangeSet declChanges;
- appendFunctionParameter(functionDeclaration.ast, functionDeclaration.file, &declChanges,
- true);
- functionDeclaration.file->setChangeSet(declChanges);
- functionDeclaration.file->apply();
- } else {
- appendFunctionParameter(functionDeclaration.ast, currentFile, &changes,
- true);
- }
- }
- currentFile->setChangeSet(changes);
- currentFile->apply();
- QTextCursor c = currentFile->cursor();
- c.setPosition(c.position() - parameterName().length());
- editor()->setTextCursor(c);
- editor()->renameSymbolUnderCursor();
- }
-
-private:
- bool hasParameters(FunctionDeclaratorAST *ast) const
- {
- return ast->parameter_declaration_clause
- && ast->parameter_declaration_clause->parameter_declaration_list
- && ast->parameter_declaration_clause->parameter_declaration_list->value;
- }
-
- void deduceTypeNameOfLiteral(const Document::Ptr &document)
- {
- TypeOfExpression typeOfExpression;
- typeOfExpression.init(document, snapshot());
- Overview overview;
- Scope *scope = m_functionDefinition->symbol->enclosingScope();
- const QList<LookupItem> items = typeOfExpression(m_literal, document, scope);
- if (!items.isEmpty())
- m_typeName = overview.prettyType(items.first().type());
- }
-
- static QString parameterName() { return QLatin1String("newParameter"); }
-
- QString parameterDeclarationTextToInsert(FunctionDeclaratorAST *ast) const
- {
- QString str;
- if (hasParameters(ast))
- str = QLatin1String(", ");
- str += m_typeName;
- if (!m_typeName.endsWith(QLatin1Char('*')))
- str += QLatin1Char(' ');
- str += parameterName();
- return str;
- }
-
- FunctionDeclaratorAST *functionDeclarator(SimpleDeclarationAST *ast) const
- {
- for (DeclaratorListAST *decls = ast->declarator_list; decls; decls = decls->next) {
- FunctionDeclaratorAST * const functionDeclaratorAST = functionDeclarator(decls->value);
- if (functionDeclaratorAST)
- return functionDeclaratorAST;
- }
- return nullptr;
- }
-
- FunctionDeclaratorAST *functionDeclarator(DeclaratorAST *ast) const
- {
- for (PostfixDeclaratorListAST *pds = ast->postfix_declarator_list; pds; pds = pds->next) {
- FunctionDeclaratorAST *funcdecl = pds->value->asFunctionDeclarator();
- if (funcdecl)
- return funcdecl;
- }
- return nullptr;
- }
-
- FunctionDeclaratorAST *functionDeclarator(FunctionDefinitionAST *ast) const
- {
- return functionDeclarator(ast->declarator);
- }
-
- void appendFunctionParameter(FunctionDeclaratorAST *ast, const CppRefactoringFileConstPtr &file,
- ChangeSet *changes, bool addDefaultValue)
- {
- if (!ast)
- return;
- if (m_declarationInsertionString.isEmpty())
- m_declarationInsertionString = parameterDeclarationTextToInsert(ast);
- QString insertion = m_declarationInsertionString;
- if (addDefaultValue)
- insertion += QLatin1String(" = ") + m_literalInfo.literalText;
- changes->insert(file->startOf(ast->rparen_token), insertion);
- }
-
- ExpressionAST *m_literal;
- FunctionDefinitionAST *m_functionDefinition;
- QString m_typeName;
- QString m_declarationInsertionString;
- ReplaceLiteralsResult m_literalInfo;
-};
-
-} // anonymous namespace
-
-void ExtractLiteralAsParameter::doMatch(const CppQuickFixInterface &interface,
- QuickFixOperations &result)
-{
- const QList<AST *> &path = interface.path();
- if (path.count() < 2)
- return;
-
- AST * const lastAst = path.last();
- ExpressionAST *literal;
- if (!((literal = lastAst->asNumericLiteral())
- || (literal = lastAst->asStringLiteral())
- || (literal = lastAst->asBoolLiteral()))) {
- return;
- }
-
- FunctionDefinitionAST *function;
- int i = path.count() - 2;
- while (!(function = path.at(i)->asFunctionDefinition())) {
- // Ignore literals in lambda expressions for now.
- if (path.at(i)->asLambdaExpression())
- return;
- if (--i < 0)
- return;
- }
-
- PostfixDeclaratorListAST * const declaratorList = function->declarator->postfix_declarator_list;
- if (!declaratorList)
- return;
- if (FunctionDeclaratorAST *declarator = declaratorList->value->asFunctionDeclarator()) {
- if (declarator->parameter_declaration_clause
- && declarator->parameter_declaration_clause->dot_dot_dot_token) {
- // Do not handle functions with ellipsis parameter.
- return;
- }
- }
-
- const int priority = path.size() - 1;
- result << new ExtractLiteralAsParameterOp(interface, priority, literal, function);
-}
-
-namespace {
-
-class ConvertFromAndToPointerOp : public CppQuickFixOperation
-{
-public:
- enum Mode { FromPointer, FromVariable, FromReference };
-
- ConvertFromAndToPointerOp(const CppQuickFixInterface &interface, int priority, Mode mode,
- bool isAutoDeclaration,
- const SimpleDeclarationAST *simpleDeclaration,
- const DeclaratorAST *declaratorAST,
- const SimpleNameAST *identifierAST,
- Symbol *symbol)
- : CppQuickFixOperation(interface, priority)
- , m_mode(mode)
- , m_isAutoDeclaration(isAutoDeclaration)
- , m_simpleDeclaration(simpleDeclaration)
- , m_declaratorAST(declaratorAST)
- , m_identifierAST(identifierAST)
- , m_symbol(symbol)
- , m_refactoring(snapshot())
- , m_file(m_refactoring.cppFile(filePath()))
- , m_document(interface.semanticInfo().doc)
- {
- setDescription(
- mode == FromPointer
- ? Tr::tr("Convert to Stack Variable")
- : Tr::tr("Convert to Pointer"));
- }
-
- void perform() override
- {
- ChangeSet changes;
-
- switch (m_mode) {
- case FromPointer:
- removePointerOperator(changes);
- convertToStackVariable(changes);
- break;
- case FromReference:
- removeReferenceOperator(changes);
- Q_FALLTHROUGH();
- case FromVariable:
- convertToPointer(changes);
- break;
- }
-
- m_file->setChangeSet(changes);
- m_file->apply();
- }
-
-private:
- void removePointerOperator(ChangeSet &changes) const
- {
- if (!m_declaratorAST->ptr_operator_list)
- return;
- PointerAST *ptrAST = m_declaratorAST->ptr_operator_list->value->asPointer();
- QTC_ASSERT(ptrAST, return);
- const int pos = m_file->startOf(ptrAST->star_token);
- changes.remove(pos, pos + 1);
- }
-
- void removeReferenceOperator(ChangeSet &changes) const
- {
- ReferenceAST *refAST = m_declaratorAST->ptr_operator_list->value->asReference();
- QTC_ASSERT(refAST, return);
- const int pos = m_file->startOf(refAST->reference_token);
- changes.remove(pos, pos + 1);
- }
-
- void removeNewExpression(ChangeSet &changes, NewExpressionAST *newExprAST) const
- {
- ExpressionListAST *exprlist = nullptr;
- if (newExprAST->new_initializer) {
- if (ExpressionListParenAST *ast = newExprAST->new_initializer->asExpressionListParen())
- exprlist = ast->expression_list;
- else if (BracedInitializerAST *ast = newExprAST->new_initializer->asBracedInitializer())
- exprlist = ast->expression_list;
- }
-
- if (exprlist) {
- // remove 'new' keyword and type before initializer
- changes.remove(m_file->startOf(newExprAST->new_token),
- m_file->startOf(newExprAST->new_initializer));
-
- changes.remove(m_file->endOf(m_declaratorAST->equal_token - 1),
- m_file->startOf(m_declaratorAST->equal_token + 1));
- } else {
- // remove the whole new expression
- changes.remove(m_file->endOf(m_identifierAST->firstToken()),
- m_file->startOf(newExprAST->lastToken()));
- }
- }
-
- void removeNewKeyword(ChangeSet &changes, NewExpressionAST *newExprAST) const
- {
- // remove 'new' keyword before initializer
- changes.remove(m_file->startOf(newExprAST->new_token),
- m_file->startOf(newExprAST->new_type_id));
- }
-
- void convertToStackVariable(ChangeSet &changes) const
- {
- // Handle the initializer.
- if (m_declaratorAST->initializer) {
- if (NewExpressionAST *newExpression = m_declaratorAST->initializer->asNewExpression()) {
- if (m_isAutoDeclaration) {
- if (!newExpression->new_initializer)
- changes.insert(m_file->endOf(newExpression), QStringLiteral("()"));
- removeNewKeyword(changes, newExpression);
- } else {
- removeNewExpression(changes, newExpression);
- }
- }
- }
-
- // Fix all occurrences of the identifier in this function.
- ASTPath astPath(m_document);
- const QList<SemanticInfo::Use> uses = semanticInfo().localUses.value(m_symbol);
- for (const SemanticInfo::Use &use : uses) {
- const QList<AST *> path = astPath(use.line, use.column);
- AST *idAST = path.last();
- bool declarationFound = false;
- bool starFound = false;
- int ampersandPos = 0;
- bool memberAccess = false;
- bool deleteCall = false;
-
- for (int i = path.count() - 2; i >= 0; --i) {
- if (path.at(i) == m_declaratorAST) {
- declarationFound = true;
- break;
- }
- if (MemberAccessAST *memberAccessAST = path.at(i)->asMemberAccess()) {
- if (m_file->tokenAt(memberAccessAST->access_token).kind() != T_ARROW)
- continue;
- int pos = m_file->startOf(memberAccessAST->access_token);
- changes.replace(pos, pos + 2, QLatin1String("."));
- memberAccess = true;
- break;
- } else if (DeleteExpressionAST *deleteAST = path.at(i)->asDeleteExpression()) {
- const int pos = m_file->startOf(deleteAST->delete_token);
- changes.insert(pos, QLatin1String("// "));
- deleteCall = true;
- break;
- } else if (UnaryExpressionAST *unaryExprAST = path.at(i)->asUnaryExpression()) {
- const Token tk = m_file->tokenAt(unaryExprAST->unary_op_token);
- if (tk.kind() == T_STAR) {
- if (!starFound) {
- int pos = m_file->startOf(unaryExprAST->unary_op_token);
- changes.remove(pos, pos + 1);
- }
- starFound = true;
- } else if (tk.kind() == T_AMPER) {
- ampersandPos = m_file->startOf(unaryExprAST->unary_op_token);
- }
- } else if (PointerAST *ptrAST = path.at(i)->asPointer()) {
- if (!starFound) {
- const int pos = m_file->startOf(ptrAST->star_token);
- changes.remove(pos, pos);
- }
- starFound = true;
- } else if (path.at(i)->asFunctionDefinition()) {
- break;
- }
- }
- if (!declarationFound && !starFound && !memberAccess && !deleteCall) {
- if (ampersandPos) {
- changes.insert(ampersandPos, QLatin1String("&("));
- changes.insert(m_file->endOf(idAST->firstToken()), QLatin1String(")"));
- } else {
- changes.insert(m_file->startOf(idAST), QLatin1String("&"));
- }
- }
- }
- }
-
- QString typeNameOfDeclaration() const
- {
- if (!m_simpleDeclaration
- || !m_simpleDeclaration->decl_specifier_list
- || !m_simpleDeclaration->decl_specifier_list->value) {
- return QString();
- }
- NamedTypeSpecifierAST *namedType
- = m_simpleDeclaration->decl_specifier_list->value->asNamedTypeSpecifier();
- if (!namedType)
- return QString();
-
- Overview overview;
- return overview.prettyName(namedType->name->name);
- }
-
- void insertNewExpression(ChangeSet &changes, ExpressionAST *ast) const
- {
- const QString typeName = typeNameOfDeclaration();
- if (CallAST *callAST = ast->asCall()) {
- if (typeName.isEmpty()) {
- changes.insert(m_file->startOf(callAST), QLatin1String("new "));
- } else {
- changes.insert(m_file->startOf(callAST),
- QLatin1String("new ") + typeName + QLatin1Char('('));
- changes.insert(m_file->startOf(callAST->lastToken()), QLatin1String(")"));
- }
- } else {
- if (typeName.isEmpty())
- return;
- changes.insert(m_file->startOf(ast), QLatin1String(" = new ") + typeName);
- }
- }
-
- void insertNewExpression(ChangeSet &changes) const
- {
- const QString typeName = typeNameOfDeclaration();
- if (typeName.isEmpty())
- return;
- changes.insert(m_file->endOf(m_identifierAST->firstToken()),
- QLatin1String(" = new ") + typeName);
- }
-
- void convertToPointer(ChangeSet &changes) const
- {
- // Handle initializer.
- if (m_declaratorAST->initializer) {
- if (IdExpressionAST *idExprAST = m_declaratorAST->initializer->asIdExpression()) {
- changes.insert(m_file->startOf(idExprAST), QLatin1String("&"));
- } else if (CallAST *callAST = m_declaratorAST->initializer->asCall()) {
- insertNewExpression(changes, callAST);
- } else if (ExpressionListParenAST *exprListAST = m_declaratorAST->initializer
- ->asExpressionListParen()) {
- insertNewExpression(changes, exprListAST);
- } else if (BracedInitializerAST *bracedInitializerAST = m_declaratorAST->initializer
- ->asBracedInitializer()) {
- insertNewExpression(changes, bracedInitializerAST);
- }
- } else {
- insertNewExpression(changes);
- }
-
- // Fix all occurrences of the identifier in this function.
- ASTPath astPath(m_document);
- const QList<SemanticInfo::Use> uses = semanticInfo().localUses.value(m_symbol);
- for (const SemanticInfo::Use &use : uses) {
- const QList<AST *> path = astPath(use.line, use.column);
- AST *idAST = path.last();
- bool insertStar = true;
- for (int i = path.count() - 2; i >= 0; --i) {
- if (m_isAutoDeclaration && path.at(i) == m_declaratorAST) {
- insertStar = false;
- break;
- }
- if (MemberAccessAST *memberAccessAST = path.at(i)->asMemberAccess()) {
- const int pos = m_file->startOf(memberAccessAST->access_token);
- changes.replace(pos, pos + 1, QLatin1String("->"));
- insertStar = false;
- break;
- } else if (UnaryExpressionAST *unaryExprAST = path.at(i)->asUnaryExpression()) {
- if (m_file->tokenAt(unaryExprAST->unary_op_token).kind() == T_AMPER) {
- const int pos = m_file->startOf(unaryExprAST->unary_op_token);
- changes.remove(pos, pos + 1);
- insertStar = false;
- break;
- }
- } else if (path.at(i)->asFunctionDefinition()) {
- break;
- }
- }
- if (insertStar)
- changes.insert(m_file->startOf(idAST), QLatin1String("*"));
- }
- }
-
- const Mode m_mode;
- const bool m_isAutoDeclaration;
- const SimpleDeclarationAST * const m_simpleDeclaration;
- const DeclaratorAST * const m_declaratorAST;
- const SimpleNameAST * const m_identifierAST;
- Symbol * const m_symbol;
- const CppRefactoringChanges m_refactoring;
- const CppRefactoringFilePtr m_file;
- const Document::Ptr m_document;
-};
-
-} // anonymous namespace
-
-void ConvertFromAndToPointer::doMatch(const CppQuickFixInterface &interface,
- QuickFixOperations &result)
-{
- const QList<AST *> &path = interface.path();
- if (path.count() < 2)
- return;
- SimpleNameAST *identifier = path.last()->asSimpleName();
- if (!identifier)
- return;
- SimpleDeclarationAST *simpleDeclaration = nullptr;
- DeclaratorAST *declarator = nullptr;
- bool isFunctionLocal = false;
- bool isClassLocal = false;
- ConvertFromAndToPointerOp::Mode mode = ConvertFromAndToPointerOp::FromVariable;
- for (int i = path.count() - 2; i >= 0; --i) {
- AST *ast = path.at(i);
- if (!declarator && (declarator = ast->asDeclarator()))
- continue;
- if (!simpleDeclaration && (simpleDeclaration = ast->asSimpleDeclaration()))
- continue;
- if (declarator && simpleDeclaration) {
- if (ast->asClassSpecifier()) {
- isClassLocal = true;
- } else if (ast->asFunctionDefinition() && !isClassLocal) {
- isFunctionLocal = true;
- break;
- }
- }
- }
- if (!isFunctionLocal || !simpleDeclaration || !declarator)
- return;
-
- Symbol *symbol = nullptr;
- for (List<Symbol *> *lst = simpleDeclaration->symbols; lst; lst = lst->next) {
- if (lst->value->name() == identifier->name) {
- symbol = lst->value;
- break;
- }
- }
- if (!symbol)
- return;
-
- bool isAutoDeclaration = false;
- if (symbol->storage() == Symbol::Auto) {
- // For auto variables we must deduce the type from the initializer.
- if (!declarator->initializer)
- return;
-
- isAutoDeclaration = true;
- TypeOfExpression typeOfExpression;
- typeOfExpression.init(interface.semanticInfo().doc, interface.snapshot());
- typeOfExpression.setExpandTemplates(true);
- CppRefactoringFilePtr file = interface.currentFile();
- Scope *scope = file->scopeAt(declarator->firstToken());
- QList<LookupItem> result = typeOfExpression(file->textOf(declarator->initializer).toUtf8(),
- scope, TypeOfExpression::Preprocess);
- if (!result.isEmpty() && result.first().type()->asPointerType())
- mode = ConvertFromAndToPointerOp::FromPointer;
- } else if (declarator->ptr_operator_list) {
- for (PtrOperatorListAST *ops = declarator->ptr_operator_list; ops; ops = ops->next) {
- if (ops != declarator->ptr_operator_list) {
- // Bail out on more complex pointer types (e.g. pointer of pointer,
- // or reference of pointer).
- return;
- }
- if (ops->value->asPointer())
- mode = ConvertFromAndToPointerOp::FromPointer;
- else if (ops->value->asReference())
- mode = ConvertFromAndToPointerOp::FromReference;
- }
- }
-
- const int priority = path.size() - 1;
- result << new ConvertFromAndToPointerOp(interface, priority, mode, isAutoDeclaration,
- simpleDeclaration, declarator, identifier, symbol);
-}
-
-namespace {
-
-void extractNames(const CppRefactoringFilePtr &file,
- QtPropertyDeclarationAST *qtPropertyDeclaration,
- ExistingGetterSetterData &data)
-{
- QtPropertyDeclarationItemListAST *it = qtPropertyDeclaration->property_declaration_item_list;
- for (; it; it = it->next) {
- const char *tokenString = file->tokenAt(it->value->item_name_token).spell();
- if (!qstrcmp(tokenString, "READ")) {
- data.getterName = file->textOf(it->value->expression);
- } else if (!qstrcmp(tokenString, "WRITE")) {
- data.setterName = file->textOf(it->value->expression);
- } else if (!qstrcmp(tokenString, "RESET")) {
- data.resetName = file->textOf(it->value->expression);
- } else if (!qstrcmp(tokenString, "NOTIFY")) {
- data.signalName = file->textOf(it->value->expression);
- } else if (!qstrcmp(tokenString, "MEMBER")) {
- data.memberVariableName = file->textOf(it->value->expression);
- }
- }
-}
-
-} // anonymous namespace
-
-void InsertQtPropertyMembers::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- using Flag = GenerateGetterSetterOp::GenerateFlag;
- ExistingGetterSetterData existing;
- // check for Q_PROPERTY
-
- const QList<AST *> &path = interface.path();
- if (path.isEmpty())
- return;
-
- AST *const ast = path.last();
- QtPropertyDeclarationAST *qtPropertyDeclaration = ast->asQtPropertyDeclaration();
- if (!qtPropertyDeclaration || !qtPropertyDeclaration->type_id)
- return;
-
- ClassSpecifierAST *klass = nullptr;
- for (int i = path.size() - 2; i >= 0; --i) {
- klass = path.at(i)->asClassSpecifier();
- if (klass)
- break;
- }
- if (!klass)
- return;
- existing.clazz = klass->symbol;
-
- CppRefactoringFilePtr file = interface.currentFile();
- const QString propertyName = file->textOf(qtPropertyDeclaration->property_name);
- existing.qPropertyName = propertyName;
- extractNames(file, qtPropertyDeclaration, existing);
-
- Control *control = interface.currentFile()->cppDocument()->control();
-
- existing.declarationSymbol = control->newDeclaration(ast->firstToken(),
- qtPropertyDeclaration->property_name->name);
- existing.declarationSymbol->setVisibility(Symbol::Private);
- existing.declarationSymbol->setEnclosingScope(existing.clazz);
-
- {
- // create a 'right' Type Object
- // if we have Q_PROPERTY(int test ...) then we only get a NamedType for 'int', but we want
- // a IntegerType. So create a new dummy file with a dummy declaration to get the right
- // object
- QByteArray type = file->textOf(qtPropertyDeclaration->type_id).toUtf8();
- QByteArray newSource = file->document()
- ->toPlainText()
- .insert(file->startOf(qtPropertyDeclaration),
- QString::fromUtf8(type + " __dummy;\n"))
- .toUtf8();
-
- Document::Ptr doc = interface.snapshot().preprocessedDocument(newSource, "___quickfix.h");
- if (!doc->parse(Document::ParseTranlationUnit))
- return;
- doc->check();
- class TypeFinder : public ASTVisitor
- {
- public:
- FullySpecifiedType type;
- TypeFinder(TranslationUnit *u)
- : ASTVisitor(u)
- {}
- bool visit(SimpleDeclarationAST *ast) override
- {
- if (ast->symbols && !ast->symbols->next) {
- const Name *name = ast->symbols->value->name();
- if (name && name->asNameId() && name->asNameId()->identifier()) {
- const Identifier *id = name->asNameId()->identifier();
- if (QString::fromUtf8(id->chars(), id->size()) == "__dummy")
- type = ast->symbols->value->type();
- }
- }
- return true;
- }
- };
- TypeFinder finder(doc->translationUnit());
- finder.accept(doc->translationUnit()->ast());
- if (finder.type.type()->isUndefinedType())
- return;
- existing.declarationSymbol->setType(finder.type);
- existing.doc = doc; // to hold type
- }
- // check which methods are already there
- const bool haveFixMemberVariableName = !existing.memberVariableName.isEmpty();
- int generateFlags = Flag::GenerateMemberVariable;
- if (!existing.resetName.isEmpty())
- generateFlags |= Flag::GenerateReset;
- if (!existing.setterName.isEmpty())
- generateFlags |= Flag::GenerateSetter;
- if (!existing.getterName.isEmpty())
- generateFlags |= Flag::GenerateGetter;
- if (!existing.signalName.isEmpty())
- generateFlags |= Flag::GenerateSignal;
- Overview overview;
- for (int i = 0; i < existing.clazz->memberCount(); ++i) {
- Symbol *member = existing.clazz->memberAt(i);
- FullySpecifiedType type = member->type();
- if (member->asFunction() || (type.isValid() && type->asFunctionType())) {
- const QString name = overview.prettyName(member->name());
- if (name == existing.getterName)
- generateFlags &= ~Flag::GenerateGetter;
- else if (name == existing.setterName)
- generateFlags &= ~Flag::GenerateSetter;
- else if (name == existing.resetName)
- generateFlags &= ~Flag::GenerateReset;
- else if (name == existing.signalName)
- generateFlags &= ~Flag::GenerateSignal;
- } else if (member->asDeclaration()) {
- const QString name = overview.prettyName(member->name());
- if (haveFixMemberVariableName) {
- if (name == existing.memberVariableName) {
- generateFlags &= ~Flag::GenerateMemberVariable;
- }
- } else {
- const QString baseName = memberBaseName(name);
- if (existing.qPropertyName == baseName) {
- existing.memberVariableName = name;
- generateFlags &= ~Flag::GenerateMemberVariable;
- }
- }
- }
- }
- if (generateFlags & Flag::GenerateMemberVariable) {
- CppQuickFixSettings *settings = CppQuickFixProjectsSettings::getQuickFixSettings(
- ProjectExplorer::ProjectTree::currentProject());
- existing.memberVariableName = settings->getMemberVariableName(existing.qPropertyName);
- }
- if (generateFlags == 0) {
- // everything is already there
- return;
- }
- generateFlags |= Flag::HaveExistingQProperty;
- GenerateGetterSetterOp::generateQuickFixes(result, interface, existing, generateFlags);
-}
-
-namespace {
-
-class ApplyDeclDefLinkOperation : public CppQuickFixOperation
-{
-public:
- explicit ApplyDeclDefLinkOperation(const CppQuickFixInterface &interface,
- const std::shared_ptr<FunctionDeclDefLink> &link)
- : CppQuickFixOperation(interface, 100)
- , m_link(link)
- {}
-
- void perform() override
- {
- if (editor()->declDefLink() == m_link)
- editor()->applyDeclDefLinkChanges(/*don't jump*/false);
- }
-
-protected:
- virtual void performChanges(const CppRefactoringFilePtr &, const CppRefactoringChanges &)
- { /* never called since perform is overridden */ }
-
-private:
- std::shared_ptr<FunctionDeclDefLink> m_link;
-};
-
-} // anonymous namespace
-
-void ApplyDeclDefLinkChanges::doMatch(const CppQuickFixInterface &interface,
- QuickFixOperations &result)
-{
- std::shared_ptr<FunctionDeclDefLink> link = interface.editor()->declDefLink();
- if (!link || !link->isMarkerVisible())
- return;
-
- auto op = new ApplyDeclDefLinkOperation(interface, link);
- op->setDescription(Tr::tr("Apply Function Signature Changes"));
- result << op;
-}
-
-namespace {
-
-QString definitionSignature(const CppQuickFixInterface *assist,
- FunctionDefinitionAST *functionDefinitionAST,
- CppRefactoringFilePtr &baseFile,
- CppRefactoringFilePtr &targetFile,
- Scope *scope)
-{
- QTC_ASSERT(assist, return QString());
- QTC_ASSERT(functionDefinitionAST, return QString());
- QTC_ASSERT(scope, return QString());
- Function *func = functionDefinitionAST->symbol;
- QTC_ASSERT(func, return QString());
-
- LookupContext cppContext(targetFile->cppDocument(), assist->snapshot());
- ClassOrNamespace *cppCoN = cppContext.lookupType(scope);
- if (!cppCoN)
- cppCoN = cppContext.globalNamespace();
- SubstitutionEnvironment env;
- env.setContext(assist->context());
- env.switchScope(func->enclosingScope());
- UseMinimalNames q(cppCoN);
- env.enter(&q);
- Control *control = assist->context().bindings()->control().get();
- Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
- oo.showFunctionSignatures = true;
- oo.showReturnTypes = true;
- oo.showArgumentNames = true;
- oo.showEnclosingTemplate = true;
- oo.showTemplateParameters = true;
- oo.trailingReturnType = functionDefinitionAST->declarator
- && functionDefinitionAST->declarator->postfix_declarator_list
- && functionDefinitionAST->declarator->postfix_declarator_list->value
- && functionDefinitionAST->declarator->postfix_declarator_list
- ->value->asFunctionDeclarator()
- && functionDefinitionAST->declarator->postfix_declarator_list
- ->value->asFunctionDeclarator()->trailing_return_type;
- const Name *name = func->name();
- if (name && nameIncludesOperatorName(name)) {
- CoreDeclaratorAST *coreDeclarator = functionDefinitionAST->declarator->core_declarator;
- const QString operatorNameText = baseFile->textOf(coreDeclarator);
- oo.includeWhiteSpaceInOperatorName = operatorNameText.contains(QLatin1Char(' '));
- }
- const QString nameText = oo.prettyName(LookupContext::minimalName(func, cppCoN, control));
- oo.showTemplateParameters = false;
- const FullySpecifiedType tn = rewriteType(func->type(), &env, control);
-
- return oo.prettyType(tn, nameText);
-}
-
-class MoveFuncDefRefactoringHelper
-{
-public:
- enum MoveType {
- MoveOutside,
- MoveToCppFile,
- MoveOutsideMemberToCppFile
- };
-
- MoveFuncDefRefactoringHelper(CppQuickFixOperation *operation, MoveType type,
- const FilePath &fromFile, const FilePath &toFile)
- : m_operation(operation), m_type(type), m_changes(m_operation->snapshot())
- {
- m_fromFile = m_changes.cppFile(fromFile);
- m_toFile = (m_type == MoveOutside) ? m_fromFile : m_changes.cppFile(toFile);
- }
-
- void performMove(FunctionDefinitionAST *funcAST)
- {
- // Determine file, insert position and scope
- InsertionLocation l = insertLocationForMethodDefinition(
- funcAST->symbol, false, NamespaceHandling::Ignore,
- m_changes, m_toFile->filePath());
- const QString prefix = l.prefix();
- const QString suffix = l.suffix();
- const int insertPos = m_toFile->position(l.line(), l.column());
- Scope *scopeAtInsertPos = m_toFile->cppDocument()->scopeAt(l.line(), l.column());
-
- // construct definition
- const QString funcDec = inlinePrefix(m_toFile->filePath(), [this] { return m_type == MoveOutside; })
- + definitionSignature(m_operation, funcAST, m_fromFile, m_toFile,
- scopeAtInsertPos);
- QString funcDef = prefix + funcDec;
- const int startPosition = m_fromFile->endOf(funcAST->declarator);
- const int endPosition = m_fromFile->endOf(funcAST);
- funcDef += m_fromFile->textOf(startPosition, endPosition);
- funcDef += suffix;
-
- // insert definition at new position
- m_toFileChangeSet.insert(insertPos, funcDef);
- m_toFile->setOpenEditor(true, insertPos);
-
- // remove definition from fromFile
- if (m_type == MoveOutsideMemberToCppFile) {
- m_fromFileChangeSet.remove(m_fromFile->range(funcAST));
- } else {
- QString textFuncDecl = m_fromFile->textOf(funcAST);
- textFuncDecl.truncate(startPosition - m_fromFile->startOf(funcAST));
- if (textFuncDecl.left(7) == QLatin1String("inline "))
- textFuncDecl = textFuncDecl.mid(7);
- else
- textFuncDecl.replace(" inline ", QLatin1String(" "));
- textFuncDecl = textFuncDecl.trimmed() + QLatin1Char(';');
- m_fromFileChangeSet.replace(m_fromFile->range(funcAST), textFuncDecl);
- }
- }
-
- void applyChanges()
- {
- if (!m_toFileChangeSet.isEmpty()) {
- m_toFile->setChangeSet(m_toFileChangeSet);
- m_toFile->apply();
- }
- if (!m_fromFileChangeSet.isEmpty()) {
- m_fromFile->setChangeSet(m_fromFileChangeSet);
- m_fromFile->apply();
- }
- }
-
-private:
- CppQuickFixOperation *m_operation;
- MoveType m_type;
- CppRefactoringChanges m_changes;
- CppRefactoringFilePtr m_fromFile;
- CppRefactoringFilePtr m_toFile;
- ChangeSet m_fromFileChangeSet;
- ChangeSet m_toFileChangeSet;
-};
-
-class MoveFuncDefOutsideOp : public CppQuickFixOperation
-{
-public:
- MoveFuncDefOutsideOp(const CppQuickFixInterface &interface,
- MoveFuncDefRefactoringHelper::MoveType type,
- FunctionDefinitionAST *funcDef, const FilePath &cppFilePath)
- : CppQuickFixOperation(interface, 0)
- , m_funcDef(funcDef)
- , m_type(type)
- , m_cppFilePath(cppFilePath)
- , m_headerFilePath(funcDef->symbol->filePath())
- {
- if (m_type == MoveFuncDefRefactoringHelper::MoveOutside) {
- setDescription(Tr::tr("Move Definition Outside Class"));
- } else {
- const FilePath resolved = m_cppFilePath.relativePathFrom(m_headerFilePath.parentDir());
- setDescription(Tr::tr("Move Definition to %1").arg(resolved.displayName()));
- }
- }
-
- void perform() override
- {
- MoveFuncDefRefactoringHelper helper(this, m_type, m_headerFilePath, m_cppFilePath);
- helper.performMove(m_funcDef);
- helper.applyChanges();
- }
-
-private:
- FunctionDefinitionAST *m_funcDef;
- MoveFuncDefRefactoringHelper::MoveType m_type;
- const FilePath m_cppFilePath;
- const FilePath m_headerFilePath;
-};
-
-} // anonymous namespace
-
-void MoveFuncDefOutside::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- const QList<AST *> &path = interface.path();
- SimpleDeclarationAST *classAST = nullptr;
- FunctionDefinitionAST *funcAST = nullptr;
- bool moveOutsideMemberDefinition = false;
-
- const int pathSize = path.size();
- for (int idx = 1; idx < pathSize; ++idx) {
- if ((funcAST = path.at(idx)->asFunctionDefinition())) {
- // check cursor position
- if (idx != pathSize - 1 // Do not allow "void a() @ {..."
- && funcAST->function_body
- && !interface.isCursorOn(funcAST->function_body)) {
- if (path.at(idx - 1)->asTranslationUnit()) { // normal function
- if (idx + 3 < pathSize && path.at(idx + 3)->asQualifiedName()) // Outside member
- moveOutsideMemberDefinition = true; // definition
- break;
- }
-
- if (idx > 1) {
- if ((classAST = path.at(idx - 2)->asSimpleDeclaration())) // member function
- break;
- if (path.at(idx - 2)->asNamespace()) // normal function in namespace
- break;
- }
- if (idx > 2 && path.at(idx - 1)->asTemplateDeclaration()) {
- if ((classAST = path.at(idx - 3)->asSimpleDeclaration())) // member template
- break;
- }
- }
- funcAST = nullptr;
- }
- }
-
- if (!funcAST || !funcAST->symbol)
- return;
-
- bool isHeaderFile = false;
- const FilePath cppFileName = correspondingHeaderOrSource(interface.filePath(), &isHeaderFile);
-
- if (isHeaderFile && !cppFileName.isEmpty()) {
- const MoveFuncDefRefactoringHelper::MoveType type = moveOutsideMemberDefinition
- ? MoveFuncDefRefactoringHelper::MoveOutsideMemberToCppFile
- : MoveFuncDefRefactoringHelper::MoveToCppFile;
- result << new MoveFuncDefOutsideOp(interface, type, funcAST, cppFileName);
- }
-
- if (classAST)
- result << new MoveFuncDefOutsideOp(interface, MoveFuncDefRefactoringHelper::MoveOutside,
- funcAST, FilePath());
-
- return;
-}
-
-namespace {
-
-class MoveAllFuncDefOutsideOp : public CppQuickFixOperation
-{
-public:
- MoveAllFuncDefOutsideOp(const CppQuickFixInterface &interface,
- MoveFuncDefRefactoringHelper::MoveType type,
- ClassSpecifierAST *classDef, const FilePath &cppFileName)
- : CppQuickFixOperation(interface, 0)
- , m_type(type)
- , m_classDef(classDef)
- , m_cppFilePath(cppFileName)
- , m_headerFilePath(classDef->symbol->filePath())
- {
- if (m_type == MoveFuncDefRefactoringHelper::MoveOutside) {
- setDescription(Tr::tr("Definitions Outside Class"));
- } else {
- const FilePath resolved = m_cppFilePath.relativePathFrom(m_headerFilePath.parentDir());
- setDescription(Tr::tr("Move All Function Definitions to %1")
- .arg(resolved.displayName()));
- }
- }
-
- void perform() override
- {
- MoveFuncDefRefactoringHelper helper(this, m_type, m_headerFilePath, m_cppFilePath);
- for (DeclarationListAST *it = m_classDef->member_specifier_list; it; it = it->next) {
- if (FunctionDefinitionAST *funcAST = it->value->asFunctionDefinition()) {
- if (funcAST->symbol && !funcAST->symbol->isGenerated())
- helper.performMove(funcAST);
- }
- }
- helper.applyChanges();
- }
-
-private:
- MoveFuncDefRefactoringHelper::MoveType m_type;
- ClassSpecifierAST *m_classDef;
- const FilePath m_cppFilePath;
- const FilePath m_headerFilePath;
-};
-
-} // anonymous namespace
-
-void MoveAllFuncDefOutside::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- ClassSpecifierAST * const classAST = astForClassOperations(interface);
- if (!classAST)
- return;
-
- // Determine if the class has at least one function definition
- bool classContainsFunctions = false;
- for (DeclarationListAST *it = classAST->member_specifier_list; it; it = it->next) {
- if (FunctionDefinitionAST *funcAST = it->value->asFunctionDefinition()) {
- if (funcAST->symbol && !funcAST->symbol->isGenerated()) {
- classContainsFunctions = true;
- break;
- }
- }
- }
- if (!classContainsFunctions)
- return;
-
- bool isHeaderFile = false;
- const FilePath cppFileName = correspondingHeaderOrSource(interface.filePath(), &isHeaderFile);
- if (isHeaderFile && !cppFileName.isEmpty()) {
- result << new MoveAllFuncDefOutsideOp(interface,
- MoveFuncDefRefactoringHelper::MoveToCppFile,
- classAST, cppFileName);
- }
- result << new MoveAllFuncDefOutsideOp(interface, MoveFuncDefRefactoringHelper::MoveOutside,
- classAST, FilePath());
-}
-
-namespace {
-
-class MoveFuncDefToDeclOp : public CppQuickFixOperation
-{
-public:
- enum Type { Push, Pull };
- MoveFuncDefToDeclOp(const CppQuickFixInterface &interface,
- const FilePath &fromFilePath, const FilePath &toFilePath,
- FunctionDefinitionAST *funcAst, Function *func, const QString &declText,
- const ChangeSet::Range &fromRange,
- const ChangeSet::Range &toRange,
- Type type)
- : CppQuickFixOperation(interface, 0)
- , m_fromFilePath(fromFilePath)
- , m_toFilePath(toFilePath)
- , m_funcAST(funcAst)
- , m_func(func)
- , m_declarationText(declText)
- , m_fromRange(fromRange)
- , m_toRange(toRange)
- {
- if (type == Type::Pull) {
- setDescription(Tr::tr("Move Definition Here"));
- } else if (m_toFilePath == m_fromFilePath) {
- setDescription(Tr::tr("Move Definition to Class"));
- } else {
- const FilePath resolved = m_toFilePath.relativePathFrom(m_fromFilePath.parentDir());
- setDescription(Tr::tr("Move Definition to %1").arg(resolved.displayName()));
- }
- }
-
-private:
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr fromFile = refactoring.cppFile(m_fromFilePath);
- CppRefactoringFilePtr toFile = refactoring.cppFile(m_toFilePath);
-
- ensureFuncDefAstAndRange(*fromFile);
- if (!m_funcAST)
- return;
-
- const QString wholeFunctionText = m_declarationText
- + fromFile->textOf(fromFile->endOf(m_funcAST->declarator),
- fromFile->endOf(m_funcAST->function_body));
-
- // Replace declaration with function and delete old definition
- ChangeSet toTarget;
- toTarget.replace(m_toRange, wholeFunctionText);
- if (m_toFilePath == m_fromFilePath)
- toTarget.remove(m_fromRange);
- toFile->setChangeSet(toTarget);
- toFile->setOpenEditor(true, m_toRange.start);
- toFile->apply();
- if (m_toFilePath != m_fromFilePath) {
- ChangeSet fromTarget;
- fromTarget.remove(m_fromRange);
- fromFile->setChangeSet(fromTarget);
- fromFile->apply();
- }
- }
-
- void ensureFuncDefAstAndRange(CppRefactoringFile &defFile)
- {
- if (m_funcAST) {
- QTC_CHECK(m_fromRange.end > m_fromRange.start);
- return;
- }
- QTC_ASSERT(m_func, return);
- const QList<AST *> astPath = ASTPath(defFile.cppDocument())(m_func->line(),
- m_func->column());
- if (astPath.isEmpty())
- return;
- for (auto it = std::rbegin(astPath); it != std::rend(astPath); ++it) {
- m_funcAST = (*it)->asFunctionDefinition();
- if (!m_funcAST)
- continue;
- AST *astForRange = m_funcAST;
- const auto prev = std::next(it);
- if (prev != std::rend(astPath)) {
- if (const auto templAst = (*prev)->asTemplateDeclaration())
- astForRange = templAst;
- }
- m_fromRange = defFile.range(astForRange);
- return;
- }
- }
-
- const FilePath m_fromFilePath;
- const FilePath m_toFilePath;
- FunctionDefinitionAST *m_funcAST;
- Function *m_func;
- const QString m_declarationText;
- ChangeSet::Range m_fromRange;
- const ChangeSet::Range m_toRange;
-};
-
-} // anonymous namespace
-
-void MoveFuncDefToDeclPush::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- const QList<AST *> &path = interface.path();
- AST *completeDefAST = nullptr;
- FunctionDefinitionAST *funcAST = nullptr;
-
- const int pathSize = path.size();
- for (int idx = 1; idx < pathSize; ++idx) {
- if ((funcAST = path.at(idx)->asFunctionDefinition())) {
- AST *enclosingAST = path.at(idx - 1);
- if (enclosingAST->asClassSpecifier())
- return;
-
- // check cursor position
- if (idx != pathSize - 1 // Do not allow "void a() @ {..."
- && funcAST->function_body
- && !interface.isCursorOn(funcAST->function_body)) {
- completeDefAST = enclosingAST->asTemplateDeclaration() ? enclosingAST : funcAST;
- break;
- }
- funcAST = nullptr;
- }
- }
-
- if (!funcAST || !funcAST->symbol)
- return;
-
- const CppRefactoringChanges refactoring(interface.snapshot());
- const CppRefactoringFilePtr defFile = refactoring.cppFile(interface.filePath());
- const ChangeSet::Range defRange = defFile->range(completeDefAST);
-
- // Determine declaration (file, range, text);
- ChangeSet::Range declRange;
- QString declText;
- FilePath declFilePath;
-
- Function *func = funcAST->symbol;
- if (Class *matchingClass = isMemberFunction(interface.context(), func)) {
- // Dealing with member functions
- const QualifiedNameId *qName = func->name()->asQualifiedNameId();
- for (Symbol *symbol = matchingClass->find(qName->identifier());
- symbol; symbol = symbol->next()) {
- Symbol *s = symbol;
- if (func->enclosingScope()->asTemplate()) {
- if (const Template *templ = s->type()->asTemplateType()) {
- if (Symbol *decl = templ->declaration()) {
- if (decl->type()->asFunctionType())
- s = decl;
- }
- }
- }
- if (!s->name()
- || !qName->identifier()->match(s->identifier())
- || !s->type()->asFunctionType()
- || !s->type().match(func->type())
- || s->asFunction()) {
- continue;
- }
-
- declFilePath = matchingClass->filePath();
- const CppRefactoringFilePtr declFile = refactoring.cppFile(declFilePath);
- ASTPath astPath(declFile->cppDocument());
- const QList<AST *> path = astPath(s->line(), s->column());
- for (int idx = path.size() - 1; idx > 0; --idx) {
- AST *node = path.at(idx);
- if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
- if (simpleDecl->symbols && !simpleDecl->symbols->next) {
- declRange = declFile->range(simpleDecl);
- declText = declFile->textOf(simpleDecl);
- declText.remove(-1, 1); // remove ';' from declaration text
- break;
- }
- }
- }
-
- if (!declText.isEmpty())
- break;
- }
- } else if (Namespace *matchingNamespace = isNamespaceFunction(interface.context(), func)) {
- // Dealing with free functions
- bool isHeaderFile = false;
- declFilePath = correspondingHeaderOrSource(interface.filePath(), &isHeaderFile);
- if (isHeaderFile)
- return;
-
- const CppRefactoringFilePtr declFile = refactoring.cppFile(declFilePath);
- const LookupContext lc(declFile->cppDocument(), interface.snapshot());
- const QList<LookupItem> candidates = lc.lookup(func->name(), matchingNamespace);
- for (const LookupItem &candidate : candidates) {
- if (Symbol *s = candidate.declaration()) {
- if (s->asDeclaration()) {
- ASTPath astPath(declFile->cppDocument());
- const QList<AST *> path = astPath(s->line(), s->column());
- for (AST *node : path) {
- if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
- declRange = declFile->range(simpleDecl);
- declText = declFile->textOf(simpleDecl);
- declText.remove(-1, 1); // remove ';' from declaration text
- break;
- }
- }
- }
- }
-
- if (!declText.isEmpty()) {
- declText.prepend(inlinePrefix(declFilePath));
- break;
- }
- }
- }
-
- if (!declFilePath.isEmpty() && !declText.isEmpty())
- result << new MoveFuncDefToDeclOp(interface,
- interface.filePath(),
- declFilePath,
- funcAST, func, declText,
- defRange, declRange, MoveFuncDefToDeclOp::Push);
-}
-
-void MoveFuncDefToDeclPull::doMatch(const CppQuickFixInterface &interface,
- QuickFixOperations &result)
-{
- const QList<AST *> &path = interface.path();
- for (auto it = std::rbegin(path); it != std::rend(path); ++it) {
- SimpleDeclarationAST * const simpleDecl = (*it)->asSimpleDeclaration();
- if (!simpleDecl)
- continue;
- const auto prev = std::next(it);
- if (prev != std::rend(path) && (*prev)->asStatement())
- return;
- if (!simpleDecl->symbols || !simpleDecl->symbols->value || simpleDecl->symbols->next)
- return;
- Declaration * const decl = simpleDecl->symbols->value->asDeclaration();
- if (!decl)
- return;
- Function * const funcDecl = decl->type()->asFunctionType();
- if (!funcDecl)
- return;
- if (funcDecl->isSignal() || funcDecl->isPureVirtual() || funcDecl->isFriend())
- return;
-
- // Is there a definition?
- SymbolFinder symbolFinder;
- Function * const funcDef = symbolFinder.findMatchingDefinition(decl, interface.snapshot(),
- true);
- if (!funcDef)
- return;
-
- QString declText = interface.currentFile()->textOf(simpleDecl);
- declText.chop(1); // semicolon
- declText.prepend(inlinePrefix(interface.filePath(), [funcDecl] {
- return !funcDecl->enclosingScope()->asClass();
- }));
- result << new MoveFuncDefToDeclOp(interface, funcDef->filePath(), decl->filePath(), nullptr,
- funcDef, declText, {},
- interface.currentFile()->range(simpleDecl),
- MoveFuncDefToDeclOp::Pull);
- return;
- }
-}
-
-
-namespace {
-
-class AssignToLocalVariableOperation : public CppQuickFixOperation
-{
-public:
- explicit AssignToLocalVariableOperation(const CppQuickFixInterface &interface,
- const int insertPos, const AST *ast, const Name *name)
- : CppQuickFixOperation(interface)
- , m_insertPos(insertPos)
- , m_ast(ast)
- , m_name(name)
- , m_oo(CppCodeStyleSettings::currentProjectCodeStyleOverview())
- , m_originalName(m_oo.prettyName(m_name))
- , m_file(CppRefactoringChanges(snapshot()).cppFile(filePath()))
- {
- setDescription(Tr::tr("Assign to Local Variable"));
- }
-
-private:
- void perform() override
- {
- QString type = deduceType();
- if (type.isEmpty())
- return;
- const int origNameLength = m_originalName.length();
- const QString varName = constructVarName();
- const QString insertString = type.replace(type.length() - origNameLength, origNameLength,
- varName + QLatin1String(" = "));
- ChangeSet changes;
- changes.insert(m_insertPos, insertString);
- m_file->setChangeSet(changes);
- m_file->apply();
-
- // move cursor to new variable name
- QTextCursor c = m_file->cursor();
- c.setPosition(m_insertPos + insertString.length() - varName.length() - 3);
- c.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
- editor()->setTextCursor(c);
- }
-
- QString deduceType() const
- {
- const auto settings = CppQuickFixProjectsSettings::getQuickFixSettings(
- ProjectExplorer::ProjectTree::currentProject());
- if (m_file->cppDocument()->languageFeatures().cxx11Enabled && settings->useAuto)
- return "auto " + m_originalName;
-
- TypeOfExpression typeOfExpression;
- typeOfExpression.init(semanticInfo().doc, snapshot(), context().bindings());
- typeOfExpression.setExpandTemplates(true);
- Scope * const scope = m_file->scopeAt(m_ast->firstToken());
- const QList<LookupItem> result = typeOfExpression(m_file->textOf(m_ast).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().get();
- FullySpecifiedType type = rewriteType(result.first().type(), &env, control);
-
- return m_oo.prettyType(type, m_name);
- }
-
- QString constructVarName() const
- {
- QString newName = m_originalName;
- if (newName.startsWith(QLatin1String("get"), Qt::CaseInsensitive)
- && newName.length() > 3
- && newName.at(3).isUpper()) {
- newName.remove(0, 3);
- newName.replace(0, 1, newName.at(0).toLower());
- } else if (newName.startsWith(QLatin1String("to"), Qt::CaseInsensitive)
- && newName.length() > 2
- && newName.at(2).isUpper()) {
- newName.remove(0, 2);
- newName.replace(0, 1, newName.at(0).toLower());
- } else {
- newName.replace(0, 1, newName.at(0).toUpper());
- newName.prepend(QLatin1String("local"));
- }
- return newName;
- }
-
- const int m_insertPos;
- const AST * const m_ast;
- const Name * const m_name;
- const Overview m_oo;
- const QString m_originalName;
- const CppRefactoringFilePtr m_file;
-};
-
-} // anonymous namespace
-
-void AssignToLocalVariable::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- const QList<AST *> &path = interface.path();
- AST *outerAST = nullptr;
- SimpleNameAST *nameAST = nullptr;
-
- for (int i = path.size() - 3; i >= 0; --i) {
- if (CallAST *callAST = path.at(i)->asCall()) {
- if (!interface.isCursorOn(callAST))
- return;
- if (i - 2 >= 0) {
- const int idx = i - 2;
- if (path.at(idx)->asSimpleDeclaration())
- return;
- if (path.at(idx)->asExpressionStatement())
- return;
- if (path.at(idx)->asMemInitializer())
- return;
- if (path.at(idx)->asCall()) { // Fallback if we have a->b()->c()...
- --i;
- continue;
- }
- }
- for (int a = i - 1; a > 0; --a) {
- if (path.at(a)->asBinaryExpression())
- return;
- if (path.at(a)->asReturnStatement())
- return;
- if (path.at(a)->asCall())
- return;
- }
-
- if (MemberAccessAST *member = path.at(i + 1)->asMemberAccess()) { // member
- if (NameAST *name = member->member_name)
- nameAST = name->asSimpleName();
- } else if (QualifiedNameAST *qname = path.at(i + 2)->asQualifiedName()) { // static or
- nameAST = qname->unqualified_name->asSimpleName(); // func in ns
- } else { // normal
- nameAST = path.at(i + 2)->asSimpleName();
- }
-
- if (nameAST) {
- outerAST = callAST;
- break;
- }
- } else if (NewExpressionAST *newexp = path.at(i)->asNewExpression()) {
- if (!interface.isCursorOn(newexp))
- return;
- if (i - 2 >= 0) {
- const int idx = i - 2;
- if (path.at(idx)->asSimpleDeclaration())
- return;
- if (path.at(idx)->asExpressionStatement())
- return;
- if (path.at(idx)->asMemInitializer())
- return;
- }
- for (int a = i - 1; a > 0; --a) {
- if (path.at(a)->asReturnStatement())
- return;
- if (path.at(a)->asCall())
- return;
- }
-
- if (NamedTypeSpecifierAST *ts = path.at(i + 2)->asNamedTypeSpecifier()) {
- nameAST = ts->name->asSimpleName();
- outerAST = newexp;
- break;
- }
- }
- }
-
- if (outerAST && nameAST) {
- const CppRefactoringFilePtr file = interface.currentFile();
- QList<LookupItem> items;
- TypeOfExpression typeOfExpression;
- typeOfExpression.init(interface.semanticInfo().doc, interface.snapshot(),
- interface.context().bindings());
- typeOfExpression.setExpandTemplates(true);
-
- // If items are empty, AssignToLocalVariableOperation will fail.
- items = typeOfExpression(file->textOf(outerAST).toUtf8(),
- file->scopeAt(outerAST->firstToken()),
- TypeOfExpression::Preprocess);
- if (items.isEmpty())
- return;
-
- if (CallAST *callAST = outerAST->asCall()) {
- items = typeOfExpression(file->textOf(callAST->base_expression).toUtf8(),
- file->scopeAt(callAST->base_expression->firstToken()),
- TypeOfExpression::Preprocess);
- } else {
- items = typeOfExpression(file->textOf(nameAST).toUtf8(),
- file->scopeAt(nameAST->firstToken()),
- TypeOfExpression::Preprocess);
- }
-
- for (const LookupItem &item : std::as_const(items)) {
- if (!item.declaration())
- continue;
-
- if (Function *func = item.declaration()->asFunction()) {
- if (func->isSignal() || func->returnType()->asVoidType())
- return;
- } else if (Declaration *dec = item.declaration()->asDeclaration()) {
- if (Function *func = dec->type()->asFunctionType()) {
- if (func->isSignal() || func->returnType()->asVoidType())
- return;
- }
- }
-
- const Name *name = nameAST->name;
- const int insertPos = interface.currentFile()->startOf(outerAST);
- result << new AssignToLocalVariableOperation(interface, insertPos, outerAST, name);
- return;
- }
- }
-}
-
-namespace {
-
-class OptimizeForLoopOperation: public CppQuickFixOperation
-{
-public:
- OptimizeForLoopOperation(const CppQuickFixInterface &interface, const ForStatementAST *forAst,
- const bool optimizePostcrement, const ExpressionAST *expression,
- const FullySpecifiedType &type)
- : CppQuickFixOperation(interface)
- , m_forAst(forAst)
- , m_optimizePostcrement(optimizePostcrement)
- , m_expression(expression)
- , m_type(type)
- {
- setDescription(Tr::tr("Optimize for-Loop"));
- }
-
- void perform() override
- {
- QTC_ASSERT(m_forAst, return);
-
- const Utils::FilePath filePath = currentFile()->filePath();
- const CppRefactoringChanges refactoring(snapshot());
- const CppRefactoringFilePtr file = refactoring.cppFile(filePath);
- ChangeSet change;
-
- // Optimize post (in|de)crement operator to pre (in|de)crement operator
- if (m_optimizePostcrement && m_forAst->expression) {
- PostIncrDecrAST *incrdecr = m_forAst->expression->asPostIncrDecr();
- if (incrdecr && incrdecr->base_expression && incrdecr->incr_decr_token) {
- change.flip(file->range(incrdecr->base_expression),
- file->range(incrdecr->incr_decr_token));
- }
- }
-
- // Optimize Condition
- int renamePos = -1;
- if (m_expression) {
- QString varName = QLatin1String("total");
-
- if (file->textOf(m_forAst->initializer).length() == 1) {
- Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
- const QString typeAndName = oo.prettyType(m_type, varName);
- renamePos = file->endOf(m_forAst->initializer) - 1 + typeAndName.length();
- change.insert(file->endOf(m_forAst->initializer) - 1, // "-1" because of ";"
- typeAndName + QLatin1String(" = ") + file->textOf(m_expression));
- } else {
- // Check if varName is already used
- if (DeclarationStatementAST *ds = m_forAst->initializer->asDeclarationStatement()) {
- if (DeclarationAST *decl = ds->declaration) {
- if (SimpleDeclarationAST *sdecl = decl->asSimpleDeclaration()) {
- for (;;) {
- bool match = false;
- for (DeclaratorListAST *it = sdecl->declarator_list; it;
- it = it->next) {
- if (file->textOf(it->value->core_declarator) == varName) {
- varName += QLatin1Char('X');
- match = true;
- break;
- }
- }
- if (!match)
- break;
- }
- }
- }
- }
-
- renamePos = file->endOf(m_forAst->initializer) + 1;
- change.insert(file->endOf(m_forAst->initializer) - 1, // "-1" because of ";"
- QLatin1String(", ") + varName + QLatin1String(" = ")
- + file->textOf(m_expression));
- }
-
- ChangeSet::Range exprRange(file->startOf(m_expression), file->endOf(m_expression));
- change.replace(exprRange, varName);
- }
-
- file->setChangeSet(change);
- file->apply();
-
- // Select variable name and trigger symbol rename
- if (renamePos != -1) {
- QTextCursor c = file->cursor();
- c.setPosition(renamePos);
- editor()->setTextCursor(c);
- editor()->renameSymbolUnderCursor();
- c.select(QTextCursor::WordUnderCursor);
- editor()->setTextCursor(c);
- }
- }
-
-private:
- const ForStatementAST *m_forAst;
- const bool m_optimizePostcrement;
- const ExpressionAST *m_expression;
- const FullySpecifiedType m_type;
-};
-
-} // anonymous namespace
-
-void OptimizeForLoop::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- const QList<AST *> path = interface.path();
- ForStatementAST *forAst = nullptr;
- if (!path.isEmpty())
- forAst = path.last()->asForStatement();
- if (!forAst || !interface.isCursorOn(forAst))
- return;
-
- // Check for optimizing a postcrement
- const CppRefactoringFilePtr file = interface.currentFile();
- bool optimizePostcrement = false;
- if (forAst->expression) {
- if (PostIncrDecrAST *incrdecr = forAst->expression->asPostIncrDecr()) {
- const Token t = file->tokenAt(incrdecr->incr_decr_token);
- if (t.is(T_PLUS_PLUS) || t.is(T_MINUS_MINUS))
- optimizePostcrement = true;
- }
- }
-
- // Check for optimizing condition
- bool optimizeCondition = false;
- FullySpecifiedType conditionType;
- ExpressionAST *conditionExpression = nullptr;
- if (forAst->initializer && forAst->condition) {
- if (BinaryExpressionAST *binary = forAst->condition->asBinaryExpression()) {
- // Get the expression against which we should evaluate
- IdExpressionAST *conditionId = binary->left_expression->asIdExpression();
- if (conditionId) {
- conditionExpression = binary->right_expression;
- } else {
- conditionId = binary->right_expression->asIdExpression();
- conditionExpression = binary->left_expression;
- }
-
- if (conditionId && conditionExpression
- && !(conditionExpression->asNumericLiteral()
- || conditionExpression->asStringLiteral()
- || conditionExpression->asIdExpression()
- || conditionExpression->asUnaryExpression())) {
- // Determine type of for initializer
- FullySpecifiedType initializerType;
- if (DeclarationStatementAST *stmt = forAst->initializer->asDeclarationStatement()) {
- if (stmt->declaration) {
- if (SimpleDeclarationAST *decl = stmt->declaration->asSimpleDeclaration()) {
- if (decl->symbols) {
- if (Symbol *symbol = decl->symbols->value)
- initializerType = symbol->type();
- }
- }
- }
- }
-
- // Determine type of for condition
- TypeOfExpression typeOfExpression;
- typeOfExpression.init(interface.semanticInfo().doc, interface.snapshot(),
- interface.context().bindings());
- typeOfExpression.setExpandTemplates(true);
- Scope *scope = file->scopeAt(conditionId->firstToken());
- const QList<LookupItem> conditionItems = typeOfExpression(
- conditionId, interface.semanticInfo().doc, scope);
- if (!conditionItems.isEmpty())
- conditionType = conditionItems.first().type();
-
- if (conditionType.isValid()
- && (file->textOf(forAst->initializer) == QLatin1String(";")
- || initializerType == conditionType)) {
- optimizeCondition = true;
- }
- }
- }
- }
-
- if (optimizePostcrement || optimizeCondition) {
- result << new OptimizeForLoopOperation(interface, forAst, optimizePostcrement,
- optimizeCondition ? conditionExpression : nullptr,
- conditionType);
- }
-}
-
-namespace {
-
-class EscapeStringLiteralOperation: public CppQuickFixOperation
-{
-public:
- EscapeStringLiteralOperation(const CppQuickFixInterface &interface,
- ExpressionAST *literal, bool escape)
- : CppQuickFixOperation(interface)
- , m_literal(literal)
- , m_escape(escape)
- {
- if (m_escape) {
- setDescription(Tr::tr("Escape String Literal as UTF-8"));
- } else {
- setDescription(Tr::tr("Unescape String Literal as UTF-8"));
- }
- }
-
-private:
- static inline bool isDigit(quint8 ch, int base)
- {
- if (base == 8)
- return ch >= '0' && ch < '8';
- if (base == 16)
- return isxdigit(ch);
- return false;
- }
-
- static QByteArrayList escapeString(const QByteArray &contents)
- {
- QByteArrayList newContents;
- QByteArray chunk;
- bool wasEscaped = false;
- for (const quint8 c : contents) {
- const bool needsEscape = !isascii(c) || !isprint(c);
- if (!needsEscape && wasEscaped && std::isxdigit(c) && !chunk.isEmpty()) {
- newContents << chunk;
- chunk.clear();
- }
- if (needsEscape)
- chunk += QByteArray("\\x") + QByteArray::number(c, 16).rightJustified(2, '0');
- else
- chunk += c;
- wasEscaped = needsEscape;
- }
- if (!chunk.isEmpty())
- newContents << chunk;
- return newContents;
- }
-
- static QByteArray unescapeString(const QByteArray &contents)
- {
- QByteArray newContents;
- const int len = contents.length();
- for (int i = 0; i < len; ++i) {
- quint8 c = contents.at(i);
- if (c == '\\' && i < len - 1) {
- int idx = i + 1;
- quint8 ch = contents.at(idx);
- int base = 0;
- int maxlen = 0;
- if (isDigit(ch, 8)) {
- base = 8;
- maxlen = 3;
- } else if ((ch == 'x' || ch == 'X') && idx < len - 1) {
- base = 16;
- maxlen = 2;
- ch = contents.at(++idx);
- }
- if (base > 0) {
- QByteArray buf;
- while (isDigit(ch, base) && idx < len && buf.length() < maxlen) {
- buf += ch;
- ++idx;
- if (idx == len)
- break;
- ch = contents.at(idx);
- }
- if (!buf.isEmpty()) {
- bool ok;
- uint value = buf.toUInt(&ok, base);
- // Don't unescape isascii() && !isprint()
- if (ok && (!isascii(value) || isprint(value))) {
- newContents += value;
- i = idx - 1;
- continue;
- }
- }
- }
- newContents += c;
- c = contents.at(++i);
- }
- newContents += c;
- }
- return newContents;
- }
-
- // QuickFixOperation interface
-public:
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
-
- const int startPos = currentFile->startOf(m_literal);
- const int endPos = currentFile->endOf(m_literal);
-
- StringLiteralAST *stringLiteral = m_literal->asStringLiteral();
- QTC_ASSERT(stringLiteral, return);
- const QByteArray oldContents(currentFile->tokenAt(stringLiteral->literal_token).
- identifier->chars());
- QByteArrayList newContents;
- if (m_escape)
- newContents = escapeString(oldContents);
- else
- newContents = {unescapeString(oldContents)};
-
- if (newContents.isEmpty()
- || (newContents.size() == 1 && newContents.first() == oldContents)) {
- return;
- }
-
- QTextCodec *utf8codec = QTextCodec::codecForName("UTF-8");
- QScopedPointer<QTextDecoder> decoder(utf8codec->makeDecoder());
- ChangeSet changes;
-
- bool replace = true;
- for (const QByteArray &chunk : std::as_const(newContents)) {
- const QString str = decoder->toUnicode(chunk);
- const QByteArray utf8buf = str.toUtf8();
- if (!utf8codec->canEncode(str) || chunk != utf8buf)
- return;
- if (replace)
- changes.replace(startPos + 1, endPos - 1, str);
- else
- changes.insert(endPos, "\"" + str + "\"");
- replace = false;
- }
- currentFile->setChangeSet(changes);
- currentFile->apply();
- }
-
-private:
- ExpressionAST *m_literal;
- bool m_escape;
-};
-
-} // anonymous namespace
-
-void EscapeStringLiteral::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- const QList<AST *> &path = interface.path();
- if (path.isEmpty())
- return;
-
- AST * const lastAst = path.last();
- ExpressionAST *literal = lastAst->asStringLiteral();
- if (!literal)
- return;
-
- StringLiteralAST *stringLiteral = literal->asStringLiteral();
- CppRefactoringFilePtr file = interface.currentFile();
- const QByteArray contents(file->tokenAt(stringLiteral->literal_token).identifier->chars());
-
- bool canEscape = false;
- bool canUnescape = false;
- for (int i = 0; i < contents.length(); ++i) {
- quint8 c = contents.at(i);
- if (!isascii(c) || !isprint(c)) {
- canEscape = true;
- } else if (c == '\\' && i < contents.length() - 1) {
- c = contents.at(++i);
- if ((c >= '0' && c < '8') || c == 'x' || c == 'X')
- canUnescape = true;
- }
- }
-
- if (canEscape)
- result << new EscapeStringLiteralOperation(interface, literal, true);
-
- if (canUnescape)
- result << new EscapeStringLiteralOperation(interface, literal, false);
-}
-
-
-namespace {
-
-class ConvertQt4ConnectOperation: public CppQuickFixOperation
-{
-public:
- ConvertQt4ConnectOperation(const CppQuickFixInterface &interface, const ChangeSet &changes)
- : CppQuickFixOperation(interface, 1), m_changes(changes)
- {
- setDescription(Tr::tr("Convert connect() to Qt 5 Style"));
- }
-
-private:
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
- currentFile->setChangeSet(m_changes);
- currentFile->apply();
- }
-
- const ChangeSet m_changes;
-};
-
-Symbol *skipForwardDeclarations(const QList<Symbol *> &symbols)
-{
- for (Symbol *symbol : symbols) {
- if (!symbol->type()->asForwardClassDeclarationType())
- return symbol;
- }
-
- return nullptr;
-}
-
-bool findRawAccessFunction(Class *klass, PointerType *pointerType, QString *objAccessFunction)
-{
- QList<Function *> candidates;
- for (auto it = klass->memberBegin(), end = klass->memberEnd(); it != end; ++it) {
- if (Function *func = (*it)->asFunction()) {
- const Name *funcName = func->name();
- if (!funcName->asOperatorNameId()
- && !funcName->asConversionNameId()
- && func->returnType().type() == pointerType
- && func->isConst()
- && func->argumentCount() == 0) {
- candidates << func;
- }
- }
- }
- const Name *funcName = nullptr;
- switch (candidates.size()) {
- case 0:
- return false;
- case 1:
- funcName = candidates.first()->name();
- break;
- default:
- // Multiple candidates - prefer a function named data
- for (Function *func : std::as_const(candidates)) {
- if (!strcmp(func->name()->identifier()->chars(), "data")) {
- funcName = func->name();
- break;
- }
- }
- if (!funcName)
- funcName = candidates.first()->name();
- }
- const Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
- *objAccessFunction = QLatin1Char('.') + oo.prettyName(funcName) + QLatin1String("()");
- return true;
-}
-
-PointerType *determineConvertedType(NamedType *namedType, const LookupContext &context,
- Scope *scope, QString *objAccessFunction)
-{
- if (!namedType)
- return nullptr;
- if (ClassOrNamespace *binding = context.lookupType(namedType->name(), scope)) {
- if (Symbol *objectClassSymbol = skipForwardDeclarations(binding->symbols())) {
- if (Class *klass = objectClassSymbol->asClass()) {
- for (auto it = klass->memberBegin(), end = klass->memberEnd(); it != end; ++it) {
- if (Function *func = (*it)->asFunction()) {
- if (const ConversionNameId *conversionName =
- func->name()->asConversionNameId()) {
- if (PointerType *type = conversionName->type()->asPointerType()) {
- if (findRawAccessFunction(klass, type, objAccessFunction))
- return type;
- }
- }
- }
- }
- }
- }
- }
-
- return nullptr;
-}
-
-Class *senderOrReceiverClass(const CppQuickFixInterface &interface,
- const CppRefactoringFilePtr &file,
- const ExpressionAST *objectPointerAST,
- Scope *objectPointerScope,
- QString *objAccessFunction)
-{
- const LookupContext &context = interface.context();
-
- QByteArray objectPointerExpression;
- if (objectPointerAST)
- objectPointerExpression = file->textOf(objectPointerAST).toUtf8();
- else
- objectPointerExpression = "this";
-
- TypeOfExpression toe;
- toe.setExpandTemplates(true);
- toe.init(interface.semanticInfo().doc, interface.snapshot(), context.bindings());
- const QList<LookupItem> objectPointerExpressions = toe(objectPointerExpression,
- objectPointerScope, TypeOfExpression::Preprocess);
- QTC_ASSERT(!objectPointerExpressions.isEmpty(), return nullptr);
-
- Type *objectPointerTypeBase = objectPointerExpressions.first().type().type();
- QTC_ASSERT(objectPointerTypeBase, return nullptr);
-
- PointerType *objectPointerType = objectPointerTypeBase->asPointerType();
- if (!objectPointerType) {
- objectPointerType = determineConvertedType(objectPointerTypeBase->asNamedType(), context,
- objectPointerScope, objAccessFunction);
- }
- QTC_ASSERT(objectPointerType, return nullptr);
-
- Type *objectTypeBase = objectPointerType->elementType().type(); // Dereference
- QTC_ASSERT(objectTypeBase, return nullptr);
-
- NamedType *objectType = objectTypeBase->asNamedType();
- QTC_ASSERT(objectType, return nullptr);
-
- ClassOrNamespace *objectClassCON = context.lookupType(objectType->name(), objectPointerScope);
- if (!objectClassCON) {
- objectClassCON = objectPointerExpressions.first().binding();
- QTC_ASSERT(objectClassCON, return nullptr);
- }
- QTC_ASSERT(!objectClassCON->symbols().isEmpty(), return nullptr);
-
- Symbol *objectClassSymbol = skipForwardDeclarations(objectClassCON->symbols());
- QTC_ASSERT(objectClassSymbol, return nullptr);
-
- return objectClassSymbol->asClass();
-}
-
-bool findConnectReplacement(const CppQuickFixInterface &interface,
- const ExpressionAST *objectPointerAST,
- const QtMethodAST *methodAST,
- const CppRefactoringFilePtr &file,
- QString *replacement,
- QString *objAccessFunction)
-{
- // Get name of method
- if (!methodAST->declarator || !methodAST->declarator->core_declarator)
- return false;
-
- DeclaratorIdAST *methodDeclIdAST = methodAST->declarator->core_declarator->asDeclaratorId();
- if (!methodDeclIdAST)
- return false;
-
- NameAST *methodNameAST = methodDeclIdAST->name;
- if (!methodNameAST)
- return false;
-
- // Lookup object pointer type
- Scope *scope = file->scopeAt(methodAST->firstToken());
- Class *objectClass = senderOrReceiverClass(interface, file, objectPointerAST, scope,
- objAccessFunction);
- QTC_ASSERT(objectClass, return false);
-
- // Look up member function in call, including base class members.
- const LookupContext &context = interface.context();
- const QList<LookupItem> methodResults = context.lookup(methodNameAST->name, objectClass);
- if (methodResults.isEmpty())
- return false; // Maybe mis-spelled signal/slot name
-
- Scope *baseClassScope = methodResults.at(0).scope(); // FIXME: Handle overloads
- QTC_ASSERT(baseClassScope, return false);
-
- Class *classOfMethod = baseClassScope->asClass(); // Declaration point of signal/slot
- QTC_ASSERT(classOfMethod, return false);
-
- Symbol *method = methodResults.at(0).declaration();
- QTC_ASSERT(method, return false);
-
- // Minimize qualification
- Control *control = context.bindings()->control().get();
- ClassOrNamespace *functionCON = context.lookupParent(scope);
- const Name *shortName = LookupContext::minimalName(method, functionCON, control);
- if (!shortName->asQualifiedNameId())
- shortName = control->qualifiedNameId(classOfMethod->name(), shortName);
-
- const Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
- *replacement = QLatin1Char('&') + oo.prettyName(shortName);
- return true;
-}
-
-bool onConnectOrDisconnectCall(AST *ast, const ExpressionListAST **arguments)
-{
- if (!ast)
- return false;
-
- CallAST *call = ast->asCall();
- if (!call)
- return false;
-
- if (!call->base_expression)
- return false;
-
- const IdExpressionAST *idExpr = call->base_expression->asIdExpression();
- if (!idExpr || !idExpr->name || !idExpr->name->name)
- return false;
-
- const ExpressionListAST *args = call->expression_list;
- if (!arguments)
- return false;
-
- const Identifier *id = idExpr->name->name->identifier();
- if (!id)
- return false;
-
- const QByteArray name(id->chars(), id->size());
- if (name != "connect" && name != "disconnect")
- return false;
-
- if (arguments)
- *arguments = args;
- return true;
-}
-
-// Might modify arg* output arguments even if false is returned.
-bool collectConnectArguments(const ExpressionListAST *arguments,
- const ExpressionAST **arg1, const QtMethodAST **arg2,
- const ExpressionAST **arg3, const QtMethodAST **arg4)
-{
- if (!arguments || !arg1 || !arg2 || !arg3 || !arg4)
- return false;
-
- *arg1 = arguments->value;
- arguments = arguments->next;
- if (!arg1 || !arguments)
- return false;
-
- *arg2 = arguments->value->asQtMethod();
- arguments = arguments->next;
- if (!*arg2 || !arguments)
- return false;
-
- *arg3 = arguments->value;
- if (!*arg3)
- return false;
-
- // Take care of three-arg version, with 'this' receiver.
- if (QtMethodAST *receiverMethod = arguments->value->asQtMethod()) {
- *arg3 = nullptr; // Means 'this'
- *arg4 = receiverMethod;
- return true;
- }
-
- arguments = arguments->next;
- if (!arguments)
- return false;
-
- *arg4 = arguments->value->asQtMethod();
- if (!*arg4)
- return false;
-
- return true;
-}
-
-} // anonynomous namespace
-
-void ConvertQt4Connect::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- const QList<AST *> &path = interface.path();
-
- for (int i = path.size(); --i >= 0; ) {
- const ExpressionListAST *arguments;
- if (!onConnectOrDisconnectCall(path.at(i), &arguments))
- continue;
-
- const ExpressionAST *arg1, *arg3;
- const QtMethodAST *arg2, *arg4;
- if (!collectConnectArguments(arguments, &arg1, &arg2, &arg3, &arg4))
- continue;
-
- const CppRefactoringFilePtr file = interface.currentFile();
-
- QString newSignal;
- QString senderAccessFunc;
- if (!findConnectReplacement(interface, arg1, arg2, file, &newSignal, &senderAccessFunc))
- continue;
-
- QString newMethod;
- QString receiverAccessFunc;
- if (!findConnectReplacement(interface, arg3, arg4, file, &newMethod, &receiverAccessFunc))
- continue;
-
- ChangeSet changes;
- changes.replace(file->endOf(arg1), file->endOf(arg1), senderAccessFunc);
- changes.replace(file->startOf(arg2), file->endOf(arg2), newSignal);
- if (!arg3)
- newMethod.prepend(QLatin1String("this, "));
- else
- changes.replace(file->endOf(arg3), file->endOf(arg3), receiverAccessFunc);
- changes.replace(file->startOf(arg4), file->endOf(arg4), newMethod);
-
- result << new ConvertQt4ConnectOperation(interface, changes);
- return;
- }
-}
-
-void ExtraRefactoringOperations::doMatch(const CppQuickFixInterface &interface,
- QuickFixOperations &result)
-{
- const auto processor = CppModelManager::cppEditorDocumentProcessor(interface.filePath());
- if (processor) {
- const auto clangFixItOperations = processor->extraRefactoringOperations(interface);
- result.append(clangFixItOperations);
- }
-}
-
-namespace {
-
-/**
- * @brief The NameCounter class counts the parts of a name. E.g. 2 for std::vector or 1 for variant
- */
-class NameCounter : private NameVisitor
-{
-public:
- int count(const Name *name)
- {
- counter = 0;
- accept(name);
- return counter;
- }
-
-private:
- void visit(const Identifier *) override { ++counter; }
- void visit(const DestructorNameId *) override { ++counter; }
- void visit(const TemplateNameId *) override { ++counter; }
- void visit(const QualifiedNameId *name) override
- {
- if (name->base())
- accept(name->base());
- accept(name->name());
- }
- int counter;
-};
-
-/**
- * @brief getBaseName returns the base name of a qualified name or nullptr.
- * E.g.: foo::bar => foo; bar => bar
- * @param name The Name, maybe qualified
- * @return The base name of the qualified name or nullptr
- */
-const Identifier *getBaseName(const Name *name)
-{
- class GetBaseName : public NameVisitor
- {
- void visit(const Identifier *name) override { baseName = name; }
- void visit(const QualifiedNameId *name) override
- {
- if (name->base())
- accept(name->base());
- else
- accept(name->name());
- }
-
- public:
- const Identifier *baseName = nullptr;
- };
- GetBaseName getter;
- getter.accept(name);
- return getter.baseName;
-}
-
-/**
- * @brief countNames counts the parts of the Name.
- * E.g. if the name is std::vector, the function returns 2, if the name is variant, returns 1
- * @param name The name that should be counted
- * @return the number of parts of the name
- */
-int countNames(const Name *name)
-{
- return NameCounter{}.count(name);
-}
-
-/**
- * @brief removeLine removes the whole line in which the ast node is located if there are otherwise only whitespaces
- * @param file The file in which the AST node is located
- * @param ast The ast node
- * @param changeSet The ChangeSet of the file
- */
-void removeLine(const CppRefactoringFile *file, AST *ast, ChangeSet &changeSet)
-{
- RefactoringFile::Range range = file->range(ast);
- --range.start;
- while (range.start >= 0) {
- QChar current = file->charAt(range.start);
- if (!current.isSpace()) {
- ++range.start;
- break;
- }
- if (current == QChar::ParagraphSeparator)
- break;
- --range.start;
- }
- range.start = std::max(0, range.start);
- while (range.end < file->document()->characterCount()) {
- QChar current = file->charAt(range.end);
- if (!current.isSpace())
- break;
- if (current == QChar::ParagraphSeparator)
- break;
- ++range.end;
- }
- range.end = std::min(file->document()->characterCount(), range.end);
- const bool newLineStart = file->charAt(range.start) == QChar::ParagraphSeparator;
- const bool newLineEnd = file->charAt(range.end) == QChar::ParagraphSeparator;
- if (!newLineEnd && newLineStart)
- ++range.start;
- changeSet.remove(range);
-}
-
-/**
- * @brief The RemoveNamespaceVisitor class removes a using namespace and rewrites all types that
- * are in the namespace if needed
- */
-class RemoveNamespaceVisitor : public ASTVisitor
-{
-public:
- constexpr static int SearchGlobalUsingDirectivePos = std::numeric_limits<int>::max();
- RemoveNamespaceVisitor(const CppRefactoringFile *file,
- const Snapshot &snapshot,
- const Name *namespace_,
- int symbolPos,
- bool removeAllAtGlobalScope)
- : ASTVisitor(file->cppDocument()->translationUnit())
- , m_file(file)
- , m_snapshot(snapshot)
- , m_namespace(namespace_)
- , m_missingNamespace(toString(namespace_) + "::")
- , m_context(m_file->cppDocument(), m_snapshot)
- , m_symbolPos(symbolPos)
- , m_removeAllAtGlobalScope(removeAllAtGlobalScope)
-
- {}
-
- const ChangeSet &getChanges() { return m_changeSet; }
-
- /**
- * @brief isGlobalUsingNamespace return true if the using namespace that should be removed
- * is not scoped and other files that include this file will also use the using namespace
- * @return true if using namespace statement is global and not scoped, false otherwise
- */
- bool isGlobalUsingNamespace() const { return m_parentNode == nullptr; }
-
- /**
- * @brief foundGlobalUsingNamespace return true if removeAllAtGlobalScope is false and
- * another using namespace is found at the global scope, so that other files that include this
- * file don't have to be processed
- * @return true if there was a 'global' second using namespace in this file and
- * removeAllAtGlobalScope is false
- */
- bool foundGlobalUsingNamespace() const { return m_foundNamespace; }
-
-private:
- bool preVisit(AST *ast) override
- {
- if (!m_start) {
- if (ast->asTranslationUnit())
- return true;
- if (UsingDirectiveAST *usingDirective = ast->asUsingDirective()) {
- if (nameEqual(usingDirective->name->name, m_namespace)) {
- if (m_symbolPos == SearchGlobalUsingDirectivePos) {
- // we have found a global using directive, so lets start
- m_start = true;
- removeLine(m_file, ast, m_changeSet);
- return false;
- }
- // ignore the using namespace that should be removed
- if (m_file->endOf(ast) != m_symbolPos) {
- if (m_removeAllAtGlobalScope)
- removeLine(m_file, ast, m_changeSet);
- else
- m_done = true;
- }
- }
- }
- // if the end of the ast is before we should start, we are not interested in the node
- if (m_file->endOf(ast) <= m_symbolPos)
- return false;
-
- if (m_file->startOf(ast) > m_symbolPos)
- m_start = true;
- }
- return !m_foundNamespace && !m_done;
- }
-
- bool visit(NamespaceAST *ast) override
- {
- if (m_start && nameEqual(m_namespace, ast->symbol->name()))
- return false;
-
- return m_start;
- }
-
- // scopes for using namespace statements:
- bool visit(LinkageBodyAST *ast) override { return visitNamespaceScope(ast); }
- bool visit(CompoundStatementAST *ast) override { return visitNamespaceScope(ast); }
- bool visitNamespaceScope(AST *ast)
- {
- ++m_namespaceScopeCounter;
- if (!m_start)
- m_parentNode = ast;
- return true;
- }
-
- void endVisit(LinkageBodyAST *ast) override { endVisitNamespaceScope(ast); }
- void endVisit(CompoundStatementAST *ast) override { endVisitNamespaceScope(ast); }
- void endVisitNamespaceScope(AST *ast)
- {
- --m_namespaceScopeCounter;
- m_foundNamespace = false;
- // if we exit the scope of the using namespace we are done
- if (ast == m_parentNode)
- m_done = true;
- }
-
- bool visit(UsingDirectiveAST *ast) override
- {
- if (nameEqual(ast->name->name, m_namespace)) {
- if (m_removeAllAtGlobalScope && m_namespaceScopeCounter == 0)
- removeLine(m_file, ast, m_changeSet);
- else
- m_foundNamespace = true;
- return false;
- }
- return handleAstWithLongestName(ast);
- }
-
- bool visit(DeclaratorIdAST *ast) override
- {
- // e.g. we have the following code and get the following Lookup items:
- // namespace test {
- // struct foo { // 1. item with test::foo
- // foo(); // 2. item with test::foo::foo
- // };
- // }
- // using namespace foo;
- // foo::foo() { ... } // 3. item with foo::foo
- // Our current name is foo::foo so we have to match with the 2. item / longest name
- return handleAstWithLongestName(ast);
- }
-
- template<typename AST>
- bool handleAstWithLongestName(AST *ast)
- {
- if (m_start) {
- Scope *scope = m_file->scopeAt(ast->firstToken());
- const QList<LookupItem> localLookup = m_context.lookup(ast->name->name, scope);
- QList<const Name *> longestName;
- for (const LookupItem &item : localLookup) {
- QList<const Name *> names
- = m_context.fullyQualifiedName(item.declaration(),
- LookupContext::HideInlineNamespaces);
- if (names.length() > longestName.length())
- longestName = names;
- }
- const int currentNameCount = countNames(ast->name->name);
- const bool needNew = needMissingNamespaces(std::move(longestName), currentNameCount);
- if (needNew)
- insertMissingNamespace(ast);
- }
- return false;
- }
-
- bool visit(NamedTypeSpecifierAST *ast) override { return handleAstWithName(ast); }
-
- bool visit(IdExpressionAST *ast) override { return handleAstWithName(ast); }
-
- template<typename AST>
- bool handleAstWithName(AST *ast)
- {
- if (m_start) {
- Scope *scope = m_file->scopeAt(ast->firstToken());
- const Name *wantToLookup = ast->name->name;
- // first check if the base name is a typedef. Consider the following example:
- // using namespace std;
- // using vec = std::vector<int>;
- // vec::iterator it; // we have to lookup 'vec' and not iterator (would result in
- // std::vector<int>::iterator => std::vec::iterator, which is wrong)
- const Name *baseName = getBaseName(wantToLookup);
- QList<LookupItem> typedefCandidates = m_context.lookup(baseName, scope);
- if (!typedefCandidates.isEmpty()) {
- if (typedefCandidates.front().declaration()->isTypedef())
- wantToLookup = baseName;
- }
-
- const QList<LookupItem> lookups = m_context.lookup(wantToLookup, scope);
- if (!lookups.empty()) {
- QList<const Name *> fullName
- = m_context.fullyQualifiedName(lookups.first().declaration(),
- LookupContext::HideInlineNamespaces);
- const int currentNameCount = countNames(wantToLookup);
- const bool needNamespace = needMissingNamespaces(std::move(fullName),
- currentNameCount);
- if (needNamespace)
- insertMissingNamespace(ast);
- }
- }
- return true;
- }
-
- template<typename AST>
- void insertMissingNamespace(AST *ast)
- {
- DestructorNameAST *destructorName = ast->name->asDestructorName();
- if (destructorName)
- m_changeSet.insert(m_file->startOf(destructorName->unqualified_name), m_missingNamespace);
- else
- m_changeSet.insert(m_file->startOf(ast->name), m_missingNamespace);
- m_changeSet.operationList().last().setFormat1(false);
- }
-
- bool needMissingNamespaces(QList<const Name *> &&fullName, int currentNameCount)
- {
- if (currentNameCount > fullName.length())
- return false;
-
- // eg. fullName = std::vector, currentName = vector => result should be std
- fullName.erase(fullName.end() - currentNameCount, fullName.end());
- if (fullName.empty())
- return false;
- return nameEqual(m_namespace, fullName.last());
- }
-
- static bool nameEqual(const Name *name1, const Name *name2)
- {
- return Matcher::match(name1, name2);
- }
-
- QString toString(const Name *id)
- {
- const Identifier *identifier = id->asNameId();
- QTC_ASSERT(identifier, return {});
- return QString::fromUtf8(identifier->chars(), identifier->size());
- }
-
- const CppRefactoringFile *const m_file;
- const Snapshot &m_snapshot;
-
- const Name *m_namespace; // the name of the namespace that should be removed
- const QString m_missingNamespace; // that should be added if a type was using the namespace
- LookupContext m_context;
- ChangeSet m_changeSet;
- const int m_symbolPos; // the end position of the start symbol
- bool m_done = false;
- bool m_start = false;
- // true if a using namespace was found at a scope and the scope should be left
- bool m_foundNamespace = false;
- bool m_removeAllAtGlobalScope;
- // the scope where the using namespace that should be removed is valid
- AST *m_parentNode = nullptr;
- int m_namespaceScopeCounter = 0;
-};
-
-class RemoveUsingNamespaceOperation : public CppQuickFixOperation
-{
- struct Node
- {
- Document::Ptr document;
- bool hasGlobalUsingDirective = false;
- int unprocessedParents;
- std::vector<std::reference_wrapper<Node>> includes;
- std::vector<std::reference_wrapper<Node>> includedBy;
- Node() = default;
- Node(const Node &) = delete;
- Node(Node &&) = delete;
- };
-
-public:
- RemoveUsingNamespaceOperation(const CppQuickFixInterface &interface,
- UsingDirectiveAST *usingDirective,
- bool removeAllAtGlobalScope)
- : CppQuickFixOperation(interface, 1)
- , m_usingDirective(usingDirective)
- , m_removeAllAtGlobalScope(removeAllAtGlobalScope)
- {
- const QString name = Overview{}.prettyName(usingDirective->name->name);
- if (m_removeAllAtGlobalScope) {
- setDescription(Tr::tr(
- "Remove All Occurrences of \"using namespace %1\" in Global Scope "
- "and Adjust Type Names Accordingly")
- .arg(name));
- } else {
- setDescription(Tr::tr("Remove \"using namespace %1\" and "
- "Adjust Type Names Accordingly")
- .arg(name));
- }
- }
-
-private:
- std::map<Utils::FilePath, Node> buildIncludeGraph(CppRefactoringChanges &refactoring)
- {
- using namespace ProjectExplorer;
- using namespace Utils;
-
- const Snapshot &s = refactoring.snapshot();
- std::map<Utils::FilePath, Node> includeGraph;
-
- auto handleFile = [&](const FilePath &filePath, Document::Ptr doc, auto shouldHandle) {
- Node &node = includeGraph[filePath];
- node.document = doc;
- for (const Document::Include &include : doc->resolvedIncludes()) {
- const FilePath filePath = include.resolvedFileName();
- if (shouldHandle(filePath)) {
- Node &includedNode = includeGraph[filePath];
- includedNode.includedBy.push_back(node);
- node.includes.push_back(includedNode);
- }
- }
- };
-
- 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) {
- const Document::Ptr doc = s.document(file);
- if (!doc)
- continue;
- handleFile(file, doc, [&](const FilePath &file) {
- return projectFiles.contains(file);
- });
- }
- } else {
- for (auto i = s.begin(); i != s.end(); ++i) {
- if (ProjectFile::classify(i.key().toString()) != ProjectFile::Unsupported) {
- handleFile(i.key(), i.value(), [](const FilePath &file) {
- return ProjectFile::classify(file.toString()) != ProjectFile::Unsupported;
- });
- }
- }
- }
- for (auto &[_, node] : includeGraph) {
- Q_UNUSED(_)
- node.unprocessedParents = static_cast<int>(node.includes.size());
- }
- return includeGraph;
- }
-
- void removeAllUsingsAtGlobalScope(CppRefactoringChanges &refactoring)
- {
- auto includeGraph = buildIncludeGraph(refactoring);
- std::vector<std::reference_wrapper<Node>> nodesWithProcessedParents;
- for (auto &[_, node] : includeGraph) {
- Q_UNUSED(_)
- if (!node.unprocessedParents)
- nodesWithProcessedParents.push_back(node);
- }
- while (!nodesWithProcessedParents.empty()) {
- Node &node = nodesWithProcessedParents.back();
- nodesWithProcessedParents.pop_back();
- CppRefactoringFilePtr file = refactoring.cppFile(node.document->filePath());
- const bool parentHasUsing = Utils::anyOf(node.includes, &Node::hasGlobalUsingDirective);
- const int startPos = parentHasUsing
- ? 0
- : RemoveNamespaceVisitor::SearchGlobalUsingDirectivePos;
- const bool noGlobalUsing = refactorFile(file, refactoring.snapshot(), startPos);
- node.hasGlobalUsingDirective = !noGlobalUsing || parentHasUsing;
-
- for (Node &subNode : node.includedBy) {
- --subNode.unprocessedParents;
- if (subNode.unprocessedParents == 0)
- nodesWithProcessedParents.push_back(subNode);
- }
- }
- }
-
- void perform() override
- {
- CppRefactoringChanges refactoring(snapshot());
- CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
- if (m_removeAllAtGlobalScope) {
- removeAllUsingsAtGlobalScope(refactoring);
- } else if (refactorFile(currentFile,
- refactoring.snapshot(),
- currentFile->endOf(m_usingDirective),
- true)) {
- processIncludes(refactoring, filePath());
- }
-
- for (auto &file : std::as_const(m_changes))
- file->apply();
- }
-
- /**
- * @brief refactorFile remove using namespace xyz in the given file and rewrite types
- * @param file The file that should be processed
- * @param snapshot The snapshot to work on
- * @param startSymbol start processing after this index
- * @param removeUsing if the using directive is in this file, remove it
- * @return true if the using statement is global and there is no other global using namespace
- */
- bool refactorFile(CppRefactoringFilePtr &file,
- const Snapshot &snapshot,
- int startSymbol,
- bool removeUsing = false)
- {
- RemoveNamespaceVisitor visitor(file.get(),
- snapshot,
- m_usingDirective->name->name,
- startSymbol,
- m_removeAllAtGlobalScope);
- visitor.accept(file->cppDocument()->translationUnit()->ast());
- Utils::ChangeSet changes = visitor.getChanges();
- if (removeUsing)
- removeLine(file.get(), m_usingDirective, changes);
- if (!changes.isEmpty()) {
- file->setChangeSet(changes);
- // apply changes at the end, otherwise the symbol finder will fail to resolve symbols if
- // the using namespace is missing
- m_changes.insert(file);
- }
- return visitor.isGlobalUsingNamespace() && !visitor.foundGlobalUsingNamespace();
- }
-
- void processIncludes(CppRefactoringChanges &refactoring, const FilePath &filePath)
- {
- QList<Snapshot::IncludeLocation>
- includeLocationsOfDocument = refactoring.snapshot().includeLocationsOfDocument(filePath);
- for (Snapshot::IncludeLocation &loc : includeLocationsOfDocument) {
- if (!Utils::insert(m_processed, loc.first))
- continue;
-
- CppRefactoringFilePtr file = refactoring.cppFile(loc.first->filePath());
- const bool noGlobalUsing = refactorFile(file,
- refactoring.snapshot(),
- file->position(loc.second, 1));
- if (noGlobalUsing)
- processIncludes(refactoring, loc.first->filePath());
- }
- }
-
- QSet<Document::Ptr> m_processed;
- QSet<CppRefactoringFilePtr> m_changes;
-
- UsingDirectiveAST *m_usingDirective;
- bool m_removeAllAtGlobalScope;
-};
-} // namespace
-
-void RemoveUsingNamespace::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- const QList<AST *> &path = interface.path();
- // We expect something like
- // [0] TranslationUnitAST
- // ...
- // [] UsingDirectiveAST : if activated at 'using namespace'
- // [] NameAST (optional): if activated at the name e.g. 'std'
- int n = path.size() - 1;
- if (n <= 0)
- return;
- if (path.last()->asName())
- --n;
- UsingDirectiveAST *usingDirective = path.at(n)->asUsingDirective();
- if (usingDirective && usingDirective->name->name->asNameId()) {
- result << new RemoveUsingNamespaceOperation(interface, usingDirective, false);
- const bool isHeader = ProjectFile::isHeader(ProjectFile::classify(interface.filePath().toString()));
- if (isHeader && path.at(n - 1)->asTranslationUnit()) // using namespace at global scope
- result << new RemoveUsingNamespaceOperation(interface, usingDirective, true);
- }
-}
-
-namespace {
-
-struct ParentClassConstructorInfo;
-
-class ConstructorMemberInfo
-{
-public:
- ConstructorMemberInfo(const QString &name, Symbol *symbol, int numberOfMember)
- : memberVariableName(name)
- , parameterName(memberBaseName(name))
- , symbol(symbol)
- , type(symbol->type())
- , numberOfMember(numberOfMember)
- {}
- ConstructorMemberInfo(const QString &memberName,
- const QString &paramName,
- const QString &defaultValue,
- Symbol *symbol,
- const ParentClassConstructorInfo *parentClassConstructor)
- : parentClassConstructor(parentClassConstructor)
- , memberVariableName(memberName)
- , parameterName(paramName)
- , defaultValue(defaultValue)
- , init(defaultValue.isEmpty())
- , symbol(symbol)
- , type(symbol->type())
- {}
- const ParentClassConstructorInfo *parentClassConstructor = nullptr;
- QString memberVariableName;
- QString parameterName;
- QString defaultValue;
- bool init = true;
- bool customValueType; // for the generation later
- Symbol *symbol; // for the right type later
- FullySpecifiedType type;
- int numberOfMember; // first member, second member, ...
-};
-
-class ConstructorParams : public QAbstractTableModel
-{
- Q_OBJECT
- std::list<ConstructorMemberInfo> candidates;
- std::vector<ConstructorMemberInfo *> infos;
-
- void validateOrder()
- {
- // parameters with default values must be at the end
- bool foundWithDefault = false;
- for (auto info : infos) {
- if (info->init) {
- if (foundWithDefault && info->defaultValue.isEmpty()) {
- emit validOrder(false);
- return;
- }
- foundWithDefault |= !info->defaultValue.isEmpty();
- }
- }
- emit validOrder(true);
- }
-
-public:
- enum Column { ShouldInitColumn, MemberNameColumn, ParameterNameColumn, DefaultValueColumn };
- template<typename... _Args>
- void emplaceBackParameter(_Args &&...__args)
- {
- candidates.emplace_back(std::forward<_Args>(__args)...);
- infos.push_back(&candidates.back());
- }
- const std::vector<ConstructorMemberInfo *> &getInfos() const { return infos; }
- void addRow(ConstructorMemberInfo *info)
- {
- beginInsertRows({}, rowCount(), rowCount());
- infos.push_back(info);
- endInsertRows();
- validateOrder();
- }
- void removeRow(ConstructorMemberInfo *info)
- {
- for (auto iter = infos.begin(); iter != infos.end(); ++iter) {
- if (*iter == info) {
- const auto index = iter - infos.begin();
- beginRemoveRows({}, index, index);
- infos.erase(iter);
- endRemoveRows();
- validateOrder();
- return;
- }
- }
- }
-
- int selectedCount() const
- {
- return Utils::count(infos, [](const ConstructorMemberInfo *mi) {
- return mi->init && !mi->parentClassConstructor;
- });
- }
- int memberCount() const
- {
- return Utils::count(infos, [](const ConstructorMemberInfo *mi) {
- return !mi->parentClassConstructor;
- });
- }
-
- int rowCount(const QModelIndex & /*parent*/ = {}) const override { return int(infos.size()); }
- int columnCount(const QModelIndex & /*parent*/ = {}) const override { return 4; }
- QVariant data(const QModelIndex &index, int role) const override
- {
- if (index.row() < 0 || index.row() >= rowCount())
- return {};
- if (role == Qt::CheckStateRole && index.column() == ShouldInitColumn
- && !infos[index.row()]->parentClassConstructor)
- return infos[index.row()]->init ? Qt::Checked : Qt::Unchecked;
- if (role == Qt::DisplayRole && index.column() == MemberNameColumn)
- return infos[index.row()]->memberVariableName;
- if ((role == Qt::DisplayRole || role == Qt::EditRole)
- && index.column() == ParameterNameColumn)
- return infos[index.row()]->parameterName;
- if ((role == Qt::DisplayRole || role == Qt::EditRole)
- && index.column() == DefaultValueColumn)
- return infos[index.row()]->defaultValue;
- if ((role == Qt::ToolTipRole) && index.column() > 0)
- return Overview{}.prettyType(infos[index.row()]->symbol->type());
- return {};
- }
- bool setData(const QModelIndex &index, const QVariant &value, int role) override
- {
- if (index.column() == ShouldInitColumn && role == Qt::CheckStateRole) {
- if (infos[index.row()]->parentClassConstructor)
- return false;
- infos[index.row()]->init = value.toInt() == Qt::Checked;
- emit dataChanged(this->index(index.row(), 0), this->index(index.row(), columnCount()));
- validateOrder();
- return true;
- }
- if (index.column() == ParameterNameColumn && role == Qt::EditRole) {
- infos[index.row()]->parameterName = value.toString();
- return true;
- }
- if (index.column() == DefaultValueColumn && role == Qt::EditRole) {
- infos[index.row()]->defaultValue = value.toString();
- validateOrder();
- return true;
- }
- return false;
- }
- Qt::DropActions supportedDropActions() const override { return Qt::MoveAction; }
- Qt::ItemFlags flags(const QModelIndex &index) const override
- {
- if (!index.isValid())
- return Qt::ItemIsSelectable | Qt::ItemIsDropEnabled;
-
- Qt::ItemFlags f{};
- if (infos[index.row()]->init) {
- f |= Qt::ItemIsDragEnabled;
- f |= Qt::ItemIsSelectable;
- }
-
- if (index.column() == ShouldInitColumn && !infos[index.row()]->parentClassConstructor)
- return f | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
- if (!infos[index.row()]->init)
- return f;
- if (index.column() == MemberNameColumn)
- return f | Qt::ItemIsEnabled;
- if (index.column() == ParameterNameColumn || index.column() == DefaultValueColumn)
- return f | Qt::ItemIsEnabled | Qt::ItemIsEditable;
- return {};
- }
-
- QVariant headerData(int section, Qt::Orientation orientation, int role) const override
- {
- if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
- switch (section) {
- case ShouldInitColumn:
- return Tr::tr("Initialize in Constructor");
- case MemberNameColumn:
- return Tr::tr("Member Name");
- case ParameterNameColumn:
- return Tr::tr("Parameter Name");
- case DefaultValueColumn:
- return Tr::tr("Default Value");
- }
- }
- return {};
- }
- bool dropMimeData(const QMimeData *data,
- Qt::DropAction /*action*/,
- int row,
- int /*column*/,
- const QModelIndex & /*parent*/) override
- {
- if (row == -1)
- row = rowCount();
- bool ok;
- int sourceRow = data->data("application/x-qabstractitemmodeldatalist").toInt(&ok);
- if (ok) {
- if (sourceRow == row || row == sourceRow + 1)
- return false;
- beginMoveRows({}, sourceRow, sourceRow, {}, row);
- infos.insert(infos.begin() + row, infos.at(sourceRow));
- if (row < sourceRow)
- ++sourceRow;
- infos.erase(infos.begin() + sourceRow);
- validateOrder();
- return true;
- }
- return false;
- }
-
- QMimeData *mimeData(const QModelIndexList &indexes) const override
- {
- for (const auto &i : indexes) {
- if (!i.isValid())
- continue;
- auto data = new QMimeData();
- data->setData("application/x-qabstractitemmodeldatalist",
- QString::number(i.row()).toLatin1());
- return data;
- }
- return nullptr;
- }
-
- class TableViewStyle : public QProxyStyle
- {
- public:
- TableViewStyle(QStyle *style)
- : QProxyStyle(style)
- {}
-
- void drawPrimitive(PrimitiveElement element,
- const QStyleOption *option,
- QPainter *painter,
- const QWidget *widget) const override
- {
- if (element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull()) {
- QStyleOption opt(*option);
- opt.rect.setLeft(0);
- if (widget)
- opt.rect.setRight(widget->width());
- QProxyStyle::drawPrimitive(element, &opt, painter, widget);
- return;
- }
- QProxyStyle::drawPrimitive(element, option, painter, widget);
- }
- };
-signals:
- void validOrder(bool valid);
-};
-
-class TopMarginDelegate : public QStyledItemDelegate
-{
-public:
- TopMarginDelegate(QObject *parent = nullptr)
- : QStyledItemDelegate(parent)
- {}
-
- void paint(QPainter *painter,
- const QStyleOptionViewItem &option,
- const QModelIndex &index) const override
- {
- Q_ASSERT(index.isValid());
- QStyleOptionViewItem opt = option;
- initStyleOption(&opt, index);
- const QWidget *widget = option.widget;
- QStyle *style = widget ? widget->style() : QApplication::style();
- if (opt.rect.height() > 20)
- opt.rect.adjust(0, 5, 0, 0);
- style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
- }
-};
-
-struct ParentClassConstructorParameter : public ConstructorMemberInfo
-{
- QString originalDefaultValue;
- QString declaration; // displayed in the treeView
- ParentClassConstructorParameter(const QString &name,
- const QString &defaultValue,
- Symbol *symbol,
- const ParentClassConstructorInfo *parentClassConstructor);
-
- ParentClassConstructorParameter(const ParentClassConstructorParameter &) = delete;
- ParentClassConstructorParameter(ParentClassConstructorParameter &&) = default;
-};
-
-struct ParentClassConstructorInfo
-{
- ParentClassConstructorInfo(const QString &name, ConstructorParams &model)
- : className(name)
- , model(model)
- {}
- bool useInConstructor = false;
- const QString className;
- QString declaration;
- std::vector<ParentClassConstructorParameter> parameters;
- ConstructorParams &model;
-
- ParentClassConstructorInfo(const ParentClassConstructorInfo &) = delete;
- ParentClassConstructorInfo(ParentClassConstructorInfo &&) = default;
-
- void addParameter(ParentClassConstructorParameter &param) { model.addRow(&param); }
- void removeParameter(ParentClassConstructorParameter &param) { model.removeRow(&param); }
- void removeAllParameters()
- {
- for (auto &param : parameters)
- model.removeRow(&param);
- }
-};
-
-ParentClassConstructorParameter::ParentClassConstructorParameter(
- const QString &name,
- const QString &defaultValue,
- Symbol *symbol,
- const ParentClassConstructorInfo *parentClassConstructor)
- : ConstructorMemberInfo(parentClassConstructor->className + "::" + name,
- name,
- defaultValue,
- symbol,
- parentClassConstructor)
- , originalDefaultValue(defaultValue)
- , declaration(Overview{}.prettyType(symbol->type(), name)
- + (defaultValue.isEmpty() ? QString{} : " = " + defaultValue))
-{}
-
-using ParentClassConstructors = std::vector<ParentClassConstructorInfo>;
-
-class ParentClassesModel : public QAbstractItemModel
-{
- ParentClassConstructors &constructors;
-
-public:
- ParentClassesModel(QObject *parent, ParentClassConstructors &constructors)
- : QAbstractItemModel(parent)
- , constructors(constructors)
- {}
- QModelIndex index(int row, int column, const QModelIndex &parent = {}) const override
- {
- if (!parent.isValid())
- return createIndex(row, column, nullptr);
- if (parent.internalPointer())
- return {};
- auto index = createIndex(row, column, &constructors.at(parent.row()));
- return index;
- }
- QModelIndex parent(const QModelIndex &index) const override
- {
- if (!index.isValid())
- return {};
- auto *parent = static_cast<ParentClassConstructorInfo *>(index.internalPointer());
- if (!parent)
- return {};
- int i = 0;
- for (const auto &info : constructors) {
- if (&info == parent)
- return createIndex(i, 0, nullptr);
- ++i;
- }
- return {};
- }
- int rowCount(const QModelIndex &parent = {}) const override
- {
- if (!parent.isValid())
- return static_cast<int>(constructors.size());
- auto info = static_cast<ParentClassConstructorInfo *>(parent.internalPointer());
- if (!info)
- return static_cast<int>(constructors.at(parent.row()).parameters.size());
- return 0;
- }
- int columnCount(const QModelIndex & /*parent*/ = {}) const override { return 1; }
- QVariant data(const QModelIndex &index, int role) const override
- {
- if (!index.isValid())
- return {};
- auto info = static_cast<ParentClassConstructorInfo *>(index.internalPointer());
-
- if (info) {
- const auto &parameter = info->parameters.at(index.row());
- if (role == Qt::CheckStateRole)
- return parameter.init ? Qt::Checked : Qt::Unchecked;
- if (role == Qt::DisplayRole)
- return parameter.declaration;
- return {};
- }
- const auto &constructor = constructors.at(index.row());
- if (role == Qt::CheckStateRole)
- return constructor.useInConstructor ? Qt::PartiallyChecked : Qt::Unchecked;
- if (role == Qt::DisplayRole)
- return constructor.declaration;
-
- // Highlight the selected items
- if (role == Qt::FontRole && constructor.useInConstructor) {
- QFont font = QApplication::font();
- font.setBold(true);
- return font;
- }
- // Create a margin between sets of constructors for base classes
- if (role == Qt::SizeHintRole && index.row() > 0
- && constructor.className != constructors.at(index.row() - 1).className) {
- return QSize(-1, 25);
- }
- return {};
- }
- bool setData(const QModelIndex &index, const QVariant &value, int /*role*/) override
- {
- if (index.isValid() && index.column() == 0) {
- auto info = static_cast<ParentClassConstructorInfo *>(index.internalPointer());
- if (info) {
- const bool nowUse = value.toBool();
- auto &param = info->parameters.at(index.row());
- param.init = nowUse;
- if (nowUse)
- info->addParameter(param);
- else
- info->removeParameter(param);
- return true;
- }
- auto &newConstructor = constructors.at(index.row());
- // You have to select a base class constructor
- if (newConstructor.useInConstructor)
- return false;
- auto c = std::find_if(constructors.begin(), constructors.end(), [&](const auto &c) {
- return c.className == newConstructor.className && c.useInConstructor;
- });
- QTC_ASSERT(c == constructors.end(), return false;);
- c->useInConstructor = false;
- newConstructor.useInConstructor = true;
- emit dataChanged(this->index(index.row(), 0), this->index(index.row(), columnCount()));
- auto parentIndex = this->index(index.row(), 0);
- emit dataChanged(this->index(0, 0, parentIndex),
- this->index(rowCount(parentIndex), columnCount()));
- const int oldIndex = c - constructors.begin();
- emit dataChanged(this->index(oldIndex, 0), this->index(oldIndex, columnCount()));
- parentIndex = this->index(oldIndex, 0);
- emit dataChanged(this->index(0, 0, parentIndex),
- this->index(rowCount(parentIndex), columnCount()));
- // update other table
- c->removeAllParameters();
- for (auto &p : newConstructor.parameters)
- if (p.init)
- newConstructor.addParameter(p);
- return true;
- }
- return false;
- }
- QVariant headerData(int section, Qt::Orientation orientation, int role) const override
- {
- if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
- switch (section) {
- case 0:
- return Tr::tr("Base Class Constructors");
- }
- }
- return {};
- }
- Qt::ItemFlags flags(const QModelIndex &index) const override
- {
- if (index.isValid()) {
- Qt::ItemFlags f;
- auto info = static_cast<ParentClassConstructorInfo *>(index.internalPointer());
- if (!info || info->useInConstructor) {
- f |= Qt::ItemIsEnabled;
- }
- f |= Qt::ItemIsUserCheckable;
-
- return f;
- }
- return {};
- }
-};
-
-class GenerateConstructorDialog : public QDialog
-{
-public:
- GenerateConstructorDialog(ConstructorParams *constructorParamsModel,
- ParentClassConstructors &constructors)
- {
- setWindowTitle(Tr::tr("Constructor"));
-
- const auto treeModel = new ParentClassesModel(this, constructors);
- const auto treeView = new QTreeView(this);
- treeView->setModel(treeModel);
- treeView->setItemDelegate(new TopMarginDelegate(this));
- treeView->expandAll();
-
- const auto view = new QTableView(this);
- view->setModel(constructorParamsModel);
- int optimalWidth = 0;
- for (int i = 0; i < constructorParamsModel->columnCount(QModelIndex{}); ++i) {
- view->resizeColumnToContents(i);
- optimalWidth += view->columnWidth(i);
- }
- view->resizeRowsToContents();
- view->verticalHeader()->setDefaultSectionSize(view->rowHeight(0));
- view->setSelectionBehavior(QAbstractItemView::SelectRows);
- view->setSelectionMode(QAbstractItemView::SingleSelection);
- view->setDragEnabled(true);
- view->setDropIndicatorShown(true);
- view->setDefaultDropAction(Qt::MoveAction);
- view->setDragDropMode(QAbstractItemView::InternalMove);
- view->setDragDropOverwriteMode(false);
- view->horizontalHeader()->setStretchLastSection(true);
- view->setStyle(new ConstructorParams::TableViewStyle(view->style()));
-
- const auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
- connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
- connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
-
- const auto errorLabel = new QLabel(
- Tr::tr("Parameters without default value must come before parameters with default value."));
- errorLabel->setStyleSheet("color: #ff0000");
- errorLabel->setVisible(false);
- QSizePolicy labelSizePolicy = errorLabel->sizePolicy();
- labelSizePolicy.setRetainSizeWhenHidden(true);
- errorLabel->setSizePolicy(labelSizePolicy);
- connect(constructorParamsModel, &ConstructorParams::validOrder, this,
- [errorLabel, button = buttonBox->button(QDialogButtonBox::Ok)](bool valid) {
- button->setEnabled(valid);
- errorLabel->setVisible(!valid);
- });
-
- // setup select all/none checkbox
- QCheckBox *const checkBox = new QCheckBox(Tr::tr("Initialize all members"));
- checkBox->setChecked(true);
- connect(checkBox, &QCheckBox::stateChanged, this,
- [model = constructorParamsModel](int state) {
- if (state != Qt::PartiallyChecked) {
- for (int i = 0; i < model->rowCount(); ++i)
- model->setData(model->index(i, ConstructorParams::ShouldInitColumn),
- state,
- Qt::CheckStateRole);
- }
- });
- connect(checkBox, &QCheckBox::clicked, this, [checkBox] {
- if (checkBox->checkState() == Qt::PartiallyChecked)
- checkBox->setCheckState(Qt::Checked);
- });
- connect(constructorParamsModel,
- &QAbstractItemModel::dataChanged,
- this,
- [model = constructorParamsModel, checkBox] {
- const auto state = [model, selectedCount = model->selectedCount()]() {
- if (selectedCount == 0)
- return Qt::Unchecked;
- if (static_cast<int>(model->memberCount() == selectedCount))
- return Qt::Checked;
- return Qt::PartiallyChecked;
- }();
- checkBox->setCheckState(state);
- });
-
- using A = InsertionPointLocator::AccessSpec;
- auto accessCombo = new QComboBox;
- connect(accessCombo, &QComboBox::currentIndexChanged, this, [this, accessCombo] {
- const auto data = accessCombo->currentData();
- m_accessSpec = static_cast<A>(data.toInt());
- });
- for (auto a : {A::Public, A::Protected, A::Private})
- accessCombo->addItem(InsertionPointLocator::accessSpecToString(a), a);
- const auto row = new QHBoxLayout();
- row->addWidget(new QLabel(Tr::tr("Access") + ":"));
- row->addWidget(accessCombo);
- row->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum));
-
- const auto mainLayout = new QVBoxLayout(this);
- mainLayout->addWidget(
- new QLabel(Tr::tr("Select the members to be initialized in the constructor.\n"
- "Use drag and drop to change the order of the parameters.")));
- mainLayout->addLayout(row);
- mainLayout->addWidget(checkBox);
- mainLayout->addWidget(view);
- mainLayout->addWidget(treeView);
- mainLayout->addWidget(errorLabel);
- mainLayout->addWidget(buttonBox);
- int left, right;
- mainLayout->getContentsMargins(&left, nullptr, &right, nullptr);
- optimalWidth += left + right;
- resize(optimalWidth, mainLayout->sizeHint().height());
- }
-
- InsertionPointLocator::AccessSpec accessSpec() const { return m_accessSpec; }
-
-private:
- InsertionPointLocator::AccessSpec m_accessSpec;
-};
-
-class GenerateConstructorOperation : public CppQuickFixOperation
-{
-public:
- GenerateConstructorOperation(const CppQuickFixInterface &interface)
- : CppQuickFixOperation(interface)
- {
- setDescription(Tr::tr("Generate Constructor"));
-
- m_classAST = astForClassOperations(interface);
- if (!m_classAST)
- return;
- Class *const theClass = m_classAST->symbol;
- if (!theClass)
- return;
-
- // Go through all members and find member variable declarations
- int memberCounter = 0;
- for (auto it = theClass->memberBegin(); it != theClass->memberEnd(); ++it) {
- Symbol *const s = *it;
- if (!s->identifier() || !s->type() || s->type().isTypedef())
- continue;
- if ((s->asDeclaration() && s->type()->asFunctionType()) || s->asFunction())
- continue;
- if (s->asDeclaration() && (s->isPrivate() || s->isProtected()) && !s->isStatic()) {
- const auto name = QString::fromUtf8(s->identifier()->chars(),
- s->identifier()->size());
- parameterModel.emplaceBackParameter(name, s, memberCounter++);
- }
- }
- Overview o = CppCodeStyleSettings::currentProjectCodeStyleOverview();
- o.showArgumentNames = true;
- o.showReturnTypes = true;
- o.showDefaultArguments = true;
- o.showTemplateParameters = true;
- o.showFunctionSignatures = true;
- LookupContext context(currentFile()->cppDocument(), interface.snapshot());
- for (BaseClass *bc : theClass->baseClasses()) {
- const QString className = o.prettyName(bc->name());
-
- ClassOrNamespace *localLookupType = context.lookupType(bc);
- QList<LookupItem> localLookup = localLookupType->lookup(bc->name());
- for (auto &li : localLookup) {
- Symbol *d = li.declaration();
- if (!d->asClass())
- continue;
- for (auto i = d->asClass()->memberBegin(); i != d->asClass()->memberEnd(); ++i) {
- Symbol *s = *i;
- if (s->isProtected() || s->isPublic()) {
- if (s->name()->match(d->name())) {
- // we have found a constructor
- Function *func = s->type().type()->asFunctionType();
- if (!func)
- continue;
- const bool isFirst = parentClassConstructors.empty()
- || parentClassConstructors.back().className
- != className;
- parentClassConstructors.emplace_back(className, parameterModel);
- ParentClassConstructorInfo &constructor = parentClassConstructors.back();
- constructor.declaration = className + o.prettyType(func->type());
- constructor.declaration.replace("std::__1::__get_nullptr_t()",
- "nullptr");
- constructor.useInConstructor = isFirst;
- for (auto arg = func->memberBegin(); arg != func->memberEnd(); ++arg) {
- Symbol *param = *arg;
- Argument *argument = param->asArgument();
- if (!argument) // can also be a block
- continue;
- const QString name = o.prettyName(param->name());
- const StringLiteral *ini = argument->initializer();
- QString defaultValue;
- if (ini)
- defaultValue = QString::fromUtf8(ini->chars(), ini->size())
- .replace("std::__1::__get_nullptr_t()",
- "nullptr");
- constructor.parameters.emplace_back(name,
- defaultValue,
- param,
- &constructor);
- // do not show constructors like QObject(QObjectPrivate & dd, ...)
- ReferenceType *ref = param->type()->asReferenceType();
- if (ref && name == "dd") {
- auto type = o.prettyType(ref->elementType());
- if (type.startsWith("Q") && type.endsWith("Private")) {
- parentClassConstructors.pop_back();
- break;
- }
- }
- }
- }
- }
- }
- }
- }
-
- // add params to parameter lists
- for (auto &c : parentClassConstructors)
- if (c.useInConstructor)
- for (auto &p : c.parameters)
- if (p.init)
- c.addParameter(p);
- }
-
- bool isApplicable() const
- {
- return parameterModel.rowCount() > 0
- || Utils::anyOf(parentClassConstructors,
- [](const auto &parent) { return !parent.parameters.empty(); });
- }
-
- void setTest(bool isTest = true) { m_test = isTest; }
-
-private:
- void perform() override
- {
- auto infos = parameterModel.getInfos();
-
- InsertionPointLocator::AccessSpec accessSpec = InsertionPointLocator::Public;
- if (!m_test) {
- GenerateConstructorDialog dlg(&parameterModel, parentClassConstructors);
- if (dlg.exec() == QDialog::Rejected)
- return;
- accessSpec = dlg.accessSpec();
- infos = parameterModel.getInfos();
- } else {
-#ifdef WITH_TESTS
- ParentClassesModel model(nullptr, parentClassConstructors);
- QAbstractItemModelTester tester(&model);
-#endif
- if (infos.size() >= 3) {
- // if we are testing and have 3 or more members => change the order
- // move first element to the back
- infos.push_back(infos[0]);
- infos.erase(infos.begin());
- }
- for (auto info : infos) {
- if (info->memberVariableName.startsWith("di_"))
- info->defaultValue = "42";
- }
- for (auto &c : parentClassConstructors) {
- if (c.useInConstructor) {
- for (auto &p : c.parameters) {
- if (!p.init && p.parameterName.startsWith("use_")) {
- infos.push_back(&p);
- p.init = true;
- }
- }
- }
- }
- }
- if (infos.empty())
- return;
- struct GenerateConstructorRefactoringHelper : public GetterSetterRefactoringHelper
- {
- const ClassSpecifierAST *m_classAST;
- InsertionPointLocator::AccessSpec m_accessSpec;
- GenerateConstructorRefactoringHelper(CppQuickFixOperation *operation,
- const FilePath &filePath,
- Class *clazz,
- const ClassSpecifierAST *classAST,
- InsertionPointLocator::AccessSpec accessSpec)
- : GetterSetterRefactoringHelper(operation, filePath, clazz)
- , m_classAST(classAST)
- , m_accessSpec(accessSpec)
- {}
- void generateConstructor(std::vector<ConstructorMemberInfo *> members,
- const ParentClassConstructors &parentClassConstructors)
- {
- auto constructorLocation = m_settings->determineSetterLocation(int(members.size()));
- if (constructorLocation == CppQuickFixSettings::FunctionLocation::CppFile
- && !hasSourceFile())
- constructorLocation = CppQuickFixSettings::FunctionLocation::OutsideClass;
-
- Overview overview = CppCodeStyleSettings::currentProjectCodeStyleOverview();
- overview.showTemplateParameters = true;
-
- InsertionLocation implLoc;
- QString implCode;
- CppRefactoringFilePtr implFile;
- QString className = overview.prettyName(m_class->name());
- QStringList insertedNamespaces;
- if (constructorLocation == CppQuickFixSettings::FunctionLocation::CppFile) {
- implLoc = sourceLocationFor(m_class, &insertedNamespaces);
- implFile = m_sourceFile;
- if (m_settings->rewriteTypesinCppFile())
- implCode = symbolAt(m_class, m_sourceFile, implLoc);
- else
- implCode = className;
- implCode += "::" + className + "(";
- } else if (constructorLocation
- == CppQuickFixSettings::FunctionLocation::OutsideClass) {
- implLoc = insertLocationForMethodDefinition(m_class,
- false,
- NamespaceHandling::Ignore,
- m_changes,
- m_headerFile->filePath(),
- &insertedNamespaces);
- implFile = m_headerFile;
- implCode = symbolAt(m_class, m_headerFile, implLoc);
- implCode += "::" + className + "(";
- }
-
- QString inClassDeclaration = overview.prettyName(m_class->name()) + "(";
- QString constructorBody = members.empty() ? QString(") {}") : QString(") : ");
- for (auto &member : members) {
- if (isValueType(member->symbol, &member->customValueType))
- member->type.setConst(false);
- else
- member->type = makeConstRef(member->type);
-
- inClassDeclaration += overview.prettyType(member->type, member->parameterName);
- if (!member->defaultValue.isEmpty())
- inClassDeclaration += " = " + member->defaultValue;
- inClassDeclaration += ", ";
- if (implFile) {
- FullySpecifiedType type = typeAt(member->type,
- m_class,
- implFile,
- implLoc,
- insertedNamespaces);
- implCode += overview.prettyType(type, member->parameterName) + ", ";
- }
- }
- Utils::sort(members, &ConstructorMemberInfo::numberOfMember);
- // first, do the base classes
- for (const auto &parent : parentClassConstructors) {
- if (!parent.useInConstructor)
- continue;
- // Check if we really need a constructor
- if (Utils::anyOf(parent.parameters, [](const auto &param) {
- return param.init || param.originalDefaultValue.isEmpty();
- })) {
- int defaultAtEndCount = 0;
- for (auto i = parent.parameters.crbegin(); i != parent.parameters.crend();
- ++i) {
- if (i->init || i->originalDefaultValue.isEmpty())
- break;
- ++defaultAtEndCount;
- }
- const int numberOfParameters = static_cast<int>(parent.parameters.size())
- - defaultAtEndCount;
- constructorBody += parent.className + "(";
- int counter = 0;
- for (const auto &param : parent.parameters) {
- if (++counter > numberOfParameters)
- break;
- if (param.init) {
- if (param.customValueType)
- constructorBody += "std::move(" + param.parameterName + ')';
- else
- constructorBody += param.parameterName;
- } else if (!param.originalDefaultValue.isEmpty())
- constructorBody += param.originalDefaultValue;
- else
- constructorBody += "/* insert value */";
- constructorBody += ", ";
- }
- constructorBody.resize(constructorBody.length() - 2);
- constructorBody += "),\n";
- }
- }
- for (auto &member : members) {
- if (member->parentClassConstructor)
- continue;
- QString param = member->parameterName;
- if (member->customValueType)
- param = "std::move(" + member->parameterName + ')';
- constructorBody += member->memberVariableName + '(' + param + "),\n";
- }
- if (!members.empty()) {
- inClassDeclaration.resize(inClassDeclaration.length() - 2);
- constructorBody.remove(constructorBody.length() - 2, 1); // ..),\n => ..)\n
- constructorBody += "{}";
- if (!implCode.isEmpty())
- implCode.resize(implCode.length() - 2);
- }
- implCode += constructorBody;
-
- if (constructorLocation == CppQuickFixSettings::FunctionLocation::InsideClass)
- inClassDeclaration += constructorBody;
- else
- inClassDeclaration += QLatin1String(");");
-
- TranslationUnit *tu = m_headerFile->cppDocument()->translationUnit();
- insertAndIndent(m_headerFile,
- m_locator.constructorDeclarationInClass(tu,
- m_classAST,
- m_accessSpec,
- int(members.size())),
- inClassDeclaration);
-
- if (constructorLocation == CppQuickFixSettings::FunctionLocation::CppFile) {
- addSourceFileCode(implCode);
- } else if (constructorLocation
- == CppQuickFixSettings::FunctionLocation::OutsideClass) {
- if (m_isHeaderHeaderFile)
- implCode.prepend("inline ");
- insertAndIndent(m_headerFile, implLoc, implCode);
- }
- }
- };
- GenerateConstructorRefactoringHelper helper(this,
- currentFile()->filePath(),
- m_classAST->symbol,
- m_classAST,
- accessSpec);
-
- auto members = Utils::filtered(infos, [](const auto mi) {
- return mi->init || mi->parentClassConstructor;
- });
- helper.generateConstructor(std::move(members), parentClassConstructors);
- helper.applyChanges();
- }
-
- ConstructorParams parameterModel;
- ParentClassConstructors parentClassConstructors;
- const ClassSpecifierAST *m_classAST = nullptr;
- bool m_test = false;
-};
-} // namespace
-void GenerateConstructor::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- const auto op = QSharedPointer<GenerateConstructorOperation>::create(interface);
- if (!op->isApplicable())
- return;
- op->setTest(m_test);
- result << op;
-}
-
-namespace {
-class ConvertCommentStyleOp : public CppQuickFixOperation
-{
-public:
- ConvertCommentStyleOp(const CppQuickFixInterface &interface, const QList<Token> &tokens,
- Kind kind)
- : CppQuickFixOperation(interface),
- m_tokens(tokens),
- m_kind(kind),
- m_wasCxxStyle(m_kind == T_CPP_COMMENT || m_kind == T_CPP_DOXY_COMMENT),
- m_isDoxygen(m_kind == T_DOXY_COMMENT || m_kind == T_CPP_DOXY_COMMENT)
- {
- setDescription(m_wasCxxStyle ? Tr::tr("Convert Comment to C-Style")
- : Tr::tr("Convert Comment to C++-Style"));
- }
-
-private:
- // Turns every line of a C-style comment into a C++-style comment and vice versa.
- // For C++ -> C, we use one /* */ comment block per line. However, doxygen
- // requires a single comment, so there we just replace the prefix with whitespace and
- // add the start and end comment in extra lines.
- // For cosmetic reasons, we offer some convenience functionality:
- // - Turn /***** ... into ////// ... and vice versa
- // - With C -> C++, remove leading asterisks.
- // - With C -> C++, remove the first and last line of a block if they have no content
- // other than the comment start and end characters.
- // - With C++ -> C, try to align the end comment characters.
- // These are obviously heuristics; we do not guarantee perfect results for everybody.
- // We also don't second-guess the users's selection: E.g. if there is an empty
- // line between the tokens, then it's not the same doxygen comment, but we merge
- // it anyway in C++ to C mode.
- void perform() override
- {
- TranslationUnit * const tu = currentFile()->cppDocument()->translationUnit();
- const QString newCommentStart = getNewCommentStart();
- ChangeSet changeSet;
- int endCommentColumn = -1;
- const QChar oldFillChar = m_wasCxxStyle ? '/' : '*';
- const QChar newFillChar = m_wasCxxStyle ? '*' : '/';
-
- for (const Token &token : m_tokens) {
- const int startPos = tu->getTokenPositionInDocument(token, textDocument());
- const int endPos = tu->getTokenEndPositionInDocument(token, textDocument());
-
- if (m_wasCxxStyle && m_isDoxygen) {
- // Replace "///" characters with whitespace (to keep alignment).
- // The insertion of "/*" and "*/" is done once after the loop.
- changeSet.replace(startPos, startPos + 3, " ");
- continue;
- }
-
- const QTextBlock firstBlock = textDocument()->findBlock(startPos);
- const QTextBlock lastBlock = textDocument()->findBlock(endPos);
- for (QTextBlock block = firstBlock; block.isValid() && block.position() <= endPos;
- block = block.next()) {
- const QString &blockText = block.text();
- const int firstColumn = block == firstBlock ? startPos - block.position() : 0;
- const int endColumn = block == lastBlock ? endPos - block.position()
- : block.length();
-
- // Returns true if the current line looks like "/********/" or "//////////",
- // as is often the case at the start and end of comment blocks.
- const auto fillChecker = [&] {
- if (m_isDoxygen)
- return false;
- QString textToCheck = blockText;
- if (block == firstBlock)
- textToCheck.remove(0, 1);
- if (block == lastBlock)
- textToCheck.chop(block.length() - endColumn);
- return Utils::allOf(textToCheck, [oldFillChar](const QChar &c)
- { return c == oldFillChar || c == ' ';
- }) && textToCheck.count(oldFillChar) > 2;
- };
-
- // Returns the index of the first character of actual comment content,
- // as opposed to visual stuff like slashes, stars or whitespace.
- const auto indexOfActualContent = [&] {
- const int offset = block == firstBlock ? firstColumn + newCommentStart.length()
- : firstColumn;
-
- for (int i = offset, lastFillChar = -1; i < blockText.length(); ++i) {
- if (blockText.at(i) == oldFillChar) {
- lastFillChar = i;
- continue;
- }
- if (!blockText.at(i).isSpace())
- return lastFillChar + 1;
- }
- return -1;
- };
-
- if (fillChecker()) {
- const QString replacement = QString(endColumn - 1 - firstColumn, newFillChar);
- changeSet.replace(block.position() + firstColumn,
- block.position() + endColumn - 1,
- replacement);
- if (m_wasCxxStyle) {
- changeSet.replace(block.position() + firstColumn,
- block.position() + firstColumn + 1, "/");
- changeSet.insert(block.position() + endColumn - 1, "*");
- endCommentColumn = endColumn - 1;
- }
- continue;
- }
-
- // Remove leading noise or even the entire block, if applicable.
- const bool blockIsRemovable = (block == firstBlock || block == lastBlock)
- && firstBlock != lastBlock;
- const auto removeBlock = [&] {
- changeSet.remove(block.position() + firstColumn, block.position() + endColumn);
- };
- const int contentIndex = indexOfActualContent();
- int removed = 0;
- if (contentIndex == -1) {
- if (blockIsRemovable) {
- removeBlock();
- continue;
- } else if (!m_wasCxxStyle) {
- changeSet.replace(block.position() + firstColumn,
- block.position() + endColumn - 1, newCommentStart);
- continue;
- }
- } else if (block == lastBlock && contentIndex == endColumn - 1) {
- if (blockIsRemovable) {
- removeBlock();
- break;
- }
- } else {
- changeSet.remove(block.position() + firstColumn,
- block.position() + firstColumn + contentIndex);
- removed = contentIndex;
- }
-
- if (block == firstBlock) {
- changeSet.replace(startPos, startPos + newCommentStart.length(),
- newCommentStart);
- } else {
- // If the line starts with enough whitespace, replace it with the
- // comment start characters, so we don't move the content to the right
- // unnecessarily. Otherwise, insert the comment start characters.
- if (blockText.startsWith(QString(newCommentStart.size() + removed + 1, ' '))) {
- changeSet.replace(block.position(),
- block.position() + newCommentStart.length(),
- newCommentStart);
- } else {
- changeSet.insert(block.position(), newCommentStart);
- }
- }
-
- if (block == lastBlock) {
- if (m_wasCxxStyle) {
- // This is for proper alignment of the end comment character.
- if (endCommentColumn != -1) {
- const int endCommentPos = block.position() + endCommentColumn;
- if (endPos < endCommentPos)
- changeSet.insert(endPos, QString(endCommentPos - endPos - 1, ' '));
- }
- changeSet.insert(endPos, " */");
- } else {
- changeSet.remove(endPos - 2, endPos);
- }
- }
- }
- }
-
- if (m_wasCxxStyle && m_isDoxygen) {
- const int startPos = tu->getTokenPositionInDocument(m_tokens.first(), textDocument());
- const int endPos = tu->getTokenEndPositionInDocument(m_tokens.last(), textDocument());
- changeSet.insert(startPos, "/*!\n");
- changeSet.insert(endPos, "\n*/");
- }
-
- changeSet.apply(textDocument());
- }
-
- QString getNewCommentStart() const
- {
- if (m_wasCxxStyle) {
- if (m_isDoxygen)
- return "/*!";
- return "/*";
- }
- if (m_isDoxygen)
- return "//!";
- return "//";
- }
-
- const QList<Token> m_tokens;
- const Kind m_kind;
- const bool m_wasCxxStyle;
- const bool m_isDoxygen;
-};
-} // namespace
-
-void ConvertCommentStyle::doMatch(const CppQuickFixInterface &interface,
- TextEditor::QuickFixOperations &result)
-{
- // If there's a selection, then it must entirely consist of comment tokens.
- // If there's no selection, the cursor must be on a comment.
- const QList<Token> &cursorTokens = interface.currentFile()->tokensForCursor();
- if (cursorTokens.empty())
- return;
- if (!cursorTokens.front().isComment())
- return;
-
- // All tokens must be the same kind of comment, but we make an exception for doxygen comments
- // that start with "///", as these are often not intended to be doxygen. For our purposes,
- // we treat them as normal comments.
- const auto effectiveKind = [&interface](const Token &token) {
- if (token.kind() != T_CPP_DOXY_COMMENT)
- return token.kind();
- TranslationUnit * const tu = interface.currentFile()->cppDocument()->translationUnit();
- const int startPos = tu->getTokenPositionInDocument(token, interface.textDocument());
- const QString commentStart = interface.textAt(startPos, 3);
- return commentStart == "///" ? T_CPP_COMMENT : T_CPP_DOXY_COMMENT;
- };
- const Kind kind = effectiveKind(cursorTokens.first());
- for (int i = 1; i < cursorTokens.count(); ++i) {
- if (effectiveKind(cursorTokens.at(i)) != kind)
- return;
- }
-
- // Ok, all tokens are of same(ish) comment type, offer quickfix.
- result << new ConvertCommentStyleOp(interface, cursorTokens, kind);
-}
-
-namespace {
-class MoveFunctionCommentsOp : public CppQuickFixOperation
-{
-public:
- enum class Direction { ToDecl, ToDef };
- MoveFunctionCommentsOp(const CppQuickFixInterface &interface, const Symbol *symbol,
- const QList<Token> &commentTokens, Direction direction)
- : CppQuickFixOperation(interface), m_symbol(symbol), m_commentTokens(commentTokens)
- {
- setDescription(direction == Direction::ToDecl
- ? Tr::tr("Move Function Documentation to Declaration")
- : Tr::tr("Move Function Documentation to Definition"));
- }
-
-private:
- void perform() override
- {
- const auto textDoc = const_cast<QTextDocument *>(currentFile()->document());
- const int pos = currentFile()->cppDocument()->translationUnit()->getTokenPositionInDocument(
- m_symbol->sourceLocation(), textDoc);
- QTextCursor cursor(textDoc);
- cursor.setPosition(pos);
- const CursorInEditor cursorInEditor(cursor, currentFile()->filePath(), editor(),
- editor()->textDocument());
- const auto callback = [symbolLoc = m_symbol->toLink(), comments = m_commentTokens]
- (const Link &link) {
- moveComments(link, symbolLoc, comments);
- };
- CppModelManager::followSymbol(cursorInEditor, callback, true, false,
- FollowSymbolMode::Exact);
- }
-
- static void moveComments(const Link &targetLoc, const Link &symbolLoc,
- const QList<Token> &comments)
- {
- if (!targetLoc.hasValidTarget() || targetLoc.hasSameLocation(symbolLoc))
- return;
-
- CppRefactoringChanges changes(CppModelManager::snapshot());
- const CppRefactoringFilePtr sourceFile = changes.cppFile(symbolLoc.targetFilePath);
- const CppRefactoringFilePtr targetFile
- = targetLoc.targetFilePath == symbolLoc.targetFilePath
- ? sourceFile
- : changes.cppFile(targetLoc.targetFilePath);
- const Document::Ptr &targetCppDoc = targetFile->cppDocument();
- const QList<AST *> targetAstPath = ASTPath(targetCppDoc)(
- targetLoc.targetLine, targetLoc.targetColumn + 1);
- if (targetAstPath.isEmpty())
- return;
- const AST *targetDeclAst = nullptr;
- for (auto it = std::next(std::rbegin(targetAstPath));
- it != std::rend(targetAstPath); ++it) {
- AST * const node = *it;
- if (node->asDeclaration()) {
- targetDeclAst = node;
- continue;
- }
- if (targetDeclAst)
- break;
- }
- if (!targetDeclAst)
- return;
- const int insertionPos = targetCppDoc->translationUnit()->getTokenPositionInDocument(
- targetDeclAst->firstToken(), targetFile->document());
- const TranslationUnit * const sourceTu = sourceFile->cppDocument()->translationUnit();
- const int sourceCommentStartPos = sourceTu->getTokenPositionInDocument(
- comments.first(), sourceFile->document());
- const int sourceCommentEndPos = sourceTu->getTokenEndPositionInDocument(
- comments.last(), sourceFile->document());
-
- // Manually adjust indentation, as both our built-in indenter and ClangFormat
- // are unreliable with regards to comment continuation lines.
- auto tabSettings = [](CppRefactoringFilePtr file) {
- if (auto editor = file->editor())
- return editor->textDocument()->tabSettings();
- return ProjectExplorer::actualTabSettings(file->filePath(), nullptr);
- };
- const TabSettings &sts = tabSettings(sourceFile);
- const TabSettings &tts = tabSettings(targetFile);
- const QTextBlock insertionBlock = targetFile->document()->findBlock(insertionPos);
- const int insertionColumn = tts.columnAt(insertionBlock.text(),
- insertionPos - insertionBlock.position());
- const QTextBlock removalBlock = sourceFile->document()->findBlock(sourceCommentStartPos);
- const QTextBlock removalBlockEnd = sourceFile->document()->findBlock(sourceCommentEndPos);
- const int removalColumn = sts.columnAt(removalBlock.text(),
- sourceCommentStartPos - removalBlock.position());
- const int columnOffset = insertionColumn - removalColumn;
- QString functionDoc;
- if (columnOffset != 0) {
- for (QTextBlock block = removalBlock;
- block.isValid() && block != removalBlockEnd.next();
- block = block.next()) {
- QString text = block.text() + QChar::ParagraphSeparator;
- if (block == removalBlockEnd)
- text = text.left(sourceCommentEndPos - block.position());
- if (block == removalBlock) {
- text = text.mid(sourceCommentStartPos - block.position());
- } else {
- int lineIndentColumn = sts.indentationColumn(text) + columnOffset;
- text.replace(0,
- TabSettings::firstNonSpace(text),
- tts.indentationString(0, lineIndentColumn, 0, insertionBlock));
- }
- functionDoc += text;
- }
- } else {
- functionDoc = sourceFile->textOf(sourceCommentStartPos, sourceCommentEndPos);
- }
-
- // Remove comment plus leading and trailing whitespace, including trailing newline.
- const auto removeAtSource = [&](ChangeSet &changeSet) {
- int removalPos = sourceCommentStartPos;
- const QChar newline(QChar::ParagraphSeparator);
- while (true) {
- const int prev = removalPos - 1;
- if (prev < 0)
- break;
- const QChar prevChar = sourceFile->charAt(prev);
- if (!prevChar.isSpace() || prevChar == newline)
- break;
- removalPos = prev;
- }
- int removalEndPos = sourceCommentEndPos;
- while (true) {
- if (removalEndPos == sourceFile->document()->characterCount())
- break;
- const QChar nextChar = sourceFile->charAt(removalEndPos);
- if (!nextChar.isSpace())
- break;
- ++removalEndPos;
- if (nextChar == newline)
- break;
- }
- changeSet.remove(removalPos, removalEndPos);
- };
-
- ChangeSet targetChangeSet;
- targetChangeSet.insert(insertionPos, functionDoc);
- targetChangeSet.insert(insertionPos, "\n");
- targetChangeSet.insert(insertionPos, QString(insertionColumn, ' '));
- if (targetFile == sourceFile)
- removeAtSource(targetChangeSet);
- targetFile->setChangeSet(targetChangeSet);
- const bool targetFileSuccess = targetFile->apply();
- if (targetFile == sourceFile || !targetFileSuccess)
- return;
- ChangeSet sourceChangeSet;
- removeAtSource(sourceChangeSet);
- sourceFile->setChangeSet(sourceChangeSet);
- sourceFile->apply();
- }
-
- const Symbol * const m_symbol;
- const QList<Token> m_commentTokens;
-};
-} // namespace
-
-void MoveFunctionComments::doMatch(const CppQuickFixInterface &interface,
- TextEditor::QuickFixOperations &result)
-{
- const QList<AST *> &astPath = interface.path();
- if (astPath.isEmpty())
- return;
- const Symbol *symbol = nullptr;
- MoveFunctionCommentsOp::Direction direction = MoveFunctionCommentsOp::Direction::ToDecl;
- for (auto it = std::next(std::rbegin(astPath)); it != std::rend(astPath); ++it) {
- if (const auto func = (*it)->asFunctionDefinition()) {
- symbol = func->symbol;
- direction = MoveFunctionCommentsOp::Direction::ToDecl;
- break;
- }
- const auto decl = (*it)->asSimpleDeclaration();
- if (!decl || !decl->declarator_list)
- continue;
- for (auto it = decl->declarator_list->begin();
- !symbol && it != decl->declarator_list->end(); ++it) {
- PostfixDeclaratorListAST * const funcDecls = (*it)->postfix_declarator_list;
- if (!funcDecls)
- continue;
- for (auto it = funcDecls->begin(); it != funcDecls->end(); ++it) {
- if (const auto func = (*it)->asFunctionDeclarator()) {
- symbol = func->symbol;
- direction = MoveFunctionCommentsOp::Direction::ToDef;
- break;
- }
- }
- }
-
- }
- if (!symbol)
- return;
-
- if (const QList<Token> commentTokens = commentsForDeclaration(
- symbol, *interface.textDocument(), interface.currentFile()->cppDocument());
- !commentTokens.isEmpty()) {
- result << new MoveFunctionCommentsOp(interface, symbol, commentTokens, direction);
- }
-}
-
-namespace {
-class ConvertToMetaMethodCallOp : public CppQuickFixOperation
-{
-public:
- ConvertToMetaMethodCallOp(const CppQuickFixInterface &interface, CallAST *callAst)
- : CppQuickFixOperation(interface), m_callAst(callAst)
- {
- setDescription(Tr::tr("Convert function call to Qt meta-method invocation"));
- }
-
-private:
- void perform() override
- {
- // Construct the argument list.
- Overview ov;
- QStringList arguments;
- for (ExpressionListAST *it = m_callAst->expression_list; it; it = it->next) {
- if (!it->value)
- return;
- const FullySpecifiedType argType
- = typeOfExpr(it->value, currentFile(), snapshot(), context());
- if (!argType.isValid())
- return;
- arguments << QString::fromUtf8("Q_ARG(%1, %2)")
- .arg(ov.prettyType(argType), currentFile()->textOf(it->value));
- }
- QString argsString = arguments.join(", ");
- if (!argsString.isEmpty())
- argsString.prepend(", ");
-
- // Construct the replace string.
- const auto memberAccessAst = m_callAst->base_expression->asMemberAccess();
- QTC_ASSERT(memberAccessAst, return);
- QString baseExpr = currentFile()->textOf(memberAccessAst->base_expression);
- const FullySpecifiedType baseExprType
- = typeOfExpr(memberAccessAst->base_expression, currentFile(), snapshot(), context());
- if (!baseExprType.isValid())
- return;
- if (!baseExprType->asPointerType())
- baseExpr.prepend('&');
- const QString functionName = currentFile()->textOf(memberAccessAst->member_name);
- const QString qMetaObject = "QMetaObject";
- const QString newCall = QString::fromUtf8("%1::invokeMethod(%2, \"%3\"%4)")
- .arg(qMetaObject, baseExpr, functionName, argsString);
-
- // Determine the start and end positions of the replace operation.
- // If the call is preceded by an "emit" keyword, that one has to be removed as well.
- int firstToken = m_callAst->firstToken();
- if (firstToken > 0)
- switch (semanticInfo().doc->translationUnit()->tokenKind(firstToken - 1)) {
- case T_EMIT: case T_Q_EMIT: --firstToken; break;
- default: break;
- }
- const TranslationUnit *const tu = semanticInfo().doc->translationUnit();
- const int startPos = tu->getTokenPositionInDocument(firstToken, textDocument());
- const int endPos = tu->getTokenPositionInDocument(m_callAst->lastToken(), textDocument());
-
- // Replace the old call with the new one.
- ChangeSet changes;
- changes.replace(startPos, endPos, newCall);
-
- // Insert include for QMetaObject, if necessary.
- const Identifier qMetaObjectId(qPrintable(qMetaObject), qMetaObject.size());
- Scope * const scope = currentFile()->scopeAt(firstToken);
- const QList<LookupItem> results = context().lookup(&qMetaObjectId, scope);
- bool isDeclared = false;
- for (const LookupItem &item : results) {
- if (Symbol *declaration = item.declaration(); declaration && declaration->asClass()) {
- isDeclared = true;
- break;
- }
- }
- if (!isDeclared) {
- insertNewIncludeDirective('<' + qMetaObject + '>', currentFile(), semanticInfo().doc,
- changes);
- }
-
- // Apply the changes.
- currentFile()->setChangeSet(changes);
- currentFile()->apply();
- }
-
- const CallAST * const m_callAst;
-};
-} // namespace
-
-void ConvertToMetaMethodCall::doMatch(const CppQuickFixInterface &interface,
- TextEditor::QuickFixOperations &result)
-{
- const Document::Ptr &cppDoc = interface.currentFile()->cppDocument();
- const QList<AST *> path = ASTPath(cppDoc)(interface.cursor());
- if (path.isEmpty())
- return;
-
- // Are we on a member function call?
- CallAST *callAst = nullptr;
- for (auto it = path.crbegin(); it != path.crend(); ++it) {
- if ((callAst = (*it)->asCall()))
- break;
- }
- if (!callAst || !callAst->base_expression)
- return;
- ExpressionAST *baseExpr = nullptr;
- const NameAST *nameAst = nullptr;
- if (const MemberAccessAST * const ast = callAst->base_expression->asMemberAccess()) {
- baseExpr = ast->base_expression;
- nameAst = ast->member_name;
- }
- if (!baseExpr || !nameAst || !nameAst->name)
- return;
-
- // Locate called function and check whether it is invokable.
- Scope *scope = cppDoc->globalNamespace();
- for (auto it = path.crbegin(); it != path.crend(); ++it) {
- if (const CompoundStatementAST * const stmtAst = (*it)->asCompoundStatement()) {
- scope = stmtAst->symbol;
- break;
- }
- }
- const LookupContext context(cppDoc, interface.snapshot());
- TypeOfExpression exprType;
- exprType.setExpandTemplates(true);
- exprType.init(cppDoc, interface.snapshot());
- const QList<LookupItem> typeMatches = exprType(callAst->base_expression, cppDoc, scope);
- for (const LookupItem &item : typeMatches) {
- if (const auto func = item.type()->asFunctionType(); func && func->methodKey()) {
- result << new ConvertToMetaMethodCallOp(interface, callAst);
- return;
- }
- }
-}
-
-namespace {
-class MoveClassToOwnFileOp : public CppQuickFixOperation
-{
-public:
- MoveClassToOwnFileOp(
- const CppQuickFixInterface &interface,
- AST *fullDecl,
- ClassSpecifierAST *classAst,
- const QList<Namespace *> &namespacePath,
- bool interactive)
- : CppQuickFixOperation(interface)
- , m_state(std::make_shared<State>())
- {
- setDescription(Tr::tr("Move class to a dedicated set of source files"));
- m_state->originalFilePath = interface.currentFile()->filePath();
- m_state->classAst = classAst;
- m_state->namespacePath = namespacePath;
- m_state->interactive = interactive;
- PerFileState &perFileState = m_state->perFileState[interface.currentFile()->filePath()];
- perFileState.refactoringFile = interface.currentFile();
- perFileState.declarationsToMove << fullDecl;
- }
-
-private:
- struct PerFileState {
- // We want to keep the relative order of moved code.
- void insertSorted(AST *decl) {
- declarationsToMove.insert(std::lower_bound(
- declarationsToMove.begin(),
- declarationsToMove.end(),
- decl,
- [](const AST *elem, const AST *value) {
- return elem->firstToken() < value->firstToken();
- }), decl);
- }
-
- CppRefactoringFilePtr refactoringFile;
- QList<AST *> declarationsToMove;
- };
- struct State {
- using Ptr = std::shared_ptr<State>;
-
- FilePath originalFilePath;
- AST *fullDecl = nullptr;
- ClassSpecifierAST *classAst = nullptr;
- QList<Namespace *> namespacePath;
- Links lookupResults;
- QMap<FilePath, PerFileState> perFileState; // A map for deterministic order of moved code.
- CppRefactoringChanges factory{CppModelManager::snapshot()};
- int remainingFollowSymbolOps = 0;
- bool interactive = true;
- };
- class Dialog : public QDialog {
- public:
- Dialog(const FilePath &defaultHeaderFilePath, const FilePath &defaultSourceFilePath,
- ProjectNode *defaultProjectNode)
- : m_buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel)
- {
- ProjectNode * const rootNode = defaultProjectNode
- ? defaultProjectNode->getProject()->rootProjectNode()
- : nullptr;
- if (rootNode) {
- const auto projectRootItem = new NodeItem(rootNode);
- buildTree(projectRootItem);
- m_projectModel.rootItem()->appendChild(projectRootItem);
- }
- m_projectNodeComboBox.setModel(&m_projectModel);
- if (defaultProjectNode) {
- const auto matcher = [defaultProjectNode](TreeItem *item) {
- return static_cast<NodeItem *>(item)->node == defaultProjectNode;
- };
- TreeItem * const defaultItem = m_projectModel.rootItem()->findAnyChild(matcher);
- if (defaultItem ) {
- QModelIndex index = m_projectModel.indexForItem(defaultItem);
- m_projectNodeComboBox.setCurrentIndex(index);
- while (index.isValid()) {
- m_projectNodeComboBox.view()->expand(index);
- index = index.parent();
- }
- }
-
- }
- connect(&m_projectNodeComboBox, &QComboBox::currentIndexChanged,
- this, [this] {
- if (m_filesEdited)
- return;
- const auto newProjectNode = projectNode();
- QTC_ASSERT(newProjectNode, return);
- const FilePath baseDir = newProjectNode->directory();
- m_sourcePathChooser.setFilePath(
- baseDir.pathAppended(sourceFilePath().fileName()));
- m_headerPathChooser.setFilePath(
- baseDir.pathAppended(headerFilePath().fileName()));
- m_filesEdited = false;
- });
-
- m_headerOnlyCheckBox.setText(Tr::tr("Header file only"));
- m_headerOnlyCheckBox.setChecked(false);
- connect(&m_headerOnlyCheckBox, &QCheckBox::toggled,
- this, [this](bool checked) { m_sourcePathChooser.setEnabled(!checked); });
-
- m_headerPathChooser.setExpectedKind(PathChooser::SaveFile);
- m_sourcePathChooser.setExpectedKind(PathChooser::SaveFile);
- m_headerPathChooser.setFilePath(defaultHeaderFilePath);
- m_sourcePathChooser.setFilePath(defaultSourceFilePath);
- connect(&m_headerPathChooser, &PathChooser::textChanged,
- this, [this] { m_filesEdited = true; });
- connect(&m_sourcePathChooser, &PathChooser::textChanged,
- this, [this] { m_filesEdited = true; });
-
- connect(&m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
- connect(&m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
-
- using namespace Layouting;
- Column {
- Form {
- Tr::tr("Project:"), &m_projectNodeComboBox, br,
- &m_headerOnlyCheckBox, br,
- Tr::tr("Header file:"), &m_headerPathChooser, br,
- Tr::tr("Implementation file:"), &m_sourcePathChooser, br,
- },
- &m_buttonBox
- }.attachTo(this);
- }
-
- ProjectNode *projectNode() const
- {
- const QVariant v = m_projectNodeComboBox.currentData(Qt::UserRole);
- return v.isNull() ? nullptr : static_cast<ProjectNode *>(v.value<void *>());
- }
- bool createSourceFile() const { return !m_headerOnlyCheckBox.isChecked(); }
- FilePath headerFilePath() const { return m_headerPathChooser.absoluteFilePath(); }
- FilePath sourceFilePath() const { return m_sourcePathChooser.absoluteFilePath(); }
-
- private:
- struct NodeItem : public StaticTreeItem {
- NodeItem(ProjectNode *node)
- : StaticTreeItem({node->displayName()}, {node->directory().toUserOutput()})
- , node(node)
- {}
- Qt::ItemFlags flags(int) const override
- {
- return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
- }
- QVariant data(int column, int role) const override
- {
- if (role == Qt::UserRole)
- return QVariant::fromValue(static_cast<void *>(node));
- return StaticTreeItem::data(column, role);
- }
-
- ProjectNode * const node;
- };
-
- void buildTree(NodeItem *parent)
- {
- for (Node * const node : parent->node->nodes()) {
- if (const auto projNode = node->asProjectNode()) {
- const auto child = new NodeItem(projNode);
- buildTree(child);
- parent->appendChild(child);
- }
- }
- }
-
- TreeViewComboBox m_projectNodeComboBox;
- QCheckBox m_headerOnlyCheckBox;
- PathChooser m_headerPathChooser;
- PathChooser m_sourcePathChooser;
- QDialogButtonBox m_buttonBox;
- TreeModel<> m_projectModel;
- bool m_filesEdited = false;
- };
-
- void perform() override
- {
- collectImplementations(m_state->classAst->symbol, m_state);
- if (m_state->remainingFollowSymbolOps == 0)
- finish(m_state);
- }
-
- static CppRefactoringFilePtr getRefactoringFile(const FilePath &filePath, const State::Ptr &state)
- {
- CppRefactoringFilePtr &refactoringFile = state->perFileState[filePath].refactoringFile;
- if (refactoringFile)
- return refactoringFile;
- CppEditorWidget *editorWidget = nullptr;
- const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForFilePath(filePath);
- for (Core::IEditor *editor : editors) {
- const auto textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor);
- if (textEditor)
- editorWidget = qobject_cast<CppEditorWidget *>(textEditor->editorWidget());
- if (editorWidget)
- break;
- }
- refactoringFile = editorWidget
- ? state->factory.file(editorWidget, editorWidget->semanticInfo().doc)
- : state->factory.cppFile(filePath);
- return refactoringFile;
- }
-
- static void lookupSymbol(Symbol *symbol, const State::Ptr &state)
- {
- const CppRefactoringFilePtr refactoringFile = getRefactoringFile(symbol->filePath(), state);
- const auto editorWidget = qobject_cast<CppEditorWidget *>(refactoringFile->editor());
- QTextCursor cursor(refactoringFile->document()->begin());
- TranslationUnit * const tu = refactoringFile->cppDocument()->translationUnit();
- const int symbolPos = tu->getTokenPositionInDocument(symbol->sourceLocation(),
- refactoringFile->document());
- cursor.setPosition(symbolPos);
- const CursorInEditor cursorInEditor(
- cursor,
- symbol->filePath(),
- editorWidget,
- editorWidget ? editorWidget->textDocument() : nullptr,
- refactoringFile->cppDocument());
- const auto callback = [symbol, symbolPos, doc = cursor.document(), state](const Link &link) {
- class FinishedChecker {
- public:
- FinishedChecker(const State::Ptr &state) : m_state(state) {}
- ~FinishedChecker() {
- if (--m_state->remainingFollowSymbolOps == 0)
- finish(m_state);
- };
- private:
- const State::Ptr &m_state;
- } finishedChecker(state);
- if (!link.hasValidTarget())
- return;
- if (symbol->filePath() == link.targetFilePath) {
- const int linkPos = Text::positionInText(doc, link.targetLine,
- link.targetColumn + 1);
- if (linkPos == symbolPos)
- return;
- }
- const CppRefactoringFilePtr refactoringFile
- = getRefactoringFile(link.targetFilePath, state);
- const QList<AST *> astPath = ASTPath(
- refactoringFile->cppDocument())(link.targetLine, link.targetColumn);
- const bool isTemplate = symbol->asTemplate();
- const bool isFunction = symbol->type()->asFunctionType();
- for (auto it = astPath.rbegin(); it != astPath.rend(); ++it) {
- const bool match = isTemplate ? bool((*it)->asTemplateDeclaration())
- : isFunction ? bool((*it)->asFunctionDefinition())
- : bool((*it)->asSimpleDeclaration());
- if (match) {
- // For member functions of class templates.
- if (isFunction) {
- const auto next = std::next(it);
- if (next != astPath.rend() && (*next)->asTemplateDeclaration())
- it = next;
- }
- state->perFileState[link.targetFilePath].insertSorted(*it);
- if (symbol->asForwardClassDeclaration()) {
- if (const auto classSpec = (*(it - 1))->asClassSpecifier();
- classSpec && classSpec->symbol) {
- collectImplementations(classSpec->symbol, state);
- }
- }
- break;
- }
- }
- };
- ++state->remainingFollowSymbolOps;
-
- // Force queued execution, as the built-in editor can run the callback synchronously.
- const auto followSymbol = [cursorInEditor, callback] {
- CppModelManager::followSymbol(
- cursorInEditor, callback, true, false, FollowSymbolMode::Exact);
- };
- QMetaObject::invokeMethod(CppModelManager::instance(), followSymbol, Qt::QueuedConnection);
- }
-
- static void collectImplementations(Class *klass, const State::Ptr &state)
- {
- for (int i = 0; i < klass->memberCount(); ++i) {
- Symbol * const member = klass->memberAt(i);
- if (member->asForwardClassDeclaration() || member->asTemplate()) {
- lookupSymbol(member, state);
- continue;
- }
- const auto decl = member->asDeclaration();
- if (!decl)
- continue;
- if (decl->type().type()->asFunctionType()) {
- if (!decl->asFunction())
- lookupSymbol(member, state);
- } else if (decl->isStatic() && !decl->type().isInline()) {
- lookupSymbol(member, state);
- }
- }
- }
-
- static void finish(const State::Ptr &state)
- {
- Overview ov;
- Project * const project = ProjectManager::projectForFile(state->originalFilePath);
- const CppFileSettings fileSettings = cppFileSettingsForProject(project);
- const auto constructDefaultFilePaths = [&] {
- const QString className = ov.prettyName(state->classAst->symbol->name());
- const QString baseFileName = fileSettings.lowerCaseFiles ? className.toLower() : className;
- const QString headerFileName = baseFileName + '.' + fileSettings.headerSuffix;
- const FilePath baseDir = state->originalFilePath.parentDir();
- const FilePath headerFilePath = baseDir.pathAppended(headerFileName);
- const QString sourceFileName = baseFileName + '.' + fileSettings.sourceSuffix;
- const FilePath sourceFilePath = baseDir.pathAppended(sourceFileName);
- return std::make_pair(headerFilePath, sourceFilePath);
- };
- auto [headerFilePath, sourceFilePath] = constructDefaultFilePaths();
- bool mustCreateSourceFile = false;
- bool mustNotCreateSourceFile = false;
- ProjectNode *projectNode = nullptr;
- if (project && project->rootProjectNode()) {
- const Node * const origNode = project->nodeForFilePath(state->originalFilePath);
- if (origNode)
- projectNode = const_cast<Node *>(origNode)->managingProject();
- }
- if (state->interactive) {
- Dialog dlg(headerFilePath, sourceFilePath, projectNode);
- if (dlg.exec() != QDialog::Accepted)
- return;
- projectNode = dlg.projectNode();
- headerFilePath = dlg.headerFilePath();
- sourceFilePath = dlg.sourceFilePath();
- mustCreateSourceFile = dlg.createSourceFile();
- mustNotCreateSourceFile = !dlg.createSourceFile();
- }
- const auto fileListForDisplay = [](const FilePaths &files) {
- return Utils::transform<QStringList>(files, [](const FilePath &fp) {
- return '"' + fp.toUserOutput() + '"';
- }).join(", ");
- };
- FilePaths existingFiles;
- if (headerFilePath.exists())
- existingFiles << headerFilePath;
- if (!mustNotCreateSourceFile && sourceFilePath.exists())
- existingFiles << sourceFilePath;
- if (!existingFiles.isEmpty()) {
- Core::MessageManager::writeDisrupting(
- Tr::tr("Refusing to overwrite the following files: %1\n")
- .arg(fileListForDisplay(existingFiles)));
- return;
- }
- const QString headerFileName = headerFilePath.fileName();
-
- QString headerContent;
- QString sourceContent;
- QList<QString *> commonContent{&headerContent};
- if (!mustNotCreateSourceFile)
- commonContent << &sourceContent;
- for (QString *const content : std::as_const(commonContent)) {
- content->append(fileSettings.licenseTemplate());
- if (!content->isEmpty())
- content->append('\n');
- }
- sourceContent.append('\n').append("#include \"").append(headerFileName).append("\"\n");
- const QStringList namespaceNames
- = Utils::transform<QStringList>(state->namespacePath, [&](const Namespace *ns) {
- return ov.prettyName(ns->name());
- });
- const QString headerGuard = Utils::headerGuard(headerFileName, namespaceNames);
- if (fileSettings.headerPragmaOnce) {
- headerContent.append("#pragma once\n");
- } else {
- headerContent.append("#ifndef " + headerGuard + "\n");
- headerContent.append("#define " + headerGuard + "\n");
- }
- if (!namespaceNames.isEmpty()) {
- for (QString *const content : std::as_const(commonContent)) {
- content->append('\n');
- for (const QString &ns : namespaceNames)
- content->append("namespace " + ns + " {\n");
- }
- }
- bool hasSourceContent = false;
- for (auto it = state->perFileState.begin(); it != state->perFileState.end(); ++it) {
- if (it->declarationsToMove.isEmpty())
- continue;
- const CppRefactoringFilePtr refactoringFile = it->refactoringFile;
- QTC_ASSERT(refactoringFile, continue);
- const bool isDeclFile = refactoringFile->filePath() == state->originalFilePath;
- ChangeSet changes;
- if (isDeclFile) {
- QString relInclude = headerFilePath.relativePathFrom(
- refactoringFile->filePath().parentDir()).toString();
- if (!relInclude.isEmpty())
- relInclude.append('/');
- relInclude.append('"').append(headerFileName).append('"');
- insertNewIncludeDirective(relInclude, refactoringFile,
- refactoringFile->cppDocument(), changes);
- }
- for (AST * const declToMove : std::as_const(it->declarationsToMove)) {
- const ChangeSet::Range rangeToMove = refactoringFile->range(declToMove);
- QString &content = isDeclFile || mustNotCreateSourceFile ? headerContent
- : sourceContent;
- if (&content == &sourceContent)
- hasSourceContent = true;
- content.append('\n')
- .append(refactoringFile->textOf(rangeToMove))
- .append('\n');
- changes.remove(rangeToMove);
- }
- refactoringFile->setChangeSet(changes);
- refactoringFile->apply();
- }
-
- if (!namespaceNames.isEmpty()) {
- for (QString *const content : std::as_const(commonContent)) {
- content->append('\n');
- for (auto it = namespaceNames.rbegin(); it != namespaceNames.rend(); ++it)
- content->append("} // namespace " + *it + '\n');
- }
- }
- if (!fileSettings.headerPragmaOnce)
- headerContent.append("\n#endif // " + headerGuard + '\n');
-
- CppRefactoringFilePtr headerFile = state->factory.cppFile(headerFilePath);
- headerFilePath.ensureExistingFile();
- ChangeSet headerChanges;
- headerChanges.insert(0, headerContent);
- headerFile->setChangeSet(headerChanges);
- headerFile->apply();
- if (hasSourceContent || mustCreateSourceFile) {
- sourceFilePath.ensureExistingFile();
- CppRefactoringFilePtr sourceFile = state->factory.cppFile(sourceFilePath);
- ChangeSet sourceChanges;
- sourceChanges.insert(0, sourceContent);
- sourceFile->setChangeSet(sourceChanges);
- sourceFile->apply();
- }
-
- if (!projectNode)
- return;
- FilePaths toAdd{headerFilePath};
- if (hasSourceContent)
- toAdd << sourceFilePath;
- FilePaths notAdded;
- projectNode->addFiles(toAdd, &notAdded);
- if (!notAdded.isEmpty()) {
- Core::MessageManager::writeDisrupting(
- Tr::tr("Failed to add to project file \"%1\": %2")
- .arg(projectNode->filePath().toUserOutput(), fileListForDisplay(notAdded)));
- }
-
- if (state->interactive)
- Core::EditorManager::openEditor(headerFilePath);
- }
-
- const State::Ptr m_state;
-};
-} // namespace
-
-// Applies if and only if:
-// - Class is not a nested class.
-// - Class name does not match file name via any of the usual transformations.
-// - There are other declarations in the same file.
-void MoveClassToOwnFile::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- ClassSpecifierAST * const classAst = astForClassOperations(interface);
- if (!classAst || !classAst->symbol)
- return;
- AST *fullDecl = nullptr;
- for (auto it = interface.path().rbegin(); it != interface.path().rend() && !fullDecl; ++it) {
- if (*it == classAst && it != interface.path().rend() - 1) {
- auto next = std::next(it);
- fullDecl = (*next)->asSimpleDeclaration();
- if (next != interface.path().rend() - 1) {
- next = std::next(next);
- if (const auto templ = (*next)->asTemplateDeclaration())
- fullDecl = templ;
- }
- }
- }
- if (!fullDecl)
- return;
-
- // Check file name.
- const QString className = Overview().prettyName(classAst->symbol->name());
- if (className.isEmpty())
- return;
- const QString lowerFileBaseName = interface.filePath().baseName().toLower();
- if (lowerFileBaseName.contains(className.toLower()))
- return;
- QString underscoredClassName = className;
- QChar curChar = underscoredClassName.at(0);
- for (int i = 1; i < underscoredClassName.size(); ++i) {
- const QChar prevChar = curChar;
- curChar = underscoredClassName.at(i);
- if (curChar.isUpper() && prevChar.isLetterOrNumber() && !prevChar.isUpper()) {
- underscoredClassName.insert(i, '_');
- ++i;
- }
- }
- if (lowerFileBaseName.contains(underscoredClassName.toLower()))
- return;
-
- // Is there more than one class definition in the file?
- AST * const ast = interface.currentFile()->cppDocument()->translationUnit()->ast();
- if (!ast)
- return;
- DeclarationListAST * const topLevelDecls = ast->asTranslationUnit()->declaration_list;
- if (!topLevelDecls)
- return;
- QList<Namespace *> namespacePath;
- QList<Namespace *> currentNamespacePath;
- bool foundOtherDecls = false;
- bool foundSelf = false;
- std::function<void(Namespace *)> collectSymbolsFromNamespace;
- const auto handleSymbol = [&](Symbol *symbol) {
- if (!symbol)
- return;
- if (const auto nsMember = symbol->asNamespace()) {
- collectSymbolsFromNamespace(nsMember);
- return;
- }
- if (symbol != classAst->symbol) {
- if (!symbol->asForwardClassDeclaration())
- foundOtherDecls = true;
- return;
- }
- QTC_ASSERT(symbol->asClass(), return);
- foundSelf = true;
- namespacePath = currentNamespacePath;
- };
- collectSymbolsFromNamespace = [&](Namespace *ns) {
- currentNamespacePath << ns;
- for (int i = 0; i < ns->memberCount() && (!foundSelf || !foundOtherDecls); ++i)
- handleSymbol(ns->memberAt(i));
- currentNamespacePath.removeLast();
- };
- for (DeclarationListAST *it = topLevelDecls; it && (!foundSelf || !foundOtherDecls);
- it = it->next) {
- DeclarationAST *decl = it->value;
- if (!decl)
- continue;
- if (const auto templ = decl->asTemplateDeclaration())
- decl = templ->declaration;
- if (!decl)
- continue;
- if (const auto ns = decl->asNamespace(); ns && ns->symbol) {
- collectSymbolsFromNamespace(ns->symbol);
- continue;
- }
- if (const auto simpleDecl = decl->asSimpleDeclaration()) {
- if (!simpleDecl->decl_specifier_list)
- continue;
- for (SpecifierListAST *spec = simpleDecl->decl_specifier_list; spec; spec = spec->next) {
- if (!spec->value)
- continue;
- if (const auto klass = spec->value->asClassSpecifier())
- handleSymbol(klass->symbol);
- else if (!spec->value->asElaboratedTypeSpecifier()) // forward decl
- foundOtherDecls = true;
- }
- } else if (decl->asDeclaration()) {
- foundOtherDecls = true;
- }
- }
-
- if (foundSelf && foundOtherDecls) {
- result << new MoveClassToOwnFileOp(
- interface, fullDecl, classAst, namespacePath, m_interactive);
- }
-}
-
-void createCppQuickFixes()
-{
- new AddIncludeForUndefinedIdentifier;
-
- new FlipLogicalOperands;
- new InverseLogicalComparison;
- new RewriteLogicalAnd;
-
- new ConvertToCamelCase;
-
- new ConvertCStringToNSString;
- new ConvertNumericLiteral;
- new TranslateStringLiteral;
- new WrapStringLiteral;
-
- new MoveDeclarationOutOfIf;
- new MoveDeclarationOutOfWhile;
-
- new SplitIfStatement;
- new SplitSimpleDeclaration;
-
- new AddBracesToControlStatement;
- new RearrangeParamDeclarationList;
- new ReformatPointerDeclaration;
-
- new CompleteSwitchCaseStatement;
- new InsertQtPropertyMembers;
- new ConvertQt4Connect;
-
- new ApplyDeclDefLinkChanges;
- new ConvertFromAndToPointer;
- new ExtractFunction;
- new ExtractLiteralAsParameter;
- new GenerateGetterSetter;
- new GenerateGettersSettersForClass;
- new InsertDeclFromDef;
- new InsertDefFromDecl;
- new AddDeclarationForUndeclaredIdentifier;
- new InsertDefsFromDecls;
-
- new MoveFuncDefOutside;
- new MoveAllFuncDefOutside;
- new MoveFuncDefToDeclPush;
- new MoveFuncDefToDeclPull;
-
- new AssignToLocalVariable;
-
- new InsertVirtualMethods;
-
- new OptimizeForLoop;
-
- new EscapeStringLiteral;
-
- new ExtraRefactoringOperations;
-
- new RemoveUsingNamespace;
- new GenerateConstructor;
- new ConvertCommentStyle;
- new MoveFunctionComments;
- new ConvertToMetaMethodCall;
- new MoveClassToOwnFile;
-}
-
-void destroyCppQuickFixes()
-{
- for (int i = g_cppQuickFixFactories.size(); --i >= 0; )
- delete g_cppQuickFixFactories.at(i);
-}
-
-} // namespace Internal
-} // namespace CppEditor
-
-#include "cppquickfixes.moc"
diff --git a/src/plugins/cppeditor/cppquickfixes.h b/src/plugins/cppeditor/cppquickfixes.h
deleted file mode 100644
index d23fc86c5f..0000000000
--- a/src/plugins/cppeditor/cppquickfixes.h
+++ /dev/null
@@ -1,644 +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 "cppquickfix.h"
-
-#include <variant>
-
-///
-/// Adding New Quick Fixes
-///
-/// When adding new Quick Fixes, make sure that the doMatch() function is "cheap".
-/// Otherwise, since the match() functions are also called to generate context menu
-/// entries, the user might experience a delay opening the context menu.
-///
-
-namespace CppEditor {
-namespace Internal {
-using TypeOrExpr = std::variant<const CPlusPlus::ExpressionAST *, CPlusPlus::FullySpecifiedType>;
-
-void createCppQuickFixes();
-void destroyCppQuickFixes();
-
-class ExtraRefactoringOperations : public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
-};
-
-/*!
- Adds an include for an undefined identifier or only forward declared identifier.
-
- Activates on: the undefined identifier
-*/
-class AddIncludeForUndefinedIdentifier : public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
-};
-
-// Exposed for tests
-class AddIncludeForUndefinedIdentifierOp: public CppQuickFixOperation
-{
-public:
- AddIncludeForUndefinedIdentifierOp(const CppQuickFixInterface &interface, int priority,
- const QString &include);
- void perform() override;
-
- QString include() const { return m_include; }
-
-private:
- QString m_include;
-};
-
-class AddForwardDeclForUndefinedIdentifierOp: public CppQuickFixOperation
-{
-public:
- AddForwardDeclForUndefinedIdentifierOp(const CppQuickFixInterface &interface, int priority,
- const QString &fqClassName, int symbolPos);
-private:
- void perform() override;
-
- const QString m_className;
- const int m_symbolPos;
-};
-
-/*!
- Rewrite
- a op b
-
- As
- b flipop a
-
- Activates on: <= < > >= == != && ||
-*/
-class FlipLogicalOperands: public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
-};
-
-/*!
- Rewrite
- a op b -> !(a invop b)
- (a op b) -> !(a invop b)
- !(a op b) -> (a invob b)
-
- Activates on: <= < > >= == !=
-*/
-class InverseLogicalComparison: public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
-};
-
-/*!
- Rewrite
- !a && !b
-
- As
- !(a || b)
-
- Activates on: &&
-*/
-class RewriteLogicalAnd: public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
-};
-
-/*!
- Replace
- "abcd"
- QLatin1String("abcd")
- QLatin1Literal("abcd")
-
- With
- @"abcd"
-
- Activates on: the string literal, if the file type is a Objective-C(++) file.
-*/
-class ConvertCStringToNSString: public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
-};
-
-/*!
- Base class for converting numeric literals between decimal, octal and hex.
- Does the base check for the specific ones and parses the number.
-
- Test cases:
- 0xFA0Bu;
- 0X856A;
- 298.3;
- 199;
- 074;
- 199L;
- 074L;
- -199;
- -017;
- 0783; // invalid octal
- 0; // border case, allow only hex<->decimal
-
- Activates on: numeric literals
-*/
-class ConvertNumericLiteral: public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
-};
-
-/*!
- Replace
- "abcd"
-
- With
- tr("abcd") or
- QCoreApplication::translate("CONTEXT", "abcd") or
- QT_TRANSLATE_NOOP("GLOBAL", "abcd")
-
- depending on what is available.
-
- Activates on: the string literal
-*/
-class TranslateStringLiteral: public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
-};
-
-/*!
- Replace
- "abcd" -> QLatin1String("abcd")
- @"abcd" -> QLatin1String("abcd") (Objective C)
- 'a' -> QLatin1Char('a')
- 'a' -> "a"
- "a" -> 'a' or QLatin1Char('a') (Single character string constants)
- "\n" -> '\n', QLatin1Char('\n')
-
- Except if they are already enclosed in
- QLatin1Char, QT_TRANSLATE_NOOP, tr,
- trUtf8, QLatin1Literal, QLatin1String
-
- Activates on: the string or character literal
-*/
-
-class WrapStringLiteral: public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
-};
-
-/*!
- Turns "an_example_symbol" into "anExampleSymbol" and
- "AN_EXAMPLE_SYMBOL" into "AnExampleSymbol".
-
- Activates on: identifiers
-*/
-class ConvertToCamelCase : public CppQuickFixFactory
-{
-public:
- ConvertToCamelCase(bool test = false) : CppQuickFixFactory(), m_test(test) {}
-
- void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
-
-private:
- const bool m_test;
-};
-
-/*!
- Replace
- if (Type name = foo()) {...}
-
- With
- Type name = foo();
- if (name) {...}
-
- Activates on: the name of the introduced variable
-*/
-class MoveDeclarationOutOfIf: public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
-};
-
-/*!
- Replace
- while (Type name = foo()) {...}
-
- With
- Type name;
- while ((name = foo()) != 0) {...}
-
- Activates on: the name of the introduced variable
-*/
-class MoveDeclarationOutOfWhile: public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
-};
-
-/*!
- Replace
- if (something && something_else) {
- }
-
- with
- if (something)
- if (something_else) {
- }
- }
-
- and
- if (something || something_else)
- x;
-
- with
- if (something)
- x;
- else if (something_else)
- x;
-
- Activates on: && or ||
-*/
-class SplitIfStatement: public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
-};
-
-/*!
- Rewrite
- int *a, b;
-
- As
- int *a;
- int b;
-
- Activates on: the type or the variable names.
-*/
-class SplitSimpleDeclaration: public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
-};
-
-/*!
- Add curly braces to a control statement that doesn't already contain a
- compound statement. I.e.
-
- if (a)
- b;
- becomes
- if (a) {
- b;
- }
-
- Activates on: the keyword
-*/
-class AddBracesToControlStatement : public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
-};
-
-/*!
- Switches places of the parameter declaration under cursor
- with the next or the previous one in the parameter declaration list
-
- Activates on: parameter declarations
-*/
-class RearrangeParamDeclarationList : public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
-};
-
-/*!
- Reformats a pointer, reference or rvalue reference type/declaration.
-
- Works also with selections (except when the cursor is not on any AST).
-
- Activates on: simple declarations, parameters and return types of function
- declarations and definitions, control flow statements (if,
- while, for, foreach) with declarations.
-*/
-class ReformatPointerDeclaration : public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
-};
-
-/*!
- Adds missing case statements for "switch (enumVariable)"
- */
-class CompleteSwitchCaseStatement: public CppQuickFixFactory
-{
-public:
- CompleteSwitchCaseStatement() { setClangdReplacement({12}); }
-
-private:
- void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
-};
-
-/*!
- Adds a declarations to a definition
- */
-class InsertDeclFromDef: public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
-};
-
-/*!
- Adds a definition for a declaration.
- */
-class InsertDefFromDecl: public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
- bool m_defPosOutsideClass = false;
-};
-
-class AddDeclarationForUndeclaredIdentifier : public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface,
- TextEditor::QuickFixOperations &result) override;
-
-#ifdef WITH_TESTS
- void setMembersOnly() { m_membersOnly = true; }
-#endif
-
-private:
- 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;
-};
-
-/*!
- Adds a definition for any number of member function declarations.
- */
-class InsertDefsFromDecls : public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface,
- TextEditor::QuickFixOperations &result) override;
-
- enum class Mode {
- Off, // Testing: simulates user canceling the dialog
- Impl, // Testing: simulates user choosing cpp file for every function
- Alternating, // Testing: simulates user choosing a different DefPos for every function
- User // Normal interactive mode
- };
- void setMode(Mode mode) { m_mode = mode; }
-
-private:
- Mode m_mode = Mode::User;
-};
-
-/*!
- Extracts the selected code and puts it to a function
- */
-class ExtractFunction : public CppQuickFixFactory
-{
-public:
- using FunctionNameGetter = std::function<QString()>;
-
- ExtractFunction(FunctionNameGetter functionNameGetter = FunctionNameGetter());
- void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
-
-private:
- FunctionNameGetter m_functionNameGetter; // For tests to avoid GUI pop-up.
-};
-
-/*!
- Extracts the selected constant and converts it to a parameter of the current function.
-
- Activates on numeric, bool, character, or string literal in the function body.
- */
-class ExtractLiteralAsParameter : public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
-};
-
-/*!
- Converts the selected variable to a pointer if it is a stack variable or reference, or vice versa.
-
- Activates on variable declarations.
- */
-class ConvertFromAndToPointer : public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
-};
-
-/*!
- Adds getter and setter functions for a member variable
- */
-class GenerateGetterSetter : public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
-};
-
-/*!
- Adds getter and setter functions for several member variables
- */
-class GenerateGettersSettersForClass : public CppQuickFixFactory
-{
-protected:
- void setTest() { m_test = true; }
-
-private:
- void doMatch(const CppQuickFixInterface &interface,
- TextEditor::QuickFixOperations &result) override;
-
- bool m_test = false;
-};
-
-/*!
- Adds missing members for a Q_PROPERTY
- */
-class InsertQtPropertyMembers : public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
-};
-
-/*!
- Converts a Qt 4 QObject::connect() to Qt 5 style.
- */
-class ConvertQt4Connect : public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
-};
-
-/*!
- Applies function signature changes
- */
-class ApplyDeclDefLinkChanges: public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
-};
-
-/*!
- Moves the definition of a member function outside the class or moves the definition of a member
- function or a normal function to the implementation file.
- */
-class MoveFuncDefOutside: public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
-};
-
-/*!
- Moves all member function definitions outside the class or to the implementation file.
- */
-class MoveAllFuncDefOutside: public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
-};
-
-/*!
- Moves the definition of a function to its declaration, with the cursor on the definition.
- */
-class MoveFuncDefToDeclPush : public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
-};
-
-/*!
- Moves the definition of a function to its declaration, with the cursor on the declaration.
- */
-class MoveFuncDefToDeclPull : public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
-};
-
-/*!
- Assigns the return value of a function call or a new expression to a local variable
- */
-class AssignToLocalVariable : public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
-};
-
-/*!
- Optimizes a for loop to avoid permanent condition check and forces to use preincrement
- or predecrement operators in the expression of the for loop.
- */
-class OptimizeForLoop : public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
-};
-
-/*!
- Escapes or unescapes a string literal as UTF-8.
-
- Escapes non-ASCII characters in a string literal to hexadecimal escape sequences.
- Unescapes octal or hexadecimal escape sequences in a string literal.
- String literals are handled as UTF-8 even if file's encoding is not UTF-8.
- */
-class EscapeStringLiteral : public CppQuickFixFactory
-{
-public:
- void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
-};
-
-/*!
- Removes a using directive (using namespace xyz).
-
-*/
-class RemoveUsingNamespace : public CppQuickFixFactory
-{
-public:
- RemoveUsingNamespace() { setClangdReplacement({10}); }
-
-private:
- void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
-};
-
-/*!
- Generate constructor
- */
-class GenerateConstructor : public CppQuickFixFactory
-{
-protected:
- void setTest() { m_test = true; }
-
-private:
- void doMatch(const CppQuickFixInterface &interface,
- TextEditor::QuickFixOperations &result) override;
-
- bool m_test = false;
-};
-
-//! Converts C-style to C++-style comments and vice versa
-class ConvertCommentStyle : public CppQuickFixFactory
-{
-private:
- void doMatch(const CppQuickFixInterface &interface,
- TextEditor::QuickFixOperations &result) override;
-};
-
-//! Moves function documentation between declaration and implementation.
-class MoveFunctionComments : public CppQuickFixFactory
-{
-private:
- void doMatch(const CppQuickFixInterface &interface,
- TextEditor::QuickFixOperations &result) override;
-};
-
-//! Converts a normal function call into a meta method invocation, if the functions is
-//! marked as invokable.
-class ConvertToMetaMethodCall : public CppQuickFixFactory
-{
-private:
- void doMatch(const CppQuickFixInterface &interface,
- TextEditor::QuickFixOperations &result) override;
-};
-
-//! Move a class into a dedicates set of files.
-class MoveClassToOwnFile : public CppQuickFixFactory
-{
-#ifdef WITH_TESTS
-public:
- void setNonInteractive() { m_interactive = false; }
-#endif
-private:
- void doMatch(const CppQuickFixInterface &interface,
- TextEditor::QuickFixOperations &result) override;
-
- bool m_interactive = true;
-};
-
-} // namespace Internal
-} // namespace CppEditor
diff --git a/src/plugins/cppeditor/cpprefactoringchanges.cpp b/src/plugins/cppeditor/cpprefactoringchanges.cpp
index 053daeb245..d9e5a1c883 100644
--- a/src/plugins/cppeditor/cpprefactoringchanges.cpp
+++ b/src/plugins/cppeditor/cpprefactoringchanges.cpp
@@ -4,6 +4,8 @@
#include "cpprefactoringchanges.h"
#include "cppeditorconstants.h"
+#include "cppeditorwidget.h"
+#include "cppsemanticinfo.h"
#include "cppworkingcopy.h"
#include <projectexplorer/editorconfiguration.h>
@@ -19,6 +21,7 @@
#include <utility>
+using namespace Core;
using namespace CPlusPlus;
using namespace Utils;
@@ -41,10 +44,15 @@ CppRefactoringChanges::CppRefactoringChanges(const Snapshot &snapshot)
{
}
-CppRefactoringFilePtr CppRefactoringChanges::file(TextEditor::TextEditorWidget *editor, const Document::Ptr &document)
+CppRefactoringFilePtr CppRefactoringChanges::file(
+ TextEditor::TextEditorWidget *editor, const Document::Ptr &document)
{
CppRefactoringFilePtr result(new CppRefactoringFile(editor));
result->setCppDocument(document);
+ if (const auto cppEditorWidget = qobject_cast<CppEditorWidget *>(editor)) {
+ result->m_data = QSharedPointer<CppRefactoringChangesData>::create(
+ cppEditorWidget->semanticInfo().snapshot);
+ }
return result;
}
@@ -55,6 +63,18 @@ TextEditor::RefactoringFilePtr CppRefactoringChanges::file(const FilePath &fileP
CppRefactoringFilePtr CppRefactoringChanges::cppFile(const Utils::FilePath &filePath) const
{
+ // Prefer documents from editors, as these are already parsed and up to date with regards to
+ // unsaved changes.
+ const QList<IEditor *> editors = DocumentModel::editorsForFilePath(filePath);
+ for (IEditor *editor : editors) {
+ if (const auto textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor)) {
+ if (const auto editorWidget = qobject_cast<CppEditorWidget *>(
+ textEditor->editorWidget())) {
+ return file(editorWidget, editorWidget->semanticInfo().doc);
+ }
+ }
+ }
+
return CppRefactoringFilePtr(new CppRefactoringFile(filePath, m_data));
}
@@ -201,6 +221,9 @@ ChangeSet::Range CppRefactoringFile::range(const AST *ast) const
int CppRefactoringFile::startOf(unsigned index) const
{
+ if (const auto loc = expansionLoc(index))
+ return loc->first;
+
int line, column;
cppDocument()->translationUnit()->getPosition(tokenAt(index).utf16charsBegin(), &line, &column);
return document()->findBlockByNumber(line - 1).position() + column - 1;
@@ -209,15 +232,14 @@ int CppRefactoringFile::startOf(unsigned index) const
int CppRefactoringFile::startOf(const AST *ast) const
{
QTC_ASSERT(ast, return 0);
- int firstToken = ast->firstToken();
- const int lastToken = ast->lastToken();
- while (tokenAt(firstToken).generated() && firstToken < lastToken)
- ++firstToken;
- return startOf(firstToken);
+ return startOf(ast->firstToken());
}
int CppRefactoringFile::endOf(unsigned index) const
{
+ if (const auto loc = expansionLoc(index))
+ return loc->first + loc->second;
+
int line, column;
cppDocument()->translationUnit()->getPosition(tokenAt(index).utf16charsEnd(), &line, &column);
return document()->findBlockByNumber(line - 1).position() + column - 1;
@@ -228,14 +250,17 @@ int CppRefactoringFile::endOf(const AST *ast) const
QTC_ASSERT(ast, return 0);
int lastToken = ast->lastToken() - 1;
QTC_ASSERT(lastToken >= 0, return -1);
- const int firstToken = ast->firstToken();
- while (tokenAt(lastToken).generated() && lastToken > firstToken)
- --lastToken;
return endOf(lastToken);
}
void CppRefactoringFile::startAndEndOf(unsigned index, int *start, int *end) const
{
+ if (const auto loc = expansionLoc(index)) {
+ *start = loc->first;
+ *end = loc->first + loc->second;
+ return;
+ }
+
int line, column;
Token token(tokenAt(index));
cppDocument()->translationUnit()->getPosition(token.utf16charsBegin(), &line, &column);
@@ -243,6 +268,13 @@ void CppRefactoringFile::startAndEndOf(unsigned index, int *start, int *end) con
*end = *start + token.utf16chars();
}
+std::optional<std::pair<int, int> > CppRefactoringFile::expansionLoc(unsigned int index) const
+{
+ if (!tokenAt(index).expanded())
+ return {};
+ return cppDocument()->translationUnit()->getExpansionPosition(index);
+}
+
QString CppRefactoringFile::textOf(const AST *ast) const
{
return textOf(startOf(ast), endOf(ast));
diff --git a/src/plugins/cppeditor/cpprefactoringchanges.h b/src/plugins/cppeditor/cpprefactoringchanges.h
index 6350751a34..957d02674c 100644
--- a/src/plugins/cppeditor/cpprefactoringchanges.h
+++ b/src/plugins/cppeditor/cpprefactoringchanges.h
@@ -11,6 +11,8 @@
#include <texteditor/refactoringchanges.h>
+#include <optional>
+
namespace CppEditor {
class CppRefactoringChanges;
class CppRefactoringFile;
@@ -30,7 +32,6 @@ public:
bool isCursorOn(unsigned tokenIndex) const;
bool isCursorOn(const CPlusPlus::AST *ast) const;
- Range range(int start, int end) const;
Range range(unsigned tokenIndex) const;
Range range(const CPlusPlus::AST *ast) const;
@@ -43,6 +44,8 @@ public:
void startAndEndOf(unsigned index, int *start, int *end) const;
+ std::optional<std::pair<int, int>> expansionLoc(unsigned index) const;
+
QList<CPlusPlus::Token> tokensForCursor() const;
QList<CPlusPlus::Token> tokensForCursor(const QTextCursor &cursor) const;
QList<CPlusPlus::Token> tokensForLine(int line) const;
diff --git a/src/plugins/cppeditor/cpprenaming_test.cpp b/src/plugins/cppeditor/cpprenaming_test.cpp
index 2827434be5..a31d00fa07 100644
--- a/src/plugins/cppeditor/cpprenaming_test.cpp
+++ b/src/plugins/cppeditor/cpprenaming_test.cpp
@@ -5,7 +5,7 @@
#include "cppeditorwidget.h"
#include "cppmodelmanager.h"
-#include "cppquickfix_test.h"
+#include "quickfixes/cppquickfix_test.h"
#include <texteditor/texteditor.h>
diff --git a/src/plugins/cppeditor/cppsemanticinfoupdater.cpp b/src/plugins/cppeditor/cppsemanticinfoupdater.cpp
index e398c14b86..f9e13df082 100644
--- a/src/plugins/cppeditor/cppsemanticinfoupdater.cpp
+++ b/src/plugins/cppeditor/cppsemanticinfoupdater.cpp
@@ -9,8 +9,6 @@
#include <cplusplus/CppDocument.h>
#include <cplusplus/TranslationUnit.h>
-#include <extensionsystem/pluginmanager.h>
-
#include <utils/async.h>
#include <utils/futuresynchronizer.h>
#include <utils/qtcassert.h>
@@ -143,7 +141,7 @@ void SemanticInfoUpdater::updateDetached(const SemanticInfo::Source &source)
});
const auto future = Utils::asyncRun(CppModelManager::sharedThreadPool(), doUpdate, source);
d->m_watcher->setFuture(future);
- ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(future);
+ Utils::futureSynchronizer()->addFuture(future);
}
SemanticInfo SemanticInfoUpdater::semanticInfo() const
diff --git a/src/plugins/cppeditor/cpptoolsreuse.cpp b/src/plugins/cppeditor/cpptoolsreuse.cpp
index 947ecdd4c6..6e0802b4bb 100644
--- a/src/plugins/cppeditor/cpptoolsreuse.cpp
+++ b/src/plugins/cppeditor/cpptoolsreuse.cpp
@@ -15,9 +15,9 @@
#include "cppfilesettingspage.h"
#include "cpphighlighter.h"
#include "cppqtstyleindenter.h"
-#include "cppquickfixassistant.h"
#include "cpprefactoringchanges.h"
#include "projectinfo.h"
+#include "quickfixes/cppquickfixassistant.h"
#include <coreplugin/documentmanager.h>
#include <coreplugin/editormanager/editormanager.h>
@@ -365,15 +365,10 @@ bool fileSizeExceedsLimit(const FilePath &filePath, int sizeLimitInMb)
const qint64 fileSizeInMB = filePath.fileSize() / (1000 * 1000);
if (fileSizeInMB > sizeLimitInMb) {
- const QString msg = Tr::tr("C++ Indexer: Skipping file \"%1\" because it is too big.")
- .arg(filePath.displayName());
-
- QMetaObject::invokeMethod(Core::MessageManager::instance(),
- [msg]() { Core::MessageManager::writeSilently(msg); });
-
+ Core::MessageManager::writeSilently(Tr::tr("C++ Indexer: Skipping file \"%1\" because "
+ "it is too big.").arg(filePath.displayName()));
return true;
}
-
return false;
}
diff --git a/src/plugins/cppeditor/cppuseselectionsupdater.cpp b/src/plugins/cppeditor/cppuseselectionsupdater.cpp
index 67fb6d7a1b..bf1eabd153 100644
--- a/src/plugins/cppeditor/cppuseselectionsupdater.cpp
+++ b/src/plugins/cppeditor/cppuseselectionsupdater.cpp
@@ -7,8 +7,6 @@
#include "cppeditorwidget.h"
#include "cppmodelmanager.h"
-#include <extensionsystem/pluginmanager.h>
-
#include <utils/futuresynchronizer.h>
#include <utils/qtcassert.h>
#include <utils/textutils.h>
@@ -75,7 +73,7 @@ CppUseSelectionsUpdater::RunnerInfo CppUseSelectionsUpdater::update(CallType cal
m_runnerWordStartPosition = params.textCursor.position();
m_runnerWatcher->setFuture(cppEditorDocument->cursorInfo(params));
- ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(m_runnerWatcher->future());
+ Utils::futureSynchronizer()->addFuture(m_runnerWatcher->future());
return RunnerInfo::Started;
} else { // synchronous case
abortSchedule();
diff --git a/src/plugins/cppeditor/fileandtokenactions_test.cpp b/src/plugins/cppeditor/fileandtokenactions_test.cpp
index 5acca7fa11..7c8175fa18 100644
--- a/src/plugins/cppeditor/fileandtokenactions_test.cpp
+++ b/src/plugins/cppeditor/fileandtokenactions_test.cpp
@@ -4,13 +4,13 @@
#include "fileandtokenactions_test.h"
#include "cppeditorwidget.h"
-#include "cppquickfix.h"
-#include "cppquickfixassistant.h"
-#include "cppinsertvirtualmethods.h"
#include "cppmodelmanager.h"
#include "cpptoolstestcase.h"
#include "cppworkingcopy.h"
#include "projectinfo.h"
+#include "quickfixes/cppquickfix.h"
+#include "quickfixes/cppquickfixassistant.h"
+#include "quickfixes/cppinsertvirtualmethods.h"
#include <coreplugin/editormanager/editormanager.h>
#include <projectexplorer/project.h>
diff --git a/src/plugins/cppeditor/quickfixes/assigntolocalvariable.cpp b/src/plugins/cppeditor/quickfixes/assigntolocalvariable.cpp
new file mode 100644
index 0000000000..ae63681dc2
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/assigntolocalvariable.cpp
@@ -0,0 +1,510 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "assigntolocalvariable.h"
+
+#include "../cppcodestylesettings.h"
+#include "../cppeditortr.h"
+#include "../cppeditorwidget.h"
+#include "../cpprefactoringchanges.h"
+#include "cppquickfix.h"
+#include "cppquickfixprojectsettings.h"
+
+#include <cplusplus/CppRewriter.h>
+#include <cplusplus/Overview.h>
+#include <cplusplus/TypeOfExpression.h>
+#include <projectexplorer/projecttree.h>
+
+#ifdef WITH_TESTS
+#include "cppquickfix_test.h"
+#include <QtTest>
+#endif
+
+using namespace CPlusPlus;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+class AssignToLocalVariableOperation : public CppQuickFixOperation
+{
+public:
+ explicit AssignToLocalVariableOperation(const CppQuickFixInterface &interface,
+ const int insertPos, const AST *ast, const Name *name)
+ : CppQuickFixOperation(interface)
+ , m_insertPos(insertPos)
+ , m_ast(ast)
+ , m_name(name)
+ , m_oo(CppCodeStyleSettings::currentProjectCodeStyleOverview())
+ , m_originalName(m_oo.prettyName(m_name))
+ , m_file(interface.currentFile())
+ {
+ setDescription(Tr::tr("Assign to Local Variable"));
+ }
+
+private:
+ void perform() override
+ {
+ QString type = deduceType();
+ if (type.isEmpty())
+ return;
+ const int origNameLength = m_originalName.length();
+ const QString varName = constructVarName();
+ const QString insertString = type.replace(type.length() - origNameLength, origNameLength,
+ varName + QLatin1String(" = "));
+ m_file->apply(ChangeSet::makeInsert(m_insertPos, insertString));
+
+ // move cursor to new variable name
+ QTextCursor c = m_file->cursor();
+ c.setPosition(m_insertPos + insertString.length() - varName.length() - 3);
+ c.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
+ editor()->setTextCursor(c);
+ }
+
+ QString deduceType() const
+ {
+ const auto settings = CppQuickFixProjectsSettings::getQuickFixSettings(
+ ProjectExplorer::ProjectTree::currentProject());
+ if (m_file->cppDocument()->languageFeatures().cxx11Enabled && settings->useAuto)
+ return "auto " + m_originalName;
+
+ TypeOfExpression typeOfExpression;
+ typeOfExpression.init(semanticInfo().doc, snapshot(), context().bindings());
+ typeOfExpression.setExpandTemplates(true);
+ Scope * const scope = m_file->scopeAt(m_ast->firstToken());
+ const QList<LookupItem> result = typeOfExpression(m_file->textOf(m_ast).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().get();
+ FullySpecifiedType type = rewriteType(result.first().type(), &env, control);
+
+ return m_oo.prettyType(type, m_name);
+ }
+
+ QString constructVarName() const
+ {
+ QString newName = m_originalName;
+ if (newName.startsWith(QLatin1String("get"), Qt::CaseInsensitive)
+ && newName.length() > 3
+ && newName.at(3).isUpper()) {
+ newName.remove(0, 3);
+ newName.replace(0, 1, newName.at(0).toLower());
+ } else if (newName.startsWith(QLatin1String("to"), Qt::CaseInsensitive)
+ && newName.length() > 2
+ && newName.at(2).isUpper()) {
+ newName.remove(0, 2);
+ newName.replace(0, 1, newName.at(0).toLower());
+ } else {
+ newName.replace(0, 1, newName.at(0).toUpper());
+ newName.prepend(QLatin1String("local"));
+ }
+ return newName;
+ }
+
+ const int m_insertPos;
+ const AST * const m_ast;
+ const Name * const m_name;
+ const Overview m_oo;
+ const QString m_originalName;
+ const CppRefactoringFilePtr m_file;
+};
+
+//! Assigns the return value of a function call or a new expression to a local variable
+class AssignToLocalVariable : public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const QList<AST *> &path = interface.path();
+ AST *outerAST = nullptr;
+ SimpleNameAST *nameAST = nullptr;
+
+ for (int i = path.size() - 3; i >= 0; --i) {
+ if (CallAST *callAST = path.at(i)->asCall()) {
+ if (!interface.isCursorOn(callAST))
+ return;
+ if (i - 2 >= 0) {
+ const int idx = i - 2;
+ if (path.at(idx)->asSimpleDeclaration())
+ return;
+ if (path.at(idx)->asExpressionStatement())
+ return;
+ if (path.at(idx)->asMemInitializer())
+ return;
+ if (path.at(idx)->asCall()) { // Fallback if we have a->b()->c()...
+ --i;
+ continue;
+ }
+ }
+ for (int a = i - 1; a > 0; --a) {
+ if (path.at(a)->asBinaryExpression())
+ return;
+ if (path.at(a)->asReturnStatement())
+ return;
+ if (path.at(a)->asCall())
+ return;
+ }
+
+ if (MemberAccessAST *member = path.at(i + 1)->asMemberAccess()) { // member
+ if (NameAST *name = member->member_name)
+ nameAST = name->asSimpleName();
+ } else if (QualifiedNameAST *qname = path.at(i + 2)->asQualifiedName()) { // static or
+ nameAST = qname->unqualified_name->asSimpleName(); // func in ns
+ } else { // normal
+ nameAST = path.at(i + 2)->asSimpleName();
+ }
+
+ if (nameAST) {
+ outerAST = callAST;
+ break;
+ }
+ } else if (NewExpressionAST *newexp = path.at(i)->asNewExpression()) {
+ if (!interface.isCursorOn(newexp))
+ return;
+ if (i - 2 >= 0) {
+ const int idx = i - 2;
+ if (path.at(idx)->asSimpleDeclaration())
+ return;
+ if (path.at(idx)->asExpressionStatement())
+ return;
+ if (path.at(idx)->asMemInitializer())
+ return;
+ }
+ for (int a = i - 1; a > 0; --a) {
+ if (path.at(a)->asReturnStatement())
+ return;
+ if (path.at(a)->asCall())
+ return;
+ }
+
+ if (NamedTypeSpecifierAST *ts = path.at(i + 2)->asNamedTypeSpecifier()) {
+ nameAST = ts->name->asSimpleName();
+ outerAST = newexp;
+ break;
+ }
+ }
+ }
+
+ if (outerAST && nameAST) {
+ const CppRefactoringFilePtr file = interface.currentFile();
+ QList<LookupItem> items;
+ TypeOfExpression typeOfExpression;
+ typeOfExpression.init(interface.semanticInfo().doc, interface.snapshot(),
+ interface.context().bindings());
+ typeOfExpression.setExpandTemplates(true);
+
+ // If items are empty, AssignToLocalVariableOperation will fail.
+ items = typeOfExpression(file->textOf(outerAST).toUtf8(),
+ file->scopeAt(outerAST->firstToken()),
+ TypeOfExpression::Preprocess);
+ if (items.isEmpty())
+ return;
+
+ if (CallAST *callAST = outerAST->asCall()) {
+ items = typeOfExpression(file->textOf(callAST->base_expression).toUtf8(),
+ file->scopeAt(callAST->base_expression->firstToken()),
+ TypeOfExpression::Preprocess);
+ } else {
+ items = typeOfExpression(file->textOf(nameAST).toUtf8(),
+ file->scopeAt(nameAST->firstToken()),
+ TypeOfExpression::Preprocess);
+ }
+
+ for (const LookupItem &item : std::as_const(items)) {
+ if (!item.declaration())
+ continue;
+
+ if (Function *func = item.declaration()->asFunction()) {
+ if (func->isSignal() || func->returnType()->asVoidType())
+ return;
+ } else if (Declaration *dec = item.declaration()->asDeclaration()) {
+ if (Function *func = dec->type()->asFunctionType()) {
+ if (func->isSignal() || func->returnType()->asVoidType())
+ return;
+ }
+ }
+
+ const Name *name = nameAST->name;
+ const int insertPos = interface.currentFile()->startOf(outerAST);
+ result << new AssignToLocalVariableOperation(interface, insertPos, outerAST, name);
+ return;
+ }
+ }
+ }
+};
+
+#ifdef WITH_TESTS
+using namespace Tests;
+
+class AssignToLocalVariableTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void testTemplates()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "template <typename T>\n"
+ "class List {\n"
+ "public:\n"
+ " T first();"
+ "};\n"
+ ;
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n"
+ "void foo() {\n"
+ " List<int> list;\n"
+ " li@st.first();\n"
+ "}\n";
+ expected =
+ "#include \"file.h\"\n"
+ "void foo() {\n"
+ " List<int> list;\n"
+ " auto localFirst = list.first();\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ AssignToLocalVariable factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void test_data()
+ {
+ QTest::addColumn<QByteArray>("original");
+ QTest::addColumn<QByteArray>("expected");
+
+ // Check: Add local variable for a free function.
+ QTest::newRow("freeFunction")
+ << QByteArray(
+ "int foo() {return 1;}\n"
+ "void bar() {fo@o();}\n")
+ << QByteArray(
+ "int foo() {return 1;}\n"
+ "void bar() {auto localFoo = foo();}\n");
+
+ // Check: Add local variable for a member function.
+ QTest::newRow("memberFunction")
+ << QByteArray(
+ "class Foo {public: int* fooFunc();}\n"
+ "void bar() {\n"
+ " Foo *f = new Foo;\n"
+ " @f->fooFunc();\n"
+ "}\n")
+ << QByteArray(
+ "class Foo {public: int* fooFunc();}\n"
+ "void bar() {\n"
+ " Foo *f = new Foo;\n"
+ " auto localFooFunc = f->fooFunc();\n"
+ "}\n");
+
+ // Check: Add local variable for a member function, cursor in the middle (QTCREATORBUG-10355)
+ QTest::newRow("memberFunction2ndGrade1")
+ << QByteArray(
+ "struct Foo {int* func();};\n"
+ "struct Baz {Foo* foo();};\n"
+ "void bar() {\n"
+ " Baz *b = new Baz;\n"
+ " b->foo@()->func();\n"
+ "}")
+ << QByteArray(
+ "struct Foo {int* func();};\n"
+ "struct Baz {Foo* foo();};\n"
+ "void bar() {\n"
+ " Baz *b = new Baz;\n"
+ " auto localFunc = b->foo()->func();\n"
+ "}");
+
+ // Check: Add local variable for a member function, cursor on function call (QTCREATORBUG-10355)
+ QTest::newRow("memberFunction2ndGrade2")
+ << QByteArray(
+ "struct Foo {int* func();};\n"
+ "struct Baz {Foo* foo();};\n"
+ "void bar() {\n"
+ " Baz *b = new Baz;\n"
+ " b->foo()->f@unc();\n"
+ "}")
+ << QByteArray(
+ "struct Foo {int* func();};\n"
+ "struct Baz {Foo* foo();};\n"
+ "void bar() {\n"
+ " Baz *b = new Baz;\n"
+ " auto localFunc = b->foo()->func();\n"
+ "}");
+
+ // Check: Add local variable for a static member function.
+ QTest::newRow("staticMemberFunction")
+ << QByteArray(
+ "class Foo {public: static int* fooFunc();}\n"
+ "void bar() {\n"
+ " Foo::fooF@unc();\n"
+ "}")
+ << QByteArray(
+ "class Foo {public: static int* fooFunc();}\n"
+ "void bar() {\n"
+ " auto localFooFunc = Foo::fooFunc();\n"
+ "}");
+
+ // Check: Add local variable for a new Expression.
+ QTest::newRow("newExpression")
+ << QByteArray(
+ "class Foo {}\n"
+ "void bar() {\n"
+ " new Fo@o;\n"
+ "}")
+ << QByteArray(
+ "class Foo {}\n"
+ "void bar() {\n"
+ " auto localFoo = new Foo;\n"
+ "}");
+
+ // Check: No trigger for function inside member initialization list.
+ QTest::newRow("noInitializationList")
+ << QByteArray(
+ "class Foo\n"
+ "{\n"
+ " public: Foo : m_i(fooF@unc()) {}\n"
+ " int fooFunc() {return 2;}\n"
+ " int m_i;\n"
+ "};\n")
+ << QByteArray();
+
+ // Check: No trigger for void functions.
+ QTest::newRow("noVoidFunction")
+ << QByteArray(
+ "void foo() {}\n"
+ "void bar() {fo@o();}")
+ << QByteArray();
+
+ // Check: No trigger for void member functions.
+ QTest::newRow("noVoidMemberFunction")
+ << QByteArray(
+ "class Foo {public: void fooFunc();}\n"
+ "void bar() {\n"
+ " Foo *f = new Foo;\n"
+ " @f->fooFunc();\n"
+ "}")
+ << QByteArray();
+
+ // Check: No trigger for void static member functions.
+ QTest::newRow("noVoidStaticMemberFunction")
+ << QByteArray(
+ "class Foo {public: static void fooFunc();}\n"
+ "void bar() {\n"
+ " Foo::fo@oFunc();\n"
+ "}")
+ << QByteArray();
+
+ // Check: No trigger for functions in expressions.
+ QTest::newRow("noFunctionInExpression")
+ << QByteArray(
+ "int foo(int a) {return a;}\n"
+ "int bar() {return 1;}"
+ "void baz() {foo(@bar() + bar());}")
+ << QByteArray();
+
+ // Check: No trigger for functions in functions. (QTCREATORBUG-9510)
+ QTest::newRow("noFunctionInFunction")
+ << QByteArray(
+ "int foo(int a, int b) {return a + b;}\n"
+ "int bar(int a) {return a;}\n"
+ "void baz() {\n"
+ " int a = foo(ba@r(), bar());\n"
+ "}\n")
+ << QByteArray();
+
+ // Check: No trigger for functions in return statements (classes).
+ QTest::newRow("noReturnClass1")
+ << QByteArray(
+ "class Foo {public: static void fooFunc();}\n"
+ "Foo* bar() {\n"
+ " return new Fo@o;\n"
+ "}")
+ << QByteArray();
+
+ // Check: No trigger for functions in return statements (classes). (QTCREATORBUG-9525)
+ QTest::newRow("noReturnClass2")
+ << QByteArray(
+ "class Foo {public: int fooFunc();}\n"
+ "int bar() {\n"
+ " return (new Fo@o)->fooFunc();\n"
+ "}")
+ << QByteArray();
+
+ // Check: No trigger for functions in return statements (functions).
+ QTest::newRow("noReturnFunc1")
+ << QByteArray(
+ "class Foo {public: int fooFunc();}\n"
+ "int bar() {\n"
+ " return Foo::fooFu@nc();\n"
+ "}")
+ << QByteArray();
+
+ // Check: No trigger for functions in return statements (functions). (QTCREATORBUG-9525)
+ QTest::newRow("noReturnFunc2")
+ << QByteArray(
+ "int bar() {\n"
+ " return list.firs@t().foo;\n"
+ "}\n")
+ << QByteArray();
+
+ // Check: No trigger for functions which does not match in signature.
+ QTest::newRow("noSignatureMatch")
+ << QByteArray(
+ "int someFunc(int);\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " some@Func();\n"
+ "}")
+ << QByteArray();
+ }
+
+ void test()
+ {
+ QFETCH(QByteArray, original);
+ QFETCH(QByteArray, expected);
+ AssignToLocalVariable factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+};
+
+QObject *AssignToLocalVariable::createTest() { return new AssignToLocalVariableTest; }
+
+#endif // WITH_TESTS
+} // namespace
+
+void registerAssignToLocalVariableQuickfix()
+{
+ CppQuickFixFactory::registerFactory<AssignToLocalVariable>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <assigntolocalvariable.moc>
+#endif
diff --git a/src/plugins/cppeditor/quickfixes/assigntolocalvariable.h b/src/plugins/cppeditor/quickfixes/assigntolocalvariable.h
new file mode 100644
index 0000000000..d8cd793c9f
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/assigntolocalvariable.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerAssignToLocalVariableQuickfix();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/bringidentifierintoscope.cpp b/src/plugins/cppeditor/quickfixes/bringidentifierintoscope.cpp
new file mode 100644
index 0000000000..3c4df4cb76
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/bringidentifierintoscope.cpp
@@ -0,0 +1,1497 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "bringidentifierintoscope.h"
+
+#include "../cppeditortr.h"
+#include "../cpplocatordata.h"
+#include "../cppprojectfile.h"
+#include "../cpprefactoringchanges.h"
+#include "../indexitem.h"
+#include "cppquickfix.h"
+#include "cppquickfixhelpers.h"
+
+#include <cplusplus/Overview.h>
+#include <projectexplorer/projectnodes.h>
+#include <projectexplorer/projecttree.h>
+#include <texteditor/quickfix.h>
+
+#ifdef WITH_TESTS
+#include "../cppsourceprocessertesthelper.h"
+#include "../cpptoolstestcase.h"
+#include "cppquickfix_test.h"
+#include <QtTest>
+#endif
+
+using namespace CPlusPlus;
+using namespace ProjectExplorer;
+using namespace Utils;
+
+#ifdef WITH_TESTS
+namespace CppEditor::Internal::Tests {
+typedef QList<TestDocumentPtr> QuickFixTestDocuments;
+}
+Q_DECLARE_METATYPE(CppEditor::Internal::Tests::QuickFixTestDocuments)
+#endif
+
+namespace CppEditor::Internal {
+namespace {
+
+static QString findShortestInclude(
+ const QString currentDocumentFilePath,
+ const QString candidateFilePath,
+ const HeaderPaths &headerPaths)
+{
+ QString result;
+
+ const QFileInfo fileInfo(candidateFilePath);
+
+ if (fileInfo.path() == QFileInfo(currentDocumentFilePath).path()) {
+ result = QLatin1Char('"') + fileInfo.fileName() + QLatin1Char('"');
+ } else {
+ for (const HeaderPath &headerPath : headerPaths) {
+ if (!candidateFilePath.startsWith(headerPath.path))
+ continue;
+ QString relativePath = candidateFilePath.mid(headerPath.path.size());
+ if (!relativePath.isEmpty() && relativePath.at(0) == QLatin1Char('/'))
+ relativePath = relativePath.mid(1);
+ if (result.isEmpty() || relativePath.size() + 2 < result.size())
+ result = QLatin1Char('<') + relativePath + QLatin1Char('>');
+ }
+ }
+
+ return result;
+}
+
+static QString findMatchingInclude(const QString &className, const HeaderPaths &headerPaths)
+{
+ const QStringList candidateFileNames{
+ className,
+ className + ".h",
+ className + ".hpp",
+ className.toLower(),
+ className.toLower() + ".h",
+ className.toLower() + ".hpp"};
+ for (const QString &fileName : candidateFileNames) {
+ for (const HeaderPath &headerPath : headerPaths) {
+ const QString headerPathCandidate = headerPath.path + QLatin1Char('/') + fileName;
+ const QFileInfo fileInfo(headerPathCandidate);
+ if (fileInfo.exists() && fileInfo.isFile())
+ return '<' + fileName + '>';
+ }
+ }
+ return {};
+}
+
+static HeaderPaths relevantHeaderPaths(const QString &filePath)
+{
+ HeaderPaths headerPaths;
+
+ const QList<ProjectPart::ConstPtr> projectParts = CppModelManager::projectPart(filePath);
+ if (projectParts.isEmpty()) { // Not part of any project, better use all include paths than none
+ headerPaths += CppModelManager::headerPaths();
+ } else {
+ for (const ProjectPart::ConstPtr &part : projectParts)
+ headerPaths += part->headerPaths;
+ }
+
+ return headerPaths;
+}
+
+static NameAST *nameUnderCursor(const QList<AST *> &path)
+{
+ if (path.isEmpty())
+ return nullptr;
+
+ NameAST *nameAst = nullptr;
+ for (int i = path.size() - 1; i >= 0; --i) {
+ AST * const ast = path.at(i);
+ if (SimpleNameAST *simpleName = ast->asSimpleName()) {
+ nameAst = simpleName;
+ } else if (TemplateIdAST *templateId = ast->asTemplateId()) {
+ nameAst = templateId;
+ } else if (nameAst && ast->asNamedTypeSpecifier()) {
+ break; // Stop at "Foo" for "N::Bar<@Foo>"
+ } else if (QualifiedNameAST *qualifiedName = ast->asQualifiedName()) {
+ nameAst = qualifiedName;
+ break;
+ }
+ }
+
+ return nameAst;
+}
+
+enum class LookupResult { Declared, ForwardDeclared, NotDeclared };
+static LookupResult lookUpDefinition(const CppQuickFixInterface &interface, const NameAST *nameAst)
+{
+ QTC_ASSERT(nameAst && nameAst->name, return LookupResult::NotDeclared);
+
+ // Find the enclosing scope
+ int line, column;
+ const Document::Ptr doc = interface.semanticInfo().doc;
+ doc->translationUnit()->getTokenPosition(nameAst->firstToken(), &line, &column);
+ Scope *scope = doc->scopeAt(line, column);
+ if (!scope)
+ return LookupResult::NotDeclared;
+
+ // Try to find the class/template definition
+ const Name *name = nameAst->name;
+ const QList<LookupItem> results = interface.context().lookup(name, scope);
+ LookupResult best = LookupResult::NotDeclared;
+ for (const LookupItem &item : results) {
+ if (Symbol *declaration = item.declaration()) {
+ if (declaration->asClass())
+ return LookupResult::Declared;
+ if (declaration->asForwardClassDeclaration()) {
+ best = LookupResult::ForwardDeclared;
+ continue;
+ }
+ if (Template *templ = declaration->asTemplate()) {
+ if (Symbol *declaration = templ->declaration()) {
+ if (declaration->asClass())
+ return LookupResult::Declared;
+ if (declaration->asForwardClassDeclaration()) {
+ best = LookupResult::ForwardDeclared;
+ continue;
+ }
+ }
+ }
+ return LookupResult::Declared;
+ }
+ }
+
+ return best;
+}
+
+static QString templateNameAsString(const TemplateNameId *templateName)
+{
+ const Identifier *id = templateName->identifier();
+ return QString::fromUtf8(id->chars(), id->size());
+}
+
+static Snapshot forwardingHeaders(const CppQuickFixInterface &interface)
+{
+ Snapshot result;
+
+ const Snapshot docs = interface.snapshot();
+ for (Document::Ptr doc : docs) {
+ if (doc->globalSymbolCount() == 0 && doc->resolvedIncludes().size() == 1)
+ result.insert(doc);
+ }
+
+ return result;
+}
+
+static QList<IndexItem::Ptr> matchName(const Name *name, QString *className)
+{
+ if (!name)
+ return {};
+
+ QString simpleName;
+ QList<IndexItem::Ptr> matches;
+ CppLocatorData *locatorData = CppModelManager::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 {
+ 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);
+ }
+
+ if (matches.isEmpty())
+ matches = locatorData->findSymbols(IndexItem::Class, *className);
+
+ if (matches.isEmpty() && !simpleName.isEmpty())
+ *className = simpleName;
+
+ return matches;
+}
+
+class AddIncludeForUndefinedIdentifierOp : public CppQuickFixOperation
+{
+public:
+ AddIncludeForUndefinedIdentifierOp(const CppQuickFixInterface &interface, int priority,
+ const QString &include);
+ void perform() override;
+
+ QString include() const { return m_include; }
+
+private:
+ QString m_include;
+};
+
+class AddForwardDeclForUndefinedIdentifierOp : public CppQuickFixOperation
+{
+public:
+ AddForwardDeclForUndefinedIdentifierOp(const CppQuickFixInterface &interface, int priority,
+ const QString &fqClassName, int symbolPos);
+private:
+ void perform() override
+ {
+ const QStringList parts = m_className.split("::");
+ QTC_ASSERT(!parts.isEmpty(), return);
+ const QStringList namespaces = parts.mid(0, parts.length() - 1);
+
+ CppRefactoringFilePtr file = currentFile();
+
+ NSVisitor visitor(file.data(), namespaces, m_symbolPos);
+ visitor.accept(file->cppDocument()->translationUnit()->ast());
+ const auto stringToInsert = [&visitor, symbol = parts.last()] {
+ QString s = "\n";
+ for (const QString &ns : visitor.remainingNamespaces())
+ s += "namespace " + ns + " { ";
+ s += "class " + symbol + ';';
+ for (int i = 0; i < visitor.remainingNamespaces().size(); ++i)
+ s += " }";
+ return s;
+ };
+
+ int insertPos = 0;
+
+ // Find the position to insert:
+ // If we have a matching namespace, we do the insertion there.
+ // If we don't have a matching namespace, but there is another namespace in the file,
+ // we assume that to be a good position for our insertion.
+ // Otherwise, do the insertion after the last include that comes before the use of the symbol.
+ // If there is no such include, do the insertion before the first token.
+ if (visitor.enclosingNamespace()) {
+ insertPos = file->startOf(visitor.enclosingNamespace()->linkage_body) + 1;
+ } else if (visitor.firstNamespace()) {
+ insertPos = file->startOf(visitor.firstNamespace());
+ } else {
+ const QTextCursor tc = file->document()->find(
+ QRegularExpression("^\\s*#include .*$"),
+ m_symbolPos,
+ QTextDocument::FindBackward | QTextDocument::FindCaseSensitively);
+ if (!tc.isNull())
+ insertPos = tc.position() + 1;
+ else if (visitor.firstToken())
+ insertPos = file->startOf(visitor.firstToken());
+ }
+
+ QString insertion = stringToInsert();
+ if (file->charAt(insertPos - 1) != QChar::ParagraphSeparator)
+ insertion.prepend('\n');
+ if (file->charAt(insertPos) != QChar::ParagraphSeparator)
+ insertion.append('\n');
+ file->apply(ChangeSet::makeInsert(insertPos, insertion));
+ }
+
+ const QString m_className;
+ const int m_symbolPos;
+};
+
+AddIncludeForUndefinedIdentifierOp::AddIncludeForUndefinedIdentifierOp(
+ const CppQuickFixInterface &interface, int priority, const QString &include)
+ : CppQuickFixOperation(interface, priority)
+ , m_include(include)
+{
+ setDescription(Tr::tr("Add #include %1").arg(m_include));
+}
+
+void AddIncludeForUndefinedIdentifierOp::perform()
+{
+ CppRefactoringFilePtr file = currentFile();
+
+ ChangeSet changes;
+ insertNewIncludeDirective(m_include, file, semanticInfo().doc, changes);
+ file->apply(changes);
+}
+
+AddForwardDeclForUndefinedIdentifierOp::AddForwardDeclForUndefinedIdentifierOp(
+ const CppQuickFixInterface &interface,
+ int priority,
+ const QString &fqClassName,
+ int symbolPos)
+ : CppQuickFixOperation(interface, priority), m_className(fqClassName), m_symbolPos(symbolPos)
+{
+ setDescription(Tr::tr("Add Forward Declaration for %1").arg(m_className));
+}
+
+/*!
+ Adds an include for an undefined identifier or only forward declared identifier.
+ Activates on: the undefined identifier
+*/
+class AddIncludeForUndefinedIdentifier : public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const NameAST *nameAst = nameUnderCursor(interface.path());
+ if (!nameAst || !nameAst->name)
+ return;
+
+ const LookupResult lookupResult = lookUpDefinition(interface, nameAst);
+ if (lookupResult == LookupResult::Declared)
+ return;
+
+ QString className;
+ const QString currentDocumentFilePath = interface.semanticInfo().doc->filePath().toString();
+ const HeaderPaths headerPaths = relevantHeaderPaths(currentDocumentFilePath);
+ FilePaths headers;
+
+ const QList<IndexItem::Ptr> matches = matchName(nameAst->name, &className);
+ // Find an include file through the locator
+ if (!matches.isEmpty()) {
+ QList<IndexItem::Ptr> indexItems;
+ const Snapshot forwardHeaders = forwardingHeaders(interface);
+ for (const IndexItem::Ptr &info : matches) {
+ if (!info || info->symbolName() != className)
+ continue;
+ indexItems << info;
+
+ Snapshot localForwardHeaders = forwardHeaders;
+ localForwardHeaders.insert(interface.snapshot().document(info->filePath()));
+ FilePaths headerAndItsForwardingHeaders;
+ headerAndItsForwardingHeaders << info->filePath();
+ headerAndItsForwardingHeaders += localForwardHeaders.filesDependingOn(info->filePath());
+
+ for (const FilePath &header : std::as_const(headerAndItsForwardingHeaders)) {
+ const QString include = findShortestInclude(currentDocumentFilePath,
+ header.toString(),
+ headerPaths);
+ if (include.size() > 2) {
+ const QString headerFileName = info->filePath().fileName();
+ QTC_ASSERT(!headerFileName.isEmpty(), break);
+
+ int priority = 0;
+ if (headerFileName == className)
+ priority = 2;
+ else if (headerFileName.at(1).isUpper())
+ priority = 1;
+
+ result << new AddIncludeForUndefinedIdentifierOp(interface, priority,
+ include);
+ headers << header;
+ }
+ }
+ }
+
+ if (lookupResult == LookupResult::NotDeclared && indexItems.size() == 1) {
+ QString qualifiedName = Overview().prettyName(nameAst->name);
+ if (qualifiedName.startsWith("::"))
+ qualifiedName.remove(0, 2);
+ if (indexItems.first()->scopedSymbolName().endsWith(qualifiedName)) {
+ const Node * const node = ProjectTree::nodeForFile(interface.filePath());
+ FileType fileType = node && node->asFileNode() ? node->asFileNode()->fileType()
+ : FileType::Unknown;
+ if (fileType == FileType::Unknown
+ && ProjectFile::isHeader(ProjectFile::classify(interface.filePath().toString()))) {
+ fileType = FileType::Header;
+ }
+ if (fileType == FileType::Header) {
+ result << new AddForwardDeclForUndefinedIdentifierOp(
+ interface, 0, indexItems.first()->scopedSymbolName(),
+ interface.currentFile()->startOf(nameAst));
+ }
+ }
+ }
+ }
+
+ if (className.isEmpty())
+ return;
+
+ // Fallback: Check the include paths for files that look like candidates
+ // for the given name.
+ if (!Utils::contains(headers,
+ [&className](const FilePath &fp) { return fp.fileName() == className; })) {
+ const QString include = findMatchingInclude(className, headerPaths);
+ const auto matcher = [&include](const TextEditor::QuickFixOperation::Ptr &o) {
+ const auto includeOp = o.dynamicCast<AddIncludeForUndefinedIdentifierOp>();
+ return includeOp && includeOp->include() == include;
+ };
+ if (!include.isEmpty() && !contains(result, matcher))
+ result << new AddIncludeForUndefinedIdentifierOp(interface, 1, include);
+ }
+ }
+};
+
+#ifdef WITH_TESTS
+using namespace Tests;
+using namespace ::CppEditor::Tests;
+using namespace ::CppEditor::Tests::Internal; // FIXME: Get rid of this nonsense namespace
+
+/// Delegates directly to AddIncludeForUndefinedIdentifierOp for easier testing.
+class AddIncludeForUndefinedIdentifierTestFactory : public CppQuickFixFactory
+{
+public:
+ AddIncludeForUndefinedIdentifierTestFactory(const QString &include)
+ : m_include(include) {}
+
+ void doMatch(const CppQuickFixInterface &cppQuickFixInterface, QuickFixOperations &result) override
+ {
+ result << new AddIncludeForUndefinedIdentifierOp(cppQuickFixInterface, 0, m_include);
+ }
+
+private:
+ const QString m_include;
+};
+
+class AddForwardDeclForUndefinedIdentifierTestFactory : public CppQuickFixFactory
+{
+public:
+ AddForwardDeclForUndefinedIdentifierTestFactory(const QString &className, int symbolPos)
+ : m_className(className), m_symbolPos(symbolPos) {}
+
+ void doMatch(const CppQuickFixInterface &cppQuickFixInterface, QuickFixOperations &result) override
+ {
+ result << new AddForwardDeclForUndefinedIdentifierOp(cppQuickFixInterface, 0,
+ m_className, m_symbolPos);
+ }
+
+private:
+ const QString m_className;
+ const int m_symbolPos;
+};
+
+class BringIdentifierIntoScopeTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void testAddIncludeForUndefinedIdentifier_data()
+ {
+ QTest::addColumn<QString>("headerPath");
+ QTest::addColumn<QuickFixTestDocuments>("testDocuments");
+ QTest::addColumn<int>("refactoringOperationIndex");
+ QTest::addColumn<QString>("includeForTestFactory");
+
+ const int firstRefactoringOperation = 0;
+ const int secondRefactoringOperation = 1;
+
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // -------------------------------------------------------------------------------------------
+
+ // Header File
+ original = "class Foo {};\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("afile.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"header.h\"\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " Fo@o foo;\n"
+ "}\n"
+ ;
+ expected =
+ "#include \"afile.h\"\n"
+ "#include \"header.h\"\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " Foo foo;\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("afile.cpp", original, expected);
+ QTest::newRow("onSimpleName")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ // Header File
+ original = "namespace N { class Foo {}; }\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("afile.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"header.h\"\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " N::Fo@o foo;\n"
+ "}\n"
+ ;
+ expected =
+ "#include \"afile.h\"\n"
+ "#include \"header.h\"\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " N::Foo foo;\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("afile.cpp", original, expected);
+ QTest::newRow("onNameOfQualifiedName")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ // Header File
+ original = "namespace N { class Foo {}; }\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("afile.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"header.h\"\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " @N::Foo foo;\n"
+ "}\n"
+ ;
+ expected =
+ "#include \"afile.h\"\n"
+ "#include \"header.h\"\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " N::Foo foo;\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("afile.cpp", original, expected);
+ QTest::newRow("onBaseOfQualifiedName")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ // Header File
+ original = "class Foo { static void bar() {} };\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("afile.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"header.h\"\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " @Foo::bar();\n"
+ "}\n"
+ ;
+ expected =
+ "#include \"afile.h\"\n"
+ "#include \"header.h\"\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " Foo::bar();\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("afile.cpp", original, expected);
+ QTest::newRow("onBaseOfQualifiedClassName")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ // Header File
+ original = "template <typename T> class Foo { static void bar() {} };\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("afile.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"header.h\"\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " @Foo<int>::bar();\n"
+ "}\n"
+ ;
+ expected =
+ "#include \"afile.h\"\n"
+ "#include \"header.h\"\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " Foo<int>::bar();\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("afile.cpp", original, expected);
+ QTest::newRow("onBaseOfQualifiedTemplateClassName")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ // Header File
+ original = "namespace N { template <typename T> class Foo {}; }\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("afile.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"header.h\"\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " @N::Foo<Bar> foo;\n"
+ "}\n"
+ ;
+ expected =
+ "#include \"afile.h\"\n"
+ "#include \"header.h\"\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " N::Foo<Bar> foo;\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("afile.cpp", original, expected);
+ QTest::newRow("onTemplateName")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ // Header File
+ original = "namespace N { template <typename T> class Foo {}; }\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("afile.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"header.h\"\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " N::Bar<@Foo> foo;\n"
+ "}\n"
+ ;
+ expected =
+ "#include \"afile.h\"\n"
+ "#include \"header.h\"\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " N::Bar<Foo> foo;\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("afile.cpp", original, expected);
+ QTest::newRow("onTemplateNameInsideArguments")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ // Header File
+ original = "class Foo {};\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("afile.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"header.h\"\n"
+ "\n"
+ "class Foo;\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " @Foo foo;\n"
+ "}\n"
+ ;
+ expected =
+ "#include \"afile.h\"\n"
+ "#include \"header.h\"\n"
+ "\n"
+ "class Foo;\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " Foo foo;\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("afile.cpp", original, expected);
+ QTest::newRow("withForwardDeclaration")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ // Header File
+ original = "template<class T> class Foo {};\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("afile.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"header.h\"\n"
+ "\n"
+ "template<class T> class Foo;\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " @Foo foo;\n"
+ "}\n"
+ ;
+ expected =
+ "#include \"afile.h\"\n"
+ "#include \"header.h\"\n"
+ "\n"
+ "template<class T> class Foo;\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " Foo foo;\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("afile.cpp", original, expected);
+ QTest::newRow("withForwardDeclaration2")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ // Header File
+ original = "template<class T> class QMyClass {};\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("qmyclass.h", original, expected);
+
+ // Forward Header File
+ original = "#include \"qmyclass.h\"\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("QMyClass", original, expected);
+
+ // Source File
+ original =
+ "#include \"header.h\"\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " @QMyClass c;\n"
+ "}\n"
+ ;
+ expected =
+ "#include \"QMyClass\"\n"
+ "#include \"header.h\"\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " QMyClass c;\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("afile.cpp", original, expected);
+ QTest::newRow("withForwardHeader")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << secondRefactoringOperation << "";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ original =
+ "void @f();\n"
+ "#include \"file.moc\";\n"
+ ;
+ expected =
+ "#include \"file.h\"\n"
+ "\n"
+ "void f();\n"
+ "#include \"file.moc\";\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("insertingIgnoreMoc")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "\"file.h\"";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ original =
+ "#include \"y.h\"\n"
+ "#include \"z.h\"\n"
+ "\n@"
+ ;
+ expected =
+ "#include \"file.h\"\n"
+ "#include \"y.h\"\n"
+ "#include \"z.h\"\n"
+ "\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("insertingSortingTop")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "\"file.h\"";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ original =
+ "#include \"a.h\"\n"
+ "#include \"z.h\"\n"
+ "\n@"
+ ;
+ expected =
+ "#include \"a.h\"\n"
+ "#include \"file.h\"\n"
+ "#include \"z.h\"\n"
+ "\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("insertingSortingMiddle")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "\"file.h\"";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ original =
+ "#include \"a.h\"\n"
+ "#include \"b.h\"\n"
+ "\n@"
+ ;
+ expected =
+ "#include \"a.h\"\n"
+ "#include \"b.h\"\n"
+ "#include \"file.h\"\n"
+ "\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("insertingSortingBottom")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "\"file.h\"";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ original =
+ "#include \"b.h\"\n"
+ "#include \"a.h\"\n"
+ "\n@"
+ ;
+ expected =
+ "#include \"b.h\"\n"
+ "#include \"a.h\"\n"
+ "#include \"file.h\"\n"
+ "\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("inserting_appendToUnsorted")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "\"file.h\"";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ original =
+ "#include <a.h>\n"
+ "#include <b.h>\n"
+ "\n@"
+ ;
+ expected =
+ "#include \"file.h\"\n"
+ "\n"
+ "#include <a.h>\n"
+ "#include <b.h>\n"
+ "\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("inserting_firstLocalIncludeAtFront")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "\"file.h\"";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ original =
+ "#include \"a.h\"\n"
+ "#include \"b.h\"\n"
+ "\n"
+ "void @f();\n"
+ ;
+ expected =
+ "#include \"a.h\"\n"
+ "#include \"b.h\"\n"
+ "\n"
+ "#include <file.h>\n"
+ "\n"
+ "void f();\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("firstGlobalIncludeAtBack")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "<file.h>";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ original =
+ "#include \"prefixa.h\"\n"
+ "#include \"prefixb.h\"\n"
+ "\n"
+ "#include \"foo.h\"\n"
+ "\n@"
+ ;
+ expected =
+ "#include \"prefixa.h\"\n"
+ "#include \"prefixb.h\"\n"
+ "#include \"prefixc.h\"\n"
+ "\n"
+ "#include \"foo.h\"\n"
+ "\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("inserting_preferGroupWithLongerMatchingPrefix")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "\"prefixc.h\"";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ original =
+ "#include \"lib/file.h\"\n"
+ "#include \"lib/fileother.h\"\n"
+ "\n@"
+ ;
+ expected =
+ "#include \"lib/file.h\"\n"
+ "#include \"lib/fileother.h\"\n"
+ "\n"
+ "#include \"file.h\"\n"
+ "\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("inserting_newGroupIfOnlyDifferentIncludeDirs")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "\"file.h\"";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ original =
+ "#include <lib/file.h>\n"
+ "#include <otherlib/file.h>\n"
+ "#include <utils/file.h>\n"
+ "\n@"
+ ;
+ expected =
+ "#include <firstlib/file.h>\n"
+ "#include <lib/file.h>\n"
+ "#include <otherlib/file.h>\n"
+ "#include <utils/file.h>\n"
+ "\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("inserting_mixedDirsSorted")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "<firstlib/file.h>";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ original =
+ "#include <otherlib/file.h>\n"
+ "#include <lib/file.h>\n"
+ "#include <utils/file.h>\n"
+ "\n@"
+ ;
+ expected =
+ "#include <otherlib/file.h>\n"
+ "#include <lib/file.h>\n"
+ "#include <utils/file.h>\n"
+ "#include <lastlib/file.h>\n"
+ "\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("inserting_mixedDirsUnsorted")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "<lastlib/file.h>";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ original =
+ "#include \"a.h\"\n"
+ "#include <global.h>\n"
+ "\n@"
+ ;
+ expected =
+ "#include \"a.h\"\n"
+ "#include \"z.h\"\n"
+ "#include <global.h>\n"
+ "\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("inserting_mixedIncludeTypes1")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "\"z.h\"";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ original =
+ "#include \"z.h\"\n"
+ "#include <global.h>\n"
+ "\n@"
+ ;
+ expected =
+ "#include \"a.h\"\n"
+ "#include \"z.h\"\n"
+ "#include <global.h>\n"
+ "\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("inserting_mixedIncludeTypes2")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "\"a.h\"";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ original =
+ "#include \"z.h\"\n"
+ "#include <global.h>\n"
+ "\n@"
+ ;
+ expected =
+ "#include \"z.h\"\n"
+ "#include \"lib/file.h\"\n"
+ "#include <global.h>\n"
+ "\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("inserting_mixedIncludeTypes3")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "\"lib/file.h\"";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ original =
+ "#include \"z.h\"\n"
+ "#include <global.h>\n"
+ "\n@"
+ ;
+ expected =
+ "#include \"z.h\"\n"
+ "#include <global.h>\n"
+ "#include <lib/file.h>\n"
+ "\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("inserting_mixedIncludeTypes4")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "<lib/file.h>";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ original =
+ "void @f();\n"
+ ;
+ expected =
+ "#include \"file.h\"\n"
+ "\n"
+ "void f();\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("inserting_noinclude")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "\"file.h\"";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ original =
+ "#ifndef FOO_H\n"
+ "#define FOO_H\n"
+ "void @f();\n"
+ "#endif\n"
+ ;
+ expected =
+ "#ifndef FOO_H\n"
+ "#define FOO_H\n"
+ "\n"
+ "#include \"file.h\"\n"
+ "\n"
+ "void f();\n"
+ "#endif\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("inserting_onlyIncludeGuard")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "\"file.h\"";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ original =
+ "\n"
+ "// comment\n"
+ "\n"
+ "void @f();\n"
+ ;
+ expected =
+ "\n"
+ "// comment\n"
+ "\n"
+ "#include \"file.h\"\n"
+ "\n"
+ "void @f();\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("inserting_veryFirstIncludeCppStyleCommentOnTop")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "\"file.h\"";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ original =
+ "\n"
+ "/*\n"
+ " comment\n"
+ " */\n"
+ "\n"
+ "void @f();\n"
+ ;
+ expected =
+ "\n"
+ "/*\n"
+ " comment\n"
+ " */\n"
+ "\n"
+ "#include \"file.h\"\n"
+ "\n"
+ "void @f();\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("inserting_veryFirstIncludeCStyleCommentOnTop")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "\"file.h\"";
+ testDocuments.clear();
+
+ // -------------------------------------------------------------------------------------------
+
+ original =
+ "@QDir dir;\n"
+ ;
+ expected =
+ "#include <QDir>\n"
+ "\n"
+ "QDir dir;\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("inserting_checkQSomethingInQtIncludePaths")
+ << TestIncludePaths::globalQtCoreIncludePath()
+ << testDocuments << firstRefactoringOperation << "";
+ testDocuments.clear();
+
+ original =
+ "std::s@tring s;\n"
+ ;
+ expected =
+ "#include <string>\n"
+ "\n"
+ "std::string s;\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QTest::newRow("inserting_std::string")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "";
+ testDocuments.clear();
+
+ original = "class A{};";
+ testDocuments << CppTestDocument::create("a.h", original, original);
+ original = "class B{};";
+ testDocuments << CppTestDocument::create("b.h", original, original);
+ original =
+ "#include \"b.h\"\n"
+ "@A a;\n"
+ "B b;";
+ expected =
+ "#include \"b.h\"\n\n"
+ "#include \"a.h\"\n"
+ "A a;\n"
+ "B b;";
+ testDocuments << CppTestDocument::create("b.cpp", original, expected);
+ QTest::newRow("preserve first header")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "";
+ testDocuments.clear();
+
+ original = "class C{};";
+ testDocuments << CppTestDocument::create("c.h", original, original);
+ original = "class B{};";
+ testDocuments << CppTestDocument::create("b.h", original, original);
+ original =
+ "#include \"c.h\"\n"
+ "C c;\n"
+ "@B b;";
+ expected =
+ "#include \"b.h\"\n"
+ "#include \"c.h\"\n"
+ "C c;\n"
+ "B b;";
+ testDocuments << CppTestDocument::create("x.cpp", original, expected);
+ QTest::newRow("do not preserve first header")
+ << TestIncludePaths::globalIncludePath()
+ << testDocuments << firstRefactoringOperation << "";
+ testDocuments.clear();
+ }
+
+ void testAddIncludeForUndefinedIdentifier()
+ {
+ QFETCH(QString, headerPath);
+ QFETCH(QuickFixTestDocuments, testDocuments);
+ QFETCH(int, refactoringOperationIndex);
+ QFETCH(QString, includeForTestFactory);
+
+ TemporaryDir temporaryDir;
+ QVERIFY(temporaryDir.isValid());
+ for (const TestDocumentPtr &testDocument : std::as_const(testDocuments))
+ testDocument->setBaseDirectory(temporaryDir.path());
+
+ QScopedPointer<CppQuickFixFactory> factory;
+ if (includeForTestFactory.isEmpty())
+ factory.reset(new AddIncludeForUndefinedIdentifier);
+ else
+ factory.reset(new AddIncludeForUndefinedIdentifierTestFactory(includeForTestFactory));
+
+ QuickFixOperationTest::run(testDocuments, factory.data(), headerPath,
+ refactoringOperationIndex);
+ }
+
+ void testAddIncludeForUndefinedIdentifierNoDoubleQtHeaderInclude()
+ {
+ TemporaryDir temporaryDir;
+ QVERIFY(temporaryDir.isValid());
+
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ const QByteArray base = temporaryDir.path().toUtf8();
+
+ // This file makes the QDir definition available so that locator finds it.
+ original = expected = "#include <QDir>\n"
+ "void avoidBeingRecognizedAsForwardingHeader();";
+ testDocuments << CppTestDocument::create(base + "/fileUsingQDir.cpp", original, expected);
+
+ original = expected = "@QDir dir;\n";
+ testDocuments << CppTestDocument::create(base + "/fileWantsToUseQDir.cpp", original, expected);
+
+ AddIncludeForUndefinedIdentifier factory;
+ const QStringList expectedOperations = QStringList("Add #include <QDir>");
+ QuickFixOfferedOperationsTest(
+ testDocuments,
+ &factory,
+ ProjectExplorer::toUserHeaderPaths(
+ QStringList{TestIncludePaths::globalQtCoreIncludePath()}),
+ expectedOperations);
+ }
+
+ void testAddForwardDeclForUndefinedIdentifier_data()
+ {
+ QTest::addColumn<QuickFixTestDocuments>("testDocuments");
+ QTest::addColumn<QString>("symbol");
+ QTest::addColumn<int>("symbolPos");
+
+ QByteArray original;
+ QByteArray expected;
+
+ original =
+ "#pragma once\n"
+ "\n"
+ "void f(const Blu@bb &b)\n"
+ "{\n"
+ "}\n"
+ ;
+ expected =
+ "#pragma once\n"
+ "\n"
+ "\n"
+ "class Blubb;\n"
+ "void f(const Blubb &b)\n"
+ "{\n"
+ "}\n"
+ ;
+ QTest::newRow("unqualified symbol")
+ << QuickFixTestDocuments{CppTestDocument::create("theheader.h", original, expected)}
+ << "Blubb" << original.indexOf('@');
+
+ original =
+ "#pragma once\n"
+ "\n"
+ "namespace NS {\n"
+ "class C;\n"
+ "}\n"
+ "void f(const NS::Blu@bb &b)\n"
+ "{\n"
+ "}\n"
+ ;
+ expected =
+ "#pragma once\n"
+ "\n"
+ "namespace NS {\n"
+ "\n"
+ "class Blubb;\n"
+ "class C;\n"
+ "}\n"
+ "void f(const NS::Blubb &b)\n"
+ "{\n"
+ "}\n"
+ ;
+ QTest::newRow("qualified symbol, full namespace present")
+ << QuickFixTestDocuments{CppTestDocument::create("theheader.h", original, expected)}
+ << "NS::Blubb" << original.indexOf('@');
+
+ original =
+ "#pragma once\n"
+ "\n"
+ "namespace NS {\n"
+ "class C;\n"
+ "}\n"
+ "void f(const NS::NS2::Blu@bb &b)\n"
+ "{\n"
+ "}\n"
+ ;
+ expected =
+ "#pragma once\n"
+ "\n"
+ "namespace NS {\n"
+ "\n"
+ "namespace NS2 { class Blubb; }\n"
+ "class C;\n"
+ "}\n"
+ "void f(const NS::NS2::Blubb &b)\n"
+ "{\n"
+ "}\n"
+ ;
+ QTest::newRow("qualified symbol, partial namespace present")
+ << QuickFixTestDocuments{CppTestDocument::create("theheader.h", original, expected)}
+ << "NS::NS2::Blubb" << original.indexOf('@');
+
+ original =
+ "#pragma once\n"
+ "\n"
+ "namespace NS {\n"
+ "class C;\n"
+ "}\n"
+ "void f(const NS2::Blu@bb &b)\n"
+ "{\n"
+ "}\n"
+ ;
+ expected =
+ "#pragma once\n"
+ "\n"
+ "\n"
+ "namespace NS2 { class Blubb; }\n"
+ "namespace NS {\n"
+ "class C;\n"
+ "}\n"
+ "void f(const NS2::Blubb &b)\n"
+ "{\n"
+ "}\n"
+ ;
+ QTest::newRow("qualified symbol, other namespace present")
+ << QuickFixTestDocuments{CppTestDocument::create("theheader.h", original, expected)}
+ << "NS2::Blubb" << original.indexOf('@');
+
+ original =
+ "#pragma once\n"
+ "\n"
+ "void f(const NS2::Blu@bb &b)\n"
+ "{\n"
+ "}\n"
+ ;
+ expected =
+ "#pragma once\n"
+ "\n"
+ "\n"
+ "namespace NS2 { class Blubb; }\n"
+ "void f(const NS2::Blubb &b)\n"
+ "{\n"
+ "}\n"
+ ;
+ QTest::newRow("qualified symbol, no namespace present")
+ << QuickFixTestDocuments{CppTestDocument::create("theheader.h", original, expected)}
+ << "NS2::Blubb" << original.indexOf('@');
+
+ original =
+ "#pragma once\n"
+ "\n"
+ "void f(const NS2::Blu@bb &b)\n"
+ "{\n"
+ "}\n"
+ "namespace NS2 {}\n"
+ ;
+ expected =
+ "#pragma once\n"
+ "\n"
+ "\n"
+ "namespace NS2 { class Blubb; }\n"
+ "void f(const NS2::Blubb &b)\n"
+ "{\n"
+ "}\n"
+ "namespace NS2 {}\n"
+ ;
+ QTest::newRow("qualified symbol, existing namespace after symbol")
+ << QuickFixTestDocuments{CppTestDocument::create("theheader.h", original, expected)}
+ << "NS2::Blubb" << original.indexOf('@');
+ }
+
+ void testAddForwardDeclForUndefinedIdentifier()
+ {
+ QFETCH(QuickFixTestDocuments, testDocuments);
+ QFETCH(QString, symbol);
+ QFETCH(int, symbolPos);
+
+ TemporaryDir temporaryDir;
+ QVERIFY(temporaryDir.isValid());
+ testDocuments.first()->setBaseDirectory(temporaryDir.path());
+
+ QScopedPointer<CppQuickFixFactory> factory(
+ new AddForwardDeclForUndefinedIdentifierTestFactory(symbol, symbolPos));
+ QuickFixOperationTest::run({testDocuments}, factory.data(), ".", 0);
+ }
+};
+
+QObject *AddIncludeForUndefinedIdentifier::createTest()
+{
+ return new BringIdentifierIntoScopeTest;
+}
+
+#endif // WITH_TESTS
+} // namespace
+
+void registerBringIdentifierIntoScopeQuickfixes()
+{
+ CppQuickFixFactory::registerFactory<AddIncludeForUndefinedIdentifier>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <bringidentifierintoscope.moc>
+#endif
diff --git a/src/plugins/cppeditor/quickfixes/bringidentifierintoscope.h b/src/plugins/cppeditor/quickfixes/bringidentifierintoscope.h
new file mode 100644
index 0000000000..b2f8bbe5d6
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/bringidentifierintoscope.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerBringIdentifierIntoScopeQuickfixes();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/completeswitchstatement.cpp b/src/plugins/cppeditor/quickfixes/completeswitchstatement.cpp
new file mode 100644
index 0000000000..15b8e592ed
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/completeswitchstatement.cpp
@@ -0,0 +1,793 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "completeswitchstatement.h"
+
+#include "../cppeditortr.h"
+#include "../cpprefactoringchanges.h"
+#include "cppquickfix.h"
+
+#include <cplusplus/Overview.h>
+#include <cplusplus/TypeOfExpression.h>
+
+#ifdef WITH_TESTS
+#include "cppquickfix_test.h"
+#include <QtTest>
+#endif
+
+using namespace CPlusPlus;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+class CaseStatementCollector : public ASTVisitor
+{
+public:
+ CaseStatementCollector(Document::Ptr document, const Snapshot &snapshot,
+ Scope *scope)
+ : ASTVisitor(document->translationUnit()),
+ document(document),
+ scope(scope)
+ {
+ typeOfExpression.init(document, snapshot);
+ }
+
+ QStringList operator ()(AST *ast)
+ {
+ values.clear();
+ foundCaseStatementLevel = false;
+ accept(ast);
+ return values;
+ }
+
+ bool preVisit(AST *ast) override {
+ if (CaseStatementAST *cs = ast->asCaseStatement()) {
+ foundCaseStatementLevel = true;
+ if (ExpressionAST *csExpression = cs->expression) {
+ if (ExpressionAST *expression = csExpression->asIdExpression()) {
+ QList<LookupItem> candidates = typeOfExpression(expression, document, scope);
+ if (!candidates.isEmpty() && candidates.first().declaration()) {
+ Symbol *decl = candidates.first().declaration();
+ values << prettyPrint.prettyName(LookupContext::fullyQualifiedName(decl));
+ }
+ }
+ }
+ return true;
+ } else if (foundCaseStatementLevel) {
+ return false;
+ }
+ return true;
+ }
+
+ Overview prettyPrint;
+ bool foundCaseStatementLevel = false;
+ QStringList values;
+ TypeOfExpression typeOfExpression;
+ Document::Ptr document;
+ Scope *scope;
+};
+
+class CompleteSwitchCaseStatementOp: public CppQuickFixOperation
+{
+public:
+ CompleteSwitchCaseStatementOp(const CppQuickFixInterface &interface,
+ int priority, CompoundStatementAST *compoundStatement, const QStringList &values)
+ : CppQuickFixOperation(interface, priority)
+ , compoundStatement(compoundStatement)
+ , values(values)
+ {
+ setDescription(Tr::tr("Complete Switch Statement"));
+ }
+
+ void perform() override
+ {
+ currentFile()->apply(ChangeSet::makeInsert(
+ currentFile()->endOf(compoundStatement->lbrace_token),
+ QLatin1String("\ncase ") + values.join(QLatin1String(":\nbreak;\ncase "))
+ + QLatin1String(":\nbreak;")));
+ }
+
+ CompoundStatementAST *compoundStatement;
+ QStringList values;
+};
+
+static Enum *findEnum(const QList<LookupItem> &results, const LookupContext &ctxt)
+{
+ for (const LookupItem &result : results) {
+ const FullySpecifiedType fst = result.type();
+
+ Type *type = result.declaration() ? result.declaration()->type().type()
+ : fst.type();
+
+ if (!type)
+ continue;
+ if (Enum *e = type->asEnumType())
+ return e;
+ if (const NamedType *namedType = type->asNamedType()) {
+ if (ClassOrNamespace *con = ctxt.lookupType(namedType->name(), result.scope())) {
+ QList<Enum *> enums = con->unscopedEnums();
+ const QList<Symbol *> symbols = con->symbols();
+ for (Symbol * const s : symbols) {
+ if (const auto e = s->asEnum())
+ enums << e;
+ }
+ const Name *referenceName = namedType->name();
+ if (const QualifiedNameId *qualifiedName = referenceName->asQualifiedNameId())
+ referenceName = qualifiedName->name();
+ for (Enum *e : std::as_const(enums)) {
+ if (const Name *candidateName = e->name()) {
+ if (candidateName->match(referenceName))
+ return e;
+ }
+ }
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+Enum *conditionEnum(const CppQuickFixInterface &interface, SwitchStatementAST *statement)
+{
+ Block *block = statement->symbol;
+ Scope *scope = interface.semanticInfo().doc->scopeAt(block->line(), block->column());
+ TypeOfExpression typeOfExpression;
+ typeOfExpression.setExpandTemplates(true);
+ typeOfExpression.init(interface.semanticInfo().doc, interface.snapshot());
+ const QList<LookupItem> results = typeOfExpression(statement->condition,
+ interface.semanticInfo().doc,
+ scope);
+
+ return findEnum(results, typeOfExpression.context());
+}
+
+//! Adds missing case statements for "switch (enumVariable)"
+class CompleteSwitchStatement: public CppQuickFixFactory
+{
+public:
+ CompleteSwitchStatement() { setClangdReplacement({12}); }
+#ifdef WITH_TESTS
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const QList<AST *> &path = interface.path();
+
+ if (path.isEmpty())
+ return;
+
+ // look for switch statement
+ for (int depth = path.size() - 1; depth >= 0; --depth) {
+ AST *ast = path.at(depth);
+ SwitchStatementAST *switchStatement = ast->asSwitchStatement();
+ if (switchStatement) {
+ if (!switchStatement->statement || !switchStatement->symbol)
+ return;
+ CompoundStatementAST *compoundStatement = switchStatement->statement->asCompoundStatement();
+ if (!compoundStatement) // we ignore pathologic case "switch (t) case A: ;"
+ return;
+ // look if the condition's type is an enum
+ if (Enum *e = conditionEnum(interface, switchStatement)) {
+ // check the possible enum values
+ QStringList values;
+ Overview prettyPrint;
+ for (int i = 0; i < e->memberCount(); ++i) {
+ if (Declaration *decl = e->memberAt(i)->asDeclaration())
+ values << prettyPrint.prettyName(LookupContext::fullyQualifiedName(decl));
+ }
+ // Get the used values
+ Block *block = switchStatement->symbol;
+ CaseStatementCollector caseValues(interface.semanticInfo().doc, interface.snapshot(),
+ interface.semanticInfo().doc->scopeAt(block->line(), block->column()));
+ const QStringList usedValues = caseValues(switchStatement);
+ // save the values that would be added
+ for (const QString &usedValue : usedValues)
+ values.removeAll(usedValue);
+ if (!values.isEmpty())
+ result << new CompleteSwitchCaseStatementOp(interface, depth,
+ compoundStatement, values);
+ return;
+ }
+
+ return;
+ }
+ }
+ }
+};
+
+#ifdef WITH_TESTS
+using namespace Tests;
+
+class CompleteSwitchStatementTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data()
+ {
+ QTest::addColumn<QByteArray>("original");
+ QTest::addColumn<QByteArray>("expected");
+
+ using QByteArray = QByteArray;
+
+ // Checks: All enum values are added as case statements for a blank switch.
+ QTest::newRow("basic1")
+ << QByteArray(
+ "enum EnumType { V1, V2 };\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " EnumType t;\n"
+ " @switch (t) {\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "enum EnumType { V1, V2 };\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " EnumType t;\n"
+ " switch (t) {\n"
+ " case V1:\n"
+ " break;\n"
+ " case V2:\n"
+ " break;\n"
+ " }\n"
+ "}\n");
+
+ // Same as above for enum class.
+ QTest::newRow("basic1_enum class")
+ << QByteArray(
+ "enum class EnumType { V1, V2 };\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " EnumType t;\n"
+ " @switch (t) {\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "enum class EnumType { V1, V2 };\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " EnumType t;\n"
+ " switch (t) {\n"
+ " case EnumType::V1:\n"
+ " break;\n"
+ " case EnumType::V2:\n"
+ " break;\n"
+ " }\n"
+ "}\n");
+
+ // Same as above with the cursor somewhere in the body.
+ QTest::newRow("basic1_enum class, cursor in the body")
+ << QByteArray(
+ "enum class EnumType { V1, V2 };\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " EnumType t;\n"
+ " switch (t) {\n"
+ " @}\n"
+ "}\n")
+ << QByteArray(
+ "enum class EnumType { V1, V2 };\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " EnumType t;\n"
+ " switch (t) {\n"
+ " case EnumType::V1:\n"
+ " break;\n"
+ " case EnumType::V2:\n"
+ " break;\n"
+ " }\n"
+ "}\n");
+
+ // Checks: All enum values are added as case statements for a blank switch when
+ // the variable is declared alongside the enum definition.
+ QTest::newRow("basic1_enum_with_declaration")
+ << QByteArray(
+ "enum EnumType { V1, V2 } t;\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " @switch (t) {\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "enum EnumType { V1, V2 } t;\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " switch (t) {\n"
+ " case V1:\n"
+ " break;\n"
+ " case V2:\n"
+ " break;\n"
+ " }\n"
+ "}\n");
+
+ // Same as above for enum class.
+ QTest::newRow("basic1_enum_with_declaration_enumClass")
+ << QByteArray(
+ "enum class EnumType { V1, V2 } t;\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " @switch (t) {\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "enum class EnumType { V1, V2 } t;\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " switch (t) {\n"
+ " case EnumType::V1:\n"
+ " break;\n"
+ " case EnumType::V2:\n"
+ " break;\n"
+ " }\n"
+ "}\n");
+
+ // Checks: All enum values are added as case statements for a blank switch
+ // for anonymous enums.
+ QTest::newRow("basic1_anonymous_enum")
+ << QByteArray(
+ "enum { V1, V2 } t;\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " @switch (t) {\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "enum { V1, V2 } t;\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " switch (t) {\n"
+ " case V1:\n"
+ " break;\n"
+ " case V2:\n"
+ " break;\n"
+ " }\n"
+ "}\n");
+
+ // Checks: All enum values are added as case statements for a blank switch with a default case.
+ QTest::newRow("basic2")
+ << QByteArray(
+ "enum EnumType { V1, V2 };\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " EnumType t;\n"
+ " @switch (t) {\n"
+ " default:\n"
+ " break;\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "enum EnumType { V1, V2 };\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " EnumType t;\n"
+ " switch (t) {\n"
+ " case V1:\n"
+ " break;\n"
+ " case V2:\n"
+ " break;\n"
+ " default:\n"
+ " break;\n"
+ " }\n"
+ "}\n");
+
+ // Same as above for enum class.
+ QTest::newRow("basic2_enumClass")
+ << QByteArray(
+ "enum class EnumType { V1, V2 };\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " EnumType t;\n"
+ " @switch (t) {\n"
+ " default:\n"
+ " break;\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "enum class EnumType { V1, V2 };\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " EnumType t;\n"
+ " switch (t) {\n"
+ " case EnumType::V1:\n"
+ " break;\n"
+ " case EnumType::V2:\n"
+ " break;\n"
+ " default:\n"
+ " break;\n"
+ " }\n"
+ "}\n");
+
+ // Checks: Enum type in class is found.
+ QTest::newRow("enumTypeInClass")
+ << QByteArray(
+ "struct C { enum EnumType { V1, V2 }; };\n"
+ "\n"
+ "void f(C::EnumType t) {\n"
+ " @switch (t) {\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "struct C { enum EnumType { V1, V2 }; };\n"
+ "\n"
+ "void f(C::EnumType t) {\n"
+ " switch (t) {\n"
+ " case C::V1:\n"
+ " break;\n"
+ " case C::V2:\n"
+ " break;\n"
+ " }\n"
+ "}\n");
+
+ // Same as above for enum class.
+ QTest::newRow("enumClassInClass")
+ << QByteArray(
+ "struct C { enum class EnumType { V1, V2 }; };\n"
+ "\n"
+ "void f(C::EnumType t) {\n"
+ " @switch (t) {\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "struct C { enum class EnumType { V1, V2 }; };\n"
+ "\n"
+ "void f(C::EnumType t) {\n"
+ " switch (t) {\n"
+ " case C::EnumType::V1:\n"
+ " break;\n"
+ " case C::EnumType::V2:\n"
+ " break;\n"
+ " }\n"
+ "}\n");
+
+ // Checks: Enum type in namespace is found.
+ QTest::newRow("enumTypeInNamespace")
+ << QByteArray(
+ "namespace N { enum EnumType { V1, V2 }; };\n"
+ "\n"
+ "void f(N::EnumType t) {\n"
+ " @switch (t) {\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "namespace N { enum EnumType { V1, V2 }; };\n"
+ "\n"
+ "void f(N::EnumType t) {\n"
+ " switch (t) {\n"
+ " case N::V1:\n"
+ " break;\n"
+ " case N::V2:\n"
+ " break;\n"
+ " }\n"
+ "}\n");
+
+ // Same as above for enum class.
+ QTest::newRow("enumClassInNamespace")
+ << QByteArray(
+ "namespace N { enum class EnumType { V1, V2 }; };\n"
+ "\n"
+ "void f(N::EnumType t) {\n"
+ " @switch (t) {\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "namespace N { enum class EnumType { V1, V2 }; };\n"
+ "\n"
+ "void f(N::EnumType t) {\n"
+ " switch (t) {\n"
+ " case N::EnumType::V1:\n"
+ " break;\n"
+ " case N::EnumType::V2:\n"
+ " break;\n"
+ " }\n"
+ "}\n");
+
+ // Checks: The missing enum value is added.
+ QTest::newRow("oneValueMissing")
+ << QByteArray(
+ "enum EnumType { V1, V2 };\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " EnumType t;\n"
+ " @switch (t) {\n"
+ " case V2:\n"
+ " break;\n"
+ " default:\n"
+ " break;\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "enum EnumType { V1, V2 };\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " EnumType t;\n"
+ " switch (t) {\n"
+ " case V1:\n"
+ " break;\n"
+ " case V2:\n"
+ " break;\n"
+ " default:\n"
+ " break;\n"
+ " }\n"
+ "}\n");
+
+ // Checks: Same as above for enum class.
+ QTest::newRow("oneValueMissing_enumClass")
+ << QByteArray(
+ "enum class EnumType { V1, V2 };\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " EnumType t;\n"
+ " @switch (t) {\n"
+ " case EnumType::V2:\n"
+ " break;\n"
+ " default:\n"
+ " break;\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "enum class EnumType { V1, V2 };\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ " EnumType t;\n"
+ " switch (t) {\n"
+ " case EnumType::V1:\n"
+ " break;\n"
+ " case EnumType::V2:\n"
+ " break;\n"
+ " default:\n"
+ " break;\n"
+ " }\n"
+ "}\n");
+
+ // Checks: Find the correct enum type despite there being a declaration with the same name.
+ QTest::newRow("QTCREATORBUG10366_1")
+ << QByteArray(
+ "enum test { TEST_1, TEST_2 };\n"
+ "\n"
+ "void f() {\n"
+ " enum test test;\n"
+ " @switch (test) {\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "enum test { TEST_1, TEST_2 };\n"
+ "\n"
+ "void f() {\n"
+ " enum test test;\n"
+ " switch (test) {\n"
+ " case TEST_1:\n"
+ " break;\n"
+ " case TEST_2:\n"
+ " break;\n"
+ " }\n"
+ "}\n");
+
+ // Same as above for enum class.
+ QTest::newRow("QTCREATORBUG10366_1_enumClass")
+ << QByteArray(
+ "enum class test { TEST_1, TEST_2 };\n"
+ "\n"
+ "void f() {\n"
+ " enum test test;\n"
+ " @switch (test) {\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "enum class test { TEST_1, TEST_2 };\n"
+ "\n"
+ "void f() {\n"
+ " enum test test;\n"
+ " switch (test) {\n"
+ " case test::TEST_1:\n"
+ " break;\n"
+ " case test::TEST_2:\n"
+ " break;\n"
+ " }\n"
+ "}\n");
+
+ // Checks: Find the correct enum type despite there being a declaration with the same name.
+ QTest::newRow("QTCREATORBUG10366_2")
+ << QByteArray(
+ "enum test1 { Wrong11, Wrong12 };\n"
+ "enum test { Right1, Right2 };\n"
+ "enum test2 { Wrong21, Wrong22 };\n"
+ "\n"
+ "int main() {\n"
+ " enum test test;\n"
+ " @switch (test) {\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "enum test1 { Wrong11, Wrong12 };\n"
+ "enum test { Right1, Right2 };\n"
+ "enum test2 { Wrong21, Wrong22 };\n"
+ "\n"
+ "int main() {\n"
+ " enum test test;\n"
+ " switch (test) {\n"
+ " case Right1:\n"
+ " break;\n"
+ " case Right2:\n"
+ " break;\n"
+ " }\n"
+ "}\n");
+
+ // Same as above for enum class.
+ QTest::newRow("QTCREATORBUG10366_2_enumClass")
+ << QByteArray(
+ "enum class test1 { Wrong11, Wrong12 };\n"
+ "enum class test { Right1, Right2 };\n"
+ "enum class test2 { Wrong21, Wrong22 };\n"
+ "\n"
+ "int main() {\n"
+ " enum test test;\n"
+ " @switch (test) {\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "enum class test1 { Wrong11, Wrong12 };\n"
+ "enum class test { Right1, Right2 };\n"
+ "enum class test2 { Wrong21, Wrong22 };\n"
+ "\n"
+ "int main() {\n"
+ " enum test test;\n"
+ " switch (test) {\n"
+ " case test::Right1:\n"
+ " break;\n"
+ " case test::Right2:\n"
+ " break;\n"
+ " }\n"
+ "}\n");
+
+ // Checks: Do not crash on incomplete case statetement.
+ QTest::newRow("doNotCrashOnIncompleteCase")
+ << QByteArray(
+ "enum E {};\n"
+ "void f(E o)\n"
+ "{\n"
+ " @switch (o)\n"
+ " {\n"
+ " case\n"
+ " }\n"
+ "}\n")
+ << QByteArray();
+
+ // Same as above for enum class.
+ QTest::newRow("doNotCrashOnIncompleteCase_enumClass")
+ << QByteArray(
+ "enum class E {};\n"
+ "void f(E o)\n"
+ "{\n"
+ " @switch (o)\n"
+ " {\n"
+ " case\n"
+ " }\n"
+ "}\n")
+ << QByteArray();
+
+ // Checks: complete switch statement where enum is goes via a template type parameter
+ QTest::newRow("QTCREATORBUG-24752")
+ << QByteArray(
+ "enum E {EA, EB};\n"
+ "template<typename T> struct S {\n"
+ " static T theType() { return T(); }\n"
+ "};\n"
+ "int main() {\n"
+ " @switch (S<E>::theType()) {\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "enum E {EA, EB};\n"
+ "template<typename T> struct S {\n"
+ " static T theType() { return T(); }\n"
+ "};\n"
+ "int main() {\n"
+ " switch (S<E>::theType()) {\n"
+ " case EA:\n"
+ " break;\n"
+ " case EB:\n"
+ " break;\n"
+ " }\n"
+ "}\n");
+
+ // Same as above for enum class.
+ QTest::newRow("QTCREATORBUG-24752_enumClass")
+ << QByteArray(
+ "enum class E {A, B};\n"
+ "template<typename T> struct S {\n"
+ " static T theType() { return T(); }\n"
+ "};\n"
+ "int main() {\n"
+ " @switch (S<E>::theType()) {\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "enum class E {A, B};\n"
+ "template<typename T> struct S {\n"
+ " static T theType() { return T(); }\n"
+ "};\n"
+ "int main() {\n"
+ " switch (S<E>::theType()) {\n"
+ " case E::A:\n"
+ " break;\n"
+ " case E::B:\n"
+ " break;\n"
+ " }\n"
+ "}\n");
+
+ // Checks: Complete switch statement where enum is return type of a template function
+ // which is outside the scope of the return value.
+ // TODO: Type minimization.
+ QTest::newRow("QTCREATORBUG-25998")
+ << QByteArray(
+ "template <typename T> T enumCast(int value) { return static_cast<T>(value); }\n"
+ "class Test {\n"
+ " enum class E { V1, V2 };"
+ " void func(int i) {\n"
+ " @switch (enumCast<E>(i)) {\n"
+ " }\n"
+ " }\n"
+ "};\n")
+ << QByteArray(
+ "template <typename T> T enumCast(int value) { return static_cast<T>(value); }\n"
+ "class Test {\n"
+ " enum class E { V1, V2 };"
+ " void func(int i) {\n"
+ " switch (enumCast<E>(i)) {\n"
+ " case Test::E::V1:\n"
+ " break;\n"
+ " case Test::E::V2:\n"
+ " break;\n"
+ " }\n"
+ " }\n"
+ "};\n");
+ }
+
+ void test()
+ {
+ QFETCH(QByteArray, original);
+ QFETCH(QByteArray, expected);
+ CompleteSwitchStatement factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+};
+
+QObject *CompleteSwitchStatement::createTest() { return new CompleteSwitchStatementTest; }
+
+#endif // WITH_TESTS
+} // namespace
+
+void registerCompleteSwitchStatementQuickfix()
+{
+ CppQuickFixFactory::registerFactory<CompleteSwitchStatement>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <completeswitchstatement.moc>
+#endif
diff --git a/src/plugins/cppeditor/quickfixes/completeswitchstatement.h b/src/plugins/cppeditor/quickfixes/completeswitchstatement.h
new file mode 100644
index 0000000000..dc2293b1b2
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/completeswitchstatement.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerCompleteSwitchStatementQuickfix();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/convertfromandtopointer.cpp b/src/plugins/cppeditor/quickfixes/convertfromandtopointer.cpp
new file mode 100644
index 0000000000..5eea005200
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/convertfromandtopointer.cpp
@@ -0,0 +1,704 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "convertfromandtopointer.h"
+
+#include "../cppeditortr.h"
+#include "../cpprefactoringchanges.h"
+#include "cppquickfix.h"
+
+#include <cplusplus/ASTPath.h>
+#include <cplusplus/Overview.h>
+#include <cplusplus/TypeOfExpression.h>
+
+#ifdef WITH_TESTS
+#include "cppquickfix_test.h"
+#include <QtTest>
+#endif
+
+using namespace CPlusPlus;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+class ConvertFromAndToPointerOp : public CppQuickFixOperation
+{
+public:
+ enum Mode { FromPointer, FromVariable, FromReference };
+
+ ConvertFromAndToPointerOp(const CppQuickFixInterface &interface, int priority, Mode mode,
+ bool isAutoDeclaration,
+ const SimpleDeclarationAST *simpleDeclaration,
+ const DeclaratorAST *declaratorAST,
+ const SimpleNameAST *identifierAST,
+ Symbol *symbol)
+ : CppQuickFixOperation(interface, priority)
+ , m_mode(mode)
+ , m_isAutoDeclaration(isAutoDeclaration)
+ , m_simpleDeclaration(simpleDeclaration)
+ , m_declaratorAST(declaratorAST)
+ , m_identifierAST(identifierAST)
+ , m_symbol(symbol)
+ , m_refactoring(snapshot())
+ , m_file(currentFile())
+ , m_document(interface.semanticInfo().doc)
+ {
+ setDescription(
+ mode == FromPointer
+ ? Tr::tr("Convert to Stack Variable")
+ : Tr::tr("Convert to Pointer"));
+ }
+
+ void perform() override
+ {
+ ChangeSet changes;
+
+ switch (m_mode) {
+ case FromPointer:
+ removePointerOperator(changes);
+ convertToStackVariable(changes);
+ break;
+ case FromReference:
+ removeReferenceOperator(changes);
+ Q_FALLTHROUGH();
+ case FromVariable:
+ convertToPointer(changes);
+ break;
+ }
+
+ m_file->apply(changes);
+ }
+
+private:
+ void removePointerOperator(ChangeSet &changes) const
+ {
+ if (!m_declaratorAST->ptr_operator_list)
+ return;
+ PointerAST *ptrAST = m_declaratorAST->ptr_operator_list->value->asPointer();
+ QTC_ASSERT(ptrAST, return);
+ const int pos = m_file->startOf(ptrAST->star_token);
+ changes.remove(pos, pos + 1);
+ }
+
+ void removeReferenceOperator(ChangeSet &changes) const
+ {
+ ReferenceAST *refAST = m_declaratorAST->ptr_operator_list->value->asReference();
+ QTC_ASSERT(refAST, return);
+ const int pos = m_file->startOf(refAST->reference_token);
+ changes.remove(pos, pos + 1);
+ }
+
+ void removeNewExpression(ChangeSet &changes, NewExpressionAST *newExprAST) const
+ {
+ ExpressionListAST *exprlist = nullptr;
+ if (newExprAST->new_initializer) {
+ if (ExpressionListParenAST *ast = newExprAST->new_initializer->asExpressionListParen())
+ exprlist = ast->expression_list;
+ else if (BracedInitializerAST *ast = newExprAST->new_initializer->asBracedInitializer())
+ exprlist = ast->expression_list;
+ }
+
+ if (exprlist) {
+ // remove 'new' keyword and type before initializer
+ changes.remove(m_file->startOf(newExprAST->new_token),
+ m_file->startOf(newExprAST->new_initializer));
+
+ changes.remove(m_file->endOf(m_declaratorAST->equal_token - 1),
+ m_file->startOf(m_declaratorAST->equal_token + 1));
+ } else {
+ // remove the whole new expression
+ changes.remove(m_file->endOf(m_identifierAST->firstToken()),
+ m_file->startOf(newExprAST->lastToken()));
+ }
+ }
+
+ void removeNewKeyword(ChangeSet &changes, NewExpressionAST *newExprAST) const
+ {
+ // remove 'new' keyword before initializer
+ changes.remove(m_file->startOf(newExprAST->new_token),
+ m_file->startOf(newExprAST->new_type_id));
+ }
+
+ void convertToStackVariable(ChangeSet &changes) const
+ {
+ // Handle the initializer.
+ if (m_declaratorAST->initializer) {
+ if (NewExpressionAST *newExpression = m_declaratorAST->initializer->asNewExpression()) {
+ if (m_isAutoDeclaration) {
+ if (!newExpression->new_initializer)
+ changes.insert(m_file->endOf(newExpression), QStringLiteral("()"));
+ removeNewKeyword(changes, newExpression);
+ } else {
+ removeNewExpression(changes, newExpression);
+ }
+ }
+ }
+
+ // Fix all occurrences of the identifier in this function.
+ ASTPath astPath(m_document);
+ const QList<SemanticInfo::Use> uses = semanticInfo().localUses.value(m_symbol);
+ for (const SemanticInfo::Use &use : uses) {
+ const QList<AST *> path = astPath(use.line, use.column);
+ AST *idAST = path.last();
+ bool declarationFound = false;
+ bool starFound = false;
+ int ampersandPos = 0;
+ bool memberAccess = false;
+ bool deleteCall = false;
+
+ for (int i = path.count() - 2; i >= 0; --i) {
+ if (path.at(i) == m_declaratorAST) {
+ declarationFound = true;
+ break;
+ }
+ if (MemberAccessAST *memberAccessAST = path.at(i)->asMemberAccess()) {
+ if (m_file->tokenAt(memberAccessAST->access_token).kind() != T_ARROW)
+ continue;
+ int pos = m_file->startOf(memberAccessAST->access_token);
+ changes.replace(pos, pos + 2, QLatin1String("."));
+ memberAccess = true;
+ break;
+ } else if (DeleteExpressionAST *deleteAST = path.at(i)->asDeleteExpression()) {
+ const int pos = m_file->startOf(deleteAST->delete_token);
+ changes.insert(pos, QLatin1String("// "));
+ deleteCall = true;
+ break;
+ } else if (UnaryExpressionAST *unaryExprAST = path.at(i)->asUnaryExpression()) {
+ const Token tk = m_file->tokenAt(unaryExprAST->unary_op_token);
+ if (tk.kind() == T_STAR) {
+ if (!starFound) {
+ int pos = m_file->startOf(unaryExprAST->unary_op_token);
+ changes.remove(pos, pos + 1);
+ }
+ starFound = true;
+ } else if (tk.kind() == T_AMPER) {
+ ampersandPos = m_file->startOf(unaryExprAST->unary_op_token);
+ }
+ } else if (PointerAST *ptrAST = path.at(i)->asPointer()) {
+ if (!starFound) {
+ const int pos = m_file->startOf(ptrAST->star_token);
+ changes.remove(pos, pos);
+ }
+ starFound = true;
+ } else if (path.at(i)->asFunctionDefinition()) {
+ break;
+ }
+ }
+ if (!declarationFound && !starFound && !memberAccess && !deleteCall) {
+ if (ampersandPos) {
+ changes.insert(ampersandPos, QLatin1String("&("));
+ changes.insert(m_file->endOf(idAST->firstToken()), QLatin1String(")"));
+ } else {
+ changes.insert(m_file->startOf(idAST), QLatin1String("&"));
+ }
+ }
+ }
+ }
+
+ QString typeNameOfDeclaration() const
+ {
+ if (!m_simpleDeclaration
+ || !m_simpleDeclaration->decl_specifier_list
+ || !m_simpleDeclaration->decl_specifier_list->value) {
+ return QString();
+ }
+ NamedTypeSpecifierAST *namedType
+ = m_simpleDeclaration->decl_specifier_list->value->asNamedTypeSpecifier();
+ if (!namedType)
+ return QString();
+
+ Overview overview;
+ return overview.prettyName(namedType->name->name);
+ }
+
+ void insertNewExpression(ChangeSet &changes, ExpressionAST *ast) const
+ {
+ const QString typeName = typeNameOfDeclaration();
+ if (CallAST *callAST = ast->asCall()) {
+ if (typeName.isEmpty()) {
+ changes.insert(m_file->startOf(callAST), QLatin1String("new "));
+ } else {
+ changes.insert(m_file->startOf(callAST),
+ QLatin1String("new ") + typeName + QLatin1Char('('));
+ changes.insert(m_file->startOf(callAST->lastToken()), QLatin1String(")"));
+ }
+ } else {
+ if (typeName.isEmpty())
+ return;
+ changes.insert(m_file->startOf(ast), QLatin1String(" = new ") + typeName);
+ }
+ }
+
+ void insertNewExpression(ChangeSet &changes) const
+ {
+ const QString typeName = typeNameOfDeclaration();
+ if (typeName.isEmpty())
+ return;
+ changes.insert(m_file->endOf(m_identifierAST->firstToken()),
+ QLatin1String(" = new ") + typeName);
+ }
+
+ void convertToPointer(ChangeSet &changes) const
+ {
+ // Handle initializer.
+ if (m_declaratorAST->initializer) {
+ if (IdExpressionAST *idExprAST = m_declaratorAST->initializer->asIdExpression()) {
+ changes.insert(m_file->startOf(idExprAST), QLatin1String("&"));
+ } else if (CallAST *callAST = m_declaratorAST->initializer->asCall()) {
+ insertNewExpression(changes, callAST);
+ } else if (ExpressionListParenAST *exprListAST = m_declaratorAST->initializer
+ ->asExpressionListParen()) {
+ insertNewExpression(changes, exprListAST);
+ } else if (BracedInitializerAST *bracedInitializerAST = m_declaratorAST->initializer
+ ->asBracedInitializer()) {
+ insertNewExpression(changes, bracedInitializerAST);
+ }
+ } else {
+ insertNewExpression(changes);
+ }
+
+ // Fix all occurrences of the identifier in this function.
+ ASTPath astPath(m_document);
+ const QList<SemanticInfo::Use> uses = semanticInfo().localUses.value(m_symbol);
+ for (const SemanticInfo::Use &use : uses) {
+ const QList<AST *> path = astPath(use.line, use.column);
+ AST *idAST = path.last();
+ bool insertStar = true;
+ for (int i = path.count() - 2; i >= 0; --i) {
+ if (m_isAutoDeclaration && path.at(i) == m_declaratorAST) {
+ insertStar = false;
+ break;
+ }
+ if (MemberAccessAST *memberAccessAST = path.at(i)->asMemberAccess()) {
+ const int pos = m_file->startOf(memberAccessAST->access_token);
+ changes.replace(pos, pos + 1, QLatin1String("->"));
+ insertStar = false;
+ break;
+ } else if (UnaryExpressionAST *unaryExprAST = path.at(i)->asUnaryExpression()) {
+ if (m_file->tokenAt(unaryExprAST->unary_op_token).kind() == T_AMPER) {
+ const int pos = m_file->startOf(unaryExprAST->unary_op_token);
+ changes.remove(pos, pos + 1);
+ insertStar = false;
+ break;
+ }
+ } else if (path.at(i)->asFunctionDefinition()) {
+ break;
+ }
+ }
+ if (insertStar)
+ changes.insert(m_file->startOf(idAST), QLatin1String("*"));
+ }
+ }
+
+ const Mode m_mode;
+ const bool m_isAutoDeclaration;
+ const SimpleDeclarationAST * const m_simpleDeclaration;
+ const DeclaratorAST * const m_declaratorAST;
+ const SimpleNameAST * const m_identifierAST;
+ Symbol * const m_symbol;
+ const CppRefactoringChanges m_refactoring;
+ const CppRefactoringFilePtr m_file;
+ const Document::Ptr m_document;
+};
+
+/*!
+ Converts the selected variable to a pointer if it is a stack variable or reference, or vice versa.
+ Activates on variable declarations.
+ */
+class ConvertFromAndToPointer : public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const QList<AST *> &path = interface.path();
+ if (path.count() < 2)
+ return;
+ SimpleNameAST *identifier = path.last()->asSimpleName();
+ if (!identifier)
+ return;
+ SimpleDeclarationAST *simpleDeclaration = nullptr;
+ DeclaratorAST *declarator = nullptr;
+ bool isFunctionLocal = false;
+ bool isClassLocal = false;
+ ConvertFromAndToPointerOp::Mode mode = ConvertFromAndToPointerOp::FromVariable;
+ for (int i = path.count() - 2; i >= 0; --i) {
+ AST *ast = path.at(i);
+ if (!declarator && (declarator = ast->asDeclarator()))
+ continue;
+ if (!simpleDeclaration && (simpleDeclaration = ast->asSimpleDeclaration()))
+ continue;
+ if (declarator && simpleDeclaration) {
+ if (ast->asClassSpecifier()) {
+ isClassLocal = true;
+ } else if (ast->asFunctionDefinition() && !isClassLocal) {
+ isFunctionLocal = true;
+ break;
+ }
+ }
+ }
+ if (!isFunctionLocal || !simpleDeclaration || !declarator)
+ return;
+
+ Symbol *symbol = nullptr;
+ for (List<Symbol *> *lst = simpleDeclaration->symbols; lst; lst = lst->next) {
+ if (lst->value->name() == identifier->name) {
+ symbol = lst->value;
+ break;
+ }
+ }
+ if (!symbol)
+ return;
+
+ bool isAutoDeclaration = false;
+ if (symbol->storage() == Symbol::Auto) {
+ // For auto variables we must deduce the type from the initializer.
+ if (!declarator->initializer)
+ return;
+
+ isAutoDeclaration = true;
+ TypeOfExpression typeOfExpression;
+ typeOfExpression.init(interface.semanticInfo().doc, interface.snapshot());
+ typeOfExpression.setExpandTemplates(true);
+ CppRefactoringFilePtr file = interface.currentFile();
+ Scope *scope = file->scopeAt(declarator->firstToken());
+ QList<LookupItem> result = typeOfExpression(file->textOf(declarator->initializer).toUtf8(),
+ scope, TypeOfExpression::Preprocess);
+ if (!result.isEmpty() && result.first().type()->asPointerType())
+ mode = ConvertFromAndToPointerOp::FromPointer;
+ } else if (declarator->ptr_operator_list) {
+ for (PtrOperatorListAST *ops = declarator->ptr_operator_list; ops; ops = ops->next) {
+ if (ops != declarator->ptr_operator_list) {
+ // Bail out on more complex pointer types (e.g. pointer of pointer,
+ // or reference of pointer).
+ return;
+ }
+ if (ops->value->asPointer())
+ mode = ConvertFromAndToPointerOp::FromPointer;
+ else if (ops->value->asReference())
+ mode = ConvertFromAndToPointerOp::FromReference;
+ }
+ }
+
+ const int priority = path.size() - 1;
+ result << new ConvertFromAndToPointerOp(interface, priority, mode, isAutoDeclaration,
+ simpleDeclaration, declarator, identifier, symbol);
+ }
+};
+
+#ifdef WITH_TESTS
+using namespace Tests;
+
+class ConvertFromAndToPointerTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data()
+ {
+ QTest::addColumn<QByteArray>("original");
+ QTest::addColumn<QByteArray>("expected");
+
+ QTest::newRow("ConvertFromPointer")
+ << QByteArray("void foo() {\n"
+ " QString *@str;\n"
+ " if (!str->isEmpty())\n"
+ " str->clear();\n"
+ " f1(*str);\n"
+ " f2(str);\n"
+ "}\n")
+ << QByteArray("void foo() {\n"
+ " QString str;\n"
+ " if (!str.isEmpty())\n"
+ " str.clear();\n"
+ " f1(str);\n"
+ " f2(&str);\n"
+ "}\n");
+
+ QTest::newRow("ConvertToPointer")
+ << QByteArray("void foo() {\n"
+ " QString @str;\n"
+ " if (!str.isEmpty())\n"
+ " str.clear();\n"
+ " f1(str);\n"
+ " f2(&str);\n"
+ "}\n")
+ << QByteArray("void foo() {\n"
+ " QString *str = new QString;\n"
+ " if (!str->isEmpty())\n"
+ " str->clear();\n"
+ " f1(*str);\n"
+ " f2(str);\n"
+ "}\n");
+
+ QTest::newRow("ConvertReferenceToPointer")
+ << QByteArray("void foo() {\n"
+ " QString narf;"
+ " QString &@str = narf;\n"
+ " if (!str.isEmpty())\n"
+ " str.clear();\n"
+ " f1(str);\n"
+ " f2(&str);\n"
+ "}\n")
+ << QByteArray("void foo() {\n"
+ " QString narf;"
+ " QString *str = &narf;\n"
+ " if (!str->isEmpty())\n"
+ " str->clear();\n"
+ " f1(*str);\n"
+ " f2(str);\n"
+ "}\n");
+
+ QTest::newRow("ConvertFromPointer_withInitializer")
+ << QByteArray("void foo() {\n"
+ " QString *@str = new QString(QLatin1String(\"schnurz\"));\n"
+ " if (!str->isEmpty())\n"
+ " str->clear();\n"
+ "}\n")
+ << QByteArray("void foo() {\n"
+ " QString str(QLatin1String(\"schnurz\"));\n"
+ " if (!str.isEmpty())\n"
+ " str.clear();\n"
+ "}\n");
+
+ QTest::newRow("ConvertFromPointer_withBareInitializer")
+ << QByteArray("void foo() {\n"
+ " QString *@str = new QString;\n"
+ " if (!str->isEmpty())\n"
+ " str->clear();\n"
+ "}\n")
+ << QByteArray("void foo() {\n"
+ " QString str;\n"
+ " if (!str.isEmpty())\n"
+ " str.clear();\n"
+ "}\n");
+
+ QTest::newRow("ConvertFromPointer_withEmptyInitializer")
+ << QByteArray("void foo() {\n"
+ " QString *@str = new QString();\n"
+ " if (!str->isEmpty())\n"
+ " str->clear();\n"
+ "}\n")
+ << QByteArray("void foo() {\n"
+ " QString str;\n"
+ " if (!str.isEmpty())\n"
+ " str.clear();\n"
+ "}\n");
+
+ QTest::newRow("ConvertFromPointer_structWithPointer")
+ << QByteArray("struct Bar{ QString *str; };\n"
+ "void foo() {\n"
+ " Bar *@bar = new Bar;\n"
+ " bar->str = new QString;\n"
+ " delete bar->str;\n"
+ " delete bar;\n"
+ "}\n")
+ << QByteArray("struct Bar{ QString *str; };\n"
+ "void foo() {\n"
+ " Bar bar;\n"
+ " bar.str = new QString;\n"
+ " delete bar.str;\n"
+ " // delete bar;\n"
+ "}\n");
+
+ QTest::newRow("ConvertToPointer_withInitializer")
+ << QByteArray("void foo() {\n"
+ " QString @str = QLatin1String(\"narf\");\n"
+ " if (!str.isEmpty())\n"
+ " str.clear();\n"
+ "}\n")
+ << QByteArray("void foo() {\n"
+ " QString *str = new QString(QLatin1String(\"narf\"));\n"
+ " if (!str->isEmpty())\n"
+ " str->clear();\n"
+ "}\n");
+
+ QTest::newRow("ConvertToPointer_withParenInitializer")
+ << QByteArray("void foo() {\n"
+ " QString @str(QLatin1String(\"narf\"));\n"
+ " if (!str.isEmpty())\n"
+ " str.clear();\n"
+ "}\n")
+ << QByteArray("void foo() {\n"
+ " QString *str = new QString(QLatin1String(\"narf\"));\n"
+ " if (!str->isEmpty())\n"
+ " str->clear();\n"
+ "}\n");
+
+ QTest::newRow("ConvertToPointer_noTriggerRValueRefs")
+ << QByteArray("void foo(Narf &&@narf) {}\n")
+ << QByteArray();
+
+ QTest::newRow("ConvertToPointer_noTriggerGlobal")
+ << QByteArray("int @global;\n")
+ << QByteArray();
+
+ QTest::newRow("ConvertToPointer_noTriggerClassMember")
+ << QByteArray("struct C { int @member; };\n")
+ << QByteArray();
+
+ QTest::newRow("ConvertToPointer_noTriggerClassMember2")
+ << QByteArray("void f() { struct C { int @member; }; }\n")
+ << QByteArray();
+
+ QTest::newRow("ConvertToPointer_functionOfFunctionLocalClass")
+ << QByteArray("void f() {\n"
+ " struct C {\n"
+ " void g() { int @member; }\n"
+ " };\n"
+ "}\n")
+ << QByteArray("void f() {\n"
+ " struct C {\n"
+ " void g() { int *member; }\n"
+ " };\n"
+ "}\n");
+
+ QTest::newRow("ConvertToPointer_redeclaredVariable_block")
+ << QByteArray("void foo() {\n"
+ " QString @str;\n"
+ " str.clear();\n"
+ " {\n"
+ " QString str;\n"
+ " str.clear();\n"
+ " }\n"
+ " f1(str);\n"
+ "}\n")
+ << QByteArray("void foo() {\n"
+ " QString *str = new QString;\n"
+ " str->clear();\n"
+ " {\n"
+ " QString str;\n"
+ " str.clear();\n"
+ " }\n"
+ " f1(*str);\n"
+ "}\n");
+
+ QTest::newRow("ConvertAutoFromPointer")
+ << QByteArray("void foo() {\n"
+ " auto @str = new QString(QLatin1String(\"foo\"));\n"
+ " if (!str->isEmpty())\n"
+ " str->clear();\n"
+ " f1(*str);\n"
+ " f2(str);\n"
+ "}\n")
+ << QByteArray("void foo() {\n"
+ " auto str = QString(QLatin1String(\"foo\"));\n"
+ " if (!str.isEmpty())\n"
+ " str.clear();\n"
+ " f1(str);\n"
+ " f2(&str);\n"
+ "}\n");
+
+ QTest::newRow("ConvertAutoFromPointer2")
+ << QByteArray("void foo() {\n"
+ " auto *@str = new QString;\n"
+ " if (!str->isEmpty())\n"
+ " str->clear();\n"
+ " f1(*str);\n"
+ " f2(str);\n"
+ "}\n")
+ << QByteArray("void foo() {\n"
+ " auto str = QString();\n"
+ " if (!str.isEmpty())\n"
+ " str.clear();\n"
+ " f1(str);\n"
+ " f2(&str);\n"
+ "}\n");
+
+ QTest::newRow("ConvertAutoToPointer")
+ << QByteArray("void foo() {\n"
+ " auto @str = QString(QLatin1String(\"foo\"));\n"
+ " if (!str.isEmpty())\n"
+ " str.clear();\n"
+ " f1(str);\n"
+ " f2(&str);\n"
+ "}\n")
+ << QByteArray("void foo() {\n"
+ " auto @str = new QString(QLatin1String(\"foo\"));\n"
+ " if (!str->isEmpty())\n"
+ " str->clear();\n"
+ " f1(*str);\n"
+ " f2(str);\n"
+ "}\n");
+
+ QTest::newRow("ConvertToPointerWithMacro")
+ << QByteArray("#define BAR bar\n"
+ "void func()\n"
+ "{\n"
+ " int @foo = 42;\n"
+ " int bar;\n"
+ " BAR = foo;\n"
+ "}\n")
+ << QByteArray("#define BAR bar\n"
+ "void func()\n"
+ "{\n"
+ " int *foo = 42;\n"
+ " int bar;\n"
+ " BAR = *foo;\n"
+ "}\n");
+
+ QString testObjAndFunc = "struct Object\n"
+ "{\n"
+ " Object(%1){}\n"
+ "};\n"
+ "void func()\n"
+ "{\n"
+ " %2\n"
+ "}\n";
+
+ QTest::newRow("ConvertToStack1_QTCREATORBUG23181")
+ << QByteArray(testObjAndFunc.arg("int").arg("Object *@obj = new Object(0);").toUtf8())
+ << QByteArray(testObjAndFunc.arg("int").arg("Object obj(0);").toUtf8());
+
+ QTest::newRow("ConvertToStack2_QTCREATORBUG23181")
+ << QByteArray(testObjAndFunc.arg("int").arg("Object *@obj = new Object{0};").toUtf8())
+ << QByteArray(testObjAndFunc.arg("int").arg("Object obj{0};").toUtf8());
+
+ QTest::newRow("ConvertToPointer1_QTCREATORBUG23181")
+ << QByteArray(testObjAndFunc.arg("").arg("Object @obj;").toUtf8())
+ << QByteArray(testObjAndFunc.arg("").arg("Object *obj = new Object;").toUtf8());
+
+ QTest::newRow("ConvertToPointer2_QTCREATORBUG23181")
+ << QByteArray(testObjAndFunc.arg("").arg("Object @obj();").toUtf8())
+ << QByteArray(testObjAndFunc.arg("").arg("Object *obj = new Object();").toUtf8());
+
+ QTest::newRow("ConvertToPointer3_QTCREATORBUG23181")
+ << QByteArray(testObjAndFunc.arg("").arg("Object @obj{};").toUtf8())
+ << QByteArray(testObjAndFunc.arg("").arg("Object *obj = new Object{};").toUtf8());
+
+ QTest::newRow("ConvertToPointer4_QTCREATORBUG23181")
+ << QByteArray(testObjAndFunc.arg("int").arg("Object @obj(0);").toUtf8())
+ << QByteArray(testObjAndFunc.arg("int").arg("Object *obj = new Object(0);").toUtf8());
+
+
+ }
+
+ void test()
+ {
+ QFETCH(QByteArray, original);
+ QFETCH(QByteArray, expected);
+ ConvertFromAndToPointer factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+};
+
+QObject *ConvertFromAndToPointer::createTest() { return new ConvertFromAndToPointerTest; }
+
+#endif // WITH_TESTS
+} // namespace
+
+void registerConvertFromAndToPointerQuickfix()
+{
+ CppQuickFixFactory::registerFactory<ConvertFromAndToPointer>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <convertfromandtopointer.moc>
+#endif
diff --git a/src/plugins/cppeditor/quickfixes/convertfromandtopointer.h b/src/plugins/cppeditor/quickfixes/convertfromandtopointer.h
new file mode 100644
index 0000000000..0deb6c0da9
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/convertfromandtopointer.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerConvertFromAndToPointerQuickfix();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/convertnumericliteral.cpp b/src/plugins/cppeditor/quickfixes/convertnumericliteral.cpp
new file mode 100644
index 0000000000..6fb5becf34
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/convertnumericliteral.cpp
@@ -0,0 +1,199 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "convertnumericliteral.h"
+
+#include "../cppeditortr.h"
+#include "../cpprefactoringchanges.h"
+#include "cppquickfix.h"
+
+#include <bitset>
+
+using namespace CPlusPlus;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+class ConvertNumericLiteralOp: public CppQuickFixOperation
+{
+public:
+ ConvertNumericLiteralOp(const CppQuickFixInterface &interface, int start, int end,
+ const QString &replacement)
+ : CppQuickFixOperation(interface)
+ , start(start)
+ , end(end)
+ , replacement(replacement)
+ {}
+
+ void perform() override
+ {
+ currentFile()->apply(ChangeSet::makeReplace(start, end, replacement));
+ }
+
+private:
+ int start, end;
+ QString replacement;
+};
+
+/*!
+ Base class for converting numeric literals between decimal, octal and hex.
+ Does the base check for the specific ones and parses the number.
+
+ Test cases:
+ 0xFA0Bu;
+ 0X856A;
+ 298.3;
+ 199;
+ 074;
+ 199L;
+ 074L;
+ -199;
+ -017;
+ 0783; // invalid octal
+ 0; // border case, allow only hex<->decimal
+
+ Activates on: numeric literals
+*/
+class ConvertNumericLiteral : public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest() { return new QObject; }
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const QList<AST *> &path = interface.path();
+ CppRefactoringFilePtr file = interface.currentFile();
+
+ if (path.isEmpty())
+ return;
+
+ NumericLiteralAST *literal = path.last()->asNumericLiteral();
+
+ if (!literal)
+ return;
+
+ Token token = file->tokenAt(literal->asNumericLiteral()->literal_token);
+ if (!token.is(T_NUMERIC_LITERAL))
+ return;
+ const NumericLiteral *numeric = token.number;
+ if (numeric->isDouble() || numeric->isFloat())
+ return;
+
+ // remove trailing L or U and stuff
+ const char * const spell = numeric->chars();
+ int numberLength = numeric->size();
+ while (numberLength > 0 && !std::isxdigit(spell[numberLength - 1]))
+ --numberLength;
+ if (numberLength < 1)
+ return;
+
+ // convert to number
+ bool valid;
+ ulong value = 0;
+ const QString x = QString::fromUtf8(spell).left(numberLength);
+ if (x.startsWith("0b", Qt::CaseInsensitive))
+ value = x.mid(2).toULong(&valid, 2);
+ else
+ value = x.toULong(&valid, 0);
+
+ if (!valid)
+ return;
+
+ const int priority = path.size() - 1; // very high priority
+ const int start = file->startOf(literal);
+ const char * const str = numeric->chars();
+
+ const bool isBinary = numberLength > 2 && str[0] == '0' && (str[1] == 'b' || str[1] == 'B');
+ const bool isOctal = numberLength >= 2 && str[0] == '0' && str[1] >= '0' && str[1] <= '7';
+ const bool isDecimal = !(isBinary || isOctal || numeric->isHex());
+
+ if (!numeric->isHex()) {
+ /*
+ Convert integer literal to hex representation.
+ Replace
+ 0b100000
+ 32
+ 040
+ With
+ 0x20
+
+ */
+ const QString replacement = QString::asprintf("0x%lX", value);
+ auto op = new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement);
+ op->setDescription(Tr::tr("Convert to Hexadecimal"));
+ op->setPriority(priority);
+ result << op;
+ }
+
+ if (!isOctal) {
+ /*
+ Convert integer literal to octal representation.
+ Replace
+ 0b100000
+ 32
+ 0x20
+ With
+ 040
+ */
+ const QString replacement = QString::asprintf("0%lo", value);
+ auto op = new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement);
+ op->setDescription(Tr::tr("Convert to Octal"));
+ op->setPriority(priority);
+ result << op;
+ }
+
+ if (!isDecimal) {
+ /*
+ Convert integer literal to decimal representation.
+ Replace
+ 0b100000
+ 0x20
+ 040
+ With
+ 32
+ */
+ const QString replacement = QString::asprintf("%lu", value);
+ auto op = new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement);
+ op->setDescription(Tr::tr("Convert to Decimal"));
+ op->setPriority(priority);
+ result << op;
+ }
+
+ if (!isBinary) {
+ /*
+ Convert integer literal to binary representation.
+ Replace
+ 32
+ 0x20
+ 040
+ With
+ 0b100000
+ */
+ QString replacement = "0b";
+ if (value == 0) {
+ replacement.append('0');
+ } else {
+ std::bitset<std::numeric_limits<decltype (value)>::digits> b(value);
+ QRegularExpression re("^[0]*");
+ replacement.append(QString::fromStdString(b.to_string()).remove(re));
+ }
+ auto op = new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement);
+ op->setDescription(Tr::tr("Convert to Binary"));
+ op->setPriority(priority);
+ result << op;
+ }
+ }
+};
+
+} // namespace
+
+void registerConvertNumericLiteralQuickfix()
+{
+ CppQuickFixFactory::registerFactory<ConvertNumericLiteral>();
+}
+
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/convertnumericliteral.h b/src/plugins/cppeditor/quickfixes/convertnumericliteral.h
new file mode 100644
index 0000000000..4158807c68
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/convertnumericliteral.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerConvertNumericLiteralQuickfix();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/convertqt4connect.cpp b/src/plugins/cppeditor/quickfixes/convertqt4connect.cpp
new file mode 100644
index 0000000000..b9f08e9021
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/convertqt4connect.cpp
@@ -0,0 +1,505 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "convertqt4connect.h"
+
+#include "../cppcodestylesettings.h"
+#include "../cppeditortr.h"
+#include "../cpprefactoringchanges.h"
+#include "cppquickfix.h"
+
+#include <cplusplus/Overview.h>
+#include <cplusplus/TypeOfExpression.h>
+
+#ifdef WITH_TESTS
+#include "cppquickfix_test.h"
+#include <QtTest>
+#endif
+
+using namespace CPlusPlus;
+using namespace TextEditor;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+class ConvertQt4ConnectOperation: public CppQuickFixOperation
+{
+public:
+ ConvertQt4ConnectOperation(const CppQuickFixInterface &interface, const ChangeSet &changes)
+ : CppQuickFixOperation(interface, 1), m_changes(changes)
+ {
+ setDescription(Tr::tr("Convert connect() to Qt 5 Style"));
+ }
+
+private:
+ void perform() override
+ {
+ currentFile()->apply(m_changes);
+ }
+
+ const ChangeSet m_changes;
+};
+
+static Symbol *skipForwardDeclarations(const QList<Symbol *> &symbols)
+{
+ for (Symbol *symbol : symbols) {
+ if (!symbol->type()->asForwardClassDeclarationType())
+ return symbol;
+ }
+
+ return nullptr;
+}
+
+static bool findRawAccessFunction(Class *klass, PointerType *pointerType, QString *objAccessFunction)
+{
+ QList<Function *> candidates;
+ for (auto it = klass->memberBegin(), end = klass->memberEnd(); it != end; ++it) {
+ if (Function *func = (*it)->asFunction()) {
+ const Name *funcName = func->name();
+ if (!funcName->asOperatorNameId()
+ && !funcName->asConversionNameId()
+ && func->returnType().type() == pointerType
+ && func->isConst()
+ && func->argumentCount() == 0) {
+ candidates << func;
+ }
+ }
+ }
+ const Name *funcName = nullptr;
+ switch (candidates.size()) {
+ case 0:
+ return false;
+ case 1:
+ funcName = candidates.first()->name();
+ break;
+ default:
+ // Multiple candidates - prefer a function named data
+ for (Function *func : std::as_const(candidates)) {
+ if (!strcmp(func->name()->identifier()->chars(), "data")) {
+ funcName = func->name();
+ break;
+ }
+ }
+ if (!funcName)
+ funcName = candidates.first()->name();
+ }
+ const Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ *objAccessFunction = QLatin1Char('.') + oo.prettyName(funcName) + QLatin1String("()");
+ return true;
+}
+
+static PointerType *determineConvertedType(
+ NamedType *namedType, const LookupContext &context, Scope *scope, QString *objAccessFunction)
+{
+ if (!namedType)
+ return nullptr;
+ if (ClassOrNamespace *binding = context.lookupType(namedType->name(), scope)) {
+ if (Symbol *objectClassSymbol = skipForwardDeclarations(binding->symbols())) {
+ if (Class *klass = objectClassSymbol->asClass()) {
+ for (auto it = klass->memberBegin(), end = klass->memberEnd(); it != end; ++it) {
+ if (Function *func = (*it)->asFunction()) {
+ if (const ConversionNameId *conversionName =
+ func->name()->asConversionNameId()) {
+ if (PointerType *type = conversionName->type()->asPointerType()) {
+ if (findRawAccessFunction(klass, type, objAccessFunction))
+ return type;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+static Class *senderOrReceiverClass(
+ const CppQuickFixInterface &interface,
+ const CppRefactoringFilePtr &file,
+ const ExpressionAST *objectPointerAST,
+ Scope *objectPointerScope,
+ QString *objAccessFunction)
+{
+ const LookupContext &context = interface.context();
+
+ QByteArray objectPointerExpression;
+ if (objectPointerAST)
+ objectPointerExpression = file->textOf(objectPointerAST).toUtf8();
+ else
+ objectPointerExpression = "this";
+
+ TypeOfExpression toe;
+ toe.setExpandTemplates(true);
+ toe.init(interface.semanticInfo().doc, interface.snapshot(), context.bindings());
+ const QList<LookupItem> objectPointerExpressions = toe(objectPointerExpression,
+ objectPointerScope, TypeOfExpression::Preprocess);
+ QTC_ASSERT(!objectPointerExpressions.isEmpty(), return nullptr);
+
+ Type *objectPointerTypeBase = objectPointerExpressions.first().type().type();
+ QTC_ASSERT(objectPointerTypeBase, return nullptr);
+
+ PointerType *objectPointerType = objectPointerTypeBase->asPointerType();
+ if (!objectPointerType) {
+ objectPointerType = determineConvertedType(objectPointerTypeBase->asNamedType(), context,
+ objectPointerScope, objAccessFunction);
+ }
+ QTC_ASSERT(objectPointerType, return nullptr);
+
+ Type *objectTypeBase = objectPointerType->elementType().type(); // Dereference
+ QTC_ASSERT(objectTypeBase, return nullptr);
+
+ NamedType *objectType = objectTypeBase->asNamedType();
+ QTC_ASSERT(objectType, return nullptr);
+
+ ClassOrNamespace *objectClassCON = context.lookupType(objectType->name(), objectPointerScope);
+ if (!objectClassCON) {
+ objectClassCON = objectPointerExpressions.first().binding();
+ QTC_ASSERT(objectClassCON, return nullptr);
+ }
+ QTC_ASSERT(!objectClassCON->symbols().isEmpty(), return nullptr);
+
+ Symbol *objectClassSymbol = skipForwardDeclarations(objectClassCON->symbols());
+ QTC_ASSERT(objectClassSymbol, return nullptr);
+
+ return objectClassSymbol->asClass();
+}
+
+static bool findConnectReplacement(
+ const CppQuickFixInterface &interface,
+ const ExpressionAST *objectPointerAST,
+ const QtMethodAST *methodAST,
+ const CppRefactoringFilePtr &file,
+ QString *replacement,
+ QString *objAccessFunction)
+{
+ // Get name of method
+ if (!methodAST->declarator || !methodAST->declarator->core_declarator)
+ return false;
+
+ DeclaratorIdAST *methodDeclIdAST = methodAST->declarator->core_declarator->asDeclaratorId();
+ if (!methodDeclIdAST)
+ return false;
+
+ NameAST *methodNameAST = methodDeclIdAST->name;
+ if (!methodNameAST)
+ return false;
+
+ // Lookup object pointer type
+ Scope *scope = file->scopeAt(methodAST->firstToken());
+ Class *objectClass = senderOrReceiverClass(interface, file, objectPointerAST, scope,
+ objAccessFunction);
+ QTC_ASSERT(objectClass, return false);
+
+ // Look up member function in call, including base class members.
+ const LookupContext &context = interface.context();
+ const QList<LookupItem> methodResults = context.lookup(methodNameAST->name, objectClass);
+ if (methodResults.isEmpty())
+ return false; // Maybe mis-spelled signal/slot name
+
+ Scope *baseClassScope = methodResults.at(0).scope(); // FIXME: Handle overloads
+ QTC_ASSERT(baseClassScope, return false);
+
+ Class *classOfMethod = baseClassScope->asClass(); // Declaration point of signal/slot
+ QTC_ASSERT(classOfMethod, return false);
+
+ Symbol *method = methodResults.at(0).declaration();
+ QTC_ASSERT(method, return false);
+
+ // Minimize qualification
+ Control *control = context.bindings()->control().get();
+ ClassOrNamespace *functionCON = context.lookupParent(scope);
+ const Name *shortName = LookupContext::minimalName(method, functionCON, control);
+ if (!shortName->asQualifiedNameId())
+ shortName = control->qualifiedNameId(classOfMethod->name(), shortName);
+
+ const Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ *replacement = QLatin1Char('&') + oo.prettyName(shortName);
+ return true;
+}
+
+static bool onConnectOrDisconnectCall(AST *ast, const ExpressionListAST **arguments)
+{
+ if (!ast)
+ return false;
+
+ CallAST *call = ast->asCall();
+ if (!call)
+ return false;
+
+ if (!call->base_expression)
+ return false;
+
+ const IdExpressionAST *idExpr = call->base_expression->asIdExpression();
+ if (!idExpr || !idExpr->name || !idExpr->name->name)
+ return false;
+
+ const ExpressionListAST *args = call->expression_list;
+ if (!arguments)
+ return false;
+
+ const Identifier *id = idExpr->name->name->identifier();
+ if (!id)
+ return false;
+
+ const QByteArray name(id->chars(), id->size());
+ if (name != "connect" && name != "disconnect")
+ return false;
+
+ if (arguments)
+ *arguments = args;
+ return true;
+}
+
+// Might modify arg* output arguments even if false is returned.
+static bool collectConnectArguments(
+ const ExpressionListAST *arguments,
+ const ExpressionAST **arg1,
+ const QtMethodAST **arg2,
+ const ExpressionAST **arg3,
+ const QtMethodAST **arg4)
+{
+ if (!arguments || !arg1 || !arg2 || !arg3 || !arg4)
+ return false;
+
+ *arg1 = arguments->value;
+ arguments = arguments->next;
+ if (!arg1 || !arguments)
+ return false;
+
+ *arg2 = arguments->value->asQtMethod();
+ arguments = arguments->next;
+ if (!*arg2 || !arguments)
+ return false;
+
+ *arg3 = arguments->value;
+ if (!*arg3)
+ return false;
+
+ // Take care of three-arg version, with 'this' receiver.
+ if (QtMethodAST *receiverMethod = arguments->value->asQtMethod()) {
+ *arg3 = nullptr; // Means 'this'
+ *arg4 = receiverMethod;
+ return true;
+ }
+
+ arguments = arguments->next;
+ if (!arguments)
+ return false;
+
+ *arg4 = arguments->value->asQtMethod();
+ if (!*arg4)
+ return false;
+
+ return true;
+}
+
+//! Converts a Qt 4 QObject::connect() to Qt 5 style.
+class ConvertQt4Connect : public CppQuickFixFactory
+{
+public:
+#ifdef WITH_TESTS
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const QList<AST *> &path = interface.path();
+
+ for (int i = path.size(); --i >= 0; ) {
+ const ExpressionListAST *arguments;
+ if (!onConnectOrDisconnectCall(path.at(i), &arguments))
+ continue;
+
+ const ExpressionAST *arg1, *arg3;
+ const QtMethodAST *arg2, *arg4;
+ if (!collectConnectArguments(arguments, &arg1, &arg2, &arg3, &arg4))
+ continue;
+
+ const CppRefactoringFilePtr file = interface.currentFile();
+
+ QString newSignal;
+ QString senderAccessFunc;
+ if (!findConnectReplacement(interface, arg1, arg2, file, &newSignal, &senderAccessFunc))
+ continue;
+
+ QString newMethod;
+ QString receiverAccessFunc;
+ if (!findConnectReplacement(interface, arg3, arg4, file, &newMethod, &receiverAccessFunc))
+ continue;
+
+ ChangeSet changes;
+ changes.replace(file->endOf(arg1), file->endOf(arg1), senderAccessFunc);
+ changes.replace(file->startOf(arg2), file->endOf(arg2), newSignal);
+ if (!arg3)
+ newMethod.prepend(QLatin1String("this, "));
+ else
+ changes.replace(file->endOf(arg3), file->endOf(arg3), receiverAccessFunc);
+ changes.replace(file->startOf(arg4), file->endOf(arg4), newMethod);
+
+ result << new ConvertQt4ConnectOperation(interface, changes);
+ return;
+ }
+ }
+};
+
+#ifdef WITH_TESTS
+using namespace Tests;
+class ConvertQt4ConnectTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void testOutOfClass()
+ {
+ QByteArray prefix =
+ "class QObject {};\n"
+ "class TestClass : public QObject\n"
+ "{\n"
+ "public:\n"
+ " void setProp(int) {}\n"
+ " void sigFoo(int) {}\n"
+ "};\n"
+ "\n"
+ "int foo()\n"
+ "{\n";
+
+ QByteArray suffix = "\n}\n";
+
+ QByteArray original = prefix
+ + " TestClass obj;\n"
+ " conne@ct(&obj, SIGNAL(sigFoo(int)), &obj, SLOT(setProp(int)));"
+ + suffix;
+
+ QByteArray expected = prefix
+ + " TestClass obj;\n"
+ " connect(&obj, &TestClass::sigFoo, &obj, &TestClass::setProp);"
+ + suffix;
+
+ QList<TestDocumentPtr> testDocuments;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ ConvertQt4Connect factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testWithinClass_data()
+ {
+ QTest::addColumn<QByteArray>("original");
+ QTest::addColumn<QByteArray>("expected");
+
+ QTest::newRow("four-args-connect")
+ << QByteArray("conne@ct(this, SIGNAL(sigFoo(int)), this, SLOT(setProp(int)));")
+ << QByteArray("connect(this, &TestClass::sigFoo, this, &TestClass::setProp);");
+
+ QTest::newRow("four-args-disconnect")
+ << QByteArray("disconne@ct(this, SIGNAL(sigFoo(int)), this, SLOT(setProp(int)));")
+ << QByteArray("disconnect(this, &TestClass::sigFoo, this, &TestClass::setProp);");
+
+ QTest::newRow("three-args-connect")
+ << QByteArray("conne@ct(this, SIGNAL(sigFoo(int)), SLOT(setProp(int)));")
+ << QByteArray("connect(this, &TestClass::sigFoo, this, &TestClass::setProp);");
+
+ QTest::newRow("template-value")
+ << QByteArray("Pointer<TestClass> p;\n"
+ "conne@ct(p.t, SIGNAL(sigFoo(int)), p.t, SLOT(setProp(int)));")
+ << QByteArray("Pointer<TestClass> p;\n"
+ "connect(p.t, &TestClass::sigFoo, p.t, &TestClass::setProp);");
+
+ QTest::newRow("implicit-pointer")
+ << QByteArray("Pointer<TestClass> p;\n"
+ "conne@ct(p, SIGNAL(sigFoo(int)), p, SLOT(setProp(int)));")
+ << QByteArray("Pointer<TestClass> p;\n"
+ "connect(p.data(), &TestClass::sigFoo, p.data(), &TestClass::setProp);");
+ }
+
+ void testWithinClass()
+ {
+ QFETCH(QByteArray, original);
+ QFETCH(QByteArray, expected);
+
+ QByteArray prefix =
+ "template<class T>\n"
+ "struct Pointer\n"
+ "{\n"
+ " T *t;\n"
+ " operator T*() const { return t; }\n"
+ " T *data() const { return t; }\n"
+ "};\n"
+ "class QObject {};\n"
+ "class TestClass : public QObject\n"
+ "{\n"
+ "public:\n"
+ " void setProp(int) {}\n"
+ " void sigFoo(int) {}\n"
+ " void setupSignals();\n"
+ "};\n"
+ "\n"
+ "int TestClass::setupSignals()\n"
+ "{\n";
+
+ QByteArray suffix = "\n}\n";
+
+ QList<TestDocumentPtr> testDocuments;
+ testDocuments << CppTestDocument::create("file.cpp",
+ prefix + original + suffix,
+ prefix + expected + suffix);
+
+ ConvertQt4Connect factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testDifferentNamespace()
+ {
+ const QByteArray prefix =
+ "namespace NsA {\n"
+ "class ClassA : public QObject\n"
+ "{\n"
+ " static ClassA *instance();\n"
+ "signals:\n"
+ " void sig();\n"
+ "};\n"
+ "}\n"
+ "\n"
+ "namespace NsB {\n"
+ "class ClassB : public QObject\n"
+ "{\n"
+ " void slot();\n"
+ " void connector() {\n";
+
+ const QByteArray suffix = " }\n};\n}";
+
+ const QByteArray original = "co@nnect(NsA::ClassA::instance(), SIGNAL(sig()),\n"
+ " this, SLOT(slot()));\n";
+ const QByteArray expected = "connect(NsA::ClassA::instance(), &NsA::ClassA::sig,\n"
+ " this, &ClassB::slot);\n";
+ QList<TestDocumentPtr> testDocuments;
+ testDocuments << CppTestDocument::create("file.cpp",
+ prefix + original + suffix,
+ prefix + expected + suffix);
+
+ ConvertQt4Connect factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+};
+
+QObject *ConvertQt4Connect::createTest()
+{
+ return new ConvertQt4ConnectTest;
+}
+#endif // WITH_TESTS
+
+} // namespace
+
+void registerConvertQt4ConnectQuickfix()
+{
+ CppQuickFixFactory::registerFactory<ConvertQt4Connect>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <convertqt4connect.moc>
+#endif
diff --git a/src/plugins/cppeditor/quickfixes/convertqt4connect.h b/src/plugins/cppeditor/quickfixes/convertqt4connect.h
new file mode 100644
index 0000000000..e96bd8f106
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/convertqt4connect.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerConvertQt4ConnectQuickfix();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/convertstringliteral.cpp b/src/plugins/cppeditor/quickfixes/convertstringliteral.cpp
new file mode 100644
index 0000000000..eff4b3a9ae
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/convertstringliteral.cpp
@@ -0,0 +1,746 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "convertstringliteral.h"
+
+#include "../cppeditordocument.h"
+#include "../cppeditortr.h"
+#include "../cppeditorwidget.h"
+#include "../cpprefactoringchanges.h"
+#include "cppquickfix.h"
+
+#include <QTextDecoder>
+
+#ifdef WITH_TESTS
+#include "cppquickfix_test.h"
+#include <QtTest>
+#endif
+
+using namespace CPlusPlus;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+enum StringLiteralType { TypeString, TypeObjCString, TypeChar, TypeNone };
+
+enum ActionFlags {
+ EncloseInQLatin1CharAction = 0x1,
+ EncloseInQLatin1StringAction = 0x2,
+ EncloseInQStringLiteralAction = 0x4,
+ EncloseInQByteArrayLiteralAction = 0x8,
+ EncloseActionMask = EncloseInQLatin1CharAction | EncloseInQLatin1StringAction
+ | EncloseInQStringLiteralAction | EncloseInQByteArrayLiteralAction,
+ TranslateTrAction = 0x10,
+ TranslateQCoreApplicationAction = 0x20,
+ TranslateNoopAction = 0x40,
+ TranslationMask = TranslateTrAction | TranslateQCoreApplicationAction | TranslateNoopAction,
+ RemoveObjectiveCAction = 0x100,
+ ConvertEscapeSequencesToCharAction = 0x200,
+ ConvertEscapeSequencesToStringAction = 0x400,
+ SingleQuoteAction = 0x800,
+ DoubleQuoteAction = 0x1000
+};
+
+static bool isQtStringLiteral(const QByteArray &id)
+{
+ return id == "QLatin1String" || id == "QLatin1Literal" || id == "QStringLiteral"
+ || id == "QByteArrayLiteral";
+}
+
+static bool isQtStringTranslation(const QByteArray &id)
+{
+ return id == "tr" || id == "trUtf8" || id == "translate" || id == "QT_TRANSLATE_NOOP";
+}
+
+/* Convert single-character string literals into character literals with some
+ * special cases "a" --> 'a', "'" --> '\'', "\n" --> '\n', "\"" --> '"'. */
+static QByteArray stringToCharEscapeSequences(const QByteArray &content)
+{
+ if (content.size() == 1)
+ return content.at(0) == '\'' ? QByteArray("\\'") : content;
+ if (content.size() == 2 && content.at(0) == '\\')
+ return content == "\\\"" ? QByteArray(1, '"') : content;
+ return QByteArray();
+}
+
+/* Convert character literal into a string literal with some special cases
+ * 'a' -> "a", '\n' -> "\n", '\'' --> "'", '"' --> "\"". */
+static QByteArray charToStringEscapeSequences(const QByteArray &content)
+{
+ if (content.size() == 1)
+ return content.at(0) == '"' ? QByteArray("\\\"") : content;
+ if (content.size() == 2)
+ return content == "\\'" ? QByteArray("'") : content;
+ return QByteArray();
+}
+
+static QString msgQtStringLiteralDescription(const QString &replacement)
+{
+ return Tr::tr("Enclose in %1(...)").arg(replacement);
+}
+
+static QString stringLiteralReplacement(unsigned actions)
+{
+ if (actions & EncloseInQLatin1CharAction)
+ return QLatin1String("QLatin1Char");
+ if (actions & EncloseInQLatin1StringAction)
+ return QLatin1String("QLatin1String");
+ if (actions & EncloseInQStringLiteralAction)
+ return QLatin1String("QStringLiteral");
+ if (actions & EncloseInQByteArrayLiteralAction)
+ return QLatin1String("QByteArrayLiteral");
+ if (actions & TranslateTrAction)
+ return QLatin1String("tr");
+ if (actions & TranslateQCoreApplicationAction)
+ return QLatin1String("QCoreApplication::translate");
+ if (actions & TranslateNoopAction)
+ return QLatin1String("QT_TRANSLATE_NOOP");
+ return QString();
+}
+
+static ExpressionAST *analyzeStringLiteral(const QList<AST *> &path,
+ const CppRefactoringFilePtr &file, StringLiteralType *type,
+ QByteArray *enclosingFunction = nullptr,
+ CallAST **enclosingFunctionCall = nullptr)
+{
+ *type = TypeNone;
+ if (enclosingFunction)
+ enclosingFunction->clear();
+ if (enclosingFunctionCall)
+ *enclosingFunctionCall = nullptr;
+
+ if (path.isEmpty())
+ return nullptr;
+
+ ExpressionAST *literal = path.last()->asExpression();
+ if (literal) {
+ if (literal->asStringLiteral()) {
+ // Check for Objective C string (@"bla")
+ const QChar firstChar = file->charAt(file->startOf(literal));
+ *type = firstChar == QLatin1Char('@') ? TypeObjCString : TypeString;
+ } else if (NumericLiteralAST *numericLiteral = literal->asNumericLiteral()) {
+ // character ('c') constants are numeric.
+ if (file->tokenAt(numericLiteral->literal_token).is(T_CHAR_LITERAL))
+ *type = TypeChar;
+ }
+ }
+
+ if (*type != TypeNone && enclosingFunction && path.size() > 1) {
+ if (CallAST *call = path.at(path.size() - 2)->asCall()) {
+ if (call->base_expression) {
+ if (IdExpressionAST *idExpr = call->base_expression->asIdExpression()) {
+ if (SimpleNameAST *functionName = idExpr->name->asSimpleName()) {
+ *enclosingFunction = file->tokenAt(functionName->identifier_token).identifier->chars();
+ if (enclosingFunctionCall)
+ *enclosingFunctionCall = call;
+ }
+ }
+ }
+ }
+ }
+ return literal;
+}
+
+class EscapeStringLiteralOperation: public CppQuickFixOperation
+{
+public:
+ EscapeStringLiteralOperation(const CppQuickFixInterface &interface,
+ ExpressionAST *literal, bool escape)
+ : CppQuickFixOperation(interface)
+ , m_literal(literal)
+ , m_escape(escape)
+ {
+ if (m_escape) {
+ setDescription(Tr::tr("Escape String Literal as UTF-8"));
+ } else {
+ setDescription(Tr::tr("Unescape String Literal as UTF-8"));
+ }
+ }
+
+private:
+ static inline bool isDigit(quint8 ch, int base)
+ {
+ if (base == 8)
+ return ch >= '0' && ch < '8';
+ if (base == 16)
+ return isxdigit(ch);
+ return false;
+ }
+
+ static QByteArrayList escapeString(const QByteArray &contents)
+ {
+ QByteArrayList newContents;
+ QByteArray chunk;
+ bool wasEscaped = false;
+ for (const quint8 c : contents) {
+ const bool needsEscape = !isascii(c) || !isprint(c);
+ if (!needsEscape && wasEscaped && std::isxdigit(c) && !chunk.isEmpty()) {
+ newContents << chunk;
+ chunk.clear();
+ }
+ if (needsEscape)
+ chunk += QByteArray("\\x") + QByteArray::number(c, 16).rightJustified(2, '0');
+ else
+ chunk += c;
+ wasEscaped = needsEscape;
+ }
+ if (!chunk.isEmpty())
+ newContents << chunk;
+ return newContents;
+ }
+
+ static QByteArray unescapeString(const QByteArray &contents)
+ {
+ QByteArray newContents;
+ const int len = contents.length();
+ for (int i = 0; i < len; ++i) {
+ quint8 c = contents.at(i);
+ if (c == '\\' && i < len - 1) {
+ int idx = i + 1;
+ quint8 ch = contents.at(idx);
+ int base = 0;
+ int maxlen = 0;
+ if (isDigit(ch, 8)) {
+ base = 8;
+ maxlen = 3;
+ } else if ((ch == 'x' || ch == 'X') && idx < len - 1) {
+ base = 16;
+ maxlen = 2;
+ ch = contents.at(++idx);
+ }
+ if (base > 0) {
+ QByteArray buf;
+ while (isDigit(ch, base) && idx < len && buf.length() < maxlen) {
+ buf += ch;
+ ++idx;
+ if (idx == len)
+ break;
+ ch = contents.at(idx);
+ }
+ if (!buf.isEmpty()) {
+ bool ok;
+ uint value = buf.toUInt(&ok, base);
+ // Don't unescape isascii() && !isprint()
+ if (ok && (!isascii(value) || isprint(value))) {
+ newContents += value;
+ i = idx - 1;
+ continue;
+ }
+ }
+ }
+ newContents += c;
+ c = contents.at(++i);
+ }
+ newContents += c;
+ }
+ return newContents;
+ }
+
+ // QuickFixOperation interface
+public:
+ void perform() override
+ {
+ const int startPos = currentFile()->startOf(m_literal);
+ const int endPos = currentFile()->endOf(m_literal);
+
+ StringLiteralAST *stringLiteral = m_literal->asStringLiteral();
+ QTC_ASSERT(stringLiteral, return);
+ const QByteArray oldContents(currentFile()->tokenAt(stringLiteral->literal_token).
+ identifier->chars());
+ QByteArrayList newContents;
+ if (m_escape)
+ newContents = escapeString(oldContents);
+ else
+ newContents = {unescapeString(oldContents)};
+
+ if (newContents.isEmpty()
+ || (newContents.size() == 1 && newContents.first() == oldContents)) {
+ return;
+ }
+
+ QTextCodec *utf8codec = QTextCodec::codecForName("UTF-8");
+ QScopedPointer<QTextDecoder> decoder(utf8codec->makeDecoder());
+ ChangeSet changes;
+
+ bool replace = true;
+ for (const QByteArray &chunk : std::as_const(newContents)) {
+ const QString str = decoder->toUnicode(chunk);
+ const QByteArray utf8buf = str.toUtf8();
+ if (!utf8codec->canEncode(str) || chunk != utf8buf)
+ return;
+ if (replace)
+ changes.replace(startPos + 1, endPos - 1, str);
+ else
+ changes.insert(endPos, "\"" + str + "\"");
+ replace = false;
+ }
+ currentFile()->apply(changes);
+ }
+
+private:
+ ExpressionAST *m_literal;
+ bool m_escape;
+};
+
+/// Operation performs the operations of type ActionFlags passed in as actions.
+class WrapStringLiteralOp : public CppQuickFixOperation
+{
+public:
+ WrapStringLiteralOp(const CppQuickFixInterface &interface, int priority,
+ unsigned actions, const QString &description, ExpressionAST *literal,
+ const QString &translationContext = QString())
+ : CppQuickFixOperation(interface, priority), m_actions(actions), m_literal(literal),
+ m_translationContext(translationContext)
+ {
+ setDescription(description);
+ }
+
+ void perform() override
+ {
+ ChangeSet changes;
+
+ const int startPos = currentFile()->startOf(m_literal);
+ const int endPos = currentFile()->endOf(m_literal);
+
+ // kill leading '@'. No need to adapt endPos, that is done by ChangeSet
+ if (m_actions & RemoveObjectiveCAction)
+ changes.remove(startPos, startPos + 1);
+
+ // Fix quotes
+ if (m_actions & (SingleQuoteAction | DoubleQuoteAction)) {
+ const QString newQuote((m_actions & SingleQuoteAction)
+ ? QLatin1Char('\'') : QLatin1Char('"'));
+ changes.replace(startPos, startPos + 1, newQuote);
+ changes.replace(endPos - 1, endPos, newQuote);
+ }
+
+ // Convert single character strings into character constants
+ if (m_actions & ConvertEscapeSequencesToCharAction) {
+ StringLiteralAST *stringLiteral = m_literal->asStringLiteral();
+ QTC_ASSERT(stringLiteral, return ;);
+ const QByteArray oldContents(currentFile()->tokenAt(stringLiteral->literal_token).identifier->chars());
+ const QByteArray newContents = stringToCharEscapeSequences(oldContents);
+ QTC_ASSERT(!newContents.isEmpty(), return ;);
+ if (oldContents != newContents)
+ changes.replace(startPos + 1, endPos -1, QString::fromLatin1(newContents));
+ }
+
+ // Convert character constants into strings constants
+ if (m_actions & ConvertEscapeSequencesToStringAction) {
+ NumericLiteralAST *charLiteral = m_literal->asNumericLiteral(); // char 'c' constants are numerical.
+ QTC_ASSERT(charLiteral, return ;);
+ const QByteArray oldContents(currentFile()->tokenAt(charLiteral->literal_token).identifier->chars());
+ const QByteArray newContents = charToStringEscapeSequences(oldContents);
+ QTC_ASSERT(!newContents.isEmpty(), return ;);
+ if (oldContents != newContents)
+ changes.replace(startPos + 1, endPos -1, QString::fromLatin1(newContents));
+ }
+
+ // Enclose in literal or translation function, macro.
+ if (m_actions & (EncloseActionMask | TranslationMask)) {
+ changes.insert(endPos, QString(QLatin1Char(')')));
+ QString leading = stringLiteralReplacement(m_actions);
+ leading += QLatin1Char('(');
+ if (m_actions
+ & (TranslateQCoreApplicationAction | TranslateNoopAction)) {
+ leading += QLatin1Char('"');
+ leading += m_translationContext;
+ leading += QLatin1String("\", ");
+ }
+ changes.insert(startPos, leading);
+ }
+
+ currentFile()->apply(changes);
+ }
+
+private:
+ const unsigned m_actions;
+ ExpressionAST *m_literal;
+ const QString m_translationContext;
+};
+
+class ConvertCStringToNSStringOp: public CppQuickFixOperation
+{
+public:
+ ConvertCStringToNSStringOp(const CppQuickFixInterface &interface, int priority,
+ StringLiteralAST *stringLiteral, CallAST *qlatin1Call)
+ : CppQuickFixOperation(interface, priority)
+ , stringLiteral(stringLiteral)
+ , qlatin1Call(qlatin1Call)
+ {
+ setDescription(Tr::tr("Convert to Objective-C String Literal"));
+ }
+
+ void perform() override
+ {
+ ChangeSet changes;
+
+ if (qlatin1Call) {
+ changes.replace(currentFile()->startOf(qlatin1Call), currentFile()->startOf(stringLiteral),
+ QLatin1String("@"));
+ changes.remove(currentFile()->endOf(stringLiteral), currentFile()->endOf(qlatin1Call));
+ } else {
+ changes.insert(currentFile()->startOf(stringLiteral), QLatin1String("@"));
+ }
+
+ currentFile()->apply(changes);
+ }
+
+private:
+ StringLiteralAST *stringLiteral;
+ CallAST *qlatin1Call;
+};
+
+/*!
+ Replace
+ "abcd"
+ QLatin1String("abcd")
+ QLatin1Literal("abcd")
+
+ With
+ @"abcd"
+
+ Activates on: the string literal, if the file type is a Objective-C(++) file.
+*/
+class ConvertCStringToNSString: public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ CppRefactoringFilePtr file = interface.currentFile();
+
+ if (!interface.editor()->cppEditorDocument()->isObjCEnabled())
+ return;
+
+ StringLiteralType type = TypeNone;
+ QByteArray enclosingFunction;
+ CallAST *qlatin1Call;
+ const QList<AST *> &path = interface.path();
+ ExpressionAST *literal = analyzeStringLiteral(path, file, &type, &enclosingFunction,
+ &qlatin1Call);
+ if (!literal || type != TypeString)
+ return;
+ if (!isQtStringLiteral(enclosingFunction))
+ qlatin1Call = nullptr;
+
+ result << new ConvertCStringToNSStringOp(interface, path.size() - 1, literal->asStringLiteral(),
+ qlatin1Call);
+ }
+};
+
+/*!
+ Replace
+ "abcd"
+
+ With
+ tr("abcd") or
+ QCoreApplication::translate("CONTEXT", "abcd") or
+ QT_TRANSLATE_NOOP("GLOBAL", "abcd")
+
+ depending on what is available.
+
+ Activates on: the string literal
+*/
+class TranslateStringLiteral: public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ // Initialize
+ StringLiteralType type = TypeNone;
+ QByteArray enclosingFunction;
+ const QList<AST *> &path = interface.path();
+ CppRefactoringFilePtr file = interface.currentFile();
+ ExpressionAST *literal = analyzeStringLiteral(path, file, &type, &enclosingFunction);
+ if (!literal || type != TypeString
+ || isQtStringLiteral(enclosingFunction) || isQtStringTranslation(enclosingFunction))
+ return;
+
+ QString trContext;
+
+ std::shared_ptr<Control> control = interface.context().bindings()->control();
+ const Name *trName = control->identifier("tr");
+
+ // Check whether we are in a function:
+ const QString description = Tr::tr("Mark as Translatable");
+ for (int i = path.size() - 1; i >= 0; --i) {
+ if (FunctionDefinitionAST *definition = path.at(i)->asFunctionDefinition()) {
+ Function *function = definition->symbol;
+ ClassOrNamespace *b = interface.context().lookupType(function);
+ if (b) {
+ // Do we have a tr function?
+ const QList<LookupItem> items = b->find(trName);
+ for (const LookupItem &r : items) {
+ Symbol *s = r.declaration();
+ if (s->type()->asFunctionType()) {
+ // no context required for tr
+ result << new WrapStringLiteralOp(interface, path.size() - 1,
+ TranslateTrAction,
+ description, literal);
+ return;
+ }
+ }
+ }
+ // We need to do a QCA::translate, so we need a context.
+ // Use fully qualified class name:
+ Overview oo;
+ const QList<const Name *> names = LookupContext::path(function);
+ for (const Name *n : names) {
+ if (!trContext.isEmpty())
+ trContext.append(QLatin1String("::"));
+ trContext.append(oo.prettyName(n));
+ }
+ // ... or global if none available!
+ if (trContext.isEmpty())
+ trContext = QLatin1String("GLOBAL");
+ result << new WrapStringLiteralOp(interface, path.size() - 1,
+ TranslateQCoreApplicationAction,
+ description, literal, trContext);
+ return;
+ }
+ }
+
+ // We need to use Q_TRANSLATE_NOOP
+ result << new WrapStringLiteralOp(interface, path.size() - 1,
+ TranslateNoopAction,
+ description, literal, trContext);
+ }
+};
+
+/*!
+ Replace
+ "abcd" -> QLatin1String("abcd")
+ @"abcd" -> QLatin1String("abcd") (Objective C)
+ 'a' -> QLatin1Char('a')
+ 'a' -> "a"
+ "a" -> 'a' or QLatin1Char('a') (Single character string constants)
+ "\n" -> '\n', QLatin1Char('\n')
+
+ Except if they are already enclosed in
+ QLatin1Char, QT_TRANSLATE_NOOP, tr,
+ trUtf8, QLatin1Literal, QLatin1String
+
+ Activates on: the string or character literal
+*/
+
+class WrapStringLiteral: public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ StringLiteralType type = TypeNone;
+ QByteArray enclosingFunction;
+ const QList<AST *> &path = interface.path();
+ CppRefactoringFilePtr file = interface.currentFile();
+ ExpressionAST *literal = analyzeStringLiteral(path, file, &type, &enclosingFunction);
+ if (!literal || type == TypeNone)
+ return;
+ if ((type == TypeChar && enclosingFunction == "QLatin1Char")
+ || isQtStringLiteral(enclosingFunction)
+ || isQtStringTranslation(enclosingFunction))
+ return;
+
+ const int priority = path.size() - 1; // very high priority
+ if (type == TypeChar) {
+ unsigned actions = EncloseInQLatin1CharAction;
+ QString description = msgQtStringLiteralDescription(stringLiteralReplacement(actions));
+ result << new WrapStringLiteralOp(interface, priority, actions, description, literal);
+ if (NumericLiteralAST *charLiteral = literal->asNumericLiteral()) {
+ const QByteArray contents(file->tokenAt(charLiteral->literal_token).identifier->chars());
+ if (!charToStringEscapeSequences(contents).isEmpty()) {
+ actions = DoubleQuoteAction | ConvertEscapeSequencesToStringAction;
+ description = Tr::tr("Convert to String Literal");
+ result << new WrapStringLiteralOp(interface, priority, actions,
+ description, literal);
+ }
+ }
+ } else {
+ const unsigned objectiveCActions = type == TypeObjCString ?
+ unsigned(RemoveObjectiveCAction) : 0u;
+ unsigned actions = 0;
+ if (StringLiteralAST *stringLiteral = literal->asStringLiteral()) {
+ const QByteArray contents(file->tokenAt(stringLiteral->literal_token).identifier->chars());
+ if (!stringToCharEscapeSequences(contents).isEmpty()) {
+ actions = EncloseInQLatin1CharAction | SingleQuoteAction
+ | ConvertEscapeSequencesToCharAction | objectiveCActions;
+ QString description =
+ Tr::tr("Convert to Character Literal and Enclose in QLatin1Char(...)");
+ result << new WrapStringLiteralOp(interface, priority, actions,
+ description, literal);
+ actions &= ~EncloseInQLatin1CharAction;
+ description = Tr::tr("Convert to Character Literal");
+ result << new WrapStringLiteralOp(interface, priority, actions,
+ description, literal);
+ }
+ }
+ actions = EncloseInQLatin1StringAction | objectiveCActions;
+ result << new WrapStringLiteralOp(interface, priority, actions,
+ msgQtStringLiteralDescription(stringLiteralReplacement(actions)), literal);
+ actions = EncloseInQStringLiteralAction | objectiveCActions;
+ result << new WrapStringLiteralOp(interface, priority, actions,
+ msgQtStringLiteralDescription(stringLiteralReplacement(actions)), literal);
+ actions = EncloseInQByteArrayLiteralAction | objectiveCActions;
+ result << new WrapStringLiteralOp(interface, priority, actions,
+ msgQtStringLiteralDescription(stringLiteralReplacement(actions)), literal);
+ }
+ }
+};
+
+/*!
+ Escapes or unescapes a string literal as UTF-8.
+
+ Escapes non-ASCII characters in a string literal to hexadecimal escape sequences.
+ Unescapes octal or hexadecimal escape sequences in a string literal.
+ String literals are handled as UTF-8 even if file's encoding is not UTF-8.
+ */
+class EscapeStringLiteral : public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override
+ {
+ const QList<AST *> &path = interface.path();
+ if (path.isEmpty())
+ return;
+
+ AST * const lastAst = path.last();
+ ExpressionAST *literal = lastAst->asStringLiteral();
+ if (!literal)
+ return;
+
+ StringLiteralAST *stringLiteral = literal->asStringLiteral();
+ CppRefactoringFilePtr file = interface.currentFile();
+ const QByteArray contents(file->tokenAt(stringLiteral->literal_token).identifier->chars());
+
+ bool canEscape = false;
+ bool canUnescape = false;
+ for (int i = 0; i < contents.length(); ++i) {
+ quint8 c = contents.at(i);
+ if (!isascii(c) || !isprint(c)) {
+ canEscape = true;
+ } else if (c == '\\' && i < contents.length() - 1) {
+ c = contents.at(++i);
+ if ((c >= '0' && c < '8') || c == 'x' || c == 'X')
+ canUnescape = true;
+ }
+ }
+
+ if (canEscape)
+ result << new EscapeStringLiteralOperation(interface, literal, true);
+
+ if (canUnescape)
+ result << new EscapeStringLiteralOperation(interface, literal, false);
+ }
+};
+
+#ifdef WITH_TESTS
+using namespace Tests;
+
+class EscapeStringLiteralTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data()
+ {
+ QTest::addColumn<QByteArray>("original");
+ QTest::addColumn<QByteArray>("expected");
+
+ // Escape String Literal as UTF-8 (no-trigger)
+ QTest::newRow("EscapeStringLiteral_notrigger")
+ << QByteArray("const char *notrigger = \"@abcdef \\a\\n\\\\\";\n")
+ << QByteArray();
+
+ // Escape String Literal as UTF-8
+ QTest::newRow("EscapeStringLiteral")
+ << QByteArray("const char *utf8 = \"@\xe3\x81\x82\xe3\x81\x84\";\n")
+ << QByteArray("const char *utf8 = \"\\xe3\\x81\\x82\\xe3\\x81\\x84\";\n");
+
+ // Unescape String Literal as UTF-8 (from hexdecimal escape sequences)
+ QTest::newRow("UnescapeStringLiteral_hex")
+ << QByteArray("const char *hex_escaped = \"@\\xe3\\x81\\x82\\xe3\\x81\\x84\";\n")
+ << QByteArray("const char *hex_escaped = \"\xe3\x81\x82\xe3\x81\x84\";\n");
+
+ // Unescape String Literal as UTF-8 (from octal escape sequences)
+ QTest::newRow("UnescapeStringLiteral_oct")
+ << QByteArray("const char *oct_escaped = \"@\\343\\201\\202\\343\\201\\204\";\n")
+ << QByteArray("const char *oct_escaped = \"\xe3\x81\x82\xe3\x81\x84\";\n");
+
+ // Unescape String Literal as UTF-8 (triggered but no change)
+ QTest::newRow("UnescapeStringLiteral_noconv")
+ << QByteArray("const char *escaped_ascii = \"@\\x1b\";\n")
+ << QByteArray("const char *escaped_ascii = \"\\x1b\";\n");
+
+ // Unescape String Literal as UTF-8 (no conversion because of invalid utf-8)
+ QTest::newRow("UnescapeStringLiteral_invalid")
+ << QByteArray("const char *escaped = \"@\\xe3\\x81\";\n")
+ << QByteArray("const char *escaped = \"\\xe3\\x81\";\n");
+
+ QTest::newRow("escape string literal: simple case")
+ << QByteArray(R"(const char *str = @"àxyz";)")
+ << QByteArray(R"(const char *str = "\xc3\xa0xyz";)");
+ QTest::newRow("escape string literal: simple case reverse")
+ << QByteArray(R"(const char *str = @"\xc3\xa0xyz";)")
+ << QByteArray(R"(const char *str = "àxyz";)");
+ QTest::newRow("escape string literal: raw string literal")
+ << QByteArray(R"x(const char *str = @R"(àxyz)";)x")
+ << QByteArray(R"x(const char *str = R"(\xc3\xa0xyz)";)x");
+ QTest::newRow("escape string literal: splitting required")
+ << QByteArray(R"(const char *str = @"àf23бgб1";)")
+ << QByteArray(R"(const char *str = "\xc3\xa0""f23\xd0\xb1g\xd0\xb1""1";)");
+ QTest::newRow("escape string literal: unescape adjacent literals")
+ << QByteArray(R"(const char *str = @"\xc3\xa0""f23\xd0\xb1g\xd0\xb1""1";)")
+ << QByteArray(R"(const char *str = "àf23бgб1";)");
+ }
+
+ void test()
+ {
+ QFETCH(QByteArray, original);
+ QFETCH(QByteArray, expected);
+
+ EscapeStringLiteral factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+};
+
+QObject *EscapeStringLiteral::createTest() { return new EscapeStringLiteralTest; }
+QObject *ConvertCStringToNSString::createTest() { return new QObject; }
+QObject *WrapStringLiteral::createTest() { return new QObject; }
+QObject *TranslateStringLiteral::createTest() { return new QObject; }
+
+#endif // WITH_TESTS
+} // namespace
+
+void registerConvertStringLiteralQuickfixes()
+{
+ CppQuickFixFactory::registerFactory<ConvertCStringToNSString>();
+ CppQuickFixFactory::registerFactory<EscapeStringLiteral>();
+ CppQuickFixFactory::registerFactory<TranslateStringLiteral>();
+ CppQuickFixFactory::registerFactory<WrapStringLiteral>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <convertstringliteral.moc>
+#endif
diff --git a/src/plugins/cppeditor/quickfixes/convertstringliteral.h b/src/plugins/cppeditor/quickfixes/convertstringliteral.h
new file mode 100644
index 0000000000..16cefacae0
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/convertstringliteral.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerConvertStringLiteralQuickfixes();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/converttocamelcase.cpp b/src/plugins/cppeditor/quickfixes/converttocamelcase.cpp
new file mode 100644
index 0000000000..c597a29c11
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/converttocamelcase.cpp
@@ -0,0 +1,184 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "converttocamelcase.h"
+
+#include "../cppeditortr.h"
+#include "../cppeditorwidget.h"
+#include "../cpprefactoringchanges.h"
+#include "cppquickfix.h"
+
+#ifdef WITH_TESTS
+#include "cppquickfix_test.h"
+#include <QtTest>
+#endif
+
+using namespace CPlusPlus;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+class ConvertToCamelCaseOp: public CppQuickFixOperation
+{
+public:
+ ConvertToCamelCaseOp(const CppQuickFixInterface &interface, const QString &name,
+ const AST *nameAst, bool test)
+ : CppQuickFixOperation(interface, -1)
+ , m_name(name)
+ , m_nameAst(nameAst)
+ , m_isAllUpper(name.isUpper())
+ , m_test(test)
+ {
+ setDescription(Tr::tr("Convert to Camel Case"));
+ }
+
+ static bool isConvertibleUnderscore(const QString &name, int pos)
+ {
+ return name.at(pos) == QLatin1Char('_') && name.at(pos+1).isLetter()
+ && !(pos == 1 && name.at(0) == QLatin1Char('m'));
+ }
+
+private:
+ void perform() override
+ {
+ QString newName = m_isAllUpper ? m_name.toLower() : m_name;
+ for (int i = 1; i < newName.length(); ++i) {
+ const QChar c = newName.at(i);
+ if (c.isUpper() && m_isAllUpper) {
+ newName[i] = c.toLower();
+ } else if (i < newName.length() - 1 && isConvertibleUnderscore(newName, i)) {
+ newName.remove(i, 1);
+ newName[i] = newName.at(i).toUpper();
+ }
+ }
+ if (m_test)
+ currentFile()->apply(ChangeSet::makeReplace(currentFile()->range(m_nameAst), newName));
+ else
+ editor()->renameUsages(newName);
+ }
+
+ const QString m_name;
+ const AST * const m_nameAst;
+ const bool m_isAllUpper;
+ const bool m_test;
+};
+
+/*!
+ Turns "an_example_symbol" into "anExampleSymbol" and
+ "AN_EXAMPLE_SYMBOL" into "AnExampleSymbol".
+
+ Activates on: identifiers
+*/
+class ConvertToCamelCase : public CppQuickFixFactory
+{
+public:
+ ConvertToCamelCase(bool test = false) : m_test(test) {}
+
+#ifdef WITH_TESTS
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const QList<AST *> &path = interface.path();
+
+ if (path.isEmpty())
+ return;
+
+ AST * const ast = path.last();
+ const Name *name = nullptr;
+ const AST *astForName = nullptr;
+ if (const NameAST * const nameAst = ast->asName()) {
+ if (nameAst->name && nameAst->name->asNameId()) {
+ astForName = nameAst;
+ name = nameAst->name;
+ }
+ } else if (const NamespaceAST * const namespaceAst = ast->asNamespace()) {
+ astForName = namespaceAst;
+ name = namespaceAst->symbol->name();
+ }
+
+ if (!name)
+ return;
+
+ QString nameString = QString::fromUtf8(name->identifier()->chars());
+ if (nameString.length() < 3)
+ return;
+ for (int i = 1; i < nameString.length() - 1; ++i) {
+ if (ConvertToCamelCaseOp::isConvertibleUnderscore(nameString, i)) {
+ result << new ConvertToCamelCaseOp(interface, nameString, astForName, m_test);
+ return;
+ }
+ }
+ }
+
+ const bool m_test;
+};
+
+#ifdef WITH_TESTS
+using namespace Tests;
+
+class ConvertToCamelCaseTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data()
+ {
+ QTest::addColumn<QByteArray>("original");
+ QTest::addColumn<QByteArray>("expected");
+
+ using QByteArray = QByteArray;
+
+ QTest::newRow("convert to camel case: normal")
+ << QByteArray("void @lower_case_function();\n")
+ << QByteArray("void lowerCaseFunction();\n");
+ QTest::newRow("convert to camel case: already camel case")
+ << QByteArray("void @camelCaseFunction();\n")
+ << QByteArray();
+ QTest::newRow("convert to camel case: no underscores (lower case)")
+ << QByteArray("void @lowercasefunction();\n")
+ << QByteArray();
+ QTest::newRow("convert to camel case: no underscores (upper case)")
+ << QByteArray("void @UPPERCASEFUNCTION();\n")
+ << QByteArray();
+ QTest::newRow("convert to camel case: non-applicable underscore")
+ << QByteArray("void @m_a_member;\n")
+ << QByteArray("void m_aMember;\n");
+ QTest::newRow("convert to camel case: upper case")
+ << QByteArray("void @UPPER_CASE_FUNCTION();\n")
+ << QByteArray("void upperCaseFunction();\n");
+ QTest::newRow("convert to camel case: partially camel case already")
+ << QByteArray("void mixed@_andCamelCase();\n")
+ << QByteArray("void mixedAndCamelCase();\n");
+ QTest::newRow("convert to camel case: wild mix")
+ << QByteArray("void @WhAt_TODO_hErE();\n")
+ << QByteArray("void WhAtTODOHErE();\n");
+ }
+
+ void test()
+ {
+ QFETCH(QByteArray, original);
+ QFETCH(QByteArray, expected);
+ ConvertToCamelCase factory(true);
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+};
+
+QObject *ConvertToCamelCase::createTest() { return new ConvertToCamelCaseTest; }
+
+#endif // WITH_TESTS
+} // namespace
+
+void registerConvertToCamelCaseQuickfix()
+{
+ CppQuickFixFactory::registerFactory<ConvertToCamelCase>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <converttocamelcase.moc>
+#endif
diff --git a/src/plugins/cppeditor/quickfixes/converttocamelcase.h b/src/plugins/cppeditor/quickfixes/converttocamelcase.h
new file mode 100644
index 0000000000..2d7acb08f7
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/converttocamelcase.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerConvertToCamelCaseQuickfix();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/converttometamethodcall.cpp b/src/plugins/cppeditor/quickfixes/converttometamethodcall.cpp
new file mode 100644
index 0000000000..9d94808fbf
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/converttometamethodcall.cpp
@@ -0,0 +1,273 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "converttometamethodcall.h"
+
+#include "../cppeditortr.h"
+#include "../cpprefactoringchanges.h"
+#include "cppquickfix.h"
+#include "cppquickfixhelpers.h"
+
+#include <cplusplus/ASTPath.h>
+#include <cplusplus/Overview.h>
+#include <cplusplus/TypeOfExpression.h>
+
+#ifdef WITH_TESTS
+#include "cppquickfix_test.h"
+#include <QtTest>
+#endif
+
+using namespace CPlusPlus;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+class ConvertToMetaMethodCallOp : public CppQuickFixOperation
+{
+public:
+ ConvertToMetaMethodCallOp(const CppQuickFixInterface &interface, CallAST *callAst)
+ : CppQuickFixOperation(interface), m_callAst(callAst)
+ {
+ setDescription(Tr::tr("Convert Function Call to Qt Meta-Method Invocation"));
+ }
+
+private:
+ void perform() override
+ {
+ // Construct the argument list.
+ Overview ov;
+ QStringList arguments;
+ for (ExpressionListAST *it = m_callAst->expression_list; it; it = it->next) {
+ if (!it->value)
+ return;
+ const FullySpecifiedType argType
+ = typeOfExpr(it->value, currentFile(), snapshot(), context());
+ if (!argType.isValid())
+ return;
+ arguments << QString::fromUtf8("Q_ARG(%1, %2)")
+ .arg(ov.prettyType(argType), currentFile()->textOf(it->value));
+ }
+ QString argsString = arguments.join(", ");
+ if (!argsString.isEmpty())
+ argsString.prepend(", ");
+
+ // Construct the replace string.
+ const auto memberAccessAst = m_callAst->base_expression->asMemberAccess();
+ QTC_ASSERT(memberAccessAst, return);
+ QString baseExpr = currentFile()->textOf(memberAccessAst->base_expression);
+ const FullySpecifiedType baseExprType
+ = typeOfExpr(memberAccessAst->base_expression, currentFile(), snapshot(), context());
+ if (!baseExprType.isValid())
+ return;
+ if (!baseExprType->asPointerType())
+ baseExpr.prepend('&');
+ const QString functionName = currentFile()->textOf(memberAccessAst->member_name);
+ const QString qMetaObject = "QMetaObject";
+ const QString newCall = QString::fromUtf8("%1::invokeMethod(%2, \"%3\"%4)")
+ .arg(qMetaObject, baseExpr, functionName, argsString);
+
+ // Determine the start and end positions of the replace operation.
+ // If the call is preceded by an "emit" keyword, that one has to be removed as well.
+ int firstToken = m_callAst->firstToken();
+ if (firstToken > 0)
+ switch (semanticInfo().doc->translationUnit()->tokenKind(firstToken - 1)) {
+ case T_EMIT: case T_Q_EMIT: --firstToken; break;
+ default: break;
+ }
+ const TranslationUnit *const tu = semanticInfo().doc->translationUnit();
+ const int startPos = tu->getTokenPositionInDocument(firstToken, textDocument());
+ const int endPos = tu->getTokenPositionInDocument(m_callAst->lastToken(), textDocument());
+
+ // Replace the old call with the new one.
+ ChangeSet changes;
+ changes.replace(startPos, endPos, newCall);
+
+ // Insert include for QMetaObject, if necessary.
+ const Identifier qMetaObjectId(qPrintable(qMetaObject), qMetaObject.size());
+ Scope * const scope = currentFile()->scopeAt(firstToken);
+ const QList<LookupItem> results = context().lookup(&qMetaObjectId, scope);
+ bool isDeclared = false;
+ for (const LookupItem &item : results) {
+ if (Symbol *declaration = item.declaration(); declaration && declaration->asClass()) {
+ isDeclared = true;
+ break;
+ }
+ }
+ if (!isDeclared) {
+ insertNewIncludeDirective('<' + qMetaObject + '>', currentFile(), semanticInfo().doc,
+ changes);
+ }
+
+ // Apply the changes.
+ currentFile()->apply(changes);
+ }
+
+ const CallAST * const m_callAst;
+};
+
+//! Converts a normal function call into a meta method invocation, if the functions is
+//! marked as invokable.
+class ConvertToMetaMethodCall : public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest();
+#endif
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const Document::Ptr &cppDoc = interface.currentFile()->cppDocument();
+ const QList<AST *> path = ASTPath(cppDoc)(interface.cursor());
+ if (path.isEmpty())
+ return;
+
+ // Are we on a member function call?
+ CallAST *callAst = nullptr;
+ for (auto it = path.crbegin(); it != path.crend(); ++it) {
+ if ((callAst = (*it)->asCall()))
+ break;
+ }
+ if (!callAst || !callAst->base_expression)
+ return;
+ ExpressionAST *baseExpr = nullptr;
+ const NameAST *nameAst = nullptr;
+ if (const MemberAccessAST * const ast = callAst->base_expression->asMemberAccess()) {
+ baseExpr = ast->base_expression;
+ nameAst = ast->member_name;
+ }
+ if (!baseExpr || !nameAst || !nameAst->name)
+ return;
+
+ // Locate called function and check whether it is invokable.
+ Scope *scope = cppDoc->globalNamespace();
+ for (auto it = path.crbegin(); it != path.crend(); ++it) {
+ if (const CompoundStatementAST * const stmtAst = (*it)->asCompoundStatement()) {
+ scope = stmtAst->symbol;
+ break;
+ }
+ }
+ const LookupContext context(cppDoc, interface.snapshot());
+ TypeOfExpression exprType;
+ exprType.setExpandTemplates(true);
+ exprType.init(cppDoc, interface.snapshot());
+ const QList<LookupItem> typeMatches = exprType(callAst->base_expression, cppDoc, scope);
+ for (const LookupItem &item : typeMatches) {
+ if (const auto func = item.type()->asFunctionType(); func && func->methodKey()) {
+ result << new ConvertToMetaMethodCallOp(interface, callAst);
+ return;
+ }
+ }
+ }
+};
+
+#ifdef WITH_TESTS
+using namespace Tests;
+
+class ConvertToMetaMethodCallTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data()
+ {
+ QTest::addColumn<QByteArray>("input");
+ QTest::addColumn<QByteArray>("expected");
+
+ // ^ marks the cursor locations.
+ // $ marks the replacement regions.
+ // The quoted string in the comment is the data tag.
+ // The rest of the comment is the replacement string.
+ const QByteArray allCases = R"(
+class C {
+public:
+ C() {
+ $this->^aSignal()$; // "signal from region on pointer to object" QMetaObject::invokeMethod(this, "aSignal")
+ C c;
+ $c.^aSignal()$; // "signal from region on object value" QMetaObject::invokeMethod(&c, "aSignal")
+ $(new C)->^aSignal()$; // "signal from region on expression" QMetaObject::invokeMethod((new C), "aSignal")
+ $emit this->^aSignal()$; // "signal from region, with emit" QMetaObject::invokeMethod(this, "aSignal")
+ $Q_EMIT this->^aSignal()$; // "signal from region, with Q_EMIT" QMetaObject::invokeMethod(this, "aSignal")
+ $this->^aSlot()$; // "slot from region" QMetaObject::invokeMethod(this, "aSlot")
+ $this->^noArgs()$; // "Q_SIGNAL, no arguments" QMetaObject::invokeMethod(this, "noArgs")
+ $this->^oneArg(0)$; // "Q_SLOT, one argument" QMetaObject::invokeMethod(this, "oneArg", Q_ARG(int, 0))
+ $this->^twoArgs(0, c)$; // "Q_INVOKABLE, two arguments" QMetaObject::invokeMethod(this, "twoArgs", Q_ARG(int, 0), Q_ARG(C, c))
+ this->^notInvokable(); // "not invokable"
+ }
+
+signals:
+ void aSignal();
+
+private slots:
+ void aSlot();
+
+private:
+ Q_SIGNAL void noArgs();
+ Q_SLOT void oneArg(int index);
+ Q_INVOKABLE void twoArgs(int index, const C &value);
+ void notInvokable();
+};
+)";
+
+ qsizetype nextCursor = allCases.indexOf('^');
+ while (nextCursor != -1) {
+ const int commentStart = allCases.indexOf("//", nextCursor);
+ QVERIFY(commentStart != -1);
+ const int tagStart = allCases.indexOf('"', commentStart + 2);
+ QVERIFY(tagStart != -1);
+ const int tagEnd = allCases.indexOf('"', tagStart + 1);
+ QVERIFY(tagEnd != -1);
+ QByteArray input = allCases;
+ QByteArray output = allCases;
+ input.replace(nextCursor, 1, "@");
+ const QByteArray tag = allCases.mid(tagStart + 1, tagEnd - tagStart - 1);
+ const int prevNewline = allCases.lastIndexOf('\n', nextCursor);
+ const int regionStart = allCases.lastIndexOf('$', nextCursor);
+ bool hasReplacement = false;
+ if (regionStart != -1 && regionStart > prevNewline) {
+ const int regionEnd = allCases.indexOf('$', regionStart + 1);
+ QVERIFY(regionEnd != -1);
+ const int nextNewline = allCases.indexOf('\n', tagEnd);
+ QVERIFY(nextNewline != -1);
+ const QByteArray replacement
+ = allCases.mid(tagEnd + 1, nextNewline - tagEnd - 1).trimmed();
+ output.replace(regionStart, regionEnd - regionStart, replacement);
+ hasReplacement = true;
+ }
+ static const auto matcher = [](char c) { return c == '^' || c == '$'; };
+ input.removeIf(matcher);
+ if (hasReplacement) {
+ output.removeIf(matcher);
+ output.prepend("#include <QMetaObject>\n\n");
+ } else {
+ output.clear();
+ }
+ QTest::newRow(tag.data()) << input << output;
+ nextCursor = allCases.indexOf('^', nextCursor + 1);
+ }
+ }
+
+ void test()
+ {
+ QFETCH(QByteArray, input);
+ QFETCH(QByteArray, expected);
+ ConvertToMetaMethodCall factory;
+ QuickFixOperationTest({CppTestDocument::create("file.cpp", input, expected)}, &factory);
+ }
+};
+
+QObject *ConvertToMetaMethodCall::createTest() { return new ConvertToMetaMethodCallTest; }
+
+#endif // WITH_TESTS
+} // namespace
+
+void registerConvertToMetaMethodCallQuickfix()
+{
+ CppQuickFixFactory::registerFactory<ConvertToMetaMethodCall>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <converttometamethodcall.moc>
+#endif
diff --git a/src/plugins/cppeditor/quickfixes/converttometamethodcall.h b/src/plugins/cppeditor/quickfixes/converttometamethodcall.h
new file mode 100644
index 0000000000..e6c309ad13
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/converttometamethodcall.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerConvertToMetaMethodCallQuickfix();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/cppcodegenerationquickfixes.cpp b/src/plugins/cppeditor/quickfixes/cppcodegenerationquickfixes.cpp
new file mode 100644
index 0000000000..2d486e1817
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/cppcodegenerationquickfixes.cpp
@@ -0,0 +1,5072 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "cppcodegenerationquickfixes.h"
+
+#include "../cppcodestylesettings.h"
+#include "../cppeditortr.h"
+#include "../cpprefactoringchanges.h"
+#include "../insertionpointlocator.h"
+#include "cppquickfix.h"
+#include "cppquickfixhelpers.h"
+#include "cppquickfixprojectsettings.h"
+#include "cppquickfixsettings.h"
+
+#include <cplusplus/Overview.h>
+#include <cplusplus/CppRewriter.h>
+#include <projectexplorer/projecttree.h>
+#include <utils/basetreeview.h>
+#include <utils/treemodel.h>
+
+#include <QApplication>
+#include <QCheckBox>
+#include <QDialog>
+#include <QDialogButtonBox>
+#include <QHBoxLayout>
+#include <QHeaderView>
+#include <QMimeData>
+#include <QPushButton>
+#include <QProxyStyle>
+#include <QStyledItemDelegate>
+#include <QTableView>
+#include <QTreeView>
+
+#ifdef WITH_TESTS
+#include "cppquickfix_test.h"
+#include <QAbstractItemModelTester>
+#include <QtTest>
+#endif
+
+using namespace CPlusPlus;
+using namespace ProjectExplorer;
+using namespace TextEditor;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+static QStringList toStringList(const QList<Symbol *> names)
+{
+ QStringList list;
+ list.reserve(names.size());
+ for (const auto symbol : names) {
+ const Identifier *const id = symbol->identifier();
+ list << QString::fromUtf8(id->chars(), id->size());
+ }
+ return list;
+}
+
+static QList<Symbol *> getMemberFunctions(const Class *clazz)
+{
+ QList<Symbol *> memberFunctions;
+ for (auto it = clazz->memberBegin(); it != clazz->memberEnd(); ++it) {
+ Symbol *const s = *it;
+ if (!s->identifier() || !s->type())
+ continue;
+ if ((s->asDeclaration() && s->type()->asFunctionType()) || s->asFunction())
+ memberFunctions << s;
+ }
+ return memberFunctions;
+}
+
+static QString symbolAtDifferentLocation(
+ const CppQuickFixInterface &interface,
+ Symbol *symbol,
+ const CppRefactoringFilePtr &targetFile,
+ InsertionLocation targetLocation)
+{
+ QTC_ASSERT(symbol, return QString());
+ Scope *scopeAtInsertPos = targetFile->cppDocument()->scopeAt(targetLocation.line(),
+ targetLocation.column());
+
+ LookupContext cppContext(targetFile->cppDocument(), interface.snapshot());
+ ClassOrNamespace *cppCoN = cppContext.lookupType(scopeAtInsertPos);
+ if (!cppCoN)
+ cppCoN = cppContext.globalNamespace();
+ SubstitutionEnvironment env;
+ env.setContext(interface.context());
+ env.switchScope(symbol->enclosingScope());
+ UseMinimalNames q(cppCoN);
+ env.enter(&q);
+ Control *control = interface.context().bindings()->control().get();
+ Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ return oo.prettyName(LookupContext::minimalName(symbol, cppCoN, control));
+}
+
+static FullySpecifiedType typeAtDifferentLocation(
+ const CppQuickFixInterface &interface,
+ FullySpecifiedType type,
+ Scope *originalScope,
+ const CppRefactoringFilePtr &targetFile,
+ InsertionLocation targetLocation,
+ const QStringList &newNamespaceNamesAtLoc = {})
+{
+ Scope *scopeAtInsertPos = targetFile->cppDocument()->scopeAt(targetLocation.line(),
+ targetLocation.column());
+ for (const QString &nsName : newNamespaceNamesAtLoc) {
+ const QByteArray utf8Name = nsName.toUtf8();
+ Control *control = targetFile->cppDocument()->control();
+ const Name *name = control->identifier(utf8Name.data(), utf8Name.size());
+ Namespace *ns = control->newNamespace(0, name);
+ ns->setEnclosingScope(scopeAtInsertPos);
+ scopeAtInsertPos = ns;
+ }
+ LookupContext cppContext(targetFile->cppDocument(), interface.snapshot());
+ ClassOrNamespace *cppCoN = cppContext.lookupType(scopeAtInsertPos);
+ if (!cppCoN)
+ cppCoN = cppContext.globalNamespace();
+ SubstitutionEnvironment env;
+ env.setContext(interface.context());
+ env.switchScope(originalScope);
+ UseMinimalNames q(cppCoN);
+ env.enter(&q);
+ Control *control = interface.context().bindings()->control().get();
+ return rewriteType(type, &env, control);
+}
+
+static QString memberBaseName(const QString &name)
+{
+ const auto validName = [](const QString &name) {
+ return !name.isEmpty() && !name.at(0).isDigit();
+ };
+ QString baseName = name;
+
+ CppQuickFixSettings *settings = CppQuickFixProjectsSettings::getQuickFixSettings(
+ ProjectExplorer::ProjectTree::currentProject());
+ const QString &nameTemplate = settings->memberVariableNameTemplate;
+ const QString prefix = nameTemplate.left(nameTemplate.indexOf('<'));
+ const QString postfix = nameTemplate.mid(nameTemplate.lastIndexOf('>') + 1);
+ if (name.startsWith(prefix) && name.endsWith(postfix)) {
+ const QString base = name.mid(prefix.length(), name.length() - postfix.length());
+ if (validName(base))
+ return base;
+ }
+
+ // Remove leading and trailing "_"
+ while (baseName.startsWith(QLatin1Char('_')))
+ baseName.remove(0, 1);
+ while (baseName.endsWith(QLatin1Char('_')))
+ baseName.chop(1);
+ if (baseName != name && validName(baseName))
+ return baseName;
+
+ // If no leading/trailing "_": remove "m_" and "m" prefix
+ if (baseName.startsWith(QLatin1String("m_"))) {
+ baseName.remove(0, 2);
+ } else if (baseName.startsWith(QLatin1Char('m')) && baseName.length() > 1
+ && baseName.at(1).isUpper()) {
+ baseName.remove(0, 1);
+ baseName[0] = baseName.at(0).toLower();
+ }
+
+ return validName(baseName) ? baseName : name;
+}
+
+static std::optional<FullySpecifiedType> getFirstTemplateParameter(const Name *name)
+{
+ if (const QualifiedNameId *qualifiedName = name->asQualifiedNameId())
+ return getFirstTemplateParameter(qualifiedName->name());
+
+ if (const TemplateNameId *templateName = name->asTemplateNameId()) {
+ if (templateName->templateArgumentCount() > 0)
+ return templateName->templateArgumentAt(0).type();
+ }
+ return {};
+}
+
+static std::optional<FullySpecifiedType> getFirstTemplateParameter(Type *type)
+{
+ if (NamedType *namedType = type->asNamedType())
+ return getFirstTemplateParameter(namedType->name());
+
+ return {};
+}
+
+static std::optional<FullySpecifiedType> getFirstTemplateParameter(FullySpecifiedType type)
+{
+ return getFirstTemplateParameter(type.type());
+}
+
+struct ExistingGetterSetterData
+{
+ Class *clazz = nullptr;
+ Declaration *declarationSymbol = nullptr;
+ QString getterName;
+ QString setterName;
+ QString resetName;
+ QString signalName;
+ QString qPropertyName;
+ QString memberVariableName;
+ Document::Ptr doc;
+
+ int computePossibleFlags() const;
+};
+
+// FIXME: Should be a member?
+static void findExistingFunctions(ExistingGetterSetterData &existing, QStringList memberFunctionNames)
+{
+ const CppQuickFixSettings *settings = CppQuickFixProjectsSettings::getQuickFixSettings(
+ ProjectExplorer::ProjectTree::currentProject());
+ const QString lowerBaseName = memberBaseName(existing.memberVariableName).toLower();
+ const QStringList getterNames{lowerBaseName,
+ "get_" + lowerBaseName,
+ "get" + lowerBaseName,
+ "is_" + lowerBaseName,
+ "is" + lowerBaseName,
+ settings->getGetterName(lowerBaseName)};
+ const QStringList setterNames{"set_" + lowerBaseName,
+ "set" + lowerBaseName,
+ settings->getSetterName(lowerBaseName)};
+ const QStringList resetNames{"reset_" + lowerBaseName,
+ "reset" + lowerBaseName,
+ settings->getResetName(lowerBaseName)};
+ const QStringList signalNames{lowerBaseName + "_changed",
+ lowerBaseName + "changed",
+ settings->getSignalName(lowerBaseName)};
+ for (const auto &memberFunctionName : memberFunctionNames) {
+ const QString lowerName = memberFunctionName.toLower();
+ if (getterNames.contains(lowerName))
+ existing.getterName = memberFunctionName;
+ else if (setterNames.contains(lowerName))
+ existing.setterName = memberFunctionName;
+ else if (resetNames.contains(lowerName))
+ existing.resetName = memberFunctionName;
+ else if (signalNames.contains(lowerName))
+ existing.signalName = memberFunctionName;
+ }
+}
+
+static void extractNames(const CppRefactoringFilePtr &file,
+ QtPropertyDeclarationAST *qtPropertyDeclaration,
+ ExistingGetterSetterData &data)
+{
+ QtPropertyDeclarationItemListAST *it = qtPropertyDeclaration->property_declaration_item_list;
+ for (; it; it = it->next) {
+ const char *tokenString = file->tokenAt(it->value->item_name_token).spell();
+ if (!qstrcmp(tokenString, "READ")) {
+ data.getterName = file->textOf(it->value->expression);
+ } else if (!qstrcmp(tokenString, "WRITE")) {
+ data.setterName = file->textOf(it->value->expression);
+ } else if (!qstrcmp(tokenString, "RESET")) {
+ data.resetName = file->textOf(it->value->expression);
+ } else if (!qstrcmp(tokenString, "NOTIFY")) {
+ data.signalName = file->textOf(it->value->expression);
+ } else if (!qstrcmp(tokenString, "MEMBER")) {
+ data.memberVariableName = file->textOf(it->value->expression);
+ }
+ }
+}
+
+class GetterSetterRefactoringHelper
+{
+public:
+ GetterSetterRefactoringHelper(CppQuickFixOperation *operation,
+ const FilePath &filePath,
+ Class *clazz)
+ : m_operation(operation)
+ , m_changes(m_operation->snapshot())
+ , m_locator(m_changes)
+ , m_headerFile(m_changes.cppFile(filePath))
+ , m_sourceFile([&] {
+ FilePath cppFilePath = correspondingHeaderOrSource(filePath, &m_isHeaderHeaderFile);
+ if (!m_isHeaderHeaderFile || !cppFilePath.exists()) {
+ // there is no "source" file
+ return m_headerFile;
+ } else {
+ return m_changes.cppFile(cppFilePath);
+ }
+ }())
+ , m_class(clazz)
+ {}
+
+ void performGeneration(ExistingGetterSetterData data, int generationFlags);
+
+ void applyChanges()
+ {
+ const auto classLayout = {
+ InsertionPointLocator::Public,
+ InsertionPointLocator::PublicSlot,
+ InsertionPointLocator::Signals,
+ InsertionPointLocator::Protected,
+ InsertionPointLocator::ProtectedSlot,
+ InsertionPointLocator::PrivateSlot,
+ InsertionPointLocator::Private,
+ };
+ for (auto spec : classLayout) {
+ const auto iter = m_headerFileCode.find(spec);
+ if (iter != m_headerFileCode.end()) {
+ const InsertionLocation loc = headerLocationFor(spec);
+ m_headerFile->setOpenEditor(true, m_headerFile->position(loc.line(), loc.column()));
+ insertAndIndent(m_headerFile, loc, *iter);
+ }
+ }
+ if (!m_sourceFileCode.isEmpty() && m_sourceFileInsertionPoint.isValid()) {
+ m_sourceFile->setOpenEditor(true, m_sourceFile->position(
+ m_sourceFileInsertionPoint.line(),
+ m_sourceFileInsertionPoint.column()));
+ insertAndIndent(m_sourceFile, m_sourceFileInsertionPoint, m_sourceFileCode);
+ }
+
+ m_headerFile->apply(m_headerFileChangeSet);
+ m_sourceFile->apply(m_sourceFileChangeSet);
+ }
+
+ bool hasSourceFile() const { return m_headerFile != m_sourceFile; }
+
+protected:
+ void insertAndIndent(const RefactoringFilePtr &file,
+ const InsertionLocation &loc,
+ const QString &text)
+ {
+ int targetPosition = file->position(loc.line(), loc.column());
+ ChangeSet &changeSet = file == m_headerFile ? m_headerFileChangeSet : m_sourceFileChangeSet;
+ changeSet.insert(targetPosition, loc.prefix() + text + loc.suffix());
+ }
+
+ FullySpecifiedType makeConstRef(FullySpecifiedType type)
+ {
+ type.setConst(true);
+ return m_operation->currentFile()->cppDocument()->control()->referenceType(type, false);
+ }
+
+ FullySpecifiedType addConstToReference(FullySpecifiedType type)
+ {
+ if (ReferenceType *ref = type.type()->asReferenceType()) {
+ FullySpecifiedType elemType = ref->elementType();
+ if (elemType.isConst())
+ return type;
+ elemType.setConst(true);
+ return m_operation->currentFile()->cppDocument()->control()->referenceType(elemType,
+ false);
+ }
+ return type;
+ }
+
+ QString symbolAt(Symbol *symbol,
+ const CppRefactoringFilePtr &targetFile,
+ InsertionLocation targetLocation)
+ {
+ return symbolAtDifferentLocation(*m_operation, symbol, targetFile, targetLocation);
+ }
+
+ FullySpecifiedType typeAt(FullySpecifiedType type,
+ Scope *originalScope,
+ const CppRefactoringFilePtr &targetFile,
+ InsertionLocation targetLocation,
+ const QStringList &newNamespaceNamesAtLoc = {})
+ {
+ return typeAtDifferentLocation(*m_operation,
+ type,
+ originalScope,
+ targetFile,
+ targetLocation,
+ newNamespaceNamesAtLoc);
+ }
+
+ /**
+ * @brief checks if the type in the enclosing scope in the header is a value type
+ * @param type a type in the m_headerFile
+ * @param enclosingScope the enclosing scope
+ * @param customValueType if not nullptr set to true when value type comes
+ * from CppQuickFixSettings::isValueType
+ * @return true if it is a pointer, enum, integer, floating point, reference, custom value type
+ */
+ bool isValueType(FullySpecifiedType type, Scope *enclosingScope, bool *customValueType = nullptr)
+ {
+ if (customValueType)
+ *customValueType = false;
+ // a type is a value type if it is one of the following
+ const auto isTypeValueType = [](const FullySpecifiedType &t) {
+ return t->asPointerType() || t->asEnumType() || t->asIntegerType() || t->asFloatType()
+ || t->asReferenceType();
+ };
+ if (type->asNamedType()) {
+ // we need a recursive search and a lookup context
+ LookupContext context(m_headerFile->cppDocument(), m_changes.snapshot());
+ auto isValueType = [settings = m_settings,
+ &customValueType,
+ &context,
+ &isTypeValueType](const Name *name,
+ Scope *scope,
+ auto &isValueType) {
+ // maybe the type is a custom value type by name
+ if (const Identifier *id = name->identifier()) {
+ if (settings->isValueType(QString::fromUtf8(id->chars(), id->size()))) {
+ if (customValueType)
+ *customValueType = true;
+ return true;
+ }
+ }
+ // search for the type declaration
+ QList<LookupItem> localLookup = context.lookup(name, scope);
+ for (auto &&i : localLookup) {
+ if (isTypeValueType(i.type()))
+ return true;
+ if (i.type()->asNamedType()) { // check if we have to search recursively
+ const Name *newName = i.type()->asNamedType()->name();
+ Scope *newScope = i.declaration()->enclosingScope();
+ if (Matcher::match(newName, name)
+ && Matcher::match(newScope->name(), scope->name())) {
+ continue; // we have found the start location of the search
+ }
+ return isValueType(newName, newScope, isValueType);
+ }
+ return false;
+ }
+ return false;
+ };
+ // start recursion
+ return isValueType(type->asNamedType()->name(), enclosingScope, isValueType);
+ }
+ return isTypeValueType(type);
+ }
+
+ bool isValueType(Symbol *symbol, bool *customValueType = nullptr)
+ {
+ return isValueType(symbol->type(), symbol->enclosingScope(), customValueType);
+ }
+
+ void addHeaderCode(InsertionPointLocator::AccessSpec spec, QString code)
+ {
+ QString &existing = m_headerFileCode[spec];
+ existing += code;
+ if (!existing.endsWith('\n'))
+ existing += '\n';
+ }
+
+ InsertionLocation headerLocationFor(InsertionPointLocator::AccessSpec spec)
+ {
+ const auto insertionPoint = m_headerInsertionPoints.find(spec);
+ if (insertionPoint != m_headerInsertionPoints.end())
+ return *insertionPoint;
+ const InsertionLocation loc = m_locator.methodDeclarationInClass(
+ m_headerFile->filePath(), m_class, spec,
+ InsertionPointLocator::ForceAccessSpec::Yes);
+ m_headerInsertionPoints.insert(spec, loc);
+ return loc;
+ }
+
+ InsertionLocation sourceLocationFor(Symbol *symbol, QStringList *insertedNamespaces = nullptr)
+ {
+ if (m_sourceFileInsertionPoint.isValid())
+ return m_sourceFileInsertionPoint;
+ m_sourceFileInsertionPoint
+ = insertLocationForMethodDefinition(symbol,
+ false,
+ m_settings->createMissingNamespacesinCppFile()
+ ? NamespaceHandling::CreateMissing
+ : NamespaceHandling::Ignore,
+ m_changes,
+ m_sourceFile->filePath(),
+ insertedNamespaces);
+ if (m_settings->addUsingNamespaceinCppFile()) {
+ // check if we have to insert a using namespace ...
+ auto requiredNamespaces = getNamespaceNames(
+ symbol->asClass() ? symbol : symbol->enclosingClass());
+ NSCheckerVisitor visitor(m_sourceFile.get(),
+ requiredNamespaces,
+ m_sourceFile->position(m_sourceFileInsertionPoint.line(),
+ m_sourceFileInsertionPoint.column()));
+ visitor.accept(m_sourceFile->cppDocument()->translationUnit()->ast());
+ if (insertedNamespaces)
+ insertedNamespaces->clear();
+ if (auto rns = visitor.remainingNamespaces(); !rns.empty()) {
+ QString ns = "using namespace ";
+ for (auto &n : rns) {
+ if (!n.isEmpty()) { // we have to ignore unnamed namespaces
+ ns += n;
+ ns += "::";
+ if (insertedNamespaces)
+ insertedNamespaces->append(n);
+ }
+ }
+ ns.resize(ns.size() - 2); // remove last '::'
+ ns += ";\n";
+ const auto &loc = m_sourceFileInsertionPoint;
+ m_sourceFileInsertionPoint = InsertionLocation(loc.filePath(),
+ loc.prefix() + ns,
+ loc.suffix(),
+ loc.line(),
+ loc.column());
+ }
+ }
+ return m_sourceFileInsertionPoint;
+ }
+
+ void addSourceFileCode(QString code)
+ {
+ while (!m_sourceFileCode.isEmpty() && !m_sourceFileCode.endsWith("\n\n"))
+ m_sourceFileCode += '\n';
+ m_sourceFileCode += code;
+ }
+
+protected:
+ CppQuickFixOperation *const m_operation;
+ const CppRefactoringChanges m_changes;
+ const InsertionPointLocator m_locator;
+ const CppRefactoringFilePtr m_headerFile;
+ bool m_isHeaderHeaderFile = false; // the "header" (where the class is defined) can be a source file
+ const CppRefactoringFilePtr m_sourceFile;
+ CppQuickFixSettings *const m_settings = CppQuickFixProjectsSettings::getQuickFixSettings(
+ ProjectTree::currentProject());
+ Class *const m_class;
+
+private:
+ ChangeSet m_headerFileChangeSet;
+ ChangeSet m_sourceFileChangeSet;
+ QMap<InsertionPointLocator::AccessSpec, InsertionLocation> m_headerInsertionPoints;
+ InsertionLocation m_sourceFileInsertionPoint;
+ QString m_sourceFileCode;
+ QMap<InsertionPointLocator::AccessSpec, QString> m_headerFileCode;
+};
+
+struct ParentClassConstructorInfo;
+
+class ConstructorMemberInfo
+{
+public:
+ ConstructorMemberInfo(const QString &name, Symbol *symbol, int numberOfMember)
+ : memberVariableName(name)
+ , parameterName(memberBaseName(name))
+ , symbol(symbol)
+ , type(symbol->type())
+ , numberOfMember(numberOfMember)
+ {}
+ ConstructorMemberInfo(const QString &memberName,
+ const QString &paramName,
+ const QString &defaultValue,
+ Symbol *symbol,
+ const ParentClassConstructorInfo *parentClassConstructor)
+ : parentClassConstructor(parentClassConstructor)
+ , memberVariableName(memberName)
+ , parameterName(paramName)
+ , defaultValue(defaultValue)
+ , init(defaultValue.isEmpty())
+ , symbol(symbol)
+ , type(symbol->type())
+ {}
+ const ParentClassConstructorInfo *parentClassConstructor = nullptr;
+ QString memberVariableName;
+ QString parameterName;
+ QString defaultValue;
+ bool init = true;
+ bool customValueType; // for the generation later
+ Symbol *symbol; // for the right type later
+ FullySpecifiedType type;
+ int numberOfMember; // first member, second member, ...
+};
+
+class ConstructorParams : public QAbstractTableModel
+{
+ Q_OBJECT
+ std::list<ConstructorMemberInfo> candidates;
+ std::vector<ConstructorMemberInfo *> infos;
+
+ void validateOrder()
+ {
+ // parameters with default values must be at the end
+ bool foundWithDefault = false;
+ for (auto info : infos) {
+ if (info->init) {
+ if (foundWithDefault && info->defaultValue.isEmpty()) {
+ emit validOrder(false);
+ return;
+ }
+ foundWithDefault |= !info->defaultValue.isEmpty();
+ }
+ }
+ emit validOrder(true);
+ }
+
+public:
+ enum Column { ShouldInitColumn, MemberNameColumn, ParameterNameColumn, DefaultValueColumn };
+ template<typename... _Args>
+ void emplaceBackParameter(_Args &&...__args)
+ {
+ candidates.emplace_back(std::forward<_Args>(__args)...);
+ infos.push_back(&candidates.back());
+ }
+ const std::vector<ConstructorMemberInfo *> &getInfos() const { return infos; }
+ void addRow(ConstructorMemberInfo *info)
+ {
+ beginInsertRows({}, rowCount(), rowCount());
+ infos.push_back(info);
+ endInsertRows();
+ validateOrder();
+ }
+ void removeRow(ConstructorMemberInfo *info)
+ {
+ for (auto iter = infos.begin(); iter != infos.end(); ++iter) {
+ if (*iter == info) {
+ const auto index = iter - infos.begin();
+ beginRemoveRows({}, index, index);
+ infos.erase(iter);
+ endRemoveRows();
+ validateOrder();
+ return;
+ }
+ }
+ }
+
+ int selectedCount() const
+ {
+ return Utils::count(infos, [](const ConstructorMemberInfo *mi) {
+ return mi->init && !mi->parentClassConstructor;
+ });
+ }
+ int memberCount() const
+ {
+ return Utils::count(infos, [](const ConstructorMemberInfo *mi) {
+ return !mi->parentClassConstructor;
+ });
+ }
+
+ int rowCount(const QModelIndex & /*parent*/ = {}) const override { return int(infos.size()); }
+ int columnCount(const QModelIndex & /*parent*/ = {}) const override { return 4; }
+ QVariant data(const QModelIndex &index, int role) const override
+ {
+ if (index.row() < 0 || index.row() >= rowCount())
+ return {};
+ if (role == Qt::CheckStateRole && index.column() == ShouldInitColumn
+ && !infos[index.row()]->parentClassConstructor)
+ return infos[index.row()]->init ? Qt::Checked : Qt::Unchecked;
+ if (role == Qt::DisplayRole && index.column() == MemberNameColumn)
+ return infos[index.row()]->memberVariableName;
+ if ((role == Qt::DisplayRole || role == Qt::EditRole)
+ && index.column() == ParameterNameColumn)
+ return infos[index.row()]->parameterName;
+ if ((role == Qt::DisplayRole || role == Qt::EditRole)
+ && index.column() == DefaultValueColumn)
+ return infos[index.row()]->defaultValue;
+ if ((role == Qt::ToolTipRole) && index.column() > 0)
+ return Overview{}.prettyType(infos[index.row()]->symbol->type());
+ return {};
+ }
+ bool setData(const QModelIndex &index, const QVariant &value, int role) override
+ {
+ if (index.column() == ShouldInitColumn && role == Qt::CheckStateRole) {
+ if (infos[index.row()]->parentClassConstructor)
+ return false;
+ infos[index.row()]->init = value.toInt() == Qt::Checked;
+ emit dataChanged(this->index(index.row(), 0), this->index(index.row(), columnCount()));
+ validateOrder();
+ return true;
+ }
+ if (index.column() == ParameterNameColumn && role == Qt::EditRole) {
+ infos[index.row()]->parameterName = value.toString();
+ return true;
+ }
+ if (index.column() == DefaultValueColumn && role == Qt::EditRole) {
+ infos[index.row()]->defaultValue = value.toString();
+ validateOrder();
+ return true;
+ }
+ return false;
+ }
+ Qt::DropActions supportedDropActions() const override { return Qt::MoveAction; }
+ Qt::ItemFlags flags(const QModelIndex &index) const override
+ {
+ if (!index.isValid())
+ return Qt::ItemIsSelectable | Qt::ItemIsDropEnabled;
+
+ Qt::ItemFlags f{};
+ if (infos[index.row()]->init) {
+ f |= Qt::ItemIsDragEnabled;
+ f |= Qt::ItemIsSelectable;
+ }
+
+ if (index.column() == ShouldInitColumn && !infos[index.row()]->parentClassConstructor)
+ return f | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
+ if (!infos[index.row()]->init)
+ return f;
+ if (index.column() == MemberNameColumn)
+ return f | Qt::ItemIsEnabled;
+ if (index.column() == ParameterNameColumn || index.column() == DefaultValueColumn)
+ return f | Qt::ItemIsEnabled | Qt::ItemIsEditable;
+ return {};
+ }
+
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const override
+ {
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+ switch (section) {
+ case ShouldInitColumn:
+ return Tr::tr("Initialize in Constructor");
+ case MemberNameColumn:
+ return Tr::tr("Member Name");
+ case ParameterNameColumn:
+ return Tr::tr("Parameter Name");
+ case DefaultValueColumn:
+ return Tr::tr("Default Value");
+ }
+ }
+ return {};
+ }
+ bool dropMimeData(const QMimeData *data,
+ Qt::DropAction /*action*/,
+ int row,
+ int /*column*/,
+ const QModelIndex & /*parent*/) override
+ {
+ if (row == -1)
+ row = rowCount();
+ bool ok;
+ int sourceRow = data->data("application/x-qabstractitemmodeldatalist").toInt(&ok);
+ if (ok) {
+ if (sourceRow == row || row == sourceRow + 1)
+ return false;
+ beginMoveRows({}, sourceRow, sourceRow, {}, row);
+ infos.insert(infos.begin() + row, infos.at(sourceRow));
+ if (row < sourceRow)
+ ++sourceRow;
+ infos.erase(infos.begin() + sourceRow);
+ validateOrder();
+ return true;
+ }
+ return false;
+ }
+
+ QMimeData *mimeData(const QModelIndexList &indexes) const override
+ {
+ for (const auto &i : indexes) {
+ if (!i.isValid())
+ continue;
+ auto data = new QMimeData();
+ data->setData("application/x-qabstractitemmodeldatalist",
+ QString::number(i.row()).toLatin1());
+ return data;
+ }
+ return nullptr;
+ }
+
+ class TableViewStyle : public QProxyStyle
+ {
+ public:
+ TableViewStyle(QStyle *style)
+ : QProxyStyle(style)
+ {}
+
+ void drawPrimitive(PrimitiveElement element,
+ const QStyleOption *option,
+ QPainter *painter,
+ const QWidget *widget) const override
+ {
+ if (element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull()) {
+ QStyleOption opt(*option);
+ opt.rect.setLeft(0);
+ if (widget)
+ opt.rect.setRight(widget->width());
+ QProxyStyle::drawPrimitive(element, &opt, painter, widget);
+ return;
+ }
+ QProxyStyle::drawPrimitive(element, option, painter, widget);
+ }
+ };
+signals:
+ void validOrder(bool valid);
+};
+
+class TopMarginDelegate : public QStyledItemDelegate
+{
+public:
+ TopMarginDelegate(QObject *parent = nullptr)
+ : QStyledItemDelegate(parent)
+ {}
+
+ void paint(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override
+ {
+ Q_ASSERT(index.isValid());
+ QStyleOptionViewItem opt = option;
+ initStyleOption(&opt, index);
+ const QWidget *widget = option.widget;
+ QStyle *style = widget ? widget->style() : QApplication::style();
+ if (opt.rect.height() > 20)
+ opt.rect.adjust(0, 5, 0, 0);
+ style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
+ }
+};
+
+struct ParentClassConstructorParameter : public ConstructorMemberInfo
+{
+ QString originalDefaultValue;
+ QString declaration; // displayed in the treeView
+ ParentClassConstructorParameter(const QString &name,
+ const QString &defaultValue,
+ Symbol *symbol,
+ const ParentClassConstructorInfo *parentClassConstructor);
+
+ ParentClassConstructorParameter(const ParentClassConstructorParameter &) = delete;
+ ParentClassConstructorParameter(ParentClassConstructorParameter &&) = default;
+};
+
+struct ParentClassConstructorInfo
+{
+ ParentClassConstructorInfo(const QString &name, ConstructorParams &model)
+ : className(name)
+ , model(model)
+ {}
+ bool useInConstructor = false;
+ const QString className;
+ QString declaration;
+ std::vector<ParentClassConstructorParameter> parameters;
+ ConstructorParams &model;
+
+ ParentClassConstructorInfo(const ParentClassConstructorInfo &) = delete;
+ ParentClassConstructorInfo(ParentClassConstructorInfo &&) = default;
+
+ void addParameter(ParentClassConstructorParameter &param) { model.addRow(&param); }
+ void removeParameter(ParentClassConstructorParameter &param) { model.removeRow(&param); }
+ void removeAllParameters()
+ {
+ for (auto &param : parameters)
+ model.removeRow(&param);
+ }
+};
+
+ParentClassConstructorParameter::ParentClassConstructorParameter(
+ const QString &name,
+ const QString &defaultValue,
+ Symbol *symbol,
+ const ParentClassConstructorInfo *parentClassConstructor)
+ : ConstructorMemberInfo(parentClassConstructor->className + "::" + name,
+ name,
+ defaultValue,
+ symbol,
+ parentClassConstructor)
+ , originalDefaultValue(defaultValue)
+ , declaration(Overview{}.prettyType(symbol->type(), name)
+ + (defaultValue.isEmpty() ? QString{} : " = " + defaultValue))
+{}
+
+using ParentClassConstructors = std::vector<ParentClassConstructorInfo>;
+
+class ParentClassesModel : public QAbstractItemModel
+{
+ ParentClassConstructors &constructors;
+
+public:
+ ParentClassesModel(QObject *parent, ParentClassConstructors &constructors)
+ : QAbstractItemModel(parent)
+ , constructors(constructors)
+ {}
+ QModelIndex index(int row, int column, const QModelIndex &parent = {}) const override
+ {
+ if (!parent.isValid())
+ return createIndex(row, column, nullptr);
+ if (parent.internalPointer())
+ return {};
+ auto index = createIndex(row, column, &constructors.at(parent.row()));
+ return index;
+ }
+ QModelIndex parent(const QModelIndex &index) const override
+ {
+ if (!index.isValid())
+ return {};
+ auto *parent = static_cast<ParentClassConstructorInfo *>(index.internalPointer());
+ if (!parent)
+ return {};
+ int i = 0;
+ for (const auto &info : constructors) {
+ if (&info == parent)
+ return createIndex(i, 0, nullptr);
+ ++i;
+ }
+ return {};
+ }
+ int rowCount(const QModelIndex &parent = {}) const override
+ {
+ if (!parent.isValid())
+ return static_cast<int>(constructors.size());
+ auto info = static_cast<ParentClassConstructorInfo *>(parent.internalPointer());
+ if (!info)
+ return static_cast<int>(constructors.at(parent.row()).parameters.size());
+ return 0;
+ }
+ int columnCount(const QModelIndex & /*parent*/ = {}) const override { return 1; }
+ QVariant data(const QModelIndex &index, int role) const override
+ {
+ if (!index.isValid())
+ return {};
+ auto info = static_cast<ParentClassConstructorInfo *>(index.internalPointer());
+
+ if (info) {
+ const auto &parameter = info->parameters.at(index.row());
+ if (role == Qt::CheckStateRole)
+ return parameter.init ? Qt::Checked : Qt::Unchecked;
+ if (role == Qt::DisplayRole)
+ return parameter.declaration;
+ return {};
+ }
+ const auto &constructor = constructors.at(index.row());
+ if (role == Qt::CheckStateRole)
+ return constructor.useInConstructor ? Qt::PartiallyChecked : Qt::Unchecked;
+ if (role == Qt::DisplayRole)
+ return constructor.declaration;
+
+ // Highlight the selected items
+ if (role == Qt::FontRole && constructor.useInConstructor) {
+ QFont font = QApplication::font();
+ font.setBold(true);
+ return font;
+ }
+ // Create a margin between sets of constructors for base classes
+ if (role == Qt::SizeHintRole && index.row() > 0
+ && constructor.className != constructors.at(index.row() - 1).className) {
+ return QSize(-1, 25);
+ }
+ return {};
+ }
+ bool setData(const QModelIndex &index, const QVariant &value, int /*role*/) override
+ {
+ if (index.isValid() && index.column() == 0) {
+ auto info = static_cast<ParentClassConstructorInfo *>(index.internalPointer());
+ if (info) {
+ const bool nowUse = value.toBool();
+ auto &param = info->parameters.at(index.row());
+ param.init = nowUse;
+ if (nowUse)
+ info->addParameter(param);
+ else
+ info->removeParameter(param);
+ return true;
+ }
+ auto &newConstructor = constructors.at(index.row());
+ // You have to select a base class constructor
+ if (newConstructor.useInConstructor)
+ return false;
+ auto c = std::find_if(constructors.begin(), constructors.end(), [&](const auto &c) {
+ return c.className == newConstructor.className && c.useInConstructor;
+ });
+ QTC_ASSERT(c == constructors.end(), return false;);
+ c->useInConstructor = false;
+ newConstructor.useInConstructor = true;
+ emit dataChanged(this->index(index.row(), 0), this->index(index.row(), columnCount()));
+ auto parentIndex = this->index(index.row(), 0);
+ emit dataChanged(this->index(0, 0, parentIndex),
+ this->index(rowCount(parentIndex), columnCount()));
+ const int oldIndex = c - constructors.begin();
+ emit dataChanged(this->index(oldIndex, 0), this->index(oldIndex, columnCount()));
+ parentIndex = this->index(oldIndex, 0);
+ emit dataChanged(this->index(0, 0, parentIndex),
+ this->index(rowCount(parentIndex), columnCount()));
+ // update other table
+ c->removeAllParameters();
+ for (auto &p : newConstructor.parameters)
+ if (p.init)
+ newConstructor.addParameter(p);
+ return true;
+ }
+ return false;
+ }
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const override
+ {
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+ switch (section) {
+ case 0:
+ return Tr::tr("Base Class Constructors");
+ }
+ }
+ return {};
+ }
+ Qt::ItemFlags flags(const QModelIndex &index) const override
+ {
+ if (index.isValid()) {
+ Qt::ItemFlags f;
+ auto info = static_cast<ParentClassConstructorInfo *>(index.internalPointer());
+ if (!info || info->useInConstructor) {
+ f |= Qt::ItemIsEnabled;
+ }
+ f |= Qt::ItemIsUserCheckable;
+
+ return f;
+ }
+ return {};
+ }
+};
+
+class GenerateConstructorDialog : public QDialog
+{
+public:
+ GenerateConstructorDialog(ConstructorParams *constructorParamsModel,
+ ParentClassConstructors &constructors)
+ {
+ setWindowTitle(Tr::tr("Constructor"));
+
+ const auto treeModel = new ParentClassesModel(this, constructors);
+ const auto treeView = new QTreeView(this);
+ treeView->setModel(treeModel);
+ treeView->setItemDelegate(new TopMarginDelegate(this));
+ treeView->expandAll();
+
+ const auto view = new QTableView(this);
+ view->setModel(constructorParamsModel);
+ int optimalWidth = 0;
+ for (int i = 0; i < constructorParamsModel->columnCount(QModelIndex{}); ++i) {
+ view->resizeColumnToContents(i);
+ optimalWidth += view->columnWidth(i);
+ }
+ view->resizeRowsToContents();
+ view->verticalHeader()->setDefaultSectionSize(view->rowHeight(0));
+ view->setSelectionBehavior(QAbstractItemView::SelectRows);
+ view->setSelectionMode(QAbstractItemView::SingleSelection);
+ view->setDragEnabled(true);
+ view->setDropIndicatorShown(true);
+ view->setDefaultDropAction(Qt::MoveAction);
+ view->setDragDropMode(QAbstractItemView::InternalMove);
+ view->setDragDropOverwriteMode(false);
+ view->horizontalHeader()->setStretchLastSection(true);
+ view->setStyle(new ConstructorParams::TableViewStyle(view->style()));
+
+ const auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+ connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+
+ const auto errorLabel = new QLabel(
+ Tr::tr("Parameters without default value must come before parameters with default value."));
+ errorLabel->setStyleSheet("color: #ff0000");
+ errorLabel->setVisible(false);
+ QSizePolicy labelSizePolicy = errorLabel->sizePolicy();
+ labelSizePolicy.setRetainSizeWhenHidden(true);
+ errorLabel->setSizePolicy(labelSizePolicy);
+ connect(constructorParamsModel, &ConstructorParams::validOrder, this,
+ [errorLabel, button = buttonBox->button(QDialogButtonBox::Ok)](bool valid) {
+ button->setEnabled(valid);
+ errorLabel->setVisible(!valid);
+ });
+
+ // setup select all/none checkbox
+ QCheckBox *const checkBox = new QCheckBox(Tr::tr("Initialize all members"));
+ checkBox->setChecked(true);
+ connect(checkBox, &QCheckBox::stateChanged, this,
+ [model = constructorParamsModel](int state) {
+ if (state != Qt::PartiallyChecked) {
+ for (int i = 0; i < model->rowCount(); ++i)
+ model->setData(model->index(i, ConstructorParams::ShouldInitColumn),
+ state,
+ Qt::CheckStateRole);
+ }
+ });
+ connect(checkBox, &QCheckBox::clicked, this, [checkBox] {
+ if (checkBox->checkState() == Qt::PartiallyChecked)
+ checkBox->setCheckState(Qt::Checked);
+ });
+ connect(constructorParamsModel,
+ &QAbstractItemModel::dataChanged,
+ this,
+ [model = constructorParamsModel, checkBox] {
+ const auto state = [model, selectedCount = model->selectedCount()]() {
+ if (selectedCount == 0)
+ return Qt::Unchecked;
+ if (static_cast<int>(model->memberCount() == selectedCount))
+ return Qt::Checked;
+ return Qt::PartiallyChecked;
+ }();
+ checkBox->setCheckState(state);
+ });
+
+ using A = InsertionPointLocator::AccessSpec;
+ auto accessCombo = new QComboBox;
+ connect(accessCombo, &QComboBox::currentIndexChanged, this, [this, accessCombo] {
+ const auto data = accessCombo->currentData();
+ m_accessSpec = static_cast<A>(data.toInt());
+ });
+ for (auto a : {A::Public, A::Protected, A::Private})
+ accessCombo->addItem(InsertionPointLocator::accessSpecToString(a), a);
+ const auto row = new QHBoxLayout();
+ row->addWidget(new QLabel(Tr::tr("Access") + ":"));
+ row->addWidget(accessCombo);
+ row->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum));
+
+ const auto mainLayout = new QVBoxLayout(this);
+ mainLayout->addWidget(
+ new QLabel(Tr::tr("Select the members to be initialized in the constructor.\n"
+ "Use drag and drop to change the order of the parameters.")));
+ mainLayout->addLayout(row);
+ mainLayout->addWidget(checkBox);
+ mainLayout->addWidget(view);
+ mainLayout->addWidget(treeView);
+ mainLayout->addWidget(errorLabel);
+ mainLayout->addWidget(buttonBox);
+ int left, right;
+ mainLayout->getContentsMargins(&left, nullptr, &right, nullptr);
+ optimalWidth += left + right;
+ resize(optimalWidth, mainLayout->sizeHint().height());
+ }
+
+ InsertionPointLocator::AccessSpec accessSpec() const { return m_accessSpec; }
+
+private:
+ InsertionPointLocator::AccessSpec m_accessSpec;
+};
+
+class MemberInfo
+{
+public:
+ MemberInfo(ExistingGetterSetterData data, int possibleFlags)
+ : data(data)
+ , possibleFlags(possibleFlags)
+ {}
+
+ ExistingGetterSetterData data;
+ int possibleFlags;
+ int requestedFlags = 0;
+};
+using GetterSetterCandidates = std::vector<MemberInfo>;
+
+class GenerateConstructorOperation : public CppQuickFixOperation
+{
+public:
+ GenerateConstructorOperation(const CppQuickFixInterface &interface)
+ : CppQuickFixOperation(interface)
+ {
+ setDescription(Tr::tr("Generate Constructor"));
+
+ m_classAST = astForClassOperations(interface);
+ if (!m_classAST)
+ return;
+ Class *const theClass = m_classAST->symbol;
+ if (!theClass)
+ return;
+
+ // Go through all members and find member variable declarations
+ int memberCounter = 0;
+ for (auto it = theClass->memberBegin(); it != theClass->memberEnd(); ++it) {
+ Symbol *const s = *it;
+ if (!s->identifier() || !s->type() || s->type().isTypedef())
+ continue;
+ if ((s->asDeclaration() && s->type()->asFunctionType()) || s->asFunction())
+ continue;
+ if (s->asDeclaration() && (s->isPrivate() || s->isProtected()) && !s->isStatic()) {
+ const auto name = QString::fromUtf8(s->identifier()->chars(),
+ s->identifier()->size());
+ parameterModel.emplaceBackParameter(name, s, memberCounter++);
+ }
+ }
+ Overview o = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ o.showArgumentNames = true;
+ o.showReturnTypes = true;
+ o.showDefaultArguments = true;
+ o.showTemplateParameters = true;
+ o.showFunctionSignatures = true;
+ LookupContext context(currentFile()->cppDocument(), interface.snapshot());
+ for (BaseClass *bc : theClass->baseClasses()) {
+ const QString className = o.prettyName(bc->name());
+
+ ClassOrNamespace *localLookupType = context.lookupType(bc);
+ QList<LookupItem> localLookup = localLookupType->lookup(bc->name());
+ for (auto &li : localLookup) {
+ Symbol *d = li.declaration();
+ if (!d->asClass())
+ continue;
+ for (auto i = d->asClass()->memberBegin(); i != d->asClass()->memberEnd(); ++i) {
+ Symbol *s = *i;
+ if (s->isProtected() || s->isPublic()) {
+ if (s->name()->match(d->name())) {
+ // we have found a constructor
+ Function *func = s->type().type()->asFunctionType();
+ if (!func)
+ continue;
+ const bool isFirst = parentClassConstructors.empty()
+ || parentClassConstructors.back().className
+ != className;
+ parentClassConstructors.emplace_back(className, parameterModel);
+ ParentClassConstructorInfo &constructor = parentClassConstructors.back();
+ constructor.declaration = className + o.prettyType(func->type());
+ constructor.declaration.replace("std::__1::__get_nullptr_t()",
+ "nullptr");
+ constructor.useInConstructor = isFirst;
+ for (auto arg = func->memberBegin(); arg != func->memberEnd(); ++arg) {
+ Symbol *param = *arg;
+ Argument *argument = param->asArgument();
+ if (!argument) // can also be a block
+ continue;
+ const QString name = o.prettyName(param->name());
+ const StringLiteral *ini = argument->initializer();
+ QString defaultValue;
+ if (ini)
+ defaultValue = QString::fromUtf8(ini->chars(), ini->size())
+ .replace("std::__1::__get_nullptr_t()",
+ "nullptr");
+ constructor.parameters.emplace_back(name,
+ defaultValue,
+ param,
+ &constructor);
+ // do not show constructors like QObject(QObjectPrivate & dd, ...)
+ ReferenceType *ref = param->type()->asReferenceType();
+ if (ref && name == "dd") {
+ auto type = o.prettyType(ref->elementType());
+ if (type.startsWith("Q") && type.endsWith("Private")) {
+ parentClassConstructors.pop_back();
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // add params to parameter lists
+ for (auto &c : parentClassConstructors)
+ if (c.useInConstructor)
+ for (auto &p : c.parameters)
+ if (p.init)
+ c.addParameter(p);
+ }
+
+ bool isApplicable() const
+ {
+ return parameterModel.rowCount() > 0
+ || Utils::anyOf(parentClassConstructors,
+ [](const auto &parent) { return !parent.parameters.empty(); });
+ }
+
+ void setTest(bool isTest = true) { m_test = isTest; }
+
+private:
+ void perform() override
+ {
+ auto infos = parameterModel.getInfos();
+
+ InsertionPointLocator::AccessSpec accessSpec = InsertionPointLocator::Public;
+ if (!m_test) {
+ GenerateConstructorDialog dlg(&parameterModel, parentClassConstructors);
+ if (dlg.exec() == QDialog::Rejected)
+ return;
+ accessSpec = dlg.accessSpec();
+ infos = parameterModel.getInfos();
+ } else {
+#ifdef WITH_TESTS
+ ParentClassesModel model(nullptr, parentClassConstructors);
+ QAbstractItemModelTester tester(&model);
+#endif
+ if (infos.size() >= 3) {
+ // if we are testing and have 3 or more members => change the order
+ // move first element to the back
+ infos.push_back(infos[0]);
+ infos.erase(infos.begin());
+ }
+ for (auto info : infos) {
+ if (info->memberVariableName.startsWith("di_"))
+ info->defaultValue = "42";
+ }
+ for (auto &c : parentClassConstructors) {
+ if (c.useInConstructor) {
+ for (auto &p : c.parameters) {
+ if (!p.init && p.parameterName.startsWith("use_")) {
+ infos.push_back(&p);
+ p.init = true;
+ }
+ }
+ }
+ }
+ }
+ if (infos.empty())
+ return;
+ struct GenerateConstructorRefactoringHelper : public GetterSetterRefactoringHelper
+ {
+ const ClassSpecifierAST *m_classAST;
+ InsertionPointLocator::AccessSpec m_accessSpec;
+ GenerateConstructorRefactoringHelper(CppQuickFixOperation *operation,
+ const FilePath &filePath,
+ Class *clazz,
+ const ClassSpecifierAST *classAST,
+ InsertionPointLocator::AccessSpec accessSpec)
+ : GetterSetterRefactoringHelper(operation, filePath, clazz)
+ , m_classAST(classAST)
+ , m_accessSpec(accessSpec)
+ {}
+ void generateConstructor(std::vector<ConstructorMemberInfo *> members,
+ const ParentClassConstructors &parentClassConstructors)
+ {
+ auto constructorLocation = m_settings->determineSetterLocation(int(members.size()));
+ if (constructorLocation == CppQuickFixSettings::FunctionLocation::CppFile
+ && !hasSourceFile())
+ constructorLocation = CppQuickFixSettings::FunctionLocation::OutsideClass;
+
+ Overview overview = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ overview.showTemplateParameters = true;
+
+ InsertionLocation implLoc;
+ QString implCode;
+ CppRefactoringFilePtr implFile;
+ QString className = overview.prettyName(m_class->name());
+ QStringList insertedNamespaces;
+ if (constructorLocation == CppQuickFixSettings::FunctionLocation::CppFile) {
+ implLoc = sourceLocationFor(m_class, &insertedNamespaces);
+ implFile = m_sourceFile;
+ if (m_settings->rewriteTypesinCppFile())
+ implCode = symbolAt(m_class, m_sourceFile, implLoc);
+ else
+ implCode = className;
+ implCode += "::" + className + "(";
+ } else if (constructorLocation
+ == CppQuickFixSettings::FunctionLocation::OutsideClass) {
+ implLoc = insertLocationForMethodDefinition(m_class,
+ false,
+ NamespaceHandling::Ignore,
+ m_changes,
+ m_headerFile->filePath(),
+ &insertedNamespaces);
+ implFile = m_headerFile;
+ implCode = symbolAt(m_class, m_headerFile, implLoc);
+ implCode += "::" + className + "(";
+ }
+
+ QString inClassDeclaration = overview.prettyName(m_class->name()) + "(";
+ QString constructorBody = members.empty() ? QString(") {}") : QString(") : ");
+ for (auto &member : members) {
+ if (isValueType(member->symbol, &member->customValueType))
+ member->type.setConst(false);
+ else
+ member->type = makeConstRef(member->type);
+
+ inClassDeclaration += overview.prettyType(member->type, member->parameterName);
+ if (!member->defaultValue.isEmpty())
+ inClassDeclaration += " = " + member->defaultValue;
+ inClassDeclaration += ", ";
+ if (implFile) {
+ FullySpecifiedType type = typeAt(member->type,
+ m_class,
+ implFile,
+ implLoc,
+ insertedNamespaces);
+ implCode += overview.prettyType(type, member->parameterName) + ", ";
+ }
+ }
+ Utils::sort(members, &ConstructorMemberInfo::numberOfMember);
+ // first, do the base classes
+ for (const auto &parent : parentClassConstructors) {
+ if (!parent.useInConstructor)
+ continue;
+ // Check if we really need a constructor
+ if (Utils::anyOf(parent.parameters, [](const auto &param) {
+ return param.init || param.originalDefaultValue.isEmpty();
+ })) {
+ int defaultAtEndCount = 0;
+ for (auto i = parent.parameters.crbegin(); i != parent.parameters.crend();
+ ++i) {
+ if (i->init || i->originalDefaultValue.isEmpty())
+ break;
+ ++defaultAtEndCount;
+ }
+ const int numberOfParameters = static_cast<int>(parent.parameters.size())
+ - defaultAtEndCount;
+ constructorBody += parent.className + "(";
+ int counter = 0;
+ for (const auto &param : parent.parameters) {
+ if (++counter > numberOfParameters)
+ break;
+ if (param.init) {
+ if (param.customValueType)
+ constructorBody += "std::move(" + param.parameterName + ')';
+ else
+ constructorBody += param.parameterName;
+ } else if (!param.originalDefaultValue.isEmpty())
+ constructorBody += param.originalDefaultValue;
+ else
+ constructorBody += "/* insert value */";
+ constructorBody += ", ";
+ }
+ constructorBody.resize(constructorBody.length() - 2);
+ constructorBody += "),\n";
+ }
+ }
+ for (auto &member : members) {
+ if (member->parentClassConstructor)
+ continue;
+ QString param = member->parameterName;
+ if (member->customValueType)
+ param = "std::move(" + member->parameterName + ')';
+ constructorBody += member->memberVariableName + '(' + param + "),\n";
+ }
+ if (!members.empty()) {
+ inClassDeclaration.resize(inClassDeclaration.length() - 2);
+ constructorBody.remove(constructorBody.length() - 2, 1); // ..),\n => ..)\n
+ constructorBody += "{}";
+ if (!implCode.isEmpty())
+ implCode.resize(implCode.length() - 2);
+ }
+ implCode += constructorBody;
+
+ if (constructorLocation == CppQuickFixSettings::FunctionLocation::InsideClass)
+ inClassDeclaration += constructorBody;
+ else
+ inClassDeclaration += QLatin1String(");");
+
+ TranslationUnit *tu = m_headerFile->cppDocument()->translationUnit();
+ insertAndIndent(m_headerFile,
+ m_locator.constructorDeclarationInClass(tu,
+ m_classAST,
+ m_accessSpec,
+ int(members.size())),
+ inClassDeclaration);
+
+ if (constructorLocation == CppQuickFixSettings::FunctionLocation::CppFile) {
+ addSourceFileCode(implCode);
+ } else if (constructorLocation
+ == CppQuickFixSettings::FunctionLocation::OutsideClass) {
+ if (m_isHeaderHeaderFile)
+ implCode.prepend("inline ");
+ insertAndIndent(m_headerFile, implLoc, implCode);
+ }
+ }
+ };
+ GenerateConstructorRefactoringHelper helper(this,
+ currentFile()->filePath(),
+ m_classAST->symbol,
+ m_classAST,
+ accessSpec);
+
+ auto members = Utils::filtered(infos, [](const auto mi) {
+ return mi->init || mi->parentClassConstructor;
+ });
+ helper.generateConstructor(std::move(members), parentClassConstructors);
+ helper.applyChanges();
+ }
+
+ ConstructorParams parameterModel;
+ ParentClassConstructors parentClassConstructors;
+ const ClassSpecifierAST *m_classAST = nullptr;
+ bool m_test = false;
+};
+
+class GenerateGetterSetterOp : public CppQuickFixOperation
+{
+public:
+ enum GenerateFlag {
+ GenerateGetter = 1 << 0,
+ GenerateSetter = 1 << 1,
+ GenerateSignal = 1 << 2,
+ GenerateMemberVariable = 1 << 3,
+ GenerateReset = 1 << 4,
+ GenerateProperty = 1 << 5,
+ GenerateConstantProperty = 1 << 6,
+ HaveExistingQProperty = 1 << 7,
+ Invalid = -1,
+ };
+
+ GenerateGetterSetterOp(const CppQuickFixInterface &interface,
+ ExistingGetterSetterData data,
+ int generateFlags,
+ int priority,
+ const QString &description)
+ : CppQuickFixOperation(interface)
+ , m_generateFlags(generateFlags)
+ , m_data(data)
+ {
+ setDescription(description);
+ setPriority(priority);
+ }
+
+ static void generateQuickFixes(QuickFixOperations &results,
+ const CppQuickFixInterface &interface,
+ const ExistingGetterSetterData &data,
+ const int possibleFlags)
+ {
+ // flags can have the value HaveExistingQProperty or a combination of all other values
+ // of the enum 'GenerateFlag'
+ int p = 0;
+ if (possibleFlags & HaveExistingQProperty) {
+ const QString desc = Tr::tr("Generate Missing Q_PROPERTY Members");
+ results << new GenerateGetterSetterOp(interface, data, possibleFlags, ++p, desc);
+ } else {
+ if (possibleFlags & GenerateSetter) {
+ const QString desc = Tr::tr("Generate Setter");
+ results << new GenerateGetterSetterOp(interface, data, GenerateSetter, ++p, desc);
+ }
+ if (possibleFlags & GenerateGetter) {
+ const QString desc = Tr::tr("Generate Getter");
+ results << new GenerateGetterSetterOp(interface, data, GenerateGetter, ++p, desc);
+ }
+ if (possibleFlags & GenerateGetter && possibleFlags & GenerateSetter) {
+ const QString desc = Tr::tr("Generate Getter and Setter");
+ const int flags = GenerateGetter | GenerateSetter;
+ results << new GenerateGetterSetterOp(interface, data, flags, ++p, desc);
+ }
+
+ if (possibleFlags & GenerateConstantProperty) {
+ const QString desc = Tr::tr("Generate Constant Q_PROPERTY and Missing Members");
+ const int flags = possibleFlags & ~(GenerateSetter | GenerateSignal | GenerateReset);
+ results << new GenerateGetterSetterOp(interface, data, flags, ++p, desc);
+ }
+ if (possibleFlags & GenerateProperty) {
+ if (possibleFlags & GenerateReset) {
+ const QString desc = Tr::tr(
+ "Generate Q_PROPERTY and Missing Members with Reset Function");
+ const int flags = possibleFlags & ~GenerateConstantProperty;
+ results << new GenerateGetterSetterOp(interface, data, flags, ++p, desc);
+ }
+ const QString desc = Tr::tr("Generate Q_PROPERTY and Missing Members");
+ const int flags = possibleFlags & ~GenerateConstantProperty & ~GenerateReset;
+ results << new GenerateGetterSetterOp(interface, data, flags, ++p, desc);
+ }
+ }
+ }
+
+ void perform() override
+ {
+ GetterSetterRefactoringHelper helper(this, currentFile()->filePath(), m_data.clazz);
+ helper.performGeneration(m_data, m_generateFlags);
+ helper.applyChanges();
+ }
+
+private:
+ int m_generateFlags;
+ ExistingGetterSetterData m_data;
+};
+
+class CandidateTreeItem : public TreeItem
+{
+public:
+ enum Column {
+ NameColumn,
+ GetterColumn,
+ SetterColumn,
+ SignalColumn,
+ ResetColumn,
+ QPropertyColumn,
+ ConstantQPropertyColumn
+ };
+ using Flag = GenerateGetterSetterOp::GenerateFlag;
+ constexpr static Flag ColumnFlag[] = {
+ Flag::Invalid,
+ Flag::GenerateGetter,
+ Flag::GenerateSetter,
+ Flag::GenerateSignal,
+ Flag::GenerateReset,
+ Flag::GenerateProperty,
+ Flag::GenerateConstantProperty,
+ };
+
+ CandidateTreeItem(MemberInfo *memberInfo)
+ : m_memberInfo(memberInfo)
+ {}
+
+private:
+ QVariant data(int column, int role) const override
+ {
+ if (role == Qt::DisplayRole && column == NameColumn)
+ return m_memberInfo->data.memberVariableName;
+ if (role == Qt::CheckStateRole && column > 0
+ && column <= static_cast<int>(std::size(ColumnFlag))) {
+ return m_memberInfo->requestedFlags & ColumnFlag[column] ? Qt::Checked : Qt::Unchecked;
+ }
+ return {};
+ }
+
+ bool setData(int column, const QVariant &data, int role) override
+ {
+ if (column < 1 || column > static_cast<int>(std::size(ColumnFlag)))
+ return false;
+ if (role != Qt::CheckStateRole)
+ return false;
+ if (!(m_memberInfo->possibleFlags & ColumnFlag[column]))
+ return false;
+ const bool nowChecked = data.toInt() == Qt::Checked;
+ if (nowChecked)
+ m_memberInfo->requestedFlags |= ColumnFlag[column];
+ else
+ m_memberInfo->requestedFlags &= ~ColumnFlag[column];
+
+ if (nowChecked) {
+ if (column == QPropertyColumn) {
+ m_memberInfo->requestedFlags |= Flag::GenerateGetter;
+ m_memberInfo->requestedFlags |= Flag::GenerateSetter;
+ m_memberInfo->requestedFlags |= Flag::GenerateSignal;
+ m_memberInfo->requestedFlags &= ~Flag::GenerateConstantProperty;
+ } else if (column == ConstantQPropertyColumn) {
+ m_memberInfo->requestedFlags |= Flag::GenerateGetter;
+ m_memberInfo->requestedFlags &= ~Flag::GenerateSetter;
+ m_memberInfo->requestedFlags &= ~Flag::GenerateSignal;
+ m_memberInfo->requestedFlags &= ~Flag::GenerateReset;
+ m_memberInfo->requestedFlags &= ~Flag::GenerateProperty;
+ } else if (column == SetterColumn || column == SignalColumn || column == ResetColumn) {
+ m_memberInfo->requestedFlags &= ~Flag::GenerateConstantProperty;
+ }
+ } else {
+ if (column == SignalColumn)
+ m_memberInfo->requestedFlags &= ~Flag::GenerateProperty;
+ }
+ for (int i = 0; i < 16; ++i) {
+ const bool allowed = m_memberInfo->possibleFlags & (1 << i);
+ if (!allowed)
+ m_memberInfo->requestedFlags &= ~(1 << i); // clear bit
+ }
+ update();
+ return true;
+ }
+
+ Qt::ItemFlags flags(int column) const override
+ {
+ if (column == NameColumn)
+ return Qt::ItemIsEnabled;
+ if (column < 1 || column > static_cast<int>(std::size(ColumnFlag)))
+ return {};
+ if (m_memberInfo->possibleFlags & ColumnFlag[column])
+ return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
+ return {};
+ }
+
+ MemberInfo *const m_memberInfo;
+};
+
+class GenerateGettersSettersDialog : public QDialog
+{
+ static constexpr CandidateTreeItem::Column CheckBoxColumn[4]
+ = {CandidateTreeItem::Column::GetterColumn,
+ CandidateTreeItem::Column::SetterColumn,
+ CandidateTreeItem::Column::SignalColumn,
+ CandidateTreeItem::Column::QPropertyColumn};
+
+public:
+ GenerateGettersSettersDialog(const GetterSetterCandidates &candidates)
+ : QDialog()
+ , m_candidates(candidates)
+ {
+ using Flags = GenerateGetterSetterOp::GenerateFlag;
+ setWindowTitle(Tr::tr("Getters and Setters"));
+ const auto model = new TreeModel<TreeItem, CandidateTreeItem>(this);
+ model->setHeader(QStringList({
+ Tr::tr("Member"),
+ Tr::tr("Getter"),
+ Tr::tr("Setter"),
+ Tr::tr("Signal"),
+ Tr::tr("Reset"),
+ Tr::tr("QProperty"),
+ Tr::tr("Constant QProperty"),
+ }));
+ for (MemberInfo &candidate : m_candidates)
+ model->rootItem()->appendChild(new CandidateTreeItem(&candidate));
+ const auto view = new BaseTreeView(this);
+ view->setModel(model);
+ int optimalWidth = 0;
+ for (int i = 0; i < model->columnCount(QModelIndex{}); ++i) {
+ view->resizeColumnToContents(i);
+ optimalWidth += view->columnWidth(i);
+ }
+
+ const auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+ connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+
+ const auto setCheckStateForAll = [model](int column, int checkState) {
+ for (int i = 0; i < model->rowCount(); ++i)
+ model->setData(model->index(i, column), checkState, Qt::CheckStateRole);
+ };
+ const auto preventPartiallyChecked = [](QCheckBox *checkbox) {
+ if (checkbox->checkState() == Qt::PartiallyChecked)
+ checkbox->setCheckState(Qt::Checked);
+ };
+ using Column = CandidateTreeItem::Column;
+ const auto createConnections = [this, setCheckStateForAll, preventPartiallyChecked](
+ QCheckBox *checkbox, Column column) {
+ connect(checkbox, &QCheckBox::stateChanged, this, [setCheckStateForAll, column](int state) {
+ if (state != Qt::PartiallyChecked)
+ setCheckStateForAll(column, state);
+ });
+ connect(checkbox, &QCheckBox::clicked, this, [checkbox, preventPartiallyChecked] {
+ preventPartiallyChecked(checkbox);
+ });
+ };
+ std::array<QCheckBox *, 4> checkBoxes = {};
+
+ static_assert(std::size(CheckBoxColumn) == checkBoxes.size(),
+ "Must contain the same number of elements");
+ for (std::size_t i = 0; i < checkBoxes.size(); ++i) {
+ if (Utils::anyOf(candidates, [i](const MemberInfo &mi) {
+ return mi.possibleFlags & CandidateTreeItem::ColumnFlag[CheckBoxColumn[i]];
+ })) {
+ const Column column = CheckBoxColumn[i];
+ if (column == Column::GetterColumn)
+ checkBoxes[i] = new QCheckBox(Tr::tr("Create getters for all members"));
+ else if (column == Column::SetterColumn)
+ checkBoxes[i] = new QCheckBox(Tr::tr("Create setters for all members"));
+ else if (column == Column::SignalColumn)
+ checkBoxes[i] = new QCheckBox(Tr::tr("Create signals for all members"));
+ else if (column == Column::QPropertyColumn)
+ checkBoxes[i] = new QCheckBox(Tr::tr("Create Q_PROPERTY for all members"));
+
+ createConnections(checkBoxes[i], column);
+ }
+ }
+ connect(model, &QAbstractItemModel::dataChanged, this, [this, checkBoxes] {
+ const auto countExisting = [this](Flags flag) {
+ return Utils::count(m_candidates, [flag](const MemberInfo &mi) {
+ return !(mi.possibleFlags & flag);
+ });
+ };
+ const auto countRequested = [this](Flags flag) {
+ return Utils::count(m_candidates, [flag](const MemberInfo &mi) {
+ return mi.requestedFlags & flag;
+ });
+ };
+ const auto countToState = [this](int requestedCount, int alreadyExistsCount) {
+ if (requestedCount == 0)
+ return Qt::Unchecked;
+ if (int(m_candidates.size()) - requestedCount == alreadyExistsCount)
+ return Qt::Checked;
+ return Qt::PartiallyChecked;
+ };
+ for (std::size_t i = 0; i < checkBoxes.size(); ++i) {
+ if (checkBoxes[i]) {
+ const Flags flag = CandidateTreeItem::ColumnFlag[CheckBoxColumn[i]];
+ checkBoxes[i]->setCheckState(
+ countToState(countRequested(flag), countExisting(flag)));
+ }
+ }
+ });
+
+ const auto mainLayout = new QVBoxLayout(this);
+ mainLayout->addWidget(new QLabel(Tr::tr("Select the getters and setters "
+ "to be created.")));
+ for (auto checkBox : checkBoxes) {
+ if (checkBox)
+ mainLayout->addWidget(checkBox);
+ }
+ mainLayout->addWidget(view);
+ mainLayout->addWidget(buttonBox);
+ int left, right;
+ mainLayout->getContentsMargins(&left, nullptr, &right, nullptr);
+ optimalWidth += left + right;
+ resize(optimalWidth, mainLayout->sizeHint().height());
+ }
+
+ GetterSetterCandidates candidates() const { return m_candidates; }
+
+private:
+ GetterSetterCandidates m_candidates;
+};
+
+class GenerateGettersSettersOperation : public CppQuickFixOperation
+{
+public:
+ GenerateGettersSettersOperation(const CppQuickFixInterface &interface)
+ : CppQuickFixOperation(interface)
+ {
+ setDescription(Tr::tr("Create Getter and Setter Member Functions"));
+
+ m_classAST = astForClassOperations(interface);
+ if (!m_classAST)
+ return;
+ Class * const theClass = m_classAST->symbol;
+ if (!theClass)
+ return;
+
+ // Go through all data members and try to find out whether they have getters and/or setters.
+ QList<Symbol *> dataMembers;
+ QList<Symbol *> memberFunctions;
+ for (auto it = theClass->memberBegin(); it != theClass->memberEnd(); ++it) {
+ Symbol *const s = *it;
+ if (!s->identifier() || !s->type() || s->type().isTypedef())
+ continue;
+ if ((s->asDeclaration() && s->type()->asFunctionType()) || s->asFunction())
+ memberFunctions << s;
+ else if (s->asDeclaration() && (s->isPrivate() || s->isProtected()))
+ dataMembers << s;
+ }
+
+ auto file = interface.currentFile();
+ QStringList qPropertyNames; // name after MEMBER or name of the property
+ for (auto it = m_classAST->member_specifier_list; it; it = it->next) {
+ if (it->value->asQtPropertyDeclaration()) {
+ auto propDecl = it->value->asQtPropertyDeclaration();
+ // iterator over 'READ ...', ... and check if we have a MEMBER
+ for (auto p = propDecl->property_declaration_item_list; p; p = p->next) {
+ const char *tokenString = file->tokenAt(p->value->item_name_token).spell();
+ if (!qstrcmp(tokenString, "MEMBER"))
+ qPropertyNames << file->textOf(p->value->expression);
+ }
+ // no MEMBER, but maybe the property name is the same
+ qPropertyNames << file->textOf(propDecl->property_name);
+ }
+ }
+ const QStringList memberFunctionsAsStrings = toStringList(memberFunctions);
+
+ for (Symbol *const member : std::as_const(dataMembers)) {
+ ExistingGetterSetterData existing;
+ existing.memberVariableName = QString::fromUtf8(member->identifier()->chars(),
+ member->identifier()->size());
+ existing.declarationSymbol = member->asDeclaration();
+ existing.clazz = theClass;
+
+ // check if a Q_PROPERTY exist
+ const QString baseName = memberBaseName(existing.memberVariableName);
+ if (qPropertyNames.contains(baseName)
+ || qPropertyNames.contains(existing.memberVariableName))
+ continue;
+
+ findExistingFunctions(existing, memberFunctionsAsStrings);
+ existing.qPropertyName = baseName;
+
+ int possibleFlags = existing.computePossibleFlags();
+ if (possibleFlags == 0)
+ continue;
+ m_candidates.emplace_back(existing, possibleFlags);
+ }
+ }
+
+ GetterSetterCandidates candidates() const { return m_candidates; }
+ bool isApplicable() const { return !m_candidates.empty(); }
+
+ void setGetterSetterData(const GetterSetterCandidates &data)
+ {
+ m_candidates = data;
+ m_hasData = true;
+ }
+
+private:
+ void perform() override
+ {
+ if (!m_hasData) {
+ GenerateGettersSettersDialog dlg(m_candidates);
+ if (dlg.exec() == QDialog::Rejected)
+ return;
+ m_candidates = dlg.candidates();
+ }
+ if (m_candidates.empty())
+ return;
+ GetterSetterRefactoringHelper helper(this,
+ currentFile()->filePath(),
+ m_candidates.front().data.clazz);
+ for (MemberInfo &mi : m_candidates) {
+ if (mi.requestedFlags != 0) {
+ helper.performGeneration(mi.data, mi.requestedFlags);
+ }
+ }
+ helper.applyChanges();
+ }
+
+ GetterSetterCandidates m_candidates;
+ const ClassSpecifierAST *m_classAST = nullptr;
+ bool m_hasData = false;
+};
+
+int ExistingGetterSetterData::computePossibleFlags() const
+{
+ const bool isConst = declarationSymbol->type().isConst();
+ const bool isStatic = declarationSymbol->type().isStatic();
+ using Flag = GenerateGetterSetterOp::GenerateFlag;
+ int generateFlags = 0;
+ if (getterName.isEmpty())
+ generateFlags |= Flag::GenerateGetter;
+ if (!isConst) {
+ if (resetName.isEmpty())
+ generateFlags |= Flag::GenerateReset;
+ if (!isStatic && signalName.isEmpty() && setterName.isEmpty())
+ generateFlags |= Flag::GenerateSignal;
+ if (setterName.isEmpty())
+ generateFlags |= Flag::GenerateSetter;
+ }
+ if (!isStatic) {
+ const bool hasSignal = !signalName.isEmpty() || generateFlags & Flag::GenerateSignal;
+ if (!isConst && hasSignal)
+ generateFlags |= Flag::GenerateProperty;
+ }
+ if (setterName.isEmpty() && signalName.isEmpty())
+ generateFlags |= Flag::GenerateConstantProperty;
+ return generateFlags;
+}
+
+void GetterSetterRefactoringHelper::performGeneration(ExistingGetterSetterData data, int generateFlags)
+{
+ using Flag = GenerateGetterSetterOp::GenerateFlag;
+
+ if (generateFlags & Flag::GenerateGetter && data.getterName.isEmpty()) {
+ data.getterName = m_settings->getGetterName(data.qPropertyName);
+ if (data.getterName == data.memberVariableName) {
+ data.getterName = "get" + data.memberVariableName.left(1).toUpper()
+ + data.memberVariableName.mid(1);
+ }
+ }
+ if (generateFlags & Flag::GenerateSetter && data.setterName.isEmpty())
+ data.setterName = m_settings->getSetterName(data.qPropertyName);
+ if (generateFlags & Flag::GenerateSignal && data.signalName.isEmpty())
+ data.signalName = m_settings->getSignalName(data.qPropertyName);
+ if (generateFlags & Flag::GenerateReset && data.resetName.isEmpty())
+ data.resetName = m_settings->getResetName(data.qPropertyName);
+
+ FullySpecifiedType memberVariableType = data.declarationSymbol->type();
+ memberVariableType.setConst(false);
+ const bool isMemberVariableStatic = memberVariableType.isStatic();
+ memberVariableType.setStatic(false);
+ Overview overview = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ overview.showTemplateParameters = false;
+ // TODO does not work with using. e.g. 'using foo = std::unique_ptr<int>'
+ // TODO must be fully qualified
+ auto getSetTemplate = m_settings->findGetterSetterTemplate(overview.prettyType(memberVariableType));
+ overview.showTemplateParameters = true;
+
+ // Ok... - If a type is a Named type we have to search recusive for the real type
+ const bool isValueType = this->isValueType(memberVariableType,
+ data.declarationSymbol->enclosingScope());
+ const FullySpecifiedType parameterType = isValueType ? memberVariableType
+ : makeConstRef(memberVariableType);
+
+ QString baseName = memberBaseName(data.memberVariableName);
+ if (baseName.isEmpty())
+ baseName = data.memberVariableName;
+
+ const QString parameterName = m_settings->getSetterParameterName(baseName);
+ if (parameterName == data.memberVariableName)
+ data.memberVariableName = "this->" + data.memberVariableName;
+
+ getSetTemplate.replacePlaceholders(data.memberVariableName, parameterName);
+
+ using Pattern = CppQuickFixSettings::GetterSetterTemplate;
+ std::optional<FullySpecifiedType> returnTypeTemplateParameter;
+ if (getSetTemplate.returnTypeTemplate.has_value()) {
+ QString returnTypeTemplate = getSetTemplate.returnTypeTemplate.value();
+ if (returnTypeTemplate.contains(Pattern::TEMPLATE_PARAMETER_PATTERN)) {
+ returnTypeTemplateParameter = getFirstTemplateParameter(data.declarationSymbol->type());
+ if (!returnTypeTemplateParameter.has_value())
+ return; // Maybe report error to the user
+ }
+ }
+ const FullySpecifiedType returnTypeHeader = [&] {
+ if (!getSetTemplate.returnTypeTemplate.has_value())
+ return m_settings->returnByConstRef ? parameterType : memberVariableType;
+ QString typeTemplate = getSetTemplate.returnTypeTemplate.value();
+ if (returnTypeTemplateParameter.has_value())
+ typeTemplate.replace(Pattern::TEMPLATE_PARAMETER_PATTERN,
+ overview.prettyType(returnTypeTemplateParameter.value()));
+ if (typeTemplate.contains(Pattern::TYPE_PATTERN))
+ typeTemplate.replace(Pattern::TYPE_PATTERN,
+ overview.prettyType(data.declarationSymbol->type()));
+ Control *control = m_operation->currentFile()->cppDocument()->control();
+ std::string utf8TypeName = typeTemplate.toUtf8().toStdString();
+ return FullySpecifiedType(control->namedType(control->identifier(utf8TypeName.c_str())));
+ }();
+
+ // getter declaration
+ if (generateFlags & Flag::GenerateGetter) {
+ // maybe we added 'this->' to memberVariableName because of a collision with parameterName
+ // but here the 'this->' is not needed
+ const QString returnExpression = QString{getSetTemplate.returnExpression}.replace("this->",
+ "");
+ QString getterInClassDeclaration = overview.prettyType(returnTypeHeader, data.getterName)
+ + QLatin1String("()");
+ if (isMemberVariableStatic)
+ getterInClassDeclaration.prepend(QLatin1String("static "));
+ else
+ getterInClassDeclaration += QLatin1String(" const");
+ getterInClassDeclaration.prepend(m_settings->getterAttributes + QLatin1Char(' '));
+
+ auto getterLocation = m_settings->determineGetterLocation(1);
+ // if we have an anonymous class we must add code inside the class
+ if (data.clazz->name()->asAnonymousNameId())
+ getterLocation = CppQuickFixSettings::FunctionLocation::InsideClass;
+
+ if (getterLocation == CppQuickFixSettings::FunctionLocation::InsideClass) {
+ getterInClassDeclaration += QLatin1String("\n{\nreturn ") + returnExpression
+ + QLatin1String(";\n}\n");
+ } else {
+ getterInClassDeclaration += QLatin1String(";\n");
+ }
+ addHeaderCode(InsertionPointLocator::Public, getterInClassDeclaration);
+ if (getterLocation == CppQuickFixSettings::FunctionLocation::CppFile && !hasSourceFile())
+ getterLocation = CppQuickFixSettings::FunctionLocation::OutsideClass;
+
+ if (getterLocation != CppQuickFixSettings::FunctionLocation::InsideClass) {
+ const auto getReturnTypeAt = [&](CppRefactoringFilePtr targetFile,
+ InsertionLocation targetLoc) {
+ if (getSetTemplate.returnTypeTemplate.has_value()) {
+ QString returnType = getSetTemplate.returnTypeTemplate.value();
+ if (returnTypeTemplateParameter.has_value()) {
+ const QString templateTypeName = overview.prettyType(typeAt(
+ returnTypeTemplateParameter.value(), data.clazz, targetFile, targetLoc));
+ returnType.replace(Pattern::TEMPLATE_PARAMETER_PATTERN, templateTypeName);
+ }
+ if (returnType.contains(Pattern::TYPE_PATTERN)) {
+ const QString declarationType = overview.prettyType(
+ typeAt(memberVariableType, data.clazz, targetFile, targetLoc));
+ returnType.replace(Pattern::TYPE_PATTERN, declarationType);
+ }
+ Control *control = m_operation->currentFile()->cppDocument()->control();
+ std::string utf8String = returnType.toUtf8().toStdString();
+ return FullySpecifiedType(
+ control->namedType(control->identifier(utf8String.c_str())));
+ } else {
+ FullySpecifiedType returnType = typeAt(memberVariableType,
+ data.clazz,
+ targetFile,
+ targetLoc);
+ if (m_settings->returnByConstRef && !isValueType)
+ return makeConstRef(returnType);
+ return returnType;
+ }
+ };
+ const QString constSpec = isMemberVariableStatic ? QLatin1String("")
+ : QLatin1String(" const");
+ if (getterLocation == CppQuickFixSettings::FunctionLocation::CppFile) {
+ InsertionLocation loc = sourceLocationFor(data.declarationSymbol);
+ FullySpecifiedType returnType;
+ QString clazz;
+ if (m_settings->rewriteTypesinCppFile()) {
+ returnType = getReturnTypeAt(m_sourceFile, loc);
+ clazz = symbolAt(data.clazz, m_sourceFile, loc);
+ } else {
+ returnType = returnTypeHeader;
+ const Identifier *identifier = data.clazz->name()->identifier();
+ clazz = QString::fromUtf8(identifier->chars(), identifier->size());
+ }
+ const QString code = overview.prettyType(returnType, clazz + "::" + data.getterName)
+ + "()" + constSpec + "\n{\nreturn " + returnExpression + ";\n}";
+ addSourceFileCode(code);
+ } else if (getterLocation == CppQuickFixSettings::FunctionLocation::OutsideClass) {
+ InsertionLocation loc
+ = insertLocationForMethodDefinition(data.declarationSymbol,
+ false,
+ NamespaceHandling::Ignore,
+ m_changes,
+ m_headerFile->filePath());
+ const FullySpecifiedType returnType = getReturnTypeAt(m_headerFile, loc);
+ const QString clazz = symbolAt(data.clazz, m_headerFile, loc);
+ QString code = overview.prettyType(returnType, clazz + "::" + data.getterName)
+ + "()" + constSpec + "\n{\nreturn " + returnExpression + ";\n}";
+ if (m_isHeaderHeaderFile)
+ code.prepend("inline ");
+ insertAndIndent(m_headerFile, loc, code);
+ }
+ }
+ }
+
+ // setter declaration
+ InsertionPointLocator::AccessSpec setterAccessSpec = InsertionPointLocator::Public;
+ if (m_settings->setterAsSlot) {
+ const QByteArray connectName = "connect";
+ const Identifier connectId(connectName.data(), connectName.size());
+ const QList<LookupItem> items = m_operation->context().lookup(&connectId, data.clazz);
+ for (const LookupItem &item : items) {
+ if (item.declaration() && item.declaration()->enclosingClass()
+ && overview.prettyName(item.declaration()->enclosingClass()->name())
+ == "QObject") {
+ setterAccessSpec = InsertionPointLocator::PublicSlot;
+ break;
+ }
+ }
+ }
+ const auto createSetterBodyWithSignal = [this, &getSetTemplate, &data] {
+ QString body;
+ QTextStream setter(&body);
+ setter << "if (" << getSetTemplate.equalComparison << ")\nreturn;\n";
+
+ setter << getSetTemplate.assignment << ";\n";
+ if (m_settings->signalWithNewValue)
+ setter << "emit " << data.signalName << "(" << getSetTemplate.returnExpression << ");\n";
+ else
+ setter << "emit " << data.signalName << "();\n";
+
+ return body;
+ };
+ if (generateFlags & Flag::GenerateSetter) {
+ QString headerDeclaration = "void " + data.setterName + '('
+ + overview.prettyType(addConstToReference(parameterType),
+ parameterName)
+ + ")";
+ if (isMemberVariableStatic)
+ headerDeclaration.prepend("static ");
+ QString body = "\n{\n";
+ if (data.signalName.isEmpty())
+ body += getSetTemplate.assignment + ";\n";
+ else
+ body += createSetterBodyWithSignal();
+
+ body += "}";
+
+ auto setterLocation = m_settings->determineSetterLocation(body.count('\n') - 2);
+ // if we have an anonymous class we must add code inside the class
+ if (data.clazz->name()->asAnonymousNameId())
+ setterLocation = CppQuickFixSettings::FunctionLocation::InsideClass;
+
+ if (setterLocation == CppQuickFixSettings::FunctionLocation::CppFile && !hasSourceFile())
+ setterLocation = CppQuickFixSettings::FunctionLocation::OutsideClass;
+
+ if (setterLocation == CppQuickFixSettings::FunctionLocation::InsideClass) {
+ headerDeclaration += body;
+ } else {
+ headerDeclaration += ";\n";
+ if (setterLocation == CppQuickFixSettings::FunctionLocation::CppFile) {
+ InsertionLocation loc = sourceLocationFor(data.declarationSymbol);
+ QString clazz;
+ FullySpecifiedType newParameterType = parameterType;
+ if (m_settings->rewriteTypesinCppFile()) {
+ newParameterType = typeAt(memberVariableType, data.clazz, m_sourceFile, loc);
+ if (!isValueType)
+ newParameterType = makeConstRef(newParameterType);
+ clazz = symbolAt(data.clazz, m_sourceFile, loc);
+ } else {
+ const Identifier *identifier = data.clazz->name()->identifier();
+ clazz = QString::fromUtf8(identifier->chars(), identifier->size());
+ }
+ newParameterType = addConstToReference(newParameterType);
+ const QString code = "void " + clazz + "::" + data.setterName + '('
+ + overview.prettyType(newParameterType, parameterName) + ')'
+ + body;
+ addSourceFileCode(code);
+ } else if (setterLocation == CppQuickFixSettings::FunctionLocation::OutsideClass) {
+ InsertionLocation loc
+ = insertLocationForMethodDefinition(data.declarationSymbol,
+ false,
+ NamespaceHandling::Ignore,
+ m_changes,
+ m_headerFile->filePath());
+
+ FullySpecifiedType newParameterType = typeAt(data.declarationSymbol->type(),
+ data.clazz,
+ m_headerFile,
+ loc);
+ if (!isValueType)
+ newParameterType = makeConstRef(newParameterType);
+ newParameterType = addConstToReference(newParameterType);
+ QString clazz = symbolAt(data.clazz, m_headerFile, loc);
+
+ QString code = "void " + clazz + "::" + data.setterName + '('
+ + overview.prettyType(newParameterType, parameterName) + ')' + body;
+ if (m_isHeaderHeaderFile)
+ code.prepend("inline ");
+ insertAndIndent(m_headerFile, loc, code);
+ }
+ }
+ addHeaderCode(setterAccessSpec, headerDeclaration);
+ }
+
+ // reset declaration
+ if (generateFlags & Flag::GenerateReset) {
+ QString headerDeclaration = "void " + data.resetName + "()";
+ if (isMemberVariableStatic)
+ headerDeclaration.prepend("static ");
+ QString body = "\n{\n";
+ if (!data.setterName.isEmpty()) {
+ body += data.setterName + "({}); // TODO: Adapt to use your actual default value\n";
+ } else {
+ body += "static $TYPE defaultValue{}; "
+ "// TODO: Adapt to use your actual default value\n";
+ if (data.signalName.isEmpty())
+ body += getSetTemplate.assignment + ";\n";
+ else
+ body += createSetterBodyWithSignal();
+ }
+ body += "}";
+
+ // the template use <parameterName> as new value name, but we want to use 'defaultValue'
+ body.replace(QRegularExpression("\\b" + parameterName + "\\b"), "defaultValue");
+ // body.count('\n') - 2 : do not count the 2 at start
+ auto resetLocation = m_settings->determineSetterLocation(body.count('\n') - 2);
+ // if we have an anonymous class we must add code inside the class
+ if (data.clazz->name()->asAnonymousNameId())
+ resetLocation = CppQuickFixSettings::FunctionLocation::InsideClass;
+
+ if (resetLocation == CppQuickFixSettings::FunctionLocation::CppFile && !hasSourceFile())
+ resetLocation = CppQuickFixSettings::FunctionLocation::OutsideClass;
+
+ if (resetLocation == CppQuickFixSettings::FunctionLocation::InsideClass) {
+ headerDeclaration += body.replace("$TYPE", overview.prettyType(memberVariableType));
+ } else {
+ headerDeclaration += ";\n";
+ if (resetLocation == CppQuickFixSettings::FunctionLocation::CppFile) {
+ const InsertionLocation loc = sourceLocationFor(data.declarationSymbol);
+ QString clazz;
+ FullySpecifiedType type = memberVariableType;
+ if (m_settings->rewriteTypesinCppFile()) {
+ type = typeAt(memberVariableType, data.clazz, m_sourceFile, loc);
+ clazz = symbolAt(data.clazz, m_sourceFile, loc);
+ } else {
+ const Identifier *identifier = data.clazz->name()->identifier();
+ clazz = QString::fromUtf8(identifier->chars(), identifier->size());
+ }
+ const QString code = "void " + clazz + "::" + data.resetName + "()"
+ + body.replace("$TYPE", overview.prettyType(type));
+ addSourceFileCode(code);
+ } else if (resetLocation == CppQuickFixSettings::FunctionLocation::OutsideClass) {
+ const InsertionLocation loc = insertLocationForMethodDefinition(
+ data.declarationSymbol,
+ false,
+ NamespaceHandling::Ignore,
+ m_changes,
+ m_headerFile->filePath());
+ const FullySpecifiedType type = typeAt(data.declarationSymbol->type(),
+ data.clazz,
+ m_headerFile,
+ loc);
+ const QString clazz = symbolAt(data.clazz, m_headerFile, loc);
+ QString code = "void " + clazz + "::" + data.resetName + "()"
+ + body.replace("$TYPE", overview.prettyType(type));
+ if (m_isHeaderHeaderFile)
+ code.prepend("inline ");
+ insertAndIndent(m_headerFile, loc, code);
+ }
+ }
+ addHeaderCode(setterAccessSpec, headerDeclaration);
+ }
+
+ // signal declaration
+ if (generateFlags & Flag::GenerateSignal) {
+ const auto &parameter = overview.prettyType(returnTypeHeader, data.qPropertyName);
+ const QString newValue = m_settings->signalWithNewValue ? parameter : QString();
+ const QString declaration = QString("void %1(%2);\n").arg(data.signalName, newValue);
+ addHeaderCode(InsertionPointLocator::Signals, declaration);
+ }
+
+ // member variable
+ if (generateFlags & Flag::GenerateMemberVariable) {
+ QString storageDeclaration = overview.prettyType(memberVariableType, data.memberVariableName);
+ if (memberVariableType->asPointerType()
+ && m_operation->semanticInfo().doc->translationUnit()->languageFeatures().cxx11Enabled) {
+ storageDeclaration.append(" = nullptr");
+ }
+ storageDeclaration.append(";\n");
+ addHeaderCode(InsertionPointLocator::Private, storageDeclaration);
+ }
+
+ // Q_PROPERTY
+ if (generateFlags & Flag::GenerateProperty || generateFlags & Flag::GenerateConstantProperty) {
+ // Use the returnTypeHeader as base because of custom types in getSetTemplates.
+ // Remove const reference from type.
+ FullySpecifiedType type = returnTypeHeader;
+ if (ReferenceType *ref = type.type()->asReferenceType())
+ type = ref->elementType();
+ type.setConst(false);
+
+ QString propertyDeclaration = QLatin1String("Q_PROPERTY(")
+ + overview.prettyType(type,
+ memberBaseName(data.memberVariableName));
+ bool needMember = false;
+ if (data.getterName.isEmpty())
+ needMember = true;
+ else
+ propertyDeclaration += QLatin1String(" READ ") + data.getterName;
+ if (generateFlags & Flag::GenerateConstantProperty) {
+ if (needMember)
+ propertyDeclaration += QLatin1String(" MEMBER ") + data.memberVariableName;
+ propertyDeclaration.append(QLatin1String(" CONSTANT"));
+ } else {
+ if (data.setterName.isEmpty()) {
+ needMember = true;
+ } else if (!getSetTemplate.returnTypeTemplate.has_value()) {
+ // if the return type of the getter and then Q_PROPERTY is different than
+ // the setter type, we should not add WRITE to the Q_PROPERTY
+ propertyDeclaration.append(QLatin1String(" WRITE ")).append(data.setterName);
+ }
+ if (needMember)
+ propertyDeclaration += QLatin1String(" MEMBER ") + data.memberVariableName;
+ if (!data.resetName.isEmpty())
+ propertyDeclaration += QLatin1String(" RESET ") + data.resetName;
+ propertyDeclaration.append(QLatin1String(" NOTIFY ")).append(data.signalName);
+ }
+
+ propertyDeclaration.append(QLatin1String(" FINAL)\n"));
+ addHeaderCode(InsertionPointLocator::Private, propertyDeclaration);
+ }
+}
+
+//! Generate constructor
+class GenerateConstructor : public CppQuickFixFactory
+{
+public:
+#ifdef WITH_TESTS
+ static QObject* createTest();
+#endif
+
+protected:
+ void setTest() { m_test = true; }
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const auto op = QSharedPointer<GenerateConstructorOperation>::create(interface);
+ if (!op->isApplicable())
+ return;
+ op->setTest(m_test);
+ result << op;
+ }
+
+ bool m_test = false;
+};
+
+//! Adds getter and setter functions for a member variable
+class GenerateGetterSetter : public CppQuickFixFactory
+{
+public:
+#ifdef WITH_TESTS
+ static QObject* createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ ExistingGetterSetterData existing;
+
+ const QList<AST *> &path = interface.path();
+ // We expect something like
+ // [0] TranslationUnitAST
+ // [1] NamespaceAST
+ // [2] LinkageBodyAST
+ // [3] SimpleDeclarationAST
+ // [4] ClassSpecifierAST
+ // [5] SimpleDeclarationAST
+ // [6] DeclaratorAST
+ // [7] DeclaratorIdAST
+ // [8] SimpleNameAST
+
+ const int n = path.size();
+ if (n < 6)
+ return;
+
+ int i = 1;
+ const auto variableNameAST = path.at(n - i++)->asSimpleName();
+ const auto declaratorId = path.at(n - i++)->asDeclaratorId();
+ // DeclaratorAST might be preceded by PointerAST, e.g. for the case
+ // "class C { char *@s; };", where '@' denotes the text cursor position.
+ auto declarator = path.at(n - i++)->asDeclarator();
+ if (!declarator) {
+ --i;
+ if (path.at(n - i++)->asPointer()) {
+ if (n < 7)
+ return;
+ declarator = path.at(n - i++)->asDeclarator();
+ }
+ if (!declarator)
+ return;
+ }
+ const auto variableDecl = path.at(n - i++)->asSimpleDeclaration();
+ const auto classSpecifier = path.at(n - i++)->asClassSpecifier();
+ const auto classDecl = path.at(n - i++)->asSimpleDeclaration();
+
+ if (!(variableNameAST && declaratorId && variableDecl && classSpecifier && classDecl))
+ return;
+
+ // Do not get triggered on member functconstions and arrays
+ if (declarator->postfix_declarator_list) {
+ return;
+ }
+
+ // Construct getter and setter names
+ const Name *variableName = variableNameAST->name;
+ if (!variableName) {
+ return;
+ }
+ const Identifier *variableId = variableName->identifier();
+ if (!variableId) {
+ return;
+ }
+ existing.memberVariableName = QString::fromUtf8(variableId->chars(), variableId->size());
+
+ // Find the right symbol (for typeName) in the simple declaration
+ Symbol *symbol = nullptr;
+ const List<Symbol *> *symbols = variableDecl->symbols;
+ QTC_ASSERT(symbols, return );
+ for (; symbols; symbols = symbols->next) {
+ Symbol *s = symbols->value;
+ if (const Name *name = s->name()) {
+ if (const Identifier *id = name->identifier()) {
+ const QString symbolName = QString::fromUtf8(id->chars(), id->size());
+ if (symbolName == existing.memberVariableName) {
+ symbol = s;
+ break;
+ }
+ }
+ }
+ }
+ if (!symbol) {
+ // no type can be determined
+ return;
+ }
+ if (!symbol->asDeclaration()) {
+ return;
+ }
+ existing.declarationSymbol = symbol->asDeclaration();
+
+ existing.clazz = classSpecifier->symbol;
+ if (!existing.clazz)
+ return;
+
+ auto file = interface.currentFile();
+ // check if a Q_PROPERTY exist
+ const QString baseName = memberBaseName(existing.memberVariableName);
+ // eg: we have 'int m_test' and now 'Q_PROPERTY(int foo WRITE setTest MEMBER m_test NOTIFY tChanged)'
+ for (auto it = classSpecifier->member_specifier_list; it; it = it->next) {
+ if (it->value->asQtPropertyDeclaration()) {
+ auto propDecl = it->value->asQtPropertyDeclaration();
+ // iterator over 'READ ...', ...
+ auto p = propDecl->property_declaration_item_list;
+ // first check, if we have a MEMBER and the member is equal to the baseName
+ for (; p; p = p->next) {
+ const char *tokenString = file->tokenAt(p->value->item_name_token).spell();
+ if (!qstrcmp(tokenString, "MEMBER")) {
+ if (baseName == file->textOf(p->value->expression))
+ return;
+ }
+ }
+ // no MEMBER, but maybe the property name is the same
+ const QString propertyName = file->textOf(propDecl->property_name);
+ // we compare the baseName. e.g. 'test' instead of 'm_test'
+ if (propertyName == baseName)
+ return; // TODO Maybe offer quick fix "Add missing Q_PROPERTY Members"
+ }
+ }
+
+ findExistingFunctions(existing, toStringList(getMemberFunctions(existing.clazz)));
+ existing.qPropertyName = memberBaseName(existing.memberVariableName);
+
+ const int possibleFlags = existing.computePossibleFlags();
+ GenerateGetterSetterOp::generateQuickFixes(result, interface, existing, possibleFlags);
+ }
+};
+
+//! Adds getter and setter functions for several member variables
+class GenerateGettersSettersForClass : public CppQuickFixFactory
+{
+public:
+#ifdef WITH_TESTS
+ static QObject* createTest();
+#endif
+
+protected:
+ void setTest() { m_test = true; }
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const auto op = QSharedPointer<GenerateGettersSettersOperation>::create(interface);
+ if (!op->isApplicable())
+ return;
+ if (m_test) {
+ GetterSetterCandidates candidates = op->candidates();
+ for (MemberInfo &mi : candidates) {
+ mi.requestedFlags = mi.possibleFlags;
+ using Flag = GenerateGetterSetterOp::GenerateFlag;
+ mi.requestedFlags &= ~Flag::GenerateConstantProperty;
+ }
+ op->setGetterSetterData(candidates);
+ }
+ result << op;
+ }
+
+ bool m_test = false;
+};
+
+//! Adds missing members for a Q_PROPERTY
+class InsertQtPropertyMembers : public CppQuickFixFactory
+{
+public:
+#ifdef WITH_TESTS
+ static QObject* createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ using Flag = GenerateGetterSetterOp::GenerateFlag;
+ ExistingGetterSetterData existing;
+ // check for Q_PROPERTY
+
+ const QList<AST *> &path = interface.path();
+ if (path.isEmpty())
+ return;
+
+ AST *const ast = path.last();
+ QtPropertyDeclarationAST *qtPropertyDeclaration = ast->asQtPropertyDeclaration();
+ if (!qtPropertyDeclaration || !qtPropertyDeclaration->type_id)
+ return;
+
+ ClassSpecifierAST *klass = nullptr;
+ for (int i = path.size() - 2; i >= 0; --i) {
+ klass = path.at(i)->asClassSpecifier();
+ if (klass)
+ break;
+ }
+ if (!klass)
+ return;
+ existing.clazz = klass->symbol;
+
+ CppRefactoringFilePtr file = interface.currentFile();
+ const QString propertyName = file->textOf(qtPropertyDeclaration->property_name);
+ existing.qPropertyName = propertyName;
+ extractNames(file, qtPropertyDeclaration, existing);
+
+ Control *control = interface.currentFile()->cppDocument()->control();
+
+ existing.declarationSymbol = control->newDeclaration(ast->firstToken(),
+ qtPropertyDeclaration->property_name->name);
+ existing.declarationSymbol->setVisibility(Symbol::Private);
+ existing.declarationSymbol->setEnclosingScope(existing.clazz);
+
+ {
+ // create a 'right' Type Object
+ // if we have Q_PROPERTY(int test ...) then we only get a NamedType for 'int', but we want
+ // a IntegerType. So create a new dummy file with a dummy declaration to get the right
+ // object
+ QByteArray type = file->textOf(qtPropertyDeclaration->type_id).toUtf8();
+ QByteArray newSource = file->document()
+ ->toPlainText()
+ .insert(file->startOf(qtPropertyDeclaration),
+ QString::fromUtf8(type + " __dummy;\n"))
+ .toUtf8();
+
+ Document::Ptr doc = interface.snapshot().preprocessedDocument(newSource, "___quickfix.h");
+ if (!doc->parse(Document::ParseTranlationUnit))
+ return;
+ doc->check();
+ class TypeFinder : public ASTVisitor
+ {
+ public:
+ FullySpecifiedType type;
+ TypeFinder(TranslationUnit *u)
+ : ASTVisitor(u)
+ {}
+ bool visit(SimpleDeclarationAST *ast) override
+ {
+ if (ast->symbols && !ast->symbols->next) {
+ const Name *name = ast->symbols->value->name();
+ if (name && name->asNameId() && name->asNameId()->identifier()) {
+ const Identifier *id = name->asNameId()->identifier();
+ if (QString::fromUtf8(id->chars(), id->size()) == "__dummy")
+ type = ast->symbols->value->type();
+ }
+ }
+ return true;
+ }
+ };
+ TypeFinder finder(doc->translationUnit());
+ finder.accept(doc->translationUnit()->ast());
+ if (finder.type.type()->isUndefinedType())
+ return;
+ existing.declarationSymbol->setType(finder.type);
+ existing.doc = doc; // to hold type
+ }
+ // check which methods are already there
+ const bool haveFixMemberVariableName = !existing.memberVariableName.isEmpty();
+ int generateFlags = Flag::GenerateMemberVariable;
+ if (!existing.resetName.isEmpty())
+ generateFlags |= Flag::GenerateReset;
+ if (!existing.setterName.isEmpty())
+ generateFlags |= Flag::GenerateSetter;
+ if (!existing.getterName.isEmpty())
+ generateFlags |= Flag::GenerateGetter;
+ if (!existing.signalName.isEmpty())
+ generateFlags |= Flag::GenerateSignal;
+ Overview overview;
+ for (int i = 0; i < existing.clazz->memberCount(); ++i) {
+ Symbol *member = existing.clazz->memberAt(i);
+ FullySpecifiedType type = member->type();
+ if (member->asFunction() || (type.isValid() && type->asFunctionType())) {
+ const QString name = overview.prettyName(member->name());
+ if (name == existing.getterName)
+ generateFlags &= ~Flag::GenerateGetter;
+ else if (name == existing.setterName)
+ generateFlags &= ~Flag::GenerateSetter;
+ else if (name == existing.resetName)
+ generateFlags &= ~Flag::GenerateReset;
+ else if (name == existing.signalName)
+ generateFlags &= ~Flag::GenerateSignal;
+ } else if (member->asDeclaration()) {
+ const QString name = overview.prettyName(member->name());
+ if (haveFixMemberVariableName) {
+ if (name == existing.memberVariableName) {
+ generateFlags &= ~Flag::GenerateMemberVariable;
+ }
+ } else {
+ const QString baseName = memberBaseName(name);
+ if (existing.qPropertyName == baseName) {
+ existing.memberVariableName = name;
+ generateFlags &= ~Flag::GenerateMemberVariable;
+ }
+ }
+ }
+ }
+ if (generateFlags & Flag::GenerateMemberVariable) {
+ CppQuickFixSettings *settings = CppQuickFixProjectsSettings::getQuickFixSettings(
+ ProjectExplorer::ProjectTree::currentProject());
+ existing.memberVariableName = settings->getMemberVariableName(existing.qPropertyName);
+ }
+ if (generateFlags == 0) {
+ // everything is already there
+ return;
+ }
+ generateFlags |= Flag::HaveExistingQProperty;
+ GenerateGetterSetterOp::generateQuickFixes(result, interface, existing, generateFlags);
+ }
+};
+
+
+#ifdef WITH_TESTS
+using namespace Tests;
+
+class GenerateGetterSetterTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void testNamespaceHandlingCreate_data()
+ {
+ QTest::addColumn<QByteArrayList>("headers");
+ QTest::addColumn<QByteArrayList>("sources");
+
+ QByteArray originalSource;
+ QByteArray expectedSource;
+
+ const QByteArray originalHeader =
+ "namespace N1 {\n"
+ "namespace N2 {\n"
+ "class Something\n"
+ "{\n"
+ " int @it;\n"
+ "};\n"
+ "}\n"
+ "}\n";
+ const QByteArray expectedHeader =
+ "namespace N1 {\n"
+ "namespace N2 {\n"
+ "class Something\n"
+ "{\n"
+ " int it;\n"
+ "\n"
+ "public:\n"
+ " int getIt() const;\n"
+ " void setIt(int value);\n"
+ "};\n"
+ "}\n"
+ "}\n";
+
+ originalSource = "#include \"file.h\"\n";
+ expectedSource =
+ "#include \"file.h\"\n\n\n"
+ "namespace N1 {\n"
+ "namespace N2 {\n"
+ "int Something::getIt() const\n"
+ "{\n"
+ " return it;\n"
+ "}\n"
+ "\n"
+ "void Something::setIt(int value)\n"
+ "{\n"
+ " it = value;\n"
+ "}\n\n"
+ "}\n"
+ "}\n";
+ QTest::addRow("insert new namespaces")
+ << QByteArrayList{originalHeader, expectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalSource =
+ "#include \"file.h\"\n"
+ "namespace N2 {} // decoy\n";
+ expectedSource =
+ "#include \"file.h\"\n"
+ "namespace N2 {} // decoy\n\n\n"
+ "namespace N1 {\n"
+ "namespace N2 {\n"
+ "int Something::getIt() const\n"
+ "{\n"
+ " return it;\n"
+ "}\n"
+ "\n"
+ "void Something::setIt(int value)\n"
+ "{\n"
+ " it = value;\n"
+ "}\n\n"
+ "}\n"
+ "}\n";
+ QTest::addRow("insert new namespaces (with decoy)")
+ << QByteArrayList{originalHeader, expectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalSource = "#include \"file.h\"\n"
+ "namespace N2 {} // decoy\n"
+ "namespace {\n"
+ "namespace N1 {\n"
+ "namespace {\n"
+ "}\n"
+ "}\n"
+ "}\n";
+ expectedSource = "#include \"file.h\"\n"
+ "namespace N2 {} // decoy\n"
+ "namespace {\n"
+ "namespace N1 {\n"
+ "namespace {\n"
+ "}\n"
+ "}\n"
+ "}\n"
+ "\n"
+ "\n"
+ "namespace N1 {\n"
+ "namespace N2 {\n"
+ "int Something::getIt() const\n"
+ "{\n"
+ " return it;\n"
+ "}\n"
+ "\n"
+ "void Something::setIt(int value)\n"
+ "{\n"
+ " it = value;\n"
+ "}\n"
+ "\n"
+ "}\n"
+ "}\n";
+ QTest::addRow("insert inner namespace (with decoy and unnamed)")
+ << QByteArrayList{originalHeader, expectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+
+ const QByteArray unnamedOriginalHeader = "namespace {\n" + originalHeader + "}\n";
+ const QByteArray unnamedExpectedHeader = "namespace {\n" + expectedHeader + "}\n";
+
+ originalSource = "#include \"file.h\"\n"
+ "namespace N2 {} // decoy\n"
+ "namespace {\n"
+ "namespace N1 {\n"
+ "namespace {\n"
+ "}\n"
+ "}\n"
+ "}\n";
+ expectedSource = "#include \"file.h\"\n"
+ "namespace N2 {} // decoy\n"
+ "namespace {\n"
+ "namespace N1 {\n"
+ "\n"
+ "namespace N2 {\n"
+ "int Something::getIt() const\n"
+ "{\n"
+ " return it;\n"
+ "}\n"
+ "\n"
+ "void Something::setIt(int value)\n"
+ "{\n"
+ " it = value;\n"
+ "}\n"
+ "\n"
+ "}\n"
+ "\n"
+ "namespace {\n"
+ "}\n"
+ "}\n"
+ "}\n";
+ QTest::addRow("insert inner namespace in unnamed (with decoy)")
+ << QByteArrayList{unnamedOriginalHeader, unnamedExpectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalSource =
+ "#include \"file.h\"\n"
+ "namespace N1 {\n"
+ "namespace N2 {\n"
+ "namespace N3 {\n"
+ "}\n"
+ "}\n"
+ "}\n";
+ expectedSource =
+ "#include \"file.h\"\n"
+ "namespace N1 {\n"
+ "namespace N2 {\n"
+ "namespace N3 {\n"
+ "}\n\n"
+ "int Something::getIt() const\n"
+ "{\n"
+ " return it;\n"
+ "}\n"
+ "\n"
+ "void Something::setIt(int value)\n"
+ "{\n"
+ " it = value;\n"
+ "}\n\n"
+ "}\n"
+ "}\n";
+ QTest::addRow("all namespaces already present")
+ << QByteArrayList{originalHeader, expectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalSource = "#include \"file.h\"\n"
+ "namespace N1 {\n"
+ "using namespace N2::N3;\n"
+ "using namespace N2;\n"
+ "using namespace N2;\n"
+ "using namespace N3;\n"
+ "}\n";
+ expectedSource = "#include \"file.h\"\n"
+ "namespace N1 {\n"
+ "using namespace N2::N3;\n"
+ "using namespace N2;\n"
+ "using namespace N2;\n"
+ "using namespace N3;\n"
+ "\n"
+ "int Something::getIt() const\n"
+ "{\n"
+ " return it;\n"
+ "}\n"
+ "\n"
+ "void Something::setIt(int value)\n"
+ "{\n"
+ " it = value;\n"
+ "}\n\n"
+ "}\n";
+ QTest::addRow("namespaces present and using namespace")
+ << QByteArrayList{originalHeader, expectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalSource = "#include \"file.h\"\n"
+ "using namespace N1::N2::N3;\n"
+ "using namespace N1::N2;\n"
+ "namespace N1 {\n"
+ "using namespace N3;\n"
+ "}\n";
+ expectedSource = "#include \"file.h\"\n"
+ "using namespace N1::N2::N3;\n"
+ "using namespace N1::N2;\n"
+ "namespace N1 {\n"
+ "using namespace N3;\n"
+ "\n"
+ "int Something::getIt() const\n"
+ "{\n"
+ " return it;\n"
+ "}\n"
+ "\n"
+ "void Something::setIt(int value)\n"
+ "{\n"
+ " it = value;\n"
+ "}\n"
+ "\n"
+ "}\n";
+ QTest::addRow("namespaces present and outer using namespace")
+ << QByteArrayList{originalHeader, expectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalSource = "#include \"file.h\"\n"
+ "using namespace N1;\n"
+ "using namespace N2;\n"
+ "namespace N3 {\n"
+ "}\n";
+ expectedSource = "#include \"file.h\"\n"
+ "using namespace N1;\n"
+ "using namespace N2;\n"
+ "namespace N3 {\n"
+ "}\n"
+ "\n"
+ "int Something::getIt() const\n"
+ "{\n"
+ " return it;\n"
+ "}\n"
+ "\n"
+ "void Something::setIt(int value)\n"
+ "{\n"
+ " it = value;\n"
+ "}\n";
+ QTest::addRow("namespaces present and outer using namespace")
+ << QByteArrayList{originalHeader, expectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+ }
+
+ void testNamespaceHandlingCreate()
+ {
+ QFETCH(QByteArrayList, headers);
+ QFETCH(QByteArrayList, sources);
+
+ QList<TestDocumentPtr> testDocuments(
+ {CppTestDocument::create("file.h", headers.at(0), headers.at(1)),
+ CppTestDocument::create("file.cpp", sources.at(0), sources.at(1))});
+
+ QuickFixSettings s;
+ s->cppFileNamespaceHandling = CppQuickFixSettings::MissingNamespaceHandling::CreateMissing;
+ s->setterParameterNameTemplate = "value";
+ s->getterNameTemplate = "get<Name>";
+ s->setterInCppFileFrom = 1;
+ s->getterInCppFileFrom = 1;
+ GenerateGetterSetter factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 2);
+ }
+
+ void testNamespaceHandlingAddUsing_data()
+ {
+ QTest::addColumn<QByteArrayList>("headers");
+ QTest::addColumn<QByteArrayList>("sources");
+
+ QByteArray originalSource;
+ QByteArray expectedSource;
+
+ const QByteArray originalHeader = "namespace N1 {\n"
+ "namespace N2 {\n"
+ "class Something\n"
+ "{\n"
+ " int @it;\n"
+ "};\n"
+ "}\n"
+ "}\n";
+ const QByteArray expectedHeader = "namespace N1 {\n"
+ "namespace N2 {\n"
+ "class Something\n"
+ "{\n"
+ " int it;\n"
+ "\n"
+ "public:\n"
+ " void setIt(int value);\n"
+ "};\n"
+ "}\n"
+ "}\n";
+
+ originalSource = "#include \"file.h\"\n";
+ expectedSource = "#include \"file.h\"\n\n"
+ "using namespace N1::N2;\n"
+ "void Something::setIt(int value)\n"
+ "{\n"
+ " it = value;\n"
+ "}\n";
+ QTest::addRow("add using namespaces") << QByteArrayList{originalHeader, expectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+
+ const QByteArray unnamedOriginalHeader = "namespace {\n" + originalHeader + "}\n";
+ const QByteArray unnamedExpectedHeader = "namespace {\n" + expectedHeader + "}\n";
+
+ originalSource = "#include \"file.h\"\n"
+ "namespace N2 {} // decoy\n"
+ "namespace {\n"
+ "namespace N1 {\n"
+ "namespace {\n"
+ "}\n"
+ "}\n"
+ "}\n";
+ expectedSource = "#include \"file.h\"\n"
+ "namespace N2 {} // decoy\n"
+ "namespace {\n"
+ "namespace N1 {\n"
+ "using namespace N2;\n"
+ "void Something::setIt(int value)\n"
+ "{\n"
+ " it = value;\n"
+ "}\n"
+ "namespace {\n"
+ "}\n"
+ "}\n"
+ "}\n";
+ QTest::addRow("insert using namespace into unnamed nested (with decoy)")
+ << QByteArrayList{unnamedOriginalHeader, unnamedExpectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalSource = "#include \"file.h\"\n";
+ expectedSource = "#include \"file.h\"\n\n"
+ "using namespace N1::N2;\n"
+ "void Something::setIt(int value)\n"
+ "{\n"
+ " it = value;\n"
+ "}\n";
+ QTest::addRow("insert using namespace into unnamed")
+ << QByteArrayList{unnamedOriginalHeader, unnamedExpectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalSource = "#include \"file.h\"\n"
+ "namespace N2 {} // decoy\n"
+ "namespace {\n"
+ "namespace N1 {\n"
+ "namespace {\n"
+ "}\n"
+ "}\n"
+ "}\n";
+ expectedSource = "#include \"file.h\"\n"
+ "namespace N2 {} // decoy\n"
+ "namespace {\n"
+ "namespace N1 {\n"
+ "namespace {\n"
+ "}\n"
+ "}\n"
+ "}\n"
+ "\n"
+ "using namespace N1::N2;\n"
+ "void Something::setIt(int value)\n"
+ "{\n"
+ " it = value;\n"
+ "}\n";
+ QTest::addRow("insert using namespace (with decoy)")
+ << QByteArrayList{originalHeader, expectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+ }
+
+ void testNamespaceHandlingAddUsing()
+ {
+ QFETCH(QByteArrayList, headers);
+ QFETCH(QByteArrayList, sources);
+
+ QList<TestDocumentPtr> testDocuments(
+ {CppTestDocument::create("file.h", headers.at(0), headers.at(1)),
+ CppTestDocument::create("file.cpp", sources.at(0), sources.at(1))});
+
+ QuickFixSettings s;
+ s->cppFileNamespaceHandling = CppQuickFixSettings::MissingNamespaceHandling::AddUsingDirective;
+ s->setterParameterNameTemplate = "value";
+ s->setterInCppFileFrom = 1;
+
+ if (std::strstr(QTest::currentDataTag(), "unnamed nested") != nullptr)
+ QSKIP("TODO"); // FIXME
+ GenerateGetterSetter factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testNamespaceHandlingFullyQualify_data()
+ {
+ QTest::addColumn<QByteArrayList>("headers");
+ QTest::addColumn<QByteArrayList>("sources");
+
+ QByteArray originalSource;
+ QByteArray expectedSource;
+
+ const QByteArray originalHeader = "namespace N1 {\n"
+ "namespace N2 {\n"
+ "class Something\n"
+ "{\n"
+ " int @it;\n"
+ "};\n"
+ "}\n"
+ "}\n";
+ const QByteArray expectedHeader = "namespace N1 {\n"
+ "namespace N2 {\n"
+ "class Something\n"
+ "{\n"
+ " int it;\n"
+ "\n"
+ "public:\n"
+ " void setIt(int value);\n"
+ "};\n"
+ "}\n"
+ "}\n";
+
+ originalSource = "#include \"file.h\"\n";
+ expectedSource = "#include \"file.h\"\n\n"
+ "void N1::N2::Something::setIt(int value)\n"
+ "{\n"
+ " it = value;\n"
+ "}\n";
+ QTest::addRow("fully qualify") << QByteArrayList{originalHeader, expectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalSource = "#include \"file.h\"\n"
+ "namespace N2 {} // decoy\n";
+ expectedSource = "#include \"file.h\"\n"
+ "namespace N2 {} // decoy\n"
+ "\n"
+ "void N1::N2::Something::setIt(int value)\n"
+ "{\n"
+ " it = value;\n"
+ "}\n";
+ QTest::addRow("fully qualify (with decoy)") << QByteArrayList{originalHeader, expectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalSource = "#include \"file.h\"\n"
+ "namespace N2 {} // decoy\n"
+ "namespace {\n"
+ "namespace N1 {\n"
+ "namespace {\n"
+ "}\n"
+ "}\n"
+ "}\n";
+ expectedSource = "#include \"file.h\"\n"
+ "namespace N2 {} // decoy\n"
+ "namespace {\n"
+ "namespace N1 {\n"
+ "namespace {\n"
+ "}\n"
+ "}\n"
+ "}\n"
+ "\n"
+ "void N1::N2::Something::setIt(int value)\n"
+ "{\n"
+ " it = value;\n"
+ "}\n";
+ QTest::addRow("qualify in inner namespace (with decoy)")
+ << QByteArrayList{originalHeader, expectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+
+ const QByteArray unnamedOriginalHeader = "namespace {\n" + originalHeader + "}\n";
+ const QByteArray unnamedExpectedHeader = "namespace {\n" + expectedHeader + "}\n";
+
+ originalSource = "#include \"file.h\"\n"
+ "namespace N2 {} // decoy\n"
+ "namespace {\n"
+ "namespace N1 {\n"
+ "namespace {\n"
+ "}\n"
+ "}\n"
+ "}\n";
+ expectedSource = "#include \"file.h\"\n"
+ "namespace N2 {} // decoy\n"
+ "namespace {\n"
+ "namespace N1 {\n"
+ "void N2::Something::setIt(int value)\n"
+ "{\n"
+ " it = value;\n"
+ "}\n"
+ "namespace {\n"
+ "}\n"
+ "}\n"
+ "}\n";
+ QTest::addRow("qualify in inner namespace unnamed nested (with decoy)")
+ << QByteArrayList{unnamedOriginalHeader, unnamedExpectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalSource = "#include \"file.h\"\n";
+ expectedSource = "#include \"file.h\"\n\n"
+ "void N1::N2::Something::setIt(int value)\n"
+ "{\n"
+ " it = value;\n"
+ "}\n";
+ QTest::addRow("qualify in unnamed namespace")
+ << QByteArrayList{unnamedOriginalHeader, unnamedExpectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+ }
+
+ void testNamespaceHandlingFullyQualify()
+ {
+ QFETCH(QByteArrayList, headers);
+ QFETCH(QByteArrayList, sources);
+
+ QList<TestDocumentPtr> testDocuments(
+ {CppTestDocument::create("file.h", headers.at(0), headers.at(1)),
+ CppTestDocument::create("file.cpp", sources.at(0), sources.at(1))});
+
+ QuickFixSettings s;
+ s->cppFileNamespaceHandling = CppQuickFixSettings::MissingNamespaceHandling::RewriteType;
+ s->setterParameterNameTemplate = "value";
+ s->setterInCppFileFrom = 1;
+
+ if (std::strstr(QTest::currentDataTag(), "unnamed nested") != nullptr)
+ QSKIP("TODO"); // FIXME
+ GenerateGetterSetter factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testCustomNames_data()
+ {
+ QTest::addColumn<QByteArrayList>("headers");
+ QTest::addColumn<int>("operation");
+
+ QByteArray originalSource;
+ QByteArray expectedSource;
+
+ // Check if right names are created
+ originalSource = R"-(
+ class Test {
+ int m_fooBar_test@;
+ };
+)-";
+ expectedSource = R"-(
+ class Test {
+ int m_fooBar_test;
+
+ public:
+ int give_me_foo_bar_test() const
+ {
+ return m_fooBar_test;
+ }
+ void Seet_FooBar_test(int New_Foo_Bar_Test)
+ {
+ if (m_fooBar_test == New_Foo_Bar_Test)
+ return;
+ m_fooBar_test = New_Foo_Bar_Test;
+ emit newFooBarTestValue();
+ }
+ void set_fooBarTest_toDefault()
+ {
+ Seet_FooBar_test({}); // TODO: Adapt to use your actual default value
+ }
+
+ 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 FINAL)
+ };
+)-";
+ QTest::addRow("create right names") << QByteArrayList{originalSource, expectedSource} << 4;
+
+ // Check if not triggered with custom names
+ originalSource = R"-(
+ class Test {
+ int m_fooBar_test@;
+
+ public:
+ int give_me_foo_bar_test() const
+ {
+ return m_fooBar_test;
+ }
+ void Seet_FooBar_test(int New_Foo_Bar_Test)
+ {
+ if (m_fooBar_test == New_Foo_Bar_Test)
+ return;
+ m_fooBar_test = New_Foo_Bar_Test;
+ emit newFooBarTestValue();
+ }
+ void set_fooBarTest_toDefault()
+ {
+ Seet_FooBar_test({}); // TODO: Adapt to use your actual default value
+ }
+
+ 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 FINAL)
+ };
+)-";
+ expectedSource = "";
+ QTest::addRow("everything already exists") << QByteArrayList{originalSource, expectedSource} << 4;
+
+ // 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 FINAL)
+
+ public:
+ int give_me_foo_bar_test() const
+ {
+ return mem_fooBar_test;
+ }
+ void Seet_FooBar_test(int New_Foo_Bar_Test)
+ {
+ if (mem_fooBar_test == New_Foo_Bar_Test)
+ return;
+ mem_fooBar_test = New_Foo_Bar_Test;
+ emit newFooBarTestValue();
+ }
+ void set_fooBarTest_toDefault()
+ {
+ Seet_FooBar_test({}); // TODO: Adapt to use your actual default value
+ }
+
+ signals:
+ void newFooBarTestValue();
+ };
+)-";
+ 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 FINAL)
+
+ public:
+ int give_me_foo_bar_test() const
+ {
+ return mem_fooBar_test;
+ }
+ void Seet_FooBar_test(int New_Foo_Bar_Test)
+ {
+ if (mem_fooBar_test == New_Foo_Bar_Test)
+ return;
+ mem_fooBar_test = New_Foo_Bar_Test;
+ emit newFooBarTestValue();
+ }
+ void set_fooBarTest_toDefault()
+ {
+ Seet_FooBar_test({}); // TODO: Adapt to use your actual default value
+ }
+
+ signals:
+ void newFooBarTestValue();
+ private:
+ int mem_fooBar_test;
+ };
+)-";
+ QTest::addRow("create only member variable")
+ << QByteArrayList{originalSource, expectedSource} << 0;
+
+ // 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 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 FINAL)
+ int mem_fooBar_test;
+ public:
+ int give_me_foo_bar_test() const
+ {
+ return mem_fooBar_test;
+ }
+ void Seet_FooBar_test(int New_Foo_Bar_Test)
+ {
+ if (mem_fooBar_test == New_Foo_Bar_Test)
+ return;
+ mem_fooBar_test = New_Foo_Bar_Test;
+ emit newFooBarTestValue();
+ }
+ void set_fooBarTest_toDefault()
+ {
+ Seet_FooBar_test({}); // TODO: Adapt to use your actual default value
+ }
+ signals:
+ void newFooBarTestValue();
+ };
+)-";
+ QTest::addRow("create methods with given member variable")
+ << QByteArrayList{originalSource, expectedSource} << 0;
+ }
+
+ void testCustomNames()
+ {
+ QFETCH(QByteArrayList, headers);
+ QFETCH(int, operation);
+
+ QList<TestDocumentPtr> testDocuments(
+ {CppTestDocument::create("file.h", headers.at(0), headers.at(1))});
+
+ QuickFixSettings s;
+ s->setterInCppFileFrom = 0;
+ s->getterInCppFileFrom = 0;
+ s->setterNameTemplate = "Seet_<Name>";
+ s->getterNameTemplate = "give_me_<snake>";
+ s->signalNameTemplate = "new<Camel>Value";
+ s->setterParameterNameTemplate = "New_<Snake>";
+ s->resetNameTemplate = "set_<camel>_toDefault";
+ s->memberVariableNameTemplate = "mem_<name>";
+ if (operation == 0) {
+ InsertQtPropertyMembers factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), operation);
+ } else {
+ GenerateGetterSetter factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), operation);
+ }
+ }
+
+ void testValueTypes_data()
+ {
+ QTest::addColumn<QByteArrayList>("headers");
+ QTest::addColumn<int>("operation");
+
+ QByteArray originalSource;
+ QByteArray expectedSource;
+
+ // int should be a value type
+ originalSource = R"-(
+ class Test {
+ int i@;
+ };
+)-";
+ expectedSource = R"-(
+ class Test {
+ int i;
+
+ public:
+ int getI() const
+ {
+ return i;
+ }
+ };
+)-";
+ QTest::addRow("int") << QByteArrayList{originalSource, expectedSource} << 1;
+
+ // return type should be only int without const
+ originalSource = R"-(
+ class Test {
+ const int i@;
+ };
+)-";
+ expectedSource = R"-(
+ class Test {
+ const int i;
+
+ public:
+ int getI() const
+ {
+ return i;
+ }
+ };
+)-";
+ QTest::addRow("const int") << QByteArrayList{originalSource, expectedSource} << 0;
+
+ // float should be a value type
+ originalSource = R"-(
+ class Test {
+ float f@;
+ };
+)-";
+ expectedSource = R"-(
+ class Test {
+ float f;
+
+ public:
+ float getF() const
+ {
+ return f;
+ }
+ };
+)-";
+ QTest::addRow("float") << QByteArrayList{originalSource, expectedSource} << 1;
+
+ // pointer should be a value type
+ originalSource = R"-(
+ class Test {
+ void* v@;
+ };
+)-";
+ expectedSource = R"-(
+ class Test {
+ void* v;
+
+ public:
+ void *getV() const
+ {
+ return v;
+ }
+ };
+)-";
+ QTest::addRow("pointer") << QByteArrayList{originalSource, expectedSource} << 1;
+
+ // reference should be a value type (setter is const ref)
+ originalSource = R"-(
+ class Test {
+ int& r@;
+ };
+)-";
+ expectedSource = R"-(
+ class Test {
+ int& r;
+
+ public:
+ int &getR() const
+ {
+ return r;
+ }
+ void setR(const int &newR)
+ {
+ r = newR;
+ }
+ };
+)-";
+ QTest::addRow("reference to value type") << QByteArrayList{originalSource, expectedSource} << 2;
+
+ // reference should be a value type
+ originalSource = R"-(
+ using bar = int;
+ class Test {
+ bar i@;
+ };
+)-";
+ expectedSource = R"-(
+ using bar = int;
+ class Test {
+ bar i;
+
+ public:
+ bar getI() const
+ {
+ return i;
+ }
+ };
+)-";
+ QTest::addRow("value type through using") << QByteArrayList{originalSource, expectedSource} << 1;
+
+ // enum should be a value type
+ originalSource = R"-(
+ enum Foo{V1, V2};
+ class Test {
+ Foo e@;
+ };
+)-";
+ expectedSource = R"-(
+ enum Foo{V1, V2};
+ class Test {
+ Foo e;
+
+ public:
+ Foo getE() const
+ {
+ return e;
+ }
+ };
+)-";
+ QTest::addRow("enum") << QByteArrayList{originalSource, expectedSource} << 1;
+
+ // class should not be a value type
+ originalSource = R"-(
+ class NoVal{};
+ class Test {
+ NoVal n@;
+ };
+)-";
+ expectedSource = R"-(
+ class NoVal{};
+ class Test {
+ NoVal n;
+
+ public:
+ const NoVal &getN() const
+ {
+ return n;
+ }
+ };
+)-";
+ QTest::addRow("class") << QByteArrayList{originalSource, expectedSource} << 1;
+
+ // custom classes can be a value type
+ originalSource = R"-(
+ class Value{};
+ class Test {
+ Value v@;
+ };
+)-";
+ expectedSource = R"-(
+ class Value{};
+ class Test {
+ Value v;
+
+ public:
+ Value getV() const
+ {
+ return v;
+ }
+ };
+)-";
+ QTest::addRow("value class") << QByteArrayList{originalSource, expectedSource} << 1;
+
+ // custom classes (in namespace) can be a value type
+ originalSource = R"-(
+ namespace N1{
+ class Value{};
+ }
+ class Test {
+ N1::Value v@;
+ };
+)-";
+ expectedSource = R"-(
+ namespace N1{
+ class Value{};
+ }
+ class Test {
+ N1::Value v;
+
+ public:
+ N1::Value getV() const
+ {
+ return v;
+ }
+ };
+)-";
+ QTest::addRow("value class in namespace") << QByteArrayList{originalSource, expectedSource} << 1;
+
+ // custom template class can be a value type
+ originalSource = R"-(
+ template<typename T>
+ class Value{};
+ class Test {
+ Value<int> v@;
+ };
+)-";
+ expectedSource = R"-(
+ template<typename T>
+ class Value{};
+ class Test {
+ Value<int> v;
+
+ public:
+ Value<int> getV() const
+ {
+ return v;
+ }
+ };
+)-";
+ QTest::addRow("value template class") << QByteArrayList{originalSource, expectedSource} << 1;
+ }
+
+ void testValueTypes()
+ {
+ QFETCH(QByteArrayList, headers);
+ QFETCH(int, operation);
+
+ QList<TestDocumentPtr> testDocuments(
+ {CppTestDocument::create("file.h", headers.at(0), headers.at(1))});
+
+ QuickFixSettings s;
+ s->setterInCppFileFrom = 0;
+ s->getterInCppFileFrom = 0;
+ s->getterNameTemplate = "get<Name>";
+ s->valueTypes << "Value";
+ s->returnByConstRef = true;
+
+ GenerateGetterSetter factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), operation);
+ }
+
+ /// Checks: Use template for a custom type
+ void testCustomTemplate()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ const QByteArray customTypeDecl = R"--(
+namespace N1 {
+namespace N2 {
+struct test{};
+}
+template<typename T>
+struct custom {
+ void assign(const custom<T>&);
+ bool equals(const custom<T>&);
+ T* get();
+};
+)--";
+ // Header File
+ original = customTypeDecl + R"--(
+class Foo
+{
+public:
+ custom<N2::test> bar@;
+};
+})--";
+ expected = customTypeDecl + R"--(
+class Foo
+{
+public:
+ custom<N2::test> bar@;
+ N2::test *getBar() const;
+ void setBar(const custom<N2::test> &newBar);
+signals:
+ void barChanged(N2::test *bar);
+private:
+ Q_PROPERTY(N2::test *bar READ getBar NOTIFY barChanged FINAL)
+};
+})--";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original = "";
+ expected = R"-(
+using namespace N1;
+N2::test *Foo::getBar() const
+{
+ return bar.get();
+}
+
+void Foo::setBar(const custom<N2::test> &newBar)
+{
+ if (bar.equals(newBar))
+ return;
+ bar.assign(newBar);
+ emit barChanged(bar.get());
+}
+)-";
+
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ QuickFixSettings s;
+ s->cppFileNamespaceHandling = CppQuickFixSettings::MissingNamespaceHandling::AddUsingDirective;
+ s->getterNameTemplate = "get<Name>";
+ s->getterInCppFileFrom = 1;
+ s->signalWithNewValue = true;
+ CppQuickFixSettings::CustomTemplate t;
+ t.types.append("custom");
+ t.equalComparison = "<cur>.equals(<new>)";
+ t.returnExpression = "<cur>.get()";
+ t.returnType = "<T> *";
+ t.assignment = "<cur>.assign(<new>)";
+ s->customTemplates.push_back(t);
+
+ GenerateGetterSetter factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 5);
+ }
+
+ /// Checks: if the setter parameter name is the same as the member variable name, this-> is needed
+ void testNeedThis()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ // Header File
+ const QByteArray original = R"-(
+ class Foo {
+ int bar@;
+ public:
+ };
+)-";
+ const QByteArray expected = R"-(
+ class Foo {
+ int bar@;
+ public:
+ void setBar(int bar)
+ {
+ this->bar = bar;
+ }
+ };
+)-";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ QuickFixSettings s;
+ s->setterParameterNameTemplate = "<name>";
+ s->setterInCppFileFrom = 0;
+
+ GenerateGetterSetter factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 0);
+ }
+
+ void testOfferedFixes_data()
+ {
+ QTest::addColumn<QByteArray>("header");
+ QTest::addColumn<QStringList>("offered");
+
+ QByteArray header;
+ QStringList offered;
+ const QString setter = QStringLiteral("Generate Setter");
+ const QString getter = QStringLiteral("Generate Getter");
+ const QString getset = QStringLiteral("Generate Getter and Setter");
+ const QString constQandMissing = QStringLiteral(
+ "Generate Constant Q_PROPERTY and Missing Members");
+ const QString qAndResetAndMissing = QStringLiteral(
+ "Generate Q_PROPERTY and Missing Members with Reset Function");
+ const QString qAndMissing = QStringLiteral("Generate Q_PROPERTY and Missing Members");
+ const QStringList all{setter, getter, getset, constQandMissing, qAndResetAndMissing, qAndMissing};
+
+ header = R"-(
+ class Foo {
+ static int bar@;
+ };
+)-";
+ offered = QStringList{setter, getter, getset, constQandMissing};
+ QTest::addRow("static") << header << offered;
+
+ header = R"-(
+ class Foo {
+ static const int bar@;
+ };
+)-";
+ offered = QStringList{getter, constQandMissing};
+ QTest::addRow("const static") << header << offered;
+
+ header = R"-(
+ class Foo {
+ const int bar@;
+ };
+)-";
+ offered = QStringList{getter, constQandMissing};
+ QTest::addRow("const") << header << offered;
+
+ header = R"-(
+ class Foo {
+ const int bar@;
+ int getBar() const;
+ };
+)-";
+ offered = QStringList{constQandMissing};
+ QTest::addRow("const + getter") << header << offered;
+
+ header = R"-(
+ class Foo {
+ const int bar@;
+ int getBar() const;
+ void setBar(int value);
+ };
+)-";
+ offered = QStringList{};
+ QTest::addRow("const + getter + setter") << header << offered;
+
+ header = R"-(
+ class Foo {
+ const int* bar@;
+ };
+)-";
+ offered = all;
+ QTest::addRow("pointer to const") << header << offered;
+
+ header = R"-(
+ class Foo {
+ int bar@;
+ public:
+ int bar();
+ };
+)-";
+ offered = QStringList{setter, constQandMissing, qAndResetAndMissing, qAndMissing};
+ QTest::addRow("existing getter") << header << offered;
+
+ header = R"-(
+ class Foo {
+ int bar@;
+ public:
+ set setBar(int);
+ };
+)-";
+ offered = QStringList{getter};
+ QTest::addRow("existing setter") << header << offered;
+
+ header = R"-(
+ class Foo {
+ int bar@;
+ signals:
+ void barChanged(int);
+ };
+)-";
+ offered = QStringList{setter, getter, getset, qAndResetAndMissing, qAndMissing};
+ QTest::addRow("existing signal (no const Q_PROPERTY)") << header << offered;
+
+ header = R"-(
+ class Foo {
+ int m_bar@;
+ Q_PROPERTY(int bar)
+ };
+)-";
+ offered = QStringList{}; // user should use "InsertQPropertyMembers", no duplicated code
+ QTest::addRow("existing Q_PROPERTY") << header << offered;
+ }
+
+ void testOfferedFixes()
+ {
+ QFETCH(QByteArray, header);
+ QFETCH(QStringList, offered);
+
+ QList<TestDocumentPtr> testDocuments(
+ {CppTestDocument::create("file.h", header, header)});
+
+ GenerateGetterSetter factory;
+ QuickFixOfferedOperationsTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), offered);
+ }
+
+ void testGeneral_data()
+ {
+ QTest::addColumn<int>("operation");
+ QTest::addColumn<QByteArray>("original");
+ QTest::addColumn<QByteArray>("expected");
+
+ QTest::newRow("GenerateGetterSetter_referenceToNonConst")
+ << 2
+ << QByteArray("\n"
+ "class Something\n"
+ "{\n"
+ " int &it@;\n"
+ "};\n")
+ << QByteArray("\n"
+ "class Something\n"
+ "{\n"
+ " int &it;\n"
+ "\n"
+ "public:\n"
+ " int &getIt() const;\n"
+ " void setIt(const int &it);\n"
+ "};\n"
+ "\n"
+ "int &Something::getIt() const\n"
+ "{\n"
+ " return it;\n"
+ "}\n"
+ "\n"
+ "void Something::setIt(const int &it)\n"
+ "{\n"
+ " this->it = it;\n"
+ "}\n");
+
+ // Checks: No special treatment for reference to const.
+ QTest::newRow("GenerateGetterSetter_referenceToConst")
+ << 2
+ << QByteArray("\n"
+ "class Something\n"
+ "{\n"
+ " const int &it@;\n"
+ "};\n")
+ << QByteArray("\n"
+ "class Something\n"
+ "{\n"
+ " const int &it;\n"
+ "\n"
+ "public:\n"
+ " const int &getIt() const;\n"
+ " void setIt(const int &it);\n"
+ "};\n"
+ "\n"
+ "const int &Something::getIt() const\n"
+ "{\n"
+ " return it;\n"
+ "}\n"
+ "\n"
+ "void Something::setIt(const int &it)\n"
+ "{\n"
+ " this->it = it;\n"
+ "}\n");
+
+ // Checks:
+ // 1. Setter: Setter is a static function.
+ // 2. Getter: Getter is a static, non const function.
+ QTest::newRow("GenerateGetterSetter_staticMember")
+ << 2
+ << QByteArray("\n"
+ "class Something\n"
+ "{\n"
+ " static int @m_member;\n"
+ "};\n")
+ << QByteArray("\n"
+ "class Something\n"
+ "{\n"
+ " static int m_member;\n"
+ "\n"
+ "public:\n"
+ " static int member();\n"
+ " static void setMember(int member);\n"
+ "};\n"
+ "\n"
+ "int Something::member()\n"
+ "{\n"
+ " return m_member;\n"
+ "}\n"
+ "\n"
+ "void Something::setMember(int member)\n"
+ "{\n"
+ " m_member = member;\n"
+ "}\n");
+
+ // Check: Check if it works on the second declarator
+ // clang-format off
+ QTest::newRow("GenerateGetterSetter_secondDeclarator") << 2
+ << QByteArray("\n"
+ "class Something\n"
+ "{\n"
+ " int *foo, @it;\n"
+ "};\n")
+ << QByteArray("\n"
+ "class Something\n"
+ "{\n"
+ " int *foo, it;\n"
+ "\n"
+ "public:\n"
+ " int getIt() const;\n"
+ " void setIt(int it);\n"
+ "};\n"
+ "\n"
+ "int Something::getIt() const\n"
+ "{\n"
+ " return it;\n"
+ "}\n"
+ "\n"
+ "void Something::setIt(int it)\n"
+ "{\n"
+ " this->it = it;\n"
+ "}\n");
+ // clang-format on
+
+ // Check: Quick fix is offered for "int *@it;" ('@' denotes the text cursor position)
+ QTest::newRow("GenerateGetterSetter_triggeringRightAfterPointerSign")
+ << 2
+ << QByteArray("\n"
+ "class Something\n"
+ "{\n"
+ " int *@it;\n"
+ "};\n")
+ << QByteArray("\n"
+ "class Something\n"
+ "{\n"
+ " int *it;\n"
+ "\n"
+ "public:\n"
+ " int *getIt() const;\n"
+ " void setIt(int *it);\n"
+ "};\n"
+ "\n"
+ "int *Something::getIt() const\n"
+ "{\n"
+ " return it;\n"
+ "}\n"
+ "\n"
+ "void Something::setIt(int *it)\n"
+ "{\n"
+ " this->it = it;\n"
+ "}\n");
+
+ // Checks if "m_" is recognized as "m" with the postfix "_" and not simply as "m_" prefix.
+ QTest::newRow("GenerateGetterSetter_recognizeMasVariableName")
+ << 2
+ << QByteArray("\n"
+ "class Something\n"
+ "{\n"
+ " int @m_;\n"
+ "};\n")
+ << QByteArray("\n"
+ "class Something\n"
+ "{\n"
+ " int m_;\n"
+ "\n"
+ "public:\n"
+ " int m() const;\n"
+ " void setM(int m);\n"
+ "};\n"
+ "\n"
+ "int Something::m() const\n"
+ "{\n"
+ " return m_;\n"
+ "}\n"
+ "\n"
+ "void Something::setM(int m)\n"
+ "{\n"
+ " m_ = m;\n"
+ "}\n");
+
+ // Checks if "m" followed by an upper character is recognized as a prefix
+ QTest::newRow("GenerateGetterSetter_recognizeMFollowedByCapital")
+ << 2
+ << QByteArray("\n"
+ "class Something\n"
+ "{\n"
+ " int @mFoo;\n"
+ "};\n")
+ << QByteArray("\n"
+ "class Something\n"
+ "{\n"
+ " int mFoo;\n"
+ "\n"
+ "public:\n"
+ " int foo() const;\n"
+ " void setFoo(int foo);\n"
+ "};\n"
+ "\n"
+ "int Something::foo() const\n"
+ "{\n"
+ " return mFoo;\n"
+ "}\n"
+ "\n"
+ "void Something::setFoo(int foo)\n"
+ "{\n"
+ " mFoo = foo;\n"
+ "}\n");
+ }
+
+ void testGeneral()
+ {
+ QFETCH(int, operation);
+ QFETCH(QByteArray, original);
+ QFETCH(QByteArray, expected);
+
+ QuickFixSettings s;
+ s->setterParameterNameTemplate = "<name>";
+ s->getterInCppFileFrom = 1;
+ s->setterInCppFileFrom = 1;
+
+ GenerateGetterSetter factory;
+ QuickFixOperationTest(singleDocument(original, expected),
+ &factory,
+ ProjectExplorer::HeaderPaths(),
+ operation);
+ }
+
+ /// Checks: Only generate getter
+ void testOnlyGetter()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "class Foo\n"
+ "{\n"
+ "public:\n"
+ " int bar@;\n"
+ "};\n";
+ expected =
+ "class Foo\n"
+ "{\n"
+ "public:\n"
+ " int bar@;\n"
+ " int getBar() const;\n"
+ "};\n";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original.resize(0);
+ expected =
+ "\n"
+ "int Foo::getBar() const\n"
+ "{\n"
+ " return bar;\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ QuickFixSettings s;
+ s->getterInCppFileFrom = 1;
+ s->getterNameTemplate = "get<Name>";
+ GenerateGetterSetter factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 1);
+ }
+
+ /// Checks: Only generate setter
+ void testOnlySetter()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+ QuickFixSettings s;
+ s->setterAsSlot = true; // To be ignored, as we don't have QObjects here.
+
+ // Header File
+ original =
+ "class Foo\n"
+ "{\n"
+ "public:\n"
+ " int bar@;\n"
+ "};\n";
+ expected =
+ "class Foo\n"
+ "{\n"
+ "public:\n"
+ " int bar@;\n"
+ " void setBar(int value);\n"
+ "};\n";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original.resize(0);
+ expected =
+ "\n"
+ "void Foo::setBar(int value)\n"
+ "{\n"
+ " bar = value;\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ s->setterInCppFileFrom = 1;
+ s->setterParameterNameTemplate = "value";
+
+ GenerateGetterSetter factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 0);
+ }
+
+ void testAnonymousClass()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+ QuickFixSettings s;
+ s->setterInCppFileFrom = 1;
+ s->setterParameterNameTemplate = "value";
+
+ // Header File
+ original = R"(
+ class {
+ int @m_foo;
+ } bar;
+)";
+ expected = R"(
+ class {
+ int m_foo;
+
+ public:
+ int foo() const
+ {
+ return m_foo;
+ }
+ void setFoo(int value)
+ {
+ if (m_foo == value)
+ return;
+ m_foo = value;
+ emit fooChanged();
+ }
+ void resetFoo()
+ {
+ setFoo({}); // TODO: Adapt to use your actual default defaultValue
+ }
+
+ signals:
+ void fooChanged();
+
+ private:
+ Q_PROPERTY(int foo READ foo WRITE setFoo RESET resetFoo NOTIFY fooChanged FINAL)
+ } bar;
+)";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ testDocuments << CppTestDocument::create("file.cpp", {}, {});
+
+ GenerateGetterSetter factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 4);
+ }
+
+ void testInlineInHeaderFile()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ const QByteArray original = R"-(
+ class Foo {
+ public:
+ int bar@;
+ };
+)-";
+ const QByteArray expected = R"-(
+ class Foo {
+ public:
+ int bar;
+ int getBar() const;
+ void setBar(int value);
+ void resetBar();
+ signals:
+ void barChanged();
+ private:
+ Q_PROPERTY(int bar READ getBar WRITE setBar RESET resetBar NOTIFY barChanged FINAL)
+ };
+
+ inline int Foo::getBar() const
+ {
+ return bar;
+ }
+
+ inline void Foo::setBar(int value)
+ {
+ if (bar == value)
+ return;
+ bar = value;
+ emit barChanged();
+ }
+
+ inline void Foo::resetBar()
+ {
+ setBar({}); // TODO: Adapt to use your actual default defaultValue
+ }
+)-";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ QuickFixSettings s;
+ s->setterOutsideClassFrom = 1;
+ s->getterOutsideClassFrom = 1;
+ s->setterParameterNameTemplate = "value";
+ s->getterNameTemplate = "get<Name>";
+
+ GenerateGetterSetter factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 4);
+ }
+
+ void testOnlySetterHeaderFileWithIncludeGuard()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ const QByteArray original =
+ "#ifndef FILE__H__DECLARED\n"
+ "#define FILE__H__DECLARED\n"
+ "class Foo\n"
+ "{\n"
+ "public:\n"
+ " int bar@;\n"
+ "};\n"
+ "#endif\n";
+ const QByteArray expected =
+ "#ifndef FILE__H__DECLARED\n"
+ "#define FILE__H__DECLARED\n"
+ "class Foo\n"
+ "{\n"
+ "public:\n"
+ " int bar@;\n"
+ " void setBar(int value);\n"
+ "};\n\n"
+ "inline void Foo::setBar(int value)\n"
+ "{\n"
+ " bar = value;\n"
+ "}\n"
+ "#endif\n";
+
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ QuickFixSettings s;
+ s->setterOutsideClassFrom = 1;
+ s->setterParameterNameTemplate = "value";
+
+ GenerateGetterSetter factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 0);
+ }
+
+ void testFunctionAsTemplateArg()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ const QByteArray original = R"(
+ template<typename T> class TS {};
+ template<typename T, typename U> class TS<T(U)> {};
+
+ class S2 {
+ TS<int(int)> @member;
+ };
+)";
+ const QByteArray expected = R"(
+ template<typename T> class TS {};
+ template<typename T, typename U> class TS<T(U)> {};
+
+ class S2 {
+ TS<int(int)> member;
+
+ public:
+ const TS<int (int)> &getMember() const
+ {
+ return member;
+ }
+ };
+)";
+
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ QuickFixSettings s;
+ s->getterOutsideClassFrom = 0;
+ s->getterInCppFileFrom = 0;
+ s->getterNameTemplate = "get<Name>";
+ s->returnByConstRef = true;
+
+ GenerateGetterSetter factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 1);
+ }
+
+ void testNotTriggeringOnMemberFunction()
+ {
+ const QByteArray input = "class Something { void @f(); };\n";
+ GenerateGetterSetter factory;
+ QuickFixOperationTest({CppTestDocument::create("file.h", input, {})}, &factory);
+ }
+
+ void testNotTriggeringOnMemberArray()
+ {
+ const QByteArray input = "class Something { void @a[10]; };\n";
+ GenerateGetterSetter factory;
+ QuickFixOperationTest({CppTestDocument::create("file.h", input, {})}, &factory);
+ }
+};
+
+class GenerateGettersSettersTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data()
+ {
+ QTest::addColumn<QByteArray>("original");
+ QTest::addColumn<QByteArray>("expected");
+
+ const QByteArray onlyReset = R"(
+ class Foo {
+ public:
+ int bar() const;
+ void setBar(int bar);
+ private:
+ int m_bar;
+ @};)";
+
+ const QByteArray onlyResetAfter = R"(
+ class @Foo {
+ public:
+ int bar() const;
+ void setBar(int bar);
+ void resetBar();
+
+ private:
+ int m_bar;
+ };
+ inline void Foo::resetBar()
+ {
+ setBar({}); // TODO: Adapt to use your actual default defaultValue
+ }
+)";
+ QTest::addRow("only reset") << onlyReset << onlyResetAfter;
+
+ const QByteArray withCandidates = R"(
+ class @Foo {
+ public:
+ int bar() const;
+ void setBar(int bar) { m_bar = bar; }
+
+ int getBar2() const;
+
+ int m_alreadyPublic;
+
+ private:
+ friend void distraction();
+ class AnotherDistraction {};
+ enum EvenMoreDistraction { val1, val2 };
+
+ int m_bar;
+ int bar2_;
+ QString bar3;
+ };)";
+ const QByteArray after = R"(
+ class Foo {
+ public:
+ int bar() const;
+ void setBar(int bar) { m_bar = bar; }
+
+ int getBar2() const;
+
+ int m_alreadyPublic;
+
+ void resetBar();
+ void setBar2(int value);
+ void resetBar2();
+ const QString &getBar3() const;
+ void setBar3(const QString &value);
+ void resetBar3();
+
+ signals:
+ void bar2Changed();
+ void bar3Changed();
+
+ private:
+ friend void distraction();
+ class AnotherDistraction {};
+ enum EvenMoreDistraction { val1, val2 };
+
+ int m_bar;
+ int bar2_;
+ QString bar3;
+ 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()
+ {
+ setBar({}); // TODO: Adapt to use your actual default defaultValue
+ }
+
+ inline void Foo::setBar2(int value)
+ {
+ if (bar2_ == value)
+ return;
+ bar2_ = value;
+ emit bar2Changed();
+ }
+
+ inline void Foo::resetBar2()
+ {
+ setBar2({}); // TODO: Adapt to use your actual default defaultValue
+ }
+
+ inline const QString &Foo::getBar3() const
+ {
+ return bar3;
+ }
+
+ inline void Foo::setBar3(const QString &value)
+ {
+ if (bar3 == value)
+ return;
+ bar3 = value;
+ emit bar3Changed();
+ }
+
+ inline void Foo::resetBar3()
+ {
+ setBar3({}); // TODO: Adapt to use your actual default defaultValue
+ }
+)";
+ QTest::addRow("with candidates") << withCandidates << after;
+ }
+
+ void test()
+ {
+ class TestFactory : public GenerateGettersSettersForClass
+ {
+ public:
+ TestFactory() { setTest(); }
+ };
+
+ QFETCH(QByteArray, original);
+ QFETCH(QByteArray, expected);
+
+ QuickFixSettings s;
+ s->getterNameTemplate = "get<Name>";
+ s->setterParameterNameTemplate = "value";
+ s->setterOutsideClassFrom = 1;
+ s->getterOutsideClassFrom = 1;
+ s->returnByConstRef = true;
+
+ TestFactory factory;
+ QuickFixOperationTest({CppTestDocument::create("file.h", original, expected)}, &factory);
+ }
+};
+
+class InsertQtPropertyMembersTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data()
+ {
+ QTest::addColumn<QByteArray>("original");
+ QTest::addColumn<QByteArray>("expected");
+
+ QTest::newRow("InsertQtPropertyMembers")
+ << QByteArray("struct QObject { void connect(); }\n"
+ "struct XmarksTheSpot : public QObject {\n"
+ " @Q_PROPERTY(int it READ getIt WRITE setIt RESET resetIt NOTIFY itChanged)\n"
+ "};\n")
+ << QByteArray("struct QObject { void connect(); }\n"
+ "struct XmarksTheSpot : public QObject {\n"
+ " Q_PROPERTY(int it READ getIt WRITE setIt RESET resetIt NOTIFY itChanged)\n"
+ "\n"
+ "public:\n"
+ " int getIt() const;\n"
+ "\n"
+ "public slots:\n"
+ " void setIt(int it)\n"
+ " {\n"
+ " if (m_it == it)\n"
+ " return;\n"
+ " m_it = it;\n"
+ " emit itChanged(m_it);\n"
+ " }\n"
+ " void resetIt()\n"
+ " {\n"
+ " setIt({}); // TODO: Adapt to use your actual default value\n"
+ " }\n"
+ "\n"
+ "signals:\n"
+ " void itChanged(int it);\n"
+ "\n"
+ "private:\n"
+ " int m_it;\n"
+ "};\n"
+ "\n"
+ "int XmarksTheSpot::getIt() const\n"
+ "{\n"
+ " return m_it;\n"
+ "}\n");
+
+ QTest::newRow("InsertQtPropertyMembersResetWithoutSet")
+ << QByteArray("struct QObject { void connect(); }\n"
+ "struct XmarksTheSpot : public QObject {\n"
+ " @Q_PROPERTY(int it READ getIt RESET resetIt NOTIFY itChanged)\n"
+ "};\n")
+ << QByteArray("struct QObject { void connect(); }\n"
+ "struct XmarksTheSpot : public QObject {\n"
+ " Q_PROPERTY(int it READ getIt RESET resetIt NOTIFY itChanged)\n"
+ "\n"
+ "public:\n"
+ " int getIt() const;\n"
+ "\n"
+ "public slots:\n"
+ " void resetIt()\n"
+ " {\n"
+ " static int defaultValue{}; // TODO: Adapt to use your actual default "
+ "value\n"
+ " if (m_it == defaultValue)\n"
+ " return;\n"
+ " m_it = defaultValue;\n"
+ " emit itChanged(m_it);\n"
+ " }\n"
+ "\n"
+ "signals:\n"
+ " void itChanged(int it);\n"
+ "\n"
+ "private:\n"
+ " int m_it;\n"
+ "};\n"
+ "\n"
+ "int XmarksTheSpot::getIt() const\n"
+ "{\n"
+ " return m_it;\n"
+ "}\n");
+
+ QTest::newRow("InsertQtPropertyMembersResetWithoutSetAndNotify")
+ << QByteArray("struct QObject { void connect(); }\n"
+ "struct XmarksTheSpot : public QObject {\n"
+ " @Q_PROPERTY(int it READ getIt RESET resetIt)\n"
+ "};\n")
+ << QByteArray("struct QObject { void connect(); }\n"
+ "struct XmarksTheSpot : public QObject {\n"
+ " Q_PROPERTY(int it READ getIt RESET resetIt)\n"
+ "\n"
+ "public:\n"
+ " int getIt() const;\n"
+ "\n"
+ "public slots:\n"
+ " void resetIt()\n"
+ " {\n"
+ " static int defaultValue{}; // TODO: Adapt to use your actual default "
+ "value\n"
+ " m_it = defaultValue;\n"
+ " }\n"
+ "\n"
+ "private:\n"
+ " int m_it;\n"
+ "};\n"
+ "\n"
+ "int XmarksTheSpot::getIt() const\n"
+ "{\n"
+ " return m_it;\n"
+ "}\n");
+
+ QTest::newRow("InsertQtPropertyMembersPrivateBeforePublic")
+ << QByteArray("struct QObject { void connect(); }\n"
+ "class XmarksTheSpot : public QObject {\n"
+ "private:\n"
+ " @Q_PROPERTY(int it READ getIt WRITE setIt NOTIFY itChanged)\n"
+ "public:\n"
+ " void find();\n"
+ "};\n")
+ << QByteArray("struct QObject { void connect(); }\n"
+ "class XmarksTheSpot : public QObject {\n"
+ "private:\n"
+ " Q_PROPERTY(int it READ getIt WRITE setIt NOTIFY itChanged)\n"
+ " int m_it;\n"
+ "\n"
+ "public:\n"
+ " void find();\n"
+ " int getIt() const;\n"
+ "public slots:\n"
+ " void setIt(int it)\n"
+ " {\n"
+ " if (m_it == it)\n"
+ " return;\n"
+ " m_it = it;\n"
+ " emit itChanged(m_it);\n"
+ " }\n"
+ "signals:\n"
+ " void itChanged(int it);\n"
+ "};\n"
+ "\n"
+ "int XmarksTheSpot::getIt() const\n"
+ "{\n"
+ " return m_it;\n"
+ "}\n");
+ }
+
+ void test()
+ {
+ QFETCH(QByteArray, original);
+ QFETCH(QByteArray, expected);
+
+ QuickFixSettings s;
+ s->setterAsSlot = true;
+ s->setterInCppFileFrom = 0;
+ s->setterParameterNameTemplate = "<name>";
+ s->signalWithNewValue = true;
+
+ InsertQtPropertyMembers factory;
+ QuickFixOperationTest({CppTestDocument::create("file.cpp", original, expected)}, &factory);
+ }
+
+ void testNotTriggeringOnInvalidCode()
+ {
+ const QByteArray input = "class C { @Q_PROPERTY(typeid foo READ foo) };\n";
+ InsertQtPropertyMembers factory;
+ QuickFixOperationTest({CppTestDocument::create("file.h", input, {})}, &factory);
+ }
+};
+
+class GenerateConstructorTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data()
+ {
+ QTest::addColumn<QByteArray>("original_header");
+ QTest::addColumn<QByteArray>("expected_header");
+ QTest::addColumn<QByteArray>("original_source");
+ QTest::addColumn<QByteArray>("expected_source");
+ QTest::addColumn<int>("location");
+ const int Inside = ConstructorLocation::Inside;
+ const int Outside = ConstructorLocation::Outside;
+ const int CppGenNamespace = ConstructorLocation::CppGenNamespace;
+ const int CppGenUsingDirective = ConstructorLocation::CppGenUsingDirective;
+ const int CppRewriteType = ConstructorLocation::CppRewriteType;
+
+ QByteArray header = R"--(
+class@ Foo{
+ int test;
+ static int s;
+};
+)--";
+ QByteArray expected = R"--(
+class Foo{
+ int test;
+ static int s;
+public:
+ Foo(int test) : test(test)
+ {}
+};
+)--";
+ QTest::newRow("ignore static") << header << expected << QByteArray() << QByteArray() << Inside;
+
+ header = R"--(
+class@ Foo{
+ CustomType test;
+};
+)--";
+ expected = R"--(
+class Foo{
+ CustomType test;
+public:
+ Foo(CustomType test) : test(std::move(test))
+ {}
+};
+)--";
+ QTest::newRow("Move custom value types")
+ << header << expected << QByteArray() << QByteArray() << Inside;
+
+ header = R"--(
+class@ Foo{
+ int test;
+protected:
+ Foo() = default;
+};
+)--";
+ expected = R"--(
+class Foo{
+ int test;
+public:
+ Foo(int test) : test(test)
+ {}
+
+protected:
+ Foo() = default;
+};
+)--";
+
+ QTest::newRow("new section before existing")
+ << header << expected << QByteArray() << QByteArray() << Inside;
+
+ header = R"--(
+class@ Foo{
+ int test;
+};
+)--";
+ expected = R"--(
+class Foo{
+ int test;
+public:
+ Foo(int test) : test(test)
+ {}
+};
+)--";
+ QTest::newRow("new section at end")
+ << header << expected << QByteArray() << QByteArray() << Inside;
+
+ header = R"--(
+class@ Foo{
+ int test;
+public:
+ /**
+ * Random comment
+ */
+ Foo(int i, int i2);
+};
+)--";
+ expected = R"--(
+class Foo{
+ int test;
+public:
+ Foo(int test) : test(test)
+ {}
+ /**
+ * Random comment
+ */
+ Foo(int i, int i2);
+};
+)--";
+ QTest::newRow("in section before")
+ << header << expected << QByteArray() << QByteArray() << Inside;
+
+ header = R"--(
+class@ Foo{
+ int test;
+public:
+ Foo() = default;
+};
+)--";
+ expected = R"--(
+class Foo{
+ int test;
+public:
+ Foo() = default;
+ Foo(int test) : test(test)
+ {}
+};
+)--";
+ QTest::newRow("in section after")
+ << header << expected << QByteArray() << QByteArray() << Inside;
+
+ header = R"--(
+class@ Foo{
+ int test1;
+ int test2;
+ int test3;
+public:
+};
+)--";
+ expected = R"--(
+class Foo{
+ int test1;
+ int test2;
+ int test3;
+public:
+ Foo(int test2, int test3, int test1) : test1(test1),
+ test2(test2),
+ test3(test3)
+ {}
+};
+)--";
+ // No worry, that is not the default behavior.
+ // Move first member to the back when testing with 3 or more members
+ QTest::newRow("changed parameter order")
+ << header << expected << QByteArray() << QByteArray() << Inside;
+
+ header = R"--(
+class@ Foo{
+ int test;
+ int di_test;
+public:
+};
+)--";
+ expected = R"--(
+class Foo{
+ int test;
+ int di_test;
+public:
+ Foo(int test, int di_test = 42) : test(test),
+ di_test(di_test)
+ {}
+};
+)--";
+ QTest::newRow("default parameters")
+ << header << expected << QByteArray() << QByteArray() << Inside;
+
+ header = R"--(
+struct Bar{
+ Bar(int i);
+};
+class@ Foo : public Bar{
+ int test;
+public:
+};
+)--";
+ expected = R"--(
+struct Bar{
+ Bar(int i);
+};
+class Foo : public Bar{
+ int test;
+public:
+ Foo(int test, int i) : Bar(i),
+ test(test)
+ {}
+};
+)--";
+ QTest::newRow("parent constructor")
+ << header << expected << QByteArray() << QByteArray() << Inside;
+
+ header = R"--(
+struct Bar{
+ Bar(int use_i = 6);
+};
+class@ Foo : public Bar{
+ int test;
+public:
+};
+)--";
+ expected = R"--(
+struct Bar{
+ Bar(int use_i = 6);
+};
+class Foo : public Bar{
+ int test;
+public:
+ Foo(int test, int use_i = 6) : Bar(use_i),
+ test(test)
+ {}
+};
+)--";
+ QTest::newRow("parent constructor with default")
+ << header << expected << QByteArray() << QByteArray() << Inside;
+
+ header = R"--(
+struct Bar{
+ Bar(int use_i = L'A', int use_i2 = u8"B");
+};
+class@ Foo : public Bar{
+public:
+};
+)--";
+ expected = R"--(
+struct Bar{
+ Bar(int use_i = L'A', int use_i2 = u8"B");
+};
+class Foo : public Bar{
+public:
+ Foo(int use_i = L'A', int use_i2 = u8"B") : Bar(use_i, use_i2)
+ {}
+};
+)--";
+ QTest::newRow("parent constructor with char/string default value")
+ << header << expected << QByteArray() << QByteArray() << Inside;
+
+ const QByteArray common = R"--(
+namespace N{
+ template<typename T>
+ struct vector{
+ };
+}
+)--";
+ header = common + R"--(
+namespace M{
+enum G{g};
+class@ Foo{
+ N::vector<G> g;
+ enum E{e}e;
+public:
+};
+}
+)--";
+
+ expected = common + R"--(
+namespace M{
+enum G{g};
+class@ Foo{
+ N::vector<G> g;
+ enum E{e}e;
+public:
+ Foo(const N::vector<G> &g, E e);
+};
+
+Foo::Foo(const N::vector<G> &g, Foo::E e) : g(g),
+ e(e)
+{}
+
+}
+)--";
+ QTest::newRow("source: right type outside class ")
+ << QByteArray() << QByteArray() << header << expected << Outside;
+ expected = common + R"--(
+namespace M{
+enum G{g};
+class@ Foo{
+ N::vector<G> g;
+ enum E{e}e;
+public:
+ Foo(const N::vector<G> &g, E e);
+};
+}
+
+
+inline M::Foo::Foo(const N::vector<M::G> &g, M::Foo::E e) : g(g),
+ e(e)
+{}
+
+)--";
+ QTest::newRow("header: right type outside class ")
+ << header << expected << QByteArray() << QByteArray() << Outside;
+
+ expected = common + R"--(
+namespace M{
+enum G{g};
+class@ Foo{
+ N::vector<G> g;
+ enum E{e}e;
+public:
+ Foo(const N::vector<G> &g, E e);
+};
+}
+)--";
+ const QByteArray source = R"--(
+#include "file.h"
+)--";
+ QByteArray expected_source = R"--(
+#include "file.h"
+
+
+namespace M {
+Foo::Foo(const N::vector<G> &g, Foo::E e) : g(g),
+ e(e)
+{}
+
+}
+)--";
+ QTest::newRow("source: right type inside namespace")
+ << header << expected << source << expected_source << CppGenNamespace;
+
+ expected_source = R"--(
+#include "file.h"
+
+using namespace M;
+Foo::Foo(const N::vector<G> &g, Foo::E e) : g(g),
+ e(e)
+{}
+)--";
+ QTest::newRow("source: right type with using directive")
+ << header << expected << source << expected_source << CppGenUsingDirective;
+
+ expected_source = R"--(
+#include "file.h"
+
+M::Foo::Foo(const N::vector<M::G> &g, M::Foo::E e) : g(g),
+ e(e)
+{}
+)--";
+ QTest::newRow("source: right type while rewritung types")
+ << header << expected << source << expected_source << CppRewriteType;
+
+ }
+
+ void test()
+ {
+ class TestFactory : public GenerateConstructor
+ {
+ public:
+ TestFactory() { setTest(); }
+ };
+
+ QFETCH(QByteArray, original_header);
+ QFETCH(QByteArray, expected_header);
+ QFETCH(QByteArray, original_source);
+ QFETCH(QByteArray, expected_source);
+ QFETCH(int, location);
+
+ QuickFixSettings s;
+ s->valueTypes << "CustomType";
+ using L = ConstructorLocation;
+ if (location == L::Inside) {
+ s->setterInCppFileFrom = -1;
+ s->setterOutsideClassFrom = -1;
+ } else if (location == L::Outside) {
+ s->setterInCppFileFrom = -1;
+ s->setterOutsideClassFrom = 1;
+ } else if (location >= L::CppGenNamespace && location <= L::CppRewriteType) {
+ s->setterInCppFileFrom = 1;
+ s->setterOutsideClassFrom = -1;
+ using Handling = CppQuickFixSettings::MissingNamespaceHandling;
+ if (location == L::CppGenNamespace)
+ s->cppFileNamespaceHandling = Handling::CreateMissing;
+ else if (location == L::CppGenUsingDirective)
+ s->cppFileNamespaceHandling = Handling::AddUsingDirective;
+ else if (location == L::CppRewriteType)
+ s->cppFileNamespaceHandling = Handling::RewriteType;
+ } else {
+ QFAIL("location is none of the values of the ConstructorLocation enum");
+ }
+
+ QList<TestDocumentPtr> testDocuments;
+ testDocuments << CppTestDocument::create("file.h", original_header, expected_header);
+ testDocuments << CppTestDocument::create("file.cpp", original_source, expected_source);
+ TestFactory factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+private:
+ enum ConstructorLocation { Inside, Outside, CppGenNamespace, CppGenUsingDirective, CppRewriteType };
+};
+
+QObject *GenerateGetterSetter::createTest()
+{
+ return new GenerateGetterSetterTest;
+}
+
+QObject *GenerateGettersSettersForClass::createTest()
+{
+ return new GenerateGettersSettersTest;
+}
+
+QObject *InsertQtPropertyMembers::createTest()
+{
+ return new InsertQtPropertyMembersTest;
+}
+
+QObject *GenerateConstructor::createTest()
+{
+ return new GenerateConstructorTest;
+}
+
+#endif // WITH_TESTS
+
+} // namespace
+
+void registerCodeGenerationQuickfixes()
+{
+ CppQuickFixFactory::registerFactory<GenerateGetterSetter>();
+ CppQuickFixFactory::registerFactory<GenerateGettersSettersForClass>();
+ CppQuickFixFactory::registerFactory<GenerateConstructor>();
+ CppQuickFixFactory::registerFactory<InsertQtPropertyMembers>();
+}
+
+} // namespace CppEditor::Internal
+
+#include <cppcodegenerationquickfixes.moc>
diff --git a/src/plugins/cppeditor/quickfixes/cppcodegenerationquickfixes.h b/src/plugins/cppeditor/quickfixes/cppcodegenerationquickfixes.h
new file mode 100644
index 0000000000..3cef95c7c1
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/cppcodegenerationquickfixes.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerCodeGenerationQuickfixes();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/cppinsertvirtualmethods.cpp b/src/plugins/cppeditor/quickfixes/cppinsertvirtualmethods.cpp
index b34977cfd8..31193e5cec 100644
--- a/src/plugins/cppeditor/cppinsertvirtualmethods.cpp
+++ b/src/plugins/cppeditor/quickfixes/cppinsertvirtualmethods.cpp
@@ -3,12 +3,13 @@
#include "cppinsertvirtualmethods.h"
-#include "cppcodestylesettings.h"
-#include "cppeditortr.h"
+#include "../cppcodestylesettings.h"
+#include "../cppeditortr.h"
+#include "../cpptoolsreuse.h"
+#include "../functionutils.h"
+#include "../insertionpointlocator.h"
#include "cppquickfixassistant.h"
-#include "cpptoolsreuse.h"
-#include "functionutils.h"
-#include "insertionpointlocator.h"
+#include "cppquickfixhelpers.h"
#include <coreplugin/icore.h>
#include <texteditor/fontsettings.h>
@@ -116,6 +117,11 @@ public:
QSortFilterProxyModel *classFunctionFilterModel;
};
+void registerInsertVirtualMethodsQuickfix()
+{
+ CppQuickFixFactory::registerFactory<InsertVirtualMethods>();
+}
+
} // namespace Internal
} // namespace CppEditor
@@ -862,9 +868,8 @@ public:
// Write header file
if (!headerChangeSet.isEmpty()) {
- headerFile->setChangeSet(headerChangeSet);
headerFile->setOpenEditor(true, m_insertPosDecl);
- headerFile->apply();
+ headerFile->apply(headerChangeSet);
}
// Insert in implementation file
@@ -915,10 +920,7 @@ public:
implementationChangeSet.insert(insertPos, QLatin1String("\n\n") + defText);
}
- if (!implementationChangeSet.isEmpty()) {
- implementationFile->setChangeSet(implementationChangeSet);
- implementationFile->apply();
- }
+ implementationFile->apply(implementationChangeSet);
}
}
@@ -1269,6 +1271,17 @@ public:
void saveSettings() override { }
};
+class InsertVirtualMethodsTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data();
+ void test();
+ void testImplementationFile();
+ void testBaseClassInNamespace();
+};
+
void InsertVirtualMethodsTest::test_data()
{
QTest::addColumn<InsertVirtualMethodsDialog::ImplementationMode>("implementationMode");
@@ -1969,6 +1982,11 @@ InsertVirtualMethods *InsertVirtualMethods::createTestFactory()
InsertVirtualMethodsDialog::ModeOutsideClass, true, false));
}
+QObject *InsertVirtualMethods::createTest()
+{
+ return new Tests::InsertVirtualMethodsTest;
+}
+
#endif // WITH_TESTS
} // namespace Internal
diff --git a/src/plugins/cppeditor/cppinsertvirtualmethods.h b/src/plugins/cppeditor/quickfixes/cppinsertvirtualmethods.h
index 7116efde1d..ffb749a71e 100644
--- a/src/plugins/cppeditor/cppinsertvirtualmethods.h
+++ b/src/plugins/cppeditor/quickfixes/cppinsertvirtualmethods.h
@@ -19,6 +19,7 @@ public:
void doMatch(const CppQuickFixInterface &interface,
TextEditor::QuickFixOperations &result) override;
#ifdef WITH_TESTS
+ static QObject *createTest();
static InsertVirtualMethods *createTestFactory();
#endif
@@ -26,20 +27,7 @@ private:
InsertVirtualMethodsDialog *m_dialog;
};
-#ifdef WITH_TESTS
-namespace Tests {
-class InsertVirtualMethodsTest : public QObject
-{
- Q_OBJECT
-
-private slots:
- void test_data();
- void test();
- void testImplementationFile();
- void testBaseClassInNamespace();
-};
-} // namespace Tests
-#endif // WITH_TESTS
+void registerInsertVirtualMethodsQuickfix();
} // namespace Internal
} // namespace CppEditor
diff --git a/src/plugins/cppeditor/quickfixes/cppquickfix.cpp b/src/plugins/cppeditor/quickfixes/cppquickfix.cpp
new file mode 100644
index 0000000000..a4052db83e
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/cppquickfix.cpp
@@ -0,0 +1,187 @@
+// 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 "cppquickfix.h"
+
+#include "../baseeditordocumentprocessor.h"
+#include "../cppeditortr.h"
+#include "../cppeditorwidget.h"
+#include "../cppfunctiondecldeflink.h"
+#include "../cpprefactoringchanges.h"
+#include "assigntolocalvariable.h"
+#include "bringidentifierintoscope.h"
+#include "completeswitchstatement.h"
+#include "convertfromandtopointer.h"
+#include "convertnumericliteral.h"
+#include "convertqt4connect.h"
+#include "convertstringliteral.h"
+#include "converttocamelcase.h"
+#include "converttometamethodcall.h"
+#include "cppcodegenerationquickfixes.h"
+#include "cppinsertvirtualmethods.h"
+#include "cppquickfixassistant.h"
+#include "createdeclarationfromuse.h"
+#include "extractfunction.h"
+#include "extractliteralasparameter.h"
+#include "insertfunctiondefinition.h"
+#include "logicaloperationquickfixes.h"
+#include "moveclasstoownfile.h"
+#include "movefunctiondefinition.h"
+#include "rearrangeparamdeclarationlist.h"
+#include "reformatpointerdeclaration.h"
+#include "removeusingnamespace.h"
+#include "rewritecomment.h"
+#include "rewritecontrolstatements.h"
+#include "splitsimpledeclaration.h"
+#include "synchronizememberfunctionorder.h"
+
+#include <extensionsystem/pluginmanager.h>
+#include <extensionsystem/pluginspec.h>
+
+using namespace CPlusPlus;
+using namespace TextEditor;
+
+namespace CppEditor {
+namespace Internal {
+namespace {
+
+class ApplyDeclDefLinkOperation : public CppQuickFixOperation
+{
+public:
+ explicit ApplyDeclDefLinkOperation(const CppQuickFixInterface &interface,
+ const std::shared_ptr<FunctionDeclDefLink> &link)
+ : CppQuickFixOperation(interface, 100)
+ , m_link(link)
+ {}
+
+ void perform() override
+ {
+ if (editor()->declDefLink() == m_link)
+ editor()->applyDeclDefLinkChanges(/*don't jump*/false);
+ }
+
+private:
+ std::shared_ptr<FunctionDeclDefLink> m_link;
+};
+
+class ExtraRefactoringOperations : public CppQuickFixFactory
+{
+public:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const auto processor = CppModelManager::cppEditorDocumentProcessor(interface.filePath());
+ if (processor) {
+ const auto clangFixItOperations = processor->extraRefactoringOperations(interface);
+ result.append(clangFixItOperations);
+ }
+ }
+};
+
+//! Applies function signature changes
+class ApplyDeclDefLinkChanges: public CppQuickFixFactory
+{
+public:
+ void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override
+ {
+ std::shared_ptr<FunctionDeclDefLink> link = interface.editor()->declDefLink();
+ if (!link || !link->isMarkerVisible())
+ return;
+
+ auto op = new ApplyDeclDefLinkOperation(interface, link);
+ op->setDescription(Tr::tr("Apply Function Signature Changes"));
+ result << op;
+ }
+};
+
+} // namespace
+
+static ExtensionSystem::IPlugin *getCppEditor()
+{
+ using namespace ExtensionSystem;
+ for (PluginSpec * const spec : PluginManager::plugins()) {
+ if (spec->name() == "CppEditor")
+ return spec->plugin();
+ }
+ QTC_ASSERT(false, return nullptr);
+}
+
+CppQuickFixOperation::~CppQuickFixOperation() = default;
+
+void createCppQuickFixFactories()
+{
+ new ApplyDeclDefLinkChanges;
+ new ExtraRefactoringOperations;
+
+ registerAssignToLocalVariableQuickfix();
+ registerBringIdentifierIntoScopeQuickfixes();
+ registerCodeGenerationQuickfixes();
+ registerCompleteSwitchStatementQuickfix();
+ registerConvertFromAndToPointerQuickfix();
+ registerConvertNumericLiteralQuickfix();
+ registerConvertQt4ConnectQuickfix();
+ registerConvertStringLiteralQuickfixes();
+ registerConvertToCamelCaseQuickfix();
+ registerConvertToMetaMethodCallQuickfix();
+ registerCreateDeclarationFromUseQuickfixes();
+ registerExtractFunctionQuickfix();
+ registerExtractLiteralAsParameterQuickfix();
+ registerInsertFunctionDefinitionQuickfixes();
+ registerInsertVirtualMethodsQuickfix();
+ registerLogicalOperationQuickfixes();
+ registerMoveClassToOwnFileQuickfix();
+ registerMoveFunctionDefinitionQuickfixes();
+ registerRearrangeParamDeclarationListQuickfix();
+ registerReformatPointerDeclarationQuickfix();
+ registerRemoveUsingNamespaceQuickfix();
+ registerRewriteCommentQuickfixes();
+ registerRewriteControlStatementQuickfixes();
+ registerSplitSimpleDeclarationQuickfix();
+ registerSynchronizeMemberFunctionOrderQuickfix();
+}
+
+static QList<CppQuickFixFactory *> g_cppQuickFixFactories;
+
+void destroyCppQuickFixFactories()
+{
+ for (int i = g_cppQuickFixFactories.size(); --i >= 0; )
+ delete g_cppQuickFixFactories.at(i);
+}
+
+} // namespace Internal
+
+CppQuickFixFactory::CppQuickFixFactory()
+{
+ Internal::g_cppQuickFixFactories.append(this);
+}
+
+CppQuickFixFactory::~CppQuickFixFactory()
+{
+ Internal::g_cppQuickFixFactories.removeOne(this);
+}
+
+ExtensionSystem::IPlugin *CppQuickFixFactory::cppEditor()
+{
+ static ExtensionSystem::IPlugin * const plugin = Internal::getCppEditor();
+ return plugin;
+}
+
+void CppQuickFixFactory::match(const Internal::CppQuickFixInterface &interface,
+ QuickFixOperations &result)
+{
+ if (m_clangdReplacement) {
+ if (const auto clangdVersion = CppModelManager::usesClangd(
+ interface.currentFile()->editor()->textDocument());
+ clangdVersion && clangdVersion >= m_clangdReplacement) {
+ return;
+ }
+ }
+
+ doMatch(interface, result);
+}
+
+const QList<CppQuickFixFactory *> &CppQuickFixFactory::cppQuickFixFactories()
+{
+ return Internal::g_cppQuickFixFactories;
+}
+
+} // namespace CppEditor
diff --git a/src/plugins/cppeditor/cppquickfix.h b/src/plugins/cppeditor/quickfixes/cppquickfix.h
index e901b2bd30..3d654661bc 100644
--- a/src/plugins/cppeditor/cppquickfix.h
+++ b/src/plugins/cppeditor/quickfixes/cppquickfix.h
@@ -3,9 +3,10 @@
#pragma once
-#include "cppeditor_global.h"
+#include "../cppeditor_global.h"
#include "cppquickfixassistant.h"
+#include <extensionsystem/iplugin.h>
#include <texteditor/quickfix.h>
#include <QVersionNumber>
@@ -16,18 +17,20 @@ namespace CppEditor {
namespace Internal {
class CppQuickFixInterface;
-// These are generated functions that should not be offered in quickfixes.
-const QStringList magicQObjectFunctions();
-
class CppQuickFixOperation
: public TextEditor::QuickFixOperation,
public Internal::CppQuickFixInterface
{
public:
- explicit CppQuickFixOperation(const CppQuickFixInterface &interface, int priority = -1);
+ explicit CppQuickFixOperation(const CppQuickFixInterface &interface, int priority = -1)
+ : QuickFixOperation(priority), CppQuickFixInterface(interface)
+ {}
~CppQuickFixOperation() override;
};
+void createCppQuickFixFactories();
+void destroyCppQuickFixFactories();
+
} // namespace Internal
/*!
@@ -59,14 +62,27 @@ public:
std::optional<QVersionNumber> clangdReplacement() const { return m_clangdReplacement; }
void setClangdReplacement(const QVersionNumber &version) { m_clangdReplacement = version; }
+ template<class Factory> static void registerFactory()
+ {
+ new Factory;
+#ifdef WITH_TESTS
+ cppEditor()->addTestCreator(Factory::createTest);
+#endif
+ }
+
private:
/*!
- Implement this function to doMatch and create the appropriate
+ Implement this function to match and create the appropriate
CppQuickFixOperation objects.
+ Make sure that the function is "cheap". Otherwise, since the match()
+ functions are also called to generate context menu entries,
+ the user might experience a delay opening the context menu.
*/
virtual void doMatch(const Internal::CppQuickFixInterface &interface,
QuickFixOperations &result) = 0;
+ static ExtensionSystem::IPlugin *cppEditor();
+
std::optional<QVersionNumber> m_clangdReplacement;
};
diff --git a/src/plugins/cppeditor/quickfixes/cppquickfix_test.cpp b/src/plugins/cppeditor/quickfixes/cppquickfix_test.cpp
new file mode 100644
index 0000000000..2bde0a1021
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/cppquickfix_test.cpp
@@ -0,0 +1,251 @@
+// 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 "cppquickfix_test.h"
+
+#include "../cppcodestylepreferences.h"
+#include "../cppeditorwidget.h"
+#include "../cppmodelmanager.h"
+#include "../cppsourceprocessertesthelper.h"
+#include "../cpptoolssettings.h"
+#include "cppquickfixassistant.h"
+
+#include <projectexplorer/kitmanager.h>
+#include <projectexplorer/projectexplorer.h>
+#include <texteditor/textdocument.h>
+#include <utils/fileutils.h>
+
+#include <QDebug>
+#include <QDir>
+#include <QtTest>
+
+/*!
+ Tests for quick-fixes.
+ */
+using namespace Core;
+using namespace CPlusPlus;
+using namespace ProjectExplorer;
+using namespace TextEditor;
+using namespace Utils;
+
+using CppEditor::Tests::TemporaryDir;
+using CppEditor::Tests::Internal::TestIncludePaths;
+
+namespace CppEditor {
+namespace Internal {
+namespace Tests {
+
+QList<TestDocumentPtr> singleDocument(const QByteArray &original,
+ const QByteArray &expected)
+{
+ return {CppTestDocument::create("file.cpp", original, expected)};
+}
+
+BaseQuickFixTestCase::BaseQuickFixTestCase(const QList<TestDocumentPtr> &testDocuments,
+ const ProjectExplorer::HeaderPaths &headerPaths,
+ const QByteArray &clangFormatSettings)
+ : m_testDocuments(testDocuments)
+ , m_cppCodeStylePreferences(0)
+ , m_restoreHeaderPaths(false)
+{
+ QVERIFY(succeededSoFar());
+ m_succeededSoFar = false;
+
+ // Check if there is exactly one cursor marker
+ unsigned cursorMarkersCount = 0;
+ for (const TestDocumentPtr &document : std::as_const(m_testDocuments)) {
+ if (document->hasCursorMarker())
+ ++cursorMarkersCount;
+ }
+ QVERIFY2(cursorMarkersCount == 1, "Exactly one cursor marker is allowed.");
+
+ // Write documents to disk
+ m_temporaryDirectory.reset(new TemporaryDir);
+ QVERIFY(m_temporaryDirectory->isValid());
+ for (const TestDocumentPtr &document : std::as_const(m_testDocuments)) {
+ if (QFileInfo(document->m_fileName).isRelative())
+ document->setBaseDirectory(m_temporaryDirectory->path());
+ document->writeToDisk();
+ }
+
+ // Create .clang-format file
+ if (!clangFormatSettings.isEmpty())
+ m_temporaryDirectory->createFile(".clang-format", clangFormatSettings);
+
+ // Set appropriate include paths
+ if (!headerPaths.isEmpty()) {
+ m_restoreHeaderPaths = true;
+ m_headerPathsToRestore = CppModelManager::headerPaths();
+ CppModelManager::setHeaderPaths(headerPaths);
+ }
+
+ // Update Code Model
+ QSet<FilePath> filePaths;
+ for (const TestDocumentPtr &document : std::as_const(m_testDocuments))
+ filePaths << document->filePath();
+ QVERIFY(parseFiles(filePaths));
+
+ // Open Files
+ for (const TestDocumentPtr &document : std::as_const(m_testDocuments)) {
+ QVERIFY(openCppEditor(document->filePath(), &document->m_editor,
+ &document->m_editorWidget));
+ closeEditorAtEndOfTestCase(document->m_editor);
+
+ // Set cursor position
+ if (document->hasCursorMarker()) {
+ if (document->hasAnchorMarker()) {
+ document->m_editor->setCursorPosition(document->m_anchorPosition);
+ document->m_editor->select(document->m_cursorPosition);
+ } else {
+ document->m_editor->setCursorPosition(document->m_cursorPosition);
+ }
+ } else {
+ document->m_editor->setCursorPosition(0);
+ }
+
+ // Rehighlight
+ waitForRehighlightedSemanticDocument(document->m_editorWidget);
+ }
+
+ // Enforce the default cpp code style, so we are independent of config file settings.
+ // This is needed by e.g. the GenerateGetterSetter quick fix.
+ m_cppCodeStylePreferences = CppToolsSettings::cppCodeStyle();
+ QVERIFY(m_cppCodeStylePreferences);
+ m_cppCodeStylePreferencesOriginalDelegateId = m_cppCodeStylePreferences->currentDelegateId();
+ m_cppCodeStylePreferences->setCurrentDelegate("qt");
+
+ // Find the document having the cursor marker
+ for (const TestDocumentPtr &document : std::as_const(m_testDocuments)) {
+ if (document->hasCursorMarker()){
+ m_documentWithMarker = document;
+ break;
+ }
+ }
+
+ QVERIFY(m_documentWithMarker);
+ m_succeededSoFar = true;
+}
+
+BaseQuickFixTestCase::~BaseQuickFixTestCase()
+{
+ // Restore default cpp code style
+ if (m_cppCodeStylePreferences)
+ m_cppCodeStylePreferences->setCurrentDelegate(m_cppCodeStylePreferencesOriginalDelegateId);
+
+ // Restore include paths
+ if (m_restoreHeaderPaths)
+ CppModelManager::setHeaderPaths(m_headerPathsToRestore);
+
+ // Remove created files from file system
+ for (const TestDocumentPtr &testDocument : std::as_const(m_testDocuments))
+ QVERIFY(testDocument->filePath().removeFile());
+}
+
+QuickFixOfferedOperationsTest::QuickFixOfferedOperationsTest(
+ const QList<TestDocumentPtr> &testDocuments,
+ CppQuickFixFactory *factory,
+ const ProjectExplorer::HeaderPaths &headerPaths,
+ const QStringList &expectedOperations)
+ : BaseQuickFixTestCase(testDocuments, headerPaths)
+{
+ // Get operations
+ CppQuickFixInterface quickFixInterface(m_documentWithMarker->m_editorWidget, ExplicitlyInvoked);
+ QuickFixOperations actualOperations;
+ factory->match(quickFixInterface, actualOperations);
+
+ // Convert to QStringList
+ QStringList actualOperationsAsStringList;
+ for (const QuickFixOperation::Ptr &operation : std::as_const(actualOperations))
+ actualOperationsAsStringList << operation->description();
+
+ QCOMPARE(actualOperationsAsStringList, expectedOperations);
+}
+
+/// Leading whitespace is not removed, so we can check if the indetation ranges
+/// have been set correctly by the quick-fix.
+static QString &removeTrailingWhitespace(QString &input)
+{
+ const QStringList lines = input.split(QLatin1Char('\n'));
+ input.resize(0);
+ for (int i = 0, total = lines.size(); i < total; ++i) {
+ QString line = lines.at(i);
+ while (line.length() > 0) {
+ QChar lastChar = line[line.length() - 1];
+ if (lastChar == QLatin1Char(' ') || lastChar == QLatin1Char('\t'))
+ line.chop(1);
+ else
+ break;
+ }
+ input.append(line);
+
+ const bool isLastLine = i == lines.size() - 1;
+ if (!isLastLine)
+ input.append(QLatin1Char('\n'));
+ }
+ return input;
+}
+
+QuickFixOperationTest::QuickFixOperationTest(const QList<TestDocumentPtr> &testDocuments,
+ CppQuickFixFactory *factory,
+ const ProjectExplorer::HeaderPaths &headerPaths,
+ int operationIndex,
+ const QByteArray &expectedFailMessage,
+ const QByteArray &clangFormatSettings)
+ : BaseQuickFixTestCase(testDocuments, headerPaths, clangFormatSettings)
+{
+ if (factory->clangdReplacement() && CppModelManager::isClangCodeModelActive())
+ return;
+
+ QVERIFY(succeededSoFar());
+
+ // Perform operation if there is one
+ CppQuickFixInterface quickFixInterface(m_documentWithMarker->m_editorWidget, ExplicitlyInvoked);
+ QuickFixOperations operations;
+ factory->match(quickFixInterface, operations);
+ if (operations.isEmpty()) {
+ QEXPECT_FAIL("QTCREATORBUG-25998", "FIXME", Abort);
+ QVERIFY(testDocuments.first()->m_expectedSource.isEmpty());
+ return;
+ }
+
+ QVERIFY(operationIndex < operations.size());
+ const QuickFixOperation::Ptr operation = operations.at(operationIndex);
+ operation->perform();
+
+ // Compare all files
+ for (const TestDocumentPtr &testDocument : std::as_const(m_testDocuments)) {
+ // Check
+ QString result = testDocument->m_editorWidget->document()->toPlainText();
+ removeTrailingWhitespace(result);
+ QEXPECT_FAIL("escape string literal: raw string literal", "FIXME", Continue);
+ QEXPECT_FAIL("escape string literal: unescape adjacent literals", "FIXME", Continue);
+ if (!expectedFailMessage.isEmpty())
+ QEXPECT_FAIL("", expectedFailMessage.data(), Continue);
+ else if (result != testDocument->m_expectedSource) {
+ qDebug() << "---" << testDocument->m_expectedSource;
+ qDebug() << "+++" << result;
+ }
+ QCOMPARE(result, testDocument->m_expectedSource);
+
+ // Undo the change
+ for (int i = 0; i < 100; ++i)
+ testDocument->m_editorWidget->undo();
+ result = testDocument->m_editorWidget->document()->toPlainText();
+ QCOMPARE(result, testDocument->m_source);
+ }
+}
+
+void QuickFixOperationTest::run(const QList<TestDocumentPtr> &testDocuments,
+ CppQuickFixFactory *factory,
+ const QString &headerPath,
+ int operationIndex)
+{
+ ProjectExplorer::HeaderPaths headerPaths;
+ headerPaths.push_back(ProjectExplorer::HeaderPath::makeUser(headerPath));
+ QuickFixOperationTest(testDocuments, factory, headerPaths, operationIndex);
+}
+
+} // namespace Tests
+} // namespace Internal
+} // namespace CppEditor
+
diff --git a/src/plugins/cppeditor/quickfixes/cppquickfix_test.h b/src/plugins/cppeditor/quickfixes/cppquickfix_test.h
new file mode 100644
index 0000000000..b199062970
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/cppquickfix_test.h
@@ -0,0 +1,94 @@
+// 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 "../cpptoolstestcase.h"
+#include "cppquickfix.h"
+#include "cppquickfixsettings.h"
+
+#include <projectexplorer/headerpath.h>
+
+#include <QByteArray>
+#include <QList>
+#include <QObject>
+#include <QSharedPointer>
+#include <QStringList>
+
+namespace TextEditor { class QuickFixOperation; }
+
+namespace CppEditor {
+class CppCodeStylePreferences;
+
+namespace Internal {
+namespace Tests {
+
+class QuickFixSettings
+{
+ const CppQuickFixSettings original = *CppQuickFixSettings::instance();
+
+public:
+ CppQuickFixSettings *operator->() { return CppQuickFixSettings::instance(); }
+ ~QuickFixSettings() { *CppQuickFixSettings::instance() = original; }
+};
+
+class BaseQuickFixTestCase : public CppEditor::Tests::TestCase
+{
+public:
+ /// Exactly one QuickFixTestDocument must contain the cursor position marker '@'
+ /// or "@{start}" and "@{end}"
+ BaseQuickFixTestCase(const QList<TestDocumentPtr> &testDocuments,
+ const ProjectExplorer::HeaderPaths &headerPaths,
+ const QByteArray &clangFormatSettings = {});
+
+ ~BaseQuickFixTestCase();
+
+protected:
+ TestDocumentPtr m_documentWithMarker;
+ QList<TestDocumentPtr> m_testDocuments;
+
+private:
+ QScopedPointer<CppEditor::Tests::TemporaryDir> m_temporaryDirectory;
+
+ CppCodeStylePreferences *m_cppCodeStylePreferences;
+ QByteArray m_cppCodeStylePreferencesOriginalDelegateId;
+
+ ProjectExplorer::HeaderPaths m_headerPathsToRestore;
+ bool m_restoreHeaderPaths;
+};
+
+/// Tests the offered operations provided by a given CppQuickFixFactory
+class QuickFixOfferedOperationsTest : public BaseQuickFixTestCase
+{
+public:
+ QuickFixOfferedOperationsTest(const QList<TestDocumentPtr> &testDocuments,
+ CppQuickFixFactory *factory,
+ const ProjectExplorer::HeaderPaths &headerPaths
+ = ProjectExplorer::HeaderPaths(),
+ const QStringList &expectedOperations = QStringList());
+};
+
+/// Tests a concrete QuickFixOperation of a given CppQuickFixFactory
+class QuickFixOperationTest : public BaseQuickFixTestCase
+{
+public:
+ QuickFixOperationTest(const QList<TestDocumentPtr> &testDocuments,
+ CppQuickFixFactory *factory,
+ const ProjectExplorer::HeaderPaths &headerPaths
+ = ProjectExplorer::HeaderPaths(),
+ int operationIndex = 0,
+ const QByteArray &expectedFailMessage = {},
+ const QByteArray &clangFormatSettings = {});
+
+ static void run(const QList<TestDocumentPtr> &testDocuments,
+ CppQuickFixFactory *factory,
+ const QString &headerPath,
+ int operationIndex = 0);
+};
+
+QList<TestDocumentPtr> singleDocument(const QByteArray &original,
+ const QByteArray &expected);
+
+} // namespace Tests
+} // namespace Internal
+} // namespace CppEditor
diff --git a/src/plugins/cppeditor/cppquickfixassistant.cpp b/src/plugins/cppeditor/quickfixes/cppquickfixassistant.cpp
index 1456ce5604..834abaed8a 100644
--- a/src/plugins/cppeditor/cppquickfixassistant.cpp
+++ b/src/plugins/cppeditor/quickfixes/cppquickfixassistant.cpp
@@ -3,10 +3,10 @@
#include "cppquickfixassistant.h"
-#include "cppeditorwidget.h"
-#include "cppmodelmanager.h"
+#include "../cppeditorwidget.h"
+#include "../cppmodelmanager.h"
+#include "../cpprefactoringchanges.h"
#include "cppquickfix.h"
-#include "cpprefactoringchanges.h"
#include <texteditor/codeassist/genericproposal.h>
#include <texteditor/codeassist/iassistprocessor.h>
diff --git a/src/plugins/cppeditor/cppquickfixassistant.h b/src/plugins/cppeditor/quickfixes/cppquickfixassistant.h
index 63a37f715a..db818c923c 100644
--- a/src/plugins/cppeditor/cppquickfixassistant.h
+++ b/src/plugins/cppeditor/quickfixes/cppquickfixassistant.h
@@ -3,7 +3,7 @@
#pragma once
-#include "cppsemanticinfo.h"
+#include "../cppsemanticinfo.h"
#include <texteditor/codeassist/assistinterface.h>
#include <texteditor/codeassist/iassistprovider.h>
diff --git a/src/plugins/cppeditor/quickfixes/cppquickfixhelpers.cpp b/src/plugins/cppeditor/quickfixes/cppquickfixhelpers.cpp
new file mode 100644
index 0000000000..eb67b928db
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/cppquickfixhelpers.cpp
@@ -0,0 +1,199 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "cppquickfixhelpers.h"
+
+#include "../cppcodestylesettings.h"
+#include "../cppprojectfile.h"
+#include "../includeutils.h"
+#include "cppquickfixassistant.h"
+
+#include <cplusplus/CppRewriter.h>
+#include <cplusplus/Overview.h>
+#include <cplusplus/TypeOfExpression.h>
+
+using namespace CPlusPlus;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+
+void insertNewIncludeDirective(
+ const QString &include,
+ CppRefactoringFilePtr file,
+ const Document::Ptr &cppDocument,
+ ChangeSet &changes)
+{
+ // Find optimal position
+ unsigned newLinesToPrepend = 0;
+ unsigned newLinesToAppend = 0;
+ const int insertLine = lineForNewIncludeDirective(
+ file->filePath(),
+ file->document(),
+ cppDocument,
+ IgnoreMocIncludes,
+ AutoDetect,
+ include,
+ &newLinesToPrepend,
+ &newLinesToAppend);
+ QTC_ASSERT(insertLine >= 1, return);
+ const int insertPosition = file->position(insertLine, 1);
+ QTC_ASSERT(insertPosition >= 0, return);
+
+ // Construct text to insert
+ const QString includeLine = QLatin1String("#include ") + include + QLatin1Char('\n');
+ QString prependedNewLines, appendedNewLines;
+ while (newLinesToAppend--)
+ appendedNewLines += QLatin1String("\n");
+ while (newLinesToPrepend--)
+ prependedNewLines += QLatin1String("\n");
+ const QString textToInsert = prependedNewLines + includeLine + appendedNewLines;
+
+ // Insert
+ changes.insert(insertPosition, textToInsert);
+}
+
+ClassSpecifierAST *astForClassOperations(const CppQuickFixInterface &interface)
+{
+ const QList<AST *> &path = interface.path();
+ if (path.isEmpty())
+ return nullptr;
+ if (const auto classSpec = path.last()->asClassSpecifier()) // Cursor inside class decl?
+ return classSpec;
+
+ // Cursor on a class name?
+ if (path.size() < 2)
+ return nullptr;
+ const SimpleNameAST * const nameAST = path.at(path.size() - 1)->asSimpleName();
+ if (!nameAST || !interface.isCursorOn(nameAST))
+ return nullptr;
+ if (const auto classSpec = path.at(path.size() - 2)->asClassSpecifier())
+ return classSpec;
+ return nullptr;
+}
+
+bool nameIncludesOperatorName(const Name *name)
+{
+ return name->asOperatorNameId()
+ || (name->asQualifiedNameId() && name->asQualifiedNameId()->name()->asOperatorNameId());
+}
+
+QString inlinePrefix(const FilePath &targetFile, const std::function<bool()> &extraCondition)
+{
+ if (ProjectFile::isHeader(ProjectFile::classify(targetFile.path()))
+ && (!extraCondition || extraCondition())) {
+ return "inline ";
+ }
+ return {};
+}
+
+Class *isMemberFunction(const CPlusPlus::LookupContext &context, CPlusPlus::Function *function)
+{
+ QTC_ASSERT(function, return nullptr);
+
+ Scope *enclosingScope = function->enclosingScope();
+ while (!(enclosingScope->asNamespace() || enclosingScope->asClass()))
+ enclosingScope = enclosingScope->enclosingScope();
+ QTC_ASSERT(enclosingScope != nullptr, return nullptr);
+
+ const Name *functionName = function->name();
+ if (!functionName)
+ return nullptr;
+
+ if (!functionName->asQualifiedNameId())
+ return nullptr; // trying to add a declaration for a global function
+
+ const QualifiedNameId *q = functionName->asQualifiedNameId();
+ if (!q->base())
+ return nullptr;
+
+ if (ClassOrNamespace *binding = context.lookupType(q->base(), enclosingScope)) {
+ const QList<Symbol *> symbols = binding->symbols();
+ for (Symbol *s : symbols) {
+ if (Class *matchingClass = s->asClass())
+ return matchingClass;
+ }
+ }
+
+ return nullptr;
+}
+
+CPlusPlus::Namespace *isNamespaceFunction(
+ const CPlusPlus::LookupContext &context, CPlusPlus::Function *function)
+{
+ QTC_ASSERT(function, return nullptr);
+ if (isMemberFunction(context, function))
+ return nullptr;
+
+ Scope *enclosingScope = function->enclosingScope();
+ while (!(enclosingScope->asNamespace() || enclosingScope->asClass()))
+ enclosingScope = enclosingScope->enclosingScope();
+ QTC_ASSERT(enclosingScope != nullptr, return nullptr);
+
+ const Name *functionName = function->name();
+ if (!functionName)
+ return nullptr;
+
+ // global namespace
+ if (!functionName->asQualifiedNameId()) {
+ const QList<Symbol *> symbols = context.globalNamespace()->symbols();
+ for (Symbol *s : symbols) {
+ if (Namespace *matchingNamespace = s->asNamespace())
+ return matchingNamespace;
+ }
+ return nullptr;
+ }
+
+ const QualifiedNameId *q = functionName->asQualifiedNameId();
+ if (!q->base())
+ return nullptr;
+
+ if (ClassOrNamespace *binding = context.lookupType(q->base(), enclosingScope)) {
+ const QList<Symbol *> symbols = binding->symbols();
+ for (Symbol *s : symbols) {
+ if (Namespace *matchingNamespace = s->asNamespace())
+ return matchingNamespace;
+ }
+ }
+
+ return nullptr;
+}
+
+QString nameString(const CPlusPlus::NameAST *name)
+{
+ return CppCodeStyleSettings::currentProjectCodeStyleOverview().prettyName(name->name);
+}
+
+CPlusPlus::FullySpecifiedType typeOfExpr(
+ const ExpressionAST *expr,
+ const CppRefactoringFilePtr &file,
+ const Snapshot &snapshot,
+ const LookupContext &context)
+{
+ 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().get();
+ return rewriteType(result.first().type(), &env, control);
+}
+
+const QStringList magicQObjectFunctions()
+{
+ static QStringList list{"metaObject", "qt_metacast", "qt_metacall", "qt_static_metacall"};
+ return list;
+}
+
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/cppquickfixhelpers.h b/src/plugins/cppeditor/quickfixes/cppquickfixhelpers.h
new file mode 100644
index 0000000000..b083fdb0d8
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/cppquickfixhelpers.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "../cpprefactoringchanges.h"
+
+#include <QStringList>
+
+namespace CppEditor::Internal {
+class CppQuickFixInterface;
+
+// These are generated functions that should not be offered in quickfixes.
+const QStringList magicQObjectFunctions();
+
+// Given include is e.g. "afile.h" or <afile.h> (quotes/angle brackets included!).
+void insertNewIncludeDirective(
+ const QString &include,
+ CppRefactoringFilePtr file,
+ const CPlusPlus::Document::Ptr &cppDocument,
+ Utils::ChangeSet &changes);
+
+// Returns a non-null value if and only if the cursor is on the name of a (proper) class
+// declaration or at some place inside the body of a class declaration that does not
+// correspond to an AST of its own, i.e. on "empty space".
+CPlusPlus::ClassSpecifierAST *astForClassOperations(const CppQuickFixInterface &interface);
+
+bool nameIncludesOperatorName(const CPlusPlus::Name *name);
+
+QString inlinePrefix(const Utils::FilePath &targetFile,
+ const std::function<bool()> &extraCondition = {});
+
+CPlusPlus::Class *isMemberFunction(
+ const CPlusPlus::LookupContext &context, CPlusPlus::Function *function);
+
+CPlusPlus::Namespace *isNamespaceFunction(
+ const CPlusPlus::LookupContext &context, CPlusPlus::Function *function);
+
+QString nameString(const CPlusPlus::NameAST *name);
+
+CPlusPlus::FullySpecifiedType typeOfExpr(
+ const CPlusPlus::ExpressionAST *expr,
+ const CppRefactoringFilePtr &file,
+ const CPlusPlus::Snapshot &snapshot,
+ const CPlusPlus::LookupContext &context);
+
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/cppquickfixprojectsettings.cpp b/src/plugins/cppeditor/quickfixes/cppquickfixprojectsettings.cpp
index 3f3f4bc366..0313103433 100644
--- a/src/plugins/cppeditor/cppquickfixprojectsettings.cpp
+++ b/src/plugins/cppeditor/quickfixes/cppquickfixprojectsettings.cpp
@@ -3,8 +3,8 @@
#include "cppquickfixprojectsettings.h"
-#include "cppeditorconstants.h"
-#include "cppeditortr.h"
+#include "../cppeditorconstants.h"
+#include "../cppeditortr.h"
#include <coreplugin/icore.h>
diff --git a/src/plugins/cppeditor/cppquickfixprojectsettings.h b/src/plugins/cppeditor/quickfixes/cppquickfixprojectsettings.h
index d8e871515f..d8e871515f 100644
--- a/src/plugins/cppeditor/cppquickfixprojectsettings.h
+++ b/src/plugins/cppeditor/quickfixes/cppquickfixprojectsettings.h
diff --git a/src/plugins/cppeditor/cppquickfixprojectsettingswidget.cpp b/src/plugins/cppeditor/quickfixes/cppquickfixprojectsettingswidget.cpp
index feb9777e45..8046d80a60 100644
--- a/src/plugins/cppeditor/cppquickfixprojectsettingswidget.cpp
+++ b/src/plugins/cppeditor/quickfixes/cppquickfixprojectsettingswidget.cpp
@@ -3,8 +3,8 @@
#include "cppquickfixprojectsettingswidget.h"
-#include "cppeditorconstants.h"
-#include "cppeditortr.h"
+#include "../cppeditorconstants.h"
+#include "../cppeditortr.h"
#include "cppquickfixprojectsettings.h"
#include "cppquickfixsettingswidget.h"
diff --git a/src/plugins/cppeditor/cppquickfixprojectsettingswidget.h b/src/plugins/cppeditor/quickfixes/cppquickfixprojectsettingswidget.h
index a88395cc73..a88395cc73 100644
--- a/src/plugins/cppeditor/cppquickfixprojectsettingswidget.h
+++ b/src/plugins/cppeditor/quickfixes/cppquickfixprojectsettingswidget.h
diff --git a/src/plugins/cppeditor/cppquickfixsettings.cpp b/src/plugins/cppeditor/quickfixes/cppquickfixsettings.cpp
index 4ce1082267..e08de44675 100644
--- a/src/plugins/cppeditor/cppquickfixsettings.cpp
+++ b/src/plugins/cppeditor/quickfixes/cppquickfixsettings.cpp
@@ -3,8 +3,8 @@
#include "cppquickfixsettings.h"
-#include "cppcodestylesettings.h"
-#include "cppeditorconstants.h"
+#include "../cppcodestylesettings.h"
+#include "../cppeditorconstants.h"
#include <coreplugin/icore.h>
diff --git a/src/plugins/cppeditor/cppquickfixsettings.h b/src/plugins/cppeditor/quickfixes/cppquickfixsettings.h
index d033516276..d033516276 100644
--- a/src/plugins/cppeditor/cppquickfixsettings.h
+++ b/src/plugins/cppeditor/quickfixes/cppquickfixsettings.h
diff --git a/src/plugins/cppeditor/cppquickfixsettingspage.cpp b/src/plugins/cppeditor/quickfixes/cppquickfixsettingspage.cpp
index 4e1297f5c2..5933694bbc 100644
--- a/src/plugins/cppeditor/cppquickfixsettingspage.cpp
+++ b/src/plugins/cppeditor/quickfixes/cppquickfixsettingspage.cpp
@@ -3,8 +3,8 @@
#include "cppquickfixsettingspage.h"
-#include "cppeditorconstants.h"
-#include "cppeditortr.h"
+#include "../cppeditorconstants.h"
+#include "../cppeditortr.h"
#include "cppquickfixsettingswidget.h"
#include <coreplugin/dialogs/ioptionspage.h>
diff --git a/src/plugins/cppeditor/cppquickfixsettingspage.h b/src/plugins/cppeditor/quickfixes/cppquickfixsettingspage.h
index 6288c9f9f2..6288c9f9f2 100644
--- a/src/plugins/cppeditor/cppquickfixsettingspage.h
+++ b/src/plugins/cppeditor/quickfixes/cppquickfixsettingspage.h
diff --git a/src/plugins/cppeditor/cppquickfixsettingswidget.cpp b/src/plugins/cppeditor/quickfixes/cppquickfixsettingswidget.cpp
index c589a26b9d..67213105bc 100644
--- a/src/plugins/cppeditor/cppquickfixsettingswidget.cpp
+++ b/src/plugins/cppeditor/quickfixes/cppquickfixsettingswidget.cpp
@@ -3,7 +3,7 @@
#include "cppquickfixsettingswidget.h"
-#include "cppeditortr.h"
+#include "../cppeditortr.h"
#include "cppquickfixsettings.h"
#include <utils/layoutbuilder.h>
@@ -215,6 +215,7 @@ e.g. name = "m_test_foo_":
using namespace Layouting;
+ // clang-format off
Grid {
empty, ulLabel(Tr::tr("Generate Setters")), ulLabel(Tr::tr("Generate Getters")), br,
Tr::tr("Inside class:"), Tr::tr("Default"), Tr::tr("Default"), br,
@@ -274,7 +275,7 @@ e.g. name = "m_test_foo_":
},
},
Group {
- title(Tr::tr("Value types:")),
+ title(Tr::tr("Value Types")),
Row {
m_valueTypes,
Column { pushButton_addValueType, pushButton_removeValueType, st, },
@@ -282,6 +283,7 @@ e.g. name = "m_test_foo_":
},
m_returnByConstRefCheckBox,
}.attachTo(this);
+ // clang-format on
// connect controls to settingsChanged signal
auto then = [this] {
diff --git a/src/plugins/cppeditor/cppquickfixsettingswidget.h b/src/plugins/cppeditor/quickfixes/cppquickfixsettingswidget.h
index e11d81a9f2..e11d81a9f2 100644
--- a/src/plugins/cppeditor/cppquickfixsettingswidget.h
+++ b/src/plugins/cppeditor/quickfixes/cppquickfixsettingswidget.h
diff --git a/src/plugins/cppeditor/quickfixes/createdeclarationfromuse.cpp b/src/plugins/cppeditor/quickfixes/createdeclarationfromuse.cpp
new file mode 100644
index 0000000000..f7a760a99e
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/createdeclarationfromuse.cpp
@@ -0,0 +1,1207 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "createdeclarationfromuse.h"
+
+#include "../cppcodestylesettings.h"
+#include "../cppeditortr.h"
+#include "../cppeditorwidget.h"
+#include "../cpprefactoringchanges.h"
+#include "../insertionpointlocator.h"
+#include "../symbolfinder.h"
+#include "cppquickfix.h"
+#include "cppquickfixhelpers.h"
+#include "cppquickfixprojectsettings.h"
+
+#include <coreplugin/icore.h>
+#include <cplusplus/Overview.h>
+#include <cplusplus/TypeOfExpression.h>
+#include <projectexplorer/projecttree.h>
+
+#include <QInputDialog>
+
+#ifdef WITH_TESTS
+#include "cppquickfix_test.h"
+#include <QtTest>
+#endif
+
+#include <variant>
+
+using namespace CPlusPlus;
+using namespace ProjectExplorer;
+using namespace TextEditor;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+using TypeOrExpr = std::variant<const CPlusPlus::ExpressionAST *, CPlusPlus::FullySpecifiedType>;
+
+// FIXME: Needs to consider the scope at the insertion site.
+static QString declFromExpr(
+ const TypeOrExpr &typeOrExpr,
+ const CallAST *call,
+ const NameAST *varName,
+ const Snapshot &snapshot,
+ const LookupContext &context,
+ const CppRefactoringFilePtr &file,
+ bool makeConst)
+{
+ 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 {
+ return typeOfExpr(expr, file, snapshot, context);
+ };
+
+ 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);
+ func.setConst(makeConst);
+ 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);
+}
+
+
+
+
+class InsertDeclOperation: public CppQuickFixOperation
+{
+public:
+ InsertDeclOperation(const CppQuickFixInterface &interface,
+ const FilePath &targetFilePath, const Class *targetSymbol,
+ InsertionPointLocator::AccessSpec xsSpec, const QString &decl, int priority)
+ : CppQuickFixOperation(interface, priority)
+ , m_targetFilePath(targetFilePath)
+ , m_targetSymbol(targetSymbol)
+ , m_xsSpec(xsSpec)
+ , m_decl(decl)
+ {
+ setDescription(Tr::tr("Add %1 Declaration")
+ .arg(InsertionPointLocator::accessSpecToString(xsSpec)));
+ }
+
+ void perform() override
+ {
+ CppRefactoringChanges refactoring(snapshot());
+
+ InsertionPointLocator locator(refactoring);
+ const InsertionLocation loc = locator.methodDeclarationInClass(
+ m_targetFilePath, m_targetSymbol, m_xsSpec);
+ QTC_ASSERT(loc.isValid(), return);
+
+ CppRefactoringFilePtr targetFile = refactoring.cppFile(m_targetFilePath);
+ int targetPosition = targetFile->position(loc.line(), loc.column());
+
+ ChangeSet target;
+ target.insert(targetPosition, loc.prefix() + m_decl);
+ targetFile->setOpenEditor(true, targetPosition);
+ targetFile->apply(target);
+ }
+
+ static QString generateDeclaration(const Function *function)
+ {
+ Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ oo.showFunctionSignatures = true;
+ oo.showReturnTypes = true;
+ oo.showArgumentNames = true;
+ oo.showEnclosingTemplate = true;
+
+ QString decl;
+ decl += oo.prettyType(function->type(), function->unqualifiedName());
+ decl += QLatin1String(";\n");
+
+ return decl;
+ }
+
+private:
+ FilePath m_targetFilePath;
+ const Class *m_targetSymbol;
+ InsertionPointLocator::AccessSpec m_xsSpec;
+ QString m_decl;
+};
+
+class DeclOperationFactory
+{
+public:
+ DeclOperationFactory(const CppQuickFixInterface &interface, const FilePath &filePath,
+ const Class *matchingClass, const QString &decl)
+ : m_interface(interface)
+ , m_filePath(filePath)
+ , m_matchingClass(matchingClass)
+ , m_decl(decl)
+ {}
+
+ QuickFixOperation *operator()(InsertionPointLocator::AccessSpec xsSpec, int priority)
+ {
+ return new InsertDeclOperation(m_interface, m_filePath, m_matchingClass, xsSpec, m_decl, priority);
+ }
+
+private:
+ const CppQuickFixInterface &m_interface;
+ const FilePath &m_filePath;
+ const Class *m_matchingClass;
+ const QString &m_decl;
+};
+
+class InsertMemberFromInitializationOp : public CppQuickFixOperation
+{
+public:
+ InsertMemberFromInitializationOp(
+ const CppQuickFixInterface &interface,
+ const Class *theClass,
+ const NameAST *memberName,
+ const TypeOrExpr &typeOrExpr,
+ const CallAST *call,
+ InsertionPointLocator::AccessSpec accessSpec,
+ bool makeStatic,
+ bool makeConst)
+ : CppQuickFixOperation(interface),
+ m_class(theClass), m_memberName(memberName), m_typeOrExpr(typeOrExpr), m_call(call),
+ m_accessSpec(accessSpec), m_makeStatic(makeStatic), m_makeConst(makeConst)
+ {
+ 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 decl = declFromExpr(m_typeOrExpr, m_call, m_memberName, snapshot(), context(),
+ currentFile(), m_makeConst);
+ 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, m_accessSpec);
+ QTC_ASSERT(loc.isValid(), return);
+
+ CppRefactoringFilePtr targetFile = refactoring.cppFile(filePath);
+ targetFile->apply(ChangeSet::makeInsert(
+ targetFile->position(loc.line(), loc.column()), loc.prefix() + decl + ";\n"));
+ }
+
+ const Class * const m_class;
+ const NameAST * const m_memberName;
+ const TypeOrExpr m_typeOrExpr;
+ const CallAST * m_call;
+ const InsertionPointLocator::AccessSpec m_accessSpec;
+ const bool m_makeStatic;
+ const bool m_makeConst;
+};
+
+class AddLocalDeclarationOp: public CppQuickFixOperation
+{
+public:
+ AddLocalDeclarationOp(const CppQuickFixInterface &interface,
+ int priority,
+ const BinaryExpressionAST *binaryAST,
+ const SimpleNameAST *simpleNameAST)
+ : CppQuickFixOperation(interface, priority)
+ , binaryAST(binaryAST)
+ , simpleNameAST(simpleNameAST)
+ {
+ setDescription(Tr::tr("Add Local Declaration"));
+ }
+
+ void perform() override
+ {
+ QString declaration = getDeclaration();
+
+ if (!declaration.isEmpty()) {
+ currentFile()->apply(ChangeSet::makeReplace(
+ currentFile()->startOf(binaryAST),
+ currentFile()->endOf(simpleNameAST),
+ declaration));
+ }
+ }
+
+private:
+ QString getDeclaration()
+ {
+ Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ const auto settings = CppQuickFixProjectsSettings::getQuickFixSettings(
+ ProjectTree::currentProject());
+
+ if (currentFile()->cppDocument()->languageFeatures().cxx11Enabled && settings->useAuto)
+ return "auto " + oo.prettyName(simpleNameAST->name);
+ return declFromExpr(binaryAST->right_expression, nullptr, simpleNameAST, snapshot(),
+ context(), currentFile(), false);
+ }
+
+ const BinaryExpressionAST *binaryAST;
+ const SimpleNameAST *simpleNameAST;
+};
+
+//! Adds a declarations to a definition
+class InsertDeclFromDef: public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const QList<AST *> &path = interface.path();
+ CppRefactoringFilePtr file = interface.currentFile();
+
+ FunctionDefinitionAST *funDef = nullptr;
+ int idx = 0;
+ for (; idx < path.size(); ++idx) {
+ AST *node = path.at(idx);
+ if (idx > 1) {
+ if (DeclaratorIdAST *declId = node->asDeclaratorId()) {
+ if (file->isCursorOn(declId)) {
+ if (FunctionDefinitionAST *candidate = path.at(idx - 2)->asFunctionDefinition()) {
+ funDef = candidate;
+ break;
+ }
+ }
+ }
+ }
+
+ if (node->asClassSpecifier())
+ return;
+ }
+
+ if (!funDef || !funDef->symbol)
+ return;
+
+ Function *fun = funDef->symbol;
+ if (Class *matchingClass = isMemberFunction(interface.context(), fun)) {
+ const QualifiedNameId *qName = fun->name()->asQualifiedNameId();
+ for (Symbol *symbol = matchingClass->find(qName->identifier());
+ symbol; symbol = symbol->next()) {
+ Symbol *s = symbol;
+ if (fun->enclosingScope()->asTemplate()) {
+ if (const Template *templ = s->type()->asTemplateType()) {
+ if (Symbol *decl = templ->declaration()) {
+ if (decl->type()->asFunctionType())
+ s = decl;
+ }
+ }
+ }
+ if (!s->name()
+ || !qName->identifier()->match(s->identifier())
+ || !s->type()->asFunctionType())
+ continue;
+
+ if (s->type().match(fun->type())) {
+ // Declaration exists.
+ return;
+ }
+ }
+ const FilePath fileName = matchingClass->filePath();
+ const QString decl = InsertDeclOperation::generateDeclaration(fun);
+
+ // Add several possible insertion locations for declaration
+ DeclOperationFactory operation(interface, fileName, matchingClass, decl);
+
+ result << operation(InsertionPointLocator::Public, 5)
+ << operation(InsertionPointLocator::PublicSlot, 4)
+ << operation(InsertionPointLocator::Protected, 3)
+ << operation(InsertionPointLocator::ProtectedSlot, 2)
+ << operation(InsertionPointLocator::Private, 1)
+ << operation(InsertionPointLocator::PrivateSlot, 0);
+ }
+ }
+};
+
+class AddDeclarationForUndeclaredIdentifier : public CppQuickFixFactory
+{
+public:
+#ifdef WITH_TESTS
+ static QObject *createTest();
+ void setMembersOnly() { m_membersOnly = true; }
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ // Are we on a name?
+ const QList<AST *> &path = interface.path();
+ if (path.isEmpty())
+ return;
+ 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,
+ FollowSymbolMode::Exact,
+ CppModelManager::Backend::Builtin);
+ }
+
+ void collectOperations(const CppQuickFixInterface &interface,
+ 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 handleCall(const CPlusPlus::CallAST *call, const CppQuickFixInterface &interface,
+ 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 ((*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 (idExpr->name->asSimpleName()) {
+ maybeAddMember(interface, file->scopeAt(idExpr->firstToken()), "this",
+ returnTypeOrExpr, call, result);
+ }
+ }
+
+ // Returns whether to still do other checks.
+ bool checkForMemberInitializer(const CppQuickFixInterface &interface,
+ 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 true;
+ if (!path.at(size - 3)->asCtorInitializer())
+ return true;
+ const FunctionDefinitionAST * ctor = path.at(size - 4)->asFunctionDefinition();
+ if (!ctor)
+ return false;
+
+ // Now find the class.
+ const Class *theClass = nullptr;
+ if (size > 4) {
+ const ClassSpecifierAST * const classSpec = path.at(size - 5)->asClassSpecifier();
+ if (classSpec) // Inline constructor. We get the class directly.
+ theClass = classSpec->symbol;
+ }
+ if (!theClass) {
+ // 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);
+ if (!matches.isEmpty())
+ theClass = matches.first()->enclosingClass();
+ }
+
+ if (!theClass)
+ 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 false;
+ }
+
+ result << new InsertMemberFromInitializationOp(
+ interface, theClass, memInitializer->name->asSimpleName(), memInitializer->expression,
+ nullptr, InsertionPointLocator::Private, false, false);
+ return false;
+ }
+
+ void maybeAddMember(const CppQuickFixInterface &interface, CPlusPlus::Scope *scope,
+ const QByteArray &classTypeExpr, const TypeOrExpr &typeOrExpr,
+ const CPlusPlus::CallAST *call, 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;
+
+ 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,
+ func->symbol->isConst());
+ }
+
+ void maybeAddStaticMember(
+ const CppQuickFixInterface &interface, const CPlusPlus::QualifiedNameAST *qualName,
+ const TypeOrExpr &typeOrExpr, const CPlusPlus::CallAST *call,
+ QuickFixOperations &result)
+ {
+ 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;
+ }
+
+ 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, false);
+ }
+ }
+
+ bool m_membersOnly = false;
+};
+
+#ifdef WITH_TESTS
+using namespace Tests;
+
+class AddDeclarationForUndeclaredIdentifierTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ // QTCREATORBUG-26004
+ void testLocalDeclFromUse()
+ {
+ const QByteArray original = "void func() {\n"
+ " QStringList list;\n"
+ " @it = list.cbegin();\n"
+ "}\n";
+ const QByteArray expected = "void func() {\n"
+ " QStringList list;\n"
+ " auto it = list.cbegin();\n"
+ "}\n";
+ AddDeclarationForUndeclaredIdentifier factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testInsertMemberFromUse_data()
+ {
+ QTest::addColumn<QByteArray>("original");
+ QTest::addColumn<QByteArray>("expected");
+
+ QByteArray original;
+ QByteArray expected;
+
+ original =
+ "class C {\n"
+ "public:\n"
+ " C(int x) : @m_x(x) {}\n"
+ "private:\n"
+ " int m_y;\n"
+ "};\n";
+ expected =
+ "class C {\n"
+ "public:\n"
+ " C(int x) : m_x(x) {}\n"
+ "private:\n"
+ " int m_y;\n"
+ " int m_x;\n"
+ "};\n";
+ QTest::addRow("inline constructor") << original << expected;
+
+ original =
+ "class C {\n"
+ "public:\n"
+ " C(int x, double d);\n"
+ "private:\n"
+ " int m_x;\n"
+ "};\n"
+ "C::C(int x, double d) : m_x(x), @m_d(d)\n";
+ expected =
+ "class C {\n"
+ "public:\n"
+ " C(int x, double d);\n"
+ "private:\n"
+ " int m_x;\n"
+ " double m_d;\n"
+ "};\n"
+ "C::C(int x, double d) : m_x(x), m_d(d)\n";
+ QTest::addRow("out-of-line constructor") << original << expected;
+
+ original =
+ "class C {\n"
+ "public:\n"
+ " C(int x) : @m_x(x) {}\n"
+ "private:\n"
+ " int m_x;\n"
+ "};\n";
+ expected = "";
+ QTest::addRow("member already present") << original << expected;
+
+ original =
+ "int func() { return 0; }\n"
+ "class C {\n"
+ "public:\n"
+ " C() : @m_x(func()) {}\n"
+ "private:\n"
+ " int m_y;\n"
+ "};\n";
+ expected =
+ "int func() { return 0; }\n"
+ "class C {\n"
+ "public:\n"
+ " C() : m_x(func()) {}\n"
+ "private:\n"
+ " int m_y;\n"
+ " 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"
+ " int value() const { return @valueInternal(); }\n"
+ "};\n";
+ expected =
+ "class C {\n"
+ "public:\n"
+ " int value() const { return valueInternal(); }\n"
+ "private:\n"
+ " int valueInternal() const;\n"
+ "};\n";
+ QTest::addRow("add const 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 testInsertMemberFromUse()
+ {
+ QFETCH(QByteArray, original);
+ QFETCH(QByteArray, expected);
+
+ QList<TestDocumentPtr> testDocuments({
+ CppTestDocument::create("file.h", original, expected)
+ });
+
+ AddDeclarationForUndeclaredIdentifier factory;
+ factory.setMembersOnly();
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+};
+
+class InsertDeclFromDefTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ /// Check from source file: Insert in header file.
+ void test()
+ {
+ insertToSectionDeclFromDef("public", 0);
+ insertToSectionDeclFromDef("public slots", 1);
+ insertToSectionDeclFromDef("protected", 2);
+ insertToSectionDeclFromDef("protected slots", 3);
+ insertToSectionDeclFromDef("private", 4);
+ insertToSectionDeclFromDef("private slots", 5);
+ }
+
+ void testTemplateFuncTypename()
+ {
+ QByteArray original =
+ "class Foo\n"
+ "{\n"
+ "};\n"
+ "\n"
+ "template<class T>\n"
+ "void Foo::fu@nc() {}\n";
+
+ QByteArray expected =
+ "class Foo\n"
+ "{\n"
+ "public:\n"
+ " template<class T>\n"
+ " void func();\n"
+ "};\n"
+ "\n"
+ "template<class T>\n"
+ "void Foo::fu@nc() {}\n";
+
+ InsertDeclFromDef factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory, {}, 0);
+ }
+
+ void testTemplateFuncInt()
+ {
+ QByteArray original =
+ "class Foo\n"
+ "{\n"
+ "};\n"
+ "\n"
+ "template<int N>\n"
+ "void Foo::fu@nc() {}\n";
+
+ QByteArray expected =
+ "class Foo\n"
+ "{\n"
+ "public:\n"
+ " template<int N>\n"
+ " void func();\n"
+ "};\n"
+ "\n"
+ "template<int N>\n"
+ "void Foo::fu@nc() {}\n";
+
+ InsertDeclFromDef factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory, {}, 0);
+ }
+
+ void testTemplateReturnType()
+ {
+ QByteArray original =
+ "class Foo\n"
+ "{\n"
+ "};\n"
+ "\n"
+ "std::vector<int> Foo::fu@nc() const {}\n";
+
+ QByteArray expected =
+ "class Foo\n"
+ "{\n"
+ "public:\n"
+ " std::vector<int> func() const;\n"
+ "};\n"
+ "\n"
+ "std::vector<int> Foo::func() const {}\n";
+
+ InsertDeclFromDef factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory, {}, 0);
+ }
+
+ void testNotTriggeredForTemplateFunc()
+ {
+ QByteArray contents =
+ "class Foo\n"
+ "{\n"
+ " template<class T>\n"
+ " void func();\n"
+ "};\n"
+ "\n"
+ "template<class T>\n"
+ "void Foo::fu@nc() {}\n";
+
+ InsertDeclFromDef factory;
+ QuickFixOperationTest(singleDocument(contents, ""), &factory);
+ }
+
+private:
+ // Function for one of InsertDeclDef section cases
+ void insertToSectionDeclFromDef(const QByteArray &section, int sectionIndex)
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+ QByteArray sectionString = section + ":\n";
+ if (sectionIndex == 4)
+ sectionString.clear();
+
+ // Header File
+ original =
+ "class Foo\n"
+ "{\n"
+ "};\n";
+ expected =
+ "class Foo\n"
+ "{\n"
+ + sectionString +
+ " Foo();\n"
+ "@};\n";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n"
+ "\n"
+ "Foo::Foo@()\n"
+ "{\n"
+ "}\n"
+ ;
+ expected = original;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDeclFromDef factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), sectionIndex);
+ }
+};
+
+QObject *AddDeclarationForUndeclaredIdentifier::createTest()
+{
+ return new AddDeclarationForUndeclaredIdentifierTest;
+}
+
+QObject *InsertDeclFromDef::createTest()
+{
+ return new InsertDeclFromDefTest;
+}
+
+#endif // WITH_TESTS
+} // namespace
+
+void registerCreateDeclarationFromUseQuickfixes()
+{
+ CppQuickFixFactory::registerFactory<InsertDeclFromDef>();
+ CppQuickFixFactory::registerFactory<AddDeclarationForUndeclaredIdentifier>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <createdeclarationfromuse.moc>
+#endif
diff --git a/src/plugins/cppeditor/quickfixes/createdeclarationfromuse.h b/src/plugins/cppeditor/quickfixes/createdeclarationfromuse.h
new file mode 100644
index 0000000000..d8452f5850
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/createdeclarationfromuse.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerCreateDeclarationFromUseQuickfixes();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/extractfunction.cpp b/src/plugins/cppeditor/quickfixes/extractfunction.cpp
new file mode 100644
index 0000000000..46be84fa78
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/extractfunction.cpp
@@ -0,0 +1,762 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "extractfunction.h"
+
+#include "../cppcodestylesettings.h"
+#include "../cppeditortr.h"
+#include "../cpprefactoringchanges.h"
+#include "../insertionpointlocator.h"
+#include "cppquickfix.h"
+#include "cppquickfixhelpers.h"
+
+#include <coreplugin/icore.h>
+#include <cplusplus/CppRewriter.h>
+#include <cplusplus/declarationcomments.h>
+#include <cplusplus/Overview.h>
+
+#include <QDialogButtonBox>
+#include <QFormLayout>
+#include <QPushButton>
+
+#include <functional>
+
+#ifdef WITH_TESTS
+#include "cppquickfix_test.h"
+#include <QTest>
+#endif
+
+using namespace CPlusPlus;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+using FunctionNameGetter = std::function<QString()>;
+
+class ExtractFunctionOptions
+{
+public:
+ static bool isValidFunctionName(const QString &name)
+ {
+ return !name.isEmpty() && isValidIdentifier(name);
+ }
+
+ bool hasValidFunctionName() const
+ {
+ return isValidFunctionName(funcName);
+ }
+
+ QString funcName;
+ InsertionPointLocator::AccessSpec access = InsertionPointLocator::Public;
+};
+
+class ExtractFunctionOperation : public CppQuickFixOperation
+{
+public:
+ ExtractFunctionOperation(
+ const CppQuickFixInterface &interface,
+ int extractionStart,
+ int extractionEnd,
+ FunctionDefinitionAST *refFuncDef,
+ Symbol *funcReturn,
+ QList<QPair<QString, QString>> relevantDecls,
+ FunctionNameGetter functionNameGetter = {})
+ : CppQuickFixOperation(interface)
+ , m_extractionStart(extractionStart)
+ , m_extractionEnd(extractionEnd)
+ , m_refFuncDef(refFuncDef)
+ , m_funcReturn(funcReturn)
+ , m_relevantDecls(relevantDecls)
+ , m_functionNameGetter(functionNameGetter)
+ {
+ setDescription(Tr::tr("Extract Function"));
+ }
+
+ void perform() override
+ {
+ QTC_ASSERT(!m_funcReturn || !m_relevantDecls.isEmpty(), return);
+
+ CppRefactoringChanges refactoring(snapshot());
+ ExtractFunctionOptions options;
+ if (m_functionNameGetter)
+ options.funcName = m_functionNameGetter();
+ else
+ options = getOptions();
+
+ if (!options.hasValidFunctionName())
+ return;
+ const QString &funcName = options.funcName;
+
+ Function *refFunc = m_refFuncDef->symbol;
+
+ // We don't need to rewrite the type for declarations made inside the reference function,
+ // since their scope will remain the same. Then we preserve the original spelling style.
+ // However, we must do so for the return type in the definition.
+ SubstitutionEnvironment env;
+ env.setContext(context());
+ env.switchScope(refFunc);
+ ClassOrNamespace *targetCoN = context().lookupType(refFunc->enclosingScope());
+ if (!targetCoN)
+ targetCoN = context().globalNamespace();
+ UseMinimalNames subs(targetCoN);
+ env.enter(&subs);
+
+ Overview printer = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ Control *control = context().bindings()->control().get();
+ QString funcDef;
+ QString funcDecl; // We generate a declaration only in the case of a member function.
+ QString funcCall;
+
+ Class *matchingClass = isMemberFunction(context(), refFunc);
+
+ // Write return type.
+ if (!m_funcReturn) {
+ funcDef.append(QLatin1String("void "));
+ if (matchingClass)
+ funcDecl.append(QLatin1String("void "));
+ } else {
+ const FullySpecifiedType &fullType = rewriteType(m_funcReturn->type(), &env, control);
+ funcDef.append(printer.prettyType(fullType) + QLatin1Char(' '));
+ funcDecl.append(printer.prettyType(m_funcReturn->type()) + QLatin1Char(' '));
+ }
+
+ // Write class qualification, if any.
+ if (matchingClass) {
+ const Scope *current = matchingClass;
+ QVector<const Name *> classes{matchingClass->name()};
+ while (current->enclosingScope()->asClass()) {
+ current = current->enclosingScope()->asClass();
+ classes.prepend(current->name());
+ }
+ while (current->enclosingScope() && current->enclosingScope()->asNamespace()) {
+ current = current->enclosingScope()->asNamespace();
+ if (current->name())
+ classes.prepend(current->name());
+ }
+ for (const Name *n : classes) {
+ const Name *name = rewriteName(n, &env, control);
+ funcDef.append(printer.prettyName(name));
+ funcDef.append(QLatin1String("::"));
+ }
+ }
+
+ // Write the extracted function itself and its call.
+ funcDef.append(funcName);
+ if (matchingClass)
+ funcDecl.append(funcName);
+ funcCall.append(funcName);
+ funcDef.append(QLatin1Char('('));
+ if (matchingClass)
+ funcDecl.append(QLatin1Char('('));
+ funcCall.append(QLatin1Char('('));
+ for (int i = m_funcReturn ? 1 : 0; i < m_relevantDecls.length(); ++i) {
+ QPair<QString, QString> p = m_relevantDecls.at(i);
+ funcCall.append(p.first);
+ funcDef.append(p.second);
+ if (matchingClass)
+ funcDecl.append(p.second);
+ if (i < m_relevantDecls.length() - 1) {
+ funcCall.append(QLatin1String(", "));
+ funcDef.append(QLatin1String(", "));
+ if (matchingClass)
+ funcDecl.append(QLatin1String(", "));
+ }
+ }
+ funcDef.append(QLatin1Char(')'));
+ if (matchingClass)
+ funcDecl.append(QLatin1Char(')'));
+ funcCall.append(QLatin1Char(')'));
+ if (refFunc->isConst()) {
+ funcDef.append(QLatin1String(" const"));
+ funcDecl.append(QLatin1String(" const"));
+ }
+ funcDef.append(QLatin1String("\n{\n"));
+ QString extract = currentFile()->textOf(m_extractionStart, m_extractionEnd);
+ extract.replace(QChar::ParagraphSeparator, QLatin1String("\n"));
+ if (!extract.endsWith(QLatin1Char('\n')) && m_funcReturn)
+ extract.append(QLatin1Char('\n'));
+ funcDef.append(extract);
+ if (matchingClass)
+ funcDecl.append(QLatin1String(";\n"));
+ if (m_funcReturn) {
+ funcDef.append(QLatin1String("\nreturn ")
+ + m_relevantDecls.at(0).first
+ + QLatin1Char(';'));
+ funcCall.prepend(m_relevantDecls.at(0).second + QLatin1String(" = "));
+ }
+ funcDef.append(QLatin1String("\n}\n\n"));
+ funcDef.replace(QChar::ParagraphSeparator, QLatin1String("\n"));
+ funcDef.prepend(inlinePrefix(currentFile()->filePath()));
+ funcCall.append(QLatin1Char(';'));
+
+ // Do not insert right between the function and an associated comment.
+ int position = currentFile()->startOf(m_refFuncDef);
+ const QList<Token> functionDoc = commentsForDeclaration(
+ m_refFuncDef->symbol, m_refFuncDef, *currentFile()->document(),
+ currentFile()->cppDocument());
+ if (!functionDoc.isEmpty()) {
+ position = currentFile()->cppDocument()->translationUnit()->getTokenPositionInDocument(
+ functionDoc.first(), currentFile()->document());
+ }
+
+ ChangeSet change;
+ change.insert(position, funcDef);
+ change.replace(m_extractionStart, m_extractionEnd, funcCall);
+ currentFile()->apply(change);
+
+ // Write declaration, if necessary.
+ if (matchingClass) {
+ InsertionPointLocator locator(refactoring);
+ const FilePath filePath = FilePath::fromUtf8(matchingClass->fileName());
+ const InsertionLocation &location =
+ locator.methodDeclarationInClass(filePath, matchingClass, options.access);
+ CppRefactoringFilePtr declFile = refactoring.cppFile(filePath);
+ declFile->apply(ChangeSet::makeInsert(
+ declFile->position(location.line(), location.column()),
+ location.prefix() + funcDecl + location.suffix()));
+ }
+ }
+
+ ExtractFunctionOptions getOptions() const
+ {
+ QDialog dlg(Core::ICore::dialogParent());
+ dlg.setWindowTitle(Tr::tr("Extract Function Refactoring"));
+ auto layout = new QFormLayout(&dlg);
+
+ auto funcNameEdit = new FancyLineEdit;
+ funcNameEdit->setValidationFunction([](FancyLineEdit *edit, QString *) {
+ return ExtractFunctionOptions::isValidFunctionName(edit->text());
+ });
+ layout->addRow(Tr::tr("Function name"), funcNameEdit);
+
+ auto accessCombo = new QComboBox;
+ accessCombo->addItem(
+ InsertionPointLocator::accessSpecToString(InsertionPointLocator::Public),
+ InsertionPointLocator::Public);
+ accessCombo->addItem(
+ InsertionPointLocator::accessSpecToString(InsertionPointLocator::PublicSlot),
+ InsertionPointLocator::PublicSlot);
+ accessCombo->addItem(
+ InsertionPointLocator::accessSpecToString(InsertionPointLocator::Protected),
+ InsertionPointLocator::Protected);
+ accessCombo->addItem(
+ InsertionPointLocator::accessSpecToString(InsertionPointLocator::ProtectedSlot),
+ InsertionPointLocator::ProtectedSlot);
+ accessCombo->addItem(
+ InsertionPointLocator::accessSpecToString(InsertionPointLocator::Private),
+ InsertionPointLocator::Private);
+ accessCombo->addItem(
+ InsertionPointLocator::accessSpecToString(InsertionPointLocator::PrivateSlot),
+ InsertionPointLocator::PrivateSlot);
+ layout->addRow(Tr::tr("Access"), accessCombo);
+
+ auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+ QObject::connect(buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
+ QObject::connect(buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
+ QPushButton *ok = buttonBox->button(QDialogButtonBox::Ok);
+ ok->setEnabled(false);
+ QObject::connect(funcNameEdit, &Utils::FancyLineEdit::validChanged,
+ ok, &QPushButton::setEnabled);
+ layout->addWidget(buttonBox);
+
+ if (dlg.exec() == QDialog::Accepted) {
+ ExtractFunctionOptions options;
+ options.funcName = funcNameEdit->text();
+ options.access = static_cast<InsertionPointLocator::AccessSpec>(accessCombo->
+ currentData().toInt());
+ return options;
+ }
+ return ExtractFunctionOptions();
+ }
+
+ int m_extractionStart;
+ int m_extractionEnd;
+ FunctionDefinitionAST *m_refFuncDef;
+ Symbol *m_funcReturn;
+ QList<QPair<QString, QString> > m_relevantDecls;
+ FunctionNameGetter m_functionNameGetter;
+};
+
+static QPair<QString, QString> assembleDeclarationData(
+ const QString &specifiers,
+ DeclaratorAST *decltr,
+ const CppRefactoringFilePtr &file,
+ const Overview &printer)
+{
+ QTC_ASSERT(decltr, return (QPair<QString, QString>()));
+ if (decltr->core_declarator
+ && decltr->core_declarator->asDeclaratorId()
+ && decltr->core_declarator->asDeclaratorId()->name) {
+ QString decltrText = file->textOf(file->startOf(decltr),
+ file->endOf(decltr->core_declarator));
+ if (!decltrText.isEmpty()) {
+ const QString &name = printer.prettyName(
+ decltr->core_declarator->asDeclaratorId()->name->name);
+ QString completeDecl = specifiers;
+ if (!decltrText.contains(QLatin1Char(' ')))
+ completeDecl.append(QLatin1Char(' ') + decltrText);
+ else
+ completeDecl.append(decltrText);
+ return {name, completeDecl};
+ }
+ }
+ return QPair<QString, QString>();
+}
+
+class FunctionExtractionAnalyser : public ASTVisitor
+{
+public:
+ FunctionExtractionAnalyser(TranslationUnit *unit,
+ const int selStart,
+ const int selEnd,
+ const CppRefactoringFilePtr &file,
+ const Overview &printer)
+ : ASTVisitor(unit)
+ , m_done(false)
+ , m_failed(false)
+ , m_selStart(selStart)
+ , m_selEnd(selEnd)
+ , m_extractionStart(0)
+ , m_extractionEnd(0)
+ , m_file(file)
+ , m_printer(printer)
+ {}
+
+ bool operator()(FunctionDefinitionAST *refFunDef)
+ {
+ accept(refFunDef);
+
+ if (!m_failed && m_extractionStart == m_extractionEnd)
+ m_failed = true;
+
+ return !m_failed;
+ }
+
+ bool preVisit(AST *) override
+ {
+ return !m_done;
+ }
+
+ void statement(StatementAST *stmt)
+ {
+ if (!stmt)
+ return;
+
+ const int stmtStart = m_file->startOf(stmt);
+ const int stmtEnd = m_file->endOf(stmt);
+
+ if (stmtStart >= m_selEnd
+ || (m_extractionStart && stmtEnd > m_selEnd)) {
+ m_done = true;
+ return;
+ }
+
+ if (stmtStart >= m_selStart && !m_extractionStart)
+ m_extractionStart = stmtStart;
+ if (stmtEnd > m_extractionEnd && m_extractionStart)
+ m_extractionEnd = stmtEnd;
+
+ accept(stmt);
+ }
+
+ bool visit(CaseStatementAST *stmt) override
+ {
+ statement(stmt->statement);
+ return false;
+ }
+
+ bool visit(CompoundStatementAST *stmt) override
+ {
+ for (StatementListAST *it = stmt->statement_list; it; it = it->next) {
+ statement(it->value);
+ if (m_done)
+ break;
+ }
+ return false;
+ }
+
+ bool visit(DoStatementAST *stmt) override
+ {
+ statement(stmt->statement);
+ return false;
+ }
+
+ bool visit(ForeachStatementAST *stmt) override
+ {
+ statement(stmt->statement);
+ return false;
+ }
+
+ bool visit(RangeBasedForStatementAST *stmt) override
+ {
+ statement(stmt->statement);
+ return false;
+ }
+
+ bool visit(ForStatementAST *stmt) override
+ {
+ statement(stmt->initializer);
+ if (!m_done)
+ statement(stmt->statement);
+ return false;
+ }
+
+ bool visit(IfStatementAST *stmt) override
+ {
+ statement(stmt->statement);
+ if (!m_done)
+ statement(stmt->else_statement);
+ return false;
+ }
+
+ bool visit(TryBlockStatementAST *stmt) override
+ {
+ statement(stmt->statement);
+ for (CatchClauseListAST *it = stmt->catch_clause_list; it; it = it->next) {
+ statement(it->value);
+ if (m_done)
+ break;
+ }
+ return false;
+ }
+
+ bool visit(WhileStatementAST *stmt) override
+ {
+ statement(stmt->statement);
+ return false;
+ }
+
+ bool visit(DeclarationStatementAST *declStmt) override
+ {
+ // We need to collect the declarations we see before the extraction or even inside it.
+ // They might need to be used as either a parameter or return value. Actually, we could
+ // still obtain their types from the local uses, but it's good to preserve the original
+ // typing style.
+ if (declStmt
+ && declStmt->declaration
+ && declStmt->declaration->asSimpleDeclaration()) {
+ SimpleDeclarationAST *simpleDecl = declStmt->declaration->asSimpleDeclaration();
+ if (simpleDecl->decl_specifier_list
+ && simpleDecl->declarator_list) {
+ const QString &specifiers =
+ m_file->textOf(m_file->startOf(simpleDecl),
+ m_file->endOf(simpleDecl->decl_specifier_list->lastValue()));
+ for (DeclaratorListAST *decltrList = simpleDecl->declarator_list;
+ decltrList;
+ decltrList = decltrList->next) {
+ const QPair<QString, QString> p =
+ assembleDeclarationData(specifiers, decltrList->value, m_file, m_printer);
+ if (!p.first.isEmpty())
+ m_knownDecls.insert(p.first, p.second);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ bool visit(ReturnStatementAST *) override
+ {
+ if (m_extractionStart) {
+ m_done = true;
+ m_failed = true;
+ }
+
+ return false;
+ }
+
+ bool m_done;
+ bool m_failed;
+ const int m_selStart;
+ const int m_selEnd;
+ int m_extractionStart;
+ int m_extractionEnd;
+ QHash<QString, QString> m_knownDecls;
+ CppRefactoringFilePtr m_file;
+ const Overview &m_printer;
+};
+
+//! Extracts the selected code and puts it to a function
+class ExtractFunction : public CppQuickFixFactory
+{
+public:
+ ExtractFunction(FunctionNameGetter functionNameGetter = FunctionNameGetter())
+ : m_functionNameGetter(functionNameGetter)
+ {}
+
+#ifdef WITH_TESTS
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const CppRefactoringFilePtr file = interface.currentFile();
+
+ // TODO: Fix upstream and uncomment; see QTCREATORBUG-28030.
+ // if (CppModelManager::usesClangd(file->editor()->textDocument())
+ // && file->cppDocument()->languageFeatures().cxxEnabled) {
+ // return;
+ // }
+
+ QTextCursor cursor = file->cursor();
+ if (!cursor.hasSelection())
+ return;
+
+ const QList<AST *> &path = interface.path();
+ FunctionDefinitionAST *refFuncDef = nullptr; // The "reference" function, which we will extract from.
+ for (int i = path.size() - 1; i >= 0; --i) {
+ refFuncDef = path.at(i)->asFunctionDefinition();
+ if (refFuncDef)
+ break;
+ }
+
+ if (!refFuncDef
+ || !refFuncDef->function_body
+ || !refFuncDef->function_body->asCompoundStatement()
+ || !refFuncDef->function_body->asCompoundStatement()->statement_list
+ || !refFuncDef->symbol
+ || !refFuncDef->symbol->name()
+ || refFuncDef->symbol->enclosingScope()->asTemplate() /* TODO: Templates... */) {
+ return;
+ }
+
+ // Adjust selection ends.
+ int selStart = cursor.selectionStart();
+ int selEnd = cursor.selectionEnd();
+ if (selStart > selEnd)
+ std::swap(selStart, selEnd);
+
+ Overview printer;
+
+ // Analyze the content to be extracted, which consists of determining the statements
+ // which are complete and collecting the declarations seen.
+ FunctionExtractionAnalyser analyser(interface.semanticInfo().doc->translationUnit(),
+ selStart, selEnd,
+ file,
+ printer);
+ if (!analyser(refFuncDef))
+ return;
+
+ // We also need to collect the declarations of the parameters from the reference function.
+ QSet<QString> refFuncParams;
+ if (refFuncDef->declarator->postfix_declarator_list
+ && refFuncDef->declarator->postfix_declarator_list->value
+ && refFuncDef->declarator->postfix_declarator_list->value->asFunctionDeclarator()) {
+ FunctionDeclaratorAST *funcDecltr =
+ refFuncDef->declarator->postfix_declarator_list->value->asFunctionDeclarator();
+ if (funcDecltr->parameter_declaration_clause
+ && funcDecltr->parameter_declaration_clause->parameter_declaration_list) {
+ for (ParameterDeclarationListAST *it =
+ funcDecltr->parameter_declaration_clause->parameter_declaration_list;
+ it;
+ it = it->next) {
+ ParameterDeclarationAST *paramDecl = it->value->asParameterDeclaration();
+ if (paramDecl->declarator) {
+ const QString &specifiers =
+ file->textOf(file->startOf(paramDecl),
+ file->endOf(paramDecl->type_specifier_list->lastValue()));
+ const QPair<QString, QString> &p =
+ assembleDeclarationData(specifiers, paramDecl->declarator,
+ file, printer);
+ if (!p.first.isEmpty()) {
+ analyser.m_knownDecls.insert(p.first, p.second);
+ refFuncParams.insert(p.first);
+ }
+ }
+ }
+ }
+ }
+
+ // Identify what would be parameters for the new function and its return value, if any.
+ Symbol *funcReturn = nullptr;
+ QList<QPair<QString, QString> > relevantDecls;
+ const SemanticInfo::LocalUseMap localUses = interface.semanticInfo().localUses;
+ for (auto it = localUses.cbegin(), end = localUses.cend(); it != end; ++it) {
+ bool usedBeforeExtraction = false;
+ bool usedAfterExtraction = false;
+ bool usedInsideExtraction = false;
+ const QList<SemanticInfo::Use> &uses = it.value();
+ for (const SemanticInfo::Use &use : uses) {
+ if (use.isInvalid())
+ continue;
+
+ const int position = file->position(use.line, use.column);
+ if (position < analyser.m_extractionStart)
+ usedBeforeExtraction = true;
+ else if (position >= analyser.m_extractionEnd)
+ usedAfterExtraction = true;
+ else
+ usedInsideExtraction = true;
+ }
+
+ const QString &name = printer.prettyName(it.key()->name());
+
+ if ((usedBeforeExtraction && usedInsideExtraction)
+ || (usedInsideExtraction && refFuncParams.contains(name))) {
+ QTC_ASSERT(analyser.m_knownDecls.contains(name), return);
+ relevantDecls.push_back({name, analyser.m_knownDecls.value(name)});
+ }
+
+ // We assume that the first use of a local corresponds to its declaration.
+ if (usedInsideExtraction && usedAfterExtraction && !usedBeforeExtraction) {
+ if (!funcReturn) {
+ QTC_ASSERT(analyser.m_knownDecls.contains(name), return);
+ // The return, if any, is stored as the first item in the list.
+ relevantDecls.push_front({name, analyser.m_knownDecls.value(name)});
+ funcReturn = it.key();
+ } else {
+ // Would require multiple returns. (Unless we do fancy things, as pointed below.)
+ return;
+ }
+ }
+ }
+
+ // The current implementation doesn't try to be too smart since it preserves the original form
+ // of the declarations. This might be or not the desired effect. An improvement would be to
+ // let the user somehow customize the function interface.
+ result << new ExtractFunctionOperation(interface,
+ analyser.m_extractionStart,
+ analyser.m_extractionEnd,
+ refFuncDef, funcReturn, relevantDecls,
+ m_functionNameGetter);
+ }
+
+private:
+ FunctionNameGetter m_functionNameGetter; // For tests to avoid GUI pop-up.
+};
+
+#ifdef WITH_TESTS
+using namespace Tests;
+
+class ExtractFunctionTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data()
+ {
+ QTest::addColumn<QByteArray>("original");
+ QTest::addColumn<QByteArray>("expected");
+
+ QTest::newRow("basic")
+ << QByteArray("// Documentation for f\n"
+ "void f()\n"
+ "{\n"
+ " @{start}g();@{end}\n"
+ "}\n")
+ << QByteArray("inline void extracted()\n"
+ "{\n"
+ " g();\n"
+ "}\n"
+ "\n"
+ "// Documentation for f\n"
+ "void f()\n"
+ "{\n"
+ " extracted();\n"
+ "}\n");
+
+ QTest::newRow("class function")
+ << QByteArray("class Foo\n"
+ "{\n"
+ "private:\n"
+ " void bar();\n"
+ "};\n\n"
+ "void Foo::bar()\n"
+ "{\n"
+ " @{start}g();@{end}\n"
+ "}\n")
+ << QByteArray("class Foo\n"
+ "{\n"
+ "public:\n"
+ " void extracted();\n\n"
+ "private:\n"
+ " void bar();\n"
+ "};\n\n"
+ "inline void Foo::extracted()\n"
+ "{\n"
+ " g();\n"
+ "}\n\n"
+ "void Foo::bar()\n"
+ "{\n"
+ " extracted();\n"
+ "}\n");
+
+ QTest::newRow("class in namespace")
+ << QByteArray("namespace NS {\n"
+ "class C {\n"
+ " void f(C &c);\n"
+ "};\n"
+ "}\n"
+ "void NS::C::f(NS::C &c)\n"
+ "{\n"
+ " @{start}C *c2 = &c;@{end}\n"
+ "}\n")
+ << QByteArray("namespace NS {\n"
+ "class C {\n"
+ " void f(C &c);\n"
+ "\n"
+ "public:\n"
+ " void extracted(NS::C &c);\n" // TODO: Remove non-required qualification
+ "};\n"
+ "}\n"
+ "inline void NS::C::extracted(NS::C &c)\n"
+ "{\n"
+ " C *c2 = &c;\n"
+ "}\n"
+ "\n"
+ "void NS::C::f(NS::C &c)\n"
+ "{\n"
+ " extracted(c);\n"
+ "}\n");
+
+ QTest::newRow("if-block")
+ << QByteArray("inline void func()\n"
+ "{\n"
+ " int dummy = 0;\n"
+ " @{start}if@{end} (dummy < 10) {\n"
+ " ++dummy;\n"
+ " }\n"
+ "}\n")
+ << QByteArray("inline void extracted(int dummy)\n"
+ "{\n"
+ " if (dummy < 10) {\n"
+ " ++dummy;\n"
+ " }\n"
+ "}\n\n"
+ "inline void func()\n"
+ "{\n"
+ " int dummy = 0;\n"
+ " extracted(dummy);\n"
+ "}\n");
+ }
+
+ void test()
+ {
+ QFETCH(QByteArray, original);
+ QFETCH(QByteArray, expected);
+
+ QList<TestDocumentPtr> testDocuments;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ ExtractFunction factory([]() { return QLatin1String("extracted"); });
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+};
+
+QObject *ExtractFunction::createTest() { return new ExtractFunctionTest; }
+
+#endif // WITH_TESTS
+
+} // namespace
+
+void registerExtractFunctionQuickfix()
+{
+ CppQuickFixFactory::registerFactory<ExtractFunction>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <extractfunction.moc>
+#endif
diff --git a/src/plugins/cppeditor/quickfixes/extractfunction.h b/src/plugins/cppeditor/quickfixes/extractfunction.h
new file mode 100644
index 0000000000..b41cef59c2
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/extractfunction.h
@@ -0,0 +1,7 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#pragma once
+
+namespace CppEditor::Internal {
+void registerExtractFunctionQuickfix();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/extractliteralasparameter.cpp b/src/plugins/cppeditor/quickfixes/extractliteralasparameter.cpp
new file mode 100644
index 0000000000..b5b23906e0
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/extractliteralasparameter.cpp
@@ -0,0 +1,560 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "extractliteralasparameter.h"
+
+#include "../cppeditortr.h"
+#include "../cppeditorwidget.h"
+#include "../cpprefactoringchanges.h"
+#include "cppquickfix.h"
+#include "cppquickfixhelpers.h"
+
+#include <cplusplus/ASTPath.h>
+#include <cplusplus/Overview.h>
+#include <cplusplus/TypeOfExpression.h>
+
+#ifdef WITH_TESTS
+#include "cppquickfix_test.h"
+#include <QtTest>
+#endif
+
+using namespace CPlusPlus;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+struct ReplaceLiteralsResult
+{
+ Token token;
+ QString literalText;
+};
+
+template <class T>
+class ReplaceLiterals : private ASTVisitor
+{
+public:
+ ReplaceLiterals(const CppRefactoringFilePtr &file, ChangeSet *changes, T *literal)
+ : ASTVisitor(file->cppDocument()->translationUnit()), m_file(file), m_changes(changes),
+ m_literal(literal)
+ {
+ m_result.token = m_file->tokenAt(literal->firstToken());
+ m_literalTokenText = m_result.token.spell();
+ m_result.literalText = QLatin1String(m_literalTokenText);
+ if (m_result.token.isCharLiteral()) {
+ m_result.literalText.prepend(QLatin1Char('\''));
+ m_result.literalText.append(QLatin1Char('\''));
+ if (m_result.token.kind() == T_WIDE_CHAR_LITERAL)
+ m_result.literalText.prepend(QLatin1Char('L'));
+ else if (m_result.token.kind() == T_UTF16_CHAR_LITERAL)
+ m_result.literalText.prepend(QLatin1Char('u'));
+ else if (m_result.token.kind() == T_UTF32_CHAR_LITERAL)
+ m_result.literalText.prepend(QLatin1Char('U'));
+ } else if (m_result.token.isStringLiteral()) {
+ m_result.literalText.prepend(QLatin1Char('"'));
+ m_result.literalText.append(QLatin1Char('"'));
+ if (m_result.token.kind() == T_WIDE_STRING_LITERAL)
+ m_result.literalText.prepend(QLatin1Char('L'));
+ else if (m_result.token.kind() == T_UTF16_STRING_LITERAL)
+ m_result.literalText.prepend(QLatin1Char('u'));
+ else if (m_result.token.kind() == T_UTF32_STRING_LITERAL)
+ m_result.literalText.prepend(QLatin1Char('U'));
+ }
+ }
+
+ ReplaceLiteralsResult apply(AST *ast)
+ {
+ ast->accept(this);
+ return m_result;
+ }
+
+private:
+ bool visit(T *ast) override
+ {
+ if (ast != m_literal
+ && strcmp(m_file->tokenAt(ast->firstToken()).spell(), m_literalTokenText) != 0) {
+ return true;
+ }
+ int start, end;
+ m_file->startAndEndOf(ast->firstToken(), &start, &end);
+ m_changes->replace(start, end, QLatin1String("newParameter"));
+ return true;
+ }
+
+ const CppRefactoringFilePtr &m_file;
+ ChangeSet *m_changes;
+ T *m_literal;
+ const char *m_literalTokenText;
+ ReplaceLiteralsResult m_result;
+};
+
+class ExtractLiteralAsParameterOp : public CppQuickFixOperation
+{
+public:
+ ExtractLiteralAsParameterOp(const CppQuickFixInterface &interface, int priority,
+ ExpressionAST *literal, FunctionDefinitionAST *function)
+ : CppQuickFixOperation(interface, priority),
+ m_literal(literal),
+ m_functionDefinition(function)
+ {
+ setDescription(Tr::tr("Extract Constant as Function Parameter"));
+ }
+
+ struct FoundDeclaration
+ {
+ FunctionDeclaratorAST *ast = nullptr;
+ CppRefactoringFilePtr file;
+ };
+
+ FoundDeclaration findDeclaration(const CppRefactoringChanges &refactoring,
+ FunctionDefinitionAST *ast)
+ {
+ FoundDeclaration result;
+ Function *func = ast->symbol;
+ if (Class *matchingClass = isMemberFunction(context(), func)) {
+ // Dealing with member functions
+ const QualifiedNameId *qName = func->name()->asQualifiedNameId();
+ for (Symbol *s = matchingClass->find(qName->identifier()); s; s = s->next()) {
+ if (!s->name()
+ || !qName->identifier()->match(s->identifier())
+ || !s->type()->asFunctionType()
+ || !s->type().match(func->type())
+ || s->asFunction()) {
+ continue;
+ }
+
+ const FilePath declFilePath = matchingClass->filePath();
+ result.file = refactoring.cppFile(declFilePath);
+ ASTPath astPath(result.file->cppDocument());
+ const QList<AST *> path = astPath(s->line(), s->column());
+ SimpleDeclarationAST *simpleDecl = nullptr;
+ for (AST *node : path) {
+ simpleDecl = node->asSimpleDeclaration();
+ if (simpleDecl) {
+ if (simpleDecl->symbols && !simpleDecl->symbols->next) {
+ result.ast = functionDeclarator(simpleDecl);
+ return result;
+ }
+ }
+ }
+
+ if (simpleDecl)
+ break;
+ }
+ } else if (Namespace *matchingNamespace = isNamespaceFunction(context(), func)) {
+ // Dealing with free functions and inline member functions.
+ bool isHeaderFile;
+ FilePath declFilePath = correspondingHeaderOrSource(filePath(), &isHeaderFile);
+ if (!declFilePath.exists())
+ return FoundDeclaration();
+ result.file = refactoring.cppFile(declFilePath);
+ if (!result.file)
+ return FoundDeclaration();
+ const LookupContext lc(result.file->cppDocument(), snapshot());
+ const QList<LookupItem> candidates = lc.lookup(func->name(), matchingNamespace);
+ for (const LookupItem &candidate : candidates) {
+ if (Symbol *s = candidate.declaration()) {
+ if (s->asDeclaration()) {
+ ASTPath astPath(result.file->cppDocument());
+ const QList<AST *> path = astPath(s->line(), s->column());
+ for (AST *node : path) {
+ SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration();
+ if (simpleDecl) {
+ result.ast = functionDeclarator(simpleDecl);
+ return result;
+ }
+ }
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ void perform() override
+ {
+ FunctionDeclaratorAST *functionDeclaratorOfDefinition
+ = functionDeclarator(m_functionDefinition);
+ const CppRefactoringChanges refactoring(snapshot());
+ deduceTypeNameOfLiteral(currentFile()->cppDocument());
+
+ ChangeSet changes;
+ if (NumericLiteralAST *concreteLiteral = m_literal->asNumericLiteral()) {
+ m_literalInfo = ReplaceLiterals<NumericLiteralAST>(currentFile(), &changes,
+ concreteLiteral)
+ .apply(m_functionDefinition->function_body);
+ } else if (StringLiteralAST *concreteLiteral = m_literal->asStringLiteral()) {
+ m_literalInfo = ReplaceLiterals<StringLiteralAST>(currentFile(), &changes,
+ concreteLiteral)
+ .apply(m_functionDefinition->function_body);
+ } else if (BoolLiteralAST *concreteLiteral = m_literal->asBoolLiteral()) {
+ m_literalInfo = ReplaceLiterals<BoolLiteralAST>(currentFile(), &changes,
+ concreteLiteral)
+ .apply(m_functionDefinition->function_body);
+ }
+ const FoundDeclaration functionDeclaration
+ = findDeclaration(refactoring, m_functionDefinition);
+ appendFunctionParameter(functionDeclaratorOfDefinition, currentFile(), &changes,
+ !functionDeclaration.ast);
+ if (functionDeclaration.ast) {
+ if (currentFile()->filePath() != functionDeclaration.file->filePath()) {
+ ChangeSet declChanges;
+ appendFunctionParameter(functionDeclaration.ast, functionDeclaration.file, &declChanges,
+ true);
+ functionDeclaration.file->apply(declChanges);
+ } else {
+ appendFunctionParameter(functionDeclaration.ast, currentFile(), &changes,
+ true);
+ }
+ }
+ currentFile()->apply(changes);
+ QTextCursor c = currentFile()->cursor();
+ c.setPosition(c.position() - parameterName().length());
+ editor()->setTextCursor(c);
+ editor()->renameSymbolUnderCursor();
+ }
+
+private:
+ bool hasParameters(FunctionDeclaratorAST *ast) const
+ {
+ return ast->parameter_declaration_clause
+ && ast->parameter_declaration_clause->parameter_declaration_list
+ && ast->parameter_declaration_clause->parameter_declaration_list->value;
+ }
+
+ void deduceTypeNameOfLiteral(const Document::Ptr &document)
+ {
+ TypeOfExpression typeOfExpression;
+ typeOfExpression.init(document, snapshot());
+ Overview overview;
+ Scope *scope = m_functionDefinition->symbol->enclosingScope();
+ const QList<LookupItem> items = typeOfExpression(m_literal, document, scope);
+ if (!items.isEmpty())
+ m_typeName = overview.prettyType(items.first().type());
+ }
+
+ static QString parameterName() { return QLatin1String("newParameter"); }
+
+ QString parameterDeclarationTextToInsert(FunctionDeclaratorAST *ast) const
+ {
+ QString str;
+ if (hasParameters(ast))
+ str = QLatin1String(", ");
+ str += m_typeName;
+ if (!m_typeName.endsWith(QLatin1Char('*')))
+ str += QLatin1Char(' ');
+ str += parameterName();
+ return str;
+ }
+
+ FunctionDeclaratorAST *functionDeclarator(SimpleDeclarationAST *ast) const
+ {
+ for (DeclaratorListAST *decls = ast->declarator_list; decls; decls = decls->next) {
+ FunctionDeclaratorAST * const functionDeclaratorAST = functionDeclarator(decls->value);
+ if (functionDeclaratorAST)
+ return functionDeclaratorAST;
+ }
+ return nullptr;
+ }
+
+ FunctionDeclaratorAST *functionDeclarator(DeclaratorAST *ast) const
+ {
+ for (PostfixDeclaratorListAST *pds = ast->postfix_declarator_list; pds; pds = pds->next) {
+ FunctionDeclaratorAST *funcdecl = pds->value->asFunctionDeclarator();
+ if (funcdecl)
+ return funcdecl;
+ }
+ return nullptr;
+ }
+
+ FunctionDeclaratorAST *functionDeclarator(FunctionDefinitionAST *ast) const
+ {
+ return functionDeclarator(ast->declarator);
+ }
+
+ void appendFunctionParameter(FunctionDeclaratorAST *ast, const CppRefactoringFileConstPtr &file,
+ ChangeSet *changes, bool addDefaultValue)
+ {
+ if (!ast)
+ return;
+ if (m_declarationInsertionString.isEmpty())
+ m_declarationInsertionString = parameterDeclarationTextToInsert(ast);
+ QString insertion = m_declarationInsertionString;
+ if (addDefaultValue)
+ insertion += QLatin1String(" = ") + m_literalInfo.literalText;
+ changes->insert(file->startOf(ast->rparen_token), insertion);
+ }
+
+ ExpressionAST *m_literal;
+ FunctionDefinitionAST *m_functionDefinition;
+ QString m_typeName;
+ QString m_declarationInsertionString;
+ ReplaceLiteralsResult m_literalInfo;
+};
+
+/*!
+ Extracts the selected constant and converts it to a parameter of the current function.
+ Activates on numeric, bool, character, or string literal in the function body.
+ */
+class ExtractLiteralAsParameter : public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const QList<AST *> &path = interface.path();
+ if (path.count() < 2)
+ return;
+
+ AST * const lastAst = path.last();
+ ExpressionAST *literal;
+ if (!((literal = lastAst->asNumericLiteral())
+ || (literal = lastAst->asStringLiteral())
+ || (literal = lastAst->asBoolLiteral()))) {
+ return;
+ }
+
+ FunctionDefinitionAST *function;
+ int i = path.count() - 2;
+ while (!(function = path.at(i)->asFunctionDefinition())) {
+ // Ignore literals in lambda expressions for now.
+ if (path.at(i)->asLambdaExpression())
+ return;
+ if (--i < 0)
+ return;
+ }
+
+ PostfixDeclaratorListAST * const declaratorList = function->declarator->postfix_declarator_list;
+ if (!declaratorList)
+ return;
+ if (FunctionDeclaratorAST *declarator = declaratorList->value->asFunctionDeclarator()) {
+ if (declarator->parameter_declaration_clause
+ && declarator->parameter_declaration_clause->dot_dot_dot_token) {
+ // Do not handle functions with ellipsis parameter.
+ return;
+ }
+ }
+
+ const int priority = path.size() - 1;
+ result << new ExtractLiteralAsParameterOp(interface, priority, literal, function);
+ }
+};
+
+#ifdef WITH_TESTS
+using namespace Tests;
+
+class ExtractLiteralAsParameterTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void testTypeDeduction_data()
+ {
+ QTest::addColumn<QByteArray>("typeString");
+ QTest::addColumn<QByteArray>("literal");
+ QTest::newRow("int")
+ << QByteArray("int ") << QByteArray("156");
+ QTest::newRow("unsigned int")
+ << QByteArray("unsigned int ") << QByteArray("156u");
+ QTest::newRow("long")
+ << QByteArray("long ") << QByteArray("156l");
+ QTest::newRow("unsigned long")
+ << QByteArray("unsigned long ") << QByteArray("156ul");
+ QTest::newRow("long long")
+ << QByteArray("long long ") << QByteArray("156ll");
+ QTest::newRow("unsigned long long")
+ << QByteArray("unsigned long long ") << QByteArray("156ull");
+ QTest::newRow("float")
+ << QByteArray("float ") << QByteArray("3.14159f");
+ QTest::newRow("double")
+ << QByteArray("double ") << QByteArray("3.14159");
+ QTest::newRow("long double")
+ << QByteArray("long double ") << QByteArray("3.14159L");
+ QTest::newRow("bool")
+ << QByteArray("bool ") << QByteArray("true");
+ QTest::newRow("bool")
+ << QByteArray("bool ") << QByteArray("false");
+ QTest::newRow("char")
+ << QByteArray("char ") << QByteArray("'X'");
+ QTest::newRow("wchar_t")
+ << QByteArray("wchar_t ") << QByteArray("L'X'");
+ QTest::newRow("char16_t")
+ << QByteArray("char16_t ") << QByteArray("u'X'");
+ QTest::newRow("char32_t")
+ << QByteArray("char32_t ") << QByteArray("U'X'");
+ QTest::newRow("const char *")
+ << QByteArray("const char *") << QByteArray("\"narf\"");
+ QTest::newRow("const wchar_t *")
+ << QByteArray("const wchar_t *") << QByteArray("L\"narf\"");
+ QTest::newRow("const char16_t *")
+ << QByteArray("const char16_t *") << QByteArray("u\"narf\"");
+ QTest::newRow("const char32_t *")
+ << QByteArray("const char32_t *") << QByteArray("U\"narf\"");
+ }
+
+ void testTypeDeduction()
+ {
+ QFETCH(QByteArray, typeString);
+ QFETCH(QByteArray, literal);
+ const QByteArray original = QByteArray("void foo() {return @") + literal + QByteArray(";}\n");
+ const QByteArray expected = QByteArray("void foo(") + typeString + QByteArray("newParameter = ")
+ + literal + QByteArray(") {return newParameter;}\n");
+
+ if (literal == "3.14159") {
+ qWarning("Literal 3.14159 is wrongly reported as int. Skipping.");
+ return;
+ } else if (literal == "3.14159L") {
+ qWarning("Literal 3.14159L is wrongly reported as long. Skipping.");
+ return;
+ }
+
+ ExtractLiteralAsParameter factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testFreeFunctionSeparateFiles()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "void foo(const char *a, long b = 1);\n";
+ expected =
+ "void foo(const char *a, long b = 1, int newParameter = 156);\n";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "void foo(const char *a, long b)\n"
+ "{return 1@56 + 123 + 156;}\n";
+ expected =
+ "void foo(const char *a, long b, int newParameter)\n"
+ "{return newParameter + 123 + newParameter;}\n";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ ExtractLiteralAsParameter factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testMemberFunctionSeparateFiles()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "class Narf {\n"
+ "public:\n"
+ " int zort();\n"
+ "};\n";
+ expected =
+ "class Narf {\n"
+ "public:\n"
+ " int zort(int newParameter = 155);\n"
+ "};\n";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n\n"
+ "int Narf::zort()\n"
+ "{ return 15@5 + 1; }\n";
+ expected =
+ "#include \"file.h\"\n\n"
+ "int Narf::zort(int newParameter)\n"
+ "{ return newParameter + 1; }\n";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ ExtractLiteralAsParameter factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testNotTriggeringForInvalidCode()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ original =
+ "T(\"test\")\n"
+ "{\n"
+ " const int i = @14;\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.cpp", original, "");
+
+ ExtractLiteralAsParameter factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void test_data()
+ {
+ QTest::addColumn<QByteArray>("original");
+ QTest::addColumn<QByteArray>("expected");
+
+ QTest::newRow("ExtractLiteralAsParameter_freeFunction")
+ << QByteArray(
+ "void foo(const char *a, long b = 1)\n"
+ "{return 1@56 + 123 + 156;}\n")
+ << QByteArray(
+ "void foo(const char *a, long b = 1, int newParameter = 156)\n"
+ "{return newParameter + 123 + newParameter;}\n");
+ QTest::newRow("ExtractLiteralAsParameter_memberFunction")
+ << QByteArray(
+ "class Narf {\n"
+ "public:\n"
+ " int zort();\n"
+ "};\n\n"
+ "int Narf::zort()\n"
+ "{ return 15@5 + 1; }\n")
+ << QByteArray(
+ "class Narf {\n"
+ "public:\n"
+ " int zort(int newParameter = 155);\n"
+ "};\n\n"
+ "int Narf::zort(int newParameter)\n"
+ "{ return newParameter + 1; }\n");
+ QTest::newRow("ExtractLiteralAsParameter_memberFunctionInline")
+ << QByteArray(
+ "class Narf {\n"
+ "public:\n"
+ " int zort()\n"
+ " { return 15@5 + 1; }\n"
+ "};\n")
+ << QByteArray(
+ "class Narf {\n"
+ "public:\n"
+ " int zort(int newParameter = 155)\n"
+ " { return newParameter + 1; }\n"
+ "};\n");
+ }
+
+ void test()
+ {
+ QFETCH(QByteArray, original);
+ QFETCH(QByteArray, expected);
+ ExtractLiteralAsParameter factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+};
+
+QObject *ExtractLiteralAsParameter::createTest() { return new ExtractLiteralAsParameterTest; }
+
+#endif // WITH_TESTS
+} // namespace
+
+void registerExtractLiteralAsParameterQuickfix()
+{
+ CppQuickFixFactory::registerFactory<ExtractLiteralAsParameter>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <extractliteralasparameter.moc>
+#endif
diff --git a/src/plugins/cppeditor/quickfixes/extractliteralasparameter.h b/src/plugins/cppeditor/quickfixes/extractliteralasparameter.h
new file mode 100644
index 0000000000..e6359f1602
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/extractliteralasparameter.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerExtractLiteralAsParameterQuickfix();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/insertfunctiondefinition.cpp b/src/plugins/cppeditor/quickfixes/insertfunctiondefinition.cpp
new file mode 100644
index 0000000000..740c42909d
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/insertfunctiondefinition.cpp
@@ -0,0 +1,2124 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "insertfunctiondefinition.h"
+
+#include "../cppcodestylepreferences.h"
+#include "../cppcodestylesettings.h"
+#include "../cppeditortr.h"
+#include "../cppeditorwidget.h"
+#include "../cpprefactoringchanges.h"
+#include "../cpptoolssettings.h"
+#include "../insertionpointlocator.h"
+#include "../symbolfinder.h"
+#include "cppquickfix.h"
+#include "cppquickfixhelpers.h"
+
+#include <coreplugin/icore.h>
+#include <cplusplus/CppRewriter.h>
+#include <cplusplus/Overview.h>
+#include <utils/layoutbuilder.h>
+
+#include <QDialogButtonBox>
+#include <QGridLayout>
+#include <QHBoxLayout>
+
+#ifdef WITH_TESTS
+#include "cppquickfix_test.h"
+#include <QtTest>
+#endif
+
+using namespace CPlusPlus;
+using namespace TextEditor;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+enum DefPos {
+ DefPosInsideClass,
+ DefPosOutsideClass,
+ DefPosImplementationFile
+};
+
+enum class InsertDefsFromDeclsMode {
+ Off, // Testing: simulates user canceling the dialog
+ Impl, // Testing: simulates user choosing cpp file for every function
+ Alternating, // Testing: simulates user choosing a different DefPos for every function
+ User // Normal interactive mode
+};
+
+class InsertDefOperation: public CppQuickFixOperation
+{
+public:
+ // Make sure that either loc is valid or targetFileName is not empty.
+ InsertDefOperation(const CppQuickFixInterface &interface,
+ Declaration *decl, DeclaratorAST *declAST, const InsertionLocation &loc,
+ const DefPos defpos, const FilePath &targetFileName = {},
+ bool freeFunction = false)
+ : CppQuickFixOperation(interface, 0)
+ , m_decl(decl)
+ , m_declAST(declAST)
+ , m_loc(loc)
+ , m_defpos(defpos)
+ , m_targetFilePath(targetFileName)
+ {
+ if (m_defpos == DefPosImplementationFile) {
+ const FilePath declFile = decl->filePath();
+ const FilePath targetFile = m_loc.isValid() ? m_loc.filePath() : m_targetFilePath;
+ const FilePath resolved = targetFile.relativePathFrom(declFile.parentDir());
+ setPriority(2);
+ setDescription(Tr::tr("Add Definition in %1").arg(resolved.displayName()));
+ } else if (freeFunction) {
+ setDescription(Tr::tr("Add Definition Here"));
+ } else if (m_defpos == DefPosInsideClass) {
+ setDescription(Tr::tr("Add Definition Inside Class"));
+ } else if (m_defpos == DefPosOutsideClass) {
+ setPriority(1);
+ setDescription(Tr::tr("Add Definition Outside Class"));
+ }
+ }
+
+ static void insertDefinition(
+ const CppQuickFixOperation *op,
+ InsertionLocation loc,
+ DefPos defPos,
+ DeclaratorAST *declAST,
+ Declaration *decl,
+ const FilePath &targetFilePath,
+ ChangeSet *changeSet = nullptr)
+ {
+ CppRefactoringChanges refactoring(op->snapshot());
+ if (!loc.isValid())
+ loc = insertLocationForMethodDefinition(decl, true, NamespaceHandling::Ignore,
+ refactoring, targetFilePath);
+ QTC_ASSERT(loc.isValid(), return);
+
+ CppRefactoringFilePtr targetFile = refactoring.cppFile(loc.filePath());
+ Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ oo.showFunctionSignatures = true;
+ oo.showReturnTypes = true;
+ oo.showArgumentNames = true;
+ oo.showEnclosingTemplate = true;
+
+ // What we really want is to show template parameters for the class, but not for the
+ // function, but we cannot express that. This is an approximation that will work
+ // as long as either the surrounding class or the function is not a template.
+ oo.showTemplateParameters = decl->enclosingClass()
+ && decl->enclosingClass()->enclosingTemplate();
+
+ if (defPos == DefPosInsideClass) {
+ const int targetPos = targetFile->position(loc.line(), loc.column());
+ ChangeSet localChangeSet;
+ ChangeSet * const target = changeSet ? changeSet : &localChangeSet;
+ target->replace(targetPos - 1, targetPos, QLatin1String("\n {\n\n}")); // replace ';'
+
+ if (!changeSet) {
+ targetFile->setOpenEditor(true, targetPos);
+ targetFile->apply(*target);
+
+ // Move cursor inside definition
+ QTextCursor c = targetFile->cursor();
+ c.setPosition(targetPos);
+ c.movePosition(QTextCursor::Down);
+ c.movePosition(QTextCursor::EndOfLine);
+ op->editor()->setTextCursor(c);
+ }
+ } else {
+ // make target lookup context
+ Document::Ptr targetDoc = targetFile->cppDocument();
+ Scope *targetScope = targetDoc->scopeAt(loc.line(), loc.column());
+
+ // Correct scope in case of a function try-block. See QTCREATORBUG-14661.
+ if (targetScope && targetScope->asBlock()) {
+ if (Class * const enclosingClass = targetScope->enclosingClass())
+ targetScope = enclosingClass;
+ else
+ targetScope = targetScope->enclosingNamespace();
+ }
+
+ LookupContext targetContext(targetDoc, op->snapshot());
+ ClassOrNamespace *targetCoN = targetContext.lookupType(targetScope);
+ if (!targetCoN)
+ targetCoN = targetContext.globalNamespace();
+
+ // setup rewriting to get minimally qualified names
+ SubstitutionEnvironment env;
+ env.setContext(op->context());
+ env.switchScope(decl->enclosingScope());
+ UseMinimalNames q(targetCoN);
+ env.enter(&q);
+ Control *control = op->context().bindings()->control().get();
+
+ // rewrite the function type
+ const FullySpecifiedType tn = rewriteType(decl->type(), &env, control);
+
+ // rewrite the function name
+ if (nameIncludesOperatorName(decl->name())) {
+ CppRefactoringFilePtr file = refactoring.cppFile(op->filePath());
+ const QString operatorNameText = file->textOf(declAST->core_declarator);
+ oo.includeWhiteSpaceInOperatorName = operatorNameText.contains(QLatin1Char(' '));
+ }
+ const QString name = oo.prettyName(LookupContext::minimalName(decl, targetCoN,
+ control));
+
+ const QString inlinePref = inlinePrefix(targetFilePath, [defPos] {
+ return defPos == DefPosOutsideClass;
+ });
+
+ const QString prettyType = oo.prettyType(tn, name);
+
+ QString input = prettyType;
+ int index = 0;
+ while (input.startsWith("template")) {
+ QRegularExpression templateRegex("template\\s*<[^>]*>");
+ QRegularExpressionMatch match = templateRegex.match(input);
+ if (match.hasMatch()) {
+ index += match.captured().size() + 1;
+ input = input.mid(match.captured().size() + 1);
+ }
+ }
+
+ QString defText = prettyType;
+ defText.insert(index, inlinePref);
+ defText += QLatin1String("\n{\n\n}");
+
+ ChangeSet localChangeSet;
+ ChangeSet * const target = changeSet ? changeSet : &localChangeSet;
+ const int targetPos = targetFile->position(loc.line(), loc.column());
+ target->insert(targetPos, loc.prefix() + defText + loc.suffix());
+
+ if (!changeSet) {
+ targetFile->setOpenEditor(true, targetPos);
+ targetFile->apply(*target);
+
+ // Move cursor inside definition
+ QTextCursor c = targetFile->cursor();
+ c.setPosition(targetPos);
+ c.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor,
+ loc.prefix().count(QLatin1String("\n")) + 2);
+ c.movePosition(QTextCursor::EndOfLine);
+ if (defPos == DefPosImplementationFile) {
+ if (targetFile->editor())
+ targetFile->editor()->setTextCursor(c);
+ } else {
+ op->editor()->setTextCursor(c);
+ }
+ }
+ }
+ }
+
+private:
+ void perform() override
+ {
+ insertDefinition(this, m_loc, m_defpos, m_declAST, m_decl, m_targetFilePath);
+ }
+
+ Declaration *m_decl;
+ DeclaratorAST *m_declAST;
+ InsertionLocation m_loc;
+ const DefPos m_defpos;
+ const FilePath m_targetFilePath;
+};
+
+class MemberFunctionImplSetting
+{
+public:
+ Symbol *func = nullptr;
+ DefPos defPos = DefPosImplementationFile;
+};
+using MemberFunctionImplSettings = QList<MemberFunctionImplSetting>;
+
+class AddImplementationsDialog : public QDialog
+{
+public:
+ AddImplementationsDialog(const QList<Symbol *> &candidates, const FilePath &implFile)
+ : QDialog(Core::ICore::dialogParent()), m_candidates(candidates)
+ {
+ setWindowTitle(Tr::tr("Member Function Implementations"));
+
+ const auto defaultImplTargetComboBox = new QComboBox;
+ QStringList implTargetStrings{Tr::tr("None"), Tr::tr("Inline"), Tr::tr("Outside Class")};
+ if (!implFile.isEmpty())
+ implTargetStrings.append(implFile.fileName());
+ defaultImplTargetComboBox->insertItems(0, implTargetStrings);
+ connect(defaultImplTargetComboBox, &QComboBox::currentIndexChanged, this,
+ [this](int index) {
+ for (int i = 0; i < m_implTargetBoxes.size(); ++i) {
+ if (!m_candidates.at(i)->type()->asFunctionType()->isPureVirtual())
+ static_cast<QComboBox *>(m_implTargetBoxes.at(i))->setCurrentIndex(index);
+ }
+ });
+ const auto defaultImplTargetLayout = new QHBoxLayout;
+ defaultImplTargetLayout->addWidget(new QLabel(Tr::tr("Default implementation location:")));
+ defaultImplTargetLayout->addWidget(defaultImplTargetComboBox);
+
+ const auto candidatesLayout = new QGridLayout;
+ Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ oo.showFunctionSignatures = true;
+ oo.showReturnTypes = true;
+ for (int i = 0; i < m_candidates.size(); ++i) {
+ const Function * const func = m_candidates.at(i)->type()->asFunctionType();
+ QTC_ASSERT(func, continue);
+ const auto implTargetComboBox = new QComboBox;
+ m_implTargetBoxes.append(implTargetComboBox);
+ implTargetComboBox->insertItems(0, implTargetStrings);
+ if (func->isPureVirtual())
+ implTargetComboBox->setCurrentIndex(0);
+ candidatesLayout->addWidget(new QLabel(oo.prettyType(func->type(), func->name())),
+ i, 0);
+ candidatesLayout->addWidget(implTargetComboBox, i, 1);
+ }
+
+ const auto buttonBox
+ = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+ connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+
+ defaultImplTargetComboBox->setCurrentIndex(implTargetStrings.size() - 1);
+ const auto mainLayout = new QVBoxLayout(this);
+ mainLayout->addLayout(defaultImplTargetLayout);
+ mainLayout->addWidget(Layouting::createHr(this));
+ mainLayout->addLayout(candidatesLayout);
+ mainLayout->addWidget(buttonBox);
+ }
+
+ MemberFunctionImplSettings settings() const
+ {
+ QTC_ASSERT(m_candidates.size() == m_implTargetBoxes.size(), return {});
+ MemberFunctionImplSettings settings;
+ for (int i = 0; i < m_candidates.size(); ++i) {
+ MemberFunctionImplSetting setting;
+ const int index = m_implTargetBoxes.at(i)->currentIndex();
+ const bool addImplementation = index != 0;
+ if (!addImplementation)
+ continue;
+ setting.func = m_candidates.at(i);
+ setting.defPos = static_cast<DefPos>(index - 1);
+ settings << setting;
+ }
+ return settings;
+ }
+
+private:
+ const QList<Symbol *> m_candidates;
+ QList<QComboBox *> m_implTargetBoxes;
+};
+
+class InsertDefsOperation: public CppQuickFixOperation
+{
+public:
+ InsertDefsOperation(const CppQuickFixInterface &interface) : CppQuickFixOperation(interface)
+ {
+ setDescription(Tr::tr("Create Implementations for Member Functions"));
+
+ m_classAST = astForClassOperations(interface);
+ if (!m_classAST)
+ return;
+ const Class * const theClass = m_classAST->symbol;
+ if (!theClass)
+ return;
+
+ // Collect all member functions.
+ for (auto it = theClass->memberBegin(); it != theClass->memberEnd(); ++it) {
+ Symbol * const s = *it;
+ if (!s->identifier() || !s->type() || !s->asDeclaration() || s->asFunction())
+ continue;
+ Function * const func = s->type()->asFunctionType();
+ if (!func || func->isSignal() || func->isFriend())
+ continue;
+ Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ oo.showFunctionSignatures = true;
+ if (magicQObjectFunctions().contains(oo.prettyName(func->name())))
+ continue;
+ m_declarations << s;
+ }
+ }
+
+ bool isApplicable() const { return !m_declarations.isEmpty(); }
+ void setMode(InsertDefsFromDeclsMode mode) { m_mode = mode; }
+
+private:
+ void perform() override
+ {
+ QList<Symbol *> unimplemented;
+ SymbolFinder symbolFinder;
+ for (Symbol * const s : std::as_const(m_declarations)) {
+ if (!symbolFinder.findMatchingDefinition(s, snapshot()))
+ unimplemented << s;
+ }
+ if (unimplemented.isEmpty())
+ return;
+
+ CppRefactoringChanges refactoring(snapshot());
+ const bool isHeaderFile = ProjectFile::isHeader(ProjectFile::classify(filePath().toString()));
+ FilePath cppFile; // Only set if the class is defined in a header file.
+ if (isHeaderFile) {
+ InsertionPointLocator locator(refactoring);
+ for (const InsertionLocation &location
+ : locator.methodDefinition(unimplemented.first(), false, {})) {
+ if (!location.isValid())
+ continue;
+ const FilePath filePath = location.filePath();
+ if (ProjectFile::isHeader(ProjectFile::classify(filePath.path()))) {
+ const FilePath source = correspondingHeaderOrSource(filePath);
+ if (!source.isEmpty())
+ cppFile = source;
+ } else {
+ cppFile = filePath;
+ }
+ break;
+ }
+ }
+
+ MemberFunctionImplSettings settings;
+ switch (m_mode) {
+ case InsertDefsFromDeclsMode::User: {
+ AddImplementationsDialog dlg(unimplemented, cppFile);
+ if (dlg.exec() == QDialog::Accepted)
+ settings = dlg.settings();
+ break;
+ }
+ case InsertDefsFromDeclsMode::Impl: {
+ for (Symbol * const func : std::as_const(unimplemented)) {
+ MemberFunctionImplSetting setting;
+ setting.func = func;
+ setting.defPos = DefPosImplementationFile;
+ settings << setting;
+ }
+ break;
+ }
+ case InsertDefsFromDeclsMode::Alternating: {
+ int defPos = DefPosImplementationFile;
+ const auto incDefPos = [&defPos] {
+ defPos = (defPos + 1) % (DefPosImplementationFile + 2);
+ };
+ for (Symbol * const func : std::as_const(unimplemented)) {
+ incDefPos();
+ if (defPos > DefPosImplementationFile)
+ continue;
+ MemberFunctionImplSetting setting;
+ setting.func = func;
+ setting.defPos = static_cast<DefPos>(defPos);
+ settings << setting;
+ }
+ break;
+ }
+ case InsertDefsFromDeclsMode::Off:
+ break;
+ }
+
+ if (settings.isEmpty())
+ return;
+
+ class DeclFinder : public ASTVisitor
+ {
+ public:
+ DeclFinder(const CppRefactoringFile *file, const Symbol *func)
+ : ASTVisitor(file->cppDocument()->translationUnit()), m_func(func) {}
+
+ SimpleDeclarationAST *decl() const { return m_decl; }
+
+ private:
+ bool visit(SimpleDeclarationAST *decl) override
+ {
+ if (m_decl)
+ return false;
+ if (decl->symbols && decl->symbols->value == m_func)
+ m_decl = decl;
+ return !m_decl;
+ }
+
+ const Symbol * const m_func;
+ SimpleDeclarationAST *m_decl = nullptr;
+ };
+
+ QHash<FilePath, ChangeSet> changeSets;
+ for (const MemberFunctionImplSetting &setting : std::as_const(settings)) {
+ DeclFinder finder(currentFile().data(), setting.func);
+ finder.accept(m_classAST);
+ QTC_ASSERT(finder.decl(), continue);
+ InsertionLocation loc;
+ const FilePath targetFilePath = setting.defPos == DefPosImplementationFile
+ ? cppFile : filePath();
+ QTC_ASSERT(!targetFilePath.isEmpty(), continue);
+ if (setting.defPos == DefPosInsideClass) {
+ int line, column;
+ currentFile()->lineAndColumn(currentFile()->endOf(finder.decl()), &line, &column);
+ loc = InsertionLocation(filePath(), QString(), QString(), line, column);
+ }
+ ChangeSet &changeSet = changeSets[targetFilePath];
+ InsertDefOperation::insertDefinition(
+ this, loc, setting.defPos, finder.decl()->declarator_list->value,
+ setting.func->asDeclaration(),targetFilePath, &changeSet);
+ }
+ for (auto it = changeSets.cbegin(); it != changeSets.cend(); ++it)
+ refactoring.cppFile(it.key())->apply(it.value());
+ }
+
+ ClassSpecifierAST *m_classAST = nullptr;
+ InsertDefsFromDeclsMode m_mode = InsertDefsFromDeclsMode::User;
+ QList<Symbol *> m_declarations;
+};
+
+class InsertDefFromDecl: public CppQuickFixFactory
+{
+public:
+#ifdef WITH_TESTS
+ static QObject *createTest();
+#endif
+
+ void setOutside() { m_defPosOutsideClass = true; }
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const QList<AST *> &path = interface.path();
+
+ int idx = path.size() - 1;
+ for (; idx >= 0; --idx) {
+ AST *node = path.at(idx);
+ if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
+ if (idx > 0 && path.at(idx - 1)->asStatement())
+ return;
+ if (simpleDecl->symbols && !simpleDecl->symbols->next) {
+ if (Symbol *symbol = simpleDecl->symbols->value) {
+ if (Declaration *decl = symbol->asDeclaration()) {
+ if (Function *func = decl->type()->asFunctionType()) {
+ if (func->isSignal() || func->isPureVirtual() || func->isFriend())
+ return;
+
+ // Check if there is already a definition
+ SymbolFinder symbolFinder;
+ if (symbolFinder.findMatchingDefinition(decl, interface.snapshot(),
+ true)) {
+ return;
+ }
+
+ // Insert Position: Implementation File
+ DeclaratorAST *declAST = simpleDecl->declarator_list->value;
+ InsertDefOperation *op = nullptr;
+ ProjectFile::Kind kind = ProjectFile::classify(interface.filePath().toString());
+ const bool isHeaderFile = ProjectFile::isHeader(kind);
+ if (isHeaderFile) {
+ CppRefactoringChanges refactoring(interface.snapshot());
+ InsertionPointLocator locator(refactoring);
+ // find appropriate implementation file, but do not use this
+ // location, because insertLocationForMethodDefinition() should
+ // be used in perform() to get consistent insert positions.
+ for (const InsertionLocation &location :
+ locator.methodDefinition(decl, false, {})) {
+ if (!location.isValid())
+ continue;
+
+ const FilePath filePath = location.filePath();
+ if (ProjectFile::isHeader(ProjectFile::classify(filePath.path()))) {
+ const FilePath source = correspondingHeaderOrSource(filePath);
+ if (!source.isEmpty()) {
+ op = new InsertDefOperation(interface, decl, declAST,
+ InsertionLocation(),
+ DefPosImplementationFile,
+ source);
+ }
+ } else {
+ op = new InsertDefOperation(interface, decl, declAST,
+ InsertionLocation(),
+ DefPosImplementationFile,
+ filePath);
+ }
+
+ if (op)
+ result << op;
+ break;
+ }
+ }
+
+ // Determine if we are dealing with a free function
+ const bool isFreeFunction = func->enclosingClass() == nullptr;
+
+ // Insert Position: Outside Class
+ if (!isFreeFunction || m_defPosOutsideClass) {
+ result << new InsertDefOperation(interface, decl, declAST,
+ InsertionLocation(),
+ DefPosOutsideClass,
+ interface.filePath());
+ }
+
+ // Insert Position: Inside Class
+ // Determine insert location direct after the declaration.
+ int line, column;
+ const CppRefactoringFilePtr file = interface.currentFile();
+ file->lineAndColumn(file->endOf(simpleDecl), &line, &column);
+ const InsertionLocation loc
+ = InsertionLocation(interface.filePath(), QString(),
+ QString(), line, column);
+ result << new InsertDefOperation(interface, decl, declAST, loc,
+ DefPosInsideClass, FilePath(),
+ isFreeFunction);
+
+ return;
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ bool m_defPosOutsideClass = false;
+};
+
+//! Adds a definition for any number of member function declarations.
+class InsertDefsFromDecls : public CppQuickFixFactory
+{
+public:
+#ifdef WITH_TESTS
+ static QObject *createTest();
+#endif
+
+ void setMode(InsertDefsFromDeclsMode mode) { m_mode = mode; }
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const auto op = QSharedPointer<InsertDefsOperation>::create(interface);
+ op->setMode(m_mode);
+ if (op->isApplicable())
+ result << op;
+ }
+
+private:
+ InsertDefsFromDeclsMode m_mode = InsertDefsFromDeclsMode::User;
+};
+
+#ifdef WITH_TESTS
+using namespace Tests;
+
+static QList<TestDocumentPtr> singleHeader(const QByteArray &original, const QByteArray &expected)
+{
+ return {CppTestDocument::create("file.h", original, expected)};
+}
+
+class InsertDefFromDeclTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ /// Check if definition is inserted right after class for insert definition outside
+ void testAfterClass()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "class Foo\n"
+ "{\n"
+ " Foo();\n"
+ " void a@();\n"
+ "};\n"
+ "\n"
+ "class Bar {};\n";
+ expected =
+ "class Foo\n"
+ "{\n"
+ " Foo();\n"
+ " void a();\n"
+ "};\n"
+ "\n"
+ "inline void Foo::a()\n"
+ "{\n\n}\n"
+ "\n"
+ "class Bar {};\n";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n"
+ "\n"
+ "Foo::Foo()\n"
+ "{\n\n"
+ "}\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 1);
+ }
+
+ /// Check from header file: If there is a source file, insert the definition in the source file.
+ /// Case: Source file is empty.
+ void testHeaderSourceBasic1()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "struct Foo\n"
+ "{\n"
+ " Foo()@;\n"
+ "};\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original.resize(0);
+ expected =
+ "\n"
+ "Foo::Foo()\n"
+ "{\n\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check from header file: If there is a source file, insert the definition in the source file.
+ /// Case: Source file is not empty.
+ void testHeaderSourceBasic2()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original = "void f(const std::vector<int> &v)@;\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n"
+ "\n"
+ "int x;\n"
+ ;
+ expected =
+ "#include \"file.h\"\n"
+ "\n"
+ "int x;\n"
+ "\n"
+ "void f(const std::vector<int> &v)\n"
+ "{\n"
+ "\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check from source file: Insert in source file, not header file.
+ void testHeaderSourceBasic3()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Empty Header File
+ testDocuments << CppTestDocument::create("file.h", "", "");
+
+ // Source File
+ original =
+ "struct Foo\n"
+ "{\n"
+ " Foo()@;\n"
+ "};\n";
+ expected = original +
+ "\n"
+ "Foo::Foo()\n"
+ "{\n\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check from header file: If the class is in a namespace, the added function definition
+ /// name must be qualified accordingly.
+ void testHeaderSourceNamespace1()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "namespace N {\n"
+ "struct Foo\n"
+ "{\n"
+ " Foo()@;\n"
+ "};\n"
+ "}\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original.resize(0);
+ expected =
+ "\n"
+ "N::Foo::Foo()\n"
+ "{\n\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check from header file: If the class is in namespace N and the source file has a
+ /// "using namespace N" line, the function definition name must be qualified accordingly.
+ void testHeaderSourceNamespace2()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "namespace N {\n"
+ "struct Foo\n"
+ "{\n"
+ " Foo()@;\n"
+ "};\n"
+ "}\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n"
+ "using namespace N;\n"
+ ;
+ expected = original +
+ "\n"
+ "Foo::Foo()\n"
+ "{\n\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check definition insert inside class
+ void testInsideClass()
+ {
+ const QByteArray original =
+ "class Foo {\n"
+ " void b@ar();\n"
+ "};";
+ const QByteArray expected =
+ "class Foo {\n"
+ " void bar()\n"
+ " {\n\n"
+ " }\n"
+ "};";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory, ProjectExplorer::HeaderPaths(),
+ 1);
+ }
+
+ /// Check not triggering when definition exists
+ void testNotTriggeringWhenDefinitionExists()
+ {
+ const QByteArray original =
+ "class Foo {\n"
+ " void b@ar();\n"
+ "};\n"
+ "void Foo::bar() {}\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, ""), &factory, ProjectExplorer::HeaderPaths(), 1);
+ }
+
+ /// Find right implementation file.
+ void testFindRightImplementationFile()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "struct Foo\n"
+ "{\n"
+ " Foo();\n"
+ " void a();\n"
+ " void b@();\n"
+ "};\n"
+ "}\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File #1
+ original =
+ "#include \"file.h\"\n"
+ "\n"
+ "Foo::Foo()\n"
+ "{\n\n"
+ "}\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+
+ // Source File #2
+ original =
+ "#include \"file.h\"\n"
+ "\n"
+ "void Foo::a()\n"
+ "{\n\n"
+ "}\n";
+ expected = original +
+ "\n"
+ "void Foo::b()\n"
+ "{\n\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file2.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Ignore generated functions declarations when looking at the surrounding
+ /// functions declarations in order to find the right implementation file.
+ void testIgnoreSurroundingGeneratedDeclarations()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "#define DECLARE_HIDDEN_FUNCTION void hidden();\n"
+ "struct Foo\n"
+ "{\n"
+ " void a();\n"
+ " DECLARE_HIDDEN_FUNCTION\n"
+ " void b@();\n"
+ "};\n"
+ "}\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File #1
+ original =
+ "#include \"file.h\"\n"
+ "\n"
+ "void Foo::a()\n"
+ "{\n\n"
+ "}\n";
+ expected =
+ "#include \"file.h\"\n"
+ "\n"
+ "void Foo::a()\n"
+ "{\n\n"
+ "}\n"
+ "\n"
+ "void Foo::b()\n"
+ "{\n\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ // Source File #2
+ original =
+ "#include \"file.h\"\n"
+ "\n"
+ "void Foo::hidden()\n"
+ "{\n\n"
+ "}\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file2.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check if whitespace is respected for operator functions
+ void testRespectWsInOperatorNames1()
+ {
+ QByteArray original =
+ "class Foo\n"
+ "{\n"
+ " Foo &opera@tor =();\n"
+ "};\n";
+ QByteArray expected =
+ "class Foo\n"
+ "{\n"
+ " Foo &operator =();\n"
+ "};\n"
+ "\n"
+ "Foo &Foo::operator =()\n"
+ "{\n"
+ "\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ /// Check if whitespace is respected for operator functions
+ void testRespectWsInOperatorNames2()
+ {
+ QByteArray original =
+ "class Foo\n"
+ "{\n"
+ " Foo &opera@tor=();\n"
+ "};\n";
+ QByteArray expected =
+ "class Foo\n"
+ "{\n"
+ " Foo &operator=();\n"
+ "};\n"
+ "\n"
+ "Foo &Foo::operator=()\n"
+ "{\n"
+ "\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ /// Check that the noexcept exception specifier is transferred
+ void testNoexceptSpecifier()
+ {
+ QByteArray original =
+ "class Foo\n"
+ "{\n"
+ " void @foo() noexcept(false);\n"
+ "};\n";
+ QByteArray expected =
+ "class Foo\n"
+ "{\n"
+ " void foo() noexcept(false);\n"
+ "};\n"
+ "\n"
+ "void Foo::foo() noexcept(false)\n"
+ "{\n"
+ "\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ /// Check if a function like macro use is not separated by the function to insert
+ /// Case: Macro preceded by preproceesor directives and declaration.
+ void testMacroUsesAtEndOfFile1()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original = "void f()@;\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n"
+ "#define MACRO(X) X x;\n"
+ "int lala;\n"
+ "\n"
+ "MACRO(int)\n"
+ ;
+ expected =
+ "#include \"file.h\"\n"
+ "#define MACRO(X) X x;\n"
+ "int lala;\n"
+ "\n"
+ "\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ "\n"
+ "}\n"
+ "\n"
+ "MACRO(int)\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check if a function like macro use is not separated by the function to insert
+ /// Case: Marco preceded only by preprocessor directives.
+ void testMacroUsesAtEndOfFile2()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original = "void f()@;\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n"
+ "#define MACRO(X) X x;\n"
+ "\n"
+ "MACRO(int)\n"
+ ;
+ expected =
+ "#include \"file.h\"\n"
+ "#define MACRO(X) X x;\n"
+ "\n"
+ "\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ "\n"
+ "}\n"
+ "\n"
+ "MACRO(int)\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check if insertion happens before syntactically erroneous statements at end of file.
+ void testErroneousStatementAtEndOfFile()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original = "void f()@;\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n"
+ "\n"
+ "MissingSemicolon(int)\n"
+ ;
+ expected =
+ "#include \"file.h\"\n"
+ "\n"
+ "\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ "\n"
+ "}\n"
+ "\n"
+ "MissingSemicolon(int)\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check: Respect rvalue references
+ void testRvalueReference()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original = "void f(Foo &&)@;\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original = "";
+ expected =
+ "\n"
+ "void f(Foo &&)\n"
+ "{\n"
+ "\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testFunctionTryBlock()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original = R"(
+struct Foo {
+ void tryCatchFunc();
+ void @otherFunc();
+};
+)";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original = R"(
+#include "file.h"
+
+void Foo::tryCatchFunc() try {} catch (...) {}
+)";
+ expected = R"(
+#include "file.h"
+
+void Foo::tryCatchFunc() try {} catch (...) {}
+
+void Foo::otherFunc()
+{
+
+}
+)";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testUsingDecl()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original = R"(
+namespace N { struct S; }
+using N::S;
+
+void @func(const S &s);
+)";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original = R"(
+#include "file.h"
+)";
+ expected = R"(
+#include "file.h"
+
+void func(const S &s)
+{
+
+}
+)";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+
+ testDocuments.clear();
+ original = R"(
+namespace N1 {
+namespace N2 { struct S; }
+using N2::S;
+}
+
+void @func(const N1::S &s);
+)";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original = R"(
+#include "file.h"
+)";
+ expected = R"(
+#include "file.h"
+
+void func(const N1::S &s)
+{
+
+}
+)";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QuickFixOperationTest(testDocuments, &factory);
+
+ // No using declarations here, but the code model has one. No idea why.
+ testDocuments.clear();
+ original = R"(
+class B {};
+class D : public B {
+ @D();
+};
+)";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original = R"(
+#include "file.h"
+)";
+ expected = R"(
+#include "file.h"
+
+D::D()
+{
+
+}
+)";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QuickFixOperationTest(testDocuments, &factory);
+
+ testDocuments.clear();
+ original = R"(
+namespace ns1 { template<typename T> class span {}; }
+
+namespace ns {
+using ns1::span;
+class foo
+{
+ void @bar(ns::span<int>);
+};
+}
+)";
+ expected = R"(
+namespace ns1 { template<typename T> class span {}; }
+
+namespace ns {
+using ns1::span;
+class foo
+{
+ void bar(ns::span<int>);
+};
+
+void foo::bar(ns::span<int>)
+{
+
+}
+
+}
+)";
+ // TODO: Unneeded namespace gets inserted in RewriteName::visit(const QualifiedNameId *)
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Find right implementation file. (QTCREATORBUG-10728)
+ void testFindImplementationFile()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "class Foo {\n"
+ " void bar();\n"
+ " void ba@z();\n"
+ "};\n"
+ "\n"
+ "void Foo::bar()\n"
+ "{}\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n"
+ ;
+ expected =
+ "#include \"file.h\"\n"
+ "\n"
+ "void Foo::baz()\n"
+ "{\n"
+ "\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testUnicodeIdentifier()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+//
+// The following "non-latin1" code points are used in the tests:
+//
+// U+00FC - 2 code units in UTF8, 1 in UTF16 - LATIN SMALL LETTER U WITH DIAERESIS
+// U+4E8C - 3 code units in UTF8, 1 in UTF16 - CJK UNIFIED IDEOGRAPH-4E8C
+// U+10302 - 4 code units in UTF8, 2 in UTF16 - OLD ITALIC LETTER KE
+//
+
+#define UNICODE_U00FC "\xc3\xbc"
+#define UNICODE_U4E8C "\xe4\xba\x8c"
+#define UNICODE_U10302 "\xf0\x90\x8c\x82"
+#define TEST_UNICODE_IDENTIFIER UNICODE_U00FC UNICODE_U4E8C UNICODE_U10302
+
+ original =
+ "class Foo {\n"
+ " void @" TEST_UNICODE_IDENTIFIER "();\n"
+ "};\n";
+ ;
+ expected = original;
+ expected +=
+ "\n"
+ "void Foo::" TEST_UNICODE_IDENTIFIER "()\n"
+ "{\n"
+ "\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+#undef UNICODE_U00FC
+#undef UNICODE_U4E8C
+#undef UNICODE_U10302
+#undef TEST_UNICODE_IDENTIFIER
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testTemplateClass()
+ {
+ QByteArray original =
+ "template<class T>\n"
+ "class Foo\n"
+ "{\n"
+ " void fun@c1();\n"
+ " void func2();\n"
+ "};\n\n"
+ "template<class T>\n"
+ "void Foo<T>::func2() {}\n";
+ QByteArray expected =
+ "template<class T>\n"
+ "class Foo\n"
+ "{\n"
+ " void func1();\n"
+ " void func2();\n"
+ "};\n\n"
+ "template<class T>\n"
+ "void Foo<T>::func1()\n"
+ "{\n"
+ "\n"
+ "}\n\n"
+ "template<class T>\n"
+ "void Foo<T>::func2() {}\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testTemplateClassWithValueParam()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original =
+ "template<typename T, int size> struct MyArray {};\n"
+ "MyArray<int, 1> @foo();";
+ QByteArray expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ original = "#include \"file.h\"\n";
+ expected =
+ "#include \"file.h\"\n\n"
+ "MyArray<int, 1> foo()\n"
+ "{\n\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testTemplateFunction()
+ {
+ QByteArray original =
+ "class Foo\n"
+ "{\n"
+ " template<class T>\n"
+ " void fun@c();\n"
+ "};\n";
+ QByteArray expected =
+ "class Foo\n"
+ "{\n"
+ " template<class T>\n"
+ " void fun@c();\n"
+ "};\n"
+ "\n"
+ "template<class T>\n"
+ "void Foo::func()\n"
+ "{\n"
+ "\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testTemplateClassAndTemplateFunction()
+ {
+ QByteArray original =
+ "template<class T>"
+ "class Foo\n"
+ "{\n"
+ " template<class U>\n"
+ " T fun@c(U u);\n"
+ "};\n";
+ QByteArray expected =
+ "template<class T>"
+ "class Foo\n"
+ "{\n"
+ " template<class U>\n"
+ " T fun@c(U u);\n"
+ "};\n"
+ "\n"
+ "template<class T>\n"
+ "template<class U>\n"
+ "T Foo<T>::func(U u)\n"
+ "{\n"
+ "\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testTemplateClassAndFunctionInsideNamespace()
+ {
+ QByteArray original =
+ "namespace N {\n"
+ "template<class T>"
+ "class Foo\n"
+ "{\n"
+ " template<class U>\n"
+ " T fun@c(U u);\n"
+ "};\n"
+ "}\n";
+ QByteArray expected =
+ "namespace N {\n"
+ "template<class T>"
+ "class Foo\n"
+ "{\n"
+ " template<class U>\n"
+ " T fun@c(U u);\n"
+ "};\n"
+ "\n"
+ "template<class T>\n"
+ "template<class U>\n"
+ "T Foo<T>::func(U u)\n"
+ "{\n"
+ "\n"
+ "}\n"
+ "\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testFunctionWithSignedUnsignedArgument()
+ {
+ QByteArray original;
+ QByteArray expected;
+ InsertDefFromDecl factory;
+
+ original =R"--(
+class myclass
+{
+ myc@lass(QVector<signed> g);
+ myclass(QVector<unsigned> g);
+}
+)--";
+ expected =R"--(
+class myclass
+{
+ myclass(QVector<signed> g);
+ myclass(QVector<unsigned> g);
+}
+
+myclass::myclass(QVector<signed int> g)
+{
+
+}
+)--";
+
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+
+ original =R"--(
+class myclass
+{
+ myclass(QVector<signed> g);
+ myc@lass(QVector<unsigned> g);
+}
+)--";
+ expected =R"--(
+class myclass
+{
+ myclass(QVector<signed> g);
+ myclass(QVector<unsigned> g);
+}
+
+myclass::myclass(QVector<unsigned int> g)
+{
+
+}
+)--";
+
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+
+ original =R"--(
+class myclass
+{
+ unsigned f@oo(unsigned);
+}
+)--";
+ expected =R"--(
+class myclass
+{
+ unsigned foo(unsigned);
+}
+
+unsigned int myclass::foo(unsigned int)
+{
+
+}
+)--";
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+
+ original =R"--(
+class myclass
+{
+ signed f@oo(signed);
+}
+)--";
+ expected =R"--(
+class myclass
+{
+ signed foo(signed);
+}
+
+signed int myclass::foo(signed int)
+{
+
+}
+)--";
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testNotTriggeredForFriendFunc()
+ {
+ const QByteArray contents =
+ "class Foo\n"
+ "{\n"
+ " friend void f@unc();\n"
+ "};\n"
+ "\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(contents, ""), &factory);
+ }
+
+ void testMinimalFunctionParameterType()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original = R"(
+class C {
+ typedef int A;
+ A @foo(A);
+};
+)";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original = R"(
+#include "file.h"
+)";
+ expected = R"(
+#include "file.h"
+
+C::A C::foo(A)
+{
+
+}
+)";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+
+ testDocuments.clear();
+ // Header File
+ original = R"(
+namespace N {
+ struct S;
+ S @foo(const S &s);
+};
+)";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original = R"(
+#include "file.h"
+)";
+ expected = R"(
+#include "file.h"
+
+N::S N::foo(const S &s)
+{
+
+}
+)";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testAliasTemplateAsReturnType()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original = R"(
+struct foo {
+ struct foo2 {
+ template <typename T> using MyType = T;
+ MyType<int> @bar();
+ };
+};
+)";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original = R"(
+#include "file.h"
+)";
+ expected = R"(
+#include "file.h"
+
+foo::foo2::MyType<int> foo::foo2::bar()
+{
+
+}
+)";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void test_data()
+ {
+ QTest::addColumn<QByteArray>("original");
+ QTest::addColumn<QByteArray>("expected");
+
+ // Check from source file: If there is no header file, insert the definition after the class.
+ QByteArray original =
+ "struct Foo\n"
+ "{\n"
+ " Foo();@\n"
+ "};\n";
+ QByteArray expected = original +
+ "\n"
+ "Foo::Foo()\n"
+ "{\n\n"
+ "}\n";
+ QTest::newRow("basic") << original << expected;
+
+ original = "void free()@;\n";
+ expected = "void free()\n{\n\n}\n";
+ QTest::newRow("freeFunction") << original << expected;
+
+ original = "class Foo {\n"
+ "public:\n"
+ " Foo() {}\n"
+ "};\n"
+ "void freeFunc() {\n"
+ " Foo @f();"
+ "}\n";
+
+ // Check not triggering when it is a statement
+ QTest::newRow("notTriggeringStatement") << original << QByteArray();
+ }
+
+ void test()
+ {
+ QFETCH(QByteArray, original);
+ QFETCH(QByteArray, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testOutsideTemplateClassAndTemplateFunction()
+ {
+ QByteArray original =
+ "template<class T>"
+ "class Foo\n"
+ "{\n"
+ " template<class U>\n"
+ " void fun@c();\n"
+ "};\n";
+ QByteArray expected =
+ "template<class T>"
+ "class Foo\n"
+ "{\n"
+ " template<class U>\n"
+ " void fun@c();\n"
+ "};\n"
+ "\n"
+ "template<class T>\n"
+ "template<class U>\n"
+ "inline void Foo<T>::func()\n"
+ "{\n"
+ "\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ factory.setOutside();
+ QuickFixOperationTest(singleHeader(original, expected), &factory);
+ }
+
+ void testOutsideTemplateClass()
+ {
+ QByteArray original =
+ "template<class T>"
+ "class Foo\n"
+ "{\n"
+ " void fun@c();\n"
+ "};\n";
+ QByteArray expected =
+ "template<class T>"
+ "class Foo\n"
+ "{\n"
+ " void fun@c();\n"
+ "};\n"
+ "\n"
+ "template<class T>\n"
+ "inline void Foo<T>::func()\n"
+ "{\n"
+ "\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ factory.setOutside();
+ QuickFixOperationTest(singleHeader(original, expected), &factory);
+ }
+
+ void testOutsideTemplateFunction()
+ {
+ QByteArray original =
+ "class Foo\n"
+ "{\n"
+ " template<class U>\n"
+ " void fun@c();\n"
+ "};\n";
+ QByteArray expected =
+ "class Foo\n"
+ "{\n"
+ " template<class U>\n"
+ " void fun@c();\n"
+ "};\n"
+ "\n"
+ "template<class U>\n"
+ "inline void Foo::func()\n"
+ "{\n"
+ "\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ factory.setOutside();
+ QuickFixOperationTest(singleHeader(original, expected), &factory);
+ }
+
+ void testOutsideFunction()
+ {
+ QByteArray original =
+ "class Foo\n"
+ "{\n"
+ " void fun@c();\n"
+ "};\n";
+ QByteArray expected =
+ "class Foo\n"
+ "{\n"
+ " void fun@c();\n"
+ "};\n"
+ "\n"
+ "inline void Foo::func()\n"
+ "{\n"
+ "\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ factory.setOutside();
+ QuickFixOperationTest(singleHeader(original, expected), &factory);
+
+ }
+
+};
+
+class InsertDefsFromDeclsTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+
+ void test_data()
+ {
+ QTest::addColumn<QByteArrayList>("headers");
+ QTest::addColumn<QByteArrayList>("sources");
+ QTest::addColumn<int>("mode");
+
+ QByteArray origHeader = R"(
+namespace N {
+class @C
+{
+public:
+ friend void ignoredFriend();
+ void ignoredImplemented() {};
+ void ignoredImplemented2(); // Below
+ void ignoredImplemented3(); // In cpp file
+ void funcNotSelected();
+ void funcInline();
+ void funcBelow();
+ void funcCppFile();
+
+signals:
+ void ignoredSignal();
+};
+
+inline void C::ignoredImplemented2() {}
+
+} // namespace N)";
+ QByteArray origSource = R"(
+#include "file.h"
+
+namespace N {
+
+void C::ignoredImplemented3() {}
+
+} // namespace N)";
+
+ QByteArray expectedHeader = R"(
+namespace N {
+class C
+{
+public:
+ friend void ignoredFriend();
+ void ignoredImplemented() {};
+ void ignoredImplemented2(); // Below
+ void ignoredImplemented3(); // In cpp file
+ void funcNotSelected();
+ void funcInline()
+ {
+
+ }
+ void funcBelow();
+ void funcCppFile();
+
+signals:
+ void ignoredSignal();
+};
+
+inline void C::ignoredImplemented2() {}
+
+inline void C::funcBelow()
+{
+
+}
+
+} // namespace N)";
+ QByteArray expectedSource = R"(
+#include "file.h"
+
+namespace N {
+
+void C::ignoredImplemented3() {}
+
+void C::funcCppFile()
+{
+
+}
+
+} // namespace N)";
+ QTest::addRow("normal case")
+ << QByteArrayList{origHeader, expectedHeader}
+ << QByteArrayList{origSource, expectedSource}
+ << int(InsertDefsFromDeclsMode::Alternating);
+ QTest::addRow("aborted dialog")
+ << QByteArrayList{origHeader, origHeader}
+ << QByteArrayList{origSource, origSource}
+ << int(InsertDefsFromDeclsMode::Off);
+
+ origHeader = R"(
+ namespace N {
+ class @C
+ {
+ public:
+ friend void ignoredFriend();
+ void ignoredImplemented() {};
+ void ignoredImplemented2(); // Below
+ void ignoredImplemented3(); // In cpp file
+
+ signals:
+ void ignoredSignal();
+ };
+
+ inline void C::ignoredImplemented2() {}
+
+ } // namespace N)";
+ QTest::addRow("no candidates")
+ << QByteArrayList{origHeader, origHeader}
+ << QByteArrayList{origSource, origSource}
+ << int(InsertDefsFromDeclsMode::Alternating);
+
+ origHeader = R"(
+ namespace N {
+ class @C
+ {
+ public:
+ friend void ignoredFriend();
+ void ignoredImplemented() {};
+
+ signals:
+ void ignoredSignal();
+ };
+ } // namespace N)";
+ QTest::addRow("no member functions")
+ << QByteArrayList{origHeader, ""}
+ << QByteArrayList{origSource, ""}
+ << int(InsertDefsFromDeclsMode::Alternating);
+
+
+ }
+
+ void test()
+ {
+ QFETCH(QByteArrayList, headers);
+ QFETCH(QByteArrayList, sources);
+ QFETCH(int, mode);
+
+ QList<TestDocumentPtr> testDocuments(
+ {CppTestDocument::create("file.h", headers.at(0), headers.at(1)),
+ CppTestDocument::create("file.cpp", sources.at(0), sources.at(1))});
+ InsertDefsFromDecls factory;
+ factory.setMode(static_cast<InsertDefsFromDeclsMode>(mode));
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testInsertAndFormat()
+ {
+ if (!isClangFormatPresent())
+ QSKIP("This test reqires ClangFormat");
+
+ const QByteArray origHeader = R"(
+class @C
+{
+public:
+ void func1 (int const &i);
+ void func2 (double const d);
+};
+)";
+ const QByteArray origSource = R"(
+#include "file.h"
+)";
+
+ const QByteArray expectedSource = R"(
+#include "file.h"
+
+void C::func1 (int const &i)
+{
+
+}
+
+void C::func2 (double const d)
+{
+
+}
+)";
+
+ const QByteArray clangFormatSettings = R"(
+BreakBeforeBraces: Allman
+QualifierAlignment: Right
+SpaceBeforeParens: Always
+)";
+
+ const QList<TestDocumentPtr> testDocuments({
+ CppTestDocument::create("file.h", origHeader, origHeader),
+ CppTestDocument::create("file.cpp", origSource, expectedSource)});
+ InsertDefsFromDecls factory;
+ factory.setMode(InsertDefsFromDeclsMode::Impl);
+ CppCodeStylePreferences * const prefs = CppToolsSettings::cppCodeStyle();
+ const CppCodeStyleSettings settings = prefs->codeStyleSettings();
+ CppCodeStyleSettings tempSettings = settings;
+ tempSettings.forceFormatting = true;
+ prefs->setCodeStyleSettings(tempSettings);
+ QuickFixOperationTest(testDocuments, &factory, {}, {}, {}, clangFormatSettings);
+ prefs->setCodeStyleSettings(settings);
+ }
+};
+
+QObject *InsertDefFromDecl::createTest()
+{
+ return new InsertDefFromDeclTest;
+}
+
+QObject *InsertDefsFromDecls::createTest()
+{
+ return new InsertDefsFromDeclsTest;
+}
+
+#endif // WITH_TESTS
+} // namespace
+
+void registerInsertFunctionDefinitionQuickfixes()
+{
+ CppQuickFixFactory::registerFactory<InsertDefFromDecl>();
+ CppQuickFixFactory::registerFactory<InsertDefsFromDecls>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <insertfunctiondefinition.moc>
+#endif
diff --git a/src/plugins/cppeditor/quickfixes/insertfunctiondefinition.h b/src/plugins/cppeditor/quickfixes/insertfunctiondefinition.h
new file mode 100644
index 0000000000..77c4c87b6d
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/insertfunctiondefinition.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerInsertFunctionDefinitionQuickfixes();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/logicaloperationquickfixes.cpp b/src/plugins/cppeditor/quickfixes/logicaloperationquickfixes.cpp
new file mode 100644
index 0000000000..05ad754074
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/logicaloperationquickfixes.cpp
@@ -0,0 +1,381 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "logicaloperationquickfixes.h"
+
+#include "../cppeditortr.h"
+#include "../cpprefactoringchanges.h"
+#include "cppquickfix.h"
+
+#ifdef WITH_TESTS
+#include "cppquickfix_test.h"
+#include <QtTest>
+#endif
+
+using namespace CPlusPlus;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+class FlipLogicalOperandsOp : public CppQuickFixOperation
+{
+public:
+ FlipLogicalOperandsOp(const CppQuickFixInterface &interface, int priority,
+ BinaryExpressionAST *binary, QString replacement)
+ : CppQuickFixOperation(interface)
+ , binary(binary)
+ , replacement(replacement)
+ {
+ setPriority(priority);
+ }
+
+ QString description() const override
+ {
+ if (replacement.isEmpty())
+ return Tr::tr("Swap Operands");
+ else
+ return Tr::tr("Rewrite Using %1").arg(replacement);
+ }
+
+ void perform() override
+ {
+ ChangeSet changes;
+ changes.flip(currentFile()->range(binary->left_expression),
+ currentFile()->range(binary->right_expression));
+ if (!replacement.isEmpty())
+ changes.replace(currentFile()->range(binary->binary_op_token), replacement);
+
+ currentFile()->apply(changes);
+ }
+
+private:
+ BinaryExpressionAST *binary;
+ QString replacement;
+};
+
+class InverseLogicalComparisonOp : public CppQuickFixOperation
+{
+public:
+ InverseLogicalComparisonOp(const CppQuickFixInterface &interface,
+ int priority,
+ BinaryExpressionAST *binary,
+ Kind invertToken)
+ : CppQuickFixOperation(interface, priority)
+ , binary(binary)
+ {
+ Token tok;
+ tok.f.kind = invertToken;
+ replacement = QLatin1String(tok.spell());
+
+ // check for enclosing nested expression
+ if (priority - 1 >= 0)
+ nested = interface.path()[priority - 1]->asNestedExpression();
+
+ // check for ! before parentheses
+ if (nested && priority - 2 >= 0) {
+ negation = interface.path()[priority - 2]->asUnaryExpression();
+ if (negation && !interface.currentFile()->tokenAt(negation->unary_op_token).is(T_EXCLAIM))
+ negation = nullptr;
+ }
+ }
+
+ QString description() const override
+ {
+ return Tr::tr("Rewrite Using %1").arg(replacement);
+ }
+
+ void perform() override
+ {
+ ChangeSet changes;
+ if (negation) {
+ // can't remove parentheses since that might break precedence
+ changes.remove(currentFile()->range(negation->unary_op_token));
+ } else if (nested) {
+ changes.insert(currentFile()->startOf(nested), QLatin1String("!"));
+ } else {
+ changes.insert(currentFile()->startOf(binary), QLatin1String("!("));
+ changes.insert(currentFile()->endOf(binary), QLatin1String(")"));
+ }
+ changes.replace(currentFile()->range(binary->binary_op_token), replacement);
+ currentFile()->apply(changes);
+ }
+
+private:
+ BinaryExpressionAST *binary = nullptr;
+ NestedExpressionAST *nested = nullptr;
+ UnaryExpressionAST *negation = nullptr;
+
+ QString replacement;
+};
+
+class RewriteLogicalAndOp : public CppQuickFixOperation
+{
+public:
+ std::shared_ptr<ASTPatternBuilder> mk;
+ UnaryExpressionAST *left;
+ UnaryExpressionAST *right;
+ BinaryExpressionAST *pattern;
+
+ RewriteLogicalAndOp(const CppQuickFixInterface &interface)
+ : CppQuickFixOperation(interface)
+ , mk(new ASTPatternBuilder)
+ {
+ left = mk->UnaryExpression();
+ right = mk->UnaryExpression();
+ pattern = mk->BinaryExpression(left, right);
+ }
+
+ void perform() override
+ {
+ ChangeSet changes;
+ changes.replace(currentFile()->range(pattern->binary_op_token), QLatin1String("||"));
+ changes.remove(currentFile()->range(left->unary_op_token));
+ changes.remove(currentFile()->range(right->unary_op_token));
+ const int start = currentFile()->startOf(pattern);
+ const int end = currentFile()->endOf(pattern);
+ changes.insert(start, QLatin1String("!("));
+ changes.insert(end, QLatin1String(")"));
+
+ currentFile()->apply(changes);
+ }
+};
+
+/*!
+ Rewrite
+ a op b
+
+ As
+ b flipop a
+
+ Activates on: <= < > >= == != && ||
+*/
+class FlipLogicalOperands : public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const QList<AST *> &path = interface.path();
+ if (path.isEmpty())
+ return;
+ CppRefactoringFilePtr file = interface.currentFile();
+
+ int index = path.size() - 1;
+ BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
+ if (!binary)
+ return;
+ if (!interface.isCursorOn(binary->binary_op_token))
+ return;
+
+ Kind flipToken;
+ switch (file->tokenAt(binary->binary_op_token).kind()) {
+ case T_LESS_EQUAL:
+ flipToken = T_GREATER_EQUAL;
+ break;
+ case T_LESS:
+ flipToken = T_GREATER;
+ break;
+ case T_GREATER:
+ flipToken = T_LESS;
+ break;
+ case T_GREATER_EQUAL:
+ flipToken = T_LESS_EQUAL;
+ break;
+ case T_EQUAL_EQUAL:
+ case T_EXCLAIM_EQUAL:
+ case T_AMPER_AMPER:
+ case T_PIPE_PIPE:
+ flipToken = T_EOF_SYMBOL;
+ break;
+ default:
+ return;
+ }
+
+ QString replacement;
+ if (flipToken != T_EOF_SYMBOL) {
+ Token tok;
+ tok.f.kind = flipToken;
+ replacement = QLatin1String(tok.spell());
+ }
+
+ result << new FlipLogicalOperandsOp(interface, index, binary, replacement);
+ }
+};
+
+/*!
+ Rewrite
+ a op b -> !(a invop b)
+ (a op b) -> !(a invop b)
+ !(a op b) -> (a invob b)
+
+ Activates on: <= < > >= == !=
+*/
+class InverseLogicalComparison : public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest() { return new QObject; }
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ CppRefactoringFilePtr file = interface.currentFile();
+
+ const QList<AST *> &path = interface.path();
+ if (path.isEmpty())
+ return;
+ int index = path.size() - 1;
+ BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
+ if (!binary)
+ return;
+ if (!interface.isCursorOn(binary->binary_op_token))
+ return;
+
+ Kind invertToken;
+ switch (file->tokenAt(binary->binary_op_token).kind()) {
+ case T_LESS_EQUAL:
+ invertToken = T_GREATER;
+ break;
+ case T_LESS:
+ invertToken = T_GREATER_EQUAL;
+ break;
+ case T_GREATER:
+ invertToken = T_LESS_EQUAL;
+ break;
+ case T_GREATER_EQUAL:
+ invertToken = T_LESS;
+ break;
+ case T_EQUAL_EQUAL:
+ invertToken = T_EXCLAIM_EQUAL;
+ break;
+ case T_EXCLAIM_EQUAL:
+ invertToken = T_EQUAL_EQUAL;
+ break;
+ default:
+ return;
+ }
+
+ result << new InverseLogicalComparisonOp(interface, index, binary, invertToken);
+ }
+};
+
+/*!
+ Rewrite
+ !a && !b
+
+ As
+ !(a || b)
+
+ Activates on: &&
+*/
+class RewriteLogicalAnd : public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest() { return new QObject; }
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ BinaryExpressionAST *expression = nullptr;
+ const QList<AST *> &path = interface.path();
+ CppRefactoringFilePtr file = interface.currentFile();
+
+ int index = path.size() - 1;
+ for (; index != -1; --index) {
+ expression = path.at(index)->asBinaryExpression();
+ if (expression)
+ break;
+ }
+
+ if (!expression)
+ return;
+
+ if (!interface.isCursorOn(expression->binary_op_token))
+ return;
+
+ QSharedPointer<RewriteLogicalAndOp> op(new RewriteLogicalAndOp(interface));
+
+ ASTMatcher matcher;
+
+ if (expression->match(op->pattern, &matcher) &&
+ file->tokenAt(op->pattern->binary_op_token).is(T_AMPER_AMPER) &&
+ file->tokenAt(op->left->unary_op_token).is(T_EXCLAIM) &&
+ file->tokenAt(op->right->unary_op_token).is(T_EXCLAIM)) {
+ op->setDescription(Tr::tr("Rewrite Condition Using ||"));
+ op->setPriority(index);
+ result.append(op);
+ }
+ }
+};
+
+#ifdef WITH_TESTS
+using namespace Tests;
+class FlipLogicalOperandsTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data()
+ {
+ QTest::addColumn<QByteArray>("original");
+ QTest::addColumn<QByteArray>("expected");
+
+ const auto makeDoc = [](const QString &expr) {
+ const QString pattern = "#define VALUE 7\n"
+ "int main() {\n"
+ " if (%1)\n"
+ " return 1;\n"
+ "}\n";
+ return pattern.arg(expr).toUtf8();
+ };
+
+ QTest::newRow("macro as left expr")
+ << makeDoc("VALUE @&& true")
+ << makeDoc("true && VALUE");
+ QTest::newRow("macro in left expr")
+ << makeDoc("(VALUE + 1) @&& true")
+ << makeDoc("true && (VALUE + 1)");
+ QTest::newRow("macro as right expr")
+ << makeDoc("false @|| VALUE")
+ << makeDoc("VALUE || false");
+ QTest::newRow("macro in right expr")
+ << makeDoc("false @|| (VALUE + 1)")
+ << makeDoc("(VALUE + 1) || false");
+ }
+
+ void test()
+ {
+ QFETCH(QByteArray, original);
+ QFETCH(QByteArray, expected);
+
+ FlipLogicalOperands factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+};
+
+QObject *FlipLogicalOperands::createTest() { return new FlipLogicalOperandsTest; }
+
+#endif // WITH_TESTS
+
+} // namespace
+
+void registerLogicalOperationQuickfixes()
+{
+ CppQuickFixFactory::registerFactory<FlipLogicalOperands>();
+ CppQuickFixFactory::registerFactory<InverseLogicalComparison>();
+ CppQuickFixFactory::registerFactory<RewriteLogicalAnd>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <logicaloperationquickfixes.moc>
+#endif
diff --git a/src/plugins/cppeditor/quickfixes/logicaloperationquickfixes.h b/src/plugins/cppeditor/quickfixes/logicaloperationquickfixes.h
new file mode 100644
index 0000000000..cf77bed8e0
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/logicaloperationquickfixes.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerLogicalOperationQuickfixes();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/moveclasstoownfile.cpp b/src/plugins/cppeditor/quickfixes/moveclasstoownfile.cpp
new file mode 100644
index 0000000000..e556f80300
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/moveclasstoownfile.cpp
@@ -0,0 +1,721 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "moveclasstoownfile.h"
+
+#include "../cppeditortr.h"
+#include "../cppeditorwidget.h"
+#include "../cppfilesettingspage.h"
+#include "../cpprefactoringchanges.h"
+#include "cppquickfix.h"
+#include "cppquickfixhelpers.h"
+
+#include <coreplugin/messagemanager.h>
+#include <cplusplus/ASTPath.h>
+#include <cplusplus/Overview.h>
+#include <projectexplorer/projectmanager.h>
+#include <projectexplorer/projectnodes.h>
+#include <utils/codegeneration.h>
+#include <utils/layoutbuilder.h>
+#include <utils/treemodel.h>
+#include <utils/treeviewcombobox.h>
+
+#include <QCheckBox>
+#include <QDialog>
+#include <QDialogButtonBox>
+
+#ifdef WITH_TESTS
+#include "../cpptoolstestcase.h"
+#include <coreplugin/editormanager/editormanager.h>
+#include <projectexplorer/kitmanager.h>
+#include <texteditor/textdocument.h>
+#include <QtTest>
+#endif // WITH_TESTS
+
+using namespace CPlusPlus;
+using namespace Core;
+using namespace ProjectExplorer;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+class MoveClassToOwnFileOp : public CppQuickFixOperation
+{
+public:
+ MoveClassToOwnFileOp(
+ const CppQuickFixInterface &interface,
+ AST *fullDecl,
+ ClassSpecifierAST *classAst,
+ const QList<Namespace *> &namespacePath,
+ bool interactive)
+ : CppQuickFixOperation(interface)
+ , m_state(std::make_shared<State>())
+ {
+ setDescription(Tr::tr("Move Class to a Dedicated Set of Source Files"));
+ m_state->originalFilePath = interface.currentFile()->filePath();
+ m_state->classAst = classAst;
+ m_state->namespacePath = namespacePath;
+ m_state->interactive = interactive;
+ PerFileState &perFileState = m_state->perFileState[interface.currentFile()->filePath()];
+ perFileState.refactoringFile = interface.currentFile();
+ perFileState.declarationsToMove << fullDecl;
+ }
+
+private:
+ struct PerFileState {
+ // We want to keep the relative order of moved code.
+ void insertSorted(AST *decl) {
+ declarationsToMove.insert(std::lower_bound(
+ declarationsToMove.begin(),
+ declarationsToMove.end(),
+ decl,
+ [](const AST *elem, const AST *value) {
+ return elem->firstToken() < value->firstToken();
+ }), decl);
+ }
+
+ CppRefactoringFilePtr refactoringFile;
+ QList<AST *> declarationsToMove;
+ };
+ struct State {
+ using Ptr = std::shared_ptr<State>;
+
+ FilePath originalFilePath;
+ AST *fullDecl = nullptr;
+ ClassSpecifierAST *classAst = nullptr;
+ QList<Namespace *> namespacePath;
+ Links lookupResults;
+ QMap<FilePath, PerFileState> perFileState; // A map for deterministic order of moved code.
+ CppRefactoringChanges factory{CppModelManager::snapshot()};
+ int remainingFollowSymbolOps = 0;
+ bool interactive = true;
+ };
+ class Dialog : public QDialog {
+ public:
+ Dialog(const FilePath &defaultHeaderFilePath, const FilePath &defaultSourceFilePath,
+ ProjectNode *defaultProjectNode)
+ : m_buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel)
+ {
+ ProjectNode * const rootNode = defaultProjectNode
+ ? defaultProjectNode->getProject()->rootProjectNode()
+ : nullptr;
+ if (rootNode) {
+ const auto projectRootItem = new NodeItem(rootNode);
+ buildTree(projectRootItem);
+ m_projectModel.rootItem()->appendChild(projectRootItem);
+ }
+ m_projectNodeComboBox.setModel(&m_projectModel);
+ if (defaultProjectNode) {
+ const auto matcher = [defaultProjectNode](TreeItem *item) {
+ return static_cast<NodeItem *>(item)->node == defaultProjectNode;
+ };
+ TreeItem * const defaultItem = m_projectModel.rootItem()->findAnyChild(matcher);
+ if (defaultItem ) {
+ QModelIndex index = m_projectModel.indexForItem(defaultItem);
+ m_projectNodeComboBox.setCurrentIndex(index);
+ while (index.isValid()) {
+ m_projectNodeComboBox.view()->expand(index);
+ index = index.parent();
+ }
+ }
+
+ }
+ connect(&m_projectNodeComboBox, &QComboBox::currentIndexChanged,
+ this, [this] {
+ if (m_filesEdited)
+ return;
+ const auto newProjectNode = projectNode();
+ QTC_ASSERT(newProjectNode, return);
+ const FilePath baseDir = newProjectNode->directory();
+ m_sourcePathChooser.setFilePath(
+ baseDir.pathAppended(sourceFilePath().fileName()));
+ m_headerPathChooser.setFilePath(
+ baseDir.pathAppended(headerFilePath().fileName()));
+ m_filesEdited = false;
+ });
+
+ m_headerOnlyCheckBox.setText(Tr::tr("Header file only"));
+ m_headerOnlyCheckBox.setChecked(false);
+ connect(&m_headerOnlyCheckBox, &QCheckBox::toggled,
+ this, [this](bool checked) { m_sourcePathChooser.setEnabled(!checked); });
+
+ m_headerPathChooser.setExpectedKind(PathChooser::SaveFile);
+ m_sourcePathChooser.setExpectedKind(PathChooser::SaveFile);
+ m_headerPathChooser.setFilePath(defaultHeaderFilePath);
+ m_sourcePathChooser.setFilePath(defaultSourceFilePath);
+ connect(&m_headerPathChooser, &PathChooser::textChanged,
+ this, [this] { m_filesEdited = true; });
+ connect(&m_sourcePathChooser, &PathChooser::textChanged,
+ this, [this] { m_filesEdited = true; });
+
+ connect(&m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(&m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+
+ using namespace Layouting;
+ Column {
+ Form {
+ Tr::tr("Project:"), &m_projectNodeComboBox, br,
+ &m_headerOnlyCheckBox, br,
+ Tr::tr("Header file:"), &m_headerPathChooser, br,
+ Tr::tr("Implementation file:"), &m_sourcePathChooser, br,
+ },
+ &m_buttonBox
+ }.attachTo(this);
+ }
+
+ ProjectNode *projectNode() const
+ {
+ const QVariant v = m_projectNodeComboBox.currentData(Qt::UserRole);
+ return v.isNull() ? nullptr : static_cast<ProjectNode *>(v.value<void *>());
+ }
+ bool createSourceFile() const { return !m_headerOnlyCheckBox.isChecked(); }
+ FilePath headerFilePath() const { return m_headerPathChooser.absoluteFilePath(); }
+ FilePath sourceFilePath() const { return m_sourcePathChooser.absoluteFilePath(); }
+
+ private:
+ struct NodeItem : public StaticTreeItem {
+ NodeItem(ProjectNode *node)
+ : StaticTreeItem({node->displayName()}, {node->directory().toUserOutput()})
+ , node(node)
+ {}
+ Qt::ItemFlags flags(int) const override
+ {
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+ }
+ QVariant data(int column, int role) const override
+ {
+ if (role == Qt::UserRole)
+ return QVariant::fromValue(static_cast<void *>(node));
+ return StaticTreeItem::data(column, role);
+ }
+
+ ProjectNode * const node;
+ };
+
+ void buildTree(NodeItem *parent)
+ {
+ for (Node * const node : parent->node->nodes()) {
+ if (const auto projNode = node->asProjectNode()) {
+ const auto child = new NodeItem(projNode);
+ buildTree(child);
+ parent->appendChild(child);
+ }
+ }
+ }
+
+ TreeViewComboBox m_projectNodeComboBox;
+ QCheckBox m_headerOnlyCheckBox;
+ PathChooser m_headerPathChooser;
+ PathChooser m_sourcePathChooser;
+ QDialogButtonBox m_buttonBox;
+ TreeModel<> m_projectModel;
+ bool m_filesEdited = false;
+ };
+
+ void perform() override
+ {
+ collectImplementations(m_state->classAst->symbol, m_state);
+ if (m_state->remainingFollowSymbolOps == 0)
+ finish(m_state);
+ }
+
+ static CppRefactoringFilePtr getRefactoringFile(const FilePath &filePath, const State::Ptr &state)
+ {
+ CppRefactoringFilePtr &refactoringFile = state->perFileState[filePath].refactoringFile;
+ if (!refactoringFile)
+ refactoringFile = state->factory.cppFile(filePath);
+ return refactoringFile;
+ }
+
+ static void lookupSymbol(Symbol *symbol, const State::Ptr &state)
+ {
+ const CppRefactoringFilePtr refactoringFile = getRefactoringFile(symbol->filePath(), state);
+ const auto editorWidget = qobject_cast<CppEditorWidget *>(refactoringFile->editor());
+ QTextCursor cursor(refactoringFile->document()->begin());
+ TranslationUnit * const tu = refactoringFile->cppDocument()->translationUnit();
+ const int symbolPos = tu->getTokenPositionInDocument(symbol->sourceLocation(),
+ refactoringFile->document());
+ cursor.setPosition(symbolPos);
+ const CursorInEditor cursorInEditor(
+ cursor,
+ symbol->filePath(),
+ editorWidget,
+ editorWidget ? editorWidget->textDocument() : nullptr,
+ refactoringFile->cppDocument());
+ const auto callback = [symbol, symbolPos, doc = cursor.document(), state](const Link &link) {
+ class FinishedChecker {
+ public:
+ FinishedChecker(const State::Ptr &state) : m_state(state) {}
+ ~FinishedChecker() {
+ if (--m_state->remainingFollowSymbolOps == 0)
+ finish(m_state);
+ };
+ private:
+ const State::Ptr &m_state;
+ } finishedChecker(state);
+ if (!link.hasValidTarget())
+ return;
+ if (symbol->filePath() == link.targetFilePath) {
+ const int linkPos = Text::positionInText(doc, link.targetLine,
+ link.targetColumn + 1);
+ if (linkPos == symbolPos)
+ return;
+ }
+ const CppRefactoringFilePtr refactoringFile
+ = getRefactoringFile(link.targetFilePath, state);
+ const QList<AST *> astPath = ASTPath(
+ refactoringFile->cppDocument())(link.targetLine, link.targetColumn);
+ const bool isTemplate = symbol->asTemplate();
+ const bool isFunction = symbol->type()->asFunctionType();
+ for (auto it = astPath.rbegin(); it != astPath.rend(); ++it) {
+ const bool match = isTemplate ? bool((*it)->asTemplateDeclaration())
+ : isFunction ? bool((*it)->asFunctionDefinition())
+ : bool((*it)->asSimpleDeclaration());
+ if (match) {
+ // For member functions of class templates.
+ if (isFunction) {
+ const auto next = std::next(it);
+ if (next != astPath.rend() && (*next)->asTemplateDeclaration())
+ it = next;
+ }
+ state->perFileState[link.targetFilePath].insertSorted(*it);
+ if (symbol->asForwardClassDeclaration()) {
+ if (const auto classSpec = (*(it - 1))->asClassSpecifier();
+ classSpec && classSpec->symbol) {
+ collectImplementations(classSpec->symbol, state);
+ }
+ }
+ break;
+ }
+ }
+ };
+ ++state->remainingFollowSymbolOps;
+
+ // Force queued execution, as the built-in editor can run the callback synchronously.
+ const auto followSymbol = [cursorInEditor, callback] {
+ CppModelManager::followSymbol(
+ cursorInEditor, callback, true, false, FollowSymbolMode::Exact);
+ };
+ QMetaObject::invokeMethod(CppModelManager::instance(), followSymbol, Qt::QueuedConnection);
+ }
+
+ static void collectImplementations(Class *klass, const State::Ptr &state)
+ {
+ for (int i = 0; i < klass->memberCount(); ++i) {
+ Symbol * const member = klass->memberAt(i);
+ if (member->asForwardClassDeclaration() || member->asTemplate()) {
+ lookupSymbol(member, state);
+ continue;
+ }
+ const auto decl = member->asDeclaration();
+ if (!decl)
+ continue;
+ if (decl->type().type()->asFunctionType()) {
+ if (!decl->asFunction())
+ lookupSymbol(member, state);
+ } else if (decl->isStatic() && !decl->type().isInline()) {
+ lookupSymbol(member, state);
+ }
+ }
+ }
+
+ static void finish(const State::Ptr &state)
+ {
+ Overview ov;
+ Project * const project = ProjectManager::projectForFile(state->originalFilePath);
+ const CppFileSettings fileSettings = cppFileSettingsForProject(project);
+ const auto constructDefaultFilePaths = [&] {
+ const QString className = ov.prettyName(state->classAst->symbol->name());
+ const QString baseFileName = fileSettings.lowerCaseFiles ? className.toLower() : className;
+ const QString headerFileName = baseFileName + '.' + fileSettings.headerSuffix;
+ const FilePath baseDir = state->originalFilePath.parentDir();
+ const FilePath headerFilePath = baseDir.pathAppended(headerFileName);
+ const QString sourceFileName = baseFileName + '.' + fileSettings.sourceSuffix;
+ const FilePath sourceFilePath = baseDir.pathAppended(sourceFileName);
+ return std::make_pair(headerFilePath, sourceFilePath);
+ };
+ auto [headerFilePath, sourceFilePath] = constructDefaultFilePaths();
+ bool mustCreateSourceFile = false;
+ bool mustNotCreateSourceFile = false;
+ ProjectNode *projectNode = nullptr;
+ if (project && project->rootProjectNode()) {
+ const Node * const origNode = project->nodeForFilePath(state->originalFilePath);
+ if (origNode)
+ projectNode = const_cast<Node *>(origNode)->managingProject();
+ }
+ if (state->interactive) {
+ Dialog dlg(headerFilePath, sourceFilePath, projectNode);
+ if (dlg.exec() != QDialog::Accepted)
+ return;
+ projectNode = dlg.projectNode();
+ headerFilePath = dlg.headerFilePath();
+ sourceFilePath = dlg.sourceFilePath();
+ mustCreateSourceFile = dlg.createSourceFile();
+ mustNotCreateSourceFile = !dlg.createSourceFile();
+ }
+ const auto fileListForDisplay = [](const FilePaths &files) {
+ return Utils::transform<QStringList>(files, [](const FilePath &fp) {
+ return '"' + fp.toUserOutput() + '"';
+ }).join(", ");
+ };
+ FilePaths existingFiles;
+ if (headerFilePath.exists())
+ existingFiles << headerFilePath;
+ if (!mustNotCreateSourceFile && sourceFilePath.exists())
+ existingFiles << sourceFilePath;
+ if (!existingFiles.isEmpty()) {
+ MessageManager::writeDisrupting(
+ Tr::tr("Refusing to overwrite the following files: %1\n")
+ .arg(fileListForDisplay(existingFiles)));
+ return;
+ }
+ const QString headerFileName = headerFilePath.fileName();
+
+ QString headerContent;
+ QString sourceContent;
+ QList<QString *> commonContent{&headerContent};
+ if (!mustNotCreateSourceFile)
+ commonContent << &sourceContent;
+ for (QString *const content : std::as_const(commonContent)) {
+ content->append(fileSettings.licenseTemplate());
+ if (!content->isEmpty())
+ content->append('\n');
+ }
+ sourceContent.append('\n').append("#include \"").append(headerFileName).append("\"\n");
+ const QStringList namespaceNames
+ = Utils::transform<QStringList>(state->namespacePath, [&](const Namespace *ns) {
+ return ov.prettyName(ns->name());
+ });
+ const QString headerGuard = Utils::headerGuard(headerFileName, namespaceNames);
+ if (fileSettings.headerPragmaOnce) {
+ headerContent.append("#pragma once\n");
+ } else {
+ headerContent.append("#ifndef " + headerGuard + "\n");
+ headerContent.append("#define " + headerGuard + "\n");
+ }
+ if (!namespaceNames.isEmpty()) {
+ for (QString *const content : std::as_const(commonContent)) {
+ content->append('\n');
+ for (const QString &ns : namespaceNames)
+ content->append("namespace " + ns + " {\n");
+ }
+ }
+ bool hasSourceContent = false;
+ for (auto it = state->perFileState.begin(); it != state->perFileState.end(); ++it) {
+ if (it->declarationsToMove.isEmpty())
+ continue;
+ const CppRefactoringFilePtr refactoringFile = it->refactoringFile;
+ QTC_ASSERT(refactoringFile, continue);
+ const bool isDeclFile = refactoringFile->filePath() == state->originalFilePath;
+ ChangeSet changes;
+ if (isDeclFile) {
+ QString relInclude = headerFilePath.relativePathFrom(
+ refactoringFile->filePath().parentDir()).toString();
+ if (!relInclude.isEmpty())
+ relInclude.append('/');
+ relInclude.append('"').append(headerFileName).append('"');
+ insertNewIncludeDirective(relInclude, refactoringFile,
+ refactoringFile->cppDocument(), changes);
+ }
+ for (AST * const declToMove : std::as_const(it->declarationsToMove)) {
+ const ChangeSet::Range rangeToMove = refactoringFile->range(declToMove);
+ QString &content = isDeclFile || mustNotCreateSourceFile ? headerContent
+ : sourceContent;
+ if (&content == &sourceContent)
+ hasSourceContent = true;
+ content.append('\n')
+ .append(refactoringFile->textOf(rangeToMove))
+ .append('\n');
+ changes.remove(rangeToMove);
+ }
+ refactoringFile->apply(changes);
+ }
+
+ if (!namespaceNames.isEmpty()) {
+ for (QString *const content : std::as_const(commonContent)) {
+ content->append('\n');
+ for (auto it = namespaceNames.rbegin(); it != namespaceNames.rend(); ++it)
+ content->append("} // namespace " + *it + '\n');
+ }
+ }
+ if (!fileSettings.headerPragmaOnce)
+ headerContent.append("\n#endif // " + headerGuard + '\n');
+
+ headerFilePath.ensureExistingFile();
+ state->factory.cppFile(headerFilePath)->apply(ChangeSet::makeInsert(0, headerContent));
+ if (hasSourceContent || mustCreateSourceFile) {
+ sourceFilePath.ensureExistingFile();
+ state->factory.cppFile(sourceFilePath)->apply(ChangeSet::makeInsert(0, sourceContent));
+ }
+
+ if (!projectNode)
+ return;
+ FilePaths toAdd{headerFilePath};
+ if (hasSourceContent)
+ toAdd << sourceFilePath;
+ FilePaths notAdded;
+ projectNode->addFiles(toAdd, &notAdded);
+ if (!notAdded.isEmpty()) {
+ MessageManager::writeDisrupting(
+ Tr::tr("Failed to add to project file \"%1\": %2")
+ .arg(projectNode->filePath().toUserOutput(), fileListForDisplay(notAdded)));
+ }
+
+ if (state->interactive)
+ EditorManager::openEditor(headerFilePath);
+ }
+
+ const State::Ptr m_state;
+};
+
+//! Move a class into a dedicates set of files.
+class MoveClassToOwnFile : public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ void setNonInteractive() { m_interactive = false; }
+ static QObject *createTest();
+#endif
+
+private:
+ // Applies if and only if:
+ // - Class is not a nested class.
+ // - Class name does not match file name via any of the usual transformations.
+ // - There are other declarations in the same file.
+ void doMatch(const CppQuickFixInterface &interface,
+ TextEditor::QuickFixOperations &result) override
+ {
+ ClassSpecifierAST * const classAst = astForClassOperations(interface);
+ if (!classAst || !classAst->symbol)
+ return;
+ AST *fullDecl = nullptr;
+ for (auto it = interface.path().rbegin(); it != interface.path().rend() && !fullDecl; ++it) {
+ if (*it == classAst && it != interface.path().rend() - 1) {
+ auto next = std::next(it);
+ fullDecl = (*next)->asSimpleDeclaration();
+ if (next != interface.path().rend() - 1) {
+ next = std::next(next);
+ if (const auto templ = (*next)->asTemplateDeclaration())
+ fullDecl = templ;
+ }
+ }
+ }
+ if (!fullDecl)
+ return;
+
+ // Check file name.
+ const QString className = Overview().prettyName(classAst->symbol->name());
+ if (className.isEmpty())
+ return;
+ const QString lowerFileBaseName = interface.filePath().baseName().toLower();
+ if (lowerFileBaseName.contains(className.toLower()))
+ return;
+ QString underscoredClassName = className;
+ QChar curChar = underscoredClassName.at(0);
+ for (int i = 1; i < underscoredClassName.size(); ++i) {
+ const QChar prevChar = curChar;
+ curChar = underscoredClassName.at(i);
+ if (curChar.isUpper() && prevChar.isLetterOrNumber() && !prevChar.isUpper()) {
+ underscoredClassName.insert(i, '_');
+ ++i;
+ }
+ }
+ if (lowerFileBaseName.contains(underscoredClassName.toLower()))
+ return;
+
+ // Is there more than one class definition in the file?
+ AST * const ast = interface.currentFile()->cppDocument()->translationUnit()->ast();
+ if (!ast)
+ return;
+ DeclarationListAST * const topLevelDecls = ast->asTranslationUnit()->declaration_list;
+ if (!topLevelDecls)
+ return;
+ QList<Namespace *> namespacePath;
+ QList<Namespace *> currentNamespacePath;
+ bool foundOtherDecls = false;
+ bool foundSelf = false;
+ std::function<void(Namespace *)> collectSymbolsFromNamespace;
+ const auto handleSymbol = [&](Symbol *symbol) {
+ if (!symbol)
+ return;
+ if (const auto nsMember = symbol->asNamespace()) {
+ collectSymbolsFromNamespace(nsMember);
+ return;
+ }
+ if (symbol != classAst->symbol) {
+ if (!symbol->asForwardClassDeclaration())
+ foundOtherDecls = true;
+ return;
+ }
+ QTC_ASSERT(symbol->asClass(), return);
+ foundSelf = true;
+ namespacePath = currentNamespacePath;
+ };
+ collectSymbolsFromNamespace = [&](Namespace *ns) {
+ currentNamespacePath << ns;
+ for (int i = 0; i < ns->memberCount() && (!foundSelf || !foundOtherDecls); ++i)
+ handleSymbol(ns->memberAt(i));
+ currentNamespacePath.removeLast();
+ };
+ for (DeclarationListAST *it = topLevelDecls; it && (!foundSelf || !foundOtherDecls);
+ it = it->next) {
+ DeclarationAST *decl = it->value;
+ if (!decl)
+ continue;
+ if (const auto templ = decl->asTemplateDeclaration())
+ decl = templ->declaration;
+ if (!decl)
+ continue;
+ if (const auto ns = decl->asNamespace(); ns && ns->symbol) {
+ collectSymbolsFromNamespace(ns->symbol);
+ continue;
+ }
+ if (const auto simpleDecl = decl->asSimpleDeclaration()) {
+ if (!simpleDecl->decl_specifier_list)
+ continue;
+ for (SpecifierListAST *spec = simpleDecl->decl_specifier_list; spec; spec = spec->next) {
+ if (!spec->value)
+ continue;
+ if (const auto klass = spec->value->asClassSpecifier())
+ handleSymbol(klass->symbol);
+ else if (!spec->value->asElaboratedTypeSpecifier()) // forward decl
+ foundOtherDecls = true;
+ }
+ } else if (decl->asDeclaration()) {
+ foundOtherDecls = true;
+ }
+ }
+
+ if (foundSelf && foundOtherDecls) {
+ result << new MoveClassToOwnFileOp(
+ interface, fullDecl, classAst, namespacePath, m_interactive);
+ }
+ }
+
+ bool m_interactive = true;
+};
+
+#ifdef WITH_TESTS
+class MoveClassToOwnFileTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data()
+ {
+ QTest::addColumn<QString>("projectName");
+ QTest::addColumn<QString>("fileName");
+ QTest::addColumn<QString>("className");
+ QTest::addColumn<bool>("applicable");
+
+ QTest::newRow("nested") << "nested" << "main.cpp" << "Inner" << false;
+ QTest::newRow("file name match 1") << "match1" << "TheClass.h" << "TheClass" << false;
+ QTest::newRow("file name match 2") << "match2" << "theclass.h" << "TheClass" << false;
+ QTest::newRow("file name match 3") << "match3" << "the_class.h" << "TheClass" << false;
+ QTest::newRow("single") << "single" << "theheader.h" << "TheClass" << false;
+ QTest::newRow("complex") << "complex" << "theheader.h" << "TheClass" << true;
+ QTest::newRow("header only") << "header-only" << "theheader.h" << "TheClass" << true;
+ QTest::newRow("decl in source file") << "decl-in-source" << "thesource.cpp" << "TheClass" << true;
+ QTest::newRow("template") << "template" << "theheader.h" << "TheClass" << true;
+ }
+
+ void test()
+ {
+ QFETCH(QString, projectName);
+ QFETCH(QString, fileName);
+ QFETCH(QString, className);
+ QFETCH(bool, applicable);
+ using namespace CppEditor::Tests;
+ using namespace TextEditor;
+
+ // Set up project.
+ Kit * const kit = Utils::findOr(KitManager::kits(), nullptr, [](const Kit *k) {
+ return k->isValid() && !k->hasWarning() && k->value("QtSupport.QtInformation").isValid();
+ });
+ if (!kit)
+ QSKIP("The test requires at least one valid kit with a valid Qt");
+ const auto projectDir = std::make_unique<TemporaryCopiedDir>(
+ ":/cppeditor/testcases/move-class/" + projectName);
+ SourceFilesRefreshGuard refreshGuard;
+ ProjectOpenerAndCloser projectMgr;
+ QVERIFY(projectMgr.open(projectDir->absolutePath(projectName + ".pro"), true, kit));
+ QVERIFY(refreshGuard.wait());
+
+ // Open header file and locate class.
+ const auto headerFilePath = projectDir->absolutePath(fileName);
+ QVERIFY2(headerFilePath.exists(), qPrintable(headerFilePath.toUserOutput()));
+ const auto editor = qobject_cast<BaseTextEditor *>(EditorManager::openEditor(headerFilePath));
+ QVERIFY(editor);
+ const auto doc = qobject_cast<TextEditor::TextDocument *>(editor->document());
+ QVERIFY(doc);
+ QTextCursor classCursor = doc->document()->find("class " + className);
+ QVERIFY(!classCursor.isNull());
+ editor->setCursorPosition(classCursor.position());
+ const auto editorWidget = qobject_cast<CppEditorWidget *>(editor->editorWidget());
+ QVERIFY(editorWidget);
+ QVERIFY(TestCase::waitForRehighlightedSemanticDocument(editorWidget));
+
+ // Query factory.
+ MoveClassToOwnFile factory;
+ factory.setNonInteractive();
+ CppQuickFixInterface quickFixInterface(editorWidget, ExplicitlyInvoked);
+ QuickFixOperations operations;
+ factory.match(quickFixInterface, operations);
+ QCOMPARE(operations.isEmpty(), !applicable);
+ if (!applicable)
+ return;
+ operations.first()->perform();
+ QVERIFY(waitForSignalOrTimeout(doc, &IDocument::saved, 30000));
+ QTest::qWait(1000);
+
+ // Compare all files.
+ const FileFilter filter({"*_expected"}, QDir::Files);
+ const FilePaths expectedDocuments = projectDir->filePath().dirEntries(filter);
+ QVERIFY(!expectedDocuments.isEmpty());
+ for (const FilePath &expected : expectedDocuments) {
+ static const QString suffix = "_expected";
+ const FilePath actual = expected.parentDir()
+ .pathAppended(expected.fileName().chopped(suffix.length()));
+ QVERIFY(actual.exists());
+ const auto actualContents = actual.fileContents();
+ QVERIFY(actualContents);
+ const auto expectedContents = expected.fileContents();
+ const QByteArrayList actualLines = actualContents->split('\n');
+ const QByteArrayList expectedLines = expectedContents->split('\n');
+ if (actualLines.size() != expectedLines.size()) {
+ qDebug().noquote().nospace() << "---\n" << *expectedContents << "EOF";
+ qDebug().noquote().nospace() << "+++\n" << *actualContents << "EOF";
+ }
+ QCOMPARE(actualLines.size(), expectedLines.size());
+ for (int i = 0; i < actualLines.size(); ++i) {
+ const QByteArray actualLine = actualLines.at(i);
+ const QByteArray expectedLine = expectedLines.at(i);
+ if (actualLine != expectedLine)
+ qDebug() << "Unexpected content in line" << (i + 1) << "of file"
+ << actual.fileName();
+ QCOMPARE(actualLine, expectedLine);
+ }
+ }
+ }
+};
+
+QObject *MoveClassToOwnFile::createTest()
+{
+ return new MoveClassToOwnFileTest;
+}
+
+#endif // WITH_TESTS
+
+} // namespace
+
+void registerMoveClassToOwnFileQuickfix()
+{
+ CppQuickFixFactory::registerFactory<MoveClassToOwnFile>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <moveclasstoownfile.moc>
+#endif
diff --git a/src/plugins/cppeditor/quickfixes/moveclasstoownfile.h b/src/plugins/cppeditor/quickfixes/moveclasstoownfile.h
new file mode 100644
index 0000000000..c46b0e5c3a
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/moveclasstoownfile.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerMoveClassToOwnFileQuickfix();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/movefunctiondefinition.cpp b/src/plugins/cppeditor/quickfixes/movefunctiondefinition.cpp
new file mode 100644
index 0000000000..f6223629ae
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/movefunctiondefinition.cpp
@@ -0,0 +1,1893 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "movefunctiondefinition.h"
+
+#include "../cppcodestylesettings.h"
+#include "../cppeditortr.h"
+#include "../cpprefactoringchanges.h"
+#include "../insertionpointlocator.h"
+#include "../symbolfinder.h"
+#include "cppquickfix.h"
+#include "cppquickfixhelpers.h"
+
+#include <cplusplus/ASTPath.h>
+#include <cplusplus/CppRewriter.h>
+#include <cplusplus/Overview.h>
+
+using namespace CPlusPlus;
+using namespace TextEditor;
+using namespace Utils;
+
+#ifdef WITH_TESTS
+#include "cppquickfix_test.h"
+#include <QtTest>
+#endif
+
+namespace CppEditor::Internal {
+namespace {
+
+static QString definitionSignature(
+ const CppQuickFixInterface *assist,
+ FunctionDefinitionAST *functionDefinitionAST,
+ CppRefactoringFilePtr &baseFile,
+ CppRefactoringFilePtr &targetFile,
+ Scope *scope)
+{
+ QTC_ASSERT(assist, return QString());
+ QTC_ASSERT(functionDefinitionAST, return QString());
+ QTC_ASSERT(scope, return QString());
+ Function *func = functionDefinitionAST->symbol;
+ QTC_ASSERT(func, return QString());
+
+ LookupContext cppContext(targetFile->cppDocument(), assist->snapshot());
+ ClassOrNamespace *cppCoN = cppContext.lookupType(scope);
+ if (!cppCoN)
+ cppCoN = cppContext.globalNamespace();
+ SubstitutionEnvironment env;
+ env.setContext(assist->context());
+ env.switchScope(func->enclosingScope());
+ UseMinimalNames q(cppCoN);
+ env.enter(&q);
+ Control *control = assist->context().bindings()->control().get();
+ Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ oo.showFunctionSignatures = true;
+ oo.showReturnTypes = true;
+ oo.showArgumentNames = true;
+ oo.showEnclosingTemplate = true;
+ oo.showTemplateParameters = true;
+ oo.trailingReturnType = functionDefinitionAST->declarator
+ && functionDefinitionAST->declarator->postfix_declarator_list
+ && functionDefinitionAST->declarator->postfix_declarator_list->value
+ && functionDefinitionAST->declarator->postfix_declarator_list
+ ->value->asFunctionDeclarator()
+ && functionDefinitionAST->declarator->postfix_declarator_list
+ ->value->asFunctionDeclarator()->trailing_return_type;
+ const Name *name = func->name();
+ if (name && nameIncludesOperatorName(name)) {
+ CoreDeclaratorAST *coreDeclarator = functionDefinitionAST->declarator->core_declarator;
+ const QString operatorNameText = baseFile->textOf(coreDeclarator);
+ oo.includeWhiteSpaceInOperatorName = operatorNameText.contains(QLatin1Char(' '));
+ }
+ const QString nameText = oo.prettyName(LookupContext::minimalName(func, cppCoN, control));
+ oo.showTemplateParameters = false;
+ const FullySpecifiedType tn = rewriteType(func->type(), &env, control);
+
+ return oo.prettyType(tn, nameText);
+}
+
+class MoveFuncDefRefactoringHelper
+{
+public:
+ enum MoveType {
+ MoveOutside,
+ MoveToCppFile,
+ MoveOutsideMemberToCppFile
+ };
+
+ MoveFuncDefRefactoringHelper(CppQuickFixOperation *operation, MoveType type,
+ const FilePath &fromFile, const FilePath &toFile)
+ : m_operation(operation), m_type(type), m_changes(m_operation->snapshot())
+ {
+ m_fromFile = m_changes.cppFile(fromFile);
+ m_toFile = (m_type == MoveOutside) ? m_fromFile : m_changes.cppFile(toFile);
+ }
+
+ void performMove(FunctionDefinitionAST *funcAST)
+ {
+ // Determine file, insert position and scope
+ InsertionLocation l = insertLocationForMethodDefinition(
+ funcAST->symbol, false, NamespaceHandling::Ignore,
+ m_changes, m_toFile->filePath());
+ const QString prefix = l.prefix();
+ const QString suffix = l.suffix();
+ const int insertPos = m_toFile->position(l.line(), l.column());
+ Scope *scopeAtInsertPos = m_toFile->cppDocument()->scopeAt(l.line(), l.column());
+
+ // construct definition
+ const QString funcDec = inlinePrefix(m_toFile->filePath(), [this] { return m_type == MoveOutside; })
+ + definitionSignature(m_operation, funcAST, m_fromFile, m_toFile,
+ scopeAtInsertPos);
+ QString funcDef = prefix + funcDec;
+ const int startPosition = m_fromFile->endOf(funcAST->declarator);
+ const int endPosition = m_fromFile->endOf(funcAST);
+ funcDef += m_fromFile->textOf(startPosition, endPosition);
+ funcDef += suffix;
+
+ // insert definition at new position
+ m_toFileChangeSet.insert(insertPos, funcDef);
+ m_toFile->setOpenEditor(true, insertPos);
+
+ // remove definition from fromFile
+ if (m_type == MoveOutsideMemberToCppFile) {
+ m_fromFileChangeSet.remove(m_fromFile->range(funcAST));
+ } else {
+ QString textFuncDecl = m_fromFile->textOf(funcAST);
+ textFuncDecl.truncate(startPosition - m_fromFile->startOf(funcAST));
+ if (textFuncDecl.left(7) == QLatin1String("inline "))
+ textFuncDecl = textFuncDecl.mid(7);
+ else
+ textFuncDecl.replace(" inline ", QLatin1String(" "));
+ textFuncDecl = textFuncDecl.trimmed() + QLatin1Char(';');
+ m_fromFileChangeSet.replace(m_fromFile->range(funcAST), textFuncDecl);
+ }
+ }
+
+ void applyChanges()
+ {
+ m_toFile->apply(m_toFileChangeSet);
+ m_fromFile->apply(m_fromFileChangeSet);
+ }
+
+private:
+ CppQuickFixOperation *m_operation;
+ MoveType m_type;
+ CppRefactoringChanges m_changes;
+ CppRefactoringFilePtr m_fromFile;
+ CppRefactoringFilePtr m_toFile;
+ ChangeSet m_fromFileChangeSet;
+ ChangeSet m_toFileChangeSet;
+};
+
+class MoveFuncDefOutsideOp : public CppQuickFixOperation
+{
+public:
+ MoveFuncDefOutsideOp(const CppQuickFixInterface &interface,
+ MoveFuncDefRefactoringHelper::MoveType type,
+ FunctionDefinitionAST *funcDef, const FilePath &cppFilePath)
+ : CppQuickFixOperation(interface, 0)
+ , m_funcDef(funcDef)
+ , m_type(type)
+ , m_cppFilePath(cppFilePath)
+ , m_headerFilePath(funcDef->symbol->filePath())
+ {
+ if (m_type == MoveFuncDefRefactoringHelper::MoveOutside) {
+ setDescription(Tr::tr("Move Definition Outside Class"));
+ } else {
+ const FilePath resolved = m_cppFilePath.relativePathFrom(m_headerFilePath.parentDir());
+ setDescription(Tr::tr("Move Definition to %1").arg(resolved.displayName()));
+ }
+ }
+
+ void perform() override
+ {
+ MoveFuncDefRefactoringHelper helper(this, m_type, m_headerFilePath, m_cppFilePath);
+ helper.performMove(m_funcDef);
+ helper.applyChanges();
+ }
+
+private:
+ FunctionDefinitionAST *m_funcDef;
+ MoveFuncDefRefactoringHelper::MoveType m_type;
+ const FilePath m_cppFilePath;
+ const FilePath m_headerFilePath;
+};
+
+class MoveAllFuncDefOutsideOp : public CppQuickFixOperation
+{
+public:
+ MoveAllFuncDefOutsideOp(const CppQuickFixInterface &interface,
+ MoveFuncDefRefactoringHelper::MoveType type,
+ ClassSpecifierAST *classDef, const FilePath &cppFileName)
+ : CppQuickFixOperation(interface, 0)
+ , m_type(type)
+ , m_classDef(classDef)
+ , m_cppFilePath(cppFileName)
+ , m_headerFilePath(classDef->symbol->filePath())
+ {
+ if (m_type == MoveFuncDefRefactoringHelper::MoveOutside) {
+ setDescription(Tr::tr("Definitions Outside Class"));
+ } else {
+ const FilePath resolved = m_cppFilePath.relativePathFrom(m_headerFilePath.parentDir());
+ setDescription(Tr::tr("Move All Function Definitions to %1")
+ .arg(resolved.displayName()));
+ }
+ }
+
+ void perform() override
+ {
+ MoveFuncDefRefactoringHelper helper(this, m_type, m_headerFilePath, m_cppFilePath);
+ for (DeclarationListAST *it = m_classDef->member_specifier_list; it; it = it->next) {
+ if (FunctionDefinitionAST *funcAST = it->value->asFunctionDefinition()) {
+ if (funcAST->symbol && !funcAST->symbol->isGenerated())
+ helper.performMove(funcAST);
+ }
+ }
+ helper.applyChanges();
+ }
+
+private:
+ MoveFuncDefRefactoringHelper::MoveType m_type;
+ ClassSpecifierAST *m_classDef;
+ const FilePath m_cppFilePath;
+ const FilePath m_headerFilePath;
+};
+
+class MoveFuncDefToDeclOp : public CppQuickFixOperation
+{
+public:
+ enum Type { Push, Pull };
+ MoveFuncDefToDeclOp(const CppQuickFixInterface &interface,
+ const FilePath &fromFilePath, const FilePath &toFilePath,
+ FunctionDefinitionAST *funcAst, Function *func, const QString &declText,
+ const ChangeSet::Range &fromRange,
+ const ChangeSet::Range &toRange,
+ Type type)
+ : CppQuickFixOperation(interface, 0)
+ , m_fromFilePath(fromFilePath)
+ , m_toFilePath(toFilePath)
+ , m_funcAST(funcAst)
+ , m_func(func)
+ , m_declarationText(declText)
+ , m_fromRange(fromRange)
+ , m_toRange(toRange)
+ {
+ if (type == Type::Pull) {
+ setDescription(Tr::tr("Move Definition Here"));
+ } else if (m_toFilePath == m_fromFilePath) {
+ setDescription(Tr::tr("Move Definition to Class"));
+ } else {
+ const FilePath resolved = m_toFilePath.relativePathFrom(m_fromFilePath.parentDir());
+ setDescription(Tr::tr("Move Definition to %1").arg(resolved.displayName()));
+ }
+ }
+
+private:
+ void perform() override
+ {
+ CppRefactoringChanges refactoring(snapshot());
+ CppRefactoringFilePtr fromFile = refactoring.cppFile(m_fromFilePath);
+ CppRefactoringFilePtr toFile = refactoring.cppFile(m_toFilePath);
+
+ ensureFuncDefAstAndRange(*fromFile);
+ if (!m_funcAST)
+ return;
+
+ const QString wholeFunctionText = m_declarationText
+ + fromFile->textOf(fromFile->endOf(m_funcAST->declarator),
+ fromFile->endOf(m_funcAST->function_body));
+
+ // Replace declaration with function and delete old definition
+ ChangeSet toTarget;
+ toTarget.replace(m_toRange, wholeFunctionText);
+ if (m_toFilePath == m_fromFilePath)
+ toTarget.remove(m_fromRange);
+ toFile->setOpenEditor(true, m_toRange.start);
+ toFile->apply(toTarget);
+ if (m_toFilePath != m_fromFilePath)
+ fromFile->apply(ChangeSet::makeRemove(m_fromRange));
+ }
+
+ void ensureFuncDefAstAndRange(CppRefactoringFile &defFile)
+ {
+ if (m_funcAST) {
+ QTC_CHECK(m_fromRange.end > m_fromRange.start);
+ return;
+ }
+ QTC_ASSERT(m_func, return);
+ const QList<AST *> astPath = ASTPath(defFile.cppDocument())(m_func->line(),
+ m_func->column());
+ if (astPath.isEmpty())
+ return;
+ for (auto it = std::rbegin(astPath); it != std::rend(astPath); ++it) {
+ m_funcAST = (*it)->asFunctionDefinition();
+ if (!m_funcAST)
+ continue;
+ AST *astForRange = m_funcAST;
+ const auto prev = std::next(it);
+ if (prev != std::rend(astPath)) {
+ if (const auto templAst = (*prev)->asTemplateDeclaration())
+ astForRange = templAst;
+ }
+ m_fromRange = defFile.range(astForRange);
+ return;
+ }
+ }
+
+ const FilePath m_fromFilePath;
+ const FilePath m_toFilePath;
+ FunctionDefinitionAST *m_funcAST;
+ Function *m_func;
+ const QString m_declarationText;
+ ChangeSet::Range m_fromRange;
+ const ChangeSet::Range m_toRange;
+};
+
+/*!
+ Moves the definition of a member function outside the class or moves the definition of a member
+ function or a normal function to the implementation file.
+ */
+class MoveFuncDefOutside : public CppQuickFixFactory
+{
+public:
+#ifdef WITH_TESTS
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const QList<AST *> &path = interface.path();
+ SimpleDeclarationAST *classAST = nullptr;
+ FunctionDefinitionAST *funcAST = nullptr;
+ bool moveOutsideMemberDefinition = false;
+
+ const int pathSize = path.size();
+ for (int idx = 1; idx < pathSize; ++idx) {
+ if ((funcAST = path.at(idx)->asFunctionDefinition())) {
+ // check cursor position
+ if (idx != pathSize - 1 // Do not allow "void a() @ {..."
+ && funcAST->function_body
+ && !interface.isCursorOn(funcAST->function_body)) {
+ if (path.at(idx - 1)->asTranslationUnit()) { // normal function
+ if (idx + 3 < pathSize && path.at(idx + 3)->asQualifiedName()) // Outside member
+ moveOutsideMemberDefinition = true; // definition
+ break;
+ }
+
+ if (idx > 1) {
+ if ((classAST = path.at(idx - 2)->asSimpleDeclaration())) // member function
+ break;
+ if (path.at(idx - 2)->asNamespace()) // normal function in namespace
+ break;
+ }
+ if (idx > 2 && path.at(idx - 1)->asTemplateDeclaration()) {
+ if ((classAST = path.at(idx - 3)->asSimpleDeclaration())) // member template
+ break;
+ }
+ }
+ funcAST = nullptr;
+ }
+ }
+
+ if (!funcAST || !funcAST->symbol)
+ return;
+
+ bool isHeaderFile = false;
+ const FilePath cppFileName = correspondingHeaderOrSource(interface.filePath(), &isHeaderFile);
+
+ if (isHeaderFile && !cppFileName.isEmpty()) {
+ const MoveFuncDefRefactoringHelper::MoveType type = moveOutsideMemberDefinition
+ ? MoveFuncDefRefactoringHelper::MoveOutsideMemberToCppFile
+ : MoveFuncDefRefactoringHelper::MoveToCppFile;
+ result << new MoveFuncDefOutsideOp(interface, type, funcAST, cppFileName);
+ }
+
+ if (classAST)
+ result << new MoveFuncDefOutsideOp(interface, MoveFuncDefRefactoringHelper::MoveOutside,
+ funcAST, FilePath());
+
+ return;
+ }
+};
+
+//! Moves all member function definitions outside the class or to the implementation file.
+class MoveAllFuncDefOutside : public CppQuickFixFactory
+{
+public:
+#ifdef WITH_TESTS
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ ClassSpecifierAST * const classAST = astForClassOperations(interface);
+ if (!classAST)
+ return;
+
+ // Determine if the class has at least one function definition
+ bool classContainsFunctions = false;
+ for (DeclarationListAST *it = classAST->member_specifier_list; it; it = it->next) {
+ if (FunctionDefinitionAST *funcAST = it->value->asFunctionDefinition()) {
+ if (funcAST->symbol && !funcAST->symbol->isGenerated()) {
+ classContainsFunctions = true;
+ break;
+ }
+ }
+ }
+ if (!classContainsFunctions)
+ return;
+
+ bool isHeaderFile = false;
+ const FilePath cppFileName = correspondingHeaderOrSource(interface.filePath(), &isHeaderFile);
+ if (isHeaderFile && !cppFileName.isEmpty()) {
+ result << new MoveAllFuncDefOutsideOp(interface,
+ MoveFuncDefRefactoringHelper::MoveToCppFile,
+ classAST, cppFileName);
+ }
+ result << new MoveAllFuncDefOutsideOp(interface, MoveFuncDefRefactoringHelper::MoveOutside,
+ classAST, FilePath());
+ }
+};
+
+//! Moves the definition of a function to its declaration, with the cursor on the definition.
+class MoveFuncDefToDeclPush : public CppQuickFixFactory
+{
+public:
+#ifdef WITH_TESTS
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const QList<AST *> &path = interface.path();
+ AST *completeDefAST = nullptr;
+ FunctionDefinitionAST *funcAST = nullptr;
+
+ const int pathSize = path.size();
+ for (int idx = 1; idx < pathSize; ++idx) {
+ if ((funcAST = path.at(idx)->asFunctionDefinition())) {
+ AST *enclosingAST = path.at(idx - 1);
+ if (enclosingAST->asClassSpecifier())
+ return;
+
+ // check cursor position
+ if (idx != pathSize - 1 // Do not allow "void a() @ {..."
+ && funcAST->function_body
+ && !interface.isCursorOn(funcAST->function_body)) {
+ completeDefAST = enclosingAST->asTemplateDeclaration() ? enclosingAST : funcAST;
+ break;
+ }
+ funcAST = nullptr;
+ }
+ }
+
+ if (!funcAST || !funcAST->symbol)
+ return;
+
+ const CppRefactoringChanges refactoring(interface.snapshot());
+ const CppRefactoringFilePtr defFile = interface.currentFile();
+ const ChangeSet::Range defRange = defFile->range(completeDefAST);
+
+ // Determine declaration (file, range, text);
+ ChangeSet::Range declRange;
+ QString declText;
+ FilePath declFilePath;
+
+ Function *func = funcAST->symbol;
+ if (Class *matchingClass = isMemberFunction(interface.context(), func)) {
+ // Dealing with member functions
+ const QualifiedNameId *qName = func->name()->asQualifiedNameId();
+ for (Symbol *symbol = matchingClass->find(qName->identifier());
+ symbol; symbol = symbol->next()) {
+ Symbol *s = symbol;
+ if (func->enclosingScope()->asTemplate()) {
+ if (const Template *templ = s->type()->asTemplateType()) {
+ if (Symbol *decl = templ->declaration()) {
+ if (decl->type()->asFunctionType())
+ s = decl;
+ }
+ }
+ }
+ if (!s->name()
+ || !qName->identifier()->match(s->identifier())
+ || !s->type()->asFunctionType()
+ || !s->type().match(func->type())
+ || s->asFunction()) {
+ continue;
+ }
+
+ declFilePath = matchingClass->filePath();
+ const CppRefactoringFilePtr declFile = refactoring.cppFile(declFilePath);
+ ASTPath astPath(declFile->cppDocument());
+ const QList<AST *> path = astPath(s->line(), s->column());
+ for (int idx = path.size() - 1; idx > 0; --idx) {
+ AST *node = path.at(idx);
+ if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
+ if (simpleDecl->symbols && !simpleDecl->symbols->next) {
+ declRange = declFile->range(simpleDecl);
+ declText = declFile->textOf(simpleDecl);
+ declText.remove(-1, 1); // remove ';' from declaration text
+ break;
+ }
+ }
+ }
+
+ if (!declText.isEmpty())
+ break;
+ }
+ } else if (Namespace *matchingNamespace = isNamespaceFunction(interface.context(), func)) {
+ // Dealing with free functions
+ bool isHeaderFile = false;
+ declFilePath = correspondingHeaderOrSource(interface.filePath(), &isHeaderFile);
+ if (isHeaderFile)
+ return;
+
+ const CppRefactoringFilePtr declFile = refactoring.cppFile(declFilePath);
+ const LookupContext lc(declFile->cppDocument(), interface.snapshot());
+ const QList<LookupItem> candidates = lc.lookup(func->name(), matchingNamespace);
+ for (const LookupItem &candidate : candidates) {
+ if (Symbol *s = candidate.declaration()) {
+ if (s->asDeclaration()) {
+ ASTPath astPath(declFile->cppDocument());
+ const QList<AST *> path = astPath(s->line(), s->column());
+ for (AST *node : path) {
+ if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
+ declRange = declFile->range(simpleDecl);
+ declText = declFile->textOf(simpleDecl);
+ declText.remove(-1, 1); // remove ';' from declaration text
+ break;
+ }
+ }
+ }
+ }
+
+ if (!declText.isEmpty()) {
+ declText.prepend(inlinePrefix(declFilePath));
+ break;
+ }
+ }
+ }
+
+ if (!declFilePath.isEmpty() && !declText.isEmpty())
+ result << new MoveFuncDefToDeclOp(interface,
+ interface.filePath(),
+ declFilePath,
+ funcAST, func, declText,
+ defRange, declRange, MoveFuncDefToDeclOp::Push);
+ }
+};
+
+//! Moves the definition of a function to its declaration, with the cursor on the declaration.
+class MoveFuncDefToDeclPull : public CppQuickFixFactory
+{
+public:
+#ifdef WITH_TESTS
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const QList<AST *> &path = interface.path();
+ for (auto it = std::rbegin(path); it != std::rend(path); ++it) {
+ SimpleDeclarationAST * const simpleDecl = (*it)->asSimpleDeclaration();
+ if (!simpleDecl)
+ continue;
+ const auto prev = std::next(it);
+ if (prev != std::rend(path) && (*prev)->asStatement())
+ return;
+ if (!simpleDecl->symbols || !simpleDecl->symbols->value || simpleDecl->symbols->next)
+ return;
+ Declaration * const decl = simpleDecl->symbols->value->asDeclaration();
+ if (!decl)
+ return;
+ Function * const funcDecl = decl->type()->asFunctionType();
+ if (!funcDecl)
+ return;
+ if (funcDecl->isSignal() || funcDecl->isPureVirtual() || funcDecl->isFriend())
+ return;
+
+ // Is there a definition?
+ SymbolFinder symbolFinder;
+ Function * const funcDef = symbolFinder.findMatchingDefinition(decl, interface.snapshot(),
+ true);
+ if (!funcDef)
+ return;
+
+ QString declText = interface.currentFile()->textOf(simpleDecl);
+ declText.chop(1); // semicolon
+ declText.prepend(inlinePrefix(interface.filePath(), [funcDecl] {
+ return !funcDecl->enclosingScope()->asClass();
+ }));
+ result << new MoveFuncDefToDeclOp(interface, funcDef->filePath(), decl->filePath(), nullptr,
+ funcDef, declText, {},
+ interface.currentFile()->range(simpleDecl),
+ MoveFuncDefToDeclOp::Pull);
+ return;
+ }
+ }
+};
+
+#ifdef WITH_TESTS
+using namespace Tests;
+
+class MoveFuncDefOutsideTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ /// Check: Move definition from header to cpp.
+ void testMemberFuncToCpp()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "class Foo {\n"
+ " inline int numbe@r() const\n"
+ " {\n"
+ " return 5;\n"
+ " }\n"
+ "\n"
+ " void bar();\n"
+ "};\n";
+ expected =
+ "class Foo {\n"
+ " int number() const;\n"
+ "\n"
+ " void bar();\n"
+ "};\n";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n";
+ expected =
+ "#include \"file.h\"\n"
+ "\n"
+ "int Foo::number() const\n"
+ "{\n"
+ " return 5;\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testMemberFuncToCppInsideNS()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "namespace SomeNamespace {\n"
+ "class Foo {\n"
+ " int ba@r()\n"
+ " {\n"
+ " return 5;\n"
+ " }\n"
+ "};\n"
+ "}\n";
+ expected =
+ "namespace SomeNamespace {\n"
+ "class Foo {\n"
+ " int ba@r();\n"
+ "};\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n"
+ "namespace SomeNamespace {\n"
+ "\n"
+ "}\n";
+ expected =
+ "#include \"file.h\"\n"
+ "namespace SomeNamespace {\n"
+ "\n"
+ "int Foo::bar()\n"
+ "{\n"
+ " return 5;\n"
+ "}\n"
+ "\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check: Move definition outside class
+ void testMemberFuncOutside1()
+ {
+ QByteArray original =
+ "class Foo {\n"
+ " void f1();\n"
+ " inline int f2@() const\n"
+ " {\n"
+ " return 1;\n"
+ " }\n"
+ " void f3();\n"
+ " void f4();\n"
+ "};\n"
+ "\n"
+ "void Foo::f4() {}\n";
+ QByteArray expected =
+ "class Foo {\n"
+ " void f1();\n"
+ " int f2@() const;\n"
+ " void f3();\n"
+ " void f4();\n"
+ "};\n"
+ "\n"
+ "int Foo::f2() const\n"
+ "{\n"
+ " return 1;\n"
+ "}\n"
+ "\n"
+ "void Foo::f4() {}\n";
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ /// Check: Move definition outside class
+ void testMemberFuncOutside2()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "class Foo {\n"
+ " void f1();\n"
+ " int f2@()\n"
+ " {\n"
+ " return 1;\n"
+ " }\n"
+ " void f3();\n"
+ "};\n";
+ expected =
+ "class Foo {\n"
+ " void f1();\n"
+ " int f2();\n"
+ " void f3();\n"
+ "};\n"
+ "\n"
+ "inline int Foo::f2()\n"
+ "{\n"
+ " return 1;\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n"
+ "void Foo::f1() {}\n"
+ "void Foo::f3() {}\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 1);
+ }
+
+ /// Check: Move definition from header to cpp (with namespace).
+ void testMemberFuncToCppNS()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "namespace MyNs {\n"
+ "class Foo {\n"
+ " inline int numbe@r() const\n"
+ " {\n"
+ " return 5;\n"
+ " }\n"
+ "};\n"
+ "}\n";
+ expected =
+ "namespace MyNs {\n"
+ "class Foo {\n"
+ " int number() const;\n"
+ "};\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n";
+ expected =
+ "#include \"file.h\"\n"
+ "\n"
+ "int MyNs::Foo::number() const\n"
+ "{\n"
+ " return 5;\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check: Move definition from header to cpp (with namespace + using).
+ void testMemberFuncToCppNSUsing()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "namespace MyNs {\n"
+ "class Foo {\n"
+ " inline int numbe@r() const\n"
+ " {\n"
+ " return 5;\n"
+ " }\n"
+ "};\n"
+ "}\n";
+ expected =
+ "namespace MyNs {\n"
+ "class Foo {\n"
+ " int number() const;\n"
+ "};\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n"
+ "using namespace MyNs;\n";
+ expected =
+ "#include \"file.h\"\n"
+ "using namespace MyNs;\n"
+ "\n"
+ "int Foo::number() const\n"
+ "{\n"
+ " return 5;\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check: Move definition outside class with Namespace
+ void testMemberFuncOutsideWithNs()
+ {
+ QByteArray original =
+ "namespace MyNs {\n"
+ "class Foo {\n"
+ " inline int numbe@r() const\n"
+ " {\n"
+ " return 5;\n"
+ " }\n"
+ "};}\n";
+ QByteArray expected =
+ "namespace MyNs {\n"
+ "class Foo {\n"
+ " int number() const;\n"
+ "};\n"
+ "\n"
+ "int Foo::number() const\n"
+ "{\n"
+ " return 5;\n"
+ "}\n"
+ "\n}\n";
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ /// Check: Move free function from header to cpp.
+ void testFreeFuncToCpp()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "int numbe@r() const\n"
+ "{\n"
+ " return 5;\n"
+ "}\n";
+ expected =
+ "int number() const;\n"
+ ;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n";
+ expected =
+ "#include \"file.h\"\n"
+ "\n"
+ "int number() const\n"
+ "{\n"
+ " return 5;\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check: Move free function from header to cpp (with namespace).
+ void testFreeFuncToCppNS()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "namespace MyNamespace {\n"
+ "int numbe@r() const\n"
+ "{\n"
+ " return 5;\n"
+ "}\n"
+ "}\n";
+ expected =
+ "namespace MyNamespace {\n"
+ "int number() const;\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n";
+ expected =
+ "#include \"file.h\"\n"
+ "\n"
+ "int MyNamespace::number() const\n"
+ "{\n"
+ " return 5;\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check: Move Ctor with member initialization list (QTCREATORBUG-9157).
+ void testCtorWithInitialization1()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "class Foo {\n"
+ "public:\n"
+ " Fo@o() : a(42), b(3.141) {}\n"
+ "private:\n"
+ " int a;\n"
+ " float b;\n"
+ "};\n";
+ expected =
+ "class Foo {\n"
+ "public:\n"
+ " Foo();\n"
+ "private:\n"
+ " int a;\n"
+ " float b;\n"
+ "};\n";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original ="#include \"file.h\"\n";
+ expected =
+ "#include \"file.h\"\n"
+ "\n"
+ "Foo::Foo() : a(42), b(3.141) {}\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check: Move Ctor with member initialization list (QTCREATORBUG-9462).
+ void testCtorWithInitialization2()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "class Foo\n"
+ "{\n"
+ "public:\n"
+ " Fo@o() : member(2)\n"
+ " {\n"
+ " }\n"
+ "\n"
+ " int member;\n"
+ "};\n";
+
+ expected =
+ "class Foo\n"
+ "{\n"
+ "public:\n"
+ " Foo();\n"
+ "\n"
+ " int member;\n"
+ "};\n";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original ="#include \"file.h\"\n";
+ expected =
+ "#include \"file.h\"\n"
+ "\n"
+ "Foo::Foo() : member(2)\n"
+ "{\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check if definition is inserted right after class for move definition outside
+ void testAfterClass()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "class Foo\n"
+ "{\n"
+ " Foo();\n"
+ " void a@() {}\n"
+ "};\n"
+ "\n"
+ "class Bar {};\n";
+ expected =
+ "class Foo\n"
+ "{\n"
+ " Foo();\n"
+ " void a();\n"
+ "};\n"
+ "\n"
+ "inline void Foo::a() {}\n"
+ "\n"
+ "class Bar {};\n";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n"
+ "\n"
+ "Foo::Foo()\n"
+ "{\n\n"
+ "}\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 1);
+ }
+
+ /// Check if whitespace is respected for operator functions
+ void testRespectWsInOperatorNames1()
+ {
+ QByteArray original =
+ "class Foo\n"
+ "{\n"
+ " Foo &opera@tor =() {}\n"
+ "};\n";
+ QByteArray expected =
+ "class Foo\n"
+ "{\n"
+ " Foo &operator =();\n"
+ "};\n"
+ "\n"
+ "Foo &Foo::operator =() {}\n"
+ ;
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ /// Check if whitespace is respected for operator functions
+ void testRespectWsInOperatorNames2()
+ {
+ QByteArray original =
+ "class Foo\n"
+ "{\n"
+ " Foo &opera@tor=() {}\n"
+ "};\n";
+ QByteArray expected =
+ "class Foo\n"
+ "{\n"
+ " Foo &operator=();\n"
+ "};\n"
+ "\n"
+ "Foo &Foo::operator=() {}\n"
+ ;
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testMacroUses()
+ {
+ QByteArray original =
+ "#define CONST const\n"
+ "#define VOLATILE volatile\n"
+ "class Foo\n"
+ "{\n"
+ " int fu@nc(int a, int b) CONST VOLATILE\n"
+ " {\n"
+ " return 42;\n"
+ " }\n"
+ "};\n";
+ QByteArray expected =
+ "#define CONST const\n"
+ "#define VOLATILE volatile\n"
+ "class Foo\n"
+ "{\n"
+ " int func(int a, int b) CONST VOLATILE;\n"
+ "};\n"
+ "\n"
+ "\n"
+ // const volatile become lowercase: QTCREATORBUG-12620
+ "int Foo::func(int a, int b) const volatile\n"
+ "{\n"
+ " return 42;\n"
+ "}\n"
+ ;
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory,
+ ProjectExplorer::HeaderPaths(), 0, "QTCREATORBUG-12314");
+ }
+
+ void testTemplate()
+ {
+ QByteArray original =
+ "template<class T>\n"
+ "class Foo { void fu@nc() {} };\n";
+ QByteArray expected =
+ "template<class T>\n"
+ "class Foo { void fu@nc(); };\n"
+ "\n"
+ "template<class T>\n"
+ "void Foo<T>::func() {}\n";
+ ;
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testMemberFunctionTemplate()
+ {
+ const QByteArray original = R"(
+struct S {
+ template<typename In>
+ void @foo(In in) { (void)in; }
+};
+)";
+ const QByteArray expected = R"(
+struct S {
+ template<typename In>
+ void foo(In in);
+};
+
+template<typename In>
+void S::foo(In in) { (void)in; }
+)";
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testTemplateSpecializedClass()
+ {
+ QByteArray original = R"(
+template<typename T> class base {};
+template<>
+class base<int>
+{
+public:
+ void @bar() {}
+};
+)";
+ QByteArray expected = R"(
+template<typename T> class base {};
+template<>
+class base<int>
+{
+public:
+ void bar();
+};
+
+void base<int>::bar() {}
+)";
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testUnnamedTemplate()
+ {
+ QByteArray original =
+ "template<typename T, typename>\n"
+ "class Foo { void fu@nc() {} };\n";
+ QByteArray expected =
+ "template<typename T, typename>\n"
+ "class Foo { void fu@nc(); };\n"
+ "\n"
+ "template<typename T, typename T2>\n"
+ "void Foo<T, T2>::func() {}\n";
+ ;
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testMemberFuncToCppStatic()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "class Foo {\n"
+ " static inline int numbe@r() const\n"
+ " {\n"
+ " return 5;\n"
+ " }\n"
+ "\n"
+ " void bar();\n"
+ "};\n";
+ expected =
+ "class Foo {\n"
+ " static int number() const;\n"
+ "\n"
+ " void bar();\n"
+ "};\n";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n";
+ expected =
+ "#include \"file.h\"\n"
+ "\n"
+ "int Foo::number() const\n"
+ "{\n"
+ " return 5;\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testMemberFuncToCppWithInlinePartOfName()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "class Foo {\n"
+ " static inline int numbe@r_inline () const\n"
+ " {\n"
+ " return 5;\n"
+ " }\n"
+ "\n"
+ " void bar();\n"
+ "};\n";
+ expected =
+ "class Foo {\n"
+ " static int number_inline () const;\n"
+ "\n"
+ " void bar();\n"
+ "};\n";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n";
+ expected =
+ "#include \"file.h\"\n"
+ "\n"
+ "int Foo::number_inline() const\n"
+ "{\n"
+ " return 5;\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testMixedQualifiers()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original = R"(
+struct Base {
+ virtual auto func() const && noexcept -> void = 0;
+};
+struct Derived : public Base {
+ auto @func() const && noexcept -> void override {}
+};)";
+ expected = R"(
+struct Base {
+ virtual auto func() const && noexcept -> void = 0;
+};
+struct Derived : public Base {
+ auto func() const && noexcept -> void override;
+};)";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original = "#include \"file.h\"\n";
+ expected = R"DELIM(#include "file.h"
+
+auto Derived::func() const && noexcept -> void {}
+)DELIM";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ MoveFuncDefOutside factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+};
+
+class MoveAllFuncDefOutsideTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void testMemberFuncToCpp()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "class Foo {@\n"
+ " int numberA() const\n"
+ " {\n"
+ " return 5;\n"
+ " }\n"
+ " int numberB() const\n"
+ " {\n"
+ " return 5;\n"
+ " }\n"
+ "};\n";
+ expected =
+ "class Foo {\n"
+ " int numberA() const;\n"
+ " int numberB() const;\n"
+ "};\n";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n";
+ expected =
+ "#include \"file.h\"\n"
+ "\n"
+ "int Foo::numberA() const\n"
+ "{\n"
+ " return 5;\n"
+ "}\n"
+ "\n"
+ "int Foo::numberB() const\n"
+ "{\n"
+ " return 5;\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ MoveAllFuncDefOutside factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testMemberFuncOutside()
+ {
+ QByteArray original =
+ "class F@oo {\n"
+ " int f1()\n"
+ " {\n"
+ " return 1;\n"
+ " }\n"
+ " int f2() const\n"
+ " {\n"
+ " return 2;\n"
+ " }\n"
+ "};\n";
+ QByteArray expected =
+ "class Foo {\n"
+ " int f1();\n"
+ " int f2() const;\n"
+ "};\n"
+ "\n"
+ "int Foo::f1()\n"
+ "{\n"
+ " return 1;\n"
+ "}\n"
+ "\n"
+ "int Foo::f2() const\n"
+ "{\n"
+ " return 2;\n"
+ "}\n";
+
+ MoveAllFuncDefOutside factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testDoNotTriggerOnBaseClass()
+ {
+ QByteArray original =
+ "class Bar;\n"
+ "class Foo : public Ba@r {\n"
+ " int f1()\n"
+ " {\n"
+ " return 1;\n"
+ " }\n"
+ "};\n";
+
+ MoveAllFuncDefOutside factory;
+ QuickFixOperationTest(singleDocument(original, ""), &factory);
+ }
+
+ void testClassWithBaseClass()
+ {
+ QByteArray original =
+ "class Bar;\n"
+ "class Fo@o : public Bar {\n"
+ " int f1()\n"
+ " {\n"
+ " return 1;\n"
+ " }\n"
+ "};\n";
+ QByteArray expected =
+ "class Bar;\n"
+ "class Foo : public Bar {\n"
+ " int f1();\n"
+ "};\n"
+ "\n"
+ "int Foo::f1()\n"
+ "{\n"
+ " return 1;\n"
+ "}\n";
+
+ MoveAllFuncDefOutside factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ /// Check: Do not take macro expanded code into account (QTCREATORBUG-13900)
+ void testIgnoreMacroCode()
+ {
+ QByteArray original =
+ "#define FAKE_Q_OBJECT int bar() {return 5;}\n"
+ "class Fo@o {\n"
+ " FAKE_Q_OBJECT\n"
+ " int f1()\n"
+ " {\n"
+ " return 1;\n"
+ " }\n"
+ "};\n";
+ QByteArray expected =
+ "#define FAKE_Q_OBJECT int bar() {return 5;}\n"
+ "class Foo {\n"
+ " FAKE_Q_OBJECT\n"
+ " int f1();\n"
+ "};\n"
+ "\n"
+ "int Foo::f1()\n"
+ "{\n"
+ " return 1;\n"
+ "}\n";
+
+ MoveAllFuncDefOutside factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+};
+
+class MoveFuncDefToDeclTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data()
+ {
+ QTest::addColumn<QByteArrayList>("headers");
+ QTest::addColumn<QByteArrayList>("sources");
+
+ QByteArray originalHeader;
+ QByteArray expectedHeader;
+ QByteArray originalSource;
+ QByteArray expectedSource;
+
+ originalHeader =
+ "class Foo {\n"
+ " inline int @number() const;\n"
+ "};\n";
+ expectedHeader =
+ "class Foo {\n"
+ " inline int number() const {return 5;}\n"
+ "};\n";
+ originalSource =
+ "#include \"file.h\"\n"
+ "\n"
+ "int Foo::num@ber() const {return 5;}\n";
+ expectedSource =
+ "#include \"file.h\"\n"
+ "\n\n";
+ QTest::newRow("member function, two files") << QByteArrayList{originalHeader, expectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalSource =
+ "class Foo {\n"
+ " inline int @number() const;\n"
+ "};\n"
+ "\n"
+ "int Foo::num@ber() const\n"
+ "{\n"
+ " return 5;\n"
+ "}\n";
+
+ expectedSource =
+ "class Foo {\n"
+ " inline int number() const\n"
+ " {\n"
+ " return 5;\n"
+ " }\n"
+ "};\n\n\n";
+ QTest::newRow("member function, one file") << QByteArrayList()
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalHeader =
+ "namespace MyNs {\n"
+ "class Foo {\n"
+ " inline int @number() const;\n"
+ "};\n"
+ "}\n";
+ expectedHeader =
+ "namespace MyNs {\n"
+ "class Foo {\n"
+ " inline int number() const\n"
+ " {\n"
+ " return 5;\n"
+ " }\n"
+ "};\n"
+ "}\n";
+ originalSource =
+ "#include \"file.h\"\n"
+ "\n"
+ "int MyNs::Foo::num@ber() const\n"
+ "{\n"
+ " return 5;\n"
+ "}\n";
+ expectedSource = "#include \"file.h\"\n\n\n";
+ QTest::newRow("member function, two files, namespace")
+ << QByteArrayList{originalHeader, expectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalHeader =
+ "namespace MyNs {\n"
+ "class Foo {\n"
+ " inline int numbe@r() const;\n"
+ "};\n"
+ "}\n";
+ expectedHeader =
+ "namespace MyNs {\n"
+ "class Foo {\n"
+ " inline int number() const\n"
+ " {\n"
+ " return 5;\n"
+ " }\n"
+ "};\n"
+ "}\n";
+ originalSource =
+ "#include \"file.h\"\n"
+ "using namespace MyNs;\n"
+ "\n"
+ "int Foo::num@ber() const\n"
+ "{\n"
+ " return 5;\n"
+ "}\n";
+ expectedSource =
+ "#include \"file.h\"\n"
+ "using namespace MyNs;\n"
+ "\n\n";
+ QTest::newRow("member function, two files, namespace with using-directive")
+ << QByteArrayList{originalHeader, expectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalSource =
+ "namespace MyNs {\n"
+ "class Foo {\n"
+ " inline int @number() const;\n"
+ "};\n"
+ "\n"
+ "int Foo::numb@er() const\n"
+ "{\n"
+ " return 5;\n"
+ "}"
+ "\n}\n";
+ expectedSource =
+ "namespace MyNs {\n"
+ "class Foo {\n"
+ " inline int number() const\n"
+ " {\n"
+ " return 5;\n"
+ " }\n"
+ "};\n\n\n}\n";
+
+ QTest::newRow("member function, one file, namespace")
+ << QByteArrayList() << QByteArrayList{originalSource, expectedSource};
+
+ originalHeader = "int nu@mber() const;\n";
+ expectedHeader =
+ "inline int number() const\n"
+ "{\n"
+ " return 5;\n"
+ "}\n";
+ originalSource =
+ "#include \"file.h\"\n"
+ "\n"
+ "\n"
+ "int numb@er() const\n"
+ "{\n"
+ " return 5;\n"
+ "}\n";
+ expectedSource = "#include \"file.h\"\n\n\n\n";
+ QTest::newRow("free function") << QByteArrayList{originalHeader, expectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalHeader =
+ "namespace MyNamespace {\n"
+ "int n@umber() const;\n"
+ "}\n";
+ expectedHeader =
+ "namespace MyNamespace {\n"
+ "inline int number() const\n"
+ "{\n"
+ " return 5;\n"
+ "}\n"
+ "}\n";
+ originalSource =
+ "#include \"file.h\"\n"
+ "\n"
+ "int MyNamespace::nu@mber() const\n"
+ "{\n"
+ " return 5;\n"
+ "}\n";
+ expectedSource =
+ "#include \"file.h\"\n"
+ "\n\n";
+ QTest::newRow("free function, namespace") << QByteArrayList{originalHeader, expectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalHeader =
+ "class Foo {\n"
+ "public:\n"
+ " Fo@o();\n"
+ "private:\n"
+ " int a;\n"
+ " float b;\n"
+ "};\n";
+ expectedHeader =
+ "class Foo {\n"
+ "public:\n"
+ " Foo() : a(42), b(3.141) {}\n"
+ "private:\n"
+ " int a;\n"
+ " float b;\n"
+ "};\n";
+ originalSource =
+ "#include \"file.h\"\n"
+ "\n"
+ "Foo::F@oo() : a(42), b(3.141) {}"
+ ;
+ expectedSource ="#include \"file.h\"\n\n";
+ QTest::newRow("constructor") << QByteArrayList{originalHeader, expectedHeader}
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalSource =
+ "struct Foo\n"
+ "{\n"
+ " void f@oo();\n"
+ "} bar;\n"
+ "void Foo::fo@o()\n"
+ "{\n"
+ " return;\n"
+ "}";
+ expectedSource =
+ "struct Foo\n"
+ "{\n"
+ " void foo()\n"
+ " {\n"
+ " return;\n"
+ " }\n"
+ "} bar;\n";
+ QTest::newRow("QTCREATORBUG-10303") << QByteArrayList()
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalSource =
+ "struct Base {\n"
+ " virtual int foo() = 0;\n"
+ "};\n"
+ "struct Derived : Base {\n"
+ " int @foo() override;\n"
+ "};\n"
+ "\n"
+ "int Derived::fo@o()\n"
+ "{\n"
+ " return 5;\n"
+ "}\n";
+ expectedSource =
+ "struct Base {\n"
+ " virtual int foo() = 0;\n"
+ "};\n"
+ "struct Derived : Base {\n"
+ " int foo() override\n"
+ " {\n"
+ " return 5;\n"
+ " }\n"
+ "};\n\n\n";
+ QTest::newRow("overridden virtual") << QByteArrayList()
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalSource =
+ "template<class T>\n"
+ "class Foo { void @func(); };\n"
+ "\n"
+ "template<class T>\n"
+ "void Foo<T>::fu@nc() {}\n";
+ expectedSource =
+ "template<class T>\n"
+ "class Foo { void fu@nc() {} };\n\n\n";
+ QTest::newRow("class template") << QByteArrayList()
+ << QByteArrayList{originalSource, expectedSource};
+
+ originalSource =
+ "class Foo\n"
+ "{\n"
+ " template<class T>\n"
+ " void @func();\n"
+ "};\n"
+ "\n"
+ "template<class T>\n"
+ "void Foo::fu@nc() {}\n";
+ expectedSource =
+ "class Foo\n"
+ "{\n"
+ " template<class T>\n"
+ " void func() {}\n"
+ "};\n\n\n";
+ QTest::newRow("function template") << QByteArrayList()
+ << QByteArrayList{originalSource, expectedSource};
+ }
+
+ void test()
+ {
+ QFETCH(QByteArrayList, headers);
+ QFETCH(QByteArrayList, sources);
+
+ QVERIFY(headers.isEmpty() || headers.size() == 2);
+ QVERIFY(sources.size() == 2);
+
+ QByteArray &declDoc = !headers.empty() ? headers.first() : sources.first();
+ const int declCursorPos = declDoc.indexOf('@');
+ QVERIFY(declCursorPos != -1);
+ const int defCursorPos = sources.first().lastIndexOf('@');
+ QVERIFY(defCursorPos != -1);
+ QVERIFY(declCursorPos != defCursorPos);
+
+ declDoc.remove(declCursorPos, 1);
+ QList<TestDocumentPtr> testDocuments;
+ if (!headers.isEmpty())
+ testDocuments << CppTestDocument::create("file.h", headers.first(), headers.last());
+ testDocuments << CppTestDocument::create("file.cpp", sources.first(), sources.last());
+
+ MoveFuncDefToDeclPush pushFactory;
+ QuickFixOperationTest(testDocuments, &pushFactory);
+
+ declDoc.insert(declCursorPos, '@');
+ sources.first().remove(defCursorPos, 1);
+ testDocuments.clear();
+ if (!headers.isEmpty())
+ testDocuments << CppTestDocument::create("file.h", headers.first(), headers.last());
+ testDocuments << CppTestDocument::create("file.cpp", sources.first(), sources.last());
+
+ MoveFuncDefToDeclPull pullFactory;
+ QuickFixOperationTest(testDocuments, &pullFactory);
+ }
+
+ void testMacroUses()
+ {
+ QByteArray original =
+ "#define CONST const\n"
+ "#define VOLATILE volatile\n"
+ "class Foo\n"
+ "{\n"
+ " int func(int a, int b) CONST VOLATILE;\n"
+ "};\n"
+ "\n"
+ "\n"
+ "int Foo::fu@nc(int a, int b) CONST VOLATILE"
+ "{\n"
+ " return 42;\n"
+ "}\n";
+ QByteArray expected =
+ "#define CONST const\n"
+ "#define VOLATILE volatile\n"
+ "class Foo\n"
+ "{\n"
+ " int func(int a, int b) CONST VOLATILE\n"
+ " {\n"
+ " return 42;\n"
+ " }\n"
+ "};\n\n\n\n";
+
+ MoveFuncDefToDeclPush factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory,
+ ProjectExplorer::HeaderPaths(), 0, "QTCREATORBUG-12314");
+ }
+};
+
+QObject *MoveFuncDefOutside::createTest()
+{
+ return new MoveFuncDefOutsideTest;
+}
+
+QObject *MoveAllFuncDefOutside::createTest()
+{
+ return new MoveAllFuncDefOutsideTest;
+}
+
+QObject *MoveFuncDefToDeclPush::createTest()
+{
+ return new MoveFuncDefToDeclTest;
+}
+
+QObject *MoveFuncDefToDeclPull::createTest()
+{
+ return new QObject; // The test for the push factory handled both cases.
+}
+
+#endif // WITH_TESTS
+
+} // namespace
+
+void registerMoveFunctionDefinitionQuickfixes()
+{
+ CppQuickFixFactory::registerFactory<MoveFuncDefOutside>();
+ CppQuickFixFactory::registerFactory<MoveAllFuncDefOutside>();
+ CppQuickFixFactory::registerFactory<MoveFuncDefToDeclPush>();
+ CppQuickFixFactory::registerFactory<MoveFuncDefToDeclPull>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <movefunctiondefinition.moc>
+#endif
diff --git a/src/plugins/cppeditor/quickfixes/movefunctiondefinition.h b/src/plugins/cppeditor/quickfixes/movefunctiondefinition.h
new file mode 100644
index 0000000000..4c6e481ad4
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/movefunctiondefinition.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerMoveFunctionDefinitionQuickfixes();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/rearrangeparamdeclarationlist.cpp b/src/plugins/cppeditor/quickfixes/rearrangeparamdeclarationlist.cpp
new file mode 100644
index 0000000000..6917771a67
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/rearrangeparamdeclarationlist.cpp
@@ -0,0 +1,121 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "rearrangeparamdeclarationlist.h"
+
+#include "../cppeditortr.h"
+#include "../cpprefactoringchanges.h"
+#include "cppquickfix.h"
+
+#ifdef WITH_TESTS
+#include <QObject>
+#endif
+
+using namespace CPlusPlus;
+
+namespace CppEditor::Internal {
+namespace {
+
+class RearrangeParamDeclarationListOp: public CppQuickFixOperation
+{
+public:
+ enum Target { TargetPrevious, TargetNext };
+
+ RearrangeParamDeclarationListOp(const CppQuickFixInterface &interface, AST *currentParam,
+ AST *targetParam, Target target)
+ : CppQuickFixOperation(interface)
+ , m_currentParam(currentParam)
+ , m_targetParam(targetParam)
+ {
+ QString targetString;
+ if (target == TargetPrevious)
+ targetString = Tr::tr("Switch with Previous Parameter");
+ else
+ targetString = Tr::tr("Switch with Next Parameter");
+ setDescription(targetString);
+ }
+
+ void perform() override
+ {
+ int targetEndPos = currentFile()->endOf(m_targetParam);
+ currentFile()->setOpenEditor(false, targetEndPos);
+ currentFile()->apply(Utils::ChangeSet::makeFlip(
+ currentFile()->startOf(m_currentParam),
+ currentFile()->endOf(m_currentParam),
+ currentFile()->startOf(m_targetParam),
+ targetEndPos));
+ }
+
+private:
+ AST *m_currentParam;
+ AST *m_targetParam;
+};
+
+
+/*!
+ Switches places of the parameter declaration under cursor
+ with the next or the previous one in the parameter declaration list
+
+ Activates on: parameter declarations
+*/
+class RearrangeParamDeclarationList : public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest() { return new QObject; }
+#endif
+
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const QList<AST *> path = interface.path();
+
+ ParameterDeclarationAST *paramDecl = nullptr;
+ int index = path.size() - 1;
+ for (; index != -1; --index) {
+ paramDecl = path.at(index)->asParameterDeclaration();
+ if (paramDecl)
+ break;
+ }
+
+ if (index < 1)
+ return;
+
+ ParameterDeclarationClauseAST *paramDeclClause
+ = path.at(index - 1)->asParameterDeclarationClause();
+ QTC_ASSERT(paramDeclClause && paramDeclClause->parameter_declaration_list, return);
+
+ ParameterDeclarationListAST *paramListNode = paramDeclClause->parameter_declaration_list;
+ ParameterDeclarationListAST *prevParamListNode = nullptr;
+ while (paramListNode) {
+ if (paramDecl == paramListNode->value)
+ break;
+ prevParamListNode = paramListNode;
+ paramListNode = paramListNode->next;
+ }
+
+ if (!paramListNode)
+ return;
+
+ if (prevParamListNode)
+ result << new RearrangeParamDeclarationListOp(
+ interface,
+ paramListNode->value,
+ prevParamListNode->value,
+ RearrangeParamDeclarationListOp::TargetPrevious);
+ if (paramListNode->next)
+ result << new RearrangeParamDeclarationListOp(
+ interface,
+ paramListNode->value,
+ paramListNode->next->value,
+ RearrangeParamDeclarationListOp::TargetNext);
+ }
+};
+
+} // namespace
+
+void registerRearrangeParamDeclarationListQuickfix()
+{
+ CppQuickFixFactory::registerFactory<RearrangeParamDeclarationList>();
+}
+
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/rearrangeparamdeclarationlist.h b/src/plugins/cppeditor/quickfixes/rearrangeparamdeclarationlist.h
new file mode 100644
index 0000000000..6fa78885d9
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/rearrangeparamdeclarationlist.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerRearrangeParamDeclarationListQuickfix();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/reformatpointerdeclaration.cpp b/src/plugins/cppeditor/quickfixes/reformatpointerdeclaration.cpp
new file mode 100644
index 0000000000..6a7ab4c240
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/reformatpointerdeclaration.cpp
@@ -0,0 +1,186 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "reformatpointerdeclaration.h"
+
+#include "../cppcodestylesettings.h"
+#include "../cppeditortr.h"
+#include "../cpppointerdeclarationformatter.h"
+#include "../cpprefactoringchanges.h"
+#include "cppquickfix.h"
+
+#include <cplusplus/Overview.h>
+
+#ifdef WITH_TESTS
+#include "cppquickfix_test.h"
+#include <QtTest>
+#endif
+
+using namespace CPlusPlus;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+class ReformatPointerDeclarationOp: public CppQuickFixOperation
+{
+public:
+ ReformatPointerDeclarationOp(const CppQuickFixInterface &interface, const ChangeSet change)
+ : CppQuickFixOperation(interface)
+ , m_change(change)
+ {
+ QString description;
+ if (m_change.operationList().size() == 1) {
+ description = Tr::tr(
+ "Reformat to \"%1\"").arg(m_change.operationList().constFirst().text());
+ } else { // > 1
+ description = Tr::tr("Reformat Pointers or References");
+ }
+ setDescription(description);
+ }
+
+ void perform() override
+ {
+ currentFile()->apply(m_change);
+ }
+
+private:
+ ChangeSet m_change;
+};
+
+/// Filter the results of ASTPath.
+/// The resulting list contains the supported AST types only once.
+/// For this, the results of ASTPath are iterated in reverse order.
+class ReformatPointerDeclarationASTPathResultsFilter
+{
+public:
+ QList<AST*> filter(const QList<AST*> &astPathList)
+ {
+ QList<AST*> filtered;
+
+ for (int i = astPathList.size() - 1; i >= 0; --i) {
+ AST *ast = astPathList.at(i);
+
+ if (!m_hasSimpleDeclaration && ast->asSimpleDeclaration()) {
+ m_hasSimpleDeclaration = true;
+ filtered.append(ast);
+ } else if (!m_hasFunctionDefinition && ast->asFunctionDefinition()) {
+ m_hasFunctionDefinition = true;
+ filtered.append(ast);
+ } else if (!m_hasParameterDeclaration && ast->asParameterDeclaration()) {
+ m_hasParameterDeclaration = true;
+ filtered.append(ast);
+ } else if (!m_hasIfStatement && ast->asIfStatement()) {
+ m_hasIfStatement = true;
+ filtered.append(ast);
+ } else if (!m_hasWhileStatement && ast->asWhileStatement()) {
+ m_hasWhileStatement = true;
+ filtered.append(ast);
+ } else if (!m_hasForStatement && ast->asForStatement()) {
+ m_hasForStatement = true;
+ filtered.append(ast);
+ } else if (!m_hasForeachStatement && ast->asForeachStatement()) {
+ m_hasForeachStatement = true;
+ filtered.append(ast);
+ }
+ }
+
+ return filtered;
+ }
+
+private:
+ bool m_hasSimpleDeclaration = false;
+ bool m_hasFunctionDefinition = false;
+ bool m_hasParameterDeclaration = false;
+ bool m_hasIfStatement = false;
+ bool m_hasWhileStatement = false;
+ bool m_hasForStatement = false;
+ bool m_hasForeachStatement = false;
+};
+
+/*!
+ Reformats a pointer, reference or rvalue reference type/declaration.
+
+ Works also with selections (except when the cursor is not on any AST).
+
+ Activates on: simple declarations, parameters and return types of function
+ declarations and definitions, control flow statements (if,
+ while, for, foreach) with declarations.
+*/
+class ReformatPointerDeclaration : public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const QList<AST *> &path = interface.path();
+ CppRefactoringFilePtr file = interface.currentFile();
+
+ Overview overview = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ overview.showArgumentNames = true;
+ overview.showReturnTypes = true;
+
+ const QTextCursor cursor = file->cursor();
+ ChangeSet change;
+ PointerDeclarationFormatter formatter(file, overview,
+ PointerDeclarationFormatter::RespectCursor);
+
+ if (cursor.hasSelection()) {
+ // This will no work always as expected since this function is only called if
+ // interface-path() is not empty. If the user selects the whole document via
+ // ctrl-a and there is an empty line in the end, then the cursor is not on
+ // any AST and therefore no quick fix will be triggered.
+ change = formatter.format(file->cppDocument()->translationUnit()->ast());
+ if (!change.isEmpty())
+ result << new ReformatPointerDeclarationOp(interface, change);
+ } else {
+ const QList<AST *> suitableASTs
+ = ReformatPointerDeclarationASTPathResultsFilter().filter(path);
+ for (AST *ast : suitableASTs) {
+ change = formatter.format(ast);
+ if (!change.isEmpty()) {
+ result << new ReformatPointerDeclarationOp(interface, change);
+ return;
+ }
+ }
+ }
+ }
+};
+
+#ifdef WITH_TESTS
+using namespace Tests;
+
+class ReformatPointerDeclarationTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test()
+ {
+ // Check: Just a basic test since the main functionality is tested in
+ // cpppointerdeclarationformatter_test.cpp
+ ReformatPointerDeclaration factory;
+ QuickFixOperationTest(
+ singleDocument(QByteArray("char@*s;"), QByteArray("char *s;")), &factory);
+ }
+};
+
+QObject *ReformatPointerDeclaration::createTest() { return new ReformatPointerDeclarationTest; }
+
+#endif // WITH_TESTS
+}
+
+void registerReformatPointerDeclarationQuickfix()
+{
+ CppQuickFixFactory::registerFactory<ReformatPointerDeclaration>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <reformatpointerdeclaration.moc>
+#endif
diff --git a/src/plugins/cppeditor/quickfixes/reformatpointerdeclaration.h b/src/plugins/cppeditor/quickfixes/reformatpointerdeclaration.h
new file mode 100644
index 0000000000..89bbe5cd77
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/reformatpointerdeclaration.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerReformatPointerDeclarationQuickfix();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/removeusingnamespace.cpp b/src/plugins/cppeditor/quickfixes/removeusingnamespace.cpp
new file mode 100644
index 0000000000..f889b14121
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/removeusingnamespace.cpp
@@ -0,0 +1,957 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "removeusingnamespace.h"
+
+#include "../cppeditortr.h"
+#include "../cppprojectfile.h"
+#include "../cpprefactoringchanges.h"
+#include "cppquickfix.h"
+
+#include <cplusplus/Overview.h>
+#include <projectexplorer/projectmanager.h>
+
+#ifdef WITH_TESTS
+#include "cppquickfix_test.h"
+#include <QtTest>
+#endif
+
+using namespace CPlusPlus;
+using namespace ProjectExplorer;
+using namespace TextEditor;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+/**
+ * @brief The NameCounter class counts the parts of a name. E.g. 2 for std::vector or 1 for variant
+ */
+class NameCounter : private NameVisitor
+{
+public:
+ int count(const Name *name)
+ {
+ counter = 0;
+ accept(name);
+ return counter;
+ }
+
+private:
+ void visit(const Identifier *) override { ++counter; }
+ void visit(const DestructorNameId *) override { ++counter; }
+ void visit(const TemplateNameId *) override { ++counter; }
+ void visit(const QualifiedNameId *name) override
+ {
+ if (name->base())
+ accept(name->base());
+ accept(name->name());
+ }
+ int counter;
+};
+
+/**
+ * @brief getBaseName returns the base name of a qualified name or nullptr.
+ * E.g.: foo::bar => foo; bar => bar
+ * @param name The Name, maybe qualified
+ * @return The base name of the qualified name or nullptr
+ */
+const Identifier *getBaseName(const Name *name)
+{
+ class GetBaseName : public NameVisitor
+ {
+ void visit(const Identifier *name) override { baseName = name; }
+ void visit(const QualifiedNameId *name) override
+ {
+ if (name->base())
+ accept(name->base());
+ else
+ accept(name->name());
+ }
+
+ public:
+ const Identifier *baseName = nullptr;
+ };
+ GetBaseName getter;
+ getter.accept(name);
+ return getter.baseName;
+}
+
+/**
+ * @brief countNames counts the parts of the Name.
+ * E.g. if the name is std::vector, the function returns 2, if the name is variant, returns 1
+ * @param name The name that should be counted
+ * @return the number of parts of the name
+ */
+int countNames(const Name *name)
+{
+ return NameCounter{}.count(name);
+}
+
+/**
+ * @brief removeLine removes the whole line in which the ast node is located if there are otherwise only whitespaces
+ * @param file The file in which the AST node is located
+ * @param ast The ast node
+ * @param changeSet The ChangeSet of the file
+ */
+void removeLine(const CppRefactoringFile *file, AST *ast, ChangeSet &changeSet)
+{
+ RefactoringFile::Range range = file->range(ast);
+ --range.start;
+ while (range.start >= 0) {
+ QChar current = file->charAt(range.start);
+ if (!current.isSpace()) {
+ ++range.start;
+ break;
+ }
+ if (current == QChar::ParagraphSeparator)
+ break;
+ --range.start;
+ }
+ range.start = std::max(0, range.start);
+ while (range.end < file->document()->characterCount()) {
+ QChar current = file->charAt(range.end);
+ if (!current.isSpace())
+ break;
+ if (current == QChar::ParagraphSeparator)
+ break;
+ ++range.end;
+ }
+ range.end = std::min(file->document()->characterCount(), range.end);
+ const bool newLineStart = file->charAt(range.start) == QChar::ParagraphSeparator;
+ const bool newLineEnd = file->charAt(range.end) == QChar::ParagraphSeparator;
+ if (!newLineEnd && newLineStart)
+ ++range.start;
+ changeSet.remove(range);
+}
+
+/**
+ * @brief The RemoveNamespaceVisitor class removes a using namespace and rewrites all types that
+ * are in the namespace if needed
+ */
+class RemoveNamespaceVisitor : public ASTVisitor
+{
+public:
+ constexpr static int SearchGlobalUsingDirectivePos = std::numeric_limits<int>::max();
+ RemoveNamespaceVisitor(const CppRefactoringFile *file,
+ const Snapshot &snapshot,
+ const Name *namespace_,
+ int symbolPos,
+ bool removeAllAtGlobalScope)
+ : ASTVisitor(file->cppDocument()->translationUnit())
+ , m_file(file)
+ , m_snapshot(snapshot)
+ , m_namespace(namespace_)
+ , m_missingNamespace(toString(namespace_) + "::")
+ , m_context(m_file->cppDocument(), m_snapshot)
+ , m_symbolPos(symbolPos)
+ , m_removeAllAtGlobalScope(removeAllAtGlobalScope)
+
+ {}
+
+ const ChangeSet &getChanges() { return m_changeSet; }
+
+ /**
+ * @brief isGlobalUsingNamespace return true if the using namespace that should be removed
+ * is not scoped and other files that include this file will also use the using namespace
+ * @return true if using namespace statement is global and not scoped, false otherwise
+ */
+ bool isGlobalUsingNamespace() const { return m_parentNode == nullptr; }
+
+ /**
+ * @brief foundGlobalUsingNamespace return true if removeAllAtGlobalScope is false and
+ * another using namespace is found at the global scope, so that other files that include this
+ * file don't have to be processed
+ * @return true if there was a 'global' second using namespace in this file and
+ * removeAllAtGlobalScope is false
+ */
+ bool foundGlobalUsingNamespace() const { return m_foundNamespace; }
+
+private:
+ bool preVisit(AST *ast) override
+ {
+ if (!m_start) {
+ if (ast->asTranslationUnit())
+ return true;
+ if (UsingDirectiveAST *usingDirective = ast->asUsingDirective()) {
+ if (nameEqual(usingDirective->name->name, m_namespace)) {
+ if (m_symbolPos == SearchGlobalUsingDirectivePos) {
+ // we have found a global using directive, so lets start
+ m_start = true;
+ removeLine(m_file, ast, m_changeSet);
+ return false;
+ }
+ // ignore the using namespace that should be removed
+ if (m_file->endOf(ast) != m_symbolPos) {
+ if (m_removeAllAtGlobalScope)
+ removeLine(m_file, ast, m_changeSet);
+ else
+ m_done = true;
+ }
+ }
+ }
+ // if the end of the ast is before we should start, we are not interested in the node
+ if (m_file->endOf(ast) <= m_symbolPos)
+ return false;
+
+ if (m_file->startOf(ast) > m_symbolPos)
+ m_start = true;
+ }
+ return !m_foundNamespace && !m_done;
+ }
+
+ bool visit(NamespaceAST *ast) override
+ {
+ if (m_start && nameEqual(m_namespace, ast->symbol->name()))
+ return false;
+
+ return m_start;
+ }
+
+ // scopes for using namespace statements:
+ bool visit(LinkageBodyAST *ast) override { return visitNamespaceScope(ast); }
+ bool visit(CompoundStatementAST *ast) override { return visitNamespaceScope(ast); }
+ bool visitNamespaceScope(AST *ast)
+ {
+ ++m_namespaceScopeCounter;
+ if (!m_start)
+ m_parentNode = ast;
+ return true;
+ }
+
+ void endVisit(LinkageBodyAST *ast) override { endVisitNamespaceScope(ast); }
+ void endVisit(CompoundStatementAST *ast) override { endVisitNamespaceScope(ast); }
+ void endVisitNamespaceScope(AST *ast)
+ {
+ --m_namespaceScopeCounter;
+ m_foundNamespace = false;
+ // if we exit the scope of the using namespace we are done
+ if (ast == m_parentNode)
+ m_done = true;
+ }
+
+ bool visit(UsingDirectiveAST *ast) override
+ {
+ if (nameEqual(ast->name->name, m_namespace)) {
+ if (m_removeAllAtGlobalScope && m_namespaceScopeCounter == 0)
+ removeLine(m_file, ast, m_changeSet);
+ else
+ m_foundNamespace = true;
+ return false;
+ }
+ return handleAstWithLongestName(ast);
+ }
+
+ bool visit(DeclaratorIdAST *ast) override
+ {
+ // e.g. we have the following code and get the following Lookup items:
+ // namespace test {
+ // struct foo { // 1. item with test::foo
+ // foo(); // 2. item with test::foo::foo
+ // };
+ // }
+ // using namespace foo;
+ // foo::foo() { ... } // 3. item with foo::foo
+ // Our current name is foo::foo so we have to match with the 2. item / longest name
+ return handleAstWithLongestName(ast);
+ }
+
+ template<typename AST>
+ bool handleAstWithLongestName(AST *ast)
+ {
+ if (m_start) {
+ Scope *scope = m_file->scopeAt(ast->firstToken());
+ const QList<LookupItem> localLookup = m_context.lookup(ast->name->name, scope);
+ QList<const Name *> longestName;
+ for (const LookupItem &item : localLookup) {
+ QList<const Name *> names
+ = m_context.fullyQualifiedName(item.declaration(),
+ LookupContext::HideInlineNamespaces);
+ if (names.length() > longestName.length())
+ longestName = names;
+ }
+ const int currentNameCount = countNames(ast->name->name);
+ const bool needNew = needMissingNamespaces(std::move(longestName), currentNameCount);
+ if (needNew)
+ insertMissingNamespace(ast);
+ }
+ return false;
+ }
+
+ bool visit(NamedTypeSpecifierAST *ast) override { return handleAstWithName(ast); }
+
+ bool visit(IdExpressionAST *ast) override { return handleAstWithName(ast); }
+
+ template<typename AST>
+ bool handleAstWithName(AST *ast)
+ {
+ if (m_start) {
+ Scope *scope = m_file->scopeAt(ast->firstToken());
+ const Name *wantToLookup = ast->name->name;
+ // first check if the base name is a typedef. Consider the following example:
+ // using namespace std;
+ // using vec = std::vector<int>;
+ // vec::iterator it; // we have to lookup 'vec' and not iterator (would result in
+ // std::vector<int>::iterator => std::vec::iterator, which is wrong)
+ const Name *baseName = getBaseName(wantToLookup);
+ QList<LookupItem> typedefCandidates = m_context.lookup(baseName, scope);
+ if (!typedefCandidates.isEmpty()) {
+ if (typedefCandidates.front().declaration()->isTypedef())
+ wantToLookup = baseName;
+ }
+
+ const QList<LookupItem> lookups = m_context.lookup(wantToLookup, scope);
+ if (!lookups.empty()) {
+ QList<const Name *> fullName
+ = m_context.fullyQualifiedName(lookups.first().declaration(),
+ LookupContext::HideInlineNamespaces);
+ const int currentNameCount = countNames(wantToLookup);
+ const bool needNamespace = needMissingNamespaces(std::move(fullName),
+ currentNameCount);
+ if (needNamespace)
+ insertMissingNamespace(ast);
+ }
+ }
+ return true;
+ }
+
+ template<typename AST>
+ void insertMissingNamespace(AST *ast)
+ {
+ DestructorNameAST *destructorName = ast->name->asDestructorName();
+ if (destructorName)
+ m_changeSet.insert(m_file->startOf(destructorName->unqualified_name), m_missingNamespace);
+ else
+ m_changeSet.insert(m_file->startOf(ast->name), m_missingNamespace);
+ m_changeSet.operationList().last().setFormat1(false);
+ }
+
+ bool needMissingNamespaces(QList<const Name *> &&fullName, int currentNameCount)
+ {
+ if (currentNameCount > fullName.length())
+ return false;
+
+ // eg. fullName = std::vector, currentName = vector => result should be std
+ fullName.erase(fullName.end() - currentNameCount, fullName.end());
+ if (fullName.empty())
+ return false;
+ return nameEqual(m_namespace, fullName.last());
+ }
+
+ static bool nameEqual(const Name *name1, const Name *name2)
+ {
+ return Matcher::match(name1, name2);
+ }
+
+ QString toString(const Name *id)
+ {
+ const Identifier *identifier = id->asNameId();
+ QTC_ASSERT(identifier, return {});
+ return QString::fromUtf8(identifier->chars(), identifier->size());
+ }
+
+ const CppRefactoringFile *const m_file;
+ const Snapshot &m_snapshot;
+
+ const Name *m_namespace; // the name of the namespace that should be removed
+ const QString m_missingNamespace; // that should be added if a type was using the namespace
+ LookupContext m_context;
+ ChangeSet m_changeSet;
+ const int m_symbolPos; // the end position of the start symbol
+ bool m_done = false;
+ bool m_start = false;
+ // true if a using namespace was found at a scope and the scope should be left
+ bool m_foundNamespace = false;
+ bool m_removeAllAtGlobalScope;
+ // the scope where the using namespace that should be removed is valid
+ AST *m_parentNode = nullptr;
+ int m_namespaceScopeCounter = 0;
+};
+
+class RemoveUsingNamespaceOperation : public CppQuickFixOperation
+{
+ struct Node
+ {
+ Document::Ptr document;
+ bool hasGlobalUsingDirective = false;
+ int unprocessedParents;
+ std::vector<std::reference_wrapper<Node>> includes;
+ std::vector<std::reference_wrapper<Node>> includedBy;
+ Node() = default;
+ Node(const Node &) = delete;
+ Node(Node &&) = delete;
+ };
+
+public:
+ RemoveUsingNamespaceOperation(const CppQuickFixInterface &interface,
+ UsingDirectiveAST *usingDirective,
+ bool removeAllAtGlobalScope)
+ : CppQuickFixOperation(interface, 1)
+ , m_usingDirective(usingDirective)
+ , m_removeAllAtGlobalScope(removeAllAtGlobalScope)
+ {
+ const QString name = Overview{}.prettyName(usingDirective->name->name);
+ if (m_removeAllAtGlobalScope) {
+ setDescription(Tr::tr(
+ "Remove All Occurrences of \"using namespace %1\" in Global Scope "
+ "and Adjust Type Names Accordingly")
+ .arg(name));
+ } else {
+ setDescription(Tr::tr("Remove \"using namespace %1\" and "
+ "Adjust Type Names Accordingly")
+ .arg(name));
+ }
+ }
+
+private:
+ std::map<Utils::FilePath, Node> buildIncludeGraph(CppRefactoringChanges &refactoring)
+ {
+ using namespace ProjectExplorer;
+ using namespace Utils;
+
+ const Snapshot &s = refactoring.snapshot();
+ std::map<Utils::FilePath, Node> includeGraph;
+
+ auto handleFile = [&](const FilePath &filePath, Document::Ptr doc, auto shouldHandle) {
+ Node &node = includeGraph[filePath];
+ node.document = doc;
+ for (const Document::Include &include : doc->resolvedIncludes()) {
+ const FilePath filePath = include.resolvedFileName();
+ if (shouldHandle(filePath)) {
+ Node &includedNode = includeGraph[filePath];
+ includedNode.includedBy.push_back(node);
+ node.includes.push_back(includedNode);
+ }
+ }
+ };
+
+ 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) {
+ const Document::Ptr doc = s.document(file);
+ if (!doc)
+ continue;
+ handleFile(file, doc, [&](const FilePath &file) {
+ return projectFiles.contains(file);
+ });
+ }
+ } else {
+ for (auto i = s.begin(); i != s.end(); ++i) {
+ if (ProjectFile::classify(i.key().toString()) != ProjectFile::Unsupported) {
+ handleFile(i.key(), i.value(), [](const FilePath &file) {
+ return ProjectFile::classify(file.toString()) != ProjectFile::Unsupported;
+ });
+ }
+ }
+ }
+ for (auto &[_, node] : includeGraph) {
+ Q_UNUSED(_)
+ node.unprocessedParents = static_cast<int>(node.includes.size());
+ }
+ return includeGraph;
+ }
+
+ void removeAllUsingsAtGlobalScope(CppRefactoringChanges &refactoring)
+ {
+ auto includeGraph = buildIncludeGraph(refactoring);
+ std::vector<std::reference_wrapper<Node>> nodesWithProcessedParents;
+ for (auto &[_, node] : includeGraph) {
+ Q_UNUSED(_)
+ if (!node.unprocessedParents)
+ nodesWithProcessedParents.push_back(node);
+ }
+ while (!nodesWithProcessedParents.empty()) {
+ Node &node = nodesWithProcessedParents.back();
+ nodesWithProcessedParents.pop_back();
+ CppRefactoringFilePtr file = refactoring.cppFile(node.document->filePath());
+ const bool parentHasUsing = Utils::anyOf(node.includes, &Node::hasGlobalUsingDirective);
+ const int startPos = parentHasUsing
+ ? 0
+ : RemoveNamespaceVisitor::SearchGlobalUsingDirectivePos;
+ const bool noGlobalUsing = refactorFile(file, refactoring.snapshot(), startPos);
+ node.hasGlobalUsingDirective = !noGlobalUsing || parentHasUsing;
+
+ for (Node &subNode : node.includedBy) {
+ --subNode.unprocessedParents;
+ if (subNode.unprocessedParents == 0)
+ nodesWithProcessedParents.push_back(subNode);
+ }
+ }
+ }
+
+ void perform() override
+ {
+ CppRefactoringChanges refactoring(snapshot());
+ if (m_removeAllAtGlobalScope) {
+ removeAllUsingsAtGlobalScope(refactoring);
+ } else if (refactorFile(currentFile(),
+ refactoring.snapshot(),
+ currentFile()->endOf(m_usingDirective),
+ true)) {
+ processIncludes(refactoring, filePath());
+ }
+
+ for (auto &file : std::as_const(m_changes))
+ file->apply();
+ }
+
+ /**
+ * @brief refactorFile remove using namespace xyz in the given file and rewrite types
+ * @param file The file that should be processed
+ * @param snapshot The snapshot to work on
+ * @param startSymbol start processing after this index
+ * @param removeUsing if the using directive is in this file, remove it
+ * @return true if the using statement is global and there is no other global using namespace
+ */
+ bool refactorFile(const CppRefactoringFilePtr &file,
+ const Snapshot &snapshot,
+ int startSymbol,
+ bool removeUsing = false)
+ {
+ RemoveNamespaceVisitor visitor(file.get(),
+ snapshot,
+ m_usingDirective->name->name,
+ startSymbol,
+ m_removeAllAtGlobalScope);
+ visitor.accept(file->cppDocument()->translationUnit()->ast());
+ Utils::ChangeSet changes = visitor.getChanges();
+ if (removeUsing)
+ removeLine(file.get(), m_usingDirective, changes);
+ if (!changes.isEmpty()) {
+ file->setChangeSet(changes);
+ // apply changes at the end, otherwise the symbol finder will fail to resolve symbols if
+ // the using namespace is missing
+ m_changes.insert(file);
+ }
+ return visitor.isGlobalUsingNamespace() && !visitor.foundGlobalUsingNamespace();
+ }
+
+ void processIncludes(CppRefactoringChanges &refactoring, const FilePath &filePath)
+ {
+ QList<Snapshot::IncludeLocation>
+ includeLocationsOfDocument = refactoring.snapshot().includeLocationsOfDocument(filePath);
+ for (Snapshot::IncludeLocation &loc : includeLocationsOfDocument) {
+ if (!Utils::insert(m_processed, loc.first))
+ continue;
+
+ CppRefactoringFilePtr file = refactoring.cppFile(loc.first->filePath());
+ const bool noGlobalUsing = refactorFile(file,
+ refactoring.snapshot(),
+ file->position(loc.second, 1));
+ if (noGlobalUsing)
+ processIncludes(refactoring, loc.first->filePath());
+ }
+ }
+
+ QSet<Document::Ptr> m_processed;
+ QSet<CppRefactoringFilePtr> m_changes;
+
+ UsingDirectiveAST *m_usingDirective;
+ bool m_removeAllAtGlobalScope;
+};
+
+//! Removes a using directive (using namespace xyz).
+class RemoveUsingNamespace : public CppQuickFixFactory
+{
+public:
+ RemoveUsingNamespace() { setClangdReplacement({10}); }
+#ifdef WITH_TESTS
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const QList<AST *> &path = interface.path();
+ // We expect something like
+ // [0] TranslationUnitAST
+ // ...
+ // [] UsingDirectiveAST : if activated at 'using namespace'
+ // [] NameAST (optional): if activated at the name e.g. 'std'
+ int n = path.size() - 1;
+ if (n <= 0)
+ return;
+ if (path.last()->asName())
+ --n;
+ UsingDirectiveAST *usingDirective = path.at(n)->asUsingDirective();
+ if (usingDirective && usingDirective->name->name->asNameId()) {
+ result << new RemoveUsingNamespaceOperation(interface, usingDirective, false);
+ const bool isHeader = ProjectFile::isHeader(ProjectFile::classify(interface.filePath().toString()));
+ if (isHeader && path.at(n - 1)->asTranslationUnit()) // using namespace at global scope
+ result << new RemoveUsingNamespaceOperation(interface, usingDirective, true);
+ }
+ }
+};
+
+#ifdef WITH_TESTS
+using namespace Tests;
+class RemoveUsingNamespaceTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data()
+ {
+ QTest::addColumn<QByteArray>("header1");
+ QTest::addColumn<QByteArray>("header2");
+ QTest::addColumn<QByteArray>("header3");
+ QTest::addColumn<QByteArray>("expected1");
+ QTest::addColumn<QByteArray>("expected2");
+ QTest::addColumn<QByteArray>("expected3");
+ QTest::addColumn<int>("operation");
+
+ const QByteArray header1 = "namespace std{\n"
+ " template<typename T>\n"
+ " class vector{};\n"
+ " namespace chrono{\n"
+ " using seconds = int;\n"
+ " }\n"
+ "}\n"
+ "using namespace std;\n"
+ "namespace test{\n"
+ " class vector{\n"
+ " std::vector<int> ints;\n"
+ " };\n"
+ "}\n";
+ const QByteArray header2 = "#include \"header1.h\"\n"
+ "using foo = test::vector;\n"
+ "using namespace std;\n"
+ "using namespace test;\n"
+ "vector<int> others;\n";
+
+ const QByteArray header3 = "#include \"header2.h\"\n"
+ "using namespace std;\n"
+ "using namespace chrono;\n"
+ "namespace test{\n"
+ " vector vec;\n"
+ " seconds t;\n"
+ "}\n"
+ "void scope(){\n"
+ " for (;;) {\n"
+ " using namespace std;\n"
+ " vector<int> fori;\n"
+ " }\n"
+ " vector<int> no;\n"
+ " using namespace std;\n"
+ " vector<int> _no_change;\n"
+ "}\n"
+ "foo foos;\n";
+
+ QByteArray h3 = "#include \"header2.h\"\n"
+ "using namespace s@td;\n"
+ "using namespace chrono;\n"
+ "namespace test{\n"
+ " vector vec;\n"
+ " seconds t;\n"
+ "}\n"
+ "void scope(){\n"
+ " for (;;) {\n"
+ " using namespace std;\n"
+ " vector<int> fori;\n"
+ " }\n"
+ " vector<int> no;\n"
+ " using namespace std;\n"
+ " vector<int> _no_change;\n"
+ "}\n"
+ "foo foos;\n";
+
+ // like header1 but without "using namespace std;\n"
+ QByteArray expected1 = "namespace std{\n"
+ " template<typename T>\n"
+ " class vector{};\n"
+ " namespace chrono{\n"
+ " using seconds = int;\n"
+ " }\n"
+ "}\n"
+ "namespace test{\n"
+ " class vector{\n"
+ " std::vector<int> ints;\n"
+ " };\n"
+ "}\n";
+
+ // like header2 but without "using namespace std;\n" and with std::vector
+ QByteArray expected2 = "#include \"header1.h\"\n"
+ "using foo = test::vector;\n"
+ "using namespace test;\n"
+ "std::vector<int> others;\n";
+
+ QByteArray expected3 = "#include \"header2.h\"\n"
+ "using namespace std::chrono;\n"
+ "namespace test{\n"
+ " vector vec;\n"
+ " seconds t;\n"
+ "}\n"
+ "void scope(){\n"
+ " for (;;) {\n"
+ " using namespace std;\n"
+ " vector<int> fori;\n"
+ " }\n"
+ " std::vector<int> no;\n"
+ " using namespace std;\n"
+ " vector<int> _no_change;\n"
+ "}\n"
+ "foo foos;\n";
+
+ QTest::newRow("remove only in one file local")
+ << header1 << header2 << h3 << header1 << header2 << expected3 << 0;
+ QTest::newRow("remove only in one file globally")
+ << header1 << header2 << h3 << expected1 << expected2 << expected3 << 1;
+
+ QByteArray h2 = "#include \"header1.h\"\n"
+ "using foo = test::vector;\n"
+ "using namespace s@td;\n"
+ "using namespace test;\n"
+ "vector<int> others;\n";
+
+ QTest::newRow("remove across two files only this")
+ << header1 << h2 << header3 << header1 << expected2 << header3 << 0;
+ QTest::newRow("remove across two files globally1")
+ << header1 << h2 << header3 << expected1 << expected2 << expected3 << 1;
+
+ QByteArray h1 = "namespace std{\n"
+ " template<typename T>\n"
+ " class vector{};\n"
+ " namespace chrono{\n"
+ " using seconds = int;\n"
+ " }\n"
+ "}\n"
+ "using namespace s@td;\n"
+ "namespace test{\n"
+ " class vector{\n"
+ " std::vector<int> ints;\n"
+ " };\n"
+ "}\n";
+
+ QTest::newRow("remove across tree files only this")
+ << h1 << header2 << header3 << expected1 << header2 << header3 << 0;
+ QTest::newRow("remove across tree files globally")
+ << h1 << header2 << header3 << expected1 << expected2 << expected3 << 1;
+
+ expected3 = "#include \"header2.h\"\n"
+ "using namespace std::chrono;\n"
+ "namespace test{\n"
+ " vector vec;\n"
+ " seconds t;\n"
+ "}\n"
+ "void scope(){\n"
+ " for (;;) {\n"
+ " using namespace s@td;\n"
+ " vector<int> fori;\n"
+ " }\n"
+ " std::vector<int> no;\n"
+ " using namespace std;\n"
+ " vector<int> _no_change;\n"
+ "}\n"
+ "foo foos;\n";
+
+ QByteArray expected3_new = "#include \"header2.h\"\n"
+ "using namespace std::chrono;\n"
+ "namespace test{\n"
+ " vector vec;\n"
+ " seconds t;\n"
+ "}\n"
+ "void scope(){\n"
+ " for (;;) {\n"
+ " std::vector<int> fori;\n"
+ " }\n"
+ " std::vector<int> no;\n"
+ " using namespace std;\n"
+ " vector<int> _no_change;\n"
+ "}\n"
+ "foo foos;\n";
+
+ QTest::newRow("scoped remove")
+ << expected1 << expected2 << expected3 << expected1 << expected2 << expected3_new << 0;
+
+ h2 = "#include \"header1.h\"\n"
+ "using foo = test::vector;\n"
+ "using namespace std;\n"
+ "using namespace t@est;\n"
+ "vector<int> others;\n";
+ expected2 = "#include \"header1.h\"\n"
+ "using foo = test::vector;\n"
+ "using namespace std;\n"
+ "vector<int> others;\n";
+
+ QTest::newRow("existing namespace")
+ << header1 << h2 << header3 << header1 << expected2 << header3 << 1;
+
+ // test: remove using directive at global scope in every file
+ h1 = "using namespace tes@t;";
+ h2 = "using namespace test;";
+ h3 = "using namespace test;";
+
+ expected1 = expected2 = expected3 = "";
+ QTest::newRow("global scope remove in every file")
+ << h1 << h2 << h3 << expected1 << expected2 << expected3 << 1;
+
+ // test: dont print inline namespaces
+ h1 = R"--(
+namespace test {
+ inline namespace test {
+ class Foo{
+ void foo1();
+ void foo2();
+ };
+ inline int TEST = 42;
+ }
+}
+)--";
+ h2 = R"--(
+#include "header1.h"
+using namespace tes@t;
+)--";
+ h3 = R"--(
+#include "header2.h"
+Foo f1;
+test::Foo f2;
+using T1 = Foo;
+using T2 = test::Foo;
+int i1 = TEST;
+int i2 = test::TEST;
+void Foo::foo1(){};
+void test::Foo::foo2(){};
+)--";
+
+ expected1 = h1;
+ expected2 = R"--(
+#include "header1.h"
+)--";
+ expected3 = R"--(
+#include "header2.h"
+test::Foo f1;
+test::Foo f2;
+using T1 = test::Foo;
+using T2 = test::Foo;
+int i1 = test::TEST;
+int i2 = test::TEST;
+void test::Foo::foo1(){};
+void test::Foo::foo2(){};
+)--";
+ QTest::newRow("don't insert inline namespaces")
+ << h1 << h2 << h3 << expected1 << expected2 << expected3 << 0;
+ }
+
+ void test()
+ {
+ QFETCH(QByteArray, header1);
+ QFETCH(QByteArray, header2);
+ QFETCH(QByteArray, header3);
+ QFETCH(QByteArray, expected1);
+ QFETCH(QByteArray, expected2);
+ QFETCH(QByteArray, expected3);
+ QFETCH(int, operation);
+
+ QList<TestDocumentPtr> testDocuments;
+ testDocuments << CppTestDocument::create("header1.h", header1, expected1);
+ testDocuments << CppTestDocument::create("header2.h", header2, expected2);
+ testDocuments << CppTestDocument::create("header3.h", header3, expected3);
+
+ RemoveUsingNamespace factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), operation);
+ }
+
+ void testSimple_data()
+ {
+ QTest::addColumn<QByteArray>("header");
+ QTest::addColumn<QByteArray>("expected");
+
+ const QByteArray common = R"--(
+namespace N{
+ template<typename T>
+ struct vector{
+ using iterator = T*;
+ };
+ using int_vector = vector<int>;
+}
+)--";
+ const QByteArray header = common + R"--(
+using namespace N@;
+int_vector ints;
+int_vector::iterator intIter;
+using vec = vector<int>;
+vec::iterator it;
+)--";
+ const QByteArray expected = common + R"--(
+N::int_vector ints;
+N::int_vector::iterator intIter;
+using vec = N::vector<int>;
+vec::iterator it;
+)--";
+
+ QTest::newRow("nested typedefs with Namespace") << header << expected;
+ }
+
+ void testSimple()
+ {
+ QFETCH(QByteArray, header);
+ QFETCH(QByteArray, expected);
+
+ QList<TestDocumentPtr> testDocuments;
+ testDocuments << CppTestDocument::create("header.h", header, expected);
+
+ RemoveUsingNamespace factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths());
+ }
+
+ void testDifferentSymbols()
+ {
+ QByteArray header = "namespace test{\n"
+ " struct foo{\n"
+ " foo();\n"
+ " void bar();\n"
+ " };\n"
+ " void func();\n"
+ " enum E {E1, E2};\n"
+ " int bar;\n"
+ "}\n"
+ "using namespace t@est;\n"
+ "foo::foo(){}\n"
+ "void foo::bar(){}\n"
+ "void test(){\n"
+ " int i = bar * 4;\n"
+ " E val = E1;\n"
+ " auto p = &foo::bar;\n"
+ " func()\n"
+ "}\n";
+ QByteArray expected = "namespace test{\n"
+ " struct foo{\n"
+ " foo();\n"
+ " void bar();\n"
+ " };\n"
+ " void func();\n"
+ " enum E {E1, E2};\n"
+ " int bar;\n"
+ "}\n"
+ "test::foo::foo(){}\n"
+ "void test::foo::bar(){}\n"
+ "void test(){\n"
+ " int i = test::bar * 4;\n"
+ " test::E val = test::E1;\n"
+ " auto p = &test::foo::bar;\n"
+ " test::func()\n"
+ "}\n";
+
+ QList<TestDocumentPtr> testDocuments;
+ testDocuments << CppTestDocument::create("file.h", header, expected);
+ RemoveUsingNamespace factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 0);
+ }
+};
+
+QObject *RemoveUsingNamespace::createTest() { return new RemoveUsingNamespaceTest; }
+#endif // WITH_TESTS
+
+} // namespace
+
+void registerRemoveUsingNamespaceQuickfix()
+{
+ CppQuickFixFactory::registerFactory<RemoveUsingNamespace>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <removeusingnamespace.moc>
+#endif
diff --git a/src/plugins/cppeditor/quickfixes/removeusingnamespace.h b/src/plugins/cppeditor/quickfixes/removeusingnamespace.h
new file mode 100644
index 0000000000..149cbe4d09
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/removeusingnamespace.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerRemoveUsingNamespaceQuickfix();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/rewritecomment.cpp b/src/plugins/cppeditor/quickfixes/rewritecomment.cpp
new file mode 100644
index 0000000000..518705039b
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/rewritecomment.cpp
@@ -0,0 +1,873 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "rewritecomment.h"
+
+#include "../cppeditortr.h"
+#include "../cppeditorwidget.h"
+#include "../cpprefactoringchanges.h"
+#include "cppquickfix.h"
+
+#include <cplusplus/ASTPath.h>
+#include <cplusplus/declarationcomments.h>
+#include <projectexplorer/editorconfiguration.h>
+#include <texteditor/tabsettings.h>
+#include <texteditor/textdocument.h>
+
+#ifdef WITH_TESTS
+#include "cppquickfix_test.h"
+#include <QtTest>
+#endif
+
+using namespace CPlusPlus;
+using namespace TextEditor;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+class ConvertCommentStyleOp : public CppQuickFixOperation
+{
+public:
+ ConvertCommentStyleOp(const CppQuickFixInterface &interface, const QList<Token> &tokens,
+ Kind kind)
+ : CppQuickFixOperation(interface),
+ m_tokens(tokens),
+ m_kind(kind),
+ m_wasCxxStyle(m_kind == T_CPP_COMMENT || m_kind == T_CPP_DOXY_COMMENT),
+ m_isDoxygen(m_kind == T_DOXY_COMMENT || m_kind == T_CPP_DOXY_COMMENT)
+ {
+ setDescription(m_wasCxxStyle ? Tr::tr("Convert Comment to C-Style")
+ : Tr::tr("Convert Comment to C++-Style"));
+ }
+
+private:
+ // Turns every line of a C-style comment into a C++-style comment and vice versa.
+ // For C++ -> C, we use one /* */ comment block per line. However, doxygen
+ // requires a single comment, so there we just replace the prefix with whitespace and
+ // add the start and end comment in extra lines.
+ // For cosmetic reasons, we offer some convenience functionality:
+ // - Turn /***** ... into ////// ... and vice versa
+ // - With C -> C++, remove leading asterisks.
+ // - With C -> C++, remove the first and last line of a block if they have no content
+ // other than the comment start and end characters.
+ // - With C++ -> C, try to align the end comment characters.
+ // These are obviously heuristics; we do not guarantee perfect results for everybody.
+ // We also don't second-guess the users's selection: E.g. if there is an empty
+ // line between the tokens, then it's not the same doxygen comment, but we merge
+ // it anyway in C++ to C mode.
+ void perform() override
+ {
+ TranslationUnit * const tu = currentFile()->cppDocument()->translationUnit();
+ const QString newCommentStart = getNewCommentStart();
+ ChangeSet changeSet;
+ int endCommentColumn = -1;
+ const QChar oldFillChar = m_wasCxxStyle ? '/' : '*';
+ const QChar newFillChar = m_wasCxxStyle ? '*' : '/';
+
+ for (const Token &token : m_tokens) {
+ const int startPos = tu->getTokenPositionInDocument(token, textDocument());
+ const int endPos = tu->getTokenEndPositionInDocument(token, textDocument());
+
+ if (m_wasCxxStyle && m_isDoxygen) {
+ // Replace "///" characters with whitespace (to keep alignment).
+ // The insertion of "/*" and "*/" is done once after the loop.
+ changeSet.replace(startPos, startPos + 3, " ");
+ continue;
+ }
+
+ const QTextBlock firstBlock = textDocument()->findBlock(startPos);
+ const QTextBlock lastBlock = textDocument()->findBlock(endPos);
+ for (QTextBlock block = firstBlock; block.isValid() && block.position() <= endPos;
+ block = block.next()) {
+ const QString &blockText = block.text();
+ const int firstColumn = block == firstBlock ? startPos - block.position() : 0;
+ const int endColumn = block == lastBlock ? endPos - block.position()
+ : block.length();
+
+ // Returns true if the current line looks like "/********/" or "//////////",
+ // as is often the case at the start and end of comment blocks.
+ const auto fillChecker = [&] {
+ if (m_isDoxygen)
+ return false;
+ QString textToCheck = blockText;
+ if (block == firstBlock)
+ textToCheck.remove(0, 1);
+ if (block == lastBlock)
+ textToCheck.chop(block.length() - endColumn);
+ return Utils::allOf(textToCheck, [oldFillChar](const QChar &c)
+ { return c == oldFillChar || c == ' ';
+ }) && textToCheck.count(oldFillChar) > 2;
+ };
+
+ // Returns the index of the first character of actual comment content,
+ // as opposed to visual stuff like slashes, stars or whitespace.
+ const auto indexOfActualContent = [&] {
+ const int offset = block == firstBlock ? firstColumn + newCommentStart.length()
+ : firstColumn;
+
+ for (int i = offset, lastFillChar = -1; i < blockText.length(); ++i) {
+ if (blockText.at(i) == oldFillChar) {
+ lastFillChar = i;
+ continue;
+ }
+ if (!blockText.at(i).isSpace())
+ return lastFillChar + 1;
+ }
+ return -1;
+ };
+
+ if (fillChecker()) {
+ const QString replacement = QString(endColumn - 1 - firstColumn, newFillChar);
+ changeSet.replace(block.position() + firstColumn,
+ block.position() + endColumn - 1,
+ replacement);
+ if (m_wasCxxStyle) {
+ changeSet.replace(block.position() + firstColumn,
+ block.position() + firstColumn + 1, "/");
+ changeSet.insert(block.position() + endColumn - 1, "*");
+ endCommentColumn = endColumn - 1;
+ }
+ continue;
+ }
+
+ // Remove leading noise or even the entire block, if applicable.
+ const bool blockIsRemovable = (block == firstBlock || block == lastBlock)
+ && firstBlock != lastBlock;
+ const auto removeBlock = [&] {
+ changeSet.remove(block.position() + firstColumn, block.position() + endColumn);
+ };
+ const int contentIndex = indexOfActualContent();
+ int removed = 0;
+ if (contentIndex == -1) {
+ if (blockIsRemovable) {
+ removeBlock();
+ continue;
+ } else if (!m_wasCxxStyle) {
+ changeSet.replace(block.position() + firstColumn,
+ block.position() + endColumn - 1, newCommentStart);
+ continue;
+ }
+ } else if (block == lastBlock && contentIndex == endColumn - 1) {
+ if (blockIsRemovable) {
+ removeBlock();
+ break;
+ }
+ } else {
+ changeSet.remove(block.position() + firstColumn,
+ block.position() + firstColumn + contentIndex);
+ removed = contentIndex;
+ }
+
+ if (block == firstBlock) {
+ changeSet.replace(startPos, startPos + newCommentStart.length(),
+ newCommentStart);
+ } else {
+ // If the line starts with enough whitespace, replace it with the
+ // comment start characters, so we don't move the content to the right
+ // unnecessarily. Otherwise, insert the comment start characters.
+ if (blockText.startsWith(QString(newCommentStart.size() + removed + 1, ' '))) {
+ changeSet.replace(block.position(),
+ block.position() + newCommentStart.length(),
+ newCommentStart);
+ } else {
+ changeSet.insert(block.position(), newCommentStart);
+ }
+ }
+
+ if (block == lastBlock) {
+ if (m_wasCxxStyle) {
+ // This is for proper alignment of the end comment character.
+ if (endCommentColumn != -1) {
+ const int endCommentPos = block.position() + endCommentColumn;
+ if (endPos < endCommentPos)
+ changeSet.insert(endPos, QString(endCommentPos - endPos - 1, ' '));
+ }
+ changeSet.insert(endPos, " */");
+ } else {
+ changeSet.remove(endPos - 2, endPos);
+ }
+ }
+ }
+ }
+
+ if (m_wasCxxStyle && m_isDoxygen) {
+ const int startPos = tu->getTokenPositionInDocument(m_tokens.first(), textDocument());
+ const int endPos = tu->getTokenEndPositionInDocument(m_tokens.last(), textDocument());
+ changeSet.insert(startPos, "/*!\n");
+ changeSet.insert(endPos, "\n*/");
+ }
+
+ changeSet.apply(textDocument());
+ }
+
+ QString getNewCommentStart() const
+ {
+ if (m_wasCxxStyle) {
+ if (m_isDoxygen)
+ return "/*!";
+ return "/*";
+ }
+ if (m_isDoxygen)
+ return "//!";
+ return "//";
+ }
+
+ const QList<Token> m_tokens;
+ const Kind m_kind;
+ const bool m_wasCxxStyle;
+ const bool m_isDoxygen;
+};
+
+class MoveFunctionCommentsOp : public CppQuickFixOperation
+{
+public:
+ enum class Direction { ToDecl, ToDef };
+ MoveFunctionCommentsOp(const CppQuickFixInterface &interface, const Symbol *symbol,
+ const QList<Token> &commentTokens, Direction direction)
+ : CppQuickFixOperation(interface), m_symbol(symbol), m_commentTokens(commentTokens)
+ {
+ setDescription(direction == Direction::ToDecl
+ ? Tr::tr("Move Function Documentation to Declaration")
+ : Tr::tr("Move Function Documentation to Definition"));
+ }
+
+private:
+ void perform() override
+ {
+ const auto textDoc = const_cast<QTextDocument *>(currentFile()->document());
+ const int pos = currentFile()->cppDocument()->translationUnit()->getTokenPositionInDocument(
+ m_symbol->sourceLocation(), textDoc);
+ QTextCursor cursor(textDoc);
+ cursor.setPosition(pos);
+ const CursorInEditor cursorInEditor(cursor, currentFile()->filePath(), editor(),
+ editor()->textDocument());
+ const auto callback = [symbolLoc = m_symbol->toLink(), comments = m_commentTokens]
+ (const Link &link) {
+ moveComments(link, symbolLoc, comments);
+ };
+ CppModelManager::followSymbol(cursorInEditor, callback, true, false,
+ FollowSymbolMode::Exact);
+ }
+
+ static void moveComments(const Link &targetLoc, const Link &symbolLoc,
+ const QList<Token> &comments)
+ {
+ if (!targetLoc.hasValidTarget() || targetLoc.hasSameLocation(symbolLoc))
+ return;
+
+ CppRefactoringChanges changes(CppModelManager::snapshot());
+ const CppRefactoringFilePtr sourceFile = changes.cppFile(symbolLoc.targetFilePath);
+ const CppRefactoringFilePtr targetFile
+ = targetLoc.targetFilePath == symbolLoc.targetFilePath
+ ? sourceFile
+ : changes.cppFile(targetLoc.targetFilePath);
+ const Document::Ptr &targetCppDoc = targetFile->cppDocument();
+ const QList<AST *> targetAstPath = ASTPath(targetCppDoc)(
+ targetLoc.targetLine, targetLoc.targetColumn + 1);
+ if (targetAstPath.isEmpty())
+ return;
+ const AST *targetDeclAst = nullptr;
+ for (auto it = std::next(std::rbegin(targetAstPath));
+ it != std::rend(targetAstPath); ++it) {
+ AST * const node = *it;
+ if (node->asDeclaration()) {
+ targetDeclAst = node;
+ continue;
+ }
+ if (targetDeclAst)
+ break;
+ }
+ if (!targetDeclAst)
+ return;
+ const int insertionPos = targetCppDoc->translationUnit()->getTokenPositionInDocument(
+ targetDeclAst->firstToken(), targetFile->document());
+ const TranslationUnit * const sourceTu = sourceFile->cppDocument()->translationUnit();
+ const int sourceCommentStartPos = sourceTu->getTokenPositionInDocument(
+ comments.first(), sourceFile->document());
+ const int sourceCommentEndPos = sourceTu->getTokenEndPositionInDocument(
+ comments.last(), sourceFile->document());
+
+ // Manually adjust indentation, as both our built-in indenter and ClangFormat
+ // are unreliable with regards to comment continuation lines.
+ auto tabSettings = [](CppRefactoringFilePtr file) {
+ if (auto editor = file->editor())
+ return editor->textDocument()->tabSettings();
+ return ProjectExplorer::actualTabSettings(file->filePath(), nullptr);
+ };
+ const TabSettings &sts = tabSettings(sourceFile);
+ const TabSettings &tts = tabSettings(targetFile);
+ const QTextBlock insertionBlock = targetFile->document()->findBlock(insertionPos);
+ const int insertionColumn = tts.columnAt(insertionBlock.text(),
+ insertionPos - insertionBlock.position());
+ const QTextBlock removalBlock = sourceFile->document()->findBlock(sourceCommentStartPos);
+ const QTextBlock removalBlockEnd = sourceFile->document()->findBlock(sourceCommentEndPos);
+ const int removalColumn = sts.columnAt(removalBlock.text(),
+ sourceCommentStartPos - removalBlock.position());
+ const int columnOffset = insertionColumn - removalColumn;
+ QString functionDoc;
+ if (columnOffset != 0) {
+ for (QTextBlock block = removalBlock;
+ block.isValid() && block != removalBlockEnd.next();
+ block = block.next()) {
+ QString text = block.text() + QChar::ParagraphSeparator;
+ if (block == removalBlockEnd)
+ text = text.left(sourceCommentEndPos - block.position());
+ if (block == removalBlock) {
+ text = text.mid(sourceCommentStartPos - block.position());
+ } else {
+ int lineIndentColumn = sts.indentationColumn(text) + columnOffset;
+ text.replace(0,
+ TabSettings::firstNonSpace(text),
+ tts.indentationString(0, lineIndentColumn, 0, insertionBlock));
+ }
+ functionDoc += text;
+ }
+ } else {
+ functionDoc = sourceFile->textOf(sourceCommentStartPos, sourceCommentEndPos);
+ }
+
+ // Remove comment plus leading and trailing whitespace, including trailing newline.
+ const auto removeAtSource = [&](ChangeSet &changeSet) {
+ int removalPos = sourceCommentStartPos;
+ const QChar newline(QChar::ParagraphSeparator);
+ while (true) {
+ const int prev = removalPos - 1;
+ if (prev < 0)
+ break;
+ const QChar prevChar = sourceFile->charAt(prev);
+ if (!prevChar.isSpace() || prevChar == newline)
+ break;
+ removalPos = prev;
+ }
+ int removalEndPos = sourceCommentEndPos;
+ while (true) {
+ if (removalEndPos == sourceFile->document()->characterCount())
+ break;
+ const QChar nextChar = sourceFile->charAt(removalEndPos);
+ if (!nextChar.isSpace())
+ break;
+ ++removalEndPos;
+ if (nextChar == newline)
+ break;
+ }
+ changeSet.remove(removalPos, removalEndPos);
+ };
+
+ ChangeSet targetChangeSet;
+ targetChangeSet.insert(insertionPos, functionDoc);
+ targetChangeSet.insert(insertionPos, "\n");
+ targetChangeSet.insert(insertionPos, QString(insertionColumn, ' '));
+ if (targetFile == sourceFile)
+ removeAtSource(targetChangeSet);
+ const bool targetFileSuccess = targetFile->apply(targetChangeSet);
+ if (targetFile == sourceFile || !targetFileSuccess)
+ return;
+ ChangeSet sourceChangeSet;
+ removeAtSource(sourceChangeSet);
+ sourceFile->apply(sourceChangeSet);
+ }
+
+ const Symbol * const m_symbol;
+ const QList<Token> m_commentTokens;
+};
+
+//! Converts C-style to C++-style comments and vice versa
+class ConvertCommentStyle : public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject* createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface,
+ TextEditor::QuickFixOperations &result) override
+ {
+ // If there's a selection, then it must entirely consist of comment tokens.
+ // If there's no selection, the cursor must be on a comment.
+ const QList<Token> &cursorTokens = interface.currentFile()->tokensForCursor();
+ if (cursorTokens.empty())
+ return;
+ if (!cursorTokens.front().isComment())
+ return;
+
+ // All tokens must be the same kind of comment, but we make an exception for doxygen comments
+ // that start with "///", as these are often not intended to be doxygen. For our purposes,
+ // we treat them as normal comments.
+ const auto effectiveKind = [&interface](const Token &token) {
+ if (token.kind() != T_CPP_DOXY_COMMENT)
+ return token.kind();
+ TranslationUnit * const tu = interface.currentFile()->cppDocument()->translationUnit();
+ const int startPos = tu->getTokenPositionInDocument(token, interface.textDocument());
+ const QString commentStart = interface.textAt(startPos, 3);
+ return commentStart == "///" ? T_CPP_COMMENT : T_CPP_DOXY_COMMENT;
+ };
+ const Kind kind = effectiveKind(cursorTokens.first());
+ for (int i = 1; i < cursorTokens.count(); ++i) {
+ if (effectiveKind(cursorTokens.at(i)) != kind)
+ return;
+ }
+
+ // Ok, all tokens are of same(ish) comment type, offer quickfix.
+ result << new ConvertCommentStyleOp(interface, cursorTokens, kind);
+ }
+};
+
+//! Moves function documentation between declaration and implementation.
+class MoveFunctionComments : public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject* createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface,
+ TextEditor::QuickFixOperations &result) override
+ {
+ const QList<AST *> &astPath = interface.path();
+ if (astPath.isEmpty())
+ return;
+ const Symbol *symbol = nullptr;
+ MoveFunctionCommentsOp::Direction direction = MoveFunctionCommentsOp::Direction::ToDecl;
+ for (auto it = std::next(std::rbegin(astPath)); it != std::rend(astPath); ++it) {
+ if (const auto func = (*it)->asFunctionDefinition()) {
+ symbol = func->symbol;
+ direction = MoveFunctionCommentsOp::Direction::ToDecl;
+ break;
+ }
+ const auto decl = (*it)->asSimpleDeclaration();
+ if (!decl || !decl->declarator_list)
+ continue;
+ for (auto it = decl->declarator_list->begin();
+ !symbol && it != decl->declarator_list->end(); ++it) {
+ PostfixDeclaratorListAST * const funcDecls = (*it)->postfix_declarator_list;
+ if (!funcDecls)
+ continue;
+ for (auto it = funcDecls->begin(); it != funcDecls->end(); ++it) {
+ if (const auto func = (*it)->asFunctionDeclarator()) {
+ symbol = func->symbol;
+ direction = MoveFunctionCommentsOp::Direction::ToDef;
+ break;
+ }
+ }
+ }
+
+ }
+ if (!symbol)
+ return;
+
+ if (const QList<Token> commentTokens = commentsForDeclaration(
+ symbol, *interface.textDocument(), interface.currentFile()->cppDocument());
+ !commentTokens.isEmpty()) {
+ result << new MoveFunctionCommentsOp(interface, symbol, commentTokens, direction);
+ }
+ }
+};
+
+#ifdef WITH_TESTS
+using namespace Tests;
+
+class ConvertCommentStyleTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data()
+ {
+ QTest::addColumn<QString>("input");
+ QTest::addColumn<QString>("expectedOutput");
+
+ QTest::newRow("C -> C++ / no selection / single line") << R"(
+int var1;
+/* Other comment, unaffected */
+/* Our @comment */
+/* Another unaffected comment */
+int var2;)" << R"(
+int var1;
+/* Other comment, unaffected */
+// Our comment
+/* Another unaffected comment */
+int var2;)";
+
+ QTest::newRow("C -> C++ / no selection / multi-line / preserved header and footer") << R"(
+/****************************************************
+ * some info
+ * more @info
+ ***************************************************/)" << R"(
+/////////////////////////////////////////////////////
+// some info
+// more info
+/////////////////////////////////////////////////////)";
+
+ QTest::newRow("C -> C++ / no selection / multi-line / non-preserved header and footer") << R"(
+/*
+ * some info
+ * more @info
+ */)" << R"(
+// some info
+// more info
+)";
+
+ QTest::newRow("C -> C++ / no selection / qdoc") << R"(
+/*!
+ \qmlproperty string Type::element.name
+ \qmlproperty int Type::element.id
+
+ \brief Holds the @element name and id.
+*/)" << R"(
+//! \qmlproperty string Type::element.name
+//! \qmlproperty @int Type::element.id
+//!
+//! \brief Holds the element name and id.
+)";
+
+ QTest::newRow("C -> C++ / no selection / doxygen") << R"(
+/*! \class Test
+ \brief A test class.
+
+ A more detailed @class description.
+*/)" << R"(
+//! \class Test
+//! \brief A test class.
+//!
+//! A more detailed class description.
+)";
+
+ QTest::newRow("C -> C++ / selection / single line") << R"(
+int var1;
+/* Other comment, unaffected */
+@{start}/* Our comment */@{end}
+/* Another unaffected comment */
+int var2;)" << R"(
+int var1;
+/* Other comment, unaffected */
+// Our comment
+/* Another unaffected comment */
+int var2;)";
+
+ QTest::newRow("C -> C++ / selection / multi-line / preserved header and footer") << R"(
+/****************************************************
+ * @{start}some info
+ * more info@{end}
+ ***************************************************/)" << R"(
+/////////////////////////////////////////////////////
+// some info
+// more info
+/////////////////////////////////////////////////////)";
+
+ QTest::newRow("C -> C++ / selection / multi-line / non-preserved header and footer") << R"(
+/*@{start}
+ * some in@{end}fo
+ * more info
+ */)" << R"(
+// some info
+// more info
+)";
+
+ QTest::newRow("C -> C++ / selection / qdoc") << R"(
+/*!@{start}
+ \qmlproperty string Type::element.name
+ \qmlproperty int Type::element.id
+
+ \brief Holds the element name and id.
+*/@{end})" << R"(
+//! \qmlproperty string Type::element.name
+//! \qmlproperty int Type::element.id
+//!
+//! \brief Holds the element name and id.
+)";
+
+ QTest::newRow("C -> C++ / selection / doxygen") << R"(
+/** Expand envi@{start}ronment variables in a string.
+ *
+ * Environment variables are accepted in the @{end}following forms:
+ * $SOMEVAR, ${SOMEVAR} on Unix and %SOMEVAR% on Windows.
+ * No escapes and quoting are supported.
+ * If a variable is not found, it is not substituted.
+ */)" << R"(
+//! Expand environment variables in a string.
+//!
+//! Environment variables are accepted in the following forms:
+//! $SOMEVAR, ${SOMEVAR} on Unix and %SOMEVAR% on Windows.
+//! No escapes and quoting are supported.
+//! If a variable is not found, it is not substituted.
+)";
+
+ QTest::newRow("C -> C++ / selection / multiple comments") << R"(
+@{start}/* Affected comment */
+/* Another affected comment */
+/* A third affected comment */@{end}
+/* An unaffected comment */)" << R"(
+// Affected comment
+// Another affected comment
+// A third affected comment
+/* An unaffected comment */)";
+
+ // FIXME: Remove adjacent newline along with last block
+ // FIXME: Use CppRefactoringFile to auto-indent continuation lines?
+ QTest::newRow("C -> C++, indented") << R"(
+struct S {
+ /*
+ * @This is an
+ * indented comment.
+ */
+ void func();
+)" << R"(
+struct S {
+ // This is an
+// indented comment.
+
+ void func();
+)";
+
+ QTest::newRow("C++ -> C / no selection / single line") << R"(
+// Other comment, unaffected
+// Our @comment
+// Another unaffected comment)" << R"(
+// Other comment, unaffected
+/* Our comment */
+// Another unaffected comment)";
+
+ QTest::newRow("C++ -> C / selection / single line") << R"(
+// Other comment, unaffected
+@{start}// Our comment@{end}
+// Another unaffected comment)" << R"(
+// Other comment, unaffected
+/* Our comment */
+// Another unaffected comment)";
+
+ QTest::newRow("C++ -> C / selection / multi-line / preserved header and footer") << R"(
+@{start}/////////////////////////////////////////////////////
+// some info
+// more info
+/////////////////////////////////////////////////////@{end})" << R"(
+/****************************************************/
+/* some info */
+/* more info */
+/****************************************************/)";
+
+ QTest::newRow("C++ -> C / selection / qdoc") << R"(
+@{start}//! \qmlproperty string Type::element.name
+//! \qmlproperty int Type::element.id
+//!
+//! \brief Holds the element name and id.@{end}
+)" << R"(
+/*!
+ \qmlproperty string Type::element.name
+ \qmlproperty int Type::element.id
+
+ \brief Holds the element name and id.
+*/
+)";
+
+ QTest::newRow("C++ -> C / selection / doxygen") << R"(
+@{start}//! \class Test
+//! \brief A test class.
+//!
+//! A more detailed class description.@{end}
+)" << R"(
+/*!
+ \class Test
+ \brief A test class.
+
+ A more detailed class description.
+*/
+)";
+ }
+
+ void test()
+ {
+ QFETCH(QString, input);
+ QFETCH(QString, expectedOutput);
+
+ ConvertCommentStyle factory;
+ QuickFixOperationTest(
+ {CppTestDocument::create("file.h", input.toUtf8(), expectedOutput.toUtf8())},
+ &factory);
+ }
+};
+
+class MoveFunctionCommentsTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data()
+ {
+ QTest::addColumn<QByteArrayList>("headers");
+ QTest::addColumn<QByteArrayList>("sources");
+
+ const QByteArrayList headersFuncDecl2Def{R"(
+// Function comment
+void @aFunction();
+)", R"(
+void aFunction();
+)"};
+ const QByteArrayList sourcesFuncDecl2Def{R"(
+#include "file.h"
+
+void aFunction() {}
+)", R"(
+#include "file.h"
+
+// Function comment
+void aFunction() {}
+)"};
+ QTest::newRow("function: from decl to def") << headersFuncDecl2Def << sourcesFuncDecl2Def;
+
+ const QByteArrayList headersFuncDef2Decl{R"(
+void aFunction();
+)", R"(
+/* function */
+/* comment */
+void aFunction();
+)"};
+ const QByteArrayList sourcesFuncDef2Decl{R"(
+#include "file.h"
+
+/* function */
+/* comment */
+void a@Function() {}
+)", R"(
+#include "file.h"
+
+void aFunction() {}
+)"};
+ QTest::newRow("function: from def to decl") << headersFuncDef2Decl << sourcesFuncDef2Decl;
+
+ const QByteArrayList headersFuncNoDef{R"(
+// Function comment
+void @aFunction();
+)", R"(
+// Function comment
+void aFunction();
+)"};
+ QTest::newRow("function: no def") << headersFuncNoDef << QByteArrayList();
+
+ const QByteArrayList headersFuncNoDecl{R"(
+// Function comment
+inline void @aFunction() {}
+)", R"(
+// Function comment
+inline void aFunction() {}
+)"};
+ QTest::newRow("function: no decl") << headersFuncNoDecl << QByteArrayList();
+
+ const QByteArrayList headersFuncTemplateDecl2Def{R"(
+// Function comment
+template<typename T> T @aFunction();
+
+template<typename T> inline T aFunction() { return T(); }
+)", R"(
+template<typename T> T aFunction();
+
+// Function comment
+template<typename T> inline T aFunction() { return T(); }
+)"};
+ QTest::newRow("function template: from decl to def") << headersFuncTemplateDecl2Def
+ << QByteArrayList();
+
+ const QByteArrayList headersFuncTemplateDef2Decl{R"(
+template<typename T> T aFunction();
+
+// Function comment
+template<typename T> inline T @aFunction() { return T(); }
+)", R"(
+// Function comment
+template<typename T> T aFunction();
+
+template<typename T> inline T aFunction() { return T(); }
+)"};
+ QTest::newRow("function template: from def to decl") << headersFuncTemplateDef2Decl
+ << QByteArrayList();
+
+ const QByteArrayList headersMemberDecl2Def{R"(
+class C {
+ /**
+ * \brief Foo::aMember
+ */
+ void @aMember();
+)", R"(
+class C {
+ void aMember();
+)"};
+ const QByteArrayList sourcesMemberDecl2Def{R"(
+#include "file.h"
+
+void C::aMember() {}
+)", R"(
+#include "file.h"
+
+/**
+ * \brief Foo::aMember
+ */
+void C::aMember() {}
+)"};
+ QTest::newRow("member function: from decl to def") << headersMemberDecl2Def
+ << sourcesMemberDecl2Def;
+
+ const QByteArrayList headersMemberDef2Decl{R"(
+class C {
+ void aMember();
+)", R"(
+class C {
+ /**
+ * \brief Foo::aMember
+ */
+ void aMember();
+)"};
+ const QByteArrayList sourcesMemberDef2Decl{R"(
+#include "file.h"
+
+/**
+ * \brief Foo::aMember
+ */
+void C::aMember() {@}
+)", R"(
+#include "file.h"
+
+void C::aMember() {}
+)"};
+ QTest::newRow("member function: from def to decl") << headersMemberDef2Decl
+ << sourcesMemberDef2Decl;
+ }
+
+ void test()
+ {
+ QFETCH(QByteArrayList, headers);
+ QFETCH(QByteArrayList, sources);
+
+ QList<TestDocumentPtr> documents;
+ QCOMPARE(headers.size(), 2);
+ documents << CppTestDocument::create("file.h", headers.at(0), headers.at(1));
+ if (!sources.isEmpty()) {
+ QCOMPARE(sources.size(), 2);
+ documents << CppTestDocument::create("file.cpp", sources.at(0), sources.at(1));
+ }
+ MoveFunctionComments factory;
+ QByteArray failMessage;
+ if (QByteArray(QTest::currentDataTag()) == "function template: from def to decl")
+ failMessage = "decl/def switch doesn't work for templates";
+ QuickFixOperationTest(documents, &factory, {}, {}, failMessage);
+ }
+};
+
+QObject * ConvertCommentStyle::createTest() { return new ConvertCommentStyleTest; }
+QObject * MoveFunctionComments::createTest() { return new MoveFunctionCommentsTest; }
+
+#endif
+} // namespace
+
+void registerRewriteCommentQuickfixes()
+{
+ CppQuickFixFactory::registerFactory<ConvertCommentStyle>();
+ CppQuickFixFactory::registerFactory<MoveFunctionComments>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <rewritecomment.moc>
+#endif
diff --git a/src/plugins/cppeditor/quickfixes/rewritecomment.h b/src/plugins/cppeditor/quickfixes/rewritecomment.h
new file mode 100644
index 0000000000..50ad0af415
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/rewritecomment.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerRewriteCommentQuickfixes();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/rewritecontrolstatements.cpp b/src/plugins/cppeditor/quickfixes/rewritecontrolstatements.cpp
new file mode 100644
index 0000000000..2b9c2392df
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/rewritecontrolstatements.cpp
@@ -0,0 +1,1323 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "rewritecontrolstatements.h"
+
+#include "../cppcodestylesettings.h"
+#include "../cppeditortr.h"
+#include "../cppeditorwidget.h"
+#include "../cpprefactoringchanges.h"
+#include "cppquickfix.h"
+
+#include <cplusplus/Overview.h>
+#include <cplusplus/TypeOfExpression.h>
+
+#ifdef WITH_TESTS
+#include "cppquickfix_test.h"
+#include <QTest>
+#endif
+
+using namespace CPlusPlus;
+using namespace TextEditor;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+template<typename Statement> Statement *asControlStatement(AST *node)
+{
+ if constexpr (std::is_same_v<Statement, IfStatementAST>)
+ return node->asIfStatement();
+ if constexpr (std::is_same_v<Statement, WhileStatementAST>)
+ return node->asWhileStatement();
+ if constexpr (std::is_same_v<Statement, ForStatementAST>)
+ return node->asForStatement();
+ if constexpr (std::is_same_v<Statement, RangeBasedForStatementAST>)
+ return node->asRangeBasedForStatement();
+ if constexpr (std::is_same_v<Statement, DoStatementAST>)
+ return node->asDoStatement();
+ return nullptr;
+}
+
+template<typename Statement>
+int triggerToken(const Statement *statement)
+{
+ if constexpr (std::is_same_v<Statement, IfStatementAST>)
+ return statement->if_token;
+ if constexpr (std::is_same_v<Statement, WhileStatementAST>)
+ return statement->while_token;
+ if constexpr (std::is_same_v<Statement, DoStatementAST>)
+ return statement->do_token;
+ if constexpr (std::is_same_v<Statement, ForStatementAST>
+ || std::is_same_v<Statement, RangeBasedForStatementAST>) {
+ return statement->for_token;
+ }
+}
+
+template<typename Statement>
+int tokenToInsertOpeningBraceAfter(const Statement *statement)
+{
+ if constexpr (std::is_same_v<Statement, DoStatementAST>)
+ return statement->do_token;
+ return statement->rparen_token;
+}
+
+template<typename Statement> class AddBracesToControlStatementOp : public CppQuickFixOperation
+{
+public:
+ AddBracesToControlStatementOp(const CppQuickFixInterface &interface,
+ const QList<Statement *> &statements,
+ StatementAST *elseStatement,
+ int elseToken)
+ : CppQuickFixOperation(interface, 0)
+ , m_statements(statements), m_elseStatement(elseStatement), m_elseToken(elseToken)
+ {
+ setDescription(Tr::tr("Add Curly Braces"));
+ }
+
+ void perform() override
+ {
+ ChangeSet changes;
+ for (Statement * const statement : m_statements) {
+ const int start = currentFile()->endOf(tokenToInsertOpeningBraceAfter(statement));
+ changes.insert(start, QLatin1String(" {"));
+ if constexpr (std::is_same_v<Statement, DoStatementAST>) {
+ const int end = currentFile()->startOf(statement->while_token);
+ changes.insert(end, QLatin1String("} "));
+ } else if constexpr (std::is_same_v<Statement, IfStatementAST>) {
+ if (statement->else_statement) {
+ changes.insert(currentFile()->startOf(statement->else_token), "} ");
+ } else {
+ changes.insert(currentFile()->endOf(statement->statement->lastToken() - 1),
+ "\n}");
+ }
+
+ } else {
+ const int end = currentFile()->endOf(statement->statement->lastToken() - 1);
+ changes.insert(end, QLatin1String("\n}"));
+ }
+ }
+ if (m_elseStatement) {
+ changes.insert(currentFile()->endOf(m_elseToken), " {");
+ changes.insert(currentFile()->endOf(m_elseStatement->lastToken() - 1), "\n}");
+ }
+
+ currentFile()->setChangeSet(changes);
+ currentFile()->apply();
+ }
+
+private:
+ const QList<Statement *> m_statements;
+ StatementAST * const m_elseStatement;
+ const int m_elseToken;
+};
+
+template<typename Statement>
+bool checkControlStatementsHelper(const CppQuickFixInterface &interface, QuickFixOperations &result)
+{
+ Statement * const statement = asControlStatement<Statement>(interface.path().last());
+ if (!statement)
+ return false;
+
+ QList<Statement *> statements;
+ if (interface.isCursorOn(triggerToken(statement)) && statement->statement
+ && !statement->statement->asCompoundStatement()) {
+ statements << statement;
+ }
+
+ StatementAST *elseStmt = nullptr;
+ int elseToken = 0;
+ if constexpr (std::is_same_v<Statement, IfStatementAST>) {
+ IfStatementAST *currentIfStmt = statement;
+ for (elseStmt = currentIfStmt->else_statement, elseToken = currentIfStmt->else_token;
+ elseStmt && (currentIfStmt = elseStmt->asIfStatement());
+ elseStmt = currentIfStmt->else_statement, elseToken = currentIfStmt->else_token) {
+ if (currentIfStmt->statement && !currentIfStmt->statement->asCompoundStatement())
+ statements << currentIfStmt;
+ }
+ if (elseStmt && (elseStmt->asIfStatement() || elseStmt->asCompoundStatement())) {
+ elseStmt = nullptr;
+ elseToken = 0;
+ }
+ }
+
+ if (!statements.isEmpty() || elseStmt)
+ result << new AddBracesToControlStatementOp(interface, statements, elseStmt, elseToken);
+ return true;
+}
+
+template<typename ...Statements>
+void checkControlStatements(const CppQuickFixInterface &interface, QuickFixOperations &result)
+{
+ (... || checkControlStatementsHelper<Statements>(interface, result));
+}
+
+class MoveDeclarationOutOfIfOp: public CppQuickFixOperation
+{
+public:
+ MoveDeclarationOutOfIfOp(const CppQuickFixInterface &interface)
+ : CppQuickFixOperation(interface)
+ {
+ setDescription(Tr::tr("Move Declaration out of Condition"));
+
+ reset();
+ }
+
+ void reset()
+ {
+ condition = mk.Condition();
+ pattern = mk.IfStatement(condition);
+ }
+
+ void perform() override
+ {
+ ChangeSet changes;
+
+ changes.copy(currentFile()->range(core), currentFile()->startOf(condition));
+
+ int insertPos = currentFile()->startOf(pattern);
+ changes.move(currentFile()->range(condition), insertPos);
+ changes.insert(insertPos, QLatin1String(";\n"));
+
+ currentFile()->apply(changes);
+ }
+
+ ASTMatcher matcher;
+ ASTPatternBuilder mk;
+ ConditionAST *condition = nullptr;
+ IfStatementAST *pattern = nullptr;
+ CoreDeclaratorAST *core = nullptr;
+};
+
+class MoveDeclarationOutOfWhileOp: public CppQuickFixOperation
+{
+public:
+ MoveDeclarationOutOfWhileOp(const CppQuickFixInterface &interface)
+ : CppQuickFixOperation(interface)
+ {
+ setDescription(Tr::tr("Move Declaration out of Condition"));
+ reset();
+ }
+
+ void reset()
+ {
+ condition = mk.Condition();
+ pattern = mk.WhileStatement(condition);
+ }
+
+ void perform() override
+ {
+ ChangeSet changes;
+
+ changes.insert(currentFile()->startOf(condition), QLatin1String("("));
+ changes.insert(currentFile()->endOf(condition), QLatin1String(") != 0"));
+
+ int insertPos = currentFile()->startOf(pattern);
+ const int conditionStart = currentFile()->startOf(condition);
+ changes.move(conditionStart, currentFile()->startOf(core), insertPos);
+ changes.copy(currentFile()->range(core), insertPos);
+ changes.insert(insertPos, QLatin1String(";\n"));
+
+ currentFile()->apply(changes);
+ }
+
+ ASTMatcher matcher;
+ ASTPatternBuilder mk;
+ ConditionAST *condition = nullptr;
+ WhileStatementAST *pattern = nullptr;
+ CoreDeclaratorAST *core = nullptr;
+};
+
+class SplitIfStatementOp: public CppQuickFixOperation
+{
+public:
+ SplitIfStatementOp(const CppQuickFixInterface &interface, int priority,
+ IfStatementAST *pattern, BinaryExpressionAST *condition)
+ : CppQuickFixOperation(interface, priority)
+ , pattern(pattern)
+ , condition(condition)
+ {
+ setDescription(Tr::tr("Split if Statement"));
+ }
+
+ void perform() override
+ {
+ const Token binaryToken = currentFile()->tokenAt(condition->binary_op_token);
+
+ if (binaryToken.is(T_AMPER_AMPER))
+ splitAndCondition();
+ else
+ splitOrCondition();
+ }
+
+ void splitAndCondition() const
+ {
+ ChangeSet changes;
+
+ int startPos = currentFile()->startOf(pattern);
+ changes.insert(startPos, QLatin1String("if ("));
+ changes.move(currentFile()->range(condition->left_expression), startPos);
+ changes.insert(startPos, QLatin1String(") {\n"));
+
+ const int lExprEnd = currentFile()->endOf(condition->left_expression);
+ changes.remove(lExprEnd, currentFile()->startOf(condition->right_expression));
+ changes.insert(currentFile()->endOf(pattern), QLatin1String("\n}"));
+
+ currentFile()->apply(changes);
+ }
+
+ void splitOrCondition() const
+ {
+ ChangeSet changes;
+
+ StatementAST *ifTrueStatement = pattern->statement;
+ CompoundStatementAST *compoundStatement = ifTrueStatement->asCompoundStatement();
+
+ int insertPos = currentFile()->endOf(ifTrueStatement);
+ if (compoundStatement)
+ changes.insert(insertPos, QLatin1String(" "));
+ else
+ changes.insert(insertPos, QLatin1String("\n"));
+ changes.insert(insertPos, QLatin1String("else if ("));
+
+ const int rExprStart = currentFile()->startOf(condition->right_expression);
+ changes.move(rExprStart, currentFile()->startOf(pattern->rparen_token), insertPos);
+ changes.insert(insertPos, QLatin1String(")"));
+
+ const int rParenEnd = currentFile()->endOf(pattern->rparen_token);
+ changes.copy(rParenEnd, currentFile()->endOf(pattern->statement), insertPos);
+
+ const int lExprEnd = currentFile()->endOf(condition->left_expression);
+ changes.remove(lExprEnd, currentFile()->startOf(condition->right_expression));
+
+ currentFile()->apply(changes);
+ }
+
+private:
+ IfStatementAST *pattern;
+ BinaryExpressionAST *condition;
+};
+
+class OptimizeForLoopOperation: public CppQuickFixOperation
+{
+public:
+ OptimizeForLoopOperation(const CppQuickFixInterface &interface, const ForStatementAST *forAst,
+ const bool optimizePostcrement, const ExpressionAST *expression,
+ const FullySpecifiedType &type)
+ : CppQuickFixOperation(interface)
+ , m_forAst(forAst)
+ , m_optimizePostcrement(optimizePostcrement)
+ , m_expression(expression)
+ , m_type(type)
+ {
+ setDescription(Tr::tr("Optimize for-Loop"));
+ }
+
+ void perform() override
+ {
+ QTC_ASSERT(m_forAst, return);
+
+ const CppRefactoringFilePtr file = currentFile();
+ ChangeSet change;
+
+ // Optimize post (in|de)crement operator to pre (in|de)crement operator
+ if (m_optimizePostcrement && m_forAst->expression) {
+ PostIncrDecrAST *incrdecr = m_forAst->expression->asPostIncrDecr();
+ if (incrdecr && incrdecr->base_expression && incrdecr->incr_decr_token) {
+ change.flip(file->range(incrdecr->base_expression),
+ file->range(incrdecr->incr_decr_token));
+ }
+ }
+
+ // Optimize Condition
+ int renamePos = -1;
+ if (m_expression) {
+ QString varName = QLatin1String("total");
+
+ if (file->textOf(m_forAst->initializer).length() == 1) {
+ Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ const QString typeAndName = oo.prettyType(m_type, varName);
+ renamePos = file->endOf(m_forAst->initializer) - 1 + typeAndName.length();
+ change.insert(file->endOf(m_forAst->initializer) - 1, // "-1" because of ";"
+ typeAndName + QLatin1String(" = ") + file->textOf(m_expression));
+ } else {
+ // Check if varName is already used
+ if (DeclarationStatementAST *ds = m_forAst->initializer->asDeclarationStatement()) {
+ if (DeclarationAST *decl = ds->declaration) {
+ if (SimpleDeclarationAST *sdecl = decl->asSimpleDeclaration()) {
+ for (;;) {
+ bool match = false;
+ for (DeclaratorListAST *it = sdecl->declarator_list; it;
+ it = it->next) {
+ if (file->textOf(it->value->core_declarator) == varName) {
+ varName += QLatin1Char('X');
+ match = true;
+ break;
+ }
+ }
+ if (!match)
+ break;
+ }
+ }
+ }
+ }
+
+ renamePos = file->endOf(m_forAst->initializer) + 1;
+ change.insert(file->endOf(m_forAst->initializer) - 1, // "-1" because of ";"
+ QLatin1String(", ") + varName + QLatin1String(" = ")
+ + file->textOf(m_expression));
+ }
+
+ ChangeSet::Range exprRange(file->startOf(m_expression), file->endOf(m_expression));
+ change.replace(exprRange, varName);
+ }
+
+ file->apply(change);
+
+ // Select variable name and trigger symbol rename
+ if (renamePos != -1) {
+ QTextCursor c = file->cursor();
+ c.setPosition(renamePos);
+ editor()->setTextCursor(c);
+ editor()->renameSymbolUnderCursor();
+ c.select(QTextCursor::WordUnderCursor);
+ editor()->setTextCursor(c);
+ }
+ }
+
+private:
+ const ForStatementAST *m_forAst;
+ const bool m_optimizePostcrement;
+ const ExpressionAST *m_expression;
+ const FullySpecifiedType m_type;
+};
+
+/*!
+ Replace
+ if (Type name = foo()) {...}
+
+ With
+ Type name = foo();
+ if (name) {...}
+
+ Activates on: the name of the introduced variable
+*/
+class MoveDeclarationOutOfIf: public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const QList<AST *> &path = interface.path();
+ using Ptr = QSharedPointer<MoveDeclarationOutOfIfOp>;
+ Ptr op(new MoveDeclarationOutOfIfOp(interface));
+
+ int index = path.size() - 1;
+ for (; index != -1; --index) {
+ if (IfStatementAST *statement = path.at(index)->asIfStatement()) {
+ if (statement->match(op->pattern, &op->matcher) && op->condition->declarator) {
+ DeclaratorAST *declarator = op->condition->declarator;
+ op->core = declarator->core_declarator;
+ if (!op->core)
+ return;
+
+ if (interface.isCursorOn(op->core)) {
+ op->setPriority(index);
+ result.append(op);
+ return;
+ }
+
+ op->reset();
+ }
+ }
+ }
+ }
+};
+
+/*!
+ Replace
+ while (Type name = foo()) {...}
+
+ With
+ Type name;
+ while ((name = foo()) != 0) {...}
+
+ Activates on: the name of the introduced variable
+*/
+class MoveDeclarationOutOfWhile: public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const QList<AST *> &path = interface.path();
+ QSharedPointer<MoveDeclarationOutOfWhileOp> op(new MoveDeclarationOutOfWhileOp(interface));
+
+ int index = path.size() - 1;
+ for (; index != -1; --index) {
+ if (WhileStatementAST *statement = path.at(index)->asWhileStatement()) {
+ if (statement->match(op->pattern, &op->matcher) && op->condition->declarator) {
+ DeclaratorAST *declarator = op->condition->declarator;
+ op->core = declarator->core_declarator;
+
+ if (!op->core)
+ return;
+
+ if (!declarator->equal_token)
+ return;
+
+ if (!declarator->initializer)
+ return;
+
+ if (interface.isCursorOn(op->core)) {
+ op->setPriority(index);
+ result.append(op);
+ return;
+ }
+
+ op->reset();
+ }
+ }
+ }
+ }
+};
+
+/*!
+ Replace
+ if (something && something_else) {
+ }
+
+ with
+ if (something)
+ if (something_else) {
+ }
+ }
+
+ and
+ if (something || something_else)
+ x;
+
+ with
+ if (something)
+ x;
+ else if (something_else)
+ x;
+
+ Activates on: && or ||
+*/
+class SplitIfStatement: public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest() { return new QObject; }
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ IfStatementAST *pattern = nullptr;
+ const QList<AST *> &path = interface.path();
+
+ int index = path.size() - 1;
+ for (; index != -1; --index) {
+ AST *node = path.at(index);
+ if (IfStatementAST *stmt = node->asIfStatement()) {
+ pattern = stmt;
+ break;
+ }
+ }
+
+ if (!pattern || !pattern->statement)
+ return;
+
+ unsigned splitKind = 0;
+ for (++index; index < path.size(); ++index) {
+ AST *node = path.at(index);
+ BinaryExpressionAST *condition = node->asBinaryExpression();
+ if (!condition)
+ return;
+
+ Token binaryToken = interface.currentFile()->tokenAt(condition->binary_op_token);
+
+ // only accept a chain of ||s or &&s - no mixing
+ if (!splitKind) {
+ splitKind = binaryToken.kind();
+ if (splitKind != T_AMPER_AMPER && splitKind != T_PIPE_PIPE)
+ return;
+ // we can't reliably split &&s in ifs with an else branch
+ if (splitKind == T_AMPER_AMPER && pattern->else_statement)
+ return;
+ } else if (splitKind != binaryToken.kind()) {
+ return;
+ }
+
+ if (interface.isCursorOn(condition->binary_op_token)) {
+ result << new SplitIfStatementOp(interface, index, pattern, condition);
+ return;
+ }
+ }
+ }
+};
+
+/*!
+ Add curly braces to a control statement that doesn't already contain a
+ compound statement. I.e.
+
+ if (a)
+ b;
+ becomes
+ if (a) {
+ b;
+ }
+
+ Activates on: the keyword
+*/
+class AddBracesToControlStatement : public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ if (interface.path().isEmpty())
+ return;
+ checkControlStatements<IfStatementAST,
+ WhileStatementAST,
+ ForStatementAST,
+ RangeBasedForStatementAST,
+ DoStatementAST>(interface, result);
+ }
+};
+
+/*!
+ Optimizes a for loop to avoid permanent condition check and forces to use preincrement
+ or predecrement operators in the expression of the for loop.
+ */
+class OptimizeForLoop : public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const QList<AST *> path = interface.path();
+ ForStatementAST *forAst = nullptr;
+ if (!path.isEmpty())
+ forAst = path.last()->asForStatement();
+ if (!forAst || !interface.isCursorOn(forAst))
+ return;
+
+ // Check for optimizing a postcrement
+ const CppRefactoringFilePtr file = interface.currentFile();
+ bool optimizePostcrement = false;
+ if (forAst->expression) {
+ if (PostIncrDecrAST *incrdecr = forAst->expression->asPostIncrDecr()) {
+ const Token t = file->tokenAt(incrdecr->incr_decr_token);
+ if (t.is(T_PLUS_PLUS) || t.is(T_MINUS_MINUS))
+ optimizePostcrement = true;
+ }
+ }
+
+ // Check for optimizing condition
+ bool optimizeCondition = false;
+ FullySpecifiedType conditionType;
+ ExpressionAST *conditionExpression = nullptr;
+ if (forAst->initializer && forAst->condition) {
+ if (BinaryExpressionAST *binary = forAst->condition->asBinaryExpression()) {
+ // Get the expression against which we should evaluate
+ IdExpressionAST *conditionId = binary->left_expression->asIdExpression();
+ if (conditionId) {
+ conditionExpression = binary->right_expression;
+ } else {
+ conditionId = binary->right_expression->asIdExpression();
+ conditionExpression = binary->left_expression;
+ }
+
+ if (conditionId && conditionExpression
+ && !(conditionExpression->asNumericLiteral()
+ || conditionExpression->asStringLiteral()
+ || conditionExpression->asIdExpression()
+ || conditionExpression->asUnaryExpression())) {
+ // Determine type of for initializer
+ FullySpecifiedType initializerType;
+ if (DeclarationStatementAST *stmt = forAst->initializer->asDeclarationStatement()) {
+ if (stmt->declaration) {
+ if (SimpleDeclarationAST *decl = stmt->declaration->asSimpleDeclaration()) {
+ if (decl->symbols) {
+ if (Symbol *symbol = decl->symbols->value)
+ initializerType = symbol->type();
+ }
+ }
+ }
+ }
+
+ // Determine type of for condition
+ TypeOfExpression typeOfExpression;
+ typeOfExpression.init(interface.semanticInfo().doc, interface.snapshot(),
+ interface.context().bindings());
+ typeOfExpression.setExpandTemplates(true);
+ Scope *scope = file->scopeAt(conditionId->firstToken());
+ const QList<LookupItem> conditionItems = typeOfExpression(
+ conditionId, interface.semanticInfo().doc, scope);
+ if (!conditionItems.isEmpty())
+ conditionType = conditionItems.first().type();
+
+ if (conditionType.isValid()
+ && (file->textOf(forAst->initializer) == QLatin1String(";")
+ || initializerType == conditionType)) {
+ optimizeCondition = true;
+ }
+ }
+ }
+ }
+
+ if (optimizePostcrement || optimizeCondition) {
+ result << new OptimizeForLoopOperation(interface, forAst, optimizePostcrement,
+ optimizeCondition ? conditionExpression : nullptr,
+ conditionType);
+ }
+ }
+};
+
+#ifdef WITH_TESTS
+using namespace Tests;
+
+class MoveDeclarationOutOfIfTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data()
+ {
+ QTest::addColumn<QByteArray>("original");
+ QTest::addColumn<QByteArray>("expected");
+
+ QTest::newRow("ifOnly")
+ << QByteArray(
+ "void f()\n"
+ "{\n"
+ " if (Foo *@foo = g())\n"
+ " h();\n"
+ "}\n")
+ << QByteArray(
+ "void f()\n"
+ "{\n"
+ " Foo *foo = g();\n"
+ " if (foo)\n"
+ " h();\n"
+ "}\n");
+ QTest::newRow("ifElse")
+ << QByteArray(
+ "void f()\n"
+ "{\n"
+ " if (Foo *@foo = g())\n"
+ " h();\n"
+ " else\n"
+ " i();\n"
+ "}\n")
+ << QByteArray(
+ "void f()\n"
+ "{\n"
+ " Foo *foo = g();\n"
+ " if (foo)\n"
+ " h();\n"
+ " else\n"
+ " i();\n"
+ "}\n");
+
+ QTest::newRow("MoveDeclarationOutOfIf_ifElseIf")
+ << QByteArray(
+ "void f()\n"
+ "{\n"
+ " if (Foo *foo = g()) {\n"
+ " if (Bar *@bar = x()) {\n"
+ " h();\n"
+ " j();\n"
+ " }\n"
+ " } else {\n"
+ " i();\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "void f()\n"
+ "{\n"
+ " if (Foo *foo = g()) {\n"
+ " Bar *bar = x();\n"
+ " if (bar) {\n"
+ " h();\n"
+ " j();\n"
+ " }\n"
+ " } else {\n"
+ " i();\n"
+ " }\n"
+ "}\n");
+ }
+
+ void test()
+ {
+ QFETCH(QByteArray, original);
+ QFETCH(QByteArray, expected);
+ MoveDeclarationOutOfIf factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+};
+
+class MoveDeclarationOutOfWhileTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data()
+ {
+ QTest::addColumn<QByteArray>("original");
+ QTest::addColumn<QByteArray>("expected");
+
+ QTest::newRow("singleWhile")
+ << QByteArray(
+ "void f()\n"
+ "{\n"
+ " while (Foo *@foo = g())\n"
+ " j();\n"
+ "}\n")
+ << QByteArray(
+ "void f()\n"
+ "{\n"
+ " Foo *foo;\n"
+ " while ((foo = g()) != 0)\n"
+ " j();\n"
+ "}\n");
+ QTest::newRow("whileInWhile")
+ << QByteArray(
+ "void f()\n"
+ "{\n"
+ " while (Foo *foo = g()) {\n"
+ " while (Bar *@bar = h()) {\n"
+ " i();\n"
+ " j();\n"
+ " }\n"
+ " }\n"
+ "}\n")
+ << QByteArray(
+ "void f()\n"
+ "{\n"
+ " while (Foo *foo = g()) {\n"
+ " Bar *bar;\n"
+ " while ((bar = h()) != 0) {\n"
+ " i();\n"
+ " j();\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ );
+
+ }
+
+ void test()
+ {
+ QFETCH(QByteArray, original);
+ QFETCH(QByteArray, expected);
+ MoveDeclarationOutOfWhile factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+};
+
+class OptimizeForLoopTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data()
+ {
+ QTest::addColumn<QByteArray>("original");
+ QTest::addColumn<QByteArray>("expected");
+
+ // Check: optimize postcrement
+ QTest::newRow("OptimizeForLoop_postcrement")
+ << QByteArray("void foo() {f@or (int i = 0; i < 3; i++) {}}\n")
+ << QByteArray("void foo() {for (int i = 0; i < 3; ++i) {}}\n");
+
+ // Check: optimize condition
+ QTest::newRow("OptimizeForLoop_condition")
+ << QByteArray("void foo() {f@or (int i = 0; i < 3 + 5; ++i) {}}\n")
+ << QByteArray("void foo() {for (int i = 0, total = 3 + 5; i < total; ++i) {}}\n");
+
+ // Check: optimize fliped condition
+ QTest::newRow("OptimizeForLoop_flipedCondition")
+ << QByteArray("void foo() {f@or (int i = 0; 3 + 5 > i; ++i) {}}\n")
+ << QByteArray("void foo() {for (int i = 0, total = 3 + 5; total > i; ++i) {}}\n");
+
+ // Check: if "total" used, create other name.
+ QTest::newRow("OptimizeForLoop_alterVariableName")
+ << QByteArray("void foo() {f@or (int i = 0, total = 0; i < 3 + 5; ++i) {}}\n")
+ << QByteArray("void foo() {for (int i = 0, total = 0, totalX = 3 + 5; i < totalX; ++i) {}}\n");
+
+ // Check: optimize postcrement and condition
+ QTest::newRow("OptimizeForLoop_optimizeBoth")
+ << QByteArray("void foo() {f@or (int i = 0; i < 3 + 5; i++) {}}\n")
+ << QByteArray("void foo() {for (int i = 0, total = 3 + 5; i < total; ++i) {}}\n");
+
+ // Check: empty initializier
+ QTest::newRow("OptimizeForLoop_emptyInitializer")
+ << QByteArray("int i; void foo() {f@or (; i < 3 + 5; ++i) {}}\n")
+ << QByteArray("int i; void foo() {for (int total = 3 + 5; i < total; ++i) {}}\n");
+
+ // Check: wrong initializier type -> no trigger
+ QTest::newRow("OptimizeForLoop_wrongInitializer")
+ << QByteArray("int i; void foo() {f@or (double a = 0; i < 3 + 5; ++i) {}}\n")
+ << QByteArray();
+
+ // Check: No trigger when numeric
+ QTest::newRow("OptimizeForLoop_noTriggerNumeric1")
+ << QByteArray("void foo() {fo@r (int i = 0; i < 3; ++i) {}}\n")
+ << QByteArray();
+
+ // Check: No trigger when numeric
+ QTest::newRow("OptimizeForLoop_noTriggerNumeric2")
+ << QByteArray("void foo() {fo@r (int i = 0; i < -3; ++i) {}}\n")
+ << QByteArray();
+ }
+
+ void test()
+ {
+ QFETCH(QByteArray, original);
+ QFETCH(QByteArray, expected);
+ OptimizeForLoop factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+};
+
+class AddBracesToControlStatementTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data()
+ {
+ QTest::addColumn<QByteArray>("original");
+ QTest::addColumn<QByteArray>("expected");
+
+ QByteArray original = R"delim(
+void MyObject::f()
+{
+ @if (true)
+ emit mySig();
+})delim";
+ QByteArray expected = R"delim(
+void MyObject::f()
+{
+ if (true) {
+ emit mySig();
+ }
+})delim";
+ QTest::newRow("if") << original << expected;
+
+ original = R"delim(
+void MyObject::f()
+{
+ @if (true)
+ emit mySig();
+ else
+ emit otherSig();
+})delim";
+ expected = R"delim(
+void MyObject::f()
+{
+ @if (true) {
+ emit mySig();
+ } else {
+ emit otherSig();
+ }
+})delim";
+ QTest::newRow("if with one else, unbraced") << original << expected;
+
+ original = R"delim(
+void MyObject::f()
+{
+ @if (true) {
+ emit mySig();
+ } else
+ emit otherSig();
+})delim";
+ expected = R"delim(
+void MyObject::f()
+{
+ @if (true) {
+ emit mySig();
+ } else {
+ emit otherSig();
+ }
+})delim";
+ QTest::newRow("if with one else, if braced") << original << expected;
+
+ original = R"delim(
+void MyObject::f()
+{
+ @if (true)
+ emit mySig();
+ else {
+ emit otherSig();
+ }
+})delim";
+ expected = R"delim(
+void MyObject::f()
+{
+ @if (true) {
+ emit mySig();
+ } else {
+ emit otherSig();
+ }
+})delim";
+ QTest::newRow("if with one else, else braced") << original << expected;
+
+ original = R"delim(
+void MyObject::f()
+{
+ @if (true) {
+ emit mySig();
+ } else {
+ emit otherSig();
+ }
+})delim";
+ expected.clear();
+ QTest::newRow("if with one else, both braced") << original << expected;
+
+ original = R"delim(
+void MyObject::f()
+{
+ @if (x == 1)
+ emit sig1();
+ else if (x == 2)
+ emit sig2();
+})delim";
+ expected = R"delim(
+void MyObject::f()
+{
+ if (x == 1) {
+ emit sig1();
+ } else if (x == 2) {
+ emit sig2();
+ }
+})delim";
+ QTest::newRow("if-else chain without final else, unbraced") << original << expected;
+
+ original = R"delim(
+void MyObject::f()
+{
+ @if (x == 1) {
+ emit sig1();
+ } else if (x == 2)
+ emit sig2();
+})delim";
+ expected = R"delim(
+void MyObject::f()
+{
+ if (x == 1) {
+ emit sig1();
+ } else if (x == 2) {
+ emit sig2();
+ }
+})delim";
+ QTest::newRow("if-else chain without final else, partially braced 1") << original << expected;
+
+ original = R"delim(
+void MyObject::f()
+{
+ @if (x == 1)
+ emit sig1();
+ else if (x == 2) {
+ emit sig2();
+ }
+})delim";
+ expected = R"delim(
+void MyObject::f()
+{
+ if (x == 1) {
+ emit sig1();
+ } else if (x == 2) {
+ emit sig2();
+ }
+})delim";
+ QTest::newRow("if-else chain without final else, partially braced 2") << original << expected;
+
+ original = R"delim(
+void MyObject::f()
+{
+ @if (x == 1) {
+ emit sig1();
+ } else if (x == 2) {
+ emit sig2();
+ }
+})delim";
+ expected.clear();
+ QTest::newRow("if-else chain without final else, fully braced") << original << expected;
+
+ original = R"delim(
+void MyObject::f()
+{
+ @if (x == 1)
+ emit sig1();
+ else if (x == 2)
+ emit sig2();
+ else if (x == 3)
+ emit sig3();
+ else
+ emit otherSig();
+})delim";
+ expected = R"delim(
+void MyObject::f()
+{
+ if (x == 1) {
+ emit sig1();
+ } else if (x == 2) {
+ emit sig2();
+ } else if (x == 3) {
+ emit sig3();
+ } else {
+ emit otherSig();
+ }
+})delim";
+ QTest::newRow("if-else chain, unbraced") << original << expected;
+
+ original = R"delim(
+void MyObject::f()
+{
+ @if (x == 1) {
+ emit sig1();
+ } else if (x == 2)
+ emit sig2();
+ else if (x == 3)
+ emit sig3();
+ else
+ emit otherSig();
+})delim";
+ expected = R"delim(
+void MyObject::f()
+{
+ if (x == 1) {
+ emit sig1();
+ } else if (x == 2) {
+ emit sig2();
+ } else if (x == 3) {
+ emit sig3();
+ } else {
+ emit otherSig();
+ }
+})delim";
+ QTest::newRow("if-else chain, partially braced 1") << original << expected;
+
+ original = R"delim(
+void MyObject::f()
+{
+ @if (x == 1)
+ emit sig1();
+ else if (x == 2) {
+ emit sig2();
+ } else if (x == 3)
+ emit sig3();
+ else
+ emit otherSig();
+})delim";
+ expected = R"delim(
+void MyObject::f()
+{
+ if (x == 1) {
+ emit sig1();
+ } else if (x == 2) {
+ emit sig2();
+ } else if (x == 3) {
+ emit sig3();
+ } else {
+ emit otherSig();
+ }
+})delim";
+ QTest::newRow("if-else chain, partially braced 2") << original << expected;
+
+ original = R"delim(
+void MyObject::f()
+{
+ @if (x == 1)
+ emit sig1();
+ else if (x == 2)
+ emit sig2();
+ else if (x == 3) {
+ emit sig3();
+ } else
+ emit otherSig();
+})delim";
+ expected = R"delim(
+void MyObject::f()
+{
+ if (x == 1) {
+ emit sig1();
+ } else if (x == 2) {
+ emit sig2();
+ } else if (x == 3) {
+ emit sig3();
+ } else {
+ emit otherSig();
+ }
+})delim";
+ QTest::newRow("if-else chain, partially braced 3") << original << expected;
+
+ original = R"delim(
+void MyObject::f()
+{
+ @if (x == 1)
+ emit sig1();
+ else if (x == 2)
+ emit sig2();
+ else if (x == 3)
+ emit sig3();
+ else {
+ emit otherSig();
+ }
+})delim";
+ expected = R"delim(
+void MyObject::f()
+{
+ if (x == 1) {
+ emit sig1();
+ } else if (x == 2) {
+ emit sig2();
+ } else if (x == 3) {
+ emit sig3();
+ } else {
+ emit otherSig();
+ }
+})delim";
+ QTest::newRow("if-else chain, partially braced 4") << original << expected;
+
+ original = R"delim(
+void MyObject::f()
+{
+ @if (x == 1) {
+ emit sig1();
+ } else if (x == 2) {
+ emit sig2();
+ } else if (x == 3) {
+ emit sig3();
+ } else {
+ emit otherSig();
+ }
+})delim";
+ expected.clear();
+ QTest::newRow("if-else chain, fully braced") << original << expected;
+
+ original = R"delim(
+void MyObject::f()
+{
+ @while (true)
+ emit mySig();
+})delim";
+ expected = R"delim(
+void MyObject::f()
+{
+ while (true) {
+ emit mySig();
+ }
+})delim";
+ QTest::newRow("while") << original << expected;
+
+ original = R"delim(
+void MyObject::f()
+{
+ @for (int i = 0; i < 10; ++i)
+ emit mySig();
+})delim";
+ expected = R"delim(
+void MyObject::f()
+{
+ for (int i = 0; i < 10; ++i) {
+ emit mySig();
+ }
+})delim";
+ QTest::newRow("for") << original << expected;
+
+ original = R"delim(
+void MyObject::f()
+{
+ @for (int i : list)
+ emit mySig();
+})delim";
+ expected = R"delim(
+void MyObject::f()
+{
+ for (int i : list) {
+ emit mySig();
+ }
+})delim";
+ QTest::newRow("range-based for") << original << expected;
+
+ original = R"delim(
+void MyObject::f()
+{
+ @do
+ emit mySig();
+ while (true);
+})delim";
+ expected = R"delim(
+void MyObject::f()
+{
+ do {
+ emit mySig();
+ } while (true);
+})delim";
+ QTest::newRow("do") << original << expected;
+
+ original = R"delim(
+void MyObject::f()
+{
+ @do {
+ emit mySig();
+ } while (true);
+})delim";
+ expected.clear();
+ QTest::newRow("already has braces") << original << expected;
+ }
+
+ void test()
+ {
+ QFETCH(QByteArray, original);
+ QFETCH(QByteArray, expected);
+
+ AddBracesToControlStatement factory;
+ QuickFixOperationTest({CppTestDocument::create("file.cpp", original, expected)}, &factory);
+ }
+};
+
+QObject *MoveDeclarationOutOfIf::createTest() { return new MoveDeclarationOutOfIfTest; }
+QObject *MoveDeclarationOutOfWhile::createTest() { return new MoveDeclarationOutOfWhileTest; }
+QObject *OptimizeForLoop::createTest() { return new OptimizeForLoopTest; }
+QObject *AddBracesToControlStatement::createTest() { return new AddBracesToControlStatementTest; }
+
+#endif // WITH_TESTS
+} // namespace
+
+void registerRewriteControlStatementQuickfixes()
+{
+ CppQuickFixFactory::registerFactory<AddBracesToControlStatement>();
+ CppQuickFixFactory::registerFactory<MoveDeclarationOutOfIf>();
+ CppQuickFixFactory::registerFactory<MoveDeclarationOutOfWhile>();
+ CppQuickFixFactory::registerFactory<OptimizeForLoop>();
+ CppQuickFixFactory::registerFactory<SplitIfStatement>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <rewritecontrolstatements.moc>
+#endif
diff --git a/src/plugins/cppeditor/quickfixes/rewritecontrolstatements.h b/src/plugins/cppeditor/quickfixes/rewritecontrolstatements.h
new file mode 100644
index 0000000000..d450708a4f
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/rewritecontrolstatements.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerRewriteControlStatementQuickfixes();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/splitsimpledeclaration.cpp b/src/plugins/cppeditor/quickfixes/splitsimpledeclaration.cpp
new file mode 100644
index 0000000000..9d160d71ed
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/splitsimpledeclaration.cpp
@@ -0,0 +1,140 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "splitsimpledeclaration.h"
+
+#include "../cppeditortr.h"
+#include "../cpprefactoringchanges.h"
+#include "cppquickfix.h"
+
+using namespace CPlusPlus;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+static bool checkDeclarationForSplit(SimpleDeclarationAST *declaration)
+{
+ if (!declaration->semicolon_token)
+ return false;
+
+ if (!declaration->decl_specifier_list)
+ return false;
+
+ for (SpecifierListAST *it = declaration->decl_specifier_list; it; it = it->next) {
+ SpecifierAST *specifier = it->value;
+ if (specifier->asEnumSpecifier() || specifier->asClassSpecifier())
+ return false;
+ }
+
+ return declaration->declarator_list && declaration->declarator_list->next;
+}
+
+class SplitSimpleDeclarationOp : public CppQuickFixOperation
+{
+public:
+ SplitSimpleDeclarationOp(const CppQuickFixInterface &interface, int priority,
+ SimpleDeclarationAST *decl)
+ : CppQuickFixOperation(interface, priority)
+ , declaration(decl)
+ {
+ setDescription(Tr::tr("Split Declaration"));
+ }
+
+ void perform() override
+ {
+ ChangeSet changes;
+
+ SpecifierListAST *specifiers = declaration->decl_specifier_list;
+ int declSpecifiersStart = currentFile()->startOf(specifiers->firstToken());
+ int declSpecifiersEnd = currentFile()->endOf(specifiers->lastToken() - 1);
+ int insertPos = currentFile()->endOf(declaration->semicolon_token);
+
+ DeclaratorAST *prevDeclarator = declaration->declarator_list->value;
+
+ for (DeclaratorListAST *it = declaration->declarator_list->next; it; it = it->next) {
+ DeclaratorAST *declarator = it->value;
+
+ changes.insert(insertPos, QLatin1String("\n"));
+ changes.copy(declSpecifiersStart, declSpecifiersEnd, insertPos);
+ changes.insert(insertPos, QLatin1String(" "));
+ changes.move(currentFile()->range(declarator), insertPos);
+ changes.insert(insertPos, QLatin1String(";"));
+
+ const int prevDeclEnd = currentFile()->endOf(prevDeclarator);
+ changes.remove(prevDeclEnd, currentFile()->startOf(declarator));
+
+ prevDeclarator = declarator;
+ }
+
+ currentFile()->apply(changes);
+ }
+
+private:
+ SimpleDeclarationAST *declaration;
+};
+
+/*!
+ Rewrite
+ int *a, b;
+
+ As
+ int *a;
+ int b;
+
+ Activates on: the type or the variable names.
+*/
+class SplitSimpleDeclaration : public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest() { return new QObject; }
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ CoreDeclaratorAST *core_declarator = nullptr;
+ const QList<AST *> &path = interface.path();
+ CppRefactoringFilePtr file = interface.currentFile();
+ const int cursorPosition = file->cursor().selectionStart();
+
+ for (int index = path.size() - 1; index != -1; --index) {
+ AST *node = path.at(index);
+
+ if (CoreDeclaratorAST *coreDecl = node->asCoreDeclarator()) {
+ core_declarator = coreDecl;
+ } else if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
+ if (checkDeclarationForSplit(simpleDecl)) {
+ SimpleDeclarationAST *declaration = simpleDecl;
+
+ const int startOfDeclSpecifier = file->startOf(declaration->decl_specifier_list->firstToken());
+ const int endOfDeclSpecifier = file->endOf(declaration->decl_specifier_list->lastToken() - 1);
+
+ if (cursorPosition >= startOfDeclSpecifier && cursorPosition <= endOfDeclSpecifier) {
+ // the AST node under cursor is a specifier.
+ result << new SplitSimpleDeclarationOp(interface, index, declaration);
+ return;
+ }
+
+ if (core_declarator && interface.isCursorOn(core_declarator)) {
+ // got a core-declarator under the text cursor.
+ result << new SplitSimpleDeclarationOp(interface, index, declaration);
+ return;
+ }
+ }
+
+ return;
+ }
+ }
+ }
+};
+
+} // namespace
+
+void registerSplitSimpleDeclarationQuickfix()
+{
+ CppQuickFixFactory::registerFactory<SplitSimpleDeclaration>();
+}
+
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/splitsimpledeclaration.h b/src/plugins/cppeditor/quickfixes/splitsimpledeclaration.h
new file mode 100644
index 0000000000..8c3100a9f4
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/splitsimpledeclaration.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerSplitSimpleDeclarationQuickfix();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/quickfixes/synchronizememberfunctionorder.cpp b/src/plugins/cppeditor/quickfixes/synchronizememberfunctionorder.cpp
new file mode 100644
index 0000000000..eddc4d6e5b
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/synchronizememberfunctionorder.cpp
@@ -0,0 +1,335 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "synchronizememberfunctionorder.h"
+
+#include "../cppeditortr.h"
+#include "../cppeditorwidget.h"
+#include "../cpprefactoringchanges.h"
+#include "cppquickfix.h"
+#include "cppquickfixhelpers.h"
+
+#include <coreplugin/editormanager/editormanager.h>
+#include <cplusplus/ASTPath.h>
+
+#include <QList>
+#include <QHash>
+
+#ifdef WITH_TESTS
+#include "cppquickfix_test.h"
+#include <projectexplorer/kitmanager.h>
+#include <texteditor/textdocument.h>
+#include <QtTest>
+#endif
+
+#include <memory>
+
+using namespace Core;
+using namespace CPlusPlus;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+class SynchronizeMemberFunctionOrderOp : public CppQuickFixOperation
+{
+public:
+ SynchronizeMemberFunctionOrderOp(
+ const CppQuickFixInterface &interface, const QList<Symbol *> &decls)
+ : CppQuickFixOperation(interface), m_state(std::make_shared<State>())
+ {
+ setDescription(
+ Tr::tr("Re-order Member Function Definitions According to Declaration Order"));
+ m_state->decls = decls;
+ }
+
+private:
+ struct DefLocation {
+ Symbol *decl = nullptr;
+ Link defLoc;
+ bool operator==(const DefLocation &other) const
+ {
+ return decl == other.decl && defLoc == other.defLoc;
+ }
+ };
+ using DefLocations = QList<DefLocation>;
+ struct State {
+ using Ptr = std::shared_ptr<State>;
+
+ void insertSorted(Symbol *decl, const Link &link) {
+ DefLocations &dl = defLocations[link.targetFilePath];
+ DefLocation newElem{decl, link};
+ const auto cmp = [](const DefLocation &elem, const DefLocation &value) {
+ if (elem.defLoc.targetLine < value.defLoc.targetLine)
+ return true;
+ if (elem.defLoc.targetLine > value.defLoc.targetLine)
+ return false;
+ return elem.defLoc.targetColumn < value.defLoc.targetColumn;
+ };
+ dl.insert(std::lower_bound(dl.begin(), dl.end(), newElem, cmp), newElem);
+ }
+
+ QList<Symbol *> decls;
+ QHash<FilePath, DefLocations> defLocations;
+ int remainingFollowSymbolOps = 0;
+ };
+
+ void perform() override
+ {
+ for (Symbol * const decl : std::as_const(m_state->decls)) {
+ QTextCursor cursor(currentFile()->document()->begin());
+ TranslationUnit * const tu = currentFile()->cppDocument()->translationUnit();
+ const int declPos = tu->getTokenPositionInDocument(decl->sourceLocation(),
+ currentFile()->document());
+ cursor.setPosition(declPos);
+ const CursorInEditor cursorInEditor(
+ cursor,
+ decl->filePath(),
+ qobject_cast<CppEditorWidget *>(currentFile()->editor()),
+ currentFile()->editor()->textDocument(),
+ currentFile()->cppDocument());
+
+ const auto callback = [decl, declPos, doc = cursor.document(), state = m_state](
+ const Link &link) {
+ class FinishedChecker
+ {
+ public:
+ FinishedChecker(const State::Ptr &state) : m_state(state)
+ {}
+ ~FinishedChecker()
+ {
+ if (--m_state->remainingFollowSymbolOps == 0)
+ finish(m_state);
+ };
+ private:
+ const State::Ptr &m_state;
+ } finishedChecker(state);
+
+ if (!link.hasValidTarget())
+ return;
+ if (decl->filePath() == link.targetFilePath) {
+ const int linkPos = Text::positionInText(doc, link.targetLine,
+ link.targetColumn + 1);
+ if (linkPos == declPos)
+ return;
+ }
+ state->insertSorted(decl, link);
+ };
+
+ ++m_state->remainingFollowSymbolOps;
+
+ // Force queued execution, as the built-in editor can run the callback synchronously.
+ const auto followSymbol = [cursorInEditor, callback] {
+ CppModelManager::followSymbol(
+ cursorInEditor, callback, true, false, FollowSymbolMode::Exact);
+ };
+ QMetaObject::invokeMethod(CppModelManager::instance(), followSymbol, Qt::QueuedConnection);
+ }
+ }
+
+ static void finish(const State::Ptr &state)
+ {
+ CppRefactoringChanges factory{CppModelManager::snapshot()};
+
+ const auto findAstRange = [](const CppRefactoringFile &file, const Link &pos) {
+ const QList<AST *> astPath = ASTPath(
+ file.cppDocument())(pos.targetLine, pos.targetColumn + 1);
+ for (auto it = astPath.rbegin(); it != astPath.rend(); ++it) {
+ if (const auto funcDef = (*it)->asFunctionDefinition()) {
+ AST *ast = funcDef;
+ for (auto next = std::next(it);
+ next != astPath.rend() && (*next)->asTemplateDeclaration();
+ ++next) {
+ ast = *next;
+ }
+ return file.range(ast);
+ }
+ }
+ return ChangeSet::Range();
+ };
+
+ for (auto it = state->defLocations.cbegin(); it != state->defLocations.cend(); ++it) {
+ const DefLocations &defLocsActualOrder = it.value();
+ const DefLocations defLocsExpectedOrder = Utils::sorted(
+ defLocsActualOrder, [](const DefLocation &loc1, const DefLocation &loc2) {
+ return loc1.decl->sourceLocation() < loc2.decl->sourceLocation();
+ });
+ if (defLocsExpectedOrder == defLocsActualOrder)
+ continue;
+
+ CppRefactoringFilePtr file = factory.cppFile(it.key());
+ ChangeSet changes;
+ for (int i = 0; i < defLocsActualOrder.size(); ++i) {
+ const DefLocation &actualLoc = defLocsActualOrder[i];
+ int expectedPos = -1;
+ for (int j = 0; j < defLocsExpectedOrder.size(); ++j) {
+ if (defLocsExpectedOrder[j].decl == actualLoc.decl) {
+ expectedPos = j;
+ break;
+ }
+ }
+ if (expectedPos == i)
+ continue;
+ const ChangeSet::Range actualRange = findAstRange(*file, actualLoc.defLoc);
+ const ChangeSet::Range expectedRange
+ = findAstRange(*file, defLocsActualOrder[expectedPos].defLoc);
+ if (actualRange.end > actualRange.start && expectedRange.end > expectedRange.start)
+ changes.move(actualRange, expectedRange.start);
+ }
+ QTC_ASSERT(!changes.hadErrors(), continue);
+ file->setChangeSet(changes);
+ file->apply();
+ }
+ }
+
+ const State::Ptr m_state;
+};
+
+//! Ensures relative order of member function implementations is the same as declaration order.
+class SynchronizeMemberFunctionOrder : public CppQuickFixFactory
+{
+#ifdef WITH_TESTS
+public:
+ static QObject *createTest();
+#endif
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ ClassSpecifierAST * const classAst = astForClassOperations(interface);
+ if (!classAst || !classAst->symbol)
+ return;
+
+ QList<Symbol *> memberFunctions;
+ const TranslationUnit * const tu
+ = interface.currentFile()->cppDocument()->translationUnit();
+ for (int i = 0; i < classAst->symbol->memberCount(); ++i) {
+ Symbol *member = classAst->symbol->memberAt(i);
+
+ // Skip macros
+ if (tu->tokenAt(member->sourceLocation()).expanded())
+ continue;
+
+ if (const auto templ = member->asTemplate())
+ member = templ->declaration();
+ if (member->type()->asFunctionType() && !member->asFunction())
+ memberFunctions << member;
+ }
+ if (!memberFunctions.isEmpty())
+ result << new SynchronizeMemberFunctionOrderOp(interface, memberFunctions);
+ }
+};
+
+#ifdef WITH_TESTS
+using namespace Tests;
+
+class SynchronizeMemberFunctionOrderTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void test_data()
+ {
+ QTest::addColumn<QString>("projectName");
+ QTest::addColumn<bool>("expectChanges");
+
+ QTest::newRow("no out-of-line definitions") << "no-out-of-line" << false;
+ QTest::newRow("already sorted") << "already-sorted" << false;
+ QTest::newRow("different impl locations") << "different-locations" << true;
+ QTest::newRow("templates") << "templates" << true;
+ }
+
+ void test()
+ {
+ QFETCH(QString, projectName);
+ QFETCH(bool, expectChanges);
+ using namespace CppEditor::Tests;
+ using namespace ProjectExplorer;
+ using namespace TextEditor;
+
+ // Set up project.
+ Kit * const kit = Utils::findOr(KitManager::kits(), nullptr, [](const Kit *k) {
+ return k->isValid() && !k->hasWarning() && k->value("QtSupport.QtInformation").isValid();
+ });
+ if (!kit)
+ QSKIP("The test requires at least one valid kit with a valid Qt");
+ const auto projectDir = std::make_unique<TemporaryCopiedDir>(
+ ":/cppeditor/testcases/reorder-member-impls/" + projectName);
+ SourceFilesRefreshGuard refreshGuard;
+ ProjectOpenerAndCloser projectMgr;
+ QVERIFY(projectMgr.open(projectDir->absolutePath(projectName + ".pro"), true, kit));
+ QVERIFY(refreshGuard.wait());
+
+ // Open header file and locate class.
+ const auto headerFilePath = projectDir->absolutePath("header.h");
+ QVERIFY2(headerFilePath.exists(), qPrintable(headerFilePath.toUserOutput()));
+ const auto editor = qobject_cast<BaseTextEditor *>(EditorManager::openEditor(headerFilePath));
+ QVERIFY(editor);
+ const auto doc = qobject_cast<TextEditor::TextDocument *>(editor->document());
+ QVERIFY(doc);
+ QTextCursor classCursor = doc->document()->find("struct S");
+ QVERIFY(!classCursor.isNull());
+ editor->setCursorPosition(classCursor.position());
+ const auto editorWidget = qobject_cast<CppEditorWidget *>(editor->editorWidget());
+ QVERIFY(editorWidget);
+ QVERIFY(TestCase::waitForRehighlightedSemanticDocument(editorWidget));
+
+ // Query factory.
+ SynchronizeMemberFunctionOrder factory;
+ CppQuickFixInterface quickFixInterface(editorWidget, ExplicitlyInvoked);
+ QuickFixOperations operations;
+ factory.match(quickFixInterface, operations);
+ operations.first()->perform();
+ if (expectChanges)
+ QVERIFY(waitForSignalOrTimeout(doc, &IDocument::saved, 30000));
+ QTest::qWait(1000);
+
+ // Compare all files.
+ const FileFilter filter({"*_expected"}, QDir::Files);
+ const FilePaths expectedDocuments = projectDir->filePath().dirEntries(filter);
+ QVERIFY(!expectedDocuments.isEmpty());
+ for (const FilePath &expected : expectedDocuments) {
+ static const QString suffix = "_expected";
+ const FilePath actual = expected.parentDir()
+ .pathAppended(expected.fileName().chopped(suffix.length()));
+ QVERIFY(actual.exists());
+ const auto actualContents = actual.fileContents();
+ QVERIFY(actualContents);
+ const auto expectedContents = expected.fileContents();
+ const QByteArrayList actualLines = actualContents->split('\n');
+ const QByteArrayList expectedLines = expectedContents->split('\n');
+ if (actualLines.size() != expectedLines.size()) {
+ qDebug().noquote().nospace() << "---\n" << *expectedContents << "EOF";
+ qDebug().noquote().nospace() << "+++\n" << *actualContents << "EOF";
+ }
+ QCOMPARE(actualLines.size(), expectedLines.size());
+ for (int i = 0; i < actualLines.size(); ++i) {
+ const QByteArray actualLine = actualLines.at(i);
+ const QByteArray expectedLine = expectedLines.at(i);
+ if (actualLine != expectedLine)
+ qDebug() << "Unexpected content in line" << (i + 1) << "of file"
+ << actual.fileName();
+ QCOMPARE(actualLine, expectedLine);
+ }
+ }
+ }
+};
+
+QObject *SynchronizeMemberFunctionOrder::createTest()
+{
+ return new SynchronizeMemberFunctionOrderTest;
+}
+
+#endif
+} // namespace
+
+void registerSynchronizeMemberFunctionOrderQuickfix()
+{
+ CppQuickFixFactory::registerFactory<SynchronizeMemberFunctionOrder>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <synchronizememberfunctionorder.moc>
+#endif
diff --git a/src/plugins/cppeditor/quickfixes/synchronizememberfunctionorder.h b/src/plugins/cppeditor/quickfixes/synchronizememberfunctionorder.h
new file mode 100644
index 0000000000..da6ad971db
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/synchronizememberfunctionorder.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace CppEditor::Internal {
+void registerSynchronizeMemberFunctionOrderQuickfix();
+} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/testcases/reorder-member-impls/already-sorted/already-sorted.pro b/src/plugins/cppeditor/testcases/reorder-member-impls/already-sorted/already-sorted.pro
new file mode 100644
index 0000000000..7051801269
--- /dev/null
+++ b/src/plugins/cppeditor/testcases/reorder-member-impls/already-sorted/already-sorted.pro
@@ -0,0 +1 @@
+HEADERS = header.h
diff --git a/src/plugins/cppeditor/testcases/reorder-member-impls/already-sorted/header.h b/src/plugins/cppeditor/testcases/reorder-member-impls/already-sorted/header.h
new file mode 100644
index 0000000000..c0d7e0caa7
--- /dev/null
+++ b/src/plugins/cppeditor/testcases/reorder-member-impls/already-sorted/header.h
@@ -0,0 +1,9 @@
+struct S {
+ void firstFunction();
+ void secondFunction();
+ void thirdFunction();
+};
+
+void S::firstFunction() {}
+void S::secondFunction() {}
+void S::thirdFunction() {}
diff --git a/src/plugins/cppeditor/testcases/reorder-member-impls/already-sorted/header.h_expected b/src/plugins/cppeditor/testcases/reorder-member-impls/already-sorted/header.h_expected
new file mode 100644
index 0000000000..c0d7e0caa7
--- /dev/null
+++ b/src/plugins/cppeditor/testcases/reorder-member-impls/already-sorted/header.h_expected
@@ -0,0 +1,9 @@
+struct S {
+ void firstFunction();
+ void secondFunction();
+ void thirdFunction();
+};
+
+void S::firstFunction() {}
+void S::secondFunction() {}
+void S::thirdFunction() {}
diff --git a/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/different-locations.pro b/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/different-locations.pro
new file mode 100644
index 0000000000..a8dd832020
--- /dev/null
+++ b/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/different-locations.pro
@@ -0,0 +1,2 @@
+SOURCES = impl1.cpp impl2.cpp
+HEADERS = header.h
diff --git a/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/header.h b/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/header.h
new file mode 100644
index 0000000000..15a428eb7c
--- /dev/null
+++ b/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/header.h
@@ -0,0 +1,15 @@
+struct S {
+ void firstFunction();
+ void secondFunction();
+ void thirdFunction() {}
+ void fourthFunction();
+ void fifthFunction();
+ void sixthFunction();
+ void seventhFunction();
+ void eighthFunction();
+ void ninthFunction();
+ void tenthFunction();
+};
+
+void S::tenthFunction() {}
+void S::fourthFunction() {}
diff --git a/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/header.h_expected b/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/header.h_expected
new file mode 100644
index 0000000000..eeec93f300
--- /dev/null
+++ b/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/header.h_expected
@@ -0,0 +1,15 @@
+struct S {
+ void firstFunction();
+ void secondFunction();
+ void thirdFunction() {}
+ void fourthFunction();
+ void fifthFunction();
+ void sixthFunction();
+ void seventhFunction();
+ void eighthFunction();
+ void ninthFunction();
+ void tenthFunction();
+};
+
+void S::fourthFunction() {}
+void S::tenthFunction() {}
diff --git a/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/impl1.cpp b/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/impl1.cpp
new file mode 100644
index 0000000000..816fef1779
--- /dev/null
+++ b/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/impl1.cpp
@@ -0,0 +1,5 @@
+#include "header.h"
+
+void S::ninthFunction() {}
+void S::firstFunction() {}
+void S::secondFunction() {}
diff --git a/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/impl1.cpp_expected b/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/impl1.cpp_expected
new file mode 100644
index 0000000000..24d0f3a212
--- /dev/null
+++ b/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/impl1.cpp_expected
@@ -0,0 +1,5 @@
+#include "header.h"
+
+void S::firstFunction() {}
+void S::secondFunction() {}
+void S::ninthFunction() {}
diff --git a/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/impl2.cpp b/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/impl2.cpp
new file mode 100644
index 0000000000..f1040e089e
--- /dev/null
+++ b/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/impl2.cpp
@@ -0,0 +1,6 @@
+#include "header.h"
+
+void S::eighthFunction() {}
+void S::seventhFunction() {}
+void S::sixthFunction() {}
+void S::fifthFunction() {}
diff --git a/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/impl2.cpp_expected b/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/impl2.cpp_expected
new file mode 100644
index 0000000000..2b65df96a5
--- /dev/null
+++ b/src/plugins/cppeditor/testcases/reorder-member-impls/different-locations/impl2.cpp_expected
@@ -0,0 +1,6 @@
+#include "header.h"
+
+void S::fifthFunction() {}
+void S::sixthFunction() {}
+void S::seventhFunction() {}
+void S::eighthFunction() {}
diff --git a/src/plugins/cppeditor/testcases/reorder-member-impls/no-out-of-line/header.h b/src/plugins/cppeditor/testcases/reorder-member-impls/no-out-of-line/header.h
new file mode 100644
index 0000000000..cff8945127
--- /dev/null
+++ b/src/plugins/cppeditor/testcases/reorder-member-impls/no-out-of-line/header.h
@@ -0,0 +1,5 @@
+struct S {
+ void firstFunction() {}
+ void secondFunction();
+ void thirdFunction() {}
+};
diff --git a/src/plugins/cppeditor/testcases/reorder-member-impls/no-out-of-line/header.h_expected b/src/plugins/cppeditor/testcases/reorder-member-impls/no-out-of-line/header.h_expected
new file mode 100644
index 0000000000..cff8945127
--- /dev/null
+++ b/src/plugins/cppeditor/testcases/reorder-member-impls/no-out-of-line/header.h_expected
@@ -0,0 +1,5 @@
+struct S {
+ void firstFunction() {}
+ void secondFunction();
+ void thirdFunction() {}
+};
diff --git a/src/plugins/cppeditor/testcases/reorder-member-impls/no-out-of-line/no-out-of-line.pro b/src/plugins/cppeditor/testcases/reorder-member-impls/no-out-of-line/no-out-of-line.pro
new file mode 100644
index 0000000000..7051801269
--- /dev/null
+++ b/src/plugins/cppeditor/testcases/reorder-member-impls/no-out-of-line/no-out-of-line.pro
@@ -0,0 +1 @@
+HEADERS = header.h
diff --git a/src/plugins/cppeditor/testcases/reorder-member-impls/templates/header.h b/src/plugins/cppeditor/testcases/reorder-member-impls/templates/header.h
new file mode 100644
index 0000000000..08b33a51d9
--- /dev/null
+++ b/src/plugins/cppeditor/testcases/reorder-member-impls/templates/header.h
@@ -0,0 +1,10 @@
+template <typename T> struct S {
+ void firstFunction();
+ template<typename U> void secondFunction(U);
+ template<typename U> void irrelevantFunction(U) {}
+ void thirdFunction();
+};
+
+template<typename T> template<typename U> void S<T>::secondFunction(U) {}
+template<typename T> void S<T>::thirdFunction() {}
+template<typename T> void S<T>::firstFunction() {}
diff --git a/src/plugins/cppeditor/testcases/reorder-member-impls/templates/header.h_expected b/src/plugins/cppeditor/testcases/reorder-member-impls/templates/header.h_expected
new file mode 100644
index 0000000000..f182bbe62d
--- /dev/null
+++ b/src/plugins/cppeditor/testcases/reorder-member-impls/templates/header.h_expected
@@ -0,0 +1,10 @@
+template <typename T> struct S {
+ void firstFunction();
+ template<typename U> void secondFunction(U);
+ template<typename U> void irrelevantFunction(U) {}
+ void thirdFunction();
+};
+
+template<typename T> void S<T>::firstFunction() {}
+template<typename T> template<typename U> void S<T>::secondFunction(U) {}
+template<typename T> void S<T>::thirdFunction() {}
diff --git a/src/plugins/cppeditor/testcases/reorder-member-impls/templates/templates.pro b/src/plugins/cppeditor/testcases/reorder-member-impls/templates/templates.pro
new file mode 100644
index 0000000000..7051801269
--- /dev/null
+++ b/src/plugins/cppeditor/testcases/reorder-member-impls/templates/templates.pro
@@ -0,0 +1 @@
+HEADERS = header.h
diff --git a/src/plugins/cvs/CVS.json.in b/src/plugins/cvs/CVS.json.in
index 6d2f92ea78..c2a0ba126a 100644
--- a/src/plugins/cvs/CVS.json.in
+++ b/src/plugins/cvs/CVS.json.in
@@ -15,7 +15,7 @@
"Category" : "Version Control",
"Description" : "CVS integration.",
"DisabledByDefault" : true,
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/debugger/Debugger.json.in b/src/plugins/debugger/Debugger.json.in
index 06df0489d6..48eeffe7c3 100644
--- a/src/plugins/debugger/Debugger.json.in
+++ b/src/plugins/debugger/Debugger.json.in
@@ -14,7 +14,7 @@
],
"Category" : "Qt Creator",
"Description" : "Debugger integration.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
"Arguments" : [
{
"Name" : "-debug",
diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp
index 83c5a9459b..432205ff02 100644
--- a/src/plugins/debugger/cdb/cdbengine.cpp
+++ b/src/plugins/debugger/cdb/cdbengine.cpp
@@ -282,7 +282,7 @@ void CdbEngine::setupEngine()
DebuggerRunParameters sp = runParameters();
if (terminal()) {
m_effectiveStartMode = AttachToLocalProcess;
- sp.inferior.command = CommandLine();
+ sp.inferior.command = {};
sp.attachPID = ProcessHandle(terminal()->applicationPid());
sp.startMode = AttachToLocalProcess;
sp.useTerminal = false; // Force no terminal.
@@ -1042,6 +1042,8 @@ void CdbEngine::doUpdateLocals(const UpdateParameters &updateParameters)
cmd.arg("partialvar", updateParameters.partialVariable);
cmd.arg("qobjectnames", s.showQObjectNames());
cmd.arg("timestamps", s.logTimeStamps());
+ cmd.arg("qtversion", runParameters().qtVersion);
+ cmd.arg("qtnamespace", runParameters().qtNamespace);
StackFrame frame = stackHandler()->currentFrame();
cmd.arg("context", frame.context);
@@ -2809,10 +2811,6 @@ void CdbEngine::setupScripting(const DebuggerResponse &response)
runCommand({command, ScriptCommand});
}
- DebuggerCommand cmd0("theDumper.setFallbackQtVersion", ScriptCommand);
- cmd0.arg("version", runParameters().fallbackQtVersion);
- runCommand(cmd0);
-
runCommand({"theDumper.loadDumpers(None)", ScriptCommand,
[this](const DebuggerResponse &response) {
watchHandler()->addDumpers(response.data["result"]["dumpers"]);
diff --git a/src/plugins/debugger/commonoptionspage.cpp b/src/plugins/debugger/commonoptionspage.cpp
index bb24d674a0..5739845fe6 100644
--- a/src/plugins/debugger/commonoptionspage.cpp
+++ b/src/plugins/debugger/commonoptionspage.cpp
@@ -157,7 +157,7 @@ CommonSettings::CommonSettings()
};
return Column {
- Group { title("Behavior"), Row { col1, col2, st } },
+ Group { title(Tr::tr("Behavior")), Row { col1, col2, st } },
sourcePathMap,
st
};
diff --git a/src/plugins/debugger/commonoptionspage.h b/src/plugins/debugger/commonoptionspage.h
index 5bf41af7a4..4d391280e8 100644
--- a/src/plugins/debugger/commonoptionspage.h
+++ b/src/plugins/debugger/commonoptionspage.h
@@ -25,7 +25,7 @@ public:
void fromMap(const Utils::Store &map) override;
void toMap(Utils::Store &map) const override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
void readSettings() override;
void writeSettings() const override;
diff --git a/src/plugins/debugger/console/consoleitemdelegate.cpp b/src/plugins/debugger/console/consoleitemdelegate.cpp
index 8f76704b70..cd7ead6de8 100644
--- a/src/plugins/debugger/console/consoleitemdelegate.cpp
+++ b/src/plugins/debugger/console/consoleitemdelegate.cpp
@@ -53,7 +53,6 @@ QColor ConsoleItemDelegate::drawBackground(QPainter *painter, const QRect &rect,
void ConsoleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
- const Utils::Theme *theme = Utils::creatorTheme();
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
painter->save();
@@ -65,23 +64,23 @@ void ConsoleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &o
ConsoleItem::TypeRole).toInt();
switch (type) {
case ConsoleItem::DebugType:
- textColor = theme->color(Utils::Theme::OutputPanes_NormalMessageTextColor);
+ textColor = creatorColor(Utils::Theme::OutputPanes_NormalMessageTextColor);
taskIcon = m_logIcon;
break;
case ConsoleItem::WarningType:
- textColor = theme->color(Utils::Theme::OutputPanes_WarningMessageTextColor);
+ textColor = creatorColor(Utils::Theme::OutputPanes_WarningMessageTextColor);
taskIcon = m_warningIcon;
break;
case ConsoleItem::ErrorType:
- textColor = theme->color(Utils::Theme::OutputPanes_ErrorMessageTextColor);
+ textColor = creatorColor(Utils::Theme::OutputPanes_ErrorMessageTextColor);
taskIcon = m_errorIcon;
break;
case ConsoleItem::InputType:
- textColor = theme->color(Utils::Theme::TextColorNormal);
+ textColor = creatorColor(Utils::Theme::TextColorNormal);
taskIcon = m_prompt;
break;
default:
- textColor = theme->color(Utils::Theme::TextColorNormal);
+ textColor = creatorColor(Utils::Theme::TextColorNormal);
break;
}
diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h
index e82a386311..b5e8a3cba8 100644
--- a/src/plugins/debugger/debuggerengine.h
+++ b/src/plugins/debugger/debuggerengine.h
@@ -188,7 +188,8 @@ public:
QStringList validationErrors;
- int fallbackQtVersion = 0x50200;
+ int qtVersion = 0;
+ QString qtNamespace;
// Common debugger constants.
Utils::FilePath peripheralDescriptionFile;
diff --git a/src/plugins/debugger/debuggeritemmanager.cpp b/src/plugins/debugger/debuggeritemmanager.cpp
index 0aa3b2e769..b93e0800de 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/kitoptionspage.h>
#include <projectexplorer/projectexplorerconstants.h>
@@ -381,7 +379,7 @@ DebuggerItemConfigWidget::DebuggerItemConfigWidget()
// clang-format off
using namespace Layouting;
Form {
- fieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow),
+ fieldGrowthPolicy(int(QFormLayout::AllNonFixedFieldsGrow)),
Tr::tr("Name:"), m_displayNameLineEdit, br,
Tr::tr("Path:"), m_binaryChooser, br,
m_cdbLabel, br,
@@ -488,7 +486,7 @@ void DebuggerItemConfigWidget::binaryPathHasChanged()
tmp.reinitializeFromFile();
return tmp;
}));
- ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(m_updateWatcher.future());
+ Utils::futureSynchronizer()->addFuture(m_updateWatcher.future());
} else {
const DebuggerItem tmp;
setAbis(tmp.abiNames());
diff --git a/src/plugins/debugger/debuggerkitaspect.cpp b/src/plugins/debugger/debuggerkitaspect.cpp
index 02f21c582a..10559a7084 100644
--- a/src/plugins/debugger/debuggerkitaspect.cpp
+++ b/src/plugins/debugger/debuggerkitaspect.cpp
@@ -66,7 +66,7 @@ public:
}
private:
- void addToLayoutImpl(Layouting::LayoutItem &parent) override
+ void addToLayoutImpl(Layouting::Layout &parent) override
{
addMutableAction(m_comboBox);
parent.addItem(m_comboBox);
@@ -329,7 +329,7 @@ public:
}
if (!item.detectionSource().isEmpty() && item.detectionSource() == k->autoDetectionSource())
level = DebuggerItem::MatchLevel(level + 2);
- } else if (rawId.type() == QVariant::String) {
+ } else if (rawId.typeId() == QMetaType::QString) {
// New structure.
if (item.id() == rawId) {
// Detected by ID.
diff --git a/src/plugins/debugger/debuggerrunconfigurationaspect.cpp b/src/plugins/debugger/debuggerrunconfigurationaspect.cpp
index d4f4361f78..dc1919ce8b 100644
--- a/src/plugins/debugger/debuggerrunconfigurationaspect.cpp
+++ b/src/plugins/debugger/debuggerrunconfigurationaspect.cpp
@@ -72,7 +72,7 @@ DebuggerRunConfigurationAspect::DebuggerRunConfigurationAspect(Target *target)
details->setState(DetailsWidget::Expanded);
auto innerPane = new QWidget;
details->setWidget(innerPane);
- builder.addItem(Layouting::noMargin);
+ builder.noMargin();
builder.attachTo(innerPane);
const auto setSummaryText = [this, details] {
diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp
index 503bad4bc2..522191e25b 100644
--- a/src/plugins/debugger/debuggerruncontrol.cpp
+++ b/src/plugins/debugger/debuggerruncontrol.cpp
@@ -898,7 +898,7 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerm
if (QtSupport::QtVersion *baseQtVersion = QtSupport::QtKitAspect::qtVersion(kit)) {
const QVersionNumber qtVersion = baseQtVersion->qtVersion();
- m_runParameters.fallbackQtVersion = 0x10000 * qtVersion.majorVersion()
+ m_runParameters.qtVersion = 0x10000 * qtVersion.majorVersion()
+ 0x100 * qtVersion.minorVersion()
+ qtVersion.microVersion();
}
diff --git a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp
index be983cf584..98b84e2020 100644
--- a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp
+++ b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp
@@ -467,7 +467,7 @@ bool SourcePathMapAspect::isDirty()
return m_internal != m_buffer;
}
-void SourcePathMapAspect::addToLayout(Layouting::LayoutItem &parent)
+void SourcePathMapAspect::addToLayout(Layouting::Layout &parent)
{
QTC_CHECK(!d->m_widget);
d->m_widget = createSubWidget<DebuggerSourcePathMappingWidget>();
diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp
index e4873de775..3abaea75a4 100644
--- a/src/plugins/debugger/gdb/gdbengine.cpp
+++ b/src/plugins/debugger/gdb/gdbengine.cpp
@@ -4054,10 +4054,6 @@ void GdbEngine::handleGdbStarted()
if (!commands.isEmpty())
runCommand({commands});
- DebuggerCommand cmd1("setFallbackQtVersion");
- cmd1.arg("version", rp.fallbackQtVersion);
- runCommand(cmd1);
-
runCommand({"loadDumpers", CB(handlePythonSetup)});
// Reload peripheral register description.
@@ -5133,6 +5129,8 @@ void GdbEngine::doUpdateLocals(const UpdateParameters &params)
cmd.arg("dyntype", s.useDynamicType());
cmd.arg("qobjectnames", s.showQObjectNames());
cmd.arg("timestamps", s.logTimeStamps());
+ cmd.arg("qtversion", runParameters().qtVersion);
+ cmd.arg("qtnamespace", runParameters().qtNamespace);
StackFrame frame = stackHandler()->currentFrame();
cmd.arg("context", frame.context);
diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp
index 3d21f82ca2..d7994be007 100644
--- a/src/plugins/debugger/lldb/lldbengine.cpp
+++ b/src/plugins/debugger/lldb/lldbengine.cpp
@@ -346,10 +346,6 @@ void LldbEngine::handleLldbStarted()
cmd2.flags = Silent;
runCommand(cmd2);
-
- DebuggerCommand cmd0("setFallbackQtVersion");
- cmd0.arg("version", rp.fallbackQtVersion);
- runCommand(cmd0);
}
void LldbEngine::runEngine()
@@ -407,9 +403,10 @@ void LldbEngine::handleResponse(const QString &response)
const QString name = item.name();
if (name == "result") {
QString msg = item["status"].data();
- if (!msg.isEmpty())
+ if (!msg.isEmpty()) {
msg[0] = msg.at(0).toUpper();
- showStatusMessage(msg);
+ showStatusMessage(msg);
+ }
int token = item["token"].toInt();
showMessage(QString("%1^").arg(token), LogOutput);
@@ -767,6 +764,8 @@ void LldbEngine::doUpdateLocals(const UpdateParameters &params)
cmd.arg("partialvar", params.partialVariable);
cmd.arg("qobjectnames", s.showQObjectNames());
cmd.arg("timestamps", s.logTimeStamps());
+ cmd.arg("qtversion", runParameters().qtVersion);
+ cmd.arg("qtnamespace", runParameters().qtNamespace);
StackFrame frame = stackHandler()->currentFrame();
cmd.arg("context", frame.context);
diff --git a/src/plugins/debugger/logwindow.cpp b/src/plugins/debugger/logwindow.cpp
index 482f2605e7..a1ca5e30e8 100644
--- a/src/plugins/debugger/logwindow.cpp
+++ b/src/plugins/debugger/logwindow.cpp
@@ -101,26 +101,25 @@ private:
{
using Utils::Theme;
QTextCharFormat format;
- Theme *theme = Utils::creatorTheme();
switch (channelForChar(text.isEmpty() ? QChar() : text.at(0))) {
case LogInput:
- format.setForeground(theme->color(Theme::Debugger_LogWindow_LogInput));
+ format.setForeground(creatorColor(Theme::Debugger_LogWindow_LogInput));
setFormat(1, text.size(), format);
break;
case LogStatus:
- format.setForeground(theme->color(Theme::Debugger_LogWindow_LogStatus));
+ format.setForeground(creatorColor(Theme::Debugger_LogWindow_LogStatus));
setFormat(1, text.size(), format);
break;
case LogWarning:
- format.setForeground(theme->color(Theme::OutputPanes_WarningMessageTextColor));
+ format.setForeground(creatorColor(Theme::OutputPanes_WarningMessageTextColor));
setFormat(1, text.size(), format);
break;
case LogError:
- format.setForeground(theme->color(Theme::OutputPanes_ErrorMessageTextColor));
+ format.setForeground(creatorColor(Theme::OutputPanes_ErrorMessageTextColor));
setFormat(1, text.size(), format);
break;
case LogTime:
- format.setForeground(theme->color(Theme::Debugger_LogWindow_LogTime));
+ format.setForeground(creatorColor(Theme::Debugger_LogWindow_LogTime));
setFormat(1, text.size(), format);
break;
default:
@@ -152,11 +151,9 @@ public:
private:
void highlightBlock(const QString &text) override
{
- using Utils::Theme;
- Theme *theme = Utils::creatorTheme();
if (text.size() > 3 && text.at(2) == ':') {
QTextCharFormat format;
- format.setForeground(theme->color(Theme::Debugger_LogWindow_LogTime));
+ format.setForeground(creatorColor(Theme::Debugger_LogWindow_LogTime));
setFormat(1, text.size(), format);
}
}
diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp
index dda927f0f1..c214926ec4 100644
--- a/src/plugins/debugger/qml/qmlengine.cpp
+++ b/src/plugins/debugger/qml/qmlengine.cpp
@@ -783,7 +783,7 @@ void QmlEngine::assignValueInDebugger(WatchItem *item,
const QString &expression, const QVariant &editValue)
{
if (!expression.isEmpty()) {
- QTC_CHECK(editValue.typeId() == QVariant::String);
+ QTC_CHECK(editValue.typeId() == QMetaType::QString);
QVariant value;
QString val = editValue.toString();
if (item->type == "boolean")
@@ -861,7 +861,7 @@ static ConsoleItem *constructLogItemTree(const QVariant &result, const QString &
QString text;
ConsoleItem *item = nullptr;
- if (result.typeId() == QVariant::Map) {
+ if (result.typeId() == QMetaType::QVariantMap) {
if (key.isEmpty())
text = "Object";
else
@@ -885,7 +885,7 @@ static ConsoleItem *constructLogItemTree(const QVariant &result, const QString &
item->appendChild(child);
}
- } else if (result.typeId() == QVariant::List) {
+ } else if (result.typeId() == QMetaType::QVariantList) {
if (key.isEmpty())
text = "List";
else
@@ -904,7 +904,7 @@ static ConsoleItem *constructLogItemTree(const QVariant &result, const QString &
if (child)
item->appendChild(child);
}
- } else if (result.canConvert(QVariant::String)) {
+ } else if (result.canConvert(QMetaType::QString)) {
item = new ConsoleItem(ConsoleItem::DefaultType, result.toString());
} else {
item = new ConsoleItem(ConsoleItem::DefaultType, "Unknown Value");
@@ -1356,9 +1356,9 @@ void QmlEnginePrivate::scripts(int types, const QList<int> ids, bool includeSour
if (includeSource)
cmd.arg(INCLUDESOURCE, includeSource);
- if (filter.typeId() == QVariant::String)
+ if (filter.typeId() == QMetaType::QString)
cmd.arg(FILTER, filter.toString());
- else if (filter.typeId() == QVariant::Int)
+ else if (filter.typeId() == QMetaType::Int)
cmd.arg(FILTER, filter.toInt());
else
QTC_CHECK(!filter.isValid());
@@ -2041,7 +2041,7 @@ StackFrame QmlEnginePrivate::extractStackFrame(const QVariant &bodyVal)
}
auto extractString = [this](const QVariant &item) {
- return (item.typeId() == QVariant::String ? item : extractData(item).value).toString();
+ return (item.typeId() == QMetaType::QString ? item : extractData(item).value).toString();
};
stackFrame.function = extractString(body.value("func"));
diff --git a/src/plugins/debugger/qml/qmlinspectoragent.cpp b/src/plugins/debugger/qml/qmlinspectoragent.cpp
index 9e30d2ad83..2769d49cd6 100644
--- a/src/plugins/debugger/qml/qmlinspectoragent.cpp
+++ b/src/plugins/debugger/qml/qmlinspectoragent.cpp
@@ -220,7 +220,7 @@ void QmlInspectorAgent::onResult(quint32 queryId, const QVariant &value,
if (m_objectTreeQueryIds.contains(queryId)) {
m_objectTreeQueryIds.removeOne(queryId);
- if (value.typeId() == QVariant::List) {
+ if (value.typeId() == QMetaType::QVariantList) {
const QVariantList objList = value.toList();
for (const QVariant &var : objList) {
// TODO: check which among the list is the actual
@@ -289,7 +289,7 @@ static void sortChildrenIfNecessary(WatchItem *propertiesWatch)
static bool insertChildren(WatchItem *parent, const QVariant &value)
{
switch (value.typeId()) {
- case QVariant::Map: {
+ case QMetaType::QVariantMap: {
const QVariantMap map = value.toMap();
for (auto it = map.begin(), end = map.end(); it != end; ++it) {
auto child = new WatchItem;
@@ -303,7 +303,7 @@ static bool insertChildren(WatchItem *parent, const QVariant &value)
sortChildrenIfNecessary(parent);
return true;
}
- case QVariant::List: {
+ case QMetaType::QVariantList: {
const QVariantList list = value.toList();
for (int i = 0, end = list.size(); i != end; ++i) {
auto child = new WatchItem;
diff --git a/src/plugins/debugger/watchdata.h b/src/plugins/debugger/watchdata.h
index 672ded5e7c..914e92decf 100644
--- a/src/plugins/debugger/watchdata.h
+++ b/src/plugins/debugger/watchdata.h
@@ -33,7 +33,7 @@ public:
QString toToolTip() const;
QVariant editValue() const;
- int editType() const;
+ QMetaType::Type editType() const;
static const qint64 InvalidId = -1;
constexpr static char loadMoreName[] = "<load more>";
diff --git a/src/plugins/debugger/watchdelegatewidgets.cpp b/src/plugins/debugger/watchdelegatewidgets.cpp
index b9337f2eea..ecaa0fda91 100644
--- a/src/plugins/debugger/watchdelegatewidgets.cpp
+++ b/src/plugins/debugger/watchdelegatewidgets.cpp
@@ -189,25 +189,25 @@ void IntegerWatchLineEdit::setModelData(const QVariant &v)
qDebug(">IntegerLineEdit::setModelData(%s, '%s'): base=%d, signed=%d, bigint=%d",
v.typeName(), qPrintable(v.toString()),
base(), isSigned(), isBigInt());
- switch (v.type()) {
- case QVariant::Int:
- case QVariant::LongLong: {
+ switch (v.typeId()) {
+ case QMetaType::Int:
+ case QMetaType::LongLong: {
const qint64 iv = v.toLongLong();
setSigned(true);
setText(QString::number(iv, base()));
}
break;
- case QVariant::UInt:
- case QVariant::ULongLong: {
+ case QMetaType::UInt:
+ case QMetaType::ULongLong: {
const quint64 iv = v.toULongLong();
setSigned(false);
setText(QString::number(iv, base()));
}
break;
- case QVariant::ByteArray:
+ case QMetaType::QByteArray:
setNumberText(QString::fromLatin1(v.toByteArray()));
break;
- case QVariant::String:
+ case QMetaType::QString:
setNumberText(v.toString());
break;
default:
@@ -243,12 +243,12 @@ void FloatWatchLineEdit::setModelData(const QVariant &v)
if (debug)
qDebug("FloatWatchLineEdit::setModelData(%s, '%s')",
v.typeName(), qPrintable(v.toString()));
- switch (v.type()) {
- case QVariant::Double:
- case QVariant::String:
+ switch (v.typeId()) {
+ case QMetaType::Double:
+ case QMetaType::QString:
setText(v.toString());
break;
- case QVariant::ByteArray:
+ case QMetaType::QByteArray:
setText(QString::fromLatin1(v.toByteArray()));
break;
default:
@@ -259,17 +259,17 @@ void FloatWatchLineEdit::setModelData(const QVariant &v)
}
}
-WatchLineEdit *WatchLineEdit::create(QVariant::Type t, QWidget *parent)
+WatchLineEdit *WatchLineEdit::create(QMetaType::Type typeId, QWidget *parent)
{
- switch (t) {
- case QVariant::Bool:
- case QVariant::Int:
- case QVariant::UInt:
- case QVariant::LongLong:
- case QVariant::ULongLong:
+ switch (typeId) {
+ case QMetaType::Bool:
+ case QMetaType::Int:
+ case QMetaType::UInt:
+ case QMetaType::LongLong:
+ case QMetaType::ULongLong:
return new IntegerWatchLineEdit(parent);
break;
- case QVariant::Double:
+ case QMetaType::Double:
return new FloatWatchLineEdit(parent);
default:
break;
@@ -297,14 +297,14 @@ void BooleanComboBox::setModelData(const QVariant &v)
qDebug("BooleanComboBox::setModelData(%s, '%s')", v.typeName(), qPrintable(v.toString()));
bool value = false;
- switch (v.type()) {
- case QVariant::Bool:
+ switch (v.typeId()) {
+ case QMetaType::Bool:
value = v.toBool();
break;
- case QVariant::Int:
- case QVariant::UInt:
- case QVariant::LongLong:
- case QVariant::ULongLong:
+ case QMetaType::Int:
+ case QMetaType::UInt:
+ case QMetaType::LongLong:
+ case QMetaType::ULongLong:
value = v.toInt() != 0;
break;
default:
diff --git a/src/plugins/debugger/watchdelegatewidgets.h b/src/plugins/debugger/watchdelegatewidgets.h
index 68b1dd2403..54b85c6257 100644
--- a/src/plugins/debugger/watchdelegatewidgets.h
+++ b/src/plugins/debugger/watchdelegatewidgets.h
@@ -27,7 +27,7 @@ public:
virtual QVariant modelData() const;
virtual void setModelData(const QVariant &);
- static WatchLineEdit *create(QVariant::Type t, QWidget *parent = nullptr);
+ static WatchLineEdit *create(QMetaType::Type typeId, QWidget *parent = nullptr);
};
/* Watch delegate line edit for integer numbers based on quint64/qint64.
diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp
index 3f47df4b88..c5600cd9ee 100644
--- a/src/plugins/debugger/watchhandler.cpp
+++ b/src/plugins/debugger/watchhandler.cpp
@@ -844,33 +844,33 @@ static inline quint64 pointerValue(QString data)
}
// Return the type used for editing
-int WatchItem::editType() const
+QMetaType::Type WatchItem::editType() const
{
if (type == "bool")
- return QVariant::Bool;
+ return QMetaType::Bool;
if (isIntType(type))
- return type.contains('u') ? QVariant::ULongLong : QVariant::LongLong;
+ return type.contains('u') ? QMetaType::ULongLong : QMetaType::LongLong;
if (isFloatType(type))
- return QVariant::Double;
+ return QMetaType::Double;
// Check for pointers using hex values (0xAD00 "Hallo")
if (isPointerType(type) && value.startsWith("0x"))
- return QVariant::ULongLong;
- return QVariant::String;
+ return QMetaType::ULongLong;
+ return QMetaType::QString;
}
// Convert to editable (see above)
QVariant WatchItem::editValue() const
{
switch (editType()) {
- case QVariant::Bool:
+ case QMetaType::Bool:
return value != "0" && value != "false";
- case QVariant::ULongLong:
+ case QMetaType::ULongLong:
if (isPointerType(type)) // Fix pointer values (0xAD00 "Hallo" -> 0xAD00)
return QVariant(pointerValue(value));
return QVariant(value.toULongLong());
- case QVariant::LongLong:
+ case QMetaType::LongLong:
return QVariant(value.toLongLong());
- case QVariant::Double:
+ case QMetaType::Double:
return QVariant(value.toDouble());
default:
break;
@@ -978,7 +978,7 @@ static QColor valueColor(const WatchItem *item, int column)
color = Theme::Debugger_WatchItem_ValueChanged;
}
}
- return creatorTheme()->color(color);
+ return creatorColor(color);
}
static DisplayFormats typeFormatList(const WatchItem *item)
@@ -2888,8 +2888,8 @@ public:
// Value column: Custom editor. Apply integer-specific settings.
if (index.column() == 1) {
- auto editType = QVariant::Type(item->editType());
- if (editType == QVariant::Bool)
+ const QMetaType::Type editType = item->editType();
+ if (editType == QMetaType::Bool)
return new BooleanComboBox(parent);
WatchLineEdit *edit = WatchLineEdit::create(editType, parent);
diff --git a/src/plugins/designer/CMakeLists.txt b/src/plugins/designer/CMakeLists.txt
index c7b3cdac0d..d9da4cfaf4 100644
--- a/src/plugins/designer/CMakeLists.txt
+++ b/src/plugins/designer/CMakeLists.txt
@@ -5,12 +5,12 @@ if(Qt6_VERSION VERSION_GREATER_EQUAL 6.7.0)
{
\"Name\" : \"-designer-qt-pluginpath\",
\"Parameter\" : \"path\",
- \"Description\" : \"Override the default search path for Qt Designer plugins\"
+ \"Description\" : \"Override the default search path for Qt Widgets Designer plugins\"
},
{
\"Name\" : \"-designer-pluginpath\",
\"Parameter\" : \"path\",
- \"Description\" : \"Add a custom search path for Qt Designer plugins\"
+ \"Description\" : \"Add a custom search path for Qt Widgets Designer plugins\"
}
],")
else()
@@ -19,7 +19,7 @@ else()
{
\"Name\" : \"-designer-qt-pluginpath\",
\"Parameter\" : \"path\",
- \"Description\" : \"Override the default search path for Qt Designer plugins\"
+ \"Description\" : \"Override the default search path for Qt Widgets Designer plugins\"
}
],")
endif()
diff --git a/src/plugins/designer/Designer.json.in b/src/plugins/designer/Designer.json.in
index 2d03527c30..1038fe8983 100644
--- a/src/plugins/designer/Designer.json.in
+++ b/src/plugins/designer/Designer.json.in
@@ -13,8 +13,8 @@
"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" : "Qt Creator",
- "Description" : "Qt Designer integration.",
- "Url" : "http://www.qt.io",
+ "Description" : "Qt Widgets Designer integration.",
+ "Url" : "https://www.qt.io",
${DESIGNER_PLUGIN_ARGUMENTS}
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/designer/cpp/formclasswizarddialog.cpp b/src/plugins/designer/cpp/formclasswizarddialog.cpp
index cc912683f6..765bf470a0 100644
--- a/src/plugins/designer/cpp/formclasswizarddialog.cpp
+++ b/src/plugins/designer/cpp/formclasswizarddialog.cpp
@@ -27,7 +27,7 @@ FormClassWizardDialog::FormClassWizardDialog(const Core::BaseFileWizardFactory *
m_formPage(new FormTemplateWizardPage),
m_classPage(new FormClassWizardPage)
{
- setWindowTitle(Tr::tr("Qt Designer Form Class"));
+ setWindowTitle(Tr::tr("Qt Widgets Designer Form Class"));
setPage(FormPageId, m_formPage);
setPage(ClassPageId, m_classPage);
diff --git a/src/plugins/designer/designer.qbs b/src/plugins/designer/designer.qbs
index 7fb484226b..38cfa5aa0d 100644
--- a/src/plugins/designer/designer.qbs
+++ b/src/plugins/designer/designer.qbs
@@ -32,12 +32,12 @@ QtcPlugin {
{\n\
\"Name\" : \"-designer-qt-pluginpath\",\n\
\"Parameter\" : \"path\",\n\
- \"Description\" : \"Override the default search path for Qt Designer plugins\"\n\
+ \"Description\" : \"Override the default search path for Qt Widgets Designer plugins\"\n\
},\n\
{\n\
\"Name\" : \"-designer-pluginpath\",\n\
\"Parameter\" : \"path\",\n\
- \"Description\" : \"Add a custom search path for Qt Designer plugins\"\n\
+ \"Description\" : \"Add a custom search path for Qt Widgets Designer plugins\"\n\
}\n\
],"})
diff --git a/src/plugins/designer/designerplugin.cpp b/src/plugins/designer/designerplugin.cpp
index e355379727..cc7b43a0a6 100644
--- a/src/plugins/designer/designerplugin.cpp
+++ b/src/plugins/designer/designerplugin.cpp
@@ -139,11 +139,13 @@ class DesignerPlugin final : public ExtensionSystem::IPlugin
IWizardFactory *wizard = new FormClassWizard;
wizard->setCategory(Core::Constants::WIZARD_CATEGORY_QT);
wizard->setDisplayCategory(::Core::Tr::tr(Core::Constants::WIZARD_TR_CATEGORY_QT));
- wizard->setDisplayName(Tr::tr("Qt Designer Form Class"));
+ wizard->setDisplayName(Tr::tr("Qt Widgets Designer Form Class"));
wizard->setIcon({}, "ui/h");
wizard->setId("C.FormClass");
- wizard->setDescription(Tr::tr("Creates a Qt Designer form along with a matching class (C++ header and source file) "
- "for implementation purposes. You can add the form and class to an existing Qt Widget Project."));
+ wizard->setDescription(Tr::tr("Creates a Qt Widgets Designer form along with a matching "
+ "class (C++ header and source file) "
+ "for implementation purposes. You can add the form and "
+ "class to an existing Qt Widget Project."));
return wizard;
});
diff --git a/src/plugins/designer/formeditor.cpp b/src/plugins/designer/formeditor.cpp
index 46afe017d0..aaf4a921dd 100644
--- a/src/plugins/designer/formeditor.cpp
+++ b/src/plugins/designer/formeditor.cpp
@@ -370,7 +370,7 @@ void FormEditorData::fullInit()
initDesignerSubWindows();
m_integration = new QtCreatorIntegration(m_formeditor, this);
m_formeditor->setIntegration(m_integration);
- // Connect Qt Designer help request to HelpManager.
+ // Connect Qt Widgets Designer help request to HelpManager.
QObject::connect(m_integration, &QtCreatorIntegration::creatorHelpRequested,
HelpManager::Signals::instance(),
[](const QUrl &url) { HelpManager::showHelpUrl(url, HelpManager::HelpModeAlways); });
@@ -657,7 +657,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..."), d);
+ m_actionAboutPlugins = new QAction(Tr::tr("About Qt Widgets Designer Plugins..."), d);
addToolAction(m_actionAboutPlugins, m_contexts, "FormEditor.AboutPlugins", mformtools,
QString(), Core::Constants::G_DEFAULT_THREE);
QObject::connect(m_actionAboutPlugins, &QAction::triggered,
diff --git a/src/plugins/designer/formeditor.h b/src/plugins/designer/formeditor.h
index e3bf9c8ebf..0419789f5d 100644
--- a/src/plugins/designer/formeditor.h
+++ b/src/plugins/designer/formeditor.h
@@ -35,7 +35,7 @@ namespace Internal {
* 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
+ * The form editor shows a read-only XML editor in edit mode and Qt Widgets Designer
* in Design mode. */
enum InitializationStage {
diff --git a/src/plugins/designer/formeditorstack.cpp b/src/plugins/designer/formeditorstack.cpp
index 1bccd34178..6631de9a4c 100644
--- a/src/plugins/designer/formeditorstack.cpp
+++ b/src/plugins/designer/formeditorstack.cpp
@@ -24,6 +24,19 @@
#include <QDebug>
#include <QRect>
+/*!
+ \class Designer::Internal::FormEditorStack
+ \inmodule QtCreator
+ \internal
+
+ \brief The FormEditorStack class maintains a stack of \QD form windows embedded
+ into a scroll area and their associated XML editors.
+
+ Takes care of updating the XML editor once design mode is left.
+ Also updates the mainc ontainer resize handles when the active form
+ window changes.
+*/
+
namespace Designer {
namespace Internal {
diff --git a/src/plugins/designer/formeditorstack.h b/src/plugins/designer/formeditorstack.h
index 0f6425e96b..125ae2940c 100644
--- a/src/plugins/designer/formeditorstack.h
+++ b/src/plugins/designer/formeditorstack.h
@@ -20,11 +20,6 @@ namespace Core { class IEditor; }
namespace Designer {
namespace Internal {
-/* FormEditorStack: Maintains a stack of Qt Designer form windows embedded
- * into a scrollarea and their associated XML editors.
- * Takes care of updating the XML editor once design mode is left.
- * Also updates the maincontainer resize handles when the active form
- * window changes. */
class FormEditorStack : public QStackedWidget
{
Q_OBJECT
diff --git a/src/plugins/designer/formtemplatewizardpage.cpp b/src/plugins/designer/formtemplatewizardpage.cpp
index 68e3fb1711..cfe02e5263 100644
--- a/src/plugins/designer/formtemplatewizardpage.cpp
+++ b/src/plugins/designer/formtemplatewizardpage.cpp
@@ -45,7 +45,7 @@ Utils::WizardPage *FormPageFactory::create(ProjectExplorer::JsonWizard *wizard,
bool FormPageFactory::validateData(Utils::Id typeId, const QVariant &data, QString *errorMessage)
{
QTC_ASSERT(canCreate(typeId), return false);
- if (!data.isNull() && (data.type() != QVariant::Map || !data.toMap().isEmpty())) {
+ if (!data.isNull() && (data.typeId() != QMetaType::QVariantMap || !data.toMap().isEmpty())) {
*errorMessage = ::ProjectExplorer::Tr::tr(
"\"data\" for a \"Form\" page needs to be unset or an empty object.");
return false;
diff --git a/src/plugins/designer/formtemplatewizardpage.h b/src/plugins/designer/formtemplatewizardpage.h
index 96bcb2c74e..40f0434221 100644
--- a/src/plugins/designer/formtemplatewizardpage.h
+++ b/src/plugins/designer/formtemplatewizardpage.h
@@ -24,7 +24,7 @@ public:
bool validateData(Utils::Id typeId, const QVariant &data, QString *errorMessage) override;
};
-// A wizard page embedding Qt Designer's QDesignerNewFormWidgetInterface
+// A wizard page embedding Qt Widgets Designer's QDesignerNewFormWidgetInterface
// widget.
// Sets FormContents property.
diff --git a/src/plugins/designer/qtcreatorintegration.cpp b/src/plugins/designer/qtcreatorintegration.cpp
index 2da21f0e54..5e6d890095 100644
--- a/src/plugins/designer/qtcreatorintegration.cpp
+++ b/src/plugins/designer/qtcreatorintegration.cpp
@@ -627,10 +627,7 @@ bool QtCreatorIntegration::navigateToSlot(const QString &objectName,
const RefactoringFilePtr file = refactoring.file(location.filePath());
const int insertionPos = Utils::Text::positionInText(file->document(),
location.line(), location.column());
- ChangeSet changeSet;
- changeSet.insert(insertionPos, definition);
- file->setChangeSet(changeSet);
- file->apply();
+ file->apply(ChangeSet::makeInsert(insertionPos, definition));
const int indentationPos = file->document()->toPlainText().indexOf('}', insertionPos) - 1;
QTextCursor cursor(editor->textDocument()->document());
cursor.setPosition(indentationPos);
diff --git a/src/plugins/designer/qtdesignerformclasscodegenerator.cpp b/src/plugins/designer/qtdesignerformclasscodegenerator.cpp
index d935079cf4..677494658b 100644
--- a/src/plugins/designer/qtdesignerformclasscodegenerator.cpp
+++ b/src/plugins/designer/qtdesignerformclasscodegenerator.cpp
@@ -103,7 +103,7 @@ bool QtDesignerFormClassCodeGenerator::generateCpp(const FormClassWizardParamete
Utils::writeIncludeFileDirective("QtGui/" + formBaseClass, true, headerStr);
headerStr << "#endif\n";
} else {
- Utils::writeIncludeFileDirective("QtGui/" + formBaseClass, true, headerStr);
+ Utils::writeIncludeFileDirective("QtWidgets/" + formBaseClass, true, headerStr);
}
} else {
Utils::writeIncludeFileDirective(formBaseClass, true, headerStr);
diff --git a/src/plugins/diffeditor/DiffEditor.json.in b/src/plugins/diffeditor/DiffEditor.json.in
index 8fc93667da..6adc01c199 100644
--- a/src/plugins/diffeditor/DiffEditor.json.in
+++ b/src/plugins/diffeditor/DiffEditor.json.in
@@ -13,6 +13,6 @@
"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" : "Diff editor component.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/diffeditor/diffeditorplugin.cpp b/src/plugins/diffeditor/diffeditorplugin.cpp
index da2e4daaf2..22492b5123 100644
--- a/src/plugins/diffeditor/diffeditorplugin.cpp
+++ b/src/plugins/diffeditor/diffeditorplugin.cpp
@@ -13,8 +13,6 @@
#include <coreplugin/editormanager/documentmodel.h>
#include <coreplugin/editormanager/editormanager.h>
-#include <extensionsystem/pluginmanager.h>
-
#include <texteditor/textdocument.h>
#include <utils/async.h>
@@ -121,7 +119,6 @@ DiffFilesController::DiffFilesController(IDocument *document)
const auto onDiffSetup = [this, reloadInput = inputList.at(i)](Async<FileData> &async) {
async.setConcurrentCallData(
DiffFile(ignoreWhitespace(), contextLineCount()), reloadInput);
- async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
};
const auto onDiffDone = [outputList, i](const Async<FileData> &async) {
diff --git a/src/plugins/diffeditor/selectabletexteditorwidget.cpp b/src/plugins/diffeditor/selectabletexteditorwidget.cpp
index bcb3ee0381..af7ed7f3d1 100644
--- a/src/plugins/diffeditor/selectabletexteditorwidget.cpp
+++ b/src/plugins/diffeditor/selectabletexteditorwidget.cpp
@@ -91,12 +91,12 @@ DiffSelections SelectableTextEditorWidget::polishedSelections(const DiffSelectio
continue;
int j = 0;
- while (j < workingList.count()) {
+ while (j < workingList.size()) {
const DiffSelection existingSelection = workingList.takeAt(j);
const QList<DiffSelection> newSelection = subtractSelection(existingSelection, diffSelection);
- for (int k = 0; k < newSelection.count(); k++)
+ for (int k = 0; k < newSelection.size(); k++)
workingList.insert(j + k, newSelection.at(k));
- j += newSelection.count();
+ j += newSelection.size();
}
workingList.append(diffSelection);
}
@@ -126,8 +126,8 @@ void SelectableTextEditorWidget::paintBlock(QPainter *painter,
QTextLayout::FormatRange formatRange;
formatRange.start = qMax(0, diffSelection.start);
const int end = diffSelection.end < 0
- ? block.text().count() + 1
- : qMin(block.text().count(), diffSelection.end);
+ ? block.text().size() + 1
+ : qMin(block.text().size(), diffSelection.end);
formatRange.length = end - formatRange.start;
formatRange.format = *diffSelection.format;
diff --git a/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp b/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp
index ecdb87f80b..65fde112c3 100644
--- a/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp
+++ b/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp
@@ -12,8 +12,6 @@
#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>
@@ -868,7 +866,6 @@ void SideBySideDiffEditorWidget::restoreState()
void SideBySideDiffEditorWidget::showDiff()
{
m_asyncTask.reset(new Async<SideBySideShowResults>());
- m_asyncTask->setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
m_controller.setBusyShowing(true);
connect(m_asyncTask.get(), &AsyncBase::done, this, [this] {
diff --git a/src/plugins/diffeditor/unifieddiffeditorwidget.cpp b/src/plugins/diffeditor/unifieddiffeditorwidget.cpp
index 31d8e1b190..0b34db53d9 100644
--- a/src/plugins/diffeditor/unifieddiffeditorwidget.cpp
+++ b/src/plugins/diffeditor/unifieddiffeditorwidget.cpp
@@ -10,8 +10,6 @@
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
-#include <extensionsystem/pluginmanager.h>
-
#include <texteditor/fontsettings.h>
#include <texteditor/textdocument.h>
#include <texteditor/texteditorsettings.h>
@@ -451,7 +449,6 @@ void UnifiedDiffEditorWidget::showDiff()
}
m_asyncTask.reset(new Async<UnifiedShowResult>());
- m_asyncTask->setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
m_controller.setBusyShowing(true);
connect(m_asyncTask.get(), &AsyncBase::done, this, [this] {
if (m_asyncTask->isCanceled() || !m_asyncTask->isResultAvailable()) {
diff --git a/src/plugins/docker/Docker.json.in b/src/plugins/docker/Docker.json.in
index 6fc896f803..3d7bc7f1fc 100644
--- a/src/plugins/docker/Docker.json.in
+++ b/src/plugins/docker/Docker.json.in
@@ -14,6 +14,6 @@
],
"Experimental" : true,
"Description" : "Basic support for Docker",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/docker/dockerapi.cpp b/src/plugins/docker/dockerapi.cpp
index 537b6c66f3..ea62fd8811 100644
--- a/src/plugins/docker/dockerapi.cpp
+++ b/src/plugins/docker/dockerapi.cpp
@@ -41,7 +41,7 @@ bool DockerApi::canConnect()
bool result = false;
- process.setCommand(CommandLine(dockerExe, QStringList{"info"}));
+ process.setCommand({dockerExe, {"info"}});
connect(&process, &Process::done, [&process, &result] {
qCInfo(dockerApiLog) << "'docker info' result:\n" << qPrintable(process.allOutput());
if (process.result() == ProcessResult::FinishedWithSuccess)
@@ -115,8 +115,7 @@ QFuture<Utils::expected_str<QList<Network>>> DockerApi::networks()
if (dockerExe.isEmpty() || !dockerExe.isExecutableFile())
return make_unexpected(Tr::tr("Docker executable not found"));
- process.setCommand(
- CommandLine(dockerExe, QStringList{"network", "ls", "--format", "{{json .}}"}));
+ process.setCommand({dockerExe, {"network", "ls", "--format", "{{json .}}"}});
process.runBlocking();
if (process.result() != ProcessResult::FinishedWithSuccess) {
diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp
index d225bbf6e6..464a76be98 100644
--- a/src/plugins/docker/dockerdevice.cpp
+++ b/src/plugins/docker/dockerdevice.cpp
@@ -605,7 +605,7 @@ DockerDevice::DockerDevice(std::unique_ptr<DockerDeviceSettings> deviceSettings)
proc.setTerminalMode(TerminalMode::Detached);
proc.setEnvironment(env);
proc.setWorkingDirectory(workingDir);
- proc.setCommand({*shell, {}});
+ proc.setCommand(CommandLine{*shell});
proc.start();
return {};
@@ -1057,13 +1057,13 @@ expected_str<void> DockerDevicePrivate::fetchSystemEnviroment()
QString stdErr;
if (m_shell && m_shell->state() == DeviceShell::State::Succeeded) {
- const RunResult result = runInShell({"env", {}});
+ const RunResult result = runInShell(CommandLine{"env"});
const QString out = QString::fromUtf8(result.stdOut);
m_cachedEnviroment = Environment(out.split('\n', Qt::SkipEmptyParts), q->osType());
stdErr = QString::fromUtf8(result.stdErr);
} else {
Process proc;
- proc.setCommand(withDockerExecCmd({"env", {}}));
+ proc.setCommand(withDockerExecCmd(CommandLine{"env"}));
proc.start();
proc.waitForFinished();
const QString remoteOutput = proc.cleanedStdOut();
diff --git a/src/plugins/effectcomposer/EffectComposer.json.in b/src/plugins/effectcomposer/EffectComposer.json.in
index 48c6955796..619fda7f01 100644
--- a/src/plugins/effectcomposer/EffectComposer.json.in
+++ b/src/plugins/effectcomposer/EffectComposer.json.in
@@ -3,13 +3,13 @@
"Version" : "${IDE_VERSION}",
"CompatVersion" : "${IDE_VERSION_COMPAT}",
"Vendor" : "The Qt Company Ltd",
- "Copyright" : "(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd",
+ "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd",
"License" : [ "Commercial Usage",
"",
"Licensees holding valid Qt Enterprise licenses may use this plugin in accordance with the Qt Enterprise License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company."
],
"Description" : "Plugin for Effect Composer.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
"DisabledByDefault" : true,
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/effectcomposer/compositionnode.cpp b/src/plugins/effectcomposer/compositionnode.cpp
index d939e2283a..a69cd00e07 100644
--- a/src/plugins/effectcomposer/compositionnode.cpp
+++ b/src/plugins/effectcomposer/compositionnode.cpp
@@ -128,7 +128,7 @@ void CompositionNode::parse(const QString &effectName, const QString &qenPath, c
// parse properties
QJsonArray jsonProps = json.value("properties").toArray();
- for (const auto /*QJsonValueRef*/ &prop : jsonProps) {
+ for (const QJsonValueConstRef &prop : jsonProps) {
const auto uniform = new Uniform(effectName, prop.toObject(), qenPath);
m_unifomrsModel.addUniform(uniform);
m_uniforms.append(uniform);
diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp
index a983072334..43efefe51d 100644
--- a/src/plugins/effectcomposer/effectcomposermodel.cpp
+++ b/src/plugins/effectcomposer/effectcomposermodel.cpp
@@ -21,6 +21,7 @@
#include <QByteArrayView>
#include <QLibraryInfo>
+#include <QTemporaryDir>
#include <QVector2D>
namespace EffectComposer {
@@ -48,6 +49,7 @@ static bool writeToFile(const QByteArray &buf, const QString &filename, FileType
EffectComposerModel::EffectComposerModel(QObject *parent)
: QAbstractListModel{parent}
+ , m_shaderDir(QDir::tempPath() + "/qds_ec_XXXXXX")
{
m_rebakeTimer.setSingleShot(true);
connect(&m_rebakeTimer, &QTimer::timeout, this, &EffectComposerModel::bakeShaders);
@@ -516,11 +518,9 @@ QJsonObject nodeToJson(const CompositionNode &node)
uniformObject.insert("type", type);
- if (uniform->type() == Uniform::Type::Define || uniform->type() == Uniform::Type::Channel) {
- QString controlType = Uniform::stringFromType(uniform->controlType());
- if (controlType != type)
- uniformObject.insert("controlType", controlType);
- }
+ QString controlType = Uniform::stringFromType(uniform->controlType());
+ if (controlType != type)
+ uniformObject.insert("controlType", controlType);
if (!uniform->displayName().isEmpty())
uniformObject.insert("displayName", QString(uniform->displayName()));
@@ -542,7 +542,8 @@ QJsonObject nodeToJson(const CompositionNode &node)
if (!uniform->description().isEmpty())
uniformObject.insert("description", uniform->description());
if (uniform->type() == Uniform::Type::Float
- || uniform->type() == Uniform::Type::Int
+ || (uniform->type() == Uniform::Type::Int
+ && uniform->controlType() != Uniform::Type::Channel)
|| uniform->type() == Uniform::Type::Vec2
|| uniform->type() == Uniform::Type::Vec3
|| uniform->type() == Uniform::Type::Vec4
@@ -1713,37 +1714,33 @@ void EffectComposerModel::updateCustomUniforms()
m_exportedEffectPropertiesString = exportedEffectPropertiesString;
}
-void EffectComposerModel::createFiles()
+void EffectComposerModel::initShaderDir()
{
- if (QFileInfo::exists(m_vertexShaderFilename))
- QFile(m_vertexShaderFilename).remove();
- if (QFileInfo::exists(m_fragmentShaderFilename))
- QFile(m_fragmentShaderFilename).remove();
- if (QFileInfo::exists(m_vertexShaderPreviewFilename))
- QFile(m_vertexShaderPreviewFilename).remove();
- if (QFileInfo::exists(m_fragmentShaderPreviewFilename))
- QFile(m_fragmentShaderPreviewFilename).remove();
+ static int count = 0;
+ static const QString fileNameTemplate = "%1_%2.%3";
+ const QString countStr = QString::number(count);
+
+ auto resetFile = [&countStr, this](QString &fileName, const QString prefix, const QString suffix) {
+ // qsb generation is done in separate process, so it is not guaranteed all of the old files
+ // get deleted here, as they may not exist yet. Any dangling files will be deleted at
+ // application shutdown, when the temporary directory is destroyed.
+ if (!fileName.isEmpty()) {
+ QFile file(fileName);
+ if (file.exists())
+ file.remove();
+ }
- auto vertexShaderFile = QTemporaryFile(QDir::tempPath() + "/dsem_XXXXXX.vert.qsb");
- auto fragmentShaderFile = QTemporaryFile(QDir::tempPath() + "/dsem_XXXXXX.frag.qsb");
- auto vertexShaderPreviewFile = QTemporaryFile(QDir::tempPath() + "/dsem_prev_XXXXXX.vert.qsb");
- auto fragmentShaderPreviewFile = QTemporaryFile(QDir::tempPath() + "/dsem_prev_XXXXXX.frag.qsb");
+ fileName = m_shaderDir.filePath(fileNameTemplate.arg(prefix, countStr, suffix));
+ };
- m_vertexSourceFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.vert");
- m_fragmentSourceFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.frag");
+ resetFile(m_vertexSourceFilename, "source", "vert");
+ resetFile(m_fragmentSourceFilename, "source", "frag");
+ resetFile(m_vertexShaderFilename, "compiled", "vert.qsb");
+ resetFile(m_fragmentShaderFilename, "compiled", "frag.qsb");
+ resetFile(m_vertexShaderPreviewFilename, "compiled_prev", "vert.qsb");
+ resetFile(m_fragmentShaderPreviewFilename, "compiled_prev", "frag.qsb");
- if (!m_vertexSourceFile.open() || !m_fragmentSourceFile.open()
- || !vertexShaderFile.open() || !fragmentShaderFile.open()
- || !vertexShaderPreviewFile.open() || !fragmentShaderPreviewFile.open()) {
- qWarning() << "Unable to open temporary files";
- } else {
- m_vertexSourceFilename = m_vertexSourceFile.fileName();
- m_fragmentSourceFilename = m_fragmentSourceFile.fileName();
- m_vertexShaderFilename = vertexShaderFile.fileName();
- m_fragmentShaderFilename = fragmentShaderFile.fileName();
- m_vertexShaderPreviewFilename = vertexShaderPreviewFile.fileName();
- m_fragmentShaderPreviewFilename = fragmentShaderPreviewFile.fileName();
- }
+ ++count;
}
void EffectComposerModel::bakeShaders()
@@ -1756,7 +1753,7 @@ void EffectComposerModel::bakeShaders()
return;
}
- createFiles();
+ initShaderDir();
resetEffectError(ErrorPreprocessor);
if (m_vertexShader == generateVertexShader() && m_fragmentShader == generateFragmentShader()) {
@@ -1775,11 +1772,11 @@ void EffectComposerModel::bakeShaders()
setVertexShader(generateVertexShader());
QString vs = m_vertexShader;
- writeToFile(vs.toUtf8(), m_vertexSourceFile.fileName(), FileType::Text);
+ writeToFile(vs.toUtf8(), m_vertexSourceFilename, FileType::Text);
setFragmentShader(generateFragmentShader());
QString fs = m_fragmentShader;
- writeToFile(fs.toUtf8(), m_fragmentSourceFile.fileName(), FileType::Text);
+ writeToFile(fs.toUtf8(), m_fragmentSourceFilename, FileType::Text);
QtSupport::QtVersion *qtVer = QtSupport::QtKitAspect::qtVersion(target->kit());
if (!qtVer) {
diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h
index 14ef09e8a9..b377ae0618 100644
--- a/src/plugins/effectcomposer/effectcomposermodel.h
+++ b/src/plugins/effectcomposer/effectcomposermodel.h
@@ -12,7 +12,7 @@
#include <QMap>
#include <QRegularExpression>
#include <QSet>
-#include <QTemporaryFile>
+#include <QTemporaryDir>
#include <QTimer>
namespace ProjectExplorer {
@@ -176,7 +176,7 @@ private:
QString getQmlEffectString();
void updateCustomUniforms();
- void createFiles();
+ void initShaderDir();
void bakeShaders();
void saveResources(const QString &name);
@@ -205,8 +205,7 @@ private:
QStringList m_defaultRootVertexShader;
QStringList m_defaultRootFragmentShader;
// Temp files to store shaders sources and binary data
- QTemporaryFile m_fragmentSourceFile;
- QTemporaryFile m_vertexSourceFile;
+ QTemporaryDir m_shaderDir;
QString m_fragmentSourceFilename;
QString m_vertexSourceFilename;
QString m_fragmentShaderFilename;
diff --git a/src/plugins/emacskeys/EmacsKeys.json.in b/src/plugins/emacskeys/EmacsKeys.json.in
index ab3e1e5c26..82c486ee7c 100644
--- a/src/plugins/emacskeys/EmacsKeys.json.in
+++ b/src/plugins/emacskeys/EmacsKeys.json.in
@@ -28,6 +28,6 @@
"",
"Also it's worth mentioning that EmacsKeys plugin forces disabling of menu mnemonics by calling Qt's qt_set_sequence_auto_mnemonic function with false argument. Many of the english menu mnemonics get into the way of typical emacs keys, this includes: Alt+F (File), Alt+B (Build), Alt+W (Window). It's a temporary solution, it remains until there is a better one."
],
- "Url" : "http://nosmileface.ru",
+ "Url" : "https://nosmileface.ru",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/extensionmanager/CMakeLists.txt b/src/plugins/extensionmanager/CMakeLists.txt
index 1afb628ff7..094c1eb7d9 100644
--- a/src/plugins/extensionmanager/CMakeLists.txt
+++ b/src/plugins/extensionmanager/CMakeLists.txt
@@ -9,4 +9,14 @@ add_qtc_plugin(ExtensionManager
extensionmanagerwidget.h
extensionsbrowser.cpp
extensionsbrowser.h
+ extensionsmodel.cpp
+ extensionsmodel.h
+)
+
+extend_qtc_plugin(ExtensionManager
+ CONDITION WITH_TESTS
+ SOURCES
+ extensionmanager_test.cpp
+ extensionmanager_test.h
+ extensionmanager_test.qrc
)
diff --git a/src/plugins/extensionmanager/ExtensionManager.json.in b/src/plugins/extensionmanager/ExtensionManager.json.in
index 1dcc10805b..7cf4fec436 100644
--- a/src/plugins/extensionmanager/ExtensionManager.json.in
+++ b/src/plugins/extensionmanager/ExtensionManager.json.in
@@ -15,6 +15,6 @@
"Category" : "Core",
"Description" : "Extension Manager",
"Experimental": true,
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/extensionmanager/extensionmanager.qbs b/src/plugins/extensionmanager/extensionmanager.qbs
index 062bdfc652..b4d1d1a4b6 100644
--- a/src/plugins/extensionmanager/extensionmanager.qbs
+++ b/src/plugins/extensionmanager/extensionmanager.qbs
@@ -14,5 +14,15 @@ QtcPlugin {
"extensionmanagerwidget.h",
"extensionsbrowser.cpp",
"extensionsbrowser.h",
+ "extensionsmodel.cpp",
+ "extensionsmodel.h",
]
+
+ QtcTestFiles {
+ files: [
+ "extensionmanager_test.h",
+ "extensionmanager_test.cpp",
+ "extensionmanager_test.qrc",
+ ]
+ }
}
diff --git a/src/plugins/extensionmanager/extensionmanager_test.cpp b/src/plugins/extensionmanager/extensionmanager_test.cpp
new file mode 100644
index 0000000000..7c66644ad7
--- /dev/null
+++ b/src/plugins/extensionmanager/extensionmanager_test.cpp
@@ -0,0 +1,40 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "extensionmanager_test.h"
+
+#include "extensionsmodel.h"
+
+#include <utils/fileutils.h>
+
+#include <QTest>
+
+namespace ExtensionManager::Internal {
+
+class ExtensionsModelTest final : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void testRepositoryJsonParser();
+};
+
+void ExtensionsModelTest::testRepositoryJsonParser()
+{
+ ExtensionsModel model;
+ model.setExtensionsJson(testData("defaultpacks"));
+}
+
+QObject *createExtensionsModelTest()
+{
+ return new ExtensionsModelTest;
+}
+
+QByteArray testData(const QString &id)
+{
+ return Utils::FileReader::fetchQrc(":/extensionmanager/testdata/" + id + ".json");
+}
+
+} // ExtensionManager::Internal
+
+#include "extensionmanager_test.moc"
diff --git a/src/plugins/extensionmanager/extensionmanager_test.h b/src/plugins/extensionmanager/extensionmanager_test.h
new file mode 100644
index 0000000000..e913688d0d
--- /dev/null
+++ b/src/plugins/extensionmanager/extensionmanager_test.h
@@ -0,0 +1,14 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QObject>
+
+namespace ExtensionManager::Internal {
+
+QObject *createExtensionsModelTest();
+
+QByteArray testData(const QString &id);
+
+} // namespace ExtensionManager::Internal
diff --git a/src/plugins/extensionmanager/extensionmanager_test.qrc b/src/plugins/extensionmanager/extensionmanager_test.qrc
new file mode 100644
index 0000000000..4c4d59f002
--- /dev/null
+++ b/src/plugins/extensionmanager/extensionmanager_test.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/extensionmanager">
+ <file>testdata/defaultpacks.json</file>
+ <file>testdata/thirdpartyplugins.json</file>
+ </qresource>
+</RCC>
diff --git a/src/plugins/extensionmanager/extensionmanagerplugin.cpp b/src/plugins/extensionmanager/extensionmanagerplugin.cpp
index 22dde816a9..d94d5aa7ac 100644
--- a/src/plugins/extensionmanager/extensionmanagerplugin.cpp
+++ b/src/plugins/extensionmanager/extensionmanagerplugin.cpp
@@ -5,6 +5,9 @@
#include "extensionmanagerconstants.h"
#include "extensionmanagerwidget.h"
+#ifdef WITH_TESTS
+#include "extensionmanager_test.h"
+#endif // WITH_TESTS
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
@@ -15,7 +18,6 @@
#include <coreplugin/imode.h>
#include <extensionsystem/iplugin.h>
-#include <extensionsystem/pluginspec.h>
#include <utils/icon.h>
#include <utils/layoutbuilder.h>
@@ -73,6 +75,10 @@ public:
void initialize() final
{
m_mode = new ExtensionManagerMode;
+
+#ifdef WITH_TESTS
+ addTestCreator(createExtensionsModelTest);
+#endif // WITH_TESTS
}
private:
diff --git a/src/plugins/extensionmanager/extensionmanagerwidget.cpp b/src/plugins/extensionmanager/extensionmanagerwidget.cpp
index d8574a740f..78acb32e28 100644
--- a/src/plugins/extensionmanager/extensionmanagerwidget.cpp
+++ b/src/plugins/extensionmanager/extensionmanagerwidget.cpp
@@ -3,26 +3,40 @@
#include "extensionmanagerwidget.h"
-#include "extensionmanagerconstants.h"
#include "extensionmanagertr.h"
#include "extensionsbrowser.h"
+#include "extensionsmodel.h"
#include <coreplugin/coreconstants.h>
#include <coreplugin/icontext.h>
#include <coreplugin/icore.h>
#include <coreplugin/iwelcomepage.h>
+#include <coreplugin/plugininstallwizard.h>
#include <coreplugin/welcomepagehelper.h>
+#include <extensionsystem/pluginmanager.h>
#include <extensionsystem/pluginspec.h>
+#include <solutions/tasking/networkquery.h>
+#include <solutions/tasking/tasktree.h>
+#include <solutions/tasking/tasktreerunner.h>
+
#include <utils/algorithm.h>
+#include <utils/fileutils.h>
+#include <utils/hostosinfo.h>
#include <utils/icon.h>
+#include <utils/infolabel.h>
#include <utils/layoutbuilder.h>
+#include <utils/networkaccessmanager.h>
#include <utils/stylehelper.h>
+#include <utils/temporarydirectory.h>
#include <utils/utilsicons.h>
#include <QAction>
+#include <QCheckBox>
+#include <QMessageBox>
#include <QTextBrowser>
+#include <QProgressDialog>
using namespace Core;
using namespace Utils;
@@ -54,80 +68,172 @@ private:
int m_width = 100;
};
-ExtensionManagerWidget::ExtensionManagerWidget()
+class PluginStatusWidget : public QWidget
{
- m_leftColumn = new ExtensionsBrowser;
+public:
+ explicit PluginStatusWidget(QWidget *parent = nullptr)
+ : QWidget(parent)
+ {
+ m_label = new InfoLabel;
+ m_checkBox = new QCheckBox(Tr::tr("Load on Start"));
+
+ using namespace Layouting;
+ Column {
+ m_label,
+ m_checkBox,
+ }.attachTo(this);
+
+ connect(m_checkBox, &QCheckBox::clicked, this, [this](bool checked) {
+ ExtensionSystem::PluginSpec *spec = ExtensionsModel::pluginSpecForName(m_pluginName);
+ if (spec == nullptr)
+ return;
+ spec->setEnabledBySettings(checked);
+ ExtensionSystem::PluginManager::writeSettings();
+ });
+
+ update();
+ }
+
+ void setPluginName(const QString &name)
+ {
+ m_pluginName = name;
+ update();
+ }
+
+private:
+ void update()
+ {
+ const ExtensionSystem::PluginSpec *spec = ExtensionsModel::pluginSpecForName(m_pluginName);
+ setVisible(spec != nullptr);
+ if (spec == nullptr)
+ return;
+
+ if (spec->hasError()) {
+ m_label->setType(InfoLabel::Error);
+ m_label->setText(Tr::tr("Error"));
+ } else if (spec->state() == ExtensionSystem::PluginSpec::Running) {
+ m_label->setType(InfoLabel::Ok);
+ m_label->setText(Tr::tr("Loaded"));
+ } else {
+ m_label->setType(InfoLabel::NotOk);
+ m_label->setText(Tr::tr("Not loaded"));
+ }
+
+ m_checkBox->setChecked(spec->isRequired() || spec->isEnabledBySettings());
+ m_checkBox->setEnabled(!spec->isRequired());
+ }
+
+ InfoLabel *m_label;
+ QCheckBox *m_checkBox;
+ QString m_pluginName;
+};
+
+class ExtensionManagerWidgetPrivate
+{
+public:
+ QString currentItemName;
+ ExtensionsBrowser *leftColumn;
+ CollapsingWidget *secondaryDescriptionWidget;
+ QTextBrowser *primaryDescription;
+ QTextBrowser *secondaryDescription;
+ PluginStatusWidget *pluginStatus;
+ QAbstractButton *installButton;
+ PluginsData currentItemPlugins;
+ Tasking::TaskTreeRunner taskTreeRunner;
+};
+
+ExtensionManagerWidget::ExtensionManagerWidget(QWidget *parent)
+ : ResizeSignallingWidget(parent)
+ , d(new ExtensionManagerWidgetPrivate)
+{
+ d->leftColumn = new ExtensionsBrowser;
auto descriptionColumns = new QWidget;
- m_secondarDescriptionWidget = new CollapsingWidget;
+ d->secondaryDescriptionWidget = new CollapsingWidget;
+
+ d->primaryDescription = new QTextBrowser;
+ d->primaryDescription->setOpenExternalLinks(true);
+ d->primaryDescription->setFrameStyle(QFrame::NoFrame);
+
+ d->secondaryDescription = new QTextBrowser;
+ d->secondaryDescription->setFrameStyle(QFrame::NoFrame);
- m_primaryDescription = new QTextBrowser;
- m_primaryDescription->setOpenExternalLinks(true);
- m_primaryDescription->setFrameStyle(QFrame::NoFrame);
+ d->pluginStatus = new PluginStatusWidget;
- m_secondaryDescription = new QTextBrowser;
- m_secondaryDescription->setFrameStyle(QFrame::NoFrame);
+ d->installButton = new Button(Tr::tr("Install..."), Button::MediumPrimary);
+ d->installButton->hide();
using namespace Layouting;
Row {
WelcomePageHelpers::createRule(Qt::Vertical),
- m_secondaryDescription,
- noMargin(), spacing(0),
- }.attachTo(m_secondarDescriptionWidget);
+ Column {
+ d->secondaryDescription,
+ d->pluginStatus,
+ d->installButton,
+ },
+ noMargin, spacing(0),
+ }.attachTo(d->secondaryDescriptionWidget);
Row {
WelcomePageHelpers::createRule(Qt::Vertical),
Row {
- m_primaryDescription,
- noMargin(),
+ d->primaryDescription,
+ noMargin,
},
- m_secondarDescriptionWidget,
- noMargin(), spacing(0),
+ d->secondaryDescriptionWidget,
+ noMargin, spacing(0),
}.attachTo(descriptionColumns);
Row {
Space(StyleHelper::SpacingTokens::ExVPaddingGapXl),
- m_leftColumn,
+ d->leftColumn,
descriptionColumns,
- noMargin(), spacing(0),
+ noMargin, spacing(0),
}.attachTo(this);
WelcomePageHelpers::setBackgroundColor(this, Theme::Token_Background_Default);
- connect(m_leftColumn, &ExtensionsBrowser::itemSelected,
+ connect(d->leftColumn, &ExtensionsBrowser::itemSelected,
this, &ExtensionManagerWidget::updateView);
connect(this, &ResizeSignallingWidget::resized, this, [this](const QSize &size) {
const int intendedLeftColumnWidth = size.width() - 580;
- m_leftColumn->adjustToWidth(intendedLeftColumnWidth);
+ d->leftColumn->adjustToWidth(intendedLeftColumnWidth);
const bool secondaryDescriptionVisible = size.width() > 970;
const int secondaryDescriptionWidth = secondaryDescriptionVisible ? 264 : 0;
- m_secondarDescriptionWidget->setWidth(secondaryDescriptionWidth);
+ d->secondaryDescriptionWidget->setWidth(secondaryDescriptionWidth);
+ });
+ connect(d->installButton, &QAbstractButton::pressed, this, [this]() {
+ fetchAndInstallPlugin(QUrl::fromUserInput(d->currentItemPlugins.constFirst().second));
});
- updateView({}, {});
+ updateView({});
+}
+
+ExtensionManagerWidget::~ExtensionManagerWidget()
+{
+ delete d;
}
-void ExtensionManagerWidget::updateView(const QModelIndex &current,
- [[maybe_unused]] const QModelIndex &previous)
+void ExtensionManagerWidget::updateView(const QModelIndex &current)
{
const QString h5Css =
StyleHelper::fontToCssProperties(StyleHelper::uiFont(StyleHelper::UiElementH5))
- + "; margin-top: 28px;";
+ + "; margin-top: 0px;";
const QString h6Css =
StyleHelper::fontToCssProperties(StyleHelper::uiFont(StyleHelper::UiElementH6))
+ "; margin-top: 28px;";
const QString h6CapitalCss =
StyleHelper::fontToCssProperties(StyleHelper::uiFont(StyleHelper::UiElementH6Capital))
- + QString::fromLatin1("; color: %1;")
- .arg(creatorTheme()->color(Theme::Token_Text_Muted).name());
- const QString bodyStyle = QString::fromLatin1("color: %1; background-color: %2;"
+ + QString::fromLatin1("; margin-top: 0px; color: %1;")
+ .arg(creatorColor(Theme::Token_Text_Muted).name());
+ const QString bodyStyle = QString::fromLatin1("color: %1; background-color: %2; "
"margin-left: %3px; margin-right: %3px;")
- .arg(creatorTheme()->color(Theme::Token_Text_Default).name())
- .arg(creatorTheme()->color(Theme::Token_Background_Muted).name())
+ .arg(creatorColor(Theme::Token_Text_Default).name())
+ .arg(creatorColor(Theme::Token_Background_Muted).name())
.arg(StyleHelper::SpacingTokens::ExVPaddingGapXl);
const QString htmlStart = QString(R"(
<html>
- <body style="%1">
+ <body style="%1"><br/>
)").arg(bodyStyle);
const QString htmlEnd = QString(R"(
</body></html>
@@ -135,161 +241,236 @@ void ExtensionManagerWidget::updateView(const QModelIndex &current,
if (!current.isValid()) {
const QString emptyHtml = htmlStart + htmlEnd;
- m_primaryDescription->setText(emptyHtml);
- m_secondaryDescription->setText(emptyHtml);
+ d->primaryDescription->setText(emptyHtml);
+ d->secondaryDescription->setText(emptyHtml);
return;
}
- const ItemData data = itemData(current);
- const bool isPack = data.type == ItemTypePack;
- const ExtensionSystem::PluginSpec *extension = data.plugins.first();
+ d->currentItemName = current.data().toString();
+ const bool isPack = current.data(RoleItemType) == ItemTypePack;
+ d->pluginStatus->setPluginName(isPack ? QString() : d->currentItemName);
+ const bool isRemotePlugin = !(isPack || ExtensionsModel::pluginSpecForName(d->currentItemName));
+ d->currentItemPlugins = current.data(RolePlugins).value<PluginsData>();
+ d->installButton->setVisible(isRemotePlugin && !d->currentItemPlugins.empty());
+ if (!d->currentItemPlugins.empty())
+ d->installButton->setToolTip(d->currentItemPlugins.constFirst().second);
{
- const QString shortDescription =
- isPack ? QLatin1String("Short description for pack ") + data.name
- : extension->description();
- QString longDescription =
- isPack ? QLatin1String("Some longer text that describes the purpose and functionality "
- "of the extensions that are part of pack ") + data.name
- : extension->longDescription();
- longDescription.replace("\n", "<br/>");
- const FilePath location = isPack ? extension->location() : extension->filePath();
-
QString description = htmlStart;
- description.append(QString(R"(
- <div style="%1"><br/>%2</div>
- <p>%3</p>
- )").arg(h5Css)
- .arg(shortDescription)
- .arg(longDescription));
-
- description.append(QString(R"(
- <div style="%1">%2</div>
- <p>%3</p>
- )").arg(h6Css)
- .arg(Tr::tr("Get started"))
- .arg(Tr::tr("Install the extension from above. Installation starts automatically. "
- "You can always uninstall the extension afterwards.")));
+ QString descriptionHtml;
+ {
+ const TextData textData = current.data(RoleDescriptionText).value<TextData>();
+ for (const TextData::Type &text : textData) {
+ if (text.second.isEmpty())
+ continue;
+ const QString paragraph =
+ QString::fromLatin1("<div style=\"%1\">%2</div><p>%3</p>")
+ .arg(descriptionHtml.isEmpty() ? h5Css : h6Css)
+ .arg(text.first)
+ .arg(text.second.join("<br/>"));
+ descriptionHtml.append(paragraph);
+ }
+ }
+ description.append(descriptionHtml);
+
+ description.append(QString::fromLatin1("<div style=\"%1\">%2</div>")
+ .arg(h6Css)
+ .arg(Tr::tr("More information")));
+ const LinksData linksData = current.data(RoleDescriptionLinks).value<LinksData>();
+ if (!linksData.isEmpty()) {
+ QString linksHtml;
+ const QStringList links = Utils::transform(linksData, [](const LinksData::Type &link) {
+ const QString anchor = link.first.isEmpty() ? link.second : link.first;
+ return QString::fromLatin1("<a href=\"%1\">%2 &gt;</a>")
+ .arg(link.second).arg(anchor);
+ });
+ linksHtml = links.join("<br/>");
+ description.append(QString::fromLatin1("<p>%1</p>").arg(linksHtml));
+ }
- description.append(QString(R"(
- <div style="%1">%2</div>
- <p>
- <a href="%3">%4 &gt;</a>
- <br/>
- <a href="%5">%6 &gt;</a>
- </p>
- )").arg(h6Css)
- .arg(Tr::tr("More information"))
- .arg(Tr::tr("Online Documentation"))
- .arg("https://doc.qt.io/qtcreator/")
- .arg(Tr::tr("Tutorials"))
- .arg("https://doc.qt.io/qtcreator/creator-tutorials.html"));
-
- const QString examplesBoxCss =
- QString::fromLatin1("height: 168px; background-color: %1; ")
- .arg(creatorTheme()->color(Theme::Token_Background_Default).name());
- description.append(QString(R"(
- <div style="%1">%2</div>
- <p style="%3">
- <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
- </p>
- )").arg(h6CapitalCss)
- .arg(Tr::tr("Examples"))
- .arg(examplesBoxCss));
+ const ImagesData imagesData = current.data(RoleDescriptionImages).value<ImagesData>();
+ if (!imagesData.isEmpty()) {
+ const QString examplesBoxCss =
+ QString::fromLatin1("height: 168px; background-color: %1; ")
+ .arg(creatorTheme()->color(Theme::Token_Background_Default).name());
+ description.append(QString(R"(
+ <br/>
+ <div style="%1">%2</div>
+ <p style="%3">
+ <br/><br/><br/><br/><br/>
+ TODO: Load imagea asynchronously, and show them in a QLabel.
+ Also Use QMovie for animated images.
+ <br/><br/><br/><br/><br/>
+ </p>
+ )").arg(h6CapitalCss)
+ .arg(Tr::tr("Examples"))
+ .arg(examplesBoxCss));
+ }
- const QString captionStrongCss = StyleHelper::fontToCssProperties(
- StyleHelper::uiFont(StyleHelper::UiElementCaptionStrong));
- description.append(QString(R"(
- <div style="%1">%2</div>
- <p>
- <table>
- <tr><td style="%3">%4</td><td>%5</td></tr>
- <tr><td style="%3">%6</td><td>%7</td></tr>
- <tr><td style="%3">%8</td><td>%9</td></tr>
- </table>
- </p>
- )").arg(h6Css)
- .arg(Tr::tr("Extension library details"))
- .arg(captionStrongCss)
- .arg(Tr::tr("Size"))
- .arg("547 MB")
- .arg(Tr::tr("Version"))
- .arg(extension->version())
- .arg(Tr::tr("Location"))
- .arg(location.toUserOutput()));
+ // Library details vanished from the Figma designs. The data is available, though.
+ const bool showDetails = false;
+ if (showDetails) {
+ const QString captionStrongCss = StyleHelper::fontToCssProperties(
+ StyleHelper::uiFont(StyleHelper::UiElementCaptionStrong));
+ const QLocale locale;
+ const uint size = current.data(RoleSize).toUInt();
+ const QString sizeFmt = locale.formattedDataSize(size);
+ const FilePath location = FilePath::fromVariant(current.data(RoleLocation));
+ const QString version = current.data(RoleVersion).toString();
+ description.append(QString(R"(
+ <div style="%1">%2</div>
+ <p>
+ <table>
+ <tr><td style="%3">%4</td><td>%5</td></tr>
+ <tr><td style="%3">%6</td><td>%7</td></tr>
+ )").arg(h6Css)
+ .arg(Tr::tr("Extension library details"))
+ .arg(captionStrongCss)
+ .arg(Tr::tr("Size"))
+ .arg(sizeFmt)
+ .arg(Tr::tr("Version"))
+ .arg(version));
+ if (!location.isEmpty()) {
+ const QString locationFmt =
+ HostOsInfo::isWindowsHost() ? location.toUserOutput()
+ : location.withTildeHomePath();
+ description.append(QString(R"(
+ <tr><td style="%3">%1</td><td>%2</td></tr>
+ )").arg(Tr::tr("Location"))
+ .arg(locationFmt));
+ }
+ description.append(QString(R"(
+ </table>
+ </p>
+ )"));
+ }
description.append(htmlEnd);
- m_primaryDescription->setText(description);
+ d->primaryDescription->setText(description);
}
{
QString description = htmlStart;
description.append(QString(R"(
- <p style="%1"><br/>%2</p>
+ <p style="%1">%2</p>
)").arg(h6CapitalCss)
.arg(Tr::tr("Extension details")));
- description.append(QString(R"(
- <div style="%1">%2</div>
- <p>%3</p>
- )").arg(h6Css)
- .arg(Tr::tr("Released"))
- .arg("23.5.2023"));
-
- const QString tagTemplate = QString(R"(
- <td style="border: 1px solid %1; padding: 3px; ">%2</td>
- )").arg(creatorTheme()->color(Theme::Token_Stroke_Subtle).name());
- const QStringList tags = Utils::transform(data.tags,
- [&tagTemplate] (const QString &tag) {
- return tagTemplate.arg(tag);
- });
- description.append(QString(R"(
- <div style="%1">%2</div>
- <p>%3</p>
- )").arg(h6Css)
- .arg(Tr::tr("Related tags"))
- .arg(tags.join("&nbsp;")));
+ const QStringList tags = current.data(RoleTags).toStringList();
+ if (!tags.isEmpty()) {
+ const QString tagTemplate = QString(R"(
+ <td style="border: 1px solid %1; padding: 3px; ">%2</td>
+ )").arg(creatorTheme()->color(Theme::Token_Stroke_Subtle).name());
+ const QStringList tagsFmt = Utils::transform(tags, [&tagTemplate](const QString &tag) {
+ return tagTemplate.arg(tag);
+ });
+ description.append(QString(R"(
+ <div style="%1">%2</div>
+ <p>%3</p>
+ )").arg(h6Css)
+ .arg(Tr::tr("Related tags"))
+ .arg(tagsFmt.join("&nbsp;")));
+ }
- description.append(QString(R"(
- <div style="%1">%2</div>
- <p>
- macOS<br/>
- Windows<br/>
- Linux
- </p>
- )").arg(h6Css)
- .arg(Tr::tr("Platforms")));
-
- QStringList dependencies;
- for (const ExtensionSystem::PluginSpec *spec : data.plugins) {
- dependencies.append(Utils::transform(spec->dependencies(),
- &ExtensionSystem::PluginDependency::toString));
+ const QStringList platforms = current.data(RolePlatforms).toStringList();
+ if (!platforms.isEmpty()) {
+ description.append(QString(R"(
+ <div style="%1">%2</div>
+ <p>%3</p>
+ )").arg(h6Css)
+ .arg(Tr::tr("Platforms"))
+ .arg(platforms.join("<br/>")));
+ }
+
+ const QStringList dependencies = current.data(RoleDependencies).toStringList();
+ if (!dependencies.isEmpty()) {
+ const QString dependenciesFmt = dependencies.join("<br/>");
+ description.append(QString(R"(
+ <div style="%1">%2</div>
+ <p>%3</p>
+ )").arg(h6Css)
+ .arg(Tr::tr("Dependencies"))
+ .arg(dependenciesFmt));
}
- dependencies.removeDuplicates();
- dependencies.sort();
- description.append(QString(R"(
- <div style="%1">%2</div>
- <p>%3</p>
- )").arg(h6Css)
- .arg(Tr::tr("Dependencies"))
- .arg(dependencies.isEmpty() ? "-" : dependencies.join("<br/>")));
if (isPack) {
- const QStringList extensions = Utils::transform(data.plugins,
- &ExtensionSystem::PluginSpec::name);
+ const PluginsData plugins = current.data(RolePlugins).value<PluginsData>();
+ const QStringList extensions = Utils::transform(plugins,
+ &QPair<QString, QString>::first);
+ const QString extensionsFmt = extensions.join("<br/>");
description.append(QString(R"(
<div style="%1">%2</div>
<p>%3</p>
)").arg(h6Css)
- .arg(Tr::tr("Extensions in pack"))
- .arg(extensions.join("<br/>")));
+ .arg(Tr::tr("Extensions in pack"))
+ .arg(extensionsFmt));
}
description.append(htmlEnd);
- m_secondaryDescription->setText(description);
+ d->secondaryDescription->setText(description);
}
}
+void ExtensionManagerWidget::fetchAndInstallPlugin(const QUrl &url)
+{
+ using namespace Tasking;
+
+ struct StorageStruct
+ {
+ StorageStruct() {
+ progressDialog.reset(new QProgressDialog(Tr::tr("Downloading Plugin..."),
+ Tr::tr("Cancel"), 0, 0,
+ Core::ICore::dialogParent()));
+ progressDialog->setWindowModality(Qt::ApplicationModal);
+ progressDialog->setFixedSize(progressDialog->sizeHint());
+ progressDialog->setAutoClose(false);
+ progressDialog->show(); // TODO: Should not be needed. Investigate possible QT_BUG
+ }
+ std::unique_ptr<QProgressDialog> progressDialog;
+ QByteArray packageData;
+ QUrl url;
+ };
+ Storage<StorageStruct> storage;
+
+ const auto onQuerySetup = [url, storage](NetworkQuery &query) {
+ storage->url = url;
+ query.setRequest(QNetworkRequest(url));
+ query.setNetworkAccessManager(NetworkAccessManager::instance());
+ };
+ const auto onQueryDone = [storage](const NetworkQuery &query, DoneWith result) {
+ storage->progressDialog->close();
+ if (result == DoneWith::Success) {
+ storage->packageData = query.reply()->readAll();
+ } else {
+ QMessageBox::warning(
+ ICore::dialogParent(),
+ Tr::tr("Download Error"),
+ Tr::tr("Could not download Plugin") + "\n\n" + storage->url.toString() + "\n\n"
+ + Tr::tr("Code: %1.").arg(query.reply()->error()));
+ }
+ };
+
+ const auto onPluginInstallation = [storage]() {
+ if (storage->packageData.isEmpty())
+ return;
+ const FilePath source = FilePath::fromUrl(storage->url);
+ TempFileSaver saver(TemporaryDirectory::masterDirectoryPath()
+ + "/XXXXXX" + source.fileName());
+
+ saver.write(storage->packageData);
+ if (saver.finalize(ICore::dialogParent()))
+ executePluginInstallWizard(saver.filePath());;
+ };
+
+ Group group{
+ storage,
+ NetworkQueryTask{onQuerySetup, onQueryDone},
+ onGroupDone(onPluginInstallation),
+ };
+
+ d->taskTreeRunner.start(group);
+}
+
} // ExtensionManager::Internal
diff --git a/src/plugins/extensionmanager/extensionmanagerwidget.h b/src/plugins/extensionmanager/extensionmanagerwidget.h
index efa2925e25..aeaad3db07 100644
--- a/src/plugins/extensionmanager/extensionmanagerwidget.h
+++ b/src/plugins/extensionmanager/extensionmanagerwidget.h
@@ -3,27 +3,19 @@
#include <coreplugin/welcomepagehelper.h>
-QT_BEGIN_NAMESPACE
-class QTextBrowser;
-QT_END_NAMESPACE
-
namespace ExtensionManager::Internal {
-class CollapsingWidget;
-class ExtensionsBrowser;
-
class ExtensionManagerWidget final : public Core::ResizeSignallingWidget
{
public:
- explicit ExtensionManagerWidget();
+ explicit ExtensionManagerWidget(QWidget *parent = nullptr);
+ ~ExtensionManagerWidget();
private:
- void updateView(const QModelIndex &current, [[maybe_unused]] const QModelIndex &previous);
+ void updateView(const QModelIndex &current);
+ void fetchAndInstallPlugin(const QUrl &url);
- ExtensionsBrowser *m_leftColumn;
- CollapsingWidget *m_secondarDescriptionWidget;
- QTextBrowser *m_primaryDescription;
- QTextBrowser *m_secondaryDescription;
+ class ExtensionManagerWidgetPrivate *d = nullptr;
};
} // ExtensionManager::Internal
diff --git a/src/plugins/extensionmanager/extensionsbrowser.cpp b/src/plugins/extensionmanager/extensionsbrowser.cpp
index 48eff3a16a..81264c59bd 100644
--- a/src/plugins/extensionmanager/extensionsbrowser.cpp
+++ b/src/plugins/extensionmanager/extensionsbrowser.cpp
@@ -4,10 +4,17 @@
#include "extensionsbrowser.h"
#include "extensionmanagertr.h"
+#include "extensionsmodel.h"
+#include "utils/hostosinfo.h"
+
+#ifdef WITH_TESTS
+#include "extensionmanager_test.h"
+#endif // WITH_TESTS
#include <coreplugin/coreconstants.h>
#include <coreplugin/icontext.h>
#include <coreplugin/icore.h>
+#include <coreplugin/plugininstallwizard.h>
#include <coreplugin/welcomepagehelper.h>
#include <extensionsystem/iplugin.h>
@@ -15,9 +22,14 @@
#include <extensionsystem/pluginview.h>
#include <extensionsystem/pluginmanager.h>
+#include <solutions/tasking/networkquery.h>
+#include <solutions/tasking/tasktree.h>
+#include <solutions/tasking/tasktreerunner.h>
+
#include <utils/fancylineedit.h>
#include <utils/icon.h>
#include <utils/layoutbuilder.h>
+#include <utils/networkaccessmanager.h>
#include <utils/stylehelper.h>
#include <QItemDelegate>
@@ -26,7 +38,6 @@
#include <QMessageBox>
#include <QPainter>
#include <QPainterPath>
-#include <QStandardItemModel>
#include <QStyle>
using namespace ExtensionSystem;
@@ -37,277 +48,16 @@ namespace ExtensionManager::Internal {
Q_LOGGING_CATEGORY(browserLog, "qtc.extensionmanager.browser", QtWarningMsg)
-using Tags = QStringList;
-
constexpr QSize itemSize = {330, 86};
constexpr int gapSize = StyleHelper::SpacingTokens::ExVPaddingGapXl;
constexpr QSize cellSize = {itemSize.width() + gapSize, itemSize.height() + gapSize};
-enum Role {
- RoleName = Qt::UserRole,
- RoleItemType,
- RoleTags,
- RolePluginSpecs,
- RoleSearchText,
-};
-
-ItemData itemData(const QModelIndex &index)
-{
- return {
- index.data(RoleName).toString(),
- index.data(RoleItemType).value<ItemType>(),
- index.data(RoleTags).toStringList(),
- index.data(RolePluginSpecs).value<PluginSpecList>(),
- };
-}
-
static QColor colorForExtensionName(const QString &name)
{
const size_t hash = qHash(name);
return QColor::fromHsv(hash % 360, 180, 110);
}
-static QStandardItemModel *extensionsModel()
-{
- // The new extensions concept renames plugins to extensions and adds "packs" which are
- // groups of extensions.
- //
- // TODO: The "meta data" here which is injected into the model is only a place holder that
- // helps exploring the upcoming extensions concept.
- //
- // Before this loses the WIP prefix, we should at least have a concrete idea of how the data
- // is structured and where it lives. Ideally, it continues to reside exclusively in the
- // extension meta data.
- //
- // The grouping of extensions into packs could be done via extension tag. Extensions and will
- // receive tags and if possible screen shots.
- // Packs will also have a complete set of meta data. That could be an accumulation of the data
- // of the contained extensions. Or simply the data from the "first" extension in a pack.
-
- static const char tagBuildTools[] = "Build Tools";
- static const char tagCodeAnalyzing[] = "Code Analyzing";
- static const char tagConnectivity[] = "Connectivity";
- static const char tagCore[] = "Core";
- static const char tagCpp[] = "C++";
- static const char tagEditorConvenience[] = "Editor Convenience";
- static const char tagEditor[] = "Editor";
- static const char tagEssentials[] = "Essentials";
- static const char tagGlsl[] = "GLSL";
- static const char tagPackageManager[] = "Package Manager";
- static const char tagPlatformSupport[] = "Platform Support";
- static const char tagProgrammingLanguage[] = "Programming Language";
- static const char tagPython[] = "Python";
- static const char tagQml[] = "QML";
- static const char tagQuick[] = "Quick";
- static const char tagService[] = "Service";
- static const char tagTestAutomation[] = "Test Automation";
- static const char tagUiEditor[] = "Visual UI Editor" ;
- static const char tagVersionControl[] = "Version Control";
- static const char tagVisualEditor[] = "Visual editor";
- static const char tagWidgets[] = "Widgets";
-
- static const char tagTagUndefined[] = "Tag undefined";
-
- static const struct {
- const QString name;
- const QStringList extensions;
- const Tags tags;
- } packs[] = {
- {"Core",
- {"Core", "Help", "ProjectExplorer", "TextEditor", "Welcome", "GenericProjectManager",
- "QtSupport"},
- {tagCore}
- },
- {"Core (from Installer)",
- {"LicenseChecker", "Marketplace", "UpdateInfo"},
- {tagCore}
- },
- {"Essentials",
- {"Bookmarks", "BinEditor", "Debugger", "DiffEditor", "ImageViewer", "Macros",
- "LanguageClient", "ResourceEditor"},
- {tagEssentials}
- },
- {"C++ Language support",
- {"ClangCodeModel", "ClangFormat", "ClassView", "CppEditor"},
- {tagProgrammingLanguage, tagCpp}
- },
- {"QML Language Support (Qt Quick libraries)",
- {"QmlJSEditor", "QmlJSTools", "QmlPreview", "QmlProfiler", "QmlProjectManager"},
- {tagProgrammingLanguage, tagQml}
- },
- {"Visual QML UI Editor",
- {"QmlDesigner", "QmlDesignerBase"},
- {tagUiEditor, tagQml, tagQuick}
- },
- {"Visual C++ Widgets UI Editor",
- {"Designer"},
- {tagUiEditor, tagCpp, tagWidgets}
- },
- };
-
- static const struct {
- const QString name;
- const Tags tags;
- } extensions[] = {
- {"GLSLEditor", {tagProgrammingLanguage, tagGlsl}},
- {"Nim", {tagProgrammingLanguage}},
- {"Python", {tagProgrammingLanguage, tagPython}},
- {"Haskell", {tagProgrammingLanguage}},
-
- {"ModelEditor", {tagVisualEditor}},
- {"ScxmlEditor", {tagVisualEditor}},
-
- {"Bazaar", {tagVersionControl}},
- {"CVS", {tagVersionControl}},
- {"ClearCase", {tagVersionControl}},
- {"Fossil", {tagVersionControl}},
- {"Git", {tagVersionControl}},
- {"Mercurial", {tagVersionControl}},
- {"Perforce", {tagVersionControl}},
- {"Subversion", {tagVersionControl}},
- {"VcsBase", {tagVersionControl}},
- {"GitLab", {tagVersionControl, tagService}},
-
- {"AutoTest", {tagTestAutomation}},
- {"Squish", {tagTestAutomation}},
- {"Coco", {tagTestAutomation}},
-
- {"Vcpkg", {tagPackageManager}},
- {"Conan", {tagPackageManager}},
-
- {"Copilot", {tagEditorConvenience}},
- {"EmacsKeys", {tagEditorConvenience}},
- {"FakeVim", {tagEditorConvenience}},
- {"Terminal", {tagEditorConvenience}},
- {"Todo", {tagEditorConvenience}},
- {"CodePaster", {tagEditorConvenience}},
- {"Beautifier", {tagEditorConvenience}},
-
- {"SerialTerminal", {tagConnectivity}},
-
- {"SilverSearcher", {tagEditor}},
-
- {"AutotoolsProjectManager", {tagBuildTools}},
- {"CMakeProjectManager", {tagBuildTools}},
- {"CompilationDatabaseProjectManager", {tagBuildTools}},
- {"IncrediBuild", {tagBuildTools}},
- {"MesonProjectManager", {tagBuildTools}},
- {"QbsProjectManager", {tagBuildTools}},
- {"QmakeProjectManager", {tagBuildTools}},
-
- {"Axivion", {tagCodeAnalyzing}},
- {"ClangTools", {tagCodeAnalyzing}},
- {"Cppcheck", {tagCodeAnalyzing}},
- {"CtfVisualizer", {tagCodeAnalyzing}},
- {"PerfProfiler", {tagCodeAnalyzing}},
- {"Valgrind", {tagCodeAnalyzing}},
-
- {"Android", {tagPlatformSupport}},
- {"BareMetal", {tagPlatformSupport}},
- {"Boot2Qt", {tagPlatformSupport}},
- {"Ios", {tagPlatformSupport}},
- {"McuSupport", {tagPlatformSupport}},
- {"Qnx", {tagPlatformSupport}},
- {"RemoteLinux", {tagPlatformSupport}},
- {"SafeRenderer", {tagPlatformSupport}},
- {"VxWorks", {tagPlatformSupport}},
- {"WebAssembly", {tagPlatformSupport}},
- {"Docker", {tagPlatformSupport}},
-
- // Missing in Kimmo's excel sheet:
- {"CompilerExplorer", {tagTagUndefined}},
- {"ExtensionManager", {tagTagUndefined}},
- {"ScreenRecorder", {tagTagUndefined}},
- };
-
- QList<QStandardItem*> items;
- QStringList expectedExtensions;
- QStringList unexpectedExtensions;
- QHash<const QString, const PluginSpec*> installedPlugins;
- for (const PluginSpec *ps : PluginManager::plugins()) {
- installedPlugins.insert(ps->name(), ps);
- unexpectedExtensions.append(ps->name());
- }
-
- const auto handleExtension = [&] (const ItemData &extension, bool addToBrowser) {
- if (!installedPlugins.contains(extension.name)) {
- expectedExtensions.append(extension.name);
- return false;
- }
- unexpectedExtensions.removeOne(extension.name);
-
- if (addToBrowser) {
- QStandardItem *item = new QStandardItem;
- const PluginSpecList pluginSpecs = {installedPlugins.value(extension.name)};
- item->setData(ItemTypeExtension, RoleItemType);
- item->setData(QVariant::fromValue(extension.tags), RoleTags);
- item->setData(QVariant::fromValue<PluginSpecList>(pluginSpecs), RolePluginSpecs);
- item->setData(extension.name, RoleName);
- items.append(item);
- }
-
- return true;
- };
-
- const bool addPackedExtensionsToBrowser = true; // TODO: Determine how we want this. As setting?
- for (const auto &pack : packs) {
- PluginSpecList pluginSpecs;
- for (const QString &extension : pack.extensions) {
- const ItemData extensionData = {extension, {}, pack.tags, {}};
- if (!handleExtension(extensionData, addPackedExtensionsToBrowser))
- continue;
- pluginSpecs.append(installedPlugins.value(extension));
- }
- if (pluginSpecs.isEmpty())
- continue;
-
- QStandardItem *item = new QStandardItem;
- item->setData(ItemTypePack, RoleItemType);
- item->setData(QVariant::fromValue(pack.tags), RoleTags);
- item->setData(QVariant::fromValue<PluginSpecList>(pluginSpecs), RolePluginSpecs);
- item->setData(pack.name, RoleName);
- items.append(item);
- }
-
- for (const auto &extension : extensions) {
- const ItemData extensionData = {extension.name, {}, extension.tags, {}};
- handleExtension(extensionData, true);
- }
-
- QStandardItemModel *result = new QStandardItemModel;
- for (auto item : items) {
- QStringList searchTexts;
- searchTexts.append(item->data(RoleName).toString());
- searchTexts.append(item->data(RoleTags).toStringList());
- const PluginSpecList pluginSpecs = item->data(RolePluginSpecs).value<PluginSpecList>();
- for (auto pluginSpec : pluginSpecs) {
- searchTexts.append(pluginSpec->name());
- searchTexts.append(pluginSpec->description());
- searchTexts.append(pluginSpec->longDescription());
- searchTexts.append(pluginSpec->category());
- searchTexts.append(pluginSpec->copyright());
- }
- searchTexts.removeDuplicates();
- item->setData(searchTexts.join(" "), RoleSearchText);
-
- item->setDragEnabled(false);
- item->setEditable(false);
-
- result->appendRow(item);
- }
-
- if (browserLog().isDebugEnabled()) {
- if (!expectedExtensions.isEmpty())
- qCDebug(browserLog) << "Expected extensions/plugins are not installed:"
- << expectedExtensions.join(", ");
- if (!unexpectedExtensions.isEmpty())
- qCDebug(browserLog) << "Unexpected extensions/plugins are installed:"
- << unexpectedExtensions.join(", ");
- }
-
- return result;
-}
-
class ExtensionItemDelegate : public QItemDelegate
{
public:
@@ -322,17 +72,17 @@ public:
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
- const ItemData data = itemData(index);
- const bool isPack = data.type == ItemTypePack;
+ const QString itemName = index.data().toString();
+ const bool isPack = index.data(RoleItemType) == ItemTypePack;
const QRectF itemRect(option.rect.topLeft(), itemSize);
{
const bool selected = option.state & QStyle::State_Selected;
const bool hovered = option.state & QStyle::State_MouseOver;
const QColor fillColor =
- creatorTheme()->color(hovered ? WelcomePageHelpers::cardHoverBackground
+ creatorColor(hovered ? WelcomePageHelpers::cardHoverBackground
: WelcomePageHelpers::cardDefaultBackground);
const QColor strokeColor =
- creatorTheme()->color(selected ? Theme::Token_Stroke_Strong
+ creatorColor(selected ? Theme::Token_Stroke_Strong
: hovered ? WelcomePageHelpers::cardHoverStroke
: WelcomePageHelpers::cardDefaultStroke);
WelcomePageHelpers::drawCardBackground(painter, itemRect, fillColor, strokeColor);
@@ -345,7 +95,7 @@ public:
bigCirclePath.addEllipse(bigCircleLocal);
QLinearGradient gradient(bigCircleLocal.topLeft(), bigCircleLocal.bottomRight());
const QColor startColor = isPack ? qRgb(0x1e, 0x99, 0x6e)
- : colorForExtensionName(data.name);
+ : colorForExtensionName(itemName);
const QColor endColor = isPack ? qRgb(0x07, 0x6b, 0x6d) : startColor.lighter(150);
gradient.setColorAt(gradientMargin, startColor);
gradient.setColorAt(1 - gradientMargin, endColor);
@@ -368,17 +118,20 @@ public:
constexpr QRectF smallCircleAdjusted = smallCircle.adjusted(shrink, shrink,
-shrink, -shrink);
const QRectF smallCircleLocal = smallCircleAdjusted.translated(itemRect.topLeft());
- const QColor fillColor = creatorTheme()->color(Theme::Token_Foreground_Muted);
- const QColor strokeColor = creatorTheme()->color(Theme::Token_Stroke_Subtle);
+ const QColor fillColor = creatorColor(Theme::Token_Foreground_Muted);
+ const QColor strokeColor = creatorColor(Theme::Token_Stroke_Subtle);
painter->setBrush(fillColor);
painter->setPen(strokeColor);
painter->drawEllipse(smallCircleLocal);
painter->setFont(StyleHelper::uiFont(StyleHelper::UiElementCaptionStrong));
- const QColor textColor = creatorTheme()->color(Theme::Token_Text_Default);
+ const QColor textColor = creatorColor(Theme::Token_Text_Default);
painter->setPen(textColor);
- painter->drawText(smallCircleLocal, QString::number(data.plugins.count()),
- QTextOption(Qt::AlignCenter));
+ const PluginsData plugins = index.data(RolePlugins).value<PluginsData>();
+ painter->drawText(
+ smallCircleLocal,
+ QString::number(plugins.count()),
+ QTextOption(Qt::AlignCenter));
}
{
constexpr int textX = 80;
@@ -388,24 +141,25 @@ public:
constexpr int titleY = 30;
const QPointF titleOrigin(itemRect.topLeft() + QPointF(textX, titleY));
- painter->setPen(creatorTheme()->color(Theme::Token_Text_Default));
+ painter->setPen(creatorColor(Theme::Token_Text_Default));
painter->setFont(StyleHelper::uiFont(StyleHelper::UiElementH6));
- const QString titleElided = painter->fontMetrics().elidedText(
- data.name, elideMode, maxTextWidth);
+ const QString titleElided
+ = painter->fontMetrics().elidedText(itemName, elideMode, maxTextWidth);
painter->drawText(titleOrigin, titleElided);
constexpr int copyrightY = 52;
const QPointF copyrightOrigin(itemRect.topLeft() + QPointF(textX, copyrightY));
- painter->setPen(creatorTheme()->color(Theme::Token_Text_Muted));
+ painter->setPen(creatorColor(Theme::Token_Text_Muted));
painter->setFont(StyleHelper::uiFont(StyleHelper::UiElementCaptionStrong));
- const QString copyrightElided = painter->fontMetrics().elidedText(
- data.plugins.first()->copyright(), elideMode, maxTextWidth);
+ const QString copyright = index.data(RoleCopyright).toString();
+ const QString copyrightElided
+ = painter->fontMetrics().elidedText(copyright, elideMode, maxTextWidth);
painter->drawText(copyrightOrigin, copyrightElided);
constexpr int tagsY = 70;
const QPointF tagsOrigin(itemRect.topLeft() + QPointF(textX, tagsY));
- const QString tags = data.tags.join(", ");
- painter->setPen(creatorTheme()->color(Theme::Token_Text_Default));
+ const QString tags = index.data(RoleTags).toStringList().join(", ");
+ painter->setPen(creatorColor(Theme::Token_Text_Default));
painter->setFont(StyleHelper::uiFont(StyleHelper::UiElementCaption));
const QString tagsElided = painter->fontMetrics().elidedText(
tags, elideMode, maxTextWidth);
@@ -422,87 +176,154 @@ public:
}
};
-ExtensionsBrowser::ExtensionsBrowser()
+class ExtensionsBrowserPrivate
+{
+public:
+ ExtensionsModel *model;
+ QLineEdit *searchBox;
+ QAbstractButton *updateButton;
+ QListView *extensionsView;
+ QItemSelectionModel *selectionModel = nullptr;
+ QSortFilterProxyModel *filterProxyModel;
+ int columnsCount = 2;
+ Tasking::TaskTreeRunner taskTreeRunner;
+};
+
+ExtensionsBrowser::ExtensionsBrowser(QWidget *parent)
+ : QWidget(parent)
+ , d(new ExtensionsBrowserPrivate)
{
setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
auto manageLabel = new QLabel(Tr::tr("Manage Extensions"));
manageLabel->setFont(StyleHelper::uiFont(StyleHelper::UiElementH1));
- m_searchBox = new Core::SearchBox;
- m_searchBox->setFixedWidth(itemSize.width());
+ d->searchBox = new Core::SearchBox;
+ d->searchBox->setFixedWidth(itemSize.width());
+
+ d->updateButton = new Button(Tr::tr("Install..."), Button::MediumPrimary);
- m_updateButton = new Button(Tr::tr("Install..."), Button::MediumPrimary);
+ d->model = new ExtensionsModel(this);
- m_filterProxyModel = new QSortFilterProxyModel(this);
- m_filterProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
- m_filterProxyModel->setFilterRole(RoleSearchText);
- m_filterProxyModel->setSortRole(RoleItemType);
+ d->filterProxyModel = new QSortFilterProxyModel(this);
+ d->filterProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+ d->filterProxyModel->setFilterRole(RoleSearchText);
+ d->filterProxyModel->setSortRole(RoleItemType);
+ d->filterProxyModel->setSourceModel(d->model);
- m_extensionsView = new QListView;
- m_extensionsView->setFrameStyle(QFrame::NoFrame);
- m_extensionsView->setItemDelegate(new ExtensionItemDelegate(this));
- m_extensionsView->setResizeMode(QListView::Adjust);
- m_extensionsView->setSelectionMode(QListView::SingleSelection);
- m_extensionsView->setUniformItemSizes(true);
- m_extensionsView->setViewMode(QListView::IconMode);
- m_extensionsView->setModel(m_filterProxyModel);
- m_extensionsView->setMouseTracking(true);
+ d->extensionsView = new QListView;
+ d->extensionsView->setFrameStyle(QFrame::NoFrame);
+ d->extensionsView->setItemDelegate(new ExtensionItemDelegate(this));
+ d->extensionsView->setResizeMode(QListView::Adjust);
+ d->extensionsView->setSelectionMode(QListView::SingleSelection);
+ d->extensionsView->setUniformItemSizes(true);
+ d->extensionsView->setViewMode(QListView::IconMode);
+ d->extensionsView->setModel(d->filterProxyModel);
+ d->extensionsView->setMouseTracking(true);
using namespace Layouting;
Column {
Space(15),
manageLabel,
Space(15),
- Row { m_searchBox, st, m_updateButton, Space(extraListViewWidth() + gapSize) },
+ Row { d->searchBox, st, d->updateButton, Space(extraListViewWidth() + gapSize) },
Space(gapSize),
- m_extensionsView,
- noMargin(), spacing(0),
+ d->extensionsView,
+ noMargin, spacing(0),
}.attachTo(this);
WelcomePageHelpers::setBackgroundColor(this, Theme::Token_Background_Default);
- WelcomePageHelpers::setBackgroundColor(m_extensionsView, Theme::Token_Background_Default);
- WelcomePageHelpers::setBackgroundColor(m_extensionsView->viewport(),
+ WelcomePageHelpers::setBackgroundColor(d->extensionsView, Theme::Token_Background_Default);
+ WelcomePageHelpers::setBackgroundColor(d->extensionsView->viewport(),
Theme::Token_Background_Default);
auto updateModel = [this] {
- m_model.reset(extensionsModel());
- m_filterProxyModel->setSourceModel(m_model.data());
- m_filterProxyModel->sort(0);
-
- if (m_selectionModel == nullptr) {
- m_selectionModel = new QItemSelectionModel(m_filterProxyModel, m_extensionsView);
- m_extensionsView->setSelectionModel(m_selectionModel);
- connect(m_extensionsView->selectionModel(), &QItemSelectionModel::currentChanged,
+ d->filterProxyModel->sort(0);
+
+ if (d->selectionModel == nullptr) {
+ d->selectionModel = new QItemSelectionModel(d->filterProxyModel,
+ d->extensionsView);
+ d->extensionsView->setSelectionModel(d->selectionModel);
+ connect(d->extensionsView->selectionModel(), &QItemSelectionModel::currentChanged,
this, &ExtensionsBrowser::itemSelected);
}
};
+ connect(d->updateButton, &QAbstractButton::pressed, this, []() {
+ executePluginInstallWizard();
+ });
connect(ExtensionSystem::PluginManager::instance(),
&ExtensionSystem::PluginManager::pluginsChanged, this, updateModel);
- connect(m_searchBox, &QLineEdit::textChanged,
- m_filterProxyModel, &QSortFilterProxyModel::setFilterWildcard);
+ connect(ExtensionSystem::PluginManager::instance(),
+ &ExtensionSystem::PluginManager::initializationDone,
+ this, &ExtensionsBrowser::fetchExtensions);
+ connect(d->searchBox, &QLineEdit::textChanged,
+ d->filterProxyModel, &QSortFilterProxyModel::setFilterWildcard);
+}
+
+ExtensionsBrowser::~ExtensionsBrowser()
+{
+ delete d;
}
void ExtensionsBrowser::adjustToWidth(const int width)
{
const int widthForItems = width - extraListViewWidth();
- m_columnsCount = qMax(1, qFloor(widthForItems / cellSize.width()));
- m_updateButton->setVisible(m_columnsCount > 1);
+ d->columnsCount = qMax(1, qFloor(widthForItems / cellSize.width()));
+ d->updateButton->setVisible(d->columnsCount > 1);
updateGeometry();
}
QSize ExtensionsBrowser::sizeHint() const
{
- const int columsWidth = m_columnsCount * cellSize.width();
+ const int columsWidth = d->columnsCount * cellSize.width();
return { columsWidth + extraListViewWidth(), 0};
}
int ExtensionsBrowser::extraListViewWidth() const
{
// TODO: Investigate "transient" scrollbar, just for this list view.
- return m_extensionsView->style()->pixelMetric(QStyle::PM_ScrollBarExtent)
+ return d->extensionsView->style()->pixelMetric(QStyle::PM_ScrollBarExtent)
+ 1; // Needed
}
+void ExtensionsBrowser::fetchExtensions()
+{
+ // d->model->setExtensionsJson(testData("thirdpartyplugins")); return;
+
+ using namespace Tasking;
+
+ const auto onQuerySetup = [](NetworkQuery &query) {
+ const QString host = "https://qc-extensions.qt.io";
+ const QString url = "%1/api/v1/search?request=";
+ const QString requestTemplate
+ = R"({"version":"%1","host_os":"%2","host_os_version":"%3","host_architecture":"%4","page_size":200})";
+ const QString request = url.arg(host)
+ + requestTemplate
+ .arg("2.2") // .arg(QCoreApplication::applicationVersion())
+ .arg("macOS") // .arg(QSysInfo::productType())
+ .arg("12") // .arg(QSysInfo::productVersion())
+ .arg("arm64"); // .arg(QSysInfo::currentCpuArchitecture());
+
+ query.setRequest(QNetworkRequest(QUrl::fromUserInput(request)));
+ query.setNetworkAccessManager(NetworkAccessManager::instance());
+ };
+ const auto onQueryDone = [this](const NetworkQuery &query, DoneWith result) {
+ if (result != DoneWith::Success) {
+#ifdef WITH_TESTS
+ d->model->setExtensionsJson(testData("defaultpacks"));
+#endif // WITH_TESTS
+ return;
+ }
+ const QByteArray response = query.reply()->readAll();
+ d->model->setExtensionsJson(response);
+ };
+
+ Group group {
+ NetworkQueryTask{onQuerySetup, onQueryDone},
+ };
+
+ d->taskTreeRunner.start(group);
+}
+
} // ExtensionManager::Internal
diff --git a/src/plugins/extensionmanager/extensionsbrowser.h b/src/plugins/extensionmanager/extensionsbrowser.h
index 2daa2362ba..d0467aa216 100644
--- a/src/plugins/extensionmanager/extensionsbrowser.h
+++ b/src/plugins/extensionmanager/extensionsbrowser.h
@@ -3,66 +3,30 @@
#pragma once
-#include <utils/theme/theme.h>
-
-#include <QStandardItemModel>
#include <QWidget>
-QT_BEGIN_NAMESPACE
-class QAbstractButton;
-class QItemSelectionModel;
-class QLineEdit;
-class QListView;
-class QSortFilterProxyModel;
-QT_END_NAMESPACE
-
-namespace ExtensionSystem
-{
-class PluginSpec;
-}
-
namespace ExtensionManager::Internal {
-using PluginSpecList = QList<const ExtensionSystem::PluginSpec *>;
-using Tags = QStringList;
-
-enum ItemType {
- ItemTypePack,
- ItemTypeExtension,
-};
-
-struct ItemData {
- const QString name;
- const ItemType type = ItemTypeExtension;
- const Tags tags;
- const PluginSpecList plugins;
-};
-
-ItemData itemData(const QModelIndex &index);
-
class ExtensionsBrowser final : public QWidget
{
Q_OBJECT
public:
- ExtensionsBrowser();
+ ExtensionsBrowser(QWidget *parent = nullptr);
+ ~ExtensionsBrowser();
void adjustToWidth(const int width);
QSize sizeHint() const override;
+ int extraListViewWidth() const; // Space for scrollbar, etc.
+
signals:
void itemSelected(const QModelIndex &current, const QModelIndex &previous);
private:
- int extraListViewWidth() const; // Space for scrollbar, etc.
+ void fetchExtensions();
- QScopedPointer<QStandardItemModel> m_model;
- QLineEdit *m_searchBox;
- QAbstractButton *m_updateButton;
- QListView *m_extensionsView;
- QItemSelectionModel *m_selectionModel = nullptr;
- QSortFilterProxyModel *m_filterProxyModel;
- int m_columnsCount = 2;
+ class ExtensionsBrowserPrivate *d = nullptr;
};
} // ExtensionManager::Internal
diff --git a/src/plugins/extensionmanager/extensionsmodel.cpp b/src/plugins/extensionmanager/extensionsmodel.cpp
new file mode 100644
index 0000000000..2315164e11
--- /dev/null
+++ b/src/plugins/extensionmanager/extensionsmodel.cpp
@@ -0,0 +1,411 @@
+// 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 "extensionsmodel.h"
+
+#include "extensionsbrowser.h"
+
+#include "extensionmanagertr.h"
+#include "utils/algorithm.h"
+
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/icore.h>
+
+#include <extensionsystem/iplugin.h>
+#include <extensionsystem/pluginspec.h>
+#include <extensionsystem/pluginview.h>
+#include <extensionsystem/pluginmanager.h>
+
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QStandardItemModel>
+#include <QVersionNumber>
+
+using namespace ExtensionSystem;
+using namespace Core;
+using namespace Utils;
+
+namespace ExtensionManager::Internal {
+
+Q_LOGGING_CATEGORY(modelLog, "qtc.extensionmanager.model", QtWarningMsg)
+
+struct PluginDependency
+{
+ QString name;
+ QString version;
+};
+using PluginDependencies = QList<PluginDependency>;
+
+struct Plugin
+{
+ PluginDependencies dependencies;
+ QString copyright;
+ QString name;
+ QString packageUrl;
+ QString vendor;
+ QString version;
+};
+using Plugins = QList<Plugin>;
+
+struct Description {
+ ImagesData images;
+ LinksData links;
+ TextData text;
+};
+
+struct Extension {
+ QString copyright;
+ Description description;
+ QString license;
+ QString name;
+ QStringList platforms;
+ Plugins plugins;
+ qint64 size = 0;
+ QStringList tags;
+ ItemType type = ItemTypePack;
+ QString vendor;
+ QString version;
+};
+using Extensions = QList<Extension>;
+
+static Plugin pluginFromJson(const QJsonObject &obj)
+{
+ const QJsonObject metaDataObj = obj.value("meta_data").toObject();
+
+ const QJsonArray dependenciesArray = metaDataObj.value("Dependencies").toArray();
+ PluginDependencies dependencies;
+ for (const QJsonValueConstRef &dependencyVal : dependenciesArray) {
+ const QJsonObject dependencyObj = dependencyVal.toObject();
+ dependencies.append(PluginDependency{
+ .name = dependencyObj.value("Name").toString(),
+ .version = dependencyObj.value("Version").toString(),
+ });
+ }
+
+ return {
+ .dependencies = dependencies,
+ .copyright = metaDataObj.value("Copyright").toString(),
+ .name = metaDataObj.value("Name").toString(),
+ .packageUrl = obj.value("url").toString(),
+ .vendor = metaDataObj.value("Vendor").toString(),
+ .version = metaDataObj.value("Version").toString(),
+ };
+}
+
+static Description descriptionFromJson(const QJsonObject &obj)
+{
+ TextData descriptionText;
+ const QJsonArray paragraphsArray = obj.value("paragraphs").toArray();
+ for (const QJsonValueConstRef &paragraphVal : paragraphsArray) {
+ const QJsonObject &paragraphObj = paragraphVal.toObject();
+ const QJsonArray &textArray = paragraphObj.value("text").toArray();
+ QStringList textLines;
+ for (const QJsonValueConstRef &textVal : textArray)
+ textLines.append(textVal.toString());
+ descriptionText.append({
+ paragraphObj.value("header").toString(),
+ textLines,
+ });
+ }
+
+ LinksData links;
+ const QJsonArray linksArray = obj.value("links").toArray();
+ for (const QJsonValueConstRef &linkVal : linksArray) {
+ const QJsonObject &linkObj = linkVal.toObject();
+ links.append({
+ linkObj.value("link_text").toString(),
+ linkObj.value("url").toString(),
+ });
+ }
+
+ ImagesData images;
+ const QJsonArray imagesArray = obj.value("images").toArray();
+ for (const QJsonValueConstRef &imageVal : imagesArray) {
+ const QJsonObject &imageObj = imageVal.toObject();
+ images.append({
+ imageObj.value("image_label").toString(),
+ imageObj.value("url").toString(),
+ });
+ }
+
+ const Description description = {
+ .images = images,
+ .links = links,
+ .text = descriptionText,
+ };
+
+ return description;
+}
+
+static Extension extensionFromJson(const QJsonObject &obj)
+{
+ Plugins plugins;
+ const QJsonArray pluginsArray = obj.value("plugins").toArray();
+ for (const QJsonValueConstRef &pluginVal : pluginsArray)
+ plugins.append(pluginFromJson(pluginVal.toObject()));
+
+ QStringList tags;
+ const QJsonArray tagsArray = obj.value("tags").toArray();
+ for (const QJsonValueConstRef &tagVal : tagsArray)
+ tags.append(tagVal.toString());
+
+ QStringList platforms;
+ const QJsonArray platformsArray = obj.value("platforms").toArray();
+ for (const QJsonValueConstRef &platformsVal : platformsArray)
+ platforms.append(platformsVal.toString());
+
+ const QJsonObject descriptionObj = obj.value("description").toObject();
+ const Description description = descriptionFromJson(descriptionObj);
+
+ const Extension extension = {
+ .copyright = obj.value("copyright").toString(),
+ .description = description,
+ .license = obj.value("license").toString(),
+ .name = obj.value("name").toString(),
+ .platforms = platforms,
+ .plugins = plugins,
+ .size = obj.value("total_size").toInteger(),
+ .tags = tags,
+ .type = obj.value("is_pack").toBool(true) ? ItemTypePack : ItemTypeExtension,
+ .vendor = obj.value("vendor").toString(),
+ .version = obj.value("version").toString(),
+ };
+
+ return extension;
+}
+
+static Extensions parseExtensionsRepoReply(const QByteArray &jsonData)
+{
+ // https://qc-extensions.qt.io/api-docs
+ Extensions parsedExtensions;
+ const QJsonObject jsonObj = QJsonDocument::fromJson(jsonData).object();
+ const QJsonArray items = jsonObj.value("items").toArray();
+ for (const QJsonValueConstRef &itemVal : items) {
+ const QJsonObject itemObj = itemVal.toObject();
+ const Extension extension = extensionFromJson(itemObj);
+ parsedExtensions.append(extension);
+ }
+ return parsedExtensions;
+}
+
+class ExtensionsModelPrivate : public QObject
+{
+public:
+ ExtensionsModelPrivate(ExtensionsModel *parent)
+ : q(parent)
+ {
+ }
+
+ void setExtensions(const Extensions &extensions);
+ void removeLocalExtensions();
+
+ ExtensionsModel *q;
+
+ Extensions allExtensions; // Original, complete extensions entries
+ Extensions absentExtensions; // All packs + plugin extensions that are not (yet) installed
+};
+
+void ExtensionsModelPrivate::setExtensions(const Extensions &extensions)
+{
+ allExtensions = extensions;
+ removeLocalExtensions();
+}
+
+void ExtensionsModelPrivate::removeLocalExtensions()
+{
+ const QStringList installedPlugins = transform(PluginManager::plugins(), &PluginSpec::name);
+ absentExtensions.clear();
+ for (const Extension &extension : allExtensions) {
+ if (extension.type == ItemTypePack || !installedPlugins.contains(extension.name))
+ absentExtensions.append(extension);
+ }
+}
+
+ExtensionsModel::ExtensionsModel(QObject *parent)
+ : QAbstractListModel(parent)
+ , d(new ExtensionsModelPrivate(this))
+{
+}
+
+ExtensionsModel::~ExtensionsModel()
+{
+ delete d;
+}
+
+int ExtensionsModel::rowCount([[maybe_unused]] const QModelIndex &parent) const
+{
+ const int remoteExtnsionsCount = d->absentExtensions.count();
+ const int installedPluginsCount = PluginManager::plugins().count();
+ return remoteExtnsionsCount + installedPluginsCount;
+}
+
+static QVariant dataFromPluginSpec(const PluginSpec *pluginSpec, int role)
+{
+ switch (role) {
+ case Qt::DisplayRole:
+ case RoleName:
+ return pluginSpec->name();
+ case RoleCopyright:
+ return pluginSpec->copyright();
+ case RoleDependencies: {
+ QStringList dependencies = transform(pluginSpec->dependencies(),
+ &ExtensionSystem::PluginDependency::toString);
+ dependencies.sort();
+ return dependencies;
+ }
+ case RoleDescriptionImages:
+ break;
+ case RoleDescriptionLinks: {
+ const QString url = pluginSpec->url();
+ if (!url.isEmpty()) {
+ const LinksData links = {{{}, url}};
+ return QVariant::fromValue(links);
+ }
+ break;
+ }
+ case RoleDescriptionText: {
+ QStringList lines = pluginSpec->description().split('\n', Qt::SkipEmptyParts);
+ lines.append(pluginSpec->longDescription().split('\n', Qt::SkipEmptyParts));
+ const TextData text = {{ pluginSpec->name(), lines }};
+ return QVariant::fromValue(text);
+ }
+ case RoleItemType:
+ return ItemTypeExtension;
+ case RoleLicense:
+ return pluginSpec->license();
+ case RoleLocation:
+ return pluginSpec->filePath().toVariant();
+ case RolePlatforms: {
+ const QString pattern = pluginSpec->platformSpecification().pattern();
+ const QStringList platforms = pattern.isEmpty()
+ ? QStringList({"macOS", "Windows", "Linux"})
+ : QStringList(pattern);
+ return platforms;
+ }
+ case RoleSize:
+ return pluginSpec->filePath().fileSize();
+ case RoleTags:
+ break;
+ case RoleVendor:
+ return pluginSpec->vendor();
+ case RoleVersion:
+ return pluginSpec->version();
+ default:
+ break;
+ }
+ return {};
+}
+
+static QStringList dependenciesFromExtension(const Extension &extension)
+{
+ QStringList dependencies;
+ for (const Plugin &plugin : extension.plugins) {
+ for (const PluginDependency &dependency : plugin.dependencies) {
+ const QString withVersion = QString::fromLatin1("%1 (%2)").arg(dependency.name)
+ .arg(dependency.version);
+ dependencies.append(withVersion);
+ }
+ }
+ dependencies.sort();
+ dependencies.removeDuplicates();
+ return dependencies;
+}
+
+static QVariant dataFromExtension(const Extension &extension, int role)
+{
+ switch (role) {
+ case Qt::DisplayRole:
+ case RoleName:
+ return extension.name;
+ case RoleCopyright:
+ return !extension.copyright.isEmpty() ? extension.copyright : QVariant();
+ case RoleDependencies:
+ return dependenciesFromExtension(extension);
+ case RoleDescriptionImages:
+ return QVariant::fromValue(extension.description.images);
+ case RoleDescriptionLinks:
+ return QVariant::fromValue(extension.description.links);
+ case RoleDescriptionText:
+ return QVariant::fromValue(extension.description.text);
+ case RoleItemType:
+ return extension.type;
+ case RoleLicense:
+ return extension.license;
+ case RoleLocation:
+ break;
+ case RolePlatforms:
+ return extension.platforms;
+ case RolePlugins: {
+ PluginsData plugins;
+ for (const Plugin &plugin : extension.plugins)
+ plugins.append(qMakePair(plugin.name, plugin.packageUrl));
+ return QVariant::fromValue(plugins);
+ }
+ case RoleSize:
+ return extension.size;
+ case RoleTags:
+ return extension.tags;
+ case RoleVendor:
+ return !extension.vendor.isEmpty() ? extension.vendor : QVariant();
+ case RoleVersion:
+ return !extension.version.isEmpty() ? extension.version : QVariant();
+ default:
+ break;
+ }
+ return {};
+}
+
+static QString searchText(const QModelIndex &index)
+{
+ QStringList searchTexts;
+ searchTexts.append(index.data(RoleName).toString());
+ searchTexts.append(index.data(RoleTags).toStringList());
+ searchTexts.append(index.data(RoleDescriptionText).toStringList());
+ searchTexts.append(index.data(RoleVendor).toString());
+ return searchTexts.join(" ");
+}
+
+QVariant ExtensionsModel::data(const QModelIndex &index, int role) const
+{
+ if (role == RoleSearchText)
+ return searchText(index);
+
+ const bool itemIsLocalPlugin = index.row() >= d->absentExtensions.count();
+ if (itemIsLocalPlugin) {
+ const PluginSpecs &pluginSpecs = PluginManager::plugins();
+ const int pluginIndex = index.row() - d->absentExtensions.count();
+ QTC_ASSERT(pluginIndex >= 0 && pluginIndex <= pluginSpecs.size(), return {});
+ const PluginSpec *plugin = pluginSpecs.at(pluginIndex);
+ return dataFromPluginSpec(plugin, role);
+ } else {
+ const Extension &extension = d->absentExtensions.at(index.row());
+ const QVariant extensionData = dataFromExtension(extension, role);
+
+ // If data is unavailable, retrieve it from the first contained plugin
+ if (extensionData.isNull() && !extension.plugins.isEmpty()) {
+ const PluginSpec *pluginSpec = ExtensionsModel::pluginSpecForName(
+ extension.plugins.constFirst().name);
+ if (pluginSpec)
+ return dataFromPluginSpec(pluginSpec, role);
+ }
+ return extensionData;
+ }
+ return {};
+}
+
+void ExtensionsModel::setExtensionsJson(const QByteArray &json)
+{
+ const Extensions extensions = parseExtensionsRepoReply(json);
+ beginResetModel();
+ d->setExtensions(extensions);
+ endResetModel();
+}
+
+PluginSpec *ExtensionsModel::pluginSpecForName(const QString &pluginName)
+{
+ return Utils::findOrDefault(PluginManager::plugins(),
+ Utils::equal(&PluginSpec::name, pluginName));
+}
+
+} // ExtensionManager::Internal
diff --git a/src/plugins/extensionmanager/extensionsmodel.h b/src/plugins/extensionmanager/extensionsmodel.h
new file mode 100644
index 0000000000..d426f44353
--- /dev/null
+++ b/src/plugins/extensionmanager/extensionsmodel.h
@@ -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
+
+#pragma once
+
+#include <QAbstractListModel>
+
+namespace ExtensionSystem {
+class PluginSpec;
+}
+
+namespace ExtensionManager::Internal {
+
+using QPairList = QList<QPair<QString, QString> >;
+
+using ImagesData = QPairList; // { <caption, url>, ... }
+using LinksData = QPairList; // { <name, url>, ... }
+using PluginsData = QPairList; // { <name, url>, ... }
+using TextData = QList<QPair<QString, QStringList> >; // { <header, text>, ... }
+
+enum ItemType {
+ ItemTypePack,
+ ItemTypeExtension,
+};
+
+enum Role {
+ RoleName = Qt::UserRole,
+ RoleCopyright,
+ RoleDependencies,
+ RoleDescriptionImages,
+ RoleDescriptionLinks,
+ RoleDescriptionText,
+ RoleItemType,
+ RoleLicense,
+ RoleLocation,
+ RolePlatforms,
+ RolePlugins,
+ RoleSearchText,
+ RoleSize,
+ RoleTags,
+ RoleVendor,
+ RoleVersion,
+};
+
+class ExtensionsModel : public QAbstractListModel
+{
+public:
+ ExtensionsModel(QObject *parent = nullptr);
+ ~ExtensionsModel();
+
+ int rowCount(const QModelIndex &parent = {}) const;
+ QVariant data(const QModelIndex &index, int role) const;
+
+ void setExtensionsJson(const QByteArray &json);
+ static ExtensionSystem::PluginSpec *pluginSpecForName(const QString &pluginName);
+
+private:
+ class ExtensionsModelPrivate *d = nullptr;
+};
+
+#ifdef WITH_TESTS
+QObject *createExtensionsModelTest();
+#endif
+
+} // ExtensionManager::Internal
+
+Q_DECLARE_METATYPE(ExtensionManager::Internal::QPairList)
+Q_DECLARE_METATYPE(ExtensionManager::Internal::TextData)
diff --git a/src/plugins/extensionmanager/testdata/defaultpacks.json b/src/plugins/extensionmanager/testdata/defaultpacks.json
new file mode 100644
index 0000000000..0323b08d27
--- /dev/null
+++ b/src/plugins/extensionmanager/testdata/defaultpacks.json
@@ -0,0 +1,161 @@
+{
+ "items": [
+ {
+ "name": "Essentials",
+ "tags": [ "Essentials" ],
+ "platforms": [ "macOS", "Windows", "Linux" ],
+ "license": "os",
+ "is_pack": true,
+ "description": {
+ "paragraphs": [
+ {
+ "text": [
+ "Basic services, such as editing and debugging code, viewing images, and adding resources to applications."
+ ],
+ "header": "Get started"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://doc.qt.io/qtcreator/creator-coding-navigating.html",
+ "link_text": "Online documentation"
+ }
+ ]
+ },
+ "plugins": [
+ { "meta_data": { "Name": "BinEditor" } },
+ { "meta_data": { "Name": "Debugger" } },
+ { "meta_data": { "Name": "DiffEditor" } },
+ { "meta_data": { "Name": "ImageViewer" } },
+ { "meta_data": { "Name": "Macros" } },
+ { "meta_data": { "Name": "LanguageClient" } },
+ { "meta_data": { "Name": "ResourceEditor" } }
+ ]
+ },
+
+ {
+ "name": "C++ Support",
+ "tags": [ "Programming Language", "C++" ],
+ "platforms": [ "macOS", "Windows", "Linux" ],
+ "license": "os",
+ "is_pack": true,
+ "description": {
+ "paragraphs": [
+ {
+ "text": [
+ "Tools for developing Qt C++ applications."
+ ],
+ "header": "Get started"
+ }
+ ]
+ },
+ "plugins": [
+ { "meta_data": { "Name": "ClangCodeModel" } },
+ { "meta_data": { "Name": "ClangFormat" } },
+ { "meta_data": { "Name": "ClassView" } },
+ { "meta_data": { "Name": "CppEditor" } }
+ ]
+ },
+
+ {
+ "name": "QML Support",
+ "tags": [ "Programming Language", "QML" ],
+ "platforms": [ "macOS", "Windows", "Linux" ],
+ "license": "os",
+ "is_pack": true,
+ "description": {
+ "paragraphs": [
+ {
+ "text": [
+ "Tools for developing Qt Quick applications."
+ ],
+ "header": "Get started"
+ }
+ ]
+ },
+ "plugins": [
+ { "meta_data": { "Name": "QmlJSEditor" } },
+ { "meta_data": { "Name": "QmlJSTools" } },
+ { "meta_data": { "Name": "QmlPreview" } },
+ { "meta_data": { "Name": "QmlProfiler" } }
+ ]
+ },
+
+ {
+ "name": "Visual QML Editor",
+ "tags": [ "Visual UI editor", "qml", "Quick" ],
+ "platforms": [ "macOS", "Windows", "Linux" ],
+ "license": "os",
+ "is_pack": true,
+ "description": {
+ "paragraphs": [
+ {
+ "text": [
+ "Tools for creating Qt Quick UIs."
+ ],
+ "header": "Get started"
+ }
+ ]
+ },
+ "plugins": [
+ { "meta_data": { "Name": "QmlDesigner" } }
+ ]
+ },
+
+ {
+ "name": "Visual Widget Editor",
+ "tags": [ "Visual UI editor", "C++", "Widgets" ],
+ "platforms": [ "macOS", "Windows", "Linux" ],
+ "license": "os",
+ "is_pack": true,
+ "description": {
+ "paragraphs": [
+ {
+ "text": [
+ "Visual tool for creating Qt widget-based UIs."
+ ],
+ "header": "Get started"
+ }
+ ]
+ },
+ "plugins": [
+ { "meta_data": { "Name": "Designer" } }
+ ]
+ },
+
+ {
+ "name": "SpellChecker",
+ "tags": [ "Editor" ],
+ "platforms": [ "macOS", "Windows", "Linux" ],
+ "license": "os",
+ "is_pack": false,
+ "description": {
+ "paragraphs": [
+ {
+ "text": [
+ "Spellcheck comments in source files."
+ ],
+ "header": "Get started"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://github.com/CJCombrink/SpellChecker-Plugin",
+ "link_text": "GitHub page"
+ }
+ ]
+ },
+ "plugins": [
+ {
+ "meta_data": {
+ "Name": "SpellChecker",
+ "Copyright": "(C) 2015 - 2024 Carel Combrink"
+ },
+ "url": "https://github.com/CJCombrink/SpellChecker-Plugin/releases/download/v3.6.0/SpellChecker-Plugin_QtC13.0.0_macos_x64.tar.gz"
+ }
+ ],
+ "vendor": "Carel Combrink",
+ "copyright": "(C) 2015 - 2024 Carel Combrink"
+ }
+ ]
+}
diff --git a/src/plugins/extensionmanager/testdata/thirdpartyplugins.json b/src/plugins/extensionmanager/testdata/thirdpartyplugins.json
new file mode 100644
index 0000000000..910854a68f
--- /dev/null
+++ b/src/plugins/extensionmanager/testdata/thirdpartyplugins.json
@@ -0,0 +1,38 @@
+{
+ "items": [
+ {
+ "name": "SpellChecker",
+ "tags": [ "Editor" ],
+ "platforms": [ "macOS", "Windows", "Linux" ],
+ "license": "os",
+ "is_pack": false,
+ "description": {
+ "paragraphs": [
+ {
+ "text": [
+ "Spellcheck comments in source files."
+ ],
+ "header": "Get started"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://github.com/CJCombrink/SpellChecker-Plugin",
+ "link_text": "GitHub page"
+ }
+ ]
+ },
+ "plugins": [
+ {
+ "meta_data": {
+ "Name": "SpellChecker",
+ "Copyright": "(C) 2015 - 2024 Carel Combrink"
+ },
+ "url": "https://github.com/CJCombrink/SpellChecker-Plugin/releases/download/v3.6.0/SpellChecker-Plugin_QtC13.0.0_macos_x64.tar.gz"
+ }
+ ],
+ "vendor": "Carel Combrink",
+ "copyright": "(C) 2015 - 2024 Carel Combrink"
+ }
+ ]
+}
diff --git a/src/plugins/fakevim/FakeVim.json.in b/src/plugins/fakevim/FakeVim.json.in
index 915b50331b..82975efb8b 100644
--- a/src/plugins/fakevim/FakeVim.json.in
+++ b/src/plugins/fakevim/FakeVim.json.in
@@ -13,6 +13,6 @@
"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" : "VI-style keyboard navigation.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/fakevim/fakevimactions.cpp b/src/plugins/fakevim/fakevimactions.cpp
index 3fd3e9c31f..e6a2a661d5 100644
--- a/src/plugins/fakevim/fakevimactions.cpp
+++ b/src/plugins/fakevim/fakevimactions.cpp
@@ -139,7 +139,7 @@ FakeVimSettings::FakeVimSettings()
startOfLine,
passKeys,
blinkingCursor,
- HostOsInfo::isWindowsHost() ? LayoutItem(systemEncoding) : empty
+ If { HostOsInfo::isWindowsHost(), { systemEncoding } }
},
Column {
incSearch,
@@ -199,7 +199,7 @@ FakeVimSettings::FakeVimSettings()
autoIndent.setValue(true);
smartIndent.setValue(tps.m_autoIndent);
incSearch.setValue(true);
- }),
+ }, nullptr),
},
PushButton {
text(Tr::tr("Set Qt Style")),
@@ -213,7 +213,7 @@ FakeVimSettings::FakeVimSettings()
incSearch.setVolatileValue(true);
backspace.setVolatileValue(QString("indent,eol,start"));
passKeys.setVolatileValue(true);
- }),
+ }, nullptr),
},
PushButton {
text(Tr::tr("Set Plain Style")),
@@ -227,7 +227,7 @@ FakeVimSettings::FakeVimSettings()
incSearch.setVolatileValue(false);
backspace.setVolatileValue(QString());
passKeys.setVolatileValue(false);
- }),
+ }, nullptr),
},
st
},
diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp
index 1e25113fe3..29257505f2 100644
--- a/src/plugins/fakevim/fakevimhandler.cpp
+++ b/src/plugins/fakevim/fakevimhandler.cpp
@@ -6123,7 +6123,7 @@ bool FakeVimHandler::Private::handleExSetCommand(const ExCommand &cmd)
FvBaseAspect *act = s.item(Utils::keyFromString(optionName));
if (!act) {
showMessage(MessageError, Tr::tr("Unknown option:") + ' ' + cmd.args);
- } else if (act->defaultVariantValue().type() == QVariant::Bool) {
+ } else if (act->defaultVariantValue().typeId() == QMetaType::Bool) {
bool oldValue = act->variantValue().toBool();
if (printOption) {
showMessage(MessageInfo, QLatin1String(oldValue ? "" : "no")
diff --git a/src/plugins/fossil/Fossil.json.in b/src/plugins/fossil/Fossil.json.in
index d83238ccca..1d8433b4e4 100644
--- a/src/plugins/fossil/Fossil.json.in
+++ b/src/plugins/fossil/Fossil.json.in
@@ -15,7 +15,7 @@
],
"Category" : "Version Control",
"Description" : "Fossil SCM integration.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"JsonWizardPaths" : [":/fossil/wizard"]
diff --git a/src/plugins/fossil/fossilplugin.cpp b/src/plugins/fossil/fossilplugin.cpp
index d627d13fda..9821607033 100644
--- a/src/plugins/fossil/fossilplugin.cpp
+++ b/src/plugins/fossil/fossilplugin.cpp
@@ -958,12 +958,9 @@ VcsCommand *FossilPluginPrivate::createInitialCheckoutCommand(const QString &sou
//
// So here we want Fossil to save the remote details when specified.
- QStringList args;
- args << fossilClient().vcsCommandString(FossilClient::CloneCommand)
- << extraOptions
- << sourceUrl
- << fossilFileNative;
- command->addJob({fossilClient().vcsBinary(checkoutPath), args}, -1);
+ command->addJob({fossilClient().vcsBinary(checkoutPath),
+ {fossilClient().vcsCommandString(FossilClient::CloneCommand), extraOptions,
+ sourceUrl, fossilFileNative}}, -1);
}
// check out the cloned repository file into the working copy directory;
@@ -977,15 +974,14 @@ VcsCommand *FossilPluginPrivate::createInitialCheckoutCommand(const QString &sou
// set user default to admin user if specified
if (!isLocalRepository
&& !adminUser.isEmpty()) {
- const QStringList args({ "user", "default", adminUser, "--user", adminUser});
- command->addJob({fossilClient().vcsBinary(checkoutPath), args}, -1);
+ command->addJob({fossilClient().vcsBinary(checkoutPath),
+ {"user", "default", adminUser, "--user", adminUser}}, -1);
}
// turn-off autosync if requested
- if (!isLocalRepository
- && disableAutosync) {
- const QStringList args({"settings", "autosync", "off"});
- command->addJob({fossilClient().vcsBinary(checkoutPath), args}, -1);
+ if (!isLocalRepository && disableAutosync) {
+ command->addJob({fossilClient().vcsBinary(checkoutPath), {"settings", "autosync", "off"}},
+ -1);
}
return command;
@@ -993,11 +989,11 @@ VcsCommand *FossilPluginPrivate::createInitialCheckoutCommand(const QString &sou
void FossilPluginPrivate::changed(const QVariant &v)
{
- switch (v.type()) {
- case QVariant::String:
+ switch (v.typeId()) {
+ case QMetaType::QString:
emit repositoryChanged(FilePath::fromVariant(v));
break;
- case QVariant::StringList:
+ case QMetaType::QStringList:
emit filesChanged(v.toStringList());
break;
default:
diff --git a/src/plugins/genericprojectmanager/GenericProjectManager.json.in b/src/plugins/genericprojectmanager/GenericProjectManager.json.in
index ec4eae552e..40a4f88829 100644
--- a/src/plugins/genericprojectmanager/GenericProjectManager.json.in
+++ b/src/plugins/genericprojectmanager/GenericProjectManager.json.in
@@ -14,7 +14,7 @@
],
"Category" : "Build Systems",
"Description" : "Generic support.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/git/Git.json.in b/src/plugins/git/Git.json.in
index db0663a876..37f54ca324 100644
--- a/src/plugins/git/Git.json.in
+++ b/src/plugins/git/Git.json.in
@@ -14,7 +14,7 @@
],
"Category" : "Version Control",
"Description" : "Git integration.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
"Arguments" : [
{
"Name" : "-git-show",
diff --git a/src/plugins/git/changeselectiondialog.cpp b/src/plugins/git/changeselectiondialog.cpp
index 24daa93829..1eb8ba7d65 100644
--- a/src/plugins/git/changeselectiondialog.cpp
+++ b/src/plugins/git/changeselectiondialog.cpp
@@ -172,18 +172,16 @@ void ChangeSelectionDialog::acceptCommand(ChangeCommand command)
//! Set commit message in details
void ChangeSelectionDialog::setDetails()
{
- Theme *theme = creatorTheme();
-
QPalette palette;
if (m_process->result() == ProcessResult::FinishedWithSuccess) {
m_detailsText->setPlainText(m_process->cleanedStdOut());
- palette.setColor(QPalette::Text, theme->color(Theme::TextColorNormal));
+ palette.setColor(QPalette::Text, creatorColor(Theme::TextColorNormal));
m_changeNumberEdit->setPalette(palette);
} else if (m_process->result() == ProcessResult::StartFailed) {
m_detailsText->setPlainText(Tr::tr("Error: Could not start Git."));
} else {
m_detailsText->setPlainText(Tr::tr("Error: Unknown reference"));
- palette.setColor(QPalette::Text, theme->color(Theme::TextColorError));
+ palette.setColor(QPalette::Text, creatorColor(Theme::TextColorError));
m_changeNumberEdit->setPalette(palette);
enableButtons(false);
}
diff --git a/src/plugins/git/gerrit/gerritpushdialog.cpp b/src/plugins/git/gerrit/gerritpushdialog.cpp
index acbf62c9ee..1a29273d09 100644
--- a/src/plugins/git/gerrit/gerritpushdialog.cpp
+++ b/src/plugins/git/gerrit/gerritpushdialog.cpp
@@ -228,7 +228,7 @@ void GerritPushDialog::setChangeRange()
const int currentRange = range.toInt();
QPalette palette = QApplication::palette();
if (currentRange > ReasonableDistance) {
- const QColor errorColor = Utils::creatorTheme()->color(Utils::Theme::TextColorError);
+ const QColor errorColor = Utils::creatorColor(Utils::Theme::TextColorError);
palette.setColor(QPalette::WindowText, errorColor);
palette.setColor(QPalette::ButtonText, errorColor);
labelText.append("\n" + Git::Tr::tr("Are you sure you selected the right target branch?"));
diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp
index 9d4d133ee4..5c60523359 100644
--- a/src/plugins/git/gitclient.cpp
+++ b/src/plugins/git/gitclient.cpp
@@ -1626,7 +1626,7 @@ QString GitClient::synchronousCurrentLocalBranch(const FilePath &workingDirector
if (!branch.isEmpty()) {
const QString refsHeadsPrefix = "refs/heads/";
if (branch.startsWith(refsHeadsPrefix)) {
- branch.remove(0, refsHeadsPrefix.count());
+ branch.remove(0, refsHeadsPrefix.size());
return branch;
}
}
@@ -2531,7 +2531,7 @@ bool GitClient::launchGitBash(const FilePath &workingDirectory)
success = false;
} else {
const FilePath gitBash = git.absolutePath().parentDir() / "git-bash.exe";
- success = Process::startDetached({gitBash, {}}, workingDirectory);
+ success = Process::startDetached(CommandLine{gitBash}, workingDirectory);
}
if (!success)
@@ -3154,7 +3154,7 @@ void GitClient::push(const FilePath &workingDirectory, const QStringList &pushAr
return;
if (pushFailure == PushFailure::NonFastForward) {
- const QColor warnColor = Utils::creatorTheme()->color(Theme::TextColorError);
+ const QColor warnColor = Utils::creatorColor(Theme::TextColorError);
if (QMessageBox::question(
Core::ICore::dialogParent(), Tr::tr("Force Push"),
Tr::tr("Push failed. Would you like to force-push <span style=\"color:#%1\">"
diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp
index af5b267101..d238f7113e 100644
--- a/src/plugins/git/gitplugin.cpp
+++ b/src/plugins/git/gitplugin.cpp
@@ -1766,13 +1766,11 @@ VcsCommand *GitPluginPrivate::createInitialCheckoutCommand(const QString &url,
const QString &localName,
const QStringList &extraArgs)
{
- QStringList args = {"clone", "--progress"};
- args << extraArgs << url << localName;
-
auto command = VcsBaseClient::createVcsCommand(this, baseDirectory,
gitClient().processEnvironment(baseDirectory));
command->addFlags(RunFlags::SuppressStdErr);
- command->addJob({gitClient().vcsBinary(baseDirectory), args}, -1);
+ command->addJob({gitClient().vcsBinary(baseDirectory),
+ {"clone", "--progress", extraArgs, url, localName}}, -1);
return command;
}
diff --git a/src/plugins/git/gitsettings.cpp b/src/plugins/git/gitsettings.cpp
index 2e7f8bf2de..f8056d9a00 100644
--- a/src/plugins/git/gitsettings.cpp
+++ b/src/plugins/git/gitsettings.cpp
@@ -151,7 +151,7 @@ GitSettings::GitSettings()
Group {
title(Tr::tr("Instant Blame")),
- instantBlame.groupChecker(),
+ groupChecker(instantBlame.groupChecker()),
Row { instantBlameIgnoreSpaceChanges, instantBlameIgnoreLineMoves, st },
},
diff --git a/src/plugins/git/gitsubmiteditor.cpp b/src/plugins/git/gitsubmiteditor.cpp
index 3a968a1e86..1e09d6ec9d 100644
--- a/src/plugins/git/gitsubmiteditor.cpp
+++ b/src/plugins/git/gitsubmiteditor.cpp
@@ -11,7 +11,6 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/iversioncontrol.h>
#include <coreplugin/progressmanager/progressmanager.h>
-#include <extensionsystem/pluginmanager.h>
#include <utils/async.h>
#include <utils/qtcassert.h>
#include <vcsbase/submitfilemodel.h>
@@ -210,7 +209,7 @@ void GitSubmitEditor::updateFileModel()
Core::ProgressManager::addTask(m_fetchWatcher.future(), Tr::tr("Refreshing Commit Data"),
TASK_UPDATE_COMMIT);
- ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(m_fetchWatcher.future());
+ Utils::futureSynchronizer()->addFuture(m_fetchWatcher.future());
}
void GitSubmitEditor::forceUpdateFileModel()
diff --git a/src/plugins/git/gitsubmiteditorwidget.cpp b/src/plugins/git/gitsubmiteditorwidget.cpp
index c9b716df26..a6635104ea 100644
--- a/src/plugins/git/gitsubmiteditorwidget.cpp
+++ b/src/plugins/git/gitsubmiteditorwidget.cpp
@@ -120,8 +120,7 @@ void GitSubmitEditorWidget::setPanelInfo(const GitSubmitEditorPanelInfo &info)
{
m_gitSubmitPanel->repositoryLabel->setText(info.repository.toUserOutput());
if (info.branch.contains("(no branch)")) {
- const QString errorColor =
- Utils::creatorTheme()->color(Utils::Theme::TextColorError).name();
+ const QString errorColor = Utils::creatorColor(Utils::Theme::TextColorError).name();
m_gitSubmitPanel->branchLabel->setText(QString::fromLatin1("<span style=\"color:%1\">%2</span>")
.arg(errorColor, Tr::tr("Detached HEAD")));
} else {
diff --git a/src/plugins/git/instantblame.cpp b/src/plugins/git/instantblame.cpp
index 5921518c34..f932c78698 100644
--- a/src/plugins/git/instantblame.cpp
+++ b/src/plugins/git/instantblame.cpp
@@ -166,6 +166,13 @@ void InstantBlame::setup()
connect(EditorManager::instance(), &EditorManager::currentEditorChanged,
this, setupBlameForEditor);
+ connect(EditorManager::instance(), &EditorManager::documentClosed,
+ this, [this](IDocument *doc) {
+ if (m_document != doc)
+ return;
+ disconnect(m_documentChangedConn);
+ m_document = nullptr;
+ });
}
// Porcelain format of git blame output
diff --git a/src/plugins/git/mergetool.cpp b/src/plugins/git/mergetool.cpp
index d0a102c9df..138943ef2c 100644
--- a/src/plugins/git/mergetool.cpp
+++ b/src/plugins/git/mergetool.cpp
@@ -35,9 +35,7 @@ MergeTool::MergeTool(QObject *parent) : QObject(parent)
void MergeTool::start(const FilePath &workingDirectory, const QStringList &files)
{
- QStringList arguments;
- arguments << "mergetool" << "-y" << files;
- const CommandLine cmd = {gitClient().vcsBinary(workingDirectory), arguments};
+ const CommandLine cmd{gitClient().vcsBinary(workingDirectory), {"mergetool", "-y", files}};
VcsOutputWindow::appendCommand(workingDirectory, cmd);
m_process.setCommand(cmd);
m_process.setWorkingDirectory(workingDirectory);
diff --git a/src/plugins/gitlab/GitLab.json.in b/src/plugins/gitlab/GitLab.json.in
index ad55a21d7c..3ddf0264b0 100644
--- a/src/plugins/gitlab/GitLab.json.in
+++ b/src/plugins/gitlab/GitLab.json.in
@@ -14,6 +14,6 @@
"Alternatively, this file may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this file. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html."
],
"Description" : "GitLab plugin.",
-"Url" : "http://www.qt.io",
+"Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/gitlab/gitlaboptionspage.cpp b/src/plugins/gitlab/gitlaboptionspage.cpp
index d550dd4845..03d078ba14 100644
--- a/src/plugins/gitlab/gitlaboptionspage.cpp
+++ b/src/plugins/gitlab/gitlaboptionspage.cpp
@@ -102,7 +102,7 @@ GitLabServerWidget::GitLabServerWidget(Mode m, QWidget *parent)
m_token, br,
m_port, br,
m_secure,
- m == Edit ? normalMargin : noMargin
+ m == Edit ? &Layout::normalMargin : &Layout::noMargin
},
}.attachTo(this);
}
diff --git a/src/plugins/glsleditor/GLSLEditor.json.in b/src/plugins/glsleditor/GLSLEditor.json.in
index 54ef8d548b..ff83b9e725 100644
--- a/src/plugins/glsleditor/GLSLEditor.json.in
+++ b/src/plugins/glsleditor/GLSLEditor.json.in
@@ -14,7 +14,7 @@
],
"Category" : "Other Languages",
"Description" : "Editor for GLSL.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/helloworld/HelloWorld.json.in b/src/plugins/helloworld/HelloWorld.json.in
index bf6fa1587b..9438334fdd 100644
--- a/src/plugins/helloworld/HelloWorld.json.in
+++ b/src/plugins/helloworld/HelloWorld.json.in
@@ -14,6 +14,6 @@
"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" : "Hello World sample plugin.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/help/Help.json.in b/src/plugins/help/Help.json.in
index e6ecc9eeb3..a5d5d3964c 100644
--- a/src/plugins/help/Help.json.in
+++ b/src/plugins/help/Help.json.in
@@ -14,6 +14,6 @@
],
"Category" : "Core",
"Description" : "Help system.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/help/generalsettingspage.cpp b/src/plugins/help/generalsettingspage.cpp
index b0c22ca0f7..a87633a840 100644
--- a/src/plugins/help/generalsettingspage.cpp
+++ b/src/plugins/help/generalsettingspage.cpp
@@ -63,7 +63,6 @@ private:
QFont m_font;
int m_fontZoom = 100;
- QFontDatabase m_fontDatabase;
QString m_homePage;
int m_contextOption;
@@ -400,9 +399,9 @@ void GeneralSettingsPageWidget::exportBookmarks()
void GeneralSettingsPageWidget::updateFontSizeSelector()
{
const QString &family = m_font.family();
- const QString &fontStyle = m_fontDatabase.styleString(m_font);
+ const QString &fontStyle = QFontDatabase::styleString(m_font);
- QList<int> pointSizes = m_fontDatabase.pointSizes(family, fontStyle);
+ QList<int> pointSizes = QFontDatabase::pointSizes(family, fontStyle);
if (pointSizes.empty())
pointSizes = QFontDatabase::standardSizes();
@@ -424,8 +423,8 @@ void GeneralSettingsPageWidget::updateFontSizeSelector()
void GeneralSettingsPageWidget::updateFontStyleSelector()
{
- const QString &fontStyle = m_fontDatabase.styleString(m_font);
- const QStringList &styles = m_fontDatabase.styles(m_font.family());
+ const QString &fontStyle = QFontDatabase::styleString(m_font);
+ const QStringList &styles = QFontDatabase::styles(m_font.family());
QSignalBlocker blocker(styleComboBox);
styleComboBox->clear();
diff --git a/src/plugins/help/helpindexfilter.cpp b/src/plugins/help/helpindexfilter.cpp
index c535fe31b0..2820c634a3 100644
--- a/src/plugins/help/helpindexfilter.cpp
+++ b/src/plugins/help/helpindexfilter.cpp
@@ -10,7 +10,6 @@
#include <coreplugin/helpmanager.h>
#include <coreplugin/icore.h>
-#include <extensionsystem/pluginmanager.h>
#include <utils/async.h>
#include <utils/utilsicons.h>
@@ -95,7 +94,6 @@ LocatorMatcherTasks HelpIndexFilter::matchers()
}
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) {
diff --git a/src/plugins/help/helpmanager.cpp b/src/plugins/help/helpmanager.cpp
index a117afd916..eee8f8fcd4 100644
--- a/src/plugins/help/helpmanager.cpp
+++ b/src/plugins/help/helpmanager.cpp
@@ -2,14 +2,13 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "helpmanager.h"
+#include "localhelpmanager.h"
#include "helptr.h"
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
-#include <extensionsystem/pluginmanager.h>
-
#include <utils/algorithm.h>
#include <utils/async.h>
#include <utils/filesystemwatcher.h>
@@ -140,7 +139,7 @@ void HelpManager::registerDocumentation(const QStringList &files)
}
QFuture<bool> future = Utils::asyncRun(&registerDocumentationNow, collectionFilePath(), files);
- ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(future);
+ Utils::futureSynchronizer()->addFuture(future);
Utils::onResultReady(future, this, [](bool docsChanged){
if (docsChanged) {
d->m_helpEngine->setupData();
@@ -203,7 +202,7 @@ void HelpManager::unregisterDocumentation(const QStringList &files)
d->m_userRegisteredFiles.subtract(Utils::toSet(files));
QFuture<bool> future = Utils::asyncRun(&unregisterDocumentationNow, collectionFilePath(), files);
- ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(future);
+ Utils::futureSynchronizer()->addFuture(future);
Utils::onResultReady(future, this, [](bool docsChanged){
if (docsChanged) {
d->m_helpEngine->setupData();
@@ -284,6 +283,11 @@ void HelpManager::showHelpUrl(const QUrl &url, Core::HelpManager::HelpViewerLoca
emit m_instance->helpRequested(url, location);
}
+void HelpManager::addOnlineHelpHandler(const Core::HelpManager::OnlineHelpHandler &handler)
+{
+ LocalHelpManager::addOnlineHelpHandler(handler);
+}
+
QStringList HelpManager::registeredNamespaces()
{
QTC_ASSERT(!d->m_needsSetup, return {});
diff --git a/src/plugins/help/helpmanager.h b/src/plugins/help/helpmanager.h
index 5f0efe5620..8842586315 100644
--- a/src/plugins/help/helpmanager.h
+++ b/src/plugins/help/helpmanager.h
@@ -57,6 +57,8 @@ public:
const QUrl &url,
Core::HelpManager::HelpViewerLocation location = Core::HelpManager::HelpModeAlways) override;
+ void addOnlineHelpHandler(const Core::HelpManager::OnlineHelpHandler &handler) override;
+
static void setupHelpManager();
signals:
diff --git a/src/plugins/help/helpplugin.cpp b/src/plugins/help/helpplugin.cpp
index 49b4624b26..c729ff575d 100644
--- a/src/plugins/help/helpplugin.cpp
+++ b/src/plugins/help/helpplugin.cpp
@@ -507,8 +507,8 @@ void HelpPluginPrivate::showContextHelp(const HelpItem &contextHelp)
"<font color=\"%3\">%5</font>"
"</center></body></html>")
.arg(Tr::tr("No Documentation"))
- .arg(creatorTheme()->color(Theme::BackgroundColorNormal).name())
- .arg(creatorTheme()->color(Theme::TextColorNormal).name())
+ .arg(creatorColor(Theme::BackgroundColorNormal).name())
+ .arg(creatorColor(Theme::TextColorNormal).name())
.arg(contextHelp.helpIds().join(", "))
.arg(Tr::tr("No documentation available.")));
}
diff --git a/src/plugins/help/helpwidget.cpp b/src/plugins/help/helpwidget.cpp
index 0aeb0b0fd8..08f66b155c 100644
--- a/src/plugins/help/helpwidget.cpp
+++ b/src/plugins/help/helpwidget.cpp
@@ -52,6 +52,24 @@ static const char kModeSideBarSettingsKey[] = "Help/ModeSideBar";
namespace Help {
namespace Internal {
+class ButtonWithMenu : public QToolButton
+{
+public:
+ ButtonWithMenu(QWidget *parent = nullptr)
+ : QToolButton(parent)
+ {}
+
+protected:
+ void mousePressEvent(QMouseEvent *e) override
+ {
+ if (e->button() == Qt::RightButton) {
+ showMenu();
+ return;
+ }
+ QToolButton::mousePressEvent(e);
+ }
+};
+
OpenPagesModel::OpenPagesModel(HelpWidget *parent)
: m_parent(parent)
{}
@@ -282,7 +300,9 @@ HelpWidget::HelpWidget(const Core::Context &context, WidgetStyle style, QWidget
m_backAction->setMenu(m_backMenu);
cmd = Core::ActionManager::registerAction(m_backAction, Constants::HELP_PREVIOUS, context);
cmd->setDefaultKeySequence(QKeySequence::Back);
- button = Core::Command::toolButtonWithAppendedShortcut(m_backAction, cmd);
+ button = new ButtonWithMenu;
+ button->setDefaultAction(m_backAction);
+ cmd->augmentActionWithShortcutToolTip(m_backAction);
button->setPopupMode(QToolButton::DelayedPopup);
layout->addWidget(button);
@@ -293,7 +313,9 @@ HelpWidget::HelpWidget(const Core::Context &context, WidgetStyle style, QWidget
m_forwardAction->setMenu(m_forwardMenu);
cmd = Core::ActionManager::registerAction(m_forwardAction, Constants::HELP_NEXT, context);
cmd->setDefaultKeySequence(QKeySequence::Forward);
- button = Core::Command::toolButtonWithAppendedShortcut(m_forwardAction, cmd);
+ button = new ButtonWithMenu;
+ button->setDefaultAction(m_forwardAction);
+ cmd->augmentActionWithShortcutToolTip(m_forwardAction);
button->setPopupMode(QToolButton::DelayedPopup);
layout->addWidget(button);
diff --git a/src/plugins/help/localhelpmanager.cpp b/src/plugins/help/localhelpmanager.cpp
index 4bca01bf79..738e3337a1 100644
--- a/src/plugins/help/localhelpmanager.cpp
+++ b/src/plugins/help/localhelpmanager.cpp
@@ -53,6 +53,8 @@ QHelpEngine* LocalHelpManager::m_guiEngine = nullptr;
QMutex LocalHelpManager::m_bkmarkMutex;
BookmarkManager* LocalHelpManager::m_bookmarkManager = nullptr;
+QList<Core::HelpManager::OnlineHelpHandler> LocalHelpManager::m_onlineHelpHandlerList;
+
const char kHelpHomePageKey[] = "Help/HomePage";
const char kFontFamilyKey[] = "Help/FallbackFontFamily";
const char kFontStyleNameKey[] = "Help/FallbackFontStyleName";
@@ -86,7 +88,7 @@ static QString defaultFallbackFontFamily()
static QString defaultFallbackFontStyleName(const QString &fontFamily)
{
- const QStringList styles = QFontDatabase().styles(fontFamily);
+ const QStringList styles = QFontDatabase::styles(fontFamily);
QTC_ASSERT(!styles.isEmpty(), return QString("Regular"));
return styles.first();
}
@@ -96,6 +98,8 @@ LocalHelpManager::LocalHelpManager(QObject *parent)
{
m_instance = this;
qRegisterMetaType<Help::Internal::LocalHelpManager::HelpData>("Help::Internal::LocalHelpManager::HelpData");
+
+ addOnlineHelpHandler({LocalHelpManager::isQtUrl, LocalHelpManager::openQtUrl});
}
LocalHelpManager::~LocalHelpManager()
@@ -508,43 +512,64 @@ QHelpFilterEngine *LocalHelpManager::filterEngine()
bool LocalHelpManager::canOpenOnlineHelp(const QUrl &url)
{
+ return Utils::anyOf(
+ m_onlineHelpHandlerList, [url](const Core::HelpManager::OnlineHelpHandler &handler) {
+ return handler.handlesUrl(url);
+ });
+}
+
+bool LocalHelpManager::isQtUrl(const QUrl &url)
+{
const QString address = url.toString();
return address.startsWith("qthelp://org.qt-project.")
|| address.startsWith("qthelp://com.nokia.")
|| address.startsWith("qthelp://com.trolltech.");
}
-bool LocalHelpManager::openOnlineHelp(const QUrl &url)
+void LocalHelpManager::openQtUrl(const QUrl &url)
{
static const QString unversionedLocalDomainName
= QString("org.qt-project.%1").arg(Utils::appInfo().id);
- if (canOpenOnlineHelp(url)) {
- QString urlPrefix = "http://doc.qt.io/";
- if (url.authority().startsWith(unversionedLocalDomainName)) {
- urlPrefix.append(Utils::appInfo().id);
+ QString urlPrefix = "http://doc.qt.io/";
+ if (url.authority().startsWith(unversionedLocalDomainName)) {
+ urlPrefix.append(Utils::appInfo().id);
+ } else {
+ const auto host = url.host();
+ const auto dot = host.lastIndexOf('.');
+ if (dot < 0) {
+ urlPrefix.append("qt-5");
} else {
- const auto host = url.host();
- const auto dot = host.lastIndexOf('.');
- if (dot < 0) {
- urlPrefix.append("qt-5");
+ const auto version = host.mid(dot + 1);
+ if (version.startsWith('6')) {
+ urlPrefix.append("qt-6");
} else {
- const auto version = host.mid(dot + 1);
- if (version.startsWith('6')) {
- urlPrefix.append("qt-6");
- } else {
- urlPrefix.append("qt-5");
- }
+ urlPrefix.append("qt-5");
}
}
- const QString address = url.toString();
- QDesktopServices::openUrl(QUrl(urlPrefix + address.mid(address.lastIndexOf(QLatin1Char('/')))));
- return true;
}
- return false;
+ const QString address = url.toString();
+ QDesktopServices::openUrl(QUrl(urlPrefix + address.mid(address.lastIndexOf(QLatin1Char('/')))));
+}
+
+bool LocalHelpManager::openOnlineHelp(const QUrl &url)
+{
+ return Utils::anyOf(
+ m_onlineHelpHandlerList, [url](const Core::HelpManager::OnlineHelpHandler &handler) {
+ if (handler.handlesUrl(url)) {
+ handler.openUrl(url);
+ return true;
+ }
+ return false;
+ });
}
QMultiMap<QString, QUrl> LocalHelpManager::linksForKeyword(const QString &keyword)
{
return HelpManager::linksForKeyword(&LocalHelpManager::helpEngine(), keyword, std::nullopt);
}
+
+void LocalHelpManager::addOnlineHelpHandler(const Core::HelpManager::OnlineHelpHandler &handler)
+{
+ LocalHelpManager::m_onlineHelpHandlerList.push_back(handler);
+}
diff --git a/src/plugins/help/localhelpmanager.h b/src/plugins/help/localhelpmanager.h
index 5195e7f1b7..13a30555c1 100644
--- a/src/plugins/help/localhelpmanager.h
+++ b/src/plugins/help/localhelpmanager.h
@@ -101,9 +101,13 @@ public:
static bool canOpenOnlineHelp(const QUrl &url);
static bool openOnlineHelp(const QUrl &url);
+ static bool isQtUrl(const QUrl &url);
+ static void openQtUrl(const QUrl &url);
static QMultiMap<QString, QUrl> linksForKeyword(const QString &keyword);
+ static void addOnlineHelpHandler(const Core::HelpManager::OnlineHelpHandler &handler);
+
signals:
void fallbackFontChanged(const QFont &font);
void fontZoomChanged(int percentage);
@@ -121,6 +125,8 @@ private:
static QMutex m_bkmarkMutex;
static BookmarkManager *m_bookmarkManager;
+
+ static QList<Core::HelpManager::OnlineHelpHandler> m_onlineHelpHandlerList;
};
} // Internal
diff --git a/src/plugins/imageviewer/ImageViewer.json.in b/src/plugins/imageviewer/ImageViewer.json.in
index 90ffbcc0c6..f75d4b7535 100644
--- a/src/plugins/imageviewer/ImageViewer.json.in
+++ b/src/plugins/imageviewer/ImageViewer.json.in
@@ -13,7 +13,7 @@
"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" : "Image Viewer component.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/incredibuild/IncrediBuild.json.in b/src/plugins/incredibuild/IncrediBuild.json.in
index d7fb5f242b..f941d3805b 100644
--- a/src/plugins/incredibuild/IncrediBuild.json.in
+++ b/src/plugins/incredibuild/IncrediBuild.json.in
@@ -6,7 +6,7 @@
"Vendor" : "IncrediBuild",
"Copyright" : "(C) IncrediBuild",
"Category" : "Build Systems",
- "Url" : "http://www.IncrediBuild.com",
+ "Url" : "https://www.IncrediBuild.com",
"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.",
diff --git a/src/plugins/incredibuild/commandbuilderaspect.cpp b/src/plugins/incredibuild/commandbuilderaspect.cpp
index 99c29cd269..38d0d858e8 100644
--- a/src/plugins/incredibuild/commandbuilderaspect.cpp
+++ b/src/plugins/incredibuild/commandbuilderaspect.cpp
@@ -112,7 +112,7 @@ void CommandBuilderAspectPrivate::tryToMigrate()
}
}
-void CommandBuilderAspect::addToLayout(Layouting::LayoutItem &parent)
+void CommandBuilderAspect::addToLayout(Layouting::Layout &parent)
{
if (!d->commandBuilder) {
d->commandBuilder = new QComboBox;
diff --git a/src/plugins/incredibuild/commandbuilderaspect.h b/src/plugins/incredibuild/commandbuilderaspect.h
index 3282a18415..2035a1c309 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(Layouting::LayoutItem &parent) final;
+ void addToLayout(Layouting::Layout &parent) final;
void fromMap(const Utils::Store &map) final;
void toMap(Utils::Store &map) const final;
diff --git a/src/plugins/insight/Insight.json.in b/src/plugins/insight/Insight.json.in
index 096cc0569f..382e8ce036 100644
--- a/src/plugins/insight/Insight.json.in
+++ b/src/plugins/insight/Insight.json.in
@@ -16,6 +16,6 @@
"Category" : "Qt Quick",
"Description" : "Qt Insight Support for Design Studio.",
"DisabledByDefault" : true,
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/ios/Ios.json.in b/src/plugins/ios/Ios.json.in
index 2b2e4bae77..16f75d34dc 100644
--- a/src/plugins/ios/Ios.json.in
+++ b/src/plugins/ios/Ios.json.in
@@ -15,6 +15,6 @@
],
"Category" : "Device Support",
"Description" : "Support for deployment to and execution on iOS Devices.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/ios/iosconfigurations.cpp b/src/plugins/ios/iosconfigurations.cpp
index d7ad6bf3be..6d7d559d48 100644
--- a/src/plugins/ios/iosconfigurations.cpp
+++ b/src/plugins/ios/iosconfigurations.cpp
@@ -12,8 +12,6 @@
#include <coreplugin/icore.h>
-#include <extensionsystem/pluginmanager.h>
-
#include <projectexplorer/kitaspects.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/devicesupport/devicemanager.h>
@@ -34,8 +32,8 @@
#include <utils/algorithm.h>
#include <utils/futuresynchronizer.h>
-#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
+#include <utils/qtcprocess.h>
#include <QDir>
#include <QDomDocument>
@@ -386,8 +384,7 @@ void IosConfigurations::updateSimulators()
dev = IDevice::ConstPtr(new IosSimulator(devId));
devManager->addDevice(dev);
}
- ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(
- SimulatorControl::updateAvailableSimulators(this));
+ Utils::futureSynchronizer()->addFuture(SimulatorControl::updateAvailableSimulators(this));
}
void IosConfigurations::setDeveloperPath(const FilePath &devPath)
diff --git a/src/plugins/ios/iosrunconfiguration.cpp b/src/plugins/ios/iosrunconfiguration.cpp
index 23a27e85f6..3fc8c75950 100644
--- a/src/plugins/ios/iosrunconfiguration.cpp
+++ b/src/plugins/ios/iosrunconfiguration.cpp
@@ -337,7 +337,7 @@ IosDeviceTypeAspect::IosDeviceTypeAspect(AspectContainer *container, IosRunConfi
this, &IosDeviceTypeAspect::deviceChanges);
}
-void IosDeviceTypeAspect::addToLayout(Layouting::LayoutItem &parent)
+void IosDeviceTypeAspect::addToLayout(Layouting::Layout &parent)
{
m_deviceTypeComboBox = new QComboBox;
m_deviceTypeComboBox->setModel(&m_deviceTypeModel);
diff --git a/src/plugins/ios/iosrunconfiguration.h b/src/plugins/ios/iosrunconfiguration.h
index 524369605c..4defcf36ee 100644
--- a/src/plugins/ios/iosrunconfiguration.h
+++ b/src/plugins/ios/iosrunconfiguration.h
@@ -31,7 +31,7 @@ public:
void fromMap(const Utils::Store &map) override;
void toMap(Utils::Store &map) const override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &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 e5b03e5229..863a2867b8 100644
--- a/src/plugins/ios/iosrunner.cpp
+++ b/src/plugins/ios/iosrunner.cpp
@@ -237,17 +237,17 @@ GroupItem DeviceCtlRunner::launchTask(const QString &bundleIdentifier)
return SetupResult::StopWithError;
}
process.setCommand({FilePath::fromString("/usr/bin/xcrun"),
- QStringList{"devicectl",
- "device",
- "process",
- "launch",
- "--device",
- m_device->uniqueInternalDeviceId(),
- "--quiet",
- "--json-output",
- "-",
- bundleIdentifier}
- + m_arguments});
+ {"devicectl",
+ "device",
+ "process",
+ "launch",
+ "--device",
+ m_device->uniqueInternalDeviceId(),
+ "--quiet",
+ "--json-output",
+ "-",
+ bundleIdentifier,
+ m_arguments}});
return SetupResult::Continue;
};
const auto onDone = [this](const Process &process, DoneWith result) {
diff --git a/src/plugins/ios/simulatorcontrol.cpp b/src/plugins/ios/simulatorcontrol.cpp
index 4def4c0e47..b702767251 100644
--- a/src/plugins/ios/simulatorcontrol.cpp
+++ b/src/plugins/ios/simulatorcontrol.cpp
@@ -86,20 +86,18 @@ static expected_str<void> runCommand(
}
static expected_str<void> runSimCtlCommand(
- QStringList args,
+ const QStringList &args,
QString *output,
QString *allOutput = nullptr,
std::function<bool()> shouldStop = [] { return false; })
{
- args.prepend("simctl");
-
// Cache xcrun's path, as this function will be called often.
static FilePath xcrun = FilePath::fromString("xcrun").searchInPath();
if (xcrun.isEmpty())
return make_unexpected(Tr::tr("Cannot find xcrun."));
else if (!xcrun.isExecutableFile())
return make_unexpected(Tr::tr("xcrun is not executable."));
- return runCommand({xcrun, args}, output, allOutput, shouldStop);
+ return runCommand({xcrun, {"simctl", args}}, output, allOutput, shouldStop);
}
static expected_str<void> launchSimulator(const QString &simUdid, std::function<bool()> shouldStop)
diff --git a/src/plugins/languageclient/LanguageClient.json.in b/src/plugins/languageclient/LanguageClient.json.in
index 8ccc43c725..0ba67e09dd 100644
--- a/src/plugins/languageclient/LanguageClient.json.in
+++ b/src/plugins/languageclient/LanguageClient.json.in
@@ -14,7 +14,7 @@
],
"Category" : "Other Languages",
"Description" : "Language Server Protocol client component. See https://microsoft.github.io/language-server-protocol/overview for an overview on Language Servers.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp
index f56538d6cb..c12693e480 100644
--- a/src/plugins/languageclient/client.cpp
+++ b/src/plugins/languageclient/client.cpp
@@ -1173,7 +1173,7 @@ void Client::documentContentsSaved(TextEditor::TextDocument *document)
includeText = saveOptions->includeText().value_or(includeText);
}
}
- if (!send)
+ if (!send || !shouldSendDidSave(document))
return;
DidSaveTextDocumentParams params(
TextDocumentIdentifier(hostPathToServerUri(document->filePath())));
diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h
index a881d17824..c80e458a47 100644
--- a/src/plugins/languageclient/client.h
+++ b/src/plugins/languageclient/client.h
@@ -235,6 +235,7 @@ private:
const Utils::FilePath &candidate);
virtual QList<Utils::Text::Range> additionalDocumentHighlights(
TextEditor::TextEditorWidget *, const QTextCursor &) { return {}; }
+ virtual bool shouldSendDidSave(const TextEditor::TextDocument *) const { return true; }
};
} // namespace LanguageClient
diff --git a/src/plugins/languageclient/clientrequest.cpp b/src/plugins/languageclient/clientrequest.cpp
index 3125b25a72..bb853f80e4 100644
--- a/src/plugins/languageclient/clientrequest.cpp
+++ b/src/plugins/languageclient/clientrequest.cpp
@@ -30,7 +30,7 @@ bool ClientWorkspaceSymbolRequest::preStartCheck()
if (!capability.has_value())
return false;
const auto boolvalue = std::get_if<bool>(&*capability);
- if (boolvalue && !boolvalue)
+ if (boolvalue && !*boolvalue)
return false;
return true;
diff --git a/src/plugins/languageclient/languageclientmanager.cpp b/src/plugins/languageclient/languageclientmanager.cpp
index ae3c52f897..8ee2254c74 100644
--- a/src/plugins/languageclient/languageclientmanager.cpp
+++ b/src/plugins/languageclient/languageclientmanager.cpp
@@ -194,7 +194,7 @@ void LanguageClientManager::clientFinished(Client *client)
openDocumentWithClient(document, nullptr);
}
- deleteClient(client);
+ deleteClient(client, unexpectedFinish);
if (isShutdownFinished())
emit managerInstance->shutdownFinished();
}
@@ -234,7 +234,7 @@ void LanguageClientManager::shutdownClient(Client *client)
deleteClient(client);
}
-void LanguageClientManager::deleteClient(Client *client)
+void LanguageClientManager::deleteClient(Client *client, bool unexpected)
{
QTC_ASSERT(managerInstance, return);
QTC_ASSERT(client, return);
@@ -252,7 +252,7 @@ void LanguageClientManager::deleteClient(Client *client)
managerInstance->trackClientDeletion(client);
if (!PluginManager::isShuttingDown())
- emit instance()->clientRemoved(client);
+ emit instance()->clientRemoved(client, unexpected);
}
void LanguageClientManager::shutdown()
diff --git a/src/plugins/languageclient/languageclientmanager.h b/src/plugins/languageclient/languageclientmanager.h
index c288ef453b..12bef4f6fe 100644
--- a/src/plugins/languageclient/languageclientmanager.h
+++ b/src/plugins/languageclient/languageclientmanager.h
@@ -41,7 +41,7 @@ public:
static void restartClient(Client *client);
static void shutdownClient(Client *client);
- static void deleteClient(Client *client);
+ static void deleteClient(Client *client, bool unexpected = false);
static void shutdown();
static bool isShutdownFinished();
@@ -87,7 +87,7 @@ public slots:
signals:
void clientAdded(Client *client);
void clientInitialized(Client *client);
- void clientRemoved(Client *client);
+ void clientRemoved(Client *client, bool unexpected);
void shutdownFinished();
void openCallHierarchy();
diff --git a/src/plugins/languageclient/languageclientsettings.cpp b/src/plugins/languageclient/languageclientsettings.cpp
index b994da6096..4176918015 100644
--- a/src/plugins/languageclient/languageclientsettings.cpp
+++ b/src/plugins/languageclient/languageclientsettings.cpp
@@ -40,6 +40,7 @@
#include <QDialogButtonBox>
#include <QDir>
#include <QFileInfo>
+#include <QFormLayout>
#include <QGroupBox>
#include <QHeaderView>
#include <QJsonDocument>
@@ -817,8 +818,8 @@ static QString startupBehaviorString(BaseSettings::StartBehavior behavior)
return {};
}
-BaseSettingsWidget::BaseSettingsWidget(
- const BaseSettings *settings, QWidget *parent, Layouting::LayoutItems additionalItems)
+BaseSettingsWidget::BaseSettingsWidget(const BaseSettings *settings, QWidget *parent,
+ Layouting::LayoutModifier additionalItems)
: QWidget(parent)
, m_name(new QLineEdit(settings->m_name, this))
, m_mimeTypes(new QLabel(settings->m_languageFilter.mimeTypes.join(filterSeparator), this))
@@ -878,7 +879,10 @@ BaseSettingsWidget::BaseSettingsWidget(
Tr::tr("Initialization options:"), m_initializationOptions, br
};
- form.addItems(additionalItems);
+
+ if (additionalItems)
+ additionalItems(&form);
+
form.attachTo(this);
// clang-format on
}
diff --git a/src/plugins/languageclient/languageclientsettings.h b/src/plugins/languageclient/languageclientsettings.h
index ad2f01ac74..a6055577bc 100644
--- a/src/plugins/languageclient/languageclientsettings.h
+++ b/src/plugins/languageclient/languageclientsettings.h
@@ -92,9 +92,6 @@ protected:
BaseSettings(BaseSettings &&other) = default;
BaseSettings &operator=(const BaseSettings &other) = default;
BaseSettings &operator=(BaseSettings &&other) = default;
-
-private:
- bool canStart(QList<const Core::IDocument *> documents) const;
};
class LANGUAGECLIENT_EXPORT StdIOSettings : public BaseSettings
@@ -162,7 +159,7 @@ public:
explicit BaseSettingsWidget(
const BaseSettings *settings,
QWidget *parent = nullptr,
- Layouting::LayoutItems additionalItems = {});
+ Layouting::LayoutModifier additionalItems = {});
~BaseSettingsWidget() override = default;
diff --git a/src/plugins/languageclient/languageclientutils.cpp b/src/plugins/languageclient/languageclientutils.cpp
index 4924654ffc..f7b52fdc9b 100644
--- a/src/plugins/languageclient/languageclientutils.cpp
+++ b/src/plugins/languageclient/languageclientutils.cpp
@@ -97,8 +97,7 @@ bool applyTextEdits(const Client *client,
if (edits.isEmpty())
return true;
const RefactoringFilePtr file = client->createRefactoringFile(filePath);
- file->setChangeSet(editsToChangeSet(edits, file->document()));
- return file->apply();
+ return file->apply(editsToChangeSet(edits, file->document()));
}
void applyTextEdit(TextDocumentManipulatorInterface &manipulator,
diff --git a/src/plugins/languageclient/locatorfilter.cpp b/src/plugins/languageclient/locatorfilter.cpp
index ccebcd7679..4fb8cea19a 100644
--- a/src/plugins/languageclient/locatorfilter.cpp
+++ b/src/plugins/languageclient/locatorfilter.cpp
@@ -8,8 +8,6 @@
#include "languageclientmanager.h"
#include "languageclienttr.h"
-#include <extensionsystem/pluginmanager.h>
-
#include <utils/async.h>
#include <utils/fuzzymatcher.h>
@@ -70,7 +68,6 @@ LocatorMatcherTask locatorMatcher(Client *client, int maxResultCount,
const QList<SymbolInformation> results = *resultStorage;
if (results.isEmpty())
return SetupResult::StopWithSuccess;
- async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
async.setConcurrentCallData(filterResults, *storage, client, results, filter);
return SetupResult::Continue;
};
@@ -130,7 +127,6 @@ LocatorMatcherTask currentDocumentMatcher()
};
const auto onFilterSetup = [storage, resultStorage](Async<void> &async) {
- async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
async.setConcurrentCallData(filterCurrentResults, *storage, *resultStorage);
};
diff --git a/src/plugins/languageclient/lualanguageclient/LuaLanguageClient.json.in b/src/plugins/languageclient/lualanguageclient/LuaLanguageClient.json.in
index a74e87f34c..9497e53668 100644
--- a/src/plugins/languageclient/lualanguageclient/LuaLanguageClient.json.in
+++ b/src/plugins/languageclient/lualanguageclient/LuaLanguageClient.json.in
@@ -16,6 +16,6 @@
],
"Category" : "Scripting",
"Description" : "Lua Language Client scripting support",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/languageclient/lualanguageclient/lualanguageclient.cpp b/src/plugins/languageclient/lualanguageclient/lualanguageclient.cpp
index bcd9ade763..44fd187a72 100644
--- a/src/plugins/languageclient/lualanguageclient/lualanguageclient.cpp
+++ b/src/plugins/languageclient/lualanguageclient/lualanguageclient.cpp
@@ -1,9 +1,12 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include <coreplugin/editormanager/editormanager.h>
+
#include <languageclient/languageclientinterface.h>
#include <languageclient/languageclientmanager.h>
#include <languageclient/languageclientsettings.h>
+#include <languageclient/languageclienttr.h>
#include <lua/bindings/inheritance.h>
#include <lua/luaengine.h>
@@ -27,6 +30,19 @@ namespace LanguageClient::Lua {
static void registerLuaApi();
+class LuaClient : public LanguageClient::Client
+{
+ Q_OBJECT
+
+public:
+ Utils::Id m_settingsId;
+
+ LuaClient(BaseClientInterface *interface, Utils::Id settingsId)
+ : LanguageClient::Client(interface)
+ , m_settingsId(settingsId)
+ {}
+};
+
class LuaLanguageClientPlugin final : public ExtensionSystem::IPlugin
{
Q_OBJECT
@@ -135,6 +151,8 @@ public:
BaseSettings *copy() const override { return new LuaClientSettings(*this); }
protected:
+ Client *createClient(BaseClientInterface *interface) const final;
+
BaseClientInterface *createInterface(ProjectExplorer::Project *project) const override;
};
enum class TransportType { StdIO, LocalSocket };
@@ -154,6 +172,7 @@ public:
BaseSettings::StartBehavior m_startBehavior = BaseSettings::RequiresFile;
std::optional<sol::protected_function> m_onInstanceStart;
+ std::optional<sol::protected_function> m_startFailedCallback;
QMap<QString, sol::protected_function> m_messageCallbacks;
QList<Client *> m_clients;
@@ -190,6 +209,8 @@ public:
m_startBehavior = startBehaviorFromString(
options.get_or<QString>("startBehavior", "AlwaysOn"));
+ m_startFailedCallback = options.get<sol::protected_function>("onStartFailed");
+
QString transportType = options.get_or<QString>("transport", "stdio");
if (transportType == "stdio")
m_transportType = TransportType::StdIO;
@@ -231,35 +252,46 @@ public:
// get<sol::optional<>> because on MSVC, get_or(..., nullptr) fails to compile
m_aspects = options.get<sol::optional<AspectContainer *>>("settings").value_or(nullptr);
+ if (m_aspects) {
+ connect(m_aspects, &AspectContainer::applied, this, [this] {
+ updateOptions();
+ LanguageClientManager::applySettings();
+ });
+ }
+
connect(
LanguageClientManager::instance(),
&LanguageClientManager::clientInitialized,
this,
[this](Client *c) {
- if (m_onInstanceStart) {
- if (auto settings = LanguageClientManager::settingForClient(c)) {
- if (settings->m_settingsTypeId == m_settingsTypeId) {
- auto result = m_onInstanceStart->call();
-
- if (!result.valid()) {
- qWarning() << "Error calling instance start callback:"
- << (result.get<sol::error>().what());
- }
-
- m_clients.push_back(c);
- updateMessageCallbacks();
- }
- }
+ auto luaClient = qobject_cast<LuaClient *>(c);
+ if (luaClient && luaClient->m_settingsId == m_settingsTypeId && m_onInstanceStart) {
+ QTC_CHECK(::Lua::LuaEngine::void_safe_call(*m_onInstanceStart, c));
+
+ m_clients.push_back(c);
+ updateMessageCallbacks();
}
});
+
connect(
LanguageClientManager::instance(),
&LanguageClientManager::clientRemoved,
this,
- [this](Client *c) {
- if (m_clients.contains(c))
- m_clients.removeOne(c);
- });
+ &LuaClientWrapper::onClientRemoved);
+ }
+
+ void onClientRemoved(Client *c, bool unexpected)
+ {
+ auto luaClient = qobject_cast<LuaClient *>(c);
+ if (!luaClient || luaClient->m_settingsId != m_settingsTypeId)
+ return;
+
+ if (m_clients.contains(c))
+ m_clients.removeOne(c);
+
+ if (unexpected && m_startFailedCallback) {
+ QTC_CHECK_EXPECTED(::Lua::LuaEngine::void_safe_call(*m_startFailedCallback));
+ }
}
// TODO: Unregister Client settings from LanguageClientManager
@@ -288,10 +320,10 @@ public:
m_aspects->toMap(map);
}
- std::optional<Layouting::LayoutItem> settingsLayout()
+ Layouting::LayoutModifier settingsLayout()
{
- if (m_aspects && m_aspects->layouter())
- return m_aspects->layouter()();
+ if (m_aspects)
+ return [this](Layouting::Layout *iface) { m_aspects->addToLayout(*iface); };
return {};
}
@@ -451,12 +483,17 @@ QWidget *LuaClientSettings::createSettingsWidget(QWidget *parent) const
using namespace Layouting;
if (auto w = m_wrapper.lock())
- if (std::optional<LayoutItem> layout = w->settingsLayout())
- return new BaseSettingsWidget(this, parent, layout->subItems);
+ return new BaseSettingsWidget(this, parent, w->settingsLayout());
return new BaseSettingsWidget(this, parent);
}
+Client *LuaClientSettings::createClient(BaseClientInterface *interface) const
+{
+ Client *client = new LuaClient(interface, m_settingsTypeId);
+ return client;
+}
+
BaseClientInterface *LuaClientSettings::createInterface(ProjectExplorer::Project *project) const
{
if (auto w = m_wrapper.lock())
diff --git a/src/plugins/lua/CMakeLists.txt b/src/plugins/lua/CMakeLists.txt
index 9663ef5081..218affe6c3 100644
--- a/src/plugins/lua/CMakeLists.txt
+++ b/src/plugins/lua/CMakeLists.txt
@@ -10,6 +10,7 @@ add_qtc_plugin(Lua
bindings/fetch.cpp
bindings/hook.cpp
bindings/inheritance.h
+ bindings/install.cpp
bindings/layout.cpp
bindings/messagemanager.cpp
bindings/qtcprocess.cpp
@@ -23,6 +24,22 @@ add_qtc_plugin(Lua
luaqttypes.cpp
luaqttypes.h
luatr.h
+ meta/action.lua
+ meta/async.lua
+ meta/core.lua
+ meta/fetch.lua
+ meta/install.lua
+ meta/layout.lua
+ meta/lsp.lua
+ meta/messagemanager.lua
+ meta/process.lua
+ meta/qt.lua
+ meta/qtc.lua
+ meta/settings.lua
+ meta/simpletypes.lua
+ meta/utils.lua
+ meta/widgets.lua
+ meta/wizard.lua
# generateqtbindings.cpp # Use this if you need to generate some code.
)
@@ -33,3 +50,31 @@ if (MSVC)
# Prevent fatal error C1128
set_property(SOURCE bindings/settings.cpp PROPERTY COMPILE_FLAGS /bigobj)
endif()
+
+set(META_FILES
+ meta/action.lua
+ meta/async.lua
+ meta/core.lua
+ meta/fetch.lua
+ meta/layout.lua
+ meta/lsp.lua
+ meta/messagemanager.lua
+ meta/process.lua
+ meta/qt.lua
+ meta/qtc.lua
+ meta/settings.lua
+ meta/simpletypes.lua
+ meta/utils.lua
+ meta/widgets.lua
+ meta/wizard.lua
+)
+
+qtc_copy_to_builddir(copy_lua_meta_files
+ DESTINATION ${IDE_DATA_PATH}/lua
+ FILES ${META_FILES}
+)
+
+install(
+ FILES ${META_FILES}
+ DESTINATION ${IDE_DATA_PATH}/lua/meta
+)
diff --git a/src/plugins/lua/Lua.json.in b/src/plugins/lua/Lua.json.in
index 7007945934..c41ce7bc88 100644
--- a/src/plugins/lua/Lua.json.in
+++ b/src/plugins/lua/Lua.json.in
@@ -16,6 +16,6 @@
],
"Category" : "Scripting",
"Description" : "Support for Lua based plugins.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/lua/bindings/fetch.cpp b/src/plugins/lua/bindings/fetch.cpp
index 0790ed8e29..35e3255943 100644
--- a/src/plugins/lua/bindings/fetch.cpp
+++ b/src/plugins/lua/bindings/fetch.cpp
@@ -8,6 +8,7 @@
#include <QJsonArray>
#include <QJsonDocument>
+#include <QMetaEnum>
#include <QNetworkAccessManager>
#include <QNetworkReply>
@@ -38,6 +39,8 @@ void addFetchModule()
{
LuaEngine::registerProvider(
"Fetch", [](sol::state_view lua) -> sol::object {
+ const ScriptPluginSpec *pluginSpec = lua.get<ScriptPluginSpec *>("PluginSpec");
+
sol::table async = lua.script("return require('async')", "_fetch_").get<sol::table>();
sol::function wrap = async["wrap"];
@@ -57,9 +60,10 @@ void addFetchModule()
.arg(r->error());
});
- fetch["fetch_cb"] = [](const sol::table &options,
- const sol::function &callback,
- const sol::this_state &thisState) {
+ fetch["fetch_cb"] = [guard = pluginSpec->connectionGuard.get()](
+ const sol::table &options,
+ const sol::function &callback,
+ const sol::this_state &thisState) {
auto url = options.get<QString>("url");
auto method = (options.get_or<QString>("method", "GET")).toLower();
@@ -84,15 +88,16 @@ void addFetchModule()
if (convertToTable) {
QObject::connect(
- reply,
- &QNetworkReply::finished,
- &LuaEngine::instance(),
- [reply, thisState, callback]() {
+ reply, &QNetworkReply::finished, guard, [reply, thisState, callback]() {
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
- callback(
- QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()));
+ callback(QString("%1 (%2):\n%3")
+ .arg(reply->errorString())
+ .arg(QString::fromLatin1(
+ QMetaEnum::fromType<QNetworkReply::NetworkError>()
+ .valueToKey(reply->error())))
+ .arg(QString::fromUtf8(reply->readAll())));
return;
}
@@ -115,7 +120,7 @@ void addFetchModule()
} else {
QObject::connect(
- reply, &QNetworkReply::finished, &LuaEngine::instance(), [reply, callback]() {
+ reply, &QNetworkReply::finished, guard, [reply, callback]() {
// We don't want the network reply to be deleted by the manager, but
// by the Lua GC
reply->setParent(nullptr);
diff --git a/src/plugins/lua/bindings/hook.cpp b/src/plugins/lua/bindings/hook.cpp
index 261e6fe115..b626a83308 100644
--- a/src/plugins/lua/bindings/hook.cpp
+++ b/src/plugins/lua/bindings/hook.cpp
@@ -46,15 +46,21 @@ void addHookModule()
[](Hook *, QMetaObject::Connection con) { QObject::disconnect(con); });
});
- LuaEngine::registerHook("editors.documentOpened", [](const sol::function &func) {
- QObject::connect(Core::EditorManager::instance(),
- &Core::EditorManager::documentOpened,
- [func](Core::IDocument *document) { func(document); });
+ LuaEngine::registerHook("editors.documentOpened", [](const sol::protected_function &func) {
+ QObject::connect(
+ Core::EditorManager::instance(),
+ &Core::EditorManager::documentOpened,
+ [func](Core::IDocument *document) {
+ QTC_CHECK_EXPECTED(LuaEngine::void_safe_call(func, document));
+ });
});
- LuaEngine::registerHook("editors.documentClosed", [](const sol::function &func) {
- QObject::connect(Core::EditorManager::instance(),
- &Core::EditorManager::documentClosed,
- [func](Core::IDocument *document) { func(document); });
+ LuaEngine::registerHook("editors.documentClosed", [](const sol::protected_function &func) {
+ QObject::connect(
+ Core::EditorManager::instance(),
+ &Core::EditorManager::documentClosed,
+ [func](Core::IDocument *document) {
+ QTC_CHECK_EXPECTED(LuaEngine::void_safe_call(func, document));
+ });
});
}
diff --git a/src/plugins/lua/bindings/inheritance.h b/src/plugins/lua/bindings/inheritance.h
index 6336ff4d2d..5e6703e75f 100644
--- a/src/plugins/lua/bindings/inheritance.h
+++ b/src/plugins/lua/bindings/inheritance.h
@@ -72,43 +72,47 @@ SOL_DERIVED_CLASSES(
Utils::AspectList);
namespace Layouting {
-class LayoutItem;
+class Object;
+
+class Layout;
class Column;
-class Row;
class Flow;
-class Grid;
class Form;
+class Grid;
+
class Widget;
-class Stack;
-class Tab;
class Group;
-class TextEdit;
class PushButton;
+class Row;
class SpinBox;
class Splitter;
-class ToolBar;
+class Stack;
+class Tab;
class TabWidget;
-class Group;
+class TextEdit;
+class ToolBar;
} // namespace Layouting
-SOL_BASE_CLASSES(Layouting::Column, Layouting::LayoutItem);
-SOL_BASE_CLASSES(Layouting::Row, Layouting::LayoutItem);
-SOL_BASE_CLASSES(Layouting::Flow, Layouting::LayoutItem);
-SOL_BASE_CLASSES(Layouting::Grid, Layouting::LayoutItem);
-SOL_BASE_CLASSES(Layouting::Form, Layouting::LayoutItem);
-SOL_BASE_CLASSES(Layouting::Widget, Layouting::LayoutItem);
-SOL_BASE_CLASSES(Layouting::Stack, Layouting::LayoutItem);
-SOL_BASE_CLASSES(Layouting::Tab, Layouting::LayoutItem);
-SOL_BASE_CLASSES(Layouting::Group, Layouting::LayoutItem);
-SOL_BASE_CLASSES(Layouting::TextEdit, Layouting::LayoutItem);
-SOL_BASE_CLASSES(Layouting::PushButton, Layouting::LayoutItem);
-SOL_BASE_CLASSES(Layouting::SpinBox, Layouting::LayoutItem);
-SOL_BASE_CLASSES(Layouting::Splitter, Layouting::LayoutItem);
-SOL_BASE_CLASSES(Layouting::ToolBar, Layouting::LayoutItem);
-SOL_BASE_CLASSES(Layouting::TabWidget, Layouting::LayoutItem);
+SOL_BASE_CLASSES(Layouting::Layout, Layouting::Object);
+SOL_BASE_CLASSES(Layouting::Column, Layouting::Layout);
+SOL_BASE_CLASSES(Layouting::Row, Layouting::Layout);
+SOL_BASE_CLASSES(Layouting::Flow, Layouting::Layout);
+SOL_BASE_CLASSES(Layouting::Grid, Layouting::Layout);
+SOL_BASE_CLASSES(Layouting::Form, Layouting::Layout);
+SOL_BASE_CLASSES(Layouting::Widget, Layouting::Object);
+SOL_BASE_CLASSES(Layouting::Stack, Layouting::Widget);
+SOL_BASE_CLASSES(Layouting::Tab, Layouting::Widget);
+SOL_BASE_CLASSES(Layouting::Group, Layouting::Widget);
+SOL_BASE_CLASSES(Layouting::TextEdit, Layouting::Widget);
+SOL_BASE_CLASSES(Layouting::PushButton, Layouting::Widget);
+SOL_BASE_CLASSES(Layouting::SpinBox, Layouting::Widget);
+SOL_BASE_CLASSES(Layouting::Splitter, Layouting::Widget);
+SOL_BASE_CLASSES(Layouting::ToolBar, Layouting::Widget);
+SOL_BASE_CLASSES(Layouting::TabWidget, Layouting::Widget);
SOL_DERIVED_CLASSES(
- Layouting::LayoutItem,
+ Layouting::Object,
+ Layouting::Layout,
Layouting::Column,
Layouting::Row,
Layouting::Flow,
@@ -124,3 +128,4 @@ SOL_DERIVED_CLASSES(
Layouting::Splitter,
Layouting::ToolBar,
Layouting::TabWidget);
+
diff --git a/src/plugins/lua/bindings/install.cpp b/src/plugins/lua/bindings/install.cpp
new file mode 100644
index 0000000000..75fb3cadb1
--- /dev/null
+++ b/src/plugins/lua/bindings/install.cpp
@@ -0,0 +1,351 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "../luaengine.h"
+#include "../luatr.h"
+
+#include <coreplugin/icore.h>
+#include <coreplugin/progressmanager/progressmanager.h>
+#include <coreplugin/progressmanager/taskprogress.h>
+
+#include <solutions/tasking/networkquery.h>
+#include <solutions/tasking/tasktree.h>
+
+#include <utils/algorithm.h>
+#include <utils/infobar.h>
+#include <utils/networkaccessmanager.h>
+#include <utils/stylehelper.h>
+#include <utils/unarchiver.h>
+
+#include <QJsonDocument>
+#include <QLabel>
+#include <QTemporaryFile>
+#include <QtConcurrent>
+
+using namespace Core;
+using namespace Tasking;
+using namespace Utils;
+
+namespace Lua::Internal {
+
+expected_str<QJsonDocument> getPackageInfo(const FilePath &appDataPath)
+{
+ const FilePath packageInfoPath = appDataPath / "package.json";
+
+ if (!packageInfoPath.exists())
+ return QJsonDocument();
+
+ expected_str<QByteArray> json = packageInfoPath.fileContents();
+ if (!json)
+ return make_unexpected(json.error());
+
+ if (json->isEmpty())
+ return QJsonDocument();
+
+ QJsonParseError error;
+ QJsonDocument doc(QJsonDocument::fromJson(*json, &error));
+ if (error.error != QJsonParseError::NoError)
+ return make_unexpected(error.errorString());
+
+ if (!doc.isObject())
+ return make_unexpected(Tr::tr("Package info is not an object"));
+
+ return doc;
+}
+
+expected_str<QJsonObject> getInstalledPackageInfo(const FilePath &appDataPath, const QString &name)
+{
+ auto packageDoc = getPackageInfo(appDataPath);
+ if (!packageDoc)
+ return make_unexpected(packageDoc.error());
+
+ QJsonObject root = packageDoc->object();
+
+ if (root.contains(name)) {
+ QJsonValue v = root[name];
+ if (!v.isObject())
+ return make_unexpected(Tr::tr("Installed package info is not an object"));
+ return v.toObject();
+ }
+
+ return QJsonObject();
+}
+
+expected_str<QJsonDocument> getOrCreatePackageInfo(const FilePath &appDataPath)
+{
+ expected_str<QJsonDocument> doc = getPackageInfo(appDataPath);
+ if (doc && doc->isObject())
+ return doc;
+
+ QJsonObject obj;
+ return QJsonDocument(obj);
+}
+
+expected_str<void> savePackageInfo(const FilePath &appDataPath, const QJsonDocument &doc)
+{
+ if (!appDataPath.ensureWritableDir())
+ return make_unexpected(Tr::tr("Could not create app data directory"));
+
+ const FilePath packageInfoPath = appDataPath / "package.json";
+ return packageInfoPath.writeFileContents(doc.toJson())
+ .transform_error([](const QString &error) {
+ return Tr::tr("Could not write to package info: %1").arg(error);
+ })
+ .transform([](qint64) { return; });
+}
+
+struct InstallOptions
+{
+ QUrl url;
+ QString name;
+ QString version;
+};
+
+size_t qHash(const InstallOptions &item, size_t seed = 0)
+{
+ return qHash(item.url, seed) ^ qHash(item.name, seed) ^ qHash(item.version, seed);
+}
+
+static FilePath destination(const FilePath &appDataPath, const InstallOptions &options)
+{
+ return appDataPath / "packages" / options.name / options.version;
+}
+
+static Group installRecipe(
+ const FilePath &appDataPath,
+ const QList<InstallOptions> &installOptions,
+ const sol::protected_function &callback)
+{
+ Storage<QFile> storage;
+
+ const LoopList<InstallOptions> installOptionsIt(installOptions);
+
+ const auto emitResult = [callback](const QString &error = QString()) {
+ if (error.isEmpty()) {
+ LuaEngine::void_safe_call(callback, true);
+ return DoneResult::Success;
+ }
+ LuaEngine::void_safe_call(callback, false, error);
+ return DoneResult::Error;
+ };
+
+ const auto onDownloadSetup = [installOptionsIt](NetworkQuery &query) {
+ query.setRequest(QNetworkRequest(installOptionsIt->url));
+ query.setNetworkAccessManager(NetworkAccessManager::instance());
+ return SetupResult::Continue;
+ };
+
+ const auto onDownloadDone = [emitResult, storage](const NetworkQuery &query, DoneWith result) {
+ if (result == DoneWith::Error)
+ return emitResult(query.reply()->errorString());
+ if (result == DoneWith::Cancel)
+ return DoneResult::Error;
+
+ QNetworkReply *reply = query.reply();
+ const auto size = reply->size();
+ const auto written = storage->write(reply->readAll());
+ if (written != size)
+ return emitResult(Tr::tr("Could not write to temporary file"));
+ storage->close();
+ return DoneResult::Success;
+ };
+
+ const auto onUnarchiveSetup =
+ [appDataPath, installOptionsIt, storage, emitResult](Unarchiver &unarchiver) {
+ const auto sourceAndCommand = Unarchiver::sourceAndCommand(
+ FilePath::fromUserInput(storage->fileName()));
+
+ if (!sourceAndCommand) {
+ emitResult(sourceAndCommand.error());
+ return SetupResult::StopWithError;
+ }
+ unarchiver.setSourceAndCommand(*sourceAndCommand);
+ unarchiver.setDestDir(destination(appDataPath, *installOptionsIt));
+ return SetupResult::Continue;
+ };
+
+ const auto onUnarchiverDone = [appDataPath, installOptionsIt, emitResult](DoneWith result) {
+ if (result == DoneWith::Error)
+ return emitResult(Tr::tr("Unarchiving failed"));
+ if (result == DoneWith::Cancel)
+ return DoneResult::Error;
+
+ expected_str<QJsonDocument> doc = getOrCreatePackageInfo(appDataPath);
+ if (!doc)
+ return emitResult(doc.error());
+
+ QJsonObject obj = doc->object();
+ QJsonObject installedPackage;
+ installedPackage["version"] = installOptionsIt->version;
+ installedPackage["name"] = installOptionsIt->name;
+ installedPackage["path"] = destination(appDataPath, *installOptionsIt).toFSPathString();
+ obj[installOptionsIt->name] = installedPackage;
+
+ expected_str<void> res = savePackageInfo(appDataPath, QJsonDocument(obj));
+ if (!res)
+ return emitResult(res.error());
+ return DoneResult::Success;
+ };
+
+ return Group{
+ storage,
+ parallelIdealThreadCountLimit,
+ installOptionsIt,
+ Group{
+ onGroupSetup([emitResult, storage, installOptionsIt] {
+ const QString fileName = installOptionsIt->url.fileName();
+ const QString ext = fileName.mid(fileName.indexOf('.'));
+ {
+ QTemporaryFile tempFile(QDir::tempPath() + "/XXXXXX" + ext);
+ tempFile.setAutoRemove(false);
+ tempFile.open();
+ (*storage).setFileName(tempFile.fileName());
+ }
+
+ if (!storage->open(QIODevice::WriteOnly)) {
+ emitResult(Tr::tr("Could not open temporary file"));
+ return SetupResult::StopWithError;
+ }
+ return SetupResult::Continue;
+ }),
+ NetworkQueryTask(onDownloadSetup, onDownloadDone),
+ UnarchiverTask(onUnarchiveSetup, onUnarchiverDone),
+ onGroupDone([storage, emitResult] { storage->remove(); }),
+ },
+ onGroupDone([emitResult](DoneWith result) {
+ if (result == DoneWith::Cancel)
+ emitResult("Installation was canceled");
+ else if (result == DoneWith::Success)
+ emitResult();
+ }),
+ };
+}
+
+void addInstallModule()
+{
+ class State
+ {
+ public:
+ State() = default;
+ State(const State &) {}
+ ~State()
+ {
+ for (auto tree : m_trees)
+ delete tree;
+ }
+
+ TaskTree *createTree()
+ {
+ auto tree = new TaskTree();
+ m_trees.append(tree);
+ QObject::connect(tree, &TaskTree::done, tree, &QObject::deleteLater);
+ return tree;
+ };
+
+ private:
+ QList<QPointer<TaskTree>> m_trees;
+ };
+
+ LuaEngine::registerProvider(
+ "Install", [state = State()](sol::state_view lua) mutable -> sol::object {
+ sol::table async
+ = lua.script("return require('async')", "_install_async_").get<sol::table>();
+ sol::function wrap = async["wrap"];
+
+ sol::table install = lua.create_table();
+ const ScriptPluginSpec *pluginSpec = lua.get<ScriptPluginSpec *>("PluginSpec");
+
+ install["packageInfo"] =
+ [pluginSpec](const QString &name, sol::this_state l) -> sol::optional<sol::table> {
+ expected_str<QJsonObject> obj
+ = getInstalledPackageInfo(pluginSpec->appDataPath, name);
+ if (!obj)
+ throw sol::error(obj.error().toStdString());
+
+ return sol::table::create_with(
+ l.lua_state(),
+ "name",
+ obj->value("name").toString(),
+ "version",
+ obj->value("version").toString(),
+ "path",
+ FilePath::fromUserInput(obj->value("path").toString()));
+ };
+
+ install["install_cb"] =
+ [pluginSpec, &state](
+ const QString &msg,
+ const sol::table &installOptions,
+ const sol::function &callback) {
+ QList<InstallOptions> installOptionsList;
+ if (installOptions.size() > 0) {
+ for (const auto &pair : installOptions) {
+ const sol::object &value = pair.second;
+ if (value.get_type() != sol::type::table)
+ throw sol::error("Install options must be a table");
+ const sol::table &table = value.as<sol::table>();
+ const QString name = table["name"];
+ const QUrl url = QUrl::fromUserInput(table["url"]);
+ if (url.scheme() != "https")
+ throw sol::error("Only HTTPS is supported");
+
+ const QString version = table["version"];
+ installOptionsList.append({url, name, version});
+ }
+ } else {
+ const QString name = installOptions["name"];
+ const QUrl url = QUrl::fromUserInput(installOptions["url"]);
+ const QString version = installOptions["version"];
+ if (url.scheme() != "https")
+ throw sol::error("Only HTTPS is supported");
+ installOptionsList.append({url, name, version});
+ }
+
+ const Utils::Id infoBarId = Utils::Id::fromString(
+ "Install" + pluginSpec->name + QString::number(qHash(installOptionsList)));
+
+ InfoBarEntry entry(infoBarId, msg, InfoBarEntry::GlobalSuppression::Enabled);
+
+ entry.addCustomButton(
+ Tr::tr("Install"),
+ [infoBarId, &state, pluginSpec, installOptionsList, callback]() {
+ auto tree = state.createTree();
+
+ auto progress = new TaskProgress(tree);
+ progress->setDisplayName(Tr::tr("Installing package(s) %1").arg("..."));
+
+ tree->setRecipe(
+ installRecipe(pluginSpec->appDataPath, installOptionsList, callback));
+ tree->start();
+
+ Core::ICore::infoBar()->removeInfo(infoBarId);
+ });
+ entry.setCancelButtonInfo(
+ [callback]() { callback(false, "User denied installation"); });
+
+ entry.setDetailsWidgetCreator([pluginSpec, installOptionsList]() -> QWidget * {
+ const QString markdown
+ = Tr::tr("The plugin \"**%1**\" would like to install the following "
+ "package(s):\n\n")
+ .arg(pluginSpec->name)
+ + Utils::transform(installOptionsList, [](const InstallOptions &options) {
+ return QString("* %1 - %2 (from: [%3](%3))")
+ .arg(options.name, options.version, options.url.toString());
+ }).join("\n");
+
+ QLabel *list = new QLabel();
+ list->setTextFormat(Qt::TextFormat::MarkdownText);
+ list->setText(markdown);
+ list->setMargin(StyleHelper::SpacingTokens::ExPaddingGapS);
+ return list;
+ });
+ Core::ICore::infoBar()->addInfo(entry);
+ };
+
+ install["install"] = wrap(install["install_cb"]);
+
+ return install;
+ });
+}
+
+} // namespace Lua::Internal
diff --git a/src/plugins/lua/bindings/layout.cpp b/src/plugins/lua/bindings/layout.cpp
index 6225bce9f3..a7c32921c6 100644
--- a/src/plugins/lua/bindings/layout.cpp
+++ b/src/plugins/lua/bindings/layout.cpp
@@ -13,129 +13,368 @@ using namespace Utils;
namespace Lua::Internal {
-static void processChildren(LayoutItem *item, const sol::table &children)
+template<class T>
+static void processChildren(T *item, const sol::table &children)
{
for (size_t i = 1; i <= children.size(); ++i) {
- const sol::object v = children[i];
-
- if (v.is<LayoutItem *>()) {
- item->addItem(*v.as<LayoutItem *>());
- } else if (v.is<BaseAspect>()) {
- v.as<BaseAspect *>()->addToLayout(*item);
- } else if (v.is<QString>()) {
- item->addItem(v.as<QString>());
- } else if (v.is<sol::function>()) {
- const sol::function f = v.as<sol::function>();
- auto res = LuaEngine::safe_call<LayoutItem *>(f);
+ const auto &child = children[i];
+ if (child.is<Layout *>()) {
+ if (Layout *layout = child.get<Layout *>())
+ item->addItem(*layout);
+ else
+ item->addItem("ERROR");
+ } else if (child.is<Widget *>()) {
+ if (Widget *widget = child.get<Widget *>())
+ item->addItem(*widget);
+ else
+ item->addItem("ERROR");
+ } else if (child.is<BaseAspect>()) {
+ child.get<BaseAspect *>()->addToLayout(*item);
+ } else if (child.is<QString>()) {
+ item->addItem(child.get<QString>());
+ } else if (child.is<sol::function>()) {
+ const sol::function f = child.get<sol::function>();
+ auto res = LuaEngine::void_safe_call(f, item);
QTC_ASSERT_EXPECTED(res, continue);
- item->addItem(**res);
+ } else if (child.is<Span>()) {
+ const Span &span = child.get<Span>();
+ item->addItem(span);
+ } else if (child.is<Space>()) {
+ const Space &space = child.get<Space>();
+ item->addItem(space);
+ } else if (child.is<Stretch>()) {
+ const Stretch &stretch = child.get<Stretch>();
+ item->addItem(stretch);
} else {
- qWarning() << "Incompatible object added to layout item: " << (int) v.get_type()
+ qWarning() << "Incompatible object added to layout item: " << (int) child.get_type()
<< " (expected LayoutItem, Aspect or function returning LayoutItem)";
}
}
}
-template<class T, typename... Args>
-static std::unique_ptr<T> construct(Args &&...args, const sol::table &children)
+template<class T>
+static std::unique_ptr<T> construct(const sol::table &children)
{
- std::unique_ptr<T> item(new T(std::forward<Args>(args)..., {}));
-
+ std::unique_ptr<T> item(new T({}));
processChildren(item.get(), children);
+ return item;
+}
+
+template<class T>
+void constructWidget(std::unique_ptr<T> &widget, const sol::table &children)
+{
+ widget->setWindowTitle(children.get_or<QString>("windowTitle", ""));
+ widget->setToolTip(children.get_or<QString>("toolTip", ""));
+
+ for (size_t i = 1; i <= children.size(); ++i) {
+ const auto &child = children[i];
+ if (child.is<Layout>())
+ widget->setLayout(*child.get<Layout *>());
+ }
+}
+
+#define HAS_MEM_FUNC(func, name) \
+ template<typename T, typename Sign> \
+ struct name \
+ { \
+ typedef char yes[1]; \
+ typedef char no[2]; \
+ template<typename U, U> \
+ struct type_check; \
+ template<typename _1> \
+ static yes &chk(type_check<Sign, &_1::func> *); \
+ template<typename> \
+ static no &chk(...); \
+ static bool const value = sizeof(chk<T>(0)) == sizeof(yes); \
+ }
+
+HAS_MEM_FUNC(onTextChanged, hasOnTextChanged);
+HAS_MEM_FUNC(onClicked, hasOnClicked);
+HAS_MEM_FUNC(setText, hasSetText);
+HAS_MEM_FUNC(setTitle, hasSetTitle);
+HAS_MEM_FUNC(setValue, hasSetValue);
+
+template<class T>
+void setProperties(std::unique_ptr<T> &item, const sol::table &children, QObject *guard)
+{
+ if constexpr (hasOnTextChanged<T, void (T::*)(const QString &)>::value) {
+ sol::optional<sol::protected_function> onTextChanged
+ = children.get<sol::optional<sol::protected_function>>("onTextChanged");
+ if (onTextChanged) {
+ item->onTextChanged(
+ [f = *onTextChanged](const QString &text) {
+ auto res = LuaEngine::void_safe_call(f, text);
+ QTC_CHECK_EXPECTED(res);
+ },
+ guard);
+ }
+ }
+ if constexpr (hasOnClicked<T, void (T::*)(const std::function<void()> &, QObject *guard)>::value) {
+ sol::optional<sol::protected_function> onClicked
+ = children.get<sol::optional<sol::protected_function>>("onClicked");
+ if (onClicked) {
+ item->onClicked(
+ [f = *onClicked]() {
+ auto res = LuaEngine::void_safe_call(f);
+ QTC_CHECK_EXPECTED(res);
+ },
+ guard);
+ }
+ }
+ if constexpr (hasSetText<T, void (T::*)(const QString &)>::value) {
+ item->setText(children.get_or<QString>("text", ""));
+ }
+ if constexpr (hasSetTitle<T, void (T::*)(const QString &)>::value) {
+ item->setTitle(children.get_or<QString>("title", ""));
+ }
+ if constexpr (hasSetValue<T, void (T::*)(int)>::value) {
+ sol::optional<int> value = children.get<sol::optional<int>>("value");
+ if (value)
+ item->setValue(*value);
+ }
+}
+
+template<class T>
+std::unique_ptr<T> constructWidgetType(const sol::table &children, QObject *guard)
+{
+ std::unique_ptr<T> item(new T({}));
+ constructWidget(item, children);
+ setProperties(item, children, guard);
+ return item;
+}
+
+std::unique_ptr<Tab> constructTabFromTable(const sol::table &children)
+{
+ if (children.size() != 2)
+ throw sol::error("Tab must have exactly two children");
+
+ auto tabName = children[1];
+ if (tabName.get_type() != sol::type::string)
+ throw sol::error("Tab name (first argument) must be a string");
+
+ const auto &layout = children[2];
+ if (!layout.is<Layout *>())
+ throw sol::error("Tab child (second argument) must be a Layout");
+
+ std::unique_ptr<Tab> item = std::make_unique<Tab>(tabName, *layout.get<Layout *>());
+ return item;
+}
+
+std::unique_ptr<Tab> constructTab(const QString &tabName, const Layout &layout)
+{
+ std::unique_ptr<Tab> item = std::make_unique<Tab>(tabName, layout);
+ return item;
+}
+
+std::unique_ptr<Span> constructSpanFromTable(const sol::table &children)
+{
+ if (children.size() != 2)
+ throw sol::error("Span must have exactly two children");
+
+ auto spanSize = children[1];
+ if (spanSize.get_type() != sol::type::number)
+ throw sol::error("Span size (first argument) must be a number");
+
+ const auto &layout = children[2];
+ if (!layout.is<Layout *>())
+ throw sol::error("Span child (second argument) must be a Layout");
+
+ std::unique_ptr<Span> item = std::make_unique<Span>(spanSize, *layout.get<Layout *>());
+ return item;
+}
+
+std::unique_ptr<Span> constructSpan(int n, const Layout &layout)
+{
+ std::unique_ptr<Span> item = std::make_unique<Span>(n, layout);
+ return item;
+}
+
+std::unique_ptr<TabWidget> constructTabWidget(const sol::table &children, QObject *guard)
+{
+ std::unique_ptr<TabWidget> item(new TabWidget({}));
+ setProperties(item, children, guard);
+ for (size_t i = 1; i <= children.size(); ++i) {
+ const auto &child = children[i];
+ if (child.is<Tab *>())
+ addToTabWidget(item.get(), *child.get<Tab *>());
+ }
+ return item;
+}
+std::unique_ptr<Splitter> constructSplitter(const sol::table &children)
+{
+ std::unique_ptr<Splitter> item(new Splitter({}));
+ constructWidget(item, children);
+
+ for (size_t i = 1; i <= children.size(); ++i) {
+ const auto &child = children[i];
+ if (child.is<Layout *>()) {
+ addToSplitter(item.get(), *child.get<Layout *>());
+ } else if (child.is<Widget *>()) {
+ addToSplitter(item.get(), *child.get<Widget *>());
+ } else {
+ qWarning() << "Incompatible object added to Splitter: " << (int) child.get_type()
+ << " (expected Layout or Widget)";
+ }
+ }
return item;
}
void addLayoutModule()
{
LuaEngine::registerProvider("Layout", [](sol::state_view l) -> sol::object {
+ const ScriptPluginSpec *pluginSpec = l.get<ScriptPluginSpec *>("PluginSpec");
+ QObject *guard = pluginSpec->connectionGuard.get();
+
sol::table layout = l.create_table();
- layout.new_usertype<LayoutItem>("LayoutItem", "attachTo", &LayoutItem::attachTo);
-
- layout["Span"] = [](int span, LayoutItem *item) {
- return createItem(item, Span(span, *item));
- };
- layout["Space"] = [](int space) { return createItem(nullptr, Space(space)); };
- layout["Stretch"] = [](int stretch) { return createItem(nullptr, Stretch(stretch)); };
-
- layout.new_usertype<Column>("Column",
- sol::call_constructor,
- sol::factories(&construct<Column>),
- sol::base_classes,
- sol::bases<LayoutItem>());
- layout.new_usertype<Row>("Row",
- sol::call_constructor,
- sol::factories(&construct<Row>),
- sol::base_classes,
- sol::bases<LayoutItem>());
- layout.new_usertype<Flow>("Flow",
- sol::call_constructor,
- sol::factories(&construct<Flow>),
- sol::base_classes,
- sol::bases<LayoutItem>());
- layout.new_usertype<Grid>("Grid",
- sol::call_constructor,
- sol::factories(&construct<Grid>),
- sol::base_classes,
- sol::bases<LayoutItem>());
- layout.new_usertype<Form>("Form",
- sol::call_constructor,
- sol::factories(&construct<Form>),
- sol::base_classes,
- sol::bases<LayoutItem>());
- layout.new_usertype<Widget>("Widget",
- sol::call_constructor,
- sol::factories(&construct<Widget>),
- sol::base_classes,
- sol::bases<LayoutItem>());
- layout.new_usertype<Stack>("Stack",
- sol::call_constructor,
- sol::factories(&construct<Stack>),
- sol::base_classes,
- sol::bases<LayoutItem>());
+ layout.new_usertype<Span>(
+ "Span", sol::call_constructor, sol::factories(&constructSpan, &constructSpanFromTable));
+
+ layout.new_usertype<Space>("Space", sol::call_constructor, sol::constructors<Space(int)>());
+
+ layout.new_usertype<Stretch>(
+ "Stretch", sol::call_constructor, sol::constructors<Stretch(int)>());
+
+ // Layouts
+ layout.new_usertype<Layout>(
+ "Layout",
+ sol::call_constructor,
+ sol::factories(&construct<Layout>),
+ "show",
+ &Layout::show,
+ sol::base_classes,
+ sol::bases<Object, Thing>());
+
+ layout.new_usertype<Form>(
+ "Form",
+ sol::call_constructor,
+ sol::factories(&construct<Form>),
+ sol::base_classes,
+ sol::bases<Layout, Object, Thing>());
+
+ layout.new_usertype<Column>(
+ "Column",
+ sol::call_constructor,
+ sol::factories(&construct<Column>),
+ sol::base_classes,
+ sol::bases<Layout, Object, Thing>());
+
+ layout.new_usertype<Row>(
+ "Row",
+ sol::call_constructor,
+ sol::factories(&construct<Row>),
+ sol::base_classes,
+ sol::bases<Layout, Object, Thing>());
+ layout.new_usertype<Flow>(
+ "Flow",
+ sol::call_constructor,
+ sol::factories(&construct<Flow>),
+ sol::base_classes,
+ sol::bases<Layout, Object, Thing>());
+ layout.new_usertype<Grid>(
+ "Grid",
+ sol::call_constructor,
+ sol::factories(&construct<Grid>),
+ sol::base_classes,
+ sol::bases<Layout, Object, Thing>());
+
+ // Widgets
+ layout.new_usertype<PushButton>(
+ "PushButton",
+ sol::call_constructor,
+ sol::factories([guard](const sol::table &children) {
+ return constructWidgetType<PushButton>(children, guard);
+ }),
+ sol::base_classes,
+ sol::bases<Widget, Object, Thing>());
+
+ layout.new_usertype<Label>(
+ "Label",
+ sol::call_constructor,
+ sol::factories([guard](const sol::table &children) {
+ return constructWidgetType<Label>(children, guard);
+ }),
+ sol::base_classes,
+ sol::bases<Widget, Object, Thing>());
+
+ layout.new_usertype<Widget>(
+ "Widget",
+ sol::call_constructor,
+ sol::factories([guard](const sol::table &children) {
+ return constructWidgetType<Widget>(children, guard);
+ }),
+ "show",
+ &Widget::show,
+ "resize",
+ &Widget::resize,
+ sol::base_classes,
+ sol::bases<Object, Thing>());
+
+ layout.new_usertype<Stack>(
+ "Stack",
+ sol::call_constructor,
+ sol::factories([guard](const sol::table &children) {
+ return constructWidgetType<Stack>(children, guard);
+ }),
+ sol::base_classes,
+ sol::bases<Widget, Object, Thing>());
+
layout.new_usertype<Tab>(
"Tab",
sol::call_constructor,
- sol::factories(&construct<Tab, QString>),
- sol::base_classes,
- sol::bases<LayoutItem>());
- layout.new_usertype<TextEdit>("TextEdit",
- sol::call_constructor,
- sol::factories(&construct<TextEdit>),
- sol::base_classes,
- sol::bases<LayoutItem>());
- layout.new_usertype<PushButton>("PushButton",
- sol::call_constructor,
- sol::factories(&construct<PushButton>),
- sol::base_classes,
- sol::bases<LayoutItem>());
- layout.new_usertype<SpinBox>("SpinBox",
- sol::call_constructor,
- sol::factories(&construct<SpinBox>),
- sol::base_classes,
- sol::bases<LayoutItem>());
- layout.new_usertype<Splitter>("Splitter",
- sol::call_constructor,
- sol::factories(&construct<Splitter>),
- sol::base_classes,
- sol::bases<LayoutItem>());
- layout.new_usertype<ToolBar>("ToolBar",
- sol::call_constructor,
- sol::factories(&construct<ToolBar>),
- sol::base_classes,
- sol::bases<LayoutItem>());
- layout.new_usertype<TabWidget>("TabWidget",
- sol::call_constructor,
- sol::factories(&construct<TabWidget>),
- sol::base_classes,
- sol::bases<LayoutItem>());
-
- layout.new_usertype<Group>("Group",
- sol::call_constructor,
- sol::factories(&construct<Group>),
- sol::base_classes,
- sol::bases<LayoutItem>());
+ sol::factories(&constructTab, &constructTabFromTable),
+ sol::base_classes,
+ sol::bases<Widget, Object, Thing>());
+
+ layout.new_usertype<TextEdit>(
+ "TextEdit",
+ sol::call_constructor,
+ sol::factories([guard](const sol::table &children) {
+ return constructWidgetType<TextEdit>(children, guard);
+ }),
+ sol::base_classes,
+ sol::bases<Widget, Object, Thing>());
+
+ layout.new_usertype<SpinBox>(
+ "SpinBox",
+ sol::call_constructor,
+ sol::factories([guard](const sol::table &children) {
+ return constructWidgetType<SpinBox>(children, guard);
+ }),
+ sol::base_classes,
+ sol::bases<Widget, Object, Thing>());
+ layout.new_usertype<Splitter>(
+ "Splitter",
+ sol::call_constructor,
+ sol::factories(&constructSplitter),
+ sol::base_classes,
+ sol::bases<Widget, Object, Thing>());
+ layout.new_usertype<ToolBar>(
+ "ToolBar",
+ sol::call_constructor,
+ sol::factories([guard](const sol::table &children) {
+ return constructWidgetType<ToolBar>(children, guard);
+ }),
+ sol::base_classes,
+ sol::bases<Widget, Object, Thing>());
+ layout.new_usertype<TabWidget>(
+ "TabWidget",
+ sol::call_constructor,
+ sol::factories([guard](const sol::table &children) {
+ return constructTabWidget(children, guard);
+ }),
+ sol::base_classes,
+ sol::bases<Widget, Object, Thing>());
+
+ layout.new_usertype<Group>(
+ "Group",
+ sol::call_constructor,
+ sol::factories([guard](const sol::table &children) {
+ return constructWidgetType<Group>(children, guard);
+ }),
+ sol::base_classes,
+ sol::bases<Widget, Object, Thing>());
layout["br"] = &br;
layout["st"] = &st;
@@ -143,32 +382,8 @@ void addLayoutModule()
layout["hr"] = &hr;
layout["noMargin"] = &noMargin;
layout["normalMargin"] = &normalMargin;
- layout["customMargin"] = [](int left, int top, int right, int bottom) {
- return customMargin(QMargins(left, top, right, bottom));
- };
layout["withFormAlignment"] = &withFormAlignment;
- layout["title"] = &title;
- layout["text"] = &text;
- layout["tooltip"] = &tooltip;
- layout["resize"] = &resize;
- layout["columnStretch"] = &columnStretch;
layout["spacing"] = &spacing;
- layout["windowTitle"] = &windowTitle;
- layout["fieldGrowthPolicy"] = &fieldGrowthPolicy;
- layout["id"] = &id;
- layout["setText"] = &setText;
- layout["onClicked"] = [](const sol::function &f) {
- return onClicked([f]() {
- auto res = LuaEngine::void_safe_call(f);
- QTC_CHECK_EXPECTED(res);
- });
- };
- layout["onTextChanged"] = [](const sol::function &f) {
- return onTextChanged([f](const QString &text) {
- auto res = LuaEngine::void_safe_call(f, text);
- QTC_CHECK_EXPECTED(res);
- });
- };
return layout;
});
diff --git a/src/plugins/lua/bindings/qtcprocess.cpp b/src/plugins/lua/bindings/qtcprocess.cpp
index 93d3af2e0a..c1b4a4eccd 100644
--- a/src/plugins/lua/bindings/qtcprocess.cpp
+++ b/src/plugins/lua/bindings/qtcprocess.cpp
@@ -12,30 +12,31 @@ namespace Lua::Internal {
void addProcessModule()
{
- LuaEngine::registerProvider(
- "Process", [](sol::state_view lua) -> sol::object {
- sol::table async = lua.script("return require('async')", "_process_").get<sol::table>();
- sol::function wrap = async["wrap"];
+ LuaEngine::registerProvider("Process", [](sol::state_view lua) -> sol::object {
+ const ScriptPluginSpec *pluginSpec = lua.get<ScriptPluginSpec *>("PluginSpec");
- sol::table process = lua.create_table();
+ sol::table async = lua.script("return require('async')", "_process_").get<sol::table>();
+ sol::function wrap = async["wrap"];
- process["runInTerminal_cb"] = [](const QString &cmdline, const sol::function &cb) {
+ sol::table process = lua.create_table();
+
+ process["runInTerminal_cb"] =
+ [guard
+ = pluginSpec->connectionGuard.get()](const QString &cmdline, const sol::function &cb) {
Process *p = new Process;
p->setTerminalMode(TerminalMode::Run);
p->setCommand(CommandLine::fromUserInput((cmdline)));
p->setEnvironment(Environment::systemEnvironment());
- QObject::connect(p, &Process::done, &LuaEngine::instance(), [p, cb]() {
- cb(p->exitCode());
- });
+ QObject::connect(p, &Process::done, guard, [p, cb]() { cb(p->exitCode()); });
p->start();
};
- process["runInTerminal"] = wrap(process["runInTerminal_cb"]);
+ process["runInTerminal"] = wrap(process["runInTerminal_cb"]);
- return process;
- });
+ return process;
+ });
}
} // namespace Lua::Internal
diff --git a/src/plugins/lua/bindings/settings.cpp b/src/plugins/lua/bindings/settings.cpp
index b1c388605f..a023cd5a55 100644
--- a/src/plugins/lua/bindings/settings.cpp
+++ b/src/plugins/lua/bindings/settings.cpp
@@ -62,8 +62,8 @@ std::unique_ptr<LuaAspectContainer> aspectContainerCreate(const sol::table &opti
} else if (key == "layouter") {
if (v.is<sol::function>())
container->setLayouter(
- [func = v.as<sol::function>()]() -> Layouting::LayoutItem {
- auto res = Lua::LuaEngine::safe_call<Layouting::LayoutItem>(func);
+ [func = v.as<sol::function>()]() -> Layouting::Layout {
+ auto res = Lua::LuaEngine::safe_call<Layouting::Layout>(func);
QTC_ASSERT_EXPECTED(res, return {});
return *res;
});
diff --git a/src/plugins/lua/bindings/utils.cpp b/src/plugins/lua/bindings/utils.cpp
index 7893a1e5e0..2ed5a4601e 100644
--- a/src/plugins/lua/bindings/utils.cpp
+++ b/src/plugins/lua/bindings/utils.cpp
@@ -19,16 +19,19 @@ void addUtilsModule()
LuaEngine::registerProvider(
"Utils",
[futureSync = Utils::FutureSynchronizer()](sol::state_view lua) mutable -> sol::object {
+ const ScriptPluginSpec *pluginSpec = lua.get<ScriptPluginSpec *>("PluginSpec");
+
auto async = lua.script("return require('async')", "_utils_").get<sol::table>();
sol::table utils = lua.create_table();
- utils.set_function("waitms_cb", [](int ms, const sol::function &cb) {
- QTimer::singleShot(ms, &LuaEngine::instance(), [cb]() { cb(); });
+ utils.set_function("waitms_cb", [guard = pluginSpec->connectionGuard.get()](int ms, const sol::function &cb) {
+ QTimer::singleShot(ms, guard, [cb]() { cb(); });
});
auto dirEntries_cb =
- [&futureSync](const FilePath &p, const sol::table &options, const sol::function &cb) {
+ [&futureSync, guard = pluginSpec->connectionGuard.get()](
+ const FilePath &p, const sol::table &options, const sol::function &cb) {
const QStringList nameFilters = options.get_or<QStringList>("nameFilters", {});
QDir::Filters fileFilters
= (QDir::Filters) options.get_or<int>("fileFilters", QDir::NoFilter);
@@ -53,22 +56,22 @@ void addUtilsModule()
futureSync.addFuture<FilePath>(future);
- Utils::onFinished<FilePath>(
- future, &LuaEngine::instance(), [cb](const QFuture<FilePath> &future) {
- cb(future.results());
- });
+ Utils::onFinished<FilePath>(future, guard, [cb](const QFuture<FilePath> &future) {
+ cb(future.results());
+ });
};
- auto searchInPath_cb = [&futureSync](const FilePath &p, const sol::function &cb) {
+ auto searchInPath_cb = [&futureSync,
+ guard = pluginSpec->connectionGuard
+ .get()](const FilePath &p, const sol::function &cb) {
QFuture<FilePath> future = Utils::asyncRun(
[p](QPromise<FilePath> &promise) { promise.addResult(p.searchInPath()); });
futureSync.addFuture<FilePath>(future);
- Utils::onFinished<FilePath>(
- future, &LuaEngine::instance(), [cb](const QFuture<FilePath> &future) {
- cb(future.result());
- });
+ Utils::onFinished<FilePath>(future, guard, [cb](const QFuture<FilePath> &future) {
+ cb(future.result());
+ });
};
utils.set_function("__dirEntries_cb__", dirEntries_cb);
@@ -92,6 +95,24 @@ void addUtilsModule()
else
return "unknown";
}());
+ hostOsInfoType["architecture"] = sol::var([]() {
+ switch (HostOsInfo::hostArchitecture()) {
+ case OsArchUnknown:
+ return "unknown";
+ case OsArchX86:
+ return "x86";
+ case OsArchAMD64:
+ return "x86_64";
+ case OsArchItanium:
+ return "itanium";
+ case OsArchArm:
+ return "arm";
+ case OsArchArm64:
+ return "arm64";
+ default:
+ return "unknown";
+ }
+ }());
sol::usertype<FilePath> filePathType = utils.new_usertype<FilePath>(
"FilePath",
diff --git a/src/plugins/lua/lua.qbs b/src/plugins/lua/lua.qbs
index e7f1507ba7..9e19377a7c 100644
--- a/src/plugins/lua/lua.qbs
+++ b/src/plugins/lua/lua.qbs
@@ -34,6 +34,7 @@ QtcPlugin {
"fetch.cpp",
"hook.cpp",
"inheritance.h",
+ "install.cpp",
"layout.cpp",
"messagemanager.cpp",
"qtcprocess.cpp",
@@ -56,6 +57,7 @@ QtcPlugin {
"async.lua",
"core.lua",
"fetch.lua",
+ "install.lua",
"layout.lua",
"lsp.lua",
"messagemanager.lua",
@@ -68,6 +70,8 @@ QtcPlugin {
"widgets.lua",
"wizard.lua",
]
+ qbs.install: true
+ qbs.installDir: qtc.ide_data_path + "/lua/meta/"
}
Group {
diff --git a/src/plugins/lua/luaengine.cpp b/src/plugins/lua/luaengine.cpp
index a49af1a388..505313da86 100644
--- a/src/plugins/lua/luaengine.cpp
+++ b/src/plugins/lua/luaengine.cpp
@@ -5,15 +5,41 @@
#include "luapluginspec.h"
+#include <coreplugin/icore.h>
+#include <coreplugin/messagemanager.h>
+
#include <utils/algorithm.h>
+#include <utils/lua.h>
+#include <utils/stringutils.h>
+#include <utils/theme/theme.h>
#include <QJsonArray>
#include <QJsonObject>
+#include <QStandardPaths>
using namespace Utils;
namespace Lua {
+class LuaInterfaceImpl : public Utils::LuaInterface
+{
+ LuaEngine *m_engine;
+
+public:
+ LuaInterfaceImpl(LuaEngine *engine)
+ : m_engine(engine)
+ {
+ Utils::setLuaInterface(this);
+ }
+ ~LuaInterfaceImpl() override { Utils::setLuaInterface(nullptr); }
+
+ expected_str<std::unique_ptr<LuaState>> runScript(
+ const QString &script, const QString &name) override
+ {
+ return m_engine->runScript(script, name);
+ }
+};
+
class LuaEnginePrivate
{
public:
@@ -23,6 +49,8 @@ public:
QList<std::function<void(sol::state_view)>> m_autoProviders;
QMap<QString, std::function<void(sol::function)>> m_hooks;
+
+ std::unique_ptr<LuaInterfaceImpl> m_luaInterface;
};
static LuaEngine *s_instance = nullptr;
@@ -37,6 +65,7 @@ LuaEngine::LuaEngine()
: d(new LuaEnginePrivate())
{
s_instance = this;
+ d->m_luaInterface.reset(new LuaInterfaceImpl(this));
}
LuaEngine::~LuaEngine()
@@ -44,6 +73,70 @@ LuaEngine::~LuaEngine()
s_instance = nullptr;
}
+class LuaStateImpl : public Utils::LuaState
+{
+public:
+ sol::state lua;
+};
+
+// Runs the gives script in a new Lua state. The returned Object manages the lifetime of the state.
+std::unique_ptr<Utils::LuaState> LuaEngine::runScript(const QString &script, const QString &name)
+{
+ std::unique_ptr<LuaStateImpl> opaque = std::make_unique<LuaStateImpl>();
+
+ opaque->lua.open_libraries(
+ sol::lib::base,
+ sol::lib::bit32,
+ sol::lib::coroutine,
+ sol::lib::debug,
+ sol::lib::io,
+ sol::lib::math,
+ sol::lib::os,
+ sol::lib::package,
+ sol::lib::string,
+ sol::lib::table,
+ sol::lib::utf8);
+
+ opaque->lua["print"] = [prefix = name, printToOutputPane = true](sol::variadic_args va) {
+ const QString msg = variadicToStringList(va).join("\t");
+
+ qDebug().noquote() << "[" << prefix << "]" << msg;
+ if (printToOutputPane) {
+ static const QString p
+ = ansiColoredText("[" + prefix + "]", creatorColor(Theme::Token_Text_Muted));
+ Core::MessageManager::writeSilently(QString("%1 %2").arg(p, msg));
+ }
+ };
+
+ opaque->lua.new_usertype<ScriptPluginSpec>(
+ "PluginSpec", sol::no_constructor, "name", sol::property([](ScriptPluginSpec &self) {
+ return self.name;
+ }));
+
+ opaque->lua["PluginSpec"] = ScriptPluginSpec{name, {}, std::make_unique<QObject>()};
+
+ for (const auto &[name, func] : d->m_providers.asKeyValueRange()) {
+ opaque->lua["package"]["preload"][name.toStdString()] =
+ [func = func](const sol::this_state &s) { return func(s); };
+ }
+
+ for (const auto &func : d->m_autoProviders)
+ func(opaque->lua);
+
+ auto result
+ = opaque->lua
+ .safe_script(script.toStdString(), sol::script_pass_on_error, name.toStdString());
+
+ if (!result.valid()) {
+ sol::error err = result;
+ qWarning() << "Failed to run script" << name << ":" << QString::fromUtf8(err.what());
+ Core::MessageManager::writeFlashing(
+ tr("Failed to run script %1: %2").arg(name, QString::fromUtf8(err.what())));
+ }
+
+ return opaque;
+}
+
void LuaEngine::registerProvider(const QString &packageName, const PackageProvider &provider)
{
QTC_ASSERT(!instance().d->m_providers.contains(packageName), return);
@@ -95,10 +188,6 @@ expected_str<LuaPluginSpec *> LuaEngine::loadPlugin(const Utils::FilePath &path)
sol::state lua;
- lua["print"] = [prefix = path.fileName()](sol::variadic_args va) {
- qDebug().noquote() << "[" << prefix << "]" << variadicToStringList(va).join("\t");
- };
-
auto result = lua.safe_script(
std::string_view(contents->data(), contents->size()),
sol::script_pass_on_error,
@@ -135,9 +224,33 @@ expected_str<void> LuaEngine::prepareSetup(
sol::lib::table,
sol::lib::utf8);
+ const bool printToOutputPane = pluginSpec.printToOutputPane();
+ const QString prefix = pluginSpec.filePath().fileName();
+ lua["print"] = [prefix, printToOutputPane](sol::variadic_args va) {
+ const QString msg = variadicToStringList(va).join("\t");
+
+ qDebug().noquote() << "[" << prefix << "]" << msg;
+ if (printToOutputPane) {
+ static const QString p
+ = ansiColoredText("[" + prefix + "]", creatorColor(Theme::Token_Text_Muted));
+ Core::MessageManager::writeSilently(QString("%1 %2").arg(p, msg));
+ }
+ };
+
const QString searchPath = (pluginSpec.location() / "?.lua").toUserOutput();
lua["package"]["path"] = searchPath.toStdString();
+ const FilePath appDataPath = Core::ICore::userResourcePath() / "plugin-data" / "lua"
+ / pluginSpec.location().fileName();
+
+ lua.new_usertype<ScriptPluginSpec>(
+ "PluginSpec", sol::no_constructor, "name", sol::property([](ScriptPluginSpec &self) {
+ return self.name;
+ }));
+
+ lua["PluginSpec"]
+ = ScriptPluginSpec{pluginSpec.name(), appDataPath, std::make_unique<QObject>()};
+
// TODO: only register what the plugin requested
for (const auto &[name, func] : d->m_providers.asKeyValueRange()) {
lua["package"]["preload"][name.toStdString()] = [func = func](const sol::this_state &s) {
diff --git a/src/plugins/lua/luaengine.h b/src/plugins/lua/luaengine.h
index a683f43f2d..688c728808 100644
--- a/src/plugins/lua/luaengine.h
+++ b/src/plugins/lua/luaengine.h
@@ -10,6 +10,7 @@
#include <utils/expected.h>
#include <utils/filepath.h>
+#include <utils/lua.h>
#include <sol/sol.hpp>
@@ -33,6 +34,13 @@ struct CoroutineState
bool isMainThread;
};
+struct ScriptPluginSpec
+{
+ QString name;
+ Utils::FilePath appDataPath;
+ std::unique_ptr<QObject> connectionGuard;
+};
+
class LUA_EXPORT LuaEngine final : public QObject
{
friend class Internal::LuaPlugin;
@@ -101,6 +109,9 @@ public:
return {};
}
+ // Runs the given script in a new Lua state. The returned Object manages the lifetime of the state.
+ std::unique_ptr<Utils::LuaState> runScript(const QString &script, const QString &name);
+
protected:
Utils::expected_str<void> connectHooks(
sol::state_view lua, const sol::table &table, const QString &path);
diff --git a/src/plugins/lua/luaplugin.cpp b/src/plugins/lua/luaplugin.cpp
index 047847459c..05c4985605 100644
--- a/src/plugins/lua/luaplugin.cpp
+++ b/src/plugins/lua/luaplugin.cpp
@@ -4,6 +4,8 @@
#include "luaengine.h"
#include "luapluginspec.h"
+#include <coreplugin/icore.h>
+#include <coreplugin/jsexpander.h>
#include <coreplugin/messagemanager.h>
#include <extensionsystem/iplugin.h>
@@ -30,6 +32,22 @@ void addLayoutModule();
void addQtModule();
void addCoreModule();
void addHookModule();
+void addInstallModule();
+
+class LuaJsExtension : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit LuaJsExtension(QObject *parent = nullptr)
+ : QObject(parent)
+ {}
+
+ Q_INVOKABLE QString metaFolder() const
+ {
+ return Core::ICore::resourcePath("lua/meta").toFSPathString();
+ }
+};
class LuaPlugin : public IPlugin
{
@@ -58,6 +76,9 @@ public:
addQtModule();
addCoreModule();
addHookModule();
+ addInstallModule();
+
+ Core::JsExpander::registerGlobalObject("Lua", [] { return new LuaJsExtension(); });
}
bool delayedInitialize() final
diff --git a/src/plugins/lua/luapluginspec.cpp b/src/plugins/lua/luapluginspec.cpp
index 19d52dbf37..4c4ac66d4e 100644
--- a/src/plugins/lua/luapluginspec.cpp
+++ b/src/plugins/lua/luapluginspec.cpp
@@ -138,4 +138,9 @@ ExtensionSystem::IPlugin::ShutdownFlag LuaPluginSpec::stop()
void LuaPluginSpec::kill() {}
+bool LuaPluginSpec::printToOutputPane() const
+{
+ return d->pluginTable.get_or("printToOutputPane", false);
+}
+
} // namespace Lua
diff --git a/src/plugins/lua/luapluginspec.h b/src/plugins/lua/luapluginspec.h
index 208aa8f196..2d72bb5390 100644
--- a/src/plugins/lua/luapluginspec.h
+++ b/src/plugins/lua/luapluginspec.h
@@ -52,6 +52,9 @@ public:
bool delayedInitialize() override;
ExtensionSystem::IPlugin::ShutdownFlag stop() override;
void kill() override;
+
+public:
+ bool printToOutputPane() const;
};
} // namespace Lua
diff --git a/src/plugins/lua/meta/install.lua b/src/plugins/lua/meta/install.lua
new file mode 100644
index 0000000000..1775206374
--- /dev/null
+++ b/src/plugins/lua/meta/install.lua
@@ -0,0 +1,29 @@
+---@meta Install
+
+local Install = {}
+
+---@class PackageInfo
+---@field name string The name of the package
+---@field version string The version of the package
+---@field path FilePath The path to the package
+
+local PackageInfo = {}
+---@class InstallOptions
+---@field name string The name of the package to install
+---@field url string The url to fetch the package from
+---@field version string The version of the package to install
+local InstallOptions = {}
+
+---Install something
+---@param msg string The message to display to the user asking for permission to install
+---@param options InstallOptions|[InstallOptions] The options to install
+---@return boolean Result Whether the installation was successful
+---@return string Error The error message if the installation failed.
+function Install.install(msg, options) end
+
+---Get the package info
+---@param name any The name of the package
+---@return PackageInfo
+function Install.packageInfo(name) end
+
+return Install
diff --git a/src/plugins/lua/meta/layout.lua b/src/plugins/lua/meta/layout.lua
index 272171beff..90c4432240 100644
--- a/src/plugins/lua/meta/layout.lua
+++ b/src/plugins/lua/meta/layout.lua
@@ -2,133 +2,146 @@
local layout = {}
----The base class of all layout items
----@class LayoutItem
-layout.LayoutItem = {
- ---Attaches the layout to the specified widget
- ---@param widget QWidget
- attachTo = function(widget) end
-}
+---The base class of all layout classes
+---@class Object
+layout.Layout = {}
+
+---The base class of all layout classes
+---@class Layout : Object
+layout.Layout = {}
+
+---The base class of all widget classes, an empty widget itself.
+---@class Widget : Object
+Widget = {}
+
+---@param children Layout
+---@return Widget
+function layout.Widget(children) end
---Column layout
----@class Column : LayoutItem
+---@class Column : Layout
local column = {}
----@param children LayoutItem|string|BaseAspect|function
+---@param children Layout|string|BaseAspect|function
---@return Column
function layout.Column(children) end
---A group box with a title
----@class Group : LayoutItem
+---@class Group : Widget
local group = {}
---@return Group
function layout.Group(children) end
---Row layout
----@class Row : LayoutItem
+---@class Row : Layout
local row = {}
----@param children LayoutItem|string|BaseAspect|function
+---@param children Layout|string|BaseAspect|function
---@return Row
function layout.Row(children) end
---Flow layout
----@class Flow : LayoutItem
+---@class Flow : Layout
local flow = {}
----@param children LayoutItem|string|BaseAspect|function
+---@param children Layout|string|BaseAspect|function
---@return Flow
function layout.Flow(children) end
---Grid layout
----@class Grid : LayoutItem
+---@class Grid : Layout
local grid = {}
----@param children LayoutItem|string|BaseAspect|function
+---@param children Layout|string|BaseAspect|function
---@return Grid
function layout.Grid(children) end
---Form layout
----@class Form : LayoutItem
+---@class Form : Layout
local form = {}
----@param children LayoutItem|string|BaseAspect|function
+---@param children Layout|string|BaseAspect|function
---@return Form
function layout.Form(children) end
----An empty widget
----@class Widget : LayoutItem
-local widget = {}
-
----@param children LayoutItem|string|BaseAspect|function
----@return Widget
-function layout.Widget(children) end
---A stack of multiple widgets
----@class Stack : LayoutItem
+---@class Stack : Widget
local stack = {}
----@param children LayoutItem|string|BaseAspect|function
+---@param children Layout|string|BaseAspect|function
---@return Stack
function layout.Stack(children) end
---A Tab widget
----@class Tab : LayoutItem
+---@class Tab : Widget
local tab = {}
----@param children LayoutItem|string|BaseAspect|function
+---@param children Layout|string|BaseAspect|function
---@return Tab
function layout.Tab(children) end
---A Multiline text edit
----@class TextEdit : LayoutItem
+---@class TextEdit : Widget
local textEdit = {}
----@param children LayoutItem|string|BaseAspect|function
+---@param children Layout|string|BaseAspect|function
---@return TextEdit
function layout.TextEdit(children) end
---A PushButton
----@class PushButton : LayoutItem
+---@class PushButton : Widget
local pushButton = {}
----@param children LayoutItem|string|BaseAspect|function
+---@param children Layout|string|BaseAspect|function
---@return PushButton
function layout.PushButton(children) end
+---A Label
+---@class Label : LayoutItem
+local label = {}
+
+---@param children LayoutItem|string|BaseAspect|function
+---@return Label
+function layout.Label(children) end
+
---A SpinBox
----@class SpinBox : LayoutItem
+---@class SpinBox : Widget
local spinBox = {}
----@param children LayoutItem|string|BaseAspect|function
+---@param children Layout|string|BaseAspect|function
---@return SpinBox
function layout.SpinBox(children) end
---A Splitter
----@class Splitter : LayoutItem
+---@class Splitter : Widget
local splitter = {}
----@param children LayoutItem|string|BaseAspect|function
+---@param children Layout|string|BaseAspect|function
---@return Splitter
function layout.Splitter(children) end
---A Toolbar
----@class ToolBar : LayoutItem
+---@class ToolBar : Widget
local toolBar = {}
----@param children LayoutItem|string|BaseAspect|function
+---@param children Layout|string|BaseAspect|function
---@return ToolBar
function layout.ToolBar(children) end
---A TabWidget
----@class TabWidget : LayoutItem
+---@class TabWidget : Widget
local tabWidget = {}
----@param children LayoutItem|string|BaseAspect|function
+---@param children Layout|string|BaseAspect|function
---@return TabWidget
function layout.TabWidget(children) end
+---@param name string
+---@param child Layout|string|BaseAspect|function
+---@return TabWidget
+function layout.TabWidget(name, child) end
---A "Line break" in the layout
function layout.br() end
@@ -147,33 +160,15 @@ function layout.noMargin() end
---Sets the margin of the layout to the default value
function layout.normalMargin() end
----Sets the margin of the layout to a custom value
-function layout.customMargin(left, top, right, bottom) end
-
---Sets the alignment of the layout to "Form"
function layout.withFormAlignment() end
----Sets the title of the parent object if possible
-function layout.title(text) end
-
----Sets the text of the parent object if possible
-function layout.text(text) end
-
----Sets the tooltip of the parent object if possible
-function layout.tooltip(text) end
-
---Sets the size of the parent object if possible
function layout.resize(width, height) end
----Sets the stretch of the column at `index`
-function layout.columnStretch(index, stretch) end
-
---Sets the spacing of the layout
function layout.spacing(spacing) end
----Sets the window title of the parent object if possible
-function layout.windowTitle(text) end
-
---Sets the field growth policy of the layout
function layout.fieldGrowthPolicy(policy) end
diff --git a/src/plugins/lua/meta/lsp.lua b/src/plugins/lua/meta/lsp.lua
index 65b1ef8361..1c404c167f 100644
--- a/src/plugins/lua/meta/lsp.lua
+++ b/src/plugins/lua/meta/lsp.lua
@@ -11,6 +11,7 @@ local lsp = {}
---@field startBehavior? "AlwaysOn"|"RequiresFile"|"RequiresProject"
---@field initializationOptions? table|string The initialization options to pass to the language server, either a json string, or a table
---@field settings? AspectContainer
+---@field onStartFailed? function This callback is called when client failed to start.
local ClientOptions = {}
---@class LanguageFilter
diff --git a/src/plugins/lua/meta/qtc.lua b/src/plugins/lua/meta/qtc.lua
index bfcfd457eb..cc7de85f05 100644
--- a/src/plugins/lua/meta/qtc.lua
+++ b/src/plugins/lua/meta/qtc.lua
@@ -23,6 +23,7 @@ Qtc = {}
---@field hooks? Hooks The hooks of the plugin.
---@field Mimetypes? string XML MIME-info for registering additional or adapting built-in MIME types.
---@field JsonWizardPaths? string[] A list of paths relative to the plugin location or paths to the Qt resource system that are searched for template-based wizards.
+---@field printToOutputPane? boolean Whether the `print(...)` function should print to the output pane or not. ( Default: false )
QtcPlugin = {}
---@class QtcPluginDependency
diff --git a/src/plugins/lua/meta/utils.lua b/src/plugins/lua/meta/utils.lua
index 2463c01fda..409946b711 100644
--- a/src/plugins/lua/meta/utils.lua
+++ b/src/plugins/lua/meta/utils.lua
@@ -81,4 +81,23 @@ function utils.FilePath:completeSuffix() end
---Returns whether the path is absolute
---@return boolean
function utils.FilePath:isAbsolutePath() end
+
+---@class HostOsInfo
+---@field os "mac"|"windows"|"linux" The current host operating system
+---@field architecture "unknown"|"x86"|"x86_64"|"itanium"|"arm"|"arm64" The current host architecture
+utils.HostOsInfo = {}
+
+---Returns whether the host operating system is windows
+---@return boolean
+function utils.HostOsInfo.isWindowsHost() end
+
+---Returns whether the host operating system is mac
+---@return boolean
+function utils.HostOsInfo.isMacHost() end
+
+---Returns whether the host operating system is linux
+---@return boolean
+function utils.HostOsInfo.isLinuxHost() end
+
+
return utils
diff --git a/src/plugins/lualsp/lualsp/init.lua b/src/plugins/lualsp/lualsp/init.lua
index 75162c95c0..187aadb50e 100644
--- a/src/plugins/lualsp/lualsp/init.lua
+++ b/src/plugins/lualsp/lualsp/init.lua
@@ -3,10 +3,10 @@
local LSP = require('LSP')
local mm = require('MessageManager')
local Utils = require('Utils')
-local Process = require('Process')
local S = require('Settings')
local Layout = require('Layout')
local a = require('async')
+local fetch = require('Fetch').fetch
Settings = {}
@@ -24,6 +24,78 @@ local function createCommand()
return cmd
end
+local function filter(tbl, callback)
+ for i = #tbl, 1, -1 do
+ if not callback(tbl[i]) then
+ table.remove(tbl, i)
+ end
+ end
+end
+
+local function installOrUpdateServer()
+ local data = a.wait(fetch({
+ url = "https://api.github.com/repos/LuaLS/lua-language-server/releases?per_page=1",
+ convertToTable = true,
+ headers = {
+ Accept = "application/vnd.github.v3+json",
+ ["X-GitHub-Api-Version"] = "2022-11-28"
+ }
+ }))
+
+ if type(data) == "table" and #data > 0 then
+ local r = data[1]
+ Install = require('Install')
+ local lspPkgInfo = Install.packageInfo("lua-language-server")
+ if not lspPkgInfo or lspPkgInfo.version ~= r.tag_name then
+ local osTr = { mac = "darwin", windows = "win32", linux = "linux" }
+ local archTr = { unknown = "", x86 = "ia32", x86_64 = "x64", itanium = "", arm = "", arm64 = "arm64" }
+ local os = osTr[Utils.HostOsInfo.os]
+ local arch = archTr[Utils.HostOsInfo.architecture]
+
+ local expectedFileName = "lua-language-server-" .. r.tag_name .. "-" .. os .. "-" .. arch
+
+ filter(r.assets, function(asset)
+ return string.find(asset.name, expectedFileName, 1, true) == 1
+ end)
+
+ if #r.assets == 0 then
+ print("No assets found for this platform")
+ return
+ end
+ local res, err = a.wait(Install.install(
+ "Do you want to install the lua-language-server?", {
+ name = "lua-language-server",
+ url = r.assets[1].browser_download_url,
+ version = r.tag_name
+ }))
+
+ if not res then
+ mm.writeFlashing("Failed to install lua-language-server: " .. err)
+ return
+ end
+
+ lspPkgInfo = Install.packageInfo("lua-language-server")
+ print("Installed:", lspPkgInfo.name, "version:", lspPkgInfo.version, "at", lspPkgInfo.path)
+ end
+
+ local binary = "bin/lua-language-server"
+ if Utils.HostOsInfo.isWindowsHost() then
+ binary = "bin/lua-language-server.exe"
+ end
+
+ Settings.binary.defaultPath = lspPkgInfo.path:resolvePath(binary)
+ Settings:apply()
+ return
+ end
+
+ if type(data) == "string" then
+ print("Failed to fetch:", data)
+ else
+ print("No lua-language-server release found.")
+ end
+end
+IsTryingToInstall = false
+
local function setupClient()
Client = LSP.Client.create({
name = 'Lua Language Server',
@@ -35,6 +107,17 @@ local function setupClient()
},
settings = Settings,
startBehavior = "RequiresFile",
+ onStartFailed = function()
+ a.sync(function()
+ if IsTryingToInstall == true then
+ mm.writeFlashing("RECURSION!");
+ return
+ end
+ IsTryingToInstall = true
+ installOrUpdateServer()
+ IsTryingToInstall = false
+ end)()
+ end
})
Client.on_instance_start = function()
@@ -46,24 +129,6 @@ local function setupClient()
end)
end
-local function installServer()
- print("Lua Language Server not found, installing ...")
- local cmds = {
- mac = "brew install lua-language-server",
- windows = "winget install lua-language-server",
- linux = "sudo apt install lua-language-server"
- }
- if a.wait(Process.runInTerminal(cmds[Utils.HostOsInfo.os])) == 0 then
- print("Lua Language Server installed!")
- Settings.binary.defaultPath = Utils.FilePath.fromUserInput("lua-language-server"):resolveSymlinks()
- Settings:apply()
- return true
- end
-
- print("Lua Language Server installation failed!")
- return false
-end
-
local function using(tbl)
local result = _G
for k, v in pairs(tbl) do result[k] = v end
@@ -80,9 +145,8 @@ local function layoutSettings()
"Language server not found:",
Row {
PushButton {
- text("Try to install lua language server"),
- onClicked(function() a.sync(installServer)() end),
- br,
+ text = "Try to install lua language server",
+ onClicked = function() a.sync(installServer)() end,
},
st
}
@@ -112,8 +176,14 @@ local function setupAspect()
labelText = "Binary:",
toolTip = "The path to the lua-language-server binary.",
expectedKind = S.Kind.ExistingCommand,
- defaultPath = Utils.FilePath.fromUserInput("lua-language-server"):resolveSymlinks(),
+ defaultPath = Utils.FilePath.fromUserInput("lua-language-server"),
})
+ -- Search for the binary in the PATH
+ local serverPath = Settings.binary.defaultPath
+ local absolute = a.wait(serverPath:searchInPath()):resolveSymlinks()
+ if absolute:isExecutableFile() == true then
+ Settings.binary.defaultPath = absolute
+ end
Settings.developMode = S.BoolAspect.create({
settingsKey = "LuaCopilot.DevelopMode",
displayName = "Enable Develop Mode",
@@ -143,15 +213,7 @@ local function setupAspect()
return Settings
end
local function setup(parameters)
- print("Setting up Lua Language Server ...")
setupAspect()
- local serverPath = Utils.FilePath.fromUserInput("lua-language-server")
- local absolute = a.wait(serverPath:searchInPath()):resolveSymlinks()
- if absolute:isExecutableFile() == true then
- Settings.binary.defaultPath = absolute
- else
- installServer()
- end
setupClient()
end
diff --git a/src/plugins/lualsp/lualsp/lualsp.lua b/src/plugins/lualsp/lualsp/lualsp.lua
index ba2de87414..843d18189e 100644
--- a/src/plugins/lualsp/lualsp/lualsp.lua
+++ b/src/plugins/lualsp/lualsp/lualsp.lua
@@ -21,10 +21,4 @@ It will try to install it if it is not found.
setup = function()
require 'init'.setup()
end,
- hooks = {
- editors = {
- documentOpened = function(doc) print("documentOpened", doc) end,
- documentClosed = function(doc) print("documentClosed", doc) end,
- }
- }
} --[[@as QtcPlugin]]
diff --git a/src/plugins/luatemplates/CMakeLists.txt b/src/plugins/luatemplates/CMakeLists.txt
deleted file mode 100644
index 87088b7058..0000000000
--- a/src/plugins/luatemplates/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-add_qtc_plugin(LuaTemplates
- CONDITION TARGET Lua
- PLUGIN_DEPENDS Lua TextEditor ProjectExplorer Core
- SOURCES
- luatemplates.cpp
-)
-
-add_subdirectory(templates)
diff --git a/src/plugins/luatemplates/luatemplates.cpp b/src/plugins/luatemplates/luatemplates.cpp
deleted file mode 100644
index 3adea77040..0000000000
--- a/src/plugins/luatemplates/luatemplates.cpp
+++ /dev/null
@@ -1,402 +0,0 @@
-// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include <lua/bindings/inheritance.h>
-#include <lua/luaengine.h>
-
-#include <coreplugin/dialogs/promptoverwritedialog.h>
-#include <coreplugin/editormanager/editormanager.h>
-#include <coreplugin/icore.h>
-#include <coreplugin/iwizardfactory.h>
-
-#include <extensionsystem/iplugin.h>
-#include <extensionsystem/pluginmanager.h>
-
-#include <utils/algorithm.h>
-#include <utils/expected.h>
-#include <utils/layoutbuilder.h>
-#include <utils/mimeutils.h>
-#include <utils/wizard.h>
-#include <utils/wizardpage.h>
-
-#include <projectexplorer/editorconfiguration.h>
-#include <projectexplorer/project.h>
-#include <projectexplorer/projectexplorertr.h>
-#include <projectexplorer/projectwizardpage.h>
-
-#include <texteditor/icodestylepreferences.h>
-#include <texteditor/icodestylepreferencesfactory.h>
-#include <texteditor/storagesettings.h>
-#include <texteditor/tabsettings.h>
-#include <texteditor/texteditorsettings.h>
-#include <texteditor/textindenter.h>
-
-#include <QMessageBox>
-
-using namespace Utils;
-using namespace Layouting;
-using namespace Core;
-using namespace ProjectExplorer;
-using namespace TextEditor;
-
-namespace LuaTemplates {
-
-static ICodeStylePreferences *codeStylePreferences(Project *project, Id languageId)
-{
- if (!languageId.isValid())
- return nullptr;
-
- if (project)
- return project->editorConfiguration()->codeStyle(languageId);
-
- return TextEditorSettings::codeStyle(languageId);
-}
-
-class LuaWizard : public Wizard
-{
-public:
- LuaWizard()
- : Wizard(Core::ICore::dialogParent())
- {}
-
- expected_str<bool> promptForOverwrite(GeneratedFiles &files)
- {
- FilePaths existingFiles;
- bool oddStuffFound = false;
-
- for (const auto &f : files) {
- if (f.filePath().exists() && !(f.attributes() & GeneratedFile::ForceOverwrite)
- && !(f.attributes() & GeneratedFile::KeepExistingFileAttribute))
- existingFiles.append(f.filePath());
- }
- if (existingFiles.isEmpty())
- return true;
-
- // Before prompting to overwrite existing files, loop over files and check
- // if there is anything blocking overwriting them (like them being links or folders).
- // Format a file list message as ( "<file1> [readonly], <file2> [folder]").
- const QString commonExistingPath = FileUtils::commonPath(existingFiles).toUserOutput();
- const int commonPathSize = commonExistingPath.size();
- QString fileNamesMsgPart;
- for (const FilePath &filePath : std::as_const(existingFiles)) {
- if (filePath.exists()) {
- if (!fileNamesMsgPart.isEmpty())
- fileNamesMsgPart += QLatin1String(", ");
- const QString namePart = filePath.toUserOutput().mid(commonPathSize);
- if (filePath.isDir()) {
- oddStuffFound = true;
- fileNamesMsgPart += Tr::tr("%1 [folder]").arg(namePart);
- } else if (filePath.isSymLink()) {
- oddStuffFound = true;
- fileNamesMsgPart += Tr::tr("%1 [symbolic link]").arg(namePart);
- } else if (!filePath.isWritableDir() && !filePath.isWritableFile()) {
- oddStuffFound = true;
- fileNamesMsgPart += Tr::tr("%1 [read only]").arg(namePart);
- }
- }
- }
-
- if (oddStuffFound) {
- return make_unexpected(
- Tr::tr("The directory %1 contains files which cannot be overwritten:\n%2.")
- .arg(commonExistingPath)
- .arg(fileNamesMsgPart));
- }
-
- // Prompt to overwrite existing files.
- PromptOverwriteDialog overwriteDialog;
-
- // Scripts cannot handle overwrite
- overwriteDialog.setFiles(existingFiles);
- for (const auto &file : files) {
- if (!allowKeepingExistingFiles)
- overwriteDialog.setFileEnabled(file.filePath(), false);
- }
- if (overwriteDialog.exec() != QDialog::Accepted)
- return false;
-
- const QSet<FilePath> existingFilesToKeep = Utils::toSet(overwriteDialog.uncheckedFiles());
- if (existingFilesToKeep.size() == files.size()) // All exist & all unchecked->Cancel.
- return false;
-
- // Set 'keep' attribute in files
- for (auto &file : files) {
- if (!existingFilesToKeep.contains(file.filePath()))
- continue;
-
- file.setAttributes(file.attributes() | GeneratedFile::KeepExistingFileAttribute);
- }
- return true;
- }
-
- void formatFile(GeneratedFile &file)
- {
- if (file.isBinary() || file.contents().isEmpty())
- return; // nothing to do
-
- Id languageId = TextEditorSettings::languageId(
- Utils::mimeTypeForFile(file.filePath()).name());
-
- if (!languageId.isValid())
- return; // don't modify files like *.ui, *.pro
-
- // TODO:
- auto baseProject
- = nullptr; // qobject_cast<Project *>( wizard->property("SelectedProject").value<QObject *>());
-
- ICodeStylePreferencesFactory *factory = TextEditorSettings::codeStyleFactory(languageId);
-
- QTextDocument doc(file.contents());
- QTextCursor cursor(&doc);
- Indenter *indenter = nullptr;
- if (factory) {
- indenter = factory->createIndenter(&doc);
- indenter->setFileName(file.filePath());
- }
- if (!indenter)
- indenter = new TextIndenter(&doc);
- ICodeStylePreferences *codeStylePrefs = codeStylePreferences(baseProject, languageId);
- indenter->setCodeStylePreferences(codeStylePrefs);
-
- cursor.select(QTextCursor::Document);
- indenter->indent(cursor, QChar::Null, codeStylePrefs->currentTabSettings());
- delete indenter;
- if (TextEditor::globalStorageSettings().m_cleanWhitespace) {
- QTextBlock block = doc.firstBlock();
- while (block.isValid()) {
- TabSettings::removeTrailingWhitespace(cursor, block);
- block = block.next();
- }
- }
- file.setContents(doc.toPlainText());
- }
-
- void formatFiles(GeneratedFiles &files)
- {
- for (auto &file : files)
- formatFile(file);
- }
-
- void accept() override
- {
- auto files = Lua::LuaEngine::safe_call<GeneratedFiles>(fileFactory);
- QTC_ASSERT_EXPECTED(files, return);
-
- auto result = promptForOverwrite(*files);
- if (!result) {
- QMessageBox::warning(this, Tr::tr("Failed to Overwrite Files"), result.error());
- return;
- }
-
- formatFiles(*files);
-
- if (*result) {
- for (const auto &file : *files) {
- QString errorMsg;
- if (file.attributes().testFlag(GeneratedFile::KeepExistingFileAttribute))
- continue;
-
- if (!file.write(&errorMsg)) {
- qWarning() << "Failed writing file:" << errorMsg;
- continue;
- } else if (file.attributes().testFlag(GeneratedFile::OpenEditorAttribute)) {
- EditorManager::openEditor(file.filePath());
- }
- }
- }
-
- Wizard::accept();
- return;
- }
-
- void reject() override { Wizard::reject(); }
-
- sol::protected_function fileFactory;
- bool allowKeepingExistingFiles{true};
-};
-
-class WizardFactory : public IWizardFactory
-{
-public:
- WizardFactory() {}
-
- Wizard *runWizardImpl(
- const FilePath &path,
- QWidget *parent,
- Id /*platform*/,
- const QVariantMap & /*variables*/,
- bool showWizard = true) override
- {
- // We assume that the parent is always "dialogParent".
- QTC_CHECK(parent == Core::ICore::dialogParent());
- auto wizard = Lua::LuaEngine::safe_call<LuaWizard *>(m_setupFunction, path);
- QTC_ASSERT_EXPECTED(wizard, return nullptr);
-
- if (showWizard)
- (*wizard)->show();
- return (*wizard);
- }
-
- sol::protected_function m_setupFunction;
-};
-
-class LuaWizardPage : public WizardPage
-{
-public:
- void initializePage() override
- {
- if (m_initializePage) {
- auto res = Lua::LuaEngine::void_safe_call(*m_initializePage, this);
- QTC_CHECK_EXPECTED(res);
- }
- WizardPage::initializePage();
- }
- std::optional<sol::function> m_initializePage;
-};
-
-class SummaryPage : public ProjectWizardPage
-{
-public:
- void initializePage() override
- {
- if (m_initializePage) {
- auto res = Lua::LuaEngine::void_safe_call(*m_initializePage, this);
- QTC_CHECK_EXPECTED(res);
- }
-
- FilePaths paths = Utils::transform(m_files,
- [](const GeneratedFile &f) { return f.filePath(); });
- initializeProjectTree(nullptr,
- paths,
- IWizardFactory::WizardKind::FileWizard,
- ProjectAction::AddNewFile);
-
- initializeVersionControls();
-
- ProjectWizardPage::initializePage();
- }
-
- void setFiles(const GeneratedFiles &files)
- {
- m_files = std::move(files);
- FilePaths paths = Utils::transform(m_files,
- [](const GeneratedFile &f) { return f.filePath(); });
- ProjectWizardPage::setFiles(paths);
- }
-
- GeneratedFiles m_files;
- std::optional<sol::protected_function> m_initializePage;
-};
-
-class LuaTemplatesPlugin final : public ExtensionSystem::IPlugin
-{
- Q_OBJECT
- Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "LuaTemplates.json")
-
-public:
- LuaTemplatesPlugin() {}
-
- using Factories = QList<std::function<IWizardFactory *()>>;
- using WeakFactories = std::weak_ptr<QList<std::function<IWizardFactory *()>>>;
-
-private:
- void initialize() final
- {
- Lua::LuaEngine::registerProvider(
- "Wizard", [](sol::state_view lua) -> sol::object {
- sol::table wizard = lua.create_table();
- wizard.new_usertype<WizardFactory>("Factory", sol::no_constructor);
-
- wizard.new_usertype<SummaryPage>(
- "SummaryPage", "setFiles", [](SummaryPage *page, QList<GeneratedFile> files) {
- page->setFiles(std::move(files));
- });
-
- wizard.new_usertype<LuaWizard>(
- "Wizard",
- sol::no_constructor,
- "addPage",
- [](LuaWizard *wizard, const sol::table &options) {
- LuaWizardPage *page = new LuaWizardPage();
-
- page->m_initializePage = options.get<std::optional<sol::function>>(
- "initializePage");
-
- page->setTitle(options.get<QString>("title"));
- auto item = options.get<Layouting::LayoutItem *>("layout");
- item->attachTo(page);
- wizard->addPage(page);
- return page;
- },
- "addSummaryPage",
- [](LuaWizard *wizard, const sol::table &options) {
- SummaryPage *page = new SummaryPage();
-
- page->m_initializePage = options.get<std::optional<sol::function>>(
- "initializePage");
-
- wizard->addPage(page);
- return page;
- });
-
- wizard.set_function("create", [](const sol::table &options) {
- std::unique_ptr<LuaWizard> wizard(new LuaWizard());
- wizard->fileFactory = options.get<sol::function>("fileFactory");
- wizard->allowKeepingExistingFiles
- = options.get<std::optional<bool>>("allowKeepingExistingFiles")
- .value_or(true);
- return wizard.release();
- });
-
- wizard.set_function(
- "registerFactory",
- [factories = std::make_shared<Factories>()](const sol::table &options) mutable {
- // We need to make sure that all options are available before registering
- // the factory.
- Lua::LuaEngine::checkKey<QString>(options, "id");
- Lua::LuaEngine::checkKey<QString>(options, "displayName");
- Lua::LuaEngine::checkKey<QString>(options, "description");
- Lua::LuaEngine::checkKey<QString>(options, "category");
- Lua::LuaEngine::checkKey<QString>(options, "displayCategory");
- Lua::LuaEngine::checkKey<sol::function>(options, "factory");
-
- // We have to make sure that no lua object is accessed after the lua_state
- // is destroyed. Therefore we store the factory in a shared_ptr and
- // only give a weak pointer to the actual registered factory function.
- // That way we can make sure that the factory list is destroyed when the
- // lua_state is destroyed, as the current function is stored inside the table
- // "wizard" which is automatically destroyed when the lua_state is destroyed.
- factories->push_back([options]() -> IWizardFactory * {
- std::unique_ptr<WizardFactory> factory(new WizardFactory());
-
- factory->setId(Utils::Id::fromString(options.get<QString>("id")));
- factory->setDisplayName(options.get<QString>("displayName"));
- factory->setDescription(options.get<QString>("description"));
- factory->setCategory(options.get<QString>("category"));
- factory->setDisplayCategory(options.get<QString>("displayCategory"));
- factory->setFlags(IWizardFactory::PlatformIndependent);
- factory->setIcon(
- QIcon(options.get_or<QString>("icon", {})),
- options.get_or<QString>("iconText", {}));
- factory->m_setupFunction = options.get<sol::function>("factory");
-
- return factory.release();
- });
-
- IWizardFactory::registerFactoryCreator(
- [weakFactories = WeakFactories(factories),
- index = factories->size() - 1]() -> IWizardFactory * {
- if (auto factories = weakFactories.lock())
- return (*factories)[index]();
- return nullptr;
- });
- });
-
- return wizard;
- });
- }
-};
-
-} // namespace LuaTemplates
-
-#include "luatemplates.moc"
diff --git a/src/plugins/luatemplates/templates/CMakeLists.txt b/src/plugins/luatemplates/templates/CMakeLists.txt
deleted file mode 100644
index 96e6dee944..0000000000
--- a/src/plugins/luatemplates/templates/CMakeLists.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-add_qtc_lua_plugin(lua_basic_templates
- SOURCES basic_templates/basic_templates.lua
- basic_templates/init.lua
-)
diff --git a/src/plugins/luatemplates/templates/basic_templates/basic_templates.lua b/src/plugins/luatemplates/templates/basic_templates/basic_templates.lua
deleted file mode 100644
index d3668daf4d..0000000000
--- a/src/plugins/luatemplates/templates/basic_templates/basic_templates.lua
+++ /dev/null
@@ -1,13 +0,0 @@
--- Copyright (C) 2024 The Qt Company Ltd.
--- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-return {
- Name = "BasicTemplates",
- Version = "1.0.0",
- CompatVersion = "1.0.0",
- Vendor = "The Qt Company",
- Category = "Templates",
- Dependencies = {
- { Name = "LuaTemplates", Version = "13.0.82", Required = true },
- },
- setup = function() require "init".setup() end,
-} --[[@as QtcPlugin]]
diff --git a/src/plugins/luatemplates/templates/basic_templates/init.lua b/src/plugins/luatemplates/templates/basic_templates/init.lua
deleted file mode 100644
index 60399467bd..0000000000
--- a/src/plugins/luatemplates/templates/basic_templates/init.lua
+++ /dev/null
@@ -1,65 +0,0 @@
--- Copyright (C) 2024 The Qt Company Ltd.
--- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-local Wizard = require('Wizard')
-local Layout = require('Layout')
-local Settings = require('Settings')
-local Core = require('Core')
-local Utils = require('Utils')
-
----@param settings AspectContainer
-local function generateFiles(settings)
- local mainFile = Core.GeneratedFile.new()
- mainFile.filePath = settings.path.expandedValue:resolvePath(settings.fileName.value)
- mainFile.contents = [[print("Hello world!")]]
- mainFile.attributes = Core.GeneratedFile.Attribute.OpenEditorAttribute
- return { mainFile }
-end
-
-local function createWizard(path)
- ---@class AspectContainer
- local settings = Settings.AspectContainer.create({
- autoApply = true,
- })
- settings.fileName = Settings.StringAspect.create({
- defaultValue = "script.lua",
- displayStyle = Settings.StringDisplayStyle.LineEdit,
- historyId = "BasicTemplate.FileName",
- })
- settings.path = Settings.FilePathAspect.create({
- defaultPath = path,
- expectedKind = Settings.Kind.ExistingDirectory,
- })
-
- local wizard = Wizard.create({
- fileFactory = function() return generateFiles(settings) end,
- })
-
- wizard:addPage({
- title = "Location",
- layout = Layout.Form {
- "File name:", settings.fileName, Layout.br,
- "Path:", settings.path, Layout.br,
- },
- })
-
- wizard:addSummaryPage({
- initializePage = function(page)
- page:setFiles(generateFiles(settings))
- end
- })
- return wizard
-end
-
-local function setup()
- Wizard.registerFactory({
- id = "org.qtproject.Qt.QtCreator.Plugin.LuaTemplates.BasicTemplates",
- displayName = "Basic Templates",
- description = "Basic Template for Lua",
- category = "Lua",
- displayCategory = "Lua",
- iconText = "lua",
- factory = createWizard,
- })
-end
-
-return { setup = setup }
diff --git a/src/plugins/luatests/luatests/luatests.lua b/src/plugins/luatests/luatests/luatests.lua
index db0fb7f0c6..4d22738f3b 100644
--- a/src/plugins/luatests/luatests/luatests.lua
+++ b/src/plugins/luatests/luatests/luatests.lua
@@ -17,4 +17,5 @@ return {
{ Name = "Lua", Version = "13.0.82", Required = true }
},
setup = function() require 'tests'.setup() end,
+ printToOutputPane = true,
} --[[@as QtcPlugin]]
diff --git a/src/plugins/macros/Macros.json.in b/src/plugins/macros/Macros.json.in
index 1c5d949c1c..23c5acf426 100644
--- a/src/plugins/macros/Macros.json.in
+++ b/src/plugins/macros/Macros.json.in
@@ -13,6 +13,6 @@
"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" : "Macros in text editors.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/marketplace/Marketplace.json.in b/src/plugins/marketplace/Marketplace.json.in
index 1122b40a67..ef45ca3cde 100644
--- a/src/plugins/marketplace/Marketplace.json.in
+++ b/src/plugins/marketplace/Marketplace.json.in
@@ -13,6 +13,6 @@
"Alternatively, this file may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this file. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html."
],
"Description" : "Qt Marketplace plugin.",
-"Url" : "http://www.qt.io",
+"Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/mcusupport/McuSupport.json.in b/src/plugins/mcusupport/McuSupport.json.in
index bac0409ee7..6521062d04 100644
--- a/src/plugins/mcusupport/McuSupport.json.in
+++ b/src/plugins/mcusupport/McuSupport.json.in
@@ -15,7 +15,7 @@
],
"Category" : "Device Support",
"Description" : "Helper for MCU related projects.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"JsonWizardPaths" : [":/mcusupport/wizards/"]
diff --git a/src/plugins/mcusupport/mcukitaspect.cpp b/src/plugins/mcusupport/mcukitaspect.cpp
index 8d497f209d..1ab803b243 100644
--- a/src/plugins/mcusupport/mcukitaspect.cpp
+++ b/src/plugins/mcusupport/mcukitaspect.cpp
@@ -24,7 +24,7 @@ public:
void makeReadOnly() override {}
void refresh() override {}
- void addToLayoutImpl(Layouting::LayoutItem &) override {}
+ void addToLayoutImpl(Layouting::Layout &) override {}
};
Utils::Id McuDependenciesKitAspect::id()
@@ -78,7 +78,7 @@ public:
const QVariant checkFormat = kit->value(McuDependenciesKitAspect::id());
if (!checkFormat.isValid() || checkFormat.isNull())
return result;
- if (!checkFormat.canConvert(QVariant::List))
+ if (!checkFormat.canConvert(QMetaType::QVariantList))
return {BuildSystemTask(Task::Error, Tr::tr("The MCU dependencies setting value is invalid."))};
// check paths defined in cmake variables for given dependencies exist
@@ -105,7 +105,7 @@ public:
QTC_ASSERT(kit, return );
const QVariant variant = kit->value(McuDependenciesKitAspect::id());
- if (!variant.isNull() && !variant.canConvert(QVariant::List)) {
+ if (!variant.isNull() && !variant.canConvert(QMetaType::QVariantList)) {
qWarning("Kit \"%s\" has a wrong mcu dependencies value set.",
qPrintable(kit->displayName()));
McuDependenciesKitAspect::setDependencies(kit, Utils::EnvironmentItems());
diff --git a/src/plugins/mercurial/Mercurial.json.in b/src/plugins/mercurial/Mercurial.json.in
index 5c897570e5..a59cc8e653 100644
--- a/src/plugins/mercurial/Mercurial.json.in
+++ b/src/plugins/mercurial/Mercurial.json.in
@@ -14,6 +14,6 @@
],
"Category" : "Version Control",
"Description" : "Mercurial integration.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/mercurial/mercurialplugin.cpp b/src/plugins/mercurial/mercurialplugin.cpp
index f87d181da4..39b3fe4432 100644
--- a/src/plugins/mercurial/mercurialplugin.cpp
+++ b/src/plugins/mercurial/mercurialplugin.cpp
@@ -734,11 +734,9 @@ VcsCommand *MercurialPluginPrivate::createInitialCheckoutCommand(const QString &
const QString &localName,
const QStringList &extraArgs)
{
- QStringList args;
- args << QLatin1String("clone") << extraArgs << url << localName;
auto command = VcsBaseClient::createVcsCommand(this, baseDirectory,
mercurialClient().processEnvironment(baseDirectory));
- command->addJob({settings().binaryPath(), args}, -1);
+ command->addJob({settings().binaryPath(), {"clone", extraArgs, url, localName}}, -1);
return command;
}
@@ -755,11 +753,11 @@ bool MercurialPluginPrivate::sccManaged(const QString &filename)
void MercurialPluginPrivate::changed(const QVariant &v)
{
- switch (v.type()) {
- case QVariant::String:
+ switch (v.typeId()) {
+ case QMetaType::QString:
emit repositoryChanged(FilePath::fromVariant(v));
break;
- case QVariant::StringList:
+ case QMetaType::QStringList:
emit filesChanged(v.toStringList());
break;
default:
diff --git a/src/plugins/mesonprojectmanager/MesonProjectManager.json.in b/src/plugins/mesonprojectmanager/MesonProjectManager.json.in
index 169ac45e62..08a5400203 100644
--- a/src/plugins/mesonprojectmanager/MesonProjectManager.json.in
+++ b/src/plugins/mesonprojectmanager/MesonProjectManager.json.in
@@ -16,6 +16,6 @@
],
"Category" : "Build Systems",
"Description" : "Meson support.",
- "Url" : "http://www.mesonbuild.com",
+ "Url" : "https://www.mesonbuild.com",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/mesonprojectmanager/buildoptionsmodel.cpp b/src/plugins/mesonprojectmanager/buildoptionsmodel.cpp
index 1f8e7acd9b..f8db40b749 100644
--- a/src/plugins/mesonprojectmanager/buildoptionsmodel.cpp
+++ b/src/plugins/mesonprojectmanager/buildoptionsmodel.cpp
@@ -118,25 +118,25 @@ bool BuidOptionsModel::hasChanges() const
QWidget *BuildOptionDelegate::makeWidget(QWidget *parent, const QVariant &data)
{
- auto type = data.userType();
+ const int type = data.typeId();
switch (type) {
- case QVariant::Int: {
+ case QMetaType::Int: {
auto w = new QSpinBox{parent};
w->setValue(data.toInt());
return w;
}
- case QVariant::Bool: {
+ case QMetaType::Bool: {
auto w = new QComboBox{parent};
w->addItems({"false", "true"});
w->setCurrentIndex(data.toBool());
return w;
}
- case QVariant::StringList: {
+ case QMetaType::QStringList: {
auto w = new ArrayOptionLineEdit{parent};
w->setPlainText(data.toStringList().join(" "));
return w;
}
- case QVariant::String: {
+ case QMetaType::QString: {
auto w = new QLineEdit{parent};
w->setText(data.toString());
return w;
diff --git a/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp b/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp
index c936ac3130..090f48257f 100644
--- a/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp
+++ b/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp
@@ -191,7 +191,7 @@ public:
Column {
noMargin,
Form {
- Tr::tr("Parameters:"), parametersLineEdit, br,
+ Tr::tr("Parameters:"), parametersLineEdit, br,
buildCfg->buildDirectoryAspect(), br
},
optionsFilterLineEdit,
diff --git a/src/plugins/mesonprojectmanager/toolkitaspectwidget.h b/src/plugins/mesonprojectmanager/toolkitaspectwidget.h
index 252e351ff7..0847199d23 100644
--- a/src/plugins/mesonprojectmanager/toolkitaspectwidget.h
+++ b/src/plugins/mesonprojectmanager/toolkitaspectwidget.h
@@ -36,7 +36,7 @@ private:
void makeReadOnly() override { m_toolsComboBox->setEnabled(false); }
- void addToLayoutImpl(Layouting::LayoutItem &parent) override
+ void addToLayoutImpl(Layouting::Layout &parent) override
{
addMutableAction(m_toolsComboBox);
parent.addItem(m_toolsComboBox);
diff --git a/src/plugins/modeleditor/ModelEditor.json.in b/src/plugins/modeleditor/ModelEditor.json.in
index 6237bce22f..108c1cd548 100644
--- a/src/plugins/modeleditor/ModelEditor.json.in
+++ b/src/plugins/modeleditor/ModelEditor.json.in
@@ -14,7 +14,7 @@
],
"Category" : "Modeling",
"Description" : "Graphical modeling with structured diagrams.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/modeleditor/componentviewcontroller.cpp b/src/plugins/modeleditor/componentviewcontroller.cpp
index b370c10d2d..cb98ca21ed 100644
--- a/src/plugins/modeleditor/componentviewcontroller.cpp
+++ b/src/plugins/modeleditor/componentviewcontroller.cpp
@@ -31,6 +31,7 @@
using namespace ProjectExplorer;
using namespace Utils;
+using Utils::FilePath;
namespace ModelEditor {
namespace Internal {
@@ -52,9 +53,9 @@ private:
void FindComponentFromFilePath::setFilePath(const QString &filePath)
{
- m_elementName = qmt::NameController::convertFileNameToElementName(Utils::FilePath::fromString(filePath));
+ m_elementName = qmt::NameController::convertFileNameToElementName(FilePath::fromString(filePath));
QFileInfo fileInfo(filePath);
- m_elementsPath = qmt::NameController::buildElementsPath(Utils::FilePath::fromString(fileInfo.path()), false);
+ m_elementsPath = qmt::NameController::buildElementsPath(FilePath::fromString(fileInfo.path()), false);
}
void FindComponentFromFilePath::visitMComponent(qmt::MComponent *component)
@@ -154,7 +155,7 @@ void UpdateIncludeDependenciesVisitor::visitMComponent(qmt::MComponent *componen
if (document) {
const QList<CPlusPlus::Document::Include> includes = document->resolvedIncludes();
for (const CPlusPlus::Document::Include &include : includes) {
- Utils::FilePath includeFilePath = include.resolvedFileName();
+ FilePath includeFilePath = include.resolvedFileName();
// replace proxy header with real one
CPlusPlus::Document::Ptr includeDocument = snapshot.document(includeFilePath);
if (includeDocument) {
@@ -221,7 +222,7 @@ void UpdateIncludeDependenciesVisitor::collectElementPaths(const ProjectExplorer
QString elementName = qmt::NameController::convertFileNameToElementName(fileNode->filePath());
QFileInfo fileInfo = fileNode->filePath().toFileInfo();
QString nodePath = fileInfo.path();
- QStringList elementsPath = qmt::NameController::buildElementsPath(Utils::FilePath::fromString(nodePath), false);
+ QStringList elementsPath = qmt::NameController::buildElementsPath(FilePath::fromString(nodePath), false);
filePathsMap->insert(elementName, Node(fileNode->filePath().toString(), elementsPath));
});
folderNode->forEachFolderNode([&](FolderNode *subNode) {
@@ -309,7 +310,7 @@ void ComponentViewController::doCreateComponentModel(const QString &filePath, qm
{
for (const QString &fileName : QDir(filePath).entryList(QDir::Files)) {
QString file = filePath + "/" + fileName;
- QString componentName = qmt::NameController::convertFileNameToElementName(Utils::FilePath::fromString(file));
+ QString componentName = qmt::NameController::convertFileNameToElementName(FilePath::fromString(file));
qmt::MComponent *component = nullptr;
bool isSource = false;
CppEditor::ProjectFile::Kind kind = CppEditor::ProjectFile::classify(file);
@@ -341,7 +342,7 @@ void ComponentViewController::doCreateComponentModel(const QString &filePath, qm
}
if (component) {
QStringList relativeElements = qmt::NameController::buildElementsPath(
- Utils::FilePath::fromString(d->pxnodeUtilities->calcRelativePath(file, anchorFolder)), false);
+ FilePath::fromString(d->pxnodeUtilities->calcRelativePath(file, anchorFolder)), false);
if (d->pxnodeUtilities->findSameObject(relativeElements, component)) {
delete component;
} else {
diff --git a/src/plugins/modeleditor/elementtasks.cpp b/src/plugins/modeleditor/elementtasks.cpp
index c13f96e39c..7bbdd7b3e0 100644
--- a/src/plugins/modeleditor/elementtasks.cpp
+++ b/src/plugins/modeleditor/elementtasks.cpp
@@ -40,6 +40,7 @@
using namespace Core;
using namespace CppEditor;
+using Utils::FilePath;
namespace ModelEditor {
namespace Internal {
@@ -401,11 +402,11 @@ void ElementTasks::createAndOpenDiagram(const qmt::DElement *element, const qmt:
createAndOpenDiagram(melement);
}
-Utils::FilePath ElementTasks::linkedFile(const qmt::MObject *mobject) const
+FilePath ElementTasks::linkedFile(const qmt::MObject *mobject) const
{
- Utils::FilePath filepath = Utils::FilePath::fromString(mobject->linkedFileName());
+ FilePath filepath = mobject->linkedFileName();
if (!filepath.isEmpty()) {
- Utils::FilePath projectName = d->documentController->projectController()->project()->fileName();
+ FilePath projectName = d->documentController->projectController()->project()->fileName();
filepath = projectName.absolutePath().resolvePath(filepath).canonicalPath();
}
return filepath;
@@ -414,7 +415,7 @@ Utils::FilePath ElementTasks::linkedFile(const qmt::MObject *mobject) const
bool ElementTasks::hasLinkedFile(const qmt::MElement *element) const
{
if (auto mobject = dynamic_cast<const qmt::MObject *>(element)) {
- Utils::FilePath filepath = linkedFile(mobject);
+ FilePath filepath = linkedFile(mobject);
if (!filepath.isEmpty())
return filepath.exists();
}
@@ -434,16 +435,16 @@ bool ElementTasks::hasLinkedFile(const qmt::DElement *element, const qmt::MDiagr
void ElementTasks::openLinkedFile(const qmt::MElement *element)
{
if (auto mobject = dynamic_cast<const qmt::MObject *>(element)) {
- Utils::FilePath filepath = linkedFile(mobject);
+ FilePath filepath = linkedFile(mobject);
if (!filepath.isEmpty()) {
if (filepath.exists()) {
Core::EditorFactories list = Core::IEditorFactory::preferredEditorFactories(filepath);
if (list.empty() || (list.count() <= 1 && list.at(0)->id() == "Core.BinaryEditor")) {
// intentionally ignore return code
- (void) Core::EditorManager::instance()->openExternalEditor(filepath, "CorePlugin.OpenWithSystemEditor");
+ (void) Core::EditorManager::openExternalEditor(filepath, "CorePlugin.OpenWithSystemEditor");
} else {
// intentionally ignore return code
- (void) Core::EditorManager::instance()->openEditor(filepath);
+ (void) Core::EditorManager::openEditor(filepath);
}
} else {
QMessageBox::critical(Core::ICore::dialogParent(), Tr::tr("Opening File"),
diff --git a/src/plugins/modeleditor/extdocumentcontroller.cpp b/src/plugins/modeleditor/extdocumentcontroller.cpp
index ad2940c05f..f62d1c173b 100644
--- a/src/plugins/modeleditor/extdocumentcontroller.cpp
+++ b/src/plugins/modeleditor/extdocumentcontroller.cpp
@@ -9,6 +9,8 @@
#include "qmt/project_controller/projectcontroller.h"
#include "qmt/tasks/diagramscenecontroller.h"
+using Utils::FilePath;
+
namespace ModelEditor {
namespace Internal {
@@ -49,7 +51,7 @@ PxNodeController *ExtDocumentController::pxNodeController() const
return d->pxNodeController;
}
-void ExtDocumentController::onProjectFileNameChanged(const Utils::FilePath &fileName)
+void ExtDocumentController::onProjectFileNameChanged(const FilePath &fileName)
{
d->pxNodeController->setAnchorFolder(fileName.path());
}
diff --git a/src/plugins/modeleditor/extpropertiesmview.cpp b/src/plugins/modeleditor/extpropertiesmview.cpp
index 68dac749f1..b105554e6f 100644
--- a/src/plugins/modeleditor/extpropertiesmview.cpp
+++ b/src/plugins/modeleditor/extpropertiesmview.cpp
@@ -21,6 +21,8 @@
#include <QMimeDatabase>
#include <QImageReader>
+using Utils::FilePath;
+
namespace ModelEditor {
namespace Internal {
@@ -52,7 +54,7 @@ static QString imageNameFilterString()
/// Constructs an absolute FilePath from \a relativePath which
/// is interpreted as being relative to \a anchor.
-Utils::FilePath absoluteFromRelativePath(const Utils::FilePath &relativePath,
+FilePath absoluteFromRelativePath(const Utils::FilePath &relativePath,
const Utils::FilePath &anchor)
{
QDir anchorDir = QFileInfo(anchor.path()).absoluteDir();
@@ -122,13 +124,13 @@ void ExtPropertiesMView::visitMObjectBehind(const qmt::MObject *object)
}
if (isSingleSelection) {
if (!m_filelinkPathChooser->hasFocus()) {
- QString path = object->linkedFileName();
+ Utils::FilePath path = object->linkedFileName();
if (path.isEmpty()) {
m_filelinkPathChooser->setPath(QString());
} else {
- Utils::FilePath relativePath = Utils::FilePath::fromString(path);
+ Utils::FilePath relativePath = path;
Utils::FilePath projectPath = project->fileName();
- QString filePath = absoluteFromRelativePath(relativePath, projectPath).toString();
+ QString filePath = absoluteFromRelativePath(relativePath, projectPath).toFSPathString();
m_filelinkPathChooser->setPath(filePath);
}
}
@@ -157,12 +159,12 @@ void ExtPropertiesMView::visitDObjectBefore(const qmt::DObject *object)
}
if (isSingleSelection) {
if (!m_imagePathChooser->hasFocus()) {
- QString path = object->imagePath();
+ Utils::FilePath path = object->imagePath();
if (path.isEmpty()) {
m_imagePathChooser->setPath(QString());
} else {
- Utils::FilePath relativePath = Utils::FilePath::fromString(path);
- QString filePath = absoluteFromRelativePath(relativePath, project->fileName()).toString();
+ Utils::FilePath relativePath = path;
+ QString filePath = absoluteFromRelativePath(relativePath, project->fileName()).toFSPathString();
m_imagePathChooser->setPath(filePath);
}
}
@@ -202,16 +204,24 @@ void ExtPropertiesMView::onFileLinkPathChanged(const QString &path)
{
qmt::Project *project = m_projectController->project();
if (path.isEmpty()) {
- assignModelElement<qmt::MObject, QString>(m_modelElements, SelectionSingle, QString(),
- &qmt::MObject::linkedFileName, &qmt::MObject::setLinkedFileName);
+ assignModelElement<qmt::MObject, Utils::FilePath>(
+ m_modelElements,
+ SelectionSingle,
+ {},
+ &qmt::MObject::linkedFileName,
+ &qmt::MObject::setLinkedFileName);
} else {
// make path relative to current project's directory
Utils::FilePath filePath = Utils::FilePath::fromString(path);
Utils::FilePath projectPath = project->fileName().absolutePath();
- QString relativeFilePath = filePath.relativePathFrom(projectPath).toString();
+ Utils::FilePath relativeFilePath = filePath.relativePathFrom(projectPath);
if (!relativeFilePath.isEmpty()) {
- assignModelElement<qmt::MObject, QString>(m_modelElements, SelectionSingle, relativeFilePath,
- &qmt::MObject::linkedFileName, &qmt::MObject::setLinkedFileName);
+ assignModelElement<qmt::MObject, Utils::FilePath>(
+ m_modelElements,
+ SelectionSingle,
+ relativeFilePath,
+ &qmt::MObject::linkedFileName,
+ &qmt::MObject::setLinkedFileName);
}
}
}
@@ -220,23 +230,30 @@ void ExtPropertiesMView::onImagePathChanged(const QString &path)
{
qmt::Project *project = m_projectController->project();
if (path.isEmpty()) {
- assignModelElement<qmt::DObject, QString>(m_diagramElements, SelectionSingle, QString(),
- &qmt::DObject::imagePath, &qmt::DObject::setImagePath);
+ assignModelElement<qmt::DObject, Utils::FilePath>(
+ m_diagramElements,
+ SelectionSingle,
+ {},
+ &qmt::DObject::imagePath,
+ &qmt::DObject::setImagePath);
assignModelElement<qmt::DObject, QImage>(m_diagramElements, SelectionSingle, QImage(),
&qmt::DObject::image, &qmt::DObject::setImage);
} else {
// make path relative to current project's directory
Utils::FilePath filePath = Utils::FilePath::fromString(path);
Utils::FilePath projectPath = project->fileName().absolutePath();
- QString relativeFilePath = filePath.relativePathFrom(projectPath).toString();
+ Utils::FilePath relativeFilePath = filePath.relativePathFrom(projectPath);
if (!relativeFilePath.isEmpty()
- && isValueChanged<qmt::DObject, QString>(m_diagramElements, SelectionSingle, relativeFilePath,
- &qmt::DObject::imagePath))
- {
+ && isValueChanged<qmt::DObject, Utils::FilePath>(
+ m_diagramElements, SelectionSingle, relativeFilePath, &qmt::DObject::imagePath)) {
QImage image;
if (image.load(path)) {
- assignModelElement<qmt::DObject, QString>(m_diagramElements, SelectionSingle, relativeFilePath,
- &qmt::DObject::imagePath, &qmt::DObject::setImagePath);
+ assignModelElement<qmt::DObject, Utils::FilePath>(
+ m_diagramElements,
+ SelectionSingle,
+ relativeFilePath,
+ &qmt::DObject::imagePath,
+ &qmt::DObject::setImagePath);
assignModelElement<qmt::DObject, QImage>(m_diagramElements, SelectionSingle, image,
&qmt::DObject::image, &qmt::DObject::setImage);
} else {
diff --git a/src/plugins/modeleditor/jsextension.cpp b/src/plugins/modeleditor/jsextension.cpp
index 60f00a113a..455814a49f 100644
--- a/src/plugins/modeleditor/jsextension.cpp
+++ b/src/plugins/modeleditor/jsextension.cpp
@@ -5,7 +5,9 @@
#include <qmt/controller/namecontroller.h>
-QString ModelEditor::Internal::JsExtension::fileNameToElementName(const Utils::FilePath &file)
+using Utils::FilePath;
+
+QString ModelEditor::Internal::JsExtension::fileNameToElementName(const FilePath &file)
{
return qmt::NameController::convertFileNameToElementName(file);
}
diff --git a/src/plugins/modeleditor/modeldocument.cpp b/src/plugins/modeleditor/modeldocument.cpp
index cc0ac3aa7d..00630341f9 100644
--- a/src/plugins/modeleditor/modeldocument.cpp
+++ b/src/plugins/modeleditor/modeldocument.cpp
@@ -19,6 +19,8 @@
#include <utils/id.h>
#include <utils/fileutils.h>
+using Utils::FilePath;
+
namespace ModelEditor {
namespace Internal {
@@ -43,8 +45,8 @@ ModelDocument::~ModelDocument()
}
Core::IDocument::OpenResult ModelDocument::open(QString *errorString,
- const Utils::FilePath &filePath,
- const Utils::FilePath &realFilePath)
+ const FilePath &filePath,
+ const FilePath &realFilePath)
{
Q_UNUSED(filePath)
@@ -52,7 +54,7 @@ Core::IDocument::OpenResult ModelDocument::open(QString *errorString,
return result;
}
-bool ModelDocument::saveImpl(QString *errorString, const Utils::FilePath &filePath, bool autoSave)
+bool ModelDocument::saveImpl(QString *errorString, const FilePath &filePath, bool autoSave)
{
if (!d->documentController) {
*errorString = Tr::tr("No model loaded. Cannot save.");
@@ -117,7 +119,7 @@ ExtDocumentController *ModelDocument::documentController() const
return d->documentController;
}
-Core::IDocument::OpenResult ModelDocument::load(QString *errorString, const Utils::FilePath &fileName)
+Core::IDocument::OpenResult ModelDocument::load(QString *errorString, const FilePath &fileName)
{
d->documentController = ModelEditorPlugin::modelsManager()->createModel(this);
connect(d->documentController, &qmt::DocumentController::changed, this, &IDocument::changed);
@@ -133,9 +135,9 @@ Core::IDocument::OpenResult ModelDocument::load(QString *errorString, const Util
return OpenResult::CannotHandle;
}
- Utils::FilePath configPath = d->documentController->projectController()->project()->configPath();
+ FilePath configPath = d->documentController->projectController()->project()->configPath();
if (!configPath.isEmpty()) {
- Utils::FilePath canonicalPath =fileName.absolutePath().resolvePath(configPath);
+ FilePath canonicalPath =fileName.absolutePath().resolvePath(configPath);
if (!canonicalPath.isEmpty()) {
// TODO error output on reading definition files
d->documentController->configController()->readStereotypeDefinitions(canonicalPath);
diff --git a/src/plugins/modeleditor/modeleditor.cpp b/src/plugins/modeleditor/modeleditor.cpp
index a518be67d0..dcf2c5b518 100644
--- a/src/plugins/modeleditor/modeleditor.cpp
+++ b/src/plugins/modeleditor/modeleditor.cpp
@@ -615,7 +615,7 @@ void ModelEditor::exportToImage(bool selectedElements)
QString fileName = FileUtils::getSaveFilePath(
nullptr,
selectedElements ? Tr::tr("Export Selected Elements") : Tr::tr("Export Diagram"),
- FilePath::fromString(d->lastExportDirPath), filter).toString();
+ FilePath::fromString(d->lastExportDirPath), filter).toFSPathString();
if (!fileName.isEmpty()) {
qmt::DocumentController *documentController = d->document->documentController();
qmt::DiagramSceneModel *sceneModel = documentController->diagramsManager()->diagramSceneModel(diagram);
@@ -1150,8 +1150,13 @@ void ModelEditor::initToolbars()
if (!tool.m_stereotype.isEmpty() && stereotypeIconElement != qmt::StereotypeIcon::ElementAny) {
const qmt::Style *style = documentController->styleController()->adaptStyle(styleEngineElementType);
icon = stereotypeController->createIcon(
- stereotypeIconElement, {tool.m_stereotype},
- QString(), style, QSize(128, 128), QMarginsF(6.0, 4.0, 6.0, 8.0), 8.0);
+ stereotypeIconElement,
+ {tool.m_stereotype},
+ {},
+ style,
+ QSize(128, 128),
+ QMarginsF(6.0, 4.0, 6.0, 8.0),
+ 8.0);
if (!icon.isNull()) {
QString stereotypeIconId = stereotypeController->findStereotypeIconId(
stereotypeIconElement, {tool.m_stereotype});
diff --git a/src/plugins/modeleditor/modelindexer.cpp b/src/plugins/modeleditor/modelindexer.cpp
index ed15c27036..a6de1c4008 100644
--- a/src/plugins/modeleditor/modelindexer.cpp
+++ b/src/plugins/modeleditor/modelindexer.cpp
@@ -36,6 +36,8 @@
#include <QPointer>
using namespace ProjectExplorer;
+using Utils::FilePath;
+using Utils::FilePaths;
namespace ModelEditor {
namespace Internal {
@@ -275,7 +277,7 @@ void ModelIndexer::IndexerThread::onFilesQueued()
qmt::ProjectSerializer projectSerializer;
qmt::Project project;
try {
- projectSerializer.load(Utils::FilePath::fromString(queuedFile.file()), &project);
+ projectSerializer.load(FilePath::fromString(queuedFile.file()), &project);
} catch (const qmt::Exception &e) {
qWarning() << e.errorMessage();
return;
@@ -375,13 +377,13 @@ void ModelIndexer::scanProject(ProjectExplorer::Project *project)
return;
// TODO harmonize following code with findFirstModel()?
- const Utils::FilePaths files = project->files(ProjectExplorer::Project::SourceFiles);
+ const FilePaths files = project->files(ProjectExplorer::Project::SourceFiles);
QQueue<QueuedFile> filesQueue;
QSet<QueuedFile> filesSet;
const Utils::MimeType modelMimeType = Utils::mimeTypeForName(Constants::MIME_TYPE_MODEL);
if (modelMimeType.isValid()) {
- for (const Utils::FilePath &file : files) {
+ for (const FilePath &file : files) {
if (modelMimeType.suffixes().contains(file.completeSuffix())) {
QueuedFile queuedFile(file.toString(), project, file.lastModified());
filesQueue.append(queuedFile);
@@ -466,10 +468,10 @@ QString ModelIndexer::findFirstModel(ProjectExplorer::FolderNode *folderNode,
void ModelIndexer::forgetProject(ProjectExplorer::Project *project)
{
- const Utils::FilePaths files = project->files(ProjectExplorer::Project::SourceFiles);
+ const FilePaths files = project->files(ProjectExplorer::Project::SourceFiles);
QMutexLocker locker(&d->indexerMutex);
- for (const Utils::FilePath &file : files) {
+ for (const FilePath &file : files) {
const QString fileString = file.toString();
// remove file from queue
QueuedFile queuedFile(fileString, project);
diff --git a/src/plugins/modeleditor/modelsmanager.cpp b/src/plugins/modeleditor/modelsmanager.cpp
index bd85e38695..02c481fa5d 100644
--- a/src/plugins/modeleditor/modelsmanager.cpp
+++ b/src/plugins/modeleditor/modelsmanager.cpp
@@ -41,6 +41,8 @@
#include <QTimer>
#include <QAction>
+using Utils::FilePath;
+
namespace ModelEditor {
namespace Internal {
@@ -236,7 +238,7 @@ void ModelsManager::onOpenDiagramFromProjectExplorer()
void ModelsManager::onOpenDefaultModel(const qmt::Uid &modelUid)
{
- const auto modelFile = Utils::FilePath::fromString(d->modelIndexer->findModel(modelUid));
+ const FilePath modelFile = FilePath::fromString(d->modelIndexer->findModel(modelUid));
if (!modelFile.isEmpty())
Core::EditorManager::openEditor(modelFile);
}
diff --git a/src/plugins/modeleditor/pxnodecontroller.cpp b/src/plugins/modeleditor/pxnodecontroller.cpp
index c83934db0f..50bbf901e6 100644
--- a/src/plugins/modeleditor/pxnodecontroller.cpp
+++ b/src/plugins/modeleditor/pxnodecontroller.cpp
@@ -30,7 +30,7 @@
#include <QMenu>
#include <QQueue>
-using namespace Utils;
+using Utils::FilePath;
namespace ModelEditor {
namespace Internal {
@@ -146,7 +146,7 @@ void PxNodeController::addFileSystemEntry(const QString &filePath, int line, int
{
QMT_ASSERT(diagram, return);
- QString elementName = qmt::NameController::convertFileNameToElementName(Utils::FilePath::fromString(filePath));
+ QString elementName = qmt::NameController::convertFileNameToElementName(FilePath::fromString(filePath));
QFileInfo fileInfo(filePath);
if (fileInfo.exists() && fileInfo.isFile()) {
@@ -212,7 +212,7 @@ qmt::MDiagram *PxNodeController::findDiagramForExplorerNode(const ProjectExplore
return nullptr;
QStringList relativeElements = qmt::NameController::buildElementsPath(
- Utils::FilePath::fromString(d->pxnodeUtilities->calcRelativePath(node, d->anchorFolder)), false);
+ FilePath::fromString(d->pxnodeUtilities->calcRelativePath(node, d->anchorFolder)), false);
QQueue<qmt::MPackage *> roots;
roots.append(d->diagramSceneController->modelController()->rootPackage());
@@ -322,7 +322,7 @@ void PxNodeController::onMenuActionTriggered(PxNodeController::MenuAction *actio
package->setStereotypes({action->stereotype});
d->diagramSceneController->modelController()->undoController()->beginMergeSequence(Tr::tr("Create Component Model"));
QStringList relativeElements = qmt::NameController::buildElementsPath(
- Utils::FilePath::fromString(d->pxnodeUtilities->calcRelativePath(filePath, d->anchorFolder)), true);
+ FilePath::fromString(d->pxnodeUtilities->calcRelativePath(filePath, d->anchorFolder)), true);
if (qmt::MObject *existingObject = d->pxnodeUtilities->findSameObject(relativeElements, package)) {
delete package;
package = dynamic_cast<qmt::MPackage *>(existingObject);
@@ -346,8 +346,8 @@ void PxNodeController::onMenuActionTriggered(PxNodeController::MenuAction *actio
item->setName(action->elementName);
item->setVariety(action->stereotype);
item->setVarietyEditable(false);
- Utils::FilePath filePath = Utils::FilePath::fromString(action->filePath);
- item->setLinkedFileName(filePath.relativePathFrom(Utils::FilePath::fromString(d->anchorFolder)).toString());
+ FilePath filePath = FilePath::fromString(action->filePath);
+ item->setLinkedFileName(filePath.relativePathFrom(FilePath::fromString(d->anchorFolder)));
newObject = item;
dropInCurrentDiagram = true;
break;
@@ -363,7 +363,7 @@ void PxNodeController::onMenuActionTriggered(PxNodeController::MenuAction *actio
} else {
qmt::MObject *parentForDiagram = nullptr;
QStringList relativeElements = qmt::NameController::buildElementsPath(
- Utils::FilePath::fromString(
+ FilePath::fromString(
d->pxnodeUtilities->calcRelativePath(filePath, d->anchorFolder)),
dynamic_cast<qmt::MPackage *>(newObject) != nullptr);
if (qmt::MObject *existingObject = d->pxnodeUtilities->findSameObject(relativeElements, newObject)) {
diff --git a/src/plugins/modeleditor/pxnodeutilities.cpp b/src/plugins/modeleditor/pxnodeutilities.cpp
index 08de4bdb05..5b8b27c830 100644
--- a/src/plugins/modeleditor/pxnodeutilities.cpp
+++ b/src/plugins/modeleditor/pxnodeutilities.cpp
@@ -20,6 +20,8 @@
#include <typeinfo>
+using Utils::FilePath;
+
namespace ModelEditor {
namespace Internal {
@@ -51,8 +53,8 @@ QString PxNodeUtilities::calcRelativePath(const ProjectExplorer::Node *node,
? node->filePath().toFileInfo().path()
: node->filePath().toString();
- return qmt::NameController::calcRelativePath(Utils::FilePath::fromString(nodePath),
- Utils::FilePath::fromString(anchorFolder)).toString();
+ return qmt::NameController::calcRelativePath(FilePath::fromString(nodePath),
+ FilePath::fromString(anchorFolder)).toString();
}
QString PxNodeUtilities::calcRelativePath(const QString &filePath, const QString &anchorFolder)
@@ -64,8 +66,8 @@ QString PxNodeUtilities::calcRelativePath(const QString &filePath, const QString
path = fileInfo.path();
else
path = filePath;
- return qmt::NameController::calcRelativePath(Utils::FilePath::fromString(path),
- Utils::FilePath::fromString(anchorFolder)).toString();
+ return qmt::NameController::calcRelativePath(FilePath::fromString(path),
+ FilePath::fromString(anchorFolder)).toString();
}
qmt::MPackage *PxNodeUtilities::createBestMatchingPackagePath(
@@ -220,7 +222,7 @@ bool PxNodeUtilities::isProxyHeader(const QString &file) const
{
CPlusPlus::Snapshot snapshot = CppEditor::CppModelManager::snapshot();
- CPlusPlus::Document::Ptr document = snapshot.document(Utils::FilePath::fromString(file));
+ CPlusPlus::Document::Ptr document = snapshot.document(FilePath::fromString(file));
if (document) {
QList<CPlusPlus::Document::Include> includes = document->resolvedIncludes();
if (includes.count() != 1)
diff --git a/src/plugins/nim/Nim.json.in b/src/plugins/nim/Nim.json.in
index 622e460a0b..77f90d74ab 100644
--- a/src/plugins/nim/Nim.json.in
+++ b/src/plugins/nim/Nim.json.in
@@ -14,7 +14,7 @@
],
"Category" : "Other Languages",
"Description" : "Plugin for supporting the Nim programming language.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
"Experimental" : true,
${IDE_PLUGIN_DEPENDENCIES},
diff --git a/src/plugins/nim/settings/nimsettings.cpp b/src/plugins/nim/settings/nimsettings.cpp
index 9df9acd73b..ce5f406f5b 100644
--- a/src/plugins/nim/settings/nimsettings.cpp
+++ b/src/plugins/nim/settings/nimsettings.cpp
@@ -30,7 +30,7 @@ NimSettings::NimSettings()
using namespace Layouting;
return Column {
Group {
- title("Nimsuggest"),
+ title(QString("Nimsuggest")),
Column { nimSuggestPath }
},
st
diff --git a/src/plugins/perforce/Perforce.json.in b/src/plugins/perforce/Perforce.json.in
index 8a8e1e5099..a50fbbfd98 100644
--- a/src/plugins/perforce/Perforce.json.in
+++ b/src/plugins/perforce/Perforce.json.in
@@ -15,7 +15,7 @@
],
"Category" : "Version Control",
"Description" : "Perforce integration.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/perforce/perforcechecker.cpp b/src/plugins/perforce/perforcechecker.cpp
index cf63d9e691..9eb66ef45c 100644
--- a/src/plugins/perforce/perforcechecker.cpp
+++ b/src/plugins/perforce/perforcechecker.cpp
@@ -52,8 +52,7 @@ void PerforceChecker::resetOverrideCursor()
}
void PerforceChecker::start(const FilePath &binary, const FilePath &workingDirectory,
- const QStringList &basicArgs,
- int timeoutMS)
+ const QStringList &basicArgs, int timeoutMS)
{
if (isRunning()) {
emitFailed(QLatin1String("Internal error: process still running"));
@@ -64,13 +63,10 @@ void PerforceChecker::start(const FilePath &binary, const FilePath &workingDirec
return;
}
m_binary = binary;
- QStringList args = basicArgs;
- args << QLatin1String("client") << QLatin1String("-o");
-
if (!workingDirectory.isEmpty())
m_process.setWorkingDirectory(workingDirectory);
- m_process.setCommand({m_binary, args});
+ m_process.setCommand({m_binary, {basicArgs, "client", "-o"}});
m_process.start();
// Timeout handling
m_timeOutMS = timeoutMS;
diff --git a/src/plugins/perforce/perforcesettings.cpp b/src/plugins/perforce/perforcesettings.cpp
index 6f7691c054..65b00a19dc 100644
--- a/src/plugins/perforce/perforcesettings.cpp
+++ b/src/plugins/perforce/perforcesettings.cpp
@@ -125,7 +125,7 @@ PerforceSettings::PerforceSettings()
Group environment {
title(Tr::tr("Environment Variables")),
- customEnv.groupChecker(),
+ groupChecker(customEnv.groupChecker()),
Row { p4Port, p4Client, p4User }
};
diff --git a/src/plugins/perfprofiler/PerfProfiler.json.in b/src/plugins/perfprofiler/PerfProfiler.json.in
index c3a439b1a5..7a0eaf6a08 100644
--- a/src/plugins/perfprofiler/PerfProfiler.json.in
+++ b/src/plugins/perfprofiler/PerfProfiler.json.in
@@ -14,6 +14,6 @@
],
"Category" : "Code Analyzer",
"Description" : "Perf Profiler Plugin.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/perfprofiler/perfprofilerflamegraphview.cpp b/src/plugins/perfprofiler/perfprofilerflamegraphview.cpp
index 076c977888..2d0c451589 100644
--- a/src/plugins/perfprofiler/perfprofilerflamegraphview.cpp
+++ b/src/plugins/perfprofiler/perfprofilerflamegraphview.cpp
@@ -28,7 +28,7 @@ PerfProfilerFlameGraphView::PerfProfilerFlameGraphView(QWidget *parent)
rootContext()->setContextProperty(QStringLiteral("flameGraphModel"), m_model);
setSource(QUrl(QStringLiteral(
"qrc:/qt/qml/QtCreator/PerfProfiler/PerfProfilerFlameGraphView.qml")));
- setClearColor(Utils::creatorTheme()->color(Utils::Theme::Timeline_BackgroundColor1));
+ setClearColor(Utils::creatorColor(Utils::Theme::Timeline_BackgroundColor1));
setResizeMode(QQuickWidget::SizeRootObjectToView);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
diff --git a/src/plugins/perfprofiler/perftimelinemodel.cpp b/src/plugins/perfprofiler/perftimelinemodel.cpp
index fac136024b..904a73fe43 100644
--- a/src/plugins/perfprofiler/perftimelinemodel.cpp
+++ b/src/plugins/perfprofiler/perftimelinemodel.cpp
@@ -86,12 +86,12 @@ QVariantList PerfTimelineModel::labels() const
QString prettyPrintTraceData(const QVariant &data)
{
- switch (data.type()) {
- case QVariant::ULongLong:
+ switch (data.typeId()) {
+ case QMetaType::ULongLong:
return QString::fromLatin1("0x%1").arg(data.toULongLong(), 16, 16, QLatin1Char('0'));
- case QVariant::UInt:
+ case QMetaType::UInt:
return QString::fromLatin1("0x%1").arg(data.toUInt(), 8, 16, QLatin1Char('0'));
- case QVariant::List: {
+ case QMetaType::QVariantList: {
QStringList ret;
for (const QVariant &item : data.toList())
ret.append(prettyPrintTraceData(item));
diff --git a/src/plugins/perfprofiler/perftimelineresourcesrenderpass.cpp b/src/plugins/perfprofiler/perftimelineresourcesrenderpass.cpp
index 8930573f32..8ccada8f4b 100644
--- a/src/plugins/perfprofiler/perftimelineresourcesrenderpass.cpp
+++ b/src/plugins/perfprofiler/perftimelineresourcesrenderpass.cpp
@@ -210,7 +210,7 @@ ResourcesRenderPassState::ResourcesRenderPassState() :
node = new QSGNode;
node->setFlag(QSGNode::OwnedByParent, false);
m_expandedRows.append(node);
- m_material.setColor(Utils::creatorTheme()->color(Utils::Theme::Timeline_HighlightColor));
+ m_material.setColor(Utils::creatorColor(Utils::Theme::Timeline_HighlightColor));
// Disable blending
m_material.setFlag(QSGMaterial::Blending, false);
diff --git a/src/plugins/perfprofiler/perftracepointdialog.cpp b/src/plugins/perfprofiler/perftracepointdialog.cpp
index d726aa7d86..66a6208c1e 100644
--- a/src/plugins/perfprofiler/perftracepointdialog.cpp
+++ b/src/plugins/perfprofiler/perftracepointdialog.cpp
@@ -101,7 +101,7 @@ void PerfTracePointDialog::runScript()
if (elevate != QLatin1String(ELEVATE_METHOD_NA))
m_process->setCommand({m_device->filePath(elevate), {"sh"}});
else
- m_process->setCommand({m_device->filePath("sh"), {}});
+ m_process->setCommand(CommandLine{m_device->filePath("sh")});
connect(m_process.get(), &Process::done, this, &PerfTracePointDialog::handleProcessDone);
m_process->start();
diff --git a/src/plugins/plugins.qbs b/src/plugins/plugins.qbs
index b16970c07c..7293d34d68 100644
--- a/src/plugins/plugins.qbs
+++ b/src/plugins/plugins.qbs
@@ -5,6 +5,7 @@ Project {
references: [
"android/android.qbs",
+ "appstatisticsmonitor/appstatisticsmonitor.qbs",
"autotest/autotest.qbs",
"autotoolsprojectmanager/autotoolsprojectmanager.qbs",
"axivion/axivion.qbs",
@@ -74,6 +75,7 @@ Project {
"qnx/qnx.qbs",
"qmakeprojectmanager/qmakeprojectmanager.qbs",
"qmldesignerbase/qmldesignerbase.qbs",
+ "qtapplicationmanager/qtapplicationmanager.qbs",
"qtsupport/qtsupport.qbs",
"remotelinux/remotelinux.qbs",
"resourceeditor/resourceeditor.qbs",
@@ -94,6 +96,5 @@ Project {
"vcsbase/vcsbase.qbs",
"webassembly/webassembly.qbs",
"welcome/welcome.qbs",
- "qtapplicationmanager/qtapplicationmanager.qbs",
].concat(project.additionalPlugins)
}
diff --git a/src/plugins/projectexplorer/CMakeLists.txt b/src/plugins/projectexplorer/CMakeLists.txt
index b449aababf..0c62d2177a 100644
--- a/src/plugins/projectexplorer/CMakeLists.txt
+++ b/src/plugins/projectexplorer/CMakeLists.txt
@@ -179,6 +179,7 @@ add_qtc_plugin(ProjectExplorer
vcsannotatetaskhandler.cpp vcsannotatetaskhandler.h
waitforstopdialog.cpp waitforstopdialog.h
windebuginterface.cpp windebuginterface.h
+ workspaceproject.h workspaceproject.cpp
xcodebuildparser.cpp xcodebuildparser.h
)
diff --git a/src/plugins/projectexplorer/ProjectExplorer.json.in b/src/plugins/projectexplorer/ProjectExplorer.json.in
index b5f30d4242..9cb6aac8a1 100644
--- a/src/plugins/projectexplorer/ProjectExplorer.json.in
+++ b/src/plugins/projectexplorer/ProjectExplorer.json.in
@@ -14,7 +14,7 @@
],
"Category" : "Core",
"Description" : "ProjectExplorer framework that can be extended with different kind of project types.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
"Arguments" : [
{
"Name" : "-customwizard-verbose",
@@ -37,6 +37,13 @@
" <glob pattern='*.tasks'/>",
" <glob pattern='*.tasks.txt'/>",
" </mime-type>",
+
+ " <mime-type type='text/x-workspace-project'>",
+ " <sub-class-of type='application/json'/>",
+ " <comment>Workspace Qt Creator Project file</comment>",
+ " <glob pattern='project.json' weight='100'/>",
+ " </mime-type>",
+
"</mime-info>"
]
}
diff --git a/src/plugins/projectexplorer/buildaspects.cpp b/src/plugins/projectexplorer/buildaspects.cpp
index 969f0b09bc..82c38c92bc 100644
--- a/src/plugins/projectexplorer/buildaspects.cpp
+++ b/src/plugins/projectexplorer/buildaspects.cpp
@@ -123,7 +123,7 @@ void BuildDirectoryAspect::fromMap(const Store &map)
}
}
-void BuildDirectoryAspect::addToLayout(Layouting::LayoutItem &parent)
+void BuildDirectoryAspect::addToLayout(Layouting::Layout &parent)
{
FilePathAspect::addToLayout(parent);
d->genericProblemSpacer = new QLabel;
diff --git a/src/plugins/projectexplorer/buildaspects.h b/src/plugins/projectexplorer/buildaspects.h
index f19cc39385..73d02b193d 100644
--- a/src/plugins/projectexplorer/buildaspects.h
+++ b/src/plugins/projectexplorer/buildaspects.h
@@ -23,7 +23,7 @@ public:
bool isShadowBuild() const;
void setProblem(const QString &description);
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &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 dd1e077af4..6b60b65921 100644
--- a/src/plugins/projectexplorer/buildconfiguration.cpp
+++ b/src/plugins/projectexplorer/buildconfiguration.cpp
@@ -326,13 +326,13 @@ NamedWidget *BuildConfiguration::createConfigWidget()
}
Layouting::Form form;
+ form.noMargin();
for (BaseAspect *aspect : aspects()) {
if (aspect->isVisible()) {
form.addItem(aspect);
- form.addItem(Layouting::br);
+ form.flush();
}
}
- form.addItem(Layouting::noMargin);
form.attachTo(widget);
return named;
diff --git a/src/plugins/projectexplorer/buildstep.cpp b/src/plugins/projectexplorer/buildstep.cpp
index 70c58eedee..9b28cd08b2 100644
--- a/src/plugins/projectexplorer/buildstep.cpp
+++ b/src/plugins/projectexplorer/buildstep.cpp
@@ -113,11 +113,13 @@ QWidget *BuildStep::doCreateConfigWidget()
QWidget *BuildStep::createConfigWidget()
{
Layouting::Form form;
+ form.noMargin();
for (BaseAspect *aspect : std::as_const(*this)) {
- if (aspect->isVisible())
- form.addItems({aspect, Layouting::br()});
+ if (aspect->isVisible()) {
+ form.addItem(aspect);
+ form.flush();
+ }
}
- form.addItem(Layouting::noMargin);
auto widget = form.emerge();
if (m_addMacroExpander)
diff --git a/src/plugins/projectexplorer/codestylesettingspropertiespage.cpp b/src/plugins/projectexplorer/codestylesettingspropertiespage.cpp
index 0153a6a195..67e1f4820b 100644
--- a/src/plugins/projectexplorer/codestylesettingspropertiespage.cpp
+++ b/src/plugins/projectexplorer/codestylesettingspropertiespage.cpp
@@ -19,6 +19,7 @@
#include <QComboBox>
#include <QLabel>
+#include <QLayout>
#include <QStackedWidget>
using namespace TextEditor;
diff --git a/src/plugins/projectexplorer/customparserconfigdialog.cpp b/src/plugins/projectexplorer/customparserconfigdialog.cpp
index cbfd1adfbc..3793c0de6c 100644
--- a/src/plugins/projectexplorer/customparserconfigdialog.cpp
+++ b/src/plugins/projectexplorer/customparserconfigdialog.cpp
@@ -406,8 +406,8 @@ bool CustomParserConfigDialog::checkPattern(QLineEdit *pattern, const QString &o
QPalette palette;
palette.setColor(QPalette::Text,
- Utils::creatorTheme()->color(rx.isValid() ? Utils::Theme::TextColorNormal
- : Utils::Theme::TextColorError));
+ Utils::creatorColor(rx.isValid() ? Utils::Theme::TextColorNormal
+ : Utils::Theme::TextColorError));
pattern->setPalette(palette);
pattern->setToolTip(rx.isValid() ? QString() : rx.errorString());
@@ -415,7 +415,7 @@ bool CustomParserConfigDialog::checkPattern(QLineEdit *pattern, const QString &o
*match = rx.match(outputText);
if (rx.pattern().isEmpty() || !rx.isValid() || !match->hasMatch()) {
*errorMessage = QString::fromLatin1("<font color=\"%1\">%2 ").arg(
- Utils::creatorTheme()->color(Utils::Theme::TextColorError).name(),
+ Utils::creatorColor(Utils::Theme::TextColorError).name(),
Tr::tr("Not applicable:"));
if (rx.pattern().isEmpty())
*errorMessage += Tr::tr("Pattern is empty.");
diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp
index 2dae1a9b68..b9558f17f1 100644
--- a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp
+++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp
@@ -6,7 +6,6 @@
#include "../projectexplorerconstants.h"
#include "../projectexplorertr.h"
#include "desktopprocesssignaloperation.h"
-#include "processlist.h"
#include <coreplugin/fileutils.h>
@@ -21,7 +20,6 @@
#include <utils/url.h>
#include <QCoreApplication>
-#include <QDateTime>
#ifdef Q_OS_WIN
#include <cstring>
@@ -65,7 +63,7 @@ DesktopDevice::DesktopDevice()
Process process;
process.setTerminalMode(TerminalMode::Detached);
process.setEnvironment(realEnv);
- process.setCommand({*shell, {}});
+ process.setCommand(CommandLine{*shell});
process.setWorkingDirectory(path);
process.start();
diff --git a/src/plugins/projectexplorer/devicesupport/devicesettingspage.cpp b/src/plugins/projectexplorer/devicesupport/devicesettingspage.cpp
index f1e520c4c6..59ead38b9f 100644
--- a/src/plugins/projectexplorer/devicesupport/devicesettingspage.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicesettingspage.cpp
@@ -23,6 +23,7 @@
#include <utils/qtcassert.h>
#include <QComboBox>
+#include <QFormLayout>
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
@@ -91,7 +92,7 @@ private:
QPushButton *m_defaultDeviceButton;
QVBoxLayout *m_buttonsLayout;
QWidget *m_deviceNameEditWidget;
- QFormLayout *m_generalFormLayout;
+ QLayout *m_generalFormLayout;
};
DeviceSettingsWidget::DeviceSettingsWidget()
@@ -338,7 +339,7 @@ void DeviceSettingsWidget::currentDeviceChanged(int index)
return;
}
- Layouting::Column item{Layouting::noMargin()};
+ Layouting::Column item{Layouting::noMargin};
device->settings()->displayName.addToLayout(item);
QWidget *newEdit = item.emerge();
QLayoutItem *oldItem = m_generalFormLayout->replaceWidget(m_deviceNameEditWidget, newEdit);
diff --git a/src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp b/src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp
index 0277fc827a..4e522f24a0 100644
--- a/src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp
@@ -95,10 +95,8 @@ void DeviceTestDialog::handleTestFinished(DeviceTester::TestResult result)
void DeviceTestDialog::addText(const QString &text, Utils::Theme::Color color, bool bold)
{
- Utils::Theme *theme = Utils::creatorTheme();
-
QTextCharFormat format = d->textEdit->currentCharFormat();
- format.setForeground(QBrush(theme->color(color)));
+ format.setForeground(QBrush(creatorColor(color)));
QFont font = format.font();
font.setBold(bold);
format.setFont(font);
diff --git a/src/plugins/projectexplorer/extracompiler.cpp b/src/plugins/projectexplorer/extracompiler.cpp
index 932e5c1bd7..b7568d4275 100644
--- a/src/plugins/projectexplorer/extracompiler.cpp
+++ b/src/plugins/projectexplorer/extracompiler.cpp
@@ -11,8 +11,6 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/idocument.h>
-#include <extensionsystem/pluginmanager.h>
-
#include <solutions/tasking/tasktreerunner.h>
#include <utils/async.h>
@@ -326,7 +324,6 @@ GroupItem ProcessExtraCompiler::taskItemImpl(const ContentProvider &provider)
const auto onSetup = [this, provider](Async<FileNameToContentsHash> &async) {
async.setThreadPool(extraCompilerThreadPool());
// 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());
};
@@ -382,7 +379,7 @@ void ProcessExtraCompiler::runInThread(QPromise<FileNameToContentsHash> &promise
process.setEnvironment(env);
if (!workDir.isEmpty())
process.setWorkingDirectory(workDir);
- process.setCommand({ cmd, args });
+ process.setCommand({cmd, args});
process.setWriteData(sourceContents);
process.start();
if (!process.waitForStarted())
diff --git a/src/plugins/projectexplorer/gccparser.cpp b/src/plugins/projectexplorer/gccparser.cpp
index 4d21d4b8d9..48c34cd591 100644
--- a/src/plugins/projectexplorer/gccparser.cpp
+++ b/src/plugins/projectexplorer/gccparser.cpp
@@ -8,37 +8,117 @@
#include <utils/qtcassert.h>
-#include <numeric>
+#include <QLoggingCategory>
+
+#include <optional>
using namespace Utils;
namespace ProjectExplorer {
-// opt. drive letter + filename: (2 brackets)
-static const char FILE_PATTERN[] = "(<command[ -]line>|([A-Za-z]:)?[^:]+):";
+static Q_LOGGING_CATEGORY(gccParserLog, "qtc.gccparser", QtWarningMsg)
+
+static const QString &filePattern()
+{
+ static const QString pattern = [] {
+ const QString pseudoFile = "<command[ -]line>";
+ const QString driveSpec = "[A-Za-z]:";
+ const QString realFile = QString::fromLatin1("(?:%1)?[^:]+").arg(driveSpec);
+ return QString::fromLatin1("(?<file>%1|%2):").arg(pseudoFile, realFile);
+ }();
+ return pattern;
+}
+
static const char COMMAND_PATTERN[] = "^(.*?[\\\\/])?([a-z0-9]+-[a-z0-9]+-[a-z0-9]+-)?(gcc|g\\+\\+)(-[0-9.]+)?(\\.exe)?: ";
+namespace {
+class MainRegEx
+{
+public:
+ struct Data {
+ QString rawFilePath;
+ QString description;
+ Task::TaskType type = Task::Unknown;
+ int line = -1;
+ int column = -1;
+ int fileOffset = -1;
+ };
+
+ static std::optional<Data> parse(const QString &line)
+ {
+ qCDebug(gccParserLog) << "checking regex" << theRegEx().pattern();
+ const QRegularExpressionMatch match = theRegEx().match(line);
+ if (!match.hasMatch())
+ return {};
+
+ Data data;
+ data.rawFilePath = match.captured("file");
+ data.fileOffset = match.capturedStart("file");
+ data.line = match.captured("line").toInt();
+ data.column = match.captured("column").toInt();
+ data.description = match.captured("description");
+ if (match.captured("type") == QLatin1String("warning")) {
+ data.type = Task::Warning;
+ } else if (match.captured("type") == QLatin1String("error") ||
+ data.description.startsWith(QLatin1String("undefined reference to")) ||
+ data.description.startsWith(QLatin1String("multiple definition of"))) {
+ data.type = Task::Error;
+ }
+
+ // Prepend "#warning" or "#error" if that triggered the match on (warning|error)
+ // We want those to show how the warning was triggered
+ if (match.captured("fullTypeString").startsWith(QLatin1Char('#')))
+ data.description.prepend(match.captured("fullTypeString"));
+
+ return data;
+ }
+
+private:
+ static const QRegularExpression &theRegEx()
+ {
+ static const QRegularExpression re = [] {
+ const QRegularExpression re(constructPattern());
+ QTC_CHECK(re.isValid());
+ return re;
+ }();
+ return re;
+ }
+
+ static QString constructPattern()
+ {
+ const QString type = "(?<type>warning|error|note)";
+ const QString typePrefix = "(?:fatal |#)";
+ const QString fullTypeString
+ = QString::fromLatin1("(?<fullTypeString>%1?%2:?\\s)").arg(typePrefix, type);
+ const QString lineAndOptionalColumn = "(?:(?<line>\\d+)(?::(?<column>\\d+))?)";
+ const QString binaryLocation = "\\(.*\\)"; // E.g. "(.text+0x40)"
+ const QString fullLocation = QString::fromLatin1("%1(?:%2|%3)")
+ .arg(filePattern(), lineAndOptionalColumn, binaryLocation);
+ const QString description = "(?<description>[^\\s].+)";
+
+ return QString::fromLatin1("^%1:\\s+%2?%3$").arg(fullLocation, fullTypeString, description);
+ }
+};
+} // namespace
+
GccParser::GccParser()
{
setObjectName(QLatin1String("GCCParser"));
- m_regExp.setPattern(QLatin1Char('^') + QLatin1String(FILE_PATTERN)
- + QLatin1String("(?:(?:(\\d+):(?:(\\d+):)?)|\\(.*\\):)\\s+((fatal |#)?(warning|error|note):?\\s)?([^\\s].+)$"));
- QTC_CHECK(m_regExp.isValid());
- m_regExpScope.setPattern(QLatin1Char('^') + FILE_PATTERN
+ m_regExpScope.setPattern(QLatin1Char('^') + filePattern()
+ "(?:(\\d+):)?(\\d+:)?\\s+((?:In .*(?:function|constructor) .*|At global scope|At top level):)$");
QTC_CHECK(m_regExpScope.isValid());
- m_regExpIncluded.setPattern(QString::fromLatin1("\\bfrom\\s") + QLatin1String(FILE_PATTERN)
+ m_regExpIncluded.setPattern(QString::fromLatin1("\\bfrom\\s") + filePattern()
+ QLatin1String("(\\d+)(:\\d+)?[,:]?$"));
QTC_CHECK(m_regExpIncluded.isValid());
m_regExpInlined.setPattern(QString::fromLatin1("\\binlined from\\s.* at ")
- + FILE_PATTERN + "(\\d+)(:\\d+)?[,:]?$");
+ + filePattern() + "(\\d+)(:\\d+)?[,:]?$");
QTC_CHECK(m_regExpInlined.isValid());
m_regExpCc1plus.setPattern(QLatin1Char('^') + "cc1plus.*(error|warning): ((?:"
- + FILE_PATTERN + " No such file or directory)?.*)");
+ + filePattern() + " No such file or directory)?.*)");
QTC_CHECK(m_regExpCc1plus.isValid());
// optional path with trailing slash
@@ -60,82 +140,36 @@ QList<OutputLineParser *> GccParser::gccParserSuite()
return {new GccParser, new Internal::LldParser, new LdParser};
}
-void GccParser::createOrAmendTask(
- Task::TaskType type,
- const QString &description,
- const QString &originalLine,
- bool forceAmend,
- const FilePath &file,
- int line,
- int column,
- const LinkSpecs &linkSpecs
- )
+void GccParser::gccCreateOrAmendTask(
+ Task::TaskType type,
+ const QString &description,
+ const QString &originalLine,
+ bool forceAmend,
+ const Utils::FilePath &file,
+ int line,
+ int column,
+ const LinkSpecs &linkSpecs)
{
- const bool amend = !m_currentTask.isNull() && (forceAmend || isContinuation(originalLine));
- if (!amend) {
- flush();
- m_currentTask = CompileTask(type, description, file, line, column);
- m_currentTask.details.append(originalLine);
- m_linkSpecs = linkSpecs;
- m_lines = 1;
- return;
- }
-
- LinkSpecs adaptedLinkSpecs = linkSpecs;
- const int offset = std::accumulate(m_currentTask.details.cbegin(), m_currentTask.details.cend(),
- 0, [](int total, const QString &line) { return total + line.length() + 1;});
- for (LinkSpec &ls : adaptedLinkSpecs)
- ls.startPos += offset;
- m_linkSpecs << adaptedLinkSpecs;
- m_currentTask.details.append(originalLine);
-
- // Check whether the new line is more relevant than the previous ones.
- if ((m_currentTask.type != Task::Error && type == Task::Error)
- || (m_currentTask.type == Task::Unknown && type != Task::Unknown)) {
- m_currentTask.type = type;
- m_currentTask.summary = description;
- if (!file.isEmpty() && !m_requiredFromHereFound) {
- m_currentTask.setFile(file);
- m_currentTask.line = line;
- m_currentTask.column = column;
- }
- }
+ createOrAmendTask(type, description, originalLine, forceAmend, file, line, column, linkSpecs);
// If a "required from here" line is present, it is almost always the cause of the problem,
// so that's where we should go when the issue is double-clicked.
if ((originalLine.endsWith("required from here") || originalLine.endsWith("requested here")
- || originalLine.endsWith("note: here")) && !file.isEmpty() && line > 0) {
- m_requiredFromHereFound = true;
- m_currentTask.setFile(file);
- m_currentTask.line = line;
- m_currentTask.column = column;
+ || originalLine.endsWith("note: here"))
+ && !file.isEmpty() && line > 0) {
+ fixTargetLink();
+ currentTask().setFile(file);
+ currentTask().line = line;
+ currentTask().column = column;
}
-
- ++m_lines;
-}
-
-void GccParser::flush()
-{
- if (m_currentTask.isNull())
- return;
-
- // If there is only one line of details, then it is the line that we generated
- // the summary from. Remove it, because it does not add any information.
- if (m_currentTask.details.count() == 1)
- m_currentTask.details.clear();
-
- setDetailsFormat(m_currentTask, m_linkSpecs);
- Task t = m_currentTask;
- m_currentTask.clear();
- m_linkSpecs.clear();
- scheduleTask(t, m_lines, 1);
- m_lines = 0;
- m_requiredFromHereFound = false;
}
OutputLineParser::Result GccParser::handleLine(const QString &line, OutputFormat type)
{
+ qCDebug(gccParserLog) << "incoming line" << line;
+
if (type == StdOutFormat) {
+ qCDebug(gccParserLog) << "not parsing stdout";
flush();
return Status::NotHandled;
}
@@ -150,37 +184,43 @@ OutputLineParser::Result GccParser::handleLine(const QString &line, OutputFormat
// Handle misc issues:
if (lne.startsWith(QLatin1String("ERROR:")) || lne == QLatin1String("* cpp failed")) {
- createOrAmendTask(Task::Error, lne, lne);
+ gccCreateOrAmendTask(Task::Error, lne, lne);
return Status::InProgress;
}
+ qCDebug(gccParserLog) << "checking regex" << m_regExpGccNames.pattern();
QRegularExpressionMatch match = m_regExpGccNames.match(lne);
if (match.hasMatch()) {
QString description = lne.mid(match.capturedLength());
Task::TaskType type = Task::Error;
if (description.startsWith(QLatin1String("warning: "))) {
type = Task::Warning;
- description = description.mid(9);
+ description = description.mid(8);
} else if (description.startsWith(QLatin1String("fatal: "))) {
- description = description.mid(7);
+ description = description.mid(6);
}
- createOrAmendTask(type, description, lne);
+ gccCreateOrAmendTask(type, description, lne);
return Status::InProgress;
}
+ qCDebug(gccParserLog) << "checking regex" << m_regExpIncluded.pattern();
match = m_regExpIncluded.match(lne);
- if (!match.hasMatch())
+ if (!match.hasMatch()) {
+ qCDebug(gccParserLog) << "checking regex" << m_regExpInlined.pattern();
match = m_regExpInlined.match(lne);
+ }
if (match.hasMatch()) {
- const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(match.captured(1)));
- const int lineNo = match.captured(3).toInt();
- const int column = match.captured(4).toInt();
+ const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(match.captured("file")));
+ const int lineNo = match.captured(2).toInt();
+ const int column = match.captured(3).toInt();
LinkSpecs linkSpecs;
- addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match, 1);
- createOrAmendTask(Task::Unknown, lne.trimmed(), lne, false, filePath, lineNo, column, linkSpecs);
+ addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match, "file");
+ gccCreateOrAmendTask(
+ Task::Unknown, lne.trimmed(), lne, false, filePath, lineNo, column, linkSpecs);
return {Status::InProgress, linkSpecs};
}
+ qCDebug(gccParserLog) << "checking regex" << m_regExpCc1plus.pattern();
match = m_regExpCc1plus.match(lne);
if (match.hasMatch()) {
const Task::TaskType type = match.captured(1) == "error" ? Task::Error : Task::Warning;
@@ -188,64 +228,53 @@ OutputLineParser::Result GccParser::handleLine(const QString &line, OutputFormat
LinkSpecs linkSpecs;
if (!filePath.isEmpty())
addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, -1, match, 3);
- createOrAmendTask(type, match.captured(2), lne, false, filePath, -1, 0, linkSpecs);
+ gccCreateOrAmendTask(type, match.captured(2), lne, false, filePath, -1, 0, linkSpecs);
flush();
return {Status::Done, linkSpecs};
}
- match = m_regExp.match(lne);
- if (match.hasMatch()) {
- int lineno = match.captured(3).toInt();
- int column = match.captured(4).toInt();
- Task::TaskType type = Task::Unknown;
- QString description = match.captured(8);
- if (match.captured(7) == QLatin1String("warning"))
- type = Task::Warning;
- else if (match.captured(7) == QLatin1String("error") ||
- description.startsWith(QLatin1String("undefined reference to")) ||
- description.startsWith(QLatin1String("multiple definition of")))
- type = Task::Error;
- // Prepend "#warning" or "#error" if that triggered the match on (warning|error)
- // We want those to show how the warning was triggered
- if (match.captured(5).startsWith(QLatin1Char('#')))
- description = match.captured(5) + description;
-
- const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(match.captured(1)));
+ if (const auto data = MainRegEx::parse(lne)) {
+ const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(data->rawFilePath));
LinkSpecs linkSpecs;
- addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineno, match, 1);
- createOrAmendTask(type, description, lne, false, filePath, lineno, column, linkSpecs);
+ addLinkSpecForAbsoluteFilePath(
+ linkSpecs, filePath, data->line, data->fileOffset, data->rawFilePath.size());
+ gccCreateOrAmendTask(
+ data->type, data->description, lne, false, filePath, data->line, data->column, linkSpecs);
return {Status::InProgress, linkSpecs};
}
+ qCDebug(gccParserLog) << "checking regex" << m_regExpScope.pattern();
match = m_regExpScope.match(lne);
if (match.hasMatch()) {
- const int lineno = match.captured(3).toInt();
- const int column = match.captured(4).toInt();
- const QString description = match.captured(5);
- const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(match.captured(1)));
+ const int lineno = match.captured(2).toInt();
+ const int column = match.captured(3).toInt();
+ const QString description = match.captured(4);
+ const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(match.captured("file")));
LinkSpecs linkSpecs;
- addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineno, match, 1);
- createOrAmendTask(Task::Unknown, description, lne, false, filePath, lineno, column, linkSpecs);
+ addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineno, match, "file");
+ gccCreateOrAmendTask(
+ Task::Unknown, description, lne, false, filePath, lineno, column, linkSpecs);
return {Status::InProgress, linkSpecs};
}
- if ((lne.startsWith(' ') && !m_currentTask.isNull()) || isContinuation(lne)) {
- createOrAmendTask(Task::Unknown, lne, lne, true);
+ if ((lne.startsWith(' ') && !currentTask().isNull()) || isContinuation(lne)) {
+ gccCreateOrAmendTask(Task::Unknown, lne, lne, true);
return Status::InProgress;
}
+ qCDebug(gccParserLog) << "no match";
flush();
return Status::NotHandled;
}
bool GccParser::isContinuation(const QString &newLine) const
{
- return !m_currentTask.isNull()
- && (m_currentTask.details.last().endsWith(':')
- || m_currentTask.details.last().endsWith(',')
- || m_currentTask.details.last().contains(" required from ")
- || newLine.contains("within this context")
- || newLine.contains("note:"));
+ return !currentTask().isNull()
+ && (currentTask().details.last().endsWith(':')
+ || currentTask().details.last().endsWith(',')
+ || currentTask().details.last().contains(" required from ")
+ || newLine.contains("within this context")
+ || newLine.contains("note:"));
}
} // ProjectExplorer
@@ -993,10 +1022,11 @@ void ProjectExplorerTest::testGccOutputParsers_data()
<< OutputParserTester::STDERR
<< QString() << QString()
<< Tasks({CompileTask(Task::Error,
- "Undefined symbols for architecture x86_64:\n"
- " \"SvgLayoutTest()\", referenced from:\n"
- " _main in main.cpp.o",
- "main.cpp.o")})
+ "Undefined symbols for architecture x86_64:\n"
+ "Undefined symbols for architecture x86_64:\n"
+ " \"SvgLayoutTest()\", referenced from:\n"
+ " _main in main.cpp.o",
+ "main.cpp.o")})
<< QString();
QTest::newRow("ld: undefined member function reference")
diff --git a/src/plugins/projectexplorer/gccparser.h b/src/plugins/projectexplorer/gccparser.h
index f4fe67f1f3..236b4d0570 100644
--- a/src/plugins/projectexplorer/gccparser.h
+++ b/src/plugins/projectexplorer/gccparser.h
@@ -11,7 +11,7 @@
namespace ProjectExplorer {
-class PROJECTEXPLORER_EXPORT GccParser : public ProjectExplorer::OutputTaskParser
+class PROJECTEXPLORER_EXPORT GccParser : public OutputTaskParser
{
Q_OBJECT
@@ -23,34 +23,26 @@ public:
static QList<OutputLineParser *> gccParserSuite();
protected:
- void createOrAmendTask(
- Task::TaskType type,
- const QString &description,
- const QString &originalLine,
- bool forceAmend = false,
- const Utils::FilePath &file = {},
- int line = -1,
- int column = 0,
- const LinkSpecs &linkSpecs = {}
- );
- void flush() override;
+ void gccCreateOrAmendTask(
+ Task::TaskType type,
+ const QString &description,
+ const QString &originalLine,
+ bool forceAmend = false,
+ const Utils::FilePath &file = {},
+ int line = -1,
+ int column = 0,
+ const LinkSpecs &linkSpecs = {});
private:
Result handleLine(const QString &line, Utils::OutputFormat type) override;
- bool isContinuation(const QString &newLine) const;
+ bool isContinuation(const QString &newLine) const override;
- QRegularExpression m_regExp;
QRegularExpression m_regExpScope;
QRegularExpression m_regExpIncluded;
QRegularExpression m_regExpInlined;
QRegularExpression m_regExpGccNames;
QRegularExpression m_regExpCc1plus;
-
- Task m_currentTask;
- LinkSpecs m_linkSpecs;
- int m_lines = 0;
- bool m_requiredFromHereFound = false;
};
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/ioutputparser.cpp b/src/plugins/projectexplorer/ioutputparser.cpp
index 5c646d0bcc..65fd3bea2b 100644
--- a/src/plugins/projectexplorer/ioutputparser.cpp
+++ b/src/plugins/projectexplorer/ioutputparser.cpp
@@ -14,6 +14,7 @@
#include <QPlainTextEdit>
+#include <numeric>
/*!
\class ProjectExplorer::OutputTaskParser
@@ -57,6 +58,10 @@ class OutputTaskParser::Private
{
public:
QList<TaskInfo> scheduledTasks;
+ Task currentTask;
+ LinkSpecs linkSpecs;
+ int lineCount = 0;
+ bool targetLinkFixed = false;
};
OutputTaskParser::OutputTaskParser() : d(new Private) { }
@@ -95,6 +100,11 @@ void OutputTaskParser::setDetailsFormat(Task &task, const LinkSpecs &linkSpecs)
}
}
+void OutputTaskParser::fixTargetLink()
+{
+ d->targetLinkFixed = true;
+}
+
void OutputTaskParser::runPostPrintActions(QPlainTextEdit *edit)
{
int offset = 0;
@@ -110,4 +120,91 @@ void OutputTaskParser::runPostPrintActions(QPlainTextEdit *edit)
d->scheduledTasks.clear();
}
+void OutputTaskParser::createOrAmendTask(
+ Task::TaskType type,
+ const QString &description,
+ const QString &originalLine,
+ bool forceAmend,
+ const Utils::FilePath &file,
+ int line,
+ int column,
+ const LinkSpecs &linkSpecs)
+{
+ const bool amend = !d->currentTask.isNull() && (forceAmend || isContinuation(originalLine));
+ if (!amend) {
+ flush();
+ d->currentTask = CompileTask(type, description, file, line, column);
+ d->currentTask.details.append(originalLine);
+ d->linkSpecs = linkSpecs;
+ d->lineCount = 1;
+ return;
+ }
+
+ LinkSpecs adaptedLinkSpecs = linkSpecs;
+ const int offset = std::accumulate(
+ d->currentTask.details.cbegin(),
+ d->currentTask.details.cend(),
+ 0,
+ [](int total, const QString &line) { return total + line.length() + 1; });
+ for (LinkSpec &ls : adaptedLinkSpecs)
+ ls.startPos += offset;
+ d->linkSpecs << adaptedLinkSpecs;
+ d->currentTask.details.append(originalLine);
+
+ // Check whether the new line is more relevant than the previous ones.
+ if ((d->currentTask.type != Task::Error && type == Task::Error)
+ || (d->currentTask.type == Task::Unknown && type != Task::Unknown)) {
+ d->currentTask.type = type;
+ d->currentTask.summary = description;
+ if (!file.isEmpty() && !d->targetLinkFixed) {
+ d->currentTask.setFile(file);
+ d->currentTask.line = line;
+ d->currentTask.column = column;
+ }
+ }
+
+ ++d->lineCount;
+}
+
+void OutputTaskParser::setCurrentTask(const Task &task)
+{
+ flush();
+ d->currentTask = task;
+ d->lineCount = 1;
+}
+
+Task &OutputTaskParser::currentTask()
+{
+ return d->currentTask;
+}
+
+const Task &OutputTaskParser::currentTask() const
+{
+ return d->currentTask;
+}
+
+bool OutputTaskParser::isContinuation(const QString &line) const
+{
+ Q_UNUSED(line)
+ return false;
+}
+
+void OutputTaskParser::flush()
+{
+ if (d->currentTask.isNull())
+ return;
+
+ // If there is only one line of details, then it is the line that we generated
+ // the summary from. Remove it, because it does not add any information.
+ if (d->currentTask.details.count() == 1)
+ d->currentTask.details.clear();
+
+ setDetailsFormat(d->currentTask, d->linkSpecs);
+ Task t = d->currentTask;
+ d->currentTask.clear();
+ d->linkSpecs.clear();
+ scheduleTask(t, d->lineCount, 1);
+ d->lineCount = 0;
+ d->targetLinkFixed = false;
+}
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/ioutputparser.h b/src/plugins/projectexplorer/ioutputparser.h
index d15257bf6f..de2ee9ebfc 100644
--- a/src/plugins/projectexplorer/ioutputparser.h
+++ b/src/plugins/projectexplorer/ioutputparser.h
@@ -8,8 +8,6 @@
#include <utils/outputformatter.h>
-#include <functional>
-
namespace ProjectExplorer {
class Task;
@@ -31,10 +29,28 @@ public:
const QList<TaskInfo> taskInfo() const;
protected:
+ void flush() override;
+
void scheduleTask(const Task &task, int outputLines, int skippedLines = 0);
void setDetailsFormat(Task &task, const LinkSpecs &linkSpecs = {});
+ void fixTargetLink();
+ void createOrAmendTask(
+ Task::TaskType type,
+ const QString &description,
+ const QString &originalLine,
+ bool forceAmend = false,
+ const Utils::FilePath &file = {},
+ int line = -1,
+ int column = 0,
+ const LinkSpecs &linkSpecs = {});
+ void setCurrentTask(const Task &task);
+
+ Task &currentTask();
+ const Task &currentTask() const;
private:
+ virtual bool isContinuation(const QString &line) const;
+
void runPostPrintActions(QPlainTextEdit *edit) override;
class Private;
diff --git a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp
index 49a0ed8957..9c55d5345c 100644
--- a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp
+++ b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp
@@ -166,7 +166,7 @@ QVariant JsonFieldPage::Field::toSettings() const
JsonFieldPage::Field *JsonFieldPage::Field::parse(const QVariant &input, QString *errorMessage)
{
- if (input.typeId() != QVariant::Map) {
+ if (input.typeId() != QMetaType::QVariantMap) {
*errorMessage = Tr::tr("Field is not an object.");
return nullptr;
}
@@ -409,7 +409,7 @@ QDebug &operator<<(QDebug &debug, const JsonFieldPage::Field &field)
bool LabelField::parseData(const QVariant &data, QString *errorMessage)
{
- if (data.typeId() != QVariant::Map) {
+ if (data.typeId() != QMetaType::QVariantMap) {
*errorMessage = Tr::tr("Label (\"%1\") data is not an object.").arg(name());
return false;
}
@@ -447,7 +447,7 @@ bool SpacerField::parseData(const QVariant &data, QString *errorMessage)
if (data.isNull())
return true;
- if (data.typeId() != QVariant::Map) {
+ if (data.typeId() != QMetaType::QVariantMap) {
*errorMessage = Tr::tr("Spacer (\"%1\") data is not an object.").arg(name());
return false;
}
@@ -492,7 +492,7 @@ bool LineEditField::parseData(const QVariant &data, QString *errorMessage)
if (data.isNull())
return true;
- if (data.typeId() != QVariant::Map) {
+ if (data.typeId() != QMetaType::QVariantMap) {
*errorMessage = Tr::tr("LineEdit (\"%1\") data is not an object.").arg(name());
return false;
}
@@ -689,7 +689,7 @@ bool TextEditField::parseData(const QVariant &data, QString *errorMessage)
if (data.isNull())
return true;
- if (data.typeId() != QVariant::Map) {
+ if (data.typeId() != QMetaType::QVariantMap) {
*errorMessage = Tr::tr("TextEdit (\"%1\") data is not an object.")
.arg(name());
return false;
@@ -772,7 +772,7 @@ bool PathChooserField::parseData(const QVariant &data, QString *errorMessage)
if (data.isNull())
return true;
- if (data.typeId() != QVariant::Map) {
+ if (data.typeId() != QMetaType::QVariantMap) {
*errorMessage = Tr::tr("PathChooser data is not an object.");
return false;
}
@@ -877,7 +877,7 @@ bool CheckBoxField::parseData(const QVariant &data, QString *errorMessage)
if (data.isNull())
return true;
- if (data.typeId() != QVariant::Map) {
+ if (data.typeId() != QMetaType::QVariantMap) {
*errorMessage = Tr::tr("CheckBox (\"%1\") data is not an object.").arg(name());
return false;
}
@@ -966,12 +966,12 @@ QVariant CheckBoxField::toSettings() const
std::unique_ptr<QStandardItem> createStandardItemFromListItem(const QVariant &item, QString *errorMessage)
{
- if (item.typeId() == QVariant::List) {
+ if (item.typeId() == QMetaType::QVariantList) {
*errorMessage = Tr::tr("No JSON lists allowed inside List items.");
return {};
}
auto standardItem = std::make_unique<QStandardItem>();
- if (item.typeId() == QVariant::Map) {
+ if (item.typeId() == QMetaType::QVariantMap) {
QVariantMap tmp = item.toMap();
const QString key = JsonWizardFactory::localizedString(consumeValue(tmp, "trKey", QString()).toString());
const QVariant value = consumeValue(tmp, "value", key);
@@ -1001,7 +1001,7 @@ ListField::~ListField() = default;
bool ListField::parseData(const QVariant &data, QString *errorMessage)
{
- if (data.typeId() != QVariant::Map) {
+ if (data.typeId() != QMetaType::QVariantMap) {
*errorMessage = Tr::tr("%1 (\"%2\") data is not an object.").arg(type(), name());
return false;
}
@@ -1027,7 +1027,7 @@ bool ListField::parseData(const QVariant &data, QString *errorMessage)
*errorMessage = Tr::tr("%1 (\"%2\") \"items\" missing.").arg(type(), name());
return false;
}
- if (value.typeId() != QVariant::List) {
+ if (value.typeId() != QMetaType::QVariantList) {
*errorMessage = Tr::tr("%1 (\"%2\") \"items\" is not a JSON list.").arg(type(), name());
return false;
}
@@ -1315,7 +1315,7 @@ JsonFieldPage::JsonFieldPage(MacroExpander *expander, QWidget *parent) :
vLayout->addLayout(m_formLayout);
m_errorLabel->setVisible(false);
QPalette palette = m_errorLabel->palette();
- palette.setColor(QPalette::WindowText, creatorTheme()->color(Theme::TextColorError));
+ palette.setColor(QPalette::WindowText, creatorColor(Theme::TextColorError));
m_errorLabel->setPalette(palette);
vLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
vLayout->addWidget(m_errorLabel);
diff --git a/src/plugins/projectexplorer/jsonwizard/jsonkitspage.cpp b/src/plugins/projectexplorer/jsonwizard/jsonkitspage.cpp
index 930d8d22f4..6e76633daf 100644
--- a/src/plugins/projectexplorer/jsonwizard/jsonkitspage.cpp
+++ b/src/plugins/projectexplorer/jsonwizard/jsonkitspage.cpp
@@ -124,7 +124,7 @@ QVector<JsonKitsPage::ConditionalFeature> JsonKitsPage::parseFeatures(const QVar
if (data.isNull())
return result;
- if (data.type() != QVariant::List) {
+ if (data.typeId() != QMetaType::QVariantList) {
if (errorMessage)
*errorMessage = Tr::tr("Feature list is set and not of type list.");
return result;
@@ -132,9 +132,9 @@ QVector<JsonKitsPage::ConditionalFeature> JsonKitsPage::parseFeatures(const QVar
const QList<QVariant> elements = data.toList();
for (const QVariant &element : elements) {
- if (element.type() == QVariant::String) {
+ if (element.typeId() == QMetaType::QString) {
result.append({ element.toString(), QVariant(true) });
- } else if (element.type() == QVariant::Map) {
+ } else if (element.typeId() == QMetaType::QVariantMap) {
const QVariantMap obj = element.toMap();
const QString feature = obj.value(QLatin1String(KEY_FEATURE)).toString();
if (feature.isEmpty()) {
diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp
index 83b8b81bef..3d846604ba 100644
--- a/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp
+++ b/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp
@@ -219,14 +219,14 @@ QString JsonWizard::stringValue(const QString &n) const
if (!v.isValid())
return {};
- if (v.typeId() == QVariant::String) {
+ if (v.typeId() == QMetaType::QString) {
QString tmp = m_expander.expand(v.toString());
if (tmp.isEmpty())
tmp = QString::fromLatin1(""); // Make sure isNull() is *not* true.
return tmp;
}
- if (v.typeId() == QVariant::StringList)
+ if (v.typeId() == QMetaType::QStringList)
return stringListToArrayString(v.toStringList(), &m_expander);
return v.toString();
@@ -277,7 +277,7 @@ QVariant JsonWizard::value(const QString &n) const
bool JsonWizard::boolFromVariant(const QVariant &v, MacroExpander *expander)
{
- if (v.typeId() == QVariant::String) {
+ if (v.typeId() == QMetaType::QString) {
const QString tmp = expander->expand(v.toString());
return !(tmp.isEmpty() || tmp == QLatin1String("false"));
}
@@ -419,7 +419,7 @@ void JsonWizard::handleError(const QString &message)
QString JsonWizard::stringify(const QVariant &v) const
{
- if (v.typeId() == QVariant::StringList)
+ if (v.typeId() == QMetaType::QStringList)
return stringListToArrayString(v.toStringList(), &m_expander);
return Wizard::stringify(v);
}
diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp
index 9c00a32329..4e8d496416 100644
--- a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp
+++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp
@@ -147,7 +147,7 @@ static JsonWizardFactory::Generator parseGenerator(const QVariant &value, QStrin
{
JsonWizardFactory::Generator gen;
- if (value.typeId() != QVariant::Map) {
+ if (value.typeId() != QMetaType::QVariantMap) {
*errorMessage = Tr::tr("Generator is not a object.");
return gen;
}
@@ -308,8 +308,8 @@ QVariant JsonWizardFactory::getDataValue(const QLatin1String &key, const QVarian
{
QVariant retVal = {};
- if ((valueSet.contains(key) && valueSet.value(key).typeId() == QVariant::Map) ||
- (defaultValueSet.contains(key) && defaultValueSet.value(key).typeId() == QVariant::Map)) {
+ if ((valueSet.contains(key) && valueSet.value(key).typeId() == QMetaType::QVariantMap) ||
+ (defaultValueSet.contains(key) && defaultValueSet.value(key).typeId() == QMetaType::QVariantMap)) {
retVal = mergeDataValueMaps(valueSet.value(key), defaultValueSet.value(key));
} else {
QVariant defaultValue = defaultValueSet.value(key, notExistValue);
@@ -335,7 +335,7 @@ std::pair<int, QStringList> JsonWizardFactory::screenSizeInfoFromPage(const QStr
return {};
const QVariant data = it->data;
- if (data.typeId() != QVariant::List)
+ if (data.typeId() != QMetaType::QVariantList)
return {};
const QVariant screenFactorField = Utils::findOrDefault(data.toList(),
@@ -344,11 +344,11 @@ std::pair<int, QStringList> JsonWizardFactory::screenSizeInfoFromPage(const QStr
return "ScreenFactor" == m["name"];
});
- if (screenFactorField.typeId() != QVariant::Map)
+ if (screenFactorField.typeId() != QMetaType::QVariantMap)
return {};
const QVariant screenFactorData = screenFactorField.toMap()["data"];
- if (screenFactorData.typeId() != QVariant::Map)
+ if (screenFactorData.typeId() != QMetaType::QVariantMap)
return {};
const QVariantMap screenFactorDataMap = screenFactorData.toMap();
@@ -376,7 +376,7 @@ JsonWizardFactory::Page JsonWizardFactory::parsePage(const QVariant &value, QStr
{
JsonWizardFactory::Page p;
- if (value.typeId() != QVariant::Map) {
+ if (value.typeId() != QMetaType::QVariantMap) {
*errorMessage = Tr::tr("Page is not an object.");
return p;
}
@@ -423,9 +423,9 @@ JsonWizardFactory::Page JsonWizardFactory::parsePage(const QVariant &value, QStr
if (specifiedSubData.isNull())
subData = defaultSubData;
- else if (specifiedSubData.typeId() == QVariant::Map)
+ else if (specifiedSubData.typeId() == QMetaType::QVariantMap)
subData = mergeDataValueMaps(specifiedSubData.toMap(), defaultSubData.toMap());
- else if (specifiedSubData.typeId() == QVariant::List)
+ else if (specifiedSubData.typeId() == QMetaType::QVariantList)
subData = specifiedSubData;
if (!factory->validateData(typeId, subData, errorMessage))
@@ -655,6 +655,7 @@ Wizard *JsonWizardFactory::runWizardImpl(const FilePath &path, QWidget *parent,
wizard->setValue(QStringLiteral("Features"), Id::toStringList(availableFeatures(platform)));
wizard->setValue(QStringLiteral("Plugins"), Id::toStringList(pluginFeatures()));
+ wizard->setValue(QStringLiteral("SupportedProjectTypes"), Id::toStringList(supportedProjectTypes()));
// Add data to wizard:
for (auto i = variables.constBegin(); i != variables.constEnd(); ++i)
@@ -741,9 +742,9 @@ QList<QVariant> JsonWizardFactory::objectOrList(const QVariant &data, QString *e
QList<QVariant> result;
if (data.isNull())
*errorMessage = Tr::tr("key not found.");
- else if (data.typeId() == QVariant::Map)
+ else if (data.typeId() == QMetaType::QVariantMap)
result.append(data);
- else if (data.typeId() == QVariant::List)
+ else if (data.typeId() == QMetaType::QVariantList)
result = data.toList();
else
*errorMessage = Tr::tr("Expected an object or a list.");
@@ -754,7 +755,7 @@ QString JsonWizardFactory::localizedString(const QVariant &value)
{
if (value.isNull())
return {};
- if (value.typeId() == QVariant::Map) {
+ if (value.typeId() == QMetaType::QVariantMap) {
QVariantMap tmp = value.toMap();
const QString locale = languageSetting().toLower();
QStringList locales;
diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardfilegenerator.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardfilegenerator.cpp
index c7409fbc46..c6f6900d04 100644
--- a/src/plugins/projectexplorer/jsonwizard/jsonwizardfilegenerator.cpp
+++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardfilegenerator.cpp
@@ -79,7 +79,7 @@ bool JsonWizardFileGenerator::setup(const QVariant &data, QString *errorMessage)
return false;
for (const QVariant &d : list) {
- if (d.type() != QVariant::Map) {
+ if (d.typeId() != QMetaType::QVariantMap) {
*errorMessage = Tr::tr("Files data list entry is not an object.");
return false;
}
diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardpagefactory_p.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardpagefactory_p.cpp
index 02f26d8401..45b2a4aa78 100644
--- a/src/plugins/projectexplorer/jsonwizard/jsonwizardpagefactory_p.cpp
+++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardpagefactory_p.cpp
@@ -114,7 +114,7 @@ WizardPage *FilePageFactory::create(JsonWizard *wizard, Id typeId, const QVarian
bool FilePageFactory::validateData(Id typeId, const QVariant &data, QString *errorMessage)
{
QTC_ASSERT(canCreate(typeId), return false);
- if (!data.isNull() && (data.typeId() != QVariant::Map || !data.toMap().isEmpty())) {
+ if (!data.isNull() && (data.typeId() != QMetaType::QVariantMap || !data.toMap().isEmpty())) {
*errorMessage = Tr::tr("\"data\" for a \"File\" page needs to be unset or an empty object.");
return false;
}
@@ -174,7 +174,7 @@ bool KitsPageFactory::validateData(Id typeId, const QVariant &data, QString *err
{
QTC_ASSERT(canCreate(typeId), return false);
- if (data.isNull() || data.typeId() != QVariant::Map) {
+ if (data.isNull() || data.typeId() != QMetaType::QVariantMap) {
*errorMessage = Tr::tr("\"data\" must be a JSON object for \"Kits\" pages.");
return false;
}
@@ -242,7 +242,7 @@ bool ProjectPageFactory::validateData(Id typeId, const QVariant &data, QString *
Q_UNUSED(errorMessage)
QTC_ASSERT(canCreate(typeId), return false);
- if (!data.isNull() && data.typeId() != QVariant::Map) {
+ if (!data.isNull() && data.typeId() != QMetaType::QVariantMap) {
*errorMessage = Tr::tr("\"data\" must be empty or a JSON object for \"Project\" pages.");
return false;
}
@@ -297,7 +297,7 @@ WizardPage *SummaryPageFactory::create(JsonWizard *wizard, Id typeId, const QVar
bool SummaryPageFactory::validateData(Id typeId, const QVariant &data, QString *errorMessage)
{
QTC_ASSERT(canCreate(typeId), return false);
- if (!data.isNull() && (data.typeId() != QVariant::Map)) {
+ if (!data.isNull() && (data.typeId() != QMetaType::QVariantMap)) {
*errorMessage = Tr::tr("\"data\" for a \"Summary\" page can be unset or needs to be an object.");
return false;
}
diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.cpp
index a7b2b9b6ee..9122ccc5ee 100644
--- a/src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.cpp
+++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.cpp
@@ -46,7 +46,7 @@ bool JsonWizardScannerGenerator::setup(const QVariant &data, QString *errorMessa
if (data.isNull())
return true;
- if (data.type() != QVariant::Map) {
+ if (data.typeId() != QMetaType::QVariantMap) {
*errorMessage = Tr::tr("Key is not an object.");
return false;
}
diff --git a/src/plugins/projectexplorer/jsonwizard/wizarddebug.h b/src/plugins/projectexplorer/jsonwizard/wizarddebug.h
index 63f8891670..6878407d9a 100644
--- a/src/plugins/projectexplorer/jsonwizard/wizarddebug.h
+++ b/src/plugins/projectexplorer/jsonwizard/wizarddebug.h
@@ -26,10 +26,10 @@ inline QDebug &operator<<(QDebug &dbg, const QVariant &var)
case QMetaType::QStringList:
dbg << var.toList();
break;
- case QVariant::List:
+ case QMetaType::QVariantList:
dbg << var.toList();
break;
- case QVariant::Map:
+ case QMetaType::QVariantMap:
dbg << var.toMap();
break;
default: {
diff --git a/src/plugins/projectexplorer/kitaspects.cpp b/src/plugins/projectexplorer/kitaspects.cpp
index 0eef454d28..db3300f327 100644
--- a/src/plugins/projectexplorer/kitaspects.cpp
+++ b/src/plugins/projectexplorer/kitaspects.cpp
@@ -61,7 +61,7 @@ public:
private:
void makeReadOnly() override { m_chooser->setReadOnly(true); }
- void addToLayoutImpl(Layouting::LayoutItem &builder) override
+ void addToLayoutImpl(Layouting::Layout &builder) override
{
addMutableAction(m_chooser);
builder.addItem(Layouting::Span(2, m_chooser));
@@ -238,7 +238,7 @@ public:
}
private:
- void addToLayoutImpl(Layouting::LayoutItem &builder) override
+ void addToLayoutImpl(Layouting::Layout &builder) override
{
addMutableAction(m_mainWidget);
builder.addItem(m_mainWidget);
@@ -739,7 +739,7 @@ public:
~DeviceTypeKitAspectImpl() override { delete m_comboBox; }
private:
- void addToLayoutImpl(Layouting::LayoutItem &builder) override
+ void addToLayoutImpl(Layouting::Layout &builder) override
{
addMutableAction(m_comboBox);
builder.addItem(m_comboBox);
@@ -889,7 +889,7 @@ public:
}
private:
- void addToLayoutImpl(Layouting::LayoutItem &builder) override
+ void addToLayoutImpl(Layouting::Layout &builder) override
{
addMutableAction(m_comboBox);
builder.addItem(m_comboBox);
@@ -1170,7 +1170,7 @@ public:
}
private:
- void addToLayoutImpl(Layouting::LayoutItem &builder) override
+ void addToLayoutImpl(Layouting::Layout &builder) override
{
addMutableAction(m_comboBox);
builder.addItem(m_comboBox);
@@ -1433,7 +1433,7 @@ public:
}
private:
- void addToLayoutImpl(Layouting::LayoutItem &builder) override
+ void addToLayoutImpl(Layouting::Layout &builder) override
{
addMutableAction(m_mainWidget);
builder.addItem(m_mainWidget);
@@ -1539,7 +1539,7 @@ Tasks EnvironmentKitAspectFactory::validate(const Kit *k) const
QTC_ASSERT(k, return result);
const QVariant variant = k->value(EnvironmentKitAspect::id());
- if (!variant.isNull() && !variant.canConvert(QVariant::List))
+ if (!variant.isNull() && !variant.canConvert(QMetaType::QVariantList))
result << BuildSystemTask(Task::Error, Tr::tr("The environment setting value is invalid."));
return result;
@@ -1550,7 +1550,7 @@ void EnvironmentKitAspectFactory::fix(Kit *k)
QTC_ASSERT(k, return);
const QVariant variant = k->value(EnvironmentKitAspect::id());
- if (!variant.isNull() && !variant.canConvert(QVariant::List)) {
+ if (!variant.isNull() && !variant.canConvert(QMetaType::QVariantList)) {
qWarning("Kit \"%s\" has a wrong environment value set.", qPrintable(k->displayName()));
EnvironmentKitAspect::setEnvironmentChanges(k, EnvironmentItems());
}
diff --git a/src/plugins/projectexplorer/kitmanager.cpp b/src/plugins/projectexplorer/kitmanager.cpp
index 9406d746d9..154ce46f4b 100644
--- a/src/plugins/projectexplorer/kitmanager.cpp
+++ b/src/plugins/projectexplorer/kitmanager.cpp
@@ -798,7 +798,7 @@ void KitAspect::makeStickySubWidgetsReadOnly()
makeReadOnly();
}
-void KitAspect::addToLayout(Layouting::LayoutItem &parentItem)
+void KitAspect::addToLayout(Layouting::Layout &parentItem)
{
auto label = createSubWidget<QLabel>(m_factory->displayName() + ':');
label->setToolTip(m_factory->description());
diff --git a/src/plugins/projectexplorer/kitmanager.h b/src/plugins/projectexplorer/kitmanager.h
index 44c6223d79..4068aea15d 100644
--- a/src/plugins/projectexplorer/kitmanager.h
+++ b/src/plugins/projectexplorer/kitmanager.h
@@ -110,7 +110,7 @@ public:
virtual void refresh() = 0;
- void addToLayout(Layouting::LayoutItem &parentItem) override;
+ void addToLayout(Layouting::Layout &parentItem) override;
static QString msgManage();
@@ -124,7 +124,7 @@ public:
protected:
virtual void makeReadOnly() {}
- virtual void addToLayoutImpl(Layouting::LayoutItem &parentItem) = 0;
+ virtual void addToLayoutImpl(Layouting::Layout &parentItem) = 0;
virtual Utils::Id settingsPageItemToPreselect() const { return {}; }
Kit *m_kit;
diff --git a/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp b/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp
index 2c142dec88..ab884a9d7f 100644
--- a/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp
+++ b/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp
@@ -200,7 +200,7 @@ QString KitManagerConfigWidget::validityMessage() const
return m_modifiedKit->toHtml(tmp);
}
-void KitManagerConfigWidget::addAspectToWorkingCopy(Layouting::LayoutItem &parent, KitAspectFactory *factory)
+void KitManagerConfigWidget::addAspectToWorkingCopy(Layouting::Layout &parent, KitAspectFactory *factory)
{
QTC_ASSERT(factory, return);
KitAspect *aspect = factory->createKitAspect(workingCopy());
diff --git a/src/plugins/projectexplorer/kitmanagerconfigwidget.h b/src/plugins/projectexplorer/kitmanagerconfigwidget.h
index 1a499f7dab..8564ccbcc9 100644
--- a/src/plugins/projectexplorer/kitmanagerconfigwidget.h
+++ b/src/plugins/projectexplorer/kitmanagerconfigwidget.h
@@ -36,7 +36,7 @@ public:
void discard();
bool isDirty() const;
QString validityMessage() const;
- void addAspectToWorkingCopy(Layouting::LayoutItem &parent, KitAspectFactory *factory);
+ void addAspectToWorkingCopy(Layouting::Layout &parent, KitAspectFactory *factory);
void makeStickySubWidgetsReadOnly();
Kit *workingCopy() const;
diff --git a/src/plugins/projectexplorer/ldparser.cpp b/src/plugins/projectexplorer/ldparser.cpp
index 82b3c3d335..67784e2a8d 100644
--- a/src/plugins/projectexplorer/ldparser.cpp
+++ b/src/plugins/projectexplorer/ldparser.cpp
@@ -39,7 +39,7 @@ Utils::OutputLineParser::Result LdParser::handleLine(const QString &line, Utils:
return Status::NotHandled;
QString lne = rightTrimmed(line);
- if (!lne.isEmpty() && !lne.at(0).isSpace() && !m_incompleteTask.isNull()) {
+ if (!lne.isEmpty() && !lne.at(0).isSpace() && !currentTask().isNull()) {
flush();
return Status::NotHandled;
}
@@ -52,19 +52,20 @@ Utils::OutputLineParser::Result LdParser::handleLine(const QString &line, Utils:
// ld on macOS
if (lne.startsWith("Undefined symbols for architecture") && lne.endsWith(":")) {
- m_incompleteTask = CompileTask(Task::Error, lne);
+ createOrAmendTask(Task::Error, lne, line);
return Status::InProgress;
}
- if (!m_incompleteTask.isNull() && lne.startsWith(" ")) {
- m_incompleteTask.details.append(lne);
+ if (!currentTask().isNull() && lne.startsWith(" ")) {
static const QRegularExpression locRegExp(" (?<symbol>\\S+) in (?<file>\\S+)");
const QRegularExpressionMatch match = locRegExp.match(lne);
LinkSpecs linkSpecs;
+ Utils::FilePath filePath;
if (match.hasMatch()) {
- m_incompleteTask.setFile(absoluteFilePath(Utils::FilePath::fromString(
- match.captured("file"))));
- addLinkSpecForAbsoluteFilePath(linkSpecs, m_incompleteTask.file, 0, match, "file");
+ filePath = absoluteFilePath(Utils::FilePath::fromString(match.captured("file")));
+ addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, 0, match, "file");
+ currentTask().setFile(filePath);
}
+ createOrAmendTask(Task::Unknown, {}, line, true, filePath);
return {Status::InProgress, linkSpecs};
}
@@ -138,12 +139,3 @@ Utils::OutputLineParser::Result LdParser::handleLine(const QString &line, Utils:
return Status::NotHandled;
}
-
-void LdParser::flush()
-{
- if (m_incompleteTask.isNull())
- return;
- const Task t = m_incompleteTask;
- m_incompleteTask.clear();
- scheduleTask(t, 1);
-}
diff --git a/src/plugins/projectexplorer/ldparser.h b/src/plugins/projectexplorer/ldparser.h
index 918f50b36c..ce2114b5f0 100644
--- a/src/plugins/projectexplorer/ldparser.h
+++ b/src/plugins/projectexplorer/ldparser.h
@@ -4,7 +4,6 @@
#pragma once
#include "ioutputparser.h"
-#include "task.h"
#include <QRegularExpression>
@@ -18,13 +17,10 @@ public:
LdParser();
private:
Result handleLine(const QString &line, Utils::OutputFormat type) override;
- void flush() override;
QRegularExpression m_ranlib;
QRegularExpression m_regExpLinker;
QRegularExpression m_regExpGccNames;
-
- Task m_incompleteTask;
};
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/linuxiccparser.cpp b/src/plugins/projectexplorer/linuxiccparser.cpp
index 62e151846e..734f814865 100644
--- a/src/plugins/projectexplorer/linuxiccparser.cpp
+++ b/src/plugins/projectexplorer/linuxiccparser.cpp
@@ -12,8 +12,7 @@ using namespace Utils;
namespace ProjectExplorer {
-LinuxIccParser::LinuxIccParser() :
- m_temporary(Task())
+LinuxIccParser::LinuxIccParser()
{
setObjectName(QLatin1String("LinuxIccParser"));
// main.cpp(53): error #308: function \"AClass::privatefunc\" (declared at line 4 of \"main.h\") is inaccessible
@@ -51,7 +50,6 @@ OutputLineParser::Result LinuxIccParser::handleLine(const QString &line, OutputF
if (m_expectFirstLine) {
const QRegularExpressionMatch match = m_firstLine.match(line);
if (match.hasMatch()) {
- // Clear out old task
Task::TaskType type = Task::Unknown;
QString category = match.captured(4);
if (category == QLatin1String("error"))
@@ -62,9 +60,7 @@ OutputLineParser::Result LinuxIccParser::handleLine(const QString &line, OutputF
const int lineNo = match.captured(2).toInt();
LinkSpecs linkSpecs;
addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match, 1);
- m_temporary = CompileTask(type, match.captured(6).trimmed(), filePath, lineNo);
-
- m_lines = 1;
+ createOrAmendTask(type, match.captured(6).trimmed(), line, false, filePath, lineNo);
m_expectFirstLine = false;
return Status::InProgress;
}
@@ -75,17 +71,14 @@ OutputLineParser::Result LinuxIccParser::handleLine(const QString &line, OutputF
}
if (!m_expectFirstLine && line.trimmed().isEmpty()) { // last Line
m_expectFirstLine = true;
- scheduleTask(m_temporary, m_lines);
- m_temporary = Task();
+ flush();
return Status::Done;
}
const QRegularExpressionMatch match = m_continuationLines.match(line);
if (!m_expectFirstLine && match.hasMatch()) {
- m_temporary.details.append(match.captured(1).trimmed());
- ++m_lines;
+ createOrAmendTask(Task::Unknown, {}, line, true);
return Status::InProgress;
}
- QTC_CHECK(m_temporary.isNull());
return Status::NotHandled;
}
@@ -99,17 +92,6 @@ QList<OutputLineParser *> LinuxIccParser::iccParserSuite()
return {new LinuxIccParser, new Internal::LldParser, new LdParser};
}
-void LinuxIccParser::flush()
-{
- if (m_temporary.isNull())
- return;
-
- setDetailsFormat(m_temporary);
- Task t = m_temporary;
- m_temporary.clear();
- scheduleTask(t, m_lines, 1);
-}
-
} // ProjectExplorer
#ifdef WITH_TESTS
@@ -155,8 +137,10 @@ void ProjectExplorerTest::testLinuxIccOutputParsers_data()
<< QString() << QString::fromLatin1("\n")
<< (Tasks()
<< CompileTask(Task::Error,
- "identifier \"f\" is undefined\nf(0);",
- FilePath::fromUserInput(QLatin1String("main.cpp")), 13))
+ "identifier \"f\" is undefined\n"
+ "main.cpp(13): error: identifier \"f\" is undefined\n"
+ " f(0);",
+ FilePath::fromUserInput(QLatin1String("main.cpp")), 13))
<< QString();
// same, with PCH remark
@@ -170,8 +154,10 @@ void ProjectExplorerTest::testLinuxIccOutputParsers_data()
<< QString() << QString::fromLatin1("\n")
<< (Tasks()
<< CompileTask(Task::Error,
- "identifier \"f\" is undefined\nf(0);",
- FilePath::fromUserInput("main.cpp"), 13))
+ "identifier \"f\" is undefined\n"
+ "main.cpp(13): error: identifier \"f\" is undefined\n"
+ " f(0);",
+ FilePath::fromUserInput("main.cpp"), 13))
<< QString();
@@ -184,8 +170,10 @@ void ProjectExplorerTest::testLinuxIccOutputParsers_data()
<< QString() << QString::fromLatin1("\n")
<< (Tasks()
<< CompileTask(Task::Error,
- "function \"AClass::privatefunc\" (declared at line 4 of \"main.h\") is inaccessible\nb.privatefunc();",
- FilePath::fromUserInput("main.cpp"), 53))
+ "function \"AClass::privatefunc\" (declared at line 4 of \"main.h\") is inaccessible\n"
+ "main.cpp(53): error #308: function \"AClass::privatefunc\" (declared at line 4 of \"main.h\") is inaccessible\n"
+ " b.privatefunc();",
+ FilePath::fromUserInput("main.cpp"), 53))
<< QString();
QTest::newRow("simple warning")
@@ -197,8 +185,10 @@ void ProjectExplorerTest::testLinuxIccOutputParsers_data()
<< QString() << QString::fromLatin1("\n")
<< (Tasks()
<< CompileTask(Task::Warning,
- "use of \"=\" where \"==\" may have been intended\nwhile (a = true)",
- FilePath::fromUserInput("main.cpp"), 41))
+ "use of \"=\" where \"==\" may have been intended\n"
+ "main.cpp(41): warning #187: use of \"=\" where \"==\" may have been intended\n"
+ " while (a = true)",
+ FilePath::fromUserInput("main.cpp"), 41))
<< QString();
}
diff --git a/src/plugins/projectexplorer/linuxiccparser.h b/src/plugins/projectexplorer/linuxiccparser.h
index 24b40f9c06..a17ffef84c 100644
--- a/src/plugins/projectexplorer/linuxiccparser.h
+++ b/src/plugins/projectexplorer/linuxiccparser.h
@@ -4,7 +4,6 @@
#pragma once
#include "ioutputparser.h"
-#include "task.h"
#include <QRegularExpression>
@@ -23,7 +22,6 @@ public:
private:
Result handleLine(const QString &line, Utils::OutputFormat type) override;
- void flush() override;
QRegularExpression m_firstLine;
QRegularExpression m_continuationLines;
@@ -31,8 +29,6 @@ private:
QRegularExpression m_pchInfoLine;
bool m_expectFirstLine = true;
- Task m_temporary;
- int m_lines = 0;
};
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/makestep.cpp b/src/plugins/projectexplorer/makestep.cpp
index 730215d81c..16e6f2c4e7 100644
--- a/src/plugins/projectexplorer/makestep.cpp
+++ b/src/plugins/projectexplorer/makestep.cpp
@@ -313,7 +313,7 @@ QWidget *MakeStep::createConfigWidget()
if (m_disablingForSubDirsSupported)
builder.addRow({m_disabledForSubdirsAspect});
builder.addRow({m_buildTargetsAspect});
- builder.addItem(Layouting::noMargin);
+ builder.noMargin();
auto widget = builder.emerge();
diff --git a/src/plugins/projectexplorer/miniprojecttargetselector.cpp b/src/plugins/projectexplorer/miniprojecttargetselector.cpp
index bbf2b8be1d..a33cd3e247 100644
--- a/src/plugins/projectexplorer/miniprojecttargetselector.cpp
+++ b/src/plugins/projectexplorer/miniprojecttargetselector.cpp
@@ -434,7 +434,7 @@ void TargetSelectorDelegate::paint(QPainter *painter,
painter->save();
painter->setClipping(false);
- QColor textColor = creatorTheme()->color(Theme::MiniProjectTargetSelectorTextColor);
+ QColor textColor = creatorColor(Theme::MiniProjectTargetSelectorTextColor);
if (option.state & QStyle::State_Selected) {
QColor color;
if (m_view->hasFocus()) {
@@ -494,7 +494,7 @@ SelectorView::SelectorView(QWidget *parent) : TreeView(parent)
setSelectionBehavior(SelectRows);
setAttribute(Qt::WA_MacShowFocusRect, false);
setHeaderHidden(true);
- const QColor bgColor = creatorTheme()->color(Theme::MiniProjectTargetSelectorBackgroundColor);
+ const QColor bgColor = creatorColor(Theme::MiniProjectTargetSelectorBackgroundColor);
const QString bgColorName = creatorTheme()->flag(Theme::FlatToolBars)
? bgColor.lighter(120).name() : bgColor.name();
setStyleSheet(QString::fromLatin1("QAbstractItemView { background: %1; border-style: none; }").arg(bgColorName));
@@ -552,7 +552,7 @@ int SelectorView::padding()
/////////
// KitAreaWidget
/////////
-void doLayout(KitAspect *aspect, Layouting::LayoutItem &builder)
+void doLayout(KitAspect *aspect, Layouting::Layout &builder)
{
aspect->addToLayout(builder);
}
@@ -586,7 +586,8 @@ public:
if (k && k->isMutable(factory->id())) {
KitAspect *aspect = factory->createKitAspect(k);
m_kitAspects << aspect;
- grid.addItems({aspect, Layouting::br});
+ grid.addItem(aspect);
+ grid.flush();
}
}
m_gridWidget = grid.emerge();
@@ -1537,7 +1538,7 @@ void MiniProjectTargetSelector::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.fillRect(rect(), StyleHelper::baseColor());
- painter.setPen(creatorTheme()->color(Theme::MiniProjectTargetSelectorBorderColor));
+ painter.setPen(creatorColor(Theme::MiniProjectTargetSelectorBorderColor));
// draw border on top and right
QRectF borderRect = QRectF(rect()).adjusted(0.5, 0.5, -0.5, -0.5);
painter.drawLine(borderRect.topLeft(), borderRect.topRight());
diff --git a/src/plugins/projectexplorer/msvcparser.cpp b/src/plugins/projectexplorer/msvcparser.cpp
index 4a53ce958f..0613a0b74e 100644
--- a/src/plugins/projectexplorer/msvcparser.cpp
+++ b/src/plugins/projectexplorer/msvcparser.cpp
@@ -94,36 +94,28 @@ OutputLineParser::Result MsvcParser::handleLine(const QString &line, OutputForma
if (type == OutputFormat::StdOutFormat) {
QRegularExpressionMatch match = m_additionalInfoRegExp.match(line);
if (line.startsWith(" ") && !match.hasMatch()) {
- if (m_lastTask.isNull())
+ if (currentTask().isNull())
return Status::NotHandled;
-
- m_lastTask.details.append(rightTrimmed(line));
- ++m_lines;
+ createOrAmendTask(Task::Unknown, {}, line, true);
return Status::InProgress;
}
const Result res = processCompileLine(line);
if (res.status != Status::NotHandled)
return res;
- const Task t = handleNmakeJomMessage(line);
- if (!t.isNull()) {
- flush();
- m_lastTask = t;
- m_lines = 1;
+ if (const Task t = handleNmakeJomMessage(line); !t.isNull()) {
+ setCurrentTask(t);
return Status::InProgress;
}
if (match.hasMatch()) {
- QString description = match.captured(1)
- + match.captured(4).trimmed();
+ QString description = match.captured(1) + match.captured(4).trimmed();
if (!match.captured(1).isEmpty())
description.chop(1); // Remove trailing quote
const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(match.captured(2)));
const int lineNo = match.captured(3).toInt();
LinkSpecs linkSpecs;
addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match, 2);
- m_lastTask = CompileTask(Task::Unknown, description, filePath, lineNo);
- m_lastTask.details << line;
- m_lines = 1;
+ createOrAmendTask(Task::Unknown, description, line, false, filePath, lineNo, 0, linkSpecs);
return {Status::InProgress, linkSpecs};
}
return Status::NotHandled;
@@ -132,63 +124,46 @@ OutputLineParser::Result MsvcParser::handleLine(const QString &line, OutputForma
const Result res = processCompileLine(line);
if (res.status != Status::NotHandled)
return res;
+
// Jom outputs errors to stderr
- const Task t = handleNmakeJomMessage(line);
- if (!t.isNull()) {
- flush();
- m_lastTask = t;
- m_lines = 1;
+ if (const Task t = handleNmakeJomMessage(line); !t.isNull()) {
+ setCurrentTask(t);
return Status::InProgress;
}
+
return Status::NotHandled;
}
+bool MsvcParser::isContinuation(const QString &line) const
+{
+ return line.contains("note: ");
+}
+
MsvcParser::Result MsvcParser::processCompileLine(const QString &line)
{
QRegularExpressionMatch match = m_compileRegExp.match(line);
if (match.hasMatch()) {
QPair<FilePath, int> position = parseFileName(match.captured(1));
const FilePath filePath = absoluteFilePath(position.first);
- LinkSpecs lineLinkSpecs;
- addLinkSpecForAbsoluteFilePath(lineLinkSpecs, filePath, position.second, match, 1);
- LinkSpecs detailsLinkSpecs = lineLinkSpecs;
- if (!m_lastTask.isNull() && line.contains("note: ")) {
- const int offset = std::accumulate(m_lastTask.details.cbegin(),
- m_lastTask.details.cend(), 0,
- [](int total, const QString &line) { return total + line.length() + 1;});
- for (LinkSpec &ls : detailsLinkSpecs)
- ls.startPos += offset;
- ++m_lines;
- } else {
- flush();
- m_lastTask = CompileTask(taskType(match.captured(2)),
- match.captured(3) + match.captured(4).trimmed(), // description
- filePath, position.second);
- m_lines = 1;
- }
- m_linkSpecs << detailsLinkSpecs;
- m_lastTask.details.append(line);
- return {Status::InProgress, lineLinkSpecs};
+ LinkSpecs linkSpecs;
+ addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, position.second, match, 1);
+ const QString &description = match.captured(3) + match.captured(4).trimmed();
+ createOrAmendTask(
+ taskType(match.captured(2)),
+ description,
+ line,
+ false,
+ filePath,
+ position.second,
+ 0,
+ linkSpecs);
+ return {Status::InProgress, linkSpecs};
}
flush();
return Status::NotHandled;
}
-void MsvcParser::flush()
-{
- if (m_lastTask.isNull())
- return;
-
- if (m_lastTask.details.count() == 1)
- m_lastTask.details.clear();
- setDetailsFormat(m_lastTask, m_linkSpecs);
- Task t = m_lastTask;
- m_lastTask.clear();
- m_linkSpecs.clear();
- scheduleTask(t, m_lines, 1);
-}
-
// --------------------------------------------------------------------------
// ClangClParser: The compiler errors look similar to MSVC, except that the
// column number is also given and there are no 4digit CXXXX error numbers.
@@ -219,11 +194,8 @@ static inline bool isClangCodeMarker(const QString &trimmedLine)
OutputLineParser::Result ClangClParser::handleLine(const QString &line, OutputFormat type)
{
if (type == StdOutFormat) {
- const Task t = handleNmakeJomMessage(line);
- if (!t.isNull()) {
- flush();
- m_lastTask = t;
- m_linkedLines = 1;
+ if (const Task t = handleNmakeJomMessage(line); !t.isNull()) {
+ setCurrentTask(t);
flush();
return Status::Done;
}
@@ -231,11 +203,8 @@ OutputLineParser::Result ClangClParser::handleLine(const QString &line, OutputFo
}
const QString lne = rightTrimmed(line); // Strip \n.
- const Task t = handleNmakeJomMessage(lne);
- if (!t.isNull()) {
- flush();
- m_lastTask = t;
- m_linkedLines = 1;
+ if (const Task t = handleNmakeJomMessage(lne); !t.isNull()) {
+ setCurrentTask(t);
flush();
return Status::Done;
}
@@ -256,36 +225,28 @@ OutputLineParser::Result ClangClParser::handleLine(const QString &line, OutputFo
if (match.hasMatch()) {
flush();
const QPair<FilePath, int> position = parseFileName(match.captured(1));
- m_lastTask = CompileTask(taskType(match.captured(2)), match.captured(3).trimmed(),
- absoluteFilePath(position.first), position.second);
- m_linkedLines = 1;
+ const FilePath file = absoluteFilePath(position.first);
+ const int lineNo = position.second;
LinkSpecs linkSpecs;
- addLinkSpecForAbsoluteFilePath(linkSpecs, m_lastTask.file, m_lastTask.line, match, 1);
+ addLinkSpecForAbsoluteFilePath(linkSpecs, file, lineNo, match, 1);
+ createOrAmendTask(
+ taskType(match.captured(2)), match.captured(3).trimmed(), line, false, file, lineNo);
return {Status::InProgress, linkSpecs};
}
- if (!m_lastTask.isNull()) {
+ if (!currentTask().isNull()) {
const QString trimmed = lne.trimmed();
if (isClangCodeMarker(trimmed)) {
flush();
return Status::Done;
}
- m_lastTask.details.append(trimmed);
- ++m_linkedLines;
+ createOrAmendTask(Task::Unknown, {}, line, true);
return Status::InProgress;
}
return Status::NotHandled;
}
-void ClangClParser::flush()
-{
- if (!m_lastTask.isNull()) {
- scheduleTask(m_lastTask, m_linkedLines, 1);
- m_lastTask.clear();
- }
-}
-
// Unit tests:
#ifdef WITH_TESTS
@@ -619,32 +580,23 @@ void ProjectExplorerTest::testClangClOutputParsers_data()
QTest::addColumn<Tasks >("tasks");
QTest::addColumn<QString>("outputLines");
- const QString warning1 = "private field 'm_version' is not used [-Wunused-private-field]\n"
- "const int m_version; //! majorVersion<<8 + minorVersion\n";
- const QString warning2 = "unused variable 'formatTextPlainC' [-Wunused-const-variable]\n"
- "static const char formatTextPlainC[] = \"text/plain\";\n";
- const QString warning3 = "unused variable 'formatTextHtmlC' [-Wunused-const-variable]\n"
- "static const char formatTextHtmlC[] = \"text/html\";\n";
- const QString error1 = "unknown type name 'errr'\n"
- " errr\n";
- const QString expectedError1 = "unknown type name 'errr'\n"
- "errr"; // Line 2 trimmed.
- const QString error2 =
- "expected unqualified-id\n"
- "void *QWindowsGdiNativeInterface::nativeResourceForBackingStore(const QByteArray &resource, QBackingStore *bs)\n";
-
const QString clangClCompilerLog =
"In file included from .\\qwindowseglcontext.cpp:40:\n"
- "./qwindowseglcontext.h(282,15) : warning: " + warning1 +
+ "./qwindowseglcontext.h(282,15) : warning: private field 'm_version' is not used [-Wunused-private-field]\n"
+ "const int m_version; //! majorVersion<<8 + minorVersion\n"
"5 warnings generated.\n"
- ".\\qwindowsclipboard.cpp(60,19) : warning: " + warning2 +
+ ".\\qwindowsclipboard.cpp(60,19) : warning: unused variable 'formatTextPlainC' [-Wunused-const-variable]\n"
+ "static const char formatTextPlainC[] = \"text/plain\";\n"
" ^\n"
- ".\\qwindowsclipboard.cpp(61,19) : warning: " + warning3 +
+ ".\\qwindowsclipboard.cpp(61,19) : warning: unused variable 'formatTextHtmlC' [-Wunused-const-variable]\n"
+ "static const char formatTextHtmlC[] = \"text/html\";\n"
" ^\n"
"2 warnings generated.\n"
- ".\\qwindowsgdinativeinterface.cpp(48,3) : error: " + error1 +
+ ".\\qwindowsgdinativeinterface.cpp(48,3) : error: unknown type name 'errr'\n"
+ " errr\n"
" ^\n"
- ".\\qwindowsgdinativeinterface.cpp(51,1) : error: " + error2 +
+ ".\\qwindowsgdinativeinterface.cpp(51,1) : error: expected unqualified-id\n"
+ "void *QWindowsGdiNativeInterface::nativeResourceForBackingStore(const QByteArray &resource, QBackingStore *bs)\n"
"^\n"
"2 errors generated.\n";
@@ -660,16 +612,31 @@ void ProjectExplorerTest::testClangClOutputParsers_data()
<< OutputParserTester::STDERR
<< "" << expectedStderr
<< (Tasks()
- << CompileTask(Task::Warning, warning1.trimmed(),
- FilePath::fromUserInput("./qwindowseglcontext.h"), 282)
- << CompileTask(Task::Warning, warning2.trimmed(),
- FilePath::fromUserInput(".\\qwindowsclipboard.cpp"), 60)
- << CompileTask(Task::Warning, warning3.trimmed(),
- FilePath::fromUserInput(".\\qwindowsclipboard.cpp"), 61)
- << CompileTask(Task::Error, expectedError1,
- FilePath::fromUserInput(".\\qwindowsgdinativeinterface.cpp"), 48)
- << CompileTask(Task::Error, error2.trimmed(),
- FilePath::fromUserInput(".\\qwindowsgdinativeinterface.cpp"), 51))
+ << CompileTask(Task::Warning,
+ "private field 'm_version' is not used [-Wunused-private-field]\n"
+ "./qwindowseglcontext.h(282,15) : warning: private field 'm_version' is not used [-Wunused-private-field]\n"
+ "const int m_version; //! majorVersion<<8 + minorVersion",
+ FilePath::fromUserInput("./qwindowseglcontext.h"), 282)
+ << CompileTask(Task::Warning,
+ "unused variable 'formatTextPlainC' [-Wunused-const-variable]\n"
+ ".\\qwindowsclipboard.cpp(60,19) : warning: unused variable 'formatTextPlainC' [-Wunused-const-variable]\n"
+ "static const char formatTextPlainC[] = \"text/plain\";",
+ FilePath::fromUserInput(".\\qwindowsclipboard.cpp"), 60)
+ << CompileTask(Task::Warning,
+ "unused variable 'formatTextHtmlC' [-Wunused-const-variable]\n"
+ ".\\qwindowsclipboard.cpp(61,19) : warning: unused variable 'formatTextHtmlC' [-Wunused-const-variable]\n"
+ "static const char formatTextHtmlC[] = \"text/html\";",
+ FilePath::fromUserInput(".\\qwindowsclipboard.cpp"), 61)
+ << CompileTask(Task::Error,
+ "unknown type name 'errr'\n"
+ ".\\qwindowsgdinativeinterface.cpp(48,3) : error: unknown type name 'errr'\n"
+ " errr",
+ FilePath::fromUserInput(".\\qwindowsgdinativeinterface.cpp"), 48)
+ << CompileTask(Task::Error,
+ "expected unqualified-id\n"
+ ".\\qwindowsgdinativeinterface.cpp(51,1) : error: expected unqualified-id\n"
+ "void *QWindowsGdiNativeInterface::nativeResourceForBackingStore(const QByteArray &resource, QBackingStore *bs)",
+ FilePath::fromUserInput(".\\qwindowsgdinativeinterface.cpp"), 51))
<< "";
QTest::newRow("other error")
@@ -688,9 +655,12 @@ void ProjectExplorerTest::testClangClOutputParsers_data()
"/FoC:\\MyData\\Project_home\\cpp\build-TestForError-msvc_2017_clang-Debug\\Debug_msvc_201_47eca974c876c8b3\\TestForError.b6dd39ae\\3a52ce780950d4d9\\main.cpp.obj "
"C:\\MyData\\Project_home\\cpp\\TestForError\\main.cpp /TP\n"
" ;\n"
- << Tasks{CompileTask(Task::Error, "expected ';' after return statement\nreturn 0",
- FilePath::fromUserInput("C:\\MyData\\Project_home\\cpp\\TestForError\\main.cpp"),
- 3)}
+ << Tasks{CompileTask(Task::Error,
+ "expected ';' after return statement\n"
+ "C:\\MyData\\Project_home\\cpp\\TestForError\\main.cpp(3,10): error: expected ';' after return statement\n"
+ "return 0",
+ FilePath::fromUserInput("C:\\MyData\\Project_home\\cpp\\TestForError\\main.cpp"),
+ 3)}
<< "";
}
diff --git a/src/plugins/projectexplorer/msvcparser.h b/src/plugins/projectexplorer/msvcparser.h
index 8655ca929c..eef1d5603b 100644
--- a/src/plugins/projectexplorer/msvcparser.h
+++ b/src/plugins/projectexplorer/msvcparser.h
@@ -4,7 +4,6 @@
#pragma once
#include "ioutputparser.h"
-#include "task.h"
#include <QRegularExpression>
#include <QString>
@@ -22,16 +21,12 @@ public:
private:
Result handleLine(const QString &line, Utils::OutputFormat type) override;
- void flush() override;
+ bool isContinuation(const QString &line) const override;
Result processCompileLine(const QString &line);
QRegularExpression m_compileRegExp;
QRegularExpression m_additionalInfoRegExp;
-
- Task m_lastTask;
- LinkSpecs m_linkSpecs;
- int m_lines = 0;
};
class PROJECTEXPLORER_EXPORT ClangClParser : public ProjectExplorer::OutputTaskParser
@@ -43,11 +38,8 @@ public:
private:
Result handleLine(const QString &line, Utils::OutputFormat type) override;
- void flush() override;
const QRegularExpression m_compileRegExp;
- Task m_lastTask;
- int m_linkedLines = 0;
};
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp
index 310e170e5a..9dad5e8eb6 100644
--- a/src/plugins/projectexplorer/msvctoolchain.cpp
+++ b/src/plugins/projectexplorer/msvctoolchain.cpp
@@ -1793,11 +1793,7 @@ Macros ClangClToolchain::msvcPredefinedMacros(const QStringList &cxxflags,
Process cpp;
cpp.setEnvironment(env);
cpp.setWorkingDirectory(Utils::TemporaryDirectory::masterDirectoryFilePath());
-
- QStringList arguments = cxxflags;
- arguments.append(gccPredefinedMacrosOptions(language()));
- arguments.append("-");
- cpp.setCommand({compilerCommand(), arguments});
+ cpp.setCommand({compilerCommand(), {cxxflags, gccPredefinedMacrosOptions(language()), "-"}});
cpp.runBlocking();
if (cpp.result() != ProcessResult::FinishedWithSuccess) {
// Show the warning but still parse the output.
diff --git a/src/plugins/projectexplorer/processparameters.cpp b/src/plugins/projectexplorer/processparameters.cpp
index a60f105947..82d391fc58 100644
--- a/src/plugins/projectexplorer/processparameters.cpp
+++ b/src/plugins/projectexplorer/processparameters.cpp
@@ -158,7 +158,7 @@ static QString invalidCommandMessage(const QString &displayName)
return QString("<b>%1:</b> <font color='%3'>%2</font>")
.arg(displayName,
::Utils::Tr::tr("Invalid command"),
- creatorTheme()->color(Theme::TextColorError).name());
+ creatorColor(Theme::TextColorError).name());
}
QString ProcessParameters::summary(const QString &displayName) const
diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp
index 7d0ed75c6c..a46f5d5eef 100644
--- a/src/plugins/projectexplorer/project.cpp
+++ b/src/plugins/projectexplorer/project.cpp
@@ -1252,6 +1252,14 @@ void Project::addVariablesToMacroExpander(const QByteArray &prefix,
return project->projectFilePath();
return {};
});
+ expander->registerVariable(fullPrefix + "ProjectDirectory",
+ //: %1 is something like "Active project"
+ ::PE::Tr::tr("%1: Full path to Project Directory.").arg(descriptor),
+ [projectGetter]() -> QString {
+ if (const Project *const project = projectGetter())
+ return project->projectDirectory().toUserOutput();
+ return {};
+ });
expander->registerVariable(fullPrefix + "Kit:Name",
//: %1 is something like "Active project"
::PE::Tr::tr("%1: The name of the active kit.").arg(descriptor),
diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h
index 9906047bc2..ec6c341bb2 100644
--- a/src/plugins/projectexplorer/project.h
+++ b/src/plugins/projectexplorer/project.h
@@ -68,8 +68,8 @@ public:
BuildSystem *createBuildSystem(Target *target) const;
- Utils::FilePath projectFilePath() const;
- Utils::FilePath projectDirectory() const;
+ virtual Utils::FilePath projectFilePath() const;
+ virtual Utils::FilePath projectDirectory() const;
// This does not affect nodes, only the root path.
void changeRootProjectDirectory();
diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp
index 7ec216c138..c3d827eb28 100644
--- a/src/plugins/projectexplorer/projectexplorer.cpp
+++ b/src/plugins/projectexplorer/projectexplorer.cpp
@@ -80,6 +80,7 @@
#include "toolchainmanager.h"
#include "toolchainoptionspage.h"
#include "vcsannotatetaskhandler.h"
+#include "workspaceproject.h"
#ifdef Q_OS_WIN
#include "windebuginterface.h"
@@ -197,6 +198,7 @@ const int P_MODE_SESSION = 85;
// Actions
const char LOAD[] = "ProjectExplorer.Load";
+const char LOADWORKSPACE[] = "ProjectExplorer.LoadWorkspace";
const char UNLOAD[] = "ProjectExplorer.Unload";
const char UNLOADCM[] = "ProjectExplorer.UnloadCM";
const char UNLOADOTHERSCM[] = "ProjectExplorer.UnloadOthersCM";
@@ -479,6 +481,7 @@ public:
void buildQueueFinished(bool success);
void loadAction();
+ void openWorkspaceAction();
void handleUnloadProject();
void unloadProjectContextMenu();
void unloadOtherProjectsContextMenu();
@@ -543,6 +546,7 @@ public:
QAction *m_newAction;
QAction *m_loadAction;
+ QAction *m_loadWorkspaceAction;
Action *m_unloadAction;
Action *m_unloadActionContextMenu;
Action *m_unloadOthersActionContextMenu;
@@ -1099,6 +1103,12 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
cmd->setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Shift+O")));
msessionContextMenu->addAction(cmd, Constants::G_SESSION_FILES);
+ // load workspace action
+ dd->m_loadWorkspaceAction = new QAction(Tr::tr("Open Workspace..."), this);
+ cmd = ActionManager::registerAction(dd->m_loadWorkspaceAction, Constants::LOADWORKSPACE);
+ mfile->addAction(cmd, Core::Constants::G_FILE_OPEN);
+ msessionContextMenu->addAction(cmd, Constants::G_SESSION_FILES);
+
// Default open action
dd->m_openFileAction = new QAction(Tr::tr("Open File"), this);
cmd = ActionManager::registerAction(dd->m_openFileAction, Constants::OPENFILE,
@@ -1659,6 +1669,8 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
dd, &ProjectExplorerPlugin::openNewProjectDialog);
connect(dd->m_loadAction, &QAction::triggered,
dd, &ProjectExplorerPluginPrivate::loadAction);
+ connect(dd->m_loadWorkspaceAction, &QAction::triggered,
+ dd, &ProjectExplorerPluginPrivate::openWorkspaceAction);
connect(dd->m_buildProjectOnlyAction, &QAction::triggered, dd, [] {
BuildManager::buildProjectWithoutDependencies(ProjectManager::startupProject());
});
@@ -1887,10 +1899,11 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
DeviceManager::instance()->addDevice(IDevice::Ptr(new DesktopDevice));
+ setupWorkspaceProject(this);
+
#ifdef WITH_TESTS
addTestCreator(&createSanitizerOutputParserTest);
#endif
-
return true;
}
@@ -1920,6 +1933,30 @@ void ProjectExplorerPluginPrivate::loadAction()
updateActions();
}
+void ProjectExplorerPluginPrivate::openWorkspaceAction()
+{
+ FilePath dir = dd->m_lastOpenDirectory;
+
+ // for your special convenience, we preselect a pro file if it is
+ // the current file
+ if (const IDocument *document = EditorManager::currentDocument()) {
+ const FilePath fn = document->filePath();
+ const bool isProject = dd->m_profileMimeTypes.contains(document->mimeType());
+ dir = isProject ? fn : fn.absolutePath();
+ }
+
+ FilePath filePath = Utils::FileUtils::getExistingDirectory(
+ ICore::dialogParent(), Tr::tr("Open Workspace"), dir);
+ if (filePath.isEmpty())
+ return;
+
+ OpenProjectResult result = ProjectExplorerPlugin::openProject(filePath);
+ if (!result)
+ ProjectExplorerPlugin::showOpenProjectError(result);
+
+ updateActions();
+}
+
void ProjectExplorerPluginPrivate::unloadProjectContextMenu()
{
if (Project *p = ProjectTree::currentProject())
@@ -2073,7 +2110,7 @@ void ProjectExplorerPlugin::extensionsInitialized()
bool ProjectExplorerPlugin::delayedInitialize()
{
- NANOTRACE_SCOPE("ProjectExplorer", "ProjectExplorerPlugin::restoreKits");
+ NANOTRACE_SCOPE("ProjectExplorer", "ProjectExplorerPlugin::delayedInitialize");
ExtraAbi::load(); // Load this before Toolchains!
ToolchainManager::restoreToolchains();
KitManager::restoreKits();
@@ -2150,7 +2187,7 @@ void ProjectExplorerPluginPrivate::checkRecentProjectsAsync()
p.exists = p.filePath.needsDevice() || p.filePath.isFile();
return p;
});
- PluginManager::futureSynchronizer()->addFuture(m_recentProjectsFuture);
+ Utils::futureSynchronizer()->addFuture(m_recentProjectsFuture);
onResultReady(m_recentProjectsFuture, this, [this](const RecentProjectsEntry &p) {
auto it = std::find_if(
@@ -2283,10 +2320,7 @@ OpenProjectResult ProjectExplorerPlugin::openProjects(const FilePaths &filePaths
MimeType mt = Utils::mimeTypeForFile(filePath);
if (ProjectManager::canOpenProjectForMimeType(mt)) {
- if (!filePath.isFile()) {
- appendError(errorString,
- Tr::tr("Failed opening project \"%1\": Project is not a file.").arg(filePath.toUserOutput()));
- } else if (Project *pro = ProjectManager::openProject(mt, filePath)) {
+ if (Project *pro = ProjectManager::openProject(mt, filePath)) {
QString restoreError;
Project::RestoreResult restoreResult = pro->restoreSettings(&restoreError);
if (restoreResult == Project::RestoreResult::Ok) {
@@ -2456,6 +2490,12 @@ void ProjectExplorerPluginPrivate::startRunControl(RunControl *runControl)
++m_activeRunControlCount;
runControl->initiateStart();
doUpdateRunActions();
+ connect(runControl, &RunControl::started, m_instance, [runControl] {
+ emit m_instance->runControlStarted(runControl);
+ });
+ connect(runControl, &RunControl::stopped, m_instance, [runControl] {
+ emit m_instance->runControlStoped(runControl);
+ });
}
void ProjectExplorerPluginPrivate::showOutputPaneForRunControl(RunControl *runControl)
@@ -3611,7 +3651,7 @@ void ProjectExplorerPluginPrivate::openTerminalHere(const EnvironmentGetter &env
}
if (buildDevice->rootPath().needsDevice())
- Terminal::Hooks::instance().openTerminal({CommandLine{*shell, {}}, workingDir, environment});
+ Terminal::Hooks::instance().openTerminal({CommandLine{*shell}, workingDir, environment});
else
Terminal::Hooks::instance().openTerminal({workingDir, environment});
}
@@ -3651,8 +3691,8 @@ void ProjectExplorerPluginPrivate::openTerminalHereWithRunEnv()
}
if (device->rootPath().needsDevice()) {
- Terminal::Hooks::instance().openTerminal(
- {CommandLine{*shell, {}}, workingDir, runnable.environment});
+ Terminal::Hooks::instance().openTerminal({CommandLine{*shell}, workingDir,
+ runnable.environment});
} else {
Terminal::Hooks::instance().openTerminal({workingDir, runnable.environment});
}
diff --git a/src/plugins/projectexplorer/projectexplorer.h b/src/plugins/projectexplorer/projectexplorer.h
index 0c737aa9ab..3c932f759e 100644
--- a/src/plugins/projectexplorer/projectexplorer.h
+++ b/src/plugins/projectexplorer/projectexplorer.h
@@ -182,6 +182,8 @@ signals:
void customParsersChanged();
void runActionsUpdated();
+ void runControlStarted(ProjectExplorer::RunControl *runControl);
+ void runControlStoped(ProjectExplorer::RunControl *runControl);
void filesRenamed(const QList<std::pair<Utils::FilePath, Utils::FilePath>> &oldAndNewPaths);
diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs
index feab87da1b..bce24dc136 100644
--- a/src/plugins/projectexplorer/projectexplorer.qbs
+++ b/src/plugins/projectexplorer/projectexplorer.qbs
@@ -154,6 +154,7 @@ QtcPlugin {
"vcsannotatetaskhandler.cpp", "vcsannotatetaskhandler.h",
"waitforstopdialog.cpp", "waitforstopdialog.h",
"windebuginterface.cpp", "windebuginterface.h",
+ "workspaceproject.cpp", "workspaceproject.h",
"xcodebuildparser.cpp", "xcodebuildparser.h"
]
}
diff --git a/src/plugins/projectexplorer/projectexplorersettings.cpp b/src/plugins/projectexplorer/projectexplorersettings.cpp
index 68b07ae2d5..1780a53eff 100644
--- a/src/plugins/projectexplorer/projectexplorersettings.cpp
+++ b/src/plugins/projectexplorer/projectexplorersettings.cpp
@@ -142,8 +142,10 @@ static void loadProjectExplorerSettings()
.toBool();
settings.environmentId =
QUuid(s->value(Constants::ENVIRONMENT_ID_SETTINGS_KEY).toByteArray());
- if (settings.environmentId.isNull())
+ if (settings.environmentId.isNull()) {
settings.environmentId = QUuid::createUuid();
+ s->setValue(Constants::ENVIRONMENT_ID_SETTINGS_KEY, settings.environmentId.toByteArray());
+ }
int tmp = s->value(Constants::STOP_BEFORE_BUILD_SETTINGS_KEY,
int(defaultSettings.stopBeforeBuild))
.toInt();
diff --git a/src/plugins/projectexplorer/projectmodels.cpp b/src/plugins/projectexplorer/projectmodels.cpp
index c8b7392bf9..484bc5a019 100644
--- a/src/plugins/projectexplorer/projectmodels.cpp
+++ b/src/plugins/projectexplorer/projectmodels.cpp
@@ -242,7 +242,7 @@ QVariant FlatModel::data(const QModelIndex &index, int role) const
}
case Qt::ForegroundRole:
return node->isEnabled() ? QVariant()
- : Utils::creatorTheme()->color(Utils::Theme::TextColorDisabled);
+ : Utils::creatorColor(Utils::Theme::TextColorDisabled);
case Project::FilePathRole:
return node->filePath().toString();
case Project::isParsingRole:
diff --git a/src/plugins/projectexplorer/projectnodeshelper.h b/src/plugins/projectexplorer/projectnodeshelper.h
index bcb5454fd8..b9e3cc27aa 100644
--- a/src/plugins/projectexplorer/projectnodeshelper.h
+++ b/src/plugins/projectexplorer/projectnodeshelper.h
@@ -14,11 +14,6 @@
#include <QPromise>
namespace ProjectExplorer {
-
-template<typename Result>
-QList<FileNode *> scanForFiles(QPromise<Result> &promise, const Utils::FilePath &directory,
- const std::function<FileNode *(const Utils::FilePath &)> factory);
-
namespace Internal {
template<typename Result>
@@ -27,43 +22,40 @@ QList<FileNode *> scanForFilesRecursively(
double progressStart,
double progressRange,
const Utils::FilePath &directory,
+ const QDir::Filters &filter,
const std::function<FileNode *(const Utils::FilePath &)> factory,
- QSet<QString> &visited,
+ QSet<Utils::FilePath> &visited,
const QList<Core::IVersionControl *> &versionControls)
{
QList<FileNode *> result;
- const QDir baseDir = QDir(directory.toString());
-
// Do not follow directory loops:
- const int visitedCount = visited.count();
- visited.insert(baseDir.canonicalPath());
- if (visitedCount == visited.count())
+ if (!Utils::insert(visited, directory.canonicalPath()))
return result;
- const QFileInfoList entries = baseDir.entryInfoList(QStringList(),
- QDir::AllEntries | QDir::NoDotAndDotDot);
+ const Utils::FilePaths entries = directory.dirEntries(filter);
double progress = 0;
const double progressIncrement = progressRange / static_cast<double>(entries.count());
int lastIntProgress = 0;
- for (const QFileInfo &entry : entries) {
+ for (const Utils::FilePath &entry : entries) {
if (promise.isCanceled())
return result;
- const Utils::FilePath entryName = Utils::FilePath::fromString(entry.absoluteFilePath());
- if (!Utils::contains(versionControls, [&entryName](const Core::IVersionControl *vc) {
- return vc->isVcsFileOrDirectory(entryName);
+ if (!Utils::contains(versionControls, [entry](const Core::IVersionControl *vc) {
+ return vc->isVcsFileOrDirectory(entry);
})) {
- if (entry.isDir())
+ if (entry.isDir()) {
result.append(scanForFilesRecursively(promise,
progress,
progressIncrement,
- entryName,
+ entry,
+ filter,
factory,
visited,
versionControls));
- else if (FileNode *node = factory(entryName))
+ } else if (FileNode *node = factory(entry)) {
result.append(node);
+ }
}
progress += progressIncrement;
const int intProgress = std::min(static_cast<int>(progressStart + progress),
@@ -80,15 +72,19 @@ QList<FileNode *> scanForFilesRecursively(
} // namespace Internal
template<typename Result>
-QList<FileNode *> scanForFiles(QPromise<Result> &promise, const Utils::FilePath &directory,
- const std::function<FileNode *(const Utils::FilePath &)> factory)
+QList<FileNode *> scanForFiles(
+ QPromise<Result> &promise,
+ const Utils::FilePath &directory,
+ const QDir::Filters &filter,
+ const std::function<FileNode *(const Utils::FilePath &)> factory)
{
- QSet<QString> visited;
+ QSet<Utils::FilePath> visited;
promise.setProgressRange(0, 1000000);
return Internal::scanForFilesRecursively(promise,
0.0,
1000000.0,
directory,
+ filter,
factory,
visited,
Core::VcsManager::versionControls());
diff --git a/src/plugins/projectexplorer/projecttreewidget.cpp b/src/plugins/projectexplorer/projecttreewidget.cpp
index db335d1ea2..8858d2252c 100644
--- a/src/plugins/projectexplorer/projecttreewidget.cpp
+++ b/src/plugins/projectexplorer/projecttreewidget.cpp
@@ -76,7 +76,7 @@ public:
const bool useUnavailableMarker = index.data(Project::UseUnavailableMarkerRole).toBool();
if (useUnavailableMarker) {
QStyleOptionViewItem opt = option;
- opt.palette.setColor(QPalette::Text, creatorTheme()->color(Theme::TextColorDisabled));
+ opt.palette.setColor(QPalette::Text, creatorColor(Theme::TextColorDisabled));
QStyledItemDelegate::paint(painter, opt, index);
static const QPixmap pixmap
= QApplication::style()->standardIcon(QStyle::SP_BrowserStop).pixmap(10);
diff --git a/src/plugins/projectexplorer/projectwelcomepage.cpp b/src/plugins/projectexplorer/projectwelcomepage.cpp
index 8d97d1ca8a..7597f88f06 100644
--- a/src/plugins/projectexplorer/projectwelcomepage.cpp
+++ b/src/plugins/projectexplorer/projectwelcomepage.cpp
@@ -219,11 +219,6 @@ void ProjectWelcomePage::createActions()
///////////////////
-static QColor themeColor(Theme::Color role)
-{
- return Utils::creatorTheme()->color(role);
-}
-
static QPixmap pixmap(const QString &id, const Theme::Color color)
{
const QString fileName = QString(":/welcome/images/%1.png").arg(id);
@@ -232,8 +227,8 @@ static QPixmap pixmap(const QString &id, const Theme::Color color)
static void drawBackgroundRect(QPainter *painter, const QRectF &rect, bool hovered)
{
- const QColor fill(themeColor(hovered ? cardHoverBackground : cardDefaultBackground));
- const QPen pen(themeColor(hovered ? cardHoverStroke : cardDefaultStroke));
+ const QColor fill(creatorColor(hovered ? cardHoverBackground : cardDefaultBackground));
+ const QPen pen(creatorColor(hovered ? cardHoverStroke : cardDefaultStroke));
const qreal rounding = s(defaultCardBackgroundRounding * 1000) / 1000.0;
const qreal saneRounding = rounding <= 2 ? 0 : rounding;
@@ -514,7 +509,7 @@ public:
.contains(mousePos) && !isDisabled;
if (isActive) {
WelcomePageHelpers::drawCardBackground(painter, actionR, Qt::transparent,
- themeColor(Theme::Token_Text_Muted));
+ creatorColor(Theme::Token_Text_Muted));
m_activeActionRects[i] = actionR;
}
painter->setFont(actionFont);
@@ -524,7 +519,7 @@ public:
xx += actionR.width();
if (i < actions.count() - 1) {
const QRect dividerR(xx + s(HGapXs), yy, actionSepWidth, buttonHeight);
- painter->fillRect(dividerR, themeColor(Theme::Token_Text_Muted));
+ painter->fillRect(dividerR, creatorColor(Theme::Token_Text_Muted));
}
xx += gapWidth;
}
@@ -789,7 +784,7 @@ public:
auto sessions = new QWidget;
{
- auto sessionsLabel = new Label(Tr::tr("Sessions"), Label::Primary);
+ auto sessionsLabel = new Core::Label(Tr::tr("Sessions"), Core::Label::Primary);
auto manageSessionsButton = new Button(Tr::tr("Manage..."), Button::MediumSecondary);
auto sessionsList = new TreeView(this, "Sessions");
sessionsList->setModel(projectWelcomePage->m_sessionModel);
@@ -816,7 +811,7 @@ public:
auto projects = new QWidget;
{
- auto projectsLabel = new Label(Tr::tr("Projects"), Label::Primary);
+ auto projectsLabel = new Core::Label(Tr::tr("Projects"), Core::Label::Primary);
auto projectsList = new TreeView(this, "Recent Projects");
projectsList->setUniformRowHeights(true);
projectsList->setModel(projectWelcomePage->m_projectModel);
@@ -840,7 +835,7 @@ public:
sessions,
projects,
spacing(0),
- noMargin(),
+ noMargin,
}.attachTo(this);
}
diff --git a/src/plugins/projectexplorer/projectwindow.cpp b/src/plugins/projectexplorer/projectwindow.cpp
index 1c2f04a04c..c3214b6498 100644
--- a/src/plugins/projectexplorer/projectwindow.cpp
+++ b/src/plugins/projectexplorer/projectwindow.cpp
@@ -1061,7 +1061,7 @@ void SelectorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
if (TreeItem *item = model->itemForIndex(index)) {
switch (item->level()) {
case 2: {
- QColor col = creatorTheme()->color(Theme::TextColorNormal);
+ QColor col = creatorColor(Theme::TextColorNormal);
opt.palette.setColor(QPalette::Text, col);
opt.font = StyleHelper::uiFont(StyleHelper::UiElementH4);
break;
diff --git a/src/plugins/projectexplorer/projectwizardpage.cpp b/src/plugins/projectexplorer/projectwizardpage.cpp
index b3c0bd37d9..c71d762aaa 100644
--- a/src/plugins/projectexplorer/projectwizardpage.cpp
+++ b/src/plugins/projectexplorer/projectwizardpage.cpp
@@ -377,7 +377,7 @@ void ProjectWizardPage::initializeVersionControls()
m_addToVersionControlComboBox->disconnect();
QList<IVersionControl *> versionControls = VcsManager::versionControls();
if (versionControls.isEmpty())
- hideVersionControlUiElements();
+ setVersionControlUiElementsVisible(false);
IVersionControl *currentSelection = nullptr;
int currentIdx = versionControlIndex() - 1;
@@ -589,11 +589,11 @@ void ProjectWizardPage::manageVcs()
ICore::showOptionsDialog(VcsBase::Constants::VCS_COMMON_SETTINGS_ID, this);
}
-void ProjectWizardPage::hideVersionControlUiElements()
+void ProjectWizardPage::setVersionControlUiElementsVisible(bool visible)
{
- m_addToVersionControlLabel->hide();
- m_vcsManageButton->hide();
- m_addToVersionControlComboBox->hide();
+ m_addToVersionControlLabel->setVisible(visible);
+ m_vcsManageButton->setVisible(visible);
+ m_addToVersionControlComboBox->setVisible(visible);
}
void ProjectWizardPage::setProjectUiVisible(bool visible)
diff --git a/src/plugins/projectexplorer/projectwizardpage.h b/src/plugins/projectexplorer/projectwizardpage.h
index cf5cf42788..835547e261 100644
--- a/src/plugins/projectexplorer/projectwizardpage.h
+++ b/src/plugins/projectexplorer/projectwizardpage.h
@@ -61,10 +61,12 @@ signals:
void projectNodeChanged();
void versionControlChanged(int);
+protected:
+ void setVersionControlUiElementsVisible(bool visible);
+
private:
void projectChanged(int);
void manageVcs();
- void hideVersionControlUiElements();
void setAdditionalInfo(const QString &text);
void setAddingSubProject(bool addingSubProject);
diff --git a/src/plugins/projectexplorer/runconfiguration.cpp b/src/plugins/projectexplorer/runconfiguration.cpp
index be59ad0eaa..3aa2457ef9 100644
--- a/src/plugins/projectexplorer/runconfiguration.cpp
+++ b/src/plugins/projectexplorer/runconfiguration.cpp
@@ -201,13 +201,13 @@ bool RunConfiguration::isEnabled(Utils::Id) const
QWidget *RunConfiguration::createConfigurationWidget()
{
Layouting::Form form;
+ form.noMargin();
for (BaseAspect *aspect : std::as_const(*this)) {
if (aspect->isVisible()) {
form.addItem(aspect);
- form.addItem(Layouting::br);
+ form.flush();
}
}
- form.addItem(Layouting::noMargin);
auto widget = form.emerge();
VariableChooser::addSupportForChildWidgets(widget, &m_expander);
diff --git a/src/plugins/projectexplorer/runconfigurationaspects.cpp b/src/plugins/projectexplorer/runconfigurationaspects.cpp
index c042f081f1..80e82b9f82 100644
--- a/src/plugins/projectexplorer/runconfigurationaspects.cpp
+++ b/src/plugins/projectexplorer/runconfigurationaspects.cpp
@@ -63,13 +63,13 @@ TerminalAspect::TerminalAspect(AspectContainer *container)
/*!
\reimp
*/
-void TerminalAspect::addToLayout(LayoutItem &parent)
+void TerminalAspect::addToLayout(Layout &parent)
{
QTC_CHECK(!m_checkBox);
m_checkBox = createSubWidget<QCheckBox>(Tr::tr("Run in terminal"));
m_checkBox->setChecked(m_useTerminal);
m_checkBox->setEnabled(isEnabled());
- parent.addItems({empty(), m_checkBox.data()});
+ parent.addItems({empty, m_checkBox.data()});
connect(m_checkBox.data(), &QAbstractButton::clicked, this, [this] {
m_userSet = true;
m_useTerminal = m_checkBox->isChecked();
@@ -174,7 +174,7 @@ void WorkingDirectoryAspect::setEnvironment(EnvironmentAspect *envAspect)
/*!
\reimp
*/
-void WorkingDirectoryAspect::addToLayout(LayoutItem &builder)
+void WorkingDirectoryAspect::addToLayout(Layout &builder)
{
QTC_CHECK(!m_chooser);
m_chooser = new PathChooser;
@@ -401,7 +401,7 @@ void ArgumentsAspect::fromMap(const Store &map)
{
QVariant args = map.value(settingsKey());
// Until 3.7 a QStringList was stored for Remote Linux
- if (args.typeId() == QVariant::StringList)
+ if (args.typeId() == QMetaType::QStringList)
m_arguments = ProcessArgs::joinArgs(args.toStringList(), OsTypeLinux);
else
m_arguments = args.toString();
@@ -451,7 +451,7 @@ QWidget *ArgumentsAspect::setupChooser()
/*!
\reimp
*/
-void ArgumentsAspect::addToLayout(LayoutItem &builder)
+void ArgumentsAspect::addToLayout(Layout &builder)
{
QTC_CHECK(!m_chooser && !m_multiLineChooser && !m_multiLineButton);
@@ -643,11 +643,13 @@ FilePath ExecutableAspect::executable() const
/*!
\reimp
*/
-void ExecutableAspect::addToLayout(LayoutItem &builder)
+void ExecutableAspect::addToLayout(Layout &builder)
{
builder.addItem(m_executable);
- if (m_alternativeExecutable)
- builder.addItems({br, m_alternativeExecutable});
+ if (m_alternativeExecutable) {
+ builder.flush();
+ builder.addItem(m_alternativeExecutable);
+ }
}
/*!
diff --git a/src/plugins/projectexplorer/runconfigurationaspects.h b/src/plugins/projectexplorer/runconfigurationaspects.h
index d02236a659..d891132ea7 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:
explicit TerminalAspect(Utils::AspectContainer *container = nullptr);
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
bool useTerminal() const;
void setUseTerminalHint(bool useTerminal);
@@ -61,7 +61,7 @@ class PROJECTEXPLORER_EXPORT WorkingDirectoryAspect : public Utils::BaseAspect
public:
explicit WorkingDirectoryAspect(Utils::AspectContainer *container = nullptr);
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
Utils::FilePath operator()() const { return workingDirectory(); }
Utils::FilePath workingDirectory() const;
@@ -93,7 +93,7 @@ class PROJECTEXPLORER_EXPORT ArgumentsAspect : public Utils::BaseAspect
public:
explicit ArgumentsAspect(Utils::AspectContainer *container = nullptr);
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
QString operator()() const { return arguments(); }
QString arguments() const;
@@ -171,7 +171,7 @@ public:
void setDeviceSelector(Target *target, ExecutionDeviceSelector selector);
void setSettingsKey(const Utils::Key &key);
void makeOverridable(const Utils::Key &overridingKey, const Utils::Key &useOverridableKey);
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
void setLabelText(const QString &labelText);
void setPlaceHolderText(const QString &placeHolderText);
void setHistoryCompleter(const Utils::Key &historyCompleterKey);
diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp
index 6085a72083..02df4037af 100644
--- a/src/plugins/projectexplorer/runcontrol.cpp
+++ b/src/plugins/projectexplorer/runcontrol.cpp
@@ -418,7 +418,7 @@ void RunControl::setDevice(const IDevice::ConstPtr &device)
QTC_CHECK(!d->device);
d->device = device;
#ifdef WITH_JOURNALD
- if (!device.isNull() && device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
+ if (device && device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
JournaldWatcher::instance()->subscribe(this, [this](const JournaldWatcher::LogEntry &entry) {
if (entry.value("_MACHINE_ID") != JournaldWatcher::instance()->machineId())
diff --git a/src/plugins/projectexplorer/targetsettingspanel.cpp b/src/plugins/projectexplorer/targetsettingspanel.cpp
index 6cf25505df..aba55e7a6d 100644
--- a/src/plugins/projectexplorer/targetsettingspanel.cpp
+++ b/src/plugins/projectexplorer/targetsettingspanel.cpp
@@ -321,7 +321,7 @@ public:
case Qt::ForegroundRole: {
if (!isEnabled())
- return Utils::creatorTheme()->color(Theme::TextColorDisabled);
+ return Utils::creatorColor(Theme::TextColorDisabled);
break;
}
diff --git a/src/plugins/projectexplorer/treescanner.cpp b/src/plugins/projectexplorer/treescanner.cpp
index 1eefc3baf4..dac83598fb 100644
--- a/src/plugins/projectexplorer/treescanner.cpp
+++ b/src/plugins/projectexplorer/treescanner.cpp
@@ -43,9 +43,10 @@ bool TreeScanner::asyncScanForFiles(const Utils::FilePath &directory)
return false;
m_scanFuture = Utils::asyncRun(
- [directory, filter = m_filter, factory = m_factory] (Promise &promise) {
- TreeScanner::scanForFiles(promise, directory, filter, factory);
- });
+ [directory, filter = m_filter, dirFilter = m_dirFilter, factory = m_factory](
+ Promise &promise) {
+ TreeScanner::scanForFiles(promise, directory, filter, dirFilter, factory);
+ });
m_futureWatcher.setFuture(m_scanFuture);
return true;
@@ -57,6 +58,12 @@ void TreeScanner::setFilter(TreeScanner::FileFilter filter)
m_filter = filter;
}
+void TreeScanner::setDirFilter(QDir::Filters dirFilter)
+{
+ if (isFinished())
+ m_dirFilter = dirFilter;
+}
+
void TreeScanner::setTypeFactory(TreeScanner::FileTypeFactory factory)
{
if (isFinished())
@@ -139,24 +146,28 @@ static std::unique_ptr<FolderNode> createFolderNode(const Utils::FilePath &direc
return fileSystemNode;
}
-void TreeScanner::scanForFiles(Promise &promise, const Utils::FilePath& directory,
- const FileFilter &filter, const FileTypeFactory &factory)
+void TreeScanner::scanForFiles(
+ Promise &promise,
+ const Utils::FilePath &directory,
+ const FileFilter &filter,
+ const QDir::Filters &dirFilter,
+ const FileTypeFactory &factory)
{
- QList<FileNode *> nodes = ProjectExplorer::scanForFiles(promise, directory,
- [&filter, &factory](const Utils::FilePath &fn) -> FileNode * {
- const Utils::MimeType mimeType = Utils::mimeTypeForFile(fn);
+ QList<FileNode *> nodes = ProjectExplorer::scanForFiles(
+ promise, directory, dirFilter, [&filter, &factory](const Utils::FilePath &fn) -> FileNode * {
+ const Utils::MimeType mimeType = Utils::mimeTypeForFile(fn);
- // Skip some files during scan.
- if (filter && filter(mimeType, fn))
- return nullptr;
+ // Skip some files during scan.
+ if (filter && filter(mimeType, fn))
+ return nullptr;
- // Type detection
- FileType type = FileType::Unknown;
- if (factory)
- type = factory(mimeType, fn);
+ // Type detection
+ FileType type = FileType::Unknown;
+ if (factory)
+ type = factory(mimeType, fn);
- return new FileNode(fn, type);
- });
+ return new FileNode(fn, type);
+ });
Utils::sort(nodes, ProjectExplorer::Node::sortByPath);
diff --git a/src/plugins/projectexplorer/treescanner.h b/src/plugins/projectexplorer/treescanner.h
index f8d019121b..6de26e399a 100644
--- a/src/plugins/projectexplorer/treescanner.h
+++ b/src/plugins/projectexplorer/treescanner.h
@@ -45,6 +45,9 @@ public:
// Setup filter for ignored files
void setFilter(FileFilter filter);
+ // Setup dir filters for scanned folders
+ void setDirFilter(QDir::Filters dirFilter);
+
// Setup factory for file types
void setTypeFactory(FileTypeFactory factory);
@@ -69,11 +72,15 @@ signals:
void finished();
private:
- static void scanForFiles(Promise &fi, const Utils::FilePath &directory,
- const FileFilter &filter, const FileTypeFactory &factory);
+ static void scanForFiles(Promise &fi,
+ const Utils::FilePath &directory,
+ const FileFilter &filter,
+ const QDir::Filters &dirFilter,
+ const FileTypeFactory &factory);
private:
FileFilter m_filter;
+ QDir::Filters m_dirFilter = QDir::AllEntries | QDir::NoDotAndDotDot;
FileTypeFactory m_factory;
FutureWatcher m_futureWatcher;
diff --git a/src/plugins/projectexplorer/userfileaccessor.cpp b/src/plugins/projectexplorer/userfileaccessor.cpp
index 82e4d0edfb..d592935377 100644
--- a/src/plugins/projectexplorer/userfileaccessor.cpp
+++ b/src/plugins/projectexplorer/userfileaccessor.cpp
@@ -453,7 +453,7 @@ Store UserFileVersion14Upgrader::upgrade(const Store &map)
{
Store result;
for (auto it = map.cbegin(), end = map.cend(); it != end; ++it) {
- if (it.value().typeId() == QVariant::Map)
+ if (it.value().typeId() == QMetaType::QVariantMap)
result.insert(it.key(), variantFromStore(upgrade(storeFromVariant(it.value()))));
else if (it.key() == "AutotoolsProjectManager.AutotoolsBuildConfiguration.BuildDirectory"
|| it.key() == "CMakeProjectManager.CMakeBuildConfiguration.BuildDirectory"
@@ -709,13 +709,13 @@ Store UserFileVersion17Upgrader::upgrade(const Store &map)
QVariant UserFileVersion17Upgrader::process(const QVariant &entry)
{
switch (entry.typeId()) {
- case QVariant::List: {
+ case QMetaType::QVariantList: {
QVariantList result;
for (const QVariant &item : entry.toList())
result.append(process(item));
return result;
}
- case QVariant::Map: {
+ case QMetaType::QVariantMap: {
Store result = storeFromVariant(entry);
for (Store::iterator i = result.begin(), end = result.end(); i != end; ++i) {
QVariant &v = i.value();
@@ -737,9 +737,9 @@ Store UserFileVersion18Upgrader::upgrade(const Store &map)
QVariant UserFileVersion18Upgrader::process(const QVariant &entry)
{
switch (entry.typeId()) {
- case QVariant::List:
+ case QMetaType::QVariantList:
return Utils::transform(entry.toList(), &UserFileVersion18Upgrader::process);
- case QVariant::Map: {
+ case QMetaType::QVariantMap: {
Store map = storeFromVariant(entry);
Store result;
for (auto it = map.cbegin(), end = map.cend(); it != end; ++it) {
@@ -793,10 +793,10 @@ QVariant UserFileVersion19Upgrader::process(const QVariant &entry, const KeyList
static const KeyList dyldKeys = {"Qbs.RunConfiguration.UseDyldImageSuffix",
"QmakeProjectManager.QmakeRunConfiguration.UseDyldImageSuffix"};
switch (entry.typeId()) {
- case QVariant::List:
+ case QMetaType::QVariantList:
return Utils::transform(entry.toList(),
std::bind(&UserFileVersion19Upgrader::process, std::placeholders::_1, path));
- case QVariant::Map: {
+ case QMetaType::QVariantMap: {
Store map = storeFromVariant(entry);
Store result;
for (auto it = map.cbegin(), end = map.cend(); it != end; ++it) {
@@ -836,9 +836,9 @@ Store UserFileVersion20Upgrader::upgrade(const Store &map)
QVariant UserFileVersion20Upgrader::process(const QVariant &entry)
{
switch (entry.typeId()) {
- case QVariant::List:
+ case QMetaType::QVariantList:
return Utils::transform(entry.toList(), &UserFileVersion20Upgrader::process);
- case QVariant::Map: {
+ case QMetaType::QVariantMap: {
Store map = storeFromVariant(entry);
Store result;
for (auto it = map.cbegin(), end = map.cend(); it != end; ++it) {
@@ -865,9 +865,9 @@ Store UserFileVersion21Upgrader::upgrade(const Store &map)
QVariant UserFileVersion21Upgrader::process(const QVariant &entry)
{
switch (entry.typeId()) {
- case QVariant::List:
+ case QMetaType::QVariantList:
return Utils::transform(entry.toList(), &UserFileVersion21Upgrader::process);
- case QVariant::Map: {
+ case QMetaType::QVariantMap: {
Store entryMap = storeFromVariant(entry);
if (entryMap.value("ProjectExplorer.ProjectConfiguration.Id").toString()
== "DeployToGenericLinux") {
diff --git a/src/plugins/projectexplorer/workspaceproject.cpp b/src/plugins/projectexplorer/workspaceproject.cpp
new file mode 100644
index 0000000000..d380f7cea8
--- /dev/null
+++ b/src/plugins/projectexplorer/workspaceproject.cpp
@@ -0,0 +1,210 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "workspaceproject.h"
+
+#include "buildsystem.h"
+#include "projectexplorer.h"
+#include "projectexplorerconstants.h"
+#include "projectexplorertr.h"
+#include "projectmanager.h"
+#include "projecttree.h"
+#include "target.h"
+#include "treescanner.h"
+
+#include <coreplugin/actionmanager/actionmanager.h>
+
+#include <utils/algorithm.h>
+#include <utils/stringutils.h>
+
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QJsonObject>
+
+const QLatin1StringView FOLDER_MIMETYPE{"inode/directory"};
+const QLatin1StringView WORKSPACE_MIMETYPE{"text/x-workspace-project"};
+const QLatin1StringView WORKSPACE_PROJECT_ID{"ProjectExplorer.WorkspaceProject"};
+
+const QLatin1StringView PROJECT_NAME_KEY{"project.name"};
+const QLatin1StringView FILES_EXCLUDE_KEY{"files.exclude"};
+const QLatin1StringView EXCLUDE_ACTION_ID{"ProjectExplorer.ExcludeFromWorkspace"};
+
+
+using namespace Utils;
+using namespace Core;
+
+namespace ProjectExplorer {
+
+const expected_str<QJsonObject> projectDefinition(const Project *project)
+{
+ if (auto fileContents = project->projectFilePath().fileContents())
+ return QJsonDocument::fromJson(*fileContents).object();
+ return {};
+}
+
+static bool checkEnabled(FolderNode *fn)
+{
+ if (fn->findChildFileNode([](FileNode *fn) { return fn->isEnabled(); }))
+ return true;
+
+ if (fn->findChildFolderNode([](FolderNode *fn) { return checkEnabled(fn); }))
+ return true;
+
+ fn->setEnabled(false);
+ return false;
+}
+
+class WorkspaceBuildSystem : public BuildSystem
+{
+public:
+ WorkspaceBuildSystem(Target *t)
+ :BuildSystem(t)
+ {
+ connect(&m_scanner, &TreeScanner::finished, this, [this] {
+ auto root = std::make_unique<ProjectNode>(projectDirectory());
+ root->setDisplayName(target()->project()->displayName());
+ std::vector<std::unique_ptr<FileNode>> nodePtrs
+ = Utils::transform<std::vector>(m_scanner.release().allFiles, [this](FileNode *fn) {
+ fn->setEnabled(!Utils::anyOf(
+ m_filters, [path = fn->path().path()](const QRegularExpression &filter) {
+ return filter.match(path).hasMatch();
+ }));
+ return std::unique_ptr<FileNode>(fn);
+ });
+ root->addNestedNodes(std::move(nodePtrs));
+ root->forEachFolderNode(&checkEnabled);
+ setRootProjectNode(std::move(root));
+
+ m_parseGuard.markAsSuccess();
+ m_parseGuard = {};
+
+ emitBuildSystemUpdated();
+ });
+ m_scanner.setDirFilter(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden);
+
+ connect(target()->project(),
+ &Project::projectFileIsDirty,
+ this,
+ &BuildSystem::requestDelayedParse);
+
+ requestDelayedParse();
+ }
+
+ void triggerParsing() final
+ {
+ m_filters.clear();
+ FilePath projectPath = project()->projectDirectory();
+
+ const QJsonObject json = projectDefinition(project()).value_or(QJsonObject());
+ const QJsonValue projectNameValue = json.value(PROJECT_NAME_KEY);
+ if (projectNameValue.isString())
+ project()->setDisplayName(projectNameValue.toString());
+ const QJsonArray excludesJson = json.value(FILES_EXCLUDE_KEY).toArray();
+ for (QJsonValue excludeJson : excludesJson) {
+ if (excludeJson.isString()) {
+ FilePath absolute = projectPath.pathAppended(excludeJson.toString());
+ if (absolute.isDir())
+ absolute = absolute.pathAppended("*");
+ m_filters << QRegularExpression(
+ Utils::wildcardToRegularExpression(absolute.path()),
+ QRegularExpression::CaseInsensitiveOption);
+ }
+ }
+
+ m_parseGuard = guardParsingRun();
+ m_scanner.asyncScanForFiles(target()->project()->projectDirectory());
+ }
+
+ QString name() const final { return QLatin1String("Workspace"); }
+
+private:
+ QList<QRegularExpression> m_filters;
+ ParseGuard m_parseGuard;
+ TreeScanner m_scanner;
+};
+
+class WorkspaceProject : public Project
+{
+ Q_OBJECT
+public:
+ WorkspaceProject(const FilePath file)
+ : Project(FOLDER_MIMETYPE, file.isDir() ? file / ".qtcreator" / "project.json" : file)
+ {
+ QTC_CHECK(projectFilePath().absolutePath().ensureWritableDir());
+ QTC_CHECK(projectFilePath().ensureExistingFile());
+
+ setId(Id::fromString(WORKSPACE_PROJECT_ID));
+ setDisplayName(projectDirectory().fileName());
+ setBuildSystemCreator([](Target *t) { return new WorkspaceBuildSystem(t); });
+ }
+
+ FilePath projectDirectory() const override
+ {
+ return Project::projectDirectory().parentDir();
+ }
+
+ void saveProjectDefinition(const QJsonObject &json)
+ {
+ Utils::FileSaver saver(projectFilePath());
+ saver.write(QJsonDocument(json).toJson());
+ saver.finalize();
+ }
+
+ void excludePath(const FilePath &path)
+ {
+ QTC_ASSERT(projectFilePath().exists(), return);
+ if (expected_str<QJsonObject> json = projectDefinition(this)) {
+ QJsonArray excludes = (*json)[FILES_EXCLUDE_KEY].toArray();
+ const QString relative = path.relativePathFrom(projectDirectory()).path();
+ if (excludes.contains(relative))
+ return;
+ excludes << relative;
+ json->insert(FILES_EXCLUDE_KEY, excludes);
+ saveProjectDefinition(*json);
+ }
+ }
+
+ void excludeNode(Node *node)
+ {
+ node->setEnabled(false);
+ if (auto fileNode = node->asFileNode()) {
+ excludePath(fileNode->path());
+ } else if (auto folderNode = node->asFolderNode()) {
+ folderNode->forEachNode([](Node *node) { node->setEnabled(false); });
+ excludePath(folderNode->path());
+ }
+ }
+};
+
+void setupWorkspaceProject(QObject *guard)
+{
+ ProjectManager::registerProjectType<WorkspaceProject>(FOLDER_MIMETYPE);
+ ProjectManager::registerProjectType<WorkspaceProject>(WORKSPACE_MIMETYPE);
+
+ QObject::connect(
+ ProjectTree::instance(),
+ &ProjectTree::aboutToShowContextMenu,
+ ProjectExplorerPlugin::instance(),
+ [](Node *node) {
+ const bool enabled = node && node->isEnabled()
+ && qobject_cast<WorkspaceProject *>(node->getProject());
+ ActionManager::command(Id::fromString(EXCLUDE_ACTION_ID))->action()->setEnabled(enabled);
+ });
+
+ ActionBuilder excludeAction(guard, Id::fromString(EXCLUDE_ACTION_ID));
+ excludeAction.setContext(Id::fromString(WORKSPACE_PROJECT_ID));
+ excludeAction.setText(Tr::tr("Exclude from Project"));
+ excludeAction.addToContainer(Constants::M_FOLDERCONTEXT, Constants::G_FOLDER_OTHER);
+ excludeAction.addToContainer(Constants::M_FILECONTEXT, Constants::G_FILE_OTHER);
+ excludeAction.addOnTriggered([] {
+ Node *node = ProjectTree::currentNode();
+ QTC_ASSERT(node, return);
+ const auto project = qobject_cast<WorkspaceProject *>(node->getProject());
+ QTC_ASSERT(project, return);
+ project->excludeNode(node);
+ });
+}
+
+} // namespace ProjectExplorer
+
+#include "workspaceproject.moc"
diff --git a/src/plugins/projectexplorer/workspaceproject.h b/src/plugins/projectexplorer/workspaceproject.h
new file mode 100644
index 0000000000..3fe26a8c8e
--- /dev/null
+++ b/src/plugins/projectexplorer/workspaceproject.h
@@ -0,0 +1,16 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <qglobal.h>
+
+QT_BEGIN_NAMESPACE
+class QObject;
+QT_END_NAMESPACE
+
+namespace ProjectExplorer {
+
+void setupWorkspaceProject(QObject *guard);
+
+} // namespace ProjectExplorer
diff --git a/src/plugins/python/Python.json.in b/src/plugins/python/Python.json.in
index 5dfb4c6d49..9ac09e4a3b 100644
--- a/src/plugins/python/Python.json.in
+++ b/src/plugins/python/Python.json.in
@@ -14,7 +14,7 @@
],
"Category" : "Other Languages",
"Description" : "Plugin for supporting the Python language.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/python/pythonbuildconfiguration.cpp b/src/plugins/python/pythonbuildconfiguration.cpp
index 1255af1442..a64020083a 100644
--- a/src/plugins/python/pythonbuildconfiguration.cpp
+++ b/src/plugins/python/pythonbuildconfiguration.cpp
@@ -30,8 +30,6 @@
#include <projectexplorer/runconfiguration.h>
#include <projectexplorer/target.h>
-#include <extensionsystem/pluginmanager.h>
-
#include <utils/algorithm.h>
#include <utils/commandline.h>
#include <utils/detailswidget.h>
@@ -110,7 +108,7 @@ void PySideBuildStep::checkForPySide(const FilePath &python, const QString &pySi
});
const auto future = Pip::instance(python)->info(package);
m_watcher->setFuture(future);
- ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(future);
+ Utils::futureSynchronizer()->addFuture(future);
}
void PySideBuildStep::handlePySidePackageInfo(const PipPackageInfo &pySideInfo,
diff --git a/src/plugins/python/pythonkitaspect.cpp b/src/plugins/python/pythonkitaspect.cpp
index 0be12cead0..1b0004fd78 100644
--- a/src/plugins/python/pythonkitaspect.cpp
+++ b/src/plugins/python/pythonkitaspect.cpp
@@ -73,7 +73,7 @@ public:
}
protected:
- void addToLayoutImpl(Layouting::LayoutItem &parent) override
+ void addToLayoutImpl(Layouting::Layout &parent) override
{
addMutableAction(m_comboBox);
parent.addItem(m_comboBox);
diff --git a/src/plugins/python/pythonlanguageclient.cpp b/src/plugins/python/pythonlanguageclient.cpp
index 2379ffb1a1..3c05b8f87e 100644
--- a/src/plugins/python/pythonlanguageclient.cpp
+++ b/src/plugins/python/pythonlanguageclient.cpp
@@ -151,7 +151,7 @@ PyLSClient *clientForPython(const FilePath &python)
if (auto client = pythonClients()[python])
return client;
auto interface = new PyLSInterface;
- interface->setCommandLine(CommandLine(python, {"-m", "pylsp"}));
+ interface->setCommandLine({python, {"-m", "pylsp"}});
auto client = new PyLSClient(interface);
client->setName(Tr::tr("Python Language Server (%1)").arg(python.toUserOutput()));
client->setActivateDocumentAutomatically(true);
diff --git a/src/plugins/python/pythonrunconfiguration.cpp b/src/plugins/python/pythonrunconfiguration.cpp
index d01f4bc9bb..e4ec5a9e84 100644
--- a/src/plugins/python/pythonrunconfiguration.cpp
+++ b/src/plugins/python/pythonrunconfiguration.cpp
@@ -3,14 +3,8 @@
#include "pythonrunconfiguration.h"
-#include "pyside.h"
-#include "pythonbuildconfiguration.h"
#include "pythonconstants.h"
-#include "pythoneditor.h"
-#include "pythonkitaspect.h"
-#include "pythonlanguageclient.h"
#include "pythonproject.h"
-#include "pythonsettings.h"
#include "pythontr.h"
#include <coreplugin/editormanager/editormanager.h>
@@ -18,31 +12,16 @@
#include <debugger/debuggerruncontrol.h>
-#include <extensionsystem/pluginmanager.h>
-
-#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/buildsystem.h>
-#include <projectexplorer/devicesupport/idevice.h>
-#include <projectexplorer/kitaspects.h>
#include <projectexplorer/runconfiguration.h>
#include <projectexplorer/runconfigurationaspects.h>
#include <projectexplorer/runcontrol.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
-#include <texteditor/textdocument.h>
-
#include <utils/aspects.h>
#include <utils/fileutils.h>
-#include <utils/futuresynchronizer.h>
-#include <utils/layoutbuilder.h>
#include <utils/outputformatter.h>
-#include <utils/qtcassert.h>
-#include <utils/theme/theme.h>
-
-#include <QComboBox>
-#include <QPlainTextEdit>
-#include <QPushButton>
using namespace ProjectExplorer;
using namespace Utils;
diff --git a/src/plugins/python/pythonsettings.cpp b/src/plugins/python/pythonsettings.cpp
index cad296ae5d..bfc411c6c2 100644
--- a/src/plugins/python/pythonsettings.cpp
+++ b/src/plugins/python/pythonsettings.cpp
@@ -751,11 +751,9 @@ PythonSettings::PythonSettings()
initFromSettings(Core::ICore::settings());
const auto onRegistrySetup = [](Async<QList<Interpreter>> &task) {
- task.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
task.setConcurrentCallData(pythonsFromRegistry);
};
const auto onPathSetup = [](Async<QList<Interpreter>> &task) {
- task.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
task.setConcurrentCallData(pythonsFromPath);
};
const auto onTaskDone = [](const Async<QList<Interpreter>> &task) {
diff --git a/src/plugins/python/pythonutils.cpp b/src/plugins/python/pythonutils.cpp
index 2e4cd4ed58..87156e3b9c 100644
--- a/src/plugins/python/pythonutils.cpp
+++ b/src/plugins/python/pythonutils.cpp
@@ -121,11 +121,9 @@ void openPythonRepl(QObject *parent, const FilePath &file, ReplType type)
return file.absolutePath();
};
- const auto args = QStringList{"-i"} + replImportArgs(file, type);
const FilePath pythonCommand = detectPython(file);
-
Process process;
- process.setCommand({pythonCommand, args});
+ process.setCommand({pythonCommand, {"-i", replImportArgs(file, type)}});
process.setWorkingDirectory(workingDir(file));
process.setTerminalMode(TerminalMode::Detached);
process.start();
@@ -201,7 +199,7 @@ static bool isUsableHelper(QHash<FilePath, bool> *cache, const QString &keyStrin
if (it == cache->end()) {
const Key key = keyFromString(keyString);
Process process;
- process.setCommand({python, QStringList{"-m", commandArg, "-h"}});
+ process.setCommand({python, {"-m", commandArg, "-h"}});
process.runBlocking();
const bool usable = process.result() == ProcessResult::FinishedWithSuccess;
it = cache->insert(python, usable);
diff --git a/src/plugins/qbsprojectmanager/QbsProjectManager.json.in b/src/plugins/qbsprojectmanager/QbsProjectManager.json.in
index 263722d28d..a82be68b0a 100644
--- a/src/plugins/qbsprojectmanager/QbsProjectManager.json.in
+++ b/src/plugins/qbsprojectmanager/QbsProjectManager.json.in
@@ -14,6 +14,6 @@
],
"Category" : "Build Systems",
"Description" : "QBS support.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/qbsprojectmanager/customqbspropertiesdialog.cpp b/src/plugins/qbsprojectmanager/customqbspropertiesdialog.cpp
index 78e0634bc5..ad8c5d7995 100644
--- a/src/plugins/qbsprojectmanager/customqbspropertiesdialog.cpp
+++ b/src/plugins/qbsprojectmanager/customqbspropertiesdialog.cpp
@@ -50,7 +50,7 @@ CustomQbsPropertiesDialog::CustomQbsPropertiesDialog(const QVariantMap &properti
Column {
PushButton {
text(Tr::tr("&Add")),
- onClicked([this] { addProperty(); } ),
+ onClicked([this] { addProperty(); }, nullptr),
},
m_removeButton,
st
diff --git a/src/plugins/qbsprojectmanager/qbsbuildstep.cpp b/src/plugins/qbsprojectmanager/qbsbuildstep.cpp
index 71cc48aaf8..9d0a9f642c 100644
--- a/src/plugins/qbsprojectmanager/qbsbuildstep.cpp
+++ b/src/plugins/qbsprojectmanager/qbsbuildstep.cpp
@@ -60,7 +60,7 @@ ArchitecturesAspect::ArchitecturesAspect(AspectContainer *container)
setAllValues(m_abisToArchMap.keys());
}
-void ArchitecturesAspect::addToLayout(Layouting::LayoutItem &parent)
+void ArchitecturesAspect::addToLayout(Layouting::Layout &parent)
{
MultiSelectionAspect::addToLayout(parent);
const auto changeHandler = [this] {
diff --git a/src/plugins/qbsprojectmanager/qbsbuildstep.h b/src/plugins/qbsprojectmanager/qbsbuildstep.h
index 4815a96a85..37ed218187 100644
--- a/src/plugins/qbsprojectmanager/qbsbuildstep.h
+++ b/src/plugins/qbsprojectmanager/qbsbuildstep.h
@@ -20,7 +20,7 @@ public:
ArchitecturesAspect(Utils::AspectContainer *container = nullptr);
void setKit(const ProjectExplorer::Kit *kit) { m_kit = kit; }
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
QStringList selectedArchitectures() const;
void setSelectedArchitectures(const QStringList& architectures);
bool isManagedByTarget() const { return m_isManagedByTarget; }
diff --git a/src/plugins/qbsprojectmanager/qbskitaspect.cpp b/src/plugins/qbsprojectmanager/qbskitaspect.cpp
index 647f44f02c..adc35c2f3f 100644
--- a/src/plugins/qbsprojectmanager/qbskitaspect.cpp
+++ b/src/plugins/qbsprojectmanager/qbskitaspect.cpp
@@ -34,7 +34,7 @@ private:
void makeReadOnly() override { m_changeButton->setEnabled(false); }
void refresh() override { m_contentLabel->setText(QbsKitAspect::representation(kit())); }
- void addToLayoutImpl(Layouting::LayoutItem &parent) override
+ void addToLayoutImpl(Layouting::Layout &parent) override
{
addMutableAction(m_contentLabel);
parent.addItem(m_contentLabel);
diff --git a/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp b/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp
index 3cfd68d5ce..01dd5efb4f 100644
--- a/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp
+++ b/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp
@@ -60,7 +60,7 @@ QString toJSLiteral(const QVariant &val)
{
if (!val.isValid())
return QString("undefined");
- if (val.typeId() == QVariant::List || val.typeId() == QVariant::StringList) {
+ if (val.typeId() == QMetaType::QVariantList || val.typeId() == QMetaType::QStringList) {
QString res;
const auto list = val.toList();
for (const QVariant &child : list) {
@@ -71,7 +71,7 @@ QString toJSLiteral(const QVariant &val)
res.append(']');
return res;
}
- if (val.typeId() == QVariant::Map) {
+ if (val.typeId() == QMetaType::QVariantMap) {
const QVariantMap &vm = val.toMap();
QString str("{");
for (auto it = vm.begin(); it != vm.end(); ++it) {
@@ -84,7 +84,7 @@ QString toJSLiteral(const QVariant &val)
}
if (val.typeId() == QVariant::Bool)
return toJSLiteral(val.toBool());
- if (val.canConvert(QVariant::String))
+ if (val.canConvert(QMetaType::QString))
return toJSLiteral(val.toString());
return QString::fromLatin1("Unconvertible type %1").arg(QLatin1String(val.typeName()));
}
diff --git a/src/plugins/qbsprojectmanager/qbsprofilessettingspage.cpp b/src/plugins/qbsprojectmanager/qbsprofilessettingspage.cpp
index fb048f9475..9b25c17d34 100644
--- a/src/plugins/qbsprojectmanager/qbsprofilessettingspage.cpp
+++ b/src/plugins/qbsprojectmanager/qbsprofilessettingspage.cpp
@@ -133,11 +133,11 @@ QbsProfilesSettingsWidget::QbsProfilesSettingsWidget()
Column {
PushButton {
text(Tr::tr("E&xpand All")),
- onClicked([this] { m_propertiesView->expandAll(); }),
+ onClicked([this] { m_propertiesView->expandAll(); }, nullptr),
},
PushButton {
text(Tr::tr("&Collapse All")),
- onClicked([this] { m_propertiesView->collapseAll(); }),
+ onClicked([this] { m_propertiesView->collapseAll(); }, nullptr),
},
st,
},
diff --git a/src/plugins/qbsprojectmanager/qbssession.cpp b/src/plugins/qbsprojectmanager/qbssession.cpp
index b2801acfa2..193d039605 100644
--- a/src/plugins/qbsprojectmanager/qbssession.cpp
+++ b/src/plugins/qbsprojectmanager/qbssession.cpp
@@ -135,12 +135,14 @@ public:
QbsLanguageClient *languageClient = nullptr;
PacketReader *packetReader = nullptr;
QJsonObject currentRequest;
+ QList<QJsonObject> queuedFileUpdateRequests;
QJsonObject projectData;
QEventLoop eventLoop;
QJsonObject reply;
QHash<QString, QStringList> generatedFilesForSources;
std::optional<Error> lastError;
State state = State::Inactive;
+ bool fileUpdatePossible = true;
};
QbsSession::QbsSession(QbsBuildSystem *buildSystem) : QObject(buildSystem), d(new Private)
@@ -465,6 +467,8 @@ void QbsSession::handlePacket(const QJsonObject &packet)
} else if (type == "project-resolved") {
setProjectDataFromReply(packet, true);
emit projectResolved(getErrorInfo(packet));
+ d->fileUpdatePossible = true;
+ sendNextPendingFileUpdateRequest();
} else if (type == "project-built") {
setProjectDataFromReply(packet, false);
emit projectBuilt(getErrorInfo(packet));
@@ -586,17 +590,21 @@ FileChangeResult QbsSession::updateFileList(const char *action, const QStringLis
{
if (d->state != State::Active)
return FileChangeResult(files, Tr::tr("The qbs session is not in a valid state."));
- sendRequestNow(QJsonObject{
+ const QJsonObject fileUpdateRequest{
{"type", QLatin1String(action)},
{"files", QJsonArray::fromStringList(files)},
{"product", product},
- {"group", group}
- });
+ {"group", group}};
+ if (d->fileUpdatePossible)
+ sendFileUpdateRequest(fileUpdateRequest);
+ else
+ d->queuedFileUpdateRequests << fileUpdateRequest;
return FileChangeResult(QStringList());
}
void QbsSession::handleFileListUpdated(const QJsonObject &reply)
{
+ QTC_CHECK(!d->fileUpdatePossible);
setProjectDataFromReply(reply, false);
const QStringList failedFiles = arrayToStringList(reply.value("failed-files"));
if (!failedFiles.isEmpty()) {
@@ -604,10 +612,24 @@ void QbsSession::handleFileListUpdated(const QJsonObject &reply)
Tr::tr("Failed to update files in Qbs project: %1.\n"
"The affected files are: \n\t%2")
.arg(getErrorInfo(reply).toString(), failedFiles.join("\n\t")));
+ d->fileUpdatePossible = true;
+ sendNextPendingFileUpdateRequest();
}
emit fileListUpdated();
}
+void QbsSession::sendNextPendingFileUpdateRequest()
+{
+ if (!d->queuedFileUpdateRequests.isEmpty())
+ sendFileUpdateRequest(d->queuedFileUpdateRequests.takeFirst());
+}
+
+void QbsSession::sendFileUpdateRequest(const QJsonObject &request)
+{
+ d->fileUpdatePossible = false;
+ sendRequestNow(request);
+}
+
ErrorInfoItem::ErrorInfoItem(const QJsonObject &data)
{
description = data.value("description").toString();
diff --git a/src/plugins/qbsprojectmanager/qbssession.h b/src/plugins/qbsprojectmanager/qbssession.h
index 670388a5bb..88c008dc16 100644
--- a/src/plugins/qbsprojectmanager/qbssession.h
+++ b/src/plugins/qbsprojectmanager/qbssession.h
@@ -173,6 +173,8 @@ private:
FileChangeResult updateFileList(const char *action, const QStringList &files,
const QString &product, const QString &group);
void handleFileListUpdated(const QJsonObject &reply);
+ void sendNextPendingFileUpdateRequest();
+ void sendFileUpdateRequest(const QJsonObject &request);
class Private;
Private * const d;
diff --git a/src/plugins/qmakeprojectmanager/QmakeProjectManager.json.in b/src/plugins/qmakeprojectmanager/QmakeProjectManager.json.in
index a080d69dec..e5d14d0ef3 100644
--- a/src/plugins/qmakeprojectmanager/QmakeProjectManager.json.in
+++ b/src/plugins/qmakeprojectmanager/QmakeProjectManager.json.in
@@ -14,7 +14,7 @@
],
"Category" : "Build Systems",
"Description" : "Provides project type for Qt/QMake .pro files and tools.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwizarddialog.cpp b/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwizarddialog.cpp
index d8d742267c..49ff339f45 100644
--- a/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwizarddialog.cpp
+++ b/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwizarddialog.cpp
@@ -28,8 +28,8 @@ CustomWidgetWizardDialog::CustomWidgetWizardDialog(const Core::BaseFileWizardFac
setWindowIcon(icon);
setWindowTitle(templateName);
- setIntroDescription(Tr::tr("This wizard generates a Qt Designer Custom Widget "
- "or a Qt Designer Custom Widget Collection project."));
+ setIntroDescription(Tr::tr("This wizard generates a Qt Widgets Designer Custom Widget "
+ "or a Qt Widgets Designer Custom Widget Collection project."));
if (!parameters.extraValues().contains(QLatin1String(ProjectExplorer::Constants::PROJECT_KIT_IDS)))
addTargetSetupPage();
diff --git a/src/plugins/qmakeprojectmanager/qmakekitaspect.cpp b/src/plugins/qmakeprojectmanager/qmakekitaspect.cpp
index d8e1e41648..c29c8da43b 100644
--- a/src/plugins/qmakeprojectmanager/qmakekitaspect.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakekitaspect.cpp
@@ -39,7 +39,7 @@ public:
~QmakeKitAspectImpl() override { delete m_lineEdit; }
private:
- void addToLayoutImpl(Layouting::LayoutItem &parent) override
+ void addToLayoutImpl(Layouting::Layout &parent) override
{
addMutableAction(m_lineEdit);
parent.addItem(m_lineEdit);
diff --git a/src/plugins/qmakeprojectmanager/qmakestep.cpp b/src/plugins/qmakeprojectmanager/qmakestep.cpp
index e012499b54..d1de44d1f5 100644
--- a/src/plugins/qmakeprojectmanager/qmakestep.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakestep.cpp
@@ -188,7 +188,7 @@ bool QMakeStep::init()
else
workingDirectory = qmakeBc->buildDirectory();
- m_qmakeCommand = CommandLine{qtVersion->qmakeFilePath(), allArguments(qtVersion), CommandLine::Raw};
+ m_qmakeCommand = {qtVersion->qmakeFilePath(), allArguments(qtVersion), CommandLine::Raw};
m_runMakeQmake = (qtVersion->qtVersion() >= QVersionNumber(5, 0 ,0));
// The Makefile is used by qmake and make on the build device, from that
@@ -216,7 +216,7 @@ bool QMakeStep::init()
OutputFormat::ErrorMessage);
return false;
}
- m_makeCommand = CommandLine{make, makeArguments(makeFile.path()), CommandLine::Raw};
+ m_makeCommand = {make, makeArguments(makeFile.path()), CommandLine::Raw};
} else {
m_makeCommand = {};
}
@@ -428,7 +428,7 @@ QWidget *QMakeStep::createConfigWidget()
builder.addRow({userArguments});
builder.addRow({effectiveCall});
builder.addRow({abisLabel, abisListWidget});
- builder.addItem(Layouting::noMargin);
+ builder.noMargin();
auto widget = builder.emerge();
qmakeBuildConfigChanged();
diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt
index 0b0075915f..c29017523d 100644
--- a/src/plugins/qmldesigner/CMakeLists.txt
+++ b/src/plugins/qmldesigner/CMakeLists.txt
@@ -27,6 +27,10 @@ env_with_default("QTC_ENABLE_MODEL_TRACING" ENV_QTC_ENABLE_MODEL_TRACING OFF)
option(ENABLE_MODEL_TRACING "Enable model tracing" ${ENV_QTC_ENABLE_MODEL_TRACING})
add_feature_info("Model tracing" ${ENABLE_MODEL_TRACING} "")
+env_with_default("QTC_ENABLE_METAINFO_TRACING" ENV_QTC_ENABLE_METAINFO_TRACING OFF)
+option(ENABLE_METAINFO_TRACING "Enable meta info tracing" ${ENV_QTC_ENABLE_METAINFO_TRACING})
+add_feature_info("Meta info tracing" ${ENABLE_METAINFO_TRACING} "")
+
add_qtc_library(QmlDesignerUtils STATIC
DEPENDS
Qt::Gui Utils Qt::QmlPrivate
@@ -93,14 +97,13 @@ add_qtc_library(QmlDesignerCore STATIC
${CMAKE_CURRENT_LIST_DIR}/designercore/include
SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore
SOURCES
- rewritertransaction.cpp
- rewritertransaction.h
- generatedcomponentutils.cpp
- generatedcomponentutils.h
+ rewritertransaction.cpp rewritertransaction.h
+ generatedcomponentutils.cpp generatedcomponentutils.h
+ uniquename.cpp uniquename.h
)
extend_qtc_library(QmlDesignerCore
- CONDITION ENABLE_PROJECT_STORAGE_TRACING OR ENABLE_IMAGE_CACHE_TRACING OR ENABLE_MODEL_TRACING
+ CONDITION ENABLE_PROJECT_STORAGE_TRACING OR ENABLE_IMAGE_CACHE_TRACING OR ENABLE_MODEL_TRACING OR ENABLE_METAINFO_TRACING
PUBLIC_DEPENDS Nanotrace
PUBLIC_DEFINES
ENABLE_QMLDESIGNER_TRACING
@@ -108,6 +111,7 @@ extend_qtc_library(QmlDesignerCore
$<$<BOOL:${ENABLE_PROJECT_STORAGE_TRACING}>:ENABLE_PROJECT_STORAGE_TRACING>
$<$<BOOL:${ENABLE_IMAGE_CACHE_TRACING}>:ENABLE_IMAGE_CACHE_TRACING>
$<$<BOOL:${ENABLE_MODEL_TRACING}>:ENABLE_MODEL_TRACING>
+ $<$<BOOL:${ENABLE_METAINFO_TRACING}>:ENABLE_METAINFO_TRACING>
)
extend_qtc_library(QmlDesignerCore
@@ -463,6 +467,8 @@ extend_qtc_library(QmlDesignerCore
projectstoragetypes.h
projectstorageupdater.cpp projectstorageupdater.h
projectstorage.cpp projectstorage.h
+ projectstorageerrornotifierinterface.h
+ projectstorageerrornotifier.cpp projectstorageerrornotifier.h
sourcepath.h
sourcepathcache.h
sourcepathcacheinterface.h
@@ -498,7 +504,6 @@ add_qtc_plugin(QmlDesigner
INCLUDES
${CMAKE_CURRENT_LIST_DIR}/components
${CMAKE_CURRENT_LIST_DIR}/components/assetslibrary
- ${CMAKE_CURRENT_LIST_DIR}/components/collectioneditor
${CMAKE_CURRENT_LIST_DIR}/components/debugview
${CMAKE_CURRENT_LIST_DIR}/components/edit3d
${CMAKE_CURRENT_LIST_DIR}/components/formeditor
@@ -739,6 +744,8 @@ extend_qtc_plugin(QmlDesigner
assetimportupdatetreeitemdelegate.cpp assetimportupdatetreeitemdelegate.h
assetimportupdatetreemodel.cpp assetimportupdatetreemodel.h
assetimportupdatetreeview.cpp assetimportupdatetreeview.h
+ import3dcanvas.cpp import3dcanvas.h
+ import3dconnectionmanager.cpp import3dconnectionmanager.h
itemlibrary.qrc
itemlibraryconstants.h
itemlibraryimageprovider.cpp itemlibraryimageprovider.h
@@ -826,7 +833,7 @@ extend_qtc_plugin(QmlDesigner
contentlibrarymaterialscategory.cpp contentlibrarymaterialscategory.h
contentlibrarymaterial.cpp contentlibrarymaterial.h
contentlibraryiconprovider.cpp contentlibraryiconprovider.h
- contentlibraryeffect.cpp contentlibraryeffect.h
+ contentlibraryitem.cpp contentlibraryitem.h
contentlibraryeffectscategory.cpp contentlibraryeffectscategory.h
contentlibraryeffectsmodel.cpp contentlibraryeffectsmodel.h
contentlibraryusermodel.cpp contentlibraryusermodel.h
@@ -844,21 +851,6 @@ extend_qtc_plugin(QmlDesigner
)
extend_qtc_plugin(QmlDesigner
- SOURCES_PREFIX components/collectioneditor
- SOURCES
- collectiondatatypemodel.cpp collectiondatatypemodel.h
- collectiondetails.cpp collectiondetails.h
- collectiondetailsmodel.cpp collectiondetailsmodel.h
- collectiondetailssortfiltermodel.cpp collectiondetailssortfiltermodel.h
- collectioneditorconstants.h
- collectioneditorutils.cpp collectioneditorutils.h
- collectionlistmodel.cpp collectionlistmodel.h
- collectionview.cpp collectionview.h
- collectionwidget.cpp collectionwidget.h
- datastoremodelnode.cpp datastoremodelnode.h
-)
-
-extend_qtc_plugin(QmlDesigner
SOURCES_PREFIX components/textureeditor
SOURCES
textureeditorcontextobject.cpp textureeditorcontextobject.h
diff --git a/src/plugins/qmldesigner/QmlDesigner.json.in b/src/plugins/qmldesigner/QmlDesigner.json.in
index 4b5682f485..e96f2f8513 100644
--- a/src/plugins/qmldesigner/QmlDesigner.json.in
+++ b/src/plugins/qmldesigner/QmlDesigner.json.in
@@ -15,7 +15,7 @@
"Category" : "Qt Quick",
"Description" : "Visual Designer for QML files.",
"Deprecated" : true,
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
"Arguments" : [
{
"Name" : "-capture-puppet-stream",
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp
index a4509c38a6..cee25240cf 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp
@@ -143,7 +143,7 @@ AssetExportDialog::AssetExportDialog(const FilePath &exportPath,
m_exportAssetsCheck,
m_perComponentExportCheck,
st,
- noMargin(),
+ noMargin,
}.attachTo(optionsWidget);
Column {
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp
index 9d09f52d8f..7f488bd615 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp
@@ -3,12 +3,15 @@
#include "assetslibrarymodel.h"
-#include <asset.h>
#include <modelnodeoperations.h>
#include <qmldesignerplugin.h>
+#include <uniquename.h>
#include <coreplugin/icore.h>
+
#include <utils/algorithm.h>
+#include <utils/asset.h>
+#include <utils/filepath.h>
#include <utils/filesystemwatcher.h>
#include <QFileInfo>
@@ -151,16 +154,15 @@ bool AssetsLibraryModel::renameFolder(const QString &folderPath, const QString &
QString AssetsLibraryModel::addNewFolder(const QString &folderPath)
{
- QString iterPath = folderPath;
- QDir dir{folderPath};
+ Utils::FilePath uniqueDirPath = Utils::FilePath::fromString(UniqueName::generatePath(folderPath));
- while (dir.exists()) {
- iterPath = getUniqueName(iterPath);
-
- dir.setPath(iterPath);
+ auto res = uniqueDirPath.ensureWritableDir();
+ if (!res.has_value()) {
+ qWarning() << __FUNCTION__ << res.error();
+ return {};
}
- return dir.mkpath(iterPath) ? iterPath : "";
+ return uniqueDirPath.path();
}
bool AssetsLibraryModel::urlPathExistsInModel(const QUrl &url) const
@@ -242,36 +244,6 @@ void AssetsLibraryModel::syncHasFiles()
setHasFiles(checkHasFiles());
}
-QString AssetsLibraryModel::getUniqueName(const QString &oldName) {
- static QRegularExpression rgx("\\d+$"); // matches a number at the end of a string
-
- QString uniqueName = oldName;
- // if the folder name ends with a number, increment it
- QRegularExpressionMatch match = rgx.match(uniqueName);
- if (match.hasMatch()) { // ends with a number
- QString numStr = match.captured(0);
- int num = match.captured(0).toInt();
-
- // get number of padding zeros, ex: for "005" = 2
- int nPaddingZeros = 0;
- for (; nPaddingZeros < numStr.size() && numStr[nPaddingZeros] == '0'; ++nPaddingZeros);
-
- ++num;
-
- // if the incremented number's digits increased, decrease the padding zeros
- if (std::fmod(std::log10(num), 1.0) == 0)
- --nPaddingZeros;
-
- uniqueName = oldName.mid(0, match.capturedStart())
- + QString('0').repeated(nPaddingZeros)
- + QString::number(num);
- } else {
- uniqueName = oldName + '1';
- }
-
- return uniqueName;
-}
-
void AssetsLibraryModel::setRootPath(const QString &newPath)
{
beginResetModel();
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h
index 2516be787f..f08578651a 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h
@@ -58,8 +58,6 @@ public:
bool hasFiles() const { return m_hasFiles; }
- QString getUniqueName(const QString &oldName);
-
signals:
void directoryLoaded(const QString &path);
void rootPathChanged();
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp
index 4b270c8902..5e2211ce0d 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp
@@ -7,7 +7,6 @@
#include "assetslibrarymodel.h"
#include "assetslibraryview.h"
-#include <asset.h>
#include <designeractionmanager.h>
#include <designerpaths.h>
#include <hdrimage.h>
@@ -18,6 +17,7 @@
#include <qmldesignerplugin.h>
#include <studioquickwidget.h>
#include <theme.h>
+#include <uniquename.h>
#include <utils3d.h>
#include <coreplugin/fileutils.h>
@@ -25,6 +25,7 @@
#include <coreplugin/messagebox.h>
#include <utils/algorithm.h>
+#include <utils/asset.h>
#include <utils/environment.h>
#include <utils/filepath.h>
#include <utils/qtcassert.h>
@@ -94,7 +95,7 @@ AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFon
, m_assetsModel{new AssetsLibraryModel(this)}
, m_assetsView{view}
, m_createTextures{view}
- , m_assetsWidget{new StudioQuickWidget(this)}
+ , m_assetsWidget{Utils::makeUniqueObjectPtr<StudioQuickWidget>(this)}
{
setWindowTitle(tr("Assets Library", "Title of assets library widget"));
setMinimumWidth(250);
@@ -130,7 +131,7 @@ AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFon
auto layout = new QVBoxLayout(this);
layout->setContentsMargins({});
layout->setSpacing(0);
- layout->addWidget(m_assetsWidget.data());
+ layout->addWidget(m_assetsWidget.get());
updateSearch();
@@ -174,23 +175,10 @@ void AssetsLibraryWidget::deleteSelectedAssets()
QString AssetsLibraryWidget::getUniqueEffectPath(const QString &parentFolder, const QString &effectName)
{
- auto genEffectPath = [&parentFolder](const QString &name) {
- QString effectsDir = ModelNodeOperations::getEffectsDefaultDirectory(parentFolder);
- return QLatin1String("%1/%2.qep").arg(effectsDir, name);
- };
+ QString effectsDir = ModelNodeOperations::getEffectsDefaultDirectory(parentFolder);
+ QString effectPath = QLatin1String("%1/%2.qep").arg(effectsDir, effectName);
- QString uniqueName = effectName;
- QString path = genEffectPath(uniqueName);
- QFileInfo file{path};
-
- while (file.exists()) {
- uniqueName = m_assetsModel->getUniqueName(uniqueName);
-
- path = genEffectPath(uniqueName);
- file.setFile(path);
- }
-
- return path;
+ return UniqueName::generatePath(effectPath);
}
bool AssetsLibraryWidget::createNewEffect(const QString &effectPath, bool openInEffectComposer)
@@ -647,12 +635,6 @@ void AssetsLibraryWidget::addResources(const QStringList &files, bool showDialog
}
}
-bool AssetsLibraryWidget::userBundleEnabled() const
-{
- // TODO: this method is to be removed after user bundle implementation is complete
- return Core::ICore::settings()->value("QML/Designer/UseExperimentalFeatures45", false).toBool();
-}
-
void AssetsLibraryWidget::addAssetsToContentLibrary(const QStringList &assetPaths)
{
m_assetsView->emitCustomNotification("add_assets_to_content_lib", {}, {assetPaths});
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h
index 8b59ae0785..f2d476c842 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h
@@ -8,6 +8,8 @@
#include <coreplugin/icontext.h>
+#include <utils/uniqueobjectptr.h>
+
#include <QFrame>
#include <QQmlPropertyMap>
#include <QQuickWidget>
@@ -98,7 +100,6 @@ public:
Q_INVOKABLE void showInGraphicalShell(const QString &path);
Q_INVOKABLE QString showInGraphicalShellMsg() const;
- Q_INVOKABLE bool userBundleEnabled() const;
Q_INVOKABLE void addAssetsToContentLibrary(const QStringList &assetPaths);
signals:
@@ -137,7 +138,7 @@ private:
AssetsLibraryView *m_assetsView = nullptr;
CreateTextures m_createTextures = nullptr;
- QScopedPointer<StudioQuickWidget> m_assetsWidget;
+ Utils::UniqueObjectPtr<StudioQuickWidget> m_assetsWidget;
std::unique_ptr<PreviewTooltipBackend> m_fontPreviewTooltipBackend;
QShortcut *m_qmlSourceUpdateShortcut = nullptr;
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp
deleted file mode 100644
index 9a534ec88e..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "collectiondatatypemodel.h"
-
-#include <QHash>
-#include <QtQml/QmlTypeAndRevisionsRegistration>
-
-namespace QmlDesigner {
-
-struct CollectionDataTypeModel::Details
-{
- CollectionDetails::DataType type;
- QString name;
- QString description;
-};
-
-const QList<CollectionDataTypeModel::Details> CollectionDataTypeModel::m_orderedDetails{
- {DataType::String, "String", "Text"},
- {DataType::Integer, "Integer", "Whole number that can be positive, negative, or zero"},
- {DataType::Real, "Real", "Number with a decimal"},
- {DataType::Image, "Image", "Image resource"},
- {DataType::Color, "Color", "HEX value"},
- {DataType::Url, "Url", "Resource locator"},
- {DataType::Boolean, "Boolean", "True/false"},
-};
-
-CollectionDataTypeModel::CollectionDataTypeModel(QObject *parent)
- : QAbstractListModel(parent)
-{
-}
-
-int CollectionDataTypeModel::rowCount([[maybe_unused]] const QModelIndex &parent) const
-{
- return m_orderedDetails.size();
-}
-
-QVariant CollectionDataTypeModel::data(const QModelIndex &index, int role) const
-{
- if (!index.isValid())
- return {};
-
- if (role == Qt::DisplayRole)
- return m_orderedDetails.at(index.row()).name;
- if (role == Qt::ToolTipRole)
- return m_orderedDetails.at(index.row()).description;
-
- return {};
-}
-
-QString CollectionDataTypeModel::dataTypeToString(DataType dataType)
-{
- static const QHash<DataType, QString> dataTypeHash = []() -> QHash<DataType, QString> {
- QHash<DataType, QString> result;
- for (const Details &details : m_orderedDetails)
- result.insert(details.type, details.name);
- return result;
- }();
-
- if (dataTypeHash.contains(dataType))
- return dataTypeHash.value(dataType);
-
- return "Unknown";
-}
-
-CollectionDetails::DataType CollectionDataTypeModel::dataTypeFromString(const QString &dataType)
-{
- static const QHash<QString, DataType> stringTypeHash = []() -> QHash<QString, DataType> {
- QHash<QString, DataType> result;
- for (const Details &details : m_orderedDetails)
- result.insert(details.name, details.type);
- return result;
- }();
-
- if (stringTypeHash.contains(dataType))
- return stringTypeHash.value(dataType);
-
- return DataType::String;
-}
-
-void CollectionDataTypeModel::registerDeclarativeType()
-{
- qmlRegisterType<CollectionDataTypeModel>("CollectionDetails", 1, 0, "CollectionDataTypeModel");
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h
deleted file mode 100644
index 1f91aecbff..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "collectiondetails.h"
-
-#include <QAbstractListModel>
-#include <QList>
-
-namespace QmlDesigner {
-
-class CollectionDataTypeModel : public QAbstractListModel
-{
- Q_OBJECT
-
-public:
- using DataType = CollectionDetails::DataType;
- CollectionDataTypeModel(QObject *parent = nullptr);
-
- int rowCount(const QModelIndex &parent = QModelIndex()) const override;
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
-
- static Q_INVOKABLE QString dataTypeToString(DataType dataType);
- static Q_INVOKABLE DataType dataTypeFromString(const QString &dataType);
-
- static void registerDeclarativeType();
-
-private:
- struct Details;
- static const QList<Details> m_orderedDetails;
-};
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp
deleted file mode 100644
index a391d9bb1f..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp
+++ /dev/null
@@ -1,965 +0,0 @@
-// 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 "collectiondetails.h"
-
-#include "collectiondatatypemodel.h"
-#include "collectioneditorutils.h"
-
-#include <utils/span.h>
-#include <qmljs/parser/qmljsast_p.h>
-#include <qmljs/parser/qmljsastvisitor_p.h>
-#include <qmljs/qmljsdocument.h>
-#include <qqml.h>
-
-#include <QJsonArray>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QRegularExpression>
-#include <QTextStream>
-#include <QUrl>
-#include <QVariant>
-
-namespace QmlDesigner {
-#define COLLERR_OK QT_TRANSLATE_NOOP("CollectioParseError", "no error occurred")
-#define COLLERR_MAINOBJECT QT_TRANSLATE_NOOP("CollectioParseError", "Document object not found")
-#define COLLERR_COLLECTIONNAME QT_TRANSLATE_NOOP("CollectioParseError", "Model name not found")
-#define COLLERR_COLLECTIONOBJ QT_TRANSLATE_NOOP("CollectioParseError", "Model is not an object")
-#define COLLERR_COLUMNARRAY QT_TRANSLATE_NOOP("CollectioParseError", "Column is not an array")
-#define COLLERR_UNKNOWN QT_TRANSLATE_NOOP("CollectioParseError", "Unknown error")
-
-struct CollectionProperty
-{
- using DataType = CollectionDetails::DataType;
-
- QString name;
- DataType type;
-};
-
-const QMap<DataTypeWarning::Warning, QString> DataTypeWarning::dataTypeWarnings = {
- {DataTypeWarning::CellDataTypeMismatch, "Cell and column data types do not match."}
-};
-
-class CollectionDetails::Private
-{
-public:
- QList<CollectionProperty> properties;
- QList<QJsonArray> dataRecords;
- CollectionReference reference;
- bool isChanged = false;
-
- bool isValidColumnId(int column) const { return column > -1 && column < properties.size(); }
-
- bool isValidRowId(int row) const { return row > -1 && row < dataRecords.size(); }
-};
-
-inline static bool isValidColorName(const QString &colorName)
-{
- return QColor::isValidColorName(colorName);
-}
-
-/**
- * @brief getCustomUrl
- * Address = <Url|LocalFile>
- *
- * @param value The input value to be evaluated
- * @param dataType if the value is a valid url, the data type
- * will be stored to this parameter, otherwise, it will be String
- * @param urlResult if the value is a valid url, the address
- * will be stored in this parameter, otherwise it will be empty.
- * @return true if the result is url
- */
-static bool getCustomUrl(const QString &value,
- CollectionDetails::DataType &dataType,
- QUrl *urlResult = nullptr)
-{
- static const QRegularExpression urlRegex{
- "^(?<Address>"
- "(?<Url>https?:\\/\\/"
- "(?:www\\.|(?!www))[A-z0-9][A-z0-9-]+[A-z0-9]\\.[^\\s]{2,}|www\\.[A-z0-9][A-z0-9-]+"
- "[A-z0-9]\\.[^\\s]{2,}|https?:\\/\\/"
- "(?:www\\.|(?!www))[A-z0-9]+\\.[^\\s]{2,}|www\\.[A-z0-9]+\\.[^\\s]{2,})|" // end of Url
- "(?<LocalFile>("
- "?:(?:[A-z]:)|(?:(?:\\\\|\\/){1,2}\\w+)\\$?)(?:(?:\\\\|\\/)(?:\\w[\\w ]*.*))+)" // end of LocalFile
- "){1}$" // end of Address
- };
-
- const QRegularExpressionMatch match = urlRegex.match(value.trimmed());
- if (match.hasCaptured("Address")) {
- dataType = CollectionDetails::DataType::Url;
-
- if (urlResult)
- urlResult->setUrl(match.captured("Address"));
-
- return true;
- }
-
- if (urlResult)
- urlResult->clear();
-
- dataType = CollectionDetails::DataType::String;
- return false;
-}
-
-/**
- * @brief dataTypeFromString
- * @param value The string value to be evaluated
- * @return Bool, Color, Integer, Real, Url,
- * Image if these types are detected within the non-empty string,
- * Otherwise it returns String.
- * If the value is integer, but it's out of the int range, it will be
- * considered as a Real.
- */
-static CollectionDetails::DataType dataTypeFromString(const QString &value)
-{
- using DataType = CollectionDetails::DataType;
- static const QRegularExpression validator{
- "(?<boolean>^(?:true|false)$)|"
- "(?<color>^(?:#(?:(?:[0-9a-fA-F]{2}){3,4}|(?:[0-9a-fA-F]){3,4}))$)|"
- "(?<integer>^\\d+$)|"
- "(?<real>^(?:-?(?:0|[1-9]\\d*)?(?:\\.\\d*)?(?<=\\d|\\.)"
- "(?:e-?(?:0|[1-9]\\d*))?|0x[0-9a-f]+)$)"};
- static const int boolIndex = validator.namedCaptureGroups().indexOf("boolean");
- static const int colorIndex = validator.namedCaptureGroups().indexOf("color");
- static const int integerIndex = validator.namedCaptureGroups().indexOf("integer");
- static const int realIndex = validator.namedCaptureGroups().indexOf("real");
-
- [[maybe_unused]] static const bool allIndexesFound =
- [](const std::initializer_list<int> &captureIndexes) {
- QTC_ASSERT(Utils::allOf(captureIndexes, [](int val) { return val > -1; }), return false);
- return true;
- }({boolIndex, colorIndex, integerIndex, realIndex});
-
- if (value.isEmpty())
- return DataType::String;
-
- const QString trimmedValue = value.trimmed();
- QRegularExpressionMatch match = validator.match(trimmedValue);
-
- if (match.hasCaptured(boolIndex))
- return DataType::Boolean;
- if (match.hasCaptured(colorIndex))
- return DataType::Color;
- if (match.hasCaptured(integerIndex)) {
- bool isInt = false;
- trimmedValue.toInt(&isInt);
- return isInt ? DataType::Integer : DataType::Real;
- }
- if (match.hasCaptured(realIndex))
- return DataType::Real;
-
- DataType urlType;
- if (getCustomUrl(trimmedValue, urlType))
- return urlType;
-
- return DataType::String;
-}
-
-static CollectionProperty::DataType dataTypeFromJsonValue(const QJsonValue &value)
-{
- using DataType = CollectionDetails::DataType;
- using JsonType = QJsonValue::Type;
-
- switch (value.type()) {
- case JsonType::Null:
- case JsonType::Undefined:
- return DataType::String;
- case JsonType::Bool:
- return DataType::Boolean;
- case JsonType::Double: {
- if (qFuzzyIsNull(std::remainder(value.toDouble(), 1)))
- return DataType::Integer;
- return DataType::Real;
- }
- case JsonType::String:
- return dataTypeFromString(value.toString());
- default:
- return DataType::String;
- }
-}
-
-static QList<CollectionProperty> getColumnsFromImportedJsonArray(const QJsonArray &importedArray)
-{
- using DataType = CollectionDetails::DataType;
-
- QHash<QString, int> resultSet;
- QList<CollectionProperty> result;
-
- for (const QJsonValue &value : importedArray) {
- if (value.isObject()) {
- const QJsonObject object = value.toObject();
- QJsonObject::ConstIterator element = object.constBegin();
- const QJsonObject::ConstIterator stopItem = object.constEnd();
-
- while (element != stopItem) {
- const QString propertyName = element.key();
- if (resultSet.contains(propertyName)) {
- CollectionProperty &property = result[resultSet.value(propertyName)];
- if (property.type == DataType::Integer) {
- const DataType currentCellDataType = dataTypeFromJsonValue(element.value());
- if (currentCellDataType == DataType::Real)
- property.type = currentCellDataType;
- }
- } else {
- result.append({propertyName, dataTypeFromJsonValue(element.value())});
- resultSet.insert(propertyName, resultSet.size());
- }
- ++element;
- }
- }
- }
-
- return result;
-}
-
-static QVariant valueToVariant(const QJsonValue &value, CollectionDetails::DataType type)
-{
- using DataType = CollectionDetails::DataType;
- QVariant variantValue = value.toVariant();
-
- switch (type) {
- case DataType::String:
- return variantValue.toString();
- case DataType::Integer:
- return variantValue.toInt();
- case DataType::Real:
- return variantValue.toDouble();
- case DataType::Boolean:
- return variantValue.toBool();
- case DataType::Color:
- return variantValue.value<QColor>();
- case DataType::Url:
- case DataType::Image:
- return variantValue.value<QUrl>();
- default:
- return variantValue;
- }
-}
-
-static QJsonValue variantToJsonValue(
- const QVariant &variant, CollectionDetails::DataType type = CollectionDetails::DataType::String)
-{
- using DataType = CollectionDetails::DataType;
-
- switch (type) {
- case DataType::Boolean:
- return variant.toBool();
- case DataType::Real:
- return variant.toDouble();
- case DataType::Integer:
- return variant.toInt();
- case DataType::Image:
- case DataType::String:
- case DataType::Color:
- case DataType::Url:
- default:
- return variant.toString();
- }
-}
-
-inline static bool isEmptyJsonValue(const QJsonValue &value)
-{
- return value.isNull() || value.isUndefined() || (value.isString() && value.toString().isEmpty());
-}
-
-QStringList csvReadLine(const QString &line)
-{
- static const QRegularExpression lineRegex{
- "(?:,\\\"|^\\\")(?<value>\\\"\\\"|[\\w\\W]*?)(?=\\\",|\\\"$)"
- "|(?:,(?!\\\")|^(?!\\\"))(?<quote>[^,]*?)(?=$|,)|(\\\\r\\\\n|\\\\n)"};
- static const int valueIndex = lineRegex.namedCaptureGroups().indexOf("value");
- static const int quoteIndex = lineRegex.namedCaptureGroups().indexOf("quote");
- Q_ASSERT(valueIndex > 0 && quoteIndex > 0);
-
- QStringList result;
- QRegularExpressionMatchIterator iterator = lineRegex.globalMatch(line, 0);
- while (iterator.hasNext()) {
- const QRegularExpressionMatch match = iterator.next();
-
- if (match.hasCaptured(valueIndex))
- result.append(match.captured(valueIndex));
- else if (match.hasCaptured(quoteIndex))
- result.append(match.captured(quoteIndex));
- }
- return result;
-}
-
-class PropertyOrderFinder : public QmlJS::AST::Visitor
-{
-public:
- static QStringList parse(const QString &jsonContent)
- {
- PropertyOrderFinder finder;
- QmlJS::Document::MutablePtr jsonDoc = QmlJS::Document::create(Utils::FilePath::fromString(
- "<expression>"),
- QmlJS::Dialect::Json);
-
- jsonDoc->setSource(jsonContent);
- jsonDoc->parseJavaScript();
-
- if (!jsonDoc->isParsedCorrectly())
- return {};
-
- jsonDoc->ast()->accept(&finder);
- return finder.m_orderedList;
- }
-
-protected:
- bool visit(QmlJS::AST::PatternProperty *patternProperty) override
- {
- const QString propertyName = patternProperty->name->asString();
- if (!m_propertySet.contains(propertyName)) {
- m_propertySet.insert(propertyName);
- m_orderedList.append(propertyName);
- }
- return true;
- }
-
- void throwRecursionDepthError() override
- {
- qWarning() << Q_FUNC_INFO << __LINE__ << "Recursion depth error";
- };
-
-private:
- QSet<QString> m_propertySet;
- QStringList m_orderedList;
-};
-
-QString CollectionParseError::errorString() const
-{
- switch (errorNo) {
- case NoError:
- return COLLERR_OK;
- case MainObjectMissing:
- return COLLERR_MAINOBJECT;
- case CollectionNameNotFound:
- return COLLERR_COLLECTIONNAME;
- case CollectionIsNotObject:
- return COLLERR_COLLECTIONOBJ;
- case ColumnsBlockIsNotArray:
- return COLLERR_COLUMNARRAY;
- case UnknownError:
- default:
- return COLLERR_UNKNOWN;
- }
-}
-
-CollectionDetails::CollectionDetails()
- : d(new Private())
-{}
-
-CollectionDetails::CollectionDetails(const CollectionReference &reference)
- : CollectionDetails()
-{
- d->reference = reference;
-}
-
-void CollectionDetails::resetData(const QJsonDocument &localDocument,
- const QString &collectionToImport,
- CollectionParseError *error)
-{
- CollectionDetails importedCollection = fromLocalJson(localDocument, collectionToImport, error);
- d->properties.swap(importedCollection.d->properties);
- d->dataRecords.swap(importedCollection.d->dataRecords);
-}
-
-CollectionDetails::CollectionDetails(const CollectionDetails &other) = default;
-
-CollectionDetails::~CollectionDetails() = default;
-
-void CollectionDetails::insertColumn(const QString &propertyName,
- int colIdx,
- const QVariant &defaultValue,
- DataType type)
-{
- if (containsPropertyName(propertyName))
- return;
-
- CollectionProperty property = {propertyName, type};
- if (d->isValidColumnId(colIdx)) {
- d->properties.insert(colIdx, property);
- } else {
- colIdx = d->properties.size();
- d->properties.append(property);
- }
-
- const QJsonValue defaultJsonValue = QJsonValue::fromVariant(defaultValue);
- for (QJsonArray &record : d->dataRecords)
- record.insert(colIdx, defaultJsonValue);
-
- markChanged();
-}
-
-bool CollectionDetails::removeColumns(int colIdx, int count)
-{
- if (!d->isValidColumnId(colIdx))
- return false;
-
- int maxCount = d->properties.count() - colIdx;
- count = std::min(maxCount, count);
-
- if (count < 1)
- return false;
-
- d->properties.remove(colIdx, count);
-
- for (QJsonArray &record : d->dataRecords) {
- QJsonArray newElement;
-
- auto elementItr = record.constBegin();
- auto elementEnd = elementItr + colIdx;
- while (elementItr != elementEnd)
- newElement.append(*(elementItr++));
-
- elementItr += count;
- elementEnd = record.constEnd();
-
- while (elementItr != elementEnd)
- newElement.append(*(elementItr++));
-
- record = newElement;
- }
-
- markChanged();
-
- return true;
-}
-
-void CollectionDetails::insertEmptyRows(int row, int count)
-{
- if (count < 1)
- return;
-
- row = qBound(0, row, rows());
-
- insertRecords({}, row, count);
-
- markChanged();
-}
-
-bool CollectionDetails::removeRows(int row, int count)
-{
- if (!d->isValidRowId(row))
- return false;
-
- int maxCount = d->dataRecords.count() - row;
- count = std::min(maxCount, count);
-
- if (count < 1)
- return false;
-
- d->dataRecords.remove(row, count);
- markChanged();
- return true;
-}
-
-bool CollectionDetails::setPropertyValue(int row, int column, const QVariant &value)
-{
- if (!d->isValidRowId(row) || !d->isValidColumnId(column))
- return false;
-
- QVariant currentValue = data(row, column);
- if (value == currentValue)
- return false;
-
- QJsonArray &record = d->dataRecords[row];
- record.replace(column, variantToJsonValue(value, typeAt(column)));
- markChanged();
- return true;
-}
-
-bool CollectionDetails::setPropertyName(int column, const QString &value)
-{
- if (!d->isValidColumnId(column))
- return false;
-
- const CollectionProperty &oldProperty = d->properties.at(column);
- if (oldProperty.name == value)
- return false;
-
- d->properties.replace(column, {value, oldProperty.type});
-
- markChanged();
- return true;
-}
-
-bool CollectionDetails::setPropertyType(int column, DataType type)
-{
- if (!d->isValidColumnId(column))
- return false;
-
- bool changed = false;
- CollectionProperty &property = d->properties[column];
- if (property.type != type)
- changed = true;
-
- const DataType formerType = property.type;
- property.type = type;
-
- for (QJsonArray &rowData : d->dataRecords) {
- if (column < rowData.size()) {
- const QJsonValue value = rowData.at(column);
- const QVariant properTypedValue = valueToVariant(value, formerType);
- const QJsonValue properTypedJsonValue = variantToJsonValue(properTypedValue, type);
- rowData.replace(column, properTypedJsonValue);
- changed = true;
- }
- }
-
- if (changed)
- markChanged();
-
- return changed;
-}
-
-CollectionReference CollectionDetails::reference() const
-{
- return d->reference;
-}
-
-QVariant CollectionDetails::data(int row, int column) const
-{
- if (!d->isValidRowId(row))
- return {};
-
- if (!d->isValidColumnId(column))
- return {};
-
- const QJsonValue cellValue = d->dataRecords.at(row).at(column);
-
- return cellValue.toVariant();
-}
-
-QString CollectionDetails::propertyAt(int column) const
-{
- if (!d->isValidColumnId(column))
- return {};
-
- return d->properties.at(column).name;
-}
-
-CollectionDetails::DataType CollectionDetails::typeAt(int column) const
-{
- if (!d->isValidColumnId(column))
- return {};
-
- return d->properties.at(column).type;
-}
-
-CollectionDetails::DataType CollectionDetails::typeAt(int row, int column) const
-{
- if (!d->isValidRowId(row) || !d->isValidColumnId(column))
- return {};
-
- const QJsonValue cellData = d->dataRecords.at(row).at(column);
- return dataTypeFromJsonValue(cellData);
-}
-
-DataTypeWarning::Warning CollectionDetails::cellWarningCheck(int row, int column) const
-{
- const QJsonValue cellValue = d->dataRecords.at(row).at(column);
-
- const DataType columnType = typeAt(column);
- const DataType cellType = typeAt(row, column);
-
- if (isEmptyJsonValue(cellValue))
- return DataTypeWarning::Warning::None;
-
- if ((columnType == DataType::String || columnType == DataType::Real) && cellType == DataType::Integer)
- return DataTypeWarning::Warning::None;
-
- if ((columnType == DataType::Url || columnType == DataType::Image) && cellType == DataType::String)
- return DataTypeWarning::Warning::None;
-
- if (columnType != cellType)
- return DataTypeWarning::Warning::CellDataTypeMismatch;
-
- return DataTypeWarning::Warning::None;
-}
-
-bool CollectionDetails::containsPropertyName(const QString &propertyName) const
-{
- return Utils::anyOf(d->properties, [&propertyName](const CollectionProperty &property) {
- return property.name == propertyName;
- });
-}
-
-bool CollectionDetails::hasValidReference() const
-{
- return d->reference.node.isValid() && d->reference.name.size();
-}
-
-bool CollectionDetails::isChanged() const
-{
- return d->isChanged;
-}
-
-int CollectionDetails::columns() const
-{
- return d->properties.size();
-}
-
-int CollectionDetails::rows() const
-{
- return d->dataRecords.size();
-}
-
-bool CollectionDetails::markSaved()
-{
- if (d->isChanged) {
- d->isChanged = false;
- return true;
- }
- return false;
-}
-
-void CollectionDetails::swap(CollectionDetails &other)
-{
- d.swap(other.d);
-}
-
-void CollectionDetails::resetReference(const CollectionReference &reference)
-{
- if (d->reference != reference) {
- d->reference = reference;
- markChanged();
- }
-}
-
-QString CollectionDetails::toJson() const
-{
- QJsonArray exportedArray;
- const int propertyCount = d->properties.count();
-
- for (const QJsonArray &record : std::as_const(d->dataRecords)) {
- const int valueCount = std::min(int(record.count()), propertyCount);
-
- QJsonObject exportedElement;
- for (int i = 0; i < valueCount; ++i) {
- const QJsonValue &value = record.at(i);
- if (isEmptyJsonValue(value))
- exportedElement.insert(d->properties.at(i).name, QJsonValue::Null);
- else
- exportedElement.insert(d->properties.at(i).name, value);
- }
-
- exportedArray.append(exportedElement);
- }
-
- return QString::fromUtf8(QJsonDocument(exportedArray).toJson());
-}
-
-QString CollectionDetails::toCsv() const
-{
- QString content;
-
- auto gotoNextLine = [&content]() {
- if (content.size() && content.back() == ',')
- content.back() = '\n';
- else
- content += "\n";
- };
-
- const int propertyCount = d->properties.count();
- if (propertyCount <= 0)
- return "";
-
- for (const CollectionProperty &property : std::as_const(d->properties))
- content += property.name + ',';
-
- gotoNextLine();
-
- for (const QJsonArray &record : std::as_const(d->dataRecords)) {
- const int valueCount = std::min(int(record.count()), propertyCount);
- int i = 0;
- for (; i < valueCount; ++i) {
- const QJsonValue &value = record.at(i);
-
- if (value.isDouble())
- content += QString::number(value.toDouble()) + ',';
- else if (value.isBool())
- content += value.toBool() ? QString("true,") : QString("false,");
- else
- content += value.toString() + ',';
- }
-
- for (; i < propertyCount; ++i)
- content += ',';
-
- gotoNextLine();
- }
-
- return content;
-}
-
-QJsonObject CollectionDetails::toLocalJson() const
-{
- QJsonObject collectionObject;
- QJsonArray columnsArray;
- QJsonArray dataArray;
-
- for (const CollectionProperty &property : std::as_const(d->properties)) {
- QJsonObject columnObject;
- columnObject.insert("name", property.name);
- columnObject.insert("type", CollectionDataTypeModel::dataTypeToString(property.type));
- columnsArray.append(columnObject);
- }
-
- for (const QJsonArray &record : std::as_const(d->dataRecords))
- dataArray.append(record);
-
- collectionObject.insert("columns", columnsArray);
- collectionObject.insert("data", dataArray);
-
- return collectionObject;
-}
-
-void CollectionDetails::registerDeclarativeType()
-{
- typedef CollectionDetails::DataType DataType;
- qRegisterMetaType<DataType>("DataType");
- qmlRegisterUncreatableType<CollectionDetails>("CollectionDetails", 1, 0, "DataType", "Enum type");
-
- qRegisterMetaType<DataTypeWarning::Warning>("Warning");
- qmlRegisterUncreatableType<DataTypeWarning>("CollectionDetails", 1, 0, "Warning", "Enum type");
-}
-
-CollectionDetails CollectionDetails::fromImportedCsv(const QByteArray &document,
- const bool &firstRowIsHeader)
-{
- QStringList headers;
- QJsonArray importedArray;
-
- QTextStream stream(document);
- stream.setEncoding(QStringConverter::Latin1);
-
- if (firstRowIsHeader && !stream.atEnd()) {
- headers = Utils::transform(csvReadLine(stream.readLine()),
- [](const QString &value) -> QString { return value.trimmed(); });
- }
-
- while (!stream.atEnd()) {
- const QStringList recordDataList = csvReadLine(stream.readLine());
- int column = -1;
- QJsonObject recordData;
- for (const QString &cellData : recordDataList) {
- if (++column == headers.size()) {
- QString proposalName;
- int proposalId = column;
- do
- proposalName = QString("Column %1").arg(++proposalId);
- while (headers.contains(proposalName));
- headers.append(proposalName);
- }
- recordData.insert(headers.at(column), cellData);
- }
- importedArray.append(recordData);
- }
-
- return fromImportedJson(importedArray, headers);
-}
-
-CollectionDetails CollectionDetails::fromImportedJson(const QByteArray &json, QJsonParseError *error)
-{
- QJsonArray importedCollection;
- auto refineJsonArray = [](const QJsonArray &array) -> QJsonArray {
- QJsonArray resultArray;
- for (const QJsonValue &collectionData : array) {
- if (collectionData.isObject()) {
- QJsonObject rowObject = collectionData.toObject();
- const QStringList rowKeys = rowObject.keys();
- for (const QString &key : rowKeys) {
- const QJsonValue cellValue = rowObject.value(key);
- if (cellValue.isArray())
- rowObject.remove(key);
- }
- resultArray.push_back(rowObject);
- }
- }
- return resultArray;
- };
-
- QJsonParseError parseError;
- QJsonDocument document = QJsonDocument::fromJson(json, &parseError);
- if (error)
- *error = parseError;
-
- if (parseError.error != QJsonParseError::NoError)
- return CollectionDetails{};
-
- if (document.isArray()) {
- importedCollection = refineJsonArray(document.array());
- } else if (document.isObject()) {
- QJsonObject documentObject = document.object();
- const QStringList mainKeys = documentObject.keys();
-
- bool arrayFound = false;
- for (const QString &key : mainKeys) {
- const QJsonValue value = documentObject.value(key);
- if (value.isArray()) {
- arrayFound = true;
- importedCollection = refineJsonArray(value.toArray());
- break;
- }
- }
-
- if (!arrayFound) {
- QJsonObject singleObject;
- for (const QString &key : mainKeys) {
- const QJsonValue value = documentObject.value(key);
-
- if (!value.isObject())
- singleObject.insert(key, value);
- }
- importedCollection.push_back(singleObject);
- }
- }
-
- return fromImportedJson(importedCollection, PropertyOrderFinder::parse(QLatin1String(json)));
-}
-
-CollectionDetails CollectionDetails::fromLocalJson(const QJsonDocument &document,
- const QString &collectionName,
- CollectionParseError *error)
-{
- auto setError = [&error](CollectionParseError::ParseError parseError) {
- if (error)
- error->errorNo = parseError;
- };
-
- setError(CollectionParseError::NoError);
-
- if (document.isObject()) {
- QJsonObject collectionMap = document.object();
- if (collectionMap.contains(collectionName)) {
- QJsonValue collectionValue = collectionMap.value(collectionName);
- if (collectionValue.isObject())
- return fromLocalCollection(collectionValue.toObject());
- else
- setError(CollectionParseError::CollectionIsNotObject);
- } else {
- setError(CollectionParseError::CollectionNameNotFound);
- }
- } else {
- setError(CollectionParseError::MainObjectMissing);
- }
-
- return CollectionDetails{};
-}
-
-CollectionDetails &CollectionDetails::operator=(const CollectionDetails &other)
-{
- CollectionDetails value(other);
- swap(value);
- return *this;
-}
-
-void CollectionDetails::markChanged()
-{
- d->isChanged = true;
-}
-
-void CollectionDetails::insertRecords(const QJsonArray &record, int idx, int count)
-{
- if (count < 1)
- return;
-
- QJsonArray localRecord;
- const int columnsCount = columns();
- for (int i = 0; i < columnsCount; i++) {
- const QJsonValue originalCellData = record.at(i);
- if (originalCellData.isArray())
- localRecord.append({});
- else
- localRecord.append(originalCellData);
- }
-
- if (idx > d->dataRecords.size() || idx < 0)
- idx = d->dataRecords.size();
-
- d->dataRecords.insert(idx, count, localRecord);
-}
-
-CollectionDetails CollectionDetails::fromImportedJson(const QJsonArray &importedArray,
- const QStringList &propertyPriority)
-{
- QList<CollectionProperty> columnData = getColumnsFromImportedJsonArray(importedArray);
- if (!propertyPriority.isEmpty()) {
- QMap<QString, int> priorityMap;
- for (const QString &propertyName : propertyPriority) {
- if (!priorityMap.contains(propertyName))
- priorityMap.insert(propertyName, priorityMap.size());
- }
- const int lowestPriority = priorityMap.size();
-
- Utils::sort(columnData, [&](const CollectionProperty &a, const CollectionProperty &b) {
- return priorityMap.value(a.name, lowestPriority)
- < priorityMap.value(b.name, lowestPriority);
- });
- }
-
- QList<QJsonArray> localJsonArray;
- for (const QJsonValue &importedRowValue : importedArray) {
- QJsonObject importedRowObject = importedRowValue.toObject();
- QJsonArray localRow;
- for (const CollectionProperty &property : columnData)
- localRow.append(importedRowObject.value(property.name));
- localJsonArray.append(localRow);
- }
- CollectionDetails result;
- result.d->properties = columnData;
- result.d->dataRecords = localJsonArray;
- result.markSaved();
-
- return result;
-}
-
-CollectionDetails CollectionDetails::fromLocalCollection(const QJsonObject &localCollection,
- CollectionParseError *error)
-{
- auto setError = [&error](CollectionParseError::ParseError parseError) {
- if (error)
- error->errorNo = parseError;
- };
-
- CollectionDetails result;
- setError(CollectionParseError::NoError);
-
- if (localCollection.contains("columns")) {
- const QJsonValue columnsValue = localCollection.value("columns");
- if (columnsValue.isArray()) {
- const QJsonArray columns = columnsValue.toArray();
- for (const QJsonValue &columnValue : columns) {
- if (columnValue.isObject()) {
- const QJsonObject column = columnValue.toObject();
- const QString columnName = column.value("name").toString();
- if (!columnName.isEmpty()) {
- result.insertColumn(columnName,
- -1,
- {},
- CollectionDataTypeModel::dataTypeFromString(
- column.value("type").toString()));
- }
- }
- }
-
- if (int columnsCount = result.columns()) {
- const QJsonArray dataRecords = localCollection.value("data").toArray();
- for (const QJsonValue &dataRecordValue : dataRecords) {
- QJsonArray dataRecord = dataRecordValue.toArray();
- while (dataRecord.count() > columnsCount)
- dataRecord.removeLast();
-
- result.insertRecords(dataRecord);
- }
- }
- } else {
- setError(CollectionParseError::ColumnsBlockIsNotArray);
- return result;
- }
- }
-
- return result;
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h
deleted file mode 100644
index 7243c585c6..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h
+++ /dev/null
@@ -1,152 +0,0 @@
-// 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 "modelnode.h"
-
-#include <QSharedPointer>
-
-QT_BEGIN_NAMESPACE
-class QJsonObject;
-struct QJsonParseError;
-class QVariant;
-QT_END_NAMESPACE
-
-namespace QmlDesigner {
-
-struct CollectionReference
-{
- ModelNode node;
- QString name;
-
- friend auto qHash(const CollectionReference &collection)
- {
- return qHash(collection.node) ^ ::qHash(collection.name);
- }
-
- bool operator==(const CollectionReference &other) const
- {
- return node == other.node && name == other.name;
- }
-
- bool operator!=(const CollectionReference &other) const { return !(*this == other); }
-};
-
-struct CollectionProperty;
-
-struct DataTypeWarning {
-public:
- enum Warning { None, CellDataTypeMismatch };
- Q_ENUM(Warning)
-
- Warning warning = None;
- DataTypeWarning(Warning warning)
- : warning(warning)
- {}
-
- static QString getDataTypeWarningString(Warning warning)
- {
- return dataTypeWarnings.value(warning);
- }
-
-private:
- Q_GADGET
- static const QMap<Warning, QString> dataTypeWarnings;
-};
-
-struct CollectionParseError
-{
- enum ParseError {
- NoError,
- MainObjectMissing,
- CollectionNameNotFound,
- CollectionIsNotObject,
- ColumnsBlockIsNotArray,
- UnknownError
- };
-
- ParseError errorNo = ParseError::NoError;
- QString errorString() const;
-};
-
-class CollectionDetails
-{
- Q_GADGET
-
-public:
- enum class DataType { Unknown, String, Url, Integer, Real, Boolean, Image, Color };
- Q_ENUM(DataType)
-
- explicit CollectionDetails();
- CollectionDetails(const CollectionReference &reference);
- CollectionDetails(const CollectionDetails &other);
- ~CollectionDetails();
-
- void resetData(const QJsonDocument &localDocument,
- const QString &collectionToImport,
- CollectionParseError *error = nullptr);
-
- void insertColumn(const QString &propertyName,
- int colIdx = -1,
- const QVariant &defaultValue = {},
- DataType type = DataType::String);
- bool removeColumns(int colIdx, int count = 1);
-
- void insertEmptyRows(int row = 0, int count = 1);
- bool removeRows(int row, int count = 1);
- bool setPropertyValue(int row, int column, const QVariant &value);
-
- bool setPropertyName(int column, const QString &value);
- bool setPropertyType(int column, DataType type);
-
- CollectionReference reference() const;
- QVariant data(int row, int column) const;
- QString propertyAt(int column) const;
- DataType typeAt(int column) const;
- DataType typeAt(int row, int column) const;
- DataTypeWarning::Warning cellWarningCheck(int row, int column) const;
- bool containsPropertyName(const QString &propertyName) const;
-
- bool hasValidReference() const;
- bool isChanged() const;
-
- int columns() const;
- int rows() const;
-
- bool markSaved();
-
- void swap(CollectionDetails &other);
- void resetReference(const CollectionReference &reference);
-
- QString toJson() const;
- QString toCsv() const;
- QJsonObject toLocalJson() const;
-
- static void registerDeclarativeType();
-
- static CollectionDetails fromImportedCsv(const QByteArray &document,
- const bool &firstRowIsHeader = true);
- static CollectionDetails fromImportedJson(const QByteArray &json,
- QJsonParseError *error = nullptr);
- static CollectionDetails fromLocalJson(const QJsonDocument &document,
- const QString &collectionName,
- CollectionParseError *error = nullptr);
-
- CollectionDetails &operator=(const CollectionDetails &other);
-
-private:
- void markChanged();
- void insertRecords(const QJsonArray &record, int idx = -1, int count = 1);
-
- static CollectionDetails fromImportedJson(const QJsonArray &importedArray,
- const QStringList &propertyPriority = {});
- static CollectionDetails fromLocalCollection(const QJsonObject &localCollection,
- CollectionParseError *error = nullptr);
-
- // The private data is supposed to be shared between the copies
- class Private;
- QSharedPointer<Private> d;
-};
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp
deleted file mode 100644
index fcd6d686ef..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp
+++ /dev/null
@@ -1,627 +0,0 @@
-// 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 "collectiondetailsmodel.h"
-
-#include "collectiondatatypemodel.h"
-#include "collectioneditorutils.h"
-#include "modelnode.h"
-
-#include <coreplugin/editormanager/editormanager.h>
-
-#include <utils/fileutils.h>
-#include <utils/qtcassert.h>
-#include <utils/textfileformat.h>
-
-#include <QJsonArray>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonParseError>
-
-namespace QmlDesigner {
-
-CollectionDetailsModel::CollectionDetailsModel(QObject *parent)
- : QAbstractTableModel(parent)
-{
- connect(this, &CollectionDetailsModel::modelReset, this, &CollectionDetailsModel::updateEmpty);
- connect(this, &CollectionDetailsModel::rowsInserted, this, &CollectionDetailsModel::updateEmpty);
- connect(this, &CollectionDetailsModel::rowsRemoved, this, &CollectionDetailsModel::updateEmpty);
-}
-
-QHash<int, QByteArray> CollectionDetailsModel::roleNames() const
-{
- static QHash<int, QByteArray> roles;
- if (roles.isEmpty()) {
- roles.insert(QAbstractTableModel::roleNames());
- roles.insert(SelectedRole, "itemSelected");
- roles.insert(DataTypeRole, "dataType");
- roles.insert(ColumnDataTypeRole, "columnType");
- roles.insert(DataTypeWarningRole, "dataTypeWarning");
- }
- return roles;
-}
-
-int CollectionDetailsModel::rowCount([[maybe_unused]] const QModelIndex &parent) const
-{
- return m_currentCollection.rows();
-}
-
-int CollectionDetailsModel::columnCount([[maybe_unused]] const QModelIndex &parent) const
-{
- return m_currentCollection.columns();
-}
-
-QVariant CollectionDetailsModel::data(const QModelIndex &index, int role) const
-{
- if (!index.isValid())
- return {};
-
- QTC_ASSERT(m_currentCollection.hasValidReference(), return {});
-
- if (role == SelectedRole)
- return (index.column() == m_selectedColumn || index.row() == m_selectedRow);
-
- if (role == DataTypeRole)
- return QVariant::fromValue(m_currentCollection.typeAt(index.row(), index.column()));
-
- if (role == ColumnDataTypeRole)
- return QVariant::fromValue(m_currentCollection.typeAt(index.column()));
-
- if (role == Qt::EditRole)
- return m_currentCollection.data(index.row(), index.column());
-
- if (role == DataTypeWarningRole )
- return QVariant::fromValue(m_currentCollection.cellWarningCheck(index.row(), index.column()));
-
- return m_currentCollection.data(index.row(), index.column()).toString();
-}
-
-bool CollectionDetailsModel::setData(const QModelIndex &index, const QVariant &value, int role)
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
-
- if (!index.isValid())
- return {};
-
- if (role == Qt::EditRole) {
- DataTypeWarning::Warning prevWarning = m_currentCollection.cellWarningCheck(index.row(), index.column());
- bool changed = m_currentCollection.setPropertyValue(index.row(), index.column(), value);
-
- if (changed) {
- QList<int> roles = {Qt::DisplayRole, Qt::EditRole};
-
- if (prevWarning != m_currentCollection.cellWarningCheck(index.row(), index.column()))
- roles << DataTypeWarningRole;
-
- setHasUnsavedChanges(true);
- emit dataChanged(index, index, roles);
- }
-
- return true;
- }
-
- return false;
-}
-
-bool CollectionDetailsModel::setHeaderData(int section,
- Qt::Orientation orientation,
- const QVariant &value,
- [[maybe_unused]] int role)
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
-
- if (orientation == Qt::Vertical)
- return false;
-
- bool headerChanged = m_currentCollection.setPropertyName(section, value.toString());
- if (headerChanged)
- emit this->headerDataChanged(orientation, section, section);
-
- return headerChanged;
-}
-
-bool CollectionDetailsModel::insertRows(int row, int count, [[maybe_unused]] const QModelIndex &parent)
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
-
- if (count < 1)
- return false;
-
- row = qBound(0, row, rowCount());
-
- beginInsertRows({}, row, row + count - 1);
- m_currentCollection.insertEmptyRows(row, count);
- endInsertRows();
- setHasUnsavedChanges(true);
-
- return true;
-}
-
-bool CollectionDetailsModel::removeColumns(int column, int count, const QModelIndex &parent)
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
-
- if (column < 0 || column >= columnCount(parent) || count < 1)
- return false;
-
- count = std::min(count, columnCount(parent) - column);
- beginRemoveColumns(parent, column, column + count - 1);
- bool columnsRemoved = m_currentCollection.removeColumns(column, count);
- endRemoveColumns();
-
- if (!columnCount(parent))
- removeRows(0, rowCount(parent), parent);
-
- ensureSingleCell();
- return columnsRemoved;
-}
-
-bool CollectionDetailsModel::removeRows(int row, int count, const QModelIndex &parent)
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
-
- if (row < 0 || row >= rowCount(parent) || count < 1)
- return false;
-
- count = std::min(count, rowCount(parent) - row);
- beginRemoveRows(parent, row, row + count - 1);
- bool rowsRemoved = m_currentCollection.removeRows(row, count);
- endRemoveRows();
-
- ensureSingleCell();
- return rowsRemoved;
-}
-
-Qt::ItemFlags CollectionDetailsModel::flags(const QModelIndex &index) const
-{
- if (!index.isValid())
- return {};
-
- return {Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable};
-}
-
-QVariant CollectionDetailsModel::headerData(int section, Qt::Orientation orientation, int role) const
-{
- if (orientation == Qt::Horizontal) {
- if (role == DataTypeRole)
- return CollectionDataTypeModel::dataTypeToString(m_currentCollection.typeAt(section));
- else
- return m_currentCollection.propertyAt(section);
- }
-
- if (orientation == Qt::Vertical)
- return section + 1;
-
- return {};
-}
-
-CollectionDetails::DataType CollectionDetailsModel::propertyDataType(int column) const
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return CollectionDetails::DataType::String);
-
- return m_currentCollection.typeAt(column);
-}
-
-int CollectionDetailsModel::selectedColumn() const
-{
- return m_selectedColumn;
-}
-
-int CollectionDetailsModel::selectedRow() const
-{
- return m_selectedRow;
-}
-
-QString CollectionDetailsModel::propertyName(int column) const
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return {});
-
- return m_currentCollection.propertyAt(column);
-}
-
-QString CollectionDetailsModel::propertyType(int column) const
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return {});
-
- return CollectionDataTypeModel::dataTypeToString(m_currentCollection.typeAt(column));
-}
-
-bool CollectionDetailsModel::isPropertyAvailable(const QString &name)
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
-
- return m_currentCollection.containsPropertyName(name);
-}
-
-bool CollectionDetailsModel::addColumn(int column, const QString &name, const QString &propertyType)
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
-
- if (m_currentCollection.containsPropertyName(name))
- return false;
-
- if (column < 0 || column > columnCount())
- column = columnCount();
-
- beginInsertColumns({}, column, column);
- m_currentCollection.insertColumn(name,
- column,
- {},
- CollectionDataTypeModel::dataTypeFromString(propertyType));
- endInsertColumns();
- setHasUnsavedChanges(true);
- return m_currentCollection.containsPropertyName(name);
-}
-
-bool CollectionDetailsModel::selectColumn(int section)
-{
- if (m_selectedColumn == section)
- return false;
-
- const int columns = columnCount();
-
- if (section >= columns)
- section = columns - 1;
-
- selectRow(-1);
-
- const int rows = rowCount();
- const int previousColumn = m_selectedColumn;
-
- m_selectedColumn = section;
- emit this->selectedColumnChanged(m_selectedColumn);
-
- auto notifySelectedDataChanged = [this, columns, rows](int notifyingColumn) {
- if (notifyingColumn > -1 && notifyingColumn < columns && rows) {
- emit dataChanged(index(0, notifyingColumn),
- index(rows - 1, notifyingColumn),
- {SelectedRole});
- }
- };
-
- notifySelectedDataChanged(previousColumn);
- notifySelectedDataChanged(m_selectedColumn);
-
- return true;
-}
-
-bool CollectionDetailsModel::renameColumn(int section, const QString &newValue)
-{
- return setHeaderData(section, Qt::Horizontal, newValue);
-}
-
-bool CollectionDetailsModel::setPropertyType(int column, const QString &newValue)
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
-
- bool changed = m_currentCollection.setPropertyType(column,
- CollectionDataTypeModel::dataTypeFromString(
- newValue));
- if (changed) {
- emit headerDataChanged(Qt::Horizontal, column, column);
- emit dataChanged(
- index(0, column),
- index(rowCount() - 1, column),
- {Qt::DisplayRole, Qt::EditRole, DataTypeRole, DataTypeWarningRole, ColumnDataTypeRole});
- }
-
- setHasUnsavedChanges(true);
- return changed;
-}
-
-bool CollectionDetailsModel::selectRow(int row)
-{
- if (m_selectedRow == row)
- return false;
-
- const int rows = rowCount();
-
- if (row >= rows)
- row = rows - 1;
-
- selectColumn(-1);
-
- const int columns = columnCount();
- const int previousRow = m_selectedRow;
-
- m_selectedRow = row;
- emit this->selectedRowChanged(m_selectedRow);
-
- auto notifySelectedDataChanged = [this, rows, columns](int notifyingRow) {
- if (notifyingRow > -1 && notifyingRow < rows && columns)
- emit dataChanged(index(notifyingRow, 0), index(notifyingRow, columns - 1), {SelectedRole});
- };
-
- notifySelectedDataChanged(previousRow);
- notifySelectedDataChanged(m_selectedRow);
-
- return true;
-}
-
-void CollectionDetailsModel::deselectAll()
-{
- selectColumn(-1);
- selectRow(-1);
-}
-
-void CollectionDetailsModel::loadCollection(const ModelNode &sourceNode, const QString &collection)
-{
- QString fileName = CollectionEditorUtils::getSourceCollectionPath(sourceNode);
-
- CollectionReference newReference{sourceNode, collection};
- bool alreadyOpen = m_openedCollections.contains(newReference);
-
- if (alreadyOpen) {
- if (m_currentCollection.reference() != newReference) {
- deselectAll();
- beginResetModel();
- switchToCollection(newReference);
- ensureSingleCell();
- endResetModel();
- }
- } else {
- deselectAll();
- switchToCollection(newReference);
- loadJsonCollection(fileName, collection);
- }
-}
-
-void CollectionDetailsModel::removeCollection(const ModelNode &sourceNode, const QString &collection)
-{
- CollectionReference collectionRef{sourceNode, collection};
- if (!m_openedCollections.contains(collectionRef))
- return;
-
- if (m_currentCollection.reference() == collectionRef)
- loadCollection({}, {});
-
- m_openedCollections.remove(collectionRef);
-}
-
-void CollectionDetailsModel::removeAllCollections()
-{
- loadCollection({}, {});
- m_openedCollections.clear();
-}
-
-void CollectionDetailsModel::renameCollection(const ModelNode &sourceNode,
- const QString &oldName,
- const QString &newName)
-{
- CollectionReference oldRef{sourceNode, oldName};
- if (!m_openedCollections.contains(oldRef))
- return;
-
- CollectionReference newReference{sourceNode, newName};
- bool collectionIsSelected = m_currentCollection.reference() == oldRef;
- CollectionDetails collection = m_openedCollections.take(oldRef);
- collection.resetReference(newReference);
- m_openedCollections.insert(newReference, collection);
-
- if (collectionIsSelected)
- setCollectionName(newName);
-}
-
-bool CollectionDetailsModel::saveDataStoreCollections()
-{
- const ModelNode node = m_currentCollection.reference().node;
- const Utils::FilePath path = CollectionEditorUtils::dataStoreJsonFilePath();
- Utils::FileReader fileData;
-
- if (!fileData.fetch(path)) {
- qWarning() << Q_FUNC_INFO << "Cannot read the json file:" << fileData.errorString();
- return false;
- }
-
- QJsonParseError jpe;
- QJsonDocument document = QJsonDocument::fromJson(fileData.data(), &jpe);
-
- if (jpe.error == QJsonParseError::NoError) {
- QJsonObject obj = document.object();
-
- QList<CollectionDetails> collectionsToBeSaved;
- for (CollectionDetails &openedCollection : m_openedCollections) {
- const CollectionReference reference = openedCollection.reference();
- if (reference.node == node) {
- obj.insert(reference.name, openedCollection.toLocalJson());
- collectionsToBeSaved << openedCollection;
- }
- }
-
- document.setObject(obj);
-
- if (CollectionEditorUtils::writeToJsonDocument(path, document)) {
- const CollectionReference currentReference = m_currentCollection.reference();
- for (CollectionDetails &collection : collectionsToBeSaved) {
- collection.markSaved();
- const CollectionReference reference = collection.reference();
- if (reference != currentReference)
- closeCollectionIfSaved(reference);
- }
- setHasUnsavedChanges(false);
- return true;
- }
- }
- return false;
-}
-
-bool CollectionDetailsModel::exportCollection(const QUrl &url)
-{
- using Core::EditorManager;
- using Utils::FilePath;
- using Utils::TextFileFormat;
-
- QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
-
- bool saved = false;
- const FilePath filePath = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile()
- : url.toString());
- const QString saveFormat = filePath.toFileInfo().suffix().toLower();
- const QString content = saveFormat == "csv" ? m_currentCollection.toCsv()
- : m_currentCollection.toJson();
-
- TextFileFormat textFileFormat;
- textFileFormat.codec = EditorManager::defaultTextCodec();
- textFileFormat.lineTerminationMode = EditorManager::defaultLineEnding();
- QString errorMessage;
- saved = textFileFormat.writeFile(filePath, content, &errorMessage);
-
- if (!saved)
- qWarning() << Q_FUNC_INFO << "Unable to write file" << errorMessage;
-
- return saved;
-}
-
-const CollectionDetails CollectionDetailsModel::upToDateConstCollection(
- const CollectionReference &reference) const
-{
- using Utils::FilePath;
- using Utils::FileReader;
- CollectionDetails collection;
-
- if (m_openedCollections.contains(reference)) {
- collection = m_openedCollections.value(reference);
- } else {
- QUrl url = CollectionEditorUtils::getSourceCollectionPath(reference.node);
- FilePath path = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile()
- : url.toString());
- FileReader file;
-
- if (!file.fetch(path))
- return collection;
-
- QJsonParseError jpe;
- QJsonDocument document = QJsonDocument::fromJson(file.data(), &jpe);
-
- if (jpe.error != QJsonParseError::NoError)
- return collection;
-
- collection = CollectionDetails::fromLocalJson(document, reference.name);
- collection.resetReference(reference);
- }
- return collection;
-}
-
-bool CollectionDetailsModel::collectionHasColumn(const CollectionReference &reference,
- const QString &columnName) const
-{
- const CollectionDetails collection = upToDateConstCollection(reference);
- return collection.containsPropertyName(columnName);
-}
-
-QString CollectionDetailsModel::getFirstColumnName(const CollectionReference &reference) const
-{
- const CollectionDetails collection = upToDateConstCollection(reference);
- return collection.propertyAt(0);
-}
-
-void CollectionDetailsModel::updateEmpty()
-{
- bool isEmptyNow = rowCount() == 0;
- if (m_isEmpty != isEmptyNow) {
- m_isEmpty = isEmptyNow;
- emit isEmptyChanged(m_isEmpty);
- }
-}
-
-void CollectionDetailsModel::switchToCollection(const CollectionReference &collection)
-{
- if (m_currentCollection.reference() == collection)
- return;
-
- closeCurrentCollectionIfSaved();
-
- if (!m_openedCollections.contains(collection))
- m_openedCollections.insert(collection, CollectionDetails(collection));
-
- m_currentCollection = m_openedCollections.value(collection);
-
- setCollectionName(collection.name);
-}
-
-void CollectionDetailsModel::closeCollectionIfSaved(const CollectionReference &collection)
-{
- if (!m_openedCollections.contains(collection))
- return;
-
- const CollectionDetails &collectionDetails = m_openedCollections.value(collection);
-
- if (!collectionDetails.isChanged())
- m_openedCollections.remove(collection);
-}
-
-void CollectionDetailsModel::closeCurrentCollectionIfSaved()
-{
- if (m_currentCollection.hasValidReference()) {
- closeCollectionIfSaved(m_currentCollection.reference());
- m_currentCollection = CollectionDetails{};
- }
-}
-
-void CollectionDetailsModel::loadJsonCollection(const QString &filePath, const QString &collection)
-{
- QJsonDocument document = readJsonFile(filePath);
-
- beginResetModel();
- m_currentCollection.resetData(document, collection);
- ensureSingleCell();
- endResetModel();
-}
-
-void CollectionDetailsModel::ensureSingleCell()
-{
- if (!m_currentCollection.hasValidReference())
- return;
-
- if (!columnCount())
- addColumn(0, "Column 1", "String");
-
- if (!rowCount())
- insertRow(0);
-
- updateEmpty();
-}
-
-QJsonDocument CollectionDetailsModel::readJsonFile(const QUrl &url)
-{
- using Utils::FilePath;
- using Utils::FileReader;
- FilePath path = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() : url.toString());
- FileReader file;
-
- if (!file.fetch(path)) {
- emit warning(tr("File reading problem"), file.errorString());
- return {};
- }
-
- QJsonParseError jpe;
- QJsonDocument document = QJsonDocument::fromJson(file.data(), &jpe);
-
- if (jpe.error != QJsonParseError::NoError)
- emit warning(tr("Json parse error"), jpe.errorString());
-
- return document;
-}
-
-void CollectionDetailsModel::setCollectionName(const QString &newCollectionName)
-{
- if (m_collectionName != newCollectionName) {
- m_collectionName = newCollectionName;
- emit this->collectionNameChanged(m_collectionName);
- }
-}
-
-QString CollectionDetailsModel::warningToString(DataTypeWarning::Warning warning) const
-{
- return DataTypeWarning::getDataTypeWarningString(warning);
-}
-
-void CollectionDetailsModel::setHasUnsavedChanges(bool val)
-{
- if (m_hasUnsavedChanges == val)
- return;
- m_hasUnsavedChanges = val;
- emit hasUnsavedChangesChanged();
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h
deleted file mode 100644
index 8844ff4a3e..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h
+++ /dev/null
@@ -1,106 +0,0 @@
-// 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 "collectiondetails.h"
-
-#include <QAbstractTableModel>
-#include <QHash>
-
-namespace QmlDesigner {
-
-class ModelNode;
-
-class CollectionDetailsModel : public QAbstractTableModel
-{
- Q_OBJECT
-
- Q_PROPERTY(QString collectionName MEMBER m_collectionName NOTIFY collectionNameChanged)
- Q_PROPERTY(int selectedColumn READ selectedColumn WRITE selectColumn NOTIFY selectedColumnChanged)
- Q_PROPERTY(int selectedRow READ selectedRow WRITE selectRow NOTIFY selectedRowChanged)
- Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
- Q_PROPERTY(bool hasUnsavedChanges MEMBER m_hasUnsavedChanges WRITE setHasUnsavedChanges NOTIFY hasUnsavedChangesChanged)
-
-public:
- enum DataRoles { SelectedRole = Qt::UserRole + 1, DataTypeRole, ColumnDataTypeRole, DataTypeWarningRole };
- explicit CollectionDetailsModel(QObject *parent = nullptr);
-
- QHash<int, QByteArray> roleNames() const override;
- int rowCount(const QModelIndex &parent = {}) const override;
- int columnCount(const QModelIndex &parent = {}) const override;
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
- bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
- bool setHeaderData(int section,
- Qt::Orientation orientation,
- const QVariant &value,
- int role = Qt::EditRole) override;
- bool insertRows(int row, int count, const QModelIndex &parent = {}) override;
- bool removeColumns(int column, int count, const QModelIndex &parent = {}) override;
- bool removeRows(int row, int count, const QModelIndex &parent = {}) override;
-
- Qt::ItemFlags flags(const QModelIndex &index) const override;
- QVariant headerData(int section,
- Qt::Orientation orientation,
- int role = Qt::DisplayRole) const override;
-
- CollectionDetails::DataType propertyDataType(int column) const;
-
- int selectedColumn() const;
- int selectedRow() const;
- Q_INVOKABLE QString propertyName(int column) const;
- Q_INVOKABLE QString propertyType(int column) const;
-
- Q_INVOKABLE bool isPropertyAvailable(const QString &name);
- Q_INVOKABLE bool addColumn(int column, const QString &name, const QString &propertyType = {});
- Q_INVOKABLE bool selectColumn(int section);
- Q_INVOKABLE bool renameColumn(int section, const QString &newValue);
- Q_INVOKABLE bool setPropertyType(int column, const QString &newValue);
- Q_INVOKABLE bool selectRow(int row);
- Q_INVOKABLE void deselectAll();
- Q_INVOKABLE QString warningToString(DataTypeWarning::Warning warning) const;
-
- void loadCollection(const ModelNode &sourceNode, const QString &collection);
- void removeCollection(const ModelNode &sourceNode, const QString &collection);
- void removeAllCollections();
- void renameCollection(const ModelNode &sourceNode, const QString &oldName, const QString &newName);
-
- Q_INVOKABLE bool saveDataStoreCollections();
- Q_INVOKABLE bool exportCollection(const QUrl &url);
-
- const CollectionDetails upToDateConstCollection(const CollectionReference &reference) const;
- bool collectionHasColumn(const CollectionReference &reference, const QString &columnName) const;
- QString getFirstColumnName(const CollectionReference &reference) const;
- void setHasUnsavedChanges(bool val);
-
-signals:
- void collectionNameChanged(const QString &collectionName);
- void selectedColumnChanged(int);
- void selectedRowChanged(int);
- void isEmptyChanged(bool);
- void hasUnsavedChangesChanged();
- void warning(const QString &title, const QString &body);
-
-private slots:
- void updateEmpty();
-
-private:
- void switchToCollection(const CollectionReference &collection);
- void closeCollectionIfSaved(const CollectionReference &collection);
- void closeCurrentCollectionIfSaved();
- void setCollectionName(const QString &newCollectionName);
- void loadJsonCollection(const QString &filePath, const QString &collection);
- void ensureSingleCell();
- QJsonDocument readJsonFile(const QUrl &url);
-
- QHash<CollectionReference, CollectionDetails> m_openedCollections;
- CollectionDetails m_currentCollection;
- bool m_isEmpty = true;
- bool m_hasUnsavedChanges = false;
- int m_selectedColumn = -1;
- int m_selectedRow = -1;
-
- QString m_collectionName;
-};
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp
deleted file mode 100644
index 2cc6ac05a6..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp
+++ /dev/null
@@ -1,163 +0,0 @@
-// 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 "collectiondetailssortfiltermodel.h"
-
-#include "collectiondetailsmodel.h"
-#include "collectioneditorutils.h"
-
-#include <utils/qtcassert.h>
-
-namespace QmlDesigner {
-
-CollectionDetailsSortFilterModel::CollectionDetailsSortFilterModel(QObject *parent)
- : QSortFilterProxyModel(parent)
-{
- connect(this, &CollectionDetailsSortFilterModel::rowsInserted,
- this, &CollectionDetailsSortFilterModel::updateRowCountChanges);
- connect(this, &CollectionDetailsSortFilterModel::rowsRemoved,
- this, &CollectionDetailsSortFilterModel::updateRowCountChanges);
- connect(this, &CollectionDetailsSortFilterModel::modelReset,
- this, &CollectionDetailsSortFilterModel::updateRowCountChanges);
-
- setDynamicSortFilter(true);
-}
-
-void CollectionDetailsSortFilterModel::setSourceModel(CollectionDetailsModel *model)
-{
- m_source = model;
- Super::setSourceModel(model);
- connect(m_source, &CollectionDetailsModel::selectedColumnChanged,
- this, &CollectionDetailsSortFilterModel::updateSelectedColumn);
-
- connect(m_source, &CollectionDetailsModel::selectedRowChanged,
- this, &CollectionDetailsSortFilterModel::updateSelectedRow);
-}
-
-int CollectionDetailsSortFilterModel::selectedRow() const
-{
- QTC_ASSERT(m_source, return -1);
-
- return mapFromSource(m_source->index(m_source->selectedRow(), 0)).row();
-}
-
-int CollectionDetailsSortFilterModel::selectedColumn() const
-{
- QTC_ASSERT(m_source, return -1);
-
- return mapFromSource(m_source->index(0, m_source->selectedColumn())).column();
-}
-
-bool CollectionDetailsSortFilterModel::selectRow(int row)
-{
- QTC_ASSERT(m_source, return false);
-
- return m_source->selectRow(mapToSource(index(row, 0)).row());
-}
-
-bool CollectionDetailsSortFilterModel::selectColumn(int column)
-{
- QTC_ASSERT(m_source, return false);
-
- return m_source->selectColumn(mapToSource(index(0, column)).column());
-}
-
-void CollectionDetailsSortFilterModel::deselectAll()
-{
- QTC_ASSERT(m_source, return);
- m_source->deselectAll();
-}
-
-CollectionDetailsSortFilterModel::~CollectionDetailsSortFilterModel() = default;
-
-bool CollectionDetailsSortFilterModel::filterAcceptsRow(int sourceRow,
- const QModelIndex &sourceParent) const
-{
- QTC_ASSERT(m_source, return false);
- QModelIndex sourceIndex(m_source->index(sourceRow, 0, sourceParent));
- return sourceIndex.isValid();
-}
-
-bool CollectionDetailsSortFilterModel::lessThan(const QModelIndex &sourceleft,
- const QModelIndex &sourceRight) const
-{
- QTC_ASSERT(m_source, return false);
-
- if (sourceleft.column() == sourceRight.column()) {
- int column = sourceleft.column();
- CollectionDetails::DataType columnType = m_source->propertyDataType(column);
- return CollectionEditorUtils::variantIslessThan(sourceleft.data(),
- sourceRight.data(),
- columnType);
- }
-
- return false;
-}
-
-void CollectionDetailsSortFilterModel::updateEmpty()
-{
- bool newValue = rowCount() == 0;
- if (m_isEmpty != newValue) {
- m_isEmpty = newValue;
- emit isEmptyChanged(m_isEmpty);
- }
-}
-
-void CollectionDetailsSortFilterModel::updateSelectedRow()
-{
- const int upToDateSelectedRow = selectedRow();
- if (m_selectedRow == upToDateSelectedRow)
- return;
-
- const int rows = rowCount();
- const int columns = columnCount();
- const int previousRow = m_selectedRow;
-
- m_selectedRow = upToDateSelectedRow;
- emit this->selectedRowChanged(m_selectedRow);
-
- auto notifySelectedDataChanged = [this, rows, columns](int notifyingRow) {
- if (notifyingRow > -1 && notifyingRow < rows && columns) {
- emit dataChanged(index(notifyingRow, 0),
- index(notifyingRow, columns - 1),
- {CollectionDetailsModel::SelectedRole});
- }
- };
-
- notifySelectedDataChanged(previousRow);
- notifySelectedDataChanged(m_selectedRow);
-}
-
-void CollectionDetailsSortFilterModel::updateSelectedColumn()
-{
- const int upToDateSelectedColumn = selectedColumn();
- if (m_selectedColumn == upToDateSelectedColumn)
- return;
-
- const int rows = rowCount();
- const int columns = columnCount();
- const int previousColumn = m_selectedColumn;
-
- m_selectedColumn = upToDateSelectedColumn;
- emit this->selectedColumnChanged(m_selectedColumn);
-
- auto notifySelectedDataChanged = [this, rows, columns](int notifyingCol) {
- if (notifyingCol > -1 && notifyingCol < columns && rows) {
- emit dataChanged(index(0, notifyingCol),
- index(rows - 1, notifyingCol),
- {CollectionDetailsModel::SelectedRole});
- }
- };
-
- notifySelectedDataChanged(previousColumn);
- notifySelectedDataChanged(m_selectedColumn);
-}
-
-void CollectionDetailsSortFilterModel::updateRowCountChanges()
-{
- updateEmpty();
- updateSelectedRow();
- invalidate();
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h
deleted file mode 100644
index 10f6e09b05..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// 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 <QPointer>
-#include <QSortFilterProxyModel>
-
-namespace QmlDesigner {
-
-class CollectionDetailsModel;
-
-class CollectionDetailsSortFilterModel : public QSortFilterProxyModel
-{
- Q_OBJECT
-
- Q_PROPERTY(int selectedColumn READ selectedColumn WRITE selectColumn NOTIFY selectedColumnChanged)
- Q_PROPERTY(int selectedRow READ selectedRow WRITE selectRow NOTIFY selectedRowChanged)
- Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
-
- using Super = QSortFilterProxyModel;
-
-public:
- explicit CollectionDetailsSortFilterModel(QObject *parent = nullptr);
- virtual ~CollectionDetailsSortFilterModel();
-
- void setSourceModel(CollectionDetailsModel *model);
-
- int selectedRow() const;
- int selectedColumn() const;
-
- Q_INVOKABLE bool selectRow(int row);
- Q_INVOKABLE bool selectColumn(int column);
- Q_INVOKABLE void deselectAll();
-
-signals:
- void selectedColumnChanged(int);
- void selectedRowChanged(int);
- void isEmptyChanged(bool);
-
-protected:
- using Super::setSourceModel;
- bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
- bool lessThan(const QModelIndex &sourceleft, const QModelIndex &sourceRight) const override;
-
-private:
- void updateEmpty();
- void updateSelectedRow();
- void updateSelectedColumn();
- void updateRowCountChanges();
-
- QPointer<CollectionDetailsModel> m_source;
- int m_selectedColumn = -1;
- int m_selectedRow = -1;
- bool m_isEmpty = true;
-};
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h
deleted file mode 100644
index 76524762ed..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// 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 QmlDesigner::CollectionEditorConstants {
-
-enum class SourceFormat { Unknown, Json };
-
-inline constexpr char SOURCEFILE_PROPERTY[] = "source";
-inline constexpr char ALLMODELS_PROPERTY[] = "allModels";
-inline constexpr char JSONCHILDMODELNAME_PROPERTY[] = "modelName";
-
-inline constexpr char COLLECTIONMODEL_IMPORT[] = "QtQuick.Studio.Utils";
-inline constexpr char JSONCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Utils.JsonListModel";
-inline constexpr char JSONCOLLECTIONCHILDMODEL_TYPENAME[] = "QtQuick.Studio.Utils.ChildListModel";
-inline constexpr char JSONBACKEND_TYPENAME[] = "JsonData";
-
-} // namespace QmlDesigner::CollectionEditorConstants
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp
deleted file mode 100644
index 29b833cc2c..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp
+++ /dev/null
@@ -1,345 +0,0 @@
-// 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 "collectioneditorutils.h"
-
-#include "collectiondatatypemodel.h"
-#include "model.h"
-#include "nodemetainfo.h"
-#include "propertymetainfo.h"
-
-#include <coreplugin/documentmanager.h>
-#include <coreplugin/icore.h>
-#include <projectexplorer/project.h>
-#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/projectmanager.h>
-#include <qmljs/qmljsmodelmanagerinterface.h>
-#include <utils/qtcassert.h>
-
-#include <variant>
-
-#include <QColor>
-#include <QJsonArray>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonParseError>
-#include <QJsonValue>
-#include <QUrl>
-
-using DataType = QmlDesigner::CollectionDetails::DataType;
-
-namespace {
-
-using CollectionDataVariant = std::variant<QString, bool, double, int, QUrl, QColor>;
-
-inline bool operator<(const QColor &a, const QColor &b)
-{
- return a.name(QColor::HexArgb) < b.name(QColor::HexArgb);
-}
-
-inline CollectionDataVariant valueToVariant(const QVariant &value, DataType type)
-{
- switch (type) {
- case DataType::String:
- return value.toString();
- case DataType::Real:
- return value.toDouble();
- case DataType::Integer:
- return value.toInt();
- case DataType::Boolean:
- return value.toBool();
- case DataType::Color:
- return value.value<QColor>();
- case DataType::Image:
- case DataType::Url:
- return value.value<QUrl>();
- default:
- return false;
- }
-}
-
-struct LessThanVisitor
-{
- template<typename T1, typename T2>
- bool operator()(const T1 &a, const T2 &b) const
- {
- return CollectionDataVariant(a).index() < CollectionDataVariant(b).index();
- }
-
- template<typename T>
- bool operator()(const T &a, const T &b) const
- {
- return a < b;
- }
-};
-
-Utils::FilePath findFile(const Utils::FilePath &path, const QString &fileName)
-{
- QDirIterator it(path.toString(), QDirIterator::Subdirectories);
-
- while (it.hasNext()) {
- QFileInfo file(it.next());
- if (file.isDir())
- continue;
-
- if (file.fileName() == fileName)
- return Utils::FilePath::fromFileInfo(file);
- }
- return {};
-}
-
-Utils::FilePath dataStoreDir()
-{
- using Utils::FilePath;
- ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::startupProject();
-
- if (!currentProject)
- return {};
-
- FilePath oldImportDirectory = currentProject->projectDirectory().pathAppended(
- "imports/" + currentProject->displayName());
- if (oldImportDirectory.exists())
- return oldImportDirectory;
-
- return currentProject->projectDirectory().pathAppended(currentProject->displayName());
-}
-
-inline Utils::FilePath collectionPath(const QString &filePath)
-{
- return dataStoreDir().pathAppended(filePath);
-}
-
-inline Utils::FilePath qmlDirFilePath()
-{
- return collectionPath("qmldir");
-}
-
-} // namespace
-
-namespace QmlDesigner::CollectionEditorUtils {
-
-bool variantIslessThan(const QVariant &a, const QVariant &b, DataType type)
-{
- return std::visit(LessThanVisitor{}, valueToVariant(a, type), valueToVariant(b, type));
-}
-
-QString getSourceCollectionType(const ModelNode &node)
-{
- using namespace QmlDesigner;
- if (node.type() == CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME)
- return "json";
-
- return {};
-}
-
-Utils::FilePath dataStoreJsonFilePath()
-{
- return collectionPath("models.json");
-}
-
-Utils::FilePath dataStoreQmlFilePath()
-{
- return collectionPath("DataStore.qml");
-}
-
-bool canAcceptCollectionAsModel(const ModelNode &node)
-{
- const NodeMetaInfo nodeMetaInfo = node.metaInfo();
- if (!nodeMetaInfo.isValid())
- return false;
-
- const PropertyMetaInfo modelProperty = nodeMetaInfo.property("model");
- if (!modelProperty.isValid())
- return false;
-
- return modelProperty.isWritable() && !modelProperty.isPrivate()
- && modelProperty.propertyType().isVariant();
-}
-
-bool hasTextRoleProperty(const ModelNode &node)
-{
- const NodeMetaInfo nodeMetaInfo = node.metaInfo();
- if (!nodeMetaInfo.isValid())
- return false;
-
- const PropertyMetaInfo textRoleProperty = nodeMetaInfo.property("textRole");
- if (!textRoleProperty.isValid())
- return false;
-
- return textRoleProperty.isWritable() && !textRoleProperty.isPrivate()
- && textRoleProperty.propertyType().isString();
-}
-
-QString getSourceCollectionPath(const ModelNode &dataStoreNode)
-{
- using Utils::FilePath;
- if (!dataStoreNode.isValid())
- return {};
-
- const FilePath expectedFile = dataStoreJsonFilePath();
-
- if (expectedFile.exists())
- return expectedFile.toFSPathString();
-
- return {};
-}
-
-bool isDataStoreNode(const ModelNode &dataStoreNode)
-{
- using Utils::FilePath;
-
- if (!dataStoreNode.isValid())
- return false;
-
- const FilePath expectedFile = dataStoreQmlFilePath();
-
- if (!expectedFile.exists())
- return false;
-
- FilePath modelPath = FilePath::fromUserInput(dataStoreNode.model()->fileUrl().toLocalFile());
-
- return modelPath.isSameFile(expectedFile);
-}
-
-bool ensureDataStoreExists(bool &justCreated)
-{
- using Utils::FilePath;
- using Utils::FileReader;
- using Utils::FileSaver;
-
- FilePath qmlDestinationPath = dataStoreQmlFilePath();
- justCreated = false;
-
- auto extractDependency = [&justCreated](const FilePath &filePath) -> bool {
- if (filePath.exists())
- return true;
-
- const QString templateFileName = filePath.fileName() + u".tpl";
- const FilePath templatePath = findFile(Core::ICore::resourcePath(), templateFileName);
- if (!templatePath.exists()) {
- qWarning() << Q_FUNC_INFO << __LINE__ << templateFileName << "does not exist";
- return false;
- }
-
- if (!filePath.parentDir().ensureWritableDir()) {
- qWarning() << Q_FUNC_INFO << __LINE__ << "Cannot create directory"
- << filePath.parentDir();
- return false;
- }
-
- if (templatePath.copyFile(filePath)) {
- justCreated = true;
- return true;
- }
-
- qWarning() << Q_FUNC_INFO << __LINE__ << "Cannot copy" << templateFileName << "to" << filePath;
- return false;
- };
-
- if (!extractDependency(dataStoreJsonFilePath()))
- return false;
-
- if (!extractDependency(collectionPath("data.json")))
- return false;
-
- if (!extractDependency(collectionPath("JsonData.qml")))
- return false;
-
- if (!qmlDestinationPath.exists()) {
- if (qmlDestinationPath.ensureExistingFile()) {
- justCreated = true;
- } else {
- qWarning() << Q_FUNC_INFO << __LINE__ << "Can't create DataStore Qml File";
- return false;
- }
- }
-
- FilePath qmlDirPath = qmlDirFilePath();
- qmlDirPath.ensureExistingFile();
-
- FileReader qmlDirReader;
- if (!qmlDirReader.fetch(qmlDirPath)) {
- qWarning() << Q_FUNC_INFO << __LINE__ << "Can't read the content of the qmldir";
- return false;
- }
-
- QByteArray qmlDirContent = qmlDirReader.data();
- const QList<QByteArray> qmlDirLines = qmlDirContent.split('\n');
- for (const QByteArray &line : qmlDirLines) {
- if (line.startsWith("singleton DataStore "))
- return true;
- }
-
- if (!qmlDirContent.isEmpty() && qmlDirContent.back() != '\n')
- qmlDirContent.append("\n");
- qmlDirContent.append("singleton DataStore 1.0 DataStore.qml\n");
-
- FileSaver qmlDirSaver(qmlDirPath);
- qmlDirSaver.write(qmlDirContent);
-
- if (qmlDirSaver.finalize()) {
- justCreated = true;
- return true;
- }
-
- qWarning() << Q_FUNC_INFO << __LINE__ << "Can't write to the qmldir file";
- return false;
-}
-
-QJsonObject defaultCollection()
-{
- QJsonObject collectionObject;
-
- QJsonArray columns;
- QJsonObject defaultColumn;
- defaultColumn.insert("name", "Column 1");
- defaultColumn.insert("type", CollectionDataTypeModel::dataTypeToString(DataType::String));
- columns.append(defaultColumn);
-
- QJsonArray collectionData;
- QJsonArray cellData;
- cellData.append(QString{});
- collectionData.append(cellData);
-
- collectionObject.insert("columns", columns);
- collectionObject.insert("data", collectionData);
-
- return collectionObject;
-}
-
-QJsonObject defaultColorCollection()
-{
- using Utils::FilePath;
- using Utils::FileReader;
- const FilePath templatePath = findFile(Core::ICore::resourcePath(), "Colors.json.tpl");
-
- FileReader fileReader;
- if (!fileReader.fetch(templatePath)) {
- qWarning() << Q_FUNC_INFO << __LINE__ << "Can't read the content of the file" << templatePath;
- return {};
- }
-
- QJsonParseError parseError;
- const CollectionDetails collection = CollectionDetails::fromImportedJson(fileReader.data(),
- &parseError);
- if (parseError.error != QJsonParseError::NoError) {
- qWarning() << Q_FUNC_INFO << __LINE__ << "Error in template file" << parseError.errorString();
- return {};
- }
-
- return collection.toLocalJson();
-}
-
-bool writeToJsonDocument(const Utils::FilePath &path, const QJsonDocument &document, QString *errorString)
-{
- Core::FileChangeBlocker fileBlocker(path);
- Utils::FileSaver jsonFile(path);
- if (jsonFile.write(document.toJson()))
- jsonFile.finalize();
- if (errorString)
- *errorString = jsonFile.errorString();
-
- return !jsonFile.hasError();
-}
-
-} // namespace QmlDesigner::CollectionEditorUtils
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h
deleted file mode 100644
index 355addf59b..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// 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 "collectiondetails.h"
-#include "collectioneditorconstants.h"
-
-QT_BEGIN_NAMESPACE
-class QJsonArray;
-class QJsonObject;
-QT_END_NAMESPACE
-
-namespace Utils {
-class FilePath;
-}
-
-namespace QmlDesigner::CollectionEditorUtils {
-
-bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type);
-
-QString getSourceCollectionType(const QmlDesigner::ModelNode &node);
-
-QString getSourceCollectionPath(const QmlDesigner::ModelNode &dataStoreNode);
-
-Utils::FilePath dataStoreJsonFilePath();
-
-Utils::FilePath dataStoreQmlFilePath();
-
-bool writeToJsonDocument(const Utils::FilePath &path,
- const QJsonDocument &document,
- QString *errorString = nullptr);
-
-bool isDataStoreNode(const ModelNode &dataStoreNode);
-
-bool ensureDataStoreExists(bool &justCreated);
-
-bool canAcceptCollectionAsModel(const ModelNode &node);
-
-bool hasTextRoleProperty(const ModelNode &node);
-
-QJsonObject defaultCollection();
-
-QJsonObject defaultColorCollection();
-
-} // namespace QmlDesigner::CollectionEditorUtils
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp
deleted file mode 100644
index d27a077d2a..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp
+++ /dev/null
@@ -1,521 +0,0 @@
-// 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 "collectionlistmodel.h"
-
-#include "collectioneditorutils.h"
-
-#include <utils/algorithm.h>
-#include <utils/fileutils.h>
-#include <utils/qtcassert.h>
-
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonParseError>
-
-namespace {
-
-template<typename ValueType>
-bool containsItem(const std::initializer_list<ValueType> &container, const ValueType &value)
-{
- auto begin = std::cbegin(container);
- auto end = std::cend(container);
-
- auto it = std::find(begin, end, value);
- return it != end;
-}
-
-bool sameCollectionNames(QStringList a, QStringList b)
-{
- if (a.size() != b.size())
- return false;
-
- a.sort(Qt::CaseSensitive);
- b.sort(Qt::CaseSensitive);
-
- return a == b;
-}
-
-} // namespace
-
-namespace QmlDesigner {
-
-CollectionListModel::CollectionListModel()
- : QAbstractListModel()
-{
- connect(this, &CollectionListModel::modelReset, this, &CollectionListModel::updateEmpty);
- connect(this, &CollectionListModel::rowsRemoved, this, &CollectionListModel::updateEmpty);
- connect(this, &CollectionListModel::rowsInserted, this, &CollectionListModel::updateEmpty);
-}
-
-QHash<int, QByteArray> CollectionListModel::roleNames() const
-{
- static QHash<int, QByteArray> roles;
- if (roles.isEmpty()) {
- roles.insert(Super::roleNames());
- roles.insert({
- {IdRole, "collectionId"},
- {NameRole, "collectionName"},
- {SelectedRole, "collectionIsSelected"},
- });
- }
- return roles;
-}
-
-int CollectionListModel::rowCount([[maybe_unused]] const QModelIndex &parent) const
-{
- return m_data.count();
-}
-
-bool CollectionListModel::setData(const QModelIndex &index, const QVariant &value, int role)
-{
- if (!index.isValid())
- return false;
-
- if (containsItem<int>({Qt::EditRole, Qt::DisplayRole, NameRole}, role)) {
- if (collectionExists(value.toString()))
- return false;
-
- QString oldName = collectionNameAt(index.row());
- bool nameChanged = value != data(index);
- if (nameChanged) {
- QString newName = value.toString();
- QString errorString;
- if (renameCollectionInDataStore(oldName, newName, errorString)) {
- m_data.replace(index.row(), newName);
- emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, NameRole});
- emit this->collectionNameChanged(oldName, newName);
- if (m_selectedCollectionName == oldName)
- updateSelectedCollectionName();
- return true;
- } else {
- emit warning("Rename Model", errorString);
- return false;
- }
- }
- } else if (role == SelectedRole) {
- if (value.toBool() != index.data(SelectedRole).toBool()) {
- setSelectedIndex(value.toBool() ? index.row() : -1);
- return true;
- }
- }
- return false;
-}
-
-bool CollectionListModel::removeRows(int row, int count, const QModelIndex &parent)
-{
- const int rows = rowCount(parent);
- if (row >= rows)
- return false;
-
- row = qBound(0, row, rows - 1);
- count = qBound(0, count, rows - row);
-
- if (count < 1)
- return false;
-
- QString errorString;
- QStringList removedCollections = m_data.mid(row, count);
- if (removeCollectionsFromDataStore(removedCollections, errorString)) {
- beginRemoveRows(parent, row, row + count - 1);
- m_data.remove(row, count);
- endRemoveRows();
-
- emit collectionsRemoved(removedCollections);
- if (m_selectedIndex >= row) {
- int preferredIndex = m_selectedIndex - count;
- if (preferredIndex < 0) // If the selected item is deleted, reset selection
- selectCollectionIndex(-1);
- selectCollectionIndex(preferredIndex, true);
- }
-
- updateSelectedCollectionName();
- return true;
- } else {
- emit warning("Remove Model", errorString);
- return false;
- }
-}
-
-QVariant CollectionListModel::data(const QModelIndex &index, int role) const
-{
- QTC_ASSERT(index.isValid(), return {});
-
- switch (role) {
- case IdRole:
- return index.row();
- case SelectedRole:
- return index.row() == m_selectedIndex;
- case NameRole:
- default:
- return m_data.at(index.row());
- }
-}
-
-void CollectionListModel::setDataStoreNode(const ModelNode &dataStoreNode)
-{
- m_dataStoreNode = dataStoreNode;
- update();
-}
-
-int CollectionListModel::selectedIndex() const
-{
- return m_selectedIndex;
-}
-
-ModelNode CollectionListModel::sourceNode() const
-{
- return m_dataStoreNode;
-}
-
-bool CollectionListModel::collectionExists(const QString &collectionName) const
-{
- return m_data.contains(collectionName);
-}
-
-QStringList CollectionListModel::collections() const
-{
- return m_data;
-}
-
-QString CollectionListModel::getUniqueCollectionName(const QString &baseName) const
-{
- QString name = baseName.isEmpty() ? "Model" : baseName;
- QString nameTemplate = name + "%1";
-
- int num = 0;
-
- while (collectionExists(name))
- name = nameTemplate.arg(++num, 2, 10, QChar('0'));
-
- return name;
-}
-
-void CollectionListModel::selectCollectionIndex(int idx, bool selectAtLeastOne)
-{
- int collectionCount = m_data.size();
- int preferredIndex = -1;
- if (collectionCount) {
- if (selectAtLeastOne)
- preferredIndex = std::max(0, std::min(idx, collectionCount - 1));
- else if (idx > -1 && idx < collectionCount)
- preferredIndex = idx;
- }
-
- setSelectedIndex(preferredIndex);
-}
-
-void CollectionListModel::selectCollectionName(QString collectionName, bool selectAtLeastOne)
-{
- int idx = m_data.indexOf(collectionName);
- if (idx > -1)
- selectCollectionIndex(idx);
- else
- selectCollectionIndex(selectedIndex(), selectAtLeastOne);
-
- collectionName = collectionNameAt(selectedIndex());
- if (m_selectedCollectionName == collectionName)
- return;
-
- m_selectedCollectionName = collectionName;
- emit selectedCollectionNameChanged(m_selectedCollectionName);
-}
-
-QString CollectionListModel::collectionNameAt(int idx) const
-{
- return index(idx).data(NameRole).toString();
-}
-
-QString CollectionListModel::selectedCollectionName() const
-{
- return m_selectedCollectionName;
-}
-
-void CollectionListModel::update()
-{
- using Utils::FilePath;
- using Utils::FileReader;
-
- FileReader sourceFile;
- QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode);
- FilePath path = FilePath::fromUserInput(sourceFileAddress);
- bool fileRead = false;
- if (path.exists()) {
- fileRead = sourceFile.fetch(path);
- if (!fileRead)
- emit this->warning(tr("Model Editor"),
- tr("Cannot read the dataStore file\n%1").arg(sourceFile.errorString()));
- }
-
- QStringList collectionNames;
- if (fileRead) {
- QJsonParseError parseError;
- QJsonDocument document = QJsonDocument::fromJson(sourceFile.data(), &parseError);
- if (parseError.error != QJsonParseError::NoError) {
- emit this->warning(tr("Model Editor"),
- tr("There is an error in the JSON file.\n%1")
- .arg(parseError.errorString()));
- } else {
- if (document.isObject())
- collectionNames = document.object().toVariantMap().keys();
- else
- emit this->warning(tr("Model Editor"), tr("The JSON document be an object."));
- }
- }
-
- if (!sameCollectionNames(m_data, collectionNames)) {
- QString prevSelectedCollection = selectedIndex() > -1 ? m_data.at(selectedIndex())
- : QString();
- beginResetModel();
- m_data = collectionNames;
- endResetModel();
- emit this->collectionNamesChanged(collections());
- selectCollectionName(prevSelectedCollection, true);
- }
-}
-
-bool CollectionListModel::addCollection(const QString &collectionName,
- const QJsonObject &localCollection)
-{
- if (collectionExists(collectionName)) {
- emit warning(tr("Add Model"), tr("Model \"%1\" already exists.").arg(collectionName));
- return false;
- }
-
- QString errorMessage;
- if (addCollectionToDataStore(collectionName, localCollection, errorMessage)) {
- int row = rowCount();
- beginInsertRows({}, row, row);
- m_data.append(collectionName);
- endInsertRows();
-
- selectCollectionName(collectionName);
- emit collectionAdded(collectionName);
- return true;
- } else {
- emit warning(tr("Add Collection"), errorMessage);
- }
- return false;
-}
-
-void CollectionListModel::setSelectedIndex(int idx)
-{
- idx = (idx > -1 && idx < rowCount()) ? idx : -1;
-
- if (m_selectedIndex != idx) {
- QModelIndex previousIndex = index(m_selectedIndex);
- QModelIndex newIndex = index(idx);
-
- m_selectedIndex = idx;
-
- if (previousIndex.isValid())
- emit dataChanged(previousIndex, previousIndex, {SelectedRole});
-
- if (newIndex.isValid())
- emit dataChanged(newIndex, newIndex, {SelectedRole});
-
- emit selectedIndexChanged(idx);
- updateSelectedCollectionName();
- }
-}
-
-bool CollectionListModel::removeCollectionsFromDataStore(const QStringList &removedCollections,
- QString &error) const
-{
- using Utils::FilePath;
- using Utils::FileReader;
- auto setErrorAndReturn = [&error](const QString &msg) -> bool {
- error = msg;
- return false;
- };
-
- if (m_dataStoreNode.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME)
- return setErrorAndReturn(tr("Invalid node type"));
-
- QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode);
-
- QFileInfo sourceFileInfo(sourceFileAddress);
- if (!sourceFileInfo.isFile())
- return setErrorAndReturn(tr("The selected node has an invalid source address"));
-
- FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress);
- FileReader jsonFile;
- if (!jsonFile.fetch(jsonPath)) {
- return setErrorAndReturn(tr("Can't read file \"%1\".\n%2")
- .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString()));
- }
-
- QJsonParseError parseError;
- QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError);
- if (parseError.error != QJsonParseError::NoError) {
- return setErrorAndReturn(tr("\"%1\" is corrupted.\n%2")
- .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString()));
- }
-
- if (document.isObject()) {
- QJsonObject rootObject = document.object();
-
- for (const QString &collectionName : removedCollections) {
- bool sourceContainsCollection = rootObject.contains(collectionName);
- if (sourceContainsCollection) {
- rootObject.remove(collectionName);
- } else {
- setErrorAndReturn(tr("The model group doesn't contain the model name (%1).")
- .arg(sourceContainsCollection));
- }
- }
-
- document.setObject(rootObject);
-
- if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) {
- error.clear();
- return true;
- } else {
- return setErrorAndReturn(
- tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
- }
- } else {
- return setErrorAndReturn(tr("Local Json Document should be an object"));
- }
-
- return false;
-}
-
-bool CollectionListModel::renameCollectionInDataStore(const QString &oldName,
- const QString &newName,
- QString &error)
-{
- using Utils::FilePath;
- using Utils::FileReader;
- using Utils::FileSaver;
-
- auto setErrorAndReturn = [&error](const QString &msg) -> bool {
- error = msg;
- return false;
- };
-
- if (m_dataStoreNode.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME)
- return setErrorAndReturn(tr("Invalid node type"));
-
- QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode);
-
- QFileInfo sourceFileInfo(sourceFileAddress);
- if (!sourceFileInfo.isFile())
- return setErrorAndReturn(tr("Selected node must have a valid source file address"));
-
- FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress);
- FileReader jsonFile;
- if (!jsonFile.fetch(jsonPath)) {
- return setErrorAndReturn(
- tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString()));
- }
-
- QJsonParseError parseError;
- QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError);
- if (parseError.error != QJsonParseError::NoError) {
- return setErrorAndReturn(tr("\"%1\" is corrupted.\n%2")
- .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString()));
- }
-
- if (document.isObject()) {
- QJsonObject rootObject = document.object();
-
- bool collectionContainsOldName = rootObject.contains(oldName);
- bool collectionContainsNewName = rootObject.contains(newName);
-
- if (!collectionContainsOldName) {
- return setErrorAndReturn(
- tr("The model group doesn't contain the old model name (%1).").arg(oldName));
- }
-
- if (collectionContainsNewName) {
- return setErrorAndReturn(
- tr("The model name \"%1\" already exists in the model group.").arg(newName));
- }
-
- QJsonValue oldValue = rootObject.value(oldName);
- rootObject.insert(newName, oldValue);
- rootObject.remove(oldName);
-
- document.setObject(rootObject);
-
- if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) {
- error.clear();
- return true;
- } else {
- return setErrorAndReturn(
- tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
- }
- } else {
- return setErrorAndReturn(tr("Local Json Document should be an object"));
- }
- return false;
-}
-
-bool CollectionListModel::addCollectionToDataStore(const QString &collectionName,
- const QJsonObject &localCollection,
- QString &errorString) const
-{
- using Utils::FilePath;
- using Utils::FileReader;
- auto returnError = [&errorString](const QString &msg) -> bool {
- errorString = msg;
- return false;
- };
-
- if (collectionExists(collectionName))
- return returnError(tr("A model with the identical name already exists."));
-
- QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode);
-
- QFileInfo sourceFileInfo(sourceFileAddress);
- if (!sourceFileInfo.isFile())
- return returnError(tr("Selected node must have a valid source file address"));
-
- FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress);
- FileReader jsonFile;
- if (!jsonFile.fetch(jsonPath)) {
- return returnError(
- tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString()));
- }
-
- QJsonParseError parseError;
- QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError);
- if (parseError.error != QJsonParseError::NoError)
- return returnError(tr("\"%1\" is corrupted.\n%2")
- .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString()));
-
- if (document.isObject()) {
- QJsonObject sourceObject = document.object();
- sourceObject.insert(collectionName, localCollection);
- document.setObject(sourceObject);
-
- if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document))
- return true;
- else
- return returnError(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
- } else {
- return returnError(tr("JSON document type should be an object containing models."));
- }
-}
-
-void CollectionListModel::updateEmpty()
-{
- bool isEmptyNow = m_data.isEmpty();
- if (m_isEmpty != isEmptyNow) {
- m_isEmpty = isEmptyNow;
- emit isEmptyChanged(m_isEmpty);
-
- if (m_isEmpty)
- setSelectedIndex(-1);
- }
-}
-
-void CollectionListModel::updateSelectedCollectionName()
-{
- QString selectedCollectionByIndex = collectionNameAt(selectedIndex());
- if (selectedCollectionByIndex != selectedCollectionName())
- selectCollectionName(selectedCollectionByIndex);
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h
deleted file mode 100644
index 7902fd5909..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// 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 <QAbstractListModel>
-#include <QHash>
-
-#include "modelnode.h"
-
-namespace QmlDesigner {
-
-class CollectionListModel : public QAbstractListModel
-{
- Q_OBJECT
-
- Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged)
- Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
- Q_PROPERTY(QString selectedCollectionName
- READ selectedCollectionName
- WRITE selectCollectionName
- NOTIFY selectedCollectionNameChanged)
-
-public:
- enum Roles { IdRole = Qt::UserRole + 1, NameRole, SelectedRole };
-
- explicit CollectionListModel();
- QHash<int, QByteArray> roleNames() const override;
-
- int rowCount(const QModelIndex &parent = QModelIndex()) const override;
- bool setData(const QModelIndex &index, const QVariant &value, int role) override;
- bool removeRows(int row, int count, const QModelIndex &parent = {}) override;
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
- void setDataStoreNode(const ModelNode &dataStoreNode = {});
-
- Q_INVOKABLE int selectedIndex() const;
- Q_INVOKABLE ModelNode sourceNode() const;
- Q_INVOKABLE bool collectionExists(const QString &collectionName) const;
- Q_INVOKABLE QStringList collections() const;
- Q_INVOKABLE QString getUniqueCollectionName(const QString &baseName = {}) const;
-
- void selectCollectionIndex(int idx, bool selectAtLeastOne = false);
- void selectCollectionName(QString collectionName, bool selectAtLeastOne = false);
- QString collectionNameAt(int idx) const;
- QString selectedCollectionName() const;
-
- void update();
- bool addCollection(const QString &collectionName, const QJsonObject &localCollection);
-
-signals:
- void selectedIndexChanged(int idx);
- void isEmptyChanged(bool);
- void collectionNameChanged(const QString &oldName, const QString &newName);
- void collectionNamesChanged(const QStringList &collectionNames);
- void collectionsRemoved(const QStringList &names);
- void collectionAdded(const QString &name);
- void selectedCollectionNameChanged(const QString &selectedCollectionName);
- void warning(const QString &title, const QString &body);
-
-private:
- void setSelectedIndex(int idx);
- bool removeCollectionsFromDataStore(const QStringList &removedCollections, QString &error) const;
- bool renameCollectionInDataStore(const QString &oldName, const QString &newName, QString &error);
- bool addCollectionToDataStore(const QString &collectionName,
- const QJsonObject &localCollection,
- QString &errorString) const;
-
- void updateEmpty();
- void updateSelectedCollectionName();
-
- using Super = QAbstractListModel;
- int m_selectedIndex = -1;
- bool m_isEmpty = false;
- ModelNode m_dataStoreNode;
- QString m_selectedCollectionName;
- QStringList m_data;
-};
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp
deleted file mode 100644
index 0c9a2eed94..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp
+++ /dev/null
@@ -1,501 +0,0 @@
-// 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 "collectionview.h"
-
-#include "collectiondatatypemodel.h"
-#include "collectiondetailsmodel.h"
-#include "collectioneditorconstants.h"
-#include "collectioneditorutils.h"
-#include "collectionlistmodel.h"
-#include "collectionwidget.h"
-#include "datastoremodelnode.h"
-#include "designmodecontext.h"
-#include "nodeabstractproperty.h"
-#include "nodemetainfo.h"
-#include "nodeproperty.h"
-#include "qmldesignerplugin.h"
-#include "variantproperty.h"
-
-#include <projectexplorer/project.h>
-#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/projectmanager.h>
-#include <qmljs/qmljsmodelmanagerinterface.h>
-
-#include <coreplugin/icore.h>
-#include <utils/algorithm.h>
-#include <utils/qtcassert.h>
-
-#include <QTimer>
-
-namespace {
-
-bool isStudioCollectionModel(const QmlDesigner::ModelNode &node)
-{
- return node.metaInfo().isQtQuickStudioUtilsJsonListModel();
-}
-
-inline bool isProjectImport(const QmlDesigner::Import &import)
-{
- ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::startupProject();
- return currentProject && import.toString() == currentProject->displayName();
-}
-
-inline void setVariantPropertyValue(const QmlDesigner::ModelNode &node,
- const QmlDesigner::PropertyName &propertyName,
- const QVariant &value)
-{
- QmlDesigner::VariantProperty property = node.variantProperty(propertyName);
- property.setValue(value);
-}
-
-inline void setBindingPropertyExpression(const QmlDesigner::ModelNode &node,
- const QmlDesigner::PropertyName &propertyName,
- const QString &expression)
-{
- QmlDesigner::BindingProperty property = node.bindingProperty(propertyName);
- property.setExpression(expression);
-}
-
-} // namespace
-
-namespace QmlDesigner {
-
-CollectionView::CollectionView(ExternalDependenciesInterface &externalDependencies)
- : AbstractView(externalDependencies)
- , m_dataStore(std::make_unique<DataStoreModelNode>())
-
-{
-}
-
-CollectionView::~CollectionView() = default;
-
-bool CollectionView::hasWidget() const
-{
- return true;
-}
-
-QmlDesigner::WidgetInfo CollectionView::widgetInfo()
-{
- if (!m_widget) {
- m_widget = Utils::makeUniqueObjectPtr<CollectionWidget>(this);
- m_widget->setMinimumSize(m_widget->minimumSizeHint());
- connect(ProjectExplorer::ProjectManager::instance(),
- &ProjectExplorer::ProjectManager::startupProjectChanged, m_widget.get(), [&] {
- resetDataStoreNode();
- m_widget->collectionDetailsModel()->removeAllCollections();
- });
-
- auto collectionEditorContext = new Internal::CollectionEditorContext(m_widget.get());
- Core::ICore::addContextObject(collectionEditorContext);
- CollectionListModel *listModel = m_widget->listModel().data();
-
- connect(listModel,
- &CollectionListModel::selectedCollectionNameChanged,
- this,
- [this](const QString &collection) {
- m_widget->collectionDetailsModel()->loadCollection(dataStoreNode(), collection);
- });
-
- connect(listModel, &CollectionListModel::isEmptyChanged, this, [this](bool isEmpty) {
- if (isEmpty)
- m_widget->collectionDetailsModel()->loadCollection({}, {});
- });
-
- connect(listModel, &CollectionListModel::modelReset, this, [this] {
- CollectionListModel *listModel = m_widget->listModel().data();
- if (listModel->sourceNode() == dataStoreNode())
- m_dataStore->setCollectionNames(listModel->collections());
- });
-
- connect(listModel,
- &CollectionListModel::collectionAdded,
- this,
- [this](const QString &collectionName) { m_dataStore->addCollection(collectionName); });
-
- connect(listModel,
- &CollectionListModel::collectionNameChanged,
- this,
- [this](const QString &oldName, const QString &newName) {
- m_dataStore->renameCollection(oldName, newName);
- m_widget->collectionDetailsModel()->renameCollection(dataStoreNode(),
- oldName,
- newName);
- });
-
- connect(listModel,
- &CollectionListModel::collectionsRemoved,
- this,
- [this](const QStringList &collectionNames) {
- m_dataStore->removeCollections(collectionNames);
- for (const QString &collectionName : collectionNames) {
- m_widget->collectionDetailsModel()->removeCollection(dataStoreNode(),
- collectionName);
- }
- });
- }
-
- return createWidgetInfo(m_widget.get(),
- "CollectionEditor",
- WidgetInfo::LeftPane,
- 0,
- tr("Model Editor [beta]"),
- tr("Model Editor view"));
-}
-
-void CollectionView::modelAttached(Model *model)
-{
- AbstractView::modelAttached(model);
- m_widget->setProjectImportExists(Utils::anyOf(model->imports(), isProjectImport));
- resetDataStoreNode();
-}
-
-void CollectionView::modelAboutToBeDetached([[maybe_unused]] Model *model)
-{
- unloadDataStore();
- m_widget->setProjectImportExists(false);
-}
-
-void CollectionView::selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
- [[maybe_unused]] const QList<ModelNode> &lastSelectedNodeList)
-{
- if (!m_widget)
- return;
-
- QList<ModelNode> selectedCollectionNodes = Utils::filtered(selectedNodeList,
- &isStudioCollectionModel);
-
- bool singleNonCollectionNodeSelected = selectedNodeList.size() == 1
- && selectedCollectionNodes.isEmpty();
-
- bool singleSelectedHasModelProperty = false;
- if (singleNonCollectionNodeSelected) {
- const ModelNode selectedNode = selectedNodeList.first();
- singleSelectedHasModelProperty = CollectionEditorUtils::canAcceptCollectionAsModel(
- selectedNode);
- }
-
- m_widget->setTargetNodeSelected(singleSelectedHasModelProperty);
-}
-
-void CollectionView::importsChanged(const Imports &addedImports, const Imports &removedImports)
-{
- if (Utils::anyOf(addedImports, isProjectImport)) {
- m_widget->setProjectImportExists(true);
- resetDataStoreNode();
- } else if (Utils::anyOf(removedImports, isProjectImport)) {
- m_widget->setProjectImportExists(false);
- unloadDataStore();
- }
-}
-
-void CollectionView::customNotification(const AbstractView *,
- const QString &identifier,
- const QList<ModelNode> &nodeList,
- const QList<QVariant> &data)
-{
- if (!m_widget)
- return;
-
- if (identifier == QLatin1String("item_library_created_by_drop") && !nodeList.isEmpty())
- onItemLibraryNodeCreated(nodeList.first());
- else if (identifier == QLatin1String("open_collection_by_id") && !data.isEmpty())
- m_widget->openCollection(collectionNameFromDataStoreChildren(data.first().toByteArray()));
- else if (identifier == "delete_selected_collection")
- m_widget->deleteSelectedCollection();
-}
-
-void CollectionView::addResource(const QUrl &url, const QString &name)
-{
- executeInTransaction(Q_FUNC_INFO, [this, &url, &name]() {
- ensureStudioModelImport();
- QString sourceAddress;
- if (url.isLocalFile()) {
- Utils::FilePath fp = QmlDesignerPlugin::instance()->currentDesignDocument()->fileName().parentDir();
- sourceAddress = Utils::FilePath::calcRelativePath(url.toLocalFile(),
- fp.absoluteFilePath().toString());
- } else {
- sourceAddress = url.toString();
- }
-#ifdef QDS_USE_PROJECTSTORAGE
- ModelNode resourceNode = createModelNode("JsonListModel");
-#else
- const NodeMetaInfo resourceMetaInfo = jsonCollectionMetaInfo();
- ModelNode resourceNode = createModelNode(resourceMetaInfo.typeName(),
- resourceMetaInfo.majorVersion(),
- resourceMetaInfo.minorVersion());
-#endif
- VariantProperty sourceProperty = resourceNode.variantProperty(
- CollectionEditorConstants::SOURCEFILE_PROPERTY);
- VariantProperty nameProperty = resourceNode.variantProperty("objectName");
- sourceProperty.setValue(sourceAddress);
- nameProperty.setValue(name);
- resourceNode.setIdWithoutRefactoring(model()->generateIdFromName(name, "model"));
- rootModelNode().defaultNodeAbstractProperty().reparentHere(resourceNode);
- });
-}
-
-void CollectionView::addProjectImport()
-{
- if (!m_widget)
- return;
-
- ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::startupProject();
- if (!currentProject)
- return;
-
- executeInTransaction(__FUNCTION__, [&] {
- Import import = Import::createLibraryImport(currentProject->displayName());
- if (!model()->hasImport(import, true, true))
- model()->changeImports({import}, {});
- });
-}
-
-void CollectionView::assignCollectionToNode(const QString &collectionName, const ModelNode &node)
-{
- if (!m_widget)
- return;
-
- using DataType = CollectionDetails::DataType;
- executeInTransaction("CollectionView::assignCollectionToNode", [&]() {
- m_dataStore->assignCollectionToNode(
- this,
- node,
- collectionName,
- [&](const QString &collectionName, const QString &columnName) -> bool {
- const CollectionReference reference{dataStoreNode(), collectionName};
- return m_widget->collectionDetailsModel()->collectionHasColumn(reference, columnName);
- },
- [&](const QString &collectionName) -> QString {
- const CollectionReference reference{dataStoreNode(), collectionName};
- return m_widget->collectionDetailsModel()->getFirstColumnName(reference);
- });
-
- // Create and assign a delegate to the list view item
- if (node.metaInfo().isQtQuickListView()) {
- CollectionDetails collection = m_widget->collectionDetailsModel()->upToDateConstCollection(
- {dataStoreNode(), collectionName});
-
- ModelNode rowItem(createModelNode("QtQuick.Row"));
- ::setVariantPropertyValue(rowItem, "spacing", 5);
-
- const int columnsCount = collection.columns();
- for (int column = 0; column < columnsCount; ++column) {
- const DataType dataType = collection.typeAt(column);
- const QString columnName = collection.propertyAt(column);
- ModelNode cellItem;
- if (dataType == DataType::Color) {
- cellItem = createModelNode("QtQuick.Rectangle");
- ::setBindingPropertyExpression(cellItem, "color", columnName);
- ::setVariantPropertyValue(cellItem, "height", 20);
- } else {
- cellItem = createModelNode("QtQuick.Text");
- ::setBindingPropertyExpression(cellItem, "text", columnName);
- }
- ::setVariantPropertyValue(cellItem, "width", 100);
- rowItem.defaultNodeAbstractProperty().reparentHere(cellItem);
- }
-
- NodeProperty delegateProperty = node.nodeProperty("delegate");
- // Remove the old model node if is available
- if (delegateProperty.modelNode())
- delegateProperty.modelNode().destroy();
-
- delegateProperty.setModelNode(rowItem);
- }
- });
-}
-
-void CollectionView::assignCollectionToSelectedNode(const QString &collectionName)
-{
- QTC_ASSERT(dataStoreNode() && hasSingleSelectedModelNode(), return);
- assignCollectionToNode(collectionName, singleSelectedModelNode());
-}
-
-void CollectionView::addNewCollection(const QString &collectionName, const QJsonObject &localCollection)
-{
- if (!m_widget)
- return;
-
- addTask(QSharedPointer<CollectionTask>(
- new AddCollectionTask(this, m_widget->listModel(), localCollection, collectionName)));
-}
-
-void CollectionView::openCollection(const QString &collectionName)
-{
- if (!m_widget)
- return;
-
- m_widget->openCollection(collectionName);
-}
-
-void CollectionView::registerDeclarativeType()
-{
- CollectionDetails::registerDeclarativeType();
- CollectionDataTypeModel::registerDeclarativeType();
-}
-
-void CollectionView::resetDataStoreNode()
-{
- if (!m_widget)
- return;
-
- m_dataStore->reloadModel();
-
- ModelNode dataStore = dataStoreNode();
- m_widget->setDataStoreExists(dataStore.isValid());
- if (!dataStore || m_widget->listModel()->sourceNode() == dataStore)
- return;
-
- bool dataStoreSingletonFound = m_dataStoreTypeFound;
- if (!dataStoreSingletonFound && rewriterView() && rewriterView()->isAttached()) {
- const QList<QmlTypeData> types = rewriterView()->getQMLTypes();
- for (const QmlTypeData &cppTypeData : types) {
- if (cppTypeData.isSingleton && cppTypeData.typeName == "DataStore") {
- dataStoreSingletonFound = true;
- break;
- }
- }
- if (!dataStoreSingletonFound && !m_rewriterAmended) {
- rewriterView()->forceAmend();
- m_rewriterAmended = true;
- }
- }
-
- if (dataStoreSingletonFound) {
- m_widget->listModel()->setDataStoreNode(dataStore);
- m_dataStoreTypeFound = true;
-
- while (!m_delayedTasks.isEmpty())
- m_delayedTasks.takeFirst()->process();
- } else if (++m_reloadCounter < 50) {
- QTimer::singleShot(200, this, &CollectionView::resetDataStoreNode);
- } else {
- QTC_ASSERT(false, m_delayedTasks.clear());
- }
-}
-
-ModelNode CollectionView::dataStoreNode() const
-{
- return m_dataStore->modelNode();
-}
-
-void CollectionView::ensureDataStoreExists()
-{
- bool filesJustCreated = false;
- bool filesExist = CollectionEditorUtils::ensureDataStoreExists(filesJustCreated);
- if (filesExist && filesJustCreated) {
- // Force code model reset to notice changes to existing module
- if (auto modelManager = QmlJS::ModelManagerInterface::instance())
- modelManager->resetCodeModel();
- resetDataStoreNode();
- }
-}
-
-QString CollectionView::collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const
-{
- return dataStoreNode()
- .nodeProperty(childPropertyName)
- .modelNode()
- .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)
- .toVariantProperty()
- .value()
- .toString();
-}
-
-NodeMetaInfo CollectionView::jsonCollectionMetaInfo() const
-{
- return model()->metaInfo(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME);
-}
-
-void CollectionView::unloadDataStore()
-{
- m_reloadCounter = 0;
- m_rewriterAmended = false;
- m_dataStoreTypeFound = false;
- QTC_ASSERT(m_delayedTasks.isEmpty(), m_delayedTasks.clear());
- if (m_widget) {
- m_widget->setDataStoreExists(dataStoreNode().isValid());
- m_widget->listModel()->setDataStoreNode();
- }
-}
-
-void CollectionView::ensureStudioModelImport()
-{
- executeInTransaction(__FUNCTION__, [&] {
- Import import = Import::createLibraryImport(CollectionEditorConstants::COLLECTIONMODEL_IMPORT);
- try {
- if (!model()->hasImport(import, true, true))
- model()->changeImports({import}, {});
- } catch (const Exception &) {
- QTC_ASSERT(false, return);
- }
- });
-}
-
-void CollectionView::onItemLibraryNodeCreated(const ModelNode &node)
-{
- if (!m_widget)
- return;
-
- if (node.metaInfo().isQtQuickListView()) {
- addTask(QSharedPointer<CollectionTask>(
- new DropListViewTask(this, m_widget->listModel(), node)));
- }
-}
-
-void CollectionView::addTask(QSharedPointer<CollectionTask> task)
-{
- ensureDataStoreExists();
- if (m_dataStoreTypeFound)
- task->process();
- else if (dataStoreNode())
- m_delayedTasks << task;
-}
-
-CollectionTask::CollectionTask(CollectionView *view, CollectionListModel *listModel)
- : m_collectionView(view)
- , m_listModel(listModel)
-{}
-
-DropListViewTask::DropListViewTask(CollectionView *view,
- CollectionListModel *listModel,
- const ModelNode &node)
- : CollectionTask(view, listModel)
- , m_node(node)
-{}
-
-void DropListViewTask::process()
-{
- AbstractView *view = m_node.view();
- if (!m_node || !m_collectionView || !m_listModel || !view)
- return;
-
- const QString newCollectionName = m_listModel->getUniqueCollectionName("ListModel");
- m_listModel->addCollection(newCollectionName, CollectionEditorUtils::defaultColorCollection());
- m_collectionView->openCollection(newCollectionName);
- m_collectionView->assignCollectionToNode(newCollectionName, m_node);
-}
-
-AddCollectionTask::AddCollectionTask(CollectionView *view,
- CollectionListModel *listModel,
- const QJsonObject &localJsonObject,
- const QString &collectionName)
- : CollectionTask(view, listModel)
- , m_localJsonObject(localJsonObject)
- , m_name(collectionName)
-{}
-
-void AddCollectionTask::process()
-{
- if (!m_listModel)
- return;
-
- const QString newCollectionName = m_listModel->collectionExists(m_name)
- ? m_listModel->getUniqueCollectionName(m_name)
- : m_name;
-
- m_listModel->addCollection(newCollectionName, m_localJsonObject);
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h
deleted file mode 100644
index 3de3bd7ae6..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h
+++ /dev/null
@@ -1,124 +0,0 @@
-// 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 "datastoremodelnode.h"
-
-#include <abstractview.h>
-#include <modelnode.h>
-
-#include <utils/uniqueobjectptr.h>
-
-#include <QJsonObject>
-
-namespace QmlJS {
-class Document;
-}
-
-namespace QmlDesigner {
-
-class CollectionDetails;
-class CollectionListModel;
-class CollectionTask;
-class CollectionWidget;
-class DataStoreModelNode;
-
-class CollectionView : public AbstractView
-{
- Q_OBJECT
-
-public:
- explicit CollectionView(ExternalDependenciesInterface &externalDependencies);
- ~CollectionView();
-
- bool hasWidget() const override;
- WidgetInfo widgetInfo() override;
-
- void modelAttached(Model *model) override;
- void modelAboutToBeDetached(Model *model) override;
-
- void selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
- const QList<ModelNode> &lastSelectedNodeList) override;
-
- void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
-
- void customNotification(const AbstractView *view,
- const QString &identifier,
- const QList<ModelNode> &nodeList,
- const QList<QVariant> &data) override;
-
- void addResource(const QUrl &url, const QString &name);
-
- void addProjectImport();
- void assignCollectionToNode(const QString &collectionName, const ModelNode &node);
- void assignCollectionToSelectedNode(const QString &collectionName);
- void addNewCollection(const QString &collectionName, const QJsonObject &localCollection);
-
- void openCollection(const QString &collectionName);
-
- static void registerDeclarativeType();
-
- void resetDataStoreNode();
- ModelNode dataStoreNode() const;
- void ensureDataStoreExists();
- QString collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const;
-
-private:
- friend class CollectionTask;
-
- NodeMetaInfo jsonCollectionMetaInfo() const;
- void unloadDataStore();
- void ensureStudioModelImport();
- void onItemLibraryNodeCreated(const ModelNode &node);
- void addTask(QSharedPointer<CollectionTask> task);
-
- std::unique_ptr<DataStoreModelNode> m_dataStore;
- Utils::UniqueObjectPtr<CollectionWidget> m_widget;
- QList<QSharedPointer<CollectionTask>> m_delayedTasks;
- bool m_dataStoreTypeFound = false;
- bool m_rewriterAmended = false;
- int m_reloadCounter = 0;
-};
-
-class CollectionTask
-{
-public:
- CollectionTask(CollectionView *view, CollectionListModel *listModel);
- CollectionTask() = delete;
- virtual ~CollectionTask() = default;
-
- virtual void process() = 0;
-
-protected:
- QPointer<CollectionView> m_collectionView;
- QPointer<CollectionListModel> m_listModel;
-};
-
-class DropListViewTask : public CollectionTask
-{
-public:
- DropListViewTask(CollectionView *view, CollectionListModel *listModel, const ModelNode &node);
-
- void process() override;
-
-private:
- ModelNode m_node;
-};
-
-class AddCollectionTask : public CollectionTask
-{
-public:
- AddCollectionTask(CollectionView *view,
- CollectionListModel *listModel,
- const QJsonObject &localJsonObject,
- const QString &collectionName);
-
- void process() override;
-
-private:
- QJsonObject m_localJsonObject;
- QString m_name;
-};
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp
deleted file mode 100644
index dd706145cf..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp
+++ /dev/null
@@ -1,320 +0,0 @@
-// 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 "collectionwidget.h"
-
-#include "collectiondetails.h"
-#include "collectiondetailsmodel.h"
-#include "collectiondetailssortfiltermodel.h"
-#include "collectioneditorutils.h"
-#include "collectionlistmodel.h"
-#include "collectionview.h"
-#include "designmodewidget.h"
-#include "qmldesignerconstants.h"
-#include "qmldesignerplugin.h"
-#include "theme.h"
-
-#include <coreplugin/icore.h>
-#include <coreplugin/messagebox.h>
-#include <studioquickwidget.h>
-
-#include <QFileInfo>
-#include <QJsonArray>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonParseError>
-#include <QMetaObject>
-#include <QQmlEngine>
-#include <QQuickItem>
-#include <QShortcut>
-#include <QVBoxLayout>
-
-namespace {
-
-QString collectionViewResourcesPath()
-{
-#ifdef SHARE_QML_PATH
- if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
- return QLatin1String(SHARE_QML_PATH) + "/collectionEditorQmlSource";
-#endif
- return Core::ICore::resourcePath("qmldesigner/collectionEditorQmlSource").toString();
-}
-
-QString getPreferredCollectionName(const QUrl &url, const QString &collectionName)
-{
- if (collectionName.isEmpty()) {
- QFileInfo fileInfo(url.isLocalFile() ? url.toLocalFile() : url.toString());
- return fileInfo.completeBaseName();
- }
-
- return collectionName;
-}
-
-} // namespace
-
-namespace QmlDesigner {
-CollectionWidget::CollectionWidget(CollectionView *view)
- : m_view(view)
- , m_listModel(new CollectionListModel)
- , m_collectionDetailsModel(new CollectionDetailsModel)
- , m_collectionDetailsSortFilterModel(std::make_unique<CollectionDetailsSortFilterModel>())
- , m_quickWidget(new StudioQuickWidget(this))
-{
- setWindowTitle(tr("Model Editor", "Title of model editor widget"));
-
- Core::IContext *icontext = nullptr;
- Core::Context context(Constants::C_QMLCOLLECTIONEDITOR);
- icontext = new Core::IContext(this);
- icontext->setContext(context);
- icontext->setWidget(this);
-
- connect(m_listModel, &CollectionListModel::warning, this, &CollectionWidget::warn);
-
- m_collectionDetailsSortFilterModel->setSourceModel(m_collectionDetailsModel);
-
- m_quickWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_COLLECTION_EDITOR);
- m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
- m_quickWidget->engine()->addImportPath(collectionViewResourcesPath() + "/imports");
- m_quickWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground));
-
- Theme::setupTheme(m_quickWidget->engine());
- m_quickWidget->quickWidget()->installEventFilter(this);
-
- auto layout = new QVBoxLayout(this);
- layout->setContentsMargins({});
- layout->setSpacing(0);
- layout->addWidget(m_quickWidget.data());
-
- qmlRegisterAnonymousType<CollectionWidget>("CollectionEditorBackend", 1);
- auto map = m_quickWidget->registerPropertyMap("CollectionEditorBackend");
- map->setProperties({
- {"rootView", QVariant::fromValue(this)},
- {"model", QVariant::fromValue(m_listModel.data())},
- {"collectionDetailsModel", QVariant::fromValue(m_collectionDetailsModel.data())},
- {"collectionDetailsSortFilterModel",
- QVariant::fromValue(m_collectionDetailsSortFilterModel.get())},
- });
-
- auto hotReloadShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F4), this);
- connect(hotReloadShortcut, &QShortcut::activated, this, &CollectionWidget::reloadQmlSource);
-
- reloadQmlSource();
-
- QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_MODELEDITOR_TIME);
-}
-
-CollectionWidget::~CollectionWidget() = default;
-
-void CollectionWidget::contextHelp(const Core::IContext::HelpCallback &callback) const
-{
- if (m_view)
- QmlDesignerPlugin::contextHelp(callback, m_view->contextHelpId());
- else
- callback({});
-}
-
-QPointer<CollectionListModel> CollectionWidget::listModel() const
-{
- return m_listModel;
-}
-
-QPointer<CollectionDetailsModel> CollectionWidget::collectionDetailsModel() const
-{
- return m_collectionDetailsModel;
-}
-
-void CollectionWidget::reloadQmlSource()
-{
- const QString collectionViewQmlPath = collectionViewResourcesPath() + "/CollectionView.qml";
-
- QTC_ASSERT(QFileInfo::exists(collectionViewQmlPath), return);
-
- m_quickWidget->setSource(QUrl::fromLocalFile(collectionViewQmlPath));
-
- if (!m_quickWidget->rootObject()) {
- QString errorString;
- const auto errors = m_quickWidget->errors();
- for (const QQmlError &error : errors)
- errorString.append("\n" + error.toString());
-
- Core::AsynchronousMessageBox::warning(tr("Cannot Create QtQuick View"),
- tr("StatesEditorWidget: %1 cannot be created.%2")
- .arg(collectionViewQmlPath, errorString));
- return;
- }
-}
-
-QSize CollectionWidget::minimumSizeHint() const
-{
- return {300, 300};
-}
-
-bool CollectionWidget::loadJsonFile(const QUrl &url, const QString &collectionName)
-{
- if (!isJsonFile(url))
- return false;
-
- m_view->addResource(url, getPreferredCollectionName(url, collectionName));
-
- return true;
-}
-
-bool CollectionWidget::loadCsvFile(const QUrl &url, const QString &collectionName)
-{
- m_view->addResource(url, getPreferredCollectionName(url, collectionName));
-
- return true;
-}
-
-bool CollectionWidget::isJsonFile(const QUrl &url) const
-{
- Utils::FilePath filePath = Utils::FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile()
- : url.toString());
- Utils::FileReader file;
- if (!file.fetch(filePath))
- return false;
-
- QJsonParseError error;
- QJsonDocument::fromJson(file.data(), &error);
- if (error.error)
- return false;
-
- return true;
-}
-
-bool CollectionWidget::isCsvFile(const QUrl &url) const
-{
- QString filePath = url.isLocalFile() ? url.toLocalFile() : url.toString();
- QFileInfo fileInfo(filePath);
- return fileInfo.exists() && !fileInfo.suffix().compare("csv", Qt::CaseInsensitive);
-}
-
-bool CollectionWidget::isValidUrlToImport(const QUrl &url) const
-{
- using Utils::FilePath;
- FilePath fileInfo = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile()
- : url.toString());
- if (fileInfo.suffix() == "json")
- return isJsonFile(url);
-
- if (fileInfo.suffix() == "csv")
- return isCsvFile(url);
-
- return false;
-}
-
-bool CollectionWidget::importFile(const QString &collectionName,
- const QUrl &url,
- const bool &firstRowIsHeader)
-{
- using Utils::FilePath;
-
- FilePath fileInfo = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile()
- : url.toString());
- CollectionDetails loadedCollection;
- QByteArray fileContent;
-
- auto loadUrlContent = [&]() -> bool {
- Utils::FileReader file;
- if (file.fetch(fileInfo)) {
- fileContent = file.data();
- return true;
- }
-
- warn(tr("Import from file"), tr("Cannot import from file \"%1\"").arg(fileInfo.fileName()));
- return false;
- };
-
- if (fileInfo.suffix() == "json") {
- if (!loadUrlContent())
- return false;
-
- QJsonParseError parseError;
- loadedCollection = CollectionDetails::fromImportedJson(fileContent, &parseError);
- if (parseError.error != QJsonParseError::NoError) {
- warn(tr("Json file Import error"),
- tr("Cannot parse json content\n%1").arg(parseError.errorString()));
- }
- } else if (fileInfo.suffix() == "csv") {
- if (!loadUrlContent())
- return false;
- loadedCollection = CollectionDetails::fromImportedCsv(fileContent, firstRowIsHeader);
- }
-
- if (loadedCollection.columns()) {
- m_view->addNewCollection(collectionName, loadedCollection.toLocalJson());
- return true;
- } else {
- warn(tr("Can not add a model to the JSON file"),
- tr("The imported model is empty or is not supported."));
- }
- return false;
-}
-
-void CollectionWidget::addProjectImport()
-{
- m_view->addProjectImport();
-}
-
-void CollectionWidget::addCollectionToDataStore(const QString &collectionName)
-{
- m_view->addNewCollection(collectionName, CollectionEditorUtils::defaultCollection());
-}
-
-void CollectionWidget::assignCollectionToSelectedNode(const QString collectionName)
-{
- m_view->assignCollectionToSelectedNode(collectionName);
-}
-
-void CollectionWidget::openCollection(const QString &collectionName)
-{
- m_listModel->selectCollectionName(collectionName);
- QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("CollectionEditor", true);
-}
-
-ModelNode CollectionWidget::dataStoreNode() const
-{
- return m_view->dataStoreNode();
-}
-
-void CollectionWidget::warn(const QString &title, const QString &body)
-{
- QMetaObject::invokeMethod(m_quickWidget->rootObject(),
- "showWarning",
- Q_ARG(QVariant, title),
- Q_ARG(QVariant, body));
-}
-
-void CollectionWidget::setTargetNodeSelected(bool selected)
-{
- if (m_targetNodeSelected == selected)
- return;
-
- m_targetNodeSelected = selected;
- emit targetNodeSelectedChanged(m_targetNodeSelected);
-}
-
-void CollectionWidget::setProjectImportExists(bool exists)
-{
- if (m_projectImportExists == exists)
- return;
-
- m_projectImportExists = exists;
- emit projectImportExistsChanged(m_projectImportExists);
-}
-
-void CollectionWidget::setDataStoreExists(bool exists)
-{
- if (m_dataStoreExists == exists)
- return;
-
- m_dataStoreExists = exists;
- emit dataStoreExistsChanged(m_dataStoreExists);
-}
-
-void CollectionWidget::deleteSelectedCollection()
-{
- QMetaObject::invokeMethod(m_quickWidget->quickWidget()->rootObject(), "deleteSelectedCollection");
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h
deleted file mode 100644
index 13c3566c78..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// 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 <QFrame>
-
-#include <coreplugin/icontext.h>
-
-class StudioQuickWidget;
-
-namespace QmlDesigner {
-
-class CollectionDetailsModel;
-class CollectionDetailsSortFilterModel;
-class CollectionListModel;
-class CollectionView;
-class ModelNode;
-
-class CollectionWidget : public QFrame
-{
- Q_OBJECT
-
- Q_PROPERTY(bool targetNodeSelected MEMBER m_targetNodeSelected NOTIFY targetNodeSelectedChanged)
- Q_PROPERTY(bool projectImportExists MEMBER m_projectImportExists NOTIFY projectImportExistsChanged)
- Q_PROPERTY(bool dataStoreExists MEMBER m_dataStoreExists NOTIFY dataStoreExistsChanged)
-
-public:
- CollectionWidget(CollectionView *view);
- ~CollectionWidget();
- void contextHelp(const Core::IContext::HelpCallback &callback) const;
-
- QPointer<CollectionListModel> listModel() const;
- QPointer<CollectionDetailsModel> collectionDetailsModel() const;
-
- void reloadQmlSource();
-
- QSize minimumSizeHint() const override;
-
- Q_INVOKABLE bool loadJsonFile(const QUrl &url, const QString &collectionName = {});
- Q_INVOKABLE bool loadCsvFile(const QUrl &url, const QString &collectionName = {});
- Q_INVOKABLE bool isJsonFile(const QUrl &url) const;
- Q_INVOKABLE bool isCsvFile(const QUrl &url) const;
- Q_INVOKABLE bool isValidUrlToImport(const QUrl &url) const;
-
- Q_INVOKABLE bool importFile(const QString &collectionName,
- const QUrl &url,
- const bool &firstRowIsHeader = true);
-
- Q_INVOKABLE void addProjectImport();
- Q_INVOKABLE void addCollectionToDataStore(const QString &collectionName);
- Q_INVOKABLE void assignCollectionToSelectedNode(const QString collectionName);
- Q_INVOKABLE void openCollection(const QString &collectionName);
- Q_INVOKABLE ModelNode dataStoreNode() const;
-
- void warn(const QString &title, const QString &body);
- void setTargetNodeSelected(bool selected);
- void setProjectImportExists(bool exists);
- void setDataStoreExists(bool exists);
-
- void deleteSelectedCollection();
-
-signals:
- void targetNodeSelectedChanged(bool);
- void projectImportExistsChanged(bool);
- void dataStoreExistsChanged(bool);
-
-private:
- QString generateUniqueCollectionName(const ModelNode &node, const QString &name);
-
- QPointer<CollectionView> m_view;
- QPointer<CollectionListModel> m_listModel;
- QPointer<CollectionDetailsModel> m_collectionDetailsModel;
- std::unique_ptr<CollectionDetailsSortFilterModel> m_collectionDetailsSortFilterModel;
- QScopedPointer<StudioQuickWidget> m_quickWidget;
- bool m_targetNodeSelected = false;
- bool m_projectImportExists = false;
- bool m_dataStoreExists = false;
-};
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp
deleted file mode 100644
index 5be9c20f9e..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp
+++ /dev/null
@@ -1,511 +0,0 @@
-// 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 "datastoremodelnode.h"
-
-#include "abstractview.h"
-#include "collectioneditorconstants.h"
-#include "collectioneditorutils.h"
-#include "model/qmltextgenerator.h"
-#include "plaintexteditmodifier.h"
-#include "qmldesignerbase/qmldesignerbaseplugin.h"
-#include "qmldesignerexternaldependencies.h"
-#include "rewriterview.h"
-
-#include <model.h>
-#include <nodemetainfo.h>
-#include <nodeproperty.h>
-#include <variantproperty.h>
-
-#include <qmljstools/qmljscodestylepreferences.h>
-#include <qmljstools/qmljstoolssettings.h>
-
-#include <coreplugin/documentmanager.h>
-#include <projectexplorer/project.h>
-#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/projectmanager.h>
-
-#include <utils/fileutils.h>
-#include <utils/qtcassert.h>
-
-#include <QPlainTextEdit>
-#include <QRegularExpression>
-#include <QRegularExpressionMatch>
-#include <QScopedPointer>
-
-namespace {
-
-inline constexpr char CHILDLISTMODEL_TYPENAME[] = "ChildListModel";
-
-QmlDesigner::PropertyNameList createNameList(const QmlDesigner::ModelNode &node)
-{
- using QmlDesigner::AbstractProperty;
- using QmlDesigner::PropertyName;
- using QmlDesigner::PropertyNameList;
- static PropertyNameList defaultsNodeProps = {
- "id",
- QmlDesigner::CollectionEditorConstants::SOURCEFILE_PROPERTY,
- QmlDesigner::CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY,
- "backend"};
- PropertyNameList dynamicPropertyNames = Utils::transform(
- node.dynamicProperties(),
- [](const AbstractProperty &property) -> PropertyName { return property.name(); });
-
- Utils::sort(dynamicPropertyNames);
-
- return defaultsNodeProps + dynamicPropertyNames;
-}
-
-bool isValidCollectionPropertyName(const QString &collectionId)
-{
- static const QmlDesigner::PropertyNameList reservedKeywords = {
- QmlDesigner::CollectionEditorConstants::SOURCEFILE_PROPERTY,
- QmlDesigner::CollectionEditorConstants::JSONBACKEND_TYPENAME,
- "backend",
- "models",
- };
-
- return QmlDesigner::ModelNode::isValidId(collectionId)
- && !reservedKeywords.contains(collectionId.toLatin1());
-}
-
-QMap<QString, QmlDesigner::PropertyName> getModelIdMap(const QmlDesigner::ModelNode &rootNode)
-{
- using namespace QmlDesigner;
- QMap<QString, PropertyName> modelNameForId;
-
- const QList<AbstractProperty> propertyNames = rootNode.dynamicProperties();
-
- for (const AbstractProperty &property : std::as_const(propertyNames)) {
- if (!property.isNodeProperty())
- continue;
-
- NodeProperty nodeProperty = property.toNodeProperty();
- if (!nodeProperty.hasDynamicTypeName(CHILDLISTMODEL_TYPENAME))
- continue;
-
- ModelNode childNode = nodeProperty.modelNode();
- if (childNode.hasProperty(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)) {
- QString modelName = childNode
- .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)
- .toVariantProperty()
- .value()
- .toString();
-
- if (!modelName.isEmpty())
- modelNameForId.insert(modelName, property.name());
- }
- }
- return modelNameForId;
-}
-
-void setQmlContextToModel(QmlDesigner::Model *model, const QString &qmlContext)
-{
- using namespace QmlDesigner;
- Q_ASSERT(model);
-
- QScopedPointer<QPlainTextEdit> textEdit(new QPlainTextEdit);
- QScopedPointer<NotIndentingTextEditModifier> modifier(
- new NotIndentingTextEditModifier(textEdit.data()));
- textEdit->hide();
- textEdit->setPlainText(qmlContext);
- QmlDesigner::ExternalDependencies externalDependencies{QmlDesignerBasePlugin::settings()};
- QScopedPointer<RewriterView> rewriter(
- new RewriterView(externalDependencies, QmlDesigner::RewriterView::Validate));
-
- rewriter->setParent(model);
- rewriter->setTextModifier(modifier.get());
- rewriter->setCheckSemanticErrors(false);
-
- model->attachView(rewriter.get());
- model->detachView(rewriter.get());
-}
-
-} // namespace
-
-namespace QmlDesigner {
-
-DataStoreModelNode::DataStoreModelNode()
-{
- reloadModel();
-}
-
-void DataStoreModelNode::reloadModel()
-{
- using Utils::FilePath;
- if (!ProjectExplorer::ProjectManager::startupProject()) {
- reset();
- return;
- }
- bool forceUpdate = false;
-
- const FilePath dataStoreQmlPath = CollectionEditorUtils::dataStoreQmlFilePath();
- const FilePath dataStoreJsonPath = CollectionEditorUtils::dataStoreJsonFilePath();
- QUrl dataStoreQmlUrl = dataStoreQmlPath.toUrl();
-
- if (dataStoreQmlPath.exists() && dataStoreJsonPath.exists()) {
- if (!m_model.get() || m_model->fileUrl() != dataStoreQmlUrl) {
-#ifdef QDS_USE_PROJECTSTORAGE
- m_model = model()->createModel("JsonListModel");
- forceUpdate = true;
- Import import = Import::createLibraryImport("QtQuick.Studio.Utils");
- m_model->changeImports({import}, {});
-#else
- m_model = Model::create(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME, 1, 1);
- forceUpdate = true;
- Import import = Import::createLibraryImport(
- CollectionEditorConstants::COLLECTIONMODEL_IMPORT);
- try {
- if (!m_model->hasImport(import, true, true))
- m_model->changeImports({import}, {});
- } catch (const Exception &) {
- QTC_ASSERT(false, return);
- }
-#endif
- }
- } else {
- reset();
- }
-
- if (!m_model.get())
- return;
-
- if (forceUpdate) {
- m_model->setFileUrl(dataStoreQmlUrl);
- m_dataRelativePath = dataStoreJsonPath.relativePathFrom(dataStoreQmlPath).toFSPathString();
- preloadFile();
- update();
- }
-}
-
-QStringList DataStoreModelNode::collectionNames() const
-{
- return m_collectionPropertyNames.keys();
-}
-
-Model *DataStoreModelNode::model() const
-{
- return m_model.get();
-}
-
-ModelNode DataStoreModelNode::modelNode() const
-{
- if (!m_model.get())
- return {};
- return m_model->rootModelNode();
-}
-
-QString DataStoreModelNode::getModelQmlText()
-{
- ModelNode node = modelNode();
- QTC_ASSERT(node, return {});
-
- Internal::QmlTextGenerator textGen(createNameList(node),
- QmlJSTools::QmlJSToolsSettings::globalCodeStyle()->tabSettings());
-
- QString genText = textGen(node);
- return genText;
-}
-
-void DataStoreModelNode::reset()
-{
- if (m_model)
- m_model.reset();
-
- m_dataRelativePath.clear();
- setCollectionNames({});
-}
-
-void DataStoreModelNode::preloadFile()
-{
- using Utils::FilePath;
- using Utils::FileReader;
-
- if (!m_model)
- return;
-
- const FilePath dataStoreQmlPath = dataStoreQmlFilePath();
- FileReader dataStoreQmlFile;
- QString sourceQmlContext;
-
- if (dataStoreQmlFile.fetch(dataStoreQmlPath))
- sourceQmlContext = QString::fromLatin1(dataStoreQmlFile.data());
-
- setQmlContextToModel(m_model.get(), sourceQmlContext);
- m_collectionPropertyNames = getModelIdMap(m_model->rootModelNode());
-}
-
-void DataStoreModelNode::updateDataStoreProperties()
-{
- QTC_ASSERT(model(), return);
-
- ModelNode rootNode = modelNode();
- QTC_ASSERT(rootNode.isValid(), return);
-
- QSet<QString> collectionNamesToBeAdded;
- const QStringList allCollectionNames = m_collectionPropertyNames.keys();
- for (const QString &collectionName : allCollectionNames)
- collectionNamesToBeAdded << collectionName;
-
- const QList<AbstractProperty> formerPropertyNames = rootNode.dynamicProperties();
-
- // Remove invalid collection names from the properties
- for (const AbstractProperty &property : formerPropertyNames) {
- if (!property.isNodeProperty())
- continue;
-
- NodeProperty nodeProprty = property.toNodeProperty();
- if (!nodeProprty.hasDynamicTypeName(CHILDLISTMODEL_TYPENAME))
- continue;
-
- ModelNode childNode = nodeProprty.modelNode();
- if (childNode.hasProperty(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)) {
- QString modelName = childNode
- .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)
- .toVariantProperty()
- .value()
- .toString();
- if (collectionNamesToBeAdded.contains(modelName)) {
- m_collectionPropertyNames.insert(modelName, property.name());
- collectionNamesToBeAdded.remove(modelName);
- } else {
- rootNode.removeProperty(property.name());
- }
- } else {
- rootNode.removeProperty(property.name());
- }
- }
-
- rootNode.setIdWithoutRefactoring("models");
-
- QStringList collectionNamesLeft = collectionNamesToBeAdded.values();
- Utils::sort(collectionNamesLeft);
- for (const QString &collectionName : std::as_const(collectionNamesLeft))
- addCollectionNameToTheModel(collectionName, getUniquePropertyName(collectionName));
-
- // Backend Property
- ModelNode backendNode = model()->createModelNode(CollectionEditorConstants::JSONBACKEND_TYPENAME);
- NodeProperty backendProperty = rootNode.nodeProperty("backend");
- backendProperty.setDynamicTypeNameAndsetModelNode(CollectionEditorConstants::JSONBACKEND_TYPENAME,
- backendNode);
- // Source Property
- VariantProperty sourceProp = rootNode.variantProperty(
- CollectionEditorConstants::SOURCEFILE_PROPERTY);
- sourceProp.setValue(m_dataRelativePath);
-}
-
-void DataStoreModelNode::updateSingletonFile()
-{
- using Utils::FilePath;
- using Utils::FileSaver;
- QTC_ASSERT(m_model.get(), return);
-
- const QString pragmaSingleTone = "pragma Singleton\n";
- QString imports;
-
- for (const Import &import : m_model->imports())
- imports += QStringLiteral("import %1\n").arg(import.toString(true));
-
- QString content = pragmaSingleTone + imports + getModelQmlText();
- Core::DocumentManager::expectFileChange(dataStoreQmlFilePath());
- FileSaver file(dataStoreQmlFilePath());
- file.write(content.toLatin1());
- file.finalize();
-}
-
-void DataStoreModelNode::update()
-{
- if (!m_model.get())
- return;
-
- updateDataStoreProperties();
- updateSingletonFile();
-}
-
-void DataStoreModelNode::addCollectionNameToTheModel(const QString &collectionName,
- const PropertyName &dataStorePropertyName)
-{
- ModelNode rootNode = modelNode();
- QTC_ASSERT(rootNode.isValid(), return);
-
- if (dataStorePropertyName.isEmpty()) {
- qWarning() << __FUNCTION__ << __LINE__
- << QString("The property name cannot be generated from \"%1\"").arg(collectionName);
- return;
- }
-
- ModelNode collectionNode = model()->createModelNode(CHILDLISTMODEL_TYPENAME);
- VariantProperty modelNameProperty = collectionNode.variantProperty(
- CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY);
- modelNameProperty.setValue(collectionName);
-
- NodeProperty nodeProp = rootNode.nodeProperty(dataStorePropertyName);
- nodeProp.setDynamicTypeNameAndsetModelNode(CHILDLISTMODEL_TYPENAME, collectionNode);
-
- m_collectionPropertyNames.insert(collectionName, dataStorePropertyName);
-}
-
-Utils::FilePath DataStoreModelNode::dataStoreQmlFilePath() const
-{
- QUrl modelUrl = m_model->fileUrl();
- return Utils::FilePath::fromUserInput(modelUrl.isLocalFile() ? modelUrl.toLocalFile()
- : modelUrl.toString());
-}
-
-PropertyName DataStoreModelNode::getUniquePropertyName(const QString &collectionName)
-{
- ModelNode dataStoreNode = modelNode();
- QTC_ASSERT(!collectionName.isEmpty() && dataStoreNode.isValid(), return {});
-
- QString newProperty;
-
- // convert to camel case
- QStringList nameWords = collectionName.split(' ');
- nameWords[0] = nameWords[0].at(0).toLower() + nameWords[0].mid(1);
- for (int i = 1; i < nameWords.size(); ++i)
- nameWords[i] = nameWords[i].at(0).toUpper() + nameWords[i].mid(1);
- newProperty = nameWords.join("");
-
- // if id starts with a number prepend an underscore
- if (newProperty.at(0).isDigit())
- newProperty.prepend('_');
-
- // If the new id is not valid (e.g. qml keyword match), prepend an underscore
- if (!isValidCollectionPropertyName(newProperty))
- newProperty.prepend('_');
-
- static const QRegularExpression rgx("\\d+$"); // matches a number at the end of a string
- while (dataStoreNode.hasProperty(newProperty.toLatin1())) { // id exists
- QRegularExpressionMatch match = rgx.match(newProperty);
- if (match.hasMatch()) { // ends with a number, increment it
- QString numStr = match.captured();
- int num = numStr.toInt() + 1;
- newProperty = newProperty.mid(0, match.capturedStart()) + QString::number(num);
- } else {
- newProperty.append('1');
- }
- }
-
- return newProperty.toLatin1();
-}
-
-void DataStoreModelNode::setCollectionNames(const QStringList &newCollectionNames)
-{
- m_collectionPropertyNames.clear();
- for (const QString &collectionName : newCollectionNames)
- m_collectionPropertyNames.insert(collectionName, {});
- update();
-}
-
-void DataStoreModelNode::addCollection(const QString &collectionName)
-{
- if (!m_collectionPropertyNames.contains(collectionName)) {
- m_collectionPropertyNames.insert(collectionName, {});
- update();
- }
-}
-
-void DataStoreModelNode::renameCollection(const QString &oldName, const QString &newName)
-{
- ModelNode dataStoreNode = modelNode();
- QTC_ASSERT(dataStoreNode.isValid(), return);
-
- if (m_collectionPropertyNames.contains(oldName)) {
- const PropertyName oldPropertyName = m_collectionPropertyNames.value(oldName);
- if (!oldPropertyName.isEmpty() && dataStoreNode.hasProperty(oldPropertyName)) {
- NodeProperty collectionNode = dataStoreNode.property(oldPropertyName).toNodeProperty();
- if (collectionNode.isValid()) {
- VariantProperty modelNameProperty = collectionNode.modelNode().variantProperty(
- CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY);
- modelNameProperty.setValue(newName);
- m_collectionPropertyNames.remove(oldName);
- m_collectionPropertyNames.insert(newName, collectionNode.name());
- update();
- return;
- }
- qWarning() << __FUNCTION__ << __LINE__
- << "There is no valid node for the old collection name";
- return;
- }
- qWarning() << __FUNCTION__ << __LINE__ << QString("Invalid old property name")
- << oldPropertyName;
- return;
- }
- qWarning() << __FUNCTION__ << __LINE__
- << QString("There is no old collection name registered with this name \"%1\"").arg(oldName);
-}
-
-void DataStoreModelNode::removeCollections(const QStringList &collectionNames)
-{
- bool updateRequired = false;
- for (const QString &collectionName : collectionNames) {
- if (m_collectionPropertyNames.contains(collectionName)) {
- m_collectionPropertyNames.remove(collectionName);
- updateRequired = true;
- }
- }
-
- if (updateRequired)
- update();
-}
-
-void DataStoreModelNode::assignCollectionToNode(AbstractView *view,
- const ModelNode &targetNode,
- const QString &collectionName,
- CollectionColumnFinder collectionHasColumn,
- FirstColumnProvider firstColumnProvider)
-{
- QTC_ASSERT(targetNode.isValid(), return);
-
- if (!CollectionEditorUtils::canAcceptCollectionAsModel(targetNode))
- return;
-
- if (!m_collectionPropertyNames.contains(collectionName)) {
- qWarning() << __FUNCTION__ << __LINE__ << "Collection doesn't exist in the DataStore"
- << collectionName;
- return;
- }
-
- PropertyName propertyName = m_collectionPropertyNames.value(collectionName);
-
- const ModelNode dataStore = modelNode();
- VariantProperty sourceProperty = dataStore.variantProperty(propertyName);
- if (!sourceProperty.exists()) {
- qWarning() << __FUNCTION__ << __LINE__
- << "The source property doesn't exist in the DataStore.";
- return;
- }
-
- view->executeInTransaction("assignCollectionToNode", [&]() {
- QString identifier = QString("DataStore.%1").arg(QString::fromLatin1(sourceProperty.name()));
-
- // Remove the old model node property if exists
- NodeProperty modelNodeProperty = targetNode.nodeProperty("model");
- if (modelNodeProperty.modelNode())
- modelNodeProperty.modelNode().destroy();
-
- // Assign the collection to the node
- BindingProperty modelProperty = targetNode.bindingProperty("model");
- modelProperty.setExpression(identifier);
-
- if (CollectionEditorUtils::hasTextRoleProperty(targetNode)) {
- VariantProperty textRoleProperty = targetNode.variantProperty("textRole");
- const QVariant currentTextRoleValue = textRoleProperty.value();
-
- if (currentTextRoleValue.isValid() && !currentTextRoleValue.isNull()) {
- if (currentTextRoleValue.type() == QVariant::String) {
- const QString currentTextRole = currentTextRoleValue.toString();
- if (collectionHasColumn(collectionName, currentTextRole))
- return;
- } else {
- return;
- }
- }
-
- QString textRoleValue = firstColumnProvider(collectionName);
- textRoleProperty.setValue(textRoleValue);
- }
- });
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h
deleted file mode 100644
index 6cd969edbe..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// 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 <modelnode.h>
-
-#include <QMap>
-
-namespace Utils {
-class FilePath;
-}
-
-namespace QmlDesigner {
-
-class Model;
-
-class DataStoreModelNode
-{
-public:
- using CollectionColumnFinder = std::function<bool(const QString &collectionName,
- const QString &columnName)>;
- using FirstColumnProvider = std::function<QString(const QString &collectionName)>;
-
- DataStoreModelNode();
-
- void reloadModel();
- QStringList collectionNames() const;
-
- Model *model() const;
- ModelNode modelNode() const;
-
- void setCollectionNames(const QStringList &newCollectionNames);
- void addCollection(const QString &collectionName);
- void renameCollection(const QString &oldName, const QString &newName);
- void removeCollections(const QStringList &collectionNames);
-
- void assignCollectionToNode(AbstractView *view,
- const ModelNode &targetNode,
- const QString &collectionName,
- CollectionColumnFinder collectionHasColumn,
- FirstColumnProvider firstColumnProvider);
-
-private:
- QString getModelQmlText();
-
- void reset();
- void preloadFile();
- void updateDataStoreProperties();
- void updateSingletonFile();
- void update();
- void addCollectionNameToTheModel(const QString &collectionName,
- const PropertyName &dataStorePropertyName);
- Utils::FilePath dataStoreQmlFilePath() const;
-
- PropertyName getUniquePropertyName(const QString &collectionName);
-
- ModelPointer m_model;
- QMap<QString, PropertyName> m_collectionPropertyNames;
- QString m_dataRelativePath;
-};
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp b/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp
index 559e8ea69c..3379d99834 100644
--- a/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp
@@ -8,7 +8,7 @@
namespace QmlDesigner {
AbstractAction::AbstractAction(const QString &description)
- : m_pureAction(new DefaultAction(description))
+ : m_pureAction(std::make_unique<DefaultAction>(description))
{
const Utils::Icon defaultIcon({
{":/utils/images/select.png", Utils::Theme::QmlDesigner_FormEditorForegroundColor}}, Utils::Icon::MenuTintedStyle);
@@ -56,7 +56,7 @@ void AbstractAction::setCheckable(bool checkable)
PureActionInterface *AbstractAction::pureAction() const
{
- return m_pureAction.data();
+ return m_pureAction.get();
}
SelectionContext AbstractAction::selectionContext() const
diff --git a/src/plugins/qmldesigner/components/componentcore/abstractaction.h b/src/plugins/qmldesigner/components/componentcore/abstractaction.h
index ca4cc582ce..53b540cc7a 100644
--- a/src/plugins/qmldesigner/components/componentcore/abstractaction.h
+++ b/src/plugins/qmldesigner/components/componentcore/abstractaction.h
@@ -6,7 +6,8 @@
#include "actioninterface.h"
#include <QAction>
-#include <QScopedPointer>
+
+#include <memory>
namespace QmlDesigner {
@@ -58,7 +59,7 @@ protected:
SelectionContext selectionContext() const;
private:
- QScopedPointer<PureActionInterface> m_pureAction;
+ std::unique_ptr<PureActionInterface> m_pureAction;
SelectionContext m_selectionContext;
};
diff --git a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp
index 288b8e409d..5b340343e7 100644
--- a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp
@@ -8,14 +8,14 @@
namespace QmlDesigner {
-AbstractActionGroup::AbstractActionGroup(const QString &displayName) :
- m_displayName(displayName),
- m_menu(new QmlEditorMenu)
+AbstractActionGroup::AbstractActionGroup(const QString &displayName)
+ : m_displayName(displayName)
+ , m_menu(Utils::makeUniqueObjectPtr<QmlEditorMenu>())
{
m_menu->setTitle(displayName);
m_action = m_menu->menuAction();
- QmlEditorMenu *qmlEditorMenu = qobject_cast<QmlEditorMenu *>(m_menu.data());
+ QmlEditorMenu *qmlEditorMenu = qobject_cast<QmlEditorMenu *>(m_menu.get());
if (qmlEditorMenu)
qmlEditorMenu->setIconsVisible(false);
}
@@ -32,7 +32,7 @@ QAction *AbstractActionGroup::action() const
QMenu *AbstractActionGroup::menu() const
{
- return m_menu.data();
+ return m_menu.get();
}
SelectionContext AbstractActionGroup::selectionContext() const
diff --git a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h
index dd89849ecf..f239eeab3d 100644
--- a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h
+++ b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h
@@ -5,9 +5,10 @@
#include "actioninterface.h"
+#include <utils/uniqueobjectptr.h>
+
#include <QAction>
#include <QMenu>
-#include <QScopedPointer>
namespace QmlDesigner {
@@ -29,7 +30,7 @@ public:
private:
const QString m_displayName;
SelectionContext m_selectionContext;
- QScopedPointer<QMenu> m_menu;
+ Utils::UniqueObjectPtr<QMenu> m_menu;
QAction *m_action;
};
diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
index d992a6a5bf..da7c5bf72e 100644
--- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
+++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
@@ -69,7 +69,6 @@ const char mergeTemplateCommandId[] = "MergeTemplate";
const char goToImplementationCommandId[] = "GoToImplementation";
const char makeComponentCommandId[] = "MakeComponent";
const char editMaterialCommandId[] = "EditMaterial";
-const char editCollectionCommandId[] = "EditCollection";
const char addItemToStackedContainerCommandId[] = "AddItemToStackedContainer";
const char addTabBarToStackedContainerCommandId[] = "AddTabBarToStackedContainer";
const char increaseIndexOfStackedContainerCommandId[] = "IncreaseIndexOfStackedContainer";
@@ -128,7 +127,6 @@ const char mergeTemplateDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMen
const char goToImplementationDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Go to Implementation");
const char makeComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Create Component");
const char editMaterialDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Material");
-const char editCollectionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Model");
const char editAnnotationsDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Annotations");
const char addMouseAreaFillDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Mouse Area");
const char editIn3dViewDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit in 3D View");
@@ -214,7 +212,6 @@ enum PrioritiesEnum : int {
ArrangeCategory,
EditCategory,
EditListModel,
- EditCollection,
/******** Section *****************************/
PositionSection = 2000,
SnappingCategory,
diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
index 8d2b2c43c2..bbe64935f6 100644
--- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
@@ -113,7 +113,6 @@ void DesignerActionManager::polishActions() const
Core::Context qmlDesignerNavigatorContext(Constants::C_QMLNAVIGATOR);
Core::Context qmlDesignerMaterialBrowserContext(Constants::C_QMLMATERIALBROWSER);
Core::Context qmlDesignerAssetsLibraryContext(Constants::C_QMLASSETSLIBRARY);
- Core::Context qmlDesignerCollectionEditorContext(Constants::C_QMLCOLLECTIONEDITOR);
Core::Context qmlDesignerUIContext;
qmlDesignerUIContext.add(qmlDesignerFormEditorContext);
@@ -121,7 +120,6 @@ void DesignerActionManager::polishActions() const
qmlDesignerUIContext.add(qmlDesignerNavigatorContext);
qmlDesignerUIContext.add(qmlDesignerMaterialBrowserContext);
qmlDesignerUIContext.add(qmlDesignerAssetsLibraryContext);
- qmlDesignerUIContext.add(qmlDesignerCollectionEditorContext);
for (auto *action : actions) {
if (!action->menuId().isEmpty()) {
@@ -1988,8 +1986,8 @@ void DesignerActionManager::createDefaultDesignerActions()
QKeySequence(),
44,
&editMaterial,
- &modelHasMaterial,
- &isModel));
+ &hasEditableMaterial,
+ &isModelOrMaterial));
addDesignerAction(new ModelNodeContextMenuAction(
mergeTemplateCommandId,
@@ -2011,16 +2009,6 @@ void DesignerActionManager::createDefaultDesignerActions()
addDesignerAction(new EditListModelAction);
- addDesignerAction(new ModelNodeContextMenuAction(editCollectionCommandId,
- editCollectionDisplayName,
- contextIcon(DesignerIcons::EditIcon),
- rootCategory,
- QKeySequence("Alt+e"),
- ComponentCoreConstants::Priorities::EditCollection,
- &editCollection,
- &hasCollectionAsModel,
- &hasCollectionAsModel));
-
addDesignerAction(new ModelNodeContextMenuAction(openSignalDialogCommandId,
openSignalDialogDisplayName,
{},
@@ -2193,7 +2181,8 @@ void DesignerActionManager::addCustomTransitionEffectAction()
void DesignerActionManager::setupIcons()
{
- m_designerIcons.reset(new DesignerIcons("qtds_propertyIconFont.ttf", designerIconResourcesPath()));
+ m_designerIcons = std::make_unique<DesignerIcons>("qtds_propertyIconFont.ttf",
+ designerIconResourcesPath());
}
QString DesignerActionManager::designerIconResourcesPath() const
diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h
index 16d6219cd6..89505fcbe8 100644
--- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h
+++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h
@@ -138,7 +138,7 @@ private:
QList<AddResourceHandler> m_addResourceHandler;
QList<ModelNodePreviewImageHandler> m_modelNodePreviewImageHandlers;
ExternalDependenciesInterface &m_externalDependencies;
- QScopedPointer<DesignerIcons> m_designerIcons;
+ std::unique_ptr<DesignerIcons> m_designerIcons;
QList<ActionAddedInterface> m_callBacks;
};
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h
index aec14e9d04..6734bac568 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h
@@ -64,33 +64,24 @@ inline bool addMouseAreaFillCheck(const SelectionContext &selectionContext)
return false;
}
-inline bool isModel(const SelectionContext &selectionState)
+inline bool isModelOrMaterial(const SelectionContext &selectionState)
{
ModelNode node = selectionState.currentSingleSelectedNode();
- return node.metaInfo().isQtQuick3DModel();
+ return node.metaInfo().isQtQuick3DModel() || node.metaInfo().isQtQuick3DMaterial();
}
-inline bool modelHasMaterial(const SelectionContext &selectionState)
+inline bool hasEditableMaterial(const SelectionContext &selectionState)
{
ModelNode node = selectionState.currentSingleSelectedNode();
+ if (node.metaInfo().isQtQuick3DMaterial())
+ return true;
+
BindingProperty prop = node.bindingProperty("materials");
return prop.exists() && (!prop.expression().isEmpty() || !prop.resolveToModelNodeList().empty());
}
-inline bool hasCollectionAsModel(const SelectionContext &selectionState)
-{
- if (!selectionState.isInBaseState() || !selectionState.singleNodeIsSelected())
- return false;
-
- const ModelNode singleSelectedNode = selectionState.currentSingleSelectedNode();
-
- return singleSelectedNode.metaInfo().isQtQuickListView()
- && singleSelectedNode.property("model").toBindingProperty().expression().startsWith(
- "DataStore.");
-}
-
inline bool selectionEnabled(const SelectionContext &selectionState)
{
return selectionState.showSelectionTools();
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
index f46b2daf78..bf8e78a2c7 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
@@ -817,21 +817,25 @@ void editMaterial(const SelectionContext &selectionContext)
QTC_ASSERT(modelNode.isValid(), return);
- BindingProperty prop = modelNode.bindingProperty("materials");
- if (!prop.exists())
- return;
-
AbstractView *view = selectionContext.view();
ModelNode material;
- if (view->hasId(prop.expression())) {
- material = view->modelNodeForId(prop.expression());
+ if (modelNode.metaInfo().isQtQuick3DMaterial()) {
+ material = modelNode;
} else {
- QList<ModelNode> materials = prop.resolveToModelNodeList();
+ BindingProperty prop = modelNode.bindingProperty("materials");
+ if (!prop.exists())
+ return;
+
+ if (view->hasId(prop.expression())) {
+ material = view->modelNodeForId(prop.expression());
+ } else {
+ QList<ModelNode> materials = prop.resolveToModelNodeList();
- if (materials.size() > 0)
- material = materials.first();
+ if (materials.size() > 0)
+ material = materials.first();
+ }
}
if (material.isValid()) {
@@ -842,30 +846,6 @@ void editMaterial(const SelectionContext &selectionContext)
}
}
-// Open a collection in the collection editor
-void editCollection(const SelectionContext &selectionContext)
-{
- ModelNode modelNode = selectionContext.targetNode();
-
- if (!modelNode)
- modelNode = selectionContext.currentSingleSelectedNode();
-
- if (!modelNode)
- return;
-
- const QString dataStoreExpression = "DataStore.";
-
- BindingProperty prop = modelNode.bindingProperty("model");
- if (!prop.exists() || !prop.expression().startsWith(dataStoreExpression))
- return;
-
- AbstractView *view = selectionContext.view();
- const QString collectionId = prop.expression().mid(dataStoreExpression.size());
-
- // to CollectionEditor...
- view->emitCustomNotification("open_collection_by_id", {}, {collectionId});
-}
-
void addItemToStackedContainer(const SelectionContext &selectionContext)
{
AbstractView *view = selectionContext.view();
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h
index a67cef4942..26562f429a 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h
@@ -92,7 +92,6 @@ void layoutGridLayout(const SelectionContext &selectionState);
void goImplementation(const SelectionContext &selectionState);
void addNewSignalHandler(const SelectionContext &selectionState);
void editMaterial(const SelectionContext &selectionContext);
-void editCollection(const SelectionContext &selectionContext);
void addSignalHandlerOrGotoImplementation(const SelectionContext &selectionState, bool addAlwaysNewSlot);
void removeLayout(const SelectionContext &selectionContext);
void removePositioner(const SelectionContext &selectionContext);
diff --git a/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp b/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp
index 8cc84058d2..58326dc77a 100644
--- a/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp
@@ -37,7 +37,7 @@ Type getProperty(const QmlJS::SimpleReaderNode *node, const QString &name)
{
if (auto property = node->property(name)) {
const auto &value = property.value;
- if (value.type() == QVariant::List) {
+ if (value.typeId() == QMetaType::QVariantList) {
auto list = value.toList();
if (list.size())
return list.front().value<Type>();
@@ -179,7 +179,7 @@ std::optional<PropertyComponentGenerator::Entry> createEntry(QmlJS::SimpleReader
if (moduleName.isEmpty())
return {};
- auto module = model->module(moduleName);
+ auto module = model->module(moduleName, Storage::ModuleKind::QmlLibrary);
auto typeName = getProperty<QByteArray>(node, "typeNames");
diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp
index b011d9fbbf..a56735862f 100644
--- a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp
@@ -7,7 +7,6 @@
#include <abstractview.h>
#include <assetslibraryview.h>
#include <capturingconnectionmanager.h>
-#include <collectionview.h>
#include <componentaction.h>
#include <componentview.h>
#include <contentlibraryview.h>
@@ -42,14 +41,6 @@
namespace QmlDesigner {
-static bool enableModelEditor()
-{
- Utils::QtcSettings *settings = Core::ICore::settings();
- const Utils::Key enableModelManagerKey = "QML/Designer/UseExperimentalFeatures44";
-
- return settings->value(enableModelManagerKey, false).toBool();
-}
-
static Q_LOGGING_CATEGORY(viewBenchmark, "qtc.viewmanager.attach", QtWarningMsg)
class ViewManagerData
@@ -64,8 +55,7 @@ public:
: connectionManager,
externalDependencies,
true)
- , collectionView{externalDependencies}
- , contentLibraryView{externalDependencies}
+ , contentLibraryView{imageCache, externalDependencies}
, componentView{externalDependencies}
#ifndef QTC_USE_QML_DESIGNER_LITE
, edit3DView{externalDependencies}
@@ -90,7 +80,6 @@ public:
Internal::DebugView debugView;
DesignerActionManagerView designerActionManagerView;
NodeInstanceView nodeInstanceView;
- CollectionView collectionView;
ContentLibraryView contentLibraryView;
ComponentView componentView;
#ifndef QTC_USE_QML_DESIGNER_LITE
@@ -235,9 +224,6 @@ QList<AbstractView *> ViewManager::standardViews() const
&d->designerActionManagerView};
#endif
- if (enableModelEditor())
- list.append(&d->collectionView);
-
if (QmlDesignerPlugin::instance()
->settings()
.value(DesignerSettingsKey::ENABLE_DEBUGVIEW)
@@ -418,8 +404,6 @@ QList<WidgetInfo> ViewManager::widgetInfos() const
widgetInfoList.append(d->textureEditorView.widgetInfo());
#endif
widgetInfoList.append(d->statesEditorView.widgetInfo());
- if (enableModelEditor())
- widgetInfoList.append(d->collectionView.widgetInfo());
if (checkEnterpriseLicense())
widgetInfoList.append(d->contentLibraryView.widgetInfo());
diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp
index f871bae84b..2cee7b0f97 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp
@@ -22,6 +22,7 @@ namespace QmlDesigner {
BindingModel::BindingModel(ConnectionView *view)
: m_connectionView(view)
+ , m_delegate(*this)
{
setHorizontalHeaderLabels(BindingModelItem::headerLabels());
}
@@ -246,11 +247,8 @@ void BindingModel::addModelNode(const ModelNode &node)
appendRow(new BindingModelItem(property));
}
-BindingModelBackendDelegate::BindingModelBackendDelegate()
- : m_targetNode()
- , m_property()
- , m_sourceNode()
- , m_sourceNodeProperty()
+BindingModelBackendDelegate::BindingModelBackendDelegate(BindingModel &model)
+ : m_model{model}
{
connect(&m_sourceNode, &StudioQmlComboBoxBackend::activated, this, [this] {
sourceNodeChanged();
@@ -322,17 +320,14 @@ StudioQmlComboBoxBackend *BindingModelBackendDelegate::sourceProperty()
void BindingModelBackendDelegate::sourceNodeChanged()
{
- BindingModel *model = qobject_cast<BindingModel *>(parent());
- QTC_ASSERT(model, return);
-
- ConnectionView *view = model->connectionView();
+ ConnectionView *view = m_model.connectionView();
QTC_ASSERT(view, return);
QTC_ASSERT(view->isAttached(), return );
const QString sourceNode = m_sourceNode.currentText();
const QString sourceProperty = m_sourceNodeProperty.currentText();
- BindingProperty targetProperty = model->currentProperty();
+ BindingProperty targetProperty = m_model.currentProperty();
QStringList properties = availableSourceProperties(sourceNode, targetProperty, view);
if (!properties.contains(sourceProperty)) {
@@ -351,9 +346,6 @@ void BindingModelBackendDelegate::sourcePropertyNameChanged() const
return;
auto commit = [this, sourceProperty]() {
- BindingModel *model = qobject_cast<BindingModel *>(parent());
- QTC_ASSERT(model, return);
-
const QString sourceNode = m_sourceNode.currentText();
QString expression;
if (sourceProperty.isEmpty())
@@ -361,8 +353,8 @@ void BindingModelBackendDelegate::sourcePropertyNameChanged() const
else
expression = sourceNode + QLatin1String(".") + sourceProperty;
- int row = model->currentIndex();
- model->commitExpression(row, expression);
+ int row = m_model.currentIndex();
+ m_model.commitExpression(row, expression);
};
callLater(commit);
@@ -371,11 +363,9 @@ void BindingModelBackendDelegate::sourcePropertyNameChanged() const
void BindingModelBackendDelegate::targetPropertyNameChanged() const
{
auto commit = [this] {
- BindingModel *model = qobject_cast<BindingModel *>(parent());
- QTC_ASSERT(model, return);
const PropertyName propertyName = m_property.currentText().toUtf8();
- int row = model->currentIndex();
- model->commitPropertyName(row, propertyName);
+ int row = m_model.currentIndex();
+ m_model.commitPropertyName(row, propertyName);
};
callLater(commit);
diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h
index b57cc5c958..69b137e78a 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h
+++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h
@@ -30,7 +30,7 @@ signals:
void targetNodeChanged();
public:
- BindingModelBackendDelegate();
+ BindingModelBackendDelegate(class BindingModel &model);
void update(const BindingProperty &property, AbstractView *view);
@@ -44,6 +44,7 @@ private:
StudioQmlComboBoxBackend *sourceNode();
StudioQmlComboBoxBackend *sourceProperty();
+ BindingModel &m_model;
QString m_targetNode;
StudioQmlComboBoxBackend m_property;
StudioQmlComboBoxBackend m_sourceNode;
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp
index 57ca619a70..3cbfb8c038 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp
@@ -212,7 +212,8 @@ bool isDynamicVariantPropertyType(const TypeName &type)
{
// "variant" is considered value type as it is initialized as one.
// This may need to change if we provide any kind of proper editor for it.
- static const QSet<TypeName> valueTypes{"int", "real", "color", "string", "bool", "url", "var", "variant"};
+ static const QSet<TypeName> valueTypes{
+ "int", "real", "double", "color", "string", "bool", "url", "var", "variant"};
return valueTypes.contains(type);
}
diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp
index 9fdd3daec3..fa29c6c8a1 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp
@@ -25,7 +25,7 @@ namespace QmlDesigner {
DynamicPropertiesModel::DynamicPropertiesModel(bool exSelection, AbstractView *view)
: m_view(view)
- , m_delegate(std::make_unique<DynamicPropertiesModelBackendDelegate>())
+ , m_delegate(std::make_unique<DynamicPropertiesModelBackendDelegate>(*this))
, m_explicitSelection(exSelection)
{
setHorizontalHeaderLabels(DynamicPropertiesItem::headerLabels());
@@ -382,8 +382,8 @@ void DynamicPropertiesModel::setSelectedNode(const ModelNode &node)
reset();
}
-DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate()
- : m_internalNodeId(std::nullopt)
+DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel &model)
+ : m_model(model)
{
m_type.setModel({"int", "bool", "var", "real", "string", "url", "color"});
connect(&m_type, &StudioQmlComboBoxBackend::activated, this, [this] { handleTypeChanged(); });
@@ -411,32 +411,26 @@ void DynamicPropertiesModelBackendDelegate::update(const AbstractProperty &prope
void DynamicPropertiesModelBackendDelegate::handleTypeChanged()
{
- DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent());
- QTC_ASSERT(model, return);
-
const PropertyName name = m_name.text().toUtf8();
- int current = model->currentIndex();
+ int current = m_model.currentIndex();
const TypeName type = m_type.currentText().toUtf8();
- model->commitPropertyType(current, type);
+ m_model.commitPropertyType(current, type);
// The order might have changed!
- model->setCurrent(m_internalNodeId.value_or(-1), name);
+ m_model.setCurrent(m_internalNodeId.value_or(-1), name);
}
void DynamicPropertiesModelBackendDelegate::handleNameChanged()
{
- DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent());
- QTC_ASSERT(model, return);
-
const PropertyName name = m_name.text().toUtf8();
QTC_ASSERT(!name.isEmpty(), return);
- int current = model->currentIndex();
- model->commitPropertyName(current, name);
+ int current = m_model.currentIndex();
+ m_model.commitPropertyName(current, name);
// The order might have changed!
- model->setCurrent(m_internalNodeId.value_or(-1), name);
+ m_model.setCurrent(m_internalNodeId.value_or(-1), name);
}
// TODO: Maybe replace with utils typeConvertVariant?
@@ -456,12 +450,9 @@ QVariant valueFromText(const QString &value, const QString &type)
void DynamicPropertiesModelBackendDelegate::handleValueChanged()
{
- DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent());
- QTC_ASSERT(model, return);
-
- int current = model->currentIndex();
+ int current = m_model.currentIndex();
QVariant value = valueFromText(m_value.text(), m_type.currentText());
- model->commitPropertyValue(current, value);
+ m_model.commitPropertyValue(current, value);
}
QString DynamicPropertiesModelBackendDelegate::targetNode() const
diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h
index c2beed8730..071c72bef8 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h
+++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h
@@ -97,7 +97,7 @@ class DynamicPropertiesModelBackendDelegate : public QObject
Q_PROPERTY(StudioQmlTextBackend *value READ value CONSTANT)
public:
- DynamicPropertiesModelBackendDelegate();
+ DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel &model);
void update(const AbstractProperty &property);
@@ -116,6 +116,7 @@ private:
StudioQmlTextBackend *value();
QString targetNode() const;
+ DynamicPropertiesModel &m_model;
std::optional<int> m_internalNodeId;
StudioQmlComboBoxBackend m_type;
StudioQmlTextBackend m_name;
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp
index 5c8d42a306..6388c93fa9 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp
@@ -3,32 +3,26 @@
#include "contentlibrarybundleimporter.h"
-#include "documentmanager.h"
-#include "import.h"
-#include "model.h"
-#include "qmldesignerconstants.h"
-#include "qmldesignerplugin.h"
-#include "rewritingexception.h"
+#include <documentmanager.h>
+#include <import.h>
+#include <model.h>
+#include <nodemetainfo.h>
+#include <qmldesignerconstants.h>
+#include <qmldesignerplugin.h>
+#include <rewritingexception.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
-#include <QFileInfo>
#include <QJsonDocument>
#include <QJsonObject>
#include <QStringList>
using namespace Utils;
-namespace QmlDesigner::Internal {
+namespace QmlDesigner {
-ContentLibraryBundleImporter::ContentLibraryBundleImporter(const QString &bundleDir,
- const QString &bundleId,
- const QStringList &sharedFiles,
- QObject *parent)
+ContentLibraryBundleImporter::ContentLibraryBundleImporter(QObject *parent)
: QObject(parent)
- , m_bundleDir(FilePath::fromString(bundleDir))
- , m_bundleId(bundleId)
- , m_sharedFiles(sharedFiles)
{
m_importTimer.setInterval(200);
connect(&m_importTimer, &QTimer::timeout, this, &ContentLibraryBundleImporter::handleImportTimer);
@@ -38,47 +32,38 @@ ContentLibraryBundleImporter::ContentLibraryBundleImporter(const QString &bundle
// Note that there is also an asynchronous portion to the import, which will only
// be done if this method returns success. Once the asynchronous portion of the
// import is completed, importFinished signal will be emitted.
-QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile,
+QString ContentLibraryBundleImporter::importComponent(const QString &bundleDir,
+ const TypeName &type,
+ const QString &qmlFile,
const QStringList &files)
{
- FilePath bundleImportPath = resolveBundleImportPath();
+ QString module = QString::fromLatin1(type.left(type.lastIndexOf('.')));
+ m_bundleId = module.mid(module.lastIndexOf('.') + 1);
+
+ FilePath bundleDirPath = FilePath::fromString(bundleDir); // source dir
+ FilePath bundleImportPath = resolveBundleImportPath(m_bundleId); // target dir
+
if (bundleImportPath.isEmpty())
return "Failed to resolve bundle import folder";
- bool bundleImportPathExists = bundleImportPath.exists();
-
- if (!bundleImportPathExists && !bundleImportPath.createDir())
+ if (!bundleImportPath.exists() && !bundleImportPath.createDir())
return QStringLiteral("Failed to create bundle import folder: '%1'").arg(bundleImportPath.toString());
- for (const QString &file : std::as_const(m_sharedFiles)) {
- FilePath target = bundleImportPath.resolvePath(file);
- if (!target.exists()) {
- FilePath parentDir = target.parentDir();
- if (!parentDir.exists() && !parentDir.createDir())
- return QStringLiteral("Failed to create folder for: '%1'").arg(target.toString());
- FilePath source = m_bundleDir.resolvePath(file);
- if (!source.copyFile(target))
- return QStringLiteral("Failed to copy shared file: '%1'").arg(source.toString());
- }
- }
-
- FilePath qmldirPath = bundleImportPath.resolvePath(QStringLiteral("qmldir"));
+ FilePath qmldirPath = bundleImportPath.pathAppended("qmldir");
QString qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray()));
if (qmldirContent.isEmpty()) {
qmldirContent.append("module ");
- qmldirContent.append(moduleName());
+ qmldirContent.append(module);
qmldirContent.append('\n');
}
- FilePath qmlSourceFile = bundleImportPath.resolvePath(FilePath::fromString(qmlFile));
+ FilePath qmlSourceFile = bundleImportPath.pathAppended(qmlFile);
const bool qmlFileExists = qmlSourceFile.exists();
const QString qmlType = qmlSourceFile.baseName();
- const QString fullTypeName = QStringLiteral("%1.%2.%3")
- .arg(QmlDesignerPlugin::instance()->documentManager()
- .generatedComponentUtils().componentBundlesTypePrefix(),
- m_bundleId, qmlType);
- if (m_pendingTypes.contains(fullTypeName) && !m_pendingTypes[fullTypeName])
- return QStringLiteral("Unable to import while unimporting the same type: '%1'").arg(fullTypeName);
+
+ if (m_pendingTypes.contains(type) && !m_pendingTypes.value(type))
+ return QStringLiteral("Unable to import while unimporting the same type: '%1'").arg(QLatin1String(type));
+
if (!qmldirContent.contains(qmlFile)) {
qmldirContent.append(qmlType);
qmldirContent.append(" 1.0 ");
@@ -91,12 +76,12 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile,
allFiles.append(files);
allFiles.append(qmlFile);
for (const QString &file : std::as_const(allFiles)) {
- FilePath target = bundleImportPath.resolvePath(file);
+ FilePath target = bundleImportPath.pathAppended(file);
FilePath parentDir = target.parentDir();
if (!parentDir.exists() && !parentDir.createDir())
return QStringLiteral("Failed to create folder for: '%1'").arg(target.toString());
- FilePath source = m_bundleDir.resolvePath(file);
+ FilePath source = bundleDirPath.pathAppended(file);
if (target.exists()) {
if (source.lastModified() == target.lastModified())
continue;
@@ -125,23 +110,23 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile,
if (!model)
return "Model not available, cannot add import statement or update code model";
- Import import = Import::createLibraryImport(moduleName(), "1.0");
+ Import import = Import::createLibraryImport(module, "1.0");
if (!model->hasImport(import)) {
if (model->possibleImports().contains(import)) {
- m_importAddPending = false;
+ m_pendingImport.clear();
try {
model->changeImports({import}, {});
} catch (const RewritingException &) {
// No point in trying to add import asynchronously either, so just fail out
- return QStringLiteral("Failed to add import statement for: '%1'").arg(moduleName());
+ return QStringLiteral("Failed to add import statement for: '%1'").arg(module);
}
} else {
// If import is not yet possible, import statement needs to be added asynchronously to
// avoid errors, as code model update takes a while.
- m_importAddPending = true;
+ m_pendingImport = module;
}
}
- m_pendingTypes.insert(fullTypeName, true);
+ m_pendingTypes.insert(type, true);
m_importTimerCount = 0;
m_importTimer.start();
@@ -153,17 +138,19 @@ void ContentLibraryBundleImporter::handleImportTimer()
auto handleFailure = [this] {
m_importTimer.stop();
m_fullReset = false;
- m_importAddPending = false;
+ m_pendingImport.clear();
m_importTimerCount = 0;
// Emit dummy finished signals for all pending types
- const QStringList pendingTypes = m_pendingTypes.keys();
- for (const QString &pendingType : pendingTypes) {
+ const QList<TypeName> pendingTypes = m_pendingTypes.keys();
+ for (const TypeName &pendingType : pendingTypes) {
m_pendingTypes.remove(pendingType);
- if (m_pendingTypes[pendingType])
- emit importFinished({});
+ if (m_pendingTypes.value(pendingType))
+ emit importFinished({}, m_bundleId);
else
- emit unimportFinished({});
+ emit unimportFinished({}, m_bundleId);
+
+ m_bundleId.clear();
}
};
@@ -185,12 +172,12 @@ void ContentLibraryBundleImporter::handleImportTimer()
QmlDesignerPlugin::instance()->documentManager().resetPossibleImports();
- if (m_importAddPending) {
+ if (!m_pendingImport.isEmpty()) {
try {
- Import import = Import::createLibraryImport(moduleName(), "1.0");
+ Import import = Import::createLibraryImport(m_pendingImport, "1.0");
if (model->possibleImports().contains(import)) {
model->changeImports({import}, {});
- m_importAddPending = false;
+ m_pendingImport.clear();
}
} catch (const RewritingException &) {
// Import adding is unlikely to succeed later, either, so just bail out
@@ -200,21 +187,23 @@ void ContentLibraryBundleImporter::handleImportTimer()
}
// Detect when the code model has the new material(s) fully available
- const QStringList pendingTypes = m_pendingTypes.keys();
- for (const QString &pendingType : pendingTypes) {
- NodeMetaInfo metaInfo = model->metaInfo(pendingType.toUtf8());
- const bool isImport = m_pendingTypes[pendingType];
+ const QList<TypeName> pendingTypes = m_pendingTypes.keys();
+ for (const TypeName &pendingType : pendingTypes) {
+ NodeMetaInfo metaInfo = model->metaInfo(pendingType);
+ const bool isImport = m_pendingTypes.value(pendingType);
const bool typeComplete = metaInfo.isValid() && !metaInfo.prototypes().empty();
if (isImport == typeComplete) {
m_pendingTypes.remove(pendingType);
if (isImport)
#ifdef QDS_USE_PROJECTSTORAGE
- emit importFinished(pendingType.toUtf8());
+ emit importFinished(pendingType, m_bundleId);
#else
- emit importFinished(metaInfo);
+ emit importFinished(metaInfo, m_bundleId);
#endif
else
- emit unimportFinished(metaInfo);
+ emit unimportFinished(metaInfo, m_bundleId);
+
+ m_bundleId.clear();
}
}
@@ -224,10 +213,10 @@ void ContentLibraryBundleImporter::handleImportTimer()
}
}
-QVariantHash ContentLibraryBundleImporter::loadAssetRefMap(const Utils::FilePath &bundlePath)
+QVariantHash ContentLibraryBundleImporter::loadAssetRefMap(const FilePath &bundlePath)
{
FilePath assetRefPath = bundlePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_ASSET_REF_FILE));
- const Utils::expected_str<QByteArray> content = assetRefPath.fileContents();
+ const expected_str<QByteArray> content = assetRefPath.fileContents();
if (content) {
QJsonParseError error;
QJsonDocument bundleDataJsonDoc = QJsonDocument::fromJson(*content, &error);
@@ -241,7 +230,7 @@ QVariantHash ContentLibraryBundleImporter::loadAssetRefMap(const Utils::FilePath
return {};
}
-void ContentLibraryBundleImporter::writeAssetRefMap(const Utils::FilePath &bundlePath,
+void ContentLibraryBundleImporter::writeAssetRefMap(const FilePath &bundlePath,
const QVariantHash &assetRefMap)
{
FilePath assetRefPath = bundlePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_ASSET_REF_FILE));
@@ -252,16 +241,14 @@ void ContentLibraryBundleImporter::writeAssetRefMap(const Utils::FilePath &bundl
}
}
-QString ContentLibraryBundleImporter::moduleName()
+QString ContentLibraryBundleImporter::unimportComponent(const TypeName &type, const QString &qmlFile)
{
- return QStringLiteral("%1.%2").arg(QmlDesignerPlugin::instance()->documentManager()
- .generatedComponentUtils().componentBundlesTypePrefix(),
- m_bundleId);
-}
+ QString module = QString::fromLatin1(type.left(type.lastIndexOf('.')));
+ m_bundleId = module.mid(module.lastIndexOf('.') + 1);
-QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile)
-{
- FilePath bundleImportPath = resolveBundleImportPath();
+ emit aboutToUnimport(type, m_bundleId);
+
+ FilePath bundleImportPath = resolveBundleImportPath(m_bundleId);
if (bundleImportPath.isEmpty())
return QStringLiteral("Failed to resolve bundle import folder for: '%1'").arg(qmlFile);
@@ -280,12 +267,10 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile)
QByteArray newContent;
QString qmlType = qmlFilePath.baseName();
- const QString fullTypeName = QStringLiteral("%1.%2.%3")
- .arg(QmlDesignerPlugin::instance()->documentManager()
- .generatedComponentUtils().componentBundlesTypePrefix(),
- m_bundleId, qmlType);
- if (m_pendingTypes.contains(fullTypeName) && m_pendingTypes[fullTypeName])
- return QStringLiteral("Unable to unimport while importing the same type: '%1'").arg(fullTypeName);
+ if (m_pendingTypes.contains(type) && m_pendingTypes.value(type)) {
+ return QStringLiteral("Unable to unimport while importing the same type: '%1'")
+ .arg(QString::fromLatin1(type));
+ }
if (qmldirContent) {
int typeIndex = qmldirContent->indexOf(qmlType.toUtf8());
@@ -301,7 +286,7 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile)
}
}
- m_pendingTypes.insert(fullTypeName, false);
+ m_pendingTypes.insert(type, false);
QVariantHash assetRefMap = loadAssetRefMap(bundleImportPath);
bool writeAssetRefs = false;
@@ -335,7 +320,7 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile)
auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
Model *model = doc ? doc->currentModel() : nullptr;
if (model) {
- Import import = Import::createLibraryImport(moduleName(), "1.0");
+ Import import = Import::createLibraryImport(module, "1.0");
if (model->imports().contains(import))
model->changeImports({}, {import});
}
@@ -348,14 +333,14 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile)
return {};
}
-FilePath ContentLibraryBundleImporter::resolveBundleImportPath()
+FilePath ContentLibraryBundleImporter::resolveBundleImportPath(const QString &bundleId)
{
FilePath bundleImportPath = QmlDesignerPlugin::instance()->documentManager()
.generatedComponentUtils().componentBundlesBasePath();
if (bundleImportPath.isEmpty())
- return bundleImportPath;
+ return {};
- return bundleImportPath.resolvePath(m_bundleId);
+ return bundleImportPath.resolvePath(bundleId);
}
-} // namespace QmlDesigner::Internal
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h
index 7fb2a48886..8155311e3e 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h
@@ -3,59 +3,53 @@
#pragma once
-#include <utils/filepath.h>
+#include <modelfwd.h>
-#include "nodemetainfo.h"
+#include <utils/filepath.h>
#include <QTimer>
#include <QVariantHash>
-QT_BEGIN_NAMESPACE
-QT_END_NAMESPACE
+namespace QmlDesigner {
-namespace QmlDesigner::Internal {
+class NodeMetaInfo;
class ContentLibraryBundleImporter : public QObject
{
Q_OBJECT
public:
- ContentLibraryBundleImporter(const QString &bundleDir,
- const QString &bundleId,
- const QStringList &sharedFiles,
- QObject *parent = nullptr);
+ ContentLibraryBundleImporter(QObject *parent = nullptr);
~ContentLibraryBundleImporter() = default;
- QString importComponent(const QString &qmlFile,
+ QString importComponent(const QString &bundleDir, const TypeName &type, const QString &qmlFile,
const QStringList &files);
- QString unimportComponent(const QString &qmlFile);
- Utils::FilePath resolveBundleImportPath();
+ QString unimportComponent(const TypeName &type, const QString &qmlFile);
+ Utils::FilePath resolveBundleImportPath(const QString &bundleId);
signals:
// The metaInfo parameter will be invalid if an error was encountered during
// asynchronous part of the import. In this case all remaining pending imports have been
// terminated, and will not receive separate importFinished notifications.
#ifdef QDS_USE_PROJECTSTORAGE
- void importFinished(const QmlDesigner::TypeName &typeName);
+ void importFinished(const QmlDesigner::TypeName &typeName, const QString &bundleId);
#else
- void importFinished(const QmlDesigner::NodeMetaInfo &metaInfo);
+ void importFinished(const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId);
#endif
- void unimportFinished(const QmlDesigner::NodeMetaInfo &metaInfo);
+ void unimportFinished(const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId);
+ void aboutToUnimport(const TypeName &type, const QString &bundleId);
private:
void handleImportTimer();
QVariantHash loadAssetRefMap(const Utils::FilePath &bundlePath);
void writeAssetRefMap(const Utils::FilePath &bundlePath, const QVariantHash &assetRefMap);
- QString moduleName();
- Utils::FilePath m_bundleDir;
- QString m_bundleId;
- QStringList m_sharedFiles;
QTimer m_importTimer;
int m_importTimerCount = 0;
- bool m_importAddPending = false;
+ QString m_pendingImport;
+ QString m_bundleId;
bool m_fullReset = false;
- QHash<QString, bool> m_pendingTypes; // <type, isImport>
+ QHash<TypeName, bool> m_pendingTypes; // <type, isImport>
};
-} // namespace QmlDesigner::Internal
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp
deleted file mode 100644
index f572fbe65f..0000000000
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-// 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 "contentlibraryeffect.h"
-
-#include <QFileInfo>
-
-namespace QmlDesigner {
-
-ContentLibraryEffect::ContentLibraryEffect(QObject *parent,
- const QString &name,
- const QString &qml,
- const TypeName &type,
- const QUrl &icon,
- const QStringList &files)
- : QObject(parent), m_name(name), m_qml(qml), m_type(type), m_icon(icon), m_files(files)
-{
- m_allFiles = m_files;
- m_allFiles.push_back(m_qml);
-}
-
-bool ContentLibraryEffect::filter(const QString &searchText)
-{
- if (m_visible != m_name.contains(searchText, Qt::CaseInsensitive)) {
- m_visible = !m_visible;
- emit itemVisibleChanged();
- }
-
- return m_visible;
-}
-
-QUrl ContentLibraryEffect::icon() const
-{
- return m_icon;
-}
-
-QString ContentLibraryEffect::qml() const
-{
- return m_qml;
-}
-
-TypeName ContentLibraryEffect::type() const
-{
- return m_type;
-}
-
-QStringList ContentLibraryEffect::files() const
-{
- return m_files;
-}
-
-bool ContentLibraryEffect::visible() const
-{
- return m_visible;
-}
-
-bool ContentLibraryEffect::setImported(bool imported)
-{
- if (m_imported != imported) {
- m_imported = imported;
- emit itemImportedChanged();
- return true;
- }
-
- return false;
-}
-
-bool ContentLibraryEffect::imported() const
-{
- return m_imported;
-}
-
-QStringList ContentLibraryEffect::allFiles() const
-{
- return m_allFiles;
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp
index 38e6eed3da..f904775d52 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp
@@ -3,14 +3,12 @@
#include "contentlibraryeffectscategory.h"
-#include "contentlibraryeffect.h"
-
namespace QmlDesigner {
ContentLibraryEffectsCategory::ContentLibraryEffectsCategory(QObject *parent, const QString &name)
: QObject(parent), m_name(name) {}
-void ContentLibraryEffectsCategory::addBundleItem(ContentLibraryEffect *bundleItem)
+void ContentLibraryEffectsCategory::addBundleItem(ContentLibraryItem *bundleItem)
{
m_categoryItems.append(bundleItem);
}
@@ -19,7 +17,7 @@ bool ContentLibraryEffectsCategory::updateImportedState(const QStringList &impor
{
bool changed = false;
- for (ContentLibraryEffect *item : std::as_const(m_categoryItems))
+ for (ContentLibraryItem *item : std::as_const(m_categoryItems))
changed |= item->setImported(importedItems.contains(item->qml().chopped(4)));
return changed;
@@ -28,7 +26,7 @@ bool ContentLibraryEffectsCategory::updateImportedState(const QStringList &impor
bool ContentLibraryEffectsCategory::filter(const QString &searchText)
{
bool visible = false;
- for (ContentLibraryEffect *item : std::as_const(m_categoryItems))
+ for (ContentLibraryItem *item : std::as_const(m_categoryItems))
visible |= item->filter(searchText);
if (visible != m_visible) {
@@ -55,7 +53,7 @@ bool ContentLibraryEffectsCategory::expanded() const
return m_expanded;
}
-QList<ContentLibraryEffect *> ContentLibraryEffectsCategory::categoryItems() const
+QList<ContentLibraryItem *> ContentLibraryEffectsCategory::categoryItems() const
{
return m_categoryItems;
}
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h
index 79737c1ec2..9f56de3c6b 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h
@@ -3,12 +3,12 @@
#pragma once
+#include "contentlibraryitem.h"
+
#include <QObject>
namespace QmlDesigner {
-class ContentLibraryEffect;
-
class ContentLibraryEffectsCategory : public QObject
{
Q_OBJECT
@@ -16,20 +16,20 @@ class ContentLibraryEffectsCategory : public QObject
Q_PROPERTY(QString bundleCategoryName MEMBER m_name CONSTANT)
Q_PROPERTY(bool bundleCategoryVisible MEMBER m_visible NOTIFY categoryVisibleChanged)
Q_PROPERTY(bool bundleCategoryExpanded MEMBER m_expanded NOTIFY categoryExpandChanged)
- Q_PROPERTY(QList<ContentLibraryEffect *> bundleCategoryItems MEMBER m_categoryItems
+ Q_PROPERTY(QList<ContentLibraryItem *> bundleCategoryItems MEMBER m_categoryItems
NOTIFY categoryItemsChanged)
public:
ContentLibraryEffectsCategory(QObject *parent, const QString &name);
- void addBundleItem(ContentLibraryEffect *bundleItem);
+ void addBundleItem(ContentLibraryItem *bundleItem);
bool updateImportedState(const QStringList &importedMats);
bool filter(const QString &searchText);
QString name() const;
bool visible() const;
bool expanded() const;
- QList<ContentLibraryEffect *> categoryItems() const;
+ QList<ContentLibraryItem *> categoryItems() const;
signals:
void categoryVisibleChanged();
@@ -41,7 +41,7 @@ private:
bool m_visible = true;
bool m_expanded = true;
- QList<ContentLibraryEffect *> m_categoryItems;
+ QList<ContentLibraryItem *> m_categoryItems;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp
index 334c017116..e0fe2b6c54 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp
@@ -4,8 +4,8 @@
#include "contentlibraryeffectsmodel.h"
#include "contentlibrarybundleimporter.h"
-#include "contentlibraryeffect.h"
#include "contentlibraryeffectscategory.h"
+#include "contentlibraryitem.h"
#include "contentlibrarywidget.h"
#include <qmldesignerplugin.h>
@@ -63,6 +63,11 @@ bool ContentLibraryEffectsModel::isValidIndex(int idx) const
return idx > -1 && idx < rowCount();
}
+QString ContentLibraryEffectsModel::bundleId() const
+{
+ return m_bundleId;
+}
+
void ContentLibraryEffectsModel::updateIsEmpty()
{
bool anyCatVisible = Utils::anyOf(m_bundleCategories, [&](ContentLibraryEffectsCategory *cat) {
@@ -88,49 +93,23 @@ QHash<int, QByteArray> ContentLibraryEffectsModel::roleNames() const
return roles;
}
-void ContentLibraryEffectsModel::createImporter(const QString &bundlePath, const QString &bundleId,
- const QStringList &sharedFiles)
-{
- m_importer = new Internal::ContentLibraryBundleImporter(bundlePath, bundleId, sharedFiles);
-#ifdef QDS_USE_PROJECTSTORAGE
- connect(m_importer,
- &Internal::ContentLibraryBundleImporter::importFinished,
- this,
- [&](const QmlDesigner::TypeName &typeName) {
- m_importerRunning = false;
- emit importerRunningChanged();
- if (typeName.size())
- emit bundleItemImported(typeName);
- });
-#else
- connect(m_importer,
- &Internal::ContentLibraryBundleImporter::importFinished,
- this,
- [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
- m_importerRunning = false;
- emit importerRunningChanged();
- if (metaInfo.isValid())
- emit bundleItemImported(metaInfo);
- });
-#endif
-
- connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this,
- [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
- Q_UNUSED(metaInfo)
- m_importerRunning = false;
- emit importerRunningChanged();
- emit bundleItemUnimported(metaInfo);
- });
-
- resetModel();
- updateIsEmpty();
-}
-
void ContentLibraryEffectsModel::loadBundle()
{
- if (m_bundleExists || m_probeBundleDir)
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+
+ if (m_probeBundleDir || (m_bundleExists && m_bundleId == compUtils.effectsBundleId()))
return;
+ // clean up
+ qDeleteAll(m_bundleCategories);
+ m_bundleCategories.clear();
+ m_bundleExists = false;
+ m_isEmpty = true;
+ m_probeBundleDir = false;
+ m_bundleObj = {};
+ m_bundleId.clear();
+ m_bundlePath.clear();
+
QDir bundleDir;
if (!qEnvironmentVariable("EFFECT_BUNDLE_PATH").isEmpty())
@@ -145,30 +124,32 @@ void ContentLibraryEffectsModel::loadBundle()
while (!bundleDir.cd("effect_bundle") && bundleDir.cdUp())
; // do nothing
- if (bundleDir.dirName() != "effect_bundle") // bundlePathDir not found
+ if (bundleDir.dirName() != "effect_bundle") { // bundlePathDir not found
+ resetModel();
return;
+ }
}
QString bundlePath = bundleDir.filePath("effect_bundle.json");
- if (m_bundleObj.isEmpty()) {
- QFile propsFile(bundlePath);
-
- if (!propsFile.open(QIODevice::ReadOnly)) {
- qWarning("Couldn't open effect_bundle.json");
- return;
- }
+ QFile bundleFile(bundlePath);
+ if (!bundleFile.open(QIODevice::ReadOnly)) {
+ qWarning("Couldn't open effect_bundle.json");
+ resetModel();
+ return;
+ }
- QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(propsFile.readAll());
- if (bundleJsonDoc.isNull()) {
- qWarning("Invalid effect_bundle.json file");
- return;
- } else {
- m_bundleObj = bundleJsonDoc.object();
- }
+ QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(bundleFile.readAll());
+ if (bundleJsonDoc.isNull()) {
+ qWarning("Invalid effect_bundle.json file");
+ resetModel();
+ return;
}
- QString bundleId = m_bundleObj.value("id").toString();
+ m_bundleObj = bundleJsonDoc.object();
+
+ QString bundleType = compUtils.effectsBundleType();
+ m_bundleId = compUtils.effectsBundleId();
const QJsonObject catsObj = m_bundleObj.value("categories").toObject();
const QStringList categories = catsObj.keys();
@@ -176,39 +157,36 @@ void ContentLibraryEffectsModel::loadBundle()
auto category = new ContentLibraryEffectsCategory(this, cat);
const QJsonObject itemsObj = catsObj.value(cat).toObject();
- const QStringList items = itemsObj.keys();
- for (const QString &item : items) {
- const QJsonObject itemObj = itemsObj.value(item).toObject();
+ const QStringList itemsNames = itemsObj.keys();
+ for (const QString &itemName : itemsNames) {
+ const QJsonObject itemObj = itemsObj.value(itemName).toObject();
QStringList files;
const QJsonArray assetsArr = itemObj.value("files").toArray();
- for (const auto /*QJson{Const,}ValueRef*/ &asset : assetsArr)
+ for (const QJsonValueConstRef &asset : assetsArr)
files.append(asset.toString());
QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(itemObj.value("icon").toString()));
QString qml = itemObj.value("qml").toString();
- TypeName type = QLatin1String("%1.%2.%3")
- .arg(QmlDesignerPlugin::instance()->documentManager()
- .generatedComponentUtils().componentBundlesTypePrefix(),
- bundleId,
- qml.chopped(4)).toLatin1(); // chopped(4): remove .qml
+ TypeName type = QLatin1String("%1.%2")
+ .arg(bundleType, qml.chopped(4)).toLatin1(); // chopped(4): remove .qml
- auto bundleItem = new ContentLibraryEffect(category, item, qml, type, icon, files);
+ auto bundleItem = new ContentLibraryItem(category, itemName, qml, type, icon, files);
category->addBundleItem(bundleItem);
}
m_bundleCategories.append(category);
}
- QStringList sharedFiles;
+ m_bundleSharedFiles.clear();
const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray();
- for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr)
- sharedFiles.append(file.toString());
-
- createImporter(bundleDir.path(), bundleId, sharedFiles);
+ for (const QJsonValueConstRef &file : sharedFilesArr)
+ m_bundleSharedFiles.append(file.toString());
+ m_bundlePath = bundleDir.path();
m_bundleExists = true;
- emit bundleExistsChanged();
+ updateIsEmpty();
+ resetModel();
}
bool ContentLibraryEffectsModel::hasRequiredQuick3DImport() const
@@ -221,11 +199,6 @@ bool ContentLibraryEffectsModel::bundleExists() const
return m_bundleExists;
}
-Internal::ContentLibraryBundleImporter *ContentLibraryEffectsModel::bundleImporter() const
-{
- return m_importer;
-}
-
void ContentLibraryEffectsModel::setSearchText(const QString &searchText)
{
QString lowerSearchText = searchText.toLower();
@@ -278,30 +251,26 @@ void ContentLibraryEffectsModel::resetModel()
endResetModel();
}
-void ContentLibraryEffectsModel::addInstance(ContentLibraryEffect *bundleItem)
+void ContentLibraryEffectsModel::addInstance(ContentLibraryItem *bundleItem)
{
- QString err = m_importer->importComponent(bundleItem->qml(), bundleItem->files());
+ QString err = m_widget->importer()->importComponent(m_bundlePath, bundleItem->type(),
+ bundleItem->qml(),
+ bundleItem->files() + m_bundleSharedFiles);
- if (err.isEmpty()) {
- m_importerRunning = true;
- emit importerRunningChanged();
- } else {
+ if (err.isEmpty())
+ m_widget->setImporterRunning(true);
+ else
qWarning() << __FUNCTION__ << err;
- }
}
-void ContentLibraryEffectsModel::removeFromProject(ContentLibraryEffect *bundleItem)
+void ContentLibraryEffectsModel::removeFromProject(ContentLibraryItem *bundleItem)
{
- emit bundleItemAboutToUnimport(bundleItem->type());
+ QString err = m_widget->importer()->unimportComponent(bundleItem->type(), bundleItem->qml());
- QString err = m_importer->unimportComponent(bundleItem->qml());
-
- if (err.isEmpty()) {
- m_importerRunning = true;
- emit importerRunningChanged();
- } else {
+ if (err.isEmpty())
+ m_widget->setImporterRunning(true);
+ else
qWarning() << __FUNCTION__ << err;
- }
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h
index 5d67ac3da8..5bcb857fca 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h
@@ -3,22 +3,15 @@
#pragma once
-#include "nodemetainfo.h"
-
#include <QAbstractListModel>
-#include <QDir>
#include <QJsonObject>
namespace QmlDesigner {
-class ContentLibraryEffect;
+class ContentLibraryItem;
class ContentLibraryEffectsCategory;
class ContentLibraryWidget;
-namespace Internal {
-class ContentLibraryBundleImporter;
-}
-
class ContentLibraryEffectsModel : public QAbstractListModel
{
Q_OBJECT
@@ -26,7 +19,6 @@ class ContentLibraryEffectsModel : public QAbstractListModel
Q_PROPERTY(bool bundleExists READ bundleExists NOTIFY bundleExistsChanged)
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged)
- Q_PROPERTY(bool importerRunning MEMBER m_importerRunning NOTIFY importerRunningChanged)
public:
ContentLibraryEffectsModel(ContentLibraryWidget *parent = nullptr);
@@ -49,46 +41,33 @@ public:
void resetModel();
void updateIsEmpty();
- Internal::ContentLibraryBundleImporter *bundleImporter() const;
+ Q_INVOKABLE void addInstance(QmlDesigner::ContentLibraryItem *bundleItem);
+ Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryItem *bundleItem);
- Q_INVOKABLE void addInstance(QmlDesigner::ContentLibraryEffect *bundleItem);
- Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryEffect *bundleItem);
+ QString bundleId() const;
signals:
void isEmptyChanged();
void hasRequiredQuick3DImportChanged();
-#ifdef QDS_USE_PROJECTSTORAGE
- void bundleItemImported(const QmlDesigner::TypeName &typeName);
-#else
- void bundleItemImported(const QmlDesigner::NodeMetaInfo &metaInfo);
-#endif
- void bundleItemAboutToUnimport(const QmlDesigner::TypeName &type);
- void bundleItemUnimported(const QmlDesigner::NodeMetaInfo &metaInfo);
- void importerRunningChanged();
void bundleExistsChanged();
private:
bool isValidIndex(int idx) const;
- void createImporter(const QString &bundlePath, const QString &bundleId,
- const QStringList &sharedFiles);
ContentLibraryWidget *m_widget = nullptr;
QString m_searchText;
+ QString m_bundlePath;
+ QString m_bundleId;
+ QStringList m_bundleSharedFiles;
QList<ContentLibraryEffectsCategory *> m_bundleCategories;
QJsonObject m_bundleObj;
- Internal::ContentLibraryBundleImporter *m_importer = nullptr;
bool m_isEmpty = true;
bool m_bundleExists = false;
bool m_probeBundleDir = false;
- bool m_importerRunning = false;
int m_quick3dMajorVersion = -1;
int m_quick3dMinorVersion = -1;
-
- QString m_importerBundlePath;
- QString m_importerBundleId;
- QStringList m_importerSharedFiles;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.cpp
new file mode 100644
index 0000000000..38fb69abbc
--- /dev/null
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.cpp
@@ -0,0 +1,76 @@
+// 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 "contentlibraryitem.h"
+
+namespace QmlDesigner {
+
+ContentLibraryItem::ContentLibraryItem(QObject *parent,
+ const QString &name,
+ const QString &qml,
+ const TypeName &type,
+ const QUrl &icon,
+ const QStringList &files)
+ : QObject(parent), m_name(name), m_qml(qml), m_type(type), m_icon(icon), m_files(files)
+{
+ m_allFiles = m_files;
+ m_allFiles.push_back(m_qml);
+}
+
+bool ContentLibraryItem::filter(const QString &searchText)
+{
+ if (m_visible != m_name.contains(searchText, Qt::CaseInsensitive)) {
+ m_visible = !m_visible;
+ emit itemVisibleChanged();
+ }
+
+ return m_visible;
+}
+
+QUrl ContentLibraryItem::icon() const
+{
+ return m_icon;
+}
+
+QString ContentLibraryItem::qml() const
+{
+ return m_qml;
+}
+
+TypeName ContentLibraryItem::type() const
+{
+ return m_type;
+}
+
+QStringList ContentLibraryItem::files() const
+{
+ return m_files;
+}
+
+bool ContentLibraryItem::visible() const
+{
+ return m_visible;
+}
+
+bool ContentLibraryItem::setImported(bool imported)
+{
+ if (m_imported != imported) {
+ m_imported = imported;
+ emit itemImportedChanged();
+ return true;
+ }
+
+ return false;
+}
+
+bool ContentLibraryItem::imported() const
+{
+ return m_imported;
+}
+
+QStringList ContentLibraryItem::allFiles() const
+{
+ return m_allFiles;
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.h
index fdb302b613..bdf8736728 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.h
@@ -10,7 +10,7 @@
namespace QmlDesigner {
-class ContentLibraryEffect : public QObject
+class ContentLibraryItem : public QObject
{
Q_OBJECT
@@ -19,14 +19,15 @@ class ContentLibraryEffect : public QObject
Q_PROPERTY(QStringList bundleItemFiles READ allFiles CONSTANT)
Q_PROPERTY(bool bundleItemVisible MEMBER m_visible NOTIFY itemVisibleChanged)
Q_PROPERTY(bool bundleItemImported READ imported WRITE setImported NOTIFY itemImportedChanged)
+ Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT)
public:
- ContentLibraryEffect(QObject *parent,
- const QString &name,
- const QString &qml,
- const TypeName &type,
- const QUrl &icon,
- const QStringList &files);
+ ContentLibraryItem(QObject *parent,
+ const QString &name,
+ const QString &qml,
+ const TypeName &type,
+ const QUrl &icon,
+ const QStringList &files);
bool filter(const QString &searchText);
@@ -35,11 +36,9 @@ public:
TypeName type() const;
QStringList files() const;
bool visible() const;
- QString qmlFilePath() const;
bool setImported(bool imported);
bool imported() const;
- QString parentDirPath() const;
QStringList allFiles() const;
signals:
@@ -57,6 +56,7 @@ private:
bool m_imported = false;
QStringList m_allFiles;
+ const QString m_itemType = "item";
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp
index 834bc8aa30..25d1523199 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp
@@ -32,6 +32,11 @@ bool ContentLibraryMaterial::filter(const QString &searchText)
return m_visible;
}
+QString ContentLibraryMaterial::name() const
+{
+ return m_name;
+}
+
QUrl ContentLibraryMaterial::icon() const
{
return m_icon;
@@ -84,7 +89,7 @@ QString ContentLibraryMaterial::qmlFilePath() const
return m_downloadPath + "/" + m_qml;
}
-QString ContentLibraryMaterial::parentDirPath() const
+QString ContentLibraryMaterial::dirPath() const
{
return m_downloadPath;
}
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h
index 55af2accbd..aaaf9517f9 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h
@@ -17,9 +17,9 @@ class ContentLibraryMaterial : public QObject
Q_PROPERTY(QString bundleMaterialName MEMBER m_name CONSTANT)
Q_PROPERTY(QUrl bundleMaterialIcon MEMBER m_icon CONSTANT)
Q_PROPERTY(bool bundleMaterialVisible MEMBER m_visible NOTIFY materialVisibleChanged)
- Q_PROPERTY(bool bundleMaterialImported READ imported WRITE setImported NOTIFY materialImportedChanged)
+ Q_PROPERTY(bool bundleItemImported READ imported WRITE setImported NOTIFY materialImportedChanged)
Q_PROPERTY(QString bundleMaterialBaseWebUrl MEMBER m_baseWebUrl CONSTANT)
- Q_PROPERTY(QString bundleMaterialParentPath READ parentDirPath CONSTANT)
+ Q_PROPERTY(QString bundleMaterialDirPath READ dirPath CONSTANT)
Q_PROPERTY(QStringList bundleMaterialFiles READ allFiles CONSTANT)
Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT)
@@ -37,6 +37,7 @@ public:
Q_INVOKABLE bool isDownloaded() const;
+ QString name() const;
QUrl icon() const;
QString qml() const;
TypeName type() const;
@@ -46,7 +47,7 @@ public:
bool setImported(bool imported);
bool imported() const;
- QString parentDirPath() const;
+ QString dirPath() const;
QStringList allFiles() const;
signals:
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp
index 26747d359c..adb47108a9 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp
@@ -20,6 +20,7 @@
#include <utils/qtcassert.h>
#include <QCoreApplication>
+#include <QDir>
#include <QJsonArray>
#include <QJsonDocument>
#include <QQmlEngine>
@@ -40,7 +41,10 @@ ContentLibraryMaterialsModel::ContentLibraryMaterialsModel(ContentLibraryWidget
qmlRegisterType<QmlDesigner::FileDownloader>("WebFetcher", 1, 0, "FileDownloader");
qmlRegisterType<QmlDesigner::MultiFileDownloader>("WebFetcher", 1, 0, "MultiFileDownloader");
+}
+void ContentLibraryMaterialsModel::loadBundle()
+{
QDir bundleDir{m_downloadPath};
if (fetchBundleMetadata(bundleDir) && fetchBundleIcons(bundleDir))
loadMaterialBundle(bundleDir);
@@ -192,11 +196,9 @@ void ContentLibraryMaterialsModel::downloadSharedFiles(const QDir &targetDir, co
extractor->setAlwaysCreateDir(false);
extractor->setClearTargetPathContents(false);
- QObject::connect(extractor, &FileExtractor::finishedChanged, this, [this, downloader, extractor]() {
+ QObject::connect(extractor, &FileExtractor::finishedChanged, this, [downloader, extractor]() {
downloader->deleteLater();
extractor->deleteLater();
-
- createImporter(m_importerBundlePath, m_importerBundleId, m_importerSharedFiles);
});
extractor->extract();
@@ -205,71 +207,48 @@ void ContentLibraryMaterialsModel::downloadSharedFiles(const QDir &targetDir, co
downloader->start();
}
-void ContentLibraryMaterialsModel::createImporter(const QString &bundlePath, const QString &bundleId,
- const QStringList &sharedFiles)
+QString ContentLibraryMaterialsModel::bundleId() const
{
- m_importer = new Internal::ContentLibraryBundleImporter(bundlePath, bundleId, sharedFiles);
-#ifdef QDS_USE_PROJECTSTORAGE
- connect(m_importer,
- &Internal::ContentLibraryBundleImporter::importFinished,
- this,
- [&](const QmlDesigner::TypeName &typeName) {
- m_importerRunning = false;
- emit importerRunningChanged();
- if (typeName.size())
- emit bundleMaterialImported(typeName);
- });
-#else
- connect(m_importer,
- &Internal::ContentLibraryBundleImporter::importFinished,
- this,
- [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
- m_importerRunning = false;
- emit importerRunningChanged();
- if (metaInfo.isValid())
- emit bundleMaterialImported(metaInfo);
- });
-#endif
-
- connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this,
- [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
- Q_UNUSED(metaInfo)
- m_importerRunning = false;
- emit importerRunningChanged();
- emit bundleMaterialUnimported(metaInfo);
- });
-
- resetModel();
- updateIsEmpty();
+ return m_bundleId;
}
-void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir)
+void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &bundleDir)
{
- if (m_matBundleExists)
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+
+ if (m_bundleExists && m_bundleId == compUtils.materialsBundleId())
return;
- QString matBundlePath = matBundleDir.filePath("material_bundle.json");
+ // clean up
+ qDeleteAll(m_bundleCategories);
+ m_bundleCategories.clear();
+ m_bundleExists = false;
+ m_isEmpty = true;
+ m_bundleObj = {};
+ m_bundleId.clear();
- if (m_matBundleObj.isEmpty()) {
- QFile matPropsFile(matBundlePath);
+ QString bundlePath = bundleDir.filePath("material_bundle.json");
- if (!matPropsFile.open(QIODevice::ReadOnly)) {
- qWarning("Couldn't open material_bundle.json");
- return;
- }
+ QFile bundleFile(bundlePath);
+ if (!bundleFile.open(QIODevice::ReadOnly)) {
+ qWarning("Couldn't open material_bundle.json");
+ resetModel();
+ return;
+ }
- QJsonDocument matBundleJsonDoc = QJsonDocument::fromJson(matPropsFile.readAll());
- if (matBundleJsonDoc.isNull()) {
- qWarning("Invalid material_bundle.json file");
- return;
- } else {
- m_matBundleObj = matBundleJsonDoc.object();
- }
+ QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(bundleFile.readAll());
+ if (bundleJsonDoc.isNull()) {
+ qWarning("Invalid material_bundle.json file");
+ resetModel();
+ return;
}
- QString bundleId = m_matBundleObj.value("id").toString();
+ m_bundleObj = bundleJsonDoc.object();
+
+ QString bundleType = compUtils.materialsBundleType();
+ m_bundleId = compUtils.materialsBundleId();
- const QJsonObject catsObj = m_matBundleObj.value("categories").toObject();
+ const QJsonObject catsObj = m_bundleObj.value("categories").toObject();
const QStringList categories = catsObj.keys();
for (const QString &cat : categories) {
auto category = new ContentLibraryMaterialsCategory(this, cat);
@@ -281,16 +260,13 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir)
QStringList files;
const QJsonArray assetsArr = matObj.value("files").toArray();
- for (const auto /*QJson{Const,}ValueRef*/ &asset : assetsArr)
+ for (const QJsonValueConstRef &asset : assetsArr)
files.append(asset.toString());
- QUrl icon = QUrl::fromLocalFile(matBundleDir.filePath(matObj.value("icon").toString()));
+ QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(matObj.value("icon").toString()));
QString qml = matObj.value("qml").toString();
- TypeName type = QLatin1String("%1.%2.%3")
- .arg(QmlDesignerPlugin::instance()->documentManager()
- .generatedComponentUtils().componentBundlesTypePrefix(),
- bundleId,
- qml.chopped(4)).toLatin1(); // chopped(4): remove .qml
+ TypeName type = QLatin1String("%1.%2")
+ .arg(bundleType, qml.chopped(4)).toLatin1(); // chopped(4): remove .qml
auto bundleMat = new ContentLibraryMaterial(category, matName, qml, type, icon, files,
m_downloadPath, m_baseUrl);
@@ -300,30 +276,23 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir)
m_bundleCategories.append(category);
}
- QStringList sharedFiles;
- const QJsonArray sharedFilesArr = m_matBundleObj.value("sharedFiles").toArray();
- for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr)
- sharedFiles.append(file.toString());
+ m_bundleSharedFiles.clear();
+ const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray();
+ for (const QJsonValueConstRef &file : sharedFilesArr)
+ m_bundleSharedFiles.append(file.toString());
QStringList missingSharedFiles;
- for (const QString &s : std::as_const(sharedFiles)) {
- const QString fullSharedFilePath = matBundleDir.filePath(s);
-
- if (!QFileInfo::exists(fullSharedFilePath))
+ for (const QString &s : std::as_const(m_bundleSharedFiles)) {
+ if (!QFileInfo::exists(bundleDir.filePath(s)))
missingSharedFiles.push_back(s);
}
- if (missingSharedFiles.length() > 0) {
- m_importerBundlePath = matBundleDir.path();
- m_importerBundleId = bundleId;
- m_importerSharedFiles = sharedFiles;
- downloadSharedFiles(matBundleDir, missingSharedFiles);
- } else {
- createImporter(matBundleDir.path(), bundleId, sharedFiles);
- }
+ if (missingSharedFiles.length() > 0)
+ downloadSharedFiles(bundleDir, missingSharedFiles);
- m_matBundleExists = true;
- emit matBundleExistsChanged();
+ m_bundleExists = true;
+ updateIsEmpty();
+ resetModel();
}
bool ContentLibraryMaterialsModel::hasRequiredQuick3DImport() const
@@ -333,12 +302,7 @@ bool ContentLibraryMaterialsModel::hasRequiredQuick3DImport() const
bool ContentLibraryMaterialsModel::matBundleExists() const
{
- return m_matBundleExists;
-}
-
-Internal::ContentLibraryBundleImporter *ContentLibraryMaterialsModel::bundleImporter() const
-{
- return m_importer;
+ return m_bundleExists;
}
void ContentLibraryMaterialsModel::setSearchText(const QString &searchText)
@@ -360,11 +324,11 @@ void ContentLibraryMaterialsModel::setSearchText(const QString &searchText)
updateIsEmpty();
}
-void ContentLibraryMaterialsModel::updateImportedState(const QStringList &importedMats)
+void ContentLibraryMaterialsModel::updateImportedState(const QStringList &importedItems)
{
bool changed = false;
for (ContentLibraryMaterialsCategory *cat : std::as_const(m_bundleCategories))
- changed |= cat->updateImportedState(importedMats);
+ changed |= cat->updateImportedState(importedItems);
if (changed)
resetModel();
@@ -400,42 +364,23 @@ void ContentLibraryMaterialsModel::applyToSelected(ContentLibraryMaterial *mat,
void ContentLibraryMaterialsModel::addToProject(ContentLibraryMaterial *mat)
{
- QString err = m_importer->importComponent(mat->qml(), mat->files());
+ QString err = m_widget->importer()->importComponent(mat->dirPath(), mat->type(), mat->qml(),
+ mat->files() + m_bundleSharedFiles);
- if (err.isEmpty()) {
- m_importerRunning = true;
- emit importerRunningChanged();
- } else {
+ if (err.isEmpty())
+ m_widget->setImporterRunning(true);
+ else
qWarning() << __FUNCTION__ << err;
- }
}
void ContentLibraryMaterialsModel::removeFromProject(ContentLibraryMaterial *mat)
{
- emit bundleMaterialAboutToUnimport(mat->type());
+ QString err = m_widget->importer()->unimportComponent(mat->type(), mat->qml());
- QString err = m_importer->unimportComponent(mat->qml());
-
- if (err.isEmpty()) {
- m_importerRunning = true;
- emit importerRunningChanged();
- } else {
+ if (err.isEmpty())
+ m_widget->setImporterRunning(true);
+ else
qWarning() << __FUNCTION__ << err;
- }
-}
-
-bool ContentLibraryMaterialsModel::hasModelSelection() const
-{
- return m_hasModelSelection;
-}
-
-void ContentLibraryMaterialsModel::setHasModelSelection(bool b)
-{
- if (b == m_hasModelSelection)
- return;
-
- m_hasModelSelection = b;
- emit hasModelSelectionChanged();
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h
index 21bd374137..c0840dc3cc 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h
@@ -3,22 +3,17 @@
#pragma once
-#include "nodemetainfo.h"
-
#include <QAbstractListModel>
-#include <QDir>
#include <QJsonObject>
+QT_FORWARD_DECLARE_CLASS(QDir)
+
namespace QmlDesigner {
class ContentLibraryMaterial;
class ContentLibraryMaterialsCategory;
class ContentLibraryWidget;
-namespace Internal {
-class ContentLibraryBundleImporter;
-}
-
class ContentLibraryMaterialsModel : public QAbstractListModel
{
Q_OBJECT
@@ -26,8 +21,6 @@ class ContentLibraryMaterialsModel : public QAbstractListModel
Q_PROPERTY(bool matBundleExists READ matBundleExists NOTIFY matBundleExistsChanged)
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged)
- Q_PROPERTY(bool hasModelSelection READ hasModelSelection NOTIFY hasModelSelectionChanged)
- Q_PROPERTY(bool importerRunning MEMBER m_importerRunning NOTIFY importerRunningChanged)
public:
ContentLibraryMaterialsModel(ContentLibraryWidget *parent = nullptr);
@@ -38,40 +31,27 @@ public:
QHash<int, QByteArray> roleNames() const override;
void setSearchText(const QString &searchText);
- void updateImportedState(const QStringList &importedMats);
-
+ void updateImportedState(const QStringList &importedItems);
void setQuick3DImportVersion(int major, int minor);
bool hasRequiredQuick3DImport() const;
-
bool matBundleExists() const;
- bool hasModelSelection() const;
- void setHasModelSelection(bool b);
-
void resetModel();
void updateIsEmpty();
-
- Internal::ContentLibraryBundleImporter *bundleImporter() const;
+ void loadBundle();
Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false);
Q_INVOKABLE void addToProject(QmlDesigner::ContentLibraryMaterial *mat);
Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryMaterial *mat);
+ QString bundleId() const;
+
signals:
void isEmptyChanged();
void hasRequiredQuick3DImportChanged();
- void hasModelSelectionChanged();
void materialVisibleChanged();
void applyToSelectedTriggered(QmlDesigner::ContentLibraryMaterial *mat, bool add = false);
-#ifdef QDS_USE_PROJECTSTORAGE
- void bundleMaterialImported(const QmlDesigner::TypeName &typeName);
-#else
- void bundleMaterialImported(const QmlDesigner::NodeMetaInfo &metaInfo);
-#endif
- void bundleMaterialAboutToUnimport(const QmlDesigner::TypeName &type);
- void bundleMaterialUnimported(const QmlDesigner::NodeMetaInfo &metaInfo);
- void importerRunningChanged();
void matBundleExistsChanged();
private:
@@ -80,29 +60,22 @@ private:
bool fetchBundleMetadata(const QDir &bundleDir);
bool isValidIndex(int idx) const;
void downloadSharedFiles(const QDir &targetDir, const QStringList &files);
- void createImporter(const QString &bundlePath, const QString &bundleId,
- const QStringList &sharedFiles);
ContentLibraryWidget *m_widget = nullptr;
QString m_searchText;
+ QString m_bundleId;
+ QStringList m_bundleSharedFiles;
QList<ContentLibraryMaterialsCategory *> m_bundleCategories;
- QJsonObject m_matBundleObj;
- Internal::ContentLibraryBundleImporter *m_importer = nullptr;
+ QJsonObject m_bundleObj;
bool m_isEmpty = true;
- bool m_matBundleExists = false;
- bool m_hasModelSelection = false;
- bool m_importerRunning = false;
+ bool m_bundleExists = false;
int m_quick3dMajorVersion = -1;
int m_quick3dMinorVersion = -1;
QString m_downloadPath;
QString m_baseUrl;
-
- QString m_importerBundlePath;
- QString m_importerBundleId;
- QStringList m_importerSharedFiles;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp
index 80dd7e816f..d2c2df2baa 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp
@@ -120,6 +120,11 @@ void ContentLibraryTexture::doSetDownloaded()
m_toolTip = resolveToolTipText();
}
+bool ContentLibraryTexture::visible() const
+{
+ return m_visible;
+}
+
QString ContentLibraryTexture::parentDirPath() const
{
return m_dirPath;
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h
index 8f7197bc72..7f5db6d7d6 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h
@@ -46,6 +46,8 @@ public:
void setHasUpdate(bool value);
bool hasUpdate() const;
+ bool visible() const;
+
signals:
void textureVisibleChanged();
void textureToolTipChanged();
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp
index ed9723a151..8dcf4575f7 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp
@@ -4,7 +4,7 @@
#include "contentlibraryusermodel.h"
#include "contentlibrarybundleimporter.h"
-#include "contentlibraryeffect.h"
+#include "contentlibraryitem.h"
#include "contentlibrarymaterial.h"
#include "contentlibrarymaterialscategory.h"
#include "contentlibrarytexture.h"
@@ -12,18 +12,16 @@
#include <designerpaths.h>
#include <imageutils.h>
+#include <qmldesignerconstants.h>
#include <qmldesignerplugin.h>
+#include <uniquename.h>
#include <utils/algorithm.h>
-#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
-#include <QCoreApplication>
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument>
-#include <QQmlEngine>
-#include <QStandardPaths>
#include <QUrl>
namespace QmlDesigner {
@@ -32,10 +30,7 @@ ContentLibraryUserModel::ContentLibraryUserModel(ContentLibraryWidget *parent)
: QAbstractListModel(parent)
, m_widget(parent)
{
- m_userCategories = {tr("Materials"), tr("Textures")/*, tr("3D"), tr("Effects"), tr("2D components")*/}; // TODO
-
- loadMaterialBundle();
- loadTextureBundle();
+ m_userCategories = {tr("Materials"), tr("Textures"), tr("3D"), /*tr("Effects"), tr("2D components")*/}; // TODO
}
int ContentLibraryUserModel::rowCount(const QModelIndex &) const
@@ -52,18 +47,37 @@ QVariant ContentLibraryUserModel::data(const QModelIndex &index, int role) const
return m_userCategories.at(index.row());
if (role == ItemsRole) {
- if (index.row() == 0)
+ if (index.row() == MaterialsSectionIdx)
return QVariant::fromValue(m_userMaterials);
- if (index.row() == 1)
+ if (index.row() == TexturesSectionIdx)
return QVariant::fromValue(m_userTextures);
- if (index.row() == 2)
+ if (index.row() == Items3DSectionIdx)
return QVariant::fromValue(m_user3DItems);
- if (index.row() == 3)
+ if (index.row() == EffectsSectionIdx)
return QVariant::fromValue(m_userEffects);
}
- if (role == VisibleRole)
- return true; // TODO
+ if (role == NoMatchRole) {
+ if (index.row() == MaterialsSectionIdx)
+ return m_noMatchMaterials;
+ if (index.row() == TexturesSectionIdx)
+ return m_noMatchTextures;
+ if (index.row() == Items3DSectionIdx)
+ return m_noMatch3D;
+ if (index.row() == EffectsSectionIdx)
+ return m_noMatchEffects;
+ }
+
+ if (role == VisibleRole) {
+ if (index.row() == MaterialsSectionIdx)
+ return !m_userMaterials.isEmpty();
+ if (index.row() == TexturesSectionIdx)
+ return !m_userTextures.isEmpty();
+ if (index.row() == Items3DSectionIdx)
+ return !m_user3DItems.isEmpty();
+ if (index.row() == EffectsSectionIdx)
+ return !m_userEffects.isEmpty();
+ }
return {};
}
@@ -73,29 +87,56 @@ bool ContentLibraryUserModel::isValidIndex(int idx) const
return idx > -1 && idx < rowCount();
}
-void ContentLibraryUserModel::updateIsEmpty()
+void ContentLibraryUserModel::updateNoMatchMaterials()
{
- bool anyMatVisible = Utils::anyOf(m_userMaterials, [&](ContentLibraryMaterial *mat) {
- return mat->visible();
+ m_noMatchMaterials = Utils::allOf(m_userMaterials, [&](ContentLibraryMaterial *item) {
+ return !item->visible();
});
+}
- bool newEmpty = !anyMatVisible || !m_widget->hasMaterialLibrary() || !hasRequiredQuick3DImport();
+void ContentLibraryUserModel::updateNoMatchTextures()
+{
+ m_noMatchTextures = Utils::allOf(m_userTextures, [&](ContentLibraryTexture *item) {
+ return !item->visible();
+ });
+}
- if (newEmpty != m_isEmpty) {
- m_isEmpty = newEmpty;
- emit isEmptyChanged();
- }
+void ContentLibraryUserModel::updateNoMatch3D()
+{
+ m_noMatch3D = Utils::allOf(m_user3DItems, [&](ContentLibraryItem *item) {
+ return !item->visible();
+ });
}
void ContentLibraryUserModel::addMaterial(const QString &name, const QString &qml,
const QUrl &icon, const QStringList &files)
{
- auto libMat = new ContentLibraryMaterial(this, name, qml, qmlToModule(qml), icon, files,
- Paths::bundlesPathSetting().append("/User/materials"));
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+
+ QString typePrefix = compUtils.userMaterialsBundleType();
+ TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1();
+ auto libMat = new ContentLibraryMaterial(this, name, qml, type, icon, files,
+ Paths::bundlesPathSetting().append("/User/materials"));
m_userMaterials.append(libMat);
- int matSectionIdx = 0;
- emit dataChanged(index(matSectionIdx), index(matSectionIdx));
+
+ emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
+}
+
+void ContentLibraryUserModel::add3DItem(const QString &name, const QString &qml,
+ const QUrl &icon, const QStringList &files)
+{
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+
+ QString typePrefix = compUtils.user3DBundleType();
+ TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1();
+
+ m_user3DItems.append(new ContentLibraryItem(this, name, qml, type, icon, files));
+}
+
+void ContentLibraryUserModel::refresh3DSection()
+{
+ emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
}
void ContentLibraryUserModel::addTextures(const QStringList &paths)
@@ -117,51 +158,185 @@ void ContentLibraryUserModel::addTextures(const QStringList &paths)
m_userTextures.append(tex);
}
- int texSectionIdx = 1;
- emit dataChanged(index(texSectionIdx), index(texSectionIdx));
+ emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx));
}
-// returns unique library material's name and qml component
-QPair<QString, QString> ContentLibraryUserModel::getUniqueLibMaterialNameAndQml(const QString &matName) const
+void ContentLibraryUserModel::add3DInstance(ContentLibraryItem *bundleItem)
{
- QTC_ASSERT(!m_bundleObj.isEmpty(), return {});
+ QString err = m_widget->importer()->importComponent(m_bundlePath3D.path(), bundleItem->type(),
+ bundleItem->qml(),
+ bundleItem->files() + m_bundle3DSharedFiles);
- const QJsonObject matsObj = m_bundleObj.value("materials").toObject();
- const QStringList matNames = matsObj.keys();
+ if (err.isEmpty())
+ m_widget->setImporterRunning(true);
+ else
+ qWarning() << __FUNCTION__ << err;
+}
+
+void ContentLibraryUserModel::removeTexture(ContentLibraryTexture *tex)
+{
+ // remove resources
+ Utils::FilePath::fromString(tex->texturePath()).removeFile();
+ Utils::FilePath::fromString(tex->iconPath()).removeFile();
+
+ // remove from model
+ m_userTextures.removeOne(tex);
+ tex->deleteLater();
+
+ // update model
+ emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx));
+}
+
+void ContentLibraryUserModel::removeFromContentLib(QObject *item)
+{
+ if (auto mat = qobject_cast<ContentLibraryMaterial *>(item))
+ removeMaterialFromContentLib(mat);
+ else if (auto itm = qobject_cast<ContentLibraryItem *>(item))
+ remove3DFromContentLib(itm);
+}
- QStringList matQmls;
- for (const QString &matName : matNames)
- matQmls.append(matsObj.value(matName).toObject().value("qml").toString().chopped(4)); // remove .qml
+void ContentLibraryUserModel::removeMaterialFromContentLib(ContentLibraryMaterial *item)
+{
+ auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/");
- QString retName = matName.isEmpty() ? "Material" : matName;
- retName = retName.trimmed();
+ QJsonArray itemsArr = m_bundleObjMaterial.value("items").toArray();
- QString retQml = retName;
- retQml.remove(' ');
- if (retQml.at(0).isLower())
- retQml[0] = retQml.at(0).toUpper();
- retQml.prepend("My");
+ // remove qml and icon files
+ Utils::FilePath::fromString(item->qmlFilePath()).removeFile();
+ Utils::FilePath::fromUrl(item->icon()).removeFile();
- int num = 1;
- if (matNames.contains(retName) || matQmls.contains(retQml)) {
- while (matNames.contains(retName + QString::number(num))
- || matQmls.contains(retQml + QString::number(num))) {
- ++num;
+ // remove from the bundle json file
+ for (int i = 0; i < itemsArr.size(); ++i) {
+ if (itemsArr.at(i).toObject().value("qml") == item->qml()) {
+ itemsArr.removeAt(i);
+ break;
}
+ }
+ m_bundleObjMaterial.insert("items", itemsArr);
+
+ auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME)
+ .writeFileContents(QJsonDocument(m_bundleObjMaterial).toJson());
+ if (!result)
+ qWarning() << __FUNCTION__ << result.error();
+
+ // delete dependency files if they are only used by the deleted material
+ QStringList allFiles;
+ for (const QJsonValueConstRef &itemRef : std::as_const(itemsArr))
+ allFiles.append(itemRef.toObject().value("files").toVariant().toStringList());
+
+ const QStringList itemFiles = item->files();
+ for (const QString &file : itemFiles) {
+ if (allFiles.count(file) == 0) // only used by the deleted item
+ bundlePath.pathAppended(file).removeFile();
+ }
+
+ // remove from model
+ m_userMaterials.removeOne(item);
+ item->deleteLater();
- retName += QString::number(num);
- retQml += QString::number(num);
+ // update model
+ emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
+}
+
+void ContentLibraryUserModel::remove3DFromContentLibByName(const QString &qmlFileName)
+{
+ ContentLibraryItem *itemToRemove = Utils::findOr(m_user3DItems, nullptr,
+ [&qmlFileName](ContentLibraryItem *item) {
+ return item->qml() == qmlFileName;
+ });
+
+ if (itemToRemove)
+ remove3DFromContentLib(itemToRemove);
+}
+
+void ContentLibraryUserModel::remove3DFromContentLib(ContentLibraryItem *item)
+{
+ QJsonArray itemsArr = m_bundleObj3D.value("items").toArray();
+
+ // remove qml and icon files
+ m_bundlePath3D.pathAppended(item->qml()).removeFile();
+ Utils::FilePath::fromUrl(item->icon()).removeFile();
+
+ // remove from the bundle json file
+ for (int i = 0; i < itemsArr.size(); ++i) {
+ if (itemsArr.at(i).toObject().value("qml") == item->qml()) {
+ itemsArr.removeAt(i);
+ break;
+ }
+ }
+ m_bundleObj3D.insert("items", itemsArr);
+
+ auto result = m_bundlePath3D.pathAppended(Constants::BUNDLE_JSON_FILENAME)
+ .writeFileContents(QJsonDocument(m_bundleObj3D).toJson());
+ if (!result)
+ qWarning() << __FUNCTION__ << result.error();
+
+ // delete dependency files if they are only used by the deleted item
+ QStringList allFiles;
+ for (const QJsonValueConstRef &itemRef : std::as_const(itemsArr))
+ allFiles.append(itemRef.toObject().value("files").toVariant().toStringList());
+
+ const QStringList itemFiles = item->files();
+ for (const QString &file : itemFiles) {
+ if (allFiles.count(file) == 0) // only used by the deleted item
+ m_bundlePath3D.pathAppended(file).removeFile();
}
- return {retName, retQml + ".qml"};
+ // remove from model
+ m_user3DItems.removeOne(item);
+ item->deleteLater();
+
+ // update model
+ emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
}
-TypeName ContentLibraryUserModel::qmlToModule(const QString &qmlName) const
+/**
+ * @brief Gets unique Qml component and icon file material names from a given name
+ * @param defaultName input name
+ * @return <Qml, icon> file names
+ */
+QPair<QString, QString> ContentLibraryUserModel::getUniqueLibMaterialNames(const QString &defaultName) const
{
- return QLatin1String("%1.%2.%3").arg(QmlDesignerPlugin::instance()->documentManager()
- .generatedComponentUtils().componentBundlesTypePrefix(),
- m_bundleId,
- qmlName.chopped(4)).toLatin1(); // chopped(4): remove .qml
+ return getUniqueLibItemNames(defaultName, m_bundleObjMaterial);
+}
+
+/**
+ * @brief Gets unique Qml component and icon file 3d item names from a given name
+ * @param defaultName input name
+ * @return <Qml, icon> file names
+ */
+QPair<QString, QString> ContentLibraryUserModel::getUniqueLib3DNames(const QString &defaultName) const
+{
+ return getUniqueLibItemNames(defaultName, m_bundleObj3D);
+}
+
+QPair<QString, QString> ContentLibraryUserModel::getUniqueLibItemNames(const QString &defaultName,
+ const QJsonObject &bundleObj) const
+{
+ QTC_ASSERT(!bundleObj.isEmpty(), return {});
+
+ const QJsonArray itemsArr = bundleObj.value("items").toArray();
+
+ QStringList itemQmls, itemIcons;
+ for (const QJsonValueConstRef &itemRef : itemsArr) {
+ const QJsonObject &obj = itemRef.toObject();
+ itemQmls.append(obj.value("qml").toString().chopped(4)); // remove .qml
+ itemIcons.append(QFileInfo(obj.value("icon").toString()).baseName());
+ }
+
+ QString baseQml = UniqueName::generateId(defaultName);
+ baseQml[0] = baseQml.at(0).toUpper();
+ baseQml.prepend("My");
+
+ QString uniqueQml = UniqueName::generate(baseQml, [&] (const QString &name) {
+ return itemQmls.contains(name);
+ });
+
+ QString uniqueIcon = UniqueName::generate(defaultName, [&] (const QString &name) {
+ return itemIcons.contains(name);
+ });
+
+ return {uniqueQml + ".qml", uniqueIcon + ".png"};
}
QHash<int, QByteArray> ContentLibraryUserModel::roleNames() const
@@ -169,121 +344,185 @@ QHash<int, QByteArray> ContentLibraryUserModel::roleNames() const
static const QHash<int, QByteArray> roles {
{NameRole, "categoryName"},
{VisibleRole, "categoryVisible"},
- {ItemsRole, "categoryItems"}
+ {ItemsRole, "categoryItems"},
+ {NoMatchRole, "categoryNoMatch"}
};
return roles;
}
-void ContentLibraryUserModel::createImporter(const QString &bundlePath, const QString &bundleId,
- const QStringList &sharedFiles)
-{
- m_importer = new Internal::ContentLibraryBundleImporter(bundlePath, bundleId, sharedFiles);
-#ifdef QDS_USE_PROJECTSTORAGE
- connect(m_importer,
- &Internal::ContentLibraryBundleImporter::importFinished,
- this,
- [&](const QmlDesigner::TypeName &typeName) {
- m_importerRunning = false;
- emit importerRunningChanged();
- if (typeName.size())
- emit bundleMaterialImported(typeName);
- });
-#else
- connect(m_importer,
- &Internal::ContentLibraryBundleImporter::importFinished,
- this,
- [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
- m_importerRunning = false;
- emit importerRunningChanged();
- if (metaInfo.isValid())
- emit bundleMaterialImported(metaInfo);
- });
-#endif
-
- connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this,
- [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
- Q_UNUSED(metaInfo)
- m_importerRunning = false;
- emit importerRunningChanged();
- emit bundleMaterialUnimported(metaInfo);
- });
+QJsonObject &ContentLibraryUserModel::bundleJsonMaterialObjectRef()
+{
+ return m_bundleObjMaterial;
+}
- resetModel();
- updateIsEmpty();
+QJsonObject &ContentLibraryUserModel::bundleJson3DObjectRef()
+{
+ return m_bundleObj3D;
}
-QJsonObject &ContentLibraryUserModel::bundleJsonObjectRef()
+void ContentLibraryUserModel::loadBundles()
{
- return m_bundleObj;
+ loadMaterialBundle();
+ load3DBundle();
+ loadTextureBundle();
}
void ContentLibraryUserModel::loadMaterialBundle()
{
- if (m_matBundleExists)
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+ if (m_matBundleExists && m_bundleIdMaterial == compUtils.userMaterialsBundleId())
return;
- QDir bundleDir{Paths::bundlesPathSetting() + "/User/materials"};
- bundleDir.mkpath(".");
-
- if (m_bundleObj.isEmpty()) {
- auto jsonFilePath = Utils::FilePath::fromString(bundleDir.filePath("user_materials_bundle.json"));
- if (!jsonFilePath.exists()) {
- QString jsonContent = "{\n";
- jsonContent += " \"id\": \"UserMaterialBundle\",\n";
- jsonContent += " \"materials\": {\n";
- jsonContent += " }\n";
- jsonContent += "}";
- jsonFilePath.writeFileContents(jsonContent.toLatin1());
- }
-
- QFile jsonFile(jsonFilePath.path());
- if (!jsonFile.open(QIODevice::ReadOnly)) {
- qWarning("Couldn't open user_materials_bundle.json");
+ // clean up
+ qDeleteAll(m_userMaterials);
+ m_userMaterials.clear();
+ m_matBundleExists = false;
+ m_noMatchMaterials = true;
+ m_bundleObjMaterial = {};
+ m_bundleIdMaterial.clear();
+
+ m_bundlePathMaterial = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials");
+ m_bundlePathMaterial.ensureWritableDir();
+ m_bundlePathMaterial.pathAppended("icons").ensureWritableDir();
+
+ auto jsonFilePath = m_bundlePathMaterial.pathAppended(Constants::BUNDLE_JSON_FILENAME);
+ if (!jsonFilePath.exists()) {
+ QString jsonContent = "{\n";
+ jsonContent += " \"id\": \"UserMaterials\",\n";
+ jsonContent += " \"items\": []\n";
+ jsonContent += "}";
+ Utils::expected_str<qint64> res = jsonFilePath.writeFileContents(jsonContent.toLatin1());
+ if (!res.has_value()) {
+ qWarning() << __FUNCTION__ << res.error();
+ emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
return;
}
+ }
- QJsonDocument matBundleJsonDoc = QJsonDocument::fromJson(jsonFile.readAll());
- if (matBundleJsonDoc.isNull()) {
- qWarning("Invalid user_materials_bundle.json file");
- return;
- } else {
- m_bundleObj = matBundleJsonDoc.object();
- }
+ Utils::expected_str<QByteArray> jsonContents = jsonFilePath.fileContents();
+ if (!jsonContents.has_value()) {
+ qWarning() << __FUNCTION__ << jsonContents.error();
+ emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
+ return;
}
- m_bundleId = m_bundleObj.value("id").toString();
+ QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(jsonContents.value());
+ if (bundleJsonDoc.isNull()) {
+ qWarning() << __FUNCTION__ << "Invalid json file" << jsonFilePath;
+ emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
+ return;
+ }
- // parse materials
- const QJsonObject matsObj = m_bundleObj.value("materials").toObject();
- const QStringList materialNames = matsObj.keys();
- for (const QString &matName : materialNames) {
- const QJsonObject matObj = matsObj.value(matName).toObject();
+ m_bundleIdMaterial = compUtils.userMaterialsBundleId();
+ m_bundleObjMaterial = bundleJsonDoc.object();
+ m_bundleObjMaterial["id"] = m_bundleIdMaterial;
+ // parse items
+ QString typePrefix = compUtils.userMaterialsBundleType();
+ const QJsonArray itemsArr = m_bundleObjMaterial.value("items").toArray();
+ for (const QJsonValueConstRef &itemRef : itemsArr) {
+ const QJsonObject itemObj = itemRef.toObject();
+
+ QString name = itemObj.value("name").toString();
+ QString qml = itemObj.value("qml").toString();
+ TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1();
+ QUrl icon = m_bundlePathMaterial.pathAppended(itemObj.value("icon").toString()).toUrl();
QStringList files;
- const QJsonArray assetsArr = matObj.value("files").toArray();
- for (const auto /*QJson{Const,}ValueRef*/ &asset : assetsArr)
+ const QJsonArray assetsArr = itemObj.value("files").toArray();
+ for (const QJsonValueConstRef &asset : assetsArr)
files.append(asset.toString());
- QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(matObj.value("icon").toString()));
- QString qml = matObj.value("qml").toString();
+ m_userMaterials.append(new ContentLibraryMaterial(this, name, qml, type, icon, files,
+ m_bundlePathMaterial.path(), ""));
+ }
+
+ m_bundleMaterialSharedFiles.clear();
+ const QJsonArray sharedFilesArr = m_bundleObjMaterial.value("sharedFiles").toArray();
+ for (const QJsonValueConstRef &file : sharedFilesArr)
+ m_bundleMaterialSharedFiles.append(file.toString());
- TypeName type = qmlToModule(qml);
+ m_matBundleExists = true;
+ updateNoMatchMaterials();
+ emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
+}
- auto userMat = new ContentLibraryMaterial(this, matName, qml, type, icon, files,
- bundleDir.path(), "");
+void ContentLibraryUserModel::load3DBundle()
+{
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
- m_userMaterials.append(userMat);
+ if (m_bundle3DExists && m_bundleId3D == compUtils.user3DBundleId())
+ return;
+
+ // clean up
+ qDeleteAll(m_user3DItems);
+ m_user3DItems.clear();
+ m_bundle3DExists = false;
+ m_noMatch3D = true;
+ m_bundleObj3D = {};
+ m_bundleId3D.clear();
+
+ m_bundlePath3D = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d");
+ m_bundlePath3D.ensureWritableDir();
+ m_bundlePath3D.pathAppended("icons").ensureWritableDir();
+
+ auto jsonFilePath = m_bundlePath3D.pathAppended(Constants::BUNDLE_JSON_FILENAME);
+ if (!jsonFilePath.exists()) {
+ QByteArray jsonContent = "{\n";
+ jsonContent += " \"id\": \"User3D\",\n";
+ jsonContent += " \"items\": []\n";
+ jsonContent += "}";
+ Utils::expected_str<qint64> res = jsonFilePath.writeFileContents(jsonContent);
+ if (!res.has_value()) {
+ qWarning() << __FUNCTION__ << res.error();
+ emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
+ return;
+ }
}
- QStringList sharedFiles;
- const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray();
- for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr)
- sharedFiles.append(file.toString());
+ Utils::expected_str<QByteArray> jsonContents = jsonFilePath.fileContents();
+ if (!jsonContents.has_value()) {
+ qWarning() << __FUNCTION__ << jsonContents.error();
+ emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
+ return;
+ }
- createImporter(bundleDir.path(), m_bundleId, sharedFiles);
+ QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(jsonContents.value());
+ if (bundleJsonDoc.isNull()) {
+ qWarning() << __FUNCTION__ << "Invalid json file" << jsonFilePath;
+ emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
+ return;
+ }
- m_matBundleExists = true;
- emit matBundleExistsChanged();
+ m_bundleId3D = compUtils.user3DBundleId();
+ m_bundleObj3D = bundleJsonDoc.object();
+ m_bundleObj3D["id"] = m_bundleId3D;
+
+ // parse items
+ QString typePrefix = compUtils.user3DBundleType();
+ const QJsonArray itemsArr = m_bundleObj3D.value("items").toArray();
+ for (const QJsonValueConstRef &itemRef : itemsArr) {
+ const QJsonObject itemObj = itemRef.toObject();
+
+ QString name = itemObj.value("name").toString();
+ QString qml = itemObj.value("qml").toString();
+ TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1();
+ QUrl icon = m_bundlePath3D.pathAppended(itemObj.value("icon").toString()).toUrl();
+ QStringList files;
+ const QJsonArray assetsArr = itemObj.value("files").toArray();
+ for (const QJsonValueConstRef &asset : assetsArr)
+ files.append(asset.toString());
+
+ m_user3DItems.append(new ContentLibraryItem(nullptr, name, qml, type, icon, files));
+ }
+
+ m_bundle3DSharedFiles.clear();
+ const QJsonArray sharedFilesArr = m_bundleObj3D.value("sharedFiles").toArray();
+ for (const QJsonValueConstRef &file : sharedFilesArr)
+ m_bundle3DSharedFiles.append(file.toString());
+
+ m_bundle3DExists = true;
+ updateNoMatch3D();
+ emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
}
void ContentLibraryUserModel::loadTextureBundle()
@@ -308,8 +547,8 @@ void ContentLibraryUserModel::loadTextureBundle()
m_userTextures.append(tex);
}
- int texSectionIdx = 1;
- emit dataChanged(index(texSectionIdx), index(texSectionIdx));
+ updateNoMatchTextures();
+ emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx));
}
bool ContentLibraryUserModel::hasRequiredQuick3DImport() const
@@ -322,11 +561,6 @@ bool ContentLibraryUserModel::matBundleExists() const
return m_matBundleExists;
}
-Internal::ContentLibraryBundleImporter *ContentLibraryUserModel::bundleImporter() const
-{
- return m_importer;
-}
-
void ContentLibraryUserModel::setSearchText(const QString &searchText)
{
QString lowerSearchText = searchText.toLower();
@@ -336,21 +570,39 @@ void ContentLibraryUserModel::setSearchText(const QString &searchText)
m_searchText = lowerSearchText;
- for (ContentLibraryMaterial *mat : std::as_const(m_userMaterials))
- mat->filter(m_searchText);
+ for (ContentLibraryMaterial *item : std::as_const(m_userMaterials))
+ item->filter(m_searchText);
+
+ for (ContentLibraryTexture *item : std::as_const(m_userTextures))
+ item->filter(m_searchText);
- updateIsEmpty();
+ for (ContentLibraryItem *item : std::as_const(m_user3DItems))
+ item->filter(m_searchText);
+
+ updateNoMatchMaterials();
+ updateNoMatchTextures();
+ updateNoMatch3D();
+ resetModel();
}
-void ContentLibraryUserModel::updateImportedState(const QStringList &importedMats)
+void ContentLibraryUserModel::updateMaterialsImportedState(const QStringList &importedItems)
{
bool changed = false;
-
for (ContentLibraryMaterial *mat : std::as_const(m_userMaterials))
- changed |= mat->setImported(importedMats.contains(mat->qml().chopped(4)));
+ changed |= mat->setImported(importedItems.contains(mat->qml().chopped(4)));
+
+ if (changed)
+ emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
+}
+
+void ContentLibraryUserModel::update3DImportedState(const QStringList &importedItems)
+{
+ bool changed = false;
+ for (ContentLibraryItem *item : std::as_const(m_user3DItems))
+ changed |= item->setImported(importedItems.contains(item->qml().chopped(4)));
if (changed)
- resetModel();
+ emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
}
void ContentLibraryUserModel::setQuick3DImportVersion(int major, int minor)
@@ -366,8 +618,6 @@ void ContentLibraryUserModel::setQuick3DImportVersion(int major, int minor)
return;
emit hasRequiredQuick3DImportChanged();
-
- updateIsEmpty();
}
void ContentLibraryUserModel::resetModel()
@@ -381,44 +631,56 @@ void ContentLibraryUserModel::applyToSelected(ContentLibraryMaterial *mat, bool
emit applyToSelectedTriggered(mat, add);
}
-void ContentLibraryUserModel::addToProject(ContentLibraryMaterial *mat)
+void ContentLibraryUserModel::addToProject(QObject *item)
{
- QString err = m_importer->importComponent(mat->qml(), mat->files());
-
- if (err.isEmpty()) {
- m_importerRunning = true;
- emit importerRunningChanged();
+ QString bundleDir;
+ TypeName type;
+ QString qmlFile;
+ QStringList files;
+ if (auto mat = qobject_cast<ContentLibraryMaterial *>(item)) {
+ bundleDir = mat->dirPath();
+ type = mat->type();
+ qmlFile = mat->qml();
+ files = mat->files() + m_bundleMaterialSharedFiles;
+ } else if (auto itm = qobject_cast<ContentLibraryItem *>(item)) {
+ bundleDir = m_bundlePath3D.toString();
+ type = itm->type();
+ qmlFile = itm->qml();
+ files = itm->files() + m_bundle3DSharedFiles;
} else {
- qWarning() << __FUNCTION__ << err;
+ qWarning() << __FUNCTION__ << "Unsupported Item";
+ return;
}
-}
-
-void ContentLibraryUserModel::removeFromProject(ContentLibraryMaterial *mat)
-{
- emit bundleMaterialAboutToUnimport(mat->type());
- QString err = m_importer->unimportComponent(mat->qml());
+ QString err = m_widget->importer()->importComponent(bundleDir, type, qmlFile, files);
- if (err.isEmpty()) {
- m_importerRunning = true;
- emit importerRunningChanged();
- } else {
+ if (err.isEmpty())
+ m_widget->setImporterRunning(true);
+ else
qWarning() << __FUNCTION__ << err;
- }
-}
-
-bool ContentLibraryUserModel::hasModelSelection() const
-{
- return m_hasModelSelection;
}
-void ContentLibraryUserModel::setHasModelSelection(bool b)
+void ContentLibraryUserModel::removeFromProject(QObject *item)
{
- if (b == m_hasModelSelection)
+ TypeName type;
+ QString qml;
+ if (auto mat = qobject_cast<ContentLibraryMaterial *>(item)) {
+ type = mat->type();
+ qml = mat->qml();
+ } else if (auto itm = qobject_cast<ContentLibraryItem *>(item)) {
+ type = itm->type();
+ qml = itm->qml();
+ } else {
+ qWarning() << __FUNCTION__ << "Unsupported Item";
return;
+ }
- m_hasModelSelection = b;
- emit hasModelSelectionChanged();
+ QString err = m_widget->importer()->unimportComponent(type, qml);
+
+ if (err.isEmpty())
+ m_widget->setImporterRunning(true);
+ else
+ qWarning() << __FUNCTION__ << err;
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h
index 3e9a96fd9d..2a7f9a66f3 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h
@@ -3,7 +3,7 @@
#pragma once
-#include "modelfwd.h"
+#include <utils/filepath.h>
#include <QAbstractListModel>
#include <QJsonObject>
@@ -12,29 +12,23 @@ QT_FORWARD_DECLARE_CLASS(QUrl)
namespace QmlDesigner {
-class ContentLibraryEffect;
+class ContentLibraryItem;
class ContentLibraryMaterial;
class ContentLibraryTexture;
class ContentLibraryWidget;
class NodeMetaInfo;
-namespace Internal {
-class ContentLibraryBundleImporter;
-}
-
class ContentLibraryUserModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(bool matBundleExists READ matBundleExists NOTIFY matBundleExistsChanged)
- Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
+ Q_PROPERTY(bool bundle3DExists MEMBER m_bundle3DExists NOTIFY bundle3DExistsChanged)
Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged)
- Q_PROPERTY(bool hasModelSelection READ hasModelSelection NOTIFY hasModelSelectionChanged)
- Q_PROPERTY(bool importerRunning MEMBER m_importerRunning NOTIFY importerRunningChanged)
Q_PROPERTY(QList<ContentLibraryMaterial *> userMaterials MEMBER m_userMaterials NOTIFY userMaterialsChanged)
Q_PROPERTY(QList<ContentLibraryTexture *> userTextures MEMBER m_userTextures NOTIFY userTexturesChanged)
- Q_PROPERTY(QList<ContentLibraryEffect *> user3DItems MEMBER m_user3DItems NOTIFY user3DItemsChanged)
- Q_PROPERTY(QList<ContentLibraryEffect *> userEffects MEMBER m_userEffects NOTIFY userEffectsChanged)
+ Q_PROPERTY(QList<ContentLibraryItem *> user3DItems MEMBER m_user3DItems NOTIFY user3DItemsChanged)
+ Q_PROPERTY(QList<ContentLibraryItem *> userEffects MEMBER m_userEffects NOTIFY userEffectsChanged)
public:
ContentLibraryUserModel(ContentLibraryWidget *parent = nullptr);
@@ -44,10 +38,11 @@ public:
QHash<int, QByteArray> roleNames() const override;
void setSearchText(const QString &searchText);
- void updateImportedState(const QStringList &importedMats);
+ void updateMaterialsImportedState(const QStringList &importedItems);
+ void update3DImportedState(const QStringList &importedItems);
- QPair<QString, QString> getUniqueLibMaterialNameAndQml(const QString &matName) const;
- TypeName qmlToModule(const QString &qmlName) const;
+ QPair<QString, QString> getUniqueLibMaterialNames(const QString &defaultName = "Material") const;
+ QPair<QString, QString> getUniqueLib3DNames(const QString &defaultName = "Item") const;
void setQuick3DImportVersion(int major, int minor);
@@ -55,78 +50,86 @@ public:
bool matBundleExists() const;
- bool hasModelSelection() const;
- void setHasModelSelection(bool b);
-
void resetModel();
- void updateIsEmpty();
+ void updateNoMatchMaterials();
+ void updateNoMatchTextures();
+ void updateNoMatch3D();
void addMaterial(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files);
+ void add3DItem(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files);
+ void refresh3DSection();
void addTextures(const QStringList &paths);
+ void add3DInstance(ContentLibraryItem *bundleItem);
+
+ void remove3DFromContentLibByName(const QString &qmlFileName);
+
void setBundleObj(const QJsonObject &newBundleObj);
- QJsonObject &bundleJsonObjectRef();
+ QJsonObject &bundleJsonMaterialObjectRef();
+ QJsonObject &bundleJson3DObjectRef();
- Internal::ContentLibraryBundleImporter *bundleImporter() const;
+ void loadBundles();
Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false);
- Q_INVOKABLE void addToProject(QmlDesigner::ContentLibraryMaterial *mat);
- Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryMaterial *mat);
+ Q_INVOKABLE void addToProject(QObject *item);
+ Q_INVOKABLE void removeFromProject(QObject *item);
+ Q_INVOKABLE void removeTexture(QmlDesigner::ContentLibraryTexture *tex);
+ Q_INVOKABLE void removeFromContentLib(QObject *item);
signals:
- void isEmptyChanged();
void hasRequiredQuick3DImportChanged();
- void hasModelSelectionChanged();
void userMaterialsChanged();
void userTexturesChanged();
void user3DItemsChanged();
void userEffectsChanged();
-
void applyToSelectedTriggered(QmlDesigner::ContentLibraryMaterial *mat, bool add = false);
-
-#ifdef QDS_USE_PROJECTSTORAGE
- void bundleMaterialImported(const QmlDesigner::TypeName &typeName);
-#else
- void bundleMaterialImported(const QmlDesigner::NodeMetaInfo &metaInfo);
-#endif
- void bundleMaterialAboutToUnimport(const QmlDesigner::TypeName &type);
- void bundleMaterialUnimported(const QmlDesigner::NodeMetaInfo &metaInfo);
- void importerRunningChanged();
void matBundleExistsChanged();
+ void bundle3DExistsChanged();
private:
+ enum SectionIndex { MaterialsSectionIdx = 0,
+ TexturesSectionIdx,
+ Items3DSectionIdx,
+ EffectsSectionIdx };
+
void loadMaterialBundle();
+ void load3DBundle();
void loadTextureBundle();
bool isValidIndex(int idx) const;
- void createImporter(const QString &bundlePath, const QString &bundleId,
- const QStringList &sharedFiles);
+ void removeMaterialFromContentLib(ContentLibraryMaterial *mat);
+ void remove3DFromContentLib(ContentLibraryItem *item);
+ QPair<QString, QString> getUniqueLibItemNames(const QString &defaultName,
+ const QJsonObject &bundleObj) const;
ContentLibraryWidget *m_widget = nullptr;
QString m_searchText;
- QString m_bundleId;
+ QString m_bundleIdMaterial;
+ QString m_bundleId3D;
+ QStringList m_bundleMaterialSharedFiles;
+ QStringList m_bundle3DSharedFiles;
+ Utils::FilePath m_bundlePathMaterial;
+ Utils::FilePath m_bundlePath3D;
QList<ContentLibraryMaterial *> m_userMaterials;
QList<ContentLibraryTexture *> m_userTextures;
- QList<ContentLibraryEffect *> m_userEffects;
- QList<ContentLibraryEffect *> m_user3DItems;
+ QList<ContentLibraryItem *> m_userEffects;
+ QList<ContentLibraryItem *> m_user3DItems;
QStringList m_userCategories;
- QJsonObject m_bundleObj;
- Internal::ContentLibraryBundleImporter *m_importer = nullptr;
+ QJsonObject m_bundleObjMaterial;
+ QJsonObject m_bundleObj3D;
- bool m_isEmpty = true;
+ bool m_noMatchMaterials = true;
+ bool m_noMatchTextures = true;
+ bool m_noMatch3D = true;
+ bool m_noMatchEffects = true;
bool m_matBundleExists = false;
- bool m_hasModelSelection = false;
- bool m_importerRunning = false;
+ bool m_bundle3DExists = false;
int m_quick3dMajorVersion = -1;
int m_quick3dMinorVersion = -1;
- QString m_importerBundlePath;
- QString m_importerBundleId;
- QStringList m_importerSharedFiles;
-
- enum Roles { NameRole = Qt::UserRole + 1, VisibleRole, ItemsRole };
+ enum Roles { NameRole = Qt::UserRole + 1, VisibleRole, ItemsRole, NoMatchRole };
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp
index dd8a4d9919..9258faaf33 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp
@@ -3,10 +3,8 @@
#include "contentlibraryview.h"
-#include "asset.h"
-#include "bindingproperty.h"
#include "contentlibrarybundleimporter.h"
-#include "contentlibraryeffect.h"
+#include "contentlibraryitem.h"
#include "contentlibraryeffectsmodel.h"
#include "contentlibrarymaterial.h"
#include "contentlibrarymaterialsmodel.h"
@@ -14,18 +12,21 @@
#include "contentlibrarytexturesmodel.h"
#include "contentlibraryusermodel.h"
#include "contentlibrarywidget.h"
-#include "documentmanager.h"
-#include "externaldependenciesinterface.h"
-#include "nodelistproperty.h"
-#include "qmldesignerconstants.h"
-#include "qmlobjectnode.h"
-#include "variantproperty.h"
-#include "utils3d.h"
+#include <asset.h>
+#include <bindingproperty.h>
#include <designerpaths.h>
-
-#include <coreplugin/messagebox.h>
+#include <documentmanager.h>
#include <enumeration.h>
+#include <externaldependenciesinterface.h>
+#include <nodelistproperty.h>
+#include <qmldesignerconstants.h>
+#include <qmldesignerplugin.h>
+#include <qmlobjectnode.h>
+#include <uniquename.h>
+#include <utils3d.h>
+#include <variantproperty.h>
+
#include <utils/algorithm.h>
#ifndef QMLDESIGNER_TEST
@@ -39,13 +40,16 @@
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
+#include <QMessageBox>
#include <QPixmap>
#include <QVector3D>
namespace QmlDesigner {
-ContentLibraryView::ContentLibraryView(ExternalDependenciesInterface &externalDependencies)
+ContentLibraryView::ContentLibraryView(AsynchronousImageCache &imageCache,
+ ExternalDependenciesInterface &externalDependencies)
: AbstractView(externalDependencies)
+ , m_imageCache(imageCache)
, m_createTexture(this)
{}
@@ -70,9 +74,9 @@ WidgetInfo ContentLibraryView::widgetInfo()
[&] (QmlDesigner::ContentLibraryTexture *tex) {
m_draggedBundleTexture = tex;
});
- connect(m_widget, &ContentLibraryWidget::bundleEffectDragStarted, this,
- [&] (QmlDesigner::ContentLibraryEffect *eff) {
- m_draggedBundleEffect = eff;
+ connect(m_widget, &ContentLibraryWidget::bundleItemDragStarted, this,
+ [&] (QmlDesigner::ContentLibraryItem *item) {
+ m_draggedBundleItem = item;
});
connect(m_widget, &ContentLibraryWidget::addTextureRequested, this,
@@ -89,198 +93,150 @@ WidgetInfo ContentLibraryView::widgetInfo()
m_widget->environmentsModel()->setHasSceneEnv(sceneEnvExists);
});
- ContentLibraryMaterialsModel *materialsModel = m_widget->materialsModel().data();
-
- connect(materialsModel,
+ connect(m_widget->materialsModel(),
&ContentLibraryMaterialsModel::applyToSelectedTriggered,
this,
[&](ContentLibraryMaterial *bundleMat, bool add) {
- if (m_selectedModels.isEmpty())
- return;
+ if (m_selectedModels.isEmpty())
+ return;
- m_bundleMaterialTargets = m_selectedModels;
- m_bundleMaterialAddToSelected = add;
+ m_bundleMaterialTargets = m_selectedModels;
+ m_bundleMaterialAddToSelected = add;
- ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type());
- if (defaultMat.isValid())
- applyBundleMaterialToDropTarget(defaultMat);
- else
- m_widget->materialsModel()->addToProject(bundleMat);
- });
+ ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type());
+ if (defaultMat.isValid())
+ applyBundleMaterialToDropTarget(defaultMat);
+ else
+ m_widget->materialsModel()->addToProject(bundleMat);
+ });
-#ifdef QDS_USE_PROJECTSTORAGE
- connect(materialsModel,
- &ContentLibraryMaterialsModel::bundleMaterialImported,
- this,
- [&](const QmlDesigner::TypeName &typeName) {
- applyBundleMaterialToDropTarget({}, typeName);
- updateBundleMaterialsImportedState();
- });
-#else
- connect(materialsModel,
- &ContentLibraryMaterialsModel::bundleMaterialImported,
+ connect(m_widget->userModel(),
+ &ContentLibraryUserModel::applyToSelectedTriggered,
this,
- [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
- applyBundleMaterialToDropTarget({}, metaInfo);
- updateBundleMaterialsImportedState();
- });
-#endif
+ [&](ContentLibraryMaterial *bundleMat, bool add) {
+ if (m_selectedModels.isEmpty())
+ return;
- connect(materialsModel, &ContentLibraryMaterialsModel::bundleMaterialAboutToUnimport, this,
- [&] (const QmlDesigner::TypeName &type) {
- // delete instances of the bundle material that is about to be unimported
- executeInTransaction("ContentLibraryView::widgetInfo", [&] {
- ModelNode matLib = Utils3D::materialLibraryNode(this);
- if (!matLib.isValid())
- return;
+ m_bundleMaterialTargets = m_selectedModels;
+ m_bundleMaterialAddToSelected = add;
- Utils::reverseForeach(matLib.directSubModelNodes(), [&](const ModelNode &mat) {
- if (mat.isValid() && mat.type() == type)
- QmlObjectNode(mat).destroy();
- });
- });
+ ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type());
+ if (defaultMat.isValid())
+ applyBundleMaterialToDropTarget(defaultMat);
+ else
+ m_widget->userModel()->addToProject(bundleMat);
});
- connect(materialsModel, &ContentLibraryMaterialsModel::bundleMaterialUnimported, this,
- &ContentLibraryView::updateBundleMaterialsImportedState);
+ connectImporter();
+ }
- ContentLibraryEffectsModel *effectsModel = m_widget->effectsModel().data();
+ return createWidgetInfo(m_widget.data(),
+ "ContentLibrary",
+ WidgetInfo::LeftPane,
+ 0,
+ tr("Content Library"));
+}
+void ContentLibraryView::connectImporter()
+{
#ifdef QDS_USE_PROJECTSTORAGE
- connect(effectsModel,
- &ContentLibraryEffectsModel::bundleItemImported,
- this,
- [&](const QmlDesigner::TypeName &typeName) {
- QTC_ASSERT(typeName.size(), return);
-
- if (!m_bundleEffectTarget)
- m_bundleEffectTarget = Utils3D::active3DSceneNode(this);
+ connect(m_widget->importer(),
+ &ContentLibraryBundleImporter::importFinished,
+ this,
+ [&](const QmlDesigner::TypeName &typeName, const QString &bundleId) {
+ QTC_ASSERT(typeName.size(), return);
+ if (isMaterialBundle(bundleId)) {
+ applyBundleMaterialToDropTarget({}, typeName);
+ } else if (isItemBundle(bundleId)) {
+ if (!m_bundleItemTarget)
+ m_bundleItemTarget = Utils3D::active3DSceneNode(this);
- QTC_ASSERT(m_bundleEffectTarget, return);
+ QTC_ASSERT(m_bundleItemTarget, return);
executeInTransaction("ContentLibraryView::widgetInfo", [&] {
- QVector3D pos = m_bundleEffectPos.value<QVector3D>();
- ModelNode newEffNode = createModelNode(
+ QVector3D pos = m_bundleItemPos.value<QVector3D>();
+ ModelNode newNode = createModelNode(
typeName, -1, -1, {{"x", pos.x()}, {"y", pos.y()}, {"z", pos.z()}});
- m_bundleEffectTarget.defaultNodeListProperty().reparentHere(newEffNode);
+ m_bundleItemTarget.defaultNodeListProperty().reparentHere(newNode);
clearSelectedModelNodes();
- selectModelNode(newEffNode);
+ selectModelNode(newNode);
});
- updateBundleEffectsImportedState();
- m_bundleEffectTarget = {};
- m_bundleEffectPos = {};
- });
+ m_bundleItemTarget = {};
+ m_bundleItemPos = {};
+ }
+ });
#else
- connect(effectsModel,
- &ContentLibraryEffectsModel::bundleItemImported,
- this,
- [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
- QTC_ASSERT(metaInfo.isValid(), return);
-
- if (!m_bundleEffectTarget)
- m_bundleEffectTarget = Utils3D::active3DSceneNode(this);
+ connect(m_widget->importer(),
+ &ContentLibraryBundleImporter::importFinished,
+ this,
+ [&](const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId) {
+ QTC_ASSERT(metaInfo.isValid(), return);
+ if (isMaterialBundle(bundleId)) {
+ applyBundleMaterialToDropTarget({}, metaInfo);
+ } else if (isItemBundle(bundleId)) {
+ if (!m_bundleItemTarget)
+ m_bundleItemTarget = Utils3D::active3DSceneNode(this);
- QTC_ASSERT(m_bundleEffectTarget, return);
+ QTC_ASSERT(m_bundleItemTarget, return);
- executeInTransaction("ContentLibraryView::widgetInfo", [&] {
- QVector3D pos = m_bundleEffectPos.value<QVector3D>();
- ModelNode newEffNode = createModelNode(metaInfo.typeName(),
+ executeInTransaction("ContentLibraryView::connectImporter", [&] {
+ QVector3D pos = m_bundleItemPos.value<QVector3D>();
+ ModelNode newNode = createModelNode(metaInfo.typeName(),
metaInfo.majorVersion(),
metaInfo.minorVersion(),
{{"x", pos.x()},
{"y", pos.y()},
{"z", pos.z()}});
- m_bundleEffectTarget.defaultNodeListProperty().reparentHere(newEffNode);
+ m_bundleItemTarget.defaultNodeListProperty().reparentHere(newNode);
clearSelectedModelNodes();
- selectModelNode(newEffNode);
+ selectModelNode(newNode);
});
- updateBundleEffectsImportedState();
- m_bundleEffectTarget = {};
- m_bundleEffectPos = {};
- });
+ m_bundleItemTarget = {};
+ m_bundleItemPos = {};
+ }
+ });
#endif
- connect(effectsModel, &ContentLibraryEffectsModel::bundleItemAboutToUnimport, this,
- [&] (const QmlDesigner::TypeName &type) {
- // delete instances of the bundle effect that is about to be unimported
- executeInTransaction("ContentLibraryView::widgetInfo", [&] {
- NodeMetaInfo metaInfo = model()->metaInfo(type);
- QList<ModelNode> effects = allModelNodesOfType(metaInfo);
- for (ModelNode &eff : effects)
- eff.destroy();
- });
- });
-
- connect(effectsModel, &ContentLibraryEffectsModel::bundleItemUnimported, this,
- &ContentLibraryView::updateBundleEffectsImportedState);
-
- connectUserBundle();
- }
-
- return createWidgetInfo(m_widget.data(),
- "ContentLibrary",
- WidgetInfo::LeftPane,
- 0,
- tr("Content Library"));
-}
-
-void ContentLibraryView::connectUserBundle()
-{
- ContentLibraryUserModel *userModel = m_widget->userModel().data();
- connect(userModel,
- &ContentLibraryUserModel::applyToSelectedTriggered,
- this,
- [&](ContentLibraryMaterial *bundleMat, bool add) {
- if (m_selectedModels.isEmpty())
+ connect(m_widget->importer(), &ContentLibraryBundleImporter::aboutToUnimport, this,
+ [&] (const QmlDesigner::TypeName &type, const QString &bundleId) {
+ if (isMaterialBundle(bundleId)) {
+ // delete instances of the bundle material that is about to be unimported
+ executeInTransaction("ContentLibraryView::connectImporter", [&] {
+ ModelNode matLib = Utils3D::materialLibraryNode(this);
+ if (!matLib.isValid())
return;
- m_bundleMaterialTargets = m_selectedModels;
- m_bundleMaterialAddToSelected = add;
-
- ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type());
- if (defaultMat.isValid())
- applyBundleMaterialToDropTarget(defaultMat);
- else
- m_widget->userModel()->addToProject(bundleMat);
- });
-
-#ifdef QDS_USE_PROJECTSTORAGE
- connect(userModel,
- &ContentLibraryUserModel::bundleMaterialImported,
- this,
- [&](const QmlDesigner::TypeName &typeName) {
- applyBundleMaterialToDropTarget({}, typeName);
- updateBundleUserMaterialsImportedState();
+ Utils::reverseForeach(matLib.directSubModelNodes(), [&](const ModelNode &mat) {
+ if (mat.isValid() && mat.type() == type)
+ QmlObjectNode(mat).destroy();
+ });
});
-#else
- connect(userModel,
- &ContentLibraryUserModel::bundleMaterialImported,
- this,
- [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
- applyBundleMaterialToDropTarget({}, metaInfo);
- updateBundleUserMaterialsImportedState();
+ } else if (isItemBundle(bundleId)) {
+ // delete instances of the bundle item that is about to be unimported
+ executeInTransaction("ContentLibraryView::connectImporter", [&] {
+ NodeMetaInfo metaInfo = model()->metaInfo(type);
+ QList<ModelNode> nodes = allModelNodesOfType(metaInfo);
+ for (ModelNode &node : nodes)
+ node.destroy();
});
-#endif
+ }
+ });
+}
- connect(userModel, &ContentLibraryUserModel::bundleMaterialAboutToUnimport, this,
- [&] (const QmlDesigner::TypeName &type) {
- // delete instances of the bundle material that is about to be unimported
- executeInTransaction("ContentLibraryView::connectUserModel", [&] {
- ModelNode matLib = Utils3D::materialLibraryNode(this);
- if (!matLib.isValid())
- return;
-
- Utils::reverseForeach(matLib.directSubModelNodes(), [&](const ModelNode &mat) {
- if (mat.isValid() && mat.type() == type)
- QmlObjectNode(mat).destroy();
- });
- });
- });
+bool ContentLibraryView::isMaterialBundle(const QString &bundleId) const
+{
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+ return bundleId == compUtils.materialsBundleId() || bundleId == compUtils.userMaterialsBundleId();
+}
- connect(userModel, &ContentLibraryUserModel::bundleMaterialUnimported, this,
- &ContentLibraryView::updateBundleUserMaterialsImportedState);
+// item bundle includes effects and 3D components
+bool ContentLibraryView::isItemBundle(const QString &bundleId) const
+{
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+ return bundleId == compUtils.effectsBundleId() || bundleId == compUtils.userEffectsBundleId()
+ || bundleId == compUtils.user3DBundleId();
}
void ContentLibraryView::modelAttached(Model *model)
@@ -290,7 +246,6 @@ void ContentLibraryView::modelAttached(Model *model)
m_hasQuick3DImport = model->hasImport("QtQuick3D");
updateBundlesQuick3DVersion();
- updateBundleMaterialsImportedState();
const bool hasLibrary = Utils3D::materialLibraryNode(this).isValid();
m_widget->setHasMaterialLibrary(hasLibrary);
@@ -302,8 +257,17 @@ void ContentLibraryView::modelAttached(Model *model)
m_widget->setHasActive3DScene(m_sceneId != -1);
m_widget->clearSearchFilter();
+ // bundles loading has to happen here, otherwise project path is not ready which will
+ // cause bundle items types to resolve incorrectly
+ m_widget->materialsModel()->loadBundle();
m_widget->effectsModel()->loadBundle();
- updateBundleEffectsImportedState();
+ m_widget->userModel()->loadBundles();
+
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+ m_widget->updateImportedState(compUtils.materialsBundleId());
+ m_widget->updateImportedState(compUtils.effectsBundleId());
+ m_widget->updateImportedState(compUtils.userMaterialsBundleId());
+ m_widget->updateImportedState(compUtils.user3DBundleId());
}
void ContentLibraryView::modelAboutToBeDetached(Model *model)
@@ -345,8 +309,7 @@ void ContentLibraryView::selectedNodesChanged(const QList<ModelNode> &selectedNo
return node.metaInfo().isQtQuick3DModel();
});
- m_widget->materialsModel()->setHasModelSelection(!m_selectedModels.isEmpty());
- m_widget->userModel()->setHasModelSelection(!m_selectedModels.isEmpty());
+ m_widget->setHasModelSelection(!m_selectedModels.isEmpty());
}
void ContentLibraryView::customNotification(const AbstractView *view,
@@ -387,18 +350,29 @@ void ContentLibraryView::customNotification(const AbstractView *view,
m_widget->addTexture(m_draggedBundleTexture);
m_draggedBundleTexture = nullptr;
- } else if (identifier == "drop_bundle_effect") {
+ } else if (identifier == "drop_bundle_item") {
QTC_ASSERT(nodeList.size() == 1, return);
- m_bundleEffectPos = data.size() == 1 ? data.first() : QVariant();
- m_widget->effectsModel()->addInstance(m_draggedBundleEffect);
- m_bundleEffectTarget = nodeList.first() ? nodeList.first() : Utils3D::active3DSceneNode(this);
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+ bool is3D = m_draggedBundleItem->type().startsWith(compUtils.user3DBundleType().toLatin1());
+
+ m_bundleItemPos = data.size() == 1 ? data.first() : QVariant();
+ if (is3D)
+ m_widget->userModel()->add3DInstance(m_draggedBundleItem);
+ else
+ m_widget->effectsModel()->addInstance(m_draggedBundleItem);
+ m_bundleItemTarget = nodeList.first() ? nodeList.first() : Utils3D::active3DSceneNode(this);
} else if (identifier == "add_material_to_content_lib") {
QTC_ASSERT(nodeList.size() == 1 && data.size() == 1, return);
addLibMaterial(nodeList.first(), data.first().value<QPixmap>());
} else if (identifier == "add_assets_to_content_lib") {
addLibAssets(data.first().toStringList());
+ } else if (identifier == "add_3d_to_content_lib") {
+ if (nodeList.first().isComponent())
+ addLib3DComponent(nodeList.first());
+ else
+ addLib3DItem(nodeList.first());
}
}
@@ -528,58 +502,49 @@ void ContentLibraryView::applyBundleMaterialToDropTarget(const ModelNode &bundle
#endif
// Add a project material to Content Library's user tab
-void ContentLibraryView::addLibMaterial(const ModelNode &mat, const QPixmap &icon)
+void ContentLibraryView::addLibMaterial(const ModelNode &node, const QPixmap &iconPixmap)
{
auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/");
- auto [name, qml] = m_widget->userModel()->getUniqueLibMaterialNameAndQml(
- mat.variantProperty("objectName").value().toString());
+ QString name = node.variantProperty("objectName").value().toString();
+ auto [qml, icon] = m_widget->userModel()->getUniqueLibMaterialNames(node.id());
- bundlePath.pathAppended("icons").createDir();
- bundlePath.pathAppended("images").createDir();
- bundlePath.pathAppended("shaders").createDir();
-
- QString iconPath = QLatin1String("icons/%1.png").arg(mat.id());
+ QString iconPath = QLatin1String("icons/%1").arg(icon);
QString fullIconPath = bundlePath.pathAppended(iconPath).toString();
// save icon
- bool iconSaved = icon.save(fullIconPath);
+ bool iconSaved = iconPixmap.save(fullIconPath);
if (!iconSaved)
qWarning() << __FUNCTION__ << "icon save failed";
// generate and save material Qml file
- const QStringList depAssets = writeLibMaterialQml(mat, qml);
+ const QStringList depAssets = writeLibItemQml(node, qml);
// add the material to the bundle json
- QJsonObject &jsonRef = m_widget->userModel()->bundleJsonObjectRef();
- QJsonObject matsObj = jsonRef.value("materials").toObject();
- QJsonObject matObj;
- matObj.insert("qml", qml);
- matObj.insert("icon", iconPath);
+ QJsonObject &jsonRef = m_widget->userModel()->bundleJsonMaterialObjectRef();
+ QJsonArray itemsArr = jsonRef.value("items").toArray();
+ QJsonObject itemObj;
+ itemObj.insert("name", name);
+ itemObj.insert("qml", qml);
+ itemObj.insert("icon", iconPath);
QJsonArray filesArr;
for (const QString &assetPath : depAssets)
filesArr.append(assetPath);
- matObj.insert("files", filesArr);
+ itemObj.insert("files", filesArr);
+
+ itemsArr.append(itemObj);
+ jsonRef["items"] = itemsArr;
- matsObj.insert(name, matObj);
- jsonRef.insert("materials", matsObj);
- auto result = bundlePath.pathAppended("user_materials_bundle.json")
+ auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME)
.writeFileContents(QJsonDocument(jsonRef).toJson());
if (!result)
qWarning() << __FUNCTION__ << result.error();
// copy material assets to bundle folder
for (const QString &assetPath : depAssets) {
- Asset asset(assetPath);
- QString subDir;
- if (asset.isImage())
- subDir = "images";
- else if (asset.isShader())
- subDir = "shaders";
-
Utils::FilePath assetPathSource = DocumentManager::currentResourcePath().pathAppended(assetPath);
- Utils::FilePath assetPathTarget = bundlePath.pathAppended(QString("%1/%2")
- .arg(subDir, "/" + asset.fileName()));
+ Utils::FilePath assetPathTarget = bundlePath.pathAppended(assetPath);
+ assetPathTarget.parentDir().ensureWritableDir();
auto result = assetPathSource.copyFile(assetPathTarget);
if (!result)
@@ -589,14 +554,16 @@ void ContentLibraryView::addLibMaterial(const ModelNode &mat, const QPixmap &ico
m_widget->userModel()->addMaterial(name, qml, QUrl::fromLocalFile(fullIconPath), depAssets);
}
-QStringList ContentLibraryView::writeLibMaterialQml(const ModelNode &mat, const QString &qml)
+QStringList ContentLibraryView::writeLibItemQml(const ModelNode &node, const QString &qml)
{
QStringList depListIds;
- auto [qmlString, assets] = modelNodeToQmlString(mat, depListIds);
+ auto [qmlString, assets] = modelNodeToQmlString(node, depListIds);
qmlString.prepend("import QtQuick\nimport QtQuick3D\n\n");
- auto qmlPath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/" + qml);
+ QString itemType = QLatin1String(node.metaInfo().isQtQuick3DMaterial() ? "materials" : "3d");
+ auto qmlPath = Utils::FilePath::fromString(QLatin1String("%1/User/%2/%3")
+ .arg(Paths::bundlesPathSetting(), itemType, qml));
auto result = qmlPath.writeFileContents(qmlString.toUtf8());
if (!result)
qWarning() << __FUNCTION__ << result.error();
@@ -619,16 +586,24 @@ QPair<QString, QSet<QString>> ContentLibraryView::modelNodeToQmlString(const Mod
qml += indent + "id: " + (depth == 0 ? "root" : node.id()) + " \n\n";
+ const QList<PropertyName> excludedProps = {"x", "y", "z", "eulerRotation.x", "eulerRotation.y",
+ "eulerRotation.z", "scale.x", "scale.y", "scale.z",
+ "pivot.x", "pivot.y", "pivot.z"};
const QList<AbstractProperty> matProps = node.properties();
for (const AbstractProperty &p : matProps) {
+ if (excludedProps.contains(p.name()))
+ continue;
+
if (p.isVariantProperty()) {
QVariant pValue = p.toVariantProperty().value();
QString val;
if (strcmp(pValue.typeName(), "QString") == 0 || strcmp(pValue.typeName(), "QColor") == 0) {
val = QLatin1String("\"%1\"").arg(pValue.toString());
} else if (strcmp(pValue.typeName(), "QUrl") == 0) {
- val = QLatin1String("\"%1\"").arg(pValue.toString());
- assets.insert(pValue.toString());
+ QString pValueStr = pValue.toString();
+ val = QLatin1String("\"%1\"").arg(pValueStr);
+ if (!pValueStr.startsWith("#"))
+ assets.insert(pValue.toString());
} else if (strcmp(pValue.typeName(), "QmlDesigner::Enumeration") == 0) {
val = pValue.value<QmlDesigner::Enumeration>().toString();
} else {
@@ -662,19 +637,28 @@ void ContentLibraryView::addLibAssets(const QStringList &paths)
auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/textures");
QStringList pathsInBundle;
+ const QStringList existingTextures = Utils::transform(bundlePath.dirEntries(QDir::Files),
+ [](const Utils::FilePath &path) {
+ return path.fileName();
+ });
+
for (const QString &path : paths) {
+ auto assetFilePath = Utils::FilePath::fromString(path);
+ if (existingTextures.contains(assetFilePath.fileName()))
+ continue;
+
Asset asset(path);
- auto assetPath = Utils::FilePath::fromString(path);
// save icon
- QString iconSavePath = bundlePath.pathAppended("icons/" + assetPath.baseName() + ".png").toString();
+ QString iconSavePath = bundlePath.pathAppended("icons/" + assetFilePath.baseName() + ".png")
+ .toString();
QPixmap icon = asset.pixmap({120, 120});
bool iconSaved = icon.save(iconSavePath);
if (!iconSaved)
qWarning() << __FUNCTION__ << "icon save failed";
// save asset
- auto result = assetPath.copyFile(bundlePath.pathAppended(asset.fileName()));
+ auto result = assetFilePath.copyFile(bundlePath.pathAppended(asset.fileName()));
if (!result)
qWarning() << __FUNCTION__ << result.error();
@@ -684,6 +668,158 @@ void ContentLibraryView::addLibAssets(const QStringList &paths)
m_widget->userModel()->addTextures(pathsInBundle);
}
+void ContentLibraryView::addLib3DComponent(const ModelNode &node)
+{
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+
+ QString compBaseName = node.simplifiedTypeName();
+ QString compFileName = compBaseName + ".qml";
+
+ Utils::FilePath compDir = DocumentManager::currentProjectDirPath()
+ .pathAppended(compUtils.import3dTypePath() + '/' + compBaseName);
+
+ auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d/");
+
+ // confirm overwrite if an item with same name exists
+ if (bundlePath.pathAppended(compFileName).exists()) {
+ // Show a QML confirmation dialog before proceeding
+ QMessageBox::StandardButton reply = QMessageBox::question(m_widget, tr("3D Item Exists"),
+ tr("A 3D item with the same name '%1' already exists in the Content Library, are you sure you want to overwrite it?")
+ .arg(compFileName), QMessageBox::Yes | QMessageBox::No);
+ if (reply == QMessageBox::No)
+ return;
+
+ // before overwriting remove old item (to avoid partial items and dangling assets)
+ m_widget->userModel()->remove3DFromContentLibByName(compFileName);
+ }
+
+ // generate and save icon
+ QString iconPath = QLatin1String("icons/%1").arg(UniqueName::generateId(compBaseName) + ".png");
+ QString fullIconPath = bundlePath.pathAppended(iconPath).toString();
+ genAndSaveIcon(compDir.pathAppended(compFileName).path(), fullIconPath);
+
+ const Utils::FilePaths sourceFiles = compDir.dirEntries({{}, QDir::Files, QDirIterator::Subdirectories});
+ const QStringList ignoreList {"_importdata.json", "qmldir", compBaseName + ".hints"};
+ QStringList filesList; // 3D component's assets (dependencies)
+
+ for (const Utils::FilePath &sourcePath : sourceFiles) {
+ Utils::FilePath relativePath = sourcePath.relativePathFrom(compDir);
+ if (ignoreList.contains(sourcePath.fileName()) || relativePath.startsWith("source scene"))
+ continue;
+
+ Utils::FilePath targetPath = bundlePath.pathAppended(relativePath.path());
+ targetPath.parentDir().ensureWritableDir();
+
+ // copy item from project to user bundle
+ auto result = sourcePath.copyFile(targetPath);
+ if (!result)
+ qWarning() << __FUNCTION__ << result.error();
+
+ if (sourcePath.fileName() != compFileName) // skip component file (only collect dependencies)
+ filesList.append(relativePath.path());
+ }
+
+ // add the item to the bundle json
+ QJsonObject &jsonRef = m_widget->userModel()->bundleJson3DObjectRef();
+ QJsonArray itemsArr = jsonRef.value("items").toArray();
+ itemsArr.append(QJsonObject {
+ {"name", node.simplifiedTypeName()},
+ {"qml", compFileName},
+ {"icon", iconPath},
+ {"files", QJsonArray::fromStringList(filesList)}
+ });
+
+ jsonRef["items"] = itemsArr;
+
+ auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME)
+ .writeFileContents(QJsonDocument(jsonRef).toJson());
+ if (!result)
+ qWarning() << __FUNCTION__ << result.error();
+
+ m_widget->userModel()->add3DItem(compBaseName, compFileName, QUrl::fromLocalFile(fullIconPath),
+ filesList);
+}
+
+void ContentLibraryView::addLib3DItem(const ModelNode &node)
+{
+ auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d/");
+
+ QString name = node.variantProperty("objectName").value().toString();
+ auto [qml, icon] = m_widget->userModel()->getUniqueLib3DNames(node.id());
+ QString iconPath = QLatin1String("icons/%1").arg(icon);
+
+ if (name.isEmpty())
+ name = node.id();
+
+ // generate and save item Qml file
+ const QStringList depAssets = writeLibItemQml(node, qml);
+
+ // generate and save icon
+ QString qmlPath = QLatin1String("%1/User/3d/%2").arg(Paths::bundlesPathSetting(), qml);
+ QString fullIconPath = bundlePath.pathAppended(iconPath).toString();
+ genAndSaveIcon(qmlPath, fullIconPath);
+
+ // add the item to the bundle json
+ QJsonObject &jsonRef = m_widget->userModel()->bundleJson3DObjectRef();
+ QJsonArray itemsArr = jsonRef.value("items").toArray();
+ itemsArr.append(QJsonObject {
+ {"name", name},
+ {"qml", qml},
+ {"icon", iconPath},
+ {"files", QJsonArray::fromStringList(depAssets)}
+ });
+
+ jsonRef["items"] = itemsArr;
+
+ auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME)
+ .writeFileContents(QJsonDocument(jsonRef).toJson());
+ if (!result)
+ qWarning() << __FUNCTION__ << result.error();
+
+ // copy item's assets to bundle folder
+ for (const QString &assetPath : depAssets) {
+ Utils::FilePath assetPathSource = DocumentManager::currentResourcePath().pathAppended(assetPath);
+ Utils::FilePath assetPathTarget = bundlePath.pathAppended(assetPath);
+ assetPathTarget.parentDir().ensureWritableDir();
+
+ auto result = assetPathSource.copyFile(assetPathTarget);
+ if (!result)
+ qWarning() << __FUNCTION__ << result.error();
+ }
+
+ m_widget->userModel()->add3DItem(name, qml, QUrl::fromLocalFile(fullIconPath), depAssets);
+}
+
+/**
+ * @brief Generates an icon image from a qml component
+ * @param qmlPath path to the qml component file to be rendered
+ * @param iconPath output save path of the generated icon
+ */
+void ContentLibraryView::genAndSaveIcon(const QString &qmlPath, const QString &iconPath)
+{
+ m_imageCache.requestSmallImage(
+ Utils::PathString{qmlPath},
+ [&, qmlPath, iconPath](const QImage &image) {
+ bool iconSaved = image.save(iconPath);
+ if (iconSaved)
+ m_widget->userModel()->refresh3DSection();
+ else
+ qWarning() << "ContentLibraryView::genAndSaveIcon(): icon save failed";
+ },
+ [&](ImageCache::AbortReason abortReason) {
+ if (abortReason == ImageCache::AbortReason::Abort) {
+ qWarning() << QLatin1String("ContentLibraryView::genAndSaveIcon(): icon generation "
+ "failed for path %1, reason: Abort").arg(qmlPath);
+ } else if (abortReason == ImageCache::AbortReason::Failed) {
+ qWarning() << QLatin1String("ContentLibraryView::genAndSaveIcon(): icon generation "
+ "failed for path %1, reason: Failed").arg(qmlPath);
+ } else if (abortReason == ImageCache::AbortReason::NoEntry) {
+ qWarning() << QLatin1String("ContentLibraryView::genAndSaveIcon(): icon generation "
+ "failed for path %1, reason: NoEntry").arg(qmlPath);
+ }
+ });
+}
+
ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &type)
{
ModelNode matLib = Utils3D::materialLibraryNode(this);
@@ -724,7 +860,7 @@ ModelNode ContentLibraryView::createMaterial(const TypeName &typeName)
QString newName = QString::fromUtf8(typeName).replace(rgx, " \\1\\2").trimmed();
if (newName.endsWith(" Material"))
newName.chop(9); // remove trailing " Material"
- QString newId = model()->generateIdFromName(newName, "material");
+ QString newId = model()->generateNewId(newName, "material");
newMatNode.setIdWithRefactoring(newId);
VariantProperty objNameProp = newMatNode.variantProperty("objectName");
@@ -750,7 +886,7 @@ ModelNode ContentLibraryView::createMaterial(const NodeMetaInfo &metaInfo)
QString newName = QString::fromLatin1(metaInfo.simplifiedTypeName()).replace(rgx, " \\1\\2").trimmed();
if (newName.endsWith(" Material"))
newName.chop(9); // remove trailing " Material"
- QString newId = model()->generateIdFromName(newName, "material");
+ QString newId = model()->generateNewId(newName, "material");
newMatNode.setIdWithRefactoring(newId);
VariantProperty objNameProp = newMatNode.variantProperty("objectName");
@@ -762,63 +898,6 @@ ModelNode ContentLibraryView::createMaterial(const NodeMetaInfo &metaInfo)
}
#endif
-void ContentLibraryView::updateBundleMaterialsImportedState()
-{
- using namespace Utils;
-
- if (!m_widget->materialsModel()->bundleImporter())
- return;
-
- QStringList importedBundleMats;
-
- FilePath materialBundlePath = m_widget->materialsModel()->bundleImporter()->resolveBundleImportPath();
-
- if (materialBundlePath.exists()) {
- importedBundleMats = transform(materialBundlePath.dirEntries({{"*.qml"}, QDir::Files}),
- [](const FilePath &f) { return f.fileName().chopped(4); });
- }
-
- m_widget->materialsModel()->updateImportedState(importedBundleMats);
-}
-
-void ContentLibraryView::updateBundleUserMaterialsImportedState()
-{
- using namespace Utils;
-
- if (!m_widget->userModel()->bundleImporter())
- return;
-
- QStringList importedBundleMats;
-
- FilePath bundlePath = m_widget->userModel()->bundleImporter()->resolveBundleImportPath();
-
- if (bundlePath.exists()) {
- importedBundleMats = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}),
- [](const FilePath &f) { return f.fileName().chopped(4); });
- }
-
- m_widget->userModel()->updateImportedState(importedBundleMats);
-}
-
-void ContentLibraryView::updateBundleEffectsImportedState()
-{
- using namespace Utils;
-
- if (!m_widget->effectsModel()->bundleImporter())
- return;
-
- QStringList importedBundleEffs;
-
- FilePath bundlePath = m_widget->effectsModel()->bundleImporter()->resolveBundleImportPath();
-
- if (bundlePath.exists()) {
- importedBundleEffs = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}),
- [](const FilePath &f) { return f.fileName().chopped(4); });
- }
-
- m_widget->effectsModel()->updateImportedState(importedBundleEffs);
-}
-
void ContentLibraryView::updateBundlesQuick3DVersion()
{
bool hasImport = false;
@@ -853,6 +932,7 @@ void ContentLibraryView::updateBundlesQuick3DVersion()
#endif
m_widget->materialsModel()->setQuick3DImportVersion(major, minor);
m_widget->effectsModel()->setQuick3DImportVersion(major, minor);
+ m_widget->userModel()->setQuick3DImportVersion(major, minor);
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h
index 03d42fa8bc..914a8b8ea0 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h
@@ -3,9 +3,10 @@
#pragma once
-#include "abstractview.h"
-#include "createtexture.h"
-#include "nodemetainfo.h"
+#include <asynchronousimagecache.h>
+#include <abstractview.h>
+#include <createtexture.h>
+#include <nodemetainfo.h>
#include <QObject>
#include <QPointer>
@@ -14,7 +15,7 @@ QT_FORWARD_DECLARE_CLASS(QPixmap)
namespace QmlDesigner {
-class ContentLibraryEffect;
+class ContentLibraryItem;
class ContentLibraryMaterial;
class ContentLibraryTexture;
class ContentLibraryWidget;
@@ -25,7 +26,8 @@ class ContentLibraryView : public AbstractView
Q_OBJECT
public:
- ContentLibraryView(ExternalDependenciesInterface &externalDependencies);
+ ContentLibraryView(AsynchronousImageCache &imageCache,
+ ExternalDependenciesInterface &externalDependencies);
~ContentLibraryView() override;
bool hasWidget() const override;
@@ -48,15 +50,17 @@ public:
const QVariant &data) override;
private:
- void connectUserBundle();
+ void connectImporter();
+ bool isMaterialBundle(const QString &bundleId) const;
+ bool isItemBundle(const QString &bundleId) const;
void active3DSceneChanged(qint32 sceneId);
- void updateBundleMaterialsImportedState();
- void updateBundleUserMaterialsImportedState();
- void updateBundleEffectsImportedState();
void updateBundlesQuick3DVersion();
- void addLibMaterial(const ModelNode &mat, const QPixmap &icon);
+ void addLibMaterial(const ModelNode &node, const QPixmap &iconPixmap);
void addLibAssets(const QStringList &paths);
- QStringList writeLibMaterialQml(const ModelNode &mat, const QString &qml);
+ void addLib3DComponent(const ModelNode &node);
+ void addLib3DItem(const ModelNode &node);
+ void genAndSaveIcon(const QString &qmlPath, const QString &iconPath);
+ QStringList writeLibItemQml(const ModelNode &node, const QString &qml);
QPair<QString, QSet<QString>> modelNodeToQmlString(const ModelNode &node, QStringList &depListIds,
int depth = 0);
@@ -74,12 +78,13 @@ private:
#endif
QPointer<ContentLibraryWidget> m_widget;
QList<ModelNode> m_bundleMaterialTargets;
- ModelNode m_bundleEffectTarget; // target of the dropped bundle effect
- QVariant m_bundleEffectPos; // pos of the dropped bundle effect
+ ModelNode m_bundleItemTarget; // target of the dropped bundle item
+ QVariant m_bundleItemPos; // pos of the dropped bundle item
QList<ModelNode> m_selectedModels; // selected 3D model nodes
ContentLibraryMaterial *m_draggedBundleMaterial = nullptr;
ContentLibraryTexture *m_draggedBundleTexture = nullptr;
- ContentLibraryEffect *m_draggedBundleEffect = nullptr;
+ ContentLibraryItem *m_draggedBundleItem = nullptr;
+ AsynchronousImageCache &m_imageCache;
bool m_bundleMaterialAddToSelected = false;
bool m_hasQuick3DImport = false;
qint32 m_sceneId = -1;
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp
index 9375d43fd4..72bece4c98 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp
@@ -3,8 +3,9 @@
#include "contentlibrarywidget.h"
-#include "contentlibraryeffect.h"
+#include "contentlibrarybundleimporter.h"
#include "contentlibraryeffectsmodel.h"
+#include "contentlibraryitem.h"
#include "contentlibrarymaterial.h"
#include "contentlibrarymaterialsmodel.h"
#include "contentlibrarytexture.h"
@@ -18,6 +19,7 @@
#include <coreplugin/icore.h>
#include <designerpaths.h>
+#include <nodemetainfo.h>
#include <qmldesignerconstants.h>
#include <qmldesignerplugin.h>
@@ -40,7 +42,6 @@
#include <QQuickWidget>
#include <QRegularExpression>
#include <QShortcut>
-#include <QStandardPaths>
#include <QVBoxLayout>
namespace QmlDesigner {
@@ -67,18 +68,18 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event)
Model *model = document->currentModel();
QTC_ASSERT(model, return false);
- if (m_effectToDrag) {
+ if (m_itemToDrag) {
QMouseEvent *me = static_cast<QMouseEvent *>(event);
if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 20) {
QByteArray data;
QMimeData *mimeData = new QMimeData;
QDataStream stream(&data, QIODevice::WriteOnly);
- stream << m_effectToDrag->type();
- mimeData->setData(Constants::MIME_TYPE_BUNDLE_EFFECT, data);
+ stream << m_itemToDrag->type();
+ mimeData->setData(Constants::MIME_TYPE_BUNDLE_ITEM, data);
- emit bundleEffectDragStarted(m_effectToDrag);
- model->startDrag(mimeData, m_effectToDrag->icon().toLocalFile());
- m_effectToDrag = nullptr;
+ emit bundleItemDragStarted(m_itemToDrag);
+ model->startDrag(mimeData, m_itemToDrag->icon().toLocalFile());
+ m_itemToDrag = nullptr;
}
} else if (m_materialToDrag) {
QMouseEvent *me = static_cast<QMouseEvent *>(event);
@@ -112,7 +113,7 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event)
}
}
} else if (event->type() == QMouseEvent::MouseButtonRelease) {
- m_effectToDrag = nullptr;
+ m_itemToDrag = nullptr;
m_materialToDrag = nullptr;
m_textureToDrag = nullptr;
setIsDragging(false);
@@ -122,7 +123,7 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event)
}
ContentLibraryWidget::ContentLibraryWidget()
- : m_quickWidget(new StudioQuickWidget(this))
+ : m_quickWidget(Utils::makeUniqueObjectPtr<StudioQuickWidget>(this))
, m_materialsModel(new ContentLibraryMaterialsModel(this))
, m_texturesModel(new ContentLibraryTexturesModel("Textures", this))
, m_environmentsModel(new ContentLibraryTexturesModel("Environments", this))
@@ -155,7 +156,7 @@ ContentLibraryWidget::ContentLibraryWidget()
auto layout = new QVBoxLayout(this);
layout->setContentsMargins({});
layout->setSpacing(0);
- layout->addWidget(m_quickWidget.data());
+ layout->addWidget(m_quickWidget.get());
updateSearch();
@@ -177,6 +178,67 @@ ContentLibraryWidget::ContentLibraryWidget()
{"userModel", QVariant::fromValue(m_userModel.data())}});
reloadQmlSource();
+ createImporter();
+}
+
+void ContentLibraryWidget::createImporter()
+{
+ m_importer = new ContentLibraryBundleImporter();
+#ifdef QDS_USE_PROJECTSTORAGE
+ connect(m_importer,
+ &ContentLibraryBundleImporter::importFinished,
+ this,
+ [&](const QmlDesigner::TypeName &typeName, const QString &bundleId) {
+ setImporterRunning(false);
+ if (typeName.size())
+ updateImportedState(bundleId);
+ });
+#else
+ connect(m_importer,
+ &ContentLibraryBundleImporter::importFinished,
+ this,
+ [&](const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId) {
+ setImporterRunning(false);
+ if (metaInfo.isValid())
+ updateImportedState(bundleId);
+ });
+#endif
+
+ connect(m_importer, &ContentLibraryBundleImporter::unimportFinished, this,
+ [&](const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId) {
+ Q_UNUSED(metaInfo)
+ setImporterRunning(false);
+ updateImportedState(bundleId);
+ });
+}
+
+void ContentLibraryWidget::updateImportedState(const QString &bundleId)
+{
+ if (!m_importer)
+ return;
+
+ Utils::FilePath bundlePath = m_importer->resolveBundleImportPath(bundleId);
+
+ QStringList importedItems;
+ if (bundlePath.exists()) {
+ importedItems = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}),
+ [](const Utils::FilePath &f) { return f.baseName(); });
+ }
+
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+ if (bundleId == compUtils.materialsBundleId())
+ m_materialsModel->updateImportedState(importedItems);
+ else if (bundleId == compUtils.effectsBundleId())
+ m_effectsModel->updateImportedState(importedItems);
+ else if (bundleId == compUtils.userMaterialsBundleId())
+ m_userModel->updateMaterialsImportedState(importedItems);
+ else if (bundleId == compUtils.user3DBundleId())
+ m_userModel->update3DImportedState(importedItems);
+}
+
+ContentLibraryBundleImporter *ContentLibraryWidget::importer() const
+{
+ return m_importer;
}
QVariantMap ContentLibraryWidget::readTextureBundleJson()
@@ -578,12 +640,6 @@ void ContentLibraryWidget::markTextureUpdated(const QString &textureKey)
m_environmentsModel->markTextureHasNoUpdates(subcategory, textureKey);
}
-bool ContentLibraryWidget::userBundleEnabled() const
-{
- // TODO: this method is to be removed after user bundle implementation is complete
- return Core::ICore::settings()->value("QML/Designer/UseExperimentalFeatures45", false).toBool();
-}
-
QSize ContentLibraryWidget::sizeHint() const
{
return {420, 420};
@@ -683,6 +739,20 @@ void ContentLibraryWidget::setIsQt6Project(bool b)
emit isQt6ProjectChanged();
}
+bool ContentLibraryWidget::importerRunning() const
+{
+ return m_importerRunning;
+}
+
+void ContentLibraryWidget::setImporterRunning(bool b)
+{
+ if (m_importerRunning == b)
+ return;
+
+ m_importerRunning = b;
+ emit importerRunningChanged();
+}
+
void ContentLibraryWidget::reloadQmlSource()
{
const QString materialBrowserQmlPath = qmlSourcesPath() + "/ContentLibrary.qml";
@@ -710,32 +780,9 @@ void ContentLibraryWidget::setIsDragging(bool val)
}
}
-QString ContentLibraryWidget::findTextureBundlePath()
+void ContentLibraryWidget::startDragItem(QmlDesigner::ContentLibraryItem *item, const QPointF &mousePos)
{
- QDir texBundleDir;
-
- if (!qEnvironmentVariable("TEXTURE_BUNDLE_PATH").isEmpty())
- texBundleDir.setPath(qEnvironmentVariable("TEXTURE_BUNDLE_PATH"));
- else if (Utils::HostOsInfo::isMacHost())
- texBundleDir.setPath(QCoreApplication::applicationDirPath() + "/../Resources/texture_bundle");
-
- // search for matBundleDir from exec dir and up
- if (texBundleDir.dirName() == ".") {
- texBundleDir.setPath(QCoreApplication::applicationDirPath());
- while (!texBundleDir.cd("texture_bundle") && texBundleDir.cdUp())
- ; // do nothing
-
- if (texBundleDir.dirName() != "texture_bundle") // bundlePathDir not found
- return {};
- }
-
- return texBundleDir.path();
-}
-
-void ContentLibraryWidget::startDragEffect(QmlDesigner::ContentLibraryEffect *eff,
- const QPointF &mousePos)
-{
- m_effectToDrag = eff;
+ m_itemToDrag = item;
m_dragStartPoint = mousePos.toPoint();
setIsDragging(true);
}
@@ -810,4 +857,18 @@ QPointer<ContentLibraryUserModel> ContentLibraryWidget::userModel() const
return m_userModel;
}
+bool ContentLibraryWidget::hasModelSelection() const
+{
+ return m_hasModelSelection;
+}
+
+void ContentLibraryWidget::setHasModelSelection(bool b)
+{
+ if (b == m_hasModelSelection)
+ return;
+
+ m_hasModelSelection = b;
+ emit hasModelSelectionChanged();
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h
index c4d51d0362..8e96d9d2f3 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h
@@ -5,6 +5,10 @@
#include "createtexture.h"
+#include <modelfwd.h>
+
+#include <utils/uniqueobjectptr.h>
+
#include <QFrame>
#include <QPointer>
@@ -18,13 +22,15 @@ class StudioQuickWidget;
namespace QmlDesigner {
-class ContentLibraryEffect;
+class ContentLibraryBundleImporter;
class ContentLibraryEffectsModel;
+class ContentLibraryItem;
class ContentLibraryMaterial;
class ContentLibraryMaterialsModel;
class ContentLibraryTexture;
class ContentLibraryTexturesModel;
class ContentLibraryUserModel;
+class NodeMetaInfo;
class ContentLibraryWidget : public QFrame
{
@@ -34,6 +40,8 @@ class ContentLibraryWidget : public QFrame
Q_PROPERTY(bool hasMaterialLibrary READ hasMaterialLibrary NOTIFY hasMaterialLibraryChanged)
Q_PROPERTY(bool hasActive3DScene READ hasActive3DScene WRITE setHasActive3DScene NOTIFY hasActive3DSceneChanged)
Q_PROPERTY(bool isQt6Project READ isQt6Project NOTIFY isQt6ProjectChanged)
+ Q_PROPERTY(bool importerRunning READ importerRunning WRITE setImporterRunning NOTIFY importerRunningChanged)
+ Q_PROPERTY(bool hasModelSelection READ hasModelSelection NOTIFY hasModelSelectionChanged)
// Needed for a workaround for a bug where after drag-n-dropping an item, the ScrollView scrolls to a random position
Q_PROPERTY(bool isDragging MEMBER m_isDragging NOTIFY isDraggingChanged)
@@ -58,9 +66,14 @@ public:
bool isQt6Project() const;
void setIsQt6Project(bool b);
- Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText);
+ bool importerRunning() const;
+ void setImporterRunning(bool b);
+
+ bool hasModelSelection() const;
+ void setHasModelSelection(bool b);
void setMaterialsModel(QPointer<ContentLibraryMaterialsModel> newMaterialsModel);
+ void updateImportedState(const QString &bundleId);
QPointer<ContentLibraryMaterialsModel> materialsModel() const;
QPointer<ContentLibraryTexturesModel> texturesModel() const;
@@ -68,7 +81,8 @@ public:
QPointer<ContentLibraryEffectsModel> effectsModel() const;
QPointer<ContentLibraryUserModel> userModel() const;
- Q_INVOKABLE void startDragEffect(QmlDesigner::ContentLibraryEffect *eff, const QPointF &mousePos);
+ Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText);
+ Q_INVOKABLE void startDragItem(QmlDesigner::ContentLibraryItem *item, const QPointF &mousePos);
Q_INVOKABLE void startDragMaterial(QmlDesigner::ContentLibraryMaterial *mat, const QPointF &mousePos);
Q_INVOKABLE void startDragTexture(QmlDesigner::ContentLibraryTexture *tex, const QPointF &mousePos);
Q_INVOKABLE void addImage(QmlDesigner::ContentLibraryTexture *tex);
@@ -76,12 +90,13 @@ public:
Q_INVOKABLE void addLightProbe(QmlDesigner::ContentLibraryTexture *tex);
Q_INVOKABLE void updateSceneEnvState();
Q_INVOKABLE void markTextureUpdated(const QString &textureKey);
- Q_INVOKABLE bool userBundleEnabled() const;
QSize sizeHint() const override;
+ ContentLibraryBundleImporter *importer() const;
+
signals:
- void bundleEffectDragStarted(QmlDesigner::ContentLibraryEffect *bundleEff);
+ void bundleItemDragStarted(QmlDesigner::ContentLibraryItem *item);
void bundleMaterialDragStarted(QmlDesigner::ContentLibraryMaterial *bundleMat);
void bundleTextureDragStarted(QmlDesigner::ContentLibraryTexture *bundleTex);
void addTextureRequested(const QString texPath, QmlDesigner::AddTextureMode mode);
@@ -91,6 +106,8 @@ signals:
void hasActive3DSceneChanged();
void isDraggingChanged();
void isQt6ProjectChanged();
+ void importerRunningChanged();
+ void hasModelSelectionChanged();
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
@@ -99,7 +116,6 @@ private:
void reloadQmlSource();
void updateSearch();
void setIsDragging(bool val);
- QString findTextureBundlePath();
void loadTextureBundles();
QVariantMap readTextureBundleJson();
bool fetchTextureBundleJson(const QDir &bundleDir);
@@ -110,19 +126,21 @@ private:
const QString &existingMetaFile, const QString downloadedMetaFile);
QStringList saveNewTextures(const QDir &bundleDir, const QStringList &newFiles);
void populateTextureBundleModels();
+ void createImporter();
- QScopedPointer<StudioQuickWidget> m_quickWidget;
+ Utils::UniqueObjectPtr<StudioQuickWidget> m_quickWidget;
QPointer<ContentLibraryMaterialsModel> m_materialsModel;
QPointer<ContentLibraryTexturesModel> m_texturesModel;
QPointer<ContentLibraryTexturesModel> m_environmentsModel;
QPointer<ContentLibraryEffectsModel> m_effectsModel;
QPointer<ContentLibraryUserModel> m_userModel;
+ ContentLibraryBundleImporter *m_importer = nullptr;
QShortcut *m_qmlSourceUpdateShortcut = nullptr;
QString m_filterText;
- ContentLibraryEffect *m_effectToDrag = nullptr;
+ ContentLibraryItem *m_itemToDrag = nullptr;
ContentLibraryMaterial *m_materialToDrag = nullptr;
ContentLibraryTexture *m_textureToDrag = nullptr;
QPoint m_dragStartPoint;
@@ -132,6 +150,8 @@ private:
bool m_hasQuick3DImport = false;
bool m_isDragging = false;
bool m_isQt6Project = false;
+ bool m_importerRunning = false;
+ bool m_hasModelSelection = false;
QString m_textureBundleUrl;
QString m_bundlePath;
};
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp
index a1c229f57e..159e7c31ee 100644
--- a/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp
@@ -422,7 +422,7 @@ QVariant KeyframeItem::itemChange(QGraphicsItem::GraphicsItemChange change, cons
rseg.moveLeftTo(position);
if (legalLeft() && legalRight()) {
- if (qApp->keyboardModifiers().testFlag(Qt::ShiftModifier) && m_validPos.has_value()) {
+ if (qApp->keyboardModifiers().testFlag(Qt::ShiftModifier) && m_validPos) {
if (m_firstPos) {
auto firstToNow = QLineF(*m_firstPos, position);
if (std::abs(firstToNow.dx()) > std::abs(firstToNow.dy()))
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp
index 2e8ef8304f..63d5e958b1 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp
@@ -51,6 +51,8 @@ Edit3DCanvas::Edit3DCanvas(Edit3DWidget *parent)
setAcceptDrops(true);
setFocusPolicy(Qt::ClickFocus);
m_busyIndicator->show();
+
+ installEventFilter(this);
}
void Edit3DCanvas::updateRenderImage(const QImage &img)
@@ -79,11 +81,20 @@ QWidget *Edit3DCanvas::busyIndicator() const
return m_busyIndicator;
}
+#ifdef Q_OS_MACOS
+extern "C" bool AXIsProcessTrusted();
+#endif
+
void Edit3DCanvas::setFlyMode(bool enabled, const QPoint &pos)
{
if (m_flyMode == enabled)
return;
+#ifdef Q_OS_MACOS
+ if (!AXIsProcessTrusted())
+ m_isTrusted = false;
+#endif
+
m_flyMode = enabled;
if (enabled) {
@@ -132,6 +143,23 @@ void Edit3DCanvas::setFlyMode(bool enabled, const QPoint &pos)
m_parent->view()->setFlyMode(enabled);
}
+bool Edit3DCanvas::eventFilter(QObject *obj, QEvent *event)
+{
+ if (m_flyMode && event->type() == QEvent::ShortcutOverride) {
+ // Suppress shortcuts that conflict with fly mode keys
+ const QList<int> controlKeys = { Qt::Key_W, Qt::Key_A, Qt::Key_S,
+ Qt::Key_D, Qt::Key_Q, Qt::Key_E,
+ Qt::Key_Up, Qt::Key_Down, Qt::Key_Left,
+ Qt::Key_Right, Qt::Key_PageDown, Qt::Key_PageUp,
+ Qt::Key_Alt, Qt::Key_Shift };
+ auto ke = static_cast<QKeyEvent *>(event);
+ if (controlKeys.contains(ke->key()))
+ event->accept();
+ }
+
+ return QObject::eventFilter(obj, event);
+}
+
void Edit3DCanvas::mousePressEvent(QMouseEvent *e)
{
m_contextMenuPending = false;
@@ -171,7 +199,8 @@ void Edit3DCanvas::mouseMoveEvent(QMouseEvent *e)
// We notify explicit camera rotation need for puppet rather than rely in mouse events,
// as mouse isn't grabbed on puppet side and can't handle fast movements that go out of
// edit camera mouse area. This also simplifies split view handling.
- QPointF diff = m_hiddenCursorPos - e->globalPos();
+ QPointF diff = m_isTrusted ? (m_hiddenCursorPos - e->globalPos()) : (m_lastCursorPos - e->globalPos());
+
if (e->buttons() == (Qt::LeftButton | Qt::RightButton)) {
m_parent->view()->emitView3DAction(View3DActionType::EditCameraMove,
QVector3D{float(-diff.x()), float(-diff.y()), 0.f});
@@ -182,13 +211,26 @@ void Edit3DCanvas::mouseMoveEvent(QMouseEvent *e)
// Skip first move to avoid undesirable jump occasionally when initiating flight mode
m_flyModeFirstUpdate = false;
}
- QCursor::setPos(m_hiddenCursorPos);
+
+ if (m_isTrusted)
+ QCursor::setPos(m_hiddenCursorPos);
+ else
+ m_lastCursorPos = e->globalPos();
}
}
void Edit3DCanvas::wheelEvent(QWheelEvent *e)
{
- m_parent->view()->sendInputEvent(e);
+ if (m_flyMode) {
+ // In fly mode, wheel controls the camera speed slider (value range 1-100)
+ double speed;
+ double mult;
+ m_parent->view()->getCameraSpeedAuxData(speed, mult);
+ speed = qMin(100., qMax(1., speed + double(e->angleDelta().y()) / 40.));
+ m_parent->view()->setCameraSpeedAuxData(speed, mult);
+ } else {
+ m_parent->view()->sendInputEvent(e);
+ }
QWidget::wheelEvent(e);
}
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h
index 39207554a7..16c1063dd6 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h
@@ -30,6 +30,7 @@ public:
bool isFlyMode() const { return m_flyMode; }
protected:
+ bool eventFilter(QObject *obj, QEvent *event) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void mouseDoubleClickEvent(QMouseEvent *e) override;
@@ -52,10 +53,12 @@ private:
qint32 m_activeScene = -1;
QElapsedTimer m_usageTimer;
qreal m_opacity = 1.0;
+ bool m_isTrusted = true;
QWidget *m_busyIndicator = nullptr;
bool m_flyMode = false;
QPoint m_flyModeStartCursorPos;
QPoint m_hiddenCursorPos;
+ QPoint m_lastCursorPos;
qint64 m_flyModeStartTime = 0;
bool m_flyModeFirstUpdate = false;
bool m_contextMenuPending = false;
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
index 4712b048b1..bb7404f252 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
@@ -20,9 +20,11 @@
#include "nodeinstanceview.h"
#include "qmldesignerconstants.h"
#include "qmldesignerplugin.h"
+#include "qmlitemnode.h"
#include "qmlvisualnode.h"
#include "seekerslider.h"
#include "snapconfiguration.h"
+#include "variantproperty.h"
#include <auxiliarydataproperties.h>
#include <model/modelutils.h>
@@ -133,6 +135,7 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
const QString orientationKey = QStringLiteral("globalOrientation");
const QString editLightKey = QStringLiteral("showEditLight");
const QString gridKey = QStringLiteral("showGrid");
+ const QString showLookAtKey = QStringLiteral("showLookAt");
const QString selectionBoxKey = QStringLiteral("showSelectionBox");
const QString iconGizmoKey = QStringLiteral("showIconGizmo");
const QString cameraFrustumKey = QStringLiteral("showCameraFrustum");
@@ -187,6 +190,11 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
else
m_showGridAction->action()->setChecked(false);
+ if (sceneState.contains(showLookAtKey))
+ m_showLookAtAction->action()->setChecked(sceneState[showLookAtKey].toBool());
+ else
+ m_showLookAtAction->action()->setChecked(false);
+
if (sceneState.contains(selectionBoxKey))
m_showSelectionBoxAction->action()->setChecked(sceneState[selectionBoxKey].toBool());
else
@@ -235,36 +243,10 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
state.showWireframe = false;
}
- // Syncing background color only makes sense for children of View3D instances
- bool syncValue = false;
- bool syncEnabled = false;
- bool desiredSyncValue = false;
if (sceneState.contains(syncEnvBgKey))
- desiredSyncValue = sceneState[syncEnvBgKey].toBool();
- ModelNode checkNode = Utils3D::active3DSceneNode(this);
- const bool activeSceneValid = checkNode.isValid();
-
- while (checkNode.isValid()) {
- if (checkNode.metaInfo().isQtQuick3DView3D()) {
- syncValue = desiredSyncValue;
- syncEnabled = true;
- break;
- }
- if (checkNode.hasParentProperty())
- checkNode = checkNode.parentProperty().parentModelNode();
- else
- break;
- }
-
- if (activeSceneValid && syncValue != desiredSyncValue) {
- // Update actual toolstate as well if we overrode it.
- QTimer::singleShot(0, this, [this, syncValue]() {
- emitView3DAction(View3DActionType::SyncEnvBackground, syncValue);
- });
- }
-
- m_syncEnvBackgroundAction->action()->setChecked(syncValue);
- m_syncEnvBackgroundAction->action()->setEnabled(syncEnabled);
+ m_syncEnvBackgroundAction->action()->setChecked(sceneState[syncEnvBgKey].toBool());
+ else
+ m_syncEnvBackgroundAction->action()->setChecked(false);
// Selection context change updates visible and enabled states
SelectionContext selectionContext(this);
@@ -273,6 +255,8 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
m_bakeLightsAction->currentContextChanged(selectionContext);
syncCameraSpeedToNewView();
+
+ storeCurrentSceneEnvironment();
}
void Edit3DView::modelAttached(Model *model)
@@ -350,11 +334,10 @@ void Edit3DView::handleEntriesChanged()
{EK_importedModels, {tr("Imported Models"), contextIcon(DesignerIcons::ImportedModelsIcon)}}};
#ifdef QDS_USE_PROJECTSTORAGE
- const auto &projectStorage = *model()->projectStorage();
auto append = [&](const NodeMetaInfo &metaInfo, ItemLibraryEntryKeys key) {
auto entries = metaInfo.itemLibrariesEntries();
if (entries.size())
- entriesMap[key].entryList.append(toItemLibraryEntries(entries, projectStorage));
+ entriesMap[key].entryList.append(toItemLibraryEntries(entries));
};
append(model()->qtQuick3DModelMetaInfo(), EK_primitives);
@@ -369,7 +352,7 @@ void Edit3DView::handleEntriesChanged()
.generatedComponentUtils()
.import3dTypePrefix();
- auto assetsModule = model()->module(import3dTypePrefix);
+ auto assetsModule = model()->module(import3dTypePrefix, Storage::ModuleKind::QmlLibrary);
for (const auto &metaInfo : model()->metaInfosForModule(assetsModule))
append(metaInfo, EK_importedModels);
@@ -386,9 +369,12 @@ void Edit3DView::handleEntriesChanged()
} else if (entry.typeName() == "QtQuick3D.OrthographicCamera"
|| entry.typeName() == "QtQuick3D.PerspectiveCamera") {
entryKey = EK_cameras;
- } else if (entry.typeName().startsWith(QmlDesignerPlugin::instance()->documentManager()
- .generatedComponentUtils().import3dTypePrefix().toUtf8())
- && NodeHints::fromItemLibraryEntry(entry).canBeDroppedInView3D()) {
+ } else if (entry.typeName().startsWith(QmlDesignerPlugin::instance()
+ ->documentManager()
+ .generatedComponentUtils()
+ .import3dTypePrefix()
+ .toUtf8())
+ && NodeHints::fromItemLibraryEntry(entry, model()).canBeDroppedInView3D()) {
entryKey = EK_importedModels;
} else {
continue;
@@ -505,7 +491,7 @@ void Edit3DView::nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos
} else if (m_nodeAtPosReqType == NodeAtPosReqType::BundleMaterialDrop) {
emitCustomNotification("drop_bundle_material", {modelNode}); // To ContentLibraryView
} else if (m_nodeAtPosReqType == NodeAtPosReqType::BundleEffectDrop) {
- emitCustomNotification("drop_bundle_effect", {modelNode}, {pos3d}); // To ContentLibraryView
+ emitCustomNotification("drop_bundle_item", {modelNode}, {pos3d}); // To ContentLibraryView
} else if (m_nodeAtPosReqType == NodeAtPosReqType::TextureDrop) {
emitCustomNotification("apply_texture_to_model3D", {modelNode, m_droppedModelNode});
} else if (m_nodeAtPosReqType == NodeAtPosReqType::AssetDrop) {
@@ -540,6 +526,21 @@ void Edit3DView::nodeRemoved(const ModelNode &,
updateAlignActionStates();
}
+void Edit3DView::propertiesRemoved(const QList<AbstractProperty> &propertyList)
+{
+ maybeStoreCurrentSceneEnvironment(propertyList);
+}
+
+void Edit3DView::bindingPropertiesChanged(const QList<BindingProperty> &propertyList, PropertyChangeFlags)
+{
+ maybeStoreCurrentSceneEnvironment(propertyList);
+}
+
+void Edit3DView::variantPropertiesChanged(const QList<VariantProperty> &propertyList, PropertyChangeFlags)
+{
+ maybeStoreCurrentSceneEnvironment(propertyList);
+}
+
void Edit3DView::sendInputEvent(QEvent *e) const
{
if (nodeInstanceView())
@@ -716,6 +717,30 @@ QPoint Edit3DView::resolveToolbarPopupPos(Edit3DAction *action) const
return pos;
}
+template<typename T, typename>
+void Edit3DView::maybeStoreCurrentSceneEnvironment(const QList<T> &propertyList)
+{
+ QSet<qint32> handledNodes;
+ QmlObjectNode sceneEnv;
+ for (const AbstractProperty &prop : propertyList) {
+ ModelNode node = prop.parentModelNode();
+ const qint32 id = node.internalId();
+ if (handledNodes.contains(id))
+ continue;
+
+ handledNodes.insert(id);
+ if (!node.metaInfo().isQtQuick3DSceneEnvironment())
+ continue;
+
+ if (!sceneEnv.isValid())
+ sceneEnv = currentSceneEnv();
+ if (sceneEnv == node) {
+ storeCurrentSceneEnvironment();
+ break;
+ }
+ }
+}
+
void Edit3DView::showContextMenu()
{
// If request for context menu is still pending, skip for now
@@ -736,32 +761,6 @@ void Edit3DView::showContextMenu()
void Edit3DView::setFlyMode(bool enabled)
{
emitView3DAction(View3DActionType::FlyModeToggle, enabled);
-
- // Disable any actions with conflicting hotkeys
- if (enabled) {
- m_flyModeDisabledActions.clear();
- const QList<QKeySequence> controlKeys = { Qt::Key_W, Qt::Key_A, Qt::Key_S,
- Qt::Key_D, Qt::Key_Q, Qt::Key_E,
- Qt::Key_Up, Qt::Key_Down, Qt::Key_Left,
- Qt::Key_Right, Qt::Key_PageDown, Qt::Key_PageUp};
- for (auto i = m_edit3DActions.cbegin(), end = m_edit3DActions.cend(); i != end; ++i) {
- for (const QKeySequence &controlKey : controlKeys) {
- if (Core::Command *cmd = m_edit3DWidget->actionToCommandHash().value(i.value()->action())) {
- if (cmd->keySequence().matches(controlKey) == QKeySequence::ExactMatch) {
- if (i.value()->action()->isEnabled()) {
- m_flyModeDisabledActions.append(i.value());
- i.value()->action()->setEnabled(false);
- }
- break;
- }
- }
- }
- }
- } else {
- for (Edit3DAction *action : std::as_const(m_flyModeDisabledActions))
- action->action()->setEnabled(true);
- m_flyModeDisabledActions.clear();
- }
}
void Edit3DView::syncSnapAuxPropsToSettings()
@@ -831,6 +830,75 @@ void Edit3DView::syncCameraSpeedToNewView()
setCameraSpeedAuxData(speed, multiplier);
}
+QmlObjectNode Edit3DView::currentSceneEnv()
+{
+ PropertyName envProp{"environment"};
+ ModelNode checkNode = Utils3D::active3DSceneNode(this);
+ while (checkNode.isValid()) {
+ if (checkNode.metaInfo().isQtQuick3DView3D()) {
+ QmlObjectNode sceneEnvNode = QmlItemNode(checkNode).bindingProperty(envProp)
+ .resolveToModelNode();
+ if (sceneEnvNode.isValid())
+ return sceneEnvNode;
+ break;
+ }
+ if (checkNode.hasParentProperty())
+ checkNode = checkNode.parentProperty().parentModelNode();
+ else
+ break;
+ }
+ return {};
+}
+
+void Edit3DView::storeCurrentSceneEnvironment()
+{
+ // If current active scene has scene environment, store relevant properties
+ QmlObjectNode sceneEnvNode = currentSceneEnv();
+ if (sceneEnvNode.isValid()) {
+ QVariantMap lastSceneEnvData;
+
+ auto insertPropValue = [](const PropertyName prop, const QmlObjectNode &node,
+ QVariantMap &map) {
+ if (!node.hasProperty(prop))
+ return;
+
+ map.insert(QString::fromUtf8(prop), node.modelValue(prop));
+ };
+
+ auto insertTextureProps = [&](const PropertyName prop) {
+ // For now we just grab the absolute path of texture source for simplicity
+ if (!sceneEnvNode.hasProperty(prop))
+ return;
+
+ QmlObjectNode bindNode = QmlItemNode(sceneEnvNode).bindingProperty(prop)
+ .resolveToModelNode();
+ if (bindNode.isValid()) {
+ QVariantMap props;
+ const PropertyName sourceProp = "source";
+ if (bindNode.hasProperty(sourceProp)) {
+ Utils::FilePath qmlPath = Utils::FilePath::fromUrl(
+ model()->fileUrl()).absolutePath();
+ Utils::FilePath sourcePath = Utils::FilePath::fromUrl(
+ bindNode.modelValue(sourceProp).toUrl());
+
+ sourcePath = qmlPath.resolvePath(sourcePath);
+
+ props.insert(QString::fromUtf8(sourceProp),
+ sourcePath.absoluteFilePath().toUrl());
+ }
+ lastSceneEnvData.insert(QString::fromUtf8(prop), props);
+ }
+ };
+
+ insertPropValue("backgroundMode", sceneEnvNode, lastSceneEnvData);
+ insertPropValue("clearColor", sceneEnvNode, lastSceneEnvData);
+ insertTextureProps("lightProbe");
+ insertTextureProps("skyBoxCubeMap");
+
+ emitView3DAction(View3DActionType::SetLastSceneEnvData, lastSceneEnvData);
+ }
+}
+
const QList<Edit3DView::SplitToolState> &Edit3DView::splitToolStates() const
{
return m_splitToolStates;
@@ -978,6 +1046,18 @@ void Edit3DView::createEdit3DActions()
nullptr,
QCoreApplication::translate("ShowGridAction", "Toggle the visibility of the helper grid."));
+ m_showLookAtAction = std::make_unique<Edit3DAction>(
+ QmlDesigner::Constants::EDIT3D_EDIT_SHOW_LOOKAT,
+ View3DActionType::ShowLookAt,
+ QCoreApplication::translate("ShowLookAtAction", "Show Look-at"),
+ QKeySequence(Qt::Key_L),
+ true,
+ true,
+ QIcon(),
+ this,
+ nullptr,
+ QCoreApplication::translate("ShowLookAtAction", "Toggle the visibility of the edit camera look-at indicator."));
+
m_showSelectionBoxAction = std::make_unique<Edit3DAction>(
QmlDesigner::Constants::EDIT3D_EDIT_SHOW_SELECTION_BOX,
View3DActionType::ShowSelectionBox,
@@ -1281,6 +1361,7 @@ void Edit3DView::createEdit3DActions()
m_rightActions << m_resetAction.get();
m_visibilityToggleActions << m_showGridAction.get();
+ m_visibilityToggleActions << m_showLookAtAction.get();
m_visibilityToggleActions << m_showSelectionBoxAction.get();
m_visibilityToggleActions << m_showIconGizmoAction.get();
m_visibilityToggleActions << m_showCameraFrustumAction.get();
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h
index ade2ef6a8f..755efc0ae3 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h
@@ -8,6 +8,7 @@
#include <abstractview.h>
#include <modelcache.h>
+#include <qmlobjectnode.h>
#include <QImage>
#include <QPointer>
@@ -59,6 +60,11 @@ public:
PropertyChangeFlags propertyChange) override;
void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty,
PropertyChangeFlags propertyChange) override;
+ void propertiesRemoved(const QList<AbstractProperty> &propertyList) override;
+ void bindingPropertiesChanged(const QList<BindingProperty> &propertyList,
+ PropertyChangeFlags propertyChange) override;
+ void variantPropertiesChanged(const QList<VariantProperty> &propertyList,
+ PropertyChangeFlags propertyChange) override;
void sendInputEvent(QEvent *e) const;
void edit3DViewResized(const QSize &size) const;
@@ -127,9 +133,14 @@ private:
void createSyncEnvBackgroundAction();
void createSeekerSliderAction();
void syncCameraSpeedToNewView();
+ QmlObjectNode currentSceneEnv();
+ void storeCurrentSceneEnvironment();
QPoint resolveToolbarPopupPos(Edit3DAction *action) const;
+ template<typename T, typename = typename std::enable_if<std::is_base_of<AbstractProperty , T>::value>::type>
+ void maybeStoreCurrentSceneEnvironment(const QList<T> &propertyList);
+
QPointer<Edit3DWidget> m_edit3DWidget;
QVector<Edit3DAction *> m_leftActions;
QVector<Edit3DAction *> m_rightActions;
@@ -148,6 +159,7 @@ private:
std::unique_ptr<Edit3DAction> m_orientationModeAction;
std::unique_ptr<Edit3DAction> m_editLightAction;
std::unique_ptr<Edit3DAction> m_showGridAction;
+ std::unique_ptr<Edit3DAction> m_showLookAtAction;
std::unique_ptr<Edit3DAction> m_showSelectionBoxAction;
std::unique_ptr<Edit3DAction> m_showIconGizmoAction;
std::unique_ptr<Edit3DAction> m_showCameraFrustumAction;
@@ -187,7 +199,6 @@ private:
int m_activeSplit = 0;
QList<SplitToolState> m_splitToolStates;
- QList<Edit3DAction *> m_flyModeDisabledActions;
ModelNode m_contextMenuPendingNode;
ModelNode m_pickView3dNode;
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
index 6f1cf2e183..f6bbf8d794 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
@@ -2,37 +2,41 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "edit3dwidget.h"
-#include "designdocument.h"
-#include "designericons.h"
+
#include "edit3dactions.h"
#include "edit3dcanvas.h"
#include "edit3dtoolbarmenu.h"
#include "edit3dview.h"
-#include "externaldependenciesinterface.h"
-#include "materialutils.h"
-#include "metainfo.h"
-#include "modelnodeoperations.h"
-#include "nodeabstractproperty.h"
-#include "nodehints.h"
-#include "qmldesignerconstants.h"
-#include "qmldesignerplugin.h"
-#include "qmleditormenu.h"
-#include "qmlvisualnode.h"
-#include "viewmanager.h"
-#include <utils3d.h>
#include <auxiliarydataproperties.h>
#include <designeractionmanager.h>
+#include <designdocument.h>
+#include <designericons.h>
#include <designermcumanager.h>
+#include <externaldependenciesinterface.h>
+#include <generatedcomponentutils.h>
#include <import.h>
-#include <model/modelutils.h>
+#include <materialutils.h>
+#include <metainfo.h>
+#include <modelnodeoperations.h>
+#include <nodeabstractproperty.h>
+#include <nodehints.h>
#include <nodeinstanceview.h>
+#include <qmldesignerconstants.h>
+#include <qmldesignerplugin.h>
+#include <qmleditormenu.h>
+#include <qmlvisualnode.h>
#include <seekerslider.h>
+#include <toolbox.h>
+#include <viewmanager.h>
+#include <utils3d.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/icore.h>
-#include <toolbox.h>
+
+#include <model/modelutils.h>
+
#include <utils/asset.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
@@ -359,6 +363,14 @@ void Edit3DWidget::createContextMenu()
resetAction->setToolTip(tr("Reset all shading options for all viewports."));
m_contextMenu->addSeparator();
+
+ m_addToContentLibAction = m_contextMenu->addAction(
+ contextIcon(DesignerIcons::CreateIcon), // TODO: placeholder icon
+ tr("Add to Content Library"), [&] {
+ view()->emitCustomNotification("add_3d_to_content_lib", {m_contextMenuTarget});
+ });
+
+ m_contextMenu->addSeparator();
}
bool Edit3DWidget::isPasteAvailable() const
@@ -402,7 +414,7 @@ void Edit3DWidget::showOnboardingLabel()
" in the"
" <b>Assets</b>"
" view.");
- text = labelText.arg(Utils::creatorTheme()->color(Utils::Theme::TextColorLink).name());
+ text = labelText.arg(Utils::creatorColor(Utils::Theme::TextColorLink).name());
} else {
text = tr("3D view is not supported in Qt5 projects.");
}
@@ -608,14 +620,18 @@ void Edit3DWidget::showBackgroundColorMenu(bool show, const QPoint &pos)
void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode, const QVector3D &pos3d)
{
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+
m_contextMenuTarget = modelNode;
m_contextMenuPos3d = pos3d;
const bool isModel = modelNode.metaInfo().isQtQuick3DModel();
+ const bool isNode = modelNode.metaInfo().isQtQuick3DNode();
const bool allowAlign = view()->edit3DAction(View3DActionType::AlignCamerasToView)->action()->isEnabled();
const bool isSingleComponent = view()->hasSingleSelectedModelNode() && modelNode.isComponent();
const bool anyNodeSelected = view()->hasSelectedModelNodes();
const bool selectionExcludingRoot = anyNodeSelected && !view()->rootModelNode().isSelected();
+ const bool isInBundle = modelNode.type().startsWith(compUtils.componentBundlesTypePrefix().toLatin1());
if (m_createSubMenu)
m_createSubMenu->setEnabled(!isSceneLocked());
@@ -633,6 +649,7 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode
m_toggleGroupAction->setEnabled(true);
m_bakeLightsAction->setVisible(view()->bakeLightsAction()->action()->isVisible());
m_bakeLightsAction->setEnabled(view()->bakeLightsAction()->action()->isEnabled());
+ m_addToContentLibAction->setEnabled(isNode && !isInBundle);
if (m_view) {
int idx = m_view->activeSplit();
@@ -685,7 +702,7 @@ void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent)
} else if (actionManager.externalDragHasSupportedAssets(dragEnterEvent->mimeData())
|| dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_MATERIAL)
|| dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_MATERIAL)
- || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_EFFECT)
+ || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_ITEM)
|| dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_TEXTURE)) {
if (Utils3D::active3DSceneNode(m_view).isValid())
dragEnterEvent->acceptProposedAction();
@@ -694,7 +711,7 @@ void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent)
if (!data.isEmpty()) {
QDataStream stream(data);
stream >> m_draggedEntry;
- if (NodeHints::fromItemLibraryEntry(m_draggedEntry).canBeDroppedInView3D())
+ if (NodeHints::fromItemLibraryEntry(m_draggedEntry, view()->model()).canBeDroppedInView3D())
dragEnterEvent->acceptProposedAction();
}
}
@@ -730,8 +747,8 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent)
return;
}
- // handle dropping bundle effects
- if (dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_EFFECT)) {
+ // handle dropping bundle items
+ if (dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_ITEM)) {
m_view->dropBundleEffect(pos);
m_view->model()->endDrag();
return;
@@ -770,9 +787,10 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent)
->documentManager()
.generatedComponentUtils()
.import3dTypePrefix();
- auto metaInfo = model->metaInfo(model->module(import3dTypePrefix), fileName.toUtf8());
+ auto moduleId = model->module(import3dTypePrefix, Storage::ModuleKind::QmlLibrary);
+ auto metaInfo = model->metaInfo(moduleId, fileName.toUtf8());
if (auto entries = metaInfo.itemLibrariesEntries(); entries.size()) {
- auto entry = ItemLibraryEntry{entries.front(), *model->projectStorage()};
+ auto entry = ItemLibraryEntry{entries.front()};
QmlVisualNode::createQml3DNode(view(), entry, m_canvas->activeScene(), {}, false);
}
}
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h
index 211b044d41..97c0469668 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h
@@ -100,6 +100,7 @@ private:
QPointer<QAction> m_selectParentAction;
QPointer<QAction> m_toggleGroupAction;
QPointer<QAction> m_wireFrameAction;
+ QPointer<QAction> m_addToContentLibAction;
QHash<int, QPointer<QAction>> m_matOverrideActions;
QPointer<QMenu> m_createSubMenu;
ModelNode m_contextMenuTarget;
diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp
index 0b7d199b50..a6494811b6 100644
--- a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp
@@ -206,9 +206,16 @@ static ItemLibraryEntry itemLibraryEntryFromMimeData(const QMimeData *mimeData)
return itemLibraryEntry;
}
-static bool canBeDropped(const QMimeData *mimeData)
+static bool canBeDropped(const QMimeData *mimeData, Model *model)
{
- return NodeHints::fromItemLibraryEntry(itemLibraryEntryFromMimeData(mimeData)).canBeDroppedInFormEditor();
+#ifdef QDS_USE_PROJECTSTORAGE
+ auto itemLibraryEntry = itemLibraryEntryFromMimeData(mimeData);
+ NodeMetaInfo metaInfo{itemLibraryEntry.typeId(), model->projectStorage()};
+ return metaInfo.canBeDroppedInFormEditor() == FlagIs::True;
+#else
+ return NodeHints::fromItemLibraryEntry(itemLibraryEntryFromMimeData(mimeData), model)
+ .canBeDroppedInFormEditor();
+#endif
}
static bool hasItemLibraryInfo(const QMimeData *mimeData)
@@ -218,7 +225,7 @@ static bool hasItemLibraryInfo(const QMimeData *mimeData)
void DragTool::dropEvent(const QList<QGraphicsItem *> &itemList, QGraphicsSceneDragDropEvent *event)
{
- if (canBeDropped(event->mimeData())) {
+ if (canBeDropped(event->mimeData(), view()->model())) {
event->accept();
end(generateUseSnapping(event->modifiers()));
@@ -290,7 +297,7 @@ void DragTool::dropEvent(const QList<QGraphicsItem *> &itemList, QGraphicsSceneD
void DragTool::dragEnterEvent(const QList<QGraphicsItem *> &/*itemList*/, QGraphicsSceneDragDropEvent *event)
{
- if (canBeDropped(event->mimeData())) {
+ if (canBeDropped(event->mimeData(), view()->model())) {
m_blockMove = false;
if (hasItemLibraryInfo(event->mimeData())) {
@@ -306,7 +313,7 @@ void DragTool::dragEnterEvent(const QList<QGraphicsItem *> &/*itemList*/, QGraph
void DragTool::dragLeaveEvent(const QList<QGraphicsItem *> &/*itemList*/, QGraphicsSceneDragDropEvent *event)
{
- if (canBeDropped(event->mimeData())) {
+ if (canBeDropped(event->mimeData(), view()->model())) {
event->accept();
m_moveManipulator.end();
@@ -363,10 +370,8 @@ void DragTool::dragMoveEvent(const QList<QGraphicsItem *> &itemList, QGraphicsSc
->data(Constants::MIME_TYPE_ASSETS)).split(',');
QString assetType = AssetsLibraryWidget::getAssetTypeAndData(assetPaths[0]).first;
- if (!m_blockMove
- && !m_isAborted
- && canBeDropped(event->mimeData())
- && assetType != Constants::MIME_TYPE_ASSET_EFFECT) {
+ if (!m_blockMove && !m_isAborted && canBeDropped(event->mimeData(), view()->model())
+ && assetType != Constants::MIME_TYPE_ASSET_EFFECT) {
event->accept();
if (!m_dragNodes.isEmpty()) {
if (targetContainerItem) {
diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.h b/src/plugins/qmldesigner/components/formeditor/dragtool.h
index 1cd2c9f487..c1d5626d28 100644
--- a/src/plugins/qmldesigner/components/formeditor/dragtool.h
+++ b/src/plugins/qmldesigner/components/formeditor/dragtool.h
@@ -7,7 +7,6 @@
#include "selectionindicator.h"
#include <QObject>
-#include <QScopedPointer>
#include <QPointer>
namespace QmlDesigner {
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp
index 35a48a6b6d..dd239dd966 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp
@@ -302,9 +302,9 @@ QGraphicsItem *FormEditorAnnotationIcon::createCommentBubble(QRectF rect, const
const QString &author, const QString &text,
const QString &date, QGraphicsItem *parent)
{
- static QColor textColor = Utils::creatorTheme()->color(Utils::Theme::DStextColor);
- static QColor backgroundColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColorDarker);
- static QColor frameColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColor);
+ static QColor textColor = Utils::creatorColor(Utils::Theme::DStextColor);
+ static QColor backgroundColor = Utils::creatorColor(Utils::Theme::QmlDesigner_BackgroundColorDarker);
+ static QColor frameColor = Utils::creatorColor(Utils::Theme::QmlDesigner_BackgroundColor);
QFont font;
font.setBold(true);
@@ -405,9 +405,9 @@ QGraphicsItem *FormEditorAnnotationIcon::createCommentBubble(QRectF rect, const
QGraphicsItem *FormEditorAnnotationIcon::createTitleBubble(const QRectF &rect, const QString &text, QGraphicsItem *parent)
{
- static QColor textColor = Utils::creatorTheme()->color(Utils::Theme::DStextColor);
- static QColor backgroundColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColorDarker);
- static QColor frameColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColor);
+ static QColor textColor = Utils::creatorColor(Utils::Theme::DStextColor);
+ static QColor backgroundColor = Utils::creatorColor(Utils::Theme::QmlDesigner_BackgroundColorDarker);
+ static QColor frameColor = Utils::creatorColor(Utils::Theme::QmlDesigner_BackgroundColor);
QFont font;
font.setBold(true);
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp
index 9549ce9dd4..4bd0b08c0a 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp
@@ -215,7 +215,7 @@ void FormEditorGraphicsView::drawBackground(QPainter *painter, const QRectF &rec
painter->fillRect(rectangle.intersected(rootItemRect()), backgroundBrush());
}
- QPen pen(Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor));
+ QPen pen(Utils::creatorColor(Utils::Theme::QmlDesigner_FormEditorSelectionColor));
pen.setStyle(Qt::DotLine);
pen.setWidth(1);
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
index 12da85e2c4..b3df6b8fe7 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
@@ -48,23 +48,19 @@ void drawIcon(QPainter *painter,
int iconSize,
const QColor &penColor)
{
- static QFontDatabase a;
-
const QString fontName = "qtds_propertyIconFont.ttf";
- Q_ASSERT(a.hasFamily(fontName));
+ QTC_ASSERT(QFontDatabase::hasFamily(fontName), return);
- if (a.hasFamily(fontName)) {
- QFont font(fontName);
- font.setPixelSize(fontSize);
+ QFont font(fontName);
+ font.setPixelSize(fontSize);
- painter->save();
- painter->setPen(penColor);
- painter->setFont(font);
- painter->drawText(QRectF(x, y, iconSize, iconSize), iconSymbol);
+ painter->save();
+ painter->setPen(penColor);
+ painter->setFont(font);
+ painter->drawText(QRectF(x, y, iconSize, iconSize), iconSymbol);
- painter->restore();
- }
+ painter->restore();
}
FormEditorScene *FormEditorItem::scene() const {
@@ -309,7 +305,7 @@ void FormEditorItem::paintBoundingRect(QPainter *painter) const
pen.setJoinStyle(Qt::MiterJoin);
const QColor frameColor(0xaa, 0xaa, 0xaa);
- static const QColor selectionColor = Utils::creatorTheme()->color(
+ static const QColor selectionColor = Utils::creatorColor(
Utils::Theme::QmlDesigner_FormEditorSelectionColor);
if (scene()->showBoundingRects()) {
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditortoolbutton.cpp b/src/plugins/qmldesigner/components/formeditor/formeditortoolbutton.cpp
index 555d0d90e3..f3aa96ada4 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditortoolbutton.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditortoolbutton.cpp
@@ -42,7 +42,7 @@ void FormEditorToolButton::paint(QPainter *painter, const QStyleOptionGraphicsIt
QRectF adjustedRect(size().width() - toolButtonSize, size().height() - toolButtonSize, toolButtonSize, toolButtonSize);
painter->setPen(Qt::NoPen);
- static QColor selectionColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor);
+ static QColor selectionColor = Utils::creatorColor(Utils::Theme::QmlDesigner_FormEditorSelectionColor);
if (m_state == Hovered)
painter->setBrush(selectionColor.lighter(110));
diff --git a/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp b/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp
index 0f5b5f4438..45d26f831e 100644
--- a/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp
@@ -78,7 +78,7 @@ void SelectionIndicator::setItems(const QList<FormEditorItem*> &itemList)
{
clear();
- static QColor selectionColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor);
+ static QColor selectionColor = Utils::creatorColor(Utils::Theme::QmlDesigner_FormEditorSelectionColor);
for (FormEditorItem *item : itemList) {
if (!item->qmlItemNode().isValid())
@@ -119,7 +119,7 @@ void SelectionIndicator::setItems(const QList<FormEditorItem*> &itemList)
m_annotationItem = nullptr;
}
- static QColor textColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorForegroundColor);
+ static QColor textColor = Utils::creatorColor(Utils::Theme::QmlDesigner_FormEditorForegroundColor);
textItem->setDefaultTextColor(textColor);
QPolygonF labelPolygon = boundingRectInLayerItemSpaceForItem(selectedItem, m_layerItem.data());
diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp
index 804ac076e6..aa2dfd3b28 100644
--- a/src/plugins/qmldesigner/components/integration/designdocument.cpp
+++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp
@@ -150,7 +150,10 @@ bool DesignDocument::loadInFileComponent(const ModelNode &componentNode)
if (!componentNode.isRootNode()) {
//change to subcomponent model
- changeToInFileComponentModel(createComponentTextModifier(m_documentTextModifier.data(), rewriterView(), componentText, componentNode));
+ changeToInFileComponentModel(createComponentTextModifier(m_documentTextModifier.get(),
+ rewriterView(),
+ componentText,
+ componentNode));
}
return true;
@@ -281,11 +284,10 @@ void DesignDocument::moveNodesToPosition(const QList<ModelNode> &nodes, const st
parentProperty.reparentHere(pastedNode);
QmlVisualNode visualNode(pastedNode);
- if (!firstVisualNode.has_value() && visualNode.isValid()){
+ if (!firstVisualNode && visualNode) {
firstVisualNode = visualNode;
- translationVect = (position.has_value() && firstVisualNode.has_value())
- ? position.value() - firstVisualNode->position()
- : QVector3D();
+ translationVect = (position && firstVisualNode) ? *position - firstVisualNode->position()
+ : QVector3D();
}
visualNode.translate(translationVect);
}
@@ -377,9 +379,12 @@ void DesignDocument::loadDocument(QPlainTextEdit *edit)
m_documentTextModifier.reset(new BaseTextEditModifier(qobject_cast<TextEditor::TextEditorWidget *>(plainTextEdit())));
- connect(m_documentTextModifier.data(), &TextModifier::textChanged, this, &DesignDocument::updateQrcFiles);
+ connect(m_documentTextModifier.get(),
+ &TextModifier::textChanged,
+ this,
+ &DesignDocument::updateQrcFiles);
- m_rewriterView->setTextModifier(m_documentTextModifier.data());
+ m_rewriterView->setTextModifier(m_documentTextModifier.get());
m_inFileComponentTextModifier.reset();
@@ -399,7 +404,7 @@ void DesignDocument::changeToDocumentModel()
if (edit)
edit->document()->clearUndoRedoStacks();
- m_rewriterView->setTextModifier(m_documentTextModifier.data());
+ m_rewriterView->setTextModifier(m_documentTextModifier.get());
m_inFileComponentModel.reset();
m_inFileComponentTextModifier.reset();
@@ -432,7 +437,7 @@ bool DesignDocument::hasProject() const
void DesignDocument::setModified()
{
- if (!m_documentTextModifier.isNull())
+ if (m_documentTextModifier)
m_documentTextModifier->textDocument()->setModified(true);
}
@@ -448,7 +453,7 @@ void DesignDocument::changeToInFileComponentModel(ComponentTextModifier *textMod
m_inFileComponentModel = createInFileComponentModel();
- m_rewriterView->setTextModifier(m_inFileComponentTextModifier.data());
+ m_rewriterView->setTextModifier(m_inFileComponentTextModifier.get());
viewManager().attachRewriterView();
viewManager().attachViewsExceptRewriterAndComponetView();
@@ -675,7 +680,7 @@ void DesignDocument::selectAll()
RewriterView *DesignDocument::rewriterView() const
{
- return m_rewriterView.data();
+ return m_rewriterView.get();
}
void DesignDocument::setEditor(Core::IEditor *editor)
diff --git a/src/plugins/qmldesigner/components/integration/designdocument.h b/src/plugins/qmldesigner/components/integration/designdocument.h
index 52089d67c1..1f67ff4b30 100644
--- a/src/plugins/qmldesigner/components/integration/designdocument.h
+++ b/src/plugins/qmldesigner/components/integration/designdocument.h
@@ -16,9 +16,10 @@
#include <QObject>
#include <QString>
-
#include <QStackedWidget>
+#include <memory>
+
QT_BEGIN_NAMESPACE
class QPlainTextEdit;
QT_END_NAMESPACE
@@ -143,12 +144,12 @@ private: // variables
ModelPointer m_documentModel;
ModelPointer m_inFileComponentModel;
QPointer<Core::IEditor> m_textEditor;
- QScopedPointer<BaseTextEditModifier> m_documentTextModifier;
- QScopedPointer<ComponentTextModifier> m_inFileComponentTextModifier;
+ std::unique_ptr<BaseTextEditModifier> m_documentTextModifier;
+ std::unique_ptr<ComponentTextModifier> m_inFileComponentTextModifier;
#ifndef QDS_USE_PROJECTSTORAGE
- QScopedPointer<SubComponentManager> m_subComponentManager;
+ std::unique_ptr<SubComponentManager> m_subComponentManager;
#endif
- QScopedPointer<RewriterView> m_rewriterView;
+ std::unique_ptr<RewriterView> m_rewriterView;
bool m_documentLoaded;
ProjectExplorer::Target *m_currentTarget;
ProjectStorageDependencies m_projectStorageDependencies;
diff --git a/src/plugins/qmldesigner/components/integration/designdocumentview.cpp b/src/plugins/qmldesigner/components/integration/designdocumentview.cpp
index 6ef95bf4c4..d97b9ff06f 100644
--- a/src/plugins/qmldesigner/components/integration/designdocumentview.cpp
+++ b/src/plugins/qmldesigner/components/integration/designdocumentview.cpp
@@ -23,6 +23,8 @@
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
+#include <memory>
+
namespace QmlDesigner {
DesignDocumentView::DesignDocumentView(ExternalDependenciesInterface &externalDependencies)
@@ -116,14 +118,14 @@ QString DesignDocumentView::toText() const
textEdit.setPlainText(imports + QStringLiteral("Item {\n}\n"));
NotIndentingTextEditModifier modifier(&textEdit);
- QScopedPointer<RewriterView> rewriterView(
- new RewriterView(externalDependencies(), RewriterView::Amend));
+ std::unique_ptr<RewriterView> rewriterView = std::make_unique<RewriterView>(externalDependencies(),
+ RewriterView::Amend);
rewriterView->setCheckSemanticErrors(false);
rewriterView->setPossibleImportsEnabled(false);
rewriterView->setTextModifier(&modifier);
- outputModel->setRewriterView(rewriterView.data());
+ outputModel->setRewriterView(rewriterView.get());
- ModelMerger merger(rewriterView.data());
+ ModelMerger merger(rewriterView.get());
merger.replaceModel(rootModelNode());
diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp
new file mode 100644
index 0000000000..608bf42eb3
--- /dev/null
+++ b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp
@@ -0,0 +1,82 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "import3dcanvas.h"
+
+#include <QImage>
+#include <QLinearGradient>
+#include <QMouseEvent>
+#include <QPainter>
+
+namespace QmlDesigner {
+
+static QImage createGradientImage(int width, int height) {
+ QImage image(width, height, QImage::Format_ARGB32_Premultiplied);
+
+ QLinearGradient gradient(0, 0, 0, height);
+ gradient.setColorAt(0, QColor(0x999999));
+ gradient.setColorAt(1, QColor(0x222222));
+
+ QPainter painter(&image);
+ painter.fillRect(0, 0, width, height, gradient);
+
+ return image;
+}
+
+Import3dCanvas::Import3dCanvas(QWidget *parent)
+ : QWidget(parent)
+{
+}
+
+void Import3dCanvas::updateRenderImage(const QImage &img)
+{
+ m_image = img;
+ update();
+}
+
+void Import3dCanvas::paintEvent([[maybe_unused]] QPaintEvent *e)
+{
+ QWidget::paintEvent(e);
+
+ QPainter painter(this);
+
+ if (m_image.isNull()) {
+ QImage image = createGradientImage(width(), height());
+ painter.drawImage(rect(), image, QRect(0, 0, image.width(), image.height()));
+ } else {
+ painter.drawImage(rect(), m_image, QRect(0, 0, m_image.width(), m_image.height()));
+ }
+}
+
+void Import3dCanvas::resizeEvent(QResizeEvent *)
+{
+ emit requestImageUpdate();
+}
+
+void Import3dCanvas::mousePressEvent(QMouseEvent *e)
+{
+ if (e->buttons() == Qt::LeftButton)
+ m_dragPos = e->position();
+ else
+ m_dragPos = {};
+}
+
+void Import3dCanvas::mouseReleaseEvent(QMouseEvent *)
+{
+ m_dragPos = {};
+}
+
+void Import3dCanvas::mouseMoveEvent(QMouseEvent *e)
+{
+ if (m_dragPos.isNull())
+ return;
+
+ const QPointF curPos = e->position();
+ const QPointF delta = curPos - m_dragPos;
+
+ m_dragPos = curPos;
+
+ emit requestRotation(delta);
+}
+
+}
diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h
new file mode 100644
index 0000000000..72fb19acff
--- /dev/null
+++ b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#pragma once
+
+#include <QEvent>
+#include <QImage>
+#include <QPointF>
+#include <QWidget>
+
+namespace QmlDesigner {
+
+class Import3dCanvas : public QWidget
+{
+ Q_OBJECT
+
+public:
+ Import3dCanvas(QWidget *parent);
+
+ void updateRenderImage(const QImage &img);
+
+signals:
+ void requestImageUpdate();
+ void requestRotation(const QPointF &delta);
+
+protected:
+ void paintEvent(QPaintEvent *e) override;
+ void resizeEvent(QResizeEvent *e) override;
+ void mousePressEvent(QMouseEvent *e) override;
+ void mouseReleaseEvent(QMouseEvent *e) override;
+ void mouseMoveEvent(QMouseEvent *e) override;
+
+private:
+ QImage m_image;
+ QPointF m_dragPos;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp
new file mode 100644
index 0000000000..4c455e3c6d
--- /dev/null
+++ b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp
@@ -0,0 +1,47 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "import3dconnectionmanager.h"
+
+#include <imagecontainer.h>
+#include <puppettocreatorcommand.h>
+
+#include <QImage>
+
+namespace QmlDesigner {
+
+Import3dConnectionManager::Import3dConnectionManager()
+{
+ connections().clear(); // Remove default interactive puppets
+ connections().emplace_back("Import 3D", "import3dmode");
+}
+
+void Import3dConnectionManager::setPreviewImageCallback(ImageCallback callback)
+{
+ m_previewImageCallback = std::move(callback);
+}
+
+void Import3dConnectionManager::dispatchCommand(const QVariant &command,
+ ConnectionManagerInterface::Connection &connection)
+{
+ static const int commandType = QMetaType::type("PuppetToCreatorCommand");
+
+ if (command.typeId() == commandType) {
+ auto cmd = command.value<PuppetToCreatorCommand>();
+ switch (cmd.type()) {
+ case PuppetToCreatorCommand::Import3DPreviewImage: {
+ ImageContainer container = qvariant_cast<ImageContainer>(cmd.data());
+ QImage image = container.image();
+ if (!image.isNull())
+ m_previewImageCallback(image);
+ break;
+ }
+ default:
+ break;
+ }
+ } else {
+ InteractiveConnectionManager::dispatchCommand(command, connection);
+ }
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h
new file mode 100644
index 0000000000..458d612ad2
--- /dev/null
+++ b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "interactiveconnectionmanager.h"
+
+QT_FORWARD_DECLARE_CLASS(QImage)
+
+namespace QmlDesigner {
+
+class Import3dConnectionManager : public InteractiveConnectionManager
+{
+public:
+ using ImageCallback = std::function<void(const QImage &)>;
+
+ Import3dConnectionManager();
+
+ void setPreviewImageCallback(ImageCallback callback);
+
+protected:
+ void dispatchCommand(const QVariant &command, Connection &connection) override;
+
+private:
+ ImageCallback m_previewImageCallback;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
index 2c69072602..271410233b 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
@@ -4,15 +4,19 @@
#include "itemlibraryassetimportdialog.h"
#include "ui_itemlibraryassetimportdialog.h"
+#include "import3dcanvas.h"
+#include "import3dconnectionmanager.h"
+
#include <model.h>
#include <model/modelutils.h>
+#include <nodeinstanceview.h>
#include <nodemetainfo.h>
#include <qmldesignerconstants.h>
#include <qmldesignerplugin.h>
+#include <rewriterview.h>
#include <variantproperty.h>
#include <theme.h>
-#include <utils/filepath.h>
#include <utils/outputformatter.h>
#include <projectexplorer/project.h>
@@ -74,9 +78,10 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
const QStringList &importFiles, const QString &defaulTargetDirectory,
const QVariantMap &supportedExts, const QVariantMap &supportedOpts,
const QJsonObject &defaultOpts, const QSet<QString> &preselectedFilesForOverwrite,
- QWidget *parent)
+ AbstractView *view, QWidget *parent)
: QDialog(parent)
, ui(new Ui::ItemLibraryAssetImportDialog)
+ , m_view(view)
, m_importer(this)
, m_preselectedFilesForOverwrite(preselectedFilesForOverwrite)
{
@@ -107,17 +112,15 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
if (m_quick3DFiles.size() != importFiles.size())
addWarning("Cannot import 3D and other assets simultaneously. Skipping non-3D assets.");
- ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Import"));
- connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked,
- this, &ItemLibraryAssetImportDialog::onImport);
+ connect(ui->importButton, &QPushButton::clicked, this, &ItemLibraryAssetImportDialog::onImport);
- ui->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
+ ui->importButton->setDefault(true);
ui->advancedSettingsButton->setStyleSheet(
"QPushButton#advancedSettingsButton {background-color: transparent}");
ui->advancedSettingsButton->setStyleSheet(
QString("QPushButton { border: none; color :%1 }").arg(
- Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_HighlightColor).name()));
+ Utils::creatorColor(Utils::Theme::QmlDesigner_HighlightColor).name()));
QStringList importPaths;
auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
@@ -210,10 +213,14 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
ui->tabWidget->setCurrentIndex(0);
}
- connect(ui->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked,
+ connect(ui->closeButton, &QPushButton::clicked,
this, &ItemLibraryAssetImportDialog::onClose);
connect(ui->tabWidget, &QTabWidget::currentChanged,
this, &ItemLibraryAssetImportDialog::updateUi);
+ connect(canvas(), &Import3dCanvas::requestImageUpdate,
+ this, &ItemLibraryAssetImportDialog::onRequestImageUpdate);
+ connect(canvas(), &Import3dCanvas::requestRotation,
+ this, &ItemLibraryAssetImportDialog::onRequestRotation);
connect(&m_importer, &ItemLibraryAssetImporter::errorReported,
this, &ItemLibraryAssetImportDialog::addError);
@@ -227,23 +234,35 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
this, &ItemLibraryAssetImportDialog::onImportFinished);
connect(&m_importer, &ItemLibraryAssetImporter::progressChanged,
this, &ItemLibraryAssetImportDialog::setImportProgress);
-
- addInfo(tr("Select import options and press \"Import\" to import the following files:"));
- for (const auto &file : std::as_const(m_quick3DFiles))
- addInfo(file);
+ connect(&m_importer, &ItemLibraryAssetImporter::importReadyForPreview,
+ this, &ItemLibraryAssetImportDialog::onImportReadyForPreview);
connect(ui->advancedSettingsButton, &QPushButton::clicked,
this, &ItemLibraryAssetImportDialog::toggleAdvanced);
QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::updateUi);
+
+ if (m_quick3DFiles.size() != 1) {
+ addInfo(tr("Select import options and press \"Import\" to import the following files:"));
+ } else {
+ addInfo(tr("Importing:"));
+ QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::onImport);
+ }
+
+ for (const auto &file : std::as_const(m_quick3DFiles))
+ addInfo(file);
+
+ updateImportButtonState();
}
ItemLibraryAssetImportDialog::~ItemLibraryAssetImportDialog()
{
+ cleanupPreviewPuppet();
delete ui;
}
-void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode,
+void ItemLibraryAssetImportDialog::updateImport(AbstractView *view,
+ const ModelNode &updateNode,
const QVariantMap &supportedExts,
const QVariantMap &supportedOpts)
{
@@ -332,7 +351,8 @@ void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode,
{sourceInfo.absoluteFilePath()},
node.model()->fileUrl().toLocalFile(),
supportedExts, supportedOpts, options,
- preselectedFiles, Core::ICore::dialogParent());
+ preselectedFiles, view,
+ Core::ICore::dialogParent());
importDlg->show();
} else {
@@ -481,6 +501,7 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid(
QJsonValue value(optCheck->isChecked());
optObj.insert("value", value);
m_importOptions[optionsIndex].insert(optKey, optObj);
+ updateImportButtonState();
});
} else {
// Simple options also exist in advanced, so don't connect simple controls directly
@@ -488,13 +509,17 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid(
auto *advCheck = qobject_cast<QCheckBox *>(
m_labelToControlWidgetMaps[optionsIndex].value(optKey));
if (advCheck) {
- QObject::connect(optCheck, &QCheckBox::toggled, this, [optCheck, advCheck]() {
- if (advCheck->isChecked() != optCheck->isChecked())
+ QObject::connect(optCheck, &QCheckBox::toggled, this, [this, optCheck, advCheck]() {
+ if (advCheck->isChecked() != optCheck->isChecked()) {
advCheck->setChecked(optCheck->isChecked());
+ updateImportButtonState();
+ }
});
- QObject::connect(advCheck, &QCheckBox::toggled, this, [optCheck, advCheck]() {
- if (advCheck->isChecked() != optCheck->isChecked())
+ QObject::connect(advCheck, &QCheckBox::toggled, this, [this, optCheck, advCheck]() {
+ if (advCheck->isChecked() != optCheck->isChecked()) {
optCheck->setChecked(advCheck->isChecked());
+ updateImportButtonState();
+ }
});
}
}
@@ -530,6 +555,7 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid(
QJsonValue value(optSpin->value());
optObj.insert("value", value);
m_importOptions[optionsIndex].insert(optKey, optObj);
+ updateImportButtonState();
});
} else {
auto *advSpin = qobject_cast<QDoubleSpinBox *>(
@@ -537,14 +563,18 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid(
if (advSpin) {
// Connect corresponding advanced control
QObject::connect(optSpin, &QDoubleSpinBox::valueChanged,
- this, [optSpin, advSpin] {
- if (advSpin->value() != optSpin->value())
+ this, [this, optSpin, advSpin] {
+ if (advSpin->value() != optSpin->value()) {
advSpin->setValue(optSpin->value());
+ updateImportButtonState();
+ }
});
QObject::connect(advSpin, &QDoubleSpinBox::valueChanged,
- this, [optSpin, advSpin] {
- if (advSpin->value() != optSpin->value())
+ this, [this, optSpin, advSpin] {
+ if (advSpin->value() != optSpin->value()) {
optSpin->setValue(advSpin->value());
+ updateImportButtonState();
+ }
});
}
}
@@ -829,6 +859,145 @@ bool ItemLibraryAssetImportDialog::isHiddenOption(const QString &id)
return hiddenOptions.contains(id);
}
+void ItemLibraryAssetImportDialog::startPreview()
+{
+ cleanupPreviewPuppet();
+
+ // Preview is done via custom QML file added into the temporary folder of the preview
+ QString previewQml =
+R"(
+import QtQuick
+import QtQuick3D
+
+Rectangle {
+ id: root
+ width: %1
+ height: %2
+
+ property alias sceneNode: sceneNode
+ property alias view3d: view3d
+ property string extents
+ property string sceneModelName: "%3"
+
+ gradient: Gradient {
+ GradientStop { position: 1.0; color: "#222222" }
+ GradientStop { position: 0.0; color: "#999999" }
+ }
+
+ View3D {
+ id: view3d
+ anchors.fill: parent
+ camera: viewCamera
+
+ environment: SceneEnvironment {
+ antialiasingMode: SceneEnvironment.MSAA
+ antialiasingQuality: SceneEnvironment.VeryHigh
+ }
+
+ PerspectiveCamera {
+ id: viewCamera
+ x: 600
+ y: 600
+ z: 600
+ eulerRotation.x: -45
+ eulerRotation.y: -45
+ clipFar: 100000
+ clipNear: 10
+ }
+
+ DirectionalLight {
+ rotation: viewCamera.rotation
+ }
+
+ Node {
+ id: sceneNode
+ }
+ }
+
+ Text {
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ color: "white"
+ text: root.extents
+ font.pixelSize: 14
+ }
+}
+)";
+
+ QSize size = canvas()->size();
+ previewQml = previewQml.arg(size.width()).arg(size.height()).arg(m_previewCompName);
+
+ m_previewFile.writeFileContents(previewQml.toUtf8());
+
+ if (!m_previewFile.exists()) {
+ addWarning("Failed to write preview file.");
+ return;
+ }
+
+ m_connectionManager = new Import3dConnectionManager;
+ m_rewriterView = new RewriterView{m_view->externalDependencies(), RewriterView::Amend};
+ m_nodeInstanceView = new NodeInstanceView{*m_connectionManager, m_view->externalDependencies()};
+
+#ifdef QDS_USE_PROJECTSTORAGE
+ m_model = m_view->model()->createModel("Item");
+#else
+ m_model = QmlDesigner::Model::create("QtQuick/Item", 2, 1);
+ m_model->setFileUrl(m_previewFile.toUrl());
+#endif
+
+ auto textDocument = std::make_unique<QTextDocument>(previewQml);
+ auto modifier = std::make_unique<NotIndentingTextEditModifier>(textDocument.get(),
+ QTextCursor{textDocument.get()});
+ m_rewriterView->setTextModifier(modifier.get());
+ m_model->setRewriterView(m_rewriterView);
+
+ if (!m_rewriterView->errors().isEmpty()) {
+ addWarning("Preview scene creation failed.");
+ cleanupPreviewPuppet();
+ return;
+ }
+
+ m_nodeInstanceView->setTarget(m_view->nodeInstanceView()->target());
+
+ auto previewImageCallback = [this](const QImage &image) {
+ canvas()->updateRenderImage(image);
+ };
+
+ auto crashCallback = [&] {
+ addWarning("Preview process crashed.");
+ cleanupPreviewPuppet();
+ };
+
+ m_connectionManager->setPreviewImageCallback(std::move(previewImageCallback));
+ m_nodeInstanceView->setCrashCallback(std::move(crashCallback));
+
+ m_model->setNodeInstanceView(m_nodeInstanceView);
+}
+
+void ItemLibraryAssetImportDialog::cleanupPreviewPuppet()
+{
+ if (m_model) {
+ m_model->setNodeInstanceView({});
+ m_model->setRewriterView({});
+ m_model.reset();
+ }
+
+ if (m_nodeInstanceView)
+ m_nodeInstanceView->setCrashCallback({});
+
+ if (m_connectionManager)
+ m_connectionManager->setPreviewImageCallback({});
+
+ delete m_rewriterView;
+ delete m_nodeInstanceView;
+ delete m_connectionManager;
+}
+
+Import3dCanvas *ItemLibraryAssetImportDialog::canvas()
+{
+ return ui->import3dcanvas;
+}
+
void ItemLibraryAssetImportDialog::resizeEvent(QResizeEvent *event)
{
m_dialogHeight = event->size().height();
@@ -837,8 +1006,13 @@ void ItemLibraryAssetImportDialog::resizeEvent(QResizeEvent *event)
void ItemLibraryAssetImportDialog::setCloseButtonState(bool importing)
{
- ui->buttonBox->button(QDialogButtonBox::Close)->setEnabled(true);
- ui->buttonBox->button(QDialogButtonBox::Close)->setText(importing ? tr("Cancel") : tr("Close"));
+ ui->closeButton->setEnabled(true);
+ ui->closeButton->setText(importing ? tr("Cancel") : tr("Close"));
+}
+
+void ItemLibraryAssetImportDialog::updateImportButtonState()
+{
+ ui->importButton->setText(m_previewOptions == m_importOptions ? tr("Accept") : tr("Import"));
}
void ItemLibraryAssetImportDialog::addError(const QString &error, const QString &srcPath)
@@ -860,14 +1034,25 @@ void ItemLibraryAssetImportDialog::addInfo(const QString &info, const QString &s
void ItemLibraryAssetImportDialog::onImport()
{
- ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+ ui->importButton->setEnabled(false);
+
+ if (!m_previewCompName.isEmpty() && m_previewOptions == m_importOptions) {
+ cleanupPreviewPuppet();
+ m_importer.finalizeQuick3DImport();
+ return;
+ }
+
setCloseButtonState(true);
ui->progressBar->setValue(0);
if (!m_quick3DFiles.isEmpty()) {
- m_importer.importQuick3D(m_quick3DFiles, m_quick3DImportPath,
- m_importOptions, m_extToImportOptionsMap,
- m_preselectedFilesForOverwrite);
+ if (!m_previewCompName.isEmpty()) {
+ m_importer.reImportQuick3D(m_previewCompName, m_importOptions);
+ } else {
+ m_importer.importQuick3D(m_quick3DFiles, m_quick3DImportPath,
+ m_importOptions, m_extToImportOptionsMap,
+ m_preselectedFilesForOverwrite);
+ }
}
}
@@ -881,10 +1066,37 @@ void ItemLibraryAssetImportDialog::setImportProgress(int value, const QString &t
ui->progressBar->setValue(value);
}
+void ItemLibraryAssetImportDialog::onImportReadyForPreview(const QString &path, const QString &compName)
+{
+ addInfo(tr("Import is ready for preview."));
+ if (m_previewCompName.isEmpty())
+ addInfo(tr("Click \"Accept\" to finish the import or adjust options and click \"Import\" to import again."));
+
+ m_previewFile = Utils::FilePath::fromString(path).pathAppended(m_importer.previewFileName());
+ m_previewCompName = compName;
+ m_previewOptions = m_importOptions;
+ QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::startPreview);
+
+ ui->importButton->setEnabled(true);
+ updateImportButtonState();
+}
+
+void ItemLibraryAssetImportDialog::onRequestImageUpdate()
+{
+ if (m_nodeInstanceView)
+ m_nodeInstanceView->view3DAction(View3DActionType::Import3dUpdatePreviewImage, canvas()->size());
+}
+
+void ItemLibraryAssetImportDialog::onRequestRotation(const QPointF &delta)
+{
+ if (m_nodeInstanceView)
+ m_nodeInstanceView->view3DAction(View3DActionType::Import3dRotatePreviewModel, delta);
+}
+
void ItemLibraryAssetImportDialog::onImportNearlyFinished()
{
// Canceling import is no longer doable
- ui->buttonBox->button(QDialogButtonBox::Close)->setEnabled(false);
+ ui->closeButton->setEnabled(false);
}
void ItemLibraryAssetImportDialog::onImportFinished()
@@ -894,19 +1106,28 @@ void ItemLibraryAssetImportDialog::onImportFinished()
QString interruptStr = tr("Import interrupted.");
addError(interruptStr);
setImportProgress(0, interruptStr);
+ if (m_explicitClose)
+ QTimer::singleShot(1000, this, &ItemLibraryAssetImportDialog::doClose);
} else {
QString doneStr = tr("Import done.");
addInfo(doneStr);
setImportProgress(100, doneStr);
if (m_closeOnFinish) {
// Add small delay to allow user to visually confirm import finishing
- QTimer::singleShot(1000, this, &ItemLibraryAssetImportDialog::onClose);
+ QTimer::singleShot(1000, this, &ItemLibraryAssetImportDialog::doClose);
}
}
}
void ItemLibraryAssetImportDialog::onClose()
{
+ ui->importButton->setEnabled(false);
+ m_explicitClose = true;
+ doClose();
+}
+
+void ItemLibraryAssetImportDialog::doClose()
+{
if (m_importer.isImporting()) {
addInfo(tr("Canceling import."));
m_importer.cancelImport();
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h
index c5da478232..e7c4933056 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h
@@ -3,14 +3,19 @@
#pragma once
#include "itemlibraryassetimporter.h"
-#include "modelnode.h"
+
+#include <modelnode.h>
+
+#include <utils/filepath.h>
#include <QDialog>
#include <QJsonObject>
+#include <QPointer>
#include <QSet>
QT_BEGIN_NAMESPACE
class QGridLayout;
+class QPushButton;
QT_END_NAMESPACE
namespace Utils {
@@ -19,6 +24,10 @@ class OutputFormatter;
namespace QmlDesigner {
class ItemLibraryAssetImporter;
+class Import3dCanvas;
+class Import3dConnectionManager;
+class NodeInstanceView;
+class RewriterView;
namespace Ui {
class ItemLibraryAssetImportDialog;
@@ -35,10 +44,12 @@ public:
const QVariantMap &supportedOpts,
const QJsonObject &defaultOpts,
const QSet<QString> &preselectedFilesForOverwrite,
+ AbstractView *view,
QWidget *parent = nullptr);
~ItemLibraryAssetImportDialog();
- static void updateImport(const ModelNode &updateNode,
+ static void updateImport(AbstractView *view,
+ const ModelNode &updateNode,
const QVariantMap &supportedExts,
const QVariantMap &supportedOpts);
@@ -52,12 +63,17 @@ private slots:
private:
void setCloseButtonState(bool importing);
+ void updateImportButtonState();
void onImport();
void setImportProgress(int value, const QString &text);
+ void onImportReadyForPreview(const QString &path, const QString &compName);
+ void onRequestImageUpdate();
+ void onRequestRotation(const QPointF &delta);
void onImportNearlyFinished();
void onImportFinished();
void onClose();
+ void doClose();
void toggleAdvanced();
void createTab(const QString &tabLabel, int optionsIndex, const QJsonObject &groups);
@@ -69,8 +85,19 @@ private:
bool isSimpleOption(const QString &id);
bool isHiddenOption(const QString &id);
+ void startPreview();
+ void cleanupPreviewPuppet();
+ Import3dCanvas *canvas();
+
Ui::ItemLibraryAssetImportDialog *ui = nullptr;
Utils::OutputFormatter *m_outputFormatter = nullptr;
+ QPointer<Import3dConnectionManager> m_connectionManager;
+ QPointer<NodeInstanceView> m_nodeInstanceView;
+ QPointer<RewriterView> m_rewriterView;
+ QPointer<AbstractView> m_view;
+ ModelPointer m_model;
+ Utils::FilePath m_previewFile;
+ QString m_previewCompName;
struct OptionsData
{
@@ -83,6 +110,7 @@ private:
QString m_quick3DImportPath;
ItemLibraryAssetImporter m_importer;
QVector<QJsonObject> m_importOptions;
+ QVector<QJsonObject> m_previewOptions;
QHash<QString, int> m_extToImportOptionsMap;
QSet<QString> m_preselectedFilesForOverwrite;
bool m_closeOnFinish = true;
@@ -91,5 +119,6 @@ private:
OptionsData m_advancedData;
bool m_advancedMode = false;
int m_dialogHeight = 350;
+ bool m_explicitClose = false;
};
}
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui
index 081bc36a3d..e0b9d925fc 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui
@@ -6,15 +6,15 @@
<rect>
<x>0</x>
<y>0</y>
- <width>630</width>
+ <width>1100</width>
<height>350</height>
</rect>
</property>
<property name="windowTitle">
<string>Asset Import</string>
</property>
- <layout class="QFormLayout" name="formLayout">
- <item row="0" column="0" colspan="2">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTabWidget" name="tabWidget">
@@ -24,6 +24,12 @@
<verstretch>2</verstretch>
</sizepolicy>
</property>
+ <property name="minimumSize">
+ <size>
+ <width>550</width>
+ <height>0</height>
+ </size>
+ </property>
<property name="currentIndex">
<number>0</number>
</property>
@@ -98,6 +104,12 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
<property name="text">
<string/>
</property>
@@ -111,16 +123,64 @@
</widget>
</item>
<item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="standardButtons">
- <set>QDialogButtonBox::Close|QDialogButtonBox::Ok</set>
- </property>
- </widget>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <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="closeButton">
+ <property name="text">
+ <string>Close</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="importButton">
+ <property name="text">
+ <string>Import</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
</layout>
</item>
+ <item>
+ <widget class="Import3dCanvas" name="import3dcanvas" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>300</width>
+ <height>300</height>
+ </size>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
+ <customwidgets>
+ <customwidget>
+ <class>Import3dCanvas</class>
+ <extends>QWidget</extends>
+ <header>import3dcanvas.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
index ed1f8041e9..010d00a970 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
@@ -21,6 +21,7 @@
#include <utils/algorithm.h>
#include <utils/async.h>
+#include <utils/filepath.h>
#include <utils/qtcassert.h>
#include <QApplication>
@@ -58,8 +59,6 @@ void ItemLibraryAssetImporter::importQuick3D(const QStringList &inputFiles,
const QHash<QString, int> &extToImportOptionsMap,
const QSet<QString> &preselectedFilesForOverwrite)
{
- if (m_isImporting)
- cancelImport();
reset();
m_isImporting = true;
@@ -92,6 +91,53 @@ void ItemLibraryAssetImporter::importQuick3D(const QStringList &inputFiles,
}
}
+void ItemLibraryAssetImporter::reImportQuick3D(const QString &assetName,
+ const QVector<QJsonObject> &options)
+{
+ if (!assetName.isEmpty() && !m_parseData.contains(assetName)) {
+ addError(tr("Attempted to reimport non-existing asset: %1").arg(assetName));
+ return;
+ }
+
+ ParseData &pd = m_parseData[assetName];
+ // Change outDir just in case reimport generates different files
+ QDir oldDir = pd.outDir;
+ QString assetFolder = generateAssetFolderName(pd.assetName);
+ pd.outDir.cdUp();
+ pd.outDir.mkpath(assetFolder);
+
+ if (!pd.outDir.cd(assetFolder)) {
+ addError(tr("Could not access temporary asset directory: \"%1\".")
+ .arg(pd.outDir.filePath(assetFolder)));
+ return;
+ }
+
+ if (oldDir.absolutePath().contains(tempDirNameBase()))
+ oldDir.removeRecursively();
+
+ m_isImporting = false;
+ m_cancelled = false;
+
+ m_puppetProcess.reset();
+ m_requiredImports.clear();
+ m_currentImportId = 0;
+ m_puppetQueue.clear();
+
+ for (ParseData &pd : m_parseData)
+ pd.importId = -1;
+
+ pd.options = options[pd.optionsIndex];
+ pd.importId = 1;
+
+ m_importFiles.remove(assetName);
+
+ m_importIdToAssetNameMap.clear();
+ m_importIdToAssetNameMap[pd.importId] = assetName;
+
+ m_puppetQueue.append(pd.importId);
+ startNextImportProcess();
+}
+
bool ItemLibraryAssetImporter::isImporting() const
{
return m_isImporting;
@@ -104,19 +150,19 @@ void ItemLibraryAssetImporter::cancelImport()
notifyFinished();
}
-void ItemLibraryAssetImporter::addError(const QString &errMsg, const QString &srcPath) const
+void ItemLibraryAssetImporter::addError(const QString &errMsg, const QString &srcPath)
{
qCDebug(importerLog) << "Error: "<< errMsg << srcPath;
emit errorReported(errMsg, srcPath);
}
-void ItemLibraryAssetImporter::addWarning(const QString &warningMsg, const QString &srcPath) const
+void ItemLibraryAssetImporter::addWarning(const QString &warningMsg, const QString &srcPath)
{
qCDebug(importerLog) << "Warning: " << warningMsg << srcPath;
emit warningReported(warningMsg, srcPath);
}
-void ItemLibraryAssetImporter::addInfo(const QString &infoMsg, const QString &srcPath) const
+void ItemLibraryAssetImporter::addInfo(const QString &infoMsg, const QString &srcPath)
{
qCDebug(importerLog) << "Info: " << infoMsg << srcPath;
emit infoReported(infoMsg, srcPath);
@@ -127,8 +173,8 @@ void ItemLibraryAssetImporter::importProcessFinished([[maybe_unused]] int exitCo
{
m_puppetProcess.reset();
- if (m_parseData.contains(m_currentImportId)) {
- const ParseData &pd = m_parseData[m_currentImportId];
+ if (m_importIdToAssetNameMap.contains(m_currentImportId)) {
+ const ParseData &pd = m_parseData[m_importIdToAssetNameMap[m_currentImportId]];
QString errStr;
if (exitStatus == QProcess::ExitStatus::CrashExit) {
errStr = tr("Import process crashed.");
@@ -151,11 +197,12 @@ void ItemLibraryAssetImporter::importProcessFinished([[maybe_unused]] int exitCo
addError(tr("Asset import process failed: \"%1\".")
.arg(pd.sourceInfo.absoluteFilePath()));
addError(errStr);
- m_parseData.remove(m_currentImportId);
+ m_parseData.remove(m_importIdToAssetNameMap[m_currentImportId]);
+ m_importIdToAssetNameMap.remove(m_currentImportId);
}
}
- int finishedCount = m_parseData.size() - m_puppetQueue.size();
+ int finishedCount = m_importIdToAssetNameMap.size() - m_puppetQueue.size();
if (!m_puppetQueue.isEmpty())
startNextImportProcess();
@@ -163,7 +210,7 @@ void ItemLibraryAssetImporter::importProcessFinished([[maybe_unused]] int exitCo
notifyProgress(100);
QTimer::singleShot(0, this, &ItemLibraryAssetImporter::postImport);
} else {
- notifyProgress(int(100. * (double(finishedCount) / double(m_parseData.size()))));
+ notifyProgress(int(100. * (double(finishedCount) / double(m_importIdToAssetNameMap.size()))));
}
}
@@ -179,7 +226,7 @@ void ItemLibraryAssetImporter::reset()
m_cancelled = false;
delete m_tempDir;
- m_tempDir = new QTemporaryDir;
+ m_tempDir = new QTemporaryDir(QDir::tempPath() + tempDirNameBase());
m_importFiles.clear();
m_overwrittenImports.clear();
m_puppetProcess.reset();
@@ -187,6 +234,7 @@ void ItemLibraryAssetImporter::reset()
m_requiredImports.clear();
m_currentImportId = 0;
m_puppetQueue.clear();
+ m_importIdToAssetNameMap.clear();
}
void ItemLibraryAssetImporter::parseFiles(const QStringList &filePaths,
@@ -208,9 +256,11 @@ void ItemLibraryAssetImporter::parseFiles(const QStringList &filePaths,
int index = extToImportOptionsMap.value(QFileInfo(file).suffix());
ParseData pd;
pd.options = options[index];
+ pd.optionsIndex = index;
if (preParseQuick3DAsset(file, pd, preselectedFilesForOverwrite)) {
pd.importId = ++m_importIdCounter;
- m_parseData.insert(pd.importId, pd);
+ m_importIdToAssetNameMap[pd.importId] = pd.assetName;
+ m_parseData.insert(pd.assetName, pd);
}
notifyProgress(qRound(++count * quota), progressTitle);
}
@@ -239,7 +289,7 @@ bool ItemLibraryAssetImporter::preParseQuick3DAsset(const QString &file, ParseDa
pd.targetDirPath = pd.targetDir.filePath(pd.assetName);
- if (pd.outDir.exists(pd.assetName)) {
+ if (m_parseData.contains(pd.assetName)) {
addWarning(tr("Skipped import of duplicate asset: \"%1\".").arg(pd.assetName));
return false;
}
@@ -288,11 +338,12 @@ bool ItemLibraryAssetImporter::preParseQuick3DAsset(const QString &file, ParseDa
}
}
- pd.outDir.mkpath(pd.assetName);
+ QString assetFolder = generateAssetFolderName(pd.assetName);
+ pd.outDir.mkpath(assetFolder);
- if (!pd.outDir.cd(pd.assetName)) {
+ if (!pd.outDir.cd(assetFolder)) {
addError(tr("Could not access temporary asset directory: \"%1\".")
- .arg(pd.outDir.filePath(pd.assetName)));
+ .arg(pd.outDir.filePath(assetFolder)));
return false;
}
return true;
@@ -425,7 +476,7 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(ParseData &pd)
// Copy the original asset into a subdirectory
assetFiles.insert(sourcePath, sourceSceneTargetFilePath(pd));
- m_importFiles.insert(assetFiles);
+ m_importFiles.insert(pd.assetName, assetFiles);
}
void ItemLibraryAssetImporter::copyImportedFiles()
@@ -500,6 +551,12 @@ void ItemLibraryAssetImporter::keepUiAlive() const
QApplication::processEvents();
}
+QString ItemLibraryAssetImporter::generateAssetFolderName(const QString &assetName) const
+{
+ static int counter = 0;
+ return assetName + "_QDS_" + QString::number(counter++);
+}
+
ItemLibraryAssetImporter::OverwriteResult ItemLibraryAssetImporter::confirmAssetOverwrite(const QString &assetName)
{
const QString title = tr("Overwrite Existing Asset?");
@@ -534,7 +591,8 @@ void ItemLibraryAssetImporter::startNextImportProcess()
if (model && view) {
bool done = false;
while (!m_puppetQueue.isEmpty() && !done) {
- const ParseData pd = m_parseData.value(m_puppetQueue.takeLast());
+ const ParseData pd = m_parseData.value(
+ m_importIdToAssetNameMap.value(m_puppetQueue.takeLast()));
QStringList puppetArgs;
QJsonDocument optDoc(pd.options);
@@ -557,7 +615,8 @@ void ItemLibraryAssetImporter::startNextImportProcess()
} else {
addError(tr("Failed to start import 3D asset process."),
pd.sourceInfo.absoluteFilePath());
- m_parseData.remove(pd.importId);
+ const QString assetName = m_importIdToAssetNameMap.take(pd.importId);
+ m_parseData.remove(assetName);
m_puppetProcess.reset();
}
}
@@ -573,8 +632,16 @@ void ItemLibraryAssetImporter::postImport()
postParseQuick3DAsset(pd);
}
- if (!isCancelled())
- finalizeQuick3DImport();
+ if (!isCancelled()) {
+ // TODO: Currently we only support import preview for single imports
+ if (m_parseData.size() != 1) {
+ finalizeQuick3DImport();
+ } else {
+ const ParseData &pd = m_parseData[m_parseData.keys().first()];
+ const QString importedComponentName = pd.assetName;
+ emit importReadyForPreview(pd.outDir.absolutePath(), importedComponentName);
+ }
+ }
}
void ItemLibraryAssetImporter::finalizeQuick3DImport()
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h
index 8ad7b5a2de..abe9690951 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h
@@ -2,8 +2,6 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
-#include "import.h"
-
#include <qprocessuniqueptr.h>
#include <QSet>
@@ -35,20 +33,28 @@ public:
const QHash<QString, int> &extToImportOptionsMap,
const QSet<QString> &preselectedFilesForOverwrite);
+ void reImportQuick3D(const QString &assetName, const QVector<QJsonObject> &options);
+
bool isImporting() const;
void cancelImport();
bool isCancelled() const;
- void addError(const QString &errMsg, const QString &srcPath = {}) const;
- void addWarning(const QString &warningMsg, const QString &srcPath = {}) const;
- void addInfo(const QString &infoMsg, const QString &srcPath = {}) const;
+ void addError(const QString &errMsg, const QString &srcPath = {});
+ void addWarning(const QString &warningMsg, const QString &srcPath = {});
+ void addInfo(const QString &infoMsg, const QString &srcPath = {});
+
+ QString previewFileName() const { return "QDSImport3dPreviewScene.qml"; }
+ QString tempDirNameBase() const { return "/qds3dimport"; }
+
+ void finalizeQuick3DImport();
signals:
- void errorReported(const QString &, const QString &) const;
- void warningReported(const QString &, const QString &) const;
- void infoReported(const QString &, const QString &) const;
- void progressChanged(int value, const QString &text) const;
- void importNearlyFinished() const;
+ void errorReported(const QString &, const QString &);
+ void warningReported(const QString &, const QString &);
+ void infoReported(const QString &, const QString &);
+ void progressChanged(int value, const QString &text);
+ void importReadyForPreview(const QString &path, const QString &compName);
+ void importNearlyFinished();
void importFinished();
private slots:
@@ -63,7 +69,8 @@ private:
QFileInfo sourceInfo;
QString assetName;
QString originalAssetName;
- int importId;
+ int importId = -1;
+ int optionsIndex = -1;
};
void notifyFinished();
@@ -79,6 +86,7 @@ private:
void notifyProgress(int value, const QString &text);
void notifyProgress(int value);
void keepUiAlive() const;
+ QString generateAssetFolderName(const QString &assetName) const;
enum class OverwriteResult {
Skip,
@@ -89,10 +97,9 @@ private:
OverwriteResult confirmAssetOverwrite(const QString &assetName);
void startNextImportProcess();
void postImport();
- void finalizeQuick3DImport();
QString sourceSceneTargetFilePath(const ParseData &pd);
- QSet<QHash<QString, QString>> m_importFiles;
+ QHash<QString, QHash<QString, QString>> m_importFiles; // Key: asset name
QHash<QString, QStringList> m_overwrittenImports;
bool m_isImporting = false;
bool m_cancelled = false;
@@ -101,7 +108,8 @@ private:
QProcessUniquePointer m_puppetProcess;
int m_importIdCounter = 0;
int m_currentImportId = 0;
- QHash<int, ParseData> m_parseData;
+ QHash<int, QString> m_importIdToAssetNameMap;
+ QHash<QString, ParseData> m_parseData; // Key: asset name
QString m_progressTitle;
QStringList m_requiredImports;
QList<int> m_puppetQueue;
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
index 3bff210520..8ef9512e26 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
@@ -313,51 +313,54 @@ void ItemLibraryModel::update(Model *model)
beginResetModel();
clearSections();
- GeneratedComponentUtils compUtils = QmlDesignerPlugin::instance()->documentManager()
- .generatedComponentUtils();
+ const QString projectName = DocumentManager::currentProjectName();
+
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
QStringList excludedImports {
- compUtils.componentBundlesTypePrefix() + ".MaterialBundle",
- compUtils.componentBundlesTypePrefix() + ".EffectBundle"
+ projectName,
+ compUtils.materialsBundleType(),
+ compUtils.effectsBundleType(),
+ compUtils.userMaterialsBundleType(),
+ compUtils.user3DBundleType(),
+ compUtils.userEffectsBundleType()
};
// create import sections
- const QString projectName = DocumentManager::currentProjectName();
const Imports usedImports = model->usedImports();
QHash<QString, ItemLibraryImport *> importHash;
for (const Import &import : model->imports()) {
- if (import.url() != projectName) {
- if (excludedImports.contains(import.url())
- || import.url().startsWith(compUtils.composedEffectsTypePrefix())) {
- continue;
- }
- bool addNew = true;
- bool isQuick3DAsset = import.url().startsWith(compUtils.import3dTypePrefix());
- QString importUrl = import.url();
- if (isQuick3DAsset)
- importUrl = ItemLibraryImport::quick3DAssetsTitle();
- else if (import.isFileImport())
- importUrl = import.toString(true, true).remove("\"");
-
- ItemLibraryImport *oldImport = importHash.value(importUrl);
- if (oldImport && oldImport->sectionType() == ItemLibraryImport::SectionType::Quick3DAssets
- && isQuick3DAsset) {
- addNew = false; // add only 1 Quick3DAssets import section
- } else if (oldImport && oldImport->importEntry().url() == import.url()) {
- // Retain the higher version if multiples exist
- if (oldImport->importEntry().toVersion() >= import.toVersion() || import.hasVersion())
- addNew = false;
- else
- delete oldImport;
- }
+ if (excludedImports.contains(import.url())
+ || import.url().startsWith(compUtils.composedEffectsTypePrefix())) {
+ continue;
+ }
- if (addNew) {
- auto sectionType = isQuick3DAsset ? ItemLibraryImport::SectionType::Quick3DAssets
- : ItemLibraryImport::SectionType::Default;
- ItemLibraryImport *itemLibImport = new ItemLibraryImport(import, this, sectionType);
- itemLibImport->setImportUsed(usedImports.contains(import));
- importHash.insert(importUrl, itemLibImport);
- }
+ bool addNew = true;
+ bool isQuick3DAsset = import.url().startsWith(compUtils.import3dTypePrefix());
+ QString importUrl = import.url();
+ if (isQuick3DAsset)
+ importUrl = ItemLibraryImport::quick3DAssetsTitle();
+ else if (import.isFileImport())
+ importUrl = import.toString(true, true).remove("\"");
+
+ ItemLibraryImport *oldImport = importHash.value(importUrl);
+ if (oldImport && oldImport->sectionType() == ItemLibraryImport::SectionType::Quick3DAssets
+ && isQuick3DAsset) {
+ addNew = false; // add only 1 Quick3DAssets import section
+ } else if (oldImport && oldImport->importEntry().url() == import.url()) {
+ // Retain the higher version if multiples exist
+ if (oldImport->importEntry().toVersion() >= import.toVersion() || import.hasVersion())
+ addNew = false;
+ else
+ delete oldImport;
+ }
+
+ if (addNew) {
+ auto sectionType = isQuick3DAsset ? ItemLibraryImport::SectionType::Quick3DAssets
+ : ItemLibraryImport::SectionType::Default;
+ ItemLibraryImport *itemLibImport = new ItemLibraryImport(import, this, sectionType);
+ itemLibImport->setImportUsed(usedImports.contains(import));
+ importHash.insert(importUrl, itemLibImport);
}
}
@@ -373,7 +376,7 @@ void ItemLibraryModel::update(Model *model)
NodeMetaInfo metaInfo;
if constexpr (useProjectStorage())
- metaInfo = entry.metaInfo();
+ metaInfo = NodeMetaInfo{entry.typeId(), model->projectStorage()};
else
metaInfo = model->metaInfo(entry.typeName());
@@ -385,7 +388,8 @@ void ItemLibraryModel::update(Model *model)
|| metaInfo.majorVersion() < 0);
#endif
bool isItem = valid && metaInfo.isQtQuickItem();
- bool forceVisibility = valid && NodeHints::fromItemLibraryEntry(entry).visibleInLibrary();
+ bool forceVisibility = valid
+ && NodeHints::fromItemLibraryEntry(entry, model).visibleInLibrary();
if (m_flowMode) {
isItem = metaInfo.isFlowViewItem();
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
index feff523b9c..e4f4ffcd9c 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
@@ -164,7 +164,7 @@ void ItemLibraryView::updateImport3DSupport(const QVariantMap &supportMap)
auto importDlg = new ItemLibraryAssetImportDialog(fileNames, defaultDir,
m_importableExtensions3DMap,
m_importOptions3DMap, {}, {},
- Core::ICore::dialogParent());
+ this, Core::ICore::dialogParent());
int result = importDlg->exec();
return result == QDialog::Accepted ? AddFilesResult::succeeded() : AddFilesResult::cancelled();
@@ -198,7 +198,7 @@ void ItemLibraryView::customNotification(const AbstractView *view, const QString
const QList<ModelNode> &nodeList, const QList<QVariant> &data)
{
if (identifier == "UpdateImported3DAsset" && nodeList.size() > 0) {
- ItemLibraryAssetImportDialog::updateImport(nodeList[0],
+ ItemLibraryAssetImportDialog::updateImport(this, nodeList[0],
m_importableExtensions3DMap,
m_importOptions3DMap);
diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp
index b6009edc77..6ab97b4307 100644
--- a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp
+++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp
@@ -32,7 +32,7 @@ public:
if (value.typeId() == QVariant::Bool)
return value;
- if (value.typeId() == QVariant::String) {
+ if (value.typeId() == QMetaType::QString) {
const QString text = value.toString();
if (text == "true")
return QVariant(true);
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp
index d36e78512b..b74f310741 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp
@@ -366,6 +366,9 @@ void MaterialBrowserModel::selectMaterial(int idx, bool force)
if (idx != m_selectedIndex || force) {
m_selectedIndex = idx;
emit selectedIndexChanged(idx);
+
+ m_selectedMaterialIsComponent = selectedMaterial().isComponent();
+ emit selectedMaterialIsComponentChanged();
}
}
@@ -434,22 +437,20 @@ void MaterialBrowserModel::copyMaterialProperties(int idx, const QString &sectio
QJsonObject propsSpecObj = m_propertyGroupsObj.value(m_copiedMaterialType).toObject();
if (propsSpecObj.contains(section)) { // should always be true
const QJsonArray propNames = propsSpecObj.value(section).toArray();
- // auto == QJsonValueConstRef after 04dc959d49e5e3 / Qt 6.4, QJsonValueRef before
- for (const auto &propName : propNames)
+ for (const QJsonValueConstRef &propName : propNames)
copiedProps.append(propName.toString().toLatin1());
if (section == "Base") { // add QtQuick3D.Material base props as well
QJsonObject propsMatObj = m_propertyGroupsObj.value("Material").toObject();
const QJsonArray propNames = propsMatObj.value("Base").toArray();
- // auto == QJsonValueConstRef after 04dc959d49e5e3 / Qt 6.4, QJsonValueRef before
- for (const auto &propName : propNames)
+ for (const QJsonValueConstRef &propName : propNames)
copiedProps.append(propName.toString().toLatin1());
}
}
}
m_copiedMaterialProps.clear();
- for (const auto &propName : copiedProps) {
+ for (const PropertyName &propName : copiedProps) {
PropertyCopyData data;
data.name = propName;
data.isValid = m_allPropsCopied || validProps.contains(propName);
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h
index 337dce0550..3b6b64ec86 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h
@@ -3,7 +3,7 @@
#pragma once
-#include "modelnode.h"
+#include <modelnode.h>
#include <QAbstractListModel>
#include <QJsonObject>
@@ -20,6 +20,7 @@ class MaterialBrowserModel : public QAbstractListModel
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged)
+ Q_PROPERTY(bool selectedMaterialIsComponent MEMBER m_selectedMaterialIsComponent NOTIFY selectedMaterialIsComponentChanged)
Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport WRITE setHasQuick3DImport NOTIFY hasQuick3DImportChanged)
Q_PROPERTY(bool hasModelSelection READ hasModelSelection WRITE setHasModelSelection NOTIFY hasModelSelectionChanged)
Q_PROPERTY(bool hasMaterialLibrary READ hasMaterialLibrary WRITE setHasMaterialLibrary NOTIFY hasMaterialLibraryChanged)
@@ -110,6 +111,7 @@ signals:
const QList<QmlDesigner::MaterialBrowserModel::PropertyCopyData> &props,
bool all);
void isQt6ProjectChanged();
+ void selectedMaterialIsComponentChanged();
private:
bool isValidIndex(int idx) const;
@@ -132,6 +134,7 @@ private:
bool m_hasMaterialLibrary = false;
bool m_allPropsCopied = true;
bool m_isQt6Project = false;
+ bool m_selectedMaterialIsComponent = false;
QString m_copiedMaterialType;
QPointer<MaterialBrowserView> m_view;
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp
index 8bd5761728..7d90dffffc 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp
@@ -446,8 +446,13 @@ void QmlDesigner::MaterialBrowserView::loadPropertyGroups()
if (!m_hasQuick3DImport || m_propertyGroupsLoaded || !model())
return;
+#ifdef QDS_USE_PROJECTSTORAGE
+ // TODO
+ QString matPropsPath;
+#else
QString matPropsPath = model()->metaInfo("QtQuick3D.Material").importDirectoryPath()
+ "/designer/propertyGroups.json";
+#endif
m_propertyGroupsLoaded = m_widget->materialBrowserModel()->loadPropertyGroups(matPropsPath);
}
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp
index 7cf0a875bc..8723611be0 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp
@@ -150,7 +150,7 @@ MaterialBrowserWidget::MaterialBrowserWidget(AsynchronousImageCache &imageCache,
: m_materialBrowserView(view)
, m_materialBrowserModel(new MaterialBrowserModel(view, this))
, m_materialBrowserTexturesModel(new MaterialBrowserTexturesModel(view, this))
- , m_quickWidget(new StudioQuickWidget(this))
+ , m_quickWidget(Utils::makeUniqueObjectPtr<StudioQuickWidget>(this))
, m_previewImageProvider(new PreviewImageProvider())
{
QImage defaultImage;
@@ -179,7 +179,7 @@ MaterialBrowserWidget::MaterialBrowserWidget(AsynchronousImageCache &imageCache,
auto layout = new QVBoxLayout(this);
layout->setContentsMargins({});
layout->setSpacing(0);
- layout->addWidget(m_quickWidget.data());
+ layout->addWidget(m_quickWidget.get());
updateSearch();
@@ -411,7 +411,7 @@ void MaterialBrowserWidget::setIsDragging(bool val)
StudioQuickWidget *MaterialBrowserWidget::quickWidget() const
{
- return m_quickWidget.data();
+ return m_quickWidget.get();
}
void MaterialBrowserWidget::clearPreviewCache()
@@ -434,10 +434,4 @@ QPointer<MaterialBrowserTexturesModel> MaterialBrowserWidget::materialBrowserTex
return m_materialBrowserTexturesModel;
}
-bool MaterialBrowserWidget::userBundleEnabled() const
-{
- // TODO: this method is to be removed after user bundle implementation is complete
- return Core::ICore::settings()->value("QML/Designer/UseExperimentalFeatures45", false).toBool();
-}
-
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h
index 97c994e565..6506283f85 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h
@@ -7,6 +7,8 @@
#include <coreplugin/icontext.h>
+#include <utils/uniqueobjectptr.h>
+
#include <QFrame>
QT_BEGIN_NAMESPACE
@@ -61,7 +63,6 @@ public:
Q_INVOKABLE void acceptTextureDropOnMaterial(int matIndex, const QString &texId);
Q_INVOKABLE void focusMaterialSection(bool focusMatSec);
Q_INVOKABLE void addMaterialToContentLibrary();
- Q_INVOKABLE bool userBundleEnabled() const;
StudioQuickWidget *quickWidget() const;
@@ -85,7 +86,7 @@ private:
QPointer<MaterialBrowserView> m_materialBrowserView;
QPointer<MaterialBrowserModel> m_materialBrowserModel;
QPointer<MaterialBrowserTexturesModel> m_materialBrowserTexturesModel;
- QScopedPointer<StudioQuickWidget> m_quickWidget;
+ Utils::UniqueObjectPtr<StudioQuickWidget> m_quickWidget;
QShortcut *m_qmlSourceUpdateShortcut = nullptr;
PreviewImageProvider *m_previewImageProvider = nullptr;
diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp
index ecc460ae51..0e508f8f36 100644
--- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp
+++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp
@@ -80,8 +80,8 @@ public:
MaterialEditorQmlBackend::MaterialEditorQmlBackend(MaterialEditorView *materialEditor)
: m_quickWidget(Utils::makeUniqueObjectPtr<QQuickWidget>())
- , m_materialEditorTransaction(new MaterialEditorTransaction(materialEditor))
- , m_contextObject(new MaterialEditorContextObject(m_quickWidget.get()))
+ , m_materialEditorTransaction(std::make_unique<MaterialEditorTransaction>(materialEditor))
+ , m_contextObject(std::make_unique<MaterialEditorContextObject>(m_quickWidget.get()))
, m_materialEditorImageProvider(new MaterialEditorImageProvider())
{
m_quickWidget->setObjectName(Constants::OBJECT_NAME_MATERIAL_EDITOR);
@@ -90,7 +90,7 @@ MaterialEditorQmlBackend::MaterialEditorQmlBackend(MaterialEditorView *materialE
m_quickWidget->engine()->addImageProvider("materialEditor", m_materialEditorImageProvider);
m_contextObject->setBackendValues(&m_backendValuesPropertyMap);
m_contextObject->setModel(materialEditor->model());
- context()->setContextObject(m_contextObject.data());
+ context()->setContextObject(m_contextObject.get());
QObject::connect(&m_backendValuesPropertyMap, &DesignerPropertyMap::valueChanged,
materialEditor, &MaterialEditorView::changeValue);
@@ -193,7 +193,7 @@ QQmlContext *MaterialEditorQmlBackend::context() const
MaterialEditorContextObject *MaterialEditorQmlBackend::contextObject() const
{
- return m_contextObject.data();
+ return m_contextObject.get();
}
QQuickWidget *MaterialEditorQmlBackend::widget() const
@@ -224,7 +224,7 @@ DesignerPropertyMap &MaterialEditorQmlBackend::backendValuesPropertyMap()
MaterialEditorTransaction *MaterialEditorQmlBackend::materialEditorTransaction() const
{
- return m_materialEditorTransaction.data();
+ return m_materialEditorTransaction.get();
}
PropertyEditorValue *MaterialEditorQmlBackend::propertyValueForName(const QString &propertyName)
@@ -267,12 +267,9 @@ void MaterialEditorQmlBackend::setup(const QmlObjectNode &selectedMaterialNode,
// anchors
m_backendAnchorBinding.setup(selectedMaterialNode.modelNode());
- context()->setContextProperties(
- QVector<QQmlContext::PropertyPair>{
- {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)},
- {{"transaction"}, QVariant::fromValue(m_materialEditorTransaction.data())}
- }
- );
+ context()->setContextProperties(QVector<QQmlContext::PropertyPair>{
+ {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)},
+ {{"transaction"}, QVariant::fromValue(m_materialEditorTransaction.get())}});
contextObject()->setSpecificsUrl(qmlSpecificsFile);
contextObject()->setStateName(stateName);
diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h
index 0792a635ca..9fd5fc2399 100644
--- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h
+++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h
@@ -11,6 +11,8 @@
#include <nodemetainfo.h>
+#include <memory>
+
class PropertyEditorValue;
QT_BEGIN_NAMESPACE
@@ -67,8 +69,8 @@ private:
Utils::UniqueObjectPtr<QQuickWidget> m_quickWidget = nullptr;
QmlAnchorBindingProxy m_backendAnchorBinding;
QmlModelNodeProxy m_backendModelNode;
- QScopedPointer<MaterialEditorTransaction> m_materialEditorTransaction;
- QScopedPointer<MaterialEditorContextObject> m_contextObject;
+ std::unique_ptr<MaterialEditorTransaction> m_materialEditorTransaction;
+ std::unique_ptr<MaterialEditorContextObject> m_contextObject;
QPointer<MaterialEditorImageProvider> m_materialEditorImageProvider;
};
diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp
index e083310cdb..21114267cb 100644
--- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp
+++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp
@@ -24,7 +24,6 @@
#include "qmldesignerplugin.h"
#include "qmltimeline.h"
#include "variantproperty.h"
-#include <itemlibraryentry.h>
#include <utils3d.h>
#include <coreplugin/icore.h>
@@ -63,10 +62,6 @@ MaterialEditorView::MaterialEditorView(ExternalDependenciesInterface &externalDe
}
});
- m_typeUpdateTimer.setSingleShot(true);
- m_typeUpdateTimer.setInterval(500);
- connect(&m_typeUpdateTimer, &QTimer::timeout, this, &MaterialEditorView::updatePossibleTypes);
-
QmlDesignerPlugin::trackWidgetFocusTime(m_stackedWidget, Constants::EVENT_MATERIALEDITOR_TIME);
MaterialEditorDynamicPropertiesProxyModel::registerDeclarativeType();
@@ -333,8 +328,10 @@ void MaterialEditorView::resetView()
setupQmlBackend();
- if (m_qmlBackEnd)
+ if (m_qmlBackEnd) {
m_qmlBackEnd->emitSelectionChanged();
+ updatePossibleTypes();
+ }
QTimer::singleShot(0, this, &MaterialEditorView::requestPreviewRender);
@@ -605,7 +602,6 @@ void MaterialEditorView::setupQmlBackend()
else
m_dynamicPropertiesModel->reset();
- delayedTypeUpdate();
initPreviewData();
m_stackedWidget->setCurrentWidget(m_qmlBackEnd->widget());
@@ -690,21 +686,6 @@ void MaterialEditorView::initPreviewData()
}
}
-void MaterialEditorView::delayedTypeUpdate()
-{
- m_typeUpdateTimer.start();
-}
-
-[[maybe_unused]] static Import entryToImport(const ItemLibraryEntry &entry)
-{
- if (entry.majorVersion() == -1 && entry.minorVersion() == -1)
- return Import::createFileImport(entry.requiredImport());
-
- return Import::createLibraryImport(entry.requiredImport(),
- QString::number(entry.majorVersion()) + QLatin1Char('.') +
- QString::number(entry.minorVersion()));
-}
-
void MaterialEditorView::updatePossibleTypes()
{
QTC_ASSERT(model(), return);
@@ -712,49 +693,21 @@ void MaterialEditorView::updatePossibleTypes()
if (!m_qmlBackEnd)
return;
-#ifdef QDS_USE_PROJECTSTORAGE
- auto heirs = model()->qtQuick3DMaterialMetaInfo().heirs();
- heirs.push_back(model()->qtQuick3DMaterialMetaInfo());
- auto entries = Utils::transform<ItemLibraryEntries>(heirs, [&](const auto &heir) {
- return toItemLibraryEntries(heir.itemLibrariesEntries(), *model()->projectStorage());
- });
+ static const QStringList basicTypes {
+ "CustomMaterial",
+ "DefaultMaterial",
+ "PrincipledMaterial",
+ "SpecularGlossyMaterial"
+ };
- // I am unsure about the code intention here
-#else // Ensure basic types are always first
- QStringList nonQuick3dTypes;
- QStringList allTypes;
-
- const QList<ItemLibraryEntry> itemLibEntries = m_itemLibraryInfo->entries();
- for (const ItemLibraryEntry &entry : itemLibEntries) {
- NodeMetaInfo metaInfo = model()->metaInfo(entry.typeName());
- bool valid = metaInfo.isValid()
- && (metaInfo.majorVersion() >= entry.majorVersion()
- || metaInfo.majorVersion() < 0);
- if (valid && metaInfo.isQtQuick3DMaterial()) {
- bool addImport = entry.requiredImport().isEmpty();
- if (!addImport) {
- Import import = entryToImport(entry);
- addImport = model()->hasImport(import, true, true);
- }
- if (addImport) {
- const QList<QByteArray> typeSplit = entry.typeName().split('.');
- const QString typeName = QString::fromLatin1(typeSplit.last());
- if (typeSplit.size() == 2 && typeSplit.first() == "QtQuick3D") {
- if (!allTypes.contains(typeName))
- allTypes.append(typeName);
- } else if (!nonQuick3dTypes.contains(typeName)) {
- nonQuick3dTypes.append(typeName);
- }
- }
- }
- }
+ const QString matType = m_selectedMaterial.simplifiedTypeName();
- allTypes.sort();
- nonQuick3dTypes.sort();
- allTypes.append(nonQuick3dTypes);
+ if (basicTypes.contains(matType)) {
+ m_qmlBackEnd->contextObject()->setPossibleTypes(basicTypes);
+ return;
+ }
- m_qmlBackEnd->contextObject()->setPossibleTypes(allTypes);
-#endif
+ m_qmlBackEnd->contextObject()->setPossibleTypes({matType});
}
void MaterialEditorView::modelAttached(Model *model)
@@ -774,20 +727,6 @@ void MaterialEditorView::modelAttached(Model *model)
m_ensureMatLibTimer.start(500);
}
-#ifndef QDS_USE_PROJECTSTORAGE
- if (m_itemLibraryInfo.data() != model->metaInfo().itemLibraryInfo()) {
- if (m_itemLibraryInfo) {
- disconnect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged,
- this, &MaterialEditorView::delayedTypeUpdate);
- }
- m_itemLibraryInfo = model->metaInfo().itemLibraryInfo();
- if (m_itemLibraryInfo) {
- connect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged,
- this, &MaterialEditorView::delayedTypeUpdate);
- }
- }
-#endif
-
if (!m_setupCompleted) {
reloadQml();
m_setupCompleted = true;
@@ -801,7 +740,8 @@ void MaterialEditorView::modelAboutToBeDetached(Model *model)
{
AbstractView::modelAboutToBeDetached(model);
m_dynamicPropertiesModel->reset();
- m_qmlBackEnd->materialEditorTransaction()->end();
+ if (auto transaction = m_qmlBackEnd->materialEditorTransaction())
+ transaction->end();
m_qmlBackEnd->contextObject()->setHasMaterialLibrary(false);
m_selectedMaterial = {};
}
@@ -938,7 +878,8 @@ void MaterialEditorView::selectedNodesChanged(const QList<ModelNode> &selectedNo
m_selectedModels.append(node);
}
- m_qmlBackEnd->contextObject()->setHasModelSelection(!m_selectedModels.isEmpty());
+ if (m_qmlBackEnd)
+ m_qmlBackEnd->contextObject()->setHasModelSelection(!m_selectedModels.isEmpty());
}
void MaterialEditorView::currentStateChanged(const ModelNode &node)
@@ -1020,7 +961,7 @@ void MaterialEditorView::renameMaterial(ModelNode &material, const QString &newN
return;
executeInTransaction(__FUNCTION__, [&] {
- material.setIdWithRefactoring(model()->generateIdFromName(newName, "material"));
+ material.setIdWithRefactoring(model()->generateNewId(newName, "material"));
VariantProperty objNameProp = material.variantProperty("objectName");
objNameProp.setValue(newName);
@@ -1057,7 +998,7 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material)
QString newName = sourceMat.modelNode().variantProperty("objectName").value().toString() + " copy";
VariantProperty objNameProp = duplicateMatNode.variantProperty("objectName");
objNameProp.setValue(newName);
- duplicateMatNode.setIdWithoutRefactoring(model()->generateIdFromName(newName, "material"));
+ duplicateMatNode.setIdWithoutRefactoring(model()->generateNewId(newName, "material"));
// sync properties. Only the base state is duplicated.
const QList<AbstractProperty> props = material.properties();
diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h
index c201742bd5..11bea46063 100644
--- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h
+++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h
@@ -109,12 +109,10 @@ private:
bool noValidSelection() const;
void initPreviewData();
- void delayedTypeUpdate();
void updatePossibleTypes();
ModelNode m_selectedMaterial;
QTimer m_ensureMatLibTimer;
- QTimer m_typeUpdateTimer;
QShortcut *m_updateShortcut = nullptr;
int m_timerId = 0;
QStackedWidget *m_stackedWidget = nullptr;
diff --git a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp
index a3ab5f2cd7..fee3218af0 100644
--- a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp
+++ b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp
@@ -102,10 +102,9 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i
#ifdef QDS_USE_PROJECTSTORAGE
// TODO add the types here or use the module
#else
- } else if (insertInfo.typeName().startsWith(
- QString("%1.MaterialBundle").arg(QmlDesignerPlugin::instance()->documentManager()
- .generatedComponentUtils().componentBundlesTypePrefix())
- .toUtf8())) {
+ } else if (insertInfo.typeName().startsWith(
+ QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().materialsBundleType().toUtf8())) {
if (parentInfo.isQtQuick3DModel())
propertyList.append("materials");
#endif
diff --git a/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp b/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp
index 24d4377ef2..e7fe0ebb77 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp
+++ b/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp
@@ -97,10 +97,10 @@ void LineEdit::paintEvent(QPaintEvent *event)
QPalette p(palette());
p.setColor(QPalette::Active,
QPalette::PlaceholderText,
- Utils::creatorTheme()->color(Utils::Theme::DSplaceholderTextColor));
+ Utils::creatorColor(Utils::Theme::DSplaceholderTextColor));
p.setColor(QPalette::Inactive,
QPalette::PlaceholderText,
- Utils::creatorTheme()->color(Utils::Theme::DSplaceholderTextColor));
+ Utils::creatorColor(Utils::Theme::DSplaceholderTextColor));
setPalette(p);
}
QLineEdit::paintEvent(event);
diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
index d305753ead..c5fa30fc7d 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
+++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
@@ -455,7 +455,7 @@ QStringList NavigatorTreeModel::mimeTypes() const
Constants::MIME_TYPE_MATERIAL,
Constants::MIME_TYPE_BUNDLE_TEXTURE,
Constants::MIME_TYPE_BUNDLE_MATERIAL,
- Constants::MIME_TYPE_BUNDLE_EFFECT,
+ Constants::MIME_TYPE_BUNDLE_ITEM,
Constants::MIME_TYPE_ASSETS});
return types;
@@ -570,9 +570,9 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData,
} else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_MATERIAL)) {
if (targetNode.isValid())
m_view->emitCustomNotification("drop_bundle_material", {targetNode}); // To ContentLibraryView
- } else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_EFFECT)) {
+ } else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_ITEM)) {
if (targetNode.isValid())
- m_view->emitCustomNotification("drop_bundle_effect", {targetNode}); // To ContentLibraryView
+ m_view->emitCustomNotification("drop_bundle_item", {targetNode}); // To ContentLibraryView
} else if (mimeData->hasFormat(Constants::MIME_TYPE_ASSETS)) {
const QStringList assetsPaths = QString::fromUtf8(mimeData->data(Constants::MIME_TYPE_ASSETS)).split(',');
NodeAbstractProperty targetProperty;
@@ -705,7 +705,7 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in
const ItemLibraryEntry itemLibraryEntry =
createItemLibraryEntryFromMimeData(mimeData->data(Constants::MIME_TYPE_ITEM_LIBRARY_INFO));
- const NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry);
+ const NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry, m_view->model());
const QString targetPropertyName = hints.forceNonDefaultProperty();
diff --git a/src/plugins/qmldesigner/components/navigator/previewtooltip.cpp b/src/plugins/qmldesigner/components/navigator/previewtooltip.cpp
index f99b964f2d..e5e42697e2 100644
--- a/src/plugins/qmldesigner/components/navigator/previewtooltip.cpp
+++ b/src/plugins/qmldesigner/components/navigator/previewtooltip.cpp
@@ -21,7 +21,8 @@ PreviewToolTip::PreviewToolTip(QWidget *parent)
m_ui->idLabel->setElideMode(Qt::ElideLeft);
m_ui->typeLabel->setElideMode(Qt::ElideLeft);
m_ui->infoLabel->setElideMode(Qt::ElideLeft);
- setStyleSheet(QString("QWidget { background-color: %1 }").arg(Utils::creatorTheme()->color(Utils::Theme::BackgroundColorNormal).name()));
+ setStyleSheet(QString("QWidget { background-color: %1 }")
+ .arg(Utils::creatorColor(Utils::Theme::BackgroundColorNormal).name()));
m_ui->imageLabel->setStyleSheet("background-color: rgba(0, 0, 0, 0)");
static QPixmap checkers;
diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp
index 248a9ec429..4034254420 100644
--- a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp
+++ b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp
@@ -20,7 +20,8 @@ PreviewImageTooltip::PreviewImageTooltip(QWidget *parent)
m_ui->nameLabel->setElideMode(Qt::ElideLeft);
m_ui->pathLabel->setElideMode(Qt::ElideLeft);
m_ui->infoLabel->setElideMode(Qt::ElideLeft);
- setStyleSheet(QString("QWidget { background-color: %1 }").arg(Utils::creatorTheme()->color(Utils::Theme::BackgroundColorNormal).name()));
+ setStyleSheet(QString("QWidget { background-color: %1 }")
+ .arg(Utils::creatorColor(Utils::Theme::BackgroundColorNormal).name()));
}
PreviewImageTooltip::~PreviewImageTooltip() = default;
diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp
index 16328ae532..b6d5e2ae47 100644
--- a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp
+++ b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp
@@ -42,7 +42,18 @@ void PreviewTooltipBackend::showTooltip()
}
});
},
- [](auto) {},
+ [&](ImageCache::AbortReason abortReason) {
+ if (abortReason == ImageCache::AbortReason::Abort) {
+ qWarning() << QLatin1String("PreviewTooltipBackend::showTooltip(): preview generation "
+ "failed for path %1, reason: Abort").arg(m_path);
+ } else if (abortReason == ImageCache::AbortReason::Failed) {
+ qWarning() << QLatin1String("PreviewTooltipBackend::showTooltip(): preview generation "
+ "failed for path %1, reason: Failed").arg(m_path);
+ } else if (abortReason == ImageCache::AbortReason::NoEntry) {
+ qWarning() << QLatin1String("PreviewTooltipBackend::showTooltip(): preview generation "
+ "failed for path %1, reason: NoEntry").arg(m_path);
+ }
+ },
Utils::PathString{m_extraId},
m_auxiliaryData);
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp
index 591ce5a57f..b6d119a424 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp
@@ -586,8 +586,7 @@ int PropertyEditorContextObject::devicePixelRatio()
QStringList PropertyEditorContextObject::styleNamesForFamily(const QString &family)
{
- const QFontDatabase dataBase;
- return dataBase.styles(family);
+ return QFontDatabase::styles(family);
}
QStringList PropertyEditorContextObject::allStatesForId(const QString &id)
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp
index eea53c3f5a..c397d445b1 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp
@@ -83,16 +83,17 @@ namespace QmlDesigner {
PropertyEditorQmlBackend::PropertyEditorQmlBackend(PropertyEditorView *propertyEditor,
AsynchronousImageCache &imageCache)
: m_view(Utils::makeUniqueObjectPtr<Quick2PropertyEditorView>(imageCache))
- , m_propertyEditorTransaction(new PropertyEditorTransaction(propertyEditor))
- , m_dummyPropertyEditorValue(new PropertyEditorValue())
- , m_contextObject(new PropertyEditorContextObject(m_view.get()))
+ , m_propertyEditorTransaction(std::make_unique<PropertyEditorTransaction>(propertyEditor))
+ , m_dummyPropertyEditorValue(std::make_unique<PropertyEditorValue>())
+ , m_contextObject(std::make_unique<PropertyEditorContextObject>(m_view.get()))
{
m_view->engine()->setOutputWarningsToStandardError(QmlDesignerPlugin::instance()
->settings().value(DesignerSettingsKey::SHOW_PROPERTYEDITOR_WARNINGS).toBool());
m_view->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
m_dummyPropertyEditorValue->setValue(QLatin1String("#000000"));
- context()->setContextProperty(QLatin1String("dummyBackendValue"), m_dummyPropertyEditorValue.data());
+ context()->setContextProperty(QLatin1String("dummyBackendValue"),
+ m_dummyPropertyEditorValue.get());
m_contextObject->setBackendValues(&m_backendValuesPropertyMap);
m_contextObject->setModel(propertyEditor->model());
m_contextObject->insertInQmlContext(context());
@@ -402,7 +403,7 @@ QQmlContext *PropertyEditorQmlBackend::context()
PropertyEditorContextObject *PropertyEditorQmlBackend::contextObject()
{
- return m_contextObject.data();
+ return m_contextObject.get();
}
QQuickWidget *PropertyEditorQmlBackend::widget()
@@ -432,7 +433,7 @@ DesignerPropertyMap &PropertyEditorQmlBackend::backendValuesPropertyMap() {
}
PropertyEditorTransaction *PropertyEditorQmlBackend::propertyEditorTransaction() {
- return m_propertyEditorTransaction.data();
+ return m_propertyEditorTransaction.get();
}
PropertyEditorValue *PropertyEditorQmlBackend::propertyValueForName(const QString &propertyName)
@@ -495,12 +496,9 @@ void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const Q
// anchors
m_backendAnchorBinding.setup(qmlObjectNode.modelNode());
- context()->setContextProperties(
- QVector<QQmlContext::PropertyPair>{
- {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)},
- {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.data())}
- }
- );
+ context()->setContextProperties(QVector<QQmlContext::PropertyPair>{
+ {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)},
+ {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.get())}});
contextObject()->setHasMultiSelection(
!qmlObjectNode.view()->singleSelectedModelNode().isValid());
@@ -592,13 +590,10 @@ void PropertyEditorQmlBackend::initialSetup(const TypeName &typeName, const QUrl
QObject::connect(valueObject, &PropertyEditorValue::valueChanged, &backendValuesPropertyMap(), &DesignerPropertyMap::valueChanged);
m_backendValuesPropertyMap.insert(QLatin1String("id"), QVariant::fromValue(valueObject));
- context()->setContextProperties(
- QVector<QQmlContext::PropertyPair>{
- {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)},
- {{"modelNodeBackend"}, QVariant::fromValue(&m_backendModelNode)},
- {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.data())}
- }
- );
+ context()->setContextProperties(QVector<QQmlContext::PropertyPair>{
+ {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)},
+ {{"modelNodeBackend"}, QVariant::fromValue(&m_backendModelNode)},
+ {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.get())}});
contextObject()->setSpecificsUrl(qmlSpecificsFile);
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h
index 120b7b4abc..1c9b808ac3 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h
@@ -16,6 +16,8 @@
#include <QQmlPropertyMap>
+#include <memory>
+
class PropertyEditorValue;
namespace QmlDesigner {
@@ -109,9 +111,9 @@ private:
Utils::UniqueObjectPtr<Quick2PropertyEditorView> m_view = nullptr;
QmlAnchorBindingProxy m_backendAnchorBinding;
QmlModelNodeProxy m_backendModelNode;
- QScopedPointer<PropertyEditorTransaction> m_propertyEditorTransaction;
- QScopedPointer<PropertyEditorValue> m_dummyPropertyEditorValue;
- QScopedPointer<PropertyEditorContextObject> m_contextObject;
+ std::unique_ptr<PropertyEditorTransaction> m_propertyEditorTransaction;
+ std::unique_ptr<PropertyEditorValue> m_dummyPropertyEditorValue;
+ std::unique_ptr<PropertyEditorContextObject> m_contextObject;
};
} //QmlDesigner
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp
index 70686f31ae..27319c15f5 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp
@@ -22,7 +22,6 @@
#include <utils/qtcassert.h>
#include <QRegularExpression>
-#include <QScopedPointer>
#include <QUrl>
namespace QmlDesigner {
@@ -60,10 +59,10 @@ static bool cleverColorCompare(const QVariant &value1, const QVariant &value2)
return c1.name() == c2.name() && c1.alpha() == c2.alpha();
}
- if (value1.typeId() == QVariant::String && value2.typeId() == QVariant::Color)
+ if (value1.typeId() == QMetaType::QString && value2.typeId() == QVariant::Color)
return cleverColorCompare(QVariant(QColor(value1.toString())), value2);
- if (value1.typeId() == QVariant::Color && value2.typeId() == QVariant::String)
+ if (value1.typeId() == QVariant::Color && value2.typeId() == QMetaType::QString)
return cleverColorCompare(value1, QVariant(QColor(value2.toString())));
return false;
@@ -549,6 +548,23 @@ void PropertyEditorValue::setForceBound(bool b)
emit isBoundChanged();
}
+void PropertyEditorValue::insertKeyframe()
+{
+ if (!m_modelNode.isValid())
+ return;
+
+ /*If we add more code here we have to forward the property editor view */
+ AbstractView *view = m_modelNode.view();
+
+ QmlTimeline timeline = view->currentTimeline();
+
+ QTC_ASSERT(timeline.isValid(), return );
+ QTC_ASSERT(m_modelNode.isValid(), return );
+
+ view->executeInTransaction("PropertyEditorContextObject::insertKeyframe",
+ [&] { timeline.insertKeyframe(m_modelNode, name()); });
+}
+
QStringList PropertyEditorValue::generateStringList(const QString &string) const
{
QString copy = string;
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h
index 70a51fffc2..c4b09f6b5a 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h
@@ -172,6 +172,8 @@ public:
Q_INVOKABLE void setForceBound(bool b);
+ Q_INVOKABLE void insertKeyframe();
+
public slots:
void resetValue();
void setEnumeration(const QString &scope, const QString &name);
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp
index b22b39e238..e0d5759617 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp
@@ -38,7 +38,6 @@
#include <QFileSystemWatcher>
#include <QQuickItem>
#include <QScopeGuard>
-#include <QScopedPointer>
#include <QShortcut>
#include <QTimer>
diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp
index fc39b37137..1cff0e55e6 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp
@@ -161,13 +161,12 @@ void QmlModelNodeProxy::createModelNode(int internalIdParent,
const QString &typeName,
const QString &requiredImport)
{
- QTC_ASSERT(m_qmlObjectNode.isValid(), return );
+ auto parentModelNode = m_qmlObjectNode.modelNode();
- auto modelNode = m_qmlObjectNode.modelNode();
+ QTC_ASSERT(parentModelNode.isValid(), return );
- AbstractView *view = modelNode.view();
+ AbstractView *view = parentModelNode.view();
- auto parentModelNode = m_qmlObjectNode.modelNode();
if (internalIdParent >= 0)
parentModelNode = view->modelNodeForInternalId(internalIdParent);
@@ -186,7 +185,7 @@ void QmlModelNodeProxy::createModelNode(int internalIdParent,
#ifdef QDS_USE_PROJECTSTORAGE
ModelNode newNode = view->createModelNode(typeName.toUtf8());
#else
- NodeMetaInfo metaInfo = modelNode.model()->metaInfo(typeName.toUtf8());
+ NodeMetaInfo metaInfo = parentModelNode.model()->metaInfo(typeName.toUtf8());
ModelNode newNode = view->createModelNode(metaInfo.typeName(),
metaInfo.majorVersion(),
metaInfo.minorVersion());
@@ -200,14 +199,17 @@ void QmlModelNodeProxy::moveNode(int internalIdParent,
int fromIndex,
int toIndex)
{
- QTC_ASSERT(m_qmlObjectNode.isValid(), return );
+ ModelNode modelNode = m_qmlObjectNode.modelNode();
- ModelNode node = m_qmlObjectNode.view()->modelNodeForInternalId(internalIdParent);
+ QTC_ASSERT(modelNode.isValid(), return );
- QTC_ASSERT(node.isValid(), return );
+ if (internalIdParent >= 0)
+ modelNode = m_qmlObjectNode.view()->modelNodeForInternalId(internalIdParent);
+
+ QTC_ASSERT(modelNode.isValid(), return );
AbstractView *view = m_qmlObjectNode.view();
- view->executeInTransaction("QmlModelNodeProxy::swapNode", [&] {
- node.nodeListProperty(propertyName.toUtf8()).slide(fromIndex, toIndex);
+ view->executeInTransaction("QmlModelNodeProxy::moveNode", [&] {
+ modelNode.nodeListProperty(propertyName.toUtf8()).slide(fromIndex, toIndex);
});
}
diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.cpp
index a8981ff2f0..3f6f6769fb 100644
--- a/src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.cpp
+++ b/src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.cpp
@@ -15,7 +15,8 @@ TextEditorStatusBar::TextEditorStatusBar(QWidget *parent) : QToolBar(parent), m_
addWidget(m_label);
/* We have to set another .css, since the central widget has already a style sheet */
- m_label->setStyleSheet(QString("QLabel { color :%1 }").arg(Utils::creatorTheme()->color(Utils::Theme::TextColorError).name()));
+ m_label->setStyleSheet(QString("QLabel { color :%1 }")
+ .arg(Utils::creatorColor(Utils::Theme::TextColorError).name()));
}
void TextEditorStatusBar::clearText()
diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp
index d400251648..2e52b3358b 100644
--- a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp
+++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp
@@ -96,7 +96,8 @@ void TextEditorView::modelAboutToBeDetached(Model *model)
{
AbstractView::modelAboutToBeDetached(model);
- m_widget->setTextEditor(nullptr);
+ if (m_widget)
+ m_widget->setTextEditor(nullptr);
// in case the user closed it explicit we do not want to do anything with the editor
if (Core::ModeManager::currentModeId() == Core::Constants::MODE_DESIGN) {
diff --git a/src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp b/src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp
index b9c5868bd8..dc57cf30c8 100644
--- a/src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp
+++ b/src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp
@@ -38,26 +38,26 @@ void TextEditItemWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem
QLineEdit* TextEditItemWidget::lineEdit() const
{
- if (m_lineEdit.isNull()) {
- m_lineEdit.reset(new QLineEdit);
+ if (!m_lineEdit) {
+ m_lineEdit = std::make_unique<QLineEdit>();
m_lineEdit->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
QPalette palette = m_lineEdit->palette();
- static QColor selectionColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor);
+ static QColor selectionColor = Utils::creatorColor(Utils::Theme::QmlDesigner_FormEditorSelectionColor);
palette.setColor(QPalette::Highlight, selectionColor);
palette.setColor(QPalette::HighlightedText, Qt::white);
palette.setColor(QPalette::Base, Qt::white);
palette.setColor(QPalette::Text, Qt::black);
m_lineEdit->setPalette(palette);
}
- return m_lineEdit.data();
+ return m_lineEdit.get();
}
QTextEdit* TextEditItemWidget::textEdit() const
{
- if (m_textEdit.isNull()) {
- m_textEdit.reset(new QTextEdit);
+ if (!m_textEdit) {
+ m_textEdit = std::make_unique<QTextEdit>();
QPalette palette = m_textEdit->palette();
- static QColor selectionColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor);
+ static QColor selectionColor = Utils::creatorColor(Utils::Theme::QmlDesigner_FormEditorSelectionColor);
palette.setColor(QPalette::Highlight, selectionColor);
palette.setColor(QPalette::HighlightedText, Qt::white);
palette.setColor(QPalette::Base, Qt::white);
@@ -65,7 +65,7 @@ QTextEdit* TextEditItemWidget::textEdit() const
m_textEdit->setPalette(palette);
}
- return m_textEdit.data();
+ return m_textEdit.get();
}
void TextEditItemWidget::activateTextEdit(const QSize &maximumSize)
@@ -83,19 +83,19 @@ void TextEditItemWidget::activateLineEdit()
QString TextEditItemWidget::text() const
{
- if (widget() == m_lineEdit.data())
+ if (widget() == m_lineEdit.get())
return m_lineEdit->text();
- else if (widget() == m_textEdit.data())
+ else if (widget() == m_textEdit.get())
return m_textEdit->toPlainText();
return QString();
}
void TextEditItemWidget::updateText(const QString &text)
{
- if (widget() == m_lineEdit.data()) {
+ if (widget() == m_lineEdit.get()) {
m_lineEdit->setText(text);
m_lineEdit->selectAll();
- } else if (widget() == m_textEdit.data()) {
+ } else if (widget() == m_textEdit.get()) {
m_textEdit->setText(text);
m_textEdit->selectAll();
}
diff --git a/src/plugins/qmldesigner/components/texttool/textedititemwidget.h b/src/plugins/qmldesigner/components/texttool/textedititemwidget.h
index 8aa6c409f9..f975f1a3da 100644
--- a/src/plugins/qmldesigner/components/texttool/textedititemwidget.h
+++ b/src/plugins/qmldesigner/components/texttool/textedititemwidget.h
@@ -3,7 +3,8 @@
#pragma once
#include <QGraphicsProxyWidget>
-#include <QScopedPointer>
+
+#include <memory>
QT_BEGIN_NAMESPACE
class QTextEdit;
@@ -32,7 +33,7 @@ protected:
QString text() const;
private:
- mutable QScopedPointer<QLineEdit> m_lineEdit;
- mutable QScopedPointer<QTextEdit> m_textEdit;
+ mutable std::unique_ptr<QLineEdit> m_lineEdit;
+ mutable std::unique_ptr<QTextEdit> m_textEdit;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp
index 972574554b..415d943723 100644
--- a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp
+++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp
@@ -42,10 +42,11 @@ static QObject *variantToQObject(const QVariant &value)
namespace QmlDesigner {
-TextureEditorQmlBackend::TextureEditorQmlBackend(TextureEditorView *textureEditor, AsynchronousImageCache &imageCache)
- : m_quickWidget(new QQuickWidget)
- , m_textureEditorTransaction(new TextureEditorTransaction(textureEditor))
- , m_contextObject(new TextureEditorContextObject(m_quickWidget->rootContext()))
+TextureEditorQmlBackend::TextureEditorQmlBackend(TextureEditorView *textureEditor,
+ AsynchronousImageCache &imageCache)
+ : m_quickWidget(Utils::makeUniqueObjectPtr<QQuickWidget>())
+ , m_textureEditorTransaction(std::make_unique<TextureEditorTransaction>(textureEditor))
+ , m_contextObject(std::make_unique<TextureEditorContextObject>(m_quickWidget->rootContext()))
{
QImage defaultImage;
defaultImage.load(Utils::StyleHelper::dpiSpecificImageFile(":/textureeditor/images/texture_default.png"));
@@ -56,7 +57,7 @@ TextureEditorQmlBackend::TextureEditorQmlBackend(TextureEditorView *textureEdito
m_quickWidget->engine()->addImageProvider("qmldesigner_thumbnails", m_textureEditorImageProvider);
m_contextObject->setBackendValues(&m_backendValuesPropertyMap);
m_contextObject->setModel(textureEditor->model());
- context()->setContextObject(m_contextObject.data());
+ context()->setContextObject(m_contextObject.get());
QObject::connect(&m_backendValuesPropertyMap, &DesignerPropertyMap::valueChanged,
textureEditor, &TextureEditorView::changeValue);
@@ -159,7 +160,7 @@ QQmlContext *TextureEditorQmlBackend::context() const
TextureEditorContextObject *TextureEditorQmlBackend::contextObject() const
{
- return m_contextObject.data();
+ return m_contextObject.get();
}
QQuickWidget *TextureEditorQmlBackend::widget() const
@@ -184,7 +185,7 @@ DesignerPropertyMap &TextureEditorQmlBackend::backendValuesPropertyMap()
TextureEditorTransaction *TextureEditorQmlBackend::textureEditorTransaction() const
{
- return m_textureEditorTransaction.data();
+ return m_textureEditorTransaction.get();
}
PropertyEditorValue *TextureEditorQmlBackend::propertyValueForName(const QString &propertyName)
@@ -227,12 +228,9 @@ void TextureEditorQmlBackend::setup(const QmlObjectNode &selectedTextureNode, co
// anchors
m_backendAnchorBinding.setup(selectedTextureNode.modelNode());
- context()->setContextProperties(
- QVector<QQmlContext::PropertyPair>{
- {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)},
- {{"transaction"}, QVariant::fromValue(m_textureEditorTransaction.data())}
- }
- );
+ context()->setContextProperties(QVector<QQmlContext::PropertyPair>{
+ {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)},
+ {{"transaction"}, QVariant::fromValue(m_textureEditorTransaction.get())}});
contextObject()->setSpecificsUrl(qmlSpecificsFile);
contextObject()->setStateName(stateName);
diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h
index c8a113f7d3..f69c46a569 100644
--- a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h
+++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h
@@ -11,6 +11,8 @@
#include <nodemetainfo.h>
+#include <memory>
+
class PropertyEditorValue;
QT_BEGIN_NAMESPACE
@@ -64,11 +66,11 @@ private:
// this needs be destructed after m_quickWidget->engine() is destructed
DesignerPropertyMap m_backendValuesPropertyMap;
- Utils::UniqueObjectPtr<QQuickWidget> m_quickWidget = nullptr;
+ Utils::UniqueObjectPtr<QQuickWidget> m_quickWidget;
QmlAnchorBindingProxy m_backendAnchorBinding;
QmlModelNodeProxy m_backendModelNode;
- QScopedPointer<TextureEditorTransaction> m_textureEditorTransaction;
- QScopedPointer<TextureEditorContextObject> m_contextObject;
+ std::unique_ptr<TextureEditorTransaction> m_textureEditorTransaction;
+ std::unique_ptr<TextureEditorContextObject> m_contextObject;
AssetImageProvider *m_textureEditorImageProvider = nullptr;
};
diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp
index 5de3730c97..a637431c4d 100644
--- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp
+++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp
@@ -43,7 +43,6 @@
#include <QFileInfo>
#include <QQuickWidget>
#include <QQuickItem>
-#include <QScopedPointer>
#include <QStackedWidget>
#include <QShortcut>
#include <QTimer>
@@ -698,7 +697,8 @@ void TextureEditorView::selectedNodesChanged(const QList<ModelNode> &selectedNod
m_selectedModel = selectedNodeList.at(0);
bool hasValidSelection = QmlObjectNode(m_selectedModel).hasBindingProperty("materials");
- m_qmlBackEnd->contextObject()->setHasSingleModelSelection(hasValidSelection);
+ if (m_qmlBackEnd)
+ m_qmlBackEnd->contextObject()->setHasSingleModelSelection(hasValidSelection);
}
void TextureEditorView::currentStateChanged(const ModelNode &node)
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp
index e65c05c39f..56e22dab1b 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp
@@ -97,10 +97,10 @@ TimelineAnimationForm::TimelineAnimationForm(QWidget *parent)
using namespace Layouting;
Grid {
Span(4, mainL), br,
- empty(), br,
+ empty, br,
idL, Span(2, m_idLineEdit), Span(2, Row{ runningL, m_running }), br,
- empty(), startFrameL, m_startFrame, endFrameL, m_endFrame, durationL, m_duration, br,
- empty(), continuousL, m_continuous, loopsL, m_loops, pingPongL, m_pingPong, str, br,
+ empty, startFrameL, m_startFrame, endFrameL, m_endFrame, durationL, m_duration, br,
+ empty, continuousL, m_continuous, loopsL, m_loops, pingPongL, m_pingPong, str, br,
tr("Transition to state:"), transitionToStateL, m_transitionToState, br,
}.attachTo(this);
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp
index 08915b6577..2d1e70cd77 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp
@@ -78,8 +78,8 @@ TimelineForm::TimelineForm(QWidget *parent)
Grid {
Span(2, mainL), br,
idL, m_idLineEdit, br,
- empty(), Row { startFrameL, m_startFrame, st(), endFrameL, m_endFrame }, str, br,
- empty(), Row { m_expressionBinding, m_animation, st() }, br,
+ empty, Row { startFrameL, m_startFrame, st, endFrameL, m_endFrame }, str, br,
+ empty, Row { m_expressionBinding, m_animation, st }, br,
expressionBindingL, m_expressionBindingLineEdit, br,
}.attachTo(this);
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp
index 698ef0f03b..762bd85c8b 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp
@@ -35,7 +35,6 @@
#include <QLineEdit>
#include <QMenu>
#include <QPainter>
-#include <QScopedPointer>
#include <algorithm>
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsmodel.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsmodel.cpp
index 39c1f01ce6..9b85599ead 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsmodel.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsmodel.cpp
@@ -68,7 +68,7 @@ TimelineEditorDelegate::TimelineEditorDelegate(QWidget *parent)
if (factory == nullptr) {
factory = new QItemEditorFactory;
QItemEditorCreatorBase *creator = new QItemEditorCreator<QComboBox>("currentText");
- factory->registerEditor(QVariant::String, creator);
+ factory->registerEditor(QMetaType::QString, creator);
}
setItemEditorFactory(factory);
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp
index 591926d3f5..592cf0dff9 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp
@@ -221,10 +221,10 @@ TimelineWidget::TimelineWidget(TimelineView *view)
{
QPalette timelinePalette;
- timelinePalette.setColor(QPalette::Text, Utils::creatorTheme()->color(
+ timelinePalette.setColor(QPalette::Text, Utils::creatorColor(
Utils::Theme::DStextColor));
timelinePalette.setColor(QPalette::WindowText, timelinePalette.color(QPalette::Text));
- timelinePalette.setColor(QPalette::Window, Utils::creatorTheme()->color(
+ timelinePalette.setColor(QPalette::Window, Utils::creatorColor(
Utils::Theme::QmlDesigner_BackgroundColorDarkAlternate));
onboardingTopLabel->setPalette(timelinePalette);
diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp
index 5ee5790b53..da40c4d387 100644
--- a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp
+++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp
@@ -2,11 +2,36 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "generatedcomponentutils.h"
-
#include <qmldesignerconstants.h>
namespace QmlDesigner {
+bool couldBeProjectModule(const Utils::FilePath &path, const QString &projectName)
+{
+ if (!path.exists())
+ return false;
+
+ Utils::FilePath qmlDirPath = path.pathAppended("qmldir");
+ if (qmlDirPath.exists()) {
+ Utils::expected_str<QByteArray> qmldirContents = qmlDirPath.fileContents();
+ if (!qmldirContents.has_value())
+ return false;
+
+ const QString expectedLine = QLatin1String("module %1").arg(projectName);
+ QByteArray fileContents = *qmldirContents;
+ QTextStream stream(fileContents);
+ while (!stream.atEnd()) {
+ QString lineData = stream.readLine().trimmed();
+ if (lineData.startsWith(u"module "))
+ return lineData == expectedLine;
+ }
+ }
+ if (path.endsWith(projectName))
+ return true;
+
+ return false;
+}
+
GeneratedComponentUtils::GeneratedComponentUtils(ExternalDependenciesInterface &externalDependencies)
: m_externalDependencies(externalDependencies)
{
@@ -60,7 +85,10 @@ Utils::FilePath GeneratedComponentUtils::componentBundlesBasePath() const
if (basePath.isEmpty())
return {};
- return basePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_TYPE));
+ if (basePath.endsWith(Constants::GENERATED_COMPONENTS_FOLDER))
+ return basePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_TYPE));
+
+ return basePath.resolvePath(QLatin1String(Constants::OLD_COMPONENT_BUNDLES_TYPE));
}
Utils::FilePath GeneratedComponentUtils::import3dBasePath() const
@@ -77,6 +105,62 @@ Utils::FilePath GeneratedComponentUtils::import3dBasePath() const
return basePath.resolvePath(QLatin1String(Constants::QUICK_3D_COMPONENTS_FOLDER));
}
+Utils::FilePath GeneratedComponentUtils::materialBundlePath() const
+{
+ Utils::FilePath basePath = componentBundlesBasePath();
+
+ if (basePath.isEmpty())
+ return {};
+
+ if (basePath.endsWith(Constants::OLD_COMPONENT_BUNDLES_TYPE))
+ return basePath.resolvePath(QLatin1String(Constants::OLD_COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE));
+
+ return basePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE));
+}
+
+Utils::FilePath GeneratedComponentUtils::effectBundlePath() const
+{
+ Utils::FilePath basePath = componentBundlesBasePath();
+
+ if (basePath.isEmpty())
+ return {};
+
+ if (basePath.endsWith(Constants::OLD_COMPONENT_BUNDLES_TYPE))
+ return basePath.resolvePath(QLatin1String(Constants::OLD_COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE));
+
+ return basePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE));
+}
+
+Utils::FilePath GeneratedComponentUtils::projectModulePath(bool generateIfNotExists) const
+{
+ using Utils::FilePath;
+ FilePath projectPath = FilePath::fromString(m_externalDependencies.currentProjectDirPath());
+
+ if (projectPath.isEmpty())
+ return {};
+
+ const QString projectName = m_externalDependencies.projectName();
+
+ FilePath newImportDirectory = projectPath.pathAppended(projectName);
+ if (couldBeProjectModule(newImportDirectory, projectName))
+ return newImportDirectory;
+
+ FilePath oldImportDirectory = projectPath.resolvePath(QLatin1String("imports/") + projectName);
+ if (couldBeProjectModule(oldImportDirectory, projectName))
+ return oldImportDirectory;
+
+ for (const QString &path : m_externalDependencies.projectModulePaths()) {
+ FilePath dir = FilePath::fromString(path);
+ if (couldBeProjectModule(dir, projectName))
+ return dir;
+ }
+
+ if (generateIfNotExists)
+ newImportDirectory.createDir();
+
+ return newImportDirectory;
+}
+
bool GeneratedComponentUtils::isImport3dPath(const QString &path) const
{
return path.contains('/' + QLatin1String(Constants::OLD_QUICK_3D_ASSETS_FOLDER))
@@ -87,9 +171,21 @@ bool GeneratedComponentUtils::isImport3dPath(const QString &path) const
bool GeneratedComponentUtils::isComposedEffectPath(const QString &path) const
{
return path.contains(Constants::OLD_EFFECTS_IMPORT_FOLDER)
- || path.contains('/' + QLatin1String(Constants::COMPOSED_EFFECTS_TYPE));
+ || path.contains(QLatin1String(Constants::GENERATED_COMPONENTS_FOLDER) + '/'
+ + QLatin1String(Constants::COMPOSED_EFFECTS_TYPE));
+}
+
+bool GeneratedComponentUtils::isBundlePath(const QString &path) const
+{
+ return path.contains(componentBundlesTypePrefix().replace('.', '/'));
}
+bool GeneratedComponentUtils::isGeneratedPath(const QString &path) const
+{
+ return path.startsWith(generatedComponentsPath().toFSPathString());
+}
+
+
QString GeneratedComponentUtils::generatedComponentTypePrefix() const
{
Utils::FilePath basePath = generatedComponentsPath();
@@ -123,7 +219,7 @@ QString GeneratedComponentUtils::componentBundlesTypePrefix() const
if (basePrefix.endsWith(Constants::GENERATED_COMPONENTS_FOLDER))
return basePrefix + '.' + QLatin1String(Constants::COMPONENT_BUNDLES_TYPE);
- return Constants::COMPONENT_BUNDLES_TYPE;
+ return Constants::OLD_COMPONENT_BUNDLES_TYPE;
}
QString GeneratedComponentUtils::composedEffectsTypePrefix() const
@@ -136,4 +232,60 @@ QString GeneratedComponentUtils::composedEffectsTypePrefix() const
return Constants::OLD_EFFECTS_FOLDER;
}
+QString GeneratedComponentUtils::materialsBundleId() const
+{
+ bool isNewImportDir = generatedComponentTypePrefix().endsWith(Constants::GENERATED_COMPONENTS_FOLDER);
+
+ return QLatin1String(isNewImportDir ? Constants::COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE
+ : Constants::OLD_COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE);
+}
+
+QString GeneratedComponentUtils::effectsBundleId() const
+{
+ bool isNewImportDir = generatedComponentTypePrefix().endsWith(Constants::GENERATED_COMPONENTS_FOLDER);
+
+ return QLatin1String(isNewImportDir ? Constants::COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE
+ : Constants::OLD_COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE);
+}
+
+QString GeneratedComponentUtils::userMaterialsBundleId() const
+{
+ return QLatin1String(Constants::COMPONENT_BUNDLES_USER_MATERIAL_BUNDLE_TYPE);
+}
+
+QString GeneratedComponentUtils::userEffectsBundleId() const
+{
+ return QLatin1String(Constants::COMPONENT_BUNDLES_USER_EFFECT_BUNDLE_TYPE);
+}
+
+QString GeneratedComponentUtils::user3DBundleId() const
+{
+ return QLatin1String(Constants::COMPONENT_BUNDLES_USER_3D_BUNDLE_TYPE);
+}
+
+QString GeneratedComponentUtils::materialsBundleType() const
+{
+ return componentBundlesTypePrefix() + '.' + materialsBundleId();
+}
+
+QString GeneratedComponentUtils::effectsBundleType() const
+{
+ return componentBundlesTypePrefix() + '.' + effectsBundleId();
+}
+
+QString GeneratedComponentUtils::userMaterialsBundleType() const
+{
+ return componentBundlesTypePrefix() + '.' + userMaterialsBundleId();
+}
+
+QString GeneratedComponentUtils::userEffectsBundleType() const
+{
+ return componentBundlesTypePrefix() + '.' + userEffectsBundleId();
+}
+
+QString GeneratedComponentUtils::user3DBundleType() const
+{
+ return componentBundlesTypePrefix() + '.' + user3DBundleId();
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.h b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h
index e2e9baf3e7..ceddb405a3 100644
--- a/src/plugins/qmldesigner/designercore/generatedcomponentutils.h
+++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h
@@ -21,9 +21,14 @@ public:
Utils::FilePath composedEffectPath(const QString &effectPath) const;
Utils::FilePath componentBundlesBasePath() const;
Utils::FilePath import3dBasePath() const;
+ Utils::FilePath materialBundlePath() const;
+ Utils::FilePath effectBundlePath() const;
+ Utils::FilePath projectModulePath(bool generateIfNotExists = false) const;
bool isImport3dPath(const QString &path) const;
bool isComposedEffectPath(const QString &path) const;
+ bool isBundlePath(const QString &path) const;
+ bool isGeneratedPath(const QString &path) const;
QString generatedComponentTypePrefix() const;
QString import3dTypePrefix() const;
@@ -31,6 +36,18 @@ public:
QString componentBundlesTypePrefix() const;
QString composedEffectsTypePrefix() const;
+ QString materialsBundleId() const;
+ QString effectsBundleId() const;
+ QString userMaterialsBundleId() const;
+ QString userEffectsBundleId() const;
+ QString user3DBundleId() const;
+
+ QString materialsBundleType() const;
+ QString effectsBundleType() const;
+ QString userMaterialsBundleType() const;
+ QString userEffectsBundleType() const;
+ QString user3DBundleType() const;
+
private:
ExternalDependenciesInterface &m_externalDependencies;
};
diff --git a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h
index 71ddeb7dc1..9055f51b6a 100644
--- a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h
+++ b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h
@@ -28,6 +28,7 @@ public:
virtual QString qmlPuppetFallbackDirectory() const = 0;
virtual QString defaultPuppetToplevelBuildDirectory() const = 0;
virtual QUrl projectUrl() const = 0;
+ virtual QString projectName() const = 0;
virtual QString currentProjectDirPath() const = 0;
virtual QUrl currentResourcePath() const = 0;
virtual void parseItemLibraryDescriptions() = 0;
diff --git a/src/plugins/qmldesigner/designercore/include/itemlibraryentry.h b/src/plugins/qmldesigner/designercore/include/itemlibraryentry.h
index f88f9e35c6..2d0f2ef31e 100644
--- a/src/plugins/qmldesigner/designercore/include/itemlibraryentry.h
+++ b/src/plugins/qmldesigner/designercore/include/itemlibraryentry.h
@@ -42,13 +42,16 @@ class QMLDESIGNERCORE_EXPORT ItemLibraryEntry
public:
ItemLibraryEntry();
- explicit ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry,
- const ProjectStorageType &projectStorage);
- ~ItemLibraryEntry() = default;
+ ItemLibraryEntry(const ItemLibraryEntry &) = default;
+ ItemLibraryEntry &operator=(const ItemLibraryEntry &) = default;
+ ItemLibraryEntry(ItemLibraryEntry &&) = default;
+ ItemLibraryEntry &operator=(ItemLibraryEntry &&) = default;
+ explicit ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry);
+ ~ItemLibraryEntry();
QString name() const;
TypeName typeName() const;
- const NodeMetaInfo &metaInfo() const;
+ TypeId typeId() const;
QIcon typeIcon() const;
QString libraryEntryIconPath() const;
int majorVersion() const;
@@ -86,7 +89,7 @@ private:
using ItemLibraryEntries = QList<ItemLibraryEntry>;
QMLDESIGNERCORE_EXPORT QList<ItemLibraryEntry> toItemLibraryEntries(
- const Storage::Info::ItemLibraryEntries &entries, const ProjectStorageType &projectStorage);
+ const Storage::Info::ItemLibraryEntries &entries);
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h
index 85f129cdbc..39b5cdaa81 100644
--- a/src/plugins/qmldesigner/designercore/include/model.h
+++ b/src/plugins/qmldesigner/designercore/include/model.h
@@ -137,7 +137,7 @@ public:
ModelPointer createModel(const TypeName &typeName,
std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {});
- QUrl fileUrl() const;
+ const QUrl &fileUrl() const;
SourceId fileUrlSourceId() const;
void setFileUrl(const QUrl &url);
@@ -147,7 +147,7 @@ public:
void setMetaInfo(const MetaInfo &metaInfo);
#endif
- Module module(Utils::SmallStringView moduleName);
+ Module module(Utils::SmallStringView moduleName, Storage::ModuleKind moduleKind);
NodeMetaInfo metaInfo(const TypeName &typeName, int majorVersion = -1, int minorVersion = -1) const;
NodeMetaInfo metaInfo(Module module,
Utils::SmallStringView typeName,
@@ -255,10 +255,7 @@ public:
bool hasId(const QString &id) const;
bool hasImport(const QString &importUrl) const;
- QString generateNewId(const QString &prefixName,
- const QString &fallbackPrefix = "element",
- std::optional<std::function<bool(const QString &)>> isDuplicate = {}) const;
- QString generateIdFromName(const QString &name, const QString &fallbackId = "element") const;
+ QString generateNewId(const QString &prefixName, const QString &fallbackPrefix = "element") const;
void startDrag(QMimeData *mimeData, const QPixmap &icon);
void endDrag();
diff --git a/src/plugins/qmldesigner/designercore/include/nodehints.h b/src/plugins/qmldesigner/designercore/include/nodehints.h
index 9e67c2d99b..99470db65f 100644
--- a/src/plugins/qmldesigner/designercore/include/nodehints.h
+++ b/src/plugins/qmldesigner/designercore/include/nodehints.h
@@ -3,9 +3,11 @@
#pragma once
+#include "modelnode.h"
+#include "nodemetainfo.h"
+
#include <QList>
#include <QString>
-#include "modelnode.h"
#include "qmldesignercorelib_global.h"
#include "invalidmetainfoexception.h"
@@ -54,18 +56,19 @@ public:
QHash<QString, QString> hints() const;
static NodeHints fromModelNode(const ModelNode &modelNode);
- static NodeHints fromItemLibraryEntry(const ItemLibraryEntry &entry);
+ static NodeHints fromItemLibraryEntry(const ItemLibraryEntry &entry, Model *model);
private:
explicit NodeHints(const ModelNode &modelNode);
explicit NodeHints(const NodeMetaInfo &metaInfo);
- explicit NodeHints(const ItemLibraryEntry &entry);
+ explicit NodeHints(const ItemLibraryEntry &entry, Model *model);
const ModelNode &modelNode() const;
bool isValid() const;
Model *model() const;
bool evaluateBooleanExpression(const QString &hintName, bool defaultValue, const ModelNode potentialParent = ModelNode()) const;
ModelNode m_modelNode;
+ NodeMetaInfo m_metaInfo;
QHash<QString, QString> m_hints;
};
diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h
index ba2e2cda65..fd3f2f9be8 100644
--- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h
+++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h
@@ -28,10 +28,14 @@ QT_END_NAMESPACE
[[deprecated( \
"In most cases you don't need them anymore because the import is setting them!")]]
# define DEPRECATED_COMPONENT_FILE_NAME [[deprecated("Use sourceId() instead.")]]
+# define DEPRECATED_IMPORT_DIRECTORY_PATH [[deprecated("Use allExportedTypeNames().")]]
+# define DEPRECATED_REQUIRED_IMPORT_STRING [[deprecated("Use allExportedTypeNames().")]]
#else
# define DEPRECATED_TYPENAME
# define DEPRECATED_VERSION_NUMBER
# define DEPRECATED_COMPONENT_FILE_NAME
+# define DEPRECATED_IMPORT_DIRECTORY_PATH
+# define DEPRECATED_REQUIRED_IMPORT_STRING
#endif
namespace QmlDesigner {
@@ -181,6 +185,7 @@ public:
bool isQtQuick3DLight() const;
bool isQtQuickListModel() const;
bool isQtQuickListView() const;
+ bool isQtQuickGridView() const;
bool isQtQuick3DMaterial() const;
bool isQtQuick3DModel() const;
bool isQtQuick3DNode() const;
@@ -237,8 +242,8 @@ public:
bool usesCustomParser() const;
bool isEnumeration() const;
- QString importDirectoryPath() const;
- QString requiredImportString() const;
+ DEPRECATED_IMPORT_DIRECTORY_PATH QString importDirectoryPath() const;
+ DEPRECATED_REQUIRED_IMPORT_STRING QString requiredImportString() const;
friend bool operator==(const NodeMetaInfo &first, const NodeMetaInfo &second)
{
diff --git a/src/plugins/qmldesigner/designercore/include/projectstorageids.h b/src/plugins/qmldesigner/designercore/include/projectstorageids.h
index 0b157e55e7..7c2e8c4b25 100644
--- a/src/plugins/qmldesigner/designercore/include/projectstorageids.h
+++ b/src/plugins/qmldesigner/designercore/include/projectstorageids.h
@@ -48,6 +48,8 @@ using EnumerationDeclarationIds = std::vector<EnumerationDeclarationId>;
using SourceContextId = Sqlite::BasicId<BasicIdType::SourceContext, int>;
using SourceContextIds = std::vector<SourceContextId>;
+template<std::size_t size>
+using SmallSourceContextIds = QVarLengthArray<SourceContextId, size>;
using SourceId = Sqlite::BasicId<BasicIdType::Source, int>;
using SourceIds = std::vector<SourceId>;
diff --git a/src/plugins/qmldesigner/designercore/include/rewriterview.h b/src/plugins/qmldesigner/designercore/include/rewriterview.h
index 0134349682..92c79c7863 100644
--- a/src/plugins/qmldesigner/designercore/include/rewriterview.h
+++ b/src/plugins/qmldesigner/designercore/include/rewriterview.h
@@ -8,11 +8,11 @@
#include "documentmessage.h"
#include "rewritertransaction.h"
-#include <QScopedPointer>
#include <QTimer>
#include <QUrl>
#include <functional>
+#include <memory>
namespace QmlJS {
class Document;
@@ -203,9 +203,9 @@ private: //variables
bool m_checkLinkErrors = true;
DifferenceHandling m_differenceHandling;
- QScopedPointer<Internal::ModelNodePositionStorage> m_positionStorage;
- QScopedPointer<Internal::ModelToTextMerger> m_modelToTextMerger;
- QScopedPointer<Internal::TextToModelMerger> m_textToModelMerger;
+ std::unique_ptr<Internal::ModelNodePositionStorage> m_positionStorage;
+ std::unique_ptr<Internal::ModelToTextMerger> m_modelToTextMerger;
+ std::unique_ptr<Internal::TextToModelMerger> m_textToModelMerger;
QList<DocumentMessage> m_errors;
QList<DocumentMessage> m_warnings;
RewriterTransaction m_removeDefaultPropertyTransaction;
diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
index 1b965db66a..484f18e42b 100644
--- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
@@ -91,7 +91,6 @@
#include <QMultiHash>
#include <QPainter>
#include <QPicture>
-#include <QScopedPointer>
#include <QTimerEvent>
#include <QUrl>
@@ -1031,8 +1030,20 @@ TypeName createQualifiedTypeName(const ModelNode &node)
auto exportedTypes = node.metaInfo().exportedTypeNamesForSourceId(model->fileUrlSourceId());
if (exportedTypes.size()) {
const auto &exportedType = exportedTypes.front();
- Utils::PathString typeName = model->projectStorage()->moduleName(exportedType.moduleId);
- typeName += '/';
+ using Storage::ModuleKind;
+ auto module = model->projectStorage()->module(exportedType.moduleId);
+ Utils::PathString typeName;
+ switch (module.kind) {
+ case ModuleKind::QmlLibrary:
+ typeName += module.name;
+ typeName += '/';
+ break;
+ case ModuleKind::PathLibrary:
+ break;
+ case ModuleKind::CppLibrary:
+ break;
+ }
+
typeName += exportedType.name;
return typeName.toQByteArray();
@@ -1205,6 +1216,13 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand()
if (stateNode.isValid() && stateNode.metaInfo().isQtQuickState())
stateInstanceId = stateNode.internalId();
+ QHash<QString, QVariantMap> sceneStates = m_edit3DToolStates[model()->fileUrl()];
+ QHash<QString, QVariantMap> projectStates = m_edit3DToolStates[
+ QUrl::fromLocalFile(m_externalDependencies.currentProjectDirPath())];
+ const QString ptsId = "@PTS";
+ if (projectStates.contains(ptsId))
+ sceneStates.insert(ptsId, projectStates[ptsId]);
+
return CreateSceneCommand(instanceContainerList,
reparentContainerList,
idContainerList,
@@ -1215,7 +1233,7 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand()
mockupTypesVector,
model()->fileUrl(),
m_externalDependencies.currentResourcePath(),
- m_edit3DToolStates[model()->fileUrl()],
+ sceneStates,
lastUsedLanguage,
m_captureImageMinimumSize,
m_captureImageMaximumSize,
@@ -1733,7 +1751,12 @@ void NodeInstanceView::handlePuppetToCreatorCommand(const PuppetToCreatorCommand
auto data = qvariant_cast<QVariantList>(command.data());
if (data.size() == 3) {
QString qmlId = data[0].toString();
- m_edit3DToolStates[model()->fileUrl()][qmlId].insert(data[1].toString(), data[2]);
+ QUrl mainKey;
+ if (qmlId == "@PTS") // Project tool state
+ mainKey = QUrl::fromLocalFile(m_externalDependencies.currentProjectDirPath());
+ else
+ mainKey = model()->fileUrl();
+ m_edit3DToolStates[mainKey][qmlId].insert(data[1].toString(), data[2]);
}
}
} else if (command.type() == PuppetToCreatorCommand::Render3DView) {
@@ -1847,7 +1870,7 @@ QVariant NodeInstanceView::modelNodePreviewImageDataToVariant(const ModelNodePre
placeHolder = {150, 150};
// Placeholder has transparency, but we don't want to show the checkerboard, so
// paint in the correct background color
- placeHolder.fill(Utils::creatorTheme()->color(Utils::Theme::BackgroundColorNormal));
+ placeHolder.fill(Utils::creatorColor(Utils::Theme::BackgroundColorNormal));
QPainter painter(&placeHolder);
painter.drawPixmap(0, 0, 150, 150, placeHolderSrc);
}
diff --git a/src/plugins/qmldesigner/designercore/metainfo/itemlibraryentry.cpp b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryentry.cpp
index 806da7e7c4..2aec766002 100644
--- a/src/plugins/qmldesigner/designercore/metainfo/itemlibraryentry.cpp
+++ b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryentry.cpp
@@ -22,7 +22,7 @@ class ItemLibraryEntryData
public:
QString name;
TypeName typeName;
- NodeMetaInfo metaInfo;
+ TypeId typeId;
QString category;
int majorVersion{-1};
int minorVersion{-1};
@@ -64,12 +64,12 @@ ItemLibraryEntry::ItemLibraryEntry()
: m_data(std::make_shared<Internal::ItemLibraryEntryData>())
{}
-ItemLibraryEntry::ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry,
- const ProjectStorageType &projectStorage)
+ItemLibraryEntry::ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry)
: ItemLibraryEntry{}
{
m_data->name = entry.name.toQString();
- m_data->metaInfo = {entry.typeId, &projectStorage};
+ m_data->typeId = entry.typeId;
+ m_data->typeName = entry.typeName.toQByteArray();
m_data->category = entry.category.toQString();
if (entry.iconPath.size())
m_data->libraryEntryIconPath = entry.iconPath.toQString();
@@ -87,6 +87,8 @@ ItemLibraryEntry::ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry,
m_data->extraFilePaths.emplace_back(extraFilePath.toQString());
}
+ItemLibraryEntry::~ItemLibraryEntry() = default;
+
QString ItemLibraryEntry::name() const
{
return m_data->name;
@@ -97,9 +99,9 @@ TypeName ItemLibraryEntry::typeName() const
return m_data->typeName;
}
-const NodeMetaInfo &ItemLibraryEntry::metaInfo() const
+TypeId ItemLibraryEntry::typeId() const
{
- return m_data->metaInfo;
+ return m_data->typeId;
}
QString ItemLibraryEntry::qmlSource() const
@@ -245,6 +247,7 @@ QDataStream &operator<<(QDataStream &stream, const ItemLibraryEntry &itemLibrary
stream << itemLibraryEntry.m_data->qmlSource;
stream << itemLibraryEntry.m_data->customComponentSource;
stream << itemLibraryEntry.m_data->extraFilePaths;
+ stream << itemLibraryEntry.m_data->typeId.internalId();
return stream;
}
@@ -270,6 +273,9 @@ QDataStream &operator>>(QDataStream &stream, ItemLibraryEntry &itemLibraryEntry)
stream >> itemLibraryEntry.m_data->qmlSource;
stream >> itemLibraryEntry.m_data->customComponentSource;
stream >> itemLibraryEntry.m_data->extraFilePaths;
+ TypeId::DatabaseType internalTypeId;
+ stream >> internalTypeId;
+ itemLibraryEntry.m_data->typeId = TypeId::create(internalTypeId);
return stream;
}
@@ -295,11 +301,10 @@ QDebug operator<<(QDebug debug, const ItemLibraryEntry &itemLibraryEntry)
return debug.space();
}
-QList<ItemLibraryEntry> toItemLibraryEntries(const Storage::Info::ItemLibraryEntries &entries,
- const ProjectStorageType &projectStorage)
+QList<ItemLibraryEntry> toItemLibraryEntries(const Storage::Info::ItemLibraryEntries &entries)
{
return Utils::transform<QList<ItemLibraryEntry>>(entries, [&](const auto &entry) {
- return ItemLibraryEntry{entry, projectStorage};
+ return ItemLibraryEntry{entry};
});
}
diff --git a/src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp b/src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp
index af61ef6573..0addc6884d 100644
--- a/src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp
+++ b/src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp
@@ -272,7 +272,7 @@ inline QString deEscape(const QString &value)
inline QVariant deEscapeVariant(const QVariant &value)
{
- if (value.typeId() == QVariant::String)
+ if (value.typeId() == QMetaType::QString)
return deEscape(value.toString());
return value;
}
diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp
index 32e68a3cdc..1f9a3e42bd 100644
--- a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp
+++ b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp
@@ -106,14 +106,15 @@ QmlDesigner::NodeHints::NodeHints(const ModelNode &node)
}
NodeHints::NodeHints(const NodeMetaInfo &metaInfo)
+ : m_metaInfo{metaInfo}
{
for (const auto &[name, expression] : metaInfo.typeHints())
m_hints.insert(name.toQString(), expression.toQString());
}
-NodeHints::NodeHints(const ItemLibraryEntry &entry)
+NodeHints::NodeHints(const ItemLibraryEntry &entry, [[maybe_unused]] Model *model)
#ifdef QDS_USE_PROJECTSTORAGE
- : NodeHints{entry.metaInfo()}
+ : NodeHints{NodeMetaInfo{entry.typeId(), model->projectStorage()}}
#endif
{
if constexpr (!useProjectStorage())
@@ -135,7 +136,7 @@ bool NodeHints::canBeContainerFor(const ModelNode &potenialChild) const
if (!isValid())
return true;
- auto flagIs = m_modelNode.metaInfo().canBeContainer();
+ auto flagIs = m_metaInfo.canBeContainer();
if (flagIs != FlagIs::Set)
return convert(flagIs);
@@ -151,7 +152,7 @@ bool NodeHints::forceClip() const
if (isSwipeView(modelNode()))
return true;
- auto flagIs = m_modelNode.metaInfo().forceClip();
+ auto flagIs = m_metaInfo.forceClip();
if (flagIs != FlagIs::Set)
return convert(flagIs);
@@ -167,7 +168,7 @@ bool NodeHints::doesLayoutChildren() const
if (isSwipeView(modelNode()))
return true;
- auto flagIs = m_modelNode.metaInfo().doesLayoutChildren();
+ auto flagIs = m_metaInfo.doesLayoutChildren();
if (flagIs != FlagIs::Set)
return convert(flagIs);
@@ -177,7 +178,7 @@ bool NodeHints::doesLayoutChildren() const
bool NodeHints::canBeDroppedInFormEditor() const
{
- auto flagIs = m_modelNode.metaInfo().canBeDroppedInFormEditor();
+ auto flagIs = m_metaInfo.canBeDroppedInFormEditor();
if (flagIs != FlagIs::Set)
return convert(flagIs);
@@ -187,7 +188,7 @@ bool NodeHints::canBeDroppedInFormEditor() const
bool NodeHints::canBeDroppedInNavigator() const
{
- auto flagIs = m_modelNode.metaInfo().canBeDroppedInNavigator();
+ auto flagIs = m_metaInfo.canBeDroppedInNavigator();
if (flagIs != FlagIs::Set)
return convert(flagIs);
@@ -197,7 +198,7 @@ bool NodeHints::canBeDroppedInNavigator() const
bool NodeHints::canBeDroppedInView3D() const
{
- auto flagIs = m_modelNode.metaInfo().canBeDroppedInView3D();
+ auto flagIs = m_metaInfo.canBeDroppedInView3D();
if (flagIs != FlagIs::Set)
return convert(flagIs);
@@ -210,7 +211,7 @@ bool NodeHints::isMovable() const
if (!isValid())
return true;
- auto flagIs = m_modelNode.metaInfo().isMovable();
+ auto flagIs = m_metaInfo.isMovable();
if (flagIs != FlagIs::Set)
return convert(flagIs);
@@ -223,7 +224,7 @@ bool NodeHints::isResizable() const
if (!isValid())
return true;
- auto flagIs = m_modelNode.metaInfo().isResizable();
+ auto flagIs = m_metaInfo.isResizable();
if (flagIs != FlagIs::Set)
return convert(flagIs);
@@ -236,7 +237,7 @@ bool NodeHints::hasFormEditorItem() const
if (!isValid())
return true;
- auto flagIs = m_modelNode.metaInfo().hasFormEditorItem();
+ auto flagIs = m_metaInfo.hasFormEditorItem();
if (flagIs != FlagIs::Set)
return convert(flagIs);
@@ -252,7 +253,7 @@ bool NodeHints::isStackedContainer() const
if (isSwipeView(modelNode()))
return true;
- auto flagIs = m_modelNode.metaInfo().isStackedContainer();
+ auto flagIs = m_metaInfo.isStackedContainer();
if (flagIs != FlagIs::Set)
return convert(flagIs);
@@ -299,7 +300,7 @@ bool NodeHints::takesOverRenderingOfChildren() const
if (!isValid())
return false;
- auto flagIs = m_modelNode.metaInfo().takesOverRenderingOfChildren();
+ auto flagIs = m_metaInfo.takesOverRenderingOfChildren();
if (flagIs != FlagIs::Set)
return convert(flagIs);
@@ -312,7 +313,7 @@ bool NodeHints::visibleInNavigator() const
if (!isValid())
return false;
- auto flagIs = m_modelNode.metaInfo().visibleInNavigator();
+ auto flagIs = m_metaInfo.visibleInNavigator();
if (flagIs != FlagIs::Set)
return convert(flagIs);
@@ -322,7 +323,7 @@ bool NodeHints::visibleInNavigator() const
bool NodeHints::visibleInLibrary() const
{
- auto flagIs = m_modelNode.metaInfo().visibleInLibrary();
+ auto flagIs = m_metaInfo.visibleInLibrary();
if (flagIs != FlagIs::Set)
return convert(flagIs);
@@ -391,9 +392,9 @@ NodeHints NodeHints::fromModelNode(const ModelNode &modelNode)
return NodeHints(modelNode);
}
-NodeHints NodeHints::fromItemLibraryEntry(const ItemLibraryEntry &entry)
+NodeHints NodeHints::fromItemLibraryEntry(const ItemLibraryEntry &entry, Model *model)
{
- return NodeHints(entry);
+ return NodeHints(entry, model);
}
const ModelNode &NodeHints::modelNode() const
diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp
index b8c3e610be..c656634272 100644
--- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp
+++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp
@@ -53,6 +53,10 @@ NodeMetaInfo object will result in an InvalidMetaInfoException being thrown.
namespace {
+using Storage::ModuleKind;
+
+auto category = MetaInfoTracing::category;
+
struct TypeDescription
{
QString className;
@@ -1493,15 +1497,20 @@ MetaInfoType NodeMetaInfo::type() const
{
if constexpr (useProjectStorage()) {
if (isValid()) {
- switch (typeData().traits.kind) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get type"_t, category(), keyValue("type id", m_typeId)};
+ auto kind = typeData().traits.kind;
+ tracer.end(keyValue("type kind", kind));
+
+ switch (kind) {
case Storage::TypeTraitsKind::Reference:
return MetaInfoType::Reference;
case Storage::TypeTraitsKind::Value:
return MetaInfoType::Value;
case Storage::TypeTraitsKind::Sequence:
return MetaInfoType::Sequence;
- default:
- break;
+ case Storage::TypeTraitsKind::None:
+ return MetaInfoType::None;
}
}
}
@@ -1511,16 +1520,38 @@ MetaInfoType NodeMetaInfo::type() const
bool NodeMetaInfo::isFileComponent() const
{
- if constexpr (useProjectStorage())
- return isValid() && typeData().traits.isFileComponent;
- else
+ if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return {};
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is file component"_t, category(), keyValue("type id", m_typeId)};
+
+ auto isFileComponent = typeData().traits.isFileComponent;
+
+ tracer.end(keyValue("is file component", isFileComponent));
+
+ return isFileComponent;
+
+ } else {
return isValid() && m_privateData->isFileComponent();
+ }
}
bool NodeMetaInfo::isProjectComponent() const
{
if constexpr (useProjectStorage()) {
- return isValid() && typeData().traits.isProjectComponent;
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is project component"_t, category(), keyValue("type id", m_typeId)};
+
+ auto isProjectComponent = typeData().traits.isProjectComponent;
+
+ tracer.end(keyValue("is project component", isProjectComponent));
+
+ return isProjectComponent;
}
return false;
@@ -1529,7 +1560,17 @@ bool NodeMetaInfo::isProjectComponent() const
bool NodeMetaInfo::isInProjectModule() const
{
if constexpr (useProjectStorage()) {
- return isValid() && typeData().traits.isInProjectModule;
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is project module"_t, category(), keyValue("type id", m_typeId)};
+
+ auto isInProjectModule = typeData().traits.isInProjectModule;
+
+ tracer.end(keyValue("is project module", isInProjectModule));
+
+ return isInProjectModule;
}
return false;
@@ -1538,10 +1579,17 @@ bool NodeMetaInfo::isInProjectModule() const
FlagIs NodeMetaInfo::canBeContainer() const
{
if constexpr (useProjectStorage()) {
- if (isValid())
- return typeData().traits.canBeContainer;
+ if (!isValid())
+ return FlagIs::False;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"can be container"_t, category(), keyValue("type id", m_typeId)};
- return FlagIs::False;
+ auto canBeContainer = typeData().traits.canBeContainer;
+
+ tracer.end(keyValue("can be container", canBeContainer));
+
+ return canBeContainer;
}
return FlagIs::Set;
@@ -1550,10 +1598,17 @@ FlagIs NodeMetaInfo::canBeContainer() const
FlagIs NodeMetaInfo::forceClip() const
{
if constexpr (useProjectStorage()) {
- if (isValid())
- return typeData().traits.forceClip;
+ if (!isValid())
+ return FlagIs::False;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"force clip"_t, category(), keyValue("type id", m_typeId)};
+
+ auto forceClip = typeData().traits.forceClip;
- return FlagIs::False;
+ tracer.end(keyValue("force clip", forceClip));
+
+ return forceClip;
}
return FlagIs::Set;
@@ -1562,10 +1617,17 @@ FlagIs NodeMetaInfo::forceClip() const
FlagIs NodeMetaInfo::doesLayoutChildren() const
{
if constexpr (useProjectStorage()) {
- if (isValid())
- return typeData().traits.doesLayoutChildren;
+ if (!isValid())
+ return FlagIs::False;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"does layout children"_t, category(), keyValue("type id", m_typeId)};
+
+ auto doesLayoutChildren = typeData().traits.doesLayoutChildren;
+
+ tracer.end(keyValue("does layout children", doesLayoutChildren));
- return FlagIs::False;
+ return doesLayoutChildren;
}
return FlagIs::Set;
@@ -1574,10 +1636,19 @@ FlagIs NodeMetaInfo::doesLayoutChildren() const
FlagIs NodeMetaInfo::canBeDroppedInFormEditor() const
{
if constexpr (useProjectStorage()) {
- if (isValid())
- return typeData().traits.canBeDroppedInFormEditor;
+ if (!isValid())
+ return FlagIs::False;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"can be dropped in form editor"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
+ auto canBeDroppedInFormEditor = typeData().traits.canBeDroppedInFormEditor;
+
+ tracer.end(keyValue("can be dropped in form editor", canBeDroppedInFormEditor));
- return FlagIs::False;
+ return canBeDroppedInFormEditor;
}
return FlagIs::Set;
@@ -1586,10 +1657,19 @@ FlagIs NodeMetaInfo::canBeDroppedInFormEditor() const
FlagIs NodeMetaInfo::canBeDroppedInNavigator() const
{
if constexpr (useProjectStorage()) {
- if (isValid())
- return typeData().traits.canBeDroppedInNavigator;
+ if (!isValid())
+ return FlagIs::False;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"can be dropped in navigator"_t,
+ category(),
+ keyValue("type id", m_typeId)};
- return FlagIs::False;
+ auto canBeDroppedInNavigator = typeData().traits.canBeDroppedInNavigator;
+
+ tracer.end(keyValue("can be dropped in navigator", canBeDroppedInNavigator));
+
+ return canBeDroppedInNavigator;
}
return FlagIs::Set;
@@ -1598,10 +1678,19 @@ FlagIs NodeMetaInfo::canBeDroppedInNavigator() const
FlagIs NodeMetaInfo::canBeDroppedInView3D() const
{
if constexpr (useProjectStorage()) {
- if (isValid())
- return typeData().traits.canBeDroppedInView3D;
+ if (!isValid())
+ return FlagIs::False;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"can be dropped in view3d"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
+ auto canBeDroppedInView3D = typeData().traits.canBeDroppedInView3D;
- return FlagIs::False;
+ tracer.end(keyValue("can be dropped in view3d", canBeDroppedInView3D));
+
+ return canBeDroppedInView3D;
}
return FlagIs::Set;
@@ -1610,10 +1699,17 @@ FlagIs NodeMetaInfo::canBeDroppedInView3D() const
FlagIs NodeMetaInfo::isMovable() const
{
if constexpr (useProjectStorage()) {
- if (isValid())
- return typeData().traits.isMovable;
+ if (!isValid())
+ return FlagIs::False;
- return FlagIs::False;
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is movable"_t, category(), keyValue("type id", m_typeId)};
+
+ auto isMovable = typeData().traits.isMovable;
+
+ tracer.end(keyValue("is movable", isMovable));
+
+ return isMovable;
}
return FlagIs::Set;
@@ -1622,10 +1718,17 @@ FlagIs NodeMetaInfo::isMovable() const
FlagIs NodeMetaInfo::isResizable() const
{
if constexpr (useProjectStorage()) {
- if (isValid())
- return typeData().traits.isResizable;
+ if (!isValid())
+ return FlagIs::False;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is resizable"_t, category(), keyValue("type id", m_typeId)};
- return FlagIs::False;
+ auto isResizable = typeData().traits.isResizable;
+
+ tracer.end(keyValue("is resizable", isResizable));
+
+ return isResizable;
}
return FlagIs::Set;
@@ -1634,10 +1737,17 @@ FlagIs NodeMetaInfo::isResizable() const
FlagIs NodeMetaInfo::hasFormEditorItem() const
{
if constexpr (useProjectStorage()) {
- if (isValid())
- return typeData().traits.hasFormEditorItem;
+ if (!isValid())
+ return FlagIs::False;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"has form editor item"_t, category(), keyValue("type id", m_typeId)};
- return FlagIs::False;
+ auto hasFormEditorItem = typeData().traits.hasFormEditorItem;
+
+ tracer.end(keyValue("has form editor item", hasFormEditorItem));
+
+ return hasFormEditorItem;
}
return FlagIs::Set;
@@ -1646,10 +1756,17 @@ FlagIs NodeMetaInfo::hasFormEditorItem() const
FlagIs NodeMetaInfo::isStackedContainer() const
{
if constexpr (useProjectStorage()) {
- if (isValid())
- return typeData().traits.isStackedContainer;
+ if (!isValid())
+ return FlagIs::False;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is stacked container"_t, category(), keyValue("type id", m_typeId)};
+
+ auto isStackedContainer = typeData().traits.isStackedContainer;
- return FlagIs::False;
+ tracer.end(keyValue("is stacked container", isStackedContainer));
+
+ return isStackedContainer;
}
return FlagIs::Set;
@@ -1658,10 +1775,19 @@ FlagIs NodeMetaInfo::isStackedContainer() const
FlagIs NodeMetaInfo::takesOverRenderingOfChildren() const
{
if constexpr (useProjectStorage()) {
- if (isValid())
- return typeData().traits.takesOverRenderingOfChildren;
+ if (!isValid())
+ return FlagIs::False;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"takes over rendering of children"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
+ auto takesOverRenderingOfChildren = typeData().traits.takesOverRenderingOfChildren;
+
+ tracer.end(keyValue("takes over rendering of children", takesOverRenderingOfChildren));
- return FlagIs::False;
+ return takesOverRenderingOfChildren;
}
return FlagIs::Set;
@@ -1670,10 +1796,17 @@ FlagIs NodeMetaInfo::takesOverRenderingOfChildren() const
FlagIs NodeMetaInfo::visibleInNavigator() const
{
if constexpr (useProjectStorage()) {
- if (isValid())
- return typeData().traits.visibleInNavigator;
+ if (!isValid())
+ return FlagIs::False;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"visible in navigator"_t, category(), keyValue("type id", m_typeId)};
- return FlagIs::False;
+ auto visibleInNavigator = typeData().traits.visibleInNavigator;
+
+ tracer.end(keyValue("visible in navigator", visibleInNavigator));
+
+ return visibleInNavigator;
}
return FlagIs::Set;
@@ -1682,10 +1815,17 @@ FlagIs NodeMetaInfo::visibleInNavigator() const
FlagIs NodeMetaInfo::visibleInLibrary() const
{
if constexpr (useProjectStorage()) {
- if (isValid())
- return typeData().traits.visibleInLibrary;
+ if (!isValid())
+ return FlagIs::False;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"visible in library"_t, category(), keyValue("type id", m_typeId)};
+
+ auto visibleInLibrary = typeData().traits.visibleInLibrary;
- return FlagIs::False;
+ tracer.end(keyValue("visible in library", visibleInLibrary));
+
+ return visibleInLibrary;
}
return FlagIs::Set;
@@ -1697,6 +1837,12 @@ namespace {
TypeId typeId,
Utils::SmallStringView propertyName)
{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get combound property id"_t,
+ category(),
+ keyValue("type id", typeId),
+ keyValue("property name", propertyName)};
+
auto begin = propertyName.begin();
const auto end = propertyName.end();
@@ -1712,11 +1858,17 @@ namespace {
if (propertyId && found != end) {
begin = std::next(found);
- return projectStorage.propertyDeclarationId(propertyTypeId, {begin, end});
+ auto id = projectStorage.propertyDeclarationId(propertyTypeId, {begin, end});
+
+ tracer.end(keyValue("property id", id));
+
+ return id;
}
}
}
+ tracer.end(keyValue("property id", propertyId));
+
return propertyId;
}
@@ -1724,10 +1876,24 @@ namespace {
bool NodeMetaInfo::hasProperty(Utils::SmallStringView propertyName) const
{
- if constexpr (useProjectStorage())
- return isValid() && bool(propertyId(*m_projectStorage, m_typeId, propertyName));
- else
+ if constexpr (useProjectStorage()) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"has property"_t,
+ category(),
+ keyValue("type id", m_typeId),
+ keyValue("property name", propertyName)};
+
+ if (!isValid())
+ return false;
+
+ auto hasPropertyId = bool(propertyId(*m_projectStorage, m_typeId, propertyName));
+
+ tracer.end(keyValue("has property", hasPropertyId));
+
+ return hasPropertyId;
+ } else {
return isValid() && m_privateData->properties().contains(QByteArrayView(propertyName));
+ }
}
PropertyMetaInfos NodeMetaInfo::properties() const
@@ -1736,12 +1902,14 @@ PropertyMetaInfos NodeMetaInfo::properties() const
return {};
if constexpr (useProjectStorage()) {
- if (isValid()) {
- return Utils::transform<PropertyMetaInfos>(
- m_projectStorage->propertyDeclarationIds(m_typeId), [&](auto id) {
- return PropertyMetaInfo{id, m_projectStorage};
- });
- }
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get properties"_t, category(), keyValue("type id", m_typeId)};
+
+ return Utils::transform<PropertyMetaInfos>(
+ m_projectStorage->propertyDeclarationIds(m_typeId), [&](auto id) {
+ return PropertyMetaInfo{id, m_projectStorage};
+ });
+
} else {
const auto &properties = m_privateData->properties();
@@ -1753,19 +1921,22 @@ PropertyMetaInfos NodeMetaInfo::properties() const
return propertyMetaInfos;
}
-
- return {};
}
PropertyMetaInfos NodeMetaInfo::localProperties() const
{
+ if (!isValid())
+ return {};
+
if constexpr (useProjectStorage()) {
- if (isValid()) {
- return Utils::transform<PropertyMetaInfos>(
- m_projectStorage->localPropertyDeclarationIds(m_typeId), [&](auto id) {
- return PropertyMetaInfo{id, m_projectStorage};
- });
- }
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get local properties"_t, category(), keyValue("type id", m_typeId)};
+
+ return Utils::transform<PropertyMetaInfos>(
+ m_projectStorage->localPropertyDeclarationIds(m_typeId), [&](auto id) {
+ return PropertyMetaInfo{id, m_projectStorage};
+ });
+
} else {
const auto &properties = m_privateData->localProperties();
@@ -1777,71 +1948,82 @@ PropertyMetaInfos NodeMetaInfo::localProperties() const
return propertyMetaInfos;
}
-
- return {};
}
PropertyMetaInfo NodeMetaInfo::property(const PropertyName &propertyName) const
{
+ if (!isValid())
+ return {};
+
if constexpr (useProjectStorage()) {
- if (isValid())
- return {propertyId(*m_projectStorage, m_typeId, propertyName), m_projectStorage};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get property"_t,
+ category(),
+ keyValue("type id", m_typeId),
+ keyValue("property name", propertyName)};
+
+ return {propertyId(*m_projectStorage, m_typeId, propertyName), m_projectStorage};
} else {
if (hasProperty(propertyName)) {
return PropertyMetaInfo{m_privateData, propertyName};
}
+ return {};
}
-
- return {};
}
PropertyNameList NodeMetaInfo::signalNames() const
{
+ if (!isValid())
+ return {};
+
if constexpr (useProjectStorage()) {
- if (isValid()) {
- return Utils::transform<PropertyNameList>(m_projectStorage->signalDeclarationNames(
- m_typeId),
- [&](const auto &name) {
- return name.toQByteArray();
- });
- }
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get signal names"_t, category(), keyValue("type id", m_typeId)};
+
+ return Utils::transform<PropertyNameList>(m_projectStorage->signalDeclarationNames(m_typeId),
+ [&](const auto &name) {
+ return name.toQByteArray();
+ });
+
} else {
- if (isValid())
- return m_privateData->signalNames();
+ return m_privateData->signalNames();
}
-
- return {};
}
PropertyNameList NodeMetaInfo::slotNames() const
{
+ if (!isValid())
+ return {};
+
if constexpr (useProjectStorage()) {
- if (isValid()) {
- return Utils::transform<PropertyNameList>(m_projectStorage->functionDeclarationNames(
- m_typeId),
- [&](const auto &name) {
- return name.toQByteArray();
- });
- }
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get slot names"_t, category(), keyValue("type id", m_typeId)};
+ return Utils::transform<PropertyNameList>(m_projectStorage->functionDeclarationNames(m_typeId),
+ [&](const auto &name) {
+ return name.toQByteArray();
+ });
} else {
- if (isValid())
- return m_privateData->slotNames();
+ return m_privateData->slotNames();
}
-
- return {};
}
PropertyName NodeMetaInfo::defaultPropertyName() const
{
+ if (!isValid())
+ return {};
+
if constexpr (useProjectStorage()) {
- if (isValid()) {
- if (auto name = m_projectStorage->propertyName(defaultPropertyDeclarationId())) {
- return name->toQByteArray();
- }
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get default property name"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+ if (auto name = m_projectStorage->propertyName(defaultPropertyDeclarationId())) {
+ tracer.end(keyValue("default property name", name));
+ return name->toQByteArray();
}
+
} else {
- if (isValid())
- return m_privateData->defaultPropertyName();
+ return m_privateData->defaultPropertyName();
}
return {};
@@ -1849,88 +2031,128 @@ PropertyName NodeMetaInfo::defaultPropertyName() const
PropertyMetaInfo NodeMetaInfo::defaultProperty() const
{
+ if (!isValid())
+ return {};
+
if constexpr (useProjectStorage()) {
- if (isValid()) {
- return PropertyMetaInfo(defaultPropertyDeclarationId(), m_projectStorage);
- }
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get default property"_t, category(), keyValue("type id", m_typeId)};
+
+ auto id = defaultPropertyDeclarationId();
+
+ tracer.end(keyValue("default property id", id));
+
+ return PropertyMetaInfo(id, m_projectStorage);
} else {
return property(defaultPropertyName());
}
-
- return {};
}
bool NodeMetaInfo::hasDefaultProperty() const
{
- if constexpr (useProjectStorage())
- return isValid() && bool(defaultPropertyDeclarationId());
- else
+ if (!isValid())
+ return false;
+
+ if constexpr (useProjectStorage()) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"has default property"_t, category(), keyValue("type id", m_typeId)};
+ auto hasDefaultProperty = bool(defaultPropertyDeclarationId());
+ tracer.end(keyValue("has default property", hasDefaultProperty));
+
+ return hasDefaultProperty;
+ } else {
return !defaultPropertyName().isEmpty();
+ }
}
std::vector<NodeMetaInfo> NodeMetaInfo::selfAndPrototypes() const
{
+ if (!isValid())
+ return {};
+
if constexpr (useProjectStorage()) {
- if (isValid()) {
- return Utils::transform<NodeMetaInfos>(
- m_projectStorage->prototypeAndSelfIds(m_typeId), [&](TypeId typeId) {
- return NodeMetaInfo{typeId, m_projectStorage};
- });
- }
- } else {
- if (isValid()) {
- NodeMetaInfos hierarchy = {*this};
- Model *model = m_privateData->model();
- for (const TypeDescription &type : m_privateData->prototypes()) {
- auto &last = hierarchy.emplace_back(model,
- type.className.toUtf8(),
- type.majorVersion,
- type.minorVersion);
- if (!last.isValid())
- hierarchy.pop_back();
- }
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get self and prototypes"_t,
+ category(),
+ keyValue("type id", m_typeId)};
- return hierarchy;
+ return Utils::transform<NodeMetaInfos>(m_projectStorage->prototypeAndSelfIds(m_typeId),
+ [&](TypeId typeId) {
+ return NodeMetaInfo{typeId, m_projectStorage};
+ });
+ } else {
+ NodeMetaInfos hierarchy = {*this};
+ Model *model = m_privateData->model();
+ for (const TypeDescription &type : m_privateData->prototypes()) {
+ auto &last = hierarchy.emplace_back(model,
+ type.className.toUtf8(),
+ type.majorVersion,
+ type.minorVersion);
+ if (!last.isValid())
+ hierarchy.pop_back();
}
- }
- return {};
+ return hierarchy;
+ }
}
NodeMetaInfos NodeMetaInfo::prototypes() const
{
+ if (!isValid())
+ return {};
+
if constexpr (useProjectStorage()) {
- if (isValid()) {
- return Utils::transform<NodeMetaInfos>(
- m_projectStorage->prototypeIds(m_typeId), [&](TypeId typeId) {
- return NodeMetaInfo{typeId, m_projectStorage};
- });
- }
- } else {
- if (isValid()) {
- NodeMetaInfos hierarchy;
- Model *model = m_privateData->model();
- for (const TypeDescription &type : m_privateData->prototypes()) {
- auto &last = hierarchy.emplace_back(model,
- type.className.toUtf8(),
- type.majorVersion,
- type.minorVersion);
- if (!last.isValid())
- hierarchy.pop_back();
- }
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get prototypes"_t, category(), keyValue("type id", m_typeId)};
+ return Utils::transform<NodeMetaInfos>(m_projectStorage->prototypeIds(m_typeId),
+ [&](TypeId typeId) {
+ return NodeMetaInfo{typeId, m_projectStorage};
+ });
- return hierarchy;
+ } else {
+ NodeMetaInfos hierarchy;
+ Model *model = m_privateData->model();
+ for (const TypeDescription &type : m_privateData->prototypes()) {
+ auto &last = hierarchy.emplace_back(model,
+ type.className.toUtf8(),
+ type.majorVersion,
+ type.minorVersion);
+ if (!last.isValid())
+ hierarchy.pop_back();
}
+
+ return hierarchy;
}
+}
- return {};
+namespace {
+template<const char *moduleName, const char *typeName, ModuleKind moduleKind = ModuleKind::QmlLibrary>
+bool isBasedOnCommonType(NotNullPointer<const ProjectStorageType> projectStorage, TypeId typeId)
+{
+ if (!typeId)
+ return false;
+
+ auto base = projectStorage->commonTypeId<moduleName, typeName, moduleKind>();
+
+ return projectStorage->isBasedOn(typeId, base);
}
+} // namespace
bool NodeMetaInfo::defaultPropertyIsComponent() const
{
- if (hasDefaultProperty())
- return defaultProperty().propertyType().isQmlComponent();
+ if (!isValid())
+ return false;
- return false;
+ if (useProjectStorage()) {
+ auto id = defaultPropertyDeclarationId();
+ auto propertyDeclaration = m_projectStorage->propertyDeclaration(id);
+
+ using namespace Storage::Info;
+ return isBasedOnCommonType<QML, Component>(m_projectStorage, propertyDeclaration->typeId);
+ } else {
+ if (hasDefaultProperty())
+ return defaultProperty().propertyType().isQmlComponent();
+ return false;
+ }
}
TypeName NodeMetaInfo::displayName() const
@@ -1976,10 +2198,16 @@ int NodeMetaInfo::minorVersion() const
Storage::Info::ExportedTypeNames NodeMetaInfo::allExportedTypeNames() const
{
+ if (!isValid())
+ return {};
+
if constexpr (useProjectStorage()) {
- if (isValid()) {
- return m_projectStorage->exportedTypeNames(m_typeId);
- }
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get all exported type names"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
+ return m_projectStorage->exportedTypeNames(m_typeId);
}
return {};
@@ -1987,10 +2215,17 @@ Storage::Info::ExportedTypeNames NodeMetaInfo::allExportedTypeNames() const
Storage::Info::ExportedTypeNames NodeMetaInfo::exportedTypeNamesForSourceId(SourceId sourceId) const
{
+ if (!isValid())
+ return {};
+
if constexpr (useProjectStorage()) {
- if (isValid()) {
- return m_projectStorage->exportedTypeNames(m_typeId, sourceId);
- }
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get exported type names for source id"_t,
+ category(),
+ keyValue("type id", m_typeId),
+ keyValue("source id", sourceId)};
+
+ return m_projectStorage->exportedTypeNames(m_typeId, sourceId);
}
return {};
@@ -1998,9 +2233,18 @@ Storage::Info::ExportedTypeNames NodeMetaInfo::exportedTypeNamesForSourceId(Sour
Storage::Info::TypeHints NodeMetaInfo::typeHints() const
{
+ if (!isValid())
+ return {};
+
if constexpr (useProjectStorage()) {
- if (isValid())
- return m_projectStorage->typeHints(m_typeId);
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get type hints"_t, category(), keyValue("type id", m_typeId)};
+
+ auto hints = m_projectStorage->typeHints(m_typeId);
+
+ tracer.end(keyValue("type hints", hints));
+
+ return hints;
}
return {};
@@ -2008,9 +2252,18 @@ Storage::Info::TypeHints NodeMetaInfo::typeHints() const
Utils::PathString NodeMetaInfo::iconPath() const
{
+ if (!isValid())
+ return {};
+
if constexpr (useProjectStorage()) {
- if (isValid())
- return m_projectStorage->typeIconPath(m_typeId);
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get icon path"_t, category(), keyValue("type id", m_typeId)};
+
+ auto iconPath = m_projectStorage->typeIconPath(m_typeId);
+
+ tracer.end(keyValue("icon path", iconPath));
+
+ return iconPath;
}
return {};
@@ -2018,9 +2271,20 @@ Utils::PathString NodeMetaInfo::iconPath() const
Storage::Info::ItemLibraryEntries NodeMetaInfo::itemLibrariesEntries() const
{
+ if (!isValid())
+ return {};
+
if constexpr (useProjectStorage()) {
- if (isValid())
- return m_projectStorage->itemLibraryEntries(m_typeId);
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get item library entries"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
+ auto entries = m_projectStorage->itemLibraryEntries(m_typeId);
+
+ tracer.end(keyValue("item library entries", entries));
+
+ return entries;
}
return {};
@@ -2028,10 +2292,18 @@ Storage::Info::ItemLibraryEntries NodeMetaInfo::itemLibrariesEntries() const
SourceId NodeMetaInfo::sourceId() const
{
+ if (!isValid())
+ return SourceId{};
+
if constexpr (useProjectStorage()) {
- if (isValid()) {
- return typeData().sourceId;
- }
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get source id"_t, category(), keyValue("type id", m_typeId)};
+
+ auto id = typeData().sourceId;
+
+ tracer.end(keyValue("source id", id));
+
+ return id;
}
return SourceId{};
@@ -2064,18 +2336,31 @@ QString NodeMetaInfo::requiredImportString() const
if (!isValid())
return {};
- Import imp = m_privateData->requiredImport();
- if (!imp.isEmpty())
- return imp.toImportString();
+ if constexpr (!useProjectStorage()) {
+ Import imp = m_privateData->requiredImport();
+ if (!imp.isEmpty())
+ return imp.toImportString();
+ }
return {};
}
SourceId NodeMetaInfo::propertyEditorPathId() const
{
+ if (!isValid())
+ return SourceId{};
+
if (useProjectStorage()) {
- if (isValid())
- return m_projectStorage->propertyEditorPathId(m_typeId);
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get property editor path id"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
+ auto id = m_projectStorage->propertyEditorPathId(m_typeId);
+
+ tracer.end(keyValue("property editor path id", id));
+
+ return id;
}
return SourceId{};
@@ -2133,9 +2418,13 @@ bool NodeMetaInfo::isSubclassOf(const TypeName &type, int majorVersion, int mino
bool NodeMetaInfo::isSuitableForMouseAreaFill() const
{
if constexpr (useProjectStorage()) {
- if (!isValid()) {
+ if (!isValid())
return false;
- }
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is suitable for mouse area fill"_t,
+ category(),
+ keyValue("type id", m_typeId)};
using namespace Storage::Info;
auto itemId = m_projectStorage->commonTypeId<QtQuick, Item>();
@@ -2143,11 +2432,16 @@ bool NodeMetaInfo::isSuitableForMouseAreaFill() const
auto controlsControlId = m_projectStorage->commonTypeId<QtQuick_Controls, Control>();
auto templatesControlId = m_projectStorage->commonTypeId<QtQuick_Templates, Control>();
- return m_projectStorage->isBasedOn(m_typeId,
- itemId,
- mouseAreaId,
- controlsControlId,
- templatesControlId);
+ auto isSuitableForMouseAreaFill = m_projectStorage->isBasedOn(m_typeId,
+ itemId,
+ mouseAreaId,
+ controlsControlId,
+ templatesControlId);
+
+ tracer.end(keyValue("is suitable for mouse area fill", isSuitableForMouseAreaFill));
+
+ return isSuitableForMouseAreaFill;
+
} else {
return isSubclassOf("QtQuick.Item") && !isSubclassOf("QtQuick.MouseArea")
&& !isSubclassOf("QtQuick.Controls.Control")
@@ -2158,6 +2452,15 @@ bool NodeMetaInfo::isSuitableForMouseAreaFill() const
bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo) const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is based on"_t,
+ category(),
+ keyValue("type id", m_typeId),
+ keyValue("meta info type id", metaInfo.m_typeId)};
+
return m_projectStorage->isBasedOn(m_typeId, metaInfo.m_typeId);
} else {
if (!isValid())
@@ -2171,6 +2474,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo) const
bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo2) const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)};
+
return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, metaInfo2.m_typeId);
} else {
if (!isValid())
@@ -2189,6 +2498,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1,
const NodeMetaInfo &metaInfo3) const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)};
+
return m_projectStorage->isBasedOn(m_typeId,
metaInfo1.m_typeId,
metaInfo2.m_typeId,
@@ -2213,6 +2528,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1,
const NodeMetaInfo &metaInfo4) const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)};
+
return m_projectStorage->isBasedOn(m_typeId,
metaInfo1.m_typeId,
metaInfo2.m_typeId,
@@ -2240,6 +2561,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1,
const NodeMetaInfo &metaInfo5) const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)};
+
return m_projectStorage->isBasedOn(m_typeId,
metaInfo1.m_typeId,
metaInfo2.m_typeId,
@@ -2272,6 +2599,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1,
const NodeMetaInfo &metaInfo6) const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)};
+
return m_projectStorage->isBasedOn(m_typeId,
metaInfo1.m_typeId,
metaInfo2.m_typeId,
@@ -2309,6 +2642,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1,
const NodeMetaInfo &metaInfo7) const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)};
+
return m_projectStorage->isBasedOn(m_typeId,
metaInfo1.m_typeId,
metaInfo2.m_typeId,
@@ -2341,26 +2680,14 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1,
}
}
-namespace {
-template<const char *moduleName, const char *typeName>
-bool isBasedOnCommonType(NotNullPointer<const ProjectStorageType> projectStorage, TypeId typeId)
-{
- if (!typeId) {
- return false;
- }
-
- auto base = projectStorage->commonTypeId<moduleName, typeName>();
-
- return projectStorage->isBasedOn(typeId, base);
-}
-} // namespace
-
bool NodeMetaInfo::isGraphicalItem() const
{
if constexpr (useProjectStorage()) {
- if (!isValid()) {
+ if (!isValid())
return false;
- }
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is graphical item"_t, category(), keyValue("type id", m_typeId)};
using namespace Storage::Info;
auto itemId = m_projectStorage->commonTypeId<QtQuick, Item>();
@@ -2380,6 +2707,12 @@ bool NodeMetaInfo::isGraphicalItem() const
bool NodeMetaInfo::isQtObject() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is Qt object"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QML, QtObject>(m_projectStorage, m_typeId);
} else {
@@ -2390,6 +2723,14 @@ bool NodeMetaInfo::isQtObject() const
bool NodeMetaInfo::isQtQmlConnections() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is Qt Qml connections"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQml, Connections>(m_projectStorage, m_typeId);
} else {
@@ -2400,9 +2741,11 @@ bool NodeMetaInfo::isQtQmlConnections() const
bool NodeMetaInfo::isLayoutable() const
{
if constexpr (useProjectStorage()) {
- if (!isValid()) {
+ if (!isValid())
return false;
- }
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is layoutable"_t, category(), keyValue("type id", m_typeId)};
using namespace Storage::Info;
auto positionerId = m_projectStorage->commonTypeId<QtQuick, Positioner>();
@@ -2421,6 +2764,14 @@ bool NodeMetaInfo::isLayoutable() const
bool NodeMetaInfo::isQtQuickLayoutsLayout() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.Layouts.Layout"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick_Layouts, Layout>(m_projectStorage, m_typeId);
} else {
@@ -2431,9 +2782,11 @@ bool NodeMetaInfo::isQtQuickLayoutsLayout() const
bool NodeMetaInfo::isView() const
{
if constexpr (useProjectStorage()) {
- if (!isValid()) {
+ if (!isValid())
return false;
- }
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is view"_t, category(), keyValue("type id", m_typeId)};
using namespace Storage::Info;
auto listViewId = m_projectStorage->commonTypeId<QtQuick, ListView>();
@@ -2450,7 +2803,13 @@ bool NodeMetaInfo::isView() const
bool NodeMetaInfo::usesCustomParser() const
{
if constexpr (useProjectStorage()) {
- return isValid() && typeData().traits.usesCustomParser;
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"uses custom parser"_t, category(), keyValue("type id", m_typeId)};
+
+ return typeData().traits.usesCustomParser;
} else {
if (!isValid())
return false;
@@ -2476,8 +2835,14 @@ bool isTypeId(TypeId typeId, TypeIds... otherTypeIds)
bool NodeMetaInfo::isVector2D() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is vector2d"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
- return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, vector2d>());
+ return isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, vector2d>());
} else {
if (!m_privateData)
return false;
@@ -2491,8 +2856,14 @@ bool NodeMetaInfo::isVector2D() const
bool NodeMetaInfo::isVector3D() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is vector3d"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
- return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, vector3d>());
+ return isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, vector3d>());
} else {
if (!m_privateData)
return false;
@@ -2506,8 +2877,14 @@ bool NodeMetaInfo::isVector3D() const
bool NodeMetaInfo::isVector4D() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is vector4d"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
- return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, vector4d>());
+ return isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, vector4d>());
} else {
if (!m_privateData)
return false;
@@ -2521,6 +2898,14 @@ bool NodeMetaInfo::isVector4D() const
bool NodeMetaInfo::isQtQuickPropertyChanges() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.PropertyChanges"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick, Storage::Info::PropertyChanges>(m_projectStorage,
m_typeId);
@@ -2532,6 +2917,14 @@ bool NodeMetaInfo::isQtQuickPropertyChanges() const
bool NodeMetaInfo::isQtSafeRendererSafeRendererPicture() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is Qt.SafeRenderer.SafeRendererPicture"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<Qt_SafeRenderer, SafeRendererPicture>(m_projectStorage, m_typeId);
} else {
@@ -2542,6 +2935,14 @@ bool NodeMetaInfo::isQtSafeRendererSafeRendererPicture() const
bool NodeMetaInfo::isQtSafeRendererSafePicture() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is Qt.SafeRenderer.SafePicture"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<Qt_SafeRenderer, SafePicture>(m_projectStorage, m_typeId);
} else {
@@ -2552,6 +2953,14 @@ bool NodeMetaInfo::isQtSafeRendererSafePicture() const
bool NodeMetaInfo::isQtQuickTimelineKeyframe() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.Timeline.Keyframe"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick_Timeline, Keyframe>(m_projectStorage, m_typeId);
@@ -2563,6 +2972,14 @@ bool NodeMetaInfo::isQtQuickTimelineKeyframe() const
bool NodeMetaInfo::isQtQuickTimelineTimelineAnimation() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.Timeline.TimelineAnimation"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick_Timeline, TimelineAnimation>(m_projectStorage, m_typeId);
} else {
@@ -2573,6 +2990,14 @@ bool NodeMetaInfo::isQtQuickTimelineTimelineAnimation() const
bool NodeMetaInfo::isQtQuickTimelineTimeline() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.Timeline.Timeline"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick_Timeline, Timeline>(m_projectStorage, m_typeId);
} else {
@@ -2583,6 +3008,14 @@ bool NodeMetaInfo::isQtQuickTimelineTimeline() const
bool NodeMetaInfo::isQtQuickTimelineKeyframeGroup() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.Timeline.KeyframeGroup"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick_Timeline, KeyframeGroup>(m_projectStorage, m_typeId);
} else {
@@ -2593,9 +3026,11 @@ bool NodeMetaInfo::isQtQuickTimelineKeyframeGroup() const
bool NodeMetaInfo::isListOrGridView() const
{
if constexpr (useProjectStorage()) {
- if (!isValid()) {
+ if (!isValid())
return false;
- }
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is list or grid view"_t, category(), keyValue("type id", m_typeId)};
using namespace Storage::Info;
auto listViewId = m_projectStorage->commonTypeId<QtQuick, ListView>();
@@ -2609,9 +3044,11 @@ bool NodeMetaInfo::isListOrGridView() const
bool NodeMetaInfo::isNumber() const
{
if constexpr (useProjectStorage()) {
- if (!isValid()) {
+ if (!isValid())
return false;
- }
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is number"_t, category(), keyValue("type id", m_typeId)};
using namespace Storage::Info;
auto intId = m_projectStorage->builtinTypeId<int>();
@@ -2632,6 +3069,14 @@ bool NodeMetaInfo::isNumber() const
bool NodeMetaInfo::isQtQuickExtrasPicture() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.Extras.Picture"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick_Extras, Picture>(m_projectStorage, m_typeId);
} else {
@@ -2642,6 +3087,12 @@ bool NodeMetaInfo::isQtQuickExtrasPicture() const
bool NodeMetaInfo::isQtQuickImage() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.Image"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick, Image>(m_projectStorage, m_typeId);
@@ -2653,6 +3104,14 @@ bool NodeMetaInfo::isQtQuickImage() const
bool NodeMetaInfo::isQtQuickBorderImage() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.BorderImage"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick, BorderImage>(m_projectStorage, m_typeId);
@@ -2664,7 +3123,13 @@ bool NodeMetaInfo::isQtQuickBorderImage() const
bool NodeMetaInfo::isAlias() const
{
if constexpr (useProjectStorage()) {
- return false; // there is no type alias
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is alias"_t, category(), keyValue("type id", m_typeId)};
+
+ return false; // all types are already resolved
} else {
return isValid() && m_privateData->qualfiedTypeName() == "alias";
}
@@ -2673,6 +3138,14 @@ bool NodeMetaInfo::isAlias() const
bool NodeMetaInfo::isQtQuickPositioner() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.Positioner"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick, Positioner>(m_projectStorage, m_typeId);
@@ -2684,6 +3157,14 @@ bool NodeMetaInfo::isQtQuickPositioner() const
bool NodeMetaInfo::isQtQuickPropertyAnimation() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.PropertyAnimation"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick, PropertyAnimation>(m_projectStorage, m_typeId);
} else {
@@ -2694,6 +3175,12 @@ bool NodeMetaInfo::isQtQuickPropertyAnimation() const
bool NodeMetaInfo::isQtQuickRepeater() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.Repeater"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick, Repeater>(m_projectStorage, m_typeId);
} else {
@@ -2704,6 +3191,14 @@ bool NodeMetaInfo::isQtQuickRepeater() const
bool NodeMetaInfo::isQtQuickControlsTabBar() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.Controls.TabBar"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick_Controls, TabBar>(m_projectStorage, m_typeId);
} else {
@@ -2714,6 +3209,14 @@ bool NodeMetaInfo::isQtQuickControlsTabBar() const
bool NodeMetaInfo::isQtQuickControlsSwipeView() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.Controls.SwipeView"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick_Controls, SwipeView>(m_projectStorage, m_typeId);
} else {
@@ -2724,6 +3227,12 @@ bool NodeMetaInfo::isQtQuickControlsSwipeView() const
bool NodeMetaInfo::isQtQuick3DCamera() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.Camera"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, Camera>(m_projectStorage, m_typeId);
} else {
@@ -2734,6 +3243,14 @@ bool NodeMetaInfo::isQtQuick3DCamera() const
bool NodeMetaInfo::isQtQuick3DBakedLightmap() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.BakedLightmap"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, BakedLightmap>(m_projectStorage, m_typeId);
} else {
@@ -2744,6 +3261,12 @@ bool NodeMetaInfo::isQtQuick3DBakedLightmap() const
bool NodeMetaInfo::isQtQuick3DBuffer() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.Buffer"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, Buffer>(m_projectStorage, m_typeId);
} else {
@@ -2754,6 +3277,14 @@ bool NodeMetaInfo::isQtQuick3DBuffer() const
bool NodeMetaInfo::isQtQuick3DInstanceListEntry() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.InstanceListEntry"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, InstanceListEntry>(m_projectStorage, m_typeId);
} else {
@@ -2764,6 +3295,12 @@ bool NodeMetaInfo::isQtQuick3DInstanceListEntry() const
bool NodeMetaInfo::isQtQuick3DLight() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.Light"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, Light>(m_projectStorage, m_typeId);
} else {
@@ -2774,6 +3311,14 @@ bool NodeMetaInfo::isQtQuick3DLight() const
bool NodeMetaInfo::isQtQmlModelsListElement() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQml.Models.ListElement"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQml_Models, ListElement>(m_projectStorage, m_typeId);
} else {
@@ -2784,6 +3329,12 @@ bool NodeMetaInfo::isQtQmlModelsListElement() const
bool NodeMetaInfo::isQtQuickListModel() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.ListModel"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQml_Models, ListModel>(m_projectStorage, m_typeId);
} else {
@@ -2794,6 +3345,12 @@ bool NodeMetaInfo::isQtQuickListModel() const
bool NodeMetaInfo::isQtQuickListView() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.ListView"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick, ListView>(m_projectStorage, m_typeId);
} else {
@@ -2801,9 +3358,33 @@ bool NodeMetaInfo::isQtQuickListView() const
}
}
+bool QmlDesigner::NodeMetaInfo::isQtQuickGridView() const
+{
+ if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.GridView"_t, category(), keyValue("type id", m_typeId)};
+
+ using namespace Storage::Info;
+ return isBasedOnCommonType<QtQuick, GridView>(m_projectStorage, m_typeId);
+ } else {
+ return isValid() && (isSubclassOf("QtQuick.GridView"));
+ }
+}
+
bool NodeMetaInfo::isQtQuick3DInstanceList() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.InstanceList"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, InstanceList>(m_projectStorage, m_typeId);
} else {
@@ -2814,6 +3395,14 @@ bool NodeMetaInfo::isQtQuick3DInstanceList() const
bool NodeMetaInfo::isQtQuick3DParticles3DParticle3D() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.Particle3D"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D_Particles3D, Particle3D>(m_projectStorage, m_typeId);
} else {
@@ -2824,6 +3413,14 @@ bool NodeMetaInfo::isQtQuick3DParticles3DParticle3D() const
bool NodeMetaInfo::isQtQuick3DParticles3DParticleEmitter3D() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.ParticleEmitter3D"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D_Particles3D, ParticleEmitter3D>(m_projectStorage,
m_typeId);
@@ -2835,6 +3432,14 @@ bool NodeMetaInfo::isQtQuick3DParticles3DParticleEmitter3D() const
bool NodeMetaInfo::isQtQuick3DParticles3DAttractor3D() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.Attractor3D"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D_Particles3D, Attractor3D>(m_projectStorage, m_typeId);
} else {
@@ -2845,8 +3450,16 @@ bool NodeMetaInfo::isQtQuick3DParticles3DAttractor3D() const
bool NodeMetaInfo::isQtQuick3DParticlesAbstractShape() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.AbstractShape"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
- return isBasedOnCommonType<QtQuick3D_Particles3D_cppnative, QQuick3DParticleAbstractShape>(
+ return isBasedOnCommonType<QtQuick3D_Particles3D, QQuick3DParticleAbstractShape, ModuleKind::CppLibrary>(
m_projectStorage, m_typeId);
} else {
return isValid() && isSubclassOf("QQuick3DParticleAbstractShape");
@@ -2856,6 +3469,12 @@ bool NodeMetaInfo::isQtQuick3DParticlesAbstractShape() const
bool NodeMetaInfo::isQtQuickItem() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.Item"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick, Item>(m_projectStorage, m_typeId);
} else {
@@ -2866,6 +3485,12 @@ bool NodeMetaInfo::isQtQuickItem() const
bool NodeMetaInfo::isQtQuickPath() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.Path"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick, Path>(m_projectStorage, m_typeId);
} else {
@@ -2876,6 +3501,14 @@ bool NodeMetaInfo::isQtQuickPath() const
bool NodeMetaInfo::isQtQuickPauseAnimation() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.PauseAnimation"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick, PauseAnimation>(m_projectStorage, m_typeId);
} else {
@@ -2886,6 +3519,14 @@ bool NodeMetaInfo::isQtQuickPauseAnimation() const
bool NodeMetaInfo::isQtQuickTransition() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.Transition"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick, Transition>(m_projectStorage, m_typeId);
} else {
@@ -2896,6 +3537,14 @@ bool NodeMetaInfo::isQtQuickTransition() const
bool NodeMetaInfo::isQtQuickWindowWindow() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.Window.Window"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick_Window, Window>(m_projectStorage, m_typeId);
} else {
@@ -2906,6 +3555,12 @@ bool NodeMetaInfo::isQtQuickWindowWindow() const
bool NodeMetaInfo::isQtQuickLoader() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.Loader"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick, Loader>(m_projectStorage, m_typeId);
} else {
@@ -2916,6 +3571,12 @@ bool NodeMetaInfo::isQtQuickLoader() const
bool NodeMetaInfo::isQtQuickState() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.State"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick, State>(m_projectStorage, m_typeId);
} else {
@@ -2926,9 +3587,17 @@ bool NodeMetaInfo::isQtQuickState() const
bool NodeMetaInfo::isQtQuickStateOperation() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.StateOperation"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
- return isBasedOnCommonType<QtQuick_cppnative, QQuickStateOperation>(m_projectStorage,
- m_typeId);
+ return isBasedOnCommonType<QtQuick, QQuickStateOperation, ModuleKind::CppLibrary>(m_projectStorage,
+ m_typeId);
} else {
return isValid() && isSubclassOf("<cpp>.QQuickStateOperation");
}
@@ -2937,6 +3606,12 @@ bool NodeMetaInfo::isQtQuickStateOperation() const
bool NodeMetaInfo::isQtQuickText() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.Text"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick, Text>(m_projectStorage, m_typeId);
} else {
@@ -2947,6 +3622,14 @@ bool NodeMetaInfo::isQtQuickText() const
bool NodeMetaInfo::isQtMultimediaSoundEffect() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtMultimedia.SoundEffect"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtMultimedia, SoundEffect>(m_projectStorage, m_typeId);
} else {
@@ -2957,9 +3640,11 @@ bool NodeMetaInfo::isQtMultimediaSoundEffect() const
bool NodeMetaInfo::isFlowViewItem() const
{
if constexpr (useProjectStorage()) {
- if (!isValid()) {
+ if (!isValid())
return false;
- }
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is FlowView.ViewItem"_t, category(), keyValue("type id", m_typeId)};
using namespace Storage::Info;
auto flowItemId = m_projectStorage->commonTypeId<FlowView, FlowItem>();
@@ -2976,6 +3661,12 @@ bool NodeMetaInfo::isFlowViewItem() const
bool NodeMetaInfo::isFlowViewFlowItem() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is FlowView.FlowItem"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<FlowView, FlowItem>(m_projectStorage, m_typeId);
} else {
@@ -2986,6 +3677,12 @@ bool NodeMetaInfo::isFlowViewFlowItem() const
bool NodeMetaInfo::isFlowViewFlowView() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is FlowView.FlowView"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<FlowView, FlowView>(m_projectStorage, m_typeId);
} else {
@@ -3006,6 +3703,14 @@ bool NodeMetaInfo::isFlowViewFlowActionArea() const
bool NodeMetaInfo::isFlowViewFlowTransition() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is FlowView.FlowTransition"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<FlowView, FlowTransition>(m_projectStorage, m_typeId);
} else {
@@ -3016,6 +3721,14 @@ bool NodeMetaInfo::isFlowViewFlowTransition() const
bool NodeMetaInfo::isFlowViewFlowDecision() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is FlowView.FlowDecision"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<FlowView, FlowDecision>(m_projectStorage, m_typeId);
} else {
@@ -3026,6 +3739,14 @@ bool NodeMetaInfo::isFlowViewFlowDecision() const
bool NodeMetaInfo::isFlowViewFlowWildcard() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is FlowView.FlowWildcard"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<FlowView, FlowWildcard>(m_projectStorage, m_typeId);
} else {
@@ -3036,6 +3757,14 @@ bool NodeMetaInfo::isFlowViewFlowWildcard() const
bool NodeMetaInfo::isQtQuickStudioComponentsGroupItem() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.Studio.Components.GroupItem"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick_Studio_Components, GroupItem>(m_projectStorage, m_typeId);
} else {
@@ -3046,6 +3775,14 @@ bool NodeMetaInfo::isQtQuickStudioComponentsGroupItem() const
bool NodeMetaInfo::isQtQuickStudioUtilsJsonListModel() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick.Studio.Utils.JsonListModel"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick_Studio_Components, JsonListModel>(m_projectStorage,
m_typeId);
@@ -3057,6 +3794,12 @@ bool NodeMetaInfo::isQtQuickStudioUtilsJsonListModel() const
bool NodeMetaInfo::isQmlComponent() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QML.Component"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QML, Component>(m_projectStorage, m_typeId);
} else {
@@ -3072,6 +3815,12 @@ bool NodeMetaInfo::isQmlComponent() const
bool NodeMetaInfo::isFont() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is font"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, font>());
} else {
@@ -3082,6 +3831,12 @@ bool NodeMetaInfo::isFont() const
bool NodeMetaInfo::isColor() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is color"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId<QColor>());
} else {
@@ -3097,6 +3852,12 @@ bool NodeMetaInfo::isColor() const
bool NodeMetaInfo::isBool() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is bool"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId<bool>());
} else {
@@ -3112,6 +3873,12 @@ bool NodeMetaInfo::isBool() const
bool NodeMetaInfo::isInteger() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is integer"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId<int>());
} else {
@@ -3127,9 +3894,11 @@ bool NodeMetaInfo::isInteger() const
bool NodeMetaInfo::isFloat() const
{
if constexpr (useProjectStorage()) {
- if (!isValid()) {
+ if (!isValid())
return false;
- }
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is float"_t, category(), keyValue("type id", m_typeId)};
using namespace Storage::Info;
auto floatId = m_projectStorage->builtinTypeId<float>();
@@ -3149,6 +3918,12 @@ bool NodeMetaInfo::isFloat() const
bool NodeMetaInfo::isVariant() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is variant"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId<QVariant>());
} else {
@@ -3164,6 +3939,12 @@ bool NodeMetaInfo::isVariant() const
bool NodeMetaInfo::isString() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is string"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId<QString>());
} else {
@@ -3179,6 +3960,12 @@ bool NodeMetaInfo::isString() const
bool NodeMetaInfo::isUrl() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is url"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId<QUrl>());
} else {
@@ -3194,6 +3981,12 @@ bool NodeMetaInfo::isUrl() const
bool NodeMetaInfo::isQtQuick3DTexture() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.Texture"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, Texture>(m_projectStorage, m_typeId);
} else {
@@ -3205,6 +3998,12 @@ bool NodeMetaInfo::isQtQuick3DTexture() const
bool NodeMetaInfo::isQtQuick3DShader() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.Shader"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, Shader>(m_projectStorage, m_typeId);
} else {
@@ -3215,6 +4014,12 @@ bool NodeMetaInfo::isQtQuick3DShader() const
bool NodeMetaInfo::isQtQuick3DPass() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.Pass"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, Pass>(m_projectStorage, m_typeId);
} else {
@@ -3225,6 +4030,12 @@ bool NodeMetaInfo::isQtQuick3DPass() const
bool NodeMetaInfo::isQtQuick3DCommand() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.Command"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, Command>(m_projectStorage, m_typeId);
} else {
@@ -3235,6 +4046,14 @@ bool NodeMetaInfo::isQtQuick3DCommand() const
bool NodeMetaInfo::isQtQuick3DDefaultMaterial() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.DefaultMaterial"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, DefaultMaterial>(m_projectStorage, m_typeId);
} else {
@@ -3255,6 +4074,12 @@ bool NodeMetaInfo::isQtQuick3DMaterial() const
bool NodeMetaInfo::isQtQuick3DModel() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.Model"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, Storage::Info::Model>(m_projectStorage, m_typeId);
} else {
@@ -3265,6 +4090,12 @@ bool NodeMetaInfo::isQtQuick3DModel() const
bool NodeMetaInfo::isQtQuick3DNode() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.Node"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, Node>(m_projectStorage, m_typeId);
} else {
@@ -3275,6 +4106,14 @@ bool NodeMetaInfo::isQtQuick3DNode() const
bool NodeMetaInfo::isQtQuick3DParticles3DAffector3D() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.Affector3D"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D_Particles3D, Affector3D>(m_projectStorage, m_typeId);
} else {
@@ -3285,6 +4124,12 @@ bool NodeMetaInfo::isQtQuick3DParticles3DAffector3D() const
bool NodeMetaInfo::isQtQuick3DView3D() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.View3D"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, View3D>(m_projectStorage, m_typeId);
} else {
@@ -3295,6 +4140,14 @@ bool NodeMetaInfo::isQtQuick3DView3D() const
bool NodeMetaInfo::isQtQuick3DPrincipledMaterial() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.PrincipledMaterial"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, PrincipledMaterial>(m_projectStorage, m_typeId);
} else {
@@ -3305,6 +4158,14 @@ bool NodeMetaInfo::isQtQuick3DPrincipledMaterial() const
bool NodeMetaInfo::isQtQuick3DSpecularGlossyMaterial() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.SpecularGlossyMaterial"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, SpecularGlossyMaterial>(m_projectStorage, m_typeId);
} else {
@@ -3315,6 +4176,14 @@ bool NodeMetaInfo::isQtQuick3DSpecularGlossyMaterial() const
bool NodeMetaInfo::isQtQuick3DParticles3DSpriteParticle3D() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.SpriteParticle3D"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D_Particles3D, SpriteParticle3D>(m_projectStorage,
m_typeId);
@@ -3326,6 +4195,14 @@ bool NodeMetaInfo::isQtQuick3DParticles3DSpriteParticle3D() const
bool NodeMetaInfo::isQtQuick3DTextureInput() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.TextureInput"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, TextureInput>(m_projectStorage, m_typeId);
} else {
@@ -3336,6 +4213,14 @@ bool NodeMetaInfo::isQtQuick3DTextureInput() const
bool NodeMetaInfo::isQtQuick3DCubeMapTexture() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.CubeMapTexture"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, CubeMapTexture>(m_projectStorage, m_typeId);
} else {
@@ -3348,6 +4233,14 @@ bool NodeMetaInfo::isQtQuick3DCubeMapTexture() const
bool NodeMetaInfo::isQtQuick3DSceneEnvironment() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.SceneEnvironment"_t,
+ category(),
+ keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, SceneEnvironment>(m_projectStorage, m_typeId);
} else {
@@ -3358,6 +4251,12 @@ bool NodeMetaInfo::isQtQuick3DSceneEnvironment() const
bool NodeMetaInfo::isQtQuick3DEffect() const
{
if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is QtQuick3D.Effect"_t, category(), keyValue("type id", m_typeId)};
+
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, Effect>(m_projectStorage, m_typeId);
} else {
@@ -3367,8 +4266,15 @@ bool NodeMetaInfo::isQtQuick3DEffect() const
bool NodeMetaInfo::isEnumeration() const
{
- if constexpr (useProjectStorage())
- return isValid() && typeData().traits.isEnum;
+ if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return false;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is enumeration"_t, category(), keyValue("type id", m_typeId)};
+
+ return typeData().traits.isEnum;
+ }
return false;
}
@@ -3393,8 +4299,15 @@ PropertyMetaInfo::~PropertyMetaInfo() = default;
NodeMetaInfo PropertyMetaInfo::propertyType() const
{
if constexpr (useProjectStorage()) {
- if (isValid())
- return {propertyData().propertyTypeId, m_projectStorage};
+ if (!isValid())
+ return {};
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get property type"_t,
+ category(),
+ keyValue("property declaration id", m_id)};
+
+ return {propertyData().propertyTypeId, m_projectStorage};
} else {
if (isValid())
return NodeMetaInfo{nodeMetaInfoPrivateData()->model(),
@@ -3409,8 +4322,15 @@ NodeMetaInfo PropertyMetaInfo::propertyType() const
NodeMetaInfo PropertyMetaInfo::type() const
{
if constexpr (useProjectStorage()) {
- if (isValid())
- return NodeMetaInfo(propertyData().typeId, m_projectStorage);
+ if (!isValid())
+ return {};
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get property owner type "_t,
+ category(),
+ keyValue("property declaration id", m_id)};
+
+ return NodeMetaInfo(propertyData().typeId, m_projectStorage);
}
return {};
@@ -3418,59 +4338,121 @@ NodeMetaInfo PropertyMetaInfo::type() const
PropertyName PropertyMetaInfo::name() const
{
- if (isValid()) {
- if constexpr (useProjectStorage())
- return PropertyName(Utils::SmallStringView(propertyData().name));
- else
- return propertyName();
- }
+ if (!isValid())
+ return {};
- return {};
+ if constexpr (useProjectStorage()) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get property name"_t,
+ category(),
+ keyValue("property declaration id", m_id)};
+
+ return PropertyName(Utils::SmallStringView(propertyData().name));
+ } else {
+ return propertyName();
+ }
}
bool PropertyMetaInfo::isWritable() const
{
- if constexpr (useProjectStorage())
- return isValid() && !(propertyData().traits & Storage::PropertyDeclarationTraits::IsReadOnly);
- else
+ if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return {};
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is property writable"_t,
+ category(),
+ keyValue("property declaration id", m_id)};
+
+ return !(propertyData().traits & Storage::PropertyDeclarationTraits::IsReadOnly);
+ } else {
return isValid() && nodeMetaInfoPrivateData()->isPropertyWritable(propertyName());
+ }
}
bool PropertyMetaInfo::isReadOnly() const
{
- return !isWritable();
+ if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return {};
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is property read only"_t,
+ category(),
+ keyValue("property declaration id", m_id)};
+
+ return propertyData().traits & Storage::PropertyDeclarationTraits::IsReadOnly;
+ } else {
+ return !isWritable();
+ }
}
bool PropertyMetaInfo::isListProperty() const
{
- if constexpr (useProjectStorage())
- return isValid() && propertyData().traits & Storage::PropertyDeclarationTraits::IsList;
- else
+ if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return {};
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is list property"_t,
+ category(),
+ keyValue("property declaration id", m_id)};
+
+ return propertyData().traits & Storage::PropertyDeclarationTraits::IsList;
+ } else {
return isValid() && nodeMetaInfoPrivateData()->isPropertyList(propertyName());
+ }
}
bool PropertyMetaInfo::isEnumType() const
{
- if constexpr (useProjectStorage())
+ if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return {};
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is enum type"_t,
+ category(),
+ keyValue("property has enumeration type", m_id)};
+
return propertyType().isEnumeration();
- else
+ } else {
return isValid() && nodeMetaInfoPrivateData()->isPropertyEnum(propertyName());
+ }
}
bool PropertyMetaInfo::isPrivate() const
{
- if constexpr (useProjectStorage())
+ if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return {};
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is private property"_t,
+ category(),
+ keyValue("property declaration id", m_id)};
+
return isValid() && propertyData().name.startsWith("__");
- else
+ } else {
return isValid() && propertyName().startsWith("__");
+ }
}
bool PropertyMetaInfo::isPointer() const
{
- if constexpr (useProjectStorage())
+ if constexpr (useProjectStorage()) {
+ if (!isValid())
+ return {};
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is pointer property"_t,
+ category(),
+ keyValue("property declaration id", m_id)};
+
return isValid() && (propertyData().traits & Storage::PropertyDeclarationTraits::IsPointer);
- else
+ } else {
return isValid() && nodeMetaInfoPrivateData()->isPropertyPointer(propertyName());
+ }
}
namespace {
@@ -3487,6 +4469,11 @@ QVariant PropertyMetaInfo::castedValue(const QVariant &value) const
return {};
if constexpr (!useProjectStorage()) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"cast value"_t,
+ category(),
+ keyValue("property declaration id", m_id)};
+
const QVariant variant = value;
QVariant copyVariant = variant;
const TypeName &typeName = propertyTypeName();
@@ -3504,7 +4491,7 @@ QVariant PropertyMetaInfo::castedValue(const QVariant &value) const
return variant;
} else if (typeId == QVariant::UserType && typeName == "var") {
return variant;
- } else if (variant.typeId() == QVariant::List) {
+ } else if (variant.typeId() == QMetaType::QVariantList) {
// TODO: check the contents of the list
return variant;
} else if (typeName == "var" || typeName == "variant") {
diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp
index 16d9217f6a..29a093f199 100644
--- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp
+++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp
@@ -345,11 +345,8 @@ void SubComponentManager::unregisterQmlFile(const QFileInfo &fileInfo, const QSt
void SubComponentManager::registerQmlFile(const QFileInfo &fileInfo, const QString &qualifier,
bool addToLibrary)
{
- if (!addToLibrary || !model()
- || m_componentUtils.isImport3dPath(fileInfo.path())
- || m_componentUtils.isComposedEffectPath(fileInfo.path())) {
+ if (!addToLibrary || !model() || m_componentUtils.isGeneratedPath(fileInfo.path()))
return;
- }
QString componentName = fileInfo.baseName();
const QString baseComponentName = componentName;
diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp
index 4f0bfba1ce..d4eea26378 100644
--- a/src/plugins/qmldesigner/designercore/model/model.cpp
+++ b/src/plugins/qmldesigner/designercore/model/model.cpp
@@ -9,7 +9,6 @@
#include "../projectstorage/sourcepath.h"
#include "../projectstorage/sourcepathcache.h"
#include "abstractview.h"
-#include "auxiliarydataproperties.h"
#include "internalbindingproperty.h"
#include "internalnodeabstractproperty.h"
#include "internalnodelistproperty.h"
@@ -33,6 +32,8 @@
#include "signalhandlerproperty.h"
#include "variantproperty.h"
+#include <uniquename.h>
+
#include <projectstorage/projectstorage.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
@@ -46,8 +47,6 @@
#include <QRegularExpression>
#include <qcompilerdetection.h>
-#include <string>
-
/*!
\defgroup CoreModel
*/
@@ -170,10 +169,10 @@ Storage::Imports createStorageImports(const Imports &imports,
SourceId fileId)
{
return Utils::transform<Storage::Imports>(imports, [&](const Import &import) {
- return Storage::Import{projectStorage.moduleId(Utils::SmallString{import.url()}),
- import.majorVersion(),
- import.minorVersion(),
- fileId};
+ using Storage::ModuleKind;
+ auto moduleKind = import.isLibraryImport() ? ModuleKind::QmlLibrary : ModuleKind::PathLibrary;
+ auto moduleId = projectStorage.moduleId(Utils::SmallString{import.url()}, moduleKind);
+ return Storage::Import{moduleId, import.majorVersion(), import.minorVersion(), fileId};
});
}
@@ -244,7 +243,7 @@ void ModelPrivate::notifyUsedImportsChanged(const Imports &usedImports)
}
}
-QUrl ModelPrivate::fileUrl() const
+const QUrl &ModelPrivate::fileUrl() const
{
return m_fileUrl;
}
@@ -390,7 +389,11 @@ ImportedTypeNameId ModelPrivate::importedTypeNameId(Utils::SmallStringView typeN
return import.alias() == aliasName;
});
if (found != m_imports.end()) {
- ModuleId moduleId = projectStorage->moduleId(Utils::PathString{found->url()});
+ using Storage::ModuleKind;
+ auto moduleKind = found->isLibraryImport() ? ModuleKind::QmlLibrary
+ : ModuleKind::PathLibrary;
+ ModuleId moduleId = projectStorage->moduleId(Utils::PathString{found->url()},
+ moduleKind);
ImportId importId = projectStorage->importId(
Storage::Import{moduleId, found->majorVersion(), found->minorVersion(), m_sourceId});
return projectStorage->importedTypeNameId(importId, shortTypeName);
@@ -1860,90 +1863,18 @@ bool Model::hasImport(const QString &importUrl) const
});
}
-static QString firstCharToLower(const QString &string)
-{
- QString resultString = string;
-
- if (!resultString.isEmpty())
- resultString[0] = resultString.at(0).toLower();
-
- return resultString;
-}
-
-QString Model::generateNewId(const QString &prefixName,
- const QString &fallbackPrefix,
- std::optional<std::function<bool(const QString &)>> isDuplicate) const
+QString Model::generateNewId(const QString &prefixName, const QString &fallbackPrefix) const
{
- // First try just the prefixName without number as postfix, then continue with 2 and further
- // as postfix until id does not already exist.
- // Properties of the root node are not allowed for ids, because they are available in the
- // complete context without qualification.
-
- int counter = 0;
-
- static const QRegularExpression nonWordCharsRegex("\\W");
- QString newBaseId = firstCharToLower(prefixName);
- newBaseId.remove(nonWordCharsRegex);
-
- if (!newBaseId.isEmpty()) {
- QChar firstChar = newBaseId.at(0);
- if (firstChar.isDigit())
- newBaseId.prepend('_');
- } else {
- newBaseId = fallbackPrefix;
- }
-
- QString newId = newBaseId;
+ QString newId = prefixName;
- if (!isDuplicate.has_value())
- isDuplicate = std::bind(&Model::hasId, this, std::placeholders::_1);
+ if (newId.isEmpty())
+ newId = fallbackPrefix;
- while (!ModelNode::isValidId(newId) || isDuplicate.value()(newId)
- || d->rootNode()->property(newId.toUtf8())) {
- ++counter;
- newId = QStringView(u"%1%2").arg(firstCharToLower(newBaseId)).arg(counter);
- }
-
- return newId;
-}
-
-// Generate a unique camelCase id from a name
-// note: this methods does the same as generateNewId(). The 2 methods should be merged into one
-QString Model::generateIdFromName(const QString &name, const QString &fallbackId) const
-{
- QString newId;
- if (name.isEmpty()) {
- newId = fallbackId;
- } else {
- // convert to camel case
- QStringList nameWords = name.split(" ");
- nameWords[0] = nameWords[0].at(0).toLower() + nameWords[0].mid(1);
- for (int i = 1; i < nameWords.size(); ++i)
- nameWords[i] = nameWords[i].at(0).toUpper() + nameWords[i].mid(1);
- newId = nameWords.join("");
-
- // if id starts with a number prepend an underscore
- if (newId.at(0).isDigit())
- newId.prepend('_');
- }
-
- // If the new id is not valid (e.g. qml keyword match), try fixing it by prepending underscore
- if (!ModelNode::isValidId(newId))
- newId.prepend("_");
-
- QRegularExpression rgx("\\d+$"); // matches a number at the end of a string
- while (hasId(newId)) { // id exists
- QRegularExpressionMatch match = rgx.match(newId);
- if (match.hasMatch()) { // ends with a number, increment it
- QString numStr = match.captured();
- int num = numStr.toInt() + 1;
- newId = newId.mid(0, match.capturedStart()) + QString::number(num);
- } else {
- newId.append('1');
- }
- }
-
- return newId;
+ return UniqueName::generateId(prefixName, [&] (const QString &id) {
+ // Properties of the root node are not allowed for ids, because they are available in the
+ // complete context without qualification.
+ return hasId(id) || d->rootNode()->property(id.toUtf8());
+ });
}
void Model::startDrag(QMimeData *mimeData, const QPixmap &icon)
@@ -2115,7 +2046,7 @@ void Model::clearMetaInfoCache()
\brief Returns the URL against which relative URLs within the model should be resolved.
\return The base URL.
*/
-QUrl Model::fileUrl() const
+const QUrl &Model::fileUrl() const
{
return d->fileUrl();
}
@@ -2556,8 +2487,7 @@ QList<ItemLibraryEntry> Model::itemLibraryEntries() const
{
#ifdef QDS_USE_PROJECTSTORAGE
using namespace Storage::Info;
- return toItemLibraryEntries(d->projectStorage->itemLibraryEntries(d->m_sourceId),
- *d->projectStorage);
+ return toItemLibraryEntries(d->projectStorage->itemLibraryEntries(d->m_sourceId));
#else
return d->metaInfo().itemLibraryInfo()->entries();
#endif
@@ -2623,11 +2553,10 @@ MetaInfo Model::metaInfo()
}
#endif
-Module Model::module(Utils::SmallStringView moduleName)
+Module Model::module(Utils::SmallStringView moduleName, Storage::ModuleKind moduleKind)
{
- if constexpr (useProjectStorage()) {
- return Module(d->projectStorage->moduleId(moduleName), d->projectStorage);
- }
+ if constexpr (useProjectStorage())
+ return Module(d->projectStorage->moduleId(moduleName, moduleKind), d->projectStorage);
return {};
}
diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h
index a3e972f329..cb082fd1d7 100644
--- a/src/plugins/qmldesigner/designercore/model/model_p.h
+++ b/src/plugins/qmldesigner/designercore/model/model_p.h
@@ -122,7 +122,7 @@ public:
ModelPrivate(const ModelPrivate &) = delete;
ModelPrivate &operator=(const ModelPrivate &) = delete;
- QUrl fileUrl() const;
+ const QUrl &fileUrl() const;
void setFileUrl(const QUrl &url);
InternalNodePointer createNode(const TypeName &typeName,
diff --git a/src/plugins/qmldesigner/designercore/model/propertycontainer.cpp b/src/plugins/qmldesigner/designercore/model/propertycontainer.cpp
index cd7cebbb73..8eaa7947de 100644
--- a/src/plugins/qmldesigner/designercore/model/propertycontainer.cpp
+++ b/src/plugins/qmldesigner/designercore/model/propertycontainer.cpp
@@ -41,7 +41,7 @@ PropertyName PropertyContainer::name() const
QVariant PropertyContainer::value() const
{
- if (m_value.typeId() == QVariant::String)
+ if (m_value.typeId() == QMetaType::QString)
m_value = PropertyParser::read(m_type, m_value.toString());
return m_value;
}
diff --git a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp
index c84f234257..726d3d52af 100644
--- a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp
@@ -24,6 +24,8 @@
#include <QDir>
#include <QRandomGenerator>
+#include <memory>
+
namespace QmlDesigner {
static char imagePlaceHolder[] = "qrc:/qtquickplugin/images/template_image.png";
@@ -182,8 +184,8 @@ void QmlVisualNode::scatter(const ModelNode &targetNode, const std::optional<int
if (!scatter)
return;
- if (offset.has_value()) { // offset
- double offsetValue = offset.value();
+ if (offset) { // offset
+ double offsetValue = *offset;
this->translate(QVector3D(offsetValue, offsetValue, offsetValue));
} else { // scatter in range
const double scatterRange = 20.;
@@ -250,8 +252,7 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view,
NodeAbstractProperty parentProperty = parentQmlItemNode.defaultNodeAbstractProperty();
-
- NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry);
+ NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry, view->model());
const PropertyName forceNonDefaultProperty = hints.forceNonDefaultProperty().toUtf8();
QmlObjectNode newNode = QmlItemNode::createQmlObjectNode(view,
@@ -289,17 +290,17 @@ static QmlObjectNode createQmlObjectNodeFromSource(AbstractView *view,
textEdit.setPlainText(source);
NotIndentingTextEditModifier modifier(&textEdit);
- QScopedPointer<RewriterView> rewriterView(
- new RewriterView(view->externalDependencies(), RewriterView::Amend));
+ std::unique_ptr<RewriterView> rewriterView = std::make_unique<RewriterView>(
+ view->externalDependencies(), RewriterView::Amend);
rewriterView->setCheckSemanticErrors(false);
rewriterView->setTextModifier(&modifier);
rewriterView->setAllowComponentRoot(true);
rewriterView->setPossibleImportsEnabled(false);
- inputModel->setRewriterView(rewriterView.data());
+ inputModel->setRewriterView(rewriterView.get());
if (rewriterView->errors().isEmpty() && rewriterView->rootModelNode().isValid()) {
ModelNode rootModelNode = rewriterView->rootModelNode();
- inputModel->detachView(rewriterView.data());
+ inputModel->detachView(rewriterView.get());
QmlVisualNode(rootModelNode).setPosition(position);
ModelMerger merger(view);
return merger.insertModel(rootModelNode);
@@ -329,7 +330,7 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view,
{
QmlObjectNode newQmlObjectNode;
- NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry);
+ NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry, view->model());
auto createNodeFunc = [=, &newQmlObjectNode, &parentProperty]() {
#ifndef QDS_USE_PROJECTSTORAGE
@@ -361,13 +362,17 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view,
propertyPairList.append(position.propertyPairList());
ModelNode::NodeSourceType nodeSourceType = ModelNode::NodeWithoutSource;
- if (itemLibraryEntry.typeName() == "QtQml.Component")
- nodeSourceType = ModelNode::NodeWithComponentSource;
#ifdef QDS_USE_PROJECTSTORAGE
+ NodeMetaInfo metaInfo{itemLibraryEntry.typeId(), view->model()->projectStorage()};
+ if (metaInfo.isQmlComponent())
+ nodeSourceType = ModelNode::NodeWithComponentSource;
newQmlObjectNode = QmlObjectNode(view->createModelNode(
itemLibraryEntry.typeName(), propertyPairList, {}, {}, nodeSourceType));
#else
+ if (itemLibraryEntry.typeName() == "QtQml.Component")
+ nodeSourceType = ModelNode::NodeWithComponentSource;
+
newQmlObjectNode = QmlObjectNode(view->createModelNode(itemLibraryEntry.typeName(),
majorVersion,
minorVersion,
diff --git a/src/plugins/qmldesigner/designercore/model/rewriteactioncompressor.cpp b/src/plugins/qmldesigner/designercore/model/rewriteactioncompressor.cpp
index c4d96bb250..edee6840ef 100644
--- a/src/plugins/qmldesigner/designercore/model/rewriteactioncompressor.cpp
+++ b/src/plugins/qmldesigner/designercore/model/rewriteactioncompressor.cpp
@@ -58,7 +58,6 @@ void RewriteActionCompressor::compressImports(QList<RewriteAction *> &actions) c
actionsToRemove.append(action);
actionsToRemove.append(addImportAction);
addedImports.remove(import);
- delete addImportAction;
} else {
removedImports.insert(import, action);
}
@@ -67,13 +66,11 @@ void RewriteActionCompressor::compressImports(QList<RewriteAction *> &actions) c
if (RewriteAction *duplicateAction = addedImports.value(import, 0)) {
actionsToRemove.append(duplicateAction);
addedImports.remove(import);
- delete duplicateAction;
addedImports.insert(import, action);
} else if (RewriteAction *removeAction = removedImports.value(import, 0)) {
actionsToRemove.append(action);
actionsToRemove.append(removeAction);
removedImports.remove(import);
- delete removeAction;
} else {
addedImports.insert(import, action);
}
diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp
index 17d40daca3..3c481573d2 100644
--- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp
+++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp
@@ -16,12 +16,14 @@
#include <filemanager/objectlengthcalculator.h>
#include <modelnode.h>
#include <modelnodepositionstorage.h>
+#include <nodemetainfo.h>
#include <nodeproperty.h>
+#include <projectstorage/projectstorage.h>
+#include <qmlobjectnode.h>
+#include <qmltimelinekeyframegroup.h>
#include <rewritingexception.h>
#include <signalhandlerproperty.h>
#include <variantproperty.h>
-#include <qmlobjectnode.h>
-#include <qmltimelinekeyframegroup.h>
#include <qmljs/parser/qmljsengine_p.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
@@ -56,9 +58,9 @@ RewriterView::RewriterView(ExternalDependenciesInterface &externalDependencies,
DifferenceHandling differenceHandling)
: AbstractView{externalDependencies}
, m_differenceHandling(differenceHandling)
- , m_positionStorage(new ModelNodePositionStorage)
- , m_modelToTextMerger(new Internal::ModelToTextMerger(this))
- , m_textToModelMerger(new Internal::TextToModelMerger(this))
+ , m_positionStorage(std::make_unique<ModelNodePositionStorage>())
+ , m_modelToTextMerger(std::make_unique<Internal::ModelToTextMerger>(this))
+ , m_textToModelMerger(std::make_unique<Internal::TextToModelMerger>(this))
{
m_amendTimer.setSingleShot(true);
@@ -78,12 +80,12 @@ RewriterView::~RewriterView() = default;
Internal::ModelToTextMerger *RewriterView::modelToTextMerger() const
{
- return m_modelToTextMerger.data();
+ return m_modelToTextMerger.get();
}
Internal::TextToModelMerger *RewriterView::textToModelMerger() const
{
- return m_textToModelMerger.data();
+ return m_textToModelMerger.get();
}
void RewriterView::modelAttached(Model *model)
@@ -92,7 +94,7 @@ void RewriterView::modelAttached(Model *model)
AbstractView::modelAttached(model);
- ModelAmender differenceHandler(m_textToModelMerger.data());
+ ModelAmender differenceHandler(m_textToModelMerger.get());
const QString qmlSource = m_textModifier->text();
if (m_textToModelMerger->load(qmlSource, differenceHandler))
m_lastCorrectQmlSource = qmlSource;
@@ -104,6 +106,7 @@ void RewriterView::modelAttached(Model *model)
m_modelAttachPending = true;
QTimer::singleShot(1000, this, [this, model](){
modelAttached(model);
+ restoreAuxiliaryData();
});
}
}
@@ -492,7 +495,7 @@ void RewriterView::amendQmlText()
const QString newQmlText = m_textModifier->text();
- ModelAmender differenceHandler(m_textToModelMerger.data());
+ ModelAmender differenceHandler(m_textToModelMerger.get());
if (m_textToModelMerger->load(newQmlText, differenceHandler))
m_lastCorrectQmlSource = newQmlText;
emitCustomNotification(EndRewriterAmend);
@@ -593,7 +596,7 @@ QString RewriterView::auxiliaryDataAsQML() const
hasAuxData = true;
QString strValue = value.toString();
- auto metaType = static_cast<QMetaType::Type>(value.type());
+ const int metaType = value.typeId();
if (metaType == QMetaType::QString
|| metaType == QMetaType::QColor) {
@@ -698,7 +701,7 @@ void RewriterView::forceAmend()
Internal::ModelNodePositionStorage *RewriterView::positionStorage() const
{
- return m_positionStorage.data();
+ return m_positionStorage.get();
}
QList<DocumentMessage> RewriterView::warnings() const
@@ -755,7 +758,7 @@ void RewriterView::resetToLastCorrectQml()
{
m_textModifier->textDocument()->undo();
m_textModifier->textDocument()->clearUndoRedoStacks(QTextDocument::RedoStack);
- ModelAmender differenceHandler(m_textToModelMerger.data());
+ ModelAmender differenceHandler(m_textToModelMerger.get());
Internal::WriteLocker::unlock(model());
m_textToModelMerger->load(m_textModifier->text(), differenceHandler);
Internal::WriteLocker::lock(model());
@@ -1004,6 +1007,61 @@ QSet<QPair<QString, QString> > RewriterView::qrcMapping() const
return m_textToModelMerger->qrcMapping();
}
+namespace {
+#ifdef QDS_USE_PROJECTSTORAGE
+
+ModuleIds generateModuleIds(const ModelNodes &nodes)
+{
+ ModuleIds moduleIds;
+ moduleIds.reserve(Utils::usize(nodes));
+ for (const auto &node : nodes) {
+ auto exportedNames = node.metaInfo().allExportedTypeNames();
+ if (exportedNames.size())
+ moduleIds.push_back(exportedNames.front().moduleId);
+ }
+
+ std::sort(moduleIds.begin(), moduleIds.end());
+ moduleIds.erase(std::unique(moduleIds.begin(), moduleIds.end()), moduleIds.end());
+
+ return moduleIds;
+}
+
+QStringList generateImports(ModuleIds moduleIds, const ProjectStorageType &projectStorage)
+{
+ QStringList imports;
+ imports.reserve(std::ssize(moduleIds));
+
+ for (auto moduleId : moduleIds) {
+ using Storage::ModuleKind;
+ auto module = projectStorage.module(moduleId);
+ switch (module.kind) {
+ case ModuleKind::QmlLibrary:
+ imports.push_back("import " + module.name.toQString());
+ break;
+ case ModuleKind::PathLibrary:
+ imports.push_back("import \"" + module.name.toQString() + "\"");
+ break;
+ case ModuleKind::CppLibrary:
+ break;
+ }
+ }
+
+ return imports;
+}
+
+QStringList generateImports(const ModelNodes &nodes)
+{
+ if (nodes.empty())
+ return {};
+
+ auto moduleIds = generateModuleIds(nodes);
+
+ return generateImports(moduleIds, *nodes.front().model()->projectStorage());
+}
+
+#endif
+} // namespace
+
void RewriterView::moveToComponent(const ModelNode &modelNode)
{
if (!modelNode.isValid())
@@ -1012,20 +1070,26 @@ void RewriterView::moveToComponent(const ModelNode &modelNode)
int offset = nodeOffset(modelNode);
const QList<ModelNode> nodes = modelNode.allSubModelNodesAndThisNode();
- QSet<QString> directPaths;
+#ifdef QDS_USE_PROJECTSTORAGE
+ auto directPaths = generateImports(nodes);
+#else
+ QSet<QString> directPathsSet;
// Always add QtQuick import
QString quickImport = model()->qtQuickItemMetaInfo().requiredImportString();
if (!quickImport.isEmpty())
- directPaths.insert(quickImport);
+ directPathsSet.insert(quickImport);
for (const ModelNode &partialNode : nodes) {
QString importStr = partialNode.metaInfo().requiredImportString();
if (importStr.size())
- directPaths << importStr;
+ directPathsSet << importStr;
}
- QString importData = Utils::sorted(directPaths.values()).join(QChar::LineFeed);
+ auto directPaths = directPathsSet.values();
+#endif
+
+ QString importData = Utils::sorted(directPaths).join(QChar::LineFeed);
if (importData.size())
importData.append(QString(2, QChar::LineFeed));
@@ -1091,7 +1155,7 @@ void RewriterView::qmlTextChanged()
switch (m_differenceHandling) {
case Validate: {
- ModelValidator differenceHandler(m_textToModelMerger.data());
+ ModelValidator differenceHandler(m_textToModelMerger.get());
if (m_textToModelMerger->load(newQmlText, differenceHandler))
m_lastCorrectQmlSource = newQmlText;
break;
diff --git a/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp b/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp
index 16877b61db..4f744d54e6 100644
--- a/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp
+++ b/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp
@@ -23,6 +23,8 @@
#include <QQueue>
#include <QRegularExpression>
+#include <memory>
+
namespace {
QPoint pointForModelNode(const QmlDesigner::ModelNode &node)
@@ -641,10 +643,10 @@ void StylesheetMerger::styleMerge(const QString &qmlTemplateString,
textEditTemplate.setPlainText(imports + qmlTemplateString);
NotIndentingTextEditModifier textModifierTemplate(&textEditTemplate);
- QScopedPointer<RewriterView> templateRewriterView(
- new RewriterView(externalDependencies, RewriterView::Amend));
+ std::unique_ptr<RewriterView> templateRewriterView = std::make_unique<RewriterView>(
+ externalDependencies, RewriterView::Amend);
templateRewriterView->setTextModifier(&textModifierTemplate);
- templateModel->attachView(templateRewriterView.data());
+ templateModel->attachView(templateRewriterView.get());
templateRewriterView->setCheckSemanticErrors(false);
templateRewriterView->setPossibleImportsEnabled(false);
@@ -665,12 +667,12 @@ void StylesheetMerger::styleMerge(const QString &qmlTemplateString,
textEditStyle.setPlainText(parentRewriterView->textModifierContent());
NotIndentingTextEditModifier textModifierStyle(&textEditStyle);
- QScopedPointer<RewriterView> styleRewriterView(
- new RewriterView(externalDependencies, RewriterView::Amend));
+ std::unique_ptr<RewriterView> styleRewriterView = std::make_unique<RewriterView>(
+ externalDependencies, RewriterView::Amend);
styleRewriterView->setTextModifier(&textModifierStyle);
- styleModel->attachView(styleRewriterView.data());
+ styleModel->attachView(styleRewriterView.get());
- StylesheetMerger merger(templateRewriterView.data(), styleRewriterView.data());
+ StylesheetMerger merger(templateRewriterView.get(), styleRewriterView.get());
try {
merger.merge();
diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
index 1c1aba5feb..b66270dca1 100644
--- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
+++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
@@ -94,21 +94,23 @@ bool isGlobalQtEnums(QStringView value)
bool isKnownEnumScopes(QStringView value)
{
- static constexpr auto list = Utils::to_array<std::u16string_view>({u"TextInput",
- u"TextEdit",
- u"Material",
- u"Universal",
- u"Font",
- u"Shape",
- u"ShapePath",
- u"AbstractButton",
- u"Text",
- u"ShaderEffectSource",
- u"Grid",
- u"ItemLayer",
- u"ImageLayer",
- u"SpriteLayer",
- u"Light"});
+ static constexpr auto list = Utils::to_array<std::u16string_view>(
+ {u"TextInput",
+ u"TextEdit",
+ u"Material",
+ u"Universal",
+ u"Font",
+ u"Shape",
+ u"ShapePath",
+ u"AbstractButton",
+ u"Text",
+ u"ShaderEffectSource",
+ u"Grid",
+ u"ItemLayer",
+ u"ImageLayer",
+ u"SpriteLayer",
+ u"Light",
+ u"ExtendedSceneEnvironment.GlowBlendMode"});
return std::find(std::begin(list), std::end(list), QmlDesigner::ModelUtils::toStdStringView(value))
!= std::end(list);
@@ -559,6 +561,11 @@ public:
//Check for known enum scopes used globally
if (isKnownEnumScopes(astValueList.constFirst()))
return QVariant::fromValue(Enumeration(astValue));
+ } else if (astValueList.size() == 3) {
+ QString enumName = astValueList.constFirst() + '.' + astValueList.at(1);
+ if (isKnownEnumScopes(enumName))
+ return QVariant::fromValue(
+ Enumeration(enumName.toUtf8(), astValueList.constLast().toUtf8()));
}
auto eStmt = AST::cast<AST::ExpressionStatement *>(rhs);
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h
index 35658c005f..76305b1fbe 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h
@@ -84,7 +84,6 @@ inline constexpr char PrincipledMaterial[] = "PrincipledMaterial";
inline constexpr char PropertyAnimation[] = "PropertyAnimation";
inline constexpr char PropertyChanges[] = "PropertyChanges";
inline constexpr char QML[] = "QML";
-inline constexpr char QML_cppnative[] = "QML-cppnative";
inline constexpr char QQuick3DParticleAbstractShape[] = "QQuick3DParticleAbstractShape";
inline constexpr char QQuickStateOperation[] = "QQuickStateOperation";
inline constexpr char QtMultimedia[] = "QtMultimedia";
@@ -94,7 +93,6 @@ inline constexpr char QtQml_Models[] = "QtQml.Models";
inline constexpr char QtQml_XmlListModel[] = "QtQml.XmlListModel";
inline constexpr char QtQuick3D[] = "QtQuick3D";
inline constexpr char QtQuick3D_Particles3D[] = "QtQuick3D.Particles3D";
-inline constexpr char QtQuick3D_Particles3D_cppnative[] = "QtQuick3D.Particles3D-cppnative";
inline constexpr char QtQuick[] = "QtQuick";
inline constexpr char QtQuick_Controls[] = "QtQuick.Controls";
inline constexpr char QtQuick_Dialogs[] = "QtQuick.Dialogs";
@@ -104,7 +102,6 @@ inline constexpr char QtQuick_Studio_Components[] = "QtQuick.Studio.Components";
inline constexpr char QtQuick_Templates[] = "QtQuick.Templates";
inline constexpr char QtQuick_Timeline[] = "QtQuick.Timeline";
inline constexpr char QtQuick_Window[] = "QtQuick.Window";
-inline constexpr char QtQuick_cppnative[] = "QtQuick-cppnative";
inline constexpr char Qt_SafeRenderer[] = "Qt.SafeRenderer";
inline constexpr char Rectangle[] = "Rectangle";
inline constexpr char Repeater[] = "Repeater";
@@ -149,7 +146,7 @@ struct BaseCacheType
QmlDesigner::TypeId typeId;
};
-template<const char *moduleName_, const char *typeName_>
+template<const char *moduleName_, ModuleKind moduleKind, const char *typeName_>
struct CacheType : public BaseCacheType
{
};
@@ -157,106 +154,107 @@ struct CacheType : public BaseCacheType
template<typename ProjectStorage>
class CommonTypeCache
{
- using CommonTypes = std::tuple<CacheType<FlowView, FlowActionArea>,
- CacheType<FlowView, FlowDecision>,
- CacheType<FlowView, FlowItem>,
- CacheType<FlowView, FlowTransition>,
- CacheType<FlowView, FlowView>,
- CacheType<FlowView, FlowWildcard>,
- CacheType<QML, BoolType>,
- CacheType<QML, Component>,
- CacheType<QML, DoubleType>,
- CacheType<QML, IntType>,
- CacheType<QML, QtObject>,
- CacheType<QML, date>,
- CacheType<QML, string>,
- CacheType<QML, url>,
- CacheType<QML, var>,
- CacheType<QML_cppnative, FloatType>,
- CacheType<QML_cppnative, UIntType>,
- CacheType<QtQml, Connections>,
- CacheType<QtMultimedia, SoundEffect>,
- CacheType<QtQml_Models, ListElement>,
- CacheType<QtQml_Models, ListModel>,
- CacheType<QtQml_XmlListModel, XmlListModelRole>,
- CacheType<QtQuick, BorderImage>,
- CacheType<QtQuick, GridView>,
- CacheType<QtQuick, Image>,
- CacheType<QtQuick, Item>,
- CacheType<QtQuick, ListView>,
- CacheType<QtQuick, Loader>,
- CacheType<QtQuick, MouseArea>,
- CacheType<QtQuick, Path>,
- CacheType<QtQuick, PathView>,
- CacheType<QtQuick, PauseAnimation>,
- CacheType<QtQuick, Positioner>,
- CacheType<QtQuick, PropertyAnimation>,
- CacheType<QtQuick, PropertyChanges>,
- CacheType<QtQuick, Rectangle>,
- CacheType<QtQuick, Repeater>,
- CacheType<QtQuick, State>,
- CacheType<QtQuick, StateGroup>,
- CacheType<QtQuick, Text>,
- CacheType<QtQuick, TextEdit>,
- CacheType<QtQuick, Transition>,
- CacheType<QtQuick, color>,
- CacheType<QtQuick, font>,
- CacheType<QtQuick, vector2d>,
- CacheType<QtQuick, vector3d>,
- CacheType<QtQuick, vector4d>,
- CacheType<QtQuick3D, BakedLightmap>,
- CacheType<QtQuick3D, Buffer>,
- CacheType<QtQuick3D, Camera>,
- CacheType<QtQuick3D, Command>,
- CacheType<QtQuick3D, CubeMapTexture>,
- CacheType<QtQuick3D, DefaultMaterial>,
- CacheType<QtQuick3D, DirectionalLight>,
- CacheType<QtQuick3D, Effect>,
- CacheType<QtQuick3D, InstanceList>,
- CacheType<QtQuick3D, InstanceListEntry>,
- CacheType<QtQuick3D, Light>,
- CacheType<QtQuick3D, Material>,
- CacheType<QtQuick3D, Model>,
- CacheType<QtQuick3D, Node>,
- CacheType<QtQuick3D, OrthographicCamera>,
- CacheType<QtQuick3D, Pass>,
- CacheType<QtQuick3D, PerspectiveCamera>,
- CacheType<QtQuick3D, PointLight>,
- CacheType<QtQuick3D, PrincipledMaterial>,
- CacheType<QtQuick3D, SceneEnvironment>,
- CacheType<QtQuick3D, Shader>,
- CacheType<QtQuick3D, SpecularGlossyMaterial>,
- CacheType<QtQuick3D, SpotLight>,
- CacheType<QtQuick3D, Texture>,
- CacheType<QtQuick3D, TextureInput>,
- CacheType<QtQuick3D, View3D>,
- CacheType<QtQuick3D_Particles3D, Affector3D>,
- CacheType<QtQuick3D_Particles3D, Attractor3D>,
- CacheType<QtQuick3D_Particles3D, Model>,
- CacheType<QtQuick3D_Particles3D, Particle3D>,
- CacheType<QtQuick3D_Particles3D, ParticleEmitter3D>,
- CacheType<QtQuick3D_Particles3D, SpriteParticle3D>,
- CacheType<QtQuick3D_Particles3D_cppnative, QQuick3DParticleAbstractShape>,
- CacheType<QtQuick_Controls, Control>,
- CacheType<QtQuick_Controls, Popup>,
- CacheType<QtQuick_Controls, SplitView>,
- CacheType<QtQuick_Controls, SwipeView>,
- CacheType<QtQuick_Controls, TabBar>,
- CacheType<QtQuick_Controls, TextArea>,
- CacheType<QtQuick_Dialogs, Dialog>,
- CacheType<QtQuick_Extras, Picture>,
- CacheType<QtQuick_Layouts, Layout>,
- CacheType<QtQuick_Studio_Components, GroupItem>,
- CacheType<QtQuick_Studio_Components, JsonListModel>,
- CacheType<QtQuick_Templates, Control>,
- CacheType<QtQuick_Timeline, Keyframe>,
- CacheType<QtQuick_Timeline, KeyframeGroup>,
- CacheType<QtQuick_Timeline, Timeline>,
- CacheType<QtQuick_Timeline, TimelineAnimation>,
- CacheType<QtQuick_cppnative, QQuickStateOperation>,
- CacheType<Qt_SafeRenderer, SafePicture>,
- CacheType<Qt_SafeRenderer, SafeRendererPicture>,
- CacheType<QtQuick_Window, Window>>;
+ using CommonTypes = std::tuple<
+ CacheType<FlowView, ModuleKind::QmlLibrary, FlowActionArea>,
+ CacheType<FlowView, ModuleKind::QmlLibrary, FlowDecision>,
+ CacheType<FlowView, ModuleKind::QmlLibrary, FlowItem>,
+ CacheType<FlowView, ModuleKind::QmlLibrary, FlowTransition>,
+ CacheType<FlowView, ModuleKind::QmlLibrary, FlowView>,
+ CacheType<FlowView, ModuleKind::QmlLibrary, FlowWildcard>,
+ CacheType<QML, ModuleKind::QmlLibrary, BoolType>,
+ CacheType<QML, ModuleKind::QmlLibrary, Component>,
+ CacheType<QML, ModuleKind::QmlLibrary, DoubleType>,
+ CacheType<QML, ModuleKind::QmlLibrary, IntType>,
+ CacheType<QML, ModuleKind::QmlLibrary, QtObject>,
+ CacheType<QML, ModuleKind::QmlLibrary, date>,
+ CacheType<QML, ModuleKind::QmlLibrary, string>,
+ CacheType<QML, ModuleKind::QmlLibrary, url>,
+ CacheType<QML, ModuleKind::QmlLibrary, var>,
+ CacheType<QML, ModuleKind::CppLibrary, FloatType>,
+ CacheType<QML, ModuleKind::CppLibrary, UIntType>,
+ CacheType<QtQml, ModuleKind::QmlLibrary, Connections>,
+ CacheType<QtMultimedia, ModuleKind::QmlLibrary, SoundEffect>,
+ CacheType<QtQml_Models, ModuleKind::QmlLibrary, ListElement>,
+ CacheType<QtQml_Models, ModuleKind::QmlLibrary, ListModel>,
+ CacheType<QtQml_XmlListModel, ModuleKind::QmlLibrary, XmlListModelRole>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, BorderImage>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, GridView>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, Image>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, Item>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, ListView>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, Loader>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, MouseArea>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, Path>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, PathView>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, PauseAnimation>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, Positioner>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, PropertyAnimation>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, PropertyChanges>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, Rectangle>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, Repeater>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, State>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, StateGroup>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, Text>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, TextEdit>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, Transition>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, color>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, font>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, vector2d>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, vector3d>,
+ CacheType<QtQuick, ModuleKind::QmlLibrary, vector4d>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, BakedLightmap>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, Buffer>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, Camera>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, Command>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, CubeMapTexture>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, DefaultMaterial>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, DirectionalLight>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, Effect>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, InstanceList>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, InstanceListEntry>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, Light>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, Material>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, Model>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, Node>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, OrthographicCamera>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, Pass>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, PerspectiveCamera>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, PointLight>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, PrincipledMaterial>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, SceneEnvironment>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, Shader>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, SpecularGlossyMaterial>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, SpotLight>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, Texture>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, TextureInput>,
+ CacheType<QtQuick3D, ModuleKind::QmlLibrary, View3D>,
+ CacheType<QtQuick3D_Particles3D, ModuleKind::QmlLibrary, Affector3D>,
+ CacheType<QtQuick3D_Particles3D, ModuleKind::QmlLibrary, Attractor3D>,
+ CacheType<QtQuick3D_Particles3D, ModuleKind::QmlLibrary, Model>,
+ CacheType<QtQuick3D_Particles3D, ModuleKind::QmlLibrary, Particle3D>,
+ CacheType<QtQuick3D_Particles3D, ModuleKind::QmlLibrary, ParticleEmitter3D>,
+ CacheType<QtQuick3D_Particles3D, ModuleKind::QmlLibrary, SpriteParticle3D>,
+ CacheType<QtQuick3D_Particles3D, ModuleKind::CppLibrary, QQuick3DParticleAbstractShape>,
+ CacheType<QtQuick_Controls, ModuleKind::QmlLibrary, Control>,
+ CacheType<QtQuick_Controls, ModuleKind::QmlLibrary, Popup>,
+ CacheType<QtQuick_Controls, ModuleKind::QmlLibrary, SplitView>,
+ CacheType<QtQuick_Controls, ModuleKind::QmlLibrary, SwipeView>,
+ CacheType<QtQuick_Controls, ModuleKind::QmlLibrary, TabBar>,
+ CacheType<QtQuick_Controls, ModuleKind::QmlLibrary, TextArea>,
+ CacheType<QtQuick_Dialogs, ModuleKind::QmlLibrary, Dialog>,
+ CacheType<QtQuick_Extras, ModuleKind::QmlLibrary, Picture>,
+ CacheType<QtQuick_Layouts, ModuleKind::QmlLibrary, Layout>,
+ CacheType<QtQuick_Studio_Components, ModuleKind::QmlLibrary, GroupItem>,
+ CacheType<QtQuick_Studio_Components, ModuleKind::QmlLibrary, JsonListModel>,
+ CacheType<QtQuick_Templates, ModuleKind::QmlLibrary, Control>,
+ CacheType<QtQuick_Timeline, ModuleKind::QmlLibrary, Keyframe>,
+ CacheType<QtQuick_Timeline, ModuleKind::QmlLibrary, KeyframeGroup>,
+ CacheType<QtQuick_Timeline, ModuleKind::QmlLibrary, Timeline>,
+ CacheType<QtQuick_Timeline, ModuleKind::QmlLibrary, TimelineAnimation>,
+ CacheType<QtQuick, ModuleKind::CppLibrary, QQuickStateOperation>,
+ CacheType<Qt_SafeRenderer, ModuleKind::QmlLibrary, SafePicture>,
+ CacheType<Qt_SafeRenderer, ModuleKind::QmlLibrary, SafeRendererPicture>,
+ CacheType<QtQuick_Window, ModuleKind::QmlLibrary, Window>>;
public:
CommonTypeCache(const ProjectStorage &projectStorage)
@@ -283,14 +281,14 @@ public:
std::fill(std::begin(m_typesWithoutProperties), std ::end(m_typesWithoutProperties), TypeId{});
}
- template<const char *moduleName, const char *typeName>
+ template<const char *moduleName, const char *typeName, ModuleKind moduleKind = ModuleKind::QmlLibrary>
TypeId typeId() const
{
- auto &type = std::get<CacheType<moduleName, typeName>>(m_types);
+ auto &type = std::get<CacheType<moduleName, moduleKind, typeName>>(m_types);
if (type.typeId)
return type.typeId;
- return refreshTypedId(type, moduleName, typeName);
+ return refreshTypedId(type, moduleName, moduleKind, typeName);
}
template<const char *typeName>
@@ -307,11 +305,11 @@ public:
else if constexpr (std::is_same_v<Type, int>)
return typeId<QML, IntType>();
else if constexpr (std::is_same_v<Type, uint>)
- return typeId<QML_cppnative, UIntType>();
+ return typeId<QML, UIntType, ModuleKind::CppLibrary>();
else if constexpr (std::is_same_v<Type, bool>)
return typeId<QML, BoolType>();
else if constexpr (std::is_same_v<Type, float>)
- return typeId<QML_cppnative, FloatType>();
+ return typeId<QML, FloatType, ModuleKind::CppLibrary>();
else if constexpr (std::is_same_v<Type, QString>)
return typeId<QML, string>();
else if constexpr (std::is_same_v<Type, QDateTime>)
@@ -341,10 +339,11 @@ public:
private:
TypeId refreshTypedId(BaseCacheType &type,
::Utils::SmallStringView moduleName,
+ ModuleKind moduleKind,
::Utils::SmallStringView typeName) const
{
if (!type.moduleId)
- type.moduleId = m_projectStorage.moduleId(moduleName);
+ type.moduleId = m_projectStorage.moduleId(moduleName, moduleKind);
type.typeId = m_projectStorage.typeId(type.moduleId, typeName, Storage::Version{});
@@ -353,10 +352,11 @@ private:
TypeId refreshTypedIdWithoutTransaction(BaseCacheType &type,
::Utils::SmallStringView moduleName,
- ::Utils::SmallStringView typeName) const
+ ::Utils::SmallStringView typeName,
+ ModuleKind moduleKind) const
{
if (!type.moduleId)
- type.moduleId = m_projectStorage.fetchModuleIdUnguarded(moduleName);
+ type.moduleId = m_projectStorage.fetchModuleIdUnguarded(moduleName, moduleKind);
type.typeId = m_projectStorage.fetchTypeIdByModuleIdAndExportedName(type.moduleId, typeName);
@@ -371,26 +371,27 @@ private:
std::copy(std::begin(typeIds), std::end(typeIds), std::begin(m_typesWithoutProperties));
}
- template<const char *moduleName, const char *typeName>
+ template<const char *moduleName, const char *typeName, ModuleKind moduleKind = ModuleKind::QmlLibrary>
TypeId typeIdWithoutTransaction() const
{
- auto &type = std::get<CacheType<moduleName, typeName>>(m_types);
+ auto &type = std::get<CacheType<moduleName, moduleKind, typeName>>(m_types);
if (type.typeId)
return type.typeId;
- return refreshTypedIdWithoutTransaction(type, moduleName, typeName);
+ return refreshTypedIdWithoutTransaction(type, moduleName, typeName, moduleKind);
}
void updateTypeIdsWithoutProperties()
{
- setupTypeIdsWithoutProperties({typeIdWithoutTransaction<QML, BoolType>(),
- typeIdWithoutTransaction<QML, IntType>(),
- typeIdWithoutTransaction<QML_cppnative, UIntType>(),
- typeIdWithoutTransaction<QML, DoubleType>(),
- typeIdWithoutTransaction<QML_cppnative, FloatType>(),
- typeIdWithoutTransaction<QML, date>(),
- typeIdWithoutTransaction<QML, string>(),
- typeIdWithoutTransaction<QML, url>()});
+ setupTypeIdsWithoutProperties(
+ {typeIdWithoutTransaction<QML, BoolType>(),
+ typeIdWithoutTransaction<QML, IntType>(),
+ typeIdWithoutTransaction<QML, UIntType, ModuleKind::CppLibrary>(),
+ typeIdWithoutTransaction<QML, DoubleType>(),
+ typeIdWithoutTransaction<QML, FloatType, ModuleKind::CppLibrary>(),
+ typeIdWithoutTransaction<QML, date>(),
+ typeIdWithoutTransaction<QML, string>(),
+ typeIdWithoutTransaction<QML, url>()});
}
private:
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.cpp b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.cpp
index 1376b2c3d9..d11190fdc7 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.cpp
@@ -11,6 +11,7 @@
#include <QDateTime>
#include <QDir>
+#include <QDirIterator>
#include <QFileInfo>
namespace QmlDesigner {
@@ -69,6 +70,18 @@ QString FileSystem::contentAsQString(const QString &filePath) const
return {};
}
+QStringList FileSystem::subdirectories(const QString &directoryPath) const
+{
+ QStringList directoryPaths;
+ directoryPaths.reserve(100);
+ QDirIterator directoryIterator{directoryPath, QDir::Dirs | QDir::NoDotAndDotDot};
+
+ while (directoryIterator.hasNext())
+ directoryPaths.push_back(directoryIterator.next());
+
+ return directoryPaths;
+}
+
void FileSystem::remove(const SourceIds &sourceIds)
{
for (SourceId sourceId : sourceIds)
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h
index 28754a8560..1c881741c6 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h
@@ -31,6 +31,7 @@ public:
long long lastModified(SourceId sourceId) const override;
FileStatus fileStatus(SourceId sourceId) const override;
QString contentAsQString(const QString &filePath) const override;
+ QStringList subdirectories(const QString &directoryPath) const override;
void remove(const SourceIds &sourceIds) override;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filesysteminterface.h b/src/plugins/qmldesigner/designercore/projectstorage/filesysteminterface.h
index 6a7c964fa6..ff7608c9a3 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/filesysteminterface.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/filesysteminterface.h
@@ -20,6 +20,7 @@ public:
virtual FileStatus fileStatus(SourceId sourceId) const = 0;
virtual void remove(const SourceIds &sourceIds) = 0;
virtual QString contentAsQString(const QString &filePath) const = 0;
+ virtual QStringList subdirectories(const QString &directoryPath) const = 0;
protected:
~FileSystemInterface() = default;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp
index a7577d3ab7..2283b64945 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp
@@ -7,6 +7,25 @@
namespace QmlDesigner {
+enum class SpecialIdState { Unresolved = -1 };
+
+constexpr TypeId unresolvedTypeId = TypeId::createSpecialState(SpecialIdState::Unresolved);
+
+class UnresolvedTypeId : public TypeId
+{
+public:
+ constexpr UnresolvedTypeId()
+ : TypeId{TypeId::createSpecialState(SpecialIdState::Unresolved)}
+ {}
+
+ static constexpr UnresolvedTypeId create(DatabaseType idNumber)
+ {
+ UnresolvedTypeId id;
+ id.id = idNumber;
+ return id;
+ }
+};
+
struct ProjectStorage::Statements
{
Statements(Sqlite::Database &database)
@@ -17,9 +36,13 @@ struct ProjectStorage::Statements
Sqlite::ReadWriteStatement<1, 2> insertTypeStatement{
"INSERT OR IGNORE INTO types(sourceId, name) VALUES(?1, ?2) RETURNING typeId", database};
Sqlite::WriteStatement<5> updatePrototypeAndExtensionStatement{
- "UPDATE types SET prototypeId=?2, prototypeNameId=?3, extensionId=?4, extensionNameId=?5 "
- "WHERE typeId=?1 AND (prototypeId IS NOT ?2 OR extensionId IS NOT ?3 AND prototypeId "
- "IS NOT ?4 OR extensionNameId IS NOT ?5)",
+ "UPDATE types "
+ "SET prototypeId=?2, prototypeNameId=?3, extensionId=?4, extensionNameId=?5 "
+ "WHERE typeId=?1 AND ( "
+ " prototypeId IS NOT ?2 "
+ " OR extensionId IS NOT ?3 "
+ " OR prototypeId IS NOT ?4 "
+ " OR extensionNameId IS NOT ?5)",
database};
mutable Sqlite::ReadStatement<1, 1> selectTypeIdByExportedNameStatement{
"SELECT typeId FROM exportedTypeNames WHERE name=?1", database};
@@ -90,7 +113,19 @@ struct ProjectStorage::Statements
Sqlite::WriteStatement<2> updateTypeTraitStatement{
"UPDATE types SET traits = ?2 WHERE typeId=?1", database};
Sqlite::WriteStatement<2> updateTypeAnnotationTraitStatement{
- "UPDATE types SET annotationTraits = ?2 WHERE typeId=?1", database};
+ "WITH RECURSIVE "
+ " typeSelection(typeId) AS ("
+ " VALUES(?1) "
+ " UNION ALL "
+ " SELECT t.typeId "
+ " FROM types AS t JOIN typeSelection AS ts "
+ " WHERE prototypeId=ts.typeId "
+ " AND t.typeId NOT IN (SELECT typeId FROM typeAnnotations)) "
+ "UPDATE types AS t "
+ "SET annotationTraits = ?2 "
+ "FROM typeSelection ts "
+ "WHERE t.typeId=ts.typeId",
+ database};
Sqlite::ReadStatement<1, 2> selectNotUpdatedTypesInSourcesStatement{
"SELECT DISTINCT typeId FROM types WHERE (sourceId IN carray(?1) AND typeId NOT IN "
"carray(?2))",
@@ -235,14 +270,14 @@ struct ProjectStorage::Statements
database};
Sqlite::WriteStatement<1> deleteEnumerationDeclarationStatement{
"DELETE FROM enumerationDeclarations WHERE enumerationDeclarationId=?", database};
- mutable Sqlite::ReadStatement<1, 1> selectModuleIdByNameStatement{
- "SELECT moduleId FROM modules WHERE name=? LIMIT 1", database};
- mutable Sqlite::ReadWriteStatement<1, 1> insertModuleNameStatement{
- "INSERT INTO modules(name) VALUES(?1) RETURNING moduleId", database};
- mutable Sqlite::ReadStatement<1, 1> selectModuleNameStatement{
- "SELECT name FROM modules WHERE moduleId =?1", database};
- mutable Sqlite::ReadStatement<2> selectAllModulesStatement{"SELECT name, moduleId FROM modules",
- database};
+ mutable Sqlite::ReadStatement<1, 2> selectModuleIdByNameStatement{
+ "SELECT moduleId FROM modules WHERE kind=?1 AND name=?2 LIMIT 1", database};
+ mutable Sqlite::ReadWriteStatement<1, 2> insertModuleNameStatement{
+ "INSERT INTO modules(kind, name) VALUES(?1, ?2) RETURNING moduleId", database};
+ mutable Sqlite::ReadStatement<2, 1> selectModuleStatement{
+ "SELECT name, kind FROM modules WHERE moduleId =?1", database};
+ mutable Sqlite::ReadStatement<3> selectAllModulesStatement{
+ "SELECT name, kind, moduleId FROM modules", database};
mutable Sqlite::ReadStatement<1, 2> selectTypeIdBySourceIdAndNameStatement{
"SELECT typeId FROM types WHERE sourceId=?1 and name=?2", database};
mutable Sqlite::ReadStatement<1, 3> selectTypeIdByModuleIdsAndExportedNameStatement{
@@ -345,13 +380,51 @@ struct ProjectStorage::Statements
"SELECT name FROM propertyDeclarations WHERE propertyDeclarationId=?", database};
Sqlite::WriteStatement<2> updatePropertyDeclarationTypeStatement{
"UPDATE propertyDeclarations SET propertyTypeId=?2 WHERE propertyDeclarationId=?1", database};
- Sqlite::ReadWriteStatement<2, 1> updatePrototypeIdToNullStatement{
- "UPDATE types SET prototypeId=NULL WHERE prototypeId=?1 RETURNING "
- "typeId, prototypeNameId",
+ Sqlite::ReadWriteStatement<2, 2> updatePrototypeIdToTypeIdStatement{
+ "UPDATE types "
+ "SET prototypeId=?2 "
+ "WHERE prototypeId=?1 "
+ "RETURNING typeId, prototypeNameId",
+ database};
+ Sqlite::ReadWriteStatement<2, 2> updateExtensionIdToTypeIdStatement{
+ "UPDATE types "
+ "SET extensionId=?2 "
+ "WHERE extensionId=?1 "
+ "RETURNING typeId, extensionNameId",
+ database};
+ Sqlite::ReadStatement<2, 2> selectTypeIdAndPrototypeNameIdForPrototypeIdAndTypeNameStatement{
+ "SELECT typeId, prototypeNameId "
+ "FROM types "
+ "WHERE prototypeNameId IN ( "
+ " SELECT importedTypeNameId "
+ " FROM "
+ " importedTypeNames WHERE name=?1) "
+ " AND prototypeId=?2",
+ database};
+ Sqlite::ReadStatement<2, 2> selectTypeIdAndPrototypeNameIdForPrototypeIdAndSourceIdStatement{
+ "SELECT typeId , prototypeNameId "
+ "FROM types "
+ "WHERE prototypeId=?1 AND sourceId=?2",
+ database};
+ Sqlite::ReadStatement<2, 2> selectTypeIdAndExtensionNameIdForExtensionIdAndSourceIdStatement{
+ "SELECT typeId, extensionNameId "
+ "FROM types "
+ "WHERE extensionId=?1 AND sourceId=?2",
database};
- Sqlite::ReadWriteStatement<2, 1> updateExtensionIdToNullStatement{
- "UPDATE types SET extensionId=NULL WHERE extensionId=?1 RETURNING "
- "typeId, extensionNameId",
+ Sqlite::ReadWriteStatement<3, 3> updatePrototypeIdAndExtensionIdToTypeIdForSourceIdStatement{
+ "UPDATE types "
+ "SET prototypeId=?2, extensionId=?3 "
+ "WHERE sourceId=?1 "
+ "RETURNING typeId, prototypeNameId, extensionNameId",
+ database};
+ Sqlite::ReadStatement<2, 2> selectTypeIdForExtensionIdAndTypeNameStatement{
+ "SELECT typeId , prototypeNameId "
+ "FROM types "
+ "WHERE extensionNameId IN ( "
+ " SELECT importedTypeNameId "
+ " FROM importedTypeNames "
+ " WHERE name=?1) "
+ " AND extensionId=?2",
database};
Sqlite::WriteStatement<2> updateTypePrototypeStatement{
"UPDATE types SET prototypeId=?2 WHERE typeId=?1", database};
@@ -490,25 +563,31 @@ struct ProjectStorage::Statements
"DELETE FROM exportedTypeNames WHERE exportedTypeNameId=?", database};
Sqlite::WriteStatement<2> updateExportedTypeNameTypeIdStatement{
"UPDATE exportedTypeNames SET typeId=?2 WHERE exportedTypeNameId=?1", database};
- mutable Sqlite::ReadStatement<4, 1> selectProjectDatasForSourceIdsStatement{
- "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE "
- "projectSourceId IN carray(?1) ORDER BY projectSourceId, sourceId",
+ mutable Sqlite::ReadStatement<4, 1> selectDirectoryInfosForSourceIdsStatement{
+ "SELECT directorySourceId, sourceId, moduleId, fileType FROM directoryInfos WHERE "
+ "directorySourceId IN carray(?1) ORDER BY directorySourceId, sourceId",
database};
- Sqlite::WriteStatement<4> insertProjectDataStatement{
- "INSERT INTO projectDatas(projectSourceId, sourceId, "
+ Sqlite::WriteStatement<4> insertDirectoryInfoStatement{
+ "INSERT INTO directoryInfos(directorySourceId, sourceId, "
"moduleId, fileType) VALUES(?1, ?2, ?3, ?4)",
database};
- Sqlite::WriteStatement<2> deleteProjectDataStatement{
- "DELETE FROM projectDatas WHERE projectSourceId=?1 AND sourceId=?2", database};
- Sqlite::WriteStatement<4> updateProjectDataStatement{
- "UPDATE projectDatas SET moduleId=?3, fileType=?4 WHERE projectSourceId=?1 AND sourceId=?2",
+ Sqlite::WriteStatement<2> deleteDirectoryInfoStatement{
+ "DELETE FROM directoryInfos WHERE directorySourceId=?1 AND sourceId=?2", database};
+ Sqlite::WriteStatement<4> updateDirectoryInfoStatement{
+ "UPDATE directoryInfos SET moduleId=?3, fileType=?4 WHERE directorySourceId=?1 AND sourceId=?2",
+ database};
+ mutable Sqlite::ReadStatement<4, 1> selectDirectoryInfosForSourceIdStatement{
+ "SELECT directorySourceId, sourceId, moduleId, fileType FROM directoryInfos WHERE "
+ "directorySourceId=?1",
database};
- mutable Sqlite::ReadStatement<4, 1> selectProjectDatasForSourceIdStatement{
- "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE "
- "projectSourceId=?1",
+ mutable Sqlite::ReadStatement<4, 2> selectDirectoryInfosForSourceIdAndFileTypeStatement{
+ "SELECT directorySourceId, sourceId, moduleId, fileType FROM directoryInfos WHERE "
+ "directorySourceId=?1 AND fileType=?2",
database};
- mutable Sqlite::ReadStatement<4, 1> selectProjectDataForSourceIdStatement{
- "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE "
+ mutable Sqlite::ReadStatement<1, 2> selectDirectoryInfosSourceIdsForSourceIdAndFileTypeStatement{
+ "SELECT sourceId FROM directoryInfos WHERE directorySourceId=?1 AND fileType=?2", database};
+ mutable Sqlite::ReadStatement<4, 1> selectDirectoryInfoForSourceIdStatement{
+ "SELECT directorySourceId, sourceId, moduleId, fileType FROM directoryInfos WHERE "
"sourceId=?1 LIMIT 1",
database};
mutable Sqlite::ReadStatement<1, 1> selectTypeIdsForSourceIdsStatement{
@@ -601,6 +680,13 @@ struct ProjectStorage::Statements
"UPDATE types SET defaultPropertyId=NULL WHERE defaultPropertyId=?1", database};
mutable Sqlite::ReadStatement<3, 1> selectInfoTypeByTypeIdStatement{
"SELECT sourceId, traits, annotationTraits FROM types WHERE typeId=?", database};
+ mutable Sqlite::ReadStatement<1, 1> selectSourceIdByTypeIdStatement{
+ "SELECT sourceId FROM types WHERE typeId=?", database};
+ mutable Sqlite::ReadStatement<1, 1> selectPrototypeAnnotationTraitsByTypeIdStatement{
+ "SELECT annotationTraits "
+ "FROM types "
+ "WHERE typeId=(SELECT prototypeId FROM types WHERE typeId=?)",
+ database};
mutable Sqlite::ReadStatement<1, 1> selectDefaultPropertyDeclarationIdStatement{
"SELECT defaultPropertyId FROM types WHERE typeId=?", database};
mutable Sqlite::ReadStatement<1, 1> selectPrototypeIdsForTypeIdInOrderStatement{
@@ -639,17 +725,21 @@ struct ProjectStorage::Statements
database};
Sqlite::WriteStatement<1> deletePropertyEditorPathStatement{
"DELETE FROM propertyEditorPaths WHERE typeId=?1", database};
- mutable Sqlite::ReadStatement<4, 1> selectTypeAnnotationsForSourceIdsStatement{
- "SELECT typeId, iconPath, itemLibrary, hints FROM typeAnnotations WHERE "
+ mutable Sqlite::ReadStatement<5, 1> selectTypeAnnotationsForSourceIdsStatement{
+ "SELECT typeId, typeName, iconPath, itemLibrary, hints FROM typeAnnotations WHERE "
"sourceId IN carray(?1) ORDER BY typeId",
database};
- Sqlite::WriteStatement<6> insertTypeAnnotationStatement{
+ Sqlite::WriteStatement<7> insertTypeAnnotationStatement{
"INSERT INTO "
- " typeAnnotations(typeId, sourceId, directorySourceId, iconPath, itemLibrary, hints) "
- "VALUES(?1, ?2, ?3, ?4, ?5, ?6)",
+ " typeAnnotations(typeId, sourceId, directorySourceId, typeName, iconPath, itemLibrary, "
+ " hints) "
+ "VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7)",
+ database};
+ Sqlite::WriteStatement<5> updateTypeAnnotationStatement{
+ "UPDATE typeAnnotations "
+ "SET typeName=?2, iconPath=?3, itemLibrary=?4, hints=?5 "
+ "WHERE typeId=?1",
database};
- Sqlite::WriteStatement<4> updateTypeAnnotationStatement{
- "UPDATE typeAnnotations SET iconPath=?2, itemLibrary=?3, hints=?4 WHERE typeId=?1", database};
Sqlite::WriteStatement<1> deleteTypeAnnotationStatement{
"DELETE FROM typeAnnotations WHERE typeId=?1", database};
mutable Sqlite::ReadStatement<1, 1> selectTypeIconPathStatement{
@@ -663,22 +753,22 @@ struct ProjectStorage::Statements
"SELECT sourceId FROM typeAnnotations WHERE directorySourceId=?1 ORDER BY sourceId", database};
mutable Sqlite::ReadStatement<1, 0> selectTypeAnnotationDirectorySourceIdsStatement{
"SELECT DISTINCT directorySourceId FROM typeAnnotations ORDER BY directorySourceId", database};
- mutable Sqlite::ReadStatement<9> selectItemLibraryEntriesStatement{
- "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', "
- " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', "
- " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' "
+ mutable Sqlite::ReadStatement<10> selectItemLibraryEntriesStatement{
+ "SELECT typeId, typeName, i.value->>'$.name', i.value->>'$.iconPath', "
+ " i.value->>'$.category', i.value->>'$.import', i.value->>'$.toolTip', "
+ " i.value->>'$.properties', i.value->>'$.extraFilePaths', i.value->>'$.templatePath' "
"FROM typeAnnotations AS ta , json_each(ta.itemLibrary) AS i "
"WHERE ta.itemLibrary IS NOT NULL",
database};
- mutable Sqlite::ReadStatement<9, 1> selectItemLibraryEntriesByTypeIdStatement{
- "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', "
- " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', "
- " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' "
+ mutable Sqlite::ReadStatement<10, 1> selectItemLibraryEntriesByTypeIdStatement{
+ "SELECT typeId, typeName, i.value->>'$.name', i.value->>'$.iconPath', "
+ " i.value->>'$.category', i.value->>'$.import', i.value->>'$.toolTip', "
+ " i.value->>'$.properties', i.value->>'$.extraFilePaths', i.value->>'$.templatePath' "
"FROM typeAnnotations AS ta, json_each(ta.itemLibrary) AS i "
"WHERE typeId=?1 AND ta.itemLibrary IS NOT NULL",
database};
- mutable Sqlite::ReadStatement<9, 1> selectItemLibraryEntriesBySourceIdStatement{
- "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', "
+ mutable Sqlite::ReadStatement<10, 1> selectItemLibraryEntriesBySourceIdStatement{
+ "SELECT typeId, typeName, i.value->>'$.name', i.value->>'$.iconPath', "
"i.value->>'$.category', "
" i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', "
" i.value->>'$.extraFilePaths', i.value->>'$.templatePath' "
@@ -724,7 +814,7 @@ public:
createModuleExportedImportsTable(database, moduleIdColumn);
createDocumentImportsTable(database, moduleIdColumn);
createFileStatusesTable(database);
- createProjectDatasTable(database);
+ createDirectoryInfosTable(database);
createPropertyEditorPathsTable(database);
createTypeAnnotionsTable(database);
}
@@ -774,23 +864,23 @@ public:
auto &sourceIdColumn = typesTable.addColumn("sourceId", Sqlite::StrictColumnType::Integer);
auto &typesNameColumn = typesTable.addColumn("name", Sqlite::StrictColumnType::Text);
typesTable.addColumn("traits", Sqlite::StrictColumnType::Integer);
- auto &prototypeIdColumn = typesTable.addForeignKeyColumn("prototypeId",
- typesTable,
- Sqlite::ForeignKeyAction::NoAction,
- Sqlite::ForeignKeyAction::Restrict);
- typesTable.addColumn("prototypeNameId", Sqlite::StrictColumnType::Integer);
- auto &extensionIdColumn = typesTable.addForeignKeyColumn("extensionId",
- typesTable,
- Sqlite::ForeignKeyAction::NoAction,
- Sqlite::ForeignKeyAction::Restrict);
- typesTable.addColumn("extensionNameId", Sqlite::StrictColumnType::Integer);
+ auto &prototypeIdColumn = typesTable.addColumn("prototypeId",
+ Sqlite::StrictColumnType::Integer);
+ auto &prototypeNameIdColumn = typesTable.addColumn("prototypeNameId",
+ Sqlite::StrictColumnType::Integer);
+ auto &extensionIdColumn = typesTable.addColumn("extensionId",
+ Sqlite::StrictColumnType::Integer);
+ auto &extensionNameIdColumn = typesTable.addColumn("extensionNameId",
+ Sqlite::StrictColumnType::Integer);
auto &defaultPropertyIdColumn = typesTable.addColumn("defaultPropertyId",
Sqlite::StrictColumnType::Integer);
typesTable.addColumn("annotationTraits", Sqlite::StrictColumnType::Integer);
typesTable.addUniqueIndex({sourceIdColumn, typesNameColumn});
typesTable.addIndex({defaultPropertyIdColumn});
- typesTable.addIndex({prototypeIdColumn});
- typesTable.addIndex({extensionIdColumn});
+ typesTable.addIndex({prototypeIdColumn, sourceIdColumn});
+ typesTable.addIndex({extensionIdColumn, sourceIdColumn});
+ typesTable.addIndex({prototypeNameIdColumn});
+ typesTable.addIndex({extensionNameIdColumn});
typesTable.initialize(database);
@@ -942,9 +1032,10 @@ public:
auto &modelIdColumn = table.addColumn("moduleId",
Sqlite::StrictColumnType::Integer,
{Sqlite::PrimaryKey{}});
+ auto &kindColumn = table.addColumn("kind", Sqlite::StrictColumnType::Integer);
auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text);
- table.addUniqueIndex({nameColumn});
+ table.addUniqueIndex({kindColumn, nameColumn});
table.initialize(database);
@@ -1041,20 +1132,21 @@ public:
table.initialize(database);
}
- void createProjectDatasTable(Database &database)
+ void createDirectoryInfosTable(Database &database)
{
Sqlite::StrictTable table;
table.setUseIfNotExists(true);
table.setUseWithoutRowId(true);
- table.setName("projectDatas");
- auto &projectSourceIdColumn = table.addColumn("projectSourceId",
+ table.setName("directoryInfos");
+ auto &directorySourceIdColumn = table.addColumn("directorySourceId",
Sqlite::StrictColumnType::Integer);
auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer);
table.addColumn("moduleId", Sqlite::StrictColumnType::Integer);
- table.addColumn("fileType", Sqlite::StrictColumnType::Integer);
+ auto &fileTypeColumn = table.addColumn("fileType", Sqlite::StrictColumnType::Integer);
- table.addPrimaryKeyContraint({projectSourceIdColumn, sourceIdColumn});
+ table.addPrimaryKeyContraint({directorySourceIdColumn, sourceIdColumn});
table.addUniqueIndex({sourceIdColumn});
+ table.addIndex({directorySourceIdColumn, fileTypeColumn});
table.initialize(database);
}
@@ -1086,7 +1178,7 @@ public:
auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer);
auto &directorySourceIdColumn = table.addColumn("directorySourceId",
Sqlite::StrictColumnType::Integer);
-
+ table.addColumn("typeName", Sqlite::StrictColumnType::Text);
table.addColumn("iconPath", Sqlite::StrictColumnType::Text);
table.addColumn("itemLibrary", Sqlite::StrictColumnType::Text);
table.addColumn("hints", Sqlite::StrictColumnType::Text);
@@ -1098,8 +1190,11 @@ public:
}
};
-ProjectStorage::ProjectStorage(Database &database, bool isInitialized)
+ProjectStorage::ProjectStorage(Database &database,
+ ProjectStorageErrorNotifierInterface &errorNotifier,
+ bool isInitialized)
: database{database}
+ , errorNotifier{&errorNotifier}
, exclusiveTransaction{database}
, initializer{std::make_unique<ProjectStorage::Initializer>(database, isInitialized)}
, moduleCache{ModuleStorageAdapter{*this}}
@@ -1143,7 +1238,9 @@ void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackag
package.moduleDependencies,
package.updatedModuleDependencySourceIds,
package.moduleExportedImports,
- package.updatedModuleIds);
+ package.updatedModuleIds,
+ relinkablePrototypes,
+ relinkableExtensions);
synchronizeTypes(package.types,
updatedTypeIds,
insertedAliasPropertyDeclarations,
@@ -1174,7 +1271,7 @@ void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackag
linkAliases(insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations);
- synchronizeProjectDatas(package.projectDatas, package.updatedProjectSourceIds);
+ synchronizeDirectoryInfos(package.directoryInfos, package.updatedDirectoryInfoSourceIds);
commonTypeCache_.resetTypeIds();
});
@@ -1191,7 +1288,24 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports imports, Source
keyValue("source id", sourceId)};
Sqlite::withImmediateTransaction(database, [&] {
- synchronizeDocumentImports(imports, {sourceId}, Storage::Synchronization::ImportKind::Import);
+ AliasPropertyDeclarations relinkableAliasPropertyDeclarations;
+ PropertyDeclarations relinkablePropertyDeclarations;
+ Prototypes relinkablePrototypes;
+ Prototypes relinkableExtensions;
+ TypeIds deletedTypeIds;
+
+ synchronizeDocumentImports(imports,
+ {sourceId},
+ Storage::Synchronization::ImportKind::Import,
+ Relink::Yes,
+ relinkablePrototypes,
+ relinkableExtensions);
+
+ relink(relinkableAliasPropertyDeclarations,
+ relinkablePropertyDeclarations,
+ relinkablePrototypes,
+ relinkableExtensions,
+ deletedTypeIds);
});
}
@@ -1207,21 +1321,21 @@ void ProjectStorage::removeObserver(ProjectStorageObserver *observer)
observers.removeOne(observer);
}
-ModuleId ProjectStorage::moduleId(Utils::SmallStringView moduleName) const
+ModuleId ProjectStorage::moduleId(Utils::SmallStringView moduleName, Storage::ModuleKind kind) const
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"get module id"_t,
projectStorageCategory(),
keyValue("module name", moduleName)};
- auto moduleId = moduleCache.id(moduleName);
+ auto moduleId = moduleCache.id({moduleName, kind});
tracer.end(keyValue("module id", moduleId));
return moduleId;
}
-Utils::SmallString ProjectStorage::moduleName(ModuleId moduleId) const
+Storage::Module ProjectStorage::module(ModuleId moduleId) const
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"get module name"_t,
@@ -1231,11 +1345,12 @@ Utils::SmallString ProjectStorage::moduleName(ModuleId moduleId) const
if (!moduleId)
throw ModuleDoesNotExists{};
- auto moduleName = moduleCache.value(moduleId);
+ auto module = moduleCache.value(moduleId);
- tracer.end(keyValue("module name", moduleName));
+ tracer.end(keyValue("module name", module.name));
+ tracer.end(keyValue("module kind", module.kind));
- return moduleName;
+ return {module.name, module.kind};
}
TypeId ProjectStorage::typeId(ModuleId moduleId,
@@ -1570,6 +1685,7 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(TypeId type
Storage::Info::ItemLibraryEntries entries;
auto callback = [&](TypeId typeId_,
+ Utils::SmallStringView typeName,
Utils::SmallStringView name,
Utils::SmallStringView iconPath,
Utils::SmallStringView category,
@@ -1578,7 +1694,8 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(TypeId type
Utils::SmallStringView properties,
Utils::SmallStringView extraFilePaths,
Utils::SmallStringView templatePath) {
- auto &last = entries.emplace_back(typeId_, name, iconPath, category, import, toolTip, templatePath);
+ auto &last = entries.emplace_back(
+ typeId_, typeName, name, iconPath, category, import, toolTip, templatePath);
if (properties.size())
s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties);
if (extraFilePaths.size())
@@ -1603,6 +1720,7 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(ImportId im
Storage::Info::ItemLibraryEntries entries;
auto callback = [&](TypeId typeId_,
+ Utils::SmallStringView typeName,
Utils::SmallStringView name,
Utils::SmallStringView iconPath,
Utils::SmallStringView category,
@@ -1611,7 +1729,8 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(ImportId im
Utils::SmallStringView properties,
Utils::SmallStringView extraFilePaths,
Utils::SmallStringView templatePath) {
- auto &last = entries.emplace_back(typeId_, name, iconPath, category, import, toolTip, templatePath);
+ auto &last = entries.emplace_back(
+ typeId_, typeName, name, iconPath, category, import, toolTip, templatePath);
if (properties.size())
s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties);
if (extraFilePaths.size())
@@ -1636,6 +1755,7 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(SourceId so
Storage::Info::ItemLibraryEntries entries;
auto callback = [&](TypeId typeId,
+ Utils::SmallStringView typeName,
Utils::SmallStringView name,
Utils::SmallStringView iconPath,
Utils::SmallStringView category,
@@ -1644,7 +1764,8 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(SourceId so
Utils::SmallStringView properties,
Utils::SmallStringView extraFilePaths,
Utils::SmallStringView templatePath) {
- auto &last = entries.emplace_back(typeId, name, iconPath, category, import, toolTip, templatePath);
+ auto &last = entries.emplace_back(
+ typeId, typeName, name, iconPath, category, import, toolTip, templatePath);
if (properties.size())
s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties);
if (extraFilePaths.size())
@@ -1667,6 +1788,7 @@ Storage::Info::ItemLibraryEntries ProjectStorage::allItemLibraryEntries() const
Storage::Info::ItemLibraryEntries entries;
auto callback = [&](TypeId typeId,
+ Utils::SmallStringView typeName,
Utils::SmallStringView name,
Utils::SmallStringView iconPath,
Utils::SmallStringView category,
@@ -1675,7 +1797,8 @@ Storage::Info::ItemLibraryEntries ProjectStorage::allItemLibraryEntries() const
Utils::SmallStringView properties,
Utils::SmallStringView extraFilePaths,
Utils::SmallStringView templatePath) {
- auto &last = entries.emplace_back(typeId, name, iconPath, category, import, toolTip, templatePath);
+ auto &last = entries.emplace_back(
+ typeId, typeName, name, iconPath, category, import, toolTip, templatePath);
if (properties.size())
s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties);
if (extraFilePaths.size())
@@ -2082,53 +2205,87 @@ FileStatus ProjectStorage::fetchFileStatus(SourceId sourceId) const
return fileStatus;
}
-std::optional<Storage::Synchronization::ProjectData> ProjectStorage::fetchProjectData(SourceId sourceId) const
+std::optional<Storage::Synchronization::DirectoryInfo> ProjectStorage::fetchDirectoryInfo(SourceId sourceId) const
{
using NanotraceHR::keyValue;
- NanotraceHR::Tracer tracer{"fetch project data"_t,
+ NanotraceHR::Tracer tracer{"fetch directory info"_t,
projectStorageCategory(),
keyValue("source id", sourceId)};
- auto projectData = s->selectProjectDataForSourceIdStatement
- .optionalValueWithTransaction<Storage::Synchronization::ProjectData>(
+ auto directoryInfo = s->selectDirectoryInfoForSourceIdStatement
+ .optionalValueWithTransaction<Storage::Synchronization::DirectoryInfo>(
sourceId);
- tracer.end(keyValue("project data", projectData));
+ tracer.end(keyValue("directory info", directoryInfo));
+
+ return directoryInfo;
+}
+
+Storage::Synchronization::DirectoryInfos ProjectStorage::fetchDirectoryInfos(SourceId directorySourceId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch directory infos by source id"_t,
+ projectStorageCategory(),
+ keyValue("source id", directorySourceId)};
+
+ auto directoryInfos = s->selectDirectoryInfosForSourceIdStatement
+ .valuesWithTransaction<Storage::Synchronization::DirectoryInfo, 1024>(
+ directorySourceId);
+
+ tracer.end(keyValue("directory infos", directoryInfos));
+
+ return directoryInfos;
+}
+
+Storage::Synchronization::DirectoryInfos ProjectStorage::fetchDirectoryInfos(
+ SourceId directorySourceId, Storage::Synchronization::FileType fileType) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch directory infos by source id and file type"_t,
+ projectStorageCategory(),
+ keyValue("source id", directorySourceId),
+ keyValue("file type", fileType)};
+
+ auto directoryInfos = s->selectDirectoryInfosForSourceIdAndFileTypeStatement
+ .valuesWithTransaction<Storage::Synchronization::DirectoryInfo, 16>(
+ directorySourceId, fileType);
- return projectData;
+ tracer.end(keyValue("directory infos", directoryInfos));
+
+ return directoryInfos;
}
-Storage::Synchronization::ProjectDatas ProjectStorage::fetchProjectDatas(SourceId projectSourceId) const
+Storage::Synchronization::DirectoryInfos ProjectStorage::fetchDirectoryInfos(
+ const SourceIds &directorySourceIds) const
{
using NanotraceHR::keyValue;
- NanotraceHR::Tracer tracer{"fetch project datas by source id"_t,
+ NanotraceHR::Tracer tracer{"fetch directory infos by source ids"_t,
projectStorageCategory(),
- keyValue("source id", projectSourceId)};
+ keyValue("source ids", directorySourceIds)};
- auto projectDatas = s->selectProjectDatasForSourceIdStatement
- .valuesWithTransaction<Storage::Synchronization::ProjectData, 1024>(
- projectSourceId);
+ auto directoryInfos = s->selectDirectoryInfosForSourceIdsStatement
+ .valuesWithTransaction<Storage::Synchronization::DirectoryInfo, 64>(
+ toIntegers(directorySourceIds));
- tracer.end(keyValue("project datas", projectDatas));
+ tracer.end(keyValue("directory infos", directoryInfos));
- return projectDatas;
+ return directoryInfos;
}
-Storage::Synchronization::ProjectDatas ProjectStorage::fetchProjectDatas(
- const SourceIds &projectSourceIds) const
+SmallSourceIds<32> ProjectStorage::fetchSubdirectorySourceIds(SourceId directorySourceId) const
{
using NanotraceHR::keyValue;
- NanotraceHR::Tracer tracer{"fetch project datas by source ids"_t,
+ NanotraceHR::Tracer tracer{"fetch subdirectory source ids"_t,
projectStorageCategory(),
- keyValue("source ids", projectSourceIds)};
+ keyValue("source id", directorySourceId)};
- auto projectDatas = s->selectProjectDatasForSourceIdsStatement
- .valuesWithTransaction<Storage::Synchronization::ProjectData, 64>(
- toIntegers(projectSourceIds));
+ auto sourceIds = s->selectDirectoryInfosSourceIdsForSourceIdAndFileTypeStatement
+ .valuesWithTransaction<SmallSourceIds<32>>(
+ directorySourceId, Storage::Synchronization::FileType::Directory);
- tracer.end(keyValue("project datas", projectDatas));
+ tracer.end(keyValue("source ids", sourceIds));
- return projectDatas;
+ return sourceIds;
}
void ProjectStorage::setPropertyEditorPathId(TypeId typeId, SourceId pathId)
@@ -2168,20 +2325,17 @@ void ProjectStorage::resetForTestsOnly()
moduleCache.clearForTestOnly();
}
-bool ProjectStorage::moduleNameLess(Utils::SmallStringView first, Utils::SmallStringView second) noexcept
-{
- return first < second;
-}
-
-ModuleId ProjectStorage::fetchModuleId(Utils::SmallStringView moduleName)
+ModuleId ProjectStorage::fetchModuleId(Utils::SmallStringView moduleName,
+ Storage::ModuleKind moduleKind)
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"fetch module id"_t,
projectStorageCategory(),
- keyValue("module name", moduleName)};
+ keyValue("module name", moduleName),
+ keyValue("module kind", moduleKind)};
auto moduleId = Sqlite::withDeferredTransaction(database, [&] {
- return fetchModuleIdUnguarded(moduleName);
+ return fetchModuleIdUnguarded(moduleName, moduleKind);
});
tracer.end(keyValue("module id", moduleId));
@@ -2189,26 +2343,26 @@ ModuleId ProjectStorage::fetchModuleId(Utils::SmallStringView moduleName)
return moduleId;
}
-Utils::PathString ProjectStorage::fetchModuleName(ModuleId id)
+Storage::Module ProjectStorage::fetchModule(ModuleId id)
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"fetch module name"_t,
projectStorageCategory(),
keyValue("module id", id)};
- auto moduleName = Sqlite::withDeferredTransaction(database,
- [&] { return fetchModuleNameUnguarded(id); });
+ auto module = Sqlite::withDeferredTransaction(database, [&] { return fetchModuleUnguarded(id); });
- tracer.end(keyValue("module name", moduleName));
+ tracer.end(keyValue("module name", module.name));
+ tracer.end(keyValue("module name", module.kind));
- return moduleName;
+ return module;
}
-ProjectStorage::Modules ProjectStorage::fetchAllModules() const
+ProjectStorage::ModuleCacheEntries ProjectStorage::fetchAllModules() const
{
NanotraceHR::Tracer tracer{"fetch all modules"_t, projectStorageCategory()};
- return s->selectAllModulesStatement.valuesWithTransaction<Module, 128>();
+ return s->selectAllModulesStatement.valuesWithTransaction<ModuleCacheEntry, 128>();
}
void ProjectStorage::callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds)
@@ -2277,12 +2431,6 @@ void ProjectStorage::updateTypeIdInTypeAnnotations(Storage::Synchronization::Typ
annotation.typeName);
}
- for (auto &annotation : typeAnnotations) {
- if (!annotation.typeId)
- qWarning() << moduleName(annotation.moduleId).toQString()
- << annotation.typeName.toQString();
- }
-
typeAnnotations.erase(std::remove_if(typeAnnotations.begin(),
typeAnnotations.end(),
[](const auto &annotation) { return !annotation.typeId; }),
@@ -2311,7 +2459,6 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn
if (!annotation.sourceId)
throw TypeAnnotationHasInvalidSourceId{};
- synchronizeTypeTraits(annotation.typeId, annotation.traits);
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"insert type annotations"_t,
@@ -2321,16 +2468,19 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn
s->insertTypeAnnotationStatement.write(annotation.typeId,
annotation.sourceId,
annotation.directorySourceId,
+ annotation.typeName,
annotation.iconPath,
createEmptyAsNull(annotation.itemLibraryJson),
createEmptyAsNull(annotation.hintsJson));
+
+ synchronizeTypeTraits(annotation.typeId, annotation.traits);
};
auto update = [&](const TypeAnnotationView &annotationFromDatabase,
const TypeAnnotation &annotation) {
- synchronizeTypeTraits(annotation.typeId, annotation.traits);
- if (annotationFromDatabase.iconPath != annotation.iconPath
+ if (annotationFromDatabase.typeName != annotation.typeName
+ || annotationFromDatabase.iconPath != annotation.iconPath
|| annotationFromDatabase.itemLibraryJson != annotation.itemLibraryJson
|| annotationFromDatabase.hintsJson != annotation.hintsJson) {
using NanotraceHR::keyValue;
@@ -2341,24 +2491,33 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn
keyValue("type annotation", annotation)};
s->updateTypeAnnotationStatement.write(annotation.typeId,
+ annotation.typeName,
annotation.iconPath,
createEmptyAsNull(annotation.itemLibraryJson),
createEmptyAsNull(annotation.hintsJson));
+
+ synchronizeTypeTraits(annotation.typeId, annotation.traits);
+
return Sqlite::UpdateChange::Update;
}
+ synchronizeTypeTraits(annotation.typeId, annotation.traits);
+
return Sqlite::UpdateChange::No;
};
auto remove = [&](const TypeAnnotationView &annotationFromDatabase) {
- synchronizeTypeTraits(annotationFromDatabase.typeId, Storage::TypeTraits{});
-
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"remove type annotations"_t,
projectStorageCategory(),
keyValue("type annotation", annotationFromDatabase)};
+ auto prototypeAnnotationTraits = s->selectPrototypeAnnotationTraitsByTypeIdStatement
+ .value<long long>(annotationFromDatabase.typeId);
s->deleteTypeAnnotationStatement.write(annotationFromDatabase.typeId);
+
+ s->updateTypeAnnotationTraitStatement.write(annotationFromDatabase.typeId,
+ prototypeAnnotationTraits);
};
Sqlite::insertUpdateDelete(range, typeAnnotations, compareKey, insert, update, remove);
@@ -2432,74 +2591,75 @@ void ProjectStorage::synchronizeTypes(Storage::Synchronization::Types &types,
syncDefaultProperties(types);
}
-void ProjectStorage::synchronizeProjectDatas(Storage::Synchronization::ProjectDatas &projectDatas,
- const SourceIds &updatedProjectSourceIds)
+void ProjectStorage::synchronizeDirectoryInfos(Storage::Synchronization::DirectoryInfos &directoryInfos,
+ const SourceIds &updatedDirectoryInfoSourceIds)
{
- NanotraceHR::Tracer tracer{"synchronize project datas"_t, projectStorageCategory()};
+ NanotraceHR::Tracer tracer{"synchronize directory infos"_t, projectStorageCategory()};
auto compareKey = [](auto &&first, auto &&second) {
- auto projectSourceIdDifference = first.projectSourceId - second.projectSourceId;
- if (projectSourceIdDifference != 0)
- return projectSourceIdDifference;
+ auto directorySourceIdDifference = first.directorySourceId - second.directorySourceId;
+ if (directorySourceIdDifference != 0)
+ return directorySourceIdDifference;
return first.sourceId - second.sourceId;
};
- std::sort(projectDatas.begin(), projectDatas.end(), [&](auto &&first, auto &&second) {
- return std::tie(first.projectSourceId, first.sourceId)
- < std::tie(second.projectSourceId, second.sourceId);
+ std::sort(directoryInfos.begin(), directoryInfos.end(), [&](auto &&first, auto &&second) {
+ return std::tie(first.directorySourceId, first.sourceId)
+ < std::tie(second.directorySourceId, second.sourceId);
});
- auto range = s->selectProjectDatasForSourceIdsStatement.range<Storage::Synchronization::ProjectData>(
- toIntegers(updatedProjectSourceIds));
+ auto range = s->selectDirectoryInfosForSourceIdsStatement.range<Storage::Synchronization::DirectoryInfo>(
+ toIntegers(updatedDirectoryInfoSourceIds));
- auto insert = [&](const Storage::Synchronization::ProjectData &projectData) {
+ auto insert = [&](const Storage::Synchronization::DirectoryInfo &directoryInfo) {
using NanotraceHR::keyValue;
- NanotraceHR::Tracer tracer{"insert project data"_t,
+ NanotraceHR::Tracer tracer{"insert directory info"_t,
projectStorageCategory(),
- keyValue("project data", projectData)};
+ keyValue("directory info", directoryInfo)};
- if (!projectData.projectSourceId)
- throw ProjectDataHasInvalidProjectSourceId{};
- if (!projectData.sourceId)
- throw ProjectDataHasInvalidSourceId{};
+ if (!directoryInfo.directorySourceId)
+ throw DirectoryInfoHasInvalidProjectSourceId{};
+ if (!directoryInfo.sourceId)
+ throw DirectoryInfoHasInvalidSourceId{};
- s->insertProjectDataStatement.write(projectData.projectSourceId,
- projectData.sourceId,
- projectData.moduleId,
- projectData.fileType);
+ s->insertDirectoryInfoStatement.write(directoryInfo.directorySourceId,
+ directoryInfo.sourceId,
+ directoryInfo.moduleId,
+ directoryInfo.fileType);
};
- auto update = [&](const Storage::Synchronization::ProjectData &projectDataFromDatabase,
- const Storage::Synchronization::ProjectData &projectData) {
- if (projectDataFromDatabase.fileType != projectData.fileType
- || !compareInvalidAreTrue(projectDataFromDatabase.moduleId, projectData.moduleId)) {
+ auto update = [&](const Storage::Synchronization::DirectoryInfo &directoryInfoFromDatabase,
+ const Storage::Synchronization::DirectoryInfo &directoryInfo) {
+ if (directoryInfoFromDatabase.fileType != directoryInfo.fileType
+ || !compareInvalidAreTrue(directoryInfoFromDatabase.moduleId, directoryInfo.moduleId)) {
using NanotraceHR::keyValue;
- NanotraceHR::Tracer tracer{"update project data"_t,
+ NanotraceHR::Tracer tracer{"update directory info"_t,
projectStorageCategory(),
- keyValue("project data", projectData),
- keyValue("project data from database", projectDataFromDatabase)};
-
- s->updateProjectDataStatement.write(projectData.projectSourceId,
- projectData.sourceId,
- projectData.moduleId,
- projectData.fileType);
+ keyValue("directory info", directoryInfo),
+ keyValue("directory info from database",
+ directoryInfoFromDatabase)};
+
+ s->updateDirectoryInfoStatement.write(directoryInfo.directorySourceId,
+ directoryInfo.sourceId,
+ directoryInfo.moduleId,
+ directoryInfo.fileType);
return Sqlite::UpdateChange::Update;
}
return Sqlite::UpdateChange::No;
};
- auto remove = [&](const Storage::Synchronization::ProjectData &projectData) {
+ auto remove = [&](const Storage::Synchronization::DirectoryInfo &directoryInfo) {
using NanotraceHR::keyValue;
- NanotraceHR::Tracer tracer{"remove project data"_t,
+ NanotraceHR::Tracer tracer{"remove directory info"_t,
projectStorageCategory(),
- keyValue("project data", projectData)};
+ keyValue("directory info", directoryInfo)};
- s->deleteProjectDataStatement.write(projectData.projectSourceId, projectData.sourceId);
+ s->deleteDirectoryInfoStatement.write(directoryInfo.directorySourceId, directoryInfo.sourceId);
};
- Sqlite::insertUpdateDelete(range, projectDatas, compareKey, insert, update, remove);
+ Sqlite::insertUpdateDelete(range, directoryInfos, compareKey, insert, update, remove);
}
void ProjectStorage::synchronizeFileStatuses(FileStatuses &fileStatuses,
@@ -2564,19 +2724,29 @@ void ProjectStorage::synchronizeImports(Storage::Imports &imports,
Storage::Imports &moduleDependencies,
const SourceIds &updatedModuleDependencySourceIds,
Storage::Synchronization::ModuleExportedImports &moduleExportedImports,
- const ModuleIds &updatedModuleIds)
+ const ModuleIds &updatedModuleIds,
+ Prototypes &relinkablePrototypes,
+ Prototypes &relinkableExtensions)
{
NanotraceHR::Tracer tracer{"synchronize imports"_t, projectStorageCategory()};
synchromizeModuleExportedImports(moduleExportedImports, updatedModuleIds);
NanotraceHR::Tracer importTracer{"synchronize qml document imports"_t, projectStorageCategory()};
- synchronizeDocumentImports(imports, updatedSourceIds, Storage::Synchronization::ImportKind::Import);
+ synchronizeDocumentImports(imports,
+ updatedSourceIds,
+ Storage::Synchronization::ImportKind::Import,
+ Relink::No,
+ relinkablePrototypes,
+ relinkableExtensions);
importTracer.end();
NanotraceHR::Tracer moduleDependenciesTracer{"synchronize module depdencies"_t,
projectStorageCategory()};
synchronizeDocumentImports(moduleDependencies,
updatedModuleDependencySourceIds,
- Storage::Synchronization::ImportKind::ModuleDependency);
+ Storage::Synchronization::ImportKind::ModuleDependency,
+ Relink::Yes,
+ relinkablePrototypes,
+ relinkableExtensions);
moduleDependenciesTracer.end();
}
@@ -2650,38 +2820,41 @@ void ProjectStorage::synchromizeModuleExportedImports(
Sqlite::insertUpdateDelete(range, moduleExportedImports, compareKey, insert, update, remove);
}
-ModuleId ProjectStorage::fetchModuleIdUnguarded(Utils::SmallStringView name) const
+ModuleId ProjectStorage::fetchModuleIdUnguarded(Utils::SmallStringView name,
+ Storage::ModuleKind kind) const
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"fetch module id ungarded"_t,
projectStorageCategory(),
- keyValue("module name", name)};
+ keyValue("module name", name),
+ keyValue("module kind", kind)};
- auto moduleId = s->selectModuleIdByNameStatement.value<ModuleId>(name);
+ auto moduleId = s->selectModuleIdByNameStatement.value<ModuleId>(kind, name);
if (!moduleId)
- moduleId = s->insertModuleNameStatement.value<ModuleId>(name);
+ moduleId = s->insertModuleNameStatement.value<ModuleId>(kind, name);
tracer.end(keyValue("module id", moduleId));
return moduleId;
}
-Utils::PathString ProjectStorage::fetchModuleNameUnguarded(ModuleId id) const
+Storage::Module ProjectStorage::fetchModuleUnguarded(ModuleId id) const
{
using NanotraceHR::keyValue;
- NanotraceHR::Tracer tracer{"fetch module name ungarded"_t,
+ NanotraceHR::Tracer tracer{"fetch module ungarded"_t,
projectStorageCategory(),
keyValue("module id", id)};
- auto moduleName = s->selectModuleNameStatement.value<Utils::PathString>(id);
+ auto module = s->selectModuleStatement.value<Storage::Module>(id);
- if (moduleName.empty())
+ if (!module)
throw ModuleDoesNotExists{};
- tracer.end(keyValue("module name", moduleName));
+ tracer.end(keyValue("module name", module.name));
+ tracer.end(keyValue("module name", module.kind));
- return moduleName;
+ return module;
}
void ProjectStorage::handleAliasPropertyDeclarationsWithPropertyType(
@@ -2741,10 +2914,29 @@ void ProjectStorage::handlePrototypes(TypeId prototypeId, Prototypes &relinkable
keyValue("relinkable prototypes", relinkablePrototypes)};
auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) {
+ if (prototypeNameId)
+ relinkablePrototypes.emplace_back(typeId, prototypeNameId);
+ };
+
+ s->updatePrototypeIdToTypeIdStatement.readCallback(callback, prototypeId, unresolvedTypeId);
+}
+
+void ProjectStorage::handlePrototypesWithExportedTypeNameAndTypeId(
+ Utils::SmallStringView exportedTypeName, TypeId typeId, Prototypes &relinkablePrototypes)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"handle invalid prototypes"_t,
+ projectStorageCategory(),
+ keyValue("type id", exportedTypeName),
+ keyValue("relinkable prototypes", relinkablePrototypes)};
+
+ auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) {
relinkablePrototypes.emplace_back(typeId, prototypeNameId);
};
- s->updatePrototypeIdToNullStatement.readCallback(callback, prototypeId);
+ s->selectTypeIdAndPrototypeNameIdForPrototypeIdAndTypeNameStatement.readCallback(callback,
+ exportedTypeName,
+ typeId);
}
void ProjectStorage::handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions)
@@ -2756,10 +2948,27 @@ void ProjectStorage::handleExtensions(TypeId extensionId, Prototypes &relinkable
keyValue("relinkable extensions", relinkableExtensions)};
auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) {
+ if (extensionNameId)
+ relinkableExtensions.emplace_back(typeId, extensionNameId);
+ };
+
+ s->updateExtensionIdToTypeIdStatement.readCallback(callback, extensionId, unresolvedTypeId);
+}
+
+void ProjectStorage::handleExtensionsWithExportedTypeNameAndTypeId(
+ Utils::SmallStringView exportedTypeName, TypeId typeId, Prototypes &relinkableExtensions)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"handle invalid extensions"_t,
+ projectStorageCategory(),
+ keyValue("type id", exportedTypeName),
+ keyValue("relinkable extensions", relinkableExtensions)};
+
+ auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) {
relinkableExtensions.emplace_back(typeId, extensionNameId);
};
- s->updateExtensionIdToNullStatement.readCallback(callback, extensionId);
+ s->selectTypeIdForExtensionIdAndTypeNameStatement.readCallback(callback, exportedTypeName, typeId);
}
void ProjectStorage::deleteType(TypeId typeId,
@@ -2846,6 +3055,39 @@ void ProjectStorage::relinkPropertyDeclarations(PropertyDeclarations &relinkable
TypeCompare<PropertyDeclaration>{});
}
+template<typename Callable>
+void ProjectStorage::relinkPrototypes(Prototypes &relinkablePrototypes,
+ const TypeIds &deletedTypeIds,
+ Callable updateStatement)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"relink prototypes"_t,
+ projectStorageCategory(),
+ keyValue("relinkable prototypes", relinkablePrototypes),
+ keyValue("deleted type ids", deletedTypeIds)};
+
+ std::sort(relinkablePrototypes.begin(), relinkablePrototypes.end());
+ relinkablePrototypes.erase(std::unique(relinkablePrototypes.begin(), relinkablePrototypes.end()),
+ relinkablePrototypes.end());
+
+ Utils::set_greedy_difference(
+ relinkablePrototypes.cbegin(),
+ relinkablePrototypes.cend(),
+ deletedTypeIds.begin(),
+ deletedTypeIds.end(),
+ [&](const Prototype &prototype) {
+ TypeId prototypeId = fetchTypeId(prototype.prototypeNameId);
+
+ if (!prototypeId)
+ errorNotifier->typeNameCannotBeResolved(fetchImportedTypeName(prototype.prototypeNameId),
+ fetchTypeSourceId(prototype.typeId));
+
+ updateStatement(prototype.typeId, prototypeId);
+ checkForPrototypeChainCycle(prototype.typeId);
+ },
+ TypeCompare<Prototype>{});
+}
+
void ProjectStorage::deleteNotUpdatedTypes(const TypeIds &updatedTypeIds,
const SourceIds &updatedSourceIds,
const TypeIds &typeIdsToBeDeleted,
@@ -3060,6 +3302,9 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds,
} catch (const Sqlite::ConstraintPreventsModification &) {
throw QmlDesigner::ExportedTypeCannotBeInserted{type.name};
}
+
+ handlePrototypesWithExportedTypeNameAndTypeId(type.name, unresolvedTypeId, relinkablePrototypes);
+ handleExtensionsWithExportedTypeNameAndTypeId(type.name, unresolvedTypeId, relinkableExtensions);
};
auto update = [&](const Storage::Synchronization::ExportedTypeView &view,
@@ -3095,6 +3340,7 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds,
relinkableAliasPropertyDeclarations);
handlePrototypes(view.typeId, relinkablePrototypes);
handleExtensions(view.typeId, relinkableExtensions);
+
s->deleteExportedTypeNameStatement.write(view.exportedTypeNameId);
};
@@ -3410,11 +3656,90 @@ void ProjectStorage::resetRemovedAliasPropertyDeclarationsToNull(
PropertyCompare<AliasPropertyDeclaration>{});
}
+void ProjectStorage::handlePrototypesWithSourceIdAndPrototypeId(SourceId sourceId,
+ TypeId prototypeId,
+ Prototypes &relinkablePrototypes)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"handle prototypes with source id and prototype id"_t,
+ projectStorageCategory(),
+ keyValue("source id", sourceId),
+ keyValue("type id", prototypeId)};
+
+ auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) {
+ if (prototypeNameId)
+ relinkablePrototypes.emplace_back(typeId, prototypeNameId);
+ };
+
+ s->selectTypeIdAndPrototypeNameIdForPrototypeIdAndSourceIdStatement.readCallback(callback,
+ prototypeId,
+ sourceId);
+}
+
+void ProjectStorage::handlePrototypesAndExtensionsWithSourceId(SourceId sourceId,
+ TypeId prototypeId,
+ TypeId extensionId,
+ Prototypes &relinkablePrototypes,
+ Prototypes &relinkableExtensions)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"handle prototypes with source id"_t,
+ projectStorageCategory(),
+ keyValue("source id", sourceId),
+ keyValue("prototype id", prototypeId),
+ keyValue("extension id", extensionId)};
+
+ auto callback =
+ [&](TypeId typeId, ImportedTypeNameId prototypeNameId, ImportedTypeNameId extensionNameId) {
+ if (prototypeNameId)
+ relinkablePrototypes.emplace_back(typeId, prototypeNameId);
+ if (extensionNameId)
+ relinkableExtensions.emplace_back(typeId, extensionNameId);
+ };
+
+ s->updatePrototypeIdAndExtensionIdToTypeIdForSourceIdStatement.readCallback(callback,
+ sourceId,
+ prototypeId,
+ extensionId);
+}
+
+void ProjectStorage::handleExtensionsWithSourceIdAndExtensionId(SourceId sourceId,
+ TypeId extensionId,
+ Prototypes &relinkableExtensions)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"handle prototypes with source id and prototype id"_t,
+ projectStorageCategory(),
+ keyValue("source id", sourceId),
+ keyValue("type id", extensionId)};
+
+ auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) {
+ if (extensionNameId)
+ relinkableExtensions.emplace_back(typeId, extensionNameId);
+ };
+
+ s->selectTypeIdAndExtensionNameIdForExtensionIdAndSourceIdStatement.readCallback(callback,
+ extensionId,
+ sourceId);
+}
+
ImportId ProjectStorage::insertDocumentImport(const Storage::Import &import,
Storage::Synchronization::ImportKind importKind,
ModuleId sourceModuleId,
- ImportId parentImportId)
+ ImportId parentImportId,
+ Relink relink,
+ Prototypes &relinkablePrototypes,
+ Prototypes &relinkableExtensions)
{
+ if (relink == Relink::Yes) {
+ handlePrototypesWithSourceIdAndPrototypeId(import.sourceId,
+ unresolvedTypeId,
+ relinkablePrototypes);
+ handleExtensionsWithSourceIdAndExtensionId(import.sourceId,
+ unresolvedTypeId,
+ relinkableExtensions);
+ }
+
if (import.version.minor) {
return s->insertDocumentImportWithVersionStatement.value<ImportId>(import.sourceId,
import.moduleId,
@@ -3441,7 +3766,10 @@ ImportId ProjectStorage::insertDocumentImport(const Storage::Import &import,
void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports,
const SourceIds &updatedSourceIds,
- Storage::Synchronization::ImportKind importKind)
+ Storage::Synchronization::ImportKind importKind,
+ Relink relink,
+ Prototypes &relinkablePrototypes,
+ Prototypes &relinkableExtensions)
{
std::sort(imports.begin(), imports.end(), [](auto &&first, auto &&second) {
return std::tie(first.sourceId, first.moduleId, first.version)
@@ -3478,7 +3806,13 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports,
keyValue("source id", import.sourceId),
keyValue("module id", import.moduleId)};
- auto importId = insertDocumentImport(import, importKind, import.moduleId, ImportId{});
+ auto importId = insertDocumentImport(import,
+ importKind,
+ import.moduleId,
+ ImportId{},
+ relink,
+ relinkablePrototypes,
+ relinkableExtensions);
auto callback = [&](ModuleId exportedModuleId, int majorVersion, int minorVersion) {
Storage::Import additionImport{exportedModuleId,
Storage::Version{majorVersion, minorVersion},
@@ -3498,7 +3832,10 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports,
auto indirectImportId = insertDocumentImport(additionImport,
exportedImportKind,
import.moduleId,
- importId);
+ importId,
+ relink,
+ relinkablePrototypes,
+ relinkableExtensions);
tracer.end(keyValue("import id", indirectImportId));
};
@@ -3525,6 +3862,13 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports,
s->deleteDocumentImportStatement.write(view.importId);
s->deleteDocumentImportsWithParentImportIdStatement.write(view.sourceId, view.importId);
+ if (relink == Relink::Yes) {
+ handlePrototypesAndExtensionsWithSourceId(view.sourceId,
+ unresolvedTypeId,
+ unresolvedTypeId,
+ relinkablePrototypes,
+ relinkableExtensions);
+ }
};
Sqlite::insertUpdateDelete(range, imports, compareKey, insert, update, remove);
@@ -3550,7 +3894,7 @@ Utils::PathString ProjectStorage::createJson(const Storage::Synchronization::Par
json.append("\"}");
} else {
json.append(R"(","tr":)");
- json.append(Utils::SmallString::number(to_underlying(parameter.traits)));
+ json.append(Utils::SmallString::number(Utils::to_underlying(parameter.traits)));
json.append("}");
}
}
@@ -4075,25 +4419,29 @@ void ProjectStorage::checkForAliasChainCycle(PropertyDeclarationId propertyDecla
}
std::pair<TypeId, ImportedTypeNameId> ProjectStorage::fetchImportedTypeNameIdAndTypeId(
- const Storage::Synchronization::ImportedTypeName &typeName, SourceId sourceId)
+ const Storage::Synchronization::ImportedTypeName &importedTypeName, SourceId sourceId)
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"fetch imported type name id and type id"_t,
projectStorageCategory(),
- keyValue("imported type name", typeName),
+ keyValue("imported type name", importedTypeName),
keyValue("source id", sourceId)};
TypeId typeId;
ImportedTypeNameId typeNameId;
- if (!std::visit([](auto &&typeName_) -> bool { return typeName_.name.isEmpty(); }, typeName)) {
- typeNameId = fetchImportedTypeNameId(typeName, sourceId);
+ auto typeName = std::visit([](auto &&importedTypeName) { return importedTypeName.name; },
+ importedTypeName);
+ if (!typeName.empty()) {
+ typeNameId = fetchImportedTypeNameId(importedTypeName, sourceId);
typeId = fetchTypeId(typeNameId);
tracer.end(keyValue("type id", typeId), keyValue("type name id", typeNameId));
- if (!typeId)
- throw TypeNameDoesNotExists{fetchImportedTypeName(typeNameId), sourceId};
+ if (!typeId) {
+ errorNotifier->typeNameCannotBeResolved(typeName, sourceId);
+ return {unresolvedTypeId, typeNameId};
+ }
}
return {typeId, typeNameId};
@@ -4242,6 +4590,11 @@ Utils::SmallString ProjectStorage::fetchImportedTypeName(ImportedTypeNameId type
return s->selectNameFromImportedTypeNamesStatement.value<Utils::SmallString>(typeNameId);
}
+SourceId ProjectStorage::fetchTypeSourceId(TypeId typeId) const
+{
+ return s->selectSourceIdByTypeIdStatement.value<SourceId>(typeId);
+}
+
TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId,
Storage::Synchronization::TypeNameKind kind) const
{
@@ -4253,9 +4606,10 @@ TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId,
TypeId typeId;
if (kind == Storage::Synchronization::TypeNameKind::Exported) {
- typeId = s->selectTypeIdForImportedTypeNameNamesStatement.value<TypeId>(typeNameId);
+ typeId = s->selectTypeIdForImportedTypeNameNamesStatement.value<UnresolvedTypeId>(typeNameId);
} else {
- typeId = s->selectTypeIdForQualifiedImportedTypeNameNamesStatement.value<TypeId>(typeNameId);
+ typeId = s->selectTypeIdForQualifiedImportedTypeNameNamesStatement.value<UnresolvedTypeId>(
+ typeNameId);
}
tracer.end(keyValue("type id", typeId));
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h
index e7826f531b..54d9101596 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h
@@ -4,6 +4,7 @@
#pragma once
#include "commontypecache.h"
+#include "projectstorageerrornotifier.h"
#include "projectstorageexceptions.h"
#include "projectstorageinterface.h"
#include "projectstoragetypes.h"
@@ -38,21 +39,30 @@ class ProjectStorage final : public ProjectStorageInterface
using Database = Sqlite::Database;
friend Storage::Info::CommonTypeCache<ProjectStorageType>;
+ enum class Relink { No, Yes };
+
public:
- ProjectStorage(Database &database, bool isInitialized);
+ ProjectStorage(Database &database,
+ ProjectStorageErrorNotifierInterface &errorNotifier,
+ bool isInitialized);
~ProjectStorage();
void synchronize(Storage::Synchronization::SynchronizationPackage package) override;
void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override;
+ void setErrorNotifier(ProjectStorageErrorNotifierInterface &errorNotifier)
+ {
+ this->errorNotifier = &errorNotifier;
+ }
+
void addObserver(ProjectStorageObserver *observer) override;
void removeObserver(ProjectStorageObserver *observer) override;
- ModuleId moduleId(Utils::SmallStringView moduleName) const override;
+ ModuleId moduleId(Utils::SmallStringView moduleName, Storage::ModuleKind kind) const override;
- Utils::SmallString moduleName(ModuleId moduleId) const override;
+ Storage::Module module(ModuleId moduleId) const override;
TypeId typeId(ModuleId moduleId,
Utils::SmallStringView exportedTypeName,
@@ -116,7 +126,7 @@ public:
return commonTypeCache_;
}
- template<const char *moduleName, const char *typeName>
+ template<const char *moduleName, const char *typeName, Storage::ModuleKind moduleKind = Storage::ModuleKind::QmlLibrary>
TypeId commonTypeId() const
{
using NanotraceHR::keyValue;
@@ -125,7 +135,7 @@ public:
keyValue("module name", std::string_view{moduleName}),
keyValue("type name", std::string_view{typeName})};
- auto typeId = commonTypeCache_.typeId<moduleName, typeName>();
+ auto typeId = commonTypeCache_.typeId<moduleName, typeName, moduleKind>();
tracer.end(keyValue("type id", typeId));
@@ -229,11 +239,13 @@ public:
FileStatus fetchFileStatus(SourceId sourceId) const override;
- std::optional<Storage::Synchronization::ProjectData> fetchProjectData(SourceId sourceId) const override;
+ std::optional<Storage::Synchronization::DirectoryInfo> fetchDirectoryInfo(SourceId sourceId) const override;
- Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId projectSourceId) const override;
-
- Storage::Synchronization::ProjectDatas fetchProjectDatas(const SourceIds &projectSourceIds) const;
+ Storage::Synchronization::DirectoryInfos fetchDirectoryInfos(SourceId directorySourceId) const override;
+ Storage::Synchronization::DirectoryInfos fetchDirectoryInfos(
+ SourceId directorySourceId, Storage::Synchronization::FileType fileType) const override;
+ Storage::Synchronization::DirectoryInfos fetchDirectoryInfos(const SourceIds &directorySourceIds) const;
+ SmallSourceIds<32> fetchSubdirectorySourceIds(SourceId directorySourceId) const override;
void setPropertyEditorPathId(TypeId typeId, SourceId pathId);
@@ -244,50 +256,90 @@ public:
void resetForTestsOnly();
private:
+ struct ModuleView
+ {
+ ModuleView() = default;
+
+ ModuleView(Utils::SmallStringView name, Storage::ModuleKind kind)
+ : name{name}
+ , kind{kind}
+ {}
+
+ ModuleView(const Storage::Module &module)
+ : name{module.name}
+ , kind{module.kind}
+ {}
+
+ Utils::SmallStringView name;
+ Storage::ModuleKind kind;
+
+ friend bool operator<(ModuleView first, ModuleView second)
+ {
+ return std::tie(first.kind, first.name) < std::tie(second.kind, second.name);
+ }
+
+ friend bool operator==(const Storage::Module &first, ModuleView second)
+ {
+ return first.name == second.name && first.kind == second.kind;
+ }
+
+ friend bool operator==(ModuleView first, const Storage::Module &second)
+ {
+ return second == first;
+ }
+ };
+
class ModuleStorageAdapter
{
public:
- auto fetchId(const Utils::SmallStringView name) { return storage.fetchModuleId(name); }
+ auto fetchId(ModuleView module) { return storage.fetchModuleId(module.name, module.kind); }
- auto fetchValue(ModuleId id) { return storage.fetchModuleName(id); }
+ auto fetchValue(ModuleId id) { return storage.fetchModule(id); }
auto fetchAll() { return storage.fetchAllModules(); }
ProjectStorage &storage;
};
- class Module : public StorageCacheEntry<Utils::PathString, Utils::SmallStringView, ModuleId>
+ friend ModuleStorageAdapter;
+
+ static bool moduleNameLess(ModuleView first, ModuleView second) noexcept
{
- using Base = StorageCacheEntry<Utils::PathString, Utils::SmallStringView, ModuleId>;
+ return first < second;
+ }
+
+ class ModuleCacheEntry : public StorageCacheEntry<Storage::Module, ModuleView, ModuleId>
+ {
+ using Base = StorageCacheEntry<Storage::Module, ModuleView, ModuleId>;
public:
using Base::Base;
- friend bool operator==(const Module &first, const Module &second)
+ ModuleCacheEntry(Utils::SmallStringView name, Storage::ModuleKind kind, ModuleId moduleId)
+ : Base{{name, kind}, moduleId}
+ {}
+
+ friend bool operator==(const ModuleCacheEntry &first, const ModuleCacheEntry &second)
{
return &first == &second && first.value == second.value;
}
- };
-
- using Modules = std::vector<Module>;
- friend ModuleStorageAdapter;
+ friend bool operator==(const ModuleCacheEntry &first, ModuleView second)
+ {
+ return first.value.name == second.name && first.value.kind == second.kind;
+ }
+ };
- static bool moduleNameLess(Utils::SmallStringView first, Utils::SmallStringView second) noexcept;
+ using ModuleCacheEntries = std::vector<ModuleCacheEntry>;
- using ModuleCache = StorageCache<Utils::PathString,
- Utils::SmallStringView,
- ModuleId,
- ModuleStorageAdapter,
- NonLockingMutex,
- moduleNameLess,
- Module>;
+ using ModuleCache
+ = StorageCache<Storage::Module, ModuleView, ModuleId, ModuleStorageAdapter, NonLockingMutex, moduleNameLess, ModuleCacheEntry>;
- ModuleId fetchModuleId(Utils::SmallStringView moduleName);
+ ModuleId fetchModuleId(Utils::SmallStringView moduleName, Storage::ModuleKind moduleKind);
- Utils::PathString fetchModuleName(ModuleId id);
+ Storage::Module fetchModule(ModuleId id);
- Modules fetchAllModules() const;
+ ModuleCacheEntries fetchAllModules() const;
void callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds);
@@ -398,6 +450,11 @@ private:
return first.typeId < second.typeId;
}
+ friend bool operator==(Prototype first, Prototype second)
+ {
+ return first.typeId == second.typeId;
+ }
+
template<typename String>
friend void convertToString(String &string, const Prototype &prototype)
{
@@ -461,10 +518,12 @@ private:
{
public:
TypeAnnotationView(TypeId typeId,
+ Utils::SmallStringView typeName,
Utils::SmallStringView iconPath,
Utils::SmallStringView itemLibraryJson,
Utils::SmallStringView hintsJson)
: typeId{typeId}
+ , typeName{typeName}
, iconPath{iconPath}
, itemLibraryJson{itemLibraryJson}
, hintsJson{hintsJson}
@@ -476,6 +535,7 @@ private:
using NanotraceHR::dictonary;
using NanotraceHR::keyValue;
auto dict = dictonary(keyValue("type id", typeAnnotationView.typeId),
+ keyValue("type name", typeAnnotationView.typeName),
keyValue("icon path", typeAnnotationView.iconPath),
keyValue("item library json", typeAnnotationView.itemLibraryJson),
keyValue("hints json", typeAnnotationView.hintsJson));
@@ -485,6 +545,7 @@ private:
public:
TypeId typeId;
+ Utils::SmallStringView typeName;
Utils::SmallStringView iconPath;
Utils::SmallStringView itemLibraryJson;
Utils::PathString hintsJson;
@@ -516,8 +577,8 @@ private:
Prototypes &relinkableExtensions,
const SourceIds &updatedSourceIds);
- void synchronizeProjectDatas(Storage::Synchronization::ProjectDatas &projectDatas,
- const SourceIds &updatedProjectSourceIds);
+ void synchronizeDirectoryInfos(Storage::Synchronization::DirectoryInfos &directoryInfos,
+ const SourceIds &updatedDirectoryInfoSourceIds);
void synchronizeFileStatuses(FileStatuses &fileStatuses, const SourceIds &updatedSourceIds);
@@ -526,15 +587,18 @@ private:
Storage::Imports &moduleDependencies,
const SourceIds &updatedModuleDependencySourceIds,
Storage::Synchronization::ModuleExportedImports &moduleExportedImports,
- const ModuleIds &updatedModuleIds);
+ const ModuleIds &updatedModuleIds,
+ Prototypes &relinkablePrototypes,
+ Prototypes &relinkableExtensions);
void synchromizeModuleExportedImports(
Storage::Synchronization::ModuleExportedImports &moduleExportedImports,
const ModuleIds &updatedModuleIds);
- ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const override;
+ ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name,
+ Storage::ModuleKind moduleKind) const override;
- Utils::PathString fetchModuleNameUnguarded(ModuleId id) const;
+ Storage::Module fetchModuleUnguarded(ModuleId id) const;
void handleAliasPropertyDeclarationsWithPropertyType(
TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations);
@@ -543,9 +607,14 @@ private:
PropertyDeclarations &relinkablePropertyDeclarations);
void handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes);
+ void handlePrototypesWithExportedTypeNameAndTypeId(Utils::SmallStringView exportedTypeName,
+ TypeId typeId,
+ Prototypes &relinkablePrototypes);
void handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions);
-
+ void handleExtensionsWithExportedTypeNameAndTypeId(Utils::SmallStringView exportedTypeName,
+ TypeId typeId,
+ Prototypes &relinkableExtensions);
void deleteType(TypeId typeId,
AliasPropertyDeclarations &relinkableAliasPropertyDeclarations,
PropertyDeclarations &relinkablePropertyDeclarations,
@@ -561,32 +630,7 @@ private:
template<typename Callable>
void relinkPrototypes(Prototypes &relinkablePrototypes,
const TypeIds &deletedTypeIds,
- Callable updateStatement)
- {
- using NanotraceHR::keyValue;
- NanotraceHR::Tracer tracer{"relink prototypes"_t,
- projectStorageCategory(),
- keyValue("relinkable prototypes", relinkablePrototypes),
- keyValue("deleted type ids", deletedTypeIds)};
-
- std::sort(relinkablePrototypes.begin(), relinkablePrototypes.end());
-
- Utils::set_greedy_difference(
- relinkablePrototypes.cbegin(),
- relinkablePrototypes.cend(),
- deletedTypeIds.begin(),
- deletedTypeIds.end(),
- [&](const Prototype &prototype) {
- TypeId prototypeId = fetchTypeId(prototype.prototypeNameId);
-
- if (!prototypeId)
- throw TypeNameDoesNotExists{fetchImportedTypeName(prototype.prototypeNameId)};
-
- updateStatement(prototype.typeId, prototypeId);
- checkForPrototypeChainCycle(prototype.typeId);
- },
- TypeCompare<Prototype>{});
- }
+ Callable updateStatement);
void deleteNotUpdatedTypes(const TypeIds &updatedTypeIds,
const SourceIds &updatedSourceIds,
@@ -701,14 +745,32 @@ private:
Storage::Synchronization::Types &types,
AliasPropertyDeclarations &relinkableAliasPropertyDeclarations);
+ void handlePrototypesWithSourceIdAndPrototypeId(SourceId sourceId,
+ TypeId prototypeId,
+ Prototypes &relinkablePrototypes);
+ void handlePrototypesAndExtensionsWithSourceId(SourceId sourceId,
+ TypeId prototypeId,
+ TypeId extensionId,
+ Prototypes &relinkablePrototypes,
+ Prototypes &relinkableExtensions);
+ void handleExtensionsWithSourceIdAndExtensionId(SourceId sourceId,
+ TypeId extensionId,
+ Prototypes &relinkableExtensions);
+
ImportId insertDocumentImport(const Storage::Import &import,
Storage::Synchronization::ImportKind importKind,
ModuleId sourceModuleId,
- ImportId parentImportId);
+ ImportId parentImportId,
+ Relink forceRelink,
+ Prototypes &relinkablePrototypes,
+ Prototypes &relinkableExtensions);
void synchronizeDocumentImports(Storage::Imports &imports,
const SourceIds &updatedSourceIds,
- Storage::Synchronization::ImportKind importKind);
+ Storage::Synchronization::ImportKind importKind,
+ Relink forceRelink,
+ Prototypes &relinkablePrototypes,
+ Prototypes &relinkableExtensions);
static Utils::PathString createJson(const Storage::Synchronization::ParameterDeclarations &parameters);
@@ -854,6 +916,7 @@ private:
TypeId fetchTypeId(ImportedTypeNameId typeNameId) const;
Utils::SmallString fetchImportedTypeName(ImportedTypeNameId typeNameId) const;
+ SourceId fetchTypeSourceId(TypeId typeId) const;
TypeId fetchTypeId(ImportedTypeNameId typeNameId,
Storage::Synchronization::TypeNameKind kind) const;
@@ -920,6 +983,7 @@ private:
public:
Database &database;
+ ProjectStorageErrorNotifierInterface *errorNotifier = nullptr; // cannot be null
Sqlite::ExclusiveNonThrowingDestructorTransaction<Database> exclusiveTransaction;
std::unique_ptr<Initializer> initializer;
mutable ModuleCache moduleCache{ModuleStorageAdapter{*this}};
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.cpp
new file mode 100644
index 0000000000..a4705f5eec
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "projectstorageerrornotifier.h"
+
+#include "sourcepathcache.h"
+
+namespace QmlDesigner {
+
+void ProjectStorageErrorNotifier::typeNameCannotBeResolved(Utils::SmallStringView typeName,
+ SourceId sourceId)
+{
+ qDebug() << "Missing type name: " << typeName
+ << " in file: " << m_pathCache.sourcePath(sourceId).toStringView();
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.h
new file mode 100644
index 0000000000..2695e93019
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.h
@@ -0,0 +1,25 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "projectstorageerrornotifierinterface.h"
+
+#include <modelfwd.h>
+
+namespace QmlDesigner {
+
+class ProjectStorageErrorNotifier final : public ProjectStorageErrorNotifierInterface
+{
+public:
+ ProjectStorageErrorNotifier(PathCacheType &pathCache)
+ : m_pathCache{pathCache}
+ {}
+
+ void typeNameCannotBeResolved(Utils::SmallStringView typeName, SourceId souceId) override;
+
+private:
+ PathCacheType &m_pathCache;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifierinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifierinterface.h
new file mode 100644
index 0000000000..8136c9d599
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifierinterface.h
@@ -0,0 +1,27 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "projectstorageids.h"
+
+#include <utils/smallstringview.h>
+
+namespace QmlDesigner {
+
+class ProjectStorageErrorNotifierInterface
+{
+public:
+ ProjectStorageErrorNotifierInterface() = default;
+ ProjectStorageErrorNotifierInterface(ProjectStorageErrorNotifierInterface &&) = default;
+ ProjectStorageErrorNotifierInterface &operator=(ProjectStorageErrorNotifierInterface &&) = default;
+ ProjectStorageErrorNotifierInterface(const ProjectStorageErrorNotifierInterface &) = delete;
+ ProjectStorageErrorNotifierInterface &operator=(const ProjectStorageErrorNotifierInterface &) = delete;
+
+ virtual void typeNameCannotBeResolved(Utils::SmallStringView typeName, SourceId souceId) = 0;
+
+protected:
+ ~ProjectStorageErrorNotifierInterface() = default;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp
index a5dc60c4fa..a86b78a785 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp
@@ -148,32 +148,32 @@ const char *CannotParseQmlDocumentFile::what() const noexcept
return "Cannot parse qml types file!";
}
-ProjectDataHasInvalidProjectSourceId::ProjectDataHasInvalidProjectSourceId()
+DirectoryInfoHasInvalidProjectSourceId::DirectoryInfoHasInvalidProjectSourceId()
{
- category().threadEvent("ProjectDataHasInvalidProjectSourceId"_t);
+ category().threadEvent("DirectoryInfoHasInvalidProjectSourceId"_t);
}
-const char *ProjectDataHasInvalidProjectSourceId::what() const noexcept
+const char *DirectoryInfoHasInvalidProjectSourceId::what() const noexcept
{
return "The project source id is invalid!";
}
-ProjectDataHasInvalidSourceId::ProjectDataHasInvalidSourceId()
+DirectoryInfoHasInvalidSourceId::DirectoryInfoHasInvalidSourceId()
{
- category().threadEvent("ProjectDataHasInvalidSourceId"_t);
+ category().threadEvent("DirectoryInfoHasInvalidSourceId"_t);
}
-const char *ProjectDataHasInvalidSourceId::what() const noexcept
+const char *DirectoryInfoHasInvalidSourceId::what() const noexcept
{
return "The source id is invalid!";
}
-ProjectDataHasInvalidModuleId::ProjectDataHasInvalidModuleId()
+DirectoryInfoHasInvalidModuleId::DirectoryInfoHasInvalidModuleId()
{
- category().threadEvent("ProjectDataHasInvalidModuleId"_t);
+ category().threadEvent("DirectoryInfoHasInvalidModuleId"_t);
}
-const char *ProjectDataHasInvalidModuleId::what() const noexcept
+const char *DirectoryInfoHasInvalidModuleId::what() const noexcept
{
return "The module id is invalid!";
}
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h
index d85f1f7f9e..f4f78f714b 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h
@@ -130,24 +130,24 @@ public:
const char *what() const noexcept override;
};
-class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidProjectSourceId : public ProjectStorageError
+class QMLDESIGNERCORE_EXPORT DirectoryInfoHasInvalidProjectSourceId : public ProjectStorageError
{
public:
- ProjectDataHasInvalidProjectSourceId();
+ DirectoryInfoHasInvalidProjectSourceId();
const char *what() const noexcept override;
};
-class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidSourceId : public ProjectStorageError
+class QMLDESIGNERCORE_EXPORT DirectoryInfoHasInvalidSourceId : public ProjectStorageError
{
public:
- ProjectDataHasInvalidSourceId();
+ DirectoryInfoHasInvalidSourceId();
const char *what() const noexcept override;
};
-class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidModuleId : public ProjectStorageError
+class QMLDESIGNERCORE_EXPORT DirectoryInfoHasInvalidModuleId : public ProjectStorageError
{
public:
- ProjectDataHasInvalidModuleId();
+ DirectoryInfoHasInvalidModuleId();
const char *what() const noexcept override;
};
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h
index 9f0c134ed3..1d630344ae 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h
@@ -7,6 +7,9 @@
#include <sqlite/sqlitevalue.h>
#include <utils/smallstring.h>
+#include <utils/utility.h>
+
+#include <QVarLengthArray>
#include <array>
#include <tuple>
@@ -15,12 +18,8 @@
namespace QmlDesigner {
-template<typename Enumeration>
-constexpr std::underlying_type_t<Enumeration> to_underlying(Enumeration enumeration) noexcept
-{
- static_assert(std::is_enum_v<Enumeration>, "to_underlying expect an enumeration");
- return static_cast<std::underlying_type_t<Enumeration>>(enumeration);
-}
+template<std::size_t size>
+using SmallPathStrings = QVarLengthArray<Utils::PathString, size>;
enum class FlagIs : unsigned int { False, Set, True };
@@ -42,6 +41,34 @@ void convertToString(String &string, const FlagIs &flagIs)
namespace QmlDesigner::Storage {
+enum class ModuleKind { QmlLibrary, CppLibrary, PathLibrary };
+
+struct Module
+{
+ Module() = default;
+
+ Module(Utils::SmallStringView name, Storage::ModuleKind kind)
+ : name{name}
+ , kind{kind}
+ {}
+
+ template<typename ModuleType>
+ Module(const ModuleType &module)
+ : name{module.name}
+ , kind{module.kind}
+ {}
+
+ Utils::PathString name;
+ Storage::ModuleKind kind = Storage::ModuleKind::QmlLibrary;
+
+ friend bool operator==(const Module &first, const Module &second)
+ {
+ return first.name == second.name && first.kind == second.kind;
+ }
+
+ explicit operator bool() const { return name.size(); }
+};
+
enum class PropertyDeclarationTraits : int {
None = 0,
IsReadOnly = 1 << 0,
@@ -352,6 +379,7 @@ using ToolTipString = Utils::BasicSmallString<94>;
struct ItemLibraryEntry
{
ItemLibraryEntry(TypeId typeId,
+ Utils::SmallStringView typeName,
Utils::SmallStringView name,
Utils::SmallStringView iconPath,
Utils::SmallStringView category,
@@ -359,6 +387,7 @@ struct ItemLibraryEntry
Utils::SmallStringView toolTip,
Utils::SmallStringView templatePath)
: typeId{typeId}
+ , typeName{typeName}
, name{name}
, iconPath{iconPath}
, category{category}
@@ -368,6 +397,7 @@ struct ItemLibraryEntry
{}
ItemLibraryEntry(TypeId typeId,
+ Utils::SmallStringView typeName,
Utils::SmallStringView name,
Utils::SmallStringView iconPath,
Utils::SmallStringView category,
@@ -375,6 +405,7 @@ struct ItemLibraryEntry
Utils::SmallStringView toolTip,
ItemLibraryProperties properties)
: typeId{typeId}
+ , typeName{typeName}
, name{name}
, iconPath{iconPath}
, category{category}
@@ -389,6 +420,7 @@ struct ItemLibraryEntry
using NanotraceHR::dictonary;
using NanotraceHR::keyValue;
auto dict = dictonary(keyValue("type id", entry.typeId),
+ keyValue("type name", entry.typeName),
keyValue("name", entry.name),
keyValue("icon path", entry.iconPath),
keyValue("category", entry.category),
@@ -402,6 +434,7 @@ struct ItemLibraryEntry
}
TypeId typeId;
+ Utils::SmallString typeName;
Utils::SmallString name;
Utils::PathString iconPath;
Utils::SmallString category;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h
index 971e635517..4d840d2a5c 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h
@@ -31,8 +31,8 @@ public:
virtual void addObserver(ProjectStorageObserver *observer) = 0;
virtual void removeObserver(ProjectStorageObserver *observer) = 0;
- virtual ModuleId moduleId(::Utils::SmallStringView name) const = 0;
- virtual Utils::SmallString moduleName(ModuleId moduleId) const = 0;
+ virtual ModuleId moduleId(::Utils::SmallStringView name, Storage::ModuleKind kind) const = 0;
+ virtual QmlDesigner::Storage::Module module(ModuleId moduleId) const = 0;
virtual std::optional<Storage::Info::PropertyDeclaration>
propertyDeclaration(PropertyDeclarationId propertyDeclarationId) const = 0;
virtual TypeId typeId(ModuleId moduleId,
@@ -80,16 +80,20 @@ public:
virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId, TypeId, TypeId, TypeId, TypeId) const = 0;
virtual FileStatus fetchFileStatus(SourceId sourceId) const = 0;
- virtual Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId sourceId) const = 0;
- virtual std::optional<Storage::Synchronization::ProjectData> fetchProjectData(SourceId sourceId) const = 0;
+ virtual Storage::Synchronization::DirectoryInfos fetchDirectoryInfos(SourceId sourceId) const = 0;
+ virtual Storage::Synchronization::DirectoryInfos fetchDirectoryInfos(
+ SourceId directorySourceId, Storage::Synchronization::FileType) const
+ = 0;
+ virtual std::optional<Storage::Synchronization::DirectoryInfo> fetchDirectoryInfo(SourceId sourceId) const = 0;
+ virtual SmallSourceIds<32> fetchSubdirectorySourceIds(SourceId directorySourceId) const = 0;
virtual SourceId propertyEditorPathId(TypeId typeId) const = 0;
virtual const Storage::Info::CommonTypeCache<ProjectStorageType> &commonTypeCache() const = 0;
- template<const char *moduleName, const char *typeName>
+ template<const char *moduleName, const char *typeName, Storage::ModuleKind moduleKind = Storage::ModuleKind::QmlLibrary>
TypeId commonTypeId() const
{
- return commonTypeCache().template typeId<moduleName, typeName>();
+ return commonTypeCache().template typeId<moduleName, typeName, moduleKind>();
}
template<typename BuiltinType>
@@ -108,7 +112,7 @@ protected:
ProjectStorageInterface() = default;
~ProjectStorageInterface() = default;
- virtual ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const = 0;
+ virtual ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name, Storage::ModuleKind moduleKind) const = 0;
virtual TypeId fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId, Utils::SmallStringView name) const = 0;
};
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h
index 8d810d94bd..1592628af5 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h
@@ -10,6 +10,7 @@
#include <nanotrace/nanotracehr.h>
#include <sqlite/sqlitevalue.h>
#include <utils/smallstring.h>
+#include <utils/utility.h>
#include <tuple>
#include <variant>
@@ -82,7 +83,7 @@ void convertToString(String &string, const TypeNameKind &kind)
}
}
-enum class FileType : char { QmlTypes, QmlDocument };
+enum class FileType : char { QmlTypes, QmlDocument, Directory };
template<typename String>
void convertToString(String &string, const FileType &type)
@@ -94,6 +95,9 @@ void convertToString(String &string, const FileType &type)
case FileType::QmlDocument:
convertToString(string, "QmlDocument");
break;
+ case FileType::Directory:
+ convertToString(string, "Directory");
+ break;
}
}
@@ -204,7 +208,7 @@ void convertToString(String &string, const IsAutoVersion &isAutoVersion)
constexpr bool operator<(IsAutoVersion first, IsAutoVersion second)
{
- return to_underlying(first) < to_underlying(second);
+ return Utils::to_underlying(first) < Utils::to_underlying(second);
}
class ModuleExportedImport
@@ -1160,44 +1164,44 @@ public:
using PropertyEditorQmlPaths = std::vector<class PropertyEditorQmlPath>;
-class ProjectData
+class DirectoryInfo
{
public:
- ProjectData(SourceId projectSourceId, SourceId sourceId, ModuleId moduleId, FileType fileType)
- : projectSourceId{projectSourceId}
+ DirectoryInfo(SourceId directorySourceId, SourceId sourceId, ModuleId moduleId, FileType fileType)
+ : directorySourceId{directorySourceId}
, sourceId{sourceId}
, moduleId{moduleId}
, fileType{fileType}
{}
- friend bool operator==(const ProjectData &first, const ProjectData &second)
+ friend bool operator==(const DirectoryInfo &first, const DirectoryInfo &second)
{
- return first.projectSourceId == second.projectSourceId && first.sourceId == second.sourceId
+ return first.directorySourceId == second.directorySourceId && first.sourceId == second.sourceId
&& first.moduleId.internalId() == second.moduleId.internalId()
&& first.fileType == second.fileType;
}
template<typename String>
- friend void convertToString(String &string, const ProjectData &projectData)
+ friend void convertToString(String &string, const DirectoryInfo &directoryInfo)
{
using NanotraceHR::dictonary;
using NanotraceHR::keyValue;
- auto dict = dictonary(keyValue("project source id", projectData.projectSourceId),
- keyValue("source id", projectData.sourceId),
- keyValue("module id", projectData.moduleId),
- keyValue("file type", projectData.fileType));
+ auto dict = dictonary(keyValue("project source id", directoryInfo.directorySourceId),
+ keyValue("source id", directoryInfo.sourceId),
+ keyValue("module id", directoryInfo.moduleId),
+ keyValue("file type", directoryInfo.fileType));
convertToString(string, dict);
}
public:
- SourceId projectSourceId;
+ SourceId directorySourceId;
SourceId sourceId;
ModuleId moduleId;
FileType fileType;
};
-using ProjectDatas = std::vector<ProjectData>;
+using DirectoryInfos = std::vector<DirectoryInfo>;
class TypeAnnotation
{
@@ -1291,9 +1295,9 @@ public:
, fileStatuses(std::move(fileStatuses))
{}
- SynchronizationPackage(SourceIds updatedProjectSourceIds, ProjectDatas projectDatas)
- : projectDatas(std::move(projectDatas))
- , updatedProjectSourceIds(std::move(updatedProjectSourceIds))
+ SynchronizationPackage(SourceIds updatedDirectoryInfoSourceIds, DirectoryInfos directoryInfos)
+ : directoryInfos(std::move(directoryInfos))
+ , updatedDirectoryInfoSourceIds(std::move(updatedDirectoryInfoSourceIds))
{}
public:
@@ -1302,8 +1306,8 @@ public:
SourceIds updatedSourceIds;
SourceIds updatedFileStatusSourceIds;
FileStatuses fileStatuses;
- ProjectDatas projectDatas;
- SourceIds updatedProjectSourceIds;
+ DirectoryInfos directoryInfos;
+ SourceIds updatedDirectoryInfoSourceIds;
Imports moduleDependencies;
SourceIds updatedModuleDependencySourceIds;
ModuleExportedImports moduleExportedImports;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp
index 761d6371ef..a0e7bba3c5 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp
@@ -13,9 +13,9 @@
#include "sourcepathcache.h"
#include "typeannotationreader.h"
-#include <tracing/qmldesignertracing.h>
-
#include <sqlitedatabase.h>
+#include <tracing/qmldesignertracing.h>
+#include <utils/set_algorithm.h>
#include <QDirIterator>
#include <QRegularExpression>
@@ -102,6 +102,8 @@ ProjectStorageUpdater::Components createComponents(
}
for (const QmlDirParser::Component &qmlDirParserComponent : qmlDirParserComponents) {
+ if (qmlDirParserComponent.fileName.contains('/'))
+ continue;
components.push_back(ProjectStorageUpdater::Component{qmlDirParserComponent.fileName,
qmlDirParserComponent.typeName,
moduleId,
@@ -134,13 +136,13 @@ SourceIds filterNotUpdatedSourceIds(SourceIds updatedSourceIds, SourceIds notUpd
}
void addSourceIds(SourceIds &sourceIds,
- const Storage::Synchronization::ProjectDatas &projectDatas,
+ const Storage::Synchronization::DirectoryInfos &directoryInfos,
TracerLiteral message,
Tracer &tracer)
{
- for (const auto &projectData : projectDatas) {
- tracer.tick(message, keyValue("source id", projectData.sourceId));
- sourceIds.push_back(projectData.sourceId);
+ for (const auto &directoryInfo : directoryInfos) {
+ tracer.tick(message, keyValue("source id", directoryInfo.sourceId));
+ sourceIds.push_back(directoryInfo.sourceId);
}
}
@@ -164,8 +166,8 @@ void addDependencies(Storage::Imports &dependencies,
Tracer &tracer)
{
for (const QmlDirParser::Import &qmldirDependency : qmldirDependencies) {
- ModuleId moduleId = projectStorage.moduleId(Utils::PathString{qmldirDependency.module}
- + "-cppnative");
+ ModuleId moduleId = projectStorage.moduleId(Utils::PathString{qmldirDependency.module},
+ Storage::ModuleKind::CppLibrary);
auto &import = dependencies.emplace_back(moduleId, Storage::Version{}, sourceId);
tracer.tick(message, keyValue("import", import));
}
@@ -177,6 +179,7 @@ void addModuleExportedImport(Storage::Synchronization::ModuleExportedImports &im
Storage::Version version,
Storage::Synchronization::IsAutoVersion isAutoVersion,
std::string_view moduleName,
+ Storage::ModuleKind moduleKind,
std::string_view exportedModuleName)
{
NanotraceHR::Tracer tracer{"add module exported imports"_t,
@@ -186,16 +189,21 @@ void addModuleExportedImport(Storage::Synchronization::ModuleExportedImports &im
keyValue("version", version),
keyValue("is auto version", isAutoVersion),
keyValue("module name", moduleName),
+ keyValue("module kind", moduleKind),
keyValue("exported module name", exportedModuleName)};
imports.emplace_back(moduleId, exportedModuleId, version, isAutoVersion);
}
+bool isOptionalImport(QmlDirParser::Import::Flags flags)
+{
+ return flags & QmlDirParser::Import::Optional && !(flags & QmlDirParser::Import::OptionalDefault);
+}
+
void addModuleExportedImports(Storage::Synchronization::ModuleExportedImports &imports,
ModuleId moduleId,
ModuleId cppModuleId,
std::string_view moduleName,
- std::string_view cppModuleName,
const QList<QmlDirParser::Import> &qmldirImports,
ProjectStorageInterface &projectStorage)
{
@@ -205,24 +213,31 @@ void addModuleExportedImports(Storage::Synchronization::ModuleExportedImports &i
keyValue("module id", moduleId)};
for (const QmlDirParser::Import &qmldirImport : qmldirImports) {
+ if (isOptionalImport(qmldirImport.flags))
+ continue;
+
Utils::PathString exportedModuleName{qmldirImport.module};
- ModuleId exportedModuleId = projectStorage.moduleId(exportedModuleName);
+ using Storage::ModuleKind;
+ ModuleId exportedModuleId = projectStorage.moduleId(exportedModuleName,
+ ModuleKind::QmlLibrary);
addModuleExportedImport(imports,
moduleId,
exportedModuleId,
convertVersion(qmldirImport.version),
convertToIsAutoVersion(qmldirImport.flags),
moduleName,
+ ModuleKind::QmlLibrary,
exportedModuleName);
- exportedModuleName += "-cppnative";
- ModuleId exportedCppModuleId = projectStorage.moduleId(exportedModuleName);
+ ModuleId exportedCppModuleId = projectStorage.moduleId(exportedModuleName,
+ ModuleKind::CppLibrary);
addModuleExportedImport(imports,
cppModuleId,
exportedCppModuleId,
Storage::Version{},
Storage::Synchronization::IsAutoVersion::No,
- cppModuleName,
+ moduleName,
+ ModuleKind::CppLibrary,
exportedModuleName);
}
}
@@ -272,6 +287,8 @@ void ProjectStorageUpdater::update(QStringList directories,
try {
m_projectStorage.synchronize(std::move(package));
+ } catch (const TypeNameDoesNotExists &exception) {
+ qDebug() << "missing type: " << exception.what();
} catch (...) {
qWarning() << "Project storage could not been updated!";
}
@@ -289,7 +306,7 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths,
NanotraceHR::Tracer tracer{"update qmltypes file"_t, category()};
- ModuleId moduleId = m_projectStorage.moduleId("QML-cppnative");
+ ModuleId moduleId = m_projectStorage.moduleId("QML", Storage::ModuleKind::CppLibrary);
for (const QString &qmlTypesPath : qmlTypesPaths) {
SourceId sourceId = m_pathCache.sourceId(SourcePath{qmlTypesPath});
@@ -298,21 +315,19 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths,
keyValue("source id", sourceId),
keyValue("qml types path", qmlTypesPath));
- Storage::Synchronization::ProjectData projectData{sourceId,
- sourceId,
- moduleId,
- Storage::Synchronization::FileType::QmlTypes};
+ Storage::Synchronization::DirectoryInfo directoryInfo{
+ sourceId, sourceId, moduleId, Storage::Synchronization::FileType::QmlTypes};
- FileState state = parseTypeInfo(projectData,
+ FileState state = parseTypeInfo(directoryInfo,
Utils::PathString{qmlTypesPath},
package,
notUpdatedSourceIds);
if (state == FileState::Changed) {
- tracer.tick("append project data"_t, keyValue("project data", projectData));
- package.projectDatas.push_back(std::move(projectData));
+ tracer.tick("append project data"_t, keyValue("project data", directoryInfo));
+ package.directoryInfos.push_back(std::move(directoryInfo));
tracer.tick("append updated project source ids"_t, keyValue("source id", sourceId));
- package.updatedProjectSourceIds.push_back(sourceId);
+ package.updatedDirectoryInfoSourceIds.push_back(sourceId);
}
}
}
@@ -352,11 +367,11 @@ void ProjectStorageUpdater::updateDirectoryChanged(std::string_view directoryPat
package.updatedSourceIds.push_back(qmldirSourceId);
}
+ using Storage::ModuleKind;
Utils::PathString moduleName{parser.typeNamespace()};
- ModuleId moduleId = m_projectStorage.moduleId(moduleName);
- Utils::PathString cppModuleName = moduleName + "-cppnative";
- ModuleId cppModuleId = m_projectStorage.moduleId(cppModuleName);
- ModuleId pathModuleId = m_projectStorage.moduleId(directoryPath);
+ ModuleId moduleId = m_projectStorage.moduleId(moduleName, ModuleKind::QmlLibrary);
+ ModuleId cppModuleId = m_projectStorage.moduleId(moduleName, ModuleKind::CppLibrary);
+ ModuleId pathModuleId = m_projectStorage.moduleId(directoryPath, ModuleKind::PathLibrary);
auto imports = filterMultipleEntries(parser.imports());
@@ -364,16 +379,15 @@ void ProjectStorageUpdater::updateDirectoryChanged(std::string_view directoryPat
moduleId,
cppModuleId,
moduleName,
- cppModuleName,
imports,
m_projectStorage);
tracer.tick("append updated module id"_t, keyValue("module id", moduleId));
package.updatedModuleIds.push_back(moduleId);
- const auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId);
- addSourceIds(package.updatedSourceIds, qmlProjectDatas, "append updated source id"_t, tracer);
+ const auto qmlDirectoryInfos = m_projectStorage.fetchDirectoryInfos(directorySourceId);
+ addSourceIds(package.updatedSourceIds, qmlDirectoryInfos, "append updated source id"_t, tracer);
addSourceIds(package.updatedFileStatusSourceIds,
- qmlProjectDatas,
+ qmlDirectoryInfos,
"append updated file status source id"_t,
tracer);
@@ -399,7 +413,7 @@ void ProjectStorageUpdater::updateDirectoryChanged(std::string_view directoryPat
watchedSourceIdsIds,
qmldirState);
tracer.tick("append updated project source id"_t, keyValue("module id", moduleId));
- package.updatedProjectSourceIds.push_back(directorySourceId);
+ package.updatedDirectoryInfoSourceIds.push_back(directorySourceId);
}
void ProjectStorageUpdater::updateDirectories(const QStringList &directories,
@@ -410,10 +424,111 @@ void ProjectStorageUpdater::updateDirectories(const QStringList &directories,
NanotraceHR::Tracer tracer{"update directories"_t, category()};
for (const QString &directory : directories)
- updateDirectory({directory}, package, notUpdatedSourceIds, watchedSourceIdsIds);
+ updateDirectory({directory}, {}, package, notUpdatedSourceIds, watchedSourceIdsIds);
+}
+
+void ProjectStorageUpdater::updateSubdirectories(const Utils::PathString &directoryPath,
+ SourceId directorySourceId,
+ FileState directoryState,
+ const SourceContextIds &subdirectoriesToIgnore,
+ Storage::Synchronization::SynchronizationPackage &package,
+ NotUpdatedSourceIds &notUpdatedSourceIds,
+ WatchedSourceIdsIds &watchedSourceIdsIds)
+{
+ struct Directory
+ {
+ Directory(Utils::SmallStringView path, SourceContextId sourceContextId, SourceId sourceId)
+ : path{path}
+ , sourceContextId{sourceContextId}
+ , sourceId{sourceId}
+ {}
+
+ bool operator<(const Directory &other) const
+ {
+ return sourceContextId < other.sourceContextId;
+ }
+
+ bool operator==(const Directory &other) const
+ {
+ return sourceContextId == other.sourceContextId;
+ }
+
+ Utils::PathString path;
+ SourceContextId sourceContextId;
+ SourceId sourceId;
+ };
+
+ struct Compare
+ {
+ bool operator()(const Directory &first, const Directory &second) const
+ {
+ return first.sourceContextId < second.sourceContextId;
+ }
+
+ bool operator()(const Directory &first, SourceContextId second) const
+ {
+ return first.sourceContextId < second;
+ }
+
+ bool operator()(SourceContextId first, const Directory &second) const
+ {
+ return first < second.sourceContextId;
+ }
+ };
+
+ using Directories = QVarLengthArray<Directory, 32>;
+
+ auto subdirectorySourceIds = m_projectStorage.fetchSubdirectorySourceIds(directorySourceId);
+ auto subdirectories = Utils::transform<Directories>(
+ subdirectorySourceIds, [&](SourceId sourceId) -> Directory {
+ auto sourceContextId = m_pathCache.sourceContextId(sourceId);
+ auto subdirectoryPath = m_pathCache.sourceContextPath(sourceContextId);
+ return {subdirectoryPath, sourceContextId, sourceId};
+ });
+
+ auto exisitingSubdirectoryPaths = m_fileSystem.subdirectories(directoryPath.toQString());
+ Directories existingSubdirecories;
+ for (const QString &subdirectory : exisitingSubdirectoryPaths) {
+ if (subdirectory.endsWith("/designer") || subdirectory.endsWith("/QtQuick/Scene2D")
+ || subdirectory.endsWith("/QtQuick/Scene3D"))
+ continue;
+ Utils::PathString subdirectoryPath = subdirectory;
+ auto [sourceContextId, sourceId] = m_pathCache.sourceContextAndSourceId(
+ SourcePath{subdirectoryPath + "/."});
+ subdirectories.emplace_back(subdirectoryPath, sourceContextId, sourceId);
+ existingSubdirecories.emplace_back(subdirectoryPath, sourceContextId, sourceId);
+ }
+
+ std::sort(subdirectories.begin(), subdirectories.end());
+ subdirectories.erase(std::unique(subdirectories.begin(), subdirectories.end()),
+ subdirectories.end());
+
+ std::set_difference(subdirectories.begin(),
+ subdirectories.end(),
+ subdirectoriesToIgnore.begin(),
+ subdirectoriesToIgnore.end(),
+ Utils::make_iterator([&](const Directory &subdirectory) {
+ updateDirectory(subdirectory.path,
+ subdirectoriesToIgnore,
+ package,
+ notUpdatedSourceIds,
+ watchedSourceIdsIds);
+ }),
+ Compare{});
+
+ if (directoryState == FileState::Changed) {
+ for (const auto &[subdirectoryPath, sourceContextId, subdirectorySourceId] :
+ existingSubdirecories) {
+ package.directoryInfos.emplace_back(directorySourceId,
+ subdirectorySourceId,
+ ModuleId{},
+ Storage::Synchronization::FileType::Directory);
+ }
+ }
}
void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPath,
+ const SourceContextIds &subdirectoriesToIgnore,
Storage::Synchronization::SynchronizationPackage &package,
NotUpdatedSourceIds &notUpdatedSourceIds,
WatchedSourceIdsIds &watchedSourceIdsIds)
@@ -451,10 +566,10 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa
case FileState::NotChanged: {
tracer.tick("update directory not changed"_t);
- parseProjectDatas(m_projectStorage.fetchProjectDatas(directorySourceId),
- package,
- notUpdatedSourceIds,
- watchedSourceIdsIds);
+ parseDirectoryInfos(m_projectStorage.fetchDirectoryInfos(directorySourceId),
+ package,
+ notUpdatedSourceIds,
+ watchedSourceIdsIds);
break;
}
case FileState::NotExists: {
@@ -462,21 +577,29 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa
package.updatedFileStatusSourceIds.push_back(directorySourceId);
package.updatedFileStatusSourceIds.push_back(qmldirSourceId);
- package.updatedProjectSourceIds.push_back(directorySourceId);
+ package.updatedDirectoryInfoSourceIds.push_back(directorySourceId);
package.updatedSourceIds.push_back(qmldirSourceId);
- auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId);
- for (const Storage::Synchronization::ProjectData &projectData : qmlProjectDatas) {
- tracer.tick("append updated source id"_t, keyValue("source id", projectData.sourceId));
- package.updatedSourceIds.push_back(projectData.sourceId);
+ auto qmlDirectoryInfos = m_projectStorage.fetchDirectoryInfos(directorySourceId);
+ for (const Storage::Synchronization::DirectoryInfo &directoryInfo : qmlDirectoryInfos) {
+ tracer.tick("append updated source id"_t, keyValue("source id", directoryInfo.sourceId));
+ package.updatedSourceIds.push_back(directoryInfo.sourceId);
tracer.tick("append updated file status source id"_t,
- keyValue("source id", projectData.sourceId));
- package.updatedFileStatusSourceIds.push_back(projectData.sourceId);
+ keyValue("source id", directoryInfo.sourceId));
+ package.updatedFileStatusSourceIds.push_back(directoryInfo.sourceId);
}
break;
}
}
+ updateSubdirectories(directoryPath,
+ directorySourceId,
+ directoryState,
+ subdirectoriesToIgnore,
+ package,
+ notUpdatedSourceIds,
+ watchedSourceIdsIds);
+
tracer.end(keyValue("qmldir source path", qmldirSourcePath),
keyValue("directory source path", directorySourcePath),
keyValue("directory id", directoryId),
@@ -509,8 +632,12 @@ void ProjectStorageUpdater::updatePropertyEditorPaths(
auto state = fileState(directorySourceId, package, notUpdatedSourceIds);
- if (state == FileState::Changed)
- updatePropertyEditorPath(pathInfo.filePath(), package, directorySourceId);
+ if (state == FileState::Changed) {
+ updatePropertyEditorPath(pathInfo.filePath(),
+ package,
+ directorySourceId,
+ propertyEditorResourcesPath.size() + 1);
+ }
}
}
@@ -647,7 +774,8 @@ void ProjectStorageUpdater::updateTypeAnnotation(const QString &directoryPath,
void ProjectStorageUpdater::updatePropertyEditorPath(
const QString &directoryPath,
Storage::Synchronization::SynchronizationPackage &package,
- SourceId directorySourceId)
+ SourceId directorySourceId,
+ long long pathOffset)
{
NanotraceHR::Tracer tracer{"update property editor path"_t,
category(),
@@ -660,28 +788,31 @@ void ProjectStorageUpdater::updatePropertyEditorPath(
auto dir = QDir{directoryPath};
const auto fileInfos = dir.entryInfoList({"*Pane.qml", "*Specifics.qml"}, QDir::Files);
for (const auto &fileInfo : fileInfos)
- updatePropertyEditorFilePath(fileInfo.filePath(), package, directorySourceId);
+ updatePropertyEditorFilePath(fileInfo.filePath(), package, directorySourceId, pathOffset);
}
void ProjectStorageUpdater::updatePropertyEditorFilePath(
const QString &path,
Storage::Synchronization::SynchronizationPackage &package,
- SourceId directorySourceId)
+ SourceId directorySourceId,
+ long long pathOffset)
{
NanotraceHR::Tracer tracer{"update property editor file path"_t,
category(),
keyValue("directory path", path),
keyValue("directory source id", directorySourceId)};
- QRegularExpression regex{R"xo(.+\/(\w+)\/(\w+)(Specifics|Pane).qml)xo"};
- auto match = regex.match(path);
+ QRegularExpression regex{R"xo((.+)\/(\w+)(Specifics|Pane).qml)xo"};
+ auto match = regex.match(QStringView{path}.mid(pathOffset));
QString oldModuleName;
ModuleId moduleId;
if (match.hasMatch()) {
- auto moduleName = match.capturedView(1);
+ auto moduleName = match.capturedView(1).toString();
+ moduleName.replace('/', '.');
if (oldModuleName != moduleName) {
- oldModuleName = moduleName.toString();
- moduleId = m_projectStorage.moduleId(Utils::SmallString{moduleName});
+ oldModuleName = moduleName;
+ moduleId = m_projectStorage.moduleId(Utils::SmallString{moduleName},
+ Storage::ModuleKind::QmlLibrary);
}
Storage::TypeNameString typeName{match.capturedView(2)};
SourceId pathId = m_pathCache.sourceId(SourcePath{path});
@@ -770,7 +901,11 @@ void ProjectStorageUpdater::pathsWithIdsChanged(const std::vector<IdPaths> &chan
for (auto sourceContextId : directorySourceContextIds) {
Utils::PathString directory = m_pathCache.sourceContextPath(sourceContextId);
- updateDirectory(directory, package, notUpdatedSourceIds, watchedSourceIds);
+ updateDirectory(directory,
+ directorySourceContextIds,
+ package,
+ notUpdatedSourceIds,
+ watchedSourceIds);
}
for (SourceId sourceId : filterUniqueSourceIds(qmlDocumentSourceIds)) {
@@ -782,9 +917,9 @@ void ProjectStorageUpdater::pathsWithIdsChanged(const std::vector<IdPaths> &chan
for (SourceId sourceId : filterUniqueSourceIds(std::move(qmltypesSourceIds))) {
if (!contains(directorySourceContextIds, m_pathCache.sourceContextId(sourceId))) {
auto qmltypesPath = m_pathCache.sourcePath(sourceId);
- auto projectData = m_projectStorage.fetchProjectData(sourceId);
- if (projectData)
- parseTypeInfo(*projectData, qmltypesPath, package, notUpdatedSourceIds);
+ auto directoryInfo = m_projectStorage.fetchDirectoryInfo(sourceId);
+ if (directoryInfo)
+ parseTypeInfo(*directoryInfo, qmltypesPath, package, notUpdatedSourceIds);
}
}
} catch (const QmlDesigner::CannotParseQmlTypesFile &) {
@@ -849,41 +984,44 @@ void ProjectStorageUpdater::parseTypeInfos(const QStringList &typeInfos,
tracer.tick("append module dependenct source source id"_t, keyValue("source id", sourceId));
package.updatedModuleDependencySourceIds.push_back(sourceId);
- auto projectData = package.projectDatas.emplace_back(
+ const auto &directoryInfo = package.directoryInfos.emplace_back(
directorySourceId, sourceId, moduleId, Storage::Synchronization::FileType::QmlTypes);
tracer.tick("append project data"_t, keyValue("source id", sourceId));
- parseTypeInfo(projectData, qmltypesPath, package, notUpdatedSourceIds);
+ parseTypeInfo(directoryInfo, qmltypesPath, package, notUpdatedSourceIds);
}
}
-void ProjectStorageUpdater::parseProjectDatas(const Storage::Synchronization::ProjectDatas &projectDatas,
- Storage::Synchronization::SynchronizationPackage &package,
- NotUpdatedSourceIds &notUpdatedSourceIds,
- WatchedSourceIdsIds &watchedSourceIds)
+void ProjectStorageUpdater::parseDirectoryInfos(
+ const Storage::Synchronization::DirectoryInfos &directoryInfos,
+ Storage::Synchronization::SynchronizationPackage &package,
+ NotUpdatedSourceIds &notUpdatedSourceIds,
+ WatchedSourceIdsIds &watchedSourceIds)
{
NanotraceHR::Tracer tracer{"parse project datas"_t, category()};
- for (const Storage::Synchronization::ProjectData &projectData : projectDatas) {
- switch (projectData.fileType) {
+ for (const Storage::Synchronization::DirectoryInfo &directoryInfo : directoryInfos) {
+ switch (directoryInfo.fileType) {
case Storage::Synchronization::FileType::QmlTypes: {
- watchedSourceIds.qmltypesSourceIds.push_back(projectData.sourceId);
+ watchedSourceIds.qmltypesSourceIds.push_back(directoryInfo.sourceId);
- auto qmltypesPath = m_pathCache.sourcePath(projectData.sourceId);
- parseTypeInfo(projectData, qmltypesPath, package, notUpdatedSourceIds);
+ auto qmltypesPath = m_pathCache.sourcePath(directoryInfo.sourceId);
+ parseTypeInfo(directoryInfo, qmltypesPath, package, notUpdatedSourceIds);
break;
}
case Storage::Synchronization::FileType::QmlDocument: {
- watchedSourceIds.qmlSourceIds.push_back(projectData.sourceId);
+ watchedSourceIds.qmlSourceIds.push_back(directoryInfo.sourceId);
- parseQmlComponent(projectData.sourceId, package, notUpdatedSourceIds);
+ parseQmlComponent(directoryInfo.sourceId, package, notUpdatedSourceIds);
break;
}
+ case Storage::Synchronization::FileType::Directory:
+ break;
}
}
}
-auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::ProjectData &projectData,
+auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::DirectoryInfo &directoryInfo,
Utils::SmallStringView qmltypesPath,
Storage::Synchronization::SynchronizationPackage &package,
NotUpdatedSourceIds &notUpdatedSourceIds) -> FileState
@@ -892,19 +1030,19 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Projec
category(),
keyValue("qmltypes path", qmltypesPath)};
- auto state = fileState(projectData.sourceId, package, notUpdatedSourceIds);
+ auto state = fileState(directoryInfo.sourceId, package, notUpdatedSourceIds);
switch (state) {
case FileState::Changed: {
- tracer.tick("append updated source ids"_t, keyValue("source id", projectData.sourceId));
- package.updatedSourceIds.push_back(projectData.sourceId);
+ tracer.tick("append updated source ids"_t, keyValue("source id", directoryInfo.sourceId));
+ package.updatedSourceIds.push_back(directoryInfo.sourceId);
const auto content = m_fileSystem.contentAsQString(QString{qmltypesPath});
- m_qmlTypesParser.parse(content, package.imports, package.types, projectData);
+ m_qmlTypesParser.parse(content, package.imports, package.types, directoryInfo);
break;
}
case FileState::NotChanged: {
- tracer.tick("append not updated source ids"_t, keyValue("source id", projectData.sourceId));
- notUpdatedSourceIds.sourceIds.push_back(projectData.sourceId);
+ tracer.tick("append not updated source ids"_t, keyValue("source id", directoryInfo.sourceId));
+ notUpdatedSourceIds.sourceIds.push_back(directoryInfo.sourceId);
break;
}
case FileState::NotExists:
@@ -951,9 +1089,9 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil
tracer.tick("append not updated source id"_t, keyValue("source id", sourceId));
notUpdatedSourceIds.sourceIds.emplace_back(sourceId);
- const auto &projectData = package.projectDatas.emplace_back(
+ const auto &directoryInfo = package.directoryInfos.emplace_back(
directorySourceId, sourceId, ModuleId{}, Storage::Synchronization::FileType::QmlDocument);
- tracer.tick("append project data"_t, keyValue("project data", projectData));
+ tracer.tick("append project data"_t, keyValue("project data", directoryInfo));
return;
}
@@ -967,9 +1105,9 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil
break;
}
- const auto &projectData = package.projectDatas.emplace_back(
+ const auto &directoryInfo = package.directoryInfos.emplace_back(
directorySourceId, sourceId, ModuleId{}, Storage::Synchronization::FileType::QmlDocument);
- tracer.tick("append project data"_t, keyValue("project data", projectData));
+ tracer.tick("append project data"_t, keyValue("project data", directoryInfo));
tracer.tick("append updated source id"_t, keyValue("source id", sourceId));
package.updatedSourceIds.push_back(sourceId);
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h
index 640969fe99..baecbd6b11 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h
@@ -143,9 +143,17 @@ private:
WatchedSourceIdsIds &watchedSourceIdsIds);
void updateDirectory(const Utils::PathString &directory,
+ const SourceContextIds &subdirecoriesToIgnore,
Storage::Synchronization::SynchronizationPackage &package,
NotUpdatedSourceIds &notUpdatedSourceIds,
WatchedSourceIdsIds &watchedSourceIdsIds);
+ void updateSubdirectories(const Utils::PathString &directory,
+ SourceId directorySourceId,
+ FileState directoryFileState,
+ const SourceContextIds &subdirecoriesToIgnore,
+ Storage::Synchronization::SynchronizationPackage &package,
+ NotUpdatedSourceIds &notUpdatedSourceIds,
+ WatchedSourceIdsIds &watchedSourceIdsIds);
void updateDirectoryChanged(std::string_view directoryPath,
FileState qmldirState,
SourcePath qmldirSourcePath,
@@ -177,10 +185,12 @@ private:
Storage::Synchronization::SynchronizationPackage &package);
void updatePropertyEditorPath(const QString &path,
Storage::Synchronization::SynchronizationPackage &package,
- SourceId directorySourceId);
+ SourceId directorySourceId,
+ long long pathOffset);
void updatePropertyEditorFilePath(const QString &filePath,
Storage::Synchronization::SynchronizationPackage &package,
- SourceId directorySourceId);
+ SourceId directorySourceId,
+ long long pathOffset);
void parseTypeInfos(const QStringList &typeInfos,
const QList<QmlDirParser::Import> &qmldirDependencies,
const QList<QmlDirParser::Import> &qmldirImports,
@@ -190,11 +200,11 @@ private:
Storage::Synchronization::SynchronizationPackage &package,
NotUpdatedSourceIds &notUpdatedSourceIds,
WatchedSourceIdsIds &watchedSourceIdsIds);
- void parseProjectDatas(const Storage::Synchronization::ProjectDatas &projectDatas,
- Storage::Synchronization::SynchronizationPackage &package,
- NotUpdatedSourceIds &notUpdatedSourceIds,
- WatchedSourceIdsIds &watchedSourceIdsIds);
- FileState parseTypeInfo(const Storage::Synchronization::ProjectData &projectData,
+ void parseDirectoryInfos(const Storage::Synchronization::DirectoryInfos &directoryInfos,
+ Storage::Synchronization::SynchronizationPackage &package,
+ NotUpdatedSourceIds &notUpdatedSourceIds,
+ WatchedSourceIdsIds &watchedSourceIdsIds);
+ FileState parseTypeInfo(const Storage::Synchronization::DirectoryInfo &directoryInfo,
Utils::SmallStringView qmltypesPath,
Storage::Synchronization::SynchronizationPackage &package,
NotUpdatedSourceIds &notUpdatedSourceIds);
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp
index 27efa8d530..4338da62ce 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp
@@ -66,23 +66,32 @@ Storage::Import createImport(const QmlDom::Import &qmlImport,
Utils::SmallStringView directoryPath,
QmlDocumentParser::ProjectStorage &storage)
{
+ using Storage::ModuleKind;
using QmlUriKind = QQmlJS::Dom::QmlUri::Kind;
auto &&uri = qmlImport.uri;
- if (uri.kind() == QmlUriKind::RelativePath) {
+ switch (uri.kind()) {
+ case QmlUriKind::AbsolutePath:
+ case QmlUriKind::DirectoryUrl: {
+ auto moduleId = storage.moduleId(Utils::PathString{uri.toString()}, ModuleKind::PathLibrary);
+ return Storage::Import(moduleId, convertVersion(qmlImport.version), sourceId);
+ }
+ case QmlUriKind::RelativePath: {
auto path = createNormalizedPath(directoryPath, uri.localPath());
- auto moduleId = storage.moduleId(createNormalizedPath(directoryPath, uri.localPath()));
+ auto moduleId = storage.moduleId(createNormalizedPath(directoryPath, uri.localPath()),
+ ModuleKind::PathLibrary);
return Storage::Import(moduleId, Storage::Version{}, sourceId);
}
-
- if (uri.kind() == QmlUriKind::ModuleUri) {
- auto moduleId = storage.moduleId(Utils::PathString{uri.moduleUri()});
+ case QmlUriKind::ModuleUri: {
+ auto moduleId = storage.moduleId(Utils::PathString{uri.moduleUri()}, ModuleKind::QmlLibrary);
return Storage::Import(moduleId, convertVersion(qmlImport.version), sourceId);
}
+ case QmlUriKind::Invalid:
+ return Storage::Import{};
+ }
- auto moduleId = storage.moduleId(Utils::PathString{uri.toString()});
- return Storage::Import(moduleId, convertVersion(qmlImport.version), sourceId);
+ return Storage::Import{};
}
QualifiedImports createQualifiedImports(const QList<QmlDom::Import> &qmlImports,
@@ -122,11 +131,13 @@ void addImports(Storage::Imports &imports,
}
}
- auto localDirectoryModuleId = storage.moduleId(directoryPath);
+ using Storage::ModuleKind;
+
+ auto localDirectoryModuleId = storage.moduleId(directoryPath, ModuleKind::PathLibrary);
imports.emplace_back(localDirectoryModuleId, Storage::Version{}, sourceId);
++importCount;
- auto qmlModuleId = storage.moduleId("QML");
+ auto qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary);
imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId);
++importCount;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp
index 104338e514..b3ec4f0024 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp
@@ -28,7 +28,7 @@ namespace QmlDesigner {
constexpr auto category = ProjectStorageTracing::projectStorageUpdaterCategory;
using NanotraceHR::keyValue;
using Tracer = ProjectStorageTracing::Category::TracerType;
-
+using Storage::ModuleKind;
namespace QmlDom = QQmlJS::Dom;
namespace {
@@ -71,8 +71,7 @@ const Storage::Import &appendImports(Storage::Imports &imports,
});
Utils::PathString moduleName{QStringView(dependency.begin(), spaceFound)};
- moduleName.append("-cppnative");
- ModuleId cppModuleId = storage.moduleId(moduleName);
+ ModuleId cppModuleId = storage.moduleId(moduleName, ModuleKind::CppLibrary);
return imports.emplace_back(cppModuleId, Storage::Version{}, sourceId);
}
@@ -98,7 +97,8 @@ void addImports(Storage::Imports &imports,
const auto &import = imports.emplace_back(cppModuleId, Storage::Version{}, sourceId);
tracer.tick("append import"_t, keyValue("import", import));
- if (ModuleId qmlCppModuleId = storage.moduleId("QML-cppnative"); cppModuleId != qmlCppModuleId) {
+ if (ModuleId qmlCppModuleId = storage.moduleId("QML", ModuleKind::CppLibrary);
+ cppModuleId != qmlCppModuleId) {
const auto &import = imports.emplace_back(qmlCppModuleId, Storage::Version{}, sourceId);
tracer.tick("append import"_t, keyValue("import", import));
}
@@ -145,7 +145,8 @@ Storage::Synchronization::ExportedTypes createExports(const QList<QQmlJSScope::E
for (const QQmlJSScope::Export &qmlExport : qmlExports) {
TypeNameString exportedTypeName{qmlExport.type()};
- exportedTypes.emplace_back(storage.moduleId(Utils::SmallString{qmlExport.package()}),
+ exportedTypes.emplace_back(storage.moduleId(Utils::SmallString{qmlExport.package()},
+ ModuleKind::QmlLibrary),
std::move(exportedTypeName),
createVersion(qmlExport.version()));
}
@@ -469,16 +470,16 @@ void addType(Storage::Synchronization::Types &types,
using namespace Qt::StringLiterals;
constexpr auto skipLists = std::make_tuple(
- std::pair{"QtQuick.Templates-cppnative"sv, std::array{"QQuickItem"_L1}});
+ std::pair{std::pair{"QtQuick.Templates"_sv, ModuleKind::CppLibrary}, std::array{"QQuickItem"_L1}});
-Utils::span<const QLatin1StringView> getSkipList(std::string_view moduleName)
+Utils::span<const QLatin1StringView> getSkipList(const Storage::Module &module)
{
static constexpr Utils::span<const QLatin1StringView> emptySkipList;
auto currentSkipList = emptySkipList;
std::apply(
[&](const auto &entry) {
- if (entry.first == moduleName)
+ if (entry.first.first == module.name && entry.first.second == module.kind)
currentSkipList = entry.second;
},
skipLists);
@@ -494,7 +495,7 @@ bool skipType(const QQmlJSExportedScope &object, Utils::span<const QLatin1String
}
void addTypes(Storage::Synchronization::Types &types,
- const Storage::Synchronization::ProjectData &projectData,
+ const Storage::Synchronization::DirectoryInfo &directoryInfo,
const QList<QQmlJSExportedScope> &objects,
QmlTypesParser::ProjectStorage &storage,
const ComponentWithoutNamespaces &componentNameWithoutNamespaces)
@@ -502,15 +503,15 @@ void addTypes(Storage::Synchronization::Types &types,
NanotraceHR::Tracer tracer{"add types"_t, category()};
types.reserve(Utils::usize(objects) + types.size());
- const auto skipList = getSkipList(storage.moduleName(projectData.moduleId));
+ const auto skipList = getSkipList(storage.module(directoryInfo.moduleId));
for (const auto &object : objects) {
if (skipType(object, skipList))
continue;
addType(types,
- projectData.sourceId,
- projectData.moduleId,
+ directoryInfo.sourceId,
+ directoryInfo.moduleId,
object,
storage,
componentNameWithoutNamespaces);
@@ -522,7 +523,7 @@ void addTypes(Storage::Synchronization::Types &types,
void QmlTypesParser::parse(const QString &sourceContent,
Storage::Imports &imports,
Storage::Synchronization::Types &types,
- const Storage::Synchronization::ProjectData &projectData)
+ const Storage::Synchronization::DirectoryInfo &directoryInfo)
{
NanotraceHR::Tracer tracer{"qmltypes parser parse"_t, category()};
@@ -535,8 +536,8 @@ void QmlTypesParser::parse(const QString &sourceContent,
auto componentNameWithoutNamespaces = createComponentNameWithoutNamespaces(components);
- addImports(imports, projectData.sourceId, dependencies, m_storage, projectData.moduleId);
- addTypes(types, projectData, components, m_storage, componentNameWithoutNamespaces);
+ addImports(imports, directoryInfo.sourceId, dependencies, m_storage, directoryInfo.moduleId);
+ addTypes(types, directoryInfo, components, m_storage, componentNameWithoutNamespaces);
}
#else
@@ -544,7 +545,7 @@ void QmlTypesParser::parse(const QString &sourceContent,
void QmlTypesParser::parse([[maybe_unused]] const QString &sourceContent,
[[maybe_unused]] Storage::Imports &imports,
[[maybe_unused]] Storage::Synchronization::Types &types,
- [[maybe_unused]] const Storage::Synchronization::ProjectData &projectData)
+ [[maybe_unused]] const Storage::Synchronization::DirectoryInfo &directoryInfo)
{}
#endif
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h
index 4a6427501b..c73a429f91 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h
@@ -31,7 +31,7 @@ public:
void parse(const QString &sourceContent,
Storage::Imports &imports,
Storage::Synchronization::Types &types,
- const Storage::Synchronization::ProjectData &projectData) override;
+ const Storage::Synchronization::DirectoryInfo &directoryInfo) override;
private:
#ifdef QDS_BUILD_QMLPARSER
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h
index cdc7cd54d7..c0880cf5c6 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h
@@ -15,7 +15,7 @@ public:
virtual void parse(const QString &sourceContent,
Storage::Imports &imports,
Storage::Synchronization::Types &types,
- const Storage::Synchronization::ProjectData &projectData)
+ const Storage::Synchronization::DirectoryInfo &directoryInfo)
= 0;
protected:
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h b/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h
index b655c5cc34..fa550a4d52 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h
@@ -7,6 +7,8 @@
#include <utils/smallstring.h>
+#include <QVarLengthArray>
+
namespace QmlDesigner {
class SourcePath : public Utils::PathString
@@ -128,5 +130,6 @@ private:
};
using SourcePaths = std::vector<SourcePath>;
-
+template<std::size_t size>
+using SmallSourcePaths = QVarLengthArray<SourcePath, size>;
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/storagecache.h b/src/plugins/qmldesigner/designercore/projectstorage/storagecache.h
index 85c6147d2c..32ecb1c3f7 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/storagecache.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/storagecache.h
@@ -313,7 +313,7 @@ private:
return entries.end();
}
- auto value = *found;
+ const auto &value = *found;
if (value == view) {
return found;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp
index 67a63542bc..71eba94966 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp
@@ -178,8 +178,15 @@ TypeAnnotationReader::ParserSate TypeAnnotationReader::readDocument(const QStrin
TypeAnnotationReader::ParserSate TypeAnnotationReader::readMetaInfoRootElement(const QString &name)
{
if (name == typeElementName) {
- m_typeAnnotations.emplace_back(m_sourceId, m_directorySourceId);
+ auto &annotation = m_typeAnnotations.emplace_back(m_sourceId, m_directorySourceId);
+ annotation.traits.canBeDroppedInFormEditor = FlagIs::True;
+ annotation.traits.canBeDroppedInNavigator = FlagIs::True;
+ annotation.traits.isMovable = FlagIs::True;
+ annotation.traits.isResizable = FlagIs::True;
+ annotation.traits.hasFormEditorItem = FlagIs::True;
+ annotation.traits.visibleInLibrary = FlagIs::True;
m_itemLibraryEntries = json::array();
+
return ParsingType;
} else {
addErrorInvalidType(name);
@@ -258,7 +265,8 @@ void TypeAnnotationReader::readTypeProperty(QStringView name, const QVariant &va
auto [moduleName, typeName] = decomposeTypePath(fullTypeName);
m_typeAnnotations.back().typeName = typeName;
- m_typeAnnotations.back().moduleId = m_projectStorage.moduleId(moduleName);
+ m_typeAnnotations.back().moduleId = m_projectStorage.moduleId(moduleName,
+ ModuleKind::QmlLibrary);
} else if (name == "icon"_L1) {
m_typeAnnotations.back().iconPath = absoluteFilePathForDocument(value.toString());
@@ -304,7 +312,7 @@ QString deEscape(const QString &value)
QVariant deEscapeVariant(const QVariant &value)
{
- if (value.typeId() == QVariant::String)
+ if (value.typeId() == QMetaType::QString)
return deEscape(value.toString());
return value;
}
@@ -459,9 +467,9 @@ using json = nlohmann::json;
out = json::array({});
out.push_back(property.name);
out.push_back(property.type);
- if (property.value.type() == QVariant::String)
+ if (property.value.typeId() == QMetaType::QString)
out.push_back(Utils::PathString{property.value.toString()});
- else if (property.value.type() == QVariant::Int || property.value.type() == QVariant::LongLong)
+ else if (property.value.typeId() == QMetaType::Int || property.value.typeId() == QMetaType::LongLong)
out.push_back(property.value.toLongLong());
else
out.push_back(property.value.toDouble());
diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp
index b5798b713d..cbe7b0ec38 100644
--- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp
+++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp
@@ -88,4 +88,13 @@ Category &projectStorageUpdaterCategory()
} // namespace ProjectStorageTracing
+namespace MetaInfoTracing {
+Category &category()
+{
+ thread_local Category category_{"meta info"_t, Tracing::eventQueueWithStringArguments(), category};
+
+ return category_;
+}
+} // namespace MetaInfoTracing
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h
index 3a33834c70..899ceb6cd2 100644
--- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h
+++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h
@@ -62,4 +62,20 @@ using Category = NanotraceHR::StringViewWithStringArgumentsCategory<projectStora
[[gnu::pure]] Category &projectStorageUpdaterCategory();
} // namespace ProjectStorageTracing
+
+namespace MetaInfoTracing {
+constexpr NanotraceHR::Tracing tracingStatus()
+{
+#ifdef ENABLE_METAINFO_TRACING
+ return NanotraceHR::Tracing::IsEnabled;
+#else
+ return NanotraceHR::Tracing::IsDisabled;
+#endif
+}
+
+using Category = NanotraceHR::StringViewWithStringArgumentsCategory<tracingStatus()>;
+
+[[gnu::pure]] Category &category();
+
+} // namespace MetaInfoTracing
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/uniquename.cpp b/src/plugins/qmldesigner/designercore/uniquename.cpp
new file mode 100644
index 0000000000..d7506164db
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/uniquename.cpp
@@ -0,0 +1,165 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "uniquename.h"
+
+#include <utils/span.h>
+
+#include <QFileInfo>
+#include <QRegularExpression>
+
+namespace QmlDesigner::UniqueName {
+
+using namespace Qt::Literals;
+
+constexpr QLatin1StringView keywords[] {
+ "anchors"_L1, "as"_L1, "baseState"_L1,
+ "border"_L1, "bottom"_L1, "break"_L1,
+ "case"_L1, "catch"_L1, "clip"_L1,
+ "color"_L1, "continue"_L1, "data"_L1,
+ "debugger"_L1, "default"_L1, "delete"_L1,
+ "do"_L1, "else"_L1, "enabled"_L1,
+ "finally"_L1, "flow"_L1, "focus"_L1,
+ "font"_L1, "for"_L1, "function"_L1,
+ "height"_L1, "if"_L1, "import"_L1,
+ "in"_L1, "instanceof"_L1, "item"_L1,
+ "layer"_L1, "left"_L1, "margin"_L1,
+ "new"_L1, "opacity"_L1, "padding"_L1,
+ "parent"_L1, "print"_L1, "rect"_L1,
+ "return"_L1, "right"_L1, "scale"_L1,
+ "shaderInfo"_L1, "source"_L1, "sprite"_L1,
+ "spriteSequence"_L1, "state"_L1, "switch"_L1,
+ "text"_L1, "this"_L1, "throw"_L1,
+ "top"_L1, "try"_L1, "typeof"_L1,
+ "var"_L1, "visible"_L1, "void"_L1,
+ "while"_L1, "with"_L1, "x"_L1,
+ "y"_L1
+};
+
+namespace {
+
+QString toCamelCase(const QString &input)
+{
+ QString result = input.at(0).toLower();
+ bool capitalizeNext = false;
+
+ for (const QChar &c : Utils::span{input}.subspan(1)) {
+ bool isValidChar = c.isLetterOrNumber() || c == '_';
+ if (isValidChar)
+ result += capitalizeNext ? c.toUpper() : c;
+
+ capitalizeNext = !isValidChar;
+ }
+
+ return result;
+}
+
+} // namespace
+
+/**
+ * @brief Generates a unique name based on the provided name.
+ *
+ * This method iteratively generates a name by appending suffixes until a unique name is found.
+ * The uniqueness of the generated name is determined by the provided predicate function.
+ *
+ * @param name The original name to be made unique.
+ * @param predicate A function that checks if a name exists. Returns true if the name exists,
+ * false if name is unique.
+ * @return A unique name derived from the provided name.
+ */
+QString generate(const QString &name, std::function<bool(const QString &)> predicate)
+{
+ if (!predicate(name))
+ return name;
+
+ // match prefix and number (including zero padding) parts
+ static QRegularExpression rgx("(\\D*?)(\\d+)$");
+ QRegularExpressionMatch match = rgx.match(name);
+
+ QString prefix;
+ int number = 0;
+ int padding = 0;
+
+ if (match.hasMatch()) {
+ // Split the name into prefix and number
+ prefix = match.captured(1);
+ QString numberStr = match.captured(2);
+ number = numberStr.toInt();
+ padding = numberStr.size();
+ } else {
+ prefix = name;
+ }
+
+ QString nameTemplate = "%1%2";
+ QString newName;
+ do {
+ newName = nameTemplate.arg(prefix).arg(++number, padding, 10, QChar('0'));
+ } while (predicate(newName));
+
+ return newName;
+}
+
+/**
+ * @brief Generates a unique path based on the provided path. If the path belongs to a file, the
+ * filename or if it's a directory, the directory name will be adjusted to ensure uniqueness.
+ *
+ * This method appends a numerical suffix (or increment it if it exists) to the filename or
+ * directory name if necessary to make it unique.
+ *
+ * @param path The original path to be made unique.
+ * @return A unique path derived from the provided path.
+ */
+QString generatePath(const QString &path)
+{
+ // Remove the trailing slash if it exists (otherwise QFileInfo::path() returns empty)
+ QString adjustedPath = path;
+ if (adjustedPath.endsWith('/'))
+ adjustedPath.chop(1);
+
+ QFileInfo fileInfo = QFileInfo(adjustedPath);
+ QString baseName = fileInfo.baseName();
+ QString suffix = fileInfo.completeSuffix();
+ if (!suffix.isEmpty())
+ suffix.prepend('.');
+
+ QString parentDir = fileInfo.path();
+ QString pathTemplate = parentDir + "/%1" + suffix;
+
+ QString uniqueBaseName = UniqueName::generate(baseName, [&] (const QString &currName) {
+ return QFileInfo::exists(pathTemplate.arg(currName));
+ });
+
+ return pathTemplate.arg(uniqueBaseName);
+}
+
+/**
+ * @brief Generates a unique ID based on the provided id
+ *
+ * This works similar to get() with additional restrictions:
+ * - Removes non-Latin1 characters
+ * - Removes spaces
+ * - Ensures the first letter is lowercase
+ * - Converts spaces to camel case
+ * - Prepends an underscore if id starts with a number or is a reserved word
+ *
+ * @param id The original id to be made unique.
+ * @return A unique Id (when predicate() returns false)
+ */
+QString generateId(const QString &id, std::function<bool(const QString &)> predicate)
+{
+ // remove non word (non A-Z, a-z, 0-9) or space characters
+ QString newId = id.trimmed();
+
+ newId = toCamelCase(newId);
+
+ // prepend _ if starts with a digit or invalid id (such as reserved words)
+ if (newId.at(0).isDigit() || std::binary_search(std::begin(keywords), std::end(keywords), newId))
+ newId.prepend('_');
+
+ if (!predicate)
+ return newId;
+
+ return UniqueName::generate(newId, predicate);
+}
+
+} // namespace QmlDesigner::UniqueName
diff --git a/src/plugins/qmldesigner/designercore/uniquename.h b/src/plugins/qmldesigner/designercore/uniquename.h
new file mode 100644
index 0000000000..85927c4514
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/uniquename.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <qmldesignercorelib_exports.h>
+
+#include <QString>
+
+namespace QmlDesigner::UniqueName {
+
+QString generate(const QString &name, std::function<bool(const QString &)> predicate);
+QString generatePath(const QString &path);
+QMLDESIGNERCORE_EXPORT QString generateId(const QString &id,
+ std::function<bool(const QString &)> predicate = {});
+
+} // namespace QmlDesigner::UniqueName
diff --git a/src/plugins/qmldesigner/designmodecontext.cpp b/src/plugins/qmldesigner/designmodecontext.cpp
index 43fb0d9d75..796261935a 100644
--- a/src/plugins/qmldesigner/designmodecontext.cpp
+++ b/src/plugins/qmldesigner/designmodecontext.cpp
@@ -3,7 +3,6 @@
#include "designmodecontext.h"
#include "assetslibrarywidget.h"
-#include "collectionwidget.h"
#include "designmodewidget.h"
#include "edit3dwidget.h"
#include "formeditorwidget.h"
@@ -98,15 +97,4 @@ void TextEditorContext::contextHelp(const HelpCallback &callback) const
qobject_cast<TextEditorWidget *>(m_widget)->contextHelp(callback);
}
-CollectionEditorContext::CollectionEditorContext(QWidget *widget)
- : IContext(widget)
-{
- setWidget(widget);
- setContext(Core::Context(Constants::C_QMLCOLLECTIONEDITOR, Constants::C_QT_QUICK_TOOLS_MENU));
-}
-
-void CollectionEditorContext::contextHelp(const HelpCallback &callback) const
-{
- qobject_cast<CollectionWidget *>(m_widget)->contextHelp(callback);
-}
} // namespace QmlDesigner::Internal
diff --git a/src/plugins/qmldesigner/designmodecontext.h b/src/plugins/qmldesigner/designmodecontext.h
index 12f0113d97..1d146deb7d 100644
--- a/src/plugins/qmldesigner/designmodecontext.h
+++ b/src/plugins/qmldesigner/designmodecontext.h
@@ -73,14 +73,5 @@ public:
TextEditorContext(QWidget *widget);
void contextHelp(const Core::IContext::HelpCallback &callback) const override;
};
-
-class CollectionEditorContext : public Core::IContext
-{
- Q_OBJECT
-
-public:
- CollectionEditorContext(QWidget *widget);
- void contextHelp(const Core::IContext::HelpCallback &callback) const override;
-};
} // namespace Internal
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designmodewidget.h b/src/plugins/qmldesigner/designmodewidget.h
index 464994b7e3..881335da75 100644
--- a/src/plugins/qmldesigner/designmodewidget.h
+++ b/src/plugins/qmldesigner/designmodewidget.h
@@ -11,7 +11,6 @@
#include <QWidget>
#include <QMainWindow>
-#include <QScopedPointer>
#include <advanceddockingsystem/dockmanager.h>
#include <annotationeditor/globalannotationeditor.h>
diff --git a/src/plugins/qmldesigner/documentwarningwidget.cpp b/src/plugins/qmldesigner/documentwarningwidget.cpp
index e1b8346a66..a1cc12d825 100644
--- a/src/plugins/qmldesigner/documentwarningwidget.cpp
+++ b/src/plugins/qmldesigner/documentwarningwidget.cpp
@@ -149,8 +149,8 @@ bool DocumentWarningWidget::eventFilter(QObject *object, QEvent *event)
void DocumentWarningWidget::showEvent(QShowEvent *event)
{
- const QColor backgroundColor = Utils::creatorTheme()->color(Utils::Theme::DScontrolBackground);
- const QColor outlineColor = Utils::creatorTheme()->color(Utils::Theme::DScontrolOutline);
+ const QColor backgroundColor = Utils::creatorColor(Utils::Theme::DScontrolBackground);
+ const QColor outlineColor = Utils::creatorColor(Utils::Theme::DScontrolOutline);
QPalette pal = palette();
pal.setColor(QPalette::ToolTipBase, backgroundColor);
pal.setColor(QPalette::ToolTipText, outlineColor);
diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h
index fb691097f7..28f3ed6097 100644
--- a/src/plugins/qmldesigner/qmldesignerconstants.h
+++ b/src/plugins/qmldesigner/qmldesignerconstants.h
@@ -19,7 +19,6 @@ inline constexpr char C_QMLNAVIGATOR[] = "QmlDesigner::Navigator";
inline constexpr char C_QMLTEXTEDITOR[] = "QmlDesigner::TextEditor";
inline constexpr char C_QMLMATERIALBROWSER[] = "QmlDesigner::MaterialBrowser";
inline constexpr char C_QMLASSETSLIBRARY[] = "QmlDesigner::AssetsLibrary";
-inline constexpr char C_QMLCOLLECTIONEDITOR[] = "QmlDesigner::CollectionEditor";
// Special context for preview menu, shared b/w designer and text editor
inline constexpr char C_QT_QUICK_TOOLS_MENU[] = "QmlDesigner::ToolsMenu";
@@ -51,6 +50,7 @@ inline constexpr char EDIT3D_EDIT_CAMERA[] = "QmlDesigner.Editor3D.EditCameraTog
inline constexpr char EDIT3D_ORIENTATION[] = "QmlDesigner.Editor3D.OrientationToggle";
inline constexpr char EDIT3D_EDIT_LIGHT[] = "QmlDesigner.Editor3D.EditLightToggle";
inline constexpr char EDIT3D_EDIT_SHOW_GRID[] = "QmlDesigner.Editor3D.ToggleGrid";
+inline constexpr char EDIT3D_EDIT_SHOW_LOOKAT[] = "QmlDesigner.Editor3D.ToggleLookAt";
inline constexpr char EDIT3D_EDIT_SELECT_BACKGROUND_COLOR[]
= "QmlDesigner.Editor3D.SelectBackgroundColor";
inline constexpr char EDIT3D_EDIT_SELECT_GRID_COLOR[] = "QmlDesigner.Editor3D.SelectGridColor";
@@ -78,8 +78,14 @@ inline constexpr char EDIT3D_SNAP_CONFIG[] = "QmlDesigner.Editor3D.SnapConfig";
inline constexpr char EDIT3D_CAMERA_SPEED_CONFIG[] = "QmlDesigner.Editor3D.CameraSpeedConfig";
inline constexpr char QML_DESIGNER_SUBFOLDER[] = "/designer/";
-inline constexpr char COMPONENT_BUNDLES_TYPE[] = "ComponentBundles";
-inline constexpr char GENERATED_COMPONENTS_FOLDER[] = "GeneratedComponents";
+inline constexpr char BUNDLE_JSON_FILENAME[] = "bundle.json";
+inline constexpr char COMPONENT_BUNDLES_TYPE[] = "Bundles";
+inline constexpr char COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE[] = "Materials";
+inline constexpr char COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE[] = "Effects";
+inline constexpr char COMPONENT_BUNDLES_USER_MATERIAL_BUNDLE_TYPE[] = "UserMaterials";
+inline constexpr char COMPONENT_BUNDLES_USER_EFFECT_BUNDLE_TYPE[] = "UserEffects";
+inline constexpr char COMPONENT_BUNDLES_USER_3D_BUNDLE_TYPE[] = "User3D";
+inline constexpr char GENERATED_COMPONENTS_FOLDER[] = "Generated";
inline constexpr char COMPONENT_BUNDLES_ASSET_REF_FILE[] = "_asset_ref.json";
inline constexpr char OLD_QUICK_3D_ASSETS_FOLDER[] = "Quick3DAssets";
inline constexpr char QUICK_3D_COMPONENTS_FOLDER[] = "QtQuick3D";
@@ -90,7 +96,10 @@ inline constexpr char QUICK_3D_ASSET_IMPORT_DATA_SOURCE_KEY[] = "source_scene";
inline constexpr char OLD_ASSET_IMPORT_FOLDER[] = "asset_imports";
inline constexpr char OLD_EFFECTS_IMPORT_FOLDER[] = "/asset_imports/Effects";
inline constexpr char OLD_EFFECTS_FOLDER[] = "Effects";
-inline constexpr char COMPOSED_EFFECTS_TYPE[] = "ComposedEffects";
+inline constexpr char OLD_COMPONENT_BUNDLES_TYPE[] = "ComponentBundles";
+inline constexpr char OLD_COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE[] = "MaterialBundle";
+inline constexpr char OLD_COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE[] = "EffectBundle";
+inline constexpr char COMPOSED_EFFECTS_TYPE[] = "Effects";
inline constexpr char MATERIAL_LIB_ID[] = "__materialLibrary__";
inline constexpr char MIME_TYPE_ITEM_LIBRARY_INFO[]
@@ -98,7 +107,7 @@ inline constexpr char MIME_TYPE_ITEM_LIBRARY_INFO[]
inline constexpr char MIME_TYPE_ASSETS[] = "application/vnd.qtdesignstudio.assets";
inline constexpr char MIME_TYPE_MATERIAL[] = "application/vnd.qtdesignstudio.material";
inline constexpr char MIME_TYPE_TEXTURE[] = "application/vnd.qtdesignstudio.texture";
-inline constexpr char MIME_TYPE_BUNDLE_EFFECT[] = "application/vnd.qtdesignstudio.bundleeffect";
+inline constexpr char MIME_TYPE_BUNDLE_ITEM[] = "application/vnd.qtdesignstudio.bundleitem";
inline constexpr char MIME_TYPE_BUNDLE_MATERIAL[] = "application/vnd.qtdesignstudio.bundlematerial";
inline constexpr char MIME_TYPE_BUNDLE_TEXTURE[] = "application/vnd.qtdesignstudio.bundletexture";
inline constexpr char MIME_TYPE_ASSET_IMAGE[] = "application/vnd.qtdesignstudio.asset.image";
@@ -178,7 +187,6 @@ inline constexpr char OBJECT_NAME_EFFECT_COMPOSER[] = "QQuickWidgetEffectCompose
inline constexpr char OBJECT_NAME_MATERIAL_BROWSER[] = "QQuickWidgetMaterialBrowser";
inline constexpr char OBJECT_NAME_MATERIAL_EDITOR[] = "QQuickWidgetMaterialEditor";
inline constexpr char OBJECT_NAME_PROPERTY_EDITOR[] = "QQuickWidgetPropertyEditor";
-inline constexpr char OBJECT_NAME_COLLECTION_EDITOR[] = "QQuickWidgetQDSCollectionEditor";
inline constexpr char OBJECT_NAME_STATES_EDITOR[] = "QQuickWidgetStatesEditor";
inline constexpr char OBJECT_NAME_TEXTURE_EDITOR[] = "QQuickWidgetTextureEditor";
inline constexpr char OBJECT_NAME_TOP_TOOLBAR[] = "QQuickWidgetTopToolbar";
diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp
index 321d95197f..97b2b46e7d 100644
--- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp
+++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp
@@ -56,6 +56,11 @@ QUrl ExternalDependencies::projectUrl() const
return {};
}
+QString ExternalDependencies::projectName() const
+{
+ return QmlDesignerPlugin::instance()->documentManager().currentProjectName();
+}
+
QString ExternalDependencies::currentProjectDirPath() const
{
return QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath().toString();
diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h
index b4908c2383..6a49e4b551 100644
--- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h
+++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h
@@ -21,6 +21,7 @@ public:
QString qmlPuppetFallbackDirectory() const override;
QString defaultPuppetToplevelBuildDirectory() const override;
QUrl projectUrl() const override;
+ QString projectName() const override;
QString currentProjectDirPath() const override;
QUrl currentResourcePath() const override;
void parseItemLibraryDescriptions() override;
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp
index f1b8ecdfa7..9b6fb50425 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.cpp
+++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp
@@ -4,7 +4,6 @@
#include "qmldesignerplugin.h"
#include "qmldesignertr.h"
-#include "collectioneditor/collectionview.h"
#include "coreplugin/iwizardfactory.h"
#include "designmodecontext.h"
#include "designmodewidget.h"
@@ -298,7 +297,6 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e
//TODO Move registering those types out of the property editor, since they are used also in the states editor
Quick2PropertyEditorView::registerQmlTypes();
- CollectionView::registerDeclarativeType();
StudioQuickWidget::registerDeclarativeType();
QmlDesignerBase::WindowManager::registerDeclarativeType();
@@ -392,7 +390,6 @@ void QmlDesignerPlugin::integrateIntoQtCreator(QWidget *modeWidget)
Core::Context qmlDesignerNavigatorContext(Constants::C_QMLNAVIGATOR);
Core::Context qmlDesignerMaterialBrowserContext(Constants::C_QMLMATERIALBROWSER);
Core::Context qmlDesignerAssetsLibraryContext(Constants::C_QMLASSETSLIBRARY);
- Core::Context qmlDesignerCollectionEditorContext(Constants::C_QMLCOLLECTIONEDITOR);
context->context().add(qmlDesignerMainContext);
context->context().add(qmlDesignerFormEditorContext);
@@ -400,7 +397,6 @@ void QmlDesignerPlugin::integrateIntoQtCreator(QWidget *modeWidget)
context->context().add(qmlDesignerNavigatorContext);
context->context().add(qmlDesignerMaterialBrowserContext);
context->context().add(qmlDesignerAssetsLibraryContext);
- context->context().add(qmlDesignerCollectionEditorContext);
context->context().add(ProjectExplorer::Constants::QMLJS_LANGUAGE_ID);
d->shortCutManager.registerActions(qmlDesignerMainContext, qmlDesignerFormEditorContext,
diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp
index 9602bf050f..730a557d12 100644
--- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp
+++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp
@@ -12,15 +12,16 @@
#include <projectstorage/filesystem.h>
#include <projectstorage/nonlockingmutex.h>
#include <projectstorage/projectstorage.h>
+#include <projectstorage/projectstorageerrornotifier.h>
#include <projectstorage/projectstoragepathwatcher.h>
#include <projectstorage/projectstorageupdater.h>
#include <projectstorage/qmldocumentparser.h>
#include <projectstorage/qmltypesparser.h>
#include <projectstorage/sourcepathcache.h>
-#include <sqlitedatabase.h>
#include <qmlprojectmanager/qmlproject.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitaspect.h>
+#include <sqlitedatabase.h>
#include <asynchronousexplicitimagecache.h>
#include <asynchronousimagecache.h>
@@ -181,7 +182,8 @@ public:
pathCache.sourceId(SourcePath{project->projectDirectory().toString() + "/."}).internalId())}
{}
Sqlite::Database database;
- ProjectStorage storage{database, database.isInitialized()};
+ ProjectStorageErrorNotifier errorNotifier{pathCache};
+ ProjectStorage storage{database, errorNotifier, database.isInitialized()};
PathCacheType pathCache{storage};
FileSystem fileSystem{pathCache};
FileStatusCache fileStatusCache{fileSystem};
@@ -238,30 +240,34 @@ QmlDesignerProjectManager::QmlDesignerProjectManager(ExternalDependenciesInterfa
, m_externalDependencies{externalDependencies}
{
auto editorManager = ::Core::EditorManager::instance();
- QObject::connect(editorManager, &::Core::EditorManager::editorOpened, [&](auto *editor) {
+ QObject::connect(editorManager, &::Core::EditorManager::editorOpened, &dummy, [&](auto *editor) {
editorOpened(editor);
});
- QObject::connect(editorManager, &::Core::EditorManager::currentEditorChanged, [&](auto *editor) {
- currentEditorChanged(editor);
- });
- QObject::connect(editorManager, &::Core::EditorManager::editorsClosed, [&](const auto &editors) {
- editorsClosed(editors);
- });
+ QObject::connect(editorManager,
+ &::Core::EditorManager::currentEditorChanged,
+ &dummy,
+ [&](auto *editor) { currentEditorChanged(editor); });
+ QObject::connect(editorManager,
+ &::Core::EditorManager::editorsClosed,
+ &dummy,
+ [&](const auto &editors) { editorsClosed(editors); });
auto sessionManager = ::ProjectExplorer::ProjectManager::instance();
QObject::connect(sessionManager,
&::ProjectExplorer::ProjectManager::projectAdded,
+ &dummy,
[&](auto *project) { projectAdded(project); });
QObject::connect(sessionManager,
&::ProjectExplorer::ProjectManager::aboutToRemoveProject,
+ &dummy,
[&](auto *project) { aboutToRemoveProject(project); });
QObject::connect(sessionManager,
&::ProjectExplorer::ProjectManager::projectRemoved,
+ &dummy,
[&](auto *project) { projectRemoved(project); });
- QObject::connect(&m_previewImageCacheData->timer,
- &QTimer::timeout,
- this,
- &QmlDesignerProjectManager::generatePreview);
+ QObject::connect(&m_previewImageCacheData->timer, &QTimer::timeout, &dummy, [&]() {
+ generatePreview();
+ });
}
QmlDesignerProjectManager::~QmlDesignerProjectManager() = default;
@@ -337,81 +343,32 @@ Utils::FilePath qmlPath(::ProjectExplorer::Target *target)
return {};
}
-template<typename... Path>
-bool skipDirectoriesWith(const QStringView directoryPath, const Path &...paths)
-{
- return (directoryPath.contains(paths) || ...);
-}
-
-template<typename... Path>
-bool skipDirectoriesEndsWith(const QStringView directoryPath, const Path &...paths)
-{
- return (directoryPath.endsWith(paths) || ...);
-}
-
-bool skipPath(const QString &directoryPath)
-{
- return skipDirectoriesWith(directoryPath,
- u"QtApplicationManager",
- u"QtInterfaceFramework",
- u"QtOpcUa",
- u"Qt3D",
- u"Scene2D",
- u"Scene3D",
- u"QtWayland",
- u"Qt5Compat",
- u"QtCharts",
- u"QtLocation",
- u"QtPositioning",
- u"MaterialEditor",
- u"QtTextToSpeech",
- u"QtWebEngine",
- u"Qt/labs",
- u"QtDataVisualization")
- || skipDirectoriesEndsWith(directoryPath, u"designer");
-}
-
-void collectQmldirPaths(const QString &path, QStringList &qmldirPaths)
-{
- QDirIterator dirIterator{path, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories};
-
- QString rootQmldirPath = path + "/qmldir";
- if (!skipPath(path) && QFileInfo::exists(rootQmldirPath))
- qmldirPaths.push_back(path);
-
- while (dirIterator.hasNext()) {
- auto directoryPath = dirIterator.next();
-
- QString qmldirPath = directoryPath + "/qmldir";
- if (!skipPath(directoryPath) && QFileInfo::exists(qmldirPath))
- qmldirPaths.push_back(directoryPath);
- }
-}
-
[[maybe_unused]] void projectQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths)
{
::QmlProjectManager::QmlBuildSystem *buildSystem = getQmlBuildSystem(target);
const Utils::FilePath projectDirectoryPath = buildSystem->canonicalProjectDir();
- const QStringList importPaths = buildSystem->importPaths();
- const QDir projectDirectory(projectDirectoryPath.toString());
- for (const QString &importPath : importPaths)
- collectQmldirPaths(importPath, qmldirPaths);
+ qmldirPaths.push_back(projectDirectoryPath.path());
}
[[maybe_unused]] void qtQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths)
{
- if constexpr (useProjectStorage())
- collectQmldirPaths(qmlPath(target).toString(), qmldirPaths);
+ if constexpr (useProjectStorage()) {
+ auto qmlRootPath = qmlPath(target).toString();
+ qmldirPaths.push_back(qmlRootPath + "/QtQml");
+ qmldirPaths.push_back(qmlRootPath + "/QtQuick");
+ qmldirPaths.push_back(qmlRootPath + "/QtQuick3D");
+ qmldirPaths.push_back(qmlRootPath + "/Qt5Compat");
+ }
}
[[maybe_unused]] void qtQmldirPathsForLiteDesigner(QStringList &qmldirPaths)
{
if constexpr (useProjectStorage()) {
auto qmlRootPath = QLibraryInfo::path(QLibraryInfo::QmlImportsPath);
- collectQmldirPaths(qmlRootPath + "/QtQml", qmldirPaths);
- collectQmldirPaths(qmlRootPath + "/QtQuick", qmldirPaths);
+ qmldirPaths.push_back(qmlRootPath + "/QtQml");
+ qmldirPaths.push_back(qmlRootPath + "/QtQuick");
}
}
@@ -567,12 +524,12 @@ QmlDesignerProjectManager::ImageCacheData *QmlDesignerProjectManager::imageCache
m_imageCacheData->nodeInstanceCollector.setTarget(project->activeTarget());
QObject::connect(project,
&ProjectExplorer::Project::activeTargetChanged,
- this,
+ &dummy,
setTargetInImageCache);
}
QObject::connect(ProjectExplorer::ProjectManager::instance(),
&ProjectExplorer::ProjectManager::startupProjectChanged,
- this,
+ &dummy,
[=](ProjectExplorer::Project *project) {
setTargetInImageCache(activeTarget(project));
});
diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.h b/src/plugins/qmldesigner/qmldesignerprojectmanager.h
index bd45bf16c9..1e5cec2e3e 100644
--- a/src/plugins/qmldesigner/qmldesignerprojectmanager.h
+++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.h
@@ -28,10 +28,8 @@ namespace QmlDesigner {
class ExternalDependenciesInterface;
-class QmlDesignerProjectManager : public QObject
+class QmlDesignerProjectManager
{
- Q_OBJECT
-
class QmlDesignerProjectManagerProjectData;
class PreviewImageCacheData;
class ImageCacheData;
@@ -70,5 +68,6 @@ private:
std::unique_ptr<PreviewImageCacheData> m_previewImageCacheData;
std::unique_ptr<QmlDesignerProjectManagerProjectData> m_projectData;
ExternalDependenciesInterface &m_externalDependencies;
+ QObject dummy;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-16px.png b/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-16px.png
new file mode 100644
index 0000000000..5171fdd79d
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-16px.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px.png b/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px.png
new file mode 100644
index 0000000000..2fba147700
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px@2x.png b/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px@2x.png
new file mode 100644
index 0000000000..170c113bbd
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px@2x.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc
index 34838797d4..c2a1ff5929 100644
--- a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc
+++ b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc
@@ -127,5 +127,8 @@
<file>images/extended-view3d-16px.png</file>
<file>images/extended-view3d-24px.png</file>
<file>images/extended-view3d-24px@2x.png</file>
+ <file>images/frame-animation-16px.png</file>
+ <file>images/frame-animation-24px.png</file>
+ <file>images/frame-animation-24px@2x.png</file>
</qresource>
</RCC>
diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
index 46cb42b60d..63d390dded 100644
--- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
+++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
@@ -549,6 +549,26 @@ MetaInfo {
}
Type {
+ name: "QtQuick.FrameAnimation"
+ icon: ":/qtquickplugin/images/frame-animation-16px.png"
+
+ Hints {
+ visibleInNavigator: true
+ canBeDroppedInNavigator: true
+ canBeDroppedInFormEditor: false
+ canBeContainer: false
+ }
+
+ ItemLibraryEntry {
+ name: "Frame Animation"
+ category: "d.Qt Quick - Animation"
+ libraryIcon: ":/qtquickplugin/images/frame-animation-24px.png"
+ version: "2.0"
+ toolTip: qsTr("Triggers a handler at every animation frame update.")
+ }
+ }
+
+ Type {
name: "QtQml.Timer"
icon: ":/qtquickplugin/images/timer-16px.png"
diff --git a/src/plugins/qmldesigner/settingspage.cpp b/src/plugins/qmldesigner/settingspage.cpp
index 17c75a0285..df17f005a2 100644
--- a/src/plugins/qmldesigner/settingspage.cpp
+++ b/src/plugins/qmldesigner/settingspage.cpp
@@ -39,7 +39,8 @@ namespace Internal {
static QStringList puppetModes()
{
- static QStringList puppetModeList{"", "all", "editormode", "rendermode", "previewmode", "bakelightsmode"};
+ static QStringList puppetModeList{"", "all", "editormode", "rendermode", "previewmode",
+ "bakelightsmode", "import3dmode"};
return puppetModeList;
}
diff --git a/src/plugins/qmldesigner/shortcutmanager.cpp b/src/plugins/qmldesigner/shortcutmanager.cpp
index 833466a2aa..661ff3f271 100644
--- a/src/plugins/qmldesigner/shortcutmanager.cpp
+++ b/src/plugins/qmldesigner/shortcutmanager.cpp
@@ -232,12 +232,10 @@ void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContex
connect(Core::ICore::instance(), &Core::ICore::contextChanged, this, [&](const Core::Context &context) {
isMatBrowserActive = context.contains(Constants::C_QMLMATERIALBROWSER);
isAssetsLibraryActive = context.contains(Constants::C_QMLASSETSLIBRARY);
- isCollectionEditorActive = context.contains(Constants::C_QMLCOLLECTIONEDITOR);
if (!context.contains(Constants::C_QMLFORMEDITOR) && !context.contains(Constants::C_QMLEDITOR3D)
&& !context.contains(Constants::C_QMLNAVIGATOR)) {
- m_deleteAction.setEnabled(isMatBrowserActive || isAssetsLibraryActive
- || isCollectionEditorActive);
+ m_deleteAction.setEnabled(isMatBrowserActive || isAssetsLibraryActive);
m_cutAction.setEnabled(false);
m_copyAction.setEnabled(false);
m_pasteAction.setEnabled(false);
@@ -294,8 +292,6 @@ void ShortCutManager::deleteSelected()
actionManager.view()->emitCustomNotification("delete_selected_material");
else if (isAssetsLibraryActive)
actionManager.view()->emitCustomNotification("delete_selected_assets");
- else if (isCollectionEditorActive)
- actionManager.view()->emitCustomNotification("delete_selected_collection");
else if (currentDesignDocument())
currentDesignDocument()->deleteSelected();
}
diff --git a/src/plugins/qmldesigner/shortcutmanager.h b/src/plugins/qmldesigner/shortcutmanager.h
index 8714bb5fbc..70b019217c 100644
--- a/src/plugins/qmldesigner/shortcutmanager.h
+++ b/src/plugins/qmldesigner/shortcutmanager.h
@@ -64,7 +64,6 @@ private:
bool isMatBrowserActive = false;
bool isAssetsLibraryActive = false;
- bool isCollectionEditorActive = false;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesignerbase/QmlDesignerBase.json.in b/src/plugins/qmldesignerbase/QmlDesignerBase.json.in
index 27c62aeef6..a02264b63f 100644
--- a/src/plugins/qmldesignerbase/QmlDesignerBase.json.in
+++ b/src/plugins/qmldesignerbase/QmlDesignerBase.json.in
@@ -14,6 +14,6 @@
],
"Category" : "Qt Quick",
"Description" : "Provides support code for the qml designer and co..",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/qmldesignerbase/studio/studiostyle.cpp b/src/plugins/qmldesignerbase/studio/studiostyle.cpp
index 6924e40f7c..8bb92ccc88 100644
--- a/src/plugins/qmldesignerbase/studio/studiostyle.cpp
+++ b/src/plugins/qmldesignerbase/studio/studiostyle.cpp
@@ -29,7 +29,7 @@ inline QColor studioTextColor(bool enabled,
: Theme::DStextColor)
: Theme::DStextColorDisabled;
- return creatorTheme()->color(themePenColorId);
+ return creatorColor(themePenColorId);
}
inline QColor studioButtonBgColor(bool enabled,
@@ -51,7 +51,7 @@ inline QColor studioButtonBgColor(bool enabled,
)
: Theme::DScontrolBackgroundDisabled;
- return creatorTheme()->color(themePenColorId);
+ return creatorColor(themePenColorId);
}
inline QColor studioButtonOutlineColor(bool enabled,
@@ -67,7 +67,7 @@ inline QColor studioButtonOutlineColor(bool enabled,
: Theme::DScontrolOutline)
: Theme::DScontrolOutlineDisabled;
- return creatorTheme()->color(themePenColorId);
+ return creatorColor(themePenColorId);
}
inline bool anyParentsFocused(const QWidget *widget)
@@ -187,7 +187,7 @@ void StudioStyle::drawPrimitive(
case PE_FrameMenu:
case PE_PanelMenu:
if (isQmlEditorMenu(widget))
- painter->fillRect(option->rect, creatorTheme()->color(Theme::DSsubPanelBackground));
+ painter->fillRect(option->rect, creatorColor(Theme::DSsubPanelBackground));
else
Super::drawPrimitive(element, option, painter, widget);
break;
@@ -241,7 +241,7 @@ void StudioStyle::drawPrimitive(
}
// The separator color is currently the same as toolbar bg
- painter->fillRect(colorRect, creatorTheme()->color(Theme::DStoolbarBackground));
+ painter->fillRect(colorRect, creatorColor(Theme::DStoolbarBackground));
} break;
default: {
@@ -282,7 +282,7 @@ void StudioStyle::drawControl(
QStyleOptionMenuItem item = *mbi;
if (isActive) {
- painter->fillRect(item.rect, creatorTheme()->color(Theme::DSinteraction));
+ painter->fillRect(item.rect, creatorColor(Theme::DSinteraction));
}
forwardX += startMargin;
@@ -294,7 +294,7 @@ void StudioStyle::drawControl(
item.rect.right() - additionalMargin,
commonHeight);
- painter->setPen(creatorTheme()->color(Theme::DSstateSeparatorColor));
+ painter->setPen(creatorColor(Theme::DSstateSeparatorColor));
painter->drawLine(separatorLine);
item.text.clear();
painter->restore();
@@ -458,7 +458,7 @@ void StudioStyle::drawComplexControl(
: Theme::DSpopupBackground // Idle
: Theme::DSpopupBackground; // Disabled
- QColor frameColor = creatorTheme()->color(themeframeColor);
+ QColor frameColor = creatorColor(themeframeColor);
if ((option->subControls & SC_SliderGroove) && groove.isValid()) {
Theme::Color themeBgPlusColor = enabled
@@ -494,9 +494,9 @@ void StudioStyle::drawComplexControl(
painter->save();
painter->setPen(Qt::NoPen);
- painter->setBrush(creatorTheme()->color(themeBgPlusColor));
+ painter->setBrush(creatorColor(themeBgPlusColor));
painter->drawRoundedRect(plusRect, borderRadius, borderRadius);
- painter->setBrush(creatorTheme()->color(themeBgMinusColor));
+ painter->setBrush(creatorColor(themeBgMinusColor));
painter->drawRoundedRect(minusRect, borderRadius, borderRadius);
painter->restore();
}
@@ -509,7 +509,7 @@ void StudioStyle::drawComplexControl(
: Theme::DScontrolBackgroundDisabled;
painter->setBrush(Qt::NoBrush);
- painter->setPen(creatorTheme()->color(tickPen));
+ painter->setPen(creatorColor(tickPen));
int tickSize = proxy()->pixelMetric(PM_SliderTickmarkOffset, option, widget);
int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget);
int interval = slider->tickInterval;
@@ -577,7 +577,7 @@ void StudioStyle::drawComplexControl(
int halfSliderThickness = horizontal
? handle.width() / 2
: handle.height() / 2;
- painter->setBrush(creatorTheme()->color(handleColor));
+ painter->setBrush(creatorColor(handleColor));
painter->setPen(Qt::NoPen);
painter->drawRoundedRect(handle,
halfSliderThickness,
@@ -733,7 +733,7 @@ void StudioStyle::drawComplexControl(
bool enabled = scrollBar->state & QStyle::State_Enabled;
bool hovered = enabled && scrollBar->state & QStyle::State_MouseOver;
- QColor buttonColor = creatorTheme()->color(hovered ? Theme::DSscrollBarHandle
+ QColor buttonColor = creatorColor(hovered ? Theme::DSscrollBarHandle
: Theme::DSscrollBarHandle_idle);
QColor gradientStartColor = buttonColor.lighter(118);
QColor gradientStopColor = buttonColor;
@@ -753,12 +753,12 @@ void StudioStyle::drawComplexControl(
painter->save();
painter->setPen(Qt::NoPen);
if (hasTransientStyle) {
- QColor brushColor(creatorTheme()->color(Theme::DSscrollBarTrack));
+ QColor brushColor(creatorColor(Theme::DSscrollBarTrack));
brushColor.setAlpha(0.3 * 255);
painter->setBrush(QBrush(brushColor));
painter->drawRoundedRect(scrollBarGroove, 4, 4);
} else {
- painter->setBrush(QBrush(creatorTheme()->color(Theme::DSscrollBarTrack)));
+ painter->setBrush(QBrush(creatorColor(Theme::DSscrollBarTrack)));
painter->drawRect(rect);
}
painter->restore();
diff --git a/src/plugins/qmldesignerbase/studio/studiostyle_p.cpp b/src/plugins/qmldesignerbase/studio/studiostyle_p.cpp
index c1e1dece05..adfd08c6ef 100644
--- a/src/plugins/qmldesignerbase/studio/studiostyle_p.cpp
+++ b/src/plugins/qmldesignerbase/studio/studiostyle_p.cpp
@@ -31,7 +31,7 @@ inline QColor studioTextColor(bool enabled, bool active, bool checked)
: Theme::DStextColor)
: Theme::DStextColorDisabled;
- return creatorTheme()->color(themePenColorId);
+ return creatorColor(themePenColorId);
}
}; // namespace
@@ -39,7 +39,7 @@ StudioStylePrivate::StudioStylePrivate(StudioStyle *q)
: QObject(q)
, q(q)
{
- auto color = [](Theme::Color c) { return creatorTheme()->color(c); };
+ auto color = [](Theme::Color c) { return creatorColor(c); };
{
stdPalette.setColorGroup(QPalette::Disabled, // group
diff --git a/src/plugins/qmldesignerlite/QmlDesignerLite.json.in b/src/plugins/qmldesignerlite/QmlDesignerLite.json.in
index 75410dfd58..80944b3087 100644
--- a/src/plugins/qmldesignerlite/QmlDesignerLite.json.in
+++ b/src/plugins/qmldesignerlite/QmlDesignerLite.json.in
@@ -15,7 +15,7 @@
"Category" : "Qt Quick",
"Description" : "Qml Designer Lite",
"LongDescription": "Qml Designer Lite is a lightweight version of Qt Design Studio, providing a subset of the features of the full Qt Design Studio.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
"Experimental": true,
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/qmljseditor/QmlJSEditor.json.in b/src/plugins/qmljseditor/QmlJSEditor.json.in
index c05d4efa80..a0738a59ed 100644
--- a/src/plugins/qmljseditor/QmlJSEditor.json.in
+++ b/src/plugins/qmljseditor/QmlJSEditor.json.in
@@ -14,6 +14,6 @@
],
"Category" : "Qt Quick",
"Description" : "Editor for QML and JavaScript.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/qmljseditor/qmljscomponentfromobjectdef.cpp b/src/plugins/qmljseditor/qmljscomponentfromobjectdef.cpp
index 134baa5827..ce6473b988 100644
--- a/src/plugins/qmljseditor/qmljscomponentfromobjectdef.cpp
+++ b/src/plugins/qmljseditor/qmljscomponentfromobjectdef.cpp
@@ -187,10 +187,7 @@ public:
for (const QString &property : std::as_const(result))
replacement += property + QLatin1String(": ") + propertyReader.readAstValue(property) + QLatin1Char('\n');
- Utils::ChangeSet changes;
- changes.replace(start, end, replacement);
- currentFile->setChangeSet(changes);
- currentFile->apply();
+ currentFile->apply(ChangeSet::makeReplace(start, end, replacement));
Core::IVersionControl *versionControl = Core::VcsManager::findVersionControlForDirectory(
path);
diff --git a/src/plugins/qmljseditor/qmljseditingsettingspage.cpp b/src/plugins/qmljseditor/qmljseditingsettingspage.cpp
index f12d1ea57a..644c0d1d50 100644
--- a/src/plugins/qmljseditor/qmljseditingsettingspage.cpp
+++ b/src/plugins/qmljseditor/qmljseditingsettingspage.cpp
@@ -21,6 +21,7 @@
#include <QCheckBox>
#include <QComboBox>
+#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QMenu>
@@ -104,7 +105,7 @@ void QmlJsEditingSettings::fromSettings(QtcSettings *settings)
= settings->value(AUTO_FORMAT_ONLY_CURRENT_PROJECT, QVariant(false)).toBool();
m_foldAuxData = settings->value(FOLD_AUX_DATA, QVariant(true)).toBool();
m_uiQmlOpenMode = settings->value(UIQML_OPEN_MODE, "").toString();
- m_qmllsSettings.useQmlls = settings->value(USE_QMLLS, QVariant(false)).toBool();
+ m_qmllsSettings.useQmlls = settings->value(USE_QMLLS, QVariant(true)).toBool();
m_qmllsSettings.useLatestQmlls = settings->value(USE_LATEST_QMLLS, QVariant(false)).toBool();
m_qmllsSettings.disableBuiltinCodemodel
= settings->value(DISABLE_BUILTIN_CODEMODEL, QVariant(false)).toBool();
@@ -396,7 +397,7 @@ public:
uiQmlOpenComboBox->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
uiQmlOpenComboBox->setSizeAdjustPolicy(QComboBox::QComboBox::AdjustToContents);
- useQmlls = new QCheckBox(Tr::tr("Enable QML Language Server (EXPERIMENTAL!)"));
+ useQmlls = new QCheckBox(Tr::tr("Enable QML Language Server"));
useQmlls->setChecked(s.qmllsSettings().useQmlls);
disableBuiltInCodemodel = new QCheckBox(
Tr::tr("Use QML Language Server advanced features (renaming, find usages and co.) "
diff --git a/src/plugins/qmljseditor/qmljsquickfixes.cpp b/src/plugins/qmljseditor/qmljsquickfixes.cpp
index 126361451e..5bb077e814 100644
--- a/src/plugins/qmljseditor/qmljsquickfixes.cpp
+++ b/src/plugins/qmljseditor/qmljsquickfixes.cpp
@@ -70,8 +70,7 @@ public:
changes.insert(currentFile->startOf(_objectInitializer->rbraceToken),
QLatin1String("\n"));
- currentFile->setChangeSet(changes);
- currentFile->apply();
+ currentFile->apply(changes);
}
};
@@ -117,11 +116,9 @@ public:
const QmlJSRefactoringChanges &,
const QString &) override
{
- Utils::ChangeSet changes;
- const int insertLoc = _message.location.begin() - _message.location.startColumn + 1;
- changes.insert(insertLoc, QString::fromLatin1("// %1\n").arg(_message.suppressionString()));
- currentFile->setChangeSet(changes);
- currentFile->apply();
+ currentFile->apply(Utils::ChangeSet::makeInsert(
+ _message.location.begin() - _message.location.startColumn + 1,
+ QString::fromLatin1("// %1\n").arg(_message.suppressionString())));
}
};
diff --git a/src/plugins/qmljseditor/qmljswrapinloader.cpp b/src/plugins/qmljseditor/qmljswrapinloader.cpp
index bf5fd44d4a..5f6b474ad6 100644
--- a/src/plugins/qmljseditor/qmljswrapinloader.cpp
+++ b/src/plugins/qmljseditor/qmljswrapinloader.cpp
@@ -146,8 +146,7 @@ public:
" id: %2\n"
" sourceComponent: %1\n"
"}\n").arg(componentId, loaderId));
- currentFile->setChangeSet(changes);
- currentFile->apply();
+ currentFile->apply(changes);
}
};
diff --git a/src/plugins/qmljseditor/qmloutlinemodel.cpp b/src/plugins/qmljseditor/qmloutlinemodel.cpp
index 456789f645..9f15680774 100644
--- a/src/plugins/qmljseditor/qmloutlinemodel.cpp
+++ b/src/plugins/qmljseditor/qmloutlinemodel.cpp
@@ -897,10 +897,8 @@ void QmlOutlineModel::reparentNodes(QmlOutlineItem *targetItem, int row, QList<Q
changedRanges << range;
}
- QmlJSRefactoringChanges refactoring(ModelManagerInterface::instance(), m_semanticInfo.snapshot);
- TextEditor::RefactoringFilePtr file = refactoring.file(m_semanticInfo.document->fileName());
- file->setChangeSet(changeSet);
- file->apply();
+ QmlJSRefactoringChanges(ModelManagerInterface::instance(), m_semanticInfo.snapshot)
+ .file(m_semanticInfo.document->fileName())->apply(changeSet);
}
void QmlOutlineModel::moveObjectMember(AST::Node *toMove,
diff --git a/src/plugins/qmljstools/QmlJSTools.json.in b/src/plugins/qmljstools/QmlJSTools.json.in
index 0edfb8a7bf..9e80e06c79 100644
--- a/src/plugins/qmljstools/QmlJSTools.json.in
+++ b/src/plugins/qmljstools/QmlJSTools.json.in
@@ -14,7 +14,7 @@
],
"Category" : "Qt Quick",
"Description" : "Tools for analyzing Qml/JS code.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/qmljstools/qmljsfunctionfilter.cpp b/src/plugins/qmljstools/qmljsfunctionfilter.cpp
index 0453d11b9c..c6f8dfa4fd 100644
--- a/src/plugins/qmljstools/qmljsfunctionfilter.cpp
+++ b/src/plugins/qmljstools/qmljsfunctionfilter.cpp
@@ -5,8 +5,6 @@
#include "qmljslocatordata.h"
#include "qmljstoolstr.h"
-#include <extensionsystem/pluginmanager.h>
-
#include <utils/algorithm.h>
#include <utils/async.h>
@@ -81,7 +79,6 @@ LocatorMatcherTasks QmlJSFunctionsFilter::matchers()
Storage<LocatorStorage> storage;
const auto onSetup = [storage, entries = m_data->entries()](Async<void> &async) {
- async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
async.setConcurrentCallData(matches, *storage, entries);
};
diff --git a/src/plugins/qmlpreview/QmlPreview.json.in b/src/plugins/qmlpreview/QmlPreview.json.in
index d053c4040b..cec78d9806 100644
--- a/src/plugins/qmlpreview/QmlPreview.json.in
+++ b/src/plugins/qmlpreview/QmlPreview.json.in
@@ -14,6 +14,6 @@
],
"Category" : "Qt Quick",
"Description" : "Qml Preview Plugin.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/qmlprofiler/QmlProfiler.json.in b/src/plugins/qmlprofiler/QmlProfiler.json.in
index 45fdb6e8ff..e92191366b 100644
--- a/src/plugins/qmlprofiler/QmlProfiler.json.in
+++ b/src/plugins/qmlprofiler/QmlProfiler.json.in
@@ -14,6 +14,6 @@
],
"Category" : "Qt Quick",
"Description" : "Qml Profiler Plugin.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/qmlprofiler/flamegraphview.cpp b/src/plugins/qmlprofiler/flamegraphview.cpp
index ad26f2156a..eae418fa25 100644
--- a/src/plugins/qmlprofiler/flamegraphview.cpp
+++ b/src/plugins/qmlprofiler/flamegraphview.cpp
@@ -31,7 +31,7 @@ FlameGraphView::FlameGraphView(QmlProfilerModelManager *manager, QWidget *parent
m_content->rootContext()->setContextProperty(QStringLiteral("flameGraphModel"), m_model);
m_content->setSource(QUrl(QStringLiteral(
"qrc:/qt/qml/QtCreator/QmlProfiler/QmlProfilerFlameGraphView.qml")));
- m_content->setClearColor(Utils::creatorTheme()->color(Utils::Theme::Timeline_BackgroundColor1));
+ m_content->setClearColor(Utils::creatorColor(Utils::Theme::Timeline_BackgroundColor1));
m_content->setResizeMode(QQuickWidget::SizeRootObjectToView);
m_content->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
diff --git a/src/plugins/qmlprofiler/qmlprofilerbindingloopsrenderpass.cpp b/src/plugins/qmlprofiler/qmlprofilerbindingloopsrenderpass.cpp
index 0db02375ae..7cea73b254 100644
--- a/src/plugins/qmlprofiler/qmlprofilerbindingloopsrenderpass.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerbindingloopsrenderpass.cpp
@@ -285,7 +285,7 @@ BindingLoopMaterialShader::BindingLoopMaterialShader()
static QColor bindingLoopsColor()
{
- return Utils::creatorTheme()->color(Utils::Theme::Timeline_HighlightColor);
+ return Utils::creatorColor(Utils::Theme::Timeline_HighlightColor);
}
bool BindingLoopMaterialShader::updateUniformData(RenderState &state, QSGMaterial *, QSGMaterial *)
diff --git a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp
index 4d3a73d4dc..1d202a64a6 100644
--- a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp
@@ -201,7 +201,7 @@ QVariant QmlProfilerStatisticsModel::dataForMainEntry(const QModelIndex &index,
case TypeIdRole:
return s_mainEntryTypeId;
case Qt::ForegroundRole:
- return Utils::creatorTheme()->color(Utils::Theme::Timeline_TextColor);
+ return Utils::creatorColor(Utils::Theme::Timeline_TextColor);
case SortRole:
switch (index.column()) {
case MainTimeInPercent:
@@ -279,8 +279,8 @@ QVariant QmlProfilerStatisticsModel::data(const QModelIndex &index, int role) co
}
case Qt::ForegroundRole:
return (stats.recursive > 0 || m_notes.contains(typeIndex))
- ? Utils::creatorTheme()->color(Utils::Theme::Timeline_HighlightColor)
- : Utils::creatorTheme()->color(Utils::Theme::Timeline_TextColor);
+ ? Utils::creatorColor(Utils::Theme::Timeline_HighlightColor)
+ : Utils::creatorColor(Utils::Theme::Timeline_TextColor);
case SortRole:
switch (index.column()) {
case MainLocation:
@@ -547,7 +547,7 @@ QVariant QmlProfilerStatisticsRelativesModel::dataForMainEntry(qint64 totalDurat
case TypeIdRole:
return QmlProfilerStatisticsModel::s_mainEntryTypeId;
case Qt::ForegroundRole:
- return Utils::creatorTheme()->color(Utils::Theme::Timeline_TextColor);
+ return Utils::creatorColor(Utils::Theme::Timeline_TextColor);
case SortRole:
if (column == RelativeTotalTime)
return totalDuration;
@@ -598,8 +598,8 @@ QVariant QmlProfilerStatisticsRelativesModel::data(const QModelIndex &index, int
return stats.isRecursive ? Tr::tr("called recursively") : QString();
case Qt::ForegroundRole:
return stats.isRecursive
- ? Utils::creatorTheme()->color(Utils::Theme::Timeline_HighlightColor)
- : Utils::creatorTheme()->color(Utils::Theme::Timeline_TextColor);
+ ? Utils::creatorColor(Utils::Theme::Timeline_HighlightColor)
+ : Utils::creatorColor(Utils::Theme::Timeline_TextColor);
case SortRole:
switch (index.column()) {
case RelativeLocation:
diff --git a/src/plugins/qmlprofiler/tests/localqmlprofilerrunner_test.cpp b/src/plugins/qmlprofiler/tests/localqmlprofilerrunner_test.cpp
index 7799c96028..a417775dbf 100644
--- a/src/plugins/qmlprofiler/tests/localqmlprofilerrunner_test.cpp
+++ b/src/plugins/qmlprofiler/tests/localqmlprofilerrunner_test.cpp
@@ -43,7 +43,7 @@ void LocalQmlProfilerRunnerTest::testRunner()
serverUrl.setPath("invalid");
runControl = new RunControl(ProjectExplorer::Constants::QML_PROFILER_RUN_MODE);
- runControl->setCommandLine({"\\-/|\\-/", {}});
+ runControl->setCommandLine(CommandLine{"\\-/|\\-/"});
profiler = new LocalQmlProfilerSupport(runControl, serverUrl);
@@ -109,7 +109,7 @@ void LocalQmlProfilerRunnerTest::testRunner()
serverUrl.clear();
serverUrl = Utils::urlFromLocalHostAndFreePort();
runControl = new RunControl(ProjectExplorer::Constants::QML_PROFILER_RUN_MODE);
- runControl->setCommandLine({app, {}});
+ runControl->setCommandLine(CommandLine{app});
profiler = new LocalQmlProfilerSupport(runControl, serverUrl);
connectRunner();
runControl->initiateStart();
diff --git a/src/plugins/qmlprojectmanager/.clang-format b/src/plugins/qmlprojectmanager/.clang-format
new file mode 100644
index 0000000000..366f82f76f
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/.clang-format
@@ -0,0 +1,50 @@
+Language: Cpp
+AccessModifierOffset: -4
+AlignEscapedNewlines: DontAlign
+AllowShortFunctionsOnASingleLine: Inline
+AlwaysBreakTemplateDeclarations: true # use with clang 19
+BinPackArguments: false
+BinPackParameters: false
+BraceWrapping:
+ AfterClass: true
+ AfterFunction: true
+ AfterStruct: true
+ SplitEmptyFunction: false
+ SplitEmptyRecord: false
+ SplitEmptyNamespace: false
+BreakBeforeBinaryOperators: All
+BreakBeforeBraces: Custom
+BreakConstructorInitializers: BeforeComma
+BreakInheritanceList: AfterComma
+# BreakTemplateDeclarations: Yes # use with clang 19
+ColumnLimit: 100
+IncludeCategories:
+ - Regex: 'Q.*'
+ Priority: 8
+ CaseSensitive: true
+IndentPPDirectives: AfterHash
+IndentWidth: 4
+KeepEmptyLinesAtTheStartOfBlocks: false
+# Do not add QT_BEGIN_NAMESPACE/QT_END_NAMESPACE as this will indent lines in between.
+ObjCBlockIndentWidth: 4
+PPIndentWidth: 2
+PackConstructorInitializers: Never
+PenaltyBreakAssignment: 500
+PenaltyBreakBeforeFirstCallParameter: 150
+PenaltyBreakComment: 500
+PenaltyBreakFirstLessLess: 400
+PenaltyBreakString: 600
+PenaltyExcessCharacter: 7
+PenaltyReturnTypeOnItsOwnLine: 300
+QualifierAlignment: Custom
+QualifierOrder: ['friend', 'inline', 'static', 'constexpr', 'const', 'type']
+ReferenceAlignment: Right
+ReflowComments: false
+SeparateDefinitionBlocks: Always
+SortUsingDeclarations: Lexicographic
+SpaceAfterCStyleCast: true
+SpaceAfterTemplateKeyword: false
+SpaceBeforeParens: ControlStatementsExceptControlMacros
+SpacesInContainerLiterals: false
+StatementAttributeLikeMacros: [emit]
+TabWidth: 4
diff --git a/src/plugins/qmlprojectmanager/QmlProjectManager.json.in b/src/plugins/qmlprojectmanager/QmlProjectManager.json.in
index 5df33015f4..18388275f3 100644
--- a/src/plugins/qmlprojectmanager/QmlProjectManager.json.in
+++ b/src/plugins/qmlprojectmanager/QmlProjectManager.json.in
@@ -14,7 +14,7 @@
],
"Category" : "Qt Quick",
"Description" : "Qt Quick support",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp
index 95cc859af5..82ba1fc2e4 100644
--- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp
+++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp
@@ -14,7 +14,12 @@ const static QStringList imageFilesFilter{QStringLiteral("*.jpeg"),
QStringLiteral("*.png"),
QStringLiteral("*.svg"),
QStringLiteral("*.hdr"),
- QStringLiteral("*.ktx")};
+ QStringLiteral("*.ktx"),
+ QStringLiteral("*.bmp"),
+ QStringLiteral("*.ttf"),
+ QStringLiteral("*.tiff"),
+ QStringLiteral("*.webp"),
+ QStringLiteral("*.gif")};
QString jsonValueToString(const QJsonValue &val, int indentationLevel, bool indented);
diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp
index eb6e6de177..5229d486ce 100644
--- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp
+++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp
@@ -79,7 +79,7 @@ void QmlProjectItem::setupFileFilters()
connect(fileFilterItem.get(),
&FileFilterItem::filesChanged,
this,
- &QmlProjectItem::qmlFilesChanged);
+ &QmlProjectItem::filesChanged);
#endif
m_content.push_back(std::move(fileFilterItem));
};
@@ -105,10 +105,7 @@ void QmlProjectItem::setupFileFilters()
fileFilterItem->setDefaultDirectory(m_projectFile.parentDir().toString());
fileFilterItem->setDirectory(groupDir.toString());
#ifndef TESTS_ENABLED_QMLPROJECTITEM
- connect(fileFilterItem.get(),
- &FileFilterItem::filesChanged,
- this,
- &QmlProjectItem::qmlFilesChanged);
+ connect(fileFilterItem.get(), &FileFilterItem::filesChanged, this, &QmlProjectItem::filesChanged);
#endif
m_content.push_back(std::move(fileFilterItem));
};
diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h
index 7d57ad2e60..5d0b520f14 100644
--- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h
+++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h
@@ -92,7 +92,7 @@ public:
void setEnableCMakeGeneration(bool enable);
signals:
- void qmlFilesChanged(const QSet<QString> &, const QSet<QString> &);
+ void filesChanged(const QSet<QString> &, const QSet<QString> &);
private:
typedef QSharedPointer<QmlProjectItem> ShrdPtrQPI;
diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp
index 00e501d6f8..97b540ad7e 100644
--- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp
+++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp
@@ -6,8 +6,9 @@
#include "../qmlprojectconstants.h"
#include "../qmlprojectmanagertr.h"
#include "../qmlproject.h"
+#include "projectitem/qmlprojectitem.h"
+#include "projectnode/qmlprojectnodes.h"
-#include <QtCore5Compat/qtextcodec.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <coreplugin/actionmanager/actioncontainer.h>
@@ -30,18 +31,18 @@
#include <projectexplorer/kitaspects.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
-#include "projectexplorer/projectmanager.h"
-#include "projectitem/qmlprojectitem.h"
-#include "projectnode/qmlprojectnodes.h"
-
-#include "utils/algorithm.h"
-#include "utils/qtcassert.h"
+#include <utils/algorithm.h>
+#include <utils/filepath.h>
+#include <utils/filesystemwatcher.h>
+#include <utils/qtcassert.h>
-#include "texteditor/textdocument.h"
+#include <texteditor/textdocument.h>
#include <QAction>
+#include <QtCore5Compat/qtextcodec.h>
using namespace ProjectExplorer;
namespace QmlProjectManager {
@@ -141,8 +142,6 @@ void QmlBuildSystem::registerMenuButtons()
//wip:
bool QmlBuildSystem::updateProjectFile()
{
- qDebug() << "debug#1-mainfilepath" << mainFilePath();
-
QFile file(mainFilePath().fileName().append("project-test"));
if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
qCritical() << "Cannot open Qml Project file for editing!";
@@ -220,17 +219,56 @@ void QmlBuildSystem::refresh(RefreshOptions options)
void QmlBuildSystem::initProjectItem()
{
m_projectItem.reset(new QmlProjectItem{projectFilePath()});
- connect(m_projectItem.get(),
- &QmlProjectItem::qmlFilesChanged,
- this,
- &QmlBuildSystem::refreshFiles);
- connect(m_projectItem.get(),
- &QmlProjectItem::qmlFilesChanged,
+ connect(m_projectItem.data(), &QmlProjectItem::filesChanged, this, &QmlBuildSystem::refreshFiles);
+ connect(m_projectItem.data(),
+ &QmlProjectItem::filesChanged,
m_cmakeGen,
&GenerateCmake::CMakeGenerator::update);
m_cmakeGen->setEnabled(m_projectItem->enableCMakeGeneration());
+
+ initMcuProjectItems();
+}
+
+void QmlBuildSystem::initMcuProjectItems()
+{
+ m_mcuProjectItems.clear();
+ m_mcuProjectFilesWatcher.clear();
+
+ Utils::FilePath projectDir = projectFilePath().parentDir();
+ // traverse the project dir and find all other mcu projects (.qmlproject files) in the project tree
+ // and add them to the m_mcuProjectItems vector
+ QDirIterator it(projectDir.toFSPathString(), QDir::Files, QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ it.next();
+ if (it.fileInfo().suffix() == "qmlproject" && it.filePath() != projectFilePath().toString()) {
+ auto qmlProjectItem = QSharedPointer<QmlProjectItem>(
+ new QmlProjectItem{Utils::FilePath::fromString(it.filePath())});
+
+ m_mcuProjectItems.append(qmlProjectItem);
+ connect(qmlProjectItem.data(),
+ &QmlProjectItem::filesChanged,
+ this,
+ &QmlBuildSystem::refreshFiles);
+ connect(qmlProjectItem.data(),
+ &QmlProjectItem::filesChanged,
+ m_cmakeGen,
+ &GenerateCmake::CMakeGenerator::update);
+
+ m_mcuProjectFilesWatcher.addFile(it.filePath(),
+ Utils::FileSystemWatcher::WatchModifiedDate);
+
+ connect(&m_mcuProjectFilesWatcher,
+ &Utils::FileSystemWatcher::fileChanged,
+ this,
+ [this](const QString &file) {
+ Q_UNUSED(file)
+ initMcuProjectItems();
+ refresh(RefreshOptions::Files);
+ });
+ }
+ }
}
void QmlBuildSystem::parseProjectFiles()
@@ -239,7 +277,6 @@ void QmlBuildSystem::parseProjectFiles()
modelManager->updateSourceFiles(m_projectItem->files(), true);
}
-
const QString mainFileName = m_projectItem->mainFile();
if (!mainFileName.isEmpty()) {
Utils::FilePath mainFilePath = canonicalProjectDir().resolvePath(mainFileName);
@@ -265,6 +302,16 @@ void QmlBuildSystem::generateProjectTree()
: FileNode::fileTypeForFileName(file);
newRoot->addNestedNode(std::make_unique<FileNode>(file, fileType));
}
+
+ for (const auto &mcuProjectItem : m_mcuProjectItems) {
+ for (const auto &file : mcuProjectItem->files()) {
+ // newRoot->addNestedNode(std::make_unique<FileNode>(file, FileType::Project));
+ const FileType fileType = (file == projectFilePath())
+ ? FileType::Project
+ : FileNode::fileTypeForFileName(file);
+ newRoot->addNestedNode(std::make_unique<FileNode>(file, fileType));
+ }
+ }
newRoot->addNestedNode(std::make_unique<FileNode>(projectFilePath(), FileType::Project));
setRootProjectNode(std::move(newRoot));
@@ -534,7 +581,7 @@ void QmlBuildSystem::refreshFiles(const QSet<QString> & /*added*/, const QSet<QS
QVariant QmlBuildSystem::additionalData(Utils::Id id) const
{
if (id == Constants::customFileSelectorsData)
- return customFileSelectors();
+ return fileSelectors();
if (id == Constants::supportedLanguagesData)
return supportedLanguages();
if (id == Constants::primaryLanguageData)
@@ -547,8 +594,6 @@ QVariant QmlBuildSystem::additionalData(Utils::Id id) const
return qt6Project();
if (id == Constants::mainFilePath)
return mainFilePath().toString();
- if (id == Constants::customImportPaths)
- return customImportPaths();
if (id == Constants::canonicalProjectDir)
return canonicalProjectDir().toString();
return {};
@@ -632,12 +677,7 @@ Utils::EnvironmentItems QmlBuildSystem::environment() const
return m_projectItem->environment();
}
-QStringList QmlBuildSystem::customImportPaths() const
-{
- return m_projectItem->importPaths();
-}
-
-QStringList QmlBuildSystem::customFileSelectors() const
+QStringList QmlBuildSystem::fileSelectors() const
{
return m_projectItem->fileSelectors();
}
@@ -682,7 +722,7 @@ QStringList QmlBuildSystem::importPaths() const
return m_projectItem->importPaths();
}
-QStringList QmlBuildSystem::absoluteImportPaths()
+QStringList QmlBuildSystem::absoluteImportPaths() const
{
return Utils::transform<QStringList>(m_projectItem->importPaths(), [&](const QString &importPath) {
Utils::FilePath filePath = Utils::FilePath::fromString(importPath);
@@ -692,11 +732,6 @@ QStringList QmlBuildSystem::absoluteImportPaths()
});
}
-Utils::FilePaths QmlBuildSystem::files() const
-{
- return m_projectItem->files();
-}
-
QString QmlBuildSystem::versionQt() const
{
return m_projectItem->versionQt();
diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h
index e3f9b99adb..d91f60cdd1 100644
--- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h
+++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h
@@ -6,7 +6,9 @@
#pragma once
#include "../qmlprojectmanager_global.h"
+
#include <projectexplorer/buildsystem.h>
+#include <utils/filesystemwatcher.h>
#include "qmlprojectmanager/cmakegen/cmakegenerator.h"
@@ -72,9 +74,8 @@ public:
Utils::EnvironmentItems environment() const;
QStringList importPaths() const;
- QStringList absoluteImportPaths();
- QStringList customImportPaths() const;
- QStringList customFileSelectors() const;
+ QStringList absoluteImportPaths() const;
+ QStringList fileSelectors() const;
bool multilanguageSupport() const;
QStringList supportedLanguages() const;
@@ -91,7 +92,6 @@ public:
QStringList shaderToolArgs() const;
QStringList shaderToolFiles() const;
- Utils::FilePaths files() const;
QString versionQt() const;
QString versionQtQuick() const;
@@ -117,10 +117,15 @@ private:
const Utils::FilePath &mainFilePath,
const QString &oldFile);
+ // this is the main project item
QSharedPointer<QmlProjectItem> m_projectItem;
+ // these are the mcu project items which can be found in the project tree
+ QList<QSharedPointer<QmlProjectItem>> m_mcuProjectItems;
+ Utils::FileSystemWatcher m_mcuProjectFilesWatcher;
bool m_blockFilesUpdate = false;
void initProjectItem();
+ void initMcuProjectItems();
void parseProjectFiles();
void generateProjectTree();
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp
index 659c544ebd..9ae576ec35 100644
--- a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp
+++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp
@@ -17,8 +17,9 @@
#include "coreplugin/actionmanager/actioncontainer.h"
#include <QDirIterator>
-#include <QRegularExpression>
+#include <QFileInfo>
#include <QMenu>
+#include <QRegularExpression>
#include <set>
@@ -87,8 +88,8 @@ void CMakeGenerator::updateMenuAction()
CMakeGenerator::CMakeGenerator(QmlBuildSystem *bs, QObject *parent)
: QObject(parent)
- , m_root(std::make_shared<Node>())
, m_buildSystem(bs)
+ , m_root(std::make_shared<Node>())
{}
const QmlProject *CMakeGenerator::qmlProject() const
@@ -170,6 +171,9 @@ void CMakeGenerator::update(const QSet<QString> &added, const QSet<QString> &rem
std::set<NodePtr> dirtyModules;
for (const QString &add : added) {
const Utils::FilePath path = Utils::FilePath::fromString(add);
+ if (ignore(path.parentDir()))
+ continue;
+
if (auto node = findOrCreateNode(m_root, path.parentDir())) {
insertFile(node, path);
if (auto module = findModuleFor(node))
@@ -208,10 +212,28 @@ bool CMakeGenerator::isResource(const Utils::FilePath &path) const
return suffixes.contains(path.suffix(), Qt::CaseInsensitive);
}
-bool CMakeGenerator::ignoreFile(const Utils::FilePath &path) const
+bool CMakeGenerator::ignore(const Utils::FilePath &path) const
{
- static const QStringList suffixes = { "hints" };
- return suffixes.contains(path.suffix(), Qt::CaseInsensitive);
+ if (path.isFile()) {
+ static const QStringList suffixes = { "hints" };
+ return suffixes.contains(path.suffix(), Qt::CaseInsensitive);
+ } else if (path.isDir()) {
+ if (!m_root->dir.exists())
+ return true;
+
+ static const QStringList fileNames = { "CMakeCache.txt", "build.ninja" };
+
+ Utils::FilePath dir = path;
+ while (dir.isChildOf(m_root->dir)) {
+ for (const QString& fileName : fileNames) {
+ Utils::FilePath checkFile = dir.pathAppended(fileName);
+ if (checkFile.exists())
+ return true;
+ }
+ dir = dir.parentDir();
+ }
+ }
+ return false;
}
void CMakeGenerator::createCMakeFiles(const NodePtr &node) const
@@ -308,8 +330,7 @@ NodePtr CMakeGenerator::findOrCreateNode(NodePtr &node, const Utils::FilePath &p
};
const Utils::FilePath relative = path.relativeChildPath(node->dir);
- const QChar separator = relative.pathComponentSeparator();
- const QList<QStringView> components = relative.pathView().split(separator);
+ const QList<QStringView> components = relative.pathView().split('/');
NodePtr lastNode = node;
for (const auto &comp : components) {
@@ -445,6 +466,9 @@ void CMakeGenerator::parseNodeTree(NodePtr &generatorNode,
{
for (const auto *childNode : folderNode->nodes()) {
if (const auto *subFolderNode = childNode->asFolderNode()) {
+ if (ignore(subFolderNode->filePath()))
+ continue;
+
NodePtr childGeneratorNode = std::make_shared<Node>();
childGeneratorNode->parent = generatorNode;
childGeneratorNode->dir = subFolderNode->filePath();
@@ -499,7 +523,10 @@ void CMakeGenerator::compareWithFileSystem(const NodePtr &node) const
while (iter.hasNext()) {
auto next = Utils::FilePath::fromString(iter.next());
- if (isResource(next) && !findFile(next) && !ignoreFile(next))
+ if (ignore(next.parentDir()))
+ continue;
+
+ if (isResource(next) && !findFile(next) && !ignore(next))
files.push_back(next);
}
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h
index 6077166d5b..3af3405879 100644
--- a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h
+++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h
@@ -47,7 +47,7 @@ public:
private:
bool isQml(const Utils::FilePath &path) const;
bool isResource(const Utils::FilePath &path) const;
- bool ignoreFile(const Utils::FilePath &path) const;
+ bool ignore(const Utils::FilePath &path) const;
void createCMakeFiles(const NodePtr &node) const;
void createSourceFiles() const;
@@ -70,12 +70,12 @@ private:
void compareWithFileSystem(const NodePtr &node) const;
bool m_enabled = false;
+ QmlBuildSystem *m_buildSystem = nullptr;
CMakeWriter::Ptr m_writer = {};
QString m_projectName = {};
NodePtr m_root = {};
QStringList m_moduleNames = {};
- QmlBuildSystem *m_buildSystem = nullptr;
};
} // namespace GenerateCmake
diff --git a/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl
index a9f20243a6..5640b85844 100644
--- a/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl
+++ b/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl
@@ -3,6 +3,7 @@
message("Building designer components.")
+set(QT_QDS_COMPONENTS_NOWARN on)
set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml")
include(FetchContent)
@@ -17,6 +18,7 @@ FetchContent_Populate(ds)
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
QuickStudioComponentsplugin
+ QuickStudioDesignEffectsplugin
QuickStudioEffectsplugin
QuickStudioApplicationplugin
FlowViewplugin
diff --git a/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp b/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp
index 7a35d9f15e..983b712633 100644
--- a/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp
+++ b/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp
@@ -56,7 +56,7 @@ QmlMainFileAspect::~QmlMainFileAspect()
delete m_fileListCombo;
}
-void QmlMainFileAspect::addToLayout(Layouting::LayoutItem &parent)
+void QmlMainFileAspect::addToLayout(Layouting::Layout &parent)
{
QTC_ASSERT(!m_fileListCombo, delete m_fileListCombo);
m_fileListCombo = new QComboBox;
diff --git a/src/plugins/qmlprojectmanager/qmlmainfileaspect.h b/src/plugins/qmlprojectmanager/qmlmainfileaspect.h
index 4a64055b6c..fd875dde79 100644
--- a/src/plugins/qmlprojectmanager/qmlmainfileaspect.h
+++ b/src/plugins/qmlprojectmanager/qmlmainfileaspect.h
@@ -42,7 +42,7 @@ public:
Utils::FilePath currentFile;
};
- void addToLayout(Layouting::LayoutItem &parent) final;
+ void addToLayout(Layouting::Layout &parent) final;
void toMap(Utils::Store &map) const final;
void fromMap(const Utils::Store &map) final;
diff --git a/src/plugins/qmlprojectmanager/qmlprojectconstants.h b/src/plugins/qmlprojectmanager/qmlprojectconstants.h
index a937979954..ad77948105 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectconstants.h
+++ b/src/plugins/qmlprojectmanager/qmlprojectconstants.h
@@ -13,7 +13,6 @@ const char customQtForMCUs[] = "CustomQtForMCUs";
const char customQt6Project[] = "CustomQt6Project";
const char mainFilePath[] = "MainFilePath";
-const char customImportPaths[] = "CustomImportPaths";
const char canonicalProjectDir[] ="CanonicalProjectDir";
const char enviromentLaunchedQDS[] = "QTC_LAUNCHED_QDS";
diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
index 5742b945a8..503738eba5 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
+++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
@@ -97,12 +97,12 @@ QmlProjectRunConfiguration::QmlProjectRunConfiguration(Target *target, Id id)
// arguments from .qmlproject file
const QmlBuildSystem *bs = qobject_cast<QmlBuildSystem *>(target->buildSystem());
- for (const QString &importPath : bs->customImportPaths()) {
+ for (const QString &importPath : bs->absoluteImportPaths()) {
cmd.addArg("-I");
- cmd.addArg(bs->targetDirectory().pathAppended(importPath).path());
+ cmd.addArg(importPath);
}
- for (const QString &fileSelector : bs->customFileSelectors()) {
+ for (const QString &fileSelector : bs->fileSelectors()) {
cmd.addArg("-S");
cmd.addArg(fileSelector);
}
diff --git a/src/plugins/qnx/Qnx.json.in b/src/plugins/qnx/Qnx.json.in
index 0340d3b7dc..c2658de2ae 100644
--- a/src/plugins/qnx/Qnx.json.in
+++ b/src/plugins/qnx/Qnx.json.in
@@ -14,6 +14,6 @@
],
"Category" : "Device Support",
"Description" : "Adds support for QNX to Qt Creator.",
- "Url" : "http://www.blackberry.com",
+ "Url" : "https://www.blackberry.com",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/qnx/slog2inforunner.cpp b/src/plugins/qnx/slog2inforunner.cpp
index e717e56e33..b7ed28ffd2 100644
--- a/src/plugins/qnx/slog2inforunner.cpp
+++ b/src/plugins/qnx/slog2inforunner.cpp
@@ -35,7 +35,7 @@ void Slog2InfoRunner::start()
QTC_CHECK(!m_taskTreeRunner.isRunning());
const auto onTestSetup = [this](Process &process) {
- process.setCommand({device()->filePath("slog2info"), {}});
+ process.setCommand(CommandLine{device()->filePath("slog2info")});
};
const auto onTestDone = [this](DoneWith result) {
if (result == DoneWith::Success) {
diff --git a/src/plugins/qtapplicationmanager/QtApplicationManagerIntegration.json.in b/src/plugins/qtapplicationmanager/QtApplicationManagerIntegration.json.in
index 75e2ddeaf2..b9f3b389fa 100644
--- a/src/plugins/qtapplicationmanager/QtApplicationManagerIntegration.json.in
+++ b/src/plugins/qtapplicationmanager/QtApplicationManagerIntegration.json.in
@@ -16,7 +16,7 @@
],
"Category" : "Device Support",
"Description" : "Support for QtApplicationManager on a variety of different target platforms.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/qtsupport/QtSupport.json.in b/src/plugins/qtsupport/QtSupport.json.in
index 0a39a762ce..af3bed662a 100644
--- a/src/plugins/qtsupport/QtSupport.json.in
+++ b/src/plugins/qtsupport/QtSupport.json.in
@@ -14,7 +14,7 @@
],
"Category" : "Build Systems",
"Description" : "Provides support code for build systems.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/qtsupport/externaleditors.cpp b/src/plugins/qtsupport/externaleditors.cpp
index 4009025b8d..371623f6cf 100644
--- a/src/plugins/qtsupport/externaleditors.cpp
+++ b/src/plugins/qtsupport/externaleditors.cpp
@@ -263,7 +263,7 @@ public:
explicit ExternalDesignerFactory(QObject *guard)
{
setId("Qt.Designer");
- setDisplayName(::Core::Tr::tr("Qt Designer"));
+ setDisplayName(::Core::Tr::tr("Qt Widgets Designer"));
setMimeTypes({Utils::Constants::FORM_MIMETYPE});
setEditorStarter([guard](const FilePath &filePath, QString *errorMessage) {
@@ -276,7 +276,7 @@ public:
if (HostOsInfo::isMacHost())
return startEditorProcess(data, errorMessage);
- /* Qt Designer on the remaining platforms: Uses Designer's own
+ /* Qt Widgets Designer on the remaining platforms: Uses Designer's own
* Tcp-based communication mechanism to ensure all files are opened
* in one instance (per version). */
@@ -288,7 +288,8 @@ public:
qDebug() << Q_FUNC_INFO << "\nWriting to socket:" << data.binary << filePath;
QTcpSocket *socket = it.value();
if (!socket->write(filePath.toString().toUtf8() + '\n')) {
- *errorMessage = Tr::tr("Qt Designer is not responding (%1).").arg(socket->errorString());
+ *errorMessage = Tr::tr("Qt Widgets Designer is not responding (%1).")
+ .arg(socket->errorString());
return false;
}
return true;
diff --git a/src/plugins/qtsupport/qtbuildaspects.cpp b/src/plugins/qtsupport/qtbuildaspects.cpp
index 2f6ee19220..2f7d966ba6 100644
--- a/src/plugins/qtsupport/qtbuildaspects.cpp
+++ b/src/plugins/qtsupport/qtbuildaspects.cpp
@@ -30,12 +30,12 @@ QmlDebuggingAspect::QmlDebuggingAspect(AspectContainer *container)
setValue(buildPropertiesSettings().qmlDebugging());
}
-void QmlDebuggingAspect::addToLayout(Layouting::LayoutItem &parent)
+void QmlDebuggingAspect::addToLayout(Layouting::Layout &parent)
{
SelectionAspect::addToLayout(parent);
const auto warningLabel = createSubWidget<InfoLabel>(QString(), InfoLabel::Warning);
warningLabel->setElideMode(Qt::ElideNone);
- parent.addRow({{}, warningLabel});
+ parent.addRow({Layouting::empty, warningLabel});
const auto changeHandler = [this, warningLabel] {
QString warningText;
QTC_ASSERT(m_buildConfig, return);
@@ -78,13 +78,13 @@ void QtQuickCompilerAspect::setBuildConfiguration(const BuildConfiguration *buil
m_buildConfig = buildConfig;
}
-void QtQuickCompilerAspect::addToLayout(Layouting::LayoutItem &parent)
+void QtQuickCompilerAspect::addToLayout(Layouting::Layout &parent)
{
SelectionAspect::addToLayout(parent);
const auto warningLabel = createSubWidget<InfoLabel>(QString(), InfoLabel::Warning);
warningLabel->setElideMode(Qt::ElideNone);
warningLabel->setVisible(false);
- parent.addRow({{}, warningLabel});
+ parent.addRow({Layouting::empty, 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 873daf000d..e112e6a60b 100644
--- a/src/plugins/qtsupport/qtbuildaspects.h
+++ b/src/plugins/qtsupport/qtbuildaspects.h
@@ -21,7 +21,7 @@ public:
void setBuildConfiguration(const ProjectExplorer::BuildConfiguration *newBuildConfig);
private:
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
const ProjectExplorer::BuildConfiguration *m_buildConfig = nullptr;
};
@@ -36,7 +36,7 @@ public:
void setBuildConfiguration(const ProjectExplorer::BuildConfiguration *newBuildConfig);
private:
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
const ProjectExplorer::BuildConfiguration *m_buildConfig = nullptr;
};
diff --git a/src/plugins/qtsupport/qtcreator_tutorials.xml b/src/plugins/qtsupport/qtcreator_tutorials.xml
index af65745443..b17b0def6a 100644
--- a/src/plugins/qtsupport/qtcreator_tutorials.xml
+++ b/src/plugins/qtsupport/qtcreator_tutorials.xml
@@ -16,14 +16,14 @@
</tutorial>
<tutorial imageUrl=":qtsupport/images/icons/tutorialicon.png" difficulty="" docUrl="qthelp://org.qt-project.qtcreator/doc/creator-writing-program.html" projectPath="" name="Creating a Qt Widget-Based Application">
<description><![CDATA[Using Qt Creator to create a small Qt application, Text Finder.]]></description>
- <tags>qt creator,qt designer,widgets,c++,text,help</tags>
+ <tags>qt creator,qt widgets designer,widgets,c++,text,help</tags>
<meta>
<entry name="category">Help</entry>
</meta>
</tutorial>
<tutorial imageUrl=":qtsupport/images/icons/tutorialicon.png" difficulty="" docUrl="qthelp://org.qt-project.qtdoc/qtwidgets/qtwidgets-tutorials-notepad-example.html" projectPath="" name="Getting Started Programming with Qt Widgets">
<description><![CDATA[Developing Qt applications using C++ and the Qt Widgets module.]]></description>
- <tags>qt,qt creator,qt designer,widgets,c++,help</tags>
+ <tags>qt,qt creator,qt widgets designer,widgets,c++,help</tags>
<meta>
<entry name="category">Help</entry>
</meta>
@@ -66,7 +66,7 @@
</tutorial>
<tutorial imageUrl=":qtsupport/images/icons/youtubeLnVjI0I7cKs.webp" difficulty="" projectPath="" name="Qt Creator - Design and Edit Modes" isVideo="true" videoUrl="https://www.youtube.com/watch?v=LnVjI0I7cKs" videoLength="4:34">
<description><![CDATA[Developing a Qt Widgets application.]]></description>
- <tags>qt creator,learning,qt designer,coding,2023</tags>
+ <tags>qt creator,learning,qt widgets designer,coding,2023</tags>
<meta>
<entry name="category">Learning</entry>
</meta>
@@ -276,9 +276,9 @@
<entry name="category">Talk</entry>
</meta>
</tutorial>
- <tutorial imageUrl=":qtsupport/images/icons/youtubeB0X5FOev9Lw.webp" difficulty="" projectPath="" name="Qt Designer tutorial: Integrate custom widgets" isVideo="true" videoUrl="https://youtu.be/B0X5FOev9Lw" videoLength="27:07">
- <description><![CDATA[Integrating custom widgets into Qt Designer.]]></description>
- <tags>qt designer,widgets,ui,talk,2019</tags>
+ <tutorial imageUrl=":qtsupport/images/icons/youtubeB0X5FOev9Lw.webp" difficulty="" projectPath="" name="Qt Widgets Designer tutorial: Integrate custom widgets" isVideo="true" videoUrl="https://youtu.be/B0X5FOev9Lw" videoLength="27:07">
+ <description><![CDATA[Integrating custom widgets into Qt Widgets Designer.]]></description>
+ <tags>qt widgets designer,widgets,ui,talk,2019</tags>
<meta>
<entry name="category">Talk</entry>
</meta>
diff --git a/src/plugins/qtsupport/qtkitaspect.cpp b/src/plugins/qtsupport/qtkitaspect.cpp
index 8059c1059a..18a18b6ae4 100644
--- a/src/plugins/qtsupport/qtkitaspect.cpp
+++ b/src/plugins/qtsupport/qtkitaspect.cpp
@@ -62,7 +62,7 @@ public:
private:
void makeReadOnly() final { m_combo->setEnabled(false); }
- void addToLayoutImpl(Layouting::LayoutItem &parent) override
+ void addToLayoutImpl(Layouting::Layout &parent) override
{
addMutableAction(m_combo);
parent.addItem(m_combo);
diff --git a/src/plugins/qtsupport/qtversionmanager.cpp b/src/plugins/qtsupport/qtversionmanager.cpp
index c269eca3cc..2a309b67b2 100644
--- a/src/plugins/qtsupport/qtversionmanager.cpp
+++ b/src/plugins/qtsupport/qtversionmanager.cpp
@@ -230,7 +230,7 @@ bool QtVersionManagerImpl::restoreQtVersions()
if (!key.view().startsWith(keyPrefix))
continue;
bool ok;
- int count = key.toByteArray().mid(keyPrefix.count()).toInt(&ok);
+ int count = key.toByteArray().mid(keyPrefix.size()).toInt(&ok);
if (!ok || count < 0)
continue;
@@ -304,7 +304,7 @@ void QtVersionManagerImpl::updateFromInstaller(bool emitSignal)
if (!key.view().startsWith(keyPrefix))
continue;
bool ok;
- int count = key.toByteArray().mid(keyPrefix.count()).toInt(&ok);
+ int count = key.toByteArray().mid(keyPrefix.size()).toInt(&ok);
if (!ok || count < 0)
continue;
diff --git a/src/plugins/qtsupport/translationwizardpage.cpp b/src/plugins/qtsupport/translationwizardpage.cpp
index bde480edd0..214996792f 100644
--- a/src/plugins/qtsupport/translationwizardpage.cpp
+++ b/src/plugins/qtsupport/translationwizardpage.cpp
@@ -76,7 +76,7 @@ TranslationWizardPage::TranslationWizardPage(const QString &enabledExpr, bool si
auto localeStrings = transform<QList<LocalePair>>(allLocales,
[](const QLocale &l) {
const QString displayName = QLocale::languageToString(l.language()).append(" (")
- .append(QLocale::countryToString(l.country())).append(')');
+ .append(QLocale::territoryToString(l.territory())).append(')');
const QString tsFileBaseName = l.name();
return qMakePair(displayName, tsFileBaseName);
});
diff --git a/src/plugins/remotelinux/RemoteLinux.json.in b/src/plugins/remotelinux/RemoteLinux.json.in
index 2345b5acab..7f22fbdefc 100644
--- a/src/plugins/remotelinux/RemoteLinux.json.in
+++ b/src/plugins/remotelinux/RemoteLinux.json.in
@@ -14,6 +14,6 @@
],
"Category" : "Device Support",
"Description" : "Support for deployment to and execution on a remote Linux host.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/remotelinux/filesystemaccess_test.cpp b/src/plugins/remotelinux/filesystemaccess_test.cpp
index 0f2e5bd937..b9e77f79e9 100644
--- a/src/plugins/remotelinux/filesystemaccess_test.cpp
+++ b/src/plugins/remotelinux/filesystemaccess_test.cpp
@@ -188,7 +188,7 @@ void FileSystemAccessTest::testWorkingDirectory()
const FilePath dir = baseFilePath() / "testdir with space and 'various' \"quotes\" here";
QVERIFY(dir.ensureWritableDir());
Process proc;
- proc.setCommand({"pwd", {}});
+ proc.setCommand(CommandLine{"pwd"});
proc.setWorkingDirectory(dir);
proc.start();
QVERIFY(proc.waitForFinished());
diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp
index 8d3b46eb1b..d2779e2ebb 100644
--- a/src/plugins/remotelinux/linuxdevice.cpp
+++ b/src/plugins/remotelinux/linuxdevice.cpp
@@ -15,8 +15,6 @@
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
-#include <extensionsystem/pluginmanager.h>
-
#include <projectexplorer/devicesupport/devicemanager.h>
#include <projectexplorer/devicesupport/filetransfer.h>
#include <projectexplorer/devicesupport/filetransferinterface.h>
@@ -367,7 +365,7 @@ Environment LinuxDevicePrivate::getEnvironment()
return m_environmentCache.value();
Process getEnvProc;
- getEnvProc.setCommand({q->filePath("env"), {}});
+ getEnvProc.setCommand(CommandLine{q->filePath("env")});
getEnvProc.runBlocking();
const QString remoteOutput = getEnvProc.cleanedStdOut();
@@ -472,12 +470,10 @@ qint64 SshProcessInterface::processId() const
ProcessResult SshProcessInterface::runInShell(const CommandLine &command, const QByteArray &data)
{
Process process;
- CommandLine cmd = {d->m_device->filePath("/bin/sh"), {"-c"}};
QString tmp;
ProcessArgs::addArg(&tmp, command.executable().path());
ProcessArgs::addArgs(&tmp, command.arguments());
- cmd.addArg(tmp);
- process.setCommand(cmd);
+ process.setCommand({d->m_device->filePath("/bin/sh"), {"-c", tmp}});
process.setWriteData(data);
using namespace std::chrono_literals;
process.runBlocking(2s);
@@ -526,7 +522,7 @@ void SshProcessInterface::handleSendControlSignal(ControlSignal controlSignal)
QTC_ASSERT(pid, return); // TODO: try sending a signal based on process name
const QString args = QString::fromLatin1("-%1 -%2")
.arg(controlSignalToInt(controlSignal)).arg(pid);
- const CommandLine command = { "kill", args, CommandLine::Raw };
+ const CommandLine command{"kill", args, CommandLine::Raw};
// Killing by using the pid as process group didn't work
// Fallback to killing the pid directly
@@ -534,7 +530,7 @@ void SshProcessInterface::handleSendControlSignal(ControlSignal controlSignal)
if (runInShell(command, {}) != ProcessResult::FinishedWithSuccess) {
const QString args = QString::fromLatin1("-%1 %2")
.arg(controlSignalToInt(controlSignal)).arg(pid);
- const CommandLine command = { "kill" , args, CommandLine::Raw };
+ const CommandLine command{"kill" , args, CommandLine::Raw};
runInShell(command, {});
}
}
@@ -1041,7 +1037,7 @@ LinuxDevice::LinuxDevice()
// specify the shell executable.
const QString shell = env.hasChanges() ? env.value_or("SHELL", "/bin/sh") : QString();
- proc->setCommand({filePath(shell), {}});
+ proc->setCommand(CommandLine{filePath(shell)});
proc->setTerminalMode(TerminalMode::Run);
proc->setEnvironment(env);
proc->setWorkingDirectory(workingDir);
@@ -1489,7 +1485,7 @@ private:
if (file.m_targetPermissions == FilePermissions::ForceExecutable)
batchData += "chmod 1775 " + target + '\n';
}
- process().setCommand({sftpBinary, fullConnectionOptions() << "-b" << "-" << host()});
+ process().setCommand({sftpBinary, {fullConnectionOptions(), "-b", "-", host()}});
process().setWriteData(batchData);
process().start();
}
@@ -1531,7 +1527,8 @@ private:
const QString sshCmdLine = ProcessArgs::joinArgs(
QStringList{SshSettings::sshFilePath().toUserOutput()}
<< fullConnectionOptions(), OsTypeLinux);
- QStringList options{"-e", sshCmdLine, m_setup.m_rsyncFlags};
+ QStringList options{"-e", sshCmdLine};
+ options << ProcessArgs::splitArgs(m_setup.m_rsyncFlags, HostOsInfo::hostOs());
if (!m_batches.isEmpty()) { // NormalRun
const auto batchIt = m_batches.begin();
@@ -1621,8 +1618,6 @@ private:
const auto onCreateDirSetup = [iteratorParentDirs](Async<expected_str<void>> &async) {
async.setConcurrentCallData(createDir, *iteratorParentDirs);
- if (Utils::isMainThread())
- async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
};
const auto onCreateDirDone = [this,
@@ -1640,8 +1635,6 @@ private:
const auto onCopySetup = [iterator](Async<expected_str<void>> &async) {
async.setConcurrentCallData(copyFile, *iterator);
- if (Utils::isMainThread())
- async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
};
const auto onCopyDone = [this, iterator, counterStorage](
diff --git a/src/plugins/remotelinux/linuxdevicetester.cpp b/src/plugins/remotelinux/linuxdevicetester.cpp
index d6ab3a9745..be9cb71686 100644
--- a/src/plugins/remotelinux/linuxdevicetester.cpp
+++ b/src/plugins/remotelinux/linuxdevicetester.cpp
@@ -7,8 +7,6 @@
#include "remotelinuxtr.h"
#include "utils/async.h"
-#include <extensionsystem/pluginmanager.h>
-
#include <projectexplorer/devicesupport/deviceusedportsgatherer.h>
#include <projectexplorer/devicesupport/filetransfer.h>
#include <projectexplorer/projectexplorerconstants.h>
@@ -102,7 +100,6 @@ GroupItem GenericLinuxDeviceTesterPrivate::connectionTask() const
const auto onSetup = [this](Async<bool> &task) {
emit q->progressMessage(Tr::tr("Connecting to device..."));
task.setConcurrentCallData([device = m_device] { return device->tryToConnect(); });
- task.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
};
const auto onDone = [this](const Async<bool> &task) {
const bool success = task.isResultAvailable() && task.result();
diff --git a/src/plugins/remotelinux/makeinstallstep.cpp b/src/plugins/remotelinux/makeinstallstep.cpp
index 38edbb6791..8b678bea96 100644
--- a/src/plugins/remotelinux/makeinstallstep.cpp
+++ b/src/plugins/remotelinux/makeinstallstep.cpp
@@ -245,7 +245,7 @@ void MakeInstallStep::updateArgsFromAspect()
void MakeInstallStep::updateFullCommandLine()
{
- CommandLine cmd{makeExecutable(), userArguments(), CommandLine::Raw};
+ const CommandLine cmd{makeExecutable(), userArguments(), CommandLine::Raw};
m_fullCommand.setValue(cmd.toUserOutput());
}
diff --git a/src/plugins/remotelinux/publickeydeploymentdialog.cpp b/src/plugins/remotelinux/publickeydeploymentdialog.cpp
index d3ade682cb..b0da7f2b1d 100644
--- a/src/plugins/remotelinux/publickeydeploymentdialog.cpp
+++ b/src/plugins/remotelinux/publickeydeploymentdialog.cpp
@@ -112,7 +112,7 @@ PublicKeyDeploymentDialog::~PublicKeyDeploymentDialog()
void PublicKeyDeploymentDialog::handleDeploymentDone(bool succeeded, const QString &errorMessage)
{
QString buttonText = succeeded ? Tr::tr("Deployment finished successfully.") : errorMessage;
- const QString textColor = creatorTheme()->color(
+ const QString textColor = creatorColor(
succeeded ? Theme::TextColorNormal : Theme::TextColorError).name();
setLabelText(QString::fromLatin1("<font color=\"%1\">%2</font>")
.arg(textColor, buttonText.replace("\n", "<br/>")));
diff --git a/src/plugins/remotelinux/sshdevicewizard.cpp b/src/plugins/remotelinux/sshdevicewizard.cpp
index d73e146957..2cf001cd17 100644
--- a/src/plugins/remotelinux/sshdevicewizard.cpp
+++ b/src/plugins/remotelinux/sshdevicewizard.cpp
@@ -19,6 +19,7 @@
#include <utils/utilsicons.h>
#include <QLabel>
+#include <QLayout>
#include <QPushButton>
#include <QSpinBox>
diff --git a/src/plugins/remotelinux/sshkeycreationdialog.cpp b/src/plugins/remotelinux/sshkeycreationdialog.cpp
index 03c6f7a543..a94a56cf61 100644
--- a/src/plugins/remotelinux/sshkeycreationdialog.cpp
+++ b/src/plugins/remotelinux/sshkeycreationdialog.cpp
@@ -110,11 +110,11 @@ void SshKeyCreationDialog::generateKeys()
const QString keyTypeString = QLatin1String(m_rsa->isChecked() ? "rsa": "ecdsa");
QApplication::setOverrideCursor(Qt::BusyCursor);
Process keygen;
- const QStringList args{"-t", keyTypeString, "-b", m_comboBox->currentText(),
- "-N", QString(), "-f", privateKeyFilePath().path()};
- QString errorMsg;
- keygen.setCommand({SshSettings::keygenFilePath(), args});
+ keygen.setCommand({SshSettings::keygenFilePath(),
+ {"-t", keyTypeString, "-b", m_comboBox->currentText(), "-N", QString(), "-f",
+ privateKeyFilePath().path()}});
keygen.start();
+ QString errorMsg;
if (!keygen.waitForFinished())
errorMsg = keygen.errorString();
else if (keygen.exitCode() != 0)
diff --git a/src/plugins/resourceeditor/ResourceEditor.json.in b/src/plugins/resourceeditor/ResourceEditor.json.in
index be3ace268a..179fdd3893 100644
--- a/src/plugins/resourceeditor/ResourceEditor.json.in
+++ b/src/plugins/resourceeditor/ResourceEditor.json.in
@@ -14,7 +14,7 @@
],
"Category" : "Qt Creator",
"Description" : "Editor for qrc files.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/resourceeditor/qrceditor/resourcefile.cpp b/src/plugins/resourceeditor/qrceditor/resourcefile.cpp
index 38ee38fedf..bfc68e89d1 100644
--- a/src/plugins/resourceeditor/qrceditor/resourcefile.cpp
+++ b/src/plugins/resourceeditor/qrceditor/resourcefile.cpp
@@ -795,7 +795,7 @@ QVariant ResourceModel::data(const QModelIndex &index, int role) const
// File node
Q_ASSERT(file);
if (!file->exists())
- result = Utils::creatorTheme()->color(Utils::Theme::TextColorError);
+ result = Utils::creatorColor(Utils::Theme::TextColorError);
}
break;
default:
diff --git a/src/plugins/rustls/CMakeLists.txt b/src/plugins/rustls/CMakeLists.txt
new file mode 100644
index 0000000000..17184ddf13
--- /dev/null
+++ b/src/plugins/rustls/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_qtc_lua_plugin(rustls
+ SOURCES rustls/rustls.lua
+ rustls/init.lua
+)
diff --git a/src/plugins/rustls/rustls/init.lua b/src/plugins/rustls/rustls/init.lua
new file mode 100644
index 0000000000..4e0aebe0e8
--- /dev/null
+++ b/src/plugins/rustls/rustls/init.lua
@@ -0,0 +1,176 @@
+-- Copyright (C) 2024 The Qt Company Ltd.
+-- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+local LSP = require('LSP')
+local mm = require('MessageManager')
+local Utils = require('Utils')
+local Process = require('Process')
+local S = require('Settings')
+local Layout = require('Layout')
+local a = require('async')
+local fetch = require('Fetch').fetch
+
+Settings = {}
+
+local function createCommand()
+ local cmd = { Settings.binary.expandedValue:nativePath() }
+ return cmd
+end
+
+local function filter(tbl, callback)
+ for i = #tbl, 1, -1 do
+ if not callback(tbl[i]) then
+ table.remove(tbl, i)
+ end
+ end
+end
+
+local function installOrUpdateServer()
+ local data = a.wait(fetch({
+ url = "https://api.github.com/repos/rust-lang/rust-analyzer/releases?per_page=2",
+ convertToTable = true,
+ headers = {
+ Accept = "application/vnd.github.v3+json",
+ ["X-GitHub-Api-Version"] = "2022-11-28"
+ }
+ }))
+
+ if type(data) == "table" and #data > 1 then
+ local r = data[1]
+ if r.prerelease then
+ r = data[2]
+ end
+ Install = require('Install')
+ local lspPkgInfo = Install.packageInfo("rust-analyzer")
+ if not lspPkgInfo or lspPkgInfo.version ~= r.tag_name then
+ local osTr = { mac = "apple-darwin", windows = "pc-windows-msvc", linux = "unknown-linux-gnu" }
+ local archTr = { unknown = "", x86 = "", x86_64 = "x86_64", itanium = "", arm = "", arm64 = "aarch64" }
+ local extTr = { mac = "gz", windows = "zip", linux = "gz" }
+ local os = osTr[Utils.HostOsInfo.os]
+ local arch = archTr[Utils.HostOsInfo.architecture]
+ local ext = extTr[Utils.HostOsInfo.os]
+
+ local expectedFileName = "rust-analyzer-" .. arch .. "-" .. os .. "." .. ext
+
+ filter(r.assets, function(asset)
+ return string.find(asset.name, expectedFileName, 1, true) == 1
+ end)
+
+ if #r.assets == 0 then
+ print("No assets found for this platform")
+ return
+ end
+ local res, err = a.wait(Install.install(
+ "Do you want to install the rust-analyzer?", {
+ name = "rust-analyzer",
+ url = r.assets[1].browser_download_url,
+ version = r.tag_name
+ }))
+
+ if not res then
+ mm.writeFlashing("Failed to install rust-analyzer: " .. err)
+ return
+ end
+
+ lspPkgInfo = Install.packageInfo("rust-analyzer")
+ print("Installed:", lspPkgInfo.name, "version:", lspPkgInfo.version, "at", lspPkgInfo.path)
+ end
+
+ local binary = "rust-analyzer"
+ if Utils.HostOsInfo.isWindowsHost() then
+ binary = "rust-analyzer.exe"
+ end
+
+ Settings.binary.defaultPath = lspPkgInfo.path:resolvePath(binary)
+ Settings:apply()
+ return
+ end
+
+ if type(data) == "string" then
+ print("Failed to fetch:", data)
+ else
+ print("No rust-analyzer release found.")
+ end
+end
+IsTryingToInstall = false
+
+local function setupClient()
+ Client = LSP.Client.create({
+ name = 'Rust Language Server',
+ cmd = createCommand,
+ transport = 'stdio',
+ languageFilter = {
+ patterns = { '*.rs' },
+ mimeTypes = { 'text/rust' }
+ },
+ settings = Settings,
+ startBehavior = "RequiresProject",
+ onStartFailed = function()
+ a.sync(function()
+ if IsTryingToInstall == true then
+ return
+ end
+ IsTryingToInstall = true
+ installOrUpdateServer()
+ IsTryingToInstall = false
+ end)()
+ end
+ })
+end
+
+local function using(tbl)
+ local result = _G
+ for k, v in pairs(tbl) do result[k] = v end
+ return result
+end
+local function layoutSettings()
+ --- "using namespace Layout"
+ local _ENV = using(Layout)
+
+ local layout = Form {
+ Settings.binary, br,
+ Row {
+ PushButton {
+ text("Try to install Rust language server"),
+ onClicked(function() a.sync(installOrUpdateServer)() end),
+ br,
+ },
+ st
+ }
+ }
+
+ return layout
+end
+
+local function setupAspect()
+ ---@class Settings: AspectContainer
+ Settings = S.AspectContainer.create({
+ autoApply = false,
+ layouter = layoutSettings,
+ });
+
+ Settings.binary = S.FilePathAspect.create({
+ settingsKey = "Rustls.Binary",
+ displayName = "Binary",
+ labelText = "Binary:",
+ toolTip = "The path to the rust analyzer binary.",
+ expectedKind = S.Kind.ExistingCommand,
+ defaultPath = Utils.FilePath.fromUserInput("rust-analyzer"),
+ })
+ -- Search for the binary in the PATH
+ local serverPath = Settings.binary.defaultPath
+ local absolute = a.wait(serverPath:searchInPath()):resolveSymlinks()
+ if absolute:isExecutableFile() == true then
+ Settings.binary.defaultPath = absolute
+ end
+
+ return Settings
+end
+
+local function setup(parameters)
+ setupAspect()
+ setupClient()
+end
+
+return {
+ setup = function() a.sync(setup)() end,
+}
diff --git a/src/plugins/rustls/rustls/rustls.lua b/src/plugins/rustls/rustls/rustls.lua
new file mode 100644
index 0000000000..ae2a5c6eb4
--- /dev/null
+++ b/src/plugins/rustls/rustls/rustls.lua
@@ -0,0 +1,24 @@
+-- Copyright (C) 2024 The Qt Company Ltd.
+-- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+return {
+ Name = "RustLanguageServer",
+ Version = "1.0.0",
+ CompatVersion = "1.0.0",
+ Vendor = "The Qt Company",
+ Category = "Language Client",
+ Description = "The Rust Language Server",
+ Experimental = false,
+ DisabledByDefault = false,
+ LongDescription = [[
+This plugin provides the Rust Language Server.
+It will try to install it if it is not found.
+ ]],
+ Dependencies = {
+ { Name = "Core", Version = "13.0.82", Required = true },
+ { Name = "Lua", Version = "13.0.82", Required = true },
+ { Name = "LuaLanguageClient", Version = "13.0.82", Required = true }
+ },
+ setup = function()
+ require 'init'.setup()
+ end,
+} --[[@as QtcPlugin]]
diff --git a/src/plugins/screenrecorder/ScreenRecorder.json.in b/src/plugins/screenrecorder/ScreenRecorder.json.in
index 671147ff0d..a5283554d3 100644
--- a/src/plugins/screenrecorder/ScreenRecorder.json.in
+++ b/src/plugins/screenrecorder/ScreenRecorder.json.in
@@ -15,6 +15,6 @@
"DisabledByDefault" : true,
"SoftLoadable" : true,
"Description" : "Screen recording.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/screenrecorder/cropandtrim.cpp b/src/plugins/screenrecorder/cropandtrim.cpp
index 4f65469e62..14f4c7fb4a 100644
--- a/src/plugins/screenrecorder/cropandtrim.cpp
+++ b/src/plugins/screenrecorder/cropandtrim.cpp
@@ -324,7 +324,7 @@ CropWidget::CropWidget(QWidget *parent)
saveImageButton,
copyImageToClipboardButton,
},
- noMargin(),
+ noMargin,
}.attachTo(this);
connect(m_xSpinBox, &QSpinBox::valueChanged, this, &CropWidget::onSpinBoxChanged);
@@ -526,19 +526,19 @@ TrimWidget::TrimWidget(const ClipInfo &clip, QWidget *parent)
using namespace Layouting;
Column {
- Row { m_frameSlider, m_currentTime, "/", m_clipDuration },
+ Row { m_frameSlider, m_currentTime, QString("/"), m_clipDuration },
Group {
title(Tr::tr("Trimming")),
Row {
m_trimStart.button, m_trimStart.timeLabel,
Space(20),
m_trimEnd.button, m_trimEnd.timeLabel,
- Stretch(), Space(20),
+ st, Space(20),
Tr::tr("Range:"), m_trimRange,
m_trimResetButton,
},
},
- noMargin(),
+ noMargin,
}.attachTo(this);
connect(m_frameSlider, &QSlider::valueChanged, this, [this] {
@@ -652,7 +652,7 @@ CropAndTrimDialog::CropAndTrimDialog(const ClipInfo &clip, QWidget *parent)
using namespace Layouting;
Column {
Group {
- title("Cropping"),
+ title(Tr::tr("Cropping")),
Column { m_cropWidget },
},
Space(16),
@@ -698,7 +698,7 @@ void CropAndTrimDialog::startFrameFetch()
if (m_nextFetchFrame == -1)
return;
- const CommandLine cl = {
+ const CommandLine cl{
Internal::settings().ffmpegTool(),
{
"-v", "error",
@@ -760,7 +760,7 @@ CropAndTrimWidget::CropAndTrimWidget(QWidget *parent)
Row {
m_button,
m_cropSizeWarningIcon,
- noMargin(), spacing(0),
+ noMargin, spacing(0),
}.attachTo(this);
connect(m_button, &QPushButton::clicked, this, [this] {
diff --git a/src/plugins/screenrecorder/export.cpp b/src/plugins/screenrecorder/export.cpp
index 15f38993ad..00daed94a8 100644
--- a/src/plugins/screenrecorder/export.cpp
+++ b/src/plugins/screenrecorder/export.cpp
@@ -144,7 +144,7 @@ ExportWidget::ExportWidget(QWidget *parent)
exportButton->setText(Tr::tr("Export..."));
using namespace Layouting;
- Row { st, new StyledSeparator, exportButton, noMargin(), spacing(0) }.attachTo(this);
+ Row { st, new StyledSeparator, exportButton, noMargin, spacing(0) }.attachTo(this);
connect(exportButton, &QToolButton::clicked, this, [this] {
FilePathAspect &lastDir = Internal::settings().exportLastDirectory;
diff --git a/src/plugins/screenrecorder/ffmpegutils.cpp b/src/plugins/screenrecorder/ffmpegutils.cpp
index fa0a6f45ae..9f1abb8ec8 100644
--- a/src/plugins/screenrecorder/ffmpegutils.cpp
+++ b/src/plugins/screenrecorder/ffmpegutils.cpp
@@ -152,7 +152,7 @@ static QVersionNumber parseVersionNumber(const QByteArray &toolOutput)
QVersionNumber toolVersion()
{
Process proc;
- const CommandLine cl = {
+ const CommandLine cl{
Internal::settings().ffprobeTool(),
{
"-v", "quiet",
@@ -201,7 +201,7 @@ static ClipInfo parseClipInfo(const QByteArray &toolOutput)
ClipInfo clipInfo(const FilePath &path)
{
Process proc;
- const CommandLine cl = {
+ const CommandLine cl{
Internal::settings().ffprobeTool(),
{
"-v", "quiet",
diff --git a/src/plugins/screenrecorder/record.cpp b/src/plugins/screenrecorder/record.cpp
index aba61ed2eb..70e1d5e275 100644
--- a/src/plugins/screenrecorder/record.cpp
+++ b/src/plugins/screenrecorder/record.cpp
@@ -21,6 +21,7 @@
#include <QAction>
#include <QDialogButtonBox>
#include <QGuiApplication>
+#include <QLayout>
#include <QLoggingCategory>
#include <QMessageBox>
#include <QScreen>
@@ -224,7 +225,7 @@ RecordWidget::RecordWidget(const FilePath &recordFile, QWidget *parent)
st,
progressLabel,
Space(6),
- noMargin(), spacing(0),
+ noMargin, spacing(0),
}.attachTo(this);
connect(settingsButton, &QToolButton::clicked, this, [this] {
diff --git a/src/plugins/screenrecorder/screenrecorderplugin.cpp b/src/plugins/screenrecorder/screenrecorderplugin.cpp
index 9e42b2979f..d7f93066e6 100644
--- a/src/plugins/screenrecorder/screenrecorderplugin.cpp
+++ b/src/plugins/screenrecorder/screenrecorderplugin.cpp
@@ -29,6 +29,7 @@
#include <coreplugin/icore.h>
#include <QDialog>
+#include <QLayout>
using namespace Utils;
using namespace Core;
@@ -56,7 +57,7 @@ public:
Column {
m_recordWidget,
Row { m_cropAndTrimStatusWidget, m_exportWidget },
- noMargin(), spacing(0),
+ noMargin, spacing(0),
}.attachTo(this);
auto setLowerRowEndabled = [this] (bool enabled) {
@@ -86,7 +87,7 @@ public:
});
m_spinner = new SpinnerSolution::Spinner(SpinnerSolution::SpinnerSize::Medium, this);
- m_spinner->setColor(creatorTheme()->color(Theme::IconsBaseColor));
+ m_spinner->setColor(creatorColor(Theme::IconsBaseColor));
m_spinner->hide();
layout()->setSizeConstraint(QLayout::SetFixedSize);
diff --git a/src/plugins/scxmleditor/ScxmlEditor.json.in b/src/plugins/scxmleditor/ScxmlEditor.json.in
index 3b4351f78b..bc1e98e0a3 100644
--- a/src/plugins/scxmleditor/ScxmlEditor.json.in
+++ b/src/plugins/scxmleditor/ScxmlEditor.json.in
@@ -14,7 +14,7 @@
],
"Category" : "Modeling",
"Description" : "Visual Editor for SCXML (State Chart XML) files.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/scxmleditor/common/shapestoolbox.cpp b/src/plugins/scxmleditor/common/shapestoolbox.cpp
index 82def88379..3fa15c562b 100644
--- a/src/plugins/scxmleditor/common/shapestoolbox.cpp
+++ b/src/plugins/scxmleditor/common/shapestoolbox.cpp
@@ -33,7 +33,7 @@ ShapesToolbox::ShapesToolbox(QWidget *parent)
Column {
spacing(0),
scrollArea,
- noMargin,
+ noMargin
}.attachTo(this);
}
diff --git a/src/plugins/scxmleditor/common/stateview.cpp b/src/plugins/scxmleditor/common/stateview.cpp
index 517f53bd3e..c31dd4f850 100644
--- a/src/plugins/scxmleditor/common/stateview.cpp
+++ b/src/plugins/scxmleditor/common/stateview.cpp
@@ -33,7 +33,7 @@ StateView::StateView(StateItem *state, QWidget *parent)
using namespace Layouting;
Row {
- PushButton{ text("Back"), onClicked([this] { closeView(); }, this) },
+ PushButton{ text(QString("Back")), onClicked([this] { closeView(); }, this) },
stateNameLabel,
noMargin
}.attachTo(titleBar);
diff --git a/src/plugins/scxmleditor/plugin_interface/scattributeitemdelegate.cpp b/src/plugins/scxmleditor/plugin_interface/scattributeitemdelegate.cpp
index fa735e5d68..0121ddf191 100644
--- a/src/plugins/scxmleditor/plugin_interface/scattributeitemdelegate.cpp
+++ b/src/plugins/scxmleditor/plugin_interface/scattributeitemdelegate.cpp
@@ -19,12 +19,12 @@ QWidget *SCAttributeItemDelegate::createEditor(QWidget *parent, const QStyleOpti
Q_UNUSED(option)
switch (index.data(DataTypeRole).toInt()) {
- case QVariant::StringList: {
+ case QMetaType::QStringList: {
auto combo = new QComboBox(parent);
combo->setFocusPolicy(Qt::StrongFocus);
return combo;
}
- case QVariant::String: {
+ case QMetaType::QString: {
if (index.column() == 0) {
auto edit = new QLineEdit(parent);
edit->setFocusPolicy(Qt::StrongFocus);
@@ -52,7 +52,7 @@ void SCAttributeItemDelegate::updateEditorGeometry(QWidget *editor, const QStyle
void SCAttributeItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
switch (index.data(DataTypeRole).toInt()) {
- case QVariant::StringList: {
+ case QMetaType::QStringList: {
auto combo = qobject_cast<QComboBox*>(editor);
if (combo) {
combo->clear();
diff --git a/src/plugins/scxmleditor/plugin_interface/scattributeitemmodel.cpp b/src/plugins/scxmleditor/plugin_interface/scattributeitemmodel.cpp
index 454af2930f..c759bcca73 100644
--- a/src/plugins/scxmleditor/plugin_interface/scattributeitemmodel.cpp
+++ b/src/plugins/scxmleditor/plugin_interface/scattributeitemmodel.cpp
@@ -84,7 +84,7 @@ QVariant SCAttributeItemModel::data(const QModelIndex &index, int role) const
}
} else {
if (bEditable) {
- if (m_tag->tagType() > MetadataItem && m_tag->info()->attributes[index.row()].datatype == QVariant::StringList)
+ if (m_tag->tagType() > MetadataItem && m_tag->info()->attributes[index.row()].datatype == QMetaType::QStringList)
return QString::fromLatin1(m_tag->info()->attributes[index.row()].value).split(";");
else
return m_tag->attribute(index.row());
@@ -100,11 +100,11 @@ QVariant SCAttributeItemModel::data(const QModelIndex &index, int role) const
break;
case DataTypeRole: {
if (m_tag->tagType() == Metadata || m_tag->tagType() == MetadataItem)
- return (int)QVariant::String;
+ return (int)QMetaType::QString;
else if (index.column() == 1 && m_tag->info()->n_attributes > 0)
return m_tag->info()->attributes[index.row()].datatype;
else
- return QVariant::Invalid;
+ return {};
}
case DataRole: {
if (m_tag->info()->n_attributes > 0)
diff --git a/src/plugins/scxmleditor/plugin_interface/scxmltypes.h b/src/plugins/scxmleditor/plugin_interface/scxmltypes.h
index 62fe00528a..0224fb8cb2 100644
--- a/src/plugins/scxmleditor/plugin_interface/scxmltypes.h
+++ b/src/plugins/scxmleditor/plugin_interface/scxmltypes.h
@@ -65,119 +65,119 @@ struct scxmltag_type_t
// Define tag-attributes
const scxmltag_attribute_t scxml_scxml_attributes[] = {
- {"initial", nullptr, false, false, QVariant::String},
- {"name", nullptr, false, true, QVariant::String},
- {"xmlns", "http://www.w3.org/2005/07/scxml", true, false, QVariant::String},
- {"version", "1.0", true, false, QVariant::String},
- {"datamodel", nullptr, false, true, QVariant::String},
- {"binding", "early;late", false, true, QVariant::StringList}
+ {"initial", nullptr, false, false, QMetaType::QString},
+ {"name", nullptr, false, true, QMetaType::QString},
+ {"xmlns", "http://www.w3.org/2005/07/scxml", true, false, QMetaType::QString},
+ {"version", "1.0", true, false, QMetaType::QString},
+ {"datamodel", nullptr, false, true, QMetaType::QString},
+ {"binding", "early;late", false, true, QMetaType::QStringList}
};
const scxmltag_attribute_t scxml_state_attributes[] = {
- {"id", nullptr, false, true, QVariant::String},
- {"initial", nullptr, false, false, QVariant::String}
+ {"id", nullptr, false, true, QMetaType::QString},
+ {"initial", nullptr, false, false, QMetaType::QString}
};
const scxmltag_attribute_t scxml_parallel_attributes[] = {
- {"id", nullptr, false, true, QVariant::String}
+ {"id", nullptr, false, true, QMetaType::QString}
};
const scxmltag_attribute_t scxml_transition_attributes[] = {
- {"event", nullptr, false, true, QVariant::String},
- {"cond", nullptr, false, true, QVariant::String},
- {"target", nullptr, false, true, QVariant::String},
- {"type", "internal;external", false, true, QVariant::StringList}
+ {"event", nullptr, false, true, QMetaType::QString},
+ {"cond", nullptr, false, true, QMetaType::QString},
+ {"target", nullptr, false, true, QMetaType::QString},
+ {"type", "internal;external", false, true, QMetaType::QStringList}
};
const scxmltag_attribute_t scxml_initialtransition_attributes[] = {
- {"target", nullptr, false, false, QVariant::String}
+ {"target", nullptr, false, false, QMetaType::QString}
};
const scxmltag_attribute_t scxml_final_attributes[] = {
- {"id", nullptr, false, true, QVariant::String}
+ {"id", nullptr, false, true, QMetaType::QString}
};
const scxmltag_attribute_t scxml_history_attributes[] = {
- {"id", nullptr, false, true, QVariant::String},
- {"type", "shallow;deep", false, true, QVariant::StringList}
+ {"id", nullptr, false, true, QMetaType::QString},
+ {"type", "shallow;deep", false, true, QMetaType::QStringList}
};
const scxmltag_attribute_t scxml_raise_attributes[] = {
- {"event", nullptr, true, true, QVariant::String}
+ {"event", nullptr, true, true, QMetaType::QString}
};
const scxmltag_attribute_t scxml_if_attributes[] = {
- {"cond", nullptr, true, true, QVariant::String},
+ {"cond", nullptr, true, true, QMetaType::QString},
};
const scxmltag_attribute_t scxml_elseif_attributes[] = {
- {"cond", nullptr, true, true, QVariant::String}
+ {"cond", nullptr, true, true, QMetaType::QString}
};
const scxmltag_attribute_t scxml_foreach_attributes[] = {
- {"array", nullptr, true, true, QVariant::String},
- {"item", nullptr, true, true, QVariant::String},
- {"index", nullptr, false, true, QVariant::String}
+ {"array", nullptr, true, true, QMetaType::QString},
+ {"item", nullptr, true, true, QMetaType::QString},
+ {"index", nullptr, false, true, QMetaType::QString}
};
const scxmltag_attribute_t scxml_log_attributes[] = {
- {"label", "", false, true, QVariant::String},
- {"expr", nullptr, false, true, QVariant::String}
+ {"label", "", false, true, QMetaType::QString},
+ {"expr", nullptr, false, true, QMetaType::QString}
};
const scxmltag_attribute_t scxml_data_attributes[] = {
- {"id", nullptr, true, true, QVariant::String},
- {"src", nullptr, false, true, QVariant::String},
- {"expr", nullptr, false, true, QVariant::String}
+ {"id", nullptr, true, true, QMetaType::QString},
+ {"src", nullptr, false, true, QMetaType::QString},
+ {"expr", nullptr, false, true, QMetaType::QString}
};
const scxmltag_attribute_t scxml_assign_attributes[] = {
- {"location", nullptr, true, true, QVariant::String},
- {"expr", nullptr, false, true, QVariant::String}
+ {"location", nullptr, true, true, QMetaType::QString},
+ {"expr", nullptr, false, true, QMetaType::QString}
};
const scxmltag_attribute_t scxml_content_attributes[] = {
- {"expr", nullptr, false, true, QVariant::String}
+ {"expr", nullptr, false, true, QMetaType::QString}
};
const scxmltag_attribute_t scxml_param_attributes[] = {
- {"name", nullptr, true, true, QVariant::String},
- {"expr", nullptr, false, true, QVariant::String},
- {"location", nullptr, false, true, QVariant::String}
+ {"name", nullptr, true, true, QMetaType::QString},
+ {"expr", nullptr, false, true, QMetaType::QString},
+ {"location", nullptr, false, true, QMetaType::QString}
};
const scxmltag_attribute_t scxml_script_attributes[] = {
- {"src", nullptr, false, true, QVariant::String}
+ {"src", nullptr, false, true, QMetaType::QString}
};
const scxmltag_attribute_t scxml_send_attributes[] = {
- {"event", nullptr, false, true, QVariant::String},
- {"eventexpr", nullptr, false, true, QVariant::String},
- {"target", nullptr, false, true, QVariant::String},
- {"targetexpr", nullptr, false, true, QVariant::String},
- {"type", nullptr, false, true, QVariant::String},
- {"typeexpr", nullptr, false, true, QVariant::String},
- {"id", nullptr, false, true, QVariant::String},
- {"idlocation", nullptr, false, true, QVariant::String},
- {"delay", nullptr, false, true, QVariant::String},
- {"delayexpr", nullptr, false, true, QVariant::String},
- {"namelist", nullptr, false, true, QVariant::String}
+ {"event", nullptr, false, true, QMetaType::QString},
+ {"eventexpr", nullptr, false, true, QMetaType::QString},
+ {"target", nullptr, false, true, QMetaType::QString},
+ {"targetexpr", nullptr, false, true, QMetaType::QString},
+ {"type", nullptr, false, true, QMetaType::QString},
+ {"typeexpr", nullptr, false, true, QMetaType::QString},
+ {"id", nullptr, false, true, QMetaType::QString},
+ {"idlocation", nullptr, false, true, QMetaType::QString},
+ {"delay", nullptr, false, true, QMetaType::QString},
+ {"delayexpr", nullptr, false, true, QMetaType::QString},
+ {"namelist", nullptr, false, true, QMetaType::QString}
};
const scxmltag_attribute_t scxml_cancel_attributes[] = {
- {"sendid", nullptr, false, true, QVariant::String},
- {"sendidexpr", nullptr, false, true, QVariant::String}
+ {"sendid", nullptr, false, true, QMetaType::QString},
+ {"sendidexpr", nullptr, false, true, QMetaType::QString}
};
const scxmltag_attribute_t scxml_invoke_attributes[] = {
- {"type", nullptr, false, true, QVariant::String},
- {"typeexpr", nullptr, false, true, QVariant::String},
- {"src", nullptr, false, true, QVariant::String},
- {"srcexpr", nullptr, false, true, QVariant::String},
- {"id", nullptr, false, true, QVariant::String},
- {"idlocation", nullptr, false, true, QVariant::String},
- {"namelist", nullptr, false, true, QVariant::String},
- {"autoforward", ";true;false", false, true, QVariant::StringList}
+ {"type", nullptr, false, true, QMetaType::QString},
+ {"typeexpr", nullptr, false, true, QMetaType::QString},
+ {"src", nullptr, false, true, QMetaType::QString},
+ {"srcexpr", nullptr, false, true, QMetaType::QString},
+ {"id", nullptr, false, true, QMetaType::QString},
+ {"idlocation", nullptr, false, true, QMetaType::QString},
+ {"namelist", nullptr, false, true, QMetaType::QString},
+ {"autoforward", ";true;false", false, true, QMetaType::QStringList}
};
const scxmltag_type_t scxml_unknown = {
diff --git a/src/plugins/serialterminal/SerialTerminal.json.in b/src/plugins/serialterminal/SerialTerminal.json.in
index 78ac127ab4..4cbefbaa20 100644
--- a/src/plugins/serialterminal/SerialTerminal.json.in
+++ b/src/plugins/serialterminal/SerialTerminal.json.in
@@ -15,6 +15,6 @@
"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" : "Serial Port Terminal",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/silversearcher/SilverSearcher.json.in b/src/plugins/silversearcher/SilverSearcher.json.in
index bfaf553635..dbb613fe2a 100644
--- a/src/plugins/silversearcher/SilverSearcher.json.in
+++ b/src/plugins/silversearcher/SilverSearcher.json.in
@@ -14,6 +14,6 @@
"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" : "Adds possibility to use SilverSearcher tool as an alternative mechanism of 'find in files'",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/squish/Squish.json.in b/src/plugins/squish/Squish.json.in
index faa56b6837..c958ef6f54 100644
--- a/src/plugins/squish/Squish.json.in
+++ b/src/plugins/squish/Squish.json.in
@@ -14,7 +14,7 @@
"Alternatively, this file may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this file. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html."
],
"Description" : "Squish plugin. Provides integration of Squish.",
-"Url" : "http://www.qt.io",
+"Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/squish/squishoutputpane.cpp b/src/plugins/squish/squishoutputpane.cpp
index adb73b6b7e..7db24f4eef 100644
--- a/src/plugins/squish/squishoutputpane.cpp
+++ b/src/plugins/squish/squishoutputpane.cpp
@@ -44,8 +44,8 @@ SquishOutputPane::SquishOutputPane()
m_outputWidget->setLayout(outputLayout);
QPalette pal;
- pal.setColor(QPalette::Window, Utils::creatorTheme()->color(Utils::Theme::InfoBarBackground));
- pal.setColor(QPalette::WindowText, Utils::creatorTheme()->color(Utils::Theme::InfoBarText));
+ pal.setColor(QPalette::Window, Utils::creatorColor(Utils::Theme::InfoBarBackground));
+ pal.setColor(QPalette::WindowText, Utils::creatorColor(Utils::Theme::InfoBarText));
m_summaryWidget = new QFrame;
m_summaryWidget->setPalette(pal);
diff --git a/src/plugins/squish/squishperspective.cpp b/src/plugins/squish/squishperspective.cpp
index 1b762c71a4..4eb63cbb7e 100644
--- a/src/plugins/squish/squishperspective.cpp
+++ b/src/plugins/squish/squishperspective.cpp
@@ -57,9 +57,9 @@ static QIcon iconForType(IconType type)
static QString customStyleSheet(bool extended)
{
- static const QString red = Utils::creatorTheme()->color(
+ static const QString red = Utils::creatorColor(
Utils::Theme::ProgressBarColorError).name();
- static const QString green = Utils::creatorTheme()->color(
+ static const QString green = Utils::creatorColor(
Utils::Theme::ProgressBarColorFinished).name();
if (!extended)
return "QProgressBar {text-align:left; border:0px}";
diff --git a/src/plugins/squish/squishserverprocess.cpp b/src/plugins/squish/squishserverprocess.cpp
index 9e4b974143..3af7bf308a 100644
--- a/src/plugins/squish/squishserverprocess.cpp
+++ b/src/plugins/squish/squishserverprocess.cpp
@@ -26,9 +26,8 @@ void SquishServerProcess::stop()
{
if (m_process.state() != QProcess::NotRunning && m_serverPort > 0) {
Utils::Process serverKiller;
- QStringList args;
- args << "--stop" << "--port" << QString::number(m_serverPort);
- serverKiller.setCommand({m_process.commandLine().executable(), args});
+ serverKiller.setCommand({m_process.commandLine().executable(),
+ {"--stop", "--port", QString::number(m_serverPort)}});
serverKiller.setEnvironment(m_process.environment());
serverKiller.start();
if (!serverKiller.waitForFinished()) {
diff --git a/src/plugins/squish/squishsettings.cpp b/src/plugins/squish/squishsettings.cpp
index a8e0518a3d..1b8c799eda 100644
--- a/src/plugins/squish/squishsettings.cpp
+++ b/src/plugins/squish/squishsettings.cpp
@@ -387,10 +387,10 @@ SquishServerSettingsWidget::SquishServerSettingsWidget(QWidget *parent)
using namespace Layouting;
Form grid {
&m_applicationsView, br,
- &m_serverSettings.autTimeout, br,
- &m_serverSettings.responseTimeout, br,
- &m_serverSettings.postMortemWaitTime, br,
- &m_serverSettings.animatedCursor, br,
+ m_serverSettings.autTimeout, br,
+ m_serverSettings.responseTimeout, br,
+ m_serverSettings.postMortemWaitTime, br,
+ m_serverSettings.animatedCursor, br,
};
Column buttonCol {
add,
diff --git a/src/plugins/squish/squishtools.cpp b/src/plugins/squish/squishtools.cpp
index f6ef2e67e0..68dabd6449 100644
--- a/src/plugins/squish/squishtools.cpp
+++ b/src/plugins/squish/squishtools.cpp
@@ -554,8 +554,8 @@ void SquishTools::startSquishServer(Request request)
m_perspective.updateStatus(Tr::tr("Running test case"));
}
- const QStringList arguments = serverArgumentsFromSettings();
- m_serverProcess.start({toolsSettings.serverPath, arguments}, squishEnvironment());
+ m_serverProcess.start({toolsSettings.serverPath, serverArgumentsFromSettings()},
+ squishEnvironment());
}
void SquishTools::stopSquishServer()
@@ -569,13 +569,10 @@ void SquishTools::startSquishRunner()
if (!isValidToStartRunner() || !setupRunnerPath())
return;
- const QStringList args = runnerArgumentsFromSettings();
-
if (m_request == RecordTestRequested)
m_closeRunnerOnEndRecord = true;
- Utils::CommandLine cmdLine = {toolsSettings.runnerPath, args};
- setupAndStartSquishRunnerProcess(cmdLine);
+ setupAndStartSquishRunnerProcess({toolsSettings.runnerPath, runnerArgumentsFromSettings()});
}
void SquishTools::setupAndStartRecorder()
@@ -604,7 +601,7 @@ void SquishTools::setupAndStartRecorder()
m_secondaryRunner = new SquishRunnerProcess(this);
m_secondaryRunner->setupProcess(SquishRunnerProcess::Record);
- const CommandLine cmd = {toolsSettings.runnerPath, args};
+ const CommandLine cmd{toolsSettings.runnerPath, args};
connect(m_secondaryRunner, &SquishRunnerProcess::recorderDone,
this, &SquishTools::onRecorderFinished);
qCDebug(LOG) << "Recorder starting:" << cmd.toUserOutput();
@@ -629,7 +626,7 @@ void SquishTools::setupAndStartInspector()
m_secondaryRunner = new SquishRunnerProcess(this);
m_secondaryRunner->setupProcess(SquishRunnerProcess::Inspect);
- const CommandLine cmd = {toolsSettings.runnerPath, args};
+ const CommandLine cmd{toolsSettings.runnerPath, args};
connect(m_secondaryRunner, &SquishRunnerProcess::logOutputReceived,
this, &SquishTools::logOutputReceived);
connect(m_secondaryRunner, &SquishRunnerProcess::objectPicked,
@@ -676,8 +673,8 @@ void SquishTools::executeRunnerQuery()
if (!isValidToStartRunner() || !setupRunnerPath())
return;
- QStringList arguments = { "--port", QString::number(m_serverProcess.port()) };
- Utils::CommandLine cmdLine = {toolsSettings.runnerPath, arguments};
+ CommandLine cmdLine{toolsSettings.runnerPath,
+ {"--port", QString::number(m_serverProcess.port())}};
switch (m_query) {
case ServerInfo:
cmdLine.addArg("--info");
@@ -690,7 +687,7 @@ void SquishTools::executeRunnerQuery()
case SetGlobalScriptDirs:
cmdLine.addArg("--config");
cmdLine.addArg("setGlobalScriptDirs");
- cmdLine.addArgs(m_queryParameter, Utils::CommandLine::Raw);
+ cmdLine.addArgs(m_queryParameter, CommandLine::Raw);
break;
default:
QTC_ASSERT(false, return);
@@ -1106,10 +1103,9 @@ void SquishTools::interruptRunner()
{
qCDebug(LOG) << "Interrupting runner";
QTC_ASSERT(m_primaryRunner, return);
- qint64 processId = m_primaryRunner->processId();
- const CommandLine cmd(toolsSettings.processComPath, {QString::number(processId), "break"});
+ const qint64 processId = m_primaryRunner->processId();
Process process;
- process.setCommand(cmd);
+ process.setCommand({toolsSettings.processComPath, {QString::number(processId), "break"}});
process.start();
process.waitForFinished();
}
@@ -1122,10 +1118,9 @@ void SquishTools::terminateRunner()
m_perspective.updateStatus(Tr::tr("User stop initiated."));
// should we terminate the AUT instead of the runner?!?
QTC_ASSERT(m_primaryRunner, return);
- qint64 processId = m_primaryRunner->processId();
- const CommandLine cmd(toolsSettings.processComPath, {QString::number(processId), "terminate"});
+ const qint64 processId = m_primaryRunner->processId();
Process process;
- process.setCommand(cmd);
+ process.setCommand({toolsSettings.processComPath, {QString::number(processId), "terminate"}});
process.start();
process.waitForFinished();
}
@@ -1263,7 +1258,7 @@ bool SquishTools::setupRunnerPath()
return true;
}
-void SquishTools::setupAndStartSquishRunnerProcess(const Utils::CommandLine &cmdLine)
+void SquishTools::setupAndStartSquishRunnerProcess(const CommandLine &cmdLine)
{
QTC_ASSERT(m_primaryRunner, return);
// avoid crashes on fast re-usage of Process
diff --git a/src/plugins/squish/squishwizardpages.cpp b/src/plugins/squish/squishwizardpages.cpp
index 0ee6dab6b2..707421c413 100644
--- a/src/plugins/squish/squishwizardpages.cpp
+++ b/src/plugins/squish/squishwizardpages.cpp
@@ -319,7 +319,7 @@ bool SquishFileGenerator::setup(const QVariant &data, QString *errorMessage)
if (data.isNull())
return false;
- if (data.typeId() != QVariant::Map) {
+ if (data.typeId() != QMetaType::QVariantMap) {
*errorMessage = Tr::tr("Key is not an object.");
return false;
}
diff --git a/src/plugins/squish/testresult.cpp b/src/plugins/squish/testresult.cpp
index 7e229d50e0..38b9ca1d9a 100644
--- a/src/plugins/squish/testresult.cpp
+++ b/src/plugins/squish/testresult.cpp
@@ -46,29 +46,27 @@ QString TestResult::typeToString(Result::Type type)
QColor TestResult::colorForType(Result::Type type)
{
- Utils::Theme *creatorTheme = Utils::creatorTheme();
-
switch (type) {
case Result::Start:
case Result::Log:
case Result::Detail:
case Result::End:
- return creatorTheme->color(Utils::Theme::OutputPanes_StdOutTextColor);
+ return creatorColor(Utils::Theme::OutputPanes_StdOutTextColor);
case Result::Pass:
- return creatorTheme->color(Utils::Theme::OutputPanes_TestPassTextColor);
+ return creatorColor(Utils::Theme::OutputPanes_TestPassTextColor);
case Result::Fail:
case Result::Error:
- return creatorTheme->color(Utils::Theme::OutputPanes_TestFailTextColor);
+ return creatorColor(Utils::Theme::OutputPanes_TestFailTextColor);
case Result::ExpectedFail:
- return creatorTheme->color(Utils::Theme::OutputPanes_TestXFailTextColor);
+ return creatorColor(Utils::Theme::OutputPanes_TestXFailTextColor);
case Result::UnexpectedPass:
- return creatorTheme->color(Utils::Theme::OutputPanes_TestXPassTextColor);
+ return creatorColor(Utils::Theme::OutputPanes_TestXPassTextColor);
case Result::Warn:
- return creatorTheme->color(Utils::Theme::OutputPanes_TestWarnTextColor);
+ return creatorColor(Utils::Theme::OutputPanes_TestWarnTextColor);
case Result::Fatal:
- return creatorTheme->color(Utils::Theme::OutputPanes_TestFatalTextColor);
+ return creatorColor(Utils::Theme::OutputPanes_TestFatalTextColor);
}
- return creatorTheme->color(Utils::Theme::OutputPanes_StdOutTextColor);
+ return creatorColor(Utils::Theme::OutputPanes_StdOutTextColor);
}
} // namespace Internal
diff --git a/src/plugins/studiowelcome/StudioWelcome.json.in b/src/plugins/studiowelcome/StudioWelcome.json.in
index 9ee3c25172..5ab72606cf 100644
--- a/src/plugins/studiowelcome/StudioWelcome.json.in
+++ b/src/plugins/studiowelcome/StudioWelcome.json.in
@@ -14,6 +14,6 @@
"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" : "Qt Design Studio Welcome Page.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp
index d526a9c436..6cb94efb35 100644
--- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp
+++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp
@@ -338,30 +338,27 @@ public:
if (exampleVersion.isEmpty())
return true;
- const QStringList exampleVersionParts = exampleVersion.split('.');
- const QStringList qdsVersionParts = QCoreApplication::applicationVersion().split('.');
+ // Split versions into parts (major, minor, patch)
+ QStringList qdsVersionParts = QCoreApplication::applicationVersion().split('.');
+ QStringList exampleVersionParts = exampleVersion.split('.');
- QList<int> exampleVerInts;
- QList<int> qdsVerInts;
- for (const QString &part : exampleVersionParts)
- exampleVerInts.append(part.toInt());
+ // Fill missing parts with zeros
+ while (qdsVersionParts.size() < 3)
+ qdsVersionParts.append("0");
- for (const QString &part : qdsVersionParts)
- qdsVerInts.append(part.toInt());
+ while (exampleVersionParts.size() < 3)
+ exampleVersionParts.append("0");
- // pad zeros so both lists are same size
- while (qdsVerInts.size() < exampleVerInts.size())
- qdsVerInts.append(0);
+ int qdsMajor = qdsVersionParts.at(0).toInt();
+ int qdsMinor = qdsVersionParts.at(1).toInt();
+ int qdsPatch = qdsVersionParts.at(2).toInt();
- while (exampleVerInts.size() < qdsVerInts.size())
- exampleVerInts.append(0);
+ int exMajor = exampleVersionParts.at(0).toInt();
+ int exMinor = exampleVersionParts.at(1).toInt();
+ int exPatch = exampleVersionParts.at(2).toInt();
- for (int i = 0; i < qdsVerInts.size(); ++i) {
- if (exampleVerInts[i] < qdsVerInts[i])
- return false;
- }
-
- return true;
+ return QT_VERSION_CHECK(exMajor, exMinor, exPatch)
+ <= QT_VERSION_CHECK(qdsMajor, qdsMinor, qdsPatch);
}
public slots:
@@ -587,6 +584,9 @@ static bool forceDownLoad()
static bool showSplashScreen()
{
+ // some error dialog is maybe open, be silent to avoid focus problems (macOS had some)
+ if (Core::ICore::mainWindow() != Core::ICore::dialogParent())
+ return false;
const Key lastQDSVersionEntry = "QML/Designer/lastQDSVersion";
QtcSettings *settings = Core::ICore::settings();
diff --git a/src/plugins/subversion/Subversion.json.in b/src/plugins/subversion/Subversion.json.in
index 604ba2648a..1e326a8567 100644
--- a/src/plugins/subversion/Subversion.json.in
+++ b/src/plugins/subversion/Subversion.json.in
@@ -14,7 +14,7 @@
],
"Category" : "Version Control",
"Description" : "Subversion integration.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/subversion/subversionclient.cpp b/src/plugins/subversion/subversionclient.cpp
index e69809143d..1dfb19ed86 100644
--- a/src/plugins/subversion/subversionclient.cpp
+++ b/src/plugins/subversion/subversionclient.cpp
@@ -115,6 +115,7 @@ CommandLine &operator<<(Utils::CommandLine &command, SubversionClient::AddAuthOp
QString SubversionClient::synchronousTopic(const FilePath &repository) const
{
+ // TODO: Looks unused
QStringList args;
QString svnVersionBinary = vcsBinary(repository).toString();
diff --git a/src/plugins/subversion/subversionsettings.cpp b/src/plugins/subversion/subversionsettings.cpp
index 284472938f..1bea22072c 100644
--- a/src/plugins/subversion/subversionsettings.cpp
+++ b/src/plugins/subversion/subversionsettings.cpp
@@ -76,7 +76,7 @@ SubversionSettings::SubversionSettings()
Group {
title(Tr::tr("Authentication")),
- useAuthentication.groupChecker(),
+ groupChecker(useAuthentication.groupChecker()),
Form {
userName, br,
password,
diff --git a/src/plugins/terminal/Terminal.json.in b/src/plugins/terminal/Terminal.json.in
index 8c631dd651..0587b5e5ee 100644
--- a/src/plugins/terminal/Terminal.json.in
+++ b/src/plugins/terminal/Terminal.json.in
@@ -13,6 +13,6 @@
"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",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/terminal/shellintegration.cpp b/src/plugins/terminal/shellintegration.cpp
index 5dfd450e68..1303f92288 100644
--- a/src/plugins/terminal/shellintegration.cpp
+++ b/src/plugins/terminal/shellintegration.cpp
@@ -133,7 +133,7 @@ void ShellIntegration::onOsc(int cmd, std::string_view str, bool initial, bool f
qCDebug(integrationLog) << "OSC 133:" << data;
} else if (cmd == 633 && command.length() == 1) {
if (command[0] == 'E') {
- CommandLine cmdLine = CommandLine::fromUserInput(data.toString());
+ const CommandLine cmdLine = CommandLine::fromUserInput(data.toString());
emit commandChanged(cmdLine);
} else if (command[0] == 'D') {
emit commandChanged({});
@@ -174,12 +174,10 @@ void ShellIntegration::prepareProcess(Utils::Process &process)
m_tempDir.filePath(filesToCopy.bash.rcFile.fileName()));
rcPath.copyFile(tmpRc);
- CommandLine newCmd = {cmd.executable(), {"--init-file", tmpRc.nativePath()}};
-
if (cmd.arguments() == "-l")
env.set("VSCODE_SHELL_LOGIN", "1");
- cmd = newCmd;
+ cmd = {cmd.executable(), {"--init-file", tmpRc.nativePath()}};
} else if (cmd.executable().baseName() == "zsh") {
for (const FileToCopy &file : filesToCopy.zsh.files) {
const auto copyResult = file.source.copyFile(
diff --git a/src/plugins/terminal/shellmodel.cpp b/src/plugins/terminal/shellmodel.cpp
index 4368efee97..dd45515b09 100644
--- a/src/plugins/terminal/shellmodel.cpp
+++ b/src/plugins/terminal/shellmodel.cpp
@@ -168,7 +168,7 @@ QList<ShellModelItem> ShellModel::remote() const
ProjectExplorer::DeviceManager::instance()->forEachDevice(
[&result](const std::shared_ptr<const ProjectExplorer::IDevice> &device) {
if (device->type() != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE)
- result << ShellModelItem{device->displayName(), {{device->rootPath(), {}}}};
+ result << ShellModelItem{device->displayName(), {CommandLine{device->rootPath()}}};
});
return result;
diff --git a/src/plugins/terminal/terminalpane.cpp b/src/plugins/terminal/terminalpane.cpp
index 1648638713..8b6699ef77 100644
--- a/src/plugins/terminal/terminalpane.cpp
+++ b/src/plugins/terminal/terminalpane.cpp
@@ -115,7 +115,7 @@ void TerminalPane::openTerminal(const OpenTerminalParameters &parameters)
if (!shell.isExecutableFile())
parametersCopy.workingDirectory.reset();
else
- parametersCopy.shellCommand = CommandLine{shell, {}};
+ parametersCopy.shellCommand = CommandLine{shell};
}
const auto terminalWidget = new TerminalWidget(&m_tabWidget, parametersCopy);
diff --git a/src/plugins/terminal/terminalsettings.cpp b/src/plugins/terminal/terminalsettings.cpp
index 62e0c209c1..4e754dc7dd 100644
--- a/src/plugins/terminal/terminalsettings.cpp
+++ b/src/plugins/terminal/terminalsettings.cpp
@@ -527,37 +527,35 @@ TerminalSettings::TerminalSettings()
enableMouseTracking.setToolTip(Tr::tr("Enables mouse tracking in the terminal."));
enableMouseTracking.setDefaultValue(true);
- Theme *theme = Utils::creatorTheme();
+ setupColor(this, foregroundColor, "Foreground", creatorColor(Theme::TerminalForeground));
+ setupColor(this, backgroundColor, "Background", creatorColor(Theme::TerminalBackground));
+ setupColor(this, selectionColor, "Selection", creatorColor(Theme::TerminalSelection));
- setupColor(this, foregroundColor, "Foreground", theme->color(Theme::TerminalForeground));
- setupColor(this, backgroundColor, "Background", theme->color(Theme::TerminalBackground));
- setupColor(this, selectionColor, "Selection", theme->color(Theme::TerminalSelection));
+ setupColor(this, findMatchColor, "Find matches", creatorColor(Theme::TerminalFindMatch));
- setupColor(this, findMatchColor, "Find matches", theme->color(Theme::TerminalFindMatch));
+ setupColor(this, colors[0], "0", creatorColor(Theme::TerminalAnsi0), "black");
+ setupColor(this, colors[8], "8", creatorColor(Theme::TerminalAnsi8), "bright black");
- setupColor(this, colors[0], "0", theme->color(Theme::TerminalAnsi0), "black");
- setupColor(this, colors[8], "8", theme->color(Theme::TerminalAnsi8), "bright black");
+ setupColor(this, colors[1], "1", creatorColor(Theme::TerminalAnsi1), "red");
+ setupColor(this, colors[9], "9", creatorColor(Theme::TerminalAnsi9), "bright red");
- setupColor(this, colors[1], "1", theme->color(Theme::TerminalAnsi1), "red");
- setupColor(this, colors[9], "9", theme->color(Theme::TerminalAnsi9), "bright red");
+ setupColor(this, colors[2], "2", creatorColor(Theme::TerminalAnsi2), "green");
+ setupColor(this, colors[10], "10", creatorColor(Theme::TerminalAnsi10), "bright green");
- setupColor(this, colors[2], "2", theme->color(Theme::TerminalAnsi2), "green");
- setupColor(this, colors[10], "10", theme->color(Theme::TerminalAnsi10), "bright green");
+ setupColor(this, colors[3], "3", creatorColor(Theme::TerminalAnsi3), "yellow");
+ setupColor(this, colors[11], "11", creatorColor(Theme::TerminalAnsi11), "bright yellow");
- setupColor(this, colors[3], "3", theme->color(Theme::TerminalAnsi3), "yellow");
- setupColor(this, colors[11], "11", theme->color(Theme::TerminalAnsi11), "bright yellow");
+ setupColor(this, colors[4], "4", creatorColor(Theme::TerminalAnsi4), "blue");
+ setupColor(this, colors[12], "12", creatorColor(Theme::TerminalAnsi12), "bright blue");
- setupColor(this, colors[4], "4", theme->color(Theme::TerminalAnsi4), "blue");
- setupColor(this, colors[12], "12", theme->color(Theme::TerminalAnsi12), "bright blue");
+ setupColor(this, colors[5], "5", creatorColor(Theme::TerminalAnsi5), "magenta");
+ setupColor(this, colors[13], "13", creatorColor(Theme::TerminalAnsi13), "bright magenta");
- setupColor(this, colors[5], "5", theme->color(Theme::TerminalAnsi5), "magenta");
- setupColor(this, colors[13], "13", theme->color(Theme::TerminalAnsi13), "bright magenta");
+ setupColor(this, colors[6], "6", creatorColor(Theme::TerminalAnsi6), "cyan");
+ setupColor(this, colors[14], "14", creatorColor(Theme::TerminalAnsi14), "bright cyan");
- setupColor(this, colors[6], "6", theme->color(Theme::TerminalAnsi6), "cyan");
- setupColor(this, colors[14], "14", theme->color(Theme::TerminalAnsi14), "bright cyan");
-
- setupColor(this, colors[7], "7", theme->color(Theme::TerminalAnsi7), "white");
- setupColor(this, colors[15], "15", theme->color(Theme::TerminalAnsi15), "bright white");
+ setupColor(this, colors[7], "7", creatorColor(Theme::TerminalAnsi7), "white");
+ setupColor(this, colors[15], "15", creatorColor(Theme::TerminalAnsi15), "bright white");
setLayouter([this] {
using namespace Layouting;
diff --git a/src/plugins/terminal/terminalwidget.cpp b/src/plugins/terminal/terminalwidget.cpp
index a314d0babe..c6c97ae9bd 100644
--- a/src/plugins/terminal/terminalwidget.cpp
+++ b/src/plugins/terminal/terminalwidget.cpp
@@ -83,7 +83,7 @@ void TerminalWidget::setupPty()
{
m_process = std::make_unique<Process>();
- CommandLine shellCommand = m_openParameters.shellCommand.value_or(
+ const CommandLine shellCommand = m_openParameters.shellCommand.value_or(
CommandLine{settings().shell(), settings().shellArguments(), CommandLine::Raw});
if (shellCommand.executable().isRootPath()) {
diff --git a/src/plugins/texteditor/TextEditor.json.in b/src/plugins/texteditor/TextEditor.json.in
index f2b6fbbcf5..42e32325ed 100644
--- a/src/plugins/texteditor/TextEditor.json.in
+++ b/src/plugins/texteditor/TextEditor.json.in
@@ -14,7 +14,7 @@
],
"Category" : "Core",
"Description" : "Text editor framework and the implementation of the basic text editor.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
"<?xml version='1.0'?>",
diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp
index ce6d2f4999..0faa871b5c 100644
--- a/src/plugins/texteditor/basefilefind.cpp
+++ b/src/plugins/texteditor/basefilefind.cpp
@@ -594,8 +594,7 @@ FilePaths BaseFileFind::replaceAll(const QString &text, const SearchResultItems
item.mainRange().end.column + 1);
changeSet.replace(start, end, replacement);
}
- file->setChangeSet(changeSet);
- file->apply();
+ file->apply(changeSet);
}
return changes.keys();
diff --git a/src/plugins/texteditor/bookmarkmanager.cpp b/src/plugins/texteditor/bookmarkmanager.cpp
index 6dcb4de6cc..a61cc58736 100644
--- a/src/plugins/texteditor/bookmarkmanager.cpp
+++ b/src/plugins/texteditor/bookmarkmanager.cpp
@@ -876,6 +876,7 @@ void BookmarkManager::edit()
auto layout = new QFormLayout(&dlg);
auto noteEdit = new QLineEdit(b->note());
noteEdit->setMinimumWidth(300);
+ noteEdit->setFocus();
auto lineNumberSpinbox = new QSpinBox;
lineNumberSpinbox->setRange(1, INT_MAX);
lineNumberSpinbox->setValue(b->lineNumber());
diff --git a/src/plugins/texteditor/completionsettingspage.cpp b/src/plugins/texteditor/completionsettingspage.cpp
index aba8646714..03f45404c6 100644
--- a/src/plugins/texteditor/completionsettingspage.cpp
+++ b/src/plugins/texteditor/completionsettingspage.cpp
@@ -214,7 +214,7 @@ CompletionSettingsPageWidget::CompletionSettingsPageWidget(CompletionSettingsPag
}
},
Group {
- title(Tr::tr("&Automatically insert matching characters")),
+ title(Tr::tr("&Automatically Insert Matching Characters")),
Row {
Column {
m_insertBrackets,
diff --git a/src/plugins/texteditor/displaysettingspage.cpp b/src/plugins/texteditor/displaysettingspage.cpp
index b5435ed5e9..fe914b27ce 100644
--- a/src/plugins/texteditor/displaysettingspage.cpp
+++ b/src/plugins/texteditor/displaysettingspage.cpp
@@ -108,7 +108,7 @@ public:
rightAligned->setChecked(true);
betweenLines = new QRadioButton(Tr::tr("Between lines"));
- displayAnnotations = new QGroupBox(Tr::tr("Line annotations")),
+ displayAnnotations = new QGroupBox(Tr::tr("Line Annotations")),
displayAnnotations->setCheckable(true);
using namespace Layouting;
diff --git a/src/plugins/texteditor/fontsettings.cpp b/src/plugins/texteditor/fontsettings.cpp
index e74a37c0d3..44b179d01c 100644
--- a/src/plugins/texteditor/fontsettings.cpp
+++ b/src/plugins/texteditor/fontsettings.cpp
@@ -491,8 +491,7 @@ static QString defaultFontFamily()
return QLatin1String("Menlo");
const QString sourceCodePro(g_sourceCodePro);
- const QFontDatabase dataBase;
- if (dataBase.hasFamily(sourceCodePro))
+ if (QFontDatabase::hasFamily(sourceCodePro))
return sourceCodePro;
if (Utils::HostOsInfo::isAnyUnixHost())
diff --git a/src/plugins/texteditor/fontsettingspage.cpp b/src/plugins/texteditor/fontsettingspage.cpp
index bdd899b7e0..f528496dcf 100644
--- a/src/plugins/texteditor/fontsettingspage.cpp
+++ b/src/plugins/texteditor/fontsettingspage.cpp
@@ -458,15 +458,14 @@ void FontSettingsPageWidget::updateFontZoom(const FontSettings &fontSettings)
QList<int> FontSettingsPageWidget::pointSizesForSelectedFont() const
{
- QFontDatabase db;
const QString familyName = m_fontComboBox->currentFont().family();
- QList<int> sizeLst = db.pointSizes(familyName);
+ QList<int> sizeLst = QFontDatabase::pointSizes(familyName);
if (!sizeLst.isEmpty())
return sizeLst;
- QStringList styles = db.styles(familyName);
+ QStringList styles = QFontDatabase::styles(familyName);
if (!styles.isEmpty())
- sizeLst = db.pointSizes(familyName, styles.first());
+ sizeLst = QFontDatabase::pointSizes(familyName, styles.first());
if (sizeLst.isEmpty())
sizeLst = QFontDatabase::standardSizes();
diff --git a/src/plugins/texteditor/refactoringchanges.cpp b/src/plugins/texteditor/refactoringchanges.cpp
index ebd487cc37..c8688f2fba 100644
--- a/src/plugins/texteditor/refactoringchanges.cpp
+++ b/src/plugins/texteditor/refactoringchanges.cpp
@@ -214,6 +214,9 @@ void RefactoringFile::setOpenEditor(bool activate, int pos)
bool RefactoringFile::apply()
{
+ if (m_changes.isEmpty())
+ return true;
+
// test file permissions
if (!m_filePath.isWritableFile()) {
ReadOnlyFilesDialog roDialog(m_filePath, ICore::dialogParent());
@@ -287,6 +290,12 @@ bool RefactoringFile::apply()
return result;
}
+bool RefactoringFile::apply(const Utils::ChangeSet &changeSet)
+{
+ setChangeSet(changeSet);
+ return apply();
+}
+
void RefactoringFile::setupFormattingRanges(const QList<ChangeSet::EditOp> &replaceList)
{
QTextDocument * const doc = m_editor ? m_editor->document() : m_document;
@@ -359,7 +368,7 @@ void RefactoringFile::doFormatting()
Utils::sort(m_formattingCursors, [](const auto &tc1, const auto &tc2) {
return tc1.first.selectionStart() < tc2.first.selectionStart();
});
- static const QString clangFormatLineRemovalBlocker("// QTC_TEMP");
+ static const QString clangFormatLineRemovalBlocker("");
for (auto &[formattingCursor, _] : m_formattingCursors) {
const QTextBlock firstBlock = document->findBlock(formattingCursor.selectionStart());
const QTextBlock lastBlock = document->findBlock(formattingCursor.selectionEnd());
diff --git a/src/plugins/texteditor/refactoringchanges.h b/src/plugins/texteditor/refactoringchanges.h
index 913c731df2..3e043c4f6b 100644
--- a/src/plugins/texteditor/refactoringchanges.h
+++ b/src/plugins/texteditor/refactoringchanges.h
@@ -60,6 +60,7 @@ public:
void setChangeSet(const Utils::ChangeSet &changeSet);
void setOpenEditor(bool activate = false, int pos = -1);
bool apply();
+ bool apply(const Utils::ChangeSet &changeSet);
bool create(const QString &contents, bool reindent, bool openInEditor);
protected:
diff --git a/src/plugins/texteditor/textdocument.cpp b/src/plugins/texteditor/textdocument.cpp
index 9d8133912c..7fcfe5c86c 100644
--- a/src/plugins/texteditor/textdocument.cpp
+++ b/src/plugins/texteditor/textdocument.cpp
@@ -522,10 +522,7 @@ bool TextDocument::applyChangeSet(const ChangeSet &changeSet)
{
if (changeSet.isEmpty())
return true;
- PlainRefactoringFileFactory changes;
- const RefactoringFilePtr file = changes.file(filePath());
- file->setChangeSet(changeSet);
- return file->apply();
+ return PlainRefactoringFileFactory().file(filePath())->apply(changeSet);
}
// the blocks list must be sorted
diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp
index 92636e63cd..75117598d3 100644
--- a/src/plugins/texteditor/texteditor.cpp
+++ b/src/plugins/texteditor/texteditor.cpp
@@ -354,6 +354,7 @@ public:
BaseTextEditorPrivate() = default;
TextEditorFactoryPrivate *m_origin = nullptr;
+ QByteArray m_savedNavigationState;
};
class HoverHandlerRunner
@@ -772,7 +773,6 @@ public:
QSharedPointer<TextDocument> m_document;
QList<QMetaObject::Connection> m_documentConnections;
QByteArray m_tempState;
- QByteArray m_tempNavigationState;
bool m_parenthesesMatchingEnabled = false;
QTimer m_parenthesesMatchingTimer;
@@ -1314,15 +1314,15 @@ TextEditorWidget::TextEditorWidget(QWidget *parent)
{
// "Needed", as the creation below triggers ChildEvents that are
// passed to this object's event() which uses 'd'.
- d = nullptr;
- d = new TextEditorWidgetPrivate(this);
-
+ d = std::make_unique<Internal::TextEditorWidgetPrivate>(this);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
setLayoutDirection(Qt::LeftToRight);
viewport()->setMouseTracking(true);
setFrameStyle(QFrame::NoFrame);
}
+TextEditorWidget::~TextEditorWidget() = default;
+
void TextEditorWidget::setTextDocument(const QSharedPointer<TextDocument> &doc)
{
d->setDocument(doc);
@@ -1501,12 +1501,6 @@ void TextEditorWidgetPrivate::setDocument(const QSharedPointer<TextDocument> &do
setupFromDefinition(currentDefinition());
}
-TextEditorWidget::~TextEditorWidget()
-{
- delete d;
- d = nullptr;
-}
-
void TextEditorWidget::print(QPrinter *printer)
{
const bool oldFullPage = printer->fullPage();
@@ -6391,7 +6385,7 @@ void TextEditorWidgetPrivate::paintRevisionMarker(QPainter &painter,
void TextEditorWidget::extraAreaPaintEvent(QPaintEvent *e)
{
- ExtraAreaPaintEventData data(this, d);
+ ExtraAreaPaintEventData data(this, d.get());
QTC_ASSERT(data.documentLayout, return);
QPainter painter(d->m_extraArea);
@@ -6498,7 +6492,7 @@ void TextEditorWidgetPrivate::slotUpdateRequest(const QRect &r, int dy)
void TextEditorWidgetPrivate::saveCurrentCursorPositionForNavigation()
{
m_lastCursorChangeWasInteresting = true;
- m_tempNavigationState = q->saveState();
+ emit q->saveCurrentStateForNavigationHistory();
}
void TextEditorWidgetPrivate::updateCurrentLineHighlight()
@@ -6554,8 +6548,7 @@ void TextEditorWidget::slotCursorPositionChanged()
<< "indent:" << BaseTextDocumentLayout::userData(textCursor().block())->foldingIndent();
#endif
if (!d->m_contentsChanged && d->m_lastCursorChangeWasInteresting) {
- if (EditorManager::currentEditor() && EditorManager::currentEditor()->widget() == this)
- EditorManager::addCurrentPositionToNavigationHistory(d->m_tempNavigationState);
+ emit addSavedStateToNavigationHistory();
d->m_lastCursorChangeWasInteresting = false;
} else if (d->m_contentsChanged) {
d->saveCurrentCursorPositionForNavigation();
@@ -6861,7 +6854,6 @@ void TextEditorWidget::mouseReleaseEvent(QMouseEvent *e)
{
const Qt::MouseButton button = e->button();
if (d->m_linkPressed && d->isMouseNavigationEvent(e) && button == Qt::LeftButton) {
- EditorManager::addCurrentPositionToNavigationHistory();
bool inNextSplit = ((e->modifiers() & Qt::AltModifier) && !alwaysOpenLinksInNextSplit())
|| (alwaysOpenLinksInNextSplit() && !(e->modifiers() & Qt::AltModifier));
@@ -7589,7 +7581,7 @@ bool TextEditorWidget::openLink(const Utils::Link &link, bool inNextSplit)
}
if (!inNextSplit && textDocument()->filePath() == link.targetFilePath) {
- EditorManager::addCurrentPositionToNavigationHistory();
+ emit addCurrentStateToNavigationHistory();
gotoLine(link.targetLine, link.targetColumn, true, true);
setFocus();
return true;
@@ -9059,7 +9051,7 @@ QMimeData *TextEditorWidget::createMimeDataFromSelection(bool withHtml) const
} else {
const int startPosition = current.position() - selectionStart
- removedCount;
- int endPosition = startPosition + current.text().count();
+ int endPosition = startPosition + current.text().size();
if (current != last)
endPosition++;
removedCount += endPosition - startPosition;
@@ -9605,6 +9597,23 @@ void BaseTextEditor::select(int toPos)
editorWidget()->setTextCursor(tc);
}
+void BaseTextEditor::saveCurrentStateForNavigationHistory()
+{
+ d->m_savedNavigationState = saveState();
+}
+
+void BaseTextEditor::addSavedStateToNavigationHistory()
+{
+ if (EditorManager::currentEditor() == this)
+ EditorManager::addCurrentPositionToNavigationHistory(d->m_savedNavigationState);
+}
+
+void BaseTextEditor::addCurrentStateToNavigationHistory()
+{
+ if (EditorManager::currentEditor() == this)
+ EditorManager::addCurrentPositionToNavigationHistory();
+}
+
void TextEditorWidgetPrivate::updateCursorPosition()
{
m_contextHelpItem = HelpItem();
@@ -10004,7 +10013,7 @@ void TextEditorWidget::setupGenericHighlighter()
setLineSeparatorsAllowed(true);
connect(textDocument(), &IDocument::filePathChanged,
- d, &TextEditorWidgetPrivate::reconfigure);
+ d.get(), &TextEditorWidgetPrivate::reconfigure);
}
//
@@ -10236,6 +10245,21 @@ BaseTextEditor *TextEditorFactoryPrivate::createEditorHelper(const TextDocumentP
[editor](EditorManager::OpenEditorFlags flags) {
EditorManager::activateEditor(editor, flags);
});
+ QObject::connect(
+ textEditorWidget,
+ &TextEditorWidget::saveCurrentStateForNavigationHistory,
+ editor,
+ &BaseTextEditor::saveCurrentStateForNavigationHistory);
+ QObject::connect(
+ textEditorWidget,
+ &TextEditorWidget::addSavedStateToNavigationHistory,
+ editor,
+ &BaseTextEditor::addSavedStateToNavigationHistory);
+ QObject::connect(
+ textEditorWidget,
+ &TextEditorWidget::addCurrentStateToNavigationHistory,
+ editor,
+ &BaseTextEditor::addCurrentStateToNavigationHistory);
if (m_useGenericHighlighter)
textEditorWidget->setupGenericHighlighter();
diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h
index 78dc0e0b82..7fc56e8733 100644
--- a/src/plugins/texteditor/texteditor.h
+++ b/src/plugins/texteditor/texteditor.h
@@ -169,6 +169,11 @@ public:
private:
friend class TextEditorFactory;
friend class Internal::TextEditorFactoryPrivate;
+
+ void saveCurrentStateForNavigationHistory();
+ void addSavedStateToNavigationHistory();
+ void addCurrentStateToNavigationHistory();
+
Internal::BaseTextEditorPrivate *d;
};
@@ -532,6 +537,11 @@ signals:
void requestCallHierarchy(const QTextCursor &cursor);
void toolbarOutlineChanged(QWidget *newOutline);
+ // used by the IEditor
+ void saveCurrentStateForNavigationHistory();
+ void addSavedStateToNavigationHistory();
+ void addCurrentStateToNavigationHistory();
+
protected:
QTextBlock blockForVisibleRow(int row) const;
QTextBlock blockForVerticalOffset(int offset) const;
@@ -657,7 +667,7 @@ protected:
virtual void slotCodeStyleSettingsChanged(const QVariant &); // Used in CppEditor
private:
- Internal::TextEditorWidgetPrivate *d;
+ std::unique_ptr<Internal::TextEditorWidgetPrivate> d;
friend class BaseTextEditor;
friend class TextEditorFactory;
friend class Internal::TextEditorFactoryPrivate;
diff --git a/src/plugins/texteditor/textmark.cpp b/src/plugins/texteditor/textmark.cpp
index 1739cf61b9..459d0ca27c 100644
--- a/src/plugins/texteditor/textmark.cpp
+++ b/src/plugins/texteditor/textmark.cpp
@@ -371,7 +371,7 @@ bool TextMark::addToolTipContent(QLayout *target) const
QColor TextMark::annotationColor() const
{
if (m_color.has_value())
- return Utils::creatorTheme()->color(*m_color).toHsl();
+ return Utils::creatorColor(*m_color).toHsl();
return {};
}
diff --git a/src/plugins/todo/Todo.json.in b/src/plugins/todo/Todo.json.in
index 2b6227492e..565e5286dd 100644
--- a/src/plugins/todo/Todo.json.in
+++ b/src/plugins/todo/Todo.json.in
@@ -14,6 +14,6 @@
"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" : "Adds pane that lists all TODO, FIXME, etc. entries in comments.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/todo/keyword.cpp b/src/plugins/todo/keyword.cpp
index 8a505b1239..a178bb92e1 100644
--- a/src/plugins/todo/keyword.cpp
+++ b/src/plugins/todo/keyword.cpp
@@ -8,7 +8,7 @@
namespace Todo {
namespace Internal {
-Keyword::Keyword() : color(Utils::creatorTheme()->color(Utils::Theme::TextColorNormal))
+Keyword::Keyword() : color(Utils::creatorColor(Utils::Theme::TextColorNormal))
{
}
diff --git a/src/plugins/todo/settings.cpp b/src/plugins/todo/settings.cpp
index 68f6a3d709..dc534f98c8 100644
--- a/src/plugins/todo/settings.cpp
+++ b/src/plugins/todo/settings.cpp
@@ -99,7 +99,6 @@ void Settings::load()
void Settings::setDefault()
{
scanningScope = ScanningScopeCurrentFile;
- Utils::Theme *theme = Utils::creatorTheme();
keywords.clear();
@@ -107,32 +106,32 @@ void Settings::setDefault()
keyword.name = "TODO";
keyword.iconType = IconType::Todo;
- keyword.color = theme->color(Utils::Theme::OutputPanes_NormalMessageTextColor);
+ keyword.color = creatorColor(Utils::Theme::OutputPanes_NormalMessageTextColor);
keywords.append(keyword);
keyword.name = R"(\todo)";
keyword.iconType = IconType::Todo;
- keyword.color = theme->color(Utils::Theme::OutputPanes_NormalMessageTextColor);
+ keyword.color = creatorColor(Utils::Theme::OutputPanes_NormalMessageTextColor);
keywords.append(keyword);
keyword.name = "NOTE";
keyword.iconType = IconType::Info;
- keyword.color = theme->color(Utils::Theme::OutputPanes_NormalMessageTextColor);
+ keyword.color = creatorColor(Utils::Theme::OutputPanes_NormalMessageTextColor);
keywords.append(keyword);
keyword.name = "FIXME";
keyword.iconType = IconType::Error;
- keyword.color = theme->color(Utils::Theme::OutputPanes_ErrorMessageTextColor);
+ keyword.color = creatorColor(Utils::Theme::OutputPanes_ErrorMessageTextColor);
keywords.append(keyword);
keyword.name = "BUG";
keyword.iconType = IconType::Bug;
- keyword.color = theme->color(Utils::Theme::OutputPanes_ErrorMessageTextColor);
+ keyword.color = creatorColor(Utils::Theme::OutputPanes_ErrorMessageTextColor);
keywords.append(keyword);
keyword.name = "WARNING";
keyword.iconType = IconType::Warning;
- keyword.color = theme->color(Utils::Theme::OutputPanes_WarningMessageTextColor);
+ keyword.color = creatorColor(Utils::Theme::OutputPanes_WarningMessageTextColor);
keywords.append(keyword);
keywordsEdited = false;
@@ -212,7 +211,7 @@ OptionsDialog::OptionsDialog()
}
},
Group {
- title(Tr::tr("Scanning scope")),
+ title(Tr::tr("Scanning Scope")),
Column {
m_scanInProjectRadioButton,
m_scanInCurrentFileRadioButton,
diff --git a/src/plugins/updateinfo/UpdateInfo.json.in b/src/plugins/updateinfo/UpdateInfo.json.in
index b7f15e449b..90633ebb2a 100644
--- a/src/plugins/updateinfo/UpdateInfo.json.in
+++ b/src/plugins/updateinfo/UpdateInfo.json.in
@@ -14,6 +14,6 @@
"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" : "Displays Update-Infos for Qt Installer Framework-based Updaters.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/valgrind/Valgrind.json.in b/src/plugins/valgrind/Valgrind.json.in
index 1f270b1218..c15a589ea0 100644
--- a/src/plugins/valgrind/Valgrind.json.in
+++ b/src/plugins/valgrind/Valgrind.json.in
@@ -14,6 +14,6 @@
],
"Category" : "Code Analyzer",
"Description" : "Valgrind Plugin.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/valgrind/valgrindmemcheckparsertest.cpp b/src/plugins/valgrind/valgrindmemcheckparsertest.cpp
index 44cac3712c..90d1f27ca0 100644
--- a/src/plugins/valgrind/valgrindmemcheckparsertest.cpp
+++ b/src/plugins/valgrind/valgrindmemcheckparsertest.cpp
@@ -187,9 +187,9 @@ void ValgrindMemcheckParserTest::initTest(const QString &testfile, const QString
QVERIFY2(fileInfo.isExecutable(), qPrintable(fakeValgrind));
QVERIFY2(!fileInfo.isDir(), qPrintable(fakeValgrind));
- const QStringList args = {QString("--xml-socket=127.0.0.1:%1").arg(m_server->serverPort()),
- "-i", testfile};
- m_process->setCommand({FilePath::fromString(fakeValgrind), args + otherArgs});
+ m_process->setCommand({FilePath::fromString(fakeValgrind),
+ {QString("--xml-socket=127.0.0.1:%1").arg(m_server->serverPort()),
+ "-i", testfile, otherArgs}});
m_process->start();
using namespace std::chrono_literals;
@@ -545,7 +545,7 @@ void ValgrindMemcheckParserTest::testRealValgrind()
debuggee.command.setExecutable(FilePath::fromString(executable));
debuggee.environment = sysEnv;
ValgrindProcess runner;
- runner.setValgrindCommand({"valgrind", {}});
+ runner.setValgrindCommand(CommandLine{"valgrind"});
runner.setDebuggee(debuggee);
RunnerDumper dumper(&runner);
runner.runBlocking();
diff --git a/src/plugins/valgrind/valgrindsettings.cpp b/src/plugins/valgrind/valgrindsettings.cpp
index b520f32892..47f4597374 100644
--- a/src/plugins/valgrind/valgrindsettings.cpp
+++ b/src/plugins/valgrind/valgrindsettings.cpp
@@ -116,7 +116,7 @@ SuppressionAspect::~SuppressionAspect()
delete d;
}
-void SuppressionAspect::addToLayout(Layouting::LayoutItem &parent)
+void SuppressionAspect::addToLayout(Layouting::Layout &parent)
{
QTC_CHECK(!d->addEntry);
QTC_CHECK(!d->removeEntry);
diff --git a/src/plugins/valgrind/valgrindsettings.h b/src/plugins/valgrind/valgrindsettings.h
index 93866bae86..43a2b94592 100644
--- a/src/plugins/valgrind/valgrindsettings.h
+++ b/src/plugins/valgrind/valgrindsettings.h
@@ -20,7 +20,7 @@ public:
SuppressionAspect(Utils::AspectContainer *container, bool global);
~SuppressionAspect() final;
- void addToLayout(Layouting::LayoutItem &parent) final;
+ void addToLayout(Layouting::Layout &parent) final;
void fromMap(const Utils::Store &map) final;
void toMap(Utils::Store &map) const final;
diff --git a/src/plugins/valgrind/valgrindtestrunnertest.cpp b/src/plugins/valgrind/valgrindtestrunnertest.cpp
index 7d53efca33..863265f8d0 100644
--- a/src/plugins/valgrind/valgrindtestrunnertest.cpp
+++ b/src/plugins/valgrind/valgrindtestrunnertest.cpp
@@ -93,11 +93,8 @@ QString ValgrindTestRunnerTest::runTestBinary(const QString &binary, const QStri
debuggee.command.setExecutable(Utils::FilePath::fromString(binPath));
debuggee.environment = Utils::Environment::systemEnvironment();
- CommandLine valgrind{"valgrind", {"--num-callers=50", "--track-origins=yes"}};
- valgrind.addArgs(vArgs);
-
m_runner->setLocalServerAddress(QHostAddress::LocalHost);
- m_runner->setValgrindCommand(valgrind);
+ m_runner->setValgrindCommand({"valgrind", {"--num-callers=50", "--track-origins=yes", vArgs}});
m_runner->setDebuggee(debuggee);
m_runner->runBlocking();
return binPath;
diff --git a/src/plugins/valgrind/xmlprotocol/parser.cpp b/src/plugins/valgrind/xmlprotocol/parser.cpp
index 2bab91fa71..550a021a87 100644
--- a/src/plugins/valgrind/xmlprotocol/parser.cpp
+++ b/src/plugins/valgrind/xmlprotocol/parser.cpp
@@ -10,8 +10,6 @@
#include "suppression.h"
#include "../valgrindtr.h"
-#include <extensionsystem/pluginmanager.h>
-
#include <utils/async.h>
#include <utils/expected.h>
#include <utils/futuresynchronizer.h>
@@ -684,7 +682,7 @@ public:
if (!m_watcher)
return;
m_thread->cancel();
- ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(m_watcher->future());
+ Utils::futureSynchronizer()->addFuture(m_watcher->future());
}
void start()
diff --git a/src/plugins/vcpkg/Vcpkg.json.in b/src/plugins/vcpkg/Vcpkg.json.in
index a24d835d91..5f7ec224e2 100644
--- a/src/plugins/vcpkg/Vcpkg.json.in
+++ b/src/plugins/vcpkg/Vcpkg.json.in
@@ -15,7 +15,7 @@
"Experimental" : true,
"SoftLoadable" : true,
"Description" : "vcpkg integration.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [
diff --git a/src/plugins/vcsbase/VcsBase.json.in b/src/plugins/vcsbase/VcsBase.json.in
index 87e343ff45..2e92383ae1 100644
--- a/src/plugins/vcsbase/VcsBase.json.in
+++ b/src/plugins/vcsbase/VcsBase.json.in
@@ -14,6 +14,6 @@
],
"Category" : "Version Control",
"Description" : "Version Control System Base Plugin.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/vcsbase/commonvcssettings.cpp b/src/plugins/vcsbase/commonvcssettings.cpp
index 6c6db16c3e..1dbbb130dc 100644
--- a/src/plugins/vcsbase/commonvcssettings.cpp
+++ b/src/plugins/vcsbase/commonvcssettings.cpp
@@ -92,9 +92,9 @@ CommonVcsSettings::CommonVcsSettings()
empty,
PushButton {
text(Tr::tr("Reset VCS Cache")),
- tooltip(Tr::tr("Reset information about which "
- "version control system handles which directory.")),
- onClicked(&VcsManager::clearVersionControlCache)
+ Layouting::toolTip(Tr::tr("Reset information about which "
+ "version control system handles which directory.")),
+ onClicked(&VcsManager::clearVersionControlCache, (QObject *)nullptr)
}
}
};
diff --git a/src/plugins/vcsbase/submiteditorwidget.cpp b/src/plugins/vcsbase/submiteditorwidget.cpp
index b8f3d2a151..73d8358656 100644
--- a/src/plugins/vcsbase/submiteditorwidget.cpp
+++ b/src/plugins/vcsbase/submiteditorwidget.cpp
@@ -585,8 +585,7 @@ void SubmitEditorWidget::verifyDescription()
}
auto fontColor = [](Utils::Theme::Color color) {
- return QString("<font color=\"%1\">")
- .arg(Utils::creatorTheme()->color(color).name());
+ return QString("<font color=\"%1\">").arg(Utils::creatorColor(color).name());
};
const QString hint = fontColor(Utils::Theme::OutputPanes_TestWarnTextColor);
const QString warning = fontColor(Utils::Theme::TextColorError);
diff --git a/src/plugins/vcsbase/submitfilemodel.cpp b/src/plugins/vcsbase/submitfilemodel.cpp
index 02a9b6e359..a07538076d 100644
--- a/src/plugins/vcsbase/submitfilemodel.cpp
+++ b/src/plugins/vcsbase/submitfilemodel.cpp
@@ -45,7 +45,7 @@ static QBrush fileStatusTextForeground(SubmitFileModel::FileStatusHint statusHin
statusTextColor = Theme::VcsBase_FileUnmerged_TextColor;
break;
}
- return QBrush(Utils::creatorTheme()->color(statusTextColor));
+ return QBrush(Utils::creatorColor(statusTextColor));
}
static QList<QStandardItem *> createFileRow(const FilePath &repositoryRoot,
diff --git a/src/plugins/vcsbase/vcsbaseclient.cpp b/src/plugins/vcsbase/vcsbaseclient.cpp
index 6a36d628bd..1ce9e6d5a8 100644
--- a/src/plugins/vcsbase/vcsbaseclient.cpp
+++ b/src/plugins/vcsbase/vcsbaseclient.cpp
@@ -125,7 +125,7 @@ QStringList VcsBaseClientImpl::splitLines(const QString &s)
QString VcsBaseClientImpl::stripLastNewline(const QString &in)
{
if (in.endsWith('\n'))
- return in.left(in.count() - 1);
+ return in.left(in.size() - 1);
return in;
}
diff --git a/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp b/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp
index 4ba6baba34..8ab4a7946f 100644
--- a/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp
+++ b/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp
@@ -3,11 +3,8 @@
#include "vcsbasediffeditorcontroller.h"
-#include <extensionsystem/pluginmanager.h>
-
#include <utils/async.h>
#include <utils/environment.h>
-#include <utils/futuresynchronizer.h>
#include <utils/qtcprocess.h>
using namespace DiffEditor;
@@ -41,7 +38,6 @@ VcsBaseDiffEditorController::~VcsBaseDiffEditorController()
GroupItem VcsBaseDiffEditorController::postProcessTask(const Storage<QString> &inputStorage)
{
const auto onSetup = [inputStorage](Async<QList<FileData>> &async) {
- async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
async.setConcurrentCallData(&DiffUtils::readPatchWithPromise, *inputStorage);
};
const auto onDone = [this](const Async<QList<FileData>> &async, DoneWith result) {
diff --git a/src/plugins/vcsbase/vcsbaseeditor.cpp b/src/plugins/vcsbase/vcsbaseeditor.cpp
index 462412df06..7efdd7d20f 100644
--- a/src/plugins/vcsbase/vcsbaseeditor.cpp
+++ b/src/plugins/vcsbase/vcsbaseeditor.cpp
@@ -435,7 +435,7 @@ bool UrlTextCursorHandler::findContentsUnderCursor(const QTextCursor &cursor)
void UrlTextCursorHandler::highlightCurrentContents()
{
- const QColor linkColor = creatorTheme()->color(Theme::TextColorLink);
+ const QColor linkColor = creatorColor(Theme::TextColorLink);
QTextEdit::ExtraSelection sel;
sel.cursor = currentCursor();
sel.cursor.setPosition(currentCursor().position()
@@ -1638,7 +1638,7 @@ IEditor *VcsBaseEditor::locateEditorByTag(const QString &tag)
const QList<IDocument *> documents = DocumentModel::openedDocuments();
for (IDocument *document : documents) {
const QVariant tagPropertyValue = document->property(tagPropertyC);
- if (tagPropertyValue.type() == QVariant::String && tagPropertyValue.toString() == tag)
+ if (tagPropertyValue.typeId() == QMetaType::QString && tagPropertyValue.toString() == tag)
return DocumentModel::editorsForDocument(document).constFirst();
}
return nullptr;
diff --git a/src/plugins/vcsbase/wizard/vcscommandpage.cpp b/src/plugins/vcsbase/wizard/vcscommandpage.cpp
index c24cdfce0e..caabb20cf2 100644
--- a/src/plugins/vcsbase/wizard/vcscommandpage.cpp
+++ b/src/plugins/vcsbase/wizard/vcscommandpage.cpp
@@ -72,9 +72,9 @@ WizardPage *VcsCommandPageFactory::create(JsonWizard *wizard, Id typeId, const Q
QStringList args;
const QVariant argsVar = tmp.value(QLatin1String(VCSCOMMAND_EXTRA_ARGS));
if (!argsVar.isNull()) {
- if (argsVar.type() == QVariant::String) {
+ if (argsVar.typeId() == QMetaType::QString) {
args << argsVar.toString();
- } else if (argsVar.type() == QVariant::List) {
+ } else if (argsVar.typeId() == QMetaType::QVariantList) {
args = Utils::transform(argsVar.toList(), &QVariant::toString);
} else {
return nullptr;
@@ -101,7 +101,7 @@ WizardPage *VcsCommandPageFactory::create(JsonWizard *wizard, Id typeId, const Q
const QVariant &jobArgVar = job.value(QLatin1String(JOB_ARGUMENTS));
QStringList jobArgs;
if (!jobArgVar.isNull()) {
- if (jobArgVar.type() == QVariant::List)
+ if (jobArgVar.typeId() == QMetaType::QVariantList)
jobArgs = Utils::transform(jobArgVar.toList(), &QVariant::toString);
else
jobArgs << jobArgVar.toString();
@@ -127,7 +127,7 @@ bool VcsCommandPageFactory::validateData(Id typeId, const QVariant &data, QStrin
QTC_ASSERT(canCreate(typeId), return false);
QString em;
- if (data.type() != QVariant::Map)
+ if (data.typeId() != QMetaType::QVariantMap)
em = Tr::tr("\"data\" is no JSON object in \"VcsCommand\" page.");
if (em.isEmpty()) {
@@ -161,13 +161,13 @@ bool VcsCommandPageFactory::validateData(Id typeId, const QVariant &data, QStrin
}
const QVariant extra = tmp.value(QLatin1String(VCSCOMMAND_EXTRA_ARGS));
- if (!extra.isNull() && extra.type() != QVariant::String && extra.type() != QVariant::List) {
+ if (!extra.isNull() && extra.typeId() != QMetaType::QString && extra.typeId() != QMetaType::QVariantList) {
em = Tr::tr("\"%1\" in \"data\" section of \"VcsCommand\" page has unexpected type (unset, String or List).")
.arg(QLatin1String(VCSCOMMAND_EXTRA_ARGS));
}
const QVariant jobs = tmp.value(QLatin1String(VCSCOMMAND_JOBS));
- if (!jobs.isNull() && extra.type() != QVariant::List) {
+ if (!jobs.isNull() && extra.typeId() != QMetaType::QVariantList) {
em = Tr::tr("\"%1\" in \"data\" section of \"VcsCommand\" page has unexpected type (unset or List).")
.arg(QLatin1String(VCSCOMMAND_JOBS));
}
@@ -178,7 +178,7 @@ bool VcsCommandPageFactory::validateData(Id typeId, const QVariant &data, QStrin
em = Tr::tr("Job in \"VcsCommand\" page is empty.");
break;
}
- if (j.type() != QVariant::Map) {
+ if (j.typeId() != QMetaType::QVariantMap) {
em = Tr::tr("Job in \"VcsCommand\" page is not an object.");
break;
}
@@ -383,11 +383,11 @@ void VcsCommandPage::finished(bool success)
if (success) {
m_state = Succeeded;
message = Tr::tr("Succeeded.");
- palette.setColor(QPalette::WindowText, creatorTheme()->color(Theme::TextColorNormal).name());
+ palette.setColor(QPalette::WindowText, creatorColor(Theme::TextColorNormal).name());
} else {
m_state = Failed;
message = Tr::tr("Failed.");
- palette.setColor(QPalette::WindowText, creatorTheme()->color(Theme::TextColorError).name());
+ palette.setColor(QPalette::WindowText, creatorColor(Theme::TextColorError).name());
}
m_statusLabel->setText(message);
diff --git a/src/plugins/vcsbase/wizard/vcsconfigurationpage.cpp b/src/plugins/vcsbase/wizard/vcsconfigurationpage.cpp
index 2de4297563..0a591be695 100644
--- a/src/plugins/vcsbase/wizard/vcsconfigurationpage.cpp
+++ b/src/plugins/vcsbase/wizard/vcsconfigurationpage.cpp
@@ -56,7 +56,7 @@ bool VcsConfigurationPageFactory::validateData(Id typeId, const QVariant &data,
{
QTC_ASSERT(canCreate(typeId), return false);
- if (data.isNull() || data.type() != QVariant::Map) {
+ if (data.isNull() || data.typeId() != QMetaType::QVariantMap) {
//: Do not translate "VcsConfiguration", because it is the id of a page.
*errorMessage = ProjectExplorer::Tr::tr("\"data\" must be a JSON object for \"VcsConfiguration\" pages.");
return false;
diff --git a/src/plugins/webassembly/WebAssembly.json.in b/src/plugins/webassembly/WebAssembly.json.in
index f860568cee..962ba9eef7 100644
--- a/src/plugins/webassembly/WebAssembly.json.in
+++ b/src/plugins/webassembly/WebAssembly.json.in
@@ -16,6 +16,6 @@
],
"Category" : "Device Support",
"Description" : "Helper for WebAssembly projects.",
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/webassembly/webassemblyrunconfiguration.cpp b/src/plugins/webassembly/webassemblyrunconfiguration.cpp
index 1c8f6b60c1..697808e70e 100644
--- a/src/plugins/webassembly/webassemblyrunconfiguration.cpp
+++ b/src/plugins/webassembly/webassemblyrunconfiguration.cpp
@@ -131,7 +131,7 @@ public:
addDataExtractor(this, &WebBrowserSelectionAspect::currentBrowser, &Data::currentBrowser);
}
- void addToLayout(Layouting::LayoutItem &parent) override
+ void addToLayout(Layouting::Layout &parent) override
{
QTC_CHECK(!m_webBrowserComboBox);
m_webBrowserComboBox = new QComboBox;
diff --git a/src/plugins/webassembly/webassemblysettings.cpp b/src/plugins/webassembly/webassemblysettings.cpp
index d09919a05b..74e4f65b84 100644
--- a/src/plugins/webassembly/webassemblysettings.cpp
+++ b/src/plugins/webassembly/webassemblysettings.cpp
@@ -21,6 +21,7 @@
#include <utils/pathchooser.h>
#include <utils/utilsicons.h>
+#include <QGroupBox>
#include <QTextBrowser>
#include <QTimer>
diff --git a/src/plugins/welcome/Welcome.json.in b/src/plugins/welcome/Welcome.json.in
index 40f7e1ee3f..4dc8529e66 100644
--- a/src/plugins/welcome/Welcome.json.in
+++ b/src/plugins/welcome/Welcome.json.in
@@ -20,6 +20,6 @@
"Description" : "Do not ask for taking a UI tour on startup"
}
],
- "Url" : "http://www.qt.io",
+ "Url" : "https://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}
diff --git a/src/plugins/welcome/welcomeplugin.cpp b/src/plugins/welcome/welcomeplugin.cpp
index 069fe1e263..68e6d549f8 100644
--- a/src/plugins/welcome/welcomeplugin.cpp
+++ b/src/plugins/welcome/welcomeplugin.cpp
@@ -165,7 +165,7 @@ public:
customMargin({HPaddingM, VPaddingM, HPaddingM, VPaddingM}),
},
createRule(Qt::Horizontal),
- noMargin(), spacing(0),
+ noMargin, spacing(0),
}.attachTo(this);
}
};
@@ -194,7 +194,7 @@ public:
m_essentials = new QWidget;
Column essentials {
spacing(0),
- noMargin(),
+ noMargin,
};
{
@@ -232,7 +232,7 @@ public:
mainColumn.addItem(st);
{
- auto label = new Label(Tr::tr("Explore more"), Label::Secondary);
+ auto label = new Core::Label(Tr::tr("Explore more"), Core::Label::Secondary);
label->setContentsMargins(HPaddingXxs, 0, 0, 0); // Is indented in Figma design
Column linksLayout {
@@ -275,7 +275,7 @@ public:
Row {
mainColumn,
createRule(Qt::Vertical),
- noMargin(), spacing(0),
+ noMargin, spacing(0),
}.attachTo(mainWidget);
setWidget(mainWidget);
@@ -342,7 +342,7 @@ WelcomeMode::WelcomeMode()
m_sideArea,
m_pageStack,
},
- noMargin(),
+ noMargin,
spacing(0),
}.attachTo(m_modeWidget);
diff --git a/src/shared/qbs b/src/shared/qbs
-Subproject 488fbe40e86602d06e568a1749277387fd4a565
+Subproject 1ee1e51fa98f89ae4ee977f6074d070f5788c63
diff --git a/src/shared/qtsingleapplication/qtlocalpeer.cpp b/src/shared/qtsingleapplication/qtlocalpeer.cpp
index 47dd3805ba..4bbdab4aba 100644
--- a/src/shared/qtsingleapplication/qtlocalpeer.cpp
+++ b/src/shared/qtsingleapplication/qtlocalpeer.cpp
@@ -60,6 +60,7 @@ QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
+ QLatin1Char('/') + socketName
+ QLatin1String("-lockfile");
lockFile.reset(new QLockFile(lockName));
+ lockFile->setStaleLockTime(0);
}
bool QtLocalPeer::isClient()
diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt
index b9f1a1544c..a4e3e44174 100644
--- a/src/tools/qml2puppet/CMakeLists.txt
+++ b/src/tools/qml2puppet/CMakeLists.txt
@@ -26,10 +26,7 @@ if (NOT QT_CREATOR_API_DEFINED)
COMPONENTS Concurrent Core Gui Network PrintSupport Qml Quick Sql Widgets Xml
REQUIRED
)
-endif()
-
-if (NOT TARGET QmlPuppetCommunication)
- include(../../libs/qmlpuppetcommunication/QmlPuppetCommunication.cmake)
+ set(IS_STAND_ALONE_PUPPET_BUILD ON)
endif()
add_qtc_executable(qml2puppet
@@ -44,31 +41,20 @@ add_qtc_executable(qml2puppet
SOURCES
qml2puppet/qml2puppetmain.cpp
qml2puppet/qmlbase.h
- qml2puppet/appmetadata.cpp qml2puppet/appmetadata.h
- qml2puppet/qmlpuppet.h qml2puppet/qmlpuppet.cpp qml2puppet/configcrashpad.h
+ qml2puppet/qmlpuppet.h qml2puppet/qmlpuppet.cpp
+ qml2puppet/configcrashpad.h
qmlpuppet.qrc
PROPERTIES
OUTPUT_NAME qml2puppet-${IDE_VERSION}
)
-if (TARGET qml2puppet)
- execute_process(
- COMMAND git describe --tags --always --dirty=+
- WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
- RESULT_VARIABLE GIT_SHA_RESULT
- OUTPUT_VARIABLE GIT_SHA
- ERROR_VARIABLE GIT_SHA_ERROR
- OUTPUT_STRIP_TRAILING_WHITESPACE
- )
-
- #if we are not a git repository use the .tag file
- if(NOT GIT_SHA)
- file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/../../../.tag GIT_SHA LIMIT_COUNT 1)
- endif()
-
- set(IDE_REVISION_STR ${GIT_SHA})
-
- configure_file(../../app/app_version.h.cmakein app/app_version.h ESCAPE_QUOTES)
+if (IS_STAND_ALONE_PUPPET_BUILD)
+ include(../../libs/qmlpuppetcommunication/QmlPuppetCommunication.cmake)
+ configure_file(../../app/app_version.h.cmakein app/app_version.h ESCAPE_QUOTES)
+else()
+ extend_qtc_executable(qml2puppet
+ DEPENDS app_version
+ )
endif()
extend_qtc_executable(qml2puppet
@@ -107,6 +93,7 @@ extend_qtc_executable(qml2puppet
linegeometry.cpp linegeometry.h
icongizmoimageprovider.cpp icongizmoimageprovider.h
boxgeometry.cpp boxgeometry.h
+ lookatgeometry.cpp lookatgeometry.h
)
find_package(Qt6 COMPONENTS Quick3DAssetImport QUIET)
@@ -159,6 +146,7 @@ extend_qtc_executable(qml2puppet
qmltransitionnodeinstance.cpp qmltransitionnodeinstance.h
qt3dpresentationnodeinstance.cpp qt3dpresentationnodeinstance.h
qt5bakelightsnodeinstanceserver.cpp qt5bakelightsnodeinstanceserver.h
+ qt5import3dnodeinstanceserver.cpp qt5import3dnodeinstanceserver.h
qt5informationnodeinstanceserver.cpp qt5informationnodeinstanceserver.h
qt5nodeinstanceclientproxy.cpp qt5nodeinstanceclientproxy.h
qt5nodeinstanceserver.cpp qt5nodeinstanceserver.h
diff --git a/src/tools/qml2puppet/editor3d_qt6.qrc b/src/tools/qml2puppet/editor3d_qt6.qrc
index d76b1941b9..85df241f16 100644
--- a/src/tools/qml2puppet/editor3d_qt6.qrc
+++ b/src/tools/qml2puppet/editor3d_qt6.qrc
@@ -19,6 +19,8 @@
<file>mockfiles/images/spot@2x.png</file>
<file>mockfiles/images/preview_landscape.hdr</file>
<file>mockfiles/images/preview_studio.hdr</file>
+ <file>mockfiles/images/reflectionprobe.png</file>
+ <file>mockfiles/images/reflectionprobe@2x.png</file>
<file>mockfiles/qt6/AdjustableArrow.qml</file>
<file>mockfiles/qt6/Arrow.qml</file>
<file>mockfiles/qt6/AutoScaleHelper.qml</file>
@@ -35,6 +37,7 @@
<file>mockfiles/qt6/LightIconGizmo.qml</file>
<file>mockfiles/qt6/LightModel.qml</file>
<file>mockfiles/qt6/Line3D.qml</file>
+ <file>mockfiles/qt6/LookAtGizmo.qml</file>
<file>mockfiles/qt6/MaterialNodeView.qml</file>
<file>mockfiles/qt6/ModelNode2DImageView.qml</file>
<file>mockfiles/qt6/ModelNode3DImageView.qml</file>
@@ -50,6 +53,7 @@
<file>mockfiles/qt6/PlanarMoveHandle.qml</file>
<file>mockfiles/qt6/PlanarScaleHandle.qml</file>
<file>mockfiles/qt6/ReflectionProbeBox.qml</file>
+ <file>mockfiles/qt6/ReflectionProbeGizmo.qml</file>
<file>mockfiles/qt6/RotateGizmo.qml</file>
<file>mockfiles/qt6/RotateRing.qml</file>
<file>mockfiles/qt6/ScaleGizmo.qml</file>
diff --git a/src/tools/qml2puppet/mockfiles/images/reflectionprobe.png b/src/tools/qml2puppet/mockfiles/images/reflectionprobe.png
new file mode 100644
index 0000000000..4f31a98f65
--- /dev/null
+++ b/src/tools/qml2puppet/mockfiles/images/reflectionprobe.png
Binary files differ
diff --git a/src/tools/qml2puppet/mockfiles/images/reflectionprobe@2x.png b/src/tools/qml2puppet/mockfiles/images/reflectionprobe@2x.png
new file mode 100644
index 0000000000..2aa3124a41
--- /dev/null
+++ b/src/tools/qml2puppet/mockfiles/images/reflectionprobe@2x.png
Binary files differ
diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml
index 255d93e529..36d67c534d 100644
--- a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml
+++ b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml
@@ -26,9 +26,10 @@ Item {
readonly property vector3d _defaultCameraPosition: Qt.vector3d(0, 600, 600)
readonly property vector3d _defaultCameraRotation: Qt.vector3d(-45, 0, 0)
readonly property real _defaultCameraLookAtDistance: _defaultCameraPosition.length()
- readonly property real _keyPanAmount: _generalHelper.cameraSpeed
+ readonly property real _keyPanAmount: 1.0
property bool ignoreToolState: false
property bool flyMode: viewRoot.flyMode
+ property bool showCrosshairs: false
z: 10
anchors.fill: parent
@@ -112,7 +113,7 @@ Item {
if (resolvedResult) {
var newLookAtAndZoom = _generalHelper.approachNode(camera, _defaultCameraLookAtDistance,
- resolvedResult, view3D);
+ resolvedResult, view3d);
_lookAtPoint = newLookAtAndZoom.toVector3d();
_zoomFactor = newLookAtAndZoom.w;
storeCameraState(0);
@@ -173,11 +174,15 @@ Item {
function rotateCamera(angles)
{
+ if (flyMode)
+ showCrosshairs = true;
cameraCtrl._lookAtPoint = _generalHelper.rotateCamera(camera, angles, _lookAtPoint);
}
function moveCamera(moveVec)
{
+ if (flyMode)
+ showCrosshairs = true;
cameraCtrl._lookAtPoint = _generalHelper.moveCamera(camera, _lookAtPoint, moveVec);
}
@@ -243,12 +248,19 @@ Item {
cameraCtrl._dragging = false;
cameraCtrl.storeCameraState(0);
}
- _generalHelper.stopAllCameraMoves()
+ showCrosshairs = false;
+ _generalHelper.stopAllCameraMoves();
+ _generalHelper.setCameraSpeedModifier(1.0);
+ }
+
+ on_LookAtPointChanged: {
+ viewRoot.overlayViews[splitId].lookAtGizmo.position = _lookAtPoint;
}
Connections {
target: _generalHelper
enabled: viewRoot.activeSplit === cameraCtrl.splitId
+
function onRequestCameraMove(camera, moveVec) {
if (camera === cameraCtrl.camera) {
cameraCtrl.moveCamera(moveVec);
@@ -260,7 +272,7 @@ Item {
Image {
anchors.centerIn: parent
source: "qrc:///qtquickplugin/mockfiles/images/crosshair.png"
- visible: cameraCtrl.flyMode && viewRoot.activeSplit === cameraCtrl.splitId
+ visible: cameraCtrl.showCrosshairs && viewRoot.activeSplit === cameraCtrl.splitId
opacity: 0.7
}
@@ -317,7 +329,7 @@ Item {
onWheel: (wheel) => {
if (cameraCtrl.flyMode && cameraCtrl.splitId !== viewRoot.activeSplit)
return;
- viewRoot.activeSplit = cameraCtrl.splitId
+ viewRoot.activeSplit = cameraCtrl.splitId;
if (cameraCtrl.camera) {
// Empirically determined divisor for nice zoom
cameraCtrl.zoomRelative(wheel.angleDelta.y / -40);
@@ -326,7 +338,17 @@ Item {
}
}
+ function setCameraSpeed(event) {
+ if (event.modifiers === Qt.AltModifier)
+ _generalHelper.setCameraSpeedModifier(0.5);
+ else if (event.modifiers === Qt.ShiftModifier)
+ _generalHelper.setCameraSpeedModifier(2.0);
+ else
+ _generalHelper.setCameraSpeedModifier(1.0);
+ }
+
Keys.onPressed: (event) => {
+ setCameraSpeed(event)
event.accepted = true;
if (cameraCtrl.flyMode && event.key === Qt.Key_Space)
approachObject();
@@ -335,6 +357,7 @@ Item {
}
Keys.onReleased: (event) => {
+ setCameraSpeed(event)
event.accepted = true;
_generalHelper.stopCameraMove(cameraCtrl.getMoveVectorForKey(event.key));
}
diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml
index 23fc1c6a78..751d21c0e8 100644
--- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml
+++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml
@@ -25,6 +25,7 @@ Item {
property bool showEditLight: false
property bool showGrid: true
+ property bool showLookAt: true
property bool showSelectionBox: true
property bool showIconGizmo: true
property bool showCameraFrustum: false
@@ -35,9 +36,10 @@ Item {
property color backgroundGradientColorStart: "#222222"
property color backgroundGradientColorEnd: "#999999"
property color gridColor: "#cccccc"
- property bool syncEnvBackground: false
+ property bool syncEnvBackground: true
property bool splitView: false
property bool flyMode: false
+ property bool showCameraSpeed: false
enum SelectionMode { Item, Group }
enum TransformMode { Move, Rotate, Scale }
@@ -65,6 +67,7 @@ Item {
onShowEditLightChanged: _generalHelper.storeToolState(sceneId, "showEditLight", showEditLight)
onGlobalOrientationChanged: _generalHelper.storeToolState(sceneId, "globalOrientation", globalOrientation)
onShowGridChanged: _generalHelper.storeToolState(sceneId, "showGrid", showGrid);
+ onShowLookAtChanged: _generalHelper.storeToolState(sceneId, "showLookAt", showLookAt);
onSyncEnvBackgroundChanged: _generalHelper.storeToolState(sceneId, "syncEnvBackground", syncEnvBackground);
onShowSelectionBoxChanged: _generalHelper.storeToolState(sceneId, "showSelectionBox", showSelectionBox);
onShowIconGizmoChanged: _generalHelper.storeToolState(sceneId, "showIconGizmo", showIconGizmo);
@@ -165,7 +168,7 @@ Item {
break;
}
}
- showEditLight = !hasSceneLight;
+ showEditLight = !hasSceneLight && !_generalHelper.sceneHasLightProbe(sceneId);
// Don't inherit camera angles from the previous scene
for (let i = 0; i < 4; ++i)
@@ -264,16 +267,22 @@ Item {
for (var i = 0; i < 4; ++i) {
if (syncEnvBackground) {
- let bgMode = _generalHelper.sceneEnvironmentBgMode(sceneId);
- if ((!_generalHelper.sceneEnvironmentLightProbe(sceneId) && bgMode === SceneEnvironment.SkyBox)
- || (!_generalHelper.sceneEnvironmentSkyBoxCubeMap(sceneId) && bgMode === SceneEnvironment.SkyBoxCubeMap)) {
- editViews[i].sceneEnv.backgroundMode = SceneEnvironment.Color;
- } else {
- editViews[i].sceneEnv.backgroundMode = bgMode;
+ if (_generalHelper.hasSceneEnvironmentData(sceneId)) {
+ let bgMode = _generalHelper.sceneEnvironmentBgMode(sceneId);
+ if ((!_generalHelper.sceneEnvironmentLightProbe(sceneId) && bgMode === SceneEnvironment.SkyBox)
+ || (!_generalHelper.sceneEnvironmentSkyBoxCubeMap(sceneId) && bgMode === SceneEnvironment.SkyBoxCubeMap)) {
+ editViews[i].sceneEnv.backgroundMode = SceneEnvironment.Color;
+ } else {
+ editViews[i].sceneEnv.backgroundMode = bgMode;
+ }
+ editViews[i].sceneEnv.lightProbe = _generalHelper.sceneEnvironmentLightProbe(sceneId);
+ editViews[i].sceneEnv.skyBoxCubeMap = _generalHelper.sceneEnvironmentSkyBoxCubeMap(sceneId);
+ editViews[i].sceneEnv.clearColor = _generalHelper.sceneEnvironmentColor(sceneId);
+ } else if (activeScene) {
+ _generalHelper.updateSceneEnvToLast(editViews[i].sceneEnv,
+ editViews[i].defaultLightProbe,
+ editViews[i].defaultCubeMap);
}
- editViews[i].sceneEnv.lightProbe = _generalHelper.sceneEnvironmentLightProbe(sceneId);
- editViews[i].sceneEnv.skyBoxCubeMap = _generalHelper.sceneEnvironmentSkyBoxCubeMap(sceneId);
- editViews[i].sceneEnv.clearColor = _generalHelper.sceneEnvironmentColor(sceneId);
} else {
editViews[i].sceneEnv.backgroundMode = SceneEnvironment.Transparent;
editViews[i].sceneEnv.lightProbe = null;
@@ -297,11 +306,16 @@ Item {
else if (resetToDefault)
showGrid = true;
+ if ("showLookAt" in toolStates)
+ showLookAt = toolStates.showLookAt;
+ else if (resetToDefault)
+ showLookAt = true;
+
if ("syncEnvBackground" in toolStates) {
syncEnvBackground = toolStates.syncEnvBackground;
updateEnvBackground();
} else if (resetToDefault) {
- syncEnvBackground = false;
+ syncEnvBackground = true;
updateEnvBackground();
}
@@ -353,10 +367,13 @@ Item {
cameraControls[i].restoreDefaultState();
}
- if ("flyMode" in toolStates)
+ if ("flyMode" in toolStates) {
flyMode = toolStates.flyMode;
- else if (resetToDefault)
+ viewRoot.showCameraSpeed = false;
+ } else if (resetToDefault) {
flyMode = false;
+ viewRoot.showCameraSpeed = false;
+ }
if ("splitView" in toolStates)
splitView = toolStates.splitView;
@@ -383,6 +400,7 @@ Item {
{
_generalHelper.storeToolState(sceneId, "showEditLight", showEditLight)
_generalHelper.storeToolState(sceneId, "showGrid", showGrid)
+ _generalHelper.storeToolState(sceneId, "showLookAt", showLookAt)
_generalHelper.storeToolState(sceneId, "syncEnvBackground", syncEnvBackground)
_generalHelper.storeToolState(sceneId, "showSelectionBox", showSelectionBox)
_generalHelper.storeToolState(sceneId, "showIconGizmo", showIconGizmo)
@@ -519,6 +537,12 @@ Item {
overlayViews[i].addParticleEmitterGizmo(scene, obj);
}
+ function addReflectionProbeGizmo(scene, obj)
+ {
+ for (var i = 0; i < 4; ++i)
+ overlayViews[i].addReflectionProbeGizmo(scene, obj);
+ }
+
function releaseLightGizmo(obj)
{
for (var i = 0; i < 4; ++i)
@@ -543,6 +567,12 @@ Item {
overlayViews[i].releaseParticleEmitterGizmo(obj);
}
+ function releaseReflectionProbeGizmo(obj)
+ {
+ for (var i = 0; i < 4; ++i)
+ overlayViews[i].releaseReflectionProbeGizmo(obj);
+ }
+
function updateLightGizmoScene(scene, obj)
{
for (var i = 0; i < 4; ++i)
@@ -567,6 +597,12 @@ Item {
overlayViews[i].updateParticleEmitterGizmoScene(scene, obj);
}
+ function updateReflectionProbeGizmoScene(scene, obj)
+ {
+ for (var i = 0; i < 4; ++i)
+ overlayViews[i].updateReflectionProbeGizmoScene(scene, obj);
+ }
+
function resolveSplitPoint(x, y)
{
if (!splitView || activeSplit === 0)
@@ -638,7 +674,6 @@ Item {
{
for (var i = 0; i < 4; ++i)
overlayViews[i].handleHiddenStateChange(node);
-
}
function onUpdateDragTooltip()
@@ -651,6 +686,18 @@ Item {
{
updateEnvBackground();
}
+
+ function onCameraSpeedChanged() {
+ _generalHelper.requestTimerEvent("hideSpeed", 1000);
+ viewRoot.showCameraSpeed = true
+ }
+
+ function onRequestedTimerEvent(timerId) {
+ if (timerId === "hideSpeed") {
+ viewRoot.showCameraSpeed = false;
+ _generalHelper.requestRender();
+ }
+ }
}
// Shared nodes of the overlay, set as importScene on all overlay views.
@@ -1058,6 +1105,38 @@ Item {
color: "white"
visible: viewRoot.fps > 0
}
+
+ Rectangle {
+ id: cameraSpeedLabel
+ width: 120
+ height: 65
+ anchors.centerIn: parent
+ opacity: 0.6
+ radius: 10
+ color: "white"
+ visible: flyMode && viewRoot.showCameraSpeed
+ enabled: false
+
+ Column {
+ anchors.fill: parent
+ anchors.margins: 8
+ spacing: 2
+ Text {
+ width: parent.width
+ horizontalAlignment: Text.AlignHCenter
+ text: "Camera Speed"
+ font.pixelSize: 16
+ }
+ Text {
+ width: parent.width
+ height: 20
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ font.pixelSize: 20
+ text: _generalHelper.cameraSpeed.toLocaleString(Qt.locale(), 'f', 1)
+ }
+ }
+ }
}
Keys.onPressed: (event) => {
diff --git a/src/tools/qml2puppet/mockfiles/qt6/IconGizmo.qml b/src/tools/qml2puppet/mockfiles/qt6/IconGizmo.qml
index acde896aeb..50c4aab1e7 100644
--- a/src/tools/qml2puppet/mockfiles/qt6/IconGizmo.qml
+++ b/src/tools/qml2puppet/mockfiles/qt6/IconGizmo.qml
@@ -29,7 +29,7 @@ Item {
property alias iconSource: iconImage.source
- signal clicked(Node node, bool multi)
+ signal clicked(Node node, int button, bool multi)
function hasPoint(x, y)
{
@@ -83,7 +83,7 @@ Item {
}
onClicked: (mouse)=> {
- iconGizmo.clicked(iconGizmo.targetNode,
+ iconGizmo.clicked(iconGizmo.targetNode, mouse.button,
mouse.modifiers & Qt.ControlModifier);
}
hoverEnabled: iconGizmo.highlightOnHover && !iconGizmo.selected
diff --git a/src/tools/qml2puppet/mockfiles/qt6/LookAtGizmo.qml b/src/tools/qml2puppet/mockfiles/qt6/LookAtGizmo.qml
new file mode 100644
index 0000000000..71527d9fb3
--- /dev/null
+++ b/src/tools/qml2puppet/mockfiles/qt6/LookAtGizmo.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import QtQuick
+import QtQuick3D
+import LookAtGeometry
+
+Node {
+ id: root
+
+ property alias crossScale: lookAtGeometry.crossScale
+ property alias color: lookAtMat.baseColor
+
+ Model {
+ readonly property bool _edit3dLocked: true // Make this non-pickable
+ geometry: LookAtGeometry {
+ id: lookAtGeometry
+ }
+ materials: [
+ PrincipledMaterial {
+ id: lookAtMat
+ lighting: DefaultMaterial.NoLighting
+ cullMode: Material.NoCulling
+ }
+ ]
+ }
+}
diff --git a/src/tools/qml2puppet/mockfiles/qt6/OverlayView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/OverlayView3D.qml
index 13a37f4f72..464e0b6b79 100644
--- a/src/tools/qml2puppet/mockfiles/qt6/OverlayView3D.qml
+++ b/src/tools/qml2puppet/mockfiles/qt6/OverlayView3D.qml
@@ -12,6 +12,7 @@ View3D {
property alias rotateGizmo: rotateGizmo
property alias scaleGizmo: scaleGizmo
property alias lightGizmo: lightGizmo
+ property alias lookAtGizmo: lookAtGizmo
property var viewRoot: null
property View3D editView: null
@@ -21,6 +22,7 @@ View3D {
property var cameraGizmos: []
property var particleSystemIconGizmos: []
property var particleEmitterGizmos: []
+ property var reflectionProbeGizmos: []
signal commitObjectProperty(var objects, var propNames)
signal changeObjectProperty(var objects, var propNames)
@@ -288,66 +290,141 @@ View3D {
}
}
+ function addReflectionProbeGizmo(scene, obj)
+ {
+ // Insert into first available gizmo if we don't already have gizmo for this object
+ var slotFound = -1;
+ for (var i = 0; i < reflectionProbeGizmos.length; ++i) {
+ if (!reflectionProbeGizmos[i].targetNode) {
+ slotFound = i;
+ } else if (reflectionProbeGizmos[i].targetNode === obj) {
+ reflectionProbeGizmos[i].scene = scene;
+ return;
+ }
+ }
+
+ if (slotFound !== -1) {
+ reflectionProbeGizmos[slotFound].scene = scene;
+ reflectionProbeGizmos[slotFound].targetNode = obj;
+ reflectionProbeGizmos[slotFound].locked = _generalHelper.isLocked(obj);
+ reflectionProbeGizmos[slotFound].hidden = _generalHelper.isHidden(obj);
+ return;
+ }
+
+ // No free gizmos available, create a new one
+ var gizmoComponent = Qt.createComponent("ReflectionProbeGizmo.qml");
+ if (gizmoComponent.status === Component.Ready) {
+ var gizmo = gizmoComponent.createObject(overlayView,
+ {"view3D": overlayView,
+ "targetNode": obj,
+ "selectedNodes": viewRoot.selectedNodes,
+ "scene": scene,
+ "activeScene": viewRoot.activeScene,
+ "locked": _generalHelper.isLocked(obj),
+ "hidden": _generalHelper.isHidden(obj),
+ "globalShow": viewRoot.showIconGizmo});
+ reflectionProbeGizmos[reflectionProbeGizmos.length] = gizmo;
+ gizmo.clicked.connect(viewRoot.handleObjectClicked);
+ gizmo.selectedNodes = Qt.binding(function() {return viewRoot.selectedNodes;});
+ gizmo.activeScene = Qt.binding(function() {return viewRoot.activeScene;});
+ gizmo.globalShow = Qt.binding(function() {return viewRoot.showIconGizmo;});
+ }
+ }
+
+ function releaseReflectionProbeGizmo(obj)
+ {
+ for (var i = 0; i < reflectionProbeGizmos.length; ++i) {
+ if (reflectionProbeGizmos[i].targetNode === obj) {
+ reflectionProbeGizmos[i].scene = null;
+ reflectionProbeGizmos[i].targetNode = null;
+ return;
+ }
+ }
+ }
+
+ function updateReflectionProbeGizmoScene(scene, obj)
+ {
+ for (var i = 0; i < reflectionProbeGizmos.length; ++i) {
+ if (reflectionProbeGizmos[i].targetNode === obj) {
+ reflectionProbeGizmos[i].scene = scene;
+ return;
+ }
+ }
+ }
+
function gizmoAt(x, y)
{
- for (var i = 0; i < lightIconGizmos.length; ++i) {
+ let i = 0;
+ for (; i < lightIconGizmos.length; ++i) {
if (lightIconGizmos[i].visible && lightIconGizmos[i].hasPoint(x, y))
return lightIconGizmos[i].targetNode;
}
- for (var i = 0; i < cameraGizmos.length; ++i) {
+ for (i = 0; i < cameraGizmos.length; ++i) {
if (cameraGizmos[i].visible && cameraGizmos[i].hasPoint(x, y))
return cameraGizmos[i].targetNode;
}
- for (var i = 0; i < particleSystemIconGizmos.length; ++i) {
+ for (i = 0; i < particleSystemIconGizmos.length; ++i) {
if (particleSystemIconGizmos[i].visible && particleSystemIconGizmos[i].hasPoint(x, y))
return particleSystemIconGizmos[i].targetNode;
}
+ for (i = 0; i < reflectionProbeGizmos.length; ++i) {
+ if (reflectionProbeGizmos[i].visible && reflectionProbeGizmos[i].hasPoint(x, y))
+ return reflectionProbeGizmos[i].targetNode;
+ }
return null;
}
function handleLockedStateChange(node)
{
- for (var i = 0; i < lightIconGizmos.length; ++i) {
+ let i = 0;
+ for (; i < lightIconGizmos.length; ++i) {
if (lightIconGizmos[i].targetNode === node) {
lightIconGizmos[i].locked = _generalHelper.isLocked(node);
return;
}
}
- for (var i = 0; i < cameraGizmos.length; ++i) {
+ for (i = 0; i < cameraGizmos.length; ++i) {
if (cameraGizmos[i].targetNode === node) {
cameraGizmos[i].locked = _generalHelper.isLocked(node);
return;
}
}
- for (var i = 0; i < particleSystemIconGizmos.length; ++i) {
+ for (i = 0; i < particleSystemIconGizmos.length; ++i) {
if (particleSystemIconGizmos[i].targetNode === node) {
particleSystemIconGizmos[i].locked = _generalHelper.isLocked(node);
return;
}
}
+ for (i = 0; i < reflectionProbeGizmos.length; ++i) {
+ if (reflectionProbeGizmos[i].targetNode === node) {
+ reflectionProbeGizmos[i].locked = _generalHelper.isLocked(node);
+ return;
+ }
+ }
}
function handleHiddenStateChange(node)
{
- for (var i = 0; i < lightIconGizmos.length; ++i) {
+ let i = 0;
+ for (; i < lightIconGizmos.length; ++i) {
if (lightIconGizmos[i].targetNode === node) {
lightIconGizmos[i].hidden = _generalHelper.isHidden(node);
return;
}
}
- for (var i = 0; i < cameraGizmos.length; ++i) {
+ for (i = 0; i < cameraGizmos.length; ++i) {
if (cameraGizmos[i].targetNode === node) {
cameraGizmos[i].hidden = _generalHelper.isHidden(node);
return;
}
}
- for (var i = 0; i < particleSystemIconGizmos.length; ++i) {
+ for (i = 0; i < particleSystemIconGizmos.length; ++i) {
if (particleSystemIconGizmos[i].targetNode === node) {
particleSystemIconGizmos[i].hidden = _generalHelper.isHidden(node);
return;
}
}
- for (var i = 0; i < particleEmitterGizmos.length; ++i) {
+ for (i = 0; i < particleEmitterGizmos.length; ++i) {
if (particleEmitterGizmos[i].targetNode === node) {
particleEmitterGizmos[i].hidden = _generalHelper.isHidden(node);
return;
@@ -356,6 +433,12 @@ View3D {
return;
}
}
+ for (i = 0; i < reflectionProbeGizmos.length; ++i) {
+ if (reflectionProbeGizmos[i].targetNode === node) {
+ reflectionProbeGizmos[i].hidden = _generalHelper.isHidden(node);
+ return;
+ }
+ }
}
SceneEnvironment {
@@ -402,6 +485,12 @@ View3D {
position: pivotLine.startPos
}
+ AutoScaleHelper {
+ id: lookAtAutoScale
+ view3D: overlayView
+ position: lookAtGizmo.scenePosition
+ }
+
MoveGizmo {
id: moveGizmo
scale: autoScale.getScale(Qt.vector3d(5, 5, 5))
@@ -537,5 +626,14 @@ View3D {
]
}
}
+
+ LookAtGizmo {
+ id: lookAtGizmo
+ color: "#ddd600"
+ scale: lookAtAutoScale.getScale(Qt.vector3d(10, 10, 10))
+ visible: overlayView.viewRoot.showLookAt
+ && overlayView.isActive
+ && !overlayView.viewRoot.cameraControls[viewRoot.activeSplit].showCrosshairs
+ }
}
}
diff --git a/src/tools/qml2puppet/mockfiles/qt6/ReflectionProbeGizmo.qml b/src/tools/qml2puppet/mockfiles/qt6/ReflectionProbeGizmo.qml
new file mode 100644
index 0000000000..2183e68679
--- /dev/null
+++ b/src/tools/qml2puppet/mockfiles/qt6/ReflectionProbeGizmo.qml
@@ -0,0 +1,9 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import QtQuick
+import QtQuick3D
+
+IconGizmo {
+ iconSource: "qrc:///qtquickplugin/mockfiles/images/reflectionprobe.png"
+}
diff --git a/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml
index 1de1cebd14..82c735467e 100644
--- a/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml
+++ b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml
@@ -15,6 +15,8 @@ View3D {
property alias perspectiveCamera: scenePerspectiveCamera
property alias orthoCamera: sceneOrthoCamera
property alias sceneEnv: sceneEnv
+ property alias defaultLightProbe: defaultLightProbe
+ property alias defaultCubeMap: defaultCubeMap
property vector3d cameraLookAt
property var selectionBoxes: []
property Node selectedNode
@@ -61,6 +63,14 @@ View3D {
id: sceneEnv
antialiasingMode: SceneEnvironment.MSAA
antialiasingQuality: SceneEnvironment.High
+
+ Texture {
+ id: defaultLightProbe
+ }
+
+ CubeMapTexture {
+ id: defaultCubeMap
+ }
}
Node {
diff --git a/src/tools/qml2puppet/qml2puppet/appmetadata.cpp b/src/tools/qml2puppet/qml2puppet/appmetadata.cpp
deleted file mode 100644
index 1896e4db92..0000000000
--- a/src/tools/qml2puppet/qml2puppet/appmetadata.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-// 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 "appmetadata.h"
-
-#include <app/app_version.h>
-
-namespace QDSMeta::AppInfo {
-
-void printAppInfo()
-{
- qInfo() << Qt::endl
- << "<< QDS Meta Info >>" << Qt::endl
- << "App Info" << Qt::endl
- << " - Name :" << Core::Constants::IDE_ID << Qt::endl
- << " - Version :" << Core::Constants::IDE_VERSION_DISPLAY << Qt::endl
- << " - Author :" << Core::Constants::IDE_AUTHOR << Qt::endl
- << " - Year :" << Core::Constants::IDE_YEAR << Qt::endl
- << " - App :" << QCoreApplication::applicationName() << Qt::endl
- << "Build Info " << Qt::endl
- << " - Date :" << __DATE__ << Qt::endl
- << " - Commit :" << QStringLiteral(QDS_STRINGIFY(IDE_REVISION_STR)) << Qt::endl
- << " - Qt Version :" << QT_VERSION_STR << Qt::endl
- << "Compiler Info " << Qt::endl
-#if defined(__GNUC__)
- << " - GCC :" << __GNUC__ << Qt::endl
- << " - GCC Minor :" << __GNUC_MINOR__ << Qt::endl
- << " - GCC Patch :" << __GNUC_PATCHLEVEL__ << Qt::endl
-#endif
-#if defined(_MSC_VER)
- << " - MSC Short :" << _MSC_VER << Qt::endl
- << " - MSC Full :" << _MSC_FULL_VER << Qt::endl
-#endif
-#if defined(__clang__)
- << " - clang maj :" << __clang_major__ << Qt::endl
- << " - clang min :" << __clang_minor__ << Qt::endl
- << " - clang patch :" << __clang_patchlevel__ << Qt::endl
-#endif
- << "<< End Of QDS Meta Info >>" << Qt::endl;
- exit(0);
-}
-
-void registerAppInfo(const QString &appName)
-{
- QCoreApplication::setOrganizationName(Core::Constants::IDE_AUTHOR);
- QCoreApplication::setOrganizationDomain("qt-project.org");
- QCoreApplication::setApplicationName(appName);
- QCoreApplication::setApplicationVersion(Core::Constants::IDE_VERSION_LONG);
-}
-
-} // namespace QDSMeta::AppInfo
diff --git a/src/tools/qml2puppet/qml2puppet/appmetadata.h b/src/tools/qml2puppet/qml2puppet/appmetadata.h
deleted file mode 100644
index 18eb650461..0000000000
--- a/src/tools/qml2puppet/qml2puppet/appmetadata.h
+++ /dev/null
@@ -1,55 +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 <QCommandLineParser>
-#include <QLoggingCategory>
-
-// Common functions can be used in all QDS apps
-namespace QDSMeta {
-
-namespace Logging {
-inline Q_LOGGING_CATEGORY(deprecated, "qt.tools.qds.deprecated");
-inline Q_LOGGING_CATEGORY(verbose1, "qt.tools.qds.verbose1");
-inline Q_LOGGING_CATEGORY(verbose2, "qt.tools.qds.verbose2");
-
-inline void registerMessageHandler()
-{
- qInstallMessageHandler(
- [](QtMsgType type, const QMessageLogContext &context, const QString &msg) {
- auto tPrinter = [&](const QString &msgPrefix) {
- fprintf(stderr,
- "%s: %s (%s:%u, %s)\n",
- msgPrefix.toLocal8Bit().constData(),
- msg.toLocal8Bit().constData(),
- context.file,
- context.line,
- context.function);
- };
-
- if (type == QtDebugMsg)
- tPrinter("Debug");
- else if (type == QtInfoMsg)
- tPrinter("Info");
- else if (type == QtWarningMsg)
- tPrinter("Warning");
- else if (type == QtCriticalMsg)
- tPrinter("Critical");
- else if (type == QtFatalMsg) {
- tPrinter("Fatal");
- abort();
- }
- });
-}
-} // namespace Logging
-
-namespace AppInfo {
-
-#define STRINGIFY_INTERNAL(x) #x
-#define QDS_STRINGIFY(x) STRINGIFY_INTERNAL(x)
-
-void printAppInfo();
-void registerAppInfo(const QString &appName);
-
-} // namespace AppInfo
-} // namespace QDSMeta
diff --git a/src/tools/qml2puppet/qml2puppet/configcrashpad.h b/src/tools/qml2puppet/qml2puppet/configcrashpad.h
index 70cbfc5f4a..b802b80957 100644
--- a/src/tools/qml2puppet/qml2puppet/configcrashpad.h
+++ b/src/tools/qml2puppet/qml2puppet/configcrashpad.h
@@ -5,6 +5,7 @@
#include <QtGlobal>
#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN)
+#include <QDir>
#define NOMINMAX
#include "client/crash_report_database.h"
#include "client/crashpad_client.h"
diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp
index dadd817633..3baf91c146 100644
--- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp
+++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp
@@ -6,6 +6,8 @@
#include "selectionboxgeometry.h"
+#include <enumeration.h>
+
#include <QGuiApplication>
#include <QtQuick3D/qquick3dobject.h>
#include <QtQuick3D/private/qquick3dorthographiccamera_p.h>
@@ -42,9 +44,17 @@
namespace QmlDesigner {
namespace Internal {
-const QString _globalStateId = QStringLiteral("@GTS"); // global tool state
+const QString _globalStateId = QStringLiteral("@GTS"); // global tool state (within document)
+const QString _projectStateId = QStringLiteral("@PTS"); // project wide tool state
const QString _lastSceneIdKey = QStringLiteral("lastSceneId");
const QString _rootSizeKey = QStringLiteral("rootSize");
+const QString _lastSceneEnvKey = QStringLiteral("lastSceneEnv");
+
+const QString _lightProbeProp = QStringLiteral("lightProbe");
+const QString _sourceProp = QStringLiteral("source");
+const QString _cubeProp = QStringLiteral("skyBoxCubeMap");
+const QString _bgProp = QStringLiteral("backgroundMode");
+const QString _colorProp = QStringLiteral("clearColor");
static const float floatMin = std::numeric_limits<float>::lowest();
static const float floatMax = std::numeric_limits<float>::max();
@@ -163,16 +173,17 @@ QVector3D GeneralHelper::moveCamera(QQuick3DCamera *camera, const QVector3D &sta
if (moveVector.length() < 0.001f)
return startLookAt;
+ QVector3D speedVector = moveVector * m_cameraSpeed * m_cameraSpeedModifier;
+
QMatrix4x4 m = camera->sceneTransform(); // Works because edit camera is at scene root
const float *dataPtr(m.data());
const QVector3D xAxis = QVector3D(dataPtr[0], dataPtr[1], dataPtr[2]).normalized();
const QVector3D yAxis = QVector3D(dataPtr[4], dataPtr[5], dataPtr[6]).normalized();
const QVector3D zAxis = QVector3D(dataPtr[8], dataPtr[9], dataPtr[10]).normalized();
- const QVector3D xDelta = xAxis * moveVector.x();
- const QVector3D yDelta = yAxis * moveVector.y();
- const QVector3D zDelta = zAxis * moveVector.z();
- // Delta multiplier for nice default speed in default scene
- const QVector3D delta = (yDelta - xDelta - zDelta) * .5f;
+ const QVector3D xDelta = xAxis * speedVector.x();
+ const QVector3D yDelta = yAxis * speedVector.y();
+ const QVector3D zDelta = zAxis * speedVector.z();
+ const QVector3D delta = (yDelta - xDelta - zDelta);
camera->setPosition(camera->position() + delta);
@@ -451,17 +462,19 @@ QVector4D GeneralHelper::approachNode(
// a selection box for bound calculations to work. This is used to focus the view for
// various preview image generations, where doing things asynchronously is not good
// and recalculating bounds for every frame is not a problem.
-void GeneralHelper::calculateNodeBoundsAndFocusCamera(
- QQuick3DCamera *camera, QQuick3DNode *node, QQuick3DViewport *viewPort,
- float defaultLookAtDistance, bool closeUp)
+void GeneralHelper::calculateBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node,
+ QQuick3DViewport *viewPort,
+ float defaultLookAtDistance,
+ bool closeUp, QVector3D &lookAt,
+ QVector3D &extents)
{
QVector3D minBounds;
QVector3D maxBounds;
getBounds(viewPort, node, minBounds, maxBounds);
- QVector3D extents = maxBounds - minBounds;
- QVector3D lookAt = minBounds + (extents / 2.f);
+ extents = maxBounds - minBounds;
+ lookAt = minBounds + (extents / 2.f);
float maxExtent = qSqrt(qreal(extents.x()) * qreal(extents.x())
+ qreal(extents.y()) * qreal(extents.y())
+ qreal(extents.z()) * qreal(extents.z()));
@@ -494,10 +507,20 @@ void GeneralHelper::calculateNodeBoundsAndFocusCamera(
perspectiveCamera->setClipNear(minDist * 0.99);
perspectiveCamera->setClipFar(maxDist * 1.01);
}
-
}
}
+void GeneralHelper::calculateNodeBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node,
+ QQuick3DViewport *viewPort,
+ float defaultLookAtDistance,
+ bool closeUp)
+{
+ QVector3D extents;
+ QVector3D lookAt;
+ calculateBoundsAndFocusCamera(camera, node, viewPort, defaultLookAtDistance, closeUp,
+ lookAt, extents);
+}
+
// Aligns any cameras found in nodes list to a camera.
// Only position and rotation are copied, rest of the camera properties stay the same.
void GeneralHelper::alignCameras(QQuick3DCamera *camera, const QVariant &nodes)
@@ -738,6 +761,11 @@ void GeneralHelper::setSceneEnvironmentData(const QString &sceneId,
}
}
+bool GeneralHelper::hasSceneEnvironmentData(const QString &sceneId) const
+{
+ return m_sceneEnvironmentData.contains(sceneId);
+}
+
QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes GeneralHelper::sceneEnvironmentBgMode(
const QString &sceneId) const
{
@@ -759,6 +787,69 @@ QQuick3DCubeMapTexture *GeneralHelper::sceneEnvironmentSkyBoxCubeMap(const QStri
return m_sceneEnvironmentData[sceneId].skyBoxCubeMap.data();
}
+void GeneralHelper::updateSceneEnvToLast(QQuick3DSceneEnvironment *env, QQuick3DTexture *lightProbe,
+ QQuick3DCubeMapTexture *cubeMap)
+{
+ if (!env)
+ return;
+
+ if (m_lastSceneEnvData.contains(_bgProp)) {
+ Enumeration enumeration = m_lastSceneEnvData[_bgProp].value<Enumeration>();
+ QMetaEnum me = QMetaEnum::fromType<QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes>();
+ int intValue = me.keyToValue(enumeration.toName());
+ env->setBackgroundMode(QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes(intValue));
+ } else {
+ env->setBackgroundMode(QQuick3DSceneEnvironment::Transparent);
+ }
+
+ if (m_lastSceneEnvData.contains(_colorProp))
+ env->setClearColor(m_lastSceneEnvData[_colorProp].value<QColor>());
+ else
+ env->setClearColor(Qt::transparent);
+
+ if (lightProbe) {
+ if (m_lastSceneEnvData.contains(_lightProbeProp)) {
+ QVariantMap props = m_lastSceneEnvData[_lightProbeProp].toMap();
+ if (props.contains(_sourceProp))
+ lightProbe->setSource(props[_sourceProp].toUrl());
+ else
+ lightProbe->setSource({});
+ env->setLightProbe(lightProbe);
+ } else {
+ env->setLightProbe(nullptr);
+ }
+ }
+
+ if (cubeMap) {
+ if (m_lastSceneEnvData.contains(_cubeProp)) {
+ QVariantMap props = m_lastSceneEnvData[_cubeProp].toMap();
+ if (props.contains(_sourceProp))
+ cubeMap->setSource(props[_sourceProp].toUrl());
+ else
+ cubeMap->setSource({});
+ env->setSkyBoxCubeMap(cubeMap);
+ } else {
+ env->setSkyBoxCubeMap(nullptr);
+ }
+ }
+}
+
+bool GeneralHelper::sceneHasLightProbe(const QString &sceneId)
+{
+ // From editor perspective, a scene is considered to have a light probe if scene itself
+ // has a light probe or scene has no env data and last scene had a light probe
+ if (m_sceneEnvironmentData.contains(sceneId)) {
+ return bool(m_sceneEnvironmentData[sceneId].lightProbe);
+ } else {
+ if (m_lastSceneEnvData.contains(_lightProbeProp)) {
+ QVariantMap props = m_lastSceneEnvData[_sourceProp].toMap();
+ if (props.contains(_sourceProp))
+ return !props[_sourceProp].toUrl().isEmpty();
+ }
+ }
+ return false;
+}
+
void GeneralHelper::clearSceneEnvironmentData()
{
for (const SceneEnvData &data : std::as_const(m_sceneEnvironmentData)) {
@@ -772,6 +863,12 @@ void GeneralHelper::clearSceneEnvironmentData()
emit sceneEnvDataChanged();
}
+void GeneralHelper::setLastSceneEnvironmentData(const QVariantMap &data)
+{
+ m_lastSceneEnvData = data;
+ storeToolState(_projectStateId, _lastSceneEnvKey, m_lastSceneEnvData);
+}
+
void GeneralHelper::initToolStates(const QString &sceneId, const QVariantMap &toolStates)
{
m_toolStates[sceneId] = toolStates;
@@ -796,11 +893,21 @@ QString GeneralHelper::globalStateId() const
return _globalStateId;
}
+QString GeneralHelper::projectStateId() const
+{
+ return _projectStateId;
+}
+
QString GeneralHelper::lastSceneIdKey() const
{
return _lastSceneIdKey;
}
+QString GeneralHelper::lastSceneEnvKey() const
+{
+ return _lastSceneEnvKey;
+}
+
QString GeneralHelper::rootSizeKey() const
{
return _rootSizeKey;
@@ -1187,6 +1294,11 @@ void GeneralHelper::setCameraSpeed(double speed)
}
}
+void GeneralHelper::setCameraSpeedModifier(double modifier)
+{
+ m_cameraSpeedModifier = modifier;
+}
+
QString GeneralHelper::formatVectorDragTooltip(const QVector3D &vec, const QString &suffix) const
{
return QObject::tr("x:%L1 y:%L2 z:%L3%L4")
@@ -1414,6 +1526,26 @@ bool GeneralHelper::compareQuaternions(const QQuaternion &q1, const QQuaternion
&& qFuzzyCompare(q1.z(), q2.z()) && qFuzzyCompare(q1.scalar(), q2.scalar());
}
+void GeneralHelper::requestTimerEvent(const QString &timerId, qint64 delay)
+{
+ if (m_eventTimers.contains(timerId)) {
+ m_eventTimers[timerId]->start(delay);
+ } else {
+ auto timer = new QTimer;
+ timer->setInterval(delay);
+ timer->setSingleShot(true);
+ connect(timer, &QTimer::timeout, this, [this, timerId]() {
+ if (m_eventTimers.contains(timerId)) {
+ QTimer *timer = m_eventTimers.take(timerId);
+ timer->deleteLater();
+ }
+ emit requestedTimerEvent(timerId);
+ });
+ m_eventTimers[timerId] = timer;
+ timer->start(delay);
+ }
+}
+
}
}
diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h
index 62a95d86a6..f1b57f4122 100644
--- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h
+++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h
@@ -72,6 +72,9 @@ public:
bool closeUp = false);
Q_INVOKABLE QVector4D approachNode(QQuick3DCamera *camera, float defaultLookAtDistance,
QObject *node, QQuick3DViewport *viewPort);
+ void calculateBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node,
+ QQuick3DViewport *viewPort, float defaultLookAtDistance,
+ bool closeUp, QVector3D &lookAt, QVector3D &extents);
Q_INVOKABLE void calculateNodeBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node,
QQuick3DViewport *viewPort,
float defaultLookAtDistance, bool closeUp);
@@ -96,7 +99,9 @@ public:
Q_INVOKABLE void enableItemUpdate(QQuickItem *item, bool enable);
Q_INVOKABLE QVariantMap getToolStates(const QString &sceneId);
QString globalStateId() const;
+ QString projectStateId() const;
QString lastSceneIdKey() const;
+ QString lastSceneEnvKey() const;
QString rootSizeKey() const;
Q_INVOKABLE void setMultiSelectionTargets(QQuick3DNode *multiSelectRootNode,
@@ -109,12 +114,18 @@ public:
Q_INVOKABLE void rotateMultiSelection(bool commit);
void setSceneEnvironmentData(const QString &sceneId, QQuick3DSceneEnvironment *env);
+ Q_INVOKABLE bool hasSceneEnvironmentData(const QString &sceneId) const;
Q_INVOKABLE QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes sceneEnvironmentBgMode(
const QString &sceneId) const;
Q_INVOKABLE QColor sceneEnvironmentColor(const QString &sceneId) const;
Q_INVOKABLE QQuick3DTexture *sceneEnvironmentLightProbe(const QString &sceneId) const;
Q_INVOKABLE QQuick3DCubeMapTexture *sceneEnvironmentSkyBoxCubeMap(const QString &sceneId) const;
+ Q_INVOKABLE void updateSceneEnvToLast(QQuick3DSceneEnvironment *env, QQuick3DTexture *lightProbe,
+ QQuick3DCubeMapTexture *cubeMap);
+ Q_INVOKABLE bool sceneHasLightProbe(const QString &sceneId);
+
void clearSceneEnvironmentData();
+ void setLastSceneEnvironmentData(const QVariantMap &data);
bool isMacOS() const;
@@ -139,6 +150,7 @@ public:
void setSnapRotationInterval(double interval) { m_snapRotationInterval = interval; }
void setSnapScaleInterval(double interval) { m_snapScaleInterval = interval / 100.; }
void setCameraSpeed(double speed);
+ Q_INVOKABLE void setCameraSpeedModifier(double modifier);
Q_INVOKABLE QString snapPositionDragTooltip(const QVector3D &pos) const;
Q_INVOKABLE QString snapRotationDragTooltip(double angle) const;
@@ -154,6 +166,8 @@ public:
Q_INVOKABLE bool compareVectors(const QVector3D &v1, const QVector3D &v2) const;
Q_INVOKABLE bool compareQuaternions(const QQuaternion &q1, const QQuaternion &q2) const;
+ Q_INVOKABLE void requestTimerEvent(const QString &timerId, qint64 delay);
+
signals:
void overlayUpdateNeeded();
void toolStateChanged(const QString &sceneId, const QString &tool, const QVariant &toolState);
@@ -167,6 +181,7 @@ signals:
void requestCameraMove(QQuick3DCamera *camera, const QVector3D &moveVector);
void requestRender();
void cameraSpeedChanged();
+ void requestedTimerEvent(const QString &timerId);
private:
void handlePendingToolStateUpdate();
@@ -198,6 +213,7 @@ private:
QPointer<QQuick3DCubeMapTexture> skyBoxCubeMap;
};
QHash<QString, SceneEnvData> m_sceneEnvironmentData;
+ QVariantMap m_lastSceneEnvData;
struct MultiSelData {
QVector3D startScenePos;
@@ -221,8 +237,10 @@ private:
double m_snapRotationInterval = 5.;
double m_snapScaleInterval = .1;
double m_cameraSpeed = 10.;
+ double m_cameraSpeedModifier = 1.;
QVariant m_bgColor;
+ QHash<QString, QTimer *> m_eventTimers;
};
}
diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.cpp
new file mode 100644
index 0000000000..b569f6ea5a
--- /dev/null
+++ b/src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.cpp
@@ -0,0 +1,62 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifdef QUICK3D_MODULE
+
+#include "lookatgeometry.h"
+
+namespace QmlDesigner::Internal {
+
+LookAtGeometry::LookAtGeometry()
+ : GeometryBase()
+ , m_crossScale(1.f, 1.f, 1.f)
+{
+}
+
+LookAtGeometry::~LookAtGeometry()
+{
+}
+
+QVector3D LookAtGeometry::crossScale() const
+{
+ return m_crossScale;
+}
+
+void LookAtGeometry::setCrossScale(const QVector3D &scale)
+{
+ if (scale != m_crossScale) {
+ m_crossScale = scale;
+ emit crossScaleChanged();
+ updateGeometry();
+ }
+}
+
+void LookAtGeometry::doUpdateGeometry()
+{
+ GeometryBase::doUpdateGeometry();
+
+ QByteArray vertexData;
+ vertexData.resize(6 * 3 * 4); // 6 vertices of 3 floats each 4 bytes
+ float *dataPtr = reinterpret_cast<float *>(vertexData.data());
+
+ auto addVertex = [&dataPtr](float x, float y, float z) {
+ dataPtr[0] = x;
+ dataPtr[1] = y;
+ dataPtr[2] = z;
+ dataPtr += 3;
+ };
+
+ addVertex(m_crossScale.x(), 0.f, 0.f);
+ addVertex(-m_crossScale.x(), 0.f, 0.f);
+ addVertex(0.f, m_crossScale.y(), 0.f);
+ addVertex(0.f, -m_crossScale.y(), 0.f);
+ addVertex(0.f, 0.f, m_crossScale.z());
+ addVertex(0.f, 0.f, -m_crossScale.z());
+
+ setVertexData(vertexData);
+ setBounds(-m_crossScale, m_crossScale);
+}
+
+} // namespace QmlDesigner::Internal
+
+#endif // QUICK3D_MODULE
diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.h b/src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.h
new file mode 100644
index 0000000000..e881c2750e
--- /dev/null
+++ b/src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.h
@@ -0,0 +1,42 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#ifdef QUICK3D_MODULE
+
+#include "geometrybase.h"
+
+#include <QtGui/QVector3D>
+
+namespace QmlDesigner::Internal {
+
+class LookAtGeometry : public GeometryBase
+{
+ Q_OBJECT
+ Q_PROPERTY(QVector3D crossScale READ crossScale WRITE setCrossScale NOTIFY crossScaleChanged)
+
+public:
+ LookAtGeometry();
+ ~LookAtGeometry() override;
+
+ QVector3D crossScale() const;
+
+public slots:
+ void setCrossScale(const QVector3D &pos);
+
+signals:
+ void crossScaleChanged();
+
+protected:
+ void doUpdateGeometry() override;
+
+private:
+ QVector3D m_crossScale;
+};
+
+} // namespace QmlDesigner::Internal
+
+QML_DECLARE_TYPE(QmlDesigner::Internal::LookAtGeometry)
+
+#endif // QUICK3D_MODULE
diff --git a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp
index f3227e386c..0f7133b1ff 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp
+++ b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp
@@ -1187,8 +1187,9 @@ InformationChangedCommand NodeInstanceServer::createAllInformationChangedCommand
static bool supportedVariantType(int type)
{
- return type < int(QVariant::UserType) && type != QMetaType::QObjectStar
- && type != QMetaType::QModelIndex && type != QMetaType::VoidStar;
+ return (type < int(QVariant::UserType) && type != QMetaType::QObjectStar
+ && type != QMetaType::QModelIndex && type != QMetaType::VoidStar)
+ || type == QMetaType::fromType<Enumeration>().id();
}
ValuesChangedCommand NodeInstanceServer::createValuesChangedCommand(const QList<ServerNodeInstance> &instanceList) const
diff --git a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h
index 65542dedc2..09c87b3af9 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h
+++ b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h
@@ -258,6 +258,7 @@ protected:
void setRenderTimerInterval(int timerInterval);
int renderTimerInterval() const;
void setSlowRenderTimerInterval(int timerInterval);
+ TimerMode timerMode() const { return m_timerMode; }
virtual void initializeView() = 0;
virtual void initializeAuxiliaryViews();
diff --git a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp
index 9815d21ac4..9e3cdac151 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp
+++ b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp
@@ -6,6 +6,7 @@
#include "qt5bakelightsnodeinstanceserver.h"
#include "qt5captureimagenodeinstanceserver.h"
#include "qt5capturepreviewnodeinstanceserver.h"
+#include "qt5import3dnodeinstanceserver.h"
#include "qt5informationnodeinstanceserver.h"
#include "qt5rendernodeinstanceserver.h"
@@ -173,6 +174,8 @@ std::unique_ptr<NodeInstanceServer> createNodeInstanceServer(
return std::make_unique<Qt5PreviewNodeInstanceServer>(nodeInstanceClient);
else if (serverName == "bakelightsmode")
return std::make_unique<Qt5BakeLightsNodeInstanceServer>(nodeInstanceClient);
+ else if (serverName == "import3dmode")
+ return std::make_unique<Qt5Import3dNodeInstanceServer>(nodeInstanceClient);
return {};
}
diff --git a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp
index a1c727f215..e2529f42af 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp
+++ b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp
@@ -7,6 +7,7 @@
#include <qmlprivategate.h>
#include <QDebug>
+#include <QDir>
#include <QEvent>
#include <QQmlContext>
#include <QQmlError>
@@ -372,7 +373,7 @@ void ObjectNodeInstance::reparent(const ObjectNodeInstance::Pointer &oldParentIn
QVariant ObjectNodeInstance::convertSpecialCharacter(const QVariant& value) const
{
QVariant specialCharacterConvertedValue = value;
- if (value.typeId() == QVariant::String) {
+ if (value.typeId() == QMetaType::QString) {
QString string = value.toString();
string.replace(QLatin1String("\\n"), QLatin1String("\n"));
string.replace(QLatin1String("\\t"), QLatin1String("\t"));
@@ -618,7 +619,8 @@ QVariant ObjectNodeInstance::property(const PropertyName &name) const
QQmlProperty property(object(), QString::fromUtf8(name), context());
if (property.property().isEnumType()) {
QVariant value = property.read();
- return property.property().enumerator().valueToKey(value.toInt());
+ QMetaEnum me = property.property().enumerator();
+ return QVariant::fromValue<Enumeration>(Enumeration(me.scope(), me.valueToKey(value.toInt())));
}
if (property.propertyType() == QVariant::Url) {
@@ -627,8 +629,8 @@ QVariant ObjectNodeInstance::property(const PropertyName &name) const
return QVariant();
if (url.scheme() == "file") {
- int basePathLength = nodeInstanceServer()->fileUrl().toLocalFile().lastIndexOf('/');
- return QUrl(url.toLocalFile().mid(basePathLength + 1));
+ QFileInfo fi{nodeInstanceServer()->fileUrl().toLocalFile()};
+ return QUrl{fi.absoluteDir().relativeFilePath(url.toLocalFile())};
}
}
diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.cpp
new file mode 100644
index 0000000000..c70bba2846
--- /dev/null
+++ b/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.cpp
@@ -0,0 +1,209 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "qt5import3dnodeinstanceserver.h"
+
+#include "createscenecommand.h"
+#include "view3dactioncommand.h"
+
+#include "imagecontainer.h"
+#include "nodeinstanceclientinterface.h"
+#include "puppettocreatorcommand.h"
+
+#include <QFileInfo>
+#include <QLocale>
+#include <QQmlComponent>
+#include <QQmlEngine>
+
+#include <QQmlProperty>
+
+#ifdef QUICK3D_MODULE
+#include <private/qquick3dnode_p.h>
+#include <private/qquick3dviewport_p.h>
+#include <private/qquickdesignersupport_p.h>
+#endif
+
+namespace QmlDesigner {
+
+Qt5Import3dNodeInstanceServer::Qt5Import3dNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient) :
+ Qt5NodeInstanceServer(nodeInstanceClient)
+{
+ setSlowRenderTimerInterval(100000000);
+ setRenderTimerInterval(20);
+
+#ifdef QUICK3D_MODULE
+ m_generalHelper = new Internal::GeneralHelper();
+ QObject::connect(m_generalHelper, &Internal::GeneralHelper::requestRender, this, [this]() {
+ startRenderTimer();
+ });
+#endif
+}
+
+Qt5Import3dNodeInstanceServer::~Qt5Import3dNodeInstanceServer()
+{
+ cleanup();
+}
+
+void Qt5Import3dNodeInstanceServer::createScene(const CreateSceneCommand &command)
+{
+ initializeView();
+ registerFonts(command.resourceUrl);
+ setTranslationLanguage(command.language);
+ setupScene(command);
+ startRenderTimer();
+}
+
+void Qt5Import3dNodeInstanceServer::view3DAction([[maybe_unused]] const View3DActionCommand &command)
+{
+#ifdef QUICK3D_MODULE
+ switch (command.type()) {
+ case View3DActionType::Import3dUpdatePreviewImage: {
+ QObject *obj = rootItem();
+ if (obj) {
+ QSize size = command.value().toSize();
+ QQmlProperty wProp(obj, "width", context());
+ QQmlProperty hProp(obj, "height", context());
+ wProp.write(size.width());
+ hProp.write(size.height());
+ resizeCanvasToRootItem();
+
+ startRenderTimer();
+ }
+ break;
+ }
+ case View3DActionType::Import3dRotatePreviewModel: {
+ QObject *obj = rootItem();
+ QQmlProperty sceneNodeProp(obj, "sceneNode", context());
+ auto sceneNode = sceneNodeProp.read().value<QQuick3DNode *>();
+ if (sceneNode) {
+ QPointF delta = command.value().toPointF();
+ m_generalHelper->orbitCamera(m_view3D->camera(), m_view3D->camera()->eulerRotation(),
+ m_lookAt, {}, {float(delta.x()), float(delta.y()), 0.f});
+ m_keepRendering = true;
+ startRenderTimer();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+#endif
+}
+
+void Qt5Import3dNodeInstanceServer::startRenderTimer()
+{
+ if (m_keepRendering && timerMode() == TimerMode::NormalTimer)
+ return;
+
+ NodeInstanceServer::startRenderTimer();
+}
+
+void Qt5Import3dNodeInstanceServer::cleanup()
+{
+#ifdef QUICK3D_MODULE
+ delete m_previewNode;
+ delete m_generalHelper;
+#endif
+}
+
+void Qt5Import3dNodeInstanceServer::finish()
+{
+ cleanup();
+}
+
+void Qt5Import3dNodeInstanceServer::collectItemChangesAndSendChangeCommands()
+{
+ static bool inFunction = false;
+
+ if (!rootNodeInstance().holdsGraphical())
+ return;
+
+ if (!inFunction) {
+ inFunction = true;
+#ifdef QUICK3D_MODULE
+ QQuickDesignerSupport::polishItems(quickWindow());
+#endif
+ render();
+
+ inFunction = false;
+ }
+}
+
+void Qt5Import3dNodeInstanceServer::render()
+{
+#ifdef QUICK3D_MODULE
+ ++m_renderCount;
+
+ if (m_renderCount == 1) {
+ QObject *obj = rootItem();
+ QQmlProperty viewProp(obj, "view3d", context());
+ QObject *viewObj = viewProp.read().value<QObject *>();
+ m_view3D = qobject_cast<QQuick3DViewport *>(viewObj);
+ if (m_view3D) {
+ QQmlProperty sceneModelNameProp(obj, "sceneModelName", context());
+ QQmlProperty sceneNodeProp(obj, "sceneNode", context());
+ auto sceneNode = sceneNodeProp.read().value<QQuick3DNode *>();
+ if (sceneNode) {
+ QString sceneModelName = sceneModelNameProp.read().toString();
+ QFileInfo fi(fileUrl().toLocalFile());
+ QString compPath = fi.absolutePath() + '/' + sceneModelName + ".qml";
+ QQmlComponent comp(engine(), compPath, QQmlComponent::PreferSynchronous);
+ m_previewNode = qobject_cast<QQuick3DNode *>(comp.create(context()));
+ if (m_previewNode) {
+ engine()->setObjectOwnership(m_previewNode, QJSEngine::CppOwnership);
+ m_previewNode->setParentItem(sceneNode);
+ m_previewNode->setParent(sceneNode);
+ }
+ }
+ }
+ }
+
+ // Render scene once to ensure geometries are intialized so bounds calculations work correctly
+ if (m_renderCount == 2 && m_view3D) {
+ QVector3D extents;
+ m_generalHelper->calculateBoundsAndFocusCamera(m_view3D->camera(), m_previewNode,
+ m_view3D, 1050, false, m_lookAt, extents);
+ auto getExtentStr = [&extents](int idx) -> QString {
+ int prec = 0;
+ float val = extents[idx];
+ while (val < 100.f) {
+ ++prec;
+ val *= 10.f;
+ }
+ // Strip unnecessary zeroes after decimal separator
+ if (prec > 0) {
+ QString checkStr = QString::number(extents[idx], 'f', prec);
+ while (prec > 0 && (checkStr.last(1) == "0" || checkStr.last(1) == ".")) {
+ --prec;
+ checkStr.chop(1);
+ }
+ }
+ QString retval = QLocale().toString(extents[idx], 'f', prec);
+ return retval;
+ };
+ QQmlProperty extentsProp(rootItem(), "extents", context());
+ extentsProp.write(tr("Dimensions: %1 x %2 x %3").arg(getExtentStr(0))
+ .arg(getExtentStr(1))
+ .arg(getExtentStr(2)));
+ }
+
+ rootNodeInstance().updateDirtyNodeRecursive();
+ QImage renderImage = grabWindow();
+
+ if (m_renderCount >= 2) {
+ ImageContainer imgContainer(0, renderImage, m_renderCount);
+ nodeInstanceClient()->handlePuppetToCreatorCommand(
+ {PuppetToCreatorCommand::Import3DPreviewImage,
+ QVariant::fromValue(imgContainer)});
+
+ if (!m_keepRendering)
+ slowDownRenderTimer();
+
+ m_keepRendering = false;
+ }
+#else
+ slowDownRenderTimer();
+#endif
+}
+
+} // namespace QmlDesigner
diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.h
new file mode 100644
index 0000000000..cfffa904bd
--- /dev/null
+++ b/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#ifdef QUICK3D_MODULE
+#include "generalhelper.h"
+#endif
+#include "qt5nodeinstanceserver.h"
+
+QT_BEGIN_NAMESPACE
+class QQuick3DNode;
+QT_END_NAMESPACE
+
+namespace QmlDesigner {
+
+class Qt5Import3dNodeInstanceServer : public Qt5NodeInstanceServer
+{
+ Q_OBJECT
+public:
+ explicit Qt5Import3dNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient);
+
+public:
+ virtual ~Qt5Import3dNodeInstanceServer();
+
+ void createScene(const CreateSceneCommand &command) override;
+ void view3DAction(const View3DActionCommand &command) override;
+
+ void render();
+
+protected:
+ void collectItemChangesAndSendChangeCommands() override;
+ void startRenderTimer() override;
+
+private:
+ void finish();
+ void cleanup();
+
+ int m_renderCount = 0;
+ bool m_keepRendering = false;
+
+#ifdef QUICK3D_MODULE
+ QQuick3DViewport *m_view3D = nullptr;
+ Internal::GeneralHelper *m_generalHelper = nullptr;
+ QQuick3DNode *m_previewNode = nullptr;
+ QVector3D m_lookAt;
+#endif
+};
+
+} // namespace QmlDesigner
diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp
index 6cd39a5a85..9549f59d3f 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp
+++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp
@@ -41,14 +41,15 @@
#include "changeauxiliarycommand.h"
#include "../editor3d/boxgeometry.h"
-#include "../editor3d/generalhelper.h"
-#include "../editor3d/mousearea3d.h"
#include "../editor3d/camerageometry.h"
-#include "../editor3d/lightgeometry.h"
+#include "../editor3d/generalhelper.h"
#include "../editor3d/gridgeometry.h"
-#include "../editor3d/selectionboxgeometry.h"
-#include "../editor3d/linegeometry.h"
#include "../editor3d/icongizmoimageprovider.h"
+#include "../editor3d/lightgeometry.h"
+#include "../editor3d/linegeometry.h"
+#include "../editor3d/lookatgeometry.h"
+#include "../editor3d/mousearea3d.h"
+#include "../editor3d/selectionboxgeometry.h"
#include <private/qquickdesignersupport_p.h>
#include <qmlprivategate.h>
@@ -74,6 +75,7 @@
#include <QtQuick3D/private/qquick3dscenerootnode_p.h>
#include <QtQuick3D/private/qquick3drepeater_p.h>
#include <QtQuick3D/private/qquick3dloader_p.h>
+#include <QtQuick3D/private/qquick3dreflectionprobe_p.h>
#include <QtQuick3D/private/qquick3dsceneenvironment_p.h>
#if defined(QUICK3D_ASSET_UTILS_MODULE)
#include <private/qquick3druntimeloader_p.h>
@@ -525,6 +527,7 @@ void Qt5InformationNodeInstanceServer::createEditView3D()
qmlRegisterType<QmlDesigner::Internal::SelectionBoxGeometry>("SelectionBoxGeometry", 1, 0, "SelectionBoxGeometry");
qmlRegisterType<QmlDesigner::Internal::LineGeometry>("LineGeometry", 1, 0, "LineGeometry");
qmlRegisterType<QmlDesigner::Internal::BoxGeometry>("BoxGeometry", 1, 0, "BoxGeometry");
+ qmlRegisterType<QmlDesigner::Internal::LookAtGeometry>("LookAtGeometry", 1, 0, "LookAtGeometry");
auto helper = new QmlDesigner::Internal::GeneralHelper();
QObject::connect(helper, &QmlDesigner::Internal::GeneralHelper::toolStateChanged,
@@ -979,6 +982,9 @@ void Qt5InformationNodeInstanceServer::handleNode3DDestroyed([[maybe_unused]] QO
QMetaObject::invokeMethod(m_editView3DData.rootItem, "releaseParticleEmitterGizmo",
Q_ARG(QVariant, objectToVariant(obj)));
#endif
+ } else if (qobject_cast<QQuick3DReflectionProbe *>(obj)) {
+ QMetaObject::invokeMethod(m_editView3DData.rootItem, "releaseReflectionProbeGizmo",
+ Q_ARG(QVariant, objectToVariant(obj)));
}
removeNode3D(obj);
#endif
@@ -1112,6 +1118,10 @@ void Qt5InformationNodeInstanceServer::resolveSceneRoots()
Q_ARG(QVariant, objectToVariant(newRoot)),
Q_ARG(QVariant, objectToVariant(node)));
#endif
+ } else if (qobject_cast<QQuick3DReflectionProbe *>(node)) {
+ QMetaObject::invokeMethod(m_editView3DData.rootItem, "updateReflectionProbeGizmoScene",
+ Q_ARG(QVariant, objectToVariant(newRoot)),
+ Q_ARG(QVariant, objectToVariant(node)));
}
}
++it;
@@ -1599,7 +1609,7 @@ QList<ServerNodeInstance> Qt5InformationNodeInstanceServer::createInstances(
if (m_editView3DSetupDone) {
add3DViewPorts(createdInstances);
add3DScenes(createdInstances);
- createCameraAndLightGizmos(createdInstances);
+ createGizmos(createdInstances);
}
render3DEditView();
@@ -1652,13 +1662,14 @@ void Qt5InformationNodeInstanceServer::handleDynamicAddObjectTimeout()
m_dynamicObjectConstructors.clear();
}
-void Qt5InformationNodeInstanceServer::createCameraAndLightGizmos(
+void Qt5InformationNodeInstanceServer::createGizmos(
const QList<ServerNodeInstance> &instanceList) const
{
QHash<QObject *, QObjectList> cameras;
QHash<QObject *, QObjectList> lights;
QHash<QObject *, QObjectList> particleSystems;
QHash<QObject *, QObjectList> particleEmitters;
+ QHash<QObject *, QObjectList> reflectionProbes;
for (const ServerNodeInstance &instance : instanceList) {
if (instance.isSubclassOf("QQuick3DCamera")) {
@@ -1671,6 +1682,8 @@ void Qt5InformationNodeInstanceServer::createCameraAndLightGizmos(
|| instance.isSubclassOf("QQuick3DParticleAttractor"))
&& !instance.isSubclassOf("QQuick3DParticleTrailEmitter")) {
particleEmitters[find3DSceneRoot(instance)] << instance.internalObject();
+ } else if (instance.isSubclassOf("QQuick3DReflectionProbe")) {
+ reflectionProbes[find3DSceneRoot(instance)] << instance.internalObject();
}
}
@@ -1715,6 +1728,17 @@ void Qt5InformationNodeInstanceServer::createCameraAndLightGizmos(
}
++emitterIt;
}
+
+ auto refProbeIt = reflectionProbes.constBegin();
+ while (refProbeIt != reflectionProbes.constEnd()) {
+ const auto refProbeObjs = refProbeIt.value();
+ for (auto &obj : refProbeObjs) {
+ QMetaObject::invokeMethod(m_editView3DData.rootItem, "addReflectionProbeGizmo",
+ Q_ARG(QVariant, objectToVariant(refProbeIt.key())),
+ Q_ARG(QVariant, objectToVariant(obj)));
+ }
+ ++refProbeIt;
+ }
}
void Qt5InformationNodeInstanceServer::add3DViewPorts(const QList<ServerNodeInstance> &instanceList)
@@ -1941,6 +1965,8 @@ void Qt5InformationNodeInstanceServer::setup3DEditView(
if (toolStates[helper->globalStateId()].contains(helper->lastSceneIdKey()))
lastSceneId = toolStates[helper->globalStateId()][helper->lastSceneIdKey()].toString();
}
+ if (toolStates.contains(helper->projectStateId()))
+ helper->setLastSceneEnvironmentData(toolStates[helper->projectStateId()][helper->lastSceneEnvKey()].toMap());
}
// Find a scene to show
@@ -1977,7 +2003,7 @@ void Qt5InformationNodeInstanceServer::setup3DEditView(
updateActiveSceneToEditView3D();
- createCameraAndLightGizmos(instanceList);
+ createGizmos(instanceList);
// Queue two renders to make sure icon gizmos update properly
render3DEditView(2);
@@ -2509,6 +2535,9 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c
case View3DActionType::ShowGrid:
updatedToolState.insert("showGrid", command.isEnabled());
break;
+ case View3DActionType::ShowLookAt:
+ updatedToolState.insert("showLookAt", command.isEnabled());
+ break;
case View3DActionType::ShowSelectionBox:
updatedToolState.insert("showSelectionBox", command.isEnabled());
break;
@@ -2585,6 +2614,12 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c
case View3DActionType::MaterialOverride:
updatedToolState.insert("matOverride", command.value().toList());
break;
+ case View3DActionType::SetLastSceneEnvData: {
+ auto helper = qobject_cast<QmlDesigner::Internal::GeneralHelper *>(m_3dHelper);
+ if (helper)
+ helper->setLastSceneEnvironmentData(command.value().toMap());
+ break;
+ }
default:
break;
diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h
index 4f7fcd7177..321751cf23 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h
+++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h
@@ -92,7 +92,7 @@ private:
void create3DPreviewView();
void setup3DEditView(const QList<ServerNodeInstance> &instanceList,
const CreateSceneCommand &command);
- void createCameraAndLightGizmos(const QList<ServerNodeInstance> &instanceList) const;
+ void createGizmos(const QList<ServerNodeInstance> &instanceList) const;
void add3DViewPorts(const QList<ServerNodeInstance> &instanceList);
void add3DScenes(const QList<ServerNodeInstance> &instanceList);
QObject *findView3DForInstance(const ServerNodeInstance &instance) const;
diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp
index f4d01d28db..c11899e0ec 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp
+++ b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp
@@ -10,6 +10,7 @@
#include "qt5captureimagenodeinstanceserver.h"
#include "qt5capturepreviewnodeinstanceserver.h"
#include "qt5informationnodeinstanceserver.h"
+#include "qt5import3dnodeinstanceserver.h"
#include "qt5previewnodeinstanceserver.h"
#include "qt5rendernodeinstanceserver.h"
#include "qt5testnodeinstanceserver.h"
@@ -70,6 +71,9 @@ Qt5NodeInstanceClientProxy::Qt5NodeInstanceClientProxy(QObject *parent) :
} else if (QCoreApplication::arguments().at(2) == QLatin1String("bakelightsmode")) {
setNodeInstanceServer(std::make_unique<Qt5BakeLightsNodeInstanceServer>(this));
initializeSocket();
+ } else if (QCoreApplication::arguments().at(2) == QLatin1String("import3dmode")) {
+ setNodeInstanceServer(std::make_unique<Qt5Import3dNodeInstanceServer>(this));
+ initializeSocket();
}
}
diff --git a/src/tools/qml2puppet/qml2puppet/qml2puppetmain.cpp b/src/tools/qml2puppet/qml2puppet/qml2puppetmain.cpp
index 5896df39e1..937bb36d8e 100644
--- a/src/tools/qml2puppet/qml2puppet/qml2puppetmain.cpp
+++ b/src/tools/qml2puppet/qml2puppet/qml2puppetmain.cpp
@@ -7,6 +7,34 @@
#include "runner/qmlruntime.h"
#endif
+#include <QLoggingCategory>
+
+inline Q_LOGGING_CATEGORY(deprecated, "qt.tools.qds.deprecated");
+inline Q_LOGGING_CATEGORY(verbose1, "qt.tools.qds.verbose1");
+inline Q_LOGGING_CATEGORY(verbose2, "qt.tools.qds.verbose2");
+
+void registerMessageHandler(
+ QtMsgType type, [[maybe_unused]] const QMessageLogContext &context, const QString &msg)
+{
+ auto tPrinter = [&](const QString &msgPrefix) {
+ fprintf(
+ stderr, "%s: %s\n", msgPrefix.toLocal8Bit().constData(), msg.toLocal8Bit().constData());
+ };
+
+ if (type == QtDebugMsg)
+ tPrinter("Debug");
+ else if (type == QtInfoMsg)
+ tPrinter("Info");
+ else if (type == QtWarningMsg)
+ tPrinter("Warning");
+ else if (type == QtCriticalMsg)
+ tPrinter("Critical");
+ else if (type == QtFatalMsg) {
+ tPrinter("Fatal");
+ abort();
+ }
+}
+
auto getQmlRunner(int &argc, char **argv)
{
#ifdef ENABLE_INTERNAL_QML_RUNTIME
@@ -24,7 +52,6 @@ auto getQmlRunner(int &argc, char **argv)
int main(int argc, char *argv[])
{
- QDSMeta::Logging::registerMessageHandler();
- QDSMeta::AppInfo::registerAppInfo("Qml2Puppet");
+ qInstallMessageHandler(registerMessageHandler);
return getQmlRunner(argc, argv)->run();
}
diff --git a/src/tools/qml2puppet/qml2puppet/qmlbase.h b/src/tools/qml2puppet/qml2puppet/qmlbase.h
index ae8995f489..45be0cae71 100644
--- a/src/tools/qml2puppet/qml2puppet/qmlbase.h
+++ b/src/tools/qml2puppet/qml2puppet/qmlbase.h
@@ -3,27 +3,12 @@
#pragma once
-#include <QDir>
-#include <QQmlApplicationEngine>
-#include <QQmlComponent>
-#include <QQmlContext>
-
-#include <QFileInfo>
-#include <QFileOpenEvent>
-#include <QLibraryInfo>
-#include <QSurfaceFormat>
-
+#include <QApplication>
#include <QCommandLineParser>
+#include <QQmlApplicationEngine>
-#include <QStandardPaths>
-#include <QTranslator>
-
-#include <QSharedPointer>
-
-#include "appmetadata.h"
#include <iostream>
-#include <QApplication>
class QmlBase : public QObject
{
Q_OBJECT
@@ -44,7 +29,6 @@ public:
#ifdef ENABLE_INTERNAL_QML_RUNTIME
m_argParser.addOption({"qml-runtime", "Run QML Runtime"});
#endif
- m_argParser.addOption({"appinfo", "Print build information"});
m_argParser.addOption({"test", "Run test mode"});
}
@@ -92,7 +76,6 @@ private:
void initParser()
{
QCommandLineOption optHelp = m_argParser.addHelpOption();
- QCommandLineOption optVers = m_argParser.addVersionOption();
if (!m_argParser.parse(m_coreApp->arguments())) {
std::cout << "Error: " << m_argParser.errorText().toStdString() << std::endl;
@@ -103,12 +86,8 @@ private:
std::cout << std::endl;
m_argParser.showHelp(1);
- } else if (m_argParser.isSet(optVers)) {
- m_argParser.showVersion();
} else if (m_argParser.isSet(optHelp)) {
m_argParser.showHelp(0);
- } else if (m_argParser.isSet("appinfo")) {
- QDSMeta::AppInfo::printAppInfo();
} else if (m_argParser.isSet("test")) {
exit(startTestMode());
}
diff --git a/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp b/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp
index 86dce8cb90..332def504e 100644
--- a/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp
+++ b/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp
@@ -10,7 +10,6 @@
#include <app/app_version.h>
#include <qml2puppet/import3d/import3d.h>
-
#include <qt5nodeinstanceclientproxy.h>
#include <QFileInfo>
diff --git a/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp b/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp
index 4435d0e9f4..2876339a90 100644
--- a/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp
+++ b/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp
@@ -13,6 +13,10 @@
#include <QDirIterator>
#include <QFontDatabase>
#include <QIcon>
+#include <QLibraryInfo>
+#include <QStandardPaths>
+#include <QSurfaceFormat>
+#include <QTranslator>
#define FILE_OPEN_EVENT_WAIT_TIME 3000 // ms
#define QSL QStringLiteral
@@ -242,12 +246,12 @@ void QmlRuntime::initQmlRunner()
if (translator.load(translationFile)) {
m_coreApp->installTranslator(&translator);
if (m_verboseMode)
- qInfo() << "qml: Loaded translation file %s\n",
- qPrintable(QDir::toNativeSeparators(translationFile));
+ qInfo() << "qml: Loaded translation file "
+ << qPrintable(QDir::toNativeSeparators(translationFile));
} else {
if (!m_quietMode)
- qInfo() << "qml: Could not load the translation file %s\n",
- qPrintable(QDir::toNativeSeparators(translationFile));
+ qInfo() << "qml: Could not load the translation file "
+ << qPrintable(QDir::toNativeSeparators(translationFile));
}
}
#else
@@ -278,7 +282,7 @@ void QmlRuntime::initQmlRunner()
for (const QString &path : std::as_const(files)) {
QUrl url = QUrl::fromUserInput(path, QDir::currentPath(), QUrl::AssumeLocalFile);
if (m_verboseMode)
- qInfo() << "qml: loading %s\n", qPrintable(url.toString());
+ qInfo() << "qml: loading " << qPrintable(url.toString());
m_qmlEngine->load(url);
}
@@ -318,8 +322,8 @@ void QmlRuntime::loadConf(const QString &override, bool quiet) // Terminates app
else
fi.setFile(override);
if (!fi.exists()) {
- qCritical() << "qml: Couldn't find required configuration file: %s\n",
- qPrintable(QDir::toNativeSeparators(fi.absoluteFilePath()));
+ qCritical() << "qml: Couldn't find required configuration file:"
+ << qPrintable(QDir::toNativeSeparators(fi.absoluteFilePath()));
exit(1);
}
settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath());
@@ -327,13 +331,14 @@ void QmlRuntime::loadConf(const QString &override, bool quiet) // Terminates app
}
if (!quiet) {
- qInfo() << "qml: %s\n", QLibraryInfo::build();
+ qInfo() << "qml:" << QLibraryInfo::build();
if (builtIn) {
- qInfo() << "qml: Using built-in configuration: %s\n",
- qPrintable(override.isEmpty() ? defaultFileName : override);
+ qInfo() << "qml: Using built-in configuration:"
+ << qPrintable(override.isEmpty() ? defaultFileName : override);
} else {
- qInfo() << "qml: Using configuration: %s\n",
- qPrintable(settingsUrl.isLocalFile()
+ qInfo() << "qml: Using configuration:"
+ << qPrintable(
+ settingsUrl.isLocalFile()
? QDir::toNativeSeparators(settingsUrl.toLocalFile())
: settingsUrl.toString());
}
@@ -345,18 +350,19 @@ void QmlRuntime::loadConf(const QString &override, bool quiet) // Terminates app
m_conf.reset(qobject_cast<Config *>(c2.create()));
if (!m_conf) {
- qCritical() << "qml: Error loading configuration file: %s\n", qPrintable(c2.errorString());
+ qCritical() << "qml: Error loading configuration file:" << qPrintable(c2.errorString());
exit(1);
}
}
void QmlRuntime::listConfFiles()
{
+ qDebug() << "qml: Built-in configurations:";
const QDir confResourceDir(m_confResourcePath);
- qInfo() << "%s\n", qPrintable(QCoreApplication::translate("main", "Built-in configurations:"));
+ qInfo() << qPrintable(QCoreApplication::translate("main", "Built-in configurations:"));
for (const QFileInfo &fi : confResourceDir.entryInfoList(QDir::Files))
- qInfo() << " %s\n", qPrintable(fi.baseName());
- qInfo() << "%s\n", qPrintable(QCoreApplication::translate("main", "Other configurations:"));
+ qInfo() << qPrintable(fi.baseName());
+ qInfo() << qPrintable(QCoreApplication::translate("main", "Other configurations:"));
bool foundOther = false;
const QStringList otherLocations = QStandardPaths::standardLocations(
QStandardPaths::AppConfigLocation);
@@ -365,16 +371,16 @@ void QmlRuntime::listConfFiles()
for (const QFileInfo &fi : confDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) {
foundOther = true;
if (m_verboseMode)
- qInfo() << " %s\n", qPrintable(fi.absoluteFilePath());
+ qInfo() << qPrintable(fi.absoluteFilePath());
else
- qInfo() << " %s\n", qPrintable(fi.baseName());
+ qInfo() << qPrintable(fi.baseName());
}
}
if (!foundOther)
- qInfo() << " %s\n", qPrintable(QCoreApplication::translate("main", "none"));
+ qInfo() << qPrintable(QCoreApplication::translate("main", "none"));
if (m_verboseMode) {
- qInfo() << "%s\n", qPrintable(QCoreApplication::translate("main", "Checked in:"));
+ qInfo() << qPrintable(QCoreApplication::translate("main", "Checked in:"));
for (const auto &confDirPath : otherLocations)
- qInfo() << " %s\n", qPrintable(confDirPath);
+ qInfo() << qPrintable(confDirPath);
}
}
diff --git a/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp b/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp
index 096ac36244..6897a23973 100644
--- a/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp
+++ b/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp
@@ -604,7 +604,7 @@ static QString qmlDesignerRCPath()
QVariant fixResourcePaths(const QVariant &value)
{
- if (value.typeId() == QVariant::Url) {
+ if (value.typeId() == QMetaType::QUrl) {
const QUrl url = value.toUrl();
if (url.scheme() == QLatin1String("qrc")) {
const QString path = QLatin1String("qrc:") + url.path();
@@ -625,7 +625,7 @@ QVariant fixResourcePaths(const QVariant &value)
}
}
}
- if (value.typeId() == QVariant::String) {
+ if (value.typeId() == QMetaType::QString) {
const QString str = value.toString();
if (str.contains(QLatin1String("qrc:"))) {
if (!qmlDesignerRCPath().isEmpty()) {
diff --git a/src/tools/sdktool/addkitoperation.cpp b/src/tools/sdktool/addkitoperation.cpp
index 037cf3c5e5..0028c5e63c 100644
--- a/src/tools/sdktool/addkitoperation.cpp
+++ b/src/tools/sdktool/addkitoperation.cpp
@@ -190,7 +190,7 @@ bool AddKitOperation::setArguments(const QStringList &args)
++i; // skip next;
const QString tmp = current.mid(2);
- const QString tmp2 = tmp.mid(0, tmp.count() - 9 /* toolchain */);
+ const QString tmp2 = tmp.mid(0, tmp.size() - 9 /* toolchain */);
const QString lang = tmp2.isEmpty() ? QString("Cxx") : tmp2;
if (next.isEmpty()) {
diff --git a/tests/auto/android/tst_avdmanageroutputparser.cpp b/tests/auto/android/tst_avdmanageroutputparser.cpp
index a41022ec5d..e65a2bb15a 100644
--- a/tests/auto/android/tst_avdmanageroutputparser.cpp
+++ b/tests/auto/android/tst_avdmanageroutputparser.cpp
@@ -80,10 +80,9 @@ void tst_AvdManagerOutputParser::parse()
QFETCH(AndroidDeviceInfoList, output);
QFETCH(Utils::FilePaths, errorPaths);
- Utils::FilePaths avdErrorPaths;
- const auto result = parseAvdList(input, &avdErrorPaths);
- QCOMPARE(result, output);
- QCOMPARE(avdErrorPaths, errorPaths);
+ const auto result = parseAvdList(input);
+ QCOMPARE(result.avdList, output);
+ QCOMPARE(result.errorPaths, errorPaths);
}
QTEST_GUILESS_MAIN(tst_AvdManagerOutputParser)
diff --git a/tests/auto/debugger/tst_dumpers.cpp b/tests/auto/debugger/tst_dumpers.cpp
index e5763e847e..4385f84084 100644
--- a/tests/auto/debugger/tst_dumpers.cpp
+++ b/tests/auto/debugger/tst_dumpers.cpp
@@ -1200,7 +1200,28 @@ void tst_Dumpers::initTestCase()
if (qEnvironmentVariableIntValue("QTC_USE_CMAKE_FOR_TEST"))
m_buildSystem = BuildSystem::CMake;
+ QProcess qmake;
+ qmake.start(m_qmakeBinary, {"--version"});
+ QVERIFY(qmake.waitForFinished());
+ QByteArray output = qmake.readAllStandardOutput();
+ QByteArray error = qmake.readAllStandardError();
+ int pos0 = output.indexOf("Qt version");
+ if (pos0 == -1) {
+ qCDebug(lcDumpers).noquote() << "Output: " << output;
+ qCDebug(lcDumpers).noquote() << "Error: " << error;
+ QVERIFY(false);
+ }
+ pos0 += 11;
+ int pos1 = output.indexOf('.', pos0 + 1);
+ int major = output.mid(pos0, pos1++ - pos0).toInt();
+ int pos2 = output.indexOf('.', pos1 + 1);
+ int minor = output.mid(pos1, pos2++ - pos1).toInt();
+ int pos3 = output.indexOf(' ', pos2 + 1);
+ int patch = output.mid(pos2, pos3++ - pos2).toInt();
+ m_qtVersion = 0x10000 * major + 0x100 * minor + patch;
+
qCDebug(lcDumpers) << "QMake : " << m_qmakeBinary;
+ qCDebug(lcDumpers) << "Qt Version : " << m_qtVersion;
qCDebug(lcDumpers) << "Use CMake : " << (m_buildSystem == BuildSystem::CMake) << int(m_buildSystem);
m_useGLibCxxDebug = qgetenv("QTC_USE_GLIBCXXDEBUG_FOR_TEST").toInt();
@@ -1381,27 +1402,6 @@ void tst_Dumpers::dumper()
QByteArray error;
if (data.neededQtVersion.isRestricted) {
- QProcess qmake;
- qmake.setWorkingDirectory(t->buildPath);
- qmake.start(m_qmakeBinary, {"--version"});
- QVERIFY(qmake.waitForFinished());
- output = qmake.readAllStandardOutput();
- error = qmake.readAllStandardError();
- int pos0 = output.indexOf("Qt version");
- if (pos0 == -1) {
- qCDebug(lcDumpers).noquote() << "Output: " << output;
- qCDebug(lcDumpers).noquote() << "Error: " << error;
- QVERIFY(false);
- }
- pos0 += 11;
- int pos1 = output.indexOf('.', pos0 + 1);
- int major = output.mid(pos0, pos1++ - pos0).toInt();
- int pos2 = output.indexOf('.', pos1 + 1);
- int minor = output.mid(pos1, pos2++ - pos1).toInt();
- int pos3 = output.indexOf(' ', pos2 + 1);
- int patch = output.mid(pos2, pos3++ - pos2).toInt();
- m_qtVersion = 0x10000 * major + 0x100 * minor + patch;
-
if (data.neededQtVersion.min > m_qtVersion)
MSKIP_SINGLE(QByteArray("Need minimum Qt version "
+ QByteArray::number(data.neededQtVersion.min, 16)));
@@ -1793,6 +1793,7 @@ void tst_Dumpers::dumper()
"python theDumper.fetchVariables({" + dumperOptions +
"'token':2,'fancy':1,'forcens':1,"
"'autoderef':1,'dyntype':1,'passexceptions':1,"
+ "'qtversion':" + QString::number(m_qtVersion) + ",'qtnamespace':'',"
"'testing':1,'qobjectnames':1,"
"'expanded':{" + expandedq + "}})\n";
@@ -1816,6 +1817,7 @@ void tst_Dumpers::dumper()
"'token':2,'fancy':1,'forcens':1,"
"'autoderef':1,'dyntype':1,'passexceptions':0,"
"'testing':1,'qobjectnames':1,"
+ "'qtversion':" + QString::number(m_qtVersion) + ",'qtnamespace':'',"
"'expanded':{" + expandedq + "}})\n"
"q\n";
} else if (m_debuggerEngine == LldbEngine) {
@@ -1840,6 +1842,7 @@ void tst_Dumpers::dumper()
"'fancy':1,'forcens':1,"
"'autoderef':1,'dyntype':1,'passexceptions':1,"
"'testing':1,'qobjectnames':1,"
+ "'qtversion':" + QString::number(m_qtVersion) + ",'qtnamespace':'',"
"'expanded':{" + expandedq + "}})\n"
"quit\n";
diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp
index 569d0ce3a4..d37b639cd0 100644
--- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp
+++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp
@@ -149,6 +149,7 @@ public:
QString defaultPuppetToplevelBuildDirectory() const override { return {}; }
QString qmlPuppetFallbackDirectory() const override { return {}; }
QUrl projectUrl() const override { return {}; }
+ QString projectName() const override { return {}; }
void parseItemLibraryDescriptions() override {}
const QmlDesigner::DesignerSettings &designerSettings() const override { return settings; }
void undoOnCurrentDesignDocument() override {}
@@ -698,17 +699,17 @@ void tst_TestCore::testRewriterDynamicProperties()
QCOMPARE(rootModelNode.properties().count(), 18);
QVERIFY(rootModelNode.hasVariantProperty("i"));
QCOMPARE(rootModelNode.variantProperty("i").dynamicTypeName(), QmlDesigner::TypeName("int"));
- QCOMPARE(rootModelNode.variantProperty("i").value().type(), QVariant::Int);
+ QCOMPARE(rootModelNode.variantProperty("i").value().typeId(), QMetaType::Int);
QCOMPARE(testRewriterView1->rootModelNode().variantProperty("i").value().toInt(), 0);
QVERIFY(rootModelNode.hasVariantProperty("ii"));
QCOMPARE(rootModelNode.variantProperty("ii").dynamicTypeName(), QmlDesigner::TypeName("int"));
- QCOMPARE(rootModelNode.variantProperty("ii").value().type(), QVariant::Int);
+ QCOMPARE(rootModelNode.variantProperty("ii").value().typeId(), QMetaType::Int);
QCOMPARE(testRewriterView1->rootModelNode().variantProperty("ii").value().toInt(), 1);
QVERIFY(rootModelNode.hasVariantProperty("b"));
QCOMPARE(rootModelNode.variantProperty("b").dynamicTypeName(), QmlDesigner::TypeName("bool"));
- QCOMPARE(rootModelNode.variantProperty("b").value().type(), QVariant::Bool);
+ QCOMPARE(rootModelNode.variantProperty("b").value().typeId(), QMetaType::Bool);
QCOMPARE(testRewriterView1->rootModelNode().variantProperty("b").value().toBool(), false);
QVERIFY(rootModelNode.hasVariantProperty("bb"));
@@ -716,7 +717,7 @@ void tst_TestCore::testRewriterDynamicProperties()
QVERIFY(rootModelNode.hasVariantProperty("d"));
QCOMPARE(rootModelNode.variantProperty("d").dynamicTypeName(), QmlDesigner::TypeName("double"));
- QCOMPARE(rootModelNode.variantProperty("d").value().type(), QVariant::Double);
+ QCOMPARE(rootModelNode.variantProperty("d").value().typeId(), QMetaType::Double);
QCOMPARE(testRewriterView1->rootModelNode().variantProperty("d").value().toDouble(), 0.0);
QVERIFY(rootModelNode.hasVariantProperty("dd"));
@@ -724,7 +725,7 @@ void tst_TestCore::testRewriterDynamicProperties()
QVERIFY(rootModelNode.hasVariantProperty("r"));
QCOMPARE(rootModelNode.variantProperty("r").dynamicTypeName(), QmlDesigner::TypeName("real"));
- QCOMPARE(rootModelNode.variantProperty("r").value().type(), QVariant::Double);
+ QCOMPARE(rootModelNode.variantProperty("r").value().typeId(), QMetaType::Double);
QCOMPARE(testRewriterView1->rootModelNode().variantProperty("r").value().toDouble(), 0.0);
QVERIFY(rootModelNode.hasVariantProperty("rr"));
@@ -732,7 +733,7 @@ void tst_TestCore::testRewriterDynamicProperties()
QVERIFY(rootModelNode.hasVariantProperty("s"));
QCOMPARE(rootModelNode.variantProperty("s").dynamicTypeName(), QmlDesigner::TypeName("string"));
- QCOMPARE(rootModelNode.variantProperty("s").value().type(), QVariant::String);
+ QCOMPARE(rootModelNode.variantProperty("s").value().typeId(), QMetaType::QString);
QCOMPARE(testRewriterView1->rootModelNode().variantProperty("s").value().toString(), QString());
QVERIFY(rootModelNode.hasVariantProperty("ss"));
@@ -740,7 +741,7 @@ void tst_TestCore::testRewriterDynamicProperties()
QVERIFY(rootModelNode.hasVariantProperty("u"));
QCOMPARE(rootModelNode.variantProperty("u").dynamicTypeName(), QmlDesigner::TypeName("url"));
- QCOMPARE(rootModelNode.variantProperty("u").value().type(), QVariant::Url);
+ QCOMPARE(rootModelNode.variantProperty("u").value().typeId(), QMetaType::QUrl);
QCOMPARE(testRewriterView1->rootModelNode().variantProperty("u").value().toUrl(), QUrl());
QVERIFY(rootModelNode.hasVariantProperty("uu"));
@@ -748,7 +749,7 @@ void tst_TestCore::testRewriterDynamicProperties()
QVERIFY(rootModelNode.hasVariantProperty("c"));
QCOMPARE(rootModelNode.variantProperty("c").dynamicTypeName(), QmlDesigner::TypeName("color"));
- QCOMPARE(rootModelNode.variantProperty("c").value().type(), QVariant::Color);
+ QCOMPARE(rootModelNode.variantProperty("c").value().typeId(), QMetaType::QColor);
QCOMPARE(testRewriterView1->rootModelNode().variantProperty("c").value().value<QColor>(), QColor());
QVERIFY(rootModelNode.hasVariantProperty("cc"));
@@ -756,7 +757,7 @@ void tst_TestCore::testRewriterDynamicProperties()
QVERIFY(rootModelNode.hasVariantProperty("t"));
QCOMPARE(rootModelNode.variantProperty("t").dynamicTypeName(), QmlDesigner::TypeName("date"));
- QCOMPARE(rootModelNode.variantProperty("t").value().type(), QVariant::Date);
+ QCOMPARE(rootModelNode.variantProperty("t").value().typeId(), QMetaType::QDate);
QCOMPARE(testRewriterView1->rootModelNode().variantProperty("t").value().value<QDate>(), QDate());
QVERIFY(rootModelNode.hasVariantProperty("tt"));
@@ -764,8 +765,8 @@ void tst_TestCore::testRewriterDynamicProperties()
QVERIFY(rootModelNode.hasVariantProperty("v"));
QCOMPARE(rootModelNode.variantProperty("v").dynamicTypeName(), QmlDesigner::TypeName("variant"));
- const int type = rootModelNode.variantProperty("v").value().type();
- QCOMPARE(type, QMetaType::type("QVariant"));
+ const int type = rootModelNode.variantProperty("v").value().typeId();
+ QCOMPARE(type, QMetaType::fromName("QVariant").id());
QVERIFY(rootModelNode.hasVariantProperty("vv"));
const QString inThere = testRewriterView1->rootModelNode().variantProperty("vv").value().value<QString>();
@@ -3872,8 +3873,8 @@ void tst_TestCore::testRewriterPreserveType()
QCOMPARE(rootNode.type(), QmlDesigner::TypeName("QtQuick.Rectangle"));
ModelNode textNode = rootNode.directSubModelNodes().first();
- QCOMPARE(QVariant::Bool, textNode.variantProperty("font.bold").value().type());
- QCOMPARE(QVariant::Double, textNode.variantProperty("font.pointSize").value().type());
+ QCOMPARE(QMetaType::Bool, textNode.variantProperty("font.bold").value().typeId());
+ QCOMPARE(QMetaType::Double, textNode.variantProperty("font.pointSize").value().typeId());
textNode.variantProperty("font.bold").setValue(QVariant(false));
textNode.variantProperty("font.bold").setValue(QVariant(true));
textNode.variantProperty("font.pointSize").setValue(QVariant(13.0));
@@ -3883,8 +3884,8 @@ void tst_TestCore::testRewriterPreserveType()
newTextNode.variantProperty("font.bold").setValue(QVariant(true));
newTextNode.variantProperty("font.pointSize").setValue(QVariant(13.0));
- QCOMPARE(QVariant::Bool, newTextNode.variantProperty("font.bold").value().type());
- QCOMPARE(QVariant::Double, newTextNode.variantProperty("font.pointSize").value().type());
+ QCOMPARE(QMetaType::Bool, newTextNode.variantProperty("font.bold").value().typeId());
+ QCOMPARE(QMetaType::Double, newTextNode.variantProperty("font.pointSize").value().typeId());
}
void tst_TestCore::testRewriterForArrayMagic()
@@ -6957,9 +6958,9 @@ void tst_TestCore::testModelPropertyValueTypes()
ModelNode rootModelNode(testRewriterView1->rootModelNode());
QVERIFY(rootModelNode.isValid());
- QCOMPARE(rootModelNode.variantProperty("width").value().type(), QVariant::Double);
- QCOMPARE(rootModelNode.variantProperty("radius").value().type(), QVariant::Double);
- QCOMPARE(rootModelNode.variantProperty("color").value().type(), QVariant::Color);
+ QCOMPARE(rootModelNode.variantProperty("width").value().typeId(), QMetaType::Double);
+ QCOMPARE(rootModelNode.variantProperty("radius").value().typeId(), QMetaType::Double);
+ QCOMPARE(rootModelNode.variantProperty("color").value().typeId(), QMetaType::QColor);
}
void tst_TestCore::testModelNodeInHierarchy()
@@ -8963,18 +8964,18 @@ void tst_TestCore::loadGradient()
QCOMPARE(pOne.id(), QString("pOne"));
QCOMPARE(pOne.directSubModelNodes().size(), 0);
QCOMPARE(pOne.propertyNames().size(), 2);
- QCOMPARE(pOne.variantProperty("position").value().type(), QVariant::Double);
+ QCOMPARE(pOne.variantProperty("position").value().typeId(), QMetaType::Double);
QCOMPARE(pOne.variantProperty("position").value().toDouble(), 0.0);
- QCOMPARE(pOne.variantProperty("color").value().type(), QVariant::Color);
+ QCOMPARE(pOne.variantProperty("color").value().typeId(), QMetaType::QColor);
QCOMPARE(pOne.variantProperty("color").value().value<QColor>(), QColor("lightsteelblue"));
QCOMPARE(pTwo.type(), QmlDesigner::TypeName("QtQuick.GradientStop"));
QCOMPARE(pTwo.id(), QString("pTwo"));
QCOMPARE(pTwo.directSubModelNodes().size(), 0);
QCOMPARE(pTwo.propertyNames().size(), 2);
- QCOMPARE(pTwo.variantProperty("position").value().type(), QVariant::Double);
+ QCOMPARE(pTwo.variantProperty("position").value().typeId(), QMetaType::Double);
QCOMPARE(pTwo.variantProperty("position").value().toDouble(), 1.0);
- QCOMPARE(pTwo.variantProperty("color").value().type(), QVariant::Color);
+ QCOMPARE(pTwo.variantProperty("color").value().typeId(), QMetaType::QColor);
QCOMPARE(pTwo.variantProperty("color").value().value<QColor>(), QColor("blue"));
}
@@ -9003,18 +9004,18 @@ void tst_TestCore::loadGradient()
QCOMPARE(nOne.id(), QString("nOne"));
QCOMPARE(nOne.directSubModelNodes().size(), 0);
QCOMPARE(nOne.propertyNames().size(), 2);
- QCOMPARE(nOne.variantProperty("position").value().type(), QVariant::Double);
+ QCOMPARE(nOne.variantProperty("position").value().typeId(), QMetaType::Double);
QCOMPARE(nOne.variantProperty("position").value().toDouble(), 0.0);
- QCOMPARE(nOne.variantProperty("color").value().type(), QVariant::Color);
+ QCOMPARE(nOne.variantProperty("color").value().typeId(), QMetaType::QColor);
QCOMPARE(nOne.variantProperty("color").value().value<QColor>(), QColor("blue"));
QCOMPARE(nTwo.type(), QmlDesigner::TypeName("QtQuick.GradientStop"));
QCOMPARE(nTwo.id(), QString("nTwo"));
QCOMPARE(nTwo.directSubModelNodes().size(), 0);
QCOMPARE(nTwo.propertyNames().size(), 2);
- QCOMPARE(nTwo.variantProperty("position").value().type(), QVariant::Double);
+ QCOMPARE(nTwo.variantProperty("position").value().typeId(), QMetaType::Double);
QCOMPARE(nTwo.variantProperty("position").value().toDouble(), 1.0);
- QCOMPARE(nTwo.variantProperty("color").value().type(), QVariant::Color);
+ QCOMPARE(nTwo.variantProperty("color").value().typeId(), QMetaType::QColor);
QCOMPARE(nTwo.variantProperty("color").value().value<QColor>(), QColor("lightsteelblue"));
}
}
diff --git a/tests/auto/utils/commandline/tst_commandline.cpp b/tests/auto/utils/commandline/tst_commandline.cpp
index d884e47c8d..aca984adf5 100644
--- a/tests/auto/utils/commandline/tst_commandline.cpp
+++ b/tests/auto/utils/commandline/tst_commandline.cpp
@@ -125,6 +125,31 @@ private slots:
QCOMPARE(actual, expected);
}
+ void testConstructor_data()
+ {
+ QTest::addColumn<CommandLine>("command");
+ QTest::addColumn<FilePath>("executable");
+ QTest::addColumn<QStringList>("arguments");
+
+ const FilePath filePath("some_path");
+ const QString arg("-arg");
+ const QStringList args{"-a", "-b", "-c"};
+
+ QTest::newRow("mixed-strings") << CommandLine{filePath, {"-A", arg, args}}
+ << filePath << (QStringList{"-A"} << arg << args);
+ }
+
+ void testConstructor()
+ {
+ QFETCH(CommandLine, command);
+ QFETCH(FilePath, executable);
+ QFETCH(QStringList, arguments);
+
+ QCOMPARE(command.executable(), executable);
+ QCOMPARE(command.arguments(), arguments.join(' '));
+ QCOMPARE(command.splitArguments(), arguments);
+ }
+
void testFromUserInput_data()
{
QTest::addColumn<QString>("input");
diff --git a/tests/auto/utils/deviceshell/tst_deviceshell.cpp b/tests/auto/utils/deviceshell/tst_deviceshell.cpp
index f94d0dc5b4..75150add83 100644
--- a/tests/auto/utils/deviceshell/tst_deviceshell.cpp
+++ b/tests/auto/utils/deviceshell/tst_deviceshell.cpp
@@ -111,7 +111,7 @@ private slots:
const FilePath executable = Environment::systemEnvironment()
.searchInPath(shell, {"/usr/local/bin"});
if (executable.exists())
- m_availableShells.append({executable, {}});
+ m_availableShells.append(CommandLine{executable});
}
}
@@ -210,7 +210,7 @@ private slots:
QRandomGenerator generator;
- const RunResult result = shell.runInShell({"cat", {}}, testData.toUtf8());
+ const RunResult result = shell.runInShell(CommandLine{"cat"}, testData.toUtf8());
QCOMPARE(result.exitCode, 0);
const QString resultAsUtf8 = QString::fromUtf8(result.stdOut);
QCOMPARE(resultAsUtf8.size(), testData.size());
@@ -236,7 +236,7 @@ private slots:
TestShell shell(cmdLine);
QCOMPARE(shell.state(), DeviceShell::State::Succeeded);
- const RunResult result = shell.runInShell({"cat", {}}, m_asciiTestData);
+ const RunResult result = shell.runInShell(CommandLine{"cat"}, m_asciiTestData);
QCOMPARE(result.stdOut, m_asciiTestData);
}
@@ -259,7 +259,7 @@ private slots:
TestShell shell(cmdLine);
QCOMPARE(shell.state(), DeviceShell::State::Succeeded);
- const RunResult result = shell.runInShell({"cat", {}}, m_asciiTestData);
+ const RunResult result = shell.runInShell(CommandLine{"cat"}, m_asciiTestData);
QCOMPARE(result.stdOut, m_asciiTestData);
QVERIFY(result.stdErr.isEmpty());
diff --git a/tests/auto/utils/persistentsettings/tst_persistentsettings.cpp b/tests/auto/utils/persistentsettings/tst_persistentsettings.cpp
index e2fde29909..9890545a7e 100644
--- a/tests/auto/utils/persistentsettings/tst_persistentsettings.cpp
+++ b/tests/auto/utils/persistentsettings/tst_persistentsettings.cpp
@@ -61,18 +61,18 @@ void PersistentSettingsTest::tst_readwrite()
auto found = restored.find(it.key());
QVERIFY(found != restoredEnd);
QVERIFY(found.value().isValid());
- if (it.value().type() == QVariant::List) {
+ if (it.value().typeId() == QMetaType::QVariantList) {
const QVariantList origList = it.value().toList();
const QVariantList foundList = found.value().toList();
QCOMPARE(foundList.size(), origList.size());
for (int i = 0, vEnd = foundList.size(); i < vEnd; ++i) {
- if (foundList.at(i).type() == QVariant::Rect)
+ if (foundList.at(i).typeId() == QMetaType::QRect)
qDebug() << foundList.at(i).toRect() << origList.at(i).toRect();
QCOMPARE(foundList.at(i), origList.at(i));
}
}
- if (it.value().type() == QVariant::Rect)
+ if (it.value().typeId() == QMetaType::QRect)
qDebug() << found.value().toRect() << "vs" << it.value().toRect();
QCOMPARE(found.value(), it.value());
}
diff --git a/tests/auto/utils/process/processtestapp/processtestapp.cpp b/tests/auto/utils/process/processtestapp/processtestapp.cpp
index 0148d1e261..b85cdabede 100644
--- a/tests/auto/utils/process/processtestapp/processtestapp.cpp
+++ b/tests/auto/utils/process/processtestapp/processtestapp.cpp
@@ -84,7 +84,7 @@ void SubProcessConfig::setupSubProcess(Process *subProcess) const
subProcess->setEnvironment(m_environment);
const FilePath filePath = FilePath::fromString(s_pathToProcessTestApp
+ QLatin1String("/processtestapp")).withExecutableSuffix();
- subProcess->setCommand(CommandLine(filePath, {}));
+ subProcess->setCommand(CommandLine{filePath});
}
void SubProcessConfig::setupSubProcess(QProcess *subProcess) const
diff --git a/tests/auto/utils/process/tst_process.cpp b/tests/auto/utils/process/tst_process.cpp
index cd9f871515..a1e92edfe2 100644
--- a/tests/auto/utils/process/tst_process.cpp
+++ b/tests/auto/utils/process/tst_process.cpp
@@ -114,7 +114,7 @@ private slots:
QCOMPARE(qproc.exitCode(), 0);
Process proc;
- proc.setCommand({envPath, {}});
+ proc.setCommand(CommandLine{envPath});
proc.runBlocking();
QCOMPARE(proc.exitCode(), 0);
const QByteArray output = proc.rawStdOut() + proc.rawStdErr();
@@ -1138,8 +1138,7 @@ void tst_Process::notRunningAfterStartingNonExistingProgram()
QFETCH(ProcessSignalType, signalType);
Process process;
- process.setCommand({ FilePath::fromString(
- "there_is_a_big_chance_that_executable_with_that_name_does_not_exists"), {} });
+ process.setCommand(CommandLine{"there_is_a_big_chance_that_executable_with_that_name_does_not_exists"});
int doneCount = 0;
QObject::connect(&process, &Process::done, [&process, &doneCount]() {
@@ -1556,7 +1555,7 @@ void tst_Process::stdinToShell()
QSKIP("Skipping env test on Windows");
Process proc;
- proc.setCommand({"sh", {}});
+ proc.setCommand(CommandLine{"sh"});
proc.setWriteData("echo hallo");
proc.runBlocking();
@@ -1595,8 +1594,8 @@ void tst_Process::eventLoopMode()
{
Process process;
- process.setCommand({FilePath::fromString(
- "there_is_a_big_chance_that_executable_with_that_name_does_not_exists"), {} });
+ process.setCommand(
+ CommandLine{"there_is_a_big_chance_that_executable_with_that_name_does_not_exists"});
process.setProcessImpl(processImpl);
process.runBlocking(10s, eventLoopMode);
QCOMPARE(process.result(), ProcessResult::StartFailed);
diff --git a/tests/manual/deviceshell/tst_deviceshell.cpp b/tests/manual/deviceshell/tst_deviceshell.cpp
index 96638763f8..88f95fd711 100644
--- a/tests/manual/deviceshell/tst_deviceshell.cpp
+++ b/tests/manual/deviceshell/tst_deviceshell.cpp
@@ -33,19 +33,17 @@ public:
const FilePath shExecutable = Environment::systemEnvironment()
.searchInPath("sh", {"/usr/local/bin"});
- if (dockerExecutable.exists()) {
+ if (dockerExecutable.exists())
cmd = {dockerExecutable, {"run", "-i", "--rm","alpine"}};
- } else if (dashExecutable.exists()) {
- cmd = {dashExecutable, {}};
- } else if (bashExecutable.exists()) {
- cmd = {bashExecutable, {}};
- } else if (shExecutable.exists()) {
- cmd = {shExecutable, {}};
- }
-
- if (cmd.isEmpty()) {
+ else if (dashExecutable.exists())
+ cmd = CommandLine{dashExecutable};
+ else if (bashExecutable.exists())
+ cmd = CommandLine{bashExecutable};
+ else if (shExecutable.exists())
+ cmd = CommandLine{shExecutable};
+
+ if (cmd.isEmpty())
return cmd;
- }
qDebug() << "Using shell cmd:" << cmd;
}
@@ -97,7 +95,7 @@ class tst_DeviceShell : public QObject
t.start();
const auto cat = [&shell](const QByteArray &data) {
- return shell.runInShell({"cat", {}}, data).stdOut;
+ return shell.runInShell(CommandLine{"cat"}, data).stdOut;
};
const QList<QByteArray> results = QtConcurrent::blockingMapped(testArray, cat);
QCOMPARE(results, testArray);
@@ -164,7 +162,7 @@ private slots:
TestShell shell;
QCOMPARE(shell.state(), DeviceShell::State::Succeeded);
- const RunResult r = shell.runInShell({"cat", {}}, utf8string.toUtf8());
+ const RunResult r = shell.runInShell(CommandLine{"cat"}, utf8string.toUtf8());
const QString output = QString::fromUtf8(r.stdOut);
QCOMPARE(output, utf8string);
}
diff --git a/tests/manual/layoutbuilder/v2/CMakeLists.txt b/tests/manual/layoutbuilder/v2/CMakeLists.txt
new file mode 100644
index 0000000000..f0d59c3768
--- /dev/null
+++ b/tests/manual/layoutbuilder/v2/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.5)
+
+project(lb LANGUAGES CXX)
+
+set(CMAKE_AUTOUIC ON)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+
+find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
+find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
+
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+add_executable(lb lb.h lb.cpp main.cpp)
+
+target_link_libraries(lb PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
diff --git a/tests/manual/layoutbuilder/v2/lb.cpp b/tests/manual/layoutbuilder/v2/lb.cpp
new file mode 100644
index 0000000000..de8f674177
--- /dev/null
+++ b/tests/manual/layoutbuilder/v2/lb.cpp
@@ -0,0 +1,972 @@
+// 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 "lb.h"
+
+#include <QDebug>
+#include <QFormLayout>
+#include <QGridLayout>
+#include <QGroupBox>
+#include <QLabel>
+#include <QPushButton>
+#include <QSpacerItem>
+#include <QSpinBox>
+#include <QSplitter>
+#include <QStackedLayout>
+#include <QStackedWidget>
+#include <QStyle>
+#include <QTabWidget>
+#include <QTextEdit>
+#include <QToolBar>
+
+namespace Layouting {
+
+// That's cut down qtcassert.{c,h} to avoid the dependency.
+#define QTC_STRINGIFY_HELPER(x) #x
+#define QTC_STRINGIFY(x) QTC_STRINGIFY_HELPER(x)
+#define QTC_STRING(cond) qDebug("SOFT ASSERT: \"%s\" in %s: %s", cond, __FILE__, QTC_STRINGIFY(__LINE__))
+#define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_STRING(#cond); action; } do {} while (0)
+#define QTC_CHECK(cond) if (cond) {} else { QTC_STRING(#cond); } do {} while (0)
+
+template <typename X>
+X::Implementation *access(const X *x)
+{
+ return static_cast<X::Implementation *>(x->ptr);
+}
+
+template <typename X>
+void apply(X *x, std::initializer_list<typename X::I> ps)
+{
+ for (auto && p : ps)
+ p.apply(x);
+}
+
+// FlowLayout
+
+class FlowLayout : public QLayout
+{
+public:
+ explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1)
+ : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
+ {
+ setContentsMargins(margin, margin, margin, margin);
+ }
+
+ FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1)
+ : m_hSpace(hSpacing), m_vSpace(vSpacing)
+ {
+ setContentsMargins(margin, margin, margin, margin);
+ }
+
+ ~FlowLayout() override
+ {
+ QLayoutItem *item;
+ while ((item = takeAt(0)))
+ delete item;
+ }
+
+ void addItem(QLayoutItem *item) override { itemList.append(item); }
+
+ int horizontalSpacing() const
+ {
+ if (m_hSpace >= 0)
+ return m_hSpace;
+ else
+ return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
+ }
+
+ int verticalSpacing() const
+ {
+ if (m_vSpace >= 0)
+ return m_vSpace;
+ else
+ return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
+ }
+
+ Qt::Orientations expandingDirections() const override
+ {
+ return {};
+ }
+
+ bool hasHeightForWidth() const override { return true; }
+
+ int heightForWidth(int width) const override
+ {
+ int height = doLayout(QRect(0, 0, width, 0), true);
+ return height;
+ }
+
+ int count() const override { return itemList.size(); }
+
+ QLayoutItem *itemAt(int index) const override
+ {
+ return itemList.value(index);
+ }
+
+ QSize minimumSize() const override
+ {
+ QSize size;
+ for (QLayoutItem *item : itemList)
+ size = size.expandedTo(item->minimumSize());
+
+ int left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+ size += QSize(left + right, top + bottom);
+ return size;
+ }
+
+ void setGeometry(const QRect &rect) override
+ {
+ QLayout::setGeometry(rect);
+ doLayout(rect, false);
+ }
+
+ QSize sizeHint() const override
+ {
+ return minimumSize();
+ }
+
+ QLayoutItem *takeAt(int index) override
+ {
+ if (index >= 0 && index < itemList.size())
+ return itemList.takeAt(index);
+ else
+ return nullptr;
+ }
+
+private:
+ int doLayout(const QRect &rect, bool testOnly) const
+ {
+ int left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+ QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
+ int x = effectiveRect.x();
+ int y = effectiveRect.y();
+ int lineHeight = 0;
+
+ for (QLayoutItem *item : itemList) {
+ QWidget *wid = item->widget();
+ int spaceX = horizontalSpacing();
+ if (spaceX == -1)
+ spaceX = wid->style()->layoutSpacing(
+ QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
+ int spaceY = verticalSpacing();
+ if (spaceY == -1)
+ spaceY = wid->style()->layoutSpacing(
+ QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
+ int nextX = x + item->sizeHint().width() + spaceX;
+ if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
+ x = effectiveRect.x();
+ y = y + lineHeight + spaceY;
+ nextX = x + item->sizeHint().width() + spaceX;
+ lineHeight = 0;
+ }
+
+ if (!testOnly)
+ item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
+
+ x = nextX;
+ lineHeight = qMax(lineHeight, item->sizeHint().height());
+ }
+ return y + lineHeight - rect.y() + bottom;
+ }
+
+ int smartSpacing(QStyle::PixelMetric pm) const
+ {
+ QObject *parent = this->parent();
+ if (!parent) {
+ return -1;
+ } else if (parent->isWidgetType()) {
+ auto pw = static_cast<QWidget *>(parent);
+ return pw->style()->pixelMetric(pm, nullptr, pw);
+ } else {
+ return static_cast<QLayout *>(parent)->spacing();
+ }
+ }
+
+ QList<QLayoutItem *> itemList;
+ int m_hSpace;
+ int m_vSpace;
+};
+
+/*!
+ \namespace Layouting
+ \inmodule QtCreator
+
+ \brief The Layouting namespace contains classes and functions to conveniently
+ create layouts in code.
+
+ Classes in the namespace help to create create QLayout or QWidget derived class,
+ instances should be used locally within a function and never stored.
+
+ \sa Layouting::Widget, Layouting::Layout
+*/
+
+
+/*!
+ \class Layouting::Layout
+ \inmodule QtCreator
+
+ The Layout class is a base class for more specific builder
+ classes to create QLayout derived objects.
+ */
+
+/*!
+ \class Layouting::Widget
+ \inmodule QtCreator
+
+ The Widget class is a base class for more specific builder
+ classes to create QWidget derived objects.
+*/
+
+/*!
+ \class Layouting::LayoutItem
+ \inmodule QtCreator
+
+ The LayoutItem class is used for intermediate results
+ while creating layouts with a concept of rows and spans, such
+ as Form and Grid.
+*/
+
+Layout::LayoutItem::LayoutItem() = default;
+
+Layout::LayoutItem::~LayoutItem() = default;
+
+Layout::LayoutItem::LayoutItem(const LayoutModifier &inner)
+{
+ ownerModifier = inner;
+}
+
+/*!
+ \fn template <class T> LayoutItem(const T &t)
+ \internal
+
+ Constructs a layout item proxy for \a t.
+
+ T could be
+ \list
+ \li \c {QString}
+ \li \c {QWidget *}
+ \li \c {QLayout *}
+ \endlist
+*/
+
+// Object
+
+Object::Object(std::initializer_list<I> ps)
+{
+ ptr = new Implementation;
+ apply(this, ps);
+}
+
+static QWidget *widgetForItem(QLayoutItem *item)
+{
+ if (QWidget *w = item->widget())
+ return w;
+ if (item->spacerItem())
+ return nullptr;
+ if (QLayout *l = item->layout()) {
+ for (int i = 0, n = l->count(); i < n; ++i) {
+ if (QWidget *w = widgetForItem(l->itemAt(i)))
+ return w;
+ }
+ }
+ return nullptr;
+}
+
+static QLabel *createLabel(const QString &text)
+{
+ auto label = new QLabel(text);
+ label->setTextInteractionFlags(Qt::TextSelectableByMouse);
+ return label;
+}
+
+using LayoutItem = Layout::LayoutItem;
+
+static void addItemToBoxLayout(QBoxLayout *layout, const LayoutItem &item)
+{
+ if (QWidget *w = item.widget) {
+ layout->addWidget(w);
+ } else if (QLayout *l = item.layout) {
+ layout->addLayout(l);
+ } else if (item.stretch != -1) {
+ layout->addStretch(item.stretch);
+ } else if (!item.text.isEmpty()) {
+ layout->addWidget(createLabel(item.text));
+ } else if (item.empty) {
+ // Nothing to do, but no reason to warn, either.
+ } else {
+ QTC_CHECK(false);
+ }
+}
+
+static void addItemToFlowLayout(FlowLayout *layout, const LayoutItem &item)
+{
+ if (QWidget *w = item.widget) {
+ layout->addWidget(w);
+ } else if (QLayout *l = item.layout) {
+ layout->addItem(l);
+// } else if (item.stretch != -1) {
+// layout->addStretch(item.stretch);
+ } else if (item.empty) {
+ // Nothing to do, but no reason to warn, either
+ } else if (!item.text.isEmpty()) {
+ layout->addWidget(createLabel(item.text));
+ } else {
+ QTC_CHECK(false);
+ }
+}
+
+/*!
+ \class Layouting::Space
+ \inmodule QtCreator
+
+ \brief The Space class represents some empty space in a layout.
+ */
+
+/*!
+ \class Layouting::Stretch
+ \inmodule QtCreator
+
+ \brief The Stretch class represents some stretch in a layout.
+ */
+
+
+// Layout
+
+void Layout::span(int cols, int rows)
+{
+ QTC_ASSERT(!pendingItems.empty(), return);
+ pendingItems.back().spanCols = cols;
+ pendingItems.back().spanRows = rows;
+}
+
+void Layout::noMargin()
+{
+ customMargin({});
+}
+
+void Layout::normalMargin()
+{
+ customMargin({9, 9, 9, 9});
+}
+
+void Layout::customMargin(const QMargins &margin)
+{
+ access(this)->setContentsMargins(margin);
+}
+
+/*!
+ Attaches the constructed layout to the provided QWidget \a w.
+
+ This operation can only be performed once per LayoutBuilder instance.
+ */
+void Layout::attachTo(QWidget *widget)
+{
+ flush();
+ widget->setLayout(access(this));
+}
+
+/*!
+ Adds the layout item \a item as sub items.
+ */
+void Layout::addItem(I item)
+{
+ item.apply(this);
+}
+
+void Layout::addItemHelper(const LayoutItem &item)
+{
+ if (QBoxLayout *lt = asBox())
+ addItemToBoxLayout(lt, item);
+ else if (FlowLayout *lt = asFlow())
+ addItemToFlowLayout(lt, item);
+ else
+ pendingItems.push_back(item);
+}
+
+/*!
+ Adds the layout items \a items as sub items.
+ */
+void Layout::addItems(std::initializer_list<I> items)
+{
+ for (const I &item : items)
+ item.apply(this);
+}
+
+/*!
+ Starts a new row containing \a items. The row can be further extended by
+ other items using \c addItem() or \c addItems().
+
+ \sa addItem(), addItems()
+ */
+
+void Layout::addRow(std::initializer_list<I> items)
+{
+ for (const I &item : items)
+ item.apply(this);
+ flush();
+}
+
+void Layout::setSpacing(int spacing)
+{
+ access(this)->setSpacing(spacing);
+}
+
+void Layout::setColumnStretch(int column, int stretch)
+{
+ if (auto grid = qobject_cast<QGridLayout *>(access(this))) {
+ grid->setColumnStretch(column, stretch);
+ } else {
+ QTC_CHECK(false);
+ }
+}
+
+void addToWidget(Widget *widget, const Layout &layout)
+{
+ layout.flush_();
+ access(widget)->setLayout(access(&layout));
+}
+
+void addToLayout(Layout *layout, const Widget &inner)
+{
+ LayoutItem item;
+ item.widget = access(&inner);
+ layout->addItemHelper(item);
+}
+
+void addToLayout(Layout *layout, QWidget *inner)
+{
+ LayoutItem item;
+ item.widget = inner;
+ layout->addItemHelper(item);
+}
+
+void addToLayout(Layout *layout, QLayout *inner)
+{
+ LayoutItem item;
+ item.layout = inner;
+ layout->addItemHelper(item);
+}
+
+void addToLayout(Layout *layout, const Layout &inner)
+{
+ inner.flush_();
+ LayoutItem item;
+ item.layout = access(&inner);
+ layout->addItemHelper(item);
+}
+
+void addToLayout(Layout *layout, const LayoutModifier &inner)
+{
+ inner(layout);
+}
+
+void addToLayout(Layout *layout, const QString &inner)
+{
+ LayoutItem item;
+ item.text = inner;
+ layout->addItemHelper(item);
+}
+
+void empty(Layout *iface)
+{
+ LayoutItem item;
+ item.empty = true;
+ iface->addItemHelper(item);
+}
+
+void hr(Layout *layout)
+{
+ layout->addItemHelper(createHr());
+}
+
+void br(Layout *iface)
+{
+ iface->flush();
+}
+
+void st(Layout *iface)
+{
+ LayoutItem item;
+ item.stretch = 1;
+ iface->addItemHelper(item);
+}
+
+void noMargin(Layout *iface)
+{
+ iface->noMargin();
+}
+
+void normalMargin(Layout *iface)
+{
+ iface->normalMargin();
+}
+
+QFormLayout *Layout::asForm()
+{
+ return qobject_cast<QFormLayout *>(access(this));
+}
+
+QGridLayout *Layout::asGrid()
+{
+ return qobject_cast<QGridLayout *>(access(this));
+}
+
+QBoxLayout *Layout::asBox()
+{
+ return qobject_cast<QBoxLayout *>(access(this));
+}
+
+FlowLayout *Layout::asFlow()
+{
+ return dynamic_cast<FlowLayout *>(access(this));
+}
+
+void Layout::flush()
+{
+ if (pendingItems.empty())
+ return;
+
+ if (QGridLayout *lt = asGrid()) {
+ for (const LayoutItem &item : std::as_const(pendingItems)) {
+ Qt::Alignment a;
+ if (currentGridColumn == 0 && useFormAlignment) {
+ // if (auto widget = builder.stack.at(builder.stack.size() - 2).widget) {
+ // a = widget->style()->styleHint(QStyle::SH_FormLayoutLabelAlignment);
+ }
+ if (item.widget)
+ lt->addWidget(item.widget, currentGridRow, currentGridColumn, item.spanRows, item.spanCols, a);
+ else if (item.layout)
+ lt->addLayout(item.layout, currentGridRow, currentGridColumn, item.spanRows, item.spanCols, a);
+ else if (!item.text.isEmpty())
+ lt->addWidget(createLabel(item.text), currentGridRow, currentGridColumn, item.spanRows, item.spanCols, a);
+ currentGridColumn += item.spanCols;
+ // Intentionally not used, use 'br'/'empty' for vertical progress.
+ // currentGridRow += item.spanRows;
+ }
+ ++currentGridRow;
+ currentGridColumn = 0;
+ pendingItems.clear();
+ return;
+ }
+
+ if (QFormLayout *fl = asForm()) {
+ if (pendingItems.size() > 2) {
+ auto hbox = new QHBoxLayout;
+ hbox->setContentsMargins(0, 0, 0, 0);
+ for (size_t i = 1; i < pendingItems.size(); ++i)
+ addItemToBoxLayout(hbox, pendingItems.at(i));
+ while (pendingItems.size() > 1)
+ pendingItems.pop_back();
+ pendingItems.push_back(hbox);
+ }
+
+ if (pendingItems.size() == 1) { // Only one item given, so this spans both columns.
+ const LayoutItem &f0 = pendingItems.at(0);
+ if (auto layout = f0.layout)
+ fl->addRow(layout);
+ else if (auto widget = f0.widget)
+ fl->addRow(widget);
+ } else if (pendingItems.size() == 2) { // Normal case, both columns used.
+ LayoutItem &f1 = pendingItems[1];
+ const LayoutItem &f0 = pendingItems.at(0);
+ if (!f1.widget && !f1.layout && !f1.text.isEmpty())
+ f1.widget = createLabel(f1.text);
+
+ // QFormLayout accepts only widgets or text in the first column.
+ // FIXME: Should we be more generous?
+ if (f0.widget) {
+ if (f1.layout)
+ fl->addRow(f0.widget, f1.layout);
+ else if (f1.widget)
+ fl->addRow(f0.widget, f1.widget);
+ } else {
+ if (f1.layout)
+ fl->addRow(createLabel(f0.text), f1.layout);
+ else if (f1.widget)
+ fl->addRow(createLabel(f0.text), f1.widget);
+ }
+ } else {
+ QTC_CHECK(false);
+ }
+
+ // Set up label as buddy if possible.
+ const int lastRow = fl->rowCount() - 1;
+ QLayoutItem *l = fl->itemAt(lastRow, QFormLayout::LabelRole);
+ QLayoutItem *f = fl->itemAt(lastRow, QFormLayout::FieldRole);
+ if (l && f) {
+ if (QLabel *label = qobject_cast<QLabel *>(l->widget())) {
+ if (QWidget *widget = widgetForItem(f))
+ label->setBuddy(widget);
+ }
+ }
+
+ pendingItems.clear();
+ return;
+ }
+
+ QTC_CHECK(false); // The other layouts shouldn't use flush()
+}
+
+void Layout::flush_() const
+{
+ const_cast<Layout *>(this)->flush();
+}
+
+void withFormAlignment(Layout *iface)
+{
+ iface->useFormAlignment = true;
+}
+
+// Flow
+
+Flow::Flow(std::initializer_list<I> ps)
+{
+ ptr = new FlowLayout;
+ apply(this, ps);
+ flush();
+}
+
+// Row & Column
+
+Row::Row(std::initializer_list<I> ps)
+{
+ ptr = new QHBoxLayout;
+ apply(this, ps);
+ flush();
+}
+
+Column::Column(std::initializer_list<I> ps)
+{
+ ptr = new QVBoxLayout;
+ apply(this, ps);
+ flush();
+}
+
+// Grid
+
+Grid::Grid()
+{
+ ptr = new QGridLayout;
+}
+
+Grid::Grid(std::initializer_list<I> ps)
+{
+ ptr = new QGridLayout;
+ apply(this, ps);
+ flush();
+}
+
+// Form
+
+Form::Form()
+{
+ ptr = new QFormLayout;
+}
+
+Form::Form(std::initializer_list<I> ps)
+{
+ auto lt = new QFormLayout;
+ ptr = lt;
+ lt->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
+ apply(this, ps);
+ flush();
+}
+
+void Layout::fieldGrowthPolicy(int policy)
+{
+ if (auto lt = asForm())
+ lt->setFieldGrowthPolicy(QFormLayout::FieldGrowthPolicy(policy));
+}
+
+QWidget *Layout::emerge() const
+{
+ const_cast<Layout *>(this)->flush();
+ QWidget *widget = new QWidget;
+ widget->setLayout(access(this));
+ return widget;
+}
+
+// "Widgets"
+
+Widget::Widget(std::initializer_list<I> ps)
+{
+ ptr = new Implementation;
+ apply(this, ps);
+}
+
+void Widget::resize(int w, int h)
+{
+ access(this)->resize(w, h);
+}
+
+void Widget::setLayout(const Layout &layout)
+{
+ access(this)->setLayout(access(&layout));
+}
+
+void Widget::setWindowTitle(const QString &title)
+{
+ access(this)->setWindowTitle(title);
+}
+
+void Widget::setToolTip(const QString &title)
+{
+ access(this)->setToolTip(title);
+}
+
+void Widget::show()
+{
+ access(this)->show();
+}
+
+void Widget::noMargin(int)
+{
+ customMargin({});
+}
+
+void Widget::normalMargin(int)
+{
+ customMargin({9, 9, 9, 9});
+}
+
+void Widget::customMargin(const QMargins &margin)
+{
+ access(this)->setContentsMargins(margin);
+}
+
+QWidget *Widget::emerge() const
+{
+ return access(this);
+}
+
+// Label
+
+Label::Label(std::initializer_list<I> ps)
+{
+ ptr = new Implementation;
+ apply(this, ps);
+}
+
+Label::Label(const QString &text)
+{
+ ptr = new Implementation;
+ setText(text);
+}
+
+void Label::setText(const QString &text)
+{
+ access(this)->setText(text);
+}
+
+// Group
+
+Group::Group(std::initializer_list<I> ps)
+{
+ ptr = new Implementation;
+ apply(this, ps);
+}
+
+void Group::setTitle(const QString &title)
+{
+ access(this)->setTitle(title);
+ access(this)->setObjectName(title);
+}
+
+void Group::setGroupChecker(const std::function<void (QObject *)> &checker)
+{
+ checker(access(this));
+}
+
+// SpinBox
+
+SpinBox::SpinBox(std::initializer_list<I> ps)
+{
+ ptr = new Implementation;
+ apply(this, ps);
+}
+
+void SpinBox::setValue(int val)
+{
+ access(this)->setValue(val);
+}
+
+void SpinBox::onTextChanged(const std::function<void (QString)> &func)
+{
+ QObject::connect(access(this), &QSpinBox::textChanged, func);
+}
+
+// TextEdit
+
+TextEdit::TextEdit(std::initializer_list<I> ps)
+{
+ ptr = new Implementation;
+ apply(this, ps);
+}
+
+void TextEdit::setText(const QString &text)
+{
+ access(this)->setText(text);
+}
+
+// PushButton
+
+PushButton::PushButton(std::initializer_list<I> ps)
+{
+ ptr = new Implementation;
+ apply(this, ps);
+}
+
+void PushButton::setText(const QString &text)
+{
+ access(this)->setText(text);
+}
+
+void PushButton::onClicked(const std::function<void ()> &func, QObject *guard)
+{
+ QObject::connect(access(this), &QAbstractButton::clicked, guard, func);
+}
+
+// Stack
+
+// We use a QStackedWidget instead of a QStackedLayout here because the latter will call
+// "setVisible()" when a child is added, which can lead to the widget being spawned as a
+// top-level widget. This can lead to the focus shifting away from the main application.
+Stack::Stack(std::initializer_list<I> ps)
+{
+ ptr = new Implementation;
+ apply(this, ps);
+}
+
+void addToStack(Stack *stack, const Widget &inner)
+{
+ access(stack)->addWidget(inner.emerge());
+}
+
+void addToStack(Stack *stack, const Layout &inner)
+{
+ inner.flush_();
+ access(stack)->addWidget(inner.emerge());
+}
+
+void addToStack(Stack *stack, QWidget *inner)
+{
+ access(stack)->addWidget(inner);
+}
+
+// Splitter
+
+Splitter::Splitter(std::initializer_list<I> ps)
+{
+ ptr = new Implementation;
+ access(this)->setOrientation(Qt::Vertical);
+ apply(this, ps);
+}
+
+void addToSplitter(Splitter *splitter, QWidget *inner)
+{
+ access(splitter)->addWidget(inner);
+}
+
+void addToSplitter(Splitter *splitter, const Widget &inner)
+{
+ access(splitter)->addWidget(inner.emerge());
+}
+
+void addToSplitter(Splitter *splitter, const Layout &inner)
+{
+ inner.flush_();
+ access(splitter)->addWidget(inner.emerge());
+}
+
+// ToolBar
+
+ToolBar::ToolBar(std::initializer_list<I> ps)
+{
+ ptr = new Implementation;
+ apply(this, ps);
+ access(this)->setOrientation(Qt::Horizontal);
+}
+
+// TabWidget
+
+TabWidget::TabWidget(std::initializer_list<I> ps)
+{
+ ptr = new Implementation;
+ apply(this, ps);
+}
+
+Tab::Tab(const QString &tabName, const Layout &inner)
+ : tabName(tabName), inner(inner)
+{}
+
+void addToTabWidget(TabWidget *tabWidget, const Tab &tab)
+{
+ access(tabWidget)->addTab(tab.inner.emerge(), tab.tabName);
+}
+
+// Special If
+
+If::If(bool condition,
+ const std::initializer_list<Layout::I> ifcase,
+ const std::initializer_list<Layout::I> thencase)
+ : used(condition ? ifcase : thencase)
+{}
+
+void addToLayout(Layout *layout, const If &inner)
+{
+ for (const Layout::I &item : inner.used)
+ item.apply(layout);
+}
+
+// Specials
+
+QWidget *createHr(QWidget *parent)
+{
+ auto frame = new QFrame(parent);
+ frame->setFrameShape(QFrame::HLine);
+ frame->setFrameShadow(QFrame::Sunken);
+ return frame;
+}
+
+Span::Span(int n, const Layout::I &item)
+ : item(item), spanCols(n)
+{}
+
+void addToLayout(Layout *layout, const Span &inner)
+{
+ LayoutItem item;
+ layout->addItem(inner.item);
+ QTC_ASSERT(!layout->pendingItems.empty(), return);
+ layout->pendingItems.back().spanCols = inner.spanCols;
+ layout->pendingItems.back().spanRows = inner.spanRows;
+}
+
+LayoutModifier spacing(int space)
+{
+ return [space](Layout *iface) { iface->setSpacing(space); };
+}
+
+void addToLayout(Layout *layout, const Space &inner)
+{
+ if (auto lt = layout->asBox())
+ lt->addSpacing(inner.space);
+}
+
+void addToLayout(Layout *layout, const Stretch &inner)
+{
+ if (auto lt = layout->asBox())
+ lt->addStretch(inner.stretch);
+}
+
+// void createItem(LayoutItem *item, QWidget *t)
+// {
+// if (auto l = qobject_cast<QLabel *>(t))
+// l->setTextInteractionFlags(l->textInteractionFlags() | Qt::TextSelectableByMouse);
+
+// item->onAdd = [t](LayoutBuilder &builder) { doAddWidget(builder, t); };
+// }
+
+
+} // Layouting
diff --git a/tests/manual/layoutbuilder/v2/lb.h b/tests/manual/layoutbuilder/v2/lb.h
new file mode 100644
index 0000000000..99c87af564
--- /dev/null
+++ b/tests/manual/layoutbuilder/v2/lb.h
@@ -0,0 +1,553 @@
+// Copyright (C) 2023 André Pönitz
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#pragma once
+
+#include <QMargins>
+#include <QString>
+
+#include <functional>
+#include <initializer_list>
+
+#if defined(UTILS_LIBRARY)
+# define QTCREATOR_UTILS_EXPORT Q_DECL_EXPORT
+#elif defined(UTILS_STATIC_LIBRARY)
+# define QTCREATOR_UTILS_EXPORT
+#else
+# define QTCREATOR_UTILS_EXPORT Q_DECL_IMPORT
+#endif
+
+QT_BEGIN_NAMESPACE
+class QBoxLayout;
+class QFormLayout;
+class QGridLayout;
+class QGroupBox;
+class QHBoxLayout;
+class QLabel;
+class QLayout;
+class QMargins;
+class QObject;
+class QPushButton;
+class QSpinBox;
+class QSplitter;
+class QStackedWidget;
+class QTabWidget;
+class QTextEdit;
+class QToolBar;
+class QVBoxLayout;
+class QWidget;
+QT_END_NAMESPACE
+
+namespace Layouting {
+
+class NestId {};
+
+template <typename T1, typename T2>
+class IdAndArg
+{
+public:
+ IdAndArg(const T1 &id, const T2 &arg) : id(id), arg(arg) {}
+ const T1 id;
+ const T2 arg; // FIXME: Could be const &, but this would currently break bindTo().
+};
+
+// The main dispatcher
+
+void doit(auto x, auto id, auto p);
+
+template <typename X> class BuilderItem
+{
+public:
+ // Nested child object
+ template <typename Inner>
+ BuilderItem(Inner && p)
+ {
+ apply = [&p](X *x) { doit(x, NestId{}, std::forward<Inner>(p)); };
+ }
+
+ // Property setter
+ template <typename Id, typename Arg>
+ BuilderItem(IdAndArg<Id, Arg> && idarg)
+ {
+ apply = [&idarg](X *x) { doit(x, idarg.id, idarg.arg); };
+ }
+
+ std::function<void(X *)> apply;
+};
+
+
+//////////////////////////////////////////////
+
+//
+// Basic
+//
+
+class QTCREATOR_UTILS_EXPORT Thing
+{
+public:
+ void *ptr; // The product.
+};
+
+class QTCREATOR_UTILS_EXPORT Object : public Thing
+{
+public:
+ using Implementation = QObject;
+ using I = BuilderItem<Object>;
+
+ Object() = default;
+ Object(std::initializer_list<I> ps);
+};
+
+//
+// Layouts
+//
+
+class FlowLayout;
+class Layout;
+using LayoutModifier = std::function<void(Layout *)>;
+// using LayoutModifier = void(*)(Layout *);
+
+class QTCREATOR_UTILS_EXPORT Layout : public Object
+{
+public:
+ using Implementation = QLayout;
+ using I = BuilderItem<Layout>;
+
+ Layout() = default;
+ Layout(Implementation *w) { ptr = w; }
+
+ class LayoutItem
+ {
+ public:
+ ~LayoutItem();
+ LayoutItem();
+ LayoutItem(QLayout *l) : layout(l) {}
+ LayoutItem(QWidget *w) : widget(w) {}
+ LayoutItem(const QString &t) : text(t) {}
+ LayoutItem(const LayoutModifier &inner);
+
+ QString text;
+ QLayout *layout = nullptr;
+ QWidget *widget = nullptr;
+ int stretch = -1;
+ int spanCols = 1;
+ int spanRows = 1;
+ bool empty = false;
+ LayoutModifier ownerModifier;
+ //Qt::Alignment align = {};
+ };
+
+ void span(int cols, int rows);
+ void noMargin();
+ void normalMargin();
+ void customMargin(const QMargins &margin);
+ void setColumnStretch(int cols, int rows);
+ void setSpacing(int space);
+
+ void attachTo(QWidget *);
+ void addItemHelper(const LayoutItem &item);
+ void addItem(I item);
+ void addItems(std::initializer_list<I> items);
+ void addRow(std::initializer_list<I> items);
+
+ void flush();
+ void flush_() const;
+ void fieldGrowthPolicy(int policy);
+
+ QWidget *emerge() const;
+
+ QFormLayout *asForm();
+ QGridLayout *asGrid();
+ QBoxLayout *asBox();
+ FlowLayout *asFlow();
+
+ // Grid-only
+ int currentGridColumn = 0;
+ int currentGridRow = 0;
+ //Qt::Alignment align = {};
+ bool useFormAlignment = false;
+
+ std::vector<LayoutItem> pendingItems;
+};
+
+class QTCREATOR_UTILS_EXPORT Column : public Layout
+{
+public:
+ using Implementation = QVBoxLayout;
+ using I = BuilderItem<Column>;
+
+ Column(std::initializer_list<I> ps);
+};
+
+class QTCREATOR_UTILS_EXPORT Row : public Layout
+{
+public:
+ using Implementation = QHBoxLayout;
+ using I = BuilderItem<Row>;
+
+ Row(std::initializer_list<I> ps);
+};
+
+class QTCREATOR_UTILS_EXPORT Form : public Layout
+{
+public:
+ using Implementation = QFormLayout;
+ using I = BuilderItem<Form>;
+
+ Form();
+ Form(std::initializer_list<I> ps);
+};
+
+class QTCREATOR_UTILS_EXPORT Grid : public Layout
+{
+public:
+ using Implementation = QGridLayout;
+ using I = BuilderItem<Grid>;
+
+ Grid();
+ Grid(std::initializer_list<I> ps);
+};
+
+class QTCREATOR_UTILS_EXPORT Flow : public Layout
+{
+public:
+ Flow(std::initializer_list<I> ps);
+};
+
+class QTCREATOR_UTILS_EXPORT Stretch
+{
+public:
+ explicit Stretch(int stretch) : stretch(stretch) {}
+
+ int stretch;
+};
+
+class QTCREATOR_UTILS_EXPORT Space
+{
+public:
+ explicit Space(int space) : space(space) {}
+
+ int space;
+};
+
+class QTCREATOR_UTILS_EXPORT Span
+{
+public:
+ Span(int n, const Layout::I &item);
+
+ Layout::I item;
+ int spanCols = 1;
+ int spanRows = 1;
+};
+
+//
+// Widgets
+//
+
+class QTCREATOR_UTILS_EXPORT Widget : public Object
+{
+public:
+ using Implementation = QWidget;
+ using I = BuilderItem<Widget>;
+
+ Widget() = default;
+ Widget(std::initializer_list<I> ps);
+ Widget(Implementation *w) { ptr = w; }
+
+ QWidget *emerge() const;
+
+ void show();
+ void resize(int, int);
+ void setLayout(const Layout &layout);
+ void setWindowTitle(const QString &);
+ void setToolTip(const QString &);
+ void noMargin(int = 0);
+ void normalMargin(int = 0);
+ void customMargin(const QMargins &margin);
+};
+
+class QTCREATOR_UTILS_EXPORT Label : public Widget
+{
+public:
+ using Implementation = QLabel;
+ using I = BuilderItem<Label>;
+
+ Label(std::initializer_list<I> ps);
+ Label(const QString &text);
+
+ void setText(const QString &);
+};
+
+class QTCREATOR_UTILS_EXPORT Group : public Widget
+{
+public:
+ using Implementation = QGroupBox;
+ using I = BuilderItem<Group>;
+
+ Group(std::initializer_list<I> ps);
+
+ void setTitle(const QString &);
+ void setGroupChecker(const std::function<void(QObject *)> &);
+};
+
+class QTCREATOR_UTILS_EXPORT SpinBox : public Widget
+{
+public:
+ using Implementation = QSpinBox;
+ using I = BuilderItem<SpinBox>;
+
+ SpinBox(std::initializer_list<I> ps);
+
+ void setValue(int);
+ void onTextChanged(const std::function<void(QString)> &);
+};
+
+class QTCREATOR_UTILS_EXPORT PushButton : public Widget
+{
+public:
+ using Implementation = QPushButton;
+ using I = BuilderItem<PushButton>;
+
+ PushButton(std::initializer_list<I> ps);
+
+ void setText(const QString &);
+ void onClicked(const std::function<void()> &, QObject *guard);
+};
+
+class QTCREATOR_UTILS_EXPORT TextEdit : public Widget
+{
+public:
+ using Implementation = QTextEdit;
+ using I = BuilderItem<TextEdit>;
+ using Id = Implementation *;
+
+ TextEdit(std::initializer_list<I> ps);
+
+ void setText(const QString &);
+};
+
+class QTCREATOR_UTILS_EXPORT Splitter : public Widget
+{
+public:
+ using Implementation = QSplitter;
+ using I = BuilderItem<Splitter>;
+
+ Splitter(std::initializer_list<I> items);
+};
+
+class QTCREATOR_UTILS_EXPORT Stack : public Widget
+{
+public:
+ using Implementation = QStackedWidget;
+ using I = BuilderItem<Stack>;
+
+ Stack() : Stack({}) {}
+ Stack(std::initializer_list<I> items);
+};
+
+class QTCREATOR_UTILS_EXPORT Tab : public Widget
+{
+public:
+ using Implementation = QWidget;
+
+ Tab(const QString &tabName, const Layout &inner);
+
+ const QString tabName;
+ const Layout inner;
+};
+
+class QTCREATOR_UTILS_EXPORT TabWidget : public Widget
+{
+public:
+ using Implementation = QTabWidget;
+ using I = BuilderItem<TabWidget>;
+
+ TabWidget(std::initializer_list<I> items);
+};
+
+class QTCREATOR_UTILS_EXPORT ToolBar : public Widget
+{
+public:
+ using Implementation = QToolBar;
+ using I = Layouting::BuilderItem<ToolBar>;
+
+ ToolBar(std::initializer_list<I> items);
+};
+
+// Special
+
+class QTCREATOR_UTILS_EXPORT If
+{
+public:
+ If(bool condition,
+ const std::initializer_list<Layout::I> ifcase,
+ const std::initializer_list<Layout::I> thencase = {});
+
+ const std::initializer_list<Layout::I> used;
+};
+
+//
+// Dispatchers
+//
+
+// We need one 'Id' (and a corresponding function wrapping arguments into a
+// tuple marked by this id) per 'name' of "backend" setter member function,
+// i.e. one 'text' is sufficient for QLabel::setText, QLineEdit::setText.
+// The name of the Id does not have to match the backend names as it
+// is mapped per-backend-type in the respective setter implementation
+// but we assume that it generally makes sense to stay close to the
+// wrapped API name-wise.
+
+// These are free functions overloaded on the type of builder object
+// and setter id. The function implementations are independent, but
+// the base expectation is that they will forwards to the backend
+// type's setter.
+
+// Special dispatchers
+
+
+class BindToId {};
+
+template <typename T>
+auto bindTo(T **p)
+{
+ return IdAndArg{BindToId{}, p};
+}
+
+template <typename Interface>
+void doit(Interface *x, BindToId, auto p)
+{
+ *p = static_cast<Interface::Implementation *>(x->ptr);
+}
+
+class IdId {};
+auto id(auto p) { return IdAndArg{IdId{}, p}; }
+
+template <typename Interface>
+void doit(Interface *x, IdId, auto p)
+{
+ *p = static_cast<Interface::Implementation *>(x->ptr);
+}
+
+// Setter dispatchers
+
+class SizeId {};
+auto size(auto w, auto h) { return IdAndArg{SizeId{}, std::pair{w, h}}; }
+void doit(auto x, SizeId, auto p) { x->resize(p.first, p.second); }
+
+class TextId {};
+auto text(auto p) { return IdAndArg{TextId{}, p}; }
+void doit(auto x, TextId, auto p) { x->setText(p); }
+
+class TitleId {};
+auto title(auto p) { return IdAndArg{TitleId{}, p}; }
+void doit(auto x, TitleId, auto p) { x->setTitle(p); }
+
+class GroupCheckerId {};
+auto groupChecker(auto p) { return IdAndArg{GroupCheckerId{}, p}; }
+void doit(auto x, GroupCheckerId, auto p) { x->setGroupChecker(p); }
+
+class ToolTipId {};
+auto toolTip(auto p) { return IdAndArg{ToolTipId{}, p}; }
+void doit(auto x, ToolTipId, auto p) { x->setToolTip(p); }
+
+class WindowTitleId {};
+auto windowTitle(auto p) { return IdAndArg{WindowTitleId{}, p}; }
+void doit(auto x, WindowTitleId, auto p) { x->setWindowTitle(p); }
+
+class OnTextChangedId {};
+auto onTextChanged(auto p) { return IdAndArg{OnTextChangedId{}, p}; }
+void doit(auto x, OnTextChangedId, auto p) { x->onTextChanged(p); }
+
+class OnClickedId {};
+auto onClicked(auto p, auto guard) { return IdAndArg{OnClickedId{}, std::pair{p, guard}}; }
+void doit(auto x, OnClickedId, auto p) { x->onClicked(p.first, p.second); }
+
+class CustomMarginId {};
+inline auto customMargin(const QMargins &p) { return IdAndArg{CustomMarginId{}, p}; }
+void doit(auto x, CustomMarginId, auto p) { x->customMargin(p); }
+
+class FieldGrowthPolicyId {};
+inline auto fieldGrowthPolicy(auto p) { return IdAndArg{FieldGrowthPolicyId{}, p}; }
+void doit(auto x, FieldGrowthPolicyId, auto p) { x->fieldGrowthPolicy(p); }
+
+class ColumnStretchId {};
+inline auto columnStretch(int column, int stretch) { return IdAndArg{ColumnStretchId{}, std::pair{column, stretch}}; }
+void doit(auto x, ColumnStretchId, auto p) { x->setColumnStretch(p.first, p.second); }
+
+// Nesting dispatchers
+
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Layout &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Widget &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, QWidget *inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, QLayout *inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const LayoutModifier &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const QString &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Space &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Stretch &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const If &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Span &inner);
+// ... can be added to anywhere later to support "user types"
+
+QTCREATOR_UTILS_EXPORT void addToWidget(Widget *widget, const Layout &layout);
+
+QTCREATOR_UTILS_EXPORT void addToTabWidget(TabWidget *tabWidget, const Tab &inner);
+
+QTCREATOR_UTILS_EXPORT void addToSplitter(Splitter *splitter, QWidget *inner);
+QTCREATOR_UTILS_EXPORT void addToSplitter(Splitter *splitter, const Widget &inner);
+QTCREATOR_UTILS_EXPORT void addToSplitter(Splitter *splitter, const Layout &inner);
+
+QTCREATOR_UTILS_EXPORT void addToStack(Stack *stack, QWidget *inner);
+QTCREATOR_UTILS_EXPORT void addToStack(Stack *stack, const Widget &inner);
+QTCREATOR_UTILS_EXPORT void addToStack(Stack *stack, const Layout &inner);
+
+template <class Inner>
+void doit_nested(Layout *outer, Inner && inner)
+{
+ addToLayout(outer, std::forward<Inner>(inner));
+}
+
+void doit_nested(Widget *outer, auto inner)
+{
+ addToWidget(outer, inner);
+}
+
+void doit_nested(TabWidget *outer, auto inner)
+{
+ addToTabWidget(outer, inner);
+}
+
+void doit_nested(Stack *outer, auto inner)
+{
+ addToStack(outer, inner);
+}
+
+void doit_nested(Splitter *outer, auto inner)
+{
+ addToSplitter(outer, inner);
+}
+
+template <class Inner>
+void doit(auto outer, NestId, Inner && inner)
+{
+ doit_nested(outer, std::forward<Inner>(inner));
+}
+
+// Special layout items
+
+QTCREATOR_UTILS_EXPORT void empty(Layout *);
+QTCREATOR_UTILS_EXPORT void br(Layout *);
+QTCREATOR_UTILS_EXPORT void st(Layout *);
+QTCREATOR_UTILS_EXPORT void noMargin(Layout *);
+QTCREATOR_UTILS_EXPORT void normalMargin(Layout *);
+QTCREATOR_UTILS_EXPORT void withFormAlignment(Layout *);
+QTCREATOR_UTILS_EXPORT void hr(Layout *);
+
+QTCREATOR_UTILS_EXPORT LayoutModifier spacing(int space);
+
+// Convenience
+
+QTCREATOR_UTILS_EXPORT QWidget *createHr(QWidget *parent = nullptr);
+
+} // Layouting
diff --git a/tests/manual/layoutbuilder/v2/main.cpp b/tests/manual/layoutbuilder/v2/main.cpp
new file mode 100644
index 0000000000..702d467016
--- /dev/null
+++ b/tests/manual/layoutbuilder/v2/main.cpp
@@ -0,0 +1,79 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "lb.h"
+
+#include <QApplication>
+#include <QWidget>
+#include <QLabel>
+#include <QGroupBox>
+#include <QTextEdit>
+
+using namespace Layouting;
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ TextEdit::Id textId;
+
+ QWidget *w = nullptr;
+ QGroupBox *g = nullptr;
+ QLabel *l = nullptr;
+
+ Group {
+ bindTo(&w), // Works, as GroupInterface derives from WidgetInterface
+ // bindTo(&l), // Does (intentionally) not work, GroupInterface does not derive from LabelInterface
+ bindTo(&g),
+ size(300, 200),
+ title("HHHHHHH"),
+ Form {
+ "Hallo",
+ Group {
+ title("Title"),
+ Column {
+ Label {
+ text("World")
+ },
+ TextEdit {
+ id(&textId),
+ text("Och noe")
+ }
+ }
+ },
+ br,
+ "Col",
+ Column {
+ Row { "1", "2", "3" },
+ Row { "3", "4", "6" }
+ },
+ br,
+ "Grid",
+ Grid {
+ Span { 2, QString("1111111") }, "3", br,
+ "3", "4", "6", br,
+ "4", empty, "6", br,
+ hr, "4", "6"
+ },
+ br,
+ Column {
+ Label {
+ text("Hi"),
+ size(30, 20)
+ },
+ Row {
+ SpinBox {
+ onTextChanged([&](const QString &text) { textId->setText(text); })
+ },
+ st,
+ PushButton {
+ text("Quit"),
+ onClicked(QApplication::quit, nullptr)
+ }
+ }
+ }
+ }
+ }.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/widgets/layoutbuilder/tst_manual_widgets_layoutbuilder.cpp b/tests/manual/widgets/layoutbuilder/tst_manual_widgets_layoutbuilder.cpp
index 343c500c28..eda6a565b1 100644
--- a/tests/manual/widgets/layoutbuilder/tst_manual_widgets_layoutbuilder.cpp
+++ b/tests/manual/widgets/layoutbuilder/tst_manual_widgets_layoutbuilder.cpp
@@ -24,9 +24,9 @@ int main(int argc, char *argv[])
};
Row {
- PushButton { text("-"), onClicked(minusClick) },
+ PushButton { text("-"), onClicked(minusClick, nullptr) },
lineEdit,
- PushButton { text("+"), onClicked(plusClick) },
+ PushButton { text("+"), onClicked(plusClick, nullptr) },
Group {
title("Splitter in Group"),
Column {
@@ -52,8 +52,8 @@ int main(int argc, char *argv[])
Splitter {
windowTitle("Splitter with sub layouts"),
- Column {"First Widget"},
- Row {"Second Widget"},
+ Column { QString("First Widget") },
+ Row { QString("Second Widget") },
}.emerge()->show();
return app.exec();
diff --git a/tests/manual/widgets/uifonts/tst_manual_widgets_uifonts.cpp b/tests/manual/widgets/uifonts/tst_manual_widgets_uifonts.cpp
index 4d324ec265..247bba6772 100644
--- a/tests/manual/widgets/uifonts/tst_manual_widgets_uifonts.cpp
+++ b/tests/manual/widgets/uifonts/tst_manual_widgets_uifonts.cpp
@@ -67,18 +67,20 @@ int main(int argc, char *argv[])
}
html.append("</table></body></html>");
- Column {
- windowTitle("Utils::StyleHelper::uiFont"),
- Group {
- title("As QFont in QLabel"),
- fontLabels,
- },
- Group {
- title("As inline CSS in HTML elements"),
- Row { html },
- },
- st,
- }.emerge()->show();
+ Widget {
+ windowTitle(QString("Utils::StyleHelper::uiFont")),
+ Column {
+ Group {
+ title(QString("As QFont in QLabel")),
+ fontLabels,
+ },
+ Group {
+ title(QString("As inline CSS in HTML elements")),
+ Row { html },
+ },
+ st,
+ }
+ }.show();
return app.exec();
}
diff --git a/tests/system/shared/project_explorer.py b/tests/system/shared/project_explorer.py
index 447b8057d5..f74e3a107e 100644
--- a/tests/system/shared/project_explorer.py
+++ b/tests/system/shared/project_explorer.py
@@ -24,6 +24,9 @@ def iterateConfiguredKits():
kitIndices = dumpIndices(treeView.model(), waitForObject(bAndRIndex))
configuredKitNames = map(lambda t: str(t.data(0)),
filter(__kitIsActivated__, kitIndices))
+ # Remove hide/show entries which are in tree but not kits
+ configuredKitNames = filter(lambda n: n != "Hide Inactive Kits" and n != "Show All Kits",
+ configuredKitNames)
return map(Targets.getIdForTargetName, configuredKitNames)
diff --git a/tests/system/shared/utils.py b/tests/system/shared/utils.py
index cb2f6875f3..221e4eeada 100644
--- a/tests/system/shared/utils.py
+++ b/tests/system/shared/utils.py
@@ -265,7 +265,6 @@ def selectFromFileDialog(fileName, waitForFile=False, ignoreFinalSnooze=False):
def addHelpDocumentation(which):
invokeMenuItem("Edit", "Preferences...")
mouseClick(waitForObjectItem(":Options_QListView", "Help"))
- waitForObject("{container=':Options.qt_tabwidget_tabbar_QTabBar' type='TabItem' text='Documentation'}")
clickOnTab(":Options.qt_tabwidget_tabbar_QTabBar", "Documentation")
# get rid of all docs already registered
listWidget = waitForObject("{type='QListView' name='docsListView' visible='1'}")
@@ -293,7 +292,6 @@ def addCurrentCreatorDocumentation():
return
invokeMenuItem("Edit", "Preferences...")
mouseClick(waitForObjectItem(":Options_QListView", "Help"))
- waitForObject("{container=':Options.qt_tabwidget_tabbar_QTabBar' type='TabItem' text='Documentation'}")
clickOnTab(":Options.qt_tabwidget_tabbar_QTabBar", "Documentation")
clickButton(waitForObject("{type='QPushButton' name='addButton' visible='1' text='Add...'}"))
selectFromFileDialog(docPath)
@@ -540,6 +538,7 @@ def clickOnTab(tabBarStr, tabText, timeout=5000):
test.log("Using workaround for Mac and Windows.")
setWindowState(tabBar, WindowState.Normal)
tabBar = waitForObject(tabBarStr, 2000)
+ waitForObject("{container='%s' type='TabItem' text='%s'}" % (tabBarStr, tabText))
clickTab(tabBar, tabText)
waitFor("str(tabBar.tabText(tabBar.currentIndex)) == '%s'" % tabText, timeout)
@@ -631,3 +630,13 @@ class GitClone:
def __exit__(self, exc_type, exc_value, traceback):
deleteDirIfExists(self.localPath)
+
+
+def setReloadBehavior(to):
+ # QC 14 changed the default, so change the preferences
+ invokeMenuItem("Edit", "Preferences...")
+ mouseClick(waitForObjectItem(":Options_QListView", "Environment"))
+ clickOnTab(":Options.qt_tabwidget_tabbar_QTabBar", "System")
+ selectFromCombo("{type='QComboBox' unnamed='1' leftWidget={type='QLabel' "
+ "text='When files are externally modified:'}}", to)
+ clickButton(":Options.OK_QPushButton")
diff --git a/tests/system/suite_editors/tst_clean_whitespaces/test.py b/tests/system/suite_editors/tst_clean_whitespaces/test.py
index 7803027f86..1f5eb7a255 100644
--- a/tests/system/suite_editors/tst_clean_whitespaces/test.py
+++ b/tests/system/suite_editors/tst_clean_whitespaces/test.py
@@ -111,8 +111,6 @@ def isIgnoredFile(fileName, ignoredFiles):
def ignoredFilesFromSettings():
invokeMenuItem("Edit", "Preferences...")
mouseClick(waitForObjectItem(":Options_QListView", "Text Editor"))
- waitForObject("{container=':Options.qt_tabwidget_tabbar_QTabBar' type='TabItem' "
- "text='Behavior'}")
clickOnTab(":Options.qt_tabwidget_tabbar_QTabBar", "Behavior")
cleanWhiteSpaceCB = "{type='QCheckBox' text='Skip clean whitespace for file types:'}"
ensureChecked(cleanWhiteSpaceCB)
diff --git a/tests/system/suite_editors/tst_delete_externally/test.py b/tests/system/suite_editors/tst_delete_externally/test.py
index e36377cb56..31583877c7 100644
--- a/tests/system/suite_editors/tst_delete_externally/test.py
+++ b/tests/system/suite_editors/tst_delete_externally/test.py
@@ -10,6 +10,9 @@ def main():
startQC()
if not startedWithoutPluginError():
return
+
+ setReloadBehavior("Always Ask")
+
for currentFile in files:
test.log("Opening file %s" % currentFile)
invokeMenuItem("File", "Open File or Project...")
diff --git a/tests/system/suite_editors/tst_edit_externally/test.py b/tests/system/suite_editors/tst_edit_externally/test.py
index e04ab19670..13cd68127f 100644
--- a/tests/system/suite_editors/tst_edit_externally/test.py
+++ b/tests/system/suite_editors/tst_edit_externally/test.py
@@ -23,6 +23,8 @@ def main():
if not startedWithoutPluginError():
return
+ setReloadBehavior("Always Ask")
+
mBox = ("{text?='The file * has been changed on disk. Do you want to reload it?' "
"type='QMessageBox' unnamed='1' visible='1'}")
popupText = ("<p>The file <i>%s</i> has been changed on disk. Do you want to reload it?</p>"
diff --git a/tests/system/suite_editors/tst_generic_highlighter/test.py b/tests/system/suite_editors/tst_generic_highlighter/test.py
index ea6c7aee40..13dda9bada 100644
--- a/tests/system/suite_editors/tst_generic_highlighter/test.py
+++ b/tests/system/suite_editors/tst_generic_highlighter/test.py
@@ -32,8 +32,6 @@ def getOrModifyFilePatternsFor(mimeType, filter='', toBePresent=None):
result = []
invokeMenuItem("Edit", "Preferences...")
mouseClick(waitForObjectItem(":Options_QListView", "Environment"))
- waitForObject("{container=':Options.qt_tabwidget_tabbar_QTabBar' type='TabItem' "
- "text='MIME Types'}")
clickOnTab(":Options.qt_tabwidget_tabbar_QTabBar", "MIME Types")
replaceEditorContent(waitForObject("{name='filterLineEdit' type='Utils::FancyLineEdit' "
"visible='1'}"), filter)
@@ -95,8 +93,6 @@ def addHighlighterDefinition(*languages):
test.log("Updating highlighter definitions...")
invokeMenuItem("Edit", "Preferences...")
mouseClick(waitForObjectItem(":Options_QListView", "Text Editor"))
- waitForObject("{container=':Options.qt_tabwidget_tabbar_QTabBar' type='TabItem' "
- "text='Generic Highlighter'}")
clickOnTab(":Options.qt_tabwidget_tabbar_QTabBar", "Generic Highlighter")
test.log("Trying to download definitions...")
diff --git a/tests/system/suite_general/tst_installed_languages/test.py b/tests/system/suite_general/tst_installed_languages/test.py
index 75a60e8fd0..e48abed67f 100644
--- a/tests/system/suite_general/tst_installed_languages/test.py
+++ b/tests/system/suite_general/tst_installed_languages/test.py
@@ -32,6 +32,5 @@ def main():
except:
test.fail("Creator seems to be missing %s translation" % languageName)
sendEvent("QCloseEvent", ":Qt Creator_Core::Internal::MainWindow")
- waitForCleanShutdown()
__removeTestingDir__()
copySettingsToTmpDir()
diff --git a/tests/system/suite_general/tst_remove_kits/test.py b/tests/system/suite_general/tst_remove_kits/test.py
index 365f4b217f..652390e010 100644
--- a/tests/system/suite_general/tst_remove_kits/test.py
+++ b/tests/system/suite_general/tst_remove_kits/test.py
@@ -8,7 +8,9 @@ def verifyProjectsMode(expectedKits):
bAndRIndex = getQModelIndexStr("text='Build & Run'",
":Projects.ProjectNavigationTreeView")
foundKits = dumpItems(treeView.model(), waitForObject(bAndRIndex))
- relevantKits = list(filter(lambda x: 'Python' not in x,foundKits)) # ignore Python kits
+ # ignore Python kits and non-kit item
+ excludes = ('Python', 'Hide Inactive Kits', 'Show All Kits')
+ relevantKits = list(filter(lambda x: all(ex not in x for ex in excludes), foundKits))
test.compare(len(relevantKits), len(expectedKits), "Verify number of listed kits.")
test.compare(set(relevantKits), set(expectedKits), "Verify if expected kits are listed.")
hasKits = len(expectedKits) > 0
diff --git a/tests/system/suite_tools/tst_designer_autocomplete/test.py b/tests/system/suite_tools/tst_designer_autocomplete/test.py
index b378407f81..c77d4b5f18 100644
--- a/tests/system/suite_tools/tst_designer_autocomplete/test.py
+++ b/tests/system/suite_tools/tst_designer_autocomplete/test.py
@@ -48,12 +48,15 @@ def main():
snooze(1)
type(editor, ">")
snooze(1)
+ proposalExists = lambda: object.exists(':popupFrame_TextEditor::GenericProposalWidget')
nativeType("%s" % buttonName[0])
- test.verify(waitFor("object.exists(':popupFrame_TextEditor::GenericProposalWidget')", 1500),
- "Verify that GenericProposalWidget is being shown.")
- nativeType("<Return>")
- test.verify(waitFor('str(lineUnderCursor(editor)).strip() == "ui->%s" % buttonName', 1000),
- 'Comparing line "%s" to expected "%s"' % (lineUnderCursor(editor), "ui->%s" % buttonName))
+ if test.verify(waitFor(proposalExists, 4000),
+ "Verify that GenericProposalWidget is being shown."):
+ nativeType("<Return>")
+ lineCorrect = lambda: str(lineUnderCursor(editor)).strip() == "ui->%s" % buttonName
+ test.verify(waitFor(lineCorrect, 1000),
+ ('Comparing line "%s" to expected "%s"'
+ % (lineUnderCursor(editor), "ui->%s" % buttonName)))
type(editor, "<Shift+Delete>") # Delete line
selectFromLocator("mainwindow.ui")
saveAndExit()
diff --git a/tests/unit/tests/matchers/projectstorage-matcher.h b/tests/unit/tests/matchers/projectstorage-matcher.h
index 02861d7eea..56b4ad9d6a 100644
--- a/tests/unit/tests/matchers/projectstorage-matcher.h
+++ b/tests/unit/tests/matchers/projectstorage-matcher.h
@@ -20,6 +20,7 @@ MATCHER_P2(IsTypeHint,
template<typename PropertiesMatcher, typename ExtraFilePathsMatcher>
auto IsItemLibraryEntry(QmlDesigner::TypeId typeId,
+ Utils::SmallStringView typeName,
Utils::SmallStringView name,
Utils::SmallStringView iconPath,
Utils::SmallStringView category,
@@ -31,6 +32,7 @@ auto IsItemLibraryEntry(QmlDesigner::TypeId typeId,
{
using QmlDesigner::Storage::Info::ItemLibraryEntry;
return AllOf(Field("typeId", &ItemLibraryEntry::typeId, typeId),
+ Field("typeName", &ItemLibraryEntry::typeName, typeName),
Field("name", &ItemLibraryEntry::name, name),
Field("iconPath", &ItemLibraryEntry::iconPath, iconPath),
Field("category", &ItemLibraryEntry::category, category),
@@ -66,7 +68,7 @@ auto IsTypeAnnotation(QmlDesigner::SourceId sourceId,
{
using QmlDesigner::Storage::Synchronization::TypeAnnotation;
return AllOf(Field("sourceId", &TypeAnnotation::sourceId, sourceId),
- Field("sourceId", &TypeAnnotation::directorySourceId, directorySourceId),
+ Field("directory sourceId", &TypeAnnotation::directorySourceId, directorySourceId),
Field("typeName", &TypeAnnotation::typeName, typeName),
Field("moduleId", &TypeAnnotation::moduleId, moduleId),
Field("iconPath", &TypeAnnotation::iconPath, iconPath),
diff --git a/tests/unit/tests/mocks/CMakeLists.txt b/tests/unit/tests/mocks/CMakeLists.txt
index d209043262..0fdfa639c0 100644
--- a/tests/unit/tests/mocks/CMakeLists.txt
+++ b/tests/unit/tests/mocks/CMakeLists.txt
@@ -22,6 +22,7 @@ add_qtc_library(TestMocks OBJECT
mocktimestampprovider.h
modelresourcemanagementmock.h
propertycomponentgeneratormock.h
+ projectstorageerrornotifiermock.h
projectstoragemock.cpp
projectstoragemock.h
projectstorageobservermock.h
diff --git a/tests/unit/tests/mocks/externaldependenciesmock.h b/tests/unit/tests/mocks/externaldependenciesmock.h
index c4cfe6cd3b..df70a7fcdb 100644
--- a/tests/unit/tests/mocks/externaldependenciesmock.h
+++ b/tests/unit/tests/mocks/externaldependenciesmock.h
@@ -15,6 +15,7 @@ public:
MOCK_METHOD(QString, qmlPuppetFallbackDirectory, (), (const, override));
MOCK_METHOD(QString, defaultPuppetToplevelBuildDirectory, (), (const, override));
MOCK_METHOD(QUrl, projectUrl, (), (const, override));
+ MOCK_METHOD(QString, projectName, (), (const, override));
MOCK_METHOD(QString, currentProjectDirPath, (), (const, override));
MOCK_METHOD(QUrl, currentResourcePath, (), (const, override));
MOCK_METHOD(void, parseItemLibraryDescriptions, (), (override));
diff --git a/tests/unit/tests/mocks/filesystemmock.h b/tests/unit/tests/mocks/filesystemmock.h
index cb1d4df4bc..f8544e509f 100644
--- a/tests/unit/tests/mocks/filesystemmock.h
+++ b/tests/unit/tests/mocks/filesystemmock.h
@@ -20,4 +20,5 @@ public:
MOCK_METHOD(QmlDesigner::FileStatus, fileStatus, (QmlDesigner::SourceId sourceId), (const, override));
MOCK_METHOD(void, remove, (const QmlDesigner::SourceIds &sourceIds), (override));
MOCK_METHOD(QString, contentAsQString, (const QString &filePath), (const, override));
+ MOCK_METHOD(QStringList, subdirectories, (const QString &directoryPath), (const, override));
};
diff --git a/tests/unit/tests/mocks/projectstorageerrornotifiermock.h b/tests/unit/tests/mocks/projectstorageerrornotifiermock.h
new file mode 100644
index 0000000000..730c70a66a
--- /dev/null
+++ b/tests/unit/tests/mocks/projectstorageerrornotifiermock.h
@@ -0,0 +1,17 @@
+// 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/googletest.h"
+
+#include <projectstorage/projectstorageerrornotifierinterface.h>
+
+class ProjectStorageErrorNotifierMock : public QmlDesigner::ProjectStorageErrorNotifierInterface
+{
+public:
+ MOCK_METHOD(void,
+ typeNameCannotBeResolved,
+ (Utils::SmallStringView typeName, QmlDesigner::SourceId souceId),
+ (override));
+};
diff --git a/tests/unit/tests/mocks/projectstoragemock.cpp b/tests/unit/tests/mocks/projectstoragemock.cpp
index 6d5304879e..d4a28d1ae6 100644
--- a/tests/unit/tests/mocks/projectstoragemock.cpp
+++ b/tests/unit/tests/mocks/projectstoragemock.cpp
@@ -12,9 +12,10 @@ using QmlDesigner::ImportId;
using QmlDesigner::ModuleId;
using QmlDesigner::PropertyDeclarationId;
using QmlDesigner::SourceId;
+using QmlDesigner::Storage::ModuleKind;
+using QmlDesigner::Storage::PropertyDeclarationTraits;
using QmlDesigner::TypeId;
using QmlDesigner::TypeIds;
-using QmlDesigner::Storage::PropertyDeclarationTraits;
namespace Storage = QmlDesigner::Storage;
@@ -41,18 +42,20 @@ void setupIsBasedOn(ProjectStorageMock &mock)
} // namespace
-ModuleId ProjectStorageMock::createModule(Utils::SmallStringView moduleName)
+ModuleId ProjectStorageMock::createModule(Utils::SmallStringView moduleName,
+ QmlDesigner::Storage::ModuleKind moduleKind)
{
- if (auto id = moduleId(moduleName)) {
+ if (auto id = moduleId(moduleName, moduleKind)) {
return id;
}
static ModuleId moduleId;
incrementBasicId(moduleId);
- ON_CALL(*this, moduleId(Eq(moduleName))).WillByDefault(Return(moduleId));
- ON_CALL(*this, moduleName(Eq(moduleId))).WillByDefault(Return(moduleName));
- ON_CALL(*this, fetchModuleIdUnguarded(Eq(moduleName))).WillByDefault(Return(moduleId));
+ ON_CALL(*this, moduleId(Eq(moduleName), Eq(moduleKind))).WillByDefault(Return(moduleId));
+ ON_CALL(*this, module(Eq(moduleId)))
+ .WillByDefault(Return(QmlDesigner::Storage::Module{moduleName, moduleKind}));
+ ON_CALL(*this, fetchModuleIdUnguarded(Eq(moduleName), Eq(moduleKind))).WillByDefault(Return(moduleId));
return moduleId;
}
@@ -385,11 +388,11 @@ void ProjectStorageMock::setupQtQuick()
{
setupIsBasedOn(*this);
- auto qmlModuleId = createModule("QML");
- auto qmlNativeModuleId = createModule("QML-cppnative");
- auto qtQmlModelsModuleId = createModule("QtQml.Models");
- auto qtQuickModuleId = createModule("QtQuick");
- auto qtQuickNativeModuleId = createModule("QtQuick-cppnative");
+ auto qmlModuleId = createModule("QML", ModuleKind::QmlLibrary);
+ auto qmlNativeModuleId = createModule("QML", ModuleKind::CppLibrary);
+ auto qtQmlModelsModuleId = createModule("QtQml.Models", ModuleKind::QmlLibrary);
+ auto qtQuickModuleId = createModule("QtQuick", ModuleKind::QmlLibrary);
+ auto qtQuickNativeModuleId = createModule("QtQuick", ModuleKind::CppLibrary);
auto boolId = createValue(qmlModuleId, "bool");
auto intId = createValue(qmlModuleId, "int");
@@ -463,11 +466,11 @@ void ProjectStorageMock::setupQtQuick()
{qtObjectId});
createObject(qtQuickModuleId, "PropertyChanges", {qtObjectId, stateOperationsId});
- auto qtQuickTimelineModuleId = createModule("QtQuick.Timeline");
+ auto qtQuickTimelineModuleId = createModule("QtQuick.Timeline", ModuleKind::QmlLibrary);
createObject(qtQuickTimelineModuleId, "KeyframeGroup", {qtObjectId});
createObject(qtQuickTimelineModuleId, "Keyframe", {qtObjectId});
- auto flowViewModuleId = createModule("FlowView");
+ auto flowViewModuleId = createModule("FlowView", ModuleKind::QmlLibrary);
createObject(flowViewModuleId,
"FlowActionArea",
"data",
@@ -492,12 +495,12 @@ void ProjectStorageMock::setupQtQuick()
void ProjectStorageMock::setupQtQuickImportedTypeNameIds(QmlDesigner::SourceId sourceId)
{
- auto qmlModuleId = moduleId("QML");
- auto qtQmlModelsModuleId = moduleId("QtQml.Models");
- auto qtQuickModuleId = moduleId("QtQuick");
- auto qtQuickNativeModuleId = moduleId("QtQuick-cppnative");
- auto qtQuickTimelineModuleId = moduleId("QtQuick.Timeline");
- auto flowViewModuleId = moduleId("FlowView");
+ auto qmlModuleId = moduleId("QML", ModuleKind::QmlLibrary);
+ auto qtQmlModelsModuleId = moduleId("QtQml.Models", ModuleKind::QmlLibrary);
+ auto qtQuickModuleId = moduleId("QtQuick", ModuleKind::QmlLibrary);
+ auto qtQuickNativeModuleId = moduleId("QtQuick", ModuleKind::CppLibrary);
+ auto qtQuickTimelineModuleId = moduleId("QtQuick.Timeline", ModuleKind::QmlLibrary);
+ auto flowViewModuleId = moduleId("FlowView", ModuleKind::QmlLibrary);
createImportedTypeNameId(sourceId, "int", qmlModuleId);
createImportedTypeNameId(sourceId, "QtObject", qmlModuleId);
diff --git a/tests/unit/tests/mocks/projectstoragemock.h b/tests/unit/tests/mocks/projectstoragemock.h
index 8aa5979ddb..8d9c3381b2 100644
--- a/tests/unit/tests/mocks/projectstoragemock.h
+++ b/tests/unit/tests/mocks/projectstoragemock.h
@@ -23,7 +23,8 @@ public:
void setupQtQuickImportedTypeNameIds(QmlDesigner::SourceId sourceId);
void setupCommonTypeCache();
- QmlDesigner::ModuleId createModule(Utils::SmallStringView moduleName);
+ QmlDesigner::ModuleId createModule(Utils::SmallStringView moduleName,
+ QmlDesigner::Storage::ModuleKind moduleKind);
QmlDesigner::ImportedTypeNameId createImportedTypeNameId(QmlDesigner::SourceId sourceId,
Utils::SmallStringView typeName,
@@ -126,8 +127,11 @@ public:
MOCK_METHOD(void, addObserver, (QmlDesigner::ProjectStorageObserver *), (override));
MOCK_METHOD(void, removeObserver, (QmlDesigner::ProjectStorageObserver *), (override));
- MOCK_METHOD(QmlDesigner::ModuleId, moduleId, (::Utils::SmallStringView), (const, override));
- MOCK_METHOD(Utils::SmallString, moduleName, (QmlDesigner::ModuleId), (const, override));
+ MOCK_METHOD(QmlDesigner::ModuleId,
+ moduleId,
+ (::Utils::SmallStringView, QmlDesigner::Storage::ModuleKind moduleKind),
+ (const, override));
+ MOCK_METHOD(QmlDesigner::Storage::Module, module, (QmlDesigner::ModuleId), (const, override));
MOCK_METHOD(std::optional<QmlDesigner::Storage::Info::PropertyDeclaration>,
propertyDeclaration,
@@ -296,13 +300,23 @@ public:
(QmlDesigner::SourceId sourceId),
(const, override));
- MOCK_METHOD(QmlDesigner::Storage::Synchronization::ProjectDatas,
- fetchProjectDatas,
+ MOCK_METHOD(QmlDesigner::Storage::Synchronization::DirectoryInfos,
+ fetchDirectoryInfos,
+ (QmlDesigner::SourceId sourceId),
+ (const, override));
+
+ MOCK_METHOD(QmlDesigner::Storage::Synchronization::DirectoryInfos,
+ fetchDirectoryInfos,
+ (QmlDesigner::SourceId sourceId, QmlDesigner::Storage::Synchronization::FileType),
+ (const, override));
+
+ MOCK_METHOD(QmlDesigner::SmallSourceIds<32>,
+ fetchSubdirectorySourceIds,
(QmlDesigner::SourceId sourceId),
(const, override));
- MOCK_METHOD(std::optional<QmlDesigner::Storage::Synchronization::ProjectData>,
- fetchProjectData,
+ MOCK_METHOD(std::optional<QmlDesigner::Storage::Synchronization::DirectoryInfo>,
+ fetchDirectoryInfo,
(QmlDesigner::SourceId sourceId),
(const, override));
@@ -337,7 +351,7 @@ public:
(const, override));
MOCK_METHOD(QmlDesigner::ModuleId,
fetchModuleIdUnguarded,
- (Utils::SmallStringView name),
+ (Utils::SmallStringView name, QmlDesigner::Storage::ModuleKind),
(const, override));
MOCK_METHOD(QmlDesigner::TypeId,
fetchTypeIdByModuleIdAndExportedName,
diff --git a/tests/unit/tests/mocks/qmltypesparsermock.h b/tests/unit/tests/mocks/qmltypesparsermock.h
index e3fa1ca605..0f57c634d0 100644
--- a/tests/unit/tests/mocks/qmltypesparsermock.h
+++ b/tests/unit/tests/mocks/qmltypesparsermock.h
@@ -15,6 +15,6 @@ public:
(const QString &sourceContent,
QmlDesigner::Storage::Imports &imports,
QmlDesigner::Storage::Synchronization::Types &types,
- const QmlDesigner::Storage::Synchronization::ProjectData &projectData),
+ const QmlDesigner::Storage::Synchronization::DirectoryInfo &directoryInfo),
(override));
};
diff --git a/tests/unit/tests/printers/gtest-creator-printing.cpp b/tests/unit/tests/printers/gtest-creator-printing.cpp
index 6bc78e9936..8ca65f4526 100644
--- a/tests/unit/tests/printers/gtest-creator-printing.cpp
+++ b/tests/unit/tests/printers/gtest-creator-printing.cpp
@@ -695,10 +695,10 @@ std::ostream &operator<<(std::ostream &out, const ItemLibraryProperty &property)
std::ostream &operator<<(std::ostream &out, const ItemLibraryEntry &entry)
{
- return out << R"((")" << entry.name << R"(", ")" << entry.iconPath << R"(", ")"
- << entry.category << R"(", ")" << entry.import << R"(", ")" << entry.toolTip
- << R"(", ")" << entry.templatePath << R"(", )" << entry.properties << ", "
- << entry.extraFilePaths << ")";
+ return out << R"((")" << entry.typeName << R"(", ")" << entry.name << R"(", ")"
+ << entry.iconPath << R"(", ")" << entry.category << R"(", ")" << entry.import
+ << R"(", ")" << entry.toolTip << R"(", ")" << entry.templatePath << R"(", )"
+ << entry.properties << ", " << entry.extraFilePaths << ")";
}
} // namespace Storage::Info
@@ -754,6 +754,8 @@ const char *fileTypeToText(FileType fileType)
return "QmlDocument";
case FileType::QmlTypes:
return "QmlTypes";
+ case FileType::Directory:
+ return "Directory";
}
return "";
@@ -791,8 +793,8 @@ std::ostream &operator<<(std::ostream &out, const SynchronizationPackage &packag
<< ", updatedSourceIds: " << package.updatedSourceIds
<< ", fileStatuses: " << package.fileStatuses
<< ", updatedFileStatusSourceIds: " << package.updatedFileStatusSourceIds
- << ", updatedProjectSourceIds: " << package.updatedProjectSourceIds
- << ", projectDatas: " << package.projectDatas
+ << ", updatedDirectoryInfoSourceIds: " << package.updatedDirectoryInfoSourceIds
+ << ", directoryInfos: " << package.directoryInfos
<< ", propertyEditorQmlPaths: " << package.propertyEditorQmlPaths
<< ", updatedPropertyEditorQmlPathSourceIds: "
<< package.updatedPropertyEditorQmlPathSourceIds
@@ -801,9 +803,9 @@ std::ostream &operator<<(std::ostream &out, const SynchronizationPackage &packag
<< ")";
}
-std::ostream &operator<<(std::ostream &out, const ProjectData &data)
+std::ostream &operator<<(std::ostream &out, const DirectoryInfo &data)
{
- return out << "(" << data.projectSourceId << ", " << data.sourceId << ", " << data.moduleId
+ return out << "(" << data.directorySourceId << ", " << data.sourceId << ", " << data.moduleId
<< ", " << data.fileType << ")";
}
@@ -831,8 +833,9 @@ std::ostream &operator<<(std::ostream &out, const Type &type)
{
using std::operator<<;
using Utils::operator<<;
- return out << "( typename: \"" << type.typeName << "\", prototype: " << type.prototype << ", "
- << type.prototypeId << ", " << type.traits << ", source: " << type.sourceId
+ return out << "( typename: \"" << type.typeName << "\", prototype: {\"" << type.prototype
+ << "\", " << type.prototypeId << "}, " << "\", extension: {\"" << type.extension
+ << "\", " << type.extensionId << "}, " << type.traits << ", source: " << type.sourceId
<< ", exports: " << type.exportedTypes << ", properties: " << type.propertyDeclarations
<< ", functions: " << type.functionDeclarations
<< ", signals: " << type.signalDeclarations << ", changeLevel: " << type.changeLevel
diff --git a/tests/unit/tests/printers/gtest-creator-printing.h b/tests/unit/tests/printers/gtest-creator-printing.h
index 8d0c77888e..2444b9d98b 100644
--- a/tests/unit/tests/printers/gtest-creator-printing.h
+++ b/tests/unit/tests/printers/gtest-creator-printing.h
@@ -207,7 +207,7 @@ class EnumeratorDeclaration;
enum class ImportKind : char;
enum class IsAutoVersion : char;
enum class IsQualified : int;
-class ProjectData;
+class DirectoryInfo;
class SynchronizationPackage;
enum class FileType : char;
enum class ChangeLevel : char;
@@ -227,7 +227,7 @@ std::ostream &operator<<(std::ostream &out, const EnumerationDeclaration &enumer
std::ostream &operator<<(std::ostream &out, const EnumeratorDeclaration &enumeratorDeclaration);
std::ostream &operator<<(std::ostream &out, const ImportKind &importKind);
std::ostream &operator<<(std::ostream &out, IsQualified isQualified);
-std::ostream &operator<<(std::ostream &out, const ProjectData &data);
+std::ostream &operator<<(std::ostream &out, const DirectoryInfo &data);
std::ostream &operator<<(std::ostream &out, const SynchronizationPackage &package);
std::ostream &operator<<(std::ostream &out, FileType fileType);
std::ostream &operator<<(std::ostream &out, ChangeLevel changeLevel);
diff --git a/tests/unit/tests/testdesignercore/CMakeLists.txt b/tests/unit/tests/testdesignercore/CMakeLists.txt
index 0bdf3452d6..c446af81a3 100644
--- a/tests/unit/tests/testdesignercore/CMakeLists.txt
+++ b/tests/unit/tests/testdesignercore/CMakeLists.txt
@@ -125,6 +125,8 @@ add_qtc_library(TestDesignerCore OBJECT
projectstorage/projectstorageinterface.h
projectstorage/projectstorageobserver.h
projectstorage/projectstorage.cpp projectstorage/projectstorage.h
+ projectstorage/projectstorageerrornotifierinterface.h
+ projectstorage/projectstorageerrornotifier.cpp projectstorage/projectstorageerrornotifier.h
projectstorage/projectstoragepathwatcher.h
projectstorage/projectstoragepathwatcherinterface.h
projectstorage/projectstoragepathwatchernotifierinterface.h
@@ -147,6 +149,8 @@ add_qtc_library(TestDesignerCore OBJECT
tracing/qmldesignertracing.cpp tracing/qmldesignertracing.h
rewritertransaction.cpp
rewritertransaction.h
+ uniquename.cpp
+ uniquename.h
)
extend_qtc_library(TestDesignerCore
diff --git a/tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp b/tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp
index 10e4cc3252..b36966dda1 100644
--- a/tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp
+++ b/tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp
@@ -11,6 +11,7 @@
#include <propertycomponentgenerator.h>
using namespace Qt::StringLiterals;
+using QmlDesigner::Storage::ModuleKind;
namespace QmlDesigner {
@@ -74,7 +75,7 @@ protected:
{
if (auto property = node->property(name)) {
const auto &value = property.value;
- if (value.type() == QVariant::List) {
+ if (value.typeId() == QMetaType::QVariantList) {
auto list = value.toList();
if (list.size())
return list.front().value<Type>();
@@ -182,7 +183,7 @@ protected:
resourceManagementMock)};
QmlDesigner::PropertyComponentGenerator generator{QString{sourcesPath}, &model};
QmlDesigner::NodeMetaInfo itemMetaInfo = model.qtQuickItemMetaInfo();
- QmlDesigner::ModuleId qmlModuleId = projectStorageMock.createModule("QML");
+ QmlDesigner::ModuleId qmlModuleId = projectStorageMock.createModule("QML", ModuleKind::QmlLibrary);
};
TEST_F(PropertyComponentGenerator,
@@ -345,7 +346,8 @@ TEST_F(PropertyComponentGenerator, after_refresh_meta_infos_type_was_deleted)
auto xProperty = itemMetaInfo.property("x");
auto doubleMetaInfo = model.doubleMetaInfo();
projectStorageMock.removeExportedTypeName(doubleMetaInfo.id(),
- projectStorageMock.createModule("QML"),
+ projectStorageMock.createModule("QML",
+ ModuleKind::QmlLibrary),
"real");
generator.refreshMetaInfos({doubleMetaInfo.id()});
@@ -359,11 +361,13 @@ TEST_F(PropertyComponentGenerator, after_refresh_meta_infos_type_was_added)
auto xProperty = itemMetaInfo.property("x");
auto doubleMetaInfo = model.doubleMetaInfo();
projectStorageMock.removeExportedTypeName(doubleMetaInfo.id(),
- projectStorageMock.createModule("QML"),
+ projectStorageMock.createModule("QML",
+ ModuleKind::QmlLibrary),
"real");
generator.refreshMetaInfos({doubleMetaInfo.id()});
projectStorageMock.addExportedTypeName(doubleMetaInfo.id(),
- projectStorageMock.createModule("QML"),
+ projectStorageMock.createModule("QML",
+ ModuleKind::QmlLibrary),
"real");
generator.refreshMetaInfos({});
diff --git a/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp b/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp
index d2f2143a73..398c54bfad 100644
--- a/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp
+++ b/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp
@@ -13,6 +13,7 @@ namespace {
using BasicProperty = QmlDesigner::PropertyComponentGenerator::BasicProperty;
using ComplexProperty = QmlDesigner::PropertyComponentGenerator::ComplexProperty;
using QmlDesigner::PropertyMetaInfo;
+using QmlDesigner::Storage::ModuleKind;
class PropertyEditorComponentGenerator : public ::testing::Test
{
@@ -86,7 +87,8 @@ protected:
NiceMock<ProjectStorageMockWithQtQtuick> projectStorageMock{sourceId};
NiceMock<PropertyComponentGeneratorMock> propertyGeneratorMock;
QmlDesigner::PropertyEditorComponentGenerator generator{propertyGeneratorMock};
- QmlDesigner::ModuleId qtQuickModuleId = projectStorageMock.createModule("QtQuick");
+ QmlDesigner::ModuleId qtQuickModuleId = projectStorageMock.createModule("QtQuick",
+ ModuleKind::QmlLibrary);
QmlDesigner::NodeMetaInfo fooTypeInfo = createType("Foo");
QmlDesigner::TypeId dummyTypeId = projectStorageMock.commonTypeCache().builtinTypeId<double>();
};
diff --git a/tests/unit/tests/unittests/listmodeleditor/listmodeleditor-test.cpp b/tests/unit/tests/unittests/listmodeleditor/listmodeleditor-test.cpp
index f8bef106ac..06ab4f930c 100644
--- a/tests/unit/tests/unittests/listmodeleditor/listmodeleditor-test.cpp
+++ b/tests/unit/tests/unittests/listmodeleditor/listmodeleditor-test.cpp
@@ -511,7 +511,7 @@ TEST_F(ListModelEditor, convert_string_float_to_float)
model.setValue(1, 1, "25.5");
ASSERT_THAT(element2.variantProperty("name").value().value<double>(), 25.5);
- ASSERT_THAT(element2.variantProperty("name").value().type(), QVariant::Double);
+ ASSERT_THAT(element2.variantProperty("name").value().typeId(), QMetaType::Double);
}
TEST_F(ListModelEditor, convert_string_integer_to_double)
@@ -521,7 +521,7 @@ TEST_F(ListModelEditor, convert_string_integer_to_double)
model.setValue(1, 1, "25");
ASSERT_THAT(element2.variantProperty("name").value().value<double>(), 25);
- ASSERT_THAT(element2.variantProperty("name").value().type(), QVariant::Double);
+ ASSERT_THAT(element2.variantProperty("name").value().typeId(), QMetaType::Double);
}
TEST_F(ListModelEditor, dont_convert_string_to_number)
@@ -531,7 +531,7 @@ TEST_F(ListModelEditor, dont_convert_string_to_number)
model.setValue(1, 1, "hello");
ASSERT_THAT(element2.variantProperty("name").value().value<QString>(), u"hello");
- ASSERT_THAT(element2.variantProperty("name").value().type(), QVariant::String);
+ ASSERT_THAT(element2.variantProperty("name").value().typeId(), QMetaType::QString);
}
TEST_F(ListModelEditor, empty_strings_removes_property)
@@ -558,7 +558,7 @@ TEST_F(ListModelEditor, dispay_value_is_changed_to_double)
model.setValue(1, 1, "25.5");
- ASSERT_THAT(displayValues()[1][1].type(), QVariant::Double);
+ ASSERT_THAT(displayValues()[1][1].typeId(), QMetaType::Double);
}
TEST_F(ListModelEditor, string_dispay_value_is_not_changed)
@@ -567,7 +567,7 @@ TEST_F(ListModelEditor, string_dispay_value_is_not_changed)
model.setValue(1, 1, "25.5a");
- ASSERT_THAT(displayValues()[1][1].type(), QVariant::String);
+ ASSERT_THAT(displayValues()[1][1].typeId(), QMetaType::QString);
}
TEST_F(ListModelEditor, set_invalid_to_dark_yellow_background_color)
diff --git a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp
index af8c4bd220..c96def356a 100644
--- a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp
+++ b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp
@@ -17,6 +17,7 @@ namespace {
using QmlDesigner::FlagIs;
using QmlDesigner::ModelNode;
using QmlDesigner::ModelNodes;
+using QmlDesigner::Storage::ModuleKind;
using QmlDesigner::Storage::TypeTraits;
using QmlDesigner::Storage::TypeTraitsKind;
@@ -68,9 +69,10 @@ protected:
}
QmlDesigner::NodeMetaInfo createDerivedDummyMetaInfo(Utils::SmallStringView moduleName,
+ ModuleKind moduleKind,
Utils::SmallStringView typeName)
{
- auto moduleId = projectStorageMock.createModule(moduleName);
+ auto moduleId = projectStorageMock.createModule(moduleName, moduleKind);
auto typeId = projectStorageMock.createType(moduleId, typeName, {});
return createDerivedDummyMetaInfo(typeId);
@@ -86,10 +88,11 @@ protected:
}
QmlDesigner::NodeMetaInfo createMetaInfo(Utils::SmallStringView moduleName,
+ ModuleKind moduleKind,
Utils::SmallStringView typeName,
QmlDesigner::Storage::TypeTraits typeTraits = {})
{
- auto moduleId = projectStorageMock.createModule(moduleName);
+ auto moduleId = projectStorageMock.createModule(moduleName, moduleKind);
auto typeId = projectStorageMock.createType(moduleId, typeName, typeTraits);
return QmlDesigner::NodeMetaInfo{typeId, &projectStorageMock};
@@ -109,8 +112,9 @@ protected:
ModelNode object = model.createModelNode("QtObject");
QmlDesigner::NodeMetaInfo itemMetaInfo = item.metaInfo();
QmlDesigner::NodeMetaInfo objectMetaInfo = object.metaInfo();
- QmlDesigner::ModuleId qmlModuleId = projectStorageMock.createModule("QML");
- QmlDesigner::ModuleId qtQuickModuleId = projectStorageMock.createModule("QtQuick");
+ QmlDesigner::ModuleId qmlModuleId = projectStorageMock.createModule("QML", ModuleKind::QmlLibrary);
+ QmlDesigner::ModuleId qtQuickModuleId = projectStorageMock.createModule("QtQuick",
+ ModuleKind::QmlLibrary);
QmlDesigner::TypeId intTypeId = projectStorageMock.typeId(qmlModuleId,
"int",
QmlDesigner::Storage::Version{});
@@ -215,7 +219,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_file_component)
TEST_F(NodeMetaInfo, component_is_file_component)
{
- auto moduleId = projectStorageMock.createModule("/path/to/project");
+ auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary);
TypeTraits traits{TypeTraitsKind::Reference};
traits.isFileComponent = true;
auto typeId = projectStorageMock.createType(moduleId, "Foo", traits);
@@ -228,7 +232,7 @@ TEST_F(NodeMetaInfo, component_is_file_component)
TEST_F(NodeMetaInfo, is_project_component)
{
- auto moduleId = projectStorageMock.createModule("/path/to/project");
+ auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary);
TypeTraits traits{TypeTraitsKind::Reference};
traits.isProjectComponent = true;
auto typeId = projectStorageMock.createType(moduleId, "Foo", traits);
@@ -242,7 +246,7 @@ TEST_F(NodeMetaInfo, is_project_component)
TEST_F(NodeMetaInfo, is_not_project_component)
{
using QmlDesigner::Storage::TypeTraits;
- auto moduleId = projectStorageMock.createModule("/path/to/project");
+ auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary);
auto typeId = projectStorageMock.createType(moduleId, "Foo", {});
QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock};
@@ -262,7 +266,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_project_component)
TEST_F(NodeMetaInfo, is_in_project_module)
{
- auto moduleId = projectStorageMock.createModule("/path/to/project");
+ auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary);
TypeTraits traits{TypeTraitsKind::Reference};
traits.isInProjectModule = true;
auto typeId = projectStorageMock.createType(moduleId, "Foo", traits);
@@ -276,7 +280,7 @@ TEST_F(NodeMetaInfo, is_in_project_module)
TEST_F(NodeMetaInfo, is_not_in_project_module)
{
using QmlDesigner::Storage::TypeTraits;
- auto moduleId = projectStorageMock.createModule("/path/to/project");
+ auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary);
auto typeId = projectStorageMock.createType(moduleId, "Foo", {});
QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock};
@@ -873,7 +877,7 @@ TEST_F(NodeMetaInfo, second_input_is_invalid_for_common_base_returns_invalid)
TEST_F(NodeMetaInfo, source_id)
{
- auto moduleId = projectStorageMock.createModule("/path/to/project");
+ auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary);
auto typeSourceId = QmlDesigner::SourceId::create(999);
auto typeId = projectStorageMock.createType(moduleId, "Foo", {}, {}, typeSourceId);
QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock};
@@ -930,7 +934,7 @@ TEST_F(NodeMetaInfo, default_is_not_color)
TEST_F(NodeMetaInfo, float_is_a_floating_type)
{
- auto metaInfo = createMetaInfo("QML-cppnative", "float");
+ auto metaInfo = createMetaInfo("QML", ModuleKind::CppLibrary, "float");
bool isType = metaInfo.isFloat();
@@ -957,7 +961,7 @@ TEST_F(NodeMetaInfo, default_is_not_float)
TEST_F(NodeMetaInfo, is_FlowView_FlowActionArea)
{
- auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowActionArea");
+ auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowActionArea");
bool isType = metaInfo.isFlowViewFlowActionArea();
@@ -975,7 +979,7 @@ TEST_F(NodeMetaInfo, default_is_not_FlowView_FlowActionArea)
TEST_F(NodeMetaInfo, is_FlowView_FlowDecision)
{
- auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowDecision");
+ auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowDecision");
bool isType = metaInfo.isFlowViewFlowDecision();
@@ -993,7 +997,7 @@ TEST_F(NodeMetaInfo, default_is_not_FlowView_FlowDecision)
TEST_F(NodeMetaInfo, is_FlowView_FlowItem)
{
- auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowItem");
+ auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowItem");
bool isType = metaInfo.isFlowViewFlowItem();
@@ -1011,7 +1015,7 @@ TEST_F(NodeMetaInfo, default_is_not_FlowView_FlowItem)
TEST_F(NodeMetaInfo, is_FlowView_FlowTransition)
{
- auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowTransition");
+ auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowTransition");
bool isType = metaInfo.isFlowViewFlowTransition();
@@ -1029,7 +1033,7 @@ TEST_F(NodeMetaInfo, default_is_not_FlowView_FlowTransition)
TEST_F(NodeMetaInfo, is_FlowView_FlowView)
{
- auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowView");
+ auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowView");
bool isType = metaInfo.isFlowViewFlowView();
@@ -1047,7 +1051,7 @@ TEST_F(NodeMetaInfo, default_is_not_FlowView_FlowView)
TEST_F(NodeMetaInfo, is_FlowView_FlowWildcard)
{
- auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowWildcard");
+ auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowWildcard");
bool isType = metaInfo.isFlowViewFlowWildcard();
@@ -1065,7 +1069,7 @@ TEST_F(NodeMetaInfo, default_is_not_FlowView_FlowWildcard)
TEST_F(NodeMetaInfo, FlowItem_is_FlowView_item)
{
- auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowItem");
+ auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowItem");
bool isType = metaInfo.isFlowViewItem();
@@ -1074,7 +1078,7 @@ TEST_F(NodeMetaInfo, FlowItem_is_FlowView_item)
TEST_F(NodeMetaInfo, FlowWildcard_is_FlowView_item)
{
- auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowWildcard");
+ auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowWildcard");
bool isType = metaInfo.isFlowViewItem();
@@ -1083,7 +1087,7 @@ TEST_F(NodeMetaInfo, FlowWildcard_is_FlowView_item)
TEST_F(NodeMetaInfo, FlowDecision_is_FlowView_item)
{
- auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowDecision");
+ auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowDecision");
bool isType = metaInfo.isFlowViewItem();
@@ -1128,7 +1132,7 @@ TEST_F(NodeMetaInfo, QtQuick_Item_is_graphical_item)
TEST_F(NodeMetaInfo, QtQuickWindow_Window_is_graphical_item)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Window", "Window");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Window", ModuleKind::QmlLibrary, "Window");
bool isType = metaInfo.isGraphicalItem();
@@ -1137,7 +1141,7 @@ TEST_F(NodeMetaInfo, QtQuickWindow_Window_is_graphical_item)
TEST_F(NodeMetaInfo, QtQuickDialogs_Dialogs_is_graphical_item)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Dialogs", "Dialog");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Dialogs", ModuleKind::QmlLibrary, "Dialog");
bool isType = metaInfo.isGraphicalItem();
@@ -1146,7 +1150,7 @@ TEST_F(NodeMetaInfo, QtQuickDialogs_Dialogs_is_graphical_item)
TEST_F(NodeMetaInfo, QtQuickControls_Popup_is_graphical_item)
{
- auto metaInfo = createMetaInfo("QtQuick.Controls", "Popup");
+ auto metaInfo = createMetaInfo("QtQuick.Controls", ModuleKind::QmlLibrary, "Popup");
bool isType = metaInfo.isGraphicalItem();
@@ -1191,7 +1195,7 @@ TEST_F(NodeMetaInfo, QtQuick_Positioner_is_layoutable)
TEST_F(NodeMetaInfo, QtQuick_Layouts_Layout_is_layoutable)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Layouts", "Layout");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Layouts", ModuleKind::QmlLibrary, "Layout");
bool isType = metaInfo.isLayoutable();
@@ -1200,7 +1204,7 @@ TEST_F(NodeMetaInfo, QtQuick_Layouts_Layout_is_layoutable)
TEST_F(NodeMetaInfo, QtQuick_Controls_SplitView_is_layoutable)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", "SplitView");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", ModuleKind::QmlLibrary, "SplitView");
bool isType = metaInfo.isLayoutable();
@@ -1263,7 +1267,8 @@ TEST_F(NodeMetaInfo, default_is_not_qml_component)
TEST_F(NodeMetaInfo, is_QtMultimedia_SoundEffect)
{
- auto qtMultimediaModuleId = projectStorageMock.createModule("QtMultimedia");
+ auto qtMultimediaModuleId = projectStorageMock.createModule("QtMultimedia",
+ ModuleKind::QmlLibrary);
auto metaInfo = createDerivedDummyMetaInfo(qtMultimediaModuleId, "SoundEffect");
bool isType = metaInfo.isQtMultimediaSoundEffect();
@@ -1300,7 +1305,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtObject)
TEST_F(NodeMetaInfo, is_QtQuick3D_BakedLightmap)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "BakedLightmap");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "BakedLightmap");
bool isType = metaInfo.isQtQuick3DBakedLightmap();
@@ -1318,7 +1323,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_BakedLightmap)
TEST_F(NodeMetaInfo, is_QtQuick3D_Camera)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Camera");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Camera");
bool isType = metaInfo.isQtQuick3DCamera();
@@ -1336,7 +1341,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Camera)
TEST_F(NodeMetaInfo, is_QtQuick3D_Command)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Command");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Command");
bool isType = metaInfo.isQtQuick3DCommand();
@@ -1354,7 +1359,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Command)
TEST_F(NodeMetaInfo, is_QtQuick3D_DefaultMaterial)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "DefaultMaterial");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "DefaultMaterial");
bool isType = metaInfo.isQtQuick3DDefaultMaterial();
@@ -1372,7 +1377,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_DefaultMaterial)
TEST_F(NodeMetaInfo, is_QtQuick3D_Effect)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Effect");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Effect");
bool isType = metaInfo.isQtQuick3DEffect();
@@ -1390,7 +1395,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Effect)
TEST_F(NodeMetaInfo, is_QtQuick3D_InstanceList)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "InstanceList");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "InstanceList");
bool isType = metaInfo.isQtQuick3DInstanceList();
@@ -1408,7 +1413,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_InstanceList)
TEST_F(NodeMetaInfo, is_QtQuick3D_InstanceListEntry)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "InstanceListEntry");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "InstanceListEntry");
bool isType = metaInfo.isQtQuick3DInstanceListEntry();
@@ -1426,7 +1431,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_InstanceListEntry)
TEST_F(NodeMetaInfo, is_QtQuick3D_Light)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Light");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Light");
bool isType = metaInfo.isQtQuick3DLight();
@@ -1444,7 +1449,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Light)
TEST_F(NodeMetaInfo, is_QtQuick3D_Material)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Material");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Material");
bool isType = metaInfo.isQtQuick3DMaterial();
@@ -1462,7 +1467,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Material)
TEST_F(NodeMetaInfo, is_QtQuick3D_Model)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Model");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Model");
bool isType = metaInfo.isQtQuick3DModel();
@@ -1480,7 +1485,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Model)
TEST_F(NodeMetaInfo, is_QtQuick3D_Node)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Node");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Node");
bool isType = metaInfo.isQtQuick3DNode();
@@ -1498,7 +1503,8 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Node)
TEST_F(NodeMetaInfo, is_QtQuick3D_Particles3D_cppnative_QQuick3DParticleAbstractShape)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D-cppnative",
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D",
+ ModuleKind::CppLibrary,
"QQuick3DParticleAbstractShape");
bool isType = metaInfo.isQtQuick3DParticlesAbstractShape();
@@ -1517,7 +1523,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Particles3D_cppnative_QQuick3DPart
TEST_F(NodeMetaInfo, is_QtQuick3D_Particles3D_Affector3D)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", "Affector3D");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D",
+ ModuleKind::QmlLibrary,
+ "Affector3D");
bool isType = metaInfo.isQtQuick3DParticles3DAffector3D();
@@ -1535,7 +1543,9 @@ TEST_F(NodeMetaInfo, QtQuick3D_Particles3D_Affector3D)
TEST_F(NodeMetaInfo, is_QtQuick3D_Particles3D_Attractor3D)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", "Attractor3D");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D",
+ ModuleKind::QmlLibrary,
+ "Attractor3D");
bool isType = metaInfo.isQtQuick3DParticles3DAttractor3D();
@@ -1553,7 +1563,9 @@ TEST_F(NodeMetaInfo, QtQuick3D_Particles3D_Attractor3D)
TEST_F(NodeMetaInfo, is_QtQuick3D_Particles3D_Particle3D)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", "Particle3D");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D",
+ ModuleKind::QmlLibrary,
+ "Particle3D");
bool isType = metaInfo.isQtQuick3DParticles3DParticle3D();
@@ -1571,7 +1583,9 @@ TEST_F(NodeMetaInfo, QtQuick3D_Particles3D_Particle3D)
TEST_F(NodeMetaInfo, is_QtQuick3D_Particles3D_ParticleEmitter3D)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", "ParticleEmitter3D");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D",
+ ModuleKind::QmlLibrary,
+ "ParticleEmitter3D");
bool isType = metaInfo.isQtQuick3DParticles3DParticleEmitter3D();
@@ -1589,7 +1603,9 @@ TEST_F(NodeMetaInfo, QtQuick3D_Particles3D_ParticleEmitter3D)
TEST_F(NodeMetaInfo, is_QtQuick3D_Particles3D_SpriteParticle3D)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", "SpriteParticle3D");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D",
+ ModuleKind::QmlLibrary,
+ "SpriteParticle3D");
bool isType = metaInfo.isQtQuick3DParticles3DSpriteParticle3D();
@@ -1607,7 +1623,7 @@ TEST_F(NodeMetaInfo, QtQuick3D_Particles3D_SpriteParticle3D)
TEST_F(NodeMetaInfo, is_QtQuick3D_Pass)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Pass");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Pass");
bool isType = metaInfo.isQtQuick3DPass();
@@ -1625,7 +1641,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Pass)
TEST_F(NodeMetaInfo, is_QtQuick3D_PrincipledMaterial)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "PrincipledMaterial");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D",
+ ModuleKind::QmlLibrary,
+ "PrincipledMaterial");
bool isType = metaInfo.isQtQuick3DPrincipledMaterial();
@@ -1643,7 +1661,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_PrincipledMaterial)
TEST_F(NodeMetaInfo, is_QtQuick3D_SpecularGlossyMaterial)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "SpecularGlossyMaterial");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D",
+ ModuleKind::QmlLibrary,
+ "SpecularGlossyMaterial");
bool isType = metaInfo.isQtQuick3DSpecularGlossyMaterial();
@@ -1661,7 +1681,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_SpecularGlossyMaterial)
TEST_F(NodeMetaInfo, is_QtQuick3D_SceneEnvironment)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "SceneEnvironment");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "SceneEnvironment");
bool isType = metaInfo.isQtQuick3DSceneEnvironment();
@@ -1679,7 +1699,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_SceneEnvironment)
TEST_F(NodeMetaInfo, is_QtQuick3D_Shader)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Shader");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Shader");
bool isType = metaInfo.isQtQuick3DShader();
@@ -1697,7 +1717,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Shader)
TEST_F(NodeMetaInfo, is_QtQuick3D_Texture)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Texture");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Texture");
bool isType = metaInfo.isQtQuick3DTexture();
@@ -1715,7 +1735,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Texture)
TEST_F(NodeMetaInfo, is_QtQuick3D_TextureInput)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "TextureInput");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "TextureInput");
bool isType = metaInfo.isQtQuick3DTextureInput();
@@ -1733,7 +1753,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_TextureInput)
TEST_F(NodeMetaInfo, is_QtQuick3D_CubeMapTexture)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "CubeMapTexture");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "CubeMapTexture");
bool isType = metaInfo.isQtQuick3DCubeMapTexture();
@@ -1751,7 +1771,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_CubeMapTexture)
TEST_F(NodeMetaInfo, is_QtQuick3D_View3D)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "View3D");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "View3D");
bool isType = metaInfo.isQtQuick3DView3D();
@@ -1769,7 +1789,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_View3D)
TEST_F(NodeMetaInfo, is_QtQuick_BorderImage)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "BorderImage");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "BorderImage");
bool isType = metaInfo.isQtQuickBorderImage();
@@ -1787,7 +1807,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_BorderImage)
TEST_F(NodeMetaInfo, is_QtQuickControls_SwipeView)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", "SwipeView");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", ModuleKind::QmlLibrary, "SwipeView");
bool isType = metaInfo.isQtQuickControlsSwipeView();
@@ -1805,7 +1825,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickControls_SwipeView)
TEST_F(NodeMetaInfo, is_QtQuickControls_TabBar)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", "TabBar");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", ModuleKind::QmlLibrary, "TabBar");
bool isType = metaInfo.isQtQuickControlsTabBar();
@@ -1823,7 +1843,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickControls_TabBar)
TEST_F(NodeMetaInfo, is_QtQuickExtras_Picture)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Extras", "Picture");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Extras", ModuleKind::QmlLibrary, "Picture");
bool isType = metaInfo.isQtQuickExtrasPicture();
@@ -1841,7 +1861,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickExtras_Picture)
TEST_F(NodeMetaInfo, is_QtQuick_Image)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Image");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Image");
bool isType = metaInfo.isQtQuickImage();
@@ -1859,7 +1879,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Image)
TEST_F(NodeMetaInfo, is_QtQuick_Item)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Item");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Item");
bool isType = metaInfo.isQtQuickItem();
@@ -1877,7 +1897,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Item)
TEST_F(NodeMetaInfo, is_QtQuickLayouts_BorderImage)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Layouts", "Layout");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Layouts", ModuleKind::QmlLibrary, "Layout");
bool isType = metaInfo.isQtQuickLayoutsLayout();
@@ -1895,7 +1915,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickLayouts_Layout)
TEST_F(NodeMetaInfo, is_QtQuick_Loader)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Loader");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Loader");
bool isType = metaInfo.isQtQuickLoader();
@@ -1913,7 +1933,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Loader)
TEST_F(NodeMetaInfo, is_QtQuick_Path)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Path");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Path");
bool isType = metaInfo.isQtQuickPath();
@@ -1931,7 +1951,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Path)
TEST_F(NodeMetaInfo, is_QtQuick_PauseAnimation)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "PauseAnimation");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "PauseAnimation");
bool isType = metaInfo.isQtQuickPauseAnimation();
@@ -1949,7 +1969,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_PauseAnimation)
TEST_F(NodeMetaInfo, is_QtQuick_Positioner)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Positioner");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Positioner");
bool isType = metaInfo.isQtQuickPositioner();
@@ -1967,7 +1987,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Positioner)
TEST_F(NodeMetaInfo, is_QtQuick_PropertyAnimation)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "PropertyAnimation");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "PropertyAnimation");
bool isType = metaInfo.isQtQuickPropertyAnimation();
@@ -1985,7 +2005,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_PropertyAnimation)
TEST_F(NodeMetaInfo, is_QtQuick_PropertyChanges)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "PropertyChanges");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "PropertyChanges");
bool isType = metaInfo.isQtQuickPropertyChanges();
@@ -2003,7 +2023,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_PropertyChanges)
TEST_F(NodeMetaInfo, is_QtQuick_Repeater)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Repeater");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Repeater");
bool isType = metaInfo.isQtQuickRepeater();
@@ -2021,7 +2041,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Repeater)
TEST_F(NodeMetaInfo, is_QtQuick_State)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "State");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "State");
bool isType = metaInfo.isQtQuickState();
@@ -2039,7 +2059,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_State)
TEST_F(NodeMetaInfo, is_QtQuickNative_StateOperation)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick-cppnative", "QQuickStateOperation");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick",
+ ModuleKind::CppLibrary,
+ "QQuickStateOperation");
bool isType = metaInfo.isQtQuickStateOperation();
@@ -2057,7 +2079,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickNative_StateOperation)
TEST_F(NodeMetaInfo, is_QtQuickStudioComponents_GroupItem)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Studio.Components", "GroupItem");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Studio.Components",
+ ModuleKind::QmlLibrary,
+ "GroupItem");
bool isType = metaInfo.isQtQuickStudioComponentsGroupItem();
@@ -2075,7 +2099,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickStudioComponents_GroupItem)
TEST_F(NodeMetaInfo, is_QtQuick_Text)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Text");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Text");
bool isType = metaInfo.isQtQuickText();
@@ -2093,7 +2117,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Text)
TEST_F(NodeMetaInfo, is_QtQuickTimeline_Keyframe)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", "Keyframe");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", ModuleKind::QmlLibrary, "Keyframe");
bool isType = metaInfo.isQtQuickTimelineKeyframe();
@@ -2111,7 +2135,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Keyframe)
TEST_F(NodeMetaInfo, is_QtQuickTimeline_KeyframeGroup)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", "KeyframeGroup");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline",
+ ModuleKind::QmlLibrary,
+ "KeyframeGroup");
bool isType = metaInfo.isQtQuickTimelineKeyframeGroup();
@@ -2129,7 +2155,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_KeyframeGroup)
TEST_F(NodeMetaInfo, is_QtQuickTimeline_Timeline)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", "Timeline");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", ModuleKind::QmlLibrary, "Timeline");
bool isType = metaInfo.isQtQuickTimelineTimeline();
@@ -2147,7 +2173,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Timeline)
TEST_F(NodeMetaInfo, is_QtQuickTimeline_TimelineAnimation)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", "TimelineAnimation");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline",
+ ModuleKind::QmlLibrary,
+ "TimelineAnimation");
bool isType = metaInfo.isQtQuickTimelineTimelineAnimation();
@@ -2165,7 +2193,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_TimelineAnimation)
TEST_F(NodeMetaInfo, is_QtQuick_Transition)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Transition");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Transition");
bool isType = metaInfo.isQtQuickTransition();
@@ -2183,7 +2211,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Transition)
TEST_F(NodeMetaInfo, is_QtQuickWindow_Window)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Window", "Window");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Window", ModuleKind::QmlLibrary, "Window");
bool isType = metaInfo.isQtQuickWindowWindow();
@@ -2201,7 +2229,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickWindow_Window)
TEST_F(NodeMetaInfo, is_QtSafeRenderer_SafeRendererPicture)
{
- auto metaInfo = createDerivedDummyMetaInfo("Qt.SafeRenderer", "SafeRendererPicture");
+ auto metaInfo = createDerivedDummyMetaInfo("Qt.SafeRenderer",
+ ModuleKind::QmlLibrary,
+ "SafeRendererPicture");
bool isType = metaInfo.isQtSafeRendererSafeRendererPicture();
@@ -2219,7 +2249,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtSafeRenderer_SafeRendererPicture)
TEST_F(NodeMetaInfo, is_QtSafeRenderer_SafePicture)
{
- auto metaInfo = createDerivedDummyMetaInfo("Qt.SafeRenderer", "SafePicture");
+ auto metaInfo = createDerivedDummyMetaInfo("Qt.SafeRenderer", ModuleKind::QmlLibrary, "SafePicture");
bool isType = metaInfo.isQtSafeRendererSafePicture();
@@ -2237,7 +2267,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtSafeRenderer_SafePicture)
TEST_F(NodeMetaInfo, is_string)
{
- auto metaInfo = createMetaInfo("QML", "string");
+ auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "string");
bool isType = metaInfo.isString();
@@ -2255,7 +2285,7 @@ TEST_F(NodeMetaInfo, default_is_not_string)
TEST_F(NodeMetaInfo, QtQuick_Item_is_suitable_for_MouseArea_fill)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Item");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Item");
bool isType = metaInfo.isSuitableForMouseAreaFill();
@@ -2264,7 +2294,7 @@ TEST_F(NodeMetaInfo, QtQuick_Item_is_suitable_for_MouseArea_fill)
TEST_F(NodeMetaInfo, QtQuick_MouseArea_is_suitable_for_MouseArea_fill)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "MouseArea");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "MouseArea");
bool isType = metaInfo.isSuitableForMouseAreaFill();
@@ -2273,7 +2303,7 @@ TEST_F(NodeMetaInfo, QtQuick_MouseArea_is_suitable_for_MouseArea_fill)
TEST_F(NodeMetaInfo, QtQuickControls_Control_is_suitable_for_MouseArea_fill)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", "Control");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", ModuleKind::QmlLibrary, "Control");
bool isType = metaInfo.isSuitableForMouseAreaFill();
@@ -2282,7 +2312,7 @@ TEST_F(NodeMetaInfo, QtQuickControls_Control_is_suitable_for_MouseArea_fill)
TEST_F(NodeMetaInfo, QtQuickTemplates_Control_is_suitable_for_MouseArea_fill)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Templates", "Control");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Templates", ModuleKind::QmlLibrary, "Control");
bool isType = metaInfo.isSuitableForMouseAreaFill();
@@ -2300,7 +2330,7 @@ TEST_F(NodeMetaInfo, default_is_not_suitable_for_MouseArea_fill)
TEST_F(NodeMetaInfo, is_url)
{
- auto metaInfo = createMetaInfo("QML", "url");
+ auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "url");
bool isType = metaInfo.isUrl();
@@ -2318,7 +2348,7 @@ TEST_F(NodeMetaInfo, default_is_not_url)
TEST_F(NodeMetaInfo, is_variant)
{
- auto metaInfo = createMetaInfo("QML", "var");
+ auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "var");
bool isType = metaInfo.isVariant();
@@ -2336,7 +2366,7 @@ TEST_F(NodeMetaInfo, default_is_not_variant)
TEST_F(NodeMetaInfo, is_vector2d)
{
- auto metaInfo = createMetaInfo("QtQuick", "vector2d");
+ auto metaInfo = createMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector2d");
bool isType = metaInfo.isVector2D();
@@ -2354,7 +2384,7 @@ TEST_F(NodeMetaInfo, default_is_not_vector2d)
TEST_F(NodeMetaInfo, is_vector3d)
{
- auto metaInfo = createMetaInfo("QtQuick", "vector3d");
+ auto metaInfo = createMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector3d");
bool isType = metaInfo.isVector3D();
@@ -2372,7 +2402,7 @@ TEST_F(NodeMetaInfo, default_is_not_vector3d)
TEST_F(NodeMetaInfo, is_vector4d)
{
- auto metaInfo = createMetaInfo("QtQuick", "vector4d");
+ auto metaInfo = createMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d");
bool isType = metaInfo.isVector4D();
@@ -2390,7 +2420,7 @@ TEST_F(NodeMetaInfo, default_is_not_vector4d)
TEST_F(NodeMetaInfo, QtQuick_ListView_is_view)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "ListView");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "ListView");
bool isType = metaInfo.isView();
@@ -2399,7 +2429,7 @@ TEST_F(NodeMetaInfo, QtQuick_ListView_is_view)
TEST_F(NodeMetaInfo, QtQuick_GridView_is_view)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "GridView");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "GridView");
bool isType = metaInfo.isView();
@@ -2408,7 +2438,7 @@ TEST_F(NodeMetaInfo, QtQuick_GridView_is_view)
TEST_F(NodeMetaInfo, QtQuick_PathView_is_view)
{
- auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "PathView");
+ auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "PathView");
bool isType = metaInfo.isView();
@@ -2428,7 +2458,7 @@ TEST_F(NodeMetaInfo, is_enumeration)
{
TypeTraits traits;
traits.isEnum = true;
- auto metaInfo = createMetaInfo("QML", "Foo", traits);
+ auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "Foo", traits);
bool isType = metaInfo.isEnumeration();
@@ -2437,7 +2467,7 @@ TEST_F(NodeMetaInfo, is_enumeration)
TEST_F(NodeMetaInfo, is_not_enumeration)
{
- auto metaInfo = createMetaInfo("QML", "Foo", {});
+ auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "Foo", {});
bool isType = metaInfo.isEnumeration();
@@ -2457,7 +2487,7 @@ TEST_F(NodeMetaInfo, all_external_type_names)
{
QmlDesigner::Storage::Info::ExportedTypeNames names{{qmlModuleId, "Object", 2, -1},
{qmlModuleId, "Obj", 2, 1}};
- auto metaInfo = createMetaInfo("QML", "Foo");
+ auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "Foo");
ON_CALL(projectStorageMock, exportedTypeNames(metaInfo.id())).WillByDefault(Return(names));
auto exportedTypeNames = metaInfo.allExportedTypeNames();
@@ -2483,7 +2513,7 @@ TEST_F(NodeMetaInfo, external_type_names_for_source_id)
{
QmlDesigner::Storage::Info::ExportedTypeNames names{{qmlModuleId, "Object", 2, -1},
{qmlModuleId, "Obj", 2, 1}};
- auto metaInfo = createMetaInfo("QML", "Foo");
+ auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "Foo");
ON_CALL(projectStorageMock, exportedTypeNames(metaInfo.id(), model.fileUrlSourceId()))
.WillByDefault(Return(names));
@@ -2511,7 +2541,7 @@ TEST_F(NodeMetaInfo, invalid_source_id_has_no_external_type_names_for_source_id)
{
QmlDesigner::Storage::Info::ExportedTypeNames names{{qmlModuleId, "Object", 2, -1},
{qmlModuleId, "Obj", 2, 1}};
- auto metaInfo = createMetaInfo("QML", "Foo");
+ auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "Foo");
ON_CALL(projectStorageMock, exportedTypeNames(metaInfo.id(), model.fileUrlSourceId()))
.WillByDefault(Return(names));
QmlDesigner::SourceId sourceId;
@@ -2523,7 +2553,7 @@ TEST_F(NodeMetaInfo, invalid_source_id_has_no_external_type_names_for_source_id)
TEST_F(NodeMetaInfo, float_is_a_number)
{
- auto metaInfo = createMetaInfo("QML-cppnative", "float");
+ auto metaInfo = createMetaInfo("QML", ModuleKind::CppLibrary, "float");
bool isType = metaInfo.isNumber();
@@ -2550,7 +2580,7 @@ TEST_F(NodeMetaInfo, int_is_a_number)
TEST_F(NodeMetaInfo, uint_is_a_number)
{
- auto metaInfo = createMetaInfo("QML-cppnative", "uint");
+ auto metaInfo = createMetaInfo("QML", ModuleKind::CppLibrary, "uint");
bool isType = metaInfo.isNumber();
@@ -2568,7 +2598,7 @@ TEST_F(NodeMetaInfo, default_is_not_number)
TEST_F(NodeMetaInfo, property_editor_specifics_path)
{
- auto metaInfo = createMetaInfo("QtQuick", "Item");
+ auto metaInfo = createMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Item");
auto pathId = QmlDesigner::SourceId::create(45);
ON_CALL(projectStorageMock, propertyEditorPathId(metaInfo.id())).WillByDefault(Return(pathId));
@@ -2588,7 +2618,7 @@ TEST_F(NodeMetaInfo, default_property_editor_specifics_path_is_empty)
TEST_F(NodeMetaInfo, is_reference)
{
- auto metaInfo = createMetaInfo("QtQuick", "Item", TypeTraitsKind::Reference);
+ auto metaInfo = createMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Item", TypeTraitsKind::Reference);
auto type = metaInfo.type();
@@ -2597,7 +2627,7 @@ TEST_F(NodeMetaInfo, is_reference)
TEST_F(NodeMetaInfo, is_value)
{
- auto metaInfo = createMetaInfo("QML", "bool", TypeTraitsKind::Value);
+ auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "bool", TypeTraitsKind::Value);
auto type = metaInfo.type();
@@ -2606,7 +2636,7 @@ TEST_F(NodeMetaInfo, is_value)
TEST_F(NodeMetaInfo, is_sequence)
{
- auto metaInfo = createMetaInfo("QML", "QObjectList", TypeTraitsKind::Sequence);
+ auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "QObjectList", TypeTraitsKind::Sequence);
auto type = metaInfo.type();
@@ -2615,7 +2645,7 @@ TEST_F(NodeMetaInfo, is_sequence)
TEST_F(NodeMetaInfo, is_none)
{
- auto metaInfo = createMetaInfo("QML", "void", TypeTraitsKind::None);
+ auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "void", TypeTraitsKind::None);
auto type = metaInfo.type();
@@ -2657,7 +2687,7 @@ TEST_F(NodeMetaInfo, invalid_can_not_be_container)
TEST_F(NodeMetaInfo, component_can_be_container)
{
- auto moduleId = projectStorageMock.createModule("/path/to/project");
+ auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary);
TypeTraits traits{TypeTraitsKind::Reference};
traits.canBeContainer = FlagIs::True;
auto typeId = projectStorageMock.createType(moduleId, "Foo", traits);
@@ -2694,7 +2724,7 @@ TEST_F(NodeMetaInfo, invalid_do_no_forces_clipping)
TEST_F(NodeMetaInfo, component_forces_clipping)
{
- auto moduleId = projectStorageMock.createModule("/path/to/project");
+ auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary);
TypeTraits traits{TypeTraitsKind::Reference};
traits.forceClip = FlagIs::True;
auto typeId = projectStorageMock.createType(moduleId, "Foo", traits);
@@ -2731,7 +2761,7 @@ TEST_F(NodeMetaInfo, invalid_does_not_layout_children)
TEST_F(NodeMetaInfo, component_layouts_children)
{
- auto moduleId = projectStorageMock.createModule("/path/to/project");
+ auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary);
TypeTraits traits{TypeTraitsKind::Reference};
traits.doesLayoutChildren = FlagIs::True;
auto typeId = projectStorageMock.createType(moduleId, "Foo", traits);
@@ -2768,7 +2798,7 @@ TEST_F(NodeMetaInfo, invalid_cannot_be_dropped_in_form_editor)
TEST_F(NodeMetaInfo, component_can_be_dropped_in_form_editor)
{
- auto moduleId = projectStorageMock.createModule("/path/to/project");
+ auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary);
TypeTraits traits{TypeTraitsKind::Reference};
traits.canBeDroppedInFormEditor = FlagIs::True;
auto typeId = projectStorageMock.createType(moduleId, "Foo", traits);
@@ -2805,7 +2835,7 @@ TEST_F(NodeMetaInfo, invalid_cannot_be_dropped_in_navigator)
TEST_F(NodeMetaInfo, component_can_be_dropped_in_navigator)
{
- auto moduleId = projectStorageMock.createModule("/path/to/project");
+ auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary);
TypeTraits traits{TypeTraitsKind::Reference};
traits.canBeDroppedInNavigator = FlagIs::True;
auto typeId = projectStorageMock.createType(moduleId, "Foo", traits);
@@ -2842,7 +2872,7 @@ TEST_F(NodeMetaInfo, invalid_cannot_be_dropped_in_3d_view)
TEST_F(NodeMetaInfo, component_can_be_dropped_in_3d_view)
{
- auto moduleId = projectStorageMock.createModule("/path/to/project");
+ auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary);
TypeTraits traits{TypeTraitsKind::Reference};
traits.canBeDroppedInView3D = FlagIs::True;
auto typeId = projectStorageMock.createType(moduleId, "Foo", traits);
@@ -2879,7 +2909,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_movable)
TEST_F(NodeMetaInfo, component_is_movable)
{
- auto moduleId = projectStorageMock.createModule("/path/to/project");
+ auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary);
TypeTraits traits{TypeTraitsKind::Reference};
traits.isMovable = FlagIs::True;
auto typeId = projectStorageMock.createType(moduleId, "Foo", traits);
@@ -2916,7 +2946,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_resizable)
TEST_F(NodeMetaInfo, component_is_resizable)
{
- auto moduleId = projectStorageMock.createModule("/path/to/project");
+ auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary);
TypeTraits traits{TypeTraitsKind::Reference};
traits.isResizable = FlagIs::True;
auto typeId = projectStorageMock.createType(moduleId, "Foo", traits);
@@ -2953,7 +2983,7 @@ TEST_F(NodeMetaInfo, invalid_has_not_form_editor_item)
TEST_F(NodeMetaInfo, component_has_form_editor_item)
{
- auto moduleId = projectStorageMock.createModule("/path/to/project");
+ auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary);
TypeTraits traits{TypeTraitsKind::Reference};
traits.hasFormEditorItem = FlagIs::True;
auto typeId = projectStorageMock.createType(moduleId, "Foo", traits);
@@ -2990,7 +3020,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_stacked_container)
TEST_F(NodeMetaInfo, component_is_stacked_container)
{
- auto moduleId = projectStorageMock.createModule("/path/to/project");
+ auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary);
TypeTraits traits{TypeTraitsKind::Reference};
traits.isStackedContainer = FlagIs::True;
auto typeId = projectStorageMock.createType(moduleId, "Foo", traits);
@@ -3027,7 +3057,7 @@ TEST_F(NodeMetaInfo, invalid_dont_takes_over_rendering_of_children)
TEST_F(NodeMetaInfo, component_takes_over_rendering_of_children)
{
- auto moduleId = projectStorageMock.createModule("/path/to/project");
+ auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary);
TypeTraits traits{TypeTraitsKind::Reference};
traits.takesOverRenderingOfChildren = FlagIs::True;
auto typeId = projectStorageMock.createType(moduleId, "Foo", traits);
@@ -3064,7 +3094,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_visible_in_navigator)
TEST_F(NodeMetaInfo, component_is_visible_in_navigator)
{
- auto moduleId = projectStorageMock.createModule("/path/to/project");
+ auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary);
TypeTraits traits{TypeTraitsKind::Reference};
traits.visibleInNavigator = FlagIs::True;
auto typeId = projectStorageMock.createType(moduleId, "Foo", traits);
@@ -3101,7 +3131,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_visible_in_library)
TEST_F(NodeMetaInfo, component_is_visible_in_library)
{
- auto moduleId = projectStorageMock.createModule("/path/to/project");
+ auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary);
TypeTraits traits{TypeTraitsKind::Reference};
traits.visibleInLibrary = FlagIs::True;
auto typeId = projectStorageMock.createType(moduleId, "Foo", traits);
@@ -3152,6 +3182,7 @@ TEST_F(NodeMetaInfo, item_library_entries)
{
projectStorageMock.setItemLibraryEntries(objectMetaInfo.id(),
{{objectMetaInfo.id(),
+ "QtObject",
"Object",
"/icon/path",
"Basic",
@@ -3163,6 +3194,7 @@ TEST_F(NodeMetaInfo, item_library_entries)
ASSERT_THAT(entries,
ElementsAre(IsItemLibraryEntry(objectMetaInfo.id(),
+ "QtObject",
"Object",
"/icon/path",
"Basic",
diff --git a/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp
index 25436264ae..d2ec90b7a8 100644
--- a/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp
+++ b/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp
@@ -22,6 +22,7 @@ namespace {
using QmlDesigner::Enumeration;
using QmlDesigner::ModelNode;
using QmlDesigner::ModelNodes;
+using QmlDesigner::Storage::ModuleKind;
using QmlDesigner::Storage::PropertyDeclarationTraits;
using QmlDesigner::Storage::TypeTraits;
@@ -29,10 +30,11 @@ class PropertyMetaInfo : public ::testing::Test
{
protected:
QmlDesigner::NodeMetaInfo createNodeMetaInfo(Utils::SmallStringView moduleName,
+ ModuleKind moduleKind,
Utils::SmallStringView typeName,
QmlDesigner::Storage::TypeTraits typeTraits = {})
{
- auto moduleId = projectStorageMock.createModule(moduleName);
+ auto moduleId = projectStorageMock.createModule(moduleName, moduleKind);
auto typeId = projectStorageMock.createType(moduleId, typeName, typeTraits);
return QmlDesigner::NodeMetaInfo{typeId, &projectStorageMock};
@@ -47,7 +49,7 @@ protected:
QmlDesigner::Import::createLibraryImport("QtQuick"),
QmlDesigner::Import::createLibraryImport("QtQml.Models")},
QUrl::fromLocalFile(pathCache.path.toQString())};
- QmlDesigner::NodeMetaInfo nodeInfo = createNodeMetaInfo("QtQuick", "Foo");
+ QmlDesigner::NodeMetaInfo nodeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Foo");
};
TEST_F(PropertyMetaInfo, name)
@@ -71,7 +73,7 @@ TEST_F(PropertyMetaInfo, default_has_no_name)
TEST_F(PropertyMetaInfo, property_type)
{
- auto barInfo = createNodeMetaInfo("QtQuick", "Bar");
+ auto barInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Bar");
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, barInfo.id());
auto propertyInfo = nodeInfo.property("bar");
@@ -91,7 +93,7 @@ TEST_F(PropertyMetaInfo, default_hads_invalid_property_type)
TEST_F(PropertyMetaInfo, type)
{
- auto barInfo = createNodeMetaInfo("QtQuick", "Bar");
+ auto barInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Bar");
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, barInfo.id());
auto propertyInfo = nodeInfo.property("bar");
@@ -181,7 +183,7 @@ TEST_F(PropertyMetaInfo, is_enumeration)
{
TypeTraits traits;
traits.isEnum = true;
- auto enumInfo = createNodeMetaInfo("QtQuick", "MyEnum", traits);
+ auto enumInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "MyEnum", traits);
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, enumInfo.id());
auto propertyInfo = nodeInfo.property("bar");
@@ -192,7 +194,7 @@ TEST_F(PropertyMetaInfo, is_enumeration)
TEST_F(PropertyMetaInfo, is_not_enumeration)
{
- auto notEnumInfo = createNodeMetaInfo("QtQuick", "NoEnum", {});
+ auto notEnumInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "NoEnum", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, notEnumInfo.id());
auto propertyInfo = nodeInfo.property("bar");
@@ -275,7 +277,7 @@ TEST_F(PropertyMetaInfo, cast_to_enumeration)
{
TypeTraits traits;
traits.isEnum = true;
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "MyEnum", traits);
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "MyEnum", traits);
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
Enumeration enumeration{"MyEnum.Foo"};
@@ -288,7 +290,7 @@ TEST_F(PropertyMetaInfo, cast_to_enumeration)
TEST_F(PropertyMetaInfo, dont_to_cast_enumeration_if_property_type_is_not_enumeration)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "MyEnum", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "MyEnum", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
Enumeration enumeration{"MyEnum.Foo"};
@@ -303,7 +305,7 @@ TEST_F(PropertyMetaInfo, dont_to_cast_enumeration_if_value_is_not_Enumeration)
{
TypeTraits traits;
traits.isEnum = true;
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "MyEnum", traits);
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "MyEnum", traits);
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(QString{"enumeration"});
@@ -315,7 +317,7 @@ TEST_F(PropertyMetaInfo, dont_to_cast_enumeration_if_value_is_not_Enumeration)
TEST_F(PropertyMetaInfo, cast_to_model_node)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "var", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "var", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(model.rootModelNode());
@@ -327,7 +329,7 @@ TEST_F(PropertyMetaInfo, cast_to_model_node)
TEST_F(PropertyMetaInfo, cast_to_qvariant_always_returns_the_save_variant_if_the_property_type_is_var)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "var", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "var", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(QString{"foo"});
@@ -339,7 +341,7 @@ TEST_F(PropertyMetaInfo, cast_to_qvariant_always_returns_the_save_variant_if_the
TEST_F(PropertyMetaInfo, cast_double_to_double)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "double", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "double", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(14.2);
@@ -351,7 +353,7 @@ TEST_F(PropertyMetaInfo, cast_double_to_double)
TEST_F(PropertyMetaInfo, cast_int_to_double_returns_number_variant)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "double", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "double", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(14);
@@ -363,7 +365,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_double_returns_number_variant)
TEST_F(PropertyMetaInfo, cast_default_to_double_returns_zero_variant)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "double", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "double", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant();
@@ -375,7 +377,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_double_returns_zero_variant)
TEST_F(PropertyMetaInfo, cast_qstring_to_double_returns_zero_variant)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "double", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "double", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(QString{"foo"});
@@ -387,7 +389,7 @@ TEST_F(PropertyMetaInfo, cast_qstring_to_double_returns_zero_variant)
TEST_F(PropertyMetaInfo, cast_float_to_float)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML-cppnative", "float", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::CppLibrary, "float", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(14.2f);
@@ -399,7 +401,7 @@ TEST_F(PropertyMetaInfo, cast_float_to_float)
TEST_F(PropertyMetaInfo, cast_int_to_float_returns_number_variant)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML-cppnative", "float", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::CppLibrary, "float", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(14);
@@ -411,7 +413,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_float_returns_number_variant)
TEST_F(PropertyMetaInfo, cast_default_to_float_returns_zero_variant)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML-cppnative", "float", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::CppLibrary, "float", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant();
@@ -423,7 +425,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_float_returns_zero_variant)
TEST_F(PropertyMetaInfo, cast_qstring_to_float_returns_zero_variant)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML-cppnative", "float", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::CppLibrary, "float", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(QString{"foo"});
@@ -435,7 +437,7 @@ TEST_F(PropertyMetaInfo, cast_qstring_to_float_returns_zero_variant)
TEST_F(PropertyMetaInfo, cast_int_to_int)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "int", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "int", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(14);
@@ -447,7 +449,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_int)
TEST_F(PropertyMetaInfo, cast_double_to_int_returns_number_variant)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "int", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "int", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(14.2);
@@ -459,7 +461,7 @@ TEST_F(PropertyMetaInfo, cast_double_to_int_returns_number_variant)
TEST_F(PropertyMetaInfo, cast_default_to_int_returns_zero_variant)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "int", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "int", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant();
@@ -471,7 +473,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_int_returns_zero_variant)
TEST_F(PropertyMetaInfo, cast_qstring_to_int_returns_zero_variant)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "int", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "int", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(QString{"foo"});
@@ -483,7 +485,7 @@ TEST_F(PropertyMetaInfo, cast_qstring_to_int_returns_zero_variant)
TEST_F(PropertyMetaInfo, cast_bool_to_bool)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(true);
@@ -495,7 +497,7 @@ TEST_F(PropertyMetaInfo, cast_bool_to_bool)
TEST_F(PropertyMetaInfo, cast_float_to_bool_returns_boolean_variant)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(14.2f);
@@ -507,7 +509,7 @@ TEST_F(PropertyMetaInfo, cast_float_to_bool_returns_boolean_variant)
TEST_F(PropertyMetaInfo, cast_double_to_bool_returns_boolean_variant)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(14.2);
@@ -519,7 +521,7 @@ TEST_F(PropertyMetaInfo, cast_double_to_bool_returns_boolean_variant)
TEST_F(PropertyMetaInfo, cast_int_to_bool_returns_boolean_variant)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(14);
@@ -531,7 +533,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_bool_returns_boolean_variant)
TEST_F(PropertyMetaInfo, cast_long_to_bool_returns_boolean_variant)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(14L);
@@ -543,7 +545,7 @@ TEST_F(PropertyMetaInfo, cast_long_to_bool_returns_boolean_variant)
TEST_F(PropertyMetaInfo, cast_long_long_to_bool_returns_boolean_variant)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(14LL);
@@ -555,7 +557,7 @@ TEST_F(PropertyMetaInfo, cast_long_long_to_bool_returns_boolean_variant)
TEST_F(PropertyMetaInfo, cast_default_to_bool_returns_zero_variant)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant();
@@ -567,7 +569,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_bool_returns_zero_variant)
TEST_F(PropertyMetaInfo, cast_qstring_to_bool_returns_zero_variant)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(QString{"foo"});
@@ -579,7 +581,7 @@ TEST_F(PropertyMetaInfo, cast_qstring_to_bool_returns_zero_variant)
TEST_F(PropertyMetaInfo, cast_string_to_string)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "string", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "string", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(QString{"foo"});
@@ -591,7 +593,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_string)
TEST_F(PropertyMetaInfo, cast_QByteArray_to_empty_string)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "string", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "string", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(QByteArray{"foo"});
@@ -603,7 +605,7 @@ TEST_F(PropertyMetaInfo, cast_QByteArray_to_empty_string)
TEST_F(PropertyMetaInfo, cast_int_to_empty_string)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "string", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "string", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(14);
@@ -615,7 +617,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_empty_string)
TEST_F(PropertyMetaInfo, cast_default_to_empty_string)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "string", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "string", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant();
@@ -627,7 +629,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_empty_string)
TEST_F(PropertyMetaInfo, cast_datatime_to_datetime)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "date", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "date", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto dataTime = QDateTime::currentDateTime();
@@ -640,7 +642,7 @@ TEST_F(PropertyMetaInfo, cast_datatime_to_datetime)
TEST_F(PropertyMetaInfo, cast_int_to_datetime_returns_default_datetime)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "date", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "date", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(14);
@@ -652,7 +654,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_datetime_returns_default_datetime)
TEST_F(PropertyMetaInfo, cast_string_to_datetime_returns_default_datetime)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "date", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "date", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(QString{"Monday"});
@@ -664,7 +666,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_datetime_returns_default_datetime)
TEST_F(PropertyMetaInfo, cast_default_to_datetime_returns_default_datetime)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "date", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "date", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant();
@@ -676,7 +678,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_datetime_returns_default_datetime)
TEST_F(PropertyMetaInfo, cast_url_to_url)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "url", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "url", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto url = QUrl("http://www.qt.io/future");
@@ -689,7 +691,7 @@ TEST_F(PropertyMetaInfo, cast_url_to_url)
TEST_F(PropertyMetaInfo, cast_string_to_empty_url)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "url", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "url", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant::fromValue(QString{"http://www.qt.io/future"});
@@ -701,7 +703,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_empty_url)
TEST_F(PropertyMetaInfo, cast_default_to_empty_url)
{
- auto propertyTypeInfo = createNodeMetaInfo("QML", "url", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "url", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant();
@@ -713,7 +715,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_empty_url)
TEST_F(PropertyMetaInfo, cast_color_to_color)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "color", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "color", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto color = QColor(Qt::red);
@@ -726,7 +728,7 @@ TEST_F(PropertyMetaInfo, cast_color_to_color)
TEST_F(PropertyMetaInfo, cast_string_to_null_color)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "color", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "color", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant("red");
@@ -738,7 +740,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_null_color)
TEST_F(PropertyMetaInfo, cast_int_to_null_color)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "color", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "color", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant(14);
@@ -750,7 +752,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_null_color)
TEST_F(PropertyMetaInfo, cast_default_to_null_color)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "color", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "color", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant();
@@ -762,7 +764,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_null_color)
TEST_F(PropertyMetaInfo, cast_vector2d_to_vector2d)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector2d", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector2d", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto vector2d = QVector2D{32.2f, 2.2f};
@@ -775,7 +777,7 @@ TEST_F(PropertyMetaInfo, cast_vector2d_to_vector2d)
TEST_F(PropertyMetaInfo, cast_string_to_vector2d_returns_an_empty_vector2d)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector2d", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector2d", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant(QString{"foo"});
@@ -787,7 +789,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_vector2d_returns_an_empty_vector2d)
TEST_F(PropertyMetaInfo, cast_int_to_vector2d_returns_an_empty_vector2d)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector2d", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector2d", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant(12);
@@ -799,7 +801,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_vector2d_returns_an_empty_vector2d)
TEST_F(PropertyMetaInfo, cast_vector3d_to_vector2d_returns_an_empty_vector2d)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector2d", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector2d", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant(QVector3D{32.2f, 2.2f, 784.f});
@@ -811,7 +813,7 @@ TEST_F(PropertyMetaInfo, cast_vector3d_to_vector2d_returns_an_empty_vector2d)
TEST_F(PropertyMetaInfo, cast_default_to_vector2d_returns_an_empty_vector2d)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector2d", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector2d", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant();
@@ -823,7 +825,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_vector2d_returns_an_empty_vector2d)
TEST_F(PropertyMetaInfo, cast_vector3d_to_vector3d)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector3d", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector3d", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto vector3d = QVector3D{32.2f, 2.2f, 44.4f};
@@ -836,7 +838,7 @@ TEST_F(PropertyMetaInfo, cast_vector3d_to_vector3d)
TEST_F(PropertyMetaInfo, cast_string_to_vector3d_returns_an_empty_vector3d)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector3d", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector3d", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant(QString{"foo"});
@@ -848,7 +850,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_vector3d_returns_an_empty_vector3d)
TEST_F(PropertyMetaInfo, cast_int_to_vector3d_returns_an_empty_vector3d)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector3d", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector3d", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant(12);
@@ -860,7 +862,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_vector3d_returns_an_empty_vector3d)
TEST_F(PropertyMetaInfo, cast_vector4d_to_vector3d_returns_an_empty_vector3d)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector3d", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector3d", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant(QVector4D{32.2f, 2.2f, 784.f, 99.f});
@@ -872,7 +874,7 @@ TEST_F(PropertyMetaInfo, cast_vector4d_to_vector3d_returns_an_empty_vector3d)
TEST_F(PropertyMetaInfo, cast_default_to_vector3d_returns_an_empty_vector3d)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector3d", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector3d", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant();
@@ -884,7 +886,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_vector3d_returns_an_empty_vector3d)
TEST_F(PropertyMetaInfo, cast_vector4d_to_vector4d)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto vector4d = QVector4D{32.2f, 2.2f, 44.4f, 23.f};
@@ -897,7 +899,7 @@ TEST_F(PropertyMetaInfo, cast_vector4d_to_vector4d)
TEST_F(PropertyMetaInfo, cast_string_to_vector4d_returns_an_empty_vector4d)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant(QString{"foo"});
@@ -909,7 +911,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_vector4d_returns_an_empty_vector4d)
TEST_F(PropertyMetaInfo, cast_int_to_vector4d_returns_an_empty_vector4d)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant(12);
@@ -921,7 +923,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_vector4d_returns_an_empty_vector4d)
TEST_F(PropertyMetaInfo, cast_vector2d_to_vector4d_returns_an_empty_vector4d)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant(QVector2D{32.2f, 2.2f});
@@ -933,7 +935,7 @@ TEST_F(PropertyMetaInfo, cast_vector2d_to_vector4d_returns_an_empty_vector4d)
TEST_F(PropertyMetaInfo, cast_default_to_vector4d_returns_an_empty_vector4d)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d", {});
projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id());
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant();
@@ -955,7 +957,7 @@ TEST_F(PropertyMetaInfo, default_cast_to_invalid_variant)
TEST_F(PropertyMetaInfo, not_existing_property_cast_returns_invalid_value)
{
- auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {});
+ auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d", {});
auto propertyInfo = nodeInfo.property("bar");
auto value = QVariant(43);
diff --git a/tests/unit/tests/unittests/model/CMakeLists.txt b/tests/unit/tests/unittests/model/CMakeLists.txt
index 75c81cca6f..43d6154c50 100644
--- a/tests/unit/tests/unittests/model/CMakeLists.txt
+++ b/tests/unit/tests/unittests/model/CMakeLists.txt
@@ -7,5 +7,5 @@ extend_qtc_test(unittest
modelresourcemanagement-test.cpp
modelutils-test.cpp
nodelistproperty-test.cpp
-
+ uniquename-test.cpp
)
diff --git a/tests/unit/tests/unittests/model/model-test.cpp b/tests/unit/tests/unittests/model/model-test.cpp
index bd34a3a6b1..227360c01f 100644
--- a/tests/unit/tests/unittests/model/model-test.cpp
+++ b/tests/unit/tests/unittests/model/model-test.cpp
@@ -26,6 +26,7 @@ using QmlDesigner::AbstractProperty;
using QmlDesigner::ModelNode;
using QmlDesigner::ModelNodes;
using QmlDesigner::ModelResourceSet;
+using QmlDesigner::Storage::ModuleKind;
MATCHER(IsSorted, std::string(negation ? "isn't sorted" : "is sorted"))
{
@@ -35,7 +36,8 @@ MATCHER(IsSorted, std::string(negation ? "isn't sorted" : "is sorted"))
}
template<typename PropertiesMatcher, typename ExtraFilePathsMatcher>
-auto IsItemLibraryEntry(const QmlDesigner::NodeMetaInfo &metaInfo,
+auto IsItemLibraryEntry(QmlDesigner::TypeId typeId,
+ QByteArrayView typeName,
QStringView name,
QStringView iconPath,
QStringView category,
@@ -46,7 +48,8 @@ auto IsItemLibraryEntry(const QmlDesigner::NodeMetaInfo &metaInfo,
ExtraFilePathsMatcher extraFilePathsMatcher)
{
using QmlDesigner::ItemLibraryEntry;
- return AllOf(Property("metaInfo", &ItemLibraryEntry::metaInfo, metaInfo),
+ return AllOf(Property("typeId", &ItemLibraryEntry::typeId, typeId),
+ Property("typeName", &ItemLibraryEntry::typeName, typeName),
Property("name", &ItemLibraryEntry::name, name),
Property("libraryEntryIconPath", &ItemLibraryEntry::libraryEntryIconPath, iconPath),
Property("category", &ItemLibraryEntry::category, category),
@@ -125,10 +128,10 @@ protected:
resourceManagementMock)};
NiceMock<AbstractViewMock> viewMock;
QmlDesigner::SourceId filePathId = pathCacheMock.sourceId;
- QmlDesigner::TypeId itemTypeId = projectStorageMock.typeId(projectStorageMock.moduleId(
- "QtQuick"),
- "Item",
- QmlDesigner::Storage::Version{});
+ QmlDesigner::TypeId itemTypeId = projectStorageMock.typeId(
+ projectStorageMock.moduleId("QtQuick", ModuleKind::QmlLibrary),
+ "Item",
+ QmlDesigner::Storage::Version{});
QmlDesigner::ImportedTypeNameId itemTypeNameId = projectStorageMock.createImportedTypeNameId(
filePathId, "Item", itemTypeId);
ModelNode rootNode;
@@ -759,8 +762,8 @@ TEST_F(Model, change_imports_is_synchronizing_imports_with_project_storage)
{
QmlDesigner::SourceId directoryPathId = QmlDesigner::SourceId::create(2);
ON_CALL(pathCacheMock, sourceId(Eq("/path/foo/."))).WillByDefault(Return(directoryPathId));
- auto qtQuickModuleId = projectStorageMock.moduleId("QtQuick");
- auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models");
+ auto qtQuickModuleId = projectStorageMock.moduleId("QtQuick", ModuleKind::QmlLibrary);
+ auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models", ModuleKind::QmlLibrary);
auto qtQuickImport = QmlDesigner::Import::createLibraryImport("QtQuick", "2.1");
auto qtQmlModelsImport = QmlDesigner::Import::createLibraryImport("QtQml.Models");
auto directoryImport = QmlDesigner::Import::createFileImport("foo");
@@ -793,8 +796,8 @@ TEST_F(Model, change_imports_is_adding_import_in_project_storage)
{
QmlDesigner::SourceId directoryPathId = QmlDesigner::SourceId::create(2);
ON_CALL(pathCacheMock, sourceId(Eq("/path/foo/."))).WillByDefault(Return(directoryPathId));
- auto qtQuickModuleId = projectStorageMock.moduleId("QtQuick");
- auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models");
+ auto qtQuickModuleId = projectStorageMock.moduleId("QtQuick", ModuleKind::QmlLibrary);
+ auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models", ModuleKind::QmlLibrary);
auto qtQuickImport = QmlDesigner::Import::createLibraryImport("QtQuick", "2.1");
auto qtQmlModelsImport = QmlDesigner::Import::createLibraryImport("QtQml.Models");
auto directoryImport = QmlDesigner::Import::createFileImport("foo");
@@ -813,7 +816,7 @@ TEST_F(Model, change_imports_is_removing_import_in_project_storage)
{
QmlDesigner::SourceId directoryPathId = QmlDesigner::SourceId::create(2);
ON_CALL(pathCacheMock, sourceId(Eq("/path/foo/."))).WillByDefault(Return(directoryPathId));
- auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models");
+ auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models", ModuleKind::QmlLibrary);
auto qtQuickImport = QmlDesigner::Import::createLibraryImport("QtQuick", "2.1");
auto qtQmlModelsImport = QmlDesigner::Import::createLibraryImport("QtQml.Models");
auto directoryImport = QmlDesigner::Import::createFileImport("foo");
@@ -846,8 +849,8 @@ TEST_F(Model, change_imports_is_changing_import_version_with_project_storage)
{
QmlDesigner::SourceId directoryPathId = QmlDesigner::SourceId::create(2);
ON_CALL(pathCacheMock, sourceId(Eq("/path/foo/."))).WillByDefault(Return(directoryPathId));
- auto qtQuickModuleId = projectStorageMock.moduleId("QtQuick");
- auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models");
+ auto qtQuickModuleId = projectStorageMock.moduleId("QtQuick", ModuleKind::QmlLibrary);
+ auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models", ModuleKind::QmlLibrary);
auto qtQuickImport = QmlDesigner::Import::createLibraryImport("QtQuick", "2.1");
auto qtQmlModelsImport = QmlDesigner::Import::createLibraryImport("QtQml.Models");
auto directoryImport = QmlDesigner::Import::createFileImport("foo");
@@ -873,7 +876,7 @@ TEST_F(Model, create_model_node_has_meta_info)
TEST_F(Model, create_qualified_model_node_has_meta_info)
{
auto qtQmlModelsImport = QmlDesigner::Import::createLibraryImport("QtQml.Models", "", "Foo");
- auto qtQmlModelsModulesId = projectStorageMock.moduleId("QtQml.Models");
+ auto qtQmlModelsModulesId = projectStorageMock.moduleId("QtQml.Models", ModuleKind::QmlLibrary);
auto importId = projectStorageMock.createImportId(qtQmlModelsModulesId, filePathId);
auto listModelTypeId = projectStorageMock.typeId(qtQmlModelsModulesId,
"ListModel",
@@ -913,23 +916,23 @@ TEST_F(Model, meta_info_of_not_existing_type_is_invalid)
TEST_F(Model, module_is_valid)
{
- auto module = model.module("QML");
+ auto module = model.module("QML", ModuleKind::QmlLibrary);
ASSERT_THAT(module, IsTrue());
}
TEST_F(Model, module_returns_always_the_same)
{
- auto oldModule = model.module("QML");
+ auto oldModule = model.module("QML", ModuleKind::QmlLibrary);
- auto module = model.module("QML");
+ auto module = model.module("QML", ModuleKind::QmlLibrary);
ASSERT_THAT(module, oldModule);
}
TEST_F(Model, get_meta_info_by_module)
{
- auto module = model.module("QML");
+ auto module = model.module("QML", ModuleKind::QmlLibrary);
auto metaInfo = model.metaInfo(module, "QtObject");
@@ -938,7 +941,7 @@ TEST_F(Model, get_meta_info_by_module)
TEST_F(Model, get_invalid_meta_info_by_module_for_wrong_name)
{
- auto module = model.module("QML");
+ auto module = model.module("QML", ModuleKind::QmlLibrary);
auto metaInfo = model.metaInfo(module, "Object");
@@ -947,7 +950,7 @@ TEST_F(Model, get_invalid_meta_info_by_module_for_wrong_name)
TEST_F(Model, get_invalid_meta_info_by_module_for_wrong_module)
{
- auto module = model.module("Qml");
+ auto module = model.module("Qml", ModuleKind::QmlLibrary);
auto metaInfo = model.metaInfo(module, "Object");
@@ -986,8 +989,8 @@ TEST_F(Model, refresh_callback_is_calling_abstract_view)
TEST_F(Model, meta_infos_for_mdoule)
{
- projectStorageMock.createModule("Foo");
- auto module = model.module("Foo");
+ projectStorageMock.createModule("Foo", ModuleKind::QmlLibrary);
+ auto module = model.module("Foo", ModuleKind::QmlLibrary);
auto typeId = projectStorageMock.createObject(module.id(), "Bar");
ON_CALL(projectStorageMock, typeIds(module.id()))
.WillByDefault(Return(QVarLengthArray<QmlDesigner::TypeId, 256>{typeId}));
@@ -1000,18 +1003,24 @@ TEST_F(Model, meta_infos_for_mdoule)
TEST_F(Model, item_library_entries)
{
using namespace Qt::StringLiterals;
- QmlDesigner::Storage::Info::ItemLibraryEntries storageEntries{
- {itemTypeId, "Item", "/path/to/icon", "basic category", "QtQuick", "It's a item", "/path/to/template"}};
+ QmlDesigner::Storage::Info::ItemLibraryEntries storageEntries{{itemTypeId,
+ "Item",
+ "Item",
+ "/path/to/icon",
+ "basic category",
+ "QtQuick",
+ "It's a item",
+ "/path/to/template"}};
storageEntries.front().properties.emplace_back("x", "double", Sqlite::ValueView::create(1));
storageEntries.front().extraFilePaths.emplace_back("/extra/file/path");
projectStorageMock.setItemLibraryEntries(pathCacheMock.sourceId, storageEntries);
- QmlDesigner::NodeMetaInfo metaInfo{itemTypeId, &projectStorageMock};
auto entries = model.itemLibraryEntries();
ASSERT_THAT(entries,
ElementsAre(
- IsItemLibraryEntry(metaInfo,
+ IsItemLibraryEntry(itemTypeId,
+ "Item",
u"Item",
u"/path/to/icon",
u"basic category",
diff --git a/tests/unit/tests/unittests/model/modelutils-test.cpp b/tests/unit/tests/unittests/model/modelutils-test.cpp
index 5a9e63b60d..2d49c6c94e 100644
--- a/tests/unit/tests/unittests/model/modelutils-test.cpp
+++ b/tests/unit/tests/unittests/model/modelutils-test.cpp
@@ -13,6 +13,7 @@
namespace {
using QmlDesigner::ModelNode;
using QmlDesigner::ModelNodes;
+using QmlDesigner::Storage::ModuleKind;
class ModelUtils : public ::testing::Test
{
@@ -20,7 +21,7 @@ protected:
NiceMock<SourcePathCacheMockWithPaths> pathCacheMock{"/path/model.qml"};
QmlDesigner::SourceId sourceId = pathCacheMock.createSourceId("/path/foo.qml");
NiceMock<ProjectStorageMockWithQtQtuick> projectStorageMock{pathCacheMock.sourceId};
- QmlDesigner::ModuleId moduleId = projectStorageMock.moduleId("QtQuick");
+ QmlDesigner::ModuleId moduleId = projectStorageMock.moduleId("QtQuick", ModuleKind::QmlLibrary);
QmlDesigner::Model model{{projectStorageMock, pathCacheMock},
"Item",
{QmlDesigner::Import::createLibraryImport("QML"),
diff --git a/tests/unit/tests/unittests/model/nodelistproperty-test.cpp b/tests/unit/tests/unittests/model/nodelistproperty-test.cpp
index aac2e729a2..238e2f5d15 100644
--- a/tests/unit/tests/unittests/model/nodelistproperty-test.cpp
+++ b/tests/unit/tests/unittests/model/nodelistproperty-test.cpp
@@ -44,11 +44,6 @@ protected:
~NodeListProperty() { model->detachView(&abstractViewMock); }
- void setModuleId(Utils::SmallStringView moduleName, ModuleId moduleId)
- {
- ON_CALL(projectStorageMock, moduleId(Eq(moduleName))).WillByDefault(Return(moduleId));
- }
-
void setType(ModuleId moduleId,
Utils::SmallStringView typeName,
Utils::SmallString defaultPeopertyName)
diff --git a/tests/unit/tests/unittests/model/uniquename-test.cpp b/tests/unit/tests/unittests/model/uniquename-test.cpp
new file mode 100644
index 0000000000..ca32972b6d
--- /dev/null
+++ b/tests/unit/tests/unittests/model/uniquename-test.cpp
@@ -0,0 +1,112 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <googletest.h>
+
+#include <uniquename.h>
+
+namespace {
+
+namespace UniqueName = QmlDesigner::UniqueName;
+
+TEST(UniqueName, generate_returns_same_input_if_predicate_returns_false)
+{
+ auto pred = [] (const QString &name) -> bool {
+ return false;
+ };
+ QString name = "abc";
+
+ QString uniqueName = UniqueName::generate(name, pred);
+
+ ASSERT_THAT(uniqueName, "abc");
+}
+
+TEST(UniqueName, generateId_returns_properly_formatted_id_when_predicate_is_not_provided)
+{
+ QString id = " A bc d _";
+
+ QString uniqueId = UniqueName::generateId(id);
+
+ ASSERT_THAT(uniqueId, "aBcD_");
+}
+
+TEST(UniqueName, generateId_returns_properly_formatted_id)
+{
+ auto pred = [] (const QString &id) -> bool {
+ return false;
+ };
+ QString id = " A bc d _";
+
+ QString uniqueId = UniqueName::generateId(id, pred);
+
+ ASSERT_THAT(uniqueId, "aBcD_");
+}
+
+TEST(UniqueName, generateId_returns_properly_formatted_unique_id_when_id_exists)
+{
+ QStringList existingIds = {"aBcD009", "aBcD010"};
+ auto pred = [&] (const QString &id) -> bool {
+ return existingIds.contains(id);
+ };
+ QString id = " A bc d 0 \t 0 9\n";
+
+ QString uniqueId = UniqueName::generateId(id, pred);
+
+ ASSERT_THAT(uniqueId, "aBcD011");
+}
+
+TEST(UniqueName, generateId_properly_handles_dot_separated_words)
+{
+ auto pred = [&] (const QString &id) -> bool {
+ return false;
+ };
+ QString id = "Foo.bar*foo";
+
+ QString uniqueId = UniqueName::generateId(id, pred);
+
+ ASSERT_THAT(uniqueId, "fooBarFoo");
+}
+
+TEST(UniqueName, generateId_prefixes_with_underscore_if_id_is_a_reserved_word)
+{
+ auto pred = [&] (const QString &id) -> bool {
+ return false;
+ };
+ QString id = "for";
+
+ QString uniqueId = UniqueName::generateId(id, pred);
+
+ ASSERT_THAT(uniqueId, "_for");
+}
+
+TEST(UniqueName, generateId_prefixes_with_underscore_if_id_is_a_number)
+{
+ auto pred = [&] (const QString &id) -> bool {
+ return false;
+ };
+ QString id = "436";
+
+ QString uniqueId = UniqueName::generateId(id, pred);
+
+ ASSERT_THAT(uniqueId, "_436");
+}
+
+TEST(UniqueName, generatePath_returns_same_path_when_path_doesnt_exist)
+{
+ QString path = "<<<non/existing/path>>>";
+
+ QString uniquePath = UniqueName::generatePath(path);
+
+ ASSERT_THAT(uniquePath, path);
+}
+
+TEST(UniqueName, generatePath_returns_unique_path_when_path_exists)
+{
+ QString path = UNITTEST_DIR;
+
+ QString uniquePath = UniqueName::generatePath(path);
+
+ ASSERT_THAT(uniquePath, QString(UNITTEST_DIR).append("1"));
+}
+
+} // namespace
diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp
index 4034ae58f9..fc806d73a9 100644
--- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp
+++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp
@@ -5,7 +5,8 @@
#include <matchers/info_exportedtypenames-matcher.h>
#include <matchers/projectstorage-matcher.h>
-#include <mocks/projectstorageobservermock.h>
+#include <projectstorageerrornotifiermock.h>
+#include <projectstorageobservermock.h>
#include <modelnode.h>
#include <projectstorage/projectstorage.h>
@@ -28,6 +29,7 @@ using QmlDesigner::PropertyDeclarationId;
using QmlDesigner::SourceContextId;
using QmlDesigner::SourceId;
using QmlDesigner::SourceIds;
+using QmlDesigner::Storage::ModuleKind;
using QmlDesigner::Storage::Synchronization::SynchronizationPackage;
using QmlDesigner::Storage::Synchronization::TypeAnnotations;
using QmlDesigner::Storage::TypeTraits;
@@ -48,6 +50,12 @@ Storage::Imports operator+(const Storage::Imports &first,
return imports;
}
+auto IsModule(Utils::SmallStringView name, ModuleKind kind)
+{
+ return AllOf(Field(&QmlDesigner::Storage::Module::name, name),
+ Field(&QmlDesigner::Storage::Module::kind, kind));
+}
+
MATCHER_P2(IsSourceContext,
id,
value,
@@ -197,6 +205,23 @@ MATCHER_P4(IsInfoPropertyDeclaration,
&& propertyDeclaration.traits == traits;
}
+auto IsUnresolvedTypeId()
+{
+ return Property(&QmlDesigner::TypeId::internalId, -1);
+}
+
+template<typename Matcher>
+auto IsPrototypeId(const Matcher &matcher)
+{
+ return Field(&Storage::Synchronization::Type::prototypeId, matcher);
+}
+
+template<typename Matcher>
+auto IsExtensionId(const Matcher &matcher)
+{
+ return Field(&Storage::Synchronization::Type::extensionId, matcher);
+}
+
class HasNameMatcher
{
public:
@@ -267,21 +292,20 @@ MATCHER_P2(IsInfoType,
class ProjectStorage : public testing::Test
{
protected:
- static void SetUpTestSuite()
+ struct StaticData
{
- static_database = std::make_unique<Sqlite::Database>(":memory:", Sqlite::JournalMode::Memory);
+ Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
+ NiceMock<ProjectStorageErrorNotifierMock> errorNotifierMock;
+ QmlDesigner::ProjectStorage storage{database, errorNotifierMock, database.isInitialized()};
+ };
- static_projectStorage = std::make_unique<QmlDesigner::ProjectStorage>(
- *static_database, static_database->isInitialized());
- }
+ static void SetUpTestSuite() { staticData = std::make_unique<StaticData>(); }
- static void TearDownTestSuite()
- {
- static_projectStorage.reset();
- static_database.reset();
- }
+ static void TearDownTestSuite() { staticData.reset(); }
- ~ProjectStorage() { static_projectStorage->resetForTestsOnly(); }
+ ProjectStorage() { storage.setErrorNotifier(errorNotifierMock); }
+
+ ~ProjectStorage() { storage.resetForTestsOnly(); }
template<typename Range>
static auto toValues(Range &&range)
@@ -307,6 +331,32 @@ protected:
storage.fetchSourceId(sourceContextId3, "bar");
}
+ auto createVerySimpleSynchronizationPackage()
+ {
+ SynchronizationPackage package;
+
+ package.types.push_back(Storage::Synchronization::Type{
+ "QQuickItem",
+ Storage::Synchronization::ImportedType{},
+ Storage::Synchronization::ImportedType{},
+ TypeTraitsKind::Reference,
+ sourceId1,
+ {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"},
+ Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}});
+ package.types.push_back(Storage::Synchronization::Type{
+ "QObject",
+ Storage::Synchronization::ImportedType{},
+ Storage::Synchronization::ImportedType{},
+ TypeTraitsKind::Reference,
+ sourceId2,
+ {Storage::Synchronization::ExportedType{qmlModuleId, "Object"},
+ Storage::Synchronization::ExportedType{qmlNativeModuleId, "QObject"}}});
+
+ package.updatedSourceIds = {sourceId1, sourceId2};
+
+ return package;
+ }
+
auto createSimpleSynchronizationPackage()
{
SynchronizationPackage package;
@@ -1134,12 +1184,11 @@ protected:
}
protected:
- inline static std::unique_ptr<Sqlite::Database> static_database;
- Sqlite::Database &database = *static_database;
- inline static std::unique_ptr<QmlDesigner::ProjectStorage> static_projectStorage;
- QmlDesigner::ProjectStorage &storage = *static_projectStorage;
- QmlDesigner::SourcePathCache<QmlDesigner::ProjectStorage> sourcePathCache{
- storage};
+ inline static std::unique_ptr<StaticData> staticData;
+ Sqlite::Database &database = staticData->database;
+ QmlDesigner::ProjectStorage &storage = staticData->storage;
+ NiceMock<ProjectStorageErrorNotifierMock> errorNotifierMock;
+ QmlDesigner::SourcePathCache<QmlDesigner::ProjectStorage> sourcePathCache{storage};
QmlDesigner::SourcePathView path1{"/path1/to"};
QmlDesigner::SourcePathView path2{"/path2/to"};
QmlDesigner::SourcePathView path3{"/path3/to"};
@@ -1158,15 +1207,15 @@ protected:
SourceId sourceIdPath6{sourcePathCache.sourceId(pathPath6)};
SourceId qmlProjectSourceId{sourcePathCache.sourceId("/path1/qmldir")};
SourceId qtQuickProjectSourceId{sourcePathCache.sourceId("/path2/qmldir")};
- ModuleId qmlModuleId{storage.moduleId("Qml")};
- ModuleId qmlNativeModuleId{storage.moduleId("Qml-cppnative")};
- ModuleId qtQuickModuleId{storage.moduleId("QtQuick")};
- ModuleId qtQuickNativeModuleId{storage.moduleId("QtQuick-cppnative")};
- ModuleId pathToModuleId{storage.moduleId("/path/to")};
- ModuleId qtQuick3DModuleId{storage.moduleId("QtQuick3D")};
- ModuleId myModuleModuleId{storage.moduleId("MyModule")};
- ModuleId QMLModuleId{storage.moduleId("QML")};
- ModuleId QMLNativeModuleId{storage.moduleId("QML-cppnative")};
+ ModuleId qmlModuleId{storage.moduleId("Qml", ModuleKind::QmlLibrary)};
+ ModuleId qmlNativeModuleId{storage.moduleId("Qml", ModuleKind::CppLibrary)};
+ ModuleId qtQuickModuleId{storage.moduleId("QtQuick", ModuleKind::QmlLibrary)};
+ ModuleId qtQuickNativeModuleId{storage.moduleId("QtQuick", ModuleKind::CppLibrary)};
+ ModuleId pathToModuleId{storage.moduleId("/path/to", ModuleKind::PathLibrary)};
+ ModuleId qtQuick3DModuleId{storage.moduleId("QtQuick3D", ModuleKind::QmlLibrary)};
+ ModuleId myModuleModuleId{storage.moduleId("MyModule", ModuleKind::QmlLibrary)};
+ ModuleId QMLModuleId{storage.moduleId("QML", ModuleKind::QmlLibrary)};
+ ModuleId QMLNativeModuleId{storage.moduleId("QML", ModuleKind::CppLibrary)};
Storage::Imports importsSourceId1;
Storage::Imports importsSourceId2;
Storage::Imports importsSourceId3;
@@ -1482,22 +1531,193 @@ TEST_F(ProjectStorage, synchronize_types_adds_new_types_with_exported_extension_
"QQuickItem"))))));
}
-TEST_F(ProjectStorage, synchronize_types_adds_new_types_throws_with_wrong_prototype_name)
+TEST_F(ProjectStorage,
+ synchronize_types_adds_unknown_prototype_which_notifies_about_unresolved_type_name)
+{
+ auto package{createSimpleSynchronizationPackage()};
+ package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
+
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Objec"), sourceId1));
+
+ storage.synchronize(std::move(package));
+}
+
+TEST_F(ProjectStorage, synchronize_types_adds_unknown_prototype_as_unresolved_type_id)
+{
+ auto package{createSimpleSynchronizationPackage()};
+ package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
+
+ storage.synchronize(std::move(package));
+
+ ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
+ IsPrototypeId(IsUnresolvedTypeId()));
+}
+
+TEST_F(ProjectStorage, synchronize_types_updates_unresolved_prototype_after_exported_type_name_is_added)
+{
+ auto package{createSimpleSynchronizationPackage()};
+ package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
+ storage.synchronize(package);
+ package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
+
+ storage.synchronize(std::move(package));
+
+ ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
+ IsPrototypeId(fetchTypeId(sourceId2, "QObject")));
+}
+
+TEST_F(ProjectStorage,
+ synchronize_types_updates_prototype_to_unresolved_after_exported_type_name_is_removed)
+{
+ auto package{createSimpleSynchronizationPackage()};
+ package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
+ package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
+ storage.synchronize(package);
+ package.types[1].exportedTypes.pop_back();
+
+ storage.synchronize(std::move(package));
+
+ ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
+ IsPrototypeId(IsUnresolvedTypeId()));
+}
+
+TEST_F(ProjectStorage,
+ synchronize_types_updates_unresolved_prototype_indirectly_after_exported_type_name_is_added)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
+ storage.synchronize(package);
+ package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
+ package.types.erase(package.types.begin());
+ package.updatedSourceIds = {sourceId2};
- ASSERT_THROW(storage.synchronize(std::move(package)), QmlDesigner::TypeNameDoesNotExists);
+ storage.synchronize(std::move(package));
+
+ ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
+ IsPrototypeId(fetchTypeId(sourceId2, "QObject")));
}
-TEST_F(ProjectStorage, synchronize_types_adds_new_types_throws_with_wrong_extension_name)
+TEST_F(ProjectStorage,
+ synchronize_types_updates_prototype_indirectly_to_unresolved_after_exported_type_name_is_removed)
+{
+ auto package{createSimpleSynchronizationPackage()};
+ package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
+ package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
+ storage.synchronize(package);
+ package.types[1].exportedTypes.pop_back();
+ package.types.erase(package.types.begin());
+ package.updatedSourceIds = {sourceId2};
+
+ storage.synchronize(std::move(package));
+
+ ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
+ IsPrototypeId(IsUnresolvedTypeId()));
+}
+
+TEST_F(ProjectStorage,
+ synchronize_types_updates_prototype_indirectly_to_unresolved_after_exported_type_name_is_removed_notifies_type_name_cannot_be_resolved)
+{
+ auto package{createSimpleSynchronizationPackage()};
+ package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
+ package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
+ storage.synchronize(package);
+ package.types[1].exportedTypes.pop_back();
+ package.types.erase(package.types.begin());
+ package.updatedSourceIds = {sourceId2};
+
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Objec"), sourceId1));
+
+ storage.synchronize(std::move(package));
+}
+
+TEST_F(ProjectStorage, synchronize_types_updates_unresolved_extension_after_exported_type_name_is_added)
+{
+ auto package{createSimpleSynchronizationPackage()};
+ package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"};
+ storage.synchronize(package);
+ package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
+
+ storage.synchronize(std::move(package));
+
+ ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
+ IsExtensionId(fetchTypeId(sourceId2, "QObject")));
+}
+
+TEST_F(ProjectStorage,
+ synchronize_types_updates_extension_to_unresolved_after_exported_type_name_is_removed)
+{
+ auto package{createSimpleSynchronizationPackage()};
+ package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"};
+ package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
+ storage.synchronize(package);
+ package.types[1].exportedTypes.pop_back();
+
+ storage.synchronize(std::move(package));
+
+ ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
+ IsExtensionId(IsUnresolvedTypeId()));
+}
+
+TEST_F(ProjectStorage,
+ synchronize_types_updates_unresolved_extension_indirectly_after_exported_type_name_is_added)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"};
+ storage.synchronize(package);
+ package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
+ package.types.erase(package.types.begin());
+ package.updatedSourceIds = {sourceId2};
- ASSERT_THROW(storage.synchronize(std::move(package)), QmlDesigner::TypeNameDoesNotExists);
+ storage.synchronize(std::move(package));
+
+ ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
+ IsExtensionId(fetchTypeId(sourceId2, "QObject")));
}
+TEST_F(ProjectStorage,
+ synchronize_types_updates_invalid_extension_indirectly_after_exported_type_name_is_removed)
+{
+ auto package{createSimpleSynchronizationPackage()};
+ package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"};
+ package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
+ storage.synchronize(package);
+ package.types[1].exportedTypes.pop_back();
+ package.types.erase(package.types.begin());
+ package.updatedSourceIds = {sourceId2};
+
+ storage.synchronize(std::move(package));
+
+ ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
+ IsExtensionId(IsUnresolvedTypeId()));
+}
+
+TEST_F(ProjectStorage,
+ synchronize_types_updates_extension_indirectly_to_unresolved_after_exported_type_name_is_removed_notifies_type_name_cannot_be_resolved)
+{
+ auto package{createSimpleSynchronizationPackage()};
+ package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"};
+ package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
+ storage.synchronize(package);
+ package.types[1].exportedTypes.pop_back();
+ package.types.erase(package.types.begin());
+ package.updatedSourceIds = {sourceId2};
+
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Objec"), sourceId1));
+
+ storage.synchronize(std::move(package));
+}
+
+TEST_F(ProjectStorage, synchronize_types_adds_extension_which_notifies_about_unresolved_type_name)
+{
+ auto package{createSimpleSynchronizationPackage()};
+ package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"};
+
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Objec"), sourceId1));
+
+ storage.synchronize(std::move(package));
+}
+
+
TEST_F(ProjectStorage, synchronize_types_adds_new_types_with_missing_module)
{
auto package{createSimpleSynchronizationPackage()};
@@ -1770,7 +1990,7 @@ TEST_F(ProjectStorage, synchronize_types_add_qualified_extension)
IsExportedType(qtQuickNativeModuleId, "QQuickItem"))))));
}
-TEST_F(ProjectStorage, synchronize_types_throws_for_missing_prototype)
+TEST_F(ProjectStorage, synchronize_types_notifies_cannot_resolve_for_missing_prototype)
{
auto package{createSimpleSynchronizationPackage()};
package.types = {Storage::Synchronization::Type{
@@ -1782,10 +2002,12 @@ TEST_F(ProjectStorage, synchronize_types_throws_for_missing_prototype)
{Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"},
Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}}};
- ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists);
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1));
+
+ storage.synchronize(package);
}
-TEST_F(ProjectStorage, synchronize_types_throws_for_missing_extension)
+TEST_F(ProjectStorage, synchronize_types_notifies_cannot_resolve_for_missing_extension)
{
auto package{createSimpleSynchronizationPackage()};
package.types = {Storage::Synchronization::Type{
@@ -1797,7 +2019,9 @@ TEST_F(ProjectStorage, synchronize_types_throws_for_missing_extension)
{Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"},
Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}}};
- ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists);
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1));
+
+ storage.synchronize(package);
}
TEST_F(ProjectStorage, synchronize_types_throws_for_invalid_module)
@@ -2915,7 +3139,7 @@ TEST_F(ProjectStorage, fetch_invalid_type_id_by_impor_ids_and_exported_name_if_n
{
auto package{createSimpleSynchronizationPackage()};
storage.synchronize(package);
- auto qtQuickModuleId = storage.moduleId("QtQuick");
+ auto qtQuickModuleId = storage.moduleId("QtQuick", ModuleKind::QmlLibrary);
auto typeId = storage.fetchTypeIdByModuleIdsAndExportedName({qtQuickModuleId}, "Object");
@@ -3666,7 +3890,8 @@ TEST_F(ProjectStorage, change_extension_type_module_id)
TypeTraitsKind::Reference)));
}
-TEST_F(ProjectStorage, change_qualified_prototype_type_module_id_throws)
+TEST_F(ProjectStorage,
+ change_qualified_prototype_type_module_id_notifies_that_type_name_cannot_be_resolved)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{
@@ -3674,12 +3899,12 @@ TEST_F(ProjectStorage, change_qualified_prototype_type_module_id_throws)
storage.synchronize(package);
package.types[1].exportedTypes[0].moduleId = qtQuickModuleId;
- ASSERT_THROW(storage.synchronize(
- SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}),
- QmlDesigner::TypeNameDoesNotExists);
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
+
+ storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}});
}
-TEST_F(ProjectStorage, change_qualified_extension_type_module_id_throws)
+TEST_F(ProjectStorage, change_qualified_extension_type_module_id_notifies_cannot_resolve)
{
auto package{createSimpleSynchronizationPackage()};
std::swap(package.types.front().extension, package.types.front().prototype);
@@ -3688,9 +3913,9 @@ TEST_F(ProjectStorage, change_qualified_extension_type_module_id_throws)
storage.synchronize(package);
package.types[1].exportedTypes[0].moduleId = qtQuickModuleId;
- ASSERT_THROW(storage.synchronize(
- SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}),
- QmlDesigner::TypeNameDoesNotExists);
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
+
+ storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}});
}
TEST_F(ProjectStorage, change_qualified_prototype_type_module_id)
@@ -3794,9 +4019,9 @@ TEST_F(ProjectStorage, change_prototype_type_name_throws_for_wrong_native_protot
package.types[1].exportedTypes[2].name = "QObject3";
package.types[1].typeName = "QObject3";
- ASSERT_THROW(storage.synchronize(
- SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}),
- QmlDesigner::TypeNameDoesNotExists);
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1));
+
+ storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}});
}
TEST_F(ProjectStorage, change_extension_type_name_throws_for_wrong_native_extension_type_name)
@@ -3809,9 +4034,9 @@ TEST_F(ProjectStorage, change_extension_type_name_throws_for_wrong_native_extens
package.types[1].exportedTypes[2].name = "QObject3";
package.types[1].typeName = "QObject3";
- ASSERT_THROW(storage.synchronize(
- SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}),
- QmlDesigner::TypeNameDoesNotExists);
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1));
+
+ storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}});
}
TEST_F(ProjectStorage, throw_for_prototype_chain_cycles)
@@ -4113,23 +4338,29 @@ TEST_F(ProjectStorage, qualified_extension)
TypeTraitsKind::Reference)));
}
-TEST_F(ProjectStorage, qualified_prototype_upper_down_the_module_chain_throws)
+TEST_F(ProjectStorage,
+ qualified_prototype_upper_down_the_module_chain_notifies_type_name_cannot_be_resolved)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{
"Object", Storage::Import{qtQuickModuleId, Storage::Version{}, sourceId1}};
- ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists);
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
+
+ storage.synchronize(package);
}
-TEST_F(ProjectStorage, qualified_extension_upper_down_the_module_chain_throws)
+TEST_F(ProjectStorage,
+ qualified_extension_upper_down_the_module_chain_notifies_type_name_cannot_be_resolved)
{
auto package{createSimpleSynchronizationPackage()};
std::swap(package.types.front().extension, package.types.front().prototype);
package.types[0].extension = Storage::Synchronization::QualifiedImportedType{
"Object", Storage::Import{qtQuickModuleId, Storage::Version{}, sourceId1}};
- ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists);
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
+
+ storage.synchronize(package);
}
TEST_F(ProjectStorage, qualified_prototype_upper_in_the_module_chain)
@@ -4185,7 +4416,7 @@ TEST_F(ProjectStorage, qualified_extension_upper_in_the_module_chain)
TypeTraitsKind::Reference)));
}
-TEST_F(ProjectStorage, qualified_prototype_with_wrong_version_throws)
+TEST_F(ProjectStorage, qualified_prototype_with_wrong_version_notifies_type_name_cannot_be_resolved)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{
@@ -4201,10 +4432,12 @@ TEST_F(ProjectStorage, qualified_prototype_with_wrong_version_throws)
package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3);
package.updatedSourceIds.push_back(sourceId3);
- ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists);
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
+
+ storage.synchronize(package);
}
-TEST_F(ProjectStorage, qualified_extension_with_wrong_version_throws)
+TEST_F(ProjectStorage, qualified_extension_with_wrong_version_notifies_type_name_cannot_be_resolved)
{
auto package{createSimpleSynchronizationPackage()};
std::swap(package.types.front().extension, package.types.front().prototype);
@@ -4221,7 +4454,9 @@ TEST_F(ProjectStorage, qualified_extension_with_wrong_version_throws)
package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3);
package.updatedSourceIds.push_back(sourceId3);
- ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists);
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
+
+ storage.synchronize(package);
}
TEST_F(ProjectStorage, qualified_prototype_with_version)
@@ -4334,23 +4569,29 @@ TEST_F(ProjectStorage, qualified_extension_with_version_in_the_proto_type_chain)
TypeTraitsKind::Reference)));
}
-TEST_F(ProjectStorage, qualified_prototype_with_version_down_the_proto_type_chain_throws)
+TEST_F(ProjectStorage,
+ qualified_prototype_with_version_down_the_proto_type_chain_notifies_type_name_cannot_be_resolved)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{
"Object", Storage::Import{qtQuickModuleId, Storage::Version{2}, sourceId1}};
- ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists);
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
+
+ storage.synchronize(package);
}
-TEST_F(ProjectStorage, qualified_extension_with_version_down_the_proto_type_chain_throws)
+TEST_F(ProjectStorage,
+ qualified_extension_with_version_down_the_proto_type_chain_notifies_type_name_cannot_be_resolved)
{
auto package{createSimpleSynchronizationPackage()};
std::swap(package.types.front().extension, package.types.front().prototype);
package.types[0].extension = Storage::Synchronization::QualifiedImportedType{
"Object", Storage::Import{qtQuickModuleId, Storage::Version{2}, sourceId1}};
- ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists);
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
+
+ storage.synchronize(package);
}
TEST_F(ProjectStorage, qualified_property_declaration_type_name)
@@ -4655,7 +4896,7 @@ TEST_F(ProjectStorage, fetch_by_major_version_and_minor_version_for_qualified_im
}
TEST_F(ProjectStorage,
- fetch_by_major_version_and_minor_version_for_imported_type_if_minor_version_is_not_exported_throws)
+ fetch_by_major_version_and_minor_version_for_imported_type_if_minor_version_is_not_exported_notifies_type_name_cannot_be_resolved)
{
auto package{createSynchronizationPackageWithVersions()};
storage.synchronize(package);
@@ -4669,12 +4910,13 @@ TEST_F(ProjectStorage,
Storage::Version{}}}};
Storage::Import import{qmlModuleId, Storage::Version{1, 1}, sourceId2};
- ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}),
- QmlDesigner::TypeNameDoesNotExists);
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId2));
+
+ storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}});
}
TEST_F(ProjectStorage,
- fetch_by_major_version_and_minor_version_for_qualified_imported_type_if_minor_version_is_not_exported_throws)
+ fetch_by_major_version_and_minor_version_for_qualified_imported_type_if_minor_version_is_not_exported_notifies_type_name_cannot_be_resolved)
{
auto package{createSynchronizationPackageWithVersions()};
storage.synchronize(package);
@@ -4687,11 +4929,12 @@ TEST_F(ProjectStorage,
sourceId2,
{Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}};
- ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}),
- QmlDesigner::TypeNameDoesNotExists);
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId2));
+
+ storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}});
}
-TEST_F(ProjectStorage, fetch_low_minor_version_for_imported_type_throws)
+TEST_F(ProjectStorage, fetch_low_minor_version_for_imported_type_notifies_type_name_cannot_be_resolved)
{
auto package{createSynchronizationPackageWithVersions()};
storage.synchronize(package);
@@ -4705,11 +4948,13 @@ TEST_F(ProjectStorage, fetch_low_minor_version_for_imported_type_throws)
Storage::Version{}}}};
Storage::Import import{qmlModuleId, Storage::Version{1, 1}, sourceId2};
- ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}),
- QmlDesigner::TypeNameDoesNotExists);
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId2));
+
+ storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}});
}
-TEST_F(ProjectStorage, fetch_low_minor_version_for_qualified_imported_type_throws)
+TEST_F(ProjectStorage,
+ fetch_low_minor_version_for_qualified_imported_type_notifies_type_name_cannot_be_resolved)
{
auto package{createSynchronizationPackageWithVersions()};
storage.synchronize(package);
@@ -4722,8 +4967,9 @@ TEST_F(ProjectStorage, fetch_low_minor_version_for_qualified_imported_type_throw
sourceId2,
{Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}};
- ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}),
- QmlDesigner::TypeNameDoesNotExists);
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId2));
+
+ storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}});
}
TEST_F(ProjectStorage, fetch_higher_minor_version_for_imported_type)
@@ -4771,7 +5017,8 @@ TEST_F(ProjectStorage, fetch_higher_minor_version_for_qualified_imported_type)
TypeTraitsKind::Reference)));
}
-TEST_F(ProjectStorage, fetch_different_major_version_for_imported_type_throws)
+TEST_F(ProjectStorage,
+ fetch_different_major_version_for_imported_type_notifies_type_name_cannot_be_resolved)
{
auto package{createSynchronizationPackageWithVersions()};
storage.synchronize(package);
@@ -4785,11 +5032,13 @@ TEST_F(ProjectStorage, fetch_different_major_version_for_imported_type_throws)
Storage::Version{}}}};
Storage::Import import{qmlModuleId, Storage::Version{3, 1}, sourceId2};
- ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}),
- QmlDesigner::TypeNameDoesNotExists);
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId2));
+
+ storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}});
}
-TEST_F(ProjectStorage, fetch_different_major_version_for_qualified_imported_type_throws)
+TEST_F(ProjectStorage,
+ fetch_different_major_version_for_qualified_imported_type_notifies_type_name_cannot_be_resolved)
{
auto package{createSynchronizationPackageWithVersions()};
storage.synchronize(package);
@@ -4802,8 +5051,9 @@ TEST_F(ProjectStorage, fetch_different_major_version_for_qualified_imported_type
sourceId2,
{Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}};
- ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}),
- QmlDesigner::TypeNameDoesNotExists);
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId2));
+
+ storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}});
}
TEST_F(ProjectStorage, fetch_other_type_by_different_version_for_imported_type)
@@ -5065,262 +5315,314 @@ TEST_F(ProjectStorage, minimal_updates)
TEST_F(ProjectStorage, get_module_id)
{
- auto id = storage.moduleId("Qml");
+ auto id = storage.moduleId("Qml", ModuleKind::QmlLibrary);
ASSERT_TRUE(id);
}
TEST_F(ProjectStorage, get_same_module_id_again)
{
- auto initialId = storage.moduleId("Qml");
+ auto initialId = storage.moduleId("Qml", ModuleKind::QmlLibrary);
- auto id = storage.moduleId("Qml");
+ auto id = storage.moduleId("Qml", ModuleKind::QmlLibrary);
ASSERT_THAT(id, Eq(initialId));
}
-TEST_F(ProjectStorage, module_name_throws_if_id_is_invalid)
+TEST_F(ProjectStorage, different_module_kind_returns_different_id)
{
- ASSERT_THROW(storage.moduleName(ModuleId{}), QmlDesigner::ModuleDoesNotExists);
+ auto qmlId = storage.moduleId("Qml", ModuleKind::QmlLibrary);
+
+ auto cppId = storage.moduleId("Qml", ModuleKind::CppLibrary);
+
+ ASSERT_THAT(cppId, Ne(qmlId));
}
-TEST_F(ProjectStorage, module_name_throws_if_id_does_not_exists)
+TEST_F(ProjectStorage, module_throws_if_id_is_invalid)
{
- ASSERT_THROW(storage.moduleName(ModuleId::create(222)), QmlDesigner::ModuleDoesNotExists);
+ ASSERT_THROW(storage.module(ModuleId{}), QmlDesigner::ModuleDoesNotExists);
}
-TEST_F(ProjectStorage, get_module_name)
+TEST_F(ProjectStorage, module_throws_if_id_does_not_exists)
{
- auto id = storage.moduleId("Qml");
+ ASSERT_THROW(storage.module(ModuleId::create(222)), QmlDesigner::ModuleDoesNotExists);
+}
+
+TEST_F(ProjectStorage, get_module)
+{
+ auto id = storage.moduleId("Qml", ModuleKind::QmlLibrary);
- auto name = storage.moduleName(id);
+ auto module = storage.module(id);
- ASSERT_THAT(name, Eq("Qml"));
+ ASSERT_THAT(module, IsModule("Qml", ModuleKind::QmlLibrary));
}
TEST_F(ProjectStorage, populate_module_cache)
{
- auto id = storage.moduleId("Qml");
+ auto id = storage.moduleId("Qml", ModuleKind::QmlLibrary);
- QmlDesigner::ProjectStorage newStorage{database, database.isInitialized()};
+ QmlDesigner::ProjectStorage newStorage{database, errorNotifierMock, database.isInitialized()};
- ASSERT_THAT(newStorage.moduleName(id), Eq("Qml"));
+ ASSERT_THAT(newStorage.module(id), IsModule("Qml", ModuleKind::QmlLibrary));
}
-TEST_F(ProjectStorage, add_project_dataes)
+TEST_F(ProjectStorage, add_directory_infoes)
{
- Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId,
sourceId1,
qmlModuleId,
Storage::Synchronization::FileType::QmlDocument};
- Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId,
sourceId2,
qmlModuleId,
Storage::Synchronization::FileType::QmlDocument};
- Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId,
sourceId3,
qtQuickModuleId,
Storage::Synchronization::FileType::QmlTypes};
storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId},
- {projectData1, projectData2, projectData3}});
+ {directoryInfo1, directoryInfo2, directoryInfo3}});
- ASSERT_THAT(storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}),
- UnorderedElementsAre(projectData1, projectData2, projectData3));
+ ASSERT_THAT(storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId}),
+ UnorderedElementsAre(directoryInfo1, directoryInfo2, directoryInfo3));
}
-TEST_F(ProjectStorage, remove_project_data)
+TEST_F(ProjectStorage, remove_directory_info)
{
- Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId,
sourceId1,
qmlModuleId,
Storage::Synchronization::FileType::QmlDocument};
- Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId,
sourceId2,
qmlModuleId,
Storage::Synchronization::FileType::QmlDocument};
- Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId,
sourceId3,
qtQuickModuleId,
Storage::Synchronization::FileType::QmlTypes};
storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId},
- {projectData1, projectData2, projectData3}});
+ {directoryInfo1, directoryInfo2, directoryInfo3}});
storage.synchronize(
- SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, {projectData1}});
+ SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, {directoryInfo1}});
- ASSERT_THAT(storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}),
- UnorderedElementsAre(projectData1));
+ ASSERT_THAT(storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId}),
+ UnorderedElementsAre(directoryInfo1));
}
-TEST_F(ProjectStorage, update_project_data_file_type)
+TEST_F(ProjectStorage, update_directory_info_file_type)
{
- Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId,
sourceId1,
qmlModuleId,
Storage::Synchronization::FileType::QmlDocument};
- Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId,
sourceId2,
qmlModuleId,
Storage::Synchronization::FileType::QmlDocument};
- Storage::Synchronization::ProjectData projectData2b{qmlProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo2b{qmlProjectSourceId,
sourceId2,
qmlModuleId,
Storage::Synchronization::FileType::QmlTypes};
- Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId,
sourceId3,
qtQuickModuleId,
Storage::Synchronization::FileType::QmlTypes};
storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId},
- {projectData1, projectData2, projectData3}});
+ {directoryInfo1, directoryInfo2, directoryInfo3}});
- storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1, projectData2b}});
+ storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1, directoryInfo2b}});
- ASSERT_THAT(storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}),
- UnorderedElementsAre(projectData1, projectData2b, projectData3));
+ ASSERT_THAT(storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId}),
+ UnorderedElementsAre(directoryInfo1, directoryInfo2b, directoryInfo3));
}
-TEST_F(ProjectStorage, update_project_data_module_id)
+TEST_F(ProjectStorage, update_directory_info_module_id)
{
- Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId,
sourceId1,
qmlModuleId,
Storage::Synchronization::FileType::QmlDocument};
- Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId,
sourceId3,
qmlModuleId,
Storage::Synchronization::FileType::QmlDocument};
- Storage::Synchronization::ProjectData projectData2b{qmlProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo2b{qmlProjectSourceId,
sourceId3,
qtQuickModuleId,
Storage::Synchronization::FileType::QmlDocument};
- Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId,
sourceId2,
qtQuickModuleId,
Storage::Synchronization::FileType::QmlTypes};
storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId},
- {projectData1, projectData2, projectData3}});
+ {directoryInfo1, directoryInfo2, directoryInfo3}});
- storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1, projectData2b}});
+ storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1, directoryInfo2b}});
- ASSERT_THAT(storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}),
- UnorderedElementsAre(projectData1, projectData2b, projectData3));
+ ASSERT_THAT(storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId}),
+ UnorderedElementsAre(directoryInfo1, directoryInfo2b, directoryInfo3));
}
-TEST_F(ProjectStorage, throw_for_invalid_source_id_in_project_data)
+TEST_F(ProjectStorage, throw_for_invalid_source_id_in_directory_info)
{
- Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId,
SourceId{},
qmlModuleId,
Storage::Synchronization::FileType::QmlDocument};
- ASSERT_THROW(storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1}}),
- QmlDesigner::ProjectDataHasInvalidSourceId);
+ ASSERT_THROW(storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1}}),
+ QmlDesigner::DirectoryInfoHasInvalidSourceId);
}
-TEST_F(ProjectStorage, insert_project_data_with_invalid_module_id)
+TEST_F(ProjectStorage, insert_directory_info_with_invalid_module_id)
{
- Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId,
sourceId1,
ModuleId{},
Storage::Synchronization::FileType::QmlDocument};
- storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1}});
+ storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1}});
- ASSERT_THAT(storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}),
- UnorderedElementsAre(projectData1));
+ ASSERT_THAT(storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId}),
+ UnorderedElementsAre(directoryInfo1));
}
-TEST_F(ProjectStorage, update_project_data_with_invalid_module_id)
+TEST_F(ProjectStorage, update_directory_info_with_invalid_module_id)
{
- Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId,
sourceId1,
qmlModuleId,
Storage::Synchronization::FileType::QmlDocument};
- storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1}});
- projectData1.moduleId = ModuleId{};
+ storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1}});
+ directoryInfo1.moduleId = ModuleId{};
- storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1}});
+ storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1}});
- ASSERT_THAT(storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}),
- UnorderedElementsAre(projectData1));
+ ASSERT_THAT(storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId}),
+ UnorderedElementsAre(directoryInfo1));
}
-TEST_F(ProjectStorage, throw_for_updating_with_invalid_project_source_id_in_project_data)
+TEST_F(ProjectStorage, throw_for_updating_with_invalid_project_source_id_in_directory_info)
{
- Storage::Synchronization::ProjectData projectData1{SourceId{},
+ Storage::Synchronization::DirectoryInfo directoryInfo1{SourceId{},
sourceId1,
qmlModuleId,
Storage::Synchronization::FileType::QmlDocument};
- ASSERT_THROW(storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1}}),
- QmlDesigner::ProjectDataHasInvalidProjectSourceId);
+ ASSERT_THROW(storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1}}),
+ QmlDesigner::DirectoryInfoHasInvalidProjectSourceId);
}
-TEST_F(ProjectStorage, fetch_project_datas_by_directory_source_ids)
+TEST_F(ProjectStorage, fetch_directory_infos_by_directory_source_ids)
{
- Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId,
sourceId1,
qmlModuleId,
Storage::Synchronization::FileType::QmlDocument};
- Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId,
sourceId2,
qmlModuleId,
Storage::Synchronization::FileType::QmlDocument};
- Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId,
sourceId3,
qtQuickModuleId,
Storage::Synchronization::FileType::QmlTypes};
storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId},
- {projectData1, projectData2, projectData3}});
+ {directoryInfo1, directoryInfo2, directoryInfo3}});
- auto projectDatas = storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId});
+ auto directoryInfos = storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId});
- ASSERT_THAT(projectDatas, UnorderedElementsAre(projectData1, projectData2, projectData3));
+ ASSERT_THAT(directoryInfos, UnorderedElementsAre(directoryInfo1, directoryInfo2, directoryInfo3));
}
-TEST_F(ProjectStorage, fetch_project_datas_by_directory_source_id)
+TEST_F(ProjectStorage, fetch_directory_infos_by_directory_source_id)
{
- Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId,
sourceId1,
qmlModuleId,
Storage::Synchronization::FileType::QmlDocument};
- Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId,
sourceId2,
qmlModuleId,
Storage::Synchronization::FileType::QmlDocument};
- Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId,
sourceId3,
qtQuickModuleId,
Storage::Synchronization::FileType::QmlTypes};
storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId},
- {projectData1, projectData2, projectData3}});
+ {directoryInfo1, directoryInfo2, directoryInfo3}});
+
+ auto directoryInfo = storage.fetchDirectoryInfos(qmlProjectSourceId);
+
+ ASSERT_THAT(directoryInfo, UnorderedElementsAre(directoryInfo1, directoryInfo2));
+}
- auto projectData = storage.fetchProjectDatas(qmlProjectSourceId);
+TEST_F(ProjectStorage, fetch_directory_infos_by_directory_source_id_and_file_type)
+{
+ Storage::Synchronization::DirectoryInfo directoryInfo1{
+ qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument};
+ Storage::Synchronization::DirectoryInfo directoryInfo2{
+ qmlProjectSourceId, sourceId2, ModuleId{}, Storage::Synchronization::FileType::Directory};
+ Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId,
+ sourceId3,
+ qtQuickModuleId,
+ Storage::Synchronization::FileType::QmlTypes};
+ Storage::Synchronization::DirectoryInfo directoryInfo4{
+ qmlProjectSourceId, sourceId4, ModuleId{}, Storage::Synchronization::FileType::Directory};
+ storage.synchronize(
+ SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId},
+ {directoryInfo1, directoryInfo2, directoryInfo3, directoryInfo4}});
+
+ auto directoryInfo = storage.fetchDirectoryInfos(qmlProjectSourceId,
+ Storage::Synchronization::FileType::Directory);
- ASSERT_THAT(projectData, UnorderedElementsAre(projectData1, projectData2));
+ ASSERT_THAT(directoryInfo, UnorderedElementsAre(directoryInfo2, directoryInfo4));
}
-TEST_F(ProjectStorage, fetch_project_data_by_source_ids)
+TEST_F(ProjectStorage, fetch_subdirectory_source_ids)
{
- Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo1{
+ qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument};
+ Storage::Synchronization::DirectoryInfo directoryInfo2{
+ qmlProjectSourceId, sourceId2, ModuleId{}, Storage::Synchronization::FileType::Directory};
+ Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId,
+ sourceId3,
+ qtQuickModuleId,
+ Storage::Synchronization::FileType::QmlTypes};
+ Storage::Synchronization::DirectoryInfo directoryInfo4{
+ qmlProjectSourceId, sourceId4, ModuleId{}, Storage::Synchronization::FileType::Directory};
+ storage.synchronize(
+ SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId},
+ {directoryInfo1, directoryInfo2, directoryInfo3, directoryInfo4}});
+
+ auto directoryInfo = storage.fetchSubdirectorySourceIds(qmlProjectSourceId);
+
+ ASSERT_THAT(directoryInfo, UnorderedElementsAre(sourceId2, sourceId4));
+}
+
+TEST_F(ProjectStorage, fetch_directory_info_by_source_ids)
+{
+ Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId,
sourceId1,
qmlModuleId,
Storage::Synchronization::FileType::QmlDocument};
- Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId,
sourceId2,
qmlModuleId,
Storage::Synchronization::FileType::QmlDocument};
- Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId,
+ Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId,
sourceId3,
qtQuickModuleId,
Storage::Synchronization::FileType::QmlTypes};
storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId},
- {projectData1, projectData2, projectData3}});
+ {directoryInfo1, directoryInfo2, directoryInfo3}});
- auto projectData = storage.fetchProjectData({sourceId2});
+ auto directoryInfo = storage.fetchDirectoryInfo({sourceId2});
- ASSERT_THAT(projectData, Eq(projectData2));
+ ASSERT_THAT(directoryInfo, Eq(directoryInfo2));
}
TEST_F(ProjectStorage, exclude_exported_types)
@@ -5468,7 +5770,7 @@ TEST_F(ProjectStorage, module_exported_import_with_indirect_different_versions)
TEST_F(ProjectStorage,
module_exported_import_prevent_collision_if_module_is_indirectly_reexported_multiple_times)
{
- ModuleId qtQuick4DModuleId{storage.moduleId("QtQuick4D")};
+ ModuleId qtQuick4DModuleId{storage.moduleId("QtQuick4D", ModuleKind::QmlLibrary)};
auto package{createModuleExportedImportSynchronizationPackage()};
package.imports.emplace_back(qtQuickModuleId, Storage::Version{1}, sourceId5);
package.moduleExportedImports.emplace_back(qtQuick4DModuleId,
@@ -5524,8 +5826,8 @@ TEST_F(ProjectStorage,
TEST_F(ProjectStorage, distinguish_between_import_kinds)
{
- ModuleId qml1ModuleId{storage.moduleId("Qml1")};
- ModuleId qml11ModuleId{storage.moduleId("Qml11")};
+ ModuleId qml1ModuleId{storage.moduleId("Qml1", ModuleKind::QmlLibrary)};
+ ModuleId qml11ModuleId{storage.moduleId("Qml11", ModuleKind::QmlLibrary)};
auto package{createSimpleSynchronizationPackage()};
package.moduleDependencies.emplace_back(qmlModuleId, Storage::Version{}, sourceId1);
package.moduleDependencies.emplace_back(qml1ModuleId, Storage::Version{1}, sourceId1);
@@ -7192,6 +7494,46 @@ TEST_F(ProjectStorage, synchronize_document_imports_adds_import)
ASSERT_TRUE(storage.importId(imports.back()));
}
+TEST_F(ProjectStorage,
+ synchronize_document_imports_removes_import_notifies_that_type_name_cannot_be_resolved)
+{
+ auto package{createVerySimpleSynchronizationPackage()};
+ package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1);
+ package.types[0].prototype = Storage::Synchronization::ImportedType{"Object"};
+ storage.synchronize(package);
+
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
+
+ storage.synchronizeDocumentImports({}, sourceId1);
+}
+
+TEST_F(ProjectStorage, synchronize_document_imports_removes_import_which_makes_prototype_unresolved)
+{
+ auto package{createVerySimpleSynchronizationPackage()};
+ package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1);
+ package.types[0].prototype = Storage::Synchronization::ImportedType{"Object"};
+ storage.synchronize(package);
+
+ storage.synchronizeDocumentImports({}, sourceId1);
+
+ ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
+ IsPrototypeId(IsUnresolvedTypeId()));
+}
+
+TEST_F(ProjectStorage, synchronize_document_imports_adds_import_which_makes_prototype_resolved)
+{
+ auto package{createVerySimpleSynchronizationPackage()};
+ package.types[0].prototype = Storage::Synchronization::ImportedType{"Object"};
+ storage.synchronize(package);
+ Storage::Imports imports;
+ imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1);
+
+ storage.synchronizeDocumentImports(imports, sourceId1);
+
+ ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
+ IsPrototypeId(fetchTypeId(sourceId2, "QObject")));
+}
+
TEST_F(ProjectStorage, get_exported_type_names)
{
auto package{createSimpleSynchronizationPackage()};
@@ -7342,9 +7684,6 @@ TEST_F(ProjectStorage, do_not_synchronize_type_annotations_without_type)
package.typeAnnotations = createTypeAnnotions();
package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds(
package.typeAnnotations);
- TypeTraits traits{TypeTraitsKind::Reference};
- traits.canBeContainer = FlagIs::True;
- traits.visibleInLibrary = FlagIs::True;
storage.synchronize(package);
@@ -7366,13 +7705,29 @@ TEST_F(ProjectStorage, synchronize_type_annotation_type_traits)
ASSERT_THAT(storage.type(fetchTypeId(sourceId2, "QObject"))->traits, traits);
}
+TEST_F(ProjectStorage, synchronize_type_annotation_type_traits_for_prototype_heirs)
+{
+ auto package{createSimpleSynchronizationPackage()};
+ package.typeAnnotations = createTypeAnnotions();
+ package.typeAnnotations.pop_back();
+ package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds(
+ package.typeAnnotations);
+ TypeTraits traits{TypeTraitsKind::Reference};
+ traits.canBeContainer = FlagIs::True;
+ traits.visibleInLibrary = FlagIs::True;
+
+ storage.synchronize(package);
+
+ ASSERT_THAT(storage.type(fetchTypeId(sourceId1, "QQuickItem"))->traits, traits);
+}
+
TEST_F(ProjectStorage, synchronize_updates_type_annotation_type_traits)
{
auto package{createSimpleSynchronizationPackage()};
storage.synchronize(package);
SynchronizationPackage annotationPackage;
annotationPackage.typeAnnotations = createTypeAnnotions();
- package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds(
+ annotationPackage.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds(
package.typeAnnotations);
TypeTraits traits{TypeTraitsKind::Reference};
traits.canBeContainer = FlagIs::True;
@@ -7383,25 +7738,47 @@ TEST_F(ProjectStorage, synchronize_updates_type_annotation_type_traits)
ASSERT_THAT(storage.type(fetchTypeId(sourceId2, "QObject"))->traits, traits);
}
+TEST_F(ProjectStorage, synchronize_updates_type_annotation_type_traits_for_prototype_heirs)
+{
+ auto package{createSimpleSynchronizationPackage()};
+ storage.synchronize(package);
+ SynchronizationPackage annotationPackage;
+ annotationPackage.typeAnnotations = createTypeAnnotions();
+ annotationPackage.typeAnnotations.pop_back();
+ annotationPackage.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds(
+ package.typeAnnotations);
+ TypeTraits traits{TypeTraitsKind::Reference};
+ traits.canBeContainer = FlagIs::True;
+ traits.visibleInLibrary = FlagIs::True;
+
+ storage.synchronize(annotationPackage);
+
+ ASSERT_THAT(storage.type(fetchTypeId(sourceId1, "QQuickItem"))->traits, traits);
+}
+
TEST_F(ProjectStorage, synchronize_clears_annotation_type_traits_if_annotation_was_removed)
{
+
+}
+
+TEST_F(ProjectStorage,
+ synchronize_clears_annotation_type_traits_if_annotation_was_removed_for_prototype_heirs)
+{
auto package{createSimpleSynchronizationPackage()};
package.typeAnnotations = createTypeAnnotions();
package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds(
package.typeAnnotations);
- storage.synchronize(package);
package.typeAnnotations[0].traits.isStackedContainer = FlagIs::True;
- TypeTraits traits{TypeTraitsKind::Reference};
- traits.canBeContainer = FlagIs::True;
- traits.visibleInLibrary = FlagIs::True;
- traits.isStackedContainer = FlagIs::True;
+ storage.synchronize(package);
+ package.typeAnnotations.pop_back();
storage.synchronize(package);
- ASSERT_THAT(storage.type(fetchTypeId(sourceId2, "QObject"))->traits, traits);
+ ASSERT_THAT(storage.type(fetchTypeId(sourceId1, "QQuickItem"))->traits,
+ package.typeAnnotations[0].traits);
}
-TEST_F(ProjectStorage, synchronize_updatesannotation_type_traits)
+TEST_F(ProjectStorage, synchronize_updates_annotation_type_traits)
{
auto package{createSimpleSynchronizationPackage()};
package.typeAnnotations = createTypeAnnotions();
@@ -7544,6 +7921,7 @@ TEST_F(ProjectStorage, synchronize_item_library_entries)
storage.allItemLibraryEntries(),
UnorderedElementsAre(
IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"),
+ "Object",
"Foo",
"/path/icon",
"Basic Items",
@@ -7555,6 +7933,7 @@ TEST_F(ProjectStorage, synchronize_item_library_entries)
UnorderedElementsAre("/path/templates/frame.png",
"/path/templates/frame.frag")),
IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"),
+ "Object",
"Bar",
"/path/icon2",
"Basic Items",
@@ -7565,6 +7944,7 @@ TEST_F(ProjectStorage, synchronize_item_library_entries)
IsEmpty()),
IsItemLibraryEntry(fetchTypeId(sourceId1, "QQuickItem"),
"Item",
+ "Item",
"/path/icon3",
"Advanced Items",
"QtQuick",
@@ -7604,6 +7984,7 @@ TEST_F(ProjectStorage, synchronize_updates_item_library_entries)
ASSERT_THAT(storage.itemLibraryEntries(fetchTypeId(sourceId2, "QObject")),
ElementsAre(
IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"),
+ "Object",
"Foo",
"/path/icon",
"Basic Items",
@@ -7682,6 +8063,7 @@ TEST_F(ProjectStorage, get_all_item_library_entries)
entries,
UnorderedElementsAre(
IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"),
+ "Object",
"Foo",
"/path/icon",
"Basic Items",
@@ -7693,6 +8075,7 @@ TEST_F(ProjectStorage, get_all_item_library_entries)
UnorderedElementsAre("/path/templates/frame.png",
"/path/templates/frame.frag")),
IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"),
+ "Object",
"Bar",
"/path/icon2",
"Basic Items",
@@ -7703,6 +8086,7 @@ TEST_F(ProjectStorage, get_all_item_library_entries)
IsEmpty()),
IsItemLibraryEntry(fetchTypeId(sourceId1, "QQuickItem"),
"Item",
+ "Item",
"/path/icon3",
"Advanced Items",
"QtQuick",
@@ -7728,6 +8112,7 @@ TEST_F(ProjectStorage, get_all_item_library_entries_handles_no_entries)
UnorderedElementsAre(
IsItemLibraryEntry(fetchTypeId(sourceId1, "QQuickItem"),
"Item",
+ "Item",
"/path/icon3",
"Advanced Items",
"QtQuick",
@@ -7753,6 +8138,7 @@ TEST_F(ProjectStorage, get_item_library_entries_by_type_id)
entries,
UnorderedElementsAre(
IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"),
+ "Object",
"Foo",
"/path/icon",
"Basic Items",
@@ -7764,6 +8150,7 @@ TEST_F(ProjectStorage, get_item_library_entries_by_type_id)
UnorderedElementsAre("/path/templates/frame.png",
"/path/templates/frame.frag")),
IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"),
+ "Object",
"Bar",
"/path/icon2",
"Basic Items",
@@ -7816,6 +8203,7 @@ TEST_F(ProjectStorage, get_item_library_entries_by_source_id)
entries,
UnorderedElementsAre(
IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"),
+ "Object",
"Foo",
"/path/icon",
"Basic Items",
@@ -7827,6 +8215,7 @@ TEST_F(ProjectStorage, get_item_library_entries_by_source_id)
UnorderedElementsAre("/path/templates/frame.png",
"/path/templates/frame.frag")),
IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"),
+ "Object",
"Bar",
"/path/icon2",
"Basic Items",
@@ -7893,4 +8282,105 @@ TEST_F(ProjectStorage, get_no_hair_ids_for_invalid_type_id)
ASSERT_THAT(heirIds, IsEmpty());
}
+
+TEST_F(ProjectStorage,
+ removed_document_import_notifies_for_prototypes_that_type_name_cannot_be_resolved)
+{
+ auto package{createVerySimpleSynchronizationPackage()};
+ package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1);
+ package.types[0].prototype = Storage::Synchronization::ImportedType{"QObject"};
+ storage.synchronize(package);
+ package.moduleDependencies.clear();
+ package.types.clear();
+ package.updatedSourceIds.clear();
+ package.updatedModuleDependencySourceIds = {sourceId1};
+
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1));
+
+ storage.synchronize(package);
+}
+
+TEST_F(ProjectStorage,
+ removed_document_import_notifies_for_extensions_that_type_name_cannot_be_resolved)
+{
+ auto package{createVerySimpleSynchronizationPackage()};
+ package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1);
+ package.types[0].extension = Storage::Synchronization::ImportedType{"QObject"};
+ storage.synchronize(package);
+ package.moduleDependencies.clear();
+ package.types.clear();
+ package.updatedSourceIds.clear();
+ package.updatedModuleDependencySourceIds = {sourceId1};
+
+ EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1));
+
+ storage.synchronize(package);
+}
+
+TEST_F(ProjectStorage, removed_document_import_changes_prototype_to_unresolved)
+{
+ auto package{createVerySimpleSynchronizationPackage()};
+ package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1);
+ package.types[0].prototype = Storage::Synchronization::ImportedType{"QObject"};
+ storage.synchronize(package);
+ package.moduleDependencies.clear();
+ package.types.clear();
+ package.updatedSourceIds.clear();
+ package.updatedModuleDependencySourceIds = {sourceId1};
+
+ storage.synchronize(package);
+
+ ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
+ Field(&Storage::Synchronization::Type::prototypeId, IsUnresolvedTypeId()));
+}
+
+TEST_F(ProjectStorage, removed_document_import_changes_extension_to_unresolved)
+{
+ auto package{createVerySimpleSynchronizationPackage()};
+ package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1);
+ package.types[0].extension = Storage::Synchronization::ImportedType{"QObject"};
+ storage.synchronize(package);
+ package.moduleDependencies.clear();
+ package.types.clear();
+ package.updatedSourceIds.clear();
+ package.updatedModuleDependencySourceIds = {sourceId1};
+
+ storage.synchronize(package);
+
+ ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
+ Field(&Storage::Synchronization::Type::extensionId, IsUnresolvedTypeId()));
+}
+
+TEST_F(ProjectStorage, added_document_import_fixes_unresolved_prototype)
+{
+ auto package{createVerySimpleSynchronizationPackage()};
+ package.types[0].prototype = Storage::Synchronization::ImportedType{"QObject"};
+ storage.synchronize(package);
+ package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1);
+ package.types.clear();
+ package.updatedSourceIds.clear();
+ package.updatedModuleDependencySourceIds = {sourceId1};
+
+ storage.synchronize(package);
+
+ ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
+ IsPrototypeId(fetchTypeId(sourceId2, "QObject")));
+}
+
+TEST_F(ProjectStorage, added_document_import_fixes_unresolved_extension)
+{
+ auto package{createVerySimpleSynchronizationPackage()};
+ package.types[0].extension = Storage::Synchronization::ImportedType{"QObject"};
+ storage.synchronize(package);
+ package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1);
+ package.types.clear();
+ package.updatedSourceIds.clear();
+ package.updatedModuleDependencySourceIds = {sourceId1};
+
+ storage.synchronize(package);
+
+ ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
+ IsExtensionId(fetchTypeId(sourceId2, "QObject")));
+}
+
} // namespace
diff --git a/tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp
index 26d5af8af8..771107ece7 100644
--- a/tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp
+++ b/tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp
@@ -3,10 +3,11 @@
#include "../utils/googletest.h"
-#include "../mocks/filesystemmock.h"
-#include "../mocks/mockqfilesystemwatcher.h"
-#include "../mocks/mocktimer.h"
-#include "../mocks/projectstoragepathwatchernotifiermock.h"
+#include <filesystemmock.h>
+#include <mockqfilesystemwatcher.h>
+#include <mocktimer.h>
+#include <projectstorageerrornotifiermock.h>
+#include <projectstoragepathwatchernotifiermock.h>
#include <projectstorage/projectstorage.h>
#include <projectstorage/projectstoragepathwatcher.h>
@@ -39,21 +40,18 @@ using QmlDesigner::WatcherEntry;
class ProjectStoragePathWatcher : public testing::Test
{
protected:
- static void SetUpTestSuite()
+ struct StaticData
{
- static_database = std::make_unique<Sqlite::Database>(":memory:", Sqlite::JournalMode::Memory);
+ Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
+ ProjectStorageErrorNotifierMock errorNotifierMock;
+ QmlDesigner::ProjectStorage storage{database, errorNotifierMock, database.isInitialized()};
+ };
- static_projectStorage = std::make_unique<QmlDesigner::ProjectStorage>(
- *static_database, static_database->isInitialized());
- }
+ static void SetUpTestSuite() { staticData = std::make_unique<StaticData>(); }
- static void TearDownTestSuite()
- {
- static_projectStorage.reset();
- static_database.reset();
- }
+ static void TearDownTestSuite() { staticData.reset(); }
- ~ProjectStoragePathWatcher() { static_projectStorage->resetForTestsOnly(); }
+ ~ProjectStoragePathWatcher() { storage.resetForTestsOnly(); }
ProjectStoragePathWatcher()
{
@@ -79,10 +77,9 @@ protected:
protected:
NiceMock<ProjectStoragePathWatcherNotifierMock> notifier;
NiceMock<FileSystemMock> mockFileSystem;
- inline static std::unique_ptr<Sqlite::Database> static_database;
- Sqlite::Database &database = *static_database;
- inline static std::unique_ptr<QmlDesigner::ProjectStorage> static_projectStorage;
- QmlDesigner::ProjectStorage &storage = *static_projectStorage;
+ inline static std::unique_ptr<StaticData> staticData;
+ Sqlite::Database &database = staticData->database;
+ QmlDesigner::ProjectStorage &storage = staticData->storage;
SourcePathCache pathCache{storage};
Watcher watcher{pathCache, mockFileSystem, &notifier};
NiceMock<MockQFileSytemWatcher> &mockQFileSytemWatcher = watcher.fileSystemWatcher();
diff --git a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp
index 96909857b3..e38f05349b 100644
--- a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp
+++ b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp
@@ -3,11 +3,12 @@
#include "../utils/googletest.h"
-#include "../mocks/filesystemmock.h"
-#include "../mocks/projectstoragemock.h"
-#include "../mocks/projectstoragepathwatchermock.h"
-#include "../mocks/qmldocumentparsermock.h"
-#include "../mocks/qmltypesparsermock.h"
+#include <filesystemmock.h>
+#include <projectstorageerrornotifiermock.h>
+#include <projectstoragemock.h>
+#include <projectstoragepathwatchermock.h>
+#include <qmldocumentparsermock.h>
+#include <qmltypesparsermock.h>
#include <projectstorage-matcher.h>
@@ -27,10 +28,11 @@ using QmlDesigner::SourceId;
namespace Storage = QmlDesigner::Storage;
using QmlDesigner::IdPaths;
using QmlDesigner::Storage::Import;
+using QmlDesigner::Storage::ModuleKind;
+using QmlDesigner::Storage::Synchronization::DirectoryInfo;
using QmlDesigner::Storage::Synchronization::FileType;
using QmlDesigner::Storage::Synchronization::IsAutoVersion;
using QmlDesigner::Storage::Synchronization::ModuleExportedImport;
-using QmlDesigner::Storage::Synchronization::ProjectData;
using QmlDesigner::Storage::Synchronization::SynchronizationPackage;
using QmlDesigner::Storage::TypeTraits;
using QmlDesigner::Storage::TypeTraitsKind;
@@ -95,21 +97,21 @@ MATCHER_P3(IsFileStatus,
&& fileStatus.lastModified == lastModified;
}
-MATCHER_P4(IsProjectData,
- projectSourceId,
+MATCHER_P4(IsDirectoryInfo,
+ directorySourceId,
sourceId,
moduleId,
fileType,
std::string(negation ? "isn't " : "is ")
- + PrintToString(Storage::Synchronization::ProjectData{
- projectSourceId, sourceId, moduleId, fileType}))
+ + PrintToString(Storage::Synchronization::DirectoryInfo{
+ directorySourceId, sourceId, moduleId, fileType}))
{
- const Storage::Synchronization::ProjectData &projectData = arg;
+ const Storage::Synchronization::DirectoryInfo &directoryInfo = arg;
- return compareInvalidAreTrue(projectData.projectSourceId, projectSourceId)
- && projectData.sourceId == sourceId
- && compareInvalidAreTrue(projectData.moduleId, moduleId)
- && projectData.fileType == fileType;
+ return compareInvalidAreTrue(directoryInfo.directorySourceId, directorySourceId)
+ && directoryInfo.sourceId == sourceId
+ && compareInvalidAreTrue(directoryInfo.moduleId, moduleId)
+ && directoryInfo.fileType == fileType;
}
MATCHER(PackageIsEmpty, std::string(negation ? "isn't empty" : "is empty"))
@@ -117,9 +119,10 @@ MATCHER(PackageIsEmpty, std::string(negation ? "isn't empty" : "is empty"))
const Storage::Synchronization::SynchronizationPackage &package = arg;
return package.imports.empty() && package.types.empty() && package.fileStatuses.empty()
- && package.updatedSourceIds.empty() && package.projectDatas.empty()
- && package.updatedFileStatusSourceIds.empty() && package.updatedProjectSourceIds.empty()
- && package.moduleDependencies.empty() && package.updatedModuleDependencySourceIds.empty()
+ && package.updatedSourceIds.empty() && package.directoryInfos.empty()
+ && package.updatedFileStatusSourceIds.empty()
+ && package.updatedDirectoryInfoSourceIds.empty() && package.moduleDependencies.empty()
+ && package.updatedModuleDependencySourceIds.empty()
&& package.moduleExportedImports.empty() && package.updatedModuleIds.empty()
&& package.propertyEditorQmlPaths.empty()
&& package.updatedPropertyEditorQmlPathSourceIds.empty()
@@ -140,19 +143,16 @@ auto IsPropertyEditorQmlPath(const ModuleIdMatcher &moduleIdMatcher,
class ProjectStorageUpdater : public testing::Test
{
public:
- static void SetUpTestSuite()
+ struct StaticData
{
- static_database = std::make_unique<Sqlite::Database>(":memory:", Sqlite::JournalMode::Memory);
+ Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
+ NiceMock<ProjectStorageErrorNotifierMock> errorNotifierMock;
+ QmlDesigner::ProjectStorage storage{database, errorNotifierMock, database.isInitialized()};
+ };
- static_projectStorage = std::make_unique<QmlDesigner::ProjectStorage>(
- *static_database, static_database->isInitialized());
- }
+ static void SetUpTestSuite() { staticData = std::make_unique<StaticData>(); }
- static void TearDownTestSuite()
- {
- static_projectStorage.reset();
- static_database.reset();
- }
+ static void TearDownTestSuite() { staticData.reset(); }
ProjectStorageUpdater()
{
@@ -180,8 +180,8 @@ public:
setQmlFileNames(u"/path", {"First.qml", "First2.qml", "Second.qml"});
- ON_CALL(projectStorageMock, moduleId(_)).WillByDefault([&](const auto &name) {
- return storage.moduleId(name);
+ ON_CALL(projectStorageMock, moduleId(_, _)).WillByDefault([&](const auto &name, const auto &kind) {
+ return storage.moduleId(name, kind);
});
firstType.prototype = Storage::Synchronization::ImportedType{"Object"};
@@ -226,7 +226,7 @@ public:
});
}
- ~ProjectStorageUpdater() { static_projectStorage->resetForTestsOnly(); }
+ ~ProjectStorageUpdater() { storage.resetForTestsOnly(); }
void setFilesDontChanged(const QmlDesigner::SourceIds &sourceIds)
{
@@ -281,14 +281,14 @@ public:
ON_CALL(fileSystemMock, qmlFileNames(Eq(directoryPath))).WillByDefault(Return(qmlFileNames));
}
- void setProjectDatas(SourceId directoryPathSourceId,
- const QmlDesigner::Storage::Synchronization::ProjectDatas &projectDatas)
+ void setDirectoryInfos(SourceId directoryPathSourceId,
+ const QmlDesigner::Storage::Synchronization::DirectoryInfos &directoryInfos)
{
- ON_CALL(projectStorageMock, fetchProjectDatas(Eq(directoryPathSourceId)))
- .WillByDefault(Return(projectDatas));
- for (const ProjectData &projectData : projectDatas) {
- ON_CALL(projectStorageMock, fetchProjectData(Eq(projectData.sourceId)))
- .WillByDefault(Return(std::optional{projectData}));
+ ON_CALL(projectStorageMock, fetchDirectoryInfos(Eq(directoryPathSourceId)))
+ .WillByDefault(Return(directoryInfos));
+ for (const DirectoryInfo &directoryInfo : directoryInfos) {
+ ON_CALL(projectStorageMock, fetchDirectoryInfo(Eq(directoryInfo.sourceId)))
+ .WillByDefault(Return(std::optional{directoryInfo}));
}
}
@@ -302,7 +302,22 @@ public:
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(path))).WillRepeatedly(Return(content));
}
- auto moduleId(Utils::SmallStringView name) const { return storage.moduleId(name); }
+ void setSubdirectoryPaths(QStringView directoryPath, const QStringList &subdirectoryPaths)
+ {
+ ON_CALL(fileSystemMock, subdirectories(Eq(directoryPath))).WillByDefault(Return(subdirectoryPaths));
+ }
+
+ void setSubdirectorySourceIds(SourceId directorySourceId,
+ const QmlDesigner::SmallSourceIds<32> &subdirectorySourceId)
+ {
+ ON_CALL(projectStorageMock, fetchSubdirectorySourceIds(Eq(directorySourceId)))
+ .WillByDefault(Return(subdirectorySourceId));
+ }
+
+ auto moduleId(Utils::SmallStringView name, ModuleKind kind) const
+ {
+ return storage.moduleId(name, kind);
+ }
protected:
NiceMock<FileSystemMock> fileSystemMock;
@@ -310,10 +325,9 @@ protected:
NiceMock<QmlTypesParserMock> qmlTypesParserMock;
NiceMock<QmlDocumentParserMock> qmlDocumentParserMock;
QmlDesigner::FileStatusCache fileStatusCache{fileSystemMock};
- inline static std::unique_ptr<Sqlite::Database> static_database;
- Sqlite::Database &database = *static_database;
- inline static std::unique_ptr<QmlDesigner::ProjectStorage> static_projectStorage;
- QmlDesigner::ProjectStorage &storage = *static_projectStorage;
+ inline static std::unique_ptr<StaticData> staticData;
+ Sqlite::Database &database = staticData->database;
+ QmlDesigner::ProjectStorage &storage = staticData->storage;
QmlDesigner::SourcePathCache<QmlDesigner::ProjectStorage> sourcePathCache{
storage};
NiceMock<ProjectStoragePathWatcherMock> patchWatcherMock;
@@ -341,16 +355,16 @@ protected:
QmlDesigner::SourcePath{itemLibraryPath + "/."});
SourceId qmlImportsPathSourceId = sourcePathCache.sourceId(
QmlDesigner::SourcePath{qmlImportsPath + "/."});
- ModuleId qmlModuleId{storage.moduleId("Qml")};
- ModuleId qmlCppNativeModuleId{storage.moduleId("Qml-cppnative")};
- ModuleId exampleModuleId{storage.moduleId("Example")};
- ModuleId exampleCppNativeModuleId{storage.moduleId("Example-cppnative")};
- ModuleId builtinModuleId{storage.moduleId("QML")};
- ModuleId builtinCppNativeModuleId{storage.moduleId("QML-cppnative")};
- ModuleId quickModuleId{storage.moduleId("Quick")};
- ModuleId quickCppNativeModuleId{storage.moduleId("Quick-cppnative")};
- ModuleId pathModuleId{storage.moduleId("/path")};
- ModuleId subPathQmlModuleId{storage.moduleId("/path/qml")};
+ ModuleId qmlModuleId{storage.moduleId("Qml", ModuleKind::QmlLibrary)};
+ ModuleId qmlCppNativeModuleId{storage.moduleId("Qml", ModuleKind::CppLibrary)};
+ ModuleId exampleModuleId{storage.moduleId("Example", ModuleKind::QmlLibrary)};
+ ModuleId exampleCppNativeModuleId{storage.moduleId("Example", ModuleKind::CppLibrary)};
+ ModuleId builtinModuleId{storage.moduleId("QML", ModuleKind::QmlLibrary)};
+ ModuleId builtinCppNativeModuleId{storage.moduleId("QML", ModuleKind::CppLibrary)};
+ ModuleId quickModuleId{storage.moduleId("Quick", ModuleKind::QmlLibrary)};
+ ModuleId quickCppNativeModuleId{storage.moduleId("Quick", ModuleKind::CppLibrary)};
+ ModuleId pathModuleId{storage.moduleId("/path", ModuleKind::PathLibrary)};
+ ModuleId subPathQmlModuleId{storage.moduleId("/path/qml", ModuleKind::PathLibrary)};
Storage::Synchronization::Type objectType{
"QObject",
Storage::Synchronization::ImportedType{},
@@ -426,6 +440,24 @@ TEST_F(ProjectStorageUpdater, get_content_for_qml_dir_paths_if_file_status_is_di
updater.update(directories, {}, {}, {});
}
+TEST_F(ProjectStorageUpdater,
+ get_content_for_qml_dir_paths_if_file_status_is_different_for_subdirectories)
+{
+ SourceId qmlDir1PathSourceId = sourcePathCache.sourceId("/path/one/qmldir");
+ SourceId qmlDir2PathSourceId = sourcePathCache.sourceId("/path/two/qmldir");
+ SourceId qmlDir3PathSourceId = sourcePathCache.sourceId("/path/three/qmldir");
+ SourceId path3SourceId = sourcePathCache.sourceId("/path/three/.");
+ QStringList directories = {"/path/one"};
+ setSubdirectoryPaths(u"/path/one", {"/path/two", "/path/three"});
+ setFilesChanged({qmlDir1PathSourceId, qmlDir2PathSourceId});
+ setFilesDontChanged({qmlDir3PathSourceId, path3SourceId});
+
+ EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/one/qmldir"))));
+ EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/two/qmldir"))));
+
+ updater.update(directories, {}, {}, {});
+}
+
TEST_F(ProjectStorageUpdater, request_file_status_from_file_system)
{
EXPECT_CALL(fileSystemMock, fileStatus(Ne(directoryPathSourceId))).Times(AnyNumber());
@@ -435,6 +467,20 @@ TEST_F(ProjectStorageUpdater, request_file_status_from_file_system)
updater.update(directories, {}, {}, {});
}
+TEST_F(ProjectStorageUpdater, request_file_status_from_file_system_for_subdirectories)
+{
+ EXPECT_CALL(fileSystemMock,
+ fileStatus(AllOf(Ne(directoryPathSourceId), Ne(path1SourceId), Ne(path2SourceId))))
+ .Times(AnyNumber());
+ setSubdirectoryPaths(u"/path", {"/path/one", "/path/two"});
+
+ EXPECT_CALL(fileSystemMock, fileStatus(Eq(path1SourceId)));
+ EXPECT_CALL(fileSystemMock, fileStatus(Eq(path2SourceId)));
+ EXPECT_CALL(fileSystemMock, fileStatus(Eq(directoryPathSourceId)));
+
+ updater.update(directories, {}, {}, {});
+}
+
TEST_F(ProjectStorageUpdater, get_content_for_qml_types)
{
QString qmldir{R"(module Example
@@ -472,9 +518,30 @@ TEST_F(ProjectStorageUpdater, parse_qml_types)
setContent(u"/path/example2.qmltypes", qmltypes2);
EXPECT_CALL(qmlTypesParserMock,
- parse(qmltypes, _, _, Field(&ProjectData::moduleId, exampleCppNativeModuleId)));
+ parse(qmltypes, _, _, Field(&DirectoryInfo::moduleId, exampleCppNativeModuleId)));
EXPECT_CALL(qmlTypesParserMock,
- parse(qmltypes2, _, _, Field(&ProjectData::moduleId, exampleCppNativeModuleId)));
+ parse(qmltypes2, _, _, Field(&DirectoryInfo::moduleId, exampleCppNativeModuleId)));
+
+ updater.update(directories, {}, {}, {});
+}
+
+TEST_F(ProjectStorageUpdater, parse_qml_types_in_subdirectories)
+{
+ QString qmldir{R"(module Example
+ typeinfo example.qmltypes
+ typeinfo example2.qmltypes)"};
+ setContent(u"/path/qmldir", qmldir);
+ QString qmltypes{"Module {\ndependencies: []}"};
+ QString qmltypes2{"Module {\ndependencies: [foo]}"};
+ setContent(u"/path/example.qmltypes", qmltypes);
+ setContent(u"/path/example2.qmltypes", qmltypes2);
+ QStringList directories = {"/root"};
+ setSubdirectoryPaths(u"/root", {"/path"});
+
+ EXPECT_CALL(qmlTypesParserMock,
+ parse(qmltypes, _, _, Field(&DirectoryInfo::moduleId, exampleCppNativeModuleId)));
+ EXPECT_CALL(qmlTypesParserMock,
+ parse(qmltypes2, _, _, Field(&DirectoryInfo::moduleId, exampleCppNativeModuleId)));
updater.update(directories, {}, {}, {});
}
@@ -488,6 +555,23 @@ TEST_F(ProjectStorageUpdater, synchronize_is_empty_for_no_change)
updater.update(directories, {}, {}, {});
}
+TEST_F(ProjectStorageUpdater, synchronize_is_empty_for_no_change_in_subdirectory)
+{
+ SourceId qmlDirRootPathSourceId = sourcePathCache.sourceId("/root/qmldir");
+ SourceId rootPathSourceId = sourcePathCache.sourceId("/root/.");
+ setFilesDontChanged({qmltypesPathSourceId,
+ qmltypes2PathSourceId,
+ qmlDirPathSourceId,
+ qmlDirRootPathSourceId,
+ rootPathSourceId});
+ QStringList directories = {"/root"};
+ setSubdirectoryPaths(u"/root", {"/path"});
+
+ EXPECT_CALL(projectStorageMock, synchronize(PackageIsEmpty()));
+
+ updater.update(directories, {}, {}, {});
+}
+
TEST_F(ProjectStorageUpdater, synchronize_qml_types)
{
Storage::Import import{qmlModuleId, Storage::Version{2, 3}, qmltypesPathSourceId};
@@ -500,9 +584,9 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_types)
imports.push_back(import);
});
- EXPECT_CALL(projectStorageMock, moduleId(Eq("Example")));
- EXPECT_CALL(projectStorageMock, moduleId(Eq("Example-cppnative")));
- EXPECT_CALL(projectStorageMock, moduleId(Eq("/path")));
+ EXPECT_CALL(projectStorageMock, moduleId(Eq("Example"), ModuleKind::QmlLibrary));
+ EXPECT_CALL(projectStorageMock, moduleId(Eq("Example"), ModuleKind::CppLibrary));
+ EXPECT_CALL(projectStorageMock, moduleId(Eq("/path"), ModuleKind::PathLibrary));
EXPECT_CALL(projectStorageMock,
synchronize(
AllOf(Field(&SynchronizationPackage::imports, ElementsAre(import)),
@@ -512,17 +596,88 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_types)
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21),
IsFileStatus(qmltypesPathSourceId, 1, 21))),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmltypesPathSourceId,
- exampleCppNativeModuleId,
- FileType::QmlTypes))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmltypesPathSourceId,
+ exampleCppNativeModuleId,
+ FileType::QmlTypes))),
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)))));
updater.update(directories, {}, {}, {});
}
+TEST_F(ProjectStorageUpdater, synchronize_subdircectories)
+{
+ QStringList directories = {"/root"};
+ setSubdirectoryPaths(u"/root", {"/path/one", "/path/two"});
+ setSubdirectoryPaths(u"/path/one", {"/path/three"});
+ SourceId rootDirectoryPathSourceId = sourcePathCache.sourceId("/root/.");
+ setFilesChanged({rootDirectoryPathSourceId, path1SourceId, path2SourceId, path3SourceId});
+
+ EXPECT_CALL(
+ projectStorageMock,
+ synchronize(AllOf(
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(
+ IsDirectoryInfo(rootDirectoryPathSourceId, path1SourceId, ModuleId{}, FileType::Directory),
+ IsDirectoryInfo(rootDirectoryPathSourceId, path2SourceId, ModuleId{}, FileType::Directory),
+ IsDirectoryInfo(path1SourceId, path3SourceId, ModuleId{}, FileType::Directory))),
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
+ UnorderedElementsAre(
+ rootDirectoryPathSourceId, path1SourceId, path2SourceId, path3SourceId)))));
+
+ updater.update(directories, {}, {}, {});
+}
+
+TEST_F(ProjectStorageUpdater, synchronize_subdircectories_even_for_no_changes)
+{
+ QStringList directories = {"/root"};
+ setSubdirectoryPaths(u"/root", {"/path/one", "/path/two"});
+ setSubdirectoryPaths(u"/path/one", {"/path/three"});
+ SourceId rootDirectoryPathSourceId = sourcePathCache.sourceId("/root/.");
+ setFilesChanged({path1SourceId, path2SourceId, path3SourceId});
+ setFilesDontChanged({rootDirectoryPathSourceId});
+
+ EXPECT_CALL(projectStorageMock,
+ synchronize(
+ AllOf(Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(
+ path1SourceId, path3SourceId, ModuleId{}, FileType::Directory))),
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
+ UnorderedElementsAre(path1SourceId, path2SourceId, path3SourceId)))));
+
+ updater.update(directories, {}, {}, {});
+}
+
+TEST_F(ProjectStorageUpdater, synchronize_subdircectories_for_deleted_subdirecties)
+{
+ QStringList directories = {"/root"};
+ setSubdirectoryPaths(u"/root", {"/path/two"});
+ SourceId rootDirectoryPathSourceId = sourcePathCache.sourceId("/root/.");
+ setFilesChanged({rootDirectoryPathSourceId});
+ setFilesDontExists({
+ path1SourceId,
+ path3SourceId,
+ });
+ setSubdirectorySourceIds(rootDirectoryPathSourceId, {path1SourceId, path2SourceId});
+ setSubdirectorySourceIds(path1SourceId, {path3SourceId});
+
+ EXPECT_CALL(projectStorageMock,
+ synchronize(AllOf(Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(rootDirectoryPathSourceId,
+ path2SourceId,
+ ModuleId{},
+ FileType::Directory))),
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
+ UnorderedElementsAre(rootDirectoryPathSourceId,
+ path1SourceId,
+ path2SourceId,
+ path3SourceId)))));
+
+ updater.update(directories, {}, {}, {});
+}
+
TEST_F(ProjectStorageUpdater, synchronize_qml_types_throws_if_qmltpes_does_not_exists)
{
Storage::Import import{qmlModuleId, Storage::Version{2, 3}, qmltypesPathSourceId};
@@ -649,21 +804,21 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents)
IsFileStatus(qmlDocumentSourceId1, 1, 21),
IsFileStatus(qmlDocumentSourceId2, 1, 21),
IsFileStatus(qmlDocumentSourceId3, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId3,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId3,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.update(directories, {}, {}, {});
}
@@ -676,8 +831,8 @@ TEST_F(ProjectStorageUpdater, synchronize_add_only_qml_document_in_directory)
setFilesChanged({directoryPathSourceId});
setFilesDontChanged({qmlDirPathSourceId, qmlDocumentSourceId1});
setFilesAdded({qmlDocumentSourceId2});
- setProjectDatas(directoryPathSourceId,
- {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}});
+ setDirectoryInfos(directoryPathSourceId,
+ {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}});
setQmlFileNames(u"/path", {"First.qml", "First2.qml"});
EXPECT_CALL(projectStorageMock,
@@ -709,17 +864,17 @@ TEST_F(ProjectStorageUpdater, synchronize_add_only_qml_document_in_directory)
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(qmlDocumentSourceId2, 1, 21),
IsFileStatus(directoryPathSourceId, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.update(directories, {}, {}, {});
}
@@ -734,10 +889,11 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document)
setFilesChanged({qmlDirPathSourceId});
setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2});
setFilesRemoved({qmlDocumentSourceId3});
- setProjectDatas(directoryPathSourceId,
- {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
- {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument},
- {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}});
+ setDirectoryInfos(
+ directoryPathSourceId,
+ {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}});
setQmlFileNames(u"/path", {"First.qml", "First2.qml"});
EXPECT_CALL(projectStorageMock,
@@ -772,17 +928,17 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document)
UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId3)),
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.update(directories, {}, {}, {});
}
@@ -795,9 +951,9 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document_in_qmldir_only)
setContent(u"/path/qmldir", qmldir);
setFilesChanged({qmlDirPathSourceId});
setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2});
- setProjectDatas(directoryPathSourceId,
- {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
- {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}});
+ setDirectoryInfos(directoryPathSourceId,
+ {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}});
setQmlFileNames(u"/path", {"First.qml", "First2.qml"});
EXPECT_CALL(
@@ -827,17 +983,17 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document_in_qmldir_only)
UnorderedElementsAre(qmlDirPathSourceId)),
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.update(directories, {}, {}, {});
}
@@ -851,9 +1007,9 @@ TEST_F(ProjectStorageUpdater, synchronize_add_qml_document_to_qmldir)
setContent(u"/path/qmldir", qmldir);
setFilesChanged({qmlDirPathSourceId});
setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2});
- setProjectDatas(directoryPathSourceId,
- {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
- {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}});
+ setDirectoryInfos(directoryPathSourceId,
+ {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}});
setQmlFileNames(u"/path", {"First.qml", "First2.qml"});
EXPECT_CALL(
@@ -885,17 +1041,17 @@ TEST_F(ProjectStorageUpdater, synchronize_add_qml_document_to_qmldir)
UnorderedElementsAre(qmlDirPathSourceId)),
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.update(directories, {}, {}, {});
}
@@ -908,9 +1064,9 @@ TEST_F(ProjectStorageUpdater, synchronize_remove_qml_document_from_qmldir)
setContent(u"/path/qmldir", qmldir);
setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2});
setFilesChanged({qmlDirPathSourceId});
- setProjectDatas(directoryPathSourceId,
- {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
- {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}});
+ setDirectoryInfos(directoryPathSourceId,
+ {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}});
setQmlFileNames(u"/path", {"First.qml", "First2.qml"});
EXPECT_CALL(
@@ -940,17 +1096,17 @@ TEST_F(ProjectStorageUpdater, synchronize_remove_qml_document_from_qmldir)
UnorderedElementsAre(qmlDirPathSourceId)),
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.update(directories, {}, {}, {});
}
@@ -1006,28 +1162,28 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_dont_update_if_up_to_dat
IsFileStatus(qmlDocumentSourceId2, 1, 21))),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId3,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId3,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.update(directories, {}, {}, {});
}
TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed)
{
- setProjectDatas(
+ setDirectoryInfos(
directoryPathSourceId,
{{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
{directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes},
@@ -1071,14 +1227,14 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed)
qmltypes2PathSourceId,
qmlDocumentSourceId1,
qmlDocumentSourceId2)),
- Field(&SynchronizationPackage::projectDatas, IsEmpty()))));
+ Field(&SynchronizationPackage::directoryInfos, IsEmpty()))));
updater.update(directories, {}, {}, {});
}
TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed_and_some_updated_files)
{
- setProjectDatas(
+ setDirectoryInfos(
directoryPathSourceId,
{{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
{directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes},
@@ -1106,7 +1262,7 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed_and_some
IsFileStatus(qmlDocumentSourceId1, 1, 21))),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmltypesPathSourceId, qmlDocumentSourceId1)),
- Field(&SynchronizationPackage::projectDatas, IsEmpty()))));
+ Field(&SynchronizationPackage::directoryInfos, IsEmpty()))));
updater.update(directories, {}, {}, {});
}
@@ -1114,7 +1270,7 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed_and_some
TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_not_changed_and_some_removed_files)
{
setQmlFileNames(u"/path", {"First2.qml"});
- setProjectDatas(
+ setDirectoryInfos(
directoryPathSourceId,
{{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
{directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes},
@@ -1133,7 +1289,7 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_changed_and_some_rem
typeinfo example2.qmltypes)"};
setContent(u"/path/qmldir", qmldir);
setQmlFileNames(u"/path", {"First2.qml"});
- setProjectDatas(
+ setDirectoryInfos(
directoryPathSourceId,
{{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
{directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes},
@@ -1165,15 +1321,15 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_changed_and_some_rem
UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmlDirPathSourceId, qmltypesPathSourceId, qmlDocumentSourceId1)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmltypes2PathSourceId,
- exampleCppNativeModuleId,
- FileType::QmlTypes))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmltypes2PathSourceId,
+ exampleCppNativeModuleId,
+ FileType::QmlTypes))))));
updater.update(directories, {}, {}, {});
}
@@ -1187,8 +1343,8 @@ TEST_F(ProjectStorageUpdater, update_qml_types_files_is_empty)
Field(&SynchronizationPackage::updatedSourceIds, IsEmpty()),
Field(&SynchronizationPackage::fileStatuses, IsEmpty()),
Field(&SynchronizationPackage::updatedFileStatusSourceIds, IsEmpty()),
- Field(&SynchronizationPackage::projectDatas, IsEmpty()),
- Field(&SynchronizationPackage::updatedProjectSourceIds, IsEmpty()))));
+ Field(&SynchronizationPackage::directoryInfos, IsEmpty()),
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, IsEmpty()))));
updater.update({}, {}, {}, {});
}
@@ -1206,16 +1362,16 @@ TEST_F(ProjectStorageUpdater, update_qml_types_files)
IsFileStatus(qmltypes2PathSourceId, 1, 21))),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(qmltypesPathSourceId,
- qmltypesPathSourceId,
- builtinCppNativeModuleId,
- FileType::QmlTypes),
- IsProjectData(qmltypes2PathSourceId,
- qmltypes2PathSourceId,
- builtinCppNativeModuleId,
- FileType::QmlTypes))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(qmltypesPathSourceId,
+ qmltypesPathSourceId,
+ builtinCppNativeModuleId,
+ FileType::QmlTypes),
+ IsDirectoryInfo(qmltypes2PathSourceId,
+ qmltypes2PathSourceId,
+ builtinCppNativeModuleId,
+ FileType::QmlTypes))),
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId)))));
updater.update({}, {"/path/example.qmltypes", "/path/example2.qmltypes"}, {}, {});
@@ -1235,12 +1391,12 @@ TEST_F(ProjectStorageUpdater, dont_update_qml_types_files_if_unchanged)
UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 1, 21))),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmltypesPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(qmltypesPathSourceId,
- qmltypesPathSourceId,
- builtinCppNativeModuleId,
- FileType::QmlTypes))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(qmltypesPathSourceId,
+ qmltypesPathSourceId,
+ builtinCppNativeModuleId,
+ FileType::QmlTypes))),
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(qmltypesPathSourceId)))));
updater.update({}, {"/path/example.qmltypes", "/path/example2.qmltypes"}, {}, {});
@@ -1278,13 +1434,13 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_with_different_version_b
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21),
IsFileStatus(qmlDocumentSourceId1, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.update(directories, {}, {}, {});
}
@@ -1319,13 +1475,13 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_with_different_type_name
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21),
IsFileStatus(qmlDocumentSourceId1, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.update(directories, {}, {}, {});
}
@@ -1541,12 +1697,12 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_imports_with_double_entries)
updater.update(directories, {}, {}, {});
}
-TEST_F(ProjectStorageUpdater, synchronize_qmldir_optional_imports)
+TEST_F(ProjectStorageUpdater, synchronize_qmldir_default_imports)
{
QString qmldir{R"(module Example
import Qml auto
import QML 2.1
- optional import Quick
+ default import Quick
)"};
setContent(u"/path/qmldir", qmldir);
@@ -1583,6 +1739,40 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_optional_imports)
updater.update(directories, {}, {}, {});
}
+TEST_F(ProjectStorageUpdater, do_not_synchronize_qmldir_optional_imports)
+{
+ QString qmldir{R"(module Example
+ import Qml auto
+ import QML 2.1
+ optional import Quick
+ )"};
+ setContent(u"/path/qmldir", qmldir);
+
+ EXPECT_CALL(projectStorageMock,
+ synchronize(
+ AllOf(Field(&SynchronizationPackage::moduleExportedImports,
+ UnorderedElementsAre(ModuleExportedImport{exampleModuleId,
+ qmlModuleId,
+ Storage::Version{},
+ IsAutoVersion::Yes},
+ ModuleExportedImport{exampleCppNativeModuleId,
+ qmlCppNativeModuleId,
+ Storage::Version{},
+ IsAutoVersion::No},
+ ModuleExportedImport{exampleModuleId,
+ builtinModuleId,
+ Storage::Version{2, 1},
+ IsAutoVersion::No},
+ ModuleExportedImport{exampleCppNativeModuleId,
+ builtinCppNativeModuleId,
+ Storage::Version{},
+ IsAutoVersion::No})),
+ Field(&SynchronizationPackage::updatedModuleIds,
+ ElementsAre(exampleModuleId)))));
+
+ updater.update(directories, {}, {}, {});
+}
+
TEST_F(ProjectStorageUpdater, update_path_watcher_directories)
{
EXPECT_CALL(patchWatcherMock,
@@ -1712,11 +1902,11 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_only_qml_files_changed)
{
setFilesDontChanged({qmldir1SourceId, qmldir2SourceId, path1SourceId, path2SourceId});
setFilesChanged({firstSourceId, secondSourceId, thirdSourceId});
- setProjectDatas(path1SourceId,
- {{path1SourceId, firstSourceId, exampleModuleId, FileType::QmlDocument},
- {path1SourceId, secondSourceId, exampleModuleId, FileType::QmlDocument}});
- setProjectDatas(path2SourceId,
- {{path2SourceId, thirdSourceId, ModuleId{}, FileType::QmlDocument}});
+ setDirectoryInfos(path1SourceId,
+ {{path1SourceId, firstSourceId, exampleModuleId, FileType::QmlDocument},
+ {path1SourceId, secondSourceId, exampleModuleId, FileType::QmlDocument}});
+ setDirectoryInfos(path2SourceId,
+ {{path2SourceId, thirdSourceId, ModuleId{}, FileType::QmlDocument}});
EXPECT_CALL(patchWatcherMock,
updateIdPaths(Contains(IdPaths{projectPartId,
@@ -1735,11 +1925,11 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qml_files_and_directories_dont
firstSourceId,
secondSourceId,
thirdSourceId});
- setProjectDatas(path1SourceId,
- {{path1SourceId, firstSourceId, exampleModuleId, FileType::QmlDocument},
- {path1SourceId, secondSourceId, exampleModuleId, FileType::QmlDocument}});
- setProjectDatas(path2SourceId,
- {{path2SourceId, thirdSourceId, ModuleId{}, FileType::QmlDocument}});
+ setDirectoryInfos(path1SourceId,
+ {{path1SourceId, firstSourceId, exampleModuleId, FileType::QmlDocument},
+ {path1SourceId, secondSourceId, exampleModuleId, FileType::QmlDocument}});
+ setDirectoryInfos(path2SourceId,
+ {{path2SourceId, thirdSourceId, ModuleId{}, FileType::QmlDocument}});
EXPECT_CALL(patchWatcherMock,
updateIdPaths(Contains(IdPaths{projectPartId,
@@ -1790,10 +1980,10 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_only_qmltypes_files_changed)
{
setFilesDontChanged({qmldir1SourceId, qmldir2SourceId, path1SourceId, path2SourceId});
setFilesChanged({qmltypes1SourceId, qmltypes2SourceId});
- setProjectDatas(path1SourceId,
- {{path1SourceId, qmltypes1SourceId, exampleModuleId, FileType::QmlTypes}});
- setProjectDatas(path2SourceId,
- {{path2SourceId, qmltypes2SourceId, exampleModuleId, FileType::QmlTypes}});
+ setDirectoryInfos(path1SourceId,
+ {{path1SourceId, qmltypes1SourceId, exampleModuleId, FileType::QmlTypes}});
+ setDirectoryInfos(path2SourceId,
+ {{path2SourceId, qmltypes2SourceId, exampleModuleId, FileType::QmlTypes}});
EXPECT_CALL(patchWatcherMock,
updateIdPaths(Contains(IdPaths{projectPartId,
@@ -1811,10 +2001,10 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qmltypes_files_and_directories
path2SourceId,
qmltypes1SourceId,
qmltypes2SourceId});
- setProjectDatas(path1SourceId,
- {{path1SourceId, qmltypes1SourceId, exampleModuleId, FileType::QmlTypes}});
- setProjectDatas(path2SourceId,
- {{path2SourceId, qmltypes2SourceId, exampleModuleId, FileType::QmlTypes}});
+ setDirectoryInfos(path1SourceId,
+ {{path1SourceId, qmltypes1SourceId, exampleModuleId, FileType::QmlTypes}});
+ setDirectoryInfos(path2SourceId,
+ {{path2SourceId, qmltypes2SourceId, exampleModuleId, FileType::QmlTypes}});
EXPECT_CALL(patchWatcherMock,
updateIdPaths(Contains(IdPaths{projectPartId,
@@ -1887,21 +2077,21 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir)
IsFileStatus(qmlDocumentSourceId1, 1, 21),
IsFileStatus(qmlDocumentSourceId2, 1, 21),
IsFileStatus(qmlDocumentSourceId3, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId3,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId3,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.update(directories, {}, {}, {});
}
@@ -1917,10 +2107,11 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if
TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if_directory_does_not_exists)
{
setFilesDontExists({qmlDirPathSourceId, directoryPathSourceId});
- setProjectDatas(directoryPathSourceId,
- {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
- {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument},
- {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}});
+ setDirectoryInfos(
+ directoryPathSourceId,
+ {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}});
EXPECT_CALL(projectStorageMock,
synchronize(AllOf(Field(&SynchronizationPackage::imports, IsEmpty()),
@@ -1937,9 +2128,9 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if
qmlDocumentSourceId2,
qmlDocumentSourceId3)),
Field(&SynchronizationPackage::fileStatuses, IsEmpty()),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas, IsEmpty()))));
+ Field(&SynchronizationPackage::directoryInfos, IsEmpty()))));
updater.update(directories, {}, {}, {});
}
@@ -1950,9 +2141,9 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_add_qml_d
setFilesChanged({directoryPathSourceId});
setFilesAdded({qmlDocumentSourceId3});
setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2});
- setProjectDatas(directoryPathSourceId,
- {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
- {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}});
+ setDirectoryInfos(directoryPathSourceId,
+ {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}});
EXPECT_CALL(
projectStorageMock,
@@ -1974,21 +2165,21 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_add_qml_d
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(directoryPathSourceId, 1, 21),
IsFileStatus(qmlDocumentSourceId3, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId3,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId3,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.update(directories, {}, {}, {});
}
@@ -2000,10 +2191,11 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_removes_q
setFilesRemoved({qmlDocumentSourceId3});
setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2});
setQmlFileNames(u"/path", {"First.qml", "First2.qml"});
- setProjectDatas(directoryPathSourceId,
- {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
- {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument},
- {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}});
+ setDirectoryInfos(
+ directoryPathSourceId,
+ {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}});
EXPECT_CALL(projectStorageMock,
synchronize(
@@ -2017,17 +2209,17 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_removes_q
qmlDocumentSourceId3)),
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(directoryPathSourceId, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.update(directories, {}, {}, {});
}
@@ -2085,25 +2277,93 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories)
IsFileStatus(qmlDocumentSourceId1, 1, 21),
IsFileStatus(qmlDocumentSourceId2, 1, 21),
IsFileStatus(qmlDocumentSourceId3, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId3,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId3,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}});
}
+TEST_F(ProjectStorageUpdater, watcher_updates_subdirectories)
+{
+ QString qmldir{R"(module Example
+ FirstType 1.0 First.qml
+ FirstType 2.2 First2.qml
+ SecondType 2.2 Second.qml)"};
+ setContent(u"/path/qmldir", qmldir);
+ SourceId rootPathSourceId = sourcePathCache.sourceId("/root/.");
+ SourceId rootQmldirPathSourceId = sourcePathCache.sourceId("/root/qmldir");
+ setFilesChanged({directoryPathSourceId, rootPathSourceId});
+ setFilesDontChanged({qmlDirPathSourceId, rootQmldirPathSourceId});
+ setSubdirectoryPaths(u"/root", {"/path"});
+ setSubdirectorySourceIds(rootPathSourceId, {directoryPathSourceId});
+
+ EXPECT_CALL(
+ projectStorageMock,
+ synchronize(AllOf(
+ Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import2, import3)),
+ Field(
+ &SynchronizationPackage::types,
+ UnorderedElementsAre(
+ AllOf(IsStorageType("First.qml",
+ Storage::Synchronization::ImportedType{"Object"},
+ TypeTraitsKind::Reference,
+ qmlDocumentSourceId1,
+ Storage::Synchronization::ChangeLevel::Full),
+ Field(&Storage::Synchronization::Type::exportedTypes,
+ UnorderedElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0),
+ IsExportedType(pathModuleId, "First", -1, -1)))),
+ AllOf(IsStorageType("First2.qml",
+ Storage::Synchronization::ImportedType{"Object2"},
+ TypeTraitsKind::Reference,
+ qmlDocumentSourceId2,
+ Storage::Synchronization::ChangeLevel::Full),
+ Field(&Storage::Synchronization::Type::exportedTypes,
+ UnorderedElementsAre(IsExportedType(exampleModuleId, "FirstType", 2, 2),
+ IsExportedType(pathModuleId, "First2", -1, -1)))),
+ AllOf(IsStorageType("Second.qml",
+ Storage::Synchronization::ImportedType{"Object3"},
+ TypeTraitsKind::Reference,
+ qmlDocumentSourceId3,
+ Storage::Synchronization::ChangeLevel::Full),
+ Field(&Storage::Synchronization::Type::exportedTypes,
+ UnorderedElementsAre(IsExportedType(exampleModuleId, "SecondType", 2, 2),
+ IsExportedType(pathModuleId, "Second", -1, -1)))))),
+ Field(&SynchronizationPackage::updatedSourceIds,
+ UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2, qmlDocumentSourceId3)),
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId3,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(rootPathSourceId,
+ directoryPathSourceId,
+ ModuleId{},
+ FileType::Directory))))));
+
+ updater.pathsWithIdsChanged({{directoryProjectChunkId, {rootPathSourceId, directoryPathSourceId}}});
+}
+
TEST_F(ProjectStorageUpdater, watcher_updates_removed_directory)
{
setFilesRemoved({directoryPathSourceId,
@@ -2111,10 +2371,11 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removed_directory)
qmlDocumentSourceId1,
qmlDocumentSourceId2,
qmlDocumentSourceId3});
- setProjectDatas(directoryPathSourceId,
- {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
- {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument},
- {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}});
+ setDirectoryInfos(
+ directoryPathSourceId,
+ {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}});
EXPECT_CALL(projectStorageMock,
synchronize(AllOf(Field(&SynchronizationPackage::imports, IsEmpty()),
@@ -2131,9 +2392,9 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removed_directory)
qmlDocumentSourceId2,
qmlDocumentSourceId3)),
Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre()),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas, IsEmpty()))));
+ Field(&SynchronizationPackage::directoryInfos, IsEmpty()))));
updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}});
}
@@ -2233,21 +2494,21 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_and_qmldir)
IsFileStatus(qmlDocumentSourceId1, 1, 21),
IsFileStatus(qmlDocumentSourceId2, 1, 21),
IsFileStatus(qmlDocumentSourceId3, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId3,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId3,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}},
{qmldirProjectChunkId, {qmlDirPathSourceId}}});
@@ -2297,8 +2558,8 @@ TEST_F(ProjectStorageUpdater, watcher_updates_add_only_qml_document_in_directory
setFilesChanged({directoryPathSourceId});
setFilesDontChanged({qmlDirPathSourceId, qmlDocumentSourceId1});
setFilesAdded({qmlDocumentSourceId2});
- setProjectDatas(directoryPathSourceId,
- {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}});
+ setDirectoryInfos(directoryPathSourceId,
+ {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}});
setQmlFileNames(u"/path", {"First.qml", "First2.qml"});
EXPECT_CALL(projectStorageMock,
@@ -2330,17 +2591,17 @@ TEST_F(ProjectStorageUpdater, watcher_updates_add_only_qml_document_in_directory
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(qmlDocumentSourceId2, 1, 21),
IsFileStatus(directoryPathSourceId, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}});
}
@@ -2355,10 +2616,11 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removes_qml_document)
setFilesChanged({qmlDirPathSourceId});
setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2});
setFilesRemoved({qmlDocumentSourceId3});
- setProjectDatas(directoryPathSourceId,
- {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
- {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument},
- {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}});
+ setDirectoryInfos(
+ directoryPathSourceId,
+ {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}});
setQmlFileNames(u"/path", {"First.qml", "First2.qml"});
EXPECT_CALL(projectStorageMock,
@@ -2393,17 +2655,17 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removes_qml_document)
UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId3)),
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}});
}
@@ -2416,9 +2678,9 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removes_qml_document_in_qmldir_onl
setContent(u"/path/qmldir", qmldir);
setFilesChanged({qmlDirPathSourceId});
setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2});
- setProjectDatas(directoryPathSourceId,
- {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
- {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}});
+ setDirectoryInfos(directoryPathSourceId,
+ {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}});
setQmlFileNames(u"/path", {"First.qml", "First2.qml"});
EXPECT_CALL(
@@ -2448,17 +2710,17 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removes_qml_document_in_qmldir_onl
UnorderedElementsAre(qmlDirPathSourceId)),
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}});
}
@@ -2472,9 +2734,9 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_add_qml_document_to_qm
setContent(u"/path/qmldir", qmldir);
setFilesChanged({qmlDirPathSourceId});
setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2});
- setProjectDatas(directoryPathSourceId,
- {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
- {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}});
+ setDirectoryInfos(directoryPathSourceId,
+ {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}});
setQmlFileNames(u"/path", {"First.qml", "First2.qml"});
EXPECT_CALL(
@@ -2506,17 +2768,17 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_add_qml_document_to_qm
UnorderedElementsAre(qmlDirPathSourceId)),
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}});
}
@@ -2529,9 +2791,9 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_remove_qml_document_fr
setContent(u"/path/qmldir", qmldir);
setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2});
setFilesChanged({qmlDirPathSourceId});
- setProjectDatas(directoryPathSourceId,
- {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
- {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}});
+ setDirectoryInfos(directoryPathSourceId,
+ {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}});
setQmlFileNames(u"/path", {"First.qml", "First2.qml"});
EXPECT_CALL(
@@ -2561,17 +2823,17 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_remove_qml_document_fr
UnorderedElementsAre(qmlDirPathSourceId)),
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}});
}
@@ -2627,21 +2889,21 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_dont_update_qml_docume
IsFileStatus(qmlDocumentSourceId2, 1, 21))),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId3,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId3,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}});
}
@@ -2697,28 +2959,28 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldirs_dont_update_qml_documents_
IsFileStatus(qmlDocumentSourceId2, 1, 21))),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId3,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId3,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}});
}
TEST_F(ProjectStorageUpdater, watcher_updates_directory_but_not_qmldir)
{
- setProjectDatas(
+ setDirectoryInfos(
directoryPathSourceId,
{{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
{directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes},
@@ -2762,7 +3024,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directory_but_not_qmldir)
qmltypes2PathSourceId,
qmlDocumentSourceId1,
qmlDocumentSourceId2)),
- Field(&SynchronizationPackage::projectDatas, IsEmpty()))));
+ Field(&SynchronizationPackage::directoryInfos, IsEmpty()))));
updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}});
}
@@ -2794,7 +3056,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qml_documents)
IsFileStatus(qmlDocumentSourceId2, 1, 21))),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2)),
- Field(&SynchronizationPackage::projectDatas, IsEmpty()))));
+ Field(&SynchronizationPackage::directoryInfos, IsEmpty()))));
updater.pathsWithIdsChanged(
{{qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}});
@@ -2821,7 +3083,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removed_qml_documents)
UnorderedElementsAre(IsFileStatus(qmlDocumentSourceId1, 1, 21))),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2)),
- Field(&SynchronizationPackage::projectDatas, IsEmpty()))));
+ Field(&SynchronizationPackage::directoryInfos, IsEmpty()))));
updater.pathsWithIdsChanged(
{{qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}});
@@ -2844,7 +3106,7 @@ TEST_F(ProjectStorageUpdater, watcher_dont_updates_qml_documents_for_other_proje
TEST_F(ProjectStorageUpdater, watcher_updates_qmltypes)
{
- setProjectDatas(
+ setDirectoryInfos(
directoryPathSourceId,
{{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
{directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}});
@@ -2864,7 +3126,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmltypes)
IsFileStatus(qmltypes2PathSourceId, 1, 21))),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId)),
- Field(&SynchronizationPackage::projectDatas, IsEmpty()))));
+ Field(&SynchronizationPackage::directoryInfos, IsEmpty()))));
updater.pathsWithIdsChanged(
{{qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}}});
@@ -2872,7 +3134,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmltypes)
TEST_F(ProjectStorageUpdater, watcher_updates_removed_qmltypes_without_updated_qmldir)
{
- setProjectDatas(
+ setDirectoryInfos(
directoryPathSourceId,
{{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
{directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}});
@@ -2888,7 +3150,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removed_qmltypes_without_updated_q
TEST_F(ProjectStorageUpdater, watcher_updates_removed_qmltypes_with_updated_qmldir)
{
- setProjectDatas(
+ setDirectoryInfos(
directoryPathSourceId,
{{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
{directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}});
@@ -2918,18 +3180,18 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removed_qmltypes_with_updated_qmld
UnorderedElementsAre(qmlDirPathSourceId,
qmltypesPathSourceId,
qmltypes2PathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmltypes2PathSourceId,
- exampleCppNativeModuleId,
- FileType::QmlTypes))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmltypes2PathSourceId,
+ exampleCppNativeModuleId,
+ FileType::QmlTypes))))));
updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}});
}
TEST_F(ProjectStorageUpdater, watcher_dont_watches_directories_after_qmltypes_changes)
{
- setProjectDatas(
+ setDirectoryInfos(
directoryPathSourceId,
{{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
{directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}});
@@ -2944,7 +3206,7 @@ TEST_F(ProjectStorageUpdater, watcher_dont_watches_directories_after_qmltypes_ch
TEST_F(ProjectStorageUpdater, watcher_dont_updates_qmltypes_for_other_projects)
{
- setProjectDatas(
+ setDirectoryInfos(
directoryPathSourceId,
{{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
{directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}});
@@ -3010,21 +3272,21 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_and_but_not_included_q
IsFileStatus(qmlDocumentSourceId1, 1, 21),
IsFileStatus(qmlDocumentSourceId2, 1, 21),
IsFileStatus(qmlDocumentSourceId3, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId3,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId3,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}},
{qmlDocumentProjectChunkId,
@@ -3087,21 +3349,21 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldir_and_but_not_included_qml_do
IsFileStatus(qmlDocumentSourceId1, 1, 21),
IsFileStatus(qmlDocumentSourceId2, 1, 21),
IsFileStatus(qmlDocumentSourceId3, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId3,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId3,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}},
{qmlDocumentProjectChunkId,
@@ -3110,7 +3372,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldir_and_but_not_included_qml_do
TEST_F(ProjectStorageUpdater, watcher_updates_qmldir_and_but_not_included_qmltypes)
{
- setProjectDatas(
+ setDirectoryInfos(
directoryPathSourceId,
{{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
{directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes},
@@ -3186,29 +3448,29 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldir_and_but_not_included_qmltyp
IsFileStatus(qmlDocumentSourceId1, 1, 21),
IsFileStatus(qmlDocumentSourceId2, 1, 21),
IsFileStatus(qmlDocumentSourceId3, 1, 21))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
+ Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds,
UnorderedElementsAre(directoryPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(directoryPathSourceId,
- qmltypesPathSourceId,
- exampleCppNativeModuleId,
- FileType::QmlTypes),
- IsProjectData(directoryPathSourceId,
- qmltypes2PathSourceId,
- exampleCppNativeModuleId,
- FileType::QmlTypes),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(directoryPathSourceId,
- qmlDocumentSourceId3,
- ModuleId{},
- FileType::QmlDocument))))));
+ Field(&SynchronizationPackage::directoryInfos,
+ UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId,
+ qmltypesPathSourceId,
+ exampleCppNativeModuleId,
+ FileType::QmlTypes),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmltypes2PathSourceId,
+ exampleCppNativeModuleId,
+ FileType::QmlTypes),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsDirectoryInfo(directoryPathSourceId,
+ qmlDocumentSourceId3,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.pathsWithIdsChanged(
{{qmldirProjectChunkId, {qmlDirPathSourceId}},
@@ -3236,7 +3498,7 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens)
FirstType 2.2 First2.qml
SecondType 2.2 Second.qml)"};
setContent(u"/path/qmldir", qmldir);
- setProjectDatas(
+ setDirectoryInfos(
directoryPathSourceId,
{{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
{directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}});
@@ -3283,7 +3545,7 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens)
qmlDocumentSourceId2,
qmltypesPathSourceId,
qmltypes2PathSourceId)),
- Field(&SynchronizationPackage::projectDatas, IsEmpty()))));
+ Field(&SynchronizationPackage::directoryInfos, IsEmpty()))));
updater.pathsWithIdsChanged(
{{qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}});
@@ -3296,7 +3558,7 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens_and_
FirstType 2.2 First2.qml
SecondType 2.2 Second.qml)"};
setContent(u"/path/qmldir", qmldir);
- setProjectDatas(
+ setDirectoryInfos(
directoryPathSourceId,
{{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
{directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}});
@@ -3343,7 +3605,7 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens_and_
qmlDocumentSourceId2,
qmltypesPathSourceId,
qmltypes2PathSourceId)),
- Field(&SynchronizationPackage::projectDatas, IsEmpty()))));
+ Field(&SynchronizationPackage::directoryInfos, IsEmpty()))));
updater.pathsWithIdsChanged(
{{qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}},
@@ -3357,7 +3619,7 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens_and_
FirstType 2.2 First2.qml
SecondType 2.2 Second.qml)"};
setContent(u"/path/qmldir", qmldir);
- setProjectDatas(
+ setDirectoryInfos(
directoryPathSourceId,
{{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
{directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes},
@@ -3406,7 +3668,7 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens_and_
qmlDocumentSourceId2,
qmltypesPathSourceId,
qmltypes2PathSourceId)),
- Field(&SynchronizationPackage::projectDatas, IsEmpty()))));
+ Field(&SynchronizationPackage::directoryInfos, IsEmpty()))));
updater.pathsWithIdsChanged(
{{qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}},
@@ -3420,7 +3682,7 @@ TEST_F(ProjectStorageUpdater, input_is_cleared_after_successful_update)
FirstType 2.2 First2.qml
SecondType 2.2 Second.qml)"};
setContent(u"/path/qmldir", qmldir);
- setProjectDatas(
+ setDirectoryInfos(
directoryPathSourceId,
{{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
{directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}});
@@ -3453,7 +3715,7 @@ TEST_F(ProjectStorageUpdater, input_is_cleared_after_successful_update)
IsFileStatus(qmlDocumentSourceId2, 1, 21))),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2)),
- Field(&SynchronizationPackage::projectDatas, IsEmpty()))));
+ Field(&SynchronizationPackage::directoryInfos, IsEmpty()))));
updater.pathsWithIdsChanged(
{{qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}});
@@ -3475,7 +3737,7 @@ TEST_F(ProjectStorageUpdater, update_property_editor_panes)
auto directoryId = sourcePathCache.sourceId(
QmlDesigner::SourcePath{propertyEditorQmlPath + "/QML/."});
setFilesChanged({directoryId});
- auto qmlModuleId = storage.moduleId("QML");
+ auto qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary);
EXPECT_CALL(projectStorageMock,
synchronize(
@@ -3499,19 +3761,26 @@ TEST_F(ProjectStorageUpdater, update_property_editor_specifics)
ON_CALL(projectStorageMock, fetchFileStatus(_)).WillByDefault([](SourceId sourceId) {
return FileStatus{sourceId, 1, 21};
});
- auto sourceId = sourcePathCache.sourceId(
+ auto textSourceId = sourcePathCache.sourceId(
QmlDesigner::SourcePath{propertyEditorQmlPath + "/QtQuick/TextSpecifics.qml"});
- auto directoryId = sourcePathCache.sourceId(
+ auto qtQuickDirectoryId = sourcePathCache.sourceId(
QmlDesigner::SourcePath{propertyEditorQmlPath + "/QtQuick/."});
- setFilesChanged({directoryId});
- auto qtQuickModuleId = storage.moduleId("QtQuick");
+ auto buttonSourceId = sourcePathCache.sourceId(
+ QmlDesigner::SourcePath{propertyEditorQmlPath + "/QtQuick/Controls/ButtonSpecifics.qml"});
+ auto controlsDirectoryId = sourcePathCache.sourceId(
+ QmlDesigner::SourcePath{propertyEditorQmlPath + "/QtQuick/Controls/."});
+ setFilesChanged({qtQuickDirectoryId, controlsDirectoryId});
+ auto qtQuickModuleId = storage.moduleId("QtQuick", ModuleKind::QmlLibrary);
+ auto controlsModuleId = storage.moduleId("QtQuick.Controls", ModuleKind::QmlLibrary);
EXPECT_CALL(projectStorageMock,
- synchronize(
- AllOf(Field(&SynchronizationPackage::propertyEditorQmlPaths,
- Contains(IsPropertyEditorQmlPath(qtQuickModuleId, "Text", sourceId))),
- Field(&SynchronizationPackage::updatedPropertyEditorQmlPathSourceIds,
- ElementsAre(directoryId)))));
+ synchronize(AllOf(
+ Field(&SynchronizationPackage::propertyEditorQmlPaths,
+ IsSupersetOf(
+ {IsPropertyEditorQmlPath(qtQuickModuleId, "Text", textSourceId),
+ IsPropertyEditorQmlPath(controlsModuleId, "Button", buttonSourceId)})),
+ Field(&SynchronizationPackage::updatedPropertyEditorQmlPathSourceIds,
+ ElementsAre(qtQuickDirectoryId, controlsDirectoryId)))));
updater.update({}, {}, propertyEditorQmlPath, {});
}
@@ -3538,8 +3807,8 @@ TEST_F(ProjectStorageUpdater, update_type_annotations)
auto buttonSourceId = sourcePathCache.sourceId(
QmlDesigner::SourcePath{itemLibraryPath + "/qtquickcontrols2.metainfo"});
setFilesChanged({itemLibraryPathSourceId, itemSourceId, buttonSourceId});
- auto qtQuickModuleId = moduleId("QtQuick");
- auto qtQuickControlsModuleId = moduleId("QtQuick.Controls.Basic");
+ auto qtQuickModuleId = moduleId("QtQuick", ModuleKind::QmlLibrary);
+ auto qtQuickControlsModuleId = moduleId("QtQuick.Controls.Basic", ModuleKind::QmlLibrary);
QmlDesigner::Storage::TypeTraits itemTraits;
itemTraits.canBeContainer = QmlDesigner::FlagIs::True;
@@ -3575,8 +3844,8 @@ TEST_F(ProjectStorageUpdater, update_changed_type_annotation)
QmlDesigner::SourcePath{itemLibraryPath + "/qtquickcontrols2.metainfo"});
setFilesDontChanged({itemLibraryPathSourceId});
setFilesChanged({itemSourceId, buttonSourceId});
- auto qtQuickModuleId = moduleId("QtQuick");
- auto qtQuickControlsModuleId = moduleId("QtQuick.Controls.Basic");
+ auto qtQuickModuleId = moduleId("QtQuick", ModuleKind::QmlLibrary);
+ auto qtQuickControlsModuleId = moduleId("QtQuick.Controls.Basic", ModuleKind::QmlLibrary);
QmlDesigner::Storage::TypeTraits itemTraits;
itemTraits.canBeContainer = QmlDesigner::FlagIs::True;
diff --git a/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp b/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp
index 513fbf2ec0..e135bb27bd 100644
--- a/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp
+++ b/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp
@@ -3,6 +3,8 @@
#include "../utils/googletest.h"
+#include <projectstorageerrornotifiermock.h>
+
#include <sqlitedatabase.h>
#include <projectstorage/projectstorage.h>
@@ -16,6 +18,7 @@ namespace Synchronization = Storage::Synchronization;
using QmlDesigner::ModuleId;
using QmlDesigner::SourceContextId;
using QmlDesigner::SourceId;
+using QmlDesigner::Storage::ModuleKind;
MATCHER_P(HasPrototype, prototype, std::string(negation ? "isn't " : "is ") + PrintToString(prototype))
{
@@ -143,7 +146,8 @@ class QmlDocumentParser : public ::testing::Test
public:
protected:
Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
- QmlDesigner::ProjectStorage storage{database, database.isInitialized()};
+ ProjectStorageErrorNotifierMock errorNotifierMock;
+ QmlDesigner::ProjectStorage storage{database, errorNotifierMock, database.isInitialized()};
QmlDesigner::SourcePathCache<QmlDesigner::ProjectStorage> sourcePathCache{
storage};
QmlDesigner::QmlDocumentParser parser{storage, sourcePathCache};
@@ -151,7 +155,7 @@ protected:
SourceId qmlFileSourceId{sourcePathCache.sourceId("/path/to/qmlfile.qml")};
SourceContextId qmlFileSourceContextId{sourcePathCache.sourceContextId(qmlFileSourceId)};
Utils::PathString directoryPath{sourcePathCache.sourceContextPath(qmlFileSourceContextId)};
- ModuleId directoryModuleId{storage.moduleId(directoryPath)};
+ ModuleId directoryModuleId{storage.moduleId(directoryPath, ModuleKind::PathLibrary)};
};
TEST_F(QmlDocumentParser, prototype)
@@ -163,7 +167,7 @@ TEST_F(QmlDocumentParser, prototype)
TEST_F(QmlDocumentParser, qualified_prototype)
{
- auto exampleModuleId = storage.moduleId("Example");
+ auto exampleModuleId = storage.moduleId("Example", ModuleKind::QmlLibrary);
QString text = R"(import Example 2.1 as Example
Example.Item{})";
@@ -187,7 +191,7 @@ TEST_F(QmlDocumentParser, properties)
TEST_F(QmlDocumentParser, qualified_properties)
{
- auto exampleModuleId = storage.moduleId("Example");
+ auto exampleModuleId = storage.moduleId("Example", ModuleKind::QmlLibrary);
auto type = parser.parse(R"(import Example 2.1 as Example
Item{ property Example.Foo foo})",
@@ -222,7 +226,7 @@ TEST_F(QmlDocumentParser, enumeration_in_properties)
TEST_F(QmlDocumentParser, qualified_enumeration_in_properties)
{
- auto exampleModuleId = storage.moduleId("Example");
+ auto exampleModuleId = storage.moduleId("Example", ModuleKind::QmlLibrary);
auto type = parser.parse(R"(import Example 2.1 as Example
Item{ property Example.Enumeration.Foo foo})",
@@ -242,9 +246,9 @@ TEST_F(QmlDocumentParser, qualified_enumeration_in_properties)
TEST_F(QmlDocumentParser, imports)
{
- ModuleId fooDirectoryModuleId = storage.moduleId("/path/foo");
- ModuleId qmlModuleId = storage.moduleId("QML");
- ModuleId qtQuickModuleId = storage.moduleId("QtQuick");
+ ModuleId fooDirectoryModuleId = storage.moduleId("/path/foo", ModuleKind::PathLibrary);
+ ModuleId qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary);
+ ModuleId qtQuickModuleId = storage.moduleId("QtQuick", ModuleKind::QmlLibrary);
auto type = parser.parse(R"(import QtQuick
import "../foo"
@@ -263,9 +267,9 @@ TEST_F(QmlDocumentParser, imports)
TEST_F(QmlDocumentParser, imports_with_version)
{
- ModuleId fooDirectoryModuleId = storage.moduleId("/path/foo");
- ModuleId qmlModuleId = storage.moduleId("QML");
- ModuleId qtQuickModuleId = storage.moduleId("QtQuick");
+ ModuleId fooDirectoryModuleId = storage.moduleId("/path/foo", ModuleKind::PathLibrary);
+ ModuleId qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary);
+ ModuleId qtQuickModuleId = storage.moduleId("QtQuick", ModuleKind::QmlLibrary);
auto type = parser.parse(R"(import QtQuick 2.1
import "../foo"
@@ -284,8 +288,8 @@ TEST_F(QmlDocumentParser, imports_with_version)
TEST_F(QmlDocumentParser, imports_with_explict_directory)
{
- ModuleId qmlModuleId = storage.moduleId("QML");
- ModuleId qtQuickModuleId = storage.moduleId("QtQuick");
+ ModuleId qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary);
+ ModuleId qtQuickModuleId = storage.moduleId("QtQuick", ModuleKind::QmlLibrary);
auto type = parser.parse(R"(import QtQuick
import "../to"
@@ -358,10 +362,10 @@ TEST_F(QmlDocumentParser, enumeration)
TEST_F(QmlDocumentParser, DISABLED_duplicate_imports_are_removed)
{
- ModuleId fooDirectoryModuleId = storage.moduleId("/path/foo");
- ModuleId qmlModuleId = storage.moduleId("QML");
- ModuleId qtQmlModuleId = storage.moduleId("QtQml");
- ModuleId qtQuickModuleId = storage.moduleId("QtQuick");
+ ModuleId fooDirectoryModuleId = storage.moduleId("/path/foo", ModuleKind::PathLibrary);
+ ModuleId qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary);
+ ModuleId qtQmlModuleId = storage.moduleId("QtQml", ModuleKind::QmlLibrary);
+ ModuleId qtQuickModuleId = storage.moduleId("QtQuick", ModuleKind::QmlLibrary);
auto type = parser.parse(R"(import QtQuick
import "../foo"
@@ -497,7 +501,7 @@ TEST_F(QmlDocumentParser, alias_on_list_property)
TEST_F(QmlDocumentParser, qualified_list_property)
{
- auto exampleModuleId = storage.moduleId("Example");
+ auto exampleModuleId = storage.moduleId("Example", ModuleKind::QmlLibrary);
auto type = parser.parse(R"(import Example 2.1 as Example
Item{
property list<Example.Foo> foos
diff --git a/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp b/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp
index 64f3631a68..e75f7bf3a9 100644
--- a/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp
+++ b/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp
@@ -3,6 +3,8 @@
#include "../utils/googletest.h"
+#include <projectstorageerrornotifiermock.h>
+
#include <sqlitedatabase.h>
#include <projectstorage/projectstorage.h>
@@ -17,6 +19,7 @@ namespace Synchronization = QmlDesigner::Storage::Synchronization;
using QmlDesigner::ModuleId;
using QmlDesigner::SourceContextId;
using QmlDesigner::SourceId;
+using QmlDesigner::Storage::ModuleKind;
MATCHER_P3(IsImport,
moduleId,
@@ -168,20 +171,20 @@ class QmlTypesParser : public ::testing::Test
public:
protected:
Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
- QmlDesigner::ProjectStorage storage{database, database.isInitialized()};
+ ProjectStorageErrorNotifierMock errorNotifierMock;
+ QmlDesigner::ProjectStorage storage{database, errorNotifierMock, database.isInitialized()};
QmlDesigner::SourcePathCache<QmlDesigner::ProjectStorage> sourcePathCache{
storage};
QmlDesigner::QmlTypesParser parser{storage};
Storage::Imports imports;
Synchronization::Types types;
SourceId qmltypesFileSourceId{sourcePathCache.sourceId("path/to/types.qmltypes")};
- ModuleId qtQmlNativeModuleId = storage.moduleId("QtQml-cppnative");
- Synchronization::ProjectData projectData{qmltypesFileSourceId,
+ ModuleId qtQmlNativeModuleId = storage.moduleId("QtQml", ModuleKind::CppLibrary);
+ Synchronization::DirectoryInfo directoryInfo{qmltypesFileSourceId,
qmltypesFileSourceId,
qtQmlNativeModuleId,
Synchronization::FileType::QmlTypes};
SourceContextId qmltypesFileSourceContextId{sourcePathCache.sourceContextId(qmltypesFileSourceId)};
- ModuleId directoryModuleId{storage.moduleId("path/to/")};
};
TEST_F(QmlTypesParser, imports)
@@ -191,22 +194,22 @@ TEST_F(QmlTypesParser, imports)
dependencies:
["QtQuick 2.15", "QtQuick.Window 2.1", "QtFoo 6"]})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(imports,
- UnorderedElementsAre(IsImport(storage.moduleId("QML-cppnative"),
+ UnorderedElementsAre(IsImport(storage.moduleId("QML", ModuleKind::CppLibrary),
QmlDesigner::Storage::Version{},
qmltypesFileSourceId),
- IsImport(storage.moduleId("QtQml-cppnative"),
+ IsImport(storage.moduleId("QtQml", ModuleKind::CppLibrary),
QmlDesigner::Storage::Version{},
qmltypesFileSourceId),
- IsImport(storage.moduleId("QtQuick-cppnative"),
+ IsImport(storage.moduleId("QtQuick", ModuleKind::CppLibrary),
QmlDesigner::Storage::Version{},
qmltypesFileSourceId),
- IsImport(storage.moduleId("QtQuick.Window-cppnative"),
+ IsImport(storage.moduleId("QtQuick.Window", ModuleKind::CppLibrary),
QmlDesigner::Storage::Version{},
qmltypesFileSourceId),
- IsImport(storage.moduleId("QtFoo-cppnative"),
+ IsImport(storage.moduleId("QtFoo", ModuleKind::CppLibrary),
QmlDesigner::Storage::Version{},
qmltypesFileSourceId)));
}
@@ -218,7 +221,7 @@ TEST_F(QmlTypesParser, types)
Component { name: "QObject"}
Component { name: "QQmlComponent"}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types,
UnorderedElementsAre(IsType("QObject",
@@ -241,7 +244,7 @@ TEST_F(QmlTypesParser, prototype)
Component { name: "QQmlComponent"
prototype: "QObject"}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types,
UnorderedElementsAre(IsType("QObject",
@@ -264,7 +267,7 @@ TEST_F(QmlTypesParser, extension)
Component { name: "QQmlComponent"
extension: "QObject"}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types,
UnorderedElementsAre(IsType("QObject",
@@ -286,10 +289,10 @@ TEST_F(QmlTypesParser, exported_types)
Component { name: "QObject"
exports: ["QML/QtObject 1.0", "QtQml/QtObject 2.1"]
}})"};
- ModuleId qmlModuleId = storage.moduleId("QML");
- ModuleId qtQmlModuleId = storage.moduleId("QtQml");
+ ModuleId qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary);
+ ModuleId qtQmlModuleId = storage.moduleId("QtQml", ModuleKind::QmlLibrary);
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(
types,
@@ -312,7 +315,7 @@ TEST_F(QmlTypesParser, properties)
Property { name: "targets"; type: "QQuickItem"; isList: true; isReadonly: true; isPointer: true }
}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types,
ElementsAre(Field(
@@ -346,7 +349,7 @@ TEST_F(QmlTypesParser, properties_with_qualified_types)
Property { name: "values2"; type: "Qt::Vector" }
}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types,
Contains(
@@ -372,7 +375,7 @@ TEST_F(QmlTypesParser, properties_without_type)
Property { name: "target"; type: "QObject"; isPointer: true }
}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types,
ElementsAre(
@@ -405,7 +408,7 @@ TEST_F(QmlTypesParser, functions)
}
}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types,
ElementsAre(Field(
@@ -436,7 +439,7 @@ TEST_F(QmlTypesParser, skip_java_script_functions)
}
}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types, ElementsAre(Field(&Synchronization::Type::functionDeclarations, IsEmpty())));
}
@@ -456,7 +459,7 @@ TEST_F(QmlTypesParser, functions_with_qualified_types)
}
}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types,
Contains(
@@ -491,7 +494,7 @@ TEST_F(QmlTypesParser, signals)
}
}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types,
ElementsAre(Field(&Synchronization::Type::signalDeclarations,
@@ -524,7 +527,7 @@ TEST_F(QmlTypesParser, signals_with_qualified_types)
}
}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types,
Contains(
@@ -557,7 +560,7 @@ TEST_F(QmlTypesParser, enumerations)
}
}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types,
Contains(Field(
@@ -596,7 +599,7 @@ TEST_F(QmlTypesParser, enumeration_is_exported_as_type)
exports: ["QML/QtObject 1.0", "QtQml/QtObject 2.1"]
}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
QmlDesigner::Storage::TypeTraits traits{QmlDesigner::Storage::TypeTraitsKind::Value};
traits.isEnum = true;
@@ -642,7 +645,7 @@ TEST_F(QmlTypesParser, enumeration_is_exported_as_type_with_alias)
exports: ["QML/QtObject 1.0", "QtQml/QtObject 2.1"]
}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
QmlDesigner::Storage::TypeTraits traits{QmlDesigner::Storage::TypeTraitsKind::Value};
traits.isEnum = true;
@@ -690,7 +693,7 @@ TEST_F(QmlTypesParser, enumeration_is_exported_as_type_with_alias_too)
exports: ["QML/QtObject 1.0", "QtQml/QtObject 2.1"]
}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
QmlDesigner::Storage::TypeTraits traits{QmlDesigner::Storage::TypeTraitsKind::Value};
traits.isEnum = true;
@@ -728,7 +731,7 @@ TEST_F(QmlTypesParser, enumeration_is_referenced_by_qualified_name)
}
}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types,
Contains(Field(&Synchronization::Type::propertyDeclarations,
@@ -756,7 +759,7 @@ TEST_F(QmlTypesParser, alias_enumeration_is_referenced_by_qualified_name)
}
}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types,
Contains(Field(&Synchronization::Type::propertyDeclarations,
@@ -773,7 +776,7 @@ TEST_F(QmlTypesParser, access_type_is_reference)
Component { name: "QObject"
accessSemantics: "reference"}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraitsKind::Reference)));
}
@@ -785,7 +788,7 @@ TEST_F(QmlTypesParser, access_type_is_value)
Component { name: "QObject"
accessSemantics: "value"}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraitsKind::Value)));
}
@@ -797,7 +800,7 @@ TEST_F(QmlTypesParser, access_type_is_sequence)
Component { name: "QObject"
accessSemantics: "sequence"}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraitsKind::Sequence)));
}
@@ -809,7 +812,7 @@ TEST_F(QmlTypesParser, access_type_is_none)
Component { name: "QObject"
accessSemantics: "none"}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraitsKind::None)));
}
@@ -821,7 +824,7 @@ TEST_F(QmlTypesParser, uses_custom_parser)
Component { name: "QObject"
hasCustomParser: true }})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types, ElementsAre(IsTypeTrait(UsesCustomParser(true))));
}
@@ -833,7 +836,7 @@ TEST_F(QmlTypesParser, uses_no_custom_parser)
Component { name: "QObject"
hasCustomParser: false }})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types, ElementsAre(IsTypeTrait(UsesCustomParser(false))));
}
@@ -845,7 +848,7 @@ TEST_F(QmlTypesParser, default_property)
Component { name: "QObject"
defaultProperty: "children" }})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types,
ElementsAre(Field(&Synchronization::Type::defaultPropertyName, Eq("children"))));
@@ -853,8 +856,8 @@ TEST_F(QmlTypesParser, default_property)
TEST_F(QmlTypesParser, skip_template_item)
{
- ModuleId moduleId = storage.moduleId("QtQuick.Templates-cppnative");
- Synchronization::ProjectData projectData{qmltypesFileSourceId,
+ ModuleId moduleId = storage.moduleId("QtQuick.Templates", ModuleKind::CppLibrary);
+ Synchronization::DirectoryInfo directoryInfo{qmltypesFileSourceId,
qmltypesFileSourceId,
moduleId,
Synchronization::FileType::QmlTypes};
@@ -863,7 +866,7 @@ TEST_F(QmlTypesParser, skip_template_item)
Component { name: "QQuickItem"}
Component { name: "QQmlComponent"}})"};
- parser.parse(source, imports, types, projectData);
+ parser.parse(source, imports, types, directoryInfo);
ASSERT_THAT(types,
UnorderedElementsAre(IsType("QQmlComponent",
diff --git a/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp b/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp
index a614a5c7cf..d55c368f6e 100644
--- a/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp
+++ b/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp
@@ -4,6 +4,7 @@
#include "../utils/googletest.h"
#include <projectstorage-matcher.h>
+#include <projectstorageerrornotifiermock.h>
#include <strippedstring-matcher.h>
#include <projectstorage/projectstorage.h>
@@ -12,34 +13,47 @@
namespace {
+using QmlDesigner::FlagIs;
class TypeAnnotationReader : public testing::Test
{
protected:
- static void SetUpTestSuite()
+ TypeAnnotationReader()
{
- static_database = std::make_unique<Sqlite::Database>(":memory:", Sqlite::JournalMode::Memory);
-
- static_projectStorage = std::make_unique<QmlDesigner::ProjectStorage>(
- *static_database, static_database->isInitialized());
+ traits.canBeDroppedInFormEditor = FlagIs::True;
+ traits.canBeDroppedInNavigator = FlagIs::True;
+ traits.isMovable = FlagIs::True;
+ traits.isResizable = FlagIs::True;
+ traits.hasFormEditorItem = FlagIs::True;
+ traits.visibleInLibrary = FlagIs::True;
}
- static void TearDownTestSuite()
+ ~TypeAnnotationReader() { storage.resetForTestsOnly(); }
+
+ struct StaticData
{
- static_projectStorage.reset();
- static_database.reset();
- }
+ Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
+ ProjectStorageErrorNotifierMock errorNotifierMock;
+ QmlDesigner::ProjectStorage storage{database, errorNotifierMock, database.isInitialized()};
+ };
+
+ static void SetUpTestSuite() { staticData = std::make_unique<StaticData>(); }
- auto moduleId(Utils::SmallStringView name) const { return storage.moduleId(name); }
+ static void TearDownTestSuite() { staticData.reset(); }
+
+ auto moduleId(Utils::SmallStringView name) const
+ {
+ return storage.moduleId(name, QmlDesigner::Storage::ModuleKind::QmlLibrary);
+ }
protected:
- inline static std::unique_ptr<Sqlite::Database> static_database;
- Sqlite::Database &database = *static_database;
- inline static std::unique_ptr<QmlDesigner::ProjectStorage> static_projectStorage;
- QmlDesigner::ProjectStorage &storage = *static_projectStorage;
+ inline static std::unique_ptr<StaticData> staticData;
+ Sqlite::Database &database = staticData->database;
+ QmlDesigner::ProjectStorage &storage = staticData->storage;
QmlDesigner::Storage::TypeAnnotationReader reader{storage};
QmlDesigner::SourceId sourceId = QmlDesigner::SourceId::create(33);
QmlDesigner::SourceId directorySourceId = QmlDesigner::SourceId::create(77);
+ QmlDesigner::Storage::TypeTraits traits;
};
TEST_F(TypeAnnotationReader, parse_type)
@@ -55,7 +69,6 @@ TEST_F(TypeAnnotationReader, parse_type)
icon: "images/item-icon16.png"
}
})xy"};
- QmlDesigner::Storage::TypeTraits traits;
auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId);
@@ -92,7 +105,6 @@ TEST_F(TypeAnnotationReader, parse_true_canBeContainer)
}
}
})xy"};
- QmlDesigner::Storage::TypeTraits traits;
traits.canBeContainer = FlagIs::True;
auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId);
@@ -122,7 +134,6 @@ TEST_F(TypeAnnotationReader, parse_true_forceClip)
}
}
})xy"};
- QmlDesigner::Storage::TypeTraits traits;
traits.forceClip = FlagIs::True;
auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId);
@@ -152,7 +163,6 @@ TEST_F(TypeAnnotationReader, parse_true_doesLayoutChildren)
}
}
})xy"};
- QmlDesigner::Storage::TypeTraits traits;
traits.doesLayoutChildren = FlagIs::True;
auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId);
@@ -168,7 +178,7 @@ TEST_F(TypeAnnotationReader, parse_true_doesLayoutChildren)
IsEmpty())));
}
-TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInFormEditor)
+TEST_F(TypeAnnotationReader, parse_false_canBeDroppedInFormEditor)
{
using QmlDesigner::FlagIs;
auto content = QString{R"xy(
@@ -178,12 +188,11 @@ TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInFormEditor)
icon: "images/frame-icon16.png"
Hints {
- canBeDroppedInFormEditor: true
+ canBeDroppedInFormEditor: false
}
}
})xy"};
- QmlDesigner::Storage::TypeTraits traits;
- traits.canBeDroppedInFormEditor = FlagIs::True;
+ traits.canBeDroppedInFormEditor = FlagIs::False;
auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId);
@@ -198,7 +207,7 @@ TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInFormEditor)
IsEmpty())));
}
-TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInNavigator)
+TEST_F(TypeAnnotationReader, parse_false_canBeDroppedInNavigator)
{
using QmlDesigner::FlagIs;
auto content = QString{R"xy(
@@ -208,12 +217,11 @@ TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInNavigator)
icon: "images/frame-icon16.png"
Hints {
- canBeDroppedInNavigator: true
+ canBeDroppedInNavigator: false
}
}
})xy"};
- QmlDesigner::Storage::TypeTraits traits;
- traits.canBeDroppedInNavigator = FlagIs::True;
+ traits.canBeDroppedInNavigator = FlagIs::False;
auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId);
@@ -242,7 +250,6 @@ TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInView3D)
}
}
})xy"};
- QmlDesigner::Storage::TypeTraits traits;
traits.canBeDroppedInView3D = FlagIs::True;
auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId);
@@ -258,7 +265,7 @@ TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInView3D)
IsEmpty())));
}
-TEST_F(TypeAnnotationReader, parse_true_isMovable)
+TEST_F(TypeAnnotationReader, parse_false_isMovable)
{
using QmlDesigner::FlagIs;
auto content = QString{R"xy(
@@ -268,12 +275,11 @@ TEST_F(TypeAnnotationReader, parse_true_isMovable)
icon: "images/frame-icon16.png"
Hints {
- isMovable: true
+ isMovable: false
}
}
})xy"};
- QmlDesigner::Storage::TypeTraits traits;
- traits.isMovable = FlagIs::True;
+ traits.isMovable = FlagIs::False;
auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId);
@@ -288,7 +294,7 @@ TEST_F(TypeAnnotationReader, parse_true_isMovable)
IsEmpty())));
}
-TEST_F(TypeAnnotationReader, parse_true_isResizable)
+TEST_F(TypeAnnotationReader, parse_false_isResizable)
{
using QmlDesigner::FlagIs;
auto content = QString{R"xy(
@@ -298,12 +304,11 @@ TEST_F(TypeAnnotationReader, parse_true_isResizable)
icon: "images/frame-icon16.png"
Hints {
- isResizable: true
+ isResizable: false
}
}
})xy"};
- QmlDesigner::Storage::TypeTraits traits;
- traits.isResizable = FlagIs::True;
+ traits.isResizable = FlagIs::False;
auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId);
@@ -318,7 +323,7 @@ TEST_F(TypeAnnotationReader, parse_true_isResizable)
IsEmpty())));
}
-TEST_F(TypeAnnotationReader, parse_true_hasFormEditorItem)
+TEST_F(TypeAnnotationReader, parse_false_hasFormEditorItem)
{
using QmlDesigner::FlagIs;
auto content = QString{R"xy(
@@ -328,12 +333,11 @@ TEST_F(TypeAnnotationReader, parse_true_hasFormEditorItem)
icon: "images/frame-icon16.png"
Hints {
- hasFormEditorItem: true
+ hasFormEditorItem: false
}
}
})xy"};
- QmlDesigner::Storage::TypeTraits traits;
- traits.hasFormEditorItem = FlagIs::True;
+ traits.hasFormEditorItem = FlagIs::False;
auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId);
@@ -362,7 +366,6 @@ TEST_F(TypeAnnotationReader, parse_true_isStackedContainer)
}
}
})xy"};
- QmlDesigner::Storage::TypeTraits traits;
traits.isStackedContainer = FlagIs::True;
auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId);
@@ -392,7 +395,6 @@ TEST_F(TypeAnnotationReader, parse_true_takesOverRenderingOfChildren)
}
}
})xy"};
- QmlDesigner::Storage::TypeTraits traits;
traits.takesOverRenderingOfChildren = FlagIs::True;
auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId);
@@ -422,7 +424,6 @@ TEST_F(TypeAnnotationReader, parse_true_visibleInNavigator)
}
}
})xy"};
- QmlDesigner::Storage::TypeTraits traits;
traits.visibleInNavigator = FlagIs::True;
auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId);
@@ -438,7 +439,7 @@ TEST_F(TypeAnnotationReader, parse_true_visibleInNavigator)
IsEmpty())));
}
-TEST_F(TypeAnnotationReader, parse_true_visibleInLibrary)
+TEST_F(TypeAnnotationReader, parse_false_visibleInLibrary)
{
using QmlDesigner::FlagIs;
auto content = QString{R"xy(
@@ -448,12 +449,11 @@ TEST_F(TypeAnnotationReader, parse_true_visibleInLibrary)
icon: "images/frame-icon16.png"
Hints {
- visibleInLibrary: true
+ visibleInLibrary: false
}
}
})xy"};
- QmlDesigner::Storage::TypeTraits traits;
- traits.visibleInLibrary = FlagIs::True;
+ traits.visibleInLibrary = FlagIs::False;
auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId);
@@ -478,11 +478,10 @@ TEST_F(TypeAnnotationReader, parse_false)
icon: "images/frame-icon16.png"
Hints {
- isMovable: false
+ isMovable: true
}
}
})xy"};
- QmlDesigner::Storage::TypeTraits traits;
auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId);
@@ -521,9 +520,9 @@ TEST_F(TypeAnnotationReader, parse_complex_expression)
}
}
})xy"};
- QmlDesigner::Storage::TypeTraits frameTraits;
+ QmlDesigner::Storage::TypeTraits frameTraits = traits;
frameTraits.isMovable = QmlDesigner::FlagIs::Set;
- QmlDesigner::Storage::TypeTraits itemTraits;
+ QmlDesigner::Storage::TypeTraits itemTraits = traits;
itemTraits.canBeContainer = QmlDesigner::FlagIs::True;
auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId);
@@ -573,7 +572,6 @@ TEST_F(TypeAnnotationReader, parse_item_library_entry)
}
}
})xy"};
- QmlDesigner::Storage::TypeTraits traits;
auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId);
@@ -630,7 +628,6 @@ TEST_F(TypeAnnotationReader, parse_item_library_entry_with_properties)
}
}
})xy"};
- QmlDesigner::Storage::TypeTraits traits;
auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId);
@@ -681,7 +678,6 @@ TEST_F(TypeAnnotationReader, parse_item_library_entry_template_path)
}
}
})xy"};
- QmlDesigner::Storage::TypeTraits traits;
auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId);
@@ -734,7 +730,6 @@ TEST_F(TypeAnnotationReader, parse_item_library_entry_extra_file_paths)
}
}
})xy"};
- QmlDesigner::Storage::TypeTraits traits;
auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId);
diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson
index 815e9a0cc5..e362cdf886 100644
--- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson
+++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson
@@ -75,7 +75,12 @@
"*.png",
"*.svg",
"*.hdr",
- "*.ktx"
+ "*.ktx",
+ "*.bmp",
+ "*.ttf",
+ "*.tiff",
+ "*.webp",
+ "*.gif"
],
"mcuProperties": {
},
@@ -93,7 +98,12 @@
"*.png",
"*.svg",
"*.hdr",
- "*.ktx"
+ "*.ktx",
+ "*.bmp",
+ "*.ttf",
+ "*.tiff",
+ "*.webp",
+ "*.gif"
],
"mcuProperties": {
},
diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson
index 2231f36ff2..80eaf6fefa 100644
--- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson
+++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson
@@ -45,7 +45,12 @@
"*.png",
"*.svg",
"*.hdr",
- "*.ktx"
+ "*.ktx",
+ "*.bmp",
+ "*.ttf",
+ "*.tiff",
+ "*.webp",
+ "*.gif"
],
"mcuProperties": {
},
diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson
index 815e9a0cc5..e362cdf886 100644
--- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson
+++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson
@@ -75,7 +75,12 @@
"*.png",
"*.svg",
"*.hdr",
- "*.ktx"
+ "*.ktx",
+ "*.bmp",
+ "*.ttf",
+ "*.tiff",
+ "*.webp",
+ "*.gif"
],
"mcuProperties": {
},
@@ -93,7 +98,12 @@
"*.png",
"*.svg",
"*.hdr",
- "*.ktx"
+ "*.ktx",
+ "*.bmp",
+ "*.ttf",
+ "*.tiff",
+ "*.webp",
+ "*.gif"
],
"mcuProperties": {
},
diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmltojson
index 303bfc3899..634623c9cf 100644
--- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmltojson
+++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmltojson
@@ -48,7 +48,12 @@
"*.png",
"*.svg",
"*.hdr",
- "*.ktx"
+ "*.ktx",
+ "*.bmp",
+ "*.ttf",
+ "*.tiff",
+ "*.webp",
+ "*.gif"
],
"mcuProperties": {
},
diff --git a/tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp b/tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp
index f533c651e9..24b20176ea 100644
--- a/tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp
+++ b/tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp
@@ -269,6 +269,21 @@ TEST_F(SqliteStatement, bind_int_id)
ASSERT_THAT(readStatement.fetchIntValue(0), 42);
}
+TEST_F(SqliteStatement, bind_special_state_id)
+{
+ enum class SpecialIdState { Unresolved = -1 };
+ constexpr TestIntId unresolvedTypeId = TestIntId::createSpecialState(SpecialIdState::Unresolved);
+ SqliteTestStatement<0, 1> statement("INSERT INTO test VALUES ('id', 323, ?)", database);
+
+ statement.bind(1, unresolvedTypeId);
+ statement.next();
+
+ SqliteTestStatement<1, 1> readStatement("SELECT value FROM test WHERE name='id'", database);
+ readStatement.next();
+ ASSERT_THAT(readStatement.fetchType(0), Sqlite::Type::Integer);
+ ASSERT_THAT(readStatement.fetchIntValue(0), -1);
+}
+
TEST_F(SqliteStatement, bind_invalid_long_long_id_to_null)
{
TestLongLongId id;