forked from qt-creator/qt-creator
Terminal: Add shell integration
Change-Id: Ic1e226b56f0103e5a6e7764073ab7ab241b67baa Reviewed-by: Cristian Adam <cristian.adam@qt.io>
This commit is contained in:
30
README.md
30
README.md
@@ -783,7 +783,6 @@ SQLite (https://www.sqlite.org) is in the Public Domain.
|
|||||||
|
|
||||||
### libvterm
|
### libvterm
|
||||||
|
|
||||||
|
|
||||||
An abstract C99 library which implements a VT220 or xterm-like terminal emulator.
|
An abstract C99 library which implements a VT220 or xterm-like terminal emulator.
|
||||||
It doesn't use any particular graphics toolkit or output system, instead it invokes callback
|
It doesn't use any particular graphics toolkit or output system, instead it invokes callback
|
||||||
function pointers that its embedding program should provide it to draw on its behalf.
|
function pointers that its embedding program should provide it to draw on its behalf.
|
||||||
@@ -813,3 +812,32 @@ SQLite (https://www.sqlite.org) is in the Public Domain.
|
|||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
### terminal/shellintegrations
|
||||||
|
|
||||||
|
The Terminal plugin uses scripts to integrate with the shell. The scripts are
|
||||||
|
located in the Qt Creator source tree in src/plugins/terminal/shellintegrations.
|
||||||
|
|
||||||
|
https://github.com/microsoft/vscode/tree/main/src/vs/workbench/contrib/terminal/browser/media
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2015 - present Microsoft Corporation
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
@@ -1046,5 +1046,18 @@
|
|||||||
|
|
||||||
\include license-mit.qdocinc
|
\include license-mit.qdocinc
|
||||||
|
|
||||||
|
\li \b terminal/shellintegrations
|
||||||
|
|
||||||
|
The Terminal plugin uses scripts to integrate with the shell. The scripts are
|
||||||
|
located in the Qt Creator source tree in src/plugins/terminal/shellintegrations.
|
||||||
|
|
||||||
|
\list
|
||||||
|
\li \l https://github.com/microsoft/vscode/tree/main/src/vs/workbench/contrib/terminal/browser/media
|
||||||
|
\endlist
|
||||||
|
|
||||||
|
Distributed under the MIT license.
|
||||||
|
|
||||||
|
\include license-mit.qdocinc
|
||||||
|
|
||||||
\endlist
|
\endlist
|
||||||
*/
|
*/
|
||||||
|
@@ -5,7 +5,6 @@
|
|||||||
#include "savefile.h"
|
#include "savefile.h"
|
||||||
|
|
||||||
#include "algorithm.h"
|
#include "algorithm.h"
|
||||||
#include "hostosinfo.h"
|
|
||||||
#include "qtcassert.h"
|
#include "qtcassert.h"
|
||||||
#include "utilstr.h"
|
#include "utilstr.h"
|
||||||
|
|
||||||
|
@@ -121,7 +121,6 @@ public:
|
|||||||
QString *selectedFilter = nullptr,
|
QString *selectedFilter = nullptr,
|
||||||
QFileDialog::Options options = {});
|
QFileDialog::Options options = {});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// for actually finding out if e.g. directories are writable on Windows
|
// for actually finding out if e.g. directories are writable on Windows
|
||||||
|
@@ -355,7 +355,9 @@ public:
|
|||||||
|
|
||||||
bool startResult
|
bool startResult
|
||||||
= m_ptyProcess->startProcess(program,
|
= m_ptyProcess->startProcess(program,
|
||||||
arguments,
|
HostOsInfo::isWindowsHost()
|
||||||
|
? QStringList{m_setup.m_nativeArguments} << arguments
|
||||||
|
: arguments,
|
||||||
m_setup.m_workingDirectory.path(),
|
m_setup.m_workingDirectory.path(),
|
||||||
m_setup.m_environment.toProcessEnvironment().toStringList(),
|
m_setup.m_environment.toProcessEnvironment().toStringList(),
|
||||||
m_setup.m_ptyData->size().width(),
|
m_setup.m_ptyData->size().width(),
|
||||||
|
@@ -7,6 +7,7 @@ add_qtc_plugin(Terminal
|
|||||||
glyphcache.cpp glyphcache.h
|
glyphcache.cpp glyphcache.h
|
||||||
keys.cpp keys.h
|
keys.cpp keys.h
|
||||||
scrollback.cpp scrollback.h
|
scrollback.cpp scrollback.h
|
||||||
|
shellintegration.cpp shellintegration.h
|
||||||
shellmodel.cpp shellmodel.h
|
shellmodel.cpp shellmodel.h
|
||||||
terminal.qrc
|
terminal.qrc
|
||||||
terminalpane.cpp terminalpane.h
|
terminalpane.cpp terminalpane.h
|
||||||
|
143
src/plugins/terminal/shellintegration.cpp
Normal file
143
src/plugins/terminal/shellintegration.cpp
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "shellintegration.h"
|
||||||
|
|
||||||
|
#include <utils/environment.h>
|
||||||
|
#include <utils/filepath.h>
|
||||||
|
#include <utils/stringutils.h>
|
||||||
|
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
|
Q_LOGGING_CATEGORY(integrationLog, "qtc.terminal.shellintegration", QtWarningMsg)
|
||||||
|
|
||||||
|
using namespace Utils;
|
||||||
|
|
||||||
|
namespace Terminal {
|
||||||
|
|
||||||
|
struct FileToCopy
|
||||||
|
{
|
||||||
|
FilePath source;
|
||||||
|
QString destName;
|
||||||
|
};
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
FilePath rcFile{":/terminal/shellintegrations/shellintegration-bash.sh"};
|
||||||
|
} bash;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
QList<FileToCopy> files{
|
||||||
|
{":/terminal/shellintegrations/shellintegration-env.zsh", ".zshenv"},
|
||||||
|
{":/terminal/shellintegrations/shellintegration-login.zsh", ".zlogin"},
|
||||||
|
{":/terminal/shellintegrations/shellintegration-profile.zsh", ".zprofile"},
|
||||||
|
{":/terminal/shellintegrations/shellintegration-rc.zsh", ".zshrc"}
|
||||||
|
};
|
||||||
|
} zsh;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
FilePath script{":/terminal/shellintegrations/shellintegration.ps1"};
|
||||||
|
} pwsh;
|
||||||
|
|
||||||
|
} filesToCopy;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
bool ShellIntegration::canIntegrate(const Utils::CommandLine &cmdLine)
|
||||||
|
{
|
||||||
|
if (cmdLine.executable().needsDevice())
|
||||||
|
return false; // TODO: Allow integration for remote shells
|
||||||
|
|
||||||
|
if (!cmdLine.arguments().isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (cmdLine.executable().baseName() == "bash")
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (cmdLine.executable().baseName() == "zsh")
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (cmdLine.executable().baseName() == "pwsh"
|
||||||
|
|| cmdLine.executable().baseName() == "powershell") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShellIntegration::onOsc(int cmd, const VTermStringFragment &fragment)
|
||||||
|
{
|
||||||
|
QString d = QString::fromLocal8Bit(fragment.str, fragment.len);
|
||||||
|
const auto [command, data] = Utils::splitAtFirst(d, ';');
|
||||||
|
|
||||||
|
if (cmd == 1337) {
|
||||||
|
const auto [key, value] = Utils::splitAtFirst(command, '=');
|
||||||
|
if (key == QStringView(u"CurrentDir"))
|
||||||
|
emit currentDirChanged(FilePath::fromUserInput(value.toString()).path());
|
||||||
|
|
||||||
|
} else if (cmd == 7) {
|
||||||
|
emit currentDirChanged(FilePath::fromUserInput(d).path());
|
||||||
|
} else if (cmd == 133) {
|
||||||
|
qCDebug(integrationLog) << "OSC 133:" << data;
|
||||||
|
} else if (cmd == 633 && command.length() == 1) {
|
||||||
|
if (command[0] == 'E') {
|
||||||
|
CommandLine cmdLine = CommandLine::fromUserInput(data.toString());
|
||||||
|
emit commandChanged(cmdLine);
|
||||||
|
} else if (command[0] == 'D') {
|
||||||
|
emit commandChanged({});
|
||||||
|
} else if (command[0] == 'P') {
|
||||||
|
const auto [key, value] = Utils::splitAtFirst(data, '=');
|
||||||
|
if (key == QStringView(u"Cwd"))
|
||||||
|
emit currentDirChanged(value.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShellIntegration::prepareProcess(Utils::QtcProcess &process)
|
||||||
|
{
|
||||||
|
Environment env = process.environment().hasChanges() ? process.environment()
|
||||||
|
: Environment::systemEnvironment();
|
||||||
|
CommandLine cmd = process.commandLine();
|
||||||
|
|
||||||
|
if (!canIntegrate(cmd))
|
||||||
|
return;
|
||||||
|
|
||||||
|
env.set("VSCODE_INJECTION", "1");
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
cmd.addArgs({"--init-file", tmpRc.nativePath()});
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.setCommand(cmd);
|
||||||
|
process.setEnvironment(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Terminal
|
34
src/plugins/terminal/shellintegration.h
Normal file
34
src/plugins/terminal/shellintegration.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH
|
||||||
|
// Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <utils/commandline.h>
|
||||||
|
#include <utils/qtcprocess.h>
|
||||||
|
|
||||||
|
#include <vterm.h>
|
||||||
|
|
||||||
|
#include <QTemporaryDir>
|
||||||
|
|
||||||
|
namespace Terminal {
|
||||||
|
|
||||||
|
class ShellIntegration : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
static bool canIntegrate(const Utils::CommandLine &cmdLine);
|
||||||
|
|
||||||
|
void onOsc(int cmd, const VTermStringFragment &fragment);
|
||||||
|
|
||||||
|
void prepareProcess(Utils::QtcProcess &process);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void commandChanged(const Utils::CommandLine &command);
|
||||||
|
void currentDirChanged(const QString &dir);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTemporaryDir m_tempDir;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Terminal
|
252
src/plugins/terminal/shellintegrations/shellintegration-bash.sh
Executable file
252
src/plugins/terminal/shellintegrations/shellintegration-bash.sh
Executable file
@@ -0,0 +1,252 @@
|
|||||||
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
|
||||||
|
# Prevent the script recursing when setting up
|
||||||
|
if [[ -n "$VSCODE_SHELL_INTEGRATION" ]]; then
|
||||||
|
builtin return
|
||||||
|
fi
|
||||||
|
|
||||||
|
VSCODE_SHELL_INTEGRATION=1
|
||||||
|
|
||||||
|
# Run relevant rc/profile only if shell integration has been injected, not when run manually
|
||||||
|
if [ "$VSCODE_INJECTION" == "1" ]; then
|
||||||
|
if [ -z "$VSCODE_SHELL_LOGIN" ]; then
|
||||||
|
if [ -r ~/.bashrc ]; then
|
||||||
|
. ~/.bashrc
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Imitate -l because --init-file doesn't support it:
|
||||||
|
# run the first of these files that exists
|
||||||
|
if [ -r /etc/profile ]; then
|
||||||
|
. /etc/profile
|
||||||
|
fi
|
||||||
|
# exceute the first that exists
|
||||||
|
if [ -r ~/.bash_profile ]; then
|
||||||
|
. ~/.bash_profile
|
||||||
|
elif [ -r ~/.bash_login ]; then
|
||||||
|
. ~/.bash_login
|
||||||
|
elif [ -r ~/.profile ]; then
|
||||||
|
. ~/.profile
|
||||||
|
fi
|
||||||
|
builtin unset VSCODE_SHELL_LOGIN
|
||||||
|
|
||||||
|
# Apply any explicit path prefix (see #99878)
|
||||||
|
if [ -n "$VSCODE_PATH_PREFIX" ]; then
|
||||||
|
export PATH=$VSCODE_PATH_PREFIX$PATH
|
||||||
|
builtin unset VSCODE_PATH_PREFIX
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
builtin unset VSCODE_INJECTION
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$VSCODE_SHELL_INTEGRATION" ]; then
|
||||||
|
builtin return
|
||||||
|
fi
|
||||||
|
|
||||||
|
__vsc_get_trap() {
|
||||||
|
# 'trap -p DEBUG' outputs a shell command like `trap -- '…shellcode…' DEBUG`.
|
||||||
|
# The terms are quoted literals, but are not guaranteed to be on a single line.
|
||||||
|
# (Consider a trap like $'echo foo\necho \'bar\'').
|
||||||
|
# To parse, we splice those terms into an expression capturing them into an array.
|
||||||
|
# This preserves the quoting of those terms: when we `eval` that expression, they are preserved exactly.
|
||||||
|
# This is different than simply exploding the string, which would split everything on IFS, oblivious to quoting.
|
||||||
|
builtin local -a terms
|
||||||
|
builtin eval "terms=( $(trap -p "${1:-DEBUG}") )"
|
||||||
|
# |________________________|
|
||||||
|
# |
|
||||||
|
# \-------------------*--------------------/
|
||||||
|
# terms=( trap -- '…arbitrary shellcode…' DEBUG )
|
||||||
|
# |____||__| |_____________________| |_____|
|
||||||
|
# | | | |
|
||||||
|
# 0 1 2 3
|
||||||
|
# |
|
||||||
|
# \--------*----/
|
||||||
|
builtin printf '%s' "${terms[2]:-}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# The property (P) and command (E) codes embed values which require escaping.
|
||||||
|
# Backslashes are doubled. Non-alphanumeric characters are converted to escaped hex.
|
||||||
|
__vsc_escape_value() {
|
||||||
|
# Process text byte by byte, not by codepoint.
|
||||||
|
builtin local LC_ALL=C str="${1}" i byte token out=''
|
||||||
|
|
||||||
|
for (( i=0; i < "${#str}"; ++i )); do
|
||||||
|
byte="${str:$i:1}"
|
||||||
|
|
||||||
|
# Escape backslashes and semi-colons
|
||||||
|
if [ "$byte" = "\\" ]; then
|
||||||
|
token="\\\\"
|
||||||
|
elif [ "$byte" = ";" ]; then
|
||||||
|
token="\\x3b"
|
||||||
|
else
|
||||||
|
token="$byte"
|
||||||
|
fi
|
||||||
|
|
||||||
|
out+="$token"
|
||||||
|
done
|
||||||
|
|
||||||
|
builtin printf '%s\n' "${out}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Send the IsWindows property if the environment looks like Windows
|
||||||
|
if [[ "$(uname -s)" =~ ^CYGWIN*|MINGW*|MSYS* ]]; then
|
||||||
|
builtin printf '\e]633;P;IsWindows=True\a'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Allow verifying $BASH_COMMAND doesn't have aliases resolved via history when the right HISTCONTROL
|
||||||
|
# configuration is used
|
||||||
|
if [[ "$HISTCONTROL" =~ .*(erasedups|ignoreboth|ignoredups).* ]]; then
|
||||||
|
__vsc_history_verify=0
|
||||||
|
else
|
||||||
|
__vsc_history_verify=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
__vsc_initialized=0
|
||||||
|
__vsc_original_PS1="$PS1"
|
||||||
|
__vsc_original_PS2="$PS2"
|
||||||
|
__vsc_custom_PS1=""
|
||||||
|
__vsc_custom_PS2=""
|
||||||
|
__vsc_in_command_execution="1"
|
||||||
|
__vsc_current_command=""
|
||||||
|
|
||||||
|
__vsc_prompt_start() {
|
||||||
|
builtin printf '\e]633;A\a'
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_prompt_end() {
|
||||||
|
builtin printf '\e]633;B\a'
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_update_cwd() {
|
||||||
|
builtin printf '\e]633;P;Cwd=%s\a' "$(__vsc_escape_value "$PWD")"
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_command_output_start() {
|
||||||
|
builtin printf '\e]633;C\a'
|
||||||
|
builtin printf '\e]633;E;%s\a' "$(__vsc_escape_value "${__vsc_current_command}")"
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_continuation_start() {
|
||||||
|
builtin printf '\e]633;F\a'
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_continuation_end() {
|
||||||
|
builtin printf '\e]633;G\a'
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_command_complete() {
|
||||||
|
if [ "$__vsc_current_command" = "" ]; then
|
||||||
|
builtin printf '\e]633;D\a'
|
||||||
|
else
|
||||||
|
builtin printf '\e]633;D;%s\a' "$__vsc_status"
|
||||||
|
fi
|
||||||
|
__vsc_update_cwd
|
||||||
|
}
|
||||||
|
__vsc_update_prompt() {
|
||||||
|
# in command execution
|
||||||
|
if [ "$__vsc_in_command_execution" = "1" ]; then
|
||||||
|
# Wrap the prompt if it is not yet wrapped, if the PS1 changed this this was last set it
|
||||||
|
# means the user re-exported the PS1 so we should re-wrap it
|
||||||
|
if [[ "$__vsc_custom_PS1" == "" || "$__vsc_custom_PS1" != "$PS1" ]]; then
|
||||||
|
__vsc_original_PS1=$PS1
|
||||||
|
__vsc_custom_PS1="\[$(__vsc_prompt_start)\]$__vsc_original_PS1\[$(__vsc_prompt_end)\]"
|
||||||
|
PS1="$__vsc_custom_PS1"
|
||||||
|
fi
|
||||||
|
if [[ "$__vsc_custom_PS2" == "" || "$__vsc_custom_PS2" != "$PS2" ]]; then
|
||||||
|
__vsc_original_PS2=$PS2
|
||||||
|
__vsc_custom_PS2="\[$(__vsc_continuation_start)\]$__vsc_original_PS2\[$(__vsc_continuation_end)\]"
|
||||||
|
PS2="$__vsc_custom_PS2"
|
||||||
|
fi
|
||||||
|
__vsc_in_command_execution="0"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_precmd() {
|
||||||
|
__vsc_command_complete "$__vsc_status"
|
||||||
|
__vsc_current_command=""
|
||||||
|
__vsc_update_prompt
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_preexec() {
|
||||||
|
__vsc_initialized=1
|
||||||
|
if [[ ! "$BASH_COMMAND" =~ ^__vsc_prompt* ]]; then
|
||||||
|
# Use history if it's available to verify the command as BASH_COMMAND comes in with aliases
|
||||||
|
# resolved
|
||||||
|
if [ "$__vsc_history_verify" = "1" ]; then
|
||||||
|
__vsc_current_command="$(builtin history 1 | sed 's/ *[0-9]* *//')"
|
||||||
|
else
|
||||||
|
__vsc_current_command=$BASH_COMMAND
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
__vsc_current_command=""
|
||||||
|
fi
|
||||||
|
__vsc_command_output_start
|
||||||
|
}
|
||||||
|
|
||||||
|
# Debug trapping/preexec inspired by starship (ISC)
|
||||||
|
if [[ -n "${bash_preexec_imported:-}" ]]; then
|
||||||
|
__vsc_preexec_only() {
|
||||||
|
if [ "$__vsc_in_command_execution" = "0" ]; then
|
||||||
|
__vsc_in_command_execution="1"
|
||||||
|
__vsc_preexec
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
precmd_functions+=(__vsc_prompt_cmd)
|
||||||
|
preexec_functions+=(__vsc_preexec_only)
|
||||||
|
else
|
||||||
|
__vsc_dbg_trap="$(__vsc_get_trap DEBUG)"
|
||||||
|
|
||||||
|
if [[ -z "$__vsc_dbg_trap" ]]; then
|
||||||
|
__vsc_preexec_only() {
|
||||||
|
if [ "$__vsc_in_command_execution" = "0" ]; then
|
||||||
|
__vsc_in_command_execution="1"
|
||||||
|
__vsc_preexec
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap '__vsc_preexec_only "$_"' DEBUG
|
||||||
|
elif [[ "$__vsc_dbg_trap" != '__vsc_preexec "$_"' && "$__vsc_dbg_trap" != '__vsc_preexec_all "$_"' ]]; then
|
||||||
|
__vsc_preexec_all() {
|
||||||
|
if [ "$__vsc_in_command_execution" = "0" ]; then
|
||||||
|
__vsc_in_command_execution="1"
|
||||||
|
builtin eval "${__vsc_dbg_trap}"
|
||||||
|
__vsc_preexec
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap '__vsc_preexec_all "$_"' DEBUG
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
__vsc_update_prompt
|
||||||
|
|
||||||
|
__vsc_restore_exit_code() {
|
||||||
|
return "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_prompt_cmd_original() {
|
||||||
|
__vsc_status="$?"
|
||||||
|
__vsc_restore_exit_code "${__vsc_status}"
|
||||||
|
# Evaluate the original PROMPT_COMMAND similarly to how bash would normally
|
||||||
|
# See https://unix.stackexchange.com/a/672843 for technique
|
||||||
|
for cmd in "${__vsc_original_prompt_command[@]}"; do
|
||||||
|
eval "${cmd:-}"
|
||||||
|
done
|
||||||
|
__vsc_precmd
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_prompt_cmd() {
|
||||||
|
__vsc_status="$?"
|
||||||
|
__vsc_precmd
|
||||||
|
}
|
||||||
|
|
||||||
|
# PROMPT_COMMAND arrays and strings seem to be handled the same (handling only the first entry of
|
||||||
|
# the array?)
|
||||||
|
__vsc_original_prompt_command=$PROMPT_COMMAND
|
||||||
|
|
||||||
|
if [[ -z "${bash_preexec_imported:-}" ]]; then
|
||||||
|
if [[ -n "$__vsc_original_prompt_command" && "$__vsc_original_prompt_command" != "__vsc_prompt_cmd" ]]; then
|
||||||
|
PROMPT_COMMAND=__vsc_prompt_cmd_original
|
||||||
|
else
|
||||||
|
PROMPT_COMMAND=__vsc_prompt_cmd
|
||||||
|
fi
|
||||||
|
fi
|
@@ -0,0 +1,15 @@
|
|||||||
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
if [[ -f $USER_ZDOTDIR/.zshenv ]]; then
|
||||||
|
VSCODE_ZDOTDIR=$ZDOTDIR
|
||||||
|
ZDOTDIR=$USER_ZDOTDIR
|
||||||
|
|
||||||
|
# prevent recursion
|
||||||
|
if [[ $USER_ZDOTDIR != $VSCODE_ZDOTDIR ]]; then
|
||||||
|
. $USER_ZDOTDIR/.zshenv
|
||||||
|
fi
|
||||||
|
|
||||||
|
USER_ZDOTDIR=$ZDOTDIR
|
||||||
|
ZDOTDIR=$VSCODE_ZDOTDIR
|
||||||
|
fi
|
@@ -0,0 +1,7 @@
|
|||||||
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
ZDOTDIR=$USER_ZDOTDIR
|
||||||
|
if [[ $options[norcs] = off && -o "login" && -f $ZDOTDIR/.zlogin ]]; then
|
||||||
|
. $ZDOTDIR/.zlogin
|
||||||
|
fi
|
@@ -0,0 +1,15 @@
|
|||||||
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
if [[ $options[norcs] = off && -o "login" && -f $USER_ZDOTDIR/.zprofile ]]; then
|
||||||
|
VSCODE_ZDOTDIR=$ZDOTDIR
|
||||||
|
ZDOTDIR=$USER_ZDOTDIR
|
||||||
|
. $USER_ZDOTDIR/.zprofile
|
||||||
|
ZDOTDIR=$VSCODE_ZDOTDIR
|
||||||
|
|
||||||
|
# Apply any explicit path prefix (see #99878)
|
||||||
|
if (( ${+VSCODE_PATH_PREFIX} )); then
|
||||||
|
export PATH=$VSCODE_PATH_PREFIX$PATH
|
||||||
|
fi
|
||||||
|
builtin unset VSCODE_PATH_PREFIX
|
||||||
|
fi
|
160
src/plugins/terminal/shellintegrations/shellintegration-rc.zsh
Normal file
160
src/plugins/terminal/shellintegrations/shellintegration-rc.zsh
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
builtin autoload -Uz add-zsh-hook
|
||||||
|
|
||||||
|
# Prevent the script recursing when setting up
|
||||||
|
if [ -n "$VSCODE_SHELL_INTEGRATION" ]; then
|
||||||
|
ZDOTDIR=$USER_ZDOTDIR
|
||||||
|
builtin return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# This variable allows the shell to both detect that VS Code's shell integration is enabled as well
|
||||||
|
# as disable it by unsetting the variable.
|
||||||
|
VSCODE_SHELL_INTEGRATION=1
|
||||||
|
|
||||||
|
# By default, zsh will set the $HISTFILE to the $ZDOTDIR location automatically. In the case of the
|
||||||
|
# shell integration being injected, this means that the terminal will use a different history file
|
||||||
|
# to other terminals. To fix this issue, set $HISTFILE back to the default location before ~/.zshrc
|
||||||
|
# is called as that may depend upon the value.
|
||||||
|
if [[ "$VSCODE_INJECTION" == "1" ]]; then
|
||||||
|
HISTFILE=$USER_ZDOTDIR/.zsh_history
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Only fix up ZDOTDIR if shell integration was injected (not manually installed) and has not been called yet
|
||||||
|
if [[ "$VSCODE_INJECTION" == "1" ]]; then
|
||||||
|
if [[ $options[norcs] = off && -f $USER_ZDOTDIR/.zshrc ]]; then
|
||||||
|
VSCODE_ZDOTDIR=$ZDOTDIR
|
||||||
|
ZDOTDIR=$USER_ZDOTDIR
|
||||||
|
# A user's custom HISTFILE location might be set when their .zshrc file is sourced below
|
||||||
|
. $USER_ZDOTDIR/.zshrc
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Shell integration was disabled by the shell, exit without warning assuming either the shell has
|
||||||
|
# explicitly disabled shell integration as it's incompatible or it implements the protocol.
|
||||||
|
if [ -z "$VSCODE_SHELL_INTEGRATION" ]; then
|
||||||
|
builtin return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The property (P) and command (E) codes embed values which require escaping.
|
||||||
|
# Backslashes are doubled. Non-alphanumeric characters are converted to escaped hex.
|
||||||
|
__vsc_escape_value() {
|
||||||
|
builtin emulate -L zsh
|
||||||
|
|
||||||
|
# Process text byte by byte, not by codepoint.
|
||||||
|
builtin local LC_ALL=C str="$1" i byte token out=''
|
||||||
|
|
||||||
|
for (( i = 0; i < ${#str}; ++i )); do
|
||||||
|
byte="${str:$i:1}"
|
||||||
|
|
||||||
|
# Escape backslashes and semi-colons
|
||||||
|
if [ "$byte" = "\\" ]; then
|
||||||
|
token="\\\\"
|
||||||
|
elif [ "$byte" = ";" ]; then
|
||||||
|
token="\\x3b"
|
||||||
|
else
|
||||||
|
token="$byte"
|
||||||
|
fi
|
||||||
|
|
||||||
|
out+="$token"
|
||||||
|
done
|
||||||
|
|
||||||
|
builtin print -r "$out"
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_in_command_execution="1"
|
||||||
|
__vsc_current_command=""
|
||||||
|
|
||||||
|
__vsc_prompt_start() {
|
||||||
|
builtin printf '\e]633;A\a'
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_prompt_end() {
|
||||||
|
builtin printf '\e]633;B\a'
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_update_cwd() {
|
||||||
|
builtin printf '\e]633;P;Cwd=%s\a' "$(__vsc_escape_value "${PWD}")"
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_command_output_start() {
|
||||||
|
builtin printf '\e]633;C\a'
|
||||||
|
builtin printf '\e]633;E;%s\a' "${__vsc_current_command}"
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_continuation_start() {
|
||||||
|
builtin printf '\e]633;F\a'
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_continuation_end() {
|
||||||
|
builtin printf '\e]633;G\a'
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_right_prompt_start() {
|
||||||
|
builtin printf '\e]633;H\a'
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_right_prompt_end() {
|
||||||
|
builtin printf '\e]633;I\a'
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_command_complete() {
|
||||||
|
if [[ "$__vsc_current_command" == "" ]]; then
|
||||||
|
builtin printf '\e]633;D\a'
|
||||||
|
else
|
||||||
|
builtin printf '\e]633;D;%s\a' "$__vsc_status"
|
||||||
|
fi
|
||||||
|
__vsc_update_cwd
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ -o NOUNSET ]]; then
|
||||||
|
if [ -z "${RPROMPT-}" ]; then
|
||||||
|
RPROMPT=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
__vsc_update_prompt() {
|
||||||
|
__vsc_prior_prompt="$PS1"
|
||||||
|
__vsc_prior_prompt2="$PS2"
|
||||||
|
__vsc_in_command_execution=""
|
||||||
|
PS1="%{$(__vsc_prompt_start)%}$PS1%{$(__vsc_prompt_end)%}"
|
||||||
|
PS2="%{$(__vsc_continuation_start)%}$PS2%{$(__vsc_continuation_end)%}"
|
||||||
|
if [ -n "$RPROMPT" ]; then
|
||||||
|
__vsc_prior_rprompt="$RPROMPT"
|
||||||
|
RPROMPT="%{$(__vsc_right_prompt_start)%}$RPROMPT%{$(__vsc_right_prompt_end)%}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_precmd() {
|
||||||
|
local __vsc_status="$?"
|
||||||
|
if [ -z "${__vsc_in_command_execution-}" ]; then
|
||||||
|
# not in command execution
|
||||||
|
__vsc_command_output_start
|
||||||
|
fi
|
||||||
|
|
||||||
|
__vsc_command_complete "$__vsc_status"
|
||||||
|
__vsc_current_command=""
|
||||||
|
|
||||||
|
# in command execution
|
||||||
|
if [ -n "$__vsc_in_command_execution" ]; then
|
||||||
|
# non null
|
||||||
|
__vsc_update_prompt
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
__vsc_preexec() {
|
||||||
|
PS1="$__vsc_prior_prompt"
|
||||||
|
PS2="$__vsc_prior_prompt2"
|
||||||
|
if [ -n "$RPROMPT" ]; then
|
||||||
|
RPROMPT="$__vsc_prior_rprompt"
|
||||||
|
fi
|
||||||
|
__vsc_in_command_execution="1"
|
||||||
|
__vsc_current_command=$2
|
||||||
|
__vsc_command_output_start
|
||||||
|
}
|
||||||
|
add-zsh-hook precmd __vsc_precmd
|
||||||
|
add-zsh-hook preexec __vsc_preexec
|
||||||
|
|
||||||
|
if [[ $options[login] = off && $USER_ZDOTDIR != $VSCODE_ZDOTDIR ]]; then
|
||||||
|
ZDOTDIR=$USER_ZDOTDIR
|
||||||
|
fi
|
122
src/plugins/terminal/shellintegrations/shellintegration.fish
Normal file
122
src/plugins/terminal/shellintegrations/shellintegration.fish
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# Visual Studio Code terminal integration for fish
|
||||||
|
#
|
||||||
|
# Manual installation:
|
||||||
|
#
|
||||||
|
# (1) Add the following to the end of `$__fish_config_dir/config.fish`:
|
||||||
|
#
|
||||||
|
# string match -q "$TERM_PROGRAM" "vscode"
|
||||||
|
# and . (code --locate-shell-integration-path fish)
|
||||||
|
#
|
||||||
|
# (2) Restart fish.
|
||||||
|
|
||||||
|
# Don't run in scripts, other terminals, or more than once per session.
|
||||||
|
status is-interactive
|
||||||
|
and string match --quiet "$TERM_PROGRAM" "vscode"
|
||||||
|
and ! set --query VSCODE_SHELL_INTEGRATION
|
||||||
|
or exit
|
||||||
|
|
||||||
|
set --global VSCODE_SHELL_INTEGRATION 1
|
||||||
|
|
||||||
|
# Apply any explicit path prefix (see #99878)
|
||||||
|
if status --is-login; and set -q VSCODE_PATH_PREFIX
|
||||||
|
fish_add_path -p $VSCODE_PATH_PREFIX
|
||||||
|
end
|
||||||
|
set -e VSCODE_PATH_PREFIX
|
||||||
|
|
||||||
|
# Helper function
|
||||||
|
function __vsc_esc -d "Emit escape sequences for VS Code shell integration"
|
||||||
|
builtin printf "\e]633;%s\a" (string join ";" $argv)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sent right before executing an interactive command.
|
||||||
|
# Marks the beginning of command output.
|
||||||
|
function __vsc_cmd_executed --on-event fish_preexec
|
||||||
|
__vsc_esc C
|
||||||
|
__vsc_esc E (__vsc_escape_value "$argv")
|
||||||
|
|
||||||
|
# Creates a marker to indicate a command was run.
|
||||||
|
set --global _vsc_has_cmd
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Escape a value for use in the 'P' ("Property") or 'E' ("Command Line") sequences.
|
||||||
|
# Backslashes are doubled and non-alphanumeric characters are hex encoded.
|
||||||
|
function __vsc_escape_value
|
||||||
|
# Escape backslashes and semi-colons
|
||||||
|
echo $argv \
|
||||||
|
| string replace --all '\\' '\\\\' \
|
||||||
|
| string replace --all ';' '\\x3b' \
|
||||||
|
;
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sent right after an interactive command has finished executing.
|
||||||
|
# Marks the end of command output.
|
||||||
|
function __vsc_cmd_finished --on-event fish_postexec
|
||||||
|
__vsc_esc D $status
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sent when a command line is cleared or reset, but no command was run.
|
||||||
|
# Marks the cleared line with neither success nor failure.
|
||||||
|
function __vsc_cmd_clear --on-event fish_cancel
|
||||||
|
__vsc_esc D
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sent whenever a new fish prompt is about to be displayed.
|
||||||
|
# Updates the current working directory.
|
||||||
|
function __vsc_update_cwd --on-event fish_prompt
|
||||||
|
__vsc_esc P Cwd=(__vsc_escape_value "$PWD")
|
||||||
|
|
||||||
|
# If a command marker exists, remove it.
|
||||||
|
# Otherwise, the commandline is empty and no command was run.
|
||||||
|
if set --query _vsc_has_cmd
|
||||||
|
set --erase _vsc_has_cmd
|
||||||
|
else
|
||||||
|
__vsc_cmd_clear
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sent at the start of the prompt.
|
||||||
|
# Marks the beginning of the prompt (and, implicitly, a new line).
|
||||||
|
function __vsc_fish_prompt_start
|
||||||
|
__vsc_esc A
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sent at the end of the prompt.
|
||||||
|
# Marks the beginning of the user's command input.
|
||||||
|
function __vsc_fish_cmd_start
|
||||||
|
__vsc_esc B
|
||||||
|
end
|
||||||
|
|
||||||
|
function __vsc_fish_has_mode_prompt -d "Returns true if fish_mode_prompt is defined and not empty"
|
||||||
|
functions fish_mode_prompt | string match -rvq '^ *(#|function |end$|$)'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Preserve the user's existing prompt, to wrap in our escape sequences.
|
||||||
|
functions --copy fish_prompt __vsc_fish_prompt
|
||||||
|
|
||||||
|
# Preserve and wrap fish_mode_prompt (which appears to the left of the regular
|
||||||
|
# prompt), but only if it's not defined as an empty function (which is the
|
||||||
|
# officially documented way to disable that feature).
|
||||||
|
if __vsc_fish_has_mode_prompt
|
||||||
|
functions --copy fish_mode_prompt __vsc_fish_mode_prompt
|
||||||
|
|
||||||
|
function fish_mode_prompt
|
||||||
|
__vsc_fish_prompt_start
|
||||||
|
__vsc_fish_mode_prompt
|
||||||
|
end
|
||||||
|
|
||||||
|
function fish_prompt
|
||||||
|
__vsc_fish_prompt
|
||||||
|
__vsc_fish_cmd_start
|
||||||
|
end
|
||||||
|
else
|
||||||
|
# No fish_mode_prompt, so put everything in fish_prompt.
|
||||||
|
function fish_prompt
|
||||||
|
__vsc_fish_prompt_start
|
||||||
|
__vsc_fish_prompt
|
||||||
|
__vsc_fish_cmd_start
|
||||||
|
end
|
||||||
|
end
|
158
src/plugins/terminal/shellintegrations/shellintegration.ps1
Normal file
158
src/plugins/terminal/shellintegrations/shellintegration.ps1
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
# Prevent installing more than once per session
|
||||||
|
if (Test-Path variable:global:__VSCodeOriginalPrompt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Disable shell integration when the language mode is restricted
|
||||||
|
if ($ExecutionContext.SessionState.LanguageMode -ne "FullLanguage") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$Global:__VSCodeOriginalPrompt = $function:Prompt
|
||||||
|
|
||||||
|
$Global:__LastHistoryId = -1
|
||||||
|
|
||||||
|
function Global:__VSCode-Escape-Value([string]$value) {
|
||||||
|
# NOTE: In PowerShell v6.1+, this can be written `$value -replace '…', { … }` instead of `[regex]::Replace`.
|
||||||
|
# Replace any non-alphanumeric characters.
|
||||||
|
[regex]::Replace($value, '[\\\n;]', { param($match)
|
||||||
|
# Encode the (ascii) matches as `\x<hex>`
|
||||||
|
-Join (
|
||||||
|
[System.Text.Encoding]::UTF8.GetBytes($match.Value) | ForEach-Object { '\x{0:x2}' -f $_ }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Global:Prompt() {
|
||||||
|
$FakeCode = [int]!$global:?
|
||||||
|
# NOTE: We disable strict mode for the scope of this function because it unhelpfully throws an
|
||||||
|
# error when $LastHistoryEntry is null, and is not otherwise useful.
|
||||||
|
Set-StrictMode -Off
|
||||||
|
$LastHistoryEntry = Get-History -Count 1
|
||||||
|
# Skip finishing the command if the first command has not yet started
|
||||||
|
if ($Global:__LastHistoryId -ne -1) {
|
||||||
|
if ($LastHistoryEntry.Id -eq $Global:__LastHistoryId) {
|
||||||
|
# Don't provide a command line or exit code if there was no history entry (eg. ctrl+c, enter on no command)
|
||||||
|
$Result = "$([char]0x1b)]633;E`a"
|
||||||
|
$Result += "$([char]0x1b)]633;D`a"
|
||||||
|
} else {
|
||||||
|
# Command finished command line
|
||||||
|
# OSC 633 ; A ; <CommandLine?> ST
|
||||||
|
$Result = "$([char]0x1b)]633;E;"
|
||||||
|
# Sanitize the command line to ensure it can get transferred to the terminal and can be parsed
|
||||||
|
# correctly. This isn't entirely safe but good for most cases, it's important for the Pt parameter
|
||||||
|
# to only be composed of _printable_ characters as per the spec.
|
||||||
|
if ($LastHistoryEntry.CommandLine) {
|
||||||
|
$CommandLine = $LastHistoryEntry.CommandLine
|
||||||
|
} else {
|
||||||
|
$CommandLine = ""
|
||||||
|
}
|
||||||
|
$Result += $(__VSCode-Escape-Value $CommandLine)
|
||||||
|
$Result += "`a"
|
||||||
|
# Command finished exit code
|
||||||
|
# OSC 633 ; D [; <ExitCode>] ST
|
||||||
|
$Result += "$([char]0x1b)]633;D;$FakeCode`a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# Prompt started
|
||||||
|
# OSC 633 ; A ST
|
||||||
|
$Result += "$([char]0x1b)]633;A`a"
|
||||||
|
# Current working directory
|
||||||
|
# OSC 633 ; <Property>=<Value> ST
|
||||||
|
$Result += if($pwd.Provider.Name -eq 'FileSystem'){"$([char]0x1b)]633;P;Cwd=$(__VSCode-Escape-Value $pwd.ProviderPath)`a"}
|
||||||
|
# Before running the original prompt, put $? back to what it was:
|
||||||
|
if ($FakeCode -ne 0) {
|
||||||
|
Write-Error "failure" -ea ignore
|
||||||
|
}
|
||||||
|
# Run the original prompt
|
||||||
|
$Result += $Global:__VSCodeOriginalPrompt.Invoke()
|
||||||
|
# Write command started
|
||||||
|
$Result += "$([char]0x1b)]633;B`a"
|
||||||
|
$Global:__LastHistoryId = $LastHistoryEntry.Id
|
||||||
|
return $Result
|
||||||
|
}
|
||||||
|
|
||||||
|
# Only send the command executed sequence when PSReadLine is loaded, if not shell integration should
|
||||||
|
# still work thanks to the command line sequence
|
||||||
|
if (Get-Module -Name PSReadLine) {
|
||||||
|
$__VSCodeOriginalPSConsoleHostReadLine = $function:PSConsoleHostReadLine
|
||||||
|
function Global:PSConsoleHostReadLine {
|
||||||
|
$tmp = $__VSCodeOriginalPSConsoleHostReadLine.Invoke()
|
||||||
|
# Write command executed sequence directly to Console to avoid the new line from Write-Host
|
||||||
|
[Console]::Write("$([char]0x1b)]633;C`a")
|
||||||
|
$tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set IsWindows property
|
||||||
|
[Console]::Write("$([char]0x1b)]633;P;IsWindows=$($IsWindows)`a")
|
||||||
|
|
||||||
|
# Set always on key handlers which map to default VS Code keybindings
|
||||||
|
function Set-MappedKeyHandler {
|
||||||
|
param ([string[]] $Chord, [string[]]$Sequence)
|
||||||
|
try {
|
||||||
|
$Handler = Get-PSReadLineKeyHandler -Chord $Chord | Select-Object -First 1
|
||||||
|
} catch [System.Management.Automation.ParameterBindingException] {
|
||||||
|
# PowerShell 5.1 ships with PSReadLine 2.0.0 which does not have -Chord,
|
||||||
|
# so we check what's bound and filter it.
|
||||||
|
$Handler = Get-PSReadLineKeyHandler -Bound | Where-Object -FilterScript { $_.Key -eq $Chord } | Select-Object -First 1
|
||||||
|
}
|
||||||
|
if ($Handler) {
|
||||||
|
Set-PSReadLineKeyHandler -Chord $Sequence -Function $Handler.Function
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Set-MappedKeyHandlers {
|
||||||
|
Set-MappedKeyHandler -Chord Ctrl+Spacebar -Sequence 'F12,a'
|
||||||
|
Set-MappedKeyHandler -Chord Alt+Spacebar -Sequence 'F12,b'
|
||||||
|
Set-MappedKeyHandler -Chord Shift+Enter -Sequence 'F12,c'
|
||||||
|
Set-MappedKeyHandler -Chord Shift+End -Sequence 'F12,d'
|
||||||
|
|
||||||
|
# Conditionally enable suggestions
|
||||||
|
if ($env:VSCODE_SUGGEST -eq '1') {
|
||||||
|
Remove-Item Env:VSCODE_SUGGEST
|
||||||
|
|
||||||
|
# VS Code send completions request (may override Ctrl+Spacebar)
|
||||||
|
Set-PSReadLineKeyHandler -Chord 'F12,e' -ScriptBlock {
|
||||||
|
Send-Completions
|
||||||
|
}
|
||||||
|
|
||||||
|
# Suggest trigger characters
|
||||||
|
Set-PSReadLineKeyHandler -Chord "-" -ScriptBlock {
|
||||||
|
[Microsoft.PowerShell.PSConsoleReadLine]::Insert("-")
|
||||||
|
Send-Completions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Send-Completions {
|
||||||
|
$commandLine = ""
|
||||||
|
$cursorIndex = 0
|
||||||
|
# TODO: Since fuzzy matching exists, should completions be provided only for character after the
|
||||||
|
# last space and then filter on the client side? That would let you trigger ctrl+space
|
||||||
|
# anywhere on a word and have full completions available
|
||||||
|
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$commandLine, [ref]$cursorIndex)
|
||||||
|
$completionPrefix = $commandLine
|
||||||
|
|
||||||
|
# Get completions
|
||||||
|
$result = "`e]633;Completions"
|
||||||
|
if ($completionPrefix.Length -gt 0) {
|
||||||
|
# Get and send completions
|
||||||
|
$completions = TabExpansion2 -inputScript $completionPrefix -cursorColumn $cursorIndex
|
||||||
|
if ($null -ne $completions.CompletionMatches) {
|
||||||
|
$result += ";$($completions.ReplacementIndex);$($completions.ReplacementLength);$($cursorIndex);"
|
||||||
|
$result += $completions.CompletionMatches | ConvertTo-Json -Compress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$result += "`a"
|
||||||
|
|
||||||
|
Write-Host -NoNewLine $result
|
||||||
|
}
|
||||||
|
|
||||||
|
# Register key handlers if PSReadLine is available
|
||||||
|
if (Get-Module -Name PSReadLine) {
|
||||||
|
Set-MappedKeyHandlers
|
||||||
|
}
|
@@ -1,6 +1,13 @@
|
|||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/terminal">
|
<qresource prefix="/terminal">
|
||||||
<file>images/settingscategory_terminal.png</file>
|
<file>images/settingscategory_terminal.png</file>
|
||||||
<file>images/settingscategory_terminal@2x.png</file>
|
<file>images/settingscategory_terminal@2x.png</file>
|
||||||
</qresource>
|
<file>shellintegrations/shellintegration-bash.sh</file>
|
||||||
|
<file>shellintegrations/shellintegration-env.zsh</file>
|
||||||
|
<file>shellintegrations/shellintegration-login.zsh</file>
|
||||||
|
<file>shellintegrations/shellintegration-profile.zsh</file>
|
||||||
|
<file>shellintegrations/shellintegration-rc.zsh</file>
|
||||||
|
<file>shellintegrations/shellintegration.fish</file>
|
||||||
|
<file>shellintegrations/shellintegration.ps1</file>
|
||||||
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
@@ -188,13 +188,29 @@ void TerminalPane::setupTerminalWidget(TerminalWidget *terminal)
|
|||||||
|
|
||||||
auto setTabText = [this](TerminalWidget * terminal) {
|
auto setTabText = [this](TerminalWidget * terminal) {
|
||||||
auto index = m_tabWidget->indexOf(terminal);
|
auto index = m_tabWidget->indexOf(terminal);
|
||||||
m_tabWidget->setTabText(index, terminal->shellName());
|
const FilePath cwd = terminal->cwd();
|
||||||
|
|
||||||
|
const QString exe = terminal->currentCommand().isEmpty() ? terminal->shellName()
|
||||||
|
: terminal->currentCommand().executable().fileName();
|
||||||
|
|
||||||
|
if (cwd.isEmpty())
|
||||||
|
m_tabWidget->setTabText(index, exe);
|
||||||
|
else
|
||||||
|
m_tabWidget->setTabText(index, exe + " - " + cwd.fileName());
|
||||||
};
|
};
|
||||||
|
|
||||||
connect(terminal, &TerminalWidget::started, [setTabText, terminal](qint64 /*pid*/) {
|
connect(terminal, &TerminalWidget::started, [setTabText, terminal](qint64 /*pid*/) {
|
||||||
setTabText(terminal);
|
setTabText(terminal);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(terminal, &TerminalWidget::cwdChanged, [setTabText, terminal]() {
|
||||||
|
setTabText(terminal);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(terminal, &TerminalWidget::commandChanged, [setTabText, terminal]() {
|
||||||
|
setTabText(terminal);
|
||||||
|
});
|
||||||
|
|
||||||
if (!terminal->shellName().isEmpty())
|
if (!terminal->shellName().isEmpty())
|
||||||
setTabText(terminal);
|
setTabText(terminal);
|
||||||
|
|
||||||
|
@@ -23,10 +23,13 @@ QColor toQColor(const VTermColor &c)
|
|||||||
|
|
||||||
struct TerminalSurfacePrivate
|
struct TerminalSurfacePrivate
|
||||||
{
|
{
|
||||||
TerminalSurfacePrivate(TerminalSurface *surface, const QSize &initialGridSize)
|
TerminalSurfacePrivate(TerminalSurface *surface,
|
||||||
|
const QSize &initialGridSize,
|
||||||
|
ShellIntegration *shellIntegration)
|
||||||
: m_vterm(vterm_new(initialGridSize.height(), initialGridSize.width()), vterm_free)
|
: m_vterm(vterm_new(initialGridSize.height(), initialGridSize.width()), vterm_free)
|
||||||
, m_vtermScreen(vterm_obtain_screen(m_vterm.get()))
|
, m_vtermScreen(vterm_obtain_screen(m_vterm.get()))
|
||||||
, m_scrollback(std::make_unique<Internal::Scrollback>(5000))
|
, m_scrollback(std::make_unique<Internal::Scrollback>(5000))
|
||||||
|
, m_shellIntegration(shellIntegration)
|
||||||
, q(surface)
|
, q(surface)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@@ -75,7 +78,15 @@ struct TerminalSurfacePrivate
|
|||||||
vterm_screen_set_damage_merge(m_vtermScreen, VTERM_DAMAGE_SCROLL);
|
vterm_screen_set_damage_merge(m_vtermScreen, VTERM_DAMAGE_SCROLL);
|
||||||
vterm_screen_enable_altscreen(m_vtermScreen, true);
|
vterm_screen_enable_altscreen(m_vtermScreen, true);
|
||||||
|
|
||||||
|
memset(&m_vtermStateFallbacks, 0, sizeof(m_vtermStateFallbacks));
|
||||||
|
|
||||||
|
m_vtermStateFallbacks.osc = [](int cmd, VTermStringFragment fragment, void *user) {
|
||||||
|
auto p = static_cast<TerminalSurfacePrivate *>(user);
|
||||||
|
return p->osc(cmd, fragment);
|
||||||
|
};
|
||||||
|
|
||||||
VTermState *vts = vterm_obtain_state(m_vterm.get());
|
VTermState *vts = vterm_obtain_state(m_vterm.get());
|
||||||
|
vterm_state_set_unrecognised_fallbacks(vts, &m_vtermStateFallbacks, this);
|
||||||
vterm_state_set_bold_highbright(vts, true);
|
vterm_state_set_bold_highbright(vts, true);
|
||||||
|
|
||||||
vterm_screen_reset(m_vtermScreen, 1);
|
vterm_screen_reset(m_vtermScreen, 1);
|
||||||
@@ -196,6 +207,14 @@ struct TerminalSurfacePrivate
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int osc(int cmd, const VTermStringFragment &fragment)
|
||||||
|
{
|
||||||
|
if (m_shellIntegration)
|
||||||
|
m_shellIntegration->onOsc(cmd, fragment);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int setTerminalProperties(VTermProp prop, VTermValue *val)
|
int setTerminalProperties(VTermProp prop, VTermValue *val)
|
||||||
{
|
{
|
||||||
switch (prop) {
|
switch (prop) {
|
||||||
@@ -274,19 +293,23 @@ struct TerminalSurfacePrivate
|
|||||||
std::unique_ptr<VTerm, void (*)(VTerm *)> m_vterm;
|
std::unique_ptr<VTerm, void (*)(VTerm *)> m_vterm;
|
||||||
VTermScreen *m_vtermScreen;
|
VTermScreen *m_vtermScreen;
|
||||||
VTermScreenCallbacks m_vtermScreenCallbacks;
|
VTermScreenCallbacks m_vtermScreenCallbacks;
|
||||||
|
VTermStateFallbacks m_vtermStateFallbacks;
|
||||||
|
|
||||||
QColor m_defaultBgColor;
|
QColor m_defaultBgColor;
|
||||||
Cursor m_cursor;
|
Cursor m_cursor;
|
||||||
|
QString m_currentCommand;
|
||||||
|
|
||||||
bool m_altscreen{false};
|
bool m_altscreen{false};
|
||||||
|
|
||||||
std::unique_ptr<Internal::Scrollback> m_scrollback;
|
std::unique_ptr<Internal::Scrollback> m_scrollback;
|
||||||
|
|
||||||
|
ShellIntegration *m_shellIntegration{nullptr};
|
||||||
|
|
||||||
TerminalSurface *q;
|
TerminalSurface *q;
|
||||||
};
|
};
|
||||||
|
|
||||||
TerminalSurface::TerminalSurface(QSize initialGridSize)
|
TerminalSurface::TerminalSurface(QSize initialGridSize, ShellIntegration *shellIntegration)
|
||||||
: d(std::make_unique<TerminalSurfacePrivate>(this, initialGridSize))
|
: d(std::make_unique<TerminalSurfacePrivate>(this, initialGridSize, shellIntegration))
|
||||||
{
|
{
|
||||||
d->init();
|
d->init();
|
||||||
}
|
}
|
||||||
@@ -478,6 +501,11 @@ QColor TerminalSurface::defaultBgColor() const
|
|||||||
return toQColor(d->defaultBgColor());
|
return toQColor(d->defaultBgColor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShellIntegration *TerminalSurface::shellIntegration() const
|
||||||
|
{
|
||||||
|
return d->m_shellIntegration;
|
||||||
|
}
|
||||||
|
|
||||||
CellIterator TerminalSurface::begin() const
|
CellIterator TerminalSurface::begin() const
|
||||||
{
|
{
|
||||||
auto res = CellIterator(this, {0, 0});
|
auto res = CellIterator(this, {0, 0});
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "celliterator.h"
|
#include "celliterator.h"
|
||||||
|
#include "shellintegration.h"
|
||||||
|
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
#include <QSize>
|
#include <QSize>
|
||||||
@@ -47,7 +48,7 @@ class TerminalSurface : public QObject
|
|||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TerminalSurface(QSize initialGridSize);
|
TerminalSurface(QSize initialGridSize, ShellIntegration *shellIntegration);
|
||||||
~TerminalSurface();
|
~TerminalSurface();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -95,6 +96,8 @@ public:
|
|||||||
|
|
||||||
QColor defaultBgColor() const;
|
QColor defaultBgColor() const;
|
||||||
|
|
||||||
|
ShellIntegration *shellIntegration() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void writeToPty(const QByteArray &data);
|
void writeToPty(const QByteArray &data);
|
||||||
void invalidated(QRect grid);
|
void invalidated(QRect grid);
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/environment.h>
|
#include <utils/environment.h>
|
||||||
|
#include <utils/fileutils.h>
|
||||||
#include <utils/hostosinfo.h>
|
#include <utils/hostosinfo.h>
|
||||||
#include <utils/processinterface.h>
|
#include <utils/processinterface.h>
|
||||||
#include <utils/stringutils.h>
|
#include <utils/stringutils.h>
|
||||||
@@ -132,6 +133,10 @@ void TerminalWidget::setupPty()
|
|||||||
m_process->setWorkingDirectory(*m_openParameters.workingDirectory);
|
m_process->setWorkingDirectory(*m_openParameters.workingDirectory);
|
||||||
m_process->setEnvironment(env);
|
m_process->setEnvironment(env);
|
||||||
|
|
||||||
|
if (m_surface->shellIntegration()) {
|
||||||
|
m_surface->shellIntegration()->prepareProcess(*m_process.get());
|
||||||
|
}
|
||||||
|
|
||||||
connect(m_process.get(), &QtcProcess::readyReadStandardOutput, this, [this]() {
|
connect(m_process.get(), &QtcProcess::readyReadStandardOutput, this, [this]() {
|
||||||
onReadyRead(false);
|
onReadyRead(false);
|
||||||
});
|
});
|
||||||
@@ -242,7 +247,8 @@ void TerminalWidget::setupActions()
|
|||||||
connect(&m_zoomInAction, &QAction::triggered, this, &TerminalWidget::zoomIn);
|
connect(&m_zoomInAction, &QAction::triggered, this, &TerminalWidget::zoomIn);
|
||||||
connect(&m_zoomOutAction, &QAction::triggered, this, &TerminalWidget::zoomOut);
|
connect(&m_zoomOutAction, &QAction::triggered, this, &TerminalWidget::zoomOut);
|
||||||
|
|
||||||
addActions({&m_copyAction, &m_pasteAction, &m_clearSelectionAction, &m_zoomInAction, &m_zoomOutAction});
|
addActions(
|
||||||
|
{&m_copyAction, &m_pasteAction, &m_clearSelectionAction, &m_zoomInAction, &m_zoomOutAction});
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerminalWidget::writeToPty(const QByteArray &data)
|
void TerminalWidget::writeToPty(const QByteArray &data)
|
||||||
@@ -253,7 +259,8 @@ void TerminalWidget::writeToPty(const QByteArray &data)
|
|||||||
|
|
||||||
void TerminalWidget::setupSurface()
|
void TerminalWidget::setupSurface()
|
||||||
{
|
{
|
||||||
m_surface = std::make_unique<Internal::TerminalSurface>(QSize{80, 60});
|
m_shellIntegration.reset(new ShellIntegration());
|
||||||
|
m_surface = std::make_unique<Internal::TerminalSurface>(QSize{80, 60}, m_shellIntegration.get());
|
||||||
|
|
||||||
connect(m_surface.get(),
|
connect(m_surface.get(),
|
||||||
&Internal::TerminalSurface::writeToPty,
|
&Internal::TerminalSurface::writeToPty,
|
||||||
@@ -299,6 +306,22 @@ void TerminalWidget::setupSurface()
|
|||||||
connect(m_surface.get(), &Internal::TerminalSurface::unscroll, this, [this] {
|
connect(m_surface.get(), &Internal::TerminalSurface::unscroll, this, [this] {
|
||||||
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
|
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
|
||||||
});
|
});
|
||||||
|
if (m_shellIntegration) {
|
||||||
|
connect(m_shellIntegration.get(),
|
||||||
|
&ShellIntegration::commandChanged,
|
||||||
|
this,
|
||||||
|
[this](const CommandLine &command) {
|
||||||
|
m_currentCommand = command;
|
||||||
|
emit commandChanged(m_currentCommand);
|
||||||
|
});
|
||||||
|
connect(m_shellIntegration.get(),
|
||||||
|
&ShellIntegration::currentDirChanged,
|
||||||
|
this,
|
||||||
|
[this](const QString ¤tDir) {
|
||||||
|
m_cwd = FilePath::fromUserInput(currentDir);
|
||||||
|
emit cwdChanged(m_cwd);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerminalWidget::configBlinkTimer()
|
void TerminalWidget::configBlinkTimer()
|
||||||
@@ -470,6 +493,16 @@ QString TerminalWidget::shellName() const
|
|||||||
return m_shellName;
|
return m_shellName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FilePath TerminalWidget::cwd() const
|
||||||
|
{
|
||||||
|
return m_cwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandLine TerminalWidget::currentCommand() const
|
||||||
|
{
|
||||||
|
return m_currentCommand;
|
||||||
|
}
|
||||||
|
|
||||||
QPoint TerminalWidget::viewportToGlobal(QPoint p) const
|
QPoint TerminalWidget::viewportToGlobal(QPoint p) const
|
||||||
{
|
{
|
||||||
int y = p.y() - topMargin();
|
int y = p.y() - topMargin();
|
||||||
|
@@ -72,8 +72,13 @@ public:
|
|||||||
|
|
||||||
QString shellName() const;
|
QString shellName() const;
|
||||||
|
|
||||||
|
Utils::FilePath cwd() const;
|
||||||
|
Utils::CommandLine currentCommand() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void started(qint64 pid);
|
void started(qint64 pid);
|
||||||
|
void cwdChanged(const Utils::FilePath &cwd);
|
||||||
|
void commandChanged(const Utils::CommandLine &cmd);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *event) override;
|
void paintEvent(QPaintEvent *event) override;
|
||||||
@@ -158,6 +163,7 @@ protected:
|
|||||||
private:
|
private:
|
||||||
std::unique_ptr<Utils::QtcProcess> m_process;
|
std::unique_ptr<Utils::QtcProcess> m_process;
|
||||||
std::unique_ptr<Internal::TerminalSurface> m_surface;
|
std::unique_ptr<Internal::TerminalSurface> m_surface;
|
||||||
|
std::unique_ptr<ShellIntegration> m_shellIntegration;
|
||||||
|
|
||||||
QString m_shellName;
|
QString m_shellName;
|
||||||
|
|
||||||
@@ -201,6 +207,9 @@ private:
|
|||||||
Internal::Cursor m_cursor;
|
Internal::Cursor m_cursor;
|
||||||
QTimer m_cursorBlinkTimer;
|
QTimer m_cursorBlinkTimer;
|
||||||
bool m_cursorBlinkState{true};
|
bool m_cursorBlinkState{true};
|
||||||
|
|
||||||
|
Utils::FilePath m_cwd;
|
||||||
|
Utils::CommandLine m_currentCommand;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Terminal
|
} // namespace Terminal
|
||||||
|
70
src/plugins/terminal/tests/integration
Executable file
70
src/plugins/terminal/tests/integration
Executable file
@@ -0,0 +1,70 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "Testing integration response, best start this from a terminal that has no builtin integration"
|
||||||
|
echo "e.g. 'sh'"
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo -e "\033[1m ⎆ Current dir should have changed to '/Some/Dir/Here'\033[0m"
|
||||||
|
printf "\033]7;file:///Some/Dir/Here\033\\"
|
||||||
|
|
||||||
|
read -p " ⎆ Press enter to continue " -n1 -s
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo -e "\033[1m ⎆ Current dir should have changed to '/Some/Other/Dir/Here'\033[0m"
|
||||||
|
printf "\033]1337;CurrentDir=/Some/Other/Dir/Here\033\\"
|
||||||
|
|
||||||
|
read -p " ⎆ Press enter to continue " -n1 -s
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
|
||||||
|
|
||||||
|
echo -e "\033[1m ⎆ Current dir should have changed to '/VSCode/dir/with space'\033[0m"
|
||||||
|
printf "\033]633P;Cwd=/VSCode/dir/with space\033\\"
|
||||||
|
|
||||||
|
read -p " ⎆ Press enter to continue " -n1 -s
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo -e "\033[1m ⎆ The current process should have changed to 'test'\033[0m"
|
||||||
|
printf "\033]633E;test with arguments\033\\"
|
||||||
|
|
||||||
|
read -p " ⎆ Press enter to continue " -n1 -s
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo -e "\033[1m ⎆ The current process should have changed to 'test with space'\033[0m"
|
||||||
|
printf "\033]633E;'test with space'\033\\"
|
||||||
|
|
||||||
|
read -p " ⎆ Press enter to continue " -n1 -s
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo -e "\033[1m ⎆ The current process should have changed to 'test with space v2'\033[0m"
|
||||||
|
printf "\033]633E;\"test with space v2\"\033\\"
|
||||||
|
|
||||||
|
read -p " ⎆ Press enter to continue " -n1 -s
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo -e "\033[1m ⎆ The current process should have changed to 'test with space v3'\033[0m"
|
||||||
|
printf "\033]633E;\"./test/test with space v3\" -argument\033\\"
|
||||||
|
|
||||||
|
read -p " ⎆ Press enter to continue " -n1 -s
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo -e "\033[1m ⎆ The current process should have changed to 'cat'\033[0m"
|
||||||
|
printf "\033]633E;cat /dev/random | base64 -argument\033\\"
|
||||||
|
|
||||||
|
read -p " ⎆ Press enter to continue " -n1 -s
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo -e "\033[1m ⎆ The current process should have changed to 'cat me'\033[0m"
|
||||||
|
printf "\033]633E;cat\\ me args \033\\"
|
||||||
|
|
||||||
|
read -p " ⎆ Press enter to continue " -n1 -s
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
|
Reference in New Issue
Block a user