Skip to content

Commit

Permalink
Merge pull request #211382 from cpendery/fix/regexless-windows-prompt…
Browse files Browse the repository at this point in the history
…-detection

refactor: support dynamic terminal prompt detection without regex on windows
  • Loading branch information
Tyriar authored May 1, 2024
2 parents 9472419 + a0e528d commit 53c1b7b
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ export interface ICommandDetectionCapability {
readonly onCommandInvalidated: Event<ITerminalCommand[]>;
readonly onCurrentCommandInvalidated: Event<ICommandInvalidationRequest>;
setContinuationPrompt(value: string): void;
setPromptTerminator(value: string): void;
setCwd(value: string): void;
setIsWindowsPty(value: boolean): void;
setIsCommandStorageDisabled(): void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe

protected _commands: TerminalCommand[] = [];
private _cwd: string | undefined;
private _promptTerminator: string | undefined;
private _currentCommand: PartialTerminalCommand = new PartialTerminalCommand(this._terminal);
private _commandMarkers: IMarker[] = [];
private _dimensions: ITerminalDimensions;
Expand All @@ -54,6 +55,7 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe
return this._currentCommand;
}
get cwd(): string | undefined { return this._cwd; }
get promptTerminator(): string | undefined { return this._promptTerminator; }
private get _isInputting(): boolean {
return !!(this._currentCommand.commandStartMarker && !this._currentCommand.commandExecutedMarker);
}
Expand Down Expand Up @@ -452,6 +454,11 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe
this._onCommandFinished.fire(newCommand);
}
}

setPromptTerminator(promptTerminator: string) {
this._logService.debug('CommandDetectionCapability#setPromptTerminator', promptTerminator);
this._promptTerminator = promptTerminator;
}
}

/**
Expand Down Expand Up @@ -922,7 +929,6 @@ class WindowsPtyHeuristics extends Disposable {
if (!line) {
return;
}
// TODO: fine tune prompt regex to accomodate for unique configurations.
const lineText = line.translateToString(true);
if (!lineText) {
return;
Expand Down Expand Up @@ -967,6 +973,14 @@ class WindowsPtyHeuristics extends Disposable {
};
}

// Dynamic prompt detection
if (this._capability.promptTerminator && lineText.trim().endsWith(this._capability.promptTerminator)) {
const adjustedPrompt = this._adjustPrompt(lineText, lineText, this._capability.promptTerminator);
if (adjustedPrompt) {
return adjustedPrompt;
}
}

// Command Prompt
const cmdMatch = lineText.match(/^(?<prompt>(\(.+\)\s)?(?:[A-Z]:\\.*>))/);
return cmdMatch?.groups?.prompt ? {
Expand Down
17 changes: 17 additions & 0 deletions src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,12 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati
this._createOrGetCommandDetection(this._terminal).setIsWindowsPty(value === 'True' ? true : false);
return true;
}
case 'Prompt': {
// Remove escape sequences from the user's prompt
const sanitizedValue = value.replace(/\x1b\[[0-9;]*m/g, '');
this._updatePromptTerminator(sanitizedValue);
return true;
}
case 'Task': {
this._createOrGetBufferMarkDetection(this._terminal);
this.capabilities.get(TerminalCapability.CommandDetection)?.setIsCommandStorageDisabled();
Expand All @@ -418,6 +424,17 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati
this._createOrGetCommandDetection(this._terminal).setContinuationPrompt(value);
}

private _updatePromptTerminator(prompt: string) {
if (!this._terminal) {
return;
}
const lastPromptLine = prompt.substring(prompt.lastIndexOf('\n')).trim();
const promptTerminator = lastPromptLine.substring(lastPromptLine.lastIndexOf(' ')).trim();
if (promptTerminator) {
this._createOrGetCommandDetection(this._terminal).setPromptTerminator(promptTerminator);
}
}

private _updateCwd(value: string) {
value = sanitizeCwd(value);
this._createOrGetCwdDetection().updateCwd(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,15 @@ __vsc_escape_value() {
for (( i=0; i < "${#str}"; ++i )); do
byte="${str:$i:1}"

# Escape backslashes, semi-colons and newlines
# Escape backslashes, semi-colons, escapes, and newlines
if [ "$byte" = "\\" ]; then
token="\\\\"
elif [ "$byte" = ";" ]; then
token="\\x3b"
elif [ "$byte" = $'\n' ]; then
token="\x0a"
elif [ "$byte" = $'\e' ]; then
token="\\x1b"
else
token="$byte"
fi
Expand Down Expand Up @@ -165,6 +167,14 @@ unset VSCODE_NONCE
# Report continuation prompt
builtin printf "\e]633;P;ContinuationPrompt=$(echo "$PS2" | sed 's/\x1b/\\\\x1b/g')\a"

__vsc_report_prompt() {
# Expand the original PS1 similarly to how bash would normally
# See https://stackoverflow.com/a/37137981 for technique
__vsc_prompt=${__vsc_original_PS1@P}
__vsc_prompt="$(builtin printf "%s" "${__vsc_prompt//[$'\001'$'\002']}")"
builtin printf "\e]633;P;Prompt=%s\a" "$(__vsc_escape_value "${__vsc_prompt}")"
}

__vsc_prompt_start() {
builtin printf '\e]633;A\a'
}
Expand Down Expand Up @@ -229,6 +239,7 @@ __vsc_precmd() {
__vsc_command_complete "$__vsc_status"
__vsc_current_command=""
__vsc_update_prompt
__vsc_report_prompt
__vsc_first_prompt=1
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,13 @@ function Global:Prompt() {
Write-Error "failure" -ea ignore
}
# Run the original prompt
$Result += $Global:__VSCodeOriginalPrompt.Invoke()
$OriginalPrompt += $Global:__VSCodeOriginalPrompt.Invoke()
$Result += $OriginalPrompt

# Prompt
# OSC 633 ; <Property>=<Value> ST
$Result += "$([char]0x1b)]633;P;Prompt=$(__VSCode-Escape-Value $OriginalPrompt)`a"

# Write command started
$Result += "$([char]0x1b)]633;B`a"
$Global:__LastHistoryId = $LastHistoryEntry.Id
Expand Down

0 comments on commit 53c1b7b

Please sign in to comment.