diff --git a/RedPandaIDE/compiler/compilermanager.cpp b/RedPandaIDE/compiler/compilermanager.cpp index 44ddbe7e..f450c7fd 100644 --- a/RedPandaIDE/compiler/compilermanager.cpp +++ b/RedPandaIDE/compiler/compilermanager.cpp @@ -265,15 +265,21 @@ void CompilerManager::run( sharedMemoryId, localizePath(filename) } + splitProcessCommand(arguments); - auto [filename, args, fileOwner] = wrapCommandForTerminalEmulator( - pSettings->environment().terminalPathForExec(), - pSettings->environment().terminalArgumentsPattern(), - execArgs - ); - //delete when thread finished - execRunner = new ExecutableRunner(filename, args, workDir); - execRunner->setShareMemoryId(sharedMemoryId); - mTempFileOwner = std::move(fileOwner); + if (pSettings->environment().useCustomTerminal()) { + auto [filename, args, fileOwner] = wrapCommandForTerminalEmulator( + pSettings->environment().terminalPathForExec(), + pSettings->environment().terminalArgumentsPattern(), + execArgs + ); + //delete when thread finished + execRunner = new ExecutableRunner(filename, args, workDir); + execRunner->setShareMemoryId(sharedMemoryId); + mTempFileOwner = std::move(fileOwner); + } else { + //delete when thread finished + execRunner = new ExecutableRunner(execArgs[0], execArgs.mid(1), workDir); + execRunner->setShareMemoryId(sharedMemoryId); + } } else { //delete when thread finished execRunner = new ExecutableRunner(filename,splitProcessCommand(arguments),workDir); diff --git a/RedPandaIDE/defaultconfigs.qrc b/RedPandaIDE/defaultconfigs.qrc index bdd1afea..fbdb565d 100644 --- a/RedPandaIDE/defaultconfigs.qrc +++ b/RedPandaIDE/defaultconfigs.qrc @@ -3,5 +3,7 @@ resources/autolink.json resources/codesnippets.json resources/autolink-xdg.json + resources/terminal-windows.json + resources/terminal-unix.json diff --git a/RedPandaIDE/resources/terminal-unix.json b/RedPandaIDE/resources/terminal-unix.json new file mode 100644 index 00000000..72d5393d --- /dev/null +++ b/RedPandaIDE/resources/terminal-unix.json @@ -0,0 +1,207 @@ +[ + { + "group": "User Installed", + "terminals": [ + { + "name": "Alacritty", + "path": "alacritty", + "argsPattern": "$term -e $argv" + }, + { + "name": "cool-retro-term", + "path": "cool-retro-term", + "argsPattern": "$term -e $argv" + }, + { + "name": "CoreTerminal", + "path": "coreterminal", + "argsPattern": "$term -e \"$command\"", + "comment": "The pair of quotation mark around `$command` is only a visual symbol, not actually required." + }, + { + "name": "iTerm2", + "path": "/Applications/iTerm.app/Contents/MacOS/iTerm2", + "argsPattern": "$term $tmpfile" + }, + { + "name": "kermit", + "path": "kermit", + "argsPattern": "$term -e \"$command\"" + }, + { + "name": "Kitty", + "path": "kitty", + "argsPattern": "$term -e $argv" + }, + { + "name": "ROXTerm", + "path": "roxterm", + "argsPattern": "$term -e \"$command\"" + }, + { + "name": "sakura", + "path": "sakura", + "argsPattern": "$term -e \"$command\"" + }, + { + "name": "Termit", + "path": "termit", + "argsPattern": "$term -e $argv" + }, + { + "name": "Termite", + "path": "termite", + "argsPattern": "$term -e \"$command\"" + }, + { + "name": "Tilix", + "path": "tilix", + "argsPattern": "$term -e $argv" + }, + { + "name": "Wayst", + "path": "wayst", + "argsPattern": "$term -e $argv" + } + ] + }, + { + "group": "Desktop Default", + "terminals": [ + { + "name": "Deepin Terminal", + "path": "deepin-terminal", + "argsPattern": "$term -e $argv" + }, + { + "name": "Konsole (KDE)", + "path": "konsole", + "argsPattern": "$term -e $argv" + }, + { + "name": "GNOME Terminal", + "path": "gnome-terminal", + "argsPattern": "$term -- $argv" + }, + { + "name": "GNOME Terminator", + "path": "terminator", + "argsPattern": "$term -x $argv" + }, + { + "name": "LXTerminal (LXDE)", + "path": "lxterminal", + "argsPattern": "$term -e $argv" + }, + { + "name": "MATE Terminal", + "path": "mate-terminal", + "argsPattern": "$term -x $argv" + }, + { + "name": "QTerminal (LXQt)", + "path": "qterminal", + "argsPattern": "$term -e $argv" + }, + { + "name": "Terminal.app (macOS 10.14 or earlier)", + "path": "/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal", + "argsPattern": "$term $tmpfile" + }, + { + "name": "Terminal.app (macOS 10.15 or later)", + "path": "/System/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal", + "argsPattern": "$term $tmpfile" + }, + { + "name": "Terminology (Enlightenment)", + "path": "terminology", + "argsPattern": "$term -e \"$command\"" + }, + { + "name": "Xfce Terminal", + "path": "xfce4-terminal", + "argsPattern": "$term -x $argv" + } + ] + }, + { + "group": "Bundled", + "terminals": [ + { + "name": "Alacritty (Bundled)", + "path": "./alacritty", + "argsPattern": "$term -e $argv" + } + ] + }, + { + "group": "With minor issue", + "terminals": [ + { + "name": "Elementary Terminal", + "path": "io.elementary.terminal", + "argsPattern": "$term -e \"$command\"", + "comment": "confirm to quit" + }, + { + "name": "foot", + "path": "foot", + "argsPattern": "$term -e $argv", + "comment": "wayland only" + }, + { + "name": "GNOME Console", + "path": "kgx", + "argsPattern": "$term -- $argv", + "comment": "confirm to quit" + }, + { + "name": "mlterm", + "path": "mlterm", + "argsPattern": "$term -e $argv", + "comment": "no out of box HiDPI support" + }, + { + "name": "st", + "path": "st", + "argsPattern": "$term -e $argv", + "comment": "no out of box HiDPI support" + }, + { + "name": "urxvt", + "path": "urxvt", + "argsPattern": "$term -e $argv", + "comment": "no out of box HiDPI support" + }, + { + "name": "xterm", + "path": "xterm", + "argsPattern": "$term -e $argv", + "comment": "no out of box HiDPI support" + }, + { + "name": "zutty", + "path": "zutty", + "argsPattern": "$term -e $argv", + "comment": "no out of box HiDPI support" + } + ] + }, + { + "group": "Unavailable", + "terminals": [], + "unavailableTerminals": { + "aterm": "AUR broken, unable to test", + "eterm": "AUR broken, unable to test", + "guake": "drop down terminal", + "hyper": "no execute support", + "liri-terminal": "no execute support", + "rxvt": "no unicode support", + "shellinabox": "AUR broken, unable to test", + "station": "no execute support", + "tilda": "drop down terminal", + "yakuake": "drop down terminal" + } + } +] diff --git a/RedPandaIDE/resources/terminal-windows.json b/RedPandaIDE/resources/terminal-windows.json new file mode 100644 index 00000000..729eaf4f --- /dev/null +++ b/RedPandaIDE/resources/terminal-windows.json @@ -0,0 +1,52 @@ +[ + { + "group": "Bundled", + "terminals": [ + { + "name": "UTF-8 compatible Console Host", + "path": "./OpenConsole.exe", + "argsPattern": "$term -- $argv" + } + ] + }, + { + "group": "System", + "terminals": [ + { + "name": "System Default", + "path": "", + "argsPattern": "$argv" + } + ] + }, + { + "group": "Predefined arguments pattern (will not be searched)", + "terminals": [ + { + "name": "Console Host", + "path": "conhost.exe", + "argsPattern": "$term -- $argv" + }, + { + "name": "Windows Terminal", + "path": "wt.exe", + "argsPattern": "$term -- $argv" + }, + { + "name": "Alacritty", + "path": "alacritty.exe", + "argsPattern": "$term -e $argv" + }, + { + "name": "Konsole", + "path": "konsole.exe", + "argsPattern": "$term -e $argv" + }, + { + "name": "Mintty", + "path": "mintty.exe", + "argsPattern": "$term -e $argv" + } + ] + } +] diff --git a/RedPandaIDE/settings.cpp b/RedPandaIDE/settings.cpp index 6587f6be..17cc6d24 100644 --- a/RedPandaIDE/settings.cpp +++ b/RedPandaIDE/settings.cpp @@ -27,6 +27,9 @@ #include #include #include +#include +#include +#include #ifdef Q_OS_LINUX #include #endif @@ -3608,146 +3611,46 @@ void Settings::Environment::doLoad() mDefaultOpenFolder = QDir::currentPath(); } - using AP = TerminalEmulatorArgumentsPattern; - struct TerminalSearchItem { - QString appName; - AP argsPattern; - }; - #ifdef Q_OS_WINDOWS - const TerminalSearchItem terminals[] { - /* explicitly installed terminals */ - - /* system */ - {"conhost.exe", AP::ImplicitSystem}, // dummy for system default - - /* will not actually be searched, just a list for users who dig into here */ - {"conhost.exe", AP::MinusMinusAppendArgs}, // yes, it accepts GNU-style (--) arguments - {"wt.exe", AP::MinusMinusAppendArgs}, // generally okay, but “Test” does not work - {"alacritty.exe", AP::MinusEAppendArgs}, // GPU-accelerated - {"C:/Program Files/Alacritty/alacritty.exe", AP::MinusEAppendArgs}, - {"C:/Program Files/konsole/bin/konsole.exe", AP::MinusEAppendArgs}, // generally okay, but “Test” does not work - {"C:/Program Files/Git/usr/bin/mintty.exe", AP::MinusEAppendArgs}, // Git Mintty - {"C:/msys64/usr/bin/mintty.exe", AP::MinusEAppendArgs}, // MSYS2 Mintty - }; -#else - const TerminalSearchItem terminals[] { - /* modern, specialized or stylized terminal -- user who installed them are likely to prefer them */ - {"alacritty", AP::MinusEAppendArgs}, // GPU-accelerated - {"kitty", AP::MinusEAppendArgs}, // GPU-accelerated - {"wayst", AP::MinusEAppendArgs}, // GPU-accelerated - - {"coreterminal", AP::MinusEAppendCommandLine}, // lightweighted - {"kermit", AP::MinusEAppendCommandLine}, // lightweighted - {"roxterm", AP::MinusEAppendCommandLine}, // lightweighted - {"sakura", AP::MinusEAppendCommandLine}, // lightweighted - {"termit", AP::MinusEAppendArgs}, // Lua scripting - {"termite", AP::MinusEAppendCommandLine}, // tiling, keyboard-centric - {"tilix", AP::MinusEAppendArgs}, // tiling - - {"cool-retro-term", AP::MinusEAppendArgs}, // old CRT style - - /* default terminal for XDG DE -- macOS user who installed them are likely to prefer them */ - {"deepin-terminal", AP::MinusEAppendArgs}, // DDE - {"konsole", AP::MinusEAppendArgs}, // KDE - {"gnome-terminal", AP::MinusMinusAppendArgs}, // GNOME - {"io.elementary.terminal", AP::MinusEAppendCommandLine}, // Pantheon (elementary OS) - {"lxterminal", AP::MinusEAppendArgs}, // LXDE - {"mate-terminal", AP::MinusXAppendArgs}, // MATE - {"qterminal", AP::MinusEAppendArgs}, // LXQt - {"terminator", AP::MinusXAppendArgs}, // tiling, also seen in SBC images - {"terminology", AP::MinusEAppendCommandLine}, // Enlightenment - {"xfce4-terminal", AP::MinusXAppendArgs}, // Xfce - - /* bundled terminal in AppImage */ - {"./alacritty", AP::MinusEAppendArgs}, - - /* compatible, with minor issue */ - {"kgx", AP::MinusMinusAppendArgs}, // GNOME Console, confirm to quit - - /* compatible, without out-of-box hidpi support on Linux */ - {"mlterm", AP::MinusEAppendArgs}, - {"st", AP::MinusEAppendArgs}, - {"urxvt", AP::MinusEAppendArgs}, - {"xterm", AP::MinusEAppendArgs}, - {"zutty", AP::MinusEAppendArgs}, - - /* macOS system */ - {"/Applications/iTerm.app/Contents/MacOS/iTerm2", AP::WriteCommandLineToTempFileThenTempFilename}, - {"/System/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal", AP::WriteCommandLineToTempFileThenTempFilename}, - {"/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal", AP::WriteCommandLineToTempFileThenTempFilename}, - - /* fallbacks */ - {"foot", AP::MinusEAppendArgs}, // Wayland only - - /* parameter incompatible */ - // "guake", // drop down - // "hyper", // no execute support - // "liri-terminal", // no execute support - // "station", // no execute support - // "tilda", // drop down - - /* incompatible -- other */ - // "aterm", // AUR broken, unable to test - // "eterm", // AUR broken, unable to test - // "rxvt", // no unicode support - // "shellinabox", // AUR broken, unable to test - }; +# if defined (__aarch64__) || defined(_M_ARM64) || defined (_M_ARM64EC) + // the only native MinGW toolchain (LLVM-MinGW) does not have local codepage support + // prefer UTF-8 compatible OpenConsole.exe + mUseCustomTerminal = boolValue("use_custom_terminal", true); +# else // x86 or x64 + mUseCustomTerminal = boolValue("use_custom_terminal", false); +# endif +#else // UNIX + mUseCustomTerminal = true; #endif - auto checkAndSetTerminalPath = [this](const TerminalSearchItem &searchItem) -> bool { -#define DO_CHECK_AND_SET do { \ - if (termPathInfo.isFile() && termPathInfo.isReadable() && termPathInfo.isExecutable()) { \ - mTerminalPath = searchItem.appName; \ - mTerminalArgumentsPattern = searchItem.argsPattern; \ - return true; \ - } \ - } while (0) - - switch (getPathUnixExecSemantics(searchItem.appName)) { - case UnixExecSemantics::Absolute: { - QFileInfo termPathInfo(searchItem.appName); - DO_CHECK_AND_SET; - break; - } - case UnixExecSemantics::RelativeToCwd: { - QDir appDir(pSettings->dirs().appDir()); - QString absoluteTerminalPath = appDir.absoluteFilePath(searchItem.appName); - QFileInfo termPathInfo(absoluteTerminalPath); - DO_CHECK_AND_SET; - break; - } - case UnixExecSemantics::SearchInPath: { - QStringList pathList = getExecutableSearchPaths(); - for (const QString &dir: pathList) { - QString absoluteTerminalPath = QDir(dir).absoluteFilePath(searchItem.appName); - QFileInfo termPathInfo(absoluteTerminalPath); - DO_CHECK_AND_SET; - } - break; - } - } - return false; - }; -#undef DO_CHECK_AND_SET +#ifdef Q_OS_WINDOWS + QString terminalListFilename(":/config/terminal-windows.json"); +#else // UNIX + QString terminalListFilename(":/config/terminal-unix.json"); +#endif + QFile terminalListFile(terminalListFilename); + if (!terminalListFile.open(QFile::ReadOnly)) + throw FileError(QObject::tr("Can't open file '%1' for read.") + .arg(terminalListFilename)); + QByteArray terminalListContent = terminalListFile.readAll(); + QJsonDocument terminalListDocument(QJsonDocument::fromJson(terminalListContent)); // check saved terminal path QString savedTerminalPath = stringValue("terminal_path", ""); - int savedArgsPattern_ = intValue("terminal_arguments_pattern", -#ifdef Q_OS_MACOS - // macOS: old versions have set Terminal.app as default terminal - // fallback to temp file to work with Terminal.app for smooth migration - int(AP::WriteCommandLineToTempFileThenTempFilename) -#else - int(AP::MinusEAppendArgs) // Linux: keep old behaviour -#endif - ); - AP savedArgsPattern = static_cast(savedArgsPattern_); - if (!checkAndSetTerminalPath(TerminalSearchItem{savedTerminalPath, savedArgsPattern})) { - // if saved terminal path is invalid, try determing terminal from our list - for (auto terminal: terminals) { - if (checkAndSetTerminalPath(terminal)) - break; + QString savedArgsPattern = stringValue("terminal_arguments_pattern", ""); + bool terminalSet = checkAndSetTerminal(savedTerminalPath, savedArgsPattern); + + // determing terminal (if not set yet) and build predefined arguments pattern map from our list + for (const auto &terminalGroup: terminalListDocument.array()) { + const QJsonArray &terminals = terminalGroup.toObject()["terminals"].toArray(); + for (const auto &terminal_: terminals) { + const QJsonObject &terminal = terminal_.toObject(); + const QString &path = terminal["path"].toString(); + const QString &executable = QFileInfo(path).fileName(); + const QString &pattern = terminal["argsPattern"].toString(); + mPredefinedTerminalArgumentsPattern[executable] = pattern; + if (!terminalSet) + terminalSet = checkAndSetTerminal(path, pattern); } } @@ -3835,12 +3738,12 @@ void Settings::Environment::setAStylePath(const QString &aStylePath) mAStylePath = aStylePath; } -TerminalEmulatorArgumentsPattern Settings::Environment::terminalArgumentsPattern() const +QString Settings::Environment::terminalArgumentsPattern() const { return mTerminalArgumentsPattern; } -void Settings::Environment::setTerminalArgumentsPattern(const TerminalEmulatorArgumentsPattern &argsPattern) +void Settings::Environment::setTerminalArgumentsPattern(const QString &argsPattern) { mTerminalArgumentsPattern = argsPattern; } @@ -3895,6 +3798,89 @@ void Settings::Environment::setIconZoomFactor(double newIconZoomFactor) mIconZoomFactor = newIconZoomFactor; } +QMap Settings::Environment::predefinedTerminalArgumentsPattern() const +{ + return mPredefinedTerminalArgumentsPattern; +} + +void Settings::Environment::setPredefinedTerminalArgumentsPattern(const QMap &newPredefinedTerminalArgumentsPattern) +{ + mPredefinedTerminalArgumentsPattern = newPredefinedTerminalArgumentsPattern; +} + +std::unique_ptr Settings::Environment::queryPredefinedTerminalArgumentsPattern(const QString &executable) const +{ + auto it = mPredefinedTerminalArgumentsPattern.find(executable); + if (it != mPredefinedTerminalArgumentsPattern.end()) + return std::make_unique(*it); + else + return nullptr; +} + +bool Settings::Environment::useCustomTerminal() const +{ + return mUseCustomTerminal; +} + +void Settings::Environment::setUseCustomTerminal(bool newUseCustomTerminal) +{ + mUseCustomTerminal = newUseCustomTerminal; +} + +bool Settings::Environment::checkAndSetTerminal(QString terminalPath, QString argsPattern) +{ + QStringList patternItems = splitProcessCommand(argsPattern); + + if (patternItems.empty() || + !(patternItems.contains("$argv") || patternItems.contains("$command") || patternItems.contains("$tmpfile")) // program not referenced + ) + return false; + + // `term` is not referenced ("$argv"), + // or is not directly called ("open -app $term -args $tmpfile"), + // do not check terminal path + if (patternItems[0] != "$term") { + setTerminalPath(terminalPath); + setTerminalArgumentsPattern(argsPattern); + return true; + } + +#define DO_CHECK_AND_SET do { \ + if (termPathInfo.isFile() && termPathInfo.isReadable() && termPathInfo.isExecutable()) { \ + mTerminalPath = terminalPath; \ + mTerminalArgumentsPattern = argsPattern; \ + return true; \ + } \ + } while (0) + + switch (getPathUnixExecSemantics(terminalPath)) { + case UnixExecSemantics::Absolute: { + QFileInfo termPathInfo(terminalPath); + DO_CHECK_AND_SET; + break; + } + case UnixExecSemantics::RelativeToCwd: { + QDir appDir(pSettings->dirs().appDir()); + QString absoluteTerminalPath = appDir.absoluteFilePath(terminalPath); + QFileInfo termPathInfo(absoluteTerminalPath); + DO_CHECK_AND_SET; + break; + } + case UnixExecSemantics::SearchInPath: { + QStringList pathList = getExecutableSearchPaths(); + for (const QString &dir: pathList) { + QString absoluteTerminalPath = QDir(dir).absoluteFilePath(terminalPath); + QFileInfo termPathInfo(absoluteTerminalPath); + DO_CHECK_AND_SET; + } + break; + } + } +#undef DO_CHECK_AND_SET + + return false; +} + void Settings::Environment::doSave() { //Appearance @@ -3910,7 +3896,10 @@ void Settings::Environment::doSave() saveValue("current_folder",mCurrentFolder); saveValue("default_open_folder",mDefaultOpenFolder); saveValue("terminal_path",mTerminalPath); - saveValue("terminal_arguments_pattern",int(mTerminalArgumentsPattern)); + saveValue("terminal_arguments_pattern",mTerminalArgumentsPattern); +#ifdef Q_OS_WINDOWS + saveValue("use_custom_terminal",mUseCustomTerminal); +#endif saveValue("asyle_path",mAStylePath); saveValue("hide_non_support_files_file_view",mHideNonSupportFilesInFileView); diff --git a/RedPandaIDE/settings.h b/RedPandaIDE/settings.h index 6b606e4a..933bfdf2 100644 --- a/RedPandaIDE/settings.h +++ b/RedPandaIDE/settings.h @@ -575,8 +575,8 @@ public: QString AStylePath() const; void setAStylePath(const QString &aStylePath); - TerminalEmulatorArgumentsPattern terminalArgumentsPattern() const; - void setTerminalArgumentsPattern(const TerminalEmulatorArgumentsPattern &argsPattern); + QString terminalArgumentsPattern() const; + void setTerminalArgumentsPattern(const QString &argsPattern); bool useCustomIconSet() const; void setUseCustomIconSet(bool newUseCustomIconSet); @@ -593,7 +593,22 @@ public: double iconZoomFactor() const; void setIconZoomFactor(double newIconZoomFactor); + QJsonArray availableTerminals() const; + void setAvailableTerminals(const QJsonArray &availableTerminals); + + QMap predefinedTerminalArgumentsPattern() const; + void setPredefinedTerminalArgumentsPattern(const QMap &newPredefinedTerminalArgumentsPattern); + // it should be `std::optional`. + // `std::unique_ptr` is a work around for Debian 10, where Qt 5.11 doesnt recognize `CONFIG += c++17`, + // and macOS, where official Qt 5.15 is built against macOS 10.13 and `std::optional` is explicitly disabled. + std::unique_ptr queryPredefinedTerminalArgumentsPattern(const QString &executable) const; + + bool useCustomTerminal() const; + void setUseCustomTerminal(bool newUseCustomTerminal); + private: + bool checkAndSetTerminal(QString terminalPath, QString argsPattern); + bool updateTerminalList(); //Appearance QString mTheme; @@ -609,7 +624,9 @@ public: QString mDefaultOpenFolder; QString mTerminalPath; QString mAStylePath; - TerminalEmulatorArgumentsPattern mTerminalArgumentsPattern; + QString mTerminalArgumentsPattern; + QMap mPredefinedTerminalArgumentsPattern; + bool mUseCustomTerminal; bool mHideNonSupportFilesInFileView; bool mOpenFilesInSingleInstance; // _Base interface diff --git a/RedPandaIDE/settingsdialog/environmentprogramswidget.cpp b/RedPandaIDE/settingsdialog/environmentprogramswidget.cpp index 43fbc57d..e0a8c630 100644 --- a/RedPandaIDE/settingsdialog/environmentprogramswidget.cpp +++ b/RedPandaIDE/settingsdialog/environmentprogramswidget.cpp @@ -22,21 +22,16 @@ #include "../compiler/executablerunner.h" #include +#include EnvironmentProgramsWidget::EnvironmentProgramsWidget(const QString& name, const QString& group, QWidget *parent) : SettingsWidget(name,group,parent), ui(new Ui::EnvironmentProgramsWidget) { ui->setupUi(this); - QFont monoFont(DEFAULT_MONO_FONT); - ui->rbImplicitSystem->setFont(monoFont); - ui->rbMinusEAppendArgs->setFont(monoFont); - ui->rbMinusXAppendArgs->setFont(monoFont); - ui->rbMinusMinusAppendArgs->setFont(monoFont); - ui->rbMinusEAppendCommandLine->setFont(monoFont); - ui->rbWriteCommandLineToTempFileThenTempFilename->setFont(monoFont); -#ifndef Q_OS_MACOS - hideMacosSpecificPattern(); + ui->labelCmdPreviewResult->setFont(QFont(DEFAULT_MONO_FONT)); +#ifndef Q_OS_WINDOWS + ui->grpUseCustomTerminal->setCheckable(false); #endif } @@ -45,68 +40,67 @@ EnvironmentProgramsWidget::~EnvironmentProgramsWidget() delete ui; } -void EnvironmentProgramsWidget::hideMacosSpecificPattern() +auto EnvironmentProgramsWidget::resolveExecArguments(const QString &terminalPath, const QString &argsPattern) + -> std::tuple> { - ui->rbWriteCommandLineToTempFileThenTempFilename->setVisible(false); - ui->rbWriteCommandLineToTempFileThenTempFilename->setEnabled(false); - ui->pbWriteCommandLineToTempFileThenTempFilename->setVisible(false); - ui->pbWriteCommandLineToTempFileThenTempFilename->setEnabled(false); + QString terminalPathForExec; + if (getPathUnixExecSemantics(terminalPath) == UnixExecSemantics::RelativeToCwd) { + QDir appDir(pSettings->dirs().appDir()); + terminalPathForExec = appDir.absoluteFilePath(terminalPath); + } else + terminalPathForExec = terminalPath; + + QString shell = defaultShell(); + QStringList payloadArgs{shell, "-c", "echo hello; sleep 3"}; + return wrapCommandForTerminalEmulator(terminalPathForExec, argsPattern, payloadArgs); } -void EnvironmentProgramsWidget::testTerminal(const TerminalEmulatorArgumentsPattern &pattern) +void EnvironmentProgramsWidget::updateCommandPreview(const QString &terminalPath, const QString &argsPattern) { - auto [filename, arguments, fileOwner] = wrapCommandForTerminalEmulator(ui->txtTerminal->text(), pattern, {defaultShell(), "-c", "echo hello; sleep 3"}); - ExecutableRunner runner(filename, arguments, "", nullptr); - runner.start(); - runner.wait(); + auto [filename, arguments, fileOwner] = resolveExecArguments(terminalPath, argsPattern); + for (auto &arg : arguments) + arg = escapeArgument(arg, false); + + ui->labelCmdPreviewResult->setText(escapeArgument(filename, true) + " " + arguments.join(' ')); +} + +void EnvironmentProgramsWidget::autoDetectAndUpdateArgumentsPattern(const QString &terminalPath) +{ + const QString &executable = QFileInfo(terminalPath).fileName(); + const std::unique_ptr &pattern = pSettings->environment().queryPredefinedTerminalArgumentsPattern(executable); + if (pattern != nullptr) + ui->txtArgsPattern->setText(*pattern); + else + QMessageBox::warning(nullptr, + QObject::tr("Auto Detection Failed"), + QObject::tr("Failed to detect terminal arguments pattern for “%1”.").arg(executable), + QMessageBox::Ok); } void EnvironmentProgramsWidget::doLoad() { +#ifdef Q_OS_WINDOWS + ui->grpUseCustomTerminal->setChecked(pSettings->environment().useCustomTerminal()); +#endif ui->txtTerminal->setText(pSettings->environment().terminalPath()); - switch (pSettings->environment().terminalArgumentsPattern()) { - case TerminalEmulatorArgumentsPattern::ImplicitSystem: - ui->rbImplicitSystem->setChecked(true); - break; - case TerminalEmulatorArgumentsPattern::MinusEAppendArgs: - ui->rbMinusEAppendArgs->setChecked(true); - break; - case TerminalEmulatorArgumentsPattern::MinusXAppendArgs: - ui->rbMinusXAppendArgs->setChecked(true); - break; - case TerminalEmulatorArgumentsPattern::MinusMinusAppendArgs: - ui->rbMinusMinusAppendArgs->setChecked(true); - break; - case TerminalEmulatorArgumentsPattern::MinusEAppendCommandLine: - ui->rbMinusEAppendCommandLine->setChecked(true); - break; - case TerminalEmulatorArgumentsPattern::WriteCommandLineToTempFileThenTempFilename: - ui->rbWriteCommandLineToTempFileThenTempFilename->setChecked(true); - break; - } + ui->txtArgsPattern->setText(pSettings->environment().terminalArgumentsPattern()); } void EnvironmentProgramsWidget::doSave() { +#ifdef Q_OS_WINDOWS + pSettings->environment().setUseCustomTerminal(ui->grpUseCustomTerminal->isChecked()); +#endif pSettings->environment().setTerminalPath(ui->txtTerminal->text()); - if (ui->rbImplicitSystem->isChecked()) - pSettings->environment().setTerminalArgumentsPattern(TerminalEmulatorArgumentsPattern::ImplicitSystem); - if (ui->rbMinusEAppendArgs->isChecked()) - pSettings->environment().setTerminalArgumentsPattern(TerminalEmulatorArgumentsPattern::MinusEAppendArgs); - if (ui->rbMinusXAppendArgs->isChecked()) - pSettings->environment().setTerminalArgumentsPattern(TerminalEmulatorArgumentsPattern::MinusXAppendArgs); - if (ui->rbMinusMinusAppendArgs->isChecked()) - pSettings->environment().setTerminalArgumentsPattern(TerminalEmulatorArgumentsPattern::MinusMinusAppendArgs); - if (ui->rbMinusEAppendCommandLine->isChecked()) - pSettings->environment().setTerminalArgumentsPattern(TerminalEmulatorArgumentsPattern::MinusEAppendCommandLine); - if (ui->rbWriteCommandLineToTempFileThenTempFilename->isChecked()) - pSettings->environment().setTerminalArgumentsPattern(TerminalEmulatorArgumentsPattern::WriteCommandLineToTempFileThenTempFilename); + pSettings->environment().setTerminalArgumentsPattern(ui->txtArgsPattern->text()); pSettings->environment().save(); } void EnvironmentProgramsWidget::updateIcons(const QSize &) { pIconsManager->setIcon(ui->btnChooseTerminal,IconsManager::ACTION_FILE_OPEN_FOLDER); + pIconsManager->setIcon(ui->btnAutoDetectArgsPattern,IconsManager::ACTION_EDIT_SEARCH); + pIconsManager->setIcon(ui->btnTest,IconsManager::ACTION_RUN_RUN); } void EnvironmentProgramsWidget::on_btnChooseTerminal_clicked() @@ -118,64 +112,34 @@ void EnvironmentProgramsWidget::on_btnChooseTerminal_clicked() tr("All files (%1)").arg(ALL_FILE_WILDCARD)); if (!filename.isEmpty() && fileExists(filename) ) { ui->txtTerminal->setText(filename); + autoDetectAndUpdateArgumentsPattern(filename); } } void EnvironmentProgramsWidget::on_txtTerminal_textChanged(const QString &terminalPath) { - QString terminalPathForExec; - if (getPathUnixExecSemantics(terminalPath) == UnixExecSemantics::RelativeToCwd) { - QDir appDir(pSettings->dirs().appDir()); - terminalPathForExec = appDir.absoluteFilePath(terminalPath); - } else - terminalPathForExec = terminalPath; - QString terminalPathEscaped = escapeArgument(terminalPathForExec, true); - QString shell = defaultShell(); - QStringList execArgs{shell, "-c", "echo hello; sleep 3"}; - - auto displayCommand = [this, &execArgs](const TerminalEmulatorArgumentsPattern &pattern) { - auto [filename, arguments, fileOwner] = wrapCommandForTerminalEmulator(ui->txtTerminal->text(), pattern, execArgs); - for (auto &arg : arguments) - arg = escapeArgument(arg, false); - return escapeArgument(filename, true) + " " + arguments.join(' '); - }; - - ui->rbImplicitSystem->setText(displayCommand(TerminalEmulatorArgumentsPattern::ImplicitSystem)); - ui->rbMinusEAppendArgs->setText(displayCommand(TerminalEmulatorArgumentsPattern::MinusEAppendArgs)); - ui->rbMinusXAppendArgs->setText(displayCommand(TerminalEmulatorArgumentsPattern::MinusXAppendArgs)); - ui->rbMinusMinusAppendArgs->setText(displayCommand(TerminalEmulatorArgumentsPattern::MinusMinusAppendArgs)); - ui->rbMinusEAppendCommandLine->setText(displayCommand(TerminalEmulatorArgumentsPattern::MinusEAppendCommandLine)); - if (ui->rbWriteCommandLineToTempFileThenTempFilename->isEnabled()) - ui->rbWriteCommandLineToTempFileThenTempFilename->setText(displayCommand(TerminalEmulatorArgumentsPattern::WriteCommandLineToTempFileThenTempFilename)); + const QString &argsPattern = ui->txtArgsPattern->text(); + updateCommandPreview(terminalPath, argsPattern); } -void EnvironmentProgramsWidget::on_pbImplicitSystem_clicked() +void EnvironmentProgramsWidget::on_txtArgsPattern_textChanged(const QString &argsPattern) { - testTerminal(TerminalEmulatorArgumentsPattern::ImplicitSystem); + const QString &terminalPath = ui->txtTerminal->text(); + updateCommandPreview(terminalPath, argsPattern); } -void EnvironmentProgramsWidget::on_pbMinusEAppendArgs_clicked() +void EnvironmentProgramsWidget::on_btnAutoDetectArgsPattern_clicked() { - testTerminal(TerminalEmulatorArgumentsPattern::MinusEAppendArgs); + const QString &terminalPath = ui->txtTerminal->text(); + autoDetectAndUpdateArgumentsPattern(terminalPath); } -void EnvironmentProgramsWidget::on_pbMinusXAppendArgs_clicked() +void EnvironmentProgramsWidget::on_btnTest_clicked() { - testTerminal(TerminalEmulatorArgumentsPattern::MinusXAppendArgs); + const QString &terminalPath = ui->txtTerminal->text(); + const QString &argsPattern = ui->txtArgsPattern->text(); + auto [filename, arguments, fileOwner] = resolveExecArguments(terminalPath, argsPattern); + ExecutableRunner runner(filename, arguments, "", nullptr); + runner.start(); + runner.wait(); } - -void EnvironmentProgramsWidget::on_pbMinusMinusAppendArgs_clicked() -{ - testTerminal(TerminalEmulatorArgumentsPattern::MinusMinusAppendArgs); -} - -void EnvironmentProgramsWidget::on_pbMinusEAppendCommandLine_clicked() -{ - testTerminal(TerminalEmulatorArgumentsPattern::MinusEAppendCommandLine); -} - -void EnvironmentProgramsWidget::on_pbWriteCommandLineToTempFileThenTempFilename_clicked() -{ - testTerminal(TerminalEmulatorArgumentsPattern::WriteCommandLineToTempFileThenTempFilename); -} - diff --git a/RedPandaIDE/settingsdialog/environmentprogramswidget.h b/RedPandaIDE/settingsdialog/environmentprogramswidget.h index 11c8cbb2..ce7b80c2 100644 --- a/RedPandaIDE/settingsdialog/environmentprogramswidget.h +++ b/RedPandaIDE/settingsdialog/environmentprogramswidget.h @@ -31,10 +31,12 @@ class EnvironmentProgramsWidget : public SettingsWidget public: explicit EnvironmentProgramsWidget(const QString& name, const QString& group, QWidget *parent = nullptr); ~EnvironmentProgramsWidget(); - void hideMacosSpecificPattern(); private: - void testTerminal(const TerminalEmulatorArgumentsPattern &pattern); + auto resolveExecArguments(const QString &terminalPath, const QString &argsPatter) + -> std::tuple>; + void updateCommandPreview(const QString &terminalPath, const QString &argsPatter); + void autoDetectAndUpdateArgumentsPattern(const QString &terminalPath); private: Ui::EnvironmentProgramsWidget *ui; @@ -47,12 +49,9 @@ protected: private slots: void on_btnChooseTerminal_clicked(); void on_txtTerminal_textChanged(const QString &terminalPath); - void on_pbImplicitSystem_clicked(); - void on_pbMinusEAppendArgs_clicked(); - void on_pbMinusXAppendArgs_clicked(); - void on_pbMinusMinusAppendArgs_clicked(); - void on_pbMinusEAppendCommandLine_clicked(); - void on_pbWriteCommandLineToTempFileThenTempFilename_clicked(); + void on_txtArgsPattern_textChanged(const QString &argsPattern); + void on_btnAutoDetectArgsPattern_clicked(); + void on_btnTest_clicked(); }; #endif // ENVIRONMENTPROGRAMSWIDGET_H diff --git a/RedPandaIDE/settingsdialog/environmentprogramswidget.ui b/RedPandaIDE/settingsdialog/environmentprogramswidget.ui index 91243b04..b68e9a45 100644 --- a/RedPandaIDE/settingsdialog/environmentprogramswidget.ui +++ b/RedPandaIDE/settingsdialog/environmentprogramswidget.ui @@ -13,163 +13,100 @@ Form - - - - - - - - ... - - - - :/icons/images/newlook24/053-open.png:/icons/images/newlook24/053-open.png - - - - - - - Terminal - - - - - + + + - Terminal emulator arguments pattern + Use custom terminal + + + true + + + true - - - - sh -c "echo hello; sleep 3" - - - - - - Qt::Horizontal - - - - 40 - 20 - - - + - - + + - Test + $term -e $argv - + + + Args. pattern + + + + + + + ... + + + + :/icons/images/newlook24/053-open.png:/icons/images/newlook24/053-open.png + + + + + + + Auto Detect Terminal Arguments Pattern + + + ... + + + + :/icons/images/newlook24/087-update.png:/icons/images/newlook24/087-update.png + + + + + + + Terminal + + + + + + + Cmd. preview + + + + + term -e sh -c "echo hello; sleep 3" - - - - Test - - - - - - - term -x sh -c "echo hello; sleep 3" - - - - - - Test + + + Test Command - - - - - term -- sh -c "echo hello; sleep 3" + ... - - - - - - Test - - - - - - - term -e "sh -c \"echo hello; sleep 3\"" - - - - - - - Test - - - - - - - term /tmp/redpanda_XXXXXX.command - - - - - - - Test - - - - - - - On clicking “Test” for the correct pattern, the terminal emulator - - - - - - - • pops up; - - - - - - - • shows “hello”; and - - - - - - - • quits in 3 seconds. + + + :/icons/images/newlook24/069-run.png:/icons/images/newlook24/069-run.png - + Qt::Vertical diff --git a/RedPandaIDE/translations/RedPandaIDE_pt_BR.ts b/RedPandaIDE/translations/RedPandaIDE_pt_BR.ts index 865c7822..11709c75 100644 --- a/RedPandaIDE/translations/RedPandaIDE_pt_BR.ts +++ b/RedPandaIDE/translations/RedPandaIDE_pt_BR.ts @@ -1958,52 +1958,32 @@ All files (%1) Todos os arquivos (%1) - - Terminal emulator arguments pattern - - - - sh -c "echo hello; sleep 3" - - - - Test - Testar - term -e sh -c "echo hello; sleep 3" - term -x sh -c "echo hello; sleep 3" + Auto Detect Terminal Arguments Pattern - term -- sh -c "echo hello; sleep 3" + Test Command - term -e "sh -c \"echo hello; sleep 3\"" + $term -e $argv - On clicking “Test” for the correct pattern, the terminal emulator + Use custom terminal - • pops up; + Args. pattern - • shows “hello”; and - - - - • quits in 3 seconds. - - - - term /tmp/redpanda_XXXXXX.command + Cmd. preview @@ -7077,6 +7057,14 @@ Replaces lcall/ljmp with acall/ajmp + + Auto Detection Failed + + + + Failed to detect terminal arguments pattern for “%1”. + + RegisterModel diff --git a/RedPandaIDE/translations/RedPandaIDE_zh_CN.ts b/RedPandaIDE/translations/RedPandaIDE_zh_CN.ts index 2acbaf5a..15a3b65e 100644 --- a/RedPandaIDE/translations/RedPandaIDE_zh_CN.ts +++ b/RedPandaIDE/translations/RedPandaIDE_zh_CN.ts @@ -2670,103 +2670,74 @@ Are you really want to continue? Editors share one code parser 编辑器共享同一个代码分析器 - - - EnvironmentProgramsWidget - - - Form - 表单 - - - - ... - ... - - - - Terminal - 终端 - - - - Terminal emulator arguments pattern - 终端模拟器参数模式 - - - - sh -c "echo hello; sleep 3" - - - - - - - - - - Test - 测试 - - - - term -e sh -c "echo hello; sleep 3" - - - - - term -x sh -c "echo hello; sleep 3" - - - - - term -- sh -c "echo hello; sleep 3" - - - - - term -e "sh -c \"echo hello; sleep 3\"" - - - - - term /tmp/redpanda_XXXXXX.command - - - - - On clicking “Test” for the correct pattern, the terminal emulator - 点击正确的模式对应的 “测试” 按钮,终端模拟器将会 - - - - • pops up; - • 弹出; - - - - • shows “hello”; and - • 显示 “hello”; - - - - • quits in 3 seconds. - • 并在 3 秒后关闭。 - - - - Choose Terminal Program - 选择终端程序 - - - - All files (%1) - 所有文件 (%1) - - - All files (*.*) - 所有文件 (*.*) - + +EnvironmentProgramsWidget + + + Form + 表单 + + + + Use custom terminal + 使用自定义终端 + + + + + + ... + ... + + + + Cmd. preview + 命令行预览 + + + + Auto Detect Terminal Arguments Pattern + 自动检测终端参数模式 + + + + Terminal + 终端 + + + + Test Command + 测试命令 + + + + $term -e $argv + + + + + Args. pattern + 参数模式 + + + + term -e sh -c "echo hello; sleep 3" + + + + + Choose Terminal Program + 选择终端程序 + + + + All files (%1) + 所有文件 (%1) + + + All files (*.*) + 所有文件 (*.*) + EnvironmentShortcutModel @@ -9633,6 +9604,16 @@ Are you really want to continue? Error when writing file "%1". 在写入文件“%1”时出错。 + + + Auto Detection Failed + 自动检测失败 + + + + Failed to detect terminal arguments pattern for “%1”. + 无法检测适用于 “%1” 的终端参数模式。 + RegisterModel diff --git a/RedPandaIDE/translations/RedPandaIDE_zh_TW.ts b/RedPandaIDE/translations/RedPandaIDE_zh_TW.ts index 7ff0aaff..34007e4a 100644 --- a/RedPandaIDE/translations/RedPandaIDE_zh_TW.ts +++ b/RedPandaIDE/translations/RedPandaIDE_zh_TW.ts @@ -1791,52 +1791,32 @@ All files (%1) - - Terminal emulator arguments pattern - - - - sh -c "echo hello; sleep 3" - - - - Test - - term -e sh -c "echo hello; sleep 3" - term -x sh -c "echo hello; sleep 3" + Auto Detect Terminal Arguments Pattern - term -- sh -c "echo hello; sleep 3" + Test Command - term -e "sh -c \"echo hello; sleep 3\"" + $term -e $argv - On clicking “Test” for the correct pattern, the terminal emulator + Use custom terminal - • pops up; + Args. pattern - • shows “hello”; and - - - - • quits in 3 seconds. - - - - term /tmp/redpanda_XXXXXX.command + Cmd. preview @@ -6602,6 +6582,14 @@ Replaces lcall/ljmp with acall/ajmp + + Auto Detection Failed + + + + Failed to detect terminal arguments pattern for “%1”. + + RegisterModel diff --git a/RedPandaIDE/utils.cpp b/RedPandaIDE/utils.cpp index 95d5fe02..d43371b7 100644 --- a/RedPandaIDE/utils.cpp +++ b/RedPandaIDE/utils.cpp @@ -723,49 +723,51 @@ QString escapeArgument(const QString &arg, [[maybe_unused]] bool isFirstArg) #endif } -auto wrapCommandForTerminalEmulator(const QString &terminal, const TerminalEmulatorArgumentsPattern &argsPattern, const QStringList &argsWithArgv0) +auto wrapCommandForTerminalEmulator(const QString &terminal, const QStringList &argsPattern, const QStringList &payloadArgsWithArgv0) -> std::tuple> { - switch (argsPattern) { - case TerminalEmulatorArgumentsPattern::ImplicitSystem: - default: { - return {argsWithArgv0[0], argsWithArgv0.mid(1), nullptr}; - } - case TerminalEmulatorArgumentsPattern::MinusEAppendArgs: { - return {terminal, QStringList{"-e"} + argsWithArgv0, nullptr}; - } - case TerminalEmulatorArgumentsPattern::MinusXAppendArgs: { - return {terminal, QStringList{"-x"} + argsWithArgv0, nullptr}; - } - case TerminalEmulatorArgumentsPattern::MinusMinusAppendArgs: { - return {terminal, QStringList{"--"} + argsWithArgv0, nullptr}; - } - case TerminalEmulatorArgumentsPattern::MinusEAppendCommandLine: { - QStringList escapedArgs; - for (int i = 0; i < argsWithArgv0.length(); i++) { - auto &arg = argsWithArgv0[i]; - auto escaped = escapeArgument(arg, i == 0); - escapedArgs.append(escaped); - } - return {terminal, QStringList{"-e", escapedArgs.join(' ')}, nullptr}; - } - case TerminalEmulatorArgumentsPattern::WriteCommandLineToTempFileThenTempFilename: { - auto fileOwner = std::make_unique(QDir::tempPath() + "/redpanda_XXXXXX.command"); - if (fileOwner->open()) { + QStringList wrappedArgs; + std::unique_ptr temproryFile; + for (const QString &patternItem : argsPattern) { + if (patternItem == "$term") + wrappedArgs.push_back(terminal); + else if (patternItem == "$argv") + wrappedArgs.append(payloadArgsWithArgv0); + else if (patternItem == "$command") { QStringList escapedArgs; - for (int i = 0; i < argsWithArgv0.length(); i++) { - auto &arg = argsWithArgv0[i]; + for (int i = 0; i < payloadArgsWithArgv0.length(); i++) { + auto &arg = payloadArgsWithArgv0[i]; auto escaped = escapeArgument(arg, i == 0); escapedArgs.append(escaped); } - fileOwner->write(escapedArgs.join(' ').toUtf8()); - fileOwner->write(QString('\n').toUtf8()); - fileOwner->flush(); - } - QFile(fileOwner->fileName()).setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner); - return {terminal, QStringList{fileOwner->fileName()}, std::move(fileOwner)}; - } + wrappedArgs.push_back(escapedArgs.join(' ')); + } else if (patternItem == "$tmpfile") { + temproryFile = std::make_unique(QDir::tempPath() + "/redpanda_XXXXXX.command"); + if (temproryFile->open()) { + QStringList escapedArgs; + for (int i = 0; i < payloadArgsWithArgv0.length(); i++) { + auto &arg = payloadArgsWithArgv0[i]; + auto escaped = escapeArgument(arg, i == 0); + escapedArgs.append(escaped); + } + temproryFile->write(escapedArgs.join(' ').toUtf8()); + temproryFile->write("\n"); + temproryFile->flush(); + QFile(temproryFile->fileName()).setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner); + } + wrappedArgs.push_back(temproryFile->fileName()); + } else + wrappedArgs.push_back(patternItem); } + if (wrappedArgs.empty()) + return {QString(""), QStringList{}, std::move(temproryFile)}; + return {wrappedArgs[0], wrappedArgs.mid(1), std::move(temproryFile)}; +} + +auto wrapCommandForTerminalEmulator(const QString &terminal, const QString &argsPattern, const QStringList &payloadArgsWithArgv0) + -> std::tuple> +{ + return wrapCommandForTerminalEmulator(terminal, splitProcessCommand(argsPattern), payloadArgsWithArgv0); } QString defaultShell() diff --git a/RedPandaIDE/utils.h b/RedPandaIDE/utils.h index 00934181..44f78503 100644 --- a/RedPandaIDE/utils.h +++ b/RedPandaIDE/utils.h @@ -121,16 +121,6 @@ enum class UnixExecSemantics { SearchInPath, }; -enum class TerminalEmulatorArgumentsPattern { - ImplicitSystem = 0, // bash -c "echo hello, world; sleep 3" - MinusEAppendArgs, // term -e bash -c "echo hello, world; sleep 3" # xterm-compatible - MinusXAppendArgs, // term -x bash -c "echo hello, world; sleep 3" # some VTE-based - MinusMinusAppendArgs, // term -- bash -c "echo hello, world; sleep 3" # gnome-terminal, kgx - MinusEAppendCommandLine, // term -e "bash -c \"echo hello, world; sleep 3\"" # some lightweighted; alternative form for VTE-based - - WriteCommandLineToTempFileThenTempFilename = 6226700, // macOS Terminal.app and iTerm2.app; 6226700 is how you dial “macOS00” -}; - FileType getFileType(const QString& filename); QStringList splitProcessCommand(const QString& cmd); @@ -189,7 +179,10 @@ QStringList getExecutableSearchPaths(); QString escapeArgument(const QString &arg, bool isFirstArg); -auto wrapCommandForTerminalEmulator(const QString &terminal, const TerminalEmulatorArgumentsPattern &argsPattern, const QStringList &argsWithArgv0) +auto wrapCommandForTerminalEmulator(const QString &terminal, const QStringList &argsPattern, const QStringList &payloadArgsWithArgv0) + -> std::tuple>; + +auto wrapCommandForTerminalEmulator(const QString &terminal, const QString &argsPattern, const QStringList &payloadArgsWithArgv0) -> std::tuple>; QString defaultShell();