2023-03-10 13:55:17 +01:00
|
|
|
// Copyright (C) 2022 The Qt Company Ltd.
|
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
|
|
|
|
|
|
|
|
|
#include "shellintegration.h"
|
|
|
|
|
|
2023-07-17 13:51:56 +02:00
|
|
|
#include "terminalsettings.h"
|
|
|
|
|
|
2023-03-10 13:55:17 +01:00
|
|
|
#include <utils/environment.h>
|
|
|
|
|
#include <utils/filepath.h>
|
|
|
|
|
#include <utils/stringutils.h>
|
|
|
|
|
|
2023-07-17 13:51:56 +02:00
|
|
|
#include <QApplication>
|
2023-03-10 13:55:17 +01:00
|
|
|
#include <QLoggingCategory>
|
|
|
|
|
|
|
|
|
|
Q_LOGGING_CATEGORY(integrationLog, "qtc.terminal.shellintegration", QtWarningMsg)
|
|
|
|
|
|
|
|
|
|
using namespace Utils;
|
|
|
|
|
|
|
|
|
|
namespace Terminal {
|
|
|
|
|
|
|
|
|
|
struct FileToCopy
|
|
|
|
|
{
|
|
|
|
|
FilePath source;
|
|
|
|
|
QString destName;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// clang-format off
|
|
|
|
|
struct
|
|
|
|
|
{
|
|
|
|
|
struct
|
|
|
|
|
{
|
|
|
|
|
FilePath rcFile{":/terminal/shellintegrations/shellintegration-bash.sh"};
|
|
|
|
|
} bash;
|
|
|
|
|
struct
|
|
|
|
|
{
|
|
|
|
|
QList<FileToCopy> files{
|
|
|
|
|
{":/terminal/shellintegrations/shellintegration-env.zsh", ".zshenv"},
|
|
|
|
|
{":/terminal/shellintegrations/shellintegration-login.zsh", ".zlogin"},
|
|
|
|
|
{":/terminal/shellintegrations/shellintegration-profile.zsh", ".zprofile"},
|
|
|
|
|
{":/terminal/shellintegrations/shellintegration-rc.zsh", ".zshrc"}
|
|
|
|
|
};
|
|
|
|
|
} zsh;
|
|
|
|
|
struct
|
|
|
|
|
{
|
|
|
|
|
FilePath script{":/terminal/shellintegrations/shellintegration.ps1"};
|
|
|
|
|
} pwsh;
|
2023-04-19 20:00:04 +02:00
|
|
|
struct
|
|
|
|
|
{
|
|
|
|
|
FilePath script{":/terminal/shellintegrations/shellintegration-clink.lua"};
|
|
|
|
|
} clink;
|
2023-03-10 13:55:17 +01:00
|
|
|
|
|
|
|
|
} filesToCopy;
|
|
|
|
|
// clang-format on
|
|
|
|
|
|
|
|
|
|
bool ShellIntegration::canIntegrate(const Utils::CommandLine &cmdLine)
|
|
|
|
|
{
|
|
|
|
|
if (cmdLine.executable().needsDevice())
|
|
|
|
|
return false; // TODO: Allow integration for remote shells
|
|
|
|
|
|
2023-05-09 10:07:05 +02:00
|
|
|
if (cmdLine.executable().baseName() == "zsh")
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (!cmdLine.arguments().isEmpty() && cmdLine.arguments() != "-l")
|
2023-03-10 13:55:17 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (cmdLine.executable().baseName() == "bash")
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (cmdLine.executable().baseName() == "pwsh"
|
|
|
|
|
|| cmdLine.executable().baseName() == "powershell") {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-19 20:00:04 +02:00
|
|
|
if (cmdLine.executable().baseName() == "cmd")
|
|
|
|
|
return true;
|
|
|
|
|
|
2023-03-10 13:55:17 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-17 13:51:56 +02:00
|
|
|
void ShellIntegration::onOsc(int cmd, std::string_view str, bool initial, bool final)
|
2023-03-10 13:55:17 +01:00
|
|
|
{
|
2023-08-03 08:25:40 +02:00
|
|
|
if (initial)
|
|
|
|
|
m_oscBuffer.clear();
|
2023-07-17 13:51:56 +02:00
|
|
|
|
2023-08-03 08:25:40 +02:00
|
|
|
m_oscBuffer.append(str);
|
|
|
|
|
|
|
|
|
|
if (!final)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
QString d = QString::fromLocal8Bit(m_oscBuffer);
|
2023-03-10 13:55:17 +01:00
|
|
|
const auto [command, data] = Utils::splitAtFirst(d, ';');
|
|
|
|
|
|
|
|
|
|
if (cmd == 1337) {
|
|
|
|
|
const auto [key, value] = Utils::splitAtFirst(command, '=');
|
|
|
|
|
if (key == QStringView(u"CurrentDir"))
|
|
|
|
|
emit currentDirChanged(FilePath::fromUserInput(value.toString()).path());
|
|
|
|
|
|
|
|
|
|
} else if (cmd == 7) {
|
2023-06-06 07:59:09 +02:00
|
|
|
const QString decoded = QUrl::fromPercentEncoding(d.toUtf8());
|
|
|
|
|
emit currentDirChanged(FilePath::fromUserInput(decoded).path());
|
2023-03-10 13:55:17 +01:00
|
|
|
} else if (cmd == 133) {
|
|
|
|
|
qCDebug(integrationLog) << "OSC 133:" << data;
|
|
|
|
|
} else if (cmd == 633 && command.length() == 1) {
|
|
|
|
|
if (command[0] == 'E') {
|
|
|
|
|
CommandLine cmdLine = CommandLine::fromUserInput(data.toString());
|
|
|
|
|
emit commandChanged(cmdLine);
|
|
|
|
|
} else if (command[0] == 'D') {
|
|
|
|
|
emit commandChanged({});
|
|
|
|
|
} else if (command[0] == 'P') {
|
|
|
|
|
const auto [key, value] = Utils::splitAtFirst(data, '=');
|
|
|
|
|
if (key == QStringView(u"Cwd"))
|
|
|
|
|
emit currentDirChanged(value.toString());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-17 13:51:56 +02:00
|
|
|
void ShellIntegration::onBell()
|
|
|
|
|
{
|
|
|
|
|
if (settings().audibleBell.value())
|
|
|
|
|
QApplication::beep();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ShellIntegration::onTitle(const QString &title)
|
|
|
|
|
{
|
|
|
|
|
emit titleChanged(title);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-03 16:00:22 +02:00
|
|
|
void ShellIntegration::prepareProcess(Utils::Process &process)
|
2023-03-10 13:55:17 +01:00
|
|
|
{
|
|
|
|
|
Environment env = process.environment().hasChanges() ? process.environment()
|
|
|
|
|
: Environment::systemEnvironment();
|
|
|
|
|
CommandLine cmd = process.commandLine();
|
|
|
|
|
|
|
|
|
|
if (!canIntegrate(cmd))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
env.set("VSCODE_INJECTION", "1");
|
2023-04-19 20:00:04 +02:00
|
|
|
env.set("TERM_PROGRAM", "vscode");
|
2023-03-10 13:55:17 +01:00
|
|
|
|
|
|
|
|
if (cmd.executable().baseName() == "bash") {
|
|
|
|
|
const FilePath rcPath = filesToCopy.bash.rcFile;
|
|
|
|
|
const FilePath tmpRc = FilePath::fromUserInput(
|
|
|
|
|
m_tempDir.filePath(filesToCopy.bash.rcFile.fileName()));
|
|
|
|
|
rcPath.copyFile(tmpRc);
|
|
|
|
|
|
2023-05-11 15:46:59 +02:00
|
|
|
CommandLine newCmd = {cmd.executable(), {"--init-file", tmpRc.nativePath()}};
|
|
|
|
|
|
|
|
|
|
if (cmd.arguments() == "-l")
|
|
|
|
|
newCmd.addArg("-l");
|
|
|
|
|
|
|
|
|
|
cmd = newCmd;
|
2023-03-10 13:55:17 +01:00
|
|
|
} else if (cmd.executable().baseName() == "zsh") {
|
|
|
|
|
for (const FileToCopy &file : filesToCopy.zsh.files) {
|
|
|
|
|
const auto copyResult = file.source.copyFile(
|
|
|
|
|
FilePath::fromUserInput(m_tempDir.filePath(file.destName)));
|
|
|
|
|
QTC_ASSERT_EXPECTED(copyResult, return);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Utils::FilePath originalZdotDir = FilePath::fromUserInput(
|
|
|
|
|
env.value_or("ZDOTDIR", QDir::homePath()));
|
|
|
|
|
|
|
|
|
|
env.set("ZDOTDIR", m_tempDir.path());
|
|
|
|
|
env.set("USER_ZDOTDIR", originalZdotDir.nativePath());
|
|
|
|
|
} else if (cmd.executable().baseName() == "pwsh"
|
|
|
|
|
|| cmd.executable().baseName() == "powershell") {
|
|
|
|
|
const FilePath rcPath = filesToCopy.pwsh.script;
|
|
|
|
|
const FilePath tmpRc = FilePath::fromUserInput(
|
|
|
|
|
m_tempDir.filePath(filesToCopy.pwsh.script.fileName()));
|
|
|
|
|
rcPath.copyFile(tmpRc);
|
|
|
|
|
|
|
|
|
|
cmd.addArgs(QString("-noexit -command try { . \"%1\" } catch {}{1}").arg(tmpRc.nativePath()),
|
|
|
|
|
CommandLine::Raw);
|
2023-04-19 20:00:04 +02:00
|
|
|
} else if (cmd.executable().baseName() == "cmd") {
|
|
|
|
|
const FilePath rcPath = filesToCopy.clink.script;
|
|
|
|
|
const FilePath tmpRc = FilePath::fromUserInput(
|
|
|
|
|
m_tempDir.filePath(filesToCopy.clink.script.fileName()));
|
|
|
|
|
rcPath.copyFile(tmpRc);
|
|
|
|
|
|
|
|
|
|
env.set("CLINK_HISTORY_LABEL", "QtCreator");
|
|
|
|
|
env.appendOrSet("CLINK_PATH", tmpRc.parentDir().nativePath(), ";");
|
2023-03-10 13:55:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
process.setCommand(cmd);
|
|
|
|
|
process.setEnvironment(env);
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-03 08:25:10 +02:00
|
|
|
void ShellIntegration::onSetClipboard(const QByteArray &text)
|
|
|
|
|
{
|
|
|
|
|
setClipboardAndSelection(QString::fromLocal8Bit(text));
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-10 13:55:17 +01:00
|
|
|
} // namespace Terminal
|