diff --git a/doc/images/qmldesigner-new-project.png b/doc/images/qmldesigner-new-project.png index bd6679aa08a..a5c34a42790 100644 Binary files a/doc/images/qmldesigner-new-project.png and b/doc/images/qmldesigner-new-project.png differ diff --git a/doc/src/howto/creator-only/creator-external-tools.qdoc b/doc/src/howto/creator-only/creator-external-tools.qdoc index a5038a65918..546efaf72a3 100644 --- a/doc/src/howto/creator-only/creator-external-tools.qdoc +++ b/doc/src/howto/creator-only/creator-external-tools.qdoc @@ -52,7 +52,11 @@ code and translations. The lrelease tool is used to create run-time translation files for use by the released application. - To synchronize translation source (TS) files from a translator with the + When you \l{Creating Projects}{create a new project}, you can automatically + generate a translation source file (TS) for one language. You can add other + languages later by editing the project file. + + To synchronize TS files from a translator with the application code, select \uicontrol Tools > \uicontrol External > \uicontrol Linguist > \uicontrol {Update Translations (lupdate)}. diff --git a/doc/src/overview/creator-only/creator-mobile-targets.qdoc b/doc/src/overview/creator-only/creator-mobile-targets.qdoc index 4c5ff795a3a..37822c534fd 100644 --- a/doc/src/overview/creator-only/creator-mobile-targets.qdoc +++ b/doc/src/overview/creator-only/creator-mobile-targets.qdoc @@ -104,8 +104,8 @@ applications in WebAssembly format, to deploy them, and to run them in a web browser. - \li \l{http://doc.qt.io/qtcreator/creator-overview-qtasam.html} - {Qt Application Manager} + \li \l{https://doc.qt.io/qtcreator/creator-overview-qtasam.html} + {Qt Creator Plugin for Qt Application Manager} You can use the experimental Qt Application Manager plugin (commercial only) to deploy, run, and debug applications on the diff --git a/doc/src/qtquick/creator-only/qtquick-creating.qdoc b/doc/src/qtquick/creator-only/qtquick-creating.qdoc index 560d3a7671c..d37992ed2ee 100644 --- a/doc/src/qtquick/creator-only/qtquick-creating.qdoc +++ b/doc/src/qtquick/creator-only/qtquick-creating.qdoc @@ -128,6 +128,16 @@ \li Select \uicontrol Next. + \li In the \uicontrol Language field, select a language that you plan + to \l {Using Qt Linguist}{translate} the application to. You can + add other languages later by editing the project file. + + \li In the \uicontrol {Translation file} field, you can edit the + name for the translation source file that will be generated + for the selected language. + + \li Select \uicontrol Next. + \li Select \l{glossary-buildandrun-kit}{kits} for running and building your project, and then click \uicontrol Next. diff --git a/doc/src/qtquick/qtquick-properties.qdoc b/doc/src/qtquick/qtquick-properties.qdoc index 4218edb04af..add17138c4f 100644 --- a/doc/src/qtquick/qtquick-properties.qdoc +++ b/doc/src/qtquick/qtquick-properties.qdoc @@ -181,6 +181,12 @@ For more information, see \l {Internationalization and Localization with Qt Quick}. + \if defined(qtcreator) + When you \l{Creating Qt Quick Projects}{create a new project}, you can + automatically generate a translation source file (TS) for one language. + You can add other languages later by editing the project file. + \endif + \section1 Loading Placeholder Data The Design mode supports views, models, and delegates, so that when you add diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index db1a5b5e2aa..d1eae35d24a 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -52,7 +52,6 @@ add_subdirectory(genericprojectmanager) add_subdirectory(git) add_subdirectory(mercurial) add_subdirectory(perforce) -add_subdirectory(python) add_subdirectory(qmakeprojectmanager) add_subdirectory(qmljstools) add_subdirectory(qmlprojectmanager) @@ -70,6 +69,7 @@ add_subdirectory(baremetal) add_subdirectory(clangcodemodel) add_subdirectory(clangtools) add_subdirectory(ios) +add_subdirectory(python) add_subdirectory(qmljseditor) add_subdirectory(qmlpreview) add_subdirectory(qmlprofiler) diff --git a/src/plugins/debugger/debuggercore.h b/src/plugins/debugger/debuggercore.h index 60bd22f3731..af0de1c25b9 100644 --- a/src/plugins/debugger/debuggercore.h +++ b/src/plugins/debugger/debuggercore.h @@ -36,7 +36,6 @@ #include QT_BEGIN_NAMESPACE -class QMessageBox; class QWidget; class QMenu; class QAction; @@ -50,9 +49,6 @@ class SavedAction; namespace Debugger { namespace Internal { -class Console; -class Symbol; -class Section; class GlobalDebuggerOptions; enum TestCases @@ -64,23 +60,16 @@ enum TestCases // Some convenience. void openTextEditor(const QString &titlePattern, const QString &contents); -void showModuleSymbols(const QString &moduleName, const QVector &symbols); -void showModuleSections(const QString &moduleName, const QVector §ions); - QSharedPointer globalDebuggerOptions(); bool isTestRun(); Utils::SavedAction *action(int code); -Console *console(); bool boolSetting(int code); QString stringSetting(int code); QStringList stringListSetting(int code); -QMessageBox *showMessageBox(int icon, const QString &title, - const QString &text, int buttons = 0); - QAction *addAction(QMenu *menu, const QString &display, bool on, const std::function &onTriggered = {}); QAction *addAction(QMenu *menu, const QString &d1, const QString &d2, bool on, diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 0dd02e1b63f..9d59cc0fe07 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -2619,6 +2619,73 @@ QString DebuggerEngine::formatStartParameters() const return rc; } +static void createNewDock(QWidget *widget) +{ + auto dockWidget = new QDockWidget; + dockWidget->setWidget(widget); + dockWidget->setWindowTitle(widget->windowTitle()); + dockWidget->setFeatures(QDockWidget::DockWidgetClosable); + dockWidget->show(); +} + +void DebuggerEngine::showModuleSymbols(const QString &moduleName, const Symbols &symbols) +{ + auto w = new QTreeWidget; + w->setUniformRowHeights(true); + w->setColumnCount(5); + w->setRootIsDecorated(false); + w->setAlternatingRowColors(true); + w->setSortingEnabled(true); + w->setObjectName("Symbols." + moduleName); + QStringList header; + header.append(tr("Symbol")); + header.append(tr("Address")); + header.append(tr("Code")); + header.append(tr("Section")); + header.append(tr("Name")); + w->setHeaderLabels(header); + w->setWindowTitle(tr("Symbols in \"%1\"").arg(moduleName)); + for (const Symbol &s : symbols) { + auto it = new QTreeWidgetItem; + it->setData(0, Qt::DisplayRole, s.name); + it->setData(1, Qt::DisplayRole, s.address); + it->setData(2, Qt::DisplayRole, s.state); + it->setData(3, Qt::DisplayRole, s.section); + it->setData(4, Qt::DisplayRole, s.demangled); + w->addTopLevelItem(it); + } + createNewDock(w); +} + +void DebuggerEngine::showModuleSections(const QString &moduleName, const Sections §ions) +{ + auto w = new QTreeWidget; + w->setUniformRowHeights(true); + w->setColumnCount(5); + w->setRootIsDecorated(false); + w->setAlternatingRowColors(true); + w->setSortingEnabled(true); + w->setObjectName("Sections." + moduleName); + QStringList header; + header.append(tr("Name")); + header.append(tr("From")); + header.append(tr("To")); + header.append(tr("Address")); + header.append(tr("Flags")); + w->setHeaderLabels(header); + w->setWindowTitle(tr("Sections in \"%1\"").arg(moduleName)); + for (const Section &s : sections) { + auto it = new QTreeWidgetItem; + it->setData(0, Qt::DisplayRole, s.name); + it->setData(1, Qt::DisplayRole, s.from); + it->setData(2, Qt::DisplayRole, s.to); + it->setData(3, Qt::DisplayRole, s.address); + it->setData(4, Qt::DisplayRole, s.flags); + w->addTopLevelItem(it); + } + createNewDock(w); +} + // CppDebuggerEngine Context CppDebuggerEngine::languageContext() const diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index f4ba9e47cb0..32c8e7481b5 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -102,9 +102,11 @@ class LogWindow; class ModulesHandler; class RegisterHandler; class PeripheralRegisterHandler; -class StackHandler; -class StackFrame; +class Section; class SourceFilesHandler; +class StackFrame; +class StackHandler; +class Symbol; class WatchHandler; class WatchTreeView; class DebuggerToolTipContext; @@ -460,6 +462,9 @@ public: void openMemoryEditor(); + static void showModuleSymbols(const QString &moduleName, const QVector &symbols); + static void showModuleSections(const QString &moduleName, const QVector
§ions); + void handleExecDetach(); void handleExecContinue(); void handleExecInterrupt(); @@ -546,7 +551,6 @@ protected: bool isNativeMixedActiveFrame() const; void startDying() const; -protected: ProjectExplorer::IDevice::ConstPtr device() const; DebuggerEngine *companionEngine() const; diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 0bf371835d3..50ac0504a7d 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -2015,15 +2015,6 @@ void DebuggerPluginPrivate::aboutToShutdown() m_shutdownTimer.start(); } -static void createNewDock(QWidget *widget) -{ - auto dockWidget = new QDockWidget; - dockWidget->setWidget(widget); - dockWidget->setWindowTitle(widget->windowTitle()); - dockWidget->setFeatures(QDockWidget::DockWidgetClosable); - dockWidget->show(); -} - void DebuggerPluginPrivate::remoteCommand(const QStringList &options) { if (options.isEmpty()) @@ -2038,17 +2029,6 @@ void DebuggerPluginPrivate::remoteCommand(const QStringList &options) runScheduled(); } -QMessageBox *showMessageBox(int icon, const QString &title, const QString &text, int buttons) -{ - QMessageBox *mb = new QMessageBox(QMessageBox::Icon(icon), - title, text, QMessageBox::StandardButtons(buttons), - ICore::mainWindow()); - mb->setAttribute(Qt::WA_DeleteOnClose); - mb->setTextInteractionFlags(Qt::TextSelectableByMouse); - mb->show(); - return mb; -} - void addDebugInfoTask(unsigned id, const QString &cmd) { dd->m_debugInfoTaskHandler.addTask(id, cmd); @@ -2117,64 +2097,6 @@ QStringList stringListSetting(int code) return action(code)->value().toStringList(); } -void showModuleSymbols(const QString &moduleName, const Symbols &symbols) -{ - auto w = new QTreeWidget; - w->setUniformRowHeights(true); - w->setColumnCount(5); - w->setRootIsDecorated(false); - w->setAlternatingRowColors(true); - w->setSortingEnabled(true); - w->setObjectName("Symbols." + moduleName); - QStringList header; - header.append(DebuggerPlugin::tr("Symbol")); - header.append(DebuggerPlugin::tr("Address")); - header.append(DebuggerPlugin::tr("Code")); - header.append(DebuggerPlugin::tr("Section")); - header.append(DebuggerPlugin::tr("Name")); - w->setHeaderLabels(header); - w->setWindowTitle(DebuggerPlugin::tr("Symbols in \"%1\"").arg(moduleName)); - for (const Symbol &s : symbols) { - auto it = new QTreeWidgetItem; - it->setData(0, Qt::DisplayRole, s.name); - it->setData(1, Qt::DisplayRole, s.address); - it->setData(2, Qt::DisplayRole, s.state); - it->setData(3, Qt::DisplayRole, s.section); - it->setData(4, Qt::DisplayRole, s.demangled); - w->addTopLevelItem(it); - } - createNewDock(w); -} - -void showModuleSections(const QString &moduleName, const Sections §ions) -{ - auto w = new QTreeWidget; - w->setUniformRowHeights(true); - w->setColumnCount(5); - w->setRootIsDecorated(false); - w->setAlternatingRowColors(true); - w->setSortingEnabled(true); - w->setObjectName("Sections." + moduleName); - QStringList header; - header.append(DebuggerPlugin::tr("Name")); - header.append(DebuggerPlugin::tr("From")); - header.append(DebuggerPlugin::tr("To")); - header.append(DebuggerPlugin::tr("Address")); - header.append(DebuggerPlugin::tr("Flags")); - w->setHeaderLabels(header); - w->setWindowTitle(DebuggerPlugin::tr("Sections in \"%1\"").arg(moduleName)); - for (const Section &s : sections) { - auto it = new QTreeWidgetItem; - it->setData(0, Qt::DisplayRole, s.name); - it->setData(1, Qt::DisplayRole, s.from); - it->setData(2, Qt::DisplayRole, s.to); - it->setData(3, Qt::DisplayRole, s.address); - it->setData(4, Qt::DisplayRole, s.flags); - w->addTopLevelItem(it); - } - createNewDock(w); -} - void openTextEditor(const QString &titlePattern0, const QString &contents) { if (dd->m_shuttingDown) diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 5a40aa2ee0c..ff0197c2250 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -103,6 +103,17 @@ static bool isMostlyHarmlessMessage(const QStringRef &msg) "Invalid argument\\n"; } +static QMessageBox *showMessageBox(QMessageBox::Icon icon, + const QString &title, const QString &text, + QMessageBox::StandardButtons buttons) +{ + auto mb = new QMessageBox(icon, title, text, buttons, ICore::mainWindow()); + mb->setAttribute(Qt::WA_DeleteOnClose); + mb->setTextInteractionFlags(Qt::TextSelectableByMouse); + mb->show(); + return mb; +} + /////////////////////////////////////////////////////////////////////// // // GdbEngine @@ -2651,7 +2662,7 @@ static void handleShowModuleSymbols(const DebuggerResponse &response, } file.close(); file.remove(); - Internal::showModuleSymbols(modulePath, symbols); + DebuggerEngine::showModuleSymbols(modulePath, symbols); } else { AsynchronousMessageBox::critical(GdbEngine::tr("Cannot Read Symbols"), GdbEngine::tr("Cannot read symbols for module \"%1\".").arg(fileName)); @@ -2716,7 +2727,7 @@ void GdbEngine::handleShowModuleSections(const DebuggerResponse &response, } } if (!sections.isEmpty()) - Internal::showModuleSections(moduleName, sections); + DebuggerEngine::showModuleSections(moduleName, sections); } } diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index d60b0951e7a..f75c491d5de 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -669,7 +669,7 @@ void LldbEngine::requestModuleSymbols(const QString &moduleName) { DebuggerCommand cmd("fetchSymbols"); cmd.arg("module", moduleName); - cmd.callback = [moduleName](const DebuggerResponse &response) { + cmd.callback = [this, moduleName](const DebuggerResponse &response) { const GdbMi &symbols = response.data["symbols"]; QString moduleName = response.data["module"].data(); Symbols syms; @@ -682,7 +682,7 @@ void LldbEngine::requestModuleSymbols(const QString &moduleName) symbol.demangled = item["demangled"].data(); syms.append(symbol); } - Internal::showModuleSymbols(moduleName, syms); + showModuleSymbols(moduleName, syms); }; runCommand(cmd); } diff --git a/src/plugins/debugger/pdb/pdbengine.cpp b/src/plugins/debugger/pdb/pdbengine.cpp index defd54050be..8d86407a4db 100644 --- a/src/plugins/debugger/pdb/pdbengine.cpp +++ b/src/plugins/debugger/pdb/pdbengine.cpp @@ -369,7 +369,7 @@ void PdbEngine::refreshSymbols(const GdbMi &symbols) symbol.name = item["name"].data(); syms.append(symbol); } - Internal::showModuleSymbols(moduleName, syms); + showModuleSymbols(moduleName, syms); } bool PdbEngine::canHandleToolTip(const DebuggerToolTipContext &) const diff --git a/src/plugins/debugger/sourceutils.cpp b/src/plugins/debugger/sourceutils.cpp index a08221c4e14..ea50eca8bd6 100644 --- a/src/plugins/debugger/sourceutils.cpp +++ b/src/plugins/debugger/sourceutils.cpp @@ -161,7 +161,7 @@ static void blockRecursion(const Overview &overview, // Go backwards in case someone has identical variables in the same scope. // Fixme: loop variables or similar are currently seen in the outer scope for (int s = scope->memberCount() - 1; s >= 0; --s){ - const Symbol *symbol = scope->memberAt(s); + const CPlusPlus::Symbol *symbol = scope->memberAt(s); if (symbol->isDeclaration()) { // Find out about shadowed symbols by bookkeeping // the already seen occurrences in a hash. @@ -197,7 +197,7 @@ QStringList getUninitializedVariables(const Snapshot &snapshot, const Document::Ptr doc = docIt.value(); // Look at symbol at line and find its function. Either it is the // function itself or some expression/variable. - const Symbol *symbolAtLine = doc->lastVisibleSymbolAt(line, 0); + const CPlusPlus::Symbol *symbolAtLine = doc->lastVisibleSymbolAt(line, 0); if (!symbolAtLine) return result; // First figure out the function to do a safety name check diff --git a/src/plugins/help/qlitehtml/CMakeLists.txt b/src/plugins/help/qlitehtml/CMakeLists.txt index c9bcd3326dc..b841a6bc7a9 100644 --- a/src/plugins/help/qlitehtml/CMakeLists.txt +++ b/src/plugins/help/qlitehtml/CMakeLists.txt @@ -4,19 +4,17 @@ project(QLiteHtml) if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/litehtml/CMakeLists.txt) set(ORIG_FPIC ${CMAKE_POSITION_INDEPENDENT_CODE}) - set(ORIG_CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE}) - if (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") - set(CMAKE_BUILD_TYPE "RelWithDebInfo") - else() - set(CMAKE_BUILD_TYPE "Release") - endif() if (WIN32) set(LITEHTML_UTF8 ON) endif() set(CMAKE_POSITION_INDEPENDENT_CODE ON) add_subdirectory(litehtml) - set(CMAKE_BUILD_TYPE ${ORIG_CMAKE_BUILD_TYPE}) set(CMAKE_POSITION_INDEPENDENT_CODE "${ORIG_FPIC}") + # force optimized litehtml even in debug + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_options(gumbo PRIVATE -O2) + target_compile_options(litehtml PRIVATE -O2) + endif() else() find_package(litehtml REQUIRED) endif() diff --git a/src/plugins/help/qlitehtml/container_qpainter.cpp b/src/plugins/help/qlitehtml/container_qpainter.cpp index 341c8b712f5..e3751f517c0 100644 --- a/src/plugins/help/qlitehtml/container_qpainter.cpp +++ b/src/plugins/help/qlitehtml/container_qpainter.cpp @@ -652,11 +652,9 @@ void DocumentContainer::buildIndex() void DocumentContainer::draw_background(litehtml::uint_ptr hdc, const litehtml::background_paint &bg) { - // TODO auto painter = toQPainter(hdc); if (bg.is_root) { // TODO ? - drawSelection(painter, toQRect(bg.border_box)); return; } painter->save(); @@ -935,6 +933,14 @@ void DocumentContainer::render(int width, int height) m_selection.update(); } +void DocumentContainer::draw(QPainter *painter, const QRect &clip) +{ + drawSelection(painter, clip); + const QPoint pos = -m_scrollPosition; + const litehtml::position clipRect = {clip.x(), clip.y(), clip.width(), clip.height()}; + document()->draw(reinterpret_cast(painter), pos.x(), pos.y(), &clipRect); +} + QVector DocumentContainer::mousePressEvent(const QPoint &documentPos, const QPoint &viewportPos, Qt::MouseButton button) diff --git a/src/plugins/help/qlitehtml/container_qpainter.h b/src/plugins/help/qlitehtml/container_qpainter.h index ab70d0561e9..018ea60a37b 100644 --- a/src/plugins/help/qlitehtml/container_qpainter.h +++ b/src/plugins/help/qlitehtml/container_qpainter.h @@ -143,6 +143,7 @@ public: // outside API litehtml::document::ptr document() const; void setScrollPosition(const QPoint &pos); void render(int width, int height); + void draw(QPainter *painter, const QRect &clip); // these return areas to redraw in document space QVector mousePressEvent(const QPoint &documentPos, diff --git a/src/plugins/help/qlitehtml/qlitehtmlwidget.cpp b/src/plugins/help/qlitehtml/qlitehtmlwidget.cpp index 15174036f70..6692d56ffd8 100644 --- a/src/plugins/help/qlitehtml/qlitehtmlwidget.cpp +++ b/src/plugins/help/qlitehtml/qlitehtmlwidget.cpp @@ -523,17 +523,11 @@ void QLiteHtmlWidget::paintEvent(QPaintEvent *event) if (!d->documentContainer.document()) return; d->documentContainer.setScrollPosition(scrollPosition()); - const QPoint pos = -scrollPosition(); - const QRect r = toVirtual(event->rect()); - const litehtml::position clip = {r.x(), r.y(), r.width(), r.height()}; QPainter p(viewport()); p.setWorldTransform(QTransform().scale(d->zoomFactor, d->zoomFactor)); p.setRenderHint(QPainter::SmoothPixmapTransform, true); p.setRenderHint(QPainter::Antialiasing, true); - d->documentContainer.document()->draw(reinterpret_cast(&p), - pos.x(), - pos.y(), - &clip); + d->documentContainer.draw(&p, toVirtual(event->rect())); } static litehtml::element::ptr elementForY(int y, const litehtml::document::ptr &document) diff --git a/src/plugins/languageclient/languageclientutils.cpp b/src/plugins/languageclient/languageclientutils.cpp index cdb114b2f12..dd0668bb8ea 100644 --- a/src/plugins/languageclient/languageclientutils.cpp +++ b/src/plugins/languageclient/languageclientutils.cpp @@ -218,18 +218,29 @@ void updateEditorToolBar(Core::IEditor *editor) const QIcon icon = Utils::Icon({{":/languageclient/images/languageclient.png", Utils::Theme::IconsBaseColor}}) .icon(); - actions[widget] = widget->toolBar()->addAction(icon, client->name(), [document]() { - auto menu = new QMenu; - for (auto client : LanguageClientManager::clientsSupportingDocument(document)) - menu->addAction(client->name(), [client = QPointer(client), document]() { - if (client) + actions[widget] = widget->toolBar()->addAction( + icon, client->name(), [document]() { + auto menu = new QMenu; + auto *clientsGroup = new QActionGroup(menu); + clientsGroup->setExclusive(true); + for (auto client : LanguageClientManager::clientsSupportingDocument(document)) { + auto action = clientsGroup->addAction(client->name()); + auto reopen = [action, client = QPointer(client), document]() { + if (!client) + return; LanguageClientManager::reOpenDocumentWithClient(document, client); + action->setChecked(true); + }; + action->setCheckable(true); + action->setChecked(client == LanguageClientManager::clientForDocument(document)); + QObject::connect(action, &QAction::triggered, reopen); + } + menu->addActions(clientsGroup->actions()); + menu->addAction("Manage...", []() { + Core::ICore::showOptionsDialog(Constants::LANGUAGECLIENT_SETTINGS_PAGE); }); - menu->addAction("Manage...", []() { - Core::ICore::showOptionsDialog(Constants::LANGUAGECLIENT_SETTINGS_PAGE); + menu->popup(QCursor::pos()); }); - menu->popup(QCursor::pos()); - }); QObject::connect(widget, &QWidget::destroyed, [widget]() { actions.remove(widget); }); diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index df6a1286fea..f81a3860a10 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -996,7 +996,7 @@ QList GccToolChainFactory::autoDetect(const QList &alr QList GccToolChainFactory::detectForImport(const ToolChainDescription &tcd) { - const QString fileName = tcd.compilerPath.toString(); + const QString fileName = tcd.compilerPath.toFileInfo().completeBaseName(); if ((tcd.language == Constants::C_LANGUAGE_ID && (fileName.startsWith("gcc") || fileName.endsWith("gcc"))) || (tcd.language == Constants::CXX_LANGUAGE_ID && (fileName.startsWith("g++") @@ -1764,7 +1764,7 @@ QList MingwToolChainFactory::autoDetect(const QList &a QList MingwToolChainFactory::detectForImport(const ToolChainDescription &tcd) { - const QString fileName = tcd.compilerPath.toString(); + const QString fileName = tcd.compilerPath.toFileInfo().completeBaseName(); if ((tcd.language == Constants::C_LANGUAGE_ID && (fileName.startsWith("gcc") || fileName.endsWith("gcc"))) || (tcd.language == Constants::CXX_LANGUAGE_ID && (fileName.startsWith("g++") diff --git a/src/plugins/python/CMakeLists.txt b/src/plugins/python/CMakeLists.txt index 7ecc160f3a9..27f45669f7f 100644 --- a/src/plugins/python/CMakeLists.txt +++ b/src/plugins/python/CMakeLists.txt @@ -1,5 +1,5 @@ add_qtc_plugin(Python - PLUGIN_DEPENDS Core ProjectExplorer TextEditor + PLUGIN_DEPENDS Core LanguageClient ProjectExplorer TextEditor SOURCES python.qrc pythoneditor.cpp pythoneditor.h diff --git a/src/plugins/python/python.qbs b/src/plugins/python/python.qbs index c90f76d5d1d..552186f8c99 100644 --- a/src/plugins/python/python.qbs +++ b/src/plugins/python/python.qbs @@ -9,6 +9,7 @@ QtcPlugin { Depends { name: "Core" } Depends { name: "TextEditor" } Depends { name: "ProjectExplorer" } + Depends { name: "LanguageClient" } Group { name: "General" diff --git a/src/plugins/python/python_dependencies.pri b/src/plugins/python/python_dependencies.pri index 5f2a87c2830..a62bb0e8af8 100644 --- a/src/plugins/python/python_dependencies.pri +++ b/src/plugins/python/python_dependencies.pri @@ -4,5 +4,6 @@ QTC_LIB_DEPENDS += \ utils QTC_PLUGIN_DEPENDS += \ coreplugin \ - texteditor \ - projectexplorer + languageclient \ + projectexplorer \ + texteditor diff --git a/src/plugins/python/pythoneditor.cpp b/src/plugins/python/pythoneditor.cpp index 145e88e1371..434ec9e1cc1 100644 --- a/src/plugins/python/pythoneditor.cpp +++ b/src/plugins/python/pythoneditor.cpp @@ -25,40 +25,241 @@ #include "pythoneditor.h" #include "pythonconstants.h" -#include "pythonplugin.h" -#include "pythonindenter.h" #include "pythonhighlighter.h" +#include "pythonindenter.h" +#include "pythonplugin.h" +#include "pythonproject.h" +#include "pythonrunconfiguration.h" +#include "pythonsettings.h" +#include + +#include +#include +#include + +#include +#include + +#include #include #include -#include #include +#include #include +#include -using namespace TextEditor; +using namespace ProjectExplorer; +using namespace Utils; namespace Python { namespace Internal { +static constexpr char startPylsInfoBarId[] = "PythonEditor::StartPyls"; + +struct PythonForProject +{ + FilePath path; + PythonProject *project = nullptr; + + QString name() const + { + if (!path.exists()) + return {}; + if (cachedName.first != path) { + SynchronousProcess pythonProcess; + const CommandLine pythonVersionCommand(path, {"--version"}); + SynchronousProcessResponse response = pythonProcess.runBlocking(pythonVersionCommand); + cachedName.first = path; + cachedName.second = response.allOutput().trimmed(); + } + return cachedName.second; + } + +private: + mutable QPair cachedName; +}; + +static PythonForProject detectPython(TextEditor::TextDocument *document) +{ + PythonForProject python; + + python.project = qobject_cast( + SessionManager::projectForFile(document->filePath())); + if (!python.project) + python.project = qobject_cast(SessionManager::startupProject()); + + if (python.project) { + if (auto target = python.project->activeTarget()) { + if (auto runConfig = qobject_cast( + target->activeRunConfiguration())) { + python.path = FilePath::fromString(runConfig->interpreter()); + } + } + } + + if (!python.path.exists()) + python.path = PythonSettings::defaultInterpreter().command; + + if (!python.path.exists() && !PythonSettings::interpreters().isEmpty()) + python.path = PythonSettings::interpreters().first().command; + + return python; +} + +FilePath getPylsModulePath(CommandLine pylsCommand) +{ + pylsCommand.addArg("-h"); + SynchronousProcess pythonProcess; + pythonProcess.setEnvironment(pythonProcess.environment() + QStringList("PYTHONVERBOSE=x")); + SynchronousProcessResponse response = pythonProcess.runBlocking(pylsCommand); + + static const QString pylsInitPattern = "(.*)" + + QRegularExpression::escape( + QDir::toNativeSeparators("/pyls/__init__.py")) + + '$'; + static const QString cachedPattern = " matches " + pylsInitPattern; + static const QRegularExpression regexCached(" matches " + pylsInitPattern, + QRegularExpression::MultilineOption); + static const QRegularExpression regexNotCached(" code object from " + pylsInitPattern, + QRegularExpression::MultilineOption); + + const QString &output = response.allOutput(); + for (auto regex : {regexCached, regexNotCached}) { + QRegularExpressionMatch result = regex.match(output); + if (result.hasMatch()) + return FilePath::fromUserInput(result.captured(1)); + } + return {}; +} + +struct PythonLanguageServerState +{ + enum { CanNotBeInstalled, CanBeInstalled, AlreadyInstalled, AlreadyConfigured } state; + FilePath pylsModulePath; +}; + +static QList configuredPythonLanguageServer( + Core::IDocument *doc) +{ + using namespace LanguageClient; + QList result; + for (const BaseSettings *setting : LanguageClientManager::currentSettings()) { + if (setting->m_languageFilter.isSupported(doc)) + result << dynamic_cast(setting); + } + return result; +} + +static PythonLanguageServerState checkPythonLanguageServer(const FilePath &python, + TextEditor::TextDocument *document) +{ + using namespace LanguageClient; + SynchronousProcess pythonProcess; + const CommandLine pythonLShelpCommand(python, {"-m", "pyls", "-h"}); + SynchronousProcessResponse response = pythonProcess.runBlocking(pythonLShelpCommand); + if (response.allOutput().contains("Python Language Server")) { + const FilePath &modulePath = getPylsModulePath(pythonLShelpCommand); + for (const StdIOSettings *serverSetting : configuredPythonLanguageServer(document)) { + CommandLine serverCommand(FilePath::fromUserInput(serverSetting->m_executable), + serverSetting->arguments(), + CommandLine::Raw); + + if (modulePath == getPylsModulePath(serverCommand)) + return {PythonLanguageServerState::AlreadyConfigured, FilePath()}; + } + + return {PythonLanguageServerState::AlreadyInstalled, getPylsModulePath(pythonLShelpCommand)}; + } + + const CommandLine pythonPipVersionCommand(python, {"-m", "pip", "-V"}); + response = pythonProcess.runBlocking(pythonPipVersionCommand); + if (response.allOutput().startsWith("pip ")) + return {PythonLanguageServerState::CanBeInstalled, FilePath()}; + else + return {PythonLanguageServerState::CanNotBeInstalled, FilePath()}; +} + +static LanguageClient::Client *registerLanguageServer(const PythonForProject &python) +{ + auto *settings = new LanguageClient::StdIOSettings(); + settings->m_executable = python.path.toString(); + settings->m_arguments = "-m pyls"; + settings->m_name = PythonEditorFactory::tr("Python Language Server (%1)").arg(python.name()); + settings->m_languageFilter.mimeTypes = QStringList(Constants::C_PY_MIMETYPE); + LanguageClient::LanguageClientManager::registerClientSettings(settings); + return LanguageClient::LanguageClientManager::clientForSetting(settings).value(0); +} + +static void setupPythonLanguageServer(const PythonForProject &python, + QPointer document) +{ + document->infoBar()->removeInfo(startPylsInfoBarId); + if (LanguageClient::Client *client = registerLanguageServer(python)) + LanguageClient::LanguageClientManager::reOpenDocumentWithClient(document, client); +} + +static void updateEditorInfoBar(const PythonForProject &python, TextEditor::TextDocument *document) +{ + const PythonLanguageServerState &lsState = checkPythonLanguageServer(python.path, document); + + if (lsState.state == PythonLanguageServerState::CanNotBeInstalled + || lsState.state == PythonLanguageServerState::AlreadyConfigured + || lsState.state == PythonLanguageServerState::CanBeInstalled /* TODO */) { + return; + } + + Core::InfoBar *infoBar = document->infoBar(); + if (lsState.state == PythonLanguageServerState::AlreadyInstalled + && infoBar->canInfoBeAdded(startPylsInfoBarId)) { + auto message = PythonEditorFactory::tr("Found a Python language server for %1 (%2). " + "Should this one be set up for this document?") + .arg(python.name(), python.path.toUserOutput()); + Core::InfoBarEntry info(startPylsInfoBarId, + message, + Core::InfoBarEntry::GlobalSuppression::Enabled); + info.setCustomButtonInfo(TextEditor::BaseTextEditor::tr("Setup"), + [=]() { setupPythonLanguageServer(python, document); }); + infoBar->addInfo(info); + } +} + +static void documentOpened(Core::IDocument *document) +{ + auto textDocument = qobject_cast(document); + if (!textDocument || textDocument->mimeType() != Constants::C_PY_MIMETYPE) + return; + + const PythonForProject &python = detectPython(textDocument); + if (!python.path.exists()) + return; + + updateEditorInfoBar(python, textDocument); +} + PythonEditorFactory::PythonEditorFactory() { setId(Constants::C_PYTHONEDITOR_ID); - setDisplayName(QCoreApplication::translate("OpenWith::Editors", Constants::C_EDITOR_DISPLAY_NAME)); + setDisplayName( + QCoreApplication::translate("OpenWith::Editors", Constants::C_EDITOR_DISPLAY_NAME)); addMimeType(Constants::C_PY_MIMETYPE); - setEditorActionHandlers(TextEditorActionHandler::Format - | TextEditorActionHandler::UnCommentSelection - | TextEditorActionHandler::UnCollapseAll - | TextEditorActionHandler::FollowSymbolUnderCursor); + setEditorActionHandlers(TextEditor::TextEditorActionHandler::Format + | TextEditor::TextEditorActionHandler::UnCommentSelection + | TextEditor::TextEditorActionHandler::UnCollapseAll + | TextEditor::TextEditorActionHandler::FollowSymbolUnderCursor); - setDocumentCreator([] { return new TextDocument(Constants::C_PYTHONEDITOR_ID); }); + setDocumentCreator([] { return new TextEditor::TextDocument(Constants::C_PYTHONEDITOR_ID); }); setIndenterCreator([](QTextDocument *doc) { return new PythonIndenter(doc); }); setSyntaxHighlighterCreator([] { return new PythonHighlighter; }); - setCommentDefinition(Utils::CommentDefinition::HashStyle); + setCommentDefinition(CommentDefinition::HashStyle); setParenthesesMatchingEnabled(true); setCodeFoldingSupported(true); + + connect(Core::EditorManager::instance(), &Core::EditorManager::documentOpened, + this, documentOpened); } } // namespace Internal diff --git a/src/plugins/python/pythonrunconfiguration.h b/src/plugins/python/pythonrunconfiguration.h index 753be4c2968..bb5139e615a 100644 --- a/src/plugins/python/pythonrunconfiguration.h +++ b/src/plugins/python/pythonrunconfiguration.h @@ -42,6 +42,7 @@ class PythonRunConfiguration : public ProjectExplorer::RunConfiguration public: PythonRunConfiguration(ProjectExplorer::Target *target, Core::Id id); + QString interpreter() const; private: void doAdditionalSetup(const ProjectExplorer::RunConfigurationCreationInfo &) final; @@ -49,7 +50,6 @@ private: bool supportsDebugger() const; QString mainScript() const; QString arguments() const; - QString interpreter() const; void updateTargetInformation(); }; diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp index bf6705fbeef..c60be833d2d 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp @@ -801,10 +801,19 @@ void TimelineBarItem::itemMoved(const QPointF &start, const QPointF &end) if (isActiveHandle(Location::Undefined)) dragInit(rect(), start); - const qreal min = qreal(TimelineConstants::sectionWidth + TimelineConstants::timelineLeftOffset - - scrollOffset()); - const qreal max = qreal(timelineScene()->rulerWidth() - TimelineConstants::sectionWidth - + rect().width()); + qreal min = qreal(TimelineConstants::sectionWidth + TimelineConstants::timelineLeftOffset + - scrollOffset()); + qreal max = qreal(timelineScene()->rulerWidth() - TimelineConstants::sectionWidth + + rect().width()); + + const qreal minFrameX = mapFromFrameToScene(timelineScene()->startFrame()); + const qreal maxFrameX = mapFromFrameToScene(timelineScene()->endFrame()); + + if (min < minFrameX) + min = minFrameX; + + if (max > maxFrameX) + max = maxFrameX; if (isActiveHandle(Location::Center)) dragCenter(rect(), end, min, max); diff --git a/src/shared/designerintegrationv2/CMakeLists.txt b/src/shared/designerintegrationv2/CMakeLists.txt index 0298277a160..8e2520d50e7 100644 --- a/src/shared/designerintegrationv2/CMakeLists.txt +++ b/src/shared/designerintegrationv2/CMakeLists.txt @@ -3,7 +3,7 @@ if (NOT TARGET Qt5::Designer) endif() add_qtc_library(designerintegrationv2 STATIC - DEPENDS Qt5::Designer + DEPENDS Qt5::Designer Qt5::Widgets SOURCES formresizer.cpp formresizer.h sizehandlerect.cpp sizehandlerect.h