Skip to content

Commit

Permalink
Merge pull request #199706 from cpendery/fix/terminal-suggestion-posi…
Browse files Browse the repository at this point in the history
…tioning__accept-completions

fix: accepting terminal completions cursor positions
  • Loading branch information
Tyriar authored Dec 7, 2023
2 parents 182f512 + b07c5df commit faacc5f
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ 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 $_ }
)
})
# Encode the (ascii) matches as `\x<hex>`
-Join (
[System.Text.Encoding]::UTF8.GetBytes($match.Value) | ForEach-Object { '\x{0:x2}' -f $_ }
)
})
}

function Global:Prompt() {
Expand All @@ -67,18 +67,20 @@ function Global:Prompt() {
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;E`a"
$Result += "$([char]0x1b)]633;D`a"
} else {
}
else {
# Command finished command line
# OSC 633 ; E ; <CommandLine?> ; <Nonce?> ST
$Result = "$([char]0x1b)]633;E;"
$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 {
}
else {
$CommandLine = ""
}
$Result += $(__VSCode-Escape-Value $CommandLine)
Expand All @@ -94,7 +96,7 @@ function Global:Prompt() {
$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"}
$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
Expand Down Expand Up @@ -123,7 +125,8 @@ if (Get-Module -Name PSReadLine) {
if ($PSVersionTable.PSVersion -lt "6.0") {
# Windows PowerShell is only available on Windows
[Console]::Write("$([char]0x1b)]633;P;IsWindows=$true`a")
} else {
}
else {
[Console]::Write("$([char]0x1b)]633;P;IsWindows=$IsWindows`a")
}

Expand All @@ -132,7 +135,8 @@ function Set-MappedKeyHandler {
param ([string[]] $Chord, [string[]]$Sequence)
try {
$Handler = Get-PSReadLineKeyHandler -Chord $Chord | Select-Object -First 1
} catch [System.Management.Automation.ParameterBindingException] {
}
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
Expand All @@ -142,6 +146,7 @@ function Set-MappedKeyHandler {
}
}

$Global:__VSCodeHaltCompletions = $false
function Set-MappedKeyHandlers {
Set-MappedKeyHandler -Chord Ctrl+Spacebar -Sequence 'F12,a'
Set-MappedKeyHandler -Chord Alt+Spacebar -Sequence 'F12,b'
Expand All @@ -160,7 +165,17 @@ function Set-MappedKeyHandlers {
# Suggest trigger characters
Set-PSReadLineKeyHandler -Chord "-" -ScriptBlock {
[Microsoft.PowerShell.PSConsoleReadLine]::Insert("-")
Send-Completions
if (!$Global:__VSCodeHaltCompletions) {
Send-Completions
}
}

Set-PSReadLineKeyHandler -Chord 'F12,y' -ScriptBlock {
$Global:__VSCodeHaltCompletions = $true
}

Set-PSReadLineKeyHandler -Chord 'F12,z' -ScriptBlock {
$Global:__VSCodeHaltCompletions = $false
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import * as dom from 'vs/base/browser/dom';
import { SimpleCompletionItem } from 'vs/workbench/services/suggest/browser/simpleCompletionItem';
import { LineContext, SimpleCompletionModel } from 'vs/workbench/services/suggest/browser/simpleCompletionModel';
import { ISimpleSelectedSuggestion, SimpleSuggestWidget } from 'vs/workbench/services/suggest/browser/simpleSuggestWidget';
import { timeout } from 'vs/base/common/async';
import { Codicon } from 'vs/base/common/codicons';
import { Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
Expand Down Expand Up @@ -80,7 +79,6 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest
private _enableWidget: boolean = true;
private _leadingLineContent?: string;
private _additionalInput?: string;
private _cursorIndexStart: number = 0;
private _cursorIndexDelta: number = 0;
private _inputQueue?: string[];

Expand Down Expand Up @@ -144,7 +142,6 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest

const replacementIndex = parseInt(args[0]);
const replacementLength = parseInt(args[1]);
this._cursorIndexStart = parseInt(args[2]);
if (!args[3]) {
this._onBell.fire();
return;
Expand Down Expand Up @@ -360,22 +357,19 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest

// Send the completion
this._onAcceptedCompletion.fire([
// TODO: Right arrow to end of the replacement
// Left arrow to end of the replacement
'\x1b[D'.repeat(Math.max(suggestion.model.replacementLength - this._cursorIndexStart + this._cursorIndexDelta, 0)),
// Delete to remove additional input
'\x1b[3~'.repeat(this._additionalInput?.length ?? 0),
// Disable suggestions
'\x1b[24~y',
// Right arrow to the end of the additional input
'\x1b[C'.repeat(Math.max((this._additionalInput?.length ?? 0) - this._cursorIndexDelta, 0)),
// Backspace to remove additional input
'\x7F'.repeat(this._additionalInput?.length ?? 0),
// Backspace to remove the replacement
'\x7F'.repeat(suggestion.model.replacementLength),
// Write the completion
suggestion.item.completion.label,
// Enable suggestions
'\x1b[24~z',
].join(''));

// Disable completions triggering the widget temporarily to avoid completion requests
// caused by the completion itself to show.
this._enableWidget = false;
// TODO: Disable the widget in a more sophisticated way
timeout(100).then(e => this._enableWidget = true);
}
}

Expand All @@ -402,12 +396,15 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest
return;
}
let handled = false;
let handledCursorDelta = 0;

// Backspace
if (data === '\x7f') {
if (this._additionalInput && this._additionalInput.length > 0 && this._cursorIndexDelta > 0) {
handled = true;
this._additionalInput = this._additionalInput.substring(0, this._cursorIndexDelta-- - 1) + this._additionalInput.substring(this._cursorIndexDelta);
this._additionalInput = this._additionalInput.substring(0, this._cursorIndexDelta - 1) + this._additionalInput.substring(this._cursorIndexDelta);
this._cursorIndexDelta--;
handledCursorDelta--;
}
}
// Delete
Expand All @@ -423,12 +420,14 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest
if (this._cursorIndexDelta > 0) {
handled = true;
this._cursorIndexDelta--;
handledCursorDelta--;
}
}
// Right
if (data === '\x1b[C') {
handled = true;
this._cursorIndexDelta += 1;
handledCursorDelta++;
}
if (data.match(/^[a-z0-9]$/i)) {

Expand All @@ -440,6 +439,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest
}
this._additionalInput += data;
this._cursorIndexDelta++;
handledCursorDelta++;
}
if (handled) {
// typed -> moved cursor RIGHT -> update UI
Expand All @@ -465,7 +465,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest
const xtermBox = this._screen!.getBoundingClientRect();
const panelBox = this._panel!.offsetParent!.getBoundingClientRect();
this._suggestWidget?.showSuggestions((this._suggestWidget as any)._completionModel, 0, false, false, {
left: (xtermBox.left - panelBox.left) + this._terminal.buffer.active.cursorX * dimensions.width,
left: (xtermBox.left - panelBox.left) + (this._terminal.buffer.active.cursorX + handledCursorDelta) * dimensions.width,
top: (xtermBox.top - panelBox.top) + this._terminal.buffer.active.cursorY * dimensions.height,
height: dimensions.height
});
Expand Down

0 comments on commit faacc5f

Please sign in to comment.